pmacct-0.14.0/0000755000175000017500000000000011741267746012070 5ustar paolopaolopmacct-0.14.0/missing0000755000175000017500000002203410556243724013461 0ustar paolopaolo#! /bin/sh # Common stub for a few missing GNU programs while installing. # Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc. # Originally by Fran,cois Pinard , 1996. # 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 to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try \`$0 --help' for more information" exit 1 fi run=: # In the cases where this matters, `missing' is being run in the # srcdir already. if test -f configure.ac; then configure_ac=configure.ac else configure_ac=configure.in fi case "$1" in --run) # Try to run requested program, and just exit if it succeeds. run= shift prog="$1" shift case "$prog" in aclocal) for suffix in "-1.5" ""; do if "$prog$suffix" "--version" >/dev/null 2>&1; then "$prog$suffix" "$@" && exit 0 fi done;; automake) for suffix in "-1.5" ""; do if "$prog$suffix" "--version" >/dev/null 2>&1; then "$prog$suffix" "$@" && exit 0 fi done;; *) "$prog" "$@" && exit 0;; esac set -- "$prog" "$@" ;; esac # If it does not exist, or fails to run (possibly an outdated version), # try to emulate it. case "$1" in -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an error status if there is no known handling for PROGRAM. Options: -h, --help display this help and exit -v, --version output version information and exit --run try to run the given command, and emulate it if it fails Supported PROGRAM values: aclocal touch file \`aclocal.m4' autoconf touch file \`configure' autoheader touch file \`config.h.in' automake touch all \`Makefile.in' files bison create \`y.tab.[ch]', if possible, from existing .[ch] flex create \`lex.yy.c', if possible, from existing .c help2man touch the output file lex create \`lex.yy.c', if possible, from existing .c makeinfo touch the output file tar try tar, gnutar, gtar, then tar without non-portable flags yacc create \`y.tab.[ch]', if possible, from existing .[ch]" ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing 0.3 - GNU automake" ;; -*) echo 1>&2 "$0: Unknown \`$1' option" echo 1>&2 "Try \`$0 --help' for more information" exit 1 ;; aclocal*) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`acinclude.m4' or \`${configure_ac}'. You might want to install the \`Automake' and \`Perl' packages. Grab them from any GNU archive site." touch aclocal.m4 ;; autoconf*) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." touch configure ;; autoheader*) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`acconfig.h' or \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` test -z "$files" && files="config.h" touch_files= for f in $files; do case "$f" in *:*) touch_files="$touch_files "`echo "$f" | sed -e 's/^[^:]*://' -e 's/:.*//'`;; *) touch_files="$touch_files $f.in";; esac done touch $touch_files ;; automake*) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. You might want to install the \`Automake' and \`Perl' packages. Grab them from any GNU archive site." find . -type f -name Makefile.am -print | sed 's/\.am$/.in/' | while read f; do touch "$f"; done ;; bison|yacc) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a \`.y' file. You may need the \`Bison' package in order for those modifications to take effect. You can get \`Bison' from any GNU archive site." rm -f y.tab.c y.tab.h if [ $# -ne 1 ]; then eval LASTARG="\${$#}" case "$LASTARG" in *.y) SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` if [ -f "$SRCFILE" ]; then cp "$SRCFILE" y.tab.c fi SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` if [ -f "$SRCFILE" ]; then cp "$SRCFILE" y.tab.h fi ;; esac fi if [ ! -f y.tab.h ]; then echo >y.tab.h fi if [ ! -f y.tab.c ]; then echo 'main() { return 0; }' >y.tab.c fi ;; lex|flex) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a \`.l' file. You may need the \`Flex' package in order for those modifications to take effect. You can get \`Flex' from any GNU archive site." rm -f lex.yy.c if [ $# -ne 1 ]; then eval LASTARG="\${$#}" case "$LASTARG" in *.l) SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` if [ -f "$SRCFILE" ]; then cp "$SRCFILE" lex.yy.c fi ;; esac fi if [ ! -f lex.yy.c ]; then echo 'main() { return 0; }' >lex.yy.c fi ;; help2man) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a dependency of a manual page. You may need the \`Help2man' package in order for those modifications to take effect. You can get \`Help2man' from any GNU archive site." file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` if test -z "$file"; then file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` fi if [ -f "$file" ]; then touch $file else test -z "$file" || exec >$file echo ".ab help2man is required to generate this page" exit 1 fi ;; makeinfo) if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then # We have makeinfo, but it failed. exit 1 fi echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a \`.texi' or \`.texinfo' file, or any other file indirectly affecting the aspect of the manual. The spurious call might also be the consequence of using a buggy \`make' (AIX, DU, IRIX). You might want to install the \`Texinfo' package or the \`GNU make' package. Grab either from any GNU archive site." file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` if test -z "$file"; then file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` fi touch $file ;; tar) shift if test -n "$run"; then echo 1>&2 "ERROR: \`tar' requires --run" exit 1 fi # We have already tried tar in the generic part. # Look for gnutar/gtar before invocation to avoid ugly error # messages. if (gnutar --version > /dev/null 2>&1); then gnutar ${1+"$@"} && exit 0 fi if (gtar --version > /dev/null 2>&1); then gtar ${1+"$@"} && exit 0 fi firstarg="$1" if shift; then case "$firstarg" in *o*) firstarg=`echo "$firstarg" | sed s/o//` tar "$firstarg" ${1+"$@"} && exit 0 ;; esac case "$firstarg" in *h*) firstarg=`echo "$firstarg" | sed s/h//` tar "$firstarg" ${1+"$@"} && exit 0 ;; esac fi echo 1>&2 "\ WARNING: I can't seem to be able to run \`tar' with the given arguments. You may want to install GNU tar or Free paxutils, or check the command line arguments." exit 1 ;; *) echo 1>&2 "\ WARNING: \`$1' is needed, and you do not seem to have it handy on your system. You might have modified some files without having the proper tools for further handling them. Check the \`README' file, it often tells you about the needed prerequirements for installing this package. You may also peek at any GNU archive site, in case some other package would contain this missing \`$1' program." exit 1 ;; esac exit 0 pmacct-0.14.0/TOOLS0000644000175000017500000000327311534221623012700 0ustar paolopaoloTOOLS DESCRIPTION. pmacctd libpcap-based accounting daemon; it captures packets from an interface it is bound to. Statistics can be printed to stdout, stored in memory tables or a PostgreSQL/MySQL/SQLite/Berkeley DB(*) database, exported via NetFlow, IPFIX or sFlow protocols. nfacctd NetFlow accounting daemon; it listens for NetFlow packets v1/v5/v7/v8/v9 and IPFIX on one or more interfaces (IPv4 and IPv6); statistics can be printed to stdout, stored in memory tables or a PostgreSQL/MySQL/SQLite/BerkeleyDB(*) database, replicated to other collectors. sfacctd sFlow accounting daemon; it listens for sFlow packets v2, v4 and v5 on one or more interfaces (both IPv4 and IPv6); statistics can be printed to stdout, stored in memory tables or a PostgreSQL/MySQL/SQLite/BerkeleyDB(*) database, replicated to other collectors. uacctd Linux Netlink ULOG accounting daemon; it captures packets by leveraging a ULOG multicast group - and works only on Linux; Statistics can be printed to stdout, stored in memory tables or a PostgreSQL/MySQL/SQLite/BerkeleyDB(*) database, exported via NetFlow, IPFIX or sFlow protocols. pmacct commandline pmacct client; used to retrieve data from a memory plugin; it can execute both partial and full data retrieval. Output is either formatted or 'counters-only', suitable for data injection in tools like MRTG, RRDtool, Gnuplot or SNMP server among the others. pmmyplay pmacct MySQL logfile player; it plays logfiles previously generated by a MySQL plugin. pmpgplay pmacct PgSQL logfile player; it plays logfiles previously generated by a PostgreSQL plugin. (*) using BerkeleyDB SQLite API pmacct-0.14.0/examples/0000755000175000017500000000000011741267746013706 5ustar paolopaolopmacct-0.14.0/examples/peers.map.example0000640000175000017500000000502711521007256017135 0ustar paolopaolo! ! BGP source peer ASN map ! ! File syntax is key-based. Read full syntax rules in 'pretag.map.example' in ! this same directory. ! ! nfacctd, sfacctd: valid keys: id, ip, in, bgp_nexthop, src_mac. ! ! list of currently supported keys follow: ! ! 'id' ID value to assign to a matching packet or flow. Other ! than hard-coded AS numbers, this field accepts also the ! 'bgp' keyword which triggers a BGP lookup and returns ! its result: useful to handle exceptions. ! 'ip' In nfacctd it's compared against the source IP address ! of the device which is originating NetFlow packets; in ! sfacctd this is compared against the AgentId field of ! received sFlow samples. ! 'in' Input interface ! 'bgp_nexthop' BGP next-hop of the flow source IP address (RPF-like). ! This value is compared against the corresponding BGP ! RIB of the exporting device. ! 'peer_dst_as' First AS hop within the AS-PATH of the source IP address ! (RPF-like). This value is compared against the BGP RIB ! of the exporting device (see 'bgp_daemon' configuration ! directive). ! 'src_mac' Source MAC address of the flow. Requires NetFlow v9, ! IPFIX or sFlow. ! ! A few examples follow. ! ! Private peering with AS12345 on router with IP address 192.168.2.1, SNMP ifIndex 7 ! id=12345 ip=192.168.2.1 in=7 ! A way to model a public internet exchange - in case MAC addresses are not available, ! ie. NetFlow v5. The catch-all entry at the end can be the AS number of the exchange. ! 'peer_dst_as' can be used instead of the BGP next-hop for the very same purpose, with ! perhaps 'peer_dst_as' being more effective in case of, say, egress NetFlow. Note that ! by using either 'bgp_nexthop' or 'peer_dst_as' for this purpose constitutes only an ! educated guess. ! id=34567 ip=192.168.1.1 in=7 bgp_nexthop=1.2.3.4 id=45678 ip=192.168.1.1 in=7 bgp_nexthop=1.2.3.5 id=56789 ip=192.168.1.1 in=7 ! A way to model a public internet exchange - in case MAC addresses are available. The ! method is exact and hence doesn't require a catch-all entry at the end. ! id=34567 ip=192.168.1.1 in=7 src_mac=00:01:02:03:04:05 id=45678 ip=192.168.1.1 in=7 src_mac=00:01:02:03:04:06 ! A simple example on how to trigger BGP lookups rather than returning a fixed result. ! This allows to handle exceptions to static mapping id=bgp ip=192.168.2.1 in=7 pmacct-0.14.0/examples/pmacctd-multiple-plugins.conf.example0000644000175000017500000000100610530072467023114 0ustar paolopaolo! ! pmacctd configuration example ! ! Did you know CONFIG-KEYS contains the detailed list of all configuration keys ! supported by 'nfacctd' and 'pmacctd' ? ! ! debug: true daemonize: true interface: eth0 aggregate[in]: src_host aggregate[out]: dst_host aggregate_filter[in]: dst net 192.168.0.0/16 aggregate_filter[out]: src net 192.168.0.0/16 plugins: memory[in], memory[out] imt_path[in]: /tmp/acct_in.pipe imt_path[out]: /tmp/acct_out.pipe imt_buckets: 65537 imt_mem_pools_size: 65536 imt_mem_pools_number: 0 pmacct-0.14.0/examples/nfacctd-sql_v2.conf.example0000644000175000017500000000136610530072467021010 0ustar paolopaolo! ! nfacctd configuration example ! ! Did you know CONFIG-KEYS contains the detailed list of all configuration keys ! supported by 'nfacctd' and 'pmacctd' ? ! ! debug: true daemonize: false ! aggregate_filter[dummy]: src net 192.168.0.0/16 aggregate: tag, src_host, dst_host ! plugin_buffer_size: 1024 pre_tag_map: ./id_map.example ! nfacctd_port: 5678 ! nfacctd_time_secs: true nfacctd_time_new: true ! plugins: pgsql plugins: mysql sql_db: pmacct sql_table: acct sql_table_version: 2 sql_passwd: arealsmartpwd sql_user: pmacct sql_refresh_time: 90 ! sql_multi_values: 1000000 ! sql_optimize_clauses: true sql_history: 10m sql_history_roundoff: mh ! sql_preprocess: qnum=1000, minp=5 ! networks_file: ./networks.example ! ports_file: ./ports.example pmacct-0.14.0/examples/pmacctd-imt.conf.example0000644000175000017500000000050310530072467020374 0ustar paolopaolo! ! pmacctd configuration example ! ! Did you know CONFIG-KEYS contains the detailed list of all configuration keys ! supported by 'nfacctd' and 'pmacctd' ? ! ! debug: true interface: eth0 daemonize: true plugins: memory aggregate: src_host,dst_host imt_buckets: 65537 imt_mem_pools_size: 65536 ! imt_mem_pools_number: 0 pmacct-0.14.0/examples/nfacctd-sql_v1.conf.example0000644000175000017500000000130710530072467021002 0ustar paolopaolo! ! nfacctd configuration example ! ! Did you know CONFIG-KEYS contains the detailed list of all configuration keys ! supported by 'nfacctd' and 'pmacctd' ? ! ! debug: true ! daemonize: false ! aggregate_filter[dummy]: src net 192.168.0.0/16 aggregate[dummy]: src_host, dst_host ! plugin_buffer_size: 1024 ! nfacctd_port: 5678 ! nfacctd_time_secs: true nfacctd_time_new: true ! plugins: pgsql plugins: mysql[dummy] sql_db: pmacct sql_table: acct sql_table_version: 1 sql_passwd: arealsmartpwd sql_user: pmacct sql_refresh_time: 90 ! sql_optimize_clauses: true sql_history: 10m sql_history_roundoff: mh ! sql_preprocess: qnum=1000, minp=5 ! networks_file: ./networks.example ! ports_file: ./ports.example pmacct-0.14.0/examples/pmacctd-sqlite3_v4.conf.example0000644000175000017500000000054510530072467021606 0ustar paolopaolo! ! pmacctd configuration example ! ! Did you know CONFIG-KEYS contains the detailed list of all configuration keys ! supported by 'nfacctd' and 'pmacctd' ? ! ! debug: true ! interface: eth0 daemonize: false aggregate: sum_host plugins: sqlite3 sql_db: /tmp/pmacct.db sql_table_version: 4 sql_refresh_time: 60 sql_history: 10m sql_history_roundoff: h pmacct-0.14.0/examples/allow-list.example0000644000175000017500000000013210530072467017333 0ustar paolopaolo! ! Sample allow-list; enabled via 'nfacctd_allow_file' key. ! 192.168.0.1 192.168.1.254 pmacct-0.14.0/examples/lpref.map.example0000640000175000017500000000360011521007256017122 0ustar paolopaolo! ! BGP source local preferecence map ! ! File syntax is key-based. Read full syntax rules in 'pretag.map.example' in ! this same directory. ! ! nfacctd, sfacctd: valid keys: id, ip, in, bgp_nexthop, src_mac. ! ! list of currently supported keys follow: ! ! 'id' ID value to assign to a matching packet or flow. Other ! than hard-coded local preference values, this field also ! accepts the 'bgp' keyword which triggers a BGP lookup ! and returns its result: useful to handle exceptions. ! 'ip' In nfacctd it's compared against the source IP address ! of the device which is originating NetFlow packets; in ! sfacctd this is compared against the AgentId field of ! received sFlow samples. ! 'in' Input interface. ! 'bgp_nexthop' BGP next-hop of the flow source IP address (RPF-like). ! This value is compared against the corresponding BGP ! RIB of the exporting device. ! 'peer_dst_as' First AS hop within the AS-PATH of the source IP address ! (RPF-like). This value is compared against the BGP RIB ! of the exporting device (see 'bgp_daemon' configuration ! directive). ! 'src_mac' Source MAC address of the flow. Requires NetFlow v9, ! IPFIX or sFlow. ! ! A few examples follow. Let's define: LP=100 identifies customers, LP=80 identifies peers ! and LP=50 identifies IP transit. ! ! Customer connected to router with IP address 192.168.2.1, SNMP ifIndex 7 ! id=100 ip=192.168.2.1 in=7 ! A way to model multiple services, ie. IP transit and peering, off the same interface. ! Realistically services should be delivered off different sub-interfaces, but still ... ! id=50 ip=192.168.1.1 in=7 bgp_nexthop=1.2.3.4 id=80 ip=192.168.1.1 in=7 bgp_nexthop=1.2.3.5 pmacct-0.14.0/examples/nfacctd-print.conf.example0000644000175000017500000000061010530072467020725 0ustar paolopaolo! ! nfacctd configuration example ! ! Did you know CONFIG-KEYS contains the detailed list of all configuration keys ! supported by 'nfacctd' and 'pmacctd' ? ! ! aggregate_filter[dummy]: src net 192.168.0.0/16 aggregate: src_host, dst_host, src_port, dst_port, proto plugins: print[dummy] ! plugin_buffer_size: 1024 ! nfacctd_port: 5678 ! nfacctd_time_secs: true ! nfacctd_time_new: true pmacct-0.14.0/examples/sampling.map.example0000640000175000017500000000216111603333035017623 0ustar paolopaolo! ! Sampling map -- given at least a router IP, returns a sampling rate ! ! File syntax is key-based. Position of keys inside the same row (rule) is not ! relevant; Spaces are not allowed (ie. 'id = 1' is not valid). The first full ! match wins (like in firewall rules). Negative values mean negations (ie. match ! data NOT entering interface 2: 'in=-2'); 'id' and 'ip' keys don't support ! negative values. ! ! nfacctd: valid keys: id, ip, in, out ! ! sfacctd: valid keys: id, ip, in, out ! ! list of currently supported keys follows: ! ! 'id' sampling rate assigned to a matching packet, flow or sample. ! The result is used to renormalize packet and bytes count if ! either of sfacctd_renormalize or nfacctd_renormalize config ! directive is set to true. ! 'ip' In nfacctd this is compared against the source IP address of ! the device which is originating NetFlow packets; in sfacctd ! this is compared against the AgentId field of received sFlow ! samples. ! 'in' Input interface ! 'out' Output interface ! ! ! Examples: ! id=1024 ip=192.168.1.1 id=2048 ip=192.168.2.1 in=5 id=4096 ip=192.168.3.1 out=3 pmacct-0.14.0/examples/med.map.example0000640000175000017500000000300211521007256016553 0ustar paolopaolo! ! BGP source MED (Multi Exit Discriminator) map ! ! File syntax is key-based. Read full syntax rules in 'pretag.map.example' in ! this same directory. ! ! nfacctd, sfacctd: valid keys: id, ip, in, bgp_nexthop, src_mac. ! ! list of currently supported keys follow: ! ! 'id' ID value to assign to a matching packet or flow. Other ! than hard-coded MED values this field accepts also the ! 'bgp' keyword which triggers a BGP lookup and returns ! its result: useful to handle exceptions. ! 'ip' In nfacctd it's compared against the source IP address ! of the device which is originating NetFlow packets; in ! sfacctd this is compared against the AgentId field of ! received sFlow samples. ! 'in' Input interface. ! 'bgp_nexthop' BGP next-hop of the flow source IP address (RPF-like). ! This value is compared against the corresponding BGP ! RIB of the exporting device. ! 'peer_dst_as' First AS hop within the AS-PATH of the source IP address ! (RPF-like). This value is compared against the BGP RIB ! of the exporting device (see 'bgp_daemon' configuration ! directive). ! 'src_mac' Source MAC address of the flow. Requires NetFlow v9, ! IPFIX or sFlow. ! ! A few examples follow. ! ! Customer connected to router with IP address 192.168.2.1, SNMP ifIndex 7 ! id=20 ip=192.168.2.1 in=7 pmacct-0.14.0/examples/probe_sflow.conf.example0000644000175000017500000000046110530072467020516 0ustar paolopaolo! ! pmacctd configuration example ! ! Did you know CONFIG-KEYS contains the detailed list of all configuration keys ! supported by 'nfacctd' and 'pmacctd' ? ! ! debug: true daemonize: true interface: eth0 plugins: sfprobe sfprobe_agentsubid: 1402 sfprobe_receiver: 1.2.3.4:6343 sfprobe_sampling_rate: 20 pmacct-0.14.0/examples/ports.lst.example0000644000175000017500000000012710530072467017220 0ustar paolopaolo! ! Sample ports-list; enabled by 'ports_file' key. ! 22 23 25 110 137 139 ! ... 4662 pmacct-0.14.0/examples/pmacctd-sql_v1.conf.example0000644000175000017500000000125710530072467021017 0ustar paolopaolo! ! pmacctd configuration example ! ! Did you know CONFIG-KEYS contains the detailed list of all configuration keys ! supported by 'nfacctd' and 'pmacctd' ? ! ! debug: true ! interface: eth0 daemonize: false aggregate: src_host,dst_host ! aggregate: src_net,dst_net ! plugins: pgsql plugins: mysql sql_db: pmacct sql_table: acct sql_table_version: 1 sql_passwd: arealsmartpwd sql_user: pmacct sql_refresh_time: 90 ! sql_optimize_clauses: true sql_history: 10m sql_history_roundoff: mh ! sql_preprocess: qnum=1000, minp=5 ! ! networks_file: ./networks.example ! ports_file: ./ports.example ! sampling_rate: 10 ! sql_trigger_time: 1h ! sql_trigger_exec: /home/paolo/codes/hello.sh ! pmacct-0.14.0/examples/iface_to_rd.map.example0000640000175000017500000000233211661415562020261 0ustar paolopaolo! ! PE router ifIndex to BGP/MPLS VPN RD map ! ! File syntax is key-based. Read full syntax rules in 'pretag.map.example' in ! this same directory. ! ! nfacctd, sfacctd: valid keys: id, ip, in, out. ! ! list of currently supported keys follow: ! ! 'id' IPv4 address or router ID of the BGP peer ! 'ip' In nfacctd it's compared against the source IP address ! of the device which is originating NetFlow packets; in ! sfacctd this is compared against the AgentId field of ! received sFlow samples. ! 'in' Input interface ! 'out' Output interface ! ! A couple of straightforward examples follow. ! ! Maps input interface 100 of router 192.168.1.1 to RD 0:65512:1 - ie. ! a BGP/MPLS VPN Route Distinguisher encoded as type #0 according to ! to rfc4659: <2-bytes ASN>: . Type #2 is equivalent to type #0 ! except it supports 4-bytes ASN encoding. ! id=0:65512:1 ip=192.168.1.1 in=100 ! ! Maps input interface 100 of router 192.168.1.1 to RD 1:192.168.1.1:1 ! ie. a BGP/MPLS VPN Route Distinguisher encoded as type #1 according ! to rfc4659: : ! id=1:192.168.1.1:1 ip=192.168.1.1 in=100 pmacct-0.14.0/examples/gnuplot.script.example0000644000175000017500000000047310530072467020247 0ustar paolopaoloset term png small color set data style lines set grid set yrange [ 0 : ] set title "Traffic in last XX hours" set xlabel "hours" set ylabel "kBytes" set multiplot plot "in.txt" using ($1/3600):($2/1000) title "IN Traffic" with linespoints, "out.txt" using ($1/3600):($2/1000) title "OUT Traffic" with linespoints pmacct-0.14.0/examples/agent_to_peer.map.example0000640000175000017500000000207211465540617020641 0ustar paolopaolo! ! NetFlow/sFlow agent to BGP peer map ! ! File syntax is key-based. Read full syntax rules in 'pretag.map.example' in ! this same directory. ! ! nfacctd, sfacctd: valid keys: id, ip. ! ! list of currently supported keys follow: ! ! 'id' IPv4 address or router ID of the BGP peer ! 'ip' In nfacctd it's compared against the source IP address ! of the device which is originating NetFlow packets; in ! sfacctd this is compared against the AgentId field of ! received sFlow samples. ! ! A couple of straightforward examples follow. ! id=1.2.3.4 ip=2.3.4.5 ! ! The following comes handy when compiling pmacct for IPv6; BGP peers ! are always IPv4 addresses. Whereas remote sFlow or NetFlow agents are ! likely to be intended as IPv4-mapped IPv6 addresses, if nfacctd_ip is ! not specified. ! ! id=3.4.5.6 ip=::ffff:4.5.6.7 ! ! The following maps something which isn't a Netflow/sFlow agent to the ! specified BGP peer. The only application for this lies with pmacctd. ! ! id=4.5.6.7 ip=0.0.0.0 ! pmacct-0.14.0/examples/bgp_md5.lst.example0000640000175000017500000000024311411156705017356 0ustar paolopaolo! ! Sample BGP MD5 map; enabled by 'bgp_daemon_md5_file' key. ! ! Format supported: , ! 192.168.1.1, arealsmartpwd 192.168.1.2, TestTest ! ... pmacct-0.14.0/examples/pretag.map.example0000644000175000017500000002600011715256660017311 0ustar paolopaolo! ! Pre-Tagging map -- multiplexes various fields into a 4-bytes numerical-only ID ! ! File syntax is key-based. Position of keys inside the same row (rule) is not ! relevant; Spaces are not allowed (ie. 'id = 1' is not valid). The first full ! match wins (like in firewall rules). Negative values mean negations (ie. match ! data NOT entering interface 2: 'in=-2'); 'id', 'id2', 'ip' and 'filter' keys ! don't support negative values. 'label', 'jeq', 'return' and 'stack' keys can ! be used to alter the standard rule evaluation flow. ! ! nfacctd: valid keys: id, id2, ip, in, out, engine_type, engine_id, nexthop, ! bgp_nexthop, src_as, dst_as, direction, v8agg, sampling_rate and filter; ! mandatory keys for each rule: id or id2 and ip. ! ! sfacctd: valid keys: id, id2, ip, in, out, agent_id, nexthop, bgp_nexthop, ! src_as, dst_as, sampling_rate, sample_type and filter; mandatory keys for ! each rule: id or id2 and ip. ! ! pmacctd: valid keys: id, id2, src_as, dst_as and filter. Only either id or id2 ! are mandatory for each rule. ! ! sfacctd, nfacctd when in 'tee' mode: valid keys: id, id2, ip; mandatory keys ! for each rule: id or id2 and ip. ! ! BGP-related keys are independent of the collection method in use, hence apply ! to all daemons (BGP daemon must be enabled): src_as, dst_as, src_comms, comms, ! peer_src_as, peer_dst_as, src_local_pref, local_pref, mpls_vpn_rd. ! ! list of currently supported keys follows: ! ! 'id' tag assigned to a matching packet, flow or sample; its ! use is mutually exclusive with id2. The resulting label ! is written to the 'tag' field when using memory tables ! and 'agent_id' when using a SQL plugin. ! 'id2' tag assigned to a matching packet, flow or sample; its ! use within a rule is mutually exclusive with 'id'. The ! resulting label is written to the 'tag2' field when ! using memory tables and 'agent_id2' when using a SQL ! plugin. If using a SQL plugin, read more about the ! 'agent_id2' field in the 'sql/README.agent_id2' document ! 'ip' In nfacctd it's compared against the source IP address ! of the device which is originating NetFlow packets; in ! sfacctd this is compared against the AgentId field of ! received sFlow samples. ! 'in' Input interface ! 'out' Output interface ! 'engine_type' In NetFlow V5 it's compared against the 'engine_type' ! header field. In NetFlow V9 it's compared against the ! 3rd octet of the 'source_id' header field. Provides ! uniqueness with respect to the routing engine on the ! exporting device. ! 'engine_id' In NetFlow V5 it's compared against the 'engine_id' ! header field. In NetFlow V9 it's compared against the ! 4th octet of the 'source_id' header field. It provides ! uniqueness with respect to the particular line card on ! the exporting device. ! 'nexthop' IPv4/IPv6 address of the next-hop router ! 'bgp_nexthop' IPv4/IPv6 address of the next-hop BGP router ! 'filter' Matches incoming packets against the supplied filter ! expression (expected in libpcap syntax); the filter ! needs to be enclosed in quotes ('). ! 'v8agg' In NetFlow V8 this is compared against the aggregation ! method in use. Valid values are in the range 0 > value ! > 15. ! 'agent_id' In sFlow v5 it's compared against the subAgentId field. ! sFlow v2/v4 do not carry such field, hence it does not ! apply. ! 'sampling_rate' In sFlow v2/v4/v5 this is compared against the sampling ! rate field; it also works against NetFlow v5. NetFlow v9 ! and IPFIX are unsupported instead. ! 'sample_type' In sFlow v2/v4/v5 this is compared against the sample ! type field. Expected in : notation. ! 'direction' In NetFlow v9 and IPFIX this is compared against the ! direction (61) field, which only valid values are 0 ! (ingress) and 1 (egress) flow. ! 'src_as' source Autonomous System Number. Whether BGP daemon is ! not enabled in pmacctd it works only against a Networks ! map (see 'networks_file' directive); in nfacctd and ! sfacctd it works against a Networks Map, the source ASN ! field in either sFlow or NetFlow datagrams. Since 0.12, ! this can be compared against the corresponding BGP RIB ! of the exporting device (see 'bgp_daemon' configuration ! directive). ! 'dst_as' destination Autonomous System Number. Same 'src_as' ! remarks hold here. Please read them above. ! 'peer_src_as' peering source Autonomous System Number. It is compared ! against the corresponding BGP RIB of the exporting ! device (see 'bgp_daemon' configuration directive). ! 'peer_dst_as' peering destination Autonomous System Number. Same ! 'peer_src_as' remarks hold here. Please read them above. ! 'src_local_pref' Source IP prefix BGP local preference attribute. This is ! compared against the BGP RIB of the exporting device. ! 'local_pref' Destination IP prefix BGP local preference attribute. ! This is compared against the BGP RIB of the exporting ! device. ! 'src_comms' Source IP prefix BGP standard communities attribbute; ! multiple elements, up to 16, can be supplied, comma- ! separated (no spaces allowed); the check is successful ! if any of the communities is matched. This is compared ! against the BGP RIB of the exporting device. Examples ! are provided below. ! 'comms' Destination IP prefix BGP standard communities; multiple ! elements, up to 16, can be supplied, comma-separated (no ! spaces allowed); the check is successful if any of the ! communities is matched. This is compared against the BGP ! RIB of the exporting device. See examples below. ! 'mpls_vpn_rd' Destination IP prefix BGP/MPLS VPN Route Distinguisher ! (RD) value. Encoding types #0, #1 and #2 are supported ! as per rfc4364. See example below. ! 'label' Mark the rule with label's value. Labels don't need to ! be unique: when jumping, the first matching label wins. ! Label value 'next' is reserved for internal use and ! hence must not be used in a map. Doing otherwise might ! give unexpected results. ! 'jeq' Jump on EQual. Jumps to the supplied label in case of ! rule match. Jumps are Only forward. Label "next" is ! reserved and causes to go to the next rule, if any. ! Before continuing the map workflow, tagged data can be ! optionally returned to plugins (jeq=xxx return=true). ! Disabled by default (ie. return=false). Beware setting ! return=true, depending on configurations, can generate ! spurious data or duplicates; the logics with which this ! is intended to work is: plugins which include 'tag' in ! their aggregation method will receive each tagged copy ! (if not filtered out by the pre_tag_filter directive); ! plugins not configured for tags will only receive a ! single copy of the data. ! 'return' Works only associated to a 'jeq' and is set to 'false' ! by default. Read full details in the 'jeq' section. If ! set to 'true', after tagged data is sent for accounting, ! the tag is zeroed making essentially this feature not ! compatible with a 'stack' one. ! 'stack' Currently '+' (ie. sum symbol) is the unique supported ! value. This key makes sense only if JEQs are in use. ! When matching, accumulate tags, using the specified ! operator/function. By setting 'stack=+', the resulting ! tag would be: =. ! ! ! Examples: ! ! Some examples applicable to NetFlow. ! id=1 ip=192.168.2.1 in=4 id=10 ip=192.168.1.1 in=5 out=3 id=11 ip=192.168.1.1 in=3 out=5 id=12 ip=192.168.1.1 in=3 id=13 ip=192.168.1.1 nexthop=10.0.0.254 id=14 ip=192.168.1.1 engine_type=1 engine_id=0 id=15 ip=192.168.1.1 in=3 filter='src net 192.168.0.0/24' ! ! The following rule applies to 'pmacctd'; it will return an error if applied to either ! 'nfacctd' or 'sfacctd' ! id=21 filter='src net 192.168.0.0/16' ! ! A few examples sFlow-related. The format of the rules is the same of 'nfacctd' ones ! but some keys don't apply to it. ! id=30 ip=192.168.1.1 id=31 ip=192.168.1.1 out=50 id=32 ip=192.168.1.1 out=50 agent_id=0 sampling_rate=512 ! ! === JEQ example #1: ! - implicit 'return' defaults to false ! - 'id' used to store input interface tags ! - 'id2' used to store output interface tags ! id=1000 ip=192.168.1.1 in=1 jeq=eval_out id=1001 ip=192.168.1.1 in=2 jeq=eval_out id=1002 ip=192.168.1.1 in=3 jeq=eval_out ! ... further INs id2=1000 ip=192.168.1.1 out=1 label=eval_out id2=1001 ip=192.168.1.1 out=2 id2=1002 ip=192.168.1.1 out=3 ! ... further OUTs ! ! === ! ! === JEQ example #2: ! - implicit 'return' defaults to false ! - 'id' structured hierarchically to store both input and output interface tags ! id=11000 ip=192.168.1.1 in=1 jeq=eval_out id=12000 ip=192.168.1.1 in=2 jeq=eval_out id=13000 ip=192.168.1.1 in=3 jeq=eval_out ! ... further INs id=100 ip=192.168.1.1 out=1 label=eval_out stack=+ id=101 ip=192.168.1.1 out=2 stack=+ id=102 ip=192.168.1.1 out=3 stack=+ ! ... further OUTs ! ! === ! ! === JEQ example #3: ! - 'return' set to true: upon matching, the packet is passed to the plugins along with its tag. ! The pre_tag_map flow continues by following up the JEQ. ! - The above leads to duplicates. Hence a pre_tag_filter should be used to split packets among plugins. ! - 'id' used to temporarily store both input and output interface tags ! id=1001 ip=192.168.1.1 in=1 jeq=eval_out return=true id=1002 ip=192.168.1.1 in=2 jeq=eval_out return=true id=1003 ip=192.168.1.1 in=3 jeq=eval_out return=true ! ... further INs id=2001 ip=192.168.1.1 out=1 label=eval_out id=2002 ip=192.168.1.1 out=2 id=2003 ip=192.168.1.1 out=3 ! ... further OUTs ! ! pre_tag_filter[in]: 1001-1003 ! pre_tag_filter[out]: 2001-2003 ! ! === ! ! === BGP standard communities example #1 ! - check is successful if matches either 65000:1234 or 65000:2345 ! id=100 ip=192.168.1.1 comms=65000:1234,65000:2345 ! ! === ! ! === BGP standard communities example #2 ! - a series of checks can be piled up in order to mimic match-all ! - underlying logics is: ! > tag=200 is considered a successful check; ! > tag=0 or tag=100 is considered unsuccessful ! id=100 ip=192.168.1.1 comms=65000:1234 label=65000:1234 jeq=65000:2345 id=100 ip=192.168.1.1 comms=65000:2345 label=65000:2345 jeq=65000:3456 ! ... further id=100 id=200 ip=192.168.1.1 comms=65000:3456 label=65000:3456 ! ! === ! ! === BGP/MPLS VPN Route Distinguisher (RD) example ! - check is successful if matches encoding type #0 with value 65512:1 ! id=100 ip=192.168.1.1 mpls_vpn_rd=0:65512:1 ! ! === ! ! === sfprobe/nfprobe: determining semi-dynamically direction and ifindex ! - Two steps approach: ! > determine direction first (1=in, 2=out) ! > then short circuit it to return an ifindex value ! - Configuration would look like the following fragment: ! ... ! nfprobe_direction: tag ! nfprobe_ifindex: tag2 ! ... ! id=1 filter='ether dst 00:11:22:33:44:55' jeq=fivefive id=1 filter='ether dst 00:11:22:33:44:66' jeq=sixsix id=1 filter='ether dst 00:11:22:33:44:77' jeq=sevenseven id=2 filter='ether src 00:11:22:33:44:55' jeq=fivefive id=2 filter='ether src 00:11:22:33:44:66' jeq=sixsix id=2 filter='ether src 00:11:22:33:44:77' jeq=sevenseven ! id2=5 label=fivefive id2=6 label=sixsix id2=7 label=sevenseven ! ! === pmacct-0.14.0/examples/networks.lst.example0000644000175000017500000000046510530072467017732 0ustar paolopaolo! ! Sample networks-list; enabled by 'networks_file' key. ! ! Format supported: [',']'/' ! 192.168.95.0/24 192.168.94.0/25 192.168.94.128/25 192.168.92.0/26 192.168.92.64/26 192.168.92.128/26 192.168.92.192/26 ! 137,150.145.0.0/16 ! 137,150.146.0.0/16 ! 137,194.119.192.0/19 pmacct-0.14.0/examples/probe_netflow.conf.example0000644000175000017500000000075310530072467021046 0ustar paolopaolo! ! pmacctd configuration example ! ! Did you know CONFIG-KEYS contains the detailed list of all configuration keys ! supported by 'nfacctd' and 'pmacctd' ? ! ! debug: true daemonize: true interface: eth0 aggregate: src_host, dst_host, src_port, dst_port, proto, tos plugins: nfprobe nfprobe_receiver: 1.2.3.4:2100 nfprobe_version: 9 ! nfprobe_engine: 1:1 ! nfprobe_timeouts: tcp=120:maxlife=3600 ! ! networks_file: /path/to/networks.lst ! classifiers: /path/to/classifiers/ ! snaplen: 700 pmacct-0.14.0/examples/gnuplot-example.sh0000755000175000017500000000251010530072467017351 0ustar paolopaolo#!/bin/bash # This file aims to be a trivial example on how to interface pmacctd/nfacctd memory # plugin to GNUPlot (http://www.gnuplot.info) to make graphs from data gathered from # the network. # # The following does the following assumptions (but these could be easily changed): # # - You are using a PostgreSQL database with two tables: 'acct_in' for incoming traffic # and 'acct_out' for outcoming traffic # - You are aggregating traffic for 'src_host' in 'acct_out' and for 'dst_host' in # 'acct_in' # - You have enabled 'sql_history' to generate timestamps in 'stamp_inserted' field; # because the variable $step is 3600, the assumption is: 'sql_history: 1h' # # After having populated the files 'in.txt' and 'out.txt' run gnuplot the following way: # # > gnuplot gnuplot.script.example > plot.png # PGPASSWORD="arealsmartpwd" export PGPASSWORD j=0 step=3600 output_in="in.txt" output_out="out.txt" rm -rf $output_in rm -rf $output_out RESULT_OUT=`psql -U pmacct -t -c "SELECT SUM(bytes) FROM acct_out WHERE ip_src = '192.168.0.133' GROUP BY stamp_inserted;"` RESULT_IN=`psql -U pmacct -t -c "SELECT SUM(bytes) FROM acct_in WHERE ip_dst = '192.168.0.133' GROUP BY stamp_inserted;"` j=0 for i in $RESULT_IN do echo $j $i >> $output_in let j+=$step done j=0 for i in $RESULT_OUT do echo $j $i >> $output_out let j+=$step done pmacct-0.14.0/examples/mrtg-example.sh0000755000175000017500000000130410530072467016632 0ustar paolopaolo#!/bin/sh # This file aims to be a trivial example on how to interface pmacctd/nfacctd memory # plugin to MRTG (people.ee.ethz.ch/~oetiker/webtools/mrtg/) to make graphs from # data gathered from the network. # # This script has to be invoked timely from crontab: # */5 * * * * /usr/local/bin/mrtg-example.sh # # The following command collects incoming and outcoming traffic (in bytes) between # two hosts; the '-r' switch makes counters 'absolute': they are zeroed after each # query. unset IN unset OUT IN=`/usr/local/bin/pmacct -c src_host,dst_host -N 192.168.0.100,192.168.0.133 -r` OUT=`/usr/local/bin/pmacct -c src_host,dst_host -N 192.168.0.133,192.168.0.100 -r` echo $IN echo $OUT echo 0 echo 0 pmacct-0.14.0/examples/rrdtool-example.sh0000755000175000017500000000133610530072467017353 0ustar paolopaolo#!/bin/sh # This file aims to be a trivial example on how to interface pmacctd/nfacctd memory # plugin to RRDtool (people.ee.ethz.ch/~oetiker/webtools/rrdtool/) to make graphs # from data gathered from the network. # # This script has to be invoked timely from crontab: # */5 * * * * /usr/local/bin/rrdtool-example.sh # # The following command feeds a two DS (Data Sources) RRD with incoming and outcoming # traffic (in bytes) between two hosts; the '-r' switch makes counters 'absolute': they # are zeroed after each query. /usr/local/bin/rrdtool update /tmp/test.rrd N:`/usr/local/bin/pmacct -c src_host,dst_host -N 192.168.0.133,192.168.0.100 -r`:`/usr/local/bin/pmacct -c src_host,dst_host -N 192.168.0.100,192.168.0.133 -r` pmacct-0.14.0/examples/pmacctd-sql_v2.conf.example0000644000175000017500000000127310530072467021016 0ustar paolopaolo! ! pmacctd configuration example ! ! Did you know CONFIG-KEYS contains the detailed list of all configuration keys ! supported by 'nfacctd' and 'pmacctd' ? ! ! debug: true ! interface: eth0 daemonize: false post_tag: 1 aggregate: src_host,dst_host ! aggregate: src_net,dst_net ! plugins: pgsql plugins: mysql sql_db: pmacct sql_table: acct sql_table_version: 2 sql_passwd: arealsmartpwd sql_user: pmacct sql_refresh_time: 90 ! sql_optimize_clauses: true sql_history: 10m sql_history_roundoff: mh ! sql_preprocess: qnum=1000, minp=5 ! ! networks_file: ./networks.example ! ports_file: ./ports.example ! sampling_rate: 10 ! sql_trigger_time: 1h ! sql_trigger_exec: /home/paolo/codes/hello.sh pmacct-0.14.0/examples/mrtg.conf.example0000644000175000017500000000156310530072467017152 0ustar paolopaolo# This is a trivial and basic config for use pmacct to export statistics # to mrtg. If you need more informations of the few commands shown below # refer to the online referenge guide at the official MRTG web page: # http://people.ee.ethz.ch/~oetiker/webtools/mrtg/reference.html # Some general definition WorkDir: /var/www/html/monitor Options[_]: growright, bits # Target specific definitions Target[ezwf]: `./mrtg-example.sh` SetEnv[ezwf]: MRTG_INT_IP="10.0.0.1" MRTG_INT_DESCR="yourip.yourdomain.com" MaxBytes[ezwf]: 1250000 LegendI[ezwf]: Title[ezwf]: yourip.yourdomain.com PageTop[ezwf]:

yourip.yourdomain.com

System: yourip.yourdomain.com in
Maintainer:
Ip: 10.0.0.1 (yourip.yourdomain.com)
# ... # Put here more targets and their definitions pmacct-0.14.0/COPYING0000644000175000017500000004334011037474162013115 0ustar paolopaolopmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente 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 Appendix: 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. pmacct-0.14.0/NEWS0000644000175000017500000000003410556243724012555 0ustar paolopaoloNEWS: see ChangeLog file pmacct-0.14.0/install-sh0000755000175000017500000001273610556243724014076 0ustar paolopaolo#!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 pmacct-0.14.0/CONFIG-KEYS0000644000175000017500000022525011740524670013526 0ustar paolopaoloSUPPORTED CONFIGURATION KEYS Both configuration directives and commandline switches are listed below. A configuration consists of key/value pairs, separated by the ':' char. Starting a line with the '!' symbol, makes the whole line to be ignored by the interpreter, making it a comment. Please refer also to the EXAMPLES document and the 'examples/' sub-tree for some examples. Directives are sometimes grouped, like sql_table and print_output_file: this is to stress if multiple plugins are running as part of the same daemon instance, such directives must be casted to the plugin they refer to - in order to prevent undesired inheritance effects. In other words, grouped directives share the same field in the configuration structure. LEGEND of flags: GLOBAL Can't be configured on individual plugins NO_GLOBAL Can't be configured globally NO_PMACCTD Does not apply to 'pmacctd' (it's likely it will not apply to 'uacctd' either) NO_UACCTD Does not apply to 'uacctd' NO_NFACCTD Does not apply to 'nfacctd' (it's likely it will not apply to 'sfacctd' either) NO_SFACCTD Does not apply to 'sfacctd' LIST OF DIRECTIVES: KEY: debug (-d) VALUES: [true|false] DESC: enables debug (default: false). KEY: daemonize (-D) [GLOBAL] VALUES: [true|false] DESC: daemonizes the process (default: false). KEY: aggregate (-c) VALUES: [src_mac, dst_mac, vlan, cos, etype, src_host, dst_host, src_net, dst_net, src_mask, dst_mask, src_as, dst_as, src_port, dst_port, tos, proto, none, sum_mac, sum_host, sum_net, sum_as, sum_port, flows, tag, tag2, class, tcpflags, in_iface, out_iface, std_comm, ext_comm, as_path, peer_src_ip, peer_dst_ip, peer_src_as, peer_dst_as, local_pref, med, src_as_path, src_std_comm, src_ext_comm, src_local_pref, src_med, mpls_vpn_rd] FOREWORDS: individual IP packets are uniquely identified by their header fields values (a rather large set of primitives!). Same applies to uni-directional IP flows, as they have at least enough information to discriminate where packets are coming from and going to. Aggregates are instead used for the sole purpose of IP accounting and hence can be identified by a custom and stripped down set of primitives. The procedure to create an aggregate starting from IP packets or flow is: (a) select only the primitives of interest (generic aggregation), (b) optionally cast certain primitive values into broader logical entities, ie. IP addresses into network prefixes or Autonomous System Numbers (spatial aggregation) and (c) sum bytes/flows/packets counters whenever a new constituent IP packet or flow is captured (temporal aggregation). DESC: aggregate captured traffic data by selecting the specified set of primitives. sum_ are compound primitives which join together both inbound and outbound traffic into a single aggregate. The 'none' primitive allows to make an unique aggregate which accounts for the grand total of traffic flowing through a specific interface. 'tag' and 'tag2' enable generation of tags when tagging engines (pre_tag_map, post_tag) are in use. 'class' enables reception of L7 traffic classes when Packet/Flow Classification engine (classifiers) is in use. (default: src_host). NOTES: * Some primitives (ie. tag2, src_as_path, src_std_comm, src_ext_comm, src_med and src_local_pref primitives) are not part of any default SQL table schema shipped with pmacct. Always check out documentation related to the RDBMS in use (ie. 'sql/README.mysql') which will point you to extra primitive-related documentation, if required. KEY: aggregate_filter [NO_GLOBAL] DESC: Per-plugin filtering applied against the original packet or flow. Aggregation is performed slightly afterwards, upon successful match of this filter. By binding a filter, in tcpdump syntax, to an active plugin, this directive allows to select which data has to be delivered to the plugin and aggregated as specified by the plugin 'aggregate' directive. See the following example: ... aggregate[inbound]: dst_host aggregate[outbound]: src_host aggregate_filter[inbound]: dst net 192.168.0.0/16 aggregate_filter[outbound]: src net 192.168.0.0/16 plugins: memory[inbound], memory[outbound] ... This directive can be used in conjunction with 'pre_tag_filter' (which, in turn, allows to filter tags). You will also need to force fragmentation handling in the specific case in which a) none of the 'aggregate' directives is including L4 primitives (ie. src_port, dst_port) but b) an 'aggregate_filter' runs a filter which requires dealing with L4 primitives. For further information, refer to the 'pmacctd_force_frag_handling' directive. KEY: pcap_filter (like tcpdump syntax) [GLOBAL, NO_NFACCTD] DESC: this filter is global and applied to all incoming packets. It's passed to libpcap and, indeed, expects libpcap/tcpdump filter syntax. Being global it doesn't offer a great flexibility but it's the fastest way to drop unwanted traffic. It applies only to pmacctd. KEY: snaplen (-L) [GLOBAL, NO_NFACCTD] DESC: specifies the maximum number of bytes to capture for each packet. This directive has key importance when enabling both classification and connection tracking engines. In fact, some protocols (mostly text-based eg.: RTSP, SIP, etc.) benefit of extra bytes because they give more chances to successfully track data streams spawned by control channel. But it must be also noted that capturing larger packet portion require more resources. The right value need to be traded-off. In case classification is enabled, values under 200 bytes are often meaningless. 500-750 bytes are enough even for text based protocols. Default snaplen values are ok if classification is disabled. For uacctd daemon, this option doesn't apply to packet snapshot length but rather to the Netlink socket read buffer size. This should be reasonably large - at least 4KB, which is the default value. For large uacctd_nl_size values snaplen could be further increased. KEY: plugins (-P) VALUES: [ memory | print | mysql | pgsql | sqlite3 | nfprobe | sfprobe | tee ] DESC: plugins to be enabled. SQL plugins are available only if configured and compiled. 'memory' enables the use of a memory table as backend; then, a client tool, 'pmacct', can fetch its content; mysql, pgsql and sqlite3 enable the use of respectively MySQL, PostgreSQL and SQLite 3.x (or BerkeleyDB 5.x with the SQLite API compiled-in) tables to store data. 'print' prints aggregates to flat-files or stdout in CSV or formatted. 'nfprobe' acts as a NetFlow/IPFIX agent and exports collected data via NetFlow v1/v5/ v9 and IPFIX datagrams to a remote collector. 'sfprobe' acts as a sFlow agent and exports collected data via sFlow v5 datagrams to a remote collector. Both 'nfprobe' and 'sfprobe' apply only to 'pmacctd' and 'uacctd' daemons. 'tee' acts as a replicator for NetFlow/IPFIX/ sFlow data (also transparent); it applies to 'nfacctd' and 'sfacctd' daemons only. Plugins can be either anonymous or named; configuration directives can be either global or bound to a specific named plugin. An anonymous plugin is declared as 'plugins: mysql' whereas a named plugin is declared as 'plugins: mysql[name]'. Then, directives can be bound to such named plugin as: 'directive[name]: value'. (default: memory) KEY: plugin_pipe_size DESC: Core process and active plugins are encapsulated into different OS processes. To exchange data, they set up a communication channel structured as a circular queue (referred as pipe). This directive sets the total size, in bytes, of such queue. Its default size is set depending on the Operating System. Whenever facing heavy traffic loads, this size can be adjusted to store more data. Read INTERNALS, 'Communications between core process and plugin' section for further details. A value of 10240000 (10MB) is usually ok. KEY: plugin_buffer_size DESC: by defining the transfer buffer size, in bytes, this directive enables bufferization of data transfers between core process and active plugins. It is disabled by default which gives best feelings while testing lab environments. The value has to be <= the size defined by 'plugin_pipe_size' and keeping a ratio of 1:1000 between the two is generally a good idea. Hence, the queue will be partitioned in plugin_buffer_size/plugin_pipe_size slots. Once a slot is filled, it is delivered to the plugin while the circular queue moves to the next buffer element. For further details, read INTERNALS, 'Communications between core process and plugin' section. A value of 10240 (10KB) is usually ok. (default: 0) KEY: plugin_pipe_backlog VALUES: [0 <= value < 100] DESC: Expects the value to be a percentage. It creates a backlog of buffers on the pipe before actually releasing them to the plugin. The strategy helps optimizing inter process communications where plugins are quicker handling data than the Core process. By default backlog is disabled; as with buffering in general, this feature should be enabled with caution in lab and low-traffic environments. (default: 0) KEY: files_umask DESC: Defines the mask for newly created files (log, pid, etc.). A mask less than "002" is currently not accepted due to security reasons. (default: 077) KEY: files_uid DESC: Defines the system user id (UID) for files opened for writing (log, pid, etc.); this is indeed possible only when running the daemon as super-user; by default this is left untouched. KEY: files_gid DESC: Defines the system group id (GID) for files opened for writing (log, pid, etc.); this is indeed possible only when running the daemon as super-user; by default this is left untouched. KEY: interface (-i) [GLOBAL, NO_NFACCTD] DESC: interface on which 'pmacctd' listens. If such directive isn't supplied, a libpcap function is used to select a valid device. [ns]facctd can catch similar behaviour by employing the [ns]facctd_ip directives; also, note that this directive is mutually exclusive with 'pcap_savefile' (-I). KEY: pcap_savefile (-I) [GLOBAL, NO_NFACCTD] DESC: file in libpcap savefile format from which read data (this is in alternative to binding to an intervace). The file has to be correctly finalized in order to be read. As soon as 'pmacctd' is finished with the file, it exits (unless the 'savefile_wait' option is in place). The directive doesn't apply to [ns]facctd; to replay original NetFlow/sFlow streams, a tool like TCPreplay can be used instead. The directive is mutually exclusive with 'interface' (-i). KEY: interface_wait (-w) [GLOBAL, NO_NFACCTD] VALUES: [true|false] DESC: if set to true, this option causes 'pmacctd' to wait for the listening device to become available; it will try to open successfully the device each few seconds. Whenever set to false, 'pmacctd' will exit as soon as any error (related to the listening interface) is detected. (default: false) KEY: savefile_wait (-W) [GLOBAL, NO_NFACCTD] VALUES: [true|false] DESC: if set to true, this option will cause 'pmacctd' to wait indefinitely for a signal (ie. CTRL-C when not daemonized or 'killall -9 pmacctd' if it is) after being finished with the supplied libpcap savefile (pcap_savefile). It's particularly useful when inserting fixed amounts of data into memory tables by keeping the daemon alive. (default: false) KEY: promisc (-N) [GLOBAL, NO_NFACCTD] VALUES: [true|false] DESC: if set to true, puts the listening interface in promiscuous mode. It's mostly useful when running 'pmacctd' in a box which is not a router, for example, when listening for traffic on a mirroring port. (default: true) KEY: imt_path (-p) DESC: specifies the full pathname where the memory plugin has to listen for client queries. When multiple memory plugins are active, each one has to use its own file to communicate with the client tool. Note that placing these files into a carefully protected directory (rather than /tmp) is the proper way to control who can access the memory backend. (default: /tmp/collect.pipe) KEY: imt_buckets (-b) DESC: defines the number of buckets of the memory table which is organized as a chained hash table. A prime number is highly recommended. Read INTERNALS 'Memory table plugin' chapter for further details. KEY: imt_mem_pools_number (-m) DESC: defines the number of memory pools the memory table is able to allocate; the size of each pool is defined by the 'imt_mem_pools_size' directive. Here, a value of 0 instructs the memory plugin to allocate new memory chunks as they are needed, potentially allowing the memory structure to grow undefinitely. A value > 0 instructs the plugin to not try to allocate more than the specified number of memory pools, thus placing an upper boundary to the table size. (default: 16) KEY: imt_mem_pools_size (-s) DESC: defines the size of each memory pool. For further details read INTERNALS 'Memory table plugin'. The number of memory pools is defined by the 'imt_mem_pools_number' directive. (default: 8192). KEY: syslog (-S) VALUES: [auth | mail | daemon | kern | user | local[0-7] ] DESC: enables syslog logging, using the specified facility. (default: none, console logging) KEY: logfile DESC: enables logging to a file (bypassing syslog); expected value is a pathname (default: none, console logging) KEY: pidfile (-F) [GLOBAL] DESC: writes PID of Core process to the specified file. PIDs of the active plugins are written aswell by employing the following syntax: 'path/to/pidfile--'. This gets particularly useful to recognize which process is which on architectures where pmacct does not support the setproctitle() function. (default: none) KEY: networks_file (-n) DESC: full pathname to a file containing a list of (known/local/meaningful) networks/ASNs (one for each line, read more on the file syntax into examples/ tree). The directive is twofold: a) it allows to rewrite as zero IP addresses not included in any defined network range (ie. to avoid IP addresses external to the local domain to be accounted for); b) it is vital for network (src_net, dst_net) and ASN (src_as, dst_as) aggregations. KEY: networks_mask DESC: specifies the network mask - in bits - to apply to IP address values in L3 header. The mask is applied sistematically and before evaluating the 'networks_file' content (if any is specified). KEY: networks_cache_entries DESC: Networks Lookup Table (which is the memory structure where the 'networks_file' data is loaded) is preeceded by a Network Lookup Cache where lookup results are saved to speed up later searches. NLC is structured as an hash table, hence, this directive is aimed to set the number of buckets for the hash table. The default value should be suitable for most common scenarios, however when facing with large-scale network definitions, it is quite adviceable to tune this parameter to improve performances. A prime number is highly recommended. KEY: ports_file DESC: full pathname to a file containing a list of (known/interesting/meaningful) ports (one for each line, read more about the file syntax into examples/ tree). The directive allows to rewrite as zero port numbers not matching any port defined in the list. Indeed, this makes sense only if aggregating on either 'src_port' or 'dst_port' primitives. KEY: sql_db DESC: defines the SQL database to use. Remember that when using the SQLite3 plugin, this directive refers to the full path to the database file (default: 'pmacct', SQLite 3.x default: '/tmp/pmacct.db'). KEY: [ sql_table | print_output_file ] DESC: In SQL plugins this defines the SQL table to use (sql_table); in print plugin it defines the file to write to (print_output_file). Dynamic names are supported through the use of variables, which are computed at the moment when data is purged to the backend. The list of supported variables follows: %d The day of the month as a decimal number (range 01 to 31). %H The hour as a decimal number using a 24 hour clock (range 00 to 23). %m The month as a decimal number (range 01 to 12). %M The minute as a decimal number (range 00 to 59). %s The number of seconds since the Epoch, i.e., since 1970-01-01 00:00:00 UTC. %w The day of the week as a decimal, range 0 to 6, Sunday being 0. %W The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01. %Y The year as a decimal number including the century. SQL plugins notes: Time-related variables require 'sql_history' to be specified in order to work correctly (see 'sql_history' entry in this in this document for further information) and that the 'sql_refresh_time' setting is aligned with the 'sql_history', ie.: sql_history: 5m sql_refresh_time: 300 Furthermore, if the 'sql_table_schema' directive is not specified, tables are expected to be already in place. This is an example on how to split accounted data among multiple tables basing on the day of the week: sql_history: 1h sql_history_roundoff: h sql_table: acct_v4_%w The above directives will account data on a hourly basis (1h). Also the above sql_table definition will make: Sunday data be inserted into the 'acct_v4_0' table, Monday into the 'acct_v4_1' table, and so on. The switch between the tables will happen each day at midnight: this behaviour is ensured by the use of the 'sql_history_roundoff' directive. Ideally sql_refresh_time and sql_history values should be aligned for the dynamic tables to work; sql_refresh_time with a value smaller than sql_history is also supported; whereas the feature does not support values of sql_refresh_time greater than sql_history. Print plugin notes: If a non-dynamic filename is selected, content is overwritten to the existing one. Common notes: The maximum table name length is 64 characters. The maximum number of variables it may contain is 8. KEY: sql_table_schema DESC: full pathname to a file containing a SQL table schema. It allows to create the SQL table if it does not exist; this directive makes sense only if a dynamic 'sql_table' is in use. A configuration example where this directive could be useful follows: sql_history: 5m sql_history_roundoff: h sql_table: acct_v4_%Y%m%d_%H%M sql_table_schema: /usr/local/pmacct/acct_v4.schema In this configuration, the content of the file pointed by 'sql_table_schema' should be: CREATE TABLE acct_v4_%Y%m%d_%H%M ( [ ... PostgreSQL/MySQL specific schema ... ] ); This setup, along with this directive, are mostly useful when the dynamic tables are not closed in a 'ring' fashion (e.g., the days of the week) but 'open' (e.g., current date). KEY: sql_table_version (-v) VALUES [ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ] DESC: defines the version of the SQL table. SQL table versioning was introduced to achieve two goals: a) make tables work out-of-the-box for the SQL beginners, smaller installations and quick try-outs; and in this context b) to allow introduction of new features over time without breaking backward compatibility. For the SQL experts, the alternative to versioning is 'sql_optimize_clauses' which allows custom mix-and-match of primitives: in such a case you have to build yourself custom SQL schemas and indexes. Check in the 'sql/' sub-tree the SQL table profiles which are supported by the pmacct version you are currently using (default: 1) KEY: sql_table_type VALUES [ bgp ] DESC: optionally combined with "sql_table_version", defines one of the supported SQL table profiles. Currently this directive has to be defined to select one of the default BGP table profiles. KEY: sql_data VALUES: [ typed | unified ] DESC: this switch makes sense only when using PostgreSQL plugin; each of the pgsql scripts in the sql/ tree will create one 'unified' table and multiple 'typed' tables. The 'unified' table has IP and MAC addresses specified as standard CHAR strings, slower and not space savy but flexible; 'typed' tables sport PostgreSQL own types (inet, mac, etc.), resulting in a faster but more rigid structure. Since v6 unified mode is being discontinued leading to efficiency and simplification. (default: 'typed'). KEY: sql_host DESC: defines the SQL server IP/hostname (default: localhost). KEY: sql_user DESC: defines the username to use when connecting to the SQL server (default: pmacct). KEY: sql_passwd DESC: defines the password to use when connecting to the SQL server (default: arealsmartpwd). KEY: [ sql_refresh_time | print_refresh_time ] (-r) DESC: time interval between consecutive executions of the SQL cache scanner. The scanner purges data from the cache into the RDBMS. The value is expressed in seconds. KEY: sql_startup_delay DESC: defines the time, in seconds, the first SQL cache scan event has to be delayed. This delay is, in turn, propagated to the subsequent scans. It comes useful in two scenarios: a) so that multiple plugins can use the same 'sql_refresh_time' value, allowing them to spread the writes among the length of the time-bin; b) with NetFlow, to keep original flow start time (nfacctd_time_new: false) while enabling the sql_dont_try_update feature (for RDBMS efficiency purposes); in such a context, sql_startup_delay value should be greater (better >= 2x the value) of the NetFlow active flow timeout. (default: 0) KEY: sql_optimize_clauses VALUES: [true|false] DESC: enables the optimization of the statements sent to the RDBMS essentially allowing to a) run stripped-down variants of the default SQL tables or b) totally customized SQL tables by a free mix-and-match of the available primitives. Either case, you will need to build the custom SQL table schema and indexes. As a rule of thumb when NOT using this directive always remember to specify which default SQL table version you intend to stick to by using the 'sql_table_version' directive. (default: false) KEY: sql_history VALUES: #[m|h|d|w|M] DESC: enables historical accounting by dividing accounted data into configurable time-bins. It will use the 'stamp_inserted' (base time of the time-bin) and 'stamp_updated' (last time the time-bin was touched) fields. The supplied value defines the time slot length during which counters are accumulated. For a nice effect, it's adviceable to pair this directive with 'sql_history_roundoff'. Note that this value is fully disjoint from the 'sql_refresh_time' directive which sets the time intervals at which data has to be written to the RDBMS instead. The final effect is close to time slots in a RRD file. Examples of valid values are: '5m' - five minutes, '1h' - one hour, '4h' - four hours, '1d' - one day, '1w' - one week, '1M' - one month). KEY: [ sql_history_roundoff | print_time_roundoff ] VALUES [m,h,d,w,M] DESC: enables alignment of minutes (m), hours (h), days of month (d), weeks (w) and months (M) in print (to print_refresh_time) and SQL plugins (to sql_history and sql_refresh_time). Suppose you go with 'sql_history: 1h', 'sql_history_roundoff: m' and it's 6:34pm. Rounding off minutes gives you an hourly timeslot (1h) starting at 6:00pm; so, subsequent ones will start at 7:00pm, 8:00pm, etc. Now, you go with 'sql_history: 5m', 'sql_history_roundoff: m' and it's 6:37pm. Rounding off minutes will result in a first slot starting at 6:35pm; next slot will start at 6:40pm, and then every 5 minutes (6:45pm ... 7:00pm, etc.). 'w' and 'd' are mutually exclusive, that is: you can either reset the date to last Monday or reset the date to the first day of the month. KEY: sql_history_since_epoch VALUES [true|false] DESC: enables the use of timestamps (stamp_inserted, stamp_updated) in the standard seconds since the Epoch format. This directive requires changes to the default types for timestamp fields in the SQL schema. (default: false) MySQL: DATETIME ==> INT(8) UNSIGNED PostgreSQL: timestamp without time zone ==> bigint SQLite3: DATETIME ==> INT(8) KEY: sql_recovery_logfile DESC: enables recovery mode; recovery mechanism kicks in if the DB fails. It works by checking for the successful result of each SQL query. By default it is disabled. By using this key aggregates are recovered to the specified logfile. Data may be played later by either 'pmmyplay' or 'pmpgplay' tools. Each time pmacct package is updated it's good rule not continue writing old files but start a new ones. Each plugin instance has to write to a different logfile in order to avoid inconsistencies over data. And, finally, the maximum size for a logfile is set to 2Gb: if the logfile reaches such size, it's automatically rotated (in a way similar to logrotate: old file is renamed, appending a little sequential integer to it, and a new file is started). See INTERNALS 'Recovery modes' section for details about this topic. SQLite 3.x note: because the database is file-based it's quite useless to have a logfile, thus this feature is not supported. However, note that the 'sql_recovery_backup_host' directive allows to specify an alternate SQLite 3.x database file. KEY: sql_recovery_backup_host DESC: enables recovery mode; recovery mechanism kicks in if DB fails. It works by checking for the successful result of each SQL query. By default it is disabled. By using this key aggregates are recovered to a secondary DB. See INTERNALS 'Recovery modes' section for details about this topic. SQLite 3.x note: the plugin uses this directive to specify a the full path to an alternate database file (e.g., because you have multiple file system on a box) to use in the case the primary backend fails. KEY: sql_max_writers DESC: sets the maximum number of concurrent writer processes the SQL plugin is allowed to fire. This setting allows pmacct to degrade gracefully during major database outages. The value is splitted as follows: up to N-1 concurrent processes will have full functionalities; Nth process will go for a recovery mechanism (sql_recovery_logfile, sql_recovery_backup_host), if any is configured; all processes beyond Nth will stop managing data (so, data will be lost at this stage) and an error message is printed out. Triggers (sql_trigger_exec) will continue working in any case. (default: 10) KEY: [ sql_cache_entries | print_cache_entries ] DESC: SQL and print plugins sport a Plugin Memory Cache (PMC) meant to accumulate bytes/packets counters until next purging event (for further insights take a look to 'sql_refresh_time'). This directive sets the number of PMC buckets. Default value is suitable for most common scenarios, however when facing large-scale networks, it's higly recommended to carefully tune this parameter to improve performances. Use a prime number of buckets. (default: sql_cache_entries: 32771, print_cache_entries: 16411) KEY: sql_dont_try_update VALUES: [true|false] DESC: by default pmacct uses an UPDATE-then-INSERT mechanism to write data to the RDBMS; this directive instructs pmacct to use a more efficient INSERT-only mechanism. This directive is useful for gaining performances by avoiding UPDATE queries. Using this directive puts some timing constraints, specifically sql_history == sql_refresh_time, otherwise it may lead to duplicate entries and, potentially, loss of data. When used in nfacctd it also requires nfacctd_time_new to be enabled. (default: false) KEY: sql_use_copy VALUES: [true|false] DESC: instructs the plugin to build non-UPDATE SQL queries using COPY (in place of INSERT). While providing same functionalities of INSERT, COPY is also more efficient. To have effect, this directive requires 'sql_dont_try_update' to be set to true. It applies to PostgreSQL plugin only. (default: false) KEY: sql_delimiter DESC: If sql_use_copy is true, uses the supplied character as delimiter. This is thought in cases where the default delimiter is part of any of the supplied strings to be inserted into the database. (default: ',') KEY: sql_multi_values DESC: enables the use of multi-values INSERT statements. The value of the directive is intended to be the size (in bytes) of the multi-values buffer. The directive applies only to MySQL and SQLite 3.x plugins. Inserting many rows at the same time is much faster (many times faster in some cases) than using separate single-row INSERT statements. It's adviceable to check the size of this pmacct buffer against the size of the corresponding MySQL buffer (max_allowed_packet). (default: none) KEY: [ sql_trigger_exec | print_trigger_exec ] DESC: defines the executable to be launched at fixed time intervals to post-process aggregates; in SQL plugins, intervals are specified by the 'sql_trigger_time' directive; if no interval is supplied 'sql_refresh_time' value is used instead: this will result in a trigger being fired each purging event. A number of environment variables are set in order to allow the trigger to take actions; take a look to docs/TRIGGER_VARS to check them out. In the print plugin a simpler implementation is made: triggers can be fired at each 'print_refresh_time' only and no environment variables are passed over to the executable. KEY: sql_trigger_time VALUES: #[m|h|d|w|M] DESC: specifies time interval at which the executable specified by 'sql_trigger_exec' has to be launched; if no executables are specified, this key is simply ignored. Values need to be in the 'sql_history' directive syntax (for example, valid values are '5m', '1h', '4h', '1d', '1w', '1M'; eg. if '1h' is selected, the executable will be fired each hour). KEY: sql_preprocess DESC: allows to process aggregates (via a comma-separated list of conditionals and checks) while purging data to the RDBMS thus resulting in a powerful selection tier; aggregates filtered out may be just discarded or saved through the recovery mechanism (if enabled). The set of available preprocessing directives follows: KEY: qnum DESC: conditional. Subsequent checks will be evaluated only if the number of queries to be created during the current cache-to-DB purging event is '>=' qnum value. KEY: minp DESC: check. Aggregates on the queue are evaluated one-by-one; each object is marked valid only if the number of packets is '>=' minp value. KEY: minf DESC: check. Aggregates on the queue are evaluated one-by-one; each object is marked valid only if the number of flows is '>=' minf value. KEY: minb DESC: check. Aggregates on the queue are evaluated one-by-one; each object is marked valid only if the bytes counter is '>=' minb value. An interesting idea is to set its value to a fraction of the link capacity. Remember that you have also a timeframe reference: the 'sql_refresh_time' seconds. For example, given the following parameters: Link Capacity = 8Mbit/s, THreshold = 0.1%, TImeframe = 60s minb = ((LC / 8) * TI) * TH -> ((8Mbit/s / 8) * 60s) * 0.1% = 60000 bytes. Given a 8Mbit link, all aggregates which have accounted for at least 60Kb of traffic in the last 60 seconds, will be written to the DB. KEY: maxp DESC: check. Aggregates on the queue are evaluated one-by-one; each object is marked valid only if the number of packets is '<' maxp value. KEY: maxf DESC: check. Aggregates on the queue are evaluated one-by-one; each object is marked valid only if the number of flows is '<' maxf value. KEY: maxb DESC: check. Aggregates on the queue are evaluated one-by-one; each object is marked valid only if the bytes counter is '<' maxb value. KEY: maxbpp DESC: check. Aggregates on the queue are evaluated one-by-one; each object is marked valid only if the number of bytes per packet is '<' maxbpp value. KEY: maxppf DESC: check. Aggregates on the queue are evaluated one-by-one; each object is marked valid only if the number of packets per flow is '<' maxppf value. KEY: minbpp DESC: check. Aggregates on the queue are evaluated one-by-one; each object is marked valid only if the number of bytes per packet is '>=' minbpp value. KEY: minppf DESC: check. Aggregates on the queue are evaluated one-by-one; each object is marked valid only if the number of packets per flow is '>=' minppf value. KEY: fss DESC: check. Enforces flow (aggregate) size dependent sampling, computed against the bytes counter and returns renormalized results. Aggregates which have collected more than the supplied 'fss' threshold in the last time window (specified by the 'sql_refresh_time' configuration key) are sampled. Those under the threshold are sampled with probability p(bytes). The method allows to get much more accurate samples compared to classic 1/N sampling approaches, providing an unbiased estimate of the real bytes counter. It would be also adviceable to hold the the equality 'sql_refresh_time' = 'sql_history'. For further references: http://www.research.att.com/projects/flowsamp/ and specifically to the papers: N.G. Duffield, C. Lund, M. Thorup, "Charging from sampled network usage", http://www.research.att.com/~duffield/pubs/DLT01-usage.pdf and N.G. Duffield and C. Lund, "Predicting Resource Usage and Estimation Accuracy in an IP Flow Measurement Collection Infrastructure", http://www.research.att.com/~duffield/pubs/p313-duffield-lund.pdf KEY: fsrc DESC: check. Enforces flow (aggregate) sampling under hard resource constraints, computed against the bytes counter and returns renormalized results. The method selects only 'fsrc' flows from the set of the flows collected during the last time window ('sql_refresh_time'), providing an unbiasied estimate of the real bytes counter. It would be also adviceable to hold the equality 'sql_refresh_time' = 'sql_history'. For further references: http://www.research.att.com/projects/flowsamp/ and specifically to the paper: N.G. Duffield, C. Lund, M. Thorup, "Flow Sampling Under Hard Resource Constraints", http://www.research.att.com/~duffield/pubs/DLT03-constrained.pdf KEY: usrf DESC: action. Applies the renormalization factor 'usrf' to counters of each aggregate. Its use is suitable for use in conjunction with uniform sampling methods (for example simple random - e.g. sFlow, 'sampling_rate' directive or simple systematic - e.g. sampled NetFlow by Cisco and Juniper). The factor is applied to recovered aggregates also. It would be also adviceable to hold the equality 'sql_refresh_time' = 'sql_history'. Before using this action to renormalize counters generated by sFlow, take also a read of the 'sfacctd_renormalize' key. KEY: adjb DESC: action. Adds (or subtracts) 'adjb' bytes to the bytes counter multiplied by the number of packet in each aggregate. This is a particularly useful action when - for example - fixed lower (link, llc, etc.) layer sizes need to be included into the bytes counter (as explained by Q7 in FAQS document). KEY: recover DESC: action. If previously evaluated checks have marked the aggregate as invalid, a positive 'recover' value makes the packet to be handled through the recovery mechanism (if enabled). KEY: sql_preprocess_type VALUES: [any|all] DESC: When more checks are to be evaluated, this directive tells whether aggregates on the queue are valid if they just match one of the checks (any) or all of them (all) (default: any). KEY: print_markers VALUES: [true|false] DESC: Enables the use of START/END markers each time data is written to 'stdout'. Start marker returns additional information about current time-bin and configured refresh time (default: false). KEY: print_output VALUES: [ formatted | csv ] DESC: Defines the print plugin output format. 'formatted' enables tabular output; 'csv' is to enable comma-separated values format, suitable for injection into 3rd party tools. (default: formatted) KEY: [ print_num_protos | sql_num_protos ] VALUES: [true|false] DESC: Defines whether IP protocols (ie. tcp, udp) should be looked up and presented in string format or left numerical. The default is to look protocol names up. (default: false) KEY: sql_num_hosts VALUES: [true|false] DESC: Defines whether IP addresses should be left numerical (in network bytes ordering) or converted into human-readable strings. Applies to MySQL and SQLite plugins only and assumes the INET_NTOA() function is defined in the RDBMS (which for MySQL is always the case, while for SQLite it is not by default). Since INET_NTOA() is used, unless redefined to some custom-made variant, this feature works for IPv4 addresses only. Default setting is to convert IP addresses into strings. (default: false) KEY: [ nfacctd_port | sfacctd_port ] (-l) [GLOBAL, NO_PMACCTD] DESC: defines the UDP port where to bind nfacctd (nfacctd_port) and sfacctd (sfacctd_port) daemons (default: nfacctd_port: 2100, sfacctd_port: 6343). KEY: [ nfacctd_ip | sfacctd_ip ] (-L) [GLOBAL, NO_PMACCTD] DESC: defines the IPv4/IPv6 address where to bind the nfacctd (nfacctd_ip) and sfacctd (sfacctd_ip) daemons (default: all interfaces). KEY: [ nfacctd_allow_file | sfacctd_allow_file ] [GLOBAL, NO_PMACCTD] DESC: full pathname to a file containing the list of IPv4/IPv6 addresses (one for each line) allowed to send packets to the daemon. Current syntax does not implement network masks but individual IP addresses only. The Allow List is intended to be small; firewall rules should be preferred to long ACLs. (default: allow all) KEY: nfacctd_time_secs [GLOBAL, NO_PMACCTD] VALUES: [true|false] DESC: makes 'nfacctd' expect times included in NetFlow header to be in seconds rather than msecs. This knob makes sense for NetFlow up to v8 - as in NetFlow v9 and IPFIX different fields are reserved for secs and msecs timestamps, increasing collector awareness. (default: false) KEY: nfacctd_time_new [GLOBAL, NO_PMACCTD] VALUES: [true|false] DESC: makes 'nfacctd' to ignore timestamps included in NetFlow header and build new ones. This gets particularly useful to assign flows to time-bins based on the flow arrival time at the collector rather than the flow start time. An application for it is when historical accounting is enabled ('sql_history') and an INSERT-only mechanism is in use ('sql_dont_try_update', 'sql_use_copy'). (default: false) KEY: [ nfacctd_as_new | sfacctd_as_new | pmacctd_as | uacctd_as ] [GLOBAL] VALUES: [ false | (true|file) | bgp | fallback ] DESC: When 'false', it instructs nfacctd and sfacctd to populate 'src_as', 'dst_as', 'peer_src_as' and 'peer_dst_as' primitives from NetFlow and sFlow datagram respectively; when 'true' ('file' being an alias of 'true') it instructs nfacctd and sfacctd to generate 'src_as' and 'dst_as' (only! ie. no peer-AS) by looking up source and destination IP addresses against a networks_file. When 'bgp' is specified, ASNs are looked up against the BGP RIB of the peer from which the NetFlow datagram was received (see also bgp_agent_map directive). When 'fallback' is specified, lookup is done against the winning longest match lookup method (sFlow/NetFlow <= BGP), which can be different for source and destination IP prefix. Intuitively if 'fallback' is specified, IS-IS/IGP daemon is enabled and IGP is the winning method then no BGP information will be attached to the prefixes. In pmacctd and uacctd 'false' (maintained for backward compatibility), 'true' and 'file' expect a 'networks_file' to be defined; 'bgp' just works as described previously for nfacctd and sfacctd; 'fallback' is mapped to 'bgp' since no export protocol lookup method is available. (default: false) KEY: [ nfacctd_net | sfacctd_net | pmacctd_net | uacctd_net ] VALUES: [ netflow | sflow | mask | file | igp | bgp | fallback ] DESC: Determines the method for performing IP prefix aggregation - hence directly influencing 'src_net', 'dst_net', 'src_mask', 'dst_mask' and 'peer_dst_ip' primitives. 'netflow' and 'sflow' get values from NetFlow and sFlow protocols respectively; these keywords are only valid in nfacctd, sfacctd. 'mask' applies a defined networks_mask; 'file' selects a defined networks_file; 'igp' and 'bgp' source values from IGP/IS-IS daemon and BGP daemon respectively. Default behaviour under pmacctd and uacctd is for backward compatibility: 'mask' and 'file' are turned on if a networks_mask and a networks_file are respectively specified by configuration. If they both are defined, the outcome will be the intersection of their definitions. 'fallback' behaves in a longest-match-wins fashion: in nfacctd and sfacctd lookup is done against sFlow/NetFlow protocol, IGP and BGP (sFlow/NetFlow < IGP <= BGP) whereas in pmacctd and uacctd lookup is done against IGP and BGP only (IGP <= BGP). (default: nfacctd: 'netflow'; sfacctd: 'sflow'; pmacctd and uacctd: 'mask', 'file') KEY: [ nfacctd_mcast_groups | sfacctd_mcast_groups ] [GLOBAL, NO_PMACCTD] DESC: defines one or more IPv4/IPv6 multicast groups to be joined by the daemon. If more groups are supplied, they are expected comma separated. A maximum of 20 multicast groups may be joined by a single daemon instance. Some OS (noticeably Solaris -- seems) may also require an interface to bind to which - in turn - can be supplied declaring an IP address ('nfacctd_ip' key). KEY: [ nfacctd_disable_checks | sfacctd_disable_checks ] [GLOBAL, NO_PMACCTD] VALUES: [true|false] DESC: both nfacctd and sfacctd check health of incoming NetFlow/sFlow datagrams - actually this is limited to just verifying sequence numbers progression. You may want to disable such feature because of non-standard implementations. By default checks are enabled (default: false) KEY: nfacctd_sql_log [NO_PMACCTD] VALUES: [true|false] DESC: under the NetFlow accounting daemon (nfacctd), it makes the SQL plugin to use a) NetFlow's First Switched timestamp as "stamp_inserted" value and b) NetFlow's Last Switched timestamp as "stamp_updated" value. By not encapsulating traffic into fixed timeslots, this directive is meant to be employed in scenarios in which it's required to log each micro-flow in the SQL database. It's not compatible with nfacctd_time_new and sql_recovery_logfile directives. It can be applied in conjunction with sql_history_since_epoch directive. (default: false) KEY: pre_tag_map [GLOBAL] DESC: full pathname to a file containing tag mappings. Enables the use of Pre-Tagging. When used in nfacctd and sfacctd this map allows (a) to translate some NetFlow/sFlow packet fields (for example 'ip': agent IP address, 'in': Input interface, 'out': Output interface) and (b) to match filter expressions (tcpdump syntax) into an ID (in the range 1-4294967295). In pmacctd it allows just the (b). Take a look to the examples/ sub-tree for all supported keys and detailed examples. Pre-Tagging is enforced in the Core Process shortly after packet/flow/sample collection. KEY: pre_tag_map_entries [GLOBAL] DESC: defines the maximum number of entries the Pre-Tagging map can contain. The default value is suitable for most scenarios, though tuning it could be required either to save on memory or to allow for more entries. Refer to the specific map directives documentation in this file to see which are affected by this setting. (default: 384) KEY: refresh_maps [GLOBAL] VALUES: [true|false] DESC: when enabled, this directive allows to reload map files without restarting the daemon instance. For example, it may result particularly useful to reload Pre-Tagging entries or Networks map in order to reflect some change in the network. After having modified the map files, a SIGUSR2 has to be sent (e.g.: in the simplest case "killall -USR2 pmacctd") to the daemon to notify the change. If such signal is sent to the daemon and this directive is not enabled, the signal is silently discarded. The Core Process is in charge of processing the Pre-Tagging map; plugins are devoted to Networks and Ports maps instead. Then, because signals can be sent either to the whole daemon (killall) or to just a specific process (kill), this mechanism also offers the advantage to elicit local reloads. (default: true) KEY: pre_tag_filter, pre_tag2_filter [NO_GLOBAL] VALUES: [0-4294967295] DESC: it expects one or more tags (when multiple tags are supplied, they need to be comma separated and a logical OR is used in the evaluation phase) as value and allows to filter aggregates basing upon their Pre Tag ID: in case of a match, the aggregate is delivered to the plugin. This directive has to be bound to a plugin (that is, it cannot be global) and is suitable to split tagged data among the active plugins. While the IDs need to be in the range 1-4294967295, this directive also allows to specify an ID '0' - which intercepts non-tagged aggregates - thus allowing to split tagged traffic from untagged one. It also allows negations by pre-pending a minus sign to the tag value (ie. '-6' would send everything but traffic tagged as '6') and ranges (ie. '10-20' would send over traffic tagged in the range 10..20) and combination of these. This directive makes sense if coupled with 'pre_tag_map'; it could be used in conjunction with 'aggregate_filter'. KEY: post_tag VALUES: [1-4294967295] DESC: it expects an ID as its value. Enables the use of Post-Tagging. Once the aggregate has passed all filters and is on the final way to the plugin, this directive allows to statically tag it using the specified value. The tag is sticked to the 'tag' aggregate field. KEY: sampling_rate VALUES: [>= 1] DESC: enables packet sampling. It expects a number which is the mean ratio of packets to be sampled (1 out of N). The currently implemented sampling algorithm is a simple randomic one. If using any SQL plugin, look also to the powerful 'sql_preprocess' layer and the more advanced sampling choices it offers: they will allow to deal with advanced sampling scenarios (e.g. probabilistic methods). Finally, note that this 'sampling_rate' directive can be renormalized by using the 'usrf' action of the 'sql_preprocess' layer. (default: no sampling) KEY: sampling_map [GLOBAL, NO_PMACCTD, NO_UACCTD] DESC: full pathname to a file containing traffic sampling mappings. It is mainly meant to be used in conjunction with nfacctd and sfacctd for the purpose of fine-grained reporting of sampling rates circumventing bugs and issues in router operating systems. Renormalization must be enabled (nfacctd_renormalize or sfacctd_renormalize set to true) in order for the feature to work. If a specific router is not defined in the map, the sampling rate advertised by the router itself is applied. Take a look to the examples/ sub-tree 'sampling.map.example' for all supported keys and detailed examples. KEY: [ pmacctd_force_frag_handling | uacctd_force_frag_handling ] [GLOBAL, NO_NFACCTD] VALUES: [true|false] DESC: forces 'pmacctd' to join together IPv4/IPv6 fragments: 'pmacctd' does this only whether any of the port primitives are selected (src_port, dst_port, sum_port); in fact, when not dealing with any upper layer primitive, fragments are just handled as normal packets. However, available filtering rules ('aggregate_filter', Pre-Tag filter rules) will need such functionality enabled whether they need to match TCP/UDP ports. So, this directive aims to support such scenarios. (default: false) KEY: [ pmacctd_frag_buffer_size | uacctd_frag_buffer_size ] [GLOBAL, NO_NFACCTD] DESC: defines the maximum size of the fragment buffer. The value is expeced in bytes (default: 4 Mb). KEY: [ pmacctd_flow_buffer_size | uacctd_flow_buffer_size ] [GLOBAL, NO_NFACCTD] DESC: defines the maximum size of the flow buffer. This is an upper limit to avoid unlimited growth of the memory structure. This value has to scale accordingly to the link traffic rate. It is expected in bytes (default: 16 Mb). KEY: [ pmacctd_flow_buffer_buckets | uacctd_flow_buffer_buckets ] [GLOBAL, NO_NFACCTD] DESC: defines the number of buckets of the flow buffer - which is organized as a chained hash table. To exploit better performances, the table should be reasonably flat. This value has to scale to higher power of 2 accordingly to the link traffic rate. For example, it has been reported that a value of 65536 works just fine under full 100Mbit load (default: 256). KEY: [ pmacctd_conntrack_buffer_size | uacctd_conntrack_buffer_size ] [GLOBAL, NO_NFACCTD] DESC: defines the maximum size of the connection tracking buffer. The value is expected in bytes (default: 8 Mb). KEY: [ pmacctd_flow_lifetime | uacctd_flow_lifetime ] [GLOBAL, NO_NFACCTD] DESC: defines how long a flow could remain inactive (ie. no packets belonging to such flow are received) before considering it expired. The value is expected in seconds. (default: 60 secs) KEY: [ pmacctd_ext_sampling_rate | uacctd_ext_sampling_rate | nfacctd_ext_sampling_rate | sfacctd_ext_sampling_rate ] [GLOBAL] flags pmacctd that captured traffic is being sampled at the specified rate. Such rate can then be renormalized by using 'pmacctd_renormalize' or otherwise is propagated by the NetFlow/sFlow probe plugins, if any of them is activated. External sampling might be performed by capturing frameworks the daemon is linked against (ie. PF_RING, ULOG) or appliances (ie. sampled packet mirroring). In nfacctd and sfacctd daemons this directive can be used to tackle corner cases, ie. sampling rate reported by the NetFlow/sFlow agent is missing or not correct. (default: no sampling) KEY: [ sfacctd_renormalize | nfacctd_renormalize | pmacctd_renormalize | uacctd_renormalize ] (-R) VALUES: [true|false] DESC: automatically renormalizes byte/packet counters value basing on information acquired from either the NetFlow data unit or sFlow packet. In particular, it allows to deal with scenarios in which multiple interfaces have been configured at different sampling rates. The feature also calculates an effective sampling rate (sFlow only) which could differ from the configured one - expecially at high rates - because of various losses. Such estimated rate is then used for renormalization purposes. (default: false) KEY: classifiers [GLOBAL, NO_NFACCTD, NO_SFACCTD] DESC: full path to a spool directory containing the packet classification patterns (expected as .pat or .so files; files with different extensions and subdirectories will be just ignored). This feature enables packet/flow classification against application layer data (that is, the packet payload) and based either over regular expression (RE) patterns (.pat) or external/pluggable C modules (.so). Patterns are loaded in filename alphabetic order and will be evaluated in the same order while classifying packets. Supported RE patterns are those from the great L7-filter project, which is a new packet classifier for Linux kernel, and are avilable for download at: http://sourceforge.net/projects/l7-filter/ (then point to the Protocol definitions archive). Existing SO patterns are available at: http://www.pmacct.net/classification/ . This configuration directive should be specified whenever the 'class' aggregation method is in use (ie. 'aggregate: class'). It's supported only by pmacctd. KEY: sql_aggressive_classification VALUES: [true|false] DESC: usually 5 to 10 packets are required to classify a stream by the 'classifiers' feature. Until the flow is not classified, such packets join the 'unknown' class. As soon as classification engine is successful identifying the stream, the packets are moved to their correct class if they are still cached by the SQL plugin. This directive delays 'unknown' streams - but only those which would have still chances to be correctly classified - from being purged to the DB but only for a small number of consecutive sql_refresh_time slots. It is incompatible with sql_dont_try_update and sql_use_copy directives (default: false) KEY: sql_locking_style DESC: defines the locking style for the SQL table. Supported values are: "table", the plugin will lock the entire table when writing data to the DB. It serializes access to the table whenever multiple plugins need to access it simultaneously. Slower but light and safe, ie. no risk for deadlocks and transaction-friendly; "row", the plugin will lock only the rows it needs to UPDATE/DELETE. It results in better overral performances but has some noticeable drawbacks in dealing with transactions and making the UPDATE-then-INSERT mechanism work smoothly. The user need to take cares on his own; a simple and safe enough protection can be tagging uniquely data coming from each plugin (see pre_tag_map, post_tag). In MySQL the use of an InnoDB table is mandatory. This config directive currently applies only to PostgreSQL and MySQL plugins. (default: table) KEY: classifier_tentatives [GLOBAL, NO_NFACCTD, NO_SFACCTD] DESC: number of tentatives to classify a stream. Usually 5 "full" (ie. carrying payload) packets are sufficient to classify an uni-directional flow. This is the default value. However classifiers not basing on the payload content may require a different (maybe larger) number of tentatives. (default: 5) KEY: classifier_table_num [GLOBAL, NO_NFACCTD, NO_SFACCTD] DESC: the maximum number of classifiers (SO + RE) that could be loaded runtime. The default number is usually ok, but some "dirty" uses of classifiers might require more entries. (default: 256) KEY: nfprobe_timeouts DESC: allows to tune a set of timeouts to be applied over collected packets. The value is expected in the following form: 'name=value:name=value:...'. The set of supported timeouts and their default values are listed below: tcp (generic tcp flow life) 3600 tcp.rst (TCP RST flow life) 120 tcp.fin (TCP FIN flow life) 300 udp (UDP flow life) 300 icmp (ICMP flow life) 300 general (generic flow life) 3600 maxlife (maximum flow life) 604800 expint (expiry interval) 60 KEY: nfprobe_hoplimit VALUES: [1-255] DESC: value of TTL for the newly generated NetFlow datagrams. (default: 0, leave default OS settings) KEY: nfprobe_maxflows DESC: maximum number of flows that can be tracked simultaneously. (default: 8192) KEY: nfprobe_receiver DESC: defines the remote IP address/hostname and port to which NetFlow dagagrams are to be exported. The value is expected to be in the usual form 'address:port'. (default: 127.0.0.1:2100) KEY: nfprobe_source_ip DESC: defines the local IP address from which NetFlow dagagrams are to be exported. Only a numerical IPv4/IPv6 address is expected. The supplied IP address is required to be already configured on one of the interfaces. This parameter is also required for graceful encoding of NetFlow v9 and IPFIX option scoping. (default: IP address is selected by the OS) KEY: nfprobe_version VALUES: [5,9,10] DESC: version of outgoing NetFlow datagrams. NetFlow v5/v9 and IPFIX (v10) are supported. NetFlow v5 features a fixed record structure and if not specifying an 'aggregate' directive it gets populated as much as possible; NetFlow v9 and IPFIX feature a dynamic template-based structure instead and by default it is populated as: 'src_host, dst_host, src_port, dst_Port, proto, tos'. (default: 5) KEY: nfprobe_engine DESC: allows to define Engine ID and Engine Type fields. It applies only to NetFlow v5/v9 and IPFIX. In NetFlow v9/IPFIX, the supplied value fills last two bytes of SourceID field. Expects two non-negative numbers, up to 255 each and separated by the ":" symbol. It also allows a collector to distinguish between distinct probe instances running on the same box; this is also important for letting NetFlow v9/IPFIX templates to work correctly: in fact, template IDs get automatically selected only inside single daemon instances. (default: 0:0) KEY: [ nfprobe_peer_as | sfprobe_peer_as ] VALUES: [true|false] DESC: includes peer-AS rather than origin-AS as part of the NetFlow/sFlow export. Requirements to enable this feature are: a) one of the nfacctd_as_new/sfacctd_as_new/pmacctd_as/uacctd_as set to 'bgp' and b) a fully functional BGP daemon (bgp_daemon). (default: false) KEY: [ nfprobe_ipprec | sfprobe_ipprec ] DESC: marks self-originated NetFlow (nfprobe) and sFlow (sfprobe) messages with the supplied IP precedence value. (default: 0) KEY: [ nfprobe_direction | sfprobe_direction ] VALUES: [in,out,tag,tag2] DESC: defines traffic direction. Can be statically defined via 'in' and 'out' keywords. It can also be dynamically determined via lookup to either 'tag' or 'tag2' values. Tag value of 1 will be mapped to 'in' direction, whereas tag value of 2 will be mapped to 'out'. The idea underlying tag lookups is that pre_tag_map supports, among the other features, 'filter' matching against a supplied tcpdump-like filter expression; doing so against L2 primitives (ie. source or destination MAC addresses) allows to dynamically determine traffic direction (see example at 'examples/pretag.map.example') (default: none) KEY: [ nfprobe_ifindex | sfprobe_ifindex ] VALUES: [tag,tag2,<1-4294967295>] DESC: associates an interface index (ifIndex) to a given nfprobe or sfprobe plugin. This is meant as an add-on to [ns]probe_direction directive, ie. when multiplexing mirrored traffic from different sources on the same interface (ie. split by VLAN). Can be statically defined via a 32-bit integer or semi-dynamically determined via lookup to either 'tag' or 'tag2' values (read full elaboration on [ns]probe_direction directive). This definition will be also always overridden whenever the ifIndex can be determined dynamically (ie. via ULOG framework). KEY: sfprobe_receiver DESC: defines the remote IP address/hostname and port to which sFlow dagagrams are to be exported. The value is expected to be in the usual form 'address:port'. (default: 127.0.0.1:6343) KEY: sfprobe_agentip DESC: sets the value of agentIp field inside the sFlow datagram header. KEY: sfprobe_agentsubid DESC: sets the value of agentSubId field inside the sFlow datagram header. KEY: sfprobe_ifspeed DESC: statically associates an interface speed to a given sfprobe plugin. (default: 100000000) KEY: bgp_daemon VALUES: [true|false] DESC: enables the skinny BGP daemon thread. This feature requires the package to be supporting multi- threading (--enable-threads). Neighbors are not defined explicitely via a piece of configuration (see bgp_daemon_max_peers directive); also, for security purposes, the daemon doesn't implement outbound BGP UPDATE messages and acts passively (ie. it never establishes a connection to a remote peer but waits for incoming connections); upon receipt of a BGP OPEN message, the local daemon presents itself as belonging to the same AS number and supporting same BGP capabilities (ie. MP-BGP, 4-bytes ASNs) as the remote peer. Per-peer RIBs are maintained basing on the IP address of the peer (and for clarity not its BGP Router-ID). (default: false) KEY: bgp_daemon_ip DESC: binds the BGP daemon to a specific interface. Expects as value an IPv4 address. The same is used as BGP Router-ID to its peers - otherwise a dumb "1.2.3.4" value is presented. Setting this directive is highly adviced. (default: 0.0.0.0) KEY: bgp_daemon_port DESC: binds the BGP daemon to a port different from the standard BGP port: 179/tcp. (default: 179) KEY: bgp_daemon_ipprec DESC: marks self-originated BGP messages with the supplied IP precedence value. (default: 0) KEY: bgp_daemon_max_peers DESC: sets the maximum number of neighbors the BGP daemon can peer to. Upon reaching of the limit, no more BGP sessions can be established. Differently from routers BGP neighbors don't need to be defined explicitely one-by-one rather an upper boundary to the number of neighbors applies. pmacctd, uacctd daemons are limited to only two BGP peers (in a primary/backup fashion, see bgp_agent_map); such hardcoded limit is imposed as the only scenarios supported in conjunction with the BGP daemon are as NetFlow/sFlow probes on-board software routers and firewalls. (default: 10) KEY: bgp_daemon_msglog VALUES: [true|false] DESC: enables BGP messages logging: as this can get easily verbose, it is intended for debug and troubleshooting purposes only. (default: false) KEY: bgp_aspath_radius DESC: cuts down AS-PATHs to the specified number of ASN hops. If the same ASN is repeated multiple times (ie. as effect of prepending), each of them is regarded as one hop. By default AS-PATHs are left intact unless reaching the maximum length of the buffer (128 chars). KEY: [ bgp_stdcomm_pattern | bgp_extcomm_pattern ] DESC: filters BGP standard/extended communities against the supplied pattern. The underlying idea is that many communities can be attached to a prefix; some of these can be of little or no interest for the accounting task; this feature allows to select only the relevant ones. By default the list of communities is left intact until reaching maximum length of the buffer (96 chars). The filter does substring matching, ie. 12345:64 will match communities in the ranges 64-64, 640-649, 6400-6499 and 64000-64999. The '.' symbol can be used to wildcard a pre-defined number of characters, ie. 12345:64... will match community values in the range 64000-64999 only. KEY: [ bgp_stdcomm_pattern_to_asn ] DESC: filters BGP standard communities against the supplied pattern. The algorithm employed is the same as for the bgp_stdcomm_pattern directive: read implementation details there. The first matching community is taken and split using the ':' symbol as delimiter. The first part is mapped onto the peer AS field while the second is mapped onto the origin AS field. The aim of this directive is to deal with IP prefixes on the own address space, ie. statics or connected redistributed in BGP. Example: BGP standard community XXXXX:YYYYY is mapped as: Peer-AS=XXXXX, Origin-AS=YYYYY. KEY: bgp_peer_as_skip_subas VALUES: [true|false] DESC: When determining the peer AS (source and destination), skip potential confederated sub-AS and report the first ASN external to the routing domain. When enabled if no external ASNs are found on the AS-PATH except the confederated sub-ASes, the first sub-AS is reported. (default: false) KEY: bgp_peer_src_as_type VALUES: [ netflow | sflow | map | bgp ] DESC: Defines the method to use to map incoming traffic to a source peer ASN. "map" selects a map, reloadable at runtime, specified by the bgp_peer_src_as_map directive (refer to it for further information); "bgp" implements naive BGP RIB lookups. BGP lookups assume traffic is symmtric, which is often not the case, affecting their accuracy. (default: netflow, sflow) KEY: bgp_peer_src_as_map DESC: full pathname to a file containing source peer AS mappings. The AS can be mapped to one or a combination of: ifIndex, source MAC address and BGP next-hop (query against the BGP RIB to look up the source IP prefix). This is sufficient to model popular tecniques for both public and private BGP peerings. Number of map entries (by default 384) can be modified via pre_tag_map_entries. Sample map in 'examples/peers.map.example'. KEY: bgp_src_std_comm_type VALUES: [ bgp ] DESC: Defines the method to use to map incoming traffic to a set of standard communities. Only naive BGP RIB lookups are currenntly supported. BGP lookups assume traffic is symmtric, which is often not the case, affecting their accuracy. KEY: bgp_src_ext_comm_type VALUES: [ bgp ] DESC: Defines the method to use to map incoming traffic to a set of extended communities. Only naive BGP RIB lookups are currenntly supported. BGP lookups assume traffic is symmtric, which is often not the case, affecting their accuracy. KEY: bgp_src_as_path_type VALUES: [ bgp ] DESC: Defines the method to use to map incoming traffic to an AS-PATH. Only naive BGP RIB lookups are currenntly supported. BGP lookups assume traffic is symmtric, which is often not the case, affecting their accuracy. KEY: bgp_src_local_pref_type VALUES: [ map | bgp ] DESC: Defines the method to use to map incoming traffic to a local preference. Only naive BGP RIB lookups are currenntly supported. BGP lookups assume traffic is symmtric, which is often not the case, affecting their accuracy. KEY: bgp_src_local_pref_map DESC: full pathname to a file containing source local preference mappings. The LP value can be mapped to one or a combination of: ifIndex, source MAC address and BGP next-hop (query against the BGP RIB to look up the source IP prefix). Number of map entries (by default 384) can be modified via pre_tag_map_entries. Sample map in 'examples/lpref.map.example'. KEY: bgp_src_med_type VALUES: [ map | bgp ] DESC: Defines the method to use to map incoming traffic to a MED value. Only naive BGP RIB lookups are currenntly supported. BGP lookups assume traffic is symmtric, which is often not the case, affecting their accuracy. KEY: bgp_src_med_map DESC: full pathname to a file containing source MED (Multi Exit Discriminator) mappings. The MED value can be mapped to one or a combination of: ifIndex, source MAC address and BGP next-hop (query against the BGP RIB to look up the source IP prefix). Number of map entries (by default 384) can be modified via pre_tag_map_entries. Sample map in 'examples/med.map.example'. KEY: bgp_agent_map DESC: full pathname to a file to map source IP address of NetFlow agents and AgentID of sFlow agents to source IP address or Router ID of BGP peers. This is particularly aimed as a resource-savy alternative to have N agents but only a restricted number of BGP peers (ie. a BGP RR), in case of simpler topologies (ie. hub-and- spoke) or scenarios (ie. single- homed). It can also be viewed as a temporary workaround to certain situations. pmacctd, uacctd daemons are required to use a bgp_agent_map with up to two "catch-all" entries - working in a primary/backup fashion (see agent_to_peer.map in the examples section): this is because these daemons do not have a NetFlow/sFlow source address to match to. Number of map entries (by default 384) can be modified via pre_tag_map_entries. KEY: bgp_iface_rd_map DESC: full pathname to a file to map PE routers ifIndexes (input interfaces typically) to a BGP/MPLS Virtual Private Network (VPN) Route Distinguisher (RD), based upon rfc4659. See iface_to_rd.map file in the examples section for further information. Number of map entries (by default 384) can be modified via pre_tag_map_entries. KEY: bgp_follow_default DESC: expects positive number value which instructs how many times a default route, if any, can be followed in order to successfully resolve source and destination IP prefixes. This is aimed at scenarios where neighbors peering with pmacct have a default-only or partial BGP view. At each recursion (default route follow-up) the value gets decremented; the process stops when one of these conditions is met: * both source and destination IP prefixes are resolved * there is no available default route * the default gateway is not BGP peering with pmacct * the the recusion value reaches zero As soon as an IP prefix is matched, it is not looked up anymore in case more recursions are required (ie. the closer the router is, the most specific the route is assumed to be). pmacctd, uacctd daemons are internally limited to only two BGP peers hence this feature can't properly work. (default: 0) KEY: bgp_follow_nexthop DESC: expects one or more IP prefix(es), ie. 192.168.0.0/16, comma separated. A maximum of 32 IP prefixes is supported. It follows the BGP next-hop up (using each next-hop as BGP source-address for the next BGP RIB lookup), returning the last next-hop part of the supplied IP prefix as value for the 'peer_ip_dst' primitive. bgp_agent_map is supported at each recursion. This feature is aimed at networks not running MPLS or being based on BGP confederations; underlying goal being to see the routing-domain "exit-point" (which is what is easily seen with MPLS LSPs in conjunction with BGP). The feature is internally protected against routing loops with an hardcoded limit of 20 lookups; pmacctd, uacctd daemons are internally limited to only two BGP peers hence this feature can't properly work. (default: none) KEY: bgp_neighbors_file DESC: writes a list of the BGP neighbors in the established state to the specified file, one per line. This gets particularly useful for automation purposes (ie. auto-discovery of devices to poll via SNMP). (default: none) KEY: bgp_daemon_allow_file [GLOBAL] DESC: full pathname to a file containing the list of IP addresses (one for each line) allowed to establish a BGP session packets to the BGP thread. Current syntax does not implement network masks but only individual IP addresses. (default: allow all) KEY: bgp_daemon_md5_file [GLOBAL] DESC: full pathname to a file containing the BGP peers (IP address only, one for each line) and their corresponding MD5 passwords in CSV format (ie. 10.15.0.1, arealsmartpwd). BGP peers not making use of a MD5 password should not be listed. The maximum number of peers supported is 8192. For a sample map look in: 'examples/bgp_md5.lst.example' The feature was tested working against a 2.6.32 Linux kernel. (default: no password) KEY: bgp_table_peer_buckets [GLOBAL] VALUES: [ 1-1000 ] DESC: From version 0.12.4, a shared BGP RIB is worked out from announcements sent by all BGP peers. Routing information related to BGP prefixes is kept per-peer in order to simulate a multi-RIB environment and is internally structured as an hash with conflict chains. This parameter sets the number of buckets of such hash structure; the value is directly related to the number of expected BGP peers, should never exceed such amount and is best set to 1/10 of the expected number of peers. The default value proved to work fine up to aprox 100 BGP peers in lab. More buckets means better CPU usage but also increased memory footprint - and vice-versa. (default: 13) KEY: isis_daemon VALUES: [true|false] DESC: Enables the skinny IS-IS daemon thread. This feature requires the package to be supporting multi-threading (--enable-threads). It implements P2P Hellos, CSNP and PSNP - and does not send any LSP information out. It currently supports a single L2 P2P neighborship. Testing has been done over a GRE tunnel. (default: false) KEY: isis_daemon_ip DESC: Sets the sub-TLV of the Extended IS Reachability TLV that contains an IPv4 address for the local end of a link. No default value is set and a non-zero value is mandatory. It should be set to the IPv4 address configured on the interface pointed by isis_daemon_iface. KEY: isis_daemon_net DESC: Defines the Network entity title (NET) of the IS-IS daemon. In turn a NET defines the area addresses for the IS-IS area and the system ID of the router. No default value is set and a non-zero value is mandatory. Extensive IS-IS and ISO literature cover the topic, example of the NET value format can be found as part of the "Quickstart guide to setup the IS-IS daemon" in the EXAMPLES document. KEY: isis_daemon_iface DESC: Defines the network interface (ie. gre1) where to bind the IS-IS daemon. No default value is set and a non-zero value is mandatory. KEY: isis_daemon_mtu DESC: Defines the available MTU for the IS-IS daemon. P2P HELLOs will be padded to such length. When the daemon is configured to set a neighborship with a Cisco router running IOS, this value should match the value of the "clns mtu" IOS directive. (default: 1476) KEY isis_daemon_msglog VALUES: [true|false] DESC: enables IS-IS messages logging: as this can get easily verbose, it is intended for debug and troubleshooting purposes only. (default: false) KEY: uacctd_group DESC: Sets the Linux Netlink ULOG multicast group to be joined. (default: 1) KEY: uacctd_nl_size DESC: Sets ULOG Netlink internal buffer size (specified in bytes). It is 4KB by default, but to safely record bursts of high-speed traffic, it could be further increased. For high loads, values as large as 2MB are recommended. When modifying this value, it is also recommended to reflect the change to the 'snaplen' option. (default: 4096) KEY: tunnel_0 [GLOBAL, NO_NFACCTD] DESC: Defines tunnel inspection, disabled by default. The daemon will then account on tunnelled data rather than on the envelope. The implementation approach is stateless, ie. control messages are not handled. Up to 4 tunnel layers are supported (ie. , ; , ; ...). Up to 8 tunnel stacks will be supported (ie. configuration directives tunnel_0 .. tunnel_8), to be used in a strictly sequential order. First stack matched at the first layering, wins. Below tunnel protocols supported and related options: GTP, GPRS tunnelling protocol. Expects as option the UDP port identifying the protocol. tunnel_0: gtp, KEY: tee_receiver DESC: defines remote IP address and port to which NetFlow/sFlow dagagrams are to be replicated to. The value is expected to be in the usual form 'address:port'. This key is mandatory for a 'tee' plugin instance and no default value is set in order to prevent loops. KEY: tee_source_ip DESC: defines the local IP address from which NetFlow/sFlow dagagrams are to be replicate from. Only a numerical IPv4/IPv6 address is expected. The supplied IP address is required to be already configured on one of the interfaces. Value is ignored when transparent replication is enabled. (default: IP address is selected by the OS) KEY: tee_transparent VALUES: [true|false] DESC: Enables transparent replication mode. It essentially spoofs the source IP address to the original sender of the datagram. It requires super-user permissions. (default: false) KEY: xlate_src VALUES: [true|false] DESC: Copies NAT L3/L4 field values in place of the original ones (ie. src_host, src_port, src_net) when available; alternatively it falls back to original values. At time of writing only IPFIX and Cisco ASA NetFlow v9 (NSEL) have support for this (default: false) KEY: xlate_dst VALUES: [true|false] DESC: Copies NAT L3/L4 field values in place of the original ones (ie. dst_host, dst_port, dst_net) when available; alternatively it falls back to original values. At time of writing only IPFIX and Cisco ASA NetFlow v9 (NSEL) have support for this (default: false) pmacct-0.14.0/Makefile.in0000644000175000017500000002311511741267656014137 0ustar paolopaolo# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am # Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. SHELL = @SHELL@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ sharedstatedir = @sharedstatedir@ localstatedir = @localstatedir@ libdir = @libdir@ infodir = @infodir@ mandir = @mandir@ includedir = @includedir@ oldincludedir = /usr/include DESTDIR = pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = . ACLOCAL = @ACLOCAL@ AUTOCONF = @AUTOCONF@ AUTOMAKE = @AUTOMAKE@ AUTOHEADER = @AUTOHEADER@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ transform = @program_transform_name@ NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : CC = @CC@ EXTRABIN = @EXTRABIN@ MAKE = @MAKE@ MAKEINFO = @MAKEINFO@ PACKAGE = @PACKAGE@ PLUGINS = @PLUGINS@ RANLIB = @RANLIB@ THREADS_SOURCES = @THREADS_SOURCES@ VERSION = @VERSION@ SUBDIRS = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_CLEAN_FILES = DIST_COMMON = README AUTHORS COPYING ChangeLog INSTALL Makefile.am \ Makefile.in NEWS TODO acinclude.m4 aclocal.m4 configure configure.in \ install-sh missing mkinstalldirs DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) TAR = tar GZIP_ENV = --best all: all-redirect .SUFFIXES: $(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status cd $(top_builddir) \ && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status $(ACLOCAL_M4): configure.in acinclude.m4 cd $(srcdir) && $(ACLOCAL) config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(srcdir)/configure: $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES) cd $(srcdir) && $(AUTOCONF) # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. @SET_MAKE@ all-recursive install-data-recursive install-exec-recursive \ installdirs-recursive install-recursive uninstall-recursive \ check-recursive installcheck-recursive info-recursive dvi-recursive: @set fnord $(MAKEFLAGS); amf=$$2; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" mostlyclean-recursive clean-recursive distclean-recursive \ maintainer-clean-recursive: @set fnord $(MAKEFLAGS); amf=$$2; \ dot_seen=no; \ rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ rev="$$subdir $$rev"; \ test "$$subdir" != "." || dot_seen=yes; \ done; \ test "$$dot_seen" = "no" && rev=". $$rev"; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done tags: TAGS ID: $(HEADERS) $(SOURCES) $(LISP) list='$(SOURCES) $(HEADERS)'; \ unique=`for i in $$list; do echo $$i; done | \ awk ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ here=`pwd` && cd $(srcdir) \ && mkid -f$$here/ID $$unique $(LISP) TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) tags=; \ here=`pwd`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS)'; \ unique=`for i in $$list; do echo $$i; done | \ awk ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) mostlyclean-tags: clean-tags: distclean-tags: -rm -f TAGS ID maintainer-clean-tags: distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist -rm -rf $(distdir) GZIP=$(GZIP_ENV) $(TAR) zxf $(distdir).tar.gz mkdir $(distdir)/=build mkdir $(distdir)/=inst dc_install_base=`cd $(distdir)/=inst && pwd`; \ cd $(distdir)/=build \ && ../configure --srcdir=.. --prefix=$$dc_install_base \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) dist -rm -rf $(distdir) @banner="$(distdir).tar.gz is ready for distribution"; \ dashes=`echo "$$banner" | sed s/./=/g`; \ echo "$$dashes"; \ echo "$$banner"; \ echo "$$dashes" dist: distdir -chmod -R a+r $(distdir) GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) -rm -rf $(distdir) dist-all: distdir -chmod -R a+r $(distdir) GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) -rm -rf $(distdir) distdir: $(DISTFILES) -rm -rf $(distdir) mkdir $(distdir) -chmod 777 $(distdir) @for file in $(DISTFILES); do \ d=$(srcdir); \ if test -d $$d/$$file; then \ cp -pr $$d/$$file $(distdir)/$$file; \ else \ test -f $(distdir)/$$file \ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ || cp -p $$d/$$file $(distdir)/$$file || :; \ fi; \ done for subdir in $(SUBDIRS); do \ if test "$$subdir" = .; then :; else \ test -d $(distdir)/$$subdir \ || mkdir $(distdir)/$$subdir \ || exit 1; \ chmod 777 $(distdir)/$$subdir; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(distdir) distdir=../$(distdir)/$$subdir distdir) \ || exit 1; \ fi; \ done info-am: info: info-recursive dvi-am: dvi: dvi-recursive check-am: all-am check: check-recursive installcheck-am: installcheck: installcheck-recursive install-exec-am: install-exec: install-exec-recursive install-data-am: install-data: install-data-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am install: install-recursive uninstall-am: uninstall: uninstall-recursive all-am: Makefile all-redirect: all-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install installdirs: installdirs-recursive installdirs-am: mostlyclean-generic: clean-generic: distclean-generic: -rm -f Makefile $(CONFIG_CLEAN_FILES) -rm -f config.cache config.log stamp-h stamp-h[0-9]* maintainer-clean-generic: mostlyclean-am: mostlyclean-tags mostlyclean-generic mostlyclean: mostlyclean-recursive clean-am: clean-tags clean-generic mostlyclean-am clean: clean-recursive distclean-am: distclean-tags distclean-generic clean-am distclean: distclean-recursive -rm -f config.status maintainer-clean-am: maintainer-clean-tags maintainer-clean-generic \ distclean-am @echo "This command is intended for maintainers to use;" @echo "it deletes files that may require special tools to rebuild." maintainer-clean: maintainer-clean-recursive -rm -f config.status .PHONY: install-data-recursive uninstall-data-recursive \ install-exec-recursive uninstall-exec-recursive installdirs-recursive \ uninstalldirs-recursive all-recursive check-recursive \ installcheck-recursive info-recursive dvi-recursive \ mostlyclean-recursive distclean-recursive clean-recursive \ maintainer-clean-recursive tags tags-recursive mostlyclean-tags \ distclean-tags clean-tags maintainer-clean-tags distdir info-am info \ dvi-am dvi check check-am installcheck-am installcheck install-exec-am \ install-exec install-data-am install-data install-am install \ uninstall-am uninstall all-redirect all-am all installdirs-am \ installdirs mostlyclean-generic distclean-generic clean-generic \ maintainer-clean-generic clean mostlyclean distclean maintainer-clean # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: pmacct-0.14.0/include/0000755000175000017500000000000011741267746013513 5ustar paolopaolopmacct-0.14.0/include/fddi.h0000644000175000017500000000563510530072467014570 0ustar paolopaolo/* * Copyright (c) 1992, 1993, 1994, 1995, 1996 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * @(#) $Header: /home/repo-0.14/pmacct/include/fddi.h,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $ (LBL) */ /* * Based on Ultrix if_fddi.h */ /* * This stuff should come from a system header file, but there's no * obviously portable way to do that and it's not really going * to change from system to system (except for the padding business). */ struct fddi_header { u_char fddi_fc; /* frame control */ u_char fddi_dhost[6]; u_char fddi_shost[6]; }; /* * Length of an FDDI header; note that some compilers may pad * "struct fddi_header" to a multiple of 4 bytes, for example, so * "sizeof (struct fddi_header)" may not give the right * answer. */ #define FDDI_HDRLEN 13 /* Useful values for fddi_fc (frame control) field */ /* * FDDI Frame Control bits */ #define FDDIFC_C 0x80 /* Class bit */ #define FDDIFC_L 0x40 /* Address length bit */ #define FDDIFC_F 0x30 /* Frame format bits */ #define FDDIFC_Z 0x0f /* Control bits */ /* * FDDI Frame Control values. (48-bit addressing only). */ #define FDDIFC_VOID 0x40 /* Void frame */ #define FDDIFC_NRT 0x80 /* Nonrestricted token */ #define FDDIFC_RT 0xc0 /* Restricted token */ #define FDDIFC_SMT_INFO 0x41 /* SMT Info */ #define FDDIFC_SMT_NSA 0x4F /* SMT Next station adrs */ #define FDDIFC_MAC_BEACON 0xc2 /* MAC Beacon frame */ #define FDDIFC_MAC_CLAIM 0xc3 /* MAC Claim frame */ #define FDDIFC_LLC_ASYNC 0x50 /* Async. LLC frame */ #define FDDIFC_LLC_SYNC 0xd0 /* Sync. LLC frame */ #define FDDIFC_IMP_ASYNC 0x60 /* Implementor Async. */ #define FDDIFC_IMP_SYNC 0xe0 /* Implementor Synch. */ #define FDDIFC_SMT 0x40 /* SMT frame */ #define FDDIFC_MAC 0xc0 /* MAC frame */ #define FDDIFC_CLFF 0xF0 /* Class/Length/Format bits */ #define FDDIFC_ZZZZ 0x0F /* Control bits */ pmacct-0.14.0/include/extract.h0000644000175000017500000000463210530072467015330 0ustar paolopaolo/* * Copyright (c) 1992, 1993, 1994, 1995, 1996 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * @(#) $Header: /home/repo-0.14/pmacct/include/extract.h,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $ (LBL) */ /* Network to host order macros */ #ifdef LBL_ALIGN #define EXTRACT_16BITS(p) \ ((u_int16_t)*((const u_int8_t *)(p) + 0) << 8 | \ (u_int16_t)*((const u_int8_t *)(p) + 1)) #define EXTRACT_32BITS(p) \ ((u_int32_t)*((const u_int8_t *)(p) + 0) << 24 | \ (u_int32_t)*((const u_int8_t *)(p) + 1) << 16 | \ (u_int32_t)*((const u_int8_t *)(p) + 2) << 8 | \ (u_int32_t)*((const u_int8_t *)(p) + 3)) #else #define EXTRACT_16BITS(p) \ ((u_int16_t)ntohs(*(const u_int16_t *)(p))) #define EXTRACT_32BITS(p) \ ((u_int32_t)ntohl(*(const u_int32_t *)(p))) #endif #define EXTRACT_24BITS(p) \ ((u_int32_t)*((const u_int8_t *)(p) + 0) << 16 | \ (u_int32_t)*((const u_int8_t *)(p) + 1) << 8 | \ (u_int32_t)*((const u_int8_t *)(p) + 2)) /* Little endian protocol host order macros */ #define EXTRACT_LE_8BITS(p) (*(p)) #define EXTRACT_LE_16BITS(p) \ ((u_int16_t)*((const u_int8_t *)(p) + 1) << 8 | \ (u_int16_t)*((const u_int8_t *)(p) + 0)) #define EXTRACT_LE_32BITS(p) \ ((u_int32_t)*((const u_int8_t *)(p) + 3) << 24 | \ (u_int32_t)*((const u_int8_t *)(p) + 2) << 16 | \ (u_int32_t)*((const u_int8_t *)(p) + 1) << 8 | \ (u_int32_t)*((const u_int8_t *)(p) + 0)) pmacct-0.14.0/include/sll.h0000644000175000017500000001276510530072467014456 0ustar paolopaolo/*- * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * * This code is derived from the Stanford/CMU enet packet filter, * (net/enet.c) distributed as part of 4.3BSD, and code contributed * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence * Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#) $Header: /home/repo-0.14/pmacct/include/sll.h,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $ (LBL) */ /* * For captures on Linux cooked sockets, we construct a fake header * that includes: * * a 2-byte "packet type" which is one of: * * LINUX_SLL_HOST packet was sent to us * LINUX_SLL_BROADCAST packet was broadcast * LINUX_SLL_MULTICAST packet was multicast * LINUX_SLL_OTHERHOST packet was sent to somebody else * LINUX_SLL_OUTGOING packet was sent *by* us; * * a 2-byte Ethernet protocol field; * * a 2-byte link-layer type; * * a 2-byte link-layer address length; * * an 8-byte source link-layer address, whose actual length is * specified by the previous value. * * All fields except for the link-layer address are in network byte order. * * DO NOT change the layout of this structure, or change any of the * LINUX_SLL_ values below. If you must change the link-layer header * for a "cooked" Linux capture, introduce a new DLT_ type (ask * "tcpdump-workers@tcpdump.org" for one, so that you don't give it a * value that collides with a value already being used), and use the * new header in captures of that type, so that programs that can * handle DLT_LINUX_SLL captures will continue to handle them correctly * without any change, and so that capture files with different headers * can be told apart and programs that read them can dissect the * packets in them. * * This structure, and the #defines below, must be the same in the * libpcap and tcpdump versions of "sll.h". */ /* * A DLT_LINUX_SLL fake link-layer header. */ #define SLL_HDR_LEN 16 /* total header length */ #define SLL_ADDRLEN 8 /* length of address field */ struct sll_header { u_int16_t sll_pkttype; /* packet type */ u_int16_t sll_hatype; /* link-layer address type */ u_int16_t sll_halen; /* link-layer address length */ u_int8_t sll_addr[SLL_ADDRLEN]; /* link-layer address */ u_int16_t sll_protocol; /* protocol */ }; /* * The LINUX_SLL_ values for "sll_pkttype"; these correspond to the * PACKET_ values on Linux, but are defined here so that they're * available even on systems other than Linux, and so that they * don't change even if the PACKET_ values change. */ #define LINUX_SLL_HOST 0 #define LINUX_SLL_BROADCAST 1 #define LINUX_SLL_MULTICAST 2 #define LINUX_SLL_OTHERHOST 3 #define LINUX_SLL_OUTGOING 4 /* * The LINUX_SLL_ values for "sll_protocol"; these correspond to the * ETH_P_ values on Linux, but are defined here so that they're * available even on systems other than Linux. We assume, for now, * that the ETH_P_ values won't change in Linux; if they do, then: * * if we don't translate them in "pcap-linux.c", capture files * won't necessarily be readable if captured on a system that * defines ETH_P_ values that don't match these values; * * if we do translate them in "pcap-linux.c", that makes life * unpleasant for the BPF code generator, as the values you test * for in the kernel aren't the values that you test for when * reading a capture file, so the fixup code run on BPF programs * handed to the kernel ends up having to do more work. * * Add other values here as necessary, for handling packet types that * might show up on non-Ethernet, non-802.x networks. (Not all the ones * in the Linux "if_ether.h" will, I suspect, actually show up in * captures.) */ #define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */ #define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */ pmacct-0.14.0/include/ip6.h0000644000175000017500000001665410530072467014363 0ustar paolopaolo/* @(#) $Header: /home/repo-0.14/pmacct/include/ip6.h,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $ (LBL) */ /* $NetBSD: ip6.h,v 1.9 2000/07/13 05:34:21 itojun Exp $ */ /* $KAME: ip6.h,v 1.9 2000/07/02 21:01:32 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NETINET_IP6_H_ #define _NETINET_IP6_H_ /* * Definition for internet protocol version 6. * RFC 2460 */ struct ip6_hdr { union { struct ip6_hdrctl { u_int32_t ip6_un1_flow; /* 20 bits of flow-ID */ u_int16_t ip6_un1_plen; /* payload length */ u_int8_t ip6_un1_nxt; /* next header */ u_int8_t ip6_un1_hlim; /* hop limit */ } ip6_un1; u_int8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */ } ip6_ctlun; struct in6_addr ip6_src; /* source address */ struct in6_addr ip6_dst; /* destination address */ }; #define ip6_vfc ip6_ctlun.ip6_un2_vfc #define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow #define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen #define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt #define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim #define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim #if !defined IPV6_VERSION #define IPV6_VERSION 0x60 #endif #define IPV6_VERSION_MASK 0xf0 /* in network endian */ #define IPV6_FLOWINFO_MASK ((u_int32_t)htonl(0x0fffffff)) /* flow info (28 bits) */ #define IPV6_FLOWLABEL_MASK ((u_int32_t)htonl(0x000fffff)) /* flow label (20 bits) */ #if 1 /* ECN bits proposed by Sally Floyd */ #define IP6TOS_CE 0x01 /* congestion experienced */ #define IP6TOS_ECT 0x02 /* ECN-capable transport */ #endif /* * Extension Headers */ struct ip6_ext { u_char ip6e_nxt; u_char ip6e_len; }; /* Hop-by-Hop options header */ /* XXX should we pad it to force alignment on an 8-byte boundary? */ struct ip6_hbh { u_int8_t ip6h_nxt; /* next header */ u_int8_t ip6h_len; /* length in units of 8 octets */ /* followed by options */ }; /* Destination options header */ /* XXX should we pad it to force alignment on an 8-byte boundary? */ struct ip6_dest { u_int8_t ip6d_nxt; /* next header */ u_int8_t ip6d_len; /* length in units of 8 octets */ /* followed by options */ }; /* Option types and related macros */ #define IP6OPT_PAD1 0x00 /* 00 0 00000 */ #define IP6OPT_PADN 0x01 /* 00 0 00001 */ #define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */ #define IP6OPT_JUMBO_LEN 6 #define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 */ #define IP6OPT_RTALERT_LEN 4 #define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ #define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ #define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */ #define IP6OPT_MINLEN 2 #define IP6OPT_BINDING_UPDATE 0xc6 /* 11 0 00110 */ #define IP6OPT_BINDING_ACK 0x07 /* 00 0 00111 */ #define IP6OPT_BINDING_REQ 0x08 /* 00 0 01000 */ #define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */ #define IP6OPT_EID 0x8a /* 10 0 01010 */ #define IP6OPT_TYPE(o) ((o) & 0xC0) #define IP6OPT_TYPE_SKIP 0x00 #define IP6OPT_TYPE_DISCARD 0x40 #define IP6OPT_TYPE_FORCEICMP 0x80 #define IP6OPT_TYPE_ICMP 0xC0 #define IP6OPT_MUTABLE 0x20 /* Routing header */ struct ip6_rthdr { u_int8_t ip6r_nxt; /* next header */ u_int8_t ip6r_len; /* length in units of 8 octets */ u_int8_t ip6r_type; /* routing type */ u_int8_t ip6r_segleft; /* segments left */ /* followed by routing type specific data */ }; /* Type 0 Routing header */ struct ip6_rthdr0 { u_int8_t ip6r0_nxt; /* next header */ u_int8_t ip6r0_len; /* length in units of 8 octets */ u_int8_t ip6r0_type; /* always zero */ u_int8_t ip6r0_segleft; /* segments left */ u_int8_t ip6r0_reserved; /* reserved field */ u_int8_t ip6r0_slmap[3]; /* strict/loose bit map */ struct in6_addr ip6r0_addr[1]; /* up to 23 addresses */ }; /* Fragment header */ struct ip6_frag { u_int8_t ip6f_nxt; /* next header */ u_int8_t ip6f_reserved; /* reserved field */ u_int16_t ip6f_offlg; /* offset, reserved, and flag */ u_int32_t ip6f_ident; /* identification */ }; #define IP6F_OFF_MASK 0xfff8 /* mask out offset from ip6f_offlg */ #define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */ #define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */ #endif /* not _NETINET_IP6_H_ */ pmacct-0.14.0/include/ah.h0000644000175000017500000000454510530072467014251 0ustar paolopaolo/* $NetBSD: ah.h,v 1.12 2000/07/23 05:23:04 itojun Exp $ */ /* $KAME: ah.h,v 1.12 2000/07/20 17:41:01 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * RFC1826/2402 authentication header. */ #ifndef _NETINET6_AH_H_ #define _NETINET6_AH_H_ struct ah { u_int8_t ah_nxt; /* Next Header */ u_int8_t ah_len; /* Length of data, in 32bit */ u_int16_t ah_reserve; /* Reserved for future use */ u_int32_t ah_spi; /* Security parameter index */ /* variable size, 32bit bound*/ /* Authentication data */ }; struct newah { u_int8_t ah_nxt; /* Next Header */ u_int8_t ah_len; /* Length of data + 1, in 32bit */ u_int16_t ah_reserve; /* Reserved for future use */ u_int32_t ah_spi; /* Security parameter index */ u_int32_t ah_seq; /* Sequence number field */ /* variable size, 32bit bound*/ /* Authentication data */ }; #endif /*_NETINET6_AH_H_*/ pmacct-0.14.0/include/ieee802_11.h0000644000175000017500000001270010530072467015313 0ustar paolopaolo/* @(#) $Header: /home/repo-0.14/pmacct/include/ieee802_11.h,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $ (LBL) */ /* * Copyright (c) 2001 * Fortress Technologies * Charlie Lenahan ( clenahan@fortresstech.com ) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #define IEEE802_11_FC_LEN 2 #define T_MGMT 0x0 /* management */ #define T_CTRL 0x1 /* control */ #define T_DATA 0x2 /* data */ #define T_RESV 0x3 /* reserved */ #define ST_ASSOC_REQUEST 0x0 #define ST_ASSOC_RESPONSE 0x1 #define ST_REASSOC_REQUEST 0x2 #define ST_REASSOC_RESPONSE 0x3 #define ST_PROBE_REQUEST 0x4 #define ST_PROBE_RESPONSE 0x5 /* RESERVED 0x6 */ /* RESERVED 0x7 */ #define ST_BEACON 0x8 #define ST_ATIM 0x9 #define ST_DISASSOC 0xA #define ST_AUTH 0xB #define ST_DEAUTH 0xC /* RESERVED 0xD */ /* RESERVED 0xE */ /* RESERVED 0xF */ #define CTRL_PS_POLL 0xA #define CTRL_RTS 0xB #define CTRL_CTS 0xC #define CTRL_ACK 0xD #define CTRL_CF_END 0xE #define CTRL_END_ACK 0xF /* * Bits in the frame control field. */ #define FC_VERSION(fc) ((fc) & 0x3) #define FC_TYPE(fc) (((fc) >> 2) & 0x3) #define FC_SUBTYPE(fc) (((fc) >> 4) & 0xF) #define FC_TO_DS(fc) ((fc) & 0x0100) #define FC_FROM_DS(fc) ((fc) & 0x0200) #define FC_MORE_FLAG(fc) ((fc) & 0x0400) #define FC_RETRY(fc) ((fc) & 0x0800) #define FC_POWER_MGMT(fc) ((fc) & 0x1000) #define FC_MORE_DATA(fc) ((fc) & 0x2000) #define FC_WEP(fc) ((fc) & 0x4000) #define FC_ORDER(fc) ((fc) & 0x8000) struct mgmt_header_t { u_int16_t fc; u_int16_t duration; u_int8_t da[6]; u_int8_t sa[6]; u_int8_t bssid[6]; u_int16_t seq_ctrl; }; #define MGMT_HEADER_LEN (2+2+6+6+6+2) #define CAPABILITY_ESS(cap) ((cap) & 0x0001) #define CAPABILITY_IBSS(cap) ((cap) & 0x0002) #define CAPABILITY_CFP(cap) ((cap) & 0x0004) #define CAPABILITY_CFP_REQ(cap) ((cap) & 0x0008) #define CAPABILITY_PRIVACY(cap) ((cap) & 0x0010) struct ssid_t { u_int8_t element_id; u_int8_t length; u_char ssid[33]; /* 32 + 1 for null */ } ; struct rates_t { u_int8_t element_id; u_int8_t length; u_int8_t rate[8]; }; struct challenge_t { u_int8_t element_id; u_int8_t length; u_int8_t text[254]; /* 1-253 + 1 for null */ }; struct fh_t { u_int8_t element_id; u_int8_t length; u_int16_t dwell_time; u_int8_t hop_set; u_int8_t hop_pattern; u_int8_t hop_index; }; struct ds_t { u_int8_t element_id; u_int8_t length; u_int8_t channel; }; struct cf_t { u_int8_t element_id; u_int8_t length; u_int8_t count; u_int8_t period; u_int16_t max_duration; u_int16_t dur_remaing; }; struct tim_t { u_int8_t element_id; u_int8_t length; u_int8_t count; u_int8_t period; u_int8_t bitmap_control; u_int8_t bitmap[251]; }; #define E_SSID 0 #define E_RATES 1 #define E_FH 2 #define E_DS 3 #define E_CF 4 #define E_TIM 5 #define E_IBSS 6 /* reserved 7 */ /* reserved 8 */ /* reserved 9 */ /* reserved 10 */ /* reserved 11 */ /* reserved 12 */ /* reserved 13 */ /* reserved 14 */ /* reserved 15 */ /* reserved 16 */ #define E_CHALLENGE 16 /* reserved 17 */ /* reserved 18 */ /* reserved 19 */ /* reserved 16 */ /* reserved 16 */ struct mgmt_body_t { u_int8_t timestamp[8]; u_int16_t beacon_interval; u_int16_t listen_interval; u_int16_t status_code; u_int16_t aid; u_char ap[6]; u_int16_t reason_code; u_int16_t auth_alg; u_int16_t auth_trans_seq_num; struct challenge_t challenge; u_int16_t capability_info; struct ssid_t ssid; struct rates_t rates; struct ds_t ds; struct cf_t cf; struct fh_t fh; struct tim_t tim; }; struct ctrl_rts_t { u_int16_t fc; u_int16_t duration; u_int8_t ra[6]; u_int8_t ta[6]; u_int8_t fcs[4]; }; #define CTRL_RTS_LEN (2+2+6+6+4) struct ctrl_cts_t { u_int16_t fc; u_int16_t duration; u_int8_t ra[6]; u_int8_t fcs[4]; }; #define CTRL_CTS_LEN (2+2+6+4) struct ctrl_ack_t { u_int16_t fc; u_int16_t duration; u_int8_t ra[6]; u_int8_t fcs[4]; }; #define CTRL_ACK_LEN (2+2+6+4) struct ctrl_ps_poll_t { u_int16_t fc; u_int16_t aid; u_int8_t bssid[6]; u_int8_t ta[6]; u_int8_t fcs[4]; }; #define CTRL_PS_POLL_LEN (2+2+6+6+4) struct ctrl_end_t { u_int16_t fc; u_int16_t duration; u_int8_t ra[6]; u_int8_t bssid[6]; u_int8_t fcs[4]; }; #define CTRL_END_LEN (2+2+6+6+4) struct ctrl_end_ack_t { u_int16_t fc; u_int16_t duration; u_int8_t ra[6]; u_int8_t bssid[6]; u_int8_t fcs[4]; }; #define CTRL_END_ACK_LEN (2+2+6+6+4) #define IV_IV(iv) ((iv) & 0xFFFFFF) #define IV_PAD(iv) (((iv) >> 24) & 0x3F) #define IV_KEYID(iv) (((iv) >> 30) & 0x03) pmacct-0.14.0/include/llc.h0000644000175000017500000000702610530072467014430 0ustar paolopaolo/* * Copyright (c) 1993, 1994, 1997 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * @(#) $Header: /home/repo-0.14/pmacct/include/llc.h,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $ (LBL) */ /* * This stuff should come from a system header file, but there's no * obviously portable way to do that and it's not really going * to change from system to system. */ /* * A somewhat abstracted view of the LLC header */ struct llc { u_int8_t dsap; u_int8_t ssap; union { u_int8_t u_ctl; u_int16_t is_ctl; struct { u_int8_t snap_ui; u_int8_t snap_pi[5]; } snap; struct { u_int8_t snap_ui; u_int8_t snap_orgcode[3]; u_int8_t snap_ethertype[2]; } snap_ether; } ctl; }; #define llcui ctl.snap.snap_ui #define llcpi ctl.snap.snap_pi #define llc_orgcode ctl.snap_ether.snap_orgcode #define llc_ethertype ctl.snap_ether.snap_ethertype #define llcis ctl.is_ctl #define llcu ctl.u_ctl #define LLC_U_FMT 3 #define LLC_GSAP 1 #define LLC_S_FMT 1 #define LLC_U_POLL 0x10 #define LLC_IS_POLL 0x0100 #define LLC_XID_FI 0x81 #define LLC_U_CMD(u) ((u) & 0xef) #define LLC_UI 0x03 #define LLC_UA 0x63 #define LLC_DISC 0x43 #define LLC_DM 0x0f #define LLC_SABME 0x6f #define LLC_TEST 0xe3 #define LLC_XID 0xaf #define LLC_FRMR 0x87 #define LLC_S_CMD(is) (((is) >> 1) & 0x03) #define LLC_RR 0x0001 #define LLC_RNR 0x0005 #define LLC_REJ 0x0009 #define LLC_IS_NR(is) (((is) >> 9) & 0x7f) #define LLC_I_NS(is) (((is) >> 1) & 0x7f) #ifndef LLCSAP_NULL #define LLCSAP_NULL 0x00 #endif #ifndef LLCSAP_GLOBAL #define LLCSAP_GLOBAL 0xff #endif #ifndef LLCSAP_8021B_I #define LLCSAP_8021B_I 0x02 #endif #ifndef LLCSAP_8021B_G #define LLCSAP_8021B_G 0x03 #endif #ifndef LLCSAP_IP #define LLCSAP_IP 0x06 #endif #ifndef LLCSAP_PROWAYNM #define LLCSAP_PROWAYNM 0x0e #endif #ifndef LLCSAP_8021D #define LLCSAP_8021D 0x42 #endif #ifndef LLCSAP_RS511 #define LLCSAP_RS511 0x4e #endif #ifndef LLCSAP_ISO8208 #define LLCSAP_ISO8208 0x7e #endif #ifndef LLCSAP_PROWAY #define LLCSAP_PROWAY 0x8e #endif #ifndef LLCSAP_SNAP #define LLCSAP_SNAP 0xaa #endif #ifndef LLCSAP_IPX #define LLCSAP_IPX 0xe0 #endif #ifndef LLCSAP_NETBEUI #define LLCSAP_NETBEUI 0xf0 #endif #ifndef LLCSAP_ISONS #define LLCSAP_ISONS 0xfe #endif #define OUI_ENCAP_ETHER 0x000000 /* encapsulated Ethernet */ #define OUI_CISCO 0x00000c /* Cisco protocols */ #define ETHERTYPE_CISCO_CDP 0x2000 /* Cisco Discovery Protocol */ #define OUI_CISCO_90 0x0000f8 /* Cisco bridging */ #define OUI_APPLETALK 0x080007 /* Appletalk */ pmacct-0.14.0/ChangeLog0000644000175000017500000042211211741103265013625 0ustar paolopaolopmacct (Promiscuous mode IP Accounting package) v0.14.0 pmacct is Copyright (C) 2003-2012 by Paolo Lucente 0.14.0 -- 11-04-2012 + pmacct now integrates an IS-IS daemon within collectors; the daemon is being run as a parallel thread within the collector core process; a single L2 P2P neighborship, ie. over a GRE tunnel, is supported; it implements P2P Hello, CSNP and PSNP - and does not send any LSP information out. The daemon is currently used for route resolution. It is well suited to several case-studies, popular one being: more specific internal routes are carried within the IGP while they are summarized in BGP crossing cluster boundaries. + A new aggregation primitive 'etype' has been introduced in order to support accounting against the EtherType field of Ethernet frames. The implementation is consistent across all data collection methods and backends. + sfacctd: introduced support for samples generated on ACL matches in Brocade (sFlow sample type: Enterprise: #1991, Format: #1). Thanks to Elisa Jasinska and Brent Van Dussen for their support. + sfacctd, pre_tag_map: introduced sample_type key. In sFlow v2/v4/v5 this is compared against the sample type field. Value is expected in : notation. ! fix, signals.c: ignoring SIGINT and SIGTERM in my_sigint_handler() to prevent multiple calls to fill_pipe_buffer(), condition that can cause pipe buffer overruns. Patch is courtesy by Osama Abu Elsorour. ! fix, pmacctd: tunnel registry now correctly supports multiple tunnel definitions for the same stack level. ! fix, print plugin: cos field now correctly shows up in the format title while CSV format is selected and L2 primitives are enabled. ! fix, util.c: a feof() check has been added to the fread() call in read_SQLquery_from_file(); thanks to Elisa Jasinska and Brent Van Dussen for their support. ! fix, nfprobe: NetFlow output socket is now re-opened after failing send() calls. Thanks to Maurizio Molina for reporting the problem. ! fix, sfacctd: length checks have been imporved while extracting string tokens (ie. AS-PATH and BGP communities) from sFlow Extended Gateway object. Thanks to Duncan Small for his support. 0.14.0rc3 -- 07-12-2011 + BGP daemon: BGP/MPLS VPNs (rfc4364) implemented! This encompasses both RIB storage (ie. virtualization layer) and lookup. bgp_iface_to_rd_map map correlates couples to Route Distinguishers (RDs). RD encapsulation types #0 (2-bytes ASN), #1 (IP address) and #2 (4-bytes ASN) are supported. Examples provided: examples/bgp_iface_to_rd.map and EXAMPLES files. + mpls_vpn_rd aggregation primitive has been added to the set. Also this is being supported key in Pre-Tagging (pre_tag_map). + print plugin: introduced print_output_file feature to write statistics to files. Output is text, formatted or CSV. Filenames can contain time- based variables to make them dynamic. If filename is static instead, content is overwritten over time. + print plugin: introduced print_time_roundoff feature to align time slots nicely, same as per the sql_history_roundoff directive. + print plugin: introduced print_trigger_exec feature to execute custom scripts at each print_refresh_time interval (ie. to process, expire, gzip, etc. files). Feature is in sync with wrap-up of data commit to screen or files. + pmacctd: introduced support for DLT_LOOP link-type (ie. OpenBSD tunnel interfaces). Thanks to Neil Reilly for his support. + uacctd: a cache of ifIndex is introduced. Hash structure with conflict chains and short expiration time (ie. to avoid getting tricked by cooked interfaces devices a-la ppp0). The cache is an effort to gain speed-ups. Implementation is courtesy by Stephen Hemminger, Vyatta. + Logging: introduced syslog-like timestamping when writing directly to files. Also a separate FD per process is used and SIGHUP elicits files reopening: all aimed at letting proper logs rotation by external tools. + Introduced plugin_pipe_backlog configuration directive: it induces a backlog of buffers on the pipe before actually releasing them to the plugin. The strategy helps optimizing inter-process communications, ie. when plugins are quicker processing data than the Core process. ! fix, peer_src_ip primitive: has been disconnected from [ns]facctd_as_new mechanism in order to ensure it's always representing a reference to the NetFlow or sFlow emitter. ! fix, nfprobe: input and output VLAN ID field types have been aligned to RFC3954, which appears to be also retroactively supported by IPFIX. The new field types are #58 and #59 respectively. Thanks to Maurizio Molina for pointing the issue out. ! fix, IMT plugin: fragmentation of the class table over multiple packets to the pmacct IMT client was failing and has been resolved. ! fix, nfprobe: individual flows start and end timestamps are now filled to the msec resolution. Thanks to Daniel Aschwanden for having reported the issue. ! fix, uacctd: NETLINK_NO_ENOBUFS is set to prevent the daemon being reported about ENOBUFS events by the underlying operating system. Works on kernels 2.6.30+. Patch is courtesy by Stephen Hemminger, Vyatta. ! fix, uacctd: get_ifindex() can now return values greater than 2^15. Patch is courtesy by Stephen Hemminger, Vyatta. ! fix, pmacctd, uacctd: case of zero IPv6 payload in conjunction with no IPv6 next header is now supported. Thanks to Quirin Scheitle for having reported the issue. - Support for is_symmetric aggregation primitive is discontinued. 0.14.0rc2 -- 26-08-2011 + sampling_map feature is introduced, allowing definition of static traffic sampling mappings. Content of the map is reloadable at runtime. If a specific router is not defined in the map, the sampling rate advertised by the router itself, if any, is applied. + nfacctd: introduced support for 16 bits SAMPLER_IDs in NetFlow v9/IPFIX; this appears to be the standard length with IOS-XR. + nfacctd: introduced support for (FLOW)_SAMPLING_INTERVAL fields as part of the NetFlow v9/IPFIX data record. This case is not prevented by the RFC although such information is typically exported as part of options. It appears some probes, ie. FlowMon by Invea-Tech, are getting down this way. + nfacctd, sfacctd: nfacctd_as_new and sfacctd_as_new got a new 'fallback' option; when specified, lookup of BGP-related primitives is done against BGP first and, if not successful, against the export protocol. + nfacctd, sfacctd: nfacctd_net and sfacctd_net got a new 'fallback' option that when specified looks up network-related primitives (prefixes, masks) against BGP first and, if not successful, against the export protocol. It gets useful for resolving prefixes advertised only in the IGP. + sql_num_hosts feature is being introduced: defines, in MySQL and SQLite plugins, whether IP addresses should be left numerical (in network bytes ordering) or converted into strings. For backward compatibility, default is to convert them into strings. + print_num_protos and sql_num_protos configuration directives have been introduced to allow to handle IP protocols (ie. tcp, udp) in numerical format. The default, backward compatible, is to look protocol names up. The feature is built against all plugins and can also be activated via the '-u' commandline switch. ! fix, nfacctd: NetFlow v9/IPFIX sampling option parsing now doesn't rely anymore solely on finding a SamplerID field; as an alternative, presence of a sampling interval field is also checked. Also a workaround is being introduced for sampled NetFlow v9 & C7600: if samplerID within a data record is defined and set to zero and no match was possible, then the last samplerID defined is returned. ! nfacctd: (FLOW)_SAMPLING_INTERVAL fields as part of the NetFlow v9/IPFIX data record are now supported also 16-bits long (in addition to 32-bits). ! fix, SQL plugins: sql_create_table() timestamp has been aligned with SQL queries (insert, update, lock); furthermore sql_create_table() is invoked every sql_refresh_time instead of every sql_history. Docs updated. Thanks to Luis Galan for having reported the issue. ! fix, pmacct client: error code when connection is refused on UNIX socket was 0; it has been changed to 1 to reflect the error condition. Thanks to Mateusz Viste for reporting the issue. ! fix, building system: CFLAGS were not always honoured. Patch is courtesy of Etienne Champetier ! fix, ll.c: empty return value was causing compiler with certain flags to complain about the issue. Patch is courtesy of Ryan Steinmetz. 0.14.0rc1 -- 31-03-2011 + IPFIX (IETF IP Flow Information Export protocol) replication and collector capabilities have been introduced as part of nfacctd, the NetFlow accounting daemon of the pmacct package. + nfprobe plugin: initial IPFIX export implementation. This is called via a 'nfprobe_version: 10' configuration directive. pmacctd, the promiscuous mode accounting daemon, and uacctd, the ULOG accounting daemon, both part of the pmacct package are now supported. + Oracle's BrekeleyDB 11gR2 offers a perfect combination of technologies by including an SQL API that is fully compatible with SQLite. As a result pmacct now opens to BerkeleyDB 5.x via its SQLite3 plugin. + sfacctd: BGP-related traffic primitives (AS Path, local preference, communities, etc.) are now read from sFlow Extended Gateway object if sfacctd_as_new is set to false (default). + nfacctd, sfacctd: source and destination peer ASNs are now read from NetFlow or sFlow data if [ns]facctd_as_new is set to false (default). + nfacctd: introduced support for NetFlow v9/IPFIX source and destination peer ASN field types 128 and 129. The support is enabled at runtime by setting to 'false' (default) the 'nfacctd_as_new' directive. + sfacctd: f_agent now points sFlow Agent ID instead of source IP address; among the other things, this allows to compare BGP source IP address/BGP Router-ID against the sFlow Agent ID. + PostgreSQL plugin: 'sql_delimiter' config directive being introduced: if sql_use_copy is true, uses the supplied character as delimiter.Useful in cases where the default delimiter is part of any of the supplied strings. + pmacct client: introduced support for Comma-Separated Values (CSV) output in addition to formatted-text. A -O commandline switch allows to enable the feature. ! fix, MySQL/PostgreSQL/SQLite3 plugins: insert of data into the database can get arbitrarily delayed under low traffic conditions. Many Thanks to Elisa Jasinska and Brent Van Dussen for their great support in solving the issue. ! fix, BGP daemon: multiple BGP capabilities per capability announcement were not supported - breaking compliancy with RFC5492. The issue was only verified against a OpenBGPd speaker. Patch is courtesy of Manuel Guesdon. ! fix, initial effort made to document uacctd, the ULOG accounting daemon 0.12.5 -- 28-12-2010 + nfacctd: introduced support for NAT L3/L4 field values via xlate_src and xlate_dst configuration directives. Implementation follows IPFIX standard for IPv4 and IPv6 (field types 225, 226, 227, 228, 281 and 282). + nfacctd: Cisco ASA NetFlow v9 NSEL field types 40001, 40002, 40003, 40004 and IPFIX/Cisco ASA NetFlow v9 NSEL msecs absolute timestamps field types 152, 153 and 323 have been added. + nfacctd: introduced support for 'new' TCP/UDP source/destination ports (field types 180, 181, 182, 183), as per IPFIX standard, basing on the L4 protocol value (if any is specified as part of the export; otherwise assume L4 is not TCP/UDP). + nfacctd, nfprobe: introduced support for application classification via NetFlow v9 field type #95 (application ID) and application name table option. This feature aligns with Cisco NBAR-NetFlow v9 integration feature. + nfacctd: introduced support for egress bytes and packet counters (field types 23, 24) basing on the direction value (if any is specified as part of the export; otherwise assume ingress as per RFC3954). + nfprobe: egress IPv4/IPv6 NetFlow v9 templates have been introduced; compatibility with Cisco (no use of OUT_BYTES, OUT_OUT_PACKETS) taken into account. + nfacctd: added support for egress datalink NetFlow v9 fields basing on direction field. + nfacctd, sfacctd: aggregate_filter can now filter against TCP flags; also, [ns]facctd_net directive can now be specified per-plugin. + BGP daemon: introduced support for IPv6 transport of BGP messaging. + BGP daemon: BGP peer information is now linked into the status table for caching purposes. This optimization results in good CPU savings in bigger deployments. ! fix, nfacctd, sfacctd: daemons were crashing on OpenBSD platform upon setting an aggregate_filter configuration directive. Patch is courtesy of Manuel Pata. ! fix, xflow_status.c: status entries were not properly linked to the hash conflict chain resulting in a memory leak. However the maximum number of table entries set by default was preventing the structure to grow undefinitely. ! fix, sql_common.c: increased buffer size available for sql_table_schema from 1KB to 8KB. Thanks to Michiel Muhlenbaumer his support. ! fix, bgp_agent_map has been improved to allow mapping of NetFlow/sFlow agents making use of IPv6 transport to either a) IPv4 transport address of BGP sessions or b) 32-bit BGP Router IDs. Mapping to IPv6 addresses is however not (yet) possible. ! fix, nfprobe: encoding of NetFlow v9 option scope has been improved; nfprobe source IPv4/IPv6 address, if specified via nfprobe_source_ip directive, is now being written. ! fix, util.c: string copies in trim_spaces(), trim_all_spaces() and strip_quotes() have been rewritten more safely. Patch is courtesy of Dmitry Koplovich. ! fix, sfacctd: interface format is now merged back into interface value fields so to ease keeping track of discards (and discard reasons) and multicast fanout. ! fix, MySQL, SQLite3 plugins: sql table version 8 issued to provide common naming convention when mapping primitives to database fields among the supported RDBMS base. Thanks to Chris Wilson for his support. ! fix, pmacct client: numeric variables output converted to unsigned from signed. ! fix, nfacctd_net, sfacctd_net: default value changed from null (and related error message) to 'netflow' for nfacctd_net and 'sflow' for sfacctd_net. ! fix, nfacctd, sfacctd: aggregate_filter was not catching L2 primitives (VLAN, MAC addresses) when performing egress measurements. 0.12.4 -- 01-10-2010 + BGP daemon: a new memory model is introduced by which IP prefixes are being shared among the BGP peers RIBs - leading to consistent memory savings whenever multiple BGP peers export full tables due to the almost total overlap of information. Longest match nature of IP lookups required to raise BGP peer awareness of the lookup algorithm. Updated INTERNALS document to support estimation of the memory footprint of the daemon. + BGP daemon: a new bgp_table_peer_buckets configuration directive is introduced: per-peer routing information is attached to IP prefixes and now hashed onto buckets with conflict chains. This parameter sets the number of buckets of such hash structure; the value is directly related to the number of expected BGP peers, should never exceed such amount and is best set to 1/10 of the expected number of peers. + nfprobe: support has been added to export direction field (NetFlow v9 field type #61); its value, 0=ingress 1=egress, is determined via nfprobe_direction configuration directive. + nfacctd: introduced support for Cisco ASA bytes counter, NetFlow v9 field type #85. Thanks to Ralf Reinartz for his support. + nfacctd: improved flow recognition heuristics for cases in which IPv4/IPv6/input/output data are combined within the same NetFlow v9 template. Thanks to Carsten Schoene for his support. ! fix, BGP daemon: bgp_nexthop_followup was not working correctly if pointed to a non-existing next-hop. ! fix, nfv9_template.c: ignoring unsupported NetFlow v9 field types; improved template logging. Thanks to Ralf Reinartz for his support. ! fix, print plugin: support for interfaces and network masks has been added. Numeric variables output converted to unsigned from signed. 0.12.3 -- 28-07-2010 + 'cos' aggregation primitive has been implemented providing support for 802.1p priority. Collection is supported via sFlow, libpcap and ULOG; export is supported via sFlow. + BGP daemon: TCP MD5 signature implemented. New 'bgp_daemon_md5_file' configuration directive is being added for the purpose of defining peers and their respective MD5 keys, one per line, in CSV format. The map is reloadable at runtime: existing MD5 keys are removed via setsockopt(), new ones are installed as per the newly supplied map. Sample map added in 'examples/bgp_md5.lst.example'. + BGP daemon: added support for RFC3107 (SAFI=4 label information) to enable receipt of labeled IPv4/IPv6 unicast prefixes. + nfprobe, sfprobe: introduced the concept of traffic direction. As a result, [ns]fprobe_direction and [ns]fprobe_ifindex configuration directives have been implemented. + [ns]fprobe_direction defines traffic direction. It can be statically defined via 'in' or 'out' keywords; values can also be dynamically determined through a pre_tag_map (1=input, 2=output) by means of 'tag' and 'tag2' keywords. + [ns]fprobe_ifindex either statically associate an interface index (ifIndex) to a given [ns]fprobe plugin or semi-dynamically via lookups against a pre_tag_map by means of 'tag' and 'tag2' keywords. + sfprobe: sfprobe_ifspeed configuration directive is introduced and aimed at statically associating an interface speed to an sfprobe plugin. + sfprobe: Switch Extension Header support added. Enabler for this development was support for 'cos' and in/out direction. Whereas VLAN information was already supported as an aggregation primitive. + sfprobe: added support for Counter Samples for multiple interfaces. Sampling function has been brought to the plugin so that Counter Samples can be populated with real bytes/packets traffic levels. ! nfprobe, sfprobe: send buffer size is now aligned to plugin_pipe_size, if specified, providing a way to tune buffers in case of sustained exports. ! fix, addr.c: pm_ntohll() and pm_htonll() routines rewritten. These are aimed at changing byte ordering of 64-bit variables. ! fix, BGP daemon: support for IPv6 global address/link-local address next-hops as part of MP_REACH_NLRI parsing. ! fix, cfg_handlers.c: bgp_daemon and bgp_daemon_msglog parsing was not correct, ie. enabled if specified as 'false'. Thanks to Brent Van Dussen for reporting the issue. ! fix, bgp.c: found a CPU hog issue caused by missing cleanup of the select() descriptors vector. ! fix, pmacct.c: in_iface/out_iface did erroneously fall inside a section protected by the "--disable-l2" switch. Thanks to Brent Van Dussen for reporting the issue. 0.12.2 -- 27-05-2010 + A new 'tee' plugin is introduced bringing both NetFlow and sFlow replication capabilities to pmacct. It supports transparent mode (tee_transparent), coarse-grained filtering capabilities via the Pre-Tagging infrastructure. Quickstart guide is included as part of the EXAMPLES file (chapter XII). + nfprobe, sfprobe: introduced support for export of the BGP next-hop information. Source data selection for BGP next-hop is being linked to [pmacctd_as|uacctd_as] configuration directive. Hence it must be set to 'bgp' in order for this feature to work. + nfprobe, sfprobe, BGP daemon: new set of features (nfprobe_ipprec, sfprobe_ipprec, bgp_daemon_ipprec) allows to mark self-originated sFlow, NetFlow and BGP datagrams with the supplied IP precedence value. + peer_src_ip (IP address of the NetFlow emitter, agent ID of the sFlow emitter) and peer_dst_ip (BGP next-hop) can now be filled from NetFlow/sFlow protocols data other than BGP. To activate the feature nfacctd_as_new/sfacctd_as_new have to be 'false' (default value), 'true' or 'file'. + print plugin: introduced support for Comma-Separated Values (CSV) output in addition to formatted-text. A new print_output feature allows to switch between the two. + pmacctd: improved 802.1ad support. While recursing, outer VLAN is always reported as value of the 'vlan' primitive. ! fix, pmacctd: 802.1p was kept integral part of the 'vlan' value. Now a 0x0FFF mask is applied in order to return only the VLAN ID. ! fix, pkt_handlers.c: added trailing '\0' symbol when truncating AS-PATH and BGP community strings due to length constraints. ! fix, sql_common.c: maximum SQL writers warning message was never reached unless a recovery method is specifited. Thanks to Sergio Charpinel Jr for reporting the issue. ! fix, MySQL and PostgreSQL plugins: PGRES_TUPLES_OK (PostgreSQL) and errno 1050 (MySQL) are now considered valid return codes when dynamic tables are involved (ie. sql_table_schema). Thanks to Sergio Charpinel Jr for his support. ! fix, BGP daemon: pkt_bgp_primitives struct has been explicitely 64-bit aligned. Mis-alignment was causing crashes when buffering was enabled (plugin_buffer_size). Verified on Solaris/sparc. 0.12.1 -- 07-04-2010 + Input/output interfaces (SNMP indexes) have now been implemented natively; it's therefore not required anymore to pass through the (Pre-)tag infrastructure. As a result two aggregation primitives are being introduced: 'in_iface' and 'out_iface'. + Support for source/destination IP prefix masks is introduced via two new aggregation primitives: src_mask and dst_mask. These are populated as defined by the [nf|sf|pm|u]acctd_net directive: NetFlow/sFlow protocols, BGP, Network files (networks_file) or static (networks_mask) being valid data sources. + A generic tunnel inspection infrastructure has been developed to benefit both pmacctd and uacctd daemons. Handlers are defined via configuration file. Once enabled daemons will account basing upon tunnelled headers rather than the envelope. Currently the only supported tunnel protocol is GTP, the GPRS tunnelling protocol (which can be configured as: "tunnel_0: gtp, "). Up to 8 different tunnel stacks and up to 4 tunnel layers per stack are supported. First matching stack, first matching layer wins. + uacctd: support for the MAC layer has been added for the Netlink/ ULOG Linux packet capturing framework. + 'nfprobe_source_ip' feature introduced: it allows to select the IPv4/IPv6 address to be used to export NetFlow datagrams to the collector. + nfprobe, sfprobe: network masks are now exported via NetFlow and sFlow. 'pmacctd_net' and its equivalent directives define how to populate src_mask and dst_mask values. ! cleanup, nfprobe/sfprobe: data source for 'src_as' and 'dst_as' primitives is now expected to be always explicitely defined (in line with how 'src_net' and 'dst_net' primitives work). See the UPGRADE doc for the (limited) backward compatibility impact. ! Updated SQL documentation: sql/README.iface guides on 'in_iface' and 'out_iface' primitives; sql/README.mask guides on 'src_mask' and 'dst_mask' primitives; sql/README.is_symmetric guides on 'is_symmetric' primitive. ! fix, nfacctd.h: source and destination network masks were twisted in the NetFlow v5 export structure definition. Affected releases are: 0.12.0rc4 and 0.12.0. ! fix, nfprobe_plugin.c: l2_to_flowrec() was missing some variable declaration when the package was configured for compilation with --disable-l2. Thanks to Brent Van Dussen for reporting the issue. ! fix, bgp.c: bgp_attr_munge_as4path() return code was not defined for some cases. This was causing some BGP messages to be marked as malformed. ! fix, sfprobe: a dummy MAC layer was created whenever this was not included as part of the captured packet. This behaviour has been changed and header protocol is now set to 11 (IPv4) or 12 (IPv6) accordingly. Thanks to Neil McKee for pointing the issue. ! workaround, building sub-system: PF_RING enabled libpcap was not recognized due to missing of pcap_dispatch(). This is now fixed. 0.12.0 -- 16-02-2010 + 'is_symmetric' aggregation primitive has been implemented: aimed at easing detection of asymmetric traffic. It's based on rule definitions supplied in a 'bgp_is_symmetric_map' map, reloadable at runtime. + A new 'bgp_daemon_allow_file' configuration directive allows to specify IP addresses that can establish a BGP session with the collector's BGP thread. Many thanks to Erik van der Burg for contributing the idea. + 'nfacctd_ext_sampling_rate' and 'sfacctd_ext_sampling_rate' are introduced: they flag the daemon that captured traffic is being sampled. Useful to tackle corner cases, ie. the sampling rate reported by the NetFlow/sFlow agent is missing or incorrect. + The 'bgp_follow_nexthop' feature has been extended so that extra IPv4/IPv6 prefixes can be supplied. Up to 32 IP prefixes are now supported and a warning message is generated whenever a supplied string fails parsing. + Pre-Tagging: implemented 'src_local_pref' and 'src_comms' keys. These allow tagging based on source IP prefix local_pref (sourced from either a map or BGP, ie. 'bgp_src_local_pref_type: map', 'bgp_src_local_pref_type: bgp') and standard BGP communities. + Pre-Tagging: 'src_peer_as' key was extended in order to match on BGP-sourced data (bgp_peer_src_as_type: bgp). + Pre-Tagging: introduced 'comms' key to tag basing on up to 16 standard BGP communities attached to the destination IP prefix. The lookup is done against the BGP RIB of the exporting router. Comparisons can be done in either match-any or match-all fashion; xidDocumentation and examples updated. ! fix, util.c: load_allow_file(), empty allow file was granting a connection to everybody being confused with a 'no map' condition. Now this case is properly recognized and correctly translates in a reject all clause. ! fix, sql_common.c: log of NetFlow micro-flows to a SQL database (nfacctd_sql_log directive) was not correctly getting committed to the backend, when sql_history was disabled. ! fix, mysql|pgsql|sqlite_plugin.c: 'flows' aggregation primitive was not suitable to mix-and-match with BGP related primitives (ie. peer_dst_as, etc.) due to an incorrect check. Many thanks to Zenon Mousmoulas for the bug report. ! fix, pretag_handlers.c: tagging against NetFlow v9 4-bytes in/out interfaces was not working properly. Thanks to Zenon Mousmoulas for reporting the issue. 0.12.0rc4 -- 21-12-2009 + BGP-related source primitives are introduced, namely: src_as_path, src_std_comm, src_ext_comm, src_local_pref and src_med. These add to peer_src_as which was already implemented. All can be resolved via reverse BGP lookups; peer_src_as, src_local_pref and src_med can also be resolved via lookup maps which support checks like: bgp_nexthop (RPF), peer_dst_as (RPF), input interface and source MAC address. Many thanks to Zenon Mousmoulas and GRNET for their fruitful cooperation. + Memory structures to store BGP-related primitives have been optimized. Memory is now allocated only for primitives part of the selected aggregation profile ('aggregate' config directive). + A new 'bgp_follow_nexthop' configuration directive is introduced to follow the BGP next-hop up to the edge of the routing domain. This is particularly aimed at networks not running MPLS, where hop-by-hop routing is in place. + Lookup maps for BGP-related source primitives (bgp_src_med_map, bgp_peer_src_as_map, bgp_src_local_pref_map): result of check(s) can now be the keyword 'bgp', ie. 'id=bgp' which triggers a BGP lookup. This is thought to handle exceptions to static mapping. + A new 'bgp_peer_as_skip_subas' configuration directive is being introduced. When computing peer_src_as and peer_dst_as, returns the first ASN which is not part of a BGP confederation; if only confederated ASNs are on the AS-Path, the first one is returned instead. + Pre-Tagging: support has been introduced for NetFlow v9 traffic direction (ingress/egress). + Network masks part of NetFlow/sFlow export protocols can now be used to compute src_net, dst_net and sum_net primitives. As a result a set of directives [nfacctd|sfacctd|pmacctd|uacctd]_net allows to globally select the method to resolve such primitives, valid values being: netflow, sflow, file (networks_file), mask (networks_mask) and bgp (bgp_daemon). + uacctd: introduced support for input/output interfaces, fetched via NetLink/ULOG API; interfaces are available for Pre-Tagging, and inclusion in NetFlow and sFlow exports. The implementation is courtesy of Stig Thormodsrud. + nfprobe, sfprobe: new [nfprobe|sfprobe]_peer_as option to set source/destination ASNs, part of the NetFlow and sFlow exports, to the peer-AS rather than origin-AS. This feature depends on a working BGP daemon thread setup. ! A few resource leaks were detected and fixed. Patch is courtesy of Eric Sesterhenn. ! bgp/bgp.c: thread concurrency was detected upon daemon startup under certain conditions. As a solution the BGP thread is being granted a time advantage over the traffic collector thread. ! bgp/bgp.c: fixed a security issue which could have allowed a malicious user to disrupt established working BGP sessions by exploiting the implemented concept of BGP session replenishment; this has been secured by a check against the session holdtime. Many thanks to Erik van der Burg for spotting the issue. ! bgp/bgp.c: BGP listener socket now sets SO_REUSEADDR option for quicker turn around times while stopping/starting the daemon. ! net_aggr.c: default route (0.0.0.0/0) was considered invalid; this is now fixed. 0.12.0rc3 -- 28-10-2009 + Support for NetFlow v9 sampling via Option templates and data is introduced; this is twofold: a) 'nfacctd_renormalize' configuration directive is now able to renormalize NetFlow v9 data on-the-fly by performing Option templates management; b) 'nfprobe', the NetFlow probe plugin, is able to flag sampling rate (either internal or external) when exporting flows to the collector. + '[pm|u]acctd_ext_sampling_rate' directives are introduced to support external sampling rate scenarios: packet selection is performed by the underlying packect capturing framework, ie. ULOG, PF_RING. Making the daemon aware of the sampling rate, allows to renormalize or export such information via NetFlow or sFlow. + pmacctd: the IPv4/IPv6 fragment handler engine was reviewed to make it sampling-friendly. The new code hooks get enabled when external sampling (pmacctd_ext_sampling_rate) is defined. + A new 'uacctd' daemon is added to the set; it is based on the Netlink ULOG packet capturing framework; this implies it works only on Linux and can be optionally enabled when compling by defining the '--enable-ulog' switch. The implementation is fully orthogonal with the existing feature set. Thanks very much to: A.O. Prokofiev for contributing the original idea and code; Stig Thormodsrud for his support and review. + The 'tag2' primitive is introduced. Its aim is to support traffic matrix scenarios by giving a second field dedicated to tag traffic. In a pre_tag_map this can be employed via the 'id2' key. See examples in the 'examples/pretag.map.example' document. SQL plugins write 'tag2' content in the 'agent_id2' field. Read 'sql/README.agent_id2' document for reference. + Some new directives to control and re-define file attributes written by the pmacct daemons, expecially when launched with increased priviledges, are introduced: file_umask, files_uid, files_gid. Files to which these apply include, ie. pidfile, logfile and BGP neighbors file. ! fix, bgp/bgp.c: upon reaching bgp_daemon_max_peers threshold, logs were flooded by warnings even when messages were coming from a previously accepted BGP neighbor. Warnings are now sent only when a new BGP connection is refused. ! fix, nfprobe/netflow9.c: tags (pre_tag_map, post_tag) were set per pair of flows, not respecting their uni-directional nature. It was generating hiding of some tags. ! fix, nfprobe/netflow9.c: templates were (wrongly) not being included in the count of flows sent in NetFlow v9 datagrams. While this was not generating any issues with parsing flows, it was originating visualization issues in Wireshark. ! fix, SQL plugins: CPU hitting 100% has been determined when sql_history is disabled but sql_history_roundoff is defined. Thanks to Charlie Allom for reporting the issue. ! fix, sfacctd.c: input and output interfaces (non-expaneded format) were not correcly decoded creating issues to Pre- tagging. Thanks to Jussi Sjostrom for reporting the issue. 0.12.0rc2 -- 09-09-2009 + BGP daemon thread has been tied up with both the NetFlow and sFlow probe plugins, nfprobe and sfprobe, allowing to encode dynamic ASN information (src_as, dst_as) instead of reading it from text files. This finds special applicability within open-source router solutions. + 'bgp_stdcomm_pattern_to_asn' feature is introduced: filters BGP standard communities against the supplied pattern. The first matching community is split using the ':' symbol. The first part is mapped onto the peer AS field while the second is mapped onto the origin AS field. The aim is to deal with prefixes on the own address space. Ie. BGP standard community XXXXX:YYYYY is mapped as: Peer-AS=XXXXX, Origin-AS=YYYYY. + 'bgp_neighbors_file' feature is introduced: writes a list of the BGP neighbors in the established state to the specified file. This gets particularly useful for automation purposes (ie. auto-discovery of devices to poll via SNMP). + 'bgp_stdcomm_pattern' feature was improved by supporting the regex '.' symbol which can be used to wildcard a pre-defined number of characters, ie. '65534:64...' will match community values in the range 64000-64999 only. + SQL preprocess layer: removed dependency between actions and checks. Overral logics was reviewed to act more consistently with recently introduced SQL cache entry status field. + SQL common layer: poll() timeout is now calculated adaptively for increased deadline precision. + sql_startup_delay feature functionality was improved in order to let it work as a sliding window to match NetFlow setups in which a) mainain original flow timestamps and b) enable the sql_dont_try_update feature is required. ! DST (Daylight Saving Time) support introduced to sql_history and sql_refresh_time directives. Thanks to for reporting the issue. ! fix, pmacctd.c: initial sfprobe plugin checks were disabling IP fragments handler. This was causing pmacctd to crash under certain conditions. Thanks to Stig Thormodsrud for having reported the issue. ! fix, nfprobe, netflow5.c: missing htons() call while encoding src_as primitive. ! fix, BGP thread, bgp_aspath.c: estimated AS-PATH length was not enough for 32-bit ASNs. String length per-ASN increased from 5 to 10 chars. ! Documentation update, EXAMPLES: how to establish a local BGP peering between pmacctd and Quagga 0.99.14 for NetFlow and sFlow probe purposes. ! fix, print_status_table(): SEGV was showing up while trying to retrieve xFlow statistics by sending a SIGUSR1 signal and a collector IP address was not configured. ! ip_flow.[c|h]: code cleanup. 0.12.0rc1 -- 01-08-2009 + a BGP daemon thread has been integrated in both the NetFlow and sFlow collectors, nfacctd and sfacctd. It maintains per- peer RIBs and supports MP-BGP (IPv4, IPv6) and 32-bit ASNs. As a result the following configuration directives are being introduced: bgp_daemon, bgp_daemon_ip, bgp_daemon_max_peers, bgp_daemon_port and bgp_daemon_msglog. For a quick-start and implementation notes refer to EXAMPLES document and detailed configuration directives description in CONFIG-KEYS. + A new set of BGP-related aggregation primitives are now supported by the "aggregate" directive: std_comm, ext_comm, as_path, peer_src_ip, peer_dst_ip, peer_src_as, peer_dst_as, med, local_pref. A few extra directives are being introduced to support (filter, map, cut down, etc.) some primitives: bgp_peer_src_as_type, bgp_peer_src_as_map, bgp_aspath_radius, bgp_stdcomm_pattern and bgp_extcomm_pattern. + nfacctd_as_new supports a new value "bgp". It is meant to populate src_as and dst_as primitives by looking up source and destination IP prefixes against the NetFlow (or sFlow) agent RIB. + A new sql_table_type directive is introduced: by combining it with sql_table_version, defines one of the standard BGP tables. + Two new directives have been developed to support scenarios where NetFlow (or sFlow) agents are not running BGP or have default-only or partial views: bgp_follow_default and bgp_agent_map. + 4-bytes ASNs are now supported: including NetFlow and sFlow collectors, NetFlow and sFlow probes, networks_file to map prefixes to ASNs. The new BGP daemon implementation is, of course, fully compliant. + Pre-Tagging: the ID is now a 32-bit unsigned value (it was 16-bit). As a result, there valid tags can be in the range 1-4294967295 and maps can now express the resulting ID as an IPv4 address (ie. bgp_agent_map). + Pre-tagging: support for 32-bit input/output interfaces is now available. ! fix, sql_common.c: read_SQLquery_from_file() was returning a random value, regardless of the successful result. Patch has been provided provided by Giedrius Liubavicius ! fix, pmacct.c: when unused, source/destination IP address fields were presented as NULL values. This is now replaced with a '0' value to improve output parsing. ! Standard major release compilation check-pointing: thanks very much to Manuel Pata and Tobias Lott for their strong support with OpenBSD and FreeBSD respectively. 0.11.6 -- 07-04-2009 + Introduced support for tag ranges into the 'pre_tag_filter' configuration directive (ie. '10-20' matches traffic tagged in the range 10..20). This works both in addition to and in combination with negations. + Tcpdump-style filters, ie. 'aggregate_filter', now support indexing within a packet, ie. 'ether[12:2]', to allow a more flexible separation of the traffic. + Introduced support for descriptions in networks definition files pointed by the 'networks_file' configuration directive. Thanks to Karl O. Pinc for contributing the patch. ! fix, pmacctd: libpcap DLT_LINUX_SLL type is not defined in older versions of the library. It was preventing successful compilation of pmacct on OpenBSD. This has been fixed by defining internally to pmacct all DLT types in use. Thanks to Karl O. Pinc for his support. ! fix, IPv6 networks_file, load_networks6(): wrong masks were applied to IPv6 networks due to dirty temporary buffers for storing IPv6 addresses and masks. Short '::' IPv6 format is currently not supported. Thanks to Robert Blechinger for flagging the issue. ! fix, pretag.c: Pre-Tagging infrastructure was SEGV'ing after having been instructed to reload via a SIGHUP signal. Patch is courtesy of Denis Cavrois and the Acipia development team. ! fix, sfacctd, nfacctd: Assign16() was not handling correctly 2-bytes EtherType values (ie. 0x86dd, 0x8847) in 802.1Q tags. As a result 'aggregate_filter' was not able to correctly match IPv6-related filters. Thanks to Axel Apitz for reporting the issue. ! fix, xflow_status.c: a cosmetic bug was displaying sequence numbers without applying previous increment. This definitely will help troubleshooting and debugging. ! fix, sfacctd, sfv245_check_status(): AF of the sFlow agent is now explicitely defined: when IPv6 is enabled the remote peer address can be reported as IPv4-mapped IPv6 address. This was causing warning messages to report the wrong sFlow agent IP address. Thanks to Axel Apitz for reporting the issue. ! fix, IMT plugin was crashing upon receipt of a classification table request (WANT_CLASS_TABLE) when stream classification was actually disabled. ! fix, pmacct.c: classifier index was not brought back to zero by the pmacct client. This was preventing the client to show correct stream classification when it was feeded with multiple queries. The fix is courtesy of Fabio Cairo. ! fix, MySQL plugin: upon enabling of the 'nfacctd_sql_log' directive, 'stamp_updated' field was incorrectly reported as '0000-00-00 00:00:00' due to wrong field formatting. Thanks to Brett D'Arcy for reporting and patching the issue. ! Initial effort to clean the code up by strcpy() calls. Thanks to Karl O. Pinc for taking such initiative. 0.11.5 -- 21-07-2008 + SQL UPDATE queries code has been rewritten for increased flexibility. The SET statement is now a vector and part of it has been shifted into the sql_compose_static_set() routine in the common SQL layer. + A new sql_locking_style directive is now supported in the MySQL plugin. To exploit it, an underlying InnoDB table is mandatory. Thanks to Matt Gillespie for his tests. + Support for Endace DAG cards is now available; this has been tested against libDAG 3.0.0. Many thanks to Robert Blechinger for his extensive support. + pmacctd, the Linux Cooked device (DLT_LINUX_SLL) handler has been enhanced by supporting 'src_mac' and 'vlan' aggregation primitives. ! fix, xflow_status.c: NetFlow/sFlow collector's IP address is being rewritten as 0.0.0.0 when NULL. Was causing SEGVs on Solaris/sparc. ! fix, server.c: WANT_RESET is copied in order to avoid losing it when handling long queries and need to fragment the reply. Thanks very much to Ruben Laban for his support. ! fix, MySQL plugin: the table name is now escaped in order to not conflict with reserved words, if one of those is selected. Thanks to Marcel Hecko for reporting the bug. ! An extra security check is being introduced in sfacctd as an unsupported extension sent over by a Foundry Bigiron 4000 kit was causing SEGV issues. Many Thanks to Michael Hoffrath for the strong support provided. ! fix, 'nfprobe' plugin: AS numbers were not correctly exported to the collector when pmacctd was in use. Patch is courtesy of Emerson Pinter. ! fix, 'nfprobe' plugin: MACs were not properly encapsulated resulting in wrong addresses being exported through NetFlow v9. The patch is courtesy of Alexander Bergolth. ! fix, buffers holding MAC address strings throughout the code had not enough space to store the trailing zero. The patch is courtesy of Alexander Bergolth. ! fix, logfile FD was not correctly passed onto active plugins. The patch is courtesy of Denis Cavrois. ! Missing field type 60 in NetFlow v9 IPv6 flows, was leading nfacctd to incorrect flow type selection (IPv4). An additional check on the source IP address has now been included to infer IPv6 flows. RFC3954 mandates such field type to be present for IPv6 flows. The issue has been verified against a Cisco 7600 w/ RSP720. Many thanks to Robert Blechinger for his extensive support. 0.11.4 -- 25-04-2007 + support for TCP flags has been introduced. Flags are ORed on a per-aggregate basis (same as what NetFlow does on a per-flow basis). The 'aggregate' directive now supports the 'tcpflags' keyword. SQL tables v7 have also been introduced in order to support the feature inside the SQL plugins. + 'nfacctd_sql_log' directive is being introduced. In nfacctd, it makes SQL plugins to use a) NetFlow's First Switched value as "stamp_inserted" timestamp and b) Last Switched value as "stamp_updated" timestamp. Then, a) by not aggregating flows and b) not making use of timeslots, this directive allows to log singular flows in the SQL database. + sfprobe and nfprobe plugins are now able to propagate tags to remote collectors through sFlow v5 and NetFlow v9 protocols. The 'tag' key must be appended to sfprobe/nfprobe 'aggregate' config directives. + pmacct memory client is now able to output either TopN bytes, flows or packets statistics. The feature is enabled by a new '-T' commandline switch. + The Pre-Tagging map is now dynamically allocated and a new 'pre_tag_map_entries' config directive allows to set the size of the map. Its default value (384) should be suitable for most common scenarios. ! Bugfix in nfprobe plugin: struct cb_ctxt was not initialized thus causing the application to exit prematurely (thinking it finished available memory). Thanks to Elio Eraseo for fixing the issue. ! Some misplaced defines were preventing 0.11.3 code to compile smoothly on OpenBSD boxes. Thanks to Dmitry Moshkov for fixing it. ! Bugfix in SQL handlers, MY_count_ip_proto_handler(): an array boundary was not properly checked and could cause the daemon to SEGV receiving certain packets. Thanks to Dmitry Frolov for debugging and fixing the issue. ! NF_counters_renormalize_handler() renormalizes sampled NetFlow v5 flows. It now checks whether a positive Sampling Rate value is defined rather than looking for the Sampling Mode. It makes the feature working on Juniper routers. Thanks once again to Inge Bjornvall Arnesen. 0.11.3 -- 31-01-2007 + 'aggregate_filter' directive now supports multiple pcap-style filters, comma separated. This, in turn, allows to bind up to 128 filters to each activated plugin. + nfacctd and sfacctd turn-back time when restarting the daemon has been significantly improved by both creating new listening sockets with SO_REUSEADDR option and disassociating them first thing on receiving SIGINT signal. + A new threaded version of pmacctd stream classification engine is being introduced. Code status is experimental and disabled by default; it could be enabled by providing --enable-threads at configure time. Many thanks to Francois Deppierraz and Eneo Tecnologia for contributing this useful piece of code. + A new 'flow_handling_threads' configuration directive allows to set the number of threads of the stream classification engine, by default 10. + A couple new '[ns]facctd_disable_checks' config directives aim to disable health checks over incoming NetFlow/sFlow streams (ie. in cases of non-standard vendor's implementations). Many thanks to Andrey Chernomyrdin for his patch. ! sfv245_check_status() was running checks (ie. verify sequence numbers) using sender's IP address. More correctly, it has to look at the Agent Address field included in sFlow datagrams. Many thanks to Juraj Sucik for spotting the issue. ! nfprobe plugin was not compiling properly in conjunction with --disable-l2 configure switch. Many thanks to Inge Bjornvall Arnesen for submitting the patch. ! sfacctd: fixed a bug which was preventing 'aggregate_filter' to match values properly in src_port, dst_port, ip proto and tos fields. Thanks to Chris Fletcher for spotting the issue. ! SQL cache: fixed a bug preventing safe actions to take place correctly. It has arisen in version 0.11.2 and hadn't severe impact. 0.11.2 -- 28-11-2006 + 'sql_max_writers' configuration directive is being introduced: sets the maximum number of concurrent writer processes the SQL plugin can fire, allowing the daemon to degrade gracefully in case of major database unavailibility. + 'sql_history_since_epoch' is being introduced: enables the use of timestamps (stamp_inserted, stamp_updated) in the standard seconds since the Epoch format as an alternative to the default date-time format. + 'sql_aggressive_classification' behaviour is changed: simpler more effective. It now operates by delaying cache-to-DB purge of unknown traffic streams - which would still have chances to be correctly classified - for a few 'sql_refresh_time' slots. The old mechanism was making use of negative UPDATE queries. + The way SQL writer processes are spawned by the SQL plugin has slightly changed in order to better exploit fork()'s copy-on- write behaviour: the writer now is mostly read-only while the plugin does most write operations before spawning the writer. ! The list of environment variables passed to the SQL triggers, 'sql_trigger_exec', has been updated. ! Fixed a bug related to sequence number checks for NetFlow v5 datagrams. Thanks very much to Peter Nixon for reporting it. 0.11.1 -- 25-10-2006 + PostgreSQL plugin: 'sql_use_copy' configuration directive has been introduced; instructs the plugin to build non-UPDATE SQL queries using COPY (in place of INSERT). While providing same functionalities of INSERT, COPY is more efficient. It requires 'sql_dont_try_update' to be enabled. Thanks to Arturas Lapiene for his support during the development. + nfprobe plugin: support for IPv4 ToS/DSCP, IPv6 CoS and MPLS top-most label has been introduced. ! Some alignment issues concerning both pkt_extras structure and Core process to Plugins memory rings have been fixed. Daemons are now reported to be running ok on MIPS/SPARC architectures. Many thanks to Michal Krzysztofowicz for his strong support. ! sfprobe plugin: a maximum default limit of 256 bytes is set on packet payload copy when building Flow Samples in pmacctd (ie. if capturing full packets through libpcap, we don't want them to be entirely copied into sFlow datagrams). ! Sanity checks now take place when processing 'sql_refresh_time' values and error messages are thrown out. ! Fixes have been committed to IPv6 code in xflow_status.c as it was not compiling properly on both Solaris and IRIX. 0.11.0 -- 27-09-2006 + NetFlow v5 sampling and renormalization are now supported: a) 'nfacctd' is able to renormalize bytes/packets counters and apply Pre-Tagging basing on the sampling rate specified in the datagram; b) 'sampling_rate' config key applies to 'nfprobe' plugin which is now able to generate sampling informations. + 'nfacctd' and 'sfacctd' are now able to give out informations about the status of active NetFlow/sFlow streams in terms of good/bad/missing datagrams. Whenever an anomaly happens (ie. missing or bad packets) a detailed message is logged; overral reports are logged by sending SIGUSR1 signals to the daemon. + 'logfile' configuration directive is introduced: it allows to log directly to custom files. This adds to console and syslog logging options. ! Old renormalization structure, renorm_table, has been dropped; the new one, which applies to both NetFlow and sFlow, is tied into the brand new xflow_status_table structure. ! When 'nfacctd_as_new' was not in use, NetFlow v5 src_as/dst_as values were erroneously swapped. Thanks to Thomas Stegbauer for reporting the bug. ! Incorrect timeout value for poll() has been fixed in 'sfprobe' plugin. It was leading the plugin to take too much resources. ! 'nfprobe' plugin was inserting jumps while generating sequence numbers. ! 'nfprobe' plugin behaviour in handling 'networks_file' content has been changed and now equals 'sfprobe': IP addresses which are not belonging to known networks/ASNs are no longer zeroed. ! 'sfprobe' was not generating correct sample_pool values. 0.11.0rc3 -- 30-08-2006 + 'sfprobe' plugin can now transport packet/flow classification tags inside sFlow v5 datagrams. Then, such tags can be read by the sFlow collector, sfacctd. + 'sfprobe' plugin is able to encapsulate basic Extended Gateway informations (src_as, dst_as) into sFlow v5 datagrams starting from a Networks File - networks_file configuration directive. + 'nfprobe' now supports network data coming from libpcap/tcpdump style savefile ('pcap_savefile', -I). + pmacctd is now able to capture packets from DLT_NULL, which is BSD loopback encapsulation link type. Thanks to Gert Burger for his support. + Sampling layer has been improved: it's now able to sample flows from NetFlow datagrams (not only packets arriving through sFlow or libpcap); 'sfprobe' sampling layer has been tied into this mechanism and as a result, 'sfprobe_sampling_rate' is now an alias for 'sampling_rate' and its default value is 1 (ie. no sampling). This change will benefit 'sfprobe' in terms of better efficiency. + A new 'pmacctd_flow_buffer_buckets' directive defines the number of buckets of the Flow Buffer. This value has to scale to higher power of 2 accordingly to the link traffic rate and is useful when packet classification is enabled. Many thanks for testing, debugging and support go to Steve Cliffe. + A new 'sql_locking_style' directive allows to choose among two types of locking: "table" (default) and "row". More details are in the CONFIG-KEYS document. "row" locking has to be considered as experimental. Many thanks go to Aaron Glenn and Peter Nixon for their close support, work and thoughts. ! IPv6 support is now working; it was broken in 0.11.0rc2; thanks to Nigel Roberts for signalling and fixing the issue. ! Fixed a few issues concerning the building system and related to the introduction of some new subtrees. Thanks to Kirill Ponomarew and Peter Nixon for signalling them. ! Fixed some signal()-related issues when running the package under DragonflyBSD. Being fork of FreeBSD 4.x, it needs same cautions. Thanks to Aaron Glenn for his support. 0.11.0rc2 -- 08-08-2006 + 'nfprobe' plugin can now transport packet/flow classification tags inside NetFlow v9 datagrams, using custom field type 200. Then, such tags can be read by the NetFlow collector, nfacctd. + 'nfprobe' plugin has now ability to select a Engine Type/Engine ID through a newly introduced 'nfprobe_engine' config directive. It will mainly allow a collector to distinguish between distinct probe instances originating from the same IP address. + 'nfprobe' plugin now can automagically select different NetFlow v9 template IDs, useful when multiple 'nfprobe' plugins run as part of the same daemon instance. + 'sfprobe' plugin is now able to redistribute NetFlow flows into sFlow samples. This adds to sFlow -> sFlow and libpcap -> sFlow. + A new data structure to pass extended data to specific plugins has been added. It is placed on the ring, next to pkt_data. It is meant to pass extra data to plugins and, same time, avoiding to inflate the main data structure. ! Wrong arguments were injected into a recently introduced Log() call in plugin_hooks.c; it's now fixed: under certain conditions, this was generating SEGV at startup while using 'sfprobe' plugin. ! Updated documentation; examples and quickstart guides for using pmacct as both emitter and collector of NetFlow and sFlow have been added. - Hooks to compile pmacct the no-mmap() style have been removed. 0.11.0rc1 -- 20-07-2006 + pmacct DAEMONS ARE NOW ABLE TO CREATE AND EXPORT NETFLOW PACKETS: a new 'nfprobe' plugin is available and allows to create NetFlow v1/v5/v9 datagrams and export them to a IPv4/IPv6 collector. The work is based on softflowd 0.9.7 software. A set of configuration directives allows to tune timeouts (nfprobe_timeouts), cache size (nfprobe_maxflows), collector parameters (nfprobe_receiver), TTL value (nfprobe_hoplimit) and NetFlow version of the datagrams to be exported (nfprobe_version). Many thanks to Ivan A. Beveridge, Peter Nixon and Sven Anderson for their support and thoughts and to Damien Miller, author of softflowd. + pmacct DAEMONS ARE NOW ABLE TO CREATE AND EXPORT SFLOW PACKETS: a new 'sfprobe' plugin is available and allows to create sFlow v5 datagrams and export them to a IPv4 collector. The work is based on InMon sFlow Agent 5.6 software. A set of configuration directives allows to tune sampling rate (sfprobe_sampling_rate), sFlow agent IP address (sfprobe_agentip), collector parameters (sfprobe_receiver) and agentSubId value (sfprobe_agentsubid). Many thanks to InMon for their software and Ivan A. Beveridge for his support. ! An incorrect pointer to the received packet was preventing Pre- Tagging filters to work correctly against DLT_LINUX_SLL links. Many thanks to Zhuang Yuyao for reporting the issue. ! Proper checks on protocol number were missing in pmacct client program, allowing to look further the bounds of the _protocols array. Many thanks to Denis N. Voituk for patching the issue. 0.10.3 -- 21-06-2006 + New Pre-Tagging key 'label': mark the rule with label's value. Labels don't need to be unique: when jumping, the first matching label wins. + New Pre-Tagging key 'jeq': Jump on EQual. Jumps to the supplied label in case of rule match. Before jumping, the tagged flow is returned to active plugins, as it happens for any regular match (set return=false to change this). In case of multiple matches for a signle flow, plugins showing 'tag' key inside 'aggregate' directive will receive each tagged copy; plugins not receiving tags will still receive unique copy of the flow. sFlow and NetFlow are usually uni-directional, ie. ingress-only or egress-only (to avoid duplicates). Meaningful application of JEQs is tagging flows two times: by incoming interface and by outgoing one. Only forward jumps are allowed. "next" is reserved label and causes to jump to the next rule. Many thanks to Aaron Glenn for brainstormings about this point. + New Pre-Tagging key 'return': if set to 'true' (which is default behaviour) returns the current packet/flow to active plugins, in case of match. If switched to 'false', it will prevent this to happen. It might be thought either as an extra filtering layer (bound to explicit Pre-Tagging rules) or (also in conjunction with 'stack') as a way to add flexibility to JEQs. + New Pre-Tagging key 'stack': actually '+' (ie. sum symbol) is the unique supported value. This key makes sense only if JEQs are in use. When matching, accumulate IDs, using the specified operator/ function. For example, usually =. By setting 'stack=+' you will be able to get =. ! Pre-Tagging table now supports a maximum of 384 rules. Because of the newly introduced flow alteration features, tables are no longer internally re-ordered. However, IPv4 and IPv6 stacks are still segregated each other. 0.10.2 -- 16-05-2006 + A new '-l' option is supported by pmacct client tool: it allows to enable locking of the memory table explicitely, when serving the requested operation. + Pre-Tagging infrastructure is now featuring negations for almost all supported keys with the exclusion of id, ip and filter. To negate, the '-' (minus symbol) need to be prepended; eg.: id=X ip=Y in=-1 means tag with X, data received from Net/sFlow agent with IP address Y and not coming from interface 1. + pre_tag_filter config directive is now featuring same negation capabilities as Pre-Tagging infrastructure. + Q16 added to FAQS document: a sum of tips for running smoothly SQL tables. Many thanks to Wim Kerkhoff and Sven Anderson for bringing up the points. 0.10.1 -- 18-04-2006 + AS numbers and IP addresses are no more multiplexed into the same field. This ends the limitation of being unable to have both data types in the same table (which could be useful for troubleshooting purposes, for example). A new SQL table version, v6, is introduced in order to support this new data model in all SQL plugins. ! Minor fixes to PostgreSQL table schemas, v2 to v5: a) the 'vlan' field was erroneously missing from primary keys, slowing down INSERT and UPDATE queries; b) primary keys were identified as 'acct_pk', thus not allowing multiple tables of different version to share the same database; now constraint name is: 'acct_vX_pk', with X being the version number. Many thanks to Sven Anderson for catching the a) ! An alignment issue has been catched when the etheraddr_string() gets called from count_src|dst_mac_handlers() in sql_handlers.c This seems to be closely connected to a similar trouble catched by Daniel Streicher on x86_64 recently. ! Fixed an issue with mask_elem() in server.c . Both src|dst_net primitives were not (positively, ie. copied back when required) masked. 0.10.0 -- 22-03-2006 + Collectors (ie. pmacctd) are now compiled exporting full Dynamic Symbol Table. This allows shared object (SO) classifiers to call routines included in the collector code. Moreover, a small set of library functions - specifically aimed to deal smoothly with the classifiers' table - are now included in the collector code: pmct_un|register(), pmct_find_first|last_free(), pmct_isfree(), pmct_get() and pmct_get_num_entries(). For further reading, take a look to README.developers document in classifiers tarball. + Classifiers table, which is the linked-list structure containing all the active classifiers (RE + SO), is now loaded into a shared memory segment, allowing plugins to keep updated about changes to the table. Furthermore, the table is now dynamically allocated at runtime, allowing an arbitrary number of classifiers to be loaded via the new 'classifier_table_num' configuration directive. + Pre-Tagging infrastructure adds two new primitives to tag network traffic: src_as and dst_as, the source and destination Autonomous System Number (ASN). In pmacctd they work against a Network Map ('networks_file' configuration directive). In nfacctd and sfacctd they work against both sFlow/NetFlow ASN fields and Network Maps. Many thanks to Aaron Glenn for his strong support. ! PostgreSQL plugin and pmpgplay no more make use of EXCLUSIVE LOCKS whenever the sql_dont_try_update directive is activated. We assume there is no need for them in a INSERTs-only framework as integrity of data is still guaranteed by transactions. The patch has been contributed by Jamie Wilkinson, many thanks ! ! Commandline switches and a configuration file should cohexist and the formers need to take precedence over the latter, if required. This is a rather standard (and definitely more flexible) approach; before this release they were mutual exclusive. Read UPGRADE notes at this propo. Thanks for the suggestion to Ivan A. Beveridge. ! Some glibc functions (noticeably syslog()) rely upon a rather non- standard "extern char *__progname" pointer. Now, its existence is properly checked at configuration time. On Linux, setproctitle() was causing plugin name/type to get cutted down in messages sent to the syslog facility. Thanks to Karl Latiss for his bug report. ! Solved a bug involving the load of IPv6 entries from Networks Maps. It was causing the count of such entries to be always zero. 0.10.0rc3 -- 01-03-2006 + Aapplication layer (L7) classification capabilities of pmacctd have been improved: shared object (SO) classifiers have been introduced; they are loaded runtime through dlopen(). pmacct offers them support for contexts (informations gathered - by the same classifier - from previous packets either in the same uni-directional flow or in the reverse one), private memory areas and lower layer header pointers, resulting in extra flexibility. Some examples can be found at the webpage: http://www.ba.cnr.it/~paolo/pmacct/classification/ + 'classifier_tentatives' configuration key has been added: it allows to customize the number of tentatives made in order to classify a flow. The default number is five, which has proven to be ok but for certain types of classification it might result restrictive. + 'pmacctd_conntrack_buffer_size' configuration key has been added: it (intuitively) defines the size for the connection tracking buffer. + Support for Token Ring (IEEE 802.5) interfaces has been introduced in pmacctd. Many thanks to Flavio Piccolo for his strong support. + 'savefile_wait' (-W commandline) configuration key has been added: if set to true causes pmacctd to not return but wait to be killed after being finished with the supplied savefile. Useful when pushing data from a tcpdump/ethereal tracefile into a memory table (ie. to build graphs). ! An erroneous replacement of dst with src in mask_elem() was causing queries like "pmacct -c dst_host -M|-N " to return zero counters. Thanks to Ryan Sleevi for signalling the weird behaviour. ! Management of the connection tracking buffer has been changed: now, a successful search frees the matched entry instead of moving it in a chain of stale entries, available for quick reuse. ! Error logging of SQL plugins has been somewhat improved: now, error messages returned by the SQL software are forwarded to sql_db_error() This will definitely allow to exit from the obscure crypticism of some generic error strings. 0.10.0rc2 -- 14-02-2006 + CONNECTION TRACKING modules has been introduced into pmacctd: they are C routines that hint IP address/port couples for upcoming data streams as signalled by one of the parties into the control channel whenever is not possible to go with a RE classificator. Conntrack modules for FTP, SIP and RTSP protocols are included. + 'pidfile' directive way of work has been improved: firstly, whenever a collector shuts down nicely, it now removes its pidfile. Secondly, active plugins now create a pidfile too: it takes the following form: -.. Thanks to Ivan A. Beveridge for sharing his thoughts at this propo. ! Minor fixes to the classification engine: TCP packets with no payload are not considered useful classification tentatives; a new flow can inherit the class of his reverse flow whenever it's still reasonably valid. ! Solved a segmentation fault issue affecting the classificator engine, whenever the 'snaplen' directive was not specified. Thanks to Flavio Piccolo for signalling it. ! Fixed a bug in the PostgreSQL plugin: it appeared in 0.10.0rc1 and was uniquely related to the newly introduced negative UPDATE SQL query. ! INTERNALS has been updated with few notes about the new classification and connection tracking features. 0.10.0rc1 -- 24-01-2006 + PACKET CLASSIFICATION capabilities have been introduced into pmacctd: the implemented approach is fully extensible: classification patterns are based on regular expressions (RE), human-readable, must be placed into a common directory and have a .pat file extension. Many patterns for widespread protocols are available at L7-filter project homepage. To support this feature, a new 'classifiers' configuration directive has been added. It expects full path to a spool directory containing the patterns. + A new 'sql_aggressive_classification' directive has been added aswell: it allows to move unclassified packets even in the case they are no more cached by the SQL plugin. This aggressive policy works by firing negative UPDATE SQL queries that, whenever successful, are followed by positive ones charging the extra packets to their final class. ! Input and Output interface fields (Pre-Tagging) have been set to be 32 bits wide. While NetFlow is ok with 16 bits, some sFlow agents are used to bigger integer values in order to identify their interfaces. The fix is courtesy of Aaron Glenn. Thank you. ! Flow filtering troubles have been noticed while handling MPLS-tagged flows inside NetFlow v9 datagrams. Thanks to Nitzan Tzelniker for his cooperation in solving the issue. ! A new exit_all() routine now handles nicely fatal errors detected by the Core Process, after plugins creation. It avoids leaving orphan plugins after the Core Process shutdown. 0.9.6 -- 27-Dec-2005 + Support for 'sql_multi_values' has been introduced into the new SQLite 3.x plugin. It allows to chain multiple INSERT queries into a single SQL statement. The idea is that inserting many rows at the same time is much faster than using separate single-row statements. ! MySQL plugin fix: AS numbers were sent to the database unquoted while the corresponding field was declared as CHAR. By correctly wrapping AS numbers, a major performance increase (expecially when UPDATE queries are spawned) has been confirmed. Many thanks to Inge Bjørnvall Arnesen for discovering, signalling and solving the issue. ! MySQL plugin fix: multi-values INSERT queries have been optimized by pushing out of the queue purging loop the proper handling for the EOQ event. ! The introduction of the intermidiate SQL layer in the 0.9.5 version choked the dynamic SQL table creation capability. This has been fixed. Thanks to Vitalij Brajchuk for promptly signalling the issue. ! The 'pidfile' configuration key has got incorrectly disabled in both nfacctd and sfacctd. Thanks to Aaron Glenn for signalling the issue. ! The 'daemonize' (-D) configuration key was incorrectly disabling the signal handlers from the Core Process once backgrounded. As a result the daemon was not listening for incoming SIGINTs. Again, many thanks go to Aaron Glenn. 0.9.5 -- 07-Dec-2005 + PMACCT OPENS TO SQLITE 3.x: a fully featured SQLite, version 3.x only, plugin has been introduced; SQLite is a small C library that implements a self-contained, embeddable, zero-configuration SQL (almost all SQL92) database engine. The plugin is LOCK-based and supports the "recovery mode" via an alternate database action. Expecially suitable for tiny and embedded environments. The plugin can be fired using the keyword 'sqlite3'. See CONFIG-KEYS and EXAMPLES for further informations. + A new SQL layer - common to MySQL, PostgreSQL and SQLite plugins - has been introduced. It's largely callback-based and results in a major architectural change: it sits below the specific SQL code (facing the Core Process's abstraction layer) and will (hopefully) help in reducing potential bugs and will allow for a quick implementation of new SQL plugins. ! A bug concerning the setup of insert callback functions for summed (in + out) IPv6 traffic has been fixed. The issue was affecting all SQL plugins. ! A bug concerning the handling of MPLS labels has been fixed in pmacctd. Many thanks to Gregoire Tourres and Frontier Online for their support. 0.9.4p1 -- 14-Nov-2005 ! Minor bugfix in pretag.c: a wrongly placed memcpy() was preventing the code to be compiled by gcc 2.x . Many thanks to Kirill Ponomarew and Kris Kennaway for signalling the issue. ! Fixed an alignment issue revealed in the query_header structure; it has been noticed only under some circumstances: '--enable-64bit' enabled, 64bit platform and gcc 3.x . Many thanks to Aaron Glenn for his strong support in solving the issue. 0.9.4 -- 08-Nov-2005 + Hot map reload has been introduced. Maps now can be modified and then reloaded without having to stop the daemon. SIGUSR2 has been reserved for this use. The feature applies to Pre-Tagging map (pre_tag_map), Networks map (networks_file) and Ports map (ports_file). It is enabled by default and might be disabled via the new 'refresh_maps' configuration directive. Further details are in CONFIG-KEYS. ! Some major issues have been solved in the processing of libpcap-format savefiles. Some output inconsistencies were caused by a corruption of the pcap file handler; bufferization is now enabled by default and the last buffer is correctly processed. Many thanks go to Amir Plivatsky for his strong support. ! 'sql_table_schema' directive: in read_SQLquery_from_file() the strchr() has been replaced by strrchr() allowing to chain more SQL statements as part of the SQL table creation. This results useful, for example, to do CREATE INDEX after CREATE TABLE. The patch is courtesy of Dmitriy Nikulin. ! SIGTERM signal is now handled properly to ensure a better compatibility of all pmacct daemons under the daemontools framework. The patch is courtesy of David C. Maple. ! Memory plugin: some issues caused by the mix of not compatible compilation parameters have been fixed. Now the pmacct client now correctly returns a warning message if: counters are of different size (32bit vs 64bit) or IP addresses are of different size (IPv4-only vs IPv6-enabled packages). ! Print plugin, few bugfixes: the handling of the data ring shared with the Core Process was not optimal; it has been rewritten. P_exit() routine was not correctly clearing cached data. 0.9.3 -- 11-Oct-2005 + IPv4/IPv6 multicast support has been introduced in the NetFlow (nfacctd) and the sFlow (sfacctd) daemons. A maximum of 20 multicast groups may be joined by a single daemon instance. Groups can be defined by using the two sister configuration keys: nfacctd_mcast_groups and sfacctd_mcast_groups. + sfacctd: a new 'sfacctd_renormalize' config key allows to automatically renormalize byte/packet counters value basing on informations acquired from the sFlow datagram. In particular, it allows to deal with scenarios in which multiple interfaces have been configured at different sampling rates. It also calculates an effective sampling rate which could differ from the configured one - expecially at high rates - because of various losses. Such estimated rate is then used for renormalization purposes. Many thanks go to Arnaud De-Bermingham and Ovanet for the strong support offered during the development. + sfacctd: a new 'sampling_rate' keyword is supported into the Pre-Tagging layer. It allows to tag aggregates - generated from sFlow datagrams - on a sampling rate basis. + setproctitle() calls have been introduced (quite conservatively) and are actually supported on Linux and BSDs. The process title is rewritten in the aim of giving the user more informations about the running processes (that is, it's not intended to be just a cosmetic stuff). ! sql_preprocess tier was suffering a bug: actions (eg. usrf, adjb), even if defined, were totally ignored if no checks were defined aswell. Many thanks to Draschl Clemens for signalling the issue. ! Some minor bugs have been catched around sfacctd and fixed accordingly. Again, many thanks to Arnaud De-Bermingham. 0.9.2 -- 14-Sep-2005 + A new 'usrf' keyword is now supported into the 'sql_preprocess' tier: it allows to apply a generic uniform renormalization factor to counters. Its use is particularly suitable for use in conjunction with uniform sampling methods (for example simple random - e.g. sFlow, 'sampling_rate' directive or simple systematic - e.g. sampled NetFlow by Cisco and Juniper). + A new 'adjb' keyword is now supported into the 'sql_preprocess' tier: it allows to add (or subtract in case of negative value) 'adjb' bytes to the bytes counter. This comes useful when fixed lower (link, llc, etc.) layer sizes need to be included into the bytes counter (as explained by the Q7 in the updated FAQS document). + A new '--enable-64bit' configuration switch allows to compile the package with byte/packet/flow counters of 64bit (instead of the usual 32bit ones). ! The sampling algorithm endorsed by the 'sampling_rate' feature has been enhanced to a simple randomic one (it was a simple systematic). ! Some static memory structures are now declared as constants allowing to save memory space (given the multi-process architecture) and offering an overral better efficiency. The patch is courtesy of Andreas Mohr. Thanks. ! Some noisy compiler warnings have been troubleshooted along with some minor code cleanups; the contribution is from Jamie Wilkinson. Thanks. ! Some unaligned pointer issues have been solved. 0.9.1 -- 16-Aug-2005 + Probabilistic, flow size dependent sampling has been introduced into the 'sql_preprocess' tier via the new 'fss' keyword: it is computed against the bytes counter and returns renormalized results. Aggregates which have collected more than the 'fss' threshold in the last time window are sampled. Those under the threshold are sampled with probability p(bytes). For further details read the CONFIG-KEYS and the paper: - N.G. Duffield, C. Lund, M. Thorup, "Charging from sampled network usage" http://www.research.att.com/~duffield/pubs/DLT01-usage.pdf + Probabilistic sampling under hard resource constraints has been introduced into the 'sql_preprocess' tier via the new 'fsrc' keyword: it is computed against the bytes counter and returns renormalized results. The method selects only 'fsrc' flows from the set of the flows collected during the last time window, providing an unbiasied estimate of the real bytes counter. For further details read the CONFIG-KEYS and the paper: - N.G. Duffield, C. Lund, M. Thorup, "Flow Sampling Under Hard Resource Constraints" http://www.research.att.com/~duffield/pubs/DLT03-constrained.pdf + A new 'networks_mask' configuration directive has been introduced: it allows to specify a network mask - in bits - to be applied apply to src_net and dst_net primitives. The mask is applied before evaluating the content of 'networks_file' (if any). + Added a new signal handler for SIGUSR1 in pmacctd: a 'killall -USR1 pmacctd' now returns a few statistics via either console or syslog; the syslog level reserved for such purpose is the NOTICE. ! sfacctd: an issue regarding non-IP packets has been fixed: some of them (mainly ARPs) were incorrectly reported. Now they are properly filtered out. ! A minor memory leak has been fixed; it was affecting running instances of pmacctd, nfacctd and sfacctd with multiple plugins attached. Now resources are properly recollected. 0.9.0 -- 25-Jul-2005 + PMACCT OPENS TO sFlow: support for the sFlow v2/v4/v5 protocol has been introduced and a new daemon 'sfacctd' has been added. The implementation includes support for BGP, MPLS, VLANs, IPv4, IPv6 along with packet tagging, filtering and aggregation capabilities. 'sfacctd' makes use of Flow Samples exported by a sFlow agent while Counter Samples are skipped and the MIB is ignored. All actually supported backends are available for storage: MySQL, PostgreSQL and In-Memory tables. http://www.sflow.org/products/network.php lists the network equipments supporting the sFlow protocol. + A new commandline option '-L' is now supported by 'nfacctd' and 'sfacctd'; it allows to specify an IPv4/IPv6 address where to bind the daemon. It is the equivalent for the 'nfacctd_ip' and 'sfacctd_ip' configuration directives. ! The NetFlow v9 MPLS stack handler has been fixed; it now also sticks the BoS bit (Bottom of the Stack) to the last processed label. This makes the flow compliant to BPF filters compiled by the newly released libpcap 0.9.3. ! Some Tru64 compilation issues related to the ip_flow.[c|h] files have been solved. ! Some configuration tests have been added; u_intXX_t definitions are tested and fixed (whenever possible, ie. uintXX_t types are available). Particularly useful on Solaris and IRIX platforms. ! Configuration hints for MySQL headers have been enhanced. This will ease the compilation of pmacct against MySQL library either from a precompiled binary distribution or from the FreeBSD ports. Many hhanks for the bug report go to John Von Essen. ! NetFlow v8 source/destination AS handlers have been fixed. 0.8.8 -- 27-Jun-2005 + Added IP flows support in pmacctd (release 0.8.5 has seen its introduction in nfacctd) for both IPv4 and IPv6 handlers. To enable flows accounting, the 'aggregate' directive now supports a new 'flows' keyword. The SQL table v4 has to be used in order to support this feature in both SQL plugins. + A new 'sum_mac' aggregation method has been added (this is in addition to the already consolidated ones: 'sum_host', 'sum_net', 'sum_as', 'sum_port'). Sum is intended to be the total traffic (inbound traffic summed to outbound one) produced by a specific MAC address. + Two new configuration directives have been introduced in order to set an upper bound to the growth of the fragment (default: 4Mb) and flow (default: 16Mb) buffers: 'pmacctd_frag_buffer_size', 'pmacctd_flows_buffer_size'. + A new configuration directive 'pmacctd_flow_lifetime' has been added and defines how long a flow could remain inactive (ie. no packets belonging to such flow are received) before considering it expired (default: 60 secs). This is part of the pmacctd IP flows support. + Console/syslog feedbacks about either generic errors or malformed packets have been greatly enhanced. Along with the cause of the message, now any generated message contains either the plugin name/type or the configuration file that is causing it. ! nfacctd: when IPv6 is enabled (on non-BSD systems) the daemon now listens by default on a IPv6 socket getting rid of the v4-in-v6 mapping feature which helps in receiving NetFlow datagrams from both IPv4 and IPv6 agents. A new configure script switch --enable-v4-mapped is aimed to turn manually on/off the feature. ! Fixed an issue with the SIGCHLD handling routine on FreeBSD 4.x systems. It was causing the sudden creation of zombie processes because of the not correct retirement of exited childs. Many thanks for his bug report and strong support go to John Von Essen. ! Fixed an endianess issue regarding Solaris/x86 platforms caused by not proper preprocessor tests. Many thanks to Imre Csatlos for his bug report. ! Fixed the default schema for the PostgreSQL table v4. The 'flows' field was lacking of the 'DEFAULT 0' modifier; it was causing some troubles expecially when such tables were used in conjunction with the 'sql_optimize_clauses' directive. Many thanks for his bug report and strong support go to Anik Rahman. 0.8.7 -- 14-Jun-2005 + pmacctd: MPLS support has been introduced. MPLS (on ethernet and ppp links) and MPLS-over-VLAN (ethernet only) packets are now supported and passed to upper layer routines. Filtering and tagging (Pre-Tagging) packets basing on MPLS labels is also supported. Recent libpcap is required (ie, CVS versions >= 06-06-2005 are highly adviceable because of the support for MPLS label hierarchies like "mpls 100000 and mpls 1024" that will match packets with an outer label of 100000 and an inner label of 1024). + nfacctd: VLAN and MAC addresses support for NetFlow v9 has been introduced. Each of them is mapped to its respective primitive (vlan, src_mac, dst_mac); filtering and tagging (Pre-Tagging) IPv4/IPv6 flows basing on them is also supported. + nfacctd: filtering and tagging (Pre-Tagging) IPv4/IPv6 flows basing on MPLS labels has been introduced (read the above notes regarding libpcap version requirements). + A new packet capturing size option has been added to pmacctd ('snaplen' configuration directive; '-L' commandline). It allows to change the default portion of the packet captured by the daemon. It results useful to cope with not fixed protocol stacks (ie, the MPLS stack). + pmacctd: CHDLC support has been introduced. IPv4, IPv6 and MPLS packets are supported on this link layer protocol. ! Cleanups have been added to the NetFlow packet processing cycle. They are mainly aimed to ensure that no stale data is read from circular buffers when processing NetFlow v8/v9 packets. ! The NetFlow v9 VLAN handling routine was missing a ntohs() call, resulting in an ncorrect VLAN id on little endian architectures. ! ether_aton()/ether_ntoa() routines were generating segmentation faults on x86_64 architectures. They have been replaced by a new handmade couple: etheraddr_string()/string_etheraddr(). Many thanks to Daniel Streicher for the bug report. 0.8.6 -- 23-May-2005 + The support for dynamic SQL tables has been introduced through the use of the following variables in the 'sql_table' directive: %d (the day of the month), %H (hours using an 24 hours clock), %m (month number), %M (minutes), %w (the day of the week as a decimal number), %W (week number in the current year) and %Y (the current year). This enables, for example, substitutions like the following ones: 'acct_v4_%Y%m%d_%H%M' ==> 'acct_v4_20050519_1500' 'acct_v4_%w' ==> 'acct_v4_05' + A new 'sql_table_schema' configuration directive has been added in order to allow the automatic creation of dynamic tables. It expects as value the full pathname to a file containing the schema to be used for table creation. An example of the schema follows: CREATE TABLE acct_v4_%Y%m%d_%H%M ( ... PostgreSQL/MySQL specific schema ... ); + Support for MySQL multi-values INSERT clauses has been added. Inserting many rows in a single shot has proven to be much faster (many times faster in some cases) than using separate single INSERT statements. A new 'sql_multi_values' configuration directive has been added to enable this feature. Its value is intended to be the size (in bytes) of the multi-values buffer. Out of the box, MySQL >= 4.0.x supports values up to 1024000 (1Mb). Because it does not require any changes on server side, people using MySQL are strongly encouraged to give it a try. + A new '--disable-l2' configure option has been added. It is aimed to compile pmacct without support for Layer-2 stuff: MAC addresses and VLANs. This option - along with some more optimizations to memory structures done in this same release - have produced memory savings up to 25% compared to previous versions. ! Recovery code for PostgreSQL plugin has been slightly revised and fixed. 0.8.5 -- 04-May-2005 + Added IP flows counter support in nfacctd, the NetFlow accounting daemon, in addition to the packets and bytes ones. To enable flows accounting, the 'aggregate' directive now supports a new 'flows' keyword. A new SQL table version, v4, has been also introduced to support this feature in both SQL plugins. + 'sql_preprocess' directive have been strongly improved by the addition of new keywords to handle thresholds. This preprocessing feature is aimed to process aggregates (via a comma-separated list of conditionals and checks) before they are pulled to the DB, thus resulting in a powerful selection tier; whether the check is meet, the aggregate goes on its way to the DB; the new thresholds are: maxp (maximum number of packets), maxb (maximum bytes transferred), minf/maxf (minimum/maximum number of flows), minbpp/maxbbp (minimum/maximum bytes per packet average value), minppf/maxppf (minimum/ maximum packets per flow average value). + Added a new 'sql_preprocess_type' directive; the values allowed are 'any' or 'all', with 'any' as default value. It is intended to be the connective whether 'sql_preprocess' contains multiple checks. 'any' requires that an aggregate has to match just one of the checks in order to be valid; 'all' requires a match against all of the checks instead. + Added the ability to instruct a BPF filter against the ToS field of a NetFlow packet. ! Minor optimizations on the 'sql_preprocess' handler chain. 0.8.4 -- 14-Apr-2005 + Added support for NetFlow v7/v8. The Version 7 (v7) format is exclusively supported by Cisco Catalyst series switches equipped with a NetFlow feature card (NFFC). v7 is not compatible with Cisco routers. The Version 8 (v8) format adds (with respect to older v5/v7 versions) router-based aggregation schemes. + Added the chance to tag packets basing on NetFlow v8 aggregation type field. As the keyword suggests, it will work successfully just when processing NetFlow v8 packets. Useful to split - backend side - data per aggregation type. + pmacct client now is able to ask for the '0' (that is, untagged packets) tag value. Moreover, all 'sum' aggregations (sum_host, sum_net, sum_as, sum_port) can now be associated with both Pre/Post-Tagging. ! Fixed a serious memory leak located in the routines for handling NetFlow v9 templates. While the bug was needing certain conditions to manifest, anyone using NetFlow v9 is strongly encouraged to upgrade to this version. All previous versions were affected. ! Some gcc4 compliance issues have been solved. The source code is known to work fine on amd64 architectures. Thanks very much to Marcelo Goes for his patch. ! Engine Type/Engine ID fields were not correctly evaluated when using NetFlow v5 and Pre-Tagging. The issue has been fixed. ! Long comments in the Ports Definition File were causing some incorrect error messages. However it seems the file were processed correctly. Thanks to Bruno Mattarollo for signalling the issue. ! Minor fix to plugins hooking code. The reception of sparse SIGCHLD signals were causing the poll() to return. The impact was null. The issue has been fixed by ignoring such signals. 0.8.3 -- 29-Mar-2005 + Pre-Tagging capabilities have been further enhanced: captured traffic can be now marked basing on the NetFlow nexthop/BGP nexthop fields. While the old NetFlow versions (v1, v5) carry an unique 'nexthop' field, NetFlow v9 supports them into two distinguished fields. + Packet/flows tagging is now explicit, gaining more flexibility: a new 'tag' keyword has been added to the 'aggregate' directive. It causes the traffic to be actually marked; the 'pre_tag_map' and 'post_tag' directives now just evaluate the tag to be assigned. Read further details about this topic in the UPGRADE document. + The 'pre_tag_filter' directive now accepts 0 (zero) as valid value: we have to remember that zero is not a valid tag; hence, its support allows to split or filter untagged traffic from tagged one. + Documentation has been expanded: a new FAQS entry now describes few and easy tweaks needed to replace the bytes counter type from u_int32_t to u_int64_t throughout the code (provided that the OS supports this type); it's useful in conjunction with the In-Memory plugin while exposed to very sustained traffic loads. A new FAQS entry describes the first efforts aimed to integrate pmacctd with popular flow-tools software by the way of the flow-export tool. A new UPGRADE document has been also created. ! pmacct client was handling counters returned by the '-N' switch as signed integers, which is not correct. The issue has been fixed. Many thanks to Tobias Bengtsson for signalling it. ! Two new routines file_lock()/file_unlock() have replaced the flock() calls because they were preventing the pmacct code to compile on Solaris. Basing over hints collected at configure time, the routines enable either the flock() or fcntl() code. Many thanks to Jan Baumann for signalling and solving the issue. 0.8.2 -- 08-Mar-2005 + Pre-Tagging capabilities have been enhanced: now, a Pre Tag Map allows to mark either packets or flows basing on the outcome of a BPF filter. Because of this new feature, Pre-tagging has been introduced in 'pmacctd' too. Pre-tagging was already allowing 'nfacctd' to translate some NetFlow packet fields (exporting agent IP address, Input/Output interface, Engine type and Engine ID) into an ID (also referred as 'tag'), a small number in the range 1-65535. + A new 'pmacctd_force_frag_handling' configuration directive has been added; it aims to support 'pmacctd' Pre-Tagging operations: whether the BPF filter requires tag assignation based on transport layer primitives (e.g. src port or dst port), this directive ensures the right tag is stamped to fragmented traffic too. + Pre Tag filtering (which can be enabled via 'pre_tag_filter' configuration directive) allows to filter aggregates basing on the previously evaluated ID: whether it matches with at least one of the filter values, the aggregate is delivered to the plugin. It has been enhanced by allowing to assign more tags to a specific plugin. + pmacctd: a new feature to read libpcap savefiles has been added; it can be enabled either via the 'pcap_savefile' configuration directive or the '-I' commandline switch. Files need to be already closed and correctly finalized in order to be read successfully. Many thanks to Rafael Portillo for proposing the idea. + pmacct client tool supports a new 'tag' keyword as value for the '-c' switch: it allows to query the daemon requesting a match against aggregate tags. + pmacct client: the behaviour of the '-N' switch (which makes the client to return a counter onto the screen suitable for data injection in tools like MRTG, Cacti, RRDtool, etc.), has been enhanced: it was already allowing to ask data from the daemon but basing only on exact matches. This concept has now extended, adding both wildcarding of specific fields and partial matches. Furthermore, when multiple requests are encapsulated into a single query, their results are by default splitted (that is, each request has its result); a newly introduced '-S' switch now allows to sum multiple results into a single counter. ! Bugfix: proper checks for the existence of a 'pre_tag_map' file were bypassed under certain conditions; however, this erroneous behaviour was not causing any serious issue. The correct behaviour is to quit and report the problem to the user. ! The sampling rate algorithm has been fixed from a minor issue: it was returning not expected results when 'sampling_rate: 1'. It now works as expected. Thanks to David C. Maple for his extensive support in gaining a better understanding of the problem. 0.8.1p1 -- 22-Feb-2005 ! 'sum_host' and 'sum_net' compound primitives have been fixed in order to work with IPv6 addresses. ! In-Memory Plugin: client queries spotted with both '-r' (reset counters) and '-N' (exact match, print counters only) switches enabled were causing the daemon to crash whether no entries were found. The problem has been fixed. Many thanks to Zach Chambers for signalling the issue. ! In-Memory Plugin: client queries spotted with either '-M' or '-N' switches enabled were failing to match actual data when either 'sum_host', 'sum_net' or 'sum_as' primitives were in use. The issue has been fixed. ! The modulo function applied to NetFlow v9 Template Cache has been enhanced in order to deal correctly with export agents having an IPv6 address. ! Networks/AS definition file: a new check has been added in order to verify whether network prefix/network mask pairs are compatible: if they are not, the mask is applied to the prefix. ! Documentation has been expanded and revised. 0.8.1 -- 25-Jan-2005 + Accounting and aggregation over DSCP, IPv4 ToS field and IPv6 traffic class field have been introduced ('aggregate' directive, 'tos' value): these fields are actually widely used to implement Layer-3 QoS policies by defining new classes of service (most noticeably 'Less than Best Effort' and 'Premium IP'). MySQL and PostgreSQL tables v3 (third version) have been introduced (they contain an additional 4-bytes 'tos' field) to support the new Layer-3 QoS accounting. + nfacctd core process has been slightly optimized: each flow is encapsulated (thus, copied field-by-field) into a BPF-suitable structure only if one or more plugins actually require BPF filtering ('aggregate_filter' directive). Otherwise, if either filtering is not required or all requested filters fail to compile, the copy is skipped. + 'pmacct', pmacct client tool: '-e' commandline option (which meaning is: full memory table erase) now might be supplied in conjunction with other options (thus avoiding the short time delays involved by two consecutive queries, ask-then-erase, which may also lead to small losses). The new implemented mechanism works as follow: queries over actual data (if any) are served before; the table is locked, new aggregates are queued until the erasure finishes (it may take seconds if the table is large enough); the table is unlocked; the queue of aggregates is processed and all normal operations are resumed. Many thanks to Piotr Gackiewicz for the valuable exchange of ideas. ! Bug fixed in nfacctd: source and destination AS numbers were incorrectly read from NetFlow packets. Thanks to Piotr Gackiewicz for his support. ! Bug fixed in pmacct client: while retrieving the whole table content was displaying espected data, asking just for 'dst_as' field was resulting in no results instead. Thanks, once more, to Piotr Gackiewicz. 0.8.0 -- 12-Jan-2005 + PMACCT OPENS TO IPv6: IPv6 support has been introduced in both 'pmacctd' and 'nfacctd' daemons. Because it requires larger memory structures to store its addresses, IPv6 support has been disabled by default. It could be enabled at configure time via '--enable-ipv6' switch. All filtering, tagging and mapping functions already support IPv6 addresses. Some notes about IPv6 and SQL table schema have been dropped into README.IPv6 file, sql section of the tarball. + PMACCT OPENS TO NetFlow v9: support for the template-based Cisco NetFlow v9 export protocol has been added. NetFlow v1/v5 were already supported. 'nfacctd' may now be bound to an IPv6 interface and is able to read both IPv4 and IPv6 data flowsets. A single 'nfacctd' instance may read flows of different versions and coming from multiple exporting agents. Source and destination MAC addresses and VLAN tags are supported in addition to the primitives already supported in v1/v5 (source/destination IP addresses, AS, ports and IP protocol). Templates are cached and refreshed as soon as they are resent by the exporting agent. + Pre Tag map ('pre_tag_map' configuration key), which allows to assign a small integer (ID) to an incoming flow basing on NetFlow auxiliar data, now may apply tags basing also over Engine Type (it provides uniqueness with respect to the routing engine on the exporting device) and Engine ID (it provides uniqueness with respect to the particular line card or VIP on the exporting device) fields. Incoming and Outcoming interfaces were already supported. See 'pretag.map.example' into tarball examples section and CONFIG-KEYS document for further details. + Raw protocol (DLT_RAW) routine has been added; it usually allows to read data from tunnels and sitX devices (used for IPv6-in-IPv4 encapsulation). + Some tests for architecture endianess, CPU type and MMU unaligned memory access capability have been added. A small and rough (yes, they work the hard way) set of unaligned copy functions have been added. They are aimed to be introduced through the code, however first tests over MIPS R10000 and Alpha EV67 (21264A) have shown positive results. ! PPPoE and VLAN layer handling routines have been slightly revised for some additional checks. ! Given the fairly good portability reported from the mmap() code introduced through the whole 0.7.x development stage, the use of shared memory segments is now enabled by default. The configure switch '--enable-mmap' has been replaced by '--disable-mmap'. ! 'pmacct' client tool: because of the IPv6 addresses introduction, separator character for multiple queries (commandline) have been changed to from ':' to ';'. ! 'nfacctd': '-F' commandline switch was listed into available options list, but getopt() stanza was missing, thus returning an invalid option message. Thanks to Chris Koutras for his support in fixing the issue. ! Some variable assignations were causing lvalue errors with gcc 4.0. Thanks to Andreas Jochens for his support in signalling and solving the problem. 0.7.9 -- 21-Dec-2004 + A new data pre-processor has been introduced in both SQL plugins: it allows to filter out data (via conditionals, checks and actions) during a cache-to-DB purging event, before building SQL queries; this way, for example, aggregates which have accounted just a few packets or bytes may be either discarded or saved through the recovery mechanism (if enabled). The small set of preprocessing directives is reported into CONFIG-KEYS document. + Some new environment variables are now available when firing a trigger from SQL plugins: $EFFECTIVE_ELEM_NUMBER reports the effective number of aggregates (that is, excluding those filtered out at preprocessing time) encapsulated in SQL queries; $TOTAL_ELEM_NUMBER reports the total number of aggregates instead. $INSERT_QUERIES_NUMBER and $UPDATE_QUERIES_NUMBER returns respectively the number of aggregates being successfully encapsulated into INSERT and UPDATE queries. $ELAPSED_TIME reports the time took to complete the last purging event. For further details and the list of supported environment variables take a look to TRIGGER_VARS document. + Some additions to both logfile players: a new '-n' switch allows to play N elements; this way, arbitrary portions of the file may be played using '-n' in conjunction with the (already existing) '-o' switch which allows to read the logfile starting at a specified offset. New switches '-H', '-D', '-T', '-U', '-P' have been introduced to override SQL parameters like hostname, DB, table, user and password. The '-t -d' combination (test only, debug) now allows to print over the screen the content of the logfile. + Logfiles size is now limited to a maximum of 2Gb, thus avoiding issues connected to the 32bit declaration of off_t. While many OS implment a solution to the problem, seems there are few chances to solve it in a portable way. When the maximum size is hit the old logfile is rotated appending to its filename a trailing small integer ( in a way similar to logrotate) and a fresh one is started. ! Logfile players: '-s' switch, which was allowing to play one element a time, has been superseded. Its current equivalent is: '-n 1'. ! The file opening algorithm has been slightly changed in SQL plugins: flock() follows shortly the fopen() and all subsequent operations and evaluations are thus strictly serialized. freopen() is avoided. 0.7.8 -- 02-Dec-2004 + Recovery logfile structure has been enhanced. Following the logfile header has been created a new template structure. Templates will avoid the issue of being not able to read old logfiles because of changes to internal data structures. Templates are made of an header and a number of entries, each describing a single field of the following data. Both players, pmmyplay and pmpgplay, are able to parse logfiles basing over the template description. Backward logfile compatibility is broken. + Execcutable triggering mechanism (from SQL plugins) has been enhanced: some status informations (eg. stats of the last purging event) are now passed to the trigged executable in the form of environment variables. The list of supported variables has been summarized into TRIGGER_VARS document. The mechanism allows to spawn executables for post-processsing operations at arbitrary timeframes. + Support for 'temporary' devices (like PPP and maybe PCMCIA cards too) has been introduced. A new configuration directive 'interface_wait' (or '-w' commandline) instructs pmacctd to wait for the listening device to become available. It works both when in startup phase and when already into main loop. A big thanks to Andre Berger for his support. ! ppp_handler() routine, which is in charge to handle PPP packets, have been totally rewritten. Thanks, again, to Andre Berger for his support. ! All link layer handling routines have been revised; some extra checks have been added to overcome issues caused from malicious handcrafted packets. ! Some time handling and timeout issues have been revised into PostgreSQL plugin code. They were affecting only the triggering mechanism. ! Fixed an execv() bug into MY_Exec() and PG_Exec(). It was causing the not correct execution of triggers. Now, a zeroed argv parameter is passed to the function. The problem has been verified on FreeBSD. 0.7.7 -- 16-Nov-2004 + Added two new aggregation primitives: 'src_as' and 'dst_as'. They allow accounting based over Autonomous System number; 'pmacctd' requires AS numbers to be supplied into a 'networks_file' configuration directive (which allows to specify the path to a networks definition file); 'nfacctd' may either look up AS numbers from the networks definition file or read them from each NetFlow flow (this is default). 'nfacctd_as_new' key could be used to switch 'nfacctd' behaviour. + Added some new aggregation modes: 'sum_net', 'sum_as', 'sum_port' ('sum' which is actually an alias for 'sum_host' has been already introduced early). Sum is intended to be the total traffic (that is, inbound plus outbound traffic amounts) for each entry. + Added another aggregation primitive: 'none'. It does not make use of any primitive: it allows to see total bytes and packets transferred through an interface. + The definition of a 'networks_file' enables network lookup: hosts inside defined networks are ok; hosts outside them are 'zeroed'. This behaviour may now also be applied to 'src_host', 'dst_host' and 'sum_host'. Under certain conditions (eg. when using only host/net/as primitives and defined networks comprise all transiting hosts) it may be seen an alternative way to filter data. ! 'frontend'/'backend' PostgreSQL plugin operations have been obsoleted. 'unified'/'typed' operations have been introduced instead. See 'sql_data' description, CONFIG-KEYS document, for further informations. ! Optimizations have been applied to: core process, the newly introduced cache code (see 0.7.6) and in-memory table plugin. ! Fixed some string handling routines: trim_all_spaces(), mark_columns() ! Solved a potential race condition which was affecting write_pid_file() 0.7.6 -- 27-Oct-2004 + Many changes has been introduced on 'pmacct' client side. '-m' switch (which output was suitable as MRTG input) has been obsoleted (though it will continue to work for next few releases). A new '-N' switch has been added: it returns counter value, suitable for integration with either RRDtool or MRTG. + Support for batch queries have also been added into pmacct client. It allows to join up to 4096 requests into a single query. Requests could either be concatenated commandline or read from a file (more details are in FAQS and EXAMPLES). Batch queries allow to handle efficiently high number of requests in a single shot (for example to timely feed data to a large amount of graphs). + Still pmacct client: '-r' switch, which already allows to reset counters for matched entries, now it also applies to group of matches (also referred as partial matches). + New scripts have been added into the examples tree which show how to integrate memory and SQL plugins with RRDtool, MRTG and GNUplot. + Memory plugin (IMT) has been further enhanced; each query from pmacct client is now evaluated and if involves just a short ride through the memory structure, it is served by the plugin itself without spawning a new child process. Batch queries support and reordering of fragmented queries have also been added. + New cache has been introduced in both SQL plugins; its layout is still an hash structure but it now features also chains, allocation, reuse and retirement of chained nodes. It also sports a LRU list of nodes which eases node handling. The new solution avoids the creation of a collision queue, ensuring uniqueness of data placed onto the queries queue. While this already greatly benefits a directive like 'sql_dont_try_update', it also opens new chances for post-processing operations of queries queue. 0.7.5 -- 14-Oct-2004 + Introduced support for the definition of a 'known ports' list, when either 'src_port' or 'dst_port' primitives are in use. Known ports will get written into the backend; unknown ports will be simply zeroed. It could be enabled via 'ports_file' configuration key or '-o' commandline switch. + Introduced support for weekly and monthly counters breakdown; hourly, minutely and daily were already supported. New breakdowns could be enabled via 'w' and 'M' words in 'sql_history' and related configuration keys. + Added a '-i' commandline switch to both 'pmmyplay' and 'pmpgplay' to avoid UPDATE SQL queries and skip directly to INSERT ones. Many thanks to Jamie Wilkinson. ! 'pmmyplay' and 'pmpgplay' code has been optimized and updated; some pieces of locking and transactional code were included into the inner loop. A big thanks goes to Wim Kerkhoff and Jamie Wilkinson. ! Networks aggregation code has been revised and optimized; a direct-mapped cache has been introduced to store (and search) last search results from the networks table. A binary search algorithm, though optimized, over the table has still been preferred over alternative approaches (hash, tries). 0.7.4 -- 30-Sep-2004 + Enhanced packet tagging support; it's now broken in Pre-Tagging and Post-Tagging; Pre-Tagging allows 'nfacctd' to assign an ID to a flow evaluating an arbitrary combination of supported NetFlow packet fields (actually: IP address, Input Interface, Output Interface); the Pre-Tagging map is global; Pre-Tag is applied as soon as each flow is processed; Post-Tagging allows both 'nfacctd' and 'pmacctd' to assign an ID to packets using a supplied value; Post-Tagging could be either global or local to a single plugin (and more plugins may tag differently); Post-Tag is applied as a last action before the packet is sent to the plugin. 'nfacctd_id_map' and 'pmacctd_id' configuration keys are now obsolete; 'pre_tag_map' and 'post_tag' are introduced to replace them. + Added support for Pre-Tag filtering; it allows to filter packets basing on their Pre-Tag value. The filter is evaluated after Pre-Tagging but before Post-Tagging; it adds to BPF filtering support ('aggregate_filter' configuration key); 'pre_tag_filter' configuration key is introduced. + Added support for Packet Sampling; the current implementation bases on a simple systematic algorithm; the new 'sampling_rate' configuration key expects a positive integer value >= 1 which is the ratio of the packets to be sampled (translates in: pick only 1 out of N packets). The key is either global or local (meaning that each plugin could apply different sampling rates). ! Fixed a bug which was causing crashes in both 'pmacctd' and 'nfacctd' when '-r' parameter was specified commandline. Thanks to Ali Nikham for his support. 0.7.3 -- 31-Aug-2004 + Added support for both Netflow 'input interface' and 'output interface' fields. These two fields are contained in each flow record inside a NetFlow packet. It works through ID mapping (read below). + The ID map file syntax has been enhanced to allow greater flexibility in ID assignation to packets; example: 'id=1 ip=192.168.1.1 in=3 out=5'; the above line will cause the 'ID' 1 to be assigned to flows exported by a NetFlow agent (for example a router) which IP address is '192.168.1.1' and transiting from interface '3' to interface '5'. + In-memory table operations have been enhanced when using shared memory; a new reset flag has been added to avoid race conditions. ! Configuration lines are no more limited to some fixed maximum length but are allocated dynamically; this to overcome the need for long configuration lines to declare arbitrary filters and plugin's list. Thanks to Jerry Ji for his support. ! Configuration handlers, which are responsible to parse and validate values for each configuration key, have been rewritten on the way for a better portability. ! Signal handler routines have been changed to better accomodate SysV semantics. ! Fixed shared memory mmap() operations on IRIX and SunOS; a further test checks for either 'MAP_ANON' or 'MAP_ANONYMOUS' definitions; in case of negative outcome, mmap() will use '/dev/zero'. ! Packet handlers have been revised and optimized. ! Some optimizations have been added when using shared memory; write() function has been usually called to signal the arrival of each new packet, through the core process/plugin control channel; now it does so if and only if the plugin, on the other side, is actually blocking over a poll(); because of sequence numbers guarantee, data is directly written into shared memory segment. 0.7.2p1 -- 08-Aug-2004 ! Multiple fixes in plugin's configuration post checks; negative outcome of some checks was leading to clear misbehaviours. Versions affected are >= 0.7.0 . A big thanks goes to Alexandra Walford for her support. 0.7.2 -- 02-Aug-2004 + VLAN accounting has been added. The new 'vlan' keyword is supported as argument of both '-c' commandline switch and 'aggregate' configuration key. + Distributed accounting support has been added. It could be enabled into 'pmacctd' via 'pmacctd_id' configuration key and into 'nfacctd' via the 'nfacctd_id_file' configuration key. While 'pmacctd_id' key expects as value a small integer, 'nfacctd_id_file' expects a path to a file which contains the mapping: 'IP address of the router (exporting Newflow) -> small integer'. This scheme ease tasks such as keeping track of who has generated what data and either cluster or keep disjoint data coming from different sources when using a SQL database as backend. + Introduced SQL table version 2. The SQL schema is the same as existing tables with the following additions: support for distributed accounting; support for VLAN accounting. + Added MAC addresses query capabilties to pmacct client. + Added '-r' commandline switch to pmacct client. It can only be used in conjunction with '-m' or '-M' switches. It allows to reset packet and bytes counters of the retrieved record. ! Exit codes have been fixed in both 'pmacctd' and 'nfacctd'. Thanks to Jerry Ji for his signallation. ! Fixed a problem when retrieving data from memory table: sometimes null data (without any error message) was returned to the client; the problem has been successfully reproduced only on FreeBSD 5.1: after an accept() call, the socket being returned inherits same flags of the listening socket, this case non-blocking flag. Thanks to Nicolas Deffayet for his support. ! Revised PostgreSQL creation script. 0.7.1 -- 14-Jul-2004 + Added shared memory implementation; core process, now, could push data into a shared memory segment and then signal arrival of new data to the plugin. Shared memory support could be enabled via '--enable-mmap' switch at configuration time. + Strongly enhanced gathering capabilities of pmacct client; pmacct client is used to fetch data from memory plugin; it is, now, able to ask exact or partial matches via '-M' switch and return a readable listing output. MRTG export capabilities, full table fetch and table status query are still supported. + Introduced SQL table versioning. It could be enabled via 'sql_table_version' configuration switch. It will enable to build new SQL tables (for example adding new aggregation methods) while allowing who is not interested in new setups to work with old tables. + Added checks for packet capture type; informations acquired are later used for better handling pcap interface. ! Fixed some issues concerning pmacctd VLAN and PPPOE code. ! Fixed a mmap() issue on Tru64 systems. ! Fixed some minor poll() misbehaviours in MySQL, PgSQL and print plugins; they were not correctly handled. 0.7.0p1 -- 13-Jul-2004 ! Fixes in cache code; affects MySQL, PgSQL and print plugins. 0.7.0 -- 01-Jul-2004 + PMACCT OPENS TO NETFLOW: a new network daemon, nfacctd, is introduced: nfacctd listens for Netflow V1/V5 packets; is able to apply BPF filters and to aggregate packets; it's then able to either save data in a memory table, MySQL or PostgreSQL database or simply output packets on the screen. It can read timestamps from Netflow packets in msecs, seconds or ignore them generating new timestamps; a simple allow table mechanism allows to silently discard Netflow packets not generated by a list of trusted hosts. + Strongly enhanced IP fragmentation handling in pmacctd. + Added new checks into the building systems; new hints when it searches for libraries and headers; initial tests for C compilers capabilities have been added. + Works to let pmacct run on IRIX platforms continue; some issues with MipsPRO compiler have been solved; added proper compilation flags/hints. SIGCHLD is now properly handled and child processes are correctly retired. (a thank for his support goes to Joerg Behrens) + First, timidous, introduction of mmap() calls in memory plugin; they need to be enabled with '--enable-mmap' flag at configure time. ! Fixed a potential deadlock issue in PostgreSQL plugin; changed locking mechanism. (a big thank to Wim Kerkhoff) ! Fixed an issue concerning networks aggregation on Tru64 systems. 0.6.4p1 -- 01-Jun-2004 ! Fixed an issue with cache aliasing in MySQL and PostgreSQL plugins. Other plugins are not affected; this potential issue affects only version 0.6.4, not previous ones. Anyone using these plugins with 0.6.4 is strongly encouraged to upgrade to 0.6.4p1. 0.6.4 -- 27-May-2004 + Added chance to launch executables from both SQL plugins at arbitrary time intervals to ease data post-processing tasks. Two new keys are available: 'sql_trigger_exec' and 'sql_trigger_time'. If any interval is supplied the specified executable is triggered every time data is purged from the cache. + Added a new 'print' plugin. Enabling it, data is pulled at regular intervals to stdout in a way similar to cflowd's 'flow-print'. tool. New config keys are 'print_refresh_time', 'print_cache_entries' and 'print_markers'. This last key enables the print of start/end markers each time the cache is purged. + Added 'sql_dont_try_update' switch to avoid UPDATE queries to the DB and skip directly to INSERT ones. Performance gains has been noticed when UPDATEs are not necessary (eg. when using timeslots to break up counters and sql_history = sql_refresh_time). Thanks to Jamie Wilkinson. + Optimized use of transactions in PostgreSQL plugin; in the new scheme is built a single big transaction for each cache purge process. This leads to good performance gains; recovery mechanisms have been modified to overcome whole transaction trashing. Many thanks to James Gregory and Jamie Wilkinson. ! Enhanced debug messages output when specific error conditions are returned by the DB. ! Fixed a potential counters overflow issue in both MySQL and PgSQL plugins cache. ! Fixed preprocessor definitions issue: LOCK_UN, LOCK_EX are undeclared on IRIX and Solaris. Thanks to Wilhelm Greiner for the fix. 0.6.3 -- 27-Apr-2004 + Added support for full libpcap-style filtering capabilities inside pmacctd. This allows to bind arbitrary filters to each plugin (in addition to already existing chance to apply them to the listening interface via 'pcap_filter' configuraiton key). The config key to specify these new filters is 'aggregate_filter'. + Strongly improved networks definition file handling; now the file is parsed and organized as a hierarchical tree in memory. This allows to recognize and support networks-in-networks. + Initial optimizations has been done over the code produced in last few months. + Preprocessor definitions has been added to some part of the code, to allow pmacctd compile over IRIX. It has been reported to work over a IRIX64 6.5.23 box. Thanks to Wilhelm Greiner for his efforts. + Added flock() protected access to recovery logfiles. ! Fixed an ugly SEGV issue detected in both 0.6.2's logfile player tools. 0.6.2 -- 14-Apr-2004 + Added support for networks aggregation. Two new primitives has been added 'src_net' and 'dst_net' to be used in conjunction with a network's definitions file (path is supplied via 'networks_file' configuration key). An example of this file is in the examples/ directory. When this aggregation is enabled, IP addresses are compared against the networks table; then the matching network will get written to the backend; if any match occurs a '0.0.0.0' is written. A really big thank goes to Martin Anderberg for his strong support during last weeks. + pipe() has been thrown away; socketpair() has been introduced to set up a communication channel between pmacctd core process and plugins. + Added 'plugin_pipe_size' configuration key to adjust queue depth (size) beween core process and plugins. A default value is set by operating system; it could not suffice when handling heavy traffic loads. Added also a specific error string when pipe gets filled. + Added 'plugin_buffer_size' configuration key to enable chances to bufferize data to be sent to plugins. When under great loads this helps in preventing high CPU usage and excessive pressure over kernel. + SQL plugins aliasing behaviour has been changed; when no free space for new data is found and old data has to be pulled out, it's now actually written to the DB but it's inserted in a new 'collision queue'. This new queue is purged together with the 'queries queue'. See INTERNALS for further details. + SQL plugins cache behaviour has been changed by a direct-mapped one to a 3-ways associative to get better scores when searching free space for new data. See INTERNALS for further details. + Added 'sql_cache_entries' configuration key to adjust bucket's number of SQL plugin cache. As every hashed structure, a prime number of buckets is advisable to get better dispersion of data through the table. ! Fixed a malloc() SEGV issue in in-memory table plugin first noticed with gcc 3.3.3 (Debian 20040320) and glibc 2.3.2. ! Fixed a SEGV issue carried with last release. Improved handling of communication channels between core process and plugins. ! Uniformed plugin's handling of signals; now sending a SIGINT to all pmacctd processes causes it to flush caches and exit nicely. ! Updated documentation; still no man page. 0.6.1 -- 24-Mar-2004 + A new concept has been introduced: plugin names. A name could be assigned to each running plugin allowing to run more instances of the same plugin type; each one is configurable with global or 'named' keys. Take a look to examples for further info. + Added support for PPPOE links. The code has been fully contributed by Vasiliy Ponomarev. A big thank goes to him. + Added a 'sql_startup_delay' configuration key to allow more plugin instances that need to write to the DB, to flush their data at same intervals but in different times to avoid locking stalls or DB overkills. + Improved handling of syslog connections. SIGHUP signal, used to reopen a connection with syslog (eg. for log rotation purposes), now is supported in all plugins. + A simple LRU (Last Recently Used) cache has been added to the in-memory table plugin. The cache gives great benefits (exploiting some kind of locality in communication flows) when the table gets large (and chain in buckets become long and expensive to traverse). + Down-up of listening interface are now handled properly. Such an event traps a reopening of connection with libpcap. [EXPERIMENTAL] + Some work has been done (mostly via directives to preprocessor) in order to get pmacct compiled under Solaris. [HIGLY EXPERIMENTAL, translates: don't assume it works but, please, try it out and some kind of feedback would be appreciated] ! Plugins have been better structured; plugin hooking has been simplified and re-documented; configuration parser has been strongly improved. ! Fixed a bug in 'configure' script; when supplying custom paths to MySQL libraries an erroneous library filename was searched for. (thanks to Wim Kerkhoff) 0.6.0p3 -- 09-Feb-2004 ! Fixed an issue concerning promiscuous mode; it was erroneously defaulting to 'false' under certain conditions. (Thanks to Royston Boot for signalling the problem) 0.6.0p2 -- 05-Feb-2004 ! Fixed pmacct daemon in-memory table plugin unstability, noticed under sustained loads. (A thank for signalling the problem goes to Martin Pot) ! Minor code rewritings for better optimizazion done in both in-memory table plugin and pmacct client. 0.6.0p1 -- 28-Jan-2004 ! Fixed a bug in in-memory table plugin that was causing incorrect memorization of statistics. (Many thanks for promptly signalling it go to Martin Pot) ! Fixed a bug in pmacct client, used to gather stats from in-memory table. Under high loads and certain conditions the client was returning SEGV due to a realloc() issue. (Thanks to Martin Pot) 0.6.0 -- 27-Jan-2004 + PMACCT OPENS TO POSTGRESQL: fully featured PostgreSQL plugin has been added; it's transaction based and already supports "recovery mode" both via logfile and backup DB actions. pmpgplay is the new tool that allows to play logfiles written in recovery mode by the plugin into a PostgreSQL DB. See CONFIG-KEYS and EXAMPLES for further informations. (Again, many thanks to Wim Kerkoff) + Added new "recovery mode" action to MySQL plugin: write data to a backup DB if primary DB fails. DB table/user/ password need to be the same as in the primary DB. The action could be enabled via "sql_backup_host" config key. + Added a "sql_data" configuration optinion; a "frontend" value means to write human readable (strings) data; a "backend" value means to write integers in network byte order. Currently, this option is supported only into the new PostgreSQL plugin. See CONFIG-KEYS and README.pgsql for further informations. + Added support for simple password authentication in client/server query mechanism for in-memory table statistics. It's available via "imt_passwd" config key. + Added a "-t" commandline switch to pmmyplay; it runs the tool in a test only mode; useful to check header infos or logfile integrity. ! Fixed an ugly bug that made impossible MAC accounting over certain links. Was affected only version 0.5.4. ! Many code and structure cleanups. 0.5.4 -- 18-Dec-2003 + Added a commandline and configuration switch to use or not promiscuous mode for traffic capturing; useful to avoid waste of resources if running over a router. + Introduced a "recovery mode" concept for MySQL plugin: if DB fails an action is taken; currently is possible to write data to a logfile. More failover solutions to come in next releases. Thanks also to Wim Kerkhoff. + Added a new "pmmyplay" tool. Allows to play logfiles previously written by a MySQL plugin in recovery mode. Check EXAMPLES for hints; see INTERNALS for further details about recovery mode and pmmyplay. + Added syslog logging and debugging. Thanks for long brainstormings to Wim Kerkhoff. + Added chance to write PID of pmacctd core process to a specified file; it could help in automating tasks that need to send signals to pmacctd (eg. to rotate logfiles and reopen syslog connection). Take a look to SIGNALS file for further informations. + support for 802.11 Wireless links. [EXPERIMENTAL] + support for linux cooked device links (DLT_LINUX_SLL). pcap library >= 0.6.x is needed. A big thank goes to KP Kirchdoerfer. ! Simplified client/server query mechanism; avoided all string comparison stuff. ! Large parts of in-memory table plugin code has been revised to achieve better efficiency and optimization of available resources. 0.5.3 -- 20-Nov-2003 ! pmacctd core has been optimized and a new loop-callback scheme driven by pcap library has been introduced; I/O multiplexing is avoided. ! In MySQL plugin, refresh of entries in the DB has been switched from a signal-driven approach to a lazy timeslot based one. If using historical recording, taking care to the choosen values, this greatly alleviates cache aliasing. ! In MySQL plugin, modulo function (for insertion of data in the direct mapped cache) has been changed: crc32 algorithm has been adopted. Experimental tests shown the reduction of cache aliasing to about 0.45%. ! The whole MySQL plugin has been inspected for performance bottlenecks resulted by the addition of new features in last releases. ! Fixed a bug in link layer handlers. 0.5.2 -- 03-Nov-2003 + "sql_history" configuration key syntax has been changed to support history recording at fixed times with mins, hrs and days granularity. A little of date arithmetics has been introduced (merely multiplicative factors, eg. to ease 95th percentile operations). + Added "sql_history_roundoff" configuration key to round off time of first timeslot. This little care gives cleaner time results and inductively affects all subsequent slots. + Achieved more precise calculations via timestamps added to the cache structure to avoid data counted during the current timeslot and not already fed in the DB to be accounted in next slot. ! Monthly historical aggregation is no more available. ! Fixed portability issues posed by vsnprintf() in MySQL plugin. Now the plugin compiles smoothly under Tru64 Unix. 0.5.1 -- 01-Oct-2003 + due to the proliferation of command-line options, the support for a configuration file has been added. All commandline switches until version 0.5.0 will be supported in the future. New configurable options (eg. log to a remote SQL server) will be only supported via configuration file. See CONFIG-KEYS file for available configuration keys. + added support for historical recording of counters in the MySQL database. Available granularities of aggregation are hourly, daily or monthly (eg. counters are separated hour by hour, daily of monthly for each record). Timestamps of last INSERT and UPDATE have been added over each record. (thanks to Wim Kerkhoff for his strong collaboration) + support for IP header options. + support for PPP links. [EXPERIMENTAL] ! Fixed a MySQL plugin direct-mapped cache issue: the cache now traps INSERT queries when an UPDATE fails due to any asyncronous table manipulation event (eg. external scripts, table truncation, etc.). ! MySQL plugin has been strongly revised and optimized; added options to save data to a remote sql server and to customize username, password and table; added MySQL locking stuff. (another big thank to Wim Kerkhoff). ! various code cleanups. 0.5.0 -- 22-Jul-2003 + static aggregation directives (src_host, dst_host, ..) are now superseded by primitives that can be stacked together to form complex aggregation methods. The commandline syntax of the client program has been consequently changed to support these new features. + two new primitives have been added: source MAC address and destination MAC address. + support for 802.1Q (VLANs) tagged packets (thanks to Rich Gade). + support for FDDI links. [EXPERIMENTAL] ! the core pmacctd loop (that gathers packets off the wire and feeds data to plugins) has been revised and strongly optimized. ! the main loop of MySQL plugin has been optimized with the introduction of adaptive selection queries during the update process. ! fixed a memory allocation issue (that caused a SIGSEGV, under certain circustances) in pmacct client: now the upper bound of dss is checked for large data retrieval. 0.4.2 -- 20-Jun-2003 + limited support for transport protocols (currently only tcp and udp): aggregation of statistics for source or destination port. + optimized query mechanism for in-memory table; solved few generalization issues that will enable (in future versions) to support complex queries. + added "-t" pmacctd commandline switch to specify a custom database table. ! fixed realloc() issue in pmacct client (thanks to Arjen Nienhuis). ! fixed an issue regarding mysql headers in the configure script. 0.4.1 -- 08-May-2003 ! missing break in a case statement that led pmacctd to misbehaviours; a cleaner approach to global vars (thanks to Peter Payne). ! fixed an issue with getopt() and external vars. Now pmacct has reported to compile without problems on FreeBSD 4.x (thanks to Kirill Ponomarew). ! missing conditional statement to check the runtime execution of compiled plugins in exec_plugins() 0.4.0 -- 02-May-2003 + switched to a plugin architecture: plugins need to be activated at configure time to be compiled and then used via "-P" command-line switch in pmacctd. See PLUGINS for more details. + added first plugin: Mysql driver. It uses a Mysql database as backend to store statistics other than in-memory table. See sql/ directory for scripts for creation of db needed to store data. + added the choice to collect statistics for traffic flows in addition to src|dst|sum aggregation via the "-c flows" command-line switch in pmacctd. + major code cleanups. + mostly rewritten configure script; switched back to autoconf 2.1. 0.3.4 -- 24-Mar-2003 + accounting of IP traffic for source, destination and aggregation of both. Introduced -c switch to pmacctd (thanks to Martynas Bieliauskas). + added daemonization of pmacctd process via -D command line switch + added buffering via pcap_open_live() timeout handling on those architectures where it is supported. + It compiles and works fine over FreeBSD 5.x; solved some pcap library issues. + added customization of pipe for client/server communication via -p command line switch both in pmacct and pmacctd 0.3.3 -- 19-Mar-2003 + introduced synchronous I/O multiplexing + support for -m 0 pmacctd switch, in-memory table can grow undefinitely. + revised memory pool descriptors table structure ! introduced realloc() in pmacct to support really large in-memory table transfers; solved additional alignment problems. ! solved compatibility issues with libpcap 0.4 ! solved nasty problem with -i pmacctd switch ! solved various memory code bugs and open issues 0.3.2 -- 13-Mar-2003 + support for pcap library filters ! minor bugfixes 0.3.1 -- 12-Mar-2003 + documentation stuff: updated TODO and added INTERNALS + revised query mechanism to server process, added a standard header to find command and optional values carried in query buffer. + added -s commandline switch to customize the size of each memory pool; see INTERNLS for more informations ! stability tests and fixes ! configure script enhancements 0.3.0 -- 11-Mar-2003 ! not public release + increased efficiency through allocation of memory pools instead of sparse malloc() calls when inserting new elements in in-memory table. + added -m commandline switch to pmacctd to set the number of available memory pools; the size of each memory pool is the number of buckets, chosen with -b commandline option, see INTERNALS for more informations. + switched client program to getopt() to acquire commandline inputs. + new -m commandline option in client program to acquire statistics of a specified IP address in a format useful for acquisition by MRTG program; see examples directory for a sample mrtg configuration. ! major bugfixes ! minor code cleanups 0.2.4 -- 07-Mar-2003 + portability: Tru64 5.x ! configure script fixes ! minor bugfixes 0.2.3 -- 05-Mar-2003 + first public release ! portability fixes ! minor bugfixes 0.2.2 -- 04-Mar-2003 + minor code cleanups + added autoconf, automake stuff 0.2.1 -- 03-Mar-2003 + fork()ing when handling queries + signal handling + command-line options using getopt() + usage instructions ! major bugfixes 0.2.0 -- 01-Mar-2003 + dynamic allocation of in-memory table + query (client/server) mechanism + added a Makefile ! major bugfixes 0.1.0 -- late Feb, 2003 + Initial release pmacct-0.14.0/UPGRADE0000644000175000017500000001371111741101437013065 0ustar paolopaoloUPGRAGE guidelines. pmacct is developed keeping an eye to backward compatibility: the upgrade to some newer version should be as smooth as possible from an user standpoint. This is because, for example, features like SQL table versioning have been introduced over the time. However, sometimes the upgrade may require some easy operations aimed to support the changes done or break old assumptions no longer valid. Such happenings have been (and will be) very limited through the development process. TO: >= 0.14.0 FROM: <= 0.14.0rc3 TOPIC: peer_dst_ip DESC: The peer_dst_ip primitive is being attached to IP prefix resolution method (ie. as defined by nfacctd_net directive) from AS number resolution method in the past (ie. as defined by nfacctd_as_new directive). TO: >= 0.14.0 FROM: <= 0.14.0rc3 TOPIC: Fallback resolution of networks and ASNs (ie. nfacctd_net, nfacctd_as_new) DESC: Longest match wins has been introduced to select which route resolution method to use in fallback scenarios. For example up to 0.14.0rc3, a route advertised via BGP would have been winning over any more specific route learned via sFlow/NetFlow regardless. TO: >= 0.14.0rc3 FROM: <= 0.14.0rc2 TOPIC: is_symmetric DESC: Support for is_symmetric aggregation primitive has been ceased due to lack of interest from the general community. TO: >= 0.14.0rc3 FROM: <= 0.14.0rc2 TOPIC: peer_src_ip DESC: peer_src_ip primitive must represent a reference (IP address, Agent ID) of the NetFlow or sFlow emitter for a certain flow. Due to previous work, this primitive was connected to the [ns]facctd_as_new mechanism which, if set to 'bgp', was making it represent the IP address of a BGP peer instead. This is found not correct and hence peer_src_ip has now been disconnected from the [ns]facctd_as_new feature and always constitutes a reference to the NetFlow or sFlow emitter. TO: >= 0.14.0rc2 FROM: <= 0.14.0rc1 TOPIC: NetFlow v9 sampling DESC: Support for sampling in NetFlow v9 and IPFIX is elegant from an architecture point of view - but complex if compared to NetFlow v5 and sFlow for example. Such increased complexity lacking of proper framing by means of a supportive RFC exposes to bizzarre and creative implementations by vendors. 0.14.0rc2 introduces fixes and workarounds to its sampled NetFlow v9 support in an effort to tackle specific but popular platforms among operators - and which can result in breaking some backward compatibility in this sense. 0.14.0rc2 introduces a sampling_map feature, which although not rocket science from a concept point of view, it helps supporting sampled NetFlow v9 in heterogeneous network hardware environments at the cost of an extra static setting to care about; on the other hand it's also true sampling rates are often uniform and seldomly redefined in a production network. TO: >= 0.12.1 FROM <= 0.12.0 TOPIC: Data source for ASNs must be explicitely defined DESC: data source for 'src_as' and 'dst_as' primitives for nfprobe and sfprobe plugins is now expected to be explicitely defined via the [ pmacctd_as | uacctd_as ] directive. All other plugins were already working like that. In terms of backward compatibility the only case affected is getting ASN values out of a Networks File: up to 0.12.0, it was sufficient to define a networks_file to implicitely use it. TO: >= 0.12.0rc1 FROM: <= 0.11 TOPIC: agent_id size and SQL table schemas DESC: With release 0.12, the agent_id field becomes 4-bytes large (from 2-bytes previously). SQL table schemas have been updated accordingly. If running a previous release and upgrading, you might incur into the risk that both Pre/Post-tagging infrastructures will accept values up to ~4M while the underlying SQL table schema is configured with a 2-bytes field. Solution is to run an "ALTER TABLE" statement to increase the field size during a maintenance window. TOPIC: nfprobe plugin: NetFlow v9 and 32-bit ASNs DESC: Release 0.12 introduces support for 32-bit ASNs in pmacct; things do not change in NetFlow v5 as if a 32-bit ASN is encountered, it is written as AS23456. In NetFlow v9, though, the source and destination AS fields are specified as 4 bytes long in the template. Given the template nature of NetFlow v9, this shouldn't pose a problem with 3rd party implementations but it's better to pay some extra attention while upgrading an existing installation. TO: >= 0.10.0 FROM: <= 0.10.0rc3 TOPIC: Configuration directives and command-line options DESC: In all previous releases, commandline options ( ie. -D -c ) were mutually exclusive with respect to configuration directives; now, they can cohexist and, more specifically, commandline options will override the content of the configuration file. This exposes to more interesting usages: shell> pmacctd -I -f to launch pmacctd sharing an unique configuration file while reading data from different tcpdump/ethereal tracefiles among multiple runs. TO: >= 0.8.3 FROM: <= 0.8.2 TOPIC: Pre-Tagging, Post-Tagging DESC: In all previous releases, the 'pre_tag_map' and 'post_tag' directives were causing the captured traffic to be automatically tagged while forwarded to each active plugin; this behaviour can result in reduced flexibility; the 0.8.3 release makes the two forementioned directives just to evaluate the tag to be assigned to captured traffic; a new 'aggregate' directive keyword - tag - causes the traffic to be marked (basing on the previous evaluation). So, a configuration like the following: ... pre_tag_map: /usr/local/pmacct/pre_tag.map aggregate[dummy]: src_host,dst_host,src_port,dst_port ... Have to be rewritten the following way in order for the plugin 'dummy' to receive the tags: ... pre_tag_map: /usr/local/pmacct/pre_tag.map aggregate[dummy]: tag,src_host,dst_host,src_port,dst_port ... [EOF] pmacct-0.14.0/acinclude.m40000644000175000017500000000255710556243724014263 0ustar paolopaolo# # Author: Guido Draheim # Last Modified: 2001-05-03 # Synopsis: AC_CHECK_TYPEDEF_(TYPEDEF, HEADER [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]) # Reference: http://autoconf-archive.cryp.to/ac_check_typedef.html # AC_DEFUN(AC_CHECK_TYPEDEF_, [dnl ac_lib_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` AC_CACHE_VAL(ac_cv_lib_$ac_lib_var, [ eval "ac_cv_type_$ac_lib_var='not-found'" ac_cv_check_typedef_header=`echo ifelse([$2], , stddef.h, $2)` AC_TRY_COMPILE( [#include <$ac_cv_check_typedef_header>], [int x = sizeof($1); x = x;], eval "ac_cv_type_$ac_lib_var=yes" , eval "ac_cv_type_$ac_lib_var=no" ) if test `eval echo '$ac_cv_type_'$ac_lib_var` = "no" ; then ifelse([$4], , :, $4) else ifelse([$3], , :, $3) fi ])]) dnl AC_CHECK_TYPEDEF(TYPEDEF, HEADER [, ACTION-IF-FOUND, dnl [, ACTION-IF-NOT-FOUND ]]) AC_DEFUN(AC_CHECK_TYPEDEF, [dnl AC_MSG_CHECKING([for $1 in $2]) AC_CHECK_TYPEDEF_($1,$2, [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_[]translit($1, [a-z], [A-Z])) HAVE_[]translit($1, [a-z], [A-Z])="1" ], AC_MSG_RESULT(no))dnl ]) # # Author: Paolo Lucente # Last Modified: 2006-03-07 # Synopsis: AC_LINEARIZE_PATH(PATH) # Reference: # AC_DEFUN(AC_LINEARIZE_PATH, [ absdir=`cd $1 2>/dev/null && pwd` if test x$absdir != x ; then [$2] fi ]) pmacct-0.14.0/configure.in0000644000175000017500000006302311741267646014404 0ustar paolopaolodnl Process this file with autoconf to produce a configure script. dnl configuration file for pmacct AC_INIT([src/pmacctd.c], [0.14.0], [paolo@pmacct.net]) AM_INIT_AUTOMAKE([pmacctd], [0.14.0]) AC_PREFIX_DEFAULT([/usr/local]) dnl Checks for programs. AC_PROG_CC host_os=`uname` host_cpu=`uname -m` host_os1=`uname -rs` AC_MSG_CHECKING(OS) AC_MSG_RESULT($host_os) AC_MSG_CHECKING(hardware) AC_MSG_RESULT($host_cpu) AC_PROG_RANLIB dnl initial checks; tweakings to CFLAGS and LDFLAGS dnl see final checks for tweakings to LIBS if test "x$ac_cv_prog_gcc" = xyes ; then CFLAGS="-O2 ${CFLAGS}" case "$host_os" in IRIX*) CFLAGS="-mabi=n32 -fno-builtins" LDFLAGS="-mabi=n32 -Wl,-rpath,/usr/lib32 ${LDFLAGS}" ;; esac else case "$host_os" in IRIX*) CFLAGS="-O2 -I/usr/freeware/include ${CFLAGS}" LDFLAGS="-n32 -L/usr/lib32 -L/usr/freeware/lib32 ${LDFLAGS}" ;; OSF*) CFLAGS="-O -assume noaligned_objects ${CFLAGS}" ;; esac fi dnl set debug level AC_MSG_CHECKING([whether to enable debugging compiler options]) AC_ARG_ENABLE(debug, [ --enable-debug enable debugging compiler options], AC_MSG_RESULT(yes) tmp_CFLAGS=`echo $CFLAGS | sed 's/O2/O0/g'` CFLAGS="$tmp_CFLAGS" CFLAGS="$CFLAGS -g -W -Wall", #CFLAGS="$CFLAGS -Waggregate-return" #CFLAGS="$CFLAGS -Wcast-align -Wcast-qual -Wnested-externs" #CFLAGS="$CFLAGS -Wshadow -Wbad-function-cast -Wwrite-strings" AC_MSG_RESULT(no) ) dnl set relax level AC_MSG_CHECKING([whether to relax compiler optimizations]) AC_ARG_ENABLE(relax, [ --enable-relax relax compiler optimization], AC_MSG_RESULT(yes) tmp_CFLAGS=`echo $CFLAGS | sed 's/O2/O0/g'` CFLAGS="$tmp_CFLAGS", AC_MSG_RESULT(no) ) dnl os specific flags case "$host_os" in OSF*) AC_DEFINE(OSF1, 1) ;; Sun*) AC_DEFINE(SOLARIS, 1) LIBS="-lresolv -lsocket -lnsl ${LIBS}" ;; IRIX*) AC_DEFINE(IRIX, 1) ;; esac dnl os-version specific flags case "$host_os1" in "FreeBSD 4"*) AC_DEFINE(FBSD4, 1) ;; "DragonFly"*) AC_DEFINE(FBSD4, 1) ;; esac dnl cpu specific flags case "$host_cpu" in sun*) AC_DEFINE(CPU_sparc, 1) ;; esac AC_CHECK_PROG(MAKE, gmake, gmake) if test x"$MAKE" = x""; then AC_CHECK_PROG(MAKE, make, make) fi AC_SUBST(MAKE) AC_PROG_MAKE_SET dnl dnl some systems have __progname ; if this is the case and we play around argv dnl we need to enable a minor hack to make things work nicely. dnl AC_MSG_CHECKING(for __progname) AC_TRY_LINK([ extern char *__progname; ], [ __progname = "test"; ], [AC_MSG_RESULT(yes); AC_DEFINE(PROGNAME, 1)], [AC_MSG_RESULT(no)]) dnl dnl Some checks to understand whether we need to instruct the linker for dnl exporting collector symbols to dynamically loaded classifiers. dnl dnl OS'es with ELF executables using the GNU linker (Linux and recent *BSD, dnl in rare cases Solaris) typically need '-Wl,-export-dynamic'; some SYSv4 dnl systems instead need '-Wl,-Bexport'; AIX 4.x wants -Wl,-bexpall,-brtl'. dnl AC_MSG_CHECKING(for extra flags needed to export symbols) if test "x$ac_cv_prog_gcc" = xyes ; then case $host_os in aix4*|aix5*) CFLAGS="${CFLAGS} -Wl,-bexpall,-brtl" ;; *) save_ldflags="${LDFLAGS}" LDFLAGS="-Wl,--export-dynamic ${save_ldflags}" AC_TRY_LINK(,, [ AC_MSG_RESULT(--export-dynamic) ], [ LDFLAGS="-Wl,-Bexport ${save_ldflags}" AC_TRY_LINK(,, [ AC_MSG_RESULT(-Bexport) ], [ AC_MSG_RESULT(none) LDFLAGS="${save_ldflags}" ] ) ] ) ;; esac else AC_MSG_RESULT(none) fi dnl dnl some C compilers (ex. MipsPRO) don't know how to parse the 'inline' keyword dnl AC_MSG_CHECKING(for static inline) AC_TRY_COMPILE([#include ] static inline func() { } , [ func(); ], [AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no); AC_DEFINE(NOINLINE, 1)]) dnl dnl Check for architecture endianess: big | little dnl dnl XXX: switch to manually define this feature ac_cv_endianess="unknown" if test x"$ac_cv_endianess" = x"unknown"; then AC_MSG_CHECKING(endianess) AC_TRY_RUN([main () { union { long l; char c[sizeof (long)]; } u; u.l = 1; exit (u.c[sizeof (long) - 1] == 1); }], ac_cv_endianess="little", ac_cv_endianess="big", ac_cv_endianess="little") AC_MSG_RESULT($ac_cv_endianess) fi if test x"$ac_cv_endianess" = x"big"; then AC_DEFINE(IM_BIG_ENDIAN, 1) fi if test x"$ac_cv_endianess" = x"little"; then AC_DEFINE(IM_LITTLE_ENDIAN, 1) fi dnl dnl Check for unaligned memory access; based entirely over dnl AC_LBL_UNALIGNED_ACCESS dnl dnl XXX: switch to manually define this feature; ac_cv_unaligned="unknown" case "$host_cpu" in alpha*|arm*|hp*|mips*|sh*|sparc*|ia64|nv1) ac_cv_unaligned="fail" AC_MSG_CHECKING(unaligned accesses) AC_MSG_RESULT($ac_cv_unaligned) ;; esac if test x"$ac_cv_unaligned" = x"unknown"; then AC_MSG_CHECKING(unaligned accesses) cat > conftest.c << EOF #include #include #include unsigned char a[[5]] = { 1, 2, 3, 4, 5 }; main () { unsigned int i; pid_t pid; int status; /* avoid "core dumped" message */ pid = fork(); if (pid < 0) exit(2); if (pid > 0) { /* parent */ pid = waitpid(pid, &status, 0); if (pid < 0) exit(3); exit(!WIFEXITED(status)); } /* child */ i = *(unsigned int *)&a[[1]]; printf("%d\n", i); exit(0); } EOF ${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS \ conftest.c $LIBS >/dev/null 2>&1 if test ! -x conftest ; then ac_cv_unaligned="fail" else ./conftest >conftest.out if test ! -s conftest.out ; then ac_cv_unaligned="fail" else ac_cv_unaligned="ok" fi fi rm -f conftest* core core.conftest AC_MSG_RESULT($ac_cv_unaligned) fi if test x"$ac_cv_unaligned" = x"fail"; then AC_DEFINE(NEED_ALIGN, 1) fi AC_MSG_CHECKING([whether to disable L2 features]) AC_ARG_ENABLE(l2, [ --disable-l2 disable Layer-2 features and support], if test x$enableval = x"yes" ; then AC_MSG_RESULT(no) AC_DEFINE(HAVE_L2, 1) else AC_MSG_RESULT(yes) fi , AC_MSG_RESULT(no) AC_DEFINE(HAVE_L2, 1) ) AC_MSG_CHECKING([whether to enable IPv6 code]) AC_ARG_ENABLE(ipv6, [ --enable-ipv6 Enable IPv6 code], [ AC_MSG_RESULT(yes) AC_CHECK_FUNCS(inet_pton) if test x"$ac_cv_func_inet_pton" = x"no"; then AC_MSG_ERROR(ERROR: missing inet_pton(); disable IPv6 hooks !) fi AC_CHECK_FUNCS(inet_ntop) if test x"$ac_cv_func_inet_ntop" = x"no"; then AC_MSG_ERROR(ERROR: missing inet_ntop(); disable IPv6 hooks !) fi AC_DEFINE(ENABLE_IPV6, 1) ipv6support="yes" case "$host_os" in IRIX*) AC_DEFINE(INET6, 1) ;; esac ], AC_MSG_RESULT(no) ipv6support="no" ) if test $ipv6support = "yes"; then AC_MSG_CHECKING([whether to enable IPv4-mapped IPv6 sockets ]) AC_ARG_ENABLE(v4-mapped, [ --enable-v4-mapped allow IPv6 sockets to handle IPv4 connections], [ if test x$enableval = x"yes" ; then AC_DEFINE(V4_MAPPED, 1) AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) fi ], [ case $host_os in *FreeBSD*|*NetBSD*|*OpenBSD*) AC_MSG_RESULT(no) ;; *) AC_DEFINE(V4_MAPPED, 1) AC_MSG_RESULT(yes) ;; esac ] ) fi AC_ARG_WITH(pcap-includes, [ --with-pcap-includes=DIR Search the specified directories for header files], [ AC_LINEARIZE_PATH($withval, withval=$absdir) INCLUDES="${INCLUDES} -I$withval" PCAPINCLS=$withval PCAPINCLUDESFOUND=1 ]) if test x"$PCAPINCLS" != x""; then AC_MSG_CHECKING(your own pcap includes) if test -r $PCAPINCLS/pcap.h; then AC_MSG_RESULT(ok) AC_DEFINE(HAVE_PCAP_H, 1) else AC_MSG_RESULT(no) AC_MSG_ERROR(ERROR: missing pcap.h in $PCAPINCLS) fi fi if test x"$PCAPINCLUDESFOUND" = x""; then AC_MSG_CHECKING([default locations for pcap.h]) if test -r /usr/include/pcap.h; then AC_MSG_RESULT([found in /usr/include]) PCAPINCLUDESFOUND=1 AC_DEFINE(HAVE_PCAP_H, 1) elif test -r /usr/include/pcap/pcap.h; then AC_MSG_RESULT([found in /usr/include]) PCAPINCLUDESFOUND=1 AC_DEFINE(HAVE_PCAP_PCAP_H, 1) elif test -r /usr/local/include/pcap.h; then AC_MSG_RESULT([found in /usr/local/include]) INCLUDES="${INCLUDES} -I/usr/local/include" PCAPINCLUDESFOUND=1 AC_DEFINE(HAVE_PCAP_H, 1) elif test -r /usr/local/include/pcap/pcap.h; then AC_MSG_RESULT([found in /usr/local/include]) INCLUDES="${INCLUDES} -I/usr/local/include" PCAPINCLUDESFOUND=1 AC_DEFINE(HAVE_PCAP_PCAP_H, 1) fi if test x"$PCAPINCLUDESFOUND" = x""; then AC_MSG_RESULT([not found]) AC_MSG_ERROR(ERROR: missing pcap.h) fi fi AC_ARG_WITH(pcap-libs, [ --with-pcap-libs=DIR Search the specified directories for libraries], [ AC_LINEARIZE_PATH($withval, withval=$absdir) LIBS="${LIBS} -L$withval" PCAPLIB=$withval PCAPLIBFOUND=1 ]) if test x"$PCAPLIB" != x""; then AC_MSG_CHECKING(your own pcap libraries) if test -r $PCAPLIB/libpcap.a -o -r $PCAPLIB/libpcap.so; then AC_MSG_RESULT(ok) PCAP_LIB_FOUND=1 AC_MSG_CHECKING(for PF_RING library) if test -r $PCAPLIB/libpfring.a -o -r $PCAPLIB/libpfring.so; then LIBS="${LIBS} -lpcap -lpfring" AC_MSG_RESULT(yes) PFRING_LIB_FOUND=1 else AC_MSG_RESULT(no) fi else AC_MSG_RESULT(no) AC_MSG_ERROR(ERROR: unable to find pcap library in $PCAPLIB) fi fi if test x"$PCAPLIBFOUND" = x""; then AC_MSG_CHECKING([default locations for libpcap]) if test -r /usr/local/lib/libpcap.a -o -r /usr/local/lib/libpcap.so; then LIBS="${LIBS} -L/usr/local/lib" AC_MSG_RESULT([found in /usr/local/lib]) PCAPLIBFOUND=1 AC_MSG_CHECKING(for PF_RING library) if test -r /usr/local/lib/libpfring.a -o -r /usr/local/lib/libpfring.so; then LIBS="${LIBS} -lpcap -lpfring" AC_MSG_RESULT(yes) PFRING_LIB_FOUND=1 else AC_MSG_RESULT(no) fi else AC_MSG_RESULT(no) fi fi dnl Checks for libraries. dnl libpcap is checked only if PF_RING is not found if test x"$PFRING_LIB_FOUND" = x""; then AC_CHECK_LIB([pcap], [pcap_dispatch], [], [AC_MSG_ERROR([ ERROR: missing pcap library. Refer to: http://www.tcpdump.org/ ])]) AC_CHECK_LIB([pcap], [pcap_setnonblock], [ AC_DEFINE(PCAP_7, 1) ], []) fi dnl packet capture type check; taken from libpcap. AC_MSG_CHECKING(packet capture type) if test -r /dev/bpf0 ; then V_PCAP=bpf elif test -r /usr/include/net/pfilt.h ; then V_PCAP=pf elif test -r /dev/enet ; then V_PCAP=enet elif test -r /dev/nit ; then V_PCAP=snit elif test -r /usr/include/sys/net/nit.h ; then V_PCAP=nit elif test -r /usr/include/linux/socket.h ; then V_PCAP=linux elif test -r /usr/include/net/raw.h ; then V_PCAP=snoop elif test -r /usr/include/odmi.h ; then # # On AIX, the BPF devices might not yet be present - they're # created the first time libpcap runs after booting. # We check for odmi.h instead. # V_PCAP=bpf elif test -r /usr/include/sys/dlpi.h ; then V_PCAP=dlpi elif test -c /dev/bpf0 ; then # check again in case not readable V_PCAP=bpf elif test -c /dev/enet ; then # check again in case not readable V_PCAP=enet elif test -c /dev/nit ; then # check again in case not readable V_PCAP=snit else V_PCAP=null fi AC_MSG_RESULT($V_PCAP) AC_DEFINE_UNQUOTED(PCAP_TYPE_$V_PCAP, 1) dnl start: mysql handling AC_MSG_CHECKING(whether to enable MySQL support) AC_ARG_ENABLE(mysql, [ --enable-mysql Enable MySQL support], [ case "$enableval" in yes) AC_MSG_RESULT(yes) USING_SQL="yes" AC_ARG_WITH(mysql-libs, [ --with-mysql-libs=DIR Search for MySQL libs in the specified directory], [ AC_LINEARIZE_PATH($withval, withval=$absdir) LIBS="${LIBS} -L$withval" MYSQLLIB=$withval MYSQLLIBFOUND=1 ]) if test x"$MYSQLLIB" != x""; then AC_MSG_CHECKING(your own MySQL client library) if test -r $MYSQLLIB/libmysqlclient.a -o -r $MYSQLLIB/libmysqlclient.so; then AC_MSG_RESULT(ok) else AC_MSG_RESULT(no) AC_MSG_ERROR(ERROR: missing MySQL client library in $MYSQLLIB) fi fi if test x"$MYSQLLIBFOUND" = x""; then AC_MSG_CHECKING([default locations for libmysqlclient]) if test -r /usr/lib/mysql/libmysqlclient.a -o -r /usr/lib/mysql/libmysqlclient.so; then LIBS="${LIBS} -L/usr/lib/mysql" AC_MSG_RESULT([found in /usr/lib/mysql]) MYSQLLIBFOUND=1 elif test -r /usr/local/mysql/lib/libmysqlclient.a -o -r /usr/local/mysql/lib/libmysqlclient.so; then LIBS="${LIBS} -L/usr/local/mysql/lib" AC_MSG_RESULT([found in /usr/local/mysql/lib]) MYSQLLIBFOUND=1 elif test -r /usr/local/lib/mysql/libmysqlclient.a -o -r /usr/local/lib/mysql/libmysqlclient.so; then LIBS="${LIBS} -L/usr/local/lib/mysql" AC_MSG_RESULT([found in /usr/local/lib/mysql]) MYSQLLIBFOUND=1 else AC_MSG_RESULT([not found]) fi fi if test x"$MYSQLLIBFOUND" = x""; then AC_CHECK_LIB([mysqlclient], [mysql_real_connect], [], [AC_MSG_ERROR([ ERROR: missing MySQL client library. Refer to: http://www.mysql.com/ ])]) else LIBS="${LIBS} -lmysqlclient" case "$host_os" in Sun*) LIBS="${LIBS} -lrt" ;; esac fi AC_ARG_WITH(mysql-includes, [ --with-mysql-includes=DIR Search for MySQL includes in the specified directory], [ AC_LINEARIZE_PATH($withval, withval=$absdir) INCLUDES="${INCLUDES} -I$withval" MYSQLINCLUDES=$withval MYSQLINCLUDESFOUND=1 ]) if test x"$MYSQLINCLUDES" != x""; then AC_MSG_CHECKING(your own MySQL headers) if test -r $MYSQLINCLUDES/mysql/mysql.h; then AC_MSG_RESULT(ok) elif test -r $MYSQLINCLUDES/mysql.h; then AC_MSG_RESULT(ok) AC_DEFINE(CUT_MYSQLINCLUDES_DIR, 1) else AC_MSG_RESULT(no) AC_MSG_ERROR(ERROR: missing MySQL headers in $MYSQLINCLUDES) fi fi if test x"$MYSQLINCLUDESFOUND" = x""; then AC_MSG_CHECKING([default locations for mysql.h]) if test -r /usr/include/mysql/mysql.h; then AC_MSG_RESULT([found in /usr/include/mysql]) MYSQLINCLUDESFOUND=1; elif test -r /usr/local/include/mysql/mysql.h; then INCLUDES="${INCLUDES} -I/usr/local/include" AC_MSG_RESULT([found in /usr/local/include/mysql]) MYSQLINCLUDESFOUND=1; elif test -r /usr/local/mysql/include/mysql.h; then INCLUDES="${INCLUDES} -I/usr/local/mysql/include" AC_MSG_RESULT([found in /usr/local/mysql/include]) AC_DEFINE(CUT_MYSQLINCLUDES_DIR, 1) MYSQLINCLUDESFOUND=1; fi if test x"$MYSQLINCLUDESFOUND" = x""; then AC_MSG_RESULT([not found]) fi fi if test x"$MYSQLINCLUDESFOUND" = x""; then AC_CHECK_HEADER([mysql/mysql.h],, [AC_MSG_ERROR(ERROR: missing MySQL headers)]) fi AC_DEFINE(WITH_MYSQL, 1) PLUGINS="${PLUGINS} mysql_plugin.c" EXTRABIN="${EXTRABIN} pmmyplay" ;; no) AC_MSG_RESULT(no) ;; esac ], AC_MSG_RESULT(no)) dnl finish: mysql handling dnl start: pgsql handling AC_MSG_CHECKING(whether to enable PostgreSQL support) AC_ARG_ENABLE(pgsql, [ --enable-pgsql Enable PostgreSQL support], [ case "$enableval" in yes) AC_MSG_RESULT(yes) USING_SQL="yes" AC_ARG_WITH(pgsql-libs, [ --with-pgsql-libs=DIR Search for PostgreSQL libs in the specified directory], [ AC_LINEARIZE_PATH($withval, withval=$absdir) LIBS="${LIBS} -L$withval" PGSQLLIB=$withval PGSQLLIBFOUND=1 ]) if test x"$PGSQLLIB" != x""; then AC_MSG_CHECKING(your own PostgreSQL client library) if test -r $PGSQLLIB/libpq.a -o -r $PGSQLLIB/libpq.so; then AC_MSG_RESULT(ok) else AC_MSG_RESULT(no) AC_MSG_ERROR(ERROR: missing PostgreSQL client library in $PGSQLLIB) fi fi if test x"$PGSQLLIBFOUND" = x""; then AC_MSG_CHECKING([default locations for libpq]) if test -r /usr/lib/libpq.a -o -r /usr/lib/libpq.so; then AC_MSG_RESULT([found in /usr/lib]) PGSQLLIBFOUND=1 elif test -r /usr/local/lib/libpq.a -o -r /usr/local/lib/libpq.so; then LIBS="${LIBS} -L/usr/local/lib" AC_MSG_RESULT([found in /usr/local/lib]) PGSQLLIBFOUND=1 elif test -r /usr/local/pgsql/lib/libpq.a -o -r /usr/local/pgsql/lib/libpq.so; then LIBS="${LIBS} -L/usr/local/pgsql/lib" AC_MSG_RESULT([found in /usr/local/pgsql/lib]) PGSQLLIBFOUND=1 else AC_MSG_RESULT([not found]) fi fi if test x"$PGSQLLIBFOUND" = x""; then AC_CHECK_LIB([pq], [PQconnectdb], [], [AC_MSG_ERROR([ ERROR: missing PQ library. Refer to: http://www.postgresql.org/ ])]) else LIBS="${LIBS} -lpq" fi AC_ARG_WITH(pgsql-includes, [ --with-pgsql-includes=DIR Search for PostgreSQL includes in the specified directory], [ AC_LINEARIZE_PATH($withval, withval=$absdir) INCLUDES="${INCLUDES} -I$withval" PGSQLINCLUDES=$withval PGSQLINCLUDESFOUND=1 ]) if test x"$PGSQLINCLUDES" != x""; then AC_MSG_CHECKING(your own PostgreSQL headers) if test -r $PGSQLINCLUDES/libpq-fe.h; then AC_MSG_RESULT(ok) else AC_MSG_RESULT(no) AC_MSG_ERROR(ERROR: missing pgsql headers in $PGSQLINCLUDES) fi fi if test x"$PGSQLINCLUDESFOUND" = x""; then AC_MSG_CHECKING([default locations for libpq-fe.h]) if test -r /usr/include/libpq-fe.h; then AC_MSG_RESULT([found in /usr/include]) PGSQLINCLUDESFOUND=1; elif test -r /usr/local/include/libpq-fe.h; then AC_MSG_RESULT([found in /usr/local/include]) INCLUDES="${INCLUDES} -I/usr/local/include" PGSQLINCLUDESFOUND=1; elif test -r /usr/local/pgsql/include/libpq-fe.h; then AC_MSG_RESULT([found in /usr/local/pgsql/include]) INCLUDES="${INCLUDES} -I/usr/local/pgsql/include" PGSQLINCLUDESFOUND=1; fi if test x"$PGSQLINCLUDESFOUND" = x""; then AC_MSG_RESULT([not found]) fi fi if test x"$PGSQLINCLUDESFOUND" = x""; then AC_CHECK_HEADER([libpq-fe.h],, [AC_MSG_ERROR(ERROR: missing PostgreSQL headers)]) fi AC_DEFINE(WITH_PGSQL, 1) PLUGINS="${PLUGINS} pgsql_plugin.c" EXTRABIN="${EXTRABIN} pmpgplay" ;; no) AC_MSG_RESULT(no) ;; esac ], AC_MSG_RESULT(no)) dnl finish: pgsql handling AC_MSG_CHECKING(whether to enable SQLite3 support) AC_ARG_ENABLE(sqlite3, [ --enable-sqlite3 Enable SQLite3 support], [ case "$enableval" in yes) AC_MSG_RESULT(yes) USING_SQL="yes" AC_ARG_WITH(sqlite3-libs, [ --with-sqlite3-libs=DIR Search for SQLite3 libs in the specified directory], [ AC_LINEARIZE_PATH($withval, withval=$absdir) LIBS="${LIBS} -L$withval" SQLITE3LIB=$withval SQLITE3LIBFOUND=1 ]) if test x"$SQLITE3LIB" != x""; then AC_MSG_CHECKING(your own SQLite3 client library) if test -r $SQLITE3LIB/libsqlite3.a -o -r $SQLITE3LIB/libsqlite3.so; then AC_MSG_RESULT(ok) else AC_MSG_RESULT(no) AC_MSG_ERROR(ERROR: missing SQLite3 client library in $SQLITE3LIB) fi fi if test x"$SQLITE3LIBFOUND" = x""; then AC_MSG_CHECKING([default locations for libsqlite3]) if test -r /usr/lib/libsqlite3.a -o -r /usr/lib/libsqlite3.so; then # LIBS="${LIBS} -L/usr/lib/sqlite3" AC_MSG_RESULT([found in /usr/lib]) SQLITE3LIBFOUND=1 elif test -r /usr/local/sqlite3/lib/libsqlite3.a -o -r /usr/local/sqlite3/lib/libsqlite3.so; then LIBS="${LIBS} -L/usr/local/sqlite3/lib" AC_MSG_RESULT([found in /usr/local/sqlite3/lib]) SQLITE3LIBFOUND=1 elif test -r /usr/local/lib/libsqlite3.a -o -r /usr/local/lib/libsqlite3.so; then LIBS="${LIBS} -L/usr/local/lib" AC_MSG_RESULT([found in /usr/local/lib]) SQLITE3LIBFOUND=1 else AC_MSG_RESULT([not found]) fi fi if test x"$SQLITE3LIBFOUND" = x""; then AC_CHECK_LIB([sqlite3], [sqlite3_open], [], [AC_MSG_ERROR([ ERROR: missing SQLite3 client library. Refer to: http://sqlite.org/ ])]) else LIBS="${LIBS} -lsqlite3" fi AC_ARG_WITH(sqlite3-includes, [ --with-sqlite3-includes=DIR Search for SQLite3 includes in the specified directory], [ AC_LINEARIZE_PATH($withval, withval=$absdir) INCLUDES="${INCLUDES} -I$withval" SQLITE3INCLUDES=$withval SQLITE3INCLUDESFOUND=1 ]) if test x"$SQLITE3INCLUDES" != x""; then AC_MSG_CHECKING(your own SQLite3 headers) if test -r $SQLITE3INCLUDES/sqlite3.h; then AC_MSG_RESULT(ok) else AC_MSG_RESULT(no) AC_MSG_ERROR(ERROR: missing SQLite3 headers in $SQLITE3INCLUDES) fi fi if test x"$SQLITE3INCLUDESFOUND" = x""; then AC_MSG_CHECKING([default locations for sqlite3.h]) if test -r /usr/include/sqlite3.h; then AC_MSG_RESULT([found in /usr/include]) SQLITE3INCLUDESFOUND=1; elif test -r /usr/local/include/sqlite3.h; then # INCLUDES="${INCLUDES} -I/usr/local/include" AC_MSG_RESULT([found in /usr/local/include]) SQLITE3INCLUDESFOUND=1; elif test -r /usr/local/sqlite3/include/sqlite3.h; then INCLUDES="${INCLUDES} -I/usr/local/sqlite3/include" AC_MSG_RESULT([found in /usr/local/sqlite3/include]) SQLITE3INCLUDESFOUND=1; fi if test x"$SQLITE3INCLUDESFOUND" = x""; then AC_MSG_RESULT([not found]) fi fi if test x"$SQLITE3INCLUDESFOUND" = x""; then AC_CHECK_HEADER([sqlite3.h],, [AC_MSG_ERROR(ERROR: missing SQLite3 headers)]) fi AC_DEFINE(WITH_SQLITE3, 1) PLUGINS="${PLUGINS} sqlite3_plugin.c" ;; no) AC_MSG_RESULT(no) ;; esac ], AC_MSG_RESULT(no)) dnl finish: sqlite3 handling AC_MSG_CHECKING([whether to disable shared objects]) AC_ARG_ENABLE(so, [ --disable-so Disable shared objects], if test x$enableval = x"yes" ; then AC_MSG_RESULT(no) AC_CHECK_FUNC(dlopen, [ USING_DLOPEN="yes" ], []) if test x"$USING_DLOPEN" != x"yes"; then AC_CHECK_LIB(dl, dlopen, [ USING_DLOPEN="yes" LIBS="${LIBS} -ldl" ], [ AC_MSG_ERROR(Unable to find dlopen(). Try with --disable-so) ]) fi else AC_MSG_RESULT(yes) fi , AC_MSG_RESULT(no) AC_CHECK_FUNC(dlopen, [ USING_DLOPEN="yes" ], []) if test x"$USING_DLOPEN" != x"yes"; then AC_CHECK_LIB(dl, dlopen, [ USING_DLOPEN="yes" LIBS="${LIBS} -ldl" ], [ AC_MSG_ERROR(Unable to find dlopen(). Try with --disable-so) ]) fi ) dnl finish: shared object handling if test x"$USING_DLOPEN" = x"yes"; then AC_DEFINE(HAVE_DLOPEN, 1) fi if test x"$USING_SQL" = x"yes"; then PLUGINS="${PLUGINS} sql_common.c sql_handlers.c log_templates.c preprocess.c" LIBS="${LIBS} -lm -lz" fi dnl Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([getopt.h sys/select.h sys/time.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_CHECK_TYPEDEF(u_int64_t, sys/types.h) AC_CHECK_TYPEDEF(u_int32_t, sys/types.h) AC_CHECK_TYPEDEF(u_int16_t, sys/types.h) AC_CHECK_TYPEDEF(u_int8_t, sys/types.h) AC_CHECK_TYPEDEF(uint64_t, sys/types.h) AC_CHECK_TYPEDEF(uint32_t, sys/types.h) AC_CHECK_TYPEDEF(uint16_t, sys/types.h) AC_CHECK_TYPEDEF(uint8_t, sys/types.h) AC_MSG_CHECKING([whether to enable 64bit counters]) AC_ARG_ENABLE(64bit, [ --enable-64bit Enable 64bit counters], if test x$enableval = x"yes" ; then AC_MSG_RESULT(yes) AC_DEFINE(HAVE_64BIT_COUNTERS, 1) else AC_MSG_RESULT(no) fi , AC_MSG_RESULT(no) ) AC_MSG_CHECKING([whether to enable multithreading in pmacctd]) AC_ARG_ENABLE(threads, [ --enable-threads Enable multi-threading in pmacctd], if test x$enableval = x"yes" ; then AC_MSG_RESULT(yes) AC_DEFINE(ENABLE_THREADS, 1) dnl OS Specifics [ case "$host" in *-linux-*) AC_DEFINE(_XOPEN_SOURCE, 600) AC_DEFINE(_GNU_SOURCE, 1) ;; esac ] LIBS="${LIBS} -lpthread" THREADS_SOURCES="thread_pool.c" else AC_MSG_RESULT(no) THREADS_SOURCES="" fi , AC_MSG_RESULT(no) ) AC_MSG_CHECKING(whether to enable ULOG support) AC_ARG_ENABLE(ulog, [ --enable-ulog Enable ULOG support], if test "x$enableval" = xyes ; then AC_MSG_RESULT(yes) CFLAGS="${CFLAGS} -DENABLE_ULOG" else AC_MSG_RESULT(no) fi , AC_MSG_RESULT(no) ) dnl Checks for library functions. AC_TYPE_SIGNAL dnl AC_CHECK_FUNCS(inet_ntoa socket) AC_CHECK_FUNCS([strlcpy vsnprintf setproctitle]) dnl final checks dnl trivial solution to portability issue CFLAGS="${CFLAGS} ${INCLUDES}" INCLUDES="" case "$host_os" in IRIX*) LIBS="${LIBS} -lgen" ;; esac LIBS="${LIBS} -lnfprobe_plugin -Lnfprobe_plugin/ -lsfprobe_plugin -Lsfprobe_plugin/ -lbgp -Lbgp/ -ltee_plugin -Ltee_plugin/ -lisis -Lisis/" echo " PLATFORM ..... : `uname -m` OS ........... : `uname -rs` (`uname -n`) COMPILER ..... : ${CC} CFLAGS ....... : ${CFLAGS} LIBS ......... : ${LIBS} LDFLAGS ...... : ${LDFLAGS} Now type 'make' to compile the source code. Are you willing to get in touch with other pmacct users? Join the pmacct mailing-list by sending a message to pmacct-discussion-subscribe@pmacct.net Need for documentation and examples? Read the README file or go to http://wiki.pmacct.net/ " AC_SUBST([PLUGINS]) AC_SUBST([THREADS_SOURCES]) AC_SUBST([EXTRABIN]) AC_OUTPUT([ Makefile \ src/Makefile src/nfprobe_plugin/Makefile \ src/sfprobe_plugin/Makefile src/bgp/Makefile \ src/tee_plugin/Makefile src/isis/Makefile ]) pmacct-0.14.0/docs/0000755000175000017500000000000011741267746013020 5ustar paolopaolopmacct-0.14.0/docs/INTERNALS0000644000175000017500000007655211442170303014275 0ustar paolopaolo(poorman's) TABLE OF CONTENTS: I. Introduction II. Primitives III. The whole picture IV. Processes Vs. threads V. Communications between core process and plugins VI. Memory table plugin VII. SQL issues and *SQL plugins VIII. Recovery modes IX. pmacctd flows counter implementation X. Classifier and connection tracking engines XI. Jumps and flow diversions in Pre-Tagging infrastructure XII. BGP daemon thread dimensioning I. Introduction Giving a quick look to the old 'INTERNALS' textfile, this new one starts with a big step forward: a rough table of contents, though the document is still not fancy nor formatted. I'm also conscious the package is still missing its man page. The goal of this document would be an 'as much as possible' careful description of the development paths, trying to expose the work done to constructive critics. Since March 2005, this document is complemented by a paper about an architectural overview of the project 'pmacct: steps forward interface counters'; the referred paper is available for download at the pmacct homepage. II. Primitives Individual packets and specific traffic flows are identified by their header fields. The union of such header fields generates (a rather large) set of primitives. Traffic aggregates are typically identified by a reduced set of primitives instead. Packets or flows are merged into aggregates by defining a sub-set of primitives, by shaving off unused primitives from the original set and finally by summing their counters (ie. bytes and packets). Additional operations involved into the process can also include logical grouping of specific primitives into more general entities (ie. IP addresses into network prefixes), temporal aggregation (ie. splitting aggregates in time bins), tagging, sampling and filtering. What are primitives? They are atomic expressions ie. "src_port", "dst_host", "proto"; currently the unique boolean operator supported to glue expressions is "and". Hence, traffic could be aggregated translating a "who connects where, using which service" speech language statement into one recognized by pmacct: "src_host,dst_host,dst_port, proto". Comma, because of the unique logical connective "and", is simply intended as a separator. III. The whole picture ----[ nfacctd loop ]--------------------------- | | | [ check ] [ handle ] | | ... =====[ Allow ]======[ pre_tag_map ]=== ... | | [ table ] | | | ------------------------------------------------ \ | | -----[ core process ]-------------------------------------------------------------------------------- | | | | | [ apply ] [ evaluate ] [ handle ] [ write buffer ] | | | [ pre_tag_filter ] [ primitives ] |==[ channel buffer ]====[ to plugin ]==== ... | mirrored | / && && | | traffic | / [ apply ] [ apply ] | [ handle ] [ write buffer ] | ====================[ aggregate_filter ]====[ post_tag ]======|==[ channel buffer ]====[ to plugin ]==== ... | NetFlow | \ && | | | | [ evaluate ] | [ handle ] [ write buffer ] | | | [ packet sampling ] |==[ channel buffer ]====[ to plugin ]==== ... | | | | | \ | ----------------------------------------------------------------------------------------------------- | | / ----[ pmacctd loop ]------------------------------------------------------------ | | | [ handle ] [ handle ] [ handle ] [ handle ] | | ... ====[ link layer ]=====[ IP layer ]====[ fragments ]==== [ flows ]==== ... | | | -------------------------------------------------------------------------------- IV. Processes Vs. threads pmacctd, nfacctd and sfacctd, the pmacct package daemons, rely over both a multi-thread and multi-process architecture. Processes are used to encapsulate each plugin instance and, indeed, the Core Process. Threads are used to encapsulate specific functions within each process - ie. the BGP daemon thread within the Core Process. The Core Process either captures packets via the well-known libpcap API (pmacctd) or listens for specific packets coming from the network (nfacctd, for example, listens for NetFlow packets); packets are then processed (parsed, filtered, sampled, tagged, aggregated and bufferized if required) and sent to the active plugins. Plugins in turn pick and handle in some meaningful way aggregated data (struct pkt_data), ie. writing them to a SQL database, a memory table, etc. A diagram follows: |===> [ pmacctd/plugin ] libpcap pipe/shm| ===========> [ pmacctd/core ]==============|===> [ pmacctd/plugin ] socket To conclude a position on threads: threads are a necessity because of the tendency modern CPU are built (ie. multi-core). So far pmacct limits iteself to a macro-usage of threads, ie. where it makes sense to save on IPC or where big memory structures would lead the pages' copy-on-write to perform horrendly. The rationale is that fine- grained multi-threading can often become a fertile source of bugs. V. Communications between core process and plugins A single running Core Process, which gathers packets or flows from the network, is able to feed aggregated data to multiple plugins; plugins are distinguished by their name. Names, for this purpose, need to be unique. Aggregates are then pushed to active plugins through a shared circular queue. Each plugin has a private control channel with the Core Process. Circular queues are encapsulated into a more complex channel structure which also includes: copy of the aggregation method, an OOB (Out-of-Band) signalling channel, buffers, one or more filters and a pointer to the next free queue element. The Core Process simply loops around all established channels, in a round-robin fashion, feeding data to active plugins. The circular queue is effectively a shared memory segment; if the Plugin is sleeping (eg. because the arrival of new data from the network is not sustained), the Core Process kicks the Plugin signalling that new data are now available at the specified memory address; the Plugin catches the message and copies the buffer into its private memory space; if it is not sleeping, once finished, will check the next queue element to see whether new data are available. Either cases, the Plugin continues processing the received buffer. 'plugin_pipe_size' configuration directive aims to tune manually the circular queue size; raising its size is vital when facing large volumes of traffic, because the amount of data pushed onto the queue is directly (linearly) proportional to the number of packets captured by the core process. A small additional space is allocated for the out-of-band signallation mechanism, which is pipe-based. 'plugin_buffer_size' defines the transfer buffer size and is disabled by default. Its value has to be <= the circular queue size, hence the queue will be divided into 'plugin_buffer_size'/'plugin_pipe_size' chunks. Let's write down a few simple equations: dss = Default Segment Size dbs = Default Buffer Size = sizeof(struct pkt_data) as = Address Size = sizeof(char *) (it depends upon the hardware architecture) bs = 'plugin_buffer_size' value ss = 'plugin_pipe_size' value a) no 'plugin_buffer_size' and no 'plugin_pipe_size': circular queue size = (dss / as) * dbs signalling queue size = dss b) 'plugin_buffer_size' defined but no 'plugin_pipe_size': circular queue size = (dss / as) * bs signalling queue size = dss c) no 'plugin_buffer_size' but 'plugin_pipe_size' defined: circular queue size = ss signalling queue size = (ss / dbs) * as d) 'plugin_buffer_size' and 'plugin_pipe_size' defined: circular queue size = ss signalling queue size = (ss / bs) * as Intuitively, the equations above tell that if no 'plugin_pipe_size' is defined, the size of the circular queue is inferred by the size of the signalling queue, which is selected by the Operating System. If 'plugin_pipe_size' is defined, the circular queue size is set to the supplied value and the signalling queue size is adjusted accordingly. If 'plugin_buffer_size' is not defined, it's assumed to be sizeof(struct pkt_data), which is the size of a single aggregate travelling through the circolar queue; 'sizeof(char *)' is the size of a pointer, which is architecture-dependant. Few final remarks: a) buffer size of 10KB and pipe size of 10MB are well-tailored for most common environments; b) by enabling buffering, attaching the collector to a mute interface and doing some pings will not show any result (... data are buffered); c) take care to the ratio between the buffer size and pipe size; choose for a ratio not less than 1:100. But keeping it around 1:1000 is strongly adviceable; selecting a reduced ratio could lead to filling the queue. You may alternatively do some calculations based on the knowledge of your network environment: average_traffic = packets per seconds in your network segment sizeof(struct pkt_data) = ~70 bytes pipe size >= average_traffic * sizeof(struct pkt_data) circular queue [ pmacctd/core ] =================================> [ pmacctd/plugin ] | | | | | | enqueued buffers free | | | |==|==|==|==|==|==|==|===========| | | | `-------------------------------------------' OOB signalling queue VI. Memory table plugin In-Memory Table plugin (IMT) stores the aggregates as they have been assembled by core process in a memory structure, organized as an hash table. Such table is divided in a number of buckets. Aggregates are framed into a structure defined as 'struct acc' and then direct mapped to a bucket by the mean of a modulo function. Collisions in each bucket are solved building collision chains. An auxiliar structure, a LSU cache (Last Recently Used), is provided to speed up searches and updates into the main table. LSU saves last updated or searched element for each bucket: when a new operation on the bucket is required, the LSU cache is compared first; if it doesn't match, the collision chain gets traversed. It's adviceable to use a prime number of buckets (defined by 'imt_buckets' configuration directive), because it helps in achieving better data dispersion when applying the modulo function. Collision chains are organized as linked lists of elements, so they should be kept short because of the linear search over them; having a flat table (so, raising the number of buckets) helps in keeping chains short. Memory is allocated in large chunks, called memory pools, to limit as possible bad effects (such as trashing) derived from dispersion through the memory pages. In fact, drawbacks of the dense use of malloc() calls are extensively described on every Operating Systems textbook. Memory allocations are tracked via a linked list of chunk descriptors (struct memory_pool_desc) for later jobs such as freeing unused memory chunks, operations over the list, etc. The memory structure can be allocated either 'fixed' or 'dynamic'; when dealing with a fixed table, all descriptors are allocated in a single shot when the daemon is fired up; when dealing with a 'dynamic' memory table (which is allowed to grow undefinitely in memory new chunks of memory are allocated and added to the list during the execution. Using a fixed table places a maximum limit to the number of entries the table is able to store; the following calculation may help in building a fixed table: ES (Entry Size) ~ 50 bytes NE (Number of entries) NE = ('imt_mem_pools_number' * 'imt_mem_pools_size') / ES Default values are: imt_mem_pools_number = 16; imt_mem_pools_size = 8192; this will let the default fixed table to contain a maximum of slightly more than 2600 aggregates. IMT plugin does not rely any way over the realloc() function, but only mmap(). Table grows and shrinks with the help of the above described tracking structures. This is because of a few assumptions about the use of realloc(): (a) try to reallocate on the original memory block and (b) if (a) failed, allocate another memory block and copy the contents of the original block to this new location. In this scheme (a) can be done in constant time; in (b) only the allocation of new memory block and the deallocation of original block are done in constant time, but the copy of previous memory area, for large in-memory tables, could perform horrendly. Data stored into the memory structure can be either fetched, erased or zeroed by a client tool, pmacct, communicating through a Unix Domain socket (/tmp/collect.pipe by default). The available queries are 'bulk data retrieval', 'group data retrieval' (partial match), 'single entry retrieval' (exact match) and 'erase table'. Additionally, both partial and full matches may supply a request for resetting the counters. On the server side, the client query is evaluated: requests that need just a short stroll through the memory structure are accomplished by the plugin itself, the others (for example batch queries or bulk data retrieval) are served by a child process spawned by the plugin. Because memory table is allocated 'shared', operations requiring table modifications by such child (eg. resetting counters for an entry) are handled by raising a flag instead: next time the plugin will update that entry, it will also serve any pending request. With the introduction of batch queries (which enable to group into a single query up to 4096 requests) transfers may be fragmented by the Operating System. IMT plugin will take care of recomposing all fragments, expecting also a '\x4' placeholder as 'End of Message' marker. If an incomplete message is received, it's discarded as soon as current transfer timeout expires (1s). VII. SQL issues and *SQL plugins Storing aggregates into a persistent backend leaves chances for advanced operations and so these plugins are intended to give a wider range of features (eg. fallback mechanisms and backup storage methods if DB fails, counters breakdown, etc.) not available in other plugins. Let's firstly give a whole picture of how these SQL plugins work. As packets received from core process via communication channel get unpacked, they are inserted in a direct-mapped cache; then, at fixed time intervals (configurable via 'sql_refresh_time' key) the cache scanner kicks in and purges content to the database; optionally triggers may be executed (sql_trigger_exec, sql_trigger_time). Data to cache bucket mapping is computed via a modulo function. If the selected bucket already contains valid data then a conflict chain is built (or traversed if it already exists); the first free node encountered is used; if no free nodes are found then two more chances are explored: if any node has been marked as stale (it happens when an allocated node is unused for some consecutive time-bins) it is then reused by moving it away from its old chain; if no free nodes are available then a new one is allocated. Stale nodes are then retired if they still remain unused for longer times (RETIRE_TIME**2). To speed up nodes reuse and retirement, an additional LRU list of nodes is also mantained. If out of memory or the maximum number of allowed elements in the cache is reached data is immediately written to the database so to make room for further elements. The maximum number of allowed elements is defined to prevent the cache to grow in memory without any control. Such limit is internally calculated as: max cache elements = sql_cache_entries + ( sql_refresh_time * 100 ) As said before, aggregates are pushed into the DB at regular intervals; to speed up such operation a queue of pending queries is built as nodes are used; this allows to avoid long walks through the whole cache structure given, for various reasons (ie. classification, sql_startup_delay) not all elements might be eligible for purging. When the cache scanner kicks incurrent a new writer process is spawned and in charge of processing the pending elements queue; SQL statements are built and sent to the RDBMS. Because we, at this moment, don't known if INSERT queries would create duplicates, an UPDATE query is launched first and only if no rows are affected, then an INSERT query is trapped. 'sql_dont_try_update' twists this behaviour and skips directly to INSERT queries; when enabling this configuration directive, you must be sure there are no risks of duplicate aggregates to avoid data loss. Data in the cache is never erased but simply marked as invalid; this way while correctess of data is still preserved, we avoid the waste of CPU cycles. The number of cache buckets is tunable via the 'sql_cache_entries' configuration key; a prime number is strongly advisable to ensure a better data dispersion through the cache. Three notes about the above described process: (a) some time ago the concept of lazy data refresh deadlines has been introduced. Expiration of timers is checked without the aid of UNIX signals but when new data comes in. If such data arrival rate is low, data is not kept stale into the cache but a poll() timeout makes the wheel spin. (b) SQL plugins main loop has been purposedly kept sufficiently fast thanks to no direct interaction with the RDBMS: it only gets data, computes modulo and handles both cache and queries queue. (c) cache has been thought to exploit a kind of temporal locality in internet flows. A picture follows: |====> [ cache ] ===| pipe | | ======> [ pmacctd/SQL plugin ] =====|====> [ cache ] ===|=============================| DB |======> | | | | |====> [ cache ] ===| | |=======> [ fallback mechanisms ] Now, let's keep an eye on how aggregates are structured on the DB side. Data is simply organized in flat tuples, without any external references. After being not full convinced about better normalized solutions aimed to satifsy an abstract concept of flexibility, we've (and here come into play the load of mails exchanged with Wim Kerkhoff) found that simple means faster. And to let the wheel spin quickly is a key achievement, because pmacct needs not only to insert new records but also update existing ones, putting under heavy pressure RDBMS when placed in busy network environments and an high number of primitives are required. Now a pair of concluding practical notes: (a) default SQL table and its primary key are suitable for many normal usages, however unused fields will be filled by zeroes. We took this choice a long time ago to allow people to compile sources and quickly get involved into the game, without caring too much about SQL details (assumption: who is involved in network management, shoult not have necessarily to be also involved into SQL stuff). So, everyone with a busy network segment under his feets has to carefully tune the table himself to avoid performance constraints; 'sql_optimize_clauses' configuration key evaluates what primitives have been selected and avoids long 'WHERE' clauses in 'INSERT' and 'UPDATE' queries. This may involve the creation of auxiliar indexes to let the execution of 'UPDATE' queries to work smoothly. A custom table might be created, trading flexibility with disk space wasting. (b) when using timestamps to break down aggregates into timeframes ('sql_history' key), validity of data is connected not only to data itself but also to its timeframe; as stated before, aggregates are pushed into DB at regular intervals ('sql_refresh_time' key). Connecting these two elements (refresh time and historical timeframe width) with a multiplicative factor helps in avoiding transient cache aliasing phenomena and in fully exploiting cache benefits. VIII. Recovery modes Forewords: this chapter is left here for historical reasons; note that recovery modes support has been largely discontinued. Resiliency and high-availability is left to clustering of the RDBMS environment itself or multiple plugins, within the same collector, running in parallel and writing to different backends. The concept of recovery mechanism is available only in SQL plugins and is aimed to avoid data loss by taking a corrective action if the DB suffers an outage or simply becomes unresponsive. Actually, two mechanisms are supported: aggregates may be either (1) pulled into a structured logfile for later processing by a player program or (2) written to a backup DB. While the latter method is quite straightforward, let's spend few words about the logfile: things has been kept simple, so much care and responsibility for keeping aggregates meaningful is on users shoulders. A logfile is made of a (a) logfile header containing DB configuration parameters, a (b) template header which contains the description of record structure, followed by (c) records dumped by the plugin. When appending new aggregates to a logfile, if the file already exists, just two brief safety checks are made against actual parameters: (1) the magic number into the logfile header is checked to ensure we are not about to write to wrong files and (2) number of record fields and their total size are checked to be moderately sure we are not about to write logfile which template doesn't precisely reflect our records. If multiple SQL plugins are running, each one should have its own logfile, moreover, when upgrading from a previous version it's good rule to not continue writing to an old logfile. A final remark about logfiles: their maximum allowed size is 2Gb, this is because seems actually there is not a standard way to guarantee 'large files' to be read. Once the maximum size is reached, data will not start to get lost, the 'old' logfile is rotated and an INFO message is trapped instead: a small integer is added at the end of the filename (suppose the logfile is 'pmacct-recovery.dat', it is rotated as 'pmacct-recovery.dat.1', etc.) and a new logfile is started. The health of SQL server is checked everytime aggregates are purged into it. If the database becomes unresponsive a 'recovery' flag is raised. This flag remains valid, with no further checks, for the entire purging event. If transactions are being involved (e.g., PostgreSQL), an additional reprocess flag signals the need to not assume previous, already processed, elements have been successfully written to DB but recover them also. Player tools are available, 'pmmyplay' and 'pmpgplay'; they currently don't contain any advanced auto-process feature: both them extract needed informations (where to connect, which username to use, etc.) from the logfile header - though, some commandline parameters may be used to override them; players read each record basing over the template header ensuring that even in the case internal records structure has changed, they are still readable (that is, the template has backward compatibility effects but if new fields are added over the time, old players will not be able to handle them). While playing the entire logfile or even a part of it, database failures are detected and signalled. A final statistics screen summarizes what has been successfully written into the DB; this aims to help reprocess the logfile at a later stage if something goes wrong once again. IX. pmacctd flows counter implementation Let's take the definition of IP flows from RFC3954, titled 'Cisco Systems NetFlow Services Export Version 9': an IP flow is defined as a set of IP packets passing an observation point in the network during a certain time interval. All packets that belong to a particular flow have a set of common properties derived both from the data contained in the packet and from the packet treatment at the observation point. Packets belonging to a specific flow also sport a very high temporal locality. While the handmade IP flows implementation in pmacctd mantains the fore-mentioned properties, it behaves quite differently when compared with NetFlow. In fact, the typical NetFlow implementation accumulates packets belonging to the same flow into a single memory object; when it comes to expire (because either the flow hits a timeout or an intercepted quit message - ie. TCP FIN/RST -) it is released and pushed into a NetFlow packet which is in turn sent to the collector. On the contrary, pmacctd does not accumulate; each packet is looked up against the flow buffer - a memory structure for active flows bookeping -: if it belongs to an already active flows its 'new flow' flag is deactivated (0); otherwise it's activated (1). While the above method is savy in terms of resource consumption, it could have some side-effects: for example it causes an entry to have a flow value '0' after either a reset of the backend memory structure (ie. pmacct -e, pmacct ... -r, etc.) or the beginning of a new timeframe when historical accounting is enabled (ie. print plugin, 'sql_history', etc.). X. Classifier and connection tracking engines pmacct 0.10.0 sees the introduction of new packet/stream classification and connection tracking features in the pmacctd daemon. Firstly, let's give a look to the global picture; then how they work: ----[ pmacctd loop ]------------------------------------------------------------- | [ regular ] | | [ expression ] | | ___[ patterns ] | | / / | | / ______/ | | | / | | | / | | [ fragment ] [ flow ] [ flow ] [ connection ] | | ... ==>[ handling ]==>[ handling ]==>[ classification ]==>[ tracking ]==> ... | | [ engine ] [ engine ] [ engine ] [ engine ] | | | \ | | | \___ | | \ \ | | \ [ shared ] | | --[ object ] | | [ pattern ] | --------------------------------------------------------------------------------- As the above picture shows, the classification engine is hooked to the flow handling engine. In fact, being able to successfully classify single packets means we can mark accordingly the whole bi-directional flow (referred also as stream) they belong to. The flow engine determines whether a flow is either newly established or terminated, sets its timeouts per protocol and state and handles timestamps. The classification engine coordinates the efforts to classify the packets by setting a maximum number of classification tentatives, handling bytes/packets accumulators for (still) unknown flows and attaching connection tracking modules whenever required. In case of successful classification, accumulators are released and sent to the active plugins, which, in turn, whenever possible (ie. counters have not been cleared, sent to the DB, etc.) will move such quantities from the 'unknown' class to the newly determined one. A connection tracking module might be assigned to certain classified streams if they belong to a protocol which is known to be based over a control channel (ie. FTP, RTSP, SIP, H.323, etc.). However, some protocols (ie. MSN messenger) spawn data channels that can still be distinguished because of some regular patterns into the payload; in such cases a classificator exists rather than a tracking module. Connection tracking modules are C routines statically compiled into the collector code that hint IP address/port couples for upcoming data streams as signalled by one of the parties into the control channel; such information fragments are then meant to classify the new data streams; classification patterns are either regular expressions (RE) or pluggable shared objects (SO, written in C), both loaded at runtime. In this context, 'snaplen' directive, which specifies the maximum number of bytes to capture for each packet, has key importance. In fact, some protocols (mostly text-based eg. RTSP, SIP, etc.) benefit of extra bytes because they give more chances to identify new data streams spawned by by the control channel. But it must be also noted that capturing larger packet portion require more system resources. Thus, the right value need to be traded-off. By enabling classification, values under 200 bytes are often meaningless. 500-750 bytes should be enough even for text-based protocols. XI. Jumps and flow diversions in Pre-Tagging infrastructure Pre-Tagging infrastructure allows to flexibly mark either packets or flows collected from the network. It's basically a chain of rules: the first matching rule wins (ie. tags collected data). This approach is effective to mantain a 1-to-1 relationship between data and tags; but this is somewhat limiting in some scenarios, ie. the need to account internal data to both the sender and the receiver: this time you would need a 1-to-2 relationship. In this context, being able to handle 1-to-2 relationships becomes a requirement when sampling comes into play as any single sample is required to be assigned to both parties in order for the algorithms to work correctly. Relationships 1-to-1 are precisely the aim for jeq, stack and return keys. XII. BGP daemon thread dimensioning Memory structure of the BGP daemon thread can be broken down in three modules: IP prefixes, BGP information and BGP attributes. Up to version 0.12.3 the multi-RIB nature of the BGP daemon was achieved separating IP prefixes and BGP information on a per active BGP peer basis, while BGP attributes were shared among all the peers. From a logical point these structures were relating each other as follows: IP prefix -(1:1)-> BGP information -(N:1)-> BGP attributes. Version 0.12.4 sees introduction of a memory model by which also IP prefixes are being shared among the BGP peers - leading to consistent memory savings whenever multiple BGP peers export full tables due to the almost total overlap of information. In this new model, structures are relating each other as follows: IP prefix -(1:N)-> BGP information -(N:1)-> BGP attributes. The longest match nature of IP lookups required to raise BGP peer awareness of the lookup algorithm in order to fully support the newly established 1:N relation. Following are some calculations that can ease memory dimensioning in order to support a certain number of peers, num(B), each exporting the same full-routing table, T, which consists of num(P) number of prefixes. The shared base of BGP attributes, A, is estimated in roughly 200MB. The BGP information base, I, is also calculated. sz(P) = 16 (size of an IP prefix) sz(Tn) = 48 (size of routing table node) sz(In) = 32 (size of BGP information base node) sz(THn) = 8 (size of routing table hash node) T = num(P) * (sz(Tn) + sz(P) + (sz(THn) * bgp_table_peer_buckets)) I = num(B) * num(P) * sz(In) RIB = T + I + A Sizes are based on worse-case scenario, ie. 64-bit executable with support for IPv6 compiled in. num(P) is a way to simplify calculations while retaining good accuracy; whereas higher precision can be achieved by using union(P), which is the union of all prefixes exported by all BGP peers. bgp_table_peer_buckets is set by default to 13 and is adviceable to keep its value to 1/10 of the expected number of BGP peers; increasing such ratio improves lookup performances at the expense of more sustained memory usage. As an example, let's imagine a scenario where 500 BGP peers export the same 500K IPv4 routes and 50K IPv6 routes and bgp_table_peer_buckets is set to a value of 50: T = (500K+50K) * (48 + 16 + (8 * 50)) = 256MB I = 500 * (500K+50K) * 32 = 8.8GB A = ~200MB RIB = 256MB + 8.8GB + 200MB = ~9.26GB Still, this example assumes a 64-bit executable. For an educated guess on how 32-bit executables would look like, it's sufficient to divide by half output of the sz() functions presented above. pmacct-0.14.0/docs/SIGNALS0000644000175000017500000000626711652204561014041 0ustar paolopaoloSIGNALS: Here follows a list of supported signals and their meaning; remember that pmacct core says goodbye when its last child dies or is terminated. pmacctd/nfacctd/sfacctd Core process: SIGCHLD: used to handle gracefully his loved child processes; SIGHUP: reopens the logging infrastructure. Works with both syslog and logfiles SIGUSR1: returns various statistics via either console or syslog; the syslog level reserved for such purpose is NOTICE; the facility is selected through configuration (ie key 'syslog'). It works for all pmacctd/nfacctd/sfacctd; SIGUSR2: if 'refresh_maps' configuration directive is enabled it causes Core Process-based maps to be reloaded (ie. Pre-Tagging, BGP source peer ASN, NetFlow/sFlow agent to BGP peer, BGP MD5, etc.); SIGINT: ignored if the daemon is started in background; otherwise the signal is propagated to each running plugin (which is in turn gracefully terminated); SIGTERM: not handled (which means it follows the default behaviour for the OS) if the daemon is started in background; else it works like SIGINT; SIGPIPE: ignored; MySQL plugin process: SIGPIPE: ignored; SIGCHLD: ignored; SIGHUP: inherited by Core Process; SIGUSR1: ignored; SIGUSR2: if 'refresh_maps' configuration directive is enabled it causes the Ports and Networks maps to be reloaded; SIGINT: causes the process to exit gracefully; PGSQL plugin process: SIGPIPE: ignored; SIGCHLD: ignored; SIGHUP: inherited by Core Process; SIGUSR1: ignored; SIGUSR2: if 'refresh_maps' configuration directive is enabled it causes the Ports and Networks maps to be reloaded; SIGINT: causes the process to exit gracefully; In-memory table process: SIGPIPE: ignored; SIGCHLD: ignored; SIGHUP: inherited by Core Process; SIGUSR1: ignored; SIGUSR2: if 'refresh_maps' configuration directive is enabled it causes the Ports and Networks maps to be reloaded; SIGINT: causes the process to exit gracefully; Print process: SIGPIPE: ignored; SIGCHLD: ignored; SIGHUP: inherited by Core Process; SIGUSR1: ignored; SIGUSR2: if 'refresh_maps' configuration directive is enabled it causes the Ports and Networks maps to be reloaded; SIGINT: causes the process to exit gracefully; NetFlow probe process: SIGPIPE: ignored; SIGCHLD: ignored; SIGHUP: inherited by Core Process; SIGUSR1: ignored; SIGUSR2: if 'refresh_maps' configuration directive is enabled it causes the Ports and Networks maps to be reloaded; SIGINT: causes the process to exit gracefully; sFlow probe process: SIGPIPE: ignored; SIGCHLD: ignored; SIGHUP: inherited by Core Process; SIGUSR1: ignored; SIGUSR2: if 'refresh_maps' configuration directive is enabled it causes the Ports and Networks maps to be reloaded; SIGINT: causes the process to exit gracefully; Tee process: SIGPIPE: ignored; SIGCHLD: ignored; SIGHUP: inherited by Core Process; SIGUSR1: ignored; SIGUSR2: ignored; SIGINT: causes the process to exit gracefully; pmacct-0.14.0/docs/TRIGGER_VARS0000644000175000017500000001176310530072467014676 0ustar paolopaoloINTRODUCTION Recently (0.7) an executable triggering mechanism has been added to both SQL plugins (sql_trigger_exec). Such executables may either be spawned each time a cache purging event occurs or at arbitrary time intervals (specified via sql_trigger_time). Because the triggering mechanism is hooked on top of 'lazy deadlines' it should absolutely not be preferred to run tasks strictly connected to timing issues (use crontab instead). The concept of lazy deadlines has been introduced a while ago to avoid the wide use of signals for time handling with the purpose of keep it light while preserving its correctness. Version 0.7.8 sees the introduction of a few status informations passed to the trigger in the form of environment variables. The list of supported variables follows: VAR: $SQL_DB DESC: The DB name currently in use by the plugin. VAR: $SQL_TABLE DESC: The SQL table name currently in use by the plugin. VAR: $EFFECTIVE_SQL_TABLE DESC: The SQL table name currently in use by the plugin. It is defined only whether the 'sql_table' value contains any variable. Variable values are substitute here by their actual values. VAR: $SQL_HOST DESC: Hostname (if any) to which the plugin is connecting to contact the SQL server. VAR: $SQL_USER DESC: Username currently in use while authenticating to the SQL server. VAR: $SQL_REFRESH_TIME DESC: The time interval at which data is currently purged from the cache into the DB. VAR: $SAMPLING_RATE DESC: The ratio of packet to be sampled (1 out of N). It is defined only if packet sampling is actually enabled via 'sampling_rate' (see CONFIG-KEYS for further details about the key). VAR: $SQL_RECOVERY_LOGFILE DESC: Full pathname to the logfile used when recovery mode is enabled. It is defined only if 'sql_recovery_logfile' (see CONFIG-KEYS for further details about the key) is actually defined. VAR: $SQL_RECOVERY_BACKUP_HOST DESC: Hostname which is contacted as a backup SQL server. It is defined only if 'sql_recovery_backup_host' (see CONFIG-KEYS for further details about the key) is actually defined. VAR: $TOTAL_ELEM_NUMBER DESC: Returns the total number of elements on the queue during the last cache-to-DB purging event. Elements may be subsequently filtered out and, then, are encapsulated in SQL queries. It is defined only if 'sql_trigger_time' is NOT defined (this causes the trigger to be launched each time the purging event occurs). VAR: $EFFECTIVE_ELEM_NUMBER DESC: Returns the effective number of elements (that is, excluding those filtered out) on the queue being encapsulated in SQL queries during the last cache-to-DB purging event. It is defined only if 'sql_trigger_time' is NOT defined (this causes the trigger to be launched each time the purging event occurs). VAR: $INSERT_QUERIES_NUMBER DESC: Returns the number of elements being successfully incapsulated into INSERT queries during the last cache-to-DB purging event. It is defined only if 'sql_trigger_time' is NOT defined (this causes the trigger to be launched each time the purging event occurs). VAR: $UPDATE_QUERIES_NUMBER DESC: Returns the number of elements being successfully incapsulated into UPDATE queries during the last cache-to-DB purging event. It is defined only if 'sql_trigger_time' is NOT defined (this causes the trigger to be launched each time the purging event occurs). VAR: $ELAPSED_TIME DESC: Returns the number of seconds the last cache-to-DB purging event took to complete. It is defined only if 'sql_trigger_time' is NOT defined (this causes the trigger to be launched each time the purging event occurs). VAR: $SQL_HISTORY_BASETIME DESC: Returns the basetime of the current timeslot, if 'sql_history' (see CONFIG-KEYS for further details about the key) is defined. It is the same value being inserted into 'stamp_inserted' field and is expressed as unixtime (seconds since Epoch). It is defined only if 'sql_trigger_time' is NOT defined (this causes the trigger to be launched each time the purging event occurs). VAR: $SQL_HISTORY_TIMESLOT DESC: Returns the current timeslot width (in seconds), if 'sql_history' (see CONFIG-KEYS for further details about the key) is defined. Note that this value may change (it's recomputed) if monthly timeslot are in use. It is defined only if 'sql_trigger_time' is NOT defined (this causes the trigger to be launched each time the purging event occurs). VAR: $SQL_MAX_WRITERS DESC: Returns the maximum number of concurrent writer processes allowed (see CONFIG-KEYS for further details about 'sql_max_writers'). VAR: $SQL_ACTIVE_WRITERS DESC: Returns the active number of concurrent writer processes in place (see CONFIG-KEYS for further details about 'sql_max_writers'). If used in conjunction with $SQL_MAX_WRITERS, it can prevent hitting the upper limit of writers by taking corrective actions or firing proper notifications. pmacct-0.14.0/docs/PLUGINS0000644000175000017500000000402511367570725014063 0ustar paolopaoloPMACCTD PLUGIN WRITING HOW-TO SHORT OVERVIEW the pmacct plugin architecture is thought to allow people that need their own backend to implement it without knowing too much of core collectors functionalities and independently by other plugins. Below are listed a few steps to hook your plugin in pmacct; pmacct is also extremely open to new ideas, so if you wish to contribute your work, you are the most welcome. - minor hacks to configure.in script following the example of what has been done there for mysql plugin; same goes for requirements like paths to headers or libraries. By making use of the PLUGINS variable, it will not be required to touch the "src/Makefile.am" script. Definitions in configure.in (ie. AC_DEFINE(WITH_MYSQL, 1)) whose existence can be checked via compiler preprocessor is an easy way to propagate user configuration choices at compilation time. - [OPTIONAL] If the plugin needs to take configurable values, this can be achieved by defining configuration handlers (pmacct-data.h, cfg_handlers.h, cfg_handlers.c) and declaring config variables to deliver configured values to the plugin (cfg.h, struct configuration). If command-line parameters are also required, some getopt() related code needs to be dealt with (pmacct-defines.h and ie. pmacctd.c, nfacctd.c, sfacctd.c and uacctd.c). - Define the new plugin in pmacct; this can be done by adding an entry to the plugin_types_list[] array (pmacct-data.h). An entry consists of two fields: an id string and a pointer to a function to be called. The first is the string which will be used to call the plugin from within the configuration or command-line. The second is effectively the entry point to the plugin. - Develop the plugin code. One of the existing plugins can be used as reference for popping data out of the circular buffer. Data structures for parsing such data are defined in network.h file. The basic layout for the main plugin loop can be grasped in the print_plugin.c file by looking at content of the "for (;;)" loop. pmacct-0.14.0/Makefile.am0000644000175000017500000000001610556243724014112 0ustar paolopaoloSUBDIRS = src pmacct-0.14.0/sql/0000755000175000017500000000000011741267746012667 5ustar paolopaolopmacct-0.14.0/sql/README.mask0000640000175000017500000000200111344241421014443 0ustar paolopaoloThis document doesn't replace documentation relevant to the database software you are using, ie. README.mysql, README.pgsql or README.sqlite3. The 'mask_src' and 'mask_dst' fields. Such fields are being introduced to support source and destination IP prefix masks. Values are grasped as configured in [nf|sf|pm|u]acctd_net directive: NetFlow/sFlow protocols, BGP, Network files (networks_file) or static (networks_mask) being valid data sources. The guidelines below (typically in MySQL format) are to add such primitives to the SQL schema: * mask_src field: - "mask_src INT(1) UNSIGNED NOT NULL," to declare the field itself - "PRIMARY KEY (..., mask_src, ...)" to put it in the primary key * mask_dst field: - "mask_dst INT(1) UNSIGNED NOT NULL," to declare the field itself - "PRIMARY KEY (..., mask_dst, ...)" to put it in the primary key The primitive is not declared as part of any default table version; yet will not fail version checks which are enabled when 'sql_optimize_clauses' feature is disabled. pmacct-0.14.0/sql/pmacct-create-table_v5.pgsql0000755000175000017500000000725011227653261020143 0ustar paolopaolo-- -- # su - postgres (or whatever your database runs as ... usually postgres) -- $ psql -d pmacct -f pmacct-create-table_v5.pgsql -- -- Tables DROP TABLE acct_uni_v5; CREATE TABLE acct_uni_v5 ( agent_id BIGINT NOT NULL DEFAULT 0, class_id CHAR(16) NOT NULL DEFAULT ' ', mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_uni_v5_pk PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE acct_v5; CREATE TABLE acct_v5 ( agent_id BIGINT NOT NULL DEFAULT 0, class_id CHAR(16) NOT NULL DEFAULT ' ', mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src inet NOT NULL DEFAULT '0.0.0.0', ip_dst inet NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_v5_pk PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE acct_as_v5; CREATE TABLE acct_as_v5 ( agent_id BIGINT NOT NULL DEFAULT 0, class_id CHAR(16) NOT NULL DEFAULT ' ', mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src INT NOT NULL DEFAULT 0, ip_dst INT NOT NULL DEFAULT 0, port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_as_v5_pk PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE proto; CREATE TABLE proto ( num SMALLINT NOT NULL, description CHAR(20), CONSTRAINT proto_pk PRIMARY KEY (num) ); COPY proto FROM stdin USING DELIMITERS ','; 0,ip 1,icmp 2,igmp 3,ggp 4,ipencap 5,st 6,tcp 8,egp 9,igp 17,udp 18,mux 27,rdp 29,iso-tp4 30,netblt 37,ddp 39,idpr-cmtp 41,ipv6 43,ipv6-route 44,ipv6-frag 46,rsvp 47,gre 50,ipv6-crypt 51,ipv6-auth 55,mobile 56,tlsp 58,ipv6-icmp 59,ipv6-nonxt 60,ipv6-opts 80,iso-ip 83,vines 88,eigrp 89,ospf 90,sprite-rpc 93,ax-25 94,ipip 98,encap 102,pnni 108,IPcomp 111,ipx-in-ip 112,vrrp 115,l2tp 124,isis 132,sctp 133,fc \. -- Perms GRANT SELECT, INSERT, UPDATE, DELETE ON acct_uni_v5 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON acct_v5 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON acct_as_v5 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON proto TO pmacct; pmacct-0.14.0/sql/pmacct-create-db_v3.mysql0000755000175000017500000000125511216727662017462 0ustar paolopaolodrop database if exists pmacct; create database pmacct; use pmacct; drop table if exists acct_v3; create table acct_v3 ( agent_id INT(4) UNSIGNED NOT NULL, mac_src CHAR(17) NOT NULL, mac_dst CHAR(17) NOT NULL, vlan INT(2) UNSIGNED NOT NULL, ip_src CHAR(15) NOT NULL, ip_dst CHAR(15) NOT NULL, src_port INT(2) UNSIGNED NOT NULL, dst_port INT(2) UNSIGNED NOT NULL, ip_proto CHAR(6) NOT NULL, tos INT(4) UNSIGNED NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, src_port, dst_port, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/README.IPv60000644000175000017500000000273411271032410014310 0ustar paolopaoloThis document doesn't replace documentation relevant to the database software you are using, ie. README.mysql, README.pgsql or README.sqlite3. MySQL and SQLite. MySQL tables define fields containing IP addresses as 15-bytes CHAR strings. Fields affected are, for example,'ip_src', 'ip_dst', 'peer_ip_src', 'peer_ip_dst' which are large enough to hold an IPv4 address but not IPv6. As variable-length strings are not an option for the sake of performance, this is in order to avoid penalties derived by the use of longer strings to people not interested (yet) to be IPv6-ready. To store IPv6 addresses into such fields, simply manually edit the SQL schema and define them as 45-bytes long CHAR strings (for example: 'ip_src CHAR(45) NOT NULL'). PostgreSQL. Typed tables. 'inet' network data type is able to store IPv6 addresses starting from PostgreSQL 7.4.x version. If you need to use IPv6 addresses, please upgrade to that version or use Unified tables. Unified tables. Into PostgreSQL Unified tables ('acct_uni', 'acct_uni_v2'), 'ip_src' and 'ip_dst' fields are both defined as 15-bytes character strings. They are large enough to hold an IPv4 address but not IPv6. This is in order to avoid penalties derived by the use of longer strings to people not interested to be IPv6-ready. To store IPv6 addresses into such tables, simply declare 'ip_src' and 'ip_dst' fields as 45-bytes long character strings (for example: 'ip_src CHAR(45) NOT NULL'). Unified PostgreSQL tables are now deprecated. pmacct-0.14.0/sql/README.sqlite30000644000175000017500000001433411712352254015122 0ustar paolopaoloTo create the database and the table you have to execute the following scripts. Remember that SQLite - unlike MySQL and PostgreSQL - does not endorse concepts of authentication (username and password couple) and permissions which are embedded into the filesystem instead. The database filename '/tmp/pmacct.db' is just a trivial example: you are free to build the database wherever fits better for you on the system (e.g., to follow a partitioning scheme, impose specific permissions, etc.). - To create v1 tables: * sqlite3 /tmp/pmacct.db < pmacct-create-table.sqlite3 - To use v1 tables: * data will be available in 'acct' table of 'pmacct' DB. * Add 'sql_table_version: 1' line to your configuration. Similarly, v2 to v7 tables: - To create v2 tables: * sqlite3 /tmp/pmacct.db < pmacct-create-table_v2.sqlite3 - To use v2 tables: * data will be available in 'acct_v2' table of 'pmacct' DB. * Add 'sql_table_version: 2' line to your configuration. [ ... ] - To create v7 tables: * sqlite3 /tmp/pmacct.db < pmacct-create-table_v7.sqlite3 - To use v7 tables: * data will be available in 'acct_v7' table of 'pmacct' DB. * Add 'sql_table_version: 7' line to your configuration. Similarly, BGP tables: - To create BGP v1 tables: * sqlite3 /tmp/pmacct.db < pmacct-create-table_bgp_v1.sqlite3 - To use BGP v1 tables: * data will be available in 'acct_bgp' table of 'pmacct' DB. * Add 'sql_table_version: 1' line to your configuration. * Add 'sql_table_type: bgp' line to your configuration. - To understand difference between the various table versions: * Do you need any of the BGP primitives ? Then look the next section. * Do you need TCP flags ? Then you have to use v7. * Do you need both IP addresses and AS numbers in the same table ? Then you have to use v6. * Do you need packet classification ? Then you have to use v5. * Do you need flows (other than packets) accounting ? Then you have to use v4. * Do you need ToS/DSCP field (QoS) accounting ? Then you have to use v3. * Do you need agent ID for distributed accounting and packet tagging ? Then you have to use v2. * Do you need VLAN traffic accounting ? Then you have to use v2. * If all of the above points sound useless, then use v1. - To understand difference between the various BGP table versions: * Only BGP table v1 is currently available. - Aggregation primitives to SQL schema mapping: Aggregation primitive => SQL table field * tag => agent_id (INT(8) NOT NULL DEFAULT 0) * tag2 => agent_id2 (INT(8) NOT NULL DEFAULT 0, see README.agent_id2) * src_as => as_src (INT(8) NOT NULL DEFAULT 0) * dst_as => as_dst (INT(8) NOT NULL DEFAULT 0) * peer_src_as => peer_as_src (INT(8) NOT NULL DEFAULT 0) * peer_dst_as => peer_as_dst (INT(8) NOT NULL DEFAULT 0) * peer_src_ip => peer_ip_src (CHAR(15) NOT NULL DEFAULT '0.0.0.0', see README.IPv6) - peer_src_ip => peer_ip_src (INT(8) DEFAULT 0, if sql_num_hosts: true) * peer_dst_ip => peer_ip_dst (CHAR(15) NOT NULL DEFAULT '0.0.0.0', see README.IPv6) - peer_dst_ip => peer_ip_dst (INT(8) DEFAULT 0, if sql_num_hosts: true) * mpls_vpn_rd => mpls_vpn_rd (CHAR(18) NOT NULL DEFAULT ' ') * std_comm => comms (CHAR(24) NOT NULL DEFAULT ' ') * ext_comm => comms (CHAR(24) NOT NULL DEFAULT ' ') * as_path => as_path (CHAR(21) NOT NULL DEFAULT ' ') * local_pref => local_pref (INT(8) NOT NULL DEFAULT 0) * med => med (INT(8) NOT NULL DEFAULT 0) * src_std_comm => comms_src (CHAR(24) NOT NULL DEFAULT ' ') * src_ext_comm => comms_src (CHAR(24) NOT NULL DEFAULT ' ') * src_as_path => as_path_src (CHAR(21) NOT NULL DEFAULT ' ') * src_local_pref => local_pref_src (INT(8) NOT NULL DEFAULT 0) * src_med => med_src (INT(8) NOT NULL DEFAULT 0) * in_iface => iface_in (INT(8) NOT NULL DEFAULT 0, see README.iface) * out_iface => iface_out (INT(8) NOT NULL DEFAULT 0, see README.iface) * src_mask => mask_src (INT(2) NOT NULL DEFAULT 0, see README.mask) * dst_mask => mask_dst (INT(2) NOT NULL DEFAULT 0, see README.mask) * cos => cos (INT(2) NOT NULL DEFAULT 0, see README.cos) * etype => etype (INT(2) NOT NULL DEFAULT 0, see README.etype) * class => class_id (CHAR(16) NOT NOT NULL DEFAULT ' ') * src_mac => mac_src (CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0') * dst_mac => mac_dst (CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0') * vlan => vlan (INT(4) NOT NULL DEFAULT 0) * src_as => as_src (INT(8) NOT NULL DEFAULT 0) * dst_as => as_dst (INT(8) NOT NULL DEFAULT 0) * src_host => ip_src (CHAR(15) NOT NULL DEFAULT '0.0.0.0', see README.IPv6) - src_host => ip_src (INT(8) DEFAULT 0, if sql_num_hosts: true) * dst_host => ip_dst (CHAR(15) NOT NULL DEFAULT '0.0.0.0', see README.IPv6) - dst_host => ip_dst (INT(8) DEFAULT 0, if sql_num_hosts: true) * src_net => ip_src (CHAR(15) NOT NULL DEFAULT '0.0.0.0', see README.IPv6) - src_net => ip_src (INT(8) DEFAULT 0, if sql_num_hosts: true) * dst_net => ip_dst (CHAR(15) NOT NULL DEFAULT '0.0.0.0', see README.IPv6) - dst_net => ip_dst (INT(8) DEFAULT 0, if sql_num_hosts: true) * src_port => src_port (INT(4) NOT NULL DEFAULT 0) - src_port => port_src (INT(4) NOT NULL DEFAULT 0, if sql_table_version: 8) * dst_port => dst_port (INT(4) NOT NULL DEFAULT 0) - dst_port => port_dst (INT(4) NOT NULL DEFAULT 0, if sql_table_version: 8) * tcpflags => tcp_flags (INT(2) NOT NULL DEFAULT 0) * proto => ip_proto (CHAR(6) NOT NULL DEFAULT ' ') - proto => ip_proto (INT(2) DEFAULT 0, if sql_num_protos: true) * tos => tos (INT(4) NOT NULL DEFAULT 0) - Counters and time reference need always to be defined as part of the SQL schema: * packets (INT(4) UNSIGNED NOT NULL) * bytes (INT(8) UNSIGNED NOT NULL) * stamp_inserted (DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', enabled by sql_history) * stamp_updated (DATETIME, enabled by sql_history) NOTE: mind to specify EVERYTIME which SQL table version you intend to adhere to by using either of the following rules: When using commandline options: * -v [ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ] When using configuration directives: * sql_table_version: [ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ] * sql_table_type: [ bgp ] NOTE: specifying a non-documented SQL table profile will result in an non-determined behaviour. Unless this will create crashes to the application, such situations will not be supported. pmacct-0.14.0/sql/pmacct-create-table_v7.sqlite30000755000175000017500000000164411227653261020404 0ustar paolopaoloDROP TABLE acct_v7; CREATE TABLE acct_v7 ( agent_id INT(8) NOT NULL DEFAULT 0, class_id CHAR(16) NOT NULL DEFAULT ' ', mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT(4) NOT NULL DEFAULT 0, as_src INT(8) NOT NULL DEFAULT 0, as_dst INT(8) NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', src_port INT(4) NOT NULL DEFAULT 0, dst_port INT(4) NOT NULL DEFAULT 0, tcp_flags INT(4) NOT NULL DEFAULT 0, ip_proto CHAR(6) NOT NULL DEFAULT 0, tos INT(4) NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', stamp_updated DATETIME, PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, as_src, as_dst, ip_src, ip_dst, src_port, dst_port, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/pmacct-create-db_v6.mysql0000755000175000017500000000150611216727662017464 0ustar paolopaolodrop database if exists pmacct; create database pmacct; use pmacct; drop table if exists acct_v6; create table acct_v6 ( agent_id INT(4) UNSIGNED NOT NULL, class_id CHAR(16) NOT NULL, mac_src CHAR(17) NOT NULL, mac_dst CHAR(17) NOT NULL, vlan INT(2) UNSIGNED NOT NULL, as_src INT(4) UNSIGNED NOT NULL, as_dst INT(4) UNSIGNED NOT NULL, ip_src CHAR(15) NOT NULL, ip_dst CHAR(15) NOT NULL, src_port INT(2) UNSIGNED NOT NULL, dst_port INT(2) UNSIGNED NOT NULL, ip_proto CHAR(6) NOT NULL, tos INT(4) UNSIGNED NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, flows INT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, as_src, as_dst, ip_src, ip_dst, src_port, dst_port, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/pmacct-create-table_v6.sqlite30000755000175000017500000000157611227653261020407 0ustar paolopaoloDROP TABLE acct_v6; CREATE TABLE acct_v6 ( agent_id INT(8) NOT NULL DEFAULT 0, class_id CHAR(16) NOT NULL DEFAULT ' ', mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT(4) NOT NULL DEFAULT 0, as_src INT(8) NOT NULL DEFAULT 0, as_dst INT(8) NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', src_port INT(4) NOT NULL DEFAULT 0, dst_port INT(4) NOT NULL DEFAULT 0, ip_proto CHAR(6) NOT NULL DEFAULT 0, tos INT(4) NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', stamp_updated DATETIME, PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, as_src, as_dst, ip_src, ip_dst, src_port, dst_port, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/pmacct-create-db_v1.mysql0000755000175000017500000000105610530072467017451 0ustar paolopaolodrop database if exists pmacct; create database pmacct; use pmacct; drop table if exists acct; create table acct ( mac_src CHAR(17) NOT NULL, mac_dst CHAR(17) NOT NULL, ip_src CHAR(15) NOT NULL, ip_dst CHAR(15) NOT NULL, src_port INT(2) UNSIGNED NOT NULL, dst_port INT(2) UNSIGNED NOT NULL, ip_proto CHAR(6) NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (mac_src, mac_dst, ip_src, ip_dst, src_port, dst_port, ip_proto, stamp_inserted) ); pmacct-0.14.0/sql/README.agent_id20000640000175000017500000000155711342063463015371 0ustar paolopaoloThis document doesn't replace documentation relevant to the database software you are using, ie. README.mysql, README.pgsql or README.sqlite3. The 'agent_id2' field. Such field is being introduced to better support traffic matrix scenarios, where it can be handy to have a tag to represent "qualities" of the source (say, agent_id) and an additional one to represent "qualities" of the destination (say, agent_id2). But of course this is only an example. The guidelines below (typically in MySQL format) are to add such primitive to the SQL schema: * agent_id2 field: - "agent_id2 INT(4) UNSIGNED NOT NULL," to declare the field itself - "PRIMARY KEY (..., agent_id2, ...)" to put it in the primary key The primitive is not declared as part of any default table version; yet will not fail version checks which are enabled when 'sql_optimize_clauses' feature is disabled. pmacct-0.14.0/sql/pmacct-create-table_v1.sqlite30000755000175000017500000000110510530072467020365 0ustar paolopaoloDROP TABLE acct; CREATE TABLE acct ( mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', src_port INT(4) NOT NULL DEFAULT 0, dst_port INT(4) NOT NULL DEFAULT 0, ip_proto CHAR(6) NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', stamp_updated DATETIME, PRIMARY KEY (mac_src, mac_dst, ip_src, ip_dst, src_port, dst_port, ip_proto, stamp_inserted) ); pmacct-0.14.0/sql/pmacct-create-table_v3.pgsql0000755000175000017500000000666211227653261020147 0ustar paolopaolo-- -- # su - postgres (or whatever your database runs as ... usually postgres) -- $ psql -d pmacct -f pmacct-create-table_v3.pgsql -- -- Tables DROP TABLE acct_uni_v3; CREATE TABLE acct_uni_v3 ( agent_id BIGINT NOT NULL DEFAULT 0, mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_uni_v3_pk PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE acct_v3; CREATE TABLE acct_v3 ( agent_id BIGINT NOT NULL DEFAULT 0, mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src inet NOT NULL DEFAULT '0.0.0.0', ip_dst inet NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_v3_pk PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE acct_as_v3; CREATE TABLE acct_as_v3 ( agent_id BIGINT NOT NULL DEFAULT 0, mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src INT NOT NULL DEFAULT 0, ip_dst INT NOT NULL DEFAULT 0, port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_as_v3_pk PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE proto; CREATE TABLE proto ( num SMALLINT NOT NULL, description CHAR(20), CONSTRAINT proto_pk PRIMARY KEY (num) ); COPY proto FROM stdin USING DELIMITERS ','; 0,ip 1,icmp 2,igmp 3,ggp 4,ipencap 5,st 6,tcp 8,egp 9,igp 17,udp 18,mux 27,rdp 29,iso-tp4 30,netblt 37,ddp 39,idpr-cmtp 41,ipv6 43,ipv6-route 44,ipv6-frag 46,rsvp 47,gre 50,ipv6-crypt 51,ipv6-auth 55,mobile 56,tlsp 58,ipv6-icmp 59,ipv6-nonxt 60,ipv6-opts 80,iso-ip 83,vines 88,eigrp 89,ospf 90,sprite-rpc 93,ax-25 94,ipip 98,encap 102,pnni 108,IPcomp 111,ipx-in-ip 112,vrrp 115,l2tp 124,isis 132,sctp 133,fc \. -- Perms GRANT SELECT, INSERT, UPDATE, DELETE ON acct_uni_v3 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON acct_v3 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON acct_as_v3 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON proto TO pmacct; pmacct-0.14.0/sql/pmacct-create-db_bgp_v1.mysql0000750000175000017500000000142111216727662020276 0ustar paolopaolodrop database if exists pmacct; create database pmacct; use pmacct; drop table if exists acct_bgp; create table acct_bgp ( agent_id INT(4) UNSIGNED NOT NULL, as_src INT(4) UNSIGNED NOT NULL, as_dst INT(4) UNSIGNED NOT NULL, peer_as_src INT(4) UNSIGNED NOT NULL, peer_as_dst INT(4) UNSIGNED NOT NULL, peer_ip_src CHAR(15) NOT NULL, peer_ip_dst CHAR(15) NOT NULL, comms CHAR(24) NOT NULL, as_path CHAR(21) NOT NULL, local_pref INT(4) UNSIGNED NOT NULL, med INT(4) UNSIGNED NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (agent_id, as_src, as_dst, peer_as_src, peer_as_dst, peer_ip_src, peer_ip_dst, comms, as_path, local_pref, med, stamp_inserted) ); pmacct-0.14.0/sql/README.64bit0000644000175000017500000000145411271032410014452 0ustar paolopaoloThis document doesn't replace documentation relevant to the database software you are using, ie. README.mysql, README.pgsql or README.sqlite3. It is about the use of increased size byte/packet/flow counter fields with the aim of easily accomodate larger counters. Why counters are not defined so large by default ? Because they would require extra space - and maybe this is not a requirement. MySQL. There is nothing to do. Each MySQL schema already sports counters large enough. bytes, packets and flows fields are already defined as either INT or BIGINT. Which translates respectively in 10 and 20 bytes fields. PostgreSQL and SQLite. There is very little to do. bytes field is already declared BIGINT, thus does not need any change. packets and flows fields should be fixed and defined as BIGINT aswell. pmacct-0.14.0/sql/pmacct-create-table_bgp_v1.pgsql0000750000175000017500000000215311227116575020761 0ustar paolopaolo-- -- # su - postgres (or whatever your database runs as ... usually postgres) -- $ psql -d pmacct -f pmacct-create-table_bgp_v1.pgsql -- -- Tables DROP TABLE acct_bgp; CREATE TABLE acct_bgp ( agent_id BIGINT NOT NULL DEFAULT 0, as_src BIGINT NOT NULL DEFAULT 0, as_dst BIGINT NOT NULL DEFAULT 0, peer_as_src BIGINT NOT NULL DEFAULT 0, peer_as_dst BIGINT NOT NULL DEFAULT 0, peer_ip_src inet NOT NULL DEFAULT '0.0.0.0', peer_ip_dst inet NOT NULL DEFAULT '0.0.0.0', comms CHAR(24) NOT NULL DEFAULT ' ', as_path CHAR(21) NOT NULL DEFAULT ' ', local_pref BIGINT NOT NULL DEFAULT 0, med BIGINT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_bgp_pk PRIMARY KEY (agent_id, as_src, as_dst, peer_as_src, peer_as_dst, peer_ip_src, peer_ip_dst, comms, as_path, local_pref, med, stamp_inserted) ); -- Perms GRANT SELECT, INSERT, UPDATE, DELETE ON acct_bgp TO pmacct; pmacct-0.14.0/sql/pmacct-create-table_v1.pgsql0000755000175000017500000000601210530072467020131 0ustar paolopaolo-- -- # su - postgres (or whatever your database runs as ... usually postgres) -- $ psql -d pmacct -f pmacct-create-table.pgsql -- -- Tables DROP TABLE acct_uni; CREATE TABLE acct_uni ( mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP(0), stamp_updated timestamp without time zone, CONSTRAINT acct_uni_pk PRIMARY KEY (mac_src, mac_dst, ip_src, ip_dst, port_src, port_dst, ip_proto, stamp_inserted) ); DROP TABLE acct; CREATE TABLE acct ( mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', ip_src inet NOT NULL DEFAULT '0.0.0.0', ip_dst inet NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP(0), stamp_updated timestamp without time zone, CONSTRAINT acct_pk PRIMARY KEY (mac_src, mac_dst, ip_src, ip_dst, port_src, port_dst, ip_proto, stamp_inserted) ); DROP TABLE acct_as; CREATE TABLE acct_as ( mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', ip_src INT NOT NULL DEFAULT 0, ip_dst INT NOT NULL DEFAULT 0, port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP(0), stamp_updated timestamp without time zone, CONSTRAINT acct_as_pk PRIMARY KEY (mac_src, mac_dst, ip_src, ip_dst, port_src, port_dst, ip_proto, stamp_inserted) ); DROP TABLE proto; CREATE TABLE proto ( num SMALLINT NOT NULL, description CHAR(20), CONSTRAINT proto_pk PRIMARY KEY (num) ); COPY proto FROM stdin USING DELIMITERS ','; 0,ip 1,icmp 2,igmp 3,ggp 4,ipencap 5,st 6,tcp 8,egp 9,igp 17,udp 18,mux 27,rdp 29,iso-tp4 30,netblt 37,ddp 39,idpr-cmtp 41,ipv6 43,ipv6-route 44,ipv6-frag 46,rsvp 47,gre 50,ipv6-crypt 51,ipv6-auth 55,mobile 56,tlsp 58,ipv6-icmp 59,ipv6-nonxt 60,ipv6-opts 80,iso-ip 83,vines 88,eigrp 89,ospf 90,sprite-rpc 93,ax-25 94,ipip 98,encap 102,pnni 108,IPcomp 111,ipx-in-ip 112,vrrp 115,l2tp 124,isis 132,sctp 133,fc \. -- Perms GRANT SELECT, INSERT, UPDATE, DELETE ON acct_uni TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON acct TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON acct_as TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON proto TO pmacct; pmacct-0.14.0/sql/pmacct-create-db_v7.mysql0000755000175000017500000000155311216727662017467 0ustar paolopaolodrop database if exists pmacct; create database pmacct; use pmacct; drop table if exists acct_v7; create table acct_v7 ( agent_id INT(4) UNSIGNED NOT NULL, class_id CHAR(16) NOT NULL, mac_src CHAR(17) NOT NULL, mac_dst CHAR(17) NOT NULL, vlan INT(2) UNSIGNED NOT NULL, as_src INT(4) UNSIGNED NOT NULL, as_dst INT(4) UNSIGNED NOT NULL, ip_src CHAR(15) NOT NULL, ip_dst CHAR(15) NOT NULL, src_port INT(2) UNSIGNED NOT NULL, dst_port INT(2) UNSIGNED NOT NULL, tcp_flags INT(4) UNSIGNED NOT NULL, ip_proto CHAR(6) NOT NULL, tos INT(4) UNSIGNED NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, flows INT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, as_src, as_dst, ip_src, ip_dst, src_port, dst_port, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/pmacct-create-db_v2.mysql0000755000175000017500000000121011216727662017450 0ustar paolopaolodrop database if exists pmacct; create database pmacct; use pmacct; drop table if exists acct_v2; create table acct_v2 ( agent_id INT(4) UNSIGNED NOT NULL, mac_src CHAR(17) NOT NULL, mac_dst CHAR(17) NOT NULL, vlan INT(2) UNSIGNED NOT NULL, ip_src CHAR(15) NOT NULL, ip_dst CHAR(15) NOT NULL, src_port INT(2) UNSIGNED NOT NULL, dst_port INT(2) UNSIGNED NOT NULL, ip_proto CHAR(6) NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, src_port, dst_port, ip_proto, stamp_inserted) ); pmacct-0.14.0/sql/pmacct-grant-db.mysql0000755000175000017500000000014610530072467016712 0ustar paolopaologrant all privileges on pmacct.* to pmacct@localhost identified by 'arealsmartpwd' with grant option; pmacct-0.14.0/sql/pmacct-create-table_bgp_v1.sqlite30000750000175000017500000000155611227130645021217 0ustar paolopaoloDROP TABLE acct_bgp; CREATE TABLE acct_bgp ( agent_id INT(8) NOT NULL DEFAULT 0, as_src INT(8) NOT NULL DEFAULT 0, as_dst INT(8) NOT NULL DEFAULT 0, peer_as_src INT(8) NOT NULL DEFAULT 0, peer_as_dst INT(8) NOT NULL DEFAULT 0, peer_ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', peer_ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', comms CHAR(24) NOT NULL DEFAULT ' ', as_path CHAR(21) NOT NULL DEFAULT ' ', local_pref INT(8) NOT NULL DEFAULT 0, med INT(8) NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', stamp_updated DATETIME, PRIMARY KEY (agent_id, as_src, as_dst, peer_as_src, peer_as_dst, peer_ip_src, peer_ip_dst, comms, as_path, local_pref, med, stamp_inserted) ); pmacct-0.14.0/sql/README.etype0000640000175000017500000000121611737342526014663 0ustar paolopaoloThis document doesn't replace documentation relevant to the database software you are using, ie. README.mysql, README.pgsql or README.sqlite3. The 'etype' field. Such field is being introduced to support Ethertype field part of the Ethernet header. The guidelines below (typically in MySQL format) are to add such primitives to the SQL schema: * etype field: - "etype CHAR(5) NOT NULL," to declare the field itself - "PRIMARY KEY (..., etype, ...)" to put it in the primary key The primitive is not declared as part of any default table version; yet will not fail version checks which are enabled when 'sql_optimize_clauses' feature is disabled. pmacct-0.14.0/sql/README.cos0000640000175000017500000000116711403044665014317 0ustar paolopaoloThis document doesn't replace documentation relevant to the database software you are using, ie. README.mysql, README.pgsql or README.sqlite3. The 'cos' field. Such field is being introduced to support 802.1p priority bits. The guidelines below (typically in MySQL format) are to add such primitives to the SQL schema: * cos field: - "cos INT(1) UNSIGNED NOT NULL," to declare the field itself - "PRIMARY KEY (..., cos, ...)" to put it in the primary key The primitive is not declared as part of any default table version; yet will not fail version checks which are enabled when 'sql_optimize_clauses' feature is disabled. pmacct-0.14.0/sql/pmacct-create-db_v8.mysql0000750000175000017500000000155311453022224017444 0ustar paolopaolodrop database if exists pmacct; create database pmacct; use pmacct; drop table if exists acct_v8; create table acct_v8 ( agent_id INT(4) UNSIGNED NOT NULL, class_id CHAR(16) NOT NULL, mac_src CHAR(17) NOT NULL, mac_dst CHAR(17) NOT NULL, vlan INT(2) UNSIGNED NOT NULL, as_src INT(4) UNSIGNED NOT NULL, as_dst INT(4) UNSIGNED NOT NULL, ip_src CHAR(15) NOT NULL, ip_dst CHAR(15) NOT NULL, port_src INT(2) UNSIGNED NOT NULL, port_dst INT(2) UNSIGNED NOT NULL, tcp_flags INT(4) UNSIGNED NOT NULL, ip_proto CHAR(6) NOT NULL, tos INT(4) UNSIGNED NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, flows INT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, as_src, as_dst, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/README.iface0000640000175000017500000000162411342063463014577 0ustar paolopaoloThis document doesn't replace documentation relevant to the database software you are using, ie. README.mysql, README.pgsql or README.sqlite3. The 'iface_in' and 'iface_out' fields. Such fields are being introduced to natively support input/output interface indexes as received from sFlow, NetFlow or ULOG packet capturing framework. The guidelines below (typically in MySQL format) are to add such primitives to the SQL schema: * iface_in field: - "iface_in INT(4) UNSIGNED NOT NULL," to declare the field itself - "PRIMARY KEY (..., iface_in, ...)" to put it in the primary key * iface_out field: - "iface_out INT(4) UNSIGNED NOT NULL," to declare the field itself - "PRIMARY KEY (..., iface_out, ...)" to put it in the primary key The primitive is not declared as part of any default table version; yet will not fail version checks which are enabled when 'sql_optimize_clauses' feature is disabled. pmacct-0.14.0/sql/pmacct-create-db_v5.mysql0000755000175000017500000000136211216727662017463 0ustar paolopaolodrop database if exists pmacct; create database pmacct; use pmacct; drop table if exists acct_v5; create table acct_v5 ( agent_id INT(4) UNSIGNED NOT NULL, class_id CHAR(16) NOT NULL, mac_src CHAR(17) NOT NULL, mac_dst CHAR(17) NOT NULL, vlan INT(2) UNSIGNED NOT NULL, ip_src CHAR(15) NOT NULL, ip_dst CHAR(15) NOT NULL, src_port INT(2) UNSIGNED NOT NULL, dst_port INT(2) UNSIGNED NOT NULL, ip_proto CHAR(6) NOT NULL, tos INT(4) UNSIGNED NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, flows INT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, ip_src, ip_dst, src_port, dst_port, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/README.mysql0000644000175000017500000001342611712352254014704 0ustar paolopaoloTo create the database and grant default permission to pmacctd you have to execute the two scripts below, in the same order (assuming mysql command is in the path): - To create v1 tables: * mysql -u root -p < pmacct-create-db_v1.mysql * mysql -u root -p < pmacct-grant-db.mysql - To use v1 tables: * data will be available in 'acct' table of 'pmacct' DB. * Add 'sql_table_version: 1' line to your configuration. Similarly, v2 to v7 tables: - To create v2 tables: * mysql -u root -p < pmacct-create-db_v2.mysql * mysql -u root -p < pmacct-grant-db.mysql - To use v2 tables: * data will be available in 'acct_v2' table of 'pmacct' DB. * Add 'sql_table_version: 2' line to your configuration. [ ... ] - To create v7 tables: * mysql -u root -p < pmacct-create-db_v7.mysql * mysql -u root -p < pmacct-grant-db.mysql - To use v7 tables: * data will be available in 'acct_v7' table of 'pmacct' DB. * Add 'sql_table_version: 7' line to your configuration. Similarly, BGP tables: - To create BGP v1 tables: * mysql -u root -p < pmacct-create-db_bgp_v1.mysql * mysql -u root -p < pmacct-grant-db.mysql - To use BGP v1 tables: * data will be available in 'acct_bgp' table of 'pmacct' DB. * Add 'sql_table_version: 1' line to your configuration. * Add 'sql_table_type: bgp' line to your configuration. - To understand difference between the various table versions: * Do you need any of the BGP primitives ? Then look the next section. * Do you need TCP flags ? Then you have to use v7. * Do you need both IP addresses and AS numbers in the same table ? Then you have to use v6. * Do you need packet classification ? Then you have to use v5. * Do you need flows (other than packets) accounting ? Then you have to use v4. * Do you need ToS/DSCP field (QoS) accounting ? Then you have to use v3. * Do you need agent ID for distributed accounting and packet tagging ? Then you have to use v2. * Do you need VLAN traffic accounting ? Then you have to use v2. * If all of the above points sound useless, then use v1. - To understand difference between the various BGP table versions: * Only BGP table v1 is currently available. - Aggregation primitives to SQL schema mapping: Aggregation primitive => SQL table field * tag => agent_id (INT(4) UNSIGNED NOT NULL) * tag2 => agent_id2 (INT(4) UNSIGNED NOT NULL, see README.agent_id2) * src_as => as_src (INT(4) UNSIGNED NOT NULL) * dst_as => as_dst (INT(4) UNSIGNED NOT NULL) * peer_src_as => peer_as_src (INT(4) UNSIGNED NOT NULL) * peer_dst_as => peer_as_dst (INT(4) UNSIGNED NOT NULL) * peer_src_ip => peer_ip_src (CHAR(15) NOT NULL, see README.IPv6) - peer_src_ip => peer_ip_src (INT(4) UNSIGNED NOT NULL, if sql_num_hosts: true) * peer_dst_ip => peer_ip_dst (CHAR(15) NOT NULL, see README.IPv6) - peer_dst_ip => peer_ip_dst (INT(4) UNSIGNED NOT NULL, if sql_num_hosts: true) * mpls_vpn_rd => mpls_vpn_rd (CHAR(18) NOT NULL) * std_comm => comms (CHAR(24) NOT NULL) * ext_comm => comms (CHAR(24) NOT NULL) * as_path => as_path (CHAR(21) NOT NULL) * local_pref => local_pref (INT(4) UNSIGNED NOT NULL) * med => med (INT(4) UNSIGNED NOT NULL) * src_std_comm => comms_src (CHAR(24) NOT NULL) * src_ext_comm => comms_src (CHAR(24) NOT NULL) * src_as_path => as_path_src (CHAR(21) NOT NULL) * src_local_pref => local_pref_src (INT(4) UNSIGNED NOT NULL) * src_med => med_src (INT(4) UNSIGNED NOT NULL) * in_iface => iface_in (INT(4) UNSIGNED NOT NULL, see README.iface) * out_iface => iface_out (INT(4) UNSIGNED NOT NULL, see README.iface) * src_mask => mask_src (INT(1) UNSIGNED NOT NULL, see README.mask) * dst_mask => mask_dst (INT(1) UNSIGNED NOT NULL, see README.mask) * cos => cos (INT(1) UNSIGNED NOT NULL, see README.cos) * etype => etype (INT(2) UNSIGNED NOT NULL, see README.etype) * class => class_id (CHAR(16) NOT NULL) * src_mac => mac_src (CHAR(17) NOT NULL) * dst_mac => mac_dst (CHAR(17) NOT NULL) * vlan => vlan (INT(2) UNSIGNED NOT NULL) * src_as => as_src (INT(4) UNSIGNED NOT NULL) * dst_as => as_dst (INT(4) UNSIGNED NOT NULL) * src_host => ip_src (CHAR(15) NOT NULL, see README.IPv6) - src_host => ip_src (INT(4) UNSIGNED NOT NULL, if sql_num_hosts: true) * dst_host => ip_dst (CHAR(15) NOT NULL, see README.IPv6) - dst_host => ip_dst (INT(4) UNSIGNED NOT NULL, if sql_num_hosts: true) * src_net => ip_src (CHAR(15) NOT NULL, see README.IPv6) - src_net => ip_src (INT(4) UNSIGNED NOT NULL, if sql_num_hosts: true) * dst_net => ip_dst (CHAR(15) NOT NULL, see README.IPv6) - dst_net => ip_dst (INT(4) UNSIGNED NOT NULL, if sql_num_hosts: true) * src_port => src_port (INT(2) UNSIGNED NOT NULL) - src_port => port_src (INT(2) UNSIGNED NOT NULL, if sql_table_version: 8) * dst_port => dst_port (INT(2) UNSIGNED NOT NULL) - dst_port => port_dst (INT(2) UNSIGNED NOT NULL, if sql_table_version: 8) * tcpflags => tcp_flags (INT(4) UNSIGNED NOT NULL) * proto => ip_proto (CHAR(6) NOT NULL) - proto => ip_proto (INT(1) UNSIGNED NOT NULL, if sql_num_protos: true) * tos => tos (INT(4) UNSIGNED NOT NULL) - Counters and time reference need always to be defined as part of the SQL schema: * packets (INT UNSIGNED NOT NULL) * bytes (BIGINT UNSIGNED NOT NULL) * stamp_inserted (DATETIME NOT NULL, enabled by sql_history) * stamp_updated (DATETIME, enabled by sql_history) NOTE: mind to specify EVERYTIME which SQL table version you intend to adhere to by using either of the following rules: When using commandline options: * -v [ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ] When using configuration directives: * sql_table_version: [ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ] * sql_table_type: [ bgp ] NOTE: specifying a non-documented SQL table profile will result in an non-determined behaviour. Unless this will create crashes to the application, such situations will not be supported. pmacct-0.14.0/sql/pmacct-create-table_v2.sqlite30000755000175000017500000000124211227653261020371 0ustar paolopaoloDROP TABLE acct_v2; CREATE TABLE acct_v2 ( agent_id INT(8) NOT NULL DEFAULT 0, mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT(4) NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', src_port INT(4) NOT NULL DEFAULT 0, dst_port INT(4) NOT NULL DEFAULT 0, ip_proto CHAR(6) NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', stamp_updated DATETIME, PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, src_port, dst_port, ip_proto, stamp_inserted) ); pmacct-0.14.0/sql/pmacct-create-table_v7.pgsql0000755000175000017500000000357311227653261020151 0ustar paolopaolo-- -- # su - postgres (or whatever your database runs as ... usually postgres) -- $ psql -d pmacct -f pmacct-create-table_v7.pgsql -- -- Tables DROP TABLE acct_v7; CREATE TABLE acct_v7 ( agent_id BIGINT NOT NULL DEFAULT 0, class_id CHAR(16) NOT NULL DEFAULT ' ', mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, as_src BIGINT NOT NULL DEFAULT 0, as_dst BIGINT NOT NULL DEFAULT 0, ip_src inet NOT NULL DEFAULT '0.0.0.0', ip_dst inet NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, tcp_flags SMALLINT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_v7_pk PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, as_src, as_dst, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE proto; CREATE TABLE proto ( num SMALLINT NOT NULL, description CHAR(20), CONSTRAINT proto_pk PRIMARY KEY (num) ); COPY proto FROM stdin USING DELIMITERS ','; 0,ip 1,icmp 2,igmp 3,ggp 4,ipencap 5,st 6,tcp 8,egp 9,igp 17,udp 18,mux 27,rdp 29,iso-tp4 30,netblt 37,ddp 39,idpr-cmtp 41,ipv6 43,ipv6-route 44,ipv6-frag 46,rsvp 47,gre 50,ipv6-crypt 51,ipv6-auth 55,mobile 56,tlsp 58,ipv6-icmp 59,ipv6-nonxt 60,ipv6-opts 80,iso-ip 83,vines 88,eigrp 89,ospf 90,sprite-rpc 93,ax-25 94,ipip 98,encap 102,pnni 108,IPcomp 111,ipx-in-ip 112,vrrp 115,l2tp 124,isis 132,sctp 133,fc \. -- Perms GRANT SELECT, INSERT, UPDATE, DELETE ON acct_v7 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON proto TO pmacct; pmacct-0.14.0/sql/pmacct-create-table_v3.sqlite30000755000175000017500000000131011227653261020366 0ustar paolopaoloDROP TABLE acct_v3; CREATE TABLE acct_v3 ( agent_id INT(8) NOT NULL DEFAULT 0, mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT(4) NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', src_port INT(4) NOT NULL DEFAULT 0, dst_port INT(4) NOT NULL DEFAULT 0, ip_proto CHAR(6) NOT NULL DEFAULT 0, tos INT(4) NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', stamp_updated DATETIME, PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, src_port, dst_port, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/pmacct-create-table_v4.pgsql0000755000175000017500000000701711227653261020143 0ustar paolopaolo-- -- # su - postgres (or whatever your database runs as ... usually postgres) -- $ psql -d pmacct -f pmacct-create-table_v4.pgsql -- -- Tables DROP TABLE acct_uni_v4; CREATE TABLE acct_uni_v4 ( agent_id BIGINT NOT NULL DEFAULT 0, mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_uni_v4_pk PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE acct_v4; CREATE TABLE acct_v4 ( agent_id BIGINT NOT NULL DEFAULT 0, mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src inet NOT NULL DEFAULT '0.0.0.0', ip_dst inet NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_v4_pk PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE acct_as_v4; CREATE TABLE acct_as_v4 ( agent_id BIGINT NOT NULL DEFAULT 0, mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src INT NOT NULL DEFAULT 0, ip_dst INT NOT NULL DEFAULT 0, port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_as_v4_pk PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE proto; CREATE TABLE proto ( num SMALLINT NOT NULL, description CHAR(20), CONSTRAINT proto_pk PRIMARY KEY (num) ); COPY proto FROM stdin USING DELIMITERS ','; 0,ip 1,icmp 2,igmp 3,ggp 4,ipencap 5,st 6,tcp 8,egp 9,igp 17,udp 18,mux 27,rdp 29,iso-tp4 30,netblt 37,ddp 39,idpr-cmtp 41,ipv6 43,ipv6-route 44,ipv6-frag 46,rsvp 47,gre 50,ipv6-crypt 51,ipv6-auth 55,mobile 56,tlsp 58,ipv6-icmp 59,ipv6-nonxt 60,ipv6-opts 80,iso-ip 83,vines 88,eigrp 89,ospf 90,sprite-rpc 93,ax-25 94,ipip 98,encap 102,pnni 108,IPcomp 111,ipx-in-ip 112,vrrp 115,l2tp 124,isis 132,sctp 133,fc \. -- Perms GRANT SELECT, INSERT, UPDATE, DELETE ON acct_uni_v4 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON acct_v4 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON acct_as_v4 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON proto TO pmacct; pmacct-0.14.0/sql/pmacct-create-db_v4.mysql0000755000175000017500000000131311216727662017456 0ustar paolopaolodrop database if exists pmacct; create database pmacct; use pmacct; drop table if exists acct_v4; create table acct_v4 ( agent_id INT(4) UNSIGNED NOT NULL, mac_src CHAR(17) NOT NULL, mac_dst CHAR(17) NOT NULL, vlan INT(2) UNSIGNED NOT NULL, ip_src CHAR(15) NOT NULL, ip_dst CHAR(15) NOT NULL, src_port INT(2) UNSIGNED NOT NULL, dst_port INT(2) UNSIGNED NOT NULL, ip_proto CHAR(6) NOT NULL, tos INT(4) UNSIGNED NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, flows INT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, src_port, dst_port, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/README.pgsql0000644000175000017500000001562411712352254014667 0ustar paolopaoloTo create the database and grant default permission to the daemon you have to execute the two scripts below, in the same order; which user has to execute them and how to autenticate with the PostgreSQL server depends upon your current configuration. Keep in mind that both scripts need postgres superuser permissions to execute commands successfully: shell> cp -p *.pgsql /tmp shell> su - postgres - To create v1 tables: * psql -d template1 -f /tmp/pmacct-create-db.pgsql * psql -d pmacct -f /tmp/pmacct-create-table_v1.pgsql - To use v1 tables: * data will be available in 'acct' table of 'pmacct' DB. * Add 'sql_table_version: 1' line to your configuration. - To create v2 tables: * psql -d template1 -f /tmp/pmacct-create-db.pgsql * psql -d pmacct -f /tmp/pmacct-create-table_v2.pgsql - To use v2 tables: * data will be available in 'acct_v2' table of 'pmacct' DB. * Add 'sql_table_version: 2' line to your configuration. [ ... ] - To create v7 tables: * psql -d template1 -f /tmp/pmacct-create-db.pgsql * psql -d pmacct -f /tmp/pmacct-create-table_v7.pgsql - To use v7 tables: * data will be available in 'acct_v7' table of 'pmacct' DB. * Add 'sql_table_version: 7' line to your configuration. Similarly, BGP tables: - To create BGP v1 tables: * psql -d template1 -f /tmp/pmacct-create-db.pgsql * psql -d pmacct -f /tmp/pmacct-create-table_bgp_v1.pgsql - To use BGP v1 tables: * data will be available in 'acct_bgp' table of 'pmacct' DB. * Add 'sql_table_version: 1' line to your configuration. * Add 'sql_table_type: bgp' line to your configuration. Until v5 a few tables are created in the 'pmacct' database. 'acct' (or 'acct_vN') table is the default table where data will be written when in 'typed' mode (see 'sql_data' option in CONFIG-KEYS text file; default value is 'typed'); 'acct_uni' (or 'acct_uni_vN') is the default table where data will be written when in 'unified' mode. Since v6 unified mode is no longer supported. - To understand difference between the various table versions: * Do you need any of the BGP primitives ? Then look the next section. * Do you need TCP flags ? Then you have to use v7. * Do you need both IP addresses and AS numbers in the same table ? Then you have to use v6. * Do you need packet classification ? Then you have to use v5. * Do you need flows (other than packets) accounting ? Then you have to use v4. * Do you need ToS/DSCP field (QoS) accounting ? Then you have to use v3. * Do you need agent ID for distributed accounting and packet tagging ? Then you have to use v2. * Do you need VLAN traffic accounting ? Then you have to use v2. * If all of the above point sound useless, then use v1. - To understand difference between the various BGP table versions: * Only BGP table v1 is currently available. - Aggregation primitives to SQL schema mapping: Aggregation primitive => SQL table field * tag => agent_id (BIGINT NOT NULL DEFAULT 0) * tag2 => agent_id2 (BIGINT NOT NULL DEFAULT 0, see README.agent_id2) * src_as => as_src (BIGINT NOT NULL DEFAULT 0) * dst_as => as_dst (BIGINT NOT NULL DEFAULT 0) * peer_src_as => peer_as_src (BIGINT NOT NULL DEFAULT 0) * peer_dst_as => peer_as_dst (BIGINT NOT NULL DEFAULT 0) * peer_src_ip => peer_ip_src (inet NOT NULL DEFAULT '0.0.0.0', see README.IPv6) * peer_dst_ip => peer_ip_dst (inet NOT NULL DEFAULT '0.0.0.0', see README.IPv6) * mpls_vpn_rd => mpls_vpn_rd (CHAR(18) NOT NULL DEFAULT ' ') * std_comm => comms (CHAR(24) NOT NULL DEFAULT ' ') * ext_comm => comms (CHAR(24) NOT NULL DEFAULT ' ') * as_path => as_path (CHAR(21) NOT NULL DEFAULT ' ') * local_pref => local_pref (BIGINT NOT NULL DEFAULT 0) * med => med (BIGINT NOT NULL DEFAULT 0) * src_std_comm => comms_src (CHAR(24) NOT NULL DEFAULT ' ') * src_ext_comm => comms_src (CHAR(24) NOT NULL DEFAULT ' ') * src_as_path => as_path_src (CHAR(21) NOT NULL DEFAULT ' ') * src_local_pref => local_pref_src (BIGINT NOT NULL DEFAULT 0) * src_med => med_src (BIGINT NOT NULL DEFAULT 0) * in_iface => iface_in (BIGINT NOT NULL DEFAULT 0, see README.iface) * out_iface => iface_out (BIGINT NOT NULL DEFAULT 0, see README.iface) * src_mask => mask_src (SMALLINT NOT NULL DEFAULT 0, see README.mask) * dst_mask => mask_dst (SMALLINT NOT NULL DEFAULT 0, see README.mask) * cos => cos (SMALLINT NOT NULL DEFAULT 0, see README.cos) * etype => etype (INT NOT NULL DEFAULT 0, see README.etype) * class => class_id (CHAR(16) NOT NOT NULL DEFAULT ' ') * src_mac => mac_src (macaddr NOT NULL DEFAULT '0:0:0:0:0:0') * dst_mac => mac_dst (macaddr NOT NULL DEFAULT '0:0:0:0:0:0') * vlan => vlan (INT NOT NULL DEFAULT 0) * src_as => as_src (BIGINT NOT NULL DEFAULT 0) * dst_as => as_dst (BIGINT NOT NULL DEFAULT 0) * src_host => ip_src (inet NOT NULL DEFAULT '0.0.0.0', see README.IPv6) * dst_host => ip_dst (inet NOT NULL DEFAULT '0.0.0.0', see README.IPv6) * src_net => ip_src (inet NOT NULL DEFAULT '0.0.0.0', see README.IPv6) * dst_net => ip_dst (inet NOT NULL DEFAULT '0.0.0.0', see README.IPv6) * src_port => port_src (INT NOT NULL DEFAULT 0) * dst_port => port_dst (INT NOT NULL DEFAULT 0) * tcpflags => tcp_flags (SMALLINT NOT NULL DEFAULT 0) * proto => ip_proto (SMALLINT NOT NULL DEFAULT 0) * tos => tos (INT NOT NULL DEFAULT 0) - Counters and time reference need always to be defined as part of the SQL schema: * packets (INT UNSIGNED NOT NULL) * bytes (BIGINT UNSIGNED NOT NULL) * stamp_inserted (timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', enabled by sql_history) * stamp_updated (timestamp without time zone, enabled by sql_history) - What is the difference between 'typed' and 'unified' modes ? It applies to IP tables only (ie. not to BGP ones). The 'unified' table has IP addresses and MAC addresses specified as standard CHAR strings, slower but flexible (in the sense it may store each kind of strings); 'typed' tables sport PostgreSQL own types (inet, mac, etc.), faster but rigid. When not specifying your own 'sql_table', this switch instructs the plugin which tables has to use. (default: 'typed'). Since v6 unified mode is not supported anymore. - What is the 'proto' table ? The auxiliar 'proto' table will be created by default. Its tuples are simply number-string pairs: the protocol field of both typed and unified tables is numerical. This table helps in looking up protocol names by their number and viceversa. Because joins are expensive, 'proto' table has been created *only* for your personal reference. NOTE: mind to specify EVERYTIME which SQL table version you intend to adhere to by using either of the following rules: When using commandline options: * -v [ 1 | 2 | 3 | 4 | 5 | 6 | 7 ] When using configuration directives: * sql_table_version: [ 1 | 2 | 3 | 4 | 5 | 6 | 7 ] * sql_table_type: [ bgp ] NOTE: specifying a non-documented SQL table profile will result in an non-determined behaviour. Unless this will create crashes to the application, such situations will not be supported. pmacct-0.14.0/sql/pmacct-create-table_v2.pgsql0000755000175000017500000000651411227653261020142 0ustar paolopaolo-- -- # su - postgres (or whatever your database runs as ... usually postgres) -- $ psql -d pmacct -f pmacct-create-table_v2.pgsql -- -- Tables DROP TABLE acct_uni_v2; CREATE TABLE acct_uni_v2 ( agent_id BIGINT NOT NULL DEFAULT 0, mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_uni_v2_pk PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, stamp_inserted) ); DROP TABLE acct_v2; CREATE TABLE acct_v2 ( agent_id BIGINT NOT NULL DEFAULT 0, mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src inet NOT NULL DEFAULT '0.0.0.0', ip_dst inet NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_v2_pk PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, stamp_inserted) ); DROP TABLE acct_as_v2; CREATE TABLE acct_as_v2 ( agent_id BIGINT NOT NULL DEFAULT 0, mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, ip_src INT NOT NULL DEFAULT 0, ip_dst INT NOT NULL DEFAULT 0, port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_as_v2_pk PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, port_src, port_dst, ip_proto, stamp_inserted) ); DROP TABLE proto; CREATE TABLE proto ( num SMALLINT NOT NULL, description CHAR(20), CONSTRAINT proto_pk PRIMARY KEY (num) ); COPY proto FROM stdin USING DELIMITERS ','; 0,ip 1,icmp 2,igmp 3,ggp 4,ipencap 5,st 6,tcp 8,egp 9,igp 17,udp 18,mux 27,rdp 29,iso-tp4 30,netblt 37,ddp 39,idpr-cmtp 41,ipv6 43,ipv6-route 44,ipv6-frag 46,rsvp 47,gre 50,ipv6-crypt 51,ipv6-auth 55,mobile 56,tlsp 58,ipv6-icmp 59,ipv6-nonxt 60,ipv6-opts 80,iso-ip 83,vines 88,eigrp 89,ospf 90,sprite-rpc 93,ax-25 94,ipip 98,encap 102,pnni 108,IPcomp 111,ipx-in-ip 112,vrrp 115,l2tp 124,isis 132,sctp 133,fc \. -- Perms GRANT SELECT, INSERT, UPDATE, DELETE ON acct_uni_v2 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON acct_v2 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON acct_as_v2 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON proto TO pmacct; pmacct-0.14.0/sql/pmacct-create-db.pgsql0000755000175000017500000000062710530072467017027 0ustar paolopaolo-- -- # su - postgres (or whatever your database runs as ... usually postgres) -- $ psql -d template1 -f pmacct-create-db.pgsql -- -- NOTE: you should have a line like "local all password" in your pg_hba.conf -- to authenticate local users against Postgres userbase passwords. -- DROP DATABASE pmacct; CREATE DATABASE pmacct; CREATE USER pmacct; ALTER USER pmacct WITH PASSWORD 'arealsmartpwd'; pmacct-0.14.0/sql/pmacct-create-table_v5.sqlite30000755000175000017500000000143211227653261020375 0ustar paolopaoloDROP TABLE acct_v5; CREATE TABLE acct_v5 ( agent_id INT(8) NOT NULL DEFAULT 0, class_id CHAR(16) NOT NULL DEFAULT ' ', mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT(4) NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', src_port INT(4) NOT NULL DEFAULT 0, dst_port INT(4) NOT NULL DEFAULT 0, ip_proto CHAR(6) NOT NULL DEFAULT 0, tos INT(4) NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', stamp_updated DATETIME, PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, ip_src, ip_dst, src_port, dst_port, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/pmacct-create-table_v6.pgsql0000755000175000017500000000351411227653261020143 0ustar paolopaolo-- -- # su - postgres (or whatever your database runs as ... usually postgres) -- $ psql -d pmacct -f pmacct-create-table_v6.pgsql -- -- Tables DROP TABLE acct_v6; CREATE TABLE acct_v6 ( agent_id BIGINT NOT NULL DEFAULT 0, class_id CHAR(16) NOT NULL DEFAULT ' ', mac_src macaddr NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst macaddr NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT NOT NULL DEFAULT 0, as_src BIGINT NOT NULL DEFAULT 0, as_dst BIGINT NOT NULL DEFAULT 0, ip_src inet NOT NULL DEFAULT '0.0.0.0', ip_dst inet NOT NULL DEFAULT '0.0.0.0', port_src INT NOT NULL DEFAULT 0, port_dst INT NOT NULL DEFAULT 0, ip_proto SMALLINT NOT NULL DEFAULT 0, tos INT NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted timestamp without time zone NOT NULL DEFAULT '0000-01-01 00:00:00', stamp_updated timestamp without time zone, CONSTRAINT acct_v6_pk PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, as_src, as_dst, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); DROP TABLE proto; CREATE TABLE proto ( num SMALLINT NOT NULL, description CHAR(20), CONSTRAINT proto_pk PRIMARY KEY (num) ); COPY proto FROM stdin USING DELIMITERS ','; 0,ip 1,icmp 2,igmp 3,ggp 4,ipencap 5,st 6,tcp 8,egp 9,igp 17,udp 18,mux 27,rdp 29,iso-tp4 30,netblt 37,ddp 39,idpr-cmtp 41,ipv6 43,ipv6-route 44,ipv6-frag 46,rsvp 47,gre 50,ipv6-crypt 51,ipv6-auth 55,mobile 56,tlsp 58,ipv6-icmp 59,ipv6-nonxt 60,ipv6-opts 80,iso-ip 83,vines 88,eigrp 89,ospf 90,sprite-rpc 93,ax-25 94,ipip 98,encap 102,pnni 108,IPcomp 111,ipx-in-ip 112,vrrp 115,l2tp 124,isis 132,sctp 133,fc \. -- Perms GRANT SELECT, INSERT, UPDATE, DELETE ON acct_v6 TO pmacct; GRANT SELECT, INSERT, UPDATE, DELETE ON proto TO pmacct; pmacct-0.14.0/sql/README.mpls_vpn_rd0000640000175000017500000000125011664553344016057 0ustar paolopaoloThis document doesn't replace documentation relevant to the database software you are using, ie. README.mysql, README.pgsql or README.sqlite3. The 'mpls_vpn_rd' field. Such field is being introduced to support BGP/MPLS VPN Route Distinguisher (RD) field. The guidelines below (typically in MySQL format) are to add such primitives to the SQL schema: * mpls_vpn_rd field: - "mpls_vpn_rd CHAR(18) NOT NULL," to declare the field itself - "PRIMARY KEY (..., mpls_vpn_rd, ...)" to put it in the primary key The primitive is not declared as part of any default table version; yet will not fail version checks which are enabled when 'sql_optimize_clauses' feature is disabled. pmacct-0.14.0/sql/pmacct-create-table_v8.sqlite30000750000175000017500000000164411453022224020366 0ustar paolopaoloDROP TABLE acct_v8; CREATE TABLE acct_v8 ( agent_id INT(8) NOT NULL DEFAULT 0, class_id CHAR(16) NOT NULL DEFAULT ' ', mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT(4) NOT NULL DEFAULT 0, as_src INT(8) NOT NULL DEFAULT 0, as_dst INT(8) NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', port_src INT(4) NOT NULL DEFAULT 0, port_dst INT(4) NOT NULL DEFAULT 0, tcp_flags INT(4) NOT NULL DEFAULT 0, ip_proto CHAR(6) NOT NULL DEFAULT 0, tos INT(4) NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', stamp_updated DATETIME, PRIMARY KEY (agent_id, class_id, mac_src, mac_dst, vlan, as_src, as_dst, ip_src, ip_dst, port_src, port_dst, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/sql/pmacct-create-table_v4.sqlite30000755000175000017500000000134711227653261020401 0ustar paolopaoloDROP TABLE acct_v4; CREATE TABLE acct_v4 ( agent_id INT(8) NOT NULL DEFAULT 0, mac_src CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', mac_dst CHAR(17) NOT NULL DEFAULT '0:0:0:0:0:0', vlan INT(4) NOT NULL DEFAULT 0, ip_src CHAR(15) NOT NULL DEFAULT '0.0.0.0', ip_dst CHAR(15) NOT NULL DEFAULT '0.0.0.0', src_port INT(4) NOT NULL DEFAULT 0, dst_port INT(4) NOT NULL DEFAULT 0, ip_proto CHAR(6) NOT NULL DEFAULT 0, tos INT(4) NOT NULL DEFAULT 0, packets INT NOT NULL, bytes BIGINT NOT NULL, flows INT NOT NULL DEFAULT 0, stamp_inserted DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', stamp_updated DATETIME, PRIMARY KEY (agent_id, mac_src, mac_dst, vlan, ip_src, ip_dst, src_port, dst_port, ip_proto, tos, stamp_inserted) ); pmacct-0.14.0/aclocal.m40000644000175000017500000001413611741267652013731 0ustar paolopaolodnl aclocal.m4 generated automatically by aclocal 1.4-p6 dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A dnl PARTICULAR PURPOSE. # # Author: Guido Draheim # Last Modified: 2001-05-03 # Synopsis: AC_CHECK_TYPEDEF_(TYPEDEF, HEADER [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]) # Reference: http://autoconf-archive.cryp.to/ac_check_typedef.html # AC_DEFUN(AC_CHECK_TYPEDEF_, [dnl ac_lib_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` AC_CACHE_VAL(ac_cv_lib_$ac_lib_var, [ eval "ac_cv_type_$ac_lib_var='not-found'" ac_cv_check_typedef_header=`echo ifelse([$2], , stddef.h, $2)` AC_TRY_COMPILE( [#include <$ac_cv_check_typedef_header>], [int x = sizeof($1); x = x;], eval "ac_cv_type_$ac_lib_var=yes" , eval "ac_cv_type_$ac_lib_var=no" ) if test `eval echo '$ac_cv_type_'$ac_lib_var` = "no" ; then ifelse([$4], , :, $4) else ifelse([$3], , :, $3) fi ])]) dnl AC_CHECK_TYPEDEF(TYPEDEF, HEADER [, ACTION-IF-FOUND, dnl [, ACTION-IF-NOT-FOUND ]]) AC_DEFUN(AC_CHECK_TYPEDEF, [dnl AC_MSG_CHECKING([for $1 in $2]) AC_CHECK_TYPEDEF_($1,$2, [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_[]translit($1, [a-z], [A-Z])) HAVE_[]translit($1, [a-z], [A-Z])="1" ], AC_MSG_RESULT(no))dnl ]) # # Author: Paolo Lucente # Last Modified: 2006-03-07 # Synopsis: AC_LINEARIZE_PATH(PATH) # Reference: # AC_DEFUN(AC_LINEARIZE_PATH, [ absdir=`cd $1 2>/dev/null && pwd` if test x$absdir != x ; then [$2] fi ]) # Do all the work for Automake. This macro actually does too much -- # some checks are only needed if your package does certain things. # But this isn't really a big deal. # serial 1 dnl Usage: dnl AM_INIT_AUTOMAKE(package,version, [no-define]) AC_DEFUN([AM_INIT_AUTOMAKE], [AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL]) PACKAGE=[$1] AC_SUBST(PACKAGE) VERSION=[$2] AC_SUBST(VERSION) dnl test to see if srcdir already configured if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi ifelse([$3],, AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])) AC_REQUIRE([AM_SANITY_CHECK]) AC_REQUIRE([AC_ARG_PROGRAM]) dnl FIXME This is truly gross. missing_dir=`cd $ac_aux_dir && pwd` AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}, $missing_dir) AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}, $missing_dir) AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) AC_REQUIRE([AC_PROG_MAKE_SET])]) # Copyright 2002 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 # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. AC_DEFUN([AM_AUTOMAKE_VERSION],[am__api_version="1.4"]) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION so it can be traced. # This function is AC_REQUIREd by AC_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.4-p6])]) # # Check to make sure that the build environment is sane. # AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Just in case sleep 1 echo timestamp > conftestfile # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null` if test "[$]*" = "X"; then # -L didn't work. set X `ls -t $srcdir/configure conftestfile` fi if test "[$]*" != "X $srcdir/configure conftestfile" \ && test "[$]*" != "X conftestfile $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi test "[$]2" = conftestfile ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi rm -f conftest* AC_MSG_RESULT(yes)]) dnl AM_MISSING_PROG(NAME, PROGRAM, DIRECTORY) dnl The program must properly implement --version. AC_DEFUN([AM_MISSING_PROG], [AC_MSG_CHECKING(for working $2) # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. if ($2 --version) < /dev/null > /dev/null 2>&1; then $1=$2 AC_MSG_RESULT(found) else $1="$3/missing $2" AC_MSG_RESULT(missing) fi AC_SUBST($1)]) pmacct-0.14.0/FAQS0000644000175000017500000004214411662513716012543 0ustar paolopaolopmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente Q1: What is pmacct project homepage ? A: It is http://www.pmacct.net/ . There isn't any official mirror site. Q2: 'pmacct', 'pmacctd', 'nfacctd', 'sfacctd', 'uacctd' -- but what do they mean ? A: 'pmacct' is intended to be the name of the project; 'pmacctd' is the name of the libpcap-based IPv4/IPv6 accounting daemon; 'nfacctd' is the name of the NetFlow (versions supported NetFlow v1 to v9) and IPFIX accounting daemon; 'sfacctd' is the name of the sFlow v2/v4/v5 accounting daemon; 'uacctd' is the name of the Linux Netlink ULOG-based accounting daemon. Q3: Does pmacct stand for Promiscuous mode IP Accounting package ? A: That is not entirely correct today, it was originally though. pmacct born as a libpcap-based project only. Over the time it evolved to include NetFlow first, sFlow shortly afterwards and ULOG more recently - striving to maintain a consistent implementation over the set, unless technical considerations prevent that to happen for specific cases. Q4: What are pmacct main features? A: pmacct can collect, replicate and export network data. Collect in memory tables, OSS RDBMS (MySQL, PostgreSQL, SQLite 3.x, BerkeleyDB 5.x via SQLite API) and files (csv, formatted output). Export speaking sFlow v5, NetFlow v1/v5/v9 and IPFIX. pmacct is able to perform data aggregation, offering a rich set of primitives to choose from; it can also filter, sample, renormalize, tag and classify at L7. pmacct integrates a BGP daemon join routing visibility and network traffic information. Q5: Does any of the pmacct daemons logs to flat files? A: Yes, but in a specific way. In other tools flat-files are typically used to log every micro-flow (or whatever aggregation the NetFlow agents have been configured to export with) and work in a two-stages fashion: a) write down to persistent storage then b) consolidate, on either or both spatial and temporal axes, to build the desired view. By inception, pmacct always aimed to a single-stage approach instead, ie. offer data reduction tecniques and correlation tools to process network traffic data on the fly, so to immediately offer the desired view(s) of the traffic. pmacct writes to files in text-format (either csv or formatted via its 'print' plugin, see EXAMPLES for further information) so to maximize potential integration with 3rd party applications while keeping low the effort of customization. Q6: Is it feasible for pmacct to scale by making use of either memory tables or RDBMS as backend for logging network traffic? A: pmacct doesn't log network traffic at packet/micro-flow level: it allows to get an aggregated view of the traffic -- both in space and in time. On top of that, there are layers of filtering, sampling and tagging. These are the keys to scale. As these features are fully configurable, data granularity and resolution can be, at any given moment, traded off in favour of increased scalability or less resources consumption. Q7: When using pmacctd, i feel a high CPU usage: i see the process getting a great share of the CPU. How to reduce it ? A: Granted that the CPU in use for accounting purposes is somewhat 'compatible' with the amount of traffic it has to process, it's possible to reduce the CPU share by avoiding unnecessary copies of data, also optimizing and buffering the necessary ones. Kernel-to-userspace copies are critical and hence the first to be optimized; for this purpose you may look at the following solutions: libpcap-mmap, http://public.lanl.gov/cpw/ : a libpcap version which supports mmap() on the linux kernel 2.[46].x . Applications, like pmacctd, need just to be linked against the mmap()ed version of libpcap to work correctly. PF_RING, http://www.ntop.org/PF_RING.html : it's a new type of network socket that improves the packet capture speed; it's available for Linux kernels 2.[46].x; it's kernel based; has libpcap support for seamless integration with existing applications. Device polling: it's available since FreeBSD 4.5REL kernel and needs just kernel recompilation (with "options DEVICE_POLLING"), and a polling-aware NIC. Linux kernel 2.6.x also supports device polling. Then also look at internal buffering which is applicable to all the pmacctd/nfacctd/ sfacctd daemons (for in-depth information you might want to see also 'Communications between core process and plugins' chapter of the INTERNALS document): 'plugin_buffer_size': turns on bufferization. '1024', '2048' or '4096' are sufficient values for common environments. If the circular queue size (plugin_pipe_size) is not defined, it is calculated the following way: ('plugin_buffer_size' / as) * dss. Where 'dss' is the default OS socket size and 'as' is the memory address size (4 bytes for a 32 bit architecture, 8 bytes for 64 bit architectures, etc.). 'plugin_pipe_size': sets the circular queue size. If bufferization is also enabled, this value has to be greater or equal to the buffer size. Values like 1MB (1024000), 2MB (2048000) or 4MB (4096000) are generally sufficient. Q8: I want to to account both inbound and outbound traffic of my network, with an host breakdown; how to do that in a savy fashion ? Do i need to run two daemon instances one per traffic direction ? A: No, you will be able to leverage the pluggable architecture of the daemons: you will run a single daemon with two plugins attached to it; each of these will get part of the traffic (aggregate_filter), either outbound or inbound. A sample config snippet follows: ... aggregate[inbound]: dst_host aggregate[outbound]: src_host aggregate_filter[inbound]: dst net 192.168.0.0/16 aggregate_filter[outbound]: src net 192.168.0.0/16 plugins: mysql[inbound], mysql[outbound] sql_table[inbound]: acct_in sql_table[outbound]: acct_out ... It will account all traffic directed to your network into the 'acct_in' table and all traffic it generates into 'acct_out' table. Furthermore, if you actually need totals (inbound plus outbound traffic), you will just need to play around with basic SQL queries. If you are only interested into having totals instead, you may alternatively use the following piece of configuration: ... aggregate: sum_host plugins: mysql networks_file: /usr/local/pmacct/etc/networks.lst ... Where 'networks.lst' is a file where to define local network prefixes. Q9: I'm intimately fashioned by the idea of storing every single flow flying through my network, before making up my mind what to do with such data: i basically would like to aggregate my traffic as 'src_host, dst_host, src_port, dst_port, proto'. Is this feasible without any filtering ? A: This is not adviceable. A simple reason being this would result in a huge matrix of data, whose behaviour and size would be totally un-predictable over time (ie. impact of port scans, DDoS, etc.). Nevertless, it remains a valid configuration. Q10: I use pmacctd. What portion of the packets is included into the bytes counter ? A: The portion of the packet accounted starts from the IPv4/IPv6 header (inclusive) and ends with the last bit of the packet payload. This means that are excluded from the accounting: packet preamble (if any), link layer headers (e.g. ethernet, llc, etc.), MPLS stack length, VLAN tags size and trailing FCS (if any). This is the main reason of skews reported while comparing pmacct counters to SNMP ones. However, by having available a counter of packets, accounting for the missing portion is, in most cases, a simple math exercise which depends on the underlying network architecture. Example: Ethernet header = 14 bytes, Preamble+SFD (Start Frame Delimiter) = 8 bytes, FCS (Framke Check Sequence) = 4 bytes. It results in an addition of a maximum of 26 bytes (14+8+4) for each packet. The use of VLANs will result in adding 4 more bytes to the forementioned 26. If using an SQL plugin, starting from release 0.9.2, such adjustment can be achieved directly within pmacct via the 'adjb' action (sql_preprocess). Q11: How to get the historical accounting enabled ? SQL table have a 'stamp_inserted' and 'stamp_updated' fields but they remain empty. A: Historical accounting is easily enabled by adding to the SQL plugin configuration a 'sql_history' directive. Associate to it a 'sql_history_roundoff'. For examples and syntax, refer to CONFIG-KEYS and EXAMPLES documents. Q12: CLI is not enough to me. I would like to graph traffic data: how to do that? A: RRDtool, MRTG and GNUplot are just some tools which could be easily integrated with pmacct operations. 'Memory plugin' is suitable as temporary storage and allows to easily retrieve counters: shell> ./pmacctd -D -c src_host -P memory -i eth0 shell> ./pmacct -c src_host -N 192.168.4.133 -r 2339 shell> Et voila'! This is the bytes counter. Because of the '-r', counters will get reset or translating into the RRDTool jargon, each time you will get an 'ABSOLUTE' value. Let's now encapsulate our query into, say, RRDtool commandline: shell> rrdtool update 192_168_4_133.rrd N:`./pmacct -c src_host -N 192.168.4.133 -r` Starting from 0.7.6, you will also be able to spawn as much as 4096 requests into a single query; you may write your requests commandline (';' separated) but also read them from a file (one per line): shell> ./pmacct -c src_host,dst_host -N 192.168.4.133,192.168.0.101;192.168.4.5,192.168.4.1;... -r 50905 1152 ... OR shell> ./pmacct -c src_host,dst_host -N "file:queries.list" -r ... shell> cat queries.list 192.168.4.133,192.168.0.101 192.168.4.5,192.168.4.1 ... Furthermore, SNMP is a widespreaded protocol used (and widely supported) in the IP accounting field to gather IP traffic information by network devices. 'pmacct' may also be easily connected to Net-SNMP extensible MIB. What follows is an example for your 'snmpd.conf': exec .1.3.6.1.4.1.2021.50 Description /usr/local/bin/pmacct -c src_host -N 192.168.4.133 -r Then, an 'snmpwalk' does the rest of the work: shell> snmpwalk -v 1 localhost -c public .1.3.6.1.4.1.2021.50 .1.3.6.1.4.1.2021.50.1.1 = 1 .1.3.6.1.4.1.2021.50.2.1 = "Description" .1.3.6.1.4.1.2021.50.3.1 = "/usr/local/bin/pmacct -c src_host -N 192.168.4.133 -r" .1.3.6.1.4.1.2021.50.100.1 = 0 .1.3.6.1.4.1.2021.50.101.1 = "92984384" .1.3.6.1.4.1.2021.50.102.1 = 0 Q13: The network equipment i'm using supports sFlow but i don't know how to enable it. I'm unable to find any sflow-related command. What to do ? A: If you are unable to enable sFlow commandline, you have to resort to the SNMP way. The sFlow MIB is documented into the RFC 3176; all you will need is to enable a SNMP community with both read and write access. Then, continue using the sflowenable tool available at the following URL: http://www.inmon.com/technology/sflowenable Q14: I've configured the pmacct package in order to support IPv6 via the '--enable-ipv6' switch. Now, when i launch either nfacctd or sfacctd i receive the following error message: ERROR ( default/core ): socket() failed. What to do ? A: When IPv6 code is enabled, sfacctd and nfacctd will try to fire up an IPv6 socket. The error message is very likely to be caused by the proper kernel module not being loaded. So, try either to load it or specify an IPv4 address to bind to. If using a configuration file, add a line like 'nfacctd_ip: 192.168.0.14'; otherwise if going commandline, use the following: 'nfacctd [ ... options ... ] -L 192.168.0.14'. Q15: 32 bit counters are not large enough to me, in fact i see them rolling over and returning inconsistent results. What to do ? A: pmacct >= 0.9.2 optionally supports 64 bits counters via a '--enable-64bit' switch while configuring the package for compilation. It will affect all counters: bytes, packets and flows. Use such switch only when required as 32 bits counters allow to save some memory. Usually, overflowing counters are recognizable by unexpected fluctuations in the counters value - caused, as said, by one or multiple rollovers. Q16: SQL table versions, what they are -- why and when do i need them ? Also, can i customize SQL tables ? A: pmacct tarball gets with so called 'default' tables (IP and BGP); they are built by SQL scripts stored in the 'sql/' section of the tarball. Default tables enable to start quickly with pmacct out-of-the-box; this doesn't imply they are suitable as-is to larger installations. SQL table versioning is used to introduce features over the time without breaking backward compatibility when upgrading pmacct. The most updated guide on which version to use given a required feature-set can be, once again, found in the 'sql/' section of the tarball. SQL tables *can* be fully customized so that primitives of interest can be freely mixed and matched - hence making a SQL table to perfectly adhere to the required feature-set. This is achieved by setting the 'sql_optimize_clauses' configuration key. You will then be responsible for building the custom schema and indexes. Q17: What is the best way to kill a running instance of pmacct avoiding data loss ? A: Two ways. a) Simply kill a specific plugin that you don't need anymore: you will have to identify it and use the 'kill -INT command; b) kill the whole pmacct instance: you can either use the 'killall -INT ' command or identify the Core Process and use the 'kill -INT command. All of these, will do the job for you: will stop receiving new data from the network, clear the memory buffers, notify the running plugins to take th exit lane (which in turn will clear cached data as required). To identify the Core Process you can either take a look to the process list (on the Operating Systems where the setproctitle() call is supported by pmacct) or use the 'pidfile' (-F) directive. Note also that shutting down nicely the daemon improves restart turn-around times: the existing daemon will, first thing, close its listening socket while the newly launched one will mostly take advantage of the SO_REUSEADDR socket option. Q18: I find interesting store network data in a SQL database. But i'm actually hitting poor performances. Do you have any tips to improve/optimize things ? A: Few hints are summed below in order to improve SQL database performances. They are not really tailored to a specific SQL engine but rather of general applicability. Many thanks to Wim Kerkhoff for the many suggestions he contributed on this topic over the time: * Keep the SQL schema lean: include only required fields, strip off all the others. Set the 'sql_optimize_clauses' configuration key in order to flag pmacct you are going to use a custom-built table. * Avoid SQL UPDATEs as much as possible and use only INSERTs. This can be achieved by setting the 'sql_dont_try_update' configuration key. A pre-condition is to let sql_history == sql_refresh_time. UPDATEs are demanding in terms of resources and are, for simplicity, enabled by default. * If the previous point holds, then look for and enable database-specific directives aimed to optimize performances ie. sql_multi_values for MySQL and sql_use_copy for PostgreSQL. * Don't rely automagically on standard indexes but enable optimal indexes based on clauses you (by means of reports, 3rd party tools, scripts, etc.) and pmacct use the most to SELECT data. Then remove every unused index. * See if the dynamic table strategy offered by pmacct fits the bill: helps keeping SQL tables and indexes of a manageable size by rotating SQL tables (ie. per hour, day, week, etc.). See the 'sql_table_schema' configuration directive. * Run all SELECT and UPDATE queries under the "EXPLAIN ANALYZE ..." method to see if they are actually hitting the indexes. If not, you need to build indexes that better fit the actual scenario. * Sometimes setting "SET enable_seqscan=no;" before a SELECT query can make a big difference. Also don't underestimate the importance of daily VACUUM queries: 3-5 VACUUMs + 1 VACUUM FULL is generally a good idea. These tips hold for PostgreSQL. * MyISAM is a lean SQL engine; if there is no concurrence, it might be preferred to InnoDB. Lack of transactions can reveal painful in case of unsecured shutdowns, requiring data recovery. This applies to MySQL only. * Disabling fsync() does improve performance. This might have painful consequences in case of unsecured shutdowns (remember power failure is a variable ...). Q19: I've configured the server hosting pmacct with my local timezone - which includes DST (Daylight Saving Time). Is this allright? A: In general, it's good rule to run the backend part of any accounting system as UTC; pmacct uses the underlying system clock, expecially in the SQL plugins to calculate time-bins and scanner deadlines among the others. The use of timezones is supported but not recommended. /* EOF */ pmacct-0.14.0/configure0000755000175000017500000033766511741267653014020 0ustar paolopaolo#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated automatically using autoconf version 2.13 # Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # Defaults: ac_help= ac_default_prefix=/usr/local # Any additions from configure.in: ac_default_prefix=/usr/local ac_help="$ac_help --enable-debug enable debugging compiler options" ac_help="$ac_help --enable-relax relax compiler optimization" ac_help="$ac_help --disable-l2 disable Layer-2 features and support" ac_help="$ac_help --enable-ipv6 Enable IPv6 code" ac_help="$ac_help --enable-v4-mapped allow IPv6 sockets to handle IPv4 connections" ac_help="$ac_help --with-pcap-includes=DIR Search the specified directories for header files" ac_help="$ac_help --with-pcap-libs=DIR Search the specified directories for libraries" ac_help="$ac_help --enable-mysql Enable MySQL support" ac_help="$ac_help --with-mysql-libs=DIR Search for MySQL libs in the specified directory" ac_help="$ac_help --with-mysql-includes=DIR Search for MySQL includes in the specified directory" ac_help="$ac_help --enable-pgsql Enable PostgreSQL support" ac_help="$ac_help --with-pgsql-libs=DIR Search for PostgreSQL libs in the specified directory" ac_help="$ac_help --with-pgsql-includes=DIR Search for PostgreSQL includes in the specified directory" ac_help="$ac_help --enable-sqlite3 Enable SQLite3 support" ac_help="$ac_help --with-sqlite3-libs=DIR Search for SQLite3 libs in the specified directory" ac_help="$ac_help --with-sqlite3-includes=DIR Search for SQLite3 includes in the specified directory" ac_help="$ac_help --disable-so Disable shared objects" ac_help="$ac_help --enable-64bit Enable 64bit counters" ac_help="$ac_help --enable-threads Enable multi-threading in pmacctd" ac_help="$ac_help --enable-ulog Enable ULOG support" # Initialize some variables set by options. # The variables have the same names as the options, with # dashes changed to underlines. build=NONE cache_file=./config.cache exec_prefix=NONE host=NONE no_create= nonopt=NONE no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= target=NONE verbose= x_includes=NONE x_libraries=NONE bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' # Initialize some other variables. subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Maximum number of lines to put in a shell here document. ac_max_here_lines=12 ac_prev= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi case "$ac_option" in -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) ac_optarg= ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case "$ac_option" in -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir="$ac_optarg" ;; -build | --build | --buil | --bui | --bu) ac_prev=build ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build="$ac_optarg" ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file="$ac_optarg" ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir="$ac_optarg" ;; -disable-* | --disable-*) ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } fi ac_feature=`echo $ac_feature| sed 's/-/_/g'` eval "enable_${ac_feature}=no" ;; -enable-* | --enable-*) ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } fi ac_feature=`echo $ac_feature| sed 's/-/_/g'` case "$ac_option" in *=*) ;; *) ac_optarg=yes ;; esac eval "enable_${ac_feature}='$ac_optarg'" ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix="$ac_optarg" ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he) # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat << EOF Usage: configure [options] [host] Options: [defaults in brackets after descriptions] Configuration: --cache-file=FILE cache test results in FILE --help print this message --no-create do not create output files --quiet, --silent do not print \`checking...' messages --version print the version of autoconf that created configure Directory and file names: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [same as prefix] --bindir=DIR user executables in DIR [EPREFIX/bin] --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] --libexecdir=DIR program executables in DIR [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data in DIR [PREFIX/share] --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data in DIR [PREFIX/com] --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] --libdir=DIR object code libraries in DIR [EPREFIX/lib] --includedir=DIR C header files in DIR [PREFIX/include] --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] --infodir=DIR info documentation in DIR [PREFIX/info] --mandir=DIR man documentation in DIR [PREFIX/man] --srcdir=DIR find the sources in DIR [configure dir or ..] --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names EOF cat << EOF Host type: --build=BUILD configure for building on BUILD [BUILD=HOST] --host=HOST configure for HOST [guessed] --target=TARGET configure for TARGET [TARGET=HOST] Features and packages: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --x-includes=DIR X include files are in DIR --x-libraries=DIR X library files are in DIR EOF if test -n "$ac_help"; then echo "--enable and --with options recognized:$ac_help" fi exit 0 ;; -host | --host | --hos | --ho) ac_prev=host ;; -host=* | --host=* | --hos=* | --ho=*) host="$ac_optarg" ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir="$ac_optarg" ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir="$ac_optarg" ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir="$ac_optarg" ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir="$ac_optarg" ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) localstatedir="$ac_optarg" ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir="$ac_optarg" ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir="$ac_optarg" ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix="$ac_optarg" ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix="$ac_optarg" ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix="$ac_optarg" ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name="$ac_optarg" ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir="$ac_optarg" ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir="$ac_optarg" ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site="$ac_optarg" ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir="$ac_optarg" ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir="$ac_optarg" ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target="$ac_optarg" ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers) echo "configure generated by autoconf version 2.13" exit 0 ;; -with-* | --with-*) ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } fi ac_package=`echo $ac_package| sed 's/-/_/g'` case "$ac_option" in *=*) ;; *) ac_optarg=yes ;; esac eval "with_${ac_package}='$ac_optarg'" ;; -without-* | --without-*) ac_package=`echo $ac_option|sed -e 's/-*without-//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } fi ac_package=`echo $ac_package| sed 's/-/_/g'` eval "with_${ac_package}=no" ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes="$ac_optarg" ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries="$ac_optarg" ;; -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } ;; *) if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then echo "configure: warning: $ac_option: invalid host type" 1>&2 fi if test "x$nonopt" != xNONE; then { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } fi nonopt="$ac_option" ;; esac done if test -n "$ac_prev"; then { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } fi trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 # File descriptor usage: # 0 standard input # 1 file creation # 2 errors and warnings # 3 some systems may open it to /dev/tty # 4 used on the Kubota Titan # 6 checking for... messages and results # 5 compiler messages saved in config.log if test "$silent" = yes; then exec 6>/dev/null else exec 6>&1 fi exec 5>./config.log echo "\ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. " 1>&5 # Strip out --no-create and --no-recursion so they do not pile up. # Also quote any args containing shell metacharacters. ac_configure_args= for ac_arg do case "$ac_arg" in -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c) ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) ac_configure_args="$ac_configure_args '$ac_arg'" ;; *) ac_configure_args="$ac_configure_args $ac_arg" ;; esac done # NLS nuisances. # Only set these to C if already set. These must not be set unconditionally # because not all systems understand e.g. LANG=C (notably SCO). # Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! # Non-C LC_CTYPE values break the ctype check. if test "${LANG+set}" = set; then LANG=C; export LANG; fi if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo > confdefs.h # A filename unique to this package, relative to the directory that # configure is in, which we can look for to find out if srcdir is correct. ac_unique_file=src/pmacctd.c # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then its parent. ac_prog=$0 ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. srcdir=$ac_confdir if test ! -r $srcdir/$ac_unique_file; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r $srcdir/$ac_unique_file; then if test "$ac_srcdir_defaulted" = yes; then { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } else { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } fi fi srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then echo "loading site script $ac_site_file" . "$ac_site_file" fi done if test -r "$cache_file"; then echo "loading cache $cache_file" . $cache_file else echo "creating cache $cache_file" > $cache_file fi ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross ac_exeext= ac_objext=o if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then ac_n= ac_c=' ' ac_t=' ' else ac_n=-n ac_c= ac_t= fi else ac_n= ac_c='\c' ac_t= fi ac_aux_dir= for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do if test -f $ac_dir/install-sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f $ac_dir/install.sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break fi done if test -z "$ac_aux_dir"; then { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } fi ac_config_guess=$ac_aux_dir/config.guess ac_config_sub=$ac_aux_dir/config.sub ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. am__api_version="1.4" # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 echo "configure:598: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" for ac_dir in $PATH; do # Account for people who put trailing slashes in PATH elements. case "$ac_dir/" in /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do if test -f $ac_dir/$ac_prog; then if test $ac_prog = install && grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : else ac_cv_path_install="$ac_dir/$ac_prog -c" break 2 fi fi done ;; esac done IFS="$ac_save_IFS" fi if test "${ac_cv_path_install+set}" = set; then INSTALL="$ac_cv_path_install" else # As a last resort, use the slow shell script. We don't cache a # path for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the path is relative. INSTALL="$ac_install_sh" fi fi echo "$ac_t""$INSTALL" 1>&6 # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' echo $ac_n "checking whether build environment is sane""... $ac_c" 1>&6 echo "configure:651: checking whether build environment is sane" >&5 # Just in case sleep 1 echo timestamp > conftestfile # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t $srcdir/configure conftestfile` fi if test "$*" != "X $srcdir/configure conftestfile" \ && test "$*" != "X conftestfile $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". { echo "configure: error: ls -t appears to fail. Make sure there is not a broken alias in your environment" 1>&2; exit 1; } fi test "$2" = conftestfile ) then # Ok. : else { echo "configure: error: newly created file is older than distributed files! Check your system clock" 1>&2; exit 1; } fi rm -f conftest* echo "$ac_t""yes" 1>&6 if test "$program_transform_name" = s,x,x,; then program_transform_name= else # Double any \ or $. echo might interpret backslashes. cat <<\EOF_SED > conftestsed s,\\,\\\\,g; s,\$,$$,g EOF_SED program_transform_name="`echo $program_transform_name|sed -f conftestsed`" rm -f conftestsed fi test "$program_prefix" != NONE && program_transform_name="s,^,${program_prefix},; $program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" # sed with no file args requires a program. test "$program_transform_name" = "" && program_transform_name="s,x,x," echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 echo "configure:708: checking whether ${MAKE-make} sets \${MAKE}" >&5 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftestmake <<\EOF all: @echo 'ac_maketemp="${MAKE}"' EOF # GNU make sometimes prints "make[1]: Entering...", which would confuse us. eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` if test -n "$ac_maketemp"; then eval ac_cv_prog_make_${ac_make}_set=yes else eval ac_cv_prog_make_${ac_make}_set=no fi rm -f conftestmake fi if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then echo "$ac_t""yes" 1>&6 SET_MAKE= else echo "$ac_t""no" 1>&6 SET_MAKE="MAKE=${MAKE-make}" fi PACKAGE=pmacctd VERSION=0.14.0 if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then { echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; } fi cat >> confdefs.h <> confdefs.h <&6 echo "configure:754: checking for working aclocal-${am__api_version}" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. if (aclocal-${am__api_version} --version) < /dev/null > /dev/null 2>&1; then ACLOCAL=aclocal-${am__api_version} echo "$ac_t""found" 1>&6 else ACLOCAL="$missing_dir/missing aclocal-${am__api_version}" echo "$ac_t""missing" 1>&6 fi echo $ac_n "checking for working autoconf""... $ac_c" 1>&6 echo "configure:767: checking for working autoconf" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. if (autoconf --version) < /dev/null > /dev/null 2>&1; then AUTOCONF=autoconf echo "$ac_t""found" 1>&6 else AUTOCONF="$missing_dir/missing autoconf" echo "$ac_t""missing" 1>&6 fi echo $ac_n "checking for working automake-${am__api_version}""... $ac_c" 1>&6 echo "configure:780: checking for working automake-${am__api_version}" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. if (automake-${am__api_version} --version) < /dev/null > /dev/null 2>&1; then AUTOMAKE=automake-${am__api_version} echo "$ac_t""found" 1>&6 else AUTOMAKE="$missing_dir/missing automake-${am__api_version}" echo "$ac_t""missing" 1>&6 fi echo $ac_n "checking for working autoheader""... $ac_c" 1>&6 echo "configure:793: checking for working autoheader" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. if (autoheader --version) < /dev/null > /dev/null 2>&1; then AUTOHEADER=autoheader echo "$ac_t""found" 1>&6 else AUTOHEADER="$missing_dir/missing autoheader" echo "$ac_t""missing" 1>&6 fi echo $ac_n "checking for working makeinfo""... $ac_c" 1>&6 echo "configure:806: checking for working makeinfo" >&5 # Run test in a subshell; some versions of sh will print an error if # an executable is not found, even if stderr is redirected. # Redirect stdin to placate older versions of autoconf. Sigh. if (makeinfo --version) < /dev/null > /dev/null 2>&1; then MAKEINFO=makeinfo echo "$ac_t""found" 1>&6 else MAKEINFO="$missing_dir/missing makeinfo" echo "$ac_t""missing" 1>&6 fi # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:824: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_CC="gcc" break fi done IFS="$ac_save_ifs" fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:854: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_prog_rejected=no ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" break fi done IFS="$ac_save_ifs" if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# -gt 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift set dummy "$ac_dir/$ac_word" "$@" shift ac_cv_prog_CC="$@" fi fi fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 fi if test -z "$CC"; then case "`uname -s`" in *win32* | *WIN32*) # Extract the first word of "cl", so it can be a program name with args. set dummy cl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:905: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_CC="cl" break fi done IFS="$ac_save_ifs" fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 fi ;; esac fi test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 echo "configure:937: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext << EOF #line 948 "configure" #include "confdefs.h" main(){return(0);} EOF if { (eval echo configure:953: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then ac_cv_prog_cc_cross=no else ac_cv_prog_cc_cross=yes fi else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 ac_cv_prog_cc_works=no fi rm -fr conftest* ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 echo "configure:979: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 echo "configure:984: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no fi fi echo "$ac_t""$ac_cv_prog_gcc" 1>&6 if test $ac_cv_prog_gcc = yes; then GCC=yes else GCC= fi ac_test_CFLAGS="${CFLAGS+set}" ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 echo "configure:1012: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else echo 'void f(){}' > conftest.c if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then ac_cv_prog_cc_g=yes else ac_cv_prog_cc_g=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 if test "$ac_test_CFLAGS" = set; then CFLAGS="$ac_save_CFLAGS" elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi host_os=`uname` host_cpu=`uname -m` host_os1=`uname -rs` echo $ac_n "checking OS""... $ac_c" 1>&6 echo "configure:1048: checking OS" >&5 echo "$ac_t""$host_os" 1>&6 echo $ac_n "checking hardware""... $ac_c" 1>&6 echo "configure:1052: checking hardware" >&5 echo "$ac_t""$host_cpu" 1>&6 # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:1058: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_RANLIB="ranlib" break fi done IFS="$ac_save_ifs" test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" fi fi RANLIB="$ac_cv_prog_RANLIB" if test -n "$RANLIB"; then echo "$ac_t""$RANLIB" 1>&6 else echo "$ac_t""no" 1>&6 fi if test "x$ac_cv_prog_gcc" = xyes ; then CFLAGS="-O2 ${CFLAGS}" case "$host_os" in IRIX*) CFLAGS="-mabi=n32 -fno-builtins" LDFLAGS="-mabi=n32 -Wl,-rpath,/usr/lib32 ${LDFLAGS}" ;; esac else case "$host_os" in IRIX*) CFLAGS="-O2 -I/usr/freeware/include ${CFLAGS}" LDFLAGS="-n32 -L/usr/lib32 -L/usr/freeware/lib32 ${LDFLAGS}" ;; OSF*) CFLAGS="-O -assume noaligned_objects ${CFLAGS}" ;; esac fi echo $ac_n "checking whether to enable debugging compiler options""... $ac_c" 1>&6 echo "configure:1107: checking whether to enable debugging compiler options" >&5 # Check whether --enable-debug or --disable-debug was given. if test "${enable_debug+set}" = set; then enableval="$enable_debug" echo "$ac_t""yes" 1>&6 tmp_CFLAGS=`echo $CFLAGS | sed 's/O2/O0/g'` CFLAGS="$tmp_CFLAGS" CFLAGS="$CFLAGS -g -W -Wall" else #CFLAGS="$CFLAGS -Waggregate-return" #CFLAGS="$CFLAGS -Wcast-align -Wcast-qual -Wnested-externs" #CFLAGS="$CFLAGS -Wshadow -Wbad-function-cast -Wwrite-strings" echo "$ac_t""no" 1>&6 fi echo $ac_n "checking whether to relax compiler optimizations""... $ac_c" 1>&6 echo "configure:1125: checking whether to relax compiler optimizations" >&5 # Check whether --enable-relax or --disable-relax was given. if test "${enable_relax+set}" = set; then enableval="$enable_relax" echo "$ac_t""yes" 1>&6 tmp_CFLAGS=`echo $CFLAGS | sed 's/O2/O0/g'` CFLAGS="$tmp_CFLAGS" else echo "$ac_t""no" 1>&6 fi case "$host_os" in OSF*) cat >> confdefs.h <<\EOF #define OSF1 1 EOF ;; Sun*) cat >> confdefs.h <<\EOF #define SOLARIS 1 EOF LIBS="-lresolv -lsocket -lnsl ${LIBS}" ;; IRIX*) cat >> confdefs.h <<\EOF #define IRIX 1 EOF ;; esac case "$host_os1" in "FreeBSD 4"*) cat >> confdefs.h <<\EOF #define FBSD4 1 EOF ;; "DragonFly"*) cat >> confdefs.h <<\EOF #define FBSD4 1 EOF ;; esac case "$host_cpu" in sun*) cat >> confdefs.h <<\EOF #define CPU_sparc 1 EOF ;; esac # Extract the first word of "gmake", so it can be a program name with args. set dummy gmake; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:1187: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_MAKE'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$MAKE"; then ac_cv_prog_MAKE="$MAKE" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_MAKE="gmake" break fi done IFS="$ac_save_ifs" fi fi MAKE="$ac_cv_prog_MAKE" if test -n "$MAKE"; then echo "$ac_t""$MAKE" 1>&6 else echo "$ac_t""no" 1>&6 fi if test x"$MAKE" = x""; then # Extract the first word of "make", so it can be a program name with args. set dummy make; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:1217: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_MAKE'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$MAKE"; then ac_cv_prog_MAKE="$MAKE" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_MAKE="make" break fi done IFS="$ac_save_ifs" fi fi MAKE="$ac_cv_prog_MAKE" if test -n "$MAKE"; then echo "$ac_t""$MAKE" 1>&6 else echo "$ac_t""no" 1>&6 fi fi echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 echo "configure:1246: checking whether ${MAKE-make} sets \${MAKE}" >&5 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftestmake <<\EOF all: @echo 'ac_maketemp="${MAKE}"' EOF # GNU make sometimes prints "make[1]: Entering...", which would confuse us. eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` if test -n "$ac_maketemp"; then eval ac_cv_prog_make_${ac_make}_set=yes else eval ac_cv_prog_make_${ac_make}_set=no fi rm -f conftestmake fi if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then echo "$ac_t""yes" 1>&6 SET_MAKE= else echo "$ac_t""no" 1>&6 SET_MAKE="MAKE=${MAKE-make}" fi echo $ac_n "checking for __progname""... $ac_c" 1>&6 echo "configure:1274: checking for __progname" >&5 cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* echo "$ac_t""yes" 1>&6; cat >> confdefs.h <<\EOF #define PROGNAME 1 EOF else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* echo "$ac_t""no" 1>&6 fi rm -f conftest* echo $ac_n "checking for extra flags needed to export symbols""... $ac_c" 1>&6 echo "configure:1299: checking for extra flags needed to export symbols" >&5 if test "x$ac_cv_prog_gcc" = xyes ; then case $host_os in aix4*|aix5*) CFLAGS="${CFLAGS} -Wl,-bexpall,-brtl" ;; *) save_ldflags="${LDFLAGS}" LDFLAGS="-Wl,--export-dynamic ${save_ldflags}" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* echo "$ac_t""--export-dynamic" 1>&6 else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* LDFLAGS="-Wl,-Bexport ${save_ldflags}" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* echo "$ac_t""-Bexport" 1>&6 else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* echo "$ac_t""none" 1>&6 LDFLAGS="${save_ldflags}" fi rm -f conftest* fi rm -f conftest* ;; esac else echo "$ac_t""none" 1>&6 fi echo $ac_n "checking for static inline""... $ac_c" 1>&6 echo "configure:1354: checking for static inline" >&5 cat > conftest.$ac_ext < static inline func() { } int main() { func(); ; return 0; } EOF if { (eval echo configure:1370: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* echo "$ac_t""yes" 1>&6 else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* echo "$ac_t""no" 1>&6; cat >> confdefs.h <<\EOF #define NOINLINE 1 EOF fi rm -f conftest* ac_cv_endianess="unknown" if test x"$ac_cv_endianess" = x"unknown"; then echo $ac_n "checking endianess""... $ac_c" 1>&6 echo "configure:1387: checking endianess" >&5 if test "$cross_compiling" = yes; then ac_cv_endianess="little" else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_endianess="little" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* ac_cv_endianess="big" fi rm -fr conftest* fi echo "$ac_t""$ac_cv_endianess" 1>&6 fi if test x"$ac_cv_endianess" = x"big"; then cat >> confdefs.h <<\EOF #define IM_BIG_ENDIAN 1 EOF fi if test x"$ac_cv_endianess" = x"little"; then cat >> confdefs.h <<\EOF #define IM_LITTLE_ENDIAN 1 EOF fi ac_cv_unaligned="unknown" case "$host_cpu" in alpha*|arm*|hp*|mips*|sh*|sparc*|ia64|nv1) ac_cv_unaligned="fail" echo $ac_n "checking unaligned accesses""... $ac_c" 1>&6 echo "configure:1438: checking unaligned accesses" >&5 echo "$ac_t""$ac_cv_unaligned" 1>&6 ;; esac if test x"$ac_cv_unaligned" = x"unknown"; then echo $ac_n "checking unaligned accesses""... $ac_c" 1>&6 echo "configure:1445: checking unaligned accesses" >&5 cat > conftest.c << EOF #include #include #include unsigned char a[5] = { 1, 2, 3, 4, 5 }; main () { unsigned int i; pid_t pid; int status; /* avoid "core dumped" message */ pid = fork(); if (pid < 0) exit(2); if (pid > 0) { /* parent */ pid = waitpid(pid, &status, 0); if (pid < 0) exit(3); exit(!WIFEXITED(status)); } /* child */ i = *(unsigned int *)&a[1]; printf("%d\n", i); exit(0); } EOF ${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS \ conftest.c $LIBS >/dev/null 2>&1 if test ! -x conftest ; then ac_cv_unaligned="fail" else ./conftest >conftest.out if test ! -s conftest.out ; then ac_cv_unaligned="fail" else ac_cv_unaligned="ok" fi fi rm -f conftest* core core.conftest echo "$ac_t""$ac_cv_unaligned" 1>&6 fi if test x"$ac_cv_unaligned" = x"fail"; then cat >> confdefs.h <<\EOF #define NEED_ALIGN 1 EOF fi echo $ac_n "checking whether to disable L2 features""... $ac_c" 1>&6 echo "configure:1492: checking whether to disable L2 features" >&5 # Check whether --enable-l2 or --disable-l2 was given. if test "${enable_l2+set}" = set; then enableval="$enable_l2" if test x$enableval = x"yes" ; then echo "$ac_t""no" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_L2 1 EOF else echo "$ac_t""yes" 1>&6 fi else echo "$ac_t""no" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_L2 1 EOF fi echo $ac_n "checking whether to enable IPv6 code""... $ac_c" 1>&6 echo "configure:1517: checking whether to enable IPv6 code" >&5 # Check whether --enable-ipv6 or --disable-ipv6 was given. if test "${enable_ipv6+set}" = set; then enableval="$enable_ipv6" echo "$ac_t""yes" 1>&6 for ac_func in inet_pton do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:1526: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:1554: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done if test x"$ac_cv_func_inet_pton" = x"no"; then { echo "configure: error: ERROR: missing inet_pton(); disable IPv6 hooks !" 1>&2; exit 1; } fi for ac_func in inet_ntop do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:1585: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:1613: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done if test x"$ac_cv_func_inet_ntop" = x"no"; then { echo "configure: error: ERROR: missing inet_ntop(); disable IPv6 hooks !" 1>&2; exit 1; } fi cat >> confdefs.h <<\EOF #define ENABLE_IPV6 1 EOF ipv6support="yes" case "$host_os" in IRIX*) cat >> confdefs.h <<\EOF #define INET6 1 EOF ;; esac else echo "$ac_t""no" 1>&6 ipv6support="no" fi if test $ipv6support = "yes"; then echo $ac_n "checking whether to enable IPv4-mapped IPv6 sockets ""... $ac_c" 1>&6 echo "configure:1663: checking whether to enable IPv4-mapped IPv6 sockets " >&5 # Check whether --enable-v4-mapped or --disable-v4-mapped was given. if test "${enable_v4_mapped+set}" = set; then enableval="$enable_v4_mapped" if test x$enableval = x"yes" ; then cat >> confdefs.h <<\EOF #define V4_MAPPED 1 EOF echo "$ac_t""yes" 1>&6 else echo "$ac_t""no" 1>&6 fi else case $host_os in *FreeBSD*|*NetBSD*|*OpenBSD*) echo "$ac_t""no" 1>&6 ;; *) cat >> confdefs.h <<\EOF #define V4_MAPPED 1 EOF echo "$ac_t""yes" 1>&6 ;; esac fi fi # Check whether --with-pcap-includes or --without-pcap-includes was given. if test "${with_pcap_includes+set}" = set; then withval="$with_pcap_includes" absdir=`cd $withval 2>/dev/null && pwd` if test x$absdir != x ; then withval=$absdir fi INCLUDES="${INCLUDES} -I$withval" PCAPINCLS=$withval PCAPINCLUDESFOUND=1 fi if test x"$PCAPINCLS" != x""; then echo $ac_n "checking your own pcap includes""... $ac_c" 1>&6 echo "configure:1715: checking your own pcap includes" >&5 if test -r $PCAPINCLS/pcap.h; then echo "$ac_t""ok" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_PCAP_H 1 EOF else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: missing pcap.h in $PCAPINCLS" 1>&2; exit 1; } fi fi if test x"$PCAPINCLUDESFOUND" = x""; then echo $ac_n "checking default locations for pcap.h""... $ac_c" 1>&6 echo "configure:1730: checking default locations for pcap.h" >&5 if test -r /usr/include/pcap.h; then echo "$ac_t""found in /usr/include" 1>&6 PCAPINCLUDESFOUND=1 cat >> confdefs.h <<\EOF #define HAVE_PCAP_H 1 EOF elif test -r /usr/include/pcap/pcap.h; then echo "$ac_t""found in /usr/include" 1>&6 PCAPINCLUDESFOUND=1 cat >> confdefs.h <<\EOF #define HAVE_PCAP_PCAP_H 1 EOF elif test -r /usr/local/include/pcap.h; then echo "$ac_t""found in /usr/local/include" 1>&6 INCLUDES="${INCLUDES} -I/usr/local/include" PCAPINCLUDESFOUND=1 cat >> confdefs.h <<\EOF #define HAVE_PCAP_H 1 EOF elif test -r /usr/local/include/pcap/pcap.h; then echo "$ac_t""found in /usr/local/include" 1>&6 INCLUDES="${INCLUDES} -I/usr/local/include" PCAPINCLUDESFOUND=1 cat >> confdefs.h <<\EOF #define HAVE_PCAP_PCAP_H 1 EOF fi if test x"$PCAPINCLUDESFOUND" = x""; then echo "$ac_t""not found" 1>&6 { echo "configure: error: ERROR: missing pcap.h" 1>&2; exit 1; } fi fi # Check whether --with-pcap-libs or --without-pcap-libs was given. if test "${with_pcap_libs+set}" = set; then withval="$with_pcap_libs" absdir=`cd $withval 2>/dev/null && pwd` if test x$absdir != x ; then withval=$absdir fi LIBS="${LIBS} -L$withval" PCAPLIB=$withval PCAPLIBFOUND=1 fi if test x"$PCAPLIB" != x""; then echo $ac_n "checking your own pcap libraries""... $ac_c" 1>&6 echo "configure:1787: checking your own pcap libraries" >&5 if test -r $PCAPLIB/libpcap.a -o -r $PCAPLIB/libpcap.so; then echo "$ac_t""ok" 1>&6 PCAP_LIB_FOUND=1 echo $ac_n "checking for PF_RING library""... $ac_c" 1>&6 echo "configure:1792: checking for PF_RING library" >&5 if test -r $PCAPLIB/libpfring.a -o -r $PCAPLIB/libpfring.so; then LIBS="${LIBS} -lpcap -lpfring" echo "$ac_t""yes" 1>&6 PFRING_LIB_FOUND=1 else echo "$ac_t""no" 1>&6 fi else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: unable to find pcap library in $PCAPLIB" 1>&2; exit 1; } fi fi if test x"$PCAPLIBFOUND" = x""; then echo $ac_n "checking default locations for libpcap""... $ac_c" 1>&6 echo "configure:1808: checking default locations for libpcap" >&5 if test -r /usr/local/lib/libpcap.a -o -r /usr/local/lib/libpcap.so; then LIBS="${LIBS} -L/usr/local/lib" echo "$ac_t""found in /usr/local/lib" 1>&6 PCAPLIBFOUND=1 echo $ac_n "checking for PF_RING library""... $ac_c" 1>&6 echo "configure:1814: checking for PF_RING library" >&5 if test -r /usr/local/lib/libpfring.a -o -r /usr/local/lib/libpfring.so; then LIBS="${LIBS} -lpcap -lpfring" echo "$ac_t""yes" 1>&6 PFRING_LIB_FOUND=1 else echo "$ac_t""no" 1>&6 fi else echo "$ac_t""no" 1>&6 fi fi if test x"$PFRING_LIB_FOUND" = x""; then echo $ac_n "checking for pcap_dispatch in -lpcap""... $ac_c" 1>&6 echo "configure:1830: checking for pcap_dispatch in -lpcap" >&5 ac_lib_var=`echo pcap'_'pcap_dispatch | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lpcap $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_lib=HAVE_LIB`echo pcap | sed -e 's/[^a-zA-Z0-9_]/_/g' \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` cat >> confdefs.h <&6 { echo "configure: error: ERROR: missing pcap library. Refer to: http://www.tcpdump.org/ " 1>&2; exit 1; } fi echo $ac_n "checking for pcap_setnonblock in -lpcap""... $ac_c" 1>&6 echo "configure:1881: checking for pcap_setnonblock in -lpcap" >&5 ac_lib_var=`echo pcap'_'pcap_setnonblock | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lpcap $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define PCAP_7 1 EOF else echo "$ac_t""no" 1>&6 fi fi echo $ac_n "checking packet capture type""... $ac_c" 1>&6 echo "configure:1926: checking packet capture type" >&5 if test -r /dev/bpf0 ; then V_PCAP=bpf elif test -r /usr/include/net/pfilt.h ; then V_PCAP=pf elif test -r /dev/enet ; then V_PCAP=enet elif test -r /dev/nit ; then V_PCAP=snit elif test -r /usr/include/sys/net/nit.h ; then V_PCAP=nit elif test -r /usr/include/linux/socket.h ; then V_PCAP=linux elif test -r /usr/include/net/raw.h ; then V_PCAP=snoop elif test -r /usr/include/odmi.h ; then # # On AIX, the BPF devices might not yet be present - they're # created the first time libpcap runs after booting. # We check for odmi.h instead. # V_PCAP=bpf elif test -r /usr/include/sys/dlpi.h ; then V_PCAP=dlpi elif test -c /dev/bpf0 ; then # check again in case not readable V_PCAP=bpf elif test -c /dev/enet ; then # check again in case not readable V_PCAP=enet elif test -c /dev/nit ; then # check again in case not readable V_PCAP=snit else V_PCAP=null fi echo "$ac_t""$V_PCAP" 1>&6 cat >> confdefs.h <&6 echo "configure:1966: checking whether to enable MySQL support" >&5 echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 echo "configure:1968: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else # This must be in double quotes, not single quotes, because CPP may get # substituted into the Makefile and "${CC-cc}" will confuse make. CPP="${CC-cc} -E" # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1989: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2006: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP="${CC-cc} -nologo -E" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2023: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP=/lib/cpp fi rm -f conftest* fi rm -f conftest* fi rm -f conftest* ac_cv_prog_CPP="$CPP" fi CPP="$ac_cv_prog_CPP" else ac_cv_prog_CPP="$CPP" fi echo "$ac_t""$CPP" 1>&6 # Check whether --enable-mysql or --disable-mysql was given. if test "${enable_mysql+set}" = set; then enableval="$enable_mysql" case "$enableval" in yes) echo "$ac_t""yes" 1>&6 USING_SQL="yes" # Check whether --with-mysql-libs or --without-mysql-libs was given. if test "${with_mysql_libs+set}" = set; then withval="$with_mysql_libs" absdir=`cd $withval 2>/dev/null && pwd` if test x$absdir != x ; then withval=$absdir fi LIBS="${LIBS} -L$withval" MYSQLLIB=$withval MYSQLLIBFOUND=1 fi if test x"$MYSQLLIB" != x""; then echo $ac_n "checking your own MySQL client library""... $ac_c" 1>&6 echo "configure:2074: checking your own MySQL client library" >&5 if test -r $MYSQLLIB/libmysqlclient.a -o -r $MYSQLLIB/libmysqlclient.so; then echo "$ac_t""ok" 1>&6 else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: missing MySQL client library in $MYSQLLIB" 1>&2; exit 1; } fi fi if test x"$MYSQLLIBFOUND" = x""; then echo $ac_n "checking default locations for libmysqlclient""... $ac_c" 1>&6 echo "configure:2085: checking default locations for libmysqlclient" >&5 if test -r /usr/lib/mysql/libmysqlclient.a -o -r /usr/lib/mysql/libmysqlclient.so; then LIBS="${LIBS} -L/usr/lib/mysql" echo "$ac_t""found in /usr/lib/mysql" 1>&6 MYSQLLIBFOUND=1 elif test -r /usr/local/mysql/lib/libmysqlclient.a -o -r /usr/local/mysql/lib/libmysqlclient.so; then LIBS="${LIBS} -L/usr/local/mysql/lib" echo "$ac_t""found in /usr/local/mysql/lib" 1>&6 MYSQLLIBFOUND=1 elif test -r /usr/local/lib/mysql/libmysqlclient.a -o -r /usr/local/lib/mysql/libmysqlclient.so; then LIBS="${LIBS} -L/usr/local/lib/mysql" echo "$ac_t""found in /usr/local/lib/mysql" 1>&6 MYSQLLIBFOUND=1 else echo "$ac_t""not found" 1>&6 fi fi if test x"$MYSQLLIBFOUND" = x""; then echo $ac_n "checking for mysql_real_connect in -lmysqlclient""... $ac_c" 1>&6 echo "configure:2105: checking for mysql_real_connect in -lmysqlclient" >&5 ac_lib_var=`echo mysqlclient'_'mysql_real_connect | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lmysqlclient $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_lib=HAVE_LIB`echo mysqlclient | sed -e 's/[^a-zA-Z0-9_]/_/g' \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` cat >> confdefs.h <&6 { echo "configure: error: ERROR: missing MySQL client library. Refer to: http://www.mysql.com/ " 1>&2; exit 1; } fi else LIBS="${LIBS} -lmysqlclient" case "$host_os" in Sun*) LIBS="${LIBS} -lrt" ;; esac fi # Check whether --with-mysql-includes or --without-mysql-includes was given. if test "${with_mysql_includes+set}" = set; then withval="$with_mysql_includes" absdir=`cd $withval 2>/dev/null && pwd` if test x$absdir != x ; then withval=$absdir fi INCLUDES="${INCLUDES} -I$withval" MYSQLINCLUDES=$withval MYSQLINCLUDESFOUND=1 fi if test x"$MYSQLINCLUDES" != x""; then echo $ac_n "checking your own MySQL headers""... $ac_c" 1>&6 echo "configure:2182: checking your own MySQL headers" >&5 if test -r $MYSQLINCLUDES/mysql/mysql.h; then echo "$ac_t""ok" 1>&6 elif test -r $MYSQLINCLUDES/mysql.h; then echo "$ac_t""ok" 1>&6 cat >> confdefs.h <<\EOF #define CUT_MYSQLINCLUDES_DIR 1 EOF else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: missing MySQL headers in $MYSQLINCLUDES" 1>&2; exit 1; } fi fi if test x"$MYSQLINCLUDESFOUND" = x""; then echo $ac_n "checking default locations for mysql.h""... $ac_c" 1>&6 echo "configure:2199: checking default locations for mysql.h" >&5 if test -r /usr/include/mysql/mysql.h; then echo "$ac_t""found in /usr/include/mysql" 1>&6 MYSQLINCLUDESFOUND=1; elif test -r /usr/local/include/mysql/mysql.h; then INCLUDES="${INCLUDES} -I/usr/local/include" echo "$ac_t""found in /usr/local/include/mysql" 1>&6 MYSQLINCLUDESFOUND=1; elif test -r /usr/local/mysql/include/mysql.h; then INCLUDES="${INCLUDES} -I/usr/local/mysql/include" echo "$ac_t""found in /usr/local/mysql/include" 1>&6 cat >> confdefs.h <<\EOF #define CUT_MYSQLINCLUDES_DIR 1 EOF MYSQLINCLUDESFOUND=1; fi if test x"$MYSQLINCLUDESFOUND" = x""; then echo "$ac_t""not found" 1>&6 fi fi if test x"$MYSQLINCLUDESFOUND" = x""; then ac_safe=`echo "mysql/mysql.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for mysql/mysql.h""... $ac_c" 1>&6 echo "configure:2224: checking for mysql/mysql.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2234: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 : else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: missing MySQL headers" 1>&2; exit 1; } fi fi cat >> confdefs.h <<\EOF #define WITH_MYSQL 1 EOF PLUGINS="${PLUGINS} mysql_plugin.c" EXTRABIN="${EXTRABIN} pmmyplay" ;; no) echo "$ac_t""no" 1>&6 ;; esac else echo "$ac_t""no" 1>&6 fi echo $ac_n "checking whether to enable PostgreSQL support""... $ac_c" 1>&6 echo "configure:2275: checking whether to enable PostgreSQL support" >&5 # Check whether --enable-pgsql or --disable-pgsql was given. if test "${enable_pgsql+set}" = set; then enableval="$enable_pgsql" case "$enableval" in yes) echo "$ac_t""yes" 1>&6 USING_SQL="yes" # Check whether --with-pgsql-libs or --without-pgsql-libs was given. if test "${with_pgsql_libs+set}" = set; then withval="$with_pgsql_libs" absdir=`cd $withval 2>/dev/null && pwd` if test x$absdir != x ; then withval=$absdir fi LIBS="${LIBS} -L$withval" PGSQLLIB=$withval PGSQLLIBFOUND=1 fi if test x"$PGSQLLIB" != x""; then echo $ac_n "checking your own PostgreSQL client library""... $ac_c" 1>&6 echo "configure:2303: checking your own PostgreSQL client library" >&5 if test -r $PGSQLLIB/libpq.a -o -r $PGSQLLIB/libpq.so; then echo "$ac_t""ok" 1>&6 else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: missing PostgreSQL client library in $PGSQLLIB" 1>&2; exit 1; } fi fi if test x"$PGSQLLIBFOUND" = x""; then echo $ac_n "checking default locations for libpq""... $ac_c" 1>&6 echo "configure:2314: checking default locations for libpq" >&5 if test -r /usr/lib/libpq.a -o -r /usr/lib/libpq.so; then echo "$ac_t""found in /usr/lib" 1>&6 PGSQLLIBFOUND=1 elif test -r /usr/local/lib/libpq.a -o -r /usr/local/lib/libpq.so; then LIBS="${LIBS} -L/usr/local/lib" echo "$ac_t""found in /usr/local/lib" 1>&6 PGSQLLIBFOUND=1 elif test -r /usr/local/pgsql/lib/libpq.a -o -r /usr/local/pgsql/lib/libpq.so; then LIBS="${LIBS} -L/usr/local/pgsql/lib" echo "$ac_t""found in /usr/local/pgsql/lib" 1>&6 PGSQLLIBFOUND=1 else echo "$ac_t""not found" 1>&6 fi fi if test x"$PGSQLLIBFOUND" = x""; then echo $ac_n "checking for PQconnectdb in -lpq""... $ac_c" 1>&6 echo "configure:2333: checking for PQconnectdb in -lpq" >&5 ac_lib_var=`echo pq'_'PQconnectdb | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lpq $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_lib=HAVE_LIB`echo pq | sed -e 's/[^a-zA-Z0-9_]/_/g' \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` cat >> confdefs.h <&6 { echo "configure: error: ERROR: missing PQ library. Refer to: http://www.postgresql.org/ " 1>&2; exit 1; } fi else LIBS="${LIBS} -lpq" fi # Check whether --with-pgsql-includes or --without-pgsql-includes was given. if test "${with_pgsql_includes+set}" = set; then withval="$with_pgsql_includes" absdir=`cd $withval 2>/dev/null && pwd` if test x$absdir != x ; then withval=$absdir fi INCLUDES="${INCLUDES} -I$withval" PGSQLINCLUDES=$withval PGSQLINCLUDESFOUND=1 fi if test x"$PGSQLINCLUDES" != x""; then echo $ac_n "checking your own PostgreSQL headers""... $ac_c" 1>&6 echo "configure:2405: checking your own PostgreSQL headers" >&5 if test -r $PGSQLINCLUDES/libpq-fe.h; then echo "$ac_t""ok" 1>&6 else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: missing pgsql headers in $PGSQLINCLUDES" 1>&2; exit 1; } fi fi if test x"$PGSQLINCLUDESFOUND" = x""; then echo $ac_n "checking default locations for libpq-fe.h""... $ac_c" 1>&6 echo "configure:2416: checking default locations for libpq-fe.h" >&5 if test -r /usr/include/libpq-fe.h; then echo "$ac_t""found in /usr/include" 1>&6 PGSQLINCLUDESFOUND=1; elif test -r /usr/local/include/libpq-fe.h; then echo "$ac_t""found in /usr/local/include" 1>&6 INCLUDES="${INCLUDES} -I/usr/local/include" PGSQLINCLUDESFOUND=1; elif test -r /usr/local/pgsql/include/libpq-fe.h; then echo "$ac_t""found in /usr/local/pgsql/include" 1>&6 INCLUDES="${INCLUDES} -I/usr/local/pgsql/include" PGSQLINCLUDESFOUND=1; fi if test x"$PGSQLINCLUDESFOUND" = x""; then echo "$ac_t""not found" 1>&6 fi fi if test x"$PGSQLINCLUDESFOUND" = x""; then ac_safe=`echo "libpq-fe.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for libpq-fe.h""... $ac_c" 1>&6 echo "configure:2437: checking for libpq-fe.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2447: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 : else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: missing PostgreSQL headers" 1>&2; exit 1; } fi fi cat >> confdefs.h <<\EOF #define WITH_PGSQL 1 EOF PLUGINS="${PLUGINS} pgsql_plugin.c" EXTRABIN="${EXTRABIN} pmpgplay" ;; no) echo "$ac_t""no" 1>&6 ;; esac else echo "$ac_t""no" 1>&6 fi echo $ac_n "checking whether to enable SQLite3 support""... $ac_c" 1>&6 echo "configure:2488: checking whether to enable SQLite3 support" >&5 # Check whether --enable-sqlite3 or --disable-sqlite3 was given. if test "${enable_sqlite3+set}" = set; then enableval="$enable_sqlite3" case "$enableval" in yes) echo "$ac_t""yes" 1>&6 USING_SQL="yes" # Check whether --with-sqlite3-libs or --without-sqlite3-libs was given. if test "${with_sqlite3_libs+set}" = set; then withval="$with_sqlite3_libs" absdir=`cd $withval 2>/dev/null && pwd` if test x$absdir != x ; then withval=$absdir fi LIBS="${LIBS} -L$withval" SQLITE3LIB=$withval SQLITE3LIBFOUND=1 fi if test x"$SQLITE3LIB" != x""; then echo $ac_n "checking your own SQLite3 client library""... $ac_c" 1>&6 echo "configure:2516: checking your own SQLite3 client library" >&5 if test -r $SQLITE3LIB/libsqlite3.a -o -r $SQLITE3LIB/libsqlite3.so; then echo "$ac_t""ok" 1>&6 else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: missing SQLite3 client library in $SQLITE3LIB" 1>&2; exit 1; } fi fi if test x"$SQLITE3LIBFOUND" = x""; then echo $ac_n "checking default locations for libsqlite3""... $ac_c" 1>&6 echo "configure:2527: checking default locations for libsqlite3" >&5 if test -r /usr/lib/libsqlite3.a -o -r /usr/lib/libsqlite3.so; then # LIBS="${LIBS} -L/usr/lib/sqlite3" echo "$ac_t""found in /usr/lib" 1>&6 SQLITE3LIBFOUND=1 elif test -r /usr/local/sqlite3/lib/libsqlite3.a -o -r /usr/local/sqlite3/lib/libsqlite3.so; then LIBS="${LIBS} -L/usr/local/sqlite3/lib" echo "$ac_t""found in /usr/local/sqlite3/lib" 1>&6 SQLITE3LIBFOUND=1 elif test -r /usr/local/lib/libsqlite3.a -o -r /usr/local/lib/libsqlite3.so; then LIBS="${LIBS} -L/usr/local/lib" echo "$ac_t""found in /usr/local/lib" 1>&6 SQLITE3LIBFOUND=1 else echo "$ac_t""not found" 1>&6 fi fi if test x"$SQLITE3LIBFOUND" = x""; then echo $ac_n "checking for sqlite3_open in -lsqlite3""... $ac_c" 1>&6 echo "configure:2547: checking for sqlite3_open in -lsqlite3" >&5 ac_lib_var=`echo sqlite3'_'sqlite3_open | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lsqlite3 $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_lib=HAVE_LIB`echo sqlite3 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` cat >> confdefs.h <&6 { echo "configure: error: ERROR: missing SQLite3 client library. Refer to: http://sqlite.org/ " 1>&2; exit 1; } fi else LIBS="${LIBS} -lsqlite3" fi # Check whether --with-sqlite3-includes or --without-sqlite3-includes was given. if test "${with_sqlite3_includes+set}" = set; then withval="$with_sqlite3_includes" absdir=`cd $withval 2>/dev/null && pwd` if test x$absdir != x ; then withval=$absdir fi INCLUDES="${INCLUDES} -I$withval" SQLITE3INCLUDES=$withval SQLITE3INCLUDESFOUND=1 fi if test x"$SQLITE3INCLUDES" != x""; then echo $ac_n "checking your own SQLite3 headers""... $ac_c" 1>&6 echo "configure:2619: checking your own SQLite3 headers" >&5 if test -r $SQLITE3INCLUDES/sqlite3.h; then echo "$ac_t""ok" 1>&6 else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: missing SQLite3 headers in $SQLITE3INCLUDES" 1>&2; exit 1; } fi fi if test x"$SQLITE3INCLUDESFOUND" = x""; then echo $ac_n "checking default locations for sqlite3.h""... $ac_c" 1>&6 echo "configure:2630: checking default locations for sqlite3.h" >&5 if test -r /usr/include/sqlite3.h; then echo "$ac_t""found in /usr/include" 1>&6 SQLITE3INCLUDESFOUND=1; elif test -r /usr/local/include/sqlite3.h; then # INCLUDES="${INCLUDES} -I/usr/local/include" echo "$ac_t""found in /usr/local/include" 1>&6 SQLITE3INCLUDESFOUND=1; elif test -r /usr/local/sqlite3/include/sqlite3.h; then INCLUDES="${INCLUDES} -I/usr/local/sqlite3/include" echo "$ac_t""found in /usr/local/sqlite3/include" 1>&6 SQLITE3INCLUDESFOUND=1; fi if test x"$SQLITE3INCLUDESFOUND" = x""; then echo "$ac_t""not found" 1>&6 fi fi if test x"$SQLITE3INCLUDESFOUND" = x""; then ac_safe=`echo "sqlite3.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for sqlite3.h""... $ac_c" 1>&6 echo "configure:2651: checking for sqlite3.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2661: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 : else echo "$ac_t""no" 1>&6 { echo "configure: error: ERROR: missing SQLite3 headers" 1>&2; exit 1; } fi fi cat >> confdefs.h <<\EOF #define WITH_SQLITE3 1 EOF PLUGINS="${PLUGINS} sqlite3_plugin.c" ;; no) echo "$ac_t""no" 1>&6 ;; esac else echo "$ac_t""no" 1>&6 fi echo $ac_n "checking whether to disable shared objects""... $ac_c" 1>&6 echo "configure:2701: checking whether to disable shared objects" >&5 # Check whether --enable-so or --disable-so was given. if test "${enable_so+set}" = set; then enableval="$enable_so" if test x$enableval = x"yes" ; then echo "$ac_t""no" 1>&6 echo $ac_n "checking for dlopen""... $ac_c" 1>&6 echo "configure:2708: checking for dlopen" >&5 if eval "test \"`echo '$''{'ac_cv_func_dlopen'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char dlopen(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_dlopen) || defined (__stub___dlopen) choke me #else dlopen(); #endif ; return 0; } EOF if { (eval echo configure:2736: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_dlopen=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_dlopen=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'dlopen`\" = yes"; then echo "$ac_t""yes" 1>&6 USING_DLOPEN="yes" else echo "$ac_t""no" 1>&6 fi if test x"$USING_DLOPEN" != x"yes"; then echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6 echo "configure:2757: checking for dlopen in -ldl" >&5 ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-ldl $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 USING_DLOPEN="yes" LIBS="${LIBS} -ldl" else echo "$ac_t""no" 1>&6 { echo "configure: error: Unable to find dlopen(). Try with --disable-so" 1>&2; exit 1; } fi fi else echo "$ac_t""yes" 1>&6 fi else echo "$ac_t""no" 1>&6 echo $ac_n "checking for dlopen""... $ac_c" 1>&6 echo "configure:2806: checking for dlopen" >&5 if eval "test \"`echo '$''{'ac_cv_func_dlopen'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char dlopen(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_dlopen) || defined (__stub___dlopen) choke me #else dlopen(); #endif ; return 0; } EOF if { (eval echo configure:2834: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_dlopen=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_dlopen=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'dlopen`\" = yes"; then echo "$ac_t""yes" 1>&6 USING_DLOPEN="yes" else echo "$ac_t""no" 1>&6 fi if test x"$USING_DLOPEN" != x"yes"; then echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6 echo "configure:2855: checking for dlopen in -ldl" >&5 ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-ldl $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 USING_DLOPEN="yes" LIBS="${LIBS} -ldl" else echo "$ac_t""no" 1>&6 { echo "configure: error: Unable to find dlopen(). Try with --disable-so" 1>&2; exit 1; } fi fi fi if test x"$USING_DLOPEN" = x"yes"; then cat >> confdefs.h <<\EOF #define HAVE_DLOPEN 1 EOF fi if test x"$USING_SQL" = x"yes"; then PLUGINS="${PLUGINS} sql_common.c sql_handlers.c log_templates.c preprocess.c" LIBS="${LIBS} -lm -lz" fi echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 echo "configure:2914: checking for ANSI C header files" >&5 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #include #include EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:2927: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* ac_cv_header_stdc=yes else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_header_stdc=no fi rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext < EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "memchr" >/dev/null 2>&1; then : else rm -rf conftest* ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat > conftest.$ac_ext < EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "free" >/dev/null 2>&1; then : else rm -rf conftest* ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat > conftest.$ac_ext < #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') #define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } EOF if { (eval echo configure:2994: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then : else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* ac_cv_header_stdc=no fi rm -fr conftest* fi fi fi echo "$ac_t""$ac_cv_header_stdc" 1>&6 if test $ac_cv_header_stdc = yes; then cat >> confdefs.h <<\EOF #define STDC_HEADERS 1 EOF fi echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6 echo "configure:3018: checking for sys/wait.h that is POSIX.1 compatible" >&5 if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #ifndef WEXITSTATUS #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED #define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif int main() { int s; wait (&s); s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; ; return 0; } EOF if { (eval echo configure:3039: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_header_sys_wait_h=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_header_sys_wait_h=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_header_sys_wait_h" 1>&6 if test $ac_cv_header_sys_wait_h = yes; then cat >> confdefs.h <<\EOF #define HAVE_SYS_WAIT_H 1 EOF fi for ac_hdr in getopt.h sys/select.h sys/time.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 echo "configure:3063: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:3073: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done echo $ac_n "checking for u_int64_t in sys/types.h""... $ac_c" 1>&6 echo "configure:3101: checking for u_int64_t in sys/types.h" >&5 ac_lib_var=`echo u_int64_t'_'sys/types.h | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else eval "ac_cv_type_$ac_lib_var='not-found'" ac_cv_check_typedef_header=`echo sys/types.h` cat > conftest.$ac_ext < int main() { int x = sizeof(u_int64_t); x = x; ; return 0; } EOF if { (eval echo configure:3116: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_type_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_type_$ac_lib_var=no" fi rm -f conftest* if test `eval echo '$ac_cv_type_'$ac_lib_var` = "no" ; then echo "$ac_t""no" 1>&6 else echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_U_INT64_T 1 EOF HAVE_U_INT64_T="1" fi fi echo $ac_n "checking for u_int32_t in sys/types.h""... $ac_c" 1>&6 echo "configure:3141: checking for u_int32_t in sys/types.h" >&5 ac_lib_var=`echo u_int32_t'_'sys/types.h | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else eval "ac_cv_type_$ac_lib_var='not-found'" ac_cv_check_typedef_header=`echo sys/types.h` cat > conftest.$ac_ext < int main() { int x = sizeof(u_int32_t); x = x; ; return 0; } EOF if { (eval echo configure:3156: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_type_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_type_$ac_lib_var=no" fi rm -f conftest* if test `eval echo '$ac_cv_type_'$ac_lib_var` = "no" ; then echo "$ac_t""no" 1>&6 else echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_U_INT32_T 1 EOF HAVE_U_INT32_T="1" fi fi echo $ac_n "checking for u_int16_t in sys/types.h""... $ac_c" 1>&6 echo "configure:3181: checking for u_int16_t in sys/types.h" >&5 ac_lib_var=`echo u_int16_t'_'sys/types.h | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else eval "ac_cv_type_$ac_lib_var='not-found'" ac_cv_check_typedef_header=`echo sys/types.h` cat > conftest.$ac_ext < int main() { int x = sizeof(u_int16_t); x = x; ; return 0; } EOF if { (eval echo configure:3196: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_type_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_type_$ac_lib_var=no" fi rm -f conftest* if test `eval echo '$ac_cv_type_'$ac_lib_var` = "no" ; then echo "$ac_t""no" 1>&6 else echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_U_INT16_T 1 EOF HAVE_U_INT16_T="1" fi fi echo $ac_n "checking for u_int8_t in sys/types.h""... $ac_c" 1>&6 echo "configure:3221: checking for u_int8_t in sys/types.h" >&5 ac_lib_var=`echo u_int8_t'_'sys/types.h | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else eval "ac_cv_type_$ac_lib_var='not-found'" ac_cv_check_typedef_header=`echo sys/types.h` cat > conftest.$ac_ext < int main() { int x = sizeof(u_int8_t); x = x; ; return 0; } EOF if { (eval echo configure:3236: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_type_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_type_$ac_lib_var=no" fi rm -f conftest* if test `eval echo '$ac_cv_type_'$ac_lib_var` = "no" ; then echo "$ac_t""no" 1>&6 else echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_U_INT8_T 1 EOF HAVE_U_INT8_T="1" fi fi echo $ac_n "checking for uint64_t in sys/types.h""... $ac_c" 1>&6 echo "configure:3261: checking for uint64_t in sys/types.h" >&5 ac_lib_var=`echo uint64_t'_'sys/types.h | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else eval "ac_cv_type_$ac_lib_var='not-found'" ac_cv_check_typedef_header=`echo sys/types.h` cat > conftest.$ac_ext < int main() { int x = sizeof(uint64_t); x = x; ; return 0; } EOF if { (eval echo configure:3276: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_type_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_type_$ac_lib_var=no" fi rm -f conftest* if test `eval echo '$ac_cv_type_'$ac_lib_var` = "no" ; then echo "$ac_t""no" 1>&6 else echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_UINT64_T 1 EOF HAVE_UINT64_T="1" fi fi echo $ac_n "checking for uint32_t in sys/types.h""... $ac_c" 1>&6 echo "configure:3301: checking for uint32_t in sys/types.h" >&5 ac_lib_var=`echo uint32_t'_'sys/types.h | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else eval "ac_cv_type_$ac_lib_var='not-found'" ac_cv_check_typedef_header=`echo sys/types.h` cat > conftest.$ac_ext < int main() { int x = sizeof(uint32_t); x = x; ; return 0; } EOF if { (eval echo configure:3316: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_type_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_type_$ac_lib_var=no" fi rm -f conftest* if test `eval echo '$ac_cv_type_'$ac_lib_var` = "no" ; then echo "$ac_t""no" 1>&6 else echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_UINT32_T 1 EOF HAVE_UINT32_T="1" fi fi echo $ac_n "checking for uint16_t in sys/types.h""... $ac_c" 1>&6 echo "configure:3341: checking for uint16_t in sys/types.h" >&5 ac_lib_var=`echo uint16_t'_'sys/types.h | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else eval "ac_cv_type_$ac_lib_var='not-found'" ac_cv_check_typedef_header=`echo sys/types.h` cat > conftest.$ac_ext < int main() { int x = sizeof(uint16_t); x = x; ; return 0; } EOF if { (eval echo configure:3356: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_type_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_type_$ac_lib_var=no" fi rm -f conftest* if test `eval echo '$ac_cv_type_'$ac_lib_var` = "no" ; then echo "$ac_t""no" 1>&6 else echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_UINT16_T 1 EOF HAVE_UINT16_T="1" fi fi echo $ac_n "checking for uint8_t in sys/types.h""... $ac_c" 1>&6 echo "configure:3381: checking for uint8_t in sys/types.h" >&5 ac_lib_var=`echo uint8_t'_'sys/types.h | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else eval "ac_cv_type_$ac_lib_var='not-found'" ac_cv_check_typedef_header=`echo sys/types.h` cat > conftest.$ac_ext < int main() { int x = sizeof(uint8_t); x = x; ; return 0; } EOF if { (eval echo configure:3396: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_type_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_type_$ac_lib_var=no" fi rm -f conftest* if test `eval echo '$ac_cv_type_'$ac_lib_var` = "no" ; then echo "$ac_t""no" 1>&6 else echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_UINT8_T 1 EOF HAVE_UINT8_T="1" fi fi echo $ac_n "checking whether to enable 64bit counters""... $ac_c" 1>&6 echo "configure:3422: checking whether to enable 64bit counters" >&5 # Check whether --enable-64bit or --disable-64bit was given. if test "${enable_64bit+set}" = set; then enableval="$enable_64bit" if test x$enableval = x"yes" ; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define HAVE_64BIT_COUNTERS 1 EOF else echo "$ac_t""no" 1>&6 fi else echo "$ac_t""no" 1>&6 fi echo $ac_n "checking whether to enable multithreading in pmacctd""... $ac_c" 1>&6 echo "configure:3443: checking whether to enable multithreading in pmacctd" >&5 # Check whether --enable-threads or --disable-threads was given. if test "${enable_threads+set}" = set; then enableval="$enable_threads" if test x$enableval = x"yes" ; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF #define ENABLE_THREADS 1 EOF case "$host" in *-linux-*) cat >> confdefs.h <<\EOF #define _XOPEN_SOURCE 600 EOF cat >> confdefs.h <<\EOF #define _GNU_SOURCE 1 EOF ;; esac LIBS="${LIBS} -lpthread" THREADS_SOURCES="thread_pool.c" else echo "$ac_t""no" 1>&6 THREADS_SOURCES="" fi else echo "$ac_t""no" 1>&6 fi echo $ac_n "checking whether to enable ULOG support""... $ac_c" 1>&6 echo "configure:3484: checking whether to enable ULOG support" >&5 # Check whether --enable-ulog or --disable-ulog was given. if test "${enable_ulog+set}" = set; then enableval="$enable_ulog" if test "x$enableval" = xyes ; then echo "$ac_t""yes" 1>&6 CFLAGS="${CFLAGS} -DENABLE_ULOG" else echo "$ac_t""no" 1>&6 fi else echo "$ac_t""no" 1>&6 fi echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 echo "configure:3502: checking return type of signal handlers" >&5 if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #ifdef signal #undef signal #endif #ifdef __cplusplus extern "C" void (*signal (int, void (*)(int)))(int); #else void (*signal ()) (); #endif int main() { int i; ; return 0; } EOF if { (eval echo configure:3524: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_type_signal=void else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_type_signal=int fi rm -f conftest* fi echo "$ac_t""$ac_cv_type_signal" 1>&6 cat >> confdefs.h <&6 echo "configure:3546: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:3574: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done CFLAGS="${CFLAGS} ${INCLUDES}" INCLUDES="" case "$host_os" in IRIX*) LIBS="${LIBS} -lgen" ;; esac LIBS="${LIBS} -lnfprobe_plugin -Lnfprobe_plugin/ -lsfprobe_plugin -Lsfprobe_plugin/ -lbgp -Lbgp/ -ltee_plugin -Ltee_plugin/ -lisis -Lisis/" echo " PLATFORM ..... : `uname -m` OS ........... : `uname -rs` (`uname -n`) COMPILER ..... : ${CC} CFLAGS ....... : ${CFLAGS} LIBS ......... : ${LIBS} LDFLAGS ...... : ${LDFLAGS} Now type 'make' to compile the source code. Are you willing to get in touch with other pmacct users? Join the pmacct mailing-list by sending a message to pmacct-discussion-subscribe@pmacct.net Need for documentation and examples? Read the README file or go to http://wiki.pmacct.net/ " trap '' 1 2 15 cat > confcache <<\EOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs. It is not useful on other systems. # If it contains results you don't want to keep, you may remove or edit it. # # By default, configure uses ./config.cache as the cache file, # creating it if it does not exist already. You can give configure # the --cache-file=FILE option to use a different cache file; that is # what configure does when it calls configure scripts in # subdirectories, so they share the cache. # Giving --cache-file=/dev/null disables caching, for debugging configure. # config.status only pays attention to the cache file if you give it the # --recheck option to rerun configure. # EOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote substitution # turns \\\\ into \\, and sed turns \\ into \). sed -n \ -e "s/'/'\\\\''/g" \ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' ;; esac >> confcache if cmp -s $cache_file confcache; then : else if test -w $cache_file; then echo "updating cache $cache_file" cat confcache > $cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Any assignment to VPATH causes Sun make to only execute # the first set of double-colon rules, so remove it if not needed. # If there is a colon in the path, we need to keep it. if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' fi trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. cat > conftest.defs <<\EOF s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g s%\[%\\&%g s%\]%\\&%g s%\$%$$%g EOF DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` rm -f conftest.defs # Without the "./", some shells look in PATH for config.status. : ${CONFIG_STATUS=./config.status} echo creating $CONFIG_STATUS rm -f $CONFIG_STATUS cat > $CONFIG_STATUS </dev/null | sed 1q`: # # $0 $ac_configure_args # # Compiler output produced by configure, useful for debugging # configure, is in ./config.log if it exists. ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" for ac_option do case "\$ac_option" in -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v) echo "$CONFIG_STATUS generated by autoconf version 2.13" exit 0 ;; -help | --help | --hel | --he | --h) echo "\$ac_cs_usage"; exit 0 ;; *) echo "\$ac_cs_usage"; exit 1 ;; esac done ac_given_srcdir=$srcdir ac_given_INSTALL="$INSTALL" trap 'rm -fr `echo " Makefile \ src/Makefile src/nfprobe_plugin/Makefile \ src/sfprobe_plugin/Makefile src/bgp/Makefile \ src/tee_plugin/Makefile src/isis/Makefile " | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 EOF cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF $ac_vpsub $extrasub s%@SHELL@%$SHELL%g s%@CFLAGS@%$CFLAGS%g s%@CPPFLAGS@%$CPPFLAGS%g s%@CXXFLAGS@%$CXXFLAGS%g s%@FFLAGS@%$FFLAGS%g s%@DEFS@%$DEFS%g s%@LDFLAGS@%$LDFLAGS%g s%@LIBS@%$LIBS%g s%@exec_prefix@%$exec_prefix%g s%@prefix@%$prefix%g s%@program_transform_name@%$program_transform_name%g s%@bindir@%$bindir%g s%@sbindir@%$sbindir%g s%@libexecdir@%$libexecdir%g s%@datadir@%$datadir%g s%@sysconfdir@%$sysconfdir%g s%@sharedstatedir@%$sharedstatedir%g s%@localstatedir@%$localstatedir%g s%@libdir@%$libdir%g s%@includedir@%$includedir%g s%@oldincludedir@%$oldincludedir%g s%@infodir@%$infodir%g s%@mandir@%$mandir%g s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g s%@INSTALL_DATA@%$INSTALL_DATA%g s%@PACKAGE@%$PACKAGE%g s%@VERSION@%$VERSION%g s%@ACLOCAL@%$ACLOCAL%g s%@AUTOCONF@%$AUTOCONF%g s%@AUTOMAKE@%$AUTOMAKE%g s%@AUTOHEADER@%$AUTOHEADER%g s%@MAKEINFO@%$MAKEINFO%g s%@SET_MAKE@%$SET_MAKE%g s%@CC@%$CC%g s%@RANLIB@%$RANLIB%g s%@MAKE@%$MAKE%g s%@CPP@%$CPP%g s%@PLUGINS@%$PLUGINS%g s%@THREADS_SOURCES@%$THREADS_SOURCES%g s%@EXTRABIN@%$EXTRABIN%g CEOF EOF cat >> $CONFIG_STATUS <<\EOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. ac_file=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_cmds # Line after last line for current file. ac_more_lines=: ac_sed_cmds="" while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file else sed "${ac_end}q" conftest.subs > conftest.s$ac_file fi if test ! -s conftest.s$ac_file; then ac_more_lines=false rm -f conftest.s$ac_file else if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f conftest.s$ac_file" else ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" fi ac_file=`expr $ac_file + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_cmds` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi EOF cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case "$ac_file" in *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; *) ac_file_in="${ac_file}.in" ;; esac # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. # Remove last slash and all that follows it. Not all systems have dirname. ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then # The file is in a subdirectory. test ! -d "$ac_dir" && mkdir "$ac_dir" ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" # A "../" for each directory in $ac_dir_suffix. ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` else ac_dir_suffix= ac_dots= fi case "$ac_given_srcdir" in .) srcdir=. if test -z "$ac_dots"; then top_srcdir=. else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; *) # Relative path. srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" top_srcdir="$ac_dots$ac_given_srcdir" ;; esac case "$ac_given_INSTALL" in [/$]*) INSTALL="$ac_given_INSTALL" ;; *) INSTALL="$ac_dots$ac_given_INSTALL" ;; esac echo creating "$ac_file" rm -f "$ac_file" configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." case "$ac_file" in *Makefile*) ac_comsub="1i\\ # $configure_input" ;; *) ac_comsub= ;; esac ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` sed -e "$ac_comsub s%@configure_input@%$configure_input%g s%@srcdir@%$srcdir%g s%@top_srcdir@%$top_srcdir%g s%@INSTALL@%$INSTALL%g " $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file fi; done rm -f conftest.s* EOF cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF exit 0 EOF chmod +x $CONFIG_STATUS rm -fr confdefs* $ac_clean_files test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 pmacct-0.14.0/src/0000755000175000017500000000000011741267746012657 5ustar paolopaolopmacct-0.14.0/src/memory.c0000644000175000017500000001060211037474163014321 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente */ /* 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. */ #define __MEMORY_C /* includes */ #include "pmacct.h" #include "imt_plugin.h" /* rules: first pool descriptor id is 1 */ /* functions */ void init_memory_pool_table() { if (config.num_memory_pools) { mpd = (unsigned char *) map_shared(0, (config.num_memory_pools+1)*sizeof(struct memory_pool_desc), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); memset(mpd, 0, (config.num_memory_pools+1)*sizeof(struct memory_pool_desc)); } else { mpd = (unsigned char *) map_shared(0, (NUM_MEMORY_POOLS+1)*sizeof(struct memory_pool_desc), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); memset(mpd, 0, (NUM_MEMORY_POOLS+1)*sizeof(struct memory_pool_desc)); } current_pool = (struct memory_pool_desc *) mpd; } void clear_memory_pool_table() { struct memory_pool_desc *pool_ptr; pool_ptr = (struct memory_pool_desc *) mpd; while (pool_ptr->next) { memset(pool_ptr->base_ptr, 0, pool_ptr->len); pool_ptr->id = 0; pool_ptr = pool_ptr->next; } /* clearing last memory pool and rewinding stuff */ memset(pool_ptr->base_ptr, 0, pool_ptr->len); current_pool = (struct memory_pool_desc *) mpd; } struct memory_pool_desc *request_memory_pool(int size) { int new_id; unsigned char *memptr; struct memory_pool_desc *new_pool; /* trying to find resource already allocated but currently unused */ if (current_pool->next) { if (!current_pool->id) { new_pool = current_pool; new_pool->id = 1; } else { new_pool = current_pool->next; new_pool->id = current_pool->id+1; } if (size <= new_pool->len) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): using an already allocated memory segment.\n", config.name, config.type); memset(new_pool->base_ptr, 0, size); new_pool->ptr = new_pool->base_ptr; new_pool->space_left = size; return new_pool; } } /* we didn't find allocated resources; requesting new news and registering them in memory pool descriptors table */ new_id = current_pool->id+1; Log(LOG_DEBUG, "DEBUG ( %s/%s ): allocating a new memory segment.\n", config.name, config.type); if (config.num_memory_pools) { if (new_id > config.num_memory_pools) return NULL; new_pool = (struct memory_pool_desc *) mpd+(new_id-1); } else { if (new_id > NUM_MEMORY_POOLS) { /* XXX: this malloc() is a quick workaround because of some mallopt() SEGV when trying to allocate chuncks < 1024; i'm not figuring out where is the *real* problem. First experienced with: gcc version 3.3.3 (Debian 20040320) / glibc 2.3.2 */ // new_pool = (struct memory_pool_desc *) malloc(sizeof(struct memory_pool_desc)); new_pool = (struct memory_pool_desc *) map_shared(0, 1024, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if (new_pool == MAP_FAILED) return NULL; memset(new_pool, 0, sizeof(struct memory_pool_desc)); } else new_pool = (struct memory_pool_desc *) mpd+(new_id-1); } if (current_pool->id) current_pool->next = new_pool; /* We found a free room in mpd table; now we have allocate needed memory */ memptr = (unsigned char *) map_shared(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if (memptr == MAP_FAILED) { Log(LOG_WARNING, "WARN ( %s/%s ): memory sold out ! Please, clear in-memory stats !\n", config.name, config.type); return NULL; } memset(memptr, 0, size); new_pool->id = new_id; new_pool->base_ptr = memptr; new_pool->ptr = memptr; new_pool->space_left = size; new_pool->len = size; return new_pool; } pmacct-0.14.0/src/conntrack.h0000644000175000017500000000603511460653755015013 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2010 by Paolo Lucente */ /* 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. */ /* defines */ #define CONNTRACK_GENERIC_LIFETIME 20 #define DEFAULT_CONNTRACK_BUFFER_SIZE 8192000 /* 8 Mb */ #define MAX_CONNTRACKS 256 /* structures */ typedef void (*conntrack_helper)(time_t, struct packet_ptrs *); struct conntrack_helper_entry { char protocol[MAX_PROTOCOL_LEN]; conntrack_helper ct_helper; }; struct conntrack_ipv4 { u_int32_t ip_src; u_int32_t ip_dst; u_int16_t port_src; u_int16_t port_dst; u_int8_t proto; pm_class_t class; /* timestamp renewal flag ? */ time_t stamp; time_t expiration; conntrack_helper helper; struct conntrack_ipv4 *next; }; #if defined ENABLE_IPV6 struct conntrack_ipv6 { u_int32_t ip_src[4]; u_int32_t ip_dst[4]; u_int16_t port_src; u_int16_t port_dst; u_int8_t proto; pm_class_t class; /* timestamp renewal flag ? */ time_t stamp; time_t expiration; conntrack_helper helper; struct conntrack_ipv6 *next; }; #endif #if defined __CONNTRACK_C || defined __PMACCT_PLAYER_C || defined __NFACCTD_C || defined __SFACCTD_C #define EXT #else #define EXT extern #endif EXT void init_conntrack_table(); EXT void conntrack_ftp_helper(time_t, struct packet_ptrs *); EXT void conntrack_sip_helper(time_t, struct packet_ptrs *); EXT void conntrack_rtsp_helper(time_t, struct packet_ptrs *); EXT void search_conntrack(struct ip_flow_common *, struct packet_ptrs *, unsigned int); EXT void search_conntrack_ipv4(struct ip_flow_common *, struct packet_ptrs *, unsigned int); EXT void insert_conntrack_ipv4(time_t, u_int32_t, u_int32_t, u_int16_t, u_int16_t, u_int8_t, pm_class_t, conntrack_helper, time_t); EXT struct conntrack_ipv4 *conntrack_ipv4_table; #if defined ENABLE_IPV6 EXT void search_conntrack_ipv6(struct ip_flow_common *, struct packet_ptrs *, unsigned int); EXT void insert_conntrack_ipv6(time_t, struct in6_addr *, struct in6_addr *, u_int16_t, u_int16_t, u_int8_t, pm_class_t, conntrack_helper, time_t); EXT struct conntrack_ipv6 *conntrack_ipv6_table; #endif #undef EXT #if defined __CONNTRACK_C || defined __CLASSIFIER_C static struct conntrack_helper_entry conntrack_helper_list[] = { { "ftp", conntrack_ftp_helper }, { "sip", conntrack_sip_helper }, // { "irc", conntrack_irc_helper }, { "rtsp", conntrack_rtsp_helper }, { "", NULL }, }; #endif pmacct-0.14.0/src/pmacct-defines.h0000644000175000017500000002074111741267721015707 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* defines */ #define ARGS_NFACCTD "n:dDhP:b:f:F:c:m:p:r:s:S:L:l:v:o:O:uR" #define ARGS_SFACCTD "n:dDhP:b:f:F:c:m:p:r:s:S:L:l:v:o:O:uR" #define ARGS_PMACCTD "n:NdDhP:b:f:F:c:i:I:m:p:r:s:S:v:o:O:uwWL:R" #define ARGS_UACCTD "n:NdDhP:b:f:F:c:m:p:r:s:S:v:o:O:uRg:L:" #define ARGS_PMACCT "Ssc:Cetm:p:P:M:arN:n:lT:Ou" #define N_PRIMITIVES 48 #define N_FUNCS 10 #define MAX_N_PLUGINS 32 #define PROTO_LEN 12 #define MAX_MAP_ENTRIES 2048 /* allow maps */ #define BGP_MD5_MAP_ENTRIES 8192 #define AGG_FILTER_ENTRIES 128 #define FOLLOW_BGP_NH_ENTRIES 32 #define MAX_PROTOCOL_LEN 16 #define UINT32T_THRESHOLD 4290000000UL #define UINT64T_THRESHOLD 18446744073709551360ULL #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #if defined ENABLE_IPV6 #define DEFAULT_SNAPLEN 128 #else #define DEFAULT_SNAPLEN 68 #endif #define SNAPLEN_ISIS_MIN 512 #define SNAPLEN_ISIS_DEFAULT 1476 #define SRVBUFLEN (256+MOREBUFSZ) #define LONGSRVBUFLEN (384+MOREBUFSZ) #define LONGLONGSRVBUFLEN (1024+MOREBUFSZ) #define LARGEBUFLEN (8192+MOREBUFSZ) #define MANTAINER "Paolo Lucente " #define PMACCTD_USAGE_HEADER "Promiscuous Mode Accounting Daemon, pmacctd 0.14.0" #define UACCTD_USAGE_HEADER "Linux NetFilter ULOG Accounting Daemon, pmacctd 0.14.0" #define PMACCT_USAGE_HEADER "pmacct, pmacct client 0.14.0" #define PMMYPLAY_USAGE_HEADER "pmmyplay, pmacct MySQL logfile player 0.14.0" #define PMPGPLAY_USAGE_HEADER "pmpgplay, pmacct PGSQL logfile player 0.14.0" #define NFACCTD_USAGE_HEADER "NetFlow Accounting Daemon, nfacctd 0.14.0" #define SFACCTD_USAGE_HEADER "sFlow Accounting Daemon, sfacctd 0.14.0" #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #define ERR -1 #define SUCCESS 0 #define E_NOTFOUND 2 #ifndef MIN #define MIN(x, y) (x <= y ? x : y) #endif #ifndef MAX #define MAX(x, y) (x <= y ? y : x) #endif /* acct_type */ #define ACCT_PM 1 /* promiscuous mode */ #define ACCT_NF 2 /* NetFlow */ #define ACCT_SF 3 /* sFlow */ #define ACCT_UL 4 /* Linux NetFilter ULOG */ /* map type */ #define MAP_TAG 0 /* pre_tag_map */ #define MAP_BGP_PEER_AS_SRC 100 /* bgp_peer_src_as_map */ #define MAP_BGP_TO_XFLOW_AGENT 101 /* bgp_to_agent_map */ #define MAP_BGP_SRC_LOCAL_PREF 102 /* bgp_src_local_pref_map */ #define MAP_BGP_SRC_MED 103 /* bgp_src_med_map */ #define MAP_BGP_IFACE_TO_RD 104 /* bgp_iface_to_rd */ #define MAP_SAMPLING 105 /* sampling_map */ /* 48 primitives currently defined */ #define COUNT_SRC_HOST 0x0000000000000001ULL #define COUNT_DST_HOST 0x0000000000000002ULL #define COUNT_SUM_HOST 0x0000000000000004ULL #define COUNT_SRC_PORT 0x0000000000000008ULL #define COUNT_DST_PORT 0x0000000000000010ULL #define COUNT_IP_PROTO 0x0000000000000020ULL #define COUNT_SRC_MAC 0x0000000000000040ULL #define COUNT_DST_MAC 0x0000000000000080ULL #define COUNT_SRC_NET 0x0000000000000100ULL #define COUNT_DST_NET 0x0000000000000200ULL #define COUNT_ID 0x0000000000000400ULL #define COUNT_VLAN 0x0000000000000800ULL #define COUNT_IP_TOS 0x0000000000001000ULL #define COUNT_NONE 0x0000000000002000ULL #define COUNT_SRC_AS 0x0000000000004000ULL #define COUNT_DST_AS 0x0000000000008000ULL #define COUNT_SUM_NET 0x0000000000010000ULL #define COUNT_SUM_AS 0x0000000000020000ULL #define COUNT_SUM_PORT 0x0000000000040000ULL #define TIMESTAMP 0x0000000000080000ULL /* USE_TIMESTAMPS */ #define COUNT_FLOWS 0x0000000000100000ULL #define COUNT_SUM_MAC 0x0000000000200000ULL #define COUNT_CLASS 0x0000000000400000ULL #define COUNT_COUNTERS 0x0000000000800000ULL #define COUNT_PAYLOAD 0x0000000001000000ULL #define COUNT_TCPFLAGS 0x0000000002000000ULL #define COUNT_STD_COMM 0x0000000004000000ULL #define COUNT_EXT_COMM 0x0000000008000000ULL #define COUNT_AS_PATH 0x0000000010000000ULL #define COUNT_LOCAL_PREF 0x0000000020000000ULL #define COUNT_MED 0x0000000040000000ULL #define COUNT_PEER_SRC_AS 0x0000000080000000ULL #define COUNT_PEER_DST_AS 0x0000000100000000ULL #define COUNT_PEER_SRC_IP 0x0000000200000000ULL #define COUNT_PEER_DST_IP 0x0000000400000000ULL #define COUNT_ID2 0x0000000800000000ULL #define COUNT_SRC_AS_PATH 0x0000001000000000ULL #define COUNT_SRC_STD_COMM 0x0000002000000000ULL #define COUNT_SRC_EXT_COMM 0x0000004000000000ULL #define COUNT_SRC_LOCAL_PREF 0x0000008000000000ULL #define COUNT_SRC_MED 0x0000010000000000ULL #define COUNT_MPLS_VPN_RD 0x0000020000000000ULL #define COUNT_IN_IFACE 0x0000040000000000ULL #define COUNT_OUT_IFACE 0x0000080000000000ULL #define COUNT_SRC_NMASK 0x0000100000000000ULL #define COUNT_DST_NMASK 0x0000200000000000ULL #define COUNT_COS 0x0000400000000000ULL #define COUNT_ETHERTYPE 0x0000800000000000ULL /* BYTES and PACKETS are used into templates; we let their values to overlap with some values we will not need into templates */ #define LT_BYTES COUNT_SRC_NET #define LT_PACKETS COUNT_DST_NET #define LT_FLOWS COUNT_SUM_HOST #define LT_NO_L2 COUNT_SUM_NET #define FAKE_SRC_MAC 0x00000001 #define FAKE_DST_MAC 0x00000002 #define FAKE_SRC_HOST 0x00000004 #define FAKE_DST_HOST 0x00000008 #define FAKE_SRC_AS 0x00000010 #define FAKE_DST_AS 0x00000020 #define FAKE_COMMS 0x00000040 #define FAKE_PEER_SRC_AS 0x00000080 #define FAKE_PEER_DST_AS 0x00000100 #define FAKE_PEER_SRC_IP 0x00000200 #define FAKE_PEER_DST_IP 0x00000400 #define FAKE_AS_PATH 0x00000800 #define COUNT_MINUTELY 0x00000001 #define COUNT_HOURLY 0x00000002 #define COUNT_DAILY 0x00000004 #define COUNT_WEEKLY 0x00000008 #define COUNT_MONTHLY 0x00000010 #define WANT_STATS 0x00000001 #define WANT_ERASE 0x00000002 #define WANT_STATUS 0x00000004 #define WANT_COUNTER 0x00000008 #define WANT_MATCH 0x00000010 #define WANT_RESET 0x00000020 #define WANT_CLASS_TABLE 0x00000040 #define WANT_LOCK_OP 0x00000080 #define PIPE_TYPE_METADATA 0x00000001 #define PIPE_TYPE_PAYLOAD 0x00000002 #define PIPE_TYPE_EXTRAS 0x00000004 #define PIPE_TYPE_BGP 0x00000008 #define PIPE_TYPE_MSG 0x00000010 #define CHLD_WARNING 0x00000001 #define CHLD_ALERT 0x00000002 #define BGP_SRC_PRIMITIVES_KEEP 0x00000001 #define BGP_SRC_PRIMITIVES_MAP 0x00000002 #define BGP_SRC_PRIMITIVES_BGP 0x00000004 #define PRINT_OUTPUT_FORMATTED 0x00000001 #define PRINT_OUTPUT_CSV 0x00000002 #define DIRECTION_UNKNOWN 0x00000000 #define DIRECTION_IN 0x00000001 #define DIRECTION_OUT 0x00000002 #define DIRECTION_TAG 0x00000004 #define DIRECTION_TAG2 0x00000008 #define IFINDEX_STATIC 0x00000001 #define IFINDEX_TAG 0x00000002 #define IFINDEX_TAG2 0x00000004 typedef u_int32_t pm_class_t; typedef u_int64_t pm_id_t; #if defined HAVE_64BIT_COUNTERS typedef u_int64_t pm_counter_t; #else typedef u_int32_t pm_counter_t; #endif /* Keep common NF_AS and NF_NET values aligned, ie. NF_[NET|AS]_KEEP == 0x00000001 */ #define NF_AS_COMPAT 0x00000000 /* Unused */ #define NF_AS_KEEP 0x00000001 /* Keep AS numbers in Sflow or NetFlow packets */ #define NF_AS_NEW 0x00000002 /* ignore ASN from NetFlow and generate from network files */ #define NF_AS_BGP 0x00000004 /* ignore ASN from NetFlow and generate from BGP peerings */ #define NF_AS_FALLBACK 0x80000000 /* Fallback flag */ #define NF_NET_COMPAT 0x00000000 /* Backward compatibility selection */ #define NF_NET_KEEP 0x00000001 /* Determine IP network prefixes from sFlow or NetFlow data */ #define NF_NET_NEW 0x00000002 /* Determine IP network prefixes from network files */ #define NF_NET_BGP 0x00000004 /* Determine IP network prefixes from BGP peerings */ #define NF_NET_STATIC 0x00000008 /* Determine IP network prefixes from static mask */ #define NF_NET_IGP 0x00000010 /* Determine IP network prefixes from IGP */ #define NF_NET_FALLBACK 0x80000000 /* Fallback flag */ pmacct-0.14.0/src/pmmyplay.c0000644000175000017500000007003111664553345014671 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #define __PMACCT_PLAYER_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "sql_common.h" #include "mysql_plugin.h" #include "ip_flow.h" #include "classifier.h" #define ARGS "df:o:n:thieP:T:U:D:H:" struct DBdesc db; struct logfile_header lh; int re = 0, we = 0; int debug = 0; int sql_dont_try_update = 0; int sql_history_since_epoch = 0; char timebuf[SRVBUFLEN]; char *sql_table; void usage(char *prog) { printf("%s\n", PMMYPLAY_USAGE_HEADER); printf("Usage: %s -f [ filename ]\n\n", prog); printf("Available options:\n"); printf(" -d\tEnable debug\n"); printf(" -f\t[ filename ]\n\tPlay specified file\n"); printf(" -o\t[ element ]\n\tPlay file starting at specified offset element\n"); printf(" -n\t[ num ]\n\tNumbers of elements to play\n"); printf(" -t\tTest only; don't actually write to the DB\n"); printf(" -P\t[ password ]\n\tConnect to SQL server using the specified password\n"); printf(" -U\t[ user ]\n\tUse the specified user when connecting to SQL server\n"); printf(" -H\t[ host ]\n\tConnect to SQL server listening at specified hostname\n"); printf(" -D\t[ DB ]\n\tUse the specified SQL database\n"); printf(" -T\t[ table ]\n\tUse the specified SQL table\n"); printf(" -i\tDon't try update, use insert only.\n"); printf(" -e\tUse seconds since the Epoch timestamps.\n"); printf("\n"); printf("For suggestions, critics, bugs, contact me: %s.\n", MANTAINER); } void print_header() { printf("NUM "); printf("ID "); #if defined (HAVE_L2) printf("SRC_MAC "); printf("DST_MAC "); printf("VLAN "); #endif printf("SRC_AS "); printf("DST_AS "); #if defined ENABLE_IPV6 printf("SRC_IP "); printf("DST_IP "); #else printf("SRC_IP "); printf("DST_IP "); #endif printf("SRC_PORT "); printf("DST_PORT "); printf("PROTOCOL "); printf("TOS "); #if defined HAVE_64BIT_COUNTERS printf("PACKETS "); printf("FLOWS "); printf("BYTES "); #else printf("PACKETS "); printf("FLOWS "); printf("BYTES "); #endif printf("BASETIME\n"); } void print_data(struct db_cache *cache_elem, u_int32_t wtc, int num) { struct tm *lt; struct pkt_primitives *data = &cache_elem->primitives; char src_mac[18], dst_mac[18], src_host[INET6_ADDRSTRLEN], dst_host[INET6_ADDRSTRLEN]; printf("%-8d ", num); printf("%-5d ", data->id); #if defined (HAVE_L2) etheraddr_string(data->eth_shost, src_mac); printf("%-17s ", src_mac); etheraddr_string(data->eth_dhost, dst_mac); printf("%-17s ", dst_mac); printf("%-5d ", data->vlan_id); #endif printf("%-5d ", data->src_as); printf("%-5d ", data->dst_as); #if defined ENABLE_IPV6 addr_to_str(src_host, &data->src_ip); printf("%-45s ", src_host); addr_to_str(dst_host, &data->dst_ip); printf("%-45s ", dst_host); #else addr_to_str(src_host, &data->src_ip); printf("%-15s ", src_host); addr_to_str(dst_host, &data->dst_ip); printf("%-15s ", dst_host); #endif printf("%-5d ", data->src_port); printf("%-5d ", data->dst_port); printf("%-10s ", _protocols[data->proto].name); printf("%-3d ", data->tos); #if defined HAVE_64BIT_COUNTERS printf("%-20llu ", cache_elem->packet_counter); printf("%-20llu ", cache_elem->flows_counter); printf("%-20llu ", cache_elem->bytes_counter); #else printf("%-10lu ", cache_elem->packet_counter); printf("%-10lu ", cache_elem->flows_counter); printf("%-10lu ", cache_elem->bytes_counter); #endif if (lh.sql_history) { if (!sql_history_since_epoch) { lt = localtime(&cache_elem->basetime); strftime(timebuf, SRVBUFLEN, "%Y-%m-%d %H:%M:%S" , lt); printf("%s\n", timebuf); } else printf("%u\n", cache_elem->basetime); } else printf("0\n"); } int main(int argc, char **argv) { struct insert_data idata; FILE *f; unsigned char fbuf[SRVBUFLEN]; char logfile[SRVBUFLEN]; char default_pwd[] = "arealsmartpwd"; int have_pwd = 0, have_logfile = 0, n; int result = 0, position = 0, howmany = 0; int do_nothing = 0; char *cl_sql_host = NULL, *cl_sql_user = NULL, *cl_sql_db = NULL, *cl_sql_table = NULL; char sql_pwd[SRVBUFLEN]; char *sql_host, *sql_user, *sql_db; struct template_entry *teptr; int tot_size = 0, cnt = 0; u_char *te; struct template_header th; struct db_cache data; /* getopt() stuff */ extern char *optarg; extern int optind, opterr, optopt; int errflag = 0, cp; memset(&idata, 0, sizeof(idata)); memset(sql_data, 0, sizeof(sql_data)); memset(lock_clause, 0, sizeof(lock_clause)); memset(unlock_clause, 0, sizeof(unlock_clause)); memset(update_clause, 0, sizeof(update_clause)); memset(insert_clause, 0, sizeof(insert_clause)); memset(where, 0, sizeof(where)); memset(values, 0, sizeof(values)); memset(&data, 0, sizeof(data)); memset(timebuf, 0, sizeof(timebuf)); db.desc = malloc(sizeof(MYSQL)); memset(db.desc, 0, sizeof(MYSQL)); pp_size = sizeof(struct db_cache); while (!errflag && ((cp = getopt(argc, argv, ARGS)) != -1)) { switch (cp) { case 'd': debug = TRUE; break; case 'f': strlcpy(logfile, optarg, sizeof(logfile)); have_logfile = TRUE; break; case 'o': position = atoi(optarg); if (!position) { printf("ERROR: invalid offset. Exiting.\n"); exit(1); } break; case 'n': howmany = atoi(optarg); if (!howmany) { printf("ERROR: invalid number of elements. Exiting.\n"); exit(1); } break; case 't': do_nothing = TRUE; break; case 'i': sql_dont_try_update = TRUE; break; case 'e': sql_history_since_epoch = TRUE; break; case 'P': strlcpy(sql_pwd, optarg, sizeof(sql_pwd)); have_pwd = TRUE; break; case 'U': cl_sql_user = malloc(SRVBUFLEN); memset(cl_sql_user, 0, SRVBUFLEN); strlcpy(cl_sql_user, optarg, SRVBUFLEN); break; case 'D': cl_sql_db = malloc(SRVBUFLEN); memset(cl_sql_db, 0, SRVBUFLEN); strlcpy(cl_sql_db, optarg, SRVBUFLEN); break; case 'H': cl_sql_host = malloc(SRVBUFLEN); memset(cl_sql_host, 0, SRVBUFLEN); strlcpy(cl_sql_host, optarg, SRVBUFLEN); break; case 'T': cl_sql_table = malloc(SRVBUFLEN); memset(cl_sql_table, 0, SRVBUFLEN); strlcpy(cl_sql_table, optarg, SRVBUFLEN); break; case 'h': usage(argv[0]); exit(0); break; default: usage(argv[0]); exit(1); } } /* searching for user supplied values */ if (!howmany) howmany = -1; if (!have_pwd) memcpy(sql_pwd, default_pwd, sizeof(default_pwd)); if (!have_logfile) { usage(argv[0]); printf("\nERROR: missing logfile (-f)\nExiting...\n"); exit(1); } f = fopen(logfile, "r"); if (!f) { printf("ERROR: %s does not exists\nExiting...\n", logfile); exit(1); } fread(&lh, sizeof(lh), 1, f); lh.sql_table_version = ntohs(lh.sql_table_version); lh.sql_optimize_clauses = ntohs(lh.sql_optimize_clauses); lh.sql_history = ntohs(lh.sql_history); lh.what_to_count = ntohl(lh.what_to_count); lh.magic = ntohl(lh.magic); if (lh.magic == MAGIC) { if (debug) printf("OK: Valid logfile header read.\n"); printf("sql_db: %s\n", lh.sql_db); printf("sql_table: %s\n", lh.sql_table); printf("sql_user: %s\n", lh.sql_user); printf("sql_host: %s\n", lh.sql_host); if (cl_sql_db||cl_sql_table||cl_sql_user||cl_sql_host) printf("OK: Overrided by commandline options:\n"); if (cl_sql_db) printf("sql_db: %s\n", cl_sql_db); if (cl_sql_table) printf("sql_table: %s\n", cl_sql_table); if (cl_sql_user) printf("sql_user: %s\n", cl_sql_user); if (cl_sql_host) printf("sql_host: %s\n", cl_sql_host); } else { printf("ERROR: Invalid magic number. Exiting.\n"); exit(1); } /* binding SQL stuff */ if (cl_sql_db) sql_db = cl_sql_db; else sql_db = lh.sql_db; if (cl_sql_table) sql_table = cl_sql_table; else sql_table = lh.sql_table; if (cl_sql_user) sql_user = cl_sql_user; else sql_user = lh.sql_user; if (cl_sql_host) sql_host = cl_sql_host; else sql_host = lh.sql_host; fread(&th, sizeof(th), 1, f); th.magic = ntohl(th.magic); th.num = ntohs(th.num); th.sz = ntohs(th.sz); if (th.magic == TH_MAGIC) { if (debug) printf("OK: Valid template header read.\n"); if (th.num > N_PRIMITIVES) { printf("ERROR: maximum number of primitives exceeded. Exiting.\n"); exit(1); } te = malloc(th.num*sizeof(struct template_entry)); memset(te, 0, th.num*sizeof(struct template_entry)); fread(te, th.num*sizeof(struct template_entry), 1, f); } else { if (debug) printf("ERROR: no template header found.\n"); exit(1); } /* checking template */ if (th.sz >= sizeof(fbuf)) { printf("ERROR: Objects are too big. Exiting.\n"); exit(1); } teptr = (struct template_entry *) te; for (tot_size = 0, cnt = 0; cnt < th.num; cnt++, teptr++) tot_size += teptr->size; if (tot_size != th.sz) { printf("ERROR: malformed template header. Size mismatch. Exiting.\n"); exit(1); } TPL_check_sizes(&th, &data, te); if (!do_nothing) { mysql_init(db.desc); if (mysql_real_connect(db.desc, sql_host, sql_user, sql_pwd, sql_db, 0, NULL, 0) == NULL) { printf("%s\n", mysql_error(db.desc)); exit(1); } } else { if (debug) print_header(); } /* setting number of entries in _protocols structure */ while (_protocols[protocols_number].number != -1) protocols_number++; /* composing the proper (filled with primitives used during the current execution) SQL strings */ idata.num_primitives = MY_compose_static_queries(); idata.now = time(NULL); /* handling offset */ if (position) n = fseek(f, (th.sz*position), SEEK_CUR); /* handling single or iterative request */ if (!do_nothing) mysql_query(db.desc, lock_clause); while(!feof(f)) { if (!howmany) break; else if (howmany > 0) howmany--; memset(fbuf, 0, th.sz); n = fread(fbuf, th.sz, 1, f); if (n) { re++; TPL_pop(fbuf, &data, &th, te); if (!do_nothing) result = MY_cache_dbop(&db, &data, &idata); else { if (debug) print_data(&data, lh.what_to_count, (position+re)); } if (!result) we++; if (re != we) printf("WARN: unable to write element %u.\n", re); } } if (!do_nothing) { mysql_query(db.desc, unlock_clause); printf("\nOK: written [%u/%u] elements.\n", we, re); } else printf("OK: read [%u] elements.\n", re); mysql_close(db.desc); fclose(f); return 0; } int MY_cache_dbop(struct DBdesc *db, struct db_cache *cache_elem, struct insert_data *idata) { char *ptr_values, *ptr_where; int num=0, ret=0, have_flows=0; if (lh.what_to_count & COUNT_FLOWS) have_flows = TRUE; /* constructing sql query */ ptr_where = where_clause; ptr_values = values_clause; while (num < idata->num_primitives) { (*where[num].handler)(cache_elem, idata, num, &ptr_values, &ptr_where); num++; } if (have_flows) snprintf(sql_data, sizeof(sql_data), update_clause, cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(sql_data, sizeof(sql_data), update_clause, cache_elem->packet_counter, cache_elem->bytes_counter); strncat(sql_data, where_clause, SPACELEFT(sql_data)); if (!sql_dont_try_update) { ret = mysql_query(db->desc, sql_data); if (ret) return ret; } if (sql_dont_try_update || (mysql_affected_rows(db->desc) == 0)) { /* UPDATE failed, trying with an INSERT query */ strncpy(sql_data, insert_clause, sizeof(sql_data)); #if defined HAVE_64BIT_COUNTERS if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), ", %llu, %llu, %llu)", cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), ", %llu, %llu)", cache_elem->packet_counter, cache_elem->bytes_counter); #else if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), ", %lu, %lu, %lu)", cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), ", %lu, %lu)", cache_elem->packet_counter, cache_elem->bytes_counter); #endif strncat(sql_data, values_clause, SPACELEFT(sql_data)); ret = mysql_query(db->desc, sql_data); if (ret) return ret; } if (debug) { printf("**********\n"); printf("%s\n", sql_data); } return ret; } int MY_evaluate_history(int primitive) { if (lh.sql_history) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (!sql_history_since_epoch) strncat(where[primitive].string, "FROM_UNIXTIME(%u) = ", SPACELEFT(where[primitive].string)); else strncat(where[primitive].string, "%u = ", SPACELEFT(where[primitive].string)); strncat(where[primitive].string, "stamp_inserted", SPACELEFT(where[primitive].string)); strncat(insert_clause, "stamp_updated, stamp_inserted", SPACELEFT(insert_clause)); if (!sql_history_since_epoch) strncat(values[primitive].string, "FROM_UNIXTIME(%u), FROM_UNIXTIME(%u)", SPACELEFT(values[primitive].string)); else strncat(values[primitive].string, "%u, %u", SPACELEFT(values[primitive].string)); where[primitive].type = values[primitive].type = TIMESTAMP; values[primitive].handler = where[primitive].handler = count_timestamp_handler; primitive++; } return primitive; } int MY_evaluate_primitives(int primitive) { u_int64_t what_to_count = 0; short int assume_custom_table = FALSE; if (lh.sql_optimize_clauses) { what_to_count = lh.what_to_count; assume_custom_table = TRUE; } else { /* we are requested to avoid optimization; then we'll construct an all-true "what to count" bitmap */ if (lh.what_to_count & COUNT_SRC_AS) what_to_count |= COUNT_SRC_AS; else if (lh.what_to_count & COUNT_SUM_HOST) what_to_count |= COUNT_SUM_HOST; else if (lh.what_to_count & COUNT_SUM_NET) what_to_count |= COUNT_SUM_NET; else if (lh.what_to_count & COUNT_SUM_AS) what_to_count |= COUNT_SUM_AS; else what_to_count |= COUNT_SRC_HOST; if (lh.what_to_count & COUNT_DST_AS) what_to_count |= COUNT_DST_AS; else what_to_count |= COUNT_DST_HOST; what_to_count |= COUNT_SRC_MAC; what_to_count |= COUNT_DST_MAC; what_to_count |= COUNT_SRC_PORT; what_to_count |= COUNT_DST_PORT; what_to_count |= COUNT_IP_TOS; what_to_count |= COUNT_IP_PROTO; what_to_count |= COUNT_ID; what_to_count |= COUNT_VLAN; if (lh.what_to_count & COUNT_SUM_PORT) what_to_count |= COUNT_SUM_PORT; if (lh.what_to_count & COUNT_SUM_MAC) what_to_count |= COUNT_SUM_MAC; } /* 1st part: arranging pointers to an opaque structure and composing the static selection (WHERE) string */ #if defined (HAVE_L2) if (what_to_count & (COUNT_SRC_MAC|COUNT_SUM_MAC)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mac_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mac_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_MAC; values[primitive].handler = where[primitive].handler = count_src_mac_handler; primitive++; } if (what_to_count & COUNT_DST_MAC) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mac_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mac_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_MAC; values[primitive].handler = where[primitive].handler = count_dst_mac_handler; primitive++; } if (what_to_count & COUNT_VLAN) { int count_it = FALSE; if ((lh.sql_table_version < 2) && !assume_custom_table) { if (lh.what_to_count & COUNT_VLAN) { printf("ERROR: The use of VLAN accounting requires SQL table v2. Exiting.\n"); exit(1); } else what_to_count ^= COUNT_VLAN; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "vlan", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "vlan=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_VLAN; values[primitive].handler = where[primitive].handler = count_vlan_handler; primitive++; } } #endif if (what_to_count & (COUNT_SRC_HOST|COUNT_SRC_NET|COUNT_SUM_HOST|COUNT_SUM_NET)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_HOST; values[primitive].handler = where[primitive].handler = count_src_host_handler; primitive++; } if (what_to_count & (COUNT_DST_HOST|COUNT_DST_NET)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_HOST; values[primitive].handler = where[primitive].handler = count_dst_host_handler; primitive++; } if (what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (lh.sql_table_version >= 6) { strncat(insert_clause, "as_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "as_src=%u", SPACELEFT(where[primitive].string)); } else { strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%u\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=\'%u\'", SPACELEFT(where[primitive].string)); } values[primitive].type = where[primitive].type = COUNT_SRC_AS; values[primitive].handler = where[primitive].handler = count_src_as_handler; primitive++; } if (what_to_count & COUNT_DST_AS) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (lh.sql_table_version >= 6) { strncat(insert_clause, "as_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "as_dst=%u", SPACELEFT(where[primitive].string)); } else { strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%u\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=\'%u\'", SPACELEFT(where[primitive].string)); } values[primitive].type = where[primitive].type = COUNT_DST_AS; values[primitive].handler = where[primitive].handler = count_dst_as_handler; primitive++; } if (what_to_count & (COUNT_SRC_PORT|COUNT_SUM_PORT)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "src_port", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "src_port=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_PORT; values[primitive].handler = where[primitive].handler = count_src_port_handler; primitive++; } if (what_to_count & COUNT_DST_PORT) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "dst_port", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "dst_port=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_PORT; values[primitive].handler = where[primitive].handler = count_dst_port_handler; primitive++; } if (what_to_count & COUNT_IP_TOS) { int count_it = FALSE; if ((lh.sql_table_version < 3) && !assume_custom_table) { if (lh.what_to_count & COUNT_IP_TOS) { printf("ERROR: The use of ToS/DSCP accounting requires SQL table v3. Exiting.\n"); exit(1); } else what_to_count ^= COUNT_IP_TOS; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "tos", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "tos=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_IP_TOS; values[primitive].handler = where[primitive].handler = count_ip_tos_handler; primitive++; } } if (what_to_count & COUNT_IP_PROTO) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_proto", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_proto=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_IP_PROTO; values[primitive].handler = where[primitive].handler = MY_count_ip_proto_handler; primitive++; } if (what_to_count & COUNT_ID) { int count_it = FALSE; if ((lh.sql_table_version < 2) && !assume_custom_table) { if (lh.what_to_count & COUNT_ID) { printf("ERROR: The use of IDs requires SQL table version 2. Exiting.\n"); exit(1); } else what_to_count ^= COUNT_ID; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "agent_id", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "agent_id=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_ID; values[primitive].handler = where[primitive].handler = count_id_handler; primitive++; } } return primitive; } int MY_compose_static_queries() { int primitives=0, have_flows=0; if (lh.what_to_count & COUNT_FLOWS || (lh.sql_table_version >= 4 && !lh.sql_optimize_clauses)) { lh.what_to_count |= COUNT_FLOWS; have_flows = TRUE; if (lh.sql_table_version < 4 && !lh.sql_optimize_clauses) { printf("ERROR: The accounting of flows requires SQL table v4. Exiting.\n"); exit(1); } } /* "INSERT INTO ... VALUES ... " and "... WHERE ..." stuff */ strncpy(where[primitives].string, " WHERE ", sizeof(where[primitives].string)); snprintf(insert_clause, sizeof(insert_clause), "INSERT INTO %s (", sql_table); strncpy(values[primitives].string, " VALUES (", sizeof(values[primitives].string)); primitives = MY_evaluate_history(primitives); primitives = MY_evaluate_primitives(primitives); strncat(insert_clause, ", packets, bytes", SPACELEFT(insert_clause)); if (have_flows) strncat(insert_clause, ", flows", SPACELEFT(insert_clause)); strncat(insert_clause, ")", SPACELEFT(insert_clause)); /* "LOCK ..." stuff */ snprintf(lock_clause, sizeof(lock_clause), "LOCK TABLES %s WRITE", sql_table); strncpy(unlock_clause, "UNLOCK TABLES", sizeof(unlock_clause)); /* "UPDATE ... SET ..." stuff */ snprintf(update_clause, sizeof(update_clause), "UPDATE %s ", sql_table); #if defined HAVE_64BIT_COUNTERS strncat(update_clause, "SET packets=packets+%llu, bytes=bytes+%llu", SPACELEFT(update_clause)); if (have_flows) strncat(update_clause, ", flows=flows+%llu", SPACELEFT(update_clause)); #else strncat(update_clause, "SET packets=packets+%lu, bytes=bytes+%lu", SPACELEFT(update_clause)); if (have_flows) strncat(update_clause, ", flows=flows+%lu", SPACELEFT(update_clause)); #endif if (lh.sql_history) { if (!sql_history_since_epoch) strncat(update_clause, ", stamp_updated=NOW()", SPACELEFT(update_clause)); else strncat(update_clause, ", stamp_updated=UNIX_TIMESTAMP(NOW())", SPACELEFT(update_clause)); } return primitives; } void MY_exit_gracefully(int signum) { printf("\nOK: written [%u/%u] elements.\n", we, re); exit(0); } /* Dummy version of bgp_rd2str() to resolve code dependencies */ int bgp_rd2str(u_char *str, rd_t *rd) { return TRUE; } pmacct-0.14.0/src/pretag.h0000644000175000017500000001101611715256661014304 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* Pre-Tag map stuff */ #define N_MAP_HANDLERS 16 #define MAX_LABEL_LEN 32 #define MAX_PRETAG_MAP_ENTRIES 384 #define PRETAG_IN_IFACE 0x00000001 #define PRETAG_OUT_IFACE 0x00000002 #define PRETAG_NEXTHOP 0x00000004 #define PRETAG_BGP_NEXTHOP 0x00000008 #define PRETAG_ENGINE_TYPE 0x00000010 #define PRETAG_ENGINE_ID 0x00000020 #define PRETAG_FILTER 0x00000040 #define PRETAG_NFV8_AGG 0x00000080 #define PRETAG_SF_AGENTID 0x00000100 #define PRETAG_SAMPLING_RATE 0x00000200 #define PRETAG_DIRECTION 0x00000400 #define PRETAG_SRC_AS 0x00000800 #define PRETAG_DST_AS 0x00001000 #define PRETAG_PEER_SRC_AS 0x00002000 #define PRETAG_PEER_DST_AS 0x00004000 #define PRETAG_SRC_LOCAL_PREF 0x00008000 #define PRETAG_LOCAL_PREF 0x00010000 #define PRETAG_SRC_STD_COMM 0x00020000 #define PRETAG_STD_COMM 0x00040000 #define PRETAG_MPLS_VPN_RD 0x00080000 #define PRETAG_SAMPLE_TYPE 0x00100000 #define PRETAG_MAP_RCODE_ID 100 #define PRETAG_MAP_RCODE_ID2 101 #define BPAS_MAP_RCODE_BGP 102 typedef int (*pretag_handler) (struct packet_ptrs *, void *, void *); typedef pm_id_t (*pretag_stack_handler) (pm_id_t, pm_id_t); typedef struct { u_int8_t neg; u_int8_t n; } pt_uint8_t; typedef struct { u_int8_t neg; u_int16_t n; } pt_uint16_t; typedef struct { u_int8_t neg; u_int32_t n; } pt_uint32_t; typedef struct { u_int8_t neg; struct host_addr a; } pt_hostaddr_t; typedef struct { u_int8_t neg; rd_t rd; } pt_rd_t; typedef struct { char *label; struct id_entry *ptr; } pt_jeq_t; typedef struct { pretag_stack_handler func; } pt_stack_t; /* Pre-Tag table (ptt) element definition */ typedef struct { u_int8_t neg; pm_id_t n; pm_id_t r; } ptt_t; struct id_entry { pm_id_t id; pm_id_t id2; pm_id_t flags; pm_id_t pos; pt_hostaddr_t agent_ip; pt_hostaddr_t nexthop; pt_hostaddr_t bgp_nexthop; pt_uint32_t input; /* input interface index */ pt_uint32_t output; /* output interface index */ pt_uint8_t engine_type; pt_uint8_t engine_id; pt_uint32_t agent_id; /* applies to sFlow agentSubId */ pt_uint32_t sampling_rate; /* applies to sFlow sampling rate */ pt_uint32_t sample_type; /* applies to sFlow sample type */ pt_uint8_t direction; pt_uint32_t src_as; pt_uint32_t dst_as; pt_uint32_t peer_src_as; pt_uint32_t peer_dst_as; pt_uint32_t src_local_pref; pt_uint32_t local_pref; char *src_comms[16]; /* XXX: MAX_BGP_COMM_PATTERNS = 16 */ char *comms[16]; /* XXX: MAX_BGP_COMM_PATTERNS = 16 */ pt_rd_t mpls_vpn_rd; struct bpf_program filter; pt_uint8_t v8agg; pretag_handler func[N_MAP_HANDLERS]; u_int32_t func_type[N_MAP_HANDLERS]; char label[MAX_LABEL_LEN]; pt_jeq_t jeq; u_int8_t ret; pt_stack_t stack; u_int32_t last_matched; }; struct id_table { unsigned short int num; struct id_entry *ipv4_base; unsigned short int ipv4_num; #if defined ENABLE_IPV6 struct id_entry *ipv6_base; unsigned short int ipv6_num; #endif struct id_entry *e; time_t timestamp; }; struct _map_dictionary_line { char key[SRVBUFLEN]; int (*func)(char *, struct id_entry *, char *, struct plugin_requests *, int); }; struct pretag_filter { u_int16_t num; ptt_t table[MAX_PRETAG_MAP_ENTRIES/4]; }; /* prototypes */ #if (!defined __PRETAG_C) #define EXT extern #else #define EXT #endif EXT void load_id_file(int, char *, struct id_table *, struct plugin_requests *, int *); EXT u_int8_t pt_check_neg(char **); EXT char * pt_check_range(char *); EXT int tag_map_allocated; EXT int bpas_map_allocated; EXT int blp_map_allocated; EXT int bmed_map_allocated; EXT int biss_map_allocated; EXT int bta_map_allocated; EXT int bitr_map_allocated; EXT int sampling_map_allocated; EXT void (*find_id_func)(struct id_table *, struct packet_ptrs *, pm_id_t *, pm_id_t *); #undef EXT pmacct-0.14.0/src/pkt_handlers.h0000644000175000017500000002556311736374110015505 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #if (defined __PKT_HANDLERS_C) extern struct channels_list_entry channels_list[MAX_N_PLUGINS]; /* communication channels: core <-> plugins */ #endif #if (!defined __PKT_HANDLERS_C) #define EXT extern #else #define EXT #endif EXT pkt_handler phandler[N_PRIMITIVES]; #undef EXT #if (!defined __PKT_HANDLERS_C) #define EXT extern #else #define EXT #endif EXT void evaluate_packet_handlers(); EXT void src_mac_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void dst_mac_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void vlan_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void cos_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void etype_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void src_host_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void dst_host_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void src_port_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void dst_port_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void ip_tos_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void ip_proto_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void tcp_flags_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void counters_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void counters_renormalize_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void id_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void flows_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void class_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void in_iface_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void out_iface_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void sfprobe_payload_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void tee_payload_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void nfprobe_extras_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_src_mac_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_dst_mac_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_vlan_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_cos_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_etype_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_src_host_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_dst_host_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_src_nmask_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_dst_nmask_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_src_port_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_dst_port_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_src_as_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_dst_as_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_peer_src_as_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_peer_dst_as_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_peer_src_ip_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_peer_dst_ip_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_ip_tos_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_ip_proto_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_tcp_flags_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_counters_msecs_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_counters_secs_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_counters_new_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_flows_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_class_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_in_iface_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_out_iface_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_id_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_id2_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_counters_renormalize_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void NF_counters_map_renormalize_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void bgp_ext_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void nfprobe_bgp_ext_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void sfprobe_bgp_ext_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void bgp_peer_src_as_frommap_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void bgp_src_local_pref_frommap_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void bgp_src_med_frommap_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void bgp_src_net_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void bgp_dst_net_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void bgp_src_nmask_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void bgp_dst_nmask_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void bgp_peer_dst_ip_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void igp_src_net_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void igp_dst_net_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void igp_src_nmask_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void igp_dst_nmask_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void igp_peer_dst_ip_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_src_mac_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_dst_mac_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_vlan_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_cos_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_etype_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_src_host_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_dst_host_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_src_nmask_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_dst_nmask_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_src_port_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_dst_port_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_src_as_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_dst_as_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_peer_src_ip_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_peer_dst_ip_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_ip_tos_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_ip_proto_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_tcp_flags_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_counters_new_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_counters_renormalize_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_counters_map_renormalize_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_id_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_id2_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_class_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_in_iface_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_out_iface_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_sampling_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_bgp_peer_src_as_fromext_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_bgp_peer_src_as_fromstd_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_as_path_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_peer_src_as_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_peer_dst_as_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_local_pref_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void SF_std_comms_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void ptag_id_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void ptag_id2_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void sampling_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT void sfprobe_sampling_handler(struct channels_list_entry *, struct packet_ptrs *, char **); EXT int evaluate_lm_method(struct packet_ptrs *, u_int8_t, u_int32_t, u_int32_t); #undef EXT pmacct-0.14.0/src/log_templates.h0000644000175000017500000000546011037474163015663 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente */ /* 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. */ #define TH_MAGIC 11071945 struct template_header { u_int32_t magic; u_int16_t num; /* number of template entries */ u_int16_t sz; /* total size of template entries */ u_char pad[8]; }; struct template_entry { u_int32_t tag; u_int8_t size; u_int8_t type; /* unused */ }; typedef void (*template_func) (u_char **, const struct db_cache *); /* prototypes */ #if (!defined __LOG_TEMPLATES_C) #define EXT extern #else #define EXT #endif EXT template_func template_funcs[N_PRIMITIVES]; EXT void set_template_funcs(struct template_header *, struct template_entry *); EXT struct template_entry *build_template(struct template_header *); EXT void TPL_check_sizes(struct template_header *, struct db_cache *, u_char *); EXT u_int16_t TPL_push(u_char *, const struct db_cache *); EXT void TPL_pop(u_char *, struct db_cache *, struct template_header *, u_char *); EXT void TPL_push_src_mac(u_char **, const struct db_cache *); EXT void TPL_push_dst_mac(u_char **, const struct db_cache *); EXT void TPL_push_vlan(u_char **, const struct db_cache *); EXT void TPL_push_src_ip(u_char **, const struct db_cache *); EXT void TPL_push_dst_ip(u_char **, const struct db_cache *); EXT void TPL_push_src_as(u_char **, const struct db_cache *); EXT void TPL_push_dst_as(u_char **, const struct db_cache *); EXT void TPL_push_src_port(u_char **, const struct db_cache *); EXT void TPL_push_dst_port(u_char **, const struct db_cache *); EXT void TPL_push_tos(u_char **, const struct db_cache *); EXT void TPL_push_proto(u_char **, const struct db_cache *); EXT void TPL_push_id(u_char **, const struct db_cache *); EXT void TPL_push_class(u_char **, const struct db_cache *); EXT void TPL_push_bytes_counter(u_char **, const struct db_cache *); EXT void TPL_push_packet_counter(u_char **, const struct db_cache *); EXT void TPL_push_flows_counter(u_char **, const struct db_cache *); EXT void TPL_push_timestamp(u_char **, const struct db_cache *); EXT void TPL_push_nol2(u_char **, const struct db_cache *); #undef EXT pmacct-0.14.0/src/sfacctd.c0000644000175000017500000030252111741105336014417 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* sflow v2/v4/v5 routines are based on sFlow toolkit 3.8 which is Copyright (C) InMon Corporation 2001 ALL RIGHTS RESERVED */ /* defines */ #define __SFACCTD_C /* includes */ #include "pmacct.h" #include "sflow.h" #include "sfacctd.h" #include "pretag_handlers.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "pkt_handlers.h" #include "ip_flow.h" #include "classifier.h" #include "net_aggr.h" #include "bgp/bgp_packet.h" #include "bgp/bgp.h" /* variables to be exported away */ int debug; struct configuration config; /* global configuration */ struct plugins_list_entry *plugins_list = NULL; /* linked list of each plugin configuration */ struct channels_list_entry channels_list[MAX_N_PLUGINS]; /* communication channels: core <-> plugins */ int have_num_memory_pools; /* global getopt() stuff */ pid_t failed_plugins[MAX_N_PLUGINS]; /* plugins failed during startup phase */ /* Functions */ void usage_daemon(char *prog_name) { printf("%s\n", SFACCTD_USAGE_HEADER); printf("Usage: %s [ -D | -d ] [ -L IP address ] [ -l port ] [ -c primitive [ , ... ] ] [ -P plugin [ , ... ] ]\n", prog_name); printf(" %s [ -f config_file ]\n", prog_name); printf(" %s [ -h ]\n", prog_name); printf("\nGeneral options:\n"); printf(" -h \tShow this page\n"); printf(" -L \tBind to the specified IP address\n"); printf(" -l \tListen on the specified UDP port\n"); printf(" -f \tLoad configuration from the specified file\n"); printf(" -c \t[ src_mac | dst_mac | vlan | src_host | dst_host | src_net | dst_net | src_port | dst_port |\n\t tos | proto | src_as | dst_as | sum_mac | sum_host | sum_net | sum_as | sum_port | tag |\n\t tag2 | flows | class | tcpflags | in_iface | out_iface | src_mask | dst_mask | cos | etype | none ] \n\tAggregation string (DEFAULT: src_host)\n"); printf(" -D \tDaemonize\n"); printf(" -n \tPath to a file containing Network definitions\n"); printf(" -o \tPath to a file containing Port definitions\n"); printf(" -P \t[ memory | print | mysql | pgsql | sqlite3 | tee ] \n\tActivate plugin\n"); printf(" -d \tEnable debug\n"); printf(" -S \t[ auth | mail | daemon | kern | user | local[0-7] ] \n\ttLog to the specified syslog facility\n"); printf(" -F \tWrite Core Process PID into the specified file\n"); printf(" -R \tRenormalize sampled data\n"); printf(" -u \tLeave IP protocols in numerical format\n"); printf("\nMemory plugin (-P memory) options:\n"); printf(" -p \tSocket for client-server communication (DEFAULT: /tmp/collect.pipe)\n"); printf(" -b \tNumber of buckets\n"); printf(" -m \tNumber of memory pools\n"); printf(" -s \tMemory pool size\n"); printf("\nPostgreSQL (-P pgsql)/MySQL (-P mysql)/SQLite (-P sqlite3) plugin options:\n"); printf(" -r \tRefresh time (in seconds)\n"); printf(" -v \t[ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ] \n\tTable version\n"); printf("\nPrint plugin (-P print) plugin options:\n"); printf(" -r \tRefresh time (in seconds)\n"); printf(" -O \t[ formatted | csv ] \n\tOutput format\n"); printf("\n"); printf(" See EXAMPLES or visit http://wiki.pmacct.net/ for examples.\n"); printf("\n"); printf("For suggestions, critics, bugs, contact me: %s.\n", MANTAINER); } int main(int argc,char **argv, char **envp) { struct plugins_list_entry *list; struct plugin_requests req; struct packet_ptrs_vector pptrs; char config_file[SRVBUFLEN]; unsigned char sflow_packet[SFLOW_MAX_MSG_SIZE]; int logf, rc, yes=1, allowed; struct host_addr addr; struct hosts_table allow; struct id_table idt; struct id_table bpas_table; struct id_table blp_table; struct id_table bmed_table; struct id_table biss_table; struct id_table bta_table; struct id_table bitr_table; struct id_table sampling_table; u_int32_t idx; u_int16_t ret; SFSample spp; #if defined ENABLE_IPV6 struct sockaddr_storage server, client; struct ipv6_mreq multi_req6; #else struct sockaddr server, client; #endif int clen = sizeof(client), slen; struct ip_mreq multi_req4; unsigned char dummy_packet[64]; unsigned char dummy_packet_vlan[64]; unsigned char dummy_packet_mpls[128]; unsigned char dummy_packet_vlan_mpls[128]; struct pcap_pkthdr dummy_pkthdr; struct pcap_pkthdr dummy_pkthdr_vlan; struct pcap_pkthdr dummy_pkthdr_mpls; struct pcap_pkthdr dummy_pkthdr_vlan_mpls; #if defined ENABLE_IPV6 unsigned char dummy_packet6[92]; unsigned char dummy_packet_vlan6[92]; unsigned char dummy_packet_mpls6[128]; unsigned char dummy_packet_vlan_mpls6[128]; struct pcap_pkthdr dummy_pkthdr6; struct pcap_pkthdr dummy_pkthdr_vlan6; struct pcap_pkthdr dummy_pkthdr_mpls6; struct pcap_pkthdr dummy_pkthdr_vlan_mpls6; #endif /* getopt() stuff */ extern char *optarg; extern int optind, opterr, optopt; int errflag, cp; umask(077); compute_once(); /* a bunch of default definitions */ have_num_memory_pools = FALSE; reload_map = FALSE; tag_map_allocated = FALSE; bpas_map_allocated = FALSE; blp_map_allocated = FALSE; bmed_map_allocated = FALSE; biss_map_allocated = FALSE; bta_map_allocated = FALSE; bitr_map_allocated = FALSE; find_id_func = SF_find_id; data_plugins = 0; tee_plugins = 0; xflow_status_table_entries = 0; xflow_tot_bad_datagrams = 0; errflag = 0; memset(cfg_cmdline, 0, sizeof(cfg_cmdline)); memset(&server, 0, sizeof(server)); memset(&config, 0, sizeof(struct configuration)); memset(&config_file, 0, sizeof(config_file)); memset(&failed_plugins, 0, sizeof(failed_plugins)); memset(&pptrs, 0, sizeof(pptrs)); memset(&req, 0, sizeof(req)); memset(&spp, 0, sizeof(spp)); memset(&class, 0, sizeof(class)); memset(&xflow_status_table, 0, sizeof(xflow_status_table)); memset(&idt, 0, sizeof(idt)); memset(&bpas_table, 0, sizeof(bpas_table)); memset(&blp_table, 0, sizeof(blp_table)); memset(&bmed_table, 0, sizeof(bmed_table)); memset(&biss_table, 0, sizeof(biss_table)); memset(&bta_table, 0, sizeof(bta_table)); memset(&bitr_table, 0, sizeof(bitr_table)); memset(&sampling_table, 0, sizeof(sampling_table)); config.acct_type = ACCT_SF; rows = 0; glob_pcapt = NULL; /* getting commandline values */ while (!errflag && ((cp = getopt(argc, argv, ARGS_SFACCTD)) != -1)) { cfg_cmdline[rows] = malloc(SRVBUFLEN); switch (cp) { case 'L': strlcpy(cfg_cmdline[rows], "sfacctd_ip: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'l': strlcpy(cfg_cmdline[rows], "sfacctd_port: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'P': strlcpy(cfg_cmdline[rows], "plugins: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'D': strlcpy(cfg_cmdline[rows], "daemonize: true", SRVBUFLEN); rows++; break; case 'd': debug = TRUE; strlcpy(cfg_cmdline[rows], "debug: true", SRVBUFLEN); rows++; break; case 'n': strlcpy(cfg_cmdline[rows], "networks_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'o': strlcpy(cfg_cmdline[rows], "ports_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'O': strlcpy(cfg_cmdline[rows], "print_output: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'u': strlcpy(cfg_cmdline[rows], "print_num_protos: true", SRVBUFLEN); rows++; break; case 'f': strlcpy(config_file, optarg, sizeof(config_file)); break; case 'F': strlcpy(cfg_cmdline[rows], "pidfile: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'c': strlcpy(cfg_cmdline[rows], "aggregate: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'b': strlcpy(cfg_cmdline[rows], "imt_buckets: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'm': strlcpy(cfg_cmdline[rows], "imt_mem_pools_number: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); have_num_memory_pools = TRUE; rows++; break; case 'p': strlcpy(cfg_cmdline[rows], "imt_path: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'r': strlcpy(cfg_cmdline[rows], "sql_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; cfg_cmdline[rows] = malloc(SRVBUFLEN); strlcpy(cfg_cmdline[rows], "print_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'v': strlcpy(cfg_cmdline[rows], "sql_table_version: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 's': strlcpy(cfg_cmdline[rows], "imt_mem_pools_size: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'S': strlcpy(cfg_cmdline[rows], "syslog: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'R': strlcpy(cfg_cmdline[rows], "sfacctd_renormalize: true", SRVBUFLEN); rows++; break; case 'h': usage_daemon(argv[0]); exit(0); break; default: usage_daemon(argv[0]); exit(1); break; } } /* post-checks and resolving conflicts */ if (strlen(config_file)) { if (parse_configuration_file(config_file) != SUCCESS) exit(1); } else { if (parse_configuration_file(NULL) != SUCCESS) exit(1); } /* XXX: glue; i'm conscious it's a dirty solution from an engineering viewpoint; someday later i'll fix this */ list = plugins_list; while(list) { list->cfg.acct_type = ACCT_SF; set_default_preferences(&list->cfg); if (!strcmp(list->name, "default") && !strcmp(list->type.string, "core")) memcpy(&config, &list->cfg, sizeof(struct configuration)); list = list->next; } if (config.files_umask) umask(config.files_umask); if (config.daemon) { list = plugins_list; while (list) { if (!strcmp(list->type.string, "print")) printf("WARN ( default/core ): Daemonizing. Hmm, bye bye screen.\n"); list = list->next; } if (debug || config.debug) printf("WARN ( default/core ): debug is enabled; forking in background. Console logging will get lost.\n"); daemonize(); } initsetproctitle(argc, argv, envp); if (config.syslog) { logf = parse_log_facility(config.syslog); if (logf == ERR) { config.syslog = NULL; printf("WARN ( default/core ): specified syslog facility is not supported; logging to console.\n"); } else openlog(NULL, LOG_PID, logf); Log(LOG_INFO, "INFO ( default/core ): Start logging ...\n"); } if (config.logfile) { config.logfile_fd = open_logfile(config.logfile); list = plugins_list; while (list) { list->cfg.logfile_fd = config.logfile_fd ; list = list->next; } } /* Enforcing policies over aggregation methods */ list = plugins_list; while (list) { if (list->type.id != PLUGIN_ID_CORE) { /* applies to all plugins */ if (list->cfg.sampling_rate && config.ext_sampling_rate) { Log(LOG_ERR, "ERROR ( default/core ): Internal packet sampling and external packet sampling are mutual exclusive.\n"); exit(1); } if (list->type.id == PLUGIN_ID_NFPROBE || list->type.id == PLUGIN_ID_SFPROBE) { Log(LOG_ERR, "ERROR ( default/core ): 'nfprobe' and 'sfprobe' plugins not supported in 'sfacctd'.\n"); exit(1); } else if (list->type.id == PLUGIN_ID_TEE) { tee_plugins++; list->cfg.what_to_count = COUNT_NONE; list->cfg.data_type = PIPE_TYPE_MSG; } else { list->cfg.data_type = PIPE_TYPE_METADATA; evaluate_sums(&list->cfg.what_to_count, list->name, list->type.string); if (!list->cfg.what_to_count) { Log(LOG_WARNING, "WARN ( %s/%s ): defaulting to SRC HOST aggregation.\n", list->name, list->type.string); list->cfg.what_to_count |= COUNT_SRC_HOST; } if ((list->cfg.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) && !list->cfg.networks_file && list->cfg.nfacctd_as & NF_AS_NEW) { Log(LOG_ERR, "ERROR ( %s/%s ): AS aggregation was selected but NO 'networks_file' specified. Exiting...\n\n", list->name, list->type.string); exit(1); } if ((list->cfg.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) && !list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP) { Log(LOG_ERR, "ERROR ( %s/%s ): AS aggregation selected but 'bgp_daemon' is not enabled. Exiting...\n\n", list->name, list->type.string); exit(1); } if (list->cfg.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET|COUNT_SUM_NET|COUNT_SRC_NMASK|COUNT_DST_NMASK|COUNT_PEER_DST_IP)) { if (!list->cfg.nfacctd_net) { if (list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; if (list->cfg.networks_mask) list->cfg.nfacctd_net |= NF_NET_STATIC; if (!list->cfg.nfacctd_net) list->cfg.nfacctd_net = NF_NET_KEEP; } else { if ((list->cfg.nfacctd_net == NF_NET_NEW && !list->cfg.networks_file) || (list->cfg.nfacctd_net == NF_NET_STATIC && !list->cfg.networks_mask) || (list->cfg.nfacctd_net == NF_NET_BGP && !list->cfg.nfacctd_bgp) || (list->cfg.nfacctd_net == NF_NET_IGP && !list->cfg.nfacctd_isis)) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'bgp_daemon', 'isis_daemon', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } } } if (list->cfg.what_to_count & COUNT_CLASS && !list->cfg.classifiers_path) { Log(LOG_ERR, "ERROR ( %s/%s ): 'class' aggregation selected but NO 'classifiers' key specified. Exiting...\n\n", list->name, list->type.string); exit(1); } bgp_config_checks(&list->cfg); data_plugins++; list->cfg.what_to_count |= COUNT_COUNTERS; } } list = list->next; } if (tee_plugins && data_plugins) { Log(LOG_ERR, "ERROR: 'tee' plugins are not compatible with data (memory/mysql/pgsql/etc.) plugins. Exiting...\n\n"); exit(1); } /* signal handling we want to inherit to plugins (when not re-defined elsewhere) */ signal(SIGCHLD, startup_handle_falling_child); /* takes note of plugins failed during startup phase */ signal(SIGHUP, reload); /* handles reopening of syslog channel */ signal(SIGUSR1, push_stats); /* logs various statistics via Log() calls */ signal(SIGUSR2, reload_maps); /* sets to true the reload_maps flag */ signal(SIGPIPE, SIG_IGN); /* we want to exit gracefully when a pipe is broken */ /* If no IP address is supplied, let's set our default behaviour: IPv4 address, INADDR_ANY, port 2100 */ if (!config.nfacctd_port) config.nfacctd_port = DEFAULT_SFACCTD_PORT; #if (defined ENABLE_IPV6 && defined V4_MAPPED) if (!config.nfacctd_ip) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&server; sa6->sin6_family = AF_INET6; sa6->sin6_port = htons(config.nfacctd_port); slen = sizeof(struct sockaddr_in6); } #else if (!config.nfacctd_ip) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&server; sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = htonl(0); sa4->sin_port = htons(config.nfacctd_port); slen = sizeof(struct sockaddr_in); } #endif else { trim_spaces(config.nfacctd_ip); ret = str_to_addr(config.nfacctd_ip, &addr); if (!ret) { Log(LOG_ERR, "ERROR ( default/core ): 'sfacctd_ip' value is not valid. Exiting.\n"); exit(1); } slen = addr_to_sa((struct sockaddr *)&server, &addr, config.nfacctd_port); } /* socket creation */ config.sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_DGRAM, 0); if (config.sock < 0) { Log(LOG_ERR, "ERROR ( default/core ): socket() failed.\n"); exit(1); } /* bind socket to port */ rc = Setsocksize(config.sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)); if (rc < 0) Log(LOG_ERR, "WARN ( default/core ): Setsocksize() failed for SO_REUSEADDR.\n"); if (config.pipe_size) { rc = Setsocksize(config.sock, SOL_SOCKET, SO_RCVBUF, &config.pipe_size, sizeof(config.pipe_size)); if (rc < 0) Log(LOG_ERR, "WARN ( default/core ): Setsocksize() failed for 'plugin_pipe_size' = '%d'.\n", config.pipe_size); } /* Multicast: memberships handling */ for (idx = 0; mcast_groups[idx].family && idx < MAX_MCAST_GROUPS; idx++) { if (mcast_groups[idx].family == AF_INET) { memset(&multi_req4, 0, sizeof(multi_req4)); multi_req4.imr_multiaddr.s_addr = mcast_groups[idx].address.ipv4.s_addr; if (setsockopt(config.sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multi_req4, sizeof(multi_req4)) < 0) { Log(LOG_ERR, "ERROR: IPv4 multicast address - ADD membership failed.\n"); exit(1); } } #if defined ENABLE_IPV6 if (mcast_groups[idx].family == AF_INET6) { memset(&multi_req6, 0, sizeof(multi_req6)); ip6_addr_cpy(&multi_req6.ipv6mr_multiaddr, &mcast_groups[idx].address.ipv6); if (setsockopt(config.sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&multi_req6, sizeof(multi_req6)) < 0) { Log(LOG_ERR, "ERROR: IPv6 multicast address - ADD membership failed.\n"); exit(1); } } #endif } if (config.nfacctd_allow_file) load_allow_file(config.nfacctd_allow_file, &allow); else memset(&allow, 0, sizeof(allow)); if (config.pre_tag_map) { load_id_file(config.acct_type, config.pre_tag_map, &idt, &req, &tag_map_allocated); pptrs.v4.idtable = (u_char *) &idt; } else pptrs.v4.idtable = NULL; if (config.sampling_map) { load_id_file(MAP_SAMPLING, config.sampling_map, &sampling_table, &req, &sampling_map_allocated); set_sampling_table(&pptrs, (u_char *) &sampling_table); } else set_sampling_table(&pptrs, NULL); #if defined ENABLE_THREADS /* starting the ISIS threa */ if (config.nfacctd_isis) { req.bpf_filter = TRUE; nfacctd_isis_wrapper(); /* Let's give the ISIS thread some advantage to create its structures */ sleep(5); } /* starting the BGP thread */ if (config.nfacctd_bgp) { req.bpf_filter = TRUE; load_comm_patterns(&config.nfacctd_bgp_stdcomm_pattern, &config.nfacctd_bgp_extcomm_pattern, &config.nfacctd_bgp_stdcomm_pattern_to_asn); if (config.nfacctd_bgp_peer_as_src_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_peer_as_src_map) { load_id_file(MAP_BGP_PEER_AS_SRC, config.nfacctd_bgp_peer_as_src_map, &bpas_table, &req, &bpas_map_allocated); pptrs.v4.bpas_table = (u_char *) &bpas_table; } else { Log(LOG_ERR, "ERROR: bgp_peer_as_src_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else pptrs.v4.bpas_table = NULL; if (config.nfacctd_bgp_src_local_pref_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_local_pref_map) { load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.nfacctd_bgp_src_local_pref_map, &blp_table, &req, &blp_map_allocated); pptrs.v4.blp_table = (u_char *) &blp_table; } else { Log(LOG_ERR, "ERROR: bgp_src_local_pref_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else pptrs.v4.blp_table = NULL; if (config.nfacctd_bgp_src_med_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_med_map) { load_id_file(MAP_BGP_SRC_MED, config.nfacctd_bgp_src_med_map, &bmed_table, &req, &bmed_map_allocated); pptrs.v4.bmed_table = (u_char *) &bmed_table; } else { Log(LOG_ERR, "ERROR: bgp_src_med_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else pptrs.v4.bmed_table = NULL; if (config.nfacctd_bgp_to_agent_map) { load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.nfacctd_bgp_to_agent_map, &bta_table, &req, &bta_map_allocated); pptrs.v4.bta_table = (u_char *) &bta_table; } else pptrs.v4.bta_table = NULL; if (config.nfacctd_bgp_iface_to_rd_map) { load_id_file(MAP_BGP_IFACE_TO_RD, config.nfacctd_bgp_iface_to_rd_map, &bitr_table, &req, &bitr_map_allocated); pptrs.v4.bitr_table = (u_char *) &bitr_table; } else pptrs.v4.bitr_table = NULL; nfacctd_bgp_wrapper(); /* Let's give the BGP thread some advantage to create its structures */ sleep(5); } #else if (config.nfacctd_isis) { Log(LOG_ERR, "ERROR ( default/core ): 'isis_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } if (config.nfacctd_bgp) { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } #endif rc = bind(config.sock, (struct sockaddr *) &server, slen); if (rc < 0) { Log(LOG_ERR, "ERROR ( default/core ): bind() to ip=%s port=%d/udp failed (errno: %d).\n", config.nfacctd_ip, config.nfacctd_port, errno); exit(1); } if (config.classifiers_path) init_classifiers(config.classifiers_path); /* plugins glue: creation */ load_plugins(&req); load_plugin_filters(1); evaluate_packet_handlers(); pm_setproctitle("%s [%s]", "Core Process", "default"); if (config.pidfile) write_pid_file(config.pidfile); load_networks(config.networks_file, &nt, &nc); /* signals to be handled only by pmacctd; we set proper handlers after plugin creation */ signal(SIGINT, my_sigint_handler); signal(SIGTERM, my_sigint_handler); signal(SIGCHLD, handle_falling_child); kill(getpid(), SIGCHLD); /* arranging pointers to dummy packet; to speed up things into the main loop we mantain two packet_ptrs structures when IPv6 is enabled: we will sync here 'pptrs6' for common tables and pointers */ memset(dummy_packet, 0, sizeof(dummy_packet)); pptrs.v4.f_data = (u_char *) &spp; pptrs.v4.f_agent = (u_char *) &client; pptrs.v4.packet_ptr = dummy_packet; pptrs.v4.pkthdr = &dummy_pkthdr; Assign16(((struct eth_header *)pptrs.v4.packet_ptr)->ether_type, htons(ETHERTYPE_IP)); /* 0x800 */ pptrs.v4.mac_ptr = (u_char *)((struct eth_header *)pptrs.v4.packet_ptr)->ether_dhost; pptrs.v4.iph_ptr = pptrs.v4.packet_ptr + ETHER_HDRLEN; pptrs.v4.tlh_ptr = pptrs.v4.packet_ptr + ETHER_HDRLEN + sizeof(struct my_iphdr); Assign8(((struct my_iphdr *)pptrs.v4.iph_ptr)->ip_vhl, 5); // pptrs.v4.pkthdr->caplen = 38; /* eth_header + my_iphdr + my_tlhdr */ pptrs.v4.pkthdr->caplen = 55; pptrs.v4.pkthdr->len = 100; /* fake len */ pptrs.v4.l3_proto = ETHERTYPE_IP; memset(dummy_packet_vlan, 0, sizeof(dummy_packet_vlan)); pptrs.vlan4.f_data = pptrs.v4.f_data; pptrs.vlan4.idtable = pptrs.v4.idtable; pptrs.vlan4.f_agent = (u_char *) &client; pptrs.vlan4.packet_ptr = dummy_packet_vlan; pptrs.vlan4.pkthdr = &dummy_pkthdr_vlan; Assign16(((struct eth_header *)pptrs.vlan4.packet_ptr)->ether_type, htons(ETHERTYPE_8021Q)); pptrs.vlan4.mac_ptr = (u_char *)((struct eth_header *)pptrs.vlan4.packet_ptr)->ether_dhost; pptrs.vlan4.vlan_ptr = pptrs.vlan4.packet_ptr + ETHER_HDRLEN; Assign16(*(pptrs.vlan4.vlan_ptr+2), htons(ETHERTYPE_IP)); pptrs.vlan4.iph_ptr = pptrs.vlan4.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN; pptrs.vlan4.tlh_ptr = pptrs.vlan4.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN + sizeof(struct my_iphdr); Assign8(((struct my_iphdr *)pptrs.vlan4.iph_ptr)->ip_vhl, 5); // pptrs.vlan4.pkthdr->caplen = 42; /* eth_header + vlan + my_iphdr + my_tlhdr */ pptrs.vlan4.pkthdr->caplen = 59; pptrs.vlan4.pkthdr->len = 100; /* fake len */ pptrs.vlan4.l3_proto = ETHERTYPE_IP; memset(dummy_packet_mpls, 0, sizeof(dummy_packet_mpls)); pptrs.mpls4.f_data = pptrs.v4.f_data; pptrs.mpls4.idtable = pptrs.v4.idtable; pptrs.mpls4.f_agent = (u_char *) &client; pptrs.mpls4.packet_ptr = dummy_packet_mpls; pptrs.mpls4.pkthdr = &dummy_pkthdr_mpls; Assign16(((struct eth_header *)pptrs.mpls4.packet_ptr)->ether_type, htons(ETHERTYPE_MPLS)); pptrs.mpls4.mac_ptr = (u_char *)((struct eth_header *)pptrs.mpls4.packet_ptr)->ether_dhost; pptrs.mpls4.mpls_ptr = pptrs.mpls4.packet_ptr + ETHER_HDRLEN; // pptrs.mpls4.pkthdr->caplen = 78; /* eth_header + upto 10 MPLS labels + my_iphdr + my_tlhdr */ pptrs.mpls4.pkthdr->caplen = 95; pptrs.mpls4.pkthdr->len = 100; /* fake len */ pptrs.mpls4.l3_proto = ETHERTYPE_IP; memset(dummy_packet_vlan_mpls, 0, sizeof(dummy_packet_vlan_mpls)); pptrs.vlanmpls4.f_data = pptrs.v4.f_data; pptrs.vlanmpls4.idtable = pptrs.v4.idtable; pptrs.vlanmpls4.f_agent = (u_char *) &client; pptrs.vlanmpls4.packet_ptr = dummy_packet_vlan_mpls; pptrs.vlanmpls4.pkthdr = &dummy_pkthdr_vlan_mpls; Assign16(((struct eth_header *)pptrs.vlanmpls4.packet_ptr)->ether_type, htons(ETHERTYPE_8021Q)); pptrs.vlanmpls4.mac_ptr = (u_char *)((struct eth_header *)pptrs.vlanmpls4.packet_ptr)->ether_dhost; pptrs.vlanmpls4.vlan_ptr = pptrs.vlanmpls4.packet_ptr + ETHER_HDRLEN; Assign16(*(pptrs.vlanmpls4.vlan_ptr+2), htons(ETHERTYPE_MPLS)); pptrs.vlanmpls4.mpls_ptr = pptrs.vlanmpls4.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN; // pptrs.vlanmpls4.pkthdr->caplen = 82; /* eth_header + vlan + upto 10 MPLS labels + my_iphdr + my_tlhdr */ pptrs.vlanmpls4.pkthdr->caplen = 99; pptrs.vlanmpls4.pkthdr->len = 100; /* fake len */ pptrs.vlanmpls4.l3_proto = ETHERTYPE_IP; #if defined ENABLE_IPV6 memset(dummy_packet6, 0, sizeof(dummy_packet6)); pptrs.v6.f_data = pptrs.v4.f_data; pptrs.v6.idtable = pptrs.v4.idtable; pptrs.v6.f_agent = (u_char *) &client; pptrs.v6.packet_ptr = dummy_packet6; pptrs.v6.pkthdr = &dummy_pkthdr6; Assign16(((struct eth_header *)pptrs.v6.packet_ptr)->ether_type, htons(ETHERTYPE_IPV6)); pptrs.v6.mac_ptr = (u_char *)((struct eth_header *)pptrs.v6.packet_ptr)->ether_dhost; pptrs.v6.iph_ptr = pptrs.v6.packet_ptr + ETHER_HDRLEN; pptrs.v6.tlh_ptr = pptrs.v6.packet_ptr + ETHER_HDRLEN + sizeof(struct ip6_hdr); Assign16(((struct ip6_hdr *)pptrs.v6.iph_ptr)->ip6_plen, htons(100)); Assign16(((struct ip6_hdr *)pptrs.v6.iph_ptr)->ip6_hlim, htons(64)); // pptrs.v6.pkthdr->caplen = 60; /* eth_header + ip6_hdr + my_tlhdr */ pptrs.v6.pkthdr->caplen = 77; pptrs.v6.pkthdr->len = 100; /* fake len */ pptrs.v6.l3_proto = ETHERTYPE_IPV6; memset(dummy_packet_vlan6, 0, sizeof(dummy_packet_vlan6)); pptrs.vlan6.f_data = pptrs.v4.f_data; pptrs.vlan6.idtable = pptrs.v4.idtable; pptrs.vlan6.f_agent = (u_char *) &client; pptrs.vlan6.packet_ptr = dummy_packet_vlan6; pptrs.vlan6.pkthdr = &dummy_pkthdr_vlan6; Assign16(((struct eth_header *)pptrs.vlan6.packet_ptr)->ether_type, htons(ETHERTYPE_8021Q)); pptrs.vlan6.mac_ptr = (u_char *)((struct eth_header *)pptrs.vlan6.packet_ptr)->ether_dhost; pptrs.vlan6.vlan_ptr = pptrs.vlan6.packet_ptr + ETHER_HDRLEN; Assign8(*(pptrs.vlan6.vlan_ptr+2), 0x86); Assign8(*(pptrs.vlan6.vlan_ptr+3), 0xDD); pptrs.vlan6.iph_ptr = pptrs.vlan6.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN; pptrs.vlan6.tlh_ptr = pptrs.vlan6.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN + sizeof(struct ip6_hdr); Assign16(((struct ip6_hdr *)pptrs.vlan6.iph_ptr)->ip6_plen, htons(100)); Assign16(((struct ip6_hdr *)pptrs.vlan6.iph_ptr)->ip6_hlim, htons(64)); // pptrs.vlan6.pkthdr->caplen = 64; /* eth_header + vlan + ip6_hdr + my_tlhdr */ pptrs.vlan6.pkthdr->caplen = 81; pptrs.vlan6.pkthdr->len = 100; /* fake len */ pptrs.vlan6.l3_proto = ETHERTYPE_IPV6; memset(dummy_packet_mpls6, 0, sizeof(dummy_packet_mpls6)); pptrs.mpls6.f_data = pptrs.v4.f_data; pptrs.mpls6.idtable = pptrs.v4.idtable; pptrs.mpls6.f_agent = (u_char *) &client; pptrs.mpls6.packet_ptr = dummy_packet_mpls6; pptrs.mpls6.pkthdr = &dummy_pkthdr_mpls6; Assign16(((struct eth_header *)pptrs.mpls6.packet_ptr)->ether_type, htons(ETHERTYPE_MPLS)); pptrs.mpls6.mac_ptr = (u_char *)((struct eth_header *)pptrs.mpls6.packet_ptr)->ether_dhost; pptrs.mpls6.mpls_ptr = pptrs.mpls6.packet_ptr + ETHER_HDRLEN; // pptrs.mpls6.pkthdr->caplen = 100; /* eth_header + upto 10 MPLS labels + ip6_hdr + my_tlhdr */ pptrs.mpls6.pkthdr->caplen = 117; pptrs.mpls6.pkthdr->len = 128; /* fake len */ pptrs.mpls6.l3_proto = ETHERTYPE_IPV6; memset(dummy_packet_vlan_mpls6, 0, sizeof(dummy_packet_vlan_mpls6)); pptrs.vlanmpls6.f_data = pptrs.v4.f_data; pptrs.vlanmpls6.idtable = pptrs.v4.idtable; pptrs.vlanmpls6.f_agent = (u_char *) &client; pptrs.vlanmpls6.packet_ptr = dummy_packet_vlan_mpls6; pptrs.vlanmpls6.pkthdr = &dummy_pkthdr_vlan_mpls6; Assign16(((struct eth_header *)pptrs.vlanmpls6.packet_ptr)->ether_type, htons(ETHERTYPE_8021Q)); pptrs.vlanmpls6.mac_ptr = (u_char *)((struct eth_header *)pptrs.vlanmpls6.packet_ptr)->ether_dhost; pptrs.vlanmpls6.vlan_ptr = pptrs.vlanmpls6.packet_ptr + ETHER_HDRLEN; Assign8(*(pptrs.vlanmpls6.vlan_ptr+2), 0x88); Assign8(*(pptrs.vlanmpls6.vlan_ptr+3), 0x47); pptrs.vlanmpls6.mpls_ptr = pptrs.vlanmpls6.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN; // pptrs.vlanmpls6.pkthdr->caplen = 104; /* eth_header + vlan + upto 10 MPLS labels + ip6_hdr + my_tlhdr */ pptrs.vlanmpls6.pkthdr->caplen = 121; pptrs.vlanmpls6.pkthdr->len = 128; /* fake len */ pptrs.vlanmpls6.l3_proto = ETHERTYPE_IP; #endif { char srv_string[INET6_ADDRSTRLEN]; struct host_addr srv_addr; u_int16_t srv_port; sa_to_addr(&server, &srv_addr, &srv_port); addr_to_str(srv_string, &srv_addr); Log(LOG_INFO, "INFO ( default/core ): waiting for sFlow data on %s:%u\n", srv_string, srv_port); allowed = TRUE; } /* Main loop */ for (;;) { // memset(&spp, 0, sizeof(spp)); ret = recvfrom(config.sock, sflow_packet, SFLOW_MAX_MSG_SIZE, 0, (struct sockaddr *) &client, &clen); spp.rawSample = pptrs.v4.f_header = sflow_packet; spp.rawSampleLen = pptrs.v4.f_len = ret; spp.datap = (u_int32_t *) spp.rawSample; spp.endp = sflow_packet + spp.rawSampleLen; reset_tag_status(&pptrs); reset_shadow_status(&pptrs); // if (ret < SFLOW_MIN_MSG_SIZE) continue; /* check if Hosts Allow Table is loaded; if it is, we will enforce rules */ if (allow.num) allowed = check_allow(&allow, (struct sockaddr *)&client); if (!allowed) continue; if (reload_map) { load_networks(config.networks_file, &nt, &nc); if (config.nfacctd_bgp && config.nfacctd_bgp_peer_as_src_map) load_id_file(MAP_BGP_PEER_AS_SRC, config.nfacctd_bgp_peer_as_src_map, &bpas_table, &req, &bpas_map_allocated); if (config.nfacctd_bgp && config.nfacctd_bgp_src_local_pref_map) load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.nfacctd_bgp_src_local_pref_map, &blp_table, &req, &blp_map_allocated); if (config.nfacctd_bgp && config.nfacctd_bgp_src_med_map) load_id_file(MAP_BGP_SRC_MED, config.nfacctd_bgp_src_med_map, &bmed_table, &req, &bmed_map_allocated); if (config.nfacctd_bgp && config.nfacctd_bgp_to_agent_map) load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.nfacctd_bgp_to_agent_map, &bta_table, &req, &bta_map_allocated); if (config.nfacctd_bgp && config.nfacctd_bgp_iface_to_rd_map) load_id_file(MAP_BGP_IFACE_TO_RD, config.nfacctd_bgp_iface_to_rd_map, &bitr_table, &req, &bitr_map_allocated); if (config.pre_tag_map) load_id_file(config.acct_type, config.pre_tag_map, &idt, &req, &tag_map_allocated); if (config.sampling_map) { load_id_file(MAP_SAMPLING, config.sampling_map, &sampling_table, &req, &sampling_map_allocated); set_sampling_table(&pptrs, (u_char *) &sampling_table); } reload_map = FALSE; } if (data_plugins) { switch(spp.datagramVersion = getData32(&spp)) { case 5: getAddress(&spp, &spp.agent_addr); /* We trash the source IP address from f_agent */ if (spp.agent_addr.type == SFLADDRESSTYPE_IP_V4) { struct sockaddr *sa = (struct sockaddr *) &client; struct sockaddr_in *sa4 = (struct sockaddr_in *) &client; sa->sa_family = AF_INET; sa4->sin_addr.s_addr = spp.agent_addr.address.ip_v4.s_addr; } #if defined ENABLE_IPV6 else if (spp.agent_addr.type == SFLADDRESSTYPE_IP_V6) { struct sockaddr *sa = (struct sockaddr *) &client; struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &client; sa->sa_family = AF_INET6; ip6_addr_cpy(&sa6->sin6_addr, &spp.agent_addr.address.ip_v6); } #endif process_SFv5_packet(&spp, &pptrs, &req, (struct sockaddr *) &client); break; case 4: case 2: getAddress(&spp, &spp.agent_addr); /* We trash the source IP address from f_agent */ if (spp.agent_addr.type == SFLADDRESSTYPE_IP_V4) { struct sockaddr *sa = (struct sockaddr *) &client; struct sockaddr_in *sa4 = (struct sockaddr_in *) &client; sa->sa_family = AF_INET; sa4->sin_addr.s_addr = spp.agent_addr.address.ip_v4.s_addr; } #if defined ENABLE_IPV6 else if (spp.agent_addr.type == SFLADDRESSTYPE_IP_V6) { struct sockaddr *sa = (struct sockaddr *) &client; struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &client; sa->sa_family = AF_INET6; ip6_addr_cpy(&sa6->sin6_addr, &spp.agent_addr.address.ip_v6); } #endif process_SFv2v4_packet(&spp, &pptrs, &req, (struct sockaddr *) &client); break; default: notify_malf_packet(LOG_INFO, "INFO: Discarding unknown packet", (struct sockaddr *) pptrs.v4.f_agent); xflow_tot_bad_datagrams++; break; } } else if (tee_plugins) { process_SF_raw_packet(&spp, &pptrs, &req, (struct sockaddr *) &client); } } } void InterSampleCleanup(SFSample *spp) { u_char *start = (u_char *) spp; u_char *ptr = (u_char *) &spp->sampleType; memset(ptr, 0, SFSampleSz-(ptr-start)); } void process_SFv2v4_packet(SFSample *spp, struct packet_ptrs_vector *pptrsv, struct plugin_requests *req, struct sockaddr *agent) { u_int32_t samplesInPacket, idx; u_int32_t sampleType; spp->agentSubId = 0; /* not supported */ spp->sequenceNo = getData32(spp); spp->sysUpTime = getData32(spp); samplesInPacket = getData32(spp); pptrsv->v4.f_status = sfv245_check_status(spp, agent); set_vector_f_status(pptrsv); for (idx = 0; idx < samplesInPacket; idx++) { InterSampleCleanup(spp); set_vector_sample_type(pptrsv, 0); SFv2v4_read_sampleType: sampleType = getData32(spp); if (!pptrsv->v4.sample_type) set_vector_sample_type(pptrsv, sampleType); switch (sampleType) { case SFLFLOW_SAMPLE: readv2v4FlowSample(spp, pptrsv, req); break; case SFLCOUNTERS_SAMPLE: readv2v4CountersSample(spp); break; default: notify_malf_packet(LOG_INFO, "INFO: Discarding unknown v2/v4 sample", (struct sockaddr *) pptrsv->v4.f_agent); xflow_tot_bad_datagrams++; return; /* unexpected sampleType; aborting packet */ } if ((u_char *)spp->datap > spp->endp) return; } } void process_SFv5_packet(SFSample *spp, struct packet_ptrs_vector *pptrsv, struct plugin_requests *req, struct sockaddr *agent) { u_int32_t samplesInPacket, idx; u_int32_t sampleType; spp->agentSubId = getData32(spp); spp->sequenceNo = getData32(spp); spp->sysUpTime = getData32(spp); samplesInPacket = getData32(spp); pptrsv->v4.f_status = sfv245_check_status(spp, agent); set_vector_f_status(pptrsv); for (idx = 0; idx < samplesInPacket; idx++) { InterSampleCleanup(spp); set_vector_sample_type(pptrsv, 0); SFv5_read_sampleType: sampleType = getData32(spp); if (!pptrsv->v4.sample_type) set_vector_sample_type(pptrsv, sampleType); switch (sampleType) { case SFLFLOW_SAMPLE: readv5FlowSample(spp, FALSE, pptrsv, req); break; case SFLCOUNTERS_SAMPLE: readv5CountersSample(spp); break; case SFLFLOW_SAMPLE_EXPANDED: readv5FlowSample(spp, TRUE, pptrsv, req); break; case SFLCOUNTERS_SAMPLE_EXPANDED: readv5CountersSample(spp); break; case SFLACL_BROCADE_SAMPLE: getData32(spp); /* trash: sample length */ getData32(spp); /* trash: FoundryFlags */ getData32(spp); /* trash: FoundryGroupID */ goto SFv5_read_sampleType; /* rewind */ break; default: notify_malf_packet(LOG_INFO, "INFO: Discarding unknown v5 sample", (struct sockaddr *) pptrsv->v4.f_agent); xflow_tot_bad_datagrams++; return; /* unexpected sampleType; aborting packet */ } if ((u_char *)spp->datap > spp->endp) return; } } void process_SF_raw_packet(SFSample *spp, struct packet_ptrs_vector *pptrsv, struct plugin_requests *req, struct sockaddr *agent) { struct packet_ptrs *pptrs = &pptrsv->v4; switch(spp->datagramVersion = getData32(spp)) { case 5: getAddress(spp, &spp->agent_addr); spp->agentSubId = getData32(spp); pptrs->seqno = getData32(spp); break; case 4: case 2: getAddress(spp, &spp->agent_addr); spp->agentSubId = 0; /* not supported */ pptrs->seqno = getData32(spp); break; default: notify_malf_packet(LOG_INFO, "INFO: Discarding unknown sFlow packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } if (config.debug) { struct host_addr a; u_char agent_addr[50]; u_int16_t agent_port; sa_to_addr((struct sockaddr *)pptrs->f_agent, &a, &agent_port); addr_to_str(agent_addr, &a); Log(LOG_DEBUG, "DEBUG ( default/core ): Received sFlow packet from [%s:%u] version [%u] seqno [%u]\n", agent_addr, agent_port, spp->datagramVersion, pptrs->seqno); } if (config.pre_tag_map) SF_find_id((struct id_table *)pptrs->idtable, pptrs, &pptrs->tag, &pptrs->tag2); exec_plugins(pptrs); } void compute_once() { struct pkt_data dummy; CounterSz = sizeof(dummy.pkt_len); PdataSz = sizeof(struct pkt_data); PpayloadSz = sizeof(struct pkt_payload); PmsgSz = sizeof(struct pkt_msg); PextrasSz = sizeof(struct pkt_extras); PbgpSz = sizeof(struct pkt_bgp_primitives); ChBufHdrSz = sizeof(struct ch_buf_hdr); CharPtrSz = sizeof(char *); IP4HdrSz = sizeof(struct my_iphdr); IP4TlSz = sizeof(struct my_iphdr)+sizeof(struct my_tlhdr); SFSampleSz = sizeof(SFSample); SFLAddressSz = sizeof(SFLAddress); SFrenormEntrySz = sizeof(struct xflow_status_entry_sampling); PptrsSz = sizeof(struct packet_ptrs); CSSz = sizeof(struct class_st); HostAddrSz = sizeof(struct host_addr); UDPHdrSz = sizeof(struct my_udphdr); #if defined ENABLE_IPV6 IP6HdrSz = sizeof(struct ip6_hdr); IP6AddrSz = sizeof(struct in6_addr); IP6TlSz = sizeof(struct ip6_hdr)+sizeof(struct my_tlhdr); #endif } void notify_malf_packet(short int severity, char *ostr, struct sockaddr *sa) { struct host_addr a; u_char errstr[SRVBUFLEN]; u_char agent_addr[50] /* able to fit an IPv6 string aswell */, any[]="0.0.0.0"; u_int16_t agent_port; sa_to_addr(sa, &a, &agent_port); addr_to_str(agent_addr, &a); if (!config.nfacctd_ip) config.nfacctd_ip = any; snprintf(errstr, SRVBUFLEN, "%s: sfacctd=%s:%u agent=%s:%u \n", ostr, config.nfacctd_ip, config.nfacctd_port, agent_addr, agent_port); Log(severity, errstr); } /*_________________---------------------------__________________ _________________ lengthCheck __________________ -----------------___________________________------------------ */ int lengthCheck(SFSample *sample, u_char *start, int len) { u_int32_t actualLen = (u_char *)sample->datap - start; if (actualLen != len) { /* XXX: notify length mismatch */ return ERR; } return FALSE; } /*_________________---------------------------__________________ _________________ decodeLinkLayer __________________ -----------------___________________________------------------ store the offset to the start of the ipv4 header in the sequence_number field or -1 if not found. Decode the 802.1d if it's there. */ #define NFT_ETHHDR_SIZ 14 #define NFT_8022_SIZ 3 #define NFT_MAX_8023_LEN 1500 void decodeLinkLayer(SFSample *sample) { u_char *start = (u_char *)sample->header; u_char *end = start + sample->headerLen; u_char *ptr = start; u_int16_t caplen = end - (u_char *)sample->datap; /* assume not found */ sample->gotIPV4 = FALSE; sample->gotIPV6 = FALSE; if (caplen < NFT_ETHHDR_SIZ) return; /* not enough for an Ethernet header */ caplen -= NFT_ETHHDR_SIZ; memcpy(sample->eth_dst, ptr, 6); ptr += 6; memcpy(sample->eth_src, ptr, 6); ptr += 6; sample->eth_type = (ptr[0] << 8) + ptr[1]; ptr += 2; if (sample->eth_type == ETHERTYPE_8021Q) { /* VLAN - next two bytes */ u_int32_t vlanData = (ptr[0] << 8) + ptr[1]; u_int32_t vlan = vlanData & 0x0fff; u_int32_t priority = vlanData >> 13; if (caplen < 2) return; ptr += 2; /* _____________________________________ */ /* | pri | c | vlan-id | */ /* ------------------------------------- */ /* [priority = 3bits] [Canonical Format Flag = 1bit] [vlan-id = 12 bits] */ if (!sample->in_vlan && !sample->out_vlan) sample->in_vlan = vlan; if (!sample->in_priority && !sample->out_priority) sample->in_priority = priority; sample->eth_type = (ptr[0] << 8) + ptr[1]; ptr += 2; caplen -= 2; } if (sample->eth_type <= NFT_MAX_8023_LEN) { /* assume 802.3+802.2 header */ if (caplen < 8) return; /* check for SNAP */ if(ptr[0] == 0xAA && ptr[1] == 0xAA && ptr[2] == 0x03) { ptr += 3; if(ptr[0] != 0 || ptr[1] != 0 || ptr[2] != 0) { return; /* no further decode for vendor-specific protocol */ } ptr += 3; /* OUI == 00-00-00 means the next two bytes are the ethernet type (RFC 2895) */ sample->eth_type = (ptr[0] << 8) + ptr[1]; ptr += 2; caplen -= 8; } else { if (ptr[0] == 0x06 && ptr[1] == 0x06 && (ptr[2] & 0x01)) { /* IP over 8022 */ ptr += 3; /* force the eth_type to be IP so we can inline the IP decode below */ sample->eth_type = ETHERTYPE_IP; caplen -= 3; } else return; } } if (sample->eth_type == ETHERTYPE_MPLS || sample->eth_type == ETHERTYPE_MPLS_MULTI) { decodeMpls(sample); caplen -= sample->lstk.depth * 4; } if (sample->eth_type == ETHERTYPE_IP) { sample->gotIPV4 = TRUE; sample->offsetToIPV4 = (ptr - start); } #if defined ENABLE_IPV6 if (sample->eth_type == ETHERTYPE_IPV6) { sample->gotIPV6 = TRUE; sample->offsetToIPV6 = (ptr - start); } #endif } /*_________________---------------------------__________________ _________________ decodeIPLayer4 __________________ -----------------___________________________------------------ */ void decodeIPLayer4(SFSample *sample, u_char *ptr, u_int32_t ipProtocol) { u_char *end = sample->header + sample->headerLen; if(ptr > (end - 8)) return; // not enough header bytes left switch(ipProtocol) { case 1: /* ICMP */ { struct SF_icmphdr icmp; memcpy(&icmp, ptr, sizeof(icmp)); sample->dcd_sport = icmp.type; sample->dcd_dport = icmp.code; } break; case 6: /* TCP */ { struct SF_tcphdr tcp; memcpy(&tcp, ptr, sizeof(tcp)); sample->dcd_sport = ntohs(tcp.th_sport); sample->dcd_dport = ntohs(tcp.th_dport); sample->dcd_tcpFlags = tcp.th_flags; if(sample->dcd_dport == 80) { int bytesLeft; int headerBytes = (tcp.th_off_and_unused >> 4) * 4; ptr += headerBytes; bytesLeft = sample->header + sample->headerLen - ptr; } } break; case 17: /* UDP */ { struct SF_udphdr udp; memcpy(&udp, ptr, sizeof(udp)); sample->dcd_sport = ntohs(udp.uh_sport); sample->dcd_dport = ntohs(udp.uh_dport); sample->udp_pduLen = ntohs(udp.uh_ulen); } break; default: /* some other protcol */ break; } } /*_________________---------------------------__________________ _________________ decodeIPV4 __________________ -----------------___________________________------------------ */ void decodeIPV4(SFSample *sample) { if (sample->gotIPV4) { u_char *end = sample->header + sample->headerLen; u_char *ptr = sample->header + sample->offsetToIPV4; u_int16_t caplen = end - ptr; /* Create a local copy of the IP header (cannot overlay structure in case it is not quad-aligned...some platforms would core-dump if we tried that). It's OK coz this probably performs just as well anyway. */ struct SF_iphdr ip; if (caplen < IP4HdrSz) return; memcpy(&ip, ptr, sizeof(ip)); /* Value copy all ip elements into sample */ sample->dcd_srcIP.s_addr = ip.saddr; sample->dcd_dstIP.s_addr = ip.daddr; sample->dcd_ipProtocol = ip.protocol; sample->dcd_ipTos = ip.tos; sample->dcd_ipTTL = ip.ttl; /* check for fragments */ sample->ip_fragmentOffset = ntohs(ip.frag_off) & 0x1FFF; if (sample->ip_fragmentOffset == 0) { /* advance the pointer to the next protocol layer */ /* ip headerLen is expressed as a number of quads */ ptr += (ip.version_and_headerLen & 0x0f) * 4; decodeIPLayer4(sample, ptr, ip.protocol); } } } /*_________________---------------------------__________________ _________________ decodeIPV6 __________________ -----------------___________________________------------------ */ #if defined ENABLE_IPV6 void decodeIPV6(SFSample *sample) { u_int16_t payloadLen; u_int32_t label; u_int32_t nextHeader; u_char *end = sample->header + sample->headerLen; if(sample->gotIPV6) { u_char *ptr = sample->header + sample->offsetToIPV6; u_int16_t caplen = end - ptr; if (caplen < IP6HdrSz) return; // check the version { int ipVersion = (*ptr >> 4); if(ipVersion != 6) return; } // get the tos (priority) sample->dcd_ipTos = *ptr++ & 15; // 24-bit label label = *ptr++; label <<= 8; label += *ptr++; label <<= 8; label += *ptr++; // payload payloadLen = (ptr[0] << 8) + ptr[1]; ptr += 2; // if payload is zero, that implies a jumbo payload // next header nextHeader = *ptr++; // TTL sample->dcd_ipTTL = *ptr++; {// src and dst address sample->ipsrc.type = SFLADDRESSTYPE_IP_V6; memcpy(&sample->ipsrc.address, ptr, 16); ptr +=16; sample->ipdst.type = SFLADDRESSTYPE_IP_V6; memcpy(&sample->ipdst.address, ptr, 16); ptr +=16; } // skip over some common header extensions... // http://searchnetworking.techtarget.com/originalContent/0,289142,sid7_gci870277,00.html while(nextHeader == 0 || // hop nextHeader == 43 || // routing nextHeader == 44 || // fragment // nextHeader == 50 || // encryption - don't bother coz we'll not be able to read any further nextHeader == 51 || // auth nextHeader == 60) { // destination options u_int32_t optionLen, skip; nextHeader = ptr[0]; optionLen = 8 * (ptr[1] + 1); // second byte gives option len in 8-byte chunks, not counting first 8 skip = optionLen - 2; ptr += skip; if(ptr > end) return; // ran off the end of the header } // now that we have eliminated the extension headers, nextHeader should have what we want to // remember as the ip protocol... sample->dcd_ipProtocol = nextHeader; decodeIPLayer4(sample, ptr, sample->dcd_ipProtocol); } } #endif /*_________________---------------------------__________________ _________________ read data fns __________________ -----------------___________________________------------------ */ u_int32_t getData32(SFSample *sample) { if ((u_char *)sample->datap > sample->endp) return 0; return ntohl(*(sample->datap)++); } u_int32_t getData32_nobswap(SFSample *sample) { if ((u_char *)sample->datap > sample->endp) return 0; return *(sample->datap)++; } void skipBytes(SFSample *sample, int skip) { int quads = (skip + 3) / 4; sample->datap += quads; // if((u_char *)sample->datap > sample->endp) return 0; } u_int32_t getString(SFSample *sample, char *buf, int bufLen) { u_int32_t len, read_len; len = getData32(sample); // truncate if too long read_len = (len >= bufLen) ? (bufLen - 1) : len; memcpy(buf, sample->datap, read_len); buf[read_len] = '\0'; // null terminate skipBytes(sample, len); return len; } u_int32_t getAddress(SFSample *sample, SFLAddress *address) { address->type = getData32(sample); if(address->type == SFLADDRESSTYPE_IP_V4) address->address.ip_v4.s_addr = getData32_nobswap(sample); else { #if defined ENABLE_IPV6 memcpy(&address->address.ip_v6.s6_addr, sample->datap, 16); #endif skipBytes(sample, 16); } return address->type; } char *printTag(u_int32_t tag, char *buf, int bufLen) { // should really be: snprintf(buf, buflen,...) but snprintf() is not always available sprintf(buf, "%lu:%lu", (tag >> 12), (tag & 0x00000FFF)); return buf; } /*_________________---------------------------__________________ _________________ readExtendedSwitch __________________ -----------------___________________________------------------ */ void readExtendedSwitch(SFSample *sample) { sample->in_vlan = getData32(sample); sample->in_priority = getData32(sample); sample->out_vlan = getData32(sample); sample->out_priority = getData32(sample); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_SWITCH; } /*_________________---------------------------__________________ _________________ readExtendedRouter __________________ -----------------___________________________------------------ */ void readExtendedRouter(SFSample *sample) { u_int32_t addrType; char buf[51]; getAddress(sample, &sample->nextHop); sample->srcMask = getData32(sample); sample->dstMask = getData32(sample); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_ROUTER; } /*_________________---------------------------__________________ _________________ readExtendedGateway_v2 __________________ -----------------___________________________------------------ */ void readExtendedGateway_v2(SFSample *sample) { sample->my_as = getData32(sample); sample->src_as = getData32(sample); sample->src_peer_as = getData32(sample); sample->dst_as_path_len = getData32(sample); /* just point at the dst_as_path array */ if(sample->dst_as_path_len > 0) { // sample->dst_as_path = sample->datap; /* and skip over it in the input */ skipBytes(sample, sample->dst_as_path_len * 4); // fill in the dst and dst_peer fields too sample->dst_peer_as = ntohl(sample->dst_as_path[0]); sample->dst_as = ntohl(sample->dst_as_path[sample->dst_as_path_len - 1]); } sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_GATEWAY; } /*_________________---------------------------__________________ _________________ readExtendedGateway __________________ -----------------___________________________------------------ */ void readExtendedGateway(SFSample *sample) { int len_tot, len_asn, len_comm, idx; char asn_str[MAX_BGP_ASPATH], comm_str[MAX_BGP_STD_COMMS], space[] = " "; char buf[51]; if(sample->datagramVersion >= 5) getAddress(sample, &sample->bgp_nextHop); sample->my_as = getData32(sample); sample->src_as = getData32(sample); sample->src_peer_as = getData32(sample); sample->dst_as_path_len = getData32(sample); if (sample->dst_as_path_len > 0) { for (idx = 0, len_tot = 0; idx < sample->dst_as_path_len; idx++) { u_int32_t seg_type; u_int32_t seg_len; int i; seg_type = getData32(sample); seg_len = getData32(sample); for (i = 0; i < seg_len; i++) { u_int32_t asNumber; asNumber = getData32(sample); snprintf(asn_str, MAX_BGP_ASPATH-1, "%u", asNumber); len_asn = strlen(asn_str); len_tot = strlen(sample->dst_as_path); if ((len_tot+len_asn) < MAX_BGP_ASPATH) { strncat(sample->dst_as_path, asn_str, len_asn); } else { sample->dst_as_path[MAX_BGP_ASPATH-2] = '+'; sample->dst_as_path[MAX_BGP_ASPATH-1] = '\0'; } /* mark the first one as the dst_peer_as */ if(i == 0 && idx == 0) sample->dst_peer_as = asNumber; /* mark the last one as the dst_as */ if (idx == (sample->dst_as_path_len - 1) && i == (seg_len - 1)) sample->dst_as = asNumber; else { if (strlen(sample->dst_as_path) < (MAX_BGP_ASPATH-1)) strncat(sample->dst_as_path, space, 1); } } } } sample->communities_len = getData32(sample); /* just point at the communities array */ if (sample->communities_len > 0) { for (idx = 0, len_tot = 0; idx < sample->communities_len; idx++) { u_int32_t comm, as, val; comm = getData32(sample); switch (comm) { case COMMUNITY_INTERNET: strcpy(comm_str, "internet"); break; case COMMUNITY_NO_EXPORT: strcpy(comm_str, "no-export"); break; case COMMUNITY_NO_ADVERTISE: strcpy (comm_str, "no-advertise"); break; case COMMUNITY_LOCAL_AS: strcpy (comm_str, "local-AS"); break; default: as = (comm >> 16) & 0xFFFF; val = comm & 0xFFFF; sprintf(comm_str, "%d:%d", as, val); break; } len_comm = strlen(comm_str); len_tot = strlen(sample->comms); if ((len_tot+len_comm) < MAX_BGP_STD_COMMS) { strncat(sample->comms, comm_str, len_comm); } else { sample->comms[MAX_BGP_STD_COMMS-2] = '+'; sample->comms[MAX_BGP_STD_COMMS-1] = '\0'; } if (idx < (sample->communities_len - 1)) { if (strlen(sample->comms) < (MAX_BGP_STD_COMMS-1)) strncat(sample->comms, space, 1); } } } sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_GATEWAY; sample->localpref = getData32(sample); } /*_________________---------------------------__________________ _________________ readExtendedUser __________________ -----------------___________________________------------------ */ void readExtendedUser(SFSample *sample) { if(sample->datagramVersion >= 5) sample->src_user_charset = getData32(sample); sample->src_user_len = getString(sample, sample->src_user, SA_MAX_EXTENDED_USER_LEN); if(sample->datagramVersion >= 5) sample->dst_user_charset = getData32(sample); sample->dst_user_len = getString(sample, sample->dst_user, SA_MAX_EXTENDED_USER_LEN); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_USER; } /*_________________---------------------------__________________ _________________ readExtendedUrl __________________ -----------------___________________________------------------ */ void readExtendedUrl(SFSample *sample) { sample->url_direction = getData32(sample); sample->url_len = getString(sample, sample->url, SA_MAX_EXTENDED_URL_LEN); if(sample->datagramVersion >= 5) sample->host_len = getString(sample, sample->host, SA_MAX_EXTENDED_HOST_LEN); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_URL; } /*_________________---------------------------__________________ _________________ mplsLabelStack __________________ -----------------___________________________------------------ */ void mplsLabelStack(SFSample *sample, char *fieldName) { u_int32_t lab; sample->lstk.depth = getData32(sample); /* just point at the lablelstack array */ if (sample->lstk.depth > 0) sample->lstk.stack = (u_int32_t *)sample->datap; /* and skip over it in the input */ skipBytes(sample, sample->lstk.depth * 4); } /*_________________---------------------------__________________ _________________ readExtendedMpls __________________ -----------------___________________________------------------ */ void readExtendedMpls(SFSample *sample) { char buf[51]; getAddress(sample, &sample->mpls_nextHop); mplsLabelStack(sample, "mpls_input_stack"); mplsLabelStack(sample, "mpls_output_stack"); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_MPLS; } /*_________________---------------------------__________________ _________________ readExtendedNat __________________ -----------------___________________________------------------ */ void readExtendedNat(SFSample *sample) { char buf[51]; getAddress(sample, &sample->nat_src); getAddress(sample, &sample->nat_dst); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_NAT; } /*_________________---------------------------__________________ _________________ readExtendedMplsTunnel __________________ -----------------___________________________------------------ */ void readExtendedMplsTunnel(SFSample *sample) { #define SA_MAX_TUNNELNAME_LEN 100 char tunnel_name[SA_MAX_TUNNELNAME_LEN+1]; u_int32_t tunnel_id, tunnel_cos; getString(sample, tunnel_name, SA_MAX_TUNNELNAME_LEN); tunnel_id = getData32(sample); tunnel_cos = getData32(sample); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_MPLS_TUNNEL; } /*_________________---------------------------__________________ _________________ readExtendedMplsVC __________________ -----------------___________________________------------------ */ void readExtendedMplsVC(SFSample *sample) { #define SA_MAX_VCNAME_LEN 100 char vc_name[SA_MAX_VCNAME_LEN+1]; u_int32_t vll_vc_id, vc_cos; getString(sample, vc_name, SA_MAX_VCNAME_LEN); vll_vc_id = getData32(sample); vc_cos = getData32(sample); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_MPLS_VC; } /*_________________---------------------------__________________ _________________ readExtendedMplsFTN __________________ -----------------___________________________------------------ */ void readExtendedMplsFTN(SFSample *sample) { #define SA_MAX_FTN_LEN 100 char ftn_descr[SA_MAX_FTN_LEN+1]; u_int32_t ftn_mask; getString(sample, ftn_descr, SA_MAX_FTN_LEN); ftn_mask = getData32(sample); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_MPLS_FTN; } /*_________________---------------------------__________________ _________________ readExtendedMplsLDP_FEC __________________ -----------------___________________________------------------ */ void readExtendedMplsLDP_FEC(SFSample *sample) { u_int32_t fec_addr_prefix_len = getData32(sample); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_MPLS_LDP_FEC; } /*_________________---------------------------__________________ _________________ readExtendedVlanTunnel __________________ -----------------___________________________------------------ */ void readExtendedVlanTunnel(SFSample *sample) { SFLLabelStack lstk; lstk.depth = getData32(sample); /* just point at the lablelstack array */ if(lstk.depth > 0) lstk.stack = (u_int32_t *)sample->datap; /* and skip over it in the input */ skipBytes(sample, lstk.depth * 4); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_VLAN_TUNNEL; } /*_________________---------------------------__________________ _________________ readExtendedProcess __________________ -----------------___________________________------------------ */ void readExtendedProcess(SFSample *sample) { u_int32_t num_processes, i; num_processes = getData32(sample); for (i = 0; i < num_processes; i++) skipBytes(sample, 4); } void readExtendedClass(SFSample *sample) { u_int32_t ret; u_char buf[MAX_PROTOCOL_LEN+1], *bufptr = buf; if (config.classifiers_path) { ret = getData32_nobswap(sample); memcpy(bufptr, &ret, 4); bufptr += 4; ret = getData32_nobswap(sample); memcpy(bufptr, &ret, 4); bufptr += 4; ret = getData32_nobswap(sample); memcpy(bufptr, &ret, 4); bufptr += 4; ret = getData32_nobswap(sample); memcpy(bufptr, &ret, 4); bufptr += 4; sample->class = SF_evaluate_classifiers(buf); } else skipBytes(sample, MAX_PROTOCOL_LEN); } void readExtendedTag(SFSample *sample) { sample->tag = getData32(sample); sample->tag2 = getData32(sample); } void decodeMpls(SFSample *sample) { struct packet_ptrs dummy_pptrs; u_char *ptr = (u_char *)sample->datap, *end = sample->header + sample->headerLen; u_int16_t nl = 0, caplen = end - ptr; memset(&dummy_pptrs, 0, sizeof(dummy_pptrs)); sample->eth_type = mpls_handler(ptr, &caplen, &nl, &dummy_pptrs); if (sample->eth_type == ETHERTYPE_IP) { sample->gotIPV4 = TRUE; sample->offsetToIPV4 = nl+(ptr-sample->header); } #if defined ENABLE_IPV6 else if (sample->eth_type == ETHERTYPE_IPV6) { sample->gotIPV6 = TRUE; sample->offsetToIPV6 = nl+(ptr-sample->header); } #endif if (nl) { sample->lstk.depth = nl / 4; sample->lstk.stack = (u_int32_t *) dummy_pptrs.mpls_ptr; } } void decodePPP(SFSample *sample) { struct packet_ptrs dummy_pptrs; struct pcap_pkthdr h; u_char *ptr = (u_char *)sample->datap, *end = sample->header + sample->headerLen; u_int16_t nl = 0; memset(&dummy_pptrs, 0, sizeof(dummy_pptrs)); h.caplen = end - ptr; dummy_pptrs.packet_ptr = ptr; ppp_handler(&h, &dummy_pptrs); sample->eth_type = dummy_pptrs.l3_proto; if (dummy_pptrs.mpls_ptr) { if (dummy_pptrs.iph_ptr) nl = dummy_pptrs.iph_ptr - dummy_pptrs.mpls_ptr; if (nl) { sample->lstk.depth = nl / 4; sample->lstk.stack = (u_int32_t *) dummy_pptrs.mpls_ptr; } } if (sample->eth_type == ETHERTYPE_IP) { sample->gotIPV4 = TRUE; sample->offsetToIPV4 = dummy_pptrs.iph_ptr - sample->header; } #if defined ENABLE_IPV6 else if (sample->eth_type == ETHERTYPE_IPV6) { sample->gotIPV6 = TRUE; sample->offsetToIPV6 = dummy_pptrs.iph_ptr - sample->header; } #endif } /*_________________---------------------------__________________ _________________ readFlowSample_header __________________ -----------------___________________________------------------ */ void readFlowSample_header(SFSample *sample) { sample->headerProtocol = getData32(sample); sample->sampledPacketSize = getData32(sample); if(sample->datagramVersion > 4) sample->stripped = getData32(sample); sample->headerLen = getData32(sample); sample->header = (u_char *)sample->datap; /* just point at the header */ switch(sample->headerProtocol) { /* the header protocol tells us where to jump into the decode */ case SFLHEADER_ETHERNET_ISO8023: decodeLinkLayer(sample); break; case SFLHEADER_IPv4: sample->gotIPV4 = TRUE; sample->offsetToIPV4 = 0; break; #if defined ENABLE_IPV6 case SFLHEADER_IPv6: sample->gotIPV6 = TRUE; sample->offsetToIPV6 = 0; break; #endif case SFLHEADER_MPLS: decodeMpls(sample); break; case SFLHEADER_PPP: decodePPP(sample); break; case SFLHEADER_ISO88024_TOKENBUS: case SFLHEADER_ISO88025_TOKENRING: case SFLHEADER_FDDI: case SFLHEADER_FRAME_RELAY: case SFLHEADER_X25: case SFLHEADER_SMDS: case SFLHEADER_AAL5: case SFLHEADER_AAL5_IP: default: /* XXX: nofity error */ break; } if (sample->gotIPV4) decodeIPV4(sample); #if defined ENABLE_IPV6 else if (sample->gotIPV6) decodeIPV6(sample); #endif skipBytes(sample, sample->headerLen); } /*_________________---------------------------__________________ _________________ readFlowSample_ethernet __________________ -----------------___________________________------------------ */ void readFlowSample_ethernet(SFSample *sample) { sample->eth_len = getData32(sample); memcpy(sample->eth_src, sample->datap, 6); skipBytes(sample, 6); memcpy(sample->eth_dst, sample->datap, 6); skipBytes(sample, 6); sample->eth_type = getData32(sample); if (sample->eth_type == ETHERTYPE_IP) sample->gotIPV4 = TRUE; #if defined ENABLE_IPV6 if (sample->eth_type == ETHERTYPE_IPV6) sample->gotIPV6 = TRUE; #endif } /*_________________---------------------------__________________ _________________ readFlowSample_IPv4 __________________ -----------------___________________________------------------ */ void readFlowSample_IPv4(SFSample *sample) { sample->headerLen = sizeof(SFLSampled_ipv4); sample->header = (u_char *)sample->datap; /* just point at the header */ skipBytes(sample, sample->headerLen); { SFLSampled_ipv4 nfKey; memcpy(&nfKey, sample->header, sizeof(nfKey)); sample->sampledPacketSize = ntohl(nfKey.length); sample->dcd_srcIP = nfKey.src_ip; sample->dcd_dstIP = nfKey.dst_ip; sample->dcd_ipProtocol = ntohl(nfKey.protocol); sample->dcd_ipTos = ntohl(nfKey.tos); sample->dcd_sport = ntohl(nfKey.src_port); sample->dcd_dport = ntohl(nfKey.dst_port); } sample->gotIPV4 = TRUE; } /*_________________---------------------------__________________ _________________ readFlowSample_IPv6 __________________ -----------------___________________________------------------ */ void readFlowSample_IPv6(SFSample *sample) { sample->header = (u_char *)sample->datap; /* just point at the header */ sample->headerLen = sizeof(SFLSampled_ipv6); skipBytes(sample, sample->headerLen); #if defined ENABLE_IPV6 { SFLSampled_ipv6 nfKey6; memcpy(&nfKey6, sample->header, sizeof(nfKey6)); sample->sampledPacketSize = ntohl(nfKey6.length); sample->ipsrc.type = SFLADDRESSTYPE_IP_V6; memcpy(&sample->ipsrc.address, &nfKey6.src_ip, IP6AddrSz); sample->ipdst.type = SFLADDRESSTYPE_IP_V6; memcpy(&sample->ipdst.address, &nfKey6.dst_ip, IP6AddrSz); sample->dcd_ipProtocol = ntohl(nfKey6.protocol); sample->dcd_ipTos = ntohl(nfKey6.priority); sample->dcd_sport = ntohl(nfKey6.src_port); sample->dcd_dport = ntohl(nfKey6.dst_port); } sample->gotIPV6 = TRUE; #endif } /*_________________---------------------------__________________ _________________ readv2v4FlowSample __________________ -----------------___________________________------------------ */ void readv2v4FlowSample(SFSample *sample, struct packet_ptrs_vector *pptrsv, struct plugin_requests *req) { sample->samplesGenerated = getData32(sample); { u_int32_t samplerId = getData32(sample); sample->ds_class = samplerId >> 24; sample->ds_index = samplerId & 0x00ffffff; } sample->meanSkipCount = getData32(sample); sample->samplePool = getData32(sample); sample->dropEvents = getData32(sample); sample->inputPort = getData32(sample); sample->outputPort = getData32(sample); sample->packet_data_tag = getData32(sample); switch(sample->packet_data_tag) { case INMPACKETTYPE_HEADER: readFlowSample_header(sample); break; case INMPACKETTYPE_IPV4: readFlowSample_IPv4(sample); break; case INMPACKETTYPE_IPV6: readFlowSample_IPv6(sample); break; default: notify_malf_packet(LOG_INFO, "INFO: Discarding unknown v2/v4 Data Tag", (struct sockaddr *) pptrsv->v4.f_agent); xflow_tot_bad_datagrams++; break; } sample->extended_data_tag = 0; { u_int32_t x; sample->num_extended = getData32(sample); for(x = 0; x < sample->num_extended; x++) { u_int32_t extended_tag; extended_tag = getData32(sample); switch(extended_tag) { case INMEXTENDED_SWITCH: readExtendedSwitch(sample); break; case INMEXTENDED_ROUTER: readExtendedRouter(sample); break; case INMEXTENDED_GATEWAY: if(sample->datagramVersion == 2) readExtendedGateway_v2(sample); else readExtendedGateway(sample); break; case INMEXTENDED_USER: readExtendedUser(sample); break; case INMEXTENDED_URL: readExtendedUrl(sample); break; default: notify_malf_packet(LOG_INFO, "INFO: Discarding unknown v2/v4 Extended Data Tag", (struct sockaddr *) pptrsv->v4.f_agent); xflow_tot_bad_datagrams++; break; } } } finalizeSample(sample, pptrsv, req); } /*_________________---------------------------__________________ _________________ readv5FlowSample __________________ -----------------___________________________------------------ */ void readv5FlowSample(SFSample *sample, int expanded, struct packet_ptrs_vector *pptrsv, struct plugin_requests *req) { u_int32_t num_elements, sampleLength, actualSampleLength; u_char *sampleStart; sampleLength = getData32(sample); sampleStart = (u_char *)sample->datap; sample->samplesGenerated = getData32(sample); if(expanded) { sample->ds_class = getData32(sample); sample->ds_index = getData32(sample); } else { u_int32_t samplerId = getData32(sample); sample->ds_class = samplerId >> 24; sample->ds_index = samplerId & 0x00ffffff; } sample->meanSkipCount = getData32(sample); sample->samplePool = getData32(sample); sample->dropEvents = getData32(sample); if(expanded) { sample->inputPortFormat = getData32(sample); sample->inputPort = getData32(sample); sample->outputPortFormat = getData32(sample); sample->outputPort = getData32(sample); } else { u_int32_t inp, outp; inp = getData32(sample); outp = getData32(sample); sample->inputPortFormat = inp >> 30; sample->outputPortFormat = outp >> 30; sample->inputPort = inp; // skip 0x3fffffff mask sample->outputPort = outp; // skip 0x3fffffff mask } num_elements = getData32(sample); { int el; for (el = 0; el < num_elements; el++) { u_int32_t tag, length; u_char *start; tag = getData32(sample); length = getData32(sample); start = (u_char *)sample->datap; switch(tag) { case SFLFLOW_HEADER: readFlowSample_header(sample); break; case SFLFLOW_ETHERNET: readFlowSample_ethernet(sample); break; case SFLFLOW_IPV4: readFlowSample_IPv4(sample); break; case SFLFLOW_IPV6: readFlowSample_IPv6(sample); break; case SFLFLOW_EX_SWITCH: readExtendedSwitch(sample); break; case SFLFLOW_EX_ROUTER: readExtendedRouter(sample); break; case SFLFLOW_EX_GATEWAY: readExtendedGateway(sample); break; case SFLFLOW_EX_USER: readExtendedUser(sample); break; case SFLFLOW_EX_URL: readExtendedUrl(sample); break; case SFLFLOW_EX_MPLS: readExtendedMpls(sample); break; case SFLFLOW_EX_NAT: readExtendedNat(sample); break; case SFLFLOW_EX_MPLS_TUNNEL: readExtendedMplsTunnel(sample); break; case SFLFLOW_EX_MPLS_VC: readExtendedMplsVC(sample); break; case SFLFLOW_EX_MPLS_FTN: readExtendedMplsFTN(sample); break; case SFLFLOW_EX_MPLS_LDP_FEC: readExtendedMplsLDP_FEC(sample); break; case SFLFLOW_EX_VLAN_TUNNEL: readExtendedVlanTunnel(sample); break; case SFLFLOW_EX_PROCESS: readExtendedProcess(sample); break; case SFLFLOW_EX_CLASS: readExtendedClass(sample); break; case SFLFLOW_EX_TAG: readExtendedTag(sample); break; default: // lengthCheck() here for extra security before skipBytes() if (lengthCheck(sample, start, length) == ERR) return; skipBytes(sample, length); break; } if (lengthCheck(sample, start, length) == ERR) return; } } if (lengthCheck(sample, sampleStart, sampleLength) == ERR) return; finalizeSample(sample, pptrsv, req); } void readv5CountersSample(SFSample *sample) { u_int32_t sampleLength; sampleLength = getData32(sample); skipBytes(sample, sampleLength); } /* seems like sFlow v2/v4 does not supply any meaningful information about the length of current sample. This is because we still need to parse the very first part of the sample */ void readv2v4CountersSample(SFSample *sample) { skipBytes(sample, 12); sample->counterBlockVersion = getData32(sample); switch(sample->counterBlockVersion) { case INMCOUNTERSVERSION_GENERIC: case INMCOUNTERSVERSION_ETHERNET: case INMCOUNTERSVERSION_TOKENRING: case INMCOUNTERSVERSION_FDDI: case INMCOUNTERSVERSION_VG: case INMCOUNTERSVERSION_WAN: skipBytes(sample, 88); break; case INMCOUNTERSVERSION_VLAN: break; default: return; } /* now see if there are any specific counter blocks to add */ switch(sample->counterBlockVersion) { case INMCOUNTERSVERSION_GENERIC: /* nothing more */ break; case INMCOUNTERSVERSION_ETHERNET: skipBytes(sample, 52); break; case INMCOUNTERSVERSION_TOKENRING: skipBytes(sample, 72); break; case INMCOUNTERSVERSION_FDDI: break; case INMCOUNTERSVERSION_VG: skipBytes(sample, 80); break; case INMCOUNTERSVERSION_WAN: break; case INMCOUNTERSVERSION_VLAN: skipBytes(sample, 28); break; default: return; } } void finalizeSample(SFSample *sample, struct packet_ptrs_vector *pptrsv, struct plugin_requests *req) { struct packet_ptrs *pptrs = &pptrsv->v4; u_int16_t dcd_sport = htons(sample->dcd_sport), dcd_dport = htons(sample->dcd_dport); u_int8_t dcd_ipProtocol = sample->dcd_ipProtocol, dcd_ipTos = sample->dcd_ipTos; u_int8_t dcd_tcpFlags = sample->dcd_tcpFlags; u_int16_t vlan = htons(sample->in_vlan); u_int16_t flow_type; /* check for out_vlan */ if (!vlan && sample->out_vlan) vlan = htons(sample->out_vlan); /* We consider packets if: - sample->gotIPV4 || sample->gotIPV6 : it belongs to either an IPv4 or IPv6 packet. - !sample->eth_type : we don't know the L3 protocol. VLAN or MPLS accounting case. */ if (sample->gotIPV4 || sample->gotIPV6 || !sample->eth_type) { reset_net_status_v(pptrsv); flow_type = SF_evaluate_flow_type(pptrs); /* we need to understand the IP protocol version in order to build the fake packet */ switch (flow_type) { case NF9_FTYPE_IPV4: if (req->bpf_filter) { reset_mac(pptrs); reset_ip4(pptrs); memcpy(pptrs->mac_ptr+ETH_ADDR_LEN, &sample->eth_src, ETH_ADDR_LEN); memcpy(pptrs->mac_ptr, &sample->eth_dst, ETH_ADDR_LEN); ((struct my_iphdr *)pptrs->iph_ptr)->ip_vhl = 0x45; memcpy(&((struct my_iphdr *)pptrs->iph_ptr)->ip_src, &sample->dcd_srcIP, 4); memcpy(&((struct my_iphdr *)pptrs->iph_ptr)->ip_dst, &sample->dcd_dstIP, 4); memcpy(&((struct my_iphdr *)pptrs->iph_ptr)->ip_p, &dcd_ipProtocol, 1); memcpy(&((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, &dcd_ipTos, 1); memcpy(&((struct my_tlhdr *)pptrs->tlh_ptr)->src_port, &dcd_sport, 2); memcpy(&((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port, &dcd_dport, 2); memcpy(&((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags, &dcd_tcpFlags, 1); } pptrs->lm_mask_src = sample->srcMask; pptrs->lm_mask_dst = sample->dstMask; pptrs->lm_method_src = NF_NET_KEEP; pptrs->lm_method_dst = NF_NET_KEEP; pptrs->l4_proto = sample->dcd_ipProtocol; if (config.nfacctd_isis) isis_srcdst_lookup(pptrs); if (config.nfacctd_bgp_to_agent_map) SF_find_id((struct id_table *)pptrs->bta_table, pptrs, &pptrs->bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) SF_find_id((struct id_table *)pptrs->bitr_table, pptrs, &pptrs->bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(pptrs); if (config.nfacctd_bgp_peer_as_src_map) SF_find_id((struct id_table *)pptrs->bpas_table, pptrs, &pptrs->bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) SF_find_id((struct id_table *)pptrs->blp_table, pptrs, &pptrs->blp, NULL); if (config.nfacctd_bgp_src_med_map) SF_find_id((struct id_table *)pptrs->bmed_table, pptrs, &pptrs->bmed, NULL); if (config.pre_tag_map) SF_find_id((struct id_table *)pptrs->idtable, pptrs, &pptrs->tag, &pptrs->tag2); exec_plugins(pptrs); break; #if defined ENABLE_IPV6 case NF9_FTYPE_IPV6: if (req->bpf_filter) { reset_mac(&pptrsv->v6); reset_ip6(&pptrsv->v6); ((struct ip6_hdr *)pptrsv->v6.iph_ptr)->ip6_ctlun.ip6_un2_vfc = 0x60; memcpy(pptrsv->v6.mac_ptr+ETH_ADDR_LEN, &sample->eth_src, ETH_ADDR_LEN); memcpy(pptrsv->v6.mac_ptr, &sample->eth_dst, ETH_ADDR_LEN); memcpy(&((struct ip6_hdr *)pptrsv->v6.iph_ptr)->ip6_src, &sample->ipsrc.address.ip_v6, IP6AddrSz); memcpy(&((struct ip6_hdr *)pptrsv->v6.iph_ptr)->ip6_dst, &sample->ipdst.address.ip_v6, IP6AddrSz); memcpy(&((struct ip6_hdr *)pptrsv->v6.iph_ptr)->ip6_nxt, &dcd_ipProtocol, 1); /* XXX: class ID ? */ memcpy(&((struct my_tlhdr *)pptrsv->v6.tlh_ptr)->src_port, &dcd_sport, 2); memcpy(&((struct my_tlhdr *)pptrsv->v6.tlh_ptr)->dst_port, &dcd_dport, 2); memcpy(&((struct my_tcphdr *)pptrsv->v6.tlh_ptr)->th_flags, &dcd_tcpFlags, 1); } pptrsv->v6.lm_mask_src = sample->srcMask; pptrsv->v6.lm_mask_dst = sample->dstMask; pptrsv->v6.lm_method_src = NF_NET_KEEP; pptrsv->v6.lm_method_dst = NF_NET_KEEP; pptrsv->v6.l4_proto = sample->dcd_ipProtocol; if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->v6); if (config.nfacctd_bgp_to_agent_map) SF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->v6, &pptrsv->v6.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) SF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->v6, &pptrsv->v6.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->v6); if (config.nfacctd_bgp_peer_as_src_map) SF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->v6, &pptrsv->v6.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) SF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->v6, &pptrsv->v6.blp, NULL); if (config.nfacctd_bgp_src_med_map) SF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->v6, &pptrsv->v6.bmed, NULL); if (config.pre_tag_map) SF_find_id((struct id_table *)pptrs->idtable, &pptrsv->v6, &pptrsv->v6.tag, &pptrsv->v6.tag2); exec_plugins(&pptrsv->v6); break; #endif case NF9_FTYPE_VLAN_IPV4: if (req->bpf_filter) { reset_mac_vlan(&pptrsv->vlan4); reset_ip4(&pptrsv->vlan4); memcpy(pptrsv->vlan4.mac_ptr+ETH_ADDR_LEN, &sample->eth_src, ETH_ADDR_LEN); memcpy(pptrsv->vlan4.mac_ptr, &sample->eth_dst, ETH_ADDR_LEN); memcpy(pptrsv->vlan4.vlan_ptr, &vlan, 2); ((struct my_iphdr *)pptrsv->vlan4.iph_ptr)->ip_vhl = 0x45; memcpy(&((struct my_iphdr *)pptrsv->vlan4.iph_ptr)->ip_src, &sample->dcd_srcIP, 4); memcpy(&((struct my_iphdr *)pptrsv->vlan4.iph_ptr)->ip_dst, &sample->dcd_dstIP, 4); memcpy(&((struct my_iphdr *)pptrsv->vlan4.iph_ptr)->ip_p, &dcd_ipProtocol, 1); memcpy(&((struct my_iphdr *)pptrsv->vlan4.iph_ptr)->ip_tos, &dcd_ipTos, 1); memcpy(&((struct my_tlhdr *)pptrsv->vlan4.tlh_ptr)->src_port, &dcd_sport, 2); memcpy(&((struct my_tlhdr *)pptrsv->vlan4.tlh_ptr)->dst_port, &dcd_dport, 2); memcpy(&((struct my_tcphdr *)pptrsv->vlan4.tlh_ptr)->th_flags, &dcd_tcpFlags, 1); } pptrsv->vlan4.lm_mask_src = sample->srcMask; pptrsv->vlan4.lm_mask_dst = sample->dstMask; pptrsv->vlan4.lm_method_src = NF_NET_KEEP; pptrsv->vlan4.lm_method_dst = NF_NET_KEEP; pptrsv->vlan4.l4_proto = sample->dcd_ipProtocol; if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->vlan4); if (config.nfacctd_bgp_to_agent_map) SF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->vlan4, &pptrsv->vlan4.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) SF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->vlan4, &pptrsv->vlan4.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->vlan4); if (config.nfacctd_bgp_peer_as_src_map) SF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->vlan4, &pptrsv->vlan4.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) SF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->vlan4, &pptrsv->vlan4.blp, NULL); if (config.nfacctd_bgp_src_med_map) SF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->vlan4, &pptrsv->vlan4.bmed, NULL); if (config.pre_tag_map) SF_find_id((struct id_table *)pptrs->idtable, &pptrsv->vlan4, &pptrsv->vlan4.tag, &pptrsv->vlan4.tag2); exec_plugins(&pptrsv->vlan4); break; #if defined ENABLE_IPV6 case NF9_FTYPE_VLAN_IPV6: if (req->bpf_filter) { reset_mac_vlan(&pptrsv->vlan6); reset_ip6(&pptrsv->vlan6); memcpy(pptrsv->vlan6.mac_ptr+ETH_ADDR_LEN, &sample->eth_src, ETH_ADDR_LEN); memcpy(pptrsv->vlan6.mac_ptr, &sample->eth_dst, ETH_ADDR_LEN); memcpy(pptrsv->vlan6.vlan_ptr, &vlan, 2); ((struct ip6_hdr *)pptrsv->vlan6.iph_ptr)->ip6_ctlun.ip6_un2_vfc = 0x60; memcpy(&((struct ip6_hdr *)pptrsv->vlan6.iph_ptr)->ip6_src, &sample->ipsrc.address.ip_v6, IP6AddrSz); memcpy(&((struct ip6_hdr *)pptrsv->vlan6.iph_ptr)->ip6_dst, &sample->ipdst.address.ip_v6, IP6AddrSz); memcpy(&((struct ip6_hdr *)pptrsv->vlan6.iph_ptr)->ip6_nxt, &dcd_ipProtocol, 1); /* XXX: class ID ? */ memcpy(&((struct my_tlhdr *)pptrsv->vlan6.tlh_ptr)->src_port, &dcd_sport, 2); memcpy(&((struct my_tlhdr *)pptrsv->vlan6.tlh_ptr)->dst_port, &dcd_dport, 2); memcpy(&((struct my_tcphdr *)pptrsv->vlan6.tlh_ptr)->th_flags, &dcd_tcpFlags, 1); } pptrsv->vlan6.lm_mask_src = sample->srcMask; pptrsv->vlan6.lm_mask_dst = sample->dstMask; pptrsv->vlan6.lm_method_src = NF_NET_KEEP; pptrsv->vlan6.lm_method_dst = NF_NET_KEEP; pptrsv->vlan6.l4_proto = sample->dcd_ipProtocol; if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->vlan6); if (config.nfacctd_bgp_to_agent_map) SF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->vlan6, &pptrsv->vlan6.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) SF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->vlan6, &pptrsv->vlan6.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->vlan6); if (config.nfacctd_bgp_peer_as_src_map) SF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->vlan6, &pptrsv->vlan6.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) SF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->vlan6, &pptrsv->vlan6.blp, NULL); if (config.nfacctd_bgp_src_med_map) SF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->vlan6, &pptrsv->vlan6.bmed, NULL); if (config.pre_tag_map) SF_find_id((struct id_table *)pptrs->idtable, &pptrsv->vlan6, &pptrsv->vlan6.tag, &pptrsv->vlan6.tag2); exec_plugins(&pptrsv->vlan6); break; #endif case NF9_FTYPE_MPLS_IPV4: if (req->bpf_filter) { u_char *ptr = pptrsv->mpls4.mpls_ptr; u_int32_t label, idx; /* XXX: fix caplen */ reset_mac(&pptrsv->mpls4); memcpy(pptrsv->mpls4.mac_ptr+ETH_ADDR_LEN, &sample->eth_src, ETH_ADDR_LEN); memcpy(pptrsv->mpls4.mac_ptr, &sample->eth_dst, ETH_ADDR_LEN); for (idx = 0; idx <= sample->lstk.depth && idx < 10; idx++) { label = sample->lstk.stack[idx]; memcpy(ptr, &label, 4); ptr += 4; } stick_bosbit(ptr-4); pptrsv->mpls4.iph_ptr = ptr; pptrsv->mpls4.tlh_ptr = ptr + IP4HdrSz; reset_ip4(&pptrsv->mpls4); ((struct my_iphdr *)pptrsv->mpls4.iph_ptr)->ip_vhl = 0x45; memcpy(&((struct my_iphdr *)pptrsv->mpls4.iph_ptr)->ip_src, &sample->dcd_srcIP, 4); memcpy(&((struct my_iphdr *)pptrsv->mpls4.iph_ptr)->ip_dst, &sample->dcd_dstIP, 4); memcpy(&((struct my_iphdr *)pptrsv->mpls4.iph_ptr)->ip_p, &dcd_ipProtocol, 1); memcpy(&((struct my_iphdr *)pptrsv->mpls4.iph_ptr)->ip_tos, &dcd_ipTos, 1); memcpy(&((struct my_tlhdr *)pptrsv->mpls4.tlh_ptr)->src_port, &dcd_sport, 2); memcpy(&((struct my_tlhdr *)pptrsv->mpls4.tlh_ptr)->dst_port, &dcd_dport, 2); memcpy(&((struct my_tcphdr *)pptrsv->mpls4.tlh_ptr)->th_flags, &dcd_tcpFlags, 1); } pptrsv->mpls4.lm_mask_src = sample->srcMask; pptrsv->mpls4.lm_mask_dst = sample->dstMask; pptrsv->mpls4.lm_method_src = NF_NET_KEEP; pptrsv->mpls4.lm_method_dst = NF_NET_KEEP; pptrsv->mpls4.l4_proto = sample->dcd_ipProtocol; if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->mpls4); if (config.nfacctd_bgp_to_agent_map) SF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->mpls4, &pptrsv->mpls4.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) SF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->mpls4, &pptrsv->mpls4.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->mpls4); if (config.nfacctd_bgp_peer_as_src_map) SF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->mpls4, &pptrsv->mpls4.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) SF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->mpls4, &pptrsv->mpls4.blp, NULL); if (config.nfacctd_bgp_src_med_map) SF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->mpls4, &pptrsv->mpls4.bmed, NULL); if (config.pre_tag_map) SF_find_id((struct id_table *)pptrs->idtable, &pptrsv->mpls4, &pptrsv->mpls4.tag, &pptrsv->mpls4.tag2); exec_plugins(&pptrsv->mpls4); break; #if defined ENABLE_IPV6 case NF9_FTYPE_MPLS_IPV6: if (req->bpf_filter) { u_char *ptr = pptrsv->mpls6.mpls_ptr; u_int32_t label, idx; /* XXX: fix caplen */ reset_mac(&pptrsv->mpls6); memcpy(pptrsv->mpls6.mac_ptr+ETH_ADDR_LEN, &sample->eth_src, ETH_ADDR_LEN); memcpy(pptrsv->mpls6.mac_ptr, &sample->eth_dst, ETH_ADDR_LEN); for (idx = 0; idx <= sample->lstk.depth && idx < 10; idx++) { label = sample->lstk.stack[idx]; memcpy(ptr, &label, 4); ptr += 4; } stick_bosbit(ptr-4); pptrsv->mpls6.iph_ptr = ptr; pptrsv->mpls6.tlh_ptr = ptr + IP6HdrSz; reset_ip6(&pptrsv->mpls6); ((struct ip6_hdr *)pptrsv->mpls6.iph_ptr)->ip6_ctlun.ip6_un2_vfc = 0x60; memcpy(&((struct ip6_hdr *)pptrsv->mpls6.iph_ptr)->ip6_src, &sample->ipsrc.address.ip_v6, IP6AddrSz); memcpy(&((struct ip6_hdr *)pptrsv->mpls6.iph_ptr)->ip6_dst, &sample->ipdst.address.ip_v6, IP6AddrSz); memcpy(&((struct ip6_hdr *)pptrsv->mpls6.iph_ptr)->ip6_nxt, &dcd_ipProtocol, 1); /* XXX: class ID ? */ memcpy(&((struct my_tlhdr *)pptrsv->mpls6.tlh_ptr)->src_port, &dcd_sport, 2); memcpy(&((struct my_tlhdr *)pptrsv->mpls6.tlh_ptr)->dst_port, &dcd_dport, 2); memcpy(&((struct my_tcphdr *)pptrsv->mpls6.tlh_ptr)->th_flags, &dcd_tcpFlags, 1); } pptrsv->mpls6.lm_mask_src = sample->srcMask; pptrsv->mpls6.lm_mask_dst = sample->dstMask; pptrsv->mpls6.lm_method_src = NF_NET_KEEP; pptrsv->mpls6.lm_method_dst = NF_NET_KEEP; pptrsv->mpls6.l4_proto = sample->dcd_ipProtocol; if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->mpls6); if (config.nfacctd_bgp_to_agent_map) SF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->mpls6, &pptrsv->mpls6.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) SF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->mpls6, &pptrsv->mpls6.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->mpls6); if (config.nfacctd_bgp_peer_as_src_map) SF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->mpls6, &pptrsv->mpls6.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) SF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->mpls6, &pptrsv->mpls6.blp, NULL); if (config.nfacctd_bgp_src_med_map) SF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->mpls6, &pptrsv->mpls6.bmed, NULL); if (config.pre_tag_map) SF_find_id((struct id_table *)pptrs->idtable, &pptrsv->mpls6, &pptrsv->mpls6.tag, &pptrsv->mpls6.tag2); exec_plugins(&pptrsv->mpls6); break; #endif case NF9_FTYPE_VLAN_MPLS_IPV4: if (req->bpf_filter) { u_char *ptr = pptrsv->vlanmpls4.mpls_ptr; u_int32_t label, idx; /* XXX: fix caplen */ reset_mac_vlan(&pptrsv->vlanmpls4); memcpy(pptrsv->vlanmpls4.mac_ptr+ETH_ADDR_LEN, &sample->eth_src, ETH_ADDR_LEN); memcpy(pptrsv->vlanmpls4.mac_ptr, &sample->eth_dst, ETH_ADDR_LEN); memcpy(pptrsv->vlanmpls4.vlan_ptr, &vlan, 2); for (idx = 0; idx <= sample->lstk.depth && idx < 10; idx++) { label = sample->lstk.stack[idx]; memcpy(ptr, &label, 4); ptr += 4; } stick_bosbit(ptr-4); pptrsv->vlanmpls4.iph_ptr = ptr; pptrsv->vlanmpls4.tlh_ptr = ptr + IP4HdrSz; reset_ip4(&pptrsv->vlanmpls4); ((struct my_iphdr *)pptrsv->vlanmpls4.iph_ptr)->ip_vhl = 0x45; memcpy(&((struct my_iphdr *)pptrsv->vlanmpls4.iph_ptr)->ip_src, &sample->dcd_srcIP, 4); memcpy(&((struct my_iphdr *)pptrsv->vlanmpls4.iph_ptr)->ip_dst, &sample->dcd_dstIP, 4); memcpy(&((struct my_iphdr *)pptrsv->vlanmpls4.iph_ptr)->ip_p, &dcd_ipProtocol, 1); memcpy(&((struct my_iphdr *)pptrsv->vlanmpls4.iph_ptr)->ip_tos, &dcd_ipTos, 1); memcpy(&((struct my_tlhdr *)pptrsv->vlanmpls4.tlh_ptr)->src_port, &dcd_sport, 2); memcpy(&((struct my_tlhdr *)pptrsv->vlanmpls4.tlh_ptr)->dst_port, &dcd_dport, 2); memcpy(&((struct my_tcphdr *)pptrsv->vlanmpls4.tlh_ptr)->th_flags, &dcd_tcpFlags, 1); } pptrsv->vlanmpls4.lm_mask_src = sample->srcMask; pptrsv->vlanmpls4.lm_mask_dst = sample->dstMask; pptrsv->vlanmpls4.lm_method_src = NF_NET_KEEP; pptrsv->vlanmpls4.lm_method_dst = NF_NET_KEEP; pptrsv->vlanmpls4.l4_proto = sample->dcd_ipProtocol; if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->vlanmpls4); if (config.nfacctd_bgp_to_agent_map) SF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) SF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->vlanmpls4); if (config.nfacctd_bgp_peer_as_src_map) SF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) SF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.blp, NULL); if (config.nfacctd_bgp_src_med_map) SF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.bmed, NULL); if (config.pre_tag_map) SF_find_id((struct id_table *)pptrs->idtable, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.tag, &pptrsv->vlanmpls4.tag2); exec_plugins(&pptrsv->vlanmpls4); break; #if defined ENABLE_IPV6 case NF9_FTYPE_VLAN_MPLS_IPV6: if (req->bpf_filter) { u_char *ptr = pptrsv->vlanmpls6.mpls_ptr; u_int32_t label, idx; /* XXX: fix caplen */ reset_mac_vlan(&pptrsv->vlanmpls6); memcpy(pptrsv->vlanmpls6.mac_ptr+ETH_ADDR_LEN, &sample->eth_src, ETH_ADDR_LEN); memcpy(pptrsv->vlanmpls6.mac_ptr, &sample->eth_dst, ETH_ADDR_LEN); memcpy(pptrsv->vlanmpls6.vlan_ptr, &vlan, 2); for (idx = 0; idx <= sample->lstk.depth && idx < 10; idx++) { label = sample->lstk.stack[idx]; memcpy(ptr, &label, 4); ptr += 4; } stick_bosbit(ptr-4); pptrsv->vlanmpls6.iph_ptr = ptr; pptrsv->vlanmpls6.tlh_ptr = ptr + IP6HdrSz; reset_ip6(&pptrsv->vlanmpls6); ((struct ip6_hdr *)pptrsv->vlanmpls6.iph_ptr)->ip6_ctlun.ip6_un2_vfc = 0x60; memcpy(&((struct ip6_hdr *)pptrsv->vlanmpls6.iph_ptr)->ip6_src, &sample->ipsrc.address.ip_v6, IP6AddrSz); memcpy(&((struct ip6_hdr *)pptrsv->vlanmpls6.iph_ptr)->ip6_dst, &sample->ipdst.address.ip_v6, IP6AddrSz); memcpy(&((struct ip6_hdr *)pptrsv->vlanmpls6.iph_ptr)->ip6_nxt, &dcd_ipProtocol, 1); /* XXX: class ID ? */ memcpy(&((struct my_tlhdr *)pptrsv->vlanmpls6.tlh_ptr)->src_port, &dcd_sport, 2); memcpy(&((struct my_tlhdr *)pptrsv->vlanmpls6.tlh_ptr)->dst_port, &dcd_dport, 2); memcpy(&((struct my_tcphdr *)pptrsv->vlanmpls6.tlh_ptr)->th_flags, &dcd_tcpFlags, 1); } pptrsv->vlanmpls6.lm_mask_src = sample->srcMask; pptrsv->vlanmpls6.lm_mask_dst = sample->dstMask; pptrsv->vlanmpls6.lm_method_src = NF_NET_KEEP; pptrsv->vlanmpls6.lm_method_dst = NF_NET_KEEP; pptrsv->vlanmpls6.l4_proto = sample->dcd_ipProtocol; if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->vlanmpls6); if (config.nfacctd_bgp_to_agent_map) SF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) SF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->vlanmpls6); if (config.nfacctd_bgp_peer_as_src_map) SF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) SF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.blp, NULL); if (config.nfacctd_bgp_src_med_map) SF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.bmed, NULL); if (config.pre_tag_map) SF_find_id((struct id_table *)pptrs->idtable, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.tag, &pptrsv->vlanmpls6.tag2); exec_plugins(&pptrsv->vlanmpls6); break; #endif default: break; } } } void SF_find_id(struct id_table *t, struct packet_ptrs *pptrs, pm_id_t *tag, pm_id_t *tag2) { SFSample *sample = (SFSample *)pptrs->f_data; int x, j, stop; pm_id_t id; if (!t) return; /* The id_table is shared between by IPv4 and IPv6 sFlow collectors. IPv4 ones are in the lower part (0..x), IPv6 ones are in the upper part (x+1..end) */ id = 0; if (tag) *tag = 0; if (tag2) *tag2 = 0; if (sample->agent_addr.type == SFLADDRESSTYPE_IP_V4) { for (x = 0; x < t->ipv4_num; x++) { if (t->e[x].agent_ip.a.address.ipv4.s_addr == sample->agent_addr.address.ip_v4.s_addr) { t->e[x].last_matched = FALSE; for (j = 0, stop = 0; !stop; j++) stop = (*t->e[x].func[j])(pptrs, &id, &t->e[x]); if (id) { if (stop == PRETAG_MAP_RCODE_ID) { if (t->e[x].stack.func) id = (*t->e[x].stack.func)(id, *tag); *tag = id; } else if (stop == PRETAG_MAP_RCODE_ID2) { if (t->e[x].stack.func) id = (*t->e[x].stack.func)(id, *tag2); *tag2 = id; } if (t->e[x].jeq.ptr) { if (t->e[x].ret) { exec_plugins(pptrs); set_shadow_status(pptrs); *tag = 0; *tag2 = 0; } x = t->e[x].jeq.ptr->pos; x--; /* yes, it will be automagically incremented by the for() cycle */ id = 0; } else break; } } } } #if defined ENABLE_IPV6 else if (sample->agent_addr.type == SFLADDRESSTYPE_IP_V6) { for (x = (t->num-t->ipv6_num); x < t->num; x++) { if (!ip6_addr_cmp(&t->e[x].agent_ip.a.address.ipv6, &sample->agent_addr.address.ip_v6)) { for (j = 0, stop = 0; !stop; j++) stop = (*t->e[x].func[j])(pptrs, &id, &t->e[x]); if (id) { if (stop == PRETAG_MAP_RCODE_ID) { if (t->e[x].stack.func) id = (*t->e[x].stack.func)(id, *tag); *tag = id; } else if (stop == PRETAG_MAP_RCODE_ID2) { if (t->e[x].stack.func) id = (*t->e[x].stack.func)(id, *tag2); *tag2 = id; } if (t->e[x].jeq.ptr) { if (t->e[x].ret) { exec_plugins(pptrs); set_shadow_status(pptrs); *tag = 0; *tag2 = 0; } x = t->e[x].jeq.ptr->pos; x--; /* yes, it will be automagically incremented by the for() cycle */ id = 0; } else break; } } } } #endif } u_int16_t SF_evaluate_flow_type(struct packet_ptrs *pptrs) { SFSample *sample = (SFSample *)pptrs->f_data; u_int8_t ret = 0; if (sample->in_vlan || sample->out_vlan) ret += NF9_FTYPE_VLAN; if (sample->lstk.depth > 0) ret += NF9_FTYPE_MPLS; if (sample->gotIPV4); else if (sample->gotIPV6) ret += NF9_FTYPE_IPV6; return ret; } void set_vector_sample_type(struct packet_ptrs_vector *pptrsv, u_int32_t sample_type) { pptrsv->v4.sample_type = sample_type; pptrsv->vlan4.sample_type = sample_type; pptrsv->mpls4.sample_type = sample_type; pptrsv->vlanmpls4.sample_type = sample_type; #if defined ENABLE_IPV6 pptrsv->v6.sample_type = sample_type; pptrsv->vlan6.sample_type = sample_type; pptrsv->mpls6.sample_type = sample_type; pptrsv->vlanmpls6.sample_type = sample_type; #endif } void reset_mac(struct packet_ptrs *pptrs) { memset(pptrs->mac_ptr, 0, 2*ETH_ADDR_LEN); } void reset_mac_vlan(struct packet_ptrs *pptrs) { memset(pptrs->mac_ptr, 0, 2*ETH_ADDR_LEN); memset(pptrs->vlan_ptr, 0, 2); } void reset_ip4(struct packet_ptrs *pptrs) { memset(pptrs->iph_ptr, 0, IP4TlSz); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_vhl, 5); } #if defined ENABLE_IPV6 void reset_ip6(struct packet_ptrs *pptrs) { memset(pptrs->iph_ptr, 0, IP6TlSz); Assign16(((struct ip6_hdr *)pptrs->iph_ptr)->ip6_plen, htons(100)); Assign16(((struct ip6_hdr *)pptrs->iph_ptr)->ip6_hlim, htons(64)); } #endif /* dummy functions; their use is limited to solve a trivial dependency */ int ip_handler(register struct packet_ptrs *pptrs) { } int ip6_handler(register struct packet_ptrs *pptrs) { } char *sfv245_check_status(SFSample *spp, struct sockaddr *sa) { struct sockaddr salocal; u_int32_t aux1 = spp->agentSubId; struct xflow_status_entry *entry = NULL; int hash; memcpy(&salocal, sa, sizeof(struct sockaddr)); /* Let's copy the IPv4 sFlow agent address; family is defined just in case the remote peer IP address was reported as IPv4-mapped IPv6 address */ salocal.sa_family = AF_INET; ( (struct sockaddr_in *)&salocal )->sin_addr = spp->agent_addr.address.ip_v4; hash = hash_status_table(aux1, &salocal, XFLOW_STATUS_TABLE_SZ); if (hash >= 0) { entry = search_status_table(&salocal, aux1, hash, XFLOW_STATUS_TABLE_MAX_ENTRIES); if (entry) { update_status_table(entry, spp->sequenceNo); entry->inc = 1; } } return (char *) entry; } /* Dummy objects here - ugly to see but well portable */ void NF_find_id(struct id_table *t, struct packet_ptrs *pptrs, pm_id_t *tag, pm_id_t *tag2) { } pmacct-0.14.0/src/pgsql_plugin.c0000644000175000017500000007741111652204562015525 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #define __PGSQL_PLUGIN_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "sql_common.h" #include "pgsql_plugin.h" #include "sql_common_m.c" /* Functions */ void pgsql_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_data *data; struct ports_table pt; struct pollfd pfd; struct insert_data idata; struct timezone tz; time_t refresh_deadline; int timeout; int ret, num; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; struct pkt_bgp_primitives *pbgp; char *dataptr; unsigned char *rgptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; memcpy(&config, cfgptr, sizeof(struct configuration)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "PostgreSQL Plugin", config.name); memset(&idata, 0, sizeof(idata)); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile); } sql_set_signals(); sql_init_default_values(); PG_init_default_values(&idata); PG_set_callbacks(&sqlfunc_cbr); sql_set_insert_func(); /* some LOCAL initialization AFTER setting some default values */ reload_map = FALSE; idata.now = time(NULL); refresh_deadline = idata.now; sql_init_maps(&nt, &nc, &pt); sql_init_global_buffers(); sql_init_pipe(&pfd, pipe_fd); sql_init_historical_acct(idata.now, &idata); sql_init_triggers(idata.now, &idata); sql_init_refresh_deadline(&refresh_deadline); /* building up static SQL clauses */ idata.num_primitives = PG_compose_static_queries(); glob_num_primitives = idata.num_primitives; /* handling logfile template stuff */ te = sql_init_logfile_template(&th); INIT_BUF(logbuf); /* handling purge preprocessor */ set_preprocess_funcs(config.sql_preprocess, &prep); /* setting up environment variables */ SQL_SetENV(); sql_link_backend_descriptors(&bed, &p, &b); /* plugin main loop */ for(;;) { poll_again: status->wakeup = TRUE; sql_calc_refresh_timeout(refresh_deadline, idata.now, &timeout); ret = poll(&pfd, 1, timeout); if (ret < 0) goto poll_again; idata.now = time(NULL); now = idata.now; if (config.sql_history) { while (idata.now > (idata.basetime + idata.timeslot)) { time_t saved_basetime = idata.basetime; idata.basetime += idata.timeslot; if (config.sql_history == COUNT_MONTHLY) idata.timeslot = calc_monthly_timeslot(idata.basetime, config.sql_history_howmany, ADD); glob_basetime = idata.basetime; idata.new_basetime = saved_basetime; glob_new_basetime = saved_basetime; } } switch (ret) { case 0: /* poll(): timeout */ if (qq_ptr) sql_cache_flush(queries_queue, qq_ptr, &idata, FALSE); switch (fork()) { case 0: /* Child */ /* we have to ignore signals to avoid loops: because we are already forked */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); pm_setproctitle("%s [%s]", "PostgreSQL Plugin -- DB Writer", config.name); if (qq_ptr && sql_writers.flags != CHLD_ALERT) { if (sql_writers.flags == CHLD_WARNING) sql_db_fail(&p); (*sqlfunc_cbr.connect)(&p, NULL); (*sqlfunc_cbr.purge)(queries_queue, qq_ptr, &idata); (*sqlfunc_cbr.close)(&bed); } if (config.sql_trigger_exec) { if (idata.now > idata.triggertime) sql_trigger_exec(config.sql_trigger_exec); } exit(0); default: /* Parent */ if (pqq_ptr) sql_cache_flush_pending(pending_queries_queue, pqq_ptr, &idata); gettimeofday(&idata.flushtime, &tz); while (idata.now > refresh_deadline) refresh_deadline += config.sql_refresh_time; while (idata.now > idata.triggertime && idata.t_timeslot > 0) { idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } idata.new_basetime = FALSE; glob_new_basetime = FALSE; qq_ptr = pqq_ptr; memcpy(queries_queue, pending_queries_queue, qq_ptr*sizeof(struct db_cache *)); if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } break; } break; default: /* poll(): received data */ read_data: if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; idata.now = time(NULL); now = idata.now; } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; goto poll_again; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%u'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%u'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); } seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); if ((rg->ptr+bufsz) >= rg->end) rg->ptr = rg->base; else rg->ptr += bufsz; /* lazy sql refresh handling */ if (idata.now > refresh_deadline) { if (qq_ptr) sql_cache_flush(queries_queue, qq_ptr, &idata, FALSE); switch (fork()) { case 0: /* Child */ /* we have to ignore signals to avoid loops: because we are already forked */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); pm_setproctitle("%s [%s]", "PostgreSQL Plugin -- DB Writer", config.name); if (qq_ptr && sql_writers.flags != CHLD_ALERT) { if (sql_writers.flags == CHLD_WARNING) sql_db_fail(&p); (*sqlfunc_cbr.connect)(&p, NULL); (*sqlfunc_cbr.purge)(queries_queue, qq_ptr, &idata); (*sqlfunc_cbr.close)(&bed); } if (config.sql_trigger_exec) { if (idata.now > idata.triggertime) sql_trigger_exec(config.sql_trigger_exec); } exit(0); default: /* Parent */ if (pqq_ptr) sql_cache_flush_pending(pending_queries_queue, pqq_ptr, &idata); gettimeofday(&idata.flushtime, &tz); while (idata.now > refresh_deadline) refresh_deadline += config.sql_refresh_time; while (idata.now > idata.triggertime && idata.t_timeslot > 0) { idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } idata.new_basetime = FALSE; glob_new_basetime = FALSE; qq_ptr = pqq_ptr; memcpy(queries_queue, pending_queries_queue, qq_ptr*sizeof(struct db_cache *)); if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } break; } } else { if (config.sql_trigger_exec) { while (idata.now > idata.triggertime && idata.t_timeslot > 0) { sql_trigger_exec(config.sql_trigger_exec); idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } } } data = (struct pkt_data *) (pipebuf+sizeof(struct ch_buf_hdr)); while (((struct ch_buf_hdr *)pipebuf)->num) { for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &data->primitives); if (config.ports_file) { if (!pt.table[data->primitives.src_port]) data->primitives.src_port = 0; if (!pt.table[data->primitives.dst_port]) data->primitives.dst_port = 0; } if (PbgpSz) pbgp = (struct pkt_bgp_primitives *) ((u_char *)data+PdataSz); else pbgp = NULL; (*insert_func)(data, pbgp, &idata); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) data; dataptr += PdataSz + PbgpSz; data = (struct pkt_data *) dataptr; } } goto read_data; } } } int PG_cache_dbop_copy(struct DBdesc *db, struct db_cache *cache_elem, struct insert_data *idata) { PGresult *ret; char *ptr_values, *ptr_where; char default_delim[] = ",", delim_buf[SRVBUFLEN]; int num=0, have_flows=0; if (config.what_to_count & COUNT_FLOWS) have_flows = TRUE; if (!config.sql_delimiter) snprintf(delim_buf, SRVBUFLEN, "%s", default_delim); else snprintf(delim_buf, SRVBUFLEN, "%s", config.sql_delimiter); /* constructing SQL query */ ptr_where = where_clause; ptr_values = values_clause; memset(where_clause, 0, sizeof(where_clause)); memset(values_clause, 0, sizeof(values_clause)); memcpy(&values, ©_values, sizeof(values)); while (num < idata->num_primitives) { (*where[num].handler)(cache_elem, idata, num, &ptr_values, &ptr_where); num++; } #if defined HAVE_64BIT_COUNTERS if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), "%s%llu%s%llu%s%llu\n", delim_buf, cache_elem->packet_counter, delim_buf, cache_elem->bytes_counter, delim_buf, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), "%s%llu%s%llu\n", delim_buf, cache_elem->packet_counter, delim_buf, cache_elem->bytes_counter); #else if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), "%s%lu%s%lu%s%lu\n", delim_buf, cache_elem->packet_counter, delim_buf, cache_elem->bytes_counter, delim_buf, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), "%s%lu%s%lu\n", delim_buf, cache_elem->packet_counter, delim_buf, cache_elem->bytes_counter); #endif strncpy(sql_data, values_clause, sizeof(sql_data)); if (PQputCopyData(db->desc, sql_data, strlen(sql_data)) < 0) { // avoid strlen() db->errmsg = PQerrorMessage(db->desc); Log(LOG_DEBUG, "DEBUG ( %s/%s ): FAILED query follows:\n%s\n", config.name, config.type, sql_data); if (db->errmsg) Log(LOG_ERR, "ERROR ( %s/%s ): %s\n", config.name, config.type, db->errmsg); sql_db_fail(db); return TRUE; } idata->iqn++; idata->een++; Log(LOG_DEBUG, "DEBUG ( %s/%s ): %s\n", config.name, config.type, sql_data); return FALSE; } int PG_cache_dbop(struct DBdesc *db, struct db_cache *cache_elem, struct insert_data *idata) { PGresult *ret; char *ptr_values, *ptr_where, *ptr_set; int num, have_flows=0; if (config.what_to_count & COUNT_FLOWS) have_flows = TRUE; /* constructing SQL query */ ptr_where = where_clause; ptr_values = values_clause; ptr_set = set_clause; memset(where_clause, 0, sizeof(where_clause)); memset(values_clause, 0, sizeof(values_clause)); memset(set_clause, 0, sizeof(set_clause)); for (num = 0; num < idata->num_primitives; num++) (*where[num].handler)(cache_elem, idata, num, &ptr_values, &ptr_where); for (num = 0; set[num].type; num++) (*set[num].handler)(cache_elem, idata, num, &ptr_set, NULL); /* sending UPDATE query */ if (!config.sql_dont_try_update) { strncpy(sql_data, update_clause, SPACELEFT(sql_data)); strncat(sql_data, set_clause, SPACELEFT(sql_data)); strncat(sql_data, where_clause, SPACELEFT(sql_data)); ret = PQexec(db->desc, sql_data); if (PQresultStatus(ret) != PGRES_COMMAND_OK) { db->errmsg = PQresultErrorMessage(ret); PQclear(ret); Log(LOG_DEBUG, "DEBUG ( %s/%s ): FAILED query follows:\n%s\n", config.name, config.type, sql_data); if (db->errmsg) Log(LOG_ERR, "ERROR ( %s/%s ): %s\n\n", config.name, config.type, db->errmsg); sql_db_fail(db); return TRUE; } PQclear(ret); } if (config.sql_dont_try_update || (!PG_affected_rows(ret))) { /* UPDATE failed, trying with an INSERT query */ strncpy(sql_data, insert_clause, sizeof(sql_data)); #if defined HAVE_64BIT_COUNTERS if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), ", %llu, %llu, %llu)", cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), ", %llu, %llu)", cache_elem->packet_counter, cache_elem->bytes_counter); #else if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), ", %lu, %lu, %lu)", cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), ", %lu, %lu)", cache_elem->packet_counter, cache_elem->bytes_counter); #endif strncat(sql_data, values_clause, SPACELEFT(sql_data)); ret = PQexec(db->desc, sql_data); if (PQresultStatus(ret) != PGRES_COMMAND_OK) { db->errmsg = PQresultErrorMessage(ret); PQclear(ret); Log(LOG_DEBUG, "DEBUG ( %s/%s ): FAILED query follows:\n%s\n", config.name, config.type, sql_data); if (db->errmsg) Log(LOG_ERR, "ERROR ( %s/%s ): %s\n\n", config.name, config.type, db->errmsg); sql_db_fail(db); return TRUE; } PQclear(ret); idata->iqn++; } else idata->uqn++; idata->een++; Log(LOG_DEBUG, "DEBUG ( %s/%s ): %s\n\n", config.name, config.type, sql_data); return FALSE; } void PG_cache_purge(struct db_cache *queue[], int index, struct insert_data *idata) { PGresult *ret; struct logfile lf; time_t start; int j, r, reprocess = 0, stop; memset(&lf, 0, sizeof(struct logfile)); bed.lf = &lf; for (j = 0, stop = 0; (!stop) && preprocess_funcs[j]; j++) stop = preprocess_funcs[j](queue, &index, j); if (config.what_to_count & COUNT_CLASS) sql_invalidate_shadow_entries(queue, &index); idata->ten = index; if (config.debug) { Log(LOG_DEBUG, "( %s/%s ) *** Purging cache - START ***\n", config.name, config.type); start = time(NULL); } /* We check for variable substitution in SQL table */ if (idata->dyn_table) { char tmpbuf[LONGLONGSRVBUFLEN]; time_t stamp = idata->new_basetime ? idata->new_basetime : idata->basetime; strftime_same(copy_clause, LONGSRVBUFLEN, tmpbuf, &stamp); strftime_same(insert_clause, LONGSRVBUFLEN, tmpbuf, &stamp); strftime_same(update_clause, LONGSRVBUFLEN, tmpbuf, &stamp); strftime_same(lock_clause, LONGSRVBUFLEN, tmpbuf, &stamp); if (config.sql_table_schema) sql_create_table(bed.p, &stamp); } // strncat(update_clause, set_clause, SPACELEFT(update_clause)); /* beginning DB transaction */ (*sqlfunc_cbr.lock)(bed.p); /* for each element of the queue to be processed we execute sql_query(); the function returns a non-zero value if DB has failed; then, first failed element is saved to allow reprocessing of previous elements if a failover method is in use; elements need to be reprocessed because at the time of DB failure they were not yet committed */ for (j = 0; j < index; j++) { if (queue[j]->valid) r = sql_query(&bed, queue[j], idata); else r = FALSE; /* not valid elements are marked as not to be reprocessed */ if (r && !reprocess) { idata->uqn = 0; idata->iqn = 0; reprocess = j+1; /* avoding reprocess to be 0 when element j = 0 fails */ } } /* Finalizing DB transaction */ if (!p.fail) { if (config.sql_use_copy) { if (PQputCopyEnd(p.desc, NULL) < 0) Log(LOG_ERR, "ERROR ( %s/%s ): COPY failed!\n\n", config.name, config.type); } ret = PQexec(p.desc, "COMMIT"); if (PQresultStatus(ret) != PGRES_COMMAND_OK) { if (!reprocess) { sql_db_fail(&p); idata->uqn = 0; idata->iqn = 0; reprocess = j+1; } } PQclear(ret); } if (p.fail) { reprocess--; if (reprocess) { for (j = 0; j <= reprocess; j++) { /* don't reprocess free (SQL_CACHE_FREE) and already recovered (SQL_CACHE_ERROR) elements */ if (queue[j]->valid == SQL_CACHE_COMMITTED) sql_query(&bed, queue[j], idata); } } } if (b.connected) { if (config.sql_use_copy) { if (PQputCopyEnd(b.desc, NULL) < 0) Log(LOG_ERR, "ERROR ( %s/%s ): COPY failed!\n\n", config.name, config.type); } ret = PQexec(b.desc, "COMMIT"); if (PQresultStatus(ret) != PGRES_COMMAND_OK) sql_db_fail(&b); PQclear(ret); } /* rewinding stuff */ if (lf.file) PG_file_close(&lf); if (lf.fail || b.fail) Log(LOG_ALERT, "ALERT ( %s/%s ): recovery for PgSQL operation failed.\n", config.name, config.type); if (config.debug) { idata->elap_time = time(NULL)-start; Log(LOG_DEBUG, "( %s/%s ) *** Purging cache - END (QN: %u, ET: %u) ***\n", config.name, config.type, idata->qn, idata->elap_time); } if (config.sql_trigger_exec) { if (!config.debug) idata->elap_time = time(NULL)-start; SQL_SetENV_child(idata); } } int PG_evaluate_history(int primitive) { if (config.sql_history || config.nfacctd_sql_log) { if (primitive) { strncat(copy_clause, ", ", SPACELEFT(copy_clause)); strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (!config.sql_history_since_epoch) strncat(where[primitive].string, "ABSTIME(%u)::Timestamp::Timestamp without time zone = ", SPACELEFT(where[primitive].string)); else strncat(where[primitive].string, "%u = ", SPACELEFT(where[primitive].string)); strncat(where[primitive].string, "stamp_inserted", SPACELEFT(where[primitive].string)); strncat(copy_clause, "stamp_updated, stamp_inserted", SPACELEFT(copy_clause)); strncat(insert_clause, "stamp_updated, stamp_inserted", SPACELEFT(insert_clause)); if (config.sql_use_copy) { char default_delim[] = ",", delim_buf[SRVBUFLEN]; if (!config.sql_delimiter || !config.sql_use_copy) snprintf(delim_buf, SRVBUFLEN, "%s ", default_delim); else snprintf(delim_buf, SRVBUFLEN, "%s ", config.sql_delimiter); if (!config.sql_history_since_epoch) { strncat(values[primitive].string, "%s", SPACELEFT(values[primitive].string)); strncat(values[primitive].string, delim_buf, SPACELEFT(values[primitive].string)); strncat(values[primitive].string, "%s", SPACELEFT(values[primitive].string)); values[primitive].handler = where[primitive].handler = count_copy_timestamp_handler; } else { strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(values[primitive].string, delim_buf, SPACELEFT(values[primitive].string)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); values[primitive].handler = where[primitive].handler = count_timestamp_handler; } } else { if (!config.sql_history_since_epoch) strncat(values[primitive].string, "ABSTIME(%u)::Timestamp, ABSTIME(%u)::Timestamp", SPACELEFT(values[primitive].string)); else strncat(values[primitive].string, "%u, %u", SPACELEFT(values[primitive].string)); values[primitive].handler = where[primitive].handler = count_timestamp_handler; } where[primitive].type = values[primitive].type = TIMESTAMP; primitive++; } return primitive; } int PG_compose_static_queries() { int primitives=0, set_primitives=0, have_flows=0, lock=0; char default_delim[] = ",", delim_buf[SRVBUFLEN]; if (config.what_to_count & COUNT_FLOWS || (config.sql_table_version >= 4 && config.sql_table_version < SQL_TABLE_VERSION_BGP && !config.sql_optimize_clauses)) { config.what_to_count |= COUNT_FLOWS; have_flows = TRUE; if ((config.sql_table_version < 4 || config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !config.sql_optimize_clauses) { Log(LOG_ERR, "ERROR ( %s/%s ): The accounting of flows requires SQL table v4. Exiting.\n", config.name, config.type); exit_plugin(1); } } /* "INSERT INTO ... VALUES ... ", "COPY ... ", "... WHERE ..." stuff */ strncpy(where[primitives].string, " WHERE ", sizeof(where[primitives].string)); snprintf(copy_clause, sizeof(copy_clause), "COPY %s (", config.sql_table); snprintf(insert_clause, sizeof(insert_clause), "INSERT INTO %s (", config.sql_table); strncpy(values[primitives].string, " VALUES (", sizeof(values[primitives].string)); primitives = PG_evaluate_history(primitives); primitives = sql_evaluate_primitives(primitives); strncat(copy_clause, ", packets, bytes", SPACELEFT(copy_clause)); if (have_flows) strncat(copy_clause, ", flows", SPACELEFT(copy_clause)); if (!config.sql_delimiter || !config.sql_use_copy) snprintf(delim_buf, SRVBUFLEN, ") FROM STDIN DELIMITER \'%s\'", default_delim); else snprintf(delim_buf, SRVBUFLEN, ") FROM STDIN DELIMITER \'%s\'", config.sql_delimiter); strncat(copy_clause, delim_buf, SPACELEFT(copy_clause)); strncat(insert_clause, ", packets, bytes", SPACELEFT(insert_clause)); if (have_flows) strncat(insert_clause, ", flows", SPACELEFT(insert_clause)); strncat(insert_clause, ")", SPACELEFT(insert_clause)); /* "LOCK ..." stuff */ if (config.sql_dont_try_update) snprintf(lock_clause, sizeof(lock_clause), "BEGIN;"); else { if (config.sql_locking_style) lock = sql_select_locking_style(config.sql_locking_style); switch (lock) { case PM_LOCK_ROW_EXCLUSIVE: snprintf(lock_clause, sizeof(lock_clause), "BEGIN; LOCK %s IN ROW EXCLUSIVE MODE;", config.sql_table); break; case PM_LOCK_EXCLUSIVE: default: snprintf(lock_clause, sizeof(lock_clause), "BEGIN; LOCK %s IN EXCLUSIVE MODE;", config.sql_table); break; } } /* "UPDATE ... SET ..." stuff */ snprintf(update_clause, sizeof(update_clause), "UPDATE %s ", config.sql_table); set_primitives = sql_compose_static_set(have_flows); if (config.sql_history || config.nfacctd_sql_log) { if (!config.nfacctd_sql_log) { if (!config.sql_history_since_epoch) { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=CURRENT_TIMESTAMP(0)", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_noop_setclause_handler; set_primitives++; } else { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=DATE_PART('epoch',NOW())::BIGINT", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_noop_setclause_handler; set_primitives++; } } else { if (!config.sql_history_since_epoch) { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=ABSTIME(%u)::Timestamp", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_timestamp_setclause_handler; set_primitives++; } else { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=%u", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_timestamp_setclause_handler; set_primitives++; } } } /* values for COPY */ memcpy(©_values, &values, sizeof(copy_values)); { int num, x, y; char *ptr; ptr = strchr(copy_values[0].string, '('); ptr++; strcpy(copy_values[0].string, ptr); for (num = 0; num < primitives; num++) { for (x = 0; copy_values[num].string[x] != '\0'; x++) { if (copy_values[num].string[x] == ' ' || copy_values[num].string[x] == '\'') { for (y = x + 1; copy_values[num].string[y] != '\0'; y++) copy_values[num].string[y-1] = copy_values[num].string[y]; copy_values[num].string[y-1] = '\0'; x--; } } copy_values[num].string[x] = '\0'; } } return primitives; } void PG_compose_conn_string(struct DBdesc *db, char *host) { char *string; int slen = SRVBUFLEN; if (!db->conn_string) { db->conn_string = (char *) malloc(slen); string = db->conn_string; snprintf(string, slen, "dbname=%s user=%s password=%s", config.sql_db, config.sql_user, config.sql_passwd); slen -= strlen(string); string += strlen(string); if (host) snprintf(string, slen, " host=%s", host); } } void PG_Lock(struct DBdesc *db) { PGresult *PGret; if (!db->fail) { PGret = PQexec(db->desc, lock_clause); if (PQresultStatus(PGret) != PGRES_COMMAND_OK) { db->errmsg = PQresultErrorMessage(PGret); sql_db_errmsg(db); sql_db_fail(db); } PQclear(PGret); /* If using COPY, let's initialize it */ if (config.sql_use_copy) { PGret = PQexec(db->desc, copy_clause); if (PQresultStatus(PGret) != PGRES_COPY_IN) { db->errmsg = PQresultErrorMessage(PGret); sql_db_errmsg(db); sql_db_fail(db); } PQclear(PGret); } } } void PG_file_close(struct logfile *lf) { if (logbuf.ptr != logbuf.base) { fwrite(logbuf.base, (logbuf.ptr-logbuf.base), 1, lf->file); logbuf.ptr = logbuf.base; } file_unlock(fileno(lf->file)); fclose(lf->file); } void PG_DB_Connect(struct DBdesc *db, char *host) { if (!db->fail) { db->desc = PQconnectdb(db->conn_string); if (PQstatus(db->desc) == CONNECTION_BAD) { char errmsg[64+SRVBUFLEN]; sql_db_fail(db); strcpy(errmsg, "Failed connecting to "); strcat(errmsg, db->conn_string); db->errmsg = errmsg; sql_db_errmsg(db); } else sql_db_ok(db); } } void PG_DB_Close(struct BE_descs *bed) { if (bed->p->connected) PQfinish(bed->p->desc); if (bed->b->connected) PQfinish(bed->b->desc); } void PG_create_dyn_table(struct DBdesc *db, char *buf) { char *err_string; PGresult *PGret; if (!db->fail) { PGret = PQexec(db->desc, buf); if ((PQresultStatus(PGret) != PGRES_COMMAND_OK) && (PQresultStatus(PGret) != PGRES_TUPLES_OK)) { err_string = PQresultErrorMessage(PGret); Log(LOG_DEBUG, "DEBUG ( %s/%s ): FAILED query follows:\n%s\n", config.name, config.type, buf); Log(LOG_ERR, "ERROR ( %s/%s ): %s\n\n", config.name, config.type, err_string); } PQclear(PGret); } } static int PG_affected_rows(PGresult *result) { return atoi(PQcmdTuples(result)); } void PG_create_backend(struct DBdesc *db) { if (db->type == BE_TYPE_BACKUP) { if (!config.sql_backup_host) return; } PG_compose_conn_string(db, config.sql_host); } void PG_set_callbacks(struct sqlfunc_cb_registry *cbr) { memset(cbr, 0, sizeof(struct sqlfunc_cb_registry)); cbr->connect = PG_DB_Connect; cbr->close = PG_DB_Close; cbr->lock = PG_Lock; /* cbr->unlock */ if (!config.sql_use_copy) cbr->op = PG_cache_dbop; else cbr->op = PG_cache_dbop_copy; cbr->create_table = PG_create_dyn_table; cbr->purge = PG_cache_purge; cbr->create_backend = PG_create_backend; } void PG_init_default_values(struct insert_data *idata) { /* Linking database parameters */ if (!config.sql_data) config.sql_data = typed_str; if (!config.sql_user) config.sql_user = pgsql_user; if (!config.sql_db) config.sql_db = pgsql_db; if (!config.sql_passwd) config.sql_passwd = pgsql_pwd; if (!config.sql_table) { /* checking 'typed' table constraints */ if (!strcmp(config.sql_data, "typed")) { if (config.what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS|COUNT_DST_AS) && config.what_to_count & (COUNT_SRC_HOST|COUNT_SUM_HOST|COUNT_DST_HOST|COUNT_SRC_NET|COUNT_SUM_NET|COUNT_DST_NET) && config.sql_table_version < 6) { Log(LOG_ERR, "ERROR ( %s/%s ): 'typed' PostgreSQL table in use: unable to mix HOST/NET and AS aggregations.\n", config.name, config.type); exit_plugin(1); } typed = TRUE; } else if (!strcmp(config.sql_data, "unified")) typed = FALSE; else { Log(LOG_ERR, "ERROR ( %s/%s ): Ignoring unknown 'sql_data' value '%s'.\n", config.name, config.type, config.sql_data); exit_plugin(1); } if (typed) { if (config.sql_table_version == (SQL_TABLE_VERSION_BGP+1)) config.sql_table = pgsql_table_bgp; else if (config.sql_table_version == 8) config.sql_table = pgsql_table_v8; else if (config.sql_table_version == 7) config.sql_table = pgsql_table_v7; else if (config.sql_table_version == 6) config.sql_table = pgsql_table_v6; else if (config.sql_table_version == 5) { if (config.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) config.sql_table = pgsql_table_as_v5; else config.sql_table = pgsql_table_v5; } else if (config.sql_table_version == 4) { if (config.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) config.sql_table = pgsql_table_as_v4; else config.sql_table = pgsql_table_v4; } else if (config.sql_table_version == 3) { if (config.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) config.sql_table = pgsql_table_as_v3; else config.sql_table = pgsql_table_v3; } else if (config.sql_table_version == 2) { if (config.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) config.sql_table = pgsql_table_as_v2; else config.sql_table = pgsql_table_v2; } else { if (config.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) config.sql_table = pgsql_table_as; else config.sql_table = pgsql_table; } } else { if (config.sql_table_version == 8) { Log(LOG_WARNING, "WARN ( %s/%s ): Unified data are no longer supported. Switching to typed data.\n", config.name, config.type); config.sql_table = pgsql_table_v8; } if (config.sql_table_version == 7) { Log(LOG_WARNING, "WARN ( %s/%s ): Unified data are no longer supported. Switching to typed data.\n", config.name, config.type); config.sql_table = pgsql_table_v7; } else if (config.sql_table_version == 6) { Log(LOG_WARNING, "WARN ( %s/%s ): Unified data are no longer supported. Switching to typed data.\n", config.name, config.type); config.sql_table = pgsql_table_v6; } else if (config.sql_table_version == 5) config.sql_table = pgsql_table_uni_v5; else if (config.sql_table_version == 4) config.sql_table = pgsql_table_uni_v4; else if (config.sql_table_version == 3) config.sql_table = pgsql_table_uni_v3; else if (config.sql_table_version == 2) config.sql_table = pgsql_table_uni_v2; else config.sql_table = pgsql_table_uni; } } if (strchr(config.sql_table, '%')) idata->dyn_table = TRUE; glob_dyn_table = idata->dyn_table; if (config.sql_backup_host || config.sql_recovery_logfile) idata->recover = TRUE; if (!config.sql_dont_try_update && config.sql_use_copy) config.sql_use_copy = FALSE; if (config.sql_locking_style) idata->locks = sql_select_locking_style(config.sql_locking_style); } pmacct-0.14.0/src/isis/0000755000175000017500000000000011741267746013626 5ustar paolopaolopmacct-0.14.0/src/isis/sockunion.h0000640000175000017500000000711711734647141016002 0ustar paolopaolo/* * Socket union header. * Copyright (c) 1997 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef _SOCKUNION_H_ #define _SOCKUNION_H_ #if 0 union sockunion { struct sockinet { u_char si_len; u_char si_family; u_short si_port; } su_si; struct sockaddr_in su_sin; struct sockaddr_in6 su_sin6; }; #define su_len su_si.si_len #define su_family su_si.si_family #define su_port su_si.si_port #endif /* 0 */ union sockunion { struct sockaddr sa; struct sockaddr_in sin; #ifdef ENABLE_IPV6 struct sockaddr_in6 sin6; #endif /* ENABLE_IPV6 */ }; enum connect_result { connect_error, connect_success, connect_in_progress }; /* Default address family. */ #ifdef ENABLE_IPV6 #define AF_INET_UNION AF_INET6 #else #define AF_INET_UNION AF_INET #endif /* Sockunion address string length. Same as INET6_ADDRSTRLEN. */ #define SU_ADDRSTRLEN 46 /* Macro to set link local index to the IPv6 address. For KAME IPv6 stack. */ #ifdef KAME #define IN6_LINKLOCAL_IFINDEX(a) ((a).s6_addr[2] << 8 | (a).s6_addr[3]) #define SET_IN6_LINKLOCAL_IFINDEX(a, i) \ do { \ (a).s6_addr[2] = ((i) >> 8) & 0xff; \ (a).s6_addr[3] = (i) & 0xff; \ } while (0) #else #define IN6_LINKLOCAL_IFINDEX(a) #define SET_IN6_LINKLOCAL_IFINDEX(a, i) #endif /* KAME */ /* shortcut macro to specify address field of struct sockaddr */ #define sock2ip(X) (((struct sockaddr_in *)(X))->sin_addr.s_addr) #ifdef ENABLE_IPV6 #define sock2ip6(X) (((struct sockaddr_in6 *)(X))->sin6_addr.s6_addr) #endif /* ENABLE_IPV6 */ #define sockunion_family(X) (X)->sa.sa_family /* Prototypes. */ #if (!defined __SOCKUNION_C) #define EXT extern #else #define EXT #endif EXT int str2sockunion (const char *, union sockunion *); EXT const char *sockunion2str (union sockunion *, char *, size_t); EXT int sockunion_cmp (union sockunion *, union sockunion *); EXT int sockunion_same (union sockunion *, union sockunion *); EXT char *sockunion_su2str (union sockunion *); EXT union sockunion *sockunion_str2su (const char *); EXT struct in_addr sockunion_get_in_addr (union sockunion *); EXT int sockunion_accept (int sock, union sockunion *); EXT int sockunion_stream_socket (union sockunion *); EXT int sockopt_reuseaddr (int); EXT int sockopt_reuseport (int); EXT int sockunion_bind (int sock, union sockunion *, unsigned short, union sockunion *); EXT int sockopt_ttl (int, int, int); EXT int sockopt_minttl (int, int, int); EXT int sockopt_cork (int, int); EXT int sockunion_socket (union sockunion *); EXT const char *inet_sutop (union sockunion *, char *); EXT enum connect_result sockunion_connect (int, union sockunion *, unsigned short, unsigned int); EXT union sockunion *sockunion_getsockname (int); EXT union sockunion *sockunion_getpeername (int); EXT union sockunion *sockunion_dup (union sockunion *); EXT void sockunion_free (union sockunion *); #undef EXT #endif /* _SOCKUNION_H_ */ pmacct-0.14.0/src/isis/isis_events.c0000640000175000017500000001522511734671403016315 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_events.c * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_EVENTS_C #include "pmacct.h" #include "linklist.h" #include "dict.h" #include "thread.h" #include "prefix.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_circuit.h" #include "isis_tlv.h" #include "isis_lsp.h" #include "isis_pdu.h" #include "isis_network.h" #include "isis_misc.h" #include "isis_constants.h" #include "isis_adjacency.h" #include "isis_flags.h" #include "isisd.h" #include "isis_csm.h" #include "isis_events.h" #include "isis_spf.h" extern struct thread_master *master; extern struct isis *isis; /* debug isis-spf spf-events 4w4d: ISIS-Spf (tlt): L2 SPF needed, new adjacency, from 0x609229F4 4w4d: ISIS-Spf (tlt): L2, 0000.0000.0042.01-00 TLV contents changed, code 0x2 4w4d: ISIS-Spf (tlt): L2, new LSP 0 DEAD.BEEF.0043.00-00 4w5d: ISIS-Spf (tlt): L1 SPF needed, periodic SPF, from 0x6091C844 4w5d: ISIS-Spf (tlt): L2 SPF needed, periodic SPF, from 0x6091C844 */ void isis_event_circuit_state_change (struct isis_circuit *circuit, int up) { struct isis_area *area; area = circuit->area; assert (area); area->circuit_state_changes++; Log(LOG_DEBUG, "DEBUG (default/core/ISIS ): ISIS-Evt (%s) circuit %s\n", circuit->area->area_tag, up ? "up" : "down"); /* * Regenerate LSPs this affects */ lsp_regenerate_schedule (area); return; } void isis_event_system_type_change (struct isis_area *area, int newtype) { struct listnode *node; struct isis_circuit *circuit; Log(LOG_DEBUG, "DEBUG (default/core/ISIS ): ISIS-Evt (%s) system type change %s -> %s\n", area->area_tag, circuit_t2string (area->is_type), circuit_t2string (newtype)); if (area->is_type == newtype) return; /* No change */ switch (area->is_type) { case IS_LEVEL_1: if (area->lspdb[1] == NULL) area->lspdb[1] = lsp_db_init (); lsp_l2_generate (area); break; case IS_LEVEL_1_AND_2: if (newtype == IS_LEVEL_1) { lsp_db_destroy (area->lspdb[1]); } else { lsp_db_destroy (area->lspdb[0]); } break; case IS_LEVEL_2: if (area->lspdb[0] == NULL) area->lspdb[0] = lsp_db_init (); lsp_l1_generate (area); break; default: break; } area->is_type = newtype; for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit)) isis_event_circuit_type_change (circuit, newtype); spftree_area_init (area); lsp_regenerate_schedule (area); return; } void isis_event_area_addr_change (struct isis_area *area) { } // XXX: send hello instead? static void circuit_commence_level (struct isis_circuit *circuit, int level) { if (level == 1) { // THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit, // isis_jitter (circuit->psnp_interval[0], PSNP_JITTER)); } else { // THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit, // isis_jitter (circuit->psnp_interval[1], PSNP_JITTER)); } return; } static void circuit_resign_level (struct isis_circuit *circuit, int level) { int idx = level - 1; // THREAD_TIMER_OFF (circuit->t_send_csnp[idx]); // THREAD_TIMER_OFF (circuit->t_send_psnp[idx]); return; } void isis_event_circuit_type_change (struct isis_circuit *circuit, int newtype) { Log(LOG_DEBUG, "DEBUG (default/core/ISIS ): ISIS-Evt (%s) circuit type change %s -> %s\n", circuit->area->area_tag, circuit_t2string (circuit->circuit_is_type), circuit_t2string (newtype)); if (circuit->circuit_is_type == newtype) return; /* No change */ if (!(newtype & circuit->area->is_type)) { Log(LOG_ERR, "ERROR (default/core/ISIS ): ISIS-Evt (%s) circuit type change - invalid level %s because area is %s\n", circuit->area->area_tag, circuit_t2string (newtype), circuit_t2string (circuit->area->is_type)); return; } switch (circuit->circuit_is_type) { case IS_LEVEL_1: if (newtype == IS_LEVEL_2) circuit_resign_level (circuit, 1); circuit_commence_level (circuit, 2); break; case IS_LEVEL_1_AND_2: if (newtype == IS_LEVEL_1) circuit_resign_level (circuit, 2); else circuit_resign_level (circuit, 1); break; case IS_LEVEL_2: if (newtype == IS_LEVEL_1) circuit_resign_level (circuit, 2); circuit_commence_level (circuit, 1); break; default: break; } circuit->circuit_is_type = newtype; lsp_regenerate_schedule (circuit->area); return; } /* 04/18/2002 by Gwak. */ /************************************************************************** * * EVENTS for LSP generation * * 1) an Adajacency or Circuit Up/Down event * 2) a chnage in Circuit metric * 3) a change in Reachable Address metric * 4) a change in manualAreaAddresses * 5) a change in systemID * 6) a change in DIS status * 7) a chnage in the waiting status * * *********************************************************************** * * current support event * * 1) Adjacency Up/Down event * 6) a change in DIS status * * ***********************************************************************/ void isis_event_adjacency_state_change (struct isis_adjacency *adj, int newstate) { /* adjacency state change event. * - the only proto-type was supported */ /* invalid arguments */ if (!adj || !adj->circuit || !adj->circuit->area) return; Log(LOG_DEBUG, "DEBUG (default/core/ISIS ): ISIS-Evt (%s) Adjacency State change\n", adj->circuit->area->area_tag); /* LSP generation again */ lsp_regenerate_schedule (adj->circuit->area); return; } /* events supporting code */ void isis_event_auth_failure (char *area_tag, const char *error_string, u_char *sysid) { Log(LOG_DEBUG, "DEBUG (default/core/ISIS ): ISIS-Evt (%s) Authentication failure %s from %s\n", area_tag, error_string, sysid_print (sysid)); return; } pmacct-0.14.0/src/isis/hash.c0000640000175000017500000001136711727477130014713 0ustar paolopaolo/* Hash routine. * Copyright (C) 1998 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #define __HASH_C #include "pmacct.h" #include "isis.h" #include "hash.h" /* Allocate a new hash. */ struct hash * hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), int (*hash_cmp) (const void *, const void *)) { struct hash *hash; hash = calloc(1, sizeof (struct hash)); hash->index = calloc(1, sizeof (struct hash_backet *) * size); hash->size = size; hash->hash_key = hash_key; hash->hash_cmp = hash_cmp; hash->count = 0; return hash; } /* Allocate a new hash with default hash size. */ struct hash * hash_create (unsigned int (*hash_key) (void *), int (*hash_cmp) (const void *, const void *)) { return hash_create_size (HASHTABSIZE, hash_key, hash_cmp); } /* Utility function for hash_get(). When this function is specified as alloc_func, return arugment as it is. This function is used for intern already allocated value. */ void * hash_alloc_intern (void *arg) { return arg; } /* Lookup and return hash backet in hash. If there is no corresponding hash backet and alloc_func is specified, create new hash backet. */ void * hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *)) { unsigned int key; unsigned int index; void *newdata; struct hash_backet *backet; key = (*hash->hash_key) (data); index = key % hash->size; for (backet = hash->index[index]; backet != NULL; backet = backet->next) if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) return backet->data; if (alloc_func) { newdata = (*alloc_func) (data); if (newdata == NULL) return NULL; backet = calloc(1, sizeof (struct hash_backet)); backet->data = newdata; backet->key = key; backet->next = hash->index[index]; hash->index[index] = backet; hash->count++; return backet->data; } return NULL; } /* Hash lookup. */ void * hash_lookup (struct hash *hash, void *data) { return hash_get (hash, data, NULL); } /* Simple Bernstein hash which is simple and fast for common case */ unsigned int string_hash_make (const char *str) { unsigned int hash = 0; while (*str) hash = (hash * 33) ^ (unsigned int) *str++; return hash; } /* This function release registered value from specified hash. When release is successfully finished, return the data pointer in the hash backet. */ void * hash_release (struct hash *hash, void *data) { void *ret; unsigned int key; unsigned int index; struct hash_backet *backet; struct hash_backet *pp; key = (*hash->hash_key) (data); index = key % hash->size; for (backet = pp = hash->index[index]; backet; backet = backet->next) { if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) { if (backet == pp) hash->index[index] = backet->next; else pp->next = backet->next; ret = backet->data; free(backet); hash->count--; return ret; } pp = backet; } return NULL; } /* Iterator function for hash. */ void hash_iterate (struct hash *hash, void (*func) (struct hash_backet *, void *), void *arg) { unsigned int i; struct hash_backet *hb; struct hash_backet *hbnext; for (i = 0; i < hash->size; i++) for (hb = hash->index[i]; hb; hb = hbnext) { /* get pointer to next hash backet here, in case (*func) * decides to delete hb by calling hash_release */ hbnext = hb->next; (*func) (hb, arg); } } /* Clean up hash. */ void hash_clean (struct hash *hash, void (*free_func) (void *)) { unsigned int i; struct hash_backet *hb; struct hash_backet *next; for (i = 0; i < hash->size; i++) { for (hb = hash->index[i]; hb; hb = next) { next = hb->next; if (free_func) (*free_func) (hb->data); free(hb); hash->count--; } hash->index[i] = NULL; } } /* Free hash memory. You may call hash_clean before call this function. */ void hash_free (struct hash *hash) { free(hash->index); free(hash); } pmacct-0.14.0/src/isis/isis_circuit.h0000640000175000017500000001351711734647141016464 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_circuit.h * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_CIRCUIT_H_ #define _ISIS_CIRCUIT_H_ #define CIRCUIT_MAX 255 struct password { struct password *next; int len; u_char *pass; }; struct metric { u_char metric_default; u_char metric_error; u_char metric_expense; u_char metric_delay; }; struct isis_bcast_info { u_char snpa[ETH_ALEN]; /* SNPA of this circuit */ char run_dr_elect[2]; /* Should we run dr election ? */ struct thread *t_run_dr[2]; /* DR election thread */ struct thread *t_send_lan_hello[2]; /* send LAN IIHs in this thread */ struct list *adjdb[2]; /* adjacency dbs */ struct list *lan_neighs[2]; /* list of lx neigh snpa */ char is_dr[2]; /* Are we level x DR ? */ u_char l1_desig_is[ISIS_SYS_ID_LEN + 1]; /* level-1 DR */ u_char l2_desig_is[ISIS_SYS_ID_LEN + 1]; /* level-2 DR */ struct thread *t_refresh_pseudo_lsp[2]; /* refresh pseudo-node LSPs */ int pad_hellos; /* add padding to Hello PDUs ? */ u_char priority[2]; /* l1/2 IS Priority */ }; struct isis_p2p_info { struct isis_adjacency *neighbor; struct thread *t_send_p2p_hello; /* send P2P IIHs in this thread */ }; struct interface { char name[SRVBUFLEN]; unsigned int ifindex; unsigned int mtu; }; struct isis_circuit { int state; u_char circuit_id; /* l1/l2 p2p/bcast CircuitID */ struct isis_area *area; /* back pointer to the area */ struct interface *interface; /* interface info from z */ int fd; /* IS-IS l1/2 socket */ int sap_length; /* SAP length for DLPI */ struct nlpids nlpids; /* * Threads */ struct thread *t_read; struct thread *t_send_csnp[2]; struct thread *t_send_psnp[2]; struct list *lsp_queue; /* LSPs to be txed (both levels) */ /* there is no real point in two streams, just for programming kicker */ int (*rx) (struct isis_circuit * circuit, u_char * ssnpa); struct stream *rcv_stream; /* Stream for receiving */ int (*tx) (struct isis_circuit * circuit, int level); struct stream *snd_stream; /* Stream for sending */ int idx; /* idx in S[RM|SN] flags */ #define CIRCUIT_T_BROADCAST 0 #define CIRCUIT_T_P2P 1 #define CIRCUIT_T_STATIC_IN 2 #define CIRCUIT_T_STATIC_OUT 3 #define CIRCUIT_T_DA 4 int circ_type; /* type of the physical interface */ union { struct isis_bcast_info bc; struct isis_p2p_info p2p; } u; char ext_domain; /* externalDomain (boolean) */ /* * Configurables */ struct isis_passwd passwd; /* Circuit rx/tx password */ long lsp_interval; int manual_l2_only; /* manualL2OnlyMode (boolean) */ int circuit_is_type; /* circuit is type == level of circuit * diffrenciated from circuit type (media) */ u_int32_t hello_interval[2]; /* l1HelloInterval in msecs */ u_int16_t hello_multiplier[2]; /* l1HelloMultiplier */ u_int16_t csnp_interval[2]; /* level-1 csnp-interval in seconds */ u_int16_t psnp_interval[2]; /* level-1 psnp-interval in seconds */ struct metric metrics[2]; /* l1XxxMetric */ u_int32_t te_metric[2]; struct password *c_rx_passwds; /* circuitReceivePasswords */ struct password *c_tc_passwd; /* circuitTransmitPassword */ int ip_router; /* Route IP ? */ struct list *ip_addrs; /* our IP addresses */ #ifdef ENABLE_IPV6 int ipv6_router; /* Route IPv6 ? */ struct list *ipv6_link; /* our link local IPv6 addresses */ struct list *ipv6_non_link; /* our non-link local IPv6 addresses */ #endif /* ENABLE_IPV6 */ /* * RFC 2973 IS-IS Mesh Groups */ #define MESH_INACTIVE 0 #define MESH_BLOCKED 1 #define MESH_SET 2 int mesh_enabled; /* meshGroupEnabled */ u_int16_t mesh_group; /* meshGroup */ u_int16_t upadjcount[2]; /* * Counters as in 10589--11.2.5.9 */ u_int32_t adj_state_changes; /* changesInAdjacencyState */ u_int32_t init_failures; /* intialisationFailures */ u_int32_t ctrl_pdus_rxed; /* controlPDUsReceived */ u_int32_t ctrl_pdus_txed; /* controlPDUsSent */ u_int32_t desig_changes[2]; /* lanLxDesignatedIntermediateSystemChanges */ u_int32_t rej_adjacencies; /* rejectedAdjacencies */ }; struct sockaddr_ll { unsigned short int sll_family; unsigned short int sll_protocol; int sll_ifindex; unsigned short int sll_hatype; unsigned char sll_pkttype; unsigned char sll_halen; unsigned char sll_addr[8]; }; #if (!defined __ISIS_CIRCUIT_C) #define EXT extern #else #define EXT #endif EXT struct isis_circuit *isis_circuit_new (); EXT void isis_circuit_del (struct isis_circuit *); EXT void isis_circuit_configure (struct isis_circuit *, struct isis_area *); EXT void isis_circuit_up (struct isis_circuit *); EXT void isis_circuit_deconfigure (struct isis_circuit *, struct isis_area *); EXT int isis_circuit_destroy (struct isis_circuit *); EXT void isis_circuit_if_add (struct isis_circuit *, struct interface *); EXT void isis_circuit_if_del (struct isis_circuit *); EXT void circuit_update_nlpids (struct isis_circuit *); EXT void isis_circuit_update_params (struct isis_circuit *, struct interface *); #undef EXT #endif /* _ISIS_CIRCUIT_H_ */ pmacct-0.14.0/src/isis/hash.h0000640000175000017500000000401711705253204014700 0ustar paolopaolo/* Hash routine. Copyright (C) 1998 Kunihiro Ishiguro This file is part of GNU Zebra. GNU Zebra 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. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _HASH_H_ #define _HASH_H_ /* Default hash table size. */ #define HASHTABSIZE 1024 struct hash_backet { /* Linked list. */ struct hash_backet *next; /* Hash key. */ unsigned int key; /* Data. */ void *data; }; struct hash { /* Hash backet. */ struct hash_backet **index; /* Hash table size. */ unsigned int size; /* Key make function. */ unsigned int (*hash_key) (void *); /* Data compare function. */ int (*hash_cmp) (const void *, const void *); /* Backet alloc. */ unsigned long count; }; #if (!defined __HASH_C) #define EXT extern #else #define EXT #endif EXT struct hash *hash_create (unsigned int (*) (void *), int (*) (const void *, const void *)); EXT struct hash *hash_create_size (unsigned int, unsigned int (*) (void *), int (*) (const void *, const void *)); EXT void *hash_get (struct hash *, void *, void * (*) (void *)); EXT void *hash_alloc_intern (void *); EXT void *hash_lookup (struct hash *, void *); EXT void *hash_release (struct hash *, void *); EXT void hash_iterate (struct hash *, void (*) (struct hash_backet *, void *), void *); EXT void hash_clean (struct hash *, void (*) (void *)); EXT void hash_free (struct hash *); EXT unsigned int string_hash_make (const char *); #undef EXT #endif /* _HASH_H_ */ pmacct-0.14.0/src/isis/isis_ll.h0000640000175000017500000000215211705253204015411 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #ifndef _ISIS_LL_H_ #define _ISIS_LL_H_ /* defines */ /* structures */ /* prototypes */ #if (!defined __ISIS_C) #define EXT extern #else #define EXT #endif EXT void isis_sll_handler(const struct pcap_pkthdr *, register struct packet_ptrs *); /* global variables */ #undef EXT #endif pmacct-0.14.0/src/isis/dict.c0000640000175000017500000007116111727477130014711 0ustar paolopaolo/* * Dictionary Abstract Data Type * Copyright (C) 1997 Kaz Kylheku * * Free Software License: * * All rights are reserved by the author, with the following exceptions: * Permission is granted to freely reproduce and distribute this software, * possibly in exchange for a fee, provided that this copyright notice appears * intact. Permission is also granted to adapt this software to produce * derivative works, as long as the modified versions carry this copyright * notice and additional notices stating that the work has been modified. * This source code may be translated into executable form and incorporated * into proprietary software; there is no requirement for such software to * contain a copyright notice related to this source. */ #define __DICT_C #include "pmacct.h" #include "isis.h" #define DICT_IMPLEMENTATION #include "dict.h" /* * These macros provide short convenient names for structure members, * which are embellished with dict_ prefixes so that they are * properly confined to the documented namespace. It's legal for a * program which uses dict to define, for instance, a macro called ``parent''. * Such a macro would interfere with the dnode_t struct definition. * In general, highly portable and reusable C modules which expose their * structures need to confine structure member names to well-defined spaces. * The resulting identifiers aren't necessarily convenient to use, nor * readable, in the implementation, however! */ #define left dict_left #define right dict_right #define parent dict_parent #define color dict_color #define key dict_key #define data dict_data #define nilnode dict_nilnode #define nodecount dict_nodecount #define maxcount dict_maxcount #define compare dict_compare #define allocnode dict_allocnode #define freenode dict_freenode #define context dict_context #define dupes dict_dupes #define dictptr dict_dictptr #define dict_root(D) ((D)->nilnode.left) #define dict_nil(D) (&(D)->nilnode) #define DICT_DEPTH_MAX 64 static dnode_t *dnode_alloc(void *context); static void dnode_free(dnode_t *node, void *context); /* * Perform a ``left rotation'' adjustment on the tree. The given node P and * its right child C are rearranged so that the P instead becomes the left * child of C. The left subtree of C is inherited as the new right subtree * for P. The ordering of the keys within the tree is thus preserved. */ static void rotate_left(dnode_t *upper) { dnode_t *lower, *lowleft, *upparent; lower = upper->right; upper->right = lowleft = lower->left; lowleft->parent = upper; lower->parent = upparent = upper->parent; /* don't need to check for root node here because root->parent is the sentinel nil node, and root->parent->left points back to root */ if (upper == upparent->left) { upparent->left = lower; } else { assert (upper == upparent->right); upparent->right = lower; } lower->left = upper; upper->parent = lower; } /* * This operation is the ``mirror'' image of rotate_left. It is * the same procedure, but with left and right interchanged. */ static void rotate_right(dnode_t *upper) { dnode_t *lower, *lowright, *upparent; lower = upper->left; upper->left = lowright = lower->right; lowright->parent = upper; lower->parent = upparent = upper->parent; if (upper == upparent->right) { upparent->right = lower; } else { assert (upper == upparent->left); upparent->left = lower; } lower->right = upper; upper->parent = lower; } /* * Do a postorder traversal of the tree rooted at the specified * node and free everything under it. Used by dict_free(). */ static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil) { if (node == nil) return; free_nodes(dict, node->left, nil); free_nodes(dict, node->right, nil); dict->freenode(node, dict->context); } /* * This procedure performs a verification that the given subtree is a binary * search tree. It performs an inorder traversal of the tree using the * dict_next() successor function, verifying that the key of each node is * strictly lower than that of its successor, if duplicates are not allowed, * or lower or equal if duplicates are allowed. This function is used for * debugging purposes. */ static int verify_bintree(dict_t *dict) { dnode_t *first, *next; first = dict_first(dict); if (dict->dupes) { while (first && (next = dict_next(dict, first))) { if (dict->compare(first->key, next->key) > 0) return 0; first = next; } } else { while (first && (next = dict_next(dict, first))) { if (dict->compare(first->key, next->key) >= 0) return 0; first = next; } } return 1; } /* * This function recursively verifies that the given binary subtree satisfies * three of the red black properties. It checks that every red node has only * black children. It makes sure that each node is either red or black. And it * checks that every path has the same count of black nodes from root to leaf. * It returns the blackheight of the given subtree; this allows blackheights to * be computed recursively and compared for left and right siblings for * mismatches. It does not check for every nil node being black, because there * is only one sentinel nil node. The return value of this function is the * black height of the subtree rooted at the node ``root'', or zero if the * subtree is not red-black. */ static unsigned int verify_redblack(dnode_t *nil, dnode_t *root) { unsigned height_left, height_right; if (root != nil) { height_left = verify_redblack(nil, root->left); height_right = verify_redblack(nil, root->right); if (height_left == 0 || height_right == 0) return 0; if (height_left != height_right) return 0; if (root->color == dnode_red) { if (root->left->color != dnode_black) return 0; if (root->right->color != dnode_black) return 0; return height_left; } if (root->color != dnode_black) return 0; return height_left + 1; } return 1; } /* * Compute the actual count of nodes by traversing the tree and * return it. This could be compared against the stored count to * detect a mismatch. */ static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root) { if (root == nil) return 0; else return 1 + verify_node_count(nil, root->left) + verify_node_count(nil, root->right); } /* * Verify that the tree contains the given node. This is done by * traversing all of the nodes and comparing their pointers to the * given pointer. Returns 1 if the node is found, otherwise * returns zero. It is intended for debugging purposes. */ static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node) { if (root != nil) { return root == node || verify_dict_has_node(nil, root->left, node) || verify_dict_has_node(nil, root->right, node); } return 0; } /* * Dynamically allocate and initialize a dictionary object. */ dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp) { dict_t *new = calloc(1, sizeof *new); if (new) { new->compare = comp; new->allocnode = dnode_alloc; new->freenode = dnode_free; new->context = NULL; new->nodecount = 0; new->maxcount = maxcount; new->nilnode.left = &new->nilnode; new->nilnode.right = &new->nilnode; new->nilnode.parent = &new->nilnode; new->nilnode.color = dnode_black; new->dupes = 0; } return new; } /* * Select a different set of node allocator routines. */ void dict_set_allocator(dict_t *dict, dnode_alloc_t al, dnode_free_t fr, void *context) { assert (dict_count(dict) == 0); assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL)); dict->allocnode = al ? al : dnode_alloc; dict->freenode = fr ? fr : dnode_free; dict->context = context; } /* * Free a dynamically allocated dictionary object. Removing the nodes * from the tree before deleting it is required. */ void dict_destroy(dict_t *dict) { assert (dict_isempty(dict)); free(dict); } /* * Free all the nodes in the dictionary by using the dictionary's * installed free routine. The dictionary is emptied. */ void dict_free_nodes(dict_t *dict) { dnode_t *nil = dict_nil(dict), *root = dict_root(dict); free_nodes(dict, root, nil); dict->nodecount = 0; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; } /* * Obsolescent function, equivalent to dict_free_nodes */ void dict_free(dict_t *dict) { dict_free_nodes(dict); } /* * Initialize a user-supplied dictionary object. */ dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp) { dict->compare = comp; dict->allocnode = dnode_alloc; dict->freenode = dnode_free; dict->context = NULL; dict->nodecount = 0; dict->maxcount = maxcount; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; dict->nilnode.parent = &dict->nilnode; dict->nilnode.color = dnode_black; dict->dupes = 0; return dict; } /* * Initialize a dictionary in the likeness of another dictionary */ void dict_init_like(dict_t *dict, const dict_t *template) { dict->compare = template->compare; dict->allocnode = template->allocnode; dict->freenode = template->freenode; dict->context = template->context; dict->nodecount = 0; dict->maxcount = template->maxcount; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; dict->nilnode.parent = &dict->nilnode; dict->nilnode.color = dnode_black; dict->dupes = template->dupes; assert (dict_similar(dict, template)); } /* * Remove all nodes from the dictionary (without freeing them in any way). */ static void dict_clear(dict_t *dict) { dict->nodecount = 0; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; dict->nilnode.parent = &dict->nilnode; assert (dict->nilnode.color == dnode_black); } /* * Verify the integrity of the dictionary structure. This is provided for * debugging purposes, and should be placed in assert statements. Just because * this function succeeds doesn't mean that the tree is not corrupt. Certain * corruptions in the tree may simply cause undefined behavior. */ int dict_verify(dict_t *dict) { dnode_t *nil = dict_nil(dict), *root = dict_root(dict); /* check that the sentinel node and root node are black */ if (root->color != dnode_black) return 0; if (nil->color != dnode_black) return 0; if (nil->right != nil) return 0; /* nil->left is the root node; check that its parent pointer is nil */ if (nil->left->parent != nil) return 0; /* perform a weak test that the tree is a binary search tree */ if (!verify_bintree(dict)) return 0; /* verify that the tree is a red-black tree */ if (!verify_redblack(nil, root)) return 0; if (verify_node_count(nil, root) != dict_count(dict)) return 0; return 1; } /* * Determine whether two dictionaries are similar: have the same comparison and * allocator functions, and same status as to whether duplicates are allowed. */ int dict_similar(const dict_t *left, const dict_t *right) { if (left->compare != right->compare) return 0; if (left->allocnode != right->allocnode) return 0; if (left->freenode != right->freenode) return 0; if (left->context != right->context) return 0; if (left->dupes != right->dupes) return 0; return 1; } /* * Locate a node in the dictionary having the given key. * If the node is not found, a null a pointer is returned (rather than * a pointer that dictionary's nil sentinel node), otherwise a pointer to the * located node is returned. */ dnode_t *dict_lookup(dict_t *dict, const void *key) { dnode_t *root = dict_root(dict); dnode_t *nil = dict_nil(dict); dnode_t *saved; int result; /* simple binary search adapted for trees that contain duplicate keys */ while (root != nil) { result = dict->compare(key, root->key); if (result < 0) root = root->left; else if (result > 0) root = root->right; else { if (!dict->dupes) { /* no duplicates, return match */ return root; } else { /* could be dupes, find leftmost one */ do { saved = root; root = root->left; while (root != nil && dict->compare(key, root->key)) root = root->right; } while (root != nil); return saved; } } } return NULL; } /* * Look for the node corresponding to the lowest key that is equal to or * greater than the given key. If there is no such node, return null. */ dnode_t *dict_lower_bound(dict_t *dict, const void *key) { dnode_t *root = dict_root(dict); dnode_t *nil = dict_nil(dict); dnode_t *tentative = 0; while (root != nil) { int result = dict->compare(key, root->key); if (result > 0) { root = root->right; } else if (result < 0) { tentative = root; root = root->left; } else { if (!dict->dupes) { return root; } else { tentative = root; root = root->left; } } } return tentative; } /* * Look for the node corresponding to the greatest key that is equal to or * lower than the given key. If there is no such node, return null. */ dnode_t *dict_upper_bound(dict_t *dict, const void *key) { dnode_t *root = dict_root(dict); dnode_t *nil = dict_nil(dict); dnode_t *tentative = 0; while (root != nil) { int result = dict->compare(key, root->key); if (result < 0) { root = root->left; } else if (result > 0) { tentative = root; root = root->right; } else { if (!dict->dupes) { return root; } else { tentative = root; root = root->right; } } } return tentative; } /* * Insert a node into the dictionary. The node should have been * initialized with a data field. All other fields are ignored. * The behavior is undefined if the user attempts to insert into * a dictionary that is already full (for which the dict_isfull() * function returns true). */ void dict_insert(dict_t *dict, dnode_t *node, const void *key) { dnode_t *where = dict_root(dict), *nil = dict_nil(dict); dnode_t *parent = nil, *uncle, *grandpa; int result = -1; node->key = key; assert (!dict_isfull(dict)); assert (!dict_contains(dict, node)); assert (!dnode_is_in_a_dict(node)); /* basic binary tree insert */ while (where != nil) { parent = where; result = dict->compare(key, where->key); /* trap attempts at duplicate key insertion unless it's explicitly allowed */ assert (dict->dupes || result != 0); if (result < 0) where = where->left; else where = where->right; } assert (where == nil); if (result < 0) parent->left = node; else parent->right = node; node->parent = parent; node->left = nil; node->right = nil; dict->nodecount++; /* red black adjustments */ node->color = dnode_red; while (parent->color == dnode_red) { grandpa = parent->parent; if (parent == grandpa->left) { uncle = grandpa->right; if (uncle->color == dnode_red) { /* red parent, red uncle */ parent->color = dnode_black; uncle->color = dnode_black; grandpa->color = dnode_red; node = grandpa; parent = grandpa->parent; } else { /* red parent, black uncle */ if (node == parent->right) { rotate_left(parent); parent = node; assert (grandpa == parent->parent); /* rotation between parent and child preserves grandpa */ } parent->color = dnode_black; grandpa->color = dnode_red; rotate_right(grandpa); break; } } else { /* symmetric cases: parent == parent->parent->right */ uncle = grandpa->left; if (uncle->color == dnode_red) { parent->color = dnode_black; uncle->color = dnode_black; grandpa->color = dnode_red; node = grandpa; parent = grandpa->parent; } else { if (node == parent->left) { rotate_right(parent); parent = node; assert (grandpa == parent->parent); } parent->color = dnode_black; grandpa->color = dnode_red; rotate_left(grandpa); break; } } } dict_root(dict)->color = dnode_black; assert (dict_verify(dict)); } /* * Delete the given node from the dictionary. If the given node does not belong * to the given dictionary, undefined behavior results. A pointer to the * deleted node is returned. */ dnode_t *dict_delete(dict_t *dict, dnode_t *delete) { dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent; /* basic deletion */ assert (!dict_isempty(dict)); assert (dict_contains(dict, delete)); /* * If the node being deleted has two children, then we replace it with its * successor (i.e. the leftmost node in the right subtree.) By doing this, * we avoid the traditional algorithm under which the successor's key and * value *only* move to the deleted node and the successor is spliced out * from the tree. We cannot use this approach because the user may hold * pointers to the successor, or nodes may be inextricably tied to some * other structures by way of embedding, etc. So we must splice out the * node we are given, not some other node, and must not move contents from * one node to another behind the user's back. */ if (delete->left != nil && delete->right != nil) { dnode_t *next = dict_next(dict, delete); dnode_t *nextparent = next->parent; dnode_color_t nextcolor = next->color; assert (next != nil); assert (next->parent != nil); assert (next->left == nil); /* * First, splice out the successor from the tree completely, by * moving up its right child into its place. */ child = next->right; child->parent = nextparent; if (nextparent->left == next) { nextparent->left = child; } else { assert (nextparent->right == next); nextparent->right = child; } /* * Now that the successor has been extricated from the tree, install it * in place of the node that we want deleted. */ next->parent = delparent; next->left = delete->left; next->right = delete->right; next->left->parent = next; next->right->parent = next; next->color = delete->color; delete->color = nextcolor; if (delparent->left == delete) { delparent->left = next; } else { assert (delparent->right == delete); delparent->right = next; } } else { assert (delete != nil); assert (delete->left == nil || delete->right == nil); child = (delete->left != nil) ? delete->left : delete->right; child->parent = delparent = delete->parent; if (delete == delparent->left) { delparent->left = child; } else { assert (delete == delparent->right); delparent->right = child; } } delete->parent = NULL; delete->right = NULL; delete->left = NULL; dict->nodecount--; assert (verify_bintree(dict)); /* red-black adjustments */ if (delete->color == dnode_black) { dnode_t *parent, *sister; dict_root(dict)->color = dnode_red; while (child->color == dnode_black) { parent = child->parent; if (child == parent->left) { sister = parent->right; assert (sister != nil); if (sister->color == dnode_red) { sister->color = dnode_black; parent->color = dnode_red; rotate_left(parent); sister = parent->right; assert (sister != nil); } if (sister->left->color == dnode_black && sister->right->color == dnode_black) { sister->color = dnode_red; child = parent; } else { if (sister->right->color == dnode_black) { assert (sister->left->color == dnode_red); sister->left->color = dnode_black; sister->color = dnode_red; rotate_right(sister); sister = parent->right; assert (sister != nil); } sister->color = parent->color; sister->right->color = dnode_black; parent->color = dnode_black; rotate_left(parent); break; } } else { /* symmetric case: child == child->parent->right */ assert (child == parent->right); sister = parent->left; assert (sister != nil); if (sister->color == dnode_red) { sister->color = dnode_black; parent->color = dnode_red; rotate_right(parent); sister = parent->left; assert (sister != nil); } if (sister->right->color == dnode_black && sister->left->color == dnode_black) { sister->color = dnode_red; child = parent; } else { if (sister->left->color == dnode_black) { assert (sister->right->color == dnode_red); sister->right->color = dnode_black; sister->color = dnode_red; rotate_left(sister); sister = parent->left; assert (sister != nil); } sister->color = parent->color; sister->left->color = dnode_black; parent->color = dnode_black; rotate_right(parent); break; } } } child->color = dnode_black; dict_root(dict)->color = dnode_black; } assert (dict_verify(dict)); return delete; } /* * Allocate a node using the dictionary's allocator routine, give it * the data item. */ int dict_alloc_insert(dict_t *dict, const void *key, void *data) { dnode_t *node = dict->allocnode(dict->context); if (node) { dnode_init(node, data); dict_insert(dict, node, key); return 1; } return 0; } void dict_delete_free(dict_t *dict, dnode_t *node) { dict_delete(dict, node); dict->freenode(node, dict->context); } /* * Return the node with the lowest (leftmost) key. If the dictionary is empty * (that is, dict_isempty(dict) returns 1) a null pointer is returned. */ dnode_t *dict_first(dict_t *dict) { dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left; if (root != nil) while ((left = root->left) != nil) root = left; return (root == nil) ? NULL : root; } /* * Return the node with the highest (rightmost) key. If the dictionary is empty * (that is, dict_isempty(dict) returns 1) a null pointer is returned. */ dnode_t *dict_last(dict_t *dict) { dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right; if (root != nil) while ((right = root->right) != nil) root = right; return (root == nil) ? NULL : root; } /* * Return the given node's successor node---the node which has the * next key in the the left to right ordering. If the node has * no successor, a null pointer is returned rather than a pointer to * the nil node. */ dnode_t *dict_next(dict_t *dict, dnode_t *curr) { dnode_t *nil = dict_nil(dict), *parent, *left; if (curr->right != nil) { curr = curr->right; while ((left = curr->left) != nil) curr = left; return curr; } parent = curr->parent; while (parent != nil && curr == parent->right) { curr = parent; parent = curr->parent; } return (parent == nil) ? NULL : parent; } /* * Return the given node's predecessor, in the key order. * The nil sentinel node is returned if there is no predecessor. */ dnode_t *dict_prev(dict_t *dict, dnode_t *curr) { dnode_t *nil = dict_nil(dict), *parent, *right; if (curr->left != nil) { curr = curr->left; while ((right = curr->right) != nil) curr = right; return curr; } parent = curr->parent; while (parent != nil && curr == parent->left) { curr = parent; parent = curr->parent; } return (parent == nil) ? NULL : parent; } void dict_allow_dupes(dict_t *dict) { dict->dupes = 1; } #undef dict_count #undef dict_isempty #undef dict_isfull #undef dnode_get #undef dnode_put #undef dnode_getkey dictcount_t dict_count(dict_t *dict) { return dict->nodecount; } int dict_isempty(dict_t *dict) { return dict->nodecount == 0; } int dict_isfull(dict_t *dict) { return dict->nodecount == dict->maxcount; } int dict_contains(dict_t *dict, dnode_t *node) { return verify_dict_has_node(dict_nil(dict), dict_root(dict), node); } static dnode_t *dnode_alloc(void *context) { return calloc(1, sizeof *dnode_alloc(NULL)); } static void dnode_free(dnode_t *node, void *context) { free(node); } dnode_t *dnode_create(void *data) { dnode_t *new = calloc(1, sizeof *new); if (new) { new->data = data; new->parent = NULL; new->left = NULL; new->right = NULL; } return new; } dnode_t *dnode_init(dnode_t *dnode, void *data) { dnode->data = data; dnode->parent = NULL; dnode->left = NULL; dnode->right = NULL; return dnode; } void dnode_destroy(dnode_t *dnode) { assert (!dnode_is_in_a_dict(dnode)); free(dnode); } void *dnode_get(dnode_t *dnode) { return dnode->data; } const void *dnode_getkey(dnode_t *dnode) { return dnode->key; } void dnode_put(dnode_t *dnode, void *data) { dnode->data = data; } int dnode_is_in_a_dict(dnode_t *dnode) { return (dnode->parent && dnode->left && dnode->right); } void dict_process(dict_t *dict, void *context, dnode_process_t function) { dnode_t *node = dict_first(dict), *next; while (node != NULL) { /* check for callback function deleting */ /* the next node from under us */ assert (dict_contains(dict, node)); next = dict_next(dict, node); function(dict, node, context); node = next; } } static void load_begin_internal(dict_load_t *load, dict_t *dict) { load->dictptr = dict; load->nilnode.left = &load->nilnode; load->nilnode.right = &load->nilnode; } void dict_load_begin(dict_load_t *load, dict_t *dict) { assert (dict_isempty(dict)); load_begin_internal(load, dict); } void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key) { dict_t *dict = load->dictptr; dnode_t *nil = &load->nilnode; assert (!dnode_is_in_a_dict(newnode)); assert (dict->nodecount < dict->maxcount); #ifndef NDEBUG if (dict->nodecount > 0) { if (dict->dupes) assert (dict->compare(nil->left->key, key) <= 0); else assert (dict->compare(nil->left->key, key) < 0); } #endif newnode->key = key; nil->right->left = newnode; nil->right = newnode; newnode->left = nil; dict->nodecount++; } void dict_load_end(dict_load_t *load) { dict_t *dict = load->dictptr; dnode_t *tree[DICT_DEPTH_MAX] = { 0 }; dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next; dnode_t *complete = 0; dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount; dictcount_t botrowcount; unsigned baselevel = 0, level = 0, i; assert (dnode_red == 0 && dnode_black == 1); while (fullcount >= nodecount && fullcount) fullcount >>= 1; botrowcount = nodecount - fullcount; for (curr = loadnil->left; curr != loadnil; curr = next) { next = curr->left; if (complete == NULL && botrowcount-- == 0) { assert (baselevel == 0); assert (level == 0); baselevel = level = 1; complete = tree[0]; if (complete != 0) { tree[0] = 0; complete->right = dictnil; while (tree[level] != 0) { tree[level]->right = complete; complete->parent = tree[level]; complete = tree[level]; tree[level++] = 0; } } } if (complete == NULL) { curr->left = dictnil; curr->right = dictnil; curr->color = level % 2; complete = curr; assert (level == baselevel); while (tree[level] != 0) { tree[level]->right = complete; complete->parent = tree[level]; complete = tree[level]; tree[level++] = 0; } } else { curr->left = complete; curr->color = (level + 1) % 2; complete->parent = curr; tree[level] = curr; complete = 0; level = baselevel; } } if (complete == NULL) complete = dictnil; for (i = 0; i < DICT_DEPTH_MAX; i++) { if (tree[i] != 0) { tree[i]->right = complete; complete->parent = tree[i]; complete = tree[i]; } } dictnil->color = dnode_black; dictnil->right = dictnil; complete->parent = dictnil; complete->color = dnode_black; dict_root(dict) = complete; assert (dict_verify(dict)); } void dict_merge(dict_t *dest, dict_t *source) { dict_load_t load; dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source); assert (dict_similar(dest, source)); if (source == dest) return; dest->nodecount = 0; load_begin_internal(&load, dest); for (;;) { if (leftnode != NULL && rightnode != NULL) { if (dest->compare(leftnode->key, rightnode->key) < 0) goto copyleft; else goto copyright; } else if (leftnode != NULL) { goto copyleft; } else if (rightnode != NULL) { goto copyright; } else { assert (leftnode == NULL && rightnode == NULL); break; } copyleft: { dnode_t *next = dict_next(dest, leftnode); #ifndef NDEBUG leftnode->left = NULL; /* suppress assertion in dict_load_next */ #endif dict_load_next(&load, leftnode, leftnode->key); leftnode = next; continue; } copyright: { dnode_t *next = dict_next(source, rightnode); #ifndef NDEBUG rightnode->left = NULL; #endif dict_load_next(&load, rightnode, rightnode->key); rightnode = next; continue; } } dict_clear(source); dict_load_end(&load); } pmacct-0.14.0/src/isis/isis_spf.c0000640000175000017500000007527111734671551015614 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_spf.c * The SPT algorithm * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_SPF_C #include "pmacct.h" #include "isis.h" #include "linklist.h" #include "prefix.h" #include "hash.h" #include "table.h" #include "isis_constants.h" #include "isis_common.h" #include "dict.h" #include "thread.h" #include "isisd.h" #include "isis_misc.h" #include "isis_adjacency.h" #include "isis_circuit.h" #include "isis_tlv.h" #include "isis_pdu.h" #include "isis_lsp.h" #include "isis_dynhn.h" #include "isis_spf.h" #include "isis_route.h" #include "isis_csm.h" extern struct isis *isis; extern struct thread_master *master; extern struct host host; int isis_run_spf_l1 (struct thread *thread); int isis_run_spf_l2 (struct thread *thread); char * vtype2string (enum vertextype vtype) { switch (vtype) { case VTYPE_PSEUDO_IS: return "pseudo_IS"; break; case VTYPE_PSEUDO_TE_IS: return "pseudo_TE-IS"; break; case VTYPE_NONPSEUDO_IS: return "IS"; break; case VTYPE_NONPSEUDO_TE_IS: return "TE-IS"; break; case VTYPE_ES: return "ES"; break; case VTYPE_IPREACH_INTERNAL: return "IP internal"; break; case VTYPE_IPREACH_EXTERNAL: return "IP external"; break; case VTYPE_IPREACH_TE: return "IP TE"; break; #ifdef ENABLE_IPV6 case VTYPE_IP6REACH_INTERNAL: return "IP6 internal"; break; case VTYPE_IP6REACH_EXTERNAL: return "IP6 external"; break; #endif /* ENABLE_IPV6 */ default: return "UNKNOWN"; } return NULL; /* Not reached */ } char * vid2string (struct isis_vertex *vertex, u_char * buff) { switch (vertex->type) { case VTYPE_PSEUDO_IS: case VTYPE_PSEUDO_TE_IS: return rawlspid_print (vertex->N.id); break; case VTYPE_NONPSEUDO_IS: case VTYPE_NONPSEUDO_TE_IS: case VTYPE_ES: return sysid_print (vertex->N.id); break; case VTYPE_IPREACH_INTERNAL: case VTYPE_IPREACH_EXTERNAL: case VTYPE_IPREACH_TE: #ifdef ENABLE_IPV6 case VTYPE_IP6REACH_INTERNAL: case VTYPE_IP6REACH_EXTERNAL: #endif /* ENABLE_IPV6 */ isis_prefix2str ((struct isis_prefix *) &vertex->N.prefix, (char *) buff, BUFSIZ); break; default: return "UNKNOWN"; } return (char *) buff; } /* 7.2.7 */ static void remove_excess_adjs (struct list *adjs) { struct listnode *node, *excess = NULL; struct isis_adjacency *adj, *candidate = NULL; int comp; for (ALL_LIST_ELEMENTS_RO (adjs, node, adj)) { if (excess == NULL) excess = node; candidate = listgetdata (excess); if (candidate->sys_type < adj->sys_type) { excess = node; candidate = adj; continue; } if (candidate->sys_type > adj->sys_type) continue; comp = memcmp (candidate->sysid, adj->sysid, ISIS_SYS_ID_LEN); if (comp > 0) { excess = node; candidate = adj; continue; } if (comp < 0) continue; if (candidate->circuit->circuit_id > adj->circuit->circuit_id) { excess = node; candidate = adj; continue; } if (candidate->circuit->circuit_id < adj->circuit->circuit_id) continue; comp = memcmp (candidate->snpa, adj->snpa, ETH_ALEN); if (comp > 0) { excess = node; candidate = adj; continue; } } list_delete_node (adjs, excess); return; } static struct isis_spftree * isis_spftree_new () { struct isis_spftree *tree; tree = calloc(1, sizeof (struct isis_spftree)); if (tree == NULL) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): ISIS-Spf: isis_spftree_new Out of memory!\n"); return NULL; } tree->tents = list_new (); tree->paths = list_new (); return tree; } static void isis_vertex_del (struct isis_vertex *vertex) { list_delete (vertex->Adj_N); free(vertex); return; } #if 0 /* HT: Not used yet. */ static void isis_spftree_del (struct isis_spftree *spftree) { spftree->tents->del = (void (*)(void *)) isis_vertex_del; list_delete (spftree->tents); spftree->paths->del = (void (*)(void *)) isis_vertex_del; list_delete (spftree->paths); free(spftree); return; } #endif void spftree_area_init (struct isis_area *area) { if ((area->is_type & IS_LEVEL_1) && area->spftree[0] == NULL) { area->spftree[0] = isis_spftree_new (); #ifdef ENABLE_IPV6 area->spftree6[0] = isis_spftree_new (); #endif /* thread_add_timer (master, isis_run_spf_l1, area, isis_jitter (PERIODIC_SPF_INTERVAL, 10)); */ } if ((area->is_type & IS_LEVEL_2) && area->spftree[1] == NULL) { area->spftree[1] = isis_spftree_new (); #ifdef ENABLE_IPV6 area->spftree6[1] = isis_spftree_new (); #endif /* thread_add_timer (master, isis_run_spf_l2, area, isis_jitter (PERIODIC_SPF_INTERVAL, 10)); */ } return; } static struct isis_vertex * isis_vertex_new (void *id, enum vertextype vtype) { struct isis_vertex *vertex; vertex = calloc(1, sizeof (struct isis_vertex)); if (vertex == NULL) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): isis_vertex_new Out of memory!\n"); return NULL; } vertex->type = vtype; switch (vtype) { case VTYPE_ES: case VTYPE_NONPSEUDO_IS: case VTYPE_NONPSEUDO_TE_IS: memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN); break; case VTYPE_PSEUDO_IS: case VTYPE_PSEUDO_TE_IS: memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN + 1); break; case VTYPE_IPREACH_INTERNAL: case VTYPE_IPREACH_EXTERNAL: case VTYPE_IPREACH_TE: #ifdef ENABLE_IPV6 case VTYPE_IP6REACH_INTERNAL: case VTYPE_IP6REACH_EXTERNAL: #endif /* ENABLE_IPV6 */ memcpy (&vertex->N.prefix, (struct isis_prefix *) id, sizeof (struct isis_prefix)); break; default: Log(LOG_ERR, "ERROR ( default/core/ISIS ): WTF!\n"); } vertex->Adj_N = list_new (); return vertex; } /* * Add this IS to the root of SPT */ static void isis_spf_add_self (struct isis_spftree *spftree, struct isis_area *area, int level) { struct isis_vertex *vertex; struct isis_lsp *lsp; u_char lspid[ISIS_SYS_ID_LEN + 2]; memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID (lspid) = 0; LSP_FRAGMENT (lspid) = 0; lsp = lsp_search (lspid, area->lspdb[level - 1]); if (lsp == NULL) Log(LOG_WARNING, "WARN ( default/core/ISIS ): ISIS-Spf: could not find own l%d LSP!\n", level); if (!area->oldmetric) vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_TE_IS); else vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_IS); vertex->lsp = lsp; listnode_add (spftree->paths, vertex); return; } static struct isis_vertex * isis_find_vertex (struct list *list, void *id, enum vertextype vtype) { struct listnode *node; struct isis_vertex *vertex; struct isis_prefix *p1, *p2; for (ALL_LIST_ELEMENTS_RO (list, node, vertex)) { if (vertex->type != vtype) continue; switch (vtype) { case VTYPE_ES: case VTYPE_NONPSEUDO_IS: case VTYPE_NONPSEUDO_TE_IS: if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN) == 0) return vertex; break; case VTYPE_PSEUDO_IS: case VTYPE_PSEUDO_TE_IS: if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN + 1) == 0) return vertex; break; case VTYPE_IPREACH_INTERNAL: case VTYPE_IPREACH_EXTERNAL: case VTYPE_IPREACH_TE: #ifdef ENABLE_IPV6 case VTYPE_IP6REACH_INTERNAL: case VTYPE_IP6REACH_EXTERNAL: #endif /* ENABLE_IPV6 */ p1 = (struct isis_prefix *) id; p2 = (struct isis_prefix *) &vertex->N.id; if (p1->family == p2->family && p1->prefixlen == p2->prefixlen && memcmp (&p1->u.prefix, &p2->u.prefix, PSIZE (p1->prefixlen)) == 0) return vertex; break; } } return NULL; } /* * Add a vertex to TENT sorted by cost and by vertextype on tie break situation */ static struct isis_vertex * isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, void *id, struct isis_adjacency *adj, u_int32_t cost, int depth, int family) { struct isis_vertex *vertex, *v; struct listnode *node; u_char buff[BUFSIZ]; vertex = isis_vertex_new (id, vtype); vertex->d_N = cost; vertex->depth = depth; if (adj) listnode_add (vertex->Adj_N, adj); if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Spf: add to TENT %s %s depth %d dist %d\n", vtype2string (vertex->type), vid2string (vertex, buff), vertex->depth, vertex->d_N); listnode_add (spftree->tents, vertex); if (list_isempty (spftree->tents)) { listnode_add (spftree->tents, vertex); return vertex; } /* XXX: This cant use the standard ALL_LIST_ELEMENT macro */ for (node = listhead (spftree->tents); node; node = listnextnode (node)) { v = listgetdata (node); if (v->d_N > vertex->d_N) { list_add_node_prev (spftree->tents, node, vertex); break; } else if (v->d_N == vertex->d_N) { /* Tie break, add according to type */ while (v && v->d_N == vertex->d_N && v->type > vertex->type) { if (v->type > vertex->type) { break; } /* XXX: this seems dubious, node is the loop iterator */ node = listnextnode (node); (node) ? (v = listgetdata (node)) : (v = NULL); } list_add_node_prev (spftree->tents, node, vertex); break; } else if (node->next == NULL) { list_add_node_next (spftree->tents, node, vertex); break; } } return vertex; } static struct isis_vertex * isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype, void *id, struct isis_adjacency *adj, u_int32_t cost, int family) { struct isis_vertex *vertex; vertex = isis_find_vertex (spftree->tents, id, vtype); if (vertex) { /* C.2.5 c) */ if (vertex->d_N == cost) { if (adj) listnode_add (vertex->Adj_N, adj); /* d) */ if (listcount (vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) remove_excess_adjs (vertex->Adj_N); } /* f) */ else if (vertex->d_N > cost) { listnode_delete (spftree->tents, vertex); goto add2tent; } /* e) do nothing */ return vertex; } add2tent: return isis_spf_add2tent (spftree, vtype, id, adj, cost, 1, family); } static void process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, u_int16_t dist, u_int16_t depth, struct isis_adjacency *adj, int family) { struct isis_vertex *vertex; /* C.2.6 b) */ if (dist > MAX_PATH_METRIC) return; /* c) */ vertex = isis_find_vertex (spftree->paths, id, vtype); if (vertex) { assert (dist >= vertex->d_N); return; } vertex = isis_find_vertex (spftree->tents, id, vtype); /* d) */ if (vertex) { /* 1) */ if (vertex->d_N == dist) { if (adj) listnode_add (vertex->Adj_N, adj); /* 2) */ if (listcount (vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) remove_excess_adjs (vertex->Adj_N); /* 3) */ return; } else if (vertex->d_N < dist) { return; /* 4) */ } else { listnode_delete (spftree->tents, vertex); } } isis_spf_add2tent (spftree, vtype, id, adj, dist, depth, family); return; } /* * C.2.6 Step 1 */ static int isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, uint32_t cost, uint16_t depth, int family) { struct listnode *node, *fragnode = NULL; u_int16_t dist; struct is_neigh *is_neigh; struct te_is_neigh *te_is_neigh; struct ipv4_reachability *ipreach; struct te_ipv4_reachability *te_ipv4_reach; enum vertextype vtype; struct isis_prefix prefix; #ifdef ENABLE_IPV6 struct ipv6_reachability *ip6reach; #endif /* ENABLE_IPV6 */ if (!lsp->adj) return ISIS_WARNING; if (lsp->tlv_data.nlpids == NULL || !speaks (lsp->tlv_data.nlpids, family)) return ISIS_OK; lspfragloop: if (lsp->lsp_header->seq_num == 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): isis_spf_process_lsp(): lsp with 0 seq_num - do not process\n"); return ISIS_WARNING; } if (!ISIS_MASK_LSP_OL_BIT (lsp->lsp_header->lsp_bits)) { if (lsp->tlv_data.is_neighs) { for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh)) { /* C.2.6 a) */ /* Two way connectivity */ if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN)) continue; dist = cost + is_neigh->metrics.metric_default; vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS : VTYPE_NONPSEUDO_IS; process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist, depth + 1, lsp->adj, family); } } if (lsp->tlv_data.te_is_neighs) { for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, te_is_neigh)) { uint32_t metric; if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN)) continue; memcpy (&metric, te_is_neigh->te_metric, 3); dist = cost + ntohl (metric << 8); vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS : VTYPE_NONPSEUDO_TE_IS; process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist, depth + 1, lsp->adj, family); } } if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs) { prefix.family = AF_INET; for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs, node, ipreach)) { dist = cost + ipreach->metrics.metric_default; vtype = VTYPE_IPREACH_INTERNAL; prefix.u.prefix4 = ipreach->prefix; prefix.prefixlen = isis_ip_masklen (ipreach->mask); process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, lsp->adj, family); } } if (family == AF_INET && lsp->tlv_data.ipv4_ext_reachs) { prefix.family = AF_INET; for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_ext_reachs, node, ipreach)) { dist = cost + ipreach->metrics.metric_default; vtype = VTYPE_IPREACH_EXTERNAL; /* Set advertising router to the first IP address */ if (lsp->tlv_data.ipv4_addrs) { memcpy(&prefix.adv_router, listnode_head(lsp->tlv_data.ipv4_addrs), sizeof(struct in_addr)); } prefix.u.prefix4 = ipreach->prefix; prefix.prefixlen = isis_ip_masklen (ipreach->mask); process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, lsp->adj, family); } } if (family == AF_INET && lsp->tlv_data.te_ipv4_reachs) { prefix.family = AF_INET; for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs, node, te_ipv4_reach)) { dist = cost + ntohl (te_ipv4_reach->te_metric); vtype = VTYPE_IPREACH_TE; /* Set advertising router to the first IP address */ if (lsp->tlv_data.ipv4_addrs) { memcpy(&prefix.adv_router, listnode_head(lsp->tlv_data.ipv4_addrs), sizeof(struct in_addr)); } prefix.u.prefix4 = newprefix2inaddr (&te_ipv4_reach->prefix_start, te_ipv4_reach->control); prefix.prefixlen = (te_ipv4_reach->control & 0x3F); process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, lsp->adj, family); } } #ifdef ENABLE_IPV6 if (family == AF_INET6 && lsp->tlv_data.ipv6_reachs) { prefix.family = AF_INET6; for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, node, ip6reach)) { dist = cost + ip6reach->metric; vtype = (ip6reach->control_info & CTRL_INFO_DISTRIBUTION) ? VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL; prefix.prefixlen = ip6reach->prefix_len; memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix, PSIZE (ip6reach->prefix_len)); process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, lsp->adj, family); } } #endif /* ENABLE_IPV6 */ } if (fragnode == NULL) fragnode = listhead (lsp->lspu.frags); else fragnode = listnextnode (fragnode); if (fragnode) { lsp = listgetdata (fragnode); goto lspfragloop; } return ISIS_OK; } static int isis_spf_process_pseudo_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, uint16_t cost, uint16_t depth, int family) { struct listnode *node, *fragnode = NULL; struct is_neigh *is_neigh; struct te_is_neigh *te_is_neigh; enum vertextype vtype; pseudofragloop: if (lsp->lsp_header->seq_num == 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): isis_spf_process_pseudo_lsp(): lsp with 0 seq_num - do not process\n"); return ISIS_WARNING; } if (lsp->tlv_data.is_neighs) for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh)) { vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS : VTYPE_NONPSEUDO_IS; /* Two way connectivity */ if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN)) continue; if (isis_find_vertex (spftree->tents, (void *) is_neigh->neigh_id, vtype) == NULL && isis_find_vertex (spftree->paths, (void *) is_neigh->neigh_id, vtype) == NULL) { /* C.2.5 i) */ isis_spf_add2tent (spftree, vtype, is_neigh->neigh_id, lsp->adj, cost, depth, family); } } if (lsp->tlv_data.te_is_neighs) for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, te_is_neigh)) { vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS : VTYPE_NONPSEUDO_TE_IS; /* Two way connectivity */ if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN)) continue; if (isis_find_vertex (spftree->tents, (void *) te_is_neigh->neigh_id, vtype) == NULL && isis_find_vertex (spftree->paths, (void *) te_is_neigh->neigh_id, vtype) == NULL) { /* C.2.5 i) */ isis_spf_add2tent (spftree, vtype, te_is_neigh->neigh_id, lsp->adj, cost, depth, family); } } if (fragnode == NULL) fragnode = listhead (lsp->lspu.frags); else fragnode = listnextnode (fragnode); if (fragnode) { lsp = listgetdata (fragnode); goto pseudofragloop; } return ISIS_OK; } static int isis_spf_preload_tent (struct isis_spftree *spftree, struct isis_area *area, int level, int family) { struct isis_vertex *vertex; struct isis_circuit *circuit; struct listnode *cnode, *anode, *ipnode; struct isis_adjacency *adj; struct isis_lsp *lsp; struct list *adj_list; struct list *adjdb; struct prefix_ipv4 *ipv4; struct isis_prefix prefix; int retval = ISIS_OK; u_char lsp_id[ISIS_SYS_ID_LEN + 2]; #ifdef ENABLE_IPV6 struct prefix_ipv6 *ipv6; #endif /* ENABLE_IPV6 */ for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit)) { if (circuit->state != C_STATE_UP) continue; if (!(circuit->circuit_is_type & level)) continue; if (family == AF_INET && !circuit->ip_router) continue; #ifdef ENABLE_IPV6 if (family == AF_INET6 && !circuit->ipv6_router) continue; #endif /* ENABLE_IPV6 */ /* * Add IP(v6) addresses of this circuit */ if (family == AF_INET) { prefix.family = AF_INET; for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4)) { prefix.u.prefix4 = ipv4->prefix; prefix.prefixlen = ipv4->prefixlen; isis_spf_add_local (spftree, VTYPE_IPREACH_INTERNAL, &prefix, NULL, 0, family); } } #ifdef ENABLE_IPV6 if (family == AF_INET6) { prefix.family = AF_INET6; for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6)) { prefix.prefixlen = ipv6->prefixlen; prefix.u.prefix6 = ipv6->prefix; isis_spf_add_local (spftree, VTYPE_IP6REACH_INTERNAL, &prefix, NULL, 0, family); } } #endif /* ENABLE_IPV6 */ if (circuit->circ_type == CIRCUIT_T_P2P) { adj = circuit->u.p2p.neighbor; if (!adj) continue; switch (adj->sys_type) { case ISIS_SYSTYPE_ES: isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj, circuit->te_metric[level - 1], family); break; case ISIS_SYSTYPE_IS: case ISIS_SYSTYPE_L1_IS: case ISIS_SYSTYPE_L2_IS: if (speaks (&adj->nlpids, family)) isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS, adj->sysid, adj, circuit->te_metric[level - 1], family); break; case ISIS_SYSTYPE_UNKNOWN: default: Log(LOG_WARNING, "WARN ( default/core/ISIS ): isis_spf_preload_tent unknow adj type\n"); break; } } else { Log(LOG_WARNING, "WARN ( default/core/ISIS ): isis_spf_preload_tent unsupported media\n"); retval = ISIS_WARNING; } } return retval; } /* * The parent(s) for vertex is set when added to TENT list * now we just put the child pointer(s) in place */ static void add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex, struct isis_area *area, int level) { listnode_add (spftree->paths, vertex); if (vertex->type > VTYPE_ES) { if (listcount (vertex->Adj_N) > 0) { isis_route_create ((struct isis_prefix *) &vertex->N.prefix, vertex->d_N, vertex->depth, vertex->Adj_N, area, level); if (config.nfacctd_isis_msglog) { u_char prefix[BUFSIZ]; isis_prefix2str (&vertex->N.prefix, prefix, BUFSIZ); if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Spf (tag: %s, level: %u): route installed: %s\n", area->area_tag, area->is_type, prefix); } } else { if (config.nfacctd_isis_msglog) { u_char prefix[BUFSIZ]; isis_prefix2str (&vertex->N.prefix, prefix, BUFSIZ); if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Spf (tag: %s, level: %u): no adjacencies do not install route: %s\n", area->area_tag, area->is_type, prefix); } } } return; } static void init_spt (struct isis_spftree *spftree) { spftree->tents->del = spftree->paths->del = (void (*)(void *)) isis_vertex_del; list_delete_all_node (spftree->tents); list_delete_all_node (spftree->paths); spftree->tents->del = spftree->paths->del = NULL; return; } int isis_run_spf (struct isis_area *area, int level, int family) { int retval = ISIS_OK; struct listnode *node; struct isis_vertex *vertex; struct isis_spftree *spftree = NULL; u_char lsp_id[ISIS_SYS_ID_LEN + 2]; struct isis_lsp *lsp; struct route_table *table = NULL; struct route_node *rode; struct isis_route_info *rinfo; if (family == AF_INET) spftree = area->spftree[level - 1]; #ifdef ENABLE_IPV6 else if (family == AF_INET6) spftree = area->spftree6[level - 1]; #endif assert (spftree); /* Make all routes in current route table inactive. */ if (family == AF_INET) table = area->route_table[level - 1]; #ifdef ENABLE_IPV6 else if (family == AF_INET6) table = area->route_table6[level - 1]; #endif for (rode = route_top (table); rode; rode = route_next (rode)) { if (rode->info == NULL) continue; rinfo = rode->info; UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE); } /* * C.2.5 Step 0 */ init_spt (spftree); /* a) */ isis_spf_add_self (spftree, area, level); /* b) */ retval = isis_spf_preload_tent (spftree, area, level, family); /* * C.2.7 Step 2 */ if (listcount (spftree->tents) == 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): ISIS-Spf: TENT is empty\n"); goto out; } while (listcount (spftree->tents) > 0) { node = listhead (spftree->tents); vertex = listgetdata (node); /* Remove from tent list */ list_delete_node (spftree->tents, node); if (isis_find_vertex (spftree->paths, vertex->N.id, vertex->type)) continue; add_to_paths (spftree, vertex, area, level); if (vertex->type == VTYPE_PSEUDO_IS || vertex->type == VTYPE_NONPSEUDO_IS || vertex->type == VTYPE_PSEUDO_TE_IS || vertex->type == VTYPE_NONPSEUDO_TE_IS) { memcpy (lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT (lsp_id) = 0; lsp = lsp_search (lsp_id, area->lspdb[level - 1]); if (lsp) { if (LSP_PSEUDO_ID (lsp_id)) { isis_spf_process_pseudo_lsp (spftree, lsp, vertex->d_N, vertex->depth, family); } else { isis_spf_process_lsp (spftree, lsp, vertex->d_N, vertex->depth, family); } } else { Log(LOG_WARNING, "WARN ( default/core/ISIS ): ISIS-Spf: No LSP found for %s\n", rawlspid_print (lsp_id)); } } } out: // thread_add_event (master, isis_route_validate, area, 0); spftree->lastrun = time (NULL); spftree->pending = 0; Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Spf (tag: %s, level: %u): SPF algorithm run\n", area->area_tag, area->is_type); return retval; } int isis_run_spf_l1 (struct thread *thread) { struct isis_area *area; int retval = ISIS_OK; area = THREAD_ARG (thread); assert (area); area->spftree[0]->t_spf = NULL; if (!(area->is_type & IS_LEVEL_1)) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-SPF (%s) area does not share level\n", area->area_tag); return ISIS_WARNING; } Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Spf (%s) L1 SPF needed, periodic SPF\n", area->area_tag); if (area->ip_circuits) retval = isis_run_spf (area, 1, AF_INET); THREAD_TIMER_ON (master, area->spftree[0]->t_spf, isis_run_spf_l1, area, isis_jitter (PERIODIC_SPF_INTERVAL, 10)); return retval; } int isis_run_spf_l2 (struct thread *thread) { struct isis_area *area; int retval = ISIS_OK; area = THREAD_ARG (thread); assert (area); area->spftree[1]->t_spf = NULL; if (!(area->is_type & IS_LEVEL_2)) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-SPF (%s) area does not share level\n", area->area_tag); return ISIS_WARNING; } Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Spf (%s) L2 SPF needed, periodic SPF\n", area->area_tag); if (area->ip_circuits) retval = isis_run_spf (area, 2, AF_INET); THREAD_TIMER_ON (master, area->spftree[1]->t_spf, isis_run_spf_l2, area, isis_jitter (PERIODIC_SPF_INTERVAL, 10)); return retval; } int isis_spf_schedule (struct isis_area *area, int level) { int retval = ISIS_OK; struct isis_spftree *spftree = area->spftree[level - 1]; time_t diff, now = time (NULL); if (spftree->pending) return retval; diff = now - spftree->lastrun; /* FIXME: let's wait a minute before doing the SPF */ if (now - isis->uptime < 60 || isis->uptime == 0) { if (level == 1) THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, 60); else THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, 60); spftree->pending = 1; return retval; } THREAD_TIMER_OFF (spftree->t_spf); if (diff < MINIMUM_SPF_INTERVAL) { if (level == 1) THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, MINIMUM_SPF_INTERVAL - diff); else THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, MINIMUM_SPF_INTERVAL - diff); spftree->pending = 1; } else { spftree->pending = 0; retval = isis_run_spf (area, level, AF_INET); if (level == 1) THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, isis_jitter (PERIODIC_SPF_INTERVAL, 10)); else THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, isis_jitter (PERIODIC_SPF_INTERVAL, 10)); } return retval; } #ifdef ENABLE_IPV6 static int isis_run_spf6_l1 (struct thread *thread) { struct isis_area *area; int retval = ISIS_OK; area = THREAD_ARG (thread); assert (area); area->spftree6[0]->t_spf = NULL; if (!(area->is_type & IS_LEVEL_1)) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-SPF (%s) area does not share level\n", area->area_tag); return ISIS_WARNING; } Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Spf (%s) L1 SPF needed, periodic SPF\n", area->area_tag); if (area->ipv6_circuits) retval = isis_run_spf (area, 1, AF_INET6); THREAD_TIMER_ON (master, area->spftree6[0]->t_spf, isis_run_spf6_l1, area, isis_jitter (PERIODIC_SPF_INTERVAL, 10)); return retval; } static int isis_run_spf6_l2 (struct thread *thread) { struct isis_area *area; int retval = ISIS_OK; area = THREAD_ARG (thread); assert (area); area->spftree6[1]->t_spf = NULL; if (!(area->is_type & IS_LEVEL_2)) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-SPF (%s) area does not share level\n", area->area_tag); return ISIS_WARNING; } Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Spf (%s) L2 SPF needed, periodic SPF\n", area->area_tag); if (area->ipv6_circuits) retval = isis_run_spf (area, 2, AF_INET6); THREAD_TIMER_ON (master, area->spftree6[1]->t_spf, isis_run_spf6_l2, area, isis_jitter (PERIODIC_SPF_INTERVAL, 10)); return retval; } int isis_spf_schedule6 (struct isis_area *area, int level) { int retval = ISIS_OK; struct isis_spftree *spftree = area->spftree6[level - 1]; time_t diff, now = time (NULL); if (spftree->pending) return retval; diff = now - spftree->lastrun; /* FIXME: let's wait a minute before doing the SPF */ if (now - isis->uptime < 60 || isis->uptime == 0) { if (level == 1) THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area, 60); else THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area, 60); spftree->pending = 1; return retval; } THREAD_TIMER_OFF (spftree->t_spf); if (diff < MINIMUM_SPF_INTERVAL) { if (level == 1) THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area, MINIMUM_SPF_INTERVAL - diff); else THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area, MINIMUM_SPF_INTERVAL - diff); spftree->pending = 1; } else { spftree->pending = 0; retval = isis_run_spf (area, level, AF_INET6); if (level == 1) THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area, isis_jitter (PERIODIC_SPF_INTERVAL, 10)); else THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area, isis_jitter (PERIODIC_SPF_INTERVAL, 10)); } return retval; } #endif pmacct-0.14.0/src/isis/sockunion.c0000640000175000017500000003675411734647141016006 0ustar paolopaolo/* Socket union related function. * Copyright (c) 1997, 98 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #define __SOCKUNION_C #include "pmacct.h" #include "isis.h" #include "prefix.h" #include "sockunion.h" const char * inet_sutop (union sockunion *su, char *str) { switch (su->sa.sa_family) { case AF_INET: inet_ntop (AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN); break; #ifdef ENABLE_IPV6 case AF_INET6: inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN); break; #endif /* ENABLE_IPV6 */ } return str; } int str2sockunion (const char *str, union sockunion *su) { int ret; memset (su, 0, sizeof (union sockunion)); ret = inet_pton (AF_INET, str, &su->sin.sin_addr); if (ret > 0) /* Valid IPv4 address format. */ { su->sin.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN su->sin.sin_len = sizeof(struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ return 0; } #ifdef ENABLE_IPV6 ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr); if (ret > 0) /* Valid IPv6 address format. */ { su->sin6.sin6_family = AF_INET6; #ifdef SIN6_LEN su->sin6.sin6_len = sizeof(struct sockaddr_in6); #endif /* SIN6_LEN */ return 0; } #endif /* ENABLE_IPV6 */ return -1; } const char * sockunion2str (union sockunion *su, char *buf, size_t len) { if (su->sa.sa_family == AF_INET) return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len); #ifdef ENABLE_IPV6 else if (su->sa.sa_family == AF_INET6) return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len); #endif /* ENABLE_IPV6 */ return NULL; } union sockunion * sockunion_str2su (const char *str) { int ret; union sockunion *su; su = calloc(1, sizeof (union sockunion)); ret = inet_pton (AF_INET, str, &su->sin.sin_addr); if (ret > 0) /* Valid IPv4 address format. */ { su->sin.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN su->sin.sin_len = sizeof(struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ return su; } #ifdef ENABLE_IPV6 ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr); if (ret > 0) /* Valid IPv6 address format. */ { su->sin6.sin6_family = AF_INET6; #ifdef SIN6_LEN su->sin6.sin6_len = sizeof(struct sockaddr_in6); #endif /* SIN6_LEN */ return su; } #endif /* ENABLE_IPV6 */ free(su); return NULL; } char * sockunion_su2str (union sockunion *su) { char str[SU_ADDRSTRLEN]; switch (su->sa.sa_family) { case AF_INET: inet_ntop (AF_INET, &su->sin.sin_addr, str, sizeof (str)); break; #ifdef ENABLE_IPV6 case AF_INET6: inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, sizeof (str)); break; #endif /* ENABLE_IPV6 */ } return strdup(str); } /* Convert IPv4 compatible IPv6 address to IPv4 address. */ static void sockunion_normalise_mapped (union sockunion *su) { struct sockaddr_in sin; #ifdef ENABLE_IPV6 if (su->sa.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr)) { memset (&sin, 0, sizeof (struct sockaddr_in)); sin.sin_family = AF_INET; sin.sin_port = su->sin6.sin6_port; memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4); memcpy (su, &sin, sizeof (struct sockaddr_in)); } #endif /* ENABLE_IPV6 */ } /* Return socket of sockunion. */ int sockunion_socket (union sockunion *su) { int sock; sock = socket (su->sa.sa_family, SOCK_STREAM, 0); if (sock < 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Can't make socket : %s\n", strerror (errno)); return -1; } return sock; } /* Return accepted new socket file descriptor. */ int sockunion_accept (int sock, union sockunion *su) { socklen_t len; int client_sock; len = sizeof (union sockunion); client_sock = accept (sock, (struct sockaddr *) su, &len); sockunion_normalise_mapped (su); return client_sock; } /* Return sizeof union sockunion. */ static int sockunion_sizeof (union sockunion *su) { int ret; ret = 0; switch (su->sa.sa_family) { case AF_INET: ret = sizeof (struct sockaddr_in); break; #ifdef ENABLE_IPV6 case AF_INET6: ret = sizeof (struct sockaddr_in6); break; #endif /* AF_INET6 */ } return ret; } /* return sockunion structure : this function should be revised. */ static char * sockunion_log (union sockunion *su) { static char buf[SU_ADDRSTRLEN]; switch (su->sa.sa_family) { case AF_INET: snprintf (buf, SU_ADDRSTRLEN, "%s", inet_ntoa (su->sin.sin_addr)); break; #ifdef ENABLE_IPV6 case AF_INET6: snprintf (buf, SU_ADDRSTRLEN, "%s", inet_ntop (AF_INET6, &(su->sin6.sin6_addr), buf, SU_ADDRSTRLEN)); break; #endif /* ENABLE_IPV6 */ default: snprintf (buf, SU_ADDRSTRLEN, "af_unknown %d ", su->sa.sa_family); break; } return (strdup(buf)); } /* sockunion_connect returns -1 : error occured 0 : connect success 1 : connect is in progress */ enum connect_result sockunion_connect (int fd, union sockunion *peersu, unsigned short port, unsigned int ifindex) { int ret; int val; union sockunion su; memcpy (&su, peersu, sizeof (union sockunion)); switch (su.sa.sa_family) { case AF_INET: su.sin.sin_port = port; break; #ifdef ENABLE_IPV6 case AF_INET6: su.sin6.sin6_port = port; #ifdef KAME if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) { #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID /* su.sin6.sin6_scope_id = ifindex; */ #ifdef MUSICA su.sin6.sin6_scope_id = ifindex; #endif #endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */ #ifndef MUSICA SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex); #endif } #endif /* KAME */ break; #endif /* ENABLE_IPV6 */ } /* Make socket non-block. */ val = fcntl (fd, F_GETFL, 0); fcntl (fd, F_SETFL, val|O_NONBLOCK); /* Call connect function. */ ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su)); /* Immediate success */ if (ret == 0) { fcntl (fd, F_SETFL, val); return connect_success; } /* If connect is in progress then return 1 else it's real error. */ if (ret < 0) { if (errno != EINPROGRESS) { Log(LOG_INFO, "INFO ( default/core/ISIS ): can't connect to %s fd %d : %s\n", sockunion_log (&su), fd, strerror (errno)); return connect_error; } } fcntl (fd, F_SETFL, val); return connect_in_progress; } /* Make socket from sockunion union. */ int sockunion_stream_socket (union sockunion *su) { int sock; if (su->sa.sa_family == 0) su->sa.sa_family = AF_INET_UNION; sock = socket (su->sa.sa_family, SOCK_STREAM, 0); if (sock < 0) Log(LOG_WARNING, "WARN ( default/core/ISIS ): can't make socket sockunion_stream_socket\n"); return sock; } /* Bind socket to specified address. */ int sockunion_bind (int sock, union sockunion *su, unsigned short port, union sockunion *su_addr) { int size = 0; int ret; if (su->sa.sa_family == AF_INET) { size = sizeof (struct sockaddr_in); su->sin.sin_port = htons (port); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN su->sin.sin_len = size; #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ if (su_addr == NULL) su->sin.sin_addr.s_addr = htonl (INADDR_ANY); } #ifdef ENABLE_IPV6 else if (su->sa.sa_family == AF_INET6) { size = sizeof (struct sockaddr_in6); su->sin6.sin6_port = htons (port); #ifdef SIN6_LEN su->sin6.sin6_len = size; #endif /* SIN6_LEN */ if (su_addr == NULL) { #if defined(LINUX_IPV6) || defined(NRL) memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr)); #else su->sin6.sin6_addr = in6addr_any; #endif /* LINUX_IPV6 */ } } #endif /* ENABLE_IPV6 */ ret = bind (sock, (struct sockaddr *)su, size); if (ret < 0) Log(LOG_WARNING, "WARN ( default/core/ISIS ): can't bind socket : %s\n", strerror (errno)); return ret; } int sockopt_reuseaddr (int sock) { int ret; int on = 1; ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof (on)); if (ret < 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): can't set sockopt SO_REUSEADDR to socket %d\n", sock); return -1; } return 0; } #ifdef SO_REUSEPORT int sockopt_reuseport (int sock) { int ret; int on = 1; ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT, (void *) &on, sizeof (on)); if (ret < 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): can't set sockopt SO_REUSEPORT to socket %d\n", sock); return -1; } return 0; } #else int sockopt_reuseport (int sock) { return 0; } #endif /* 0 */ int sockopt_ttl (int family, int sock, int ttl) { int ret; #ifdef IP_TTL if (family == AF_INET) { ret = setsockopt (sock, IPPROTO_IP, IP_TTL, (void *) &ttl, sizeof (int)); if (ret < 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): can't set sockopt IP_TTL %d to socket %d\n", ttl, sock); return -1; } return 0; } #endif /* IP_TTL */ #ifdef ENABLE_IPV6 if (family == AF_INET6) { ret = setsockopt (sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (void *) &ttl, sizeof (int)); if (ret < 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): can't set sockopt IPV6_UNICAST_HOPS %d to socket %d\n", ttl, sock); return -1; } return 0; } #endif /* ENABLE_IPV6 */ return 0; } int sockopt_cork (int sock, int onoff) { #ifdef TCP_CORK return setsockopt (sock, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff)); #else return 0; #endif } int sockopt_minttl (int family, int sock, int minttl) { #ifdef IP_MINTTL if (family == AF_INET) { int ret = setsockopt (sock, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl)); if (ret < 0) Log(LOG_WARNING, "WARN ( default/core/ISIS ): can't set sockopt IP_MINTTL to %d on socket %d: %s\n", minttl, sock, strerror (errno)); return ret; } #endif /* IP_MINTTL */ #ifdef IPV6_MINHOPCNT if (family == AF_INET6) { int ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MINHOPCNT, &minttl, sizeof(minttl)); if (ret < 0) Log(LOG_WARNING, "WARN ( default/core/ISIS ): can't set sockopt IPV6_MINHOPCNT to %d on socket %d: %s\n", minttl, sock, strerror (errno)); return ret; } #endif errno = EOPNOTSUPP; return -1; } /* If same family and same prefix return 1. */ int sockunion_same (union sockunion *su1, union sockunion *su2) { int ret = 0; if (su1->sa.sa_family != su2->sa.sa_family) return 0; switch (su1->sa.sa_family) { case AF_INET: ret = memcmp (&su1->sin.sin_addr, &su2->sin.sin_addr, sizeof (struct in_addr)); break; #ifdef ENABLE_IPV6 case AF_INET6: ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr, sizeof (struct in6_addr)); break; #endif /* ENABLE_IPV6 */ } if (ret == 0) return 1; else return 0; } /* After TCP connection is established. Get local address and port. */ union sockunion * sockunion_getsockname (int fd) { int ret; socklen_t len; union { struct sockaddr sa; struct sockaddr_in sin; #ifdef ENABLE_IPV6 struct sockaddr_in6 sin6; #endif /* ENABLE_IPV6 */ char tmp_buffer[128]; } name; union sockunion *su; memset (&name, 0, sizeof name); len = sizeof name; ret = getsockname (fd, (struct sockaddr *)&name, &len); if (ret < 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Can't get local address and port by getsockname: %s\n", strerror (errno)); return NULL; } if (name.sa.sa_family == AF_INET) { su = calloc(1, sizeof (union sockunion)); memcpy (su, &name, sizeof (struct sockaddr_in)); return su; } #ifdef ENABLE_IPV6 if (name.sa.sa_family == AF_INET6) { su = calloc(1, sizeof (union sockunion)); memcpy (su, &name, sizeof (struct sockaddr_in6)); sockunion_normalise_mapped (su); return su; } #endif /* ENABLE_IPV6 */ return NULL; } /* After TCP connection is established. Get remote address and port. */ union sockunion * sockunion_getpeername (int fd) { int ret; socklen_t len; union { struct sockaddr sa; struct sockaddr_in sin; #ifdef ENABLE_IPV6 struct sockaddr_in6 sin6; #endif /* ENABLE_IPV6 */ char tmp_buffer[128]; } name; union sockunion *su; memset (&name, 0, sizeof name); len = sizeof name; ret = getpeername (fd, (struct sockaddr *)&name, &len); if (ret < 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Can't get remote address and port: %s\n", strerror (errno)); return NULL; } if (name.sa.sa_family == AF_INET) { su = calloc(1, sizeof (union sockunion)); memcpy (su, &name, sizeof (struct sockaddr_in)); return su; } #ifdef ENABLE_IPV6 if (name.sa.sa_family == AF_INET6) { su = calloc(1, sizeof (union sockunion)); memcpy (su, &name, sizeof (struct sockaddr_in6)); sockunion_normalise_mapped (su); return su; } #endif /* ENABLE_IPV6 */ return NULL; } /* Print sockunion structure */ static void __attribute__ ((unused)) sockunion_print (union sockunion *su) { if (su == NULL) return; switch (su->sa.sa_family) { case AF_INET: printf ("%s\n", inet_ntoa (su->sin.sin_addr)); break; #ifdef ENABLE_IPV6 case AF_INET6: { char buf [SU_ADDRSTRLEN]; printf ("%s\n", inet_ntop (AF_INET6, &(su->sin6.sin6_addr), buf, sizeof (buf))); } break; #endif /* ENABLE_IPV6 */ #ifdef AF_LINK case AF_LINK: { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)&(su->sa); printf ("link#%d\n", sdl->sdl_index); } break; #endif /* AF_LINK */ default: printf ("af_unknown %d\n", su->sa.sa_family); break; } } #ifdef ENABLE_IPV6 static int in6addr_cmp (struct in6_addr *addr1, struct in6_addr *addr2) { unsigned int i; u_char *p1, *p2; p1 = (u_char *)addr1; p2 = (u_char *)addr2; for (i = 0; i < sizeof (struct in6_addr); i++) { if (p1[i] > p2[i]) return 1; else if (p1[i] < p2[i]) return -1; } return 0; } #endif /* ENABLE_IPV6 */ int sockunion_cmp (union sockunion *su1, union sockunion *su2) { if (su1->sa.sa_family > su2->sa.sa_family) return 1; if (su1->sa.sa_family < su2->sa.sa_family) return -1; if (su1->sa.sa_family == AF_INET) { if (ntohl (su1->sin.sin_addr.s_addr) == ntohl (su2->sin.sin_addr.s_addr)) return 0; if (ntohl (su1->sin.sin_addr.s_addr) > ntohl (su2->sin.sin_addr.s_addr)) return 1; else return -1; } #ifdef ENABLE_IPV6 if (su1->sa.sa_family == AF_INET6) return in6addr_cmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); #endif /* ENABLE_IPV6 */ return 0; } /* Duplicate sockunion. */ union sockunion * sockunion_dup (union sockunion *su) { union sockunion *dup = calloc(1, sizeof (union sockunion)); memcpy (dup, su, sizeof (union sockunion)); return dup; } void sockunion_free (union sockunion *su) { free(su); } pmacct-0.14.0/src/isis/table.c0000640000175000017500000002262011734647141015050 0ustar paolopaolo/* * Routing Table functions. * Copyright (C) 1998 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #define __TABLE_C #include "pmacct.h" #include "isis.h" #include "prefix.h" #include "table.h" #include "sockunion.h" void route_node_delete (struct route_node *); void route_table_free (struct route_table *); struct route_table * route_table_init (void) { struct route_table *rt; rt = calloc(1, sizeof(struct route_table)); return rt; } void route_table_finish (struct route_table *rt) { route_table_free (rt); } /* Allocate new route node. */ static struct route_node * route_node_new (void) { struct route_node *node; node = calloc(1, sizeof (struct route_node)); return node; } /* Allocate new route node with prefix set. */ static struct route_node * route_node_set (struct route_table *table, struct isis_prefix *prefix) { struct route_node *node; node = route_node_new (); isis_prefix_copy (&node->p, prefix); node->table = table; return node; } /* Free route node. */ static void route_node_free (struct route_node *node) { free(node); } /* Free route table. */ void route_table_free (struct route_table *rt) { struct route_node *tmp_node; struct route_node *node; if (rt == NULL) return; node = rt->top; while (node) { if (node->l_left) { node = node->l_left; continue; } if (node->l_right) { node = node->l_right; continue; } tmp_node = node; node = node->parent; if (node != NULL) { if (node->l_left == tmp_node) node->l_left = NULL; else node->l_right = NULL; route_node_free (tmp_node); } else { route_node_free (tmp_node); break; } } free(rt); return; } /* Utility mask array. */ static const u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; /* Common prefix route genaration. */ static void route_common (struct isis_prefix *n, struct isis_prefix *p, struct isis_prefix *new) { int i; u_char diff; u_char mask; u_char *np = (u_char *)&n->u.prefix; u_char *pp = (u_char *)&p->u.prefix; u_char *newp = (u_char *)&new->u.prefix; for (i = 0; i < p->prefixlen / 8; i++) { if (np[i] == pp[i]) newp[i] = np[i]; else break; } new->prefixlen = i * 8; if (new->prefixlen != p->prefixlen) { diff = np[i] ^ pp[i]; mask = 0x80; while (new->prefixlen < p->prefixlen && !(mask & diff)) { mask >>= 1; new->prefixlen++; } newp[i] = np[i] & maskbit[new->prefixlen % 8]; } } static void set_link (struct route_node *node, struct route_node *new) { unsigned int bit = prefix_bit (&new->p.u.prefix, node->p.prefixlen); node->link[bit] = new; new->parent = node; } /* Lock node. */ struct route_node * route_lock_node (struct route_node *node) { node->lock++; return node; } /* Unlock node. */ void route_unlock_node (struct route_node *node) { node->lock--; if (node->lock == 0) route_node_delete (node); } /* Find matched prefix. */ struct route_node * route_node_match (const struct route_table *table, const struct isis_prefix *p) { struct route_node *node; struct route_node *matched; matched = NULL; node = table->top; /* Walk down tree. If there is matched route then store it to matched. */ while (node && node->p.prefixlen <= p->prefixlen && isis_prefix_match (&node->p, p)) { if (node->info) matched = node; if (node->p.prefixlen == p->prefixlen) break; node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; } /* If matched route found, return it. */ if (matched) return route_lock_node (matched); return NULL; } struct route_node * route_node_match_ipv4 (const struct route_table *table, const struct in_addr *addr) { struct prefix_ipv4 p; memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; p.prefix = *addr; return route_node_match (table, (struct isis_prefix *) &p); } #ifdef ENABLE_IPV6 struct route_node * route_node_match_ipv6 (const struct route_table *table, const struct in6_addr *addr) { struct prefix_ipv6 p; memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; p.prefixlen = IPV6_MAX_PREFIXLEN; p.prefix = *addr; return route_node_match (table, (struct sis_prefix *) &p); } #endif /* ENABLE_IPV6 */ /* Lookup same prefix node. Return NULL when we can't find route. */ struct route_node * route_node_lookup (struct route_table *table, struct isis_prefix *p) { struct route_node *node; node = table->top; while (node && node->p.prefixlen <= p->prefixlen && isis_prefix_match (&node->p, p)) { if (node->p.prefixlen == p->prefixlen) return node->info ? route_lock_node (node) : NULL; node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; } return NULL; } /* Add node to routing table. */ struct route_node * route_node_get (struct route_table *table, struct isis_prefix *p) { struct route_node *new; struct route_node *node; struct route_node *match; match = NULL; node = table->top; while (node && node->p.prefixlen <= p->prefixlen && isis_prefix_match (&node->p, p)) { if (node->p.prefixlen == p->prefixlen) return route_lock_node (node); match = node; node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; } if (node == NULL) { new = route_node_set (table, p); if (match) set_link (match, new); else table->top = new; } else { new = route_node_new (); route_common (&node->p, p, &new->p); new->p.family = p->family; new->table = table; set_link (new, node); if (match) set_link (match, new); else table->top = new; if (new->p.prefixlen != p->prefixlen) { match = new; new = route_node_set (table, p); set_link (match, new); } } route_lock_node (new); return new; } /* Delete node from the routing table. */ void route_node_delete (struct route_node *node) { struct route_node *child; struct route_node *parent; assert (node->lock == 0); assert (node->info == NULL); if (node->l_left && node->l_right) return; if (node->l_left) child = node->l_left; else child = node->l_right; parent = node->parent; if (child) child->parent = parent; if (parent) { if (parent->l_left == node) parent->l_left = child; else parent->l_right = child; } else node->table->top = child; route_node_free (node); /* If parent node is stub then delete it also. */ if (parent && parent->lock == 0) route_node_delete (parent); } /* Get fist node and lock it. This function is useful when one want to lookup all the node exist in the routing table. */ struct route_node * route_top (struct route_table *table) { /* If there is no node in the routing table return NULL. */ if (table->top == NULL) return NULL; /* Lock the top node and return it. */ route_lock_node (table->top); return table->top; } /* Unlock current node and lock next node then return it. */ struct route_node * route_next (struct route_node *node) { struct route_node *next; struct route_node *start; /* Node may be deleted from route_unlock_node so we have to preserve next node's pointer. */ if (node->l_left) { next = node->l_left; route_lock_node (next); route_unlock_node (node); return next; } if (node->l_right) { next = node->l_right; route_lock_node (next); route_unlock_node (node); return next; } start = node; while (node->parent) { if (node->parent->l_left == node && node->parent->l_right) { next = node->parent->l_right; route_lock_node (next); route_unlock_node (start); return next; } node = node->parent; } route_unlock_node (start); return NULL; } /* Unlock current node and lock next node until limit. */ struct route_node * route_next_until (struct route_node *node, struct route_node *limit) { struct route_node *next; struct route_node *start; /* Node may be deleted from route_unlock_node so we have to preserve next node's pointer. */ if (node->l_left) { next = node->l_left; route_lock_node (next); route_unlock_node (node); return next; } if (node->l_right) { next = node->l_right; route_lock_node (next); route_unlock_node (node); return next; } start = node; while (node->parent && node != limit) { if (node->parent->l_left == node && node->parent->l_right) { next = node->parent->l_right; route_lock_node (next); route_unlock_node (start); return next; } node = node->parent; } route_unlock_node (start); return NULL; } pmacct-0.14.0/src/isis/isis_misc.h0000640000175000017500000000544211705253204015742 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_misc.h * Miscellanous routines * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_MISC_H_ #define _ISIS_MISC_H_ /* includes */ #include /* defines */ /* Host configuration variable */ struct host { /* Host name of this router. */ char *name; /* Password for vty interface. */ char *password; char *password_encrypt; /* Enable password */ char *enable; char *enable_encrypt; /* System wide terminal lines. */ int lines; /* Log filename. */ char *logfile; /* config file name of this host */ char *config; /* Flags for services */ int advanced; int encrypt; /* Banner configuration. */ const char *motd; char *motdfile; }; /* prototypes */ #if (!defined __ISIS_MISC_C) #define EXT extern #else #define EXT #endif EXT int string2circuit_t (const u_char *); EXT const char *circuit_t2string (int); EXT const char *syst2string (int); EXT struct in_addr newprefix2inaddr (u_char *, u_char); EXT int dotformat2buff (u_char *, const u_char *); EXT int sysid2buff (u_char *, const u_char *); EXT const char *isonet_print (u_char *, int); EXT const char *sysid_print (u_char *); EXT const char *snpa_print (u_char *); EXT const char *rawlspid_print (u_char *); EXT const char *time2string (u_int32_t); EXT char *nlpid2string (struct nlpids *); EXT int speaks (struct nlpids *, int); EXT unsigned long isis_jitter (unsigned long, unsigned long); EXT const char *unix_hostname (void); #undef EXT /* * macros */ #define GETSYSID(A,L) (A->area_addr + (A->addr_len - (L + 1))) /* used for calculating nice string representation instead of plain seconds */ #define SECS_PER_MINUTE 60 #define SECS_PER_HOUR 3600 #define SECS_PER_DAY 86400 #define SECS_PER_WEEK 604800 #define SECS_PER_MONTH 2628000 #define SECS_PER_YEAR 31536000 enum { ISIS_UI_LEVEL_BRIEF, ISIS_UI_LEVEL_DETAIL, ISIS_UI_LEVEL_EXTENSIVE, }; #endif /* _ISIS_MISC_H_ */ pmacct-0.14.0/src/isis/isis_lsp.h0000640000175000017500000001071011705253204015577 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_lsp.h * LSP processing * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_LSP_H_ #define _ISIS_LSP_H_ /* The grand plan is to support 1024 circuits so we have 32*32 bit flags * the support will be achived using the newest drafts */ #define ISIS_MAX_CIRCUITS 32 /* = 1024 - FIXME:defined in flags.h as well */ /* Structure for isis_lsp, this structure will only support the fixed * System ID (Currently 6) (atleast for now). In order to support more * We will have to split the header into two parts, and for readability * sake it should better be avoided */ struct isis_lsp { struct isis_fixed_hdr *isis_header; /* normally equals pdu */ struct isis_link_state_hdr *lsp_header; /* pdu + isis_header_len */ struct stream *pdu; /* full pdu lsp */ union { struct list *frags; struct isis_lsp *zero_lsp; } lspu; u_int32_t SRMflags[ISIS_MAX_CIRCUITS]; u_int32_t SSNflags[ISIS_MAX_CIRCUITS]; u_int32_t rexmit_queue[ISIS_MAX_CIRCUITS]; int level; /* L1 or L2? */ int purged; /* have purged this one */ int scheduled; /* scheduled for sending */ time_t installed; time_t last_generated; time_t last_sent; int own_lsp; #ifdef TOPOLOGY_GENERATE int from_topology; struct thread *t_lsp_top_ref; #endif /* used for 60 second counting when rem_lifetime is zero */ int age_out; struct isis_adjacency *adj; /* FIXME: For now only topology LSP's use this. Is it helpful for others? */ struct isis_area *area; struct tlvs tlv_data; /* Simplifies TLV access */ }; #define LSP_EQUAL 1 #define LSP_NEWER 2 #define LSP_OLDER 3 #define LSP_PSEUDO_ID(I) ((I)[ISIS_SYS_ID_LEN]) #define LSP_FRAGMENT(I) ((I)[ISIS_SYS_ID_LEN + 1]) #define OWNLSPID(I) \ memcpy ((I), isis->sysid, ISIS_SYS_ID_LEN);\ (I)[ISIS_SYS_ID_LEN] = 0;\ (I)[ISIS_SYS_ID_LEN + 1] = 0 #if (!defined __ISIS_LSP_C) #define EXT extern #else #define EXT #endif EXT dict_t *lsp_db_init (); EXT void lsp_db_destroy (dict_t *); EXT int lsp_l1_generate (struct isis_area *); EXT int lsp_l2_generate (struct isis_area *); EXT int lsp_refresh_l1 (struct thread *); EXT int lsp_refresh_l2 (struct thread *); EXT int lsp_regenerate_schedule (struct isis_area *); EXT int lsp_l1_pseudo_generate (struct isis_circuit *); EXT int lsp_l2_pseudo_generate (struct isis_circuit *); EXT int lsp_l1_refresh_pseudo (struct thread *); EXT int lsp_l2_refresh_pseudo (struct thread *); EXT int isis_lsp_authinfo_check (struct stream *, struct isis_area *, int, struct isis_passwd *); EXT struct isis_lsp *lsp_new (u_char *, u_int16_t, u_int32_t, u_int8_t, u_int16_t, int); EXT struct isis_lsp *lsp_new_from_stream_ptr (struct stream *, u_int16_t, struct isis_lsp *, struct isis_area *); EXT void lsp_insert (struct isis_lsp *, dict_t *); EXT struct isis_lsp *lsp_search (u_char *, dict_t *); EXT void lsp_build_list (u_char *, u_char *, struct list *, dict_t *); EXT void lsp_build_list_nonzero_ht (u_char *, u_char *, struct list *, dict_t *); EXT void lsp_build_list_ssn (struct isis_circuit *, struct list *, dict_t *); EXT void lsp_search_and_destroy (u_char *, dict_t *); EXT void lsp_purge_dr (u_char *, struct isis_circuit *, int); EXT void lsp_purge_non_exist (struct isis_link_state_hdr *, struct isis_area *); EXT int lsp_id_cmp (u_char *, u_char *); EXT int lsp_compare (char *, struct isis_lsp *, u_int32_t, u_int16_t, u_int16_t); EXT void lsp_update (struct isis_lsp *, struct isis_link_state_hdr *, struct stream *, struct isis_area *, int); EXT void lsp_inc_seqnum (struct isis_lsp *, u_int32_t); EXT const char *lsp_bits2string (u_char *); #undef EXT #endif /* ISIS_LSP */ pmacct-0.14.0/src/isis/isis_tlv.h0000640000175000017500000002414111734647141015622 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_tlv.h * IS-IS TLV related routines * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_TLV_H_ #define _ISIS_TLV_H_ /* * The list of TLVs we (should) support. * ____________________________________________________________________________ * Name Value IIH LSP SNP Status * LAN * ____________________________________________________________________________ * * Area Addresses 1 y y n ISO10589 * IIS Neighbors 2 n y n ISO10589 * ES Neighbors 3 n y n ISO10589 * IIS Neighbors 6 y n n ISO10589 * Padding 8 y n n ISO10589 * LSP Entries 9 n n y ISO10589 * Authentication 10 y y y ISO10589, RFC3567 * Checksum 12 y n y RFC3358 * TE IS Reachability 22 n y n RFC3784 * IS Alias 24 n y n RFC3786 * IP Int. Reachability 128 n y n RFC1195 * Protocols Supported 129 y y n RFC1195 * IP Ext. Reachability 130 n y n RFC1195 * IDRPI 131 n y y RFC1195 * IP Interface Address 132 y y n RFC1195 * TE Router ID 134 n y n RFC3784 * Extended IP Reachability 135 n y n RFC3784 * Dynamic Hostname 137 n y n RFC2763 * Shared Risk Link Group 138 n y y draft-ietf-isis-gmpls-extensions * Restart TLV 211 y n n RFC3847 * MT IS Reachability 222 n y n draft-ietf-isis-wg-multi-topology * MT Supported 229 y y n draft-ietf-isis-wg-multi-topology * IPv6 Interface Address 232 y y n draft-ietf-isis_ipv6 * MT IP Reachability 235 n y n draft-ietf-isis-wg-multi-topology * IPv6 IP Reachability 236 n y n draft-ietf-isis_ipv6 * MT IPv6 IP Reachability 237 n y n draft-ietf-isis-wg-multi-topology * P2P Adjacency State 240 y n n RFC3373 * IIH Sequence Number 241 y n n draft-shen-isis-iih-sequence * Router Capability 242 - - - draft-ietf-isis-caps * * * IS Reachability sub-TLVs we (should) support. * ____________________________________________________________________________ * Name Value Status * ____________________________________________________________________________ * Administartive group (color) 3 RFC3784 * Link Local/Remote Identifiers 4 draft-ietf-isis-gmpls-extensions * IPv4 interface address 6 RFC3784 * IPv4 neighbor address 8 RFC3784 * Maximum link bandwidth 9 RFC3784 * Reservable link bandwidth 10 RFC3784 * Unreserved bandwidth 11 RFC3784 * TE Default metric 18 RFC3784 * Link Protection Type 20 draft-ietf-isis-gmpls-extensions * Interface Switching Capability 21 draft-ietf-isis-gmpls-extensions * * * IP Reachability sub-TLVs we (should) support. * ____________________________________________________________________________ * Name Value Status * ____________________________________________________________________________ * 32bit administrative tag 1 draft-ietf-isis-admin-tags * 64bit administrative tag 2 draft-ietf-isis-admin-tags * Management prefix color 117 draft-ietf-isis-wg-multi-topology */ #define AREA_ADDRESSES 1 #define IS_NEIGHBOURS 2 #define ES_NEIGHBOURS 3 #define LAN_NEIGHBOURS 6 #define PADDING 8 #define LSP_ENTRIES 9 #define AUTH_INFO 10 #define CHECKSUM 12 #define TE_IS_NEIGHBOURS 22 #define IS_ALIAS 24 #define IPV4_INT_REACHABILITY 128 #define PROTOCOLS_SUPPORTED 129 #define IPV4_EXT_REACHABILITY 130 #define IDRP_INFO 131 #define IPV4_ADDR 132 #define TE_ROUTER_ID 134 #define TE_IPV4_REACHABILITY 135 #define DYNAMIC_HOSTNAME 137 #define GRACEFUL_RESTART 211 #define IPV6_ADDR 232 #define IPV6_REACHABILITY 236 #define WAY3_HELLO 240 #define IS_NEIGHBOURS_LEN (ISIS_SYS_ID_LEN + 5) #define LAN_NEIGHBOURS_LEN 6 #define LSP_ENTRIES_LEN (10 + ISIS_SYS_ID_LEN) /* FIXME: should be entry */ #define IPV4_REACH_LEN 12 #define IPV6_REACH_LEN 22 /* struct for neighbor */ struct is_neigh { struct metric metrics; u_char neigh_id[ISIS_SYS_ID_LEN + 1]; }; /* struct for te is neighbor */ struct te_is_neigh { u_char neigh_id[ISIS_SYS_ID_LEN + 1]; u_char te_metric[3]; u_char sub_tlvs_length; }; /* struct for es neighbors */ struct es_neigh { struct metric metrics; /* approximate position of first, we use the * length ((uchar*)metric-1) to know all */ u_char first_es_neigh[ISIS_SYS_ID_LEN]; }; struct partition_desig_level2_is { struct list *isis_system_ids; }; /* struct for lan neighbors */ struct lan_neigh { u_char LAN_addr[6]; }; /* struct for LSP entry */ struct lsp_entry { u_int16_t rem_lifetime; u_char lsp_id[ISIS_SYS_ID_LEN + 2]; u_int32_t seq_num; u_int16_t checksum; } __attribute__ ((packed)); /* struct for checksum */ struct checksum { u_int16_t checksum; }; /* ipv4 reachability */ struct ipv4_reachability { struct metric metrics; struct in_addr prefix; struct in_addr mask; }; /* te router id */ struct te_router_id { struct in_addr id; }; /* te ipv4 reachability */ struct te_ipv4_reachability { u_int32_t te_metric; u_char control; u_char prefix_start; /* since this is variable length by nature it only */ }; /* points to an approximate location */ struct idrp_info { u_char len; u_char *value; }; #ifdef ENABLE_IPV6 struct ipv6_reachability { u_int32_t metric; u_char control_info; u_char prefix_len; u_char prefix[16]; }; #endif /* ENABLE_IPV6 */ /* bits in control_info */ #define CTRL_INFO_DIRECTION 0x80 #define DIRECTION_UP 0 #define DIRECTION_DOWN 1 #define CTRL_INFO_DISTRIBUTION 0x40 #define DISTRIBUTION_INTERNAL 0 #define DISTRIBUTION_EXTERNAL 1 #define CTRL_INFO_SUBTLVS 0x20 /* * Pointer to each tlv type, filled by parse_tlvs() */ struct tlvs { struct list *area_addrs; struct list *is_neighs; struct list *te_is_neighs; struct list *es_neighs; struct list *lsp_entries; struct list *prefix_neighs; struct list *lan_neighs; struct checksum *checksum; struct nlpids *nlpids; struct list *ipv4_addrs; struct list *ipv4_int_reachs; struct list *ipv4_ext_reachs; struct list *te_ipv4_reachs; struct hostname *hostname; struct te_router_id *router_id; #ifdef ENABLE_IPV6 struct list *ipv6_addrs; struct list *ipv6_reachs; #endif struct isis_passwd auth_info; }; /* * Own definitions - used to bitmask found and expected */ #define TLVFLAG_AREA_ADDRS (1<<0) #define TLVFLAG_IS_NEIGHS (1<<1) #define TLVFLAG_ES_NEIGHS (1<<2) #define TLVFLAG_PARTITION_DESIG_LEVEL2_IS (1<<3) #define TLVFLAG_PREFIX_NEIGHS (1<<4) #define TLVFLAG_LAN_NEIGHS (1<<5) #define TLVFLAG_LSP_ENTRIES (1<<6) #define TLVFLAG_PADDING (1<<7) #define TLVFLAG_AUTH_INFO (1<<8) #define TLVFLAG_IPV4_INT_REACHABILITY (1<<9) #define TLVFLAG_NLPID (1<<10) #define TLVFLAG_IPV4_EXT_REACHABILITY (1<<11) #define TLVFLAG_IPV4_ADDR (1<<12) #define TLVFLAG_DYN_HOSTNAME (1<<13) #define TLVFLAG_IPV6_ADDR (1<<14) #define TLVFLAG_IPV6_REACHABILITY (1<<15) #define TLVFLAG_TE_IS_NEIGHS (1<<16) #define TLVFLAG_TE_IPV4_REACHABILITY (1<<17) #define TLVFLAG_3WAY_HELLO (1<<18) #define TLVFLAG_TE_ROUTER_ID (1<<19) #define TLVFLAG_CHECKSUM (1<<20) #define TLVFLAG_GRACEFUL_RESTART (1<<21) #if (!defined __ISIS_TLV_C) #define EXT extern #else #define EXT #endif EXT void init_tlvs (struct tlvs *, uint32_t); EXT void free_tlvs (struct tlvs *); EXT int parse_tlvs (char *, u_char *, int, u_int32_t *, u_int32_t *, struct tlvs *); EXT void free_tlv (void *); EXT int tlv_add_area_addrs (struct list *, struct stream *); EXT int tlv_add_is_neighs (struct list *, struct stream *); EXT int tlv_add_te_is_neighs (struct list *, struct stream *); EXT int tlv_add_lan_neighs (struct list *, struct stream *); EXT int tlv_add_nlpid (struct nlpids *, struct stream *); EXT int tlv_add_checksum (struct checksum *, struct stream *); EXT int tlv_add_authinfo (char, char, u_char *, struct stream *); EXT int tlv_add_ip_addrs (struct list *, struct stream *); EXT int tlv_add_in_addr (struct in_addr *, struct stream *, u_char); EXT int tlv_add_dynamic_hostname (struct hostname *, struct stream *); EXT int tlv_add_lsp_entries (struct list *, struct stream *); EXT int tlv_add_ipv4_reachs (struct list *, struct stream *); EXT int tlv_add_te_ipv4_reachs (struct list *, struct stream *); #ifdef ENABLE_IPV6 EXT int tlv_add_ipv6_addrs (struct list *, struct stream *); EXT int tlv_add_ipv6_reachs (struct list *, struct stream *); #endif /* ENABLE_IPV6 */ EXT int tlv_add_padding (struct stream *); #undef EXT #endif /* _ISIS_TLV_H_ */ pmacct-0.14.0/src/isis/isisd.c0000640000175000017500000002114011734671574015077 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isisd.c * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISISD_C #include "pmacct.h" #include "isis.h" #include "hash.h" #include "linklist.h" #include "prefix.h" #include "table.h" #include "dict.h" #include "thread.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_circuit.h" #include "isis_flags.h" #include "isisd.h" #include "isis_misc.h" #include "isis_tlv.h" #include "isis_lsp.h" #include "isis_constants.h" #include "isis_adjacency.h" #include "isis_dynhn.h" #include "isis_pdu.h" #include "isis_spf.h" #include "isis_route.h" #include "isis_csm.h" struct isis *isis = NULL; extern struct thread_master *master; /* * Prototypes. */ void isis_new(unsigned long); struct isis_area *isis_area_create(void); int isis_area_get(const char *); int isis_area_destroy(const char *); int area_net_title(struct isis_area *, const u_char *); int area_clear_net_title(struct isis_area *, const u_char *); void isis_new (unsigned long process_id) { isis = calloc(1, sizeof (struct isis)); /* * Default values */ isis->max_area_addrs = 3; isis->process_id = process_id; isis->area_list = list_new (); isis->init_circ_list = list_new (); isis->uptime = time (NULL); isis->nexthops = list_new (); #ifdef ENABLE_IPV6 isis->nexthops6 = list_new (); #endif /* ENABLE_IPV6 */ /* * uncomment the next line for full debugs */ /* isis->debugs = 0xFFFF; */ } void isis_init () { isis_new (0); } struct isis_area * isis_area_create () { struct isis_area *area; area = calloc(1, sizeof (struct isis_area)); /* * The first instance is level-1-2 rest are level-1, unless otherwise * configured */ if (listcount (isis->area_list) > 0) area->is_type = IS_LEVEL_1; else area->is_type = IS_LEVEL_1_AND_2; /* * intialize the databases */ area->lspdb[0] = lsp_db_init (); area->lspdb[1] = lsp_db_init (); spftree_area_init (area); area->route_table[0] = route_table_init (); area->route_table[1] = route_table_init (); #ifdef ENABLE_IPV6 area->route_table6[0] = route_table_init (); area->route_table6[1] = route_table_init (); #endif /* ENABLE_IPV6 */ area->circuit_list = list_new (); area->area_addrs = list_new (); flags_initialize (&area->flags); /* * Default values */ area->max_lsp_lifetime[0] = MAX_AGE; /* 1200 */ area->max_lsp_lifetime[1] = MAX_AGE; /* 1200 */ area->lsp_gen_interval[0] = LSP_GEN_INTERVAL_DEFAULT; area->lsp_gen_interval[1] = LSP_GEN_INTERVAL_DEFAULT; area->lsp_refresh[0] = MAX_LSP_GEN_INTERVAL; /* 900 */ area->lsp_refresh[1] = MAX_LSP_GEN_INTERVAL; /* 900 */ area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; area->dynhostname = 1; area->oldmetric = 1; area->lsp_frag_threshold = 90; /* FIXME: Think of a better way... */ area->min_bcast_mtu = 1497; return area; } struct isis_area * isis_area_lookup (const char *area_tag) { struct isis_area *area; struct listnode *node; for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) if ((area->area_tag == NULL && area_tag == NULL) || (area->area_tag && area_tag && strcmp (area->area_tag, area_tag) == 0)) return area; return NULL; } int isis_area_get (const char *area_tag) { struct isis_area *area; area = isis_area_lookup (area_tag); if (area) { return FALSE; } area = isis_area_create (); area->area_tag = strdup (area_tag); listnode_add (isis->area_list, area); Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): New IS-IS area instance %s\n", area->area_tag); return FALSE; } int isis_area_destroy (const char *area_tag) { struct isis_area *area; struct listnode *node, *nnode; struct isis_circuit *circuit; area = isis_area_lookup (area_tag); if (area == NULL) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Can't find ISIS instance %s\n", area_tag); return TRUE; } if (area->circuit_list) { for (ALL_LIST_ELEMENTS (area->circuit_list, node, nnode, circuit)) { /* The fact that it's in circuit_list means that it was configured */ isis_csm_state_change (ISIS_DISABLE, circuit, area); isis_circuit_down (circuit); isis_circuit_deconfigure (circuit, area); } list_delete (area->circuit_list); } listnode_delete (isis->area_list, area); if (area->t_remove_aged) thread_cancel (area->t_remove_aged); THREAD_TIMER_OFF (area->spftree[0]->t_spf); THREAD_TIMER_OFF (area->spftree[1]->t_spf); free(area); isis->sysid_set=0; return FALSE; } int area_net_title (struct isis_area *area, const u_char *net_title) { struct area_addr *addr; struct area_addr *addrp; struct listnode *node; u_char buff[255]; if (!area) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Can't find ISIS instance\n"); return TRUE; } /* We check that we are not over the maximal number of addresses */ if (listcount (area->area_addrs) >= isis->max_area_addrs) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Maximum of area addresses (%d) already reached\n", isis->max_area_addrs); return TRUE; } addr = calloc(1, sizeof (struct area_addr)); addr->addr_len = dotformat2buff (buff, net_title); memcpy (addr->area_addr, buff, addr->addr_len); if (addr->addr_len < 8 || addr->addr_len > 20) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): area address must be at least 8..20 octets long (%d)\n", addr->addr_len); free(addr); return TRUE; } if (isis->sysid_set == 0) { /* * First area address - get the SystemID for this router */ memcpy (isis->sysid, GETSYSID (addr, ISIS_SYS_ID_LEN), ISIS_SYS_ID_LEN); isis->sysid_set = 1; Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): Router has SystemID %s\n", sysid_print (isis->sysid)); } else { /* * Check that the SystemID portions match */ if (memcmp (isis->sysid, GETSYSID (addr, ISIS_SYS_ID_LEN), ISIS_SYS_ID_LEN)) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): System ID must not change when defining additional area addresses\n"); free(addr); return TRUE; } /* now we see that we don't already have this address */ for (ALL_LIST_ELEMENTS_RO (area->area_addrs, node, addrp)) { if ((addrp->addr_len + ISIS_SYS_ID_LEN + 1) != (addr->addr_len)) continue; if (!memcmp (addrp->area_addr, addr->area_addr, addr->addr_len)) { free(addr); return FALSE; /* silent fail */ } } } /* * Forget the systemID part of the address */ addr->addr_len -= (ISIS_SYS_ID_LEN + 1); listnode_add (area->area_addrs, addr); /* Only now we can safely generate our LSPs for this area */ if (listcount (area->area_addrs) > 0) { lsp_l1_generate (area); lsp_l2_generate (area); } return FALSE; } int area_clear_net_title (struct isis_area *area, const u_char *net_title) { struct area_addr addr, *addrp = NULL; struct listnode *node; u_char buff[255]; if (!area) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Can't find ISIS instance\n"); return TRUE; } addr.addr_len = dotformat2buff (buff, net_title); if (addr.addr_len < 8 || addr.addr_len > 20) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Unsupported area address length %d, should be 8...20\n", addr.addr_len); return TRUE; } memcpy (addr.area_addr, buff, (int) addr.addr_len); for (ALL_LIST_ELEMENTS_RO (area->area_addrs, node, addrp)) if (addrp->addr_len == addr.addr_len && !memcmp (addrp->area_addr, addr.area_addr, addr.addr_len)) break; if (!addrp) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): No area address %s for area %s\n", net_title, area->area_tag); return TRUE; } listnode_delete (area->area_addrs, addrp); return FALSE; } pmacct-0.14.0/src/isis/isis_flags.h0000640000175000017500000000377711705253204016114 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_flags.h * Routines for manipulation of SSN and SRM flags * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_FLAGS_H_ #define _ISIS_FLAGS_H_ /* The grand plan is to support 1024 circuits so we have 32*32 bit flags * the support will be achived using the newest drafts */ #define ISIS_MAX_CIRCUITS 32 /* = 1024 */ /*FIXME:defined in lsp.h as well */ #if (!defined __ISIS_FLAGS_C) #define EXT extern #else #define EXT #endif EXT void flags_initialize (struct flags *); EXT struct flags *new_flags (int); EXT int flags_get_index (struct flags *); EXT void flags_free_index (struct flags *, int); EXT int flags_any_set (u_int32_t *); #undef EXT #define ISIS_SET_FLAG(F,C) \ F[C->idx>>5] |= (1<<(C->idx & 0x1F)); #define ISIS_CLEAR_FLAG(F,C) \ F[C->idx>>5] &= ~(1<<(C->idx & 0x1F)); #define ISIS_CHECK_FLAG(F, C) F[(C)->idx>>5] & (1<<(C->idx & 0x1F)) /* sets all u_32int_t flags to 1 */ #define ISIS_FLAGS_SET_ALL(FLAGS) \ memset(FLAGS,0xFF,ISIS_MAX_CIRCUITS*4); #define ISIS_FLAGS_CLEAR_ALL(FLAGS) \ memset(FLAGS,0x00,ISIS_MAX_CIRCUITS*4); #endif /* _ISIS_FLAGS_H_ */ pmacct-0.14.0/src/isis/isis_adjacency.h0000640000175000017500000000745611734647141016750 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_adjacency.h * IS-IS adjacency handling * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_ADJACENCY_H_ #define _ISIS_ADJACENCY_H_ enum isis_adj_usage { ISIS_ADJ_NONE, ISIS_ADJ_LEVEL1, ISIS_ADJ_LEVEL2, ISIS_ADJ_LEVEL1AND2 }; enum isis_system_type { ISIS_SYSTYPE_UNKNOWN, ISIS_SYSTYPE_ES, ISIS_SYSTYPE_IS, ISIS_SYSTYPE_L1_IS, ISIS_SYSTYPE_L2_IS }; enum isis_adj_state { ISIS_ADJ_INITIALIZING, ISIS_ADJ_UP, ISIS_ADJ_DOWN }; /* * we use the following codes to give an indication _why_ * a specific adjacency is up or down */ enum isis_adj_updown_reason { ISIS_ADJ_REASON_SEENSELF, ISIS_ADJ_REASON_AREA_MISMATCH, ISIS_ADJ_REASON_HOLDTIMER_EXPIRED, ISIS_ADJ_REASON_AUTH_FAILED, ISIS_ADJ_REASON_CHECKSUM_FAILED }; #define DIS_RECORDS 8 /* keep the last 8 DIS state changes on record */ struct isis_dis_record { int dis; /* is our neighbor the DIS ? */ time_t last_dis_change; /* timestamp for last dis change */ }; struct isis_adjacency { u_char snpa[ETH_ALEN]; /* NeighbourSNPAAddress */ u_char sysid[ISIS_SYS_ID_LEN]; /* neighbourSystemIdentifier */ u_char lanid[ISIS_SYS_ID_LEN + 1]; /* LAN id on bcast circuits */ int dischanges[ISIS_LEVELS]; /* how many DIS changes ? */ /* an array of N levels for M records */ struct isis_dis_record dis_record[DIS_RECORDS * ISIS_LEVELS]; enum isis_adj_state adj_state; /* adjacencyState */ enum isis_adj_usage adj_usage; /* adjacencyUsage */ struct list *area_addrs; /* areaAdressesOfNeighbour */ struct nlpids nlpids; /* protocols spoken ... */ struct list *ipv4_addrs; #ifdef ENABLE_IPV6 struct list *ipv6_addrs; #endif /* ENABLE_IPV6 */ u_char prio[ISIS_LEVELS]; /* priorityOfNeighbour for DIS */ int circuit_t; /* from hello PDU hdr */ int level; /* level (1 or 2) */ enum isis_system_type sys_type; /* neighbourSystemType */ u_int16_t hold_time; /* entryRemainingTime */ u_int32_t last_upd; u_int32_t last_flap; /* last time the adj flapped */ int flaps; /* number of adjacency flaps */ struct timeval expire; /* expiration timestamp */ struct isis_circuit *circuit; /* back pointer */ }; #if (!defined __ISIS_ADJACENCY_C) #define EXT extern #else #define EXT #endif EXT struct isis_adjacency *isis_adj_lookup (u_char *, struct list *); EXT struct isis_adjacency *isis_adj_lookup_snpa (u_char *, struct list *); EXT struct isis_adjacency *isis_new_adj (u_char *, u_char *, int, struct isis_circuit *); EXT void isis_delete_adj (struct isis_adjacency *, struct list *); EXT void isis_adj_state_change (struct isis_adjacency *, enum isis_adj_state, const char *); EXT int isis_adj_expire (struct isis_adjacency *); EXT void isis_adj_build_neigh_list (struct list *, struct list *); EXT void isis_adj_build_up_list (struct list *, struct list *); EXT void isis_adjdb_iterate (struct list *, void (*func) (struct isis_adjacency *, void *), void *); #undef EXT #endif /* _ISIS_ADJACENCY_H_ */ pmacct-0.14.0/src/isis/Makefile.in0000640000175000017500000000236211734647141015663 0ustar paolopaolo# $Id: Makefile.in,v 1.2 2012/03/28 17:46:09 paolo Exp $ prefix=@prefix@ exec_prefix=@exec_prefix@ bindir=@bindir@ sbindir=@sbindir@ libexecdir=@libexecdir@ datadir=@datadir@ mandir=@mandir@ sysconfdir=@sysconfdir@ srcdir=@srcdir@ top_srcdir=@top_srcdir@ VPATH=@srcdir@ CC=@CC@ DEFS=@DEFS@ LDFLAGS=@LDFLAGS@ CFLAGS=$(DEFS) -I$(srcdir) -I.. @CFLAGS@ CPPFLAGS=@CPPFLAGS@ LIBS=@LIBS@ INSTALL=@INSTALL@ RANLIB=@RANLIB@ #CFLAGS+= TARGETS=libisis.a COMMON= all: $(TARGETS) libisis.a: isis.o checksum.o dict.o table.o prefix.o sockunion.o hash.o stream.o thread.o linklist.o isis_circuit.o isis_events.o isis_route.o isis_tlv.o isis_csm.o isis_flags.o isis_misc.o isisd.o isis_adjacency.o isis_dynhn.o isis_spf.o iso_checksum.o isis_lsp.o isis_pdu.o $(COMMON) ar rc $@ isis.o checksum.o dict.o table.o prefix.o sockunion.o hash.o stream.o thread.o linklist.o isis_circuit.o isis_events.o isis_route.o isis_tlv.o isis_csm.o isis_flags.o isis_misc.o isisd.o isis_adjacency.o isis_dynhn.o isis_spf.o iso_checksum.o isis_lsp.o isis_pdu.o $(COMMON) $(RANLIB) $@ clean: rm -f $(TARGETS) *.o core *.core realclean: clean rm -rf autom4te.cache Makefile config.log config.status distclean: realclean rm -f config.h* configure strip: strip $(TARGETS) install: all pmacct-0.14.0/src/isis/isis_pdu.h0000640000175000017500000002346711731471736015621 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_pdu.h * PDU processing * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_PDU_H_ #define _ISIS_PDU_H_ /* * ISO 9542 - 7.5,7.6 * * ES to IS Fixed Header * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Intradomain Routeing Protocol Discriminator | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Length Indicator | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Version/Protocol ID extension | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Reserved = 0 | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | 0 | 0 | 0 | PDU Type | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Holding Time | 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Checksum | 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ */ struct esis_fixed_hdr { u_char idrp; u_char length; u_char version; u_char id_len; u_char pdu_type; u_int16_t holdtime; u_int16_t checksum; } __attribute__ ((packed)); #define ESIS_FIXED_HDR_LEN 9 #define ESH_PDU 2 #define ISH_PDU 4 #define RD_PDU 5 /* * IS to IS Fixed Header * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Intradomain Routeing Protocol Discriminator | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Length Indicator | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Version/Protocol ID extension | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | R | R | R | PDU Type | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Version | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Reserved | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Maximum Area Addresses | * +-------+-------+-------+-------+-------+-------+-------+-------+ */ struct isis_fixed_hdr { u_char idrp; u_char length; u_char version1; u_char id_len; u_char pdu_type; u_char version2; u_char reserved; u_char max_area_addrs; }; #define ISIS_FIXED_HDR_LEN 8 /* * IS-IS PDU types. */ #define L1_LAN_HELLO 15 #define L2_LAN_HELLO 16 /* * L1 and L2 LAN IS to IS Hello PDU header * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Reserved | Circuit Type | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Source ID + id_len * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Holding Time | 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | PDU Lenght | 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | R | Priority | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | LAN ID | id_len + 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ */ struct isis_lan_hello_hdr { u_char circuit_t; u_char source_id[ISIS_SYS_ID_LEN]; u_int16_t hold_time; u_int16_t pdu_len; u_char prio; u_char lan_id[ISIS_SYS_ID_LEN + 1]; } __attribute__ ((packed)); #define ISIS_LANHELLO_HDRLEN 19 #define P2P_HELLO 17 /* * Point-to-point IS to IS hello PDU header * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Reserved | Circuit Type | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Source ID + id_len * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Holding Time + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + PDU Lenght + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Local Circuit ID | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ */ struct isis_p2p_hello_hdr { u_char circuit_t; u_char source_id[ISIS_SYS_ID_LEN]; u_int16_t hold_time; u_int16_t pdu_len; u_char local_id; } __attribute__ ((packed)); #define ISIS_P2PHELLO_HDRLEN 12 #define L1_LINK_STATE 18 #define L2_LINK_STATE 20 /* * L1 and L2 IS to IS link state PDU header * +-------+-------+-------+-------+-------+-------+-------+-------+ * + PDU Length + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Remaining Lifetime + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | LSP ID | id_len + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Sequence Number + 4 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Checksum + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | P | ATT |LSPDBOL| ISTYPE | * +-------+-------+-------+-------+-------+-------+-------+-------+ */ struct isis_link_state_hdr { u_int16_t pdu_len; u_int16_t rem_lifetime; u_char lsp_id[ISIS_SYS_ID_LEN + 2]; u_int32_t seq_num; u_int16_t checksum; u_int8_t lsp_bits; } __attribute__ ((packed)); #define ISIS_LSP_HDR_LEN 19 #define L1_COMPLETE_SEQ_NUM 24 #define L2_COMPLETE_SEQ_NUM 25 /* * L1 and L2 IS to IS complete sequence numbers PDU header * +-------+-------+-------+-------+-------+-------+-------+-------+ * + PDU Lenght + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Source ID + id_len + 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Start LSP ID + id_len + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + End LSP ID + id_len + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ */ struct isis_complete_seqnum_hdr { u_int16_t pdu_len; u_char source_id[ISIS_SYS_ID_LEN + 1]; u_char start_lsp_id[ISIS_SYS_ID_LEN + 2]; u_char stop_lsp_id[ISIS_SYS_ID_LEN + 2]; }; #define ISIS_CSNP_HDRLEN 25 #define L1_PARTIAL_SEQ_NUM 26 #define L2_PARTIAL_SEQ_NUM 27 /* * L1 and L2 IS to IS partial sequence numbers PDU header * +-------+-------+-------+-------+-------+-------+-------+-------+ * + PDU Length + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Source ID + id_len + 1 * +---------------------------------------------------------------+ */ struct isis_partial_seqnum_hdr { u_int16_t pdu_len; u_char source_id[ISIS_SYS_ID_LEN + 1]; }; #define ISIS_PSNP_HDRLEN 9 /* * calling arguments for snp_process () */ #define ISIS_SNP_PSNP_FLAG 0 #define ISIS_SNP_CSNP_FLAG 1 #if (!defined __ISIS_PDU_C) #define EXT extern #else #define EXT #endif EXT int send_p2p_hello (struct thread *); EXT int ack_lsp (struct isis_link_state_hdr *, struct isis_circuit *, int); EXT void fill_fixed_hdr (struct isis_fixed_hdr *, u_char); EXT int send_hello (struct isis_circuit *, int); EXT int authentication_check (struct isis_passwd *, struct isis_passwd *); EXT int isis_handle_pdu (struct isis_circuit *, u_char *); EXT int isis_send_pdu_p2p (struct isis_circuit *, int); EXT int build_psnp (int, struct isis_circuit *, struct list *); EXT int send_psnp (int, struct isis_circuit *); EXT int build_csnp (int, u_char *, u_char *, struct list *, struct isis_circuit *); EXT int send_csnp (struct isis_circuit *, int); #undef EXT #endif /* _ISIS_PDU_H_ */ pmacct-0.14.0/src/isis/isis_route.h0000640000175000017500000000376611734647141016165 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_route.h * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * based on ../ospf6d/ospf6_route.[ch] * by Yasuhiro Ohara * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_ROUTE_H_ #define _ISIS_ROUTE_H_ #ifdef ENABLE_IPV6 struct isis_nexthop6 { unsigned int ifindex; struct in6_addr ip6; unsigned int lock; }; #endif /* ENABLE_IPV6 */ struct isis_nexthop { unsigned int ifindex; struct in_addr ip; unsigned int lock; }; struct isis_route_info { #define ISIS_ROUTE_FLAG_ZEBRA_SYNC 0x01 #define ISIS_ROUTE_FLAG_ACTIVE 0x02 u_char flag; u_int32_t cost; u_int32_t depth; struct list *nexthops; #ifdef ENABLE_IPV6 struct list *nexthops6; #endif /* ENABLE_IPV6 */ }; #if (!defined __ISIS_ROUTE_C) #define EXT extern #else #define EXT #endif EXT struct isis_route_info *isis_route_create (struct isis_prefix *, u_int32_t, u_int32_t, struct list *, struct isis_area *, int); EXT void isis_route_validate_table (struct isis_area *, struct route_table *); EXT void isis_route_validate_merge (struct isis_area *, int); #undef EXT #endif /* _ISIS_ROUTE_H_ */ pmacct-0.14.0/src/isis/iso_checksum.h0000640000175000017500000000232011705253204016424 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - iso_checksum.c * ISO checksum related routines * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISO_CSUM_H_ #define _ISO_CSUM_H_ #if (!defined __ISO_CHECKSUM_C) #define EXT extern #else #define EXT #endif EXT int iso_csum_verify (u_char *, int, uint16_t *); #undef EXT #endif /* _ISO_CSUM_H_ */ pmacct-0.14.0/src/isis/stream.c0000640000175000017500000004411711732176613015260 0ustar paolopaolo /* * Packet interface * Copyright (C) 1999 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #define __STREAM_C #include "pmacct.h" #include "isis.h" #include "stream.h" #include "prefix.h" /* Tests whether a position is valid */ #define GETP_VALID(S,G) \ ((G) <= (S)->endp) #define PUT_AT_VALID(S,G) GETP_VALID(S,G) #define ENDP_VALID(S,E) \ ((E) <= (S)->size) /* asserting sanity checks. Following must be true before * stream functions are called: * * Following must always be true of stream elements * before and after calls to stream functions: * * getp <= endp <= size * * Note that after a stream function is called following may be true: * if (getp == endp) then stream is no longer readable * if (endp == size) then stream is no longer writeable * * It is valid to put to anywhere within the size of the stream, but only * using stream_put..._at() functions. */ #define STREAM_WARN_OFFSETS(S) \ Log(LOG_WARNING, "WARN ( default/core/ISIS ): &(struct stream): %p, size: %lu, endp: %lu, getp: %lu\n", \ (S), \ (unsigned long) (S)->size, \ (unsigned long) (S)->getp, \ (unsigned long) (S)->endp)\ #define STREAM_VERIFY_SANE(S) \ do { \ if ( !(GETP_VALID(S, (S)->getp)) && ENDP_VALID(S, (S)->endp) ) \ STREAM_WARN_OFFSETS(S); \ assert ( GETP_VALID(S, (S)->getp) ); \ assert ( ENDP_VALID(S, (S)->endp) ); \ } while (0) #define STREAM_BOUND_WARN(S, WHAT) \ do { \ Log(LOG_WARNING, "WARN ( default/core/ISIS ): %s: Attempt to %s out of bounds", __func__, (WHAT)); \ STREAM_WARN_OFFSETS(S); \ assert (0); \ } while (0) /* XXX: Deprecated macro: do not use */ #define CHECK_SIZE(S, Z) \ do { \ if (((S)->endp + (Z)) > (S)->size) \ { \ Log(LOG_WARNING, "WARN ( default/core/ISIS ): CHECK_SIZE: truncating requested size %lu\n", \ (unsigned long) (Z)); \ STREAM_WARN_OFFSETS(S); \ (Z) = (S)->size - (S)->endp; \ } \ } while (0); /* Make stream buffer. */ struct stream * stream_new (size_t size) { struct stream *s; assert (size > 0); if (size == 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): stream_new(): called with 0 size!\n"); return NULL; } s = calloc(1, sizeof (struct stream)); if (s == NULL) return s; if ( (s->data = calloc(1, size)) == NULL) { free(s); return NULL; } s->size = size; return s; } /* Free it now. */ void stream_free (struct stream *s) { if (!s) return; free(s->data); free(s); } struct stream * stream_copy (struct stream *new, struct stream *src) { STREAM_VERIFY_SANE (src); assert (new != NULL); assert (STREAM_SIZE(new) >= src->endp); new->endp = src->endp; new->getp = src->getp; memcpy (new->data, src->data, src->endp); return new; } struct stream * stream_dup (struct stream *s) { struct stream *new; STREAM_VERIFY_SANE (s); if ( (new = stream_new (s->endp)) == NULL) return NULL; return (stream_copy (new, s)); } size_t stream_resize (struct stream *s, size_t newsize) { u_char *newdata; STREAM_VERIFY_SANE (s); newdata = realloc(s->data, newsize); if (newdata == NULL) return s->size; s->data = newdata; s->size = newsize; if (s->endp > s->size) s->endp = s->size; if (s->getp > s->endp) s->getp = s->endp; STREAM_VERIFY_SANE (s); return s->size; } size_t stream_get_getp (struct stream *s) { STREAM_VERIFY_SANE(s); return s->getp; } size_t stream_get_endp (struct stream *s) { STREAM_VERIFY_SANE(s); return s->endp; } size_t stream_get_size (struct stream *s) { STREAM_VERIFY_SANE(s); return s->size; } /* Stream structre' stream pointer related functions. */ void stream_set_getp (struct stream *s, size_t pos) { STREAM_VERIFY_SANE(s); if (!GETP_VALID (s, pos)) { STREAM_BOUND_WARN (s, "set getp"); pos = s->endp; } s->getp = pos; } /* Forward pointer. */ void stream_forward_getp (struct stream *s, size_t size) { STREAM_VERIFY_SANE(s); if (!GETP_VALID (s, s->getp + size)) { STREAM_BOUND_WARN (s, "seek getp"); return; } s->getp += size; } void stream_forward_endp (struct stream *s, size_t size) { STREAM_VERIFY_SANE(s); if (!ENDP_VALID (s, s->endp + size)) { STREAM_BOUND_WARN (s, "seek endp"); return; } s->endp += size; } /* Copy from stream to destination. */ void stream_get (void *dst, struct stream *s, size_t size) { STREAM_VERIFY_SANE(s); if (STREAM_READABLE(s) < size) { STREAM_BOUND_WARN (s, "get"); return; } memcpy (dst, s->data + s->getp, size); s->getp += size; } /* Get next character from the stream. */ u_char stream_getc (struct stream *s) { u_char c; STREAM_VERIFY_SANE (s); if (STREAM_READABLE(s) < sizeof (u_char)) { STREAM_BOUND_WARN (s, "get char"); return 0; } c = s->data[s->getp++]; return c; } /* Get next character from the stream. */ u_char stream_getc_from (struct stream *s, size_t from) { u_char c; STREAM_VERIFY_SANE(s); if (!GETP_VALID (s, from + sizeof (u_char))) { STREAM_BOUND_WARN (s, "get char"); return 0; } c = s->data[from]; return c; } /* Get next word from the stream. */ u_int16_t stream_getw (struct stream *s) { u_int16_t w; STREAM_VERIFY_SANE (s); if (STREAM_READABLE (s) < sizeof (u_int16_t)) { STREAM_BOUND_WARN (s, "get "); return 0; } w = s->data[s->getp++] << 8; w |= s->data[s->getp++]; return w; } /* Get next word from the stream. */ u_int16_t stream_getw_from (struct stream *s, size_t from) { u_int16_t w; STREAM_VERIFY_SANE(s); if (!GETP_VALID (s, from + sizeof (u_int16_t))) { STREAM_BOUND_WARN (s, "get "); return 0; } w = s->data[from++] << 8; w |= s->data[from]; return w; } /* Get next long word from the stream. */ u_int32_t stream_getl_from (struct stream *s, size_t from) { u_int32_t l; STREAM_VERIFY_SANE(s); if (!GETP_VALID (s, from + sizeof (u_int32_t))) { STREAM_BOUND_WARN (s, "get long"); return 0; } l = s->data[from++] << 24; l |= s->data[from++] << 16; l |= s->data[from++] << 8; l |= s->data[from]; return l; } u_int32_t stream_getl (struct stream *s) { u_int32_t l; STREAM_VERIFY_SANE(s); if (STREAM_READABLE (s) < sizeof (u_int32_t)) { STREAM_BOUND_WARN (s, "get long"); return 0; } l = s->data[s->getp++] << 24; l |= s->data[s->getp++] << 16; l |= s->data[s->getp++] << 8; l |= s->data[s->getp++]; return l; } /* Get next quad word from the stream. */ uint64_t stream_getq_from (struct stream *s, size_t from) { uint64_t q; STREAM_VERIFY_SANE(s); if (!GETP_VALID (s, from + sizeof (uint64_t))) { STREAM_BOUND_WARN (s, "get quad"); return 0; } q = ((uint64_t) s->data[from++]) << 56; q |= ((uint64_t) s->data[from++]) << 48; q |= ((uint64_t) s->data[from++]) << 40; q |= ((uint64_t) s->data[from++]) << 32; q |= ((uint64_t) s->data[from++]) << 24; q |= ((uint64_t) s->data[from++]) << 16; q |= ((uint64_t) s->data[from++]) << 8; q |= ((uint64_t) s->data[from++]); return q; } uint64_t stream_getq (struct stream *s) { uint64_t q; STREAM_VERIFY_SANE(s); if (STREAM_READABLE (s) < sizeof (uint64_t)) { STREAM_BOUND_WARN (s, "get quad"); return 0; } q = ((uint64_t) s->data[s->getp++]) << 56; q |= ((uint64_t) s->data[s->getp++]) << 48; q |= ((uint64_t) s->data[s->getp++]) << 40; q |= ((uint64_t) s->data[s->getp++]) << 32; q |= ((uint64_t) s->data[s->getp++]) << 24; q |= ((uint64_t) s->data[s->getp++]) << 16; q |= ((uint64_t) s->data[s->getp++]) << 8; q |= ((uint64_t) s->data[s->getp++]); return q; } /* Get next long word from the stream. */ u_int32_t stream_get_ipv4 (struct stream *s) { u_int32_t l; STREAM_VERIFY_SANE(s); if (STREAM_READABLE (s) < sizeof(u_int32_t)) { STREAM_BOUND_WARN (s, "get ipv4"); return 0; } memcpy (&l, s->data + s->getp, sizeof(u_int32_t)); s->getp += sizeof(u_int32_t); return l; } /* Copy to source to stream. * * XXX: This uses CHECK_SIZE and hence has funny semantics -> Size will wrap * around. This should be fixed once the stream updates are working. * * stream_write() is saner */ void stream_put (struct stream *s, const void *src, size_t size) { /* XXX: CHECK_SIZE has strange semantics. It should be deprecated */ CHECK_SIZE(s, size); STREAM_VERIFY_SANE(s); if (STREAM_WRITEABLE (s) < size) { STREAM_BOUND_WARN (s, "put"); return; } if (src) memcpy (s->data + s->endp, src, size); else memset (s->data + s->endp, 0, size); s->endp += size; } /* Put character to the stream. */ int stream_putc (struct stream *s, u_char c) { STREAM_VERIFY_SANE(s); if (STREAM_WRITEABLE (s) < sizeof(u_char)) { STREAM_BOUND_WARN (s, "put"); return 0; } s->data[s->endp++] = c; return sizeof (u_char); } /* Put word to the stream. */ int stream_putw (struct stream *s, u_int16_t w) { STREAM_VERIFY_SANE (s); if (STREAM_WRITEABLE (s) < sizeof (u_int16_t)) { STREAM_BOUND_WARN (s, "put"); return 0; } s->data[s->endp++] = (u_char)(w >> 8); s->data[s->endp++] = (u_char) w; return 2; } /* Put long word to the stream. */ int stream_putl (struct stream *s, u_int32_t l) { STREAM_VERIFY_SANE (s); if (STREAM_WRITEABLE (s) < sizeof (u_int32_t)) { STREAM_BOUND_WARN (s, "put"); return 0; } s->data[s->endp++] = (u_char)(l >> 24); s->data[s->endp++] = (u_char)(l >> 16); s->data[s->endp++] = (u_char)(l >> 8); s->data[s->endp++] = (u_char)l; return 4; } /* Put quad word to the stream. */ int stream_putq (struct stream *s, uint64_t q) { STREAM_VERIFY_SANE (s); if (STREAM_WRITEABLE (s) < sizeof (uint64_t)) { STREAM_BOUND_WARN (s, "put quad"); return 0; } s->data[s->endp++] = (u_char)(q >> 56); s->data[s->endp++] = (u_char)(q >> 48); s->data[s->endp++] = (u_char)(q >> 40); s->data[s->endp++] = (u_char)(q >> 32); s->data[s->endp++] = (u_char)(q >> 24); s->data[s->endp++] = (u_char)(q >> 16); s->data[s->endp++] = (u_char)(q >> 8); s->data[s->endp++] = (u_char)q; return 8; } int stream_putc_at (struct stream *s, size_t putp, u_char c) { STREAM_VERIFY_SANE(s); if (!PUT_AT_VALID (s, putp + sizeof (u_char))) { STREAM_BOUND_WARN (s, "put"); return 0; } s->data[putp] = c; return 1; } int stream_putw_at (struct stream *s, size_t putp, u_int16_t w) { STREAM_VERIFY_SANE(s); if (!PUT_AT_VALID (s, putp + sizeof (u_int16_t))) { STREAM_BOUND_WARN (s, "put"); return 0; } s->data[putp] = (u_char)(w >> 8); s->data[putp + 1] = (u_char) w; return 2; } int stream_putl_at (struct stream *s, size_t putp, u_int32_t l) { STREAM_VERIFY_SANE(s); if (!PUT_AT_VALID (s, putp + sizeof (u_int32_t))) { STREAM_BOUND_WARN (s, "put"); return 0; } s->data[putp] = (u_char)(l >> 24); s->data[putp + 1] = (u_char)(l >> 16); s->data[putp + 2] = (u_char)(l >> 8); s->data[putp + 3] = (u_char)l; return 4; } int stream_putq_at (struct stream *s, size_t putp, uint64_t q) { STREAM_VERIFY_SANE(s); if (!PUT_AT_VALID (s, putp + sizeof (uint64_t))) { STREAM_BOUND_WARN (s, "put"); return 0; } s->data[putp] = (u_char)(q >> 56); s->data[putp + 1] = (u_char)(q >> 48); s->data[putp + 2] = (u_char)(q >> 40); s->data[putp + 3] = (u_char)(q >> 32); s->data[putp + 4] = (u_char)(q >> 24); s->data[putp + 5] = (u_char)(q >> 16); s->data[putp + 6] = (u_char)(q >> 8); s->data[putp + 7] = (u_char)q; return 8; } /* Put long word to the stream. */ int stream_put_ipv4 (struct stream *s, u_int32_t l) { STREAM_VERIFY_SANE(s); if (STREAM_WRITEABLE (s) < sizeof (u_int32_t)) { STREAM_BOUND_WARN (s, "put"); return 0; } memcpy (s->data + s->endp, &l, sizeof (u_int32_t)); s->endp += sizeof (u_int32_t); return sizeof (u_int32_t); } /* Put long word to the stream. */ int stream_put_in_addr (struct stream *s, struct in_addr *addr) { STREAM_VERIFY_SANE(s); if (STREAM_WRITEABLE (s) < sizeof (u_int32_t)) { STREAM_BOUND_WARN (s, "put"); return 0; } memcpy (s->data + s->endp, addr, sizeof (u_int32_t)); s->endp += sizeof (u_int32_t); return sizeof (u_int32_t); } /* Put prefix by nlri type format. */ int stream_put_prefix (struct stream *s, struct isis_prefix *p) { size_t psize; STREAM_VERIFY_SANE(s); psize = PSIZE (p->prefixlen); if (STREAM_WRITEABLE (s) < psize) { STREAM_BOUND_WARN (s, "put"); return 0; } stream_putc (s, p->prefixlen); memcpy (s->data + s->endp, &p->u.prefix, psize); s->endp += psize; return psize; } /* Read size from fd. */ int stream_read (struct stream *s, int fd, size_t size) { int nbytes; STREAM_VERIFY_SANE(s); if (STREAM_WRITEABLE (s) < size) { STREAM_BOUND_WARN (s, "put"); return 0; } nbytes = readn (fd, s->data + s->endp, size); if (nbytes > 0) s->endp += nbytes; return nbytes; } /* Read size from fd. */ int stream_read_unblock (struct stream *s, int fd, size_t size) { int nbytes; int val; STREAM_VERIFY_SANE(s); if (STREAM_WRITEABLE (s) < size) { STREAM_BOUND_WARN (s, "put"); return 0; } val = fcntl (fd, F_GETFL, 0); fcntl (fd, F_SETFL, val|O_NONBLOCK); nbytes = read (fd, s->data + s->endp, size); fcntl (fd, F_SETFL, val); if (nbytes > 0) s->endp += nbytes; return nbytes; } ssize_t stream_read_try(struct stream *s, int fd, size_t size) { ssize_t nbytes; STREAM_VERIFY_SANE(s); if (STREAM_WRITEABLE(s) < size) { STREAM_BOUND_WARN (s, "put"); /* Fatal (not transient) error, since retrying will not help (stream is too small to contain the desired data). */ return -1; } if ((nbytes = read(fd, s->data + s->endp, size)) >= 0) { s->endp += nbytes; return nbytes; } /* Error: was it transient (return -2) or fatal (return -1)? */ if (ERRNO_IO_RETRY(errno)) return -2; Log(LOG_WARNING, "WARN ( default/core/ISIS ): %s: read failed on fd %d: %s\n", __func__, fd, strerror(errno)); return -1; } /* Read up to size bytes into the stream from the fd, using recvmsgfrom * whose arguments match the remaining arguments to this function */ ssize_t stream_recvfrom (struct stream *s, int fd, size_t size, int flags, struct sockaddr *from, socklen_t *fromlen) { ssize_t nbytes; STREAM_VERIFY_SANE(s); if (STREAM_WRITEABLE(s) < size) { STREAM_BOUND_WARN (s, "put"); /* Fatal (not transient) error, since retrying will not help (stream is too small to contain the desired data). */ return -1; } if ((nbytes = recvfrom (fd, s->data + s->endp, size, flags, from, fromlen)) >= 0) { s->endp += nbytes; return nbytes; } /* Error: was it transient (return -2) or fatal (return -1)? */ if (ERRNO_IO_RETRY(errno)) return -2; Log(LOG_WARNING, "WARN ( default/core/ISIS ): %s: read failed on fd %d: %s\n", __func__, fd, strerror(errno)); return -1; } /* Read up to smaller of size or SIZE_REMAIN() bytes to the stream, starting * from endp. * First iovec will be used to receive the data. * Stream need not be empty. */ ssize_t stream_recvmsg (struct stream *s, int fd, struct msghdr *msgh, int flags, size_t size) { int nbytes; struct iovec *iov; STREAM_VERIFY_SANE(s); assert (msgh->msg_iovlen > 0); if (STREAM_WRITEABLE (s) < size) { STREAM_BOUND_WARN (s, "put"); /* This is a logic error in the calling code: the stream is too small to hold the desired data! */ return -1; } iov = &(msgh->msg_iov[0]); iov->iov_base = (s->data + s->endp); iov->iov_len = size; nbytes = recvmsg (fd, msgh, flags); if (nbytes > 0) s->endp += nbytes; return nbytes; } /* Write data to buffer. */ size_t stream_write (struct stream *s, const void *ptr, size_t size) { CHECK_SIZE(s, size); STREAM_VERIFY_SANE(s); if (STREAM_WRITEABLE (s) < size) { STREAM_BOUND_WARN (s, "put"); return 0; } memcpy (s->data + s->endp, ptr, size); s->endp += size; return size; } /* Return current read pointer. * DEPRECATED! * Use stream_get_pnt_to if you must, but decoding streams properly * is preferred */ u_char * stream_pnt (struct stream *s) { STREAM_VERIFY_SANE(s); return s->data + s->getp; } /* Check does this stream empty? */ int stream_empty (struct stream *s) { STREAM_VERIFY_SANE(s); return (s->endp == 0); } /* Reset stream. */ void stream_reset (struct stream *s) { STREAM_VERIFY_SANE (s); s->getp = s->endp = 0; } /* Write stream contens to the file discriptor. */ int stream_flush (struct stream *s, int fd) { int nbytes; STREAM_VERIFY_SANE(s); nbytes = write (fd, s->data + s->getp, s->endp - s->getp); return nbytes; } /* Read nbytes from fd and store into ptr. */ int readn (int fd, u_char *ptr, int nbytes) { int nleft; int nread; nleft = nbytes; while (nleft > 0) { nread = read (fd, ptr, nleft); if (nread < 0) return (nread); else if (nread == 0) break; nleft -= nread; ptr += nread; } return nbytes - nleft; } pmacct-0.14.0/src/isis/checksum.c0000640000175000017500000000525711705253204015561 0ustar paolopaolo/* * Checksum routine for Internet Protocol family headers (C Version). * * Refer to "Computing the Internet Checksum" by R. Braden, D. Borman and * C. Partridge, Computer Communication Review, Vol. 19, No. 2, April 1989, * pp. 86-101, for additional details on computing this checksum. */ #define __CHECKSUM_C #include "pmacct.h" #include "isis.h" #include "checksum.h" int /* return checksum in low-order 16 bits */ in_cksum(void *parg, int nbytes) { u_short *ptr = parg; register long sum; /* assumes long == 32 bits */ u_short oddbyte; register u_short answer; /* assumes u_short == 16 bits */ /* * Our algorithm is simple, using a 32-bit accumulator (sum), * we add sequential 16-bit words to it, and at the end, fold back * all the carry bits from the top 16 bits into the lower 16 bits. */ sum = 0; while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } /* mop up an odd byte, if necessary */ if (nbytes == 1) { oddbyte = 0; /* make sure top half is zero */ *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */ sum += oddbyte; } /* * Add back carry outs from top 16 bits to low 16 bits. */ sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* ones-complement, then truncate to 16 bits */ return(answer); } /* Fletcher Checksum -- Refer to RFC1008. */ #define MODX 4102 /* 5802 should be fine */ /* To be consistent, offset is 0-based index, rather than the 1-based index required in the specification ISO 8473, Annex C.1 */ u_int16_t fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset) { u_int8_t *p; int x, y, c0, c1; u_int16_t checksum; u_int16_t *csum; size_t partial_len, i, left = len; checksum = 0; assert (offset < len); /* * Zero the csum in the packet. */ csum = (u_int16_t *) (buffer + offset); *(csum) = 0; p = buffer; c0 = 0; c1 = 0; while (left != 0) { partial_len = MIN(left, MODX); for (i = 0; i < partial_len; i++) { c0 = c0 + *(p++); c1 += c0; } c0 = c0 % 255; c1 = c1 % 255; left -= partial_len; } /* The cast is important, to ensure the mod is taken as a signed value. */ x = (int)((len - offset - 1) * c0 - c1) % 255; if (x <= 0) x += 255; y = 510 - c0 - x; if (y > 255) y -= 255; /* * Now we write this to the packet. * We could skip this step too, since the checksum returned would * be stored into the checksum field by the caller. */ buffer[offset] = x; buffer[offset + 1] = y; /* Take care of the endian issue */ checksum = htons((x << 8) | (y & 0xFF)); return checksum; } pmacct-0.14.0/src/isis/isis_network.h0000640000175000017500000000264511705253204016502 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_network.h * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_NETWORK_H_ #define _ISIS_NETWORK_H_ extern u_char ALL_L1_ISYSTEMS[]; extern u_char ALL_L2_ISYSTEMS[]; /* int isis_sock_init (struct isis_circuit *circuit); int isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa); int isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa); int isis_send_pdu_bcast (struct isis_circuit *circuit, int level); int isis_send_pdu_p2p (struct isis_circuit *circuit, int level); */ #endif /* _ISIS_NETWORK_H_ */ pmacct-0.14.0/src/isis/isis_tlv.c0000640000175000017500000007675411734647141015636 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_tlv.c * IS-IS TLV related routines * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_TLV_C #include "pmacct.h" #include "isis.h" #include "linklist.h" #include "prefix.h" #include "dict.h" #include "stream.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_flags.h" #include "isis_circuit.h" #include "isis_tlv.h" #include "isisd.h" #include "isis_dynhn.h" #include "isis_misc.h" #include "isis_pdu.h" #include "isis_lsp.h" extern struct isis *isis; /* * Prototypes. */ int add_tlv (u_char, u_char, u_char *, struct stream *); void free_tlv (void *val) { free(val); return; } /* * Called after parsing of a PDU. There shouldn't be any tlv's left, so this * is only a caution to avoid memory leaks */ void free_tlvs (struct tlvs *tlvs) { if (tlvs->area_addrs) list_delete (tlvs->area_addrs); if (tlvs->is_neighs) list_delete (tlvs->is_neighs); if (tlvs->te_is_neighs) list_delete (tlvs->te_is_neighs); if (tlvs->es_neighs) list_delete (tlvs->es_neighs); if (tlvs->lsp_entries) list_delete (tlvs->lsp_entries); if (tlvs->lan_neighs) list_delete (tlvs->lan_neighs); if (tlvs->prefix_neighs) list_delete (tlvs->prefix_neighs); if (tlvs->ipv4_addrs) list_delete (tlvs->ipv4_addrs); if (tlvs->ipv4_int_reachs) list_delete (tlvs->ipv4_int_reachs); if (tlvs->ipv4_ext_reachs) list_delete (tlvs->ipv4_ext_reachs); if (tlvs->te_ipv4_reachs) list_delete (tlvs->te_ipv4_reachs); #ifdef ENABLE_IPV6 if (tlvs->ipv6_addrs) list_delete (tlvs->ipv6_addrs); if (tlvs->ipv6_reachs) list_delete (tlvs->ipv6_reachs); #endif /* ENABLE_IPV6 */ return; } /* * Parses the tlvs found in the variant length part of the PDU. * Caller tells with flags in "expected" which TLV's it is interested in. */ int parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, u_int32_t * found, struct tlvs *tlvs) { u_char type, length; struct lan_neigh *lan_nei; struct area_addr *area_addr; struct is_neigh *is_nei; struct te_is_neigh *te_is_nei; struct es_neigh *es_nei; struct lsp_entry *lsp_entry; struct in_addr *ipv4_addr; struct ipv4_reachability *ipv4_reach; struct te_ipv4_reachability *te_ipv4_reach; #ifdef ENABLE_IPV6 struct in6_addr *ipv6_addr; struct ipv6_reachability *ipv6_reach; int prefix_octets; #endif /* ENABLE_IPV6 */ u_char virtual; int value_len, retval = ISIS_OK; u_char *pnt = stream; *found = 0; memset (tlvs, 0, sizeof (struct tlvs)); while (pnt < stream + size - 2) { type = *pnt; length = *(pnt + 1); pnt += 2; value_len = 0; if (pnt + length > stream + size) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): ISIS-TLV (%s): TLV (type %d, length %d) exceeds packet boundaries\n", areatag, type, length); retval = ISIS_WARNING; break; } switch (type) { case AREA_ADDRESSES: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | Address Length | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Area Address | * +-------+-------+-------+-------+-------+-------+-------+-------+ * : : */ *found |= TLVFLAG_AREA_ADDRS; if (*expected & TLVFLAG_AREA_ADDRS) { while (length > value_len) { area_addr = (struct area_addr *) pnt; value_len += area_addr->addr_len + 1; pnt += area_addr->addr_len + 1; if (!tlvs->area_addrs) tlvs->area_addrs = list_new (); listnode_add (tlvs->area_addrs, area_addr); } } else { pnt += length; } break; case IS_NEIGHBOURS: *found |= TLVFLAG_IS_NEIGHS; if (TLVFLAG_IS_NEIGHS & *expected) { /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | Virtual Flag | * +-------+-------+-------+-------+-------+-------+-------+-------+ */ virtual = *pnt; /* FIXME: what is the use for this? */ pnt++; value_len++; /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | 0 | I/E | Default Metric | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Delay Metric | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Expense Metric | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Error Metric | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Neighbour ID | * +---------------------------------------------------------------+ * : : */ while (length > value_len) { is_nei = (struct is_neigh *) pnt; value_len += 4 + ISIS_SYS_ID_LEN + 1; pnt += 4 + ISIS_SYS_ID_LEN + 1; if (!tlvs->is_neighs) tlvs->is_neighs = list_new (); listnode_add (tlvs->is_neighs, is_nei); } } else { pnt += length; } break; case TE_IS_NEIGHBOURS: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | Neighbour ID | 7 * +---------------------------------------------------------------+ * | TE Metric | 3 * +---------------------------------------------------------------+ * | SubTLVs Length | 1 * +---------------------------------------------------------------+ * : : */ *found |= TLVFLAG_TE_IS_NEIGHS; if (TLVFLAG_TE_IS_NEIGHS & *expected) { while (length > value_len) { te_is_nei = (struct te_is_neigh *) pnt; value_len += 11; pnt += 11; /* FIXME - subtlvs are handled here, for now we skip */ value_len += te_is_nei->sub_tlvs_length; pnt += te_is_nei->sub_tlvs_length; if (!tlvs->te_is_neighs) tlvs->te_is_neighs = list_new (); listnode_add (tlvs->te_is_neighs, te_is_nei); } } else { pnt += length; } break; case ES_NEIGHBOURS: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | 0 | I/E | Default Metric | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Delay Metric | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Expense Metric | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Error Metric | * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Neighbour ID | * +---------------------------------------------------------------+ * | Neighbour ID | * +---------------------------------------------------------------+ * : : */ *found |= TLVFLAG_ES_NEIGHS; if (*expected & TLVFLAG_ES_NEIGHS) { es_nei = (struct es_neigh *) pnt; value_len += 4; pnt += 4; while (length > value_len) { /* FIXME FIXME FIXME - add to the list */ /* sys_id->id = pnt; */ value_len += ISIS_SYS_ID_LEN; pnt += ISIS_SYS_ID_LEN; /* if (!es_nei->neigh_ids) es_nei->neigh_ids = sysid; */ } if (!tlvs->es_neighs) tlvs->es_neighs = list_new (); listnode_add (tlvs->es_neighs, es_nei); } else { pnt += length; } break; case LAN_NEIGHBOURS: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | LAN Address | * +-------+-------+-------+-------+-------+-------+-------+-------+ * : : */ *found |= TLVFLAG_LAN_NEIGHS; if (TLVFLAG_LAN_NEIGHS & *expected) { while (length > value_len) { lan_nei = (struct lan_neigh *) pnt; if (!tlvs->lan_neighs) tlvs->lan_neighs = list_new (); listnode_add (tlvs->lan_neighs, lan_nei); value_len += ETH_ALEN; pnt += ETH_ALEN; } } else { pnt += length; } break; case PADDING: pnt += length; break; case LSP_ENTRIES: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | Remaining Lifetime | 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | LSP ID | id+2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | LSP Sequence Number | 4 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Checksum | 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ */ *found |= TLVFLAG_LSP_ENTRIES; if (TLVFLAG_LSP_ENTRIES & *expected) { while (length > value_len) { lsp_entry = (struct lsp_entry *) pnt; value_len += 10 + ISIS_SYS_ID_LEN; pnt += 10 + ISIS_SYS_ID_LEN; if (!tlvs->lsp_entries) tlvs->lsp_entries = list_new (); listnode_add (tlvs->lsp_entries, lsp_entry); } } else { pnt += length; } break; case CHECKSUM: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | 16 bit fletcher CHECKSUM | * +-------+-------+-------+-------+-------+-------+-------+-------+ * : : */ *found |= TLVFLAG_CHECKSUM; if (*expected & TLVFLAG_CHECKSUM) { tlvs->checksum = (struct checksum *) pnt; } pnt += length; break; case PROTOCOLS_SUPPORTED: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | NLPID | * +-------+-------+-------+-------+-------+-------+-------+-------+ * : : */ *found |= TLVFLAG_NLPID; if (*expected & TLVFLAG_NLPID) { tlvs->nlpids = (struct nlpids *) (pnt - 1); } pnt += length; break; case IPV4_ADDR: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * + IP version 4 address + 4 * +-------+-------+-------+-------+-------+-------+-------+-------+ * : : */ *found |= TLVFLAG_IPV4_ADDR; if (*expected & TLVFLAG_IPV4_ADDR) { while (length > value_len) { ipv4_addr = (struct in_addr *) pnt; if (!tlvs->ipv4_addrs) tlvs->ipv4_addrs = list_new (); listnode_add (tlvs->ipv4_addrs, ipv4_addr); value_len += 4; pnt += 4; } } else { pnt += length; } break; case AUTH_INFO: *found |= TLVFLAG_AUTH_INFO; if (*expected & TLVFLAG_AUTH_INFO) { tlvs->auth_info.type = *pnt; tlvs->auth_info.len = length-1; pnt++; memcpy (tlvs->auth_info.passwd, pnt, length - 1); pnt += length - 1; } else { pnt += length; } break; case DYNAMIC_HOSTNAME: *found |= TLVFLAG_DYN_HOSTNAME; if (*expected & TLVFLAG_DYN_HOSTNAME) { /* the length is also included in the pointed struct */ tlvs->hostname = (struct hostname *) (pnt - 1); } pnt += length; break; case TE_ROUTER_ID: /* +---------------------------------------------------------------+ * + Router ID + 4 * +---------------------------------------------------------------+ */ *found |= TLVFLAG_TE_ROUTER_ID; if (*expected & TLVFLAG_TE_ROUTER_ID) tlvs->router_id = (struct te_router_id *) (pnt); pnt += length; break; case IPV4_INT_REACHABILITY: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | 0 | I/E | Default Metric | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Delay Metric | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Expense Metric | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Error Metric | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | ip address | 4 * +---------------------------------------------------------------+ * | address mask | 4 * +---------------------------------------------------------------+ * : : */ *found |= TLVFLAG_IPV4_INT_REACHABILITY; if (*expected & TLVFLAG_IPV4_INT_REACHABILITY) { while (length > value_len) { ipv4_reach = (struct ipv4_reachability *) pnt; if (!tlvs->ipv4_int_reachs) tlvs->ipv4_int_reachs = list_new (); listnode_add (tlvs->ipv4_int_reachs, ipv4_reach); value_len += 12; pnt += 12; } } else { pnt += length; } break; case IPV4_EXT_REACHABILITY: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | 0 | I/E | Default Metric | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Delay Metric | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Expense Metric | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | S | I/E | Error Metric | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | ip address | 4 * +---------------------------------------------------------------+ * | address mask | 4 * +---------------------------------------------------------------+ * : : */ *found |= TLVFLAG_IPV4_EXT_REACHABILITY; if (*expected & TLVFLAG_IPV4_EXT_REACHABILITY) { while (length > value_len) { ipv4_reach = (struct ipv4_reachability *) pnt; if (!tlvs->ipv4_ext_reachs) tlvs->ipv4_ext_reachs = list_new (); listnode_add (tlvs->ipv4_ext_reachs, ipv4_reach); value_len += 12; pnt += 12; } } else { pnt += length; } break; case TE_IPV4_REACHABILITY: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | TE Metric | 4 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | U/D | sTLV? | Prefix Mask Len | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Prefix | 0-4 * +---------------------------------------------------------------+ * | sub tlvs | * +---------------------------------------------------------------+ * : : */ *found |= TLVFLAG_TE_IPV4_REACHABILITY; if (*expected & TLVFLAG_TE_IPV4_REACHABILITY) { while (length > value_len) { te_ipv4_reach = (struct te_ipv4_reachability *) pnt; if (!tlvs->te_ipv4_reachs) tlvs->te_ipv4_reachs = list_new (); listnode_add (tlvs->te_ipv4_reachs, te_ipv4_reach); /* this trickery is permitable since no subtlvs are defined */ value_len += 5 + ((te_ipv4_reach->control & 0x3F) ? ((((te_ipv4_reach->control & 0x3F) - 1) >> 3) + 1) : 0); pnt += 5 + ((te_ipv4_reach->control & 0x3F) ? ((((te_ipv4_reach->control & 0x3F) - 1) >> 3) + 1) : 0); } } else { pnt += length; } break; #ifdef ENABLE_IPV6 case IPV6_ADDR: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * + IP version 6 address + 16 * +-------+-------+-------+-------+-------+-------+-------+-------+ * : : */ *found |= TLVFLAG_IPV6_ADDR; if (*expected & TLVFLAG_IPV6_ADDR) { while (length > value_len) { ipv6_addr = (struct in6_addr *) pnt; if (!tlvs->ipv6_addrs) tlvs->ipv6_addrs = list_new (); listnode_add (tlvs->ipv6_addrs, ipv6_addr); value_len += 16; pnt += 16; } } else { pnt += length; } break; case IPV6_REACHABILITY: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | Default Metric | 4 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Control Informantion | * +---------------------------------------------------------------+ * | IPv6 Prefix Length |--+ * +---------------------------------------------------------------+ | * | IPv6 Prefix |<-+ * +---------------------------------------------------------------+ */ *found |= TLVFLAG_IPV6_REACHABILITY; if (*expected & TLVFLAG_IPV6_REACHABILITY) { while (length > value_len) { ipv6_reach = (struct ipv6_reachability *) pnt; prefix_octets = ((ipv6_reach->prefix_len + 7) / 8); value_len += prefix_octets + 6; pnt += prefix_octets + 6; /* FIXME: sub-tlvs */ if (!tlvs->ipv6_reachs) tlvs->ipv6_reachs = list_new (); listnode_add (tlvs->ipv6_reachs, ipv6_reach); } } else { pnt += length; } break; #endif /* ENABLE_IPV6 */ case WAY3_HELLO: /* +---------------------------------------------------------------+ * | Adjacency state | 1 * +---------------------------------------------------------------+ * | Extended Local Circuit ID | 4 * +---------------------------------------------------------------+ * | Neighbor System ID (If known) | 0-8 * (probably 6) * +---------------------------------------------------------------+ * | Neighbor Local Circuit ID (If known) | 4 * +---------------------------------------------------------------+ */ *found |= TLVFLAG_3WAY_HELLO; if (*expected & TLVFLAG_3WAY_HELLO) { while (length > value_len) { /* FIXME: make this work */ /* Adjacency State (one octet): 0 = Up 1 = Initializing 2 = Down Extended Local Circuit ID (four octets) Neighbor System ID if known (zero to eight octets) Neighbor Extended Local Circuit ID (four octets, if Neighbor System ID is present) */ pnt += length; } } else { pnt += length; } break; case GRACEFUL_RESTART: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * | Reserved | SA | RA | RR | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Remaining Time | 2 * +---------------------------------------------------------------+ * | Restarting Neighbor ID (If known) | 0-8 * +---------------------------------------------------------------+ */ *found |= TLVFLAG_GRACEFUL_RESTART; if (*expected & TLVFLAG_GRACEFUL_RESTART) { /* FIXME: make this work */ } pnt += length; break; default: Log(LOG_WARNING, "WARN ( default/core/ISIS ): ISIS-TLV (%s): unsupported TLV type %d, length %d\n", areatag, type, length); retval = ISIS_WARNING; pnt += length; break; } } return retval; } int add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream) { if (STREAM_SIZE (stream) - stream_get_endp (stream) < (unsigned) len + 2) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): No room for TLV of type %d\n", tag); return ISIS_WARNING; } stream_putc (stream, tag); /* TAG */ stream_putc (stream, len); /* LENGTH */ stream_put (stream, value, (int) len); /* VALUE */ return ISIS_OK; } int tlv_add_area_addrs (struct list *area_addrs, struct stream *stream) { struct listnode *node; struct area_addr *area_addr; u_char value[255]; u_char *pos = value; for (ALL_LIST_ELEMENTS_RO (area_addrs, node, area_addr)) { if (pos - value + area_addr->addr_len > 255) goto err; *pos = area_addr->addr_len; pos++; memcpy (pos, area_addr->area_addr, (int) area_addr->addr_len); pos += area_addr->addr_len; } return add_tlv (AREA_ADDRESSES, pos - value, value, stream); err: Log(LOG_WARNING, "WARN ( default/core/ISIS ): tlv_add_area_addrs(): TLV longer than 255\n"); return ISIS_WARNING; } int tlv_add_is_neighs (struct list *is_neighs, struct stream *stream) { struct listnode *node; struct is_neigh *is_neigh; u_char value[255]; u_char *pos = value; int retval; *pos = 0; /*is_neigh->virtual; */ pos++; for (ALL_LIST_ELEMENTS_RO (is_neighs, node, is_neigh)) { if (pos - value + IS_NEIGHBOURS_LEN > 255) { retval = add_tlv (IS_NEIGHBOURS, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; } *pos = is_neigh->metrics.metric_default; pos++; *pos = is_neigh->metrics.metric_delay; pos++; *pos = is_neigh->metrics.metric_expense; pos++; *pos = is_neigh->metrics.metric_error; pos++; memcpy (pos, is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1); pos += ISIS_SYS_ID_LEN + 1; } return add_tlv (IS_NEIGHBOURS, pos - value, value, stream); } int tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream) { struct listnode *node; struct te_is_neigh *te_is_neigh; u_char value[255]; u_char *pos = value; int retval; for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh)) { /* FIXME: This will be wrong if we are going to add TE sub TLVs. */ if (pos - value + IS_NEIGHBOURS_LEN > 255) { retval = add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; } memcpy (pos, te_is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1); pos += ISIS_SYS_ID_LEN + 1; memcpy (pos, te_is_neigh->te_metric, 3); pos += 3; /* Sub TLVs length. */ *pos = 0; pos++; } return add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream); } int tlv_add_lan_neighs (struct list *lan_neighs, struct stream *stream) { struct listnode *node; u_char *snpa; u_char value[255]; u_char *pos = value; int retval; for (ALL_LIST_ELEMENTS_RO (lan_neighs, node, snpa)) { if (pos - value + ETH_ALEN > 255) { retval = add_tlv (LAN_NEIGHBOURS, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; } memcpy (pos, snpa, ETH_ALEN); pos += ETH_ALEN; } return add_tlv (LAN_NEIGHBOURS, pos - value, value, stream); } int tlv_add_nlpid (struct nlpids *nlpids, struct stream *stream) { return add_tlv (PROTOCOLS_SUPPORTED, nlpids->count, nlpids->nlpids, stream); } int tlv_add_authinfo (char auth_type, char auth_len, u_char *auth_value, struct stream *stream) { u_char value[255]; u_char *pos = value; *pos++ = ISIS_PASSWD_TYPE_CLEARTXT; memcpy (pos, auth_value, auth_len); return add_tlv (AUTH_INFO, auth_len + 1, value, stream); } int tlv_add_checksum (struct checksum *checksum, struct stream *stream) { u_char value[255]; u_char *pos = value; return add_tlv (CHECKSUM, pos - value, value, stream); } int tlv_add_ip_addrs (struct list *ip_addrs, struct stream *stream) { struct listnode *node; struct prefix_ipv4 *ipv4; u_char value[255]; u_char *pos = value; int retval; for (ALL_LIST_ELEMENTS_RO (ip_addrs, node, ipv4)) { if (pos - value + IPV4_MAX_BYTELEN > 255) { retval = add_tlv (IPV4_ADDR, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; } *(u_int32_t *) pos = ipv4->prefix.s_addr; pos += IPV4_MAX_BYTELEN; } return add_tlv (IPV4_ADDR, pos - value, value, stream); } /* Used to add TLV containing just one IPv4 address - either IPv4 address TLV * (in case of LSP) or TE router ID TLV. */ int tlv_add_in_addr (struct in_addr *addr, struct stream *stream, u_char tag) { u_char value[255]; u_char *pos = value; memcpy (pos, addr, IPV4_MAX_BYTELEN); pos += IPV4_MAX_BYTELEN; return add_tlv (tag, pos - value, value, stream); } int tlv_add_dynamic_hostname (struct hostname *hostname, struct stream *stream) { return add_tlv (DYNAMIC_HOSTNAME, hostname->namelen, hostname->name, stream); } int tlv_add_lsp_entries (struct list *lsps, struct stream *stream) { struct listnode *node; struct isis_lsp *lsp; u_char value[255]; u_char *pos = value; int retval; for (ALL_LIST_ELEMENTS_RO (lsps, node, lsp)) { if (pos - value + LSP_ENTRIES_LEN > 255) { retval = add_tlv (LSP_ENTRIES, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; } *((u_int16_t *) pos) = lsp->lsp_header->rem_lifetime; pos += 2; memcpy (pos, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 2); pos += ISIS_SYS_ID_LEN + 2; *((u_int32_t *) pos) = lsp->lsp_header->seq_num; pos += 4; *((u_int16_t *) pos) = lsp->lsp_header->checksum; pos += 2; } return add_tlv (LSP_ENTRIES, pos - value, value, stream); } int tlv_add_ipv4_reachs (struct list *ipv4_reachs, struct stream *stream) { struct listnode *node; struct ipv4_reachability *reach; u_char value[255]; u_char *pos = value; int retval; for (ALL_LIST_ELEMENTS_RO (ipv4_reachs, node, reach)) { if (pos - value + IPV4_REACH_LEN > 255) { retval = add_tlv (IPV4_INT_REACHABILITY, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; } *pos = reach->metrics.metric_default; pos++; *pos = reach->metrics.metric_delay; pos++; *pos = reach->metrics.metric_expense; pos++; *pos = reach->metrics.metric_error; pos++; *(u_int32_t *) pos = reach->prefix.s_addr; pos += IPV4_MAX_BYTELEN; *(u_int32_t *) pos = reach->mask.s_addr; pos += IPV4_MAX_BYTELEN; } return add_tlv (IPV4_INT_REACHABILITY, pos - value, value, stream); } int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream) { struct listnode *node; struct te_ipv4_reachability *te_reach; u_char value[255]; u_char *pos = value; u_char prefix_size; int retval; for (ALL_LIST_ELEMENTS_RO (te_ipv4_reachs, node, te_reach)) { prefix_size = ((((te_reach->control & 0x3F) - 1) >> 3) + 1); if (pos - value + (5 + prefix_size) > 255) { retval = add_tlv (IPV4_INT_REACHABILITY, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; } *(u_int32_t *) pos = te_reach->te_metric; pos += 4; *pos = te_reach->control; pos++; memcpy (pos, &te_reach->prefix_start, prefix_size); pos += prefix_size; } return add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream); } #ifdef ENABLE_IPV6 int tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream) { struct listnode *node; struct prefix_ipv6 *ipv6; u_char value[255]; u_char *pos = value; int retval; for (ALL_LIST_ELEMENTS_RO (ipv6_addrs, node, ipv6)) { if (pos - value + IPV6_MAX_BYTELEN > 255) { retval = add_tlv (IPV6_ADDR, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; } memcpy (pos, ipv6->prefix.s6_addr, IPV6_MAX_BYTELEN); pos += IPV6_MAX_BYTELEN; } return add_tlv (IPV6_ADDR, pos - value, value, stream); } int tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream) { struct listnode *node; struct ipv6_reachability *ip6reach; u_char value[255]; u_char *pos = value; int retval, prefix_octets; for (ALL_LIST_ELEMENTS_RO (ipv6_reachs, node, ip6reach)) { if (pos - value + IPV6_MAX_BYTELEN + 6 > 255) { retval = add_tlv (IPV6_REACHABILITY, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; } *(uint32_t *) pos = ip6reach->metric; pos += 4; *pos = ip6reach->control_info; pos++; prefix_octets = ((ip6reach->prefix_len + 7) / 8); *pos = ip6reach->prefix_len; pos++; memcpy (pos, ip6reach->prefix, prefix_octets); pos += prefix_octets; } return add_tlv (IPV6_REACHABILITY, pos - value, value, stream); } #endif /* ENABLE_IPV6 */ int tlv_add_padding (struct stream *stream) { int fullpads, i, left; /* * How many times can we add full padding ? */ fullpads = (STREAM_SIZE (stream) - stream_get_endp (stream)) / 257; for (i = 0; i < fullpads; i++) { if (!stream_putc (stream, (u_char) PADDING)) /* TAG */ goto err; if (!stream_putc (stream, (u_char) 255)) /* LENGHT */ goto err; stream_put (stream, NULL, 255); /* zero padding */ } left = STREAM_SIZE (stream) - stream_get_endp (stream); if (left < 2) return ISIS_OK; if (left == 2) { stream_putc (stream, PADDING); stream_putc (stream, 0); return ISIS_OK; } stream_putc (stream, PADDING); stream_putc (stream, left - 2); stream_put (stream, NULL, left-2); return ISIS_OK; err: Log(LOG_WARNING, "WARN ( default/core/ISIS ): tlv_add_padding(): no room for tlv\n"); return ISIS_WARNING; } pmacct-0.14.0/src/isis/isis.c0000640000175000017500000002553211740410133014717 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* defines */ #define __ISIS_C /* includes */ #include "pmacct.h" #include "isis.h" #include "thread_pool.h" #include "linklist.h" #include "stream.h" #include "hash.h" #include "prefix.h" #include "dict.h" #include "thread.h" #include "iso.h" #include "table.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_adjacency.h" #include "isis_circuit.h" #include "isis_network.h" #include "isis_misc.h" #include "isis_flags.h" #include "isis_tlv.h" #include "isisd.h" #include "isis_dynhn.h" #include "isis_lsp.h" #include "isis_pdu.h" #include "iso_checksum.h" #include "isis_csm.h" #include "isis_events.h" #include "isis_spf.h" #include "isis_route.h" /* variables to be exported away */ thread_pool_t *isis_pool; /* Functions */ #if defined ENABLE_THREADS void nfacctd_isis_wrapper() { /* initialize threads pool */ isis_pool = allocate_thread_pool(1); assert(isis_pool); Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): %d thread(s) initialized\n", 1); /* giving a kick to the BGP thread */ send_to_pool(isis_pool, skinny_isis_daemon, NULL); } #endif void skinny_isis_daemon() { char errbuf[PCAP_ERRBUF_SIZE]; struct pcap_device device; struct pcap_isis_callback_data cb_data; struct host_addr addr; struct prefix_ipv4 *ipv4; int index, ret; char area_tag[] = "default"; struct isis_area *area; struct isis_circuit *circuit; struct interface interface; memset(&device, 0, sizeof(struct pcap_device)); memset(&cb_data, 0, sizeof(cb_data)); memset(&interface, 0, sizeof(interface)); memset(&isis_spf_deadline, 0, sizeof(isis_spf_deadline)); /* initializing IS-IS structures */ isis_init(); dyn_cache_init(); /* thread master */ master = thread_master_create(); if (!config.nfacctd_isis_iface) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): 'isis_daemon_iface' value is not specified. Terminating thread.\n"); exit(1); } if ((device.dev_desc = pcap_open_live(config.nfacctd_isis_iface, 65535, 0, 1000, errbuf)) == NULL) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): pcap_open_live(): %s\n", errbuf); exit(1); } device.link_type = pcap_datalink(device.dev_desc); for (index = 0; _isis_devices[index].link_type != -1; index++) { if (device.link_type == _isis_devices[index].link_type) device.data = &_isis_devices[index]; } if (device.data == NULL) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): data link not supported: %d\n", device.link_type); return; } else { Log(LOG_INFO, "OK ( default/core/ISIS ): link type is: %d\n", device.link_type); cb_data.device = &device; } area = isis_area_create(); area->area_tag = area_tag; area->is_type = IS_LEVEL_2; area->newmetric = TRUE; listnode_add(isis->area_list, area); Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): New IS-IS area instance %s\n", area->area_tag); if (config.nfacctd_isis_net) area_net_title(area, config.nfacctd_isis_net); else { Log(LOG_ERR, "ERROR ( default/core/ISIS ): 'isis_daemon_net' value is not specified. Terminating thread.\n"); exit_all(1); } circuit = isis_circuit_new(); circuit->circ_type = CIRCUIT_T_P2P; circuit->fd = pcap_fileno(device.dev_desc); circuit->tx = isis_send_pdu_p2p; circuit->interface = &interface; circuit->state = C_STATE_UP; if (config.nfacctd_isis_ip) { trim_spaces(config.nfacctd_isis_ip); ret = str_to_addr(config.nfacctd_isis_ip, &addr); if (!ret) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): 'isis_daemon_ip' value is not a valid IPv4/IPv6 address. Terminating thread.\n"); exit_all(1); } } else { Log(LOG_ERR, "ERROR ( default/core/ISIS ): 'isis_daemon_ip' value is not specified. Terminating thread.\n"); exit_all(1); } circuit->ip_router = addr.address.ipv4.s_addr; ipv4 = isis_prefix_ipv4_new(); ipv4->prefixlen = 32; ipv4->prefix.s_addr = addr.address.ipv4.s_addr; circuit->ip_addrs = list_new(); listnode_add(circuit->ip_addrs, ipv4); circuit_update_nlpids(circuit); isis_circuit_configure(circuit, area); cb_data.circuit = circuit; area->ip_circuits = 1; memcpy(circuit->interface->name, config.nfacctd_isis_iface, strlen(config.nfacctd_isis_iface)); circuit->interface->ifindex = if_nametoindex(config.nfacctd_isis_iface); if (!config.nfacctd_isis_mtu) config.nfacctd_isis_mtu = SNAPLEN_ISIS_DEFAULT; for (;;) { /* XXX: should get a select() here at some stage? */ pcap_loop(device.dev_desc, -1, isis_pdu_runner, (u_char *) &cb_data); break; } pcap_close(device.dev_desc); } void isis_pdu_runner(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *buf) { struct pcap_isis_callback_data *cb_data = (struct pcap_isis_callback_data *) user; struct pcap_device *device = cb_data->device; struct isis_circuit *circuit = cb_data->circuit; struct packet_ptrs pptrs; struct thread thread; int ret; struct stream stm; char *ssnpa; /* Let's export a time reference */ memcpy(&isis_now, &pkthdr->ts, sizeof(struct timeval)); /* check if we have to expire adjacency first */ if (circuit && circuit->u.p2p.neighbor) { if (timeval_cmp(&isis_now, &circuit->u.p2p.neighbor->expire) >= 0) isis_adj_expire(circuit->u.p2p.neighbor); } memset(&pptrs, 0, sizeof(pptrs)); memset(&stm, 0, sizeof(stm)); if (buf) { pptrs.pkthdr = (struct pcap_pkthdr *) pkthdr; pptrs.packet_ptr = (u_char *) buf; (*device->data->handler)(pkthdr, &pptrs); if (pptrs.iph_ptr) { if ((*pptrs.l3_handler)(&pptrs)) { /*assembling handover to isis_handle_pdu() */ stm.data = pptrs.iph_ptr; stm.getp = 0; stm.endp = pkthdr->caplen - (pptrs.iph_ptr - pptrs.packet_ptr); stm.size = pkthdr->caplen - (pptrs.iph_ptr - pptrs.packet_ptr); ssnpa = pptrs.packet_ptr; circuit->rcv_stream = &stm; circuit->interface->mtu = config.nfacctd_isis_mtu; /* process IS-IS packet */ isis_handle_pdu (circuit, ssnpa); } } } /* check if it's time to run SPF */ if (timeval_cmp(&isis_now, &isis_spf_deadline) >= 0) { if (circuit->area->is_type & IS_LEVEL_1) { if (circuit->area->ip_circuits) { ret = isis_run_spf(circuit->area, 1, AF_INET); isis_route_validate_table (circuit->area, circuit->area->route_table[0]); } /* XXX: IPv6 handled here */ } if (circuit->area->is_type & IS_LEVEL_2) { if (circuit->area->ip_circuits) { ret = isis_run_spf(circuit->area, 2, AF_INET); isis_route_validate_table (circuit->area, circuit->area->route_table[1]); } /* XXX: IPv6 handled here */ } isis_route_validate_merge (circuit->area, AF_INET); dyn_cache_cleanup(); isis_spf_deadline.tv_sec = isis_now.tv_sec + isis_jitter(PERIODIC_SPF_INTERVAL, 10); isis_spf_deadline.tv_usec = 0; } if (timeval_cmp(&isis_now, &isis_psnp_deadline) >= 0) { send_psnp(1, circuit); send_psnp(2, circuit); isis_psnp_deadline.tv_sec = isis_now.tv_sec + isis_jitter(PSNP_INTERVAL, PSNP_JITTER); isis_psnp_deadline.tv_usec = 0; } } void isis_sll_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs) { register const struct sll_header *sllp; u_int caplen = h->caplen; u_int16_t etype, nl; u_char *p; if (caplen < SLL_HDR_LEN) { pptrs->iph_ptr = NULL; return; } p = pptrs->packet_ptr; sllp = (const struct sll_header *) pptrs->packet_ptr; etype = ntohs(sllp->sll_protocol); nl = SLL_HDR_LEN; if (etype == ETHERTYPE_GRE_ISO) { pptrs->l3_proto = ETHERTYPE_GRE_ISO; pptrs->l3_handler = iso_handler; pptrs->iph_ptr = (u_char *)(pptrs->packet_ptr + nl); return; } pptrs->l3_proto = 0; pptrs->l3_handler = NULL; pptrs->iph_ptr = NULL; } int iso_handler(register struct packet_ptrs *pptrs) { } void isis_srcdst_lookup(struct packet_ptrs *pptrs) { struct route_node *result; struct isis_area *area; char area_tag[] = "default"; int level; struct in_addr pref4; #if defined ENABLE_IPV6 struct in6_addr pref6; #endif pptrs->igp_src = NULL; pptrs->igp_dst = NULL; pptrs->igp_src_info = NULL; pptrs->igp_dst_info = NULL; area = isis_area_lookup(area_tag); if (area) { level = MAX(area->is_type, 2); if (pptrs->l3_proto == ETHERTYPE_IP) { if (!pptrs->igp_src) { memcpy(&pref4, &((struct my_iphdr *)pptrs->iph_ptr)->ip_src, sizeof(struct in_addr)); result = route_node_match_ipv4(area->route_table[level-1], &pref4); if (result) { pptrs->igp_src = (char *) &result->p; pptrs->igp_src_info = (char *) result->info; if (result->p.prefixlen > pptrs->lm_mask_src) { pptrs->lm_mask_src = result->p.prefixlen; pptrs->lm_method_src = NF_NET_IGP; } } } if (!pptrs->igp_dst) { memcpy(&pref4, &((struct my_iphdr *)pptrs->iph_ptr)->ip_dst, sizeof(struct in_addr)); result = route_node_match_ipv4(area->route_table[level-1], &pref4); if (result) { pptrs->igp_dst = (char *) &result->p; pptrs->igp_dst_info = (char *) result->info; if (result->p.prefixlen > pptrs->lm_mask_dst) { pptrs->lm_mask_dst = result->p.prefixlen; pptrs->lm_method_dst = NF_NET_IGP; } } } } #if defined ENABLE_IPV6 else if (area && pptrs->l3_proto == ETHERTYPE_IPV6) { if (!pptrs->igp_src) { memcpy(&pref6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_src, sizeof(struct in6_addr)); result = route_node_match_ipv6(area->route_table6[level-1], &pref6); if (result) { pptrs->igp_src = (char *) &result->p; pptrs->igp_src_info = (char *) result->info; if (result->p.prefixlen > pptrs->lm_mask_src) { pptrs->lm_mask_src = result->p.prefixlen; pptrs->lm_method_src = NF_NET_IGP; } } } if (!pptrs->igp_dst) { memcpy(&pref6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_dst, sizeof(struct in6_addr)); result = route_node_match_ipv6(area->route_table6[level-1], &pref6); if (result) { pptrs->igp_dst = (char *) &result->p; pptrs->igp_dst_info = (char *) result->info; if (result->p.prefixlen > pptrs->lm_mask_dst) { pptrs->lm_mask_dst = result->p.prefixlen; pptrs->lm_method_dst = NF_NET_IGP; } } } } #endif } } pmacct-0.14.0/src/isis/iso.h0000640000175000017500000001616711705254624014567 0ustar paolopaolo/* $NetBSD: iso.h,v 1.13 2000/07/28 12:13:34 kleink Exp $ */ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)iso.h 8.1 (Berkeley) 6/10/93 */ /*********************************************************** Copyright IBM Corporation 1987 All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of IBM not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ /* * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison */ #ifndef _NETISO_ISO_H_ #define _NETISO_ISO_H_ #if 0 #include #endif #if 0 #ifndef sa_family_t typedef __sa_family_t sa_family_t; #define sa_family_t __sa_family_t #endif #endif /* * Return true if this is a multicast address * This assumes that the bit transmission is lsb first. This * assumption is valid for 802.3 but not 802.5. There is a * kludge to get around this for 802.5 -- see if_lan.c * where subnetwork header is setup. */ #define IS_MULTICAST(snpa)\ ((snpa)[0] & 0x01) /* * Protocols */ #define ISOPROTO_TCP 6 /* IETF experiment */ #define ISOPROTO_UDP 17 /* IETF experiment */ #define ISOPROTO_TP0 25 /* connection oriented transport protocol */ #define ISOPROTO_TP1 26 /* not implemented */ #define ISOPROTO_TP2 27 /* not implemented */ #define ISOPROTO_TP3 28 /* not implemented */ #define ISOPROTO_TP4 29 /* connection oriented transport protocol */ #define ISOPROTO_TP ISOPROTO_TP4 /* tp-4 with negotiation */ #define ISOPROTO_CLTP 30 /* connectionless transport (not yet impl.) */ #define ISOPROTO_CLNP 31 /* connectionless internetworking protocol */ #define ISOPROTO_X25 32 /* cons */ #define ISOPROTO_INACT_NL 33 /* inactive network layer! */ #define ISOPROTO_ESIS 34 /* ES-IS protocol */ #define ISOPROTO_INTRAISIS 35 /* IS-IS protocol */ #define ISOPROTO_IDRP 36 /* Interdomain Routing Protocol */ #define ISOPROTO_RAW 255 /* raw clnp */ #define ISOPROTO_MAX 256 #define ISO_PORT_RESERVED 1024 #define ISO_PORT_USERRESERVED 5000 /* * Port/socket numbers: standard network functions * NOT PRESENTLY USED */ #define ISO_PORT_MAINT 501 #define ISO_PORT_ECHO 507 #define ISO_PORT_DISCARD 509 #define ISO_PORT_SYSTAT 511 #define ISO_PORT_NETSTAT 515 /* * Port/socket numbers: non-standard application functions */ #define ISO_PORT_LOGIN 513 /* * Port/socket numbers: public use */ #define ISO_PORT_PUBLIC 1024 /* high bit set --> public */ /* * Network layer protocol identifiers */ #define ISO8473_CLNP 0x81 #define ISO9542_ESIS 0x82 #define ISO9542X25_ESIS 0x8a #define ISO10589_ISIS 0x83 #define ISO8878A_CONS 0x84 #define ISO10747_IDRP 0x85 #ifndef IN_CLASSA_NET #include #endif /* IN_CLASSA_NET */ /* * The following looks like a sockaddr to facilitate using tree lookup * routines */ struct iso_addr { u_char isoa_len; /* length (in bytes) */ char isoa_genaddr[20]; /* general opaque address */ }; struct sockaddr_iso { u_char siso_len; /* length */ sa_family_t siso_family; /* family */ u_char siso_plen; /* presentation selector length */ u_char siso_slen; /* session selector length */ u_char siso_tlen; /* transport selector length */ struct iso_addr siso_addr; /* network address */ u_char siso_pad[6]; /* space for gosip v2 sels */ /* makes struct 32 bytes long */ }; #define siso_nlen siso_addr.isoa_len #define siso_data siso_addr.isoa_genaddr #define TSEL(s) ((caddr_t)((s)->siso_data + (s)->siso_nlen)) #define SAME_ISOADDR(a, b) \ (bcmp((a)->siso_data, (b)->siso_data, (unsigned)(a)->siso_nlen)==0) #define SAME_ISOIFADDR(a, b) (bcmp((a)->siso_data, (b)->siso_data, \ (unsigned)((b)->siso_nlen - (b)->siso_tlen)) == 0) /* * The following are specific values for siso->siso_data[0], * otherwise known as the AFI: */ #define AFI_37 0x37 /* bcd of "37" */ #define AFI_OSINET 0x47 /* bcd of "47" */ #define AFI_RFC986 0x47 /* bcd of "47" */ #define AFI_SNA 0x00 /* SubNetwork Address; invalid really... */ #ifdef _KERNEL extern struct domain isodomain; extern struct protosw isosw[]; #define satosiso(sa) ((struct sockaddr_iso *)(sa)) #define sisotosa(siso) ((struct sockaddr *)(siso)) #else /* user utilities definitions from the iso library */ #ifdef SOLARIS #define __P(x) x #define __BEGIN_DECLS #define __END_DECLS #else #include #endif __BEGIN_DECLS struct iso_addr *iso_addr __P((const char *)); char *iso_ntoa __P((const struct iso_addr *)); /* THESE DON'T EXIST YET */ struct hostent *iso_gethostbyname __P((const char *)); struct hostent *iso_gethostbyaddr __P((const char *, int, int)); __END_DECLS #endif /* _KERNEL */ #endif /* _NETISO_ISO_H_ */ pmacct-0.14.0/src/isis/isis_csm.c0000640000175000017500000001216711734671340015575 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_csm.c * IS-IS circuit state machine * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_CSM_C #include "pmacct.h" #include "isis.h" #include "linklist.h" #include "hash.h" #include "prefix.h" #include "dict.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_circuit.h" #include "isis_tlv.h" #include "isis_lsp.h" #include "isis_pdu.h" #include "isis_network.h" #include "isis_misc.h" #include "isis_constants.h" #include "isis_adjacency.h" #include "isis_flags.h" #include "isisd.h" #include "isis_csm.h" #include "isis_events.h" extern struct isis *isis; static const char *csm_statestr[] = { "C_STATE_NA", "C_STATE_INIT", "C_STATE_CONF", "C_STATE_UP" }; #define STATE2STR(S) csm_statestr[S] static const char *csm_eventstr[] = { "NO_STATE", "ISIS_ENABLE", "IF_UP_FROM_Z", "ISIS_DISABLE", "IF_DOWN_FROM_Z", }; #define EVENT2STR(E) csm_eventstr[E] // XXX: isis_circuit_if_add() and isis_circuit_if_del commented out struct isis_circuit * isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg) { int old_state; old_state = circuit ? circuit->state : C_STATE_NA; Log(LOG_DEBUG, "DEBUG (default/core/ISIS ): CSM_EVENT: %s\n", EVENT2STR (event)); switch (old_state) { case C_STATE_NA: if (circuit) Log(LOG_WARNING, "WARN (default/core/ISIS ): Non-null circuit while state C_STATE_NA\n"); switch (event) { case ISIS_ENABLE: circuit = isis_circuit_new (); isis_circuit_configure (circuit, (struct isis_area *) arg); circuit->state = C_STATE_CONF; break; case IF_UP_FROM_Z: circuit = isis_circuit_new (); // isis_circuit_if_add (circuit, (struct interface *) arg); listnode_add (isis->init_circ_list, circuit); circuit->state = C_STATE_INIT; break; case ISIS_DISABLE: Log(LOG_WARNING, "WARN (default/core/ISIS ): circuit already disabled\n"); case IF_DOWN_FROM_Z: Log(LOG_WARNING, "WARN (default/core/ISIS ): circuit already disconnected\n"); break; } break; case C_STATE_INIT: switch (event) { case ISIS_ENABLE: isis_circuit_configure (circuit, (struct isis_area *) arg); isis_circuit_up (circuit); circuit->state = C_STATE_UP; isis_event_circuit_state_change (circuit, 1); listnode_delete (isis->init_circ_list, circuit); break; case IF_UP_FROM_Z: Log(LOG_WARNING, "WARN (default/core/ISIS ): circuit already connected\n"); break; case ISIS_DISABLE: Log(LOG_WARNING, "WARN (default/core/ISIS ): circuit already disabled\n"); break; case IF_DOWN_FROM_Z: // isis_circuit_if_del (circuit); listnode_delete (isis->init_circ_list, circuit); isis_circuit_del (circuit); circuit = NULL; break; } break; case C_STATE_CONF: switch (event) { case ISIS_ENABLE: Log(LOG_WARNING, "WARN (default/core/ISIS ): circuit already enabled\n"); break; case IF_UP_FROM_Z: // isis_circuit_if_add (circuit, (struct interface *) arg); isis_circuit_up (circuit); circuit->state = C_STATE_UP; isis_event_circuit_state_change (circuit, 1); break; case ISIS_DISABLE: isis_circuit_deconfigure (circuit, (struct isis_area *) arg); isis_circuit_del (circuit); circuit = NULL; break; case IF_DOWN_FROM_Z: Log(LOG_WARNING, "WARN (default/core/ISIS ): circuit already disconnected\n"); break; } break; case C_STATE_UP: switch (event) { case ISIS_ENABLE: Log(LOG_WARNING, "WARN (default/core/ISIS ): circuit already configured\n"); break; case IF_UP_FROM_Z: Log(LOG_WARNING, "WARN (default/core/ISIS ): circuit already connected\n"); break; case ISIS_DISABLE: isis_circuit_deconfigure (circuit, (struct isis_area *) arg); listnode_add (isis->init_circ_list, circuit); circuit->state = C_STATE_INIT; isis_event_circuit_state_change (circuit, 0); break; case IF_DOWN_FROM_Z: // isis_circuit_if_del (circuit); circuit->state = C_STATE_CONF; isis_event_circuit_state_change (circuit, 0); break; } break; default: Log(LOG_WARNING, "WARN (default/core/ISIS ): Invalid circuit state %d\n", old_state); } Log(LOG_DEBUG, "DEBUG (default/core/ISIS ): CSM_STATE_CHANGE: %s -> %s \n", STATE2STR (old_state), circuit ? STATE2STR (circuit->state) : STATE2STR (C_STATE_NA)); return circuit; } pmacct-0.14.0/src/isis/iso_checksum.c0000640000175000017500000000373411705253204016431 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - iso_checksum.c * ISO checksum related routines * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISO_CHECKSUM_C #include "pmacct.h" #include "isis.h" #include "iso_checksum.h" /* * Calculations of the OSI checksum. * ISO/IEC 8473 defines the sum as * * L * sum a (mod 255) = 0 * 1 i * * L * sum (L-i+1)a (mod 255) = 0 * 1 i * */ /* * Verifies that the checksum is correct. * Return 0 on correct and 1 on invalid checksum. * Based on Annex C.4 of ISO/IEC 8473 */ int iso_csum_verify (u_char * buffer, int len, uint16_t * csum) { u_int16_t checksum; u_int32_t c0; u_int32_t c1; c0 = *csum & 0xff00; c1 = *csum & 0x00ff; /* * If both are zero return correct */ if (c0 == 0 && c1 == 0) return 0; /* * If either, but not both are zero return incorrect */ if (c0 == 0 || c1 == 0) return 1; /* Offset of checksum from the start of the buffer */ int offset = (u_char *) csum - buffer; checksum = fletcher_checksum(buffer, len, offset); if (checksum == *csum) return 0; return 1; } pmacct-0.14.0/src/isis/isis_misc.c0000640000175000017500000002226311705253204015735 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_misc.c * Miscellanous routines * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_MISC_C #include "pmacct.h" #include "isis.h" #include "hash.h" #include "dict.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_circuit.h" #include "isisd.h" #include "isis_misc.h" #include "isis_tlv.h" #include "isis_lsp.h" #include "isis_constants.h" #include "isis_adjacency.h" /* staticly assigned vars for printing purposes */ struct in_addr new_prefix; /* len of xxxx.xxxx.xxxx + place for #0 termination */ char sysid[15]; /* len of xxxx.xxxx.xxxx + place for #0 termination */ char snpa[15]; /* len of xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx */ char isonet[51]; /* + place for #0 termination */ /* len of xxxx.xxxx.xxxx.xx.xx + place for #0 termination */ char lspid[21]; /* len of xxYxxMxWxdxxhxxmxxs + place for #0 termination */ char datestring[20]; char nlpidstring[30]; /* * This converts the isonet to its printable format */ const char * isonet_print (u_char * from, int len) { int i = 0; char *pos = isonet; if (!from) return "unknown"; while (i < len) { if (i & 1) { sprintf (pos, "%02x", *(from + i)); pos += 2; } else { if (i == (len - 1)) { /* No dot at the end of address */ sprintf (pos, "%02x", *(from + i)); pos += 2; } else { sprintf (pos, "%02x.", *(from + i)); pos += 3; } } i++; } *(pos) = '\0'; return isonet; } /* * Returns 0 on error, length of buff on ok * extract dot from the dotted str, and insert all the number in a buff */ int dotformat2buff (u_char * buff, const u_char * dotted) { int dotlen, len = 0; const u_char *pos = dotted; u_char number[3]; int nextdotpos = 2; number[2] = '\0'; dotlen = strlen(dotted); if (dotlen > 50) { /* this can't be an iso net, its too long */ return 0; } while ((pos - dotted) < dotlen && len < 20) { if (*pos == '.') { /* we expect the . at 2, and than every 5 */ if ((pos - dotted) != nextdotpos) { len = 0; break; } nextdotpos += 5; pos++; continue; } /* we must have at least two chars left here */ if (dotlen - (pos - dotted) < 2) { len = 0; break; } if ((isxdigit ((int) *pos)) && (isxdigit ((int) *(pos + 1)))) { memcpy (number, pos, 2); pos += 2; } else { len = 0; break; } *(buff + len) = (char) strtol ((char *)number, NULL, 16); len++; } return len; } /* * conversion of XXXX.XXXX.XXXX to memory */ int sysid2buff (u_char * buff, const u_char * dotted) { int len = 0; const u_char *pos = dotted; u_char number[3]; number[2] = '\0'; // surely not a sysid_string if not 14 length if (strlen (dotted) != 14) { return 0; } while (len < ISIS_SYS_ID_LEN) { if (*pos == '.') { /* the . is not positioned correctly */ if (((pos - dotted) != 4) && ((pos - dotted) != 9)) { len = 0; break; } pos++; continue; } if ((isxdigit ((int) *pos)) && (isxdigit ((int) *(pos + 1)))) { memcpy (number, pos, 2); pos += 2; } else { len = 0; break; } *(buff + len) = (char) strtol ((char *)number, NULL, 16); len++; } return len; } /* * converts the nlpids struct (filled by TLV #129) * into a string */ char * nlpid2string (struct nlpids *nlpids) { char *pos = nlpidstring; int i; for (i = 0; i < nlpids->count; i++) { switch (nlpids->nlpids[i]) { case NLPID_IP: pos += sprintf (pos, "IPv4"); break; case NLPID_IPV6: pos += sprintf (pos, "IPv6"); break; case NLPID_SNAP: pos += sprintf (pos, "SNAP"); break; case NLPID_CLNP: pos += sprintf (pos, "CLNP"); break; case NLPID_ESIS: pos += sprintf (pos, "ES-IS"); break; default: pos += sprintf (pos, "unknown"); break; } if (nlpids->count - i > 1) pos += sprintf (pos, ", "); } *(pos) = '\0'; return nlpidstring; } /* * supports the given af ? */ int speaks (struct nlpids *nlpids, int family) { int i, speaks = 0; if (nlpids == (struct nlpids *) NULL) return speaks; for (i = 0; i < nlpids->count; i++) { if (family == AF_INET && nlpids->nlpids[i] == NLPID_IP) speaks = 1; if (family == AF_INET6 && nlpids->nlpids[i] == NLPID_IPV6) speaks = 1; } return speaks; } /* * Returns 0 on error, IS-IS Circuit Type on ok */ int string2circuit_t (const u_char * str) { if (!str) return 0; if (!strcmp (str, "level-1")) return IS_LEVEL_1; if (!strcmp (str, "level-2-only") || !strcmp (str, "level-2")) return IS_LEVEL_2; if (!strcmp (str, "level-1-2")) return IS_LEVEL_1_AND_2; return 0; } const char * circuit_t2string (int circuit_t) { switch (circuit_t) { case IS_LEVEL_1: return "L1"; case IS_LEVEL_2: return "L2"; case IS_LEVEL_1_AND_2: return "L1L2"; default: return "??"; } return NULL; /* not reached */ } const char * syst2string (int type) { switch (type) { case ISIS_SYSTYPE_ES: return "ES"; case ISIS_SYSTYPE_IS: return "IS"; case ISIS_SYSTYPE_L1_IS: return "1"; case ISIS_SYSTYPE_L2_IS: return "2"; default: return "??"; } return NULL; /* not reached */ } /* * Print functions - we print to static vars */ const char * snpa_print (u_char * from) { int i = 0; u_char *pos = (u_char *)snpa; if (!from) return "unknown"; while (i < ETH_ALEN - 1) { if (i & 1) { sprintf ((char *)pos, "%02x.", *(from + i)); pos += 3; } else { sprintf ((char *)pos, "%02x", *(from + i)); pos += 2; } i++; } sprintf ((char *)pos, "%02x", *(from + (ISIS_SYS_ID_LEN - 1))); pos += 2; *(pos) = '\0'; return snpa; } const char * sysid_print (u_char * from) { int i = 0; char *pos = sysid; if (!from) return "unknown"; while (i < ISIS_SYS_ID_LEN - 1) { if (i & 1) { sprintf (pos, "%02x.", *(from + i)); pos += 3; } else { sprintf (pos, "%02x", *(from + i)); pos += 2; } i++; } sprintf (pos, "%02x", *(from + (ISIS_SYS_ID_LEN - 1))); pos += 2; *(pos) = '\0'; return sysid; } const char * rawlspid_print (u_char * from) { char *pos = lspid; if (!from) return "unknown"; memcpy (pos, sysid_print (from), 15); pos += 14; sprintf (pos, ".%02x", LSP_PSEUDO_ID (from)); pos += 3; sprintf (pos, "-%02x", LSP_FRAGMENT (from)); pos += 3; *(pos) = '\0'; return lspid; } const char * time2string (u_int32_t time) { char *pos = datestring; u_int32_t rest; if (time == 0) return "-"; if (time / SECS_PER_YEAR) pos += sprintf (pos, "%uY", time / SECS_PER_YEAR); rest = time % SECS_PER_YEAR; if (rest / SECS_PER_MONTH) pos += sprintf (pos, "%uM", rest / SECS_PER_MONTH); rest = rest % SECS_PER_MONTH; if (rest / SECS_PER_WEEK) pos += sprintf (pos, "%uw", rest / SECS_PER_WEEK); rest = rest % SECS_PER_WEEK; if (rest / SECS_PER_DAY) pos += sprintf (pos, "%ud", rest / SECS_PER_DAY); rest = rest % SECS_PER_DAY; if (rest / SECS_PER_HOUR) pos += sprintf (pos, "%uh", rest / SECS_PER_HOUR); rest = rest % SECS_PER_HOUR; if (rest / SECS_PER_MINUTE) pos += sprintf (pos, "%um", rest / SECS_PER_MINUTE); rest = rest % SECS_PER_MINUTE; if (rest) pos += sprintf (pos, "%us", rest); *(pos) = 0; return datestring; } /* * routine to decrement a timer by a random * number * * first argument is the timer and the second is * the jitter */ unsigned long isis_jitter (unsigned long timer, unsigned long jitter) { int j, k; if (jitter >= 100) return timer; if (timer == 1) return timer; /* * randomizing just the percent value provides * no good random numbers - hence the spread * to RANDOM_SPREAD (100000), which is ok as * most IS-IS timers are no longer than 16 bit */ j = 1 + (int) ((RANDOM_SPREAD * rand ()) / (RAND_MAX + 1.0)); k = timer - (timer * (100 - jitter)) / 100; timer = timer - (k * j / RANDOM_SPREAD); return timer; } struct in_addr newprefix2inaddr (u_char * prefix_start, u_char prefix_masklen) { memset (&new_prefix, 0, sizeof (new_prefix)); memcpy (&new_prefix, prefix_start, (prefix_masklen & 0x3F) ? ((((prefix_masklen & 0x3F) - 1) >> 3) + 1) : 0); return new_prefix; } pmacct-0.14.0/src/isis/thread.h0000640000175000017500000001471411705253204015231 0ustar paolopaolo/* Thread management routine header. * Copyright (C) 1998 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef _THREAD_H_ #define _THREAD_H_ struct rusage_t { struct timeval real; }; #define RUSAGE_T struct rusage_t #define GETRUSAGE(X) thread_getrusage(X) /* Linked list of thread. */ struct thread_list { struct thread *head; struct thread *tail; int count; }; /* Master of the theads. */ struct thread_master { struct thread_list read; struct thread_list write; struct thread_list timer; struct thread_list event; struct thread_list ready; struct thread_list unuse; struct thread_list background; fd_set readfd; fd_set writefd; fd_set exceptfd; unsigned long alloc; }; typedef unsigned char thread_type; /* Thread itself. */ struct thread { thread_type type; /* thread type */ thread_type add_type; /* thread type */ struct thread *next; /* next pointer of the thread */ struct thread *prev; /* previous pointer of the thread */ struct thread_master *master; /* pointer to the struct thread_master. */ int (*func) (struct thread *); /* event function */ void *arg; /* event argument */ union { int val; /* second argument of the event. */ int fd; /* file descriptor in case of read/write. */ struct timeval sands; /* rest of time sands value. */ } u; RUSAGE_T ru; /* Indepth usage info. */ struct cpu_thread_history *hist; /* cache pointer to cpu_history */ char* funcname; }; struct cpu_thread_history { int (*func)(struct thread *); char *funcname; unsigned int total_calls; struct time_stats { unsigned long total, max; } real; thread_type types; }; /* Clocks supported by Quagga */ enum quagga_clkid { QUAGGA_CLK_REALTIME = 0, /* ala gettimeofday() */ QUAGGA_CLK_MONOTONIC, /* monotonic, against an indeterminate base */ QUAGGA_CLK_REALTIME_STABILISED, /* like realtime, but non-decrementing */ }; /* Thread types. */ #define THREAD_READ 0 #define THREAD_WRITE 1 #define THREAD_TIMER 2 #define THREAD_EVENT 3 #define THREAD_READY 4 #define THREAD_BACKGROUND 5 #define THREAD_UNUSED 6 #define THREAD_EXECUTE 7 /* Thread yield time. */ #define THREAD_YIELD_TIME_SLOT 10 * 1000L /* 10ms */ /* Macros. */ #define THREAD_ARG(X) ((X)->arg) #define THREAD_FD(X) ((X)->u.fd) #define THREAD_VAL(X) ((X)->u.val) #define THREAD_READ_ON(master,thread,func,arg,sock) \ do { \ if (! thread) \ thread = thread_add_read (master, func, arg, sock); \ } while (0) #define THREAD_WRITE_ON(master,thread,func,arg,sock) \ do { \ if (! thread) \ thread = thread_add_write (master, func, arg, sock); \ } while (0) #define THREAD_TIMER_ON(master,thread,func,arg,time) \ do { \ if (! thread) \ thread = thread_add_timer (master, func, arg, time); \ } while (0) #define THREAD_OFF(thread) \ do { \ if (thread) \ { \ thread_cancel (thread); \ thread = NULL; \ } \ } while (0) #define THREAD_READ_OFF(thread) THREAD_OFF(thread) #define THREAD_WRITE_OFF(thread) THREAD_OFF(thread) #define THREAD_TIMER_OFF(thread) THREAD_OFF(thread) #define thread_add_read(m,f,a,v) funcname_thread_add_read(m,f,a,v,#f) #define thread_add_write(m,f,a,v) funcname_thread_add_write(m,f,a,v,#f) #define thread_add_timer(m,f,a,v) funcname_thread_add_timer(m,f,a,v,#f) #define thread_add_timer_msec(m,f,a,v) funcname_thread_add_timer_msec(m,f,a,v,#f) #define thread_add_event(m,f,a,v) funcname_thread_add_event(m,f,a,v,#f) #define thread_execute(m,f,a,v) funcname_thread_execute(m,f,a,v,#f) /* The 4th arg to thread_add_background is the # of milliseconds to delay. */ #define thread_add_background(m,f,a,v) funcname_thread_add_background(m,f,a,v,#f) /* Prototypes. */ #if (!defined __THREAD_C) #define EXT extern #else #define EXT #endif EXT struct thread_master *thread_master_create (void); EXT void thread_master_free (struct thread_master *); EXT struct thread *funcname_thread_add_read (struct thread_master *, int (*)(struct thread *), void *, int, const char *); EXT struct thread *funcname_thread_add_write (struct thread_master *, int (*)(struct thread *), void *, int, const char *); EXT struct thread *funcname_thread_add_timer (struct thread_master *, int (*)(struct thread *), void *, long, const char *); EXT struct thread *funcname_thread_add_timer_msec (struct thread_master *, int (*)(struct thread *), void *, long, const char *); EXT struct thread *funcname_thread_add_event (struct thread_master *, int (*)(struct thread *), void *, int, const char *); EXT struct thread *funcname_thread_add_background (struct thread_master *, int (*func)(struct thread *), void *arg, long, const char *); EXT struct thread *funcname_thread_execute (struct thread_master *, int (*)(struct thread *), void *, int, const char *); EXT void thread_cancel (struct thread *); EXT unsigned int thread_cancel_event (struct thread_master *, void *); EXT struct thread *thread_fetch (struct thread_master *, struct thread *); EXT void thread_call (struct thread *); EXT unsigned long thread_timer_remain_second (struct thread *); EXT int thread_should_yield (struct thread *); EXT void thread_getrusage (RUSAGE_T *); EXT int quagga_gettime (enum quagga_clkid, struct timeval *); EXT time_t quagga_time (time_t *); EXT unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, unsigned long *cpu_time_elapsed); /* Global variable containing a recent result from gettimeofday. This can be used instead of calling gettimeofday if a recent value is sufficient. This is guaranteed to be refreshed before a thread is called. */ EXT struct timeval recent_time; /* Similar to recent_time, but a monotonically increasing time value */ EXT struct timeval recent_relative_time (void); #undef EXT #endif /* _THREAD_H_ */ pmacct-0.14.0/src/isis/isis_pdu.c0000640000175000017500000015007211734713264015603 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_pdu.c * PDU processing * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_PDU_C #include "pmacct.h" #include "isis.h" #include "linklist.h" #include "stream.h" #include "hash.h" #include "prefix.h" #include "dict.h" #include "thread.h" #include "iso.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_adjacency.h" #include "isis_circuit.h" #include "isis_network.h" #include "isis_misc.h" #include "isis_flags.h" #include "isis_tlv.h" #include "isisd.h" #include "isis_dynhn.h" #include "isis_lsp.h" #include "isis_pdu.h" #include "iso_checksum.h" #include "isis_csm.h" #include "isis_events.h" extern struct thread_master *master; extern struct isis *isis; #define ISIS_MINIMUM_FIXED_HDR_LEN 15 #define ISIS_MIN_PDU_LEN 13 /* partial seqnum pdu with id_len=2 */ #ifndef PNBBY #define PNBBY 8 #endif /* PNBBY */ /* Utility mask array. */ static const u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 }; u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 }; u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 }; u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 }; /* * HELPER FUNCS */ /* * Compares two sets of area addresses */ static int area_match (struct list *left, struct list *right) { struct area_addr *addr1, *addr2; struct listnode *node1, *node2; if (!left || !right) return 0; /* mismatch */ for (ALL_LIST_ELEMENTS_RO (left, node1, addr1)) { for (ALL_LIST_ELEMENTS_RO (right, node2, addr2)) { if (addr1->addr_len == addr2->addr_len && !memcmp (addr1->area_addr, addr2->area_addr, (int) addr1->addr_len)) return 1; /* match */ } } return 0; /* mismatch */ } /* * Check if ip2 is in the ip1's network (function like Prefix.h:prefix_match() ) * param ip1 the IS interface ip address structure * param ip2 the IIH's ip address * return 0 the IIH's IP is not in the IS's subnetwork * 1 the IIH's IP is in the IS's subnetwork */ static int ip_same_subnet (struct prefix_ipv4 *ip1, struct in_addr *ip2) { u_char *addr1, *addr2; int shift, offset, offsetloop; int len; addr1 = (u_char *) & ip1->prefix.s_addr; addr2 = (u_char *) & ip2->s_addr; len = ip1->prefixlen; shift = len % PNBBY; offsetloop = offset = len / PNBBY; while (offsetloop--) if (addr1[offsetloop] != addr2[offsetloop]) return 0; if (shift) if (maskbit[shift] & (addr1[offset] ^ addr2[offset])) return 0; return 1; /* match */ } /* * Compares two set of ip addresses * param left the local interface's ip addresses * param right the iih interface's ip address * return 0 no match; * 1 match; */ static int ip_match (struct list *left, struct list *right) { struct prefix_ipv4 *ip1; struct in_addr *ip2; struct listnode *node1, *node2; if ((left == NULL) || (right == NULL)) return 0; for (ALL_LIST_ELEMENTS_RO (left, node1, ip1)) { for (ALL_LIST_ELEMENTS_RO (right, node2, ip2)) { if (ip_same_subnet (ip1, ip2)) { return 1; /* match */ } } } return 0; } /* * Checks whether we should accept a PDU of given level */ static int accept_level (int level, int circuit_t) { int retval = ((circuit_t & level) == level); /* simple approach */ return retval; } int authentication_check (struct isis_passwd *one, struct isis_passwd *theother) { if (one->type != theother->type) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Unsupported authentication type %d\n", theother->type); return 1; /* Auth fail (different authentication types) */ } switch (one->type) { case ISIS_PASSWD_TYPE_CLEARTXT: if (one->len != theother->len) return 1; /* Auth fail () - passwd len mismatch */ return memcmp (one->passwd, theother->passwd, one->len); break; default: Log(LOG_WARNING, "WARN ( default/core/ISIS ): Unsupported authentication type\n"); break; } return 0; /* Auth pass */ } /* * Processing helper functions */ static void tlvs_to_adj_nlpids (struct tlvs *tlvs, struct isis_adjacency *adj) { int i; struct nlpids *tlv_nlpids; if (tlvs->nlpids) { tlv_nlpids = tlvs->nlpids; adj->nlpids.count = tlv_nlpids->count; for (i = 0; i < tlv_nlpids->count; i++) { adj->nlpids.nlpids[i] = tlv_nlpids->nlpids[i]; } } } static void del_ip_addr (void *val) { free(val); } static void tlvs_to_adj_ipv4_addrs (struct tlvs *tlvs, struct isis_adjacency *adj) { struct listnode *node; struct in_addr *ipv4_addr, *malloced; if (adj->ipv4_addrs) { adj->ipv4_addrs->del = del_ip_addr; list_delete (adj->ipv4_addrs); } adj->ipv4_addrs = list_new (); if (tlvs->ipv4_addrs) { for (ALL_LIST_ELEMENTS_RO (tlvs->ipv4_addrs, node, ipv4_addr)) { malloced = calloc(1, sizeof (struct in_addr)); memcpy (malloced, ipv4_addr, sizeof (struct in_addr)); listnode_add (adj->ipv4_addrs, malloced); } } } #ifdef ENABLE_IPV6 static void tlvs_to_adj_ipv6_addrs (struct tlvs *tlvs, struct isis_adjacency *adj) { struct listnode *node; struct in6_addr *ipv6_addr, *malloced; if (adj->ipv6_addrs) { adj->ipv6_addrs->del = del_ip_addr; list_delete (adj->ipv6_addrs); } adj->ipv6_addrs = list_new (); if (tlvs->ipv6_addrs) { for (ALL_LIST_ELEMENTS_RO (tlvs->ipv6_addrs, node, ipv6_addr)) { malloced = calloc(1, sizeof (struct in6_addr)); memcpy (malloced, ipv6_addr, sizeof (struct in6_addr)); listnode_add (adj->ipv6_addrs, malloced); } } } #endif /* ENABLE_IPV6 */ /* * RECEIVE SIDE */ /* * Process P2P IIH * ISO - 10589 * Section 8.2.5 - Receiving point-to-point IIH PDUs * */ static int process_p2p_hello (struct isis_circuit *circuit) { int retval = ISIS_OK; struct isis_p2p_hello_hdr *hdr; struct isis_adjacency *adj; u_int32_t expected = 0, found; struct tlvs tlvs; if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_P2PHELLO_HDRLEN) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Packet too short\n"); return ISIS_WARNING; } /* 8.2.5.1 PDU acceptance tests */ /* 8.2.5.1 a) external domain untrue */ /* FIXME: not useful at all? */ /* 8.2.5.1 b) ID Length mismatch */ /* checked at the handle_pdu */ /* 8.2.5.2 IIH PDU Processing */ /* 8.2.5.2 a) 1) Maximum Area Addresses */ /* Already checked, and can also be ommited */ /* * Get the header */ hdr = (struct isis_p2p_hello_hdr *) STREAM_PNT (circuit->rcv_stream); circuit->rcv_stream->getp += ISIS_P2PHELLO_HDRLEN; /* hdr.circuit_t = stream_getc (stream); stream_get (hdr.source_id, stream, ISIS_SYS_ID_LEN); hdr.hold_time = stream_getw (stream); hdr.pdu_len = stream_getw (stream); hdr.local_id = stream_getc (stream); */ /* * My interpertation of the ISO, if no adj exists we will create one for * the circuit */ adj = circuit->u.p2p.neighbor; if (!adj) { adj = isis_new_adj (hdr->source_id, NULL, 0, circuit); if (adj == NULL) return ISIS_ERROR; circuit->u.p2p.neighbor = adj; isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); adj->sys_type = ISIS_SYSTYPE_UNKNOWN; } /* 8.2.6 Monitoring point-to-point adjacencies */ adj->hold_time = ntohs (hdr->hold_time); adj->last_upd = time (NULL); /* * Lets get the TLVS now */ expected |= TLVFLAG_AREA_ADDRS; expected |= TLVFLAG_AUTH_INFO; expected |= TLVFLAG_NLPID; expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV6_ADDR; retval = parse_tlvs (circuit->area->area_tag, STREAM_PNT (circuit->rcv_stream), ntohs (hdr->pdu_len) - ISIS_P2PHELLO_HDRLEN - ISIS_FIXED_HDR_LEN, &expected, &found, &tlvs); if (retval > ISIS_WARNING) { free_tlvs (&tlvs); return retval; }; /* 8.2.5.1 c) Authentication */ if (circuit->passwd.type) { if (!(found & TLVFLAG_AUTH_INFO) || authentication_check (&circuit->passwd, &tlvs.auth_info)) { isis_event_auth_failure (circuit->area->area_tag, "P2P hello authentication failure", hdr->source_id); return ISIS_OK; } } /* we do this now because the adj may not survive till the end... */ /* which protocol are spoken ??? */ if (found & TLVFLAG_NLPID) tlvs_to_adj_nlpids (&tlvs, adj); /* we need to copy addresses to the adj */ if (found & TLVFLAG_IPV4_ADDR) tlvs_to_adj_ipv4_addrs (&tlvs, adj); #ifdef ENABLE_IPV6 if (found & TLVFLAG_IPV6_ADDR) tlvs_to_adj_ipv6_addrs (&tlvs, adj); #endif /* ENABLE_IPV6 */ /* lets take care of the expiry */ adj->expire.tv_sec = isis_now.tv_sec + adj->hold_time; adj->expire.tv_usec = isis_now.tv_usec; /* 8.2.5.2 a) a match was detected */ if (area_match (circuit->area->area_addrs, tlvs.area_addrs)) { /* 8.2.5.2 a) 2) If the system is L1 - table 5 */ if (circuit->area->is_type == IS_LEVEL_1) { switch (hdr->circuit_t) { case IS_LEVEL_1: case IS_LEVEL_1_AND_2: if (adj->adj_state != ISIS_ADJ_UP) { /* (4) adj state up */ isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); /* (5) adj usage level 1 */ adj->adj_usage = ISIS_ADJ_LEVEL1; } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { ; /* accept */ } break; case IS_LEVEL_2: if (adj->adj_state != ISIS_ADJ_UP) { /* (7) reject - wrong system type event */ Log(LOG_WARNING, "WARN ( default/core/ISIS ): wrongSystemType\n"); return ISIS_WARNING; /* Reject */ } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { /* (6) down - wrong system */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); } break; } } /* 8.2.5.2 a) 3) If the system is L1L2 - table 6 */ if (circuit->area->is_type == IS_LEVEL_1_AND_2) { switch (hdr->circuit_t) { case IS_LEVEL_1: if (adj->adj_state != ISIS_ADJ_UP) { /* (6) adj state up */ isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); /* (7) adj usage level 1 */ adj->adj_usage = ISIS_ADJ_LEVEL1; } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { ; /* accept */ } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || (adj->adj_usage == ISIS_ADJ_LEVEL2)) { /* (8) down - wrong system */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); } break; case IS_LEVEL_2: if (adj->adj_state != ISIS_ADJ_UP) { /* (6) adj state up */ isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); /* (9) adj usage level 2 */ adj->adj_usage = ISIS_ADJ_LEVEL2; } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) || (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)) { /* (8) down - wrong system */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); } else if (adj->adj_usage == ISIS_ADJ_LEVEL2) { ; /* Accept */ } break; case IS_LEVEL_1_AND_2: if (adj->adj_state != ISIS_ADJ_UP) { /* (6) adj state up */ isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); /* (10) adj usage level 1 */ adj->adj_usage = ISIS_ADJ_LEVEL1AND2; } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) || (adj->adj_usage == ISIS_ADJ_LEVEL2)) { /* (8) down - wrong system */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); } else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2) { ; /* Accept */ } break; } } /* 8.2.5.2 a) 4) If the system is L2 - table 7 */ if (circuit->area->is_type == IS_LEVEL_2) { switch (hdr->circuit_t) { case IS_LEVEL_1: if (adj->adj_state != ISIS_ADJ_UP) { /* (5) reject - wrong system type event */ Log(LOG_WARNING, "WARN ( default/core/ISIS ): wrongSystemType\n"); return ISIS_WARNING; /* Reject */ } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || (adj->adj_usage == ISIS_ADJ_LEVEL2)) { /* (6) down - wrong system */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); } break; case IS_LEVEL_1_AND_2: case IS_LEVEL_2: if (adj->adj_state != ISIS_ADJ_UP) { /* (7) adj state up */ isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); /* (8) adj usage level 2 */ adj->adj_usage = ISIS_ADJ_LEVEL2; } else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2) { /* (6) down - wrong system */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); } else if (adj->adj_usage == ISIS_ADJ_LEVEL2) { ; /* Accept */ } break; } } } /* 8.2.5.2 b) if no match was detected */ else { if (circuit->area->is_type == IS_LEVEL_1) { /* 8.2.5.2 b) 1) is_type L1 and adj is not up */ if (adj->adj_state != ISIS_ADJ_UP) { isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch"); /* 8.2.5.2 b) 2)is_type L1 and adj is up */ } else { isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Down - Area Mismatch"); } } /* 8.2.5.2 b 3 If the system is L2 or L1L2 - table 8 */ else { switch (hdr->circuit_t) { case IS_LEVEL_1: if (adj->adj_state != ISIS_ADJ_UP) { /* (6) reject - Area Mismatch event */ Log(LOG_WARNING, "WARN ( default/core/ISIS ): AreaMismatch\n"); return ISIS_WARNING; /* Reject */ } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { /* (7) down - area mismatch */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch"); } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || (adj->adj_usage == ISIS_ADJ_LEVEL2)) { /* (7) down - wrong system */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); } break; case IS_LEVEL_1_AND_2: case IS_LEVEL_2: if (adj->adj_state != ISIS_ADJ_UP) { /* (8) adj state up */ isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); /* (9) adj usage level 2 */ adj->adj_usage = ISIS_ADJ_LEVEL2; } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { /* (7) down - wrong system */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); } else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2) { if (hdr->circuit_t == IS_LEVEL_2) { /* (7) down - wrong system */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); } else { /* (7) down - area mismatch */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch"); } } else if (adj->adj_usage == ISIS_ADJ_LEVEL2) { ; /* Accept */ } break; } } } /* 8.2.5.2 c) if the action was up - comparing circuit IDs */ /* FIXME - Missing parts */ /* some of my own understanding of the ISO, why the heck does * it not say what should I change the system_type to... */ switch (adj->adj_usage) { case ISIS_ADJ_LEVEL1: adj->sys_type = ISIS_SYSTYPE_L1_IS; break; case ISIS_ADJ_LEVEL2: adj->sys_type = ISIS_SYSTYPE_L2_IS; break; case ISIS_ADJ_LEVEL1AND2: adj->sys_type = ISIS_SYSTYPE_L2_IS; break; case ISIS_ADJ_NONE: adj->sys_type = ISIS_SYSTYPE_UNKNOWN; break; } adj->circuit_t = hdr->circuit_t; adj->level = hdr->circuit_t; free_tlvs (&tlvs); return retval; } /* * Process Level 1/2 Link State * ISO - 10589 * Section 7.3.15.1 - Action on receipt of a link state PDU */ static int process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) { struct isis_link_state_hdr *hdr; struct isis_adjacency *adj = NULL; struct isis_lsp *lsp, *lsp0 = NULL; int retval = ISIS_OK, comp = 0; u_char lspid[ISIS_SYS_ID_LEN + 2]; struct isis_passwd *passwd; /* Sanity check - FIXME: move to correct place */ if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_LSP_HDR_LEN) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Packet too short\n"); return ISIS_WARNING; } /* Reference the header */ hdr = (struct isis_link_state_hdr *) STREAM_PNT (circuit->rcv_stream); assert (ntohs (hdr->pdu_len) > ISIS_LSP_HDR_LEN); /* Checksum sanity check - FIXME: move to correct place */ /* 12 = sysid+pdu+remtime */ if (iso_csum_verify (STREAM_PNT (circuit->rcv_stream) + 4, ntohs (hdr->pdu_len) - 12, &hdr->checksum)) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04x\n", circuit->area->area_tag, rawlspid_print (hdr->lsp_id), ntohs (hdr->checksum)); return ISIS_WARNING; } /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */ if (circuit->ext_domain) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Upd (%s): LSP %s received at level %d over circuit with externalDomain = true\n", circuit->area->area_tag, rawlspid_print (hdr->lsp_id), level); return ISIS_WARNING; } /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */ if (!accept_level (level, circuit->circuit_is_type)) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Upd (%s): LSP %s received at level %d over circuit of type %s\n", circuit->area->area_tag, rawlspid_print (hdr->lsp_id), level, circuit_t2string (circuit->circuit_is_type)); return ISIS_WARNING; } /* 7.3.15.1 a) 4 - need to make sure IDLength matches */ /* 7.3.15.1 a) 5 - maximum area match, can be ommited since we only use 3 */ /* 7.3.15.1 a) 7 - password check */ (level == ISIS_LEVEL1) ? (passwd = &circuit->area->area_passwd) : (passwd = &circuit->area->domain_passwd); if (passwd->type) { if (isis_lsp_authinfo_check (circuit->rcv_stream, circuit->area, ntohs (hdr->pdu_len), passwd)) { isis_event_auth_failure (circuit->area->area_tag, "LSP authentication failure", hdr->lsp_id); return ISIS_WARNING; } } /* Find the LSP in our database and compare it to this Link State header */ lsp = lsp_search (hdr->lsp_id, circuit->area->lspdb[level - 1]); if (lsp) comp = lsp_compare (circuit->area->area_tag, lsp, hdr->seq_num, hdr->checksum, hdr->rem_lifetime); if (lsp && (lsp->own_lsp )) goto dontcheckadj; /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same level */ /* for non broadcast, we just need to find same level adj */ if (circuit->circ_type == CIRCUIT_T_P2P) { /* If no adj, or no sharing of level */ if (!circuit->u.p2p.neighbor) { return ISIS_OK; /* Silently discard */ } else { if (((level == 1) && (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL2)) || ((level == 2) && (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL1))) return ISIS_WARNING; /* Silently discard */ else { adj = circuit->u.p2p.neighbor; } } } dontcheckadj: /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented */ /* 7.3.15.1 a) 8 - Passwords for level 2 - not implemented */ /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented FIXME: do it */ /* 7.3.15.1 b) - If the remaining life time is 0, we perform 7.3.16.4 */ if (hdr->rem_lifetime == 0) { if (!lsp) { /* 7.3.16.4 a) 1) No LSP in db -> send an ack, but don't save */ /* only needed on explicit update, eg - p2p */ if (circuit->circ_type == CIRCUIT_T_P2P) ack_lsp (hdr, circuit, level); return retval; /* FIXME: do we need a purge? */ } else { if (memcmp (hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) { /* LSP by some other system -> do 7.3.16.4 b) */ /* 7.3.16.4 b) 1) */ if (comp == LSP_NEWER) { lsp_update (lsp, hdr, circuit->rcv_stream, circuit->area, level); /* ii */ ISIS_FLAGS_SET_ALL (lsp->SRMflags); /* iii */ ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); /* v */ ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags); /* FIXME: OTHER than c */ /* iv */ if (circuit->circ_type != CIRCUIT_T_BROADCAST) ISIS_SET_FLAG (lsp->SSNflags, circuit); } /* 7.3.16.4 b) 2) */ else if (comp == LSP_EQUAL) { /* i */ ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); /* ii */ if (circuit->circ_type != CIRCUIT_T_BROADCAST) ISIS_SET_FLAG (lsp->SSNflags, circuit); } /* 7.3.16.4 b) 3) */ else { ISIS_SET_FLAG (lsp->SRMflags, circuit); ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); } } else { /* our own LSP -> 7.3.16.4 c) */ if (LSP_PSEUDO_ID (lsp->lsp_header->lsp_id) != circuit->circuit_id || (LSP_PSEUDO_ID (lsp->lsp_header->lsp_id) == circuit->circuit_id && circuit->u.bc.is_dr[level - 1] == 1)) { lsp->lsp_header->seq_num = htonl (ntohl (hdr->seq_num) + 1); Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): LSP LEN: %d\n", ntohs (lsp->lsp_header->pdu_len)); fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, ntohs (lsp->lsp_header->pdu_len) - 12, 12); ISIS_FLAGS_SET_ALL (lsp->SRMflags); Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Upd (%s): (1) re-originating LSP %s new seq 0x%08x\n", circuit->area->area_tag, rawlspid_print (hdr->lsp_id), ntohl (lsp->lsp_header->seq_num)); lsp->lsp_header->rem_lifetime = htons (isis_jitter (circuit->area->max_lsp_lifetime[level - 1], MAX_AGE_JITTER)); } else { /* Got purge for own pseudo-lsp, and we are not DR */ lsp_purge_dr (lsp->lsp_header->lsp_id, circuit, level); } } } return retval; } /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a * purge */ if (memcmp (hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0) { if (!lsp) { /* 7.3.16.4: initiate a purge */ lsp_purge_non_exist (hdr, circuit->area); return ISIS_OK; } /* 7.3.15.1 d) - If this is our own lsp and we have it */ /* In 7.3.16.1, If an Intermediate system R somewhere in the domain * has information that the current sequence number for source S is * "greater" than that held by S, ... */ else if (ntohl (hdr->seq_num) > ntohl (lsp->lsp_header->seq_num)) { /* 7.3.16.1 */ lsp->lsp_header->seq_num = htonl (ntohl (hdr->seq_num) + 1); fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, ntohs (lsp->lsp_header->pdu_len) - 12, 12); ISIS_FLAGS_SET_ALL (lsp->SRMflags); Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08x\n", circuit->area->area_tag, rawlspid_print (hdr->lsp_id), ntohl (lsp->lsp_header->seq_num)); lsp->lsp_header->rem_lifetime = htons (isis_jitter (circuit->area->max_lsp_lifetime[level - 1], MAX_AGE_JITTER)); } } else { /* 7.3.15.1 e) - This lsp originated on another system */ /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db */ if ((!lsp || comp == LSP_NEWER)) { /* i */ if (lsp) { lsp_search_and_destroy (hdr->lsp_id, circuit->area->lspdb[level - 1]); /* exists, so we overwrite */ } /* * If this lsp is a frag, need to see if we have zero lsp present */ if (LSP_FRAGMENT (hdr->lsp_id) != 0) { memcpy (lspid, hdr->lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT (lspid) = 0; lsp0 = lsp_search (lspid, circuit->area->lspdb[level - 1]); if (!lsp0) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): Got lsp frag, while zero lsp not database\n"); return ISIS_OK; } } lsp = lsp_new_from_stream_ptr (circuit->rcv_stream, ntohs (hdr->pdu_len), lsp0, circuit->area); lsp->level = level; lsp->adj = adj; lsp_insert (lsp, circuit->area->lspdb[level - 1]); /* ii */ ISIS_FLAGS_SET_ALL (lsp->SRMflags); /* iii */ ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); /* iv */ if (circuit->circ_type != CIRCUIT_T_BROADCAST) ISIS_SET_FLAG (lsp->SSNflags, circuit); /* FIXME: v) */ } /* 7.3.15.1 e) 2) LSP equal to the one in db */ else if (comp == LSP_EQUAL) { ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); lsp_update (lsp, hdr, circuit->rcv_stream, circuit->area, level); if (circuit->circ_type != CIRCUIT_T_BROADCAST) { ISIS_SET_FLAG (lsp->SSNflags, circuit); } } /* 7.3.15.1 e) 3) LSP older than the one in db */ else { ISIS_SET_FLAG (lsp->SRMflags, circuit); ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); } } if (lsp) lsp->adj = adj; return retval; } /* * Process Sequence Numbers * ISO - 10589 * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU */ static int process_snp (int snp_type, int level, struct isis_circuit *circuit, u_char * ssnpa) { int retval = ISIS_OK; int cmp, own_lsp; char typechar = ' '; int len; struct isis_adjacency *adj; struct isis_complete_seqnum_hdr *chdr = NULL; struct isis_partial_seqnum_hdr *phdr = NULL; uint32_t found = 0, expected = 0; struct isis_lsp *lsp; struct lsp_entry *entry; struct listnode *node, *nnode; struct listnode *node2, *nnode2; struct tlvs tlvs; struct list *lsp_list = NULL; struct isis_passwd *passwd; if (snp_type == ISIS_SNP_CSNP_FLAG) { /* getting the header info */ typechar = 'C'; chdr = (struct isis_complete_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream); circuit->rcv_stream->getp += ISIS_CSNP_HDRLEN; len = ntohs (chdr->pdu_len); if (len < ISIS_CSNP_HDRLEN) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Received a CSNP with bogus length!\n"); return ISIS_OK; } } else { typechar = 'P'; phdr = (struct isis_partial_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream); circuit->rcv_stream->getp += ISIS_PSNP_HDRLEN; len = ntohs (phdr->pdu_len); if (len < ISIS_PSNP_HDRLEN) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Received a CSNP with bogus length!\n"); return ISIS_OK; } } /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */ if (circuit->ext_domain) { return ISIS_OK; } /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */ if (!accept_level (level, circuit->circuit_is_type)) { return ISIS_OK; } /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked */ /* 7.3.15.2 a) 6 - maximum area match, can be ommited since we only use 3 * - already checked */ /* 7.3.15.2 a) 7 - Must check that we have an adjacency of the same level */ /* for broadcast circuits, snpa should be compared */ if (circuit->circ_type == CIRCUIT_T_P2P) { if (!circuit->u.p2p.neighbor) return ISIS_OK; /* Silently discard */ } /* 7.3.15.2 a) 8 - Passwords for level 1 - not implemented */ /* 7.3.15.2 a) 9 - Passwords for level 2 - not implemented */ memset (&tlvs, 0, sizeof (struct tlvs)); /* parse the SNP */ expected |= TLVFLAG_LSP_ENTRIES; expected |= TLVFLAG_AUTH_INFO; retval = parse_tlvs (circuit->area->area_tag, STREAM_PNT (circuit->rcv_stream), len - circuit->rcv_stream->getp, &expected, &found, &tlvs); if (retval > ISIS_WARNING) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): something went very wrong processing SNP\n"); free_tlvs (&tlvs); return retval; } if (level == 1) passwd = &circuit->area->area_passwd; else passwd = &circuit->area->domain_passwd; if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) { if (passwd->type) { if (!(found & TLVFLAG_AUTH_INFO) || authentication_check (passwd, &tlvs.auth_info)) { isis_event_auth_failure (circuit->area->area_tag, "SNP authentication" " failure", phdr ? phdr->source_id : chdr->source_id); return ISIS_OK; } } } /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */ if (tlvs.lsp_entries) { for (ALL_LIST_ELEMENTS_RO (tlvs.lsp_entries, node, entry)) { lsp = lsp_search (entry->lsp_id, circuit->area->lspdb[level - 1]); own_lsp = !memcmp (entry->lsp_id, isis->sysid, ISIS_SYS_ID_LEN); if (lsp) { /* 7.3.15.2 b) 1) is this LSP newer */ cmp = lsp_compare (circuit->area->area_tag, lsp, entry->seq_num, entry->checksum, entry->rem_lifetime); /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p */ if (cmp == LSP_EQUAL) { if (circuit->circ_type != CIRCUIT_T_BROADCAST) ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); /* 7.3.15.2 b) 3) if it is older, clear SSN and set SRM */ } else if (cmp == LSP_OLDER) { ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); ISIS_SET_FLAG (lsp->SRMflags, circuit); } else { /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM * on p2p */ if (own_lsp) { lsp_inc_seqnum (lsp, ntohl (entry->seq_num)); ISIS_SET_FLAG (lsp->SRMflags, circuit); } else { ISIS_SET_FLAG (lsp->SSNflags, circuit); if (circuit->circ_type != CIRCUIT_T_BROADCAST) ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); } } } else { /* 7.3.15.2 b) 5) if it was not found, and all of those are not 0, * insert it and set SSN on it */ if (entry->rem_lifetime && entry->checksum && entry->seq_num && memcmp (entry->lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) { lsp = lsp_new (entry->lsp_id, ntohs (entry->rem_lifetime), 0, 0, entry->checksum, level); lsp_insert (lsp, circuit->area->lspdb[level - 1]); ISIS_SET_FLAG (lsp->SSNflags, circuit); } } } } /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported */ if (snp_type == ISIS_SNP_CSNP_FLAG) { /* * Build a list from our own LSP db bounded with start_ and stop_lsp_id */ lsp_list = list_new (); lsp_build_list_nonzero_ht (chdr->start_lsp_id, chdr->stop_lsp_id, lsp_list, circuit->area->lspdb[level - 1]); /* Fixme: Find a better solution */ if (tlvs.lsp_entries) { for (ALL_LIST_ELEMENTS (tlvs.lsp_entries, node, nnode, entry)) { for (ALL_LIST_ELEMENTS (lsp_list, node2, nnode2, lsp)) { if (lsp_id_cmp (lsp->lsp_header->lsp_id, entry->lsp_id) == 0) { list_delete_node (lsp_list, node2); break; } } } } /* on remaining LSPs we set SRM (neighbor knew not of) */ for (ALL_LIST_ELEMENTS_RO (lsp_list, node, lsp)) { ISIS_SET_FLAG (lsp->SRMflags, circuit); } /* lets free it */ list_free (lsp_list); } free_tlvs (&tlvs); return retval; } static int process_csnp (int level, struct isis_circuit *circuit, u_char * ssnpa) { /* Sanity check - FIXME: move to correct place */ if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_CSNP_HDRLEN) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Packet too short ( < %d)\n", ISIS_CSNP_HDRLEN); return ISIS_WARNING; } return process_snp (ISIS_SNP_CSNP_FLAG, level, circuit, ssnpa); } static int process_psnp (int level, struct isis_circuit *circuit, u_char * ssnpa) { if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_PSNP_HDRLEN) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Packet too short\n"); return ISIS_WARNING; } return process_snp (ISIS_SNP_PSNP_FLAG, level, circuit, ssnpa); } /* * Process ISH * ISO - 10589 * Section 8.2.2 - Receiving ISH PDUs by an intermediate system * FIXME: sample packet dump, need to figure 0x81 - looks like NLPid * 0x82 0x15 0x01 0x00 0x04 0x01 0x2c 0x59 * 0x38 0x08 0x47 0x00 0x01 0x00 0x02 0x00 * 0x03 0x00 0x81 0x01 0xcc */ static int process_is_hello (struct isis_circuit *circuit) { struct isis_adjacency *adj; int retval = ISIS_OK; u_char neigh_len; u_char *sysid; neigh_len = stream_getc (circuit->rcv_stream); sysid = STREAM_PNT (circuit->rcv_stream) + neigh_len - 1 - ISIS_SYS_ID_LEN; adj = circuit->u.p2p.neighbor; if (!adj) { /* 8.2.2 */ adj = isis_new_adj (sysid, NULL, 0, circuit); if (adj == NULL) return ISIS_ERROR; isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); adj->sys_type = ISIS_SYSTYPE_UNKNOWN; circuit->u.p2p.neighbor = adj; } /* 8.2.2 a) */ if ((adj->adj_state == ISIS_ADJ_UP) && memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN)) { /* 8.2.2 a) 1) FIXME: adjStateChange(down) event */ /* 8.2.2 a) 2) delete the adj */ free(adj); /* 8.2.2 a) 3) create a new adj */ adj = isis_new_adj (sysid, NULL, 0, circuit); if (adj == NULL) return ISIS_ERROR; /* 8.2.2 a) 3) i */ isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); /* 8.2.2 a) 3) ii */ adj->sys_type = ISIS_SYSTYPE_UNKNOWN; /* 8.2.2 a) 4) quite meaningless */ } /* 8.2.2 b) ignore on condition */ if ((adj->adj_state == ISIS_ADJ_INITIALIZING) && (adj->sys_type == ISIS_SYSTYPE_IS)) { /* do nothing */ } else { /* 8.2.2 c) respond with a p2p IIH */ send_hello (circuit, 1); } /* 8.2.2 d) type is IS */ adj->sys_type = ISIS_SYSTYPE_IS; /* 8.2.2 e) FIXME: Circuit type of? */ return retval; } /* * PDU Dispatcher */ int isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa) { struct isis_fixed_hdr *hdr; struct esis_fixed_hdr *esis_hdr; int retval = ISIS_OK; /* * Let's first read data from stream to the header */ hdr = (struct isis_fixed_hdr *) STREAM_DATA (circuit->rcv_stream); if ((hdr->idrp != ISO10589_ISIS) && (hdr->idrp != ISO9542_ESIS)) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Not an IS-IS or ES-IS packet IDRP=%02x\n", hdr->idrp); return ISIS_ERROR; } /* now we need to know if this is an ISO 9542 packet and * take real good care of it, waaa! */ if (hdr->idrp == ISO9542_ESIS) { esis_hdr = (struct esis_fixed_hdr *) STREAM_DATA (circuit->rcv_stream); stream_set_getp (circuit->rcv_stream, ESIS_FIXED_HDR_LEN); /* FIXME: Need to do some acceptence tests */ /* example length... */ switch (esis_hdr->pdu_type) { case ESH_PDU: /* FIXME */ break; case ISH_PDU: retval = process_is_hello (circuit); break; default: return ISIS_ERROR; } return retval; } else { stream_set_getp (circuit->rcv_stream, ISIS_FIXED_HDR_LEN); } /* * and then process it */ if (hdr->length < ISIS_MINIMUM_FIXED_HDR_LEN) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): Fixed header length = %d\n", hdr->length); return ISIS_ERROR; } if (hdr->version1 != 1) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Unsupported ISIS version %u\n", hdr->version1); return ISIS_WARNING; } /* either 6 or 0 */ if ((hdr->id_len != 0) && (hdr->id_len != ISIS_SYS_ID_LEN)) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): IDFieldLengthMismatch: ID Length field in a received PDU %u, while the parameter for this IS is %u\n", hdr->id_len, ISIS_SYS_ID_LEN); return ISIS_ERROR; } if (hdr->version2 != 1) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): Unsupported ISIS version %u\n", hdr->version2); return ISIS_WARNING; } /* either 3 or 0 */ if ((hdr->max_area_addrs != 0) && (hdr->max_area_addrs != isis->max_area_addrs)) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %u while the parameter for this IS is %u\n", hdr->max_area_addrs, isis->max_area_addrs); return ISIS_ERROR; } switch (hdr->pdu_type) { case P2P_HELLO: retval = process_p2p_hello (circuit); if (retval <= ISIS_WARNING) send_hello(circuit, 1); break; case L1_LINK_STATE: retval = process_lsp (ISIS_LEVEL1, circuit, ssnpa); break; case L2_LINK_STATE: retval = process_lsp (ISIS_LEVEL2, circuit, ssnpa); break; case L1_COMPLETE_SEQ_NUM: retval = process_csnp (ISIS_LEVEL1, circuit, ssnpa); break; case L2_COMPLETE_SEQ_NUM: retval = process_csnp (ISIS_LEVEL2, circuit, ssnpa); break; case L1_PARTIAL_SEQ_NUM: retval = process_psnp (ISIS_LEVEL1, circuit, ssnpa); break; case L2_PARTIAL_SEQ_NUM: retval = process_psnp (ISIS_LEVEL2, circuit, ssnpa); break; default: return ISIS_ERROR; } return retval; } /* filling of the fixed isis header */ void fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type) { memset (hdr, 0, sizeof (struct isis_fixed_hdr)); hdr->idrp = ISO10589_ISIS; switch (pdu_type) { case L1_LAN_HELLO: case L2_LAN_HELLO: hdr->length = ISIS_LANHELLO_HDRLEN; break; case P2P_HELLO: hdr->length = ISIS_P2PHELLO_HDRLEN; break; case L1_LINK_STATE: case L2_LINK_STATE: hdr->length = ISIS_LSP_HDR_LEN; break; case L1_COMPLETE_SEQ_NUM: case L2_COMPLETE_SEQ_NUM: hdr->length = ISIS_CSNP_HDRLEN; break; case L1_PARTIAL_SEQ_NUM: case L2_PARTIAL_SEQ_NUM: hdr->length = ISIS_PSNP_HDRLEN; break; default: Log(LOG_WARNING, "WARN ( default/core/ISIS ): fill_fixed_hdr(): unknown pdu type %d\n", pdu_type); return; } hdr->length += ISIS_FIXED_HDR_LEN; hdr->pdu_type = pdu_type; hdr->version1 = 1; hdr->id_len = 0; /* ISIS_SYS_ID_LEN - 0==6 */ hdr->version2 = 1; hdr->max_area_addrs = 0; /* isis->max_area_addrs - 0==3 */ } /* * SEND SIDE */ static void fill_fixed_hdr_andstream (struct isis_fixed_hdr *hdr, u_char pdu_type, struct stream *stream) { fill_fixed_hdr (hdr, pdu_type); stream_putc (stream, hdr->idrp); stream_putc (stream, hdr->length); stream_putc (stream, hdr->version1); stream_putc (stream, hdr->id_len); stream_putc (stream, hdr->pdu_type); stream_putc (stream, hdr->version2); stream_putc (stream, hdr->reserved); stream_putc (stream, hdr->max_area_addrs); return; } int send_hello (struct isis_circuit *circuit, int level) { struct isis_fixed_hdr fixed_hdr; struct isis_lan_hello_hdr hello_hdr; struct isis_p2p_hello_hdr p2p_hello_hdr; u_int32_t interval; unsigned long len_pointer, length; int retval; if (circuit->interface->mtu == 0) { Log(LOG_WARNING, "WARN ( default/core/ISIS ): circuit has zero MTU\n"); return ISIS_WARNING; } if (!circuit->snd_stream) circuit->snd_stream = stream_new ( ISO_MTU(circuit) ); else stream_reset (circuit->snd_stream); if (circuit->circ_type == CIRCUIT_T_P2P) fill_fixed_hdr_andstream (&fixed_hdr, P2P_HELLO, circuit->snd_stream); /* * Fill LAN Level 1 or 2 Hello PDU header */ memset (&hello_hdr, 0, sizeof (struct isis_lan_hello_hdr)); interval = circuit->hello_multiplier[level - 1] * circuit->hello_interval[level - 1]; /* If we are the DIS then hello interval is divided by three, as is the hold-timer */ if (circuit->u.bc.is_dr[level - 1]) interval=interval/3; if (interval > USHRT_MAX) interval = USHRT_MAX; hello_hdr.circuit_t = circuit->circuit_is_type; memcpy (hello_hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN); hello_hdr.hold_time = htons ((u_int16_t) interval); hello_hdr.pdu_len = 0; /* Update the PDU Length later */ len_pointer = stream_get_endp (circuit->snd_stream) + 3 + ISIS_SYS_ID_LEN; /* copy the shared part of the hello to the p2p hello if needed */ if (circuit->circ_type == CIRCUIT_T_P2P) { memcpy (&p2p_hello_hdr, &hello_hdr, 5 + ISIS_SYS_ID_LEN); p2p_hello_hdr.local_id = circuit->circuit_id; /* FIXME: need better understanding */ stream_put (circuit->snd_stream, &p2p_hello_hdr, ISIS_P2PHELLO_HDRLEN); } else { hello_hdr.prio = circuit->u.bc.priority[level - 1]; if (level == 1 && circuit->u.bc.l1_desig_is) { memcpy (hello_hdr.lan_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); } else if (level == 2 && circuit->u.bc.l2_desig_is) { memcpy (hello_hdr.lan_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); } stream_put (circuit->snd_stream, &hello_hdr, ISIS_LANHELLO_HDRLEN); } /* * Then the variable length part */ /* add circuit password */ if (circuit->passwd.type) if (tlv_add_authinfo (circuit->passwd.type, circuit->passwd.len, circuit->passwd.passwd, circuit->snd_stream)) return ISIS_WARNING; /* Area Addresses TLV */ assert (circuit->area); if (circuit->area->area_addrs && circuit->area->area_addrs->count > 0) if (tlv_add_area_addrs (circuit->area->area_addrs, circuit->snd_stream)) return ISIS_WARNING; /* Protocols Supported TLV */ if (circuit->nlpids.count > 0) if (tlv_add_nlpid (&circuit->nlpids, circuit->snd_stream)) return ISIS_WARNING; /* IP interface Address TLV */ if (circuit->ip_router && circuit->ip_addrs && circuit->ip_addrs->count > 0) if (tlv_add_ip_addrs (circuit->ip_addrs, circuit->snd_stream)) return ISIS_WARNING; #ifdef ENABLE_IPV6 /* IPv6 Interface Address TLV */ if (circuit->ipv6_router && circuit->ipv6_link && circuit->ipv6_link->count > 0) if (tlv_add_ipv6_addrs (circuit->ipv6_link, circuit->snd_stream)) return ISIS_WARNING; #endif /* ENABLE_IPV6 */ /* We should always pad hellos, even on p2p links */ /* if (circuit->u.bc.pad_hellos) */ if (tlv_add_padding (circuit->snd_stream)) return ISIS_WARNING; length = stream_get_endp (circuit->snd_stream); /* Update PDU length */ stream_putw_at (circuit->snd_stream, len_pointer, (u_int16_t) length); retval = circuit->tx (circuit, level); if (retval) Log(LOG_WARNING, "WARN ( default/core/ISIS ): sending of LAN Level %d Hello failed\n", level); return retval; } int send_p2p_hello (struct thread *thread) { struct isis_circuit *circuit; circuit = THREAD_ARG (thread); assert (circuit); circuit->u.p2p.t_send_p2p_hello = NULL; send_hello (circuit, 1); /* set next timer thread */ THREAD_TIMER_ON (master, circuit->u.p2p.t_send_p2p_hello, send_p2p_hello, circuit, isis_jitter (circuit->hello_interval[1], IIH_JITTER)); return ISIS_OK; } int ack_lsp (struct isis_link_state_hdr *hdr, struct isis_circuit *circuit, int level) { unsigned long lenp; int retval; u_int16_t length; struct isis_fixed_hdr fixed_hdr; if (!circuit->snd_stream) circuit->snd_stream = stream_new ( ISO_MTU(circuit) ); else stream_reset (circuit->snd_stream); // fill_llc_hdr (stream); if (level == 1) fill_fixed_hdr_andstream (&fixed_hdr, L1_PARTIAL_SEQ_NUM, circuit->snd_stream); else fill_fixed_hdr_andstream (&fixed_hdr, L2_PARTIAL_SEQ_NUM, circuit->snd_stream); lenp = stream_get_endp (circuit->snd_stream); stream_putw (circuit->snd_stream, 0); /* PDU length */ stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); stream_putc (circuit->snd_stream, circuit->idx); stream_putc (circuit->snd_stream, 9); /* code */ stream_putc (circuit->snd_stream, 16); /* len */ stream_putw (circuit->snd_stream, ntohs (hdr->rem_lifetime)); stream_put (circuit->snd_stream, hdr->lsp_id, ISIS_SYS_ID_LEN + 2); stream_putl (circuit->snd_stream, ntohl (hdr->seq_num)); stream_putw (circuit->snd_stream, ntohs (hdr->checksum)); length = (u_int16_t) stream_get_endp (circuit->snd_stream); /* Update PDU length */ stream_putw_at (circuit->snd_stream, lenp, length); retval = circuit->tx (circuit, level); return retval; } int isis_send_pdu_p2p (struct isis_circuit *circuit, int level) { int written = 1; struct sockaddr_ll sa; stream_set_getp (circuit->snd_stream, 0); memset (&sa, 0, sizeof (struct sockaddr_ll)); sa.sll_family = 0; /* XXX: let's see how far we get with this */ sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN); sa.sll_ifindex = if_nametoindex(config.nfacctd_isis_iface); sa.sll_halen = ETH_ALEN; if (level == 1) memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN); else memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN); /* lets try correcting the protocol */ sa.sll_protocol = htons (0x00FE); written = sendto (circuit->fd, circuit->snd_stream->data, stream_get_endp (circuit->snd_stream), 0, (struct sockaddr *) &sa, sizeof (struct sockaddr_ll)); return ISIS_OK; } int build_psnp (int level, struct isis_circuit *circuit, struct list *lsps) { struct isis_fixed_hdr fixed_hdr; unsigned long lenp; u_int16_t length; int retval = 0; struct isis_lsp *lsp; struct isis_passwd *passwd; struct listnode *node; if (level == 1) fill_fixed_hdr_andstream (&fixed_hdr, L1_PARTIAL_SEQ_NUM, circuit->snd_stream); else fill_fixed_hdr_andstream (&fixed_hdr, L2_PARTIAL_SEQ_NUM, circuit->snd_stream); /* * Fill Level 1 or 2 Partial Sequence Numbers header */ lenp = stream_get_endp (circuit->snd_stream); stream_putw (circuit->snd_stream, 0); /* PDU length - when we know it */ stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); stream_putc (circuit->snd_stream, circuit->idx); /* * And TLVs */ if (level == 1) passwd = &circuit->area->area_passwd; else passwd = &circuit->area->domain_passwd; if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) if (passwd->type) retval = tlv_add_authinfo (passwd->type, passwd->len, passwd->passwd, circuit->snd_stream); if (!retval && lsps) { retval = tlv_add_lsp_entries (lsps, circuit->snd_stream); } if (config.nfacctd_isis_msglog) { for (ALL_LIST_ELEMENTS_RO (lsps, node, lsp)) { if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Snp (%s): PSNP entry %s, seq 0x%08x," " cksum 0x%04x, lifetime %us\n", circuit->area->area_tag, rawlspid_print (lsp->lsp_header->lsp_id), ntohl (lsp->lsp_header->seq_num), ntohs (lsp->lsp_header->checksum), ntohs (lsp->lsp_header->rem_lifetime)); } } length = (u_int16_t) stream_get_endp (circuit->snd_stream); assert (length >= ISIS_PSNP_HDRLEN); /* Update PDU length */ stream_putw_at (circuit->snd_stream, lenp, length); return ISIS_OK; } /* * 7.3.15.4 action on expiration of partial SNP interval * level 1 */ /* XXX: debugs in this function should be a-la bgp_daemon_msglog */ int send_psnp (int level, struct isis_circuit *circuit) { int retval = ISIS_OK; struct isis_lsp *lsp; struct list *list = NULL; struct listnode *node; if ((circuit->circ_type == CIRCUIT_T_BROADCAST && !circuit->u.bc.is_dr[level - 1]) || circuit->circ_type != CIRCUIT_T_BROADCAST) { if (circuit->area->lspdb[level - 1] && dict_count (circuit->area->lspdb[level - 1]) > 0) { list = list_new (); lsp_build_list_ssn (circuit, list, circuit->area->lspdb[level - 1]); if (listcount (list) > 0) { if (circuit->snd_stream == NULL) circuit->snd_stream = stream_new (ISO_MTU (circuit)); else stream_reset (circuit->snd_stream); if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Snp (%s): Sent L%d PSNP on %s, length %ld\n", circuit->area->area_tag, level, circuit->interface->name, /* FIXME: use %z when we stop supporting old * compilers. */ (unsigned long) STREAM_SIZE (circuit->snd_stream)); retval = build_psnp (level, circuit, list); if (retval == ISIS_OK) retval = circuit->tx (circuit, level); if (retval == ISIS_OK) { /* * sending succeeded, we can clear SSN flags of this circuit * for the LSPs in list */ for (ALL_LIST_ELEMENTS_RO (list, node, lsp)) ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); } } list_delete (list); } } return retval; } /* XXX: debugs in this function should be a-la bgp_daemon_msglog */ int build_csnp (int level, u_char * start, u_char * stop, struct list *lsps, struct isis_circuit *circuit) { struct isis_fixed_hdr fixed_hdr; struct isis_passwd *passwd; int retval = ISIS_OK; unsigned long lenp; u_int16_t length; if (level == 1) fill_fixed_hdr_andstream (&fixed_hdr, L1_COMPLETE_SEQ_NUM, circuit->snd_stream); else fill_fixed_hdr_andstream (&fixed_hdr, L2_COMPLETE_SEQ_NUM, circuit->snd_stream); /* * Fill Level 1 or 2 Complete Sequence Numbers header */ lenp = stream_get_endp (circuit->snd_stream); stream_putw (circuit->snd_stream, 0); /* PDU length - when we know it */ /* no need to send the source here, it is always us if we csnp */ stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); /* with zero circuit id - ref 9.10, 9.11 */ stream_putc (circuit->snd_stream, 0x00); stream_put (circuit->snd_stream, start, ISIS_SYS_ID_LEN + 2); stream_put (circuit->snd_stream, stop, ISIS_SYS_ID_LEN + 2); /* * And TLVs */ if (level == 1) passwd = &circuit->area->area_passwd; else passwd = &circuit->area->domain_passwd; if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) if (passwd->type) retval = tlv_add_authinfo (passwd->type, passwd->len, passwd->passwd, circuit->snd_stream); if (!retval && lsps) { retval = tlv_add_lsp_entries (lsps, circuit->snd_stream); } length = (u_int16_t) stream_get_endp (circuit->snd_stream); assert (length >= ISIS_CSNP_HDRLEN); /* Update PU length */ stream_putw_at (circuit->snd_stream, lenp, length); return retval; } /* * FIXME: support multiple CSNPs */ /* XXX: debugs in this function should be a-la bgp_daemon_msglog */ int send_csnp (struct isis_circuit *circuit, int level) { int retval = ISIS_OK; u_char start[ISIS_SYS_ID_LEN + 2]; u_char stop[ISIS_SYS_ID_LEN + 2]; struct list *list = NULL; struct listnode *node; struct isis_lsp *lsp; memset (start, 0x00, ISIS_SYS_ID_LEN + 2); memset (stop, 0xff, ISIS_SYS_ID_LEN + 2); if (circuit->area->lspdb[level - 1] && dict_count (circuit->area->lspdb[level - 1]) > 0) { list = list_new (); lsp_build_list (start, stop, list, circuit->area->lspdb[level - 1]); if (circuit->snd_stream == NULL) circuit->snd_stream = stream_new (ISO_MTU (circuit)); else stream_reset (circuit->snd_stream); retval = build_csnp (level, start, stop, list, circuit); if (config.nfacctd_isis_msglog) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Snp (%s): Sent L%d CSNP on %s, length %ld\n", circuit->area->area_tag, level, circuit->interface->name, /* FIXME: use %z when we stop supporting old compilers. */ (unsigned long) STREAM_SIZE (circuit->snd_stream)); for (ALL_LIST_ELEMENTS_RO (list, node, lsp)) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Snp (%s): CSNP entry %s, seq 0x%08x," " cksum 0x%04x, lifetime %us\n", circuit->area->area_tag, rawlspid_print (lsp->lsp_header->lsp_id), ntohl (lsp->lsp_header->seq_num), ntohs (lsp->lsp_header->checksum), ntohs (lsp->lsp_header->rem_lifetime)); } } list_delete (list); if (retval == ISIS_OK) retval = circuit->tx (circuit, level); } return retval; } pmacct-0.14.0/src/isis/isis_spf.h0000640000175000017500000000442711734647141015612 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_spf.h * IS-IS Shortest Path First algorithm * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_SPF_H_ #define _ISIS_SPF_H_ enum vertextype { VTYPE_PSEUDO_IS = 1, VTYPE_PSEUDO_TE_IS, VTYPE_NONPSEUDO_IS, VTYPE_NONPSEUDO_TE_IS, VTYPE_ES, VTYPE_IPREACH_INTERNAL, VTYPE_IPREACH_EXTERNAL, VTYPE_IPREACH_TE #ifdef ENABLE_IPV6 , VTYPE_IP6REACH_INTERNAL, VTYPE_IP6REACH_EXTERNAL #endif /* ENABLE_IPV6 */ }; /* * Triple */ struct isis_vertex { enum vertextype type; union { u_char id[ISIS_SYS_ID_LEN + 1]; struct isis_prefix prefix; } N; struct isis_lsp *lsp; u_int32_t d_N; /* d(N) Distance from this IS */ u_int16_t depth; /* The depth in the imaginary tree */ struct list *Adj_N; /* {Adj(N)} */ }; struct isis_spftree { struct thread *t_spf; /* spf threads */ time_t lastrun; /* for scheduling */ int pending; /* already scheduled */ struct list *paths; /* the SPT */ struct list *tents; /* TENT */ u_int32_t timerun; /* statistics */ }; #if (!defined __ISIS_SPF_C) #define EXT extern #else #define EXT #endif EXT void spftree_area_init (struct isis_area *); EXT int isis_spf_schedule (struct isis_area *, int); EXT int isis_run_spf (struct isis_area *, int, int); #ifdef ENABLE_IPV6 EXT int isis_spf_schedule6 (struct isis_area *, int); #endif #undef EXT #endif /* _ISIS_SPF_H_ */ pmacct-0.14.0/src/isis/thread.c0000640000175000017500000005744611734647141015246 0ustar paolopaolo/* Thread management routine * Copyright (C) 1998, 2000 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #define __THREAD_C #include "pmacct.h" #include "isis.h" #include "thread.h" #include "hash.h" /* Recent absolute time of day */ struct timeval recent_time; static struct timeval last_recent_time; /* Relative time, since startup */ static struct timeval relative_time; static struct timeval relative_time_base; /* init flag */ static unsigned short timers_inited; static struct hash *cpu_record = NULL; /* Struct timeval's tv_usec one second value. */ #define TIMER_SECOND_MICRO 1000000L /* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO). And change negative values to 0. */ static struct timeval timeval_adjust (struct timeval a) { while (a.tv_usec >= TIMER_SECOND_MICRO) { a.tv_usec -= TIMER_SECOND_MICRO; a.tv_sec++; } while (a.tv_usec < 0) { a.tv_usec += TIMER_SECOND_MICRO; a.tv_sec--; } if (a.tv_sec < 0) /* Change negative timeouts to 0. */ a.tv_sec = a.tv_usec = 0; return a; } static struct timeval timeval_subtract (struct timeval a, struct timeval b) { struct timeval ret; ret.tv_usec = a.tv_usec - b.tv_usec; ret.tv_sec = a.tv_sec - b.tv_sec; return timeval_adjust (ret); } static long isis_timeval_cmp (struct timeval a, struct timeval b) { return (a.tv_sec == b.tv_sec ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec); } static unsigned long timeval_elapsed (struct timeval a, struct timeval b) { return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + (a.tv_usec - b.tv_usec)); } #ifndef HAVE_CLOCK_MONOTONIC static void quagga_gettimeofday_relative_adjust (void) { struct timeval diff; if (isis_timeval_cmp (recent_time, last_recent_time) < 0) { relative_time.tv_sec++; relative_time.tv_usec = 0; } else { diff = timeval_subtract (recent_time, last_recent_time); relative_time.tv_sec += diff.tv_sec; relative_time.tv_usec += diff.tv_usec; relative_time = timeval_adjust (relative_time); } last_recent_time = recent_time; } #endif /* !HAVE_CLOCK_MONOTONIC */ /* gettimeofday wrapper, to keep recent_time updated */ static int quagga_gettimeofday (struct timeval *tv) { int ret; assert (tv); if (!(ret = gettimeofday (&recent_time, NULL))) { /* init... */ if (!timers_inited) { relative_time_base = last_recent_time = recent_time; timers_inited = 1; } /* avoid copy if user passed recent_time pointer.. */ if (tv != &recent_time) *tv = recent_time; return 0; } return ret; } static int quagga_get_relative (struct timeval *tv) { int ret; #ifdef HAVE_CLOCK_MONOTONIC { struct timespec tp; if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp))) { relative_time.tv_sec = tp.tv_sec; relative_time.tv_usec = tp.tv_nsec / 1000; } } #else /* !HAVE_CLOCK_MONOTONIC */ if (!(ret = quagga_gettimeofday (&recent_time))) quagga_gettimeofday_relative_adjust(); #endif /* HAVE_CLOCK_MONOTONIC */ if (tv) *tv = relative_time; return ret; } /* Get absolute time stamp, but in terms of the internal timer * Could be wrong, but at least won't go back. */ static void quagga_real_stabilised (struct timeval *tv) { *tv = relative_time_base; tv->tv_sec += relative_time.tv_sec; tv->tv_usec += relative_time.tv_usec; *tv = timeval_adjust (*tv); } /* Exported Quagga timestamp function. * Modelled on POSIX clock_gettime. */ int quagga_gettime (enum quagga_clkid clkid, struct timeval *tv) { switch (clkid) { case QUAGGA_CLK_REALTIME: return quagga_gettimeofday (tv); case QUAGGA_CLK_MONOTONIC: return quagga_get_relative (tv); case QUAGGA_CLK_REALTIME_STABILISED: quagga_real_stabilised (tv); return 0; default: errno = EINVAL; return -1; } } /* time_t value in terms of stabilised absolute time. * replacement for POSIX time() */ time_t quagga_time (time_t *t) { struct timeval tv; quagga_real_stabilised (&tv); if (t) *t = tv.tv_sec; return tv.tv_sec; } /* Public export of recent_relative_time by value */ struct timeval recent_relative_time (void) { return relative_time; } static unsigned int cpu_record_hash_key (struct cpu_thread_history *a) { return (uintptr_t) a->func; } static int cpu_record_hash_cmp (const struct cpu_thread_history *a, const struct cpu_thread_history *b) { return a->func == b->func; } static void * cpu_record_hash_alloc (struct cpu_thread_history *a) { struct cpu_thread_history *new; new = calloc(1, sizeof (struct cpu_thread_history)); new->func = a->func; new->funcname = strdup(a->funcname); return new; } static void cpu_record_hash_free (void *a) { struct cpu_thread_history *hist = a; free(hist->funcname); free(hist); } static void cpu_record_hash_clear (struct hash_backet *bucket, void *args) { thread_type *filter = args; struct cpu_thread_history *a = bucket->data; a = bucket->data; if ( !(a->types & *filter) ) return; hash_release (cpu_record, bucket->data); } /* Allocate new thread master. */ struct thread_master * thread_master_create () { if (cpu_record == NULL) cpu_record = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key, (int (*) (const void *, const void *))cpu_record_hash_cmp); return (struct thread_master *) calloc(1, sizeof (struct thread_master)); } /* Add a new thread to the list. */ static void thread_list_add (struct thread_list *list, struct thread *thread) { thread->next = NULL; thread->prev = list->tail; if (list->tail) list->tail->next = thread; else list->head = thread; list->tail = thread; list->count++; } /* Add a new thread just before the point. */ static void thread_list_add_before (struct thread_list *list, struct thread *point, struct thread *thread) { thread->next = point; thread->prev = point->prev; if (point->prev) point->prev->next = thread; else list->head = thread; point->prev = thread; list->count++; } /* Delete a thread from the list. */ static struct thread * thread_list_delete (struct thread_list *list, struct thread *thread) { if (thread->next) thread->next->prev = thread->prev; else list->tail = thread->prev; if (thread->prev) thread->prev->next = thread->next; else list->head = thread->next; thread->next = thread->prev = NULL; list->count--; return thread; } /* Move thread to unuse list. */ static void thread_add_unuse (struct thread_master *m, struct thread *thread) { assert (m != NULL && thread != NULL); assert (thread->next == NULL); assert (thread->prev == NULL); assert (thread->type == THREAD_UNUSED); thread_list_add (&m->unuse, thread); /* XXX: Should we deallocate funcname here? */ } /* Free all unused thread. */ static void thread_list_free (struct thread_master *m, struct thread_list *list) { struct thread *t; struct thread *next; for (t = list->head; t; t = next) { next = t->next; if (t->funcname) free(t->funcname); free(t); list->count--; m->alloc--; } } /* Stop thread scheduler. */ void thread_master_free (struct thread_master *m) { thread_list_free (m, &m->read); thread_list_free (m, &m->write); thread_list_free (m, &m->timer); thread_list_free (m, &m->event); thread_list_free (m, &m->ready); thread_list_free (m, &m->unuse); thread_list_free (m, &m->background); free(m); if (cpu_record) { hash_clean (cpu_record, cpu_record_hash_free); hash_free (cpu_record); cpu_record = NULL; } } /* Thread list is empty or not. */ static inline int thread_empty (struct thread_list *list) { return list->head ? 0 : 1; } /* Delete top of the list and return it. */ static struct thread * thread_trim_head (struct thread_list *list) { if (!thread_empty (list)) return thread_list_delete (list, list->head); return NULL; } /* Return remain time in second. */ unsigned long thread_timer_remain_second (struct thread *thread) { quagga_get_relative (NULL); if (thread->u.sands.tv_sec - relative_time.tv_sec > 0) return thread->u.sands.tv_sec - relative_time.tv_sec; else return 0; } /* Trim blankspace and "()"s */ static char * strip_funcname (const char *funcname) { char buff[100]; char tmp, *ret, *e, *b = buff; strncpy(buff, funcname, sizeof(buff)); buff[ sizeof(buff) -1] = '\0'; e = buff +strlen(buff) -1; /* Wont work for funcname == "Word (explanation)" */ while (*b == ' ' || *b == '(') ++b; while (*e == ' ' || *e == ')') --e; e++; tmp = *e; *e = '\0'; ret = strdup(b); *e = tmp; return ret; } /* Get new thread. */ static struct thread * thread_get (struct thread_master *m, u_char type, int (*func) (struct thread *), void *arg, const char* funcname) { struct thread *thread; if (!thread_empty (&m->unuse)) { thread = thread_trim_head (&m->unuse); if (thread->funcname) free(thread->funcname); } else { thread = calloc(1, sizeof (struct thread)); m->alloc++; } thread->type = type; thread->add_type = type; thread->master = m; thread->func = func; thread->arg = arg; thread->funcname = strip_funcname(funcname); return thread; } /* Add new read thread. */ struct thread * funcname_thread_add_read (struct thread_master *m, int (*func) (struct thread *), void *arg, int fd, const char* funcname) { struct thread *thread; assert (m != NULL); if (FD_ISSET (fd, &m->readfd)) { Log(LOG_WARNING, "WARN (default/core/ISIS ): There is already read fd [%d]\n", fd); return NULL; } thread = thread_get (m, THREAD_READ, func, arg, funcname); FD_SET (fd, &m->readfd); thread->u.fd = fd; thread_list_add (&m->read, thread); return thread; } /* Add new write thread. */ struct thread * funcname_thread_add_write (struct thread_master *m, int (*func) (struct thread *), void *arg, int fd, const char* funcname) { struct thread *thread; assert (m != NULL); if (FD_ISSET (fd, &m->writefd)) { Log(LOG_WARNING, "WARN (default/core/ISIS ): There is already write fd [%d]\n", fd); return NULL; } thread = thread_get (m, THREAD_WRITE, func, arg, funcname); FD_SET (fd, &m->writefd); thread->u.fd = fd; thread_list_add (&m->write, thread); return thread; } static struct thread * funcname_thread_add_timer_timeval (struct thread_master *m, int (*func) (struct thread *), int type, void *arg, struct timeval *time_relative, const char* funcname) { struct thread *thread; struct thread_list *list; struct timeval alarm_time; struct thread *tt; assert (m != NULL); assert (type == THREAD_TIMER || type == THREAD_BACKGROUND); assert (time_relative); list = ((type == THREAD_TIMER) ? &m->timer : &m->background); thread = thread_get (m, type, func, arg, funcname); /* Do we need jitter here? */ quagga_get_relative (NULL); alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec; alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec; thread->u.sands = timeval_adjust(alarm_time); /* Sort by timeval. */ for (tt = list->head; tt; tt = tt->next) if (isis_timeval_cmp (thread->u.sands, tt->u.sands) <= 0) break; if (tt) thread_list_add_before (list, tt, thread); else thread_list_add (list, thread); return thread; } /* Add timer event thread. */ struct thread * funcname_thread_add_timer (struct thread_master *m, int (*func) (struct thread *), void *arg, long timer, const char* funcname) { struct timeval trel; assert (m != NULL); trel.tv_sec = timer; trel.tv_usec = 0; return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg, &trel, funcname); } /* Add timer event thread with "millisecond" resolution */ struct thread * funcname_thread_add_timer_msec (struct thread_master *m, int (*func) (struct thread *), void *arg, long timer, const char* funcname) { struct timeval trel; assert (m != NULL); trel.tv_sec = timer / 1000; trel.tv_usec = 1000*(timer % 1000); return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg, &trel, funcname); } /* Add a background thread, with an optional millisec delay */ struct thread * funcname_thread_add_background (struct thread_master *m, int (*func) (struct thread *), void *arg, long delay, const char *funcname) { struct timeval trel; assert (m != NULL); if (delay) { trel.tv_sec = delay / 1000; trel.tv_usec = 1000*(delay % 1000); } else { trel.tv_sec = 0; trel.tv_usec = 0; } return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND, arg, &trel, funcname); } /* Add simple event thread. */ struct thread * funcname_thread_add_event (struct thread_master *m, int (*func) (struct thread *), void *arg, int val, const char* funcname) { struct thread *thread; assert (m != NULL); thread = thread_get (m, THREAD_EVENT, func, arg, funcname); thread->u.val = val; thread_list_add (&m->event, thread); return thread; } /* Cancel thread from scheduler. */ void thread_cancel (struct thread *thread) { struct thread_list *list; switch (thread->type) { case THREAD_READ: assert (FD_ISSET (thread->u.fd, &thread->master->readfd)); FD_CLR (thread->u.fd, &thread->master->readfd); list = &thread->master->read; break; case THREAD_WRITE: assert (FD_ISSET (thread->u.fd, &thread->master->writefd)); FD_CLR (thread->u.fd, &thread->master->writefd); list = &thread->master->write; break; case THREAD_TIMER: list = &thread->master->timer; break; case THREAD_EVENT: list = &thread->master->event; break; case THREAD_READY: list = &thread->master->ready; break; case THREAD_BACKGROUND: list = &thread->master->background; break; default: return; break; } thread_list_delete (list, thread); thread->type = THREAD_UNUSED; thread_add_unuse (thread->master, thread); } /* Delete all events which has argument value arg. */ unsigned int thread_cancel_event (struct thread_master *m, void *arg) { unsigned int ret = 0; struct thread *thread; thread = m->event.head; while (thread) { struct thread *t; t = thread; thread = t->next; if (t->arg == arg) { ret++; thread_list_delete (&m->event, t); t->type = THREAD_UNUSED; thread_add_unuse (m, t); } } return ret; } static struct timeval * thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val) { if (!thread_empty (tlist)) { *timer_val = timeval_subtract (tlist->head->u.sands, relative_time); return timer_val; } return NULL; } static struct thread * thread_run (struct thread_master *m, struct thread *thread, struct thread *fetch) { *fetch = *thread; thread->type = THREAD_UNUSED; thread->funcname = NULL; /* thread_call will free fetch's copied pointer */ thread_add_unuse (m, thread); return fetch; } static int thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset) { struct thread *thread; struct thread *next; int ready = 0; assert (list); for (thread = list->head; thread; thread = next) { next = thread->next; if (FD_ISSET (THREAD_FD (thread), fdset)) { assert (FD_ISSET (THREAD_FD (thread), mfdset)); FD_CLR(THREAD_FD (thread), mfdset); thread_list_delete (list, thread); thread_list_add (&thread->master->ready, thread); thread->type = THREAD_READY; ready++; } } return ready; } /* Add all timers that have popped to the ready list. */ static unsigned int thread_timer_process (struct thread_list *list, struct timeval *timenow) { struct thread *thread; unsigned int ready = 0; for (thread = list->head; thread; thread = thread->next) { if (isis_timeval_cmp (*timenow, thread->u.sands) < 0) return ready; thread_list_delete (list, thread); thread->type = THREAD_READY; thread_list_add (&thread->master->ready, thread); ready++; } return ready; } /* process a list en masse, e.g. for event thread lists */ static unsigned int thread_process (struct thread_list *list) { struct thread *thread; unsigned int ready = 0; for (thread = list->head; thread; thread = thread->next) { thread_list_delete (list, thread); thread->type = THREAD_READY; thread_list_add (&thread->master->ready, thread); ready++; } return ready; } /* Fetch next ready thread. */ struct thread * thread_fetch (struct thread_master *m, struct thread *fetch) { struct thread *thread; fd_set readfd; fd_set writefd; fd_set exceptfd; struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 }; struct timeval timer_val_bg; struct timeval *timer_wait = &timer_val; struct timeval *timer_wait_bg; while (1) { int num = 0; /* Signals pre-empt everything */ // quagga_sigevent_process (); /* Drain the ready queue of already scheduled jobs, before scheduling * more. */ if ((thread = thread_trim_head (&m->ready)) != NULL) return thread_run (m, thread, fetch); /* To be fair to all kinds of threads, and avoid starvation, we * need to be careful to consider all thread types for scheduling * in each quanta. I.e. we should not return early from here on. */ /* Normal event are the next highest priority. */ thread_process (&m->event); /* Structure copy. */ readfd = m->readfd; writefd = m->writefd; exceptfd = m->exceptfd; /* Calculate select wait timer if nothing else to do */ if (m->ready.count == 0) { quagga_get_relative (NULL); timer_wait = thread_timer_wait (&m->timer, &timer_val); timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg); if (timer_wait_bg && (!timer_wait || (isis_timeval_cmp (*timer_wait, *timer_wait_bg) > 0))) timer_wait = timer_wait_bg; } num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait); /* Signals should get quick treatment */ if (num < 0) { if (errno == EINTR) continue; /* signal received - process it */ Log(LOG_WARNING, "WARN (default/core/ISIS ): select() error: %s\n", strerror (errno)); return NULL; } /* Check foreground timers. Historically, they have had higher priority than I/O threads, so let's push them onto the ready list in front of the I/O threads. */ quagga_get_relative (NULL); thread_timer_process (&m->timer, &relative_time); /* Got IO, process it */ if (num > 0) { /* Normal priority read thead. */ thread_process_fd (&m->read, &readfd, &m->readfd); /* Write thead. */ thread_process_fd (&m->write, &writefd, &m->writefd); } #if 0 /* If any threads were made ready above (I/O or foreground timer), perhaps we should avoid adding background timers to the ready list at this time. If this is code is uncommented, then background timer threads will not run unless there is nothing else to do. */ if ((thread = thread_trim_head (&m->ready)) != NULL) return thread_run (m, thread, fetch); #endif /* Background timer/events, lowest priority */ thread_timer_process (&m->background, &relative_time); if ((thread = thread_trim_head (&m->ready)) != NULL) return thread_run (m, thread, fetch); } } unsigned long thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) { *cputime = 0; return timeval_elapsed (now->real, start->real); } /* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds. Note: we are using real (wall clock) time for this calculation. It could be argued that CPU time may make more sense in certain contexts. The things to consider are whether the thread may have blocked (in which case wall time increases, but CPU time does not), or whether the system is heavily loaded with other processes competing for CPU time. On balance, wall clock time seems to make sense. Plus it has the added benefit that gettimeofday should be faster than calling getrusage. */ int thread_should_yield (struct thread *thread) { quagga_get_relative (NULL); return (timeval_elapsed(relative_time, thread->ru.real) > THREAD_YIELD_TIME_SLOT); } void thread_getrusage (RUSAGE_T *r) { quagga_get_relative (NULL); r->real = relative_time; #ifdef HAVE_CLOCK_MONOTONIC /* quagga_get_relative() only updates recent_time if gettimeofday * based, not when using CLOCK_MONOTONIC. As we export recent_time * and guarantee to update it before threads are run... */ quagga_gettimeofday(&recent_time); #endif /* HAVE_CLOCK_MONOTONIC */ } /* We check thread consumed time. If the system has getrusage, we'll use that to get in-depth stats on the performance of the thread in addition to wall clock time stats from gettimeofday. */ void thread_call (struct thread *thread) { unsigned long realtime, cputime; RUSAGE_T ru; /* Cache a pointer to the relevant cpu history thread, if the thread * does not have it yet. * * Callers submitting 'dummy threads' hence must take care that * thread->cpu is NULL */ if (!thread->hist) { struct cpu_thread_history tmp; tmp.func = thread->func; tmp.funcname = thread->funcname; thread->hist = hash_get (cpu_record, &tmp, (void * (*) (void *))cpu_record_hash_alloc); } GETRUSAGE (&thread->ru); (*thread->func) (thread); GETRUSAGE (&ru); realtime = thread_consumed_time (&ru, &thread->ru, &cputime); thread->hist->real.total += realtime; if (thread->hist->real.max < realtime) thread->hist->real.max = realtime; ++(thread->hist->total_calls); thread->hist->types |= (1 << thread->add_type); free(thread->funcname); } /* Execute thread */ struct thread * funcname_thread_execute (struct thread_master *m, int (*func)(struct thread *), void *arg, int val, const char* funcname) { struct thread dummy; memset (&dummy, 0, sizeof (struct thread)); dummy.type = THREAD_EVENT; dummy.add_type = THREAD_EXECUTE; dummy.master = NULL; dummy.func = func; dummy.arg = arg; dummy.u.val = val; dummy.funcname = strip_funcname (funcname); thread_call (&dummy); free(dummy.funcname); return NULL; } pmacct-0.14.0/src/isis/stream.h0000640000175000017500000001641111732176613015261 0ustar paolopaolo/* * Packet interface * Copyright (C) 1999 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef _STREAM_H_ #define _STREAM_H_ #include "prefix.h" /* * A stream is an arbitrary buffer, whose contents generally are assumed to * be in network order. * * A stream has the following attributes associated with it: * * - size: the allocated, invariant size of the buffer. * * - getp: the get position marker, denoting the offset in the stream where * the next read (or 'get') will be from. This getp marker is * automatically adjusted when data is read from the stream, the * user may also manipulate this offset as they wish, within limits * (see below) * * - endp: the end position marker, denoting the offset in the stream where * valid data ends, and if the user attempted to write (or * 'put') data where that data would be written (or 'put') to. * * These attributes are all size_t values. * * Constraints: * * 1. getp can never exceed endp * * - hence if getp is equal to endp, there is no more valid data that can be * gotten from the stream (though, the user may reposition getp to earlier in * the stream, if they wish). * * 2. endp can never exceed size * * - hence, if endp is equal to size, then the stream is full, and no more * data can be written to the stream. * * In other words the following must always be true, and the stream * abstraction is allowed internally to assert that the following property * holds true for a stream, as and when it wishes: * * getp <= endp <= size * * It is the users responsibility to ensure this property is never violated. * * A stream therefore can be thought of like this: * * --------------------------------------------------- * |XXXXXXXXXXXXXXXXXXXXXXXX | * --------------------------------------------------- * ^ ^ ^ * getp endp size * * This shows a stream containing data (shown as 'X') up to the endp offset. * The stream is empty from endp to size. Without adjusting getp, there are * still endp-getp bytes of valid data to be read from the stream. * * Methods are provided to get and put to/from the stream, as well as * retrieve the values of the 3 markers and manipulate the getp marker. * * Note: * At the moment, newly allocated streams are zero filled. Hence, one can * use stream_forward_endp() to effectively create arbitrary zero-fill * padding. However, note that stream_reset() does *not* zero-out the * stream. This property should **not** be relied upon. * * Best practice is to use stream_put (, NULL, ) to zero out * any part of a stream which isn't otherwise written to. */ /* Stream buffer. */ struct stream { size_t getp; /* next get position */ size_t endp; /* last valid data position */ size_t size; /* size of data segment */ unsigned char *data; /* data pointer */ }; /* Utility macros. */ #define STREAM_SIZE(S) ((S)->size) /* number of bytes which can still be written */ #define STREAM_WRITEABLE(S) ((S)->size - (S)->endp) /* number of bytes still to be read */ #define STREAM_READABLE(S) ((S)->endp - (S)->getp) /* deprecated macros - do not use in new code */ #define STREAM_PNT(S) stream_pnt((S)) #define STREAM_DATA(S) ((S)->data) #define STREAM_REMAIN(S) STREAM_WRITEABLE((S)) /* Stream prototypes. * For stream_{put,get}S, the S suffix mean: * * c: character (unsigned byte) * w: word (two bytes) * l: long (two words) * q: quad (four words) */ #if (!defined __STREAM_C) #define EXT extern #else #define EXT #endif EXT struct stream *stream_new (size_t); EXT void stream_free (struct stream *); EXT struct stream * stream_copy (struct stream *, struct stream *src); EXT struct stream *stream_dup (struct stream *); EXT size_t stream_resize (struct stream *, size_t); EXT size_t stream_get_getp (struct stream *); EXT size_t stream_get_endp (struct stream *); EXT size_t stream_get_size (struct stream *); EXT u_char *stream_get_data (struct stream *); EXT void stream_set_getp (struct stream *, size_t); EXT void stream_forward_getp (struct stream *, size_t); EXT void stream_forward_endp (struct stream *, size_t); EXT void stream_put (struct stream *, const void *, size_t); EXT int stream_putc (struct stream *, u_char); EXT int stream_putc_at (struct stream *, size_t, u_char); EXT int stream_putw (struct stream *, u_int16_t); EXT int stream_putw_at (struct stream *, size_t, u_int16_t); EXT int stream_putl (struct stream *, u_int32_t); EXT int stream_putl_at (struct stream *, size_t, u_int32_t); EXT int stream_putq (struct stream *, uint64_t); EXT int stream_putq_at (struct stream *, size_t, uint64_t); EXT int stream_put_ipv4 (struct stream *, u_int32_t); EXT int stream_put_in_addr (struct stream *, struct in_addr *); EXT int stream_put_prefix (struct stream *, struct isis_prefix *); EXT void stream_get (void *, struct stream *, size_t); EXT u_char stream_getc (struct stream *); EXT u_char stream_getc_from (struct stream *, size_t); EXT u_int16_t stream_getw (struct stream *); EXT u_int16_t stream_getw_from (struct stream *, size_t); EXT u_int32_t stream_getl (struct stream *); EXT u_int32_t stream_getl_from (struct stream *, size_t); EXT uint64_t stream_getq (struct stream *); EXT uint64_t stream_getq_from (struct stream *, size_t); EXT u_int32_t stream_get_ipv4 (struct stream *); #undef stream_read #undef stream_write /* Deprecated: assumes blocking I/O. Will be removed. Use stream_read_try instead. */ EXT int stream_read (struct stream *, int, size_t); /* Deprecated: all file descriptors should already be non-blocking. Will be removed. Use stream_read_try instead. */ EXT int stream_read_unblock (struct stream *, int, size_t); /* Read up to size bytes into the stream. Return code: >0: number of bytes read 0: end-of-file -1: fatal error -2: transient error, should retry later (i.e. EAGAIN or EINTR) This is suitable for use with non-blocking file descriptors. */ EXT ssize_t stream_read_try(struct stream *, int, size_t); EXT ssize_t stream_recvmsg (struct stream *, int, struct msghdr *, int, size_t); EXT ssize_t stream_recvfrom (struct stream *, int, size_t, int, struct sockaddr *, socklen_t *); EXT size_t stream_write (struct stream *, const void *, size_t); EXT void stream_reset (struct stream *); EXT int stream_flush (struct stream *, int); EXT int stream_empty (struct stream *); /* is the stream empty? */ EXT u_char *stream_pnt (struct stream *); EXT int readn (int, u_char *, int); #undef EXT #endif /* _STREAM_H_ */ pmacct-0.14.0/src/isis/table.h0000640000175000017500000000461411734647141015060 0ustar paolopaolo/* * Routing Table * Copyright (C) 1998 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef _TABLE_H_ #define _TABLE_H_ /* Routing table top structure. */ struct route_table { struct route_node *top; }; /* Each routing entry. */ struct route_node { /* Actual prefix of this radix. */ struct isis_prefix p; /* Tree link. */ struct route_table *table; struct route_node *parent; struct route_node *link[2]; #define l_left link[0] #define l_right link[1] /* Lock of this radix */ unsigned int lock; /* Each node of route. */ void *info; /* Aggregation. */ void *aggregate; }; /* Prototypes. */ #if (!defined __TABLE_C) #define EXT extern #else #define EXT #endif EXT struct route_table *route_table_init (void); EXT void route_table_finish (struct route_table *); EXT void route_unlock_node (struct route_node *); EXT void route_node_delete (struct route_node *); EXT struct route_node *route_top (struct route_table *); EXT struct route_node *route_next (struct route_node *); EXT struct route_node *route_next_until (struct route_node *, struct route_node *); EXT struct route_node *route_node_get (struct route_table *, struct isis_prefix *); EXT struct route_node *route_node_lookup (struct route_table *, struct isis_prefix *); EXT struct route_node *route_lock_node (struct route_node *); EXT struct route_node *route_node_match (const struct route_table *, const struct isis_prefix *); EXT struct route_node *route_node_match_ipv4 (const struct route_table *, const struct in_addr *); #ifdef ENABLE_IPV6 EXT struct route_node *route_node_match_ipv6 (const struct route_table *, const struct in6_addr *); #endif /* ENABLE_IPV6 */ #undef EXT #endif /* _TABLE_H_ */ pmacct-0.14.0/src/isis/isis_route.c0000640000175000017500000004244511734672041016152 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_route.c * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * based on ../ospf6d/ospf6_route.[ch] * by Yasuhiro Ohara * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_ROUTE_C #include "pmacct.h" #include "isis.h" #include "linklist.h" #include "dict.h" #include "thread.h" #include "prefix.h" #include "table.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_circuit.h" #include "isis_tlv.h" #include "isis_lsp.h" #include "isis_pdu.h" #include "isis_network.h" #include "isis_misc.h" #include "isis_constants.h" #include "isis_adjacency.h" #include "isis_flags.h" #include "isisd.h" #include "isis_csm.h" #include "isis_route.h" #include "isis_spf.h" extern struct isis *isis; extern struct thread_master *master; static struct isis_nexthop * isis_nexthop_create (struct in_addr *ip, unsigned int ifindex) { struct listnode *node; struct isis_nexthop *nexthop; for (ALL_LIST_ELEMENTS_RO (isis->nexthops, node, nexthop)) { if (nexthop->ifindex != ifindex) continue; if (ip && memcmp (&nexthop->ip, ip, sizeof (struct in_addr)) != 0) continue; nexthop->lock++; return nexthop; } nexthop = calloc(1, sizeof (struct isis_nexthop)); if (!nexthop) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): ISIS-Rte: isis_nexthop_create: out of memory!\n"); } nexthop->ifindex = ifindex; memcpy (&nexthop->ip, ip, sizeof (struct in_addr)); listnode_add (isis->nexthops, nexthop); nexthop->lock++; return nexthop; } static void isis_nexthop_delete (struct isis_nexthop *nexthop) { nexthop->lock--; if (nexthop->lock == 0) { listnode_delete (isis->nexthops, nexthop); free(nexthop); } return; } static int nexthoplookup (struct list *nexthops, struct in_addr *ip, unsigned int ifindex) { struct listnode *node; struct isis_nexthop *nh; for (ALL_LIST_ELEMENTS_RO (nexthops, node, nh)) { if (!(memcmp (ip, &nh->ip, sizeof (struct in_addr))) && ifindex == nh->ifindex) return 1; } return 0; } #ifdef ENABLE_IPV6 static struct isis_nexthop6 * isis_nexthop6_new (struct in6_addr *ip6, unsigned int ifindex) { struct isis_nexthop6 *nexthop6; nexthop6 = calloc(1, sizeof (struct isis_nexthop6)); if (!nexthop6) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): ISIS-Rte: isis_nexthop_create6: out of memory!\n"); } nexthop6->ifindex = ifindex; memcpy (&nexthop6->ip6, ip6, sizeof (struct in6_addr)); nexthop6->lock++; return nexthop6; } static struct isis_nexthop6 * isis_nexthop6_create (struct in6_addr *ip6, unsigned int ifindex) { struct listnode *node; struct isis_nexthop6 *nexthop6; for (ALL_LIST_ELEMENTS_RO (isis->nexthops6, node, nexthop6)) { if (nexthop6->ifindex != ifindex) continue; if (ip6 && memcmp (&nexthop6->ip6, ip6, sizeof (struct in6_addr)) != 0) continue; nexthop6->lock++; return nexthop6; } nexthop6 = isis_nexthop6_new (ip6, ifindex); return nexthop6; } static void isis_nexthop6_delete (struct isis_nexthop6 *nexthop6) { nexthop6->lock--; if (nexthop6->lock == 0) { listnode_delete (isis->nexthops6, nexthop6); free(nexthop6); } return; } static int nexthop6lookup (struct list *nexthops6, struct in6_addr *ip6, unsigned int ifindex) { struct listnode *node; struct isis_nexthop6 *nh6; for (ALL_LIST_ELEMENTS_RO (nexthops6, node, nh6)) { if (!(memcmp (ip6, &nh6->ip6, sizeof (struct in6_addr))) && ifindex == nh6->ifindex) return 1; } return 0; } #endif /* ENABLE_IPV6 */ static void adjinfo2nexthop (struct list *nexthops, struct isis_adjacency *adj) { struct isis_nexthop *nh; struct listnode *node; struct in_addr *ipv4_addr; if (adj->ipv4_addrs == NULL) return; for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ipv4_addr)) { if (!nexthoplookup (nexthops, ipv4_addr, adj->circuit->interface->ifindex)) { nh = isis_nexthop_create (ipv4_addr, adj->circuit->interface->ifindex); listnode_add (nexthops, nh); } } } #ifdef ENABLE_IPV6 static void adjinfo2nexthop6 (struct list *nexthops6, struct isis_adjacency *adj) { struct listnode *node; struct in6_addr *ipv6_addr; struct isis_nexthop6 *nh6; if (!adj->ipv6_addrs) return; for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr)) { if (!nexthop6lookup (nexthops6, ipv6_addr, adj->circuit->interface->ifindex)) { nh6 = isis_nexthop6_create (ipv6_addr, adj->circuit->interface->ifindex); listnode_add (nexthops6, nh6); } } } #endif static struct isis_route_info * isis_route_info_new (uint32_t cost, uint32_t depth, u_char family, struct list *adjacencies) { struct isis_route_info *rinfo; struct isis_adjacency *adj; struct listnode *node; rinfo = calloc(1, sizeof (struct isis_route_info)); if (!rinfo) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): ISIS-Rte: isis_route_info_new: out of memory!\n"); return NULL; } if (family == AF_INET) { rinfo->nexthops = list_new (); for (ALL_LIST_ELEMENTS_RO (adjacencies, node, adj)) adjinfo2nexthop (rinfo->nexthops, adj); } #ifdef ENABLE_IPV6 if (family == AF_INET6) { rinfo->nexthops6 = list_new (); for (ALL_LIST_ELEMENTS_RO (adjacencies, node, adj)) adjinfo2nexthop6 (rinfo->nexthops6, adj); } #endif rinfo->cost = cost; rinfo->depth = depth; return rinfo; } static void isis_route_info_delete (struct isis_route_info *route_info) { if (route_info->nexthops) { route_info->nexthops->del = (void (*)(void *)) isis_nexthop_delete; list_delete (route_info->nexthops); } #ifdef ENABLE_IPV6 if (route_info->nexthops6) { route_info->nexthops6->del = (void (*)(void *)) isis_nexthop6_delete; list_delete (route_info->nexthops6); } #endif /* ENABLE_IPV6 */ free(route_info); } static int isis_route_info_same_attrib (struct isis_route_info *new, struct isis_route_info *old) { if (new->cost != old->cost) return 0; if (new->depth != old->depth) return 0; return 1; } static int isis_route_info_same (struct isis_route_info *new, struct isis_route_info *old, u_char family) { struct listnode *node; struct isis_nexthop *nexthop; #ifdef ENABLE_IPV6 struct isis_nexthop6 *nexthop6; #endif /* ENABLE_IPV6 */ if (!isis_route_info_same_attrib (new, old)) return 0; if (family == AF_INET) { for (ALL_LIST_ELEMENTS_RO (new->nexthops, node, nexthop)) if (nexthoplookup (old->nexthops, &nexthop->ip, nexthop->ifindex) == 0) return 0; for (ALL_LIST_ELEMENTS_RO (old->nexthops, node, nexthop)) if (nexthoplookup (new->nexthops, &nexthop->ip, nexthop->ifindex) == 0) return 0; } #ifdef ENABLE_IPV6 else if (family == AF_INET6) { for (ALL_LIST_ELEMENTS_RO (new->nexthops6, node, nexthop6)) if (nexthop6lookup (old->nexthops6, &nexthop6->ip6, nexthop6->ifindex) == 0) return 0; for (ALL_LIST_ELEMENTS_RO (old->nexthops6, node, nexthop6)) if (nexthop6lookup (new->nexthops6, &nexthop6->ip6, nexthop6->ifindex) == 0) return 0; } #endif /* ENABLE_IPV6 */ return 1; } static void isis_nexthops_merge (struct list *new, struct list *old) { struct listnode *node; struct isis_nexthop *nexthop; for (ALL_LIST_ELEMENTS_RO (new, node, nexthop)) { if (nexthoplookup (old, &nexthop->ip, nexthop->ifindex)) continue; listnode_add (old, nexthop); nexthop->lock++; } } #ifdef ENABLE_IPV6 static void isis_nexthops6_merge (struct list *new, struct list *old) { struct listnode *node; struct isis_nexthop6 *nexthop6; for (ALL_LIST_ELEMENTS_RO (new, node, nexthop6)) { if (nexthop6lookup (old, &nexthop6->ip6, nexthop6->ifindex)) continue; listnode_add (old, nexthop6); nexthop6->lock++; } } #endif /* ENABLE_IPV6 */ static void isis_route_info_merge (struct isis_route_info *new, struct isis_route_info *old, u_char family) { if (family == AF_INET) isis_nexthops_merge (new->nexthops, old->nexthops); #ifdef ENABLE_IPV6 else if (family == AF_INET6) isis_nexthops6_merge (new->nexthops6, old->nexthops6); #endif /* ENABLE_IPV6 */ return; } static int isis_route_info_prefer_new (struct isis_route_info *new, struct isis_route_info *old) { if (!CHECK_FLAG (old->flag, ISIS_ROUTE_FLAG_ACTIVE)) return 1; if (new->cost < old->cost) return 1; return 0; } struct isis_route_info * isis_route_create (struct isis_prefix *prefix, u_int32_t cost, u_int32_t depth, struct list *adjacencies, struct isis_area *area, int level) { struct route_node *route_node; struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL; u_char buff[BUFSIZ]; u_char family; family = prefix->family; /* for debugs */ isis_prefix2str (prefix, (char *) buff, BUFSIZ); rinfo_new = isis_route_info_new (cost, depth, family, adjacencies); if (!rinfo_new) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): ISIS-Rte (%s): isis_route_create: out of memory!\n", area->area_tag); return NULL; } if (family == AF_INET) route_node = route_node_get (area->route_table[level - 1], prefix); #ifdef ENABLE_IPV6 else if (family == AF_INET6) route_node = route_node_get (area->route_table6[level - 1], prefix); #endif /* ENABLE_IPV6 */ else return NULL; rinfo_old = route_node->info; if (!rinfo_old) { if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Rte (tag: %s, level: %u) route created: %s\n", area->area_tag, area->is_type, buff); SET_FLAG (rinfo_new->flag, ISIS_ROUTE_FLAG_ACTIVE); route_node->info = rinfo_new; return rinfo_new; } if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Rte (tag: %s, level: %u) route already exists: %s\n", area->area_tag, area->is_type, buff); if (isis_route_info_same (rinfo_new, rinfo_old, family)) { if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Rte (tag: %s, level: %u) route unchanged: %s\n", area->area_tag, area->is_type, buff); isis_route_info_delete (rinfo_new); route_info = rinfo_old; } else if (isis_route_info_same_attrib (rinfo_new, rinfo_old)) { /* merge the nexthop lists */ if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Rte (tag: %s, level: %u) route changed (same attribs): %s\n", area->area_tag, area->is_type, buff); isis_route_info_merge (rinfo_new, rinfo_old, family); isis_route_info_delete (rinfo_new); route_info = rinfo_old; UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC); } else { if (isis_route_info_prefer_new (rinfo_new, rinfo_old)) { if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Rte (tag: %s, level: %u) route changed: %s\n", area->area_tag, area->is_type, buff); isis_route_info_delete (rinfo_old); route_info = rinfo_new; } else { if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Rte (tag: %s, level: %u) route rejected: %s\n", area->area_tag, area->is_type, buff); isis_route_info_delete (rinfo_new); route_info = rinfo_old; } } SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ACTIVE); route_node->info = route_info; return route_info; } static void isis_route_delete (struct isis_prefix *prefix, struct route_table *table) { struct route_node *rode; struct isis_route_info *rinfo; char buff[BUFSIZ]; /* for log */ isis_prefix2str (prefix, buff, BUFSIZ); rode = route_node_get (table, prefix); rinfo = rode->info; if (rinfo == NULL) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Rte: tried to delete non-existant route: %s\n", buff); return; } isis_route_info_delete (rinfo); rode->info = NULL; return; } /* Validating routes in particular table. */ void isis_route_validate_table (struct isis_area *area, struct route_table *table) { struct route_node *rnode, *drnode; struct isis_route_info *rinfo; u_char buff[BUFSIZ]; for (rnode = route_top (table); rnode; rnode = route_next (rnode)) { if (rnode->info == NULL) continue; rinfo = rnode->info; if (config.debug) { isis_prefix2str (&rnode->p, (char *) buff, BUFSIZ); if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Rte (tag: %s, level: %u): route validate: %s %s\n", area->area_tag, area->is_type, (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE) ? "active" : "inactive"), buff); } if (!CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)) { /* Area is either L1 or L2 => we use level route tables directly for * validating => no problems with deleting routes. */ if (area->is_type != IS_LEVEL_1_AND_2) { isis_route_delete (&rnode->p, table); continue; } /* If area is L1L2, we work with merge table and therefore must * delete node from level tables as well before deleting route info. * FIXME: Is it performance problem? There has to be the better way. * Like not to deal with it here at all (see the next comment)? */ if (rnode->p.family == AF_INET) { drnode = route_node_get (area->route_table[0], &rnode->p); if (drnode->info == rnode->info) drnode->info = NULL; drnode = route_node_get (area->route_table[1], &rnode->p); if (drnode->info == rnode->info) drnode->info = NULL; } #ifdef ENABLE_IPV6 if (rnode->p.family == AF_INET6) { drnode = route_node_get (area->route_table6[0], &rnode->p); if (drnode->info == rnode->info) drnode->info = NULL; drnode = route_node_get (area->route_table6[1], &rnode->p); if (drnode->info == rnode->info) drnode->info = NULL; } #endif isis_route_delete (&rnode->p, table); } } } /* Function to validate route tables for L1L2 areas. In this case we can't use * level route tables directly, we have to merge them at first. L1 routes are * preferred over the L2 ones. * * Merge algorithm is trivial (at least for now). All L1 paths are copied into * merge table at first, then L2 paths are added if L1 path for same prefix * doesn't already exists there. * * FIXME: Is it right place to do it at all? Maybe we should push both levels * to the RIB with different zebra route types and let RIB handle this? */ void isis_route_validate_merge (struct isis_area *area, int family) { struct route_table *table = NULL; struct route_table *merge; struct route_node *rnode, *mrnode; merge = route_table_init (); if (family == AF_INET) table = area->route_table[0]; #ifdef ENABLE_IPV6 else if (family == AF_INET6) table = area->route_table6[0]; #endif for (rnode = route_top (table); rnode; rnode = route_next (rnode)) { if (rnode->info == NULL) continue; mrnode = route_node_get (merge, &rnode->p); mrnode->info = rnode->info; } if (family == AF_INET) table = area->route_table[1]; #ifdef ENABLE_IPV6 else if (family == AF_INET6) table = area->route_table6[1]; #endif for (rnode = route_top (table); rnode; rnode = route_next (rnode)) { if (rnode->info == NULL) continue; mrnode = route_node_get (merge, &rnode->p); if (mrnode->info != NULL) continue; mrnode->info = rnode->info; } isis_route_validate_table (area, merge); route_table_finish (merge); } /* Walk through route tables and propagate necessary changes into RIB. In case * of L1L2 area, level tables have to be merged at first. */ int isis_route_validate (struct thread *thread) { struct isis_area *area; area = THREAD_ARG (thread); if (area->is_type == IS_LEVEL_1) { isis_route_validate_table (area, area->route_table[0]); goto validate_ipv6; } if (area->is_type == IS_LEVEL_2) { isis_route_validate_table (area, area->route_table[1]); goto validate_ipv6; } isis_route_validate_merge (area, AF_INET); validate_ipv6: #ifdef ENABLE_IPV6 if (area->is_type == IS_LEVEL_1) { isis_route_validate_table (area, area->route_table6[0]); return ISIS_OK; } if (area->is_type == IS_LEVEL_2) { isis_route_validate_table (area, area->route_table6[1]); return ISIS_OK; } isis_route_validate_merge (area, AF_INET6); #endif return ISIS_OK; } pmacct-0.14.0/src/isis/isis_dynhn.c0000640000175000017500000000532311731471736016133 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_dynhn.c * Dynamic hostname cache * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_DYNHN_C #include "pmacct.h" #include "isis.h" #include "linklist.h" #include "dict.h" #include "thread.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_flags.h" #include "isis_circuit.h" #include "isisd.h" #include "isis_dynhn.h" #include "isis_misc.h" #include "isis_constants.h" extern struct host host; void dyn_cache_init (void) { dyn_cache = NULL; dyn_cache = list_new (); } int dyn_cache_cleanup () { struct listnode *node, *nnode; struct isis_dynhn *dyn; time_t now = time (NULL); isis->t_dync_clean = NULL; for (ALL_LIST_ELEMENTS (dyn_cache, node, nnode, dyn)) { if ((now - dyn->refresh) < (MAX_AGE + 120)) continue; list_delete_node (dyn_cache, node); free(dyn); } return ISIS_OK; } struct isis_dynhn * dynhn_find_by_id (u_char * id) { struct listnode *node = NULL; struct isis_dynhn *dyn = NULL; for (ALL_LIST_ELEMENTS_RO (dyn_cache, node, dyn)) if (memcmp (dyn->id, id, ISIS_SYS_ID_LEN) == 0) return dyn; return NULL; } void isis_dynhn_insert (u_char * id, struct hostname *hostname, int level) { struct isis_dynhn *dyn; dyn = dynhn_find_by_id (id); if (dyn) { memcpy (&dyn->name, hostname, hostname->namelen + 1); memcpy (dyn->id, id, ISIS_SYS_ID_LEN); dyn->refresh = time (NULL); return; } dyn = calloc(1, sizeof (struct isis_dynhn)); if (!dyn) { Log(LOG_WARNING, "WARN (default/core/ISIS ): isis_dynhn_insert(): out of memory!\n"); return; } /* we also copy the length */ memcpy (&dyn->name, hostname, hostname->namelen + 1); memcpy (dyn->id, id, ISIS_SYS_ID_LEN); dyn->refresh = time (NULL); dyn->level = level; listnode_add (dyn_cache, dyn); return; } pmacct-0.14.0/src/isis/checksum.h0000640000175000017500000000040111705253204015550 0ustar paolopaolo#ifndef _CHECKSUM_H_ #define _CHECKSUM_H_ #if (!defined __CHECKSUM_C) #define EXT extern #else #define EXT #endif EXT int in_cksum(void *, int); EXT u_int16_t fletcher_checksum(u_char *, const size_t, const uint16_t); #undef EXT #endif /* _CHECKSUM_H_ */ pmacct-0.14.0/src/isis/linklist.c0000640000175000017500000001344111727477131015615 0ustar paolopaolo/* Generic linked list routine. * Copyright (C) 1997, 2000 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #define __LINKLIST_C #include "pmacct.h" #include "isis.h" #include "linklist.h" /* Allocate new list. */ struct list * list_new (void) { return calloc(1, sizeof (struct list)); } /* Free list. */ void list_free (struct list *l) { free(l); } /* Allocate new listnode. Internal use only. */ static struct listnode * listnode_new (void) { return calloc(1, sizeof (struct listnode)); } /* Free listnode. */ static void listnode_free (struct listnode *node) { free(node); } /* Add new data to the list. */ void listnode_add (struct list *list, void *val) { struct listnode *node; assert (val != NULL); node = listnode_new (); node->prev = list->tail; node->data = val; if (list->head == NULL) list->head = node; else list->tail->next = node; list->tail = node; list->count++; } /* * Add a node to the list. If the list was sorted according to the * cmp function, insert a new node with the given val such that the * list remains sorted. The new node is always inserted; there is no * notion of omitting duplicates. */ void listnode_add_sort (struct list *list, void *val) { struct listnode *n; struct listnode *new; assert (val != NULL); new = listnode_new (); new->data = val; if (list->cmp) { for (n = list->head; n; n = n->next) { if ((*list->cmp) (val, n->data) < 0) { new->next = n; new->prev = n->prev; if (n->prev) n->prev->next = new; else list->head = new; n->prev = new; list->count++; return; } } } new->prev = list->tail; if (list->tail) list->tail->next = new; else list->head = new; list->tail = new; list->count++; } void listnode_add_after (struct list *list, struct listnode *pp, void *val) { struct listnode *nn; assert (val != NULL); nn = listnode_new (); nn->data = val; if (pp == NULL) { if (list->head) list->head->prev = nn; else list->tail = nn; nn->next = list->head; nn->prev = pp; list->head = nn; } else { if (pp->next) pp->next->prev = nn; else list->tail = nn; nn->next = pp->next; nn->prev = pp; pp->next = nn; } list->count++; } /* Delete specific date pointer from the list. */ void listnode_delete (struct list *list, void *val) { struct listnode *node; assert(list); for (node = list->head; node; node = node->next) { if (node->data == val) { if (node->prev) node->prev->next = node->next; else list->head = node->next; if (node->next) node->next->prev = node->prev; else list->tail = node->prev; list->count--; listnode_free (node); return; } } } /* Return first node's data if it is there. */ void * listnode_head (struct list *list) { struct listnode *node; assert(list); node = list->head; if (node) return node->data; return NULL; } /* Delete all listnode from the list. */ void list_delete_all_node (struct list *list) { struct listnode *node; struct listnode *next; assert(list); for (node = list->head; node; node = next) { next = node->next; if (list->del) (*list->del) (node->data); listnode_free (node); } list->head = list->tail = NULL; list->count = 0; } /* Delete all listnode then free list itself. */ void list_delete (struct list *list) { assert(list); list_delete_all_node (list); list_free (list); } /* Lookup the node which has given data. */ struct listnode * listnode_lookup (struct list *list, void *data) { struct listnode *node; assert(list); for (node = listhead(list); node; node = listnextnode (node)) if (data == listgetdata (node)) return node; return NULL; } /* Delete the node from list. For ospfd and ospf6d. */ void list_delete_node (struct list *list, struct listnode *node) { if (node->prev) node->prev->next = node->next; else list->head = node->next; if (node->next) node->next->prev = node->prev; else list->tail = node->prev; list->count--; listnode_free (node); } /* ospf_spf.c */ void list_add_node_prev (struct list *list, struct listnode *current, void *val) { struct listnode *node; assert (val != NULL); node = listnode_new (); node->next = current; node->data = val; if (current->prev == NULL) list->head = node; else current->prev->next = node; node->prev = current->prev; current->prev = node; list->count++; } /* ospf_spf.c */ void list_add_node_next (struct list *list, struct listnode *current, void *val) { struct listnode *node; assert (val != NULL); node = listnode_new (); node->prev = current; node->data = val; if (current->next == NULL) list->tail = node; else current->next->prev = node; node->next = current->next; current->next = node; list->count++; } /* ospf_spf.c */ void list_add_list (struct list *l, struct list *m) { struct listnode *n; for (n = listhead (m); n; n = listnextnode (n)) listnode_add (l, n->data); } pmacct-0.14.0/src/isis/prefix.c0000640000175000017500000003360511734647141015263 0ustar paolopaolo/* * Prefix related functions. * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #define __PREFIX_C #include "pmacct.h" #include "isis.h" #include "prefix.h" #include "sockunion.h" /* Maskbit. */ static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; /* Number of bits in prefix type. */ #ifndef PNBBY #define PNBBY 8 #endif /* PNBBY */ #define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff) /* If n includes p prefix then return 1 else return 0. */ int isis_prefix_match (const struct isis_prefix *n, const struct isis_prefix *p) { int offset; int shift; const u_char *np, *pp; /* If n's prefix is longer than p's one return 0. */ if (n->prefixlen > p->prefixlen) return 0; /* Set both prefix's head pointer. */ np = (const u_char *)&n->u.prefix; pp = (const u_char *)&p->u.prefix; offset = n->prefixlen / PNBBY; shift = n->prefixlen % PNBBY; if (shift) if (maskbit[shift] & (np[offset] ^ pp[offset])) return 0; while (offset--) if (np[offset] != pp[offset]) return 0; return 1; } /* Copy prefix from src to dest. */ void isis_prefix_copy (struct isis_prefix *dest, const struct isis_prefix *src) { dest->family = src->family; dest->prefixlen = src->prefixlen; if (src->family == AF_INET) dest->u.prefix4 = src->u.prefix4; #ifdef ENABLE_IPV6 else if (src->family == AF_INET6) dest->u.prefix6 = src->u.prefix6; #endif /* ENABLE_IPV6 */ else { Log(LOG_ERR, "ERROR ( default/core/ISIS ): isis_prefix_copy(): Unknown address family %d\n", src->family); assert (0); } dest->adv_router = src->adv_router; } /* * Return 1 if the address/netmask contained in the prefix structure * is the same, and else return 0. For this routine, 'same' requires * that not only the prefix length and the network part be the same, * but also the host part. Thus, 10.0.0.1/8 and 10.0.0.2/8 are not * the same. Note that this routine has the same return value sense * as '==' (which is different from isis_prefix_cmp). */ int isis_prefix_same (const struct isis_prefix *p1, const struct isis_prefix *p2) { if (p1->family == p2->family && p1->prefixlen == p2->prefixlen) { if (p1->family == AF_INET) if (IPV4_ADDR_SAME (&p1->u.prefix, &p2->u.prefix)) return 1; #ifdef ENABLE_IPV6 if (p1->family == AF_INET6 ) if (IPV6_ADDR_SAME (&p1->u.prefix, &p2->u.prefix)) return 1; #endif /* ENABLE_IPV6 */ } return 0; } /* * Return 0 if the network prefixes represented by the struct prefix * arguments are the same prefix, and 1 otherwise. Network prefixes * are considered the same if the prefix lengths are equal and the * network parts are the same. Host bits (which are considered masked * by the prefix length) are not significant. Thus, 10.0.0.1/8 and * 10.0.0.2/8 are considered equivalent by this routine. Note that * this routine has the same return sense as strcmp (which is different * from isis_prefix_same). */ int isis_prefix_cmp (const struct isis_prefix *p1, const struct isis_prefix *p2) { int offset; int shift; /* Set both prefix's head pointer. */ const u_char *pp1 = (const u_char *)&p1->u.prefix; const u_char *pp2 = (const u_char *)&p2->u.prefix; if (p1->family != p2->family || p1->prefixlen != p2->prefixlen) return 1; offset = p1->prefixlen / 8; shift = p1->prefixlen % 8; if (shift) if (maskbit[shift] & (pp1[offset] ^ pp2[offset])) return 1; while (offset--) if (pp1[offset] != pp2[offset]) return 1; return 0; } /* Return prefix family type string. */ const char * isis_prefix_family_str (const struct isis_prefix *p) { if (p->family == AF_INET) return "inet"; #ifdef ENABLE_IPV6 if (p->family == AF_INET6) return "inet6"; #endif /* ENABLE_IPV6 */ return "unspec"; } /* Allocate new prefix_ipv4 structure. */ struct prefix_ipv4 * isis_prefix_ipv4_new () { struct prefix_ipv4 *p; /* Call isis_prefix_new to allocate a full-size struct isis_prefix to avoid problems where the struct prefix_ipv4 is cast to struct isis_prefix and unallocated bytes were being referenced (e.g. in structure assignments). */ p = (struct prefix_ipv4 *)isis_prefix_new(); p->family = AF_INET; return p; } /* When string format is invalid return 0. */ int isis_str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p) { int ret; int plen; char *pnt; char *cp; /* Find slash inside string. */ pnt = strchr (str, '/'); /* String doesn't contail slash. */ if (pnt == NULL) { /* Convert string to prefix. */ ret = inet_aton (str, &p->prefix); if (ret == 0) return 0; /* If address doesn't contain slash we assume it host address. */ p->family = AF_INET; p->prefixlen = IPV4_MAX_BITLEN; return ret; } else { cp = calloc(1, (pnt - str) + 1); strncpy (cp, str, pnt - str); *(cp + (pnt - str)) = '\0'; ret = inet_aton (cp, &p->prefix); free(cp); /* Get prefix length. */ plen = (u_char) atoi (++pnt); if (plen > IPV4_MAX_PREFIXLEN) return 0; p->family = AF_INET; p->prefixlen = plen; } return ret; } /* Convert masklen into IP address's netmask. */ void isis_masklen2ip (int masklen, struct in_addr *netmask) { u_char *pnt; int bit; int offset; memset (netmask, 0, sizeof (struct in_addr)); pnt = (unsigned char *) netmask; offset = masklen / 8; bit = masklen % 8; while (offset--) *pnt++ = 0xff; if (bit) *pnt = maskbit[bit]; } /* Convert IP address's netmask into integer. We assume netmask is sequential one. Argument netmask should be network byte order. */ u_char isis_ip_masklen (struct in_addr netmask) { u_char len; u_char *pnt; u_char *end; u_char val; len = 0; pnt = (u_char *) &netmask; end = pnt + 4; while ((pnt < end) && (*pnt == 0xff)) { len+= 8; pnt++; } if (pnt < end) { val = *pnt; while (val) { len++; val <<= 1; } } return len; } /* Apply mask to IPv4 prefix. */ void isis_apply_mask_ipv4 (struct prefix_ipv4 *p) { u_char *pnt; int index; int offset; index = p->prefixlen / 8; if (index < 4) { pnt = (u_char *) &p->prefix; offset = p->prefixlen % 8; pnt[index] &= maskbit[offset]; index++; while (index < 4) pnt[index++] = 0; } } #ifdef ENABLE_IPV6 /* Allocate a new ip version 6 route */ struct prefix_ipv6 * isis_prefix_ipv6_new (void) { struct prefix_ipv6 *p; /* Allocate a full-size struct isis_prefix to avoid problems with structure size mismatches. */ p = (struct prefix_ipv6 *)isis_prefix_new(); p->family = AF_INET6; return p; } /* Free prefix for IPv6. */ void isis_prefix_ipv6_free (struct prefix_ipv6 *p) { isis_prefix_free((struct isis_prefix *)p); } /* If given string is valid return pin6 else return NULL */ int isis_str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p) { char *pnt; char *cp; int ret; pnt = strchr (str, '/'); /* If string doesn't contain `/' treat it as host route. */ if (pnt == NULL) { ret = inet_pton (AF_INET6, str, &p->prefix); if (ret == 0) return 0; p->prefixlen = IPV6_MAX_BITLEN; } else { int plen; cp = calloc(0, (pnt - str) + 1); strncpy (cp, str, pnt - str); *(cp + (pnt - str)) = '\0'; ret = inet_pton (AF_INET6, cp, &p->prefix); free (cp); if (ret == 0) return 0; plen = (u_char) atoi (++pnt); if (plen > 128) return 0; p->prefixlen = plen; } p->family = AF_INET6; return ret; } /* Convert struct in6_addr netmask into integer. * FIXME return u_char as isis_ip_maskleni() does. */ int isis_ip6_masklen (struct in6_addr netmask) { int len = 0; unsigned char val; unsigned char *pnt; pnt = (unsigned char *) & netmask; while ((*pnt == 0xff) && len < 128) { len += 8; pnt++; } if (len < 128) { val = *pnt; while (val) { len++; val <<= 1; } } return len; } void isis_masklen2ip6 (int masklen, struct in6_addr *netmask) { unsigned char *pnt; int bit; int offset; memset (netmask, 0, sizeof (struct in6_addr)); pnt = (unsigned char *) netmask; offset = masklen / 8; bit = masklen % 8; while (offset--) *pnt++ = 0xff; if (bit) *pnt = maskbit[bit]; } void isis_apply_mask_ipv6 (struct prefix_ipv6 *p) { u_char *pnt; int index; int offset; index = p->prefixlen / 8; if (index < 16) { pnt = (u_char *) &p->prefix; offset = p->prefixlen % 8; pnt[index] &= maskbit[offset]; index++; while (index < 16) pnt[index++] = 0; } } void isis_str2in6_addr (const char *str, struct in6_addr *addr) { int i; unsigned int x; /* %x must point to unsinged int */ for (i = 0; i < 16; i++) { sscanf (str + (i * 2), "%02x", &x); addr->s6_addr[i] = x & 0xff; } } #endif /* ENABLE_IPV6 */ void isis_apply_mask (struct isis_prefix *p) { switch (p->family) { case AF_INET: isis_apply_mask_ipv4 ((struct prefix_ipv4 *)p); break; #ifdef ENABLE_IPV6 case AF_INET6: isis_apply_mask_ipv6 ((struct prefix_ipv6 *)p); break; #endif /* ENABLE_IPV6 */ default: break; } return; } /* Utility function of convert between struct isis_prefix <=> union sockunion. * FIXME This function isn't used anywhere. */ struct isis_prefix * sockunion2prefix (const union sockunion *dest, const union sockunion *mask) { if (dest->sa.sa_family == AF_INET) { struct prefix_ipv4 *p; p = isis_prefix_ipv4_new (); p->family = AF_INET; p->prefix = dest->sin.sin_addr; p->prefixlen = isis_ip_masklen (mask->sin.sin_addr); return (struct isis_prefix *) p; } #ifdef ENABLE_IPV6 if (dest->sa.sa_family == AF_INET6) { struct prefix_ipv6 *p; p = isis_prefix_ipv6_new (); p->family = AF_INET6; p->prefixlen = isis_ip6_masklen (mask->sin6.sin6_addr); memcpy (&p->prefix, &dest->sin6.sin6_addr, sizeof (struct in6_addr)); return (struct isis_prefix *) p; } #endif /* ENABLE_IPV6 */ return NULL; } /* Utility function of convert between struct isis_prefix <=> union sockunion. */ struct isis_prefix * sockunion2hostprefix (const union sockunion *su) { if (su->sa.sa_family == AF_INET) { struct prefix_ipv4 *p; p = isis_prefix_ipv4_new (); p->family = AF_INET; p->prefix = su->sin.sin_addr; p->prefixlen = IPV4_MAX_BITLEN; return (struct isis_prefix *) p; } #ifdef ENABLE_IPV6 if (su->sa.sa_family == AF_INET6) { struct prefix_ipv6 *p; p = isis_prefix_ipv6_new (); p->family = AF_INET6; p->prefixlen = IPV6_MAX_BITLEN; memcpy (&p->prefix, &su->sin6.sin6_addr, sizeof (struct in6_addr)); return (struct isis_prefix *) p; } #endif /* ENABLE_IPV6 */ return NULL; } int isis_prefix_blen (const struct isis_prefix *p) { switch (p->family) { case AF_INET: return IPV4_MAX_BYTELEN; break; #ifdef ENABLE_IPV6 case AF_INET6: return IPV6_MAX_BYTELEN; break; #endif /* ENABLE_IPV6 */ } return 0; } /* Generic function for conversion string to struct prefix. */ int isis_str2prefix (const char *str, struct isis_prefix *p) { int ret; /* First we try to convert string to struct prefix_ipv4. */ ret = isis_str2prefix_ipv4 (str, (struct prefix_ipv4 *) p); if (ret) return ret; #ifdef ENABLE_IPV6 /* Next we try to convert string to struct prefix_ipv6. */ ret = isis_str2prefix_ipv6 (str, (struct prefix_ipv6 *) p); if (ret) return ret; #endif /* ENABLE_IPV6 */ return 0; } int isis_prefix2str (const struct isis_prefix *p, char *str, int size) { char buf[BUFSIZ]; inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ); snprintf (str, size, "%s/%d", buf, p->prefixlen); return 0; } struct isis_prefix * isis_prefix_new () { struct isis_prefix *p; p = calloc(1, sizeof *p); return p; } /* Free prefix structure. */ void isis_prefix_free (struct isis_prefix *p) { free(p); } /* Utility function to convert ipv4 netmask to prefixes ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16" ex.) "1.0.0.0" NULL => "1.0.0.0/8" */ int netmask_isis_str2prefix_str (const char *net_str, const char *mask_str, char *prefix_str) { struct in_addr network; struct in_addr mask; u_char prefixlen; u_int32_t destination; int ret; ret = inet_aton (net_str, &network); if (! ret) return 0; if (mask_str) { ret = inet_aton (mask_str, &mask); if (! ret) return 0; prefixlen = isis_ip_masklen (mask); } else { destination = ntohl (network.s_addr); if (network.s_addr == 0) prefixlen = 0; else if (IN_CLASSC (destination)) prefixlen = 24; else if (IN_CLASSB (destination)) prefixlen = 16; else if (IN_CLASSA (destination)) prefixlen = 8; else return 0; } sprintf (prefix_str, "%s/%d", net_str, prefixlen); return 1; } #ifdef ENABLE_IPV6 /* Utility function for making IPv6 address string. */ const char * isis_inet6_ntoa (struct in6_addr addr) { static char buf[INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, &addr, buf, INET6_ADDRSTRLEN); return buf; } #endif /* ENABLE_IPV6 */ pmacct-0.14.0/src/isis/linklist.h0000640000175000017500000000767111705253204015617 0ustar paolopaolo/* Generic linked list * Copyright (C) 1997, 2000 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef _LINKLIST_H_ #define _LINKLIST_H_ /* listnodes must always contain data to be valid. Adding an empty node * to a list is invalid */ struct listnode { struct listnode *next; struct listnode *prev; /* private member, use getdata() to retrieve, do not access directly */ void *data; }; struct list { struct listnode *head; struct listnode *tail; /* invariant: count is the number of listnodes in the list */ unsigned int count; /* * Returns -1 if val1 < val2, 0 if equal?, 1 if val1 > val2. * Used as definition of sorted for listnode_add_sort */ int (*cmp) (void *val1, void *val2); /* callback to free user-owned data when listnode is deleted. supplying * this callback is very much encouraged! */ void (*del) (void *val); }; #define listnextnode(X) ((X)->next) #define listhead(X) ((X)->head) #define listtail(X) ((X)->tail) #define listcount(X) ((X)->count) #define list_isempty(X) ((X)->head == NULL && (X)->tail == NULL) #define listgetdata(X) (assert((X)->data != NULL), (X)->data) /* Prototypes. */ #if (!defined __LINKLIST_C) #define EXT extern #else #define EXT #endif EXT struct list *list_new(void); /* encouraged: set list.del callback on new lists */ EXT void list_free (struct list *); EXT void listnode_add (struct list *, void *); EXT void listnode_add_sort (struct list *, void *); EXT void listnode_add_after (struct list *, struct listnode *, void *); EXT void listnode_delete (struct list *, void *); EXT struct listnode *listnode_lookup (struct list *, void *); EXT void *listnode_head (struct list *); EXT void list_delete (struct list *); EXT void list_delete_all_node (struct list *); #undef EXT /* List iteration macro. * Usage: for (ALL_LIST_ELEMENTS (...) { ... } * It is safe to delete the listnode using this macro. */ #define ALL_LIST_ELEMENTS(list,node,nextnode,data) \ (node) = listhead(list); \ (node) != NULL && \ ((data) = listgetdata(node),(nextnode) = listnextnode(node), 1); \ (node) = (nextnode) /* read-only list iteration macro. * Usage: as per ALL_LIST_ELEMENTS, but not safe to delete the listnode Only * use this macro when it is *immediately obvious* the listnode is not * deleted in the body of the loop. Does not have forward-reference overhead * of previous macro. */ #define ALL_LIST_ELEMENTS_RO(list,node,data) \ (node) = listhead(list); \ (node) != NULL && ((data) = listgetdata(node), 1); \ (node) = listnextnode(node) /* these *do not* cleanup list nodes and referenced data, as the functions * do - these macros simply {de,at}tach a listnode from/to a list. */ /* List node attach macro. */ #define LISTNODE_ATTACH(L,N) \ do { \ (N)->prev = (L)->tail; \ if ((L)->head == NULL) \ (L)->head = (N); \ else \ (L)->tail->next = (N); \ (L)->tail = (N); \ (L)->count++; \ } while (0) /* List node detach macro. */ #define LISTNODE_DETACH(L,N) \ do { \ if ((N)->prev) \ (N)->prev->next = (N)->next; \ else \ (L)->head = (N)->next; \ if ((N)->next) \ (N)->next->prev = (N)->prev; \ else \ (L)->tail = (N)->prev; \ (L)->count--; \ } while (0) #endif /* _LINKLIST_H_ */ pmacct-0.14.0/src/isis/isis_circuit.c0000640000175000017500000001515011734647141016452 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_circuit.c * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_CIRCUIT_C #include "pmacct.h" #include "isis.h" #ifndef ETHER_ADDR_LEN #define ETHER_ADDR_LEN ETHERADDRL #endif #include "linklist.h" #include "thread.h" #include "hash.h" #include "prefix.h" #include "stream.h" #include "dict.h" #include "iso.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_circuit.h" #include "isis_tlv.h" #include "isis_lsp.h" #include "isis_pdu.h" #include "isis_network.h" #include "isis_misc.h" #include "isis_constants.h" #include "isis_adjacency.h" #include "isis_flags.h" #include "isisd.h" #include "isis_csm.h" #include "isis_events.h" extern struct thread_master *master; extern struct isis *isis; /* * Prototypes. */ void isis_circuit_down(struct isis_circuit *); struct isis_circuit * isis_circuit_new () { struct isis_circuit *circuit; int i; circuit = calloc(1, sizeof (struct isis_circuit)); if (circuit) { /* set default metrics for circuit */ for (i = 0; i < 2; i++) { circuit->metrics[i].metric_default = DEFAULT_CIRCUIT_METRICS; circuit->metrics[i].metric_expense = METRICS_UNSUPPORTED; circuit->metrics[i].metric_error = METRICS_UNSUPPORTED; circuit->metrics[i].metric_delay = METRICS_UNSUPPORTED; circuit->te_metric[i] = DEFAULT_CIRCUIT_METRICS; } } else { Log(LOG_ERR, "ERROR ( default/core/ISIS ): Can't calloc isis circuit\n"); return NULL; } return circuit; } void isis_circuit_configure (struct isis_circuit *circuit, struct isis_area *area) { int i; circuit->area = area; /* * The level for the circuit is same as for the area, unless configured * otherwise. */ circuit->circuit_is_type = area->is_type; /* * Default values */ for (i = 0; i < 2; i++) { circuit->hello_interval[i] = HELLO_INTERVAL; circuit->hello_multiplier[i] = HELLO_MULTIPLIER; circuit->csnp_interval[i] = CSNP_INTERVAL; circuit->psnp_interval[i] = PSNP_INTERVAL; circuit->u.bc.priority[i] = DEFAULT_PRIORITY; } if (circuit->circ_type == CIRCUIT_T_BROADCAST) { circuit->u.bc.adjdb[0] = list_new (); circuit->u.bc.adjdb[1] = list_new (); circuit->u.bc.pad_hellos = 1; } circuit->lsp_interval = LSP_INTERVAL; /* * Add the circuit into area */ listnode_add (area->circuit_list, circuit); circuit->idx = flags_get_index (&area->flags); circuit->lsp_queue = list_new (); return; } void isis_circuit_deconfigure (struct isis_circuit *circuit, struct isis_area *area) { /* destroy adjacencies */ if (circuit->u.bc.adjdb[0]) isis_adjdb_iterate (circuit->u.bc.adjdb[0], (void(*) (struct isis_adjacency *, void *)) isis_delete_adj, circuit->u.bc.adjdb[0]); if (circuit->u.bc.adjdb[1]) isis_adjdb_iterate (circuit->u.bc.adjdb[1], (void(*) (struct isis_adjacency *, void *)) isis_delete_adj, circuit->u.bc.adjdb[1]); /* Remove circuit from area */ listnode_delete (area->circuit_list, circuit); /* Free the index of SRM and SSN flags */ flags_free_index (&area->flags, circuit->idx); return; } void isis_circuit_del (struct isis_circuit *circuit) { if (!circuit) return; if (circuit->circ_type == CIRCUIT_T_BROADCAST) { /* destroy adjacency databases */ if (circuit->u.bc.adjdb[0]) list_delete (circuit->u.bc.adjdb[0]); if (circuit->u.bc.adjdb[1]) list_delete (circuit->u.bc.adjdb[1]); /* destroy neighbour lists */ if (circuit->u.bc.lan_neighs[0]) list_delete (circuit->u.bc.lan_neighs[0]); if (circuit->u.bc.lan_neighs[1]) list_delete (circuit->u.bc.lan_neighs[1]); /* destroy addresses */ } if (circuit->ip_addrs) list_delete (circuit->ip_addrs); #ifdef ENABLE_IPV6 if (circuit->ipv6_link) list_delete (circuit->ipv6_link); if (circuit->ipv6_non_link) list_delete (circuit->ipv6_non_link); #endif /* ENABLE_IPV6 */ /* and lastly the circuit itself */ free(circuit); return; } void isis_circuit_up (struct isis_circuit *circuit) { if (circuit->circ_type == CIRCUIT_T_P2P) { /* initializing the hello send threads * for a ptp IF */ thread_add_event (master, send_p2p_hello, circuit, 0); } /* if needed, initialize the circuit streams (most likely not) */ if (circuit->rcv_stream == NULL) circuit->rcv_stream = stream_new (ISO_MTU (circuit)); if (circuit->snd_stream == NULL) circuit->snd_stream = stream_new (ISO_MTU (circuit)); // isis_sock_init (circuit); // THREAD_TIMER_ON (master, circuit->t_read, isis_receive, circuit, circuit->fd); } void isis_circuit_down (struct isis_circuit *circuit) { /* Cancel all active threads -- FIXME: wrong place */ /* HT: Read thread if GNU_LINUX, TIMER thread otherwise. */ THREAD_OFF (circuit->t_read); if (circuit->circ_type == CIRCUIT_T_BROADCAST) { THREAD_TIMER_OFF (circuit->u.bc.t_send_lan_hello[0]); THREAD_TIMER_OFF (circuit->u.bc.t_send_lan_hello[1]); THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[0]); THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[1]); } else if (circuit->circ_type == CIRCUIT_T_P2P) { THREAD_TIMER_OFF (circuit->u.p2p.t_send_p2p_hello); } if (circuit->t_send_psnp[0]) { THREAD_TIMER_OFF (circuit->t_send_psnp[0]); } if (circuit->t_send_psnp[1]) { THREAD_TIMER_OFF (circuit->t_send_psnp[1]); } /* close the socket */ close (circuit->fd); return; } void circuit_update_nlpids (struct isis_circuit *circuit) { circuit->nlpids.count = 0; if (circuit->ip_router) { circuit->nlpids.nlpids[0] = NLPID_IP; circuit->nlpids.count++; } #ifdef ENABLE_IPV6 if (circuit->ipv6_router) { circuit->nlpids.nlpids[circuit->nlpids.count] = NLPID_IPV6; circuit->nlpids.count++; } #endif /* ENABLE_IPV6 */ return; } pmacct-0.14.0/src/isis/isis_lsp.c0000640000175000017500000013567711734671467015637 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_lsp.c * LSP processing * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_LSP_C #include "pmacct.h" #include "isis.h" #include "linklist.h" #include "thread.h" #include "stream.h" #include "prefix.h" #include "hash.h" #include "dict.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_circuit.h" #include "isisd.h" #include "isis_tlv.h" #include "isis_lsp.h" #include "isis_pdu.h" #include "isis_dynhn.h" #include "isis_misc.h" #include "isis_flags.h" #include "isis_csm.h" #include "isis_adjacency.h" #include "isis_spf.h" #define LSP_MEMORY_PREASSIGN extern struct isis *isis; /* staticly assigned vars for printing purposes */ char lsp_bits_string[200]; /* FIXME: enough ? */ int lsp_id_cmp (u_char * id1, u_char * id2) { return memcmp (id1, id2, ISIS_SYS_ID_LEN + 2); } dict_t * lsp_db_init (void) { dict_t *dict; dict = dict_create (DICTCOUNT_T_MAX, (dict_comp_t) lsp_id_cmp); return dict; } struct isis_lsp * lsp_search (u_char * id, dict_t * lspdb) { dnode_t *node; node = dict_lookup (lspdb, id); if (node) return (struct isis_lsp *) dnode_get (node); return NULL; } static void lsp_clear_data (struct isis_lsp *lsp) { if (!lsp) return; if (lsp->own_lsp) { if (lsp->tlv_data.nlpids) free(lsp->tlv_data.nlpids); if (lsp->tlv_data.hostname) free(lsp->tlv_data.hostname); } if (lsp->tlv_data.is_neighs) list_delete (lsp->tlv_data.is_neighs); if (lsp->tlv_data.te_is_neighs) list_delete (lsp->tlv_data.te_is_neighs); if (lsp->tlv_data.area_addrs) list_delete (lsp->tlv_data.area_addrs); if (lsp->tlv_data.es_neighs) list_delete (lsp->tlv_data.es_neighs); if (lsp->tlv_data.ipv4_addrs) list_delete (lsp->tlv_data.ipv4_addrs); if (lsp->tlv_data.ipv4_int_reachs) list_delete (lsp->tlv_data.ipv4_int_reachs); if (lsp->tlv_data.ipv4_ext_reachs) list_delete (lsp->tlv_data.ipv4_ext_reachs); if (lsp->tlv_data.te_ipv4_reachs) list_delete (lsp->tlv_data.te_ipv4_reachs); #ifdef ENABLE_IPV6 if (lsp->tlv_data.ipv6_addrs) list_delete (lsp->tlv_data.ipv6_addrs); if (lsp->tlv_data.ipv6_reachs) list_delete (lsp->tlv_data.ipv6_reachs); #endif /* ENABLE_IPV6 */ memset (&lsp->tlv_data, 0, sizeof (struct tlvs)); return; } static void lsp_destroy (struct isis_lsp *lsp) { if (!lsp) return; lsp_clear_data (lsp); if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0 && lsp->lspu.frags) { list_delete (lsp->lspu.frags); } if (lsp->pdu) stream_free (lsp->pdu); free(lsp); } void lsp_db_destroy (dict_t * lspdb) { dnode_t *dnode, *next; struct isis_lsp *lsp; dnode = dict_first (lspdb); while (dnode) { next = dict_next (lspdb, dnode); lsp = dnode_get (dnode); lsp_destroy (lsp); dict_delete_free (lspdb, dnode); dnode = next; } dict_free (lspdb); return; } /* * Remove all the frags belonging to the given lsp */ static void lsp_remove_frags (struct list *frags, dict_t * lspdb) { dnode_t *dnode; struct listnode *lnode, *lnnode; struct isis_lsp *lsp; for (ALL_LIST_ELEMENTS (frags, lnode, lnnode, lsp)) { dnode = dict_lookup (lspdb, lsp->lsp_header->lsp_id); lsp_destroy (lsp); dnode_destroy (dict_delete (lspdb, dnode)); } list_delete_all_node (frags); return; } void lsp_search_and_destroy (u_char * id, dict_t * lspdb) { dnode_t *node; struct isis_lsp *lsp; node = dict_lookup (lspdb, id); if (node) { node = dict_delete (lspdb, node); lsp = dnode_get (node); /* * If this is a zero lsp, remove all the frags now */ if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0) { if (lsp->lspu.frags) lsp_remove_frags (lsp->lspu.frags, lspdb); } else { /* * else just remove this frag, from the zero lsps' frag list */ if (lsp->lspu.zero_lsp && lsp->lspu.zero_lsp->lspu.frags) listnode_delete (lsp->lspu.zero_lsp->lspu.frags, lsp); } lsp_destroy (lsp); dnode_destroy (node); } } /* * Compares a LSP to given values * Params are given in net order */ int lsp_compare (char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, u_int16_t checksum, u_int16_t rem_lifetime) { /* no point in double ntohl on seqnum */ if (lsp->lsp_header->seq_num == seq_num && lsp->lsp_header->checksum == checksum && /*comparing with 0, no need to do ntohl */ ((lsp->lsp_header->rem_lifetime == 0 && rem_lifetime == 0) || (lsp->lsp_header->rem_lifetime != 0 && rem_lifetime != 0))) { if (config.nfacctd_isis_msglog) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Snp (%s): LSP %s seq 0x%08x, cksum 0x%04x, lifetime %us\n", areatag, rawlspid_print (lsp->lsp_header->lsp_id), ntohl (lsp->lsp_header->seq_num), ntohs (lsp->lsp_header->checksum), ntohs (lsp->lsp_header->rem_lifetime)); Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Snp (%s): is equal to ours seq 0x%08x, cksum 0x%04x, lifetime %us\n", areatag, ntohl (seq_num), ntohs (checksum), ntohs (rem_lifetime)); } return LSP_EQUAL; } if (ntohl (seq_num) >= ntohl (lsp->lsp_header->seq_num)) { if (config.nfacctd_isis_msglog) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Snp (%s): LSP %s seq 0x%08x, cksum 0x%04x, lifetime %us\n", areatag, rawlspid_print (lsp->lsp_header->lsp_id), ntohl (seq_num), ntohs (checksum), ntohs (rem_lifetime)); Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Snp (%s): is newer than ours seq 0x%08x, cksum 0x%04x, lifetime %us\n", areatag, ntohl (lsp->lsp_header->seq_num), ntohs (lsp->lsp_header->checksum), ntohs (lsp->lsp_header->rem_lifetime)); } return LSP_NEWER; } if (config.nfacctd_isis_msglog) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Snp (%s): LSP %s seq 0x%08x, cksum 0x%04x, lifetime %us\n", areatag, rawlspid_print (lsp->lsp_header->lsp_id), ntohl (seq_num), ntohs (checksum), ntohs (rem_lifetime)); Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Snp (%s): is older than ours seq 0x%08x, cksum 0x%04x, lifetime %us\n", areatag, ntohl (lsp->lsp_header->seq_num), ntohs (lsp->lsp_header->checksum), ntohs (lsp->lsp_header->rem_lifetime)); } return LSP_OLDER; } void lsp_inc_seqnum (struct isis_lsp *lsp, u_int32_t seq_num) { u_int32_t newseq; if (seq_num == 0 || ntohl (lsp->lsp_header->seq_num) > seq_num) newseq = ntohl (lsp->lsp_header->seq_num) + 1; else newseq = seq_num++; lsp->lsp_header->seq_num = htonl (newseq); fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, ntohs (lsp->lsp_header->pdu_len) - 12, 12); return; } /* * Genetates checksum for LSP and its frags */ static void lsp_seqnum_update (struct isis_lsp *lsp0) { struct isis_lsp *lsp; struct listnode *node; lsp_inc_seqnum (lsp0, 0); if (!lsp0->lspu.frags) return; for (ALL_LIST_ELEMENTS_RO (lsp0->lspu.frags, node, lsp)) lsp_inc_seqnum (lsp, 0); return; } int isis_lsp_authinfo_check (struct stream *stream, struct isis_area *area, int pdulen, struct isis_passwd *passwd) { uint32_t expected = 0, found; struct tlvs tlvs; int retval = 0; expected |= TLVFLAG_AUTH_INFO; retval = parse_tlvs (area->area_tag, stream->data + ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN, pdulen - ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN, &expected, &found, &tlvs); if (retval || !(found & TLVFLAG_AUTH_INFO)) return 1; /* Auth fail (parsing failed or no auth-tlv) */ return authentication_check (passwd, &tlvs.auth_info); } static void lsp_update_data (struct isis_lsp *lsp, struct stream *stream, struct isis_area *area) { uint32_t expected = 0, found; int retval; /* copying only the relevant part of our stream */ lsp->pdu = stream_dup (stream); /* setting pointers to the correct place */ lsp->isis_header = (struct isis_fixed_hdr *) (STREAM_DATA (lsp->pdu)); lsp->lsp_header = (struct isis_link_state_hdr *) (STREAM_DATA (lsp->pdu) + ISIS_FIXED_HDR_LEN); lsp->age_out = ZERO_AGE_LIFETIME; lsp->installed = time (NULL); /* * Get LSP data i.e. TLVs */ expected |= TLVFLAG_AUTH_INFO; expected |= TLVFLAG_AREA_ADDRS; expected |= TLVFLAG_IS_NEIGHS; if ((lsp->lsp_header->lsp_bits & 3) == 3) /* a level 2 LSP */ expected |= TLVFLAG_PARTITION_DESIG_LEVEL2_IS; expected |= TLVFLAG_NLPID; if (area->dynhostname) expected |= TLVFLAG_DYN_HOSTNAME; if (area->newmetric) { expected |= TLVFLAG_TE_IS_NEIGHS; expected |= TLVFLAG_TE_IPV4_REACHABILITY; expected |= TLVFLAG_TE_ROUTER_ID; } expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV4_INT_REACHABILITY; expected |= TLVFLAG_IPV4_EXT_REACHABILITY; #ifdef ENABLE_IPV6 expected |= TLVFLAG_IPV6_ADDR; expected |= TLVFLAG_IPV6_REACHABILITY; #endif /* ENABLE_IPV6 */ retval = parse_tlvs (area->area_tag, lsp->pdu->data + ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN, ntohs (lsp->lsp_header->pdu_len) - ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN, &expected, &found, &lsp->tlv_data); if (found & TLVFLAG_DYN_HOSTNAME) { if (area->dynhostname) isis_dynhn_insert (lsp->lsp_header->lsp_id, lsp->tlv_data.hostname, (lsp->lsp_header->lsp_bits & LSPBIT_IST) == IS_LEVEL_1_AND_2 ? IS_LEVEL_2 : (lsp->lsp_header->lsp_bits & LSPBIT_IST)); } } void lsp_update (struct isis_lsp *lsp, struct isis_link_state_hdr *lsp_hdr, struct stream *stream, struct isis_area *area, int level) { dnode_t *dnode = NULL; /* Remove old LSP from LSP database. */ dnode = dict_lookup (area->lspdb[level - 1], lsp->lsp_header->lsp_id); if (dnode) dnode_destroy (dict_delete (area->lspdb[level - 1], dnode)); /* free the old lsp data */ free(lsp->pdu); lsp_clear_data (lsp); /* rebuild the lsp data */ lsp_update_data (lsp, stream, area); /* set the new values for lsp header */ memcpy (lsp->lsp_header, lsp_hdr, ISIS_LSP_HDR_LEN); if (dnode) lsp_insert (lsp, area->lspdb[level - 1]); } /* creation of LSP directly from what we received */ struct isis_lsp * lsp_new_from_stream_ptr (struct stream *stream, u_int16_t pdu_len, struct isis_lsp *lsp0, struct isis_area *area) { struct isis_lsp *lsp; lsp = calloc(1, sizeof (struct isis_lsp)); lsp_update_data (lsp, stream, area); if (lsp0 == NULL) { /* * zero lsp -> create the list for fragments */ lsp->lspu.frags = list_new (); } else { /* * a fragment -> set the backpointer and add this to zero lsps frag list */ lsp->lspu.zero_lsp = lsp0; listnode_add (lsp0->lspu.frags, lsp); } return lsp; } struct isis_lsp * lsp_new (u_char * lsp_id, u_int16_t rem_lifetime, u_int32_t seq_num, u_int8_t lsp_bits, u_int16_t checksum, int level) { struct isis_lsp *lsp; lsp = calloc(1, sizeof (struct isis_lsp)); if (!lsp) { /* FIXME: set lspdbol bit */ Log(LOG_WARNING, "WARN ( default/core/ISIS ): lsp_new(): out of memory\n"); return NULL; } #ifdef LSP_MEMORY_PREASSIGN lsp->pdu = stream_new (1514); /*Should be minimal mtu? yup... */ #else /* We need to do realloc on TLVs additions */ lsp->pdu = calloc(1, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); #endif /* LSP_MEMORY_PREASSIGN */ if (LSP_FRAGMENT (lsp_id) == 0) lsp->lspu.frags = list_new (); lsp->isis_header = (struct isis_fixed_hdr *) (STREAM_DATA (lsp->pdu)); lsp->lsp_header = (struct isis_link_state_hdr *) (STREAM_DATA (lsp->pdu) + ISIS_FIXED_HDR_LEN); /* at first we fill the FIXED HEADER */ (level == 1) ? fill_fixed_hdr (lsp->isis_header, L1_LINK_STATE) : fill_fixed_hdr (lsp->isis_header, L2_LINK_STATE); /* now for the LSP HEADER */ /* Minimal LSP PDU size */ lsp->lsp_header->pdu_len = htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); memcpy (lsp->lsp_header->lsp_id, lsp_id, ISIS_SYS_ID_LEN + 2); lsp->lsp_header->checksum = checksum; /* Provided in network order */ lsp->lsp_header->seq_num = htonl (seq_num); lsp->lsp_header->rem_lifetime = htons (rem_lifetime); lsp->lsp_header->lsp_bits = lsp_bits; lsp->level = level; lsp->age_out = ZERO_AGE_LIFETIME; stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); if (config.nfacctd_isis_msglog) Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): New LSP with ID %s-%02x-%02x seqnum %08x\n", sysid_print (lsp_id), LSP_PSEUDO_ID (lsp->lsp_header->lsp_id), LSP_FRAGMENT (lsp->lsp_header->lsp_id), ntohl (lsp->lsp_header->seq_num)); return lsp; } void lsp_insert (struct isis_lsp *lsp, dict_t * lspdb) { dict_alloc_insert (lspdb, lsp->lsp_header->lsp_id, lsp); } /* * Build a list of LSPs with non-zero ht bounded by start and stop ids */ void lsp_build_list_nonzero_ht (u_char * start_id, u_char * stop_id, struct list *list, dict_t * lspdb) { dnode_t *first, *last, *curr; first = dict_lower_bound (lspdb, start_id); if (!first) return; last = dict_upper_bound (lspdb, stop_id); curr = first; if (((struct isis_lsp *) (curr->dict_data))->lsp_header->rem_lifetime) listnode_add (list, first->dict_data); while (curr) { curr = dict_next (lspdb, curr); if (curr && ((struct isis_lsp *) (curr->dict_data))->lsp_header->rem_lifetime) listnode_add (list, curr->dict_data); if (curr == last) break; } return; } /* * Build a list of all LSPs bounded by start and stop ids */ void lsp_build_list (u_char * start_id, u_char * stop_id, struct list *list, dict_t * lspdb) { dnode_t *first, *last, *curr; first = dict_lower_bound (lspdb, start_id); if (!first) return; last = dict_upper_bound (lspdb, stop_id); curr = first; listnode_add (list, first->dict_data); while (curr) { curr = dict_next (lspdb, curr); if (curr) listnode_add (list, curr->dict_data); if (curr == last) break; } return; } /* * Build a list of LSPs with SSN flag set for the given circuit */ void lsp_build_list_ssn (struct isis_circuit *circuit, struct list *list, dict_t * lspdb) { dnode_t *dnode, *next; struct isis_lsp *lsp; dnode = dict_first (lspdb); while (dnode != NULL) { next = dict_next (lspdb, dnode); lsp = dnode_get (dnode); if (ISIS_CHECK_FLAG (lsp->SSNflags, circuit)) listnode_add (list, lsp); dnode = next; } return; } static void lsp_set_time (struct isis_lsp *lsp) { assert (lsp); if (lsp->lsp_header->rem_lifetime == 0) { if (lsp->age_out != 0) lsp->age_out--; return; } /* If we are turning 0 */ /* ISO 10589 - 7.3.16.4 first paragraph */ if (ntohs (lsp->lsp_header->rem_lifetime) == 1) { /* 7.3.16.4 a) set SRM flags on all */ ISIS_FLAGS_SET_ALL (lsp->SRMflags); /* 7.3.16.4 b) retain only the header FIXME */ /* 7.3.16.4 c) record the time to purge FIXME (other way to do it) */ } lsp->lsp_header->rem_lifetime = htons (ntohs (lsp->lsp_header->rem_lifetime) - 1); } /* Convert the lsp attribute bits to attribute string */ const char * lsp_bits2string (u_char * lsp_bits) { char *pos = lsp_bits_string; if (!*lsp_bits) return " none"; /* we only focus on the default metric */ pos += sprintf (pos, "%d/", ISIS_MASK_LSP_ATT_DEFAULT_BIT (*lsp_bits) ? 1 : 0); pos += sprintf (pos, "%d/", ISIS_MASK_LSP_PARTITION_BIT (*lsp_bits) ? 1 : 0); pos += sprintf (pos, "%d", ISIS_MASK_LSP_OL_BIT (*lsp_bits) ? 1 : 0); *(pos) = '\0'; return lsp_bits_string; } #define FRAG_THOLD(S,T) \ ((STREAM_SIZE(S)*T)/100) /* stream*, area->lsp_frag_threshold, increment */ #define FRAG_NEEDED(S,T,I) \ (STREAM_SIZE(S)-STREAM_REMAIN(S)+(I) > FRAG_THOLD(S,T)) /* FIXME: It shouldn't be necessary to pass tlvsize here, TLVs can have * variable length (TE TLVs, sub TLVs). */ static void lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, int tlvsize, int frag_thold, int tlv_build_func (struct list *, struct stream *)) { int count, i; /* can we fit all ? */ if (!FRAG_NEEDED (lsp->pdu, frag_thold, listcount (*from) * tlvsize + 2)) { tlv_build_func (*from, lsp->pdu); *to = *from; *from = NULL; } else if (!FRAG_NEEDED (lsp->pdu, frag_thold, tlvsize + 2)) { /* fit all we can */ count = FRAG_THOLD (lsp->pdu, frag_thold) - 2 - (STREAM_SIZE (lsp->pdu) - STREAM_REMAIN (lsp->pdu)); if (count) count = count / tlvsize; for (i = 0; i < count; i++) { listnode_add (*to, listgetdata (listhead (*from))); listnode_delete (*from, listgetdata (listhead (*from))); } tlv_build_func (*to, lsp->pdu); } lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); return; } static struct isis_lsp * lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area, int level) { struct isis_lsp *lsp; u_char frag_id[ISIS_SYS_ID_LEN + 2]; memcpy (frag_id, lsp0->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT (frag_id) = frag_num; lsp = lsp_search (frag_id, area->lspdb[level - 1]); if (lsp) { /* * Clear the TLVs, but inherit the authinfo */ lsp_clear_data (lsp); if (lsp0->tlv_data.auth_info.type) { memcpy (&lsp->tlv_data.auth_info, &lsp->tlv_data.auth_info, sizeof (struct isis_passwd)); tlv_add_authinfo (lsp->tlv_data.auth_info.type, lsp->tlv_data.auth_info.len, lsp->tlv_data.auth_info.passwd, lsp->pdu); } return lsp; } lsp = lsp_new (frag_id, area->max_lsp_lifetime[level - 1], 0, area->is_type, 0, level); lsp->own_lsp = 1; lsp_insert (lsp, area->lspdb[level - 1]); listnode_add (lsp0->lspu.frags, lsp); lsp->lspu.zero_lsp = lsp0; /* * Copy the authinfo from zero LSP */ if (lsp0->tlv_data.auth_info.type) { memcpy (&lsp->tlv_data.auth_info, &lsp->tlv_data.auth_info, sizeof (struct isis_passwd)); tlv_add_authinfo (lsp->tlv_data.auth_info.type, lsp->tlv_data.auth_info.len, lsp->tlv_data.auth_info.passwd, lsp->pdu); } return lsp; } /* * Builds the LSP data part. This func creates a new frag whenever * area->lsp_frag_threshold is exceeded. */ static void lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) { struct is_neigh *is_neigh; struct te_is_neigh *te_is_neigh; struct listnode *node, *ipnode; int level = lsp->level; struct isis_circuit *circuit; struct prefix_ipv4 *ipv4; struct ipv4_reachability *ipreach; struct te_ipv4_reachability *te_ipreach; struct isis_adjacency *nei; #ifdef ENABLE_IPV6 struct prefix_ipv6 *ipv6, *ip6prefix; struct ipv6_reachability *ip6reach; #endif /* ENABLE_IPV6 */ struct tlvs tlv_data; struct isis_lsp *lsp0 = lsp; struct isis_passwd *passwd; struct in_addr *routerid; /* * First add the tlvs related to area */ /* Area addresses */ if (lsp->tlv_data.area_addrs == NULL) lsp->tlv_data.area_addrs = list_new (); list_add_list (lsp->tlv_data.area_addrs, area->area_addrs); /* Protocols Supported */ if (area->ip_circuits > 0 #ifdef ENABLE_IPV6 || area->ipv6_circuits > 0 #endif /* ENABLE_IPV6 */ ) { lsp->tlv_data.nlpids = calloc(1, sizeof (struct nlpids)); lsp->tlv_data.nlpids->count = 0; if (area->ip_circuits > 0) { lsp->tlv_data.nlpids->count++; lsp->tlv_data.nlpids->nlpids[0] = NLPID_IP; } #ifdef ENABLE_IPV6 if (area->ipv6_circuits > 0) { lsp->tlv_data.nlpids->count++; lsp->tlv_data.nlpids->nlpids[lsp->tlv_data.nlpids->count - 1] = NLPID_IPV6; } #endif /* ENABLE_IPV6 */ } /* XXX: Dynamic Hostname */ /* if (area->dynhostname) { lsp->tlv_data.hostname = calloc(1, sizeof (struct hostname)); memcpy (lsp->tlv_data.hostname->name, unix_hostname (), strlen (unix_hostname ())); lsp->tlv_data.hostname->namelen = strlen (unix_hostname ()); } */ /* * Building the zero lsp */ /* Reset stream endp. Stream is always there and on every LSP refresh only * TLV part of it is overwritten. So we must seek past header we will not * touch. */ stream_reset (lsp->pdu); stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); /* * Add the authentication info if its present */ (level == 1) ? (passwd = &area->area_passwd) : (passwd = &area->domain_passwd); if (passwd->type) { memcpy (&lsp->tlv_data.auth_info, passwd, sizeof (struct isis_passwd)); tlv_add_authinfo (passwd->type, passwd->len, passwd->passwd, lsp->pdu); } if (lsp->tlv_data.nlpids) tlv_add_nlpid (lsp->tlv_data.nlpids, lsp->pdu); if (lsp->tlv_data.hostname) tlv_add_dynamic_hostname (lsp->tlv_data.hostname, lsp->pdu); if (lsp->tlv_data.area_addrs && listcount (lsp->tlv_data.area_addrs) > 0) tlv_add_area_addrs (lsp->tlv_data.area_addrs, lsp->pdu); /* IPv4 address and TE router ID TLVs. In case of the first one we don't * follow "C" vendor, but "J" vendor behavior - one IPv4 address is put into * LSP and this address is same as router id. */ if (router_id_zebra.s_addr != 0) { if (lsp->tlv_data.ipv4_addrs == NULL) { lsp->tlv_data.ipv4_addrs = list_new (); lsp->tlv_data.ipv4_addrs->del = free_tlv; } routerid = calloc(1, sizeof (struct in_addr)); routerid->s_addr = router_id_zebra.s_addr; listnode_add (lsp->tlv_data.ipv4_addrs, routerid); tlv_add_in_addr (routerid, lsp->pdu, IPV4_ADDR); /* Exactly same data is put into TE router ID TLV, but only if new style * TLV's are in use. */ if (area->newmetric) { lsp->tlv_data.router_id = calloc(1, sizeof (struct in_addr)); lsp->tlv_data.router_id->id.s_addr = router_id_zebra.s_addr; tlv_add_in_addr (&lsp->tlv_data.router_id->id, lsp->pdu, TE_ROUTER_ID); } } memset (&tlv_data, 0, sizeof (struct tlvs)); /* * Then build lists of tlvs related to circuits */ for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit)) { if (circuit->state != C_STATE_UP) continue; /* * Add IPv4 internal reachability of this circuit */ if (circuit->ip_router && circuit->ip_addrs && circuit->ip_addrs->count > 0) { if (area->oldmetric) { if (tlv_data.ipv4_int_reachs == NULL) { tlv_data.ipv4_int_reachs = list_new (); tlv_data.ipv4_int_reachs->del = free_tlv; } for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4)) { ipreach = calloc(1, sizeof (struct ipv4_reachability)); ipreach->metrics = circuit->metrics[level - 1]; isis_masklen2ip (ipv4->prefixlen, &ipreach->mask); ipreach->prefix.s_addr = ((ipreach->mask.s_addr) & (ipv4->prefix.s_addr)); listnode_add (tlv_data.ipv4_int_reachs, ipreach); } tlv_data.ipv4_int_reachs->del = free_tlv; } if (area->newmetric) { if (tlv_data.te_ipv4_reachs == NULL) { tlv_data.te_ipv4_reachs = list_new (); tlv_data.te_ipv4_reachs->del = free_tlv; } for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4)) { /* FIXME All this assumes that we have no sub TLVs. */ te_ipreach = calloc(1, sizeof (struct te_ipv4_reachability) + ((ipv4->prefixlen + 7)/8) - 1); if (area->oldmetric) te_ipreach->te_metric = htonl (circuit->metrics[level - 1].metric_default); else te_ipreach->te_metric = htonl (circuit->te_metric[level - 1]); te_ipreach->control = (ipv4->prefixlen & 0x3F); memcpy (&te_ipreach->prefix_start, &ipv4->prefix.s_addr, (ipv4->prefixlen + 7)/8); listnode_add (tlv_data.te_ipv4_reachs, te_ipreach); } } } #ifdef ENABLE_IPV6 /* * Add IPv6 reachability of this circuit */ if (circuit->ipv6_router && circuit->ipv6_non_link && circuit->ipv6_non_link->count > 0) { if (tlv_data.ipv6_reachs == NULL) { tlv_data.ipv6_reachs = list_new (); tlv_data.ipv6_reachs->del = free_tlv; } for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6)) { ip6reach = calloc(1, sizeof (struct ipv6_reachability)); if (area->oldmetric) ip6reach->metric = htonl (circuit->metrics[level - 1].metric_default); else ip6reach->metric = htonl (circuit->te_metric[level - 1]); ip6reach->control_info = 0; ip6reach->prefix_len = ipv6->prefixlen; memcpy (&ip6prefix, &ipv6, sizeof(ip6prefix)); isis_apply_mask_ipv6 (ip6prefix); memcpy (ip6reach->prefix, ip6prefix->prefix.s6_addr, sizeof (ip6reach->prefix)); listnode_add (tlv_data.ipv6_reachs, ip6reach); } } #endif /* ENABLE_IPV6 */ switch (circuit->circ_type) { case CIRCUIT_T_BROADCAST: if (level & circuit->circuit_is_type) { if (area->oldmetric) { if (tlv_data.is_neighs == NULL) { tlv_data.is_neighs = list_new (); tlv_data.is_neighs->del = free_tlv; } is_neigh = calloc(1, sizeof (struct is_neigh)); if (level == 1) memcpy (is_neigh->neigh_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); else memcpy (is_neigh->neigh_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); is_neigh->metrics = circuit->metrics[level - 1]; listnode_add (tlv_data.is_neighs, is_neigh); tlv_data.is_neighs->del = free_tlv; } if (area->newmetric) { uint32_t metric; if (tlv_data.te_is_neighs == NULL) { tlv_data.te_is_neighs = list_new (); tlv_data.te_is_neighs->del = free_tlv; } te_is_neigh = calloc(1, sizeof (struct te_is_neigh)); if (level == 1) memcpy (te_is_neigh->neigh_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); else memcpy (te_is_neigh->neigh_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); if (area->oldmetric) metric = ((htonl(circuit->metrics[level - 1].metric_default) >> 8) & 0xffffff); else metric = ((htonl(*circuit->te_metric) >> 8) & 0xffffff); memcpy (te_is_neigh->te_metric, &metric, 3); listnode_add (tlv_data.te_is_neighs, te_is_neigh); } } break; case CIRCUIT_T_P2P: nei = circuit->u.p2p.neighbor; if (nei && (level & nei->circuit_t)) { if (area->oldmetric) { if (tlv_data.is_neighs == NULL) { tlv_data.is_neighs = list_new (); tlv_data.is_neighs->del = free_tlv; } is_neigh = calloc(1, sizeof (struct is_neigh)); memcpy (is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN); is_neigh->metrics = circuit->metrics[level - 1]; listnode_add (tlv_data.is_neighs, is_neigh); } if (area->newmetric) { uint32_t metric; if (tlv_data.te_is_neighs == NULL) { tlv_data.te_is_neighs = list_new (); tlv_data.te_is_neighs->del = free_tlv; } te_is_neigh = calloc(1, sizeof (struct te_is_neigh)); memcpy (te_is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN); metric = ((htonl(*circuit->te_metric) >> 8) & 0xffffff); memcpy (te_is_neigh->te_metric, &metric, 3); listnode_add (tlv_data.te_is_neighs, te_is_neigh); } } break; case CIRCUIT_T_STATIC_IN: Log(LOG_WARNING, "WARN ( default/core/ISIS ): lsp_area_create: unsupported circuit type\n"); break; case CIRCUIT_T_STATIC_OUT: Log(LOG_WARNING, "WARN ( default/core/ISIS ): lsp_area_create: unsupported circuit type\n"); break; case CIRCUIT_T_DA: Log(LOG_WARNING, "WARN ( default/core/ISIS ): lsp_area_create: unsupported circuit type\n"); break; default: Log(LOG_WARNING, "WARN ( default/core/ISIS ): lsp_area_create: unknown circuit type\n"); } } while (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs)) { if (lsp->tlv_data.ipv4_int_reachs == NULL) lsp->tlv_data.ipv4_int_reachs = list_new (); lsp_tlv_fit (lsp, &tlv_data.ipv4_int_reachs, &lsp->tlv_data.ipv4_int_reachs, IPV4_REACH_LEN, area->lsp_frag_threshold, tlv_add_ipv4_reachs); if (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } /* FIXME: We pass maximum te_ipv4_reachability length to the lsp_tlv_fit() * for now. lsp_tlv_fit() needs to be fixed to deal with variable length * TLVs (sub TLVs!). */ while (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs)) { if (lsp->tlv_data.te_ipv4_reachs == NULL) lsp->tlv_data.te_ipv4_reachs = list_new (); lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs, &lsp->tlv_data.te_ipv4_reachs, 9, area->lsp_frag_threshold, tlv_add_te_ipv4_reachs); if (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } #ifdef ENABLE_IPV6 while (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs)) { if (lsp->tlv_data.ipv6_reachs == NULL) lsp->tlv_data.ipv6_reachs = list_new (); lsp_tlv_fit (lsp, &tlv_data.ipv6_reachs, &lsp->tlv_data.ipv6_reachs, IPV6_REACH_LEN, area->lsp_frag_threshold, tlv_add_ipv6_reachs); if (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } #endif /* ENABLE_IPV6 */ while (tlv_data.is_neighs && listcount (tlv_data.is_neighs)) { if (lsp->tlv_data.is_neighs == NULL) lsp->tlv_data.is_neighs = list_new (); lsp_tlv_fit (lsp, &tlv_data.is_neighs, &lsp->tlv_data.is_neighs, IS_NEIGHBOURS_LEN, area->lsp_frag_threshold, tlv_add_is_neighs); if (tlv_data.is_neighs && listcount (tlv_data.is_neighs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } while (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs)) { if (lsp->tlv_data.te_is_neighs == NULL) lsp->tlv_data.te_is_neighs = list_new (); lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs, IS_NEIGHBOURS_LEN, area->lsp_frag_threshold, tlv_add_te_is_neighs); if (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } free_tlvs (&tlv_data); return; } /* * 7.3.7 Generation on non-pseudonode LSPs */ static int lsp_generate_non_pseudo (struct isis_area *area, int level) { struct isis_lsp *oldlsp, *newlsp; u_int32_t seq_num = 0; u_char lspid[ISIS_SYS_ID_LEN + 2]; memset (&lspid, 0, ISIS_SYS_ID_LEN + 2); memcpy (&lspid, isis->sysid, ISIS_SYS_ID_LEN); /* only builds the lsp if the area shares the level */ if ((area->is_type & level) == level) { oldlsp = lsp_search (lspid, area->lspdb[level - 1]); if (oldlsp) { seq_num = ntohl (oldlsp->lsp_header->seq_num); lsp_search_and_destroy (oldlsp->lsp_header->lsp_id, area->lspdb[level - 1]); /* FIXME: we should actually initiate a purge */ } newlsp = lsp_new (lspid, area->max_lsp_lifetime[level - 1], seq_num, area->is_type, 0, level); newlsp->own_lsp = 1; lsp_insert (newlsp, area->lspdb[level - 1]); /* build_lsp_data (newlsp, area); */ lsp_build_nonpseudo (newlsp, area); /* time to calculate our checksum */ lsp_seqnum_update (newlsp); } /* DEBUG_ADJ_PACKETS */ if (config.nfacctd_isis_msglog) { /* FIXME: is this place right? fix missing info */ Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Upd (%s): Building L%d LSP\n", area->area_tag, level); } return ISIS_OK; } /* * 7.3.9 Generation of level 1 LSPs (non-pseudonode) */ int lsp_l1_generate (struct isis_area *area) { THREAD_TIMER_ON (master, area->t_lsp_refresh[0], lsp_refresh_l1, area, MAX_LSP_GEN_INTERVAL); return lsp_generate_non_pseudo (area, 1); } /* * 7.3.9 Generation of level 2 LSPs (non-pseudonode) */ int lsp_l2_generate (struct isis_area *area) { THREAD_TIMER_ON (master, area->t_lsp_refresh[1], lsp_refresh_l2, area, MAX_LSP_GEN_INTERVAL); return lsp_generate_non_pseudo (area, 2); } static int lsp_non_pseudo_regenerate (struct isis_area *area, int level) { dict_t *lspdb = area->lspdb[level - 1]; struct isis_lsp *lsp, *frag; struct listnode *node; u_char lspid[ISIS_SYS_ID_LEN + 2]; memset (lspid, 0, ISIS_SYS_ID_LEN + 2); memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN); lsp = lsp_search (lspid, lspdb); if (!lsp) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): ISIS-Upd (%s): lsp_non_pseudo_regenerate(): no L%d LSP found!\n", area->area_tag, level); return ISIS_ERROR; } lsp_clear_data (lsp); lsp_build_nonpseudo (lsp, area); lsp->lsp_header->rem_lifetime = htons (isis_jitter (area->max_lsp_lifetime[level - 1], MAX_AGE_JITTER)); lsp_seqnum_update (lsp); if (config.nfacctd_isis_msglog) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Upd (%s): refreshing our L%d LSP %s, seq 0x%08x, cksum 0x%04x lifetime %us\n", area->area_tag, level, rawlspid_print (lsp->lsp_header->lsp_id), ntohl (lsp->lsp_header->seq_num), ntohs (lsp->lsp_header->checksum), ntohs (lsp->lsp_header->rem_lifetime)); } lsp->last_generated = time (NULL); area->lsp_regenerate_pending[level - 1] = 0; ISIS_FLAGS_SET_ALL (lsp->SRMflags); for (ALL_LIST_ELEMENTS_RO (lsp->lspu.frags, node, frag)) { frag->lsp_header->rem_lifetime = htons (isis_jitter (area-> max_lsp_lifetime[level - 1], MAX_AGE_JITTER)); ISIS_FLAGS_SET_ALL (frag->SRMflags); } if (area->ip_circuits) isis_spf_schedule (area, level); #ifdef ENABLE_IPV6 if (area->ipv6_circuits) isis_spf_schedule6 (area, level); #endif return ISIS_OK; } /* * Done at least every MAX_LSP_GEN_INTERVAL. Search own LSPs, update holding * time and set SRM */ int lsp_refresh_l1 (struct thread *thread) { struct isis_area *area; unsigned long ref_time; area = THREAD_ARG (thread); assert (area); area->t_lsp_refresh[0] = NULL; if (area->is_type & IS_LEVEL_1) lsp_non_pseudo_regenerate (area, 1); ref_time = area->lsp_refresh[0] > MAX_LSP_GEN_INTERVAL ? MAX_LSP_GEN_INTERVAL : area->lsp_refresh[0]; THREAD_TIMER_ON (master, area->t_lsp_refresh[0], lsp_refresh_l1, area, isis_jitter (ref_time, MAX_AGE_JITTER)); return ISIS_OK; } int lsp_refresh_l2 (struct thread *thread) { struct isis_area *area; unsigned long ref_time; area = THREAD_ARG (thread); assert (area); area->t_lsp_refresh[1] = NULL; if (area->is_type & IS_LEVEL_2) lsp_non_pseudo_regenerate (area, 2); ref_time = area->lsp_refresh[1] > MAX_LSP_GEN_INTERVAL ? MAX_LSP_GEN_INTERVAL : area->lsp_refresh[1]; THREAD_TIMER_ON (master, area->t_lsp_refresh[1], lsp_refresh_l2, area, isis_jitter (ref_time, MAX_AGE_JITTER)); return ISIS_OK; } /* * Something has changed -> regenerate LSP */ static int lsp_l1_regenerate (struct thread *thread) { struct isis_area *area; area = THREAD_ARG (thread); area->lsp_regenerate_pending[0] = 0; return lsp_non_pseudo_regenerate (area, 1); } static int lsp_l2_regenerate (struct thread *thread) { struct isis_area *area; area = THREAD_ARG (thread); area->lsp_regenerate_pending[1] = 0; return lsp_non_pseudo_regenerate (area, 2); } int lsp_regenerate_schedule (struct isis_area *area) { struct isis_lsp *lsp; u_char id[ISIS_SYS_ID_LEN + 2]; time_t now, diff; memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID (id) = LSP_FRAGMENT (id) = 0; now = time (NULL); /* * First level 1 */ if (area->is_type & IS_LEVEL_1) { lsp = lsp_search (id, area->lspdb[0]); if (!lsp || area->lsp_regenerate_pending[0]) goto L2; /* * Throttle avoidance */ diff = now - lsp->last_generated; if (diff < MIN_LSP_GEN_INTERVAL) { area->lsp_regenerate_pending[0] = 1; area->t_lsp_l1_regenerate=thread_add_timer (master, lsp_l1_regenerate, area, MIN_LSP_GEN_INTERVAL - diff); goto L2; } else lsp_non_pseudo_regenerate (area, 1); } /* * then 2 */ L2: if (area->is_type & IS_LEVEL_2) { lsp = lsp_search (id, area->lspdb[1]); if (!lsp || area->lsp_regenerate_pending[1]) return ISIS_OK; /* * Throttle avoidance */ diff = now - lsp->last_generated; if (diff < MIN_LSP_GEN_INTERVAL) { area->lsp_regenerate_pending[1] = 1; area->t_lsp_l2_regenerate=thread_add_timer (master, lsp_l2_regenerate, area, MIN_LSP_GEN_INTERVAL - diff); return ISIS_OK; } else lsp_non_pseudo_regenerate (area, 2); } return ISIS_OK; } /* * Funcs for pseudonode LSPs */ /* * 7.3.8 and 7.3.10 Generation of level 1 and 2 pseudonode LSPs */ static void lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, int level) { struct isis_adjacency *adj; struct is_neigh *is_neigh; struct te_is_neigh *te_is_neigh; struct es_neigh *es_neigh; struct list *adj_list; struct listnode *node; struct isis_passwd *passwd; assert (circuit); assert (circuit->circ_type == CIRCUIT_T_BROADCAST); if (!circuit->u.bc.is_dr[level - 1]) return; /* we are not DIS on this circuit */ lsp->level = level; if (level == 1) lsp->lsp_header->lsp_bits |= IS_LEVEL_1; else lsp->lsp_header->lsp_bits |= IS_LEVEL_2; /* * add self to IS neighbours */ if (circuit->area->oldmetric) { if (lsp->tlv_data.is_neighs == NULL) { lsp->tlv_data.is_neighs = list_new (); lsp->tlv_data.is_neighs->del = free_tlv; } is_neigh = calloc(1, sizeof (struct is_neigh)); memcpy (&is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN); listnode_add (lsp->tlv_data.is_neighs, is_neigh); } if (circuit->area->newmetric) { if (lsp->tlv_data.te_is_neighs == NULL) { lsp->tlv_data.te_is_neighs = list_new (); lsp->tlv_data.te_is_neighs->del = free_tlv; } te_is_neigh = calloc(1, sizeof (struct te_is_neigh)); memcpy (&te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN); listnode_add (lsp->tlv_data.te_is_neighs, te_is_neigh); } adj_list = list_new (); isis_adj_build_up_list (circuit->u.bc.adjdb[level - 1], adj_list); for (ALL_LIST_ELEMENTS_RO (adj_list, node, adj)) { if (adj->circuit_t & level) { if ((level == 1 && adj->sys_type == ISIS_SYSTYPE_L1_IS) || (level == 1 && adj->sys_type == ISIS_SYSTYPE_L2_IS && adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || (level == 2 && adj->sys_type == ISIS_SYSTYPE_L2_IS)) { /* an IS neighbour -> add it */ if (circuit->area->oldmetric) { is_neigh = calloc(1, sizeof (struct is_neigh)); memcpy (&is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN); listnode_add (lsp->tlv_data.is_neighs, is_neigh); } if (circuit->area->newmetric) { te_is_neigh = calloc(1, sizeof (struct te_is_neigh)); memcpy (&te_is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN); listnode_add (lsp->tlv_data.te_is_neighs, te_is_neigh); } } else if (level == 1 && adj->sys_type == ISIS_SYSTYPE_ES) { /* an ES neigbour add it, if we are building level 1 LSP */ /* FIXME: the tlv-format is hard to use here */ if (lsp->tlv_data.es_neighs == NULL) { lsp->tlv_data.es_neighs = list_new (); lsp->tlv_data.es_neighs->del = free_tlv; } es_neigh = calloc(1, sizeof (struct es_neigh)); memcpy (&es_neigh->first_es_neigh, adj->sysid, ISIS_SYS_ID_LEN); listnode_add (lsp->tlv_data.es_neighs, es_neigh); } } } /* Reset endp of stream to overwrite only TLV part of it. */ stream_reset (lsp->pdu); stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); /* * Add the authentication info if it's present */ (level == 1) ? (passwd = &circuit->area->area_passwd) : (passwd = &circuit->area->domain_passwd); if (passwd->type) { memcpy (&lsp->tlv_data.auth_info, passwd, sizeof (struct isis_passwd)); tlv_add_authinfo (passwd->type, passwd->len, passwd->passwd, lsp->pdu); } if (lsp->tlv_data.is_neighs && listcount (lsp->tlv_data.is_neighs) > 0) tlv_add_is_neighs (lsp->tlv_data.is_neighs, lsp->pdu); if (lsp->tlv_data.te_is_neighs && listcount (lsp->tlv_data.te_is_neighs) > 0) tlv_add_te_is_neighs (lsp->tlv_data.te_is_neighs, lsp->pdu); if (lsp->tlv_data.es_neighs && listcount (lsp->tlv_data.es_neighs) > 0) tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu); lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, ntohs (lsp->lsp_header->pdu_len) - 12, 12); list_delete (adj_list); return; } static int lsp_pseudo_regenerate (struct isis_circuit *circuit, int level) { dict_t *lspdb = circuit->area->lspdb[level - 1]; struct isis_lsp *lsp; u_char lsp_id[ISIS_SYS_ID_LEN + 2]; memcpy (lsp_id, isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID (lsp_id) = circuit->circuit_id; LSP_FRAGMENT (lsp_id) = 0; lsp = lsp_search (lsp_id, lspdb); if (!lsp) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): lsp_pseudo_regenerate(): no l%d LSP %s found!\n", level, rawlspid_print (lsp_id)); return ISIS_ERROR; } lsp_clear_data (lsp); lsp_build_pseudo (lsp, circuit, level); lsp->lsp_header->rem_lifetime = htons (isis_jitter (circuit->area->max_lsp_lifetime[level - 1], MAX_AGE_JITTER)); lsp_inc_seqnum (lsp, 0); if (config.nfacctd_isis_msglog) { Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Upd (%s): refreshing pseudo LSP L%d %s\n", circuit->area->area_tag, level, rawlspid_print (lsp->lsp_header->lsp_id)); } lsp->last_generated = time (NULL); ISIS_FLAGS_SET_ALL (lsp->SRMflags); return ISIS_OK; } int lsp_l1_refresh_pseudo (struct thread *thread) { struct isis_circuit *circuit; int retval; unsigned long ref_time; circuit = THREAD_ARG (thread); if (!circuit->u.bc.is_dr[0]) return ISIS_ERROR; /* FIXME: purge and such */ circuit->u.bc.t_refresh_pseudo_lsp[0] = NULL; retval = lsp_pseudo_regenerate (circuit, 1); ref_time = circuit->area->lsp_refresh[0] > MAX_LSP_GEN_INTERVAL ? MAX_LSP_GEN_INTERVAL : circuit->area->lsp_refresh[0]; THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[0], lsp_l1_refresh_pseudo, circuit, isis_jitter (ref_time, MAX_AGE_JITTER)); return retval; } int lsp_l1_pseudo_generate (struct isis_circuit *circuit) { struct isis_lsp *lsp; u_char id[ISIS_SYS_ID_LEN + 2]; unsigned long ref_time; memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); LSP_FRAGMENT (id) = 0; LSP_PSEUDO_ID (id) = circuit->circuit_id; /* * If for some reason have a pseudo LSP in the db already -> regenerate */ if (lsp_search (id, circuit->area->lspdb[0])) return lsp_pseudo_regenerate (circuit, 1); lsp = lsp_new (id, circuit->area->max_lsp_lifetime[0], 1, circuit->area->is_type, 0, 1); lsp_build_pseudo (lsp, circuit, 1); lsp->own_lsp = 1; lsp_insert (lsp, circuit->area->lspdb[0]); ISIS_FLAGS_SET_ALL (lsp->SRMflags); ref_time = circuit->area->lsp_refresh[0] > MAX_LSP_GEN_INTERVAL ? MAX_LSP_GEN_INTERVAL : circuit->area->lsp_refresh[0]; THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[0], lsp_l1_refresh_pseudo, circuit, isis_jitter (ref_time, MAX_AGE_JITTER)); return lsp_regenerate_schedule (circuit->area); } int lsp_l2_refresh_pseudo (struct thread *thread) { struct isis_circuit *circuit; int retval; unsigned long ref_time; circuit = THREAD_ARG (thread); if (!circuit->u.bc.is_dr[1]) return ISIS_ERROR; /* FIXME: purge and such */ circuit->u.bc.t_refresh_pseudo_lsp[1] = NULL; retval = lsp_pseudo_regenerate (circuit, 2); ref_time = circuit->area->lsp_refresh[1] > MAX_LSP_GEN_INTERVAL ? MAX_LSP_GEN_INTERVAL : circuit->area->lsp_refresh[1]; THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[1], lsp_l2_refresh_pseudo, circuit, isis_jitter (ref_time, MAX_AGE_JITTER)); return retval; } int lsp_l2_pseudo_generate (struct isis_circuit *circuit) { struct isis_lsp *lsp; u_char id[ISIS_SYS_ID_LEN + 2]; unsigned long ref_time; memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); LSP_FRAGMENT (id) = 0; LSP_PSEUDO_ID (id) = circuit->circuit_id; if (lsp_search (id, circuit->area->lspdb[1])) return lsp_pseudo_regenerate (circuit, 2); lsp = lsp_new (id, circuit->area->max_lsp_lifetime[1], 1, circuit->area->is_type, 0, 2); lsp_build_pseudo (lsp, circuit, 2); ref_time = circuit->area->lsp_refresh[1] > MAX_LSP_GEN_INTERVAL ? MAX_LSP_GEN_INTERVAL : circuit->area->lsp_refresh[1]; lsp->own_lsp = 1; lsp_insert (lsp, circuit->area->lspdb[1]); ISIS_FLAGS_SET_ALL (lsp->SRMflags); THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[1], lsp_l2_refresh_pseudo, circuit, isis_jitter (ref_time, MAX_AGE_JITTER)); return lsp_regenerate_schedule (circuit->area); } void lsp_purge_dr (u_char * id, struct isis_circuit *circuit, int level) { struct isis_lsp *lsp; lsp = lsp_search (id, circuit->area->lspdb[level - 1]); if (lsp && lsp->purged == 0) { lsp->lsp_header->rem_lifetime = htons (0); lsp->lsp_header->pdu_len = htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); lsp->purged = 0; fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, ntohs (lsp->lsp_header->pdu_len) - 12, 12); ISIS_FLAGS_SET_ALL (lsp->SRMflags); } return; } /* * Purge own LSP that is received and we don't have. * -> Do as in 7.3.16.4 */ void lsp_purge_non_exist (struct isis_link_state_hdr *lsp_hdr, struct isis_area *area) { struct isis_lsp *lsp; /* * We need to create the LSP to be purged */ Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): LSP PURGE NON EXIST\n"); lsp = calloc(1, sizeof (struct isis_lsp)); /*FIXME: BUG BUG BUG! the lsp doesn't exist here! */ /*did smt here, maybe good probably not */ lsp->level = ((lsp_hdr->lsp_bits & LSPBIT_IST) == IS_LEVEL_1) ? 1 : 2; lsp->pdu = stream_new (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); lsp->isis_header = (struct isis_fixed_hdr *) STREAM_DATA (lsp->pdu); fill_fixed_hdr (lsp->isis_header, (lsp->level == 1) ? L1_LINK_STATE : L2_LINK_STATE); lsp->lsp_header = (struct isis_link_state_hdr *) (STREAM_DATA (lsp->pdu) + ISIS_FIXED_HDR_LEN); memcpy (lsp->lsp_header, lsp_hdr, ISIS_LSP_HDR_LEN); /* * Retain only LSP header */ lsp->lsp_header->pdu_len = htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); /* * Set the remaining lifetime to 0 */ lsp->lsp_header->rem_lifetime = 0; /* * Put the lsp into LSPdb */ lsp_insert (lsp, area->lspdb[lsp->level - 1]); /* * Send in to whole area */ ISIS_FLAGS_SET_ALL (lsp->SRMflags); return; } pmacct-0.14.0/src/isis/isis_dynhn.h0000640000175000017500000000272111731471736016137 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_dynhn.h * Dynamic hostname cache * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_DYNHN_H_ #define _ISIS_DYNHN_H_ struct isis_dynhn { u_char id[ISIS_SYS_ID_LEN]; struct hostname name; time_t refresh; int level; }; #if (!defined __ISIS_DYNHN_C) #define EXT extern #else #define EXT #endif EXT void dyn_cache_init (); EXT void isis_dynhn_insert (u_char * id, struct hostname *, int); EXT struct isis_dynhn *dynhn_find_by_id (u_char *); EXT int dyn_cache_cleanup (); EXT struct list *dyn_cache; #undef EXT #endif /* _ISIS_DYNHN_H_ */ pmacct-0.14.0/src/isis/isis_csm.h0000640000175000017500000000306611705253204015571 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_csm.h * IS-IS circuit state machine * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_CSM_H_ #define _ISIS_CSM_H_ /* * Circuit states */ #define C_STATE_NA 0 #define C_STATE_INIT 1 /* Connected to interface */ #define C_STATE_CONF 2 /* Configured for ISIS */ #define C_STATE_UP 3 /* CONN | CONF */ /* * Circuit events */ #define ISIS_ENABLE 1 #define IF_UP_FROM_Z 2 #define ISIS_DISABLE 3 #define IF_DOWN_FROM_Z 4 #if (!defined __ISIS_CSM_C) #define EXT extern #else #define EXT #endif EXT struct isis_circuit *isis_csm_state_change (int, struct isis_circuit *, void *); #undef EXT #endif /* _ISIS_CSM_H_ */ pmacct-0.14.0/src/isis/isis_flags.c0000640000175000017500000000416411705253204016076 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_flags.c * Routines for manipulation of SSN and SRM flags * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_FLAGS_C #include "pmacct.h" #include "isis.h" #include "linklist.h" #include "isis_constants.h" #include "isis_common.h" #include "isis_flags.h" void flags_initialize (struct flags *flags) { flags->maxindex = 0; flags->free_idcs = NULL; } int flags_get_index (struct flags *flags) { struct listnode *node; int index; if (flags->free_idcs == NULL || flags->free_idcs->count == 0) { index = flags->maxindex++; } else { node = listhead (flags->free_idcs); index = (int) listgetdata (node); listnode_delete (flags->free_idcs, (void *) index); index--; } return index; } void flags_free_index (struct flags *flags, int index) { if (index + 1 == flags->maxindex) { flags->maxindex--; return; } if (flags->free_idcs == NULL) { flags->free_idcs = list_new (); } listnode_add (flags->free_idcs, (void *) (index + 1)); return; } int flags_any_set (u_int32_t * flags) { u_int32_t zero[ISIS_MAX_CIRCUITS]; memset (zero, 0x00, ISIS_MAX_CIRCUITS * 4); return bcmp (flags, zero, ISIS_MAX_CIRCUITS * 4); } pmacct-0.14.0/src/isis/isis_constants.h0000640000175000017500000001024111705253204017014 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_constants.h * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_CONSTANTS_H_ #define _ISIS_CONSTANTS_H_ /* * Architectural constant values from p. 35 of ISO/IEC 10589 */ #define MAX_LINK_METRIC 63 #define MAX_PATH_METRIC 1023 #define ISO_SAP 0xFE #define INTRADOMAIN_ROUTEING_SELECTOR 0 #define SEQUENCE_MODULUS 4294967296 #define RECEIVE_LSP_BUFFER_SIZE 1492 /* * implementation specific jitter values */ #define IIH_JITTER 25 /* % */ #define MAX_AGE_JITTER 5 /* % */ #define MAX_LSP_GEN_JITTER 5 /* % */ #define CSNP_JITTER 10 /* % */ #define PSNP_JITTER 10 /* % */ #define RANDOM_SPREAD 100000.0 /* * Default values * ISO - 10589 * Section 7.3.21 - Parameters */ #define MAX_AGE 1200 #define ZERO_AGE_LIFETIME 60 #define MAX_LSP_GEN_INTERVAL 900 #define MIN_LSP_GEN_INTERVAL 30 #define MIN_LSP_TRANS_INTERVAL 5 #define ISIS_MIN_LSP_LIFETIME 380 #define CSNP_INTERVAL 10 #define PSNP_INTERVAL 2 #define ISIS_MAX_PATH_SPLITS 3 #define ISIS_LEVELS 2 #define ISIS_LEVEL1 1 #define ISIS_LEVEL2 2 #define HELLO_INTERVAL 10 #define HELLO_MINIMAL HELLO_INTERVAL #define HELLO_MULTIPLIER 3 #define DEFAULT_PRIORITY 64 /* different vendors implement different values 5-10 on average */ #define LSP_GEN_INTERVAL_DEFAULT 10 #define LSP_INTERVAL 33 /* msecs */ #define DEFAULT_CIRCUIT_METRICS 10 #define METRICS_UNSUPPORTED 0x80 #define PERIODIC_SPF_INTERVAL 60 /* at the top of my head */ #define MINIMUM_SPF_INTERVAL 5 /* .. same here */ /* * NLPID values */ #define NLPID_IP 204 #define NLPID_IPV6 142 #define NLPID_SNAP 128 #define NLPID_CLNP 129 #define NLPID_ESIS 130 /* * Return values for functions */ #define ISIS_OK 0 #define ISIS_WARNING 1 #define ISIS_ERROR 2 #define ISIS_CRITICAL 3 /* * IS-IS Circuit Types */ #define IS_LEVEL_1 1 #define IS_LEVEL_2 2 #define IS_LEVEL_1_AND_2 3 #define SNPA_ADDRSTRLEN 18 #define ISIS_SYS_ID_LEN 6 #define SYSID_STRLEN 24 /* * LSP bit masks */ #define LSPBIT_P 0x80 #define LSPBIT_ATT 0x78 #define LSPBIT_OL 0x04 #define LSPBIT_IST 0x03 /* * LSP bit masking macros * taken from tcpdumps * print-isoclns.c */ #define ISIS_MASK_LSP_OL_BIT(x) ((x)&0x4) #define ISIS_MASK_LSP_IS_L1_BIT(x) ((x)&0x1) #define ISIS_MASK_LSP_IS_L2_BIT(x) ((x)&0x2) #define ISIS_MASK_LSP_PARTITION_BIT(x) ((x)&0x80) #define ISIS_MASK_LSP_ATT_BITS(x) ((x)&0x78) #define ISIS_MASK_LSP_ATT_ERROR_BIT(x) ((x)&0x40) #define ISIS_MASK_LSP_ATT_EXPENSE_BIT(x) ((x)&0x20) #define ISIS_MASK_LSP_ATT_DELAY_BIT(x) ((x)&0x10) #define ISIS_MASK_LSP_ATT_DEFAULT_BIT(x) ((x)&0x8) #define LLC_LEN 3 /* we need to be aware of the fact we are using ISO sized * packets, using isomtu = mtu - LLC_LEN */ #define ISO_MTU(C) \ (C->circ_type==CIRCUIT_T_BROADCAST) ? \ (C->interface->mtu - LLC_LEN) : (C->interface->mtu) #ifndef ETH_ALEN #define ETH_ALEN 6 #endif #endif /* _ISIS_CONSTANTS_H_ */ pmacct-0.14.0/src/isis/prefix.h0000640000175000017500000001346411734647141015271 0ustar paolopaolo/* * Prefix structure. * Copyright (C) 1998 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef _PREFIX_H_ #define _PREFIX_H_ #include "sockunion.h" /* * A struct prefix contains an address family, a prefix length, and an * address. This can represent either a 'network prefix' as defined * by CIDR, where the 'host bits' of the prefix are 0 * (e.g. AF_INET:10.0.0.0/8), or an address and netmask * (e.g. AF_INET:10.0.0.9/8), such as might be configured on an * interface. */ /* IPv4 and IPv6 unified prefix structure. */ struct isis_prefix { u_char family; u_char prefixlen; union { u_char prefix; struct in_addr prefix4; #ifdef ENABLE_IPV6 struct in6_addr prefix6; #endif u_char val[8]; } u __attribute__ ((aligned (8))); struct in_addr adv_router; }; /* IPv4 prefix structure. */ /* struct prefix_ipv4 { u_char family; u_char prefixlen; struct in_addr prefix __attribute__ ((aligned (8))); }; */ /* IPv6 prefix structure. */ /* #ifdef ENABLE_IPV6 struct prefix_ipv6 { u_char family; u_char prefixlen; struct in6_addr prefix __attribute__ ((aligned (8))); }; #endif */ /* struct prefix_ls { u_char family; u_char prefixlen; struct in_addr id __attribute__ ((aligned (8))); struct in_addr adv_router; }; */ /* Prefix for routing distinguisher. */ /* struct prefix_rd { u_char family; u_char prefixlen; u_char val[8] __attribute__ ((aligned (8))); }; */ #ifndef INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 #endif /* INET_ADDRSTRLEN */ #ifndef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN 46 #endif /* INET6_ADDRSTRLEN */ #ifndef INET6_BUFSIZ #define INET6_BUFSIZ 51 #endif /* INET6_BUFSIZ */ /* Max bit/byte length of IPv4 address. */ #define IPV4_MAX_BYTELEN 4 #define IPV4_MAX_BITLEN 32 #define IPV4_MAX_PREFIXLEN 32 #define IPV4_ADDR_CMP(D,S) memcmp ((D), (S), IPV4_MAX_BYTELEN) #define IPV4_ADDR_SAME(D,S) (memcmp ((D), (S), IPV4_MAX_BYTELEN) == 0) #define IPV4_ADDR_COPY(D,S) memcpy ((D), (S), IPV4_MAX_BYTELEN) #define IPV4_NET0(a) ((((u_int32_t) (a)) & 0xff000000) == 0x00000000) #define IPV4_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000) #define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000) /* Max bit/byte length of IPv6 address. */ #define IPV6_MAX_BYTELEN 16 #define IPV6_MAX_BITLEN 128 #define IPV6_MAX_PREFIXLEN 128 #define IPV6_ADDR_CMP(D,S) memcmp ((D), (S), IPV6_MAX_BYTELEN) #define IPV6_ADDR_SAME(D,S) (memcmp ((D), (S), IPV6_MAX_BYTELEN) == 0) #define IPV6_ADDR_COPY(D,S) memcpy ((D), (S), IPV6_MAX_BYTELEN) /* Count prefix size from mask length */ #define PSIZE(a) (((a) + 7) / (8)) /* Prefix's family member. */ #define PREFIX_FAMILY(p) ((p)->family) /* Check bit of the prefix. */ static inline unsigned int prefix_bit (const u_char *prefix, const u_char prefixlen) { unsigned int offset = prefixlen / 8; unsigned int shift = 7 - (prefixlen % 8); return (prefix[offset] >> shift) & 1; } static inline unsigned int prefix6_bit (const struct in6_addr *prefix, const u_char prefixlen) { return prefix_bit((const u_char *) &prefix->s6_addr, prefixlen); } #define PREFIX_COPY_IPV4(DST, SRC) \ *((struct prefix_ipv4 *)(DST)) = *((const struct prefix_ipv4 *)(SRC)); #ifdef ENABLE_IPV6 #define PREFIX_COPY_IPV6(DST, SRC) \ *((struct prefix_ipv6 *)(DST)) = *((const struct prefix_ipv6 *)(SRC)); #endif /* Prototypes. */ #if (!defined __PREFIX_C) #define EXT extern #else #define EXT #endif EXT struct isis_prefix *isis_prefix_new (void); EXT void isis_prefix_free (struct isis_prefix *); EXT const char *isis_prefix_family_str (const struct isis_prefix *); EXT int isis_prefix_blen (const struct isis_prefix *); EXT int isis_str2prefix (const char *, struct isis_prefix *); EXT int isis_prefix2str (const struct isis_prefix *, char *, int); EXT int isis_prefix_match (const struct isis_prefix *, const struct isis_prefix *); EXT int isis_prefix_same (const struct isis_prefix *, const struct isis_prefix *); EXT int isis_prefix_cmp (const struct isis_prefix *, const struct isis_prefix *); EXT void isis_prefix_copy (struct isis_prefix *, const struct isis_prefix *); EXT void isis_apply_mask (struct isis_prefix *); EXT struct isis_prefix *sockunion2prefix (const union sockunion *, const union sockunion *); EXT struct isis_prefix *sockunion2hostprefix (const union sockunion *); EXT struct prefix_ipv4 *isis_prefix_ipv4_new (void); EXT int isis_str2prefix_ipv4 (const char *, struct prefix_ipv4 *); EXT void isis_apply_mask_ipv4 (struct prefix_ipv4 *); EXT u_char isis_ip_masklen (struct in_addr); EXT void isis_masklen2ip (int, struct in_addr *); EXT int netmask_isis_str2prefix_str (const char *, const char *, char *); #ifdef ENABLE_IPV6 EXT struct prefix_ipv6 *prefix_ipv6_new (void); EXT void isis_prefix_ipv6_free (struct prefix_ipv6 *); EXT int isis_str2prefix_ipv6 (const char *, struct prefix_ipv6 *); EXT void isis_apply_mask_ipv6 (struct prefix_ipv6 *); EXT int isis_ip6_masklen (struct in6_addr); EXT void isis_masklen2ip6 (int, struct in6_addr *); EXT void isis_str2in6_addr (const char *, struct in6_addr *); EXT const char *isis_inet6_ntoa (struct in6_addr); #endif /* ENABLE_IPV6 */ #undef EXT #endif /* _PREFIX_H_ */ pmacct-0.14.0/src/isis/dict.h0000640000175000017500000001061311705253204014677 0ustar paolopaolo/* * Dictionary Abstract Data Type * Copyright (C) 1997 Kaz Kylheku * * Free Software License: * * All rights are reserved by the author, with the following exceptions: * Permission is granted to freely reproduce and distribute this software, * possibly in exchange for a fee, provided that this copyright notice appears * intact. Permission is also granted to adapt this software to produce * derivative works, as long as the modified versions carry this copyright * notice and additional notices stating that the work has been modified. * This source code may be translated into executable form and incorporated * into proprietary software; there is no requirement for such software to * contain a copyright notice related to this source. * * $Id: dict.h,v 1.1 2012/01/17 11:07:48 paolo Exp $ * $Name: $ */ #ifndef _DICT_H_ #define _DICT_H_ #include #ifdef KAZLIB_SIDEEFFECT_DEBUG #include "sfx.h" #endif typedef unsigned long dictcount_t; #define DICTCOUNT_T_MAX ULONG_MAX /* * The dictionary is implemented as a red-black tree */ typedef enum { dnode_red, dnode_black } dnode_color_t; typedef struct dnode_t { #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) struct dnode_t *dict_left; struct dnode_t *dict_right; struct dnode_t *dict_parent; dnode_color_t dict_color; const void *dict_key; void *dict_data; #else int dict_dummy; #endif } dnode_t; typedef int (*dict_comp_t)(const void *, const void *); typedef dnode_t *(*dnode_alloc_t)(void *); typedef void (*dnode_free_t)(dnode_t *, void *); typedef struct dict_t { #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) dnode_t dict_nilnode; dictcount_t dict_nodecount; dictcount_t dict_maxcount; dict_comp_t dict_compare; dnode_alloc_t dict_allocnode; dnode_free_t dict_freenode; void *dict_context; int dict_dupes; #else int dict_dummmy; #endif } dict_t; typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); typedef struct dict_load_t { #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) dict_t *dict_dictptr; dnode_t dict_nilnode; #else int dict_dummmy; #endif } dict_load_t; #if (!defined __DICT_C) #define EXT extern #else #define EXT #endif EXT dict_t *dict_create(dictcount_t, dict_comp_t); EXT void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *); EXT void dict_destroy(dict_t *); EXT void dict_free_nodes(dict_t *); EXT void dict_free(dict_t *); EXT dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t); EXT void dict_init_like(dict_t *, const dict_t *); EXT int dict_verify(dict_t *); EXT int dict_similar(const dict_t *, const dict_t *); EXT dnode_t *dict_lookup(dict_t *, const void *); EXT dnode_t *dict_lower_bound(dict_t *, const void *); EXT dnode_t *dict_upper_bound(dict_t *, const void *); EXT void dict_insert(dict_t *, dnode_t *, const void *); EXT dnode_t *dict_delete(dict_t *, dnode_t *); EXT int dict_alloc_insert(dict_t *, const void *, void *); EXT void dict_delete_free(dict_t *, dnode_t *); EXT dnode_t *dict_first(dict_t *); EXT dnode_t *dict_last(dict_t *); EXT dnode_t *dict_next(dict_t *, dnode_t *); EXT dnode_t *dict_prev(dict_t *, dnode_t *); EXT dictcount_t dict_count(dict_t *); EXT int dict_isempty(dict_t *); EXT int dict_isfull(dict_t *); EXT int dict_contains(dict_t *, dnode_t *); EXT void dict_allow_dupes(dict_t *); EXT int dnode_is_in_a_dict(dnode_t *); EXT dnode_t *dnode_create(void *); EXT dnode_t *dnode_init(dnode_t *, void *); EXT void dnode_destroy(dnode_t *); EXT void *dnode_get(dnode_t *); EXT const void *dnode_getkey(dnode_t *); EXT void dnode_put(dnode_t *, void *); EXT void dict_process(dict_t *, void *, dnode_process_t); EXT void dict_load_begin(dict_load_t *, dict_t *); EXT void dict_load_next(dict_load_t *, dnode_t *, const void *); EXT void dict_load_end(dict_load_t *); EXT void dict_merge(dict_t *, dict_t *); #undef EXT #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) #ifdef KAZLIB_SIDEEFFECT_DEBUG #define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount) #else #define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount) #endif #define dict_count(D) ((D)->dict_nodecount) #define dict_isempty(D) ((D)->dict_nodecount == 0) #define dnode_get(N) ((N)->dict_data) #define dnode_getkey(N) ((N)->dict_key) #define dnode_put(N, X) ((N)->dict_data = (X)) #endif #endif pmacct-0.14.0/src/isis/isis_common.h0000640000175000017500000000346211705253204016277 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_common.h * some common data structures * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_COMMON_H_ #define _ISIS_COMMON_H_ /* * Area Address */ struct area_addr { u_char addr_len; u_char area_addr[20]; }; struct isis_passwd { u_char len; #define ISIS_PASSWD_TYPE_UNUSED 0 #define ISIS_PASSWD_TYPE_CLEARTXT 1 #define ISIS_PASSWD_TYPE_PRIVATE 255 u_char type; /* Authenticate SNPs? */ #define SNP_AUTH_SEND 0x01 #define SNP_AUTH_RECV 0x02 u_char snp_auth; u_char passwd[255]; }; /* * (Dynamic) Hostname * one struct for cache list * one struct for LSP TLV */ struct hostname { u_char namelen; u_char name[255]; }; /* * Supported Protocol IDs */ struct nlpids { u_char count; u_char nlpids[4]; /* FIXME: enough ? */ }; /* * Flags structure for SSN and SRM flags */ struct flags { int maxindex; struct list *free_idcs; }; #endif /* _ISIS_COMMON_H_ */ pmacct-0.14.0/src/isis/isisd.h0000640000175000017500000001131511734647141015100 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isisd.h * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISISD_H_ #define _ISISD_H_ #define ISISD_VERSION "0.0.7" #define ZEBRA_ROUTE_MAX 11 /* uncomment if you are a developer in bug hunt */ struct rmap { char *name; struct route_map *map; }; struct isis { u_long process_id; int sysid_set; u_char sysid[ISIS_SYS_ID_LEN]; /* SystemID for this IS */ struct list *area_list; /* list of IS-IS areas */ struct list *init_circ_list; struct list *nexthops; /* IPv4 next hops from this IS */ #ifdef ENABLE_IPV6 struct list *nexthops6; /* IPv6 next hops from this IS */ #endif /* ENABLE_IPV6 */ u_char max_area_addrs; /* maximumAreaAdresses */ struct area_addr *man_area_addrs; /* manualAreaAddresses */ u_int32_t debugs; /* bitmap for debug */ time_t uptime; /* when did we start */ struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */ /* Redistributed external information. */ struct route_table *external_info[ZEBRA_ROUTE_MAX + 1]; /* Redistribute metric info. */ struct { int type; /* Internal or External */ int value; /* metric value */ } dmetric[ZEBRA_ROUTE_MAX + 1]; struct { char *name; struct route_map *map; } rmap[ZEBRA_ROUTE_MAX + 1]; #ifdef ENABLE_IPV6 struct { struct { char *name; struct route_map *map; } rmap[ZEBRA_ROUTE_MAX + 1]; } inet6_afmode; #endif }; struct isis_area { struct isis *isis; /* back pointer */ dict_t *lspdb[ISIS_LEVELS]; /* link-state dbs */ struct isis_spftree *spftree[ISIS_LEVELS]; /* The v4 SPTs */ struct route_table *route_table[ISIS_LEVELS]; /* IPv4 routes */ #ifdef ENABLE_IPV6 struct isis_spftree *spftree6[ISIS_LEVELS]; /* The v6 SPTs */ struct route_table *route_table6[ISIS_LEVELS]; /* IPv6 routes */ #endif unsigned int min_bcast_mtu; struct list *circuit_list; /* IS-IS circuits */ struct flags flags; struct thread *t_tick; /* LSP walker */ struct thread *t_remove_aged; struct thread *t_lsp_l1_regenerate; struct thread *t_lsp_l2_regenerate; int lsp_regenerate_pending[ISIS_LEVELS]; struct thread *t_lsp_refresh[ISIS_LEVELS]; /* * Configurables */ struct isis_passwd area_passwd; struct isis_passwd domain_passwd; /* do we support dynamic hostnames? */ char dynhostname; /* do we support new style metrics? */ char newmetric; char oldmetric; /* identifies the routing instance */ char *area_tag; /* area addresses for this area */ struct list *area_addrs; u_int16_t max_lsp_lifetime[ISIS_LEVELS]; char is_type; /* level-1 level-1-2 or level-2-only */ u_int16_t lsp_refresh[ISIS_LEVELS]; /* minimum time allowed before lsp retransmission */ u_int16_t lsp_gen_interval[ISIS_LEVELS]; /* min interval between between consequtive SPFs */ u_int16_t min_spf_interval[ISIS_LEVELS]; /* the percentage of LSP mtu size used, before generating a new frag */ int lsp_frag_threshold; int ip_circuits; #ifdef ENABLE_IPV6 int ipv6_circuits; #endif /* ENABLE_IPV6 */ /* Counters */ u_int32_t circuit_state_changes; }; #if (!defined __ISISD_C) #define EXT extern #else #define EXT #endif EXT void isis_init (void); EXT struct isis_area *isis_area_lookup (const char *); EXT struct isis_area *isis_area_create (); #undef EXT #define DEBUG_ADJ_PACKETS (1<<0) #define DEBUG_CHECKSUM_ERRORS (1<<1) #define DEBUG_LOCAL_UPDATES (1<<2) #define DEBUG_PROTOCOL_ERRORS (1<<3) #define DEBUG_SNP_PACKETS (1<<4) #define DEBUG_UPDATE_PACKETS (1<<5) #define DEBUG_SPF_EVENTS (1<<6) #define DEBUG_SPF_STATS (1<<7) #define DEBUG_SPF_TRIGGERS (1<<8) #define DEBUG_RTE_EVENTS (1<<9) #define DEBUG_EVENTS (1<<10) #define DEBUG_ZEBRA (1<<11) #endif /* _ISISD_H_ */ pmacct-0.14.0/src/isis/isis_adjacency.c0000640000175000017500000001354211734674572016744 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_adjacency.c * handling of IS-IS adjacencies * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #define __ISIS_ADJACENCY_C #include "pmacct.h" #include "isis.h" #include "hash.h" #include "linklist.h" #include "dict.h" #include "thread.h" #include "isis_constants.h" #include "isis_common.h" #include "isisd.h" #include "isis_circuit.h" #include "isis_adjacency.h" #include "isis_misc.h" #include "isis_dynhn.h" #include "isis_pdu.h" extern struct isis *isis; static struct isis_adjacency * adj_alloc (u_char * id) { struct isis_adjacency *adj; adj = calloc(1, sizeof (struct isis_adjacency)); memcpy (adj->sysid, id, ISIS_SYS_ID_LEN); return adj; } struct isis_adjacency * isis_new_adj (u_char * id, u_char * snpa, int level, struct isis_circuit *circuit) { struct isis_adjacency *adj; int i; adj = adj_alloc (id); /* P2P kludge */ if (adj == NULL) { Log(LOG_ERR, "ERROR ( default/core/ISIS ): isis_new_adj() out of memory!\n"); return NULL; } if (snpa) { memcpy (adj->snpa, snpa, 6); } else { memset (adj->snpa, ' ', 6); } adj->circuit = circuit; adj->level = level; adj->flaps = 0; adj->last_flap = time (NULL); return adj; } struct isis_adjacency * isis_adj_lookup (u_char * sysid, struct list *adjdb) { struct isis_adjacency *adj; struct listnode *node; for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj)) if (memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN) == 0) return adj; return NULL; } struct isis_adjacency * isis_adj_lookup_snpa (u_char * ssnpa, struct list *adjdb) { struct listnode *node; struct isis_adjacency *adj; for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj)) if (memcmp (adj->snpa, ssnpa, ETH_ALEN) == 0) return adj; return NULL; } void isis_delete_adj (struct isis_adjacency *adj, struct list *adjdb) { if (!adj) return; /* When we recieve a NULL list, we will know its p2p. */ if (adjdb) listnode_delete (adjdb, adj); memset(&adj->expire, 0, sizeof(struct timeval)); if (adj->ipv4_addrs) list_delete (adj->ipv4_addrs); #ifdef ENABLE_IPV6 if (adj->ipv6_addrs) list_delete (adj->ipv6_addrs); #endif free(adj); return; } void isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state state, const char *reason) { int old_state; int level = adj->level; struct isis_circuit *circuit; old_state = adj->adj_state; adj->adj_state = state; circuit = adj->circuit; Log(LOG_DEBUG, "DEBUG ( default/core/ISIS ): ISIS-Adj (%s): Adjacency state change %d->%d: %s\n", circuit->area->area_tag, old_state, state, reason ? reason : "unspecified"); if (state == ISIS_ADJ_UP) { /* update counter & timers for debugging purposes */ adj->last_flap = time (NULL); adj->flaps++; /* 7.3.17 - going up on P2P -> send CSNP */ send_csnp (circuit, 1); send_csnp (circuit, 2); } else if (state == ISIS_ADJ_DOWN) { /* p2p interface */ adj->circuit->u.p2p.neighbor = NULL; isis_delete_adj (adj, NULL); } return; } int isis_adj_expire (struct isis_adjacency *adj) { int level; /* * Get the adjacency */ assert (adj); level = adj->level; memset(&adj->expire, 0, sizeof(struct timeval)); /* trigger the adj expire event */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "holding time expired"); return 0; } static const char * adj_state2string (int state) { switch (state) { case ISIS_ADJ_INITIALIZING: return "Initializing"; case ISIS_ADJ_UP: return "Up"; case ISIS_ADJ_DOWN: return "Down"; default: return "Unknown"; } return NULL; /* not reached */ } void isis_adjdb_iterate (struct list *adjdb, void (*func) (struct isis_adjacency *, void *), void *arg) { struct listnode *node, *nnode; struct isis_adjacency *adj; for (ALL_LIST_ELEMENTS (adjdb, node, nnode, adj)) (*func) (adj, arg); } void isis_adj_build_neigh_list (struct list *adjdb, struct list *list) { struct isis_adjacency *adj; struct listnode *node; if (!list) { Log(LOG_WARNING, "WARN (default/core/ISIS ): isis_adj_build_neigh_list(): NULL list\n"); return; } for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj)) { if (!adj) { Log(LOG_WARNING, "WARN (default/core/ISIS ): isis_adj_build_neigh_list(): NULL adj\n"); return; } if ((adj->adj_state == ISIS_ADJ_UP || adj->adj_state == ISIS_ADJ_INITIALIZING)) listnode_add (list, adj->snpa); } return; } void isis_adj_build_up_list (struct list *adjdb, struct list *list) { struct isis_adjacency *adj; struct listnode *node; if (!list) { Log(LOG_WARNING, "WARN (default/core/ISIS ): isis_adj_build_up_list(): NULL list\n"); return; } for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj)) { if (!adj) { Log(LOG_WARNING, "WARN (default/core/ISIS ): isis_adj_build_up_list(): NULL adj\n"); return; } if (adj->adj_state == ISIS_ADJ_UP) listnode_add (list, adj); } return; } pmacct-0.14.0/src/isis/isis_events.h0000640000175000017500000000314011705253204016304 0ustar paolopaolo/* * IS-IS Rout(e)ing protocol - isis_events.h * * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas 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. */ #ifndef _ISIS_EVENTS_H_ #define _ISIS_EVENTS_H_ #define AUTH_ERROR_TYPE_LSP 3 #define AUTH_ERROR_TYPE_SNP 2 #define AUTH_ERROR_TYPE_HELLO 1 #if (!defined __ISIS_EVENTS_C) #define EXT extern #else #define EXT #endif EXT void isis_event_system_type_change (struct isis_area *, int); EXT void isis_event_area_addr_change (struct isis_area *); EXT void isis_event_circuit_state_change (struct isis_circuit *, int); EXT void isis_event_circuit_type_change (struct isis_circuit *, int); EXT void isis_event_adjacency_state_change (struct isis_adjacency *, int); EXT void isis_event_auth_failure (char *, const char *, u_char *); #undef EXT #endif /* _ISIS_EVENTS_H_ */ pmacct-0.14.0/src/isis/isis.h0000640000175000017500000000404711731471736014742 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #ifndef _ISIS_H_ #define _ISIS_H_ /* includes */ #include "isis_ll.h" /* defines */ typedef u_int16_t afi_t; typedef u_int8_t safi_t; /* Flag manipulation macros. */ #define CHECK_FLAG(V,F) ((V) & (F)) #define SET_FLAG(V,F) (V) |= (F) #define UNSET_FLAG(V,F) (V) &= ~(F) /* Does the I/O error indicate that the operation should be retried later? */ #define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR)) /* structures */ static struct _devices_struct _isis_devices[] = { #if defined DLT_LINUX_SLL {isis_sll_handler, DLT_LINUX_SLL}, #endif {NULL, -1}, }; struct pcap_isis_callback_data { struct pcap_device *device; struct isis_circuit *circuit; }; /* prototypes */ #if (!defined __ISIS_C) #define EXT extern #else #define EXT #endif EXT void nfacctd_isis_wrapper(); EXT void skinny_isis_daemon(); EXT void isis_pdu_runner(u_char *, const struct pcap_pkthdr *, const u_char *); EXT int iso_handler(register struct packet_ptrs *); /* global variables */ EXT struct thread_master *master; EXT struct isis *isis; EXT struct in_addr router_id_zebra; /* XXX: do something with this eventually */ EXT struct timeval isis_now, isis_spf_deadline, isis_psnp_deadline; #undef EXT #endif pmacct-0.14.0/src/conntrack.c0000644000175000017500000004261511037474162015003 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente */ /* 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. */ #define __CONNTRACK_C #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "ip_flow.h" #include "classifier.h" #include "jhash.h" u_int32_t conntrack_total_nodes_v4; u_int32_t conntrack_total_nodes_v6; void init_conntrack_table() { if (config.conntrack_bufsz) conntrack_total_nodes_v4 = config.conntrack_bufsz / sizeof(struct conntrack_ipv4); else conntrack_total_nodes_v4 = DEFAULT_CONNTRACK_BUFFER_SIZE / sizeof(struct conntrack_ipv4); conntrack_ipv4_table = NULL; #if defined ENABLE_IPV6 if (config.conntrack_bufsz) conntrack_total_nodes_v6 = config.conntrack_bufsz / sizeof(struct conntrack_ipv6); else conntrack_total_nodes_v6 = DEFAULT_CONNTRACK_BUFFER_SIZE / sizeof(struct conntrack_ipv6); conntrack_ipv6_table = NULL; #endif } void conntrack_ftp_helper(time_t now, struct packet_ptrs *pptrs) { char *start = NULL, *end = NULL, *ptr; u_int16_t port[2]; int len; if (!pptrs->payload_ptr) return; len = strlen(pptrs->payload_ptr); /* truncated payload */ if (len < 4) return; /* XXX: is it correct to assume that the commands are in the first 4 bytes of the payload ? */ /* PORT/LPRT command, active FTP */ if ((pptrs->payload_ptr[0] == 'P' && pptrs->payload_ptr[1] == 'O' && pptrs->payload_ptr[2] == 'R' && pptrs->payload_ptr[3] == 'T') || (pptrs->payload_ptr[0] == 'L' && pptrs->payload_ptr[1] == 'P' && pptrs->payload_ptr[2] == 'R' && pptrs->payload_ptr[3] == 'T')) { start = strchr(pptrs->payload_ptr, ' '); end = strchr(pptrs->payload_ptr, '\r'); if (start && end) { /* getting the port number */ ptr = end; *end = '\0'; while (*ptr != ',' && ptr > start) ptr--; port[1] = atoi(ptr+1); *end = '\r'; end = ptr; *end = '\0'; while (*ptr != ',' && ptr > start) ptr--; port[0] = atoi(ptr+1); *end = ','; if (pptrs->l3_proto == ETHERTYPE_IP) insert_conntrack_ipv4(now, ((struct my_iphdr *) pptrs->iph_ptr)->ip_src.s_addr, ((struct my_iphdr *) pptrs->iph_ptr)->ip_dst.s_addr, port[0]*256+port[1], 0, IPPROTO_TCP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) insert_conntrack_ipv6(now, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_src, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_dst, port[0]*256+port[1], 0, IPPROTO_TCP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #endif } } /* 227/228 reply, passive (PASV/LPASV) FTP */ else if ((pptrs->payload_ptr[0] == '2' && pptrs->payload_ptr[1] == '2' && pptrs->payload_ptr[2] == '7' && pptrs->payload_ptr[3] == ' ') || (pptrs->payload_ptr[0] == '2' && pptrs->payload_ptr[1] == '2' && pptrs->payload_ptr[2] == '8' && pptrs->payload_ptr[3] == ' ')) { start = strchr(pptrs->payload_ptr, '('); end = strchr(pptrs->payload_ptr, ')'); if (start && end) { /* getting the port number */ ptr = end; *end = '\0'; while (*ptr != ',' && ptr > start) ptr--; port[1] = atoi(ptr+1); *end = ')'; end = ptr; *end = '\0'; while (*ptr != ',' && ptr > start) ptr--; port[0] = atoi(ptr+1); *end = ','; if (pptrs->l3_proto == ETHERTYPE_IP) insert_conntrack_ipv4(now, ((struct my_iphdr *) pptrs->iph_ptr)->ip_src.s_addr, ((struct my_iphdr *) pptrs->iph_ptr)->ip_dst.s_addr, port[0]*256+port[1], 0, IPPROTO_TCP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) insert_conntrack_ipv6(now, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_src, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_dst, port[0]*256+port[1], 0, IPPROTO_TCP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #endif } } /* EPRT command, Extended data port */ else if (pptrs->payload_ptr[0] == 'E' && pptrs->payload_ptr[1] == 'P' && pptrs->payload_ptr[2] == 'R' && pptrs->payload_ptr[3] == 'T') { start = strchr(pptrs->payload_ptr, ' '); end = strchr(pptrs->payload_ptr, '\r'); if (start && end) { /* getting the port number */ while (*end != '|' && end >= start) end--; if (*end != '|') return; ptr = end; *end = '\0'; while (*ptr != '|' && ptr >= start) ptr--; if (*ptr != '|') return; port[0] = atoi(ptr+1); *end = '|'; if (pptrs->l3_proto == ETHERTYPE_IP) insert_conntrack_ipv4(now, ((struct my_iphdr *) pptrs->iph_ptr)->ip_src.s_addr, ((struct my_iphdr *) pptrs->iph_ptr)->ip_dst.s_addr, port[0], 0, IPPROTO_TCP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) insert_conntrack_ipv6(now, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_src, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_dst, port[0], 0, IPPROTO_TCP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #endif } } /* 229 reply, extended passive (EPASV) FTP */ else if (pptrs->payload_ptr[0] == '2' && pptrs->payload_ptr[1] == '2' && pptrs->payload_ptr[2] == '9' && pptrs->payload_ptr[3] == ' ') { start = strchr(pptrs->payload_ptr, '('); end = strchr(pptrs->payload_ptr, ')'); if (start && end) { /* getting the port number */ while (*end != '|' && end >= start) end--; if (*end != '|') return; ptr = end; *end = '\0'; while (*ptr != '|' && ptr >= start) ptr--; if (*ptr != '|') return; port[0] = atoi(ptr+1); *end = '|'; if (pptrs->l3_proto == ETHERTYPE_IP) insert_conntrack_ipv4(now, ((struct my_iphdr *) pptrs->iph_ptr)->ip_src.s_addr, ((struct my_iphdr *) pptrs->iph_ptr)->ip_dst.s_addr, port[0], 0, IPPROTO_TCP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) insert_conntrack_ipv6(now, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_src, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_dst, port[0], 0, IPPROTO_TCP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #endif } } } void conntrack_rtsp_helper(time_t now, struct packet_ptrs *pptrs) { char *start = NULL, *end = NULL, *ptr; u_int16_t port[2]; int x = 0, len; port[0] = 0; port[1] = 0; if (!pptrs->payload_ptr) return; len = strlen(pptrs->payload_ptr); /* truncated payload */ if (len < 6) return; /* We need to look into RTSP SETUP messages */ if ( !strncmp(pptrs->payload_ptr, "SETUP ", 6) ) { start = strchr(pptrs->payload_ptr, '\n'); end = pptrs->payload_ptr+len; while (start && start < end) { start++; /* Then, we need to look into the Transport: line */ if ( !strncmp(start, "Transport:", 10) ) { if ( ptr = strchr(start, '\r') ) end = ptr; ptr = strchr(start, ':'); ptr++; while (ptr && ptr < end) { ptr++; /* Then, we need to search for the client_port= key */ if ( !strncmp(ptr, "client_port=", 12) ) { char *ss_start, *ss_sep, *ss_end; ss_end = strchr(ptr, ';'); /* If we are unable to find the trailing separator, lets return */ if (!ss_end) return; ss_start = strchr(ptr, '='); ss_start++; *ss_end = '\0'; /* We have reached the client_port info; let's handle it meaningfully: we expect either a single port or a range of ports (lo-hi) */ if ( ss_sep = strchr(ss_start, '-') ) { *ss_sep = '\0'; port[0] = atoi(ss_start); *ss_sep = '-'; port[1] = atoi(ss_sep+1); } else { port[0] = atoi(ss_start); port[1] = port[0]; } *ss_end = ';'; for (x = port[0]; x <= port[1]; x++) { if (pptrs->l3_proto == ETHERTYPE_IP) insert_conntrack_ipv4(now, ((struct my_iphdr *) pptrs->iph_ptr)->ip_src.s_addr, ((struct my_iphdr *) pptrs->iph_ptr)->ip_dst.s_addr, x, 0, IPPROTO_UDP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) insert_conntrack_ipv6(now, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_src, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_dst, x, 0, IPPROTO_UDP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #endif } } else ptr = strchr(ptr, ';'); } } else start = strchr(start, '\n'); } } } void conntrack_sip_helper(time_t now, struct packet_ptrs *pptrs) { char *start = NULL, *end = NULL, *ptr; u_int16_t port; int x = 0, len; if (!pptrs->payload_ptr) return; len = strlen(pptrs->payload_ptr); /* truncated payload */ if (len < 11) return; /* We need to look into SIP INVITE messages */ if ( !strncmp(pptrs->payload_ptr, "INVITE ", 7) || !strncmp(pptrs->payload_ptr, "SIP/2.0 200", 11) ) { /* We are searching for the m= line */ for ( start = pptrs->payload_ptr, end = pptrs->payload_ptr+len; start && start < end; start = strchr(start, '\n') ) { start++; if ( !strncmp(start, "m=", 2) ) { end = strchr(start, '\r'); break; } } if (!start || !end) return; ptr = start; while (*ptr != ' ' && ptr < end) ptr++; if (*ptr != ' ') return; while (*ptr == ' ' && ptr < end) ptr++; if (ptr == end) return; start = ptr; while (*ptr != ' ' && ptr < end) ptr++; if (ptr == end) return; *ptr = '\0'; port = atoi(start); *ptr = ' '; if (pptrs->l3_proto == ETHERTYPE_IP) insert_conntrack_ipv4(now, ((struct my_iphdr *) pptrs->iph_ptr)->ip_src.s_addr, ((struct my_iphdr *) pptrs->iph_ptr)->ip_dst.s_addr, port, 0, IPPROTO_UDP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) insert_conntrack_ipv6(now, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_src, &((struct ip6_hdr *) pptrs->iph_ptr)->ip6_dst, port, 0, IPPROTO_UDP, pptrs->class, NULL, CONNTRACK_GENERIC_LIFETIME); #endif } } void conntrack_irc_helper(time_t now, struct packet_ptrs *pptrs) { /* while (isprint(pptrs->payload_ptr[x]) || isspace(pptrs->payload_ptr[x])) { printf("%c", pptrs->payload_ptr[x]); x++; } printf("\n\n"); */ } void insert_conntrack_ipv4(time_t now, u_int32_t ip_src, u_int32_t ip_dst, u_int16_t port_src, u_int16_t port_dst, u_int8_t proto, pm_class_t class, conntrack_helper helper, time_t exp) { int size = sizeof(struct conntrack_ipv4); struct conntrack_ipv4 *ct_elem; if (conntrack_ipv4_table) { ct_elem = conntrack_ipv4_table; while (ct_elem->next && now < ct_elem->stamp+ct_elem->expiration) ct_elem = ct_elem->next; /* no entry expired and we reached the tail: let's allocate a new one */ if (now < ct_elem->stamp+ct_elem->expiration && !ct_elem->next) { if (conntrack_total_nodes_v4) { ct_elem->next = malloc(size); ct_elem->next->next = NULL; ct_elem = ct_elem->next; conntrack_total_nodes_v4--; } else { Log(LOG_INFO, "INFO ( default/core ): Conntrack/4 buffer full. Skipping packet.\n"); return; } } } /* let's allocate our first element */ else { conntrack_ipv4_table = malloc(size); ct_elem = conntrack_ipv4_table; ct_elem->next = NULL; } ct_elem->ip_src = ip_src; ct_elem->ip_dst = ip_dst; ct_elem->port_src = port_src; ct_elem->port_dst = port_dst; ct_elem->proto = proto; ct_elem->class = class; ct_elem->stamp = now; ct_elem->helper = helper; ct_elem->expiration = exp; } void search_conntrack(struct ip_flow_common *fp, struct packet_ptrs *pptrs, unsigned int idx) { if (pptrs->l3_proto == ETHERTYPE_IP) search_conntrack_ipv4(fp, pptrs, idx); #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) search_conntrack_ipv6(fp, pptrs, idx); #endif } void search_conntrack_ipv4(struct ip_flow_common *fp, struct packet_ptrs *pptrs, unsigned int idx) { struct conntrack_ipv4 *ct_elem = conntrack_ipv4_table, *aux = NULL; struct my_iphdr *iphp = (struct my_iphdr *)pptrs->iph_ptr; struct my_tlhdr *tlhp = (struct my_tlhdr *)pptrs->tlh_ptr; if (!conntrack_ipv4_table) return; while (ct_elem) { /* if (fp->last[idx] < ct_elem->stamp+CONNTRACK_GENERIC_LIFETIME) { printf("IP SRC: %x %x\n", iphp->ip_src.s_addr, ct_elem->ip_src); printf("IP DST: %x %x\n", iphp->ip_dst.s_addr, ct_elem->ip_dst); printf("SRC PORT: %u %u\n", ntohs(tlhp->src_port), ct_elem->port_src); printf("DST PORT: %u %u\n", ntohs(tlhp->dst_port), ct_elem->port_dst); printf("IP PROTO: %u %u\n", pptrs->l4_proto, ct_elem->proto); } */ /* conntrack entries usually have incomplete informations about the upcoming data channels; missing primitives are to be considered always true; then, we assure a) full match on the remaining primitives and b) our conntrack entry has not been aged out. */ if (fp->last[idx].tv_sec < ct_elem->stamp+ct_elem->expiration && (ct_elem->ip_src ? iphp->ip_src.s_addr == ct_elem->ip_src : 1) && (ct_elem->ip_dst ? iphp->ip_dst.s_addr == ct_elem->ip_dst : 1) && (ct_elem->proto ? pptrs->l4_proto == ct_elem->proto : 1) && (ct_elem->port_src ? ntohs(tlhp->src_port) == ct_elem->port_src : 1) && (ct_elem->port_dst ? ntohs(tlhp->dst_port) == ct_elem->port_dst : 1)) { fp->class[0] = ct_elem->class; fp->class[1] = ct_elem->class; fp->conntrack_helper = ct_elem->helper; ct_elem->stamp = 0; /* no aux means we are facing the first element in the chain */ if (aux) aux->next = ct_elem->next; else conntrack_ipv4_table = ct_elem->next; free(ct_elem); return; } aux = ct_elem; ct_elem = ct_elem->next; } } #if defined ENABLE_IPV6 void insert_conntrack_ipv6(time_t now, struct in6_addr *ip_src, struct in6_addr *ip_dst, u_int16_t port_src, u_int16_t port_dst, u_int8_t proto, pm_class_t class, conntrack_helper helper, time_t exp) { int size = sizeof(struct conntrack_ipv4); struct conntrack_ipv6 *ct_elem; if (conntrack_ipv6_table) { ct_elem = conntrack_ipv6_table; while (ct_elem->next && now < ct_elem->stamp+ct_elem->expiration) ct_elem = ct_elem->next; /* no entry expired and we reached the tail: let's allocate a new one */ if (now < ct_elem->stamp+ct_elem->expiration && !ct_elem->next) { if (conntrack_total_nodes_v6) { ct_elem->next = malloc(size); ct_elem->next->next = NULL; ct_elem = ct_elem->next; conntrack_total_nodes_v6--; } else { Log(LOG_INFO, "INFO ( default/core ): Conntrack/6 buffer full. Skipping packet.\n"); return; } } } /* let's allocate our first element */ else { conntrack_ipv6_table = malloc(size); ct_elem = conntrack_ipv6_table; ct_elem->next = NULL; } memcpy(&ct_elem->ip_src, ip_src, IP6AddrSz); memcpy(&ct_elem->ip_dst, ip_dst, IP6AddrSz); ct_elem->port_src = port_src; ct_elem->port_dst = port_dst; ct_elem->proto = proto; ct_elem->class = class; ct_elem->stamp = now; ct_elem->helper = helper; ct_elem->expiration = exp; } void search_conntrack_ipv6(struct ip_flow_common *fp, struct packet_ptrs *pptrs, unsigned int idx) { struct conntrack_ipv6 *ct_elem = conntrack_ipv6_table, *aux = NULL; struct ip6_hdr *iphp = (struct ip6_hdr *)pptrs->iph_ptr; struct my_tlhdr *tlhp = (struct my_tlhdr *)pptrs->tlh_ptr; if (!conntrack_ipv6_table) return; while (ct_elem) { /* conntrack entries usually have incomplete informations about the upcoming data channels; missing primitives are to be considered always true; then, we assure a) full match on the remaining primitives and b) our conntrack entry has not been aged out. */ if (fp->last[idx].tv_sec < ct_elem->stamp+ct_elem->expiration && (ct_elem->ip_src[0] ? !ip6_addr_cmp(&iphp->ip6_src, &ct_elem->ip_src) : 1) && (ct_elem->ip_dst[0] ? !ip6_addr_cmp(&iphp->ip6_dst, &ct_elem->ip_dst) : 1) && (ct_elem->proto ? pptrs->l4_proto == ct_elem->proto : 1) && (ct_elem->port_src ? ntohs(tlhp->src_port) == ct_elem->port_src : 1) && (ct_elem->port_dst ? ntohs(tlhp->dst_port) == ct_elem->port_dst : 1)) { fp->class[0] = ct_elem->class; fp->class[1] = ct_elem->class; fp->conntrack_helper = ct_elem->helper; ct_elem->stamp = 0; /* no aux means we are facing the first element in the chain */ if (aux) aux->next = ct_elem->next; else conntrack_ipv6_table = ct_elem->next; free(ct_elem); return; } aux = ct_elem; ct_elem = ct_elem->next; } } #endif pmacct-0.14.0/src/util.c0000644000175000017500000006673311735327144014007 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #define __UTIL_C /* includes */ #include "pmacct.h" /* functions */ void setnonblocking(int sock) { int opts; opts = fcntl(sock,F_GETFL); opts = (opts | O_NONBLOCK); fcntl(sock,F_SETFL,opts); } void setblocking(int sock) { int opts; opts = fcntl(sock, F_GETFL); opts & O_NONBLOCK ? opts ^= O_NONBLOCK : opts; fcntl(sock, F_SETFL, opts); } int daemonize() { int fdd; pid_t pid; pid = fork(); switch (pid) { case -1: return -1; case 0: break; default: exit(0); } if (setsid() == -1) return -1; fdd = open("/dev/null", O_RDWR, 0); if (fdd != -1) { dup2(fdd, 0); dup2(fdd, 1); dup2(fdd, 2); if (fdd > 2) close(fdd); } return 0; } char *extract_token(char **string, int delim) { char *token, *delim_ptr; if (!strlen(*string)) return NULL; start: if (delim_ptr = strchr(*string, delim)) { *delim_ptr = '\0'; token = *string; *string = delim_ptr+1; if (!strlen(token)) goto start; } else { token = *string; *string += strlen(*string); if (!strlen(token)) return NULL; } return token; } char *extract_plugin_name(char **string) { char *name, *delim_ptr; char name_start = '['; char name_end = ']'; if ((delim_ptr = strchr(*string, name_start))) { *delim_ptr = '\0'; name = delim_ptr+1; if ((delim_ptr = strchr(name, name_end))) *delim_ptr = '\0'; else { printf("ERROR: Not weighted parhentesis: '[%s'\n", name); exit(1); } } else return NULL; return name; } /* * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ char *copy_argv(register char **argv) { register char **p; register unsigned int len = 0; char *buf; char *src, *dst; p = argv; if (*p == 0) return NULL; while (*p) len += strlen(*p++) + 1; buf = (char *)malloc(len); if (buf == NULL) { Log(LOG_ERR, "ERROR: copy_argv: malloc()\n"); return NULL; } p = argv; dst = buf; while ((src = *p++) != NULL) { while ((*dst++ = *src++) != '\0'); dst[-1] = ' '; } dst[-1] = '\0'; return buf; } void trim_spaces(char *buf) { char *tmp_buf; int i, len; len = strlen(buf); tmp_buf = (char *)malloc(len + 1); if (tmp_buf == NULL) { Log(LOG_ERR, "ERROR: trim_spaces: malloc()\n"); return; } /* trimming spaces at beginning of the string */ for (i = 0; i <= len; i++) { if (!isspace(buf[i])) { if (i != 0) { strlcpy(tmp_buf, &buf[i], len+1-i); strlcpy(buf, tmp_buf, len+1-i); } break; } } /* trimming spaces at the end of the string */ for (i = strlen(buf)-1; i >= 0; i--) { if (isspace(buf[i])) buf[i] = '\0'; else break; } free(tmp_buf); } void trim_all_spaces(char *buf) { char *tmp_buf; int i = 0, len, quotes = FALSE; len = strlen(buf); tmp_buf = (char *)malloc(len + 1); if (tmp_buf == NULL) { Log(LOG_ERR, "ERROR: trim_all_spaces: malloc()\n"); return; } /* trimming all spaces */ while (i <= len) { if (buf[i] == '\'') { if (!quotes) quotes = TRUE; else if (quotes) quotes = FALSE; } if (isspace(buf[i]) && !quotes) { strlcpy(tmp_buf, &buf[i+1], len); strlcpy(&buf[i], tmp_buf, len); len--; } else i++; } free(tmp_buf); } void strip_quotes(char *buf) { char *ptr, *tmp_buf; int i = 0, len; len = strlen(buf); tmp_buf = (char *)malloc(len + 1); if (tmp_buf == NULL) { Log(LOG_ERR, "ERROR: strip_quotes: malloc()\n"); return; } ptr = buf; /* stripping all quote marks using a temporary buffer to avoid string corruption by strcpy() */ while (i <= len) { if (ptr[i] == '\'') { strcpy(tmp_buf, &ptr[i+1]); strcpy(&buf[i], tmp_buf); len--; } else i++; } free(tmp_buf); } int isblankline(char *line) { int len, j, n_spaces = 0; if (!line) return FALSE; len = strlen(line); for (j = 0; j < len; j++) if (isspace(line[j])) n_spaces++; if (n_spaces == len) return TRUE; else return FALSE; } int iscomment(char *line) { int len, j, first_char = TRUE; if (!line) return FALSE; len = strlen(line); for (j = 0; j <= len; j++) { if (!isspace(line[j])) first_char--; if (!first_char) { if (line[j] == '!') return TRUE; else return FALSE; } } return FALSE; } time_t roundoff_time(time_t t, char *value) { // char *value = config.sql_history_roundoff; struct tm *rounded; int len, j; rounded = localtime(&t); rounded->tm_sec = 0; /* default round off */ if (value) { len = strlen(value); for (j = 0; j < len; j++) { if (value[j] == 'm') rounded->tm_min = 0; else if (value[j] == 'h') { rounded->tm_min = 0; rounded->tm_hour = 0; } else if (value[j] == 'd') { rounded->tm_min = 0; rounded->tm_hour = 0; rounded->tm_mday = 1; } else if (value[j] == 'w') { rounded->tm_min = 0; rounded->tm_hour = 0; while (rounded->tm_wday > 1) { rounded->tm_mday--; rounded->tm_wday--; } } else if (value[j] == 'M') { rounded->tm_min = 0; rounded->tm_hour = 0; rounded->tm_mday = 1; rounded->tm_mon = 0; } else Log(LOG_WARNING, "WARN: ignoring unknown round off value: %c\n", value[j]); } } t = mktime(rounded); return t; } /* op = 0 (add); op = 1 (sub) */ time_t calc_monthly_timeslot(time_t t, int howmany, int op) { time_t base = t, final; struct tm *tmt; tmt = localtime(&t); while (howmany) { tmt->tm_mday = 1; if (op == ADD) tmt->tm_mon++; else if (op == SUB) tmt->tm_mon--; howmany--; } final = mktime(tmt); return (final-base); } FILE *open_logfile(char *filename) { char timebuf[SRVBUFLEN]; FILE *file = NULL; struct tm *tmnow; time_t now; uid_t owner = -1; gid_t group = -1; if (config.files_uid) owner = config.files_uid; if (config.files_gid) group = config.files_gid; file = fopen(filename, "a"); if (file) { if (chown(filename, owner, group) == -1) printf("WARN: Unable to chown() logfile '%s': %s\n", filename, strerror(errno)); } else { printf("WARN: Unable to fopen() logfile '%s': %s\n", filename, strerror(errno)); file = NULL; } return file; } FILE *open_print_output_file(char *filename, time_t now) { char buf[LARGEBUFLEN]; FILE *file = NULL; struct tm *tmnow; uid_t owner = -1; gid_t group = -1; if (config.files_uid) owner = config.files_uid; if (config.files_gid) group = config.files_gid; tmnow = localtime(&now); strftime(buf, LARGEBUFLEN, filename, tmnow); file = fopen(buf, "w"); if (file) { if (chown(buf, owner, group) == -1) Log(LOG_WARNING, "WARN: Unable to chown() print_ouput_file '%s': %s\n", buf, strerror(errno)); if (file_lock(fileno(file))) { Log(LOG_ALERT, "ALERT: Unable to obtain lock for print_ouput_file '%s'.\n", buf); file = NULL; } } else { Log(LOG_ERR, "ERROR: Unable to open print_ouput_file '%s'\n", buf); file = NULL; } return file; } void write_pid_file(char *filename) { FILE *file; char pid[10]; uid_t owner = -1; gid_t group = -1; unlink(filename); if (config.files_uid) owner = config.files_uid; if (config.files_gid) group = config.files_gid; file = fopen(filename,"w"); if (file) { if (chown(filename, owner, group) == -1) Log(LOG_WARNING, "WARN: Unable to chown() pidfile '%s': %s\n", filename, strerror(errno)); if (file_lock(fileno(file))) { Log(LOG_ALERT, "ALERT: Unable to obtain lock for pidfile '%s'.\n", filename); return; } sprintf(pid, "%d\n", getpid()); fwrite(pid, strlen(pid), 1, file); file_unlock(fileno(file)); fclose(file); } else { Log(LOG_ERR, "ERROR: Unable to open pidfile '%s'\n", filename); return; } } void write_pid_file_plugin(char *filename, char *type, char *name) { int len = strlen(filename) + strlen(type) + strlen(name) + 3; FILE *file; char *fname, pid[10], minus[] = "-"; uid_t owner = -1; gid_t group = -1; fname = malloc(len); memset(fname, 0, sizeof(fname)); strcpy(fname, filename); strcat(fname, minus); strcat(fname, type); strcat(fname, minus); strcat(fname, name); config.pidfile = fname; unlink(fname); if (config.files_uid) owner = config.files_uid; if (config.files_gid) group = config.files_gid; file = fopen(fname,"w"); if (file) { if (chown(fname, owner, group) == -1) Log(LOG_WARNING, "WARN: Unable to chown() '%s': %s\n", fname, strerror(errno)); if (file_lock(fileno(file))) { Log(LOG_ALERT, "ALERT: Unable to obtain lock of '%s'.\n", fname); return; } sprintf(pid, "%d\n", getpid()); fwrite(pid, strlen(pid), 1, file); file_unlock(fileno(file)); fclose(file); } else { Log(LOG_ERR, "ERROR: Unable to open file '%s'\n", fname); return; } } void remove_pid_file(char *filename) { unlink(filename); } int file_lock(int fd) { int ret; #if defined SOLARIS flock_t lock; lock.l_type = F_WRLCK; lock.l_whence = 0; lock.l_start = 0; lock.l_len = 0; ret = fcntl(fd, F_SETLK, &lock); return((ret == -1) ? -1 : 0); #else ret = flock(fd, LOCK_EX); return ret; #endif } int file_unlock(int fd) { int ret; #if defined SOLARIS flock_t lock; lock.l_type = F_UNLCK; lock.l_whence = 0; lock.l_start = 0; lock.l_len = 0; ret = fcntl(fd, F_SETLK, &lock); return((ret == -1) ? -1 : 0); #else ret = flock(fd, LOCK_UN); return ret; #endif } int sanitize_buf_net(char *filename, char *buf, int rows) { if (!sanitize_buf(buf)) { if (!strchr(buf, '/')) { Log(LOG_ERR, "ERROR ( %s ): Missing '/' separator at line %d. Ignoring.\n", filename, rows); return TRUE; } } else return TRUE; return FALSE; } int sanitize_buf(char *buf) { int x = 0, valid_char = 0; trim_all_spaces(buf); while (x < strlen(buf)) { if (!isspace(buf[x])) valid_char++; x++; } if (!valid_char) return TRUE; if (buf[0] == '!') return TRUE; return FALSE; } int check_not_valid_char(char *filename, char *buf, int c) { if (!buf) return FALSE; if (strchr(buf, c)) { Log(LOG_ERR, "ERROR ( %s ): Invalid symbol '%c' detected. ", filename, c); return TRUE; } else return FALSE; } void mark_columns(char *buf) { int len, x, word = FALSE, quotes = FALSE; if (!buf) return; len = strlen(buf); for (x = 0; x < len; x++) { if (buf[x] == '\'') { if (!quotes) quotes = TRUE; else if (quotes) quotes = FALSE; } if ((isalpha(buf[x])||isdigit(buf[x])||ispunct(buf[x])) && !word) word = TRUE; if (isspace(buf[x]) && word && !quotes) { buf[x] = '|'; word = FALSE; } } /* removing trailing '|' if any */ x = strlen(buf); word = FALSE; while (x > 0) { if (buf[x] == '|' && !word) buf[x] = '\0'; if ((isalpha(buf[x])||isdigit(buf[x])||ispunct(buf[x])) && !word) word = TRUE; x--; } } int Setsocksize(int s, int level, int optname, void *optval, int optlen) { int ret, len, saved, value; memcpy(&value, optval, sizeof(int)); getsockopt(s, level, optname, &saved, &len); if (value > saved) { for (; value; value >>= 1) { ret = setsockopt(s, level, optname, &value, optlen); if (ret >= 0) break; } if (!value) setsockopt(s, level, optname, &saved, len); } return ret; } void *map_shared(void *addr, size_t len, int prot, int flags, int fd, off_t off) { #if defined USE_DEVZERO void *mem; int devzero; devzero = open ("/dev/zero", O_RDWR); if (devzero < 0) return MAP_FAILED; mem = mmap(addr, len, prot, flags, devzero, off); close(devzero); return mem; #else /* MAP_ANON or MAP_ANONYMOUS */ return (void *)mmap(addr, len, prot, flags, fd, off); #endif } void lower_string(char *string) { int i = 0; while (string[i] != '\0') { string[i] = tolower(string[i]); i++; } } void evaluate_sums(u_int64_t *wtc, char *name, char *type) { int tag = FALSE; int tag2 = FALSE; int class = FALSE; int flows = FALSE; if (*wtc & COUNT_ID) { *wtc ^= COUNT_ID; tag = TRUE; } if (*wtc & COUNT_ID2) { *wtc ^= COUNT_ID2; tag2 = TRUE; } if (*wtc & COUNT_CLASS) { *wtc ^= COUNT_CLASS; class = TRUE; } if (*wtc & COUNT_FLOWS) { *wtc ^= COUNT_FLOWS; flows = TRUE; } if (*wtc & COUNT_SUM_MAC) { if (*wtc != COUNT_SUM_MAC) { *wtc = COUNT_SUM_MAC; Log(LOG_WARNING, "WARN ( %s/%s ): SUM aggregation is to be used alone. Resetting other aggregation methods.\n", name, type); } } if (*wtc & COUNT_SUM_HOST) { if (*wtc != COUNT_SUM_HOST) { *wtc = COUNT_SUM_HOST; Log(LOG_WARNING, "WARN ( %s/%s ): SUM aggregation is to be used alone. Resetting other aggregation methods.\n", name, type); } } else if (*wtc & COUNT_SUM_NET) { if (*wtc != COUNT_SUM_NET) { *wtc = COUNT_SUM_NET; Log(LOG_WARNING, "WARN ( %s/%s ): SUM aggregation is to be used alone. Resetting other aggregation methods.\n", name, type); } } else if (*wtc & COUNT_SUM_AS) { if (*wtc != COUNT_SUM_AS) { *wtc = COUNT_SUM_AS; Log(LOG_WARNING, "WARN ( %s/%s ): SUM aggregation is to be used alone. Resetting other aggregation methods.\n", name, type); } } else if (*wtc & COUNT_SUM_PORT) { if (*wtc != COUNT_SUM_PORT) { *wtc = COUNT_SUM_PORT; Log(LOG_WARNING, "WARN ( %s/%s ): SUM aggregation is to be used alone. Resetting other aggregation methods.\n", name, type); } } if (tag) *wtc |= COUNT_ID; if (tag2) *wtc |= COUNT_ID2; if (class) *wtc |= COUNT_CLASS; if (flows) *wtc |= COUNT_FLOWS; } int file_archive(const char *path, int rotations) { struct stat st; char *new_path; int j, ret, len = strlen(path)+11; new_path = malloc(len); memset(new_path, 0, len); for (j = 1; j < rotations; j++) { snprintf(new_path, len, "%s.%d", path, j); ret = stat(new_path, &st); if (ret < 0) { rename(path, new_path); return 0; } } /* we should never reach this point */ Log(LOG_ALERT, "ALERT: No more recovery logfile ( %s ) rotations allowed. Data is getting lost.\n", path); return -1; } void stop_all_childs() { my_sigint_handler(0); /* it does same thing */ } void strftime_same(char *s, int max, char *tmp, const time_t *now) { struct tm *nowtm; nowtm = localtime(now); strftime(tmp, max, s, nowtm); strlcpy(s, tmp, max); } int read_SQLquery_from_file(char *path, char *buf, int size) { FILE *f; char *ptr; int ret; memset(buf, 0, size); f = fopen(path, "r"); if (!f) { Log(LOG_ERR, "ERROR: %s does not exist.\n", path); return(0); } ret = fread(buf, size, 1, f); if (ret != 1 && !feof(f)) { Log(LOG_ERR, "ERROR: Unable to read from SQL schema '%s': %s\n", path, strerror(errno)); return(0); } fclose(f); ptr = strrchr(buf, ';'); if (!ptr) { Log(LOG_ERR, "ERROR: missing trailing ';' in SQL query read from %s.\n", path); return(0); } else *ptr = '\0'; return (int)*ptr-(int)*buf; } void stick_bosbit(u_char *label) { u_char *ptr; ptr = label+2; *ptr |= 0x1; } /* * timeval_cmp(): returns > 0 if a > b; < 0 if a < b; 0 if a == b. */ int timeval_cmp(struct timeval *a, struct timeval *b) { if (a->tv_sec > b->tv_sec) return 1; if (a->tv_sec < b->tv_sec) return -1; if (a->tv_sec == b->tv_sec) { if (a->tv_usec > b->tv_usec) return 1; if (a->tv_usec < b->tv_usec) return -1; if (a->tv_usec == b->tv_usec) return 0; } } /* * exit_all(): Core Process exit lane. Not meant to be a nice shutdown method: it is * an exit() replacement that sends kill signals to the plugins. */ void exit_all(int status) { struct plugins_list_entry *list = plugins_list; #if defined (IRIX) || (SOLARIS) signal(SIGCHLD, SIG_IGN); #else signal(SIGCHLD, ignore_falling_child); #endif while (list) { if (memcmp(list->type.string, "core", sizeof("core"))) kill(list->pid, SIGKILL); list = list->next; } wait(NULL); if (config.pidfile) remove_pid_file(config.pidfile); exit(status); } /* exit_plugin(): meant to be called on exit by plugins; it is a simple wrapper to enforce some final operations before shutting down */ void exit_plugin(int status) { if (config.pidfile) remove_pid_file(config.pidfile); exit(status); } void reset_tag_status(struct packet_ptrs_vector *pptrsv) { pptrsv->v4.tag = FALSE; pptrsv->vlan4.tag = FALSE; pptrsv->mpls4.tag = FALSE; pptrsv->vlanmpls4.tag = FALSE; pptrsv->v4.tag2 = FALSE; pptrsv->vlan4.tag2 = FALSE; pptrsv->mpls4.tag2 = FALSE; pptrsv->vlanmpls4.tag2 = FALSE; #if defined ENABLE_IPV6 pptrsv->v6.tag = FALSE; pptrsv->vlan6.tag = FALSE; pptrsv->mpls6.tag = FALSE; pptrsv->vlanmpls6.tag = FALSE; pptrsv->v6.tag2 = FALSE; pptrsv->vlan6.tag2 = FALSE; pptrsv->mpls6.tag2 = FALSE; pptrsv->vlanmpls6.tag2 = FALSE; #endif } void reset_net_status(struct packet_ptrs *pptrs) { pptrs->lm_mask_src = FALSE; pptrs->lm_mask_dst = FALSE; pptrs->lm_method_src = FALSE; pptrs->lm_method_dst = FALSE; } void reset_net_status_v(struct packet_ptrs_vector *pptrsv) { pptrsv->v4.lm_mask_src = FALSE; pptrsv->vlan4.lm_mask_src = FALSE; pptrsv->mpls4.lm_mask_src = FALSE; pptrsv->vlanmpls4.lm_mask_src = FALSE; pptrsv->v4.lm_mask_dst = FALSE; pptrsv->vlan4.lm_mask_dst = FALSE; pptrsv->mpls4.lm_mask_dst = FALSE; pptrsv->vlanmpls4.lm_mask_dst = FALSE; pptrsv->v4.lm_method_src = FALSE; pptrsv->vlan4.lm_method_src = FALSE; pptrsv->mpls4.lm_method_src = FALSE; pptrsv->vlanmpls4.lm_method_src = FALSE; pptrsv->v4.lm_method_dst = FALSE; pptrsv->vlan4.lm_method_dst = FALSE; pptrsv->mpls4.lm_method_dst = FALSE; pptrsv->vlanmpls4.lm_method_dst = FALSE; #if defined ENABLE_IPV6 pptrsv->v6.lm_mask_src = FALSE; pptrsv->vlan6.lm_mask_src = FALSE; pptrsv->mpls6.lm_mask_src = FALSE; pptrsv->vlanmpls6.lm_mask_src = FALSE; pptrsv->v6.lm_mask_dst = FALSE; pptrsv->vlan6.lm_mask_dst = FALSE; pptrsv->mpls6.lm_mask_dst = FALSE; pptrsv->vlanmpls6.lm_mask_dst = FALSE; pptrsv->v6.lm_method_src = FALSE; pptrsv->vlan6.lm_method_src = FALSE; pptrsv->mpls6.lm_method_src = FALSE; pptrsv->vlanmpls6.lm_method_src = FALSE; pptrsv->v6.lm_method_dst = FALSE; pptrsv->vlan6.lm_method_dst = FALSE; pptrsv->mpls6.lm_method_dst = FALSE; pptrsv->vlanmpls6.lm_method_dst = FALSE; #endif } void reset_shadow_status(struct packet_ptrs_vector *pptrsv) { pptrsv->v4.shadow = FALSE; pptrsv->vlan4.shadow = FALSE; pptrsv->mpls4.shadow = FALSE; pptrsv->vlanmpls4.shadow = FALSE; #if defined ENABLE_IPV6 pptrsv->v6.shadow = FALSE; pptrsv->vlan6.shadow = FALSE; pptrsv->mpls6.shadow = FALSE; pptrsv->vlanmpls6.shadow = FALSE; #endif } void reset_fallback_status(struct packet_ptrs *pptrs) { pptrs->renormalized = FALSE; } void set_default_preferences(struct configuration *cfg) { if (!cfg->nfacctd_as) cfg->nfacctd_as = NF_AS_KEEP; if (!cfg->nfacctd_bgp_peer_as_src_type) cfg->nfacctd_bgp_peer_as_src_type = BGP_SRC_PRIMITIVES_KEEP; if (!cfg->nfacctd_bgp_src_std_comm_type) cfg->nfacctd_bgp_src_std_comm_type = BGP_SRC_PRIMITIVES_KEEP; if (!cfg->nfacctd_bgp_src_ext_comm_type) cfg->nfacctd_bgp_src_ext_comm_type = BGP_SRC_PRIMITIVES_KEEP; if (!cfg->nfacctd_bgp_src_as_path_type) cfg->nfacctd_bgp_src_as_path_type = BGP_SRC_PRIMITIVES_KEEP; if (!cfg->nfacctd_bgp_src_local_pref_type) cfg->nfacctd_bgp_src_local_pref_type = BGP_SRC_PRIMITIVES_KEEP; if (!cfg->nfacctd_bgp_src_med_type) cfg->nfacctd_bgp_src_med_type = BGP_SRC_PRIMITIVES_KEEP; } void set_shadow_status(struct packet_ptrs *pptrs) { pptrs->shadow = TRUE; } void set_sampling_table(struct packet_ptrs_vector *pptrsv, u_char *t) { pptrsv->v4.sampling_table = t; pptrsv->vlan4.sampling_table = t; pptrsv->mpls4.sampling_table = t; pptrsv->vlanmpls4.sampling_table = t; #if defined ENABLE_IPV6 pptrsv->v6.sampling_table = t; pptrsv->vlan6.sampling_table = t; pptrsv->mpls6.sampling_table = t; pptrsv->vlanmpls6.sampling_table = t; #endif } struct packet_ptrs *copy_packet_ptrs(struct packet_ptrs *pptrs) { struct packet_ptrs *new_pptrs; int offset; u_char dummy_tlhdr[16]; /* Copy the whole structure first */ if ((new_pptrs = malloc(sizeof(struct packet_ptrs))) == NULL) { return NULL; } memcpy(new_pptrs, pptrs, sizeof(struct packet_ptrs)); /* Copy the packet buffer */ if ((new_pptrs->packet_ptr = malloc(pptrs->pkthdr->caplen)) == NULL) { free(new_pptrs); return NULL; } memcpy(new_pptrs->packet_ptr, pptrs->packet_ptr, pptrs->pkthdr->caplen); /* Copy the pcap packet header */ if ((new_pptrs->pkthdr = malloc(sizeof(struct pcap_pkthdr))) == NULL) { free(new_pptrs->packet_ptr); free(new_pptrs); return NULL; } memcpy(new_pptrs->pkthdr, pptrs->pkthdr, sizeof(struct pcap_pkthdr)); /* Fix the pointers */ offset = (int) new_pptrs->packet_ptr - (int) pptrs->packet_ptr; /* Pointers can be NULL */ if (pptrs->iph_ptr) new_pptrs->iph_ptr += offset; if (pptrs->tlh_ptr) if(pptrs->tlh_ptr > pptrs->packet_ptr && pptrs->tlh_ptr < pptrs->packet_ptr+offset) // If it is not a dummy tlh_ptr new_pptrs->tlh_ptr += offset; else { memset(dummy_tlhdr, 0, sizeof(dummy_tlhdr)); new_pptrs->tlh_ptr = dummy_tlhdr; } if (pptrs->payload_ptr) new_pptrs->payload_ptr += offset; return new_pptrs; } void free_packet_ptrs(struct packet_ptrs *pptrs) { free(pptrs->pkthdr); free(pptrs->packet_ptr); free(pptrs); } #if DEBUG_TIMING void start_timer(struct mytimer *t) { gettimeofday(&t->t0, NULL); } void stop_timer(struct mytimer *t, const char *format, ...) { char msg[1024]; va_list ap; gettimeofday(&t->t1, NULL); va_start(ap, format); vsnprintf(msg, 1024, format, ap); va_end(ap); fprintf(stderr, "TIMER:%s:%d\n", msg, (t->t1.tv_sec - t->t0.tv_sec) * 1000000 + (t->t1.tv_usec - t->t0.tv_usec)); } #endif void evaluate_bgp_aspath_radius(char *path, int len, int radius) { int count, idx; for (idx = 0, count = 0; idx < len; idx++) { if (path[idx] == ' ') count++; if (count == radius) { path[idx] = '\0'; memset(&path[idx+1], 0, len-strlen(path)); break; } } } void copy_stdcomm_to_asn(char *stdcomm, as_t *asn, int is_origin) { char *delim, *delim2; char *p1, *p2; if (!stdcomm || !strlen(stdcomm) || (delim = strchr(stdcomm, ':')) == NULL) return; delim2 = strchr(stdcomm, ','); *delim = '\0'; if (delim2) *delim2 = '\0'; p1 = stdcomm; p2 = delim+1; if (is_origin) *asn = atoi(p2); else *asn = atoi(p1); } void *Malloc(unsigned int size) { unsigned char *obj; obj = (unsigned char *) malloc(size); if (!obj) { sbrk(size); obj = (unsigned char *) malloc(size); if (!obj) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to grab enough memory (requested: %u bytes). Exiting ...\n", config.name, config.type, size); exit_plugin(1); } } return obj; } void load_allow_file(char *filename, struct hosts_table *t) { FILE *file; char buf[SRVBUFLEN]; int index = 0; if (filename) { if ((file = fopen(filename, "r")) == NULL) { Log(LOG_ERR, "ERROR ( default/core ): allow file '%s' not found\n", filename); exit(1); } memset(t->table, 0, sizeof(t->table)); while (!feof(file)) { if (index >= MAX_MAP_ENTRIES) break; /* XXX: we shouldn't exit silently */ memset(buf, 0, SRVBUFLEN); if (fgets(buf, SRVBUFLEN, file)) { if (!sanitize_buf(buf)) { if (str_to_addr(buf, &t->table[index])) index++; else Log(LOG_WARNING, "WARN ( default/core ): 'nfacctd_allow_file': Bad IP address '%s'. Ignored.\n", buf); } } } t->num = index; /* Set to -1 to distinguish between no map and empty map conditions */ if (!t->num) t->num = -1; fclose(file); } } void load_bgp_md5_file(char *filename, struct bgp_md5_table *t) { FILE *file; char buf[SRVBUFLEN], *ptr; int index = 0; if (filename) { if ((file = fopen(filename, "r")) == NULL) { Log(LOG_ERR, "ERROR ( default/core/BGP ): BGP MD5 file '%s' not found\n", filename); exit(1); } memset(t->table, 0, sizeof(t->table)); while (!feof(file)) { if (index >= BGP_MD5_MAP_ENTRIES) break; /* XXX: we shouldn't exit silently */ memset(buf, 0, SRVBUFLEN); if (fgets(buf, SRVBUFLEN, file)) { if (!sanitize_buf(buf)) { char *endptr, *token; int tk_idx = 0, ret = 0, len = 0; ptr = buf; memset(&t->table[index], 0, sizeof(t->table[index])); while ( (token = extract_token(&ptr, ',')) && tk_idx < 2 ) { if (tk_idx == 0) ret = str_to_addr(token, &t->table[index].addr); else if (tk_idx == 1) { strlcpy(t->table[index].key, token, TCP_MD5SIG_MAXKEYLEN); len = strlen(t->table[index].key); } tk_idx++; } if (ret > 0 && len > 0) index++; else Log(LOG_WARNING, "WARN ( default/core/BGP ): 'bgp_daemon_md5_file': line '%s' ignored.\n", buf); } } } t->num = index; /* Set to -1 to distinguish between no map and empty map conditions */ if (!t->num) t->num = -1; fclose(file); } } void unload_bgp_md5_file(struct bgp_md5_table *t) { int index = 0; while (index < t->num) { memset(t->table[index].key, 0, TCP_MD5SIG_MAXKEYLEN); index++; } } int check_allow(struct hosts_table *allow, struct sockaddr *sa) { int index; for (index = 0; index < allow->num; index++) { if (((struct sockaddr *)sa)->sa_family == allow->table[index].family) { if (allow->table[index].family == AF_INET) { if (((struct sockaddr_in *)sa)->sin_addr.s_addr == allow->table[index].address.ipv4.s_addr) return TRUE; } #if defined ENABLE_IPV6 else if (allow->table[index].family == AF_INET6) { if (!ip6_addr_cmp(&(((struct sockaddr_in6 *)sa)->sin6_addr), &allow->table[index].address.ipv6)) return TRUE; } #endif } } return FALSE; } pmacct-0.14.0/src/ports_aggr.h0000644000175000017500000000217211037474163015170 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente */ /* 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. */ /* defines */ #define PORTS_TABLE_ENTRIES 65536 /* structures */ struct ports_table { u_int8_t table[PORTS_TABLE_ENTRIES]; time_t timestamp; }; /* prototypes */ #if (!defined __NET_AGGR_C) #define EXT extern #else #define EXT #endif EXT void load_ports(char *, struct ports_table *); #undef EXT pmacct-0.14.0/src/addr.h0000644000175000017500000000215610530072467013733 0ustar paolopaolo/* defines */ #if defined IM_LITTLE_ENDIAN #define IS_IPV4_MULTICAST(a) ((((u_int32_t)(a)) & 0x000000f0) == 0x000000e0) #else #define IS_IPV4_MULTICAST(a) ((((u_int32_t)(a)) & 0xf0000000) == 0xe0000000) #endif #define IS_IPV6_MULTICAST(a) (((const uint8_t *) (a))[0] == 0xff) /* prototypes */ #if (!defined __ADDR_C) #define EXT extern #else #define EXT #endif EXT unsigned int str_to_addr(const char *, struct host_addr *); EXT unsigned int addr_to_str(char *, const struct host_addr *); EXT unsigned int addr_to_sa(struct sockaddr *, struct host_addr *, u_int16_t); EXT unsigned int sa_to_addr(struct sockaddr *, struct host_addr *, u_int16_t *); EXT unsigned int sa_addr_cmp(struct sockaddr *, struct host_addr *); EXT void *pm_htonl6(void *); EXT void *pm_ntohl6(void *); EXT u_int64_t pm_htonll(u_int64_t); EXT u_int64_t pm_ntohll(u_int64_t); EXT unsigned int ip6_addr_cmp(void *, void *); EXT void ip6_addr_cpy(void *, void *); EXT void etheraddr_string(const u_char *, char *); EXT int string_etheraddr(const u_char *, char *); EXT int is_multicast(struct host_addr *); EXT void clean_sin_addr(struct sockaddr *); #undef EXT pmacct-0.14.0/src/jhash.h0000644000175000017500000000655610530072467014126 0ustar paolopaolo#ifndef _LINUX_JHASH_H #define _LINUX_JHASH_H /* jhash.h: Jenkins hash support. * * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) * * http://burtleburtle.net/bob/hash/ * * These are the credits from Bob's sources: * * lookup2.c, by Bob Jenkins, December 1996, Public Domain. * hash(), hash2(), hash3, and mix() are externally useful functions. * Routines to test the hash are included if SELF_TEST is defined. * You can use this free for any purpose. It has no warranty. * * Copyright (C) 2003 David S. Miller (davem@redhat.com) * * I've modified Bob's hash to be useful in the Linux kernel, and * any bugs present are surely my fault. -DaveM */ /* NOTE: Arguments are modified. */ #define __jhash_mix(a, b, c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<<8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>>5); \ a -= b; a -= c; a ^= (c>>3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } /* The golden ration: an arbitrary value */ #define JHASH_GOLDEN_RATIO 0x9e3779b9 /* The most generic version, hashes an arbitrary sequence * of bytes. No alignment or length assumptions are made about * the input key. */ Inline u_int32_t jhash(void *key, u_int32_t length, u_int32_t initval) { u_int32_t a, b, c, len; u_int8_t *k = key; len = length; a = b = JHASH_GOLDEN_RATIO; c = initval; while (len >= 12) { a += (k[0] +((u_int32_t)k[1]<<8) +((u_int32_t)k[2]<<16) +((u_int32_t)k[3]<<24)); b += (k[4] +((u_int32_t)k[5]<<8) +((u_int32_t)k[6]<<16) +((u_int32_t)k[7]<<24)); c += (k[8] +((u_int32_t)k[9]<<8) +((u_int32_t)k[10]<<16)+((u_int32_t)k[11]<<24)); __jhash_mix(a,b,c); k += 12; len -= 12; } c += length; switch (len) { case 11: c += ((u_int32_t)k[10]<<24); case 10: c += ((u_int32_t)k[9]<<16); case 9 : c += ((u_int32_t)k[8]<<8); case 8 : b += ((u_int32_t)k[7]<<24); case 7 : b += ((u_int32_t)k[6]<<16); case 6 : b += ((u_int32_t)k[5]<<8); case 5 : b += k[4]; case 4 : a += ((u_int32_t)k[3]<<24); case 3 : a += ((u_int32_t)k[2]<<16); case 2 : a += ((u_int32_t)k[1]<<8); case 1 : a += k[0]; }; __jhash_mix(a,b,c); return c; } /* A special optimized version that handles 1 or more of u_int32_ts. * The length parameter here is the number of u_int32_ts in the key. */ Inline u_int32_t jhash2(u_int32_t *k, u_int32_t length, u_int32_t initval) { u_int32_t a, b, c, len; a = b = JHASH_GOLDEN_RATIO; c = initval; len = length; while (len >= 3) { a += k[0]; b += k[1]; c += k[2]; __jhash_mix(a, b, c); k += 3; len -= 3; } c += length * 4; switch (len) { case 2 : b += k[1]; case 1 : a += k[0]; }; __jhash_mix(a,b,c); return c; } /* A special ultra-optimized versions that knows they are hashing exactly * 3, 2 or 1 word(s). * * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally * done at the end is not done here. */ Inline u_int32_t jhash_3words(u_int32_t a, u_int32_t b, u_int32_t c, u_int32_t initval) { a += JHASH_GOLDEN_RATIO; b += JHASH_GOLDEN_RATIO; c += initval; __jhash_mix(a, b, c); return c; } Inline u_int32_t jhash_2words(u_int32_t a, u_int32_t b, u_int32_t initval) { return jhash_3words(a, b, 0, initval); } Inline u_int32_t jhash_1word(u_int32_t a, u_int32_t initval) { return jhash_3words(a, 0, 0, initval); } #endif /* _LINUX_JHASH_H */ pmacct-0.14.0/src/sql_common.h0000644000175000017500000003606711741022377015200 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* includes */ #include #include "net_aggr.h" #include "ports_aggr.h" /* defines */ #define DEFAULT_DB_REFRESH_TIME 60 #define DEFAULT_SQL_TABLE_VERSION 1 #define DEFAULT_SQL_WRITERS_NO 10 #define CACHE_ENTRIES 32771 #define QUERY_BUFFER_SIZE 32768 #define MAGIC 14021979 #define DEF_HDR_FIELD_LEN 128 #define MAX_LOGFILE_SIZE 2048000000 #define MAX_LOGFILE_ROTATIONS 1000 /* cache elements defines */ #define REASONABLE_NUMBER 100 #define STALE_M 3 #define RETIRE_M STALE_M*STALE_M /* backend types */ #define BE_TYPE_PRIMARY 0 #define BE_TYPE_BACKUP 1 #define BE_TYPE_LOGFILE 2 /* lock types */ #define PM_LOCK_EXCLUSIVE 0 #define PM_LOCK_ROW_EXCLUSIVE 1 /* cache element states */ #define SQL_CACHE_FREE 0 #define SQL_CACHE_COMMITTED 1 #define SQL_CACHE_INUSE 2 #define SQL_CACHE_ERROR 255 #define SQL_TABLE_VERSION_PLAIN 0 #define SQL_TABLE_VERSION_BGP 1000 /* macros */ #define SPACELEFT(x) (sizeof(x)-strlen(x)) #define SPACELEFT_LEN(x,y) (sizeof(x)-y) #define SPACELEFT_PTR(x,y) (y-strlen(x)) struct multi_values { int buffer_offset; /* multi-values buffer offset where to write next query */ int head_buffer_elem; /* first multi-values buffer element */ int buffer_elem_num; /* number of elements in the multi-values buffer */ int last_queue_elem; /* last queue element signallation */ }; /* structures */ struct insert_data { unsigned int hash; unsigned int modulo; time_t now; time_t basetime; time_t triggertime; time_t timeslot; /* counters timeslot */ time_t t_timeslot; /* trigger timeslot */ struct timeval flushtime; /* last time the table has been flushed */ int pending_accumulators; int num_primitives; int dyn_table; int recover; int locks; time_t new_basetime; time_t committed_basetime; int current_queue_elem; struct multi_values mv; /* stats */ time_t elap_time; /* elapsed time */ unsigned int ten; /* total elements number */ unsigned int een; /* effective elements number */ unsigned int qn; /* total query number */ unsigned int iqn; /* INSERTs query number */ unsigned int uqn; /* UPDATEs query number */ }; struct db_cache { struct pkt_primitives primitives; pm_counter_t bytes_counter; pm_counter_t packet_counter; pm_counter_t flows_counter; u_int32_t tcp_flags; u_int8_t tentatives; /* support to classifiers: tentatives remaining */ time_t basetime; time_t endtime; struct cache_bgp_primitives *cbgp; u_int8_t valid; u_int8_t prep_valid; unsigned int signature; u_int8_t chained; struct db_cache *prev; struct db_cache *next; time_t start_tag; /* time: first packet received */ time_t lru_tag; /* time: last packet received */ struct db_cache *lru_prev; struct db_cache *lru_next; }; struct logfile_header { u_int32_t magic; char sql_db[DEF_HDR_FIELD_LEN]; char sql_table[DEF_HDR_FIELD_LEN]; char sql_user[DEF_HDR_FIELD_LEN]; char sql_host[DEF_HDR_FIELD_LEN]; u_int16_t sql_table_version; u_int16_t sql_optimize_clauses; u_int16_t sql_history; u_int64_t what_to_count; u_char pad[8]; }; struct logfile { FILE *file; short int open; short int fail; }; typedef void (*dbop_handler) (const struct db_cache *, const struct insert_data *, int, char **, char **); typedef int (*preprocess_func) (struct db_cache *[], int *, int); struct frags { dbop_handler handler; u_int64_t type; char string[SRVBUFLEN]; }; /* Backend descriptors */ struct DBdesc { void *desc; char *conn_string; /* PostgreSQL */ char *filename; /* SQLite */ char *errmsg; short int type; short int connected; short int fail; }; struct BE_descs { struct DBdesc *p; struct DBdesc *b; struct logfile *lf; }; /* Callbacks for a common SQL layer */ typedef void (*db_connect)(struct DBdesc *, char *); typedef void (*db_close)(struct BE_descs *); typedef void (*db_lock)(struct DBdesc *); typedef void (*db_unlock)(struct BE_descs *); typedef void (*db_create_table)(struct DBdesc *, char *); typedef int (*db_op)(struct DBdesc *, struct db_cache *, struct insert_data *); typedef void (*sqlcache_purge)(struct db_cache *[], int, struct insert_data *); typedef void (*sqlbackend_create)(struct DBdesc *); struct sqlfunc_cb_registry { db_connect connect; db_close close; db_lock lock; db_unlock unlock; db_op op; db_create_table create_table; sqlbackend_create create_backend; sqlcache_purge purge; /* flush and query wrapper are common for all SQL plugins */ }; /* the following include depends on structure definition above */ #include "log_templates.h" #include "preprocess.h" /* functions */ #if (!defined __SQL_HANDLERS_C) #define EXT extern #else #define EXT #endif EXT void count_src_mac_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_dst_mac_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_vlan_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_cos_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_etype_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_src_host_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_src_as_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_dst_host_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_dst_as_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_std_comm_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_ext_comm_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_as_path_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_local_pref_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_med_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_src_std_comm_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_src_ext_comm_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_src_as_path_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_src_local_pref_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_src_med_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_mpls_vpn_rd_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_peer_src_as_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_peer_dst_as_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_peer_src_ip_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_peer_dst_ip_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_src_port_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_dst_port_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_ip_tos_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_in_iface_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_out_iface_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_src_nmask_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_dst_nmask_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void MY_count_ip_proto_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void PG_count_ip_proto_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_timestamp_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_copy_timestamp_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_id_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_id2_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_class_id_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_tcpflags_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void fake_mac_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void fake_host_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void fake_as_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void fake_comms_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void fake_as_path_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void fake_mpls_vpn_rd_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_counters_setclause_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_flows_setclause_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_timestamp_setclause_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_tcpflags_setclause_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); EXT void count_noop_setclause_handler(const struct db_cache *, const struct insert_data *, int, char **, char **); #undef EXT #if (defined __SQL_COMMON_C) #define EXT extern #else #define EXT #endif /* Toward a common SQL layer */ EXT void sql_set_signals(); EXT void sql_set_insert_func(); EXT void sql_init_maps(struct networks_table *, struct networks_cache *, struct ports_table *); EXT void sql_init_global_buffers(); EXT void sql_init_default_values(); EXT void sql_init_historical_acct(time_t, struct insert_data *); EXT void sql_init_triggers(time_t, struct insert_data *); EXT void sql_init_refresh_deadline(time_t *); EXT void sql_calc_refresh_timeout(time_t, time_t, int *); EXT void sql_init_pipe(struct pollfd *, int); EXT struct template_entry *sql_init_logfile_template(struct template_header *); EXT void sql_link_backend_descriptors(struct BE_descs *, struct DBdesc *, struct DBdesc *); EXT void sql_cache_modulo(struct pkt_primitives *, struct pkt_bgp_primitives *, struct insert_data *); EXT int sql_cache_flush(struct db_cache *[], int, struct insert_data *, int); EXT int sql_cache_flush_pending(struct db_cache *[], int, struct insert_data *); EXT void sql_cache_insert(struct pkt_data *, struct pkt_bgp_primitives *, struct insert_data *); EXT struct db_cache *sql_cache_search(struct pkt_primitives *, struct pkt_bgp_primitives *, time_t); EXT int sql_trigger_exec(char *); EXT void sql_db_ok(struct DBdesc *); EXT void sql_db_fail(struct DBdesc *); EXT void sql_db_errmsg(struct DBdesc *); EXT int sql_query(struct BE_descs *, struct db_cache *, struct insert_data *); EXT void sql_exit_gracefully(int); EXT int sql_evaluate_primitives(int); EXT FILE *sql_file_open(const char *, const char *, const struct insert_data *); EXT void sql_create_table(struct DBdesc *, time_t *); EXT void sql_invalidate_shadow_entries(struct db_cache *[], int *); EXT int sql_select_locking_style(char *); EXT int sql_compose_static_set(int); EXT void sql_sum_host_insert(struct pkt_data *, struct pkt_bgp_primitives *, struct insert_data *); EXT void sql_sum_port_insert(struct pkt_data *, struct pkt_bgp_primitives *, struct insert_data *); EXT void sql_sum_as_insert(struct pkt_data *, struct pkt_bgp_primitives *, struct insert_data *); #if defined (HAVE_L2) EXT void sql_sum_mac_insert(struct pkt_data *, struct pkt_bgp_primitives *, struct insert_data *); #endif EXT void sql_sum_std_comm_insert(struct pkt_data *, struct pkt_bgp_primitives *, struct insert_data *); EXT void sql_sum_ext_comm_insert(struct pkt_data *, struct pkt_bgp_primitives *, struct insert_data *); #undef EXT #if (!defined __MYSQL_PLUGIN_C) && (!defined __PMACCT_PLAYER_C) && \ (!defined __PGSQL_PLUGIN_C) && (!defined __SQLITE3_PLUGIN_C) #define EXT extern #else #define EXT #endif /* Global Variables: a simple way of gain precious speed when playing with strings */ EXT char sql_data[LARGEBUFLEN]; EXT char lock_clause[LONGSRVBUFLEN]; EXT char unlock_clause[LONGSRVBUFLEN]; EXT char update_clause[LONGSRVBUFLEN]; EXT char set_clause[LONGSRVBUFLEN]; EXT char copy_clause[LONGSRVBUFLEN]; EXT char insert_clause[LONGSRVBUFLEN]; EXT char values_clause[LONGLONGSRVBUFLEN]; EXT char *multi_values_buffer; EXT char where_clause[LONGLONGSRVBUFLEN]; EXT unsigned char *pipebuf; EXT struct db_cache *cache; EXT struct db_cache **queries_queue, **pending_queries_queue; EXT struct db_cache *collision_queue; EXT int cq_ptr, qq_ptr, qq_size, pp_size, pb_size, dbc_size, cq_size, pqq_ptr; EXT struct db_cache lru_head, *lru_tail; EXT struct frags where[N_PRIMITIVES+2]; EXT struct frags values[N_PRIMITIVES+2]; EXT struct frags copy_values[N_PRIMITIVES+2]; EXT struct frags set[N_PRIMITIVES+2]; EXT int glob_num_primitives; /* last resort for signal handling */ EXT int glob_basetime; /* last resort for signal handling */ EXT time_t glob_new_basetime; /* last resort for signal handling */ EXT time_t glob_committed_basetime; /* last resort for signal handling */ EXT int glob_dyn_table; /* last resort for signal handling */ EXT int glob_nfacctd_sql_log; /* last resort for sql handlers */ EXT int glob_timeslot; /* last resort for sql handlers */ EXT struct sqlfunc_cb_registry sqlfunc_cbr; EXT void (*insert_func)(struct pkt_data *, struct pkt_bgp_primitives *, struct insert_data *); EXT struct DBdesc p; EXT struct DBdesc b; EXT struct BE_descs bed; EXT struct template_header th; EXT struct template_entry *te; EXT struct largebuf logbuf; EXT struct largebuf envbuf; EXT time_t now; /* PostgreSQL */ #undef EXT pmacct-0.14.0/src/server.c0000644000175000017500000003156411712352255014326 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #define __SERVER_C /* includes */ #include "pmacct.h" #include "imt_plugin.h" #include "ip_flow.h" #include "classifier.h" #include "bgp/bgp_packet.h" #include "bgp/bgp.h" /* functions */ int build_query_server(char *path_ptr) { struct sockaddr_un sAddr; int sd, rc; sd=socket(AF_UNIX, SOCK_STREAM, 0); if (sd < 0) { Log(LOG_ERR, "ERROR ( %s/%s ): cannot open socket.\n", config.name, config.type); exit_plugin(1); } sAddr.sun_family = AF_UNIX; strcpy(sAddr.sun_path, path_ptr); unlink(path_ptr); rc = bind(sd, (struct sockaddr *) &sAddr,sizeof(sAddr)); if (rc < 0) { Log(LOG_ERR, "ERROR ( %s/%s ): cannot bind to file %s .\n", config.name, config.type, path_ptr); exit_plugin(1); } chmod(path_ptr, S_IRUSR|S_IWUSR|S_IXUSR| S_IRGRP|S_IWGRP|S_IXGRP| S_IROTH|S_IWOTH|S_IXOTH); setnonblocking(sd); listen(sd, 1); Log(LOG_INFO, "OK ( %s/%s ): waiting for data on: '%s'\n", config.name, config.type, path_ptr); return sd; } void process_query_data(int sd, unsigned char *buf, int len, int forked) { struct acc *acc_elem = 0, tmpbuf; struct bucket_desc bd; struct query_header *q, *uq; struct query_entry request; struct reply_buffer rb; unsigned char *elem, *bufptr; int following_chain=0; unsigned int idx; struct pkt_data dummy; struct pkt_bgp_primitives dummy_pbgp; int reset_counter; memset(&dummy, 0, sizeof(struct pkt_data)); memset(&dummy_pbgp, 0, sizeof(struct pkt_bgp_primitives)); memset(&rb, 0, sizeof(struct reply_buffer)); memcpy(rb.buf, buf, sizeof(struct query_header)); rb.len = LARGEBUFLEN-sizeof(struct query_header); rb.packed = sizeof(struct query_header); /* arranging some pointer */ uq = (struct query_header *) buf; q = (struct query_header *) rb.buf; rb.ptr = rb.buf+sizeof(struct query_header); bufptr = buf+sizeof(struct query_header); q->ip_sz = sizeof(acc_elem->primitives.src_ip); q->cnt_sz = sizeof(acc_elem->bytes_counter); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Processing data received from client ...\n", config.name, config.type); if (config.imt_plugin_passwd) { if (!strncmp(config.imt_plugin_passwd, q->passwd, MIN(strlen(config.imt_plugin_passwd), 8))); else return; } elem = (unsigned char *) a; reset_counter = q->type & WANT_RESET; if (q->type & WANT_STATS) { q->what_to_count = config.what_to_count; for (idx = 0; idx < config.buckets; idx++) { if (!following_chain) acc_elem = (struct acc *) elem; if (acc_elem->bytes_counter && !acc_elem->reset_flag) { enQueue_elem(sd, &rb, acc_elem, PdataSz, PdataSz+PbgpSz); /* XXX: to be optimized ? */ if (PbgpSz) { if (acc_elem->cbgp) { struct pkt_bgp_primitives tmp_pbgp; cache_to_pkt_bgp_primitives(&tmp_pbgp, acc_elem->cbgp); enQueue_elem(sd, &rb, &tmp_pbgp, PbgpSz, PbgpSz); } else enQueue_elem(sd, &rb, &dummy_pbgp, PbgpSz, PbgpSz); } } if (acc_elem->next != NULL) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): Following chain in reply ...\n", config.name, config.type); acc_elem = acc_elem->next; following_chain = TRUE; idx--; } else { elem += sizeof(struct acc); following_chain = FALSE; } } send(sd, rb.buf, rb.packed, 0); /* send remainder data */ } else if (q->type & WANT_STATUS) { for (idx = 0; idx < config.buckets; idx++) { /* Administrativia */ following_chain = FALSE; bd.num = 0; bd.howmany = 0; acc_elem = (struct acc *) elem; do { if (following_chain) acc_elem = acc_elem->next; if (acc_elem->bytes_counter && !acc_elem->reset_flag) bd.howmany++; bd.num = idx; /* we need to avoid this redundancy */ following_chain = TRUE; } while (acc_elem->next != NULL); enQueue_elem(sd, &rb, &bd, sizeof(struct bucket_desc), sizeof(struct bucket_desc)); elem += sizeof(struct acc); } send(sd, rb.buf, rb.packed, 0); } else if (q->type & WANT_MATCH || q->type & WANT_COUNTER) { unsigned int j; q->what_to_count = config.what_to_count; for (j = 0; j < uq->num; j++, bufptr += sizeof(struct query_entry)) { memcpy(&request, bufptr, sizeof(struct query_entry)); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Searching into accounting structure ...\n", config.name, config.type); if (request.what_to_count == config.what_to_count) { acc_elem = search_accounting_structure(&request.data, &request.pbgp); if (acc_elem) { if (acc_elem->bytes_counter && !acc_elem->reset_flag) { enQueue_elem(sd, &rb, acc_elem, PdataSz, PdataSz+PbgpSz); /* XXX: to be optimized ? */ if (PbgpSz) { if (acc_elem->cbgp) { struct pkt_bgp_primitives tmp_pbgp; cache_to_pkt_bgp_primitives(&tmp_pbgp, acc_elem->cbgp); enQueue_elem(sd, &rb, &tmp_pbgp, PbgpSz, PbgpSz); } else enQueue_elem(sd, &rb, &dummy_pbgp, PbgpSz, PbgpSz); } if (reset_counter) { if (forked) set_reset_flag(acc_elem); else reset_counters(acc_elem); } } else { if (q->type & WANT_COUNTER) { enQueue_elem(sd, &rb, &dummy, PdataSz, PdataSz+PbgpSz); if (PbgpSz) enQueue_elem(sd, &rb, &dummy_pbgp, PbgpSz, PbgpSz); } } } else { if (q->type & WANT_COUNTER) { enQueue_elem(sd, &rb, &dummy, PdataSz, PdataSz+PbgpSz); if (PbgpSz) enQueue_elem(sd, &rb, &dummy_pbgp, PbgpSz, PbgpSz); } } } else { struct pkt_primitives tbuf; struct pkt_bgp_primitives bbuf; struct pkt_data abuf; following_chain = FALSE; elem = (unsigned char *) a; memset(&abuf, 0, sizeof(abuf)); for (idx = 0; idx < config.buckets; idx++) { if (!following_chain) acc_elem = (struct acc *) elem; if (acc_elem->bytes_counter && !acc_elem->reset_flag) { mask_elem(&tbuf, &bbuf, acc_elem, request.what_to_count); if (!memcmp(&tbuf, &request.data, sizeof(struct pkt_primitives)) && !memcmp(&bbuf, &request.pbgp, sizeof(struct pkt_bgp_primitives))) { if (q->type & WANT_COUNTER) Accumulate_Counters(&abuf, acc_elem); else { enQueue_elem(sd, &rb, acc_elem, PdataSz, PdataSz+PbgpSz); /* q->type == WANT_MATCH */ if (PbgpSz) { if (acc_elem->cbgp) { struct pkt_bgp_primitives tmp_pbgp; cache_to_pkt_bgp_primitives(&tmp_pbgp, acc_elem->cbgp); enQueue_elem(sd, &rb, &tmp_pbgp, PbgpSz, PbgpSz); } else enQueue_elem(sd, &rb, &dummy_pbgp, PbgpSz, PbgpSz); } } if (reset_counter) set_reset_flag(acc_elem); } } if (acc_elem->next) { acc_elem = acc_elem->next; following_chain = TRUE; idx--; } else { elem += sizeof(struct acc); following_chain = FALSE; } } if (q->type & WANT_COUNTER) enQueue_elem(sd, &rb, &abuf, PdataSz, PdataSz); /* enqueue accumulated data */ } } send(sd, rb.buf, rb.packed, 0); /* send remainder data */ } else if (q->type & WANT_CLASS_TABLE) { struct stripped_class dummy; u_int32_t idx = 0, max = 0; /* XXX: we should try using pmct_get_max_entries() */ max = q->num = config.classifier_table_num; if (!q->num && class) max = q->num = MAX_CLASSIFIERS; while (idx < max) { enQueue_elem(sd, &rb, &class[idx], sizeof(struct stripped_class), sizeof(struct stripped_class)); idx++; } send_ct_dummy: memset(&dummy, 0, sizeof(dummy)); enQueue_elem(sd, &rb, &dummy, sizeof(dummy), sizeof(dummy)); send(sd, rb.buf, rb.packed, 0); /* send remainder data */ } } void mask_elem(struct pkt_primitives *d1, struct pkt_bgp_primitives *d2, struct acc *src, u_int64_t w) { struct pkt_primitives *s1 = &src->primitives; struct pkt_bgp_primitives tmp_pbgp; struct pkt_bgp_primitives *s2 = &tmp_pbgp; cache_to_pkt_bgp_primitives(s2, src->cbgp); memset(d1, 0, sizeof(struct pkt_primitives)); memset(d2, 0, sizeof(struct pkt_bgp_primitives)); #if defined (HAVE_L2) if (w & COUNT_SRC_MAC) memcpy(d1->eth_shost, s1->eth_shost, ETH_ADDR_LEN); if (w & COUNT_DST_MAC) memcpy(d1->eth_dhost, s1->eth_dhost, ETH_ADDR_LEN); if (w & COUNT_VLAN) d1->vlan_id = s1->vlan_id; if (w & COUNT_COS) d1->cos = s1->cos; if (w & COUNT_ETHERTYPE) d1->etype = s1->etype; #endif if (w & (COUNT_SRC_HOST|COUNT_SRC_NET)) { if (s1->src_ip.family == AF_INET) d1->src_ip.address.ipv4.s_addr = s1->src_ip.address.ipv4.s_addr; #if defined ENABLE_IPV6 else if (s1->src_ip.family == AF_INET6) memcpy(&d1->src_ip.address.ipv6, &s1->src_ip.address.ipv6, sizeof(struct in6_addr)); #endif d1->src_ip.family = s1->src_ip.family; } if (w & (COUNT_DST_HOST|COUNT_DST_NET)) { if (s1->dst_ip.family == AF_INET) d1->dst_ip.address.ipv4.s_addr = s1->dst_ip.address.ipv4.s_addr; #if defined ENABLE_IPV6 else if (s1->dst_ip.family == AF_INET6) memcpy(&d1->dst_ip.address.ipv6, &s1->dst_ip.address.ipv6, sizeof(struct in6_addr)); #endif d1->dst_ip.family = s1->dst_ip.family; } if (w & COUNT_SRC_NMASK) d1->src_nmask = s1->src_nmask; if (w & COUNT_DST_NMASK) d1->dst_nmask = s1->dst_nmask; if (w & COUNT_SRC_AS) d1->src_as = s1->src_as; if (w & COUNT_DST_AS) d1->dst_as = s1->dst_as; if (w & COUNT_SRC_PORT) d1->src_port = s1->src_port; if (w & COUNT_DST_PORT) d1->dst_port = s1->dst_port; if (w & COUNT_IP_TOS) d1->tos = s1->tos; if (w & COUNT_IP_PROTO) d1->proto = s1->proto; if (w & COUNT_IN_IFACE) d1->ifindex_in = s1->ifindex_in; if (w & COUNT_OUT_IFACE) d1->ifindex_out = s1->ifindex_out; if (w & COUNT_ID) d1->id = s1->id; if (w & COUNT_ID2) d1->id2 = s1->id2; if (w & COUNT_CLASS) d1->class = s1->class; if (PbgpSz && s2) { if (w & COUNT_STD_COMM) strlcpy(d2->std_comms, s2->std_comms, MAX_BGP_STD_COMMS); if (w & COUNT_SRC_STD_COMM) strlcpy(d2->src_std_comms, s2->src_std_comms, MAX_BGP_STD_COMMS); if (w & COUNT_EXT_COMM) strlcpy(d2->ext_comms, s2->ext_comms, MAX_BGP_EXT_COMMS); if (w & COUNT_SRC_EXT_COMM) strlcpy(d2->src_ext_comms, s2->src_ext_comms, MAX_BGP_EXT_COMMS); if (w & COUNT_AS_PATH) strlcpy(d2->as_path, s2->as_path, MAX_BGP_ASPATH); if (w & COUNT_SRC_AS_PATH) strlcpy(d2->src_as_path, s2->src_as_path, MAX_BGP_ASPATH); if (w & COUNT_LOCAL_PREF) d2->local_pref = s2->local_pref; if (w & COUNT_SRC_LOCAL_PREF) d2->src_local_pref = s2->src_local_pref; if (w & COUNT_MED) d2->med = s2->med; if (w & COUNT_SRC_MED) d2->src_med = s2->src_med; if (w & COUNT_PEER_SRC_AS) d2->peer_src_as = s2->peer_src_as; if (w & COUNT_PEER_DST_AS) d2->peer_dst_as = s2->peer_dst_as; if (w & COUNT_PEER_SRC_IP) { if (s2->peer_src_ip.family == AF_INET) d2->peer_src_ip.address.ipv4.s_addr = s2->peer_src_ip.address.ipv4.s_addr; #if defined ENABLE_IPV6 else if (s2->peer_src_ip.family == AF_INET6) memcpy(&d2->peer_src_ip.address.ipv6, &s2->peer_src_ip.address.ipv6, sizeof(struct in6_addr)); #endif d2->peer_src_ip.family = s2->peer_src_ip.family; } if (w & COUNT_PEER_DST_IP) { if (s2->peer_dst_ip.family == AF_INET) d2->peer_dst_ip.address.ipv4.s_addr = s2->peer_dst_ip.address.ipv4.s_addr; #if defined ENABLE_IPV6 else if (s2->peer_dst_ip.family == AF_INET6) memcpy(&d2->peer_dst_ip.address.ipv6, &s2->peer_dst_ip.address.ipv6, sizeof(struct in6_addr)); #endif d2->peer_dst_ip.family = s2->peer_dst_ip.family; } if (w & COUNT_MPLS_VPN_RD) memcpy(&d2->mpls_vpn_rd, &s2->mpls_vpn_rd, sizeof(rd_t)); } } void enQueue_elem(int sd, struct reply_buffer *rb, void *elem, int size, int tot_size) { if ((rb->packed + tot_size) < rb->len) { memcpy(rb->ptr, elem, size); rb->ptr += size; rb->packed += size; } else { send(sd, rb->buf, rb->packed, 0); rb->len = LARGEBUFLEN; memset(rb->buf, 0, sizeof(rb->buf)); rb->packed = 0; rb->ptr = rb->buf; memcpy(rb->ptr, elem, size); rb->ptr += size; rb->packed += size; } } void Accumulate_Counters(struct pkt_data *abuf, struct acc *elem) { abuf->pkt_len += elem->bytes_counter; abuf->pkt_num += elem->packet_counter; abuf->flo_num += elem->flow_counter; abuf->time_start.tv_sec++; /* XXX: this unused field works as counter of how much entries we are accumulating */ } pmacct-0.14.0/src/strlcpy.c0000644000175000017500000000507110530072467014513 0ustar paolopaolo/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(HAVE_STRLCPY) #include #include /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(dst, src, siz) char *dst; const char *src; size_t siz; { register char *d = dst; register const char *s = src; register size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } #endif /* ! __*BSD__ */ pmacct-0.14.0/src/pkt_handlers.c0000644000175000017500000041560011741100774015472 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define __PKT_HANDLERS_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "nfacctd.h" #include "sflow.h" #include "sfacctd.h" #include "plugin_hooks.h" #include "pkt_handlers.h" #include "addr.h" #include "bgp/bgp.h" #include "isis/prefix.h" #include "isis/table.h" /* functions */ void evaluate_packet_handlers() { int primitives, index = 0; while (channels_list[index].aggregation) { primitives = 0; memset(&channels_list[index].phandler, 0, N_PRIMITIVES); #if defined (HAVE_L2) if (channels_list[index].aggregation & (COUNT_SRC_MAC|COUNT_SUM_MAC)) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = src_mac_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_src_mac_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_src_mac_handler; primitives++; } if (channels_list[index].aggregation & (COUNT_DST_MAC|COUNT_SUM_MAC)) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = dst_mac_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_dst_mac_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_dst_mac_handler; primitives++; } if (channels_list[index].aggregation & COUNT_VLAN) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = vlan_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_vlan_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_vlan_handler; primitives++; } if (channels_list[index].aggregation & COUNT_COS) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = cos_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_cos_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_cos_handler; primitives++; } if (channels_list[index].aggregation & COUNT_ETHERTYPE) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = etype_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_etype_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_etype_handler; primitives++; } #endif if (channels_list[index].aggregation & (COUNT_SRC_HOST|COUNT_SRC_NET|COUNT_SUM_HOST|COUNT_SUM_NET)) { if (channels_list[index].aggregation & (COUNT_SRC_NET|COUNT_SUM_NET)) { if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_BGP) { channels_list[index].phandler[primitives] = bgp_src_net_handler; primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_IGP) { channels_list[index].phandler[primitives] = igp_src_net_handler; primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_KEEP) { if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_src_host_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_src_host_handler; else primitives--; /* Just in case */ primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & (NF_NET_COMPAT|NF_NET_NEW|NF_NET_STATIC)) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = src_host_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_src_host_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_src_host_handler; primitives++; } } else { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = src_host_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_src_host_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_src_host_handler; primitives++; } } if (channels_list[index].aggregation & (COUNT_DST_HOST|COUNT_DST_NET|COUNT_SUM_HOST|COUNT_SUM_NET)) { if (channels_list[index].aggregation & (COUNT_DST_NET|COUNT_SUM_NET)) { if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_BGP) { channels_list[index].phandler[primitives] = bgp_dst_net_handler; primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_IGP) { channels_list[index].phandler[primitives] = igp_dst_net_handler; primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_KEEP) { if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_dst_host_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_dst_host_handler; else primitives--; /* Just in case */ primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & (NF_NET_COMPAT|NF_NET_NEW|NF_NET_STATIC)) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = dst_host_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_dst_host_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_dst_host_handler; primitives++; } } else { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = dst_host_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_dst_host_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_dst_host_handler; primitives++; } } if (channels_list[index].aggregation & COUNT_SRC_NMASK) { if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_BGP) { channels_list[index].phandler[primitives] = bgp_src_nmask_handler; primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_IGP) { channels_list[index].phandler[primitives] = igp_src_nmask_handler; primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_KEEP) { if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_src_nmask_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_src_nmask_handler; else primitives--; /* Just in case */ primitives++; } } if (channels_list[index].aggregation & COUNT_DST_NMASK) { if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_BGP) { channels_list[index].phandler[primitives] = bgp_dst_nmask_handler; primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_IGP) { channels_list[index].phandler[primitives] = igp_dst_nmask_handler; primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_KEEP) { if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_dst_nmask_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_dst_nmask_handler; else primitives--; /* Just in case */ primitives++; } } if (channels_list[index].aggregation & (COUNT_SRC_AS|COUNT_SUM_AS)) { if (config.acct_type == ACCT_PM) { if (config.nfacctd_as & NF_AS_KEEP) channels_list[index].phandler[primitives] = src_host_handler; else if (config.nfacctd_as & NF_AS_NEW) channels_list[index].phandler[primitives] = src_host_handler; else if (config.nfacctd_as & NF_AS_BGP) primitives--; /* This is handled elsewhere */ } else if (config.acct_type == ACCT_NF) { if (config.nfacctd_as & NF_AS_KEEP) channels_list[index].phandler[primitives] = NF_src_as_handler; else if (config.nfacctd_as & NF_AS_NEW) channels_list[index].phandler[primitives] = NF_src_host_handler; else if (config.nfacctd_as & NF_AS_BGP) primitives--; /* This is handled elsewhere */ } else if (config.acct_type == ACCT_SF) { if (config.nfacctd_as & NF_AS_KEEP) channels_list[index].phandler[primitives] = SF_src_as_handler; else if (config.nfacctd_as & NF_AS_NEW) channels_list[index].phandler[primitives] = SF_src_host_handler; else if (config.nfacctd_as & NF_AS_BGP) primitives--; /* This is handled elsewhere */ } primitives++; } if (channels_list[index].aggregation & (COUNT_DST_AS|COUNT_SUM_AS)) { if (config.acct_type == ACCT_PM) { if (config.nfacctd_as & NF_AS_KEEP) channels_list[index].phandler[primitives] = dst_host_handler; else if (config.nfacctd_as & NF_AS_NEW) channels_list[index].phandler[primitives] = dst_host_handler; else if (config.nfacctd_as & NF_AS_BGP) primitives--; /* This is handled elsewhere */ } else if (config.acct_type == ACCT_NF) { if (config.nfacctd_as & NF_AS_KEEP) channels_list[index].phandler[primitives] = NF_dst_as_handler; else if (config.nfacctd_as & NF_AS_NEW) channels_list[index].phandler[primitives] = NF_dst_host_handler; else if (config.nfacctd_as & NF_AS_BGP) primitives--; /* This is handled elsewhere */ } else if (config.acct_type == ACCT_SF) { if (config.nfacctd_as & NF_AS_KEEP) channels_list[index].phandler[primitives] = SF_dst_as_handler; else if (config.nfacctd_as & NF_AS_NEW) channels_list[index].phandler[primitives] = SF_dst_host_handler; else if (config.nfacctd_as & NF_AS_BGP) primitives--; /* This is handled elsewhere */ } primitives++; } if (channels_list[index].aggregation & COUNT_PEER_SRC_IP) { if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_peer_src_ip_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_peer_src_ip_handler; else primitives--; /* Just in case */ primitives++; } if (channels_list[index].aggregation & COUNT_PEER_DST_IP) { if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_BGP) { channels_list[index].phandler[primitives] = bgp_peer_dst_ip_handler; primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_IGP) { channels_list[index].phandler[primitives] = igp_peer_dst_ip_handler; primitives++; } if (channels_list[index].plugin->cfg.nfacctd_net & NF_NET_KEEP) { if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_peer_dst_ip_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_peer_dst_ip_handler; else primitives--; /* Just in case */ primitives++; } } if (channels_list[index].aggregation & COUNT_AS_PATH) { if (config.acct_type == ACCT_SF) { if (config.nfacctd_as & NF_AS_KEEP) { channels_list[index].phandler[primitives] = SF_as_path_handler; primitives++; } } } if (channels_list[index].aggregation & COUNT_PEER_SRC_AS) { if (config.acct_type == ACCT_NF) { if (config.nfacctd_as & NF_AS_KEEP && config.nfacctd_bgp_peer_as_src_type & BGP_SRC_PRIMITIVES_KEEP) { channels_list[index].phandler[primitives] = NF_peer_src_as_handler; primitives++; } } else if (config.acct_type == ACCT_SF) { if (config.nfacctd_as & NF_AS_KEEP && config.nfacctd_bgp_peer_as_src_type & BGP_SRC_PRIMITIVES_KEEP) { channels_list[index].phandler[primitives] = SF_peer_src_as_handler; primitives++; } } } if (channels_list[index].aggregation & COUNT_PEER_DST_AS) { if (config.acct_type == ACCT_NF) { if (config.nfacctd_as & NF_AS_KEEP) { channels_list[index].phandler[primitives] = NF_peer_dst_as_handler; primitives++; } } else if (config.acct_type == ACCT_SF) { if (config.nfacctd_as & NF_AS_KEEP) { channels_list[index].phandler[primitives] = SF_peer_dst_as_handler; primitives++; } } } if (channels_list[index].aggregation & COUNT_LOCAL_PREF) { if (config.acct_type == ACCT_SF) { if (config.nfacctd_as & NF_AS_KEEP) { channels_list[index].phandler[primitives] = SF_local_pref_handler; primitives++; } } } if (channels_list[index].aggregation & COUNT_STD_COMM) { if (config.acct_type == ACCT_SF) { if (config.nfacctd_as & NF_AS_KEEP) { channels_list[index].phandler[primitives] = SF_std_comms_handler; primitives++; } } } if (channels_list[index].aggregation & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED| COUNT_AS_PATH|COUNT_PEER_DST_AS|COUNT_SRC_AS_PATH|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF|COUNT_SRC_AS| COUNT_DST_AS|COUNT_PEER_SRC_AS|COUNT_MPLS_VPN_RD) && config.nfacctd_as & NF_AS_BGP) { if (config.acct_type == ACCT_PM && config.nfacctd_bgp) { if (channels_list[index].plugin->type.id == PLUGIN_ID_SFPROBE) { channels_list[index].phandler[primitives] = sfprobe_bgp_ext_handler; } else if (channels_list[index].plugin->type.id == PLUGIN_ID_NFPROBE) { channels_list[index].phandler[primitives] = nfprobe_bgp_ext_handler; } else { channels_list[index].phandler[primitives] = bgp_ext_handler; } primitives++; } else if (config.acct_type == ACCT_NF && config.nfacctd_bgp) { channels_list[index].phandler[primitives] = bgp_ext_handler; primitives++; } else if (config.acct_type == ACCT_SF && config.nfacctd_bgp) { channels_list[index].phandler[primitives] = bgp_ext_handler; primitives++; } } if (channels_list[index].aggregation & COUNT_PEER_SRC_AS) { if (config.acct_type == ACCT_PM && config.nfacctd_bgp) { if (config.nfacctd_bgp_peer_as_src_type & BGP_SRC_PRIMITIVES_MAP) { channels_list[index].phandler[primitives] = bgp_peer_src_as_frommap_handler; primitives++; } } else if (config.acct_type == ACCT_NF) { if (config.nfacctd_bgp && config.nfacctd_bgp_peer_as_src_type & BGP_SRC_PRIMITIVES_MAP) { channels_list[index].phandler[primitives] = bgp_peer_src_as_frommap_handler; primitives++; } } else if (config.acct_type == ACCT_SF) { if (config.nfacctd_bgp && config.nfacctd_bgp_peer_as_src_type & BGP_SRC_PRIMITIVES_MAP) { channels_list[index].phandler[primitives] = bgp_peer_src_as_frommap_handler; primitives++; } } } if (channels_list[index].aggregation & COUNT_SRC_LOCAL_PREF) { if (config.acct_type == ACCT_PM && config.nfacctd_bgp) { if (config.nfacctd_bgp_src_local_pref_type & BGP_SRC_PRIMITIVES_MAP) { channels_list[index].phandler[primitives] = bgp_src_local_pref_frommap_handler; primitives++; } } else if (config.acct_type == ACCT_NF && config.nfacctd_bgp) { if (config.nfacctd_bgp_src_local_pref_type & BGP_SRC_PRIMITIVES_MAP) { channels_list[index].phandler[primitives] = bgp_src_local_pref_frommap_handler; primitives++; } } else if (config.acct_type == ACCT_SF && config.nfacctd_bgp) { if (config.nfacctd_bgp_src_local_pref_type & BGP_SRC_PRIMITIVES_MAP) { channels_list[index].phandler[primitives] = bgp_src_local_pref_frommap_handler; primitives++; } } } if (channels_list[index].aggregation & COUNT_SRC_MED) { if (config.acct_type == ACCT_PM && config.nfacctd_bgp) { if (config.nfacctd_bgp_src_med_type & BGP_SRC_PRIMITIVES_MAP) { channels_list[index].phandler[primitives] = bgp_src_med_frommap_handler; primitives++; } } else if (config.acct_type == ACCT_NF && config.nfacctd_bgp) { if (config.nfacctd_bgp_src_med_type & BGP_SRC_PRIMITIVES_MAP) { channels_list[index].phandler[primitives] = bgp_src_med_frommap_handler; primitives++; } } else if (config.acct_type == ACCT_SF && config.nfacctd_bgp) { if (config.nfacctd_bgp_src_med_type & BGP_SRC_PRIMITIVES_MAP) { channels_list[index].phandler[primitives] = bgp_src_med_frommap_handler; primitives++; } } } if (channels_list[index].aggregation & (COUNT_SRC_PORT|COUNT_SUM_PORT)) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = src_port_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_src_port_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_src_port_handler; primitives++; } if (channels_list[index].aggregation & (COUNT_DST_PORT|COUNT_SUM_PORT)) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = dst_port_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_dst_port_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_dst_port_handler; primitives++; } if (channels_list[index].aggregation & COUNT_IP_TOS) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = ip_tos_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_ip_tos_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_ip_tos_handler; primitives++; } if (channels_list[index].aggregation & COUNT_IP_PROTO) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = ip_proto_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_ip_proto_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_ip_proto_handler; primitives++; } if (channels_list[index].aggregation & COUNT_TCPFLAGS) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = tcp_flags_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_tcp_flags_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_tcp_flags_handler; primitives++; } if (channels_list[index].aggregation & COUNT_FLOWS) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = flows_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_flows_handler; else if (config.acct_type == ACCT_SF) primitives--; /* NO flows handling for sFlow */ primitives++; } if (channels_list[index].aggregation & COUNT_CLASS) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = class_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_class_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_class_handler; primitives++; } if (channels_list[index].aggregation & COUNT_IN_IFACE) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = in_iface_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_in_iface_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_in_iface_handler; primitives++; } if (channels_list[index].aggregation & COUNT_OUT_IFACE) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = out_iface_handler; else if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = NF_out_iface_handler; else if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = SF_out_iface_handler; primitives++; } if (channels_list[index].aggregation & COUNT_COUNTERS) { if (config.acct_type == ACCT_PM) { channels_list[index].phandler[primitives] = counters_handler; if (config.sfacctd_renormalize && config.ext_sampling_rate) { primitives++; channels_list[index].phandler[primitives] = counters_renormalize_handler; } } else if (config.acct_type == ACCT_NF) { if (config.nfacctd_time == NF_TIME_SECS) channels_list[index].phandler[primitives] = NF_counters_secs_handler; else if (config.nfacctd_time == NF_TIME_NEW) channels_list[index].phandler[primitives] = NF_counters_new_handler; else channels_list[index].phandler[primitives] = NF_counters_msecs_handler; /* default */ if (config.sfacctd_renormalize) { primitives++; if (config.ext_sampling_rate) channels_list[index].phandler[primitives] = counters_renormalize_handler; else if (config.sampling_map) { channels_list[index].phandler[primitives] = NF_counters_map_renormalize_handler; /* Fallback to advertised sampling rate if needed */ primitives++; channels_list[index].phandler[primitives] = NF_counters_renormalize_handler; } else channels_list[index].phandler[primitives] = NF_counters_renormalize_handler; } } else if (config.acct_type == ACCT_SF) { channels_list[index].phandler[primitives] = SF_counters_new_handler; if (config.sfacctd_renormalize) { primitives++; if (config.ext_sampling_rate) channels_list[index].phandler[primitives] = counters_renormalize_handler; else if (config.sampling_map) { channels_list[index].phandler[primitives] = SF_counters_map_renormalize_handler; /* Fallback to advertised sampling rate if needed */ primitives++; channels_list[index].phandler[primitives] = SF_counters_renormalize_handler; } else channels_list[index].phandler[primitives] = SF_counters_renormalize_handler; } } primitives++; } if (channels_list[index].plugin->type.id == PLUGIN_ID_NFPROBE) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = nfprobe_extras_handler; else primitives--; /* This case is filtered out at startup: getting out silently */ primitives++; } if (config.acct_type == ACCT_PM || config.acct_type == ACCT_NF || config.acct_type == ACCT_SF) { if (channels_list[index].aggregation & COUNT_ID) { /* we infer 'pre_tag_map' from configuration because it's global */ if (config.pre_tag_map) { channels_list[index].phandler[primitives] = ptag_id_handler; primitives++; } else { if (config.acct_type == ACCT_NF) { channels_list[index].phandler[primitives] = NF_id_handler; primitives++; } else if (config.acct_type == ACCT_SF) { channels_list[index].phandler[primitives] = SF_id_handler; primitives++; } } if (channels_list[index].id) { channels_list[index].phandler[primitives] = id_handler; primitives++; } } } if (config.acct_type == ACCT_PM || config.acct_type == ACCT_NF || config.acct_type == ACCT_SF) { if (channels_list[index].aggregation & COUNT_ID2) { if (config.pre_tag_map) { channels_list[index].phandler[primitives] = ptag_id2_handler; primitives++; } else { if (config.acct_type == ACCT_NF) { channels_list[index].phandler[primitives] = NF_id2_handler; primitives++; } else if (config.acct_type == ACCT_SF) { channels_list[index].phandler[primitives] = SF_id2_handler; primitives++; } } } } /* Better these two sfprobe-related functions to stay last due to different structure put on the pipe; ie. id/id2 handlers were writing in the middle of the payload */ if (channels_list[index].aggregation & COUNT_PAYLOAD) { if (channels_list[index].plugin->type.id == PLUGIN_ID_SFPROBE) { if (config.acct_type == ACCT_PM) channels_list[index].phandler[primitives] = sfprobe_payload_handler; else primitives--; /* This case is filtered out at startup: getting out silently */ } primitives++; } if (channels_list[index].s.rate) { if (channels_list[index].plugin->type.id == PLUGIN_ID_SFPROBE) channels_list[index].phandler[primitives] = sfprobe_sampling_handler; else channels_list[index].phandler[primitives] = sampling_handler; primitives++; } if (channels_list[index].aggregation & COUNT_NONE) { if (channels_list[index].plugin->type.id == PLUGIN_ID_TEE) { if (config.acct_type == ACCT_SF) channels_list[index].phandler[primitives] = tee_payload_handler; if (config.acct_type == ACCT_NF) channels_list[index].phandler[primitives] = tee_payload_handler; else primitives--; /* This case is filtered out at startup: getting out silently */ } primitives++; } index++; } assert(primitives < N_PRIMITIVES); } #if defined (HAVE_L2) void src_mac_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->mac_ptr) memcpy(pdata->primitives.eth_shost, (pptrs->mac_ptr+ETH_ADDR_LEN), ETH_ADDR_LEN); } void dst_mac_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->mac_ptr) memcpy(pdata->primitives.eth_dhost, pptrs->mac_ptr, ETH_ADDR_LEN); } void vlan_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; u_int16_t vlan_id = 0; if (pptrs->vlan_ptr) { memcpy(&vlan_id, pptrs->vlan_ptr, 2); pdata->primitives.vlan_id = ntohs(vlan_id); pdata->primitives.vlan_id = pdata->primitives.vlan_id & 0x0FFF; } } void cos_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; u_int16_t cos = 0; if (pptrs->vlan_ptr) { memcpy(&cos, pptrs->vlan_ptr, 2); cos = ntohs(cos); pdata->primitives.cos = cos >> 13; } } void etype_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; pdata->primitives.etype = pptrs->l3_proto; } #endif void bgp_src_net_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct bgp_node *ret = (struct bgp_node *) pptrs->bgp_src; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_net, NF_NET_BGP)) return; if (pptrs->l3_proto == ETHERTYPE_IP) { if (ret) { memcpy(&pdata->primitives.src_ip.address.ipv4, &ret->p.u.prefix4, 4); pdata->primitives.src_ip.family = AF_INET; } } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { if (ret) { memcpy(&pdata->primitives.src_ip.address.ipv6, &ret->p.u.prefix6, 16); pdata->primitives.src_ip.family = AF_INET6; } } #endif } void bgp_dst_net_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct bgp_node *ret = (struct bgp_node *) pptrs->bgp_dst; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_BGP)) return; if (pptrs->l3_proto == ETHERTYPE_IP) { if (ret) { memcpy(&pdata->primitives.dst_ip.address.ipv4, &ret->p.u.prefix4, 4); pdata->primitives.dst_ip.family = AF_INET; } } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { if (ret) { memcpy(&pdata->primitives.dst_ip.address.ipv6, &ret->p.u.prefix6, 16); pdata->primitives.dst_ip.family = AF_INET6; } } #endif } void bgp_src_nmask_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct bgp_node *ret = (struct bgp_node *) pptrs->bgp_src; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_net, NF_NET_BGP)) return; if (ret) pdata->primitives.src_nmask = ret->p.prefixlen; } void bgp_dst_nmask_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct bgp_node *ret = (struct bgp_node *) pptrs->bgp_dst; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_BGP)) return; if (ret) pdata->primitives.dst_nmask = ret->p.prefixlen; } void bgp_peer_dst_ip_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct bgp_node *ret = (struct bgp_node *) pptrs->bgp_dst; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; struct bgp_info *nh_info; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_BGP)) return; if (pptrs->bgp_nexthop_info) nh_info = (struct bgp_info *) pptrs->bgp_nexthop_info; else if (pptrs->bgp_dst_info) nh_info = (struct bgp_info *) pptrs->bgp_dst_info; if (nh_info && nh_info->attr) { if (nh_info->attr->mp_nexthop.family == AF_INET) { pbgp->peer_dst_ip.family = AF_INET; memcpy(&pbgp->peer_dst_ip.address.ipv4, &nh_info->attr->mp_nexthop.address.ipv4, 4); } #if defined ENABLE_IPV6 else if (nh_info->attr->mp_nexthop.family == AF_INET6) { pbgp->peer_dst_ip.family = AF_INET6; memcpy(&pbgp->peer_dst_ip.address.ipv6, &nh_info->attr->mp_nexthop.address.ipv6, 16); } #endif else { pbgp->peer_dst_ip.family = AF_INET; pbgp->peer_dst_ip.address.ipv4.s_addr = nh_info->attr->nexthop.s_addr; } } } void igp_src_net_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct route_node *ret = (struct route_node *) pptrs->igp_src; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_net, NF_NET_IGP)) return; if (pptrs->l3_proto == ETHERTYPE_IP) { if (ret) { memcpy(&pdata->primitives.src_ip.address.ipv4, &ret->p.u.prefix4, 4); pdata->primitives.src_ip.family = AF_INET; } } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { if (ret) { memcpy(&pdata->primitives.src_ip.address.ipv6, &ret->p.u.prefix6, 16); pdata->primitives.src_ip.family = AF_INET6; } } #endif } void igp_dst_net_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct route_node *ret = (struct route_node *) pptrs->igp_dst; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_IGP)) return; if (pptrs->l3_proto == ETHERTYPE_IP) { if (ret) { memcpy(&pdata->primitives.dst_ip.address.ipv4, &ret->p.u.prefix4, 4); pdata->primitives.dst_ip.family = AF_INET; } } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { if (ret) { memcpy(&pdata->primitives.dst_ip.address.ipv6, &ret->p.u.prefix6, 16); pdata->primitives.dst_ip.family = AF_INET6; } } #endif } void igp_src_nmask_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct route_node *ret = (struct route_node *) pptrs->igp_src; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_net, NF_NET_IGP)) return; if (ret) pdata->primitives.src_nmask = ret->p.prefixlen; } void igp_dst_nmask_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct route_node *ret = (struct route_node *) pptrs->igp_dst; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_IGP)) return; if (ret) pdata->primitives.dst_nmask = ret->p.prefixlen; } void igp_peer_dst_ip_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct route_node *ret = (struct route_node *) pptrs->igp_dst; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_IGP)) return; if (ret) { pbgp->peer_dst_ip.family = AF_INET; memcpy(&pbgp->peer_dst_ip.address.ipv4, &ret->p.adv_router, 4); } } void src_host_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->l3_proto == ETHERTYPE_IP) { pdata->primitives.src_ip.address.ipv4.s_addr = ((struct my_iphdr *) pptrs->iph_ptr)->ip_src.s_addr; pdata->primitives.src_ip.family = AF_INET; } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { memcpy(&pdata->primitives.src_ip.address.ipv6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_src, IP6AddrSz); pdata->primitives.src_ip.family = AF_INET6; } #endif } void dst_host_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->l3_proto == ETHERTYPE_IP) { pdata->primitives.dst_ip.address.ipv4.s_addr = ((struct my_iphdr *) pptrs->iph_ptr)->ip_dst.s_addr; pdata->primitives.dst_ip.family = AF_INET; } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { memcpy(&pdata->primitives.dst_ip.address.ipv6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_dst, IP6AddrSz); pdata->primitives.dst_ip.family = AF_INET6; } #endif } void src_port_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->l4_proto == IPPROTO_UDP || pptrs->l4_proto == IPPROTO_TCP) pdata->primitives.src_port = ntohs(((struct my_tlhdr *) pptrs->tlh_ptr)->src_port); else pdata->primitives.src_port = 0; } void dst_port_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->l4_proto == IPPROTO_UDP || pptrs->l4_proto == IPPROTO_TCP) pdata->primitives.dst_port = ntohs(((struct my_tlhdr *) pptrs->tlh_ptr)->dst_port); else pdata->primitives.dst_port = 0; } void ip_tos_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; u_int32_t tos = 0; if (pptrs->l3_proto == ETHERTYPE_IP) { pdata->primitives.tos = ((struct my_iphdr *) pptrs->iph_ptr)->ip_tos; } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { tos = ntohl(((struct ip6_hdr *) pptrs->iph_ptr)->ip6_flow); tos = ((tos & 0x0ff00000) >> 20); pdata->primitives.tos = tos; } #endif } void ip_proto_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; pdata->primitives.proto = pptrs->l4_proto; } void tcp_flags_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->l4_proto == IPPROTO_TCP) pdata->tcp_flags = pptrs->tcp_flags; } void counters_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->l3_proto == ETHERTYPE_IP) pdata->pkt_len = ntohs(((struct my_iphdr *) pptrs->iph_ptr)->ip_len); #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) pdata->pkt_len = ntohs(((struct ip6_hdr *) pptrs->iph_ptr)->ip6_plen)+IP6HdrSz; #endif if (pptrs->pf) { pdata->pkt_num = pptrs->pf+1; pptrs->pf = 0; } else pdata->pkt_num = 1; pdata->time_start.tv_sec = ((struct pcap_pkthdr *)pptrs->pkthdr)->ts.tv_sec; pdata->time_start.tv_usec = ((struct pcap_pkthdr *)pptrs->pkthdr)->ts.tv_usec; pdata->time_end.tv_sec = 0; pdata->time_end.tv_usec = 0; } void counters_renormalize_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->renormalized) return; pdata->pkt_len = pdata->pkt_len*config.ext_sampling_rate; pdata->pkt_num = pdata->pkt_num*config.ext_sampling_rate; pptrs->renormalized = TRUE; } void id_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; pdata->primitives.id = chptr->id; } void flows_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->new_flow) pdata->flo_num = 1; } void class_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; pdata->primitives.class = pptrs->class; pdata->cst.ba = pptrs->cst.ba; pdata->cst.pa = pptrs->cst.pa; if (chptr->aggregation & COUNT_FLOWS) pdata->cst.fa = pptrs->cst.fa; pdata->cst.stamp.tv_sec = pptrs->cst.stamp.tv_sec; pdata->cst.stamp.tv_usec = pptrs->cst.stamp.tv_usec; pdata->cst.tentatives = pptrs->cst.tentatives; } void sfprobe_payload_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_payload *payload = (struct pkt_payload *) *data; struct pkt_data tmp; struct eth_header eh; char *buf = (char *) *data, *tmpp = (char *) &tmp; int space = (chptr->bufend - chptr->bufptr) - PpayloadSz; int ethHdrLen = 0; memset(&tmp, 0, sizeof(tmp)); if (chptr->plugin->cfg.nfacctd_as & NF_AS_NEW || chptr->plugin->cfg.nfacctd_net == NF_NET_NEW) { src_host_handler(chptr, pptrs, &tmpp); dst_host_handler(chptr, pptrs, &tmpp); memcpy(&payload->src_ip, &tmp.primitives.src_ip, HostAddrSz); memcpy(&payload->dst_ip, &tmp.primitives.dst_ip, HostAddrSz); } if (chptr->plugin->cfg.nfacctd_net == NF_NET_BGP) { bgp_src_nmask_handler(chptr, pptrs, &tmpp); bgp_dst_nmask_handler(chptr, pptrs, &tmpp); payload->src_nmask = tmp.primitives.src_nmask; payload->dst_nmask = tmp.primitives.dst_nmask; } payload->cap_len = ((struct pcap_pkthdr *)pptrs->pkthdr)->caplen; payload->pkt_len = ((struct pcap_pkthdr *)pptrs->pkthdr)->len; payload->pkt_num = 1; payload->time_start = ((struct pcap_pkthdr *)pptrs->pkthdr)->ts.tv_sec; payload->class = pptrs->class; payload->tag = pptrs->tag; payload->tag2 = pptrs->tag2; if (pptrs->ifindex_in > 0) payload->ifindex_in = pptrs->ifindex_in; if (pptrs->ifindex_out > 0) payload->ifindex_out = pptrs->ifindex_out; if (pptrs->vlan_ptr) { u_int16_t vlan_id = 0; memcpy(&vlan_id, pptrs->vlan_ptr, 2); vlan_id = ntohs(vlan_id); payload->vlan = vlan_id & 0x0FFF; payload->priority = vlan_id >> 13; } /* Typically don't have L2 info under ULOG */ if (!pptrs->mac_ptr) { ethHdrLen = sizeof(struct eth_header); memset(&eh, 0, ethHdrLen); eh.ether_type = htons(pptrs->l3_proto); payload->cap_len += ethHdrLen; payload->pkt_len += ethHdrLen; } /* We could be capturing the entire packet; DEFAULT_PLOAD_SIZE is our cut-off point */ if (payload->cap_len > DEFAULT_PLOAD_SIZE) payload->cap_len = DEFAULT_PLOAD_SIZE; if (space >= payload->cap_len) { buf += PpayloadSz; if (!pptrs->mac_ptr) { memcpy(buf, &eh, ethHdrLen); buf += ethHdrLen; } memcpy(buf, pptrs->packet_ptr, payload->cap_len-ethHdrLen); chptr->bufptr += payload->cap_len; /* don't count pkt_payload here */ #if NEED_ALIGN while (chptr->bufptr % 4 != 0) chptr->bufptr++; /* Don't worry, it's harmless increasing here */ #endif } else { chptr->bufptr += space; chptr->reprocess = TRUE; } } void tee_payload_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_msg *pmsg = (struct pkt_msg *) *data; pmsg->seqno = pptrs->seqno; pmsg->len = pptrs->f_len; memcpy(&pmsg->agent, pptrs->f_agent, sizeof(pmsg->agent)); memcpy(&pmsg->payload, pptrs->f_header, MIN(sizeof(pmsg->payload), pptrs->f_len)); } void nfprobe_extras_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct pkt_extras *pextras = (struct pkt_extras *) ++pdata; --pdata; /* Bringing back to original place */ if (pptrs->mpls_ptr) memcpy(&pextras->mpls_top_label, pptrs->mpls_ptr, 4); if (pptrs->l4_proto == IPPROTO_TCP) pextras->tcp_flags = pptrs->tcp_flags; } void in_iface_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->ifindex_in > 0) pdata->primitives.ifindex_in = pptrs->ifindex_in; } void out_iface_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->ifindex_out > 0) pdata->primitives.ifindex_out = pptrs->ifindex_out; } #if defined (HAVE_L2) void NF_src_mac_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_IN_SRC_MAC].len) memcpy(&pdata->primitives.eth_shost, pptrs->f_data+tpl->tpl[NF9_IN_SRC_MAC].off, MIN(tpl->tpl[NF9_IN_SRC_MAC].len, 6)); else if (tpl->tpl[NF9_OUT_SRC_MAC].len) memcpy(&pdata->primitives.eth_shost, pptrs->f_data+tpl->tpl[NF9_OUT_SRC_MAC].off, MIN(tpl->tpl[NF9_OUT_SRC_MAC].len, 6)); break; default: break; } } void NF_dst_mac_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_IN_DST_MAC].len) memcpy(&pdata->primitives.eth_dhost, pptrs->f_data+tpl->tpl[NF9_IN_DST_MAC].off, MIN(tpl->tpl[NF9_IN_DST_MAC].len, 6)); else if (tpl->tpl[NF9_OUT_DST_MAC].len) memcpy(&pdata->primitives.eth_dhost, pptrs->f_data+tpl->tpl[NF9_OUT_DST_MAC].off, MIN(tpl->tpl[NF9_OUT_DST_MAC].len, 6)); break; default: break; } } void NF_vlan_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_IN_VLAN].len) memcpy(&pdata->primitives.vlan_id, pptrs->f_data+tpl->tpl[NF9_IN_VLAN].off, MIN(tpl->tpl[NF9_IN_VLAN].len, 2)); else if (tpl->tpl[NF9_OUT_VLAN].len) memcpy(&pdata->primitives.vlan_id, pptrs->f_data+tpl->tpl[NF9_OUT_VLAN].off, MIN(tpl->tpl[NF9_OUT_VLAN].len, 2)); pdata->primitives.vlan_id = ntohs(pdata->primitives.vlan_id); break; default: break; } } void NF_cos_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { } void NF_etype_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; switch(hdr->version) { case 10: case 9: memcpy(&pdata->primitives.etype, pptrs->f_data+tpl->tpl[NF9_ETHERTYPE].off, MIN(tpl->tpl[NF9_ETHERTYPE].len, 2)); pdata->primitives.etype = ntohs(pdata->primitives.etype); break; default: break; } } #endif void NF_src_host_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int8_t src_mask = 0; /* check network-related primitives against fallback scenarios */ if (chptr->aggregation & (COUNT_SRC_NET|COUNT_SUM_NET) && !evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_net, NF_NET_KEEP)) return; switch(hdr->version) { case 10: case 9: if (pptrs->l3_proto == ETHERTYPE_IP) { if (chptr->plugin->cfg.xlate_src && tpl->tpl[NF9_XLATE_IPV4_SRC_ADDR].len) { memcpy(&pdata->primitives.src_ip.address.ipv4, pptrs->f_data+tpl->tpl[NF9_XLATE_IPV4_SRC_ADDR].off, MIN(tpl->tpl[NF9_XLATE_IPV4_SRC_ADDR].len, 4)); src_mask = 32; /* no xlate mask field exists atm */ } else { memcpy(&pdata->primitives.src_ip.address.ipv4, pptrs->f_data+tpl->tpl[NF9_IPV4_SRC_ADDR].off, MIN(tpl->tpl[NF9_IPV4_SRC_ADDR].len, 4)); memcpy(&src_mask, pptrs->f_data+tpl->tpl[NF9_SRC_MASK].off, tpl->tpl[NF9_SRC_MASK].len); } pdata->primitives.src_ip.family = AF_INET; break; } #if defined ENABLE_IPV6 if (pptrs->l3_proto == ETHERTYPE_IPV6) { if (chptr->plugin->cfg.xlate_src && tpl->tpl[NF9_XLATE_IPV6_SRC_ADDR].len) { memcpy(&pdata->primitives.src_ip.address.ipv6, pptrs->f_data+tpl->tpl[NF9_XLATE_IPV6_SRC_ADDR].off, MIN(tpl->tpl[NF9_XLATE_IPV6_SRC_ADDR].len, 16)); src_mask = 128; } else { memcpy(&pdata->primitives.src_ip.address.ipv6, pptrs->f_data+tpl->tpl[NF9_IPV6_SRC_ADDR].off, MIN(tpl->tpl[NF9_IPV6_SRC_ADDR].len, 16)); memcpy(&src_mask, pptrs->f_data+tpl->tpl[NF9_IPV6_SRC_MASK].off, tpl->tpl[NF9_IPV6_SRC_MASK].len); } pdata->primitives.src_ip.family = AF_INET6; break; } #endif break; case 8: switch(hdr->aggregation) { case 3: pdata->primitives.src_ip.address.ipv4.s_addr = ((struct struct_export_v8_3 *) pptrs->f_data)->src_prefix; src_mask = ((struct struct_export_v8_3 *) pptrs->f_data)->src_mask; pdata->primitives.src_ip.family = AF_INET; break; case 5: pdata->primitives.src_ip.address.ipv4.s_addr = ((struct struct_export_v8_5 *) pptrs->f_data)->src_prefix; src_mask = ((struct struct_export_v8_5 *) pptrs->f_data)->src_mask; pdata->primitives.src_ip.family = AF_INET; break; case 7: pdata->primitives.src_ip.address.ipv4.s_addr = ((struct struct_export_v8_7 *) pptrs->f_data)->srcaddr; pdata->primitives.src_ip.family = AF_INET; break; case 8: pdata->primitives.src_ip.address.ipv4.s_addr = ((struct struct_export_v8_8 *) pptrs->f_data)->srcaddr; pdata->primitives.src_ip.family = AF_INET; break; case 11: pdata->primitives.src_ip.address.ipv4.s_addr = ((struct struct_export_v8_11 *) pptrs->f_data)->src_prefix; src_mask = ((struct struct_export_v8_11 *) pptrs->f_data)->src_mask; pdata->primitives.src_ip.family = AF_INET; break; case 13: pdata->primitives.src_ip.address.ipv4.s_addr = ((struct struct_export_v8_13 *) pptrs->f_data)->src_prefix; src_mask = ((struct struct_export_v8_13 *) pptrs->f_data)->src_mask; pdata->primitives.src_ip.family = AF_INET; break; case 14: pdata->primitives.src_ip.address.ipv4.s_addr = ((struct struct_export_v8_14 *) pptrs->f_data)->src_prefix; src_mask = ((struct struct_export_v8_14 *) pptrs->f_data)->src_mask; pdata->primitives.src_ip.family = AF_INET; break; default: pdata->primitives.src_ip.address.ipv4.s_addr = 0; pdata->primitives.src_ip.family = AF_INET; break; } break; default: pdata->primitives.src_ip.address.ipv4.s_addr = ((struct struct_export_v5 *) pptrs->f_data)->srcaddr.s_addr; src_mask = ((struct struct_export_v5 *) pptrs->f_data)->src_mask; pdata->primitives.src_ip.family = AF_INET; break; } if ((chptr->aggregation & (COUNT_SRC_NET|COUNT_SUM_NET)) && chptr->plugin->cfg.nfacctd_net & NF_NET_KEEP) { u_int32_t maskbits[4], addrh[4]; u_int8_t j; memset(maskbits, 0,sizeof(maskbits)); for (j = 0; j < 4 && src_mask >= 32; j++, src_mask -= 32) maskbits[j] = 0xffffffffU; if (j < 4 && src_mask) maskbits[j] = ~(0xffffffffU >> src_mask); if (pdata->primitives.src_ip.family == AF_INET) { addrh[0] = ntohl(pdata->primitives.src_ip.address.ipv4.s_addr); addrh[0] &= maskbits[0]; pdata->primitives.src_ip.address.ipv4.s_addr = htonl(addrh[0]); } #if defined ENABLE_IPV6 else if (pdata->primitives.src_ip.family == AF_INET6) { memcpy(&addrh, (void *) pm_ntohl6(&pdata->primitives.src_ip.address.ipv6), IP6AddrSz); for (j = 0; j < 4; j++) addrh[j] &= maskbits[j]; memcpy(&pdata->primitives.src_ip.address.ipv6, (void *) pm_htonl6(addrh), IP6AddrSz); } #endif } } void NF_dst_host_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int8_t dst_mask = 0; /* check network-related primitives against fallback scenarios */ if (chptr->aggregation & (COUNT_DST_NET|COUNT_SUM_NET) && !evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_KEEP)) return; switch(hdr->version) { case 10: case 9: if (pptrs->l3_proto == ETHERTYPE_IP) { if (chptr->plugin->cfg.xlate_dst && tpl->tpl[NF9_XLATE_IPV4_DST_ADDR].len) { memcpy(&pdata->primitives.dst_ip.address.ipv4, pptrs->f_data+tpl->tpl[NF9_XLATE_IPV4_DST_ADDR].off, MIN(tpl->tpl[NF9_XLATE_IPV4_DST_ADDR].len, 4)); dst_mask = 32; /* no xlate mask field exists atm */ } else { memcpy(&pdata->primitives.dst_ip.address.ipv4, pptrs->f_data+tpl->tpl[NF9_IPV4_DST_ADDR].off, MIN(tpl->tpl[NF9_IPV4_DST_ADDR].len, 4)); memcpy(&dst_mask, pptrs->f_data+tpl->tpl[NF9_DST_MASK].off, tpl->tpl[NF9_DST_MASK].len); } pdata->primitives.dst_ip.family = AF_INET; break; } #if defined ENABLE_IPV6 if (pptrs->l3_proto == ETHERTYPE_IPV6) { if (chptr->plugin->cfg.xlate_dst && tpl->tpl[NF9_XLATE_IPV6_DST_ADDR].len) { memcpy(&pdata->primitives.dst_ip.address.ipv6, pptrs->f_data+tpl->tpl[NF9_XLATE_IPV6_DST_ADDR].off, MIN(tpl->tpl[NF9_XLATE_IPV6_DST_ADDR].len, 16)); dst_mask = 128; } else { memcpy(&pdata->primitives.dst_ip.address.ipv6, pptrs->f_data+tpl->tpl[NF9_IPV6_DST_ADDR].off, MIN(tpl->tpl[NF9_IPV6_DST_ADDR].len, 16)); memcpy(&dst_mask, pptrs->f_data+tpl->tpl[NF9_IPV6_DST_MASK].off, tpl->tpl[NF9_IPV6_DST_MASK].len); } pdata->primitives.dst_ip.family = AF_INET6; break; } #endif break; case 8: switch(hdr->aggregation) { case 4: pdata->primitives.dst_ip.address.ipv4.s_addr = ((struct struct_export_v8_4 *) pptrs->f_data)->dst_prefix; dst_mask = ((struct struct_export_v8_4 *) pptrs->f_data)->dst_mask; pdata->primitives.dst_ip.family = AF_INET; break; case 5: pdata->primitives.dst_ip.address.ipv4.s_addr = ((struct struct_export_v8_5 *) pptrs->f_data)->dst_prefix; dst_mask = ((struct struct_export_v8_5 *) pptrs->f_data)->dst_mask; pdata->primitives.dst_ip.family = AF_INET; break; case 6: pdata->primitives.dst_ip.address.ipv4.s_addr = ((struct struct_export_v8_6 *) pptrs->f_data)->dstaddr; pdata->primitives.dst_ip.family = AF_INET; break; case 7: pdata->primitives.dst_ip.address.ipv4.s_addr = ((struct struct_export_v8_7 *) pptrs->f_data)->dstaddr; pdata->primitives.dst_ip.family = AF_INET; break; case 8: pdata->primitives.dst_ip.address.ipv4.s_addr = ((struct struct_export_v8_8 *) pptrs->f_data)->dstaddr; pdata->primitives.dst_ip.family = AF_INET; break; case 12: pdata->primitives.dst_ip.address.ipv4.s_addr = ((struct struct_export_v8_12 *) pptrs->f_data)->dst_prefix; dst_mask = ((struct struct_export_v8_12 *) pptrs->f_data)->dst_mask; pdata->primitives.dst_ip.family = AF_INET; break; case 13: pdata->primitives.dst_ip.address.ipv4.s_addr = ((struct struct_export_v8_13 *) pptrs->f_data)->dst_prefix; dst_mask = ((struct struct_export_v8_13 *) pptrs->f_data)->dst_mask; pdata->primitives.dst_ip.family = AF_INET; break; case 14: pdata->primitives.dst_ip.address.ipv4.s_addr = ((struct struct_export_v8_14 *) pptrs->f_data)->dst_prefix; dst_mask = ((struct struct_export_v8_14 *) pptrs->f_data)->dst_mask; pdata->primitives.dst_ip.family = AF_INET; break; default: pdata->primitives.dst_ip.address.ipv4.s_addr = 0; pdata->primitives.dst_ip.family = AF_INET; break; } break; default: pdata->primitives.dst_ip.address.ipv4.s_addr = ((struct struct_export_v5 *) pptrs->f_data)->dstaddr.s_addr; dst_mask = ((struct struct_export_v5 *) pptrs->f_data)->dst_mask; pdata->primitives.dst_ip.family = AF_INET; break; } if ((chptr->aggregation & (COUNT_DST_NET|COUNT_SUM_NET)) && chptr->plugin->cfg.nfacctd_net & NF_NET_KEEP) { u_int32_t maskbits[4], addrh[4]; u_int8_t j; memset(maskbits, 0, sizeof(maskbits)); for (j = 0; j < 4 && dst_mask >= 32; j++, dst_mask -= 32) maskbits[j] = 0xffffffffU; if (j < 4 && dst_mask) maskbits[j] = ~(0xffffffffU >> dst_mask); if (pdata->primitives.dst_ip.family == AF_INET) { addrh[0] = ntohl(pdata->primitives.dst_ip.address.ipv4.s_addr); addrh[0] &= maskbits[0]; pdata->primitives.dst_ip.address.ipv4.s_addr = htonl(addrh[0]); } #if defined ENABLE_IPV6 else if (pdata->primitives.dst_ip.family == AF_INET6) { memcpy(&addrh, (void *) pm_ntohl6(&pdata->primitives.dst_ip.address.ipv6), IP6AddrSz); for (j = 0; j < 4; j++) addrh[j] &= maskbits[j]; memcpy(&pdata->primitives.dst_ip.address.ipv6, (void *) pm_htonl6(addrh), IP6AddrSz); } #endif } } void NF_src_nmask_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_net, NF_NET_KEEP)) return; switch(hdr->version) { case 10: case 9: if (pptrs->l3_proto == ETHERTYPE_IP) { memcpy(&pdata->primitives.src_nmask, pptrs->f_data+tpl->tpl[NF9_SRC_MASK].off, tpl->tpl[NF9_SRC_MASK].len); break; } #if defined ENABLE_IPV6 if (pptrs->l3_proto == ETHERTYPE_IPV6) { memcpy(&pdata->primitives.src_nmask, pptrs->f_data+tpl->tpl[NF9_IPV6_SRC_MASK].off, tpl->tpl[NF9_IPV6_SRC_MASK].len); break; } #endif break; case 8: switch(hdr->aggregation) { case 3: pdata->primitives.src_nmask = ((struct struct_export_v8_3 *) pptrs->f_data)->src_mask; break; case 5: pdata->primitives.src_nmask = ((struct struct_export_v8_5 *) pptrs->f_data)->src_mask; break; case 11: pdata->primitives.src_nmask = ((struct struct_export_v8_11 *) pptrs->f_data)->src_mask; break; case 13: pdata->primitives.src_nmask = ((struct struct_export_v8_13 *) pptrs->f_data)->src_mask; break; case 14: pdata->primitives.src_nmask = ((struct struct_export_v8_14 *) pptrs->f_data)->src_mask; break; default: pdata->primitives.src_nmask = 0; break; } break; default: pdata->primitives.src_nmask = ((struct struct_export_v5 *) pptrs->f_data)->src_mask; break; } } void NF_dst_nmask_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_KEEP)) return; switch(hdr->version) { case 10: case 9: if (pptrs->l3_proto == ETHERTYPE_IP) { memcpy(&pdata->primitives.dst_nmask, pptrs->f_data+tpl->tpl[NF9_DST_MASK].off, tpl->tpl[NF9_DST_MASK].len); break; } #if defined ENABLE_IPV6 if (pptrs->l3_proto == ETHERTYPE_IPV6) { memcpy(&pdata->primitives.dst_nmask, pptrs->f_data+tpl->tpl[NF9_IPV6_DST_MASK].off, tpl->tpl[NF9_IPV6_DST_MASK].len); break; } #endif break; case 8: switch(hdr->aggregation) { case 4: pdata->primitives.dst_nmask = ((struct struct_export_v8_4 *) pptrs->f_data)->dst_mask; break; case 5: pdata->primitives.dst_nmask = ((struct struct_export_v8_5 *) pptrs->f_data)->dst_mask; break; case 12: pdata->primitives.dst_nmask = ((struct struct_export_v8_12 *) pptrs->f_data)->dst_mask; break; case 13: pdata->primitives.dst_nmask = ((struct struct_export_v8_13 *) pptrs->f_data)->dst_mask; break; case 14: pdata->primitives.dst_nmask = ((struct struct_export_v8_14 *) pptrs->f_data)->dst_mask; break; default: pdata->primitives.dst_nmask = 0; break; } break; default: pdata->primitives.dst_nmask = ((struct struct_export_v5 *) pptrs->f_data)->dst_mask; break; } } void NF_src_as_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t asn16 = 0; u_int32_t asn32 = 0; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_SRC_AS].len == 2) { memcpy(&asn16, pptrs->f_data+tpl->tpl[NF9_SRC_AS].off, 2); pdata->primitives.src_as = ntohs(asn16); } else if (tpl->tpl[NF9_SRC_AS].len == 4) { memcpy(&asn32, pptrs->f_data+tpl->tpl[NF9_SRC_AS].off, 4); pdata->primitives.src_as = ntohl(asn32); } break; case 8: switch(hdr->aggregation) { case 1: pdata->primitives.src_as = ntohs(((struct struct_export_v8_1 *) pptrs->f_data)->src_as); break; case 3: pdata->primitives.src_as = ntohs(((struct struct_export_v8_3 *) pptrs->f_data)->src_as); break; case 5: pdata->primitives.src_as = ntohs(((struct struct_export_v8_5 *) pptrs->f_data)->src_as); break; case 9: pdata->primitives.src_as = ntohs(((struct struct_export_v8_9 *) pptrs->f_data)->src_as); break; case 11: pdata->primitives.src_as = ntohs(((struct struct_export_v8_11 *) pptrs->f_data)->src_as); break; case 13: pdata->primitives.src_as = ntohs(((struct struct_export_v8_13 *) pptrs->f_data)->src_as); break; default: pdata->primitives.src_as = 0; break; } break; default: pdata->primitives.src_as = ntohs(((struct struct_export_v5 *) pptrs->f_data)->src_as); break; } } void NF_dst_as_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t asn16 = 0; u_int32_t asn32 = 0; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_DST_AS].len == 2) { memcpy(&asn16, pptrs->f_data+tpl->tpl[NF9_DST_AS].off, 2); pdata->primitives.dst_as = ntohs(asn16); } else if (tpl->tpl[NF9_DST_AS].len == 4) { memcpy(&asn32, pptrs->f_data+tpl->tpl[NF9_DST_AS].off, 4); pdata->primitives.dst_as = ntohl(asn32); } break; case 8: switch(hdr->aggregation) { case 1: pdata->primitives.dst_as = ntohs(((struct struct_export_v8_1 *) pptrs->f_data)->dst_as); break; case 4: pdata->primitives.dst_as = ntohs(((struct struct_export_v8_4 *) pptrs->f_data)->dst_as); break; case 5: pdata->primitives.dst_as = ntohs(((struct struct_export_v8_5 *) pptrs->f_data)->dst_as); break; case 9: pdata->primitives.dst_as = ntohs(((struct struct_export_v8_9 *) pptrs->f_data)->dst_as); break; case 12: pdata->primitives.dst_as = ntohs(((struct struct_export_v8_12 *) pptrs->f_data)->dst_as); break; case 13: pdata->primitives.dst_as = ntohs(((struct struct_export_v8_13 *) pptrs->f_data)->dst_as); break; default: pdata->primitives.dst_as = 0; break; } break; default: pdata->primitives.dst_as = ntohs(((struct struct_export_v5 *) pptrs->f_data)->dst_as); break; } } void NF_peer_src_as_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; u_int16_t asn16 = 0; u_int32_t asn32 = 0; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; switch (hdr->version) { case 10: case 9: if (tpl->tpl[NF9_PEER_SRC_AS].len == 2) { memcpy(&asn16, pptrs->f_data+tpl->tpl[NF9_PEER_SRC_AS].off, 2); pbgp->peer_src_as = ntohs(asn16); } else if (tpl->tpl[NF9_PEER_SRC_AS].len == 4) { memcpy(&asn32, pptrs->f_data+tpl->tpl[NF9_PEER_SRC_AS].off, 4); pbgp->peer_src_as = ntohl(asn32); } break; case 8: default: break; } } void NF_peer_dst_as_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; u_int16_t asn16 = 0; u_int32_t asn32 = 0; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; switch (hdr->version) { case 10: case 9: if (tpl->tpl[NF9_PEER_DST_AS].len == 2) { memcpy(&asn16, pptrs->f_data+tpl->tpl[NF9_PEER_DST_AS].off, 2); pbgp->peer_dst_as = ntohs(asn16); } else if (tpl->tpl[NF9_PEER_DST_AS].len == 4) { memcpy(&asn32, pptrs->f_data+tpl->tpl[NF9_PEER_DST_AS].off, 4); pbgp->peer_dst_as = ntohl(asn32); } break; case 8: default: break; } } void NF_peer_src_ip_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; struct sockaddr *sa = (struct sockaddr *) pptrs->f_agent; --pdata; /* Bringing back to original place */ if (sa->sa_family == AF_INET) { pbgp->peer_src_ip.address.ipv4.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; pbgp->peer_src_ip.family = AF_INET; } #if defined ENABLE_IPV6 else if (sa->sa_family == AF_INET6) { memcpy(&pbgp->peer_src_ip.address.ipv6, &((struct sockaddr_in6 *)sa)->sin6_addr, IP6AddrSz); pbgp->peer_src_ip.family = AF_INET6; } #endif } void NF_peer_dst_ip_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_KEEP)) return; switch(hdr->version) { case 10: case 9: if (pptrs->l3_proto == ETHERTYPE_IP) { memcpy(&pbgp->peer_dst_ip.address.ipv4, pptrs->f_data+tpl->tpl[NF9_BGP_IPV4_NEXT_HOP].off, MIN(tpl->tpl[NF9_BGP_IPV4_NEXT_HOP].len, 4)); pbgp->peer_dst_ip.family = AF_INET; break; } #if defined ENABLE_IPV6 if (pptrs->l3_proto == ETHERTYPE_IPV6) { memcpy(&pbgp->peer_dst_ip.address.ipv6, pptrs->f_data+tpl->tpl[NF9_BGP_IPV6_NEXT_HOP].off, MIN(tpl->tpl[NF9_BGP_IPV6_NEXT_HOP].len, 16)); pbgp->peer_dst_ip.family = AF_INET6; break; } #endif break; case 8: default: break; } } void NF_src_port_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int8_t l4_proto = 0; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_L4_PROTOCOL].len == 1) memcpy(&l4_proto, pptrs->f_data+tpl->tpl[NF9_L4_PROTOCOL].off, 1); if (l4_proto == IPPROTO_UDP || l4_proto == IPPROTO_TCP) { if (chptr->plugin->cfg.xlate_src && tpl->tpl[NF9_XLATE_L4_SRC_PORT].len) memcpy(&pdata->primitives.src_port, pptrs->f_data+tpl->tpl[NF9_XLATE_L4_SRC_PORT].off, MIN(tpl->tpl[NF9_XLATE_L4_SRC_PORT].len, 2)); else if (tpl->tpl[NF9_L4_SRC_PORT].len) memcpy(&pdata->primitives.src_port, pptrs->f_data+tpl->tpl[NF9_L4_SRC_PORT].off, MIN(tpl->tpl[NF9_L4_SRC_PORT].len, 2)); else if (l4_proto == IPPROTO_UDP && tpl->tpl[NF9_UDP_SRC_PORT].len) memcpy(&pdata->primitives.src_port, pptrs->f_data+tpl->tpl[NF9_UDP_SRC_PORT].off, MIN(tpl->tpl[NF9_UDP_SRC_PORT].len, 2)); else if (l4_proto == IPPROTO_TCP && tpl->tpl[NF9_TCP_SRC_PORT].len) memcpy(&pdata->primitives.src_port, pptrs->f_data+tpl->tpl[NF9_TCP_SRC_PORT].off, MIN(tpl->tpl[NF9_TCP_SRC_PORT].len, 2)); pdata->primitives.src_port = ntohs(pdata->primitives.src_port); } else pdata->primitives.src_port = 0; break; case 8: switch(hdr->aggregation) { case 2: if ((((struct struct_export_v8_2 *) pptrs->f_data)->prot == IPPROTO_UDP) || ((struct struct_export_v8_2 *) pptrs->f_data)->prot == IPPROTO_TCP) pdata->primitives.src_port = ntohs(((struct struct_export_v8_2 *) pptrs->f_data)->srcport); break; case 8: if ((((struct struct_export_v8_8 *) pptrs->f_data)->prot == IPPROTO_UDP) || ((struct struct_export_v8_8 *) pptrs->f_data)->prot == IPPROTO_TCP) pdata->primitives.src_port = ntohs(((struct struct_export_v8_8 *) pptrs->f_data)->srcport); break; case 10: if ((((struct struct_export_v8_10 *) pptrs->f_data)->prot == IPPROTO_UDP) || ((struct struct_export_v8_10 *) pptrs->f_data)->prot == IPPROTO_TCP) pdata->primitives.src_port = ntohs(((struct struct_export_v8_10 *) pptrs->f_data)->srcport); break; case 14: if ((((struct struct_export_v8_14 *) pptrs->f_data)->prot == IPPROTO_UDP) || ((struct struct_export_v8_14 *) pptrs->f_data)->prot == IPPROTO_TCP) pdata->primitives.src_port = ntohs(((struct struct_export_v8_14 *) pptrs->f_data)->srcport); break; default: pdata->primitives.src_port = 0; break; } break; default: if ((((struct struct_export_v5 *) pptrs->f_data)->prot == IPPROTO_UDP) || ((struct struct_export_v5 *) pptrs->f_data)->prot == IPPROTO_TCP) { pdata->primitives.src_port = ntohs(((struct struct_export_v5 *) pptrs->f_data)->srcport); } else pdata->primitives.src_port = 0; break; } } void NF_dst_port_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int8_t l4_proto = 0; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_L4_PROTOCOL].len == 1) memcpy(&l4_proto, pptrs->f_data+tpl->tpl[NF9_L4_PROTOCOL].off, 1); if (l4_proto == IPPROTO_UDP || l4_proto == IPPROTO_TCP) { if (chptr->plugin->cfg.xlate_dst && tpl->tpl[NF9_XLATE_L4_DST_PORT].len) memcpy(&pdata->primitives.dst_port, pptrs->f_data+tpl->tpl[NF9_XLATE_L4_DST_PORT].off, MIN(tpl->tpl[NF9_XLATE_L4_DST_PORT].len, 2)); else if (tpl->tpl[NF9_L4_DST_PORT].len) memcpy(&pdata->primitives.dst_port, pptrs->f_data+tpl->tpl[NF9_L4_DST_PORT].off, MIN(tpl->tpl[NF9_L4_DST_PORT].len, 2)); else if (l4_proto == IPPROTO_UDP && tpl->tpl[NF9_UDP_DST_PORT].len) memcpy(&pdata->primitives.dst_port, pptrs->f_data+tpl->tpl[NF9_UDP_DST_PORT].off, MIN(tpl->tpl[NF9_UDP_DST_PORT].len, 2)); else if (l4_proto == IPPROTO_TCP && tpl->tpl[NF9_TCP_DST_PORT].len) memcpy(&pdata->primitives.dst_port, pptrs->f_data+tpl->tpl[NF9_TCP_DST_PORT].off, MIN(tpl->tpl[NF9_TCP_DST_PORT].len, 2)); pdata->primitives.dst_port = ntohs(pdata->primitives.dst_port); } else pdata->primitives.dst_port = 0; break; case 8: switch(hdr->aggregation) { case 2: if ((((struct struct_export_v8_2 *) pptrs->f_data)->prot == IPPROTO_UDP) || ((struct struct_export_v8_2 *) pptrs->f_data)->prot == IPPROTO_TCP) pdata->primitives.dst_port = ntohs(((struct struct_export_v8_2 *) pptrs->f_data)->dstport); break; case 8: if ((((struct struct_export_v8_8 *) pptrs->f_data)->prot == IPPROTO_UDP) || ((struct struct_export_v8_8 *) pptrs->f_data)->prot == IPPROTO_TCP) pdata->primitives.dst_port = ntohs(((struct struct_export_v8_8 *) pptrs->f_data)->dstport); break; case 10: if ((((struct struct_export_v8_10 *) pptrs->f_data)->prot == IPPROTO_UDP) || ((struct struct_export_v8_10 *) pptrs->f_data)->prot == IPPROTO_TCP) pdata->primitives.dst_port = ntohs(((struct struct_export_v8_10 *) pptrs->f_data)->dstport); break; case 14: if ((((struct struct_export_v8_14 *) pptrs->f_data)->prot == IPPROTO_UDP) || ((struct struct_export_v8_14 *) pptrs->f_data)->prot == IPPROTO_TCP) pdata->primitives.dst_port = ntohs(((struct struct_export_v8_14 *) pptrs->f_data)->dstport); break; default: pdata->primitives.dst_port = 0; break; } break; default: if ((((struct struct_export_v5 *) pptrs->f_data)->prot == IPPROTO_UDP) || ((struct struct_export_v5 *) pptrs->f_data)->prot == IPPROTO_TCP) pdata->primitives.dst_port = ntohs(((struct struct_export_v5 *) pptrs->f_data)->dstport); else pdata->primitives.dst_port = 0; break; } } void NF_ip_tos_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; switch(hdr->version) { case 10: case 9: memcpy(&pdata->primitives.tos, pptrs->f_data+tpl->tpl[NF9_SRC_TOS].off, MIN(tpl->tpl[NF9_SRC_TOS].len, 1)); break; case 8: switch(hdr->aggregation) { case 6: pdata->primitives.tos = ((struct struct_export_v8_6 *) pptrs->f_data)->tos; break; case 7: pdata->primitives.tos = ((struct struct_export_v8_7 *) pptrs->f_data)->tos; break; case 8: pdata->primitives.tos = ((struct struct_export_v8_8 *) pptrs->f_data)->tos; break; case 9: pdata->primitives.tos = ((struct struct_export_v8_9 *) pptrs->f_data)->tos; break; case 10: pdata->primitives.tos = ((struct struct_export_v8_10 *) pptrs->f_data)->tos; break; case 11: pdata->primitives.tos = ((struct struct_export_v8_11 *) pptrs->f_data)->tos; break; case 12: pdata->primitives.tos = ((struct struct_export_v8_12 *) pptrs->f_data)->tos; break; case 13: pdata->primitives.tos = ((struct struct_export_v8_13 *) pptrs->f_data)->tos; break; case 14: pdata->primitives.tos = ((struct struct_export_v8_14 *) pptrs->f_data)->tos; break; default: pdata->primitives.tos = 0; break; } break; default: pdata->primitives.tos = ((struct struct_export_v5 *) pptrs->f_data)->tos; break; } } void NF_ip_proto_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; switch(hdr->version) { case 10: case 9: memcpy(&pdata->primitives.proto, pptrs->f_data+tpl->tpl[NF9_L4_PROTOCOL].off, MIN(tpl->tpl[NF9_L4_PROTOCOL].len, 1)); break; case 8: switch(hdr->aggregation) { case 2: pdata->primitives.proto = ((struct struct_export_v8_2 *) pptrs->f_data)->prot; break; case 8: pdata->primitives.proto = ((struct struct_export_v8_8 *) pptrs->f_data)->prot; break; case 10: pdata->primitives.proto = ((struct struct_export_v8_10 *) pptrs->f_data)->prot; break; case 14: pdata->primitives.proto = ((struct struct_export_v8_14 *) pptrs->f_data)->prot; break; default: pdata->primitives.proto = 0; break; } break; default: pdata->primitives.proto = ((struct struct_export_v5 *) pptrs->f_data)->prot; break; } } void NF_tcp_flags_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int8_t tcp_flags = 0; switch(hdr->version) { case 10: case 9: if ((u_int8_t)*(pptrs->f_data+tpl->tpl[NF9_L4_PROTOCOL].off) == IPPROTO_TCP) { memcpy(&tcp_flags, pptrs->f_data+tpl->tpl[NF9_TCP_FLAGS].off, MIN(tpl->tpl[NF9_TCP_FLAGS].len, 1)); pdata->tcp_flags = tcp_flags; } break; default: if (((struct struct_export_v5 *) pptrs->f_data)->prot == IPPROTO_TCP && hdr->version == 5) pdata->tcp_flags = ((struct struct_export_v5 *) pptrs->f_data)->tcp_flags; break; } } /* times from the netflow engine are in msecs */ void NF_counters_msecs_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; time_t fstime = 0; u_int32_t t32 = 0; u_int64_t t64 = 0; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_IN_BYTES].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_IN_BYTES].off, 4); pdata->pkt_len = ntohl(t32); } else if (tpl->tpl[NF9_IN_BYTES].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_IN_BYTES].off, 8); pdata->pkt_len = pm_ntohll(t64); } else if (tpl->tpl[NF9_FLOW_BYTES].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_FLOW_BYTES].off, 4); pdata->pkt_len = ntohl(t32); } else if (tpl->tpl[NF9_FLOW_BYTES].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_FLOW_BYTES].off, 8); pdata->pkt_len = pm_ntohll(t64); } else if (tpl->tpl[NF9_OUT_BYTES].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_OUT_BYTES].off, 4); pdata->pkt_len = ntohl(t32); } else if (tpl->tpl[NF9_OUT_BYTES].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_OUT_BYTES].off, 8); pdata->pkt_len = pm_ntohll(t64); } if (tpl->tpl[NF9_IN_PACKETS].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_IN_PACKETS].off, 4); pdata->pkt_num = ntohl(t32); } else if (tpl->tpl[NF9_IN_PACKETS].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_IN_PACKETS].off, 8); pdata->pkt_num = pm_ntohll(t64); } else if (tpl->tpl[NF9_FLOW_PACKETS].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_FLOW_PACKETS].off, 4); pdata->pkt_num = ntohl(t32); } else if (tpl->tpl[NF9_FLOW_PACKETS].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_FLOW_PACKETS].off, 8); pdata->pkt_num = pm_ntohll(t64); } else if (tpl->tpl[NF9_OUT_PACKETS].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_OUT_PACKETS].off, 4); pdata->pkt_num = ntohl(t32); } else if (tpl->tpl[NF9_OUT_PACKETS].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_OUT_PACKETS].off, 8); pdata->pkt_num = pm_ntohll(t64); } if ((tpl->tpl[NF9_FIRST_SWITCHED].len || tpl->tpl[NF9_LAST_SWITCHED].len) && hdr->version == 9) { memcpy(&fstime, pptrs->f_data+tpl->tpl[NF9_FIRST_SWITCHED].off, tpl->tpl[NF9_FIRST_SWITCHED].len); pdata->time_start.tv_sec = ntohl(((struct struct_header_v9 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v9 *) pptrs->f_header)->SysUptime)-ntohl(fstime))/1000); memcpy(&fstime, pptrs->f_data+tpl->tpl[NF9_LAST_SWITCHED].off, tpl->tpl[NF9_LAST_SWITCHED].len); pdata->time_end.tv_sec = ntohl(((struct struct_header_v9 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v9 *) pptrs->f_header)->SysUptime)-ntohl(fstime))/1000); } else if (tpl->tpl[NF9_FIRST_SWITCHED_MSEC].len || tpl->tpl[NF9_LAST_SWITCHED_MSEC].len) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_FIRST_SWITCHED_MSEC].off, tpl->tpl[NF9_FIRST_SWITCHED_MSEC].len); pdata->time_start.tv_sec = pm_ntohll(t64)/1000; memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_LAST_SWITCHED_MSEC].off, tpl->tpl[NF9_LAST_SWITCHED_MSEC].len); pdata->time_end.tv_sec = pm_ntohll(t64)/1000; } else if (tpl->tpl[NF9_OBSERVATION_TIME_MSEC].len) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_OBSERVATION_TIME_MSEC].off, tpl->tpl[NF9_OBSERVATION_TIME_MSEC].len); pdata->time_start.tv_sec = pm_ntohll(t64)/1000; } /* sec handling here: msec vs sec restricted up to NetFlow v8 */ else if (tpl->tpl[NF9_FIRST_SWITCHED_SEC].len == 4 || tpl->tpl[NF9_LAST_SWITCHED_SEC].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_FIRST_SWITCHED_SEC].off, tpl->tpl[NF9_FIRST_SWITCHED_SEC].len); pdata->time_start.tv_sec = ntohl(t32); memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_LAST_SWITCHED_SEC].off, tpl->tpl[NF9_LAST_SWITCHED_SEC].len); pdata->time_end.tv_sec = ntohl(t32); } else if (tpl->tpl[NF9_FIRST_SWITCHED_SEC].len == 8 || tpl->tpl[NF9_LAST_SWITCHED_SEC].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_FIRST_SWITCHED_SEC].off, tpl->tpl[NF9_FIRST_SWITCHED_SEC].len); pdata->time_start.tv_sec = pm_ntohll(t64); memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_LAST_SWITCHED_SEC].off, tpl->tpl[NF9_LAST_SWITCHED_SEC].len); pdata->time_end.tv_sec = pm_ntohll(t64); } break; case 8: switch(hdr->aggregation) { case 6: pdata->pkt_len = ntohl(((struct struct_export_v8_6 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_6 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_6 *) pptrs->f_data)->First))/1000); pdata->time_end.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_6 *) pptrs->f_data)->Last))/1000); break; case 7: pdata->pkt_len = ntohl(((struct struct_export_v8_7 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_7 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_7 *) pptrs->f_data)->First))/1000); pdata->time_end.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_7 *) pptrs->f_data)->Last))/1000); break; case 8: pdata->pkt_len = ntohl(((struct struct_export_v8_8 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_8 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_8 *) pptrs->f_data)->First))/1000); pdata->time_end.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_8 *) pptrs->f_data)->Last))/1000); break; default: pdata->pkt_len = ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->First))/1000); pdata->time_end.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->Last))/1000); break; } break; default: pdata->pkt_len = ntohl(((struct struct_export_v5 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v5 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = ntohl(((struct struct_header_v5 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v5 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v5 *) pptrs->f_data)->First))/1000); pdata->time_end.tv_sec = ntohl(((struct struct_header_v5 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v5 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v5 *) pptrs->f_data)->Last))/1000); break; } } /* times from the netflow engine are in secs */ void NF_counters_secs_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; time_t fstime = 0; u_int32_t t32 = 0; u_int64_t t64 = 0; switch(hdr->version) { case 9: if (tpl->tpl[NF9_IN_BYTES].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_IN_BYTES].off, 4); pdata->pkt_len = ntohl(t32); } else if (tpl->tpl[NF9_IN_BYTES].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_IN_BYTES].off, 8); pdata->pkt_len = pm_ntohll(t64); } else if (tpl->tpl[NF9_FLOW_BYTES].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_FLOW_BYTES].off, 4); pdata->pkt_len = ntohl(t32); } else if (tpl->tpl[NF9_FLOW_BYTES].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_FLOW_BYTES].off, 8); pdata->pkt_len = pm_ntohll(t64); } if (tpl->tpl[NF9_IN_PACKETS].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_IN_PACKETS].off, 4); pdata->pkt_num = ntohl(t32); } else if (tpl->tpl[NF9_IN_PACKETS].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_IN_PACKETS].off, 8); pdata->pkt_num = pm_ntohll(t64); } else if (tpl->tpl[NF9_FLOW_PACKETS].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_FLOW_PACKETS].off, 4); pdata->pkt_num = ntohl(t32); } else if (tpl->tpl[NF9_FLOW_PACKETS].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_FLOW_PACKETS].off, 8); pdata->pkt_num = pm_ntohll(t64); } memcpy(&fstime, pptrs->f_data+tpl->tpl[NF9_FIRST_SWITCHED].off, tpl->tpl[NF9_FIRST_SWITCHED].len); pdata->time_start.tv_sec = ntohl(((struct struct_header_v9 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v9 *) pptrs->f_header)->SysUptime)-ntohl(fstime)); memcpy(&fstime, pptrs->f_data+tpl->tpl[NF9_LAST_SWITCHED].off, tpl->tpl[NF9_LAST_SWITCHED].len); pdata->time_end.tv_sec = ntohl(((struct struct_header_v9 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v9 *) pptrs->f_header)->SysUptime)-ntohl(fstime)); break; case 8: switch(hdr->aggregation) { case 6: pdata->pkt_len = ntohl(((struct struct_export_v8_6 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_6 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_6 *) pptrs->f_data)->First)); pdata->time_end.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_6 *) pptrs->f_data)->Last)); break; case 7: pdata->pkt_len = ntohl(((struct struct_export_v8_7 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_7 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_7 *) pptrs->f_data)->First)); pdata->time_end.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_7 *) pptrs->f_data)->Last)); break; case 8: pdata->pkt_len = ntohl(((struct struct_export_v8_8 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_8 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_8 *) pptrs->f_data)->First)); pdata->time_end.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_8 *) pptrs->f_data)->Last)); break; default: pdata->pkt_len = ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->First)); pdata->time_end.tv_sec = ntohl(((struct struct_header_v8 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v8 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->Last)); break; } break; default: pdata->pkt_len = ntohl(((struct struct_export_v5 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v5 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = ntohl(((struct struct_header_v5 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v5 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v5 *) pptrs->f_data)->First)); pdata->time_end.tv_sec = ntohl(((struct struct_header_v5 *) pptrs->f_header)->unix_secs)- (ntohl(((struct struct_header_v5 *) pptrs->f_header)->SysUptime)-ntohl(((struct struct_export_v5 *) pptrs->f_data)->Last)); break; } } /* ignore netflow engine times and generate new ones */ void NF_counters_new_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int32_t t32 = 0; u_int64_t t64 = 0; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_IN_BYTES].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_IN_BYTES].off, 4); pdata->pkt_len = ntohl(t32); } else if (tpl->tpl[NF9_IN_BYTES].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_IN_BYTES].off, 8); pdata->pkt_len = pm_ntohll(t64); } else if (tpl->tpl[NF9_FLOW_BYTES].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_FLOW_BYTES].off, 4); pdata->pkt_len = ntohl(t32); } else if (tpl->tpl[NF9_FLOW_BYTES].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_FLOW_BYTES].off, 8); pdata->pkt_len = pm_ntohll(t64); } if (tpl->tpl[NF9_IN_PACKETS].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_IN_PACKETS].off, 4); pdata->pkt_num = ntohl(t32); } else if (tpl->tpl[NF9_IN_PACKETS].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_IN_PACKETS].off, 8); pdata->pkt_num = pm_ntohll(t64); } else if (tpl->tpl[NF9_FLOW_PACKETS].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_FLOW_PACKETS].off, 4); pdata->pkt_num = ntohl(t32); } else if (tpl->tpl[NF9_FLOW_PACKETS].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_FLOW_PACKETS].off, 8); pdata->pkt_num = pm_ntohll(t64); } pdata->time_start.tv_sec = 0; pdata->time_start.tv_usec = 0; pdata->time_end.tv_sec = 0; pdata->time_end.tv_usec = 0; break; case 8: switch(hdr->aggregation) { case 6: pdata->pkt_len = ntohl(((struct struct_export_v8_6 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_6 *) pptrs->f_data)->dPkts); break; case 7: pdata->pkt_len = ntohl(((struct struct_export_v8_7 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_7 *) pptrs->f_data)->dPkts); break; case 8: pdata->pkt_len = ntohl(((struct struct_export_v8_8 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_8 *) pptrs->f_data)->dPkts); break; default: pdata->pkt_len = ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->dPkts); break; } pdata->time_start.tv_sec = 0; pdata->time_start.tv_usec = 0; pdata->time_end.tv_sec = 0; pdata->time_end.tv_usec = 0; break; default: pdata->pkt_len = ntohl(((struct struct_export_v5 *) pptrs->f_data)->dOctets); pdata->pkt_num = ntohl(((struct struct_export_v5 *) pptrs->f_data)->dPkts); pdata->time_start.tv_sec = 0; pdata->time_start.tv_usec = 0; pdata->time_end.tv_sec = 0; pdata->time_end.tv_usec = 0; break; } } void ptag_id_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; pdata->primitives.id = pptrs->tag; } void ptag_id2_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; pdata->primitives.id2 = pptrs->tag2; } void NF_flows_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int32_t t32 = 0; u_int64_t t64 = 0; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_FLOWS].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_FLOWS].off, 4); pdata->flo_num = ntohl(t32); } else if (tpl->tpl[NF9_FLOWS].len == 8) { memcpy(&t64, pptrs->f_data+tpl->tpl[NF9_FLOWS].off, 8); pdata->flo_num = pm_ntohll(t64); } if (!pdata->flo_num) pdata->flo_num = 1; break; case 8: switch(hdr->aggregation) { case 6: case 7: case 8: break; default: pdata->flo_num = ntohl(((struct struct_export_v8_1 *) pptrs->f_data)->dFlows); break; } break; default: pdata->flo_num = 1; break; } } void NF_in_iface_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t iface16 = 0; u_int32_t iface32 = 0; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_INPUT_SNMP].len == 2) { memcpy(&iface16, pptrs->f_data+tpl->tpl[NF9_INPUT_SNMP].off, 2); pdata->primitives.ifindex_in = ntohs(iface16); } else if (tpl->tpl[NF9_INPUT_SNMP].len == 4) { memcpy(&iface32, pptrs->f_data+tpl->tpl[NF9_INPUT_SNMP].off, 4); pdata->primitives.ifindex_in = ntohl(iface32); } break; case 8: switch(hdr->aggregation) { case 1: iface16 = ntohs(((struct struct_export_v8_1 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; case 3: iface16 = ntohs(((struct struct_export_v8_3 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; case 5: iface16 = ntohs(((struct struct_export_v8_5 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; case 7: iface16 = ntohs(((struct struct_export_v8_7 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; case 8: iface16 = ntohs(((struct struct_export_v8_8 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; case 9: iface16 = ntohs(((struct struct_export_v8_9 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; case 10: iface16 = ntohs(((struct struct_export_v8_10 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; case 11: iface16 = ntohs(((struct struct_export_v8_11 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; case 13: iface16 = ntohs(((struct struct_export_v8_13 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; case 14: iface16 = ntohs(((struct struct_export_v8_14 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; default: pdata->primitives.ifindex_in = 0; break; } break; default: iface16 = ntohs(((struct struct_export_v5 *) pptrs->f_data)->input); pdata->primitives.ifindex_in = iface16; break; } } void NF_out_iface_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t iface16 = 0; u_int32_t iface32 = 0; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_OUTPUT_SNMP].len == 2) { memcpy(&iface16, pptrs->f_data+tpl->tpl[NF9_OUTPUT_SNMP].off, 2); pdata->primitives.ifindex_out = ntohs(iface16); } else if (tpl->tpl[NF9_OUTPUT_SNMP].len == 4) { memcpy(&iface32, pptrs->f_data+tpl->tpl[NF9_OUTPUT_SNMP].off, 4); pdata->primitives.ifindex_out = ntohl(iface32); } break; case 8: switch(hdr->aggregation) { case 1: iface16 = ntohs(((struct struct_export_v8_1 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; case 4: iface16 = ntohs(((struct struct_export_v8_4 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; case 5: iface16 = ntohs(((struct struct_export_v8_5 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; case 6: iface16 = ntohs(((struct struct_export_v8_6 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; case 7: iface16 = ntohs(((struct struct_export_v8_7 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; case 8: iface16 = ntohs(((struct struct_export_v8_8 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; case 9: iface16 = ntohs(((struct struct_export_v8_9 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; case 10: iface16 = ntohs(((struct struct_export_v8_10 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; case 12: iface16 = ntohs(((struct struct_export_v8_12 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; case 13: iface16 = ntohs(((struct struct_export_v8_13 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; case 14: iface16 = ntohs(((struct struct_export_v8_14 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; default: pdata->primitives.ifindex_out = 0; break; } break; default: iface16 = ntohs(((struct struct_export_v5 *) pptrs->f_data)->output); pdata->primitives.ifindex_out = iface16; break; } } void NF_class_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; time_t fstime; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_APPLICATION_ID].len) { pdata->primitives.class = pptrs->class; pdata->cst.ba = 0; pdata->cst.pa = 0; pdata->cst.fa = 0; if (tpl->tpl[NF9_FIRST_SWITCHED].len && hdr->version == 9) { memcpy(&fstime, pptrs->f_data+tpl->tpl[NF9_FIRST_SWITCHED].off, tpl->tpl[NF9_FIRST_SWITCHED].len); pdata->cst.stamp.tv_sec = ntohl(((struct struct_header_v9 *) pptrs->f_header)->unix_secs)- ((ntohl(((struct struct_header_v9 *) pptrs->f_header)->SysUptime)-ntohl(fstime))/1000); } else pdata->cst.stamp.tv_sec = time(NULL); pdata->cst.stamp.tv_usec = 0; } break; default: break; } } void NF_id_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_CUST_TAG].len) { memcpy(&pdata->primitives.id, pptrs->f_data+tpl->tpl[NF9_CUST_TAG].off, tpl->tpl[NF9_CUST_TAG].len); pdata->primitives.id = ntohl(pdata->primitives.id); } break; default: break; } } void NF_id2_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; switch(hdr->version) { case 10: case 9: if (tpl->tpl[NF9_CUST_TAG2].len) { memcpy(&pdata->primitives.id2, pptrs->f_data+tpl->tpl[NF9_CUST_TAG2].off, tpl->tpl[NF9_CUST_TAG2].len); pdata->primitives.id2 = ntohl(pdata->primitives.id2); } break; default: break; } } void NF_counters_renormalize_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrs->f_status; struct xflow_status_entry_sampling *sentry = NULL; struct pkt_data *pdata = (struct pkt_data *) *data; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct struct_header_v5 *hdr5 = (struct struct_header_v5 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t srate = 0, is_sampled = 0; u_int16_t sampler_id = 0, t16 = 0; u_int32_t sample_pool = 0, t32 = 0; u_int8_t t8 = 0; if (pptrs->renormalized) return; switch (hdr->version) { case 10: case 9: if (tpl->tpl[NF9_FLOW_SAMPLER_ID].len) { if (tpl->tpl[NF9_FLOW_SAMPLER_ID].len == 1) { memcpy(&t8, pptrs->f_data+tpl->tpl[NF9_FLOW_SAMPLER_ID].off, 1); sampler_id = t8; } else if (tpl->tpl[NF9_FLOW_SAMPLER_ID].len == 2) { memcpy(&t16, pptrs->f_data+tpl->tpl[NF9_FLOW_SAMPLER_ID].off, 2); sampler_id = ntohs(t16); } if (entry) sentry = search_smp_id_status_table(entry->sampling, sampler_id, TRUE); if (sentry) { pdata->pkt_len = pdata->pkt_len * sentry->sample_pool; pdata->pkt_num = pdata->pkt_num * sentry->sample_pool; pptrs->renormalized = TRUE; } } /* SAMPLING_INTERVAL part of the NetFlow v9/IPFIX record seems to be reality, ie. FlowMon by Invea-Tech */ else if (tpl->tpl[NF9_SAMPLING_INTERVAL].len || tpl->tpl[NF9_FLOW_SAMPLER_INTERVAL].len) { if (tpl->tpl[NF9_SAMPLING_INTERVAL].len == 2) { memcpy(&t16, pptrs->f_data+tpl->tpl[NF9_SAMPLING_INTERVAL].off, 2); sample_pool = ntohs(t16); } else if (tpl->tpl[NF9_SAMPLING_INTERVAL].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_SAMPLING_INTERVAL].off, 4); sample_pool = ntohl(t32); } if (tpl->tpl[NF9_FLOW_SAMPLER_INTERVAL].len == 2) { memcpy(&t16, pptrs->f_data+tpl->tpl[NF9_FLOW_SAMPLER_INTERVAL].off, 2); sample_pool = ntohs(t16); } else if (tpl->tpl[NF9_FLOW_SAMPLER_INTERVAL].len == 4) { memcpy(&t32, pptrs->f_data+tpl->tpl[NF9_FLOW_SAMPLER_INTERVAL].off, 4); sample_pool = ntohl(t32); } pdata->pkt_len = pdata->pkt_len * sample_pool; pdata->pkt_num = pdata->pkt_num * sample_pool; pptrs->renormalized = TRUE; } break; case 5: hdr5 = (struct struct_header_v5 *) pptrs->f_header; is_sampled = ( ntohs(hdr5->sampling) & 0xC000 ); srate = ( ntohs(hdr5->sampling) & 0x3FFF ); /* XXX: checking srate value instead of is_sampled as Sampling Mode seems not to be a mandatory field. */ if (srate) { pdata->pkt_len = pdata->pkt_len * srate; pdata->pkt_num = pdata->pkt_num * srate; pptrs->renormalized = TRUE; } break; default: break; } } void NF_counters_map_renormalize_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->renormalized) return; NF_find_id((struct id_table *)pptrs->sampling_table, pptrs, &pptrs->st, NULL); if (pptrs->st) { pdata->pkt_len = pdata->pkt_len * pptrs->st; pdata->pkt_num = pdata->pkt_num * pptrs->st; pptrs->renormalized = TRUE; } } void bgp_ext_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_node *dst_ret = (struct bgp_node *) pptrs->bgp_dst; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; --pdata; /* Bringing back to original place */ if (src_ret && evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_as, NF_AS_BGP)) { info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr) { if (config.nfacctd_as & NF_AS_BGP) { if (chptr->aggregation & COUNT_SRC_AS && info->attr->aspath) { pdata->primitives.src_as = evaluate_last_asn(info->attr->aspath); if (!pdata->primitives.src_as && config.nfacctd_bgp_stdcomm_pattern_to_asn) { char tmp_stdcomms[MAX_BGP_STD_COMMS]; if (info->attr->community && info->attr->community->str) { evaluate_comm_patterns(tmp_stdcomms, info->attr->community->str, std_comm_patterns_to_asn, MAX_BGP_STD_COMMS); copy_stdcomm_to_asn(tmp_stdcomms, &pdata->primitives.src_as, TRUE); } } } } if (chptr->aggregation & COUNT_SRC_AS_PATH && config.nfacctd_bgp_src_as_path_type & BGP_SRC_PRIMITIVES_BGP && info->attr->aspath && info->attr->aspath->str) { strlcpy(pbgp->src_as_path, info->attr->aspath->str, MAX_BGP_ASPATH); if (strlen(info->attr->aspath->str) >= MAX_BGP_ASPATH) { pbgp->src_as_path[MAX_BGP_ASPATH-2] = '+'; pbgp->src_as_path[MAX_BGP_ASPATH-1] = '\0'; } if (config.nfacctd_bgp_aspath_radius) evaluate_bgp_aspath_radius(pbgp->src_as_path, MAX_BGP_ASPATH, config.nfacctd_bgp_aspath_radius); } if (chptr->aggregation & COUNT_SRC_STD_COMM && config.nfacctd_bgp_src_std_comm_type & BGP_SRC_PRIMITIVES_BGP && info->attr->community && info->attr->community->str) { if (config.nfacctd_bgp_stdcomm_pattern) evaluate_comm_patterns(pbgp->src_std_comms, info->attr->community->str, std_comm_patterns, MAX_BGP_STD_COMMS); else { strlcpy(pbgp->src_std_comms, info->attr->community->str, MAX_BGP_STD_COMMS); if (strlen(info->attr->community->str) >= MAX_BGP_STD_COMMS) { pbgp->src_std_comms[MAX_BGP_STD_COMMS-2] = '+'; pbgp->src_std_comms[MAX_BGP_STD_COMMS-1] = '\0'; } } } if (chptr->aggregation & COUNT_SRC_EXT_COMM && config.nfacctd_bgp_src_ext_comm_type & BGP_SRC_PRIMITIVES_BGP && info->attr->ecommunity && info->attr->ecommunity->str) { if (config.nfacctd_bgp_extcomm_pattern) evaluate_comm_patterns(pbgp->src_ext_comms, info->attr->ecommunity->str, ext_comm_patterns, MAX_BGP_EXT_COMMS); else { strlcpy(pbgp->src_ext_comms, info->attr->ecommunity->str, MAX_BGP_EXT_COMMS); if (strlen(info->attr->ecommunity->str) >= MAX_BGP_EXT_COMMS) { pbgp->src_ext_comms[MAX_BGP_EXT_COMMS-2] = '+'; pbgp->src_ext_comms[MAX_BGP_EXT_COMMS-1] = '\0'; } } } if (chptr->aggregation & COUNT_SRC_LOCAL_PREF && config.nfacctd_bgp_src_local_pref_type & BGP_SRC_PRIMITIVES_BGP) pbgp->src_local_pref = info->attr->local_pref; if (chptr->aggregation & COUNT_SRC_MED && config.nfacctd_bgp_src_med_type & BGP_SRC_PRIMITIVES_BGP) pbgp->src_med = info->attr->med; if (chptr->aggregation & COUNT_PEER_SRC_AS && config.nfacctd_bgp_peer_as_src_type & BGP_SRC_PRIMITIVES_BGP && info->attr->aspath && info->attr->aspath->str) { pbgp->peer_src_as = evaluate_first_asn(info->attr->aspath->str); if (!pbgp->peer_src_as && config.nfacctd_bgp_stdcomm_pattern_to_asn) { char tmp_stdcomms[MAX_BGP_STD_COMMS]; if (info->attr->community && info->attr->community->str) { evaluate_comm_patterns(tmp_stdcomms, info->attr->community->str, std_comm_patterns_to_asn, MAX_BGP_STD_COMMS); copy_stdcomm_to_asn(tmp_stdcomms, &pbgp->peer_src_as, FALSE); } } } } } if (dst_ret && evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_as, NF_AS_BGP)) { info = (struct bgp_info *) pptrs->bgp_dst_info; if (info && info->attr) { if (chptr->aggregation & COUNT_STD_COMM && info->attr->community && info->attr->community->str) { if (config.nfacctd_bgp_stdcomm_pattern) evaluate_comm_patterns(pbgp->std_comms, info->attr->community->str, std_comm_patterns, MAX_BGP_STD_COMMS); else { strlcpy(pbgp->std_comms, info->attr->community->str, MAX_BGP_STD_COMMS); if (strlen(info->attr->community->str) >= MAX_BGP_STD_COMMS) { pbgp->std_comms[MAX_BGP_STD_COMMS-2] = '+'; pbgp->std_comms[MAX_BGP_STD_COMMS-1] = '\0'; } } } if (chptr->aggregation & COUNT_EXT_COMM && info->attr->ecommunity && info->attr->ecommunity->str) { if (config.nfacctd_bgp_extcomm_pattern) evaluate_comm_patterns(pbgp->ext_comms, info->attr->ecommunity->str, ext_comm_patterns, MAX_BGP_EXT_COMMS); else { strlcpy(pbgp->ext_comms, info->attr->ecommunity->str, MAX_BGP_EXT_COMMS); if (strlen(info->attr->ecommunity->str) >= MAX_BGP_EXT_COMMS) { pbgp->ext_comms[MAX_BGP_EXT_COMMS-2] = '+'; pbgp->ext_comms[MAX_BGP_EXT_COMMS-1] = '\0'; } } } if (chptr->aggregation & COUNT_AS_PATH && info->attr->aspath && info->attr->aspath->str) { strlcpy(pbgp->as_path, info->attr->aspath->str, MAX_BGP_ASPATH); if (strlen(info->attr->aspath->str) >= MAX_BGP_ASPATH) { pbgp->as_path[MAX_BGP_ASPATH-2] = '+'; pbgp->as_path[MAX_BGP_ASPATH-1] = '\0'; } if (config.nfacctd_bgp_aspath_radius) evaluate_bgp_aspath_radius(pbgp->as_path, MAX_BGP_ASPATH, config.nfacctd_bgp_aspath_radius); } if (config.nfacctd_as & NF_AS_BGP) { if (chptr->aggregation & COUNT_DST_AS && info->attr->aspath) { pdata->primitives.dst_as = evaluate_last_asn(info->attr->aspath); if (!pdata->primitives.dst_as && config.nfacctd_bgp_stdcomm_pattern_to_asn) { char tmp_stdcomms[MAX_BGP_STD_COMMS]; if (info->attr->community && info->attr->community->str) { evaluate_comm_patterns(tmp_stdcomms, info->attr->community->str, std_comm_patterns_to_asn, MAX_BGP_STD_COMMS); copy_stdcomm_to_asn(tmp_stdcomms, &pdata->primitives.dst_as, TRUE); } } } } if (chptr->aggregation & COUNT_LOCAL_PREF) pbgp->local_pref = info->attr->local_pref; if (chptr->aggregation & COUNT_MED) pbgp->med = info->attr->med; if (chptr->aggregation & COUNT_PEER_DST_AS && info->attr->aspath && info->attr->aspath->str) { pbgp->peer_dst_as = evaluate_first_asn(info->attr->aspath->str); if (!pbgp->peer_dst_as && config.nfacctd_bgp_stdcomm_pattern_to_asn) { char tmp_stdcomms[MAX_BGP_STD_COMMS]; if (info->attr->community && info->attr->community->str) { evaluate_comm_patterns(tmp_stdcomms, info->attr->community->str, std_comm_patterns_to_asn, MAX_BGP_STD_COMMS); copy_stdcomm_to_asn(tmp_stdcomms, &pbgp->peer_dst_as, FALSE); } } } } if (info && info->extra) { if (chptr->aggregation & COUNT_MPLS_VPN_RD) memcpy(&pbgp->mpls_vpn_rd, &info->extra->rd, sizeof(rd_t)); } } } void sfprobe_bgp_ext_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_payload *payload = (struct pkt_payload *) *data; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_node *dst_ret = (struct bgp_node *) pptrs->bgp_dst; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; if (src_ret && evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_as, NF_AS_BGP)) { info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr) { if (config.nfacctd_as & NF_AS_BGP) { if (chptr->aggregation & COUNT_SRC_AS && info->attr->aspath) { if (!chptr->plugin->cfg.nfprobe_peer_as) payload->src_as = evaluate_last_asn(info->attr->aspath); else payload->src_as = evaluate_first_asn(info->attr->aspath->str); } } } } if (dst_ret && evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_as, NF_AS_BGP)) { info = (struct bgp_info *) pptrs->bgp_dst_info; if (info && info->attr) { if (config.nfacctd_as & NF_AS_BGP) { if (chptr->aggregation & COUNT_DST_AS && info->attr->aspath) { if (!chptr->plugin->cfg.nfprobe_peer_as) payload->dst_as = evaluate_last_asn(info->attr->aspath); else payload->dst_as = evaluate_first_asn(info->attr->aspath->str); } } } } } void nfprobe_bgp_ext_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct pkt_extras *pextras = (struct pkt_extras *) ++pdata; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_node *dst_ret = (struct bgp_node *) pptrs->bgp_dst; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; --pdata; /* Bringing back to original place */ if (src_ret && evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_as, NF_AS_BGP)) { info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr) { if (config.nfacctd_as & NF_AS_BGP) { if (chptr->aggregation & COUNT_SRC_AS && info->attr->aspath) { if (!chptr->plugin->cfg.nfprobe_peer_as) pdata->primitives.src_as = evaluate_last_asn(info->attr->aspath); else pdata->primitives.src_as = evaluate_first_asn(info->attr->aspath->str); } } } } if (dst_ret && evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_as, NF_AS_BGP)) { info = (struct bgp_info *) pptrs->bgp_dst_info; if (info && info->attr) { if (config.nfacctd_as & NF_AS_BGP) { if (chptr->aggregation & COUNT_DST_AS && info->attr->aspath) { if (!chptr->plugin->cfg.nfprobe_peer_as) pdata->primitives.dst_as = evaluate_last_asn(info->attr->aspath); else pdata->primitives.dst_as = evaluate_first_asn(info->attr->aspath->str); } } } } } void bgp_peer_src_as_frommap_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; --pdata; /* Bringing back to original place */ pbgp->peer_src_as = pptrs->bpas; /* XXX: extra check: was src_as written by copy_stdcomm_to_asn() ? */ if (!pbgp->peer_src_as && config.nfacctd_bgp_stdcomm_pattern_to_asn) { if (src_ret) { char tmp_stdcomms[MAX_BGP_STD_COMMS]; info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr && info->attr->community && info->attr->community->str) { evaluate_comm_patterns(tmp_stdcomms, info->attr->community->str, std_comm_patterns_to_asn, MAX_BGP_STD_COMMS); copy_stdcomm_to_asn(tmp_stdcomms, &pbgp->peer_src_as, FALSE); } } } } void bgp_src_local_pref_frommap_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; --pdata; /* Bringing back to original place */ pbgp->src_local_pref = pptrs->blp; } void bgp_src_med_frommap_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; --pdata; /* Bringing back to original place */ pbgp->src_med = pptrs->bmed; } #if defined (HAVE_L2) void SF_src_mac_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; memcpy(pdata->primitives.eth_shost, sample->eth_src, ETH_ADDR_LEN); } void SF_dst_mac_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; memcpy(pdata->primitives.eth_dhost, sample->eth_dst, ETH_ADDR_LEN); } void SF_vlan_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->primitives.vlan_id = sample->in_vlan; if (!pdata->primitives.vlan_id) pdata->primitives.vlan_id = sample->out_vlan; } void SF_cos_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->primitives.cos = sample->in_priority; if (!pdata->primitives.cos) pdata->primitives.cos = sample->out_priority; } void SF_etype_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->primitives.etype = sample->eth_type; } #endif void SF_src_host_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; SFLAddress *addr = &sample->ipsrc; u_int8_t src_mask = sample->srcMask; /* check network-related primitives against fallback scenarios */ if (chptr->aggregation & (COUNT_SRC_NET|COUNT_SUM_NET) && !evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_net, NF_NET_KEEP)) return; if (sample->gotIPV4) { pdata->primitives.src_ip.address.ipv4.s_addr = sample->dcd_srcIP.s_addr; pdata->primitives.src_ip.family = AF_INET; } #if defined ENABLE_IPV6 else if (sample->gotIPV6) { memcpy(&pdata->primitives.src_ip.address.ipv6, &addr->address.ip_v6, IP6AddrSz); pdata->primitives.src_ip.family = AF_INET6; } #endif if ((chptr->aggregation & (COUNT_SRC_NET|COUNT_SUM_NET)) && chptr->plugin->cfg.nfacctd_net & NF_NET_KEEP) { u_int32_t maskbits[4], addrh[4]; u_int8_t j; memset(maskbits, 0, sizeof(maskbits)); for (j = 0; j < 4 && src_mask >= 32; j++, src_mask -= 32) maskbits[j] = 0xffffffffU; if (j < 4 && src_mask) maskbits[j] = ~(0xffffffffU >> src_mask); if (pdata->primitives.src_ip.family == AF_INET) { addrh[0] = ntohl(pdata->primitives.src_ip.address.ipv4.s_addr); addrh[0] &= maskbits[0]; pdata->primitives.src_ip.address.ipv4.s_addr = htonl(addrh[0]); } #if defined ENABLE_IPV6 else if (pdata->primitives.src_ip.family == AF_INET6) { memcpy(&addrh, (void *) pm_ntohl6(&pdata->primitives.src_ip.address.ipv6), IP6AddrSz); for (j = 0; j < 4; j++) addrh[j] &= maskbits[j]; memcpy(&pdata->primitives.src_ip.address.ipv6, (void *) pm_htonl6(addrh), IP6AddrSz); } #endif } } void SF_dst_host_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; SFLAddress *addr = &sample->ipdst; u_int8_t dst_mask = sample->dstMask; /* check network-related primitives against fallback scenarios */ if (chptr->aggregation & (COUNT_DST_NET|COUNT_SUM_NET) && !evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_KEEP)) return; if (sample->gotIPV4) { pdata->primitives.dst_ip.address.ipv4.s_addr = sample->dcd_dstIP.s_addr; pdata->primitives.dst_ip.family = AF_INET; } #if defined ENABLE_IPV6 else if (sample->gotIPV6) { memcpy(&pdata->primitives.dst_ip.address.ipv6, &addr->address.ip_v6, IP6AddrSz); pdata->primitives.dst_ip.family = AF_INET6; } #endif if ((chptr->aggregation & (COUNT_DST_NET|COUNT_SUM_NET)) && chptr->plugin->cfg.nfacctd_net & NF_NET_KEEP) { u_int32_t maskbits[4], addrh[4]; u_int8_t j; memset(maskbits, 0, sizeof(maskbits)); for (j = 0; j < 4 && dst_mask >= 32; j++, dst_mask -= 32) maskbits[j] = 0xffffffffU; if (j < 4 && dst_mask) maskbits[j] = ~(0xffffffffU >> dst_mask); if (pdata->primitives.dst_ip.family == AF_INET) { addrh[0] = ntohl(pdata->primitives.dst_ip.address.ipv4.s_addr); addrh[0] &= maskbits[0]; pdata->primitives.dst_ip.address.ipv4.s_addr = htonl(addrh[0]); } #if defined ENABLE_IPV6 else if (pdata->primitives.dst_ip.family == AF_INET6) { memcpy(&addrh, (void *) pm_ntohl6(&pdata->primitives.dst_ip.address.ipv6), IP6AddrSz); for (j = 0; j < 4; j++) addrh[j] &= maskbits[j]; memcpy(&pdata->primitives.dst_ip.address.ipv6, (void *) pm_htonl6(addrh), IP6AddrSz); } #endif } } void SF_src_nmask_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_net, NF_NET_KEEP)) return; pdata->primitives.src_nmask = sample->srcMask; } void SF_dst_nmask_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_KEEP)) return; pdata->primitives.dst_nmask = sample->dstMask; } void SF_src_port_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; if (sample->dcd_ipProtocol == IPPROTO_UDP || sample->dcd_ipProtocol == IPPROTO_TCP) pdata->primitives.src_port = sample->dcd_sport; else pdata->primitives.src_port = 0; } void SF_dst_port_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; if (sample->dcd_ipProtocol == IPPROTO_UDP || sample->dcd_ipProtocol == IPPROTO_TCP) pdata->primitives.dst_port = sample->dcd_dport; else pdata->primitives.dst_port = 0; } void SF_ip_tos_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->primitives.tos = sample->dcd_ipTos; } void SF_ip_proto_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->primitives.proto = sample->dcd_ipProtocol; } void SF_tcp_flags_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->tcp_flags = sample->dcd_tcpFlags; } void SF_counters_new_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->pkt_len = sample->sampledPacketSize; pdata->pkt_num = 1; pdata->time_start.tv_sec = 0; pdata->time_start.tv_usec = 0; pdata->time_end.tv_sec = 0; pdata->time_end.tv_usec = 0; /* XXX: fragment handling */ } void SF_counters_renormalize_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrs->f_status; struct xflow_status_entry_sampling *sentry = NULL; struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; u_int32_t eff_srate = 0; if (pptrs->renormalized) return; if (entry) sentry = search_smp_if_status_table(entry->sampling, (sample->ds_class << 24 | sample->ds_index)); if (sentry) { /* flow sequence number is strictly increasing; however we need a) to avoid a division-by-zero by checking the last value and the new one and b) to deal with out-of-order datagrams */ if (sample->samplesGenerated > sentry->seqno && sample->samplePool > sentry->sample_pool) { eff_srate = (sample->samplePool-sentry->sample_pool) / (sample->samplesGenerated-sentry->seqno); pdata->pkt_len = pdata->pkt_len * eff_srate; pdata->pkt_num = pdata->pkt_num * eff_srate; sentry->sample_pool = sample->samplePool; sentry->seqno = sample->samplesGenerated; return; } /* Let's handle long positive/negative jumps as resets */ else if (MAX(sample->samplesGenerated, sentry->seqno) > (MIN(sample->samplesGenerated, sentry->seqno)+XFLOW_RESET_BOUNDARY)) { sentry->sample_pool = sample->samplePool; sentry->seqno = sample->samplesGenerated; } } else { if (entry) sentry = create_smp_entry_status_table(entry); if (sentry) { sentry->interface = (sample->ds_class << 24 | sample->ds_index); sentry->sample_pool = sample->samplePool; sentry->seqno = sample->samplesGenerated; } } pdata->pkt_len = pdata->pkt_len * sample->meanSkipCount; pdata->pkt_num = pdata->pkt_num * sample->meanSkipCount; pptrs->renormalized = TRUE; } void SF_counters_map_renormalize_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; if (pptrs->renormalized) return; SF_find_id((struct id_table *)pptrs->sampling_table, pptrs, &pptrs->st, NULL); if (pptrs->st) { pdata->pkt_len = pdata->pkt_len * pptrs->st; pdata->pkt_num = pdata->pkt_num * pptrs->st; pptrs->renormalized = TRUE; } } void SF_src_as_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; pdata->primitives.src_as = sample->src_as; } void SF_dst_as_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; pdata->primitives.dst_as = sample->dst_as; } void SF_as_path_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; if (sample->dst_as_path_len) { strlcpy(pbgp->as_path, sample->dst_as_path, MAX_BGP_ASPATH); if (config.nfacctd_bgp_aspath_radius) evaluate_bgp_aspath_radius(pbgp->as_path, MAX_BGP_ASPATH, config.nfacctd_bgp_aspath_radius); } } void SF_peer_src_as_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, FALSE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; pbgp->peer_src_as = sample->src_peer_as; } void SF_peer_dst_as_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; pbgp->peer_dst_as = sample->dst_peer_as; } void SF_local_pref_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; pbgp->local_pref = sample->localpref; } void SF_std_comms_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_as, NF_AS_KEEP)) return; if (sample->communities_len) strlcpy(pbgp->std_comms, sample->comms, MAX_BGP_STD_COMMS); } void SF_peer_src_ip_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; SFSample *sample = (SFSample *) pptrs->f_data; --pdata; /* Bringing back to original place */ if (sample->agent_addr.type == SFLADDRESSTYPE_IP_V4) { pbgp->peer_src_ip.address.ipv4.s_addr = sample->agent_addr.address.ip_v4.s_addr; pbgp->peer_src_ip.family = AF_INET; } #if defined ENABLE_IPV6 else if (sample->agent_addr.type == SFLADDRESSTYPE_IP_V6) { memcpy(&pbgp->peer_src_ip.address.ipv6, &sample->agent_addr.address.ip_v6, IP6AddrSz); pbgp->peer_src_ip.family = AF_INET6; } #endif } void SF_peer_dst_ip_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; SFSample *sample = (SFSample *) pptrs->f_data; --pdata; /* Bringing back to original place */ /* check network-related primitives against fallback scenarios */ if (!evaluate_lm_method(pptrs, TRUE, chptr->plugin->cfg.nfacctd_net, NF_NET_KEEP)) return; if (sample->bgp_nextHop.type == SFLADDRESSTYPE_IP_V4) { pbgp->peer_dst_ip.address.ipv4.s_addr = sample->bgp_nextHop.address.ip_v4.s_addr; pbgp->peer_dst_ip.family = AF_INET; } #if defined ENABLE_IPV6 else if (sample->bgp_nextHop.type == SFLADDRESSTYPE_IP_V6) { memcpy(&pbgp->peer_dst_ip.address.ipv6, &sample->bgp_nextHop.address.ip_v6, IP6AddrSz); pbgp->peer_dst_ip.family = AF_INET6; } #endif } void SF_in_iface_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->primitives.ifindex_in = sample->inputPort; } void SF_out_iface_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->primitives.ifindex_out = sample->outputPort; } void SF_class_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->primitives.class = sample->class; pdata->cst.ba = 0; pdata->cst.pa = 0; pdata->cst.fa = 0; pdata->cst.stamp.tv_sec = time(NULL); /* XXX */ pdata->cst.stamp.tv_usec = 0; } void SF_id_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->primitives.id = sample->tag; } void SF_id2_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; SFSample *sample = (SFSample *) pptrs->f_data; pdata->primitives.id2 = sample->tag2; } void sampling_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; pm_counter_t sample_pool = 0; evaluate_sampling(&chptr->s, &pdata->pkt_len, &pdata->pkt_num, &sample_pool); } void sfprobe_sampling_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_payload *payload = (struct pkt_payload *) *data; evaluate_sampling(&chptr->s, &payload->pkt_len, &payload->pkt_num, &payload->sample_pool); } void SF_bgp_peer_src_as_fromstd_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; pbgp->peer_src_as = 0; // XXX: fill this in } void SF_bgp_peer_src_as_fromext_handler(struct channels_list_entry *chptr, struct packet_ptrs *pptrs, char **data) { struct pkt_data *pdata = (struct pkt_data *) *data; struct pkt_bgp_primitives *pbgp = (struct pkt_bgp_primitives *) ++pdata; pbgp->peer_src_as = 0; // XXX: fill this in } /* srcdst: 0 == src, 1 == dst */ int evaluate_lm_method(struct packet_ptrs *pptrs, u_int8_t srcdst, u_int32_t bitmap, u_int32_t method) { /* src */ if (srcdst == FALSE) { if (pptrs->lm_method_src == method || !(bitmap & NF_NET_FALLBACK)) return TRUE; else return FALSE; } /* dst */ else if (srcdst == TRUE) { if (pptrs->lm_method_dst == method || !(bitmap & NF_NET_FALLBACK)) return TRUE; else return FALSE; } } pmacct-0.14.0/src/log.c0000644000175000017500000000363111652010543013565 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* includes */ #include "pmacct.h" /* functions */ void Log(short int level, char *msg, ...) { va_list ap; char syslog_string[LOGSTRLEN]; if ((level == LOG_DEBUG) && (!config.debug && !debug)) return; if (!config.syslog && !config.logfile_fd) { va_start(ap, msg); vprintf(msg, ap); va_end(ap); fflush(stdout); } else { va_start(ap, msg); vsnprintf(syslog_string, LOGSTRLEN, msg, ap); va_end(ap); if (config.syslog) { sbrk(LOGSTRLEN); syslog(level, syslog_string); sbrk(-LOGSTRLEN); } if (config.logfile_fd) { char timebuf[SRVBUFLEN]; struct tm *tmnow; time_t now; now = time(NULL); tmnow = localtime(&now); strftime(timebuf, SRVBUFLEN, "%b %d %H:%M:%S", tmnow); fprintf(config.logfile_fd, "%s %s", timebuf, syslog_string); fflush(config.logfile_fd); } } } int parse_log_facility(const char *facility) { int i; for (i = 0; facility_map[i].num != -1; i++) { if (!strcmp(facility, facility_map[i].string)) return facility_map[i].num; } return ERR; } pmacct-0.14.0/src/sqlite3_plugin.c0000644000175000017500000005734511652204562015767 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #define __SQLITE3_PLUGIN_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "sql_common.h" #include "sqlite3_plugin.h" #include "sql_common_m.c" /* Functions */ void sqlite3_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_data *data; struct ports_table pt; struct pollfd pfd; struct insert_data idata; struct timezone tz; time_t refresh_deadline; int timeout; int ret, num; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; struct pkt_bgp_primitives *pbgp; char *dataptr; unsigned char *rgptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; memcpy(&config, cfgptr, sizeof(struct configuration)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "SQLite3 Plugin", config.name); memset(&idata, 0, sizeof(idata)); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile); } sql_set_signals(); sql_init_default_values(); SQLI_init_default_values(&idata); SQLI_set_callbacks(&sqlfunc_cbr); sql_set_insert_func(); /* some LOCAL initialization AFTER setting some default values */ reload_map = FALSE; idata.now = time(NULL); refresh_deadline = idata.now; sql_init_maps(&nt, &nc, &pt); sql_init_global_buffers(); sql_init_pipe(&pfd, pipe_fd); sql_init_historical_acct(idata.now, &idata); sql_init_triggers(idata.now, &idata); sql_init_refresh_deadline(&refresh_deadline); /* setting number of entries in _protocols structure */ while (_protocols[protocols_number].number != -1) protocols_number++; /* building up static SQL clauses */ idata.num_primitives = SQLI_compose_static_queries(); glob_num_primitives = idata.num_primitives; /* handling purge preprocessor */ set_preprocess_funcs(config.sql_preprocess, &prep); /* setting up environment variables */ SQL_SetENV(); sql_link_backend_descriptors(&bed, &p, &b); /* plugin main loop */ for(;;) { poll_again: status->wakeup = TRUE; sql_calc_refresh_timeout(refresh_deadline, idata.now, &timeout); ret = poll(&pfd, 1, timeout); if (ret < 0) goto poll_again; idata.now = time(NULL); if (config.sql_history) { while (idata.now > (idata.basetime + idata.timeslot)) { time_t saved_basetime = idata.basetime; idata.basetime += idata.timeslot; if (config.sql_history == COUNT_MONTHLY) idata.timeslot = calc_monthly_timeslot(idata.basetime, config.sql_history_howmany, ADD); glob_basetime = idata.basetime; idata.new_basetime = saved_basetime; glob_new_basetime = saved_basetime; } } switch (ret) { case 0: /* timeout */ if (qq_ptr) sql_cache_flush(queries_queue, qq_ptr, &idata, FALSE); switch (fork()) { case 0: /* Child */ /* we have to ignore signals to avoid loops: because we are already forked */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); pm_setproctitle("%s [%s]", "SQLite3 Plugin -- DB Writer", config.name); if (qq_ptr && sql_writers.flags != CHLD_ALERT) { if (sql_writers.flags == CHLD_WARNING) sql_db_fail(&p); (*sqlfunc_cbr.connect)(&p, NULL); (*sqlfunc_cbr.purge)(queries_queue, qq_ptr, &idata); (*sqlfunc_cbr.close)(&bed); } if (config.sql_trigger_exec) { if (idata.now > idata.triggertime) sql_trigger_exec(config.sql_trigger_exec); } exit(0); default: /* Parent */ if (pqq_ptr) sql_cache_flush_pending(pending_queries_queue, pqq_ptr, &idata); gettimeofday(&idata.flushtime, &tz); while (idata.now > refresh_deadline) refresh_deadline += config.sql_refresh_time; while (idata.now > idata.triggertime && idata.t_timeslot > 0) { idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } idata.new_basetime = FALSE; glob_new_basetime = FALSE; qq_ptr = pqq_ptr; memcpy(queries_queue, pending_queries_queue, qq_ptr*sizeof(struct db_cache *)); if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } break; } break; default: /* we received data */ read_data: if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; idata.now = time(NULL); } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; goto poll_again; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%u'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%u'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); } seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); if ((rg->ptr+bufsz) >= rg->end) rg->ptr = rg->base; else rg->ptr += bufsz; /* lazy sql refresh handling */ if (idata.now > refresh_deadline) { if (qq_ptr) sql_cache_flush(queries_queue, qq_ptr, &idata, FALSE); switch (fork()) { case 0: /* Child */ /* we have to ignore signals to avoid loops: because we are already forked */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); pm_setproctitle("%s [%s]", "SQLite3 Plugin -- DB Writer", config.name); if (qq_ptr && sql_writers.flags != CHLD_ALERT) { if (sql_writers.flags == CHLD_WARNING) sql_db_fail(&p); (*sqlfunc_cbr.connect)(&p, NULL); (*sqlfunc_cbr.purge)(queries_queue, qq_ptr, &idata); (*sqlfunc_cbr.close)(&bed); } if (config.sql_trigger_exec) { if (idata.now > idata.triggertime) sql_trigger_exec(config.sql_trigger_exec); } exit(0); default: /* Parent */ if (pqq_ptr) sql_cache_flush_pending(pending_queries_queue, pqq_ptr, &idata); gettimeofday(&idata.flushtime, &tz); while (idata.now > refresh_deadline) refresh_deadline += config.sql_refresh_time; while (idata.now > idata.triggertime && idata.t_timeslot > 0) { idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } idata.new_basetime = FALSE; glob_new_basetime = FALSE; qq_ptr = pqq_ptr; memcpy(queries_queue, pending_queries_queue, qq_ptr*sizeof(struct db_cache *)); if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } break; } } else { if (config.sql_trigger_exec) { while (idata.now > idata.triggertime && idata.t_timeslot > 0) { sql_trigger_exec(config.sql_trigger_exec); idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } } } data = (struct pkt_data *) (pipebuf+sizeof(struct ch_buf_hdr)); while (((struct ch_buf_hdr *)pipebuf)->num) { for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &data->primitives); if (config.ports_file) { if (!pt.table[data->primitives.src_port]) data->primitives.src_port = 0; if (!pt.table[data->primitives.dst_port]) data->primitives.dst_port = 0; } if (PbgpSz) pbgp = (struct pkt_bgp_primitives *) ((u_char *)data+PdataSz); else pbgp = NULL; (*insert_func)(data, pbgp, &idata); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) data; dataptr += PdataSz + PbgpSz; data = (struct pkt_data *) dataptr; } } goto read_data; } } } int SQLI_cache_dbop(struct DBdesc *db, struct db_cache *cache_elem, struct insert_data *idata) { char *ptr_values, *ptr_where, *ptr_mv, *ptr_set; int num=0, ret=0, have_flows=0, len=0; if (idata->mv.last_queue_elem) { ret = sqlite3_exec(db->desc, multi_values_buffer, NULL, NULL, NULL); Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d INSERT statements sent to the SQLite database.\n", config.name, config.type, idata->mv.buffer_elem_num); if (ret) goto signal_error; idata->iqn++; idata->mv.buffer_elem_num = FALSE; idata->mv.buffer_offset = 0; return FALSE; } if (config.what_to_count & COUNT_FLOWS) have_flows = TRUE; /* constructing sql query */ ptr_where = where_clause; ptr_values = values_clause; ptr_set = set_clause; memset(where_clause, 0, sizeof(where_clause)); memset(values_clause, 0, sizeof(values_clause)); memset(set_clause, 0, sizeof(set_clause)); for (num = 0; num < idata->num_primitives; num++) (*where[num].handler)(cache_elem, idata, num, &ptr_values, &ptr_where); for (num = 0; set[num].type; num++) (*set[num].handler)(cache_elem, idata, num, &ptr_set, NULL); /* sending UPDATE query */ if (!config.sql_dont_try_update) { strncpy(sql_data, update_clause, SPACELEFT(sql_data)); strncat(sql_data, set_clause, SPACELEFT(sql_data)); strncat(sql_data, where_clause, SPACELEFT(sql_data)); ret = sqlite3_exec(db->desc, sql_data, NULL, NULL, NULL); if (ret) goto signal_error; } if (config.sql_dont_try_update || (sqlite3_changes(db->desc) == 0)) { /* UPDATE failed, trying with an INSERT query */ #if defined HAVE_64BIT_COUNTERS if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), ", %llu, %llu, %llu)", cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), ", %llu, %llu)", cache_elem->packet_counter, cache_elem->bytes_counter); #else if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), ", %lu, %lu, %lu)", cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), ", %lu, %lu)", cache_elem->packet_counter, cache_elem->bytes_counter); #endif strncpy(sql_data, insert_clause, sizeof(sql_data)); strncat(sql_data, values_clause, SPACELEFT(sql_data)); if (config.sql_multi_values) { multi_values_handling: len = config.sql_multi_values-idata->mv.buffer_offset; if (strlen(values_clause) < len) { if (idata->mv.buffer_elem_num) { strcpy(multi_values_buffer+idata->mv.buffer_offset, "; "); idata->mv.buffer_offset++; idata->mv.buffer_offset++; } ptr_mv = multi_values_buffer+idata->mv.buffer_offset; strcpy(multi_values_buffer+idata->mv.buffer_offset, sql_data); idata->mv.buffer_offset += strlen(ptr_mv); idata->mv.buffer_elem_num++; } else { if (idata->mv.buffer_elem_num) { ret = sqlite3_exec(db->desc, multi_values_buffer, NULL, NULL, NULL); Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d INSERT statements sent to the SQLite database.\n", config.name, config.type, idata->mv.buffer_elem_num); if (ret) goto signal_error; idata->iqn++; idata->mv.buffer_elem_num = FALSE; idata->mv.head_buffer_elem = FALSE; idata->mv.buffer_offset = 0; goto multi_values_handling; } else { Log(LOG_ERR, "ERROR ( %s/%s ): 'sql_multi_values' is too small (%d). Try with a larger value.\n", config.name, config.type, config.sql_multi_values); exit_plugin(1); } } } else { ret = sqlite3_exec(db->desc, sql_data, NULL, NULL, NULL); Log(LOG_DEBUG, "( %s/%s ): %s\n\n", config.name, config.type, sql_data); if (ret) goto signal_error; idata->iqn++; } } else { Log(LOG_DEBUG, "( %s/%s ): %s\n\n", config.name, config.type, sql_data); idata->uqn++; } idata->een++; // cache_elem->valid = FALSE; /* committed */ return ret; signal_error: if (!idata->mv.buffer_elem_num) Log(LOG_DEBUG, "DEBUG ( %s/%s ): FAILED query follows:\n%s\n", config.name, config.type, sql_data); else { if (!idata->recover || db->type != BE_TYPE_PRIMARY) { /* DB failure: we will rewind the multi-values buffer */ idata->current_queue_elem = idata->mv.head_buffer_elem; idata->mv.buffer_elem_num = 0; } } SQLI_get_errmsg(db); if (db->errmsg) Log(LOG_ERR, "ERROR ( %s/%s ): %s\n\n", config.name, config.type, db->errmsg); return ret; } void SQLI_cache_purge(struct db_cache *queue[], int index, struct insert_data *idata) { struct db_cache *LastElemCommitted = NULL; time_t start; int j, stop, ret; for (j = 0, stop = 0; (!stop) && preprocess_funcs[j]; j++) stop = preprocess_funcs[j](queue, &index, j); if (config.what_to_count & COUNT_CLASS) sql_invalidate_shadow_entries(queue, &index); idata->ten = index; if (config.debug) { Log(LOG_DEBUG, "( %s/%s ) *** Purging cache - START ***\n", config.name, config.type); start = time(NULL); } /* We check for variable substitution in SQL table */ if (idata->dyn_table) { char tmpbuf[LONGLONGSRVBUFLEN]; time_t stamp = idata->new_basetime ? idata->new_basetime : idata->basetime; strftime_same(insert_clause, LONGSRVBUFLEN, tmpbuf, &stamp); strftime_same(update_clause, LONGSRVBUFLEN, tmpbuf, &stamp); strftime_same(lock_clause, LONGSRVBUFLEN, tmpbuf, &stamp); if (config.sql_table_schema) sql_create_table(bed.p, &stamp); } // strncat(update_clause, set_clause, SPACELEFT(update_clause)); (*sqlfunc_cbr.lock)(bed.p); for (idata->current_queue_elem = 0; idata->current_queue_elem < index; idata->current_queue_elem++) { if (queue[idata->current_queue_elem]->valid) sql_query(&bed, queue[idata->current_queue_elem], idata); if (queue[idata->current_queue_elem]->valid == SQL_CACHE_COMMITTED) LastElemCommitted = queue[idata->current_queue_elem]; } /* multi-value INSERT query: wrap-up */ if (idata->mv.buffer_elem_num) { idata->mv.last_queue_elem = TRUE; sql_query(&bed, LastElemCommitted, idata); } /* rewinding stuff */ (*sqlfunc_cbr.unlock)(&bed); if (b.fail) Log(LOG_ALERT, "ALERT ( %s/%s ): recovery for SQLite3 daemon failed.\n", config.name, config.type); if (config.debug) { idata->elap_time = time(NULL)-start; Log(LOG_DEBUG, "( %s/%s ) *** Purging cache - END (QN: %u, ET: %u) ***\n", config.name, config.type, idata->qn, idata->elap_time); } if (config.sql_trigger_exec) { if (!config.debug) idata->elap_time = time(NULL)-start; SQL_SetENV_child(idata); } } int SQLI_evaluate_history(int primitive) { if (config.sql_history || config.nfacctd_sql_log) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (!config.sql_history_since_epoch) strncat(where[primitive].string, "DATETIME(%u, 'unixepoch', 'localtime') = ", SPACELEFT(where[primitive].string)); else strncat(where[primitive].string, "%u = ", SPACELEFT(where[primitive].string)); strncat(where[primitive].string, "stamp_inserted", SPACELEFT(where[primitive].string)); strncat(insert_clause, "stamp_updated, stamp_inserted", SPACELEFT(insert_clause)); if (!config.sql_history_since_epoch) strncat(values[primitive].string, "DATETIME(%u, 'unixepoch', 'localtime'), DATETIME(%u, 'unixepoch', 'localtime')", SPACELEFT(values[primitive].string)); else strncat(values[primitive].string, "%u, %u", SPACELEFT(values[primitive].string)); where[primitive].type = values[primitive].type = TIMESTAMP; values[primitive].handler = where[primitive].handler = count_timestamp_handler; primitive++; } return primitive; } int SQLI_compose_static_queries() { int primitives=0, set_primitives=0, have_flows=0; if (config.what_to_count & COUNT_FLOWS || (config.sql_table_version >= 4 && config.sql_table_version < SQL_TABLE_VERSION_BGP && !config.sql_optimize_clauses)) { config.what_to_count |= COUNT_FLOWS; have_flows = TRUE; if ((config.sql_table_version < 4 || config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !config.sql_optimize_clauses) { Log(LOG_ERR, "ERROR ( %s/%s ): The accounting of flows requires SQL table v4. Exiting.\n", config.name, config.type); exit_plugin(1); } } /* "INSERT INTO ... VALUES ... " and "... WHERE ..." stuff */ strncpy(where[primitives].string, " WHERE ", sizeof(where[primitives].string)); snprintf(insert_clause, sizeof(insert_clause), "INSERT INTO %s (", config.sql_table); strncpy(values[primitives].string, " VALUES (", sizeof(values[primitives].string)); primitives = SQLI_evaluate_history(primitives); primitives = sql_evaluate_primitives(primitives); strncat(insert_clause, ", packets, bytes", SPACELEFT(insert_clause)); if (have_flows) strncat(insert_clause, ", flows", SPACELEFT(insert_clause)); strncat(insert_clause, ")", SPACELEFT(insert_clause)); /* "LOCK ..." stuff */ if (config.sql_locking_style) Log(LOG_WARNING, "WARN ( %s/%s ): sql_locking_style is not supported. Ignored.\n", config.name, config.type); snprintf(lock_clause, sizeof(lock_clause), "BEGIN", config.sql_table); strncpy(unlock_clause, "COMMIT", sizeof(unlock_clause)); /* "UPDATE ... SET ..." stuff */ snprintf(update_clause, sizeof(update_clause), "UPDATE %s ", config.sql_table); set_primitives = sql_compose_static_set(have_flows); if (config.sql_history || config.nfacctd_sql_log) { if (!config.nfacctd_sql_log) { if (!config.sql_history_since_epoch) { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=DATETIME('now', 'localtime')", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_noop_setclause_handler; set_primitives++; } else { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=STRFTIME('%%s', 'now')", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_noop_setclause_handler; set_primitives++; } } else { if (!config.sql_history_since_epoch) { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=DATETIME(%u, 'unixepoch', 'localtime')", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_timestamp_setclause_handler; set_primitives++; } else { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=%u", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_timestamp_setclause_handler; set_primitives++; } } } return primitives; } void SQLI_Lock(struct DBdesc *db) { if (!db->fail) { if (sqlite3_exec(db->desc, lock_clause, NULL, NULL, NULL)) { SQLI_get_errmsg(db); sql_db_errmsg(db); sql_db_fail(db); } } } void SQLI_Unlock(struct BE_descs *bed) { if (bed->p->connected) sqlite3_exec(bed->p->desc, unlock_clause, NULL, NULL, NULL); if (bed->b->connected) sqlite3_exec(bed->b->desc, unlock_clause, NULL, NULL, NULL); } void SQLI_DB_Connect(struct DBdesc *db, char *host) { if (!db->fail) { if (sqlite3_open(db->filename, (sqlite3 **)&db->desc)) { sql_db_fail(db); SQLI_get_errmsg(db); sql_db_errmsg(db); } else sql_db_ok(db); } } void SQLI_DB_Close(struct BE_descs *bed) { if (bed->p->connected) sqlite3_close(bed->p->desc); if (bed->b->connected) sqlite3_close(bed->b->desc); } void SQLI_create_dyn_table(struct DBdesc *db, char *buf) { if (!db->fail) { if (sqlite3_exec(db->desc, buf, NULL, NULL, NULL)) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): FAILED query follows:\n%s\n", config.name, config.type, buf); SQLI_get_errmsg(db); sql_db_errmsg(db); } } } void SQLI_get_errmsg(struct DBdesc *db) { db->errmsg = (char *) sqlite3_errmsg(db->desc); } void SQLI_create_backend(struct DBdesc *db) { if (db->type == BE_TYPE_PRIMARY) db->filename = config.sql_db; if (db->type == BE_TYPE_BACKUP) db->filename = config.sql_backup_host; } void SQLI_set_callbacks(struct sqlfunc_cb_registry *cbr) { memset(cbr, 0, sizeof(struct sqlfunc_cb_registry)); cbr->connect = SQLI_DB_Connect; cbr->close = SQLI_DB_Close; cbr->lock = SQLI_Lock; cbr->unlock = SQLI_Unlock; cbr->op = SQLI_cache_dbop; cbr->create_table = SQLI_create_dyn_table; cbr->purge = SQLI_cache_purge; cbr->create_backend = SQLI_create_backend; } void SQLI_init_default_values(struct insert_data *idata) { config.sql_recovery_logfile = FALSE; /* not supported */ /* Linking database parameters */ if (!config.sql_db) config.sql_db = sqlite3_db; if (!config.sql_table) { if (config.sql_table_version == (SQL_TABLE_VERSION_BGP+1)) config.sql_table = sqlite3_table_bgp; else if (config.sql_table_version == 8) config.sql_table = sqlite3_table_v8; else if (config.sql_table_version == 7) config.sql_table = sqlite3_table_v7; else if (config.sql_table_version == 6) config.sql_table = sqlite3_table_v6; else if (config.sql_table_version == 5) config.sql_table = sqlite3_table_v5; else if (config.sql_table_version == 4) config.sql_table = sqlite3_table_v4; else if (config.sql_table_version == 3) config.sql_table = sqlite3_table_v3; else if (config.sql_table_version == 2) config.sql_table = sqlite3_table_v2; else config.sql_table = sqlite3_table; } if (strchr(config.sql_table, '%')) idata->dyn_table = TRUE; glob_dyn_table = idata->dyn_table; if (config.sql_backup_host) idata->recover = TRUE; if (config.sql_multi_values) { multi_values_buffer = malloc(config.sql_multi_values); if (!multi_values_buffer) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to get enough room (%d) for multi value queries.\n", config.name, config.type, config.sql_multi_values); config.sql_multi_values = FALSE; } memset(multi_values_buffer, 0, config.sql_multi_values); } if (config.sql_locking_style) idata->locks = sql_select_locking_style(config.sql_locking_style); } pmacct-0.14.0/src/Makefile.in0000644000175000017500000004004311741267656014725 0ustar paolopaolo# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am # Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. SHELL = @SHELL@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ libexecdir = @libexecdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ sharedstatedir = @sharedstatedir@ localstatedir = @localstatedir@ libdir = @libdir@ infodir = @infodir@ mandir = @mandir@ includedir = @includedir@ oldincludedir = /usr/include DESTDIR = pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = .. ACLOCAL = @ACLOCAL@ AUTOCONF = @AUTOCONF@ AUTOMAKE = @AUTOMAKE@ AUTOHEADER = @AUTOHEADER@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ transform = @program_transform_name@ NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : CC = @CC@ EXTRABIN = @EXTRABIN@ MAKE = @MAKE@ MAKEINFO = @MAKEINFO@ PACKAGE = @PACKAGE@ PLUGINS = @PLUGINS@ RANLIB = @RANLIB@ THREADS_SOURCES = @THREADS_SOURCES@ VERSION = @VERSION@ SUBDIRS = nfprobe_plugin sfprobe_plugin bgp tee_plugin isis sbin_PROGRAMS = pmacctd nfacctd sfacctd uacctd bin_PROGRAMS = pmacct @EXTRABIN@ EXTRA_PROGRAMS = pmmyplay pmpgplay pmacctd_PLUGINS = @PLUGINS@ @THREADS_SOURCES@ pmacctd_SOURCES = pmacctd.c signals.c util.c strlcpy.c plugin_hooks.c server.c acct.c memory.c ll.c cfg.c imt_plugin.c log.c pkt_handlers.c cfg_handlers.c net_aggr.c bpf_filter.c print_plugin.c ip_frag.c ports_aggr.c addr.c pretag.c pretag_handlers.c ip_flow.c setproctitle.c classifier.c regexp.c regsub.c conntrack.c xflow_status.c nl.c pmacctd_LDFLAGS = $(DEFS) pmacctd_LDADD = $(pmacctd_PLUGINS) nfacctd_SOURCES = nfacctd.c signals.c util.c strlcpy.c plugin_hooks.c server.c acct.c memory.c cfg.c imt_plugin.c log.c pkt_handlers.c cfg_handlers.c net_aggr.c bpf_filter.c print_plugin.c pretag.c pretag_handlers.c ports_aggr.c nfv8_handlers.c nfv9_template.c addr.c setproctitle.c ip_flow.c classifier.c regexp.c regsub.c conntrack.c xflow_status.c nfacctd_LDFLAGS = $(DEFS) nfacctd_LDADD = $(pmacctd_PLUGINS) sfacctd_SOURCES = sfacctd.c signals.c util.c strlcpy.c plugin_hooks.c server.c acct.c memory.c cfg.c imt_plugin.c log.c pkt_handlers.c cfg_handlers.c net_aggr.c bpf_filter.c print_plugin.c pretag.c pretag_handlers.c ports_aggr.c addr.c ll.c setproctitle.c ip_flow.c classifier.c regexp.c regsub.c conntrack.c xflow_status.c sfacctd_LDFLAGS = $(DEFS) sfacctd_LDADD = $(pmacctd_PLUGINS) uacctd_SOURCES = uacctd.c signals.c util.c strlcpy.c plugin_hooks.c server.c acct.c memory.c ll.c cfg.c imt_plugin.c log.c pkt_handlers.c cfg_handlers.c net_aggr.c bpf_filter.c print_plugin.c ip_frag.c ports_aggr.c addr.c pretag.c pretag_handlers.c ip_flow.c setproctitle.c classifier.c regexp.c regsub.c conntrack.c xflow_status.c nl.c uacctd_LDFLAGS = $(DEFS) uacctd_LDADD = $(pmacctd_PLUGINS) pmacct_SOURCES = pmacct.c strlcpy.c addr.c pmmyplay_SOURCES = pmmyplay.c strlcpy.c sql_handlers.c log_templates.c addr.c pmpgplay_SOURCES = pmpgplay.c strlcpy.c sql_handlers.c log_templates.c addr.c mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_CLEAN_FILES = PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS) DEFS = @DEFS@ -I. -I$(srcdir) CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ pmmyplay_OBJECTS = pmmyplay.o strlcpy.o sql_handlers.o log_templates.o \ addr.o pmmyplay_LDADD = $(LDADD) pmmyplay_DEPENDENCIES = pmmyplay_LDFLAGS = pmpgplay_OBJECTS = pmpgplay.o strlcpy.o sql_handlers.o log_templates.o \ addr.o pmpgplay_LDADD = $(LDADD) pmpgplay_DEPENDENCIES = pmpgplay_LDFLAGS = pmacct_OBJECTS = pmacct.o strlcpy.o addr.o pmacct_LDADD = $(LDADD) pmacct_DEPENDENCIES = pmacct_LDFLAGS = pmacctd_OBJECTS = pmacctd.o signals.o util.o strlcpy.o plugin_hooks.o \ server.o acct.o memory.o ll.o cfg.o imt_plugin.o log.o pkt_handlers.o \ cfg_handlers.o net_aggr.o bpf_filter.o print_plugin.o ip_frag.o \ ports_aggr.o addr.o pretag.o pretag_handlers.o ip_flow.o setproctitle.o \ classifier.o regexp.o regsub.o conntrack.o xflow_status.o nl.o pmacctd_DEPENDENCIES = nfacctd_OBJECTS = nfacctd.o signals.o util.o strlcpy.o plugin_hooks.o \ server.o acct.o memory.o cfg.o imt_plugin.o log.o pkt_handlers.o \ cfg_handlers.o net_aggr.o bpf_filter.o print_plugin.o pretag.o \ pretag_handlers.o ports_aggr.o nfv8_handlers.o nfv9_template.o addr.o \ setproctitle.o ip_flow.o classifier.o regexp.o regsub.o conntrack.o \ xflow_status.o nfacctd_DEPENDENCIES = sfacctd_OBJECTS = sfacctd.o signals.o util.o strlcpy.o plugin_hooks.o \ server.o acct.o memory.o cfg.o imt_plugin.o log.o pkt_handlers.o \ cfg_handlers.o net_aggr.o bpf_filter.o print_plugin.o pretag.o \ pretag_handlers.o ports_aggr.o addr.o ll.o setproctitle.o ip_flow.o \ classifier.o regexp.o regsub.o conntrack.o xflow_status.o sfacctd_DEPENDENCIES = uacctd_OBJECTS = uacctd.o signals.o util.o strlcpy.o plugin_hooks.o \ server.o acct.o memory.o ll.o cfg.o imt_plugin.o log.o pkt_handlers.o \ cfg_handlers.o net_aggr.o bpf_filter.o print_plugin.o ip_frag.o \ ports_aggr.o addr.o pretag.o pretag_handlers.o ip_flow.o setproctitle.o \ classifier.o regexp.o regsub.o conntrack.o xflow_status.o nl.o uacctd_DEPENDENCIES = CFLAGS = @CFLAGS@ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ DIST_COMMON = Makefile.am Makefile.in DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) TAR = tar GZIP_ENV = --best SOURCES = $(pmmyplay_SOURCES) $(pmpgplay_SOURCES) $(pmacct_SOURCES) $(pmacctd_SOURCES) $(nfacctd_SOURCES) $(sfacctd_SOURCES) $(uacctd_SOURCES) OBJECTS = $(pmmyplay_OBJECTS) $(pmpgplay_OBJECTS) $(pmacct_OBJECTS) $(pmacctd_OBJECTS) $(nfacctd_OBJECTS) $(sfacctd_OBJECTS) $(uacctd_OBJECTS) all: all-redirect .SUFFIXES: .SUFFIXES: .S .c .o .s $(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps src/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status cd $(top_builddir) \ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status mostlyclean-binPROGRAMS: clean-binPROGRAMS: -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) distclean-binPROGRAMS: maintainer-clean-binPROGRAMS: install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(DESTDIR)$(bindir) @list='$(bin_PROGRAMS)'; for p in $$list; do \ if test -f $$p; then \ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ else :; fi; \ done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) list='$(bin_PROGRAMS)'; for p in $$list; do \ rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ done mostlyclean-sbinPROGRAMS: clean-sbinPROGRAMS: -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) distclean-sbinPROGRAMS: maintainer-clean-sbinPROGRAMS: install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(DESTDIR)$(sbindir) @list='$(sbin_PROGRAMS)'; for p in $$list; do \ if test -f $$p; then \ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ else :; fi; \ done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) list='$(sbin_PROGRAMS)'; for p in $$list; do \ rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ done .c.o: $(COMPILE) -c $< .s.o: $(COMPILE) -c $< .S.o: $(COMPILE) -c $< mostlyclean-compile: -rm -f *.o core *.core clean-compile: distclean-compile: -rm -f *.tab.c maintainer-clean-compile: pmmyplay: $(pmmyplay_OBJECTS) $(pmmyplay_DEPENDENCIES) @rm -f pmmyplay $(LINK) $(pmmyplay_LDFLAGS) $(pmmyplay_OBJECTS) $(pmmyplay_LDADD) $(LIBS) pmpgplay: $(pmpgplay_OBJECTS) $(pmpgplay_DEPENDENCIES) @rm -f pmpgplay $(LINK) $(pmpgplay_LDFLAGS) $(pmpgplay_OBJECTS) $(pmpgplay_LDADD) $(LIBS) pmacct: $(pmacct_OBJECTS) $(pmacct_DEPENDENCIES) @rm -f pmacct $(LINK) $(pmacct_LDFLAGS) $(pmacct_OBJECTS) $(pmacct_LDADD) $(LIBS) pmacctd: $(pmacctd_OBJECTS) $(pmacctd_DEPENDENCIES) @rm -f pmacctd $(LINK) $(pmacctd_LDFLAGS) $(pmacctd_OBJECTS) $(pmacctd_LDADD) $(LIBS) nfacctd: $(nfacctd_OBJECTS) $(nfacctd_DEPENDENCIES) @rm -f nfacctd $(LINK) $(nfacctd_LDFLAGS) $(nfacctd_OBJECTS) $(nfacctd_LDADD) $(LIBS) sfacctd: $(sfacctd_OBJECTS) $(sfacctd_DEPENDENCIES) @rm -f sfacctd $(LINK) $(sfacctd_LDFLAGS) $(sfacctd_OBJECTS) $(sfacctd_LDADD) $(LIBS) uacctd: $(uacctd_OBJECTS) $(uacctd_DEPENDENCIES) @rm -f uacctd $(LINK) $(uacctd_LDFLAGS) $(uacctd_OBJECTS) $(uacctd_LDADD) $(LIBS) # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. @SET_MAKE@ all-recursive install-data-recursive install-exec-recursive \ installdirs-recursive install-recursive uninstall-recursive \ check-recursive installcheck-recursive info-recursive dvi-recursive: @set fnord $(MAKEFLAGS); amf=$$2; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" mostlyclean-recursive clean-recursive distclean-recursive \ maintainer-clean-recursive: @set fnord $(MAKEFLAGS); amf=$$2; \ dot_seen=no; \ rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ rev="$$subdir $$rev"; \ test "$$subdir" != "." || dot_seen=yes; \ done; \ test "$$dot_seen" = "no" && rev=". $$rev"; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done tags: TAGS ID: $(HEADERS) $(SOURCES) $(LISP) list='$(SOURCES) $(HEADERS)'; \ unique=`for i in $$list; do echo $$i; done | \ awk ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ here=`pwd` && cd $(srcdir) \ && mkid -f$$here/ID $$unique $(LISP) TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) tags=; \ here=`pwd`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS)'; \ unique=`for i in $$list; do echo $$i; done | \ awk ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) mostlyclean-tags: clean-tags: distclean-tags: -rm -f TAGS ID maintainer-clean-tags: distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) subdir = src distdir: $(DISTFILES) @for file in $(DISTFILES); do \ d=$(srcdir); \ if test -d $$d/$$file; then \ cp -pr $$d/$$file $(distdir)/$$file; \ else \ test -f $(distdir)/$$file \ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ || cp -p $$d/$$file $(distdir)/$$file || :; \ fi; \ done for subdir in $(SUBDIRS); do \ if test "$$subdir" = .; then :; else \ test -d $(distdir)/$$subdir \ || mkdir $(distdir)/$$subdir \ || exit 1; \ chmod 777 $(distdir)/$$subdir; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(top_distdir) distdir=../$(distdir)/$$subdir distdir) \ || exit 1; \ fi; \ done info-am: info: info-recursive dvi-am: dvi: dvi-recursive check-am: all-am check: check-recursive installcheck-am: installcheck: installcheck-recursive install-exec-am: install-binPROGRAMS install-sbinPROGRAMS install-exec: install-exec-recursive install-data-am: install-data: install-data-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am install: install-recursive uninstall-am: uninstall-binPROGRAMS uninstall-sbinPROGRAMS uninstall: uninstall-recursive all-am: Makefile $(PROGRAMS) all-redirect: all-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install installdirs: installdirs-recursive installdirs-am: $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(sbindir) mostlyclean-generic: clean-generic: distclean-generic: -rm -f Makefile $(CONFIG_CLEAN_FILES) -rm -f config.cache config.log stamp-h stamp-h[0-9]* maintainer-clean-generic: mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-sbinPROGRAMS \ mostlyclean-compile mostlyclean-tags \ mostlyclean-generic mostlyclean: mostlyclean-recursive clean-am: clean-binPROGRAMS clean-sbinPROGRAMS clean-compile clean-tags \ clean-generic mostlyclean-am clean: clean-recursive distclean-am: distclean-binPROGRAMS distclean-sbinPROGRAMS \ distclean-compile distclean-tags distclean-generic \ clean-am distclean: distclean-recursive maintainer-clean-am: maintainer-clean-binPROGRAMS \ maintainer-clean-sbinPROGRAMS maintainer-clean-compile \ maintainer-clean-tags maintainer-clean-generic \ distclean-am @echo "This command is intended for maintainers to use;" @echo "it deletes files that may require special tools to rebuild." maintainer-clean: maintainer-clean-recursive .PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \ maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \ mostlyclean-sbinPROGRAMS distclean-sbinPROGRAMS clean-sbinPROGRAMS \ maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \ install-sbinPROGRAMS mostlyclean-compile distclean-compile \ clean-compile maintainer-clean-compile install-data-recursive \ uninstall-data-recursive install-exec-recursive \ uninstall-exec-recursive installdirs-recursive uninstalldirs-recursive \ all-recursive check-recursive installcheck-recursive info-recursive \ dvi-recursive mostlyclean-recursive distclean-recursive clean-recursive \ maintainer-clean-recursive tags tags-recursive mostlyclean-tags \ distclean-tags clean-tags maintainer-clean-tags distdir info-am info \ dvi-am dvi check check-am installcheck-am installcheck install-exec-am \ install-exec install-data-am install-data install-am install \ uninstall-am uninstall all-redirect all-am all installdirs-am \ installdirs mostlyclean-generic distclean-generic clean-generic \ maintainer-clean-generic clean mostlyclean distclean maintainer-clean # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: pmacct-0.14.0/src/thread_pool.h0000644000175000017500000000361010534423663015317 0ustar paolopaolo/* Thread pool implementation for pmacct Copyright (C) 2006 Francois Deppierraz */ /* 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. */ #ifndef _THREAD_POOL_H_ #define _THREAD_POOL_H_ #include #include /* for assert() */ #include /* for EBUSY */ #define DEBUG 0 #define THREAD_DEBUG 0 #define DEFAULT_TH_NUM 10 typedef struct thread_pool_item { int id; pthread_mutex_t *mutex; pthread_cond_t *cond; pthread_t *thread; void (*function)(struct packet_ptrs *data); struct packet_ptrs *data; int usage; int go; int quit; struct thread_pool_item *next; struct thread_pool *owner; } thread_pool_item_t; typedef struct thread_pool { int count; thread_pool_item_t *free_list; pthread_cond_t *cond; pthread_mutex_t *mutex; } thread_pool_t; #if (!defined __THREAD_POOL_C) #define EXT extern #else #define EXT #endif EXT thread_pool_t *allocate_thread_pool(int count); EXT void desallocate_thread_pool(thread_pool_t *pool); EXT void send_to_pool(thread_pool_t *pool, void *function, struct packet_ptrs *data); EXT void *thread_runner(void *); #undef EXT #endif /* _THREAD_POOL_H_ */ pmacct-0.14.0/src/mysql_plugin.c0000644000175000017500000006043211652204562015537 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #define __MYSQL_PLUGIN_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "sql_common.h" #include "mysql_plugin.h" #include "sql_common_m.c" /* Functions */ void mysql_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_data *data; struct ports_table pt; struct pollfd pfd; struct insert_data idata; struct timezone tz; time_t refresh_deadline; int timeout; int ret, num; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; struct pkt_bgp_primitives *pbgp; char *dataptr; unsigned char *rgptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; memcpy(&config, cfgptr, sizeof(struct configuration)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "MySQL Plugin", config.name); memset(&idata, 0, sizeof(idata)); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile); } sql_set_signals(); sql_init_default_values(); MY_init_default_values(&idata); MY_set_callbacks(&sqlfunc_cbr); sql_set_insert_func(); /* some LOCAL initialization AFTER setting some default values */ reload_map = FALSE; idata.now = time(NULL); refresh_deadline = idata.now; sql_init_maps(&nt, &nc, &pt); sql_init_global_buffers(); sql_init_pipe(&pfd, pipe_fd); sql_init_historical_acct(idata.now, &idata); sql_init_triggers(idata.now, &idata); sql_init_refresh_deadline(&refresh_deadline); /* setting number of entries in _protocols structure */ while (_protocols[protocols_number].number != -1) protocols_number++; /* building up static SQL clauses */ idata.num_primitives = MY_compose_static_queries(); glob_num_primitives = idata.num_primitives; /* handling logfile template stuff */ te = sql_init_logfile_template(&th); INIT_BUF(logbuf); /* handling purge preprocessor */ set_preprocess_funcs(config.sql_preprocess, &prep); /* setting up environment variables */ SQL_SetENV(); sql_link_backend_descriptors(&bed, &p, &b); /* plugin main loop */ for(;;) { poll_again: status->wakeup = TRUE; sql_calc_refresh_timeout(refresh_deadline, idata.now, &timeout); ret = poll(&pfd, 1, timeout); if (ret < 0) goto poll_again; idata.now = time(NULL); if (config.sql_history) { while (idata.now > (idata.basetime + idata.timeslot)) { time_t saved_basetime = idata.basetime; idata.basetime += idata.timeslot; if (config.sql_history == COUNT_MONTHLY) idata.timeslot = calc_monthly_timeslot(idata.basetime, config.sql_history_howmany, ADD); glob_basetime = idata.basetime; idata.new_basetime = saved_basetime; glob_new_basetime = saved_basetime; } } switch (ret) { case 0: /* timeout */ if (qq_ptr) sql_cache_flush(queries_queue, qq_ptr, &idata, FALSE); switch (fork()) { case 0: /* Child */ /* we have to ignore signals to avoid loops: because we are already forked */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); pm_setproctitle("%s [%s]", "MySQL Plugin -- DB Writer", config.name); if (qq_ptr && sql_writers.flags != CHLD_ALERT) { if (sql_writers.flags == CHLD_WARNING) sql_db_fail(&p); (*sqlfunc_cbr.connect)(&p, config.sql_host); (*sqlfunc_cbr.purge)(queries_queue, qq_ptr, &idata); (*sqlfunc_cbr.close)(&bed); } if (config.sql_trigger_exec) { if (idata.now > idata.triggertime) sql_trigger_exec(config.sql_trigger_exec); } exit(0); default: /* Parent */ if (pqq_ptr) sql_cache_flush_pending(pending_queries_queue, pqq_ptr, &idata); gettimeofday(&idata.flushtime, &tz); while (idata.now > refresh_deadline) refresh_deadline += config.sql_refresh_time; while (idata.now > idata.triggertime && idata.t_timeslot > 0) { idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } idata.new_basetime = FALSE; glob_new_basetime = FALSE; qq_ptr = pqq_ptr; memcpy(queries_queue, pending_queries_queue, qq_ptr*sizeof(struct db_cache *)); if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } break; } break; default: /* we received data */ read_data: if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; idata.now = time(NULL); } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; goto poll_again; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%u'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%u'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); } seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); if ((rg->ptr+bufsz) >= rg->end) rg->ptr = rg->base; else rg->ptr += bufsz; /* lazy sql refresh handling */ if (idata.now > refresh_deadline) { if (qq_ptr) sql_cache_flush(queries_queue, qq_ptr, &idata, FALSE); switch (fork()) { case 0: /* Child */ /* we have to ignore signals to avoid loops: because we are already forked */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); pm_setproctitle("%s [%s]", "MySQL Plugin -- DB Writer", config.name); if (qq_ptr && sql_writers.flags != CHLD_ALERT) { if (sql_writers.flags == CHLD_WARNING) sql_db_fail(&p); (*sqlfunc_cbr.connect)(&p, config.sql_host); (*sqlfunc_cbr.purge)(queries_queue, qq_ptr, &idata); (*sqlfunc_cbr.close)(&bed); } if (config.sql_trigger_exec) { if (idata.now > idata.triggertime) sql_trigger_exec(config.sql_trigger_exec); } exit(0); default: /* Parent */ if (pqq_ptr) sql_cache_flush_pending(pending_queries_queue, pqq_ptr, &idata); gettimeofday(&idata.flushtime, &tz); while (idata.now > refresh_deadline) refresh_deadline += config.sql_refresh_time; while (idata.now > idata.triggertime && idata.t_timeslot > 0) { idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } idata.new_basetime = FALSE; glob_new_basetime = FALSE; qq_ptr = pqq_ptr; memcpy(queries_queue, pending_queries_queue, qq_ptr*sizeof(struct db_cache *)); if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } break; } } else { if (config.sql_trigger_exec) { while (idata.now > idata.triggertime && idata.t_timeslot > 0) { sql_trigger_exec(config.sql_trigger_exec); idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } } } data = (struct pkt_data *) (pipebuf+sizeof(struct ch_buf_hdr)); while (((struct ch_buf_hdr *)pipebuf)->num) { for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &data->primitives); if (config.ports_file) { if (!pt.table[data->primitives.src_port]) data->primitives.src_port = 0; if (!pt.table[data->primitives.dst_port]) data->primitives.dst_port = 0; } if (PbgpSz) pbgp = (struct pkt_bgp_primitives *) ((u_char *)data+PdataSz); else pbgp = NULL; (*insert_func)(data, pbgp, &idata); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) data; dataptr += PdataSz + PbgpSz; data = (struct pkt_data *) dataptr; } } goto read_data; } } } int MY_cache_dbop(struct DBdesc *db, struct db_cache *cache_elem, struct insert_data *idata) { char *ptr_values, *ptr_where, *ptr_mv, *ptr_set; int num=0, ret=0, have_flows=0, len=0; if (idata->mv.last_queue_elem) { ret = mysql_query(db->desc, multi_values_buffer); Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d VALUES statements sent to the MySQL server.\n", config.name, config.type, idata->mv.buffer_elem_num); if (ret) goto signal_error; idata->iqn++; idata->mv.buffer_elem_num = FALSE; idata->mv.buffer_offset = 0; return FALSE; } if (config.what_to_count & COUNT_FLOWS) have_flows = TRUE; /* constructing sql query */ ptr_where = where_clause; ptr_values = values_clause; ptr_set = set_clause; memset(where_clause, 0, sizeof(where_clause)); memset(values_clause, 0, sizeof(values_clause)); memset(set_clause, 0, sizeof(set_clause)); for (num = 0; num < idata->num_primitives; num++) (*where[num].handler)(cache_elem, idata, num, &ptr_values, &ptr_where); for (num = 0; set[num].type; num++) (*set[num].handler)(cache_elem, idata, num, &ptr_set, NULL); /* sending UPDATE query */ if (!config.sql_dont_try_update) { strncpy(sql_data, update_clause, SPACELEFT(sql_data)); strncat(sql_data, set_clause, SPACELEFT(sql_data)); strncat(sql_data, where_clause, SPACELEFT(sql_data)); ret = mysql_query(db->desc, sql_data); if (ret) goto signal_error; } if (config.sql_dont_try_update || (mysql_affected_rows(db->desc) == 0)) { /* UPDATE failed, trying with an INSERT query */ #if defined HAVE_64BIT_COUNTERS if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), ", %llu, %llu, %llu)", cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), ", %llu, %llu)", cache_elem->packet_counter, cache_elem->bytes_counter); #else if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), ", %lu, %lu, %lu)", cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), ", %lu, %lu)", cache_elem->packet_counter, cache_elem->bytes_counter); #endif if (config.sql_multi_values) { multi_values_handling: if (!idata->mv.buffer_elem_num) { strncpy(multi_values_buffer, insert_clause, config.sql_multi_values); strcat(multi_values_buffer, " VALUES"); idata->mv.buffer_offset += strlen(multi_values_buffer); idata->mv.head_buffer_elem = idata->current_queue_elem; } len = config.sql_multi_values-idata->mv.buffer_offset; if (strlen(values_clause) < len) { if (idata->mv.buffer_elem_num) { strcpy(multi_values_buffer+idata->mv.buffer_offset, ","); idata->mv.buffer_offset++; } ptr_mv = multi_values_buffer+idata->mv.buffer_offset; strcpy(multi_values_buffer+idata->mv.buffer_offset, values_clause+7); /* cut the initial 'VALUES' */ idata->mv.buffer_offset += strlen(ptr_mv); idata->mv.buffer_elem_num++; } else { if (idata->mv.buffer_elem_num) { ret = mysql_query(db->desc, multi_values_buffer); Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d VALUES statements sent to the MySQL server.\n", config.name, config.type, idata->mv.buffer_elem_num); if (ret) goto signal_error; idata->iqn++; idata->mv.buffer_elem_num = FALSE; idata->mv.head_buffer_elem = FALSE; idata->mv.buffer_offset = 0; goto multi_values_handling; } else { Log(LOG_ERR, "ERROR ( %s/%s ): 'sql_multi_values' is too small (%d). Try with a larger value.\n", config.name, config.type, config.sql_multi_values); exit_plugin(1); } } } else { strncpy(sql_data, insert_clause, sizeof(sql_data)); strncat(sql_data, values_clause, SPACELEFT(sql_data)); ret = mysql_query(db->desc, sql_data); if (ret) goto signal_error; Log(LOG_DEBUG, "DEBUG ( %s/%s ): %s\n\n", config.name, config.type, sql_data); idata->iqn++; } } else { Log(LOG_DEBUG, "DEBUG ( %s/%s ): %s\n\n", config.name, config.type, sql_data); idata->uqn++; } idata->een++; // cache_elem->valid = FALSE; /* committed */ return ret; signal_error: if (!idata->mv.buffer_elem_num) Log(LOG_DEBUG, "DEBUG ( %s/%s ): FAILED query follows:\n%s\n", config.name, config.type, sql_data); else { if (!idata->recover || db->type != BE_TYPE_PRIMARY) { /* DB failure: we will rewind the multi-values buffer */ idata->current_queue_elem = idata->mv.head_buffer_elem; idata->mv.buffer_elem_num = 0; } } MY_get_errmsg(db); if (db->errmsg) Log(LOG_ERR, "ERROR ( %s/%s ): %s\n\n", config.name, config.type, db->errmsg); if (mysql_errno(db->desc) == 1062) return FALSE; /* not signalling duplicate entry problems */ else return ret; } void MY_cache_purge(struct db_cache *queue[], int index, struct insert_data *idata) { struct db_cache *LastElemCommitted = NULL; struct logfile lf; time_t start; int j, stop, ret; bed.lf = &lf; memset(&lf, 0, sizeof(struct logfile)); for (j = 0, stop = 0; (!stop) && preprocess_funcs[j]; j++) stop = preprocess_funcs[j](queue, &index, j); if (config.what_to_count & COUNT_CLASS) sql_invalidate_shadow_entries(queue, &index); idata->ten = index; if (config.debug) { Log(LOG_DEBUG, "( %s/%s ) *** Purging cache - START ***\n", config.name, config.type); start = time(NULL); } /* We check for variable substitution in SQL table */ if (idata->dyn_table) { char tmpbuf[LONGLONGSRVBUFLEN]; time_t stamp = idata->new_basetime ? idata->new_basetime : idata->basetime; strftime_same(insert_clause, LONGSRVBUFLEN, tmpbuf, &stamp); strftime_same(update_clause, LONGSRVBUFLEN, tmpbuf, &stamp); strftime_same(lock_clause, LONGSRVBUFLEN, tmpbuf, &stamp); if (config.sql_table_schema) sql_create_table(bed.p, &stamp); } if (idata->locks == PM_LOCK_EXCLUSIVE) (*sqlfunc_cbr.lock)(bed.p); for (idata->current_queue_elem = 0; idata->current_queue_elem < index; idata->current_queue_elem++) { if (queue[idata->current_queue_elem]->valid) sql_query(&bed, queue[idata->current_queue_elem], idata); if (queue[idata->current_queue_elem]->valid == SQL_CACHE_COMMITTED) LastElemCommitted = queue[idata->current_queue_elem]; } /* multi-value INSERT query: wrap-up */ if (idata->mv.buffer_elem_num) { idata->mv.last_queue_elem = TRUE; sql_query(&bed, LastElemCommitted, idata); } /* rewinding stuff */ if (idata->locks == PM_LOCK_EXCLUSIVE) (*sqlfunc_cbr.unlock)(&bed); if ((lf.fail) || (b.fail)) Log(LOG_ALERT, "ALERT ( %s/%s ): recovery for MySQL daemon failed.\n", config.name, config.type); if (config.debug) { idata->elap_time = time(NULL)-start; Log(LOG_DEBUG, "( %s/%s ) *** Purging cache - END (QN: %u, ET: %u) ***\n", config.name, config.type, idata->qn, idata->elap_time); } if (config.sql_trigger_exec) { if (!config.debug) idata->elap_time = time(NULL)-start; SQL_SetENV_child(idata); } } int MY_evaluate_history(int primitive) { if (config.sql_history || config.nfacctd_sql_log) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (!config.sql_history_since_epoch) strncat(where[primitive].string, "FROM_UNIXTIME(%u) = ", SPACELEFT(where[primitive].string)); else strncat(where[primitive].string, "%u = ", SPACELEFT(where[primitive].string)); strncat(where[primitive].string, "stamp_inserted", SPACELEFT(where[primitive].string)); strncat(insert_clause, "stamp_updated, stamp_inserted", SPACELEFT(insert_clause)); if (!config.sql_history_since_epoch) strncat(values[primitive].string, "FROM_UNIXTIME(%u), FROM_UNIXTIME(%u)", SPACELEFT(values[primitive].string)); else strncat(values[primitive].string, "%u, %u", SPACELEFT(values[primitive].string)); where[primitive].type = values[primitive].type = TIMESTAMP; values[primitive].handler = where[primitive].handler = count_timestamp_handler; primitive++; } return primitive; } int MY_compose_static_queries() { int primitives=0, set_primitives=0, have_flows=0; if (config.what_to_count & COUNT_FLOWS || (config.sql_table_version >= 4 && config.sql_table_version < SQL_TABLE_VERSION_BGP && !config.sql_optimize_clauses)) { config.what_to_count |= COUNT_FLOWS; have_flows = TRUE; if ((config.sql_table_version < 4 || config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !config.sql_optimize_clauses) { Log(LOG_ERR, "ERROR ( %s/%s ): The accounting of flows requires SQL table v4. Exiting.\n", config.name, config.type); exit_plugin(1); } } /* "INSERT INTO ... VALUES ... " and "... WHERE ..." stuff */ strncpy(where[primitives].string, " WHERE ", sizeof(where[primitives].string)); snprintf(insert_clause, sizeof(insert_clause), "INSERT INTO `%s` (", config.sql_table); strncpy(values[primitives].string, " VALUES (", sizeof(values[primitives].string)); primitives = MY_evaluate_history(primitives); primitives = sql_evaluate_primitives(primitives); strncat(insert_clause, ", packets, bytes", SPACELEFT(insert_clause)); if (have_flows) strncat(insert_clause, ", flows", SPACELEFT(insert_clause)); strncat(insert_clause, ")", SPACELEFT(insert_clause)); /* "LOCK ..." stuff */ snprintf(lock_clause, sizeof(lock_clause), "LOCK TABLES `%s` WRITE", config.sql_table); strncpy(unlock_clause, "UNLOCK TABLES", sizeof(unlock_clause)); /* "UPDATE ... SET ..." stuff */ snprintf(update_clause, sizeof(update_clause), "UPDATE `%s` ", config.sql_table); set_primitives = sql_compose_static_set(have_flows); if (config.sql_history || config.nfacctd_sql_log) { if (!config.nfacctd_sql_log) { if (!config.sql_history_since_epoch) { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=NOW()", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_noop_setclause_handler; set_primitives++; } else { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=UNIX_TIMESTAMP(NOW())", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_noop_setclause_handler; set_primitives++; } } else { if (!config.sql_history_since_epoch) { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=FROM_UNIXTIME(%u)", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_timestamp_setclause_handler; set_primitives++; } else { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "stamp_updated=%u", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = TIMESTAMP; set[set_primitives].handler = count_timestamp_setclause_handler; set_primitives++; } } } return primitives; } void MY_Lock(struct DBdesc *db) { if (!db->fail) { if (mysql_query(db->desc, lock_clause)) { MY_get_errmsg(db); sql_db_errmsg(db); sql_db_fail(db); } } } void MY_Unlock(struct BE_descs *bed) { if (bed->p->connected) mysql_query(bed->p->desc, unlock_clause); if (bed->b->connected) mysql_query(bed->b->desc, unlock_clause); if (bed->lf->open) { if (logbuf.ptr != logbuf.base) { fwrite(logbuf.base, (logbuf.ptr-logbuf.base), 1, bed->lf->file); logbuf.ptr = logbuf.base; } file_unlock(fileno(bed->lf->file)); fclose(bed->lf->file); bed->lf->open = FALSE; } } void MY_DB_Connect(struct DBdesc *db, char *host) { MYSQL *dbptr = db->desc; if (!db->fail) { mysql_init(db->desc); dbptr->reconnect = TRUE; if (!mysql_real_connect(db->desc, host, config.sql_user, config.sql_passwd, config.sql_db, 0, NULL, 0)) { sql_db_fail(db); MY_get_errmsg(db); sql_db_errmsg(db); } else sql_db_ok(db); } } void MY_DB_Close(struct BE_descs *bed) { if (bed->p->connected) mysql_close(bed->p->desc); if (bed->b->connected) mysql_close(bed->b->desc); } void MY_create_dyn_table(struct DBdesc *db, char *buf) { if (!db->fail) { if (mysql_query(db->desc, buf)) { if (mysql_errno(db->desc) != 1050 /* ER_TABLE_EXISTS_ERROR */) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): FAILED query follows:\n%s\n", config.name, config.type, buf); MY_get_errmsg(db); sql_db_errmsg(db); } } } } void MY_create_backend(struct DBdesc *db) { db->desc = malloc(sizeof(MYSQL)); memset(db->desc, 0, sizeof(MYSQL)); } void MY_get_errmsg(struct DBdesc *db) { db->errmsg = (char *) mysql_error(db->desc); } void MY_set_callbacks(struct sqlfunc_cb_registry *cbr) { memset(cbr, 0, sizeof(struct sqlfunc_cb_registry)); cbr->connect = MY_DB_Connect; cbr->close = MY_DB_Close; cbr->lock = MY_Lock; cbr->unlock = MY_Unlock; cbr->op = MY_cache_dbop; cbr->create_table = MY_create_dyn_table; cbr->purge = MY_cache_purge; cbr->create_backend = MY_create_backend; } void MY_init_default_values(struct insert_data *idata) { /* Linking database parameters */ if (!config.sql_user) config.sql_user = mysql_user; if (!config.sql_db) config.sql_db = mysql_db; if (!config.sql_passwd) config.sql_passwd = mysql_pwd; if (!config.sql_table) { if (config.sql_table_version == (SQL_TABLE_VERSION_BGP+1)) config.sql_table = mysql_table_bgp; else if (config.sql_table_version == 8) config.sql_table = mysql_table_v8; else if (config.sql_table_version == 7) config.sql_table = mysql_table_v7; else if (config.sql_table_version == 6) config.sql_table = mysql_table_v6; else if (config.sql_table_version == 5) config.sql_table = mysql_table_v5; else if (config.sql_table_version == 4) config.sql_table = mysql_table_v4; else if (config.sql_table_version == 3) config.sql_table = mysql_table_v3; else if (config.sql_table_version == 2) config.sql_table = mysql_table_v2; else config.sql_table = mysql_table; } if (strchr(config.sql_table, '%')) idata->dyn_table = TRUE; glob_dyn_table = idata->dyn_table; if (config.sql_backup_host || config.sql_recovery_logfile) idata->recover = TRUE; if (config.sql_multi_values) { multi_values_buffer = malloc(config.sql_multi_values); if (!multi_values_buffer) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to get enough room (%d) for multi value queries.\n", config.name, config.type, config.sql_multi_values); config.sql_multi_values = FALSE; } memset(multi_values_buffer, 0, config.sql_multi_values); } if (config.sql_locking_style) idata->locks = sql_select_locking_style(config.sql_locking_style); } pmacct-0.14.0/src/pmacct.c0000644000175000017500000022063311715256661014273 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #define __PMACCT_CLIENT_C /* include */ #include "pmacct.h" #include "pmacct-data.h" #include "imt_plugin.h" #include "bgp/bgp_packet.h" #include "bgp/bgp.h" /* prototypes */ int Recv(int, unsigned char **); void print_ex_options_error(); void write_status_header_formatted(); void write_status_header_csv(); void write_class_table_header(); int CHECK_Q_TYPE(int); int check_data_sizes(struct query_header *, struct pkt_data *); void client_counters_merge_sort(void *, int, int, int, int); void client_counters_merge(void *, int, int, int, int, int); int pmc_sanitize_buf(char *); void pmc_trim_all_spaces(char *); char *pmc_extract_token(char **, int); int pmc_bgp_rd2str(char *, rd_t *); int pmc_bgp_str2rd(rd_t *, char *); /* functions */ int CHECK_Q_TYPE(int type) { if (!type) return 0; if (type & WANT_RESET) type ^= WANT_RESET; if (type & WANT_ERASE) type ^= WANT_ERASE; if (type & WANT_LOCK_OP) type ^= WANT_LOCK_OP; return type; } void usage_client(char *prog) { printf("%s\n", PMACCT_USAGE_HEADER); printf("Usage: %s [query]\n\n", prog); printf("Queries:\n"); printf(" -s\tShow statistics\n"); printf(" -N\t[matching data[';' ... ]] | ['file:'[filename]] \n\tMatch primitives; print counters only (requires -c)\n"); printf(" -n\t[bytes|packets|flows|all] \n\tSelect the counters to print (applies to -N)\n"); printf(" -S\tSum counters instead of returning a single counter for each request (applies to -N)\n"); printf(" -M\t[matching data[';' ... ]] | ['file:'[filename]] \n\tMatch primitives; print formatted table (requires -c)\n"); printf(" -a\tDisplay all table fields (even those currently unused)\n"); printf(" -c\t[ src_mac | dst_mac | vlan | cos | src_host | dst_host | src_net | dst_net | src_mask | dst_mask | \n\t src_port | dst_port | tos | proto | src_as | dst_as | sum_mac | sum_host | sum_net | sum_as | \n\t sum_port | in_iface | out_iface | tag | tag2 | flows | class | std_comm | ext_comm | as_path | \n\t peer_src_ip | peer_dst_ip | peer_src_as | peer_dst_as | src_as_path | src_std_comm | src_med | \n\t src_ext_comm | src_local_pref | mpls_vpn_rd | etype ] \n\tSelect primitives to match (required by -N and -M)\n"); printf(" -T\t[bytes|packets|flows] \n\tOutput top N statistics (applies to -M and -s)\n"); printf(" -e\tClear statistics\n"); printf(" -r\tReset counters (applies to -N and -M)\n"); printf(" -l\tPerform locking of the table\n"); printf(" -t\tShow memory table status\n"); printf(" -C\tShow classifiers table\n"); printf(" -p\t[file] \n\tSocket for client-server communication (DEFAULT: /tmp/collect.pipe)\n"); printf(" -O\tShow output in CSV format (applies to -M and -s)\n"); printf(" -u\tLeave IP protocols in numerical format\n"); printf("\n"); printf(" See EXAMPLES file in the distribution for examples\n"); printf("\n"); printf("For suggestions, critics, bugs, contact me: %s.\n", MANTAINER); } int pmc_sanitize_buf(char *buf) { int x = 0, valid_char = 0; pmc_trim_all_spaces(buf); while (x < strlen(buf)) { if (!isspace(buf[x])) valid_char++; x++; } if (!valid_char) return TRUE; if (buf[0] == '!') return TRUE; return FALSE; } void pmc_trim_all_spaces(char *buf) { int i = 0, len; len = strlen(buf); /* trimming all spaces */ while (i <= len) { if (isspace(buf[i])) { strlcpy(&buf[i], &buf[i+1], len); len--; } else i++; } } void print_ex_options_error() { printf("ERROR: -s, -t, -N, -M and -C options are each mutually exclusive.\n\n"); exit(1); } void write_stats_header_formatted(u_int64_t what_to_count, u_int8_t have_wtc) { if (!have_wtc) { printf("TAG "); printf("TAG2 "); printf("CLASS "); printf("IN_IFACE "); printf("OUT_IFACE "); #if defined HAVE_L2 printf("SRC_MAC "); printf("DST_MAC "); printf("VLAN "); printf("COS "); printf("ETYPE "); #endif printf("SRC_AS "); printf("DST_AS "); printf("BGP_COMMS "); printf("SRC_BGP_COMMS "); printf("AS_PATH "); printf("SRC_AS_PATH "); printf("PREF "); printf("SRC_PREF "); printf("MED "); printf("SRC_MED "); printf("SYM "); printf("PEER_SRC_AS "); printf("PEER_DST_AS "); #if defined ENABLE_IPV6 printf("PEER_SRC_IP "); printf("PEER_DST_IP "); #else printf("PEER_SRC_IP "); printf("PEER_DST_IP "); #endif #if defined ENABLE_IPV6 printf("SRC_IP "); printf("DST_IP "); #else printf("SRC_IP "); printf("DST_IP "); #endif printf("SRC_MASK "); printf("DST_MASK "); printf("SRC_PORT "); printf("DST_PORT "); printf("TCP_FLAGS "); printf("PROTOCOL "); printf("TOS "); #if defined HAVE_64BIT_COUNTERS printf("PACKETS "); printf("FLOWS "); printf("BYTES\n"); #else printf("PACKETS "); printf("FLOWS "); printf("BYTES\n"); #endif } else { if (what_to_count & COUNT_ID) printf("TAG "); if (what_to_count & COUNT_ID2) printf("TAG2 "); if (what_to_count & COUNT_CLASS) printf("CLASS "); if (what_to_count & COUNT_IN_IFACE) printf("IN_IFACE "); if (what_to_count & COUNT_OUT_IFACE) printf("OUT_IFACE "); #if defined HAVE_L2 if (what_to_count & (COUNT_SRC_MAC|COUNT_SUM_MAC)) printf("SRC_MAC "); if (what_to_count & COUNT_DST_MAC) printf("DST_MAC "); if (what_to_count & COUNT_VLAN) printf("VLAN "); if (what_to_count & COUNT_COS) printf("COS "); if (what_to_count & COUNT_ETHERTYPE) printf("ETYPE "); #endif if (what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS)) printf("SRC_AS "); if (what_to_count & COUNT_DST_AS) printf("DST_AS "); if (what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM)) printf("BGP_COMMS "); if (what_to_count & (COUNT_SRC_STD_COMM|COUNT_SRC_EXT_COMM)) printf("SRC_BGP_COMMS "); if (what_to_count & COUNT_AS_PATH) printf("AS_PATH "); if (what_to_count & COUNT_SRC_AS_PATH) printf("SRC_AS_PATH "); if (what_to_count & COUNT_LOCAL_PREF) printf("PREF "); if (what_to_count & COUNT_SRC_LOCAL_PREF) printf("SRC_PREF "); if (what_to_count & COUNT_MED) printf("MED "); if (what_to_count & COUNT_SRC_MED) printf("SRC_MED "); if (what_to_count & COUNT_PEER_SRC_AS) printf("PEER_SRC_AS "); if (what_to_count & COUNT_PEER_DST_AS) printf("PEER_DST_AS "); #if defined ENABLE_IPV6 if (what_to_count & COUNT_PEER_SRC_IP) printf("PEER_SRC_IP "); if (what_to_count & COUNT_PEER_DST_IP) printf("PEER_DST_IP "); #else if (what_to_count & COUNT_PEER_SRC_IP) printf("PEER_SRC_IP "); if (what_to_count & COUNT_PEER_DST_IP) printf("PEER_DST_IP "); #endif if (what_to_count & COUNT_MPLS_VPN_RD) printf("MPLS_VPN_RD "); #if defined ENABLE_IPV6 if (what_to_count & (COUNT_SRC_HOST|COUNT_SRC_NET)) printf("SRC_IP "); if (what_to_count & (COUNT_SUM_HOST|COUNT_SUM_NET)) printf("SRC_IP "); if (what_to_count & (COUNT_DST_HOST|COUNT_DST_NET)) printf("DST_IP "); #else if (what_to_count & (COUNT_SRC_HOST|COUNT_SRC_NET)) printf("SRC_IP "); if (what_to_count & (COUNT_SUM_HOST|COUNT_SUM_NET)) printf("SRC_IP "); if (what_to_count & (COUNT_DST_HOST|COUNT_DST_NET)) printf("DST_IP "); #endif if (what_to_count & COUNT_SRC_NMASK) printf("SRC_MASK "); if (what_to_count & COUNT_DST_NMASK) printf("DST_MASK "); if (what_to_count & (COUNT_SRC_PORT|COUNT_SUM_PORT)) printf("SRC_PORT "); if (what_to_count & COUNT_DST_PORT) printf("DST_PORT "); if (what_to_count & COUNT_TCPFLAGS) printf("TCP_FLAGS "); if (what_to_count & COUNT_IP_PROTO) printf("PROTOCOL "); if (what_to_count & COUNT_IP_TOS) printf("TOS "); #if defined HAVE_64BIT_COUNTERS printf("PACKETS "); if (what_to_count & COUNT_FLOWS) printf("FLOWS "); printf("BYTES\n"); #else printf("PACKETS "); if (what_to_count & COUNT_FLOWS) printf("FLOWS "); printf("BYTES\n"); #endif } } void write_stats_header_csv(u_int64_t what_to_count, u_int8_t have_wtc) { if (!have_wtc) { printf("TAG,"); printf("TAG2,"); printf("CLASS,"); printf("IN_IFACE,"); printf("OUT_IFACE,"); #if defined HAVE_L2 printf("SRC_MAC,"); printf("DST_MAC,"); printf("VLAN,"); printf("COS,"); printf("ETYPE,"); #endif printf("SRC_AS,"); printf("DST_AS,"); printf("BGP_COMMS,"); printf("SRC_BGP_COMMS,"); printf("AS_PATH,"); printf("SRC_AS_PATH,"); printf("PREF,"); printf("SRC_PREF,"); printf("MED,"); printf("SRC_MED,"); printf("SYM,"); printf("PEER_SRC_AS,"); printf("PEER_DST_AS,"); #if defined ENABLE_IPV6 printf("PEER_SRC_IP,"); printf("PEER_DST_IP,"); #else printf("PEER_SRC_IP,"); printf("PEER_DST_IP,"); #endif #if defined ENABLE_IPV6 printf("SRC_IP,"); printf("DST_IP,"); #else printf("SRC_IP,"); printf("DST_IP,"); #endif printf("SRC_MASK,"); printf("DST_MASK,"); printf("SRC_PORT,"); printf("DST_PORT,"); printf("TCP_FLAGS,"); printf("PROTOCOL,"); printf("TOS,"); #if defined HAVE_64BIT_COUNTERS printf("PACKETS,"); printf("FLOWS,"); printf("BYTES\n"); #else printf("PACKETS,"); printf("FLOWS,"); printf("BYTES\n"); #endif } else { if (what_to_count & COUNT_ID) printf("TAG,"); if (what_to_count & COUNT_ID2) printf("TAG2,"); if (what_to_count & COUNT_CLASS) printf("CLASS,"); if (what_to_count & COUNT_IN_IFACE) printf("IN_IFACE,"); if (what_to_count & COUNT_OUT_IFACE) printf("OUT_IFACE,"); #if defined HAVE_L2 if (what_to_count & (COUNT_SRC_MAC|COUNT_SUM_MAC)) printf("SRC_MAC,"); if (what_to_count & COUNT_DST_MAC) printf("DST_MAC,"); if (what_to_count & COUNT_VLAN) printf("VLAN,"); if (what_to_count & COUNT_COS) printf("COS,"); if (what_to_count & COUNT_ETHERTYPE) printf("ETYPE,"); #endif if (what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS)) printf("SRC_AS,"); if (what_to_count & COUNT_DST_AS) printf("DST_AS,"); if (what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM)) printf("BGP_COMMS,"); if (what_to_count & (COUNT_SRC_STD_COMM|COUNT_SRC_EXT_COMM)) printf("SRC_BGP_COMMS,"); if (what_to_count & COUNT_AS_PATH) printf("AS_PATH,"); if (what_to_count & COUNT_SRC_AS_PATH) printf("SRC_AS_PATH,"); if (what_to_count & COUNT_LOCAL_PREF) printf("PREF,"); if (what_to_count & COUNT_SRC_LOCAL_PREF) printf("SRC_PREF,"); if (what_to_count & COUNT_MED) printf("MED,"); if (what_to_count & COUNT_SRC_MED) printf("SRC_MED,"); if (what_to_count & COUNT_PEER_SRC_AS) printf("PEER_SRC_AS,"); if (what_to_count & COUNT_PEER_DST_AS) printf("PEER_DST_AS,"); #if defined ENABLE_IPV6 if (what_to_count & COUNT_PEER_SRC_IP) printf("PEER_SRC_IP,"); if (what_to_count & COUNT_PEER_DST_IP) printf("PEER_DST_IP,"); #else if (what_to_count & COUNT_PEER_SRC_IP) printf("PEER_SRC_IP,"); if (what_to_count & COUNT_PEER_DST_IP) printf("PEER_DST_IP,"); #endif if (what_to_count & COUNT_MPLS_VPN_RD) printf("MPLS_VPN_RD,"); #if defined ENABLE_IPV6 if (what_to_count & (COUNT_SRC_HOST|COUNT_SRC_NET)) printf("SRC_IP,"); if (what_to_count & (COUNT_SUM_HOST|COUNT_SUM_NET)) printf("SRC_IP,"); if (what_to_count & (COUNT_DST_HOST|COUNT_DST_NET)) printf("DST_IP,"); #else if (what_to_count & (COUNT_SRC_HOST|COUNT_SRC_NET)) printf("SRC_IP,"); if (what_to_count & (COUNT_SUM_HOST|COUNT_SUM_NET)) printf("SRC_IP,"); if (what_to_count & (COUNT_DST_HOST|COUNT_DST_NET)) printf("DST_IP,"); #endif if (what_to_count & COUNT_SRC_NMASK) printf("SRC_MASK,"); if (what_to_count & COUNT_DST_NMASK) printf("DST_MASK,"); if (what_to_count & (COUNT_SRC_PORT|COUNT_SUM_PORT)) printf("SRC_PORT,"); if (what_to_count & COUNT_DST_PORT) printf("DST_PORT,"); if (what_to_count & COUNT_TCPFLAGS) printf("TCP_FLAGS,"); if (what_to_count & COUNT_IP_PROTO) printf("PROTOCOL,"); if (what_to_count & COUNT_IP_TOS) printf("TOS,"); #if defined HAVE_64BIT_COUNTERS printf("PACKETS,"); if (what_to_count & COUNT_FLOWS) printf("FLOWS,"); printf("BYTES\n"); #else printf("PACKETS,"); if (what_to_count & COUNT_FLOWS) printf("FLOWS,"); printf("BYTES\n"); #endif } } void write_status_header() { printf("* = element\n\n"); printf("BUCKET\tCHAIN STATUS\n"); } void write_class_table_header() { printf("CLASS ID\tCLASS NAME\n"); } int build_query_client(char *path_ptr) { struct sockaddr_un cAddr; int sd, rc, cLen; sd = socket(AF_UNIX, SOCK_STREAM, 0); if (sd < 0) { printf("ERROR: Unable to open socket.\n"); exit(1); } cAddr.sun_family = AF_UNIX; strcpy(cAddr.sun_path, path_ptr); cLen = sizeof(cAddr); rc = connect(sd, (struct sockaddr *) &cAddr, cLen); if (rc < 0) { if (errno == ECONNREFUSED) { printf("INFO: Connection refused while trying to connect to '%s'\n\n", path_ptr); exit(1); } else { printf("ERROR: Unable to connect to '%s'\n\n", path_ptr); exit(1); } } return sd; } int main(int argc,char **argv) { int clibufsz = (MAX_QUERIES*sizeof(struct query_entry))+sizeof(struct query_header)+2; struct pkt_data *acc_elem; struct bucket_desc *bd; struct query_header q; struct pkt_primitives empty_addr; struct pkt_bgp_primitives empty_pbgp; struct query_entry request; struct stripped_class *class_table = NULL; struct pkt_bgp_primitives *pbgp = NULL; char clibuf[clibufsz], *bufptr; unsigned char *largebuf, *elem, *ct; char ethernet_address[18], ip_address[INET6_ADDRSTRLEN]; char path[128], file[128], password[9], rd_str[SRVBUFLEN]; char *as_path, empty_aspath[] = "^$", *bgp_comm; int sd, buflen, unpacked, printed; int counter=0, ct_idx=0, ct_num=0; /* mrtg stuff */ char match_string[LARGEBUFLEN], *match_string_token, *match_string_ptr; char count[128], *count_token[N_PRIMITIVES], *count_ptr; int count_index = 0, match_string_index = 0, index = 0; u_int64_t count_token_int[N_PRIMITIVES]; /* getopt() stuff */ extern char *optarg; extern int optind, opterr, optopt; int errflag, cp, want_stats, want_erase, want_reset, want_class_table; int want_status, want_mrtg, want_counter, want_match, want_all_fields; int want_output, want_ipproto_num; int which_counter, topN_counter, fetch_from_file, sum_counters, num_counters; u_int64_t what_to_count, have_wtc; u_int32_t tmpnum; int PbgpSz = FALSE; /* Administrativia */ memset(&q, 0, sizeof(struct query_header)); memset(&empty_addr, 0, sizeof(struct pkt_primitives)); memset(&empty_pbgp, 0, sizeof(struct pkt_bgp_primitives)); memset(count, 0, sizeof(count)); memset(password, 0, sizeof(password)); strcpy(path, "/tmp/collect.pipe"); unpacked = 0; printed = 0; errflag = 0; buflen = 0; protocols_number = 0; want_stats = FALSE; want_erase = FALSE; want_status = FALSE; want_counter = FALSE; want_mrtg = FALSE; want_match = FALSE; want_all_fields = FALSE; want_reset = FALSE; want_class_table = FALSE; want_ipproto_num = FALSE; which_counter = FALSE; topN_counter = FALSE; sum_counters = FALSE; num_counters = FALSE; fetch_from_file = FALSE; what_to_count = FALSE; have_wtc = FALSE; want_output = PRINT_OUTPUT_FORMATTED; while (!errflag && ((cp = getopt(argc, argv, ARGS_PMACCT)) != -1)) { switch (cp) { case 's': if (CHECK_Q_TYPE(q.type)) print_ex_options_error(); q.type |= WANT_STATS; q.num = 1; want_stats = TRUE; break; case 'c': strlcpy(count, optarg, sizeof(count)); count_ptr = count; while ((*count_ptr != '\0') && (count_index <= N_PRIMITIVES-1)) { count_token[count_index] = pmc_extract_token(&count_ptr, ','); if (!strcmp(count_token[count_index], "src_host")) { count_token_int[count_index] = COUNT_SRC_HOST; what_to_count |= COUNT_SRC_HOST; } else if (!strcmp(count_token[count_index], "dst_host")) { count_token_int[count_index] = COUNT_DST_HOST; what_to_count |= COUNT_DST_HOST; } else if (!strcmp(count_token[count_index], "sum")) { count_token_int[count_index] = COUNT_SUM_HOST; what_to_count |= COUNT_SUM_HOST; } else if (!strcmp(count_token[count_index], "src_port")) { count_token_int[count_index] = COUNT_SRC_PORT; what_to_count |= COUNT_SRC_PORT; } else if (!strcmp(count_token[count_index], "dst_port")) { count_token_int[count_index] = COUNT_DST_PORT; what_to_count |= COUNT_DST_PORT; } else if (!strcmp(count_token[count_index], "proto")) { count_token_int[count_index] = COUNT_IP_PROTO; what_to_count |= COUNT_IP_PROTO; } #if defined HAVE_L2 else if (!strcmp(count_token[count_index], "src_mac")) { count_token_int[count_index] = COUNT_SRC_MAC; what_to_count |= COUNT_SRC_MAC; } else if (!strcmp(count_token[count_index], "dst_mac")) { count_token_int[count_index] = COUNT_DST_MAC; what_to_count |= COUNT_DST_MAC; } else if (!strcmp(count_token[count_index], "vlan")) { count_token_int[count_index] = COUNT_VLAN; what_to_count |= COUNT_VLAN; } else if (!strcmp(count_token[count_index], "cos")) { count_token_int[count_index] = COUNT_COS; what_to_count |= COUNT_COS; } else if (!strcmp(count_token[count_index], "etype")) { count_token_int[count_index] = COUNT_ETHERTYPE; what_to_count |= COUNT_ETHERTYPE; } else if (!strcmp(count_token[count_index], "sum_mac")) { count_token_int[count_index] = COUNT_SUM_MAC; what_to_count |= COUNT_SUM_MAC; } #endif else if (!strcmp(count_token[count_index], "in_iface")) { count_token_int[count_index] = COUNT_IN_IFACE; what_to_count |= COUNT_IN_IFACE; } else if (!strcmp(count_token[count_index], "out_iface")) { count_token_int[count_index] = COUNT_OUT_IFACE; what_to_count |= COUNT_OUT_IFACE; } else if (!strcmp(count_token[count_index], "tos")) { count_token_int[count_index] = COUNT_IP_TOS; what_to_count |= COUNT_IP_TOS; } else if (!strcmp(count_token[count_index], "none")) { count_token_int[count_index] = COUNT_NONE; what_to_count |= COUNT_NONE; } else if (!strcmp(count_token[count_index], "src_as")) { count_token_int[count_index] = COUNT_SRC_AS; what_to_count |= COUNT_SRC_AS; } else if (!strcmp(count_token[count_index], "dst_as")) { count_token_int[count_index] = COUNT_DST_AS; what_to_count |= COUNT_DST_AS; } else if (!strcmp(count_token[count_index], "src_net")) { count_token_int[count_index] = COUNT_SRC_NET; what_to_count |= COUNT_SRC_NET; } else if (!strcmp(count_token[count_index], "dst_net")) { count_token_int[count_index] = COUNT_DST_NET; what_to_count |= COUNT_DST_NET; } else if (!strcmp(count_token[count_index], "sum_host")) { count_token_int[count_index] = COUNT_SUM_HOST; what_to_count |= COUNT_SUM_HOST; } else if (!strcmp(count_token[count_index], "sum_net")) { count_token_int[count_index] = COUNT_SUM_NET; what_to_count |= COUNT_SUM_NET; } else if (!strcmp(count_token[count_index], "sum_as")) { count_token_int[count_index] = COUNT_SUM_AS; what_to_count |= COUNT_SUM_AS; } else if (!strcmp(count_token[count_index], "sum_port")) { count_token_int[count_index] = COUNT_SUM_PORT; what_to_count |= COUNT_SUM_PORT; } else if (!strcmp(count_token[count_index], "src_mask")) { count_token_int[count_index] = COUNT_SRC_NMASK; what_to_count |= COUNT_SRC_NMASK; } else if (!strcmp(count_token[count_index], "dst_mask")) { count_token_int[count_index] = COUNT_DST_NMASK; what_to_count |= COUNT_DST_NMASK; } else if (!strcmp(count_token[count_index], "tag")) { count_token_int[count_index] = COUNT_ID; what_to_count |= COUNT_ID; } else if (!strcmp(count_token[count_index], "tag2")) { count_token_int[count_index] = COUNT_ID2; what_to_count |= COUNT_ID2; } else if (!strcmp(count_token[count_index], "class")) { count_token_int[count_index] = COUNT_CLASS; what_to_count |= COUNT_CLASS; } else if (!strcmp(count_token[count_index], "std_comm")) { count_token_int[count_index] = COUNT_STD_COMM; what_to_count |= COUNT_STD_COMM; } else if (!strcmp(count_token[count_index], "src_std_comm")) { count_token_int[count_index] = COUNT_SRC_STD_COMM; what_to_count |= COUNT_SRC_STD_COMM; } else if (!strcmp(count_token[count_index], "ext_comm")) { count_token_int[count_index] = COUNT_EXT_COMM; what_to_count |= COUNT_EXT_COMM; } else if (!strcmp(count_token[count_index], "src_ext_comm")) { count_token_int[count_index] = COUNT_SRC_EXT_COMM; what_to_count |= COUNT_SRC_EXT_COMM; } else if (!strcmp(count_token[count_index], "as_path")) { count_token_int[count_index] = COUNT_AS_PATH; what_to_count |= COUNT_AS_PATH; } else if (!strcmp(count_token[count_index], "src_as_path")) { count_token_int[count_index] = COUNT_SRC_AS_PATH; what_to_count |= COUNT_SRC_AS_PATH; } else if (!strcmp(count_token[count_index], "local_pref")) { count_token_int[count_index] = COUNT_LOCAL_PREF; what_to_count |= COUNT_LOCAL_PREF; } else if (!strcmp(count_token[count_index], "src_local_pref")) { count_token_int[count_index] = COUNT_SRC_LOCAL_PREF; what_to_count |= COUNT_SRC_LOCAL_PREF; } else if (!strcmp(count_token[count_index], "med")) { count_token_int[count_index] = COUNT_MED; what_to_count |= COUNT_MED; } else if (!strcmp(count_token[count_index], "src_med")) { count_token_int[count_index] = COUNT_SRC_MED; what_to_count |= COUNT_SRC_MED; } else if (!strcmp(count_token[count_index], "peer_src_as")) { count_token_int[count_index] = COUNT_PEER_SRC_AS; what_to_count |= COUNT_PEER_SRC_AS; } else if (!strcmp(count_token[count_index], "peer_dst_as")) { count_token_int[count_index] = COUNT_PEER_DST_AS; what_to_count |= COUNT_PEER_DST_AS; } else if (!strcmp(count_token[count_index], "peer_src_ip")) { count_token_int[count_index] = COUNT_PEER_SRC_IP; what_to_count |= COUNT_PEER_SRC_IP; } else if (!strcmp(count_token[count_index], "peer_dst_ip")) { count_token_int[count_index] = COUNT_PEER_DST_IP; what_to_count |= COUNT_PEER_DST_IP; } else if (!strcmp(count_token[count_index], "mpls_vpn_rd")) { count_token_int[count_index] = COUNT_MPLS_VPN_RD; what_to_count |= COUNT_MPLS_VPN_RD; } else printf("WARN: ignoring unknown aggregation method: %s.\n", count_token[count_index]); what_to_count |= COUNT_COUNTERS; /* we always count counters ;-) */ count_index++; } break; case 'C': if (CHECK_Q_TYPE(q.type)) print_ex_options_error(); q.type |= WANT_CLASS_TABLE; q.num = 1; want_class_table = TRUE; break; case 'e': q.type |= WANT_ERASE; want_erase = TRUE; break; case 't': if (CHECK_Q_TYPE(q.type)) print_ex_options_error(); q.type |= WANT_STATUS; want_status = TRUE; break; case 'l': q.type |= WANT_LOCK_OP; break; case 'm': /* obsoleted */ want_mrtg = TRUE; case 'N': if (CHECK_Q_TYPE(q.type)) print_ex_options_error(); strlcpy(match_string, optarg, sizeof(match_string)); match_string[LARGEBUFLEN-1] = '\0'; q.type |= WANT_COUNTER; want_counter = TRUE; break; case 'n': if (!strcmp(optarg, "bytes")) which_counter = 0; else if (!strcmp(optarg, "packets")) which_counter = 1; else if (!strcmp(optarg, "flows")) which_counter = 3; else if (!strcmp(optarg, "all")) which_counter = 2; else printf("WARN: -n, ignoring unknown counter type: %s.\n", optarg); break; case 'T': if (!strcmp(optarg, "bytes")) topN_counter = 1; else if (!strcmp(optarg, "packets")) topN_counter = 2; else if (!strcmp(optarg, "flows")) topN_counter = 3; else printf("WARN: -T, ignoring unknown counter type: %s.\n", optarg); break; case 'S': sum_counters = TRUE; break; case 'M': if (CHECK_Q_TYPE(q.type)) print_ex_options_error(); strlcpy(match_string, optarg, sizeof(match_string)); match_string[LARGEBUFLEN-1] = '\0'; q.type |= WANT_MATCH; want_match = TRUE; break; case 'p': strlcpy(path, optarg, sizeof(path)); break; case 'P': strlcpy(password, optarg, sizeof(password)); break; case 'a': want_all_fields = TRUE; break; case 'r': q.type |= WANT_RESET; want_reset = TRUE; break; case 'O': want_output = PRINT_OUTPUT_CSV; break; case 'u': want_ipproto_num = TRUE; break; default: printf("ERROR: parameter %c unknown! \n Exiting...\n\n", cp); usage_client(argv[0]); exit(1); break; } } /* some post-getopt-processing task */ if (!q.type) { printf("ERROR: no options specified. Either -s, -e, -t, -M, -N or -C must be supplied. \n Exiting...\n\n"); usage_client(argv[0]); exit(1); } if ((want_counter || want_match) && !what_to_count) { printf("ERROR: -N or -M selected but -c has not been specified or is invalid.\n Exiting...\n\n"); usage_client(argv[0]); exit(1); } if (want_reset && !(want_counter || want_match)) { printf("ERROR: -r selected but either -N or -M has not been specified.\n Exiting...\n\n"); usage_client(argv[0]); exit(1); } if ((which_counter||sum_counters) && !want_counter) { printf("ERROR: -n and -S options apply only to -N\n Exiting...\n\n"); usage_client(argv[0]); exit(1); } if (topN_counter && (!want_match && !want_stats)) { printf("ERROR: -T option apply only to -M or -s\n Exiting...\n\n"); usage_client(argv[0]); exit(1); } if (want_counter || want_match) { char *ptr = match_string, prefix[] = "file:"; while(isspace(*ptr)) ptr++; if (!strncmp(ptr, prefix, strlen(prefix))) { fetch_from_file = TRUE; ptr += strlen(prefix); strlcpy(file, ptr, sizeof(file)); } } /* Sanitizing the aggregation method */ if (what_to_count) { if (what_to_count & COUNT_STD_COMM && what_to_count & COUNT_EXT_COMM) { printf("ERROR: The use of STANDARD and EXTENDED BGP communitities is mutual exclusive.\n"); exit(1); } if (what_to_count & COUNT_SRC_STD_COMM && what_to_count & COUNT_SRC_EXT_COMM) { printf("ERROR: The use of STANDARD and EXTENDED BGP communitities is mutual exclusive.\n"); exit(1); } } memcpy(q.passwd, password, sizeof(password)); /* setting number of entries in _protocols structure */ while (_protocols[protocols_number].number != -1) protocols_number++; if (want_counter || want_match) { FILE *f; int strnum; char **strings, *tptr1, *tptr2, tmpstr[SRVBUFLEN]; char *tmpbuf, *tmpbufptr; /* 1st step: count how many queries we will have */ if (!fetch_from_file) { for (strnum = 0, tptr1 = match_string; tptr1 && (strnum < MAX_QUERIES); strnum++) { tptr2 = tptr1; tptr1 = strchr(tptr1, ';'); if (tptr1) { if (*tptr2 == *tptr1) strnum--; /* void string */ tptr1++; } } } else { if ((f = fopen(file, "r")) == NULL) { printf("ERROR: file '%s' not found\n", file); exit(1); } else { strnum = 0; while (!feof(f) && (strnum < MAX_QUERIES)) { if (fgets(tmpstr, SRVBUFLEN, f)) { if (!pmc_sanitize_buf(tmpstr)) strnum++; } } } } strings = malloc((strnum+1)*sizeof(char *)); if (!strings) { printf("ERROR: Unable to allocate sufficient memory.\n"); exit(1); } memset(strings, 0, (strnum+1)*sizeof(char *)); if (fetch_from_file) { tmpbuf = malloc((strnum+1)*SRVBUFLEN); if (!tmpbuf) { printf("ERROR: Unable to allocate sufficient memory.\n"); exit(1); } memset(tmpbuf, 0, (strnum+1)*SRVBUFLEN); } /* 2nd step: tokenize the whole string */ if (!fetch_from_file) { for (strnum = 0, tptr1 = match_string; tptr1 && (strnum < MAX_QUERIES); strnum++) { tptr2 = tptr1; tptr1 = strchr(tptr1, ';'); if (tptr1) *tptr1 = '\0'; if (strlen(tptr2)) strings[strnum] = tptr2; else strnum--; /* void string */ if (tptr1) tptr1++; } } else { if (!freopen(file, "r", f)) { printf("ERROR: freopen() failed: %s\n", strerror(errno)); exit(1); } strnum = 0; tmpbufptr = tmpbuf; while (!feof(f) && (strnum < MAX_QUERIES)) { if (fgets(tmpbufptr, SRVBUFLEN, f)) { tmpbufptr[SRVBUFLEN-1] = '\0'; if (!pmc_sanitize_buf(tmpbufptr)) { strings[strnum] = tmpbufptr; strnum++; tmpbufptr += SRVBUFLEN; } } } fclose(f); } bufptr = clibuf; bufptr += sizeof(struct query_header); /* 4th step: build queries */ for (q.num = 0; (q.num < strnum) && (q.num < MAX_QUERIES); q.num++) { u_int64_t entry_wtc = what_to_count; match_string_ptr = strings[q.num]; match_string_index = 0; memset(&request, 0, sizeof(struct query_entry)); request.what_to_count = what_to_count; while ((*match_string_ptr != '\0') && (match_string_index < count_index)) { match_string_token = pmc_extract_token(&match_string_ptr, ','); /* Handling wildcards meaningfully */ if (!strcmp(match_string_token, "*")) { request.what_to_count ^= count_token_int[match_string_index]; match_string_index++; continue; } if (!strcmp(count_token[match_string_index], "src_host") || !strcmp(count_token[match_string_index], "src_net") || !strcmp(count_token[match_string_index], "sum_host") || !strcmp(count_token[match_string_index], "sum_net")) { if (!str_to_addr(match_string_token, &request.data.src_ip)) { printf("ERROR: src_host: Invalid IP address: '%s'\n", match_string_token); exit(1); } } else if (!strcmp(count_token[match_string_index], "dst_host") || !strcmp(count_token[match_string_index], "dst_net")) { if (!str_to_addr(match_string_token, &request.data.dst_ip)) { printf("ERROR: dst_host: Invalid IP address: '%s'\n", match_string_token); exit(1); } } #if defined (HAVE_L2) else if (!strcmp(count_token[match_string_index], "src_mac") || !strcmp(count_token[match_string_index], "sum_mac")) { unsigned char ethaddr[ETH_ADDR_LEN]; int res; res = string_etheraddr(match_string_token, ethaddr); if (res) { printf("ERROR: src_mac: Invalid MAC address: '%s'\n", match_string_token); exit(1); } else memcpy(&request.data.eth_shost, ethaddr, ETH_ADDR_LEN); } else if (!strcmp(count_token[match_string_index], "dst_mac")) { unsigned char ethaddr[ETH_ADDR_LEN]; int res; res = string_etheraddr(match_string_token, ethaddr); if (res) { printf("ERROR: src_mac: Invalid MAC address: '%s'\n", match_string_token); exit(1); } else memcpy(&request.data.eth_dhost, ethaddr, ETH_ADDR_LEN); } else if (!strcmp(count_token[match_string_index], "vlan")) { request.data.vlan_id = atoi(match_string_token); } else if (!strcmp(count_token[match_string_index], "cos")) { request.data.cos = atoi(match_string_token); } else if (!strcmp(count_token[match_string_index], "etype")) { sscanf(match_string_token, "%x", &request.data.etype); } #endif else if (!strcmp(count_token[match_string_index], "in_iface")) { char *endptr; request.data.ifindex_in = strtoul(match_string_token, &endptr, 10); } else if (!strcmp(count_token[match_string_index], "out_iface")) { char *endptr; request.data.ifindex_out = strtoul(match_string_token, &endptr, 10); } else if (!strcmp(count_token[match_string_index], "src_mask")) { char *endptr; u_int32_t src_mask; src_mask = strtoul(match_string_token, &endptr, 10); request.data.src_nmask = src_mask; } else if (!strcmp(count_token[match_string_index], "dst_mask")) { char *endptr; u_int32_t dst_mask; dst_mask = strtoul(match_string_token, &endptr, 10); request.data.dst_nmask = dst_mask; } else if (!strcmp(count_token[match_string_index], "src_port") || !strcmp(count_token[match_string_index], "sum_port")) { request.data.src_port = atoi(match_string_token); } else if (!strcmp(count_token[match_string_index], "dst_port")) { request.data.dst_port = atoi(match_string_token); } else if (!strcmp(count_token[match_string_index], "tos")) { tmpnum = atoi(match_string_token); request.data.tos = (u_int8_t) tmpnum; } else if (!strcmp(count_token[match_string_index], "proto")) { int proto; if (!want_ipproto_num) { for (index = 0; _protocols[index].number != -1; index++) { if (!strcmp(_protocols[index].name, match_string_token)) { proto = _protocols[index].number; break; } } if (proto <= 0) { proto = atoi(match_string_token); if ((proto <= 0) || (proto > 255)) { printf("ERROR: invalid protocol: '%s'\n", match_string_token); exit(1); } } } else { proto = atoi(match_string_token); if ((proto <= 0) || (proto > 255)) { printf("ERROR: invalid protocol: '%s'\n", match_string_token); exit(1); } } request.data.proto = proto; } else if (!strcmp(count_token[match_string_index], "none")); else if (!strcmp(count_token[match_string_index], "src_as") || !strcmp(count_token[match_string_index], "sum_as")) { char *endptr; request.data.src_as = strtoul(match_string_token, &endptr, 10); } else if (!strcmp(count_token[match_string_index], "dst_as")) { char *endptr; request.data.dst_as = strtoul(match_string_token, &endptr, 10); } else if (!strcmp(count_token[match_string_index], "tag")) { char *endptr = NULL; u_int32_t value; value = strtoull(match_string_token, &endptr, 10); request.data.id = value; } else if (!strcmp(count_token[match_string_index], "tag2")) { char *endptr = NULL; u_int32_t value; value = strtoull(match_string_token, &endptr, 10); request.data.id2 = value; } else if (!strcmp(count_token[match_string_index], "class")) { struct query_header qhdr; char sclass[MAX_PROTOCOL_LEN]; pm_class_t value = 0; memset(sclass, 0, sizeof(sclass)); strlcpy(sclass, match_string_token, MAX_PROTOCOL_LEN); sclass[MAX_PROTOCOL_LEN-1] = '\0'; if (!strcmp("unknown", sclass)) request.data.class = 0; else { memset(&qhdr, 0, sizeof(struct query_header)); qhdr.type = WANT_CLASS_TABLE; qhdr.num = 1; memcpy(clibuf, &qhdr, sizeof(struct query_header)); buflen = sizeof(struct query_header); buflen++; clibuf[buflen] = '\x4'; /* EOT */ buflen++; sd = build_query_client(path); send(sd, clibuf, buflen, 0); Recv(sd, &ct); ct_num = ((struct query_header *)ct)->num; elem = ct+sizeof(struct query_header); class_table = (struct stripped_class *) elem; ct_idx = 0; while (ct_idx < ct_num) { class_table[ct_idx].protocol[MAX_PROTOCOL_LEN-1] = '\0'; if (!strcmp(class_table[ct_idx].protocol, sclass)) { value = class_table[ct_idx].id; break; } ct_idx++; } if (!value) { printf("ERROR: Server has not loaded any classifier for '%s'.\n", sclass); exit(1); } else request.data.class = value; } } else if (!strcmp(count_token[match_string_index], "std_comm")) { if (!strcmp(match_string_token, "0")) memset(request.pbgp.std_comms, 0, MAX_BGP_STD_COMMS); else { strlcpy(request.pbgp.std_comms, match_string_token, MAX_BGP_STD_COMMS); bgp_comm = request.pbgp.std_comms; while (bgp_comm) { bgp_comm = strchr(request.pbgp.std_comms, '_'); if (bgp_comm) *bgp_comm = ' '; } } } else if (!strcmp(count_token[match_string_index], "src_std_comm")) { if (!strcmp(match_string_token, "0")) memset(request.pbgp.src_std_comms, 0, MAX_BGP_STD_COMMS); else { strlcpy(request.pbgp.src_std_comms, match_string_token, MAX_BGP_STD_COMMS); bgp_comm = request.pbgp.src_std_comms; while (bgp_comm) { bgp_comm = strchr(request.pbgp.src_std_comms, '_'); if (bgp_comm) *bgp_comm = ' '; } } } else if (!strcmp(count_token[match_string_index], "ext_comm")) { if (!strcmp(match_string_token, "0")) memset(request.pbgp.ext_comms, 0, MAX_BGP_EXT_COMMS); else { strlcpy(request.pbgp.ext_comms, match_string_token, MAX_BGP_EXT_COMMS); bgp_comm = request.pbgp.ext_comms; while (bgp_comm) { bgp_comm = strchr(request.pbgp.ext_comms, '_'); if (bgp_comm) *bgp_comm = ' '; } } } else if (!strcmp(count_token[match_string_index], "src_ext_comm")) { if (!strcmp(match_string_token, "0")) memset(request.pbgp.src_ext_comms, 0, MAX_BGP_EXT_COMMS); else { strlcpy(request.pbgp.src_ext_comms, match_string_token, MAX_BGP_EXT_COMMS); bgp_comm = request.pbgp.src_ext_comms; while (bgp_comm) { bgp_comm = strchr(request.pbgp.src_ext_comms, '_'); if (bgp_comm) *bgp_comm = ' '; } } } else if (!strcmp(count_token[match_string_index], "as_path")) { if (!strcmp(match_string_token, "^$")) memset(request.pbgp.as_path, 0, MAX_BGP_ASPATH); else { strlcpy(request.pbgp.as_path, match_string_token, MAX_BGP_ASPATH); as_path = request.pbgp.as_path; while (as_path) { as_path = strchr(request.pbgp.as_path, '_'); if (as_path) *as_path = ' '; } } } else if (!strcmp(count_token[match_string_index], "src_as_path")) { if (!strcmp(match_string_token, "^$")) memset(request.pbgp.src_as_path, 0, MAX_BGP_ASPATH); else { strlcpy(request.pbgp.src_as_path, match_string_token, MAX_BGP_ASPATH); as_path = request.pbgp.src_as_path; while (as_path) { as_path = strchr(request.pbgp.src_as_path, '_'); if (as_path) *as_path = ' '; } } } else if (!strcmp(count_token[match_string_index], "local_pref")) { char *endptr; request.pbgp.local_pref = strtoul(match_string_token, &endptr, 10); } else if (!strcmp(count_token[match_string_index], "src_local_pref")) { char *endptr; request.pbgp.src_local_pref = strtoul(match_string_token, &endptr, 10); } else if (!strcmp(count_token[match_string_index], "med")) { char *endptr; request.pbgp.med = strtoul(match_string_token, &endptr, 10); } else if (!strcmp(count_token[match_string_index], "src_med")) { char *endptr; request.pbgp.src_med = strtoul(match_string_token, &endptr, 10); } else if (!strcmp(count_token[match_string_index], "peer_src_as")) { char *endptr; request.pbgp.peer_src_as = strtoul(match_string_token, &endptr, 10); } else if (!strcmp(count_token[match_string_index], "peer_dst_as")) { char *endptr; request.pbgp.peer_dst_as = strtoul(match_string_token, &endptr, 10); } else if (!strcmp(count_token[match_string_index], "peer_src_ip")) { if (!str_to_addr(match_string_token, &request.pbgp.peer_src_ip)) { printf("ERROR: peer_src_ip: Invalid IP address: '%s'\n", match_string_token); exit(1); } } else if (!strcmp(count_token[match_string_index], "peer_dst_ip")) { if (!str_to_addr(match_string_token, &request.pbgp.peer_dst_ip)) { printf("ERROR: peer_dst_ip: Invalid IP address: '%s'\n", match_string_token); exit(1); } } else if (!strcmp(count_token[match_string_index], "mpls_vpn_rd")) { if (!pmc_bgp_str2rd((rd_t *) &request.pbgp.mpls_vpn_rd, match_string_token)) { printf("ERROR: mpls_vpn_rd: Invalid MPLS VPN RD value: '%s'\n", match_string_token); exit(1); } } else printf("WARN: ignoring unknown aggregation method: '%s'.\n", *count_token); match_string_index++; } memcpy(bufptr, &request, sizeof(struct query_entry)); bufptr += sizeof(struct query_entry); } } /* arranging header and size of buffer to send */ memcpy(clibuf, &q, sizeof(struct query_header)); buflen = sizeof(struct query_header)+(q.num*sizeof(struct query_entry)); buflen++; clibuf[buflen] = '\x4'; /* EOT */ buflen++; sd = build_query_client(path); send(sd, clibuf, buflen, 0); /* reading results */ if (want_stats || want_match) { unpacked = Recv(sd, &largebuf); if (want_all_fields) have_wtc = FALSE; else have_wtc = TRUE; what_to_count = ((struct query_header *)largebuf)->what_to_count; if (check_data_sizes((struct query_header *)largebuf, acc_elem)) exit(1); /* Before going on with the output, we need to retrieve the class strings from the server */ if (what_to_count & COUNT_CLASS && !class_table) { struct query_header qhdr; memset(&qhdr, 0, sizeof(struct query_header)); qhdr.type = WANT_CLASS_TABLE; qhdr.num = 1; memcpy(clibuf, &qhdr, sizeof(struct query_header)); buflen = sizeof(struct query_header); buflen++; clibuf[buflen] = '\x4'; /* EOT */ buflen++; sd = build_query_client(path); send(sd, clibuf, buflen, 0); Recv(sd, &ct); ct_num = ((struct query_header *)ct)->num; elem = ct+sizeof(struct query_header); class_table = (struct stripped_class *) elem; while (ct_idx < ct_num) { class_table[ct_idx].protocol[MAX_PROTOCOL_LEN-1] = '\0'; ct_idx++; } } if (what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_PEER_DST_IP| COUNT_SRC_AS_PATH|COUNT_SRC_STD_COMM|COUNT_SRC_EXT_COMM|COUNT_SRC_MED| COUNT_SRC_LOCAL_PREF|COUNT_MPLS_VPN_RD)) PbgpSz = TRUE; if (want_output == PRINT_OUTPUT_FORMATTED) write_stats_header_formatted(what_to_count, have_wtc); else if (want_output == PRINT_OUTPUT_CSV) write_stats_header_csv(what_to_count, have_wtc); elem = largebuf+sizeof(struct query_header); unpacked -= sizeof(struct query_header); acc_elem = (struct pkt_data *) elem; if (topN_counter) { int num, size; if (PbgpSz) { num = unpacked/(sizeof(struct pkt_data)+sizeof(struct pkt_bgp_primitives)); size = sizeof(struct pkt_data)+sizeof(struct pkt_bgp_primitives); } else { num = unpacked/sizeof(struct pkt_data); size = sizeof(struct pkt_data); } client_counters_merge_sort((void *)acc_elem, 0, num, size, topN_counter); } while (printed < unpacked) { acc_elem = (struct pkt_data *) elem; if (PbgpSz) pbgp = (struct pkt_bgp_primitives *) ((u_char *)elem+sizeof(struct pkt_data)); else pbgp = &empty_pbgp; if (memcmp(&acc_elem, &empty_addr, sizeof(struct pkt_primitives)) != 0 || memcmp(pbgp, &empty_pbgp, sizeof(struct pkt_bgp_primitives)) != 0) { if (!have_wtc || (what_to_count & COUNT_ID)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10llu ", acc_elem->primitives.id); else if (want_output == PRINT_OUTPUT_CSV) printf("%llu,", acc_elem->primitives.id); } if (!have_wtc || (what_to_count & COUNT_ID2)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10llu ", acc_elem->primitives.id2); else if (want_output == PRINT_OUTPUT_CSV) printf("%llu,", acc_elem->primitives.id2); } if (!have_wtc || (what_to_count & COUNT_CLASS)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-16s ", (acc_elem->primitives.class == 0 || acc_elem->primitives.class > ct_idx || !class_table[acc_elem->primitives.class-1].id) ? "unknown" : class_table[acc_elem->primitives.class-1].protocol); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", (acc_elem->primitives.class == 0 || acc_elem->primitives.class > ct_idx || !class_table[acc_elem->primitives.class-1].id) ? "unknown" : class_table[acc_elem->primitives.class-1].protocol); } if (!have_wtc || (what_to_count & COUNT_IN_IFACE)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10u ", acc_elem->primitives.ifindex_in); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.ifindex_in); } if (!have_wtc || (what_to_count & COUNT_OUT_IFACE)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10u ", acc_elem->primitives.ifindex_out); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.ifindex_out); } #if defined (HAVE_L2) if (!have_wtc || (what_to_count & (COUNT_SRC_MAC|COUNT_SUM_MAC))) { etheraddr_string(acc_elem->primitives.eth_shost, ethernet_address); if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-17s ", ethernet_address); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", ethernet_address); } if (!have_wtc || (what_to_count & COUNT_DST_MAC)) { etheraddr_string(acc_elem->primitives.eth_dhost, ethernet_address); if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-17s ", ethernet_address); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", ethernet_address); } if (!have_wtc || (what_to_count & COUNT_VLAN)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-5u ", acc_elem->primitives.vlan_id); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.vlan_id); } if (!have_wtc || (what_to_count & COUNT_COS)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-2u ", acc_elem->primitives.cos); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.cos); } if (!have_wtc || (what_to_count & COUNT_ETHERTYPE)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-5x ", acc_elem->primitives.etype); else if (want_output == PRINT_OUTPUT_CSV) printf("%x,", acc_elem->primitives.etype); } #endif if (!have_wtc || (what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS))) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10u ", acc_elem->primitives.src_as); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.src_as); } if (!have_wtc || (what_to_count & COUNT_DST_AS)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10u ", acc_elem->primitives.dst_as); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.dst_as); } /* Slightly special "!have_wtc" handling due to standard and extended BGP communities being mutual exclusive */ if ((!have_wtc && !(what_to_count & COUNT_EXT_COMM)) || (what_to_count & COUNT_STD_COMM)) { bgp_comm = pbgp->std_comms; while (bgp_comm) { bgp_comm = strchr(pbgp->std_comms, ' '); if (bgp_comm) *bgp_comm = '_'; } if (strlen(pbgp->std_comms)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22s ", pbgp->std_comms); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", pbgp->std_comms); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } } if ((!have_wtc && !(what_to_count & COUNT_SRC_EXT_COMM)) || (what_to_count & COUNT_SRC_STD_COMM)) { bgp_comm = pbgp->src_std_comms; while (bgp_comm) { bgp_comm = strchr(pbgp->src_std_comms, ' '); if (bgp_comm) *bgp_comm = '_'; } if (strlen(pbgp->src_std_comms)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22s ", pbgp->src_std_comms); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", pbgp->src_std_comms); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } } if (what_to_count & COUNT_EXT_COMM) { bgp_comm = pbgp->ext_comms; while (bgp_comm) { bgp_comm = strchr(pbgp->ext_comms, ' '); if (bgp_comm) *bgp_comm = '_'; } if (strlen(pbgp->ext_comms)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22s ", pbgp->ext_comms); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", pbgp->ext_comms); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } } if (what_to_count & COUNT_SRC_EXT_COMM) { bgp_comm = pbgp->src_ext_comms; while (bgp_comm) { bgp_comm = strchr(pbgp->src_ext_comms, ' '); if (bgp_comm) *bgp_comm = '_'; } if (strlen(pbgp->src_ext_comms)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22s ", pbgp->src_ext_comms); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", pbgp->src_ext_comms); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } } if (!have_wtc || (what_to_count & COUNT_AS_PATH)) { as_path = pbgp->as_path; while (as_path) { as_path = strchr(pbgp->as_path, ' '); if (as_path) *as_path = '_'; } if (strlen(pbgp->as_path)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22s ", pbgp->as_path); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", pbgp->as_path); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22s ", empty_aspath); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", pbgp->as_path); } } if (!have_wtc || (what_to_count & COUNT_SRC_AS_PATH)) { as_path = pbgp->src_as_path; while (as_path) { as_path = strchr(pbgp->src_as_path, ' '); if (as_path) *as_path = '_'; } if (strlen(pbgp->src_as_path)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22s ", pbgp->src_as_path); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", pbgp->src_as_path); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-22s ", empty_aspath); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", empty_aspath); } } if (!have_wtc || (what_to_count & COUNT_LOCAL_PREF)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-7u ", pbgp->local_pref); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", pbgp->local_pref); } if (!have_wtc || (what_to_count & COUNT_SRC_LOCAL_PREF)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-7u ", pbgp->src_local_pref); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", pbgp->src_local_pref); } if (!have_wtc || (what_to_count & COUNT_MED)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-6u ", pbgp->med); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", pbgp->med); } if (!have_wtc || (what_to_count & COUNT_SRC_MED)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-6u ", pbgp->src_med); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", pbgp->src_med); } if (!have_wtc || (what_to_count & COUNT_PEER_SRC_AS)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10u ", pbgp->peer_src_as); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", pbgp->peer_src_as); } if (!have_wtc || (what_to_count & COUNT_PEER_DST_AS)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10u ", pbgp->peer_dst_as); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", pbgp->peer_dst_as); } if (!have_wtc || (what_to_count & COUNT_PEER_SRC_IP)) { addr_to_str(ip_address, &pbgp->peer_src_ip); #if defined ENABLE_IPV6 if (strlen(ip_address)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-45s ", ip_address); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", ip_address); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-45u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } #else if (strlen(ip_address)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-15s ", ip_address); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", ip_address); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-15u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } #endif } if (!have_wtc || (what_to_count & COUNT_PEER_DST_IP)) { addr_to_str(ip_address, &pbgp->peer_dst_ip); #if defined ENABLE_IPV6 if (strlen(ip_address)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-45s ", ip_address); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", ip_address); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-45u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } #else if (strlen(ip_address)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-15s ", ip_address); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", ip_address); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-15u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } #endif } if (!have_wtc || (what_to_count & COUNT_MPLS_VPN_RD)) { pmc_bgp_rd2str(rd_str, (rd_t *) &pbgp->mpls_vpn_rd); if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-18s ", rd_str); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", rd_str); } if (!have_wtc || (what_to_count & (COUNT_SRC_HOST|COUNT_SUM_HOST| COUNT_SRC_NET|COUNT_SUM_NET))) { addr_to_str(ip_address, &acc_elem->primitives.src_ip); #if defined ENABLE_IPV6 if (strlen(ip_address)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-45s ", ip_address); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", ip_address); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-45u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } #else if (strlen(ip_address)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-15s ", ip_address); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", ip_address); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-15u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } #endif } if (!have_wtc || (what_to_count & (COUNT_DST_HOST|COUNT_DST_NET))) { addr_to_str(ip_address, &acc_elem->primitives.dst_ip); #if defined ENABLE_IPV6 if (strlen(ip_address)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-45s ", ip_address); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", ip_address); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-45u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } #else if (strlen(ip_address)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-15s ", ip_address); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", ip_address); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-15u ", 0); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", 0); } #endif } if (!have_wtc || (what_to_count & COUNT_SRC_NMASK)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-3u ", acc_elem->primitives.src_nmask); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.src_nmask); } if (!have_wtc || (what_to_count & COUNT_DST_NMASK)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-3u ", acc_elem->primitives.dst_nmask); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.dst_nmask); } if (!have_wtc || (what_to_count & (COUNT_SRC_PORT|COUNT_SUM_PORT))) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-5u ", acc_elem->primitives.src_port); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.src_port); } if (!have_wtc || (what_to_count & COUNT_DST_PORT)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-5u ", acc_elem->primitives.dst_port); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.dst_port); } if (!have_wtc || (what_to_count & COUNT_TCPFLAGS)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-6u ", acc_elem->tcp_flags); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->tcp_flags); } if (!have_wtc || (what_to_count & COUNT_IP_PROTO)) { if (acc_elem->primitives.proto < protocols_number && !want_ipproto_num) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10s ", _protocols[acc_elem->primitives.proto].name); else if (want_output == PRINT_OUTPUT_CSV) printf("%s,", _protocols[acc_elem->primitives.proto].name); } else { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10u ", acc_elem->primitives.proto); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.proto); } } if (!have_wtc || (what_to_count & COUNT_IP_TOS)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-3u ", acc_elem->primitives.tos); else if (want_output == PRINT_OUTPUT_CSV) printf("%u,", acc_elem->primitives.tos); } #if defined HAVE_64BIT_COUNTERS if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-20llu ", acc_elem->pkt_num); else if (want_output == PRINT_OUTPUT_CSV) printf("%llu,", acc_elem->pkt_num); if (!have_wtc || (what_to_count & COUNT_FLOWS)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-20llu ", acc_elem->flo_num); else if (want_output == PRINT_OUTPUT_CSV) printf("%llu,", acc_elem->flo_num); } printf("%llu\n", acc_elem->pkt_len); #else if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10lu ", acc_elem->pkt_num); else if (want_output == PRINT_OUTPUT_CSV) printf("%lu,", acc_elem->pkt_num); if (!have_wtc || (what_to_count & COUNT_FLOWS)) { if (want_output == PRINT_OUTPUT_FORMATTED) printf("%-10lu ", acc_elem->flo_num); else if (want_output == PRINT_OUTPUT_CSV) printf("%lu,", acc_elem->flo_num); } printf("%lu\n", acc_elem->pkt_len); #endif counter++; } if (PbgpSz) { elem += (sizeof(struct pkt_data)+sizeof(struct pkt_bgp_primitives)); printed += (sizeof(struct pkt_data)+sizeof(struct pkt_bgp_primitives)); } else { elem += sizeof(struct pkt_data); printed += sizeof(struct pkt_data); } } if (want_output == PRINT_OUTPUT_FORMATTED) printf("\nFor a total of: %d entries\n", counter); } else if (want_erase) printf("OK: Clearing stats.\n"); else if (want_status) { unpacked = Recv(sd, &largebuf); write_status_header(); elem = largebuf+sizeof(struct query_header); unpacked -= sizeof(struct query_header); while (printed < unpacked) { bd = (struct bucket_desc *) elem; printf("%u\t", bd->num); while (bd->howmany > 0) { printf("*"); bd->howmany--; } printf("\n"); elem += sizeof(struct bucket_desc); printed += sizeof(struct bucket_desc); } } else if (want_counter) { unsigned char *base; #if defined HAVE_64BIT_COUNTERS u_int64_t bcnt = 0, pcnt = 0, fcnt = 0; #else u_int32_t bcnt = 0, pcnt = 0, fcnt = 0; #endif int printed; unpacked = Recv(sd, &largebuf); base = largebuf+sizeof(struct query_header); if (check_data_sizes((struct query_header *)largebuf, acc_elem)) exit(1); acc_elem = (struct pkt_data *) base; for (printed = sizeof(struct query_header); printed < unpacked; printed += sizeof(struct pkt_data), acc_elem++) { if (sum_counters) { pcnt += acc_elem->pkt_num; fcnt += acc_elem->flo_num; bcnt += acc_elem->pkt_len; num_counters += acc_elem->time_start.tv_sec; /* XXX: this field is used here to count how much entries we are accumulating */ } else { #if defined HAVE_64BIT_COUNTERS /* print bytes */ if (which_counter == 0) printf("%llu\n", acc_elem->pkt_len); /* print packets */ else if (which_counter == 1) printf("%llu\n", acc_elem->pkt_num); /* print packets+bytes+flows+num */ else if (which_counter == 2) printf("%llu %llu %llu %lu\n", acc_elem->pkt_num, acc_elem->pkt_len, acc_elem->flo_num, acc_elem->time_start.tv_sec); /* print flows */ else if (which_counter == 3) printf("%llu\n", acc_elem->flo_num); #else if (which_counter == 0) printf("%lu\n", acc_elem->pkt_len); else if (which_counter == 1) printf("%lu\n", acc_elem->pkt_num); else if (which_counter == 2) printf("%lu %lu %lu %lu\n", acc_elem->pkt_num, acc_elem->pkt_len, acc_elem->flo_num, acc_elem->time_start.tv_sec); else if (which_counter == 3) printf("%lu\n", acc_elem->flo_num); #endif } } if (sum_counters) { #if defined HAVE_64BIT_COUNTERS if (which_counter == 0) printf("%llu\n", bcnt); /* print bytes */ else if (which_counter == 1) printf("%llu\n", pcnt); /* print packets */ else if (which_counter == 2) printf("%llu %llu %llu %u\n", pcnt, bcnt, fcnt, num_counters); /* print packets+bytes+flows+num */ else if (which_counter == 3) printf("%llu\n", fcnt); /* print flows */ #else if (which_counter == 0) printf("%lu\n", bcnt); else if (which_counter == 1) printf("%lu\n", pcnt); else if (which_counter == 2) printf("%lu %lu %lu %u\n", pcnt, bcnt, fcnt, num_counters); else if (which_counter == 3) printf("%lu\n", fcnt); #endif } } else if (want_class_table) { int ct_eff=0; Recv(sd, &ct); write_class_table_header(); ct_num = ((struct query_header *)ct)->num; elem = ct+sizeof(struct query_header); class_table = (struct stripped_class *) elem; while (ct_idx < ct_num) { class_table[ct_idx].protocol[MAX_PROTOCOL_LEN-1] = '\0'; if (class_table[ct_idx].id) { printf("%u\t\t%s\n", class_table[ct_idx].id, class_table[ct_idx].protocol); ct_eff++; } ct_idx++; } printf("\nFor a total of: %d classifiers\n", ct_eff); } else { usage_client(argv[0]); exit(1); } close(sd); return 0; } char *pmc_extract_token(char **string, int delim) { char *token, *delim_ptr; if ((delim_ptr = strchr(*string, delim))) { *delim_ptr = '\0'; token = *string; *string = delim_ptr+1; } else { token = *string; *string += strlen(*string); } return token; } int Recv(int sd, unsigned char **buf) { int num, unpacked = 0, round = 0; unsigned char rxbuf[LARGEBUFLEN], *elem; *buf = (unsigned char *) malloc(LARGEBUFLEN); memset(*buf, 0, LARGEBUFLEN); memset(rxbuf, 0, LARGEBUFLEN); do { num = recv(sd, rxbuf, LARGEBUFLEN, 0); if (num > 0) { /* check 1: enough space in allocated buffer */ if (unpacked+num >= round*LARGEBUFLEN) { round++; *buf = realloc((unsigned char *) *buf, round*LARGEBUFLEN); if (!(*buf)) { printf("ERROR: realloc() out of memory\n"); exit(1); } /* ensuring realloc() didn't move somewhere else our memory area */ elem = *buf; elem += unpacked; } /* check 2: enough space in dss */ if (((u_int32_t)elem+num) > (u_int32_t)sbrk(0)) sbrk(LARGEBUFLEN); memcpy(elem, rxbuf, num); unpacked += num; elem += num; } } while (num > 0); return unpacked; } int check_data_sizes(struct query_header *qh, struct pkt_data *acc_elem) { if (qh->cnt_sz != sizeof(acc_elem->pkt_len)) { printf("ERROR: Counter sizes mismatch: daemon: %d client: %d\n", qh->cnt_sz*8, sizeof(acc_elem->pkt_len)*8); printf("ERROR: It's very likely that a 64bit package has been mixed with a 32bit one.\n\n"); printf("ERROR: Please fix the issue before trying again.\n"); return (qh->cnt_sz-sizeof(acc_elem->pkt_len)); } if (qh->ip_sz != sizeof(acc_elem->primitives.src_ip)) { printf("ERROR: IP address sizes mismatch. daemon: %d client: %d\n", qh->ip_sz, sizeof(acc_elem->primitives.src_ip)); printf("ERROR: It's very likely that an IPv6-enabled package has been mixed with a IPv4-only one.\n\n"); printf("ERROR: Please fix the issue before trying again.\n"); return (qh->ip_sz-sizeof(acc_elem->primitives.src_ip)); } return 0; } /* sort the (sub)array v from start to end */ void client_counters_merge_sort(void *table, int start, int end, int size, int order) { int middle; /* no elements to sort */ if ((start == end) || (start == end-1)) return; /* find the middle of the array, splitting it into two subarrays */ middle = (start+end)/2; /* sort the subarray from start..middle */ client_counters_merge_sort(table, start, middle, size, order); /* sort the subarray from middle..end */ client_counters_merge_sort(table, middle, end, size, order); /* merge the two sorted halves */ client_counters_merge(table, start, middle, end, size, order); } /* merge the subarray v[start..middle] with v[middle..end], placing the result back into v. */ void client_counters_merge(void *table, int start, int middle, int end, int size, int order) { void *v1, *v2; int v1_n, v2_n, v1_index, v2_index, i, s = size; struct pkt_data data1, data2; v1_n = middle-start; v2_n = end-middle; v1 = malloc(v1_n*s); v2 = malloc(v2_n*s); if ((!v1) || (!v2)) printf("ERROR: Memory sold out while sorting statistics.\n"); for (i=0; itype) { case RD_TYPE_AS: rda = (struct rd_as *) rd; sprintf(str, "%u:%u:%u", rda->type, rda->as, rda->val); break; case RD_TYPE_IP: rdi = (struct rd_ip *) rd; a.family = AF_INET; a.address.ipv4.s_addr = rdi->ip.s_addr; addr_to_str(ip_address, &a); sprintf(str, "%u:%s:%u", rdi->type, ip_address, rdi->val); break; case RD_TYPE_AS4: rda4 = (struct rd_as4 *) rd; sprintf(str, "%u:%u:%u", rda4->type, rda4->as, rda4->val); break; default: sprintf(str, "unknown"); break; } } int pmc_bgp_str2rd(rd_t *output, char *value) { struct host_addr a; char *endptr, *token; u_int32_t tmp32; u_int16_t tmp16; struct rd_ip *rdi; struct rd_as *rda; struct rd_as4 *rda4; int idx = 0; rd_t rd; memset(&a, 0, sizeof(a)); memset(&rd, 0, sizeof(rd)); /* type:RD_subfield1:RD_subfield2 */ while ( (token = pmc_extract_token(&value, ':')) && idx < 3) { if (idx == 0) { tmp32 = strtoul(token, &endptr, 10); rd.type = tmp32; switch (rd.type) { case RD_TYPE_AS: rda = (struct rd_as *) &rd; break; case RD_TYPE_IP: rdi = (struct rd_ip *) &rd; break; case RD_TYPE_AS4: rda4 = (struct rd_as4 *) &rd; break; default: printf("ERROR: Invalid RD type specified\n"); return FALSE; } } if (idx == 1) { switch (rd.type) { case RD_TYPE_AS: tmp32 = strtoul(token, &endptr, 10); rda->as = tmp32; break; case RD_TYPE_IP: memset(&a, 0, sizeof(a)); str_to_addr(token, &a); if (a.family == AF_INET) rdi->ip.s_addr = a.address.ipv4.s_addr; break; case RD_TYPE_AS4: tmp32 = strtoul(token, &endptr, 10); rda4->as = tmp32; break; } } if (idx == 2) { switch (rd.type) { case RD_TYPE_AS: tmp32 = strtoul(token, &endptr, 10); rda->val = tmp32; break; case RD_TYPE_IP: tmp32 = strtoul(token, &endptr, 10); rdi->val = tmp32; break; case RD_TYPE_AS4: tmp32 = strtoul(token, &endptr, 10); rda4->val = tmp32; break; } } idx++; } memcpy(output, &rd, sizeof(rd)); return TRUE; } pmacct-0.14.0/src/preprocess.c0000644000175000017500000004143011250743543015177 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente */ /* 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. */ #define __PREPROCESS_C #include "pmacct.h" #include "pmacct-data.h" #include "sql_common.h" void set_preprocess_funcs(char *string, struct preprocess *prep) { char *token, *sep, *key, *value; int j = 0; memset(preprocess_funcs, 0, sizeof(preprocess_funcs)); memset(prep, 0, sizeof(struct preprocess)); if (!string) return; trim_all_spaces(string); while (token = extract_token(&string, ',')) { sep = strchr(token, '='); if (!sep) { Log(LOG_WARNING, "WARN ( %s/%s ): Malformed preprocess string. Discarded.\n", config.name, config.type); return; } else { key = token; *sep = '\0'; value = sep+1; } if (!strcmp(key, "qnum")) { prep->qnum = atoi(value); if (!prep->qnum) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'qnum' value.\n", config.name, config.type); } else if (!strcmp(key, "minp")) { prep->minp = atoi(value); if (!prep->minp) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'minp' value.\n", config.name, config.type); } else if (!strcmp(key, "minf")) { prep->minf = atoi(value); if (!prep->minf) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'minf' value.\n", config.name, config.type); } else if (!strcmp(key, "minb")) { prep->minb = atoi(value); if (!prep->minb) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'minb' value.\n", config.name, config.type); } else if (!strcmp(key, "maxp")) { prep->maxp = atoi(value); if (!prep->maxp) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'maxp' value.\n", config.name, config.type); } else if (!strcmp(key, "maxf")) { prep->maxf = atoi(value); if (!prep->maxf) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'maxf' value.\n", config.name, config.type); } else if (!strcmp(key, "maxb")) { prep->maxb = atoi(value); if (!prep->maxb) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'maxb' value.\n", config.name, config.type); } else if (!strcmp(key, "maxbpp")) { prep->maxbpp = atoi(value); if (!prep->maxbpp) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'maxbpp' value.\n", config.name, config.type); } else if (!strcmp(key, "maxppf")) { prep->maxppf = atoi(value); if (!prep->maxppf) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'maxppf' value.\n", config.name, config.type); } else if (!strcmp(key, "minbpp")) { prep->minbpp = atoi(value); if (!prep->minbpp) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'minbpp' value.\n", config.name, config.type); } else if (!strcmp(key, "minppf")) { prep->minppf = atoi(value); if (!prep->minppf) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'minppf' value.\n", config.name, config.type); } else if (!strcmp(key, "fss")) { prep->fss = atoi(value); if (!prep->fss) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'fss' value.\n", config.name, config.type); } else if (!strcmp(key, "fsrc")) { prep->fsrc = atoi(value); if (!prep->fsrc) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'fsrc' value.\n", config.name, config.type); else { fsrc_queue.num = 0; memset(&fsrc_queue.head, 0, sizeof(struct fsrc_queue_elem)); } } else if (!strcmp(key, "usrf")) { prep->usrf = atoi(value); if (!prep->usrf) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'usrf' value.\n", config.name, config.type); } else if (!strcmp(key, "adjb")) { prep->adjb = atoi(value); if (!prep->adjb) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'adjb' value.\n", config.name, config.type); } else if (!strcmp(key, "recover")) { prep->recover = atoi(value); if (!prep->recover) Log(LOG_WARNING, "WARN ( %s/%s ): preprocess: Invalid 'recover' value.\n", config.name, config.type); } else Log(LOG_ERR, "ERROR ( %s/%s ): Invalid preprocess key: '%s'. Ignored.\n", config.name, config.type, key); } /* Post checks: almost one check should have been specified */ if ((!prep->minp) && (!prep->minb) && (!prep->minf) && (!prep->maxp) && (!prep->maxb) && (!prep->maxf) && (!prep->maxbpp) && (!prep->maxppf) && (!prep->minbpp) && (!prep->minppf) && (!prep->fss) && (!prep->fsrc) && (!prep->usrf) && (!prep->adjb)) { Log(LOG_ERR, "ERROR ( %s/%s ): 'sql_preprocess' does not contain any check. Ignored.\n", config.name, config.type); return; } /* 1st step: insert conditionals */ if (prep->qnum) { preprocess_funcs[j] = cond_qnum; j++; } /* 2nd step: invalidation of committed cache entries - if at least one check was specified; each check will selectively re-validate entries that pass tests successfully */ preprocess_funcs[j] = mandatory_invalidate; j++; /* 3rd step: insert checks */ if (prep->minp) { preprocess_funcs[j] = check_minp; prep->num++; j++; prep->checkno++; } if (prep->minf) { preprocess_funcs[j] = check_minf; prep->num++; j++; prep->checkno++; } if (prep->minb) { preprocess_funcs[j] = check_minb; prep->num++; j++; prep->checkno++; } if (prep->maxp) { preprocess_funcs[j] = check_maxp; prep->num++; j++; prep->checkno++; } if (prep->maxf) { preprocess_funcs[j] = check_maxf; prep->num++; j++; prep->checkno++; } if (prep->maxb) { preprocess_funcs[j] = check_maxb; prep->num++; j++; prep->checkno++; } if (prep->maxbpp) { preprocess_funcs[j] = check_maxbpp; prep->num++; j++; prep->checkno++; } if (prep->maxppf) { preprocess_funcs[j] = check_maxppf; prep->num++; j++; prep->checkno++; } if (prep->minbpp) { preprocess_funcs[j] = check_minbpp; prep->num++; j++; prep->checkno++; } if (prep->minppf) { preprocess_funcs[j] = check_minppf; prep->num++; j++; prep->checkno++; } if (prep->fss) { preprocess_funcs[j] = check_fss; prep->num++; j++; prep->checkno++; } if (prep->fsrc) { preprocess_funcs[j] = check_fsrc; prep->num++; j++; prep->checkno++; } if (prep->usrf) { preprocess_funcs[j] = action_usrf; prep->num++; j++; prep->actionno++; } if (prep->adjb) { preprocess_funcs[j] = action_adjb; prep->num++; j++; prep->actionno++; } /* 4th and final step: check points: - if in 'any' mode, any entry with 'points >= 1' is valid - if in 'all' mode, any entry with 'points == number of conditions' is valid */ preprocess_funcs[j] = mandatory_validate; j++; } void check_validity(struct db_cache *entry, int seq) { if (config.sql_preprocess_type == 0) { if (entry->prep_valid > 0 && entry->valid == SQL_CACHE_FREE) entry->valid = SQL_CACHE_COMMITTED; } else { if (entry->prep_valid == seq) entry->valid = SQL_CACHE_COMMITTED; else entry->valid = SQL_CACHE_FREE; } } int cond_qnum(struct db_cache *queue[], int *num, int seq) { if (*num > prep.qnum) return FALSE; else return TRUE; } int check_minp(struct db_cache *queue[], int *num, int seq) { int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { if (queue[x]->packet_counter >= prep.minp) queue[x]->prep_valid++; check_validity(queue[x], seq); } } return FALSE; } int check_minb(struct db_cache *queue[], int *num, int seq) { int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { if (queue[x]->bytes_counter >= prep.minb) queue[x]->prep_valid++; check_validity(queue[x], seq); } } return FALSE; } int check_minf(struct db_cache *queue[], int *num, int seq) { int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { if (queue[x]->flows_counter >= prep.minf) queue[x]->prep_valid++; check_validity(queue[x], seq); } } return FALSE; } int check_maxp(struct db_cache *queue[], int *num, int seq) { int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { if (queue[x]->packet_counter < prep.maxp) queue[x]->prep_valid++; check_validity(queue[x], seq); } } return FALSE; } int check_maxb(struct db_cache *queue[], int *num, int seq) { int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { if (queue[x]->bytes_counter < prep.maxb) queue[x]->prep_valid++; check_validity(queue[x], seq); } } return FALSE; } int check_maxf(struct db_cache *queue[], int *num, int seq) { int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { if (queue[x]->flows_counter < prep.maxf) queue[x]->prep_valid++; check_validity(queue[x], seq); } } return FALSE; } int check_maxbpp(struct db_cache *queue[], int *num, int seq) { int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { if (queue[x]->bytes_counter/queue[x]->packet_counter < prep.maxbpp) queue[x]->prep_valid++; check_validity(queue[x], seq); } } return FALSE; } int check_maxppf(struct db_cache *queue[], int *num, int seq) { int x; if (!queue[0]->flows_counter) return FALSE; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { if (queue[x]->packet_counter/queue[x]->flows_counter < prep.maxppf) queue[x]->prep_valid++; check_validity(queue[x], seq); } } return FALSE; } int check_minbpp(struct db_cache *queue[], int *num, int seq) { int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { if (queue[x]->bytes_counter/queue[x]->packet_counter >= prep.minbpp) queue[x]->prep_valid++; check_validity(queue[x], seq); } } return FALSE; } int check_minppf(struct db_cache *queue[], int *num, int seq) { int x; if (!queue[0]->flows_counter) return FALSE; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { if (queue[x]->packet_counter/queue[x]->flows_counter >= prep.minppf) queue[x]->prep_valid++; check_validity(queue[x], seq); } } return FALSE; } int check_fss(struct db_cache *queue[], int *num, int seq) { u_int32_t t = prep.fss; /* threshold */ float p = 0 /* probability */, res; u_int16_t bpratio; int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { res = (float) queue[x]->bytes_counter/t; if (res < 1) p += res; if (p >= 1 || res >= 1) { queue[x]->prep_valid++; if (queue[x]->bytes_counter < t) { bpratio = queue[x]->bytes_counter/queue[x]->packet_counter; queue[x]->bytes_counter = t; queue[x]->packet_counter = queue[x]->bytes_counter/bpratio; /* hmmm */ } if (p >= 1) p -= 1; } check_validity(queue[x], seq); } } return FALSE; } /* This is an initial implementation and any advice is welcome: - seed: microseconds value returned by the gettimeofday() call - random value: high-order bits returned by the random() call */ int check_fsrc(struct db_cache *queue[], int *num, int seq) { struct fsrc_queue_elem *ptr, *last_seen, *new; struct timeval tv; struct timezone tz; float w /* random variable */, z; u_int32_t max = prep.fsrc+1; /* maximum number of allowed flows */ int x, queueElemSz = sizeof(struct fsrc_queue_elem); u_int16_t bpratio; u_int32_t total = 0, subtotal = 0; /* no need to sample */ if (*num <= prep.fsrc) { for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { queue[x]->prep_valid++; check_validity(queue[x], seq); } } goto end; } /* 1st stage: computing the m+1==max flows with highest z */ for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_FREE || queue[x]->valid == SQL_CACHE_COMMITTED) { gettimeofday(&tv, &tz); srandom((unsigned int)tv.tv_usec); w = (float) (random()/(RAND_MAX+1.0)); z = (float) queue[x]->bytes_counter/w; ptr = &fsrc_queue.head; while (z > ptr->z) { last_seen = ptr; if (ptr->next) ptr = ptr->next; else break; } if (fsrc_queue.num < max) { new = malloc(queueElemSz); fsrc_queue.num++; new->next = last_seen->next; last_seen->next = new; } else { if (last_seen == &fsrc_queue.head) continue; new = fsrc_queue.head.next; if (last_seen != fsrc_queue.head.next) { fsrc_queue.head.next = new->next; new->next = last_seen->next; last_seen->next = new; } } new->cache_ptr = queue[x]; new->z = z; total += queue[x]->bytes_counter; } } /* 2nd stage + 3rd stage: - validating the highest m flows - renormalizing the highest m flows: Xi(bytes_counter) = { max[ Xi(bytes_counter), Xm+1(z) ]: i = 1,...,m } */ for (ptr = fsrc_queue.head.next->next; ptr; ptr = ptr->next) { ptr->cache_ptr->prep_valid++; if (ptr->cache_ptr->bytes_counter < fsrc_queue.head.next->z) { bpratio = ptr->cache_ptr->bytes_counter/ptr->cache_ptr->packet_counter; ptr->cache_ptr->bytes_counter = fsrc_queue.head.next->z; ptr->cache_ptr->packet_counter = ptr->cache_ptr->bytes_counter/bpratio; /* hmmm */ } subtotal += ptr->cache_ptr->bytes_counter; check_validity(ptr->cache_ptr, seq); } if (config.debug) Log(LOG_DEBUG, "DEBUG: TOT/%u/%u SUBTOT/%u/%u\n", *num, total, fsrc_queue.num-1, subtotal); end: return FALSE; } int action_usrf(struct db_cache *queue[], int *num, int seq) { u_int32_t r = prep.usrf; /* renormalization factor */ u_int16_t bpratio; int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_COMMITTED) { bpratio = queue[x]->bytes_counter/queue[x]->packet_counter; queue[x]->bytes_counter = queue[x]->bytes_counter*r; queue[x]->packet_counter = queue[x]->bytes_counter/bpratio; /* hmmm */ } } return FALSE; } int action_adjb(struct db_cache *queue[], int *num, int seq) { int x; for (x = 0; x < *num; x++) { if (queue[x]->valid == SQL_CACHE_COMMITTED) { queue[x]->bytes_counter += (queue[x]->packet_counter * prep.adjb); } } return FALSE; } int mandatory_invalidate(struct db_cache *queue[], int *num, int seq) { int x; /* Two validation mechanisms are used: if ALL checks have to be successful, prep_valid is a) initializated to a base value, b) incremented at every test concluding positively and c) checked for prep_valid == seq; if instead ANY check has to be successful, a) prep_valid is initializeted to zero, b) is brought to a positive value by the first positive test and c) finally checked for a non-zero value */ for (x = 0; x < *num; x++) { if (config.sql_preprocess_type == 0) queue[x]->prep_valid = 0; else queue[x]->prep_valid = seq; if (prep.checkno && queue[x]->valid == SQL_CACHE_COMMITTED) queue[x]->valid = SQL_CACHE_FREE; } return FALSE; } /* - 'sql_preprocess_type == 0' means match 'any' of the checks - 'sql_preprocess_type == 1' means match 'all' checks - queue[x]->valid floor value is 2 (SQL_CACHE_COMMITTED) */ int mandatory_validate(struct db_cache *queue[], int *num, int seq) { int x; for (x = 0; x < *num; x++) { // if (!prep.checkno) queue[x]->valid = SQL_CACHE_INUSE; // if (config.sql_preprocess_type == 1 && (queue[x]->valid-1) < (prep.num-prep.actionno)) // queue[x]->valid = SQL_CACHE_FREE; if (queue[x]->valid == SQL_CACHE_FREE && prep.recover) queue[x]->valid = SQL_CACHE_ERROR; } return FALSE; } pmacct-0.14.0/src/regsub.c0000644000175000017500000000420211027472602014272 0ustar paolopaolo/* * regsub * @(#)regsub.c 1.3 of 2 April 86 * * Copyright (c) 1986 by University of Toronto. * Written by Henry Spencer. Not derived from licensed software. * * Permission is granted to anyone to use this software for any * purpose on any computer system, and to redistribute it freely, * subject to the following restrictions: * * 1. The author is not responsible for the consequences of use of * this software, no matter how awful, even if they arise * from defects in it. * * 2. The origin of this software must not be misrepresented, either * by explicit claim or by omission. * * 3. Altered versions must be plainly marked as such, and must not * be misrepresented as being the original software. * * * This code was modified by Ethan Sommer to work within the kernel * (it now uses kmalloc etc..) * */ #include "pmacct.h" #include "pmacct-data.h" #include "regexp.h" #include "regmagic.h" #ifndef CHARBITS #define UCHARAT(p) ((int)*(unsigned char *)(p)) #else #define UCHARAT(p) ((int)*(p)&CHARBITS) #endif /* - regsub - perform substitutions after a regexp match */ void pm_regsub(regexp * prog, char * source, char * dest) { register char *src; register char *dst; register char c; register int no; register int len; /* Not necessary and gcc doesn't like it -MLS */ /*extern char *strncpy();*/ if (prog == NULL || source == NULL || dest == NULL) { pm_regerror("NULL parm to regsub"); return; } if (UCHARAT(prog->program) != MAGIC) { pm_regerror("damaged regexp fed to regsub"); return; } src = source; dst = dest; while ((c = *src++) != '\0') { if (c == '&') no = 0; else if (c == '\\' && '0' <= *src && *src <= '9') no = *src++ - '0'; else no = -1; if (no < 0) { /* Ordinary character. */ if (c == '\\' && (*src == '\\' || *src == '&')) c = *src++; *dst++ = c; } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { len = prog->endp[no] - prog->startp[no]; (void) strncpy(dst, prog->startp[no], len); dst += len; if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ pm_regerror("damaged match string"); return; } } } *dst++ = '\0'; } pmacct-0.14.0/src/pmacct-bpf.h0000644000175000017500000001342411147231147015032 0ustar paolopaolo/*- * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * * This code is derived from the Stanford/CMU enet packet filter, * (net/enet.c) distributed as part of 4.3BSD, and code contributed * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence * Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)bpf.h 7.1 (Berkeley) 5/7/91 * * @(#) $Header: /home/repo-0.14/pmacct/src/pmacct-bpf.h,v 1.1.1.1 2009/02/19 10:20:23 paolo Exp $ (LBL) */ /* * This is a cut-down version of libpcap bpf.h; it includes only * the stuff needed for the code generator and the userland BPF * interpreter, and the libpcap APIs for setting filters, etc.. * * "pcap-bpf.c" will include the native OS version, as it deals with * the OS's BPF implementation. */ #ifndef BPF_MAJOR_VERSION #ifdef __cplusplus extern "C" { #endif /* BSD style release date */ #define BPF_RELEASE 199606 typedef int bpf_int32; typedef u_int bpf_u_int32; /* * Alignment macros. BPF_WORDALIGN rounds up to the next * even multiple of BPF_ALIGNMENT. */ #ifndef __NetBSD__ #define BPF_ALIGNMENT sizeof(bpf_int32) #else #define BPF_ALIGNMENT sizeof(long) #endif #define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) #define BPF_MAXINSNS 512 #define BPF_MAXBUFSIZE 0x8000 #define BPF_MINBUFSIZE 32 /* * Structure for "pcap_compile()", "pcap_setfilter()", etc.. */ struct bpf_program { u_int bf_len; struct bpf_insn *bf_insns; }; /* * Struct return by BIOCVERSION. This represents the version number of * the filter language described by the instruction encodings below. * bpf understands a program iff kernel_major == filter_major && * kernel_minor >= filter_minor, that is, if the value returned by the * running kernel has the same major number and a minor number equal * equal to or less than the filter being downloaded. Otherwise, the * results are undefined, meaning an error may be returned or packets * may be accepted haphazardly. * It has nothing to do with the source code version. */ struct bpf_version { u_short bv_major; u_short bv_minor; }; /* Current version number of filter architecture. */ #define BPF_MAJOR_VERSION 1 #define BPF_MINOR_VERSION 1 #include "pmacct-dlt.h" /* * The instruction encodings. */ /* instruction classes */ #define BPF_CLASS(code) ((code) & 0x07) #define BPF_LD 0x00 #define BPF_LDX 0x01 #define BPF_ST 0x02 #define BPF_STX 0x03 #define BPF_ALU 0x04 #define BPF_JMP 0x05 #define BPF_RET 0x06 #define BPF_MISC 0x07 /* ld/ldx fields */ #define BPF_SIZE(code) ((code) & 0x18) #define BPF_W 0x00 #define BPF_H 0x08 #define BPF_B 0x10 #define BPF_MODE(code) ((code) & 0xe0) #define BPF_IMM 0x00 #define BPF_ABS 0x20 #define BPF_IND 0x40 #define BPF_MEM 0x60 #define BPF_LEN 0x80 #define BPF_MSH 0xa0 /* alu/jmp fields */ #define BPF_OP(code) ((code) & 0xf0) #define BPF_ADD 0x00 #define BPF_SUB 0x10 #define BPF_MUL 0x20 #define BPF_DIV 0x30 #define BPF_OR 0x40 #define BPF_AND 0x50 #define BPF_LSH 0x60 #define BPF_RSH 0x70 #define BPF_NEG 0x80 #define BPF_JA 0x00 #define BPF_JEQ 0x10 #define BPF_JGT 0x20 #define BPF_JGE 0x30 #define BPF_JSET 0x40 #define BPF_SRC(code) ((code) & 0x08) #define BPF_K 0x00 #define BPF_X 0x08 /* ret - BPF_K and BPF_X also apply */ #define BPF_RVAL(code) ((code) & 0x18) #define BPF_A 0x10 /* misc */ #define BPF_MISCOP(code) ((code) & 0xf8) #define BPF_TAX 0x00 #define BPF_TXA 0x80 /* * The instruction data structure. */ struct bpf_insn { u_short code; u_char jt; u_char jf; bpf_int32 k; }; /* * Macros for insn array initializers. */ #define BPF_STMT(code, k) { (u_short)(code), 0, 0, k } #define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } #if __STDC__ || defined(__cplusplus) extern int bpf_validate(struct bpf_insn *, int); extern u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int); #else extern int bpf_validate(); extern u_int bpf_filter(); #endif /* * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). */ #define BPF_MEMWORDS 16 #ifdef __cplusplus } #endif #endif pmacct-0.14.0/src/uacctd.h0000640000175000017500000000165711640430152014255 0ustar paolopaolo#ifdef ENABLE_ULOG /* Linux NetFilter ULOG stuff */ #include #include #include #include #include #include #define ULOG_BUFLEN 10480 /*should be enough room up to 9K Ethernet jumbo frames */ #define DEFAULT_ULOG_GROUP 1 #define IFCACHE_HASHSIZ 32 #define IFCACHE_LIFETIME 15 /* seconds */ #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif #ifndef NETLINK_NO_ENOBUFS #define NETLINK_NO_ENOBUFS 5 #endif struct ifname_cache { struct ifname_cache *next; unsigned long tstamp; unsigned int index; char name[IFNAMSIZ]; }; /* functions */ #if (!defined __UACCTD_C) #define EXT extern #else #define EXT EXT unsigned int get_ifindex(char *); EXT unsigned int cache_ifindex(char *, unsigned long); EXT unsigned int hash_ifname(char *); EXT struct ifname_cache *hash_heads[IFCACHE_HASHSIZ]; #endif #undef EXT #endif pmacct-0.14.0/src/nl.c0000640000175000017500000004160311737110634013420 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* defines */ #define __NL_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "pmacct-dlt.h" #include "pretag_handlers.h" #include "plugin_hooks.h" #include "pkt_handlers.h" #include "ip_frag.h" #include "ip_flow.h" #include "net_aggr.h" #include "thread_pool.h" void pcap_cb(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *buf) { struct packet_ptrs pptrs; struct pcap_callback_data *cb_data = (struct pcap_callback_data *) user; struct pcap_device *device = cb_data->device; struct plugin_requests req; /* We process the packet with the appropriate data link layer function */ if (buf) { pptrs.pkthdr = (struct pcap_pkthdr *) pkthdr; pptrs.packet_ptr = (u_char *) buf; pptrs.mac_ptr = 0; pptrs.vlan_ptr = 0; pptrs.mpls_ptr = 0; pptrs.pf = 0; pptrs.shadow = 0; pptrs.tag = 0; pptrs.tag2 = 0; pptrs.class = 0; pptrs.bpas = 0, pptrs.bta = 0; pptrs.blp = 0; pptrs.bmed = 0; pptrs.bitr = 0; pptrs.tun_layer = 0; pptrs.tun_stack = 0; pptrs.f_agent = cb_data->f_agent; pptrs.idtable = cb_data->idt; pptrs.bpas_table = cb_data->bpas_table; pptrs.blp_table = cb_data->blp_table; pptrs.bmed_table = cb_data->bmed_table; pptrs.bta_table = cb_data->bta_table; pptrs.ifindex_in = cb_data->ifindex_in; pptrs.ifindex_out = cb_data->ifindex_out; pptrs.f_status = NULL; (*device->data->handler)(pkthdr, &pptrs); if (pptrs.iph_ptr) { if ((*pptrs.l3_handler)(&pptrs)) { if (config.nfacctd_isis) { isis_srcdst_lookup(&pptrs); } if (config.nfacctd_bgp) { PM_find_id((struct id_table *)pptrs.bta_table, &pptrs, &pptrs.bta, NULL); bgp_srcdst_lookup(&pptrs); } if (config.nfacctd_bgp_peer_as_src_map) PM_find_id((struct id_table *)pptrs.bpas_table, &pptrs, &pptrs.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) PM_find_id((struct id_table *)pptrs.blp_table, &pptrs, &pptrs.blp, NULL); if (config.nfacctd_bgp_src_med_map) PM_find_id((struct id_table *)pptrs.bmed_table, &pptrs, &pptrs.bmed, NULL); if (config.pre_tag_map) PM_find_id((struct id_table *)pptrs.idtable, &pptrs, &pptrs.tag, &pptrs.tag2); exec_plugins(&pptrs); } } } if (reload_map) { load_networks(config.networks_file, &nt, &nc); if (config.nfacctd_bgp && config.nfacctd_bgp_peer_as_src_map) load_id_file(MAP_BGP_PEER_AS_SRC, config.nfacctd_bgp_peer_as_src_map, (struct id_table *)cb_data->bpas_table, &req, &bpas_map_allocated); if (config.nfacctd_bgp && config.nfacctd_bgp_src_local_pref_map) load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.nfacctd_bgp_src_local_pref_map, (struct id_table *)cb_data->blp_table, &req, &blp_map_allocated); if (config.nfacctd_bgp && config.nfacctd_bgp_src_med_map) load_id_file(MAP_BGP_SRC_MED, config.nfacctd_bgp_src_med_map, (struct id_table *)cb_data->bmed_table, &req, &bmed_map_allocated); if (config.nfacctd_bgp) load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.nfacctd_bgp_to_agent_map, (struct id_table *)cb_data->bta_table, &req, &bta_map_allocated); if (config.pre_tag_map) load_id_file(config.acct_type, config.pre_tag_map, (struct id_table *) pptrs.idtable, &req, &tag_map_allocated); reload_map = FALSE; } } int ip_handler(register struct packet_ptrs *pptrs) { register u_int8_t len = 0; register u_int16_t caplen = ((struct pcap_pkthdr *)pptrs->pkthdr)->caplen; register unsigned char *ptr; register u_int16_t off = pptrs->iph_ptr-pptrs->packet_ptr, off_l4; int ret = TRUE, num, is_fragment = 0; /* len: number of 32bit words forming the header */ len = IP_HL(((struct my_iphdr *) pptrs->iph_ptr)); len <<= 2; ptr = pptrs->iph_ptr+len; off += len; /* check len */ if (off > caplen) return FALSE; /* IP packet truncated */ pptrs->l4_proto = ((struct my_iphdr *)pptrs->iph_ptr)->ip_p; pptrs->payload_ptr = NULL; off_l4 = off; /* check fragments if needed */ if (config.handle_fragments) { if (pptrs->l4_proto == IPPROTO_TCP || pptrs->l4_proto == IPPROTO_UDP) { if (off+MyTLHdrSz > caplen) { Log(LOG_INFO, "INFO ( default/core ): short IPv4 packet read (%u/%u/frags). Snaplen issue ?\n", caplen, off+MyTLHdrSz); return FALSE; } pptrs->tlh_ptr = ptr; if (((struct my_iphdr *)pptrs->iph_ptr)->ip_off & htons(IP_MF|IP_OFFMASK)) { is_fragment = TRUE; ret = ip_fragment_handler(pptrs); if (!ret) { if (!config.ext_sampling_rate) goto quit; else { pptrs->tlh_ptr = dummy_tlhdr; pptrs->tcp_flags = FALSE; if (off < caplen) pptrs->payload_ptr = ptr; ret = TRUE; goto quit; } } } /* Let's handle both fragments and packets. If we are facing any subsequent frag our pointer is in place; we handle unknown L4 protocols likewise. In case of "entire" TCP/UDP packets we have to jump the L4 header instead */ if (((struct my_iphdr *)pptrs->iph_ptr)->ip_off & htons(IP_OFFMASK)); else if (pptrs->l4_proto == IPPROTO_UDP) { ptr += UDPHdrSz; off += UDPHdrSz; } else if (pptrs->l4_proto == IPPROTO_TCP) { ptr += ((struct my_tcphdr *)pptrs->tlh_ptr)->th_off << 2; off += ((struct my_tcphdr *)pptrs->tlh_ptr)->th_off << 2; } if (off < caplen) pptrs->payload_ptr = ptr; } else { pptrs->tlh_ptr = dummy_tlhdr; if (off < caplen) pptrs->payload_ptr = ptr; } if (config.handle_flows) { pptrs->tcp_flags = FALSE; if (pptrs->l4_proto == IPPROTO_TCP) { if (off_l4+TCPFlagOff+1 > caplen) { Log(LOG_INFO, "INFO ( default/core ): short IPv4 packet read (%u/%u/flows). Snaplen issue ?\n", caplen, off_l4+TCPFlagOff+1); return FALSE; } if (((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_SYN) pptrs->tcp_flags |= TH_SYN; if (((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_FIN) pptrs->tcp_flags |= TH_FIN; if (((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_RST) pptrs->tcp_flags |= TH_RST; if (((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_ACK && pptrs->tcp_flags) pptrs->tcp_flags |= TH_ACK; } ip_flow_handler(pptrs); } /* XXX: optimize/short circuit here! */ pptrs->tcp_flags = FALSE; if (pptrs->l4_proto == IPPROTO_TCP && off_l4+TCPFlagOff+1 <= caplen) pptrs->tcp_flags = ((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags; /* tunnel handlers here */ if (config.tunnel0 && !pptrs->tun_stack) { for (num = 0; pptrs->payload_ptr && !is_fragment && tunnel_registry[0][num].tf; num++) { if (tunnel_registry[0][num].proto == pptrs->l4_proto) { if (!tunnel_registry[0][num].port || (pptrs->tlh_ptr && tunnel_registry[0][num].port == ntohs(((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port))) { pptrs->tun_stack = num; ret = (*tunnel_registry[0][num].tf)(pptrs); } } } } else if (pptrs->tun_stack) { if (tunnel_registry[pptrs->tun_stack][pptrs->tun_layer].proto == pptrs->l4_proto) { if (!tunnel_registry[pptrs->tun_stack][pptrs->tun_layer].port || (pptrs->tlh_ptr && tunnel_registry[pptrs->tun_stack][pptrs->tun_layer].port == ntohs(((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port))) { ret = (*tunnel_registry[pptrs->tun_stack][pptrs->tun_layer].tf)(pptrs); } } } } quit: return ret; } #if defined ENABLE_IPV6 int ip6_handler(register struct packet_ptrs *pptrs) { struct ip6_frag *fhdr = NULL; register u_int16_t caplen = ((struct pcap_pkthdr *)pptrs->pkthdr)->caplen; u_int16_t len = 0, plen = ntohs(((struct ip6_hdr *)pptrs->iph_ptr)->ip6_plen); u_int16_t off = pptrs->iph_ptr-pptrs->packet_ptr, off_l4; u_int32_t advance; u_int8_t nh, fragmented = 0; u_char *ptr = pptrs->iph_ptr; int ret = TRUE; /* length checks */ if (off+IP6HdrSz > caplen) return FALSE; /* IP packet truncated */ if (plen == 0 && ((struct ip6_hdr *)pptrs->iph_ptr)->ip6_nxt != IPPROTO_NONE) { Log(LOG_INFO, "INFO ( default/core ): NULL IPv6 payload length. Jumbo packets are currently not supported.\n"); return FALSE; } pptrs->l4_proto = 0; pptrs->payload_ptr = NULL; nh = ((struct ip6_hdr *)pptrs->iph_ptr)->ip6_nxt; advance = IP6HdrSz; while ((off+advance <= caplen) && advance) { off += advance; ptr += advance; switch(nh) { case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: case IPPROTO_ROUTING: case IPPROTO_MOBILITY: nh = ((struct ip6_ext *)ptr)->ip6e_nxt; advance = (((struct ip6_ext *)ptr)->ip6e_len + 1) << 3; break; case IPPROTO_AH: nh = ((struct ip6_ext *)ptr)->ip6e_nxt; advance = sizeof(struct ah)+(((struct ah *)ptr)->ah_len << 2); /* hdr + sumlen */ break; case IPPROTO_FRAGMENT: fhdr = (struct ip6_frag *) ptr; nh = ((struct ip6_ext *)ptr)->ip6e_nxt; advance = sizeof(struct ip6_frag); break; /* XXX: case IPPROTO_ESP: */ /* XXX: case IPPROTO_IPCOMP: */ default: pptrs->tlh_ptr = ptr; pptrs->l4_proto = nh; goto end; } } end: off_l4 = off; if (config.handle_fragments) { if (pptrs->l4_proto == IPPROTO_TCP || pptrs->l4_proto == IPPROTO_UDP) { if (off+MyTLHdrSz > caplen) { Log(LOG_INFO, "INFO ( default/core ): short IPv6 packet read (%u/%u/frags). Snaplen issue ?\n", caplen, off+MyTLHdrSz); return FALSE; } if (fhdr && (fhdr->ip6f_offlg & htons(IP6F_MORE_FRAG|IP6F_OFF_MASK))) { ret = ip6_fragment_handler(pptrs, fhdr); if (!ret) { if (!config.ext_sampling_rate) goto quit; else { pptrs->tlh_ptr = dummy_tlhdr; pptrs->tcp_flags = FALSE; if (off < caplen) pptrs->payload_ptr = ptr; ret = TRUE; goto quit; } } } /* Let's handle both fragments and packets. If we are facing any subsequent frag our pointer is in place; we handle unknown L4 protocols likewise. In case of "entire" TCP/UDP packets we have to jump the L4 header instead */ if (fhdr && (fhdr->ip6f_offlg & htons(IP6F_OFF_MASK))); else if (pptrs->l4_proto == IPPROTO_UDP) { ptr += UDPHdrSz; off += UDPHdrSz; } else if (pptrs->l4_proto == IPPROTO_TCP) { ptr += ((struct my_tcphdr *)pptrs->tlh_ptr)->th_off << 2; off += ((struct my_tcphdr *)pptrs->tlh_ptr)->th_off << 2; } if (off < caplen) pptrs->payload_ptr = ptr; } else { pptrs->tlh_ptr = dummy_tlhdr; if (off < caplen) pptrs->payload_ptr = ptr; } if (config.handle_flows) { pptrs->tcp_flags = FALSE; if (pptrs->l4_proto == IPPROTO_TCP) { if (off_l4+TCPFlagOff+1 > caplen) { Log(LOG_INFO, "INFO ( default/core ): short IPv6 packet read (%u/%u/flows). Snaplen issue ?\n", caplen, off_l4+TCPFlagOff+1); return FALSE; } if (((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_SYN) pptrs->tcp_flags |= TH_SYN; if (((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_FIN) pptrs->tcp_flags |= TH_FIN; if (((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_RST) pptrs->tcp_flags |= TH_RST; if (((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_ACK && pptrs->tcp_flags) pptrs->tcp_flags |= TH_ACK; } ip_flow6_handler(pptrs); } /* XXX: optimize/short circuit here! */ pptrs->tcp_flags = FALSE; if (pptrs->l4_proto == IPPROTO_TCP && off_l4+TCPFlagOff+1 <= caplen) pptrs->tcp_flags = ((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags; } quit: return TRUE; } #endif void PM_find_id(struct id_table *t, struct packet_ptrs *pptrs, pm_id_t *tag, pm_id_t *tag2) { int x, j, stop; pm_id_t id; if (!t) return; id = 0; if (tag) *tag = 0; if (tag2) *tag2 = 0; for (x = 0; x < t->ipv4_num; x++) { t->e[x].last_matched = FALSE; for (j = 0, stop = 0; !stop; j++) stop = (*t->e[x].func[j])(pptrs, &id, &t->e[x]); if (id) { if (stop == PRETAG_MAP_RCODE_ID) { if (t->e[x].stack.func) id = (*t->e[x].stack.func)(id, *tag); *tag = id; } else if (stop == PRETAG_MAP_RCODE_ID2) { if (t->e[x].stack.func) id = (*t->e[x].stack.func)(id, *tag2); *tag2 = id; } if (t->e[x].jeq.ptr) { if (t->e[x].ret) { exec_plugins(pptrs); set_shadow_status(pptrs); *tag = 0; *tag2 = 0; } x = t->e[x].jeq.ptr->pos; x--; /* yes, it will be automagically incremented by the for() cycle */ id = 0; } else break; } } } void compute_once() { struct pkt_data dummy; CounterSz = sizeof(dummy.pkt_len); PdataSz = sizeof(struct pkt_data); PpayloadSz = sizeof(struct pkt_payload); PextrasSz = sizeof(struct pkt_extras); PbgpSz = sizeof(struct pkt_bgp_primitives); ChBufHdrSz = sizeof(struct ch_buf_hdr); CharPtrSz = sizeof(char *); IP4HdrSz = sizeof(struct my_iphdr); MyTLHdrSz = sizeof(struct my_tlhdr); TCPFlagOff = 13; MyTCPHdrSz = TCPFlagOff+1; PptrsSz = sizeof(struct packet_ptrs); UDPHdrSz = 8; CSSz = sizeof(struct class_st); IpFlowCmnSz = sizeof(struct ip_flow_common); HostAddrSz = sizeof(struct host_addr); #if defined ENABLE_IPV6 IP6HdrSz = sizeof(struct ip6_hdr); IP6AddrSz = sizeof(struct in6_addr); #endif } void tunnel_registry_init() { if (config.tunnel0) { char *tun_string = config.tunnel0, *tun_entry = NULL, *tun_type = NULL; int th_index = 0 /* tunnel handler index */, tr_index = 0 /* tunnel registry index */; int ret; while (tun_entry = extract_token(&tun_string, ';')) { tun_type = extract_token(&tun_entry, ','); for (th_index = 0; strcmp(tunnel_handlers_list[th_index].type, ""); th_index++) { if (!strcmp(tunnel_handlers_list[th_index].type, tun_type)) { if (tr_index < TUNNEL_REGISTRY_ENTRIES) { (*tunnel_handlers_list[th_index].tc)(&tunnel_registry[0][tr_index], tun_entry); tr_index++; } break; } } } } } int gtp_tunnel_configurator(struct tunnel_handler *th, char *opts) { th->proto = IPPROTO_UDP; th->port = atoi(opts); if (th->port) { th->tf = gtp_tunnel_func; } else { th->tf = NULL; Log(LOG_WARNING, "WARN ( default/core ): GTP tunnel handler not loaded due to invalid options: '%s'\n", opts); } return 0; } int gtp_tunnel_func(register struct packet_ptrs *pptrs) { register u_int16_t caplen = ((struct pcap_pkthdr *)pptrs->pkthdr)->caplen; struct my_gtphdr *gtp_hdr = (struct my_gtphdr *) pptrs->payload_ptr; struct my_udphdr *udp_hdr = (struct my_udphdr *) pptrs->tlh_ptr; u_int16_t off = pptrs->payload_ptr-pptrs->packet_ptr, gtp_len; char *ptr = pptrs->payload_ptr; int ret; if (off+sizeof(struct my_gtphdr) < caplen) { gtp_len = (ntohs(udp_hdr->uh_ulen)-sizeof(struct my_udphdr))-ntohs(gtp_hdr->length); if (off+gtp_len < caplen) { off += gtp_len; ptr += gtp_len; pptrs->iph_ptr = ptr; pptrs->tlh_ptr = NULL; pptrs->payload_ptr = NULL; pptrs->l4_proto = 0; pptrs->tcp_flags = 0; /* same trick used for MPLS BoS in ll.c: let's look at the first payload byte to guess which protocol we are speaking about */ switch (*pptrs->iph_ptr) { case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f: pptrs->tun_layer++; ret = ip_handler(pptrs); break; #if defined ENABLE_IPV6 case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: pptrs->tun_layer++; ret = ip6_handler(pptrs); break; #endif default: ret = FALSE; break; } } else { Log(LOG_INFO, "INFO ( default/core ): short GTP packet read (%u/%u/tunnel/#1). Snaplen issue ?\n", caplen, off+gtp_len); return FALSE; } } else { Log(LOG_INFO, "INFO ( default/core ): short GTP packet read (%u/%u/tunnel/#2). Snaplen issue ?\n", caplen, off+sizeof(struct my_gtphdr)); return FALSE; } return ret; } pmacct-0.14.0/src/log.h0000644000175000017500000000116710530072467013603 0ustar paolopaolo/* includes */ #include #include /* defines */ #define LOGSTRLEN LONGSRVBUFLEN struct _facility_map { char string[10]; int num; }; static const struct _facility_map facility_map[] = { {"auth", LOG_AUTH}, {"mail", LOG_MAIL}, {"daemon", LOG_DAEMON}, {"kern", LOG_KERN}, {"user", LOG_USER}, {"local0", LOG_LOCAL0}, {"local1", LOG_LOCAL1}, {"local2", LOG_LOCAL2}, {"local3", LOG_LOCAL3}, {"local4", LOG_LOCAL4}, {"local5", LOG_LOCAL5}, {"local6", LOG_LOCAL6}, {"local7", LOG_LOCAL7}, {"-1", -1}, }; /* prototypes */ void Log(short int, char *, ...); int parse_log_facility(const char *); pmacct-0.14.0/src/cfg.c0000644000175000017500000003335211224114046013544 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente */ /* 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. */ #define __CFG_C /* includes */ #include "pmacct.h" #include "plugin_hooks.h" #include "pmacct-data.h" #include "pkt_handlers.h" /* evaluate_configuration() handles all supported configuration keys and inserts them in configuration structure of plugins */ void evaluate_configuration(char *filename, int rows) { char *key, *value, *name, *delim; int index = 0, dindex, valid_line, key_found = 0, res; while (index < rows) { if (*cfg[index] == '\0') valid_line = FALSE; else valid_line = TRUE; if (valid_line) { /* debugging the line if required */ if (debug) Log(LOG_DEBUG, "DEBUG ( %s ): %s\n", filename, cfg[index]); /* splitting key, value and name */ delim = strchr(cfg[index], ':'); *delim = '\0'; key = cfg[index]; value = delim+1; delim = strchr(key, '['); if (delim) { *delim = '\0'; name = delim+1; delim = strchr(name, ']'); *delim = '\0'; } else name = NULL; /* parsing keys */ for (dindex = 0; strcmp(dictionary[dindex].key, ""); dindex++) { if (!strcmp(dictionary[dindex].key, key)) { res = FALSE; if ((*dictionary[dindex].func)) { res = (*dictionary[dindex].func)(filename, name, value); if (res < 0) Log(LOG_WARNING, "WARN ( %s ): Invalid value at line: %d. Ignored.\n", filename, index+1); else if (!res) Log(LOG_WARNING, "WARN ( %s ): Unknown symbol '%s'. Line %d ignored.\n", filename, name, index+1); } else Log(LOG_WARNING, "WARN ( %s ): Unable to handle key: %s. Line %d ignored.\n", filename, key, index+1); key_found = TRUE; break; } else key_found = FALSE; } if (!key_found) Log(LOG_WARNING, "WARN ( %s ): Unknown key: %s. Line %d ignored.\n", filename, key, index+1); } index++; } } /* parse_configuration_file() reads configuration file and stores its content in an array; then creates plugin structures and parses supported config keys */ int parse_configuration_file(char *filename) { char localbuf[10240]; char cmdline [] = "cmdline"; FILE *file; int num = 0, cmdlineflag = FALSE, rows_cmdline = rows, idx; rows = 0; /* NULL filename means we don't have a configuration file; 1st stage: read from file and store lines into a first char* array; merge commandline options, if required, placing them at the tail - in order to override directives placed in the configuration file */ if (filename) { if ((file = fopen(filename,"r")) == NULL) { Log(LOG_ERR, "ERROR: file %s not found.\n", filename); return ERR; } else { while (!feof(file)) { if (rows == SRVBUFLEN) { Log(LOG_ERR, "ERROR ( %s ): maximum number of %d lines reached.\n", filename, SRVBUFLEN); break; } memset(localbuf, 0, sizeof(localbuf)); if (fgets(localbuf, sizeof(localbuf), file) == NULL) break; else { localbuf[sizeof(localbuf)-1] = '\0'; cfg[rows] = malloc(strlen(localbuf)+2); strcpy(cfg[rows], localbuf); cfg[rows][strlen(localbuf)+1] = '\0'; rows++; } } } fclose(file); } else { filename = cmdline; cmdlineflag = TRUE; } if (rows_cmdline) { for (idx = 0; idx < rows_cmdline && (rows+idx) < SRVBUFLEN; idx++) { cfg[rows+idx] = cfg_cmdline[idx]; } rows += idx; } /* 2nd stage: sanitize lines */ sanitize_cfg(rows, filename); /* 3rd stage: plugin structures creation; we discard plugin names if 'pmacctd' has been invoked commandline; if any plugin has been activated we default to a single 'imt' plugin */ create_plugin(filename, "default", "core"); if (!cmdlineflag) num = parse_plugin_names(filename, rows, FALSE); else num = parse_plugin_names(filename, rows, TRUE); if (!num) { Log(LOG_WARNING, "WARN ( %s ): No plugin has been activated; defaulting to in-memory table.\n", filename); num = create_plugin(filename, "default", "memory"); } if (debug) { struct plugins_list_entry *list = plugins_list; while (list) { Log(LOG_DEBUG, "DEBUG ( %s ): plugin name/type: '%s'/'%s'.\n", filename, list->name, list->type.string); list = list->next; } } /* 4th stage: setting some default value */ set_default_values(); /* 5th stage: parsing keys and building configurations */ evaluate_configuration(filename, rows); return SUCCESS; } void sanitize_cfg(int rows, char *filename) { int rindex = 0, len, got_first; char localbuf[10240]; while (rindex < rows) { memset(localbuf, 0, 10240); /* checking the whole line: if it's a comment starting with '!', it will be removed */ if (iscomment(cfg[rindex])) memset(cfg[rindex], 0, strlen(cfg[rindex])); /* checking the whole line: if it's void, it will be removed */ if (isblankline(cfg[rindex])) memset(cfg[rindex], 0, strlen(cfg[rindex])); /* a pair of syntax checks on the whole line: - does the line contain at least a ':' verb ? - are the square brackets weighted both in key and value ? */ len = strlen(cfg[rindex]); if (len) { int symbol = FALSE, cindex = 0, got_first = 0; if (!strchr(cfg[rindex], ':')) { Log(LOG_ERR, "ERROR ( %s ): Syntax error: missing ':' at line %d. Exiting.\n", filename, rindex+1); exit(1); } while(cindex <= len) { if (cfg[rindex][cindex] == '[') symbol++; else if (cfg[rindex][cindex] == ']') { symbol--; got_first++; } if ((cfg[rindex][cindex] == ':') || (cfg[rindex][cindex] == '\0')) { if (symbol && !got_first) { Log(LOG_ERR, "ERROR ( %s ): Syntax error: not weighted brackets at line %d. Exiting.\n", filename, rindex+1); exit(1); } } if (symbol < 0 && !got_first) { Log(LOG_ERR, "ERROR ( %s ): Syntax error: not weighted brackets at line %d. Exiting.\n", filename, rindex+1); exit(1); } if (symbol > 1 && !got_first) { Log(LOG_ERR, "ERROR ( %s ): Syntax error: nested symbols not allowed at line %d. Exiting.\n", filename, rindex+1); exit(1); } cindex++; } } /* checking the whole line: erasing unwanted spaces from key; trimming start/end spaces from value; symbols will be left untouched */ len = strlen(cfg[rindex]); if (len) { int symbol = FALSE, value = FALSE, cindex = 0, lbindex = 0; char *valueptr; while(cindex <= len) { if (!value) { if (cfg[rindex][cindex] == '[') symbol++; else if (cfg[rindex][cindex] == ']') symbol--; else if (cfg[rindex][cindex] == ':') { value++; valueptr = &localbuf[lbindex+1]; } } if ((!symbol) && (!value)) { if (!isspace(cfg[rindex][cindex])) { localbuf[lbindex] = cfg[rindex][cindex]; lbindex++; } } else { localbuf[lbindex] = cfg[rindex][cindex]; lbindex++; } cindex++; } localbuf[lbindex] = '\0'; trim_spaces(valueptr); strcpy(cfg[rindex], localbuf); } /* checking key field: each symbol must refer to a key */ len = strlen(cfg[rindex]); if (len) { int symbol = FALSE, key = FALSE, cindex = 0; while (cindex < rows) { if (cfg[rindex][cindex] == '[') symbol++; else if (cfg[rindex][cindex] == ']') { symbol--; key--; } if (cfg[rindex][cindex] == ':') break; if (!symbol) { if (isalpha(cfg[rindex][cindex])) key = TRUE; } else { if (!key) { Log(LOG_ERR, "ERROR ( %s ): Syntax error: symbol not referring to any key at line %d. Exiting.\n", filename, rindex+1); exit(1); } } cindex++; } } /* checking key field: does a key still exist ? */ len = strlen(cfg[rindex]); if (len) { if (cfg[rindex][0] == ':') { Log(LOG_ERR, "ERROR ( %s ): Syntax error: missing key at line %d. Exiting.\n", filename, rindex+1); exit(1); } } /* checking key field: converting key to lower chars */ len = strlen(cfg[rindex]); if (len) { int symbol = FALSE, cindex = 0; while(cindex <= len) { if (cfg[rindex][cindex] == '[') symbol++; else if (cfg[rindex][cindex] == ']') symbol--; if (cfg[rindex][cindex] == ':') break; if (!symbol) { if (isalpha(cfg[rindex][cindex])) cfg[rindex][cindex] = tolower(cfg[rindex][cindex]); } cindex++; } } rindex++; } } /* parse_plugin_names() leaves cfg array untouched: parses the key 'plugins' if it exists and creates the plugins linked list */ int parse_plugin_names(char *filename, int rows, int ignore_names) { int index = 0, num = 0, found = 0; char *start, *end, *start_name, *end_name; char key[SRVBUFLEN], value[10240], token[SRVBUFLEN], name[SRVBUFLEN]; /* searching for 'plugins' key */ while (index < rows) { memset(key, 0, SRVBUFLEN); start = NULL; end = NULL; start = cfg[index]; end = strchr(cfg[index], ':'); if (end > start) { strlcpy(key, cfg[index], (end-start)+1); if (!strncmp(key, "plugins", sizeof("plugins"))) { start = end+1; strcpy(value, start); found = TRUE; break; } } index++; } if (!found) return 0; /* parsing declared plugins */ start = value; while (*end != '\0') { memset(token, 0, SRVBUFLEN); if (!(end = strchr(start, ','))) end = strchr(start, '\0'); if (end > start) { strlcpy(token, start, (end-start)+1); if ((start_name = strchr(token, '[')) && (end_name = strchr(token, ']'))) { if (end_name > (start_name+1)) { strlcpy(name, (start_name+1), (end_name-start_name)); trim_spaces(name); *start_name = '\0'; } } else strcpy(name, "default"); /* Having already plugins name and type, we'll filter out reserved symbols */ trim_spaces(token); if (!strcmp(token, "core")) { Log(LOG_ERR, "ERROR ( %s ): plugins of type 'core' are not allowed. Exiting.\n", filename); exit(1); } if (!ignore_names) { if (create_plugin(filename, name, token)) num++; } else { if (create_plugin(filename, "default", token)) num++; } } start = end+1; } /* having already processed it, we erase 'plugins' line */ memset(cfg[index], 0, strlen(cfg[index])); return num; } /* rough and dirty function to assign default values to configuration file of each plugin */ void set_default_values() { struct plugins_list_entry *list = plugins_list; while (list) { list->cfg.promisc = TRUE; list->cfg.refresh_maps = TRUE; list = list->next; } } int create_plugin(char *filename, char *name, char *type) { struct plugins_list_entry *plugin, *ptr; struct plugin_type_entry *ptype = NULL; int index = 0, id = 0; /* searching for a valid known plugin type */ while(strcmp(plugin_types_list[index].string, "")) { if (!strcmp(type, plugin_types_list[index].string)) ptype = &plugin_types_list[index]; index++; } if (!ptype) { Log(LOG_ERR, "ERROR ( %s ): Unknown plugin type: %s. Ignoring.\n", filename, type); return FALSE; } /* checks */ if (plugins_list) { id = 0; ptr = plugins_list; while(ptr) { /* plugin id */ if (ptr->id > id) id = ptr->id; /* dupes */ if (!strcmp(name, ptr->name)) { if (!strcmp(type, ptr->type.string)) { Log(LOG_WARNING, "WARN ( %s ): another plugin with the same name '%s' already exists. Preserving first.\n", filename, name); return FALSE; } } ptr = ptr->next; } id++; } /* creating a new plugin structure */ plugin = (struct plugins_list_entry *) malloc(sizeof(struct plugins_list_entry)); if (!plugin) { Log(LOG_ERR, "ERROR ( %s ): Unable to allocate memory config_plugin structure.\n", filename); exit(1); } memset(plugin, 0, sizeof(struct plugins_list_entry)); strcpy(plugin->name, name); plugin->id = id; memcpy(&plugin->type, ptype, sizeof(struct plugin_type_entry)); plugin->next = NULL; /* inserting our object in plugin's linked list */ if (plugins_list) { ptr = plugins_list; while(ptr->next) ptr = ptr->next; ptr->next = plugin; } else plugins_list = plugin; return TRUE; } int delete_plugin_by_id(int id) { struct plugins_list_entry *list = plugins_list; struct plugins_list_entry *aux = plugins_list; int highest_id = 0; if (id == 0) return ERR; while (list) { if (list->id == id) { aux->next = list->next; free(list); list = aux; } else { if (list->id > highest_id) highest_id = list->id; } aux = list; list = list->next; } return highest_id; } struct plugins_list_entry *search_plugin_by_pipe(int pipe) { struct plugins_list_entry *list = plugins_list; if (pipe < 0) return NULL; while (list) { if (list->pipe[1] == pipe) return list; else list = list->next; } return NULL; } struct plugins_list_entry *search_plugin_by_pid(pid_t pid) { struct plugins_list_entry *list = plugins_list; if (pid <= 0) return NULL; while (list) { if (list->pid == pid) return list; else list = list->next; } return NULL; } pmacct-0.14.0/src/pretag-data.h0000644000175000017500000000616411715256661015223 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ const struct _map_dictionary_line tag_map_dictionary[] = { {"id", PT_map_id_handler}, {"id2", PT_map_id2_handler}, {"ip", PT_map_ip_handler}, {"in", PT_map_input_handler}, {"out", PT_map_output_handler}, {"engine_type", PT_map_engine_type_handler}, {"engine_id", PT_map_engine_id_handler}, {"nexthop", PT_map_nexthop_handler}, {"bgp_nexthop", PT_map_bgp_nexthop_handler}, {"filter", PT_map_filter_handler}, {"v8agg", PT_map_v8agg_handler}, {"agent_id", PT_map_agent_id_handler}, {"sampling_rate", PT_map_sampling_rate_handler}, {"sample_type", PT_map_sample_type_handler}, {"direction", PT_map_direction_handler}, {"src_as", PT_map_src_as_handler}, {"dst_as", PT_map_dst_as_handler}, {"peer_src_as", PT_map_peer_src_as_handler}, {"peer_dst_as", PT_map_peer_dst_as_handler}, {"src_local_pref", PT_map_src_local_pref_handler}, {"local_pref", PT_map_local_pref_handler}, {"src_comms", PT_map_src_comms_handler}, {"comms", PT_map_comms_handler}, {"mpls_vpn_rd", PT_map_mpls_vpn_rd_handler}, {"label", PT_map_label_handler}, {"jeq", PT_map_jeq_handler}, {"return", PT_map_return_handler}, {"stack", PT_map_stack_handler}, {"", NULL} }; const struct _map_dictionary_line tag_map_tee_dictionary[] = { {"id", PT_map_id_handler}, {"id2", PT_map_id2_handler}, {"ip", PT_map_ip_handler}, {"label", PT_map_label_handler}, {"jeq", PT_map_jeq_handler}, {"return", PT_map_return_handler}, {"stack", PT_map_stack_handler}, {"", NULL} }; const struct _map_dictionary_line bpas_map_dictionary[] = { {"id", PT_map_id_handler}, {"ip", PT_map_ip_handler}, {"in", PT_map_input_handler}, {"bgp_nexthop", BPAS_map_bgp_nexthop_handler}, {"peer_dst_as", BPAS_map_bgp_peer_dst_as_handler}, {"src_mac", BPAS_map_src_mac_handler}, {"", NULL} }; const struct _map_dictionary_line bta_map_dictionary[] = { {"id", PT_map_id_handler}, {"ip", PT_map_ip_handler}, {"", NULL} }; const struct _map_dictionary_line sampling_map_dictionary[] = { {"id", PT_map_id_handler}, {"ip", PT_map_ip_handler}, {"in", PT_map_input_handler}, {"out", PT_map_output_handler}, {"", NULL} }; const struct _map_dictionary_line bitr_map_dictionary[] = { {"id", PT_map_id_handler}, {"ip", PT_map_ip_handler}, {"in", PT_map_input_handler}, {"out", PT_map_output_handler}, {"", NULL} }; pmacct-0.14.0/src/Makefile.am0000644000175000017500000000407111705253203014674 0ustar paolopaoloSUBDIRS = nfprobe_plugin sfprobe_plugin bgp tee_plugin isis sbin_PROGRAMS = pmacctd nfacctd sfacctd uacctd bin_PROGRAMS = pmacct @EXTRABIN@ EXTRA_PROGRAMS = pmmyplay pmpgplay pmacctd_PLUGINS = @PLUGINS@ @THREADS_SOURCES@ pmacctd_SOURCES = pmacctd.c signals.c util.c strlcpy.c plugin_hooks.c \ server.c acct.c memory.c ll.c cfg.c imt_plugin.c log.c pkt_handlers.c \ cfg_handlers.c net_aggr.c bpf_filter.c print_plugin.c ip_frag.c \ ports_aggr.c addr.c pretag.c pretag_handlers.c ip_flow.c setproctitle.c \ classifier.c regexp.c regsub.c conntrack.c xflow_status.c nl.c pmacctd_LDFLAGS = $(DEFS) pmacctd_LDADD = $(pmacctd_PLUGINS) nfacctd_SOURCES = nfacctd.c signals.c util.c strlcpy.c plugin_hooks.c \ server.c acct.c memory.c cfg.c imt_plugin.c log.c pkt_handlers.c \ cfg_handlers.c net_aggr.c bpf_filter.c print_plugin.c pretag.c \ pretag_handlers.c ports_aggr.c nfv8_handlers.c nfv9_template.c addr.c \ setproctitle.c ip_flow.c classifier.c regexp.c regsub.c conntrack.c \ xflow_status.c nfacctd_LDFLAGS = $(DEFS) nfacctd_LDADD = $(pmacctd_PLUGINS) sfacctd_SOURCES = sfacctd.c signals.c util.c strlcpy.c plugin_hooks.c \ server.c acct.c memory.c cfg.c imt_plugin.c log.c pkt_handlers.c \ cfg_handlers.c net_aggr.c bpf_filter.c print_plugin.c pretag.c \ pretag_handlers.c ports_aggr.c addr.c ll.c setproctitle.c ip_flow.c \ classifier.c regexp.c regsub.c conntrack.c xflow_status.c sfacctd_LDFLAGS = $(DEFS) sfacctd_LDADD = $(pmacctd_PLUGINS) uacctd_SOURCES = uacctd.c signals.c util.c strlcpy.c plugin_hooks.c \ server.c acct.c memory.c ll.c cfg.c imt_plugin.c log.c pkt_handlers.c \ cfg_handlers.c net_aggr.c bpf_filter.c print_plugin.c ip_frag.c \ ports_aggr.c addr.c pretag.c pretag_handlers.c ip_flow.c setproctitle.c \ classifier.c regexp.c regsub.c conntrack.c xflow_status.c nl.c uacctd_LDFLAGS = $(DEFS) uacctd_LDADD = $(pmacctd_PLUGINS) pmacct_SOURCES = pmacct.c strlcpy.c addr.c pmmyplay_SOURCES = pmmyplay.c strlcpy.c sql_handlers.c log_templates.c addr.c pmpgplay_SOURCES = pmpgplay.c strlcpy.c sql_handlers.c log_templates.c addr.c pmacct-0.14.0/src/nfv8_handlers.c0000644000175000017500000001734011037474163015560 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente */ /* 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. */ /* defines */ #define __NFV8_HANDLERS_C /* includes */ #include "pmacct.h" #include "nfacctd.h" #include "pmacct-data.h" #include "nfv8_handlers.h" void load_nfv8_handlers() { memset(&v8_handlers, 0, sizeof(v8_handlers)); v8_handlers[1].fh = v8_1_filter_handler; v8_handlers[1].max_flows = V8_1_MAXFLOWS; v8_handlers[1].exp_size = sizeof(struct struct_export_v8_1); v8_handlers[2].fh = v8_2_filter_handler; v8_handlers[2].max_flows = V8_2_MAXFLOWS; v8_handlers[2].exp_size = sizeof(struct struct_export_v8_2); v8_handlers[3].fh = v8_3_filter_handler; v8_handlers[3].max_flows = V8_3_MAXFLOWS; v8_handlers[3].exp_size = sizeof(struct struct_export_v8_3); v8_handlers[4].fh = v8_4_filter_handler; v8_handlers[4].max_flows = V8_4_MAXFLOWS; v8_handlers[4].exp_size = sizeof(struct struct_export_v8_4); v8_handlers[5].fh = v8_5_filter_handler; v8_handlers[5].max_flows = V8_5_MAXFLOWS; v8_handlers[5].exp_size = sizeof(struct struct_export_v8_5); v8_handlers[6].fh = v8_6_filter_handler; v8_handlers[6].max_flows = V8_6_MAXFLOWS; v8_handlers[6].exp_size = sizeof(struct struct_export_v8_6); v8_handlers[7].fh = v8_7_filter_handler; v8_handlers[7].max_flows = V8_7_MAXFLOWS; v8_handlers[7].exp_size = sizeof(struct struct_export_v8_7); v8_handlers[8].fh = v8_8_filter_handler; v8_handlers[8].max_flows = V8_8_MAXFLOWS; v8_handlers[8].exp_size = sizeof(struct struct_export_v8_8); v8_handlers[9].fh = v8_9_filter_handler; v8_handlers[9].max_flows = V8_9_MAXFLOWS; v8_handlers[9].exp_size = sizeof(struct struct_export_v8_9); v8_handlers[10].fh = v8_10_filter_handler; v8_handlers[10].max_flows = V8_10_MAXFLOWS; v8_handlers[10].exp_size = sizeof(struct struct_export_v8_10); v8_handlers[11].fh = v8_11_filter_handler; v8_handlers[11].max_flows = V8_11_MAXFLOWS; v8_handlers[11].exp_size = sizeof(struct struct_export_v8_11); v8_handlers[12].fh = v8_12_filter_handler; v8_handlers[12].max_flows = V8_12_MAXFLOWS; v8_handlers[12].exp_size = sizeof(struct struct_export_v8_12); v8_handlers[13].fh = v8_13_filter_handler; v8_handlers[13].max_flows = V8_13_MAXFLOWS; v8_handlers[13].exp_size = sizeof(struct struct_export_v8_13); v8_handlers[14].fh = v8_14_filter_handler; v8_handlers[14].max_flows = V8_14_MAXFLOWS; v8_handlers[14].exp_size = sizeof(struct struct_export_v8_14); } void v8_1_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_1 *exp = (struct struct_export_v8_1 *) data; /* It contains just AS informations; no filtering chances */ } void v8_2_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_2 *exp = (struct struct_export_v8_2 *) data; Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_p, exp->prot); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->src_port, exp->srcport); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port, exp->dstport); } void v8_3_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_3 *exp = (struct struct_export_v8_3 *) data; Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_src.s_addr, exp->src_prefix); } void v8_4_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_4 *exp = (struct struct_export_v8_4 *) data; Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp->dst_prefix); } void v8_5_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_5 *exp = (struct struct_export_v8_5 *) data; Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_src.s_addr, exp->src_prefix); Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp->dst_prefix); } void v8_6_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_6 *exp = (struct struct_export_v8_6 *) data; Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp->dstaddr); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp->tos); } void v8_7_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_7 *exp = (struct struct_export_v8_7 *) data; Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_src.s_addr, exp->srcaddr); Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp->dstaddr); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp->tos); } void v8_8_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_8 *exp = (struct struct_export_v8_8 *) data; Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_src.s_addr, exp->srcaddr); Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp->dstaddr); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_p, exp->prot); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp->tos); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->src_port, exp->srcport); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port, exp->dstport); } void v8_9_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_9 *exp = (struct struct_export_v8_9 *) data; Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp->tos); } void v8_10_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_10 *exp = (struct struct_export_v8_10 *) data; Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_p, exp->prot); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp->tos); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->src_port, exp->srcport); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port, exp->dstport); } void v8_11_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_11 *exp = (struct struct_export_v8_11 *) data; Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_src.s_addr, exp->src_prefix); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp->tos); } void v8_12_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_12 *exp = (struct struct_export_v8_12 *) data; Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp->dst_prefix); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp->tos); } void v8_13_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_13 *exp = (struct struct_export_v8_13 *) data; Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_src.s_addr, exp->src_prefix); Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp->dst_prefix); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp->tos); } void v8_14_filter_handler(struct packet_ptrs *pptrs, void *data) { struct struct_export_v8_14 *exp = (struct struct_export_v8_14 *) data; Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_src.s_addr, exp->src_prefix); Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp->dst_prefix); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_p, exp->prot); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp->tos); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->src_port, exp->srcport); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port, exp->dstport); } pmacct-0.14.0/src/cfg_handlers.c0000644000175000017500000025120011737667675015454 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #define __CFG_HANDLERS_C /* includes */ #include "pmacct.h" #include "nfacctd.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "cfg_handlers.h" int parse_truefalse(char *value_ptr) { int value; lower_string(value_ptr); if (!strcmp("true", value_ptr)) value = TRUE; else if (!strcmp("false", value_ptr)) value = FALSE; else value = ERR; return value; } int cfg_key_debug(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.debug = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.debug = value; changes++; break; } } } return changes; } int cfg_key_syslog(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.syslog = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.syslog = value_ptr; changes++; break; } } } return changes; } int cfg_key_logfile(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.logfile = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'logfile'. Globalized.\n", filename); return changes; } int cfg_key_pidfile(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.pidfile = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pidfile'. Globalized.\n", filename); return changes; } int cfg_key_daemonize(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.daemon = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'daemonize'. Globalized.\n", filename); return changes; } int cfg_key_aggregate(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; char *count_token; u_int64_t value = 0; u_int32_t changes = 0; trim_all_spaces(value_ptr); while (count_token = extract_token(&value_ptr, ',')) { if (!strcmp(count_token, "src_host")) value |= COUNT_SRC_HOST; else if (!strcmp(count_token, "dst_host")) value |= COUNT_DST_HOST; else if (!strcmp(count_token, "src_net")) value |= COUNT_SRC_NET; else if (!strcmp(count_token, "dst_net")) value |= COUNT_DST_NET; else if (!strcmp(count_token, "sum")) value |= COUNT_SUM_HOST; else if (!strcmp(count_token, "src_port")) value |= COUNT_SRC_PORT; else if (!strcmp(count_token, "dst_port")) value |= COUNT_DST_PORT; else if (!strcmp(count_token, "proto")) value |= COUNT_IP_PROTO; #if defined (HAVE_L2) else if (!strcmp(count_token, "src_mac")) value |= COUNT_SRC_MAC; else if (!strcmp(count_token, "dst_mac")) value |= COUNT_DST_MAC; else if (!strcmp(count_token, "vlan")) value |= COUNT_VLAN; else if (!strcmp(count_token, "sum_mac")) value |= COUNT_SUM_MAC; #endif else if (!strcmp(count_token, "tos")) value |= COUNT_IP_TOS; else if (!strcmp(count_token, "none")) value |= COUNT_NONE; else if (!strcmp(count_token, "src_as")) value |= COUNT_SRC_AS; else if (!strcmp(count_token, "dst_as")) value |= COUNT_DST_AS; else if (!strcmp(count_token, "sum_host")) value |= COUNT_SUM_HOST; else if (!strcmp(count_token, "sum_net")) value |= COUNT_SUM_NET; else if (!strcmp(count_token, "sum_as")) value |= COUNT_SUM_AS; else if (!strcmp(count_token, "sum_port")) value |= COUNT_SUM_PORT; else if (!strcmp(count_token, "tag")) value |= COUNT_ID; else if (!strcmp(count_token, "tag2")) value |= COUNT_ID2; else if (!strcmp(count_token, "flows")) value |= COUNT_FLOWS; else if (!strcmp(count_token, "class")) value |= COUNT_CLASS; else if (!strcmp(count_token, "tcpflags")) value |= COUNT_TCPFLAGS; else if (!strcmp(count_token, "std_comm")) value |= COUNT_STD_COMM; else if (!strcmp(count_token, "ext_comm")) value |= COUNT_EXT_COMM; else if (!strcmp(count_token, "as_path")) value |= COUNT_AS_PATH; else if (!strcmp(count_token, "local_pref")) value |= COUNT_LOCAL_PREF; else if (!strcmp(count_token, "med")) value |= COUNT_MED; else if (!strcmp(count_token, "peer_src_as")) value |= COUNT_PEER_SRC_AS; else if (!strcmp(count_token, "peer_dst_as")) value |= COUNT_PEER_DST_AS; else if (!strcmp(count_token, "peer_src_ip")) value |= COUNT_PEER_SRC_IP; else if (!strcmp(count_token, "peer_dst_ip")) value |= COUNT_PEER_DST_IP; else if (!strcmp(count_token, "src_as_path")) value |= COUNT_SRC_AS_PATH; else if (!strcmp(count_token, "src_std_comm")) value |= COUNT_SRC_STD_COMM; else if (!strcmp(count_token, "src_ext_comm")) value |= COUNT_SRC_EXT_COMM; else if (!strcmp(count_token, "src_local_pref")) value |= COUNT_SRC_LOCAL_PREF; else if (!strcmp(count_token, "src_med")) value |= COUNT_SRC_MED; else if (!strcmp(count_token, "in_iface")) value |= COUNT_IN_IFACE; else if (!strcmp(count_token, "out_iface")) value |= COUNT_OUT_IFACE; else if (!strcmp(count_token, "src_mask")) value |= COUNT_SRC_NMASK; else if (!strcmp(count_token, "dst_mask")) value |= COUNT_DST_NMASK; else if (!strcmp(count_token, "cos")) value |= COUNT_COS; else if (!strcmp(count_token, "etype")) value |= COUNT_ETHERTYPE; else if (!strcmp(count_token, "mpls_vpn_rd")) value |= COUNT_MPLS_VPN_RD; else Log(LOG_WARNING, "WARN ( %s ): ignoring unknown aggregation method: %s.\n", filename, count_token); } if (!name) for (; list; list = list->next, changes++) list->cfg.what_to_count = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.what_to_count = value; changes++; break; } } } return changes; } int cfg_key_snaplen(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < DEFAULT_SNAPLEN) { Log(LOG_WARNING, "WARN ( %s ): 'snaplen' has to be >= %d.\n", filename, DEFAULT_SNAPLEN); return ERR; } for (; list; list = list->next, changes++) list->cfg.snaplen = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'snaplen'. Globalized.\n", filename); return changes; } int cfg_key_aggregate_filter(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) { Log(LOG_ERR, "ERROR ( %s ): aggregation filter cannot be global. Not loaded.\n", filename); changes++; } else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.a_filter = value_ptr; changes++; break; } } } return changes; } int cfg_key_pre_tag_filter(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; char *count_token, *range_ptr; pm_id_t value = 0, range = 0; int changes = 0; char *endptr_v, *endptr_r; u_int8_t neg; if (!name) { Log(LOG_ERR, "ERROR ( %s ): TAG filter cannot be global. Not loaded.\n", filename); changes++; } else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { trim_all_spaces(value_ptr); list->cfg.ptf.num = 0; while ((count_token = extract_token(&value_ptr, ',')) && changes < MAX_PRETAG_MAP_ENTRIES/4) { neg = pt_check_neg(&count_token); range_ptr = pt_check_range(count_token); value = strtoull(count_token, &endptr_v, 10); if (range_ptr) range = strtoull(range_ptr, &endptr_r, 10); else range = value; if (range_ptr && range <= value) { Log(LOG_ERR, "WARN ( %s ): Range value is expected in the format low-high. '%llu-%llu' not loaded.\n", filename, value, range); changes++; break; } list->cfg.ptf.table[list->cfg.ptf.num].neg = neg; list->cfg.ptf.table[list->cfg.ptf.num].n = value; list->cfg.ptf.table[list->cfg.ptf.num].r = range; list->cfg.ptf.num++; changes++; } break; } } } return changes; } int cfg_key_pre_tag2_filter(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; char *count_token, *range_ptr; pm_id_t value = 0, range = 0; int changes = 0; char *endptr_v, *endptr_r; u_int8_t neg; if (!name) { Log(LOG_ERR, "ERROR ( %s ): TAG2 filter cannot be global. Not loaded.\n", filename); changes++; } else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { trim_all_spaces(value_ptr); list->cfg.pt2f.num = 0; while ((count_token = extract_token(&value_ptr, ',')) && changes < MAX_PRETAG_MAP_ENTRIES/4) { neg = pt_check_neg(&count_token); range_ptr = pt_check_range(count_token); value = strtoull(count_token, &endptr_v, 10); if (range_ptr) range = strtoull(range_ptr, &endptr_r, 10); else range = value; if (range_ptr && range <= value) { Log(LOG_ERR, "WARN ( %s ): Range value is expected in the format low-high. '%llu-%llu' not loaded.\n", filename, value, range); changes++; break; } list->cfg.pt2f.table[list->cfg.pt2f.num].neg = neg; list->cfg.pt2f.table[list->cfg.pt2f.num].n = value; list->cfg.pt2f.table[list->cfg.pt2f.num].r = range; list->cfg.pt2f.num++; changes++; } break; } } } return changes; } int cfg_key_pcap_filter(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.clbuf = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pcap_filter'. Globalized.\n", filename); return changes; } int cfg_key_interface(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.dev = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'interface'. Globalized.\n", filename); return changes; } int cfg_key_files_umask(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; char *endp; value = strtoul(value_ptr, &endp, 8); if (value < 2) { Log(LOG_WARNING, "WARN ( %s ): 'files_umask' has to be >= '002'.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.files_umask = value & 0666; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.files_umask = value & 0666; changes++; break; } } } return changes; } int cfg_key_files_uid(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 1) { Log(LOG_ERR, "WARN ( %s ): 'files_uid' has to be >= 1.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.files_uid = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.files_uid = value; changes++; break; } } } return changes; } int cfg_key_files_gid(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 1) { Log(LOG_ERR, "WARN ( %s ): 'files_gid' has to be >= 1.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.files_gid = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.files_gid = value; changes++; break; } } } return changes; } int cfg_key_interface_wait(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.if_wait = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'interface_wait'. Globalized.\n", filename); return changes; } int cfg_key_savefile_wait(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.sf_wait = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'savefile_wait'. Globalized.\n", filename); return changes; } int cfg_key_promisc(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.promisc = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'promisc'. Globalized.\n", filename); return changes; } int cfg_key_imt_path(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.imt_plugin_path = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.imt_plugin_path = value_ptr; changes++; break; } } } return changes; } int cfg_key_imt_passwd(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.imt_plugin_passwd = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.imt_plugin_passwd = value_ptr; changes++; break; } } } return changes; } int cfg_key_imt_buckets(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_WARNING, "WARN ( %s ): 'imt_buckets' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.buckets = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.buckets = value; changes++; break; } } } return changes; } int cfg_key_imt_mem_pools_number(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 0) { Log(LOG_WARNING, "WARN ( %s ): 'imt_mem_pools_number' has to be >= 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.num_memory_pools = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.num_memory_pools = value; changes++; break; } } } have_num_memory_pools = TRUE; return changes; } int cfg_key_imt_mem_pools_size(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; /* legal values should be >= sizeof(struct acc), though we are unable to check this condition here. Thus, this function will just cut clearly wrong values ie. < = 0. Strict checks will be accomplished later, by the memory plugin */ value = atoi(value_ptr); if (value <= 0) { Log(LOG_WARNING, "WARN ( %s ): 'imt_mem_pools_size' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.memory_pool_size = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.memory_pool_size = value; changes++; break; } } } return changes; } int cfg_key_sql_db(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_db = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_db = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_table(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; /* validations: we allow only a) certain variable names, b) a maximum of 8 variables and c) a maximum table name length of 64 chars */ { int num = 0; char *c, *ptr = value_ptr; while (c = strchr(ptr, '%')) { c++; ptr = c; switch (*c) { case 'd': num++; break; case 'H': num++; break; case 'm': num++; break; case 'M': num++; break; case 'w': num++; break; case 'W': num++; break; case 'Y': num++; break; case 's': num++; break; default: Log(LOG_ERR, "ERROR ( %s ): sql_table, %%%c not supported.\n", filename, *c); exit(1); break; } } if (num > 8) { Log(LOG_ERR, "ERROR ( %s ): sql_table, exceeded the maximum allowed variables (8) into the table name.\n", filename); exit(1); } } if (strlen(value_ptr) > 64) { Log(LOG_ERR, "ERROR ( %s ): sql_table, exceeded the maximum SQL table name length (64).\n", filename); exit(1); } if (!name) for (; list; list = list->next, changes++) list->cfg.sql_table = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_table = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_table_schema(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_table_schema = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_table_schema = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_table_version(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_ERR, "ERROR ( %s ): invalid 'sql_table_version' value.\n", filename); exit(1); } if (!name) for (; list; list = list->next, changes++) list->cfg.sql_table_version = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_table_version = value; changes++; break; } } } return changes; } int cfg_key_sql_table_type(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_table_type = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_table_type = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_data(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_data = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_data = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_host(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_host = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_host = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_recovery_backup_host(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_backup_host = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_backup_host = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_max_writers(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 1 || value >= 100) { Log(LOG_WARNING, "WARN ( %s ): invalid 'sql_max_writers' value). Allowed values are: 1 <= sql_max_writers < 100.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.sql_max_writers = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_max_writers = value; changes++; break; } } } return changes; } int cfg_key_sql_trigger_exec(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_trigger_exec = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_trigger_exec = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_trigger_time(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0, t, t_howmany; parse_time(filename, value_ptr, &t, &t_howmany); if (!name) { for (; list; list = list->next, changes++) { list->cfg.sql_trigger_time = t; list->cfg.sql_trigger_time_howmany = t_howmany; } } else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_trigger_time = t; list->cfg.sql_trigger_time_howmany = t_howmany; changes++; break; } } } return changes; } int cfg_key_sql_user(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_user = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_user = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_passwd(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_passwd = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_passwd = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_refresh_time(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0, i, len = strlen(value_ptr); for (i = 0; i < len; i++) { if (!isdigit(value_ptr[i]) && !isspace(value_ptr[i])) { Log(LOG_ERR, "WARN ( %s ): 'sql_refresh_time' is expected in secs but contains non-digit chars: '%c'\n", filename, value_ptr[i]); return ERR; } } value = atoi(value_ptr); if (value <= 0) { Log(LOG_ERR, "WARN ( %s ): 'sql_refresh_time' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.sql_refresh_time = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_refresh_time = value; changes++; break; } } } return changes; } int cfg_key_sql_startup_delay(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_ERR, "WARN ( %s ): 'sql_startup_delay' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.sql_startup_delay = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_startup_delay = value; changes++; break; } } } return changes; } int cfg_key_sql_optimize_clauses(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_optimize_clauses = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_optimize_clauses = value; changes++; break; } } } return changes; } int cfg_key_sql_history_roundoff(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; int i, check, len; len = strlen(value_ptr); for (i = 0, check = 0; i < len; i++) { if (value_ptr[i] == 'd') check |= COUNT_DAILY; if (value_ptr[i] == 'w') check |= COUNT_WEEKLY; if (value_ptr[i] == 'M') check |= COUNT_MONTHLY; } if (((check & COUNT_DAILY) || (check & COUNT_MONTHLY)) && (check & COUNT_WEEKLY)) { Log(LOG_ERR, "WARN ( %s ): 'sql_history_roundoff' 'w' is not compatible with either 'd' or 'M'.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.sql_history_roundoff = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_history_roundoff = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_recovery_logfile(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_recovery_logfile = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_recovery_logfile = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_history(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0, sql_history, sql_history_howmany; parse_time(filename, value_ptr, &sql_history, &sql_history_howmany); if (!name) { for (; list; list = list->next, changes++) { list->cfg.sql_history = sql_history; list->cfg.sql_history_howmany = sql_history_howmany; } } else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_history = sql_history; list->cfg.sql_history_howmany = sql_history_howmany; changes++; break; } } } return changes; } int cfg_key_sql_history_since_epoch(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_history_since_epoch = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_history_since_epoch = value; changes++; break; } } } return changes; } int cfg_key_sql_cache_entries(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_WARNING, "WARN ( %s ): 'sql_cache_entries' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.sql_cache_entries = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_cache_entries = value; changes++; break; } } } return changes; } int cfg_key_sql_dont_try_update(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_dont_try_update = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_dont_try_update = value; changes++; break; } } } return changes; } int cfg_key_sql_preprocess(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_preprocess = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_preprocess = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_preprocess_type(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0, value = 0; if (!strncmp(value_ptr, "any", 3)) value = FALSE; if (!strncmp(value_ptr, "all", 3)) value = TRUE; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_preprocess_type = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_preprocess_type = value; changes++; break; } } } return changes; } int cfg_key_sql_multi_values(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0, value = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_WARNING, "WARN ( %s ): 'sql_multi_values' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.sql_multi_values = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_multi_values = value; changes++; break; } } } return changes; } int cfg_key_sql_aggressive_classification(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_aggressive_classification = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_aggressive_classification = value; changes++; break; } } } return changes; } int cfg_key_sql_locking_style(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_locking_style = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_locking_style = value_ptr; changes++; break; } } } return changes; } int cfg_key_sql_use_copy(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.sql_use_copy = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_use_copy = value; changes++; break; } } } return changes; } int cfg_key_sql_delimiter(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; /* delimiter is only one character */ if (strlen(value_ptr) != 1) { Log(LOG_WARNING, "WARN ( %s ): 'sql_delimiter' length has to be 1.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.sql_delimiter = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sql_delimiter = value_ptr; changes++; break; } } } return changes; } int cfg_key_plugin_pipe_size(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; /* legal values should be >= sizeof(struct pkt_data)+sizeof(struct ch_buf_hdr) though we are unable to check this condition here. Thus, this function will just cut clearly wrong values ie. < = 0. Strict checks will be accomplished later, by the load_plugins() */ value = atoi(value_ptr); if (value <= 0) { Log(LOG_WARNING, "WARN ( %s ): 'plugin_pipe_size' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.pipe_size = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.pipe_size = value; changes++; break; } } } return changes; } int cfg_key_plugin_pipe_backlog(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 0 || value >= 100) { Log(LOG_WARNING, "WARN ( %s ): 'plugin_pipe_backlog' is a percentage: 0 <= plugin_pipe_backlog < 100.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.pipe_backlog = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.pipe_backlog = value; changes++; break; } } } return changes; } int cfg_key_plugin_buffer_size(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; /* legal values should be >= sizeof(struct pkt_data) and < plugin_pipe_size value, if any though we are unable to check this condition here. Thus, this function will just cut clearly wrong values ie. < = 0. Strict checks will be accomplished later, by the load_plugins() */ value = atoi(value_ptr); if (value <= 0) { Log(LOG_WARNING, "WARN ( %s ): 'plugin_buffer_size' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.buffer_size = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.buffer_size = value; changes++; break; } } } return changes; } int cfg_key_networks_mask(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_WARNING, "WARN ( %s ): 'networks_mask' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.networks_mask = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.networks_mask = value; changes++; break; } } } return changes; } int cfg_key_networks_file(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.networks_file = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.networks_file = value_ptr; changes++; break; } } } return changes; } int cfg_key_networks_cache_entries(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (value <= 0) { Log(LOG_WARNING, "WARN ( %s ): 'networks_cache_entries' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.networks_cache_entries = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.networks_cache_entries = value; changes++; break; } } } return changes; } int cfg_key_ports_file(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.ports_file = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.ports_file = value_ptr; changes++; break; } } } return changes; } int cfg_key_refresh_maps(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.refresh_maps = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'refresh_maps'. Globalized.\n", filename); return changes; } int cfg_key_print_refresh_time(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_ERR, "WARN ( %s ): 'print_refresh_time' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.print_refresh_time = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.print_refresh_time = value; changes++; break; } } } return changes; } int cfg_key_print_cache_entries(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_ERR, "WARN ( %s ): 'print_cache_entries' has to be > 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.print_cache_entries = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.print_cache_entries = value; changes++; break; } } } return changes; } int cfg_key_print_markers(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.print_markers = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.print_markers = value; changes++; break; } } } return changes; } int cfg_key_print_output(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (!strcmp(value_ptr, "formatted")) value = PRINT_OUTPUT_FORMATTED; else if (!strcmp(value_ptr, "csv")) value = PRINT_OUTPUT_CSV; else { Log(LOG_WARNING, "WARN ( %s ): Invalid print output value '%s'\n", filename, value_ptr); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.print_output = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.print_output = value; changes++; break; } } } return changes; } int cfg_key_num_protos(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.num_protos = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.num_protos = value; changes++; break; } } } return changes; } int cfg_key_num_hosts(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.num_hosts = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.num_hosts = value; changes++; break; } } } return changes; } int cfg_key_post_tag(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; pm_id_t value, changes = 0; char *endptr; value = strtoul(value_ptr, &endptr, 10); if (value < 1) { Log(LOG_ERR, "WARN ( %s ): 'post_tag' cannot be zero.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.post_tag = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.post_tag = value; changes++; break; } } } return changes; } int cfg_key_sampling_rate(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 1) { Log(LOG_ERR, "WARN ( %s ): 'sampling_rate' has to be >= 1.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.sampling_rate = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sampling_rate = value; changes++; break; } } } return changes; } int cfg_key_sampling_map(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.sampling_map = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'sampling_map'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_port(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if ((value <= 0) || (value > 65535)) { Log(LOG_ERR, "WARN ( %s ): 'nfacctd_port' has to be in the range 0-65535.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.nfacctd_port = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'nfacctd_port'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_ip(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_ip = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'nfacctd_ip'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_allow_file(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_allow_file = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'nfacctd_allow_file'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_allow_file(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_allow_file = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_daemon_allow_file'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_md5_file(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_md5_file = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_daemon_md5_file'. Globalized.\n", filename); return changes; } int cfg_key_pre_tag_map(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.pre_tag_map = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pre_tag_map'. Globalized.\n", filename); return changes; } int cfg_key_pre_tag_map_entries(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_ERR, "WARN ( %s ): 'pre_tag_map_entries' has to be > 0.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.pre_tag_map_entries = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pre_tag_map_entries'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_time_secs(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.nfacctd_time = NF_TIME_SECS; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'nfacctd_time_secs'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_time_new(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.nfacctd_time = NF_TIME_NEW; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'nfacctd_time_new'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_mcast_groups(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; struct host_addr tmp_addr; char *count_token; u_int32_t value = 0, changes = 0; u_int8_t idx = 0, more = 0, mcast_family; trim_all_spaces(value_ptr); memset(mcast_groups, 0, sizeof(mcast_groups)); while (count_token = extract_token(&value_ptr, ',')) { memset(&tmp_addr, 0, sizeof(tmp_addr)); str_to_addr(count_token, &tmp_addr); if (is_multicast(&tmp_addr)) { if (idx < MAX_MCAST_GROUPS) { memcpy(&mcast_groups[idx], &tmp_addr, sizeof(tmp_addr)); idx++; } else more++; } } for (; list; list = list->next, changes++); /* Nothing to do because of the global array, just rolling changes counters */ if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for keys '[nfacctd|sfacctd]_mcast_groups'. Globalized.\n", filename); if (more) Log(LOG_WARNING, "WARN ( %s ): Only the first %u (on a total of %u) multicast groups will be joined.\n", filename, MAX_MCAST_GROUPS, MAX_MCAST_GROUPS+more); return changes; } int cfg_key_nfacctd_sql_log(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.nfacctd_sql_log = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfacctd_sql_log = value; changes++; break; } } } return changes; } int cfg_key_nfacctd_bgp(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_daemon'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_msglog(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_msglog = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_daemon_msglog'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_aspath_radius(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 1) { Log(LOG_ERR, "WARN ( %s ): 'bgp_aspath_radius' has to be >= 1.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_aspath_radius = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_aspath_radius'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_stdcomm_pattern(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_stdcomm_pattern = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_stdcomm_pattern'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_extcomm_pattern(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_extcomm_pattern = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_extcomm_pattern'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_stdcomm_pattern_to_asn(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_stdcomm_pattern_to_asn = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_stdcomm_pattern_to_asn'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_peer_src_as_type(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (!strncmp(value_ptr, "netflow", strlen("netflow"))) value = BGP_SRC_PRIMITIVES_KEEP; else if (!strncmp(value_ptr, "sflow", strlen("sflow"))) value = BGP_SRC_PRIMITIVES_KEEP; else if (!strncmp(value_ptr, "map", strlen("map"))) value = BGP_SRC_PRIMITIVES_MAP; else if (!strncmp(value_ptr, "bgp", strlen("bgp"))) value = BGP_SRC_PRIMITIVES_BGP; else if (!strncmp(value_ptr, "fallback", strlen("fallback"))) { value = BGP_SRC_PRIMITIVES_KEEP; value |= BGP_SRC_PRIMITIVES_BGP; } else Log(LOG_WARNING, "WARN ( %s ): Ignoring uknown 'bgp_peer_src_as_type' value.\n", filename); for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_peer_as_src_type = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_peer_src_as_type'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_src_std_comm_type(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (!strncmp(value_ptr, "bgp", strlen("bgp"))) value = BGP_SRC_PRIMITIVES_BGP; else Log(LOG_WARNING, "WARN ( %s ): Ignoring uknown 'bgp_src_std_comm_type' value.\n", filename); for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_src_std_comm_type = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_src_std_comm_type'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_src_ext_comm_type(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (!strncmp(value_ptr, "bgp", strlen("bgp"))) value = BGP_SRC_PRIMITIVES_BGP; else Log(LOG_WARNING, "WARN ( %s ): Ignoring uknown 'bgp_src_ext_comm_type' value.\n", filename); for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_src_ext_comm_type = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_src_ext_comm_type'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_src_as_path_type(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (!strncmp(value_ptr, "bgp", strlen("bgp"))) value = BGP_SRC_PRIMITIVES_BGP; else Log(LOG_WARNING, "WARN ( %s ): Ignoring uknown 'bgp_src_as_path_type' value.\n", filename); for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_src_as_path_type = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_src_as_path_type'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_src_local_pref_type(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (!strncmp(value_ptr, "map", strlen("map"))) value = BGP_SRC_PRIMITIVES_MAP; else if (!strncmp(value_ptr, "bgp", strlen("bgp"))) value = BGP_SRC_PRIMITIVES_BGP; else Log(LOG_WARNING, "WARN ( %s ): Ignoring uknown 'bgp_src_local_pref_type' value.\n", filename); for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_src_local_pref_type = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_src_local_pref_type'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_src_med_type(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (!strncmp(value_ptr, "map", strlen("map"))) value = BGP_SRC_PRIMITIVES_MAP; else if (!strncmp(value_ptr, "bgp", strlen("bgp"))) value = BGP_SRC_PRIMITIVES_BGP; else Log(LOG_WARNING, "WARN ( %s ): Ignoring uknown 'bgp_src_med_type' value.\n", filename); for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_src_med_type = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_src_med_type'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_peer_as_skip_subas(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_peer_as_skip_subas = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_peer_as_skip_subas'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_src_local_pref_map(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_src_local_pref_map = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_src_local_pref_map'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_peer_src_as_map(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_peer_as_src_map = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_peer_src_as_map'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_src_med_map(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_src_med_map = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_src_med_map'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_to_agent_map(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_to_agent_map = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_to_agent_map'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_iface_to_rd_map(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_iface_to_rd_map = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_iface_rd_map'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_follow_default(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_follow_default = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_follow_default'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_follow_nexthop(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; char *count_token; int changes = 0, idx = 0, valid; trim_all_spaces(value_ptr); while ((count_token = extract_token(&value_ptr, ',')) && idx < FOLLOW_BGP_NH_ENTRIES) { for (list = plugins_list; list; list = list->next) { valid = str2prefix(count_token, &list->cfg.nfacctd_bgp_follow_nexthop[idx]); if (!valid) { Log(LOG_WARNING, "WARN ( %s ): bgp_follow_nexthop: invalid IP prefix '%s'.\n", filename, count_token); break; } } if (valid) idx++; } changes = idx; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_follow_nexthop'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_neighbors_file(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_neighbors_file = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_neighbors_file'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_max_peers(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 1) { Log(LOG_ERR, "WARN ( %s ): 'nfacctd_bgp_max_peers' has to be >= 1.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_max_peers = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_daemon_max_peers'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_ip(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_ip = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_daemon_ip'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_port(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if ((value <= 0) || (value > 65535)) { Log(LOG_ERR, "WARN ( %s ): 'bgp_daemon_port' has to be in the range 0-65535.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_port = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_daemon_port'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_ip_precedence(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if ((value < 0) || (value > 7)) { Log(LOG_ERR, "WARN ( %s ): 'bgp_daemon_ipprec' has to be in the range 0-7.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.nfacctd_bgp_ipprec = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_daemon_ipprec'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_bgp_table_peer_buckets(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if ((value <= 0) || (value > 1000)) { Log(LOG_ERR, "WARN ( %s ): 'bgp_table_peer_buckets' has to be in the range 1-1000.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.bgp_table_peer_buckets = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'bgp_table_peer_buckets'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_isis(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.nfacctd_isis = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'isis_daemon'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_isis_ip(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_isis_ip = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'isis_daemon_ip'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_isis_net(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_isis_net = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'isis_daemon_net'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_isis_iface(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.nfacctd_isis_iface = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'isis_daemon_iface'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_isis_mtu(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < SNAPLEN_ISIS_MIN) { Log(LOG_WARNING, "WARN ( %s ): 'isis_daemon_mtu' has to be >= %d.\n", filename, SNAPLEN_ISIS_MIN); return ERR; } for (; list; list = list->next, changes++) list->cfg.nfacctd_isis_mtu = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'isis_daemon_mtu'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_isis_msglog(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.nfacctd_isis_msglog = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'isis_daemon_msglog'. Globalized.\n", filename); return changes; } int cfg_key_pmacctd_force_frag_handling(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.handle_fragments = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pmacctd_force_frag_handling'. Globalized.\n", filename); return changes; } int cfg_key_pmacctd_frag_buffer_size(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_ERR, "WARN ( %s ): 'pmacctd_frag_buffer_size' has to be > 0.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.frag_bufsz = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pmacctd_frag_buffer_size'. Globalized.\n", filename); return changes; } int cfg_key_pmacctd_flow_buffer_size(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_ERR, "WARN ( %s ): 'pmacctd_flow_buffer_size' has to be > 0.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.flow_bufsz = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pmacctd_flow_buffer_size'. Globalized.\n", filename); return changes; } int cfg_key_pmacctd_flow_buffer_buckets(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_WARNING, "WARN ( %s ): 'flow_buffer_buckets' has to be > 0.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.flow_hashsz = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pmacctd_flow_buffer_buckets'. Globalized.\n", filename); return changes; } int cfg_key_pmacctd_conntrack_buffer_size(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_ERR, "WARN ( %s ): 'pmacctd_conntrack_buffer_size' has to be > 0.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.conntrack_bufsz = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pmacctd_conntrack_buffer_size'. Globalized.\n", filename); return changes; } int cfg_key_pmacctd_flow_lifetime(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_ERR, "WARN ( %s ): 'pmacctd_flow_lifetime' has to be > 0.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.flow_lifetime = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pmacctd_flow_lifetime'. Globalized.\n", filename); return changes; } int cfg_key_pmacctd_ext_sampling_rate(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 1) { Log(LOG_ERR, "WARN ( %s ): 'pmacctd_ext_sampling_rate' has to be >= 1.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.ext_sampling_rate = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pmacctd_ext_sampling_rate'. Globalized.\n", filename); return changes; } int cfg_key_sfacctd_renormalize(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.sfacctd_renormalize = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'sfacctd_renormalize'. Globalized.\n", filename); return changes; } int cfg_key_pcap_savefile(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.pcap_savefile = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'pcap_savefile'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_as_new(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (!strcmp(value_ptr, "false")) value = NF_AS_KEEP; else if (!strcmp(value_ptr, "true") || !strcmp(value_ptr, "file")) value = NF_AS_NEW; else if (!strcmp(value_ptr, "bgp")) value = NF_AS_BGP; else if (!strcmp(value_ptr, "fallback")) { value = NF_AS_FALLBACK; if (config.acct_type == ACCT_NF || config.acct_type == ACCT_SF) { value |= NF_AS_KEEP; value |= NF_AS_BGP; } else value = NF_AS_BGP; /* NF_AS_KEEP does not apply to ACCT_PM and ACCT_UL; we set value to NF_AS_BGP since we can't fallback to any alternative method as of yet */ } else { Log(LOG_ERR, "WARN ( %s ): Invalid AS aggregation value '%s'\n", filename, value_ptr); return ERR; } for (; list; list = list->next, changes++) list->cfg.nfacctd_as = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key '[nf|pm|sf|ua]acctd_as_new'. Globalized.\n", filename); return changes; } int cfg_key_nfacctd_net(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (!strcmp(value_ptr, "sflow") || !strcmp(value_ptr, "netflow")) value = NF_NET_KEEP; else if (!strcmp(value_ptr, "file")) value = NF_NET_NEW; else if (!strcmp(value_ptr, "mask")) value = NF_NET_STATIC; else if (!strcmp(value_ptr, "bgp")) value = NF_NET_BGP; else if (!strcmp(value_ptr, "igp")) value = NF_NET_IGP; else if (!strcmp(value_ptr, "fallback")) { value = NF_NET_FALLBACK; if (config.acct_type == ACCT_NF || config.acct_type == ACCT_SF) { value |= NF_NET_KEEP; value |= NF_NET_BGP; value |= NF_NET_IGP; } else { value |= NF_NET_BGP; value |= NF_NET_IGP; } } else { Log(LOG_ERR, "WARN ( %s ): Invalid network aggregation value '%s'\n", filename, value_ptr); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.nfacctd_net = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfacctd_net = value; changes++; break; } } } return changes; } int cfg_key_nfacctd_disable_checks(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; for (; list; list = list->next, changes++) list->cfg.nfacctd_disable_checks = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key '[ns]facctd_disable_checks'. Globalized.\n", filename); return changes; } int cfg_key_classifiers(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; for (; list; list = list->next, changes++) list->cfg.classifiers_path = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'classifiers'. Globalized.\n", filename); return changes; } int cfg_key_classifier_tentatives(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_INFO, "INFO ( %s ): 'classifier_tentatives' has to be >= 1.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.classifier_tentatives = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'classifier_tentatives'. Globalized.\n", filename); return changes; } int cfg_key_classifier_table_num(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value <= 0) { Log(LOG_INFO, "INFO ( %s ): 'classifier_table_num' has to be >= 1.\n", filename); return ERR; } for (; list; list = list->next, changes++) list->cfg.classifier_table_num = value; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'classifier_table_num'. Globalized.\n", filename); return changes; } int cfg_key_nfprobe_timeouts(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.nfprobe_timeouts = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_timeouts = value_ptr; changes++; break; } } } return changes; } int cfg_key_nfprobe_hoplimit(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if ((value < 1) || (value > 255)) { Log(LOG_ERR, "WARN ( %s ): 'nfprobe_hoplimit' has to be in the range 1-255.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.nfprobe_hoplimit = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_hoplimit = value; changes++; break; } } } return changes; } int cfg_key_nfprobe_maxflows(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 1) { Log(LOG_ERR, "WARN ( %s ): 'nfprobe_maxflows' has to be >= 1.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.nfprobe_maxflows = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_maxflows = value; changes++; break; } } } return changes; } int cfg_key_nfprobe_receiver(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.nfprobe_receiver = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_receiver = value_ptr; changes++; break; } } } return changes; } int cfg_key_nfprobe_source_ip(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.nfprobe_source_ip = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_source_ip = value_ptr; changes++; break; } } } return changes; } int cfg_key_nfprobe_version(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value != 1 && value != 5 && value != 9 && value != 10) { Log(LOG_ERR, "WARN ( %s ): 'nfprobe_version' has to be either 1/5/9/10.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.nfprobe_version = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_version = value; changes++; break; } } } return changes; } int cfg_key_nfprobe_engine(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.nfprobe_engine = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_engine = value_ptr; changes++; break; } } } return changes; } int cfg_key_nfprobe_peer_as(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.nfprobe_peer_as = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_peer_as = value; changes++; break; } } } return changes; } int cfg_key_nfprobe_ip_precedence(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if ((value <= 0) || (value > 7)) { Log(LOG_ERR, "WARN ( %s ): 'nfprobe_ipprec' and 'sfprobe_ipprec' have to be in the range 0-7.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.nfprobe_ipprec = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_ipprec = value; changes++; break; } } } return changes; } int cfg_key_nfprobe_direction(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; if (!strcmp(value_ptr, "tag")) value = DIRECTION_TAG; else if (!strcmp(value_ptr, "tag2")) value = DIRECTION_TAG2; else if (!strcmp(value_ptr, "in")) value = DIRECTION_IN; else if (!strcmp(value_ptr, "out")) value = DIRECTION_OUT; else { Log(LOG_ERR, "WARN ( %s ): Invalid nfprobe_direction or sfprobe_direction value '%s'\n", filename, value_ptr); return ERR; } if (!name) { Log(LOG_ERR, "ERROR ( %s ): nfprobe_direction and sfprobe_direction cannot be global. Not loaded.\n", filename); changes++; } else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_direction = value; changes++; break; } } } return changes; } int cfg_key_nfprobe_ifindex(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0, value2 = 0; u_int32_t value = 0; if (!strcmp(value_ptr, "tag")) value2 = IFINDEX_TAG; else if (!strcmp(value_ptr, "tag2")) value2 = IFINDEX_TAG2; else if (value = strtol(value_ptr, NULL, 0)) value2 = IFINDEX_STATIC; else { Log(LOG_ERR, "WARN ( %s ): Invalid nfprobe_ifindex or sfprobe_ifindex value '%s'\n", filename, value_ptr); return ERR; } if (!name) { Log(LOG_ERR, "ERROR ( %s ): nfprobe_ifindex and sfprobe_ifindex cannot be global. Not loaded.\n", filename); changes++; } else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.nfprobe_ifindex = value; list->cfg.nfprobe_ifindex_type = value2; changes++; break; } } } return changes; } int cfg_key_sfprobe_receiver(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sfprobe_receiver = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sfprobe_receiver = value_ptr; changes++; break; } } } return changes; } int cfg_key_sfprobe_agentip(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; if (!name) for (; list; list = list->next, changes++) list->cfg.sfprobe_agentip = value_ptr; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sfprobe_agentip = value_ptr; changes++; break; } } } return changes; } int cfg_key_sfprobe_agentsubid(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 0) { Log(LOG_ERR, "WARN ( %s ): 'sfprobe_agentsubid' has to be >= 0.\n", filename); return ERR; } if (!name) for (; list; list = list->next, changes++) list->cfg.sfprobe_agentsubid = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sfprobe_agentsubid = value; changes++; break; } } } return changes; } int cfg_key_sfprobe_ifspeed(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; u_int64_t value; value = strtoll(value_ptr, NULL, 0); if (!name) for (; list; list = list->next, changes++) list->cfg.sfprobe_ifspeed = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.sfprobe_ifspeed = value; changes++; break; } } } return changes; } int cfg_key_tee_transparent(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.tee_transparent = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.tee_transparent = value; changes++; break; } } } return changes; } void parse_time(char *filename, char *value, int *mu, int *howmany) { int k, j, len; len = strlen(value); for (j = 0; j < len; j++) { if (!isdigit(value[j])) { if (value[j] == 'm') *mu = COUNT_MINUTELY; else if (value[j] == 'h') *mu = COUNT_HOURLY; else if (value[j] == 'd') *mu = COUNT_DAILY; else if (value[j] == 'w') *mu = COUNT_WEEKLY; else if (value[j] == 'M') *mu = COUNT_MONTHLY; else { Log(LOG_WARNING, "WARN ( %s ): Ignoring unknown time measuring unit: '%c'.\n", filename, value[j]); *mu = 0; *howmany = 0; return; } if (*mu) { value[j] = '\0'; break; } } } k = atoi(value); if (k > 0) *howmany = k; else { Log(LOG_WARNING, "WARN ( %s ): ignoring invalid time value: %d\n", filename, k); *mu = 0; *howmany = 0; } } int cfg_key_uacctd_group(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); if (value < 1 || value > 32) return ERR; for (; list; list = list->next, changes++) list->cfg.uacctd_group = (1 << (value-1)); return changes; } int cfg_key_uacctd_nl_size(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = atoi(value_ptr); for (; list; list = list->next, changes++) list->cfg.uacctd_nl_size = value; return changes; } int cfg_key_tunnel_0(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int changes = 0; trim_all_spaces(value_ptr); for (; list; list = list->next, changes++) list->cfg.tunnel0 = value_ptr; if (name) Log(LOG_WARNING, "WARN ( %s ): plugin name not supported for key 'tunnel_0'. Globalized.\n", filename); return changes; } int cfg_key_xlate_src(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.xlate_src = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.xlate_src = value; changes++; break; } } } return changes; } int cfg_key_xlate_dst(char *filename, char *name, char *value_ptr) { struct plugins_list_entry *list = plugins_list; int value, changes = 0; value = parse_truefalse(value_ptr); if (value < 0) return ERR; if (!name) for (; list; list = list->next, changes++) list->cfg.xlate_dst = value; else { for (; list; list = list->next) { if (!strcmp(name, list->name)) { list->cfg.xlate_dst = value; changes++; break; } } } return changes; } pmacct-0.14.0/src/crc32.c0000644000175000017500000001541610530072467013733 0ustar paolopaolo/* * Copyright (C) 1986 Gary S. Brown. You may use this program, or * code or tables extracted from it, as desired without restriction. * * First, the polynomial itself and its table of feedback terms. The * polynomial is * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 * Note that we take it "backwards" and put the highest-order term in * the lowest-order bit. The X^32 term is "implied"; the LSB is the * X^31 term, etc. The X^0 term (usually shown as "+1") results in * the MSB being 1. * Note that the usual hardware shift register implementation, which * is what we're using (we're merely optimizing it by doing eight-bit * chunks at a time) shifts bits into the lowest-order term. In our * implementation, that means shifting towards the right. Why do we * do it this way? Because the calculated CRC must be transmitted in * order from highest-order term to lowest-order term. UARTs transmit * characters in order from LSB to MSB. By storing the CRC this way, * we hand it to the UART in the order low-byte to high-byte; the UART * sends each low-bit to hight-bit; and the result is transmission bit * by bit from highest- to lowest-order term without requiring any bit * shuffling on our part. Reception works similarly. * The feedback terms table consists of 256, 32-bit entries. Notes: * * 1. The table can be generated at runtime if desired; code to do so * is shown later. It might not be obvious, but the feedback * terms simply represent the results of eight shift/xor opera- * tions for all combinations of data and CRC register values. * * 2. The CRC accumulation logic is the same for all CRC polynomials, * be they sixteen or thirty-two bits wide. You simply choose the * appropriate table. Alternatively, because the table can be * generated at runtime, you can start by generating the table for * the polynomial in question and use exactly the same "updcrc", * if your application needn't simultaneously handle two CRC * polynomials. (Note, however, that XMODEM is strange.) * * 3. For 16-bit CRCs, the table entries need be only 16 bits wide; * of course, 32-bit entries work OK if the high 16 bits are zero. * * 4. The values must be right-shifted by eight bits by the "updcrc" * logic; the shift must be unsigned (bring in zeroes). On some * hardware you could probably optimize the shift in assembler by * using byte-swap instructions. */ static unsigned int crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ 0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U, 0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U, 0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U, 0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU, 0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U, 0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U, 0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U, 0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU, 0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U, 0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU, 0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U, 0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U, 0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U, 0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU, 0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU, 0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U, 0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU, 0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U, 0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U, 0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U, 0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU, 0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U, 0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U, 0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU, 0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U, 0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U, 0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U, 0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U, 0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U, 0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU, 0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU, 0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U, 0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U, 0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU, 0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU, 0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U, 0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU, 0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U, 0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU, 0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U, 0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU, 0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U, 0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U, 0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU, 0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U, 0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U, 0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U, 0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U, 0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U, 0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U, 0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU, 0x2d02ef8dU }; static unsigned int __crc32 = 0x0U; /* XXX: to be optimized */ /* Inline unsigned int cache_crc32(const unsigned char *buf, unsigned int len) { unsigned int crc = __crc32; while (len--) crc = crc_32_tab[(crc ^ (*buf++)) & 0xff] ^ (crc >> 8); return crc; } */ Inline unsigned int cache_crc32(const unsigned char *buf, unsigned int len) { unsigned int hash = 5381; unsigned int i = 0; for (i = 0; i < len; buf++, i++) { hash = ((hash << 5) + hash) + (*buf); } return hash; } pmacct-0.14.0/src/pmacct.h0000644000175000017500000001715711734647140014302 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #ifndef _PMACCT_H_ #define _PMACCT_H_ /* includes */ #ifdef HAVE_PCAP_PCAP_H #include #endif #ifdef HAVE_PCAP_H #include #endif #include #include #include #include #include #include #ifdef HAVE_GETOPT_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined (MAP_ANONYMOUS) #if defined (MAP_ANON) #define MAP_ANONYMOUS MAP_ANON #else #define MAP_ANONYMOUS 0 #define USE_DEVZERO 1 #endif #endif #if !defined INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 #endif #if !defined INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN 46 #endif #if (defined SOLARIS) && (defined CPU_sparc) #define htons(x) (x) #define htonl(x) (x) #endif #if (!defined HAVE_U_INT8_T) && (defined HAVE_UINT8_T) #define u_int8_t uint8_t #endif #if (!defined HAVE_U_INT16_T) && (defined HAVE_UINT16_T) #define u_int16_t uint16_t #endif #if (!defined HAVE_U_INT32_T) && (defined HAVE_UINT32_T) #define u_int32_t uint32_t #endif #if (!defined HAVE_U_INT64_T) && (defined HAVE_UINT64_T) #define u_int64_t uint64_t #endif #if defined HAVE_64BIT_COUNTERS #define MOREBUFSZ 32 #else #define MOREBUFSZ 0 #endif #ifndef LOCK_UN #define LOCK_UN 8 #endif #ifndef LOCK_EX #define LOCK_EX 2 #endif #ifdef NOINLINE #define Inline #else #define Inline static inline #endif /* Let work the unaligned copy macros the hard way: byte-per byte copy via u_char pointers. We discard the packed attribute way because it fits just to GNU compiler */ #if !defined NEED_ALIGN #define Assign8(a, b) a = b #else #define Assign8(a, b) \ { \ u_char *ptr = (u_char *)&a; \ *ptr = b; \ } #endif #if !defined NEED_ALIGN #define Assign16(a, b) a = b #else #define Assign16(a, b) \ { \ u_int16_t c = b; \ u_char *dst = (u_char *)&a; \ u_char *src = (u_char *)&c; \ *(dst + 0) = *(src + 0); \ *(dst + 1) = *(src + 1); \ } #endif #if !defined NEED_ALIGN #define Assign32(a, b) a = b #else #define Assign32(a, b) \ { \ u_int32_t c = b; \ u_char *dst = (u_char *)&a; \ u_char *src = (u_char *)&c; \ *(dst + 0) = *(src + 0); \ *(dst + 1) = *(src + 1); \ *(dst + 2) = *(src + 2); \ *(dst + 3) = *(src + 3); \ } #endif struct plugin_requests { u_int8_t bpf_filter; /* On-request packet copy for BPF purposes */ }; #include "pmacct-defines.h" #include "network.h" #include "pretag.h" #include "cfg.h" #include "util.h" #include "xflow_status.h" #include "log.h" #include "once.h" #include "mpls.h" /* * htonvl(): host to network (byte ordering) variable length * ntohvl(): network to host (byer ordering) variable length * * This is in order to handle meaningfully both 32bit and 64bit * counters avoiding a bunch of #if-#else statements. */ #if defined HAVE_64BIT_COUNTERS #define htonvl(x) pm_htonll(x) #define ntohvl(x) pm_ntohll(x) #define CACHE_THRESHOLD UINT64T_THRESHOLD #else #define htonvl(x) htonl(x) #define ntohvl(x) ntohl(x) #define CACHE_THRESHOLD UINT32T_THRESHOLD #endif /* structures */ struct pcap_device { pcap_t *dev_desc; int link_type; int active; struct _devices_struct *data; }; struct pcap_callback_data { u_char * f_agent; u_char * idt; u_char * bta_table; u_char * bpas_table; u_char * blp_table; u_char * bmed_table; u_char * biss_table; struct pcap_device *device; u_int16_t ifindex_in; u_int16_t ifindex_out; }; struct _protocols_struct { char name[PROTO_LEN]; int number; }; struct _devices_struct { void (*handler)(const struct pcap_pkthdr *, register struct packet_ptrs *); int link_type; }; struct smallbuf { u_char base[SRVBUFLEN]; u_char *end; u_char *ptr; }; struct largebuf { u_char base[LARGEBUFLEN]; u_char *end; u_char *ptr; }; struct child_ctl { u_int16_t active; u_int16_t retired; u_int32_t flags; }; #define INIT_BUF(x) \ memset(x.base, 0, sizeof(x.base)); \ x.end = x.base+sizeof(x.base); \ x.ptr = x.base; /* prototypes */ void startup_handle_falling_child(); void handle_falling_child(); void ignore_falling_child(); void my_sigint_handler(); void reload(); void push_stats(); void reload_maps(); #if (!defined __LL_C) #define EXT extern #else #define EXT #endif EXT char sll_mac[2][ETH_ADDR_LEN]; EXT void null_handler(const struct pcap_pkthdr *, register struct packet_ptrs *); EXT void eth_handler(const struct pcap_pkthdr *, register struct packet_ptrs *); EXT void fddi_handler(const struct pcap_pkthdr *, register struct packet_ptrs *); EXT void tr_handler(const struct pcap_pkthdr *, register struct packet_ptrs *); EXT u_int16_t mpls_handler(u_char *, u_int16_t *, u_int16_t *, register struct packet_ptrs *); EXT void ppp_handler(const struct pcap_pkthdr *, register struct packet_ptrs *); EXT void ieee_802_11_handler(const struct pcap_pkthdr *, register struct packet_ptrs *); EXT void sll_handler(const struct pcap_pkthdr *, register struct packet_ptrs *); EXT void raw_handler(const struct pcap_pkthdr *, register struct packet_ptrs *); EXT u_char *llc_handler(const struct pcap_pkthdr *, u_int, register u_char *, register struct packet_ptrs *); EXT void chdlc_handler(const struct pcap_pkthdr *, register struct packet_ptrs *); #undef EXT #if (!defined __NL_C) #define EXT extern #else #define EXT #endif EXT int ip_handler(register struct packet_ptrs *); EXT int ip6_handler(register struct packet_ptrs *); EXT int gtp_tunnel_func(register struct packet_ptrs *); EXT int gtp_tunnel_configurator(struct tunnel_handler *, char *); EXT void tunnel_registry_init(); EXT void pcap_cb(u_char *, const struct pcap_pkthdr *, const u_char *); EXT void PM_find_id(struct id_table *, struct packet_ptrs *, pm_id_t *, pm_id_t *); EXT void compute_once(); #undef EXT #if (!defined __PMACCTD_C) && (!defined __NFACCTD_C) && (!defined __SFACCTD_C) && (!defined __UACCTD_C) #define EXT extern #else #define EXT #endif EXT struct host_addr mcast_groups[MAX_MCAST_GROUPS]; EXT int reload_map, reload_map_bgp_thread, data_plugins, tee_plugins; EXT struct child_ctl sql_writers; #undef EXT size_t strlcpy(char *, const char *, size_t); /* global variables */ pcap_t *glob_pcapt; struct pcap_stat ps; #if (!defined __PMACCTD_C) && (!defined __NFACCTD_C) && (!defined __SFACCTD_C) && (!defined __UACCTD_C) extern int debug; extern int have_num_memory_pools; /* global getopt() stuff */ extern struct configuration config; /* global configuration structure */ extern struct plugins_list_entry *plugins_list; /* linked list of each plugin configuration */ extern pid_t failed_plugins[MAX_N_PLUGINS]; /* plugins failed during startup phase */ extern u_char dummy_tlhdr[16]; #endif #endif /* _PMACCT_H_ */ pmacct-0.14.0/src/plugin_hooks.c0000644000175000017500000004720711672203713015521 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define __PLUGIN_HOOKS_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "thread_pool.h" #include "plugin_hooks.h" #include "pkt_handlers.h" /* functions */ /* load_plugins() starts plugin processes; creates pipes and handles them inserting in channels_list structure */ /* when not using map_shared, 'pipe_size' is the size of the pipe created with socketpair(); when map_shared is enabled, it refers to the size of the shared memory area */ void load_plugins(struct plugin_requests *req) { int x, v, socklen, nfprobe_id = 0, min_sz = 0; struct plugins_list_entry *list = plugins_list; int l = sizeof(list->cfg.pipe_size); struct channels_list_entry *chptr = NULL; init_random_seed(); init_pipe_channels(); while (list) { if ((*list->type.func)) { if (list->cfg.data_type & (PIPE_TYPE_METADATA|PIPE_TYPE_PAYLOAD|PIPE_TYPE_MSG)); else { Log(LOG_ERR, "ERROR ( %s/%s ): Data type not supported: %d\n", list->name, list->type.string, list->cfg.data_type); exit(1); } min_sz = ChBufHdrSz; if (list->cfg.data_type & PIPE_TYPE_METADATA) min_sz += PdataSz; if (list->cfg.data_type & PIPE_TYPE_PAYLOAD) { if (list->cfg.acct_type == ACCT_PM && list->cfg.snaplen) min_sz += (PpayloadSz+list->cfg.snaplen); else min_sz += (PpayloadSz+DEFAULT_PLOAD_SIZE); } if (list->cfg.data_type & PIPE_TYPE_EXTRAS) min_sz += PextrasSz; if (list->cfg.data_type & PIPE_TYPE_BGP) min_sz += PbgpSz; if (list->cfg.data_type & PIPE_TYPE_MSG) min_sz += PmsgSz; /* If nothing is supplied, let's hint some working default values */ if (list->cfg.pcap_savefile && !list->cfg.pipe_size && !list->cfg.buffer_size) { list->cfg.pipe_size = 4096000; /* 4Mb */ list->cfg.buffer_size = 10240; /* 10Kb */ } /* creating communication channel */ socketpair(AF_UNIX, SOCK_DGRAM, 0, list->pipe); if (list->cfg.pipe_size) { if (list->cfg.pipe_size < min_sz) list->cfg.pipe_size = min_sz; } else { x = DEFAULT_PIPE_SIZE; Setsocksize(list->pipe[0], SOL_SOCKET, SO_RCVBUF, &x, l); Setsocksize(list->pipe[1], SOL_SOCKET, SO_SNDBUF, &x, l); } /* checking SO_RCVBUF and SO_SNDBUF values; if different we take the smaller one */ getsockopt(list->pipe[0], SOL_SOCKET, SO_RCVBUF, &v, &l); x = v; getsockopt(list->pipe[1], SOL_SOCKET, SO_SNDBUF, &v, &l); socklen = (v < x) ? v : x; /* checking Core <-> Plugins buffer size; then, if required let's align it on 4 bytes boundary -- on the assumption that data strucures are aligned aswell. */ if (list->cfg.buffer_size < min_sz) list->cfg.buffer_size = min_sz; #if NEED_ALIGN while (list->cfg.buffer_size % 4 != 0) list->cfg.buffer_size--; #endif if (list->cfg.data_type == PIPE_TYPE_PAYLOAD) { /* Let's tweak plugin_pipe_size if we don't have an explicit size */ if (!list->cfg.pipe_size) list->cfg.pipe_size = 4096000; /* 4Mb */ } /* if we are not supplied a 'plugin_pipe_size', then we calculate it using buffer size and given socket size; if 'plugin_pipe_size' is known, we reverse the method: we try to obtain needed socket size to accomodate given pipe and buffer size */ if (!list->cfg.pipe_size) { list->cfg.pipe_size = (socklen/sizeof(char *))*list->cfg.buffer_size; if ((list->cfg.debug) || (list->cfg.pipe_size > WARNING_PIPE_SIZE)) { Log(LOG_INFO, "INFO ( %s/%s ): %d bytes are available to address shared memory segment; buffer size is %d bytes.\n", list->name, list->type.string, socklen, list->cfg.buffer_size); Log(LOG_INFO, "INFO ( %s/%s ): Trying to allocate a shared memory segment of %d bytes.\n", list->name, list->type.string, list->cfg.pipe_size); } } else { if (list->cfg.buffer_size > list->cfg.pipe_size) list->cfg.buffer_size = list->cfg.pipe_size; x = (list->cfg.pipe_size/list->cfg.buffer_size)*sizeof(char *); if (x > socklen) { Setsocksize(list->pipe[0], SOL_SOCKET, SO_RCVBUF, &x, l); Setsocksize(list->pipe[1], SOL_SOCKET, SO_SNDBUF, &x, l); } socklen = x; getsockopt(list->pipe[0], SOL_SOCKET, SO_RCVBUF, &v, &l); x = v; getsockopt(list->pipe[1], SOL_SOCKET, SO_SNDBUF, &v, &l); if (x > v) x = v; if ((x < socklen) || (list->cfg.debug)) Log(LOG_INFO, "INFO ( %s/%s ): Pipe size obtained: %d / %d.\n", list->name, list->type.string, x, socklen); } list->cfg.name = list->name; list->cfg.type = list->type.string; chptr = insert_pipe_channel(list->type.id, &list->cfg, list->pipe[1]); if (!chptr) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to setup a new Core Process <-> Plugin channel.\nExiting.\n", list->name, list->type.string); exit_all(1); } else chptr->plugin = list; /* sets new value to be assigned to 'wakeup'; 'TRUE' disables on-request wakeup */ if (list->type.id == PLUGIN_ID_MEMORY) chptr->request = TRUE; /* sets cleaner routine; XXX: we should definitely refine the way it works, maybe by looking at stacking more of them, ie. extras assumes it's automagically piled with metadata */ if (list->cfg.data_type & PIPE_TYPE_METADATA) chptr->clean_func = pkt_data_clean; if (list->cfg.data_type & PIPE_TYPE_PAYLOAD) chptr->clean_func = pkt_payload_clean; if (list->cfg.data_type & PIPE_TYPE_EXTRAS) chptr->clean_func = pkt_extras_clean; if (list->cfg.data_type & PIPE_TYPE_BGP) chptr->clean_func = pkt_bgp_clean; if (list->cfg.data_type & PIPE_TYPE_MSG) chptr->clean_func = pkt_msg_clean; /* sets nfprobe ID */ if (list->type.id == PLUGIN_ID_NFPROBE) { list->cfg.nfprobe_id = nfprobe_id; nfprobe_id++; } switch (list->pid = fork()) { case 0: /* Child */ /* SIGCHLD handling issue: SysV avoids zombies by ignoring SIGCHLD; to emulate such semantics on BSD systems, we need an handler like handle_falling_child() */ #if defined (IRIX) || (SOLARIS) signal(SIGCHLD, SIG_IGN); #else signal(SIGCHLD, ignore_falling_child); #endif close(config.sock); close(list->pipe[1]); (*list->type.func)(list->pipe[0], &list->cfg, chptr); exit(0); default: /* Parent */ close(list->pipe[0]); setnonblocking(list->pipe[1]); break; } /* some residual check */ if (chptr && list->cfg.a_filter) req->bpf_filter = TRUE; } list = list->next; } sort_pipe_channels(); } void exec_plugins(struct packet_ptrs *pptrs) { int num, size, already_reprocessed = 0; u_int32_t savedptr; char *bptr; int index; for (index = 0; channels_list[index].aggregation; index++) { if (evaluate_filters(&channels_list[index].agg_filter, pptrs->packet_ptr, pptrs->pkthdr) && !evaluate_tags(&channels_list[index].tag_filter, pptrs->tag) && !evaluate_tags(&channels_list[index].tag2_filter, pptrs->tag2) && !check_shadow_status(pptrs, &channels_list[index])) { /* arranging buffer: supported primitives + packet total length */ reprocess: channels_list[index].reprocess = FALSE; num = 0; /* rg.ptr points to slot's base address into the ring (shared memory); bufptr works as a displacement into the slot to place sequentially packets */ bptr = channels_list[index].rg.ptr+ChBufHdrSz+channels_list[index].bufptr; size = (*channels_list[index].clean_func)(bptr); savedptr = channels_list[index].bufptr; reset_fallback_status(pptrs); while (channels_list[index].phandler[num]) { (*channels_list[index].phandler[num])(&channels_list[index], pptrs, &bptr); num++; } if (channels_list[index].s.rate && !channels_list[index].s.sampled_pkts) { channels_list[index].reprocess = FALSE; channels_list[index].bufptr = savedptr; channels_list[index].hdr.num--; /* let's cheat this value as it will get increased later */ size = 0; } if (channels_list[index].reprocess) { /* Let's check if we have an issue with the buffer size */ if (already_reprocessed) { struct plugins_list_entry *list = channels_list[index].plugin; Log(LOG_ERR, "ERROR ( %s/%s ): plugin_buffer_size is too short.\n", list->name, list->type.string); exit_all(1); } already_reprocessed = TRUE; /* Let's cheat the size in order to send out the current buffer */ size = channels_list[index].plugin->cfg.pipe_size; } else { channels_list[index].hdr.num++; channels_list[index].bufptr += size; } if ((channels_list[index].bufptr+size) > channels_list[index].bufend) { channels_list[index].hdr.seq++; channels_list[index].hdr.seq %= MAX_SEQNUM; ((struct ch_buf_hdr *)channels_list[index].rg.ptr)->seq = channels_list[index].hdr.seq; ((struct ch_buf_hdr *)channels_list[index].rg.ptr)->num = channels_list[index].hdr.num; if (channels_list[index].status->wakeup) { channels_list[index].status->backlog++; if (channels_list[index].status->backlog > ((channels_list[index].plugin->cfg.pipe_size/channels_list[index].plugin->cfg.buffer_size)*channels_list[index].plugin->cfg.pipe_backlog)/100) { channels_list[index].status->wakeup = channels_list[index].request; if (write(channels_list[index].pipe, &channels_list[index].rg.ptr, CharPtrSz) != CharPtrSz) Log(LOG_WARNING, "WARN: Failed during write: %s\n", strerror(errno)); channels_list[index].status->backlog = 0; } } channels_list[index].rg.ptr += channels_list[index].bufsize; if ((channels_list[index].rg.ptr+channels_list[index].bufsize) > channels_list[index].rg.end) channels_list[index].rg.ptr = channels_list[index].rg.base; /* rewind pointer */ channels_list[index].bufptr = channels_list[index].buf; channels_list[index].hdr.num = 0; if (channels_list[index].reprocess) goto reprocess; } } } } struct channels_list_entry *insert_pipe_channel(int plugin_type, struct configuration *cfg, int pipe) { struct channels_list_entry *chptr; int index = 0, x; while (index < MAX_N_PLUGINS) { chptr = &channels_list[index]; if (!chptr->aggregation) { /* found room */ chptr->aggregation = cfg->what_to_count; chptr->pipe = pipe; chptr->agg_filter.table = cfg->bpfp_a_table; chptr->agg_filter.num = (int *) &cfg->bpfp_a_num; chptr->bufsize = cfg->buffer_size; chptr->id = cfg->post_tag; if (cfg->sampling_rate && plugin_type != PLUGIN_ID_SFPROBE) { /* sfprobe cares for itself */ chptr->s.rate = cfg->sampling_rate; if (cfg->acct_type == ACCT_NF) chptr->s.sf = &take_simple_systematic_skip; else chptr->s.sf = &take_simple_random_skip; } memcpy(&chptr->tag_filter, &cfg->ptf, sizeof(struct pretag_filter)); memcpy(&chptr->tag2_filter, &cfg->pt2f, sizeof(struct pretag_filter)); chptr->buf = 0; chptr->bufptr = chptr->buf; chptr->bufend = cfg->buffer_size-sizeof(struct ch_buf_hdr); /* +1550 (NETFLOW_MSG_SIZE) has been introduced as a margin as a countermeasure against the reception of malicious NetFlow v9 templates */ chptr->rg.base = map_shared(0, cfg->pipe_size+1550, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if (chptr->rg.base == MAP_FAILED) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate pipe buffer. Exiting ...\n", cfg->name, cfg->type); exit_all(1); } memset(chptr->rg.base, 0, cfg->pipe_size); chptr->rg.ptr = chptr->rg.base; chptr->rg.end = chptr->rg.base+cfg->pipe_size; chptr->status = map_shared(0, sizeof(struct ch_status), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if (chptr->status == MAP_FAILED) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate status buffer. Exiting ...\n", cfg->name, cfg->type); exit_all(1); } memset(chptr->status, 0, sizeof(struct ch_status)); break; } else chptr = NULL; index++; } return chptr; } void delete_pipe_channel(int pipe) { struct channels_list_entry *chptr; int index = 0, index2; while (index < MAX_N_PLUGINS) { chptr = &channels_list[index]; if (chptr->pipe == pipe) { chptr->aggregation = FALSE; /* we ensure that any plugin is depending on the one being removed via the 'same_aggregate' flag */ if (!chptr->same_aggregate) { index2 = index; for (index2++; index2 < MAX_N_PLUGINS; index2++) { chptr = &channels_list[index2]; if (!chptr->aggregation) break; /* we finished channels */ if (chptr->same_aggregate) { chptr->same_aggregate = FALSE; break; } else break; /* we have nothing to do */ } } index2 = index; for (index2++; index2 < MAX_N_PLUGINS; index2++) { chptr = &channels_list[index2]; if (chptr->aggregation) { memcpy(&channels_list[index], chptr, sizeof(struct channels_list_entry)); memset(chptr, 0, sizeof(struct channels_list_entry)); index++; } else break; /* we finished channels */ } break; } index++; } } /* trivial sorting(tm) :-) */ void sort_pipe_channels() { struct channels_list_entry ctmp; int x = 0, y = 0; while (x < MAX_N_PLUGINS) { if (!channels_list[x].aggregation) break; y = x+1; while (y < MAX_N_PLUGINS) { if (!channels_list[y].aggregation) break; if (channels_list[x].aggregation == channels_list[y].aggregation) { channels_list[y].same_aggregate = TRUE; if (y == x+1) x++; else { memcpy(&ctmp, &channels_list[x+1], sizeof(struct channels_list_entry)); memcpy(&channels_list[x+1], &channels_list[y], sizeof(struct channels_list_entry)); memcpy(&channels_list[y], &ctmp, sizeof(struct channels_list_entry)); x++; } } y++; } x++; } } void init_pipe_channels() { memset(&channels_list, 0, MAX_N_PLUGINS*sizeof(struct channels_list_entry)); } void evaluate_sampling(struct sampling *smp, pm_counter_t *pkt_len, pm_counter_t *pkt_num, pm_counter_t *sample_pool) { pm_counter_t delta, pkts = *pkt_num; if (!smp->rate) { /* sampling is disabled */ smp->sample_pool = pkts; smp->sampled_pkts = pkts; return; } smp->sampled_pkts = 0; run_again: if (!smp->counter) smp->counter = (smp->sf)(smp->rate); delta = MIN(smp->counter, pkts); smp->counter -= delta; pkts -= delta; smp->sample_pool += delta; if (!smp->counter) { smp->sampled_pkts++; *sample_pool = smp->sample_pool; smp->sample_pool = 0; if (pkts > 0) goto run_again; } /* Let's handle flows meaningfully */ if (smp->sampled_pkts && *pkt_num > 1) { *pkt_len = ( *pkt_len / *pkt_num ) * smp->sampled_pkts; *pkt_num = smp->sampled_pkts; } } /* simple random algorithm */ pm_counter_t take_simple_random_skip(pm_counter_t mean) { pm_counter_t skip; if (mean > 1) { skip = ((random() % ((2 * mean) - 1)) + 1); srandom(random()); } else skip = 1; /* smp->rate == 1 */ return skip; } /* simple systematic algorithm */ pm_counter_t take_simple_systematic_skip(pm_counter_t mean) { pm_counter_t skip = mean; return skip; } /* return value: TRUE: We want it! FALSE: Discard it! */ int evaluate_tags(struct pretag_filter *filter, pm_id_t tag) { int index; if (filter->num == 0) return FALSE; /* no entries in the filter array: tag filtering disabled */ for (index = 0; index < filter->num; index++) { if (filter->table[index].n <= tag && filter->table[index].r >= tag) return (FALSE | filter->table[index].neg); else if (filter->table[index].neg) return FALSE; } return TRUE; } /* return value: TRUE: We want it! FALSE: Discard it! */ int evaluate_filters(struct aggregate_filter *filter, char *pkt, struct pcap_pkthdr *pkthdr) { int index; if (*filter->num == 0) return TRUE; /* no entries in the filter array: aggregate filtering disabled */ for (index = 0; index < *filter->num; index++) { if (bpf_filter(filter->table[index]->bf_insns, pkt, pkthdr->len, pkthdr->caplen)) return TRUE; } return FALSE; } void recollect_pipe_memory(struct channels_list_entry *mychptr) { struct channels_list_entry *chptr; int index = 0; while (index < MAX_N_PLUGINS) { chptr = &channels_list[index]; if (mychptr->rg.base != chptr->rg.base) { munmap(chptr->rg.base, (chptr->rg.end-chptr->rg.base)+1550); munmap(chptr->status, sizeof(struct ch_status)); } index++; } } void init_random_seed() { struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); srandom((unsigned int)tv.tv_usec); } void fill_pipe_buffer() { int index; for (index = 0; channels_list[index].aggregation; index++) { channels_list[index].hdr.seq++; channels_list[index].hdr.seq %= MAX_SEQNUM; ((struct ch_buf_hdr *)channels_list[index].rg.ptr)->seq = channels_list[index].hdr.seq; ((struct ch_buf_hdr *)channels_list[index].rg.ptr)->num = channels_list[index].hdr.num; if (channels_list[index].status->wakeup) { channels_list[index].status->wakeup = channels_list[index].request; if (write(channels_list[index].pipe, &channels_list[index].rg.ptr, CharPtrSz) != CharPtrSz) Log(LOG_WARNING, "WARN: Failed during write: %s\n", strerror(errno)); } } } int check_shadow_status(struct packet_ptrs *pptrs, struct channels_list_entry *mychptr) { if (pptrs->shadow) { if (pptrs->tag && mychptr->aggregation & COUNT_ID) return FALSE; else if (pptrs->tag2 && mychptr->aggregation & COUNT_ID2) return FALSE; else return TRUE; } else return FALSE; } void load_plugin_filters(int link_type) { struct plugins_list_entry *list = plugins_list; while (list) { if ((*list->type.func)) { /* compiling aggregation filter if needed */ if (list->cfg.a_filter) { pcap_t *dev_desc; bpf_u_int32 localnet, netmask = 0; /* pcap library stuff */ char errbuf[PCAP_ERRBUF_SIZE], *count_token; int idx = 0; dev_desc = pcap_open_dead(link_type, 128); /* 128 bytes should be long enough */ if (config.dev) pcap_lookupnet(config.dev, &localnet, &netmask, errbuf); list->cfg.bpfp_a_table[idx] = malloc(sizeof(struct bpf_program)); while ( (count_token = extract_token(&list->cfg.a_filter, ',')) && idx < AGG_FILTER_ENTRIES ) { if (pcap_compile(dev_desc, list->cfg.bpfp_a_table[idx], count_token, 0, netmask) < 0) { Log(LOG_WARNING, "WARN: %s\nWARN ( %s/%s ): aggregation filter disabled.\n", pcap_geterr(dev_desc), list->cfg.name, list->cfg.type); } else { idx++; list->cfg.bpfp_a_table[idx] = malloc(sizeof(struct bpf_program)); } } list->cfg.bpfp_a_num = idx; } } list = list->next; } } int pkt_data_clean(void *pdata) { memset(pdata, 0, PdataSz); return PdataSz; } int pkt_payload_clean(void *ppayload) { memset(ppayload, 0, PpayloadSz); return PpayloadSz; } int pkt_msg_clean(void *ppayload) { memset(ppayload, 0, PmsgSz); return PmsgSz; } int pkt_extras_clean(void *pextras) { memset(pextras, 0, PdataSz+PextrasSz); return PdataSz+PextrasSz; } int pkt_bgp_clean(void *pbgp) { memset(pbgp, 0, PdataSz+PbgpSz); return PdataSz+PbgpSz; } pmacct-0.14.0/src/pmpgplay.c0000644000175000017500000010020311664553345014645 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #define __PMACCT_PLAYER_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "sql_common.h" #include "pgsql_plugin.h" #include "ip_flow.h" #include "classifier.h" #define ARGS "df:o:n:sthieP:T:U:D:H:" struct logfile_header lh; char sql_pwd[SRVBUFLEN]; int re = 0, we = 0; int debug = 0; int sql_dont_try_update = 0; int sql_history_since_epoch = 0; char *sql_table, *sql_user, *sql_db; char timebuf[SRVBUFLEN]; void usage(char *prog) { printf("%s\n", PMPGPLAY_USAGE_HEADER); printf("Usage: %s -f [ filename ]\n\n", prog); printf("Available options:\n"); printf(" -d\tEnable debug\n"); printf(" -f\t[ filename ]\n\tPlay specified file\n"); printf(" -o\t[ element ]\n\tPlay file starting at specified offset element\n"); printf(" -n\t[ num ]\n\tNumbers of elements to play\n"); printf(" -t\tTest only; don't actually write to the DB\n"); printf(" -P\t[ password ]\n\tConnect to SQL server using the specified password\n"); printf(" -U\t[ user ]\n\tUse the specified user when connecting to SQL server\n"); printf(" -H\t[ host ]\n\tConnect to SQL server listening at specified hostname\n"); printf(" -D\t[ DB ]\n\tUse the specified SQL database\n"); printf(" -T\t[ table ]\n\tUse the specified SQL table\n"); printf(" -i\tDon't try update, use insert only.\n"); printf(" -e\tUse seconds since Epoch timestamps.\n"); printf("\n"); printf("For suggestions, critics, bugs, contact me: %s.\n", MANTAINER); } void print_header() { printf("NUM "); printf("ID "); #if defined (HAVE_L2) printf("SRC_MAC "); printf("DST_MAC "); printf("VLAN "); #endif printf("SRC_AS "); printf("DST_AS "); #if defined ENABLE_IPV6 printf("SRC_IP "); printf("DST_IP "); #else printf("SRC_IP "); printf("DST_IP "); #endif printf("SRC_PORT "); printf("DST_PORT "); printf("PROTOCOL "); printf("TOS "); #if defined HAVE_64BIT_COUNTERS printf("PACKETS "); printf("FLOWS "); printf("BYTES "); #else printf("PACKETS "); printf("FLOWS "); printf("BYTES "); #endif printf("BASETIME\n"); } void print_data(struct db_cache *cache_elem, u_int32_t wtc, int num) { struct pkt_primitives *data = &cache_elem->primitives; struct tm *lt; char src_mac[18], dst_mac[18], src_host[INET6_ADDRSTRLEN], dst_host[INET6_ADDRSTRLEN]; int j; printf("%-8d ", num); printf("%-5d ", data->id); #if defined (HAVE_L2) etheraddr_string(data->eth_shost, src_mac); printf("%-17s ", src_mac); etheraddr_string(data->eth_dhost, dst_mac); printf("%-17s ", dst_mac); printf("%-5d ", data->vlan_id); #endif printf("%-5d ", data->src_as); printf("%-5d ", data->dst_as); #if defined ENABLE_IPV6 addr_to_str(src_host, &data->src_ip); printf("%-45s ", src_host); addr_to_str(dst_host, &data->dst_ip); printf("%-45s ", dst_host); #else addr_to_str(src_host, &data->src_ip); printf("%-15s ", src_host); addr_to_str(dst_host, &data->dst_ip); printf("%-15s ", dst_host); #endif printf("%-5d ", data->src_port); printf("%-5d ", data->dst_port); printf("%-10s ", _protocols[data->proto].name); printf("%-3d ", data->tos); #if defined HAVE_64BIT_COUNTERS printf("%-20llu ", cache_elem->packet_counter); printf("%-20llu ", cache_elem->flows_counter); printf("%-20llu ", cache_elem->bytes_counter); #else printf("%-10lu ", cache_elem->packet_counter); printf("%-10lu ", cache_elem->flows_counter); printf("%-10lu ", cache_elem->bytes_counter); #endif if (lh.sql_history) { if (!sql_history_since_epoch) { lt = localtime(&cache_elem->basetime); strftime(timebuf, SRVBUFLEN, "%Y-%m-%d %H:%M:%S" , lt); printf("%s\n", timebuf); } else printf("%u\n", cache_elem->basetime); } else printf("0\n"); } int main(int argc, char **argv) { struct insert_data idata; PGresult *ret; FILE *f; unsigned char fbuf[SRVBUFLEN]; char logfile[SRVBUFLEN]; char default_pwd[] = "arealsmartpwd"; int have_pwd = 0, have_logfile = 0, n; int result = 0, position = 0, howmany = 0; int do_nothing = 0; char *cl_sql_host = NULL, *cl_sql_user = NULL, *cl_sql_db = NULL, *cl_sql_table = NULL; char *sql_host; struct template_entry *teptr; int tot_size = 0, cnt = 0; u_char *te; struct template_header th; struct db_cache data; /* getopt() stuff */ extern char *optarg; extern int optind, opterr, optopt; int errflag = 0, cp; memset(&idata, 0, sizeof(idata)); memset(sql_data, 0, sizeof(sql_data)); memset(update_clause, 0, sizeof(update_clause)); memset(insert_clause, 0, sizeof(insert_clause)); memset(lock_clause, 0, sizeof(lock_clause)); memset(where, 0, sizeof(where)); memset(values, 0, sizeof(values)); memset(&data, 0, sizeof(data)); memset(timebuf, 0, sizeof(timebuf)); pp_size = sizeof(struct db_cache); while (!errflag && ((cp = getopt(argc, argv, ARGS)) != -1)) { switch (cp) { case 'd': debug = TRUE; break; case 'f': strlcpy(logfile, optarg, sizeof(logfile)); have_logfile = TRUE; break; case 'o': position = atoi(optarg); if (!position) { printf("ERROR: invalid offset. Exiting.\n"); exit(1); } break; case 'n': howmany = atoi(optarg); if (!howmany) { printf("ERROR: invalid number of elements. Exiting.\n"); exit(1); } break; case 't': do_nothing = TRUE; break; case 'i': sql_dont_try_update = TRUE; break; case 'e': sql_history_since_epoch = TRUE; break; case 'P': strlcpy(sql_pwd, optarg, sizeof(sql_pwd)); have_pwd = TRUE; break; case 'U': cl_sql_user = malloc(SRVBUFLEN); memset(cl_sql_user, 0, SRVBUFLEN); strlcpy(cl_sql_user, optarg, SRVBUFLEN); break; case 'D': cl_sql_db = malloc(SRVBUFLEN); memset(cl_sql_db, 0, SRVBUFLEN); strlcpy(cl_sql_db, optarg, SRVBUFLEN); break; case 'H': cl_sql_host = malloc(SRVBUFLEN); memset(cl_sql_host, 0, SRVBUFLEN); strlcpy(cl_sql_host, optarg, SRVBUFLEN); break; case 'T': cl_sql_table = malloc(SRVBUFLEN); memset(cl_sql_table, 0, SRVBUFLEN); strlcpy(cl_sql_table, optarg, SRVBUFLEN); break; case 'h': usage(argv[0]); exit(0); break; default: usage(argv[0]); exit(1); } } /* searching for user supplied values */ if (!howmany) howmany = -1; if (!have_pwd) memcpy(sql_pwd, default_pwd, sizeof(default_pwd)); if (!have_logfile) { usage(argv[0]); printf("\nERROR: missing logfile (-f)\nExiting...\n"); exit(1); } f = fopen(logfile, "r"); if (!f) { printf("ERROR: %s does not exists\nExiting...\n", logfile); exit(1); } fread(&lh, sizeof(lh), 1, f); lh.sql_table_version = ntohs(lh.sql_table_version); lh.sql_optimize_clauses = ntohs(lh.sql_optimize_clauses); lh.sql_history = ntohs(lh.sql_history); lh.what_to_count = ntohl(lh.what_to_count); lh.magic = ntohl(lh.magic); if (lh.magic == MAGIC) { if (debug) printf("OK: Valid logfile header read.\n"); printf("sql_db: %s\n", lh.sql_db); printf("sql_table: %s\n", lh.sql_table); printf("sql_user: %s\n", lh.sql_user); printf("sql_host: %s\n", lh.sql_host); if (cl_sql_db||cl_sql_table||cl_sql_user||cl_sql_host) printf("OK: Overrided by commandline options:\n"); if (cl_sql_db) printf("sql_db: %s\n", cl_sql_db); if (cl_sql_table) printf("sql_table: %s\n", cl_sql_table); if (cl_sql_user) printf("sql_user: %s\n", cl_sql_user); if (cl_sql_host) printf("sql_host: %s\n", cl_sql_host); } else { printf("ERROR: Invalid magic number. Exiting.\n"); exit(1); } /* binding SQL stuff */ if (cl_sql_db) sql_db = cl_sql_db; else sql_db = lh.sql_db; if (cl_sql_table) sql_table = cl_sql_table; else sql_table = lh.sql_table; if (cl_sql_user) sql_user = cl_sql_user; else sql_user = lh.sql_user; if (cl_sql_host) sql_host = cl_sql_host; else sql_host = lh.sql_host; fread(&th, sizeof(th), 1, f); th.magic = ntohl(th.magic); th.num = ntohs(th.num); th.sz = ntohs(th.sz); if (th.magic == TH_MAGIC) { if (debug) printf("OK: Valid template header read.\n"); if (th.num > N_PRIMITIVES) { printf("ERROR: maximum number of primitives exceeded. Exiting.\n"); exit(1); } te = malloc(th.num*sizeof(struct template_entry)); memset(te, 0, th.num*sizeof(struct template_entry)); fread(te, th.num*sizeof(struct template_entry), 1, f); } else { if (debug) printf("ERROR: no template header found.\n"); exit(1); } /* checking template */ if (th.sz >= sizeof(fbuf)) { printf("ERROR: Objects are too big. Exiting.\n"); exit(1); } teptr = (struct template_entry *) te; for (tot_size = 0, cnt = 0; cnt < th.num; cnt++, teptr++) tot_size += teptr->size; if (tot_size != th.sz) { printf("ERROR: malformed template header. Size mismatch. Exiting.\n"); exit(1); } TPL_check_sizes(&th, &data, te); if (!do_nothing) { PG_compose_conn_string(&p, sql_host); if (!PG_DB_Connect2(&p)) { printf("ALERT: PG_DB_Connect2(): PGSQL daemon failed.\n"); exit(1); } } else { if (debug) print_header(); } /* composing the proper (filled with primitives used during the current execution) SQL strings */ idata.num_primitives = PG_compose_static_queries(); idata.now = time(NULL); /* handling offset */ if (position) n = fseek(f, (th.sz*position), SEEK_CUR); /* handling single or iterative request */ if (!do_nothing) ret = PQexec(p.desc, lock_clause); while(!feof(f)) { if (!howmany) break; else if (howmany > 0) howmany--; memset(fbuf, 0, th.sz); n = fread(fbuf, th.sz, 1, f); if (n) { re++; TPL_pop(fbuf, &data, &th, te); if (!do_nothing) result = PG_cache_dbop(&p, &data, &idata); else { if (debug) print_data(&data, lh.what_to_count, (position+re)); } if (!result) we++; if (re != we) printf("WARN: unable to write element %u.\n", re); } } if (!do_nothing) { ret = PQexec(p.desc, "COMMIT"); if (PQresultStatus(ret) != PGRES_COMMAND_OK) { we = 0; /* if we fail to commit, no elements will be written */ PQclear(ret); } printf("\nOK: written [%u/%u] elements.\n", we, re); } else printf("OK: read [%u] elements.\n", re); PQfinish(p.desc); fclose(f); return 0; } int PG_cache_dbop(struct DBdesc *db, struct db_cache *cache_elem, struct insert_data *idata) { PGresult *ret; char *ptr_values, *ptr_where, *err_string; int num=0, have_flows=0; if (lh.what_to_count & COUNT_FLOWS) have_flows = TRUE; /* constructing SQL query */ ptr_where = where_clause; ptr_values = values_clause; while (num < idata->num_primitives) { (*where[num].handler)(cache_elem, idata, num, &ptr_values, &ptr_where); num++; } if (have_flows) snprintf(sql_data, sizeof(sql_data), update_clause, cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter, time(NULL)); else snprintf(sql_data, sizeof(sql_data), update_clause, cache_elem->packet_counter, cache_elem->bytes_counter, time(NULL)); strncat(sql_data, where_clause, SPACELEFT(sql_data)); /* sending UPDATE query */ if (!sql_dont_try_update) { ret = PQexec(db->desc, sql_data); if (PQresultStatus(ret) != PGRES_COMMAND_OK) { err_string = PQresultErrorMessage(ret); PQclear(ret); printf("FAILED query follows:\n%s\n", sql_data); printf("%s\n", err_string); return TRUE; } PQclear(ret); } if (sql_dont_try_update || (!PG_affected_rows(ret))) { /* UPDATE failed, trying with an INSERT query */ strncpy(sql_data, insert_clause, sizeof(sql_data)); #if defined HAVE_64BIT_COUNTERS if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), ", %llu, %llu, %llu)", cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), ", %llu, %llu)", cache_elem->packet_counter, cache_elem->bytes_counter); #else if (have_flows) snprintf(ptr_values, SPACELEFT(values_clause), ", %lu, %lu, %lu)", cache_elem->packet_counter, cache_elem->bytes_counter, cache_elem->flows_counter); else snprintf(ptr_values, SPACELEFT(values_clause), ", %lu, %lu)", cache_elem->packet_counter, cache_elem->bytes_counter); #endif strncat(sql_data, values_clause, SPACELEFT(sql_data)); ret = PQexec(db->desc, sql_data); if (PQresultStatus(ret) != PGRES_COMMAND_OK) { err_string = PQresultErrorMessage(ret); PQclear(ret); printf("FAILED query follows:\n%s\n", sql_data); printf("%s\n", err_string); return TRUE; } PQclear(ret); } if (debug) printf("%s\n\n", sql_data); return FALSE; } int PG_evaluate_history(int primitive) { if (lh.sql_history) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (!sql_history_since_epoch) strncat(where[primitive].string, "ABSTIME(%u)::Timestamp::Timestamp without time zone = ", SPACELEFT(where[primitive].string)); else strncat(where[primitive].string, "%u = ", SPACELEFT(where[primitive].string)); strncat(where[primitive].string, "stamp_inserted", SPACELEFT(where[primitive].string)); strncat(insert_clause, "stamp_updated, stamp_inserted", SPACELEFT(insert_clause)); if (!sql_history_since_epoch) strncat(values[primitive].string, "ABSTIME(%u)::Timestamp, ABSTIME(%u)::Timestamp", SPACELEFT(values[primitive].string)); else strncat(values[primitive].string, "%u, %u", SPACELEFT(values[primitive].string)); where[primitive].type = values[primitive].type = TIMESTAMP; values[primitive].handler = where[primitive].handler = count_timestamp_handler; primitive++; } return primitive; } int PG_evaluate_primitives(int primitive) { u_int64_t what_to_count = 0, fakes = 0; short int assume_custom_table = FALSE; if (lh.sql_optimize_clauses) { what_to_count = lh.what_to_count; assume_custom_table = TRUE; } else { /* we are requested to avoid optimization; then we'll construct an all-true "what to count" bitmap. */ if (lh.what_to_count & COUNT_SRC_MAC) what_to_count |= COUNT_SRC_MAC; else fakes |= FAKE_SRC_MAC; if (lh.what_to_count & COUNT_DST_MAC) what_to_count |= COUNT_DST_MAC; else fakes |= FAKE_DST_MAC; if (lh.what_to_count & (COUNT_SRC_HOST|COUNT_SRC_NET)) what_to_count |= COUNT_SRC_HOST; else if (lh.what_to_count & COUNT_SRC_AS) what_to_count |= COUNT_SRC_AS; else if (lh.what_to_count & COUNT_SUM_HOST) what_to_count |= COUNT_SUM_HOST; else if (lh.what_to_count & COUNT_SUM_NET) what_to_count |= COUNT_SUM_NET; else if (lh.what_to_count & COUNT_SUM_AS) what_to_count |= COUNT_SUM_AS; else { if (lh.what_to_count & COUNT_DST_AS) what_to_count |= COUNT_SRC_AS; else fakes |= FAKE_SRC_HOST; } if (lh.what_to_count & (COUNT_DST_HOST|COUNT_DST_NET)) what_to_count |= COUNT_DST_HOST; else if (lh.what_to_count & COUNT_DST_AS) what_to_count |= COUNT_DST_AS; else { if (lh.what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS)) what_to_count |= COUNT_DST_AS; else fakes |= FAKE_DST_HOST; } if (lh.what_to_count & COUNT_SUM_PORT) what_to_count |= COUNT_SUM_PORT; if (lh.what_to_count & COUNT_SUM_MAC) what_to_count |= COUNT_SUM_MAC; what_to_count |= COUNT_SRC_PORT|COUNT_DST_PORT|COUNT_IP_PROTO|COUNT_ID|COUNT_VLAN|COUNT_IP_TOS; } /* 1st part: arranging pointers to an opaque structure and composing the static selection (WHERE) string */ #if defined (HAVE_L2) if (what_to_count & (COUNT_SRC_MAC|COUNT_SUM_MAC)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mac_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mac_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_MAC; values[primitive].handler = where[primitive].handler = count_src_mac_handler; primitive++; } if (what_to_count & COUNT_DST_MAC) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mac_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mac_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_MAC; values[primitive].handler = where[primitive].handler = count_dst_mac_handler; primitive++; } if (what_to_count & COUNT_VLAN) { int count_it = FALSE; if ((lh.sql_table_version < 2) && !assume_custom_table) { if (lh.what_to_count & COUNT_VLAN) { printf("ERROR: The use of VLAN accounting requires SQL table v2. Exiting.\n"); exit(1); } else what_to_count ^= COUNT_VLAN; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "vlan", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "vlan=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_VLAN; values[primitive].handler = where[primitive].handler = count_vlan_handler; primitive++; } } #endif if (what_to_count & (COUNT_SRC_HOST|COUNT_SRC_NET|COUNT_SUM_HOST|COUNT_SUM_NET)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_HOST; values[primitive].handler = where[primitive].handler = count_src_host_handler; primitive++; } if (what_to_count & (COUNT_DST_HOST|COUNT_DST_NET)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_HOST; values[primitive].handler = where[primitive].handler = count_dst_host_handler; primitive++; } if (what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (lh.sql_table_version >= 6) { strncat(insert_clause, "as_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "as_src=%u", SPACELEFT(where[primitive].string)); } else { strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%u\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=\'%u\'", SPACELEFT(where[primitive].string)); } values[primitive].type = where[primitive].type = COUNT_SRC_AS; values[primitive].handler = where[primitive].handler = count_src_as_handler; primitive++; } if (what_to_count & COUNT_DST_AS) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (lh.sql_table_version >= 6) { strncat(insert_clause, "as_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "as_dst=%u", SPACELEFT(where[primitive].string)); } else { strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%u\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=\'%u\'", SPACELEFT(where[primitive].string)); } values[primitive].type = where[primitive].type = COUNT_DST_AS; values[primitive].handler = where[primitive].handler = count_dst_as_handler; primitive++; } if (what_to_count & (COUNT_SRC_PORT|COUNT_SUM_PORT)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "port_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "port_src=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_PORT; values[primitive].handler = where[primitive].handler = count_src_port_handler; primitive++; } if (what_to_count & COUNT_DST_PORT) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "port_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "port_dst=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_PORT; values[primitive].handler = where[primitive].handler = count_dst_port_handler; primitive++; } if (what_to_count & COUNT_IP_TOS) { int count_it = FALSE; if ((lh.sql_table_version < 3) && !assume_custom_table) { if (lh.what_to_count & COUNT_IP_TOS) { printf("ERROR: The use of ToS/DSCP accounting requires SQL table v3. Exiting.\n"); exit(1); } else what_to_count ^= COUNT_IP_TOS; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "tos", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "tos=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_IP_TOS; values[primitive].handler = where[primitive].handler = count_ip_tos_handler; primitive++; } } if (what_to_count & COUNT_IP_PROTO) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_proto", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_proto=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_IP_PROTO; values[primitive].handler = where[primitive].handler = PG_count_ip_proto_handler; primitive++; } if (what_to_count & COUNT_ID) { int count_it = FALSE; if ((lh.sql_table_version < 2) && !assume_custom_table) { if (lh.what_to_count & COUNT_ID) { printf("ERROR: The use of IDs requires SQL table version 2. Exiting.\n"); exit(1); } else what_to_count ^= COUNT_ID; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "agent_id", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "agent_id=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_ID; values[primitive].handler = where[primitive].handler = count_id_handler; primitive++; } } if (fakes & FAKE_SRC_MAC) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mac_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mac_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_SRC_MAC; values[primitive].handler = where[primitive].handler = fake_mac_handler; primitive++; } if (fakes & FAKE_DST_MAC) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mac_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mac_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_DST_MAC; values[primitive].handler = where[primitive].handler = fake_mac_handler; primitive++; } if (fakes & FAKE_SRC_HOST) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_SRC_HOST; values[primitive].handler = where[primitive].handler = fake_host_handler; primitive++; } if (fakes & FAKE_DST_HOST) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, ", ", sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_DST_HOST; values[primitive].handler = where[primitive].handler = fake_host_handler; primitive++; } return primitive; } int PG_compose_static_queries() { int primitives=0, have_flows=0; if (lh.what_to_count & COUNT_FLOWS || (lh.sql_table_version >= 4 && !lh.sql_optimize_clauses)) { lh.what_to_count |= COUNT_FLOWS; have_flows = TRUE; if (lh.sql_table_version < 4 && !lh.sql_optimize_clauses) { printf("ERROR: The accounting of flows requires SQL table v4. Exiting.\n"); exit(1); } } /* "INSERT INTO ... VALUES ... " and "... WHERE ..." stuff */ strncpy(where[primitives].string, " WHERE ", sizeof(where[primitives].string)); snprintf(insert_clause, sizeof(insert_clause), "INSERT INTO %s (", sql_table); strncpy(values[primitives].string, " VALUES (", sizeof(values[primitives].string)); primitives = PG_evaluate_history(primitives); primitives = PG_evaluate_primitives(primitives); strncat(insert_clause, ", packets, bytes", SPACELEFT(insert_clause)); if (have_flows) strncat(insert_clause, ", flows", SPACELEFT(insert_clause)); strncat(insert_clause, ")", SPACELEFT(insert_clause)); /* "LOCK ..." stuff */ if (sql_dont_try_update) snprintf(lock_clause, sizeof(lock_clause), "BEGIN;"); else snprintf(lock_clause, sizeof(lock_clause), "BEGIN; LOCK %s IN EXCLUSIVE MODE;", sql_table); /* "UPDATE ... SET ..." stuff */ snprintf(update_clause, sizeof(update_clause), "UPDATE %s ", sql_table); #if defined HAVE_64BIT_COUNTERS strncat(update_clause, "SET packets=packets+%llu, bytes=bytes+%llu", SPACELEFT(update_clause)); if (have_flows) strncat(update_clause, ", flows=flows+%llu", SPACELEFT(update_clause)); #else strncat(update_clause, "SET packets=packets+%lu, bytes=bytes+%lu", SPACELEFT(update_clause)); if (have_flows) strncat(update_clause, ", flows=flows+%lu", SPACELEFT(update_clause)); #endif if (lh.sql_history) { if (!sql_history_since_epoch) strncat(update_clause, ", stamp_updated=CURRENT_TIMESTAMP(0)", SPACELEFT(update_clause)); else strncat(update_clause, ", stamp_updated=DATE_PART('epoch',NOW())::BIGINT", SPACELEFT(update_clause)); } return primitives; } void PG_exit_gracefully(int signum) { printf("\nOK: written [%u/%u] elements.\n", we, re); exit(0); } void PG_compose_conn_string(struct DBdesc *db, char *host) { char *string; int slen = SRVBUFLEN; if (!db->conn_string) { db->conn_string = (char *) malloc(slen); string = db->conn_string; snprintf(string, slen, "dbname=%s user=%s password=%s", sql_db, sql_user, sql_pwd); slen -= strlen(string); string += strlen(string); if (host) snprintf(string, slen, " host=%s", host); } } int PG_DB_Connect2(struct DBdesc *db) { db->desc = PQconnectdb(db->conn_string); if (PQstatus(db->desc) == CONNECTION_BAD) db->connected = FALSE; else db->connected = TRUE; return db->connected; } static int PG_affected_rows(PGresult *result) { return atoi(PQcmdTuples(result)); } /* Dummy version of bgp_rd2str() to resolve code dependencies */ int bgp_rd2str(u_char *str, rd_t *rd) { return TRUE; } pmacct-0.14.0/src/pmacct-dlt.h0000644000175000017500000003773011147231147015054 0ustar paolopaolo/*- * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * * This code is derived from the Stanford/CMU enet packet filter, * (net/enet.c) distributed as part of 4.3BSD, and code contributed * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence * Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)bpf.h 7.1 (Berkeley) 5/7/91 * * @(#) $Header: /home/repo-0.14/pmacct/src/pmacct-dlt.h,v 1.1.1.1 2009/02/19 10:20:23 paolo Exp $ (LBL) */ /* * This is the DLT definition section of original libpcap bpf.h header; * it includes only the what is required by the code generator and the * userland BPF interpreter, and the libpcap APIs for setting filters, * etc.. * * "pcap-bpf.c" will include the native OS version, as it deals with * the OS's BPF implementation. */ /* * Data-link level type codes. * * Do *NOT* add new values to this list without asking * "tcpdump-workers@tcpdump.org" for a value. Otherwise, you run the * risk of using a value that's already being used for some other purpose, * and of having tools that read libpcap-format captures not being able * to handle captures with your new DLT_ value, with no hope that they * will ever be changed to do so (as that would destroy their ability * to read captures using that value for that other purpose). */ /* * These are the types that are the same on all platforms, and that * have been defined by for ages. */ #ifndef DLT_NULL #define DLT_NULL 0 /* no link-layer encapsulation */ #endif #ifndef DLT_EN10MB #define DLT_EN10MB 1 /* Ethernet (10Mb) */ #endif #ifndef DLT_EN3MB #define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */ #endif #ifndef DLT_AX25 #define DLT_AX25 3 /* Amateur Radio AX.25 */ #endif #ifndef DLT_PRONET #define DLT_PRONET 4 /* Proteon ProNET Token Ring */ #endif #ifndef DLT_CHAOS #define DLT_CHAOS 5 /* Chaos */ #endif #ifndef DLT_IEEE802 #define DLT_IEEE802 6 /* IEEE 802 Networks */ #endif #ifndef DLT_ARCNET #define DLT_ARCNET 7 /* ARCNET, with BSD-style header */ #endif #ifndef DLT_SLIP #define DLT_SLIP 8 /* Serial Line IP */ #endif #ifndef DLT_PPP #define DLT_PPP 9 /* Point-to-point Protocol */ #endif #ifndef DLT_FDDI #define DLT_FDDI 10 /* FDDI */ #endif /* * These are types that are different on some platforms, and that * have been defined by for ages. We use #ifdefs to * detect the BSDs that define them differently from the traditional * libpcap * * XXX - DLT_ATM_RFC1483 is 13 in BSD/OS, and DLT_RAW is 14 in BSD/OS, * but I don't know what the right #define is for BSD/OS. */ #ifndef DLT_ATM_RFC1483 #define DLT_ATM_RFC1483 11 /* LLC/SNAP encapsulated atm */ #endif #ifndef DLT_RAW #ifdef __OpenBSD__ #define DLT_RAW 14 /* raw IP */ #else #define DLT_RAW 12 /* raw IP */ #endif #endif /* * Given that the only OS that currently generates BSD/OS SLIP or PPP * is, well, BSD/OS, arguably everybody should have chosen its values * for DLT_SLIP_BSDOS and DLT_PPP_BSDOS, which are 15 and 16, but they * didn't. So it goes. */ #if defined(__NetBSD__) || defined(__FreeBSD__) #ifndef DLT_SLIP_BSDOS #define DLT_SLIP_BSDOS 13 /* BSD/OS Serial Line IP */ #define DLT_PPP_BSDOS 14 /* BSD/OS Point-to-point Protocol */ #endif #else #define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */ #define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */ #endif /* * 17 is used for DLT_PFLOG in OpenBSD; don't use it for anything else. */ #ifndef DLT_ATM_CLIP #define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */ #endif /* * These values are defined by NetBSD; other platforms should refrain from * using them for other purposes, so that NetBSD savefiles with link * types of 50 or 51 can be read as this type on all platforms. */ #ifndef DLT_PPP_SERIAL #define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */ #endif #ifndef DLT_PPP_ETHER #define DLT_PPP_ETHER 51 /* PPP over Ethernet */ #endif /* * Values between 100 and 103 are used in capture file headers as * link-layer types corresponding to DLT_ types that differ * between platforms; don't use those values for new DLT_ new types. */ /* * This value was defined by libpcap 0.5; platforms that have defined * it with a different value should define it here with that value - * a link type of 104 in a save file will be mapped to DLT_C_HDLC, * whatever value that happens to be, so programs will correctly * handle files with that link type regardless of the value of * DLT_C_HDLC. * * The name DLT_C_HDLC was used by BSD/OS; we use that name for source * compatibility with programs written for BSD/OS. * * libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well, * for source compatibility with programs written for libpcap 0.5. */ #ifndef DLT_C_HDLC #define DLT_C_HDLC 104 /* Cisco HDLC */ #endif #ifndef DLT_CHDLC #define DLT_CHDLC DLT_C_HDLC #endif #ifndef DLT_IEEE802_11 #define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */ #endif /* * 106 is reserved for Linux Classical IP over ATM; it's like DLT_RAW, * except when it isn't. (I.e., sometimes it's just raw IP, and * sometimes it isn't.) We currently handle it as DLT_LINUX_SLL, * so that we don't have to worry about the link-layer header.) */ /* * Frame Relay; BSD/OS has a DLT_FR with a value of 11, but that collides * with other values. * DLT_FR and DLT_FRELAY packets start with the Q.922 Frame Relay header * (DLCI, etc.). */ #ifndef DLT_FRELAY #define DLT_FRELAY 107 #endif /* * OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except * that the AF_ type in the link-layer header is in network byte order. * * OpenBSD defines it as 12, but that collides with DLT_RAW, so we * define it as 108 here. If OpenBSD picks up this file, it should * define DLT_LOOP as 12 in its version, as per the comment above - * and should not use 108 as a DLT_ value. */ #ifndef DLT_LOOP #define DLT_LOOP 108 #endif /* * Encapsulated packets for IPsec; DLT_ENC is 13 in OpenBSD, but that's * DLT_SLIP_BSDOS in NetBSD, so we don't use 13 for it in OSes other * than OpenBSD. */ #ifndef DLT_ENC #ifdef __OpenBSD__ #define DLT_ENC 13 #else #define DLT_ENC 109 #endif #endif /* * Values between 110 and 112 are reserved for use in capture file headers * as link-layer types corresponding to DLT_ types that might differ * between platforms; don't use those values for new DLT_ types * other than the corresponding DLT_ types. */ /* * This is for Linux cooked sockets. */ #ifndef DLT_LINUX_SLL #define DLT_LINUX_SLL 113 #endif /* * Apple LocalTalk hardware. */ #ifndef DLT_LTALK #define DLT_LTALK 114 #endif /* * Acorn Econet. */ #ifndef DLT_ECONET #define DLT_ECONET 115 #endif /* * Reserved for use with OpenBSD ipfilter. */ #ifndef DLT_IPFILTER #define DLT_IPFILTER 116 #endif /* * OpenBSD DLT_PFLOG; DLT_PFLOG is 17 in OpenBSD, but that's DLT_LANE8023 * in SuSE 6.3, so we can't use 17 for it in capture-file headers. */ #ifndef DLT_PFLOG #ifdef __OpenBSD__ #define DLT_PFLOG 17 #else #define DLT_PFLOG 117 #endif #endif /* * Registered for Cisco-internal use. */ #ifndef DLT_CISCO_IOS #define DLT_CISCO_IOS 118 #endif /* * For 802.11 cards using the Prism II chips, with a link-layer * header including Prism monitor mode information plus an 802.11 * header. */ #ifndef DLT_PRISM_HEADER #define DLT_PRISM_HEADER 119 #endif /* * Reserved for Aironet 802.11 cards, with an Aironet link-layer header * (see Doug Ambrisko's FreeBSD patches). */ #ifndef DLT_AIRONET_HEADER #define DLT_AIRONET_HEADER 120 #endif /* * Reserved for Siemens HiPath HDLC. */ #ifndef DLT_HHDLC #define DLT_HHDLC 121 #endif /* * This is for RFC 2625 IP-over-Fibre Channel. * * This is not for use with raw Fibre Channel, where the link-layer * header starts with a Fibre Channel frame header; it's for IP-over-FC, * where the link-layer header starts with an RFC 2625 Network_Header * field. */ #ifndef DLT_IP_OVER_FC #define DLT_IP_OVER_FC 122 #endif /* * This is for Full Frontal ATM on Solaris with SunATM, with a * pseudo-header followed by an AALn PDU. * * There may be other forms of Full Frontal ATM on other OSes, * with different pseudo-headers. * * If ATM software returns a pseudo-header with VPI/VCI information * (and, ideally, packet type information, e.g. signalling, ILMI, * LANE, LLC-multiplexed traffic, etc.), it should not use * DLT_ATM_RFC1483, but should get a new DLT_ value, so tcpdump * and the like don't have to infer the presence or absence of a * pseudo-header and the form of the pseudo-header. */ #ifndef DLT_SUNATM #define DLT_SUNATM 123 /* Solaris+SunATM */ #endif /* * Reserved as per request from Kent Dahlgren * for private use. */ #ifndef DLT_RIO #define DLT_RIO 124 /* RapidIO */ #endif #ifndef DLT_PCI_EXP #define DLT_PCI_EXP 125 /* PCI Express */ #endif #ifndef DLT_AURORA #define DLT_AURORA 126 /* Xilinx Aurora link layer */ #endif /* * For future use with 802.11 captures - defined by AbsoluteValue * Systems to store a number of bits of link-layer information: * * http://www.shaftnet.org/~pizza/software/capturefrm.txt * * but could and arguably should also be used by non-AVS Linux * 802.11 drivers and BSD drivers; that may happen in the future. */ #ifndef DLT_IEEE802_11_RADIO #define DLT_IEEE802_11_RADIO 127 /* 802.11 plus WLAN header */ #endif /* * Reserved for the TZSP encapsulation, as per request from * Chris Waters * TZSP is a generic encapsulation for any other link type, * which includes a means to include meta-information * with the packet, e.g. signal strength and channel * for 802.11 packets. */ #ifndef DLT_TZSP #define DLT_TZSP 128 /* Tazmen Sniffer Protocol */ #endif /* * BSD's ARCNET headers have the source host, destination host, * and type at the beginning of the packet; that's what's handed * up to userland via BPF. * * Linux's ARCNET headers, however, have a 2-byte offset field * between the host IDs and the type; that's what's handed up * to userland via PF_PACKET sockets. * * We therefore have to have separate DLT_ values for them. */ #ifndef DLT_ARCNET_LINUX #define DLT_ARCNET_LINUX 129 /* ARCNET */ #endif /* * juniper-private data link types, as per request from * Hannes Gredler the DLT_s are used * for passing on chassis-internal metainformation like * QOS profiles etc. */ #ifndef DLT_JUNIPER_MLPPP #define DLT_JUNIPER_MLPPP 130 #endif #ifndef DLT_JUNIPER_MLFR #define DLT_JUNIPER_MLFR 131 #endif #ifndef DLT_JUNIPER_ES #define DLT_JUNIPER_ES 132 #endif #ifndef DLT_JUNIPER_GGSN #define DLT_JUNIPER_GGSN 133 #endif #ifndef DLT_JUNIPER_MFR #define DLT_JUNIPER_MFR 134 #endif #ifndef DLT_JUNIPER_ATM2 #define DLT_JUNIPER_ATM2 135 #endif #ifndef DLT_JUNIPER_SERVICES #define DLT_JUNIPER_SERVICES 136 #endif #ifndef DLT_JUNIPER_ATM1 #define DLT_JUNIPER_ATM1 137 #endif /* * Reserved for Apple IP-over-IEEE 1394, as per a request from Dieter * Siegmund . The header that would be presented * would be an Ethernet-like header: * * #define FIREWIRE_EUI64_LEN 8 * struct firewire_header { * u_char firewire_dhost[FIREWIRE_EUI64_LEN]; * u_char firewire_dhost[FIREWIRE_EUI64_LEN]; * u_short firewire_type; * }; * * with "firewire_type" being an Ethernet type value, rather than, * for example, raw GASP frames being handed up. */ #ifndef DLT_APPLE_IP_OVER_IEEE1394 #define DLT_APPLE_IP_OVER_IEEE1394 138 #endif /* * 139 through 142 are reserved for SS7. */ /* * Reserved for DOCSIS MAC frames. */ #ifndef DLT_DOCSIS #define DLT_DOCSIS 143 #endif /* * Linux-IrDA packets. Protocol defined at http://www.irda.org. * Those packets include IrLAP headers and above (IrLMP...), but * don't include Phy framing (SOF/EOF/CRC & byte stuffing), because Phy * framing can be handled by the hardware and depend on the bitrate. * This is exactly the format you would get capturing on a Linux-IrDA * interface (irdaX), but not on a raw serial port. * Note the capture is done in "Linux-cooked" mode, so each packet include * a fake packet header (struct sll_header). This is because IrDA packet * decoding is dependant on the direction of the packet (incomming or * outgoing). * When/if other platform implement IrDA capture, we may revisit the * issue and define a real DLT_IRDA... * Jean II */ #ifndef DLT_LINUX_IRDA #define DLT_LINUX_IRDA 144 #endif /* * Reserved for IBM SP switch and IBM Next Federation switch. */ #ifndef DLT_IBM_SP #define DLT_IBM_SP 145 #endif #ifndef DLT_IBM_SN #define DLT_IBM_SN 146 #endif /* * Reserved for private use. If you have some link-layer header type * that you want to use within your organization, with the capture files * using that link-layer header type not ever be sent outside your * organization, you can use these values. * * No libpcap release will use these for any purpose, nor will any * tcpdump release use them, either. * * Do *NOT* use these in capture files that you expect anybody not using * your private versions of capture-file-reading tools to read; in * particular, do *NOT* use them in products, otherwise you may find that * people won't be able to use tcpdump, or snort, or Ethereal, or... to * read capture files from your firewall/intrusion detection/traffic * monitoring/etc. appliance, or whatever product uses that DLT_ value, * and you may also find that the developers of those applications will * not accept patches to let them read those files. * * Instead, ask "tcpdump-workers@tcpdump.org" for a new DLT_ value, * as per the comment above. */ #ifndef DLT_USER0 #define DLT_USER0 147 #endif #ifndef DLT_USER1 #define DLT_USER1 148 #endif #ifndef DLT_USER2 #define DLT_USER2 149 #endif #ifndef DLT_USER3 #define DLT_USER3 150 #endif #ifndef DLT_USER4 #define DLT_USER4 151 #endif #ifndef DLT_USER5 #define DLT_USER5 152 #endif #ifndef DLT_USER6 #define DLT_USER6 153 #endif #ifndef DLT_USER7 #define DLT_USER7 154 #endif #ifndef DLT_USER8 #define DLT_USER8 155 #endif #ifndef DLT_USER9 #define DLT_USER9 156 #endif #ifndef DLT_USER10 #define DLT_USER10 157 #endif #ifndef DLT_USER11 #define DLT_USER11 158 #endif #ifndef DLT_USER12 #define DLT_USER12 159 #endif #ifndef DLT_USER13 #define DLT_USER13 160 #endif #ifndef DLT_USER14 #define DLT_USER14 161 #endif #ifndef DLT_USER15 #define DLT_USER15 162 #endif pmacct-0.14.0/src/nfv8_handlers.h0000644000175000017500000000341611037474163015564 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #if (!defined __NFV8_HANDLERS_C) #define EXT extern #else #define EXT #endif EXT void load_nfv8_handlers(); EXT void v8_1_filter_handler(struct packet_ptrs *, void *); EXT void v8_2_filter_handler(struct packet_ptrs *, void *); EXT void v8_3_filter_handler(struct packet_ptrs *, void *); EXT void v8_4_filter_handler(struct packet_ptrs *, void *); EXT void v8_5_filter_handler(struct packet_ptrs *, void *); EXT void v8_6_filter_handler(struct packet_ptrs *, void *); EXT void v8_7_filter_handler(struct packet_ptrs *, void *); EXT void v8_8_filter_handler(struct packet_ptrs *, void *); EXT void v8_9_filter_handler(struct packet_ptrs *, void *); EXT void v8_10_filter_handler(struct packet_ptrs *, void *); EXT void v8_11_filter_handler(struct packet_ptrs *, void *); EXT void v8_12_filter_handler(struct packet_ptrs *, void *); EXT void v8_13_filter_handler(struct packet_ptrs *, void *); EXT void v8_14_filter_handler(struct packet_ptrs *, void *); #undef EXT pmacct-0.14.0/src/uacctd.c0000640000175000017500000007401411741105377014257 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* defines */ #define __UACCTD_C /* includes */ #include "pmacct.h" #include "uacctd.h" #include "pmacct-data.h" #include "pretag_handlers.h" #include "plugin_hooks.h" #include "pkt_handlers.h" #include "ip_frag.h" #include "ip_flow.h" #include "net_aggr.h" #include "thread_pool.h" /* variables to be exported away */ int debug; struct configuration config; /* global configuration */ struct plugins_list_entry *plugins_list = NULL; /* linked list of each plugin configuration */ struct channels_list_entry channels_list[MAX_N_PLUGINS]; /* communication channels: core <-> plugins */ int have_num_memory_pools; /* global getopt() stuff */ pid_t failed_plugins[MAX_N_PLUGINS]; /* plugins failed during startup phase */ u_char dummy_tlhdr[16]; #ifdef ENABLE_ULOG /* Functions */ void usage_daemon(char *prog_name) { printf("%s\n", UACCTD_USAGE_HEADER); printf("Usage: %s [ -D | -d ] [ -g ULOG group ] [ -c primitive [ , ... ] ] [ -P plugin [ , ... ] ]\n", prog_name); printf(" %s [ -f config_file ]\n", prog_name); printf(" %s [ -h ]\n", prog_name); printf("\nGeneral options:\n"); printf(" -h \tShow this page\n"); printf(" -f \tLoad configuration from the specified file\n"); printf(" -c \t[ src_mac | dst_mac | vlan | src_host | dst_host | src_net | dst_net | src_port | dst_port |\n\t proto | tos | src_as | dst_as | sum_mac | sum_host | sum_net | sum_as | sum_port | tag |\n\t tag2 | flows | class | tcpflags | in_iface | out_iface | src_mask | dst_mask | cos | etype | none ] \n\tAggregation string (DEFAULT: src_host)\n"); printf(" -D \tDaemonize\n"); printf(" -n \tPath to a file containing Network definitions\n"); printf(" -o \tPath to a file containing Port definitions\n"); printf(" -P \t[ memory | print | mysql | pgsql | sqlite3 | nfprobe | sfprobe ] \n\tActivate plugin\n"); printf(" -d \tEnable debug\n"); printf(" -S \t[ auth | mail | daemon | kern | user | local[0-7] ] \n\tLog to the specified syslog facility\n"); printf(" -F \tWrite Core Process PID into the specified file\n"); printf(" -R \tRenormalize sampled data\n"); printf(" -g \tNetlink ULOG group\n"); printf(" -L \tNetlink socket read buffer size\n"); printf(" -u \tLeave IP protocols in numerical format\n"); printf("\nMemory plugin (-P memory) options:\n"); printf(" -p \tSocket for client-server communication (DEFAULT: /tmp/collect.pipe)\n"); printf(" -b \tNumber of buckets\n"); printf(" -m \tNumber of memory pools\n"); printf(" -s \tMemory pool size\n"); printf("\nPostgreSQL (-P pgsql)/MySQL (-P mysql)/SQLite (-P sqlite3) plugin options:\n"); printf(" -r \tRefresh time (in seconds)\n"); printf(" -v \t[ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ] \n\tTable version\n"); printf("\nPrint plugin (-P print) plugin options:\n"); printf(" -r \tRefresh time (in seconds)\n"); printf(" -O \t[ formatted | csv ] \n\tOutput format\n"); printf("\n"); printf(" See EXAMPLES or visit http://wiki.pmacct.net/ for examples.\n"); printf("\n"); printf("For suggestions, critics, bugs, contact me: %s.\n", MANTAINER); } int main(int argc,char **argv, char **envp) { bpf_u_int32 localnet, netmask; /* pcap library stuff */ struct bpf_program filter; struct pcap_device device; char errbuf[PCAP_ERRBUF_SIZE]; int index, logf; struct plugins_list_entry *list; struct plugin_requests req; char config_file[SRVBUFLEN]; int psize = ULOG_BUFLEN; struct id_table bpas_table; struct id_table blp_table; struct id_table bmed_table; struct id_table biss_table; struct id_table bta_table; struct id_table idt; struct pcap_callback_data cb_data; /* getopt() stuff */ extern char *optarg; extern int optind, opterr, optopt; int errflag, cp; /* ULOG stuff */ int ulog_fd, one = 1; struct nlmsghdr *nlh; struct sockaddr_nl nls; ulog_packet_msg_t *ulog_pkt; ssize_t len = 0; socklen_t alen; unsigned char *ulog_buffer; struct pcap_pkthdr hdr; struct timeval tv; char jumbo_container[10000]; u_int8_t mac_len; #if defined ENABLE_IPV6 struct sockaddr_storage client; #else struct sockaddr client; #endif umask(077); compute_once(); /* a bunch of default definitions */ have_num_memory_pools = FALSE; reload_map = FALSE; tag_map_allocated = FALSE; bpas_map_allocated = FALSE; blp_map_allocated = FALSE; bmed_map_allocated = FALSE; biss_map_allocated = FALSE; find_id_func = PM_find_id; errflag = 0; memset(cfg_cmdline, 0, sizeof(cfg_cmdline)); memset(&config, 0, sizeof(struct configuration)); memset(&device, 0, sizeof(struct pcap_device)); memset(&config_file, 0, sizeof(config_file)); memset(&failed_plugins, 0, sizeof(failed_plugins)); memset(&req, 0, sizeof(req)); memset(dummy_tlhdr, 0, sizeof(dummy_tlhdr)); memset(sll_mac, 0, sizeof(sll_mac)); memset(&bpas_table, 0, sizeof(bpas_table)); memset(&blp_table, 0, sizeof(blp_table)); memset(&bmed_table, 0, sizeof(bmed_table)); memset(&biss_table, 0, sizeof(biss_table)); memset(&bta_table, 0, sizeof(bta_table)); memset(&client, 0, sizeof(client)); memset(&cb_data, 0, sizeof(cb_data)); memset(&tunnel_registry, 0, sizeof(tunnel_registry)); config.acct_type = ACCT_PM; rows = 0; glob_pcapt = NULL; /* getting commandline values */ while (!errflag && ((cp = getopt(argc, argv, ARGS_UACCTD)) != -1)) { cfg_cmdline[rows] = malloc(SRVBUFLEN); switch (cp) { case 'P': strlcpy(cfg_cmdline[rows], "plugins: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'D': strlcpy(cfg_cmdline[rows], "daemonize: true", SRVBUFLEN); rows++; break; case 'd': debug = TRUE; strlcpy(cfg_cmdline[rows], "debug: true", SRVBUFLEN); rows++; break; case 'n': strlcpy(cfg_cmdline[rows], "networks_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'o': strlcpy(cfg_cmdline[rows], "ports_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'O': strlcpy(cfg_cmdline[rows], "print_output: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'u': strlcpy(cfg_cmdline[rows], "print_num_protos: true", SRVBUFLEN); rows++; break; case 'f': strlcpy(config_file, optarg, sizeof(config_file)); break; case 'F': strlcpy(cfg_cmdline[rows], "pidfile: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'c': strlcpy(cfg_cmdline[rows], "aggregate: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'b': strlcpy(cfg_cmdline[rows], "imt_buckets: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'm': strlcpy(cfg_cmdline[rows], "imt_mem_pools_number: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); have_num_memory_pools = TRUE; rows++; break; case 'p': strlcpy(cfg_cmdline[rows], "imt_path: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'r': strlcpy(cfg_cmdline[rows], "sql_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; cfg_cmdline[rows] = malloc(SRVBUFLEN); strlcpy(cfg_cmdline[rows], "print_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'v': strlcpy(cfg_cmdline[rows], "sql_table_version: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 's': strlcpy(cfg_cmdline[rows], "imt_mem_pools_size: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'S': strlcpy(cfg_cmdline[rows], "syslog: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'g': strlcpy(cfg_cmdline[rows], "uacctd_group: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'L': strlcpy(cfg_cmdline[rows], "snaplen: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'R': strlcpy(cfg_cmdline[rows], "sfacctd_renormalize: true", SRVBUFLEN); rows++; break; case 'h': usage_daemon(argv[0]); exit(0); break; default: usage_daemon(argv[0]); exit(1); break; } } /* post-checks and resolving conflicts */ if (strlen(config_file)) { if (parse_configuration_file(config_file) != SUCCESS) exit(1); } else { if (parse_configuration_file(NULL) != SUCCESS) exit(1); } /* XXX: glue; i'm conscious it's a dirty solution from an engineering viewpoint; someday later i'll fix this */ list = plugins_list; while (list) { list->cfg.acct_type = ACCT_PM; set_default_preferences(&list->cfg); if (!strcmp(list->name, "default") && !strcmp(list->type.string, "core")) memcpy(&config, &list->cfg, sizeof(struct configuration)); list = list->next; } if (config.files_umask) umask(config.files_umask); if (!config.snaplen) config.snaplen = psize; if (!config.uacctd_nl_size) config.uacctd_nl_size = psize; /* Let's check whether we need superuser privileges */ if (getuid() != 0) { printf("%s\n\n", UACCTD_USAGE_HEADER); printf("ERROR ( default/core ): You need superuser privileges to run this command.\nExiting ...\n\n"); exit(1); } if (!config.uacctd_group) { config.uacctd_group = DEFAULT_ULOG_GROUP; list = plugins_list; while (list) { list->cfg.uacctd_group = DEFAULT_ULOG_GROUP; list = list->next; } } if (config.daemon) { list = plugins_list; while (list) { if (!strcmp(list->type.string, "print")) printf("WARN ( default/core ): Daemonizing. Hmm, bye bye screen.\n"); list = list->next; } if (debug || config.debug) printf("WARN ( default/core ): debug is enabled; forking in background. Console logging will get lost.\n"); daemonize(); } initsetproctitle(argc, argv, envp); if (config.syslog) { logf = parse_log_facility(config.syslog); if (logf == ERR) { config.syslog = NULL; printf("WARN ( default/core ): specified syslog facility is not supported; logging to console.\n"); } else openlog(NULL, LOG_PID, logf); Log(LOG_INFO, "INFO ( default/core ): Start logging ...\n"); } if (config.logfile) { config.logfile_fd = open_logfile(config.logfile); list = plugins_list; while (list) { list->cfg.logfile_fd = config.logfile_fd ; list = list->next; } } /* Enforcing policies over aggregation methods */ list = plugins_list; while (list) { if (list->type.id != PLUGIN_ID_CORE) { /* applies to all plugins */ if (config.classifiers_path && (list->cfg.sampling_rate || config.ext_sampling_rate)) { Log(LOG_ERR, "ERROR ( default/core ): Packet sampling and classification are mutual exclusive.\n"); exit(1); } if (list->cfg.sampling_rate && config.ext_sampling_rate) { Log(LOG_ERR, "ERROR ( default/core ): Internal packet sampling and external packet sampling are mutual exclusive.\n"); exit(1); } if (list->type.id == PLUGIN_ID_TEE) { Log(LOG_ERR, "ERROR ( default/core ): 'tee' plugin not supported in 'uacctd'.\n"); exit(1); } else if (list->type.id == PLUGIN_ID_NFPROBE) { /* If we already renormalizing an external sampling rate, we cancel the sampling information from the probe plugin */ if (config.sfacctd_renormalize && list->cfg.ext_sampling_rate) list->cfg.ext_sampling_rate = 0; config.handle_fragments = TRUE; list->cfg.nfprobe_what_to_count = list->cfg.what_to_count; list->cfg.what_to_count = 0; #if defined (HAVE_L2) if (list->cfg.nfprobe_version == 9 || list->cfg.nfprobe_version == 10) { list->cfg.what_to_count |= COUNT_SRC_MAC; list->cfg.what_to_count |= COUNT_DST_MAC; list->cfg.what_to_count |= COUNT_VLAN; } #endif list->cfg.what_to_count |= COUNT_SRC_HOST; list->cfg.what_to_count |= COUNT_DST_HOST; if (list->cfg.networks_file || list->cfg.networks_mask || list->cfg.nfacctd_net) { list->cfg.what_to_count |= COUNT_SRC_NMASK; list->cfg.what_to_count |= COUNT_DST_NMASK; } list->cfg.what_to_count |= COUNT_SRC_PORT; list->cfg.what_to_count |= COUNT_DST_PORT; list->cfg.what_to_count |= COUNT_IP_TOS; list->cfg.what_to_count |= COUNT_IP_PROTO; if (list->cfg.networks_file || (list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP)) { list->cfg.what_to_count |= COUNT_SRC_AS; list->cfg.what_to_count |= COUNT_DST_AS; list->cfg.what_to_count |= COUNT_PEER_DST_IP; } if ((list->cfg.nfprobe_version == 9 || list->cfg.nfprobe_version == 10) && list->cfg.classifiers_path) { list->cfg.what_to_count |= COUNT_CLASS; config.handle_flows = TRUE; } if (list->cfg.pre_tag_map) { list->cfg.what_to_count |= COUNT_ID; list->cfg.what_to_count |= COUNT_ID2; } list->cfg.what_to_count |= COUNT_IN_IFACE; list->cfg.what_to_count |= COUNT_OUT_IFACE; if (list->cfg.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF| COUNT_MPLS_VPN_RD)) { Log(LOG_ERR, "ERROR ( default/core ): 'src_as', 'dst_as' and 'peer_dst_ip' are currently the only BGP-related primitives supported within the 'nfprobe' plugin.\n"); exit(1); } list->cfg.what_to_count |= COUNT_COUNTERS; list->cfg.data_type = PIPE_TYPE_METADATA; list->cfg.data_type |= PIPE_TYPE_EXTRAS; } else if (list->type.id == PLUGIN_ID_SFPROBE) { /* If we already renormalizing an external sampling rate, we cancel the sampling information from the probe plugin */ if (config.sfacctd_renormalize && list->cfg.ext_sampling_rate) list->cfg.ext_sampling_rate = 0; if (psize < 128) psize = config.snaplen = 128; /* SFL_DEFAULT_HEADER_SIZE */ list->cfg.what_to_count = COUNT_PAYLOAD; if (list->cfg.classifiers_path) { list->cfg.what_to_count |= COUNT_CLASS; config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP) { list->cfg.what_to_count |= COUNT_SRC_AS; list->cfg.what_to_count |= COUNT_DST_AS; list->cfg.what_to_count |= COUNT_PEER_DST_IP; } if ((list->cfg.nfacctd_bgp && list->cfg.nfacctd_net == NF_NET_BGP) || (list->cfg.nfacctd_isis && list->cfg.nfacctd_net == NF_NET_IGP)) { list->cfg.what_to_count |= COUNT_SRC_NMASK; list->cfg.what_to_count |= COUNT_DST_NMASK; } if (list->cfg.pre_tag_map) { list->cfg.what_to_count |= COUNT_ID; list->cfg.what_to_count |= COUNT_ID2; } if (list->cfg.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF| COUNT_MPLS_VPN_RD)) { Log(LOG_ERR, "ERROR ( default/core ): 'src_as', 'dst_as' and 'peer_dst_ip' are currently the only BGP-related primitives supported within the 'sfprobe' plugin.\n"); exit(1); } #if defined (HAVE_L2) list->cfg.what_to_count |= COUNT_VLAN; list->cfg.what_to_count |= COUNT_COS; #endif list->cfg.data_type = PIPE_TYPE_PAYLOAD; } else { evaluate_sums(&list->cfg.what_to_count, list->name, list->type.string); if (list->cfg.what_to_count & (COUNT_SRC_PORT|COUNT_DST_PORT|COUNT_SUM_PORT|COUNT_TCPFLAGS)) config.handle_fragments = TRUE; if (list->cfg.what_to_count & COUNT_FLOWS) { config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (list->cfg.what_to_count & COUNT_CLASS) { config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (!list->cfg.what_to_count) { Log(LOG_WARNING, "WARN ( %s/%s ): defaulting to SRC HOST aggregation.\n", list->name, list->type.string); list->cfg.what_to_count |= COUNT_SRC_HOST; } if ((list->cfg.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) && !list->cfg.networks_file && list->cfg.nfacctd_as != NF_AS_BGP) { Log(LOG_ERR, "ERROR ( %s/%s ): AS aggregation selected but NO 'networks_file' or 'uacctd_as' are specified. Exiting...\n\n", list->name, list->type.string); exit(1); } if (list->cfg.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET|COUNT_SUM_NET|COUNT_SRC_NMASK|COUNT_DST_NMASK|COUNT_PEER_DST_IP)) { if (!list->cfg.nfacctd_net) { if (list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; if (list->cfg.networks_mask) list->cfg.nfacctd_net |= NF_NET_STATIC; if (!list->cfg.nfacctd_net) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'uacctd_net', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } } else { if ((list->cfg.nfacctd_net == NF_NET_NEW && !list->cfg.networks_file) || (list->cfg.nfacctd_net == NF_NET_STATIC && !list->cfg.networks_mask) || (list->cfg.nfacctd_net == NF_NET_BGP && !list->cfg.nfacctd_bgp) || (list->cfg.nfacctd_net == NF_NET_IGP && !list->cfg.nfacctd_isis) || (list->cfg.nfacctd_net == NF_NET_KEEP)) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'bgp_daemon', 'isis_daemon', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } } } if (list->cfg.what_to_count & COUNT_CLASS && !list->cfg.classifiers_path) { Log(LOG_ERR, "ERROR ( %s/%s ): 'class' aggregation selected but NO 'classifiers' key specified. Exiting...\n\n", list->name, list->type.string); exit(1); } bgp_config_checks(&list->cfg); list->cfg.what_to_count |= COUNT_COUNTERS; list->cfg.data_type |= PIPE_TYPE_METADATA; } } list = list->next; } /* plugins glue: creation (since 094) */ if (config.classifiers_path) { init_classifiers(config.classifiers_path); init_conntrack_table(); } load_plugins(&req); if (config.handle_fragments) init_ip_fragment_handler(); if (config.handle_flows) init_ip_flow_handler(); load_networks(config.networks_file, &nt, &nc); #if defined (HAVE_L2) device.link_type = DLT_EN10MB; #else device.link_type = DLT_RAW; #endif for (index = 0; _devices[index].link_type != -1; index++) { if (device.link_type == _devices[index].link_type) device.data = &_devices[index]; } load_plugin_filters(device.link_type); cb_data.device = &device; /* signal handling we want to inherit to plugins (when not re-defined elsewhere) */ signal(SIGCHLD, startup_handle_falling_child); /* takes note of plugins failed during startup phase */ signal(SIGHUP, reload); /* handles reopening of syslog channel */ signal(SIGUSR1, push_stats); /* logs various statistics via Log() calls */ signal(SIGUSR2, reload_maps); /* sets to true the reload_maps flag */ signal(SIGPIPE, SIG_IGN); /* we want to exit gracefully when a pipe is broken */ ulog_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NFLOG); if (ulog_fd == -1) { Log(LOG_ERR, "ERROR ( default/core ): Failed to create Netlink ULOG socket\n"); exit_all(1); } Log(LOG_INFO, "INFO ( default/core ): Successfully connected Netlink ULOG socket\n"); /* Turn off netlink errors from overrun. */ if (setsockopt(ulog_fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &one, sizeof(one))) Log(LOG_ERR, "ERROR ( default/core ): Failed to turn off netlink ENOBUFS\n"); if (config.uacctd_nl_size > ULOG_BUFLEN) { /* If configured buffer size is larger than default 4KB */ if (setsockopt(ulog_fd, SOL_SOCKET, SO_RCVBUF, &config.uacctd_nl_size, sizeof(config.uacctd_nl_size))) Log(LOG_ERR, "ERROR ( default/core ): Failed to set Netlink receive buffer size\n"); else Log(LOG_INFO, "INFO ( default/core ): Netlink receive buffer size set to %u\n", config.uacctd_nl_size); } ulog_buffer = malloc(config.snaplen); if (ulog_buffer == NULL) { Log(LOG_ERR, "ERROR ( default/core ): ULOG buffer malloc() failed\n"); close(ulog_fd); exit_all(1); } memset(&nls, 0, sizeof(nls)); nls.nl_family = AF_NETLINK; nls.nl_pid = getpid(); nls.nl_groups = config.uacctd_group; alen = sizeof(nls); if (bind(ulog_fd, (struct sockaddr *) &nls, sizeof(nls))) { Log(LOG_ERR, "ERROR ( default/core ): bind() to Netlink ULOG socket failed\n"); close(ulog_fd); exit_all(1); } Log(LOG_INFO, "INFO ( default/core ): Netlink ULOG: binding to group %x\n", config.uacctd_group); /* loading pre-tagging map, if any */ if (config.pre_tag_map) { load_id_file(config.acct_type, config.pre_tag_map, &idt, &req, &tag_map_allocated); cb_data.idt = (u_char *) &idt; } else { memset(&idt, 0, sizeof(idt)); cb_data.idt = NULL; } #if defined ENABLE_THREADS /* starting the ISIS threa */ if (config.nfacctd_isis) { req.bpf_filter = TRUE; nfacctd_isis_wrapper(); /* Let's give the ISIS thread some advantage to create its structures */ sleep(5); } /* starting the BGP thread */ if (config.nfacctd_bgp) { req.bpf_filter = TRUE; load_comm_patterns(&config.nfacctd_bgp_stdcomm_pattern, &config.nfacctd_bgp_extcomm_pattern, &config.nfacctd_bgp_stdcomm_pattern_to_asn); if (config.nfacctd_bgp_peer_as_src_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_peer_as_src_map) { load_id_file(MAP_BGP_PEER_AS_SRC, config.nfacctd_bgp_peer_as_src_map, &bpas_table, &req, &bpas_map_allocated); cb_data.bpas_table = (u_char *) &bpas_table; } else { Log(LOG_ERR, "ERROR: bgp_peer_as_src_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.bpas_table = NULL; if (config.nfacctd_bgp_src_local_pref_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_local_pref_map) { load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.nfacctd_bgp_src_local_pref_map, &blp_table, &req, &blp_map_allocated); cb_data.blp_table = (u_char *) &blp_table; } else { Log(LOG_ERR, "ERROR: bgp_src_local_pref_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.bpas_table = NULL; if (config.nfacctd_bgp_src_med_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_med_map) { load_id_file(MAP_BGP_SRC_MED, config.nfacctd_bgp_src_med_map, &bmed_table, &req, &bmed_map_allocated); cb_data.bmed_table = (u_char *) &bmed_table; } else { Log(LOG_ERR, "ERROR: bgp_src_med_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.bmed_table = NULL; if (config.nfacctd_bgp_to_agent_map) { load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.nfacctd_bgp_to_agent_map, &bta_table, &req, &bta_map_allocated); cb_data.bta_table = (u_char *) &bta_table; } else { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_daemon' configured but no 'bgp_agent_map' has been specified. Exiting.\n"); exit(1); } /* Limiting BGP peers to only two: one would suffice in pmacctd but in case maps are reloadable (ie. bta), it could be handy to keep a backup feed in memory */ config.nfacctd_bgp_max_peers = 2; if (config.nfacctd_bgp_iface_to_rd_map) { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_iface_to_rd_map' is not supported by this daemon. Exiting.\n"); exit(1); } cb_data.f_agent = (char *)&client; nfacctd_bgp_wrapper(); /* Let's give the BGP thread some advantage to create its structures */ sleep(5); } #else if (config.nfacctd_isis) { Log(LOG_ERR, "ERROR ( default/core ): 'isis_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } if (config.nfacctd_bgp) { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } #endif /* plugins glue: creation (until 093) */ evaluate_packet_handlers(); pm_setproctitle("%s [%s]", "Core Process", "default"); if (config.pidfile) write_pid_file(config.pidfile); /* signals to be handled only by pmacctd; we set proper handlers after plugin creation */ signal(SIGINT, my_sigint_handler); signal(SIGTERM, my_sigint_handler); signal(SIGCHLD, handle_falling_child); kill(getpid(), SIGCHLD); /* Main loop: if pcap_loop() exits maybe an error occurred; we will try closing and reopening again our listening device */ for (;;) { if (len == -1) { if (errno != EAGAIN) { /* We can't deal with permanent errors. * Just sleep a bit. */ Log(LOG_ERR, "ERROR ( default/core ): Syscall returned %d: %s. Sleeping for 1 sec.\n", errno, strerror(errno)); sleep(1); } } len = recvfrom(ulog_fd, ulog_buffer, config.snaplen, 0, (struct sockaddr*) &nls, &alen); /* * Read timeout or failure condition. */ if (len < (int)sizeof(struct nlmsghdr)) continue; if (alen != sizeof(nls)) continue; nlh = (struct nlmsghdr*) ulog_buffer; if ((nlh->nlmsg_flags & MSG_TRUNC) || ((size_t)len > config.snaplen)) continue; gettimeofday(&tv, NULL); while (NLMSG_OK(nlh, (size_t)len)) { ulog_pkt = NLMSG_DATA(nlh); hdr.ts = tv; hdr.caplen = MIN(ulog_pkt->data_len, config.snaplen); hdr.len = ulog_pkt->data_len; if (strlen(ulog_pkt->indev_name) > 1) { cb_data.ifindex_in = get_ifindex(ulog_pkt->indev_name); } else cb_data.ifindex_in = 0; if (strlen(ulog_pkt->outdev_name) > 1) { cb_data.ifindex_out = get_ifindex(ulog_pkt->outdev_name); } else cb_data.ifindex_out = 0; #if defined (HAVE_L2) if (ulog_pkt->mac_len) { memcpy(jumbo_container, ulog_pkt->mac, ulog_pkt->mac_len); memcpy(jumbo_container+ulog_pkt->mac_len, ulog_pkt->payload, hdr.caplen); // XXX hdr.caplen += ulog_pkt->mac_len; hdr.len += ulog_pkt->mac_len; } else { memset(jumbo_container, 0, ETHER_HDRLEN); memcpy(jumbo_container+ETHER_HDRLEN, ulog_pkt->payload, hdr.caplen); hdr.caplen += ETHER_HDRLEN; hdr.len += ETHER_HDRLEN; switch (IP_V((struct my_iphdr *) ulog_pkt->payload)) { case 4: ((struct eth_header *)jumbo_container)->ether_type = ntohs(ETHERTYPE_IP); break; case 6: ((struct eth_header *)jumbo_container)->ether_type = ntohs(ETHERTYPE_IPV6); break; } } pcap_cb((u_char *) &cb_data, &hdr, jumbo_container); #else pcap_cb((u_char *) &cb_data, &hdr, ulog_pkt->payload); #endif if (nlh->nlmsg_type == NLMSG_DONE || !(nlh->nlmsg_flags & NLM_F_MULTI)) { /* Last part of the multilink message */ break; } nlh = NLMSG_NEXT(nlh, len); } } } unsigned int get_ifindex(char *device) { static int sock = -1; if (sock < 0) { sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock < 0) { Log(LOG_ERR, "ERROR: Unable to open socket for ifindex"); return -1; } } struct ifreq req; strcpy(req.ifr_name, device); if (ioctl(sock, SIOCGIFINDEX, &req)) { Log(LOG_ERR, "ERROR: Interface %s not found\n", device); return -1; } return req.ifr_ifindex; } unsigned int hash_ifname(char *name) { unsigned hash = 0; while (*name) hash = 33 * hash + *name++; return (hash & IFCACHE_HASHSIZ-1); } /* Cache name to ifindex mapping */ unsigned int cache_ifindex(char *device, unsigned long now) { struct ifname_cache *ifc, **top; unsigned int ifindex; top = &hash_heads[hash_ifname(device)]; while ( (ifc = *top) != NULL) { if (strncmp(device, ifc->name, IFNAMSIZ)) { top = &ifc->next; continue; } /* prune old entry to deal with hotplug */ if ((long)(now - ifc->tstamp) > IFCACHE_LIFETIME) { *top = ifc->next; free(ifc); break; } return ifc->index; } ifindex = get_ifindex(device); if (ifindex) { ifc = malloc(sizeof(struct ifname_cache)); if (ifc) { ifc->index = ifindex; strncpy(ifc->name, device, IFNAMSIZ); ifc->tstamp = now; ifc->next = *top; *top = ifc; } } return ifindex; } #else int main(int argc,char **argv, char **envp) { printf("WARN: uacctd (Linux NetFilter ULOG accounting) daemon is not active. This is enabled by --enable-ulog\n"); } #endif /* Dummy objects here - ugly to see but well portable */ void NF_find_id(struct id_table *t, struct packet_ptrs *pptrs, pm_id_t *tag, pm_id_t *tag2) { } void SF_find_id(struct id_table *t, struct packet_ptrs *pptrs, pm_id_t *tag, pm_id_t *tag2) { } pmacct-0.14.0/src/ports_aggr.c0000644000175000017500000000426111037474163015164 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente */ /* 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. */ #define __PORTS_AGGR_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "ports_aggr.h" void load_ports(char *filename, struct ports_table *pt) { FILE *file; char buf[8]; int ret, rows = 0, newline = TRUE; struct stat st; memset(&st, 0, sizeof(st)); if (filename) { if ((file = fopen(filename,"r")) == NULL) { Log(LOG_ERR, "ERROR: Ports File '%s' not found\n", filename); goto handle_error; } else { memset(pt, 0, sizeof(struct ports_table)); while (!feof(file)) { if (fgets(buf, 8, file)) { if (strchr(buf, '\n')) { if (!newline) { newline = TRUE; continue; } } else { if (!newline) continue; newline = FALSE; } trim_spaces(buf); if (!strlen(buf) || (buf[0] == '!')) continue; ret = atoi(buf); if ((ret > 0) && (ret < PORTS_TABLE_ENTRIES)) pt->table[ret] = TRUE; else Log(LOG_WARNING, "WARN ( %s ): invalid port %d\n", filename, rows); } } fclose(file); stat(filename, &st); pt->timestamp = st.st_mtime; } } return; handle_error: if (pt->timestamp) { Log(LOG_WARNING, "WARN: Rolling back the old Ports Table.\n"); /* we update the timestamp to avoid loops */ stat(filename, &st); pt->timestamp = st.st_mtime; } else exit_plugin(1); } pmacct-0.14.0/src/pmacct-data.h0000644000175000017500000003352511734666076015217 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* defines */ #define PLUGIN_ID_CORE 0 #define PLUGIN_ID_MEMORY 1 #define PLUGIN_ID_PRINT 2 #define PLUGIN_ID_NFPROBE 3 #define PLUGIN_ID_SFPROBE 4 #define PLUGIN_ID_MYSQL 5 #define PLUGIN_ID_PGSQL 6 #define PLUGIN_ID_SQLITE3 7 #define PLUGIN_ID_TEE 8 #define PLUGIN_ID_UNKNOWN -1 /* vars */ int protocols_number; /* structures */ static const struct _protocols_struct _protocols[] = { {"ip", 0}, {"icmp", 1}, {"igmp", 2}, {"ggp", 3}, {"4", 4}, {"5", 5}, {"tcp", 6}, {"7", 7}, {"egp", 8}, {"igp", 9}, {"10", 10}, {"11", 11}, {"12", 12}, {"13", 13}, {"14", 14}, {"15", 15}, {"16", 16}, {"udp", 17}, {"mux", 18}, {"19", 19}, {"20", 20}, {"21", 21}, {"22", 22}, {"23", 23}, {"24", 24}, {"25", 25}, {"26", 26}, {"27", 27}, {"28", 28}, {"29", 29}, {"30", 30}, {"31", 31}, {"32", 32}, {"33", 33}, {"34", 34}, {"35", 35}, {"36", 36}, {"37", 37}, {"38", 38}, {"39", 39}, {"40", 40}, {"ipv6", 41}, {"42", 42}, {"ipv6-route", 43}, {"ipv6-frag", 44}, {"45", 45}, {"rsvp", 46}, {"gre", 47}, {"48", 48}, {"49", 49}, {"ipv6-crypt", 50}, {"ipv6-auth", 51}, {"52", 52}, {"53", 53}, {"54", 54}, {"mobile", 55}, {"tlsp", 56}, {"57", 57}, {"ipv6-icmp", 58}, {"ipv6-nonxt", 59}, {"ipv6-opts", 60}, {"61", 61}, {"62", 62}, {"63", 63}, {"64", 64}, {"65", 65}, {"66", 66}, {"67", 67}, {"68", 68}, {"69", 69}, {"70", 70}, {"71", 71}, {"72", 72}, {"73", 73}, {"74", 74}, {"75", 75}, {"76", 76}, {"77", 77}, {"78", 78}, {"79", 79}, {"iso-ip", 80}, {"81", 81}, {"82", 82}, {"vines", 83}, {"84", 84}, {"85", 85}, {"86", 86}, {"87", 87}, {"eigrp", 88}, {"ospf", 89}, {"90", 90}, {"larp", 91}, {"92", 92}, {"ax.25", 93}, {"ipip", 94}, {"95", 95}, {"96", 96}, {"97", 97}, {"encap", 98}, {"99", 99}, {"100", 100}, {"101", 101}, {"pnni", 102}, {"pim", 103}, {"104", 104}, {"105", 105}, {"106", 106}, {"107", 107}, {"IPcomp", 108}, {"109", 109}, {"110", 110}, {"ipx-in-ip", 111}, {"vrrp", 112}, {"113", 113}, {"114", 114}, {"l2tp", 115}, {"116", 116}, {"117", 117}, {"118", 118}, {"119", 119}, {"120", 120}, {"121", 121}, {"122", 122}, {"123", 123}, {"isis", 124}, {"125", 125}, {"126", 126}, {"127", 127}, {"128", 128}, {"129", 129}, {"130", 130}, {"131", 131}, {"sctp", 132}, {"fc", 133}, {"", -1}, }; #if defined __PMACCTD_C || defined __UACCTD_C static struct _devices_struct _devices[] = { #if defined DLT_LOOP {null_handler, DLT_LOOP}, #endif {null_handler, DLT_NULL}, {eth_handler, DLT_EN10MB}, {ppp_handler, DLT_PPP}, {fddi_handler, DLT_FDDI}, {tr_handler, DLT_IEEE802}, #if defined DLT_IEEE802_11 {ieee_802_11_handler, DLT_IEEE802_11}, #endif #if defined DLT_LINUX_SLL {sll_handler, DLT_LINUX_SLL}, #endif #if defined DLT_RAW {raw_handler, DLT_RAW}, #endif #if defined DLT_C_HDLC {chdlc_handler, DLT_C_HDLC}, #endif #ifdef DLT_HDLC {chdlc_handler, DLT_HDLC}, #endif {NULL, -1}, }; #endif #ifdef __CFG_C static const struct _dictionary_line dictionary[] = { {"debug", cfg_key_debug}, {"syslog", cfg_key_syslog}, {"logfile", cfg_key_logfile}, {"pidfile", cfg_key_pidfile}, {"daemonize", cfg_key_daemonize}, {"aggregate", cfg_key_aggregate}, {"snaplen", cfg_key_snaplen}, {"aggregate_filter", cfg_key_aggregate_filter}, {"promisc", cfg_key_promisc}, {"pcap_filter", cfg_key_pcap_filter}, {"pmacctd_as", cfg_key_nfacctd_as_new}, {"uacctd_as", cfg_key_nfacctd_as_new}, {"pmacctd_net", cfg_key_nfacctd_net}, {"uacctd_net", cfg_key_nfacctd_net}, {"plugins", NULL}, {"plugin_pipe_size", cfg_key_plugin_pipe_size}, {"plugin_pipe_backlog", cfg_key_plugin_pipe_backlog}, {"plugin_buffer_size", cfg_key_plugin_buffer_size}, {"interface", cfg_key_interface}, {"interface_wait", cfg_key_interface_wait}, {"files_umask", cfg_key_files_umask}, {"files_uid", cfg_key_files_uid}, {"files_gid", cfg_key_files_gid}, {"savefile_wait", cfg_key_savefile_wait}, {"networks_mask", cfg_key_networks_mask}, {"networks_file", cfg_key_networks_file}, {"networks_cache_entries", cfg_key_networks_cache_entries}, {"refresh_maps", cfg_key_refresh_maps}, {"ports_file", cfg_key_ports_file}, {"imt_path", cfg_key_imt_path}, {"imt_passwd", cfg_key_imt_passwd}, {"imt_buckets", cfg_key_imt_buckets}, {"imt_mem_pools_number", cfg_key_imt_mem_pools_number}, {"imt_mem_pools_size", cfg_key_imt_mem_pools_size}, {"sql_db", cfg_key_sql_db}, {"sql_table", cfg_key_sql_table}, {"sql_table_schema", cfg_key_sql_table_schema}, {"sql_table_version", cfg_key_sql_table_version}, {"sql_table_type", cfg_key_sql_table_type}, {"sql_host", cfg_key_sql_host}, {"sql_data", cfg_key_sql_data}, {"sql_backup_host", cfg_key_sql_recovery_backup_host}, /* obsolete */ {"sql_user", cfg_key_sql_user}, {"sql_passwd", cfg_key_sql_passwd}, {"sql_refresh_time", cfg_key_sql_refresh_time}, {"sql_startup_delay", cfg_key_sql_startup_delay}, {"sql_optimize_clauses", cfg_key_sql_optimize_clauses}, {"sql_history", cfg_key_sql_history}, {"sql_history_roundoff", cfg_key_sql_history_roundoff}, {"sql_history_since_epoch", cfg_key_sql_history_since_epoch}, {"sql_recovery_backup_host", cfg_key_sql_recovery_backup_host}, {"sql_recovery_logfile", cfg_key_sql_recovery_logfile}, {"sql_delimiter", cfg_key_sql_delimiter}, {"sql_max_writers", cfg_key_sql_max_writers}, {"sql_trigger_exec", cfg_key_sql_trigger_exec}, {"sql_trigger_time", cfg_key_sql_trigger_time}, {"sql_cache_entries", cfg_key_sql_cache_entries}, {"sql_dont_try_update", cfg_key_sql_dont_try_update}, {"sql_preprocess", cfg_key_sql_preprocess}, {"sql_preprocess_type", cfg_key_sql_preprocess_type}, {"sql_multi_values", cfg_key_sql_multi_values}, {"sql_aggressive_classification", cfg_key_sql_aggressive_classification}, {"sql_locking_style", cfg_key_sql_locking_style}, {"sql_use_copy", cfg_key_sql_use_copy}, {"sql_num_protos", cfg_key_num_protos}, {"sql_num_hosts", cfg_key_num_hosts}, {"print_refresh_time", cfg_key_print_refresh_time}, {"print_cache_entries", cfg_key_print_cache_entries}, {"print_markers", cfg_key_print_markers}, {"print_output", cfg_key_print_output}, {"print_num_protos", cfg_key_num_protos}, {"print_time_roundoff", cfg_key_sql_history_roundoff}, {"print_output_file", cfg_key_sql_table}, {"print_trigger_exec", cfg_key_sql_trigger_exec}, {"nfacctd_port", cfg_key_nfacctd_port}, {"nfacctd_ip", cfg_key_nfacctd_ip}, {"nfacctd_allow_file", cfg_key_nfacctd_allow_file}, {"nfacctd_time_secs", cfg_key_nfacctd_time_secs}, {"nfacctd_time_new", cfg_key_nfacctd_time_new}, {"nfacctd_as_new", cfg_key_nfacctd_as_new}, {"nfacctd_net", cfg_key_nfacctd_net}, {"nfacctd_mcast_groups", cfg_key_nfacctd_mcast_groups}, {"nfacctd_sql_log", cfg_key_nfacctd_sql_log}, {"pmacctd_force_frag_handling", cfg_key_pmacctd_force_frag_handling}, {"pmacctd_frag_buffer_size", cfg_key_pmacctd_frag_buffer_size}, {"pmacctd_flow_buffer_size", cfg_key_pmacctd_flow_buffer_size}, {"pmacctd_flow_buffer_buckets", cfg_key_pmacctd_flow_buffer_buckets}, {"pmacctd_conntrack_buffer_size", cfg_key_pmacctd_conntrack_buffer_size}, {"pmacctd_flow_lifetime", cfg_key_pmacctd_flow_lifetime}, {"pmacctd_ext_sampling_rate", cfg_key_pmacctd_ext_sampling_rate}, {"uacctd_force_frag_handling", cfg_key_pmacctd_force_frag_handling}, {"uacctd_frag_buffer_size", cfg_key_pmacctd_frag_buffer_size}, {"uacctd_flow_buffer_size", cfg_key_pmacctd_flow_buffer_size}, {"uacctd_flow_buffer_buckets", cfg_key_pmacctd_flow_buffer_buckets}, {"uacctd_conntrack_buffer_size", cfg_key_pmacctd_conntrack_buffer_size}, {"uacctd_flow_lifetime", cfg_key_pmacctd_flow_lifetime}, {"uacctd_ext_sampling_rate", cfg_key_pmacctd_ext_sampling_rate}, {"nfacctd_ext_sampling_rate", cfg_key_pmacctd_ext_sampling_rate}, {"sfacctd_ext_sampling_rate", cfg_key_pmacctd_ext_sampling_rate}, {"pcap_savefile", cfg_key_pcap_savefile}, {"pre_tag_map", cfg_key_pre_tag_map}, {"pre_tag_map_entries", cfg_key_pre_tag_map_entries}, {"pre_tag_filter", cfg_key_pre_tag_filter}, {"pre_tag2_filter", cfg_key_pre_tag2_filter}, {"post_tag", cfg_key_post_tag}, {"sampling_rate", cfg_key_sampling_rate}, {"sampling_map", cfg_key_sampling_map}, {"sfacctd_port", cfg_key_nfacctd_port}, {"sfacctd_ip", cfg_key_nfacctd_ip}, {"sfacctd_allow_file", cfg_key_nfacctd_allow_file}, {"sfacctd_as_new", cfg_key_nfacctd_as_new}, {"sfacctd_net", cfg_key_nfacctd_net}, {"uacctd_renormalize", cfg_key_sfacctd_renormalize}, {"pmacctd_renormalize", cfg_key_sfacctd_renormalize}, {"sfacctd_renormalize", cfg_key_sfacctd_renormalize}, {"nfacctd_renormalize", cfg_key_sfacctd_renormalize}, {"nfacctd_disable_checks", cfg_key_nfacctd_disable_checks}, {"sfacctd_disable_checks", cfg_key_nfacctd_disable_checks}, {"sfacctd_mcast_groups", cfg_key_nfacctd_mcast_groups}, {"classifiers", cfg_key_classifiers}, {"classifier_tentatives", cfg_key_classifier_tentatives}, {"classifier_table_num", cfg_key_classifier_table_num}, {"nfprobe_timeouts", cfg_key_nfprobe_timeouts}, {"nfprobe_hoplimit", cfg_key_nfprobe_hoplimit}, {"nfprobe_maxflows", cfg_key_nfprobe_maxflows}, {"nfprobe_receiver", cfg_key_nfprobe_receiver}, {"nfprobe_engine", cfg_key_nfprobe_engine}, {"nfprobe_version", cfg_key_nfprobe_version}, {"nfprobe_peer_as", cfg_key_nfprobe_peer_as}, {"nfprobe_source_ip", cfg_key_nfprobe_source_ip}, {"nfprobe_ipprec", cfg_key_nfprobe_ip_precedence}, {"nfprobe_direction", cfg_key_nfprobe_direction}, {"nfprobe_ifindex", cfg_key_nfprobe_ifindex}, {"sfprobe_receiver", cfg_key_sfprobe_receiver}, {"sfprobe_agentip", cfg_key_sfprobe_agentip}, {"sfprobe_agentsubid", cfg_key_sfprobe_agentsubid}, {"sfprobe_peer_as", cfg_key_nfprobe_peer_as}, {"sfprobe_ipprec", cfg_key_nfprobe_ip_precedence}, {"sfprobe_direction", cfg_key_nfprobe_direction}, {"sfprobe_ifindex", cfg_key_nfprobe_ifindex}, {"sfprobe_ifspeed", cfg_key_sfprobe_ifspeed}, {"tee_receiver", cfg_key_nfprobe_receiver}, {"tee_source_ip", cfg_key_nfprobe_source_ip}, {"tee_transparent", cfg_key_tee_transparent}, {"bgp_daemon", cfg_key_nfacctd_bgp}, {"bgp_daemon_ip", cfg_key_nfacctd_bgp_ip}, {"bgp_daemon_port", cfg_key_nfacctd_bgp_port}, {"bgp_daemon_max_peers", cfg_key_nfacctd_bgp_max_peers}, {"bgp_daemon_msglog", cfg_key_nfacctd_bgp_msglog}, {"bgp_daemon_allow_file", cfg_key_nfacctd_bgp_allow_file}, {"bgp_daemon_ipprec", cfg_key_nfacctd_bgp_ip_precedence}, {"bgp_daemon_md5_file", cfg_key_nfacctd_bgp_md5_file}, {"bgp_aspath_radius", cfg_key_nfacctd_bgp_aspath_radius}, {"bgp_stdcomm_pattern", cfg_key_nfacctd_bgp_stdcomm_pattern}, {"bgp_extcomm_pattern", cfg_key_nfacctd_bgp_extcomm_pattern}, {"bgp_stdcomm_pattern_to_asn", cfg_key_nfacctd_bgp_stdcomm_pattern_to_asn}, {"bgp_peer_as_skip_subas", cfg_key_nfacctd_bgp_peer_as_skip_subas}, {"bgp_peer_src_as_map", cfg_key_nfacctd_bgp_peer_src_as_map}, {"bgp_src_local_pref_map", cfg_key_nfacctd_bgp_src_local_pref_map}, {"bgp_src_med_map", cfg_key_nfacctd_bgp_src_med_map}, {"bgp_peer_src_as_type", cfg_key_nfacctd_bgp_peer_src_as_type}, {"bgp_src_std_comm_type", cfg_key_nfacctd_bgp_src_std_comm_type}, {"bgp_src_ext_comm_type", cfg_key_nfacctd_bgp_src_ext_comm_type}, {"bgp_src_as_path_type", cfg_key_nfacctd_bgp_src_as_path_type}, {"bgp_src_local_pref_type", cfg_key_nfacctd_bgp_src_local_pref_type}, {"bgp_src_med_type", cfg_key_nfacctd_bgp_src_med_type}, {"bgp_agent_map", cfg_key_nfacctd_bgp_to_agent_map}, {"bgp_iface_rd_map", cfg_key_nfacctd_bgp_iface_to_rd_map}, {"bgp_follow_default", cfg_key_nfacctd_bgp_follow_default}, {"bgp_follow_nexthop", cfg_key_nfacctd_bgp_follow_nexthop}, {"bgp_neighbors_file", cfg_key_nfacctd_bgp_neighbors_file}, {"bgp_table_peer_buckets", cfg_key_nfacctd_bgp_table_peer_buckets}, {"isis_daemon", cfg_key_nfacctd_isis}, {"isis_daemon_ip", cfg_key_nfacctd_isis_ip}, {"isis_daemon_net", cfg_key_nfacctd_isis_net}, {"isis_daemon_iface", cfg_key_nfacctd_isis_iface}, {"isis_daemon_mtu", cfg_key_nfacctd_isis_mtu}, {"isis_daemon_msglog", cfg_key_nfacctd_isis_msglog}, {"uacctd_group", cfg_key_uacctd_group}, {"uacctd_nl_size", cfg_key_uacctd_nl_size}, {"tunnel_0", cfg_key_tunnel_0}, {"xlate_src", cfg_key_xlate_src}, {"xlate_dst", cfg_key_xlate_dst}, {"", NULL}, }; static struct plugin_type_entry plugin_types_list[] = { {PLUGIN_ID_CORE, "core", NULL}, {PLUGIN_ID_MEMORY, "memory", imt_plugin}, {PLUGIN_ID_PRINT, "print", print_plugin}, {PLUGIN_ID_NFPROBE, "nfprobe", nfprobe_plugin}, {PLUGIN_ID_SFPROBE, "sfprobe", sfprobe_plugin}, #ifdef WITH_MYSQL {PLUGIN_ID_MYSQL, "mysql", mysql_plugin}, #endif #ifdef WITH_PGSQL {PLUGIN_ID_PGSQL, "pgsql", pgsql_plugin}, #endif #ifdef WITH_SQLITE3 {PLUGIN_ID_SQLITE3, "sqlite3", sqlite3_plugin}, #endif {PLUGIN_ID_TEE, "tee", tee_plugin}, {PLUGIN_ID_UNKNOWN, "", NULL}, }; #endif #ifdef __NL_C static struct tunnel_entry tunnel_handlers_list[] = { {"gtp", gtp_tunnel_func, gtp_tunnel_configurator}, {"", NULL, NULL}, }; #endif pmacct-0.14.0/src/sfprobe_plugin/0000755000175000017500000000000011741267746015675 5ustar paolopaolopmacct-0.14.0/src/sfprobe_plugin/sflow_sampler.c0000644000175000017500000001616711423143744020715 0ustar paolopaolo/* Copyright (c) 2002-2006 InMon Corp. Licensed under the terms of the InMon sFlow licence: */ /* http://www.inmon.com/technology/sflowlicense.txt */ #include #include #include #include #include #include #include "sflow_api.h" /*_________________--------------------------__________________ _________________ sfl_sampler_init __________________ -----------------__________________________------------------ */ void sfl_sampler_init(SFLSampler *sampler, SFLAgent *agent, SFLDataSource_instance *pdsi) { /* copy the dsi in case it points to sampler->dsi, which we are about to clear. (Thanks to Jagjit Choudray of Force 10 Networks for pointing out this bug) */ SFLDataSource_instance dsi = *pdsi; /* clear everything */ memset(sampler, 0, sizeof(*sampler)); /* now copy in the parameters */ sampler->agent = agent; sampler->dsi = dsi; /* set defaults */ sampler->sFlowFsMaximumHeaderSize = SFL_DEFAULT_HEADER_SIZE; sampler->sFlowFsPacketSamplingRate = SFL_DEFAULT_SAMPLING_RATE; } /*_________________--------------------------__________________ _________________ reset __________________ -----------------__________________________------------------ */ static void reset(SFLSampler *sampler) { SFLDataSource_instance dsi = sampler->dsi; sfl_sampler_init(sampler, sampler->agent, &dsi); } /*_________________---------------------------__________________ _________________ MIB access __________________ -----------------___________________________------------------ */ u_int32_t sfl_sampler_get_sFlowFsReceiver(SFLSampler *sampler) { return sampler->sFlowFsReceiver; } void sfl_sampler_set_sFlowFsReceiver(SFLSampler *sampler, u_int32_t sFlowFsReceiver) { sampler->sFlowFsReceiver = sFlowFsReceiver; if(sFlowFsReceiver == 0) reset(sampler); else { /* retrieve and cache a direct pointer to my receiver */ sampler->myReceiver = sfl_agent_getReceiver(sampler->agent, sampler->sFlowFsReceiver); } } u_int32_t sfl_sampler_get_sFlowFsPacketSamplingRate(SFLSampler *sampler) { return sampler->sFlowFsPacketSamplingRate; } void sfl_sampler_set_sFlowFsPacketSamplingRate(SFLSampler *sampler, u_int32_t sFlowFsPacketSamplingRate) { sampler->sFlowFsPacketSamplingRate = sFlowFsPacketSamplingRate; } u_int32_t sfl_sampler_get_sFlowFsMaximumHeaderSize(SFLSampler *sampler) { return sampler->sFlowFsMaximumHeaderSize; } void sfl_sampler_set_sFlowFsMaximumHeaderSize(SFLSampler *sampler, u_int32_t sFlowFsMaximumHeaderSize) { sampler->sFlowFsMaximumHeaderSize = sFlowFsMaximumHeaderSize; } /* call this to set a maximum samples-per-second threshold. If the sampler reaches this threshold it will automatically back off the sampling rate. A value of 0 disables the mechanism */ void sfl_sampler_set_backoffThreshold(SFLSampler *sampler, u_int32_t samplesPerSecond) { sampler->backoffThreshold = samplesPerSecond; } u_int32_t sfl_sampler_get_backoffThreshold(SFLSampler *sampler) { return sampler->backoffThreshold; } u_int32_t sfl_sampler_get_samplesLastTick(SFLSampler *sampler) { return sampler->samplesLastTick; } /*_________________---------------------------------__________________ _________________ sequence number reset __________________ -----------------_________________________________------------------ Used by the agent to indicate a samplePool discontinuity so that the sflow collector will know to ignore the next delta. */ void sfl_sampler_resetFlowSeqNo(SFLSampler *sampler) { sampler->flowSampleSeqNo = 0; } /*_________________---------------------------__________________ _________________ sfl_sampler_tick __________________ -----------------___________________________------------------ */ void sfl_sampler_tick(SFLSampler *sampler, time_t now) { if(sampler->backoffThreshold && sampler->samplesThisTick > sampler->backoffThreshold) { // automatic backoff. If using hardware sampling then this is where you have to // call out to change the sampling rate and make sure that any other registers/variables // that hold this value are updated. sampler->sFlowFsPacketSamplingRate *= 2; } sampler->samplesLastTick = sampler->samplesThisTick; sampler->samplesThisTick = 0; } /*_________________------------------------------__________________ _________________ sfl_sampler_writeFlowSample __________________ -----------------______________________________------------------ */ void sfl_sampler_writeFlowSample(SFLSampler *sampler, SFL_FLOW_SAMPLE_TYPE *fs) { if(fs == NULL) return; sampler->samplesThisTick++; /* increment the sequence number */ fs->sequence_number = ++sampler->flowSampleSeqNo; /* copy the other header fields in */ #ifdef SFL_USE_32BIT_INDEX fs->ds_class = SFL_DS_CLASS(sampler->dsi); fs->ds_index = SFL_DS_INDEX(sampler->dsi); #else fs->source_id = SFL_DS_DATASOURCE(sampler->dsi); #endif /* the sampling rate may have been set already. */ if(fs->sampling_rate == 0) fs->sampling_rate = sampler->sFlowFsPacketSamplingRate; /* the samplePool may be maintained upstream too. */ if( fs->sample_pool == 0) fs->sample_pool = sampler->samplePool; /* sent to my receiver */ if(sampler->myReceiver) sfl_receiver_writeFlowSample(sampler->myReceiver, fs); } /* ================== software sampling ========================*/ /*_________________---------------------------__________________ _________________ nextRandomSkip __________________ -----------------___________________________------------------ */ inline static u_int32_t nextRandomSkip(u_int32_t mean) { if(mean == 0 || mean == 1) return 1; return ((random() % ((2 * mean) - 1)) + 1); } /*_________________---------------------------__________________ _________________ sfl_sampler_takeSample __________________ -----------------___________________________------------------ */ int sfl_sampler_takeSample(SFLSampler *sampler) { if (sampler->skip == 0) { /* first time - seed the random number generator */ srandom(SFL_DS_INDEX(sampler->dsi)); sampler->skip = nextRandomSkip(sampler->sFlowFsPacketSamplingRate); } // increment the samplePool sampler->samplePool++; if (--sampler->skip == 0) { /* reached zero. Set the next skip and return true. */ sampler->skip = nextRandomSkip(sampler->sFlowFsPacketSamplingRate); return 1; } return 0; } /* int sfl_sampler_takeSample(SFLSampler *sampler, u_int32_t pkts) { sampler->samplePool++; return pkts; } */ /* int sfl_sampler_takeSample(SFLSampler *sampler, u_int32_t pkts) { int delta, sampledPkts = 0; run_again: if (sampler->skip == 0) { // first time - seed the random number generator srandom(SFL_DS_INDEX(sampler->dsi)); sampler->skip = nextRandomSkip(sampler->sFlowFsPacketSamplingRate); } // increment the samplePool sampler->samplePool++; delta = MIN(sampler->skip, pkts); sampler->skip -= delta; pkts -= delta; if (sampler->skip == 0) { // reached zero. Set the next skip and return true. sampler->skip = nextRandomSkip(sampler->sFlowFsPacketSamplingRate); sampledPkts++; if (pkts > 0) goto run_again; } return sampledPkts; } */ pmacct-0.14.0/src/sfprobe_plugin/Makefile.in0000644000175000017500000000146210530072467017732 0ustar paolopaoloprefix=@prefix@ exec_prefix=@exec_prefix@ bindir=@bindir@ sbindir=@sbindir@ libexecdir=@libexecdir@ datadir=@datadir@ mandir=@mandir@ sysconfdir=@sysconfdir@ srcdir=@srcdir@ top_srcdir=@top_srcdir@ VPATH=@srcdir@ CC=@CC@ DEFS=@DEFS@ LDFLAGS=@LDFLAGS@ CFLAGS=$(DEFS) -I$(srcdir) -I.. @CFLAGS@ CPPFLAGS=@CPPFLAGS@ LIBS=@LIBS@ INSTALL=@INSTALL@ RANLIB=@RANLIB@ TARGETS=libsfprobe_plugin.a all: $(TARGETS) libsfprobe_plugin.a: sflow_agent.o sflow_poller.o sflow_receiver.o sflow_sampler.o sfprobe_plugin.o ar rc $@ sflow_agent.o sflow_poller.o sflow_receiver.o sflow_sampler.o sfprobe_plugin.o $(RANLIB) $@ clean: rm -f $(TARGETS) *.o core *.core realclean: clean rm -rf autom4te.cache Makefile config.log config.status distclean: realclean rm -f config.h* configure strip: strip $(TARGETS) install: all pmacct-0.14.0/src/sfprobe_plugin/sflow_poller.c0000644000175000017500000001077510530072467020547 0ustar paolopaolo/* Copyright (c) 2002-2006 InMon Corp. Licensed under the terms of the InMon sFlow licence: */ /* http://www.inmon.com/technology/sflowlicense.txt */ #include #include #include #include #include #include #include "sflow_api.h" /*_________________--------------------------__________________ _________________ sfl_poller_init __________________ -----------------__________________________------------------ */ void sfl_poller_init(SFLPoller *poller, SFLAgent *agent, SFLDataSource_instance *pdsi, void *magic, /* ptr to pass back in getCountersFn() */ getCountersFn_t getCountersFn) { /* copy the dsi in case it points to poller->dsi, which we are about to clear */ SFLDataSource_instance dsi = *pdsi; /* clear everything */ memset(poller, 0, sizeof(*poller)); /* now copy in the parameters */ poller->agent = agent; poller->dsi = dsi; /* structure copy */ poller->magic = magic; poller->getCountersFn = getCountersFn; } /*_________________--------------------------__________________ _________________ reset __________________ -----------------__________________________------------------ */ static void reset(SFLPoller *poller) { SFLDataSource_instance dsi = poller->dsi; sfl_poller_init(poller, poller->agent, &dsi, poller->magic, poller->getCountersFn); } /*_________________---------------------------__________________ _________________ MIB access __________________ -----------------___________________________------------------ */ u_int32_t sfl_poller_get_sFlowCpReceiver(SFLPoller *poller) { return poller->sFlowCpReceiver; } void sfl_poller_set_sFlowCpReceiver(SFLPoller *poller, u_int32_t sFlowCpReceiver) { poller->sFlowCpReceiver = sFlowCpReceiver; if(sFlowCpReceiver == 0) reset(poller); else { /* retrieve and cache a direct pointer to my receiver */ poller->myReceiver = sfl_agent_getReceiver(poller->agent, poller->sFlowCpReceiver); } } u_int32_t sfl_poller_get_sFlowCpInterval(SFLPoller *poller) { return poller->sFlowCpInterval; } void sfl_poller_set_sFlowCpInterval(SFLPoller *poller, u_int32_t sFlowCpInterval) { poller->sFlowCpInterval = sFlowCpInterval; /* Set the countersCountdown to be a randomly selected value between 1 and sFlowCpInterval. That way the counter polling would be desynchronised (on a 200-port switch, polling all the counters in one second could be harmful). */ poller->countersCountdown = 1 + (random() % sFlowCpInterval); } /*_________________---------------------------------__________________ _________________ sequence number reset __________________ -----------------_________________________________------------------ Used to indicate a counter discontinuity so that the sflow collector will know to ignore the next delta. */ void sfl_poller_resetCountersSeqNo(SFLPoller *poller) { poller->countersSampleSeqNo = 0; } /*_________________---------------------------__________________ _________________ sfl_poller_tick __________________ -----------------___________________________------------------ */ void sfl_poller_tick(SFLPoller *poller, time_t now) { if(poller->countersCountdown == 0) return; /* counters retrieval was not enabled */ if(poller->sFlowCpReceiver == 0) return; if(--poller->countersCountdown == 0) { if(poller->getCountersFn != NULL) { /* call out for counters */ SFL_COUNTERS_SAMPLE_TYPE cs; memset(&cs, 0, sizeof(cs)); poller->getCountersFn(poller->magic, poller, &cs); // this countersFn is expected to fill in some counter block elements // and then call sfl_poller_writeCountersSample(poller, &cs); } /* reset the countdown */ poller->countersCountdown = poller->sFlowCpInterval; } } /*_________________---------------------------------__________________ _________________ sfl_poller_writeCountersSample __________________ -----------------_________________________________------------------ */ void sfl_poller_writeCountersSample(SFLPoller *poller, SFL_COUNTERS_SAMPLE_TYPE *cs) { /* fill in the rest of the header fields, and send to the receiver */ cs->sequence_number = ++poller->countersSampleSeqNo; #ifdef SFL_USE_32BIT_INDEX cs->ds_class = SFL_DS_CLASS(poller->dsi); cs->ds_index = SFL_DS_INDEX(poller->dsi); #else cs->source_id = SFL_DS_DATASOURCE(poller->dsi); #endif /* sent to my receiver */ if(poller->myReceiver) sfl_receiver_writeCountersSample(poller->myReceiver, cs); } pmacct-0.14.0/src/sfprobe_plugin/sfprobe_plugin.c0000644000175000017500000006021511741023173021043 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* Originally based on sflowtool which is: Copyright (c) 2002-2006 InMon Corp. Licensed under the terms of the InMon sFlow licence: http://www.inmon.com/technology/sflowlicense.txt */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sflow_api.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "net_aggr.h" #include "ports_aggr.h" #define SFL_DIRECTION_IN 0 #define SFL_DIRECTION_OUT 1 #define SFL_MAX_INTERFACES 4096 typedef struct _SflSp_counters { u_int32_t ifIndex; u_int32_t frames[2]; u_int64_t bytes[2]; u_int32_t multicasts[2]; u_int32_t broadcasts[2]; } SflSp_counters; typedef struct _SflSp { int verbose; char *device; int ifIndex_Type; int ifType; u_int64_t ifSpeed; int ifDirection; int promiscuous; u_int32_t samplingRate; u_int32_t counterSamplingInterval; struct in_addr collectorIP; u_int32_t collectorPort; int snaplen; int timeout_ms; int batch; pcap_t *pcap; SflSp_counters counters[SFL_MAX_INTERFACES]; struct in_addr agentIP; u_int32_t agentSubId; struct in_addr interfaceIP; struct in6_addr interfaceIP6; char pad[2]; SFLAgent *agent; SFLSampler *sampler; } SflSp; void sfprobe_exit_now(int signum) { Log(LOG_WARNING, "WARN ( %s/%s ): Shutting down on user request.\n", config.name, config.type); exit_plugin(0); } /*_________________---------------------------__________________ _________________ Name_to_IP __________________ -----------------___________________________------------------ */ u_long Name_to_IP(char *domainName) { struct hostent *ent = gethostbyname(domainName); if(ent == NULL) return 0; else return ((struct in_addr *)(ent->h_addr))->s_addr; } /*_________________---------------------------__________________ _________________ getMyIPAddress __________________ -----------------___________________________------------------ */ u_long getMyIPAddress() { struct utsname uts; if (uname(&uts) == -1) return Name_to_IP("localhost"); else return Name_to_IP(uts.nodename); } /* setDefaults(): here we define some fixed infos to be placed in our sFlow datagrams. We should either gather real informations from the system or let the user fill these fields through some configuration directives. XXX */ static void setDefaults(SflSp *sp) { sp->device = NULL; sp->counters[0].ifIndex = 1; sp->ifIndex_Type = IFINDEX_STATIC; sp->ifType = 6; // ethernet_csmacd sp->ifSpeed = 100000000L; // assume 100 MBit sp->ifDirection = 1; // assume full duplex // if (config.acct_type != ACCT_SF) sp->samplingRate = SFL_DEFAULT_SAMPLING_RATE; sp->samplingRate = SFL_DEFAULT_SFACCTD_SAMPLING_RATE; sp->counterSamplingInterval = 20; sp->snaplen = 128; sp->collectorIP.s_addr = Name_to_IP("localhost"); sp->collectorPort = SFL_DEFAULT_COLLECTOR_PORT; sp->agentIP.s_addr = getMyIPAddress(); sp->agentSubId = 0; sp->agentIP.s_addr = Name_to_IP("localhost"); } /*_________________---------------------------__________________ _________________ agent callbacks __________________ -----------------___________________________------------------ */ static void *agentCB_alloc(void *magic, SFLAgent *agent, size_t bytes) { return calloc(1, bytes); } static int agentCB_free(void *magic, SFLAgent *agent, void *obj) { free(obj); return 0; } static void agentCB_error(void *magic, SFLAgent *agent, char *msg) { Log(LOG_ERR, "ERROR ( %s/%s ): sFlow agent error: %s\n", config.name, config.type, msg); } void agentCB_getCounters(void *magic, SFLPoller *poller, SFL_COUNTERS_SAMPLE_TYPE *cs) { SFLCounters_sample_element genElem[SFL_MAX_INTERFACES]; SflSp *sp = (SflSp *)magic; int idx = 0; memset(&genElem, 0, sizeof(genElem)); // build a counters sample for (idx = 0; idx < SFL_MAX_INTERFACES && sp->counters[idx].ifIndex; idx++) { if (sp->counters[idx].frames[SFL_DIRECTION_IN] || sp->counters[idx].frames[SFL_DIRECTION_OUT]) { genElem[idx].tag = SFLCOUNTERS_GENERIC; // don't need to set the length here (set by the encoder) genElem[idx].counterBlock.generic.ifIndex = sp->counters[idx].ifIndex; genElem[idx].counterBlock.generic.ifType = sp->ifType; genElem[idx].counterBlock.generic.ifSpeed = sp->ifSpeed; genElem[idx].counterBlock.generic.ifDirection = sp->ifDirection; genElem[idx].counterBlock.generic.ifStatus = 0x03; // adminStatus = up, operStatus = up genElem[idx].counterBlock.generic.ifPromiscuousMode = sp->promiscuous; // these counters would normally be a snapshot the hardware interface counters - the // same ones that the SNMP agent uses to answer SNMP requests to the ifTable. To ease // the portability of this program, however, I am just using some counters that were // added up in software: genElem[idx].counterBlock.generic.ifInOctets = sp->counters[idx].bytes[SFL_DIRECTION_IN]; genElem[idx].counterBlock.generic.ifInUcastPkts = sp->counters[idx].frames[SFL_DIRECTION_IN]; genElem[idx].counterBlock.generic.ifInMulticastPkts = sp->counters[idx].multicasts[SFL_DIRECTION_IN]; genElem[idx].counterBlock.generic.ifInBroadcastPkts = sp->counters[idx].broadcasts[SFL_DIRECTION_IN]; genElem[idx].counterBlock.generic.ifOutOctets = sp->counters[idx].bytes[SFL_DIRECTION_OUT]; genElem[idx].counterBlock.generic.ifOutUcastPkts = sp->counters[idx].frames[SFL_DIRECTION_OUT]; genElem[idx].counterBlock.generic.ifOutMulticastPkts = sp->counters[idx].multicasts[SFL_DIRECTION_OUT]; genElem[idx].counterBlock.generic.ifOutBroadcastPkts = sp->counters[idx].broadcasts[SFL_DIRECTION_OUT]; // add this counter block to the counter sample that we are building SFLADD_ELEMENT(cs, &genElem[idx]); } } // pass these counters down to be encoded and included with the next sFlow datagram sfl_poller_writeCountersSample(poller, cs); } /*_________________---------------------------__________________ _________________ init_agent __________________ -----------------___________________________------------------ */ static void init_agent(SflSp *sp) { SFLReceiver *receiver; SFLDataSource_instance dsi; Log(LOG_DEBUG, "DEBUG ( %s/%s ): Creating sFlow agent.\n", config.name, config.type); { // create an agent SFLAddress myIP; time_t now = time(NULL); myIP.type = SFLADDRESSTYPE_IP_V4; myIP.address.ip_v4 = sp->agentIP; sp->agent = (SFLAgent *)calloc(1, sizeof(SFLAgent)); sfl_agent_init(sp->agent, &myIP, sp->agentSubId, now, now, sp, agentCB_alloc, agentCB_free, agentCB_error, NULL); } // add a receiver receiver = sfl_agent_addReceiver(sp->agent); // define the data source SFL_DS_SET(dsi, 0, 1, 0); // ds_class = 0, ds_index = 1, ds_instance = 0 // create a sampler for it sfl_agent_addSampler(sp->agent, &dsi); // and a poller too sfl_agent_addPoller(sp->agent, &dsi, sp, agentCB_getCounters); // now configure it just as if it were as series of SNMP SET operations through the MIB interface... // claim the receiver slot sfl_receiver_set_sFlowRcvrOwner(sfl_agent_getReceiver(sp->agent, 1), "my owner string $$$"); // set the timeout to infinity sfl_receiver_set_sFlowRcvrTimeout(sfl_agent_getReceiver(sp->agent, 1), 0xFFFFFFFF); { // collector address SFLAddress addr; addr.type = SFLADDRESSTYPE_IP_V4; addr.address.ip_v4 = sp->collectorIP; sfl_receiver_set_sFlowRcvrAddress(sfl_agent_getReceiver(sp->agent, 1), &addr); } // collector port sfl_receiver_set_sFlowRcvrPort(sfl_agent_getReceiver(sp->agent, 1), sp->collectorPort); // set the sampling rate sfl_sampler_set_sFlowFsPacketSamplingRate(sfl_agent_getSampler(sp->agent, &dsi), sp->samplingRate); // set the counter interval sfl_poller_set_sFlowCpInterval(sfl_agent_getPoller(sp->agent, &dsi), sp->counterSamplingInterval); // point the sampler to the receiver sfl_sampler_set_sFlowFsReceiver(sfl_agent_getSampler(sp->agent, &dsi), 1); // point the poller to the receiver sfl_poller_set_sFlowCpReceiver(sfl_agent_getPoller(sp->agent, &dsi), 1); // cache the sampler pointer for performance reasons... sp->sampler = sfl_agent_getSampler(sp->agent, &dsi); } /*_________________---------------------------__________________ _________________ readPacket __________________ -----------------___________________________------------------ */ static void readPacket(SflSp *sp, struct pkt_payload *hdr, const unsigned char *buf) { SFLFlow_sample_element hdrElem, classHdrElem, gatewayHdrElem, routerHdrElem, tagHdrElem, switchHdrElem; SFLExtended_as_path_segment as_path_segment; u_int32_t frame_len, header_len; int direction, sampledPackets, ethHdrLen, idx = 0; struct eth_header dummy_eh; u_int16_t ethType = 0, cap_len = hdr->cap_len, pkt_len = hdr->pkt_len; unsigned char *local_buf = (unsigned char *) buf; /* If we have a dummy ethernet header, we strip it here; we have rewritten Ethertype field: only src/dst MAC addresses should be compared */ ethHdrLen = sizeof(dummy_eh); memset(&dummy_eh, 0, ethHdrLen); if (memcmp(&dummy_eh, local_buf, ethHdrLen-2) == 0) { ethType = ((struct eth_header *)local_buf)->ether_type; local_buf += ethHdrLen; cap_len -= ethHdrLen; pkt_len -= ethHdrLen; } /* Let's fill sample direction in - and default to ingress */ direction = 0; if (config.nfprobe_direction) { switch (config.nfprobe_direction) { case DIRECTION_IN: direction = SFL_DIRECTION_IN; break; case DIRECTION_OUT: direction = SFL_DIRECTION_OUT; break; case DIRECTION_TAG: if (hdr->tag == 1) direction = SFL_DIRECTION_IN; else if (hdr->tag == 2) direction = SFL_DIRECTION_OUT; break; case DIRECTION_TAG2: if (hdr->tag2 == 1) direction = SFL_DIRECTION_IN; else if (hdr->tag2 == 2) direction = SFL_DIRECTION_OUT; break; } } // Let's determine the ifIndex if (!hdr->ifindex_in && !hdr->ifindex_out) { if (sp->ifIndex_Type) { switch (sp->ifIndex_Type) { case IFINDEX_TAG: for (idx = 0; idx < SFL_MAX_INTERFACES; idx++) { if (sp->counters[idx].ifIndex == hdr->tag || idx == (SFL_MAX_INTERFACES-1)) break; else if (sp->counters[idx].ifIndex == 0) { sp->counters[idx].ifIndex = hdr->tag; break; } } break; case IFINDEX_TAG2: for (idx = 0; idx < SFL_MAX_INTERFACES; idx++) { if (sp->counters[idx].ifIndex == hdr->tag2 || idx == (SFL_MAX_INTERFACES-1)) break; else if (sp->counters[idx].ifIndex == 0) { sp->counters[idx].ifIndex = hdr->tag2; break; } } break; } } } else { u_int32_t ifIndex = (direction == SFL_DIRECTION_IN) ? hdr->ifindex_in : hdr->ifindex_out; for (idx = 0; idx < SFL_MAX_INTERFACES; idx++) { if (sp->counters[idx].ifIndex == ifIndex || idx == (SFL_MAX_INTERFACES-1)) break; else if (sp->counters[idx].ifIndex == 0) { sp->counters[idx].ifIndex = ifIndex; break; } } } // maintain some counters in software - just to ease portability sp->counters[idx].bytes[direction] += pkt_len; if (local_buf[0] & 0x01) { if(local_buf[0] == 0xff && local_buf[1] == 0xff && local_buf[2] == 0xff && local_buf[3] == 0xff && local_buf[4] == 0xff && local_buf[5] == 0xff) sp->counters[idx].broadcasts[direction]++; else sp->counters[idx].multicasts[direction]++; } else sp->counters[idx].frames[direction]++; if (config.ext_sampling_rate || sfl_sampler_takeSample(sp->sampler)) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): %02x%02x%02x%02x%02x%02x -> %02x%02x%02x%02x%02x%02x (len = %d, captured = %d)\n", config.name, config.type, local_buf[6], local_buf[7], local_buf[8], local_buf[9], local_buf[10], local_buf[11], local_buf[0], local_buf[1], local_buf[2], local_buf[3], local_buf[4], local_buf[5], pkt_len, cap_len); // Yes. Build a flow sample and send it off... SFL_FLOW_SAMPLE_TYPE fs; memset(&fs, 0, sizeof(fs)); if (!hdr->ifindex_in && !hdr->ifindex_out) { if (sp->ifIndex_Type) { switch (sp->ifIndex_Type) { case IFINDEX_STATIC: fs.input = (direction == SFL_DIRECTION_IN) ? sp->counters[0].ifIndex : 0x3FFFFFFF; fs.output = (direction == SFL_DIRECTION_OUT) ? sp->counters[0].ifIndex : 0x3FFFFFFF; break; case IFINDEX_TAG: fs.input = (direction == SFL_DIRECTION_IN) ? hdr->tag : 0x3FFFFFFF; fs.output = (direction == SFL_DIRECTION_OUT) ? hdr->tag : 0x3FFFFFFF; break; case IFINDEX_TAG2: fs.input = (direction == SFL_DIRECTION_IN) ? hdr->tag2 : 0x3FFFFFFF; fs.output = (direction == SFL_DIRECTION_OUT) ? hdr->tag2 : 0x3FFFFFFF; break; } } } else { fs.input = (hdr->ifindex_in) ? hdr->ifindex_in : 0x3FFFFFFF; fs.output = (hdr->ifindex_out) ? hdr->ifindex_out : 0x3FFFFFFF; } memset(&hdrElem, 0, sizeof(hdrElem)); hdrElem.tag = SFLFLOW_HEADER; if (!ethType) hdrElem.flowType.header.header_protocol = SFLHEADER_ETHERNET_ISO8023; else { switch (ntohs(ethType)) { case ETHERTYPE_IP: hdrElem.flowType.header.header_protocol = SFLHEADER_IPv4; break; case ETHERTYPE_IPV6: hdrElem.flowType.header.header_protocol = SFLHEADER_IPv6; break; default: hdrElem.flowType.header.header_protocol = SFLHEADER_ETHERNET_ISO8023; break; } } // the FCS trailing bytes should be counted in the frame_length // but they should also be recorded in the "stripped" field. // assume that libpcap is not giving us the FCS frame_len = pkt_len; if (config.acct_type == ACCT_PM) { u_int32_t FCS_bytes = 4; hdrElem.flowType.header.frame_length = frame_len + FCS_bytes; hdrElem.flowType.header.stripped = FCS_bytes; } else hdrElem.flowType.header.frame_length = frame_len; header_len = cap_len; if (header_len > frame_len) header_len = frame_len; if (header_len > sp->snaplen) header_len = sp->snaplen; hdrElem.flowType.header.header_length = header_len; hdrElem.flowType.header.header_bytes = (u_int8_t *)local_buf; SFLADD_ELEMENT(&fs, &hdrElem); if (config.what_to_count & COUNT_CLASS) { memset(&classHdrElem, 0, sizeof(classHdrElem)); classHdrElem.tag = SFLFLOW_EX_CLASS; classHdrElem.flowType.class.class = hdr->class; SFLADD_ELEMENT(&fs, &classHdrElem); } if (config.what_to_count & (COUNT_ID|COUNT_ID2)) { memset(&tagHdrElem, 0, sizeof(tagHdrElem)); tagHdrElem.tag = SFLFLOW_EX_TAG; tagHdrElem.flowType.tag.tag = hdr->tag; tagHdrElem.flowType.tag.tag2 = hdr->tag2; SFLADD_ELEMENT(&fs, &tagHdrElem); } /* Extended gateway is meant to have a broad range of informations; we will fill in only infos pertaining to src and dst ASNs */ if (config.networks_file || config.nfacctd_as == NF_AS_BGP) { memset(&gatewayHdrElem, 0, sizeof(gatewayHdrElem)); memset(&as_path_segment, 0, sizeof(as_path_segment)); gatewayHdrElem.tag = SFLFLOW_EX_GATEWAY; // gatewayHdrElem.flowType.gateway.src_as = htonl(hdr->src_ip.address.ipv4.s_addr); gatewayHdrElem.flowType.gateway.src_as = hdr->src_as; gatewayHdrElem.flowType.gateway.dst_as_path_segments = 1; gatewayHdrElem.flowType.gateway.dst_as_path = &as_path_segment; as_path_segment.type = SFLEXTENDED_AS_SET; as_path_segment.length = 1; as_path_segment.as.set = &hdr->dst_as; if (config.what_to_count & COUNT_PEER_DST_IP) { switch (hdr->bgp_next_hop.family) { case AF_INET: gatewayHdrElem.flowType.gateway.nexthop.type = SFLADDRESSTYPE_IP_V4; memcpy(&gatewayHdrElem.flowType.gateway.nexthop.address.ip_v4, &hdr->bgp_next_hop.address.ipv4, 4); break; #if defined ENABLE_IPV6 case AF_INET6: gatewayHdrElem.flowType.gateway.nexthop.type = SFLADDRESSTYPE_IP_V6; memcpy(&gatewayHdrElem.flowType.gateway.nexthop.address.ip_v6, &hdr->bgp_next_hop.address.ipv6, 16); break; #endif default: memset(&gatewayHdrElem.flowType.gateway.nexthop, 0, sizeof(routerHdrElem.flowType.router.nexthop)); break; } } SFLADD_ELEMENT(&fs, &gatewayHdrElem); } if (config.what_to_count & (COUNT_SRC_NMASK|COUNT_DST_NMASK)) { memset(&routerHdrElem, 0, sizeof(routerHdrElem)); routerHdrElem.tag = SFLFLOW_EX_ROUTER; routerHdrElem.flowType.router.src_mask = hdr->src_nmask; routerHdrElem.flowType.router.dst_mask = hdr->dst_nmask; SFLADD_ELEMENT(&fs, &routerHdrElem); } if (config.what_to_count & (COUNT_VLAN|COUNT_COS)) { memset(&switchHdrElem, 0, sizeof(switchHdrElem)); switchHdrElem.tag = SFLFLOW_EX_SWITCH; if (direction == SFL_DIRECTION_IN) { switchHdrElem.flowType.sw.src_vlan = hdr->vlan; switchHdrElem.flowType.sw.src_priority = hdr->priority; } else if (direction == SFL_DIRECTION_OUT) { switchHdrElem.flowType.sw.dst_vlan = hdr->vlan; switchHdrElem.flowType.sw.dst_priority = hdr->priority; } SFLADD_ELEMENT(&fs, &switchHdrElem); } // submit the sample to be encoded and sent out - that's all there is to it(!) sfl_sampler_writeFlowSample(sp->sampler, &fs); } } static void parse_receiver(char *string, struct in_addr *addr, u_int32_t *port) { char *delim, *ptr; trim_spaces(string); delim = strchr(string, ':'); if (delim) { *delim = '\0'; ptr = delim+1; addr->s_addr = Name_to_IP(string); *port = atoi(ptr); *delim = ':'; } else Log(LOG_WARNING, "WARN ( %s/%s ): Receiver address '%s' is not valid. Ignoring.\n", config.name, config.type, string); } /*_________________---------------------------__________________ _________________ process_config_options __________________ -----------------___________________________------------------ */ static void process_config_options(SflSp *sp) { if (config.nfprobe_ifindex_type) sp->ifIndex_Type = config.nfprobe_ifindex_type; if (config.nfprobe_ifindex) sp->counters[0].ifIndex = config.nfprobe_ifindex; if (config.sfprobe_ifspeed) sp->ifSpeed = config.sfprobe_ifspeed; if (config.sfprobe_agentip) sp->agentIP.s_addr = Name_to_IP(config.sfprobe_agentip); if (config.sfprobe_agentsubid) sp->agentSubId = config.sfprobe_agentsubid; if (config.sfprobe_receiver) parse_receiver(config.sfprobe_receiver, &sp->collectorIP, &sp->collectorPort); if (config.sampling_rate) sp->samplingRate = config.sampling_rate; else if (config.ext_sampling_rate) sp->samplingRate = config.ext_sampling_rate; } /*_________________---------------------------__________________ _________________ sfprobe_plugin __________________ -----------------___________________________------------------ */ #define NF_NET_NEW 0x00000002 void sfprobe_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_payload *hdr; struct pkt_data dummy; struct pollfd pfd; struct timezone tz; unsigned char *pipebuf, *pipebuf_ptr; time_t now; int timeout; int ret, num; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; unsigned char *rgptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; time_t clk, test_clk; SflSp sp; memset(&sp, 0, sizeof(sp)); /* XXX: glue */ memcpy(&config, cfgptr, sizeof(struct configuration)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "sFlow Probe Plugin", config.name); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile); } reload_map = FALSE; /* signal handling */ signal(SIGINT, sfprobe_exit_now); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, reload_maps); signal(SIGPIPE, SIG_IGN); #if !defined FBSD4 signal(SIGCHLD, SIG_IGN); #else signal(SIGCHLD, ignore_falling_child); #endif /* ****** sFlow part starts here ****** */ setDefaults(&sp); process_config_options(&sp); // create the agent and sampler objects init_agent(&sp); // initialize the clock so we can detect second boundaries clk = time(NULL); /* ****** sFlow part ends here ****** */ Log(LOG_INFO, "INFO ( %s/%s ): Exporting flows to [%s]:%d\n", config.name, config.type, inet_ntoa(sp.collectorIP), sp.collectorPort); Log(LOG_INFO, "INFO ( %s/%s ): Sampling at: 1/%d\n", config.name, config.type, sp.samplingRate); memset(&nt, 0, sizeof(nt)); memset(&nc, 0, sizeof(nc)); memset(&dummy, 0, sizeof(dummy)); if (config.networks_file) { config.what_to_count |= (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SRC_NMASK|COUNT_DST_NMASK); load_networks(config.networks_file, &nt, &nc); set_net_funcs(&nt); } pipebuf = (unsigned char *) Malloc(config.buffer_size); memset(pipebuf, 0, config.buffer_size); pfd.fd = pipe_fd; pfd.events = POLLIN; setnonblocking(pipe_fd); timeout = 60 * 1000; /* 1 min */ for (;;) { poll_again: status->wakeup = TRUE; ret = poll(&pfd, 1, timeout); if (ret < 0) goto poll_again; if (reload_map) { load_networks(config.networks_file, &nt, &nc); reload_map = FALSE; } if (ret > 0) { /* we received data */ read_data: if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; // goto poll_again; goto handle_tick; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%u'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%u'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); } seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); if ((rg->ptr+bufsz) >= rg->end) rg->ptr = rg->base; else rg->ptr += bufsz; hdr = (struct pkt_payload *) (pipebuf+ChBufHdrSz); pipebuf_ptr = (unsigned char *) pipebuf+ChBufHdrSz+PpayloadSz; while (((struct ch_buf_hdr *)pipebuf)->num) { if (config.networks_file) { memcpy(&dummy.primitives.src_ip, &hdr->src_ip, HostAddrSz); memcpy(&dummy.primitives.dst_ip, &hdr->dst_ip, HostAddrSz); for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &dummy.primitives); if (config.nfacctd_as == NF_AS_NEW) { hdr->src_as = dummy.primitives.src_as; hdr->dst_as = dummy.primitives.dst_as; } if (config.nfacctd_net == NF_NET_NEW) { hdr->src_nmask = dummy.primitives.src_nmask; hdr->dst_nmask = dummy.primitives.dst_nmask; } } readPacket(&sp, hdr, pipebuf_ptr); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { pipebuf_ptr += hdr->cap_len; #if NEED_ALIGN while ((u_int32_t)pipebuf_ptr % 4 != 0) (u_int32_t)pipebuf_ptr++; #endif hdr = (struct pkt_payload *) pipebuf_ptr; pipebuf_ptr += PpayloadSz; } } goto read_data; } handle_tick: test_clk = time(NULL); while(clk < test_clk) sfl_agent_tick(sp.agent, clk++); } } pmacct-0.14.0/src/sfprobe_plugin/sflow_receiver.c0000644000175000017500000007450311264400030021037 0ustar paolopaolo/* Copyright (c) 2002-2006 InMon Corp. Licensed under the terms of the InMon sFlow licence: */ /* http://www.inmon.com/technology/sflowlicense.txt */ #include #include #include #include #include #include #include #include "sflow_api.h" #include "ip_flow.h" #include "classifier.h" static void resetSampleCollector(SFLReceiver *receiver); static void sendSample(SFLReceiver *receiver); static void sflError(SFLReceiver *receiver, char *errm); static void putNet32(SFLReceiver *receiver, u_int32_t val); static void putAddress(SFLReceiver *receiver, SFLAddress *addr); /*_________________--------------------------__________________ _________________ sfl_receiver_init __________________ -----------------__________________________------------------ */ void sfl_receiver_init(SFLReceiver *receiver, SFLAgent *agent) { /* first clear everything */ memset(receiver, 0, sizeof(*receiver)); /* now copy in the parameters */ receiver->agent = agent; /* set defaults */ receiver->sFlowRcvrMaximumDatagramSize = SFL_DEFAULT_DATAGRAM_SIZE; receiver->sFlowRcvrPort = SFL_DEFAULT_COLLECTOR_PORT; /* initialize the socket address */ receiver->receiver.sin_family = AF_INET; receiver->receiver.sin_port = htons((u_int16_t)receiver->sFlowRcvrPort); receiver->receiver.sin_addr = receiver->sFlowRcvrAddress.address.ip_v4; /* preset some of the header fields */ receiver->sampleCollector.datap = receiver->sampleCollector.data; putNet32(receiver, SFLDATAGRAM_VERSION5); putAddress(receiver, &agent->myIP); putNet32(receiver, agent->subId); /* prepare to receive the first sample */ resetSampleCollector(receiver); } /*_________________---------------------------__________________ _________________ reset __________________ -----------------___________________________------------------ called on timeout, or when owner string is cleared */ static void reset(SFLReceiver *receiver) { // ask agent to tell samplers and pollers to stop sending samples sfl_agent_resetReceiver(receiver->agent, receiver); // reinitialize sfl_receiver_init(receiver, receiver->agent); } /*_________________----------------------------------------_____________ _________________ MIB Vars _____________ -----------------________________________________________------------- */ char * sfl_receiver_get_sFlowRcvrOwner(SFLReceiver *receiver) { return receiver->sFlowRcvrOwner; } void sfl_receiver_set_sFlowRcvrOwner(SFLReceiver *receiver, char *sFlowRcvrOwner) { receiver->sFlowRcvrOwner = sFlowRcvrOwner; if(sFlowRcvrOwner == NULL || sFlowRcvrOwner[0] == '\0') { // reset condition! owner string was cleared reset(receiver); } } time_t sfl_receiver_get_sFlowRcvrTimeout(SFLReceiver *receiver) { return receiver->sFlowRcvrTimeout; } void sfl_receiver_set_sFlowRcvrTimeout(SFLReceiver *receiver, time_t sFlowRcvrTimeout) { receiver->sFlowRcvrTimeout =sFlowRcvrTimeout; } u_int32_t sfl_receiver_get_sFlowRcvrMaximumDatagramSize(SFLReceiver *receiver) { return receiver->sFlowRcvrMaximumDatagramSize; } void sfl_receiver_set_sFlowRcvrMaximumDatagramSize(SFLReceiver *receiver, u_int32_t sFlowRcvrMaximumDatagramSize) { u_int32_t mdz = sFlowRcvrMaximumDatagramSize; if(mdz < SFL_MIN_DATAGRAM_SIZE) mdz = SFL_MIN_DATAGRAM_SIZE; receiver->sFlowRcvrMaximumDatagramSize = mdz; } SFLAddress *sfl_receiver_get_sFlowRcvrAddress(SFLReceiver *receiver) { return &receiver->sFlowRcvrAddress; } void sfl_receiver_set_sFlowRcvrAddress(SFLReceiver *receiver, SFLAddress *sFlowRcvrAddress) { if(sFlowRcvrAddress) receiver->sFlowRcvrAddress = *sFlowRcvrAddress; // structure copy // update the socket structure receiver->receiver.sin_addr = receiver->sFlowRcvrAddress.address.ip_v4; } u_int32_t sfl_receiver_get_sFlowRcvrPort(SFLReceiver *receiver) { return receiver->sFlowRcvrPort; } void sfl_receiver_set_sFlowRcvrPort(SFLReceiver *receiver, u_int32_t sFlowRcvrPort) { receiver->sFlowRcvrPort = sFlowRcvrPort; // update the socket structure receiver->receiver.sin_port = htons((u_int16_t)receiver->sFlowRcvrPort); } /*_________________---------------------------__________________ _________________ sfl_receiver_tick __________________ -----------------___________________________------------------ */ void sfl_receiver_tick(SFLReceiver *receiver, time_t now) { // if there are any samples to send, flush them now if(receiver->sampleCollector.numSamples > 0) sendSample(receiver); // check the timeout if(receiver->sFlowRcvrTimeout && receiver->sFlowRcvrTimeout != 0xFFFFFFFF) { // count down one tick and reset if we reach 0 if(--receiver->sFlowRcvrTimeout == 0) reset(receiver); } } /*_________________-----------------------------__________________ _________________ receiver write utilities __________________ -----------------_____________________________------------------ */ inline static void put32(SFLReceiver *receiver, u_int32_t val) { *receiver->sampleCollector.datap++ = val; } inline static void putNet32(SFLReceiver *receiver, u_int32_t val) { *receiver->sampleCollector.datap++ = htonl(val); } inline static void putNet32_run(SFLReceiver *receiver, void *obj, size_t quads) { u_int32_t *from = (u_int32_t *)obj; while(quads--) putNet32(receiver, *from++); } inline static void putNet64(SFLReceiver *receiver, u_int64_t val64) { u_int32_t *firstQuadPtr = receiver->sampleCollector.datap; // first copy the bytes in memcpy((u_char *)firstQuadPtr, &val64, 8); if(htonl(1) != 1) { // swap the bytes, and reverse the quads too u_int32_t tmp = *receiver->sampleCollector.datap++; *firstQuadPtr = htonl(*receiver->sampleCollector.datap); *receiver->sampleCollector.datap++ = htonl(tmp); } else receiver->sampleCollector.datap += 2; } inline static void put128(SFLReceiver *receiver, u_char *val) { memcpy(receiver->sampleCollector.datap, val, 16); receiver->sampleCollector.datap += 4; } inline static void putString(SFLReceiver *receiver, SFLString *s) { putNet32(receiver, s->len); memcpy(receiver->sampleCollector.datap, s->str, s->len); receiver->sampleCollector.datap += (s->len + 3) / 4; /* pad to 4-byte boundary */ } inline static u_int32_t stringEncodingLength(SFLString *s) { // answer in bytes, so remember to mulitply by 4 after rounding up to nearest 4-byte boundary return 4 + (((s->len + 3) / 4) * 4); } inline static void putAddress(SFLReceiver *receiver, SFLAddress *addr) { // encode unspecified addresses as IPV4:0.0.0.0 - or should we flag this as an error? if(addr->type == 0) { putNet32(receiver, SFLADDRESSTYPE_IP_V4); put32(receiver, 0); } else { putNet32(receiver, addr->type); if(addr->type == SFLADDRESSTYPE_IP_V4) put32(receiver, addr->address.ip_v4.s_addr); else put128(receiver, addr->address.ip_v6.s6_addr); } } inline static u_int32_t addressEncodingLength(SFLAddress *addr) { return (addr->type == SFLADDRESSTYPE_IP_V6) ? 20 : 8; // type + address (unspecified == IPV4) } inline static void putMACAddress(SFLReceiver *receiver, u_int8_t *mac) { memcpy(receiver->sampleCollector.datap, mac, 6); receiver->sampleCollector.datap += 2; } inline static void putSwitch(SFLReceiver *receiver, SFLExtended_switch *sw) { putNet32(receiver, sw->src_vlan); putNet32(receiver, sw->src_priority); putNet32(receiver, sw->dst_vlan); putNet32(receiver, sw->dst_priority); } inline static void putRouter(SFLReceiver *receiver, SFLExtended_router *router) { putAddress(receiver, &router->nexthop); putNet32(receiver, router->src_mask); putNet32(receiver, router->dst_mask); } inline static u_int32_t routerEncodingLength(SFLExtended_router *router) { return addressEncodingLength(&router->nexthop) + 8; } inline static void putGateway(SFLReceiver *receiver, SFLExtended_gateway *gw) { int seg; putAddress(receiver, &gw->nexthop); putNet32(receiver, gw->as); putNet32(receiver, gw->src_as); putNet32(receiver, gw->src_peer_as); putNet32(receiver, gw->dst_as_path_segments); for(seg = 0; seg < gw->dst_as_path_segments; seg++) { putNet32(receiver, gw->dst_as_path[seg].type); putNet32(receiver, gw->dst_as_path[seg].length); putNet32_run(receiver, gw->dst_as_path[seg].as.seq, gw->dst_as_path[seg].length); } putNet32(receiver, gw->communities_length); putNet32_run(receiver, gw->communities, gw->communities_length); putNet32(receiver, gw->localpref); } inline static u_int32_t gatewayEncodingLength(SFLExtended_gateway *gw) { u_int32_t elemSiz = addressEncodingLength(&gw->nexthop); int seg; elemSiz += 16; // as, src_as, src_peer_as, dst_as_path_segments for(seg = 0; seg < gw->dst_as_path_segments; seg++) { elemSiz += 8; // type, length elemSiz += 4 * gw->dst_as_path[seg].length; // set/seq bytes } elemSiz += 4; // communities_length elemSiz += 4 * gw->communities_length; // communities elemSiz += 4; // localpref return elemSiz; } inline static void putUser(SFLReceiver *receiver, SFLExtended_user *user) { putNet32(receiver, user->src_charset); putString(receiver, &user->src_user); putNet32(receiver, user->dst_charset); putString(receiver, &user->dst_user); } inline static u_int32_t userEncodingLength(SFLExtended_user *user) { return 4 + stringEncodingLength(&user->src_user) + 4 + stringEncodingLength(&user->dst_user); } inline static void putUrl(SFLReceiver *receiver, SFLExtended_url *url) { putNet32(receiver, url->direction); putString(receiver, &url->url); putString(receiver, &url->host); } inline static u_int32_t urlEncodingLength(SFLExtended_url *url) { return 4 + stringEncodingLength(&url->url) + stringEncodingLength(&url->host); } inline static void putLabelStack(SFLReceiver *receiver, SFLLabelStack *labelStack) { putNet32(receiver, labelStack->depth); putNet32_run(receiver, labelStack->stack, labelStack->depth); } inline static u_int32_t labelStackEncodingLength(SFLLabelStack *labelStack) { return 4 + (4 * labelStack->depth); } inline static void putMpls(SFLReceiver *receiver, SFLExtended_mpls *mpls) { putAddress(receiver, &mpls->nextHop); putLabelStack(receiver, &mpls->in_stack); putLabelStack(receiver, &mpls->out_stack); } inline static u_int32_t mplsEncodingLength(SFLExtended_mpls *mpls) { return addressEncodingLength(&mpls->nextHop) + labelStackEncodingLength(&mpls->in_stack) + labelStackEncodingLength(&mpls->out_stack); } inline static void putNat(SFLReceiver *receiver, SFLExtended_nat *nat) { putAddress(receiver, &nat->src); putAddress(receiver, &nat->dst); } inline static u_int32_t natEncodingLength(SFLExtended_nat *nat) { return addressEncodingLength(&nat->src) + addressEncodingLength(&nat->dst); } inline static void putMplsTunnel(SFLReceiver *receiver, SFLExtended_mpls_tunnel *tunnel) { putString(receiver, &tunnel->tunnel_lsp_name); putNet32(receiver, tunnel->tunnel_id); putNet32(receiver, tunnel->tunnel_cos); } inline static u_int32_t mplsTunnelEncodingLength(SFLExtended_mpls_tunnel *tunnel) { return stringEncodingLength(&tunnel->tunnel_lsp_name) + 8; } inline static void putMplsVc(SFLReceiver *receiver, SFLExtended_mpls_vc *vc) { putString(receiver, &vc->vc_instance_name); putNet32(receiver, vc->vll_vc_id); putNet32(receiver, vc->vc_label_cos); } inline static u_int32_t mplsVcEncodingLength(SFLExtended_mpls_vc *vc) { return stringEncodingLength( &vc->vc_instance_name) + 8; } inline static void putMplsFtn(SFLReceiver *receiver, SFLExtended_mpls_FTN *ftn) { putString(receiver, &ftn->mplsFTNDescr); putNet32(receiver, ftn->mplsFTNMask); } inline static u_int32_t mplsFtnEncodingLength(SFLExtended_mpls_FTN *ftn) { return stringEncodingLength( &ftn->mplsFTNDescr) + 4; } inline static void putMplsLdpFec(SFLReceiver *receiver, SFLExtended_mpls_LDP_FEC *ldpfec) { putNet32(receiver, ldpfec->mplsFecAddrPrefixLength); } inline static u_int32_t mplsLdpFecEncodingLength(SFLExtended_mpls_LDP_FEC *ldpfec) { return 4; } inline static void putVlanTunnel(SFLReceiver *receiver, SFLExtended_vlan_tunnel *vlanTunnel) { putLabelStack(receiver, &vlanTunnel->stack); } inline static u_int32_t vlanTunnelEncodingLength(SFLExtended_vlan_tunnel *vlanTunnel) { return labelStackEncodingLength(&vlanTunnel->stack); } inline static void putClass(SFLReceiver *receiver, SFLExtended_classification *class_elem) { char buf[MAX_PROTOCOL_LEN+1]; memset(buf, 0, MAX_PROTOCOL_LEN+1); if (class_elem->class && class[class_elem->class-1].id) { strlcpy(buf, class[class_elem->class-1].protocol, MAX_PROTOCOL_LEN); buf[sizeof(buf)-1] = '\0'; } else strlcpy(buf, "unknown", MAX_PROTOCOL_LEN); put128(receiver, buf); } inline static void putTag(SFLReceiver *receiver, SFLExtended_tag *tag_elem) { putNet32(receiver, tag_elem->tag); putNet32(receiver, tag_elem->tag2); } inline static void putGenericCounters(SFLReceiver *receiver, SFLIf_counters *counters) { putNet32(receiver, counters->ifIndex); putNet32(receiver, counters->ifType); putNet64(receiver, counters->ifSpeed); putNet32(receiver, counters->ifDirection); putNet32(receiver, counters->ifStatus); putNet64(receiver, counters->ifInOctets); putNet32(receiver, counters->ifInUcastPkts); putNet32(receiver, counters->ifInMulticastPkts); putNet32(receiver, counters->ifInBroadcastPkts); putNet32(receiver, counters->ifInDiscards); putNet32(receiver, counters->ifInErrors); putNet32(receiver, counters->ifInUnknownProtos); putNet64(receiver, counters->ifOutOctets); putNet32(receiver, counters->ifOutUcastPkts); putNet32(receiver, counters->ifOutMulticastPkts); putNet32(receiver, counters->ifOutBroadcastPkts); putNet32(receiver, counters->ifOutDiscards); putNet32(receiver, counters->ifOutErrors); putNet32(receiver, counters->ifPromiscuousMode); } /*_________________-----------------------------__________________ _________________ computeFlowSampleSize __________________ -----------------_____________________________------------------ */ static int computeFlowSampleSize(SFLReceiver *receiver, SFL_FLOW_SAMPLE_TYPE *fs) { SFLFlow_sample_element *elem; u_int elemSiz = 0; #ifdef SFL_USE_32BIT_INDEX u_int siz = 52; /* tag, length, sequence_number, ds_class, ds_index, sampling_rate, sample_pool, drops, inputFormat, input, outputFormat, output, number of elements */ #else u_int siz = 40; /* tag, length, sequence_number, source_id, sampling_rate, sample_pool, drops, input, output, number of elements */ #endif fs->num_elements = 0; /* we're going to count them again even if this was set by the client */ for(elem = fs->elements; elem != NULL; elem = elem->nxt) { fs->num_elements++; siz += 8; /* tag, length */ switch(elem->tag) { case SFLFLOW_HEADER: elemSiz = 16; /* header_protocol, frame_length, stripped, header_length */ elemSiz += ((elem->flowType.header.header_length + 3) / 4) * 4; /* header, rounded up to nearest 4 bytes */ break; case SFLFLOW_ETHERNET: elemSiz = sizeof(SFLSampled_ethernet); break; case SFLFLOW_IPV4: elemSiz = sizeof(SFLSampled_ipv4); break; case SFLFLOW_IPV6: elemSiz = sizeof(SFLSampled_ipv6); break; case SFLFLOW_EX_SWITCH: elemSiz = sizeof(SFLExtended_switch); break; case SFLFLOW_EX_ROUTER: elemSiz = routerEncodingLength(&elem->flowType.router); break; case SFLFLOW_EX_GATEWAY: elemSiz = gatewayEncodingLength(&elem->flowType.gateway); break; case SFLFLOW_EX_USER: elemSiz = userEncodingLength(&elem->flowType.user); break; case SFLFLOW_EX_URL: elemSiz = urlEncodingLength(&elem->flowType.url); break; case SFLFLOW_EX_MPLS: elemSiz = mplsEncodingLength(&elem->flowType.mpls); break; case SFLFLOW_EX_NAT: elemSiz = natEncodingLength(&elem->flowType.nat); break; case SFLFLOW_EX_MPLS_TUNNEL: elemSiz = mplsTunnelEncodingLength(&elem->flowType.mpls_tunnel); break; case SFLFLOW_EX_MPLS_VC: elemSiz = mplsVcEncodingLength(&elem->flowType.mpls_vc); break; case SFLFLOW_EX_MPLS_FTN: elemSiz = mplsFtnEncodingLength(&elem->flowType.mpls_ftn); break; case SFLFLOW_EX_MPLS_LDP_FEC: elemSiz = mplsLdpFecEncodingLength(&elem->flowType.mpls_ldp_fec); break; case SFLFLOW_EX_VLAN_TUNNEL: elemSiz = vlanTunnelEncodingLength(&elem->flowType.vlan_tunnel); break; case SFLFLOW_EX_CLASS: elemSiz = MAX_PROTOCOL_LEN; break; case SFLFLOW_EX_TAG: elemSiz = 8; break; default: sflError(receiver, "unexpected packet_data_tag"); return -1; break; } // cache the element size, and accumulate it into the overall FlowSample size elem->length = elemSiz; siz += elemSiz; } return siz; } /*_________________-------------------------------__________________ _________________ sfl_receiver_writeFlowSample __________________ -----------------_______________________________------------------ */ int sfl_receiver_writeFlowSample(SFLReceiver *receiver, SFL_FLOW_SAMPLE_TYPE *fs) { SFLFlow_sample_element *elem; int num_extended = 0, packedSize; if(fs == NULL) return -1; if((packedSize = computeFlowSampleSize(receiver, fs)) == -1) return -1; // check in case this one sample alone is too big for the datagram // in fact - if it is even half as big then we should ditch it. Very // important to avoid overruning the packet buffer. if(packedSize > (receiver->sFlowRcvrMaximumDatagramSize / 2)) { sflError(receiver, "flow sample too big for datagram"); return -1; } // if the sample pkt is full enough so that this sample might put // it over the limit, then we should send it now before going on. if((receiver->sampleCollector.pktlen + packedSize) >= receiver->sFlowRcvrMaximumDatagramSize) sendSample(receiver); receiver->sampleCollector.numSamples++; #ifdef SFL_USE_32BIT_INDEX putNet32(receiver, SFLFLOW_SAMPLE_EXPANDED); #else putNet32(receiver, SFLFLOW_SAMPLE); #endif putNet32(receiver, packedSize - 8); // don't include tag and len putNet32(receiver, fs->sequence_number); #ifdef SFL_USE_32BIT_INDEX putNet32(receiver, fs->ds_class); putNet32(receiver, fs->ds_index); #else putNet32(receiver, fs->source_id); #endif putNet32(receiver, fs->sampling_rate); putNet32(receiver, fs->sample_pool); putNet32(receiver, fs->drops); #ifdef SFL_USE_32BIT_INDEX putNet32(receiver, fs->inputFormat); putNet32(receiver, fs->input); putNet32(receiver, fs->outputFormat); putNet32(receiver, fs->output); #else putNet32(receiver, fs->input); putNet32(receiver, fs->output); #endif putNet32(receiver, fs->num_elements); for (elem = fs->elements; elem != NULL; elem = elem->nxt) { putNet32(receiver, elem->tag); putNet32(receiver, elem->length); // length cached in computeFlowSampleSize() switch(elem->tag) { case SFLFLOW_HEADER: putNet32(receiver, elem->flowType.header.header_protocol); putNet32(receiver, elem->flowType.header.frame_length); putNet32(receiver, elem->flowType.header.stripped); putNet32(receiver, elem->flowType.header.header_length); /* the header */ memcpy(receiver->sampleCollector.datap, elem->flowType.header.header_bytes, elem->flowType.header.header_length); /* round up to multiple of 4 to preserve alignment */ receiver->sampleCollector.datap += ((elem->flowType.header.header_length + 3) / 4); break; case SFLFLOW_ETHERNET: putNet32(receiver, elem->flowType.ethernet.eth_len); putMACAddress(receiver, elem->flowType.ethernet.src_mac); putMACAddress(receiver, elem->flowType.ethernet.dst_mac); putNet32(receiver, elem->flowType.ethernet.eth_type); break; case SFLFLOW_IPV4: putNet32(receiver, elem->flowType.ipv4.length); putNet32(receiver, elem->flowType.ipv4.protocol); put32(receiver, elem->flowType.ipv4.src_ip.s_addr); put32(receiver, elem->flowType.ipv4.dst_ip.s_addr); putNet32(receiver, elem->flowType.ipv4.src_port); putNet32(receiver, elem->flowType.ipv4.dst_port); putNet32(receiver, elem->flowType.ipv4.tcp_flags); putNet32(receiver, elem->flowType.ipv4.tos); break; case SFLFLOW_IPV6: putNet32(receiver, elem->flowType.ipv6.length); putNet32(receiver, elem->flowType.ipv6.protocol); put128(receiver, elem->flowType.ipv6.src_ip.s6_addr); put128(receiver, elem->flowType.ipv6.dst_ip.s6_addr); putNet32(receiver, elem->flowType.ipv6.src_port); putNet32(receiver, elem->flowType.ipv6.dst_port); putNet32(receiver, elem->flowType.ipv6.tcp_flags); putNet32(receiver, elem->flowType.ipv6.priority); break; case SFLFLOW_EX_SWITCH: putSwitch(receiver, &elem->flowType.sw); break; case SFLFLOW_EX_ROUTER: putRouter(receiver, &elem->flowType.router); break; case SFLFLOW_EX_GATEWAY: putGateway(receiver, &elem->flowType.gateway); break; case SFLFLOW_EX_USER: putUser(receiver, &elem->flowType.user); break; case SFLFLOW_EX_URL: putUrl(receiver, &elem->flowType.url); break; case SFLFLOW_EX_MPLS: putMpls(receiver, &elem->flowType.mpls); break; case SFLFLOW_EX_NAT: putNat(receiver, &elem->flowType.nat); break; case SFLFLOW_EX_MPLS_TUNNEL: putMplsTunnel(receiver, &elem->flowType.mpls_tunnel); break; case SFLFLOW_EX_MPLS_VC: putMplsVc(receiver, &elem->flowType.mpls_vc); break; case SFLFLOW_EX_MPLS_FTN: putMplsFtn(receiver, &elem->flowType.mpls_ftn); break; case SFLFLOW_EX_MPLS_LDP_FEC: putMplsLdpFec(receiver, &elem->flowType.mpls_ldp_fec); break; case SFLFLOW_EX_VLAN_TUNNEL: putVlanTunnel(receiver, &elem->flowType.vlan_tunnel); break; case SFLFLOW_EX_CLASS: putClass(receiver, &elem->flowType.class); break; case SFLFLOW_EX_TAG: putTag(receiver, &elem->flowType.tag); break; default: sflError(receiver, "unexpected packet_data_tag"); return -1; break; } } // sanity check assert(((u_char *)receiver->sampleCollector.datap - (u_char *)receiver->sampleCollector.data - receiver->sampleCollector.pktlen) == packedSize); // update the pktlen receiver->sampleCollector.pktlen = (u_char *)receiver->sampleCollector.datap - (u_char *)receiver->sampleCollector.data; return packedSize; } /*_________________-----------------------------__________________ _________________ computeCountersSampleSize __________________ -----------------_____________________________------------------ */ static int computeCountersSampleSize(SFLReceiver *receiver, SFL_COUNTERS_SAMPLE_TYPE *cs) { SFLCounters_sample_element *elem; u_int elemSiz = 0; #ifdef SFL_USE_32BIT_INDEX u_int siz = 24; /* tag, length, sequence_number, ds_class, ds_index, number of elements */ #else u_int siz = 20; /* tag, length, sequence_number, source_id, number of elements */ #endif cs->num_elements = 0; /* we're going to count them again even if this was set by the client */ for(elem = cs->elements; elem != NULL; elem = elem->nxt) { cs->num_elements++; siz += 8; /* tag, length */ switch(elem->tag) { case SFLCOUNTERS_GENERIC: elemSiz = sizeof(elem->counterBlock.generic); break; case SFLCOUNTERS_ETHERNET: elemSiz = sizeof(elem->counterBlock.ethernet); break; case SFLCOUNTERS_TOKENRING: elemSiz = sizeof(elem->counterBlock.tokenring); break; case SFLCOUNTERS_VG: elemSiz = sizeof(elem->counterBlock.vg); break; case SFLCOUNTERS_VLAN: elemSiz = sizeof(elem->counterBlock.vlan); break; default: sflError(receiver, "unexpected counters_tag"); return -1; break; } // cache the element size, and accumulate it into the overall FlowSample size elem->length = elemSiz; siz += elemSiz; } return siz; } /*_________________----------------------------------__________________ _________________ sfl_receiver_writeCountersSample __________________ -----------------__________________________________------------------ */ int sfl_receiver_writeCountersSample(SFLReceiver *receiver, SFL_COUNTERS_SAMPLE_TYPE *cs) { SFLCounters_sample_element *elem; int packedSize; if(cs == NULL) return -1; // if the sample pkt is full enough so that this sample might put // it over the limit, then we should send it now. if((packedSize = computeCountersSampleSize(receiver, cs)) == -1) return -1; // check in case this one sample alone is too big for the datagram // in fact - if it is even half as big then we should ditch it. Very // important to avoid overruning the packet buffer. if(packedSize > (receiver->sFlowRcvrMaximumDatagramSize / 2)) { sflError(receiver, "counters sample too big for datagram"); return -1; } if((receiver->sampleCollector.pktlen + packedSize) >= receiver->sFlowRcvrMaximumDatagramSize) sendSample(receiver); receiver->sampleCollector.numSamples++; #ifdef SFL_USE_32BIT_INDEX putNet32(receiver, SFLCOUNTERS_SAMPLE_EXPANDED); #else putNet32(receiver, SFLCOUNTERS_SAMPLE); #endif putNet32(receiver, packedSize - 8); // tag and length not included putNet32(receiver, cs->sequence_number); #ifdef SFL_USE_32BIT_INDEX putNet32(receiver, cs->ds_class); putNet32(receiver, cs->ds_index); #else putNet32(receiver, cs->source_id); #endif putNet32(receiver, cs->num_elements); for(elem = cs->elements; elem != NULL; elem = elem->nxt) { putNet32(receiver, elem->tag); putNet32(receiver, elem->length); // length cached in computeCountersSampleSize() switch(elem->tag) { case SFLCOUNTERS_GENERIC: putGenericCounters(receiver, &(elem->counterBlock.generic)); break; case SFLCOUNTERS_ETHERNET: // all these counters are 32-bit putNet32_run(receiver, &elem->counterBlock.ethernet, sizeof(elem->counterBlock.ethernet) / 4); break; case SFLCOUNTERS_TOKENRING: // all these counters are 32-bit putNet32_run(receiver, &elem->counterBlock.tokenring, sizeof(elem->counterBlock.tokenring) / 4); break; case SFLCOUNTERS_VG: // mixed sizes putNet32(receiver, elem->counterBlock.vg.dot12InHighPriorityFrames); putNet64(receiver, elem->counterBlock.vg.dot12InHighPriorityOctets); putNet32(receiver, elem->counterBlock.vg.dot12InNormPriorityFrames); putNet64(receiver, elem->counterBlock.vg.dot12InNormPriorityOctets); putNet32(receiver, elem->counterBlock.vg.dot12InIPMErrors); putNet32(receiver, elem->counterBlock.vg.dot12InOversizeFrameErrors); putNet32(receiver, elem->counterBlock.vg.dot12InDataErrors); putNet32(receiver, elem->counterBlock.vg.dot12InNullAddressedFrames); putNet32(receiver, elem->counterBlock.vg.dot12OutHighPriorityFrames); putNet64(receiver, elem->counterBlock.vg.dot12OutHighPriorityOctets); putNet32(receiver, elem->counterBlock.vg.dot12TransitionIntoTrainings); putNet64(receiver, elem->counterBlock.vg.dot12HCInHighPriorityOctets); putNet64(receiver, elem->counterBlock.vg.dot12HCInNormPriorityOctets); putNet64(receiver, elem->counterBlock.vg.dot12HCOutHighPriorityOctets); break; case SFLCOUNTERS_VLAN: // mixed sizes putNet32(receiver, elem->counterBlock.vlan.vlan_id); putNet64(receiver, elem->counterBlock.vlan.octets); putNet32(receiver, elem->counterBlock.vlan.ucastPkts); putNet32(receiver, elem->counterBlock.vlan.multicastPkts); putNet32(receiver, elem->counterBlock.vlan.broadcastPkts); putNet32(receiver, elem->counterBlock.vlan.discards); break; default: sflError(receiver, "unexpected counters_tag"); return -1; break; } } // sanity check assert(((u_char *)receiver->sampleCollector.datap - (u_char *)receiver->sampleCollector.data - receiver->sampleCollector.pktlen) == packedSize); // update the pktlen receiver->sampleCollector.pktlen = (u_char *)receiver->sampleCollector.datap - (u_char *)receiver->sampleCollector.data; return packedSize; } /*_________________---------------------------------__________________ _________________ sfl_receiver_samplePacketsSent __________________ -----------------_________________________________------------------ */ u_int32_t sfl_receiver_samplePacketsSent(SFLReceiver *receiver) { return receiver->sampleCollector.packetSeqNo; } /*_________________---------------------------__________________ _________________ sendSample __________________ -----------------___________________________------------------ */ static void sendSample(SFLReceiver *receiver) { /* construct and send out the sample, then reset for the next one... */ /* first fill in the header with the latest values */ /* version, agent_address and sub_agent_id were pre-set. */ u_int32_t hdrIdx = (receiver->sFlowRcvrAddress.type == SFLADDRESSTYPE_IP_V6) ? 7 : 4; receiver->sampleCollector.data[hdrIdx++] = htonl(++receiver->sampleCollector.packetSeqNo); /* seq no */ receiver->sampleCollector.data[hdrIdx++] = htonl((receiver->agent->now - receiver->agent->bootTime) * 1000); /* uptime */ receiver->sampleCollector.data[hdrIdx++] = htonl(receiver->sampleCollector.numSamples); /* num samples */ /* send */ if(receiver->agent->sendFn) (*receiver->agent->sendFn)(receiver->agent->magic, receiver->agent, receiver, (u_char *)receiver->sampleCollector.data, receiver->sampleCollector.pktlen); else { /* send it myself */ int result = sendto(receiver->agent->receiverSocket, receiver->sampleCollector.data, receiver->sampleCollector.pktlen, 0, (struct sockaddr *)&receiver->receiver, sizeof(receiver->receiver)); if(result == -1 && errno != EINTR) sfl_agent_sysError(receiver->agent, "receiver", "socket sendto error"); if(result == 0) sfl_agent_error(receiver->agent, "receiver", "socket sendto returned 0"); } /* reset for the next time */ resetSampleCollector(receiver); } /*_________________---------------------------__________________ _________________ resetSampleCollector __________________ -----------------___________________________------------------ */ static void resetSampleCollector(SFLReceiver *receiver) { receiver->sampleCollector.pktlen = 0; receiver->sampleCollector.numSamples = 0; /* point the datap to just after the header */ receiver->sampleCollector.datap = (receiver->sFlowRcvrAddress.type == SFLADDRESSTYPE_IP_V6) ? (receiver->sampleCollector.data + 10) : (receiver->sampleCollector.data + 7); receiver->sampleCollector.pktlen = (u_char *)receiver->sampleCollector.datap - (u_char *)receiver->sampleCollector.data; } /*_________________---------------------------__________________ _________________ sflError __________________ -----------------___________________________------------------ */ static void sflError(SFLReceiver *receiver, char *msg) { sfl_agent_error(receiver->agent, "receiver", msg); resetSampleCollector(receiver); } pmacct-0.14.0/src/sfprobe_plugin/sflow.h0000644000175000017500000005012211264400030017147 0ustar paolopaolo/* Copyright (c) 2002-2006 InMon Corp. Licensed under the terms of the InMon sFlow licence: */ /* http://www.inmon.com/technology/sflowlicense.txt */ ///////////////////////////////////////////////////////////////////////////////// /////////////////////// sFlow Sampling Packet Data Types //////////////////////// ///////////////////////////////////////////////////////////////////////////////// #ifndef SFLOW_H #define SFLOW_H 1 #if defined(__cplusplus) extern "C" { #endif enum SFLAddress_type { SFLADDRESSTYPE_IP_V4 = 1, SFLADDRESSTYPE_IP_V6 = 2 }; typedef union _SFLAddress_value { struct in_addr ip_v4; struct in6_addr ip_v6; } SFLAddress_value; typedef struct _SFLAddress { u_int32_t type; /* enum SFLAddress_type */ SFLAddress_value address; } SFLAddress; /* Packet header data */ #define SFL_DEFAULT_HEADER_SIZE 128 #define SFL_DEFAULT_COLLECTOR_PORT 6343 #define SFL_DEFAULT_SAMPLING_RATE 20 #define SFL_DEFAULT_SFACCTD_SAMPLING_RATE 1 /* The header protocol describes the format of the sampled header */ enum SFLHeader_protocol { SFLHEADER_ETHERNET_ISO8023 = 1, SFLHEADER_ISO88024_TOKENBUS = 2, SFLHEADER_ISO88025_TOKENRING = 3, SFLHEADER_FDDI = 4, SFLHEADER_FRAME_RELAY = 5, SFLHEADER_X25 = 6, SFLHEADER_PPP = 7, SFLHEADER_SMDS = 8, SFLHEADER_AAL5 = 9, SFLHEADER_AAL5_IP = 10, /* e.g. Cisco AAL5 mux */ SFLHEADER_IPv4 = 11, SFLHEADER_IPv6 = 12, SFLHEADER_MPLS = 13 }; /* raw sampled header */ typedef struct _SFLSampled_header { u_int32_t header_protocol; /* (enum SFLHeader_protocol) */ u_int32_t frame_length; /* Original length of packet before sampling */ u_int32_t stripped; /* header/trailer bytes stripped by sender */ u_int32_t header_length; /* length of sampled header bytes to follow */ u_int8_t *header_bytes; /* Header bytes */ } SFLSampled_header; /* decoded ethernet header */ typedef struct _SFLSampled_ethernet { u_int32_t eth_len; /* The length of the MAC packet excluding lower layer encapsulations */ u_int8_t src_mac[8]; /* 6 bytes + 2 pad */ u_int8_t dst_mac[8]; u_int32_t eth_type; } SFLSampled_ethernet; /* decoded IP version 4 header */ typedef struct _SFLSampled_ipv4 { u_int32_t length; /* The length of the IP packet excluding lower layer encapsulations */ u_int32_t protocol; /* IP Protocol type (for example, TCP = 6, UDP = 17) */ struct in_addr src_ip; /* Source IP Address */ struct in_addr dst_ip; /* Destination IP Address */ u_int32_t src_port; /* TCP/UDP source port number or equivalent */ u_int32_t dst_port; /* TCP/UDP destination port number or equivalent */ u_int32_t tcp_flags; /* TCP flags */ u_int32_t tos; /* IP type of service */ } SFLSampled_ipv4; /* decoded IP version 6 data */ typedef struct _SFLSampled_ipv6 { u_int32_t length; /* The length of the IP packet excluding lower layer encapsulations */ u_int32_t protocol; /* IP Protocol type (for example, TCP = 6, UDP = 17) */ struct in6_addr src_ip; /* Source IP Address */ struct in6_addr dst_ip; /* Destination IP Address */ u_int32_t src_port; /* TCP/UDP source port number or equivalent */ u_int32_t dst_port; /* TCP/UDP destination port number or equivalent */ u_int32_t tcp_flags; /* TCP flags */ u_int32_t priority; /* IP priority */ } SFLSampled_ipv6; /* Extended data types */ /* Extended switch data */ typedef struct _SFLExtended_switch { u_int32_t src_vlan; /* The 802.1Q VLAN id of incomming frame */ u_int32_t src_priority; /* The 802.1p priority */ u_int32_t dst_vlan; /* The 802.1Q VLAN id of outgoing frame */ u_int32_t dst_priority; /* The 802.1p priority */ } SFLExtended_switch; /* Extended router data */ typedef struct _SFLExtended_router { SFLAddress nexthop; /* IP address of next hop router */ u_int32_t src_mask; /* Source address prefix mask bits */ u_int32_t dst_mask; /* Destination address prefix mask bits */ } SFLExtended_router; /* Extended gateway data */ enum SFLExtended_as_path_segment_type { SFLEXTENDED_AS_SET = 1, /* Unordered set of ASs */ SFLEXTENDED_AS_SEQUENCE = 2 /* Ordered sequence of ASs */ }; typedef struct _SFLExtended_as_path_segment { u_int32_t type; /* enum SFLExtended_as_path_segment_type */ u_int32_t length; /* number of AS numbers in set/sequence */ union { u_int32_t *set; u_int32_t *seq; } as; } SFLExtended_as_path_segment; typedef struct _SFLExtended_gateway { SFLAddress nexthop; /* Address of the border router that should be used for the destination network */ u_int32_t as; /* AS number for this gateway */ u_int32_t src_as; /* AS number of source (origin) */ u_int32_t src_peer_as; /* AS number of source peer */ u_int32_t dst_as_path_segments; /* number of segments in path */ SFLExtended_as_path_segment *dst_as_path; /* list of seqs or sets */ u_int32_t communities_length; /* number of communities */ u_int32_t *communities; /* set of communities */ u_int32_t localpref; /* LocalPref associated with this route */ } SFLExtended_gateway; typedef struct _SFLString { u_int32_t len; char *str; } SFLString; /* Extended user data */ typedef struct _SFLExtended_user { u_int32_t src_charset; /* MIBEnum value of character set used to encode a string - See RFC 2978 Where possible UTF-8 encoding (MIBEnum=106) should be used. A value of zero indicates an unknown encoding. */ SFLString src_user; u_int32_t dst_charset; SFLString dst_user; } SFLExtended_user; /* Extended URL data */ enum SFLExtended_url_direction { SFLEXTENDED_URL_SRC = 1, /* URL is associated with source address */ SFLEXTENDED_URL_DST = 2 /* URL is associated with destination address */ }; typedef struct _SFLExtended_url { u_int32_t direction; /* enum SFLExtended_url_direction */ SFLString url; /* URL associated with the packet flow. Must be URL encoded */ SFLString host; /* The host field from the HTTP header */ } SFLExtended_url; /* Extended MPLS data */ typedef struct _SFLLabelStack { u_int32_t depth; u_int32_t *stack; /* first entry is top of stack - see RFC 3032 for encoding */ } SFLLabelStack; typedef struct _SFLExtended_mpls { SFLAddress nextHop; /* Address of the next hop */ SFLLabelStack in_stack; SFLLabelStack out_stack; } SFLExtended_mpls; /* Extended NAT data Packet header records report addresses as seen at the sFlowDataSource. The extended_nat structure reports on translated source and/or destination addesses for this packet. If an address was not translated it should be equal to that reported for the header. */ typedef struct _SFLExtended_nat { SFLAddress src; /* Source address */ SFLAddress dst; /* Destination address */ } SFLExtended_nat; /* additional Extended MPLS stucts */ typedef struct _SFLExtended_mpls_tunnel { SFLString tunnel_lsp_name; /* Tunnel name */ u_int32_t tunnel_id; /* Tunnel ID */ u_int32_t tunnel_cos; /* Tunnel COS value */ } SFLExtended_mpls_tunnel; typedef struct _SFLExtended_mpls_vc { SFLString vc_instance_name; /* VC instance name */ u_int32_t vll_vc_id; /* VLL/VC instance ID */ u_int32_t vc_label_cos; /* VC Label COS value */ } SFLExtended_mpls_vc; /* Extended MPLS FEC - Definitions from MPLS-FTN-STD-MIB mplsFTNTable */ typedef struct _SFLExtended_mpls_FTN { SFLString mplsFTNDescr; u_int32_t mplsFTNMask; } SFLExtended_mpls_FTN; /* Extended MPLS LVP FEC - Definition from MPLS-LDP-STD-MIB mplsFecTable Note: mplsFecAddrType, mplsFecAddr information available from packet header */ typedef struct _SFLExtended_mpls_LDP_FEC { u_int32_t mplsFecAddrPrefixLength; } SFLExtended_mpls_LDP_FEC; /* Extended VLAN tunnel information Record outer VLAN encapsulations that have been stripped. extended_vlantunnel information should only be reported if all the following conditions are satisfied: 1. The packet has nested vlan tags, AND 2. The reporting device is VLAN aware, AND 3. One or more VLAN tags have been stripped, either because they represent proprietary encapsulations, or because switch hardware automatically strips the outer VLAN encapsulation. Reporting extended_vlantunnel information is not a substitute for reporting extended_switch information. extended_switch data must always be reported to describe the ingress/egress VLAN information for the packet. The extended_vlantunnel information only applies to nested VLAN tags, and then only when one or more tags has been stripped. */ typedef SFLLabelStack SFLVlanStack; typedef struct _SFLExtended_vlan_tunnel { SFLVlanStack stack; /* List of stripped 802.1Q TPID/TCI layers. Each TPID,TCI pair is represented as a single 32 bit integer. Layers listed from outermost to innermost. */ } SFLExtended_vlan_tunnel; typedef struct _SFLExtended_classification { pm_class_t class; } SFLExtended_classification; typedef struct _SFLExtended_tag { pm_id_t tag; pm_id_t tag2; } SFLExtended_tag; enum SFLFlow_type_tag { /* enterprise = 0, format = ... */ SFLFLOW_HEADER = 1, /* Packet headers are sampled */ SFLFLOW_ETHERNET = 2, /* MAC layer information */ SFLFLOW_IPV4 = 3, /* IP version 4 data */ SFLFLOW_IPV6 = 4, /* IP version 6 data */ SFLFLOW_EX_SWITCH = 1001, /* Extended switch information */ SFLFLOW_EX_ROUTER = 1002, /* Extended router information */ SFLFLOW_EX_GATEWAY = 1003, /* Extended gateway router information */ SFLFLOW_EX_USER = 1004, /* Extended TACAS/RADIUS user information */ SFLFLOW_EX_URL = 1005, /* Extended URL information */ SFLFLOW_EX_MPLS = 1006, /* Extended MPLS information */ SFLFLOW_EX_NAT = 1007, /* Extended NAT information */ SFLFLOW_EX_MPLS_TUNNEL = 1008, /* additional MPLS information */ SFLFLOW_EX_MPLS_VC = 1009, SFLFLOW_EX_MPLS_FTN = 1010, SFLFLOW_EX_MPLS_LDP_FEC = 1011, SFLFLOW_EX_VLAN_TUNNEL = 1012, /* VLAN stack */ /* enterprise = 8800 pmacct */ SFLFLOW_EX_CLASS = (8800 << 12) + 1, SFLFLOW_EX_TAG = (8800 << 12) + 2, }; typedef union _SFLFlow_type { SFLSampled_header header; SFLSampled_ethernet ethernet; SFLSampled_ipv4 ipv4; SFLSampled_ipv6 ipv6; SFLExtended_switch sw; SFLExtended_router router; SFLExtended_gateway gateway; SFLExtended_user user; SFLExtended_url url; SFLExtended_mpls mpls; SFLExtended_nat nat; SFLExtended_mpls_tunnel mpls_tunnel; SFLExtended_mpls_vc mpls_vc; SFLExtended_mpls_FTN mpls_ftn; SFLExtended_mpls_LDP_FEC mpls_ldp_fec; SFLExtended_vlan_tunnel vlan_tunnel; SFLExtended_classification class; SFLExtended_tag tag; } SFLFlow_type; typedef struct _SFLFlow_sample_element { struct _SFLFlow_sample_element *nxt; u_int32_t tag; /* SFLFlow_type_tag */ u_int32_t length; SFLFlow_type flowType; } SFLFlow_sample_element; enum SFL_sample_tag { SFLFLOW_SAMPLE = 1, /* enterprise = 0 : format = 1 */ SFLCOUNTERS_SAMPLE = 2, /* enterprise = 0 : format = 2 */ SFLFLOW_SAMPLE_EXPANDED = 3, /* enterprise = 0 : format = 3 */ SFLCOUNTERS_SAMPLE_EXPANDED = 4 /* enterprise = 0 : format = 4 */ }; /* Format of a single flow sample */ typedef struct _SFLFlow_sample { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 1 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each flow sample generated */ u_int32_t source_id; /* fsSourceId */ u_int32_t sampling_rate; /* fsPacketSamplingRate */ u_int32_t sample_pool; /* Total number of packets that could have been sampled (i.e. packets skipped by sampling process + total number of samples) */ u_int32_t drops; /* Number of times a packet was dropped due to lack of resources */ u_int32_t input; /* SNMP ifIndex of input interface. 0 if interface is not known. */ u_int32_t output; /* SNMP ifIndex of output interface, 0 if interface is not known. Set most significant bit to indicate multiple destination interfaces (i.e. in case of broadcast or multicast) and set lower order bits to indicate number of destination interfaces. Examples: 0x00000002 indicates ifIndex = 2 0x00000000 ifIndex unknown. 0x80000007 indicates a packet sent to 7 interfaces. 0x80000000 indicates a packet sent to an unknown number of interfaces greater than 1.*/ u_int32_t num_elements; SFLFlow_sample_element *elements; } SFLFlow_sample; /* same thing, but the expanded version (for full 32-bit ifIndex numbers) */ typedef struct _SFLFlow_sample_expanded { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 1 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each flow sample generated */ u_int32_t ds_class; /* EXPANDED */ u_int32_t ds_index; /* EXPANDED */ u_int32_t sampling_rate; /* fsPacketSamplingRate */ u_int32_t sample_pool; /* Total number of packets that could have been sampled (i.e. packets skipped by sampling process + total number of samples) */ u_int32_t drops; /* Number of times a packet was dropped due to lack of resources */ u_int32_t inputFormat; /* EXPANDED */ u_int32_t input; /* SNMP ifIndex of input interface. 0 if interface is not known. */ u_int32_t outputFormat; /* EXPANDED */ u_int32_t output; /* SNMP ifIndex of output interface, 0 if interface is not known. */ u_int32_t num_elements; SFLFlow_sample_element *elements; } SFLFlow_sample_expanded; /* Counter types */ /* Generic interface counters - see RFC 1573, 2233 */ typedef struct _SFLIf_counters { u_int32_t ifIndex; u_int32_t ifType; u_int64_t ifSpeed; u_int32_t ifDirection; /* Derived from MAU MIB (RFC 2668) 0 = unknown, 1 = full-duplex, 2 = half-duplex, 3 = in, 4 = out */ u_int32_t ifStatus; /* bit field with the following bits assigned: bit 0 = ifAdminStatus (0 = down, 1 = up) bit 1 = ifOperStatus (0 = down, 1 = up) */ u_int64_t ifInOctets; u_int32_t ifInUcastPkts; u_int32_t ifInMulticastPkts; u_int32_t ifInBroadcastPkts; u_int32_t ifInDiscards; u_int32_t ifInErrors; u_int32_t ifInUnknownProtos; u_int64_t ifOutOctets; u_int32_t ifOutUcastPkts; u_int32_t ifOutMulticastPkts; u_int32_t ifOutBroadcastPkts; u_int32_t ifOutDiscards; u_int32_t ifOutErrors; u_int32_t ifPromiscuousMode; } SFLIf_counters; /* Ethernet interface counters - see RFC 2358 */ typedef struct _SFLEthernet_counters { u_int32_t dot3StatsAlignmentErrors; u_int32_t dot3StatsFCSErrors; u_int32_t dot3StatsSingleCollisionFrames; u_int32_t dot3StatsMultipleCollisionFrames; u_int32_t dot3StatsSQETestErrors; u_int32_t dot3StatsDeferredTransmissions; u_int32_t dot3StatsLateCollisions; u_int32_t dot3StatsExcessiveCollisions; u_int32_t dot3StatsInternalMacTransmitErrors; u_int32_t dot3StatsCarrierSenseErrors; u_int32_t dot3StatsFrameTooLongs; u_int32_t dot3StatsInternalMacReceiveErrors; u_int32_t dot3StatsSymbolErrors; } SFLEthernet_counters; /* Token ring counters - see RFC 1748 */ typedef struct _SFLTokenring_counters { u_int32_t dot5StatsLineErrors; u_int32_t dot5StatsBurstErrors; u_int32_t dot5StatsACErrors; u_int32_t dot5StatsAbortTransErrors; u_int32_t dot5StatsInternalErrors; u_int32_t dot5StatsLostFrameErrors; u_int32_t dot5StatsReceiveCongestions; u_int32_t dot5StatsFrameCopiedErrors; u_int32_t dot5StatsTokenErrors; u_int32_t dot5StatsSoftErrors; u_int32_t dot5StatsHardErrors; u_int32_t dot5StatsSignalLoss; u_int32_t dot5StatsTransmitBeacons; u_int32_t dot5StatsRecoverys; u_int32_t dot5StatsLobeWires; u_int32_t dot5StatsRemoves; u_int32_t dot5StatsSingles; u_int32_t dot5StatsFreqErrors; } SFLTokenring_counters; /* 100 BaseVG interface counters - see RFC 2020 */ typedef struct _SFLVg_counters { u_int32_t dot12InHighPriorityFrames; u_int64_t dot12InHighPriorityOctets; u_int32_t dot12InNormPriorityFrames; u_int64_t dot12InNormPriorityOctets; u_int32_t dot12InIPMErrors; u_int32_t dot12InOversizeFrameErrors; u_int32_t dot12InDataErrors; u_int32_t dot12InNullAddressedFrames; u_int32_t dot12OutHighPriorityFrames; u_int64_t dot12OutHighPriorityOctets; u_int32_t dot12TransitionIntoTrainings; u_int64_t dot12HCInHighPriorityOctets; u_int64_t dot12HCInNormPriorityOctets; u_int64_t dot12HCOutHighPriorityOctets; } SFLVg_counters; typedef struct _SFLVlan_counters { u_int32_t vlan_id; u_int64_t octets; u_int32_t ucastPkts; u_int32_t multicastPkts; u_int32_t broadcastPkts; u_int32_t discards; } SFLVlan_counters; /* Counters data */ enum SFLCounters_type_tag { /* enterprise = 0, format = ... */ SFLCOUNTERS_GENERIC = 1, SFLCOUNTERS_ETHERNET = 2, SFLCOUNTERS_TOKENRING = 3, SFLCOUNTERS_VG = 4, SFLCOUNTERS_VLAN = 5 }; typedef union _SFLCounters_type { SFLIf_counters generic; SFLEthernet_counters ethernet; SFLTokenring_counters tokenring; SFLVg_counters vg; SFLVlan_counters vlan; } SFLCounters_type; typedef struct _SFLCounters_sample_element { struct _SFLCounters_sample_element *nxt; /* linked list */ u_int32_t tag; /* SFLCounters_type_tag */ u_int32_t length; SFLCounters_type counterBlock; } SFLCounters_sample_element; typedef struct _SFLCounters_sample { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 2 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each counters sample generated by this source_id */ u_int32_t source_id; /* fsSourceId */ u_int32_t num_elements; SFLCounters_sample_element *elements; } SFLCounters_sample; /* same thing, but the expanded version, so ds_index can be a full 32 bits */ typedef struct _SFLCounters_sample_expanded { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 2 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each counters sample generated by this source_id */ u_int32_t ds_class; /* EXPANDED */ u_int32_t ds_index; /* EXPANDED */ u_int32_t num_elements; SFLCounters_sample_element *elements; } SFLCounters_sample_expanded; #define SFLADD_ELEMENT(_sm, _el) do { (_el)->nxt = (_sm)->elements; (_sm)->elements = (_el); } while(0) /* Format of a sample datagram */ enum SFLDatagram_version { SFLDATAGRAM_VERSION2 = 2, SFLDATAGRAM_VERSION4 = 4, SFLDATAGRAM_VERSION5 = 5 }; typedef struct _SFLSample_datagram_hdr { u_int32_t datagram_version; /* (enum SFLDatagram_version) = VERSION5 = 5 */ SFLAddress agent_address; /* IP address of sampling agent */ u_int32_t sub_agent_id; /* Used to distinguishing between datagram streams from separate agent sub entities within an device. */ u_int32_t sequence_number; /* Incremented with each sample datagram generated */ u_int32_t uptime; /* Current time (in milliseconds since device last booted). Should be set as close to datagram transmission time as possible.*/ u_int32_t num_records; /* Number of tag-len-val flow/counter records to follow */ } SFLSample_datagram_hdr; #define SFL_MAX_DATAGRAM_SIZE 1500 #define SFL_MIN_DATAGRAM_SIZE 200 #define SFL_DEFAULT_DATAGRAM_SIZE 1400 #define SFL_DATA_PAD 400 #if defined(__cplusplus) } /* extern "C" */ #endif #endif /* SFLOW_H */ pmacct-0.14.0/src/sfprobe_plugin/sflow_api.h0000644000175000017500000002770611423143744020031 0ustar paolopaolo/* Copyright (c) 2002-2006 InMon Corp. Licensed under the terms of the InMon sFlow licence: */ /* http://www.inmon.com/technology/sflowlicense.txt */ ///////////////////////////////////////////////////////////////////////////////// /////////////////////// sFlow Sampling Agent API //////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// #ifndef SFLOW_API_H #define SFLOW_API_H 1 #if defined(__cplusplus) extern "C" { #endif #include "pmacct.h" #include #include #include #include #include "sflow.h" /* uncomment this preprocessor flag (or compile with -DSFL_USE_32BIT_INDEX) if your ds_index numbers can ever be >= 2^30-1 (i.e. >= 0x3FFFFFFF) */ /* #define SFL_USE_32BIT_INDEX */ /* Used to combine ds_class, ds_index and instance into a single 64-bit number like this: __________________________________ | cls| index | instance | ---------------------------------- but now is opened up to a 12-byte struct to ensure that ds_index has a full 32-bit field, and to make accessing the components simpler. The macros have the same behavior as before, so this change should be transparent. The only difference is that these objects are now passed around by reference instead of by value, and the comparison is done using a fn. */ typedef struct _SFLDataSource_instance { u_int32_t ds_class; u_int32_t ds_index; u_int32_t ds_instance; } SFLDataSource_instance; #ifdef SFL_USE_32BIT_INDEX #define SFL_FLOW_SAMPLE_TYPE SFLFlow_sample_expanded #define SFL_COUNTERS_SAMPLE_TYPE SFLCounters_sample_expanded #else #define SFL_FLOW_SAMPLE_TYPE SFLFlow_sample #define SFL_COUNTERS_SAMPLE_TYPE SFLCounters_sample /* if index numbers are not going to use all 32 bits, then we can use the more compact encoding, with the dataSource class and index merged */ #define SFL_DS_DATASOURCE(dsi) (((dsi).ds_class << 24) + (dsi).ds_index) #endif #define SFL_DS_INSTANCE(dsi) (dsi).ds_instance #define SFL_DS_CLASS(dsi) (dsi).ds_class #define SFL_DS_INDEX(dsi) (dsi).ds_index #define SFL_DS_SET(dsi,clss,indx,inst) \ do { \ (dsi).ds_class = (clss); \ (dsi).ds_index = (indx); \ (dsi).ds_instance = (inst); \ } while(0) typedef struct _SFLSampleCollector { u_int32_t data[(SFL_MAX_DATAGRAM_SIZE + SFL_DATA_PAD) / sizeof(u_int32_t)]; u_int32_t *datap; /* packet fill pointer */ u_int32_t pktlen; /* accumulated size */ u_int32_t packetSeqNo; u_int32_t numSamples; } SFLSampleCollector; struct _SFLAgent; /* forward decl */ typedef struct _SFLReceiver { struct _SFLReceiver *nxt; /* MIB fields */ char *sFlowRcvrOwner; time_t sFlowRcvrTimeout; u_int32_t sFlowRcvrMaximumDatagramSize; SFLAddress sFlowRcvrAddress; u_int32_t sFlowRcvrPort; u_int32_t sFlowRcvrDatagramVersion; /* public fields */ struct _SFLAgent *agent; /* pointer to my agent */ /* private fields */ SFLSampleCollector sampleCollector; struct sockaddr_in receiver; } SFLReceiver; typedef struct _SFLSampler { /* for linked list */ struct _SFLSampler *nxt; /* for hash lookup table */ struct _SFLSampler *hash_nxt; /* MIB fields */ SFLDataSource_instance dsi; u_int32_t sFlowFsReceiver; u_int32_t sFlowFsPacketSamplingRate; u_int32_t sFlowFsMaximumHeaderSize; /* public fields */ struct _SFLAgent *agent; /* pointer to my agent */ /* private fields */ SFLReceiver *myReceiver; u_int32_t skip; u_int32_t samplePool; u_int32_t flowSampleSeqNo; /* rate checking */ u_int32_t samplesThisTick; u_int32_t samplesLastTick; u_int32_t backoffThreshold; } SFLSampler; /* declare */ struct _SFLPoller; typedef void (*getCountersFn_t)(void *magic, /* callback to get counters */ struct _SFLPoller *sampler, /* called with self */ SFL_COUNTERS_SAMPLE_TYPE *cs); /* struct to fill in */ typedef struct _SFLPoller { /* for linked list */ struct _SFLPoller *nxt; /* MIB fields */ SFLDataSource_instance dsi; u_int32_t sFlowCpReceiver; time_t sFlowCpInterval; /* public fields */ struct _SFLAgent *agent; /* pointer to my agent */ void *magic; /* ptr to pass back in getCountersFn() */ getCountersFn_t getCountersFn; /* private fields */ SFLReceiver *myReceiver; time_t countersCountdown; u_int32_t countersSampleSeqNo; } SFLPoller; typedef void *(*allocFn_t)(void *magic, /* callback to allocate space on heap */ struct _SFLAgent *agent, /* called with self */ size_t bytes); /* bytes requested */ typedef int (*freeFn_t)(void *magic, /* callback to free space on heap */ struct _SFLAgent *agent, /* called with self */ void *obj); /* obj to free */ typedef void (*errorFn_t)(void *magic, /* callback to log error message */ struct _SFLAgent *agent, /* called with self */ char *msg); /* error message */ typedef void (*sendFn_t)(void *magic, /* optional override fn to send packet */ struct _SFLAgent *agent, SFLReceiver *receiver, u_char *pkt, u_int32_t pktLen); /* prime numbers are good for hash tables */ #define SFL_HASHTABLE_SIZ 199 typedef struct _SFLAgent { SFLSampler *jumpTable[SFL_HASHTABLE_SIZ]; /* fast lookup table for samplers (by ifIndex) */ SFLSampler *samplers; /* the list of samplers */ SFLPoller *pollers; /* the list of samplers */ SFLReceiver *receivers; /* the array of receivers */ time_t bootTime; /* time when we booted or started */ time_t now; /* time now */ SFLAddress myIP; /* IP address of this node */ u_int32_t subId; /* sub_agent_id */ void *magic; /* ptr to pass back in logging and alloc fns */ allocFn_t allocFn; freeFn_t freeFn; errorFn_t errorFn; sendFn_t sendFn; int receiverSocket; } SFLAgent; /* call this at the start with a newly created agent */ void sfl_agent_init(SFLAgent *agent, SFLAddress *myIP, /* IP address of this agent */ u_int32_t subId, /* agent_sub_id */ time_t bootTime, /* agent boot time */ time_t now, /* time now */ void *magic, /* ptr to pass back in logging and alloc fns */ allocFn_t allocFn, freeFn_t freeFn, errorFn_t errorFn, sendFn_t sendFn); /* call this to create samplers */ SFLSampler *sfl_agent_addSampler(SFLAgent *agent, SFLDataSource_instance *pdsi); /* call this to create pollers */ SFLPoller *sfl_agent_addPoller(SFLAgent *agent, SFLDataSource_instance *pdsi, void *magic, /* ptr to pass back in getCountersFn() */ getCountersFn_t getCountersFn); /* call this to create receivers */ SFLReceiver *sfl_agent_addReceiver(SFLAgent *agent); /* call this to remove samplers */ int sfl_agent_removeSampler(SFLAgent *agent, SFLDataSource_instance *pdsi); /* call this to remove pollers */ int sfl_agent_removePoller(SFLAgent *agent, SFLDataSource_instance *pdsi); /* note: receivers should not be removed. Typically the receivers list will be created at init time and never changed */ /* call these fns to retrieve sampler, poller or receiver (e.g. for SNMP GET or GETNEXT operation) */ SFLSampler *sfl_agent_getSampler(SFLAgent *agent, SFLDataSource_instance *pdsi); SFLSampler *sfl_agent_getNextSampler(SFLAgent *agent, SFLDataSource_instance *pdsi); SFLPoller *sfl_agent_getPoller(SFLAgent *agent, SFLDataSource_instance *pdsi); SFLPoller *sfl_agent_getNextPoller(SFLAgent *agent, SFLDataSource_instance *pdsi); SFLReceiver *sfl_agent_getReceiver(SFLAgent *agent, u_int32_t receiverIndex); SFLReceiver *sfl_agent_getNextReceiver(SFLAgent *agent, u_int32_t receiverIndex); /* jump table access - for performance */ SFLSampler *sfl_agent_getSamplerByIfIndex(SFLAgent *agent, u_int32_t ifIndex); /* call these functions to GET and SET MIB values */ /* receiver */ char * sfl_receiver_get_sFlowRcvrOwner(SFLReceiver *receiver); void sfl_receiver_set_sFlowRcvrOwner(SFLReceiver *receiver, char *sFlowRcvrOwner); time_t sfl_receiver_get_sFlowRcvrTimeout(SFLReceiver *receiver); void sfl_receiver_set_sFlowRcvrTimeout(SFLReceiver *receiver, time_t sFlowRcvrTimeout); u_int32_t sfl_receiver_get_sFlowRcvrMaximumDatagramSize(SFLReceiver *receiver); void sfl_receiver_set_sFlowRcvrMaximumDatagramSize(SFLReceiver *receiver, u_int32_t sFlowRcvrMaximumDatagramSize); SFLAddress *sfl_receiver_get_sFlowRcvrAddress(SFLReceiver *receiver); void sfl_receiver_set_sFlowRcvrAddress(SFLReceiver *receiver, SFLAddress *sFlowRcvrAddress); u_int32_t sfl_receiver_get_sFlowRcvrPort(SFLReceiver *receiver); void sfl_receiver_set_sFlowRcvrPort(SFLReceiver *receiver, u_int32_t sFlowRcvrPort); /* sampler */ u_int32_t sfl_sampler_get_sFlowFsReceiver(SFLSampler *sampler); void sfl_sampler_set_sFlowFsReceiver(SFLSampler *sampler, u_int32_t sFlowFsReceiver); u_int32_t sfl_sampler_get_sFlowFsPacketSamplingRate(SFLSampler *sampler); void sfl_sampler_set_sFlowFsPacketSamplingRate(SFLSampler *sampler, u_int32_t sFlowFsPacketSamplingRate); u_int32_t sfl_sampler_get_sFlowFsMaximumHeaderSize(SFLSampler *sampler); void sfl_sampler_set_sFlowFsMaximumHeaderSize(SFLSampler *sampler, u_int32_t sFlowFsMaximumHeaderSize); /* poller */ u_int32_t sfl_poller_get_sFlowCpReceiver(SFLPoller *poller); void sfl_poller_set_sFlowCpReceiver(SFLPoller *poller, u_int32_t sFlowCpReceiver); u_int32_t sfl_poller_get_sFlowCpInterval(SFLPoller *poller); void sfl_poller_set_sFlowCpInterval(SFLPoller *poller, u_int32_t sFlowCpInterval); /* call this to indicate a discontinuity with a counter like samplePool so that the sflow collector will ignore the next delta */ void sfl_sampler_resetFlowSeqNo(SFLSampler *sampler); /* call this to indicate a discontinuity with one or more of the counters so that the sflow collector will ignore the next delta */ void sfl_poller_resetCountersSeqNo(SFLPoller *poller); /* software sampling: call this with every packet - returns non-zero if the packet should be sampled (in which case you then call sfl_sampler_writeFlowSample()) */ int sfl_sampler_takeSample(SFLSampler *sampler); /* call this to set a maximum samples-per-second threshold. If the sampler reaches this threshold it will automatically back off the sampling rate. A value of 0 disables the mechanism */ void sfl_sampler_set_backoffThreshold(SFLSampler *sampler, u_int32_t samplesPerSecond); u_int32_t sfl_sampler_get_backoffThreshold(SFLSampler *sampler); /* call this once per second (N.B. not on interrupt stack i.e. not hard real-time) */ void sfl_agent_tick(SFLAgent *agent, time_t now); /* call this with each flow sample */ void sfl_sampler_writeFlowSample(SFLSampler *sampler, SFL_FLOW_SAMPLE_TYPE *fs); /* call this to push counters samples (usually done in the getCountersFn callback) */ void sfl_poller_writeCountersSample(SFLPoller *poller, SFL_COUNTERS_SAMPLE_TYPE *cs); /* call this to deallocate resources */ void sfl_agent_release(SFLAgent *agent); /* internal fns */ void sfl_receiver_init(SFLReceiver *receiver, SFLAgent *agent); void sfl_sampler_init(SFLSampler *sampler, SFLAgent *agent, SFLDataSource_instance *pdsi); void sfl_poller_init(SFLPoller *poller, SFLAgent *agent, SFLDataSource_instance *pdsi, void *magic, getCountersFn_t getCountersFn); void sfl_receiver_tick(SFLReceiver *receiver, time_t now); void sfl_poller_tick(SFLPoller *poller, time_t now); void sfl_sampler_tick(SFLSampler *sampler, time_t now); int sfl_receiver_writeFlowSample(SFLReceiver *receiver, SFL_FLOW_SAMPLE_TYPE *fs); int sfl_receiver_writeCountersSample(SFLReceiver *receiver, SFL_COUNTERS_SAMPLE_TYPE *cs); void sfl_agent_resetReceiver(SFLAgent *agent, SFLReceiver *receiver); void sfl_agent_error(SFLAgent *agent, char *modName, char *msg); void sfl_agent_sysError(SFLAgent *agent, char *modName, char *msg); u_int32_t sfl_receiver_samplePacketsSent(SFLReceiver *receiver); #define SFL_ALLOC malloc #define SFL_FREE free #if defined(__cplusplus) } /* extern "C" */ #endif #endif /* SFLOW_API_H */ pmacct-0.14.0/src/sfprobe_plugin/sflow_agent.c0000644000175000017500000004042011403735575020344 0ustar paolopaolo/* Copyright (c) 2002-2006 InMon Corp. Licensed under the terms of the InMon sFlow licence: */ /* http://www.inmon.com/technology/sflowlicense.txt */ #include #include #include #include #include #include #include "sflow_api.h" static void * sflAlloc(SFLAgent *agent, size_t bytes); static void sflFree(SFLAgent *agent, void *obj); static void sfl_agent_jumpTableAdd(SFLAgent *agent, SFLSampler *sampler); static void sfl_agent_jumpTableRemove(SFLAgent *agent, SFLSampler *sampler); /*________________--------------------------__________________ ________________ sfl_agent_init __________________ ----------------__________________________------------------ */ void sfl_agent_init(SFLAgent *agent, SFLAddress *myIP, /* IP address of this agent in net byte order */ u_int32_t subId, /* agent_sub_id */ time_t bootTime, /* agent boot time */ time_t now, /* time now */ void *magic, /* ptr to pass back in logging and alloc fns */ allocFn_t allocFn, freeFn_t freeFn, errorFn_t errorFn, sendFn_t sendFn) { /* first clear everything */ memset(agent, 0, sizeof(*agent)); /* now copy in the parameters */ agent->myIP = *myIP; /* structure copy */ agent->subId = subId; agent->bootTime = bootTime; agent->now = now; agent->magic = magic; agent->allocFn = allocFn; agent->freeFn = freeFn; agent->errorFn = errorFn; agent->sendFn = sendFn; if(sendFn == NULL) { /* open the socket */ if((agent->receiverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) sfl_agent_sysError(agent, "agent", "socket open failed"); } if (config.nfprobe_ipprec) { int opt = config.nfprobe_ipprec << 5; int rc; rc = setsockopt(agent->receiverSocket, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)); if (rc < 0) Log(LOG_WARNING, "WARN ( %s/%s ): setsockopt() failed for IP_TOS: %s\n", config.name, config.type, strerror(errno)); } if (config.pipe_size) { int rc; rc = Setsocksize(agent->receiverSocket, SOL_SOCKET, SO_SNDBUF, &config.pipe_size, sizeof(config.pipe_size)); if (rc < 0) Log(LOG_WARNING, "WARN ( %s/%s ): setsockopt() failed for SOL_SNDBUF: %s\n", config.name, config.type, strerror(errno)); } } /*_________________---------------------------__________________ _________________ sfl_agent_release __________________ -----------------___________________________------------------ */ void sfl_agent_release(SFLAgent *agent) { SFLSampler *sm; SFLPoller *pl; SFLReceiver *rcv; /* release and free the samplers */ for(sm = agent->samplers; sm != NULL; ) { SFLSampler *nextSm = sm->nxt; sflFree(agent, sm); sm = nextSm; } agent->samplers = NULL; /* release and free the pollers */ for(pl = agent->pollers; pl != NULL; ) { SFLPoller *nextPl = pl->nxt; sflFree(agent, pl); pl = nextPl; } agent->pollers = NULL; /* release and free the receivers */ for(rcv = agent->receivers; rcv != NULL; ) { SFLReceiver *nextRcv = rcv->nxt; sflFree(agent, rcv); rcv = nextRcv; } agent->receivers = NULL; /* close the socket */ if(agent->receiverSocket > 0) close(agent->receiverSocket); } /*_________________---------------------------__________________ _________________ sfl_agent_tick __________________ -----------------___________________________------------------ */ void sfl_agent_tick(SFLAgent *agent, time_t now) { SFLReceiver *rcv; SFLSampler *sm; SFLPoller *pl; agent->now = now; /* receivers use ticks to flush send data */ for(rcv = agent->receivers; rcv != NULL; rcv = rcv->nxt) sfl_receiver_tick(rcv, now); /* samplers use ticks to decide when they are sampling too fast */ for(sm = agent->samplers; sm != NULL; sm = sm->nxt) sfl_sampler_tick(sm, now); /* pollers use ticks to decide when to ask for counters */ for(pl = agent->pollers; pl != NULL; pl = pl->nxt) sfl_poller_tick(pl, now); } /*_________________---------------------------__________________ _________________ sfl_agent_addReceiver __________________ -----------------___________________________------------------ */ SFLReceiver *sfl_agent_addReceiver(SFLAgent *agent) { SFLReceiver *rcv = (SFLReceiver *)sflAlloc(agent, sizeof(SFLReceiver)); SFLReceiver *r, *prev = NULL; sfl_receiver_init(rcv, agent); // add to end of list - to preserve the receiver index numbers for existing receivers for(r = agent->receivers; r != NULL; prev = r, r = r->nxt); if(prev) prev->nxt = rcv; else agent->receivers = rcv; rcv->nxt = NULL; return rcv; } /*_________________---------------------------__________________ _________________ sfl_dsi_compare __________________ -----------------___________________________------------------ Note that if there is a mixture of ds_classes for this agent, then the simple numeric comparison may not be correct - the sort order (for the purposes of the SNMP MIB) should really be determined by the OID that these numeric ds_class numbers are a shorthand for. For example, ds_class == 0 means ifIndex, which is the oid "1.3.6.1.2.1.2.2.1" */ static inline int sfl_dsi_compare(SFLDataSource_instance *pdsi1, SFLDataSource_instance *pdsi2) { // could have used just memcmp(), but not sure if that would // give the right answer on little-endian platforms. Safer to be explicit... int cmp = pdsi2->ds_class - pdsi1->ds_class; if(cmp == 0) cmp = pdsi2->ds_index - pdsi1->ds_index; if(cmp == 0) cmp = pdsi2->ds_instance - pdsi1->ds_instance; return cmp; } /*_________________---------------------------__________________ _________________ sfl_agent_addSampler __________________ -----------------___________________________------------------ */ SFLSampler *sfl_agent_addSampler(SFLAgent *agent, SFLDataSource_instance *pdsi) { SFLSampler *newsm; // keep the list sorted SFLSampler *prev = NULL, *sm = agent->samplers; for(; sm != NULL; prev = sm, sm = sm->nxt) { int64_t cmp = sfl_dsi_compare(pdsi, &sm->dsi); if(cmp == 0) return sm; // found - return existing one if(cmp < 0) break; // insert here } // either we found the insert point, or reached the end of the list... newsm = (SFLSampler *)sflAlloc(agent, sizeof(SFLSampler)); sfl_sampler_init(newsm, agent, pdsi); if(prev) prev->nxt = newsm; else agent->samplers = newsm; newsm->nxt = sm; // see if we should go in the ifIndex jumpTable if(SFL_DS_CLASS(newsm->dsi) == 0) { SFLSampler *test = sfl_agent_getSamplerByIfIndex(agent, SFL_DS_INDEX(newsm->dsi)); if(test && (SFL_DS_INSTANCE(newsm->dsi) < SFL_DS_INSTANCE(test->dsi))) { // replace with this new one because it has a lower ds_instance number sfl_agent_jumpTableRemove(agent, test); test = NULL; } if(test == NULL) sfl_agent_jumpTableAdd(agent, newsm); } return newsm; } /*_________________---------------------------__________________ _________________ sfl_agent_addPoller __________________ -----------------___________________________------------------ */ SFLPoller *sfl_agent_addPoller(SFLAgent *agent, SFLDataSource_instance *pdsi, void *magic, /* ptr to pass back in getCountersFn() */ getCountersFn_t getCountersFn) { int64_t cmp; SFLPoller *newpl; // keep the list sorted SFLPoller *prev = NULL, *pl = agent->pollers; for(; pl != NULL; prev = pl, pl = pl->nxt) { cmp = sfl_dsi_compare(pdsi, &pl->dsi); if(cmp == 0) return pl; // found - return existing one if(cmp < 0) break; // insert here } // either we found the insert point, or reached the end of the list... newpl = (SFLPoller *)sflAlloc(agent, sizeof(SFLPoller)); sfl_poller_init(newpl, agent, pdsi, magic, getCountersFn); if(prev) prev->nxt = newpl; else agent->pollers = newpl; newpl->nxt = pl; return newpl; } /*_________________---------------------------__________________ _________________ sfl_agent_removeSampler __________________ -----------------___________________________------------------ */ int sfl_agent_removeSampler(SFLAgent *agent, SFLDataSource_instance *pdsi) { SFLSampler *prev, *sm; /* find it, unlink it and free it */ for(prev = NULL, sm = agent->samplers; sm != NULL; prev = sm, sm = sm->nxt) { if(sfl_dsi_compare(pdsi, &sm->dsi) == 0) { if(prev == NULL) agent->samplers = sm->nxt; else prev->nxt = sm->nxt; sfl_agent_jumpTableRemove(agent, sm); sflFree(agent, sm); return 1; } } /* not found */ return 0; } /*_________________---------------------------__________________ _________________ sfl_agent_removePoller __________________ -----------------___________________________------------------ */ int sfl_agent_removePoller(SFLAgent *agent, SFLDataSource_instance *pdsi) { SFLPoller *prev, *pl; /* find it, unlink it and free it */ for(prev = NULL, pl = agent->pollers; pl != NULL; prev = pl, pl = pl->nxt) { if(sfl_dsi_compare(pdsi, &pl->dsi) == 0) { if(prev == NULL) agent->pollers = pl->nxt; else prev->nxt = pl->nxt; sflFree(agent, pl); return 1; } } /* not found */ return 0; } /*_________________--------------------------------__________________ _________________ sfl_agent_jumpTableAdd __________________ -----------------________________________________------------------ */ static void sfl_agent_jumpTableAdd(SFLAgent *agent, SFLSampler *sampler) { u_int32_t hashIndex = SFL_DS_INDEX(sampler->dsi) % SFL_HASHTABLE_SIZ; sampler->hash_nxt = agent->jumpTable[hashIndex]; agent->jumpTable[hashIndex] = sampler; } /*_________________--------------------------------__________________ _________________ sfl_agent_jumpTableRemove __________________ -----------------________________________________------------------ */ static void sfl_agent_jumpTableRemove(SFLAgent *agent, SFLSampler *sampler) { u_int32_t hashIndex = SFL_DS_INDEX(sampler->dsi) % SFL_HASHTABLE_SIZ; SFLSampler *search = agent->jumpTable[hashIndex], *prev = NULL; for( ; search != NULL; prev = search, search = search->hash_nxt) if(search == sampler) break; if(search) { // found - unlink if(prev) prev->hash_nxt = search->hash_nxt; else agent->jumpTable[hashIndex] = search->hash_nxt; search->hash_nxt = NULL; } } /*_________________--------------------------------__________________ _________________ sfl_agent_getSamplerByIfIndex __________________ -----------------________________________________------------------ fast lookup (pointers cached in hash table). If there are multiple sampler instances for a given ifIndex, then this fn will return the one with the lowest instance number. Since the samplers list is sorted, this means the other instances will be accesible by following the sampler->nxt pointer (until the ds_class or ds_index changes). This is helpful if you need to offer the same flowSample to multiple samplers. */ SFLSampler *sfl_agent_getSamplerByIfIndex(SFLAgent *agent, u_int32_t ifIndex) { SFLSampler *search = agent->jumpTable[ifIndex % SFL_HASHTABLE_SIZ]; for( ; search != NULL; search = search->hash_nxt) if(SFL_DS_INDEX(search->dsi) == ifIndex) break; return search; } /*_________________---------------------------__________________ _________________ sfl_agent_getSampler __________________ -----------------___________________________------------------ */ SFLSampler *sfl_agent_getSampler(SFLAgent *agent, SFLDataSource_instance *pdsi) { SFLSampler *sm; /* find it and return it */ for(sm = agent->samplers; sm != NULL; sm = sm->nxt) if(sfl_dsi_compare(pdsi, &sm->dsi) == 0) return sm; /* not found */ return NULL; } /*_________________---------------------------__________________ _________________ sfl_agent_getPoller __________________ -----------------___________________________------------------ */ SFLPoller *sfl_agent_getPoller(SFLAgent *agent, SFLDataSource_instance *pdsi) { SFLPoller *pl; /* find it and return it */ for(pl = agent->pollers; pl != NULL; pl = pl->nxt) if(sfl_dsi_compare(pdsi, &pl->dsi) == 0) return pl; /* not found */ return NULL; } /*_________________---------------------------__________________ _________________ sfl_agent_getReceiver __________________ -----------------___________________________------------------ */ SFLReceiver *sfl_agent_getReceiver(SFLAgent *agent, u_int32_t receiverIndex) { SFLReceiver *rcv; u_int32_t rcvIdx = 0; for(rcv = agent->receivers; rcv != NULL; rcv = rcv->nxt) if(receiverIndex == ++rcvIdx) return rcv; /* not found - ran off the end of the table */ return NULL; } /*_________________---------------------------__________________ _________________ sfl_agent_getNextSampler __________________ -----------------___________________________------------------ */ SFLSampler *sfl_agent_getNextSampler(SFLAgent *agent, SFLDataSource_instance *pdsi) { /* return the one lexograpically just after it - assume they are sorted correctly according to the lexographical ordering of the object ids */ SFLSampler *sm = sfl_agent_getSampler(agent, pdsi); return sm ? sm->nxt : NULL; } /*_________________---------------------------__________________ _________________ sfl_agent_getNextPoller __________________ -----------------___________________________------------------ */ SFLPoller *sfl_agent_getNextPoller(SFLAgent *agent, SFLDataSource_instance *pdsi) { /* return the one lexograpically just after it - assume they are sorted correctly according to the lexographical ordering of the object ids */ SFLPoller *pl = sfl_agent_getPoller(agent, pdsi); return pl ? pl->nxt : NULL; } /*_________________---------------------------__________________ _________________ sfl_agent_getNextReceiver __________________ -----------------___________________________------------------ */ SFLReceiver *sfl_agent_getNextReceiver(SFLAgent *agent, u_int32_t receiverIndex) { return sfl_agent_getReceiver(agent, receiverIndex + 1); } /*_________________---------------------------__________________ _________________ sfl_agent_resetReceiver __________________ -----------------___________________________------------------ */ void sfl_agent_resetReceiver(SFLAgent *agent, SFLReceiver *receiver) { SFLReceiver *rcv; SFLSampler *sm; SFLPoller *pl; /* tell samplers and pollers to stop sending to this receiver */ /* first get his receiverIndex */ u_int32_t rcvIdx = 0; for(rcv = agent->receivers; rcv != NULL; rcv = rcv->nxt) if(rcv == receiver) break; /* now tell anyone that is using it to stop */ for(sm = agent->samplers; sm != NULL; sm = sm->nxt) if(sfl_sampler_get_sFlowFsReceiver(sm) == rcvIdx) sfl_sampler_set_sFlowFsReceiver(sm, 0); for(pl = agent->pollers; pl != NULL; pl = pl->nxt) if(sfl_poller_get_sFlowCpReceiver(pl) == rcvIdx) sfl_poller_set_sFlowCpReceiver(pl, 0); } /*_________________---------------------------__________________ _________________ sfl_agent_error __________________ -----------------___________________________------------------ */ #define MAX_ERRMSG_LEN 1000 void sfl_agent_error(SFLAgent *agent, char *modName, char *msg) { char errm[MAX_ERRMSG_LEN]; sprintf(errm, "sfl_agent_error: %s: %s\n", modName, msg); if(agent->errorFn) (*agent->errorFn)(agent->magic, agent, errm); else Log(LOG_ERR, "ERROR ( %s/%s ): %s\n", config.name, config.type, errm); } /*_________________---------------------------__________________ _________________ sfl_agent_sysError __________________ -----------------___________________________------------------ */ void sfl_agent_sysError(SFLAgent *agent, char *modName, char *msg) { char errm[MAX_ERRMSG_LEN]; sprintf(errm, "sfl_agent_sysError: %s: %s (errno = %d - %s)\n", modName, msg, errno, strerror(errno)); if(agent->errorFn) (*agent->errorFn)(agent->magic, agent, errm); else Log(LOG_ERR, "ERROR ( %s/%s ): %s\n", config.name, config.type, errm); } /*_________________---------------------------__________________ _________________ alloc and free __________________ -----------------___________________________------------------ */ static void * sflAlloc(SFLAgent *agent, size_t bytes) { if(agent->allocFn) return (*agent->allocFn)(agent->magic, agent, bytes); else return SFL_ALLOC(bytes); } static void sflFree(SFLAgent *agent, void *obj) { if(agent->freeFn) (*agent->freeFn)(agent->magic, agent, obj); else SFL_FREE(obj); } pmacct-0.14.0/src/classifier.h0000644000175000017500000000504511460653755015155 0ustar paolopaolo#include #include "regexp.h" #include "conntrack.h" /* defines */ #define MAX_FN_LEN 256 #define MAX_SUBDIRS 128 #define MAX_CLASSIFIERS 256 #define MAX_PATTERN_LEN 2048 #define DEFAULT_TENTATIVES 5 /* data structures */ struct pkt_classifier_data { struct timeval stamp; u_char *packet_ptr; u_char *l3_ptr; u_char *l4_ptr; u_char *payload_ptr; u_int16_t l3_proto; u_int16_t l4_proto; u_int16_t plen; u_int8_t tentatives; u_int16_t sampling_rate; }; struct pkt_classifier { pm_class_t id; char protocol[MAX_PROTOCOL_LEN]; regexp *pattern; pm_class_t (*func)(struct pkt_classifier_data *, int, void **, void **, void **); conntrack_helper ct_helper; void *extra; }; /* All but __CLASSIFIER_C are dummy entries. They are required to export locally the 'class' array. This is in order to avoid to link extra C files into nfacctd, sfacctd, pmmyplay and pmpgplay. */ #if defined __CLASSIFIER_C || defined __PMACCT_PLAYER_C || defined __NFACCTD_C || defined __SFACCTD_C #define EXT #else #define EXT extern #endif /* prototypes */ EXT void init_classifiers(char *); EXT void evaluate_classifiers(struct packet_ptrs *, struct ip_flow_common *, unsigned int); EXT pm_class_t SF_evaluate_classifiers(char *); EXT int parse_pattern_file(char *, struct pkt_classifier *); EXT int parse_shared_object(char *, struct pkt_classifier *); EXT int dot_pat(char *); EXT int dot_so(char *); EXT void init_class_accumulators(struct packet_ptrs *, struct ip_flow_common *, unsigned int); EXT void handle_class_accumulators(struct packet_ptrs *, struct ip_flow_common *, unsigned int); EXT int pm_scandir(const char *, struct dirent ***, int (*select)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)); EXT int pm_alphasort(const struct dirent **, const struct dirent **); EXT void link_conntrack_helper(struct pkt_classifier *); EXT void *search_context_chain(struct ip_flow_common *, unsigned int, char *); EXT void insert_context_chain(struct ip_flow_common *, unsigned int, char *, void *); EXT void clear_context_chain(struct ip_flow_common *, unsigned int); EXT void prepare_classifier_data(struct pkt_classifier_data *, struct ip_flow_common *, unsigned int, struct packet_ptrs *); EXT pm_class_t pmct_register(struct pkt_classifier *); EXT void pmct_unregister(pm_class_t); EXT pm_class_t pmct_find_first_free(); EXT pm_class_t pmct_find_last_free(); EXT int pmct_isfree(pm_class_t); EXT int pmct_get(pm_class_t, struct pkt_classifier *); EXT int pmct_get_num_entries(); EXT struct pkt_classifier *class; #undef EXT pmacct-0.14.0/src/sflow.h0000644000175000017500000005164311714307357014164 0ustar paolopaolo/* Copyright (C) InMon Corporation 2002-2003 ALL RIGHTS RESERVED */ /* $Header: /home/repo-0.14/pmacct/src/sflow.h,v 1.2 2012/02/07 20:51:59 paolo Exp $ */ ///////////////////////////////////////////////////////////////////////////////// /////////////////////// sFlow Sampling Packet Data Types //////////////////////// ///////////////////////////////////////////////////////////////////////////////// enum SFLAddress_type { SFLADDRESSTYPE_IP_V4 = 1, SFLADDRESSTYPE_IP_V6 = 2 }; typedef union _SFLAddress_value { struct in_addr ip_v4; #if defined ENABLE_IPV6 struct in6_addr ip_v6; #endif } SFLAddress_value; typedef struct _SFLAddress { u_int32_t type; /* enum SFLAddress_type */ SFLAddress_value address; } SFLAddress; /* Packet header data */ #define SFL_DEFAULT_HEADER_SIZE 128 #define SFL_DEFAULT_COLLECTOR_PORT 6343 #define SFL_DEFAULT_SAMPLING_RATE 400 /* The header protocol describes the format of the sampled header */ enum SFLHeader_protocol { SFLHEADER_ETHERNET_ISO8023 = 1, SFLHEADER_ISO88024_TOKENBUS = 2, SFLHEADER_ISO88025_TOKENRING = 3, SFLHEADER_FDDI = 4, SFLHEADER_FRAME_RELAY = 5, SFLHEADER_X25 = 6, SFLHEADER_PPP = 7, SFLHEADER_SMDS = 8, SFLHEADER_AAL5 = 9, SFLHEADER_AAL5_IP = 10, /* e.g. Cisco AAL5 mux */ SFLHEADER_IPv4 = 11, SFLHEADER_IPv6 = 12, SFLHEADER_MPLS = 13 }; /* raw sampled header */ typedef struct _SFLSampled_header { u_int32_t header_protocol; /* (enum SFLHeader_protocol) */ u_int32_t frame_length; /* Original length of packet before sampling */ u_int32_t stripped; /* header/trailer bytes stripped by sender */ u_int32_t header_length; /* length of sampled header bytes to follow */ u_int8_t *header_bytes; /* Header bytes */ } SFLSampled_header; /* decoded ethernet header */ typedef struct _SFLSampled_ethernet { u_int32_t eth_len; /* The length of the MAC packet excluding lower layer encapsulations */ u_int8_t src_mac[8]; /* 6 bytes + 2 pad */ u_int8_t dst_mac[8]; u_int32_t eth_type; } SFLSampled_ethernet; /* decoded IP version 4 header */ typedef struct _SFLSampled_ipv4 { u_int32_t length; /* The length of the IP packet excluding lower layer encapsulations */ u_int32_t protocol; /* IP Protocol type (for example, TCP = 6, UDP = 17) */ struct in_addr src_ip; /* Source IP Address */ struct in_addr dst_ip; /* Destination IP Address */ u_int32_t src_port; /* TCP/UDP source port number or equivalent */ u_int32_t dst_port; /* TCP/UDP destination port number or equivalent */ u_int32_t tcp_flags; /* TCP flags */ u_int32_t tos; /* IP type of service */ } SFLSampled_ipv4; /* decoded IP version 6 data */ typedef struct _SFLSampled_ipv6 { u_int32_t length; /* The length of the IP packet excluding lower layer encapsulations */ u_int32_t protocol; /* IP Protocol type (for example, TCP = 6, UDP = 17) */ #if defined ENABLE_IPV6 struct in6_addr src_ip; /* Source IP Address */ struct in6_addr dst_ip; /* Destination IP Address */ #else u_int8_t src_ip[16]; u_int8_t dst_ip[16]; #endif u_int32_t src_port; /* TCP/UDP source port number or equivalent */ u_int32_t dst_port; /* TCP/UDP destination port number or equivalent */ u_int32_t tcp_flags; /* TCP flags */ u_int32_t priority; /* IP priority */ } SFLSampled_ipv6; /* Extended data types */ /* Extended switch data */ typedef struct _SFLExtended_switch { u_int32_t src_vlan; /* The 802.1Q VLAN id of incomming frame */ u_int32_t src_priority; /* The 802.1p priority */ u_int32_t dst_vlan; /* The 802.1Q VLAN id of outgoing frame */ u_int32_t dst_priority; /* The 802.1p priority */ } SFLExtended_switch; /* Extended router data */ typedef struct _SFLExtended_router { SFLAddress nexthop; /* IP address of next hop router */ u_int32_t src_mask; /* Source address prefix mask bits */ u_int32_t dst_mask; /* Destination address prefix mask bits */ } SFLExtended_router; /* Extended gateway data */ enum SFLExtended_as_path_segment_type { SFLEXTENDED_AS_SET = 1, /* Unordered set of ASs */ SFLEXTENDED_AS_SEQUENCE = 2 /* Ordered sequence of ASs */ }; typedef struct _SFLExtended_as_path_segment { u_int32_t type; /* enum SFLExtended_as_path_segment_type */ u_int32_t length; /* number of AS numbers in set/sequence */ union { u_int32_t *set; u_int32_t *seq; } as; } SFLExtended_as_path_segment; typedef struct _SFLExtended_gateway { SFLAddress nexthop; /* Address of the border router that should be used for the destination network */ u_int32_t as; /* AS number for this gateway */ u_int32_t src_as; /* AS number of source (origin) */ u_int32_t src_peer_as; /* AS number of source peer */ u_int32_t dst_as_path_segments; /* number of segments in path */ SFLExtended_as_path_segment *dst_as_path; /* list of seqs or sets */ u_int32_t communities_length; /* number of communities */ u_int32_t *communities; /* set of communities */ u_int32_t localpref; /* LocalPref associated with this route */ } SFLExtended_gateway; typedef struct _SFLString { u_int32_t len; char *str; } SFLString; /* Extended user data */ typedef struct _SFLExtended_user { u_int32_t src_charset; /* MIBEnum value of character set used to encode a string - See RFC 2978 Where possible UTF-8 encoding (MIBEnum=106) should be used. A value of zero indicates an unknown encoding. */ SFLString src_user; u_int32_t dst_charset; SFLString dst_user; } SFLExtended_user; /* Extended URL data */ enum SFLExtended_url_direction { SFLEXTENDED_URL_SRC = 1, /* URL is associated with source address */ SFLEXTENDED_URL_DST = 2 /* URL is associated with destination address */ }; typedef struct _SFLExtended_url { u_int32_t direction; /* enum SFLExtended_url_direction */ SFLString url; /* URL associated with the packet flow. Must be URL encoded */ SFLString host; /* The host field from the HTTP header */ } SFLExtended_url; /* Extended MPLS data */ typedef struct _SFLLabelStack { u_int32_t depth; u_int32_t *stack; /* first entry is top of stack - see RFC 3032 for encoding */ } SFLLabelStack; typedef struct _SFLExtended_mpls { SFLAddress nextHop; /* Address of the next hop */ SFLLabelStack in_stack; SFLLabelStack out_stack; } SFLExtended_mpls; /* Extended NAT data Packet header records report addresses as seen at the sFlowDataSource. The extended_nat structure reports on translated source and/or destination addesses for this packet. If an address was not translated it should be equal to that reported for the header. */ typedef struct _SFLExtended_nat { SFLAddress src; /* Source address */ SFLAddress dst; /* Destination address */ } SFLExtended_nat; /* additional Extended MPLS stucts */ typedef struct _SFLExtended_mpls_tunnel { SFLString tunnel_lsp_name; /* Tunnel name */ u_int32_t tunnel_id; /* Tunnel ID */ u_int32_t tunnel_cos; /* Tunnel COS value */ } SFLExtended_mpls_tunnel; typedef struct _SFLExtended_mpls_vc { SFLString vc_instance_name; /* VC instance name */ u_int32_t vll_vc_id; /* VLL/VC instance ID */ u_int32_t vc_label_cos; /* VC Label COS value */ } SFLExtended_mpls_vc; /* Extended MPLS FEC - Definitions from MPLS-FTN-STD-MIB mplsFTNTable */ typedef struct _SFLExtended_mpls_FTN { SFLString mplsFTNDescr; u_int32_t mplsFTNMask; } SFLExtended_mpls_FTN; /* Extended MPLS LVP FEC - Definition from MPLS-LDP-STD-MIB mplsFecTable Note: mplsFecAddrType, mplsFecAddr information available from packet header */ typedef struct _SFLExtended_mpls_LDP_FEC { u_int32_t mplsFecAddrPrefixLength; } SFLExtended_mpls_LDP_FEC; /* Extended VLAN tunnel information Record outer VLAN encapsulations that have been stripped. extended_vlantunnel information should only be reported if all the following conditions are satisfied: 1. The packet has nested vlan tags, AND 2. The reporting device is VLAN aware, AND 3. One or more VLAN tags have been stripped, either because they represent proprietary encapsulations, or because switch hardware automatically strips the outer VLAN encapsulation. Reporting extended_vlantunnel information is not a substitute for reporting extended_switch information. extended_switch data must always be reported to describe the ingress/egress VLAN information for the packet. The extended_vlantunnel information only applies to nested VLAN tags, and then only when one or more tags has been stripped. */ typedef SFLLabelStack SFLVlanStack; typedef struct _SFLExtended_vlan_tunnel { SFLVlanStack stack; /* List of stripped 802.1Q TPID/TCI layers. Each TPID,TCI pair is represented as a single 32 bit integer. Layers listed from outermost to innermost. */ } SFLExtended_vlan_tunnel; ////////////////// InMon Extension structs ////////////////////////// typedef struct _SFLProcess { u_int32_t pid; SFLString command; } SFLProcess; #define SFL_MAX_PROCESSES 10 typedef struct _SFLExtended_process { u_int32_t num_processes; SFLProcess processes[SFL_MAX_PROCESSES]; } SFLExtended_process; ////////////////////////////////////////////////////////////////////// enum SFLFlow_type_tag { /* enterprise = 0, format = ... */ SFLFLOW_HEADER = 1, /* Packet headers are sampled */ SFLFLOW_ETHERNET = 2, /* MAC layer information */ SFLFLOW_IPV4 = 3, /* IP version 4 data */ SFLFLOW_IPV6 = 4, /* IP version 6 data */ SFLFLOW_EX_SWITCH = 1001, /* Extended switch information */ SFLFLOW_EX_ROUTER = 1002, /* Extended router information */ SFLFLOW_EX_GATEWAY = 1003, /* Extended gateway router information */ SFLFLOW_EX_USER = 1004, /* Extended TACAS/RADIUS user information */ SFLFLOW_EX_URL = 1005, /* Extended URL information */ SFLFLOW_EX_MPLS = 1006, /* Extended MPLS information */ SFLFLOW_EX_NAT = 1007, /* Extended NAT information */ SFLFLOW_EX_MPLS_TUNNEL = 1008, /* additional MPLS information */ SFLFLOW_EX_MPLS_VC = 1009, SFLFLOW_EX_MPLS_FTN = 1010, SFLFLOW_EX_MPLS_LDP_FEC = 1011, SFLFLOW_EX_VLAN_TUNNEL = 1012, /* VLAN stack */ /* enterprise = 8800 pmacct */ SFLFLOW_EX_CLASS = (8800 << 12) + 1, SFLFLOW_EX_TAG = (8800 << 12) + 2, /* enterprise = 4300 (inmon)...*/ SFLFLOW_EX_PROCESS = (4300 << 12) + 3, /* =17612803 Extended Process information */ }; typedef union _SFLFlow_type { SFLSampled_header header; SFLSampled_ethernet ethernet; SFLSampled_ipv4 ipv4; SFLSampled_ipv6 ipv6; SFLExtended_switch sw; SFLExtended_router router; SFLExtended_gateway gateway; SFLExtended_user user; SFLExtended_url url; SFLExtended_mpls mpls; SFLExtended_nat nat; SFLExtended_mpls_tunnel mpls_tunnel; SFLExtended_mpls_vc mpls_vc; SFLExtended_mpls_FTN mpls_ftn; SFLExtended_mpls_LDP_FEC mpls_ldp_fec; SFLExtended_vlan_tunnel vlan_tunnel; // extensions SFLExtended_process process; } SFLFlow_type; typedef struct _SFLFlow_sample_element { struct _SFLFlow_sample_element *nxt; u_int32_t tag; /* SFLFlow_type_tag */ u_int32_t length; SFLFlow_type flowType; } SFLFlow_sample_element; #define SFLFLOW_SAMPLE 1 /* enterprise = 0 : format = 1 */ #define SFLCOUNTERS_SAMPLE 2 /* enterprise = 0 : format = 2 */ #define SFLFLOW_SAMPLE_EXPANDED 3 /* enterprise = 0 : format = 3 */ #define SFLCOUNTERS_SAMPLE_EXPANDED 4 /* enterprise = 0 : format = 4 */ #define SFLACL_BROCADE_SAMPLE 8155137 /* enterprise = 1991 : format = 1 */ /* Format of a single flow sample */ typedef struct _SFLFlow_sample { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 1 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each flow sample generated */ u_int32_t source_id; /* fsSourceId */ u_int32_t sampling_rate; /* fsPacketSamplingRate */ u_int32_t sample_pool; /* Total number of packets that could have been sampled (i.e. packets skipped by sampling process + total number of samples) */ u_int32_t drops; /* Number of times a packet was dropped due to lack of resources */ u_int32_t input; /* SNMP ifIndex of input interface. 0 if interface is not known. */ u_int32_t output; /* SNMP ifIndex of output interface, 0 if interface is not known. Set most significant bit to indicate multiple destination interfaces (i.e. in case of broadcast or multicast) and set lower order bits to indicate number of destination interfaces. Examples: 0x00000002 indicates ifIndex = 2 0x00000000 ifIndex unknown. 0x80000007 indicates a packet sent to 7 interfaces. 0x80000000 indicates a packet sent to an unknown number of interfaces greater than 1.*/ u_int32_t num_elements; SFLFlow_sample_element *elements; } SFLFlow_sample; /* same thing, but the expanded version (for full 32-bit ifIndex numbers) */ typedef struct _SFLFlow_sample_expanded { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 1 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each flow sample generated */ u_int32_t ds_class; /* EXPANDED */ u_int32_t ds_index; /* EXPANDED */ u_int32_t sampling_rate; /* fsPacketSamplingRate */ u_int32_t sample_pool; /* Total number of packets that could have been sampled (i.e. packets skipped by sampling process + total number of samples) */ u_int32_t drops; /* Number of times a packet was dropped due to lack of resources */ u_int32_t inputFormat; /* EXPANDED */ u_int32_t input; /* SNMP ifIndex of input interface. 0 if interface is not known. */ u_int32_t outputFormat; /* EXPANDED */ u_int32_t output; /* SNMP ifIndex of output interface, 0 if interface is not known. */ u_int32_t num_elements; SFLFlow_sample_element *elements; } SFLFlow_sample_expanded; /* Counter types */ /* Generic interface counters - see RFC 1573, 2233 */ typedef struct _SFLIf_counters { u_int32_t ifIndex; u_int32_t ifType; u_int64_t ifSpeed; u_int32_t ifDirection; /* Derived from MAU MIB (RFC 2668) 0 = unknown, 1 = full-duplex, 2 = half-duplex, 3 = in, 4 = out */ u_int32_t ifStatus; /* bit field with the following bits assigned: bit 0 = ifAdminStatus (0 = down, 1 = up) bit 1 = ifOperStatus (0 = down, 1 = up) */ u_int64_t ifInOctets; u_int32_t ifInUcastPkts; u_int32_t ifInMulticastPkts; u_int32_t ifInBroadcastPkts; u_int32_t ifInDiscards; u_int32_t ifInErrors; u_int32_t ifInUnknownProtos; u_int64_t ifOutOctets; u_int32_t ifOutUcastPkts; u_int32_t ifOutMulticastPkts; u_int32_t ifOutBroadcastPkts; u_int32_t ifOutDiscards; u_int32_t ifOutErrors; u_int32_t ifPromiscuousMode; } SFLIf_counters; /* Ethernet interface counters - see RFC 2358 */ typedef struct _SFLEthernet_counters { u_int32_t dot3StatsAlignmentErrors; u_int32_t dot3StatsFCSErrors; u_int32_t dot3StatsSingleCollisionFrames; u_int32_t dot3StatsMultipleCollisionFrames; u_int32_t dot3StatsSQETestErrors; u_int32_t dot3StatsDeferredTransmissions; u_int32_t dot3StatsLateCollisions; u_int32_t dot3StatsExcessiveCollisions; u_int32_t dot3StatsInternalMacTransmitErrors; u_int32_t dot3StatsCarrierSenseErrors; u_int32_t dot3StatsFrameTooLongs; u_int32_t dot3StatsInternalMacReceiveErrors; u_int32_t dot3StatsSymbolErrors; } SFLEthernet_counters; /* Token ring counters - see RFC 1748 */ typedef struct _SFLTokenring_counters { u_int32_t dot5StatsLineErrors; u_int32_t dot5StatsBurstErrors; u_int32_t dot5StatsACErrors; u_int32_t dot5StatsAbortTransErrors; u_int32_t dot5StatsInternalErrors; u_int32_t dot5StatsLostFrameErrors; u_int32_t dot5StatsReceiveCongestions; u_int32_t dot5StatsFrameCopiedErrors; u_int32_t dot5StatsTokenErrors; u_int32_t dot5StatsSoftErrors; u_int32_t dot5StatsHardErrors; u_int32_t dot5StatsSignalLoss; u_int32_t dot5StatsTransmitBeacons; u_int32_t dot5StatsRecoverys; u_int32_t dot5StatsLobeWires; u_int32_t dot5StatsRemoves; u_int32_t dot5StatsSingles; u_int32_t dot5StatsFreqErrors; } SFLTokenring_counters; /* 100 BaseVG interface counters - see RFC 2020 */ typedef struct _SFLVg_counters { u_int32_t dot12InHighPriorityFrames; u_int64_t dot12InHighPriorityOctets; u_int32_t dot12InNormPriorityFrames; u_int64_t dot12InNormPriorityOctets; u_int32_t dot12InIPMErrors; u_int32_t dot12InOversizeFrameErrors; u_int32_t dot12InDataErrors; u_int32_t dot12InNullAddressedFrames; u_int32_t dot12OutHighPriorityFrames; u_int64_t dot12OutHighPriorityOctets; u_int32_t dot12TransitionIntoTrainings; u_int64_t dot12HCInHighPriorityOctets; u_int64_t dot12HCInNormPriorityOctets; u_int64_t dot12HCOutHighPriorityOctets; } SFLVg_counters; typedef struct _SFLVlan_counters { u_int32_t vlan_id; u_int64_t octets; u_int32_t ucastPkts; u_int32_t multicastPkts; u_int32_t broadcastPkts; u_int32_t discards; } SFLVlan_counters; /* Processor Information */ /* opaque = counter_data; enterprise = 0; format = 1001 */ typedef struct _SFLProcessor_counters { u_int32_t five_sec_cpu; /* 5 second average CPU utilization */ u_int32_t one_min_cpu; /* 1 minute average CPU utilization */ u_int32_t five_min_cpu; /* 5 minute average CPU utilization */ u_int64_t total_memory; /* total memory (in bytes) */ u_int64_t free_memory; /* free memory (in bytes) */ } SFLProcessor_counters; /* Counters data */ enum SFLCounters_type_tag { /* enterprise = 0, format = ... */ SFLCOUNTERS_GENERIC = 1, SFLCOUNTERS_ETHERNET = 2, SFLCOUNTERS_TOKENRING = 3, SFLCOUNTERS_VG = 4, SFLCOUNTERS_VLAN = 5, SFLCOUNTERS_PROCESSOR = 1001 }; typedef union _SFLCounters_type { SFLIf_counters generic; SFLEthernet_counters ethernet; SFLTokenring_counters tokenring; SFLVg_counters vg; SFLVlan_counters vlan; SFLProcessor_counters processor; } SFLCounters_type; typedef struct _SFLCounters_sample_element { struct _SFLCounters_sample_element *nxt; /* linked list */ u_int32_t tag; /* SFLCounters_type_tag */ u_int32_t length; SFLCounters_type counterBlock; } SFLCounters_sample_element; typedef struct _SFLCounters_sample { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 2 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each counters sample generated by this source_id */ u_int32_t source_id; /* fsSourceId */ u_int32_t num_elements; SFLCounters_sample_element *elements; } SFLCounters_sample; /* same thing, but the expanded version, so ds_index can be a full 32 bits */ typedef struct _SFLCounters_sample_expanded { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 2 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each counters sample generated by this source_id */ u_int32_t ds_class; /* EXPANDED */ u_int32_t ds_index; /* EXPANDED */ u_int32_t num_elements; SFLCounters_sample_element *elements; } SFLCounters_sample_expanded; #define SFLADD_ELEMENT(_sm, _el) do { (_el)->nxt = (_sm)->elements; (_sm)->elements = (_el); } while(0) /* Format of a sample datagram */ enum SFLDatagram_version { SFLDATAGRAM_VERSION2 = 2, SFLDATAGRAM_VERSION4 = 4, SFLDATAGRAM_VERSION5 = 5 }; typedef struct _SFLSample_datagram_hdr { u_int32_t datagram_version; /* (enum SFLDatagram_version) = VERSION5 = 5 */ SFLAddress agent_address; /* IP address of sampling agent */ u_int32_t sub_agent_id; /* Used to distinguishing between datagram streams from separate agent sub entities within an device. */ u_int32_t sequence_number; /* Incremented with each sample datagram generated */ u_int32_t uptime; /* Current time (in milliseconds since device last booted). Should be set as close to datagram transmission time as possible.*/ u_int32_t num_records; /* Number of tag-len-val flow/counter records to follow */ } SFLSample_datagram_hdr; #define SFL_MAX_DATAGRAM_SIZE 1500 #define SFL_MIN_DATAGRAM_SIZE 200 #define SFL_DEFAULT_DATAGRAM_SIZE 1400 #define SFL_DATA_PAD 400 pmacct-0.14.0/src/ll.c0000644000175000017500000003166611604044474013433 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ /* defines */ #define __LL_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" /* eth_handler() picks a whole packet, reads informtions contained in the link layer protocol header and fills a pointer structure */ void eth_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs) { u_int16_t e8021Q, ppp; struct eth_header *eth_pk; u_int16_t etype, caplen = h->caplen, nl; u_int8_t cursor = 0; if (caplen < ETHER_HDRLEN) { pptrs->iph_ptr = NULL; return; } eth_pk = (struct eth_header *) pptrs->packet_ptr; etype = ntohs(eth_pk->ether_type); pptrs->mac_ptr = (u_char *) eth_pk->ether_dhost; pptrs->vlan_ptr = NULL; /* avoid stale vlan pointers */ pptrs->mpls_ptr = NULL; /* avoid stale MPLS pointers */ nl = ETHER_HDRLEN; caplen -= ETHER_HDRLEN; recurse: if (etype == ETHERTYPE_IP) { pptrs->l3_proto = ETHERTYPE_IP; pptrs->l3_handler = ip_handler; pptrs->iph_ptr = pptrs->packet_ptr + nl; return; } #if defined ENABLE_IPV6 if (etype == ETHERTYPE_IPV6) { pptrs->l3_proto = ETHERTYPE_IPV6; pptrs->l3_handler = ip6_handler; pptrs->iph_ptr = pptrs->packet_ptr + nl; return; } #endif /* originally contributed by Rich Gade */ if (etype == ETHERTYPE_8021Q) { if (caplen < IEEE8021Q_TAGLEN) { pptrs->iph_ptr = NULL; return; } memcpy(&e8021Q, pptrs->packet_ptr+nl+2, 2); if (!cursor) pptrs->vlan_ptr = pptrs->packet_ptr + nl; etype = ntohs(e8021Q); nl += IEEE8021Q_TAGLEN; caplen -= IEEE8021Q_TAGLEN; cursor++; goto recurse; } /* originally contributed by Vasiliy Ponomarev */ if (etype == ETHERTYPE_PPPOE) { if (caplen < PPPOE_HDRLEN+PPP_TAGLEN) { pptrs->iph_ptr = NULL; return; } memcpy(&ppp, pptrs->packet_ptr+nl+PPPOE_HDRLEN, 2); etype = ntohs(ppp); if (etype == PPP_IP) etype = ETHERTYPE_IP; #if defined ENABLE_IPV6 if (etype == PPP_IPV6) etype = ETHERTYPE_IPV6; #endif nl += PPPOE_HDRLEN+PPP_TAGLEN; caplen -= PPPOE_HDRLEN+PPP_TAGLEN; cursor = 1; goto recurse; } if (etype == ETHERTYPE_MPLS || etype == ETHERTYPE_MPLS_MULTI) { etype = mpls_handler(pptrs->packet_ptr + nl, &caplen, &nl, pptrs); cursor = 1; goto recurse; } pptrs->l3_proto = 0; pptrs->l3_handler = NULL; pptrs->iph_ptr = NULL; } u_int16_t mpls_handler(u_char *bp, u_int16_t *caplen, u_int16_t *nl, register struct packet_ptrs *pptrs) { u_char *p = bp; u_int32_t label=0; pptrs->mpls_ptr = bp; if (*caplen < 4) { pptrs->iph_ptr = NULL; return 0; } do { label = ntohl(*p); p += 4; *nl += 4; *caplen -= 4; } while (!MPLS_STACK(label) && *caplen >= 4); switch (MPLS_LABEL(label)) { case 0: /* IPv4 explicit NULL label */ case 3: /* IPv4 implicit NULL label */ return ETHERTYPE_IP; #if defined ENABLE_IPV6 case 2: /* IPv6 explicit NULL label */ return ETHERTYPE_IPV6; #endif default: /* support for what is sometimes referred as null-encapsulation: by looking at the first payload byte (but only if the Bottom of Stack bit is set) we try to determine the network layer protocol: 0x45-0x4f is IPv4 0x60-0x6f is IPv6 */ if (MPLS_STACK(label)) { switch (*p) { case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f: return ETHERTYPE_IP; #if defined ENABLE_IPV6 case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: return ETHERTYPE_IPV6; #endif default: break; } } break; } return FALSE; } void fddi_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs) { register u_char *p; u_int caplen = h->caplen; struct fddi_header *fddi_pk; if (caplen < FDDI_HDRLEN) { pptrs->iph_ptr = NULL; return; } p = pptrs->packet_ptr; fddi_pk = (struct fddi_header *) pptrs->packet_ptr; if ((fddi_pk->fddi_fc & FDDIFC_CLFF) == FDDIFC_LLC_ASYNC) { pptrs->mac_ptr = (u_char *) fddi_pk->fddi_dhost; /* going up to LLC/SNAP layer header */ p += FDDI_HDRLEN; caplen -= FDDI_HDRLEN; if ((p = llc_handler(h, caplen, p, pptrs)) != NULL) { pptrs->iph_ptr = p; return; } } pptrs->iph_ptr = NULL; } void tr_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs) { register u_char *p; u_int caplen = h->caplen, route_len = 0; struct token_header *tr_pk; if (caplen < ETHER_HDRLEN) { pptrs->iph_ptr = NULL; return; } p = pptrs->packet_ptr; tr_pk = (struct token_header *) pptrs->packet_ptr; if (TR_IS_SOURCE_ROUTED(tr_pk)) { route_len = TR_RIF_LENGTH(tr_pk); if (caplen < ETHER_HDRLEN + route_len) { pptrs->iph_ptr = NULL; return; } } if (((tr_pk->token_fc & 0xC0) >> 6) == TOKEN_FC_LLC) { pptrs->mac_ptr = (u_char *) tr_pk->token_dhost; /* going up to LLC/SNAP layer header */ p += (ETHER_HDRLEN + route_len); caplen -= (ETHER_HDRLEN + route_len); if ((p = llc_handler(h, caplen, p, pptrs)) != NULL) { pptrs->iph_ptr = p; return; } } pptrs->iph_ptr = NULL; } void ppp_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs) { u_char *p = pptrs->packet_ptr; u_int16_t caplen = h->caplen, nl = 0; unsigned int proto = 0; if (caplen < PPP_HDRLEN) { pptrs->iph_ptr = NULL; return; } if (*p == PPP_ADDRESS && *(p + 1) == PPP_CONTROL) { p += 2; caplen -= 2; if (caplen < 2) { pptrs->iph_ptr = NULL; return; } } if (*p % 2) { proto = *p; p++; } else { proto = EXTRACT_16BITS(p); p += 2; } recurse: if ((proto == PPP_IP) || (proto == ETHERTYPE_IP)) { pptrs->l3_proto = ETHERTYPE_IP; pptrs->l3_handler = ip_handler; pptrs->iph_ptr = p; return; } #if defined ENABLE_IPV6 if ((proto == PPP_IPV6) || (proto == ETHERTYPE_IPV6)) { pptrs->l3_proto = ETHERTYPE_IPV6; pptrs->l3_handler = ip6_handler; pptrs->iph_ptr = p; return; } #endif if (proto == PPP_MPLS_UCAST || proto == PPP_MPLS_MCAST) { proto = mpls_handler(p, &caplen, &nl, pptrs); goto recurse; } pptrs->l3_proto = 0; pptrs->l3_handler = NULL; pptrs->iph_ptr = NULL; } /* support for 802.11 Wireless LAN protocol. I'm writing it during a sad morning spent at Fiumicino's Airport because of Alitalia strikes. It's currently working well for me at FCO WiFi zone. Let me know. 28-11-2003, Paolo. */ void ieee_802_11_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs) { u_int16_t fc; u_int caplen = h->caplen; short int hdrlen; u_char *p; if (caplen < IEEE802_11_FC_LEN) { pptrs->iph_ptr = NULL; return; } p = pptrs->packet_ptr; fc = EXTRACT_LE_16BITS(p); if (FC_TYPE(fc) == T_DATA) { if (FC_TO_DS(fc) && FC_FROM_DS(fc)) hdrlen = 30; else hdrlen = 24; if (caplen < hdrlen) { pptrs->iph_ptr = NULL; return; } caplen -= hdrlen; p += hdrlen; if (!FC_WEP(fc)) { if ((p = llc_handler(h, caplen, p, pptrs)) != NULL) { pptrs->iph_ptr = p; return; } } } pptrs->l3_proto = 0; pptrs->l3_handler = NULL; pptrs->iph_ptr = NULL; } void raw_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs) { register u_int16_t caplen = h->caplen; struct my_iphdr *hdr; if (caplen < 4) { pptrs->iph_ptr = NULL; return; } hdr = (struct my_iphdr *) pptrs->packet_ptr; switch (IP_V(hdr)) { case 4: pptrs->iph_ptr = pptrs->packet_ptr; pptrs->l3_proto = ETHERTYPE_IP; pptrs->l3_handler = ip_handler; return; #if defined ENABLE_IPV6 case 6: pptrs->iph_ptr = pptrs->packet_ptr; pptrs->l3_proto = ETHERTYPE_IPV6; pptrs->l3_handler = ip6_handler; return; #endif default: pptrs->iph_ptr = NULL; pptrs->l3_proto = 0; pptrs->l3_handler = NULL; return; } } void null_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs) { register u_int32_t *family; u_int caplen = h->caplen; u_char *p; if (caplen < 4) { pptrs->iph_ptr = NULL; return; } family = (u_int32_t *) pptrs->packet_ptr; p = pptrs->packet_ptr; if (*family == AF_INET || ntohl(*family) == AF_INET ) { pptrs->l3_proto = ETHERTYPE_IP; pptrs->l3_handler = ip_handler; pptrs->iph_ptr = (u_char *)(pptrs->packet_ptr + 4); return; } #if defined ENABLE_IPV6 if (*family == AF_INET6 || ntohl(*family) == AF_INET6 ) { pptrs->l3_proto = ETHERTYPE_IPV6; pptrs->l3_handler = ip6_handler; pptrs->iph_ptr = (u_char *)(pptrs->packet_ptr + 4); return; } #endif pptrs->l3_proto = 0; pptrs->l3_handler = NULL; pptrs->iph_ptr = NULL; } void sll_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs) { register const struct sll_header *sllp; register u_short etype; u_char *p; u_int caplen = h->caplen; u_int16_t e8021Q, nl; if (caplen < SLL_HDR_LEN) { pptrs->iph_ptr = NULL; return; } pptrs->mac_ptr = NULL; pptrs->vlan_ptr = NULL; pptrs->mpls_ptr = NULL; p = pptrs->packet_ptr; sllp = (const struct sll_header *) pptrs->packet_ptr; etype = ntohs(sllp->sll_protocol); nl = SLL_HDR_LEN; if (EXTRACT_16BITS(&sllp->sll_halen) == ETH_ADDR_LEN) { memcpy(sll_mac[1], sllp->sll_addr, ETH_ADDR_LEN); pptrs->mac_ptr = (char *) sll_mac; } recurse: if (etype == ETHERTYPE_IP) { pptrs->l3_proto = ETHERTYPE_IP; pptrs->l3_handler = ip_handler; pptrs->iph_ptr = (u_char *)(pptrs->packet_ptr + nl); return; } /* XXX: ETHERTYPE_IPV6 ? */ if (etype == LINUX_SLL_P_802_2) { /* going up to LLC/SNAP layer header */ p += SLL_HDR_LEN; caplen -= SLL_HDR_LEN; if ((p = llc_handler(h, caplen, p, pptrs)) != NULL) { pptrs->iph_ptr = p; return; } } /* originally contributed by Rich Gade for eth_handler() */ if (etype == ETHERTYPE_8021Q) { if (caplen < IEEE8021Q_TAGLEN) { pptrs->iph_ptr = NULL; return; } memcpy(&e8021Q, pptrs->packet_ptr+nl+2, 2); pptrs->vlan_ptr = pptrs->packet_ptr + nl; etype = ntohs(e8021Q); nl += IEEE8021Q_TAGLEN; caplen -= IEEE8021Q_TAGLEN; goto recurse; } pptrs->l3_proto = 0; pptrs->l3_handler = NULL; pptrs->iph_ptr = NULL; } u_char *llc_handler(const struct pcap_pkthdr *h, u_int caplen, register u_char *buf, register struct packet_ptrs *pptrs) { struct llc llc; register u_short etype; if (caplen < 3) return NULL; memcpy((char *)&llc, (char *) buf, min(caplen, sizeof(llc))); if (llc.ssap == LLCSAP_SNAP && llc.dsap == LLCSAP_SNAP && llc.ctl.snap.snap_ui == LLC_UI) { etype = EXTRACT_16BITS(&llc.ctl.snap_ether.snap_ethertype[0]); if (etype == ETHERTYPE_IP) { pptrs->l3_proto = ETHERTYPE_IP; pptrs->l3_handler = ip_handler; return (u_char *)(buf + min(caplen, sizeof(llc))); } #if defined ENABLE_IPV6 if (etype == ETHERTYPE_IPV6) { pptrs->l3_proto = ETHERTYPE_IPV6; pptrs->l3_handler = ip6_handler; return (u_char *)(buf + min(caplen, sizeof(llc))); } #endif else return 0; } else return 0; } void chdlc_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs) { u_char *p = pptrs->packet_ptr; u_int16_t caplen = h->caplen, nl = 0; u_int16_t proto; if (caplen < CHDLC_HDRLEN) { pptrs->iph_ptr = NULL; return; } proto = EXTRACT_16BITS(&p[2]); caplen -= CHDLC_HDRLEN; p += CHDLC_HDRLEN; recurse: switch (proto) { case ETHERTYPE_IP: pptrs->l3_proto = ETHERTYPE_IP; pptrs->l3_handler = ip_handler; pptrs->iph_ptr = p; return; #if defined ENABLE_IPV6 case ETHERTYPE_IPV6: pptrs->l3_proto = ETHERTYPE_IPV6; pptrs->l3_handler = ip6_handler; pptrs->iph_ptr = p; return; #endif case ETHERTYPE_MPLS: case ETHERTYPE_MPLS_MULTI: proto = mpls_handler(p, &caplen, &nl, pptrs); goto recurse; default: break; } pptrs->l3_proto = 0; pptrs->l3_handler = NULL; pptrs->iph_ptr = NULL; } pmacct-0.14.0/src/addr.c0000644000175000017500000002216011403044665013722 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2010 by Paolo Lucente */ /* 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. */ #define __ADDR_C #include "pmacct.h" #include "addr.h" static const char hex[] = "0123456789abcdef"; /* * str_to_addr() converts a string into a supported family address */ unsigned int str_to_addr(const char *str, struct host_addr *a) { if (inet_aton(str, &a->address.ipv4)) { a->family = AF_INET; return a->family; } #if defined ENABLE_IPV6 if (inet_pton(AF_INET6, str, &a->address.ipv6) > 0) { a->family = AF_INET6; return a->family; } #endif return 0; } /* * addr_to_str() converts a supported family addres into a string * NOTE: 'str' length is not checked ! */ unsigned int addr_to_str(char *str, const struct host_addr *a) { if (a->family == AF_INET) { inet_ntop(AF_INET, &a->address.ipv4, str, INET6_ADDRSTRLEN); return a->family; } #if defined ENABLE_IPV6 if (a->family == AF_INET6) { inet_ntop(AF_INET6, &a->address.ipv6, str, INET6_ADDRSTRLEN); return a->family; } #endif memset(str, 0, INET6_ADDRSTRLEN); return 0; } /* * addr_to_sa() converts a supported family address into a sockaddr * structure */ unsigned int addr_to_sa(struct sockaddr *sa, struct host_addr *a, u_int16_t port) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; #if defined ENABLE_IPV6 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; #endif if (a->family == AF_INET) { sa->sa_family = AF_INET; sa4->sin_addr.s_addr = a->address.ipv4.s_addr; sa4->sin_port = htons(port); return sizeof(struct sockaddr_in); } #if defined ENABLE_IPV6 if (a->family == AF_INET6) { sa->sa_family = AF_INET6; ip6_addr_cpy(&sa6->sin6_addr, &a->address.ipv6); sa6->sin6_port = htons(port); return sizeof(struct sockaddr_in6); } #endif memset(sa, 0, sizeof(struct sockaddr)); return 0; } /* * sa_to_addr() converts a sockaddr structure into a supported family * address */ unsigned int sa_to_addr(struct sockaddr *sa, struct host_addr *a, u_int16_t *port) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; #if defined ENABLE_IPV6 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; #endif if (sa->sa_family == AF_INET) { a->family = AF_INET; a->address.ipv4.s_addr = sa4->sin_addr.s_addr; *port = ntohs(sa4->sin_port); return sizeof(struct sockaddr_in); } #if defined ENABLE_IPV6 if (sa->sa_family == AF_INET6) { a->family = AF_INET6; ip6_addr_cpy(&a->address.ipv6, &sa6->sin6_addr); *port = ntohs(sa6->sin6_port); return sizeof(struct sockaddr_in6); } #endif memset(a, 0, sizeof(struct host_addr)); return 0; } /* * sa_addr_cmp(): compare two IP addresses: the first encapsulated into a * 'struct sockaddr' and the second into a 'struct host_addr'. * returns 0 if they match; 1 if they don't match; -1 to signal a generic * error (e.g. unsupported family mismatch). */ unsigned int sa_addr_cmp(struct sockaddr *sa, struct host_addr *a) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; #if defined ENABLE_IPV6 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; struct sockaddr_in6 sa6_local; #endif if (a->family == AF_INET && sa->sa_family == AF_INET) { if (sa4->sin_addr.s_addr == a->address.ipv4.s_addr) return FALSE; else return TRUE; } #if defined ENABLE_IPV6 if (a->family == AF_INET6 && sa->sa_family == AF_INET6) { if (!ip6_addr_cmp(&sa6->sin6_addr, &a->address.ipv6)) return FALSE; else return TRUE; } else if (a->family == AF_INET && sa->sa_family == AF_INET6) { memset(&sa6_local, 0, sizeof(sa6_local)); memset((u_int8_t *)&sa6_local.sin6_addr+10, 0xff, 2); memcpy((u_int8_t *)&sa6_local.sin6_addr+12, &a->address.ipv4.s_addr, 4); if (!ip6_addr_cmp(&sa6->sin6_addr, &sa6_local.sin6_addr)) return FALSE; else return TRUE; } else if (a->family == AF_INET6 && sa->sa_family == AF_INET) { memset(&sa6_local, 0, sizeof(sa6_local)); memset((u_int8_t *)&sa6_local.sin6_addr+10, 0xff, 2); memcpy((u_int8_t *)&sa6_local.sin6_addr+12, &sa4->sin_addr, 4); if (!ip6_addr_cmp(&sa6_local.sin6_addr, &a->address.ipv6)) return FALSE; else return TRUE; } #endif return -1; } /* * pm_htonl6(): same as htonl() for IPv6 addresses; no checks are done * on the length of the buffer. */ void *pm_htonl6(void *addr) { register u_int32_t *ptr = addr; static u_int32_t buf[4]; u_int8_t chunk; for (chunk = 0; chunk < 4; chunk++) buf[chunk] = htonl(ptr[chunk]); return buf; } /* * pm_ntohl6(): same as ntohl() for IPv6 addresses; no checks are done * on the length of the buffer. */ void *pm_ntohl6(void *addr) { register u_int32_t *ptr = addr; static u_int32_t buf[4]; u_int8_t chunk; for (chunk = 0; chunk < 4; chunk++) buf[chunk] = ntohl(ptr[chunk]); return buf; } /* * ip6_addr_cmp(): compare two IPv6 addresses; returns 0 if they match, * 1 if the first not matching chunk of addr1 is found to be greater than * addr2; -1 on the contrary. */ unsigned int ip6_addr_cmp(void *addr1, void *addr2) { register u_int32_t *ptr1 = addr1, *ptr2 = addr2; int chunk; for (chunk = 0; chunk < 4; chunk++) { if (ptr1[chunk] == ptr2[chunk]) continue; else { if (ptr1[chunk] > ptr2[chunk]) return TRUE; else return -1; } } return FALSE; } /* * ip6_addr_cpy(): copy of a *src IPv6 address into a *dst buffer. */ void ip6_addr_cpy(void *dst, void *src) { register u_int32_t *ptrs = src, *ptrd = dst; int chunk; for (chunk = 0; chunk < 4; chunk++) ptrd[chunk] = ptrs[chunk]; } void etheraddr_string(const u_char *ep, char *buf) { u_int i, j; char *cp; cp = buf; if ((j = *ep >> 4) != 0) *cp++ = hex[j]; else *cp++ = '0'; *cp++ = hex[*ep++ & 0xf]; for (i = 5; (int)--i >= 0;) { *cp++ = ':'; if ((j = *ep >> 4) != 0) *cp++ = hex[j]; else *cp++ = '0'; *cp++ = hex[*ep++ & 0xf]; } *cp = '\0'; } /* * string_etheraddr() writes the content of *asc in *addr (which has * to be ETH_ADDR_LEN long). TRUE is returned if any failure occurs; * TRUE if the routine completes the job successfully */ int string_etheraddr(const u_char *asc, char *addr) { int cnt; for (cnt = 0; cnt < 6; ++cnt) { unsigned int number; char ch; ch = tolower (*asc++); if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f')) return 1; number = isdigit (ch) ? (ch - '0') : (ch - 'a' + 10); ch = tolower(*asc); if ((cnt < 5 && ch != ':') || (cnt == 5 && ch != '\0' && !isspace (ch))) { ++asc; if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f')) return 1; number <<= 4; number += isdigit (ch) ? (ch - '0') : (ch - 'a' + 10); ch = *asc; if (cnt < 5 && ch != ':') return 1; } /* Store result. */ addr[cnt] = (unsigned char) number; /* Skip ':'. */ ++asc; } return FALSE; } /* * pm_htonll(): similar to htonl() for 64 bits integers; no checks are done * on the length of the buffer. */ u_int64_t pm_htonll(u_int64_t addr) { #if defined IM_LITTLE_ENDIAN static u_int64_t buf; register u_int32_t *x = (u_int32_t *)(void *) &addr; register u_int32_t *y = (u_int32_t *)(void *) &buf; y[0] = htonl(x[1]); y[1] = htonl(x[0]); return buf; #else return addr; #endif } /* * pm_ntohll(): similar to ntohl() for 64 bits integers; no checks are done * on the length of the buffer. */ u_int64_t pm_ntohll(u_int64_t addr) { #if defined IM_LITTLE_ENDIAN static u_int64_t buf; buf = ((u_int64_t) ntohl(addr & 0xFFFFFFFFLLU)) << 32; buf |= ntohl((addr & 0xFFFFFFFF00000000LLU) >> 32); return buf; #else return addr; #endif } /* * is_multicast(): determines whether the supplied IPv4/IPv6 address is a * multicast address or not. */ int is_multicast(struct host_addr *a) { if (a->family == AF_INET) { if (IS_IPV4_MULTICAST(a->address.ipv4.s_addr)) return a->family; else return FALSE; } #if defined ENABLE_IPV6 if (a->family == AF_INET6) { if (IS_IPV6_MULTICAST(&a->address.ipv6)) return a->family; else return FALSE; } #endif return FALSE; } /* * clean_sin_addr(): cleans the IP address from the pointed sockaddr structure */ void clean_sin_addr(struct sockaddr *sa) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; #if defined ENABLE_IPV6 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; #endif if (sa->sa_family == AF_INET) sa4->sin_addr.s_addr = 0; #if defined ENABLE_IPV6 if (sa->sa_family == AF_INET6) memset(&sa6->sin6_addr, 0, 16); #endif } pmacct-0.14.0/src/sql_common.c0000644000175000017500000026743311737341414015177 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #define __SQL_COMMON_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "sql_common.h" #include "crc32.c" #include "sql_common_m.c" /* Functions */ void sql_set_signals() { signal(SIGINT, sql_exit_gracefully); signal(SIGHUP, reload); /* handles reopening of syslog channel */ signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, reload_maps); signal(SIGPIPE, SIG_IGN); //#if !defined FBSD4 // signal(SIGCHLD, SIG_IGN); //#else signal(SIGCHLD, ignore_falling_child); //#endif } void sql_set_insert_func() { if (config.what_to_count & (COUNT_SUM_HOST|COUNT_SUM_NET)) insert_func = sql_sum_host_insert; else if (config.what_to_count & COUNT_SUM_PORT) insert_func = sql_sum_port_insert; else if (config.what_to_count & COUNT_SUM_AS) insert_func = sql_sum_as_insert; #if defined (HAVE_L2) else if (config.what_to_count & COUNT_SUM_MAC) insert_func = sql_sum_mac_insert; #endif else insert_func = sql_cache_insert; } void sql_init_maps(struct networks_table *nt, struct networks_cache *nc, struct ports_table *pt) { memset(nt, 0, sizeof(struct networks_table)); memset(nc, 0, sizeof(struct networks_cache)); memset(pt, 0, sizeof(struct ports_table)); load_networks(config.networks_file, nt, nc); set_net_funcs(nt); if (config.ports_file) load_ports(config.ports_file, pt); } void sql_init_global_buffers() { memset(sql_data, 0, sizeof(sql_data)); memset(lock_clause, 0, sizeof(lock_clause)); memset(unlock_clause, 0, sizeof(unlock_clause)); memset(update_clause, 0, sizeof(update_clause)); memset(set_clause, 0, sizeof(set_clause)); memset(copy_clause, 0, sizeof(copy_clause)); memset(insert_clause, 0, sizeof(insert_clause)); memset(where, 0, sizeof(where)); memset(values, 0, sizeof(values)); memset(set, 0, sizeof(set)); memset(&lru_head, 0, sizeof(lru_head)); lru_tail = &lru_head; pipebuf = (unsigned char *) malloc(config.buffer_size); cache = (struct db_cache *) malloc(config.sql_cache_entries*sizeof(struct db_cache)); queries_queue = (struct db_cache **) malloc(qq_size*sizeof(struct db_cache *)); pending_queries_queue = (struct db_cache **) malloc(qq_size*sizeof(struct db_cache *)); memset(pipebuf, 0, config.buffer_size); memset(cache, 0, config.sql_cache_entries*sizeof(struct db_cache)); memset(queries_queue, 0, qq_size*sizeof(struct db_cache *)); memset(pending_queries_queue, 0, qq_size*sizeof(struct db_cache *)); } /* being the first routine to be called by each SQL plugin, this is also the place for some initial common configuration consistency check */ void sql_init_default_values() { /* Dirty but allows to save some IFs, centralizes checks and makes later comparison statements lean */ if (!(config.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_PEER_DST_IP| COUNT_SRC_STD_COMM|COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED| COUNT_SRC_LOCAL_PREF|COUNT_MPLS_VPN_RD))) PbgpSz = 0; if ( (config.what_to_count & COUNT_CLASS || config.what_to_count & COUNT_TCPFLAGS || PbgpSz || /* In short: any BGP primitives */ config.nfacctd_sql_log) && config.sql_recovery_logfile) { Log(LOG_ERR, "ERROR ( %s/%s ): sql_recovery_logfile is not compatible with: classifiers, BGP-related primitives, TCP flags and nfacctd_sql_log. Try configuring a backup DB.\n", config.name, config.type); exit_plugin(1); } if (!config.sql_refresh_time) config.sql_refresh_time = DEFAULT_DB_REFRESH_TIME; if (!config.sql_table_version) config.sql_table_version = DEFAULT_SQL_TABLE_VERSION; if (!config.sql_cache_entries) config.sql_cache_entries = CACHE_ENTRIES; if (!config.sql_max_writers) config.sql_max_writers = DEFAULT_SQL_WRITERS_NO; if (config.nfacctd_sql_log && config.acct_type != ACCT_NF) config.nfacctd_sql_log = FALSE; if (config.sql_aggressive_classification) { if (config.acct_type == ACCT_PM && config.what_to_count & COUNT_CLASS); else config.sql_aggressive_classification = FALSE; } /* SQL table type parsing; basically mapping everything down to a SQL table version */ /* ie. BGP == 1000 */ if (config.sql_table_type) { if (!strcmp(config.sql_table_type, "bgp")) config.sql_table_version += SQL_TABLE_VERSION_BGP; else { Log(LOG_ERR, "ERROR ( %s/%s ): Unknown sql_table_type value: '%s'.\n", config.name, config.type, config.sql_table_type); exit_plugin(1); } } else { /* PbgpSz is non-zero if at least one of the BGP-related primitives is enabled. This helps putting ASNs in the right field */ if (PbgpSz) { config.sql_table_version += SQL_TABLE_VERSION_BGP; Log(LOG_INFO, "INFO ( %s/%s ): sql_table_type set to 'bgp' (aggregate includes one or more BGP primitives).\n", config.name, config.type); } } qq_ptr = 0; pqq_ptr = 0; qq_size = config.sql_cache_entries+(config.sql_refresh_time*REASONABLE_NUMBER); pp_size = sizeof(struct pkt_primitives); pb_size = sizeof(struct pkt_bgp_primitives); dbc_size = sizeof(struct db_cache); glob_nfacctd_sql_log = config.nfacctd_sql_log; memset(&sql_writers, 0, sizeof(sql_writers)); } void sql_init_historical_acct(time_t now, struct insert_data *idata) { time_t t; if (config.sql_history) { idata->basetime = now; if (config.sql_history == COUNT_MINUTELY) idata->timeslot = config.sql_history_howmany*60; else if (config.sql_history == COUNT_HOURLY) idata->timeslot = config.sql_history_howmany*3600; else if (config.sql_history == COUNT_DAILY) idata->timeslot = config.sql_history_howmany*86400; else if (config.sql_history == COUNT_WEEKLY) idata->timeslot = config.sql_history_howmany*86400*7; else if (config.sql_history == COUNT_MONTHLY) { idata->basetime = roundoff_time(idata->basetime, "d"); /* resetting day of month */ idata->timeslot = calc_monthly_timeslot(idata->basetime, config.sql_history_howmany, ADD); } /* round off stuff */ t = roundoff_time(idata->basetime, config.sql_history_roundoff); while ((t+idata->timeslot) < idata->basetime) { t += idata->timeslot; if (config.sql_history == COUNT_MONTHLY) idata->timeslot = calc_monthly_timeslot(t, config.sql_history_howmany, ADD); } idata->basetime = t; glob_basetime = idata->basetime; idata->new_basetime = idata->basetime; glob_new_basetime = idata->basetime; glob_timeslot = idata->timeslot; idata->committed_basetime = 0; glob_committed_basetime = 0; } } /* NOTE: sql triggers time init: deadline; if a trigger exec is specified but no time is supplied, use 'sql_refresh_time' as interval; this will result in a trigger being executed each time data is purged into the DB */ void sql_init_triggers(time_t now, struct insert_data *idata) { time_t t, deadline; if (config.sql_trigger_exec) { deadline = now; if (config.sql_trigger_time == COUNT_MINUTELY) idata->t_timeslot = config.sql_trigger_time_howmany*60; else if (config.sql_trigger_time == COUNT_HOURLY) idata->t_timeslot = config.sql_trigger_time_howmany*3600; else if (config.sql_trigger_time == COUNT_DAILY) idata->t_timeslot = config.sql_trigger_time_howmany*86400; else if (config.sql_trigger_time == COUNT_WEEKLY) idata->t_timeslot = config.sql_trigger_time_howmany*86400*7; else if (config.sql_trigger_time == COUNT_MONTHLY) { deadline = roundoff_time(deadline, "d"); /* resetting day of month */ idata->t_timeslot = calc_monthly_timeslot(deadline, config.sql_trigger_time_howmany, ADD); } else idata->t_timeslot = config.sql_refresh_time; /* round off stuff */ t = roundoff_time(deadline, config.sql_history_roundoff); while ((t+idata->t_timeslot) < deadline) { t += idata->t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata->t_timeslot = calc_monthly_timeslot(t, config.sql_trigger_time_howmany, ADD); } idata->triggertime = t; /* adding a trailer timeslot: it's a deadline not a basetime */ idata->triggertime += idata->t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata->t_timeslot = calc_monthly_timeslot(t, config.sql_trigger_time_howmany, ADD); } } void sql_init_refresh_deadline(time_t *rd) { time_t t; t = roundoff_time(*rd, config.sql_history_roundoff); while ((t+config.sql_refresh_time) < *rd) t += config.sql_refresh_time; *rd = t; *rd += (config.sql_refresh_time+config.sql_startup_delay); /* it's a deadline not a basetime */ } void sql_calc_refresh_timeout(time_t deadline, time_t now, int *timeout) { *timeout = ((deadline-now)+1)*1000; } void sql_init_pipe(struct pollfd *pollfd, int fd) { pollfd->fd = fd; pollfd->events = POLLIN; setnonblocking(fd); } struct template_entry *sql_init_logfile_template(struct template_header *hdr) { struct template_entry *te; te = build_template(hdr); set_template_funcs(hdr, te); return te; } void sql_link_backend_descriptors(struct BE_descs *registry, struct DBdesc *p, struct DBdesc *b) { memset(registry, 0, sizeof(struct BE_descs)); memset(p, 0, sizeof(struct DBdesc)); memset(b, 0, sizeof(struct DBdesc)); registry->p = p; registry->b = b; registry->p->type = BE_TYPE_PRIMARY; registry->b->type = BE_TYPE_BACKUP; if (*sqlfunc_cbr.create_backend) { (*sqlfunc_cbr.create_backend)(registry->p); (*sqlfunc_cbr.create_backend)(registry->b); } } void sql_cache_modulo(struct pkt_primitives *srcdst, struct pkt_bgp_primitives *pbgp, struct insert_data *idata) { idata->hash = cache_crc32((unsigned char *)srcdst, pp_size); if (PbgpSz) { if (pbgp) idata->hash ^= cache_crc32((unsigned char *)pbgp, pb_size); } idata->modulo = idata->hash % config.sql_cache_entries; } int sql_cache_flush(struct db_cache *queue[], int index, struct insert_data *idata, int exiting) { int j, tmp_retired = sql_writers.retired, delay = 0, new_basetime = FALSE; struct db_cache *Cursor, *auxCursor, *PendingElem, SavedCursor; /* We are seeking how many time-bins data has to be delayed by; residual time is taken into account by scanner deadlines (sql_refresh_time) */ if (config.sql_startup_delay) { delay = config.sql_startup_delay/idata->timeslot; delay = delay*idata->timeslot; } /* check-pointing: we record last committed time-bin; part of this is checking whether basetime was moved forward yet (as this is not for sure). */ if (idata->new_basetime && idata->new_basetime < idata->basetime && idata->new_basetime > idata->committed_basetime) { new_basetime = TRUE; idata->committed_basetime = idata->new_basetime; } else idata->committed_basetime = idata->basetime; /* If aggressive classification is enabled and there are still chances for the stream to be classified - ie. tentatives is non-zero - let's leave it in SQL_CACHE_INUSE state */ if (!exiting && !config.nfacctd_sql_log) { if (config.sql_aggressive_classification) { for (j = 0, pqq_ptr = 0; j < index; j++) { if (!queue[j]->primitives.class && queue[j]->tentatives && (queue[j]->start_tag > (idata->now - ((STALE_M-1) * config.sql_refresh_time))) ) { pending_queries_queue[pqq_ptr] = queue[j]; pqq_ptr++; } else if (new_basetime && queue[j]->basetime+delay >= idata->basetime) { pending_queries_queue[pqq_ptr] = queue[j]; pqq_ptr++; } else if (!new_basetime && queue[j]->basetime+delay > idata->basetime) { pending_queries_queue[pqq_ptr] = queue[j]; pqq_ptr++; } else queue[j]->valid = SQL_CACHE_COMMITTED; } } else { for (j = 0, pqq_ptr = 0; j < index; j++) { if (new_basetime && queue[j]->basetime+delay >= idata->basetime) { pending_queries_queue[pqq_ptr] = queue[j]; pqq_ptr++; } else if (!new_basetime && queue[j]->basetime+delay > idata->basetime) { pending_queries_queue[pqq_ptr] = queue[j]; pqq_ptr++; } else queue[j]->valid = SQL_CACHE_COMMITTED; } } } /* If exiting or logging commit everything is still in the cache */ else { for (j = 0; j < index; j++) queue[j]->valid = SQL_CACHE_COMMITTED; } /* Imposing maximum number of writers */ sql_writers.active -= MIN(sql_writers.active, tmp_retired); sql_writers.retired -= tmp_retired; if (sql_writers.active < config.sql_max_writers) { /* If we are very near to our maximum writers threshold, let's resort to any configured recovery mechanism - SQL_CACHE_COMMITTED => SQL_CACHE_ERROR; otherwise, will proceed as usual */ if ((sql_writers.active == config.sql_max_writers-1) && (config.sql_backup_host || config.sql_recovery_logfile)) { for (j = 0; j < index; j++) { if (queue[j]->valid == SQL_CACHE_COMMITTED) queue[j]->valid = SQL_CACHE_ERROR; } sql_writers.flags = CHLD_WARNING; } else sql_writers.flags = 0; /* everything is just fine */ sql_writers.active++; } else { Log(LOG_WARNING, "WARN ( %s/%s ): Maximum number of SQL writer processes reached (%d).\n", config.name, config.type, sql_writers.active); sql_writers.flags = CHLD_ALERT; } return index; } int sql_cache_flush_pending(struct db_cache *queue[], int index, struct insert_data *idata) { struct db_cache *Cursor, *auxCursor, *PendingElem, SavedCursor; int j; /* Not everything was purged, let's sort out the SQL cache buckets involved into that */ if (index) { for (j = 0; j < index; j++) { PendingElem = queue[j]; for (Cursor = PendingElem, auxCursor = NULL; Cursor; auxCursor = Cursor, Cursor = Cursor->prev); /* Check whether we are already first in the bucket */ if (auxCursor != PendingElem) { for (Cursor = auxCursor; Cursor && Cursor != PendingElem && Cursor->valid == SQL_CACHE_INUSE; Cursor = Cursor->next); /* Check whether a) the whole bucket chain is currently in use or b) we came across the current pending element: meaning no free positions are available in the chain, ahead of it */ if (Cursor && Cursor != PendingElem) { /* Check whether we have to replace the first element in the bucket */ if (!Cursor->prev) { memcpy(&SavedCursor, Cursor, sizeof(struct db_cache)); memcpy(Cursor, PendingElem, sizeof(struct db_cache)); Cursor->prev = NULL; Cursor->next = SavedCursor.next; Cursor->chained = 0; Cursor->lru_prev = NULL; Cursor->lru_next = NULL; Cursor->lru_tag = PendingElem->lru_tag; if (PendingElem->cbgp) PendingElem->cbgp = NULL; RetireElem(PendingElem); queue[j] = Cursor; } /* We found at least one Cursor->valid == SQL_CACHE_INUSE */ else SwapChainedElems(PendingElem, Cursor); } } } } } struct db_cache *sql_cache_search(struct pkt_primitives *data, struct pkt_bgp_primitives *pbgp, time_t basetime) { unsigned int modulo; struct db_cache *Cursor; struct insert_data idata; int res_data = TRUE, res_bgp = TRUE; sql_cache_modulo(data, pbgp, &idata); modulo = idata.modulo; Cursor = &cache[idata.modulo]; start: if (idata.hash != Cursor->signature) { if (Cursor->valid == SQL_CACHE_INUSE) { follow_chain: if (Cursor->next) { Cursor = Cursor->next; goto start; } } } else { if (Cursor->valid == SQL_CACHE_INUSE) { /* checks: pkt_primitives and pkt_bgp_primitives */ res_data = memcmp(&Cursor->primitives, data, sizeof(struct pkt_primitives)); if (PbgpSz) { if (Cursor->cbgp) { struct pkt_bgp_primitives tmp_pbgp; cache_to_pkt_bgp_primitives(&tmp_pbgp, Cursor->cbgp); res_bgp = memcmp(&tmp_pbgp, pbgp, sizeof(struct pkt_bgp_primitives)); } } else res_bgp = FALSE; if (!res_data && !res_bgp) { /* additional check: time */ if ((Cursor->basetime < basetime) && (config.sql_history || config.nfacctd_sql_log)) goto follow_chain; else return Cursor; } else goto follow_chain; } } return NULL; } void sql_cache_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp, struct insert_data *idata) { unsigned int modulo; unsigned long int basetime = idata->basetime, timeslot = idata->timeslot; struct pkt_primitives *srcdst = &data->primitives; struct db_cache *Cursor, *newElem, *SafePtr = NULL, *staleElem = NULL; unsigned int cb_size = sizeof(struct cache_bgp_primitives); if (data->time_start.tv_sec && config.sql_history) { while (basetime > data->time_start.tv_sec) { if (config.sql_history != COUNT_MONTHLY) basetime -= timeslot; else { timeslot = calc_monthly_timeslot(basetime, config.sql_history_howmany, SUB); basetime -= timeslot; } } while ((basetime+timeslot) < data->time_start.tv_sec) { if (config.sql_history != COUNT_MONTHLY) basetime += timeslot; else { basetime += timeslot; timeslot = calc_monthly_timeslot(basetime, config.sql_history_howmany, ADD); } } } /* We are classifing packets. We have a non-zero bytes accumulator (ba) and a non-zero class. Before accounting ba to this class, we have to remove ba from class zero. */ if (config.what_to_count & COUNT_CLASS && data->cst.ba && data->primitives.class) { pm_class_t lclass = data->primitives.class; data->primitives.class = 0; Cursor = sql_cache_search(&data->primitives, pbgp, basetime); data->primitives.class = lclass; /* We can assign the flow to a new class only if we are able to subtract the accumulator from the zero-class. If this is not the case, we will discard the accumulators. The assumption is that accumulators are not retroactive */ if (Cursor) { if (timeval_cmp(&data->cst.stamp, &idata->flushtime) >= 0) { Cursor->bytes_counter -= MIN(Cursor->bytes_counter, data->cst.ba); Cursor->packet_counter -= MIN(Cursor->packet_counter, data->cst.pa); Cursor->flows_counter -= MIN(Cursor->flows_counter, data->cst.fa); } else memset(&data->cst, 0, CSSz); } else memset(&data->cst, 0, CSSz); } sql_cache_modulo(&data->primitives, pbgp, idata); modulo = idata->modulo; /* housekeeping */ if (lru_head.lru_next && ((idata->now-lru_head.lru_next->lru_tag) > RETIRE_M*config.sql_refresh_time)) RetireElem(lru_head.lru_next); Cursor = &cache[idata->modulo]; start: if (idata->hash != Cursor->signature) { if (Cursor->valid == SQL_CACHE_INUSE) { follow_chain: if (Cursor->next) { Cursor = Cursor->next; goto start; } else { if (lru_head.lru_next && ((idata->now-lru_head.lru_next->lru_tag) > STALE_M*config.sql_refresh_time)) { newElem = lru_head.lru_next; if (newElem != Cursor) { ReBuildChain(Cursor, newElem); Cursor = newElem; goto insert; /* we have successfully reused a stale element */ } /* if the last LRU element is our cursor and is still in use, we are forced to abort the LRU idea and create a new brand new element */ else goto create; } else { create: newElem = (struct db_cache *) malloc(sizeof(struct db_cache)); if (newElem) { memset(newElem, 0, sizeof(struct db_cache)); BuildChain(Cursor, newElem); Cursor = newElem; goto insert; /* creating a new element */ } else goto safe_action; /* we should have finished memory */ } } } else goto insert; /* we found a no more valid entry; let's insert here our data */ } else { if (Cursor->valid == SQL_CACHE_INUSE) { int res_data = TRUE, res_bgp = TRUE; /* checks: pkt_primitives and pkt_bgp_primitives */ res_data = memcmp(&Cursor->primitives, srcdst, sizeof(struct pkt_primitives)); if (PbgpSz) { if (Cursor->cbgp) { struct pkt_bgp_primitives tmp_pbgp; cache_to_pkt_bgp_primitives(&tmp_pbgp, Cursor->cbgp); res_bgp = memcmp(&tmp_pbgp, pbgp, sizeof(struct pkt_bgp_primitives)); } } else res_bgp = FALSE; if (!res_data && !res_bgp) { /* additional check: time */ if ((Cursor->basetime < basetime) && (config.sql_history || config.nfacctd_sql_log)) { if (!staleElem && Cursor->chained) staleElem = Cursor; goto follow_chain; } /* additional check: bytes counter overflow */ else if (Cursor->bytes_counter > CACHE_THRESHOLD) { if (!staleElem && Cursor->chained) staleElem = Cursor; goto follow_chain; } else goto update; } else goto follow_chain; } else goto insert; } insert: if (qq_ptr < qq_size) { queries_queue[qq_ptr] = Cursor; qq_ptr++; } else SafePtr = Cursor; /* we add the new entry in the cache */ memcpy(&Cursor->primitives, srcdst, sizeof(struct pkt_primitives)); if (PbgpSz) { if (!Cursor->cbgp) { Cursor->cbgp = (struct cache_bgp_primitives *) malloc(cb_size); memset(Cursor->cbgp, 0, cb_size); } pkt_to_cache_bgp_primitives(Cursor->cbgp, pbgp, config.what_to_count); } else Cursor->cbgp = NULL; Cursor->packet_counter = data->pkt_num; Cursor->flows_counter = data->flo_num; Cursor->bytes_counter = data->pkt_len; Cursor->tcp_flags = data->tcp_flags; if (config.what_to_count & COUNT_CLASS) { Cursor->bytes_counter += data->cst.ba; Cursor->packet_counter += data->cst.pa; Cursor->flows_counter += data->cst.fa; Cursor->tentatives = data->cst.tentatives; } Cursor->valid = SQL_CACHE_INUSE; if (!config.nfacctd_sql_log) { Cursor->basetime = basetime; Cursor->endtime = 0; } else { Cursor->basetime = data->time_start.tv_sec; Cursor->endtime = data->time_end.tv_sec; } Cursor->start_tag = idata->now; Cursor->lru_tag = idata->now; Cursor->signature = idata->hash; /* We are not so fancy to reuse elements which have not been malloc()'d before */ if (Cursor->chained) AddToLRUTail(Cursor); if (SafePtr) goto safe_action; if (staleElem) SwapChainedElems(Cursor, staleElem); return; update: Cursor->packet_counter += data->pkt_num; Cursor->flows_counter += data->flo_num; Cursor->bytes_counter += data->pkt_len; Cursor->tcp_flags |= data->tcp_flags; if (config.what_to_count & COUNT_CLASS) { Cursor->bytes_counter += data->cst.ba; Cursor->packet_counter += data->cst.pa; Cursor->flows_counter += data->cst.fa; Cursor->tentatives = data->cst.tentatives; } return; safe_action: Log(LOG_DEBUG, "DEBUG ( %s/%s ): purging process (CAUSE: safe action)\n", config.name, config.type); if (qq_ptr) sql_cache_flush(queries_queue, qq_ptr, idata, FALSE); switch (fork()) { case 0: /* Child */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); pm_setproctitle("%s [%s]", "SQL Plugin -- DB Writer (urgent)", config.name); if (qq_ptr && sql_writers.flags != CHLD_ALERT) { if (sql_writers.flags == CHLD_WARNING) sql_db_fail(&p); (*sqlfunc_cbr.connect)(&p, config.sql_host); (*sqlfunc_cbr.purge)(queries_queue, qq_ptr, idata); (*sqlfunc_cbr.close)(&bed); } exit(0); default: /* Parent */ qq_ptr = pqq_ptr; memcpy(queries_queue, pending_queries_queue, sizeof(queries_queue)); break; } if (SafePtr) { queries_queue[qq_ptr] = Cursor; qq_ptr++; } else { Cursor = &cache[idata->modulo]; goto start; } } void sql_sum_host_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp, struct insert_data *idata) { struct in_addr ip; #if defined ENABLE_IPV6 struct in6_addr ip6; #endif if (data->primitives.dst_ip.family == AF_INET) { ip.s_addr = data->primitives.dst_ip.address.ipv4.s_addr; data->primitives.dst_ip.address.ipv4.s_addr = 0; data->primitives.dst_ip.family = 0; sql_cache_insert(data, pbgp, idata); data->primitives.src_ip.address.ipv4.s_addr = ip.s_addr; sql_cache_insert(data, pbgp, idata); } #if defined ENABLE_IPV6 if (data->primitives.dst_ip.family == AF_INET6) { memcpy(&ip6, &data->primitives.dst_ip.address.ipv6, sizeof(struct in6_addr)); memset(&data->primitives.dst_ip.address.ipv6, 0, sizeof(struct in6_addr)); data->primitives.dst_ip.family = 0; sql_cache_insert(data, pbgp, idata); memcpy(&data->primitives.src_ip.address.ipv6, &ip6, sizeof(struct in6_addr)); sql_cache_insert(data, pbgp, idata); return; } #endif } void sql_sum_port_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp, struct insert_data *idata) { u_int16_t port; port = data->primitives.dst_port; data->primitives.dst_port = 0; sql_cache_insert(data, pbgp, idata); data->primitives.src_port = port; sql_cache_insert(data, pbgp, idata); } void sql_sum_as_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp, struct insert_data *idata) { as_t asn; asn = data->primitives.dst_as; data->primitives.dst_as = 0; sql_cache_insert(data, pbgp, idata); data->primitives.src_as = asn; sql_cache_insert(data, pbgp, idata); } #if defined (HAVE_L2) void sql_sum_mac_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp, struct insert_data *idata) { u_char macaddr[ETH_ADDR_LEN]; memcpy(macaddr, &data->primitives.eth_dhost, ETH_ADDR_LEN); memset(data->primitives.eth_dhost, 0, ETH_ADDR_LEN); sql_cache_insert(data, pbgp, idata); memcpy(&data->primitives.eth_shost, macaddr, ETH_ADDR_LEN); sql_cache_insert(data, pbgp, idata); } #endif int sql_trigger_exec(char *filename) { char *args[1]; int pid; memset(args, 0, sizeof(args)); switch (pid = vfork()) { case -1: return -1; case 0: execv(filename, args); exit(0); } return 0; } void sql_db_ok(struct DBdesc *db) { db->fail = FALSE; db->connected = TRUE; } void sql_db_fail(struct DBdesc *db) { db->fail = TRUE; db->connected = FALSE; } void sql_db_errmsg(struct DBdesc *db) { if (db->type == BE_TYPE_PRIMARY) Log(LOG_ERR, "ERROR ( %s/%s ): PRIMARY '%s' backend trouble.\n", config.name, config.type, config.type); else if (db->type == BE_TYPE_BACKUP) Log(LOG_ERR, "ERROR ( %s/%s ): BACKUP '%s' backend trouble.\n", config.name, config.type, config.type); if (db->errmsg) Log(LOG_ERR, "ERROR ( %s/%s ): The SQL server says: %s\n\n", config.name, config.type, db->errmsg); } void sql_exit_gracefully(int signum) { struct insert_data idata; signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); Log(LOG_DEBUG, "( %s/%s ) *** Purging queries queue ***\n", config.name, config.type); if (config.syslog) closelog(); memset(&idata, 0, sizeof(idata)); idata.num_primitives = glob_num_primitives; idata.now = time(NULL); idata.basetime = glob_basetime; idata.dyn_table = glob_dyn_table; idata.new_basetime = glob_new_basetime; idata.timeslot = glob_timeslot; idata.committed_basetime = glob_committed_basetime; if (config.sql_backup_host || config.sql_recovery_logfile) idata.recover = TRUE; if (config.what_to_count & COUNT_CLASS) config.sql_aggressive_classification = FALSE; sql_cache_flush(queries_queue, qq_ptr, &idata, TRUE); if (sql_writers.flags != CHLD_ALERT) { if (sql_writers.flags == CHLD_WARNING) sql_db_fail(&p); (*sqlfunc_cbr.connect)(&p, config.sql_host); (*sqlfunc_cbr.purge)(queries_queue, qq_ptr, &idata); (*sqlfunc_cbr.close)(&bed); } exit_plugin(0); } int sql_evaluate_primitives(int primitive) { u_int64_t what_to_count = 0, fakes = 0; short int assume_custom_table = FALSE; char *insert_clause_start_ptr = insert_clause + strlen(insert_clause); char default_delim[] = ",", delim_buf[SRVBUFLEN]; /* SQL tables < v6 multiplex IP addresses and AS numbers on the same field, thus are unable to use both them for a same direction (ie. src, dst). Tables v6 break such assumption */ if (((config.what_to_count & (COUNT_SRC_HOST|COUNT_SRC_NET|COUNT_SUM_HOST|COUNT_SUM_NET) && config.what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS)) || (config.what_to_count & COUNT_DST_AS && config.what_to_count & (COUNT_DST_HOST|COUNT_DST_NET))) && config.sql_table_version < 6) { Log(LOG_ERR, "ERROR ( %s/%s ): SQL tables < v6 are unable to mix IP addresses and AS numbers (ie. src_ip, src_as).\n", config.name, config.type); exit_plugin(1); } if (config.sql_optimize_clauses) { what_to_count = config.what_to_count; assume_custom_table = TRUE; } else { /* It is being requested to avoid SQL query optmization; then we will build an all-true bitmap */ if (config.what_to_count & COUNT_SRC_MAC) what_to_count |= COUNT_SRC_MAC; else if (config.what_to_count & COUNT_SUM_MAC) what_to_count |= COUNT_SUM_MAC; else fakes |= FAKE_SRC_MAC; if (config.what_to_count & COUNT_DST_MAC) what_to_count |= COUNT_DST_MAC; else fakes |= FAKE_DST_MAC; if (config.what_to_count & COUNT_SUM_PORT) what_to_count |= COUNT_SUM_PORT; what_to_count |= COUNT_SRC_PORT|COUNT_DST_PORT|COUNT_TCPFLAGS|COUNT_IP_PROTO|COUNT_CLASS|COUNT_VLAN|COUNT_IP_TOS; if (config.what_to_count & (COUNT_SRC_HOST|COUNT_SRC_NET)) what_to_count |= COUNT_SRC_HOST; else if (config.what_to_count & COUNT_SUM_HOST) what_to_count |= COUNT_SUM_HOST; else if (config.what_to_count & COUNT_SUM_NET) what_to_count |= COUNT_SUM_NET; else fakes |= FAKE_SRC_HOST; if (config.what_to_count & (COUNT_DST_HOST|COUNT_DST_NET)) what_to_count |= COUNT_DST_HOST; else fakes |= FAKE_DST_HOST; if (config.what_to_count & COUNT_AS_PATH) what_to_count |= COUNT_AS_PATH; else fakes |= FAKE_AS_PATH; if (config.what_to_count & COUNT_SRC_AS_PATH) what_to_count |= COUNT_SRC_AS_PATH; if (config.what_to_count & COUNT_STD_COMM) what_to_count |= COUNT_STD_COMM; else if (config.what_to_count & COUNT_EXT_COMM) what_to_count |= COUNT_EXT_COMM; else fakes |= FAKE_COMMS; if (config.what_to_count & COUNT_SRC_STD_COMM) what_to_count |= COUNT_SRC_STD_COMM; else if (config.what_to_count & COUNT_SRC_EXT_COMM) what_to_count |= COUNT_SRC_EXT_COMM; if (config.what_to_count & COUNT_PEER_SRC_AS) what_to_count |= COUNT_PEER_SRC_AS; else fakes |= FAKE_PEER_SRC_AS; if (config.what_to_count & COUNT_PEER_DST_AS) what_to_count |= COUNT_PEER_DST_AS; else fakes |= FAKE_PEER_DST_AS; if (config.what_to_count & COUNT_PEER_SRC_IP) what_to_count |= COUNT_PEER_SRC_IP; else fakes |= FAKE_PEER_SRC_IP; if (config.what_to_count & COUNT_PEER_DST_IP) what_to_count |= COUNT_PEER_DST_IP; else fakes |= FAKE_PEER_DST_IP; what_to_count |= COUNT_LOCAL_PREF|COUNT_MED; if (config.what_to_count & COUNT_SRC_LOCAL_PREF) what_to_count |= COUNT_SRC_LOCAL_PREF; if (config.what_to_count & COUNT_SRC_MED) what_to_count |= COUNT_SRC_MED; if (config.sql_table_version < 6) { if (config.what_to_count & COUNT_SRC_AS) what_to_count |= COUNT_SRC_AS; else if (config.what_to_count & COUNT_SUM_AS) what_to_count |= COUNT_SUM_AS; else fakes |= FAKE_SRC_AS; } else { what_to_count |= COUNT_SRC_AS; if (config.what_to_count & COUNT_SUM_AS) what_to_count |= COUNT_SUM_AS; } if (config.sql_table_version < 6) { if (config.what_to_count & COUNT_DST_AS) what_to_count |= COUNT_DST_AS; else fakes |= FAKE_DST_AS; } else what_to_count |= COUNT_DST_AS; if (config.sql_table_version < 6) { if (what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS)) { if (fakes & FAKE_SRC_HOST) fakes ^= FAKE_SRC_HOST; } else { if (fakes & FAKE_SRC_AS) fakes ^= FAKE_SRC_AS; } if (what_to_count & COUNT_DST_AS) { if (fakes & FAKE_DST_HOST) fakes ^= FAKE_DST_HOST; } else { if (fakes & FAKE_DST_AS) fakes ^= FAKE_DST_AS; } } what_to_count |= COUNT_ID; /* aggregation primitives listed below are not part of any default SQL schema; hence no matter if SQL statements optimization is enabled or not, they have to be passed on blindly */ if (config.what_to_count & COUNT_ID2) what_to_count |= COUNT_ID2; if (config.what_to_count & COUNT_COS) what_to_count |= COUNT_COS; if (config.what_to_count & COUNT_ETHERTYPE) what_to_count |= COUNT_ETHERTYPE; if (config.what_to_count & COUNT_MPLS_VPN_RD) what_to_count |= COUNT_MPLS_VPN_RD; if (config.what_to_count & COUNT_IN_IFACE) what_to_count |= COUNT_IN_IFACE; if (config.what_to_count & COUNT_OUT_IFACE) what_to_count |= COUNT_OUT_IFACE; if (config.what_to_count & COUNT_SRC_NMASK) what_to_count |= COUNT_SRC_NMASK; if (config.what_to_count & COUNT_DST_NMASK) what_to_count |= COUNT_DST_NMASK; } /* sorting out delimiter */ if (!config.sql_delimiter || !config.sql_use_copy) snprintf(delim_buf, SRVBUFLEN, "%s ", default_delim); else snprintf(delim_buf, SRVBUFLEN, "%s ", config.sql_delimiter); /* 1st part: arranging pointers to an opaque structure and composing the static selection (WHERE) string */ #if defined (HAVE_L2) if (what_to_count & (COUNT_SRC_MAC|COUNT_SUM_MAC)) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { Log(LOG_ERR, "ERROR ( %s/%s ): MAC accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mac_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mac_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_MAC; values[primitive].handler = where[primitive].handler = count_src_mac_handler; primitive++; } } if (what_to_count & COUNT_DST_MAC) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { Log(LOG_ERR, "ERROR ( %s/%s ): MAC accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mac_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mac_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_MAC; values[primitive].handler = where[primitive].handler = count_dst_mac_handler; primitive++; } } if (what_to_count & COUNT_VLAN) { int count_it = FALSE; if ((config.sql_table_version < 2 || config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { if (config.what_to_count & COUNT_VLAN) { Log(LOG_ERR, "ERROR ( %s/%s ): VLAN accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else what_to_count ^= COUNT_VLAN; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "vlan", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "vlan=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_VLAN; values[primitive].handler = where[primitive].handler = count_vlan_handler; primitive++; } } if (what_to_count & COUNT_COS) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "cos", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "cos=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_COS; values[primitive].handler = where[primitive].handler = count_cos_handler; primitive++; } if (what_to_count & COUNT_ETHERTYPE) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "etype", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%x\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "etype=\'%x\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_ETHERTYPE; values[primitive].handler = where[primitive].handler = count_etype_handler; primitive++; } #endif if (what_to_count & (COUNT_SRC_HOST|COUNT_SRC_NET|COUNT_SUM_HOST|COUNT_SUM_NET)) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { Log(LOG_ERR, "ERROR ( %s/%s ): IP host accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if ((!strcmp(config.type, "sqlite3") || !strcmp(config.type, "mysql")) && config.num_hosts) { strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "INET_ATON(\'%s\')", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=INET_ATON(\'%s\')", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_HOST; values[primitive].handler = where[primitive].handler = count_src_host_handler; primitive++; } else { strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_HOST; values[primitive].handler = where[primitive].handler = count_src_host_handler; primitive++; } } } if (what_to_count & (COUNT_DST_HOST|COUNT_DST_NET)) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { Log(LOG_ERR, "ERROR ( %s/%s ): IP host accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if ((!strcmp(config.type, "sqlite3") || !strcmp(config.type, "mysql")) && config.num_hosts) { strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "INET_ATON(\'%s\')", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=INET_ATON(\'%s\')", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_HOST; values[primitive].handler = where[primitive].handler = count_dst_host_handler; primitive++; } else { strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_HOST; values[primitive].handler = where[primitive].handler = count_dst_host_handler; primitive++; } } } if (what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (config.sql_table_version >= 6) { strncat(insert_clause, "as_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "as_src=%u", SPACELEFT(where[primitive].string)); } else { strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); if (!strcmp(config.type, "mysql") || !strcmp(config.type, "sqlite3") || (!strcmp(config.type, "pgsql") && !strcmp(config.sql_data, "unified"))) { strncat(values[primitive].string, "\'%u\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=\'%u\'", SPACELEFT(where[primitive].string)); } else { strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=%u", SPACELEFT(where[primitive].string)); } } values[primitive].type = where[primitive].type = COUNT_SRC_AS; values[primitive].handler = where[primitive].handler = count_src_as_handler; primitive++; } if (what_to_count & COUNT_IN_IFACE) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "iface_in", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "iface_in=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_IN_IFACE; values[primitive].handler = where[primitive].handler = count_in_iface_handler; primitive++; } if (what_to_count & COUNT_OUT_IFACE) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "iface_out", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "iface_out=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_OUT_IFACE; values[primitive].handler = where[primitive].handler = count_out_iface_handler; primitive++; } if (what_to_count & COUNT_SRC_NMASK) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mask_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mask_src=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_NMASK; values[primitive].handler = where[primitive].handler = count_src_nmask_handler; primitive++; } if (what_to_count & COUNT_DST_NMASK) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mask_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mask_dst=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_NMASK; values[primitive].handler = where[primitive].handler = count_dst_nmask_handler; primitive++; } if (what_to_count & COUNT_DST_AS) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if (config.sql_table_version >= 6) { strncat(insert_clause, "as_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "as_dst=%u", SPACELEFT(where[primitive].string)); } else { strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); if (!strcmp(config.type, "mysql") || !strcmp(config.type, "sqlite3") || (!strcmp(config.type, "pgsql") && !strcmp(config.sql_data, "unified"))) { strncat(values[primitive].string, "\'%u\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=\'%u\'", SPACELEFT(where[primitive].string)); } else { strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=%u", SPACELEFT(where[primitive].string)); } } values[primitive].type = where[primitive].type = COUNT_DST_AS; values[primitive].handler = where[primitive].handler = count_dst_as_handler; primitive++; } if (what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM)) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { Log(LOG_ERR, "ERROR ( %s/%s ): BGP accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "comms", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "comms=\'%s\'", SPACELEFT(where[primitive].string)); if (what_to_count & COUNT_STD_COMM) { values[primitive].type = where[primitive].type = COUNT_STD_COMM; values[primitive].handler = where[primitive].handler = count_std_comm_handler; } else if (what_to_count & COUNT_EXT_COMM) { values[primitive].type = where[primitive].type = COUNT_EXT_COMM; values[primitive].handler = where[primitive].handler = count_ext_comm_handler; } primitive++; } } if (what_to_count & (COUNT_SRC_STD_COMM|COUNT_SRC_EXT_COMM)) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "comms_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "comms_src=\'%s\'", SPACELEFT(where[primitive].string)); if (what_to_count & COUNT_SRC_STD_COMM) { values[primitive].type = where[primitive].type = COUNT_SRC_STD_COMM; values[primitive].handler = where[primitive].handler = count_src_std_comm_handler; } else if (what_to_count & COUNT_SRC_EXT_COMM) { values[primitive].type = where[primitive].type = COUNT_SRC_EXT_COMM; values[primitive].handler = where[primitive].handler = count_src_ext_comm_handler; } primitive++; } if (what_to_count & COUNT_AS_PATH) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { Log(LOG_ERR, "ERROR ( %s/%s ): BGP accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "as_path", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "as_path=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_AS_PATH; values[primitive].handler = where[primitive].handler = count_as_path_handler; primitive++; } } if (what_to_count & COUNT_SRC_AS_PATH) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "as_path_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "as_path_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_AS_PATH; values[primitive].handler = where[primitive].handler = count_src_as_path_handler; primitive++; } if (what_to_count & COUNT_LOCAL_PREF) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { if (config.what_to_count & COUNT_LOCAL_PREF) { Log(LOG_ERR, "ERROR ( %s/%s ): BGP accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else what_to_count ^= COUNT_LOCAL_PREF; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "local_pref", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "local_pref=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_LOCAL_PREF; values[primitive].handler = where[primitive].handler = count_local_pref_handler; primitive++; } } if (what_to_count & COUNT_SRC_LOCAL_PREF) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "local_pref_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "local_pref_src=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_LOCAL_PREF; values[primitive].handler = where[primitive].handler = count_src_local_pref_handler; primitive++; } if (what_to_count & COUNT_MED) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { if (config.what_to_count & COUNT_MED) { Log(LOG_ERR, "ERROR ( %s/%s ): BGP accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else what_to_count ^= COUNT_MED; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "med", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "med=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_MED; values[primitive].handler = where[primitive].handler = count_med_handler; primitive++; } } if (what_to_count & COUNT_SRC_MED) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "med_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "med_src=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_MED; values[primitive].handler = where[primitive].handler = count_src_med_handler; primitive++; } if (what_to_count & COUNT_MPLS_VPN_RD) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mpls_vpn_rd", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mpls_vpn_rd=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_MPLS_VPN_RD; values[primitive].handler = where[primitive].handler = count_mpls_vpn_rd_handler; primitive++; } if (what_to_count & COUNT_PEER_SRC_AS) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { Log(LOG_ERR, "ERROR ( %s/%s ): BGP accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "peer_as_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_as_src=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_PEER_SRC_AS; values[primitive].handler = where[primitive].handler = count_peer_src_as_handler; primitive++; } } if (what_to_count & COUNT_PEER_DST_AS) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { Log(LOG_ERR, "ERROR ( %s/%s ): BGP accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "peer_as_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_as_dst=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_PEER_DST_AS; values[primitive].handler = where[primitive].handler = count_peer_dst_as_handler; primitive++; } } if (what_to_count & COUNT_PEER_SRC_IP) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { Log(LOG_ERR, "ERROR ( %s/%s ): BGP accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if ((!strcmp(config.type, "sqlite3") || !strcmp(config.type, "mysql")) && config.num_hosts) { strncat(insert_clause, "peer_ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "INET_ATON(\'%s\')", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_ip_src=INET_ATON(\'%s\')", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_PEER_SRC_IP; values[primitive].handler = where[primitive].handler = count_peer_src_ip_handler; primitive++; } else { strncat(insert_clause, "peer_ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_ip_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_PEER_SRC_IP; values[primitive].handler = where[primitive].handler = count_peer_src_ip_handler; primitive++; } } } if (what_to_count & COUNT_PEER_DST_IP) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { Log(LOG_ERR, "ERROR ( %s/%s ): BGP accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if ((!strcmp(config.type, "sqlite3") || !strcmp(config.type, "mysql")) && config.num_hosts) { strncat(insert_clause, "peer_ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "INET_ATON(\'%s\')", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_ip_dst=INET_ATON(\'%s\')", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_PEER_DST_IP; values[primitive].handler = where[primitive].handler = count_peer_dst_ip_handler; primitive++; } else { strncat(insert_clause, "peer_ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_ip_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_PEER_DST_IP; values[primitive].handler = where[primitive].handler = count_peer_dst_ip_handler; primitive++; } } } if (what_to_count & (COUNT_SRC_PORT|COUNT_SUM_PORT)) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { if (config.what_to_count & (COUNT_SRC_PORT|COUNT_SUM_PORT)) { Log(LOG_ERR, "ERROR ( %s/%s ): TCP/UDP port accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else { if (what_to_count & COUNT_SRC_PORT) what_to_count ^= COUNT_SRC_PORT; if (what_to_count & COUNT_SUM_PORT) what_to_count ^= COUNT_SUM_PORT; } } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if ((!strcmp(config.type, "mysql") || !strcmp(config.type, "sqlite3")) && config.sql_table_version != 8) { strncat(insert_clause, "src_port", SPACELEFT(insert_clause)); strncat(where[primitive].string, "src_port=%u", SPACELEFT(where[primitive].string)); } else { strncat(insert_clause, "port_src", SPACELEFT(insert_clause)); strncat(where[primitive].string, "port_src=%u", SPACELEFT(where[primitive].string)); } strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); values[primitive].type = where[primitive].type = COUNT_SRC_PORT; values[primitive].handler = where[primitive].handler = count_src_port_handler; primitive++; } } if (what_to_count & COUNT_DST_PORT) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { if (config.what_to_count & COUNT_DST_PORT) { Log(LOG_ERR, "ERROR ( %s/%s ): TCP/UDP port accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else what_to_count ^= COUNT_DST_PORT; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if ((!strcmp(config.type, "mysql") || !strcmp(config.type, "sqlite3")) && config.sql_table_version != 8) { strncat(insert_clause, "dst_port", SPACELEFT(insert_clause)); strncat(where[primitive].string, "dst_port=%u", SPACELEFT(where[primitive].string)); } else { strncat(insert_clause, "port_dst", SPACELEFT(insert_clause)); strncat(where[primitive].string, "port_dst=%u", SPACELEFT(where[primitive].string)); } strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); values[primitive].type = where[primitive].type = COUNT_DST_PORT; values[primitive].handler = where[primitive].handler = count_dst_port_handler; primitive++; } } if (what_to_count & COUNT_TCPFLAGS) { int count_it = FALSE; if ((config.sql_table_version < 7 || config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { if (config.what_to_count & COUNT_TCPFLAGS) { Log(LOG_ERR, "ERROR ( %s/%s ): TCP flags accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else what_to_count ^= COUNT_TCPFLAGS; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); } strncat(insert_clause, "tcp_flags", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); values[primitive].type = where[primitive].type = COUNT_TCPFLAGS; values[primitive].handler = where[primitive].handler = count_tcpflags_handler; primitive++; } } if (what_to_count & COUNT_IP_TOS) { int count_it = FALSE; if ((config.sql_table_version < 3 || config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { if (config.what_to_count & COUNT_IP_TOS) { Log(LOG_ERR, "ERROR ( %s/%s ): IP ToS accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else what_to_count ^= COUNT_IP_TOS; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "tos", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "tos=%u", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_IP_TOS; values[primitive].handler = where[primitive].handler = count_ip_tos_handler; primitive++; } } if (what_to_count & COUNT_IP_PROTO) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { if (config.what_to_count & COUNT_IP_PROTO) { Log(LOG_ERR, "ERROR ( %s/%s ): IP proto accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else what_to_count ^= COUNT_IP_PROTO; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_proto", SPACELEFT(insert_clause)); if ((!strcmp(config.type, "sqlite3") || !strcmp(config.type, "mysql")) && !config.num_protos) { strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_proto=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].handler = where[primitive].handler = MY_count_ip_proto_handler; } else { strncat(values[primitive].string, "%u", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_proto=%u", SPACELEFT(where[primitive].string)); values[primitive].handler = where[primitive].handler = PG_count_ip_proto_handler; } values[primitive].type = where[primitive].type = COUNT_IP_PROTO; primitive++; } } if (what_to_count & COUNT_ID) { int count_it = FALSE; if ((config.sql_table_version < 2) && !assume_custom_table) { if (config.what_to_count & COUNT_ID) { Log(LOG_ERR, "ERROR ( %s/%s ): Tag/ID accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else what_to_count ^= COUNT_ID; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "agent_id", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%llu", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "agent_id=%llu", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_ID; values[primitive].handler = where[primitive].handler = count_id_handler; primitive++; } } if (what_to_count & COUNT_ID2) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "agent_id2", SPACELEFT(insert_clause)); strncat(values[primitive].string, "%llu", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "agent_id2=%llu", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_ID2; values[primitive].handler = where[primitive].handler = count_id2_handler; primitive++; } if (what_to_count & COUNT_CLASS) { int count_it = FALSE; if ((config.sql_table_version < 5 || config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { if (config.what_to_count & COUNT_CLASS) { Log(LOG_ERR, "ERROR ( %s/%s ): L7 classification accounting not supported for selected sql_table_version/_type. Read about SQL table versioning or consider using sql_optimize_clauses.\n", config.name, config.type); exit_plugin(1); } else what_to_count ^= COUNT_CLASS; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "class_id", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "class_id=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = COUNT_CLASS; values[primitive].handler = where[primitive].handler = count_class_id_handler; primitive++; } } #if defined (HAVE_L2) if (fakes & FAKE_SRC_MAC) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { fakes ^= FAKE_SRC_MAC; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mac_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mac_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_SRC_MAC; values[primitive].handler = where[primitive].handler = fake_mac_handler; primitive++; } } if (fakes & FAKE_DST_MAC) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { fakes ^= FAKE_DST_MAC; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "mac_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "mac_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_DST_MAC; values[primitive].handler = where[primitive].handler = fake_mac_handler; primitive++; } } #endif if (fakes & FAKE_SRC_HOST) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { fakes ^= FAKE_SRC_HOST; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if ((!strcmp(config.type, "sqlite3") || !strcmp(config.type, "mysql")) && config.num_hosts) { strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "INET_ATON(\'%s\')", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=INET_ATON(\'%s\')", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_SRC_HOST; values[primitive].handler = where[primitive].handler = fake_host_handler; primitive++; } else { strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_SRC_HOST; values[primitive].handler = where[primitive].handler = fake_host_handler; primitive++; } } } if (fakes & FAKE_DST_HOST) { int count_it = FALSE; if ((config.sql_table_version >= SQL_TABLE_VERSION_BGP) && !assume_custom_table) { fakes ^= FAKE_DST_HOST; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if ((!strcmp(config.type, "sqlite3") || !strcmp(config.type, "mysql")) && config.num_hosts) { strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "INET_ATON(\'%s\')", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=INET_ATON(\'%s\')", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_DST_HOST; values[primitive].handler = where[primitive].handler = fake_host_handler; primitive++; } else { strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_DST_HOST; values[primitive].handler = where[primitive].handler = fake_host_handler; primitive++; } } } if (fakes & FAKE_SRC_AS) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_src", SPACELEFT(insert_clause)); if (!strcmp(config.type, "mysql") || !strcmp(config.type, "sqlite3") || (!strcmp(config.type, "pgsql") && !strcmp(config.sql_data, "unified"))) { strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=\'%s\'", SPACELEFT(where[primitive].string)); } else { strncat(values[primitive].string, "%s", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_src=%s", SPACELEFT(where[primitive].string)); } values[primitive].type = where[primitive].type = FAKE_SRC_AS; values[primitive].handler = where[primitive].handler = fake_as_handler; primitive++; } if (fakes & FAKE_DST_AS) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "ip_dst", SPACELEFT(insert_clause)); if (!strcmp(config.type, "mysql") || !strcmp(config.type, "sqlite3") || (!strcmp(config.type, "pgsql") && !strcmp(config.sql_data, "unified"))) { strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=\'%s\'", SPACELEFT(where[primitive].string)); } else { strncat(values[primitive].string, "%s", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "ip_dst=%s", SPACELEFT(where[primitive].string)); } values[primitive].type = where[primitive].type = FAKE_DST_AS; values[primitive].handler = where[primitive].handler = fake_as_handler; primitive++; } if (fakes & FAKE_COMMS) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { fakes ^= FAKE_COMMS; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "comms", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "comms=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_COMMS; values[primitive].handler = where[primitive].handler = fake_comms_handler; primitive++; } } if (fakes & FAKE_AS_PATH) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { fakes ^= FAKE_AS_PATH; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "as_path", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "as_path=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_AS_PATH; values[primitive].handler = where[primitive].handler = fake_as_path_handler; primitive++; } } if (fakes & FAKE_PEER_SRC_AS) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { fakes ^= FAKE_PEER_SRC_AS; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "peer_as_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_as_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_PEER_SRC_AS; values[primitive].handler = where[primitive].handler = fake_as_handler; primitive++; } } if (fakes & FAKE_PEER_DST_AS) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { fakes ^= FAKE_PEER_DST_AS; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } strncat(insert_clause, "peer_as_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_as_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_PEER_DST_AS; values[primitive].handler = where[primitive].handler = fake_as_handler; primitive++; } } if (fakes & FAKE_PEER_SRC_IP) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { fakes ^= FAKE_PEER_SRC_IP; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if ((!strcmp(config.type, "sqlite3") || !strcmp(config.type, "mysql")) && config.num_hosts) { strncat(insert_clause, "peer_ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "INET_ATON(\'%s\')", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_ip_src=INET_ATON(\'%s\')", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_PEER_SRC_IP; values[primitive].handler = where[primitive].handler = fake_host_handler; primitive++; } else { strncat(insert_clause, "peer_ip_src", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_ip_src=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_PEER_SRC_IP; values[primitive].handler = where[primitive].handler = fake_host_handler; primitive++; } } } if (fakes & FAKE_PEER_DST_IP) { int count_it = FALSE; if ((config.sql_table_version < SQL_TABLE_VERSION_BGP) && !assume_custom_table) { fakes ^= FAKE_PEER_DST_IP; } else count_it = TRUE; if (count_it) { if (primitive) { strncat(insert_clause, ", ", SPACELEFT(insert_clause)); strncat(values[primitive].string, delim_buf, sizeof(values[primitive].string)); strncat(where[primitive].string, " AND ", sizeof(where[primitive].string)); } if ((!strcmp(config.type, "sqlite3") || !strcmp(config.type, "mysql")) && config.num_hosts) { strncat(insert_clause, "peer_ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "INET_ATON(\'%s\')", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_ip_dst=INET_ATON(\'%s\')", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_PEER_DST_IP; values[primitive].handler = where[primitive].handler = fake_host_handler; primitive++; } else { strncat(insert_clause, "peer_ip_dst", SPACELEFT(insert_clause)); strncat(values[primitive].string, "\'%s\'", SPACELEFT(values[primitive].string)); strncat(where[primitive].string, "peer_ip_dst=\'%s\'", SPACELEFT(where[primitive].string)); values[primitive].type = where[primitive].type = FAKE_PEER_DST_IP; values[primitive].handler = where[primitive].handler = fake_host_handler; primitive++; } } } strncat(copy_clause, insert_clause_start_ptr, SPACELEFT(copy_clause)); return primitive; } int sql_query(struct BE_descs *bed, struct db_cache *elem, struct insert_data *idata) { if (!bed->p->fail && elem->valid == SQL_CACHE_COMMITTED) { if ((*sqlfunc_cbr.op)(bed->p, elem, idata)); /* failed */ else { idata->qn++; return FALSE; } } if ( elem->valid == SQL_CACHE_ERROR || (bed->p->fail && !(elem->valid == SQL_CACHE_INUSE)) ) { if (config.sql_backup_host) { if (!bed->b->fail) { if (!bed->b->connected) { (*sqlfunc_cbr.connect)(bed->b, config.sql_backup_host); if (config.sql_table_schema) { time_t stamp = idata->new_basetime ? idata->new_basetime : idata->basetime; sql_create_table(bed->b, &stamp); } (*sqlfunc_cbr.lock)(bed->b); } if (!bed->b->fail) { if ((*sqlfunc_cbr.op)(bed->b, elem, idata)) sql_db_fail(bed->b); } } } if (config.sql_recovery_logfile) { int sz; if (idata->mv.last_queue_elem) goto quit; if (!bed->lf->fail) { if (!bed->lf->open) { bed->lf->file = sql_file_open(config.sql_recovery_logfile, "a", idata); if (bed->lf->file) bed->lf->open = TRUE; else { bed->lf->open = FALSE; bed->lf->fail = TRUE; } } if (!bed->lf->fail) { sz = TPL_push(logbuf.ptr, elem); logbuf.ptr += sz; if ((logbuf.ptr+sz) > logbuf.end) { /* we test whether the next element will fit into the buffer */ fwrite(logbuf.base, (logbuf.ptr-logbuf.base), 1, bed->lf->file); logbuf.ptr = logbuf.base; } } } } } quit: return TRUE; } FILE *sql_file_open(const char *path, const char *mode, const struct insert_data *idata) { struct stat st, st2; struct logfile_header lh; struct template_header tth; FILE *f; int ret; uid_t owner = -1; gid_t group = -1; if (config.files_uid) owner = config.files_uid; if (config.files_gid) group = config.files_gid; file_open: f = fopen(path, "a+"); if (f) { chown(path, owner, group); if (file_lock(fileno(f))) { Log(LOG_ALERT, "ALERT ( %s/%s ): Unable to obtain lock of '%s'.\n", config.name, config.type, path); goto close; } fstat(fileno(f), &st); if (!st.st_size) { memset(&lh, 0, sizeof(struct logfile_header)); strlcpy(lh.sql_db, config.sql_db, DEF_HDR_FIELD_LEN); if (!idata->dyn_table) strlcpy(lh.sql_table, config.sql_table, DEF_HDR_FIELD_LEN); else { struct tm *nowtm; nowtm = localtime(&idata->new_basetime); strftime(lh.sql_table, DEF_HDR_FIELD_LEN, config.sql_table, nowtm); } strlcpy(lh.sql_user, config.sql_user, DEF_HDR_FIELD_LEN); if (config.sql_host) strlcpy(lh.sql_host, config.sql_host, DEF_HDR_FIELD_LEN); else lh.sql_host[0] = '\0'; lh.sql_table_version = config.sql_table_version; lh.sql_table_version = htons(lh.sql_table_version); lh.sql_optimize_clauses = config.sql_optimize_clauses; lh.sql_optimize_clauses = htons(lh.sql_optimize_clauses); lh.sql_history = config.sql_history; lh.sql_history = htons(lh.sql_history); lh.what_to_count = htonl(config.what_to_count); lh.magic = htonl(MAGIC); fwrite(&lh, sizeof(lh), 1, f); fwrite(&th, sizeof(th), 1, f); fwrite(te, ntohs(th.num)*sizeof(struct template_entry), 1, f); } else { rewind(f); fread(&lh, sizeof(lh), 1, f); if (ntohl(lh.magic) != MAGIC) { Log(LOG_ALERT, "ALERT ( %s/%s ): Invalid magic number: '%s'.\n", config.name, config.type, path); goto close; } fread(&tth, sizeof(tth), 1, f); if ((tth.num != th.num) || (tth.sz != th.sz)) { Log(LOG_ALERT, "ALERT ( %s/%s ): Invalid template in: '%s'.\n", config.name, config.type, path); goto close; } if ((st.st_size+(idata->ten*sizeof(struct pkt_data))) >= MAX_LOGFILE_SIZE) { Log(LOG_INFO, "INFO ( %s/%s ): No more space in '%s'.\n", config.name, config.type, path); /* We reached the maximum logfile length; we test if any previous process has already rotated the logfile. If not, we will rotate it. */ stat(path, &st2); if (st2.st_size >= st.st_size) { ret = file_archive(path, MAX_LOGFILE_ROTATIONS); if (ret < 0) goto close; } file_unlock(fileno(f)); fclose(f); goto file_open; } fseek(f, 0, SEEK_END); } } return f; close: file_unlock(fileno(f)); fclose(f); return NULL; } void sql_create_table(struct DBdesc *db, time_t *basetime) { struct tm *nowtm; char buf[LARGEBUFLEN], tmpbuf[LARGEBUFLEN]; int ret; ret = read_SQLquery_from_file(config.sql_table_schema, tmpbuf, LARGEBUFLEN); if (ret) { nowtm = localtime(basetime); strftime(buf, LARGEBUFLEN, tmpbuf, nowtm); (*sqlfunc_cbr.create_table)(db, buf); } } void sql_invalidate_shadow_entries(struct db_cache *queue[], int *num) { int x; for (x = 0; x < *num; x++) { if (!queue[x]->bytes_counter && !queue[x]->packet_counter && !queue[x]->flows_counter) queue[x]->valid = SQL_CACHE_FREE; } } int sql_select_locking_style(char *lock) { int i = 0, len = strlen(lock); while (i < len) { lock[i] = tolower(lock[i]); i++; } if (!strcmp(lock, "table")) return PM_LOCK_EXCLUSIVE; else if (!strcmp(lock, "row")) return PM_LOCK_ROW_EXCLUSIVE; Log(LOG_WARNING, "WARN ( %s/%s ): sql_locking_style value '%s' is unknown. Ignored.\n", config.name, config.type, lock); return PM_LOCK_EXCLUSIVE; } int sql_compose_static_set(int have_flows) { int set_primitives=0; #if defined HAVE_64BIT_COUNTERS strncpy(set[set_primitives].string, "SET packets=packets+%llu, bytes=bytes+%llu", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = COUNT_COUNTERS; set[set_primitives].handler = count_counters_setclause_handler; set_primitives++; if (have_flows) { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "flows=flows+%llu", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = COUNT_FLOWS; set[set_primitives].handler = count_flows_setclause_handler; set_primitives++; } #else strncpy(set[set_primitives].string, "SET packets=packets+%u, bytes=bytes+%u", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = COUNT_COUNTERS; set[set_primitives].handler = count_counters_setclause_handler; set_primitives++; if (have_flows) { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "flows=flows+%u", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = COUNT_FLOWS; set[set_primitives].handler = count_flows_setclause_handler; set_primitives++; } #endif if (config.what_to_count & COUNT_TCPFLAGS) { strncpy(set[set_primitives].string, ", ", SPACELEFT(set[set_primitives].string)); strncat(set[set_primitives].string, "tcp_flags=tcp_flags|%u", SPACELEFT(set[set_primitives].string)); set[set_primitives].type = COUNT_TCPFLAGS; set[set_primitives].handler = count_tcpflags_setclause_handler; set_primitives++; } return set_primitives; } pmacct-0.14.0/src/cfg.h0000644000175000017500000001377211736323743013573 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #include "cfg_handlers.h" #include "bgp/bgp_prefix.h" /* defines */ #define CFG_LINE_LEN(x) (SRVBUFLEN-strlen(x)) /* structures */ struct _dictionary_line { char key[SRVBUFLEN]; int (*func)(char *, char *, char *); }; struct configuration { u_int64_t what_to_count; u_int64_t nfprobe_what_to_count; char *name; char *type; int sock; int acct_type; int data_type; int pipe_size; int pipe_backlog; int buffer_size; int files_umask; int files_uid; int files_gid; int handle_fragments; int handle_flows; int frag_bufsz; int flow_bufsz; int flow_hashsz; int conntrack_bufsz; int flow_lifetime; int num_protos; int num_hosts; char *imt_plugin_path; char *imt_plugin_passwd; char *sql_db; char *sql_table; char *sql_table_schema; int sql_table_version; char *sql_table_type; char *sql_user; char *sql_passwd; char *sql_host; char *sql_data; char *sql_backup_host; int sql_optimize_clauses; int sql_refresh_time; int sql_history; int sql_history_howmany; /* internal */ int sql_history_since_epoch; int sql_startup_delay; int sql_cache_entries; int sql_dont_try_update; char *sql_history_roundoff; char *sql_recovery_logfile; int sql_max_writers; int sql_trigger_time; int sql_trigger_time_howmany; /* internal */ char *sql_trigger_exec; int sql_max_queries; char *sql_preprocess; int sql_preprocess_type; int sql_multi_values; int sql_aggressive_classification; char *sql_locking_style; int sql_use_copy; char *sql_delimiter; int print_refresh_time; int print_cache_entries; int print_markers; int print_output; char *print_output_file; int nfacctd_port; char *nfacctd_ip; char *nfacctd_allow_file; int nfacctd_time; u_int32_t nfacctd_as; u_int32_t nfacctd_net; int sfacctd_renormalize; int nfacctd_disable_checks; int nfacctd_sql_log; int nfacctd_bgp; int nfacctd_bgp_msglog; char *nfacctd_bgp_ip; int nfacctd_bgp_port; int nfacctd_bgp_ipprec; char *nfacctd_bgp_allow_file; int nfacctd_bgp_max_peers; int nfacctd_bgp_aspath_radius; char *nfacctd_bgp_stdcomm_pattern; char *nfacctd_bgp_extcomm_pattern; char *nfacctd_bgp_stdcomm_pattern_to_asn; int nfacctd_bgp_peer_as_src_type; int nfacctd_bgp_src_std_comm_type; int nfacctd_bgp_src_ext_comm_type; int nfacctd_bgp_src_as_path_type; int nfacctd_bgp_src_local_pref_type; int nfacctd_bgp_src_med_type; int nfacctd_bgp_peer_as_skip_subas; char *nfacctd_bgp_peer_as_src_map; char *nfacctd_bgp_src_local_pref_map; char *nfacctd_bgp_src_med_map; char *nfacctd_bgp_to_agent_map; char *nfacctd_bgp_iface_to_rd_map; int nfacctd_bgp_follow_default; struct prefix nfacctd_bgp_follow_nexthop[FOLLOW_BGP_NH_ENTRIES]; char *nfacctd_bgp_neighbors_file; char *nfacctd_bgp_md5_file; int bgp_table_peer_buckets; int nfacctd_isis; char *nfacctd_isis_ip; char *nfacctd_isis_net; char *nfacctd_isis_iface; int nfacctd_isis_mtu; int nfacctd_isis_msglog; int promisc; /* pcap_open_live() promisc parameter */ char *clbuf; /* pcap filter */ char *pcap_savefile; char *dev; int if_wait; int sf_wait; int num_memory_pools; int memory_pool_size; int buckets; int daemon; int active_plugins; char *logfile; FILE *logfile_fd; char *pidfile; int networks_mask; char *networks_file; int networks_cache_entries; char *ports_file; int refresh_maps; char *a_filter; int bpfp_a_num; struct bpf_program *bpfp_a_table[AGG_FILTER_ENTRIES]; struct pretag_filter ptf; struct pretag_filter pt2f; char *pre_tag_map; int pre_tag_map_entries; pm_id_t post_tag; int ext_sampling_rate; int sampling_rate; char *sampling_map; char *syslog; int debug; int snaplen; char *classifiers_path; int classifier_tentatives; int classifier_table_num; char *nfprobe_timeouts; int nfprobe_id; int nfprobe_hoplimit; int nfprobe_maxflows; char *nfprobe_receiver; int nfprobe_version; char *nfprobe_engine; int nfprobe_peer_as; char *nfprobe_source_ip; struct host_addr nfprobe_source_ha; int nfprobe_ipprec; int nfprobe_direction; u_int32_t nfprobe_ifindex; int nfprobe_ifindex_type; char *sfprobe_receiver; char *sfprobe_agentip; int sfprobe_agentsubid; u_int64_t sfprobe_ifspeed; int tee_transparent; int uacctd_group; int uacctd_nl_size; char *tunnel0; int xlate_src; int xlate_dst; }; struct plugin_type_entry { int id; char string[10]; void (*func)(int, struct configuration *, void *); }; struct plugins_list_entry { int id; pid_t pid; char name[SRVBUFLEN]; struct configuration cfg; int pipe[2]; struct plugin_type_entry type; struct plugins_list_entry *next; }; /* prototypes */ #if (!defined __CFG_C) #define EXT extern #else #define EXT #endif EXT void evaluate_configuration(char *, int); EXT int parse_configuration_file(char *); EXT int parse_plugin_names(char *, int, int); EXT int create_plugin(char *, char *, char *); EXT int delete_plugin_by_id(int); EXT struct plugins_list_entry *search_plugin_by_pipe(int); EXT struct plugins_list_entry *search_plugin_by_pid(pid_t); EXT void sanitize_cfg(int, char *); EXT void set_default_values(); /* global vars */ EXT char *cfg[SRVBUFLEN], *cfg_cmdline[SRVBUFLEN]; EXT int rows; #undef EXT pmacct-0.14.0/src/pmacctd.c0000644000175000017500000007231211741105354014425 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* defines */ #define __PMACCTD_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "pmacct-dlt.h" #include "pretag_handlers.h" #include "plugin_hooks.h" #include "pkt_handlers.h" #include "ip_frag.h" #include "ip_flow.h" #include "net_aggr.h" #include "thread_pool.h" /* variables to be exported away */ int debug; struct configuration config; /* global configuration */ struct plugins_list_entry *plugins_list = NULL; /* linked list of each plugin configuration */ struct channels_list_entry channels_list[MAX_N_PLUGINS]; /* communication channels: core <-> plugins */ int have_num_memory_pools; /* global getopt() stuff */ pid_t failed_plugins[MAX_N_PLUGINS]; /* plugins failed during startup phase */ u_char dummy_tlhdr[16]; /* Functions */ void usage_daemon(char *prog_name) { printf("%s\n", PMACCTD_USAGE_HEADER); printf("Usage: %s [ -D | -d ] [ -i interface ] [ -c primitive [ , ... ] ] [ -P plugin [ , ... ] ] [ filter ]\n", prog_name); printf(" %s [ -f config_file ]\n", prog_name); printf(" %s [ -h ]\n", prog_name); printf("\nGeneral options:\n"); printf(" -h \tShow this page\n"); printf(" -f \tLoad configuration from the specified file\n"); printf(" -c \t[ src_mac | dst_mac | vlan | src_host | dst_host | src_net | dst_net | src_port | dst_port |\n\t proto | tos | src_as | dst_as | sum_mac | sum_host | sum_net | sum_as | sum_port | tag |\n\t tag2 | flows | class | tcpflags | in_iface | out_iface | src_mask | dst_mask | cos | etype | none ] \n\tAggregation string (DEFAULT: src_host)\n"); printf(" -D \tDaemonize\n"); printf(" -N \tDisable promiscuous mode\n"); printf(" -n \tPath to a file containing Network definitions\n"); printf(" -o \tPath to a file containing Port definitions\n"); printf(" -P \t[ memory | print | mysql | pgsql | sqlite3 | nfprobe | sfprobe ] \n\tActivate plugin\n"); printf(" -d \tEnable debug\n"); printf(" -i \tListen on the specified interface\n"); printf(" -I \tRead packets from the specified savefile\n"); printf(" -S \t[ auth | mail | daemon | kern | user | local[0-7] ] \n\tLog to the specified syslog facility\n"); printf(" -F \tWrite Core Process PID into the specified file\n"); printf(" -w \tWait for the listening interface to become available\n"); printf(" -W \tReading from a savefile, don't exit but sleep when finished\n"); printf(" -R \tRenormalize sampled data\n"); printf(" -L \tSet snapshot length\n"); printf(" -u \tLeave IP protocols in numerical format\n"); printf("\nMemory plugin (-P memory) options:\n"); printf(" -p \tSocket for client-server communication (DEFAULT: /tmp/collect.pipe)\n"); printf(" -b \tNumber of buckets\n"); printf(" -m \tNumber of memory pools\n"); printf(" -s \tMemory pool size\n"); printf("\nPostgreSQL (-P pgsql)/MySQL (-P mysql)/SQLite (-P sqlite3) plugin options:\n"); printf(" -r \tRefresh time (in seconds)\n"); printf(" -v \t[ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ] \n\tTable version\n"); printf("\nPrint plugin (-P print) plugin options:\n"); printf(" -r \tRefresh time (in seconds)\n"); printf(" -O \t[ formatted | csv ] \n\tOutput format\n"); printf("\n"); printf(" See EXAMPLES or visit http://wiki.pmacct.net/ for examples.\n"); printf("\n"); printf("For suggestions, critics, bugs, contact me: %s.\n", MANTAINER); } int main(int argc,char **argv, char **envp) { bpf_u_int32 localnet, netmask; /* pcap library stuff */ struct bpf_program filter; struct pcap_device device; char errbuf[PCAP_ERRBUF_SIZE]; int index, logf; struct plugins_list_entry *list; struct plugin_requests req; char config_file[SRVBUFLEN]; int psize = DEFAULT_SNAPLEN; struct id_table bpas_table; struct id_table blp_table; struct id_table bmed_table; struct id_table biss_table; struct id_table bta_table; struct id_table idt; struct pcap_callback_data cb_data; /* getopt() stuff */ extern char *optarg; extern int optind, opterr, optopt; int errflag, cp; #if defined ENABLE_IPV6 struct sockaddr_storage client; #else struct sockaddr client; #endif umask(077); compute_once(); /* a bunch of default definitions */ have_num_memory_pools = FALSE; reload_map = FALSE; tag_map_allocated = FALSE; bpas_map_allocated = FALSE; blp_map_allocated = FALSE; bmed_map_allocated = FALSE; biss_map_allocated = FALSE; find_id_func = PM_find_id; errflag = 0; memset(cfg_cmdline, 0, sizeof(cfg_cmdline)); memset(&config, 0, sizeof(struct configuration)); memset(&device, 0, sizeof(struct pcap_device)); memset(&config_file, 0, sizeof(config_file)); memset(&failed_plugins, 0, sizeof(failed_plugins)); memset(&req, 0, sizeof(req)); memset(dummy_tlhdr, 0, sizeof(dummy_tlhdr)); memset(sll_mac, 0, sizeof(sll_mac)); memset(&bpas_table, 0, sizeof(bpas_table)); memset(&blp_table, 0, sizeof(blp_table)); memset(&bmed_table, 0, sizeof(bmed_table)); memset(&biss_table, 0, sizeof(biss_table)); memset(&bta_table, 0, sizeof(bta_table)); memset(&client, 0, sizeof(client)); memset(&cb_data, 0, sizeof(cb_data)); memset(&tunnel_registry, 0, sizeof(tunnel_registry)); config.acct_type = ACCT_PM; rows = 0; glob_pcapt = NULL; /* getting commandline values */ while (!errflag && ((cp = getopt(argc, argv, ARGS_PMACCTD)) != -1)) { cfg_cmdline[rows] = malloc(SRVBUFLEN); switch (cp) { case 'P': strlcpy(cfg_cmdline[rows], "plugins: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'D': strlcpy(cfg_cmdline[rows], "daemonize: true", SRVBUFLEN); rows++; break; case 'd': debug = TRUE; strlcpy(cfg_cmdline[rows], "debug: true", SRVBUFLEN); rows++; break; case 'n': strlcpy(cfg_cmdline[rows], "networks_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'o': strlcpy(cfg_cmdline[rows], "ports_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'O': strlcpy(cfg_cmdline[rows], "print_output: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'u': strlcpy(cfg_cmdline[rows], "print_num_protos: true", SRVBUFLEN); rows++; break; case 'N': strlcpy(cfg_cmdline[rows], "promisc: false", SRVBUFLEN); rows++; break; case 'f': strlcpy(config_file, optarg, sizeof(config_file)); break; case 'F': strlcpy(cfg_cmdline[rows], "pidfile: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'c': strlcpy(cfg_cmdline[rows], "aggregate: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'b': strlcpy(cfg_cmdline[rows], "imt_buckets: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'm': strlcpy(cfg_cmdline[rows], "imt_mem_pools_number: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); have_num_memory_pools = TRUE; rows++; break; case 'p': strlcpy(cfg_cmdline[rows], "imt_path: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'r': strlcpy(cfg_cmdline[rows], "sql_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; cfg_cmdline[rows] = malloc(SRVBUFLEN); strlcpy(cfg_cmdline[rows], "print_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'v': strlcpy(cfg_cmdline[rows], "sql_table_version: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 's': strlcpy(cfg_cmdline[rows], "imt_mem_pools_size: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'S': strlcpy(cfg_cmdline[rows], "syslog: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'i': strlcpy(cfg_cmdline[rows], "interface: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'I': strlcpy(cfg_cmdline[rows], "pcap_savefile: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'w': strlcpy(cfg_cmdline[rows], "interface_wait: true", SRVBUFLEN); rows++; break; case 'W': strlcpy(cfg_cmdline[rows], "savefile_wait: true", SRVBUFLEN); rows++; break; case 'L': strlcpy(cfg_cmdline[rows], "snaplen: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'R': strlcpy(cfg_cmdline[rows], "sfacctd_renormalize: true", SRVBUFLEN); rows++; break; case 'h': usage_daemon(argv[0]); exit(0); break; default: usage_daemon(argv[0]); exit(1); break; } } /* post-checks and resolving conflicts */ if (strlen(config_file)) { if (parse_configuration_file(config_file) != SUCCESS) exit(1); } else { if (parse_configuration_file(NULL) != SUCCESS) exit(1); } /* XXX: glue; i'm conscious it's a dirty solution from an engineering viewpoint; someday later i'll fix this */ list = plugins_list; while(list) { list->cfg.acct_type = ACCT_PM; set_default_preferences(&list->cfg); if (!strcmp(list->name, "default") && !strcmp(list->type.string, "core")) memcpy(&config, &list->cfg, sizeof(struct configuration)); list = list->next; } if (config.files_umask) umask(config.files_umask); /* Let's check whether we need superuser privileges */ if (config.snaplen) psize = config.snaplen; else config.snaplen = psize; if (!config.pcap_savefile) { if (getuid() != 0) { printf("%s\n\n", PMACCTD_USAGE_HEADER); printf("ERROR ( default/core ): You need superuser privileges to run this command.\nExiting ...\n\n"); exit(1); } } if (config.daemon) { list = plugins_list; while (list) { if (!strcmp(list->type.string, "print")) printf("WARN ( default/core ): Daemonizing. Hmm, bye bye screen.\n"); list = list->next; } if (debug || config.debug) printf("WARN ( default/core ): debug is enabled; forking in background. Console logging will get lost.\n"); daemonize(); } initsetproctitle(argc, argv, envp); if (config.syslog) { logf = parse_log_facility(config.syslog); if (logf == ERR) { config.syslog = NULL; printf("WARN ( default/core ): specified syslog facility is not supported; logging to console.\n"); } else openlog(NULL, LOG_PID, logf); Log(LOG_INFO, "INFO ( default/core ): Start logging ...\n"); } if (config.logfile) { config.logfile_fd = open_logfile(config.logfile); list = plugins_list; while (list) { list->cfg.logfile_fd = config.logfile_fd ; list = list->next; } } /* Enforcing policies over aggregation methods */ list = plugins_list; while (list) { if (list->type.id != PLUGIN_ID_CORE) { /* applies to all plugins */ if (config.classifiers_path && (list->cfg.sampling_rate || config.ext_sampling_rate)) { Log(LOG_ERR, "ERROR ( default/core ): Packet sampling and classification are mutual exclusive.\n"); exit(1); } if (list->cfg.sampling_rate && config.ext_sampling_rate) { Log(LOG_ERR, "ERROR ( default/core ): Internal packet sampling and external packet sampling are mutual exclusive.\n"); exit(1); } if (list->type.id == PLUGIN_ID_TEE) { Log(LOG_ERR, "ERROR ( default/core ): 'tee' plugin not supported in 'pmacctd'.\n"); exit(1); } else if (list->type.id == PLUGIN_ID_NFPROBE) { /* If we already renormalizing an external sampling rate, we cancel the sampling information from the probe plugin */ if (config.sfacctd_renormalize && list->cfg.ext_sampling_rate) list->cfg.ext_sampling_rate = 0; config.handle_fragments = TRUE; list->cfg.nfprobe_what_to_count = list->cfg.what_to_count; list->cfg.what_to_count = 0; #if defined (HAVE_L2) if (list->cfg.nfprobe_version == 9 || list->cfg.nfprobe_version == 10) { list->cfg.what_to_count |= COUNT_SRC_MAC; list->cfg.what_to_count |= COUNT_DST_MAC; list->cfg.what_to_count |= COUNT_VLAN; } #endif list->cfg.what_to_count |= COUNT_SRC_HOST; list->cfg.what_to_count |= COUNT_DST_HOST; if (list->cfg.networks_file || list->cfg.networks_mask || list->cfg.nfacctd_net) { list->cfg.what_to_count |= COUNT_SRC_NMASK; list->cfg.what_to_count |= COUNT_DST_NMASK; } list->cfg.what_to_count |= COUNT_SRC_PORT; list->cfg.what_to_count |= COUNT_DST_PORT; list->cfg.what_to_count |= COUNT_IP_TOS; list->cfg.what_to_count |= COUNT_IP_PROTO; if (list->cfg.networks_file || (list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP)) { list->cfg.what_to_count |= COUNT_SRC_AS; list->cfg.what_to_count |= COUNT_DST_AS; list->cfg.what_to_count |= COUNT_PEER_DST_IP; } if ((list->cfg.nfprobe_version == 9 || list->cfg.nfprobe_version == 10) && list->cfg.classifiers_path) { list->cfg.what_to_count |= COUNT_CLASS; config.handle_flows = TRUE; } if (list->cfg.pre_tag_map) { list->cfg.what_to_count |= COUNT_ID; list->cfg.what_to_count |= COUNT_ID2; } list->cfg.what_to_count |= COUNT_IN_IFACE; list->cfg.what_to_count |= COUNT_OUT_IFACE; if (list->cfg.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF| COUNT_MPLS_VPN_RD)) { Log(LOG_ERR, "ERROR ( default/core ): 'src_as', 'dst_as' and 'peer_dst_ip' are currently the only BGP-related primitives supported within the 'nfprobe' plugin.\n"); exit(1); } list->cfg.what_to_count |= COUNT_COUNTERS; list->cfg.data_type = PIPE_TYPE_METADATA; list->cfg.data_type |= PIPE_TYPE_EXTRAS; } else if (list->type.id == PLUGIN_ID_SFPROBE) { /* If we already renormalizing an external sampling rate, we cancel the sampling information from the probe plugin */ if (config.sfacctd_renormalize && list->cfg.ext_sampling_rate) list->cfg.ext_sampling_rate = 0; if (psize < 128) psize = config.snaplen = 128; /* SFL_DEFAULT_HEADER_SIZE */ list->cfg.what_to_count = COUNT_PAYLOAD; if (list->cfg.classifiers_path) { list->cfg.what_to_count |= COUNT_CLASS; config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP) { list->cfg.what_to_count |= COUNT_SRC_AS; list->cfg.what_to_count |= COUNT_DST_AS; list->cfg.what_to_count |= COUNT_PEER_DST_IP; } if ((list->cfg.nfacctd_bgp && list->cfg.nfacctd_net == NF_NET_BGP) || (list->cfg.nfacctd_isis && list->cfg.nfacctd_net == NF_NET_IGP)) { list->cfg.what_to_count |= COUNT_SRC_NMASK; list->cfg.what_to_count |= COUNT_DST_NMASK; } if (list->cfg.pre_tag_map) { list->cfg.what_to_count |= COUNT_ID; list->cfg.what_to_count |= COUNT_ID2; } if (list->cfg.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF| COUNT_MPLS_VPN_RD)) { Log(LOG_ERR, "ERROR ( default/core ): 'src_as', 'dst_as' and 'peer_dst_ip' are currently the only BGP-related primitives supported within the 'sfprobe' plugin.\n"); exit(1); } #if defined (HAVE_L2) list->cfg.what_to_count |= COUNT_VLAN; list->cfg.what_to_count |= COUNT_COS; #endif list->cfg.data_type = PIPE_TYPE_PAYLOAD; } else { evaluate_sums(&list->cfg.what_to_count, list->name, list->type.string); if (list->cfg.what_to_count & (COUNT_SRC_PORT|COUNT_DST_PORT|COUNT_SUM_PORT|COUNT_TCPFLAGS)) config.handle_fragments = TRUE; if (list->cfg.what_to_count & COUNT_FLOWS) { config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (list->cfg.what_to_count & COUNT_CLASS) { config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (!list->cfg.what_to_count) { Log(LOG_WARNING, "WARN ( %s/%s ): defaulting to SRC HOST aggregation.\n", list->name, list->type.string); list->cfg.what_to_count |= COUNT_SRC_HOST; } if ((list->cfg.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) && !list->cfg.networks_file && list->cfg.nfacctd_as != NF_AS_BGP) { Log(LOG_ERR, "ERROR ( %s/%s ): AS aggregation selected but NO 'networks_file' or 'pmacctd_as' are specified. Exiting...\n\n", list->name, list->type.string); exit(1); } if (list->cfg.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET|COUNT_SUM_NET|COUNT_SRC_NMASK|COUNT_DST_NMASK|COUNT_PEER_DST_IP)) { if (!list->cfg.nfacctd_net) { if (list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; if (list->cfg.networks_mask) list->cfg.nfacctd_net |= NF_NET_STATIC; if (!list->cfg.nfacctd_net) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'pmacctd_net', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } } else { if ((list->cfg.nfacctd_net == NF_NET_NEW && !list->cfg.networks_file) || (list->cfg.nfacctd_net == NF_NET_STATIC && !list->cfg.networks_mask) || (list->cfg.nfacctd_net == NF_NET_BGP && !list->cfg.nfacctd_bgp) || (list->cfg.nfacctd_net == NF_NET_IGP && !list->cfg.nfacctd_isis) || (list->cfg.nfacctd_net == NF_NET_KEEP)) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'bgp_daemon', 'isis_daemon', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } } } if (list->cfg.what_to_count & COUNT_CLASS && !list->cfg.classifiers_path) { Log(LOG_ERR, "ERROR ( %s/%s ): 'class' aggregation selected but NO 'classifiers' key specified. Exiting...\n\n", list->name, list->type.string); exit(1); } bgp_config_checks(&list->cfg); list->cfg.what_to_count |= COUNT_COUNTERS; list->cfg.data_type |= PIPE_TYPE_METADATA; } } list = list->next; } /* plugins glue: creation (since 094) */ if (config.classifiers_path) { init_classifiers(config.classifiers_path); init_conntrack_table(); } load_plugins(&req); if (config.handle_fragments) init_ip_fragment_handler(); if (config.handle_flows) init_ip_flow_handler(); load_networks(config.networks_file, &nt, &nc); /* If any device/savefile have been specified, choose a suitable device where to listen for traffic */ if (!config.dev && !config.pcap_savefile) { Log(LOG_WARNING, "WARN ( default/core ): Selecting a suitable device.\n"); config.dev = pcap_lookupdev(errbuf); if (!config.dev) { Log(LOG_WARNING, "WARN ( default/core ): Unable to find a suitable device. Exiting.\n"); exit_all(1); } else Log(LOG_DEBUG, "DEBUG ( default/core ): device is %s\n", config.dev); } /* reading filter; if it exists, we'll take an action later */ if (!strlen(config_file)) config.clbuf = copy_argv(&argv[optind]); if (config.dev && config.pcap_savefile) { Log(LOG_ERR, "ERROR ( default/core ): 'interface' (-i) and 'pcap_savefile' (-I) directives are mutually exclusive. Exiting.\n"); exit_all(1); } throttle_startup: if (config.dev) { if ((device.dev_desc = pcap_open_live(config.dev, psize, config.promisc, 1000, errbuf)) == NULL) { if (!config.if_wait) { Log(LOG_ERR, "ERROR ( default/core ): pcap_open_live(): %s\n", errbuf); exit_all(1); } else { sleep(5); /* XXX: user defined ? */ goto throttle_startup; } } } else if (config.pcap_savefile) { if ((device.dev_desc = pcap_open_offline(config.pcap_savefile, errbuf)) == NULL) { Log(LOG_ERR, "ERROR ( default/core ): pcap_open_offline(): %s\n", errbuf); exit_all(1); } } device.active = TRUE; glob_pcapt = device.dev_desc; /* SIGINT/stats handling */ if (config.pipe_size) { int slen = sizeof(config.pipe_size), x; #if defined (PCAP_TYPE_linux) || (PCAP_TYPE_snoop) Setsocksize(pcap_fileno(device.dev_desc), SOL_SOCKET, SO_RCVBUF, &config.pipe_size, slen); getsockopt(pcap_fileno(device.dev_desc), SOL_SOCKET, SO_RCVBUF, &x, &slen); Log(LOG_DEBUG, "DEBUG ( default/core ): PCAP buffer: obtained %d / %d bytes.\n", x, config.pipe_size); #endif } device.link_type = pcap_datalink(device.dev_desc); for (index = 0; _devices[index].link_type != -1; index++) { if (device.link_type == _devices[index].link_type) device.data = &_devices[index]; } load_plugin_filters(device.link_type); /* we need to solve some link constraints */ if (device.data == NULL) { Log(LOG_ERR, "ERROR ( default/core ): data link not supported: %d\n", device.link_type); exit_all(1); } else Log(LOG_INFO, "OK ( default/core ): link type is: %d\n", device.link_type); if (device.link_type != DLT_EN10MB && device.link_type != DLT_IEEE802 && device.link_type != DLT_LINUX_SLL) { list = plugins_list; while (list) { if ((list->cfg.what_to_count & COUNT_SRC_MAC) || (list->cfg.what_to_count & COUNT_DST_MAC)) { Log(LOG_ERR, "ERROR ( default/core ): MAC aggregation not available for link type: %d\n", device.link_type); exit_all(1); } list = list->next; } } cb_data.device = &device; /* doing pcap stuff */ if (!config.dev || pcap_lookupnet(config.dev, &localnet, &netmask, errbuf) < 0) { localnet = 0; netmask = 0; Log(LOG_WARNING, "WARN ( default/core ): %s\n", errbuf); } if (pcap_compile(device.dev_desc, &filter, config.clbuf, 0, netmask) < 0) Log(LOG_WARNING, "WARN: %s\nWARN ( default/core ): going on without a filter\n", pcap_geterr(device.dev_desc)); else { if (pcap_setfilter(device.dev_desc, &filter) < 0) Log(LOG_WARNING, "WARN: %s\nWARN ( default/core ): going on without a filter\n", pcap_geterr(device.dev_desc)); } /* signal handling we want to inherit to plugins (when not re-defined elsewhere) */ signal(SIGCHLD, startup_handle_falling_child); /* takes note of plugins failed during startup phase */ signal(SIGHUP, reload); /* handles reopening of syslog channel */ signal(SIGUSR1, push_stats); /* logs various statistics via Log() calls */ signal(SIGUSR2, reload_maps); /* sets to true the reload_maps flag */ signal(SIGPIPE, SIG_IGN); /* we want to exit gracefully when a pipe is broken */ /* loading pre-tagging map, if any */ if (config.pre_tag_map) { load_id_file(config.acct_type, config.pre_tag_map, &idt, &req, &tag_map_allocated); cb_data.idt = (u_char *) &idt; } else { memset(&idt, 0, sizeof(idt)); cb_data.idt = NULL; } #if defined ENABLE_THREADS /* starting the ISIS threa */ if (config.nfacctd_isis) { req.bpf_filter = TRUE; nfacctd_isis_wrapper(); /* Let's give the ISIS thread some advantage to create its structures */ sleep(5); } /* starting the BGP thread */ if (config.nfacctd_bgp) { req.bpf_filter = TRUE; load_comm_patterns(&config.nfacctd_bgp_stdcomm_pattern, &config.nfacctd_bgp_extcomm_pattern, &config.nfacctd_bgp_stdcomm_pattern_to_asn); if (config.nfacctd_bgp_peer_as_src_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_peer_as_src_map) { load_id_file(MAP_BGP_PEER_AS_SRC, config.nfacctd_bgp_peer_as_src_map, &bpas_table, &req, &bpas_map_allocated); cb_data.bpas_table = (u_char *) &bpas_table; } else { Log(LOG_ERR, "ERROR: bgp_peer_as_src_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.bpas_table = NULL; if (config.nfacctd_bgp_src_local_pref_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_local_pref_map) { load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.nfacctd_bgp_src_local_pref_map, &blp_table, &req, &blp_map_allocated); cb_data.blp_table = (u_char *) &blp_table; } else { Log(LOG_ERR, "ERROR: bgp_src_local_pref_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.blp_table = NULL; if (config.nfacctd_bgp_src_med_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_med_map) { load_id_file(MAP_BGP_SRC_MED, config.nfacctd_bgp_src_med_map, &bmed_table, &req, &bmed_map_allocated); cb_data.bmed_table = (u_char *) &bmed_table; } else { Log(LOG_ERR, "ERROR: bgp_src_med_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.bmed_table = NULL; if (config.nfacctd_bgp_to_agent_map) { load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.nfacctd_bgp_to_agent_map, &bta_table, &req, &bta_map_allocated); cb_data.bta_table = (u_char *) &bta_table; } else { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_daemon' configured but no 'bgp_agent_map' has been specified. Exiting.\n"); exit(1); } /* Limiting BGP peers to only two: one would suffice in pmacctd but in case maps are reloadable (ie. bta), it could be handy to keep a backup feed in memory */ config.nfacctd_bgp_max_peers = 2; if (config.nfacctd_bgp_iface_to_rd_map) { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_iface_to_rd_map' is not supported by this daemon. Exiting.\n"); exit(1); } cb_data.f_agent = (char *)&client; nfacctd_bgp_wrapper(); /* Let's give the BGP thread some advantage to create its structures */ sleep(5); } #else if (config.nfacctd_isis) { Log(LOG_ERR, "ERROR ( default/core ): 'isis_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } if (config.nfacctd_bgp) { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } #endif /* Init tunnel handlers */ tunnel_registry_init(); /* plugins glue: creation (until 093) */ evaluate_packet_handlers(); pm_setproctitle("%s [%s]", "Core Process", "default"); if (config.pidfile) write_pid_file(config.pidfile); /* signals to be handled only by pmacctd; we set proper handlers after plugin creation */ signal(SIGINT, my_sigint_handler); signal(SIGTERM, my_sigint_handler); signal(SIGCHLD, handle_falling_child); kill(getpid(), SIGCHLD); /* When reading packets from a savefile, things are lightning fast; we will sit here just few seconds, thus allowing plugins to complete their startup operations */ if (config.pcap_savefile) { Log(LOG_INFO, "INFO ( default/core ): PCAP capture file, sleeping for 2 seconds\n"); sleep(2); } /* Main loop: if pcap_loop() exits maybe an error occurred; we will try closing and reopening again our listening device */ for(;;) { if (!device.active) { Log(LOG_WARNING, "WARN ( default/core ): %s has become unavailable; throttling ...\n", config.dev); throttle_loop: sleep(5); /* XXX: user defined ? */ if ((device.dev_desc = pcap_open_live(config.dev, psize, config.promisc, 1000, errbuf)) == NULL) goto throttle_loop; pcap_setfilter(device.dev_desc, &filter); device.active = TRUE; } pcap_loop(device.dev_desc, -1, pcap_cb, (u_char *) &cb_data); pcap_close(device.dev_desc); if (config.pcap_savefile) { if (config.sf_wait) { fill_pipe_buffer(); Log(LOG_INFO, "INFO ( default/core ): finished reading PCAP capture file\n"); wait(NULL); } stop_all_childs(); } device.active = FALSE; } } /* Dummy objects here - ugly to see but well portable */ void NF_find_id(struct id_table *t, struct packet_ptrs *pptrs, pm_id_t *tag, pm_id_t *tag2) { } void SF_find_id(struct id_table *t, struct packet_ptrs *pptrs, pm_id_t *tag, pm_id_t *tag2) { } pmacct-0.14.0/src/sqlite3_plugin.h0000644000175000017500000000375111453022224015754 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente */ /* 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. */ /* includes */ #include /* prototypes */ void sqlite3_plugin(int, struct configuration *, void *); int SQLI_cache_dbop(struct DBdesc *, struct db_cache *, struct insert_data *); void SQLI_cache_purge(struct db_cache *[], int, struct insert_data *); int SQLI_evaluate_history(int); int SQLI_compose_static_queries(); void SQLI_Lock(struct DBdesc *); void SQLI_Unlock(struct BE_descs *); void SQLI_DB_Connect(struct DBdesc *, char *); void SQLI_DB_Close(struct BE_descs *); void SQLI_create_dyn_table(struct DBdesc *, char *); void SQLI_get_errmsg(struct DBdesc *); void SQLI_create_backend(struct DBdesc *); void SQLI_set_callbacks(struct sqlfunc_cb_registry *); void SQLI_init_default_values(struct insert_data *); /* variables */ static char sqlite3_db[] = "/tmp/pmacct.db"; static char sqlite3_table[] = "acct"; static char sqlite3_table_v2[] = "acct_v2"; static char sqlite3_table_v3[] = "acct_v3"; static char sqlite3_table_v4[] = "acct_v4"; static char sqlite3_table_v5[] = "acct_v5"; static char sqlite3_table_v6[] = "acct_v6"; static char sqlite3_table_v7[] = "acct_v7"; static char sqlite3_table_v8[] = "acct_v8"; static char sqlite3_table_bgp[] = "acct_bgp"; pmacct-0.14.0/src/regmagic.h0000644000175000017500000000023110530072467014567 0ustar paolopaolo/* * The first byte of the regexp internal "program" is actually this magic * number; the start node begins in the second byte. */ #define MAGIC 0234 pmacct-0.14.0/src/imt_plugin.h0000644000175000017500000001060111460653755015172 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2010 by Paolo Lucente */ /* 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. */ #include /* defines */ #define NUM_MEMORY_POOLS 16 #define MEMORY_POOL_SIZE 8192 #define MAX_HOSTS 32771 #define MAX_QUERIES 4096 /* Structures */ struct acc { struct pkt_primitives primitives; pm_counter_t bytes_counter; pm_counter_t packet_counter; pm_counter_t flow_counter; u_int32_t tcp_flags; unsigned int signature; u_int8_t reset_flag; struct timeval rstamp; /* classifiers: reset timestamp */ struct cache_bgp_primitives *cbgp; struct acc *next; }; struct bucket_desc { unsigned int num; unsigned short int howmany; }; struct memory_pool_desc { int id; unsigned char *base_ptr; unsigned char *ptr; int space_left; int len; struct memory_pool_desc *next; }; struct query_header { int type; /* type of query */ u_int64_t what_to_count; /* aggregation */ unsigned int num; /* number of queries */ unsigned int ip_sz; /* IP addresses size (in bytes) */ unsigned int cnt_sz; /* counters size (in bytes) */ char passwd[12]; /* OBSOLETED: password */ }; struct query_entry { u_int64_t what_to_count; /* aggregation */ struct pkt_primitives data; /* actual data */ struct pkt_bgp_primitives pbgp; /* extended BGP data */ }; struct reply_buffer { unsigned char buf[LARGEBUFLEN]; unsigned char *ptr; int len; int packed; }; struct stripped_class { pm_class_t id; char protocol[MAX_PROTOCOL_LEN]; }; /* prototypes */ #if (!defined __ACCT_C) #define EXT extern #else #define EXT #endif EXT void insert_accounting_structure(struct pkt_data *, struct pkt_bgp_primitives *); EXT struct acc *search_accounting_structure(struct pkt_primitives *, struct pkt_bgp_primitives *); EXT int compare_accounting_structure(struct acc *, struct pkt_primitives *, struct pkt_bgp_primitives *); #undef EXT #if (!defined __MEMORY_C) #define EXT extern #else #define EXT #endif EXT void init_memory_pool_table(); EXT void clear_memory_pool_table(); EXT struct memory_pool_desc *request_memory_pool(int); #undef EXT #if (!defined __SERVER_C) #define EXT extern #else #define EXT #endif EXT void set_reset_flag(struct acc *); EXT void reset_counters(struct acc *); EXT int build_query_server(char *); EXT void process_query_data(int, unsigned char *, int, int); EXT void mask_elem(struct pkt_primitives *, struct pkt_bgp_primitives *, struct acc *, u_int64_t); EXT void enQueue_elem(int, struct reply_buffer *, void *, int, int); EXT void Accumulate_Counters(struct pkt_data *, struct acc *); #undef EXT #if (!defined __IMT_PLUGIN_C) #define EXT extern #else #define EXT #endif EXT void sum_host_insert(struct pkt_data *, struct pkt_bgp_primitives *); EXT void sum_port_insert(struct pkt_data *, struct pkt_bgp_primitives *); EXT void sum_as_insert(struct pkt_data *, struct pkt_bgp_primitives *); #if defined HAVE_L2 EXT void sum_mac_insert(struct pkt_data *, struct pkt_bgp_primitives *); #endif EXT void exit_now(int); EXT void free_bgp_allocs(); #undef EXT /* global vars */ #if (!defined __IMT_PLUGIN_C && !defined __PMACCT_CLIENT_C) #define EXT extern #else #define EXT #endif EXT void (*insert_func)(struct pkt_data *, struct pkt_bgp_primitives *); /* pointer to INSERT function */ EXT unsigned char *mpd; /* memory pool descriptors table */ EXT unsigned char *a; /* accounting in-memory table */ EXT struct memory_pool_desc *current_pool; /* pointer to currently used memory pool */ EXT struct acc **lru_elem_ptr; /* pointer to Last Recently Used (lru) element in a bucket */ EXT int no_more_space; EXT struct timeval cycle_stamp; /* timestamp for the current cycle */ EXT struct timeval table_reset_stamp; /* global table reset timestamp */ #undef EXT pmacct-0.14.0/src/regexp.h0000644000175000017500000000202011027472602014276 0ustar paolopaolo/* * Definitions etc. for regexp(3) routines. * * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], * not the System V one. */ #ifndef REGEXP_H #define REGEXP_H /* http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , which contains a version of this library, says: * * NSUBEXP must be at least 10, and no greater than 117 or the parser * will not work properly. * However, it looks rather like this library is limited to 10. If you think otherwise, let us know. */ #define NSUBEXP 10 typedef struct regexp { char *startp[NSUBEXP]; char *endp[NSUBEXP]; char regstart; /* Internal use only. */ char reganch; /* Internal use only. */ char *regmust; /* Internal use only. */ int regmlen; /* Internal use only. */ char program[1]; /* Unwarranted chumminess with compiler. */ } regexp; regexp * pm_regcomp(char *exp, int *patternsize); int pm_regexec(regexp *prog, char *string); void pm_regsub(regexp *prog, char *source, char *dest); void pm_regerror(char *s); #endif pmacct-0.14.0/src/util.h0000644000175000017500000000676711741022360014002 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* defines */ #define ADD 0 #define SUB 1 #define DEBUG_TIMING 0 /* prototypes */ #if (!defined __UTIL_C) #define EXT extern #else #define EXT #endif EXT void setnonblocking(int); EXT void setblocking(int); EXT int daemonize(); EXT char *copy_argv(register char **); EXT char *extract_token(char **, int); EXT char *extract_plugin_name(char **); EXT void trim_spaces(char *); EXT void trim_all_spaces(char *); EXT void strip_quotes(char *); EXT int isblankline(char *); EXT int iscomment(char *); EXT int check_not_valid_char(char *, char *, int); EXT time_t roundoff_time(time_t, char *); EXT time_t calc_monthly_timeslot(time_t, int, int); EXT void write_pid_file(char *); EXT void write_pid_file_plugin(char *, char *, char *); EXT void remove_pid_file(char *); EXT int sanitize_buf_net(char *, char *, int); EXT int sanitize_buf(char *); EXT void mark_columns(char *); EXT int Setsocksize(int, int, int, void *, int); EXT void *map_shared(void *, size_t, int, int, int, off_t); EXT void lower_string(char *); EXT void evaluate_sums(u_int64_t *, char *, char *); EXT int file_archive(const char *, int); EXT void stop_all_childs(); EXT int file_lock(int); EXT int file_unlock(int); EXT void strftime_same(char *, int, char *, const time_t *); EXT int read_SQLquery_from_file(char *, char *, int); EXT void stick_bosbit(u_char *); EXT int timeval_cmp(struct timeval *, struct timeval *); EXT void exit_all(int); EXT void exit_plugin(int); EXT void reset_tag_status(struct packet_ptrs_vector *); EXT void reset_net_status(struct packet_ptrs *); EXT void reset_net_status_v(struct packet_ptrs_vector *); EXT void reset_shadow_status(struct packet_ptrs_vector *); EXT void reset_fallback_status(struct packet_ptrs *); EXT void set_sampling_table(struct packet_ptrs_vector *, u_char *); EXT void set_shadow_status(struct packet_ptrs *); EXT void set_default_preferences(struct configuration *); EXT FILE *open_logfile(char *); EXT FILE *open_print_output_file(char *, time_t); EXT void evaluate_bgp_aspath_radius(char *, int, int); EXT void copy_stdcomm_to_asn(char *, as_t *, int); EXT void *Malloc(unsigned int); EXT void load_allow_file(char *, struct hosts_table *); EXT int check_allow(struct hosts_table *, struct sockaddr *); EXT void load_bgp_md5_file(char *, struct bgp_md5_table *); EXT void unload_bgp_md5_file(struct bgp_md5_table *); EXT unsigned int str_to_addr(const char *, struct host_addr *); EXT struct packet_ptrs *copy_packet_ptrs(struct packet_ptrs *); EXT void free_packet_ptrs(struct packet_ptrs *); #undef EXT /* Timing Stuff */ #if DEBUG_TIMING struct mytimer { struct timeval t0; struct timeval t1; }; void start_timer(struct mytimer *); void stop_timer(struct mytimer *, const char *, ...); #endif pmacct-0.14.0/src/sql_common_m.c0000644000175000017500000002230011306525417015470 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente */ /* 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. */ #define __SQL_COMMON_M_C Inline void AddToLRUTail(struct db_cache *Cursor) { if (Cursor == lru_tail) return; if (Cursor->lru_prev) Cursor->lru_prev->lru_next = Cursor->lru_next; if (Cursor->lru_next) Cursor->lru_next->lru_prev = Cursor->lru_prev; Cursor->lru_prev = lru_tail; Cursor->lru_prev->lru_next = Cursor; Cursor->lru_next = NULL; lru_tail = Cursor; } Inline void RetireElem(struct db_cache *Cursor) { assert(Cursor->prev); Cursor->lru_prev->lru_next = Cursor->lru_next; if (Cursor->lru_next) Cursor->lru_next->lru_prev = Cursor->lru_prev; if (Cursor == lru_tail) lru_tail = &lru_head; if (Cursor->next) { Cursor->prev->next = Cursor->next; Cursor->next->prev = Cursor->prev; } else Cursor->prev->next = NULL; if (Cursor->cbgp) { if (Cursor->cbgp->std_comms) free(Cursor->cbgp->std_comms); if (Cursor->cbgp->ext_comms) free(Cursor->cbgp->ext_comms); if (Cursor->cbgp->as_path) free(Cursor->cbgp->as_path); if (Cursor->cbgp->src_std_comms) free(Cursor->cbgp->src_std_comms); if (Cursor->cbgp->src_ext_comms) free(Cursor->cbgp->src_ext_comms); if (Cursor->cbgp->src_as_path) free(Cursor->cbgp->src_as_path); free(Cursor->cbgp); } free(Cursor); } Inline void BuildChain(struct db_cache *Cursor, struct db_cache *newElem) { Cursor->next = newElem; newElem->prev = Cursor; newElem->chained = TRUE; } Inline void ReBuildChain(struct db_cache *Cursor, struct db_cache *newElem) { assert(Cursor != newElem); if (newElem->next) { newElem->prev->next = newElem->next; newElem->next->prev = newElem->prev; } else newElem->prev->next = NULL; Cursor->next = newElem; newElem->prev = Cursor; newElem->next = NULL; } Inline void SwapChainedElems(struct db_cache *Cursor, struct db_cache *staleElem) { struct db_cache *auxPtr; assert(Cursor != staleElem); assert(Cursor->prev); assert(staleElem->prev); /* Specific cases first */ if (Cursor == staleElem->prev) { staleElem->prev = Cursor->prev; Cursor->next = staleElem->next; staleElem->next = Cursor; Cursor->prev = staleElem; staleElem->prev->next = staleElem; if (Cursor->next) Cursor->next->prev = Cursor; } else if (staleElem == Cursor->prev) { Cursor->prev = staleElem->prev; staleElem->next = Cursor->next; Cursor->next = staleElem; staleElem->prev = Cursor; Cursor->prev->next = Cursor; if (staleElem->next) staleElem->next->prev = staleElem; } /* General case */ else { auxPtr = Cursor->prev; Cursor->prev = staleElem->prev; Cursor->prev->next = Cursor; staleElem->prev = auxPtr; staleElem->prev->next = staleElem; auxPtr = Cursor->next; Cursor->next = staleElem->next; if (Cursor->next) Cursor->next->prev = Cursor; staleElem->next = auxPtr; if (staleElem->next) staleElem->next->prev = staleElem; } } Inline void SQL_SetENV() { u_char *ptrs[16]; int count = 0, i; INIT_BUF(envbuf); memset(ptrs, 0, sizeof(ptrs)); if (config.sql_db) { strncat(envbuf.ptr, "SQL_DB=", envbuf.end-envbuf.ptr); strncat(envbuf.ptr, config.sql_db, envbuf.end-envbuf.ptr); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } if (config.sql_table) { strncat(envbuf.ptr, "SQL_TABLE=", envbuf.end-envbuf.ptr); strncat(envbuf.ptr, config.sql_table, envbuf.end-envbuf.ptr); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } if (config.sql_host) { strncat(envbuf.ptr, "SQL_HOST=", envbuf.end-envbuf.ptr); strncat(envbuf.ptr, config.sql_host, envbuf.end-envbuf.ptr); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } if (config.sql_user) { strncat(envbuf.ptr, "SQL_USER=", envbuf.end-envbuf.ptr); strncat(envbuf.ptr, config.sql_user, envbuf.end-envbuf.ptr); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } { u_char *tmpptr; strncat(envbuf.ptr, "SQL_REFRESH_TIME=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%d", config.sql_refresh_time); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } if (config.sampling_rate >= 1 || config.ext_sampling_rate >= 1) { u_char *tmpptr; strncat(envbuf.ptr, "SAMPLING_RATE=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%d", config.sampling_rate ? config.sampling_rate : config.ext_sampling_rate); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } if (config.sql_recovery_logfile) { strncat(envbuf.ptr, "SQL_RECOVERY_LOGFILE=", envbuf.end-envbuf.ptr); strncat(envbuf.ptr, config.sql_recovery_logfile, envbuf.end-envbuf.ptr); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } if (config.sql_backup_host) { strncat(envbuf.ptr, "SQL_RECOVERY_BACKUP_HOST=", envbuf.end-envbuf.ptr); strncat(envbuf.ptr, config.sql_backup_host, envbuf.end-envbuf.ptr); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } { u_char *tmpptr; strncat(envbuf.ptr, "SQL_MAX_WRITERS=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%d", config.sql_max_writers); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } for (i = 0; i < count; i++) putenv(ptrs[i]); } Inline void SQL_SetENV_child(const struct insert_data *idata) { u_char *ptrs[N_FUNCS]; int count = 0, i; memset(ptrs, 0, sizeof(ptrs)); { u_char *tmpptr; strncat(envbuf.ptr, "INSERT_QUERIES_NUMBER=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%d", idata->iqn); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } { u_char *tmpptr; strncat(envbuf.ptr, "UPDATE_QUERIES_NUMBER=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%d", idata->uqn); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } { u_char *tmpptr; strncat(envbuf.ptr, "ELAPSED_TIME=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%u", idata->elap_time); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } { u_char *tmpptr; strncat(envbuf.ptr, "TOTAL_ELEM_NUMBER=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%d", idata->ten); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } if (idata->een) { u_char *tmpptr; strncat(envbuf.ptr, "EFFECTIVE_ELEM_NUMBER=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%u", idata->een); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } if (idata->basetime) { u_char *tmpptr; strncat(envbuf.ptr, "SQL_HISTORY_BASETIME=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%u", idata->basetime); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } if (idata->timeslot) { u_char *tmpptr; strncat(envbuf.ptr, "SQL_HISTORY_TIMESLOT=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%u", idata->timeslot); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } if (idata->dyn_table) { u_char *tmpptr; struct tm *nowtm; nowtm = localtime(&idata->basetime); strncat(envbuf.ptr, "EFFECTIVE_SQL_TABLE=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); strftime(tmpptr, envbuf.end-tmpptr, config.sql_table, nowtm); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } { u_char *tmpptr; strncat(envbuf.ptr, "SQL_ACTIVE_WRITERS=", envbuf.end-envbuf.ptr); tmpptr = envbuf.ptr + strlen(envbuf.ptr); snprintf(tmpptr, envbuf.end-tmpptr, "%d", sql_writers.active); ptrs[count] = envbuf.ptr; envbuf.ptr += strlen(envbuf.ptr)+1; count++; } for (i = 0; i < count; i++) putenv(ptrs[i]); } #undef __SQL_COMMON_M_C pmacct-0.14.0/src/log_templates.c0000644000175000017500000003261511037474163015660 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente */ /* 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. */ #define __LOG_TEMPLATES_C #include "pmacct.h" #include "pmacct-data.h" #include "sql_common.h" struct template_entry *build_template(struct template_header *th) { struct template_entry *ptr, *base; struct db_cache dummy; u_char *te; u_int16_t tot_size = 0; th->num = 17; te = malloc(th->num*sizeof(struct template_entry)); memset(te, 0, th->num*sizeof(struct template_entry)); base = (struct template_entry *) te; ptr = base; #if defined (HAVE_L2) ptr->tag = COUNT_DST_MAC; ptr->size = sizeof(dummy.primitives.eth_dhost); tot_size += ptr->size; ptr++; ptr->tag = COUNT_SRC_MAC; ptr->size = sizeof(dummy.primitives.eth_shost); tot_size += ptr->size; ptr++; ptr->tag = COUNT_VLAN; ptr->size = sizeof(dummy.primitives.vlan_id); tot_size += ptr->size; ptr++; #else th->num--; th->num--; /* we replace 3 entries with just 1 */ ptr->tag = LT_NO_L2; ptr->size = 14; tot_size += ptr->size; ptr++; #endif ptr->tag = COUNT_SRC_HOST; ptr->size = sizeof(dummy.primitives.src_ip); tot_size += ptr->size; ptr++; ptr->tag = COUNT_DST_HOST; ptr->size = sizeof(dummy.primitives.dst_ip); tot_size += ptr->size; ptr++; ptr->tag = COUNT_SRC_AS; ptr->size = sizeof(dummy.primitives.src_as); tot_size += ptr->size; ptr++; ptr->tag = COUNT_DST_AS; ptr->size = sizeof(dummy.primitives.dst_as); tot_size += ptr->size; ptr++; ptr->tag = COUNT_SRC_PORT; ptr->size = sizeof(dummy.primitives.src_port); tot_size += ptr->size; ptr++; ptr->tag = COUNT_DST_PORT; ptr->size = sizeof(dummy.primitives.dst_port); tot_size += ptr->size; ptr++; ptr->tag = COUNT_IP_TOS; ptr->size = sizeof(dummy.primitives.tos); tot_size += ptr->size; ptr++; ptr->tag = COUNT_IP_PROTO; ptr->size = sizeof(dummy.primitives.proto); tot_size += ptr->size; ptr++; ptr->tag = COUNT_ID; ptr->size = sizeof(dummy.primitives.id); tot_size += ptr->size; ptr++; ptr->tag = COUNT_CLASS; ptr->size = sizeof(dummy.primitives.class); tot_size += ptr->size; ptr++; ptr->tag = LT_BYTES; ptr->size = sizeof(dummy.bytes_counter); tot_size += ptr->size; ptr++; ptr->tag = LT_PACKETS; ptr->size = sizeof(dummy.packet_counter); tot_size += ptr->size; ptr++; ptr->tag = LT_FLOWS; ptr->size = sizeof(dummy.flows_counter); tot_size += ptr->size; ptr++; ptr->tag = TIMESTAMP; ptr->size = sizeof(dummy.basetime); tot_size += ptr->size; th->magic = htonl(TH_MAGIC); th->num = htons(th->num); th->sz = htons(tot_size); return base; } void set_template_funcs(struct template_header *th, struct template_entry *head) { struct template_entry *te; int cnt; memset(&template_funcs, 0, sizeof(template_funcs)); for (te = head, cnt = 0; cnt < ntohs(th->num); cnt++, te++) { switch (te->tag) { #if defined (HAVE_L2) case COUNT_SRC_MAC: template_funcs[cnt] = TPL_push_src_mac; break; case COUNT_DST_MAC: template_funcs[cnt] = TPL_push_dst_mac; break; case COUNT_VLAN: template_funcs[cnt] = TPL_push_vlan; break; #endif case COUNT_SRC_HOST: template_funcs[cnt] = TPL_push_src_ip; break; case COUNT_DST_HOST: template_funcs[cnt] = TPL_push_dst_ip; break; case COUNT_SRC_AS: template_funcs[cnt] = TPL_push_src_as; break; case COUNT_DST_AS: template_funcs[cnt] = TPL_push_dst_as; break; case COUNT_SRC_PORT: template_funcs[cnt] = TPL_push_src_port; break; case COUNT_DST_PORT: template_funcs[cnt] = TPL_push_dst_port; break; case COUNT_IP_TOS: template_funcs[cnt] = TPL_push_tos; break; case COUNT_IP_PROTO: template_funcs[cnt] = TPL_push_proto; break; case COUNT_ID: template_funcs[cnt] = TPL_push_id; break; case COUNT_CLASS: template_funcs[cnt] = TPL_push_class; break; case LT_BYTES: template_funcs[cnt] = TPL_push_bytes_counter; break; case LT_PACKETS: template_funcs[cnt] = TPL_push_packet_counter; break; case LT_FLOWS: template_funcs[cnt] = TPL_push_flows_counter; break; case TIMESTAMP: template_funcs[cnt] = TPL_push_timestamp; break; case LT_NO_L2: template_funcs[cnt] = TPL_push_nol2; break; default: template_funcs[cnt] = NULL; break; } } } u_int16_t TPL_push(u_char *dst, const struct db_cache *src) { u_char *ptr = dst; int cnt = 0; while (template_funcs[cnt]) { (*template_funcs[cnt])(&ptr, src); cnt++; } return ptr-dst; } #if defined (HAVE_L2) void TPL_push_src_mac(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.eth_shost); memcpy(*dst, &src->primitives.eth_shost, size); *dst += size; } void TPL_push_dst_mac(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.eth_dhost); memcpy(*dst, &src->primitives.eth_dhost, size); *dst += size; } void TPL_push_vlan(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.vlan_id); memcpy(*dst, &src->primitives.vlan_id, size); *dst += size; } #endif void TPL_push_nol2(u_char **dst, const struct db_cache *src) { int size = 14; memset(*dst, 0, size); *dst += size; } void TPL_push_src_ip(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.src_ip); memcpy(*dst, &src->primitives.src_ip, size); *dst += size; } void TPL_push_dst_ip(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.dst_ip); memcpy(*dst, &src->primitives.dst_ip, size); *dst += size; } void TPL_push_src_as(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.src_as); memcpy(*dst, &src->primitives.src_as, size); *dst += size; } void TPL_push_dst_as(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.dst_as); memcpy(*dst, &src->primitives.dst_as, size); *dst += size; } void TPL_push_src_port(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.src_port); memcpy(*dst, &src->primitives.src_port, size); *dst += size; } void TPL_push_dst_port(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.dst_port); memcpy(*dst, &src->primitives.dst_port, size); *dst += size; } void TPL_push_tos(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.tos); memcpy(*dst, &src->primitives.tos, size); *dst += size; } void TPL_push_proto(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.proto); memcpy(*dst, &src->primitives.proto, size); *dst += size; } void TPL_push_id(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.id); memcpy(*dst, &src->primitives.id, size); *dst += size; } void TPL_push_class(u_char **dst, const struct db_cache *src) { int size = sizeof(src->primitives.class); memset(*dst, 0, size); *dst += size; } void TPL_push_bytes_counter(u_char **dst, const struct db_cache *src) { int size = sizeof(src->bytes_counter); memcpy(*dst, &src->bytes_counter, size); *dst += size; } void TPL_push_packet_counter(u_char **dst, const struct db_cache *src) { int size = sizeof(src->packet_counter); memcpy(*dst, &src->packet_counter, size); *dst += size; } void TPL_push_flows_counter(u_char **dst, const struct db_cache *src) { int size = sizeof(src->flows_counter); memcpy(*dst, &src->flows_counter, size); *dst += size; } void TPL_push_timestamp(u_char **dst, const struct db_cache *src) { int size = sizeof(src->basetime); memcpy(*dst, &src->basetime, size); *dst += size; } void TPL_pop(u_char *src, struct db_cache *dst, struct template_header *th, u_char *te) { struct template_entry *teptr = (struct template_entry *)te; u_char *ptr = src; int cnt = 0, tot_sz = 0, sz = 0; u_int32_t t32; u_int64_t t64; for (; cnt < th->num; cnt++, ptr += sz, tot_sz += sz, teptr++) { if (tot_sz > th->sz) { printf("ERROR: malformed template entry. Size mismatch. Exiting.\n"); exit(1); } sz = teptr->size; switch (teptr->tag) { #if defined (HAVE_L2) case COUNT_SRC_MAC: memcpy(&dst->primitives.eth_shost, ptr, sz); break; case COUNT_DST_MAC: memcpy(&dst->primitives.eth_dhost, ptr, sz); break; case COUNT_VLAN: memcpy(&dst->primitives.vlan_id, ptr, sz); break; #endif case COUNT_SRC_HOST: if (sz == 4) { /* legacy IP addresses */ memcpy(&dst->primitives.src_ip.address.ipv4, ptr, sz); dst->primitives.src_ip.family = AF_INET; break; } memcpy(&dst->primitives.src_ip, ptr, sz); break; case COUNT_DST_HOST: if (sz == 4) { /* legacy IP addresses */ memcpy(&dst->primitives.dst_ip.address.ipv4, ptr, sz); dst->primitives.dst_ip.family = AF_INET; break; } memcpy(&dst->primitives.dst_ip, ptr, sz); break; case COUNT_SRC_AS: memcpy(&dst->primitives.src_as, ptr, sz); break; case COUNT_DST_AS: memcpy(&dst->primitives.dst_as, ptr, sz); break; case COUNT_SRC_PORT: memcpy(&dst->primitives.src_port, ptr, sz); break; case COUNT_DST_PORT: memcpy(&dst->primitives.dst_port, ptr, sz); break; case COUNT_IP_TOS: memcpy(&dst->primitives.tos, ptr, sz); break; case COUNT_IP_PROTO: memcpy(&dst->primitives.proto, ptr, sz); break; case COUNT_ID: memcpy(&dst->primitives.id, ptr, sz); break; case COUNT_CLASS: memcpy(&dst->primitives.class, ptr, sz); break; case LT_BYTES: if (sz == 4) { memcpy(&t32, ptr, sz); dst->bytes_counter = t32; } else if (sz == 8) { memcpy(&t64, ptr, sz); dst->bytes_counter = t64; } break; case LT_PACKETS: if (sz == 4) { memcpy(&t32, ptr, sz); dst->packet_counter = t32; } else if (sz == 8) { memcpy(&t64, ptr, sz); dst->packet_counter = t64; } break; case LT_FLOWS: if (sz == 4) { memcpy(&t32, ptr, sz); dst->flows_counter = t32; } else if (sz == 8) { memcpy(&t64, ptr, sz); dst->flows_counter = t64; } break; case TIMESTAMP: memcpy(&dst->basetime, ptr, sz); break; case LT_NO_L2: break; default: printf("ERROR: template entry not supported: '%d'\n", teptr->tag); exit(1); } } } void TPL_check_sizes(struct template_header *th, struct db_cache *elem, u_char *te) { struct template_entry *teptr = (struct template_entry *) te; int cnt = 0; for (; cnt < th->num; cnt++, teptr++) { switch (teptr->tag) { #if defined (HAVE_L2) case COUNT_SRC_MAC: if (teptr->size > sizeof(elem->primitives.eth_shost)) goto exit_lane; break; case COUNT_DST_MAC: if (teptr->size > sizeof(elem->primitives.eth_dhost)) goto exit_lane; break; case COUNT_VLAN: if (teptr->size > sizeof(elem->primitives.vlan_id)) goto exit_lane; break; #endif case COUNT_SRC_HOST: if (teptr->size > sizeof(elem->primitives.src_ip)) goto exit_lane; break; case COUNT_DST_HOST: if (teptr->size > sizeof(elem->primitives.dst_ip)) goto exit_lane; break; case COUNT_SRC_AS: if (teptr->size > sizeof(elem->primitives.src_as)) goto exit_lane; break; case COUNT_DST_AS: if (teptr->size > sizeof(elem->primitives.dst_as)) goto exit_lane; break; case COUNT_SRC_PORT: if (teptr->size > sizeof(elem->primitives.src_port)) goto exit_lane; break; case COUNT_DST_PORT: if (teptr->size > sizeof(elem->primitives.dst_port)) goto exit_lane; break; case COUNT_IP_TOS: if (teptr->size > sizeof(elem->primitives.tos)) goto exit_lane; break; case COUNT_IP_PROTO: if (teptr->size > sizeof(elem->primitives.proto)) goto exit_lane; break; case COUNT_ID: if (teptr->size > sizeof(elem->primitives.id)) goto exit_lane; break; case COUNT_CLASS: if (teptr->size > sizeof(elem->primitives.class)) goto exit_lane; break; case LT_BYTES: if (teptr->size != 4 && teptr->size != 8) goto exit_lane; break; case LT_PACKETS: if (teptr->size != 4 && teptr->size != 8) goto exit_lane; break; case LT_FLOWS: if (teptr->size != 4 && teptr->size != 8) goto exit_lane; break; case TIMESTAMP: if (teptr->size > sizeof(elem->basetime)) goto exit_lane; break; case LT_NO_L2: break; default: printf("ERROR: template entry not supported: '%d'\n", teptr->tag); exit(1); exit_lane: printf("ERROR: template entry '%d' is too big. Exiting.\n", teptr->tag); exit(1); } } } pmacct-0.14.0/src/ip_flow.c0000644000175000017500000005152311241063623014447 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define __IP_FLOW_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "ip_flow.h" #include "classifier.h" #include "jhash.h" u_int32_t flt_total_nodes; time_t flt_prune_deadline; time_t flt_emergency_prune; time_t flow_generic_lifetime; time_t flow_tcpest_lifetime; u_int32_t flt_trivial_hash_rnd = 140281; /* ummmh */ #if defined ENABLE_IPV6 u_int32_t flt6_total_nodes; time_t flt6_prune_deadline; time_t flt6_emergency_prune; #endif void init_ip_flow_handler() { init_ip4_flow_handler(); #if defined ENABLE_IPV6 init_ip6_flow_handler(); #endif } void init_ip4_flow_handler() { int size; if (config.flow_bufsz) flt_total_nodes = config.flow_bufsz / sizeof(struct ip_flow); else flt_total_nodes = DEFAULT_FLOW_BUFFER_SIZE / sizeof(struct ip_flow); if (!config.flow_hashsz) config.flow_hashsz = FLOW_TABLE_HASHSZ; size = sizeof(struct ip_flow) * config.flow_hashsz; ip_flow_table = (struct ip_flow **) malloc(size); assert(ip_flow_table); memset(ip_flow_table, 0, size); flow_lru_list.root = (struct ip_flow *) malloc(sizeof(struct ip_flow)); flow_lru_list.last = flow_lru_list.root; memset(flow_lru_list.root, 0, sizeof(struct ip_flow)); flt_prune_deadline = time(NULL)+FLOW_TABLE_PRUNE_INTERVAL; flt_emergency_prune = 0; if (config.flow_lifetime) flow_generic_lifetime = config.flow_lifetime; else flow_generic_lifetime = FLOW_GENERIC_LIFETIME; if (config.classifiers_path) flow_tcpest_lifetime = FLOW_TCPEST_LIFETIME; else flow_tcpest_lifetime = flow_generic_lifetime; } void ip_flow_handler(struct packet_ptrs *pptrs) { struct timeval now; struct timezone tz; gettimeofday(&now, &tz); if (now.tv_sec > flt_prune_deadline) { prune_old_flows(&now); flt_prune_deadline = now.tv_sec+FLOW_TABLE_PRUNE_INTERVAL; } find_flow(&now, pptrs); } void evaluate_tcp_flags(struct timeval *now, struct packet_ptrs *pptrs, struct ip_flow_common *fp, unsigned int idx) { unsigned int rev = idx ? 0 : 1; if (fp->proto == IPPROTO_TCP) { /* evaluating the transition to the ESTABLISHED state: we need to be as much precise as possible as the lifetime for an established flow is quite high. We check that we have a) SYN flag on a forward direction, b) SYN+ACK on the reverse one and c) that cur_seqno == syn_seqno+1 holds */ if (fp->tcp_flags[idx] & TH_SYN && fp->tcp_flags[rev] & TH_SYN && fp->tcp_flags[rev] & TH_ACK) { if (ntohl(((struct my_tcphdr *)pptrs->tlh_ptr)->th_seq) == fp->last_tcp_seq+1) { /* The flow successfully entered the ESTABLISHED state: clearing flags */ fp->tcp_flags[idx] = FALSE; fp->tcp_flags[rev] = FALSE; } } if (pptrs->tcp_flags) { if (pptrs->tcp_flags & TH_SYN) { fp->tcp_flags[idx] = TH_SYN; if (pptrs->tcp_flags & TH_ACK) fp->tcp_flags[idx] |= TH_ACK; else fp->last_tcp_seq = ntohl(((struct my_tcphdr *)pptrs->tlh_ptr)->th_seq); } if (pptrs->tcp_flags & TH_FIN || pptrs->tcp_flags & TH_RST) { fp->tcp_flags[idx] = pptrs->tcp_flags; fp->tcp_flags[rev] = pptrs->tcp_flags; } } } } void clear_tcp_flow_cmn(struct ip_flow_common *fp, unsigned int idx) { fp->last[idx].tv_sec = 0; fp->last[idx].tv_usec = 0; fp->tcp_flags[idx] = 0; fp->class[idx] = 0; memset(&fp->cst[idx], 0, CSSz); } void find_flow(struct timeval *now, struct packet_ptrs *pptrs) { struct my_iphdr my_iph; struct my_tcphdr my_tlh; struct my_iphdr *iphp = &my_iph; struct my_tlhdr *tlhp = (struct my_tlhdr *) &my_tlh; struct ip_flow *fp, *candidate = NULL, *last_seen = NULL; unsigned int idx, bucket; memcpy(&my_iph, pptrs->iph_ptr, IP4HdrSz); memcpy(&my_tlh, pptrs->tlh_ptr, MyTCPHdrSz); idx = normalize_flow(&iphp->ip_src.s_addr, &iphp->ip_dst.s_addr, &tlhp->src_port, &tlhp->dst_port); bucket = hash_flow(iphp->ip_src.s_addr, iphp->ip_dst.s_addr, tlhp->src_port, tlhp->dst_port, iphp->ip_p); for (fp = ip_flow_table[bucket]; fp; fp = fp->next) { if (fp->ip_src == iphp->ip_src.s_addr && fp->ip_dst == iphp->ip_dst.s_addr && fp->port_src == tlhp->src_port && fp->port_dst == tlhp->dst_port && fp->cmn.proto == iphp->ip_p) { /* flow found; will check for its lifetime */ if (!is_expired_uni(now, &fp->cmn, idx)) { /* still valid flow */ evaluate_tcp_flags(now, pptrs, &fp->cmn, idx); fp->cmn.last[idx].tv_sec = now->tv_sec; fp->cmn.last[idx].tv_usec = now->tv_usec; pptrs->new_flow = FALSE; if (config.classifiers_path) evaluate_classifiers(pptrs, &fp->cmn, idx); return; } else { /* stale flow: will start a new one */ clear_tcp_flow_cmn(&fp->cmn, idx); evaluate_tcp_flags(now, pptrs, &fp->cmn, idx); fp->cmn.last[idx].tv_sec = now->tv_sec; fp->cmn.last[idx].tv_usec = now->tv_usec; pptrs->new_flow = TRUE; if (config.classifiers_path) evaluate_classifiers(pptrs, &fp->cmn, idx); return; } } if (!candidate && is_expired(now, &fp->cmn)) candidate = fp; last_seen = fp; } if (candidate) create_flow(now, candidate, TRUE, bucket, pptrs, iphp, tlhp, idx); else create_flow(now, last_seen, FALSE, bucket, pptrs, iphp, tlhp, idx); } void create_flow(struct timeval *now, struct ip_flow *fp, u_int8_t is_candidate, unsigned int bucket, struct packet_ptrs *pptrs, struct my_iphdr *iphp, struct my_tlhdr *tlhp, unsigned int idx) { struct ip_flow *newf; if (!flt_total_nodes) { if (now->tv_sec > flt_emergency_prune+FLOW_TABLE_EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Flow/4 buffer full. Skipping flows.\n"); flt_emergency_prune = now->tv_sec; prune_old_flows(now); } pptrs->new_flow = FALSE; return; } if (fp) { /* a 'not candidate' is simply the tail (last node) of the list. We need to allocate a new node */ if (!is_candidate) { newf = (struct ip_flow *) malloc(sizeof(struct ip_flow)); if (!newf) { if (now->tv_sec > flt_emergency_prune+FLOW_TABLE_EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Flow/4 buffer finished memory. Skipping flows.\n"); flt_emergency_prune = now->tv_sec; prune_old_flows(now); } pptrs->new_flow = FALSE; return; } else flt_total_nodes--; memset(newf, 0, sizeof(struct ip_flow)); fp->next = newf; newf->prev = fp; flow_lru_list.last->lru_next = newf; /* placing new node as LRU tail */ newf->lru_prev = flow_lru_list.last; flow_lru_list.last = newf; fp = newf; } else { if (fp->lru_next) { /* if fp->lru_next==NULL the node is already the tail */ fp->lru_prev->lru_next = fp->lru_next; fp->lru_next->lru_prev = fp->lru_prev; flow_lru_list.last->lru_next = fp; fp->lru_prev = flow_lru_list.last; fp->lru_next = NULL; flow_lru_list.last = fp; } clear_context_chain(&fp->cmn, 0); clear_context_chain(&fp->cmn, 1); memset(&fp->cmn, 0, sizeof(struct ip_flow_common)); } } else { /* we don't have any pointer to existing flows; this is because the current bucket doesn't contain any node; we'll allocate the first one */ fp = (struct ip_flow *) malloc(sizeof(struct ip_flow)); if (!fp) { if (now->tv_sec > flt_emergency_prune+FLOW_TABLE_EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Flow/4 buffer finished memory. Skipping flows.\n"); flt_emergency_prune = now->tv_sec; prune_old_flows(now); } pptrs->new_flow = FALSE; return; } else flt_total_nodes--; memset(fp, 0, sizeof(struct ip_flow)); ip_flow_table[bucket] = fp; flow_lru_list.last->lru_next = fp; /* placing new node as LRU tail */ fp->lru_prev = flow_lru_list.last; flow_lru_list.last = fp; } fp->ip_src = iphp->ip_src.s_addr; fp->ip_dst = iphp->ip_dst.s_addr; fp->port_src = tlhp->src_port; fp->port_dst = tlhp->dst_port; fp->cmn.proto = iphp->ip_p; fp->cmn.bucket = bucket; evaluate_tcp_flags(now, pptrs, &fp->cmn, idx); fp->cmn.last[idx].tv_sec = now->tv_sec; fp->cmn.last[idx].tv_usec = now->tv_usec; pptrs->new_flow = TRUE; if (config.classifiers_path) evaluate_classifiers(pptrs, &fp->cmn, idx); } void prune_old_flows(struct timeval *now) { struct ip_flow *fp, *temp, *last_seen = flow_lru_list.root; fp = flow_lru_list.root->lru_next; while (fp) { if (is_expired(now, &fp->cmn)) { /* we found a stale element; we'll prune it */ if (fp->lru_next) temp = fp->lru_next; else temp = NULL; /* rearranging bucket's pointers */ if (fp->prev && fp->next) { fp->prev->next = fp->next; fp->next->prev = fp->prev; } else if (fp->prev) fp->prev->next = NULL; else if (fp->next) { ip_flow_table[fp->cmn.bucket] = fp->next; fp->next->prev = NULL; } else ip_flow_table[fp->cmn.bucket] = NULL; /* rearranging LRU pointers */ if (fp->lru_next) { fp->lru_next->lru_prev = fp->lru_prev; fp->lru_prev->lru_next = fp->lru_next; } else fp->lru_prev->lru_next = NULL; clear_context_chain(&fp->cmn, 0); clear_context_chain(&fp->cmn, 1); free(fp); flt_total_nodes++; if (temp) fp = temp; else fp = NULL; } else { last_seen = fp; fp = fp->lru_next; } } flow_lru_list.last = last_seen; } unsigned int normalize_flow(u_int32_t *ip_src, u_int32_t *ip_dst, u_int16_t *port_src, u_int16_t *port_dst) { u_int16_t port_tmp; u_int32_t ip_tmp; if (*port_src < *port_dst) { port_tmp = *port_src; *port_src = *port_dst; *port_dst = port_tmp; ip_tmp = *ip_src; *ip_src = *ip_dst; *ip_dst = ip_tmp; return TRUE; /* reverse flow */ } if (*port_src == *port_dst) { if (*ip_src < *ip_dst) { ip_tmp = *ip_src; *ip_src = *ip_dst; *ip_dst = ip_tmp; return TRUE; /* reverse flow */ } } return FALSE; /* forward flow */ } /* hash_flow() is taken (it has another name there) from Linux kernel 2.4; see full credits contained in jhash.h */ unsigned int hash_flow(u_int32_t ip_src, u_int32_t ip_dst, u_int16_t port_src, u_int16_t port_dst, u_int8_t proto) { return jhash_3words((u_int32_t)(port_src ^ port_dst) << 16 | proto, ip_src, ip_dst, flt_trivial_hash_rnd) & (config.flow_hashsz-1); } /* is_expired() checks for the expiration of the bi-directional flow; returns: TRUE if a) the TCP flow is expired or, b) the non-TCP flow scores 2 points; FALSE in any other case. This function will also contain any further semi-stateful evaluation of specific protocols */ unsigned int is_expired(struct timeval *now, struct ip_flow_common *fp) { int forward = 0, reverse = 0; forward = is_expired_uni(now, fp, 0); reverse = is_expired_uni(now, fp, 1); if (forward && reverse) return TRUE; else return FALSE; } /* is_expired_uni() checks for the expiration of the uni-directional flow; returns: TRUE if the flow has expired; FALSE in any other case. */ unsigned int is_expired_uni(struct timeval *now, struct ip_flow_common *fp, unsigned int idx) { if (fp->proto == IPPROTO_TCP) { /* tcp_flags == 0 ==> the TCP flow is in ESTABLISHED mode */ if (!fp->tcp_flags[idx]) { if (now->tv_sec > fp->last[idx].tv_sec+flow_tcpest_lifetime) return TRUE; } else { if (fp->tcp_flags[idx] & TH_SYN && now->tv_sec > fp->last[idx].tv_sec+FLOW_TCPSYN_LIFETIME) return TRUE; if (fp->tcp_flags[idx] & TH_FIN && now->tv_sec > fp->last[idx].tv_sec+FLOW_TCPFIN_LIFETIME) return TRUE; if (fp->tcp_flags[idx] & TH_RST && now->tv_sec > fp->last[idx].tv_sec+FLOW_TCPRST_LIFETIME) return TRUE; } } else { if (now->tv_sec > fp->last[idx].tv_sec+flow_generic_lifetime) return TRUE; } return FALSE; } #if defined ENABLE_IPV6 void init_ip6_flow_handler() { int size; if (config.frag_bufsz) flt6_total_nodes = config.frag_bufsz / sizeof(struct ip_flow6); else flt6_total_nodes = DEFAULT_FLOW_BUFFER_SIZE / sizeof(struct ip_flow6); if (!config.flow_hashsz) config.flow_hashsz = FLOW_TABLE_HASHSZ; size = sizeof(struct ip_flow6) * config.flow_hashsz; ip_flow_table6 = (struct ip_flow6 **) malloc(size); memset(ip_flow_table6, 0, size); flow_lru_list6.root = (struct ip_flow6 *) malloc(sizeof(struct ip_flow6)); flow_lru_list6.last = flow_lru_list6.root; memset(flow_lru_list6.root, 0, sizeof(struct ip_flow6)); flt6_prune_deadline = time(NULL)+FLOW_TABLE_PRUNE_INTERVAL; flt6_emergency_prune = 0; if (config.flow_lifetime) flow_generic_lifetime = config.flow_lifetime; else flow_generic_lifetime = FLOW_GENERIC_LIFETIME; if (config.classifiers_path) flow_tcpest_lifetime = FLOW_TCPEST_LIFETIME; else flow_tcpest_lifetime = flow_generic_lifetime; } void ip_flow6_handler(struct packet_ptrs *pptrs) { struct timeval now; struct timezone tz; gettimeofday(&now, &tz); if (now.tv_sec > flt6_prune_deadline) { prune_old_flows6(&now); flt6_prune_deadline = now.tv_sec+FLOW_TABLE_PRUNE_INTERVAL; } find_flow6(&now, pptrs); } unsigned int hash_flow6(u_int32_t id, struct in6_addr *saddr, struct in6_addr *daddr) { u_int32_t a, b, c; u_int32_t *src = (u_int32_t *)saddr, *dst = (u_int32_t *)daddr; a = src[0]; b = src[1]; c = src[2]; a += JHASH_GOLDEN_RATIO; b += JHASH_GOLDEN_RATIO; c += flt_trivial_hash_rnd; __jhash_mix(a, b, c); a += src[3]; b += dst[0]; c += dst[1]; __jhash_mix(a, b, c); a += dst[2]; b += dst[3]; c += id; __jhash_mix(a, b, c); return c & (config.flow_hashsz - 1); } unsigned int normalize_flow6(struct in6_addr *saddr, struct in6_addr *daddr, u_int16_t *port_src, u_int16_t *port_dst) { struct in6_addr taddr; u_int16_t port_tmp; if (*port_src < *port_dst) { port_tmp = *port_src; *port_src = *port_dst; *port_dst = port_tmp; ip6_addr_cpy(&taddr, saddr); ip6_addr_cpy(saddr, daddr); ip6_addr_cpy(daddr, &taddr); return TRUE; /* reverse flow */ } if (*port_src == *port_dst) { if (ip6_addr_cmp(saddr, daddr) < 0) { ip6_addr_cpy(&taddr, saddr); ip6_addr_cpy(saddr, daddr); ip6_addr_cpy(daddr, &taddr); return TRUE; /* reverse flow */ } } return FALSE; /* forward flow */ } void find_flow6(struct timeval *now, struct packet_ptrs *pptrs) { struct ip6_hdr my_iph; struct my_tcphdr my_tlh; struct ip6_hdr *iphp = &my_iph; struct my_tlhdr *tlhp = (struct my_tlhdr *) &my_tlh; struct ip_flow6 *fp, *candidate = NULL, *last_seen = NULL; unsigned int idx, bucket; memcpy(&my_iph, pptrs->iph_ptr, IP6HdrSz); memcpy(&my_tlh, pptrs->tlh_ptr, MyTCPHdrSz); idx = normalize_flow6(&iphp->ip6_src, &iphp->ip6_dst, &tlhp->src_port, &tlhp->dst_port); bucket = hash_flow6((tlhp->src_port << 16) | tlhp->dst_port, &iphp->ip6_src, &iphp->ip6_dst); for (fp = ip_flow_table6[bucket]; fp; fp = fp->next) { if (!ip6_addr_cmp(&fp->ip_src, &iphp->ip6_src) && !ip6_addr_cmp(&fp->ip_dst, &iphp->ip6_dst) && fp->port_src == tlhp->src_port && fp->port_dst == tlhp->dst_port && fp->cmn.proto == pptrs->l4_proto) { /* flow found; will check for its lifetime */ if (!is_expired_uni(now, &fp->cmn, idx)) { /* still valid flow */ evaluate_tcp_flags(now, pptrs, &fp->cmn, idx); fp->cmn.last[idx].tv_sec = now->tv_sec; fp->cmn.last[idx].tv_usec = now->tv_usec; pptrs->new_flow = FALSE; if (config.classifiers_path) evaluate_classifiers(pptrs, &fp->cmn, idx); return; } else { /* stale flow: will start a new one */ clear_tcp_flow_cmn(&fp->cmn, idx); evaluate_tcp_flags(now, pptrs, &fp->cmn, idx); fp->cmn.last[idx].tv_sec = now->tv_sec; fp->cmn.last[idx].tv_usec = now->tv_usec; pptrs->new_flow = TRUE; if (config.classifiers_path) evaluate_classifiers(pptrs, &fp->cmn, idx); return; } } if (!candidate && is_expired(now, &fp->cmn)) candidate = fp; last_seen = fp; } create: if (candidate) create_flow6(now, candidate, TRUE, bucket, pptrs, iphp, tlhp, idx); else create_flow6(now, last_seen, FALSE, bucket, pptrs, iphp, tlhp, idx); } void create_flow6(struct timeval *now, struct ip_flow6 *fp, u_int8_t is_candidate, unsigned int bucket, struct packet_ptrs *pptrs, struct ip6_hdr *iphp, struct my_tlhdr *tlhp, unsigned int idx) { struct ip_flow6 *newf; if (!flt6_total_nodes) { if (now->tv_sec > flt6_emergency_prune+FLOW_TABLE_EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Flow/6 buffer full. Skipping flows.\n"); flt6_emergency_prune = now->tv_sec; prune_old_flows6(now); } pptrs->new_flow = FALSE; return; } if (fp) { /* a 'not candidate' is simply the tail (last node) of the list. We need to allocate a new node */ if (!is_candidate) { newf = (struct ip_flow6 *) malloc(sizeof(struct ip_flow6)); if (!newf) { if (now->tv_sec > flt6_emergency_prune+FLOW_TABLE_EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Flow/6 buffer full. Skipping flows.\n"); flt6_emergency_prune = now->tv_sec; prune_old_flows6(now); } pptrs->new_flow = FALSE; return; } else flt6_total_nodes--; memset(newf, 0, sizeof(struct ip_flow6)); fp->next = newf; newf->prev = fp; flow_lru_list6.last->lru_next = newf; /* placing new node as LRU tail */ newf->lru_prev = flow_lru_list6.last; flow_lru_list6.last = newf; fp = newf; } else { if (fp->lru_next) { /* if fp->lru_next==NULL the node is already the tail */ fp->lru_prev->lru_next = fp->lru_next; fp->lru_next->lru_prev = fp->lru_prev; flow_lru_list6.last->lru_next = fp; fp->lru_prev = flow_lru_list6.last; fp->lru_next = NULL; flow_lru_list6.last = fp; } clear_context_chain(&fp->cmn, 0); clear_context_chain(&fp->cmn, 1); memset(&fp->cmn, 0, sizeof(struct ip_flow_common)); } } else { /* we don't have any fragment pointer; this is because current bucket doesn't contain any node; we'll allocate first one */ fp = (struct ip_flow6 *) malloc(sizeof(struct ip_flow6)); if (!fp) { if (now->tv_sec > flt6_emergency_prune+FLOW_TABLE_EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Flow/6 buffer full. Skipping flows.\n"); flt6_emergency_prune = now->tv_sec; prune_old_flows6(now); } pptrs->new_flow = FALSE; return; } else flt6_total_nodes--; memset(fp, 0, sizeof(struct ip_flow6)); ip_flow_table6[bucket] = fp; flow_lru_list6.last->lru_next = fp; /* placing new node as LRU tail */ fp->lru_prev = flow_lru_list6.last; flow_lru_list6.last = fp; } ip6_addr_cpy(&fp->ip_src, &iphp->ip6_src); ip6_addr_cpy(&fp->ip_dst, &iphp->ip6_dst); fp->port_src = tlhp->src_port; fp->port_dst = tlhp->dst_port; fp->cmn.proto = pptrs->l4_proto; fp->cmn.bucket = bucket; evaluate_tcp_flags(now, pptrs, &fp->cmn, idx); fp->cmn.last[idx].tv_sec = now->tv_sec; fp->cmn.last[idx].tv_usec = now->tv_usec; pptrs->new_flow = TRUE; if (config.classifiers_path) evaluate_classifiers(pptrs, &fp->cmn, idx); } void prune_old_flows6(struct timeval *now) { struct ip_flow6 *fp, *temp, *last_seen = flow_lru_list6.root; fp = flow_lru_list6.root->lru_next; while (fp) { if (is_expired(now, &fp->cmn)) { /* we found a stale element; we'll prune it */ if (fp->lru_next) temp = fp->lru_next; else temp = NULL; /* rearranging bucket's pointers */ if (fp->prev && fp->next) { fp->prev->next = fp->next; fp->next->prev = fp->prev; } else if (fp->prev) fp->prev->next = NULL; else if (fp->next) { ip_flow_table6[fp->cmn.bucket] = fp->next; fp->next->prev = NULL; } else ip_flow_table6[fp->cmn.bucket] = NULL; /* rearranging LRU pointers */ if (fp->lru_next) { fp->lru_next->lru_prev = fp->lru_prev; fp->lru_prev->lru_next = fp->lru_next; } else fp->lru_prev->lru_next = NULL; clear_context_chain(&fp->cmn, 0); clear_context_chain(&fp->cmn, 1); free(fp); flt6_total_nodes++; if (temp) fp = temp; else fp = NULL; } else { last_seen = fp; fp = fp->lru_next; } } flow_lru_list6.last = last_seen; } #endif pmacct-0.14.0/src/setproctitle.h0000644000175000017500000000222410530072467015536 0ustar paolopaolo/* * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * * * $Id: setproctitle.h,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $ */ #ifdef __hpux # undef SPT_TYPE // # define SPT_TYPE SPT_PSTAT #endif #ifdef _AIX3 # define SPT_PADCHAR '\0' #endif #ifdef AIX # define SPT_PADCHAR '\0' #endif #if defined (__bsdi__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) # undef SPT_TYPE # define SPT_TYPE SPT_BUILTIN #endif #if defined(__linux__) # undef SPT_TYPE # define SPT_TYPE SPT_REUSEARGV # define SPT_PADCHAR '\0' #endif #if !defined(__hpux) && (defined(_H3050R) || defined(_HIUX_SOURCE)) # define SPT_TYPE SPT_PSTAT #endif #if defined(HAVE_SETPROCTITLE) # undef SPT_TYPE # define SPT_TYPE SPT_BUILTIN #endif pmacct-0.14.0/src/net_aggr.h0000644000175000017500000001252311344241422014577 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2010 by Paolo Lucente */ /* 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. */ /* defines */ #define NETWORKS_CACHE_ENTRIES 99991 #define NETWORKS6_CACHE_ENTRIES 32771 #define RETURN_NET 0 #define RETURN_AS 1 /* structures */ struct networks_cache_entry { u_int32_t key; struct networks_table_entry *result; }; struct networks_cache { struct networks_cache_entry *cache; unsigned int num; #if defined ENABLE_IPV6 struct networks6_cache_entry *cache6; unsigned int num6; #endif }; struct networks_table { struct networks_table_entry *table; unsigned int num; #if defined ENABLE_IPV6 struct networks6_table_entry *table6; unsigned int num6; #endif u_int32_t maskbits[4]; time_t timestamp; }; struct networks_table_entry { u_int32_t net; u_int32_t mask; u_int8_t masknum; as_t as; struct networks_table childs_table; }; #if defined ENABLE_IPV6 struct networks6_cache_entry { u_int32_t key[4]; struct networks6_table_entry *result; }; struct networks6_table_entry { u_int32_t net[4]; u_int32_t mask[4]; u_int8_t masknum; as_t as; struct networks_table childs_table; }; #endif struct networks_table_metadata { u_int8_t level; u_int32_t childs; }; typedef void (*net_func) (struct networks_table *, struct networks_cache *, struct pkt_primitives *); /* prototypes */ #if (!defined __NET_AGGR_C) #define EXT extern #else #define EXT #endif EXT net_func net_funcs[8]; EXT void set_net_funcs(); EXT void mask_src_ipaddr(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void mask_dst_ipaddr(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void copy_src_mask(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void copy_dst_mask(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void search_src_host(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void search_dst_host(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void search_src_net(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void search_dst_net(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void search_src_nmask(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void search_dst_nmask(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void search_src_as(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void search_dst_as(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT as_t search_pretag_src_as(struct networks_table *, struct networks_cache *, struct packet_ptrs *); EXT as_t search_pretag_dst_as(struct networks_table *, struct networks_cache *, struct packet_ptrs *); EXT void drop_src_host(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void drop_dst_host(struct networks_table *, struct networks_cache *, struct pkt_primitives *); EXT void load_networks(char *, struct networks_table *, struct networks_cache *); /* wrapper */ EXT void load_networks4(char *, struct networks_table *, struct networks_cache *); EXT void merge_sort(char *, struct networks_table_entry *, int, int); EXT void merge(char *, struct networks_table_entry *, int, int, int); EXT struct networks_table_entry *binsearch(struct networks_table *, struct networks_cache *, struct host_addr *); EXT void networks_cache_insert(struct networks_cache *, u_int32_t *, struct networks_table_entry *); EXT struct networks_table_entry *networks_cache_search(struct networks_cache *, u_int32_t *); #if defined ENABLE_IPV6 EXT void load_networks6(char *, struct networks_table *, struct networks_cache *); EXT void merge_sort6(char *, struct networks6_table_entry *, int, int); EXT void merge6(char *, struct networks6_table_entry *, int, int, int); EXT struct networks6_table_entry *binsearch6(struct networks_table *, struct networks_cache *, struct host_addr *); EXT void networks_cache_insert6(struct networks_cache *, void *, struct networks6_table_entry *); EXT struct networks6_table_entry *networks_cache_search6(struct networks_cache *, void *); EXT unsigned int networks_cache_hash6(void *); #endif #undef EXT /* global vars */ #if (!defined __NET_AGGR_C) #define EXT extern #else #define EXT #endif EXT struct networks_table nt; EXT struct networks_cache nc; EXT struct networks_table_entry dummy_entry; EXT int default_route_in_networks4_table; #if defined ENABLE_IPV6 EXT struct networks6_table_entry dummy_entry6; EXT int default_route_in_networks6_table; #endif #undef EXT pmacct-0.14.0/src/bgp/0000755000175000017500000000000011741267746013427 5ustar paolopaolopmacct-0.14.0/src/bgp/bgp_table.h0000644000175000017500000000564511741024622015511 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* Originally based on Quagga BGP routing table which is: Copyright (C) 1998, 2001 Kunihiro Ishiguro GNU Zebra 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. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _BGP_TABLE_H_ #define _BGP_TABLE_H_ #define DEFAULT_BGP_INFO_HASH 13 /* AFI and SAFI type. */ typedef u_int16_t afi_t; typedef u_int8_t safi_t; typedef enum { BGP_TABLE_MAIN, BGP_TABLE_RSCLIENT, } bgp_table_t; struct bgp_table { bgp_table_t type; /* afi/safi of this table */ afi_t afi; safi_t safi; /* The owner of this 'bgp_table' structure. */ void *owner; struct bgp_node *top; unsigned long count; }; struct bgp_node { struct prefix p; struct bgp_table *table; struct bgp_node *parent; struct bgp_node *link[2]; #define l_left link[0] #define l_right link[1] void **info; unsigned int lock; /* struct bgp_adj_out *adj_out; struct bgp_adj_in *adj_in; struct bgp_node *prn; unsigned int lock; u_char flags; #define BGP_NODE_PROCESS_SCHEDULED (1 << 0) */ }; struct bgp_info_extra { rd_t rd; u_char label[3]; }; struct bgp_info { struct bgp_info *next; struct bgp_info *prev; struct bgp_peer *peer; struct bgp_attr *attr; struct bgp_info_extra *extra; }; /* Prototypes */ #if (!defined __BGP_TABLE_C) #define EXT extern #else #define EXT #endif EXT struct bgp_table *bgp_table_init (afi_t, safi_t); EXT void bgp_table_finish (struct bgp_table **); EXT void bgp_unlock_node (struct bgp_node *node); EXT struct bgp_node *bgp_table_top (const struct bgp_table *const); EXT struct bgp_node *bgp_route_next (struct bgp_node *); EXT struct bgp_node *bgp_route_next_until (struct bgp_node *, struct bgp_node *); EXT struct bgp_node *bgp_node_get (struct bgp_table *const, struct prefix *); EXT struct bgp_node *bgp_lock_node (struct bgp_node *node); EXT struct bgp_node *bgp_node_match (const struct bgp_table *, struct prefix *, struct bgp_peer *); EXT struct bgp_node *bgp_node_match_ipv4 (const struct bgp_table *, struct in_addr *, struct bgp_peer *); #ifdef ENABLE_IPV6 EXT struct bgp_node *bgp_node_match_ipv6 (const struct bgp_table *, struct in6_addr *, struct bgp_peer *); #endif /* ENABLE_IPV6 */ EXT unsigned long bgp_table_count (const struct bgp_table *const); #undef EXT #endif pmacct-0.14.0/src/bgp/bgp_packet.h0000644000175000017500000002235511741044541015670 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* * Definitions for BGP packet disassembly structures and routine * * Baselined from: * * $Id: bgp_packet.h,v 1.4 2012/04/10 14:53:21 paolo Exp $ * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * 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. */ #ifndef _BGP_PACKET_H_ #define _BGP_PACKET_H_ /* some handy things to know */ #define BGP_MAX_PACKET_SIZE 4096 #define BGP_MARKER_SIZE 16 /* size of BGP marker */ #define BGP_HEADER_SIZE 19 /* size of BGP header, including marker */ #define BGP_MIN_OPEN_MSG_SIZE 29 #define BGP_MIN_UPDATE_MSG_SIZE 23 #define BGP_MIN_NOTIFICATION_MSG_SIZE 21 #define BGP_MIN_KEEPALVE_MSG_SIZE BGP_HEADER_SIZE #define BGP_TCP_PORT 179 #define BGP_VERSION4 4 #define CAPABILITY_CODE_AS4_LEN 4 /* BGP message types */ #define BGP_OPEN 1 #define BGP_UPDATE 2 #define BGP_NOTIFICATION 3 #define BGP_KEEPALIVE 4 #define BGP_ROUTE_REFRESH 5 #define BGP_CAPABILITY 6 #define BGP_ROUTE_REFRESH_CISCO 0x80 /* Address family numbers from RFC1700. */ #define AFI_IP 1 #define AFI_IP6 2 #define AFI_MAX 3 /* Subsequent Address Family Identifier. */ #define SAFI_UNICAST 1 #define SAFI_MULTICAST 2 #define SAFI_UNICAST_MULTICAST 3 #define SAFI_MPLS_LABEL 4 #define SAFI_MPLS_VPN 128 #define SAFI_MAX 129 struct bgp_header { u_int8_t bgpo_marker[BGP_MARKER_SIZE]; u_int16_t bgpo_len; u_int8_t bgpo_type; }; /* BGP OPEN message */ struct bgp_open { u_int8_t bgpo_marker[BGP_MARKER_SIZE]; u_int16_t bgpo_len; u_int8_t bgpo_type; u_int8_t bgpo_version; u_int16_t bgpo_myas; u_int16_t bgpo_holdtime; u_int32_t bgpo_id; u_int8_t bgpo_optlen; /* options should follow */ }; /* BGP NOTIFICATION message */ struct bgp_notification { u_int8_t bgpn_marker[BGP_MARKER_SIZE]; u_int16_t bgpn_len; u_int8_t bgpn_type; u_int8_t bgpn_major; u_int8_t bgpn_minor; /* data should follow */ }; /* BGP ROUTE-REFRESH message */ struct bgp_route_refresh { u_int8_t bgpr_marker[BGP_MARKER_SIZE]; u_int16_t bgpr_len; u_int8_t bgpr_type; u_int16_t bgpr_afi; u_int8_t bgpr_reserved; u_int8_t bgpr_safi; }; struct capability_mp_data { u_int16_t afi; u_char reserved; u_char safi; }; struct capability_as4 { uint32_t as4; }; /* attribute flags, from RFC1771 */ #define BGP_ATTR_FLAG_OPTIONAL 0x80 #define BGP_ATTR_FLAG_TRANSITIVE 0x40 #define BGP_ATTR_FLAG_PARTIAL 0x20 #define BGP_ATTR_FLAG_EXTENDED_LENGTH 0x10 /* SSA flags */ #define BGP_SSA_TRANSITIVE 0x8000 #define BGP_SSA_TYPE 0x7FFF /* SSA Types */ #define BGP_SSA_L2TPv3 1 #define BGP_SSA_mGRE 2 #define BGP_SSA_IPSec 3 #define BGP_SSA_MPLS 4 /* AS_PATH segment types */ #define AS_SET 1 /* RFC1771 */ #define AS_SEQUENCE 2 /* RFC1771 */ #define AS_CONFED_SET 4 /* RFC1965 has the wrong values, corrected in */ #define AS_CONFED_SEQUENCE 3 /* draft-ietf-idr-bgp-confed-rfc1965bis-01.txt */ /* OPEN message Optional Parameter types */ #define BGP_OPTION_AUTHENTICATION 1 /* RFC1771 */ #define BGP_OPTION_CAPABILITY 2 /* RFC2842 */ /* BGP capability code */ #define BGP_CAPABILITY_RESERVED 0 /* RFC2434 */ #define BGP_CAPABILITY_MULTIPROTOCOL 1 /* RFC2858 */ #define BGP_CAPABILITY_ROUTE_REFRESH 2 /* RFC2918 */ #define BGP_CAPABILITY_COOPERATIVE_ROUTE_FILTERING 3 /* draft-ietf-idr-route-filter-04.txt */ #define BGP_CAPABILITY_GRACEFUL_RESTART 0x40 /* draft-ietf-idr-restart-05 */ #define BGP_CAPABILITY_4_OCTET_AS_NUMBER 0x41 /* draft-ietf-idr-as4bytes-06 */ #define BGP_CAPABILITY_DYNAMIC_CAPABILITY 0x42 /* draft-ietf-idr-dynamic-cap-03 */ #define BGP_CAPABILITY_ORF_CISCO 0x82 /* Cisco */ #define BGP_CAPABILITY_ROUTE_REFRESH_CISCO 0x80 /* Cisco */ #define BGP_ORF_PREFIX_CISCO 0x80 /* Cisco */ #define BGP_ORF_COMM_CISCO 0x81 /* Cisco */ #define BGP_ORF_EXTCOMM_CISCO 0x82 /* Cisco */ #define BGP_ORF_ASPATH_CISCO 0x83 /* Cisco */ #define BGP_ORF_COMM 0x02 /* draft-ietf-idr-route-filter-06.txt */ #define BGP_ORF_EXTCOMM 0x03 /* draft-ietf-idr-route-filter-06.txt */ #define BGP_ORF_ASPATH 0x04 /* draft-ietf-idr-aspath-orf-02.txt */ /* draft-ietf-idr-route-filter-06.txt */ #define BGP_ORF_ACTION 0xc0 #define BGP_ORF_ADD 0x00 #define BGP_ORF_REMOVE 0x40 #define BGP_ORF_REMOVEALL 0x80 #define BGP_ORF_MATCH 0x10 #define BGP_ORF_PERMIT 0x00 #define BGP_ORF_DENY 0x10 /* well-known communities, from RFC1997 */ #define BGP_COMM_NO_EXPORT 0xFFFFFF01 #define BGP_COMM_NO_ADVERTISE 0xFFFFFF02 #define BGP_COMM_NO_EXPORT_SUBCONFED 0xFFFFFF03 #define FOURHEX0 0x00000000 #define FOURHEXF 0xFFFF0000 /* attribute types */ #define BGPTYPE_ORIGIN 1 /* RFC1771 */ #define BGPTYPE_AS_PATH 2 /* RFC1771 */ #define BGPTYPE_NEXT_HOP 3 /* RFC1771 */ #define BGPTYPE_MULTI_EXIT_DISC 4 /* RFC1771 */ #define BGPTYPE_LOCAL_PREF 5 /* RFC1771 */ #define BGPTYPE_ATOMIC_AGGREGATE 6 /* RFC1771 */ #define BGPTYPE_AGGREGATOR 7 /* RFC1771 */ #define BGPTYPE_COMMUNITIES 8 /* RFC1997 */ #define BGPTYPE_ORIGINATOR_ID 9 /* RFC2796 */ #define BGPTYPE_CLUSTER_LIST 10 /* RFC2796 */ #define BGPTYPE_DPA 11 /* work in progress */ #define BGPTYPE_ADVERTISER 12 /* RFC1863 */ #define BGPTYPE_RCID_PATH 13 /* RFC1863 */ #define BGPTYPE_MP_REACH_NLRI 14 /* RFC2858 */ #define BGPTYPE_MP_UNREACH_NLRI 15 /* RFC2858 */ #define BGPTYPE_EXTENDED_COMMUNITY 16 /* Draft Ramachandra */ #define BGPTYPE_NEW_AS_PATH 17 /* draft-ietf-idr-as4bytes */ #define BGPTYPE_NEW_AGGREGATOR 18 /* draft-ietf-idr-as4bytes */ #define BGPTYPE_SAFI_SPECIFIC_ATTR 19 /* draft-kapoor-nalawade-idr-bgp-ssa-00.txt */ /* Extended community type */ /* draft-ietf-idr-bgp-ext-communities */ #define BGP_EXT_COM_RT_0 0x0002 /* Route Target,Format AS(2bytes):AN(4bytes) */ #define BGP_EXT_COM_RT_1 0x0102 /* Route Target,Format IP address:AN(2bytes) */ #define BGP_EXT_COM_RT_2 0x0202 /* Route Target,Format AS(2bytes):AN(4bytes) */ #define BGP_EXT_COM_RO_0 0x0003 /* Route Origin,Format AS(2bytes):AN(4bytes) */ #define BGP_EXT_COM_RO_1 0x0103 /* Route Origin,Format IP address:AN(2bytes) */ #define BGP_EXT_COM_RO_2 0x0203 /* Route Origin,Format AS(2bytes):AN(4bytes) */ #define BGP_EXT_COM_LINKBAND 0x0004 /* Link Bandwidth,Format AS(2B):Bandwidth(4B) */ /* -2 version of the draft */ #define BGP_EXT_COM_VPN_ORIGIN 0x0005 /* OSPF Domin ID / VPN of Origin */ /* draft-rosen-vpns-ospf-bgp-mpls */ #define BGP_EXT_COM_OSPF_RTYPE 0X8000 /* OSPF Route Type,Format Area(4B):RouteType(1B):Options(1B) */ #define BGP_EXT_COM_OSPF_RID 0x8001 /* OSPF Router ID,Format RouterID(4B):Unused(2B) */ #define BGP_EXT_COM_L2INFO 0x800a /* draft-kompella-ppvpn-l2vpn */ /* OSPF codes for BGP_EXT_COM_OSPF_RTYPE draft-rosen-vpns-ospf-bgp-mpls */ #define BGP_OSPF_RTYPE_RTR 1 /* OSPF Router LSA */ #define BGP_OSPF_RTYPE_NET 2 /* OSPF Network LSA */ #define BGP_OSPF_RTYPE_SUM 3 /* OSPF Summary LSA */ #define BGP_OSPF_RTYPE_EXT 5 /* OSPF External LSA, note that ASBR doesn't apply to MPLS-VPN */ #define BGP_OSPF_RTYPE_NSSA 7 /* OSPF NSSA External*/ #define BGP_OSPF_RTYPE_SHAM 129 /* OSPF-MPLS-VPN Sham link */ #define BGP_OSPF_RTYPE_METRIC_TYPE 0x1 /* LSB of RTYPE Options Field */ /* Extended community & Route dinstinguisher formats */ #define FORMAT_AS2_LOC 0x00 /* Format AS(2bytes):AN(4bytes) */ #define FORMAT_IP_LOC 0x01 /* Format IP address:AN(2bytes) */ #define FORMAT_AS4_LOC 0x02 /* Format AS(4bytes):AN(2bytes) */ /* RFC 2858 subsequent address family numbers */ #define SAFNUM_UNICAST 1 #define SAFNUM_MULCAST 2 #define SAFNUM_UNIMULC 3 #define SAFNUM_MPLS_LABEL 4 /* rfc3107 */ #define SAFNUM_TUNNEL 64 /* draft-nalawade-kapoor-tunnel-safi-02.txt */ #define SAFNUM_VPLS 65 #define SAFNUM_LAB_VPNUNICAST 128 /* Draft-rosen-rfc2547bis-03 */ #define SAFNUM_LAB_VPNMULCAST 129 #define SAFNUM_LAB_VPNUNIMULC 130 #ifndef offsetof #define offsetof(type, member) ((size_t)(&((type *)0)->member)) #endif #endif pmacct-0.14.0/src/bgp/bgp_ecommunity.c0000644000175000017500000004113111741025145016575 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* Originally based on Quagga BGP extended community attribute related functions which is: Copyright (C) 2000 Kunihiro Ishiguro GNU Zebra 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. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define __BGP_ECOMMUNITY_C #include "pmacct.h" #include "bgp_hash.h" #include "bgp_prefix.h" #include "bgp.h" #include "bgp_ecommunity.h" #include "bgp_aspath.h" /* Hash of community attribute. */ // struct hash *ecomhash; /* Allocate a new ecommunities. */ struct ecommunity * ecommunity_new () { void *tmp; tmp = malloc(sizeof (struct ecommunity)); memset(tmp, 0, sizeof (struct ecommunity)); return (struct ecommunity *) tmp; } /* Allocate ecommunities. */ void ecommunity_free (struct ecommunity *ecom) { if (ecom->val) free(ecom->val); if (ecom->str) free(ecom->str); free(ecom); } /* Add a new Extended Communities value to Extended Communities Attribute structure. When the value is already exists in the structure, we don't add the value. Newly added value is sorted by numerical order. When the value is added to the structure return 1 else return 0. */ static int ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval) { u_int8_t *p; int ret; int c; /* When this is fist value, just add it. */ if (ecom->val == NULL) { ecom->size++; ecom->val = malloc(ecom_length (ecom)); memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE); return 1; } /* If the value already exists in the structure return 0. */ c = 0; for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { ret = memcmp (p, eval->val, ECOMMUNITY_SIZE); if (ret == 0) return 0; if (ret > 0) break; } /* Add the value to the structure with numerical sorting. */ ecom->size++; ecom->val = realloc(ecom->val, ecom_length (ecom)); memmove (ecom->val + (c + 1) * ECOMMUNITY_SIZE, ecom->val + c * ECOMMUNITY_SIZE, (ecom->size - 1 - c) * ECOMMUNITY_SIZE); memcpy (ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE); return 1; } /* This function takes pointer to Extended Communites strucutre then create a new Extended Communities structure by uniq and sort each Exteneded Communities value. */ static struct ecommunity * ecommunity_uniq_sort (struct ecommunity *ecom) { int i; struct ecommunity *new; struct ecommunity_val *eval; if (! ecom) return NULL; new = ecommunity_new ();; for (i = 0; i < ecom->size; i++) { eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE)); ecommunity_add_val (new, eval); } return new; } /* Parse Extended Communites Attribute in BGP packet. */ struct ecommunity * ecommunity_parse (u_int8_t *pnt, u_short length) { struct ecommunity tmp; struct ecommunity *new; /* Length check. */ if (length % ECOMMUNITY_SIZE) return NULL; /* Prepare tmporary structure for making a new Extended Communities Attribute. */ tmp.size = length / ECOMMUNITY_SIZE; tmp.val = pnt; /* Create a new Extended Communities Attribute by uniq and sort each Extended Communities value */ new = ecommunity_uniq_sort (&tmp); return ecommunity_intern (new); } /* Duplicate the Extended Communities Attribute structure. */ struct ecommunity * ecommunity_dup (struct ecommunity *ecom) { struct ecommunity *new; new = malloc(sizeof (struct ecommunity)); new->size = ecom->size; if (new->size) { new->val = malloc(ecom->size * ECOMMUNITY_SIZE); memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE); } else new->val = NULL; return new; } /* Retrun string representation of communities attribute. */ char * ecommunity_str (struct ecommunity *ecom) { if (! ecom->str) ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY); return ecom->str; } /* Merge two Extended Communities Attribute structure. */ struct ecommunity * ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2) { if (ecom1->val) ecom1->val = realloc(ecom1->val, (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); else ecom1->val = malloc((ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val, ecom2->size * ECOMMUNITY_SIZE); ecom1->size += ecom2->size; return ecom1; } /* Intern Extended Communities Attribute. */ struct ecommunity * ecommunity_intern (struct ecommunity *ecom) { struct ecommunity *find; assert (ecom->refcnt == 0); find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern); if (find != ecom) ecommunity_free (ecom); find->refcnt++; if (! find->str) find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY); return find; } /* Unintern Extended Communities Attribute. */ void ecommunity_unintern (struct ecommunity *ecom) { struct ecommunity *ret; if (ecom->refcnt) ecom->refcnt--; /* Pull off from hash. */ if (ecom->refcnt == 0) { /* Extended community must be in the hash. */ ret = (struct ecommunity *) hash_release (ecomhash, ecom); assert (ret != NULL); ecommunity_free (ecom); } } /* Utinity function to make hash key. */ unsigned int ecommunity_hash_make (void *arg) { const struct ecommunity *ecom = arg; int c; unsigned int key; u_int8_t *pnt; key = 0; pnt = ecom->val; for (c = 0; c < ecom->size * ECOMMUNITY_SIZE; c++) key += pnt[c]; return key; } /* Compare two Extended Communities Attribute structure. */ int ecommunity_cmp (const void *arg1, const void *arg2) { const struct ecommunity *ecom1 = arg1; const struct ecommunity *ecom2 = arg2; return (ecom1->size == ecom2->size && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0); } /* Initialize Extended Comminities related hash. */ void ecommunity_init () { ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp); } /* Extended Communities token enum. */ enum ecommunity_token { ecommunity_token_rt, ecommunity_token_soo, ecommunity_token_val, ecommunity_token_unknown }; /* Get next Extended Communities token from the string. */ static const char * ecommunity_gettoken (const char *str, struct ecommunity_val *eval, enum ecommunity_token *token) { int ret; int dot = 0; int digit = 0; int separator = 0; const char *p = str; char *endptr; struct in_addr ip; as_t as = 0; u_int32_t val = 0; char buf[INET_ADDRSTRLEN + 1]; /* Skip white space. */ while (isspace ((int) *p)) { p++; str++; } /* Check the end of the line. */ if (*p == '\0') return NULL; /* "rt" and "soo" keyword parse. */ if (! isdigit ((int) *p)) { /* "rt" match check. */ if (tolower ((int) *p) == 'r') { p++; if (tolower ((int) *p) == 't') { p++; *token = ecommunity_token_rt; return p; } if (isspace ((int) *p) || *p == '\0') { *token = ecommunity_token_rt; return p; } goto error; } /* "soo" match check. */ else if (tolower ((int) *p) == 's') { p++; if (tolower ((int) *p) == 'o') { p++; if (tolower ((int) *p) == 'o') { p++; *token = ecommunity_token_soo; return p; } if (isspace ((int) *p) || *p == '\0') { *token = ecommunity_token_soo; return p; } goto error; } if (isspace ((int) *p) || *p == '\0') { *token = ecommunity_token_soo; return p; } goto error; } goto error; } /* What a mess, there are several possibilities: * * a) A.B.C.D:MN * b) EF:OPQR * c) GHJK:MN * * A.B.C.D: Four Byte IP * EF: Two byte ASN * GHJK: Four-byte ASN * MN: Two byte value * OPQR: Four byte value * */ while (isdigit ((int) *p) || *p == ':' || *p == '.') { if (*p == ':') { if (separator) goto error; separator = 1; digit = 0; if ((p - str) > INET_ADDRSTRLEN) goto error; memset (buf, 0, INET_ADDRSTRLEN + 1); memcpy (buf, str, p - str); if (dot) { /* Parsing A.B.C.D in: * A.B.C.D:MN */ ret = inet_aton (buf, &ip); if (ret == 0) goto error; } else { /* ASN */ as = strtoul (buf, &endptr, 10); if (*endptr != '\0' || as == BGP_AS4_MAX) goto error; } } else if (*p == '.') { if (separator) goto error; dot++; if (dot > 4) goto error; } else { digit = 1; /* We're past the IP/ASN part */ if (separator) { val *= 10; val += (*p - '0'); } } p++; } /* Low digit part must be there. */ if (!digit || !separator) goto error; /* Encode result into routing distinguisher. */ if (dot) { if (val > UINT16_MAX) goto error; eval->val[0] = ECOMMUNITY_ENCODE_IP; eval->val[1] = 0; memcpy (&eval->val[2], &ip, sizeof (struct in_addr)); eval->val[6] = (val >> 8) & 0xff; eval->val[7] = val & 0xff; } else if (as > BGP_AS_MAX) { if (val > UINT16_MAX) goto error; eval->val[0] = ECOMMUNITY_ENCODE_AS4; eval->val[1] = 0; eval->val[2] = (as >>24) & 0xff; eval->val[3] = (as >>16) & 0xff; eval->val[4] = (as >>8) & 0xff; eval->val[5] = as & 0xff; eval->val[6] = (val >> 8) & 0xff; eval->val[7] = val & 0xff; } else { eval->val[0] = ECOMMUNITY_ENCODE_AS; eval->val[1] = 0; eval->val[2] = (as >>8) & 0xff; eval->val[3] = as & 0xff; eval->val[4] = (val >>24) & 0xff; eval->val[5] = (val >>16) & 0xff; eval->val[6] = (val >>8) & 0xff; eval->val[7] = val & 0xff; } *token = ecommunity_token_val; return p; error: *token = ecommunity_token_unknown; return p; } /* Convert string to extended community attribute. When type is already known, please specify both str and type. str should not include keyword such as "rt" and "soo". Type is ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN. keyword_included should be zero. For example route-map's "set extcommunity" command case: "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3" type = ECOMMUNITY_ROUTE_TARGET keyword_included = 0 "soo 100:1" -> str = "100:1" type = ECOMMUNITY_SITE_ORIGIN keyword_included = 0 When string includes keyword for each extended community value. Please specify keyword_included as non-zero value. For example standard extcommunity-list case: "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1" type = 0 keyword_include = 1 */ struct ecommunity * ecommunity_str2com (const char *str, int type, int keyword_included) { struct ecommunity *ecom = NULL; enum ecommunity_token token; struct ecommunity_val eval; int keyword = 0; while ((str = ecommunity_gettoken (str, &eval, &token))) { switch (token) { case ecommunity_token_rt: case ecommunity_token_soo: if (! keyword_included || keyword) { if (ecom) ecommunity_free (ecom); return NULL; } keyword = 1; if (token == ecommunity_token_rt) { type = ECOMMUNITY_ROUTE_TARGET; } if (token == ecommunity_token_soo) { type = ECOMMUNITY_SITE_ORIGIN; } break; case ecommunity_token_val: if (keyword_included) { if (! keyword) { if (ecom) ecommunity_free (ecom); return NULL; } keyword = 0; } if (ecom == NULL) ecom = ecommunity_new (); eval.val[1] = type; ecommunity_add_val (ecom, &eval); break; case ecommunity_token_unknown: default: if (ecom) ecommunity_free (ecom); return NULL; } } return ecom; } /* Convert extended community attribute to string. Due to historical reason of industry standard implementation, there are three types of format. route-map set extcommunity format "rt 100:1 100:2" "soo 100:3" extcommunity-list "rt 100:1 rt 100:2 soo 100:3" "show ip bgp" and extcommunity-list regular expression matching "RT:100:1 RT:100:2 SoO:100:3" For each formath please use below definition for format: ECOMMUNITY_FORMAT_ROUTE_MAP ECOMMUNITY_FORMAT_COMMUNITY_LIST ECOMMUNITY_FORMAT_DISPLAY */ char * ecommunity_ecom2str (struct ecommunity *ecom, int format) { int i; u_int8_t *pnt; int encode = 0; int type = 0; #define ECOMMUNITY_STR_DEFAULT_LEN 27 int str_size; int str_pnt; char *str_buf; const char *prefix; int len = 0; int first = 1; /* For parse Extended Community attribute tupple. */ struct ecommunity_as { as_t as; u_int32_t val; } eas; struct ecommunity_ip { struct in_addr ip; u_int16_t val; } eip; if (ecom->size == 0) { str_buf = malloc(1); str_buf[0] = '\0'; return str_buf; } /* Prepare buffer. */ str_buf = malloc(ECOMMUNITY_STR_DEFAULT_LEN + 1); str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1; str_pnt = 0; for (i = 0; i < ecom->size; i++) { /* Space between each value. */ if (! first) str_buf[str_pnt++] = ' '; pnt = ecom->val + (i * 8); /* High-order octet of type. */ encode = *pnt++; if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP && encode != ECOMMUNITY_ENCODE_AS4) { len = sprintf (str_buf + str_pnt, "?"); str_pnt += len; first = 0; continue; } /* Low-order octet of type. */ type = *pnt++; if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN) { len = sprintf (str_buf + str_pnt, "?"); str_pnt += len; first = 0; continue; } switch (format) { case ECOMMUNITY_FORMAT_COMMUNITY_LIST: prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo "); break; case ECOMMUNITY_FORMAT_DISPLAY: prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:"); break; case ECOMMUNITY_FORMAT_ROUTE_MAP: prefix = ""; break; default: prefix = ""; break; } /* Make it sure size is enough. */ while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size) { str_size *= 2; str_buf = realloc(str_buf, str_size); } /* Put string into buffer. */ if (encode == ECOMMUNITY_ENCODE_AS4) { eas.as = (*pnt++ << 24); eas.as |= (*pnt++ << 16); eas.as |= (*pnt++ << 8); eas.as |= (*pnt++); eas.val = (*pnt++ << 8); eas.val |= (*pnt++); len = sprintf( str_buf + str_pnt, "%s%d:%d", prefix, eas.as, eas.val ); str_pnt += len; first = 0; } if (encode == ECOMMUNITY_ENCODE_AS) { eas.as = (*pnt++ << 8); eas.as |= (*pnt++); eas.val = (*pnt++ << 24); eas.val |= (*pnt++ << 16); eas.val |= (*pnt++ << 8); eas.val |= (*pnt++); len = sprintf (str_buf + str_pnt, "%s%d:%d", prefix, eas.as, eas.val); str_pnt += len; first = 0; } else if (encode == ECOMMUNITY_ENCODE_IP) { memcpy (&eip.ip, pnt, 4); pnt += 4; eip.val = (*pnt++ << 8); eip.val |= (*pnt++); len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix, inet_ntoa (eip.ip), eip.val); str_pnt += len; first = 0; } } return str_buf; } int ecommunity_match (const struct ecommunity *ecom1, const struct ecommunity *ecom2) { int i = 0; int j = 0; if (ecom1 == NULL && ecom2 == NULL) return 1; if (ecom1 == NULL || ecom2 == NULL) return 0; if (ecom1->size < ecom2->size) return 0; /* Every community on com2 needs to be on com1 for this to match */ while (i < ecom1->size && j < ecom2->size) { if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0) j++; i++; } if (j == ecom2->size) return 1; else return 0; } pmacct-0.14.0/src/bgp/bgp_table.c0000644000175000017500000002632411741024430015476 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* Originally based on Quagga BGP routing table which is: Copyright (C) 1998, 2001 Kunihiro Ishiguro GNU Zebra 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. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* defines */ #define __BGP_TABLE_C /* includes */ #include "pmacct.h" #include "bgp.h" static void bgp_node_delete (struct bgp_node *); static void bgp_node_free_aggressive (struct bgp_node *); static void bgp_table_free (struct bgp_table *); struct bgp_table * bgp_table_init (afi_t afi, safi_t safi) { struct bgp_table *rt; rt = malloc (sizeof (struct bgp_table)); memset (rt, 0, sizeof (struct bgp_table)); rt->type = BGP_TABLE_MAIN; rt->afi = afi; rt->safi = safi; return rt; } void bgp_table_finish (struct bgp_table **rt) { bgp_table_free (*rt); *rt = NULL; } static struct bgp_node * bgp_node_create () { struct bgp_node *rn; rn = (struct bgp_node *) malloc (sizeof (struct bgp_node)); memset (rn, 0, sizeof (struct bgp_node)); rn->info = (void **) malloc(sizeof(struct bgp_info *) * config.bgp_table_peer_buckets); memset (rn->info, 0, sizeof(struct bgp_info *) * config.bgp_table_peer_buckets); return rn; } /* Allocate new route node with prefix set. */ static struct bgp_node * bgp_node_set (struct bgp_table *table, struct prefix *prefix) { struct bgp_node *node; node = bgp_node_create (); prefix_copy (&node->p, prefix); node->table = table; return node; } /* Free route node. */ static void bgp_node_free (struct bgp_node *node) { free (node->info); free (node); } /* Free route node aggressively: also attributes and info; should be meant to be invoked only by bgp_table_free() */ static void bgp_node_free_aggressive (struct bgp_node *node) { struct bgp_info *ri, *next; u_int32_t ri_idx; for (ri_idx = 0; ri_idx < config.bgp_table_peer_buckets; ri_idx++) { for (ri = node->info[ri_idx]; ri; ri = next) { if (config.nfacctd_bgp_msglog) { char empty[] = ""; char prefix_str[INET6_ADDRSTRLEN]; char *aspath, *comm, *ecomm; memset(prefix_str, 0, INET6_ADDRSTRLEN); prefix2str(&node->p, prefix_str, INET6_ADDRSTRLEN); aspath = ri->attr->aspath ? ri->attr->aspath->str : empty; comm = ri->attr->community ? ri->attr->community->str : empty; ecomm = ri->attr->ecommunity ? ri->attr->ecommunity->str : empty; Log(LOG_INFO, "INFO ( default/core/BGP ): d Prefix: %s Path: '%s' Comms: '%s' EComms: '%s'\n", prefix_str, aspath, comm, ecomm); } next = ri->next; bgp_info_free(ri); } } free (node->info); free (node); } /* Free route table. */ static void bgp_table_free (struct bgp_table *rt) { struct bgp_node *tmp_node; struct bgp_node *node; if (rt == NULL) return; node = rt->top; while (node) { if (node->l_left) { node = node->l_left; continue; } if (node->l_right) { node = node->l_right; continue; } tmp_node = node; node = node->parent; if (node != NULL) { if (node->l_left == tmp_node) node->l_left = NULL; else node->l_right = NULL; bgp_node_free_aggressive (tmp_node); } else { bgp_node_free_aggressive (tmp_node); break; } } free (rt); return; } /* Utility mask array. */ static u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; /* Common prefix route genaration. */ static void route_common (struct prefix *n, struct prefix *p, struct prefix *new) { int i; u_char diff; u_char mask; u_char *np = (u_char *)&n->u.prefix; u_char *pp = (u_char *)&p->u.prefix; u_char *newp = (u_char *)&new->u.prefix; for (i = 0; i < p->prefixlen / 8; i++) { if (np[i] == pp[i]) newp[i] = np[i]; else break; } new->prefixlen = i * 8; if (new->prefixlen != p->prefixlen) { diff = np[i] ^ pp[i]; mask = 0x80; while (new->prefixlen < p->prefixlen && !(mask & diff)) { mask >>= 1; new->prefixlen++; } newp[i] = np[i] & maskbit[new->prefixlen % 8]; } } /* Macro version of check_bit (). */ #define CHECK_BIT(X,P) ((((u_char *)(X))[(P) / 8]) >> (7 - ((P) % 8)) & 1) /* Check bit of the prefix. */ static int check_bit (u_char *prefix, u_char prefixlen) { int offset; int shift; u_char *p = (u_char *)prefix; assert (prefixlen <= 128); offset = prefixlen / 8; shift = 7 - (prefixlen % 8); return (p[offset] >> shift & 1); } /* Macro version of set_link (). */ #define SET_LINK(X,Y) (X)->link[CHECK_BIT(&(Y)->prefix,(X)->prefixlen)] = (Y);\ (Y)->parent = (X) static void set_link (struct bgp_node *node, struct bgp_node *new) { int bit; bit = check_bit (&new->p.u.prefix, node->p.prefixlen); assert (bit == 0 || bit == 1); node->link[bit] = new; new->parent = node; } /* Lock node. */ struct bgp_node * bgp_lock_node (struct bgp_node *node) { node->lock++; return node; } /* Unlock node. */ void bgp_unlock_node (struct bgp_node *node) { node->lock--; if (node->lock == 0) bgp_node_delete (node); } /* Find matched prefix. */ struct bgp_node * bgp_node_match (const struct bgp_table *table, struct prefix *p, struct bgp_peer *peer) { struct bgp_node *node; struct bgp_node *matched; struct bgp_info *info; u_int32_t modulo = peer->fd % config.bgp_table_peer_buckets; matched = NULL; node = table->top; /* Walk down tree. If there is matched route then store it to matched. */ while (node && node->p.prefixlen <= p->prefixlen && prefix_match(&node->p, p)) { for (info = node->info[modulo]; info; info = info->next) { if (info->peer == peer) { matched = node; break; } } node = node->link[check_bit(&p->u.prefix, node->p.prefixlen)]; } /* If matched route found, return it. */ if (matched) return bgp_lock_node (matched); return NULL; } struct bgp_node * bgp_node_match_ipv4 (const struct bgp_table *table, struct in_addr *addr, struct bgp_peer *peer) { struct prefix_ipv4 p; memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; p.prefix = *addr; return bgp_node_match (table, (struct prefix *) &p, peer); } #ifdef ENABLE_IPV6 struct bgp_node * bgp_node_match_ipv6 (const struct bgp_table *table, struct in6_addr *addr, struct bgp_peer *peer) { struct prefix_ipv6 p; memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; p.prefixlen = IPV6_MAX_PREFIXLEN; p.prefix = *addr; return bgp_node_match (table, (struct prefix *) &p, peer); } #endif /* ENABLE_IPV6 */ /* Add node to routing table. */ struct bgp_node * bgp_node_get (struct bgp_table *const table, struct prefix *p) { struct bgp_node *new; struct bgp_node *node; struct bgp_node *match; match = NULL; node = table->top; while (node && node->p.prefixlen <= p->prefixlen && prefix_match (&node->p, p)) { if (node->p.prefixlen == p->prefixlen) { bgp_lock_node (node); return node; } match = node; node = node->link[check_bit(&p->u.prefix, node->p.prefixlen)]; } if (node == NULL) { new = bgp_node_set (table, p); if (match) set_link (match, new); else table->top = new; } else { new = bgp_node_create (); route_common (&node->p, p, &new->p); new->p.family = p->family; new->table = table; set_link (new, node); if (match) set_link (match, new); else table->top = new; if (new->p.prefixlen != p->prefixlen) { match = new; new = bgp_node_set (table, p); set_link (match, new); table->count++; } } table->count++; bgp_lock_node (new); return new; } /* Delete node from the routing table. */ static void bgp_node_delete (struct bgp_node *node) { struct bgp_node *child; struct bgp_node *parent; u_int32_t ri_idx; assert (node->lock == 0); for (ri_idx = 0; ri_idx < config.bgp_table_peer_buckets; ri_idx++) assert (node->info[ri_idx] == NULL); if (node->l_left && node->l_right) return; if (node->l_left) child = node->l_left; else child = node->l_right; parent = node->parent; if (child) child->parent = parent; if (parent) { if (parent->l_left == node) parent->l_left = child; else parent->l_right = child; } else node->table->top = child; node->table->count--; bgp_node_free (node); /* If parent node is stub then delete it also. */ if (parent && parent->lock == 0) bgp_node_delete (parent); } /* Get fist node and lock it. This function is useful when one want to lookup all the node exist in the routing table. */ struct bgp_node * bgp_table_top (const struct bgp_table *const table) { /* If there is no node in the routing table return NULL. */ if (table->top == NULL) return NULL; /* Lock the top node and return it. */ bgp_lock_node (table->top); return table->top; } /* Unlock current node and lock next node then return it. */ struct bgp_node * bgp_route_next (struct bgp_node *node) { struct bgp_node *next; struct bgp_node *start; /* Node may be deleted from bgp_unlock_node so we have to preserve next node's pointer. */ if (node->l_left) { next = node->l_left; bgp_lock_node (next); bgp_unlock_node (node); return next; } if (node->l_right) { next = node->l_right; bgp_lock_node (next); bgp_unlock_node (node); return next; } start = node; while (node->parent) { if (node->parent->l_left == node && node->parent->l_right) { next = node->parent->l_right; bgp_lock_node (next); bgp_unlock_node (start); return next; } node = node->parent; } bgp_unlock_node (start); return NULL; } /* Unlock current node and lock next node until limit. */ struct bgp_node * bgp_route_next_until (struct bgp_node *node, struct bgp_node *limit) { struct bgp_node *next; struct bgp_node *start; /* Node may be deleted from bgp_unlock_node so we have to preserve next node's pointer. */ if (node->l_left) { next = node->l_left; bgp_lock_node (next); bgp_unlock_node (node); return next; } if (node->l_right) { next = node->l_right; bgp_lock_node (next); bgp_unlock_node (node); return next; } start = node; while (node->parent && node != limit) { if (node->parent->l_left == node && node->parent->l_right) { next = node->parent->l_right; bgp_lock_node (next); bgp_unlock_node (start); return next; } node = node->parent; } bgp_unlock_node (start); return NULL; } unsigned long bgp_table_count (const struct bgp_table *table) { return table->count; } pmacct-0.14.0/src/bgp/bgp_prefix.h0000644000175000017500000001354411741024553015717 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* * Originally based on Quagga prefix structure which is: * * Copyright (C) 1998 Kunihiro Ishiguro * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef _BGP_PREFIX_H_ #define _BGP_PREFIX_H_ /* * A struct prefix contains an address family, a prefix length, and an * address. This can represent either a 'network prefix' as defined * by CIDR, where the 'host bits' of the prefix are 0 * (e.g. AF_INET:10.0.0.0/8), or an address and netmask * (e.g. AF_INET:10.0.0.9/8), such as might be configured on an * interface. */ /* IPv4 and IPv6 unified prefix structure. */ struct prefix { u_char family; u_char prefixlen; union { u_char prefix; struct in_addr prefix4; #ifdef ENABLE_IPV6 struct in6_addr prefix6; #endif /* ENABLE_IPV6 */ struct { struct in_addr id; struct in_addr adv_router; } lp; u_char val[8]; } u __attribute__ ((aligned (8))); }; /* IPv4 prefix structure. */ struct prefix_ipv4 { u_char family; u_char prefixlen; struct in_addr prefix __attribute__ ((aligned (8))); }; /* IPv6 prefix structure. */ #ifdef ENABLE_IPV6 struct prefix_ipv6 { u_char family; u_char prefixlen; struct in6_addr prefix __attribute__ ((aligned (8))); }; #endif /* ENABLE_IPV6 */ struct prefix_ls { u_char family; u_char prefixlen; struct in_addr id __attribute__ ((aligned (8))); struct in_addr adv_router; }; /* Prefix for routing distinguisher. */ struct prefix_rd { u_char family; u_char prefixlen; u_char val[8] __attribute__ ((aligned (8))); }; #ifndef INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 #endif /* INET_ADDRSTRLEN */ #ifndef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN 46 #endif /* INET6_ADDRSTRLEN */ #ifndef INET6_BUFSIZ #define INET6_BUFSIZ 51 #endif /* INET6_BUFSIZ */ /* Max bit/byte length of IPv4 address. */ #define IPV4_MAX_BYTELEN 4 #define IPV4_MAX_BITLEN 32 #define IPV4_MAX_PREFIXLEN 32 #define IPV4_ADDR_CMP(D,S) memcmp ((D), (S), IPV4_MAX_BYTELEN) #define IPV4_ADDR_SAME(D,S) (memcmp ((D), (S), IPV4_MAX_BYTELEN) == 0) #define IPV4_ADDR_COPY(D,S) memcpy ((D), (S), IPV4_MAX_BYTELEN) #define IPV4_NET0(a) ((((u_int32_t) (a)) & 0xff000000) == 0x00000000) #define IPV4_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000) #define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000) /* Max bit/byte length of IPv6 address. */ #define IPV6_MAX_BYTELEN 16 #define IPV6_MAX_BITLEN 128 #define IPV6_MAX_PREFIXLEN 128 #define IPV6_ADDR_CMP(D,S) memcmp ((D), (S), IPV6_MAX_BYTELEN) #define IPV6_ADDR_SAME(D,S) (memcmp ((D), (S), IPV6_MAX_BYTELEN) == 0) #define IPV6_ADDR_COPY(D,S) memcpy ((D), (S), IPV6_MAX_BYTELEN) /* Count prefix size from mask length */ #define PSIZE(a) (((a) + 7) / (8)) /* Prefix's family member. */ #define PREFIX_FAMILY(p) ((p)->family) /* Prototypes. */ #if (!defined __BGP_PREFIX_C) #define EXT extern #else #define EXT #endif EXT int afi2family (int); EXT int family2afi (int); EXT struct prefix *prefix_new (void); EXT void prefix_free (struct prefix *); EXT const char *prefix_family_str (const struct prefix *); EXT int prefix_blen (const struct prefix *); EXT int str2prefix (const char *, struct prefix *); EXT int prefix2str (const struct prefix *, char *, int); EXT int prefix_match (const struct prefix *, const struct prefix *); EXT int prefix_same (const struct prefix *, const struct prefix *); EXT int prefix_cmp (const struct prefix *, const struct prefix *); EXT void prefix_copy (struct prefix *dest, const struct prefix *src); EXT void apply_mask (struct prefix *); EXT struct prefix_ipv4 *prefix_ipv4_new (void); EXT void prefix_ipv4_free (struct prefix_ipv4 *); EXT int str2prefix_ipv4 (const char *, struct prefix_ipv4 *); EXT void apply_mask_ipv4 (struct prefix_ipv4 *); #define PREFIX_COPY_IPV4(DST, SRC) \ *((struct prefix_ipv4 *)(DST)) = *((const struct prefix_ipv4 *)(SRC)); EXT int prefix_ipv4_any (const struct prefix_ipv4 *); EXT void apply_classful_mask_ipv4 (struct prefix_ipv4 *); EXT u_char ip_masklen (struct in_addr); EXT void masklen2ip (int, struct in_addr *); /* returns the network portion of the host address */ EXT in_addr_t ipv4_network_addr (in_addr_t hostaddr, int masklen); /* given the address of a host on a network and the network mask length, * calculate the broadcast address for that network; * special treatment for /31: returns the address of the other host * on the network by flipping the host bit */ EXT in_addr_t ipv4_broadcast_addr (in_addr_t hostaddr, int masklen); EXT int netmask_str2prefix_str (const char *, const char *, char *); #ifdef ENABLE_IPV6 EXT struct prefix_ipv6 *prefix_ipv6_new (void); EXT void prefix_ipv6_free (struct prefix_ipv6 *); EXT int str2prefix_ipv6 (const char *, struct prefix_ipv6 *); EXT void apply_mask_ipv6 (struct prefix_ipv6 *); #define PREFIX_COPY_IPV6(DST, SRC) \ *((struct prefix_ipv6 *)(DST)) = *((const struct prefix_ipv6 *)(SRC)); EXT int ip6_masklen (struct in6_addr); EXT void masklen2ip6 (int, struct in6_addr *); EXT void str2in6_addr (const char *, struct in6_addr *); EXT const char *inet6_ntoa (struct in6_addr); #endif /* ENABLE_IPV6 */ EXT int all_digit (const char *); #undef EXT #endif pmacct-0.14.0/src/bgp/bgp_hash.h0000644000175000017500000000412111741024445015334 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* Originally based on Quagga hash routine which is: Copyright (C) 1998 Kunihiro Ishiguro GNU Zebra 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. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _BGP_HASH_H_ #define _BGP_HASH_H_ /* Default hash table size. */ #define HASHTABSIZE 1024 struct hash_backet { /* Linked list. */ struct hash_backet *next; /* Hash key. */ unsigned int key; /* Data. */ void *data; }; struct hash { /* Hash backet. */ struct hash_backet **index; /* Hash table size. */ unsigned int size; /* Key make function. */ unsigned int (*hash_key) (void *); /* Data compare function. */ int (*hash_cmp) (const void *, const void *); /* Backet alloc. */ unsigned long count; }; #if (!defined __BGP_HASH_C) #define EXT extern #else #define EXT #endif EXT struct hash *hash_create (unsigned int (*) (void *), int (*) (const void *, const void *)); EXT struct hash *hash_create_size (unsigned int, unsigned int (*) (void *), int (*) (const void *, const void *)); EXT void *hash_get (struct hash *, void *, void * (*) (void *)); EXT void *hash_alloc_intern (void *); EXT void *hash_lookup (struct hash *, void *); EXT void *hash_release (struct hash *, void *); EXT void hash_iterate (struct hash *, void (*) (struct hash_backet *, void *), void *); EXT void hash_clean (struct hash *, void (*) (void *)); EXT void hash_free (struct hash *); #undef EXT #endif pmacct-0.14.0/src/bgp/bgp_ecommunity.h0000644000175000017500000000606211741025164016607 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* Originally based on Quagga BGP extended community attribute related functions which is: Copyright (C) 2000 Kunihiro Ishiguro GNU Zebra 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. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _BGP_ECOMMUNITY_H_ #define _BGP_ECOMMUNITY_H_ /* High-order octet of the Extended Communities type field. */ #define ECOMMUNITY_ENCODE_AS 0x00 #define ECOMMUNITY_ENCODE_IP 0x01 #define ECOMMUNITY_ENCODE_AS4 0x02 /* Low-order octet of the Extended Communityes type field. */ #define ECOMMUNITY_ROUTE_TARGET 0x02 #define ECOMMUNITY_SITE_ORIGIN 0x03 /* Extended communities attribute string format. */ #define ECOMMUNITY_FORMAT_ROUTE_MAP 0 #define ECOMMUNITY_FORMAT_COMMUNITY_LIST 1 #define ECOMMUNITY_FORMAT_DISPLAY 2 /* Extended Communities value is eight octet long. */ #define ECOMMUNITY_SIZE 8 /* Extended Communities type flag. */ #define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 /* Extended Communities attribute. */ struct ecommunity { /* Reference counter. */ unsigned long refcnt; /* Size of Extended Communities attribute. */ int size; /* Extended Communities value. */ u_int8_t *val; /* Human readable format string. */ char *str; }; /* Extended community value is eight octet. */ struct ecommunity_val { char val[ECOMMUNITY_SIZE]; }; #define ecom_length(X) ((X)->size * ECOMMUNITY_SIZE) #if (!defined __BGP_ECOMMUNITY_C) #define EXT extern #else #define EXT #endif EXT void ecommunity_init (); EXT void ecommunity_free (struct ecommunity *); EXT struct ecommunity *ecommunity_new (void); EXT struct ecommunity *ecommunity_parse (u_int8_t *, u_short); EXT struct ecommunity *ecommunity_dup (struct ecommunity *); EXT struct ecommunity *ecommunity_merge (struct ecommunity *, struct ecommunity *); EXT struct ecommunity *ecommunity_intern (struct ecommunity *); EXT int ecommunity_cmp (const void *, const void *); EXT void ecommunity_unintern (struct ecommunity *); EXT unsigned int ecommunity_hash_make (void *); EXT struct ecommunity *ecommunity_str2com (const char *, int, int); EXT char *ecommunity_ecom2str (struct ecommunity *, int); EXT int ecommunity_match (const struct ecommunity *, const struct ecommunity *); EXT char *ecommunity_str (struct ecommunity *); #undef EXT #endif pmacct-0.14.0/src/bgp/Makefile.in0000644000175000017500000000162211154224051015451 0ustar paolopaolo# $Id: Makefile.in,v 1.1.1.1 2009/03/06 13:42:01 paolo Exp $ prefix=@prefix@ exec_prefix=@exec_prefix@ bindir=@bindir@ sbindir=@sbindir@ libexecdir=@libexecdir@ datadir=@datadir@ mandir=@mandir@ sysconfdir=@sysconfdir@ srcdir=@srcdir@ top_srcdir=@top_srcdir@ VPATH=@srcdir@ CC=@CC@ DEFS=@DEFS@ LDFLAGS=@LDFLAGS@ CFLAGS=$(DEFS) -I$(srcdir) -I.. @CFLAGS@ CPPFLAGS=@CPPFLAGS@ LIBS=@LIBS@ INSTALL=@INSTALL@ RANLIB=@RANLIB@ #CFLAGS+= TARGETS=libbgp.a COMMON= all: $(TARGETS) libbgp.a: bgp.o bgp_aspath.o bgp_community.o bgp_ecommunity.o bgp_hash.o bgp_prefix.o bgp_table.o $(COMMON) ar rc $@ bgp.o bgp_aspath.o bgp_community.o bgp_ecommunity.o bgp_hash.o bgp_prefix.o bgp_table.o $(COMMON) $(RANLIB) $@ clean: rm -f $(TARGETS) *.o core *.core realclean: clean rm -rf autom4te.cache Makefile config.log config.status distclean: realclean rm -f config.h* configure strip: strip $(TARGETS) install: all pmacct-0.14.0/src/bgp/bgp.h0000644000175000017500000001725411665132207014345 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #include "bgp_prefix.h" #include "bgp_packet.h" #include "bgp_table.h" #include "bgp_community.h" #include "bgp_ecommunity.h" #ifndef _BGP_H_ #define _BGP_H_ /* defines */ /* BGP finite state machine status. */ #define Idle 1 #define Connect 2 #define Active 3 #define OpenSent 4 #define OpenConfirm 5 #define Established 6 #define Clearing 7 #define Deleted 8 #define BGP_ATTR_MIN_LEN 3 /* Attribute flag, type length. */ /* BGP4 attribute type codes. */ #define BGP_ATTR_ORIGIN 1 #define BGP_ATTR_AS_PATH 2 #define BGP_ATTR_NEXT_HOP 3 #define BGP_ATTR_MULTI_EXIT_DISC 4 #define BGP_ATTR_LOCAL_PREF 5 #define BGP_ATTR_ATOMIC_AGGREGATE 6 #define BGP_ATTR_AGGREGATOR 7 #define BGP_ATTR_COMMUNITIES 8 #define BGP_ATTR_ORIGINATOR_ID 9 #define BGP_ATTR_CLUSTER_LIST 10 #define BGP_ATTR_DPA 11 #define BGP_ATTR_ADVERTISER 12 #define BGP_ATTR_RCID_PATH 13 #define BGP_ATTR_MP_REACH_NLRI 14 #define BGP_ATTR_MP_UNREACH_NLRI 15 #define BGP_ATTR_EXT_COMMUNITIES 16 #define BGP_ATTR_AS4_PATH 17 #define BGP_ATTR_AS4_AGGREGATOR 18 #define BGP_ATTR_AS_PATHLIMIT 21 /* BGP Attribute flags. */ #define BGP_ATTR_FLAG_OPTIONAL 0x80 /* Attribute is optional. */ #define BGP_ATTR_FLAG_TRANS 0x40 /* Attribute is transitive. */ #define BGP_ATTR_FLAG_PARTIAL 0x20 /* Attribute is partial. */ #define BGP_ATTR_FLAG_EXTLEN 0x10 /* Extended length flag. */ /* BGP misc */ #define MAX_BGP_PEERS_DEFAULT 4 #define MAX_HOPS_FOLLOW_NH 20 #define MAX_NH_SELF_REFERENCES 1 /* Maximum BGP standard/extended community patterns supported: nfacctd_bgp_stdcomm_pattern, nfacctd_bgp_extcomm_pattern */ #define MAX_BGP_COMM_PATTERNS 16 /* requires as_t */ #include "bgp_aspath.h" /* structures */ struct bgp_peer_buf { char *base; int len; int truncated_len; }; struct bgp_peer { int fd; int lock; u_int8_t status; as_t myas; as_t as; u_int16_t ht; time_t last_keepalive; struct host_addr id; struct host_addr addr; u_int8_t cap_mp; char *cap_4as; u_int32_t msglen; struct bgp_peer_buf buf; }; struct bgp_nlri { afi_t afi; safi_t safi; u_char *nlri; u_int16_t length; }; struct bgp_attr { struct aspath *aspath; struct community *community; struct ecommunity *ecommunity; unsigned long refcnt; u_int32_t flag; struct in_addr nexthop; struct host_addr mp_nexthop; u_int32_t med; u_int32_t local_pref; struct { u_int32_t as; u_char ttl; } pathlimit; u_char origin; }; struct bgp_comm_range { u_int32_t first; u_int32_t last; }; /* prototypes */ #if (!defined __BGP_C) #define EXT extern #else #define EXT #endif EXT void nfacctd_bgp_wrapper(); EXT void skinny_bgp_daemon(); EXT int bgp_marker_check(struct bgp_header *, int); EXT int bgp_keepalive_msg(char *); EXT int bgp_open_msg(char *, char *, int, struct bgp_peer *); EXT int bgp_update_msg(struct bgp_peer *, char *); EXT int bgp_attr_parse(struct bgp_peer *, struct bgp_attr *, char *, int, struct bgp_nlri *, struct bgp_nlri *); EXT int bgp_attr_parse_community(struct bgp_peer *, u_int16_t, struct bgp_attr *, char *, u_int8_t); EXT int bgp_attr_parse_ecommunity(struct bgp_peer *, u_int16_t, struct bgp_attr *, char *, u_int8_t); EXT int bgp_attr_parse_aspath(struct bgp_peer *, u_int16_t, struct bgp_attr *, char *, u_int8_t); EXT int bgp_attr_parse_as4path(struct bgp_peer *, u_int16_t, struct bgp_attr *, char *, u_int8_t, struct aspath **); EXT int bgp_attr_parse_nexthop(struct bgp_peer *, u_int16_t, struct bgp_attr *, char *, u_int8_t); EXT int bgp_attr_parse_med(struct bgp_peer *, u_int16_t, struct bgp_attr *, char *, u_char); EXT int bgp_attr_parse_local_pref(struct bgp_peer *, u_int16_t, struct bgp_attr *, char *, u_char); EXT int bgp_attr_parse_mp_reach(struct bgp_peer *, u_int16_t, struct bgp_attr *, char *, struct bgp_nlri *); EXT int bgp_attr_parse_mp_unreach(struct bgp_peer *, u_int16_t, struct bgp_attr *, char *, struct bgp_nlri *); EXT int bgp_nlri_parse(struct bgp_peer *, void *, struct bgp_nlri *); EXT int bgp_process_update(struct bgp_peer *, struct prefix *, void *, afi_t, safi_t, rd_t *, char *); EXT int bgp_process_withdraw(struct bgp_peer *, struct prefix *, void *, afi_t, safi_t, rd_t *, char *); EXT int bgp_afi2family(int); EXT int bgp_rd2str(char *, rd_t *); EXT int bgp_str2rd(rd_t *, char *); EXT struct bgp_info_extra *bgp_info_extra_new(); EXT void bgp_info_extra_free(struct bgp_info_extra **); EXT struct bgp_info_extra *bgp_info_extra_get(struct bgp_info *); EXT struct bgp_info *bgp_info_new(); EXT void bgp_info_add(struct bgp_node *, struct bgp_info *, u_int32_t); EXT void bgp_info_delete(struct bgp_node *, struct bgp_info *, u_int32_t); EXT void bgp_info_free(struct bgp_info *); EXT void bgp_attr_init(); EXT struct bgp_attr *bgp_attr_intern(struct bgp_attr *); EXT void bgp_attr_unintern (struct bgp_attr *); EXT void *bgp_attr_hash_alloc (void *); EXT void bgp_peer_init(struct bgp_peer *); EXT void bgp_peer_close(struct bgp_peer *); EXT int bgp_attr_munge_as4path(struct bgp_peer *, struct bgp_attr *, struct aspath *); EXT void load_comm_patterns(char **, char **, char **); EXT void load_peer_src_as_comm_ranges(char *, char *); EXT void evaluate_comm_patterns(char *, char *, char **, int); //EXT as_t evaluate_last_asn(char *); EXT as_t evaluate_last_asn(struct aspath *); EXT as_t evaluate_first_asn(char *); EXT void bgp_srcdst_lookup(struct packet_ptrs *); EXT void bgp_follow_nexthop_lookup(struct packet_ptrs *); EXT void write_neighbors_file(char *); EXT void process_bgp_md5_file(int, struct bgp_md5_table *); EXT unsigned int attrhash_key_make(void *); EXT int attrhash_cmp(void *, void *); EXT void attrhash_init(); EXT void cache_to_pkt_bgp_primitives(struct pkt_bgp_primitives *, struct cache_bgp_primitives *); EXT void pkt_to_cache_bgp_primitives(struct cache_bgp_primitives *, struct pkt_bgp_primitives *, u_int64_t); EXT void bgp_config_checks(struct configuration *); /* global variables */ EXT struct bgp_peer *peers; EXT struct hash *attrhash; EXT struct hash *ashash; EXT struct hash *comhash; EXT struct hash *ecomhash; EXT char *std_comm_patterns[MAX_BGP_COMM_PATTERNS]; EXT char *ext_comm_patterns[MAX_BGP_COMM_PATTERNS]; EXT char *std_comm_patterns_to_asn[MAX_BGP_COMM_PATTERNS]; EXT struct bgp_comm_range peer_src_as_ifrange; EXT struct bgp_comm_range peer_src_as_asrange; EXT struct bgp_table *rib[AFI_MAX][SAFI_MAX]; #undef EXT #endif pmacct-0.14.0/src/bgp/bgp_prefix.c0000644000175000017500000003723511741024462015714 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* * Originally based on Quagga prefix related functions which is: * * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #define __BGP_PREFIX_C #include "pmacct.h" #include "bgp_packet.h" #include "bgp_prefix.h" /* Maskbit. */ static u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; /* Number of bits in prefix type. */ #ifndef PNBBY #define PNBBY 8 #endif /* PNBBY */ #define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff) /* Address Famiy Identifier to Address Family converter. */ int afi2family (int afi) { if (afi == AFI_IP) return AF_INET; #ifdef ENABLE_IPV6 else if (afi == AFI_IP6) return AF_INET6; #endif /* ENABLE_IPV6 */ return 0; } int family2afi (int family) { if (family == AF_INET) return AFI_IP; #ifdef ENABLE_IPV6 else if (family == AF_INET6) return AFI_IP6; #endif /* ENABLE_IPV6 */ return 0; } /* If n includes p prefix then return 1 else return 0. */ int prefix_match (const struct prefix *n, const struct prefix *p) { int offset; int shift; /* Set both prefix's head pointer. */ const u_char *np = (const u_char *)&n->u.prefix; const u_char *pp = (const u_char *)&p->u.prefix; /* If n's prefix is longer than p's one return 0. */ if (n->prefixlen > p->prefixlen) return 0; offset = n->prefixlen / PNBBY; shift = n->prefixlen % PNBBY; if (shift) if (maskbit[shift] & (np[offset] ^ pp[offset])) return 0; while (offset--) if (np[offset] != pp[offset]) return 0; return 1; } /* Copy prefix from src to dest. */ void prefix_copy (struct prefix *dest, const struct prefix *src) { dest->family = src->family; dest->prefixlen = src->prefixlen; if (src->family == AF_INET) dest->u.prefix4 = src->u.prefix4; #ifdef ENABLE_IPV6 else if (src->family == AF_INET6) dest->u.prefix6 = src->u.prefix6; #endif /* ENABLE_IPV6 */ else if (src->family == AF_UNSPEC) { dest->u.lp.id = src->u.lp.id; dest->u.lp.adv_router = src->u.lp.adv_router; } else { //zlog (NULL, LOG_ERR, "prefix_copy(): Unknown address family %d", // src->family); assert (0); } } /* * Return 1 if the address/netmask contained in the prefix structure * is the same, and else return 0. For this routine, 'same' requires * that not only the prefix length and the network part be the same, * but also the host part. Thus, 10.0.0.1/8 and 10.0.0.2/8 are not * the same. Note that this routine has the same return value sense * as '==' (which is different from prefix_cmp). */ int prefix_same (const struct prefix *p1, const struct prefix *p2) { if (p1->family == p2->family && p1->prefixlen == p2->prefixlen) { if (p1->family == AF_INET) if (IPV4_ADDR_SAME (&p1->u.prefix, &p2->u.prefix)) return 1; #ifdef ENABLE_IPV6 if (p1->family == AF_INET6 ) if (IPV6_ADDR_SAME (&p1->u.prefix, &p2->u.prefix)) return 1; #endif /* ENABLE_IPV6 */ } return 0; } /* * Return 0 if the network prefixes represented by the struct prefix * arguments are the same prefix, and 1 otherwise. Network prefixes * are considered the same if the prefix lengths are equal and the * network parts are the same. Host bits (which are considered masked * by the prefix length) are not significant. Thus, 10.0.0.1/8 and * 10.0.0.2/8 are considered equivalent by this routine. Note that * this routine has the same return sense as strcmp (which is different * from prefix_same). */ int prefix_cmp (const struct prefix *p1, const struct prefix *p2) { int offset; int shift; /* Set both prefix's head pointer. */ const u_char *pp1 = (const u_char *)&p1->u.prefix; const u_char *pp2 = (const u_char *)&p2->u.prefix; if (p1->family != p2->family || p1->prefixlen != p2->prefixlen) return 1; offset = p1->prefixlen / 8; shift = p1->prefixlen % 8; if (shift) if (maskbit[shift] & (pp1[offset] ^ pp2[offset])) return 1; while (offset--) if (pp1[offset] != pp2[offset]) return 1; return 0; } /* Return prefix family type string. */ const char * prefix_family_str (const struct prefix *p) { if (p->family == AF_INET) return "inet"; #ifdef ENABLE_IPV6 if (p->family == AF_INET6) return "inet6"; #endif /* ENABLE_IPV6 */ return "unspec"; } /* Allocate new prefix_ipv4 structure. */ struct prefix_ipv4 * prefix_ipv4_new () { struct prefix_ipv4 *p; /* Call prefix_new to allocate a full-size struct prefix to avoid problems where the struct prefix_ipv4 is cast to struct prefix and unallocated bytes were being referenced (e.g. in structure assignments). */ p = (struct prefix_ipv4 *)prefix_new(); p->family = AF_INET; return p; } /* Free prefix_ipv4 structure. */ void prefix_ipv4_free (struct prefix_ipv4 *p) { prefix_free((struct prefix *)p); } /* When string format is invalid return 0. */ int str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p) { int ret; int plen; char *pnt; char *cp; /* Find slash inside string. */ pnt = strchr (str, '/'); /* String doesn't contail slash. */ if (pnt == NULL) { /* Convert string to prefix. */ ret = inet_aton (str, &p->prefix); if (ret == 0) return 0; /* If address doesn't contain slash we assume it host address. */ p->family = AF_INET; p->prefixlen = IPV4_MAX_BITLEN; return ret; } else { cp = malloc ((pnt - str) + 1); strncpy (cp, str, pnt - str); *(cp + (pnt - str)) = '\0'; ret = inet_aton (cp, &p->prefix); free (cp); /* Get prefix length. */ plen = (u_char) atoi (++pnt); if (plen > IPV4_MAX_PREFIXLEN) return 0; p->family = AF_INET; p->prefixlen = plen; } return ret; } /* Convert masklen into IP address's netmask. */ void masklen2ip (int masklen, struct in_addr *netmask) { u_char *pnt; int bit; int offset; memset (netmask, 0, sizeof (struct in_addr)); pnt = (unsigned char *) netmask; offset = masklen / 8; bit = masklen % 8; while (offset--) *pnt++ = 0xff; if (bit) *pnt = maskbit[bit]; } /* Convert IP address's netmask into integer. We assume netmask is sequential one. Argument netmask should be network byte order. */ u_char ip_masklen (struct in_addr netmask) { u_char len; u_char *pnt; u_char *end; u_char val; len = 0; pnt = (u_char *) &netmask; end = pnt + 4; while ((pnt < end) && (*pnt == 0xff)) { len+= 8; pnt++; } if (pnt < end) { val = *pnt; while (val) { len++; val <<= 1; } } return len; } /* Apply mask to IPv4 prefix. */ void apply_mask_ipv4 (struct prefix_ipv4 *p) { u_char *pnt; int index; int offset; index = p->prefixlen / 8; if (index < 4) { pnt = (u_char *) &p->prefix; offset = p->prefixlen % 8; pnt[index] &= maskbit[offset]; index++; while (index < 4) pnt[index++] = 0; } } /* If prefix is 0.0.0.0/0 then return 1 else return 0. */ int prefix_ipv4_any (const struct prefix_ipv4 *p) { return (p->prefix.s_addr == 0 && p->prefixlen == 0); } #ifdef ENABLE_IPV6 /* Allocate a new ip version 6 route */ struct prefix_ipv6 * prefix_ipv6_new (void) { struct prefix_ipv6 *p; /* Allocate a full-size struct prefix to avoid problems with structure size mismatches. */ p = (struct prefix_ipv6 *)prefix_new(); p->family = AF_INET6; return p; } /* Free prefix for IPv6. */ void prefix_ipv6_free (struct prefix_ipv6 *p) { prefix_free((struct prefix *)p); } /* If given string is valid return pin6 else return NULL */ int str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p) { char *pnt; char *cp; int ret; pnt = strchr (str, '/'); /* If string doesn't contain `/' treat it as host route. */ if (pnt == NULL) { ret = inet_pton (AF_INET6, str, &p->prefix); if (ret != 1) return 0; p->prefixlen = IPV6_MAX_BITLEN; } else { int plen; cp = malloc((pnt - str) + 1); strncpy (cp, str, pnt - str); *(cp + (pnt - str)) = '\0'; ret = inet_pton (AF_INET6, cp, &p->prefix); free (cp); if (ret != 1) return 0; plen = (u_char) atoi (++pnt); if (plen > 128) return 0; p->prefixlen = plen; } p->family = AF_INET6; return ret; } /* Convert struct in6_addr netmask into integer. * FIXME return u_char as ip_maskleni() does. */ int ip6_masklen (struct in6_addr netmask) { int len = 0; unsigned char val; unsigned char *pnt; pnt = (unsigned char *) & netmask; while ((*pnt == 0xff) && len < 128) { len += 8; pnt++; } if (len < 128) { val = *pnt; while (val) { len++; val <<= 1; } } return len; } void masklen2ip6 (int masklen, struct in6_addr *netmask) { unsigned char *pnt; int bit; int offset; memset (netmask, 0, sizeof (struct in6_addr)); pnt = (unsigned char *) netmask; offset = masklen / 8; bit = masklen % 8; while (offset--) *pnt++ = 0xff; if (bit) *pnt = maskbit[bit]; } void apply_mask_ipv6 (struct prefix_ipv6 *p) { u_char *pnt; int index; int offset; index = p->prefixlen / 8; if (index < 16) { pnt = (u_char *) &p->prefix; offset = p->prefixlen % 8; pnt[index] &= maskbit[offset]; index++; while (index < 16) pnt[index++] = 0; } } void str2in6_addr (const char *str, struct in6_addr *addr) { int i; unsigned int x; /* %x must point to unsinged int */ for (i = 0; i < 16; i++) { sscanf (str + (i * 2), "%02x", &x); addr->s6_addr[i] = x & 0xff; } } #endif /* ENABLE_IPV6 */ void apply_mask (struct prefix *p) { switch (p->family) { case AF_INET: apply_mask_ipv4 ((struct prefix_ipv4 *)p); break; #ifdef ENABLE_IPV6 case AF_INET6: apply_mask_ipv6 ((struct prefix_ipv6 *)p); break; #endif /* ENABLE_IPV6 */ default: break; } return; } /* Utility function of convert between struct prefix <=> union sockunion. * FIXME This function isn't used anywhere. */ /* struct prefix * sockunion2prefix (const union sockunion *dest, const union sockunion *mask) { if (dest->sa.sa_family == AF_INET) { struct prefix_ipv4 *p; p = prefix_ipv4_new (); p->family = AF_INET; p->prefix = dest->sin.sin_addr; p->prefixlen = ip_masklen (mask->sin.sin_addr); return (struct prefix *) p; } #ifdef ENABLE_IPV6 if (dest->sa.sa_family == AF_INET6) { struct prefix_ipv6 *p; p = prefix_ipv6_new (); p->family = AF_INET6; p->prefixlen = ip6_masklen (mask->sin6.sin6_addr); memcpy (&p->prefix, &dest->sin6.sin6_addr, sizeof (struct in6_addr)); return (struct prefix *) p; } #endif // ENABLE_IPV6 return NULL; } */ /* Utility function of convert between struct prefix <=> union sockunion. */ /* struct prefix * sockunion2hostprefix (const union sockunion *su) { if (su->sa.sa_family == AF_INET) { struct prefix_ipv4 *p; p = prefix_ipv4_new (); p->family = AF_INET; p->prefix = su->sin.sin_addr; p->prefixlen = IPV4_MAX_BITLEN; return (struct prefix *) p; } #ifdef ENABLE_IPV6 if (su->sa.sa_family == AF_INET6) { struct prefix_ipv6 *p; p = prefix_ipv6_new (); p->family = AF_INET6; p->prefixlen = IPV6_MAX_BITLEN; memcpy (&p->prefix, &su->sin6.sin6_addr, sizeof (struct in6_addr)); return (struct prefix *) p; } #endif // ENABLE_IPV6 return NULL; } */ int prefix_blen (const struct prefix *p) { switch (p->family) { case AF_INET: return IPV4_MAX_BYTELEN; break; #ifdef ENABLE_IPV6 case AF_INET6: return IPV6_MAX_BYTELEN; break; #endif /* ENABLE_IPV6 */ } return 0; } /* Generic function for conversion string to struct prefix. */ int str2prefix (const char *str, struct prefix *p) { int ret; /* First we try to convert string to struct prefix_ipv4. */ ret = str2prefix_ipv4 (str, (struct prefix_ipv4 *) p); if (ret) return ret; #ifdef ENABLE_IPV6 /* Next we try to convert string to struct prefix_ipv6. */ ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p); if (ret) return ret; #endif /* ENABLE_IPV6 */ return 0; } int prefix2str (const struct prefix *p, char *str, int size) { char buf[BUFSIZ]; inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ); snprintf (str, size, "%s/%d", buf, p->prefixlen); return 0; } struct prefix * prefix_new () { struct prefix *p; p = malloc (sizeof *p); memset(p, 0, sizeof *p); return p; } /* Free prefix structure. */ void prefix_free (struct prefix *p) { free (p); } /* Utility function. Check the string only contains digit * character. * FIXME str.[c|h] would be better place for this function. */ int all_digit (const char *str) { for (; *str != '\0'; str++) if (!isdigit ((int) *str)) return 0; return 1; } /* Utility function to convert ipv4 prefixes to Classful prefixes */ void apply_classful_mask_ipv4 (struct prefix_ipv4 *p) { u_int32_t destination; destination = ntohl (p->prefix.s_addr); if (p->prefixlen == IPV4_MAX_PREFIXLEN); /* do nothing for host routes */ else if (IN_CLASSC (destination)) { p->prefixlen=24; apply_mask_ipv4(p); } else if (IN_CLASSB(destination)) { p->prefixlen=16; apply_mask_ipv4(p); } else { p->prefixlen=8; apply_mask_ipv4(p); } } in_addr_t ipv4_network_addr (in_addr_t hostaddr, int masklen) { struct in_addr mask; masklen2ip (masklen, &mask); return hostaddr & mask.s_addr; } in_addr_t ipv4_broadcast_addr (in_addr_t hostaddr, int masklen) { struct in_addr mask; masklen2ip (masklen, &mask); return (masklen != IPV4_MAX_PREFIXLEN-1) ? /* normal case */ (hostaddr | ~mask.s_addr) : /* special case for /31 */ (hostaddr ^ ~mask.s_addr); } /* Utility function to convert ipv4 netmask to prefixes ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16" ex.) "1.0.0.0" NULL => "1.0.0.0/8" */ int netmask_str2prefix_str (const char *net_str, const char *mask_str, char *prefix_str) { struct in_addr network; struct in_addr mask; u_char prefixlen; u_int32_t destination; int ret; ret = inet_aton (net_str, &network); if (! ret) return 0; if (mask_str) { ret = inet_aton (mask_str, &mask); if (! ret) return 0; prefixlen = ip_masklen (mask); } else { destination = ntohl (network.s_addr); if (network.s_addr == 0) prefixlen = 0; else if (IN_CLASSC (destination)) prefixlen = 24; else if (IN_CLASSB (destination)) prefixlen = 16; else if (IN_CLASSA (destination)) prefixlen = 8; else return 0; } sprintf (prefix_str, "%s/%d", net_str, prefixlen); return 1; } #ifdef ENABLE_IPV6 /* Utility function for making IPv6 address string. */ const char * inet6_ntoa (struct in6_addr addr) { static char buf[INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, &addr, buf, INET6_ADDRSTRLEN); return buf; } #endif /* ENABLE_IPV6 */ pmacct-0.14.0/src/bgp/bgp_community.h0000644000175000017500000000567511741025071016450 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* Originally based on Quagga BGP community attribute related functions which is: Copyright (C) 1998, 2001 Kunihiro Ishiguro GNU Zebra 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. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _BGP_COMMUNITY_H_ #define _BGP_COMMUNITY_H_ /* Communities attribute. */ struct community { /* Reference count of communities value. */ unsigned long refcnt; /* Communities value size. */ int size; /* Communities value. */ u_int32_t *val; /* String of community attribute. This sring is used by vty output and expanded community-list for regular expression match. */ char *str; }; /* Well-known communities value. */ #define COMMUNITY_INTERNET 0x0 #define COMMUNITY_NO_EXPORT 0xFFFFFF01 #define COMMUNITY_NO_ADVERTISE 0xFFFFFF02 #define COMMUNITY_NO_EXPORT_SUBCONFED 0xFFFFFF03 #define COMMUNITY_LOCAL_AS 0xFFFFFF03 /* Macros of community attribute. */ #define com_length(X) ((X)->size * 4) #define com_lastval(X) ((X)->val + (X)->size - 1) #define com_nthval(X,n) ((X)->val + (n)) /* Prototypes of communities attribute functions. */ #if (!defined __BGP_COMMUNITY_C) #define EXT extern #else #define EXT #endif EXT void community_init (); EXT void community_free (struct community *); EXT struct community *community_uniq_sort (struct community *); EXT struct community *community_parse (u_int32_t *, u_short); EXT struct community *community_intern (struct community *); EXT void community_unintern (struct community *); EXT char *community_str (struct community *); EXT unsigned int community_hash_make (struct community *); EXT struct community *community_str2com (const char *); EXT int community_match (const struct community *, const struct community *); EXT int community_cmp (const struct community *, const struct community *); EXT struct community *community_merge (struct community *, struct community *); EXT struct community *community_delete (struct community *, struct community *); EXT struct community *community_dup (struct community *); EXT int community_include (struct community *, u_int32_t); EXT void community_del_val (struct community *, u_int32_t *); EXT unsigned long community_count (void); EXT struct hash *community_hash (void); #undef EXT #endif pmacct-0.14.0/src/bgp/bgp.c0000644000175000017500000021736411736330465014351 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* defines */ #define __BGP_C /* includes */ #include "pmacct.h" #include "bgp.h" #include "bgp_hash.h" #include "thread_pool.h" /* variables to be exported away */ thread_pool_t *bgp_pool; /* Functions */ #if defined ENABLE_THREADS void nfacctd_bgp_wrapper() { /* initialize variables */ if (!config.nfacctd_bgp_port) config.nfacctd_bgp_port = BGP_TCP_PORT; /* initialize threads pool */ bgp_pool = allocate_thread_pool(1); assert(bgp_pool); Log(LOG_DEBUG, "DEBUG ( default/core/BGP ): %d thread(s) initialized\n", 1); /* giving a kick to the BGP thread */ send_to_pool(bgp_pool, skinny_bgp_daemon, NULL); } #endif void skinny_bgp_daemon() { int slen, ret, sock, rc, peers_idx, allowed; struct host_addr addr; struct bgp_header bhdr; struct bgp_peer *peer; struct bgp_open *bopen; char bgp_packet[BGP_MAX_PACKET_SIZE], *bgp_packet_ptr; char bgp_reply_pkt[BGP_MAX_PACKET_SIZE], *bgp_reply_pkt_ptr; #if defined ENABLE_IPV6 struct sockaddr_storage server, client; struct ipv6_mreq multi_req6; #else struct sockaddr server, client; #endif afi_t afi; safi_t safi; int clen = sizeof(client), yes=1; u_int16_t remote_as = 0; u_int32_t remote_as4 = 0; time_t now; struct hosts_table allow; struct bgp_md5_table bgp_md5; /* select() stuff */ fd_set read_descs, bkp_read_descs; int select_fd, select_num; /* initial cleanups */ reload_map_bgp_thread = FALSE; memset(&server, 0, sizeof(server)); memset(&client, 0, sizeof(client)); memset(bgp_packet, 0, BGP_MAX_PACKET_SIZE); memset(&allow, 0, sizeof(struct hosts_table)); bgp_attr_init(); /* socket creation for BGP server: IPv4 only */ #if (defined ENABLE_IPV6 && defined V4_MAPPED) if (!config.nfacctd_bgp_ip) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&server; sa6->sin6_family = AF_INET6; sa6->sin6_port = htons(config.nfacctd_bgp_port); slen = sizeof(struct sockaddr_in6); } #else if (!config.nfacctd_bgp_ip) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&server; sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = htonl(0); sa4->sin_port = htons(config.nfacctd_bgp_port); slen = sizeof(struct sockaddr_in); } #endif else { trim_spaces(config.nfacctd_bgp_ip); ret = str_to_addr(config.nfacctd_bgp_ip, &addr); if (!ret) { Log(LOG_ERR, "ERROR ( default/core/BGP ): 'nfacctd_bgp_ip' value is not a valid IPv4/IPv6 address. Terminating thread.\n"); exit_all(1); } slen = addr_to_sa((struct sockaddr *)&server, &addr, config.nfacctd_bgp_port); } if (!config.nfacctd_bgp_max_peers) config.nfacctd_bgp_max_peers = MAX_BGP_PEERS_DEFAULT; Log(LOG_INFO, "INFO ( default/core/BGP ): maximum BGP peers allowed: %d\n", config.nfacctd_bgp_max_peers); peers = malloc(config.nfacctd_bgp_max_peers*sizeof(struct bgp_peer)); memset(peers, 0, config.nfacctd_bgp_max_peers*sizeof(struct bgp_peer)); if (!config.bgp_table_peer_buckets) config.bgp_table_peer_buckets = DEFAULT_BGP_INFO_HASH; sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_STREAM, 0); if (sock < 0) { Log(LOG_ERR, "ERROR ( default/core/BGP ): thread socket() failed. Terminating thread.\n"); exit_all(1); } if (config.nfacctd_bgp_ipprec) { int opt = config.nfacctd_bgp_ipprec << 5; rc = setsockopt(sock, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)); if (rc < 0) Log(LOG_ERR, "WARN ( default/core/BGP ): setsockopt() failed for IP_TOS (errno: %d).\n", errno); } rc = Setsocksize(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)); if (rc < 0) Log(LOG_ERR, "WARN ( default/core/BGP ): Setsocksize() failed for SO_REUSEADDR (errno: %d).\n", errno); rc = bind(sock, (struct sockaddr *) &server, slen); if (rc < 0) { char null_ip_address[] = "0.0.0.0"; char *ip_address; ip_address = config.nfacctd_bgp_ip ? config.nfacctd_bgp_ip : null_ip_address; Log(LOG_ERR, "ERROR ( default/core/BGP ): bind() to ip=%s port=%d/tcp failed (errno: %d).\n", ip_address, config.nfacctd_bgp_port, errno); exit_all(1); } rc = listen(sock, config.nfacctd_bgp_max_peers); if (rc < 0) { Log(LOG_ERR, "ERROR ( default/core/BGP ): listen() failed (errno: %d).\n", errno); exit_all(1); } /* Preparing for syncronous I/O multiplexing */ select_fd = 0; FD_ZERO(&bkp_read_descs); FD_SET(sock, &bkp_read_descs); { char srv_string[INET6_ADDRSTRLEN]; struct host_addr srv_addr; u_int16_t srv_port; sa_to_addr(&server, &srv_addr, &srv_port); addr_to_str(srv_string, &srv_addr); Log(LOG_INFO, "INFO ( default/core/BGP ): waiting for BGP data on %s:%u\n", srv_string, srv_port); } /* Preparing ACL, if any */ if (config.nfacctd_bgp_allow_file) load_allow_file(config.nfacctd_bgp_allow_file, &allow); /* Preparing MD5 keys, if any */ if (config.nfacctd_bgp_md5_file) { load_bgp_md5_file(config.nfacctd_bgp_md5_file, &bgp_md5); if (bgp_md5.num) process_bgp_md5_file(sock, &bgp_md5); } /* Let's initialize clean shared RIB */ for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { rib[afi][safi] = bgp_table_init(afi, safi); } } for (;;) { select_again: select_fd = sock; for (peers_idx = 0; peers_idx < config.nfacctd_bgp_max_peers; peers_idx++) if (select_fd < peers[peers_idx].fd) select_fd = peers[peers_idx].fd; select_fd++; memcpy(&read_descs, &bkp_read_descs, sizeof(bkp_read_descs)); select_num = select(select_fd, &read_descs, NULL, NULL, NULL); if (select_num < 0) goto select_again; if (reload_map_bgp_thread) { if (config.nfacctd_bgp_md5_file) { unload_bgp_md5_file(&bgp_md5); if (bgp_md5.num) process_bgp_md5_file(sock, &bgp_md5); // process unload load_bgp_md5_file(config.nfacctd_bgp_md5_file, &bgp_md5); if (bgp_md5.num) process_bgp_md5_file(sock, &bgp_md5); // process load } reload_map_bgp_thread = FALSE; } /* New connection is coming in */ if (FD_ISSET(sock, &read_descs)) { int peers_check_idx, peers_num; for (peer = NULL, peers_idx = 0; peers_idx < config.nfacctd_bgp_max_peers; peers_idx++) { if (peers[peers_idx].fd == 0) { peer = &peers[peers_idx]; bgp_peer_init(peer); break; } /* XXX: replenish sessions with expired keepalives */ } if (!peer) { int fd; /* We briefly accept the new connection to be able to drop it */ Log(LOG_ERR, "ERROR ( default/core/BGP ): Insufficient number of BGP peers has been configured by 'nfacctd_bgp_max_peers' (%d).\n", config.nfacctd_bgp_max_peers); fd = accept(sock, (struct sockaddr *) &client, &clen); close(fd); goto select_again; } peer->fd = accept(sock, (struct sockaddr *) &client, &clen); /* If an ACL is defined, here we check against and enforce it */ if (allow.num) allowed = check_allow(&allow, (struct sockaddr *)&client); else allowed = TRUE; if (!allowed) { bgp_peer_close(peer); goto select_again; } FD_SET(peer->fd, &bkp_read_descs); peer->addr.family = ((struct sockaddr *)&client)->sa_family; if (peer->addr.family == AF_INET) peer->addr.address.ipv4.s_addr = ((struct sockaddr_in *)&client)->sin_addr.s_addr; #if defined ENABLE_IPV6 else if (peer->addr.family == AF_INET6) memcpy(&peer->addr.address.ipv6, &((struct sockaddr_in6 *)&client)->sin6_addr, 16); #endif /* Check: only one TCP connection is allowed per peer */ for (peers_check_idx = 0, peers_num = 0; peers_check_idx < config.nfacctd_bgp_max_peers; peers_check_idx++) { if (peers_idx != peers_check_idx && !memcmp(&peers[peers_check_idx].addr, &peer->addr, sizeof(peers[peers_check_idx].addr))) { now = time(NULL); if ((now - peers[peers_check_idx].last_keepalive) > peers[peers_check_idx].ht) { Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Replenishing stale connection by peer.\n", inet_ntoa(peers[peers_check_idx].id.address.ipv4)); FD_CLR(peers[peers_check_idx].fd, &bkp_read_descs); bgp_peer_close(&peers[peers_check_idx]); } else { Log(LOG_ERR, "ERROR ( default/core/BGP ): [Id: %s] Refusing new connection from existing peer (residual holdtime: %u).\n", inet_ntoa(peers[peers_check_idx].id.address.ipv4), (peers[peers_check_idx].ht - (now - peers[peers_check_idx].last_keepalive))); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } } else { if (peers[peers_check_idx].fd) peers_num++; } } Log(LOG_INFO, "INFO ( default/core/BGP ): BGP peers usage: %u/%u\n", peers_num, config.nfacctd_bgp_max_peers); if (config.nfacctd_bgp_neighbors_file) write_neighbors_file(config.nfacctd_bgp_neighbors_file); goto select_again; } /* We have something coming in: let's lookup which peer is thatl XXX: to be optimized */ for (peer = NULL, peers_idx = 0; peers_idx < config.nfacctd_bgp_max_peers; peers_idx++) { if (peers[peers_idx].fd && FD_ISSET(peers[peers_idx].fd, &read_descs)) { peer = &peers[peers_idx]; break; } } if (!peer) { Log(LOG_ERR, "ERROR ( default/core/BGP ): message delivered to an unknown peer (FD bits: %d; FD max: %d)\n", select_num, select_fd); goto select_again; } peer->msglen = ret = recv(peer->fd, bgp_packet, BGP_MAX_PACKET_SIZE, 0); if (ret <= 0) { Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Existing BGP connection was reset (%d).\n", inet_ntoa(peer->id.address.ipv4), errno); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } else if (peer->msglen+peer->buf.truncated_len < BGP_HEADER_SIZE) { Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Received malformed BGP packet (too short).\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } else { /* Appears a valid peer with a valid BGP message: before continuing let's see if it's time to send a KEEPALIVE back */ now = time(NULL); if (peer->status == Established && ((now - peer->last_keepalive) > (peer->ht / 2))) { bgp_reply_pkt_ptr = bgp_reply_pkt; bgp_reply_pkt_ptr += bgp_keepalive_msg(bgp_reply_pkt_ptr); ret = send(peer->fd, bgp_reply_pkt, bgp_reply_pkt_ptr - bgp_reply_pkt, 0); peer->last_keepalive = now; } /* BGP payload reassembly if required */ if (peer->buf.truncated_len) { if (peer->buf.truncated_len+peer->msglen > peer->buf.len) { char *newptr; peer->buf.len += peer->buf.truncated_len+peer->msglen; newptr = malloc(peer->buf.len); if (newptr) { memcpy(newptr, peer->buf.base, peer->buf.truncated_len); free(peer->buf.base); peer->buf.base = newptr; } else { Log(LOG_ERR, "ERROR ( default/core/BGP ): malloc() failed (newptr). Exiting ..\n"); exit_all(1); } } memcpy(peer->buf.base+peer->buf.truncated_len, bgp_packet, peer->msglen); peer->msglen += peer->buf.truncated_len; peer->buf.truncated_len = 0; bgp_packet_ptr = peer->buf.base; } else { if (peer->buf.len > BGP_MAX_PACKET_SIZE) { realloc(peer->buf.base, BGP_MAX_PACKET_SIZE); memset(peer->buf.base, 0, BGP_MAX_PACKET_SIZE); peer->buf.len = BGP_MAX_PACKET_SIZE; } bgp_packet_ptr = bgp_packet; } memset(&bhdr, 0, sizeof(bhdr)); for ( ; peer->msglen > 0; peer->msglen -= ntohs(bhdr.bgpo_len), bgp_packet_ptr += ntohs(bhdr.bgpo_len)) { memcpy(&bhdr, bgp_packet_ptr, sizeof(bhdr)); /* BGP payload fragmentation check */ if (peer->msglen < BGP_HEADER_SIZE || peer->msglen < ntohs(bhdr.bgpo_len)) { peer->buf.truncated_len = peer->msglen; if (bgp_packet_ptr != peer->buf.base) { char *aux_buf; aux_buf = malloc(peer->buf.truncated_len); if (aux_buf) { memcpy(aux_buf, bgp_packet_ptr, peer->buf.truncated_len); memcpy(peer->buf.base, aux_buf, peer->buf.truncated_len); free(aux_buf); } else { Log(LOG_ERR, "ERROR ( default/core/BGP ): malloc() failed (aux_buf). Exiting ..\n"); exit_all(1); } } // goto bgp_recv; goto select_again; } if (!bgp_marker_check(&bhdr, BGP_MARKER_SIZE)) { Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Received malformed BGP packet (marker check failed).\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } memset(bgp_reply_pkt, 0, BGP_MAX_PACKET_SIZE); switch (bhdr.bgpo_type) { case BGP_OPEN: remote_as = remote_as4 = 0; if (peer->status < OpenSent) { peer->status = Active; bopen = (struct bgp_open *) bgp_packet; if (bopen->bgpo_version == BGP_VERSION4) { char bgp_open_cap_reply[BGP_MAX_PACKET_SIZE-BGP_MIN_OPEN_MSG_SIZE]; char *bgp_open_cap_reply_ptr = bgp_open_cap_reply, *bgp_open_cap_ptr; remote_as = ntohs(bopen->bgpo_myas); peer->ht = MAX(5, ntohs(bopen->bgpo_holdtime)); peer->id.family = AF_INET; peer->id.address.ipv4.s_addr = bopen->bgpo_id; /* OPEN options parsing */ if (bopen->bgpo_optlen && bopen->bgpo_optlen >= 2) { u_int8_t len, opt_type, opt_len, cap_type; char *ptr; ptr = bgp_packet + BGP_MIN_OPEN_MSG_SIZE; memset(bgp_open_cap_reply, 0, sizeof(bgp_open_cap_reply)); for (len = bopen->bgpo_optlen; len > 0; len -= opt_len, ptr += opt_len) { opt_type = (u_int8_t) ptr[0]; opt_len = (u_int8_t) ptr[1]; if (opt_len > bopen->bgpo_optlen) { Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Received malformed BGP packet (option length).\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } /* * If we stumble upon capabilities let's curse through them to find * some we are forced to support (ie. MP-BGP or 4-bytes AS support) */ if (opt_type == BGP_OPTION_CAPABILITY) { char *optcap_ptr; int optcap_len; bgp_open_cap_ptr = ptr; ptr += 2; len -= 2; optcap_ptr = ptr; optcap_len = len; while (optcap_len > 0) { u_int8_t cap_len = optcap_ptr[1]; u_int8_t cap_type = optcap_ptr[0]; if (cap_type == BGP_CAPABILITY_MULTIPROTOCOL) { char *cap_ptr = optcap_ptr+2; struct capability_mp_data cap_data; memcpy(&cap_data, cap_ptr, sizeof(cap_data)); Log(LOG_DEBUG, "DEBUG ( default/core/BGP ): Capability: MultiProtocol [%x] AFI [%x] SAFI [%x]\n", cap_type, ntohs(cap_data.afi), cap_data.safi); peer->cap_mp = TRUE; memcpy(bgp_open_cap_reply_ptr, bgp_open_cap_ptr, opt_len+2); bgp_open_cap_reply_ptr += opt_len+2; } else if (cap_type == BGP_CAPABILITY_4_OCTET_AS_NUMBER) { char *cap_ptr = optcap_ptr+2; u_int32_t as4_ptr; if (cap_len == CAPABILITY_CODE_AS4_LEN) { struct capability_as4 cap_data; memcpy(&cap_data, cap_ptr, sizeof(cap_data)); Log(LOG_DEBUG, "DEBUG ( default/core/BGP ): Capability: 4-bytes AS [%x] ASN [%u]\n", cap_type, ntohl(cap_data.as4)); memcpy(&as4_ptr, cap_ptr, 4); remote_as4 = ntohl(as4_ptr); memcpy(bgp_open_cap_reply_ptr, bgp_open_cap_ptr, opt_len+2); peer->cap_4as = bgp_open_cap_reply_ptr+4; bgp_open_cap_reply_ptr += opt_len+2; } else { Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Received malformed BGP packet (malformed AS4 option).\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } } optcap_ptr += cap_len+2; optcap_len -= cap_len+2; } } else { ptr += 2; len -= 2; } } } /* Let's grasp the remote ASN */ if (remote_as == BGP_AS_TRANS) { if (remote_as4 && remote_as4 != BGP_AS_TRANS) peer->as = remote_as4; /* It is not valid to use the transitional ASN in the BGP OPEN and present an ASN == 0 or ASN == 23456 in the 4AS capability */ else { Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Received malformed BGP packet (invalid AS4 option).\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } } else { if (remote_as4 == 0 || remote_as4 == remote_as) peer->as = remote_as; /* It is not valid to not use the transitional ASN in the BGP OPEN and present an ASN != remote_as in the 4AS capability */ else { Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Received malformed BGP packet (mismatching AS4 option).\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } } Log(LOG_DEBUG, "DEBUG ( default/core/BGP ): [Id: %s] BGP_OPEN: Asn: %u HoldTime: %u\n", inet_ntoa(peer->id.address.ipv4), peer->as, peer->ht); bgp_reply_pkt_ptr = bgp_reply_pkt; /* Replying to OPEN message */ peer->myas = peer->as; ret = bgp_open_msg(bgp_reply_pkt_ptr, bgp_open_cap_reply, bgp_open_cap_reply_ptr-bgp_open_cap_reply, peer); if (ret > 0) bgp_reply_pkt_ptr += ret; else { Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Local peer is 4AS while remote peer is 2AS: unsupported configuration.\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } /* sticking a KEEPALIVE to it */ bgp_reply_pkt_ptr += bgp_keepalive_msg(bgp_reply_pkt_ptr); ret = send(peer->fd, bgp_reply_pkt, bgp_reply_pkt_ptr - bgp_reply_pkt, 0); peer->last_keepalive = now; } else { Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Received malformed BGP packet (unsupported version).\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } // peer->status = OpenSent; peer->status = Established; } /* If we already passed successfully through an BGP OPEN exchange let's just ignore further BGP OPEN messages */ break; case BGP_NOTIFICATION: Log(LOG_DEBUG, "DEBUG ( default/core/BGP ): [Id: %s] BGP_NOTIFICATION received\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; break; case BGP_KEEPALIVE: Log(LOG_DEBUG, "DEBUG ( default/core/BGP ): [Id: %s] BGP_KEEPALIVE received\n", inet_ntoa(peer->id.address.ipv4)); if (peer->status >= OpenSent) { if (peer->status < Established) peer->status = Established; bgp_reply_pkt_ptr = bgp_reply_pkt; bgp_reply_pkt_ptr += bgp_keepalive_msg(bgp_reply_pkt_ptr); ret = send(peer->fd, bgp_reply_pkt, bgp_reply_pkt_ptr - bgp_reply_pkt, 0); peer->last_keepalive = now; Log(LOG_DEBUG, "DEBUG ( default/core/BGP ): [Id: %s] BGP_KEEPALIVE sent\n", inet_ntoa(peer->id.address.ipv4)); } /* If we didn't pass through a successful BGP OPEN exchange just yet let's temporarily discard BGP KEEPALIVEs */ break; case BGP_UPDATE: if (peer->status < Established) { Log(LOG_DEBUG, "DEBUG ( default/core/BGP ): [Id: %s] BGP UPDATE received (no neighbor). Discarding.\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } ret = bgp_update_msg(peer, bgp_packet_ptr); if (ret < 0) Log(LOG_WARNING, "WARN ( default/core/BGP ): [Id: %s] BGP UPDATE: malformed (%d).\n", inet_ntoa(peer->id.address.ipv4), ret); break; default: Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] Received malformed BGP packet (unsupported message type).\n", inet_ntoa(peer->id.address.ipv4)); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer); goto select_again; } } } } } /* Marker check. */ int bgp_marker_check(struct bgp_header *bhdr, int length) { int i; for (i = 0; i < length; i++) if (bhdr->bgpo_marker[i] != 0xff) return 0; return 1; } /* write BGP KEEPALIVE msg */ int bgp_keepalive_msg(char *msg) { struct bgp_header bhdr; memset(&bhdr.bgpo_marker, 0xff, BGP_MARKER_SIZE); bhdr.bgpo_type = BGP_KEEPALIVE; bhdr.bgpo_len = htons(BGP_HEADER_SIZE); memcpy(msg, &bhdr, sizeof(bhdr)); return BGP_HEADER_SIZE; } /* write BGP OPEN msg */ int bgp_open_msg(char *msg, char *cp_msg, int cp_msglen, struct bgp_peer *peer) { struct bgp_open *bopen_reply = (struct bgp_open *) msg; char my_id_static[] = "1.2.3.4", *my_id = my_id_static; struct host_addr my_id_addr; u_int16_t local_as; u_int32_t *local_as4; memset(bopen_reply->bgpo_marker, 0xff, BGP_MARKER_SIZE); bopen_reply->bgpo_type = BGP_OPEN; bopen_reply->bgpo_version = BGP_VERSION4; bopen_reply->bgpo_holdtime = htons(peer->ht); if (peer->myas > BGP_AS_MAX) { if (peer->cap_4as) { bopen_reply->bgpo_myas = htons(BGP_AS_TRANS); local_as4 = (u_int32_t *) peer->cap_4as; *local_as4 = htonl(peer->myas); } /* This is currently an unsupported configuration */ else return -1; } else { local_as = peer->myas; bopen_reply->bgpo_myas = htons(local_as); if (peer->cap_4as) { local_as4 = (u_int32_t *) peer->cap_4as; *local_as4 = htonl(peer->myas); } } bopen_reply->bgpo_optlen = cp_msglen; bopen_reply->bgpo_len = htons(BGP_MIN_OPEN_MSG_SIZE + bopen_reply->bgpo_optlen); if (config.nfacctd_bgp_ip) { my_id = config.nfacctd_bgp_ip; str_to_addr(my_id, &my_id_addr); if (my_id_addr.family != AF_INET) { my_id = my_id_static; str_to_addr(my_id, &my_id_addr); } } else str_to_addr(my_id, &my_id_addr); bopen_reply->bgpo_id = my_id_addr.address.ipv4.s_addr; memcpy(msg+BGP_MIN_OPEN_MSG_SIZE, cp_msg, cp_msglen); return BGP_MIN_OPEN_MSG_SIZE + cp_msglen; } int bgp_update_msg(struct bgp_peer *peer, char *pkt) { struct bgp_header bhdr; u_char *startp, *endp; struct bgp_attr attr; u_int16_t attribute_len; u_int16_t update_len; u_int16_t withdraw_len; u_int16_t end, tmp; struct bgp_nlri update; struct bgp_nlri withdraw; struct bgp_nlri mp_update; struct bgp_nlri mp_withdraw; int ret; /* Set initial values. */ memset(&attr, 0, sizeof (struct bgp_attr)); memset(&update, 0, sizeof (struct bgp_nlri)); memset(&withdraw, 0, sizeof (struct bgp_nlri)); memset(&mp_update, 0, sizeof (struct bgp_nlri)); memset(&mp_withdraw, 0, sizeof (struct bgp_nlri)); memcpy(&bhdr, pkt, sizeof(bhdr)); end = ntohs(bhdr.bgpo_len); end -= BGP_HEADER_SIZE; pkt += BGP_HEADER_SIZE; /* handling Unfeasible routes */ memcpy(&tmp, pkt, 2); withdraw_len = ntohs(tmp); if (withdraw_len > end) return -1; else { end -= withdraw_len; pkt += 2; end -= 2; } if (withdraw_len > 0) { withdraw.afi = AFI_IP; withdraw.safi = SAFI_UNICAST; withdraw.nlri = pkt; withdraw.length = withdraw_len; pkt += withdraw_len; } /* handling Attributes */ memcpy(&tmp, pkt, 2); attribute_len = ntohs(tmp); if (attribute_len > end) return -1; else { end -= attribute_len; pkt += 2; end -= 2; } if (attribute_len > 0) { ret = bgp_attr_parse(peer, &attr, pkt, attribute_len, &mp_update, &mp_withdraw); if (ret < 0) return ret; pkt += attribute_len; } update_len = end; end = 0; if (update_len > 0) { update.afi = AFI_IP; update.safi = SAFI_UNICAST; update.nlri = pkt; update.length = update_len; } if (withdraw.length) bgp_nlri_parse(peer, NULL, &withdraw); /* NLRI parsing */ if (update.length) bgp_nlri_parse(peer, &attr, &update); if (mp_update.length && mp_update.afi == AFI_IP && (mp_update.safi == SAFI_UNICAST || mp_update.safi == SAFI_MPLS_LABEL)) bgp_nlri_parse(peer, &attr, &mp_update); if (mp_withdraw.length && mp_withdraw.afi == AFI_IP && (mp_withdraw.safi == SAFI_UNICAST || mp_withdraw.safi == SAFI_MPLS_LABEL)) bgp_nlri_parse (peer, NULL, &mp_withdraw); if (mp_update.length && mp_update.afi == AFI_IP && mp_update.safi == SAFI_MPLS_VPN) bgp_nlri_parse(peer, &attr, &mp_update); if (mp_withdraw.length && mp_withdraw.afi == AFI_IP && mp_withdraw.safi == SAFI_MPLS_VPN) bgp_nlri_parse(peer, NULL, &mp_withdraw); if (mp_update.length && mp_update.afi == AFI_IP6 && (mp_update.safi == SAFI_UNICAST || mp_update.safi == SAFI_MPLS_LABEL)) bgp_nlri_parse(peer, &attr, &mp_update); if (mp_withdraw.length && mp_withdraw.afi == AFI_IP6 && (mp_withdraw.safi == SAFI_UNICAST || mp_withdraw.safi == SAFI_MPLS_LABEL)) bgp_nlri_parse (peer, NULL, &mp_withdraw); /* Receipt of End-of-RIB can be processed here; being a silent BGP receiver only, honestly it doesn't matter to us */ /* Everything is done. We unintern temporary structures which interned in bgp_attr_parse(). */ if (attr.aspath) aspath_unintern(attr.aspath); if (attr.community) community_unintern(attr.community); if (attr.ecommunity) ecommunity_unintern(attr.ecommunity); return 0; } /* BGP UPDATE Attribute parsing */ int bgp_attr_parse(struct bgp_peer *peer, struct bgp_attr *attr, char *ptr, int len, struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw) { int to_the_end = len, ret; u_int8_t flag, type, *tmp, mp_nlri = 0; u_int16_t tmp16, attr_len; struct aspath *as4_path = NULL; while (to_the_end > 0) { if (to_the_end < BGP_ATTR_MIN_LEN) return -1; tmp = (u_int8_t *) ptr++; to_the_end--; flag = *tmp; tmp = (u_int8_t *) ptr++; to_the_end--; type = *tmp; /* Attribute length */ if (flag & BGP_ATTR_FLAG_EXTLEN) { memcpy(&tmp16, ptr, 2); ptr += 2; to_the_end -= 2; attr_len = ntohs(tmp16); if (attr_len > to_the_end) return -1; } else { tmp = (u_int8_t *) ptr++; to_the_end--; attr_len = *tmp; if (attr_len > to_the_end) return -1; } switch (type) { case BGP_ATTR_AS_PATH: ret = bgp_attr_parse_aspath(peer, attr_len, attr, ptr, flag); break; case BGP_ATTR_AS4_PATH: ret = bgp_attr_parse_as4path(peer, attr_len, attr, ptr, flag, &as4_path); break; case BGP_ATTR_NEXT_HOP: ret = bgp_attr_parse_nexthop(peer, attr_len, attr, ptr, flag); break; case BGP_ATTR_COMMUNITIES: ret = bgp_attr_parse_community(peer, attr_len, attr, ptr, flag); break; case BGP_ATTR_EXT_COMMUNITIES: ret = bgp_attr_parse_ecommunity(peer, attr_len, attr, ptr, flag); break; case BGP_ATTR_MULTI_EXIT_DISC: ret = bgp_attr_parse_med(peer, attr_len, attr, ptr, flag); break; case BGP_ATTR_LOCAL_PREF: ret = bgp_attr_parse_local_pref(peer, attr_len, attr, ptr, flag); break; case BGP_ATTR_MP_REACH_NLRI: ret = bgp_attr_parse_mp_reach(peer, attr_len, attr, ptr, mp_update); mp_nlri = TRUE; break; case BGP_ATTR_MP_UNREACH_NLRI: ret = bgp_attr_parse_mp_unreach(peer, attr_len, attr, ptr, mp_withdraw); mp_nlri = TRUE; break; default: ret = 0; break; } if (ret < 0) return ret; if (!mp_nlri) { ptr += attr_len; to_the_end -= attr_len; } else { ptr += to_the_end; to_the_end = 0; } } if (as4_path) { /* AS_PATH and AS4_PATH merge up */ ret = bgp_attr_munge_as4path(peer, attr, as4_path); /* AS_PATH and AS4_PATH info are now fully merged; hence we can free up temporary structures. */ aspath_unintern(as4_path); if (ret < 0) return ret; } return 0; } int bgp_attr_parse_aspath(struct bgp_peer *peer, u_int16_t len, struct bgp_attr *attr, char *ptr, u_int8_t flag) { u_int8_t cap_4as = peer->cap_4as ? 1 : 0; attr->aspath = aspath_parse(ptr, len, cap_4as); return 0; } int bgp_attr_parse_as4path(struct bgp_peer *peer, u_int16_t len, struct bgp_attr *attr, char *ptr, u_int8_t flag, struct aspath **aspath4) { *aspath4 = aspath_parse(ptr, len, 1); return 0; } int bgp_attr_parse_nexthop(struct bgp_peer *peer, u_int16_t len, struct bgp_attr *attr, char *ptr, u_char flag) { u_int32_t tmp; /* Length check. */ if (len != 4) return -1; memcpy(&tmp, ptr, 4); attr->nexthop.s_addr = tmp; ptr += 4; return 0; } int bgp_attr_parse_community(struct bgp_peer *peer, u_int16_t len, struct bgp_attr *attr, char *ptr, u_int8_t flag) { if (len == 0) { attr->community = NULL; return 0; } else attr->community = (struct community *) community_parse((u_int32_t *)ptr, len); return 0; } int bgp_attr_parse_ecommunity(struct bgp_peer *peer, u_int16_t len, struct bgp_attr *attr, char *ptr, u_int8_t flag) { if (len == 0) attr->ecommunity = NULL; else attr->ecommunity = (struct ecommunity *) ecommunity_parse(ptr, len); return 0; } /* MED atrribute. */ int bgp_attr_parse_med(struct bgp_peer *peer, u_int16_t len, struct bgp_attr *attr, char *ptr, u_char flag) { u_int32_t tmp; /* Length check. */ if (len != 4) return -1; memcpy(&tmp, ptr, 4); attr->med = ntohl(tmp); ptr += 4; return 0; } /* Local preference attribute. */ int bgp_attr_parse_local_pref(struct bgp_peer *peer, u_int16_t len, struct bgp_attr *attr, char *ptr, u_char flag) { u_int32_t tmp; if (len != 4) return -1; memcpy(&tmp, ptr, 4); attr->local_pref = ntohl(tmp); ptr += 4; return 0; } int bgp_attr_parse_mp_reach(struct bgp_peer *peer, u_int16_t len, struct bgp_attr *attr, char *ptr, struct bgp_nlri *mp_update) { u_int16_t afi, tmp16, mpreachlen, mpnhoplen; u_int16_t nlri_len; u_char safi; /* length check */ #define BGP_MP_REACH_MIN_SIZE 5 if (len < BGP_MP_REACH_MIN_SIZE) return -1; mpreachlen = len; memcpy(&tmp16, ptr, 2); afi = ntohs(tmp16); ptr += 2; safi = *ptr; ptr++; mpnhoplen = *ptr; ptr++; mpreachlen -= 4; /* 2+1+1 above */ /* IPv4, RD+IPv4, IPv6, IPv6 link-local+IPv6 global */ if (mpnhoplen == 4 || mpnhoplen == 12 || mpnhoplen == 16 || mpnhoplen == 32) { if (mpreachlen > mpnhoplen) { switch (mpnhoplen) { case 4: attr->mp_nexthop.family = AF_INET; memcpy(&attr->mp_nexthop.address.ipv4, ptr, 4); break; case 12: // XXX: make any use of RD ? attr->mp_nexthop.family = AF_INET; memcpy(&attr->mp_nexthop.address.ipv4, ptr+8, 4); break; #if defined ENABLE_IPV6 case 16: case 32: attr->mp_nexthop.family = AF_INET6; memcpy(&attr->mp_nexthop.address.ipv6, ptr, 16); break; #endif default: memset(&attr->mp_nexthop, 0, sizeof(struct host_addr)); break; } mpreachlen -= mpnhoplen; ptr += mpnhoplen; /* Skipping SNPA info */ mpreachlen--; ptr++; } else return -1; } else return -1; nlri_len = mpreachlen; /* length check once again */ if (!nlri_len || nlri_len > len) return -1; /* XXX: perhaps sanity check (applies to: mp_reach, mp_unreach, update, withdraw) */ mp_update->afi = afi; mp_update->safi = safi; mp_update->nlri = ptr; mp_update->length = nlri_len; return 0; } int bgp_attr_parse_mp_unreach(struct bgp_peer *peer, u_int16_t len, struct bgp_attr *attr, char *ptr, struct bgp_nlri *mp_withdraw) { u_int16_t afi, mpunreachlen, tmp16; u_int16_t withdraw_len; u_char safi; /* length check */ #define BGP_MP_UNREACH_MIN_SIZE 3 if (len < BGP_MP_UNREACH_MIN_SIZE) return -1; mpunreachlen = len; memcpy(&tmp16, ptr, 2); afi = ntohs(tmp16); ptr += 2; safi = *ptr; ptr++; mpunreachlen -= 3; /* 2+1 above */ withdraw_len = mpunreachlen; mp_withdraw->afi = afi; mp_withdraw->safi = safi; mp_withdraw->nlri = ptr; mp_withdraw->length = withdraw_len; return 0; } /* BGP UPDATE NLRI parsing */ int bgp_nlri_parse(struct bgp_peer *peer, void *attr, struct bgp_nlri *info) { u_char *pnt; u_char *lim; u_char safi, label[3]; struct prefix p; int psize, end; int ret; u_int32_t tmp32; u_int16_t tmp16; struct rd_ip *rdi; struct rd_as *rda; struct rd_as4 *rda4; rd_t rd; memset (&p, 0, sizeof(struct prefix)); memset (&rd, 0, sizeof(rd_t)); pnt = info->nlri; lim = pnt + info->length; end = info->length; safi = info->safi; for (; pnt < lim; pnt += psize) { memset(&p, 0, sizeof(struct prefix)); /* Fetch prefix length and cross-check */ p.prefixlen = *pnt++; end--; p.family = bgp_afi2family (info->afi); if (info->safi == SAFI_UNICAST) { if ((info->afi == AFI_IP && p.prefixlen > 32) || (info->afi == AFI_IP6 && p.prefixlen > 128)) return -1; psize = ((p.prefixlen+7)/8); if (psize > end) return -1; /* Fetch prefix from NLRI packet. */ memcpy(&p.u.prefix, pnt, psize); // XXX: check address correctnesss now that we have it? } else if (info->safi == SAFI_MPLS_LABEL) { /* rfc3107 labeled unicast */ if ((info->afi == AFI_IP && p.prefixlen > 56) || (info->afi == AFI_IP6 && p.prefixlen > 152)) return -1; psize = ((p.prefixlen+7)/8); if (psize > end) return -1; /* Fetch prefix from NLRI packet, drop the 3 bytes label. */ memcpy(&p.u.prefix, pnt+3, (psize-3)); p.prefixlen -= 24; /* As we trash the label anyway, let's rewrite the SAFI as plain unicast */ safi = SAFI_UNICAST; } else if (info->safi == SAFI_MPLS_VPN) { /* rfc4364 BGP/MPLS IP Virtual Private Networks */ if (info->afi == AFI_IP && p.prefixlen > 120 || info->afi != AFI_IP /* XXX: IPv6? */) return -1; psize = ((p.prefixlen+7)/8); if (psize > end) return -1; /* Fetch label (3), RD (8) and prefix (4) from NLRI packet */ memcpy(label, pnt, 3); memcpy(&rd.type, pnt+3, 2); rd.type = ntohs(rd.type); switch(rd.type) { case RD_TYPE_AS: rda = (struct rd_as *) &rd; memcpy(&tmp16, pnt+5, 2); memcpy(&tmp32, pnt+7, 4); rda->as = ntohs(tmp16); rda->val = ntohl(tmp32); break; case RD_TYPE_IP: rdi = (struct rd_ip *) &rd; memcpy(&tmp32, pnt+5, 4); memcpy(&tmp16, pnt+9, 2); rdi->ip.s_addr = ntohl(tmp32); rdi->val = ntohs(tmp16); break; case RD_TYPE_AS4: rda4 = (struct rd_as4 *) &rd; memcpy(&tmp32, pnt+5, 4); memcpy(&tmp16, pnt+9, 2); rda4->as = ntohl(tmp32); rda4->val = ntohs(tmp16); break; default: return -1; break; } memcpy(&p.u.prefix, pnt+11, (psize-11)); p.prefixlen -= 88; } /* Let's do our job now! */ if (attr) ret = bgp_process_update(peer, &p, attr, info->afi, safi, &rd, label); else ret = bgp_process_withdraw(peer, &p, attr, info->afi, safi, &rd, label); } return 0; } int bgp_process_update(struct bgp_peer *peer, struct prefix *p, void *attr, afi_t afi, safi_t safi, rd_t *rd, char *label) { struct bgp_node *route = NULL; struct bgp_info *ri = NULL, *new = NULL; struct bgp_attr *attr_new = NULL; u_int32_t modulo = peer->fd % config.bgp_table_peer_buckets; route = bgp_node_get(rib[afi][safi], p); /* Check previously received route. */ for (ri = route->info[modulo]; ri; ri = ri->next) { if (safi != SAFI_MPLS_VPN) { if (ri->peer == peer) break; } else { if (ri->peer == peer && ri->extra && !memcmp(&ri->extra->rd, &rd, sizeof(rd_t))) break; } } attr_new = bgp_attr_intern(attr); if (ri) { /* Received same information */ if (attrhash_cmp(ri->attr, attr_new)) { bgp_unlock_node (route); bgp_attr_unintern(attr_new); return 0; } else { /* Update to new attribute. */ bgp_attr_unintern(ri->attr); ri->attr = attr_new; /* Install/update MPLS stuff if required */ if (safi == SAFI_MPLS_VPN) { struct bgp_info_extra *rie; rie = bgp_info_extra_get(ri); memcpy(&rie->rd, rd, sizeof(rd_t)); memcpy(&rie->label, label, 3); } bgp_unlock_node (route); if (config.nfacctd_bgp_msglog) goto log_update; return 0; } } /* Make new BGP info. */ new = bgp_info_new(); new->peer = peer; new->attr = attr_new; if (safi == SAFI_MPLS_VPN) { struct bgp_info_extra *rie; rie = bgp_info_extra_get(new); memcpy(&rie->rd, rd, sizeof(rd_t)); memcpy(&rie->label, label, 3); } /* Register new BGP information. */ bgp_info_add(route, new, modulo); /* route_node_get lock */ bgp_unlock_node(route); if (config.nfacctd_bgp_msglog) { ri = new; goto log_update; } /* XXX: Impose a maximum number of prefixes allowed */ // if (bgp_maximum_prefix_overflow(peer, afi, safi, 0)) // return -1; return 0; log_update: { char empty[] = ""; char prefix_str[INET6_ADDRSTRLEN], nexthop_str[INET6_ADDRSTRLEN]; char *aspath, *comm, *ecomm; u_int32_t lp, med; struct rd_ip *rdi; struct rd_as *rda; struct rd_as4 *rda4; memset(prefix_str, 0, INET6_ADDRSTRLEN); memset(nexthop_str, 0, INET6_ADDRSTRLEN); prefix2str(&route->p, prefix_str, INET6_ADDRSTRLEN); aspath = attr_new->aspath ? attr_new->aspath->str : empty; comm = attr_new->community ? attr_new->community->str : empty; ecomm = attr_new->ecommunity ? attr_new->ecommunity->str : empty; lp = attr_new->local_pref; med = attr_new->med; addr_to_str(nexthop_str, &attr_new->mp_nexthop); if (safi != SAFI_MPLS_VPN) Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] u Prefix: '%s' Path: '%s' Comms: '%s' EComms: '%s' LP: '%u' MED: '%u' Nexthop: '%s'\n", inet_ntoa(peer->id.address.ipv4), prefix_str, aspath, comm, ecomm, lp, med, nexthop_str); else { if (ri && ri->extra) { u_char rd_str[SRVBUFLEN]; bgp_rd2str(rd_str, &ri->extra->rd); Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] u RD: '%s' Prefix: '%s' Path: '%s' Comms: '%s' EComms: '%s' LP: '%u' MED: '%u' Nexthop: '%s'\n", inet_ntoa(peer->id.address.ipv4), rd_str, prefix_str, aspath, comm, ecomm, lp, med, nexthop_str); } } } return 0; } int bgp_process_withdraw(struct bgp_peer *peer, struct prefix *p, void *attr, afi_t afi, safi_t safi, rd_t *rd, char *label) { struct bgp_node *route = NULL; struct bgp_info *ri = NULL; u_int32_t modulo = peer->fd % config.bgp_table_peer_buckets; /* Lookup node. */ route = bgp_node_get(rib[afi][safi], p); for (ri = route->info[modulo]; ri; ri = ri->next) { if (safi != SAFI_MPLS_VPN) { if (ri->peer == peer) break; } else { if (ri->peer == peer && ri->extra && !memcmp(&ri->extra->rd, &rd, sizeof(rd_t))) break; } } if (ri && config.nfacctd_bgp_msglog) { char empty[] = ""; char prefix_str[INET6_ADDRSTRLEN]; char *aspath, *comm, *ecomm; memset(prefix_str, 0, INET6_ADDRSTRLEN); prefix2str(&route->p, prefix_str, INET6_ADDRSTRLEN); aspath = ri->attr->aspath ? ri->attr->aspath->str : empty; comm = ri->attr->community ? ri->attr->community->str : empty; ecomm = ri->attr->ecommunity ? ri->attr->ecommunity->str : empty; if (safi != SAFI_MPLS_VPN) Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] w Prefix: '%s' Path: '%s' Comms: '%s' EComms: '%s'\n", inet_ntoa(peer->id.address.ipv4), prefix_str, aspath, comm, ecomm); else { if (ri && ri->extra) { u_char rd_str[SRVBUFLEN]; bgp_rd2str(rd_str, &ri->extra->rd); Log(LOG_INFO, "INFO ( default/core/BGP ): [Id: %s] w RD: '%s' Prefix: '%s' Path: '%s' Comms: '%s' EComms: '%s'\n", inet_ntoa(peer->id.address.ipv4), rd_str, prefix_str, aspath, comm, ecomm); } } } /* Withdraw specified route from routing table. */ if (ri) bgp_info_delete(route, ri, modulo); /* Unlock bgp_node_get() lock. */ bgp_unlock_node(route); return 0; } /* BGP Address Famiy Identifier to UNIX Address Family converter. */ int bgp_afi2family (int afi) { if (afi == AFI_IP) return AF_INET; #ifdef ENABLE_IPV6 else if (afi == AFI_IP6) return AF_INET6; #endif return 0; } int bgp_rd2str(char *str, rd_t *rd) { struct rd_ip *rdi; struct rd_as *rda; struct rd_as4 *rda4; struct host_addr a; u_char ip_address[INET6_ADDRSTRLEN]; switch (rd->type) { case RD_TYPE_AS: rda = (struct rd_as *) rd; sprintf(str, "%u:%u:%u", rda->type, rda->as, rda->val); break; case RD_TYPE_IP: rdi = (struct rd_ip *) rd; a.family = AF_INET; a.address.ipv4.s_addr = rdi->ip.s_addr; addr_to_str(ip_address, &a); sprintf(str, "%u:%s:%u", rdi->type, ip_address, rdi->val); break; case RD_TYPE_AS4: rda4 = (struct rd_as4 *) rd; sprintf(str, "%u:%u:%u", rda4->type, rda4->as, rda4->val); break; default: sprintf(str, "unknown"); break; } } int bgp_str2rd(rd_t *output, char *value) { struct host_addr a; char *endptr, *token; u_int32_t tmp32; u_int16_t tmp16; struct rd_ip *rdi; struct rd_as *rda; struct rd_as4 *rda4; int idx = 0; rd_t rd; memset(&a, 0, sizeof(a)); memset(&rd, 0, sizeof(rd)); /* type:RD_subfield1:RD_subfield2 */ while ( (token = extract_token(&value, ':')) && idx < 3) { if (idx == 0) { tmp32 = strtoul(token, &endptr, 10); rd.type = tmp32; switch (rd.type) { case RD_TYPE_AS: rda = (struct rd_as *) &rd; break; case RD_TYPE_IP: rdi = (struct rd_ip *) &rd; break; case RD_TYPE_AS4: rda4 = (struct rd_as4 *) &rd; break; default: printf("ERROR: Invalid RD type specified\n"); return FALSE; } } if (idx == 1) { switch (rd.type) { case RD_TYPE_AS: tmp32 = strtoul(token, &endptr, 10); rda->as = tmp32; break; case RD_TYPE_IP: memset(&a, 0, sizeof(a)); str_to_addr(token, &a); if (a.family == AF_INET) rdi->ip.s_addr = a.address.ipv4.s_addr; break; case RD_TYPE_AS4: tmp32 = strtoul(token, &endptr, 10); rda4->as = tmp32; break; } } if (idx == 2) { switch (rd.type) { case RD_TYPE_AS: tmp32 = strtoul(token, &endptr, 10); rda->val = tmp32; break; case RD_TYPE_IP: tmp32 = strtoul(token, &endptr, 10); rdi->val = tmp32; break; case RD_TYPE_AS4: tmp32 = strtoul(token, &endptr, 10); rda4->val = tmp32; break; } } idx++; } memcpy(output, &rd, sizeof(rd)); return TRUE; } /* Allocate bgp_info_extra */ struct bgp_info_extra *bgp_info_extra_new(void) { struct bgp_info_extra *new; new = malloc(sizeof(struct bgp_info_extra)); return new; } void bgp_info_extra_free(struct bgp_info_extra **extra) { if (extra && *extra) { free(*extra); *extra = NULL; } } /* Get bgp_info extra information for the given bgp_info */ struct bgp_info_extra *bgp_info_extra_get(struct bgp_info *ri) { if (!ri->extra) ri->extra = bgp_info_extra_new(); return ri->extra; } /* Allocate new bgp info structure. */ struct bgp_info *bgp_info_new() { struct bgp_info *new; new = malloc(sizeof(struct bgp_info)); memset(new, 0, sizeof (struct bgp_info)); return new; } void bgp_info_add(struct bgp_node *rn, struct bgp_info *ri, u_int32_t modulo) { struct bgp_info *top; top = rn->info[modulo]; ri->next = rn->info[modulo]; ri->prev = NULL; if (top) top->prev = ri; rn->info[modulo] = ri; // ri->lock++; bgp_lock_node(rn); ri->peer->lock++; } void bgp_info_delete(struct bgp_node *rn, struct bgp_info *ri, u_int32_t modulo) { if (ri->next) ri->next->prev = ri->prev; if (ri->prev) ri->prev->next = ri->next; else rn->info[modulo] = ri->next; bgp_info_free(ri); bgp_unlock_node(rn); } /* Free bgp route information. */ void bgp_info_free(struct bgp_info *ri) { if (ri->attr) bgp_attr_unintern(ri->attr); bgp_info_extra_free(&ri->extra); ri->peer->lock--; free(ri); } /* Initialization of attributes */ /* void bgp_attr_init(struct bgp_peer *peer) { aspath_init(peer); attrhash_init(peer); community_init(peer); ecommunity_init(peer); } */ void bgp_attr_init() { aspath_init(); attrhash_init(); community_init(); ecommunity_init(); } unsigned int attrhash_key_make(void *p) { struct bgp_attr *attr = (struct bgp_attr *) p; unsigned int key = 0; key += attr->origin; key += attr->nexthop.s_addr; key += attr->med; key += attr->local_pref; if (attr->pathlimit.as) { key += attr->pathlimit.ttl; key += attr->pathlimit.as; } if (attr->aspath) key += aspath_key_make(attr->aspath); if (attr->community) key += community_hash_make(attr->community); if (attr->ecommunity) key += ecommunity_hash_make(attr->ecommunity); return key; } int attrhash_cmp(void *p1,void *p2) { struct bgp_attr *attr1 = p1; struct bgp_attr *attr2 = p2; if (attr1->flag == attr2->flag && attr1->origin == attr2->origin && attr1->nexthop.s_addr == attr2->nexthop.s_addr && attr1->aspath == attr2->aspath && attr1->community == attr2->community && attr1->ecommunity == attr2->ecommunity && attr1->med == attr2->med && attr1->local_pref == attr2->local_pref && attr1->pathlimit.ttl == attr2->pathlimit.ttl && attr1->pathlimit.as == attr2->pathlimit.as) { if (attr1->mp_nexthop.family == attr2->mp_nexthop.family) { if (attr1->mp_nexthop.family == AF_INET && attr1->mp_nexthop.address.ipv4.s_addr == attr2->mp_nexthop.address.ipv4.s_addr) return 1; #if defined ENABLE_IPV6 else if (attr1->mp_nexthop.family == AF_INET6 && !memcmp(&attr1->mp_nexthop.address.ipv6, &attr2->mp_nexthop.address.ipv6, 16)) return 1; #endif else return 1; } } return 0; } void attrhash_init() { attrhash = (struct hash *) hash_create(attrhash_key_make, attrhash_cmp); } /* Internet argument attribute. */ struct bgp_attr *bgp_attr_intern(struct bgp_attr *attr) { struct bgp_attr *find; /* Intern referenced strucutre. */ if (attr->aspath) { if (! attr->aspath->refcnt) attr->aspath = aspath_intern (attr->aspath); else attr->aspath->refcnt++; } if (attr->community) { if (! attr->community->refcnt) attr->community = community_intern (attr->community); else attr->community->refcnt++; } if (attr->ecommunity) { if (!attr->ecommunity->refcnt) attr->ecommunity = ecommunity_intern (attr->ecommunity); else attr->ecommunity->refcnt++; } find = (struct bgp_attr *) hash_get(attrhash, attr, bgp_attr_hash_alloc); find->refcnt++; return find; } /* Free bgp attribute and aspath. */ void bgp_attr_unintern(struct bgp_attr *attr) { struct bgp_attr *ret; struct aspath *aspath; struct community *community; struct ecommunity *ecommunity = NULL; /* Decrement attribute reference. */ attr->refcnt--; aspath = attr->aspath; community = attr->community; ecommunity = attr->ecommunity; /* If reference becomes zero then free attribute object. */ if (attr->refcnt == 0) { ret = (struct bgp_attr *) hash_release (attrhash, attr); // assert (ret != NULL); // if (ret) free(attr); if (!ret) Log(LOG_WARNING, "WARN ( default/core/BGP ): bgp_attr_unintern() hash lookup failed.\n"); free(attr); } /* aspath refcount shoud be decrement. */ if (aspath) aspath_unintern (aspath); if (community) community_unintern (community); if (ecommunity) ecommunity_unintern (ecommunity); } void *bgp_attr_hash_alloc (void *p) { struct bgp_attr *val = (struct bgp_attr *) p; struct bgp_attr *attr; attr = malloc(sizeof (struct bgp_attr)); memset(attr, 0, sizeof (struct bgp_attr)); *attr = *val; attr->refcnt = 0; return attr; } void bgp_peer_init(struct bgp_peer *peer) { afi_t afi; safi_t safi; memset(peer, 0, sizeof(struct bgp_peer)); peer->status = Idle; peer->buf.len = BGP_MAX_PACKET_SIZE; peer->buf.base = malloc(peer->buf.len); memset(peer->buf.base, 0, peer->buf.len); } void bgp_peer_close(struct bgp_peer *peer) { afi_t afi; safi_t safi; close(peer->fd); peer->fd = 0; memset(&peer->id, 0, sizeof(peer->id)); memset(&peer->addr, 0, sizeof(peer->addr)); free(peer->buf.base); if (config.nfacctd_bgp_neighbors_file) write_neighbors_file(config.nfacctd_bgp_neighbors_file); } int bgp_attr_munge_as4path(struct bgp_peer *peer, struct bgp_attr *attr, struct aspath *as4path) { struct aspath *newpath; /* If the BGP peer supports 32bit AS_PATH then we are done */ if (peer->cap_4as) return 0; /* pre-requisite for AS4_PATH is AS_PATH indeed */ // XXX if (as4path && !attr->aspath) return -1; newpath = aspath_reconcile_as4(attr->aspath, as4path); aspath_unintern(attr->aspath); attr->aspath = aspath_intern(newpath); return 0; } void load_comm_patterns(char **stdcomm, char **extcomm, char **stdcomm_to_asn) { int idx; char *token; memset(std_comm_patterns, 0, sizeof(std_comm_patterns)); memset(ext_comm_patterns, 0, sizeof(ext_comm_patterns)); memset(std_comm_patterns_to_asn, 0, sizeof(std_comm_patterns_to_asn)); if (*stdcomm) { idx = 0; while ( (token = extract_token(stdcomm, ',')) && idx < MAX_BGP_COMM_PATTERNS ) { std_comm_patterns[idx] = token; trim_spaces(std_comm_patterns[idx]); idx++; } } if (*extcomm) { idx = 0; while ( (token = extract_token(extcomm, ',')) && idx < MAX_BGP_COMM_PATTERNS ) { ext_comm_patterns[idx] = token; trim_spaces(ext_comm_patterns[idx]); idx++; } } if (*stdcomm_to_asn) { idx = 0; while ( (token = extract_token(stdcomm_to_asn, ',')) && idx < MAX_BGP_COMM_PATTERNS ) { std_comm_patterns_to_asn[idx] = token; trim_spaces(std_comm_patterns_to_asn[idx]); idx++; } } } void evaluate_comm_patterns(char *dst, char *src, char **patterns, int dstlen) { char *ptr, *haystack, *delim_src, *delim_ptn; char local_ptr[MAX_BGP_STD_COMMS], *auxptr; int idx, i, j, srclen; srclen = strlen(src); for (idx = 0, j = 0; patterns[idx]; idx++) { haystack = src; find_again: delim_ptn = strchr(patterns[idx], '.'); if (delim_ptn) *delim_ptn = '\0'; ptr = strstr(haystack, patterns[idx]); if (ptr && delim_ptn) { delim_src = strchr(ptr, ' '); if (delim_src) { memcpy(local_ptr, ptr, delim_src-ptr); local_ptr[delim_src-ptr] = '\0'; } else memcpy(local_ptr, ptr, strlen(ptr)+1); *delim_ptn = '.'; if (strlen(local_ptr) != strlen(patterns[idx])) ptr = NULL; else { for (auxptr = strchr(patterns[idx], '.'); auxptr; auxptr = strchr(auxptr, '.')) { local_ptr[auxptr-patterns[idx]] = '.'; auxptr++; } if (strncmp(patterns[idx], local_ptr, strlen(patterns[idx]))) ptr = NULL; } } else if (delim_ptn) *delim_ptn = '.'; if (ptr) { /* If we have already something on the stack, let's insert a space */ if (j && j < dstlen) { dst[j] = ' '; j++; } /* We should be able to trust this string */ for (i = 0; ptr[i] != ' ' && ptr[i] != '\0'; i++, j++) { if (j < dstlen) dst[j] = ptr[i]; else break; } haystack = &ptr[i]; } /* If we don't have space anymore, let's finish it here */ if (j >= dstlen) { dst[dstlen-1] = '+'; break; } /* Trick to find multiple occurrences */ if (ptr) goto find_again; } } as_t evaluate_last_asn(struct aspath *as) { if (!as) return 0; return as->last_as; } as_t evaluate_first_asn(char *src) { int idx, is_space = FALSE, len = strlen(src), start, sub_as, iteration; char *endptr, *ptr, saved; as_t asn, real_first_asn; start = 0; iteration = 0; real_first_asn = 0; start_again: asn = 0; sub_as = FALSE; for (idx = start; idx < len && (src[idx] != ' ' && src[idx] != ')'); idx++); /* Mangling the AS_PATH string */ if (src[idx] == ' ' || src[idx] == ')') { is_space = TRUE; saved = src[idx]; src[idx] = '\0'; } if (src[start] == '(') { ptr = &src[start+1]; sub_as = TRUE; } else ptr = &src[start]; asn = strtoul(ptr, &endptr, 10); /* Restoring mangled AS_PATH */ if (is_space) { src[idx] = saved; saved = '\0'; is_space = FALSE; } if (config.nfacctd_bgp_peer_as_skip_subas && sub_as) { while (idx < len && (src[idx] == ' ' || src[idx] == ')')) idx++; if (idx != len-1) { start = idx; if (iteration == 0) real_first_asn = asn; iteration++; goto start_again; } } /* skip sub-as kicks-in only when traffic is delivered to a different ASN */ if (real_first_asn && (!asn || sub_as)) asn = real_first_asn; return asn; } void bgp_srcdst_lookup(struct packet_ptrs *pptrs) { struct sockaddr *sa = (struct sockaddr *) pptrs->f_agent, sa_local; struct xflow_status_entry *xs_entry = (struct xflow_status_entry *) pptrs->f_status; struct bgp_peer *peer; struct bgp_node *default_node, *result; struct bgp_info *info; struct prefix default_prefix; int peers_idx; int follow_default = config.nfacctd_bgp_follow_default; struct in_addr pref4; #if defined ENABLE_IPV6 struct in6_addr pref6; #endif u_int32_t modulo; safi_t safi; rd_t rd; pptrs->bgp_src = NULL; pptrs->bgp_dst = NULL; pptrs->bgp_src_info = NULL; pptrs->bgp_dst_info = NULL; pptrs->bgp_peer = NULL; pptrs->bgp_nexthop_info = NULL; safi = SAFI_UNICAST; if (pptrs->bta) { sa = &sa_local; sa->sa_family = AF_INET; ((struct sockaddr_in *)sa)->sin_addr.s_addr = pptrs->bta; } start_again: if (xs_entry && xs_entry->peer_idx) { if (!sa_addr_cmp(sa, &peers[xs_entry->peer_idx].addr) || !sa_addr_cmp(sa, &peers[xs_entry->peer_idx].id)) { peer = &peers[xs_entry->peer_idx]; pptrs->bgp_peer = (char *) &peers[xs_entry->peer_idx]; } /* If no match then let's invalidate the entry */ else { xs_entry->peer_idx = 0; peer = NULL; } } else { for (peer = NULL, peers_idx = 0; peers_idx < config.nfacctd_bgp_max_peers; peers_idx++) { if (!sa_addr_cmp(sa, &peers[peers_idx].addr) || !sa_addr_cmp(sa, &peers[peers_idx].id)) { peer = &peers[peers_idx]; pptrs->bgp_peer = (char *) &peers[peers_idx]; if (xs_entry) xs_entry->peer_idx = peers_idx; break; } } } if (peer) { modulo = peer->fd % config.bgp_table_peer_buckets; if (pptrs->bitr) { safi = SAFI_MPLS_VPN; memcpy(&rd, &pptrs->bitr, sizeof(rd)); } if (pptrs->l3_proto == ETHERTYPE_IP) { if (!pptrs->bgp_src) { memcpy(&pref4, &((struct my_iphdr *)pptrs->iph_ptr)->ip_src, sizeof(struct in_addr)); pptrs->bgp_src = (char *) bgp_node_match_ipv4(rib[AFI_IP][safi], &pref4, (struct bgp_peer *) pptrs->bgp_peer); } if (!pptrs->bgp_src_info && pptrs->bgp_src) { result = (struct bgp_node *) pptrs->bgp_src; if (result->p.prefixlen >= pptrs->lm_mask_src) { pptrs->lm_mask_src = result->p.prefixlen; pptrs->lm_method_src = NF_NET_BGP; } for (info = result->info[modulo]; info; info = info->next) { if (safi != SAFI_MPLS_VPN) { if (info->peer == peer) { pptrs->bgp_src_info = (char *) info; break; } } else { if (info->peer == peer && info->extra && !memcmp(&info->extra->rd, &rd, sizeof(rd_t))) { pptrs->bgp_src_info = (char *) info; break; } } } } if (!pptrs->bgp_dst) { memcpy(&pref4, &((struct my_iphdr *)pptrs->iph_ptr)->ip_dst, sizeof(struct in_addr)); pptrs->bgp_dst = (char *) bgp_node_match_ipv4(rib[AFI_IP][safi], &pref4, (struct bgp_peer *) pptrs->bgp_peer); } if (!pptrs->bgp_dst_info && pptrs->bgp_dst) { result = (struct bgp_node *) pptrs->bgp_dst; if (result->p.prefixlen >= pptrs->lm_mask_dst) { pptrs->lm_mask_dst = result->p.prefixlen; pptrs->lm_method_dst = NF_NET_BGP; } for (info = result->info[modulo]; info; info = info->next) { if (safi != SAFI_MPLS_VPN) { if (info->peer == peer) { pptrs->bgp_dst_info = (char *) info; break; } } else { if (info->peer == peer && info->extra && !memcmp(&info->extra->rd, &rd, sizeof(rd_t))) { pptrs->bgp_dst_info = (char *) info; break; } } } } } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { if (!pptrs->bgp_src) { memcpy(&pref6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_src, sizeof(struct in6_addr)); pptrs->bgp_src = (char *) bgp_node_match_ipv6(rib[AFI_IP6][safi], &pref6, (struct bgp_peer *) pptrs->bgp_peer); } if (!pptrs->bgp_src_info && pptrs->bgp_src) { result = (struct bgp_node *) pptrs->bgp_src; if (result->p.prefixlen >= pptrs->lm_mask_src) { pptrs->lm_mask_src = result->p.prefixlen; pptrs->lm_method_src = NF_NET_BGP; } for (info = result->info[modulo]; info; info = info->next) { if (safi != SAFI_MPLS_VPN) { if (info->peer == peer) { pptrs->bgp_src_info = (char *) info; break; } } else { if (info->peer == peer && info->extra && !memcmp(&info->extra->rd, &rd, sizeof(rd_t))) { pptrs->bgp_src_info = (char *) info; break; } } } } if (!pptrs->bgp_dst) { memcpy(&pref6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_dst, sizeof(struct in6_addr)); pptrs->bgp_dst = (char *) bgp_node_match_ipv6(rib[AFI_IP6][safi], &pref6, (struct bgp_peer *) pptrs->bgp_peer); } if (!pptrs->bgp_dst_info && pptrs->bgp_dst) { result = (struct bgp_node *) pptrs->bgp_dst; if (result->p.prefixlen >= pptrs->lm_mask_dst) { pptrs->lm_mask_dst = result->p.prefixlen; pptrs->lm_method_dst = NF_NET_BGP; } for (info = result->info[modulo]; info; info = info->next) { if (safi != SAFI_MPLS_VPN) { if (info->peer == peer) { pptrs->bgp_dst_info = (char *) info; break; } } else { if (info->peer == peer && info->extra && !memcmp(&info->extra->rd, &rd, sizeof(rd_t))) { pptrs->bgp_dst_info = (char *) info; break; } } } } } #endif if (follow_default && safi != SAFI_MPLS_VPN) { default_node = NULL; if (pptrs->l3_proto == ETHERTYPE_IP) { memset(&default_prefix, 0, sizeof(default_prefix)); default_prefix.family = AF_INET; result = (struct bgp_node *) pptrs->bgp_src; if (result && prefix_match(&result->p, &default_prefix)) { default_node = result; pptrs->bgp_src = NULL; pptrs->bgp_src_info = NULL; } result = (struct bgp_node *) pptrs->bgp_dst; if (result && prefix_match(&result->p, &default_prefix)) { default_node = result; pptrs->bgp_dst = NULL; pptrs->bgp_dst_info = NULL; } } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { memset(&default_prefix, 0, sizeof(default_prefix)); default_prefix.family = AF_INET6; result = (struct bgp_node *) pptrs->bgp_src; if (result && prefix_match(&result->p, &default_prefix)) { default_node = result; info = result->info[modulo]; pptrs->bgp_src = NULL; pptrs->bgp_src_info = NULL; } result = (struct bgp_node *) pptrs->bgp_dst; if (result && prefix_match(&result->p, &default_prefix)) { default_node = result; info = result->info[modulo]; pptrs->bgp_dst = NULL; pptrs->bgp_dst_info = NULL; } } #endif if (!pptrs->bgp_src || !pptrs->bgp_dst) { follow_default--; if (default_node) { if (info && info->attr) { if (info->attr->mp_nexthop.family == AF_INET) { sa = &sa_local; memset(sa, 0, sizeof(struct sockaddr)); sa->sa_family = AF_INET; memcpy(&((struct sockaddr_in *)sa)->sin_addr, &info->attr->mp_nexthop.address.ipv4, 4); goto start_again; } #if defined ENABLE_IPV6 else if (info->attr->mp_nexthop.family == AF_INET6) { sa = &sa_local; memset(sa, 0, sizeof(struct sockaddr)); sa->sa_family = AF_INET6; memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr, &info->attr->mp_nexthop.address.ipv6, 16); goto start_again; } #endif else { sa = &sa_local; memset(sa, 0, sizeof(struct sockaddr)); sa->sa_family = AF_INET; memcpy(&((struct sockaddr_in *)sa)->sin_addr, &info->attr->nexthop, 4); goto start_again; } } } } } if (config.nfacctd_bgp_follow_nexthop[0].family && pptrs->bgp_dst && safi != SAFI_MPLS_VPN) bgp_follow_nexthop_lookup(pptrs); } } void bgp_follow_nexthop_lookup(struct packet_ptrs *pptrs) { struct sockaddr *sa = (struct sockaddr *) pptrs->f_agent, sa_local; struct bgp_peer *nh_peer; struct bgp_node *result_node = NULL; struct bgp_info *info; char *result = NULL, *saved_info = NULL; int peers_idx, ttl = MAX_HOPS_FOLLOW_NH, self = MAX_NH_SELF_REFERENCES; int nh_idx, matched = 0; struct prefix nh, ch; struct in_addr pref4; #if defined ENABLE_IPV6 struct in6_addr pref6; #endif char *saved_agent = pptrs->f_agent; pm_id_t bta; u_int32_t modulo; start_again: if (config.nfacctd_bgp_to_agent_map && (*find_id_func)) { bta = 0; (*find_id_func)((struct id_table *)pptrs->bta_table, pptrs, &bta, NULL); if (bta) { sa = &sa_local; sa->sa_family = AF_INET; ((struct sockaddr_in *)sa)->sin_addr.s_addr = bta; } } for (nh_peer = NULL, peers_idx = 0; peers_idx < config.nfacctd_bgp_max_peers; peers_idx++) { if (!sa_addr_cmp(sa, &peers[peers_idx].addr) || !sa_addr_cmp(sa, &peers[peers_idx].id)) { nh_peer = &peers[peers_idx]; break; } } if (nh_peer) { modulo = nh_peer->fd % config.bgp_table_peer_buckets; memset(&ch, 0, sizeof(ch)); ch.family = AF_INET; ch.prefixlen = 32; memcpy(&ch.u.prefix4, &nh_peer->addr.address.ipv4, 4); if (!result) { if (pptrs->l3_proto == ETHERTYPE_IP) { memcpy(&pref4, &((struct my_iphdr *)pptrs->iph_ptr)->ip_dst, sizeof(struct in_addr)); result = (char *) bgp_node_match_ipv4(rib[AFI_IP][SAFI_UNICAST], &pref4, nh_peer); } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { memcpy(&pref6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_dst, sizeof(struct in6_addr)); result = (char *) bgp_node_match_ipv6(rib[AFI_IP6][SAFI_UNICAST], &pref6, nh_peer); } #endif } memset(&nh, 0, sizeof(nh)); result_node = (struct bgp_node *) result; if (result_node) { for (info = result_node->info[modulo]; info; info = info->next) { if (info->peer == nh_peer) break; } } else info = NULL; if (info && info->attr) { if (info->attr->mp_nexthop.family == AF_INET) { nh.family = AF_INET; nh.prefixlen = 32; memcpy(&nh.u.prefix4, &info->attr->mp_nexthop.address.ipv4, 4); for (nh_idx = 0; config.nfacctd_bgp_follow_nexthop[nh_idx].family && nh_idx < FOLLOW_BGP_NH_ENTRIES; nh_idx++) { matched = prefix_match(&config.nfacctd_bgp_follow_nexthop[nh_idx], &nh); if (matched) break; } if (matched && self > 0 && ttl > 0) { if (prefix_match(&ch, &nh)) self--; sa = &sa_local; pptrs->f_agent = (char *) &sa_local; memset(sa, 0, sizeof(struct sockaddr)); sa->sa_family = AF_INET; memcpy(&((struct sockaddr_in *)sa)->sin_addr, &info->attr->mp_nexthop.address.ipv4, 4); saved_info = (char *) info; ttl--; goto start_again; } else goto end; } #if defined ENABLE_IPV6 else if (info->attr->mp_nexthop.family == AF_INET6) { nh.family = AF_INET6; nh.prefixlen = 128; memcpy(&nh.u.prefix6, &info->attr->mp_nexthop.address.ipv6, 16); for (nh_idx = 0; config.nfacctd_bgp_follow_nexthop[nh_idx].family && nh_idx < FOLLOW_BGP_NH_ENTRIES; nh_idx++) { matched = prefix_match(&config.nfacctd_bgp_follow_nexthop[nh_idx], &nh); if (matched) break; } if (matched && self > 0 && ttl > 0) { if (prefix_match(&ch, &nh)) self--; sa = &sa_local; pptrs->f_agent = (char *) &sa_local; memset(sa, 0, sizeof(struct sockaddr)); sa->sa_family = AF_INET6; memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr, &info->attr->mp_nexthop.address.ipv6, 16); saved_info = (char *) info; ttl--; goto start_again; } else goto end; } #endif else { nh.family = AF_INET; nh.prefixlen = 32; memcpy(&nh.u.prefix4, &info->attr->nexthop, 4); for (nh_idx = 0; config.nfacctd_bgp_follow_nexthop[nh_idx].family && nh_idx < FOLLOW_BGP_NH_ENTRIES; nh_idx++) { matched = prefix_match(&config.nfacctd_bgp_follow_nexthop[nh_idx], &nh); if (matched) break; } if (matched && self > 0 && ttl > 0) { if (prefix_match(&ch, &nh)) self--; sa = &sa_local; pptrs->f_agent = (char *) &sa_local; memset(sa, 0, sizeof(struct sockaddr)); sa->sa_family = AF_INET; memcpy(&((struct sockaddr_in *)sa)->sin_addr, &info->attr->nexthop, 4); saved_info = (char *) info; ttl--; goto start_again; } else goto end; } } } end: if (saved_info) pptrs->bgp_nexthop_info = saved_info; pptrs->f_agent = saved_agent; } void write_neighbors_file(char *filename) { FILE *file; char neighbor[INET6_ADDRSTRLEN+1]; int idx, len; uid_t owner = -1; gid_t group = -1; unlink(filename); if (config.files_uid) owner = config.files_uid; if (config.files_gid) group = config.files_gid; file = fopen(filename,"w"); if (file) { if (chown(filename, owner, group) == -1) Log(LOG_WARNING, "WARN: Unable to chown() '%s': %s\n", filename, strerror(errno)); if (file_lock(fileno(file))) { Log(LOG_ALERT, "ALERT: Unable to obtain lock for bgp_neighbors_file '%s'.\n", filename); return; } for (idx = 0; idx < config.nfacctd_bgp_max_peers; idx++) { if (peers[idx].fd) { if (peers[idx].addr.family == AF_INET) { inet_ntop(AF_INET, &peers[idx].addr.address.ipv4, neighbor, INET6_ADDRSTRLEN); len = strlen(neighbor); neighbor[len] = '\n'; len++; neighbor[len] = '\0'; fwrite(neighbor, len, 1, file); } /* we don't happen to support IPv6 neighbors just yet */ } } file_unlock(fileno(file)); fclose(file); } else { Log(LOG_ERR, "ERROR: Unable to open bgp_neighbors_file '%s'\n", filename); return; } } void pkt_to_cache_bgp_primitives(struct cache_bgp_primitives *c, struct pkt_bgp_primitives *p, u_int64_t what_to_count) { if (c) { c->peer_src_as = p->peer_src_as; c->peer_dst_as = p->peer_dst_as; memcpy(&c->peer_src_ip, &p->peer_src_ip, HostAddrSz); memcpy(&c->peer_dst_ip, &p->peer_dst_ip, HostAddrSz); if (what_to_count & COUNT_STD_COMM) { if (!c->std_comms) c->std_comms = malloc(MAX_BGP_STD_COMMS); memcpy(c->std_comms, p->std_comms, MAX_BGP_STD_COMMS); } else { if (c->std_comms) free(c->std_comms); } if (what_to_count & COUNT_EXT_COMM) { if (!c->ext_comms) c->ext_comms = malloc(MAX_BGP_EXT_COMMS); memcpy(c->ext_comms, p->ext_comms, MAX_BGP_EXT_COMMS); } else { if (c->ext_comms) free(c->ext_comms); } if (what_to_count & COUNT_AS_PATH) { if (!c->as_path) c->as_path = malloc(MAX_BGP_ASPATH); memcpy(c->as_path, p->as_path, MAX_BGP_ASPATH); } else { if (c->as_path) free(c->as_path); } c->local_pref = p->local_pref; c->med = p->med; if (what_to_count & COUNT_SRC_STD_COMM) { if (!c->src_std_comms) c->src_std_comms = malloc(MAX_BGP_STD_COMMS); memcpy(c->src_std_comms, p->src_std_comms, MAX_BGP_STD_COMMS); } else { if (c->src_std_comms) free(c->src_std_comms); } if (what_to_count & COUNT_SRC_EXT_COMM) { if (!c->src_ext_comms) c->src_ext_comms = malloc(MAX_BGP_EXT_COMMS); memcpy(c->src_ext_comms, p->src_ext_comms, MAX_BGP_EXT_COMMS); } else { if (c->src_ext_comms) free(c->src_ext_comms); } if (what_to_count & COUNT_SRC_AS_PATH) { if (!c->src_as_path) c->src_as_path = malloc(MAX_BGP_ASPATH); memcpy(c->src_as_path, p->src_as_path, MAX_BGP_ASPATH); } else { if (c->src_as_path) free(c->src_as_path); } c->src_local_pref = p->src_local_pref; c->src_med = p->src_med; memcpy(&c->mpls_vpn_rd, &p->mpls_vpn_rd, sizeof(rd_t)); } } void cache_to_pkt_bgp_primitives(struct pkt_bgp_primitives *p, struct cache_bgp_primitives *c) { memset(p, 0, PbgpSz); if (c) { p->peer_src_as = c->peer_src_as; p->peer_dst_as = c->peer_dst_as; memcpy(&p->peer_src_ip, &c->peer_src_ip, HostAddrSz); memcpy(&p->peer_dst_ip, &c->peer_dst_ip, HostAddrSz); if (c->std_comms) memcpy(p->std_comms, c->std_comms, MAX_BGP_STD_COMMS); if (c->ext_comms) memcpy(p->ext_comms, c->ext_comms, MAX_BGP_EXT_COMMS); if (c->as_path) memcpy(p->as_path, c->as_path, MAX_BGP_ASPATH); p->local_pref = c->local_pref; p->med = c->med; if (c->src_std_comms) memcpy(p->src_std_comms, c->src_std_comms, MAX_BGP_STD_COMMS); if (c->src_ext_comms) memcpy(p->src_ext_comms, c->src_ext_comms, MAX_BGP_EXT_COMMS); if (c->src_as_path) memcpy(p->src_as_path, c->src_as_path, MAX_BGP_ASPATH); p->src_local_pref = c->src_local_pref; p->src_med = c->src_med; memcpy(&p->mpls_vpn_rd, &c->mpls_vpn_rd, sizeof(rd_t)); } } void bgp_config_checks(struct configuration *c) { if (c->what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_PEER_DST_IP| COUNT_SRC_STD_COMM|COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED| COUNT_SRC_LOCAL_PREF|COUNT_MPLS_VPN_RD)) { /* Sanitizing the aggregation method */ if ( ((c->what_to_count & COUNT_STD_COMM) && (c->what_to_count & COUNT_EXT_COMM)) || ((c->what_to_count & COUNT_SRC_STD_COMM) && (c->what_to_count & COUNT_SRC_EXT_COMM)) ) { printf("ERROR: The use of STANDARD and EXTENDED BGP communitities is mutual exclusive.\n"); exit(1); } if ( (c->what_to_count & COUNT_SRC_STD_COMM && !c->nfacctd_bgp_src_std_comm_type) || (c->what_to_count & COUNT_SRC_EXT_COMM && !c->nfacctd_bgp_src_ext_comm_type) || (c->what_to_count & COUNT_SRC_AS_PATH && !c->nfacctd_bgp_src_as_path_type ) || (c->what_to_count & COUNT_SRC_LOCAL_PREF && !c->nfacctd_bgp_src_local_pref_type ) || (c->what_to_count & COUNT_SRC_MED && !c->nfacctd_bgp_src_med_type ) || (c->what_to_count & COUNT_PEER_SRC_AS && !c->nfacctd_bgp_peer_as_src_type && (config.acct_type != ACCT_SF && config.acct_type != ACCT_NF)) ) { printf("ERROR: At least one of the following primitives is in use but its source type is not specified:\n"); printf(" peer_src_as => bgp_peer_src_as_type\n"); printf(" src_as_path => bgp_src_as_path_type\n"); printf(" src_std_comm => bgp_src_std_comm_type\n"); printf(" src_ext_comm => bgp_src_ext_comm_type\n"); printf(" src_local_pref => bgp_src_local_pref_type\n"); printf(" src_med => bgp_src_med_type\n"); exit(1); } c->data_type |= PIPE_TYPE_BGP; } } void process_bgp_md5_file(int sock, struct bgp_md5_table *bgp_md5) { struct my_tcp_md5sig md5sig; struct sockaddr_storage ss_md5sig; int rc, keylen, idx = 0, ss_md5sig_len; while (idx < bgp_md5->num) { memset(&md5sig, 0, sizeof(md5sig)); memset(&ss_md5sig, 0, sizeof(ss_md5sig)); ss_md5sig_len = addr_to_sa((struct sockaddr *)&ss_md5sig, &bgp_md5->table[idx].addr, 0); memcpy(&md5sig.tcpm_addr, &ss_md5sig, ss_md5sig_len); keylen = strlen(bgp_md5->table[idx].key); if (keylen) { md5sig.tcpm_keylen = keylen; memcpy(md5sig.tcpm_key, &bgp_md5->table[idx].key, keylen); } rc = setsockopt(sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)); if (rc < 0) Log(LOG_ERR, "WARN ( default/core/BGP ): setsockopt() failed for TCP_MD5SIG (errno: %d).\n", errno); idx++; } } pmacct-0.14.0/src/bgp/bgp_hash.c0000644000175000017500000001146011741024530015326 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* * Originally based on Quagga hash routine which is: * * Copyright (C) 1998 Kunihiro Ishiguro * * GNU Zebra 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. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #define __BGP_HASH_C #include "pmacct.h" #include "bgp_packet.h" #include "bgp_hash.h" /* Allocate a new hash. */ struct hash * hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), int (*hash_cmp) (const void *, const void *)) { struct hash *hash; hash = malloc(sizeof (struct hash)); memset (hash, 0, sizeof (struct hash)); hash->index = malloc(sizeof (struct hash_backet *) * size); memset (hash->index, 0, sizeof (struct hash_backet *) * size); hash->size = size; hash->hash_key = hash_key; hash->hash_cmp = hash_cmp; hash->count = 0; return hash; } /* Allocate a new hash with default hash size. */ struct hash * hash_create (unsigned int (*hash_key) (void *), int (*hash_cmp) (const void *, const void *)) { return hash_create_size (HASHTABSIZE, hash_key, hash_cmp); } /* Utility function for hash_get(). When this function is specified as alloc_func, return arugment as it is. This function is used for intern already allocated value. */ void * hash_alloc_intern (void *arg) { return arg; } /* Lookup and return hash backet in hash. If there is no corresponding hash backet and alloc_func is specified, create new hash backet. */ void * hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *)) { unsigned int key; unsigned int index; void *newdata; struct hash_backet *backet; key = (*hash->hash_key) (data); index = key % hash->size; for (backet = hash->index[index]; backet != NULL; backet = backet->next) if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) return backet->data; if (alloc_func) { newdata = (*alloc_func) (data); if (newdata == NULL) return NULL; backet = malloc(sizeof (struct hash_backet)); memset(backet, 0, sizeof (struct hash_backet)); backet->data = newdata; backet->key = key; backet->next = hash->index[index]; hash->index[index] = backet; hash->count++; return backet->data; } return NULL; } /* Hash lookup. */ void * hash_lookup (struct hash *hash, void *data) { return hash_get (hash, data, NULL); } /* This function release registered value from specified hash. When release is successfully finished, return the data pointer in the hash backet. */ void * hash_release (struct hash *hash, void *data) { void *ret; unsigned int key; unsigned int index; struct hash_backet *backet; struct hash_backet *pp; key = (*hash->hash_key) (data); index = key % hash->size; for (backet = pp = hash->index[index]; backet; backet = backet->next) { if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) { if (backet == pp) hash->index[index] = backet->next; else pp->next = backet->next; ret = backet->data; free(backet); hash->count--; return ret; } pp = backet; } return NULL; } /* Iterator function for hash. */ void hash_iterate (struct hash *hash, void (*func) (struct hash_backet *, void *), void *arg) { unsigned int i; struct hash_backet *hb; struct hash_backet *hbnext; for (i = 0; i < hash->size; i++) for (hb = hash->index[i]; hb; hb = hbnext) { /* get pointer to next hash backet here, in case (*func) * decides to delete hb by calling hash_release */ hbnext = hb->next; (*func) (hb, arg); } } /* Clean up hash. */ void hash_clean (struct hash *hash, void (*free_func) (void *)) { unsigned int i; struct hash_backet *hb; struct hash_backet *next; for (i = 0; i < hash->size; i++) { for (hb = hash->index[i]; hb; hb = next) { next = hb->next; if (free_func) (*free_func) (hb->data); free(hb); hash->count--; } hash->index[i] = NULL; } } /* Free hash memory. You may call hash_clean before call this function. */ void hash_free (struct hash *hash) { free(hash->index); free(hash); } pmacct-0.14.0/src/bgp/bgp_aspath.c0000644000175000017500000011454011741024731015671 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* Originally based on Quagga AS path management routines which is: Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro Copyright (C) 2005 Sun Microsystems, Inc. GNU Zebra 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. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define __BGP_ASPATH_C #include "pmacct.h" #include "bgp_hash.h" #include "jhash.h" #include "bgp.h" /* Attr. Flags and Attr. Type Code. */ #define AS_HEADER_SIZE 2 /* Now FOUR octets are used for AS value. */ #define AS_VALUE_SIZE sizeof (as_t) /* This is the old one */ #define AS16_VALUE_SIZE sizeof (as16_t) /* Maximum protocol segment length value */ #define AS_SEGMENT_MAX 255 /* Calculated size in bytes of ASN segment data to hold N ASN's */ #define ASSEGMENT_DATA_SIZE(N,S) \ ((N) * ( (S) ? AS_VALUE_SIZE : AS16_VALUE_SIZE) ) /* Calculated size of segment struct to hold N ASN's */ #define ASSEGMENT_SIZE(N,S) (AS_HEADER_SIZE + ASSEGMENT_DATA_SIZE (N,S)) /* AS segment octet length. */ #define ASSEGMENT_LEN(X,S) ASSEGMENT_SIZE((X)->length,S) /* AS_SEQUENCE segments can be packed together */ /* Can the types of X and Y be considered for packing? */ #define ASSEGMENT_TYPES_PACKABLE(X,Y) \ ( ((X)->type == (Y)->type) \ && ((X)->type == AS_SEQUENCE)) /* Types and length of X,Y suitable for packing? */ #define ASSEGMENTS_PACKABLE(X,Y) \ ( ASSEGMENT_TYPES_PACKABLE( (X), (Y)) \ && ( ((X)->length + (Y)->length) <= AS_SEGMENT_MAX ) ) /* As segment header - the on-wire representation * NOT the internal representation! */ struct assegment_header { u_char type; u_char length; }; /* Hash for aspath. This is the top level structure of AS path. */ // struct hash *ashash; static inline as_t * assegment_data_new (int num) { return (malloc(ASSEGMENT_DATA_SIZE (num, 1))); } static inline void assegment_data_free (as_t *asdata) { free(asdata); } /* Get a new segment. Note that 0 is an allowed length, * and will result in a segment with no allocated data segment. * the caller should immediately assign data to the segment, as the segment * otherwise is not generally valid */ static struct assegment * assegment_new (u_char type, u_short length) { struct assegment *new; new = malloc(sizeof (struct assegment)); memset(new, 0, sizeof (struct assegment)); if (length) { new->as = assegment_data_new (length); memset(new->as, 0, length); } new->length = length; new->type = type; return new; } static void assegment_free (struct assegment *seg) { if (!seg) return; if (seg->as) free(seg->as); memset (seg, 0xfe, sizeof(struct assegment)); free(seg); return; } /* free entire chain of segments */ static void assegment_free_all (struct assegment *seg) { struct assegment *prev; while (seg) { prev = seg; seg = seg->next; assegment_free (prev); } } /* Duplicate just the given assegment and its data */ static struct assegment * assegment_dup (struct assegment *seg) { struct assegment *new; new = assegment_new (seg->type, seg->length); memcpy (new->as, seg->as, ASSEGMENT_DATA_SIZE (new->length, 1) ); return new; } /* Duplicate entire chain of assegments, return the head */ static struct assegment * assegment_dup_all (struct assegment *seg) { struct assegment *new = NULL; struct assegment *head = NULL; while (seg) { if (head) { new->next = assegment_dup (seg); new = new->next; } else head = new = assegment_dup (seg); seg = seg->next; } return head; } /* prepend the as number to given segment, given num of times */ static struct assegment * assegment_prepend_asns (struct assegment *seg, as_t asnum, int num) { as_t *newas; if (!num) return seg; if (num >= AS_SEGMENT_MAX) return seg; /* we don't do huge prepends */ newas = assegment_data_new (seg->length + num); if (newas) { int i; for (i = 0; i < num; i++) newas[i] = asnum; memcpy (newas + num, seg->as, ASSEGMENT_DATA_SIZE (seg->length, 1)); free(seg->as); seg->as = newas; seg->length += num; return seg; } assegment_free_all (seg); return NULL; } /* append given array of as numbers to the segment */ static struct assegment * assegment_append_asns (struct assegment *seg, as_t *asnos, int num) { as_t *newas; newas = realloc(seg->as, ASSEGMENT_DATA_SIZE (seg->length + num, 1)); if (newas) { seg->as = newas; memcpy (seg->as + seg->length, asnos, ASSEGMENT_DATA_SIZE(num, 1)); seg->length += num; return seg; } assegment_free_all (seg); return NULL; } static int int_cmp (const void *p1, const void *p2) { const as_t *as1 = p1; const as_t *as2 = p2; return (*as1 == *as2) ? 0 : ( (*as1 > *as2) ? 1 : -1); } /* normalise the segment. * In particular, merge runs of AS_SEQUENCEs into one segment * Internally, we do not care about the wire segment length limit, and * we want each distinct AS_PATHs to have the exact same internal * representation - eg, so that our hashing actually works.. */ static struct assegment * assegment_normalise (struct assegment *head) { struct assegment *seg = head, *pin; struct assegment *tmp; if (!head) return head; while (seg) { pin = seg; /* Sort values SET segments, for determinism in paths to aid * creation of hash values / path comparisons * and because it helps other lesser implementations ;) */ if (seg->type == AS_SET || seg->type == AS_CONFED_SET) { int tail = 0; int i; qsort (seg->as, seg->length, sizeof(as_t), int_cmp); /* weed out dupes */ for (i=1; i < seg->length; i++) { if (seg->as[tail] == seg->as[i]) continue; tail++; if (tail < i) seg->as[tail] = seg->as[i]; } /* seg->length can be 0.. */ if (seg->length) seg->length = tail + 1; } /* read ahead from the current, pinned segment while the segments * are packable/mergeable. Append all following packable segments * to the segment we have pinned and remove these appended * segments. */ while (pin->next && ASSEGMENT_TYPES_PACKABLE(pin, pin->next)) { tmp = pin->next; seg = pin->next; /* append the next sequence to the pinned sequence */ pin = assegment_append_asns (pin, seg->as, seg->length); /* bypass the next sequence */ pin->next = seg->next; /* get rid of the now referenceless segment */ assegment_free (tmp); } seg = pin->next; } return head; } static struct aspath * aspath_new (void) { struct aspath *aspath; aspath = malloc(sizeof (struct aspath)); memset (aspath, 0, sizeof (struct aspath)); return aspath; } /* Free AS path structure. */ void aspath_free (struct aspath *aspath) { if (!aspath) return; if (aspath->segments) assegment_free_all (aspath->segments); if (aspath->str) free(aspath->str); free(aspath); } /* Unintern aspath from AS path bucket. */ void aspath_unintern (struct aspath *aspath) { struct aspath *ret; if (aspath->refcnt) aspath->refcnt--; if (aspath->refcnt == 0) { /* This aspath must exist in aspath hash table. */ ret = hash_release (ashash, aspath); assert (ret != NULL); aspath_free (aspath); } } /* Return the start or end delimiters for a particular Segment type */ #define AS_SEG_START 0 #define AS_SEG_END 1 static char aspath_delimiter_char (u_char type, u_char which) { int i; struct { int type; char start; char end; } aspath_delim_char [] = { { AS_SET, '{', '}' }, { AS_CONFED_SET, '[', ']' }, { AS_CONFED_SEQUENCE, '(', ')' }, { 0 } }; for (i = 0; aspath_delim_char[i].type != 0; i++) { if (aspath_delim_char[i].type == type) { if (which == AS_SEG_START) return aspath_delim_char[i].start; else if (which == AS_SEG_END) return aspath_delim_char[i].end; } } return ' '; } /* countup asns from this segment and index onward */ static int assegment_count_asns (struct assegment *seg, int from) { int count = 0; while (seg) { if (!from) count += seg->length; else { count += (seg->length - from); from = 0; } seg = seg->next; } return count; } unsigned int aspath_count_confeds (struct aspath *aspath) { int count = 0; struct assegment *seg = aspath->segments; while (seg) { if (seg->type == AS_CONFED_SEQUENCE) count += seg->length; else if (seg->type == AS_CONFED_SET) count++; seg = seg->next; } return count; } unsigned int aspath_count_hops (struct aspath *aspath) { int count = 0; struct assegment *seg = aspath->segments; while (seg) { if (seg->type == AS_SEQUENCE) count += seg->length; else if (seg->type == AS_SET) count++; seg = seg->next; } return count; } /* Estimate size aspath /might/ take if encoded into an * ASPATH attribute. * * This is a quick estimate, not definitive! aspath_put() * may return a different number!! */ unsigned int aspath_size (struct aspath *aspath) { int size = 0; struct assegment *seg = aspath->segments; while (seg) { size += ASSEGMENT_SIZE(seg->length, 1); seg = seg->next; } return size; } /* Return highest public ASN in path */ as_t aspath_highest (struct aspath *aspath) { struct assegment *seg = aspath->segments; as_t highest = 0; unsigned int i; while (seg) { for (i = 0; i < seg->length; i++) if (seg->as[i] > highest && (seg->as[i] < BGP_PRIVATE_AS_MIN || seg->as[i] > BGP_PRIVATE_AS_MAX)) highest = seg->as[i]; seg = seg->next; } return highest; } /* Return 1 if there are any 4-byte ASes in the path */ unsigned int aspath_has_as4 (struct aspath *aspath) { struct assegment *seg = aspath->segments; unsigned int i; while (seg) { for (i = 0; i < seg->length; i++) if (seg->as[i] > BGP_AS_MAX) return 1; seg = seg->next; } return 0; } /* Return number of as numbers in in path */ unsigned int aspath_count_numas (struct aspath *aspath) { struct assegment *seg = aspath->segments; unsigned int num; num=0; while (seg) { num += seg->length; seg = seg->next; } return num; } /* Convert aspath structure to string expression. */ static char * aspath_make_str_count (struct aspath *as) { struct assegment *seg; int str_size; int len = 0; char *str_buf; /* Empty aspath. */ if (!as->segments) { str_buf = malloc(1); str_buf[0] = '\0'; return str_buf; } seg = as->segments; /* ASN takes 5 chars at least, plus seperator, see below. * If there is one differing segment type, we need an additional * 2 chars for segment delimiters, and the final '\0'. * Hopefully this is large enough to avoid hitting the realloc * code below for most common sequences. * * With 32bit ASNs, this range will increase, but only worth changing * once there are significant numbers of ASN >= 100000 */ #define ASN_STR_LEN (10 + 1) str_size = MAX (assegment_count_asns (seg, 0) * ASN_STR_LEN + 2 + 1, ASPATH_STR_DEFAULT_LEN); str_buf = malloc(str_size); while (seg) { int i; char seperator; /* Check AS type validity. Set seperator for segment */ switch (seg->type) { case AS_SET: case AS_CONFED_SET: seperator = ','; break; case AS_SEQUENCE: case AS_CONFED_SEQUENCE: seperator = ' '; break; default: free(str_buf); return NULL; } /* We might need to increase str_buf, particularly if path has * differing segments types, our initial guesstimate above will * have been wrong. need 5 chars for ASN, a seperator each and * potentially two segment delimiters, plus a space between each * segment and trailing zero. * * This may need to revised if/when significant numbers of * ASNs >= 100000 are assigned and in-use on the internet... */ #define SEGMENT_STR_LEN(X) (((X)->length * ASN_STR_LEN) + 2 + 1 + 1) if ( (len + SEGMENT_STR_LEN(seg)) > str_size) { str_size = len + SEGMENT_STR_LEN(seg); str_buf = realloc(str_buf, str_size); } #undef ASN_STR_LEN #undef SEGMENT_STR_LEN if (seg->type != AS_SEQUENCE) len += snprintf (str_buf + len, str_size - len, "%c", aspath_delimiter_char (seg->type, AS_SEG_START)); /* write out the ASNs, with their seperators, bar the last one*/ for (i = 0; i < seg->length; i++) { len += snprintf (str_buf + len, str_size - len, "%u", seg->as[i]); as->last_as = seg->as[i]; if (i < (seg->length - 1)) len += snprintf (str_buf + len, str_size - len, "%c", seperator); } if (seg->type != AS_SEQUENCE) len += snprintf (str_buf + len, str_size - len, "%c", aspath_delimiter_char (seg->type, AS_SEG_END)); if (seg->next) len += snprintf (str_buf + len, str_size - len, " "); seg = seg->next; } assert (len < str_size); str_buf[len] = '\0'; return str_buf; } static void aspath_str_update (struct aspath *as) { if (as->str) free(as->str); as->str = aspath_make_str_count (as); } /* Intern allocated AS path. */ struct aspath * aspath_intern (struct aspath *aspath) { struct aspath *find; /* Assert this AS path structure is not interned. */ assert (aspath->refcnt == 0); /* Check AS path hash. */ find = hash_get (ashash, aspath, hash_alloc_intern); if (find != aspath) aspath_free (aspath); find->refcnt++; if (! find->str) find->str = aspath_make_str_count (find); return find; } /* Duplicate aspath structure. Created same aspath structure but reference count and AS path string is cleared. */ struct aspath * aspath_dup (struct aspath *aspath) { struct aspath *new; new = malloc(sizeof (struct aspath)); memset(new, 0, sizeof(struct aspath)); if (aspath->segments) new->segments = assegment_dup_all (aspath->segments); else new->segments = NULL; new->str = aspath_make_str_count (aspath); new->last_as = aspath->last_as; return new; } static void * aspath_hash_alloc (void *arg) { struct aspath *aspath; /* New aspath structure is needed. */ aspath = aspath_dup (arg); /* Malformed AS path value. */ if (! aspath->str) { aspath_free (aspath); return NULL; } return aspath; } /* parse as-segment in struct assegment */ static struct assegment * assegments_parse(char *s, size_t length, int use32bit) { struct assegment_header segh; struct assegment *seg, *prev = NULL, *head = NULL; size_t bytes = 0, aspathlen; u_char *tmp8; u_int16_t tmp16; u_int32_t tmp32; /* empty aspath (ie iBGP or somesuch) */ if (length == 0) return NULL; /* basic checks; XXX: length? */ if (length % AS16_VALUE_SIZE) return NULL; aspathlen = length; // while ((length > AS_HEADER_SIZE) && (bytes < length)) { // while ((aspathlen > AS_HEADER_SIZE) && (bytes < aspathlen)) { while (aspathlen > 0) { int i; int seg_size; /* softly softly, get the header first on its own */ tmp8 = (u_char *) s; segh.type = *tmp8; s++; tmp8 = (u_char *) s; segh.length = *tmp8; s++; seg_size = ASSEGMENT_SIZE(segh.length, use32bit); /* check it.. */ if ( ((bytes + seg_size) > length) /* 1771bis 4.3b: seg length contains one or more */ || (segh.length == 0) /* Paranoia in case someone changes type of segment length */ || ((sizeof(segh.length) > 1) && (segh.length > AS_SEGMENT_MAX)) ) { if (head) assegment_free_all (head); return NULL; } /* now its safe to trust lengths */ seg = assegment_new (segh.type, segh.length); if (head) prev->next = seg; else /* it's the first segment */ head = prev = seg; for (i = 0; i < segh.length; i++) { if (use32bit) { memcpy(&tmp32, s, 4); seg->as[i] = ntohl(tmp32); s += 4; } else { memcpy(&tmp16, s, 2); seg->as[i] = ntohs(tmp16); s += 2; } } bytes += seg_size; aspathlen -= seg_size; prev = seg; } return assegment_normalise (head); } /* AS path parse function. If there is same AS path in the the AS path hash then return it else make new AS path structure. */ struct aspath *aspath_parse(char *s, size_t length, int use32bit) { struct aspath as; struct aspath *find; /* If length is odd it's malformed AS path. */ /* Nit-picking: if (use32bit == 0) it is malformed if odd, * otherwise its malformed when length is larger than 2 and (length-2) * is not dividable by 4. * But... this time we're lazy */ if (length % AS16_VALUE_SIZE ) return NULL; memset (&as, 0, sizeof (struct aspath)); as.segments = assegments_parse(s, length, use32bit); /* If already same aspath exist then return it. */ find = hash_get (ashash, &as, aspath_hash_alloc); /* aspath_hash_alloc dupes segments too. that probably could be * optimised out. */ assegment_free_all (as.segments); if (as.str) free(as.str); if (! find) return NULL; find->refcnt++; return find; } static struct assegment * aspath_aggregate_as_set_add (struct aspath *aspath, struct assegment *asset, as_t as) { int i; /* If this is first AS set member, create new as-set segment. */ if (asset == NULL) { asset = assegment_new (AS_SET, 1); if (! aspath->segments) aspath->segments = asset; else { struct assegment *seg = aspath->segments; while (seg->next) seg = seg->next; seg->next = asset; } asset->type = AS_SET; asset->length = 1; asset->as[0] = as; } else { /* Check this AS value already exists or not. */ for (i = 0; i < asset->length; i++) if (asset->as[i] == as) return asset; asset->length++; asset->as = realloc(asset->as, asset->length * AS_VALUE_SIZE); asset->as[asset->length - 1] = as; } return asset; } /* Modify as1 using as2 for aggregation. */ struct aspath * aspath_aggregate (struct aspath *as1, struct aspath *as2) { int i; int minlen; int match; int from; struct assegment *seg1 = as1->segments; struct assegment *seg2 = as2->segments; struct aspath *aspath = NULL; struct assegment *asset; struct assegment *prevseg = NULL; match = 0; minlen = 0; aspath = NULL; asset = NULL; /* First of all check common leading sequence. */ while (seg1 && seg2) { /* Check segment type. */ if (seg1->type != seg2->type) break; /* Minimum segment length. */ minlen = MIN(seg1->length, seg2->length); for (match = 0; match < minlen; match++) if (seg1->as[match] != seg2->as[match]) break; if (match) { struct assegment *seg = assegment_new (seg1->type, 0); seg = assegment_append_asns (seg, seg1->as, match); if (! aspath) { aspath = aspath_new (); aspath->segments = seg; } else prevseg->next = seg; prevseg = seg; } if (match != minlen || match != seg1->length || seg1->length != seg2->length) break; seg1 = seg1->next; seg2 = seg2->next; } if (! aspath) aspath = aspath_new(); /* Make as-set using rest of all information. */ from = match; while (seg1) { for (i = from; i < seg1->length; i++) asset = aspath_aggregate_as_set_add (aspath, asset, seg1->as[i]); from = 0; seg1 = seg1->next; } from = match; while (seg2) { for (i = from; i < seg2->length; i++) asset = aspath_aggregate_as_set_add (aspath, asset, seg2->as[i]); from = 0; seg2 = seg2->next; } assegment_normalise (aspath->segments); aspath_str_update (aspath); return aspath; } /* When a BGP router receives an UPDATE with an MP_REACH_NLRI attribute, check the leftmost AS number in the AS_PATH attribute is or not the peer's AS number. */ int aspath_firstas_check (struct aspath *aspath, as_t asno) { if ( (aspath == NULL) || (aspath->segments == NULL) ) return 0; if (aspath->segments && (aspath->segments->type == AS_SEQUENCE) && (aspath->segments->as[0] == asno )) return 1; return 0; } /* AS path loop check. If aspath contains asno then return >= 1. */ int aspath_loop_check (struct aspath *aspath, as_t asno) { struct assegment *seg; int count = 0; if ( (aspath == NULL) || (aspath->segments == NULL) ) return 0; seg = aspath->segments; while (seg) { int i; for (i = 0; i < seg->length; i++) if (seg->as[i] == asno) count++; seg = seg->next; } return count; } /* When all of AS path is private AS return 1. */ int aspath_private_as_check (struct aspath *aspath) { struct assegment *seg; if ( !(aspath && aspath->segments) ) return 0; seg = aspath->segments; while (seg) { int i; for (i = 0; i < seg->length; i++) { if ( (seg->as[i] < BGP_PRIVATE_AS_MIN) || (seg->as[i] > BGP_PRIVATE_AS_MAX) ) return 0; } seg = seg->next; } return 1; } /* Merge as1 to as2. as2 should be uninterned aspath. */ static struct aspath * aspath_merge (struct aspath *as1, struct aspath *as2) { struct assegment *last, *new; if (! as1 || ! as2) return NULL; last = new = assegment_dup_all (as1->segments); /* find the last valid segment */ while (last && last->next) last = last->next; last->next = as2->segments; as2->segments = new; aspath_str_update (as2); return as2; } /* Prepend as1 to as2. as2 should be uninterned aspath. */ struct aspath * aspath_prepend (struct aspath *as1, struct aspath *as2) { struct assegment *seg1; struct assegment *seg2; if (! as1 || ! as2) return NULL; seg1 = as1->segments; seg2 = as2->segments; /* If as2 is empty, only need to dupe as1's chain onto as2 */ if (seg2 == NULL) { as2->segments = assegment_dup_all (as1->segments); aspath_str_update (as2); return as2; } /* If as1 is empty AS, no prepending to do. */ if (seg1 == NULL) return as2; /* find the tail as1's segment chain. */ while (seg1 && seg1->next) seg1 = seg1->next; /* Compare last segment type of as1 and first segment type of as2. */ if (seg1->type != seg2->type) return aspath_merge (as1, as2); if (seg1->type == AS_SEQUENCE) { /* We have two chains of segments, as1->segments and seg2, * and we have to attach them together, merging the attaching * segments together into one. * * 1. dupe as1->segments onto head of as2 * 2. merge seg2's asns onto last segment of this new chain * 3. attach chain after seg2 */ /* dupe as1 onto as2's head */ seg1 = as2->segments = assegment_dup_all (as1->segments); /* refind the tail of as2, reusing seg1 */ while (seg1 && seg1->next) seg1 = seg1->next; /* merge the old head, seg2, into tail, seg1 */ seg1 = assegment_append_asns (seg1, seg2->as, seg2->length); /* bypass the merged seg2, and attach any chain after it to * chain descending from as2's head */ seg1->next = seg2->next; /* seg2 is now referenceless and useless*/ assegment_free (seg2); /* we've now prepended as1's segment chain to as2, merging * the inbetween AS_SEQUENCE of seg2 in the process */ aspath_str_update (as2); return as2; } else { /* AS_SET merge code is needed at here. */ return aspath_merge (as1, as2); } /* XXX: Ermmm, what if as1 has multiple segments?? */ /* Not reached */ } /* Iterate over AS_PATH segments and wipe all occurences of the * listed AS numbers. Hence some segments may lose some or even * all data on the way, the operation is implemented as a smarter * version of aspath_dup(), which allocates memory to hold the new * data, not the original. The new AS path is returned. */ struct aspath * aspath_filter_exclude (struct aspath * source, struct aspath * exclude_list) { struct assegment * srcseg, * exclseg, * lastseg; struct aspath * newpath; newpath = aspath_new(); lastseg = NULL; for (srcseg = source->segments; srcseg; srcseg = srcseg->next) { unsigned i, y, newlen = 0, done = 0, skip_as; struct assegment * newseg; /* Find out, how much ASns are we going to pick from this segment. * We can't perform filtering right inline, because the size of * the new segment isn't known at the moment yet. */ for (i = 0; i < srcseg->length; i++) { skip_as = 0; for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next) for (y = 0; y < exclseg->length; y++) if (srcseg->as[i] == exclseg->as[y]) { skip_as = 1; // There's no sense in testing the rest of exclusion list, bail out. break; } if (!skip_as) newlen++; } /* newlen is now the number of ASns to copy */ if (!newlen) continue; /* Actual copying. Allocate memory and iterate once more, performing filtering. */ newseg = assegment_new (srcseg->type, newlen); for (i = 0; i < srcseg->length; i++) { skip_as = 0; for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next) for (y = 0; y < exclseg->length; y++) if (srcseg->as[i] == exclseg->as[y]) { skip_as = 1; break; } if (skip_as) continue; newseg->as[done++] = srcseg->as[i]; } /* At his point newlen must be equal to done, and both must be positive. Append * the filtered segment to the gross result. */ if (!lastseg) newpath->segments = newseg; else lastseg->next = newseg; lastseg = newseg; } aspath_str_update (newpath); /* We are happy returning even an empty AS_PATH, because the administrator * might expect this very behaviour. There's a mean to avoid this, if necessary, * by having a match rule against certain AS_PATH regexps in the route-map index. */ aspath_free (source); return newpath; } /* Add specified AS to the leftmost of aspath. */ static struct aspath * aspath_add_one_as (struct aspath *aspath, as_t asno, u_char type) { struct assegment *assegment = aspath->segments; /* In case of empty aspath. */ if (assegment == NULL || assegment->length == 0) { aspath->segments = assegment_new (type, 1); aspath->segments->as[0] = asno; if (assegment) assegment_free (assegment); return aspath; } if (assegment->type == type) aspath->segments = assegment_prepend_asns (aspath->segments, asno, 1); else { /* create new segment * push it onto head of aspath's segment chain */ struct assegment *newsegment; newsegment = assegment_new (type, 1); newsegment->as[0] = asno; newsegment->next = assegment; aspath->segments = newsegment; } return aspath; } /* Add specified AS to the leftmost of aspath. */ struct aspath * aspath_add_seq (struct aspath *aspath, as_t asno) { return aspath_add_one_as (aspath, asno, AS_SEQUENCE); } /* Compare leftmost AS value for MED check. If as1's leftmost AS and as2's leftmost AS is same return 1. */ int aspath_cmp_left (const struct aspath *aspath1, const struct aspath *aspath2) { const struct assegment *seg1 = NULL; const struct assegment *seg2 = NULL; if (!(aspath1 && aspath2)) return 0; seg1 = aspath1->segments; seg2 = aspath2->segments; /* find first non-confed segments for each */ while (seg1 && ((seg1->type == AS_CONFED_SEQUENCE) || (seg1->type == AS_CONFED_SET))) seg1 = seg1->next; while (seg2 && ((seg2->type == AS_CONFED_SEQUENCE) || (seg2->type == AS_CONFED_SET))) seg2 = seg2->next; /* Check as1's */ if (!(seg1 && seg2 && (seg1->type == AS_SEQUENCE) && (seg2->type == AS_SEQUENCE))) return 0; if (seg1->as[0] == seg2->as[0]) return 1; return 0; } /* Truncate an aspath after a number of hops, and put the hops remaining * at the front of another aspath. Needed for AS4 compat. * * Returned aspath is a /new/ aspath, which should either by free'd or * interned by the caller, as desired. */ struct aspath * aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path) { struct assegment *seg, *newseg, *prevseg = NULL; struct aspath *newpath = NULL, *mergedpath; int hops, cpasns = 0; if (!aspath) return NULL; seg = aspath->segments; /* CONFEDs should get reconciled too.. */ hops = (aspath_count_hops (aspath) + aspath_count_confeds (aspath)) - aspath_count_hops (as4path); if (hops < 0) { /* Something's gone wrong. The RFC says we should now ignore AS4_PATH, * which is daft behaviour - it contains vital loop-detection * information which must have been removed from AS_PATH. */ hops = aspath_count_hops (aspath); } if (!hops) return aspath_dup (as4path); while (seg && hops > 0) { switch (seg->type) { case AS_SET: case AS_CONFED_SET: hops--; cpasns = seg->length; break; case AS_CONFED_SEQUENCE: /* Should never split a confed-sequence, if hop-count * suggests we must then something's gone wrong somewhere. * * Most important goal is to preserve AS_PATHs prime function * as loop-detector, so we fudge the numbers so that the entire * confed-sequence is merged in. */ if (hops < seg->length) { hops = seg->length; } case AS_SEQUENCE: cpasns = MIN(seg->length, hops); hops -= seg->length; } assert (cpasns <= seg->length); newseg = assegment_new (seg->type, 0); newseg = assegment_append_asns (newseg, seg->as, cpasns); if (!newpath) { newpath = aspath_new (); newpath->segments = newseg; } else prevseg->next = newseg; prevseg = newseg; seg = seg->next; } /* We may be able to join some segments here, and we must * do this because... we want normalised aspaths in out hash * and we do not want to stumble in aspath_put. */ mergedpath = aspath_merge (newpath, aspath_dup(as4path)); aspath_free (newpath); mergedpath->segments = assegment_normalise (mergedpath->segments); aspath_str_update (mergedpath); return mergedpath; } /* Compare leftmost AS value for MED check. If as1's leftmost AS and as2's leftmost AS is same return 1. (confederation as-path only). */ int aspath_cmp_left_confed (const struct aspath *aspath1, const struct aspath *aspath2) { if (! (aspath1 && aspath2) ) return 0; if ( !(aspath1->segments && aspath2->segments) ) return 0; if ( (aspath1->segments->type != AS_CONFED_SEQUENCE) || (aspath2->segments->type != AS_CONFED_SEQUENCE) ) return 0; if (aspath1->segments->as[0] == aspath2->segments->as[0]) return 1; return 0; } /* Delete all leading AS_CONFED_SEQUENCE/SET segments from aspath. * See RFC3065, 6.1 c1 */ struct aspath * aspath_delete_confed_seq (struct aspath *aspath) { struct assegment *seg; if (!(aspath && aspath->segments)) return aspath; seg = aspath->segments; /* "if the first path segment of the AS_PATH is * of type AS_CONFED_SEQUENCE," */ if (aspath->segments->type != AS_CONFED_SEQUENCE) return aspath; /* "... that segment and any immediately following segments * of the type AS_CONFED_SET or AS_CONFED_SEQUENCE are removed * from the AS_PATH attribute," */ while (seg && (seg->type == AS_CONFED_SEQUENCE || seg->type == AS_CONFED_SET)) { aspath->segments = seg->next; assegment_free (seg); seg = aspath->segments; } aspath_str_update (aspath); return aspath; } /* Add new AS number to the leftmost part of the aspath as AS_CONFED_SEQUENCE. */ struct aspath* aspath_add_confed_seq (struct aspath *aspath, as_t asno) { return aspath_add_one_as (aspath, asno, AS_CONFED_SEQUENCE); } /* Add new as value to as path structure. */ static void aspath_as_add (struct aspath *as, as_t asno) { struct assegment *seg = as->segments; if (!seg) return; /* Last segment search procedure. */ while (seg->next) seg = seg->next; assegment_append_asns (seg, &asno, 1); } /* Add new as segment to the as path. */ static void aspath_segment_add (struct aspath *as, int type) { struct assegment *seg = as->segments; struct assegment *new = assegment_new (type, 0); if (seg) { while (seg->next) seg = seg->next; seg->next = new; } else as->segments = new; } struct aspath * aspath_empty (void) { return aspath_parse (NULL, 0, 1); /* 32Bit ;-) */ } struct aspath * aspath_empty_get (void) { struct aspath *aspath; aspath = aspath_new (); aspath->str = aspath_make_str_count (aspath); return aspath; } unsigned long aspath_count (void) { return ashash->count; } /* Theoretically, one as path can have: One BGP packet size should be less than 4096. One BGP attribute size should be less than 4096 - BGP header size. One BGP aspath size should be less than 4096 - BGP header size - BGP mandantry attribute size. */ /* AS path string lexical token enum. */ enum as_token { as_token_asval, as_token_set_start, as_token_set_end, as_token_confed_seq_start, as_token_confed_seq_end, as_token_confed_set_start, as_token_confed_set_end, as_token_unknown }; /* Return next token and point for string parse. */ static const char * aspath_gettoken (const char *buf, enum as_token *token, u_long *asno) { const char *p = buf; /* Skip seperators (space for sequences, ',' for sets). */ while (isspace ((int) *p) || *p == ',') p++; /* Check the end of the string and type specify characters (e.g. {}()). */ switch (*p) { case '\0': return NULL; case '{': *token = as_token_set_start; p++; return p; case '}': *token = as_token_set_end; p++; return p; case '(': *token = as_token_confed_seq_start; p++; return p; case ')': *token = as_token_confed_seq_end; p++; return p; case '[': *token = as_token_confed_set_start; p++; return p; case ']': *token = as_token_confed_set_end; p++; return p; } /* Check actual AS value. */ if (isdigit ((int) *p)) { u_short asval; *token = as_token_asval; asval = (*p - '0'); p++; while (isdigit ((int) *p)) { asval *= 10; asval += (*p - '0'); p++; } *asno = asval; return p; } /* There is no match then return unknown token. */ *token = as_token_unknown; return p++; } struct aspath * aspath_str2aspath (const char *str) { enum as_token token = as_token_unknown; u_short as_type; u_long asno = 0; struct aspath *aspath; int needtype; aspath = aspath_new (); /* We start default type as AS_SEQUENCE. */ as_type = AS_SEQUENCE; needtype = 1; while ((str = aspath_gettoken (str, &token, &asno)) != NULL) { switch (token) { case as_token_asval: if (needtype) { aspath_segment_add (aspath, as_type); needtype = 0; } aspath_as_add (aspath, asno); break; case as_token_set_start: as_type = AS_SET; aspath_segment_add (aspath, as_type); needtype = 0; break; case as_token_set_end: as_type = AS_SEQUENCE; needtype = 1; break; case as_token_confed_seq_start: as_type = AS_CONFED_SEQUENCE; aspath_segment_add (aspath, as_type); needtype = 0; break; case as_token_confed_seq_end: as_type = AS_SEQUENCE; needtype = 1; break; case as_token_confed_set_start: as_type = AS_CONFED_SET; aspath_segment_add (aspath, as_type); needtype = 0; break; case as_token_confed_set_end: as_type = AS_SEQUENCE; needtype = 1; break; case as_token_unknown: default: aspath_free (aspath); return NULL; } } aspath->str = aspath_make_str_count (aspath); return aspath; } /* Make hash value by raw aspath data. */ unsigned int aspath_key_make (void *p) { struct aspath * aspath = (struct aspath *) p; unsigned int key = 0; if (!aspath->str) aspath_str_update (aspath); key = jhash (aspath->str, strlen(aspath->str), 2334325); return key; } /* If two aspath have same value then return 1 else return 0 */ static int aspath_cmp (const void *arg1, const void *arg2) { const struct assegment *seg1 = ((struct aspath *)arg1)->segments; const struct assegment *seg2 = ((struct aspath *)arg2)->segments; while (seg1 || seg2) { int i; if ((!seg1 && seg2) || (seg1 && !seg2)) return 0; if (seg1->type != seg2->type) return 0; if (seg1->length != seg2->length) return 0; for (i = 0; i < seg1->length; i++) if (seg1->as[i] != seg2->as[i]) return 0; seg1 = seg1->next; seg2 = seg2->next; } return 1; } /* AS path hash initialize. */ void aspath_init () { ashash = hash_create_size (32767, aspath_key_make, aspath_cmp); } void aspath_finish (void) { hash_free (ashash); } /* return and as path value */ const char * aspath_print (struct aspath *as) { return (as ? as->str : NULL); } pmacct-0.14.0/src/bgp/bgp_aspath.h0000644000175000017500000000711511741024773015703 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* Originally based on Quagga AS path related definitions which is: Copyright (C) 1997, 98, 99 Kunihiro Ishiguro GNU Zebra 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. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _BGP_ASPATH_H_ #define _BGP_ASPATH_H_ /* AS path segment type. */ #define AS_SET 1 #define AS_SEQUENCE 2 #define AS_CONFED_SEQUENCE 3 #define AS_CONFED_SET 4 /* Private AS range defined in RFC2270. */ #define BGP_PRIVATE_AS_MIN 64512U #define BGP_PRIVATE_AS_MAX 65535U /* we leave BGP_AS_MAX as the 16bit AS MAX number. */ #define BGP_AS_MAX 65535U #define BGP_AS4_MAX 4294967295U /* Transition 16Bit AS as defined by IANA */ #define BGP_AS_TRANS 23456U /* AS_PATH segment data in abstracted form, no limit is placed on length */ struct assegment { struct assegment *next; as_t *as; u_short length; u_char type; }; /* AS path may be include some AsSegments. */ struct aspath { /* Reference count to this aspath. */ unsigned long refcnt; /* segment data */ struct assegment *segments; as_t last_as; char *str; }; #define ASPATH_STR_DEFAULT_LEN 32 /* Prototypes. */ #if (!defined __BGP_ASPATH_C) #define EXT extern #else #define EXT #endif EXT void aspath_init (); EXT void aspath_finish (void); EXT struct aspath *aspath_parse (char *, size_t, int); EXT struct aspath *aspath_dup (struct aspath *); EXT struct aspath *aspath_aggregate (struct aspath *, struct aspath *); EXT struct aspath *aspath_prepend (struct aspath *, struct aspath *); EXT struct aspath *aspath_filter_exclude (struct aspath *, struct aspath *); EXT struct aspath *aspath_add_seq (struct aspath *, as_t); EXT struct aspath *aspath_add_confed_seq (struct aspath *, as_t); EXT int aspath_cmp_left (const struct aspath *, const struct aspath *); EXT int aspath_cmp_left_confed (const struct aspath *, const struct aspath *); EXT struct aspath *aspath_delete_confed_seq (struct aspath *); EXT struct aspath *aspath_empty (void); EXT struct aspath *aspath_empty_get (void); EXT struct aspath *aspath_str2aspath (const char *); EXT void aspath_free (struct aspath *); EXT struct aspath *aspath_intern (struct aspath *); EXT void aspath_unintern (struct aspath *); EXT const char *aspath_print (struct aspath *); EXT unsigned int aspath_key_make (void *); EXT int aspath_loop_check (struct aspath *, as_t); EXT int aspath_private_as_check (struct aspath *); EXT int aspath_firstas_check (struct aspath *, as_t); EXT unsigned long aspath_count (void); EXT unsigned int aspath_count_hops (struct aspath *); EXT unsigned int aspath_count_confeds (struct aspath *); EXT unsigned int aspath_size (struct aspath *); EXT as_t aspath_highest (struct aspath *); EXT struct aspath *aspath_reconcile_as4 (struct aspath *, struct aspath *); EXT unsigned int aspath_has_as4 (struct aspath *); EXT unsigned int aspath_count_numas (struct aspath *); #undef EXT #endif pmacct-0.14.0/src/bgp/bgp_community.c0000644000175000017500000003141011741025043016424 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* Originally based on Quagga BGP community attribute related functions which is: Copyright (C) 1998, 2001 Kunihiro Ishiguro GNU Zebra 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. GNU Zebra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define __BGP_COMMUNITY_C #include "pmacct.h" #include "bgp_hash.h" #include "bgp.h" #include "bgp_community.h" /* Hash of community attribute. */ // struct hash *comhash; /* Allocate a new communities value. */ static struct community * community_new (void) { void *tmp; tmp = malloc(sizeof (struct community)); memset(tmp, 0, sizeof (struct community)); return (struct community *) tmp; } /* Free communities value. */ void community_free (struct community *com) { if (com->val) free(com->val); if (com->str) free(com->str); free(com); } /* Add one community value to the community. */ static void community_add_val (struct community *com, u_int32_t val) { com->size++; if (com->val) com->val = realloc(com->val, com_length (com)); else com->val = malloc(com_length (com)); val = htonl (val); memcpy (com_lastval (com), &val, sizeof (u_int32_t)); } /* Delete one community. */ void community_del_val (struct community *com, u_int32_t *val) { int i = 0; int c = 0; if (! com->val) return; while (i < com->size) { if (memcmp (com->val + i, val, sizeof (u_int32_t)) == 0) { c = com->size -i -1; if (c > 0) memcpy (com->val + i, com->val + (i + 1), c * sizeof (val)); com->size--; if (com->size > 0) com->val = realloc(com->val, com_length (com)); else { free(com->val); com->val = NULL; } return; } i++; } } /* Delete all communities listed in com2 from com1 */ struct community * community_delete (struct community *com1, struct community *com2) { int i = 0; while(i < com2->size) { community_del_val (com1, com2->val + i); i++; } return com1; } /* Callback function from qsort(). */ static int community_compare (const void *a1, const void *a2) { u_int32_t v1; u_int32_t v2; memcpy (&v1, a1, sizeof (u_int32_t)); memcpy (&v2, a2, sizeof (u_int32_t)); v1 = ntohl (v1); v2 = ntohl (v2); if (v1 < v2) return -1; if (v1 > v2) return 1; return 0; } int community_include (struct community *com, u_int32_t val) { int i; val = htonl (val); for (i = 0; i < com->size; i++) if (memcmp (&val, com_nthval (com, i), sizeof (u_int32_t)) == 0) return 1; return 0; } static u_int32_t community_val_get (struct community *com, int i) { u_char *p; u_int32_t val; p = (u_char *) com->val; p += (i * 4); memcpy (&val, p, sizeof (u_int32_t)); return ntohl (val); } /* Sort and uniq given community. */ struct community * community_uniq_sort (struct community *com) { int i; struct community *new; u_int32_t val; if (! com) return NULL; new = community_new ();; for (i = 0; i < com->size; i++) { val = community_val_get (com, i); if (! community_include (new, val)) community_add_val (new, val); } qsort (new->val, new->size, sizeof (u_int32_t), community_compare); return new; } /* Convert communities attribute to string. For Well-known communities value, below keyword is used. 0x0 "internet" 0xFFFFFF01 "no-export" 0xFFFFFF02 "no-advertise" 0xFFFFFF03 "local-AS" For other values, "AS:VAL" format is used. */ static char * community_com2str (struct community *com) { int i; char *str; char *pnt; int len; int first; u_int32_t comval; u_int16_t as; u_int16_t val; if (!com) return NULL; /* When communities attribute is empty. */ if (com->size == 0) { str = malloc(1); str[0] = '\0'; return str; } /* Memory allocation is time consuming work. So we calculate required string length first. */ len = 0; for (i = 0; i < com->size; i++) { memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t)); comval = ntohl (comval); switch (comval) { case COMMUNITY_INTERNET: len += strlen (" internet"); break; case COMMUNITY_NO_EXPORT: len += strlen (" no-export"); break; case COMMUNITY_NO_ADVERTISE: len += strlen (" no-advertise"); break; case COMMUNITY_LOCAL_AS: len += strlen (" local-AS"); break; default: len += strlen (" 65536:65535"); break; } } /* Allocate memory. */ str = pnt = malloc(len); first = 1; /* Fill in string. */ for (i = 0; i < com->size; i++) { memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t)); comval = ntohl (comval); if (first) first = 0; else *pnt++ = ' '; switch (comval) { case COMMUNITY_INTERNET: strcpy (pnt, "internet"); pnt += strlen ("internet"); break; case COMMUNITY_NO_EXPORT: strcpy (pnt, "no-export"); pnt += strlen ("no-export"); break; case COMMUNITY_NO_ADVERTISE: strcpy (pnt, "no-advertise"); pnt += strlen ("no-advertise"); break; case COMMUNITY_LOCAL_AS: strcpy (pnt, "local-AS"); pnt += strlen ("local-AS"); break; default: as = (comval >> 16) & 0xFFFF; val = comval & 0xFFFF; sprintf (pnt, "%d:%d", as, val); pnt += strlen (pnt); break; } } *pnt = '\0'; return str; } /* Intern communities attribute. */ struct community * community_intern (struct community *com) { struct community *find; /* Assert this community structure is not interned. */ assert (com->refcnt == 0); /* Lookup community hash. */ find = (struct community *) hash_get (comhash, com, hash_alloc_intern); /* Arguemnt com is allocated temporary. So when it is not used in hash, it should be freed. */ if (find != com) community_free (com); /* Increment refrence counter. */ find->refcnt++; /* Make string. */ if (! find->str) find->str = community_com2str (find); return find; } /* Free community attribute. */ void community_unintern (struct community *com) { struct community *ret; if (com->refcnt) com->refcnt--; /* Pull off from hash. */ if (com->refcnt == 0) { /* Community value com must exist in hash. */ ret = (struct community *) hash_release (comhash, com); assert (ret != NULL); community_free (com); } } /* Create new community attribute. */ struct community * community_parse (u_int32_t *pnt, u_short length) { struct community tmp; struct community *new; /* If length is malformed return NULL. */ if (length % 4) return NULL; /* Make temporary community for hash look up. */ tmp.size = length / 4; tmp.val = pnt; new = community_uniq_sort (&tmp); return community_intern (new); } struct community * community_dup (struct community *com) { struct community *new; new = malloc(sizeof (struct community)); new->size = com->size; if (new->size) { new->val = malloc(com->size * 4); memcpy (new->val, com->val, com->size * 4); } else new->val = NULL; return new; } /* Retrun string representation of communities attribute. */ char * community_str (struct community *com) { if (!com) return NULL; if (! com->str) com->str = community_com2str (com); return com->str; } /* Make hash value of community attribute. This function is used by hash package.*/ unsigned int community_hash_make (struct community *com) { int c; unsigned int key; unsigned char *pnt; key = 0; pnt = (unsigned char *)com->val; for(c = 0; c < com->size * 4; c++) key += pnt[c]; return key; } int community_match (const struct community *com1, const struct community *com2) { int i = 0; int j = 0; if (com1 == NULL && com2 == NULL) return 1; if (com1 == NULL || com2 == NULL) return 0; if (com1->size < com2->size) return 0; /* Every community on com2 needs to be on com1 for this to match */ while (i < com1->size && j < com2->size) { if (memcmp (com1->val + i, com2->val + j, sizeof (u_int32_t)) == 0) j++; i++; } if (j == com2->size) return 1; else return 0; } /* If two aspath have same value then return 1 else return 0. This function is used by hash package. */ int community_cmp (const struct community *com1, const struct community *com2) { if (com1 == NULL && com2 == NULL) return 1; if (com1 == NULL || com2 == NULL) return 0; if (com1->size == com2->size) if (memcmp (com1->val, com2->val, com1->size * 4) == 0) return 1; return 0; } /* Add com2 to the end of com1. */ struct community * community_merge (struct community *com1, struct community *com2) { if (com1->val) com1->val = realloc(com1->val, (com1->size + com2->size) * 4); else com1->val = malloc((com1->size + com2->size) * 4); memcpy (com1->val + com1->size, com2->val, com2->size * 4); com1->size += com2->size; return com1; } /* Community token enum. */ enum community_token { community_token_val, community_token_no_export, community_token_no_advertise, community_token_local_as, community_token_unknown }; /* Get next community token from string. */ static const char * community_gettoken (const char *buf, enum community_token *token, u_int32_t *val) { const char *p = buf; /* Skip white space. */ while (isspace ((int) *p)) p++; /* Check the end of the line. */ if (*p == '\0') return NULL; /* Well known community string check. */ if (isalpha ((int) *p)) { if (strncmp (p, "internet", strlen ("internet")) == 0) { *val = COMMUNITY_INTERNET; *token = community_token_no_export; p += strlen ("internet"); return p; } if (strncmp (p, "no-export", strlen ("no-export")) == 0) { *val = COMMUNITY_NO_EXPORT; *token = community_token_no_export; p += strlen ("no-export"); return p; } if (strncmp (p, "no-advertise", strlen ("no-advertise")) == 0) { *val = COMMUNITY_NO_ADVERTISE; *token = community_token_no_advertise; p += strlen ("no-advertise"); return p; } if (strncmp (p, "local-AS", strlen ("local-AS")) == 0) { *val = COMMUNITY_LOCAL_AS; *token = community_token_local_as; p += strlen ("local-AS"); return p; } /* Unknown string. */ *token = community_token_unknown; return NULL; } /* Community value. */ if (isdigit ((int) *p)) { int separator = 0; int digit = 0; u_int32_t community_low = 0; u_int32_t community_high = 0; while (isdigit ((int) *p) || *p == ':') { if (*p == ':') { if (separator) { *token = community_token_unknown; return NULL; } else { separator = 1; digit = 0; community_high = community_low << 16; community_low = 0; } } else { digit = 1; community_low *= 10; community_low += (*p - '0'); } p++; } if (! digit) { *token = community_token_unknown; return NULL; } *val = community_high + community_low; *token = community_token_val; return p; } *token = community_token_unknown; return NULL; } /* convert string to community structure */ struct community * community_str2com (const char *str) { struct community *com = NULL; struct community *com_sort = NULL; u_int32_t val = 0; enum community_token token = community_token_unknown; do { str = community_gettoken (str, &token, &val); switch (token) { case community_token_val: case community_token_no_export: case community_token_no_advertise: case community_token_local_as: if (com == NULL) com = community_new(); community_add_val (com, val); break; case community_token_unknown: default: if (com) community_free (com); return NULL; } } while (str); if (! com) return NULL; com_sort = community_uniq_sort (com); community_free (com); return com_sort; } /* Return communities hash entry count. */ unsigned long community_count () { return comhash->count; } /* Return communities hash. */ struct hash * community_hash (void) { return comhash; } /* Initialize comminity related hash. */ void community_init () { comhash = hash_create ((unsigned int (*) (void *))community_hash_make, (int (*) (const void *, const void *))community_cmp); } pmacct-0.14.0/src/bpf_filter.c0000644000175000017500000002674110530072467015136 0ustar paolopaolo/*- * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * * This code is derived from the Stanford/CMU enet packet filter, * (net/enet.c) distributed as part of 4.3BSD, and code contributed * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence * Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)bpf.c 7.5 (Berkeley) 7/15/91 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef WIN32 #include #else /* WIN32 */ #include #include #include #define SOLARIS (defined(sun) && (defined(__SVR4) || defined(__svr4__))) #if defined(__hpux) || SOLARIS # include # include # define mbuf msgb # define m_next b_cont # define MLEN(m) ((m)->b_wptr - (m)->b_rptr) # define mtod(m,t) ((t)(m)->b_rptr) #else # define MLEN(m) ((m)->m_len) #endif #endif /* WIN32 */ /* #include */ #include "pmacct-bpf.h" #if !defined(KERNEL) && !defined(_KERNEL) #include #endif #define int32 bpf_int32 #define u_int32 bpf_u_int32 #ifndef LBL_ALIGN /* * XXX - IA-64? If not, this probably won't work on Win64 IA-64 * systems, unless LBL_ALIGN is defined elsewhere for them. * XXX - SuperH? If not, this probably won't work on WinCE SuperH * systems, unless LBL_ALIGN is defined elsewhere for them. */ #if defined(sparc) || defined(__sparc__) || defined(mips) || \ defined(ibm032) || defined(__alpha) || defined(__hpux) || \ defined(__arm__) #define LBL_ALIGN #endif #endif #ifndef LBL_ALIGN #ifndef WIN32 #include #endif #define EXTRACT_SHORT(p) ((u_short)ntohs(*(u_short *)p)) #define EXTRACT_LONG(p) (ntohl(*(u_int32 *)p)) #else #define EXTRACT_SHORT(p)\ ((u_short)\ ((u_short)*((u_char *)p+0)<<8|\ (u_short)*((u_char *)p+1)<<0)) #define EXTRACT_LONG(p)\ ((u_int32)*((u_char *)p+0)<<24|\ (u_int32)*((u_char *)p+1)<<16|\ (u_int32)*((u_char *)p+2)<<8|\ (u_int32)*((u_char *)p+3)<<0) #endif #if defined(KERNEL) || defined(_KERNEL) # if !defined(__hpux) && !SOLARIS #include # endif #define MINDEX(len, _m, _k) \ { \ len = MLEN(m); \ while ((_k) >= len) { \ (_k) -= len; \ (_m) = (_m)->m_next; \ if ((_m) == 0) \ return 0; \ len = MLEN(m); \ } \ } static int m_xword(m, k, err) register struct mbuf *m; register int k, *err; { register int len; register u_char *cp, *np; register struct mbuf *m0; MINDEX(len, m, k); cp = mtod(m, u_char *) + k; if (len - k >= 4) { *err = 0; return EXTRACT_LONG(cp); } m0 = m->m_next; if (m0 == 0 || MLEN(m0) + len - k < 4) goto bad; *err = 0; np = mtod(m0, u_char *); switch (len - k) { case 1: return (cp[0] << 24) | (np[0] << 16) | (np[1] << 8) | np[2]; case 2: return (cp[0] << 24) | (cp[1] << 16) | (np[0] << 8) | np[1]; default: return (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | np[0]; } bad: *err = 1; return 0; } static int m_xhalf(m, k, err) register struct mbuf *m; register int k, *err; { register int len; register u_char *cp; register struct mbuf *m0; MINDEX(len, m, k); cp = mtod(m, u_char *) + k; if (len - k >= 2) { *err = 0; return EXTRACT_SHORT(cp); } m0 = m->m_next; if (m0 == 0) goto bad; *err = 0; return (cp[0] << 8) | mtod(m0, u_char *)[0]; bad: *err = 1; return 0; } #endif /* * Execute the filter program starting at pc on the packet p * wirelen is the length of the original packet * buflen is the amount of data present * For the kernel, p is assumed to be a pointer to an mbuf if buflen is 0, * in all other cases, p is a pointer to a buffer and buflen is its size. */ u_int bpf_filter(pc, p, wirelen, buflen) register struct bpf_insn *pc; register u_char *p; u_int wirelen; register u_int buflen; { register u_int32 A, X; register int k; int32 mem[BPF_MEMWORDS]; #if defined(KERNEL) || defined(_KERNEL) struct mbuf *m, *n; int merr, len; if (buflen == 0) { m = (struct mbuf *)p; p = mtod(m, u_char *); buflen = MLEN(m); } else m = NULL; #endif if (pc == 0) /* * No filter means accept all. */ return (u_int)-1; A = 0; X = 0; --pc; while (1) { ++pc; switch (pc->code) { default: #if defined(KERNEL) || defined(_KERNEL) return 0; #else abort(); #endif case BPF_RET|BPF_K: return (u_int)pc->k; case BPF_RET|BPF_A: return (u_int)A; case BPF_LD|BPF_W|BPF_ABS: k = pc->k; if (k + sizeof(int32) > buflen) { #if defined(KERNEL) || defined(_KERNEL) if (m == NULL) return 0; A = m_xword(m, k, &merr); if (merr != 0) return 0; continue; #else return 0; #endif } A = EXTRACT_LONG(&p[k]); continue; case BPF_LD|BPF_H|BPF_ABS: k = pc->k; if (k + sizeof(short) > buflen) { #if defined(KERNEL) || defined(_KERNEL) if (m == NULL) return 0; A = m_xhalf(m, k, &merr); if (merr != 0) return 0; continue; #else return 0; #endif } A = EXTRACT_SHORT(&p[k]); continue; case BPF_LD|BPF_B|BPF_ABS: k = pc->k; if (k >= buflen) { #if defined(KERNEL) || defined(_KERNEL) if (m == NULL) return 0; n = m; MINDEX(len, n, k); A = mtod(n, u_char *)[k]; continue; #else return 0; #endif } A = p[k]; continue; case BPF_LD|BPF_W|BPF_LEN: A = wirelen; continue; case BPF_LDX|BPF_W|BPF_LEN: X = wirelen; continue; case BPF_LD|BPF_W|BPF_IND: k = X + pc->k; if (k + sizeof(int32) > buflen) { #if defined(KERNEL) || defined(_KERNEL) if (m == NULL) return 0; A = m_xword(m, k, &merr); if (merr != 0) return 0; continue; #else return 0; #endif } A = EXTRACT_LONG(&p[k]); continue; case BPF_LD|BPF_H|BPF_IND: k = X + pc->k; if (k + sizeof(short) > buflen) { #if defined(KERNEL) || defined(_KERNEL) if (m == NULL) return 0; A = m_xhalf(m, k, &merr); if (merr != 0) return 0; continue; #else return 0; #endif } A = EXTRACT_SHORT(&p[k]); continue; case BPF_LD|BPF_B|BPF_IND: k = X + pc->k; if (k >= buflen) { #if defined(KERNEL) || defined(_KERNEL) if (m == NULL) return 0; n = m; MINDEX(len, n, k); A = mtod(n, u_char *)[k]; continue; #else return 0; #endif } A = p[k]; continue; case BPF_LDX|BPF_MSH|BPF_B: k = pc->k; if (k >= buflen) { #if defined(KERNEL) || defined(_KERNEL) if (m == NULL) return 0; n = m; MINDEX(len, n, k); X = (mtod(n, char *)[k] & 0xf) << 2; continue; #else return 0; #endif } X = (p[pc->k] & 0xf) << 2; continue; case BPF_LD|BPF_IMM: A = pc->k; continue; case BPF_LDX|BPF_IMM: X = pc->k; continue; case BPF_LD|BPF_MEM: A = mem[pc->k]; continue; case BPF_LDX|BPF_MEM: X = mem[pc->k]; continue; case BPF_ST: mem[pc->k] = A; continue; case BPF_STX: mem[pc->k] = X; continue; case BPF_JMP|BPF_JA: pc += pc->k; continue; case BPF_JMP|BPF_JGT|BPF_K: pc += (A > pc->k) ? pc->jt : pc->jf; continue; case BPF_JMP|BPF_JGE|BPF_K: pc += (A >= pc->k) ? pc->jt : pc->jf; continue; case BPF_JMP|BPF_JEQ|BPF_K: pc += (A == pc->k) ? pc->jt : pc->jf; continue; case BPF_JMP|BPF_JSET|BPF_K: pc += (A & pc->k) ? pc->jt : pc->jf; continue; case BPF_JMP|BPF_JGT|BPF_X: pc += (A > X) ? pc->jt : pc->jf; continue; case BPF_JMP|BPF_JGE|BPF_X: pc += (A >= X) ? pc->jt : pc->jf; continue; case BPF_JMP|BPF_JEQ|BPF_X: pc += (A == X) ? pc->jt : pc->jf; continue; case BPF_JMP|BPF_JSET|BPF_X: pc += (A & X) ? pc->jt : pc->jf; continue; case BPF_ALU|BPF_ADD|BPF_X: A += X; continue; case BPF_ALU|BPF_SUB|BPF_X: A -= X; continue; case BPF_ALU|BPF_MUL|BPF_X: A *= X; continue; case BPF_ALU|BPF_DIV|BPF_X: if (X == 0) return 0; A /= X; continue; case BPF_ALU|BPF_AND|BPF_X: A &= X; continue; case BPF_ALU|BPF_OR|BPF_X: A |= X; continue; case BPF_ALU|BPF_LSH|BPF_X: A <<= X; continue; case BPF_ALU|BPF_RSH|BPF_X: A >>= X; continue; case BPF_ALU|BPF_ADD|BPF_K: A += pc->k; continue; case BPF_ALU|BPF_SUB|BPF_K: A -= pc->k; continue; case BPF_ALU|BPF_MUL|BPF_K: A *= pc->k; continue; case BPF_ALU|BPF_DIV|BPF_K: A /= pc->k; continue; case BPF_ALU|BPF_AND|BPF_K: A &= pc->k; continue; case BPF_ALU|BPF_OR|BPF_K: A |= pc->k; continue; case BPF_ALU|BPF_LSH|BPF_K: A <<= pc->k; continue; case BPF_ALU|BPF_RSH|BPF_K: A >>= pc->k; continue; case BPF_ALU|BPF_NEG: A = -A; continue; case BPF_MISC|BPF_TAX: X = A; continue; case BPF_MISC|BPF_TXA: A = X; continue; } } } /* * Return true if the 'fcode' is a valid filter program. * The constraints are that each jump be forward and to a valid * code. The code must terminate with either an accept or reject. * 'valid' is an array for use by the routine (it must be at least * 'len' bytes long). * * The kernel needs to be able to verify an application's filter code. * Otherwise, a bogus program could easily crash the system. */ int bpf_validate(f, len) struct bpf_insn *f; int len; { register int i; register struct bpf_insn *p; for (i = 0; i < len; ++i) { /* * Check that that jumps are forward, and within * the code block. */ p = &f[i]; if (BPF_CLASS(p->code) == BPF_JMP) { register int from = i + 1; if (BPF_OP(p->code) == BPF_JA) { if (from + p->k >= (unsigned)len) return 0; } else if (from + p->jt >= len || from + p->jf >= len) return 0; } /* * Check that memory operations use valid addresses. */ if ((BPF_CLASS(p->code) == BPF_ST || (BPF_CLASS(p->code) == BPF_LD && (p->code & 0xe0) == BPF_MEM)) && (p->k >= BPF_MEMWORDS || p->k < 0)) return 0; /* * Check for constant division by 0. */ if (p->code == (BPF_ALU|BPF_DIV|BPF_K) && p->k == 0) return 0; } return BPF_CLASS(f[len - 1].code) == BPF_RET; } pmacct-0.14.0/src/nfacctd.h0000644000175000017500000006417311712352254014430 0ustar paolopaolo/* Netflow stuff */ /* NetFlow Export Version 1 Header Format */ struct struct_header_v1 { u_int16_t version; /* Current version = 1 */ u_int16_t count; /* The number of records in PDU. */ u_int32_t SysUptime; /* Current time in msecs since router booted */ u_int32_t unix_secs; /* Current seconds since 0000 UTC 1970 */ u_int32_t unix_nsecs; /* Residual nanoseconds since 0000 UTC 1970 */ }; /* NetFlow Export Version 5 Header Format */ struct struct_header_v5 { u_int16_t version; /* Version = 5 */ u_int16_t count; /* The number of records in PDU. */ u_int32_t SysUptime; /* Current time in msecs since router booted */ u_int32_t unix_secs; /* Current seconds since 0000 UTC 1970 */ u_int32_t unix_nsecs; /* Residual nanoseconds since 0000 UTC 1970 */ u_int32_t flow_sequence; /* Sequence number of total flows seen */ unsigned char engine_type; /* Type of flow switching engine (RP,VIP,etc.) */ unsigned char engine_id; /* Slot number of the flow switching engine */ u_int16_t sampling; }; /* NetFlow Export Version 7 Header Format */ struct struct_header_v7 { u_int16_t version; /* Version = 7 */ u_int16_t count; /* The number of records in the PDU */ u_int32_t SysUptime; /* Current time in millisecs since router booted */ u_int32_t unix_secs; /* Current seconds since 0000 UTC 1970 */ u_int32_t unix_nsecs; /* Residual nanoseconds since 0000 UTC 1970 */ u_int32_t flow_sequence; /* Seq counter of total flows seen */ u_int8_t engine_type; /* Type of flow switching engine (RP,VIP,etc.) */ u_int8_t engine_id; /* Slot number of the flow switching engine */ u_int16_t reserved; }; /* NetFlow Export Version 8 Header Format */ struct struct_header_v8 { u_int16_t version; /* Version = 8 */ u_int16_t count; /* The number of records in the PDU */ u_int32_t SysUptime; /* Current time in millisecs since router booted */ u_int32_t unix_secs; /* Current seconds since 0000 UTC 1970 */ u_int32_t unix_nsecs; /* Residual nanoseconds since 0000 UTC 1970 */ u_int32_t flow_sequence; /* Seq counter of total flows seen */ unsigned char engine_type; /* Type of flow switching engine (RP,VIP,etc.) */ unsigned char engine_id; /* Slot number of the flow switching engine */ u_int8_t aggregation; /* Aggregation method being used */ u_int8_t agg_version; /* Version of the aggregation export */ u_int32_t reserved; }; /* NetFlow Export Version 9 Header Format */ struct struct_header_v9 { u_int16_t version; /* version = 9 */ u_int16_t count; /* The number of records in PDU. */ u_int32_t SysUptime; /* Current time in msecs since router booted */ u_int32_t unix_secs; /* Current seconds since 0000 UTC 1970 */ u_int32_t flow_sequence; /* Sequence number of total flows seen */ u_int32_t source_id; /* Source id */ }; struct struct_header_ipfix { u_int16_t version; /* version = 10 */ u_int16_t len; /* Total length of the IPFIX Message */ u_int32_t unix_secs; /* Current seconds since 0000 UTC 1970 */ u_int32_t flow_sequence; /* Sequence number of total flows seen */ u_int32_t source_id; /* Source id */ }; /* NetFlow Export version 1 */ struct struct_export_v1 { struct in_addr srcaddr; /* Source IP Address */ struct in_addr dstaddr; /* Destination IP Address */ struct in_addr nexthop; /* Next hop router's IP Address */ u_int16_t input; /* Input interface index */ u_int16_t output; /* Output interface index */ u_int32_t dPkts; /* Packets sent in Duration (milliseconds between 1st & last packet in this flow)*/ u_int32_t dOctets; /* Octets sent in Duration (milliseconds between 1st & last packet in this flow)*/ u_int32_t First; /* SysUptime at start of flow */ u_int32_t Last; /* and of last packet of the flow */ u_int16_t srcport; /* TCP/UDP source port number (.e.g, FTP, Telnet, etc.,or equivalent) */ u_int16_t dstport; /* TCP/UDP destination port number (.e.g, FTP, Telnet, etc.,or equivalent) */ u_int16_t pad; /* pad to word boundary */ unsigned char prot; /* IP protocol, e.g., 6=TCP, 17=UDP, etc... */ unsigned char tos; /* IP Type-of-Service */ unsigned char pad_2[8]; /* pad to word boundary */ }; /* NetFlow Export version 5 */ struct struct_export_v5 { struct in_addr srcaddr; /* Source IP Address */ struct in_addr dstaddr; /* Destination IP Address */ struct in_addr nexthop; /* Next hop router's IP Address */ u_int16_t input; /* Input interface index */ u_int16_t output; /* Output interface index */ u_int32_t dPkts; /* Packets sent in Duration (milliseconds between 1st & last packet in this flow) */ u_int32_t dOctets; /* Octets sent in Duration (milliseconds between 1st & last packet in this flow) */ u_int32_t First; /* SysUptime at start of flow */ u_int32_t Last; /* and of last packet of the flow */ u_int16_t srcport; /* TCP/UDP source port number (.e.g, FTP, Telnet, etc.,or equivalent) */ u_int16_t dstport; /* TCP/UDP destination port number (.e.g, FTP, Telnet, etc.,or equivalent) */ unsigned char pad; /* pad to word boundary */ unsigned char tcp_flags; /* Cumulative OR of tcp flags */ unsigned char prot; /* IP protocol, e.g., 6=TCP, 17=UDP, etc... */ unsigned char tos; /* IP Type-of-Service */ u_int16_t src_as; /* source peer/origin Autonomous System */ u_int16_t dst_as; /* dst peer/origin Autonomous System */ unsigned char src_mask; /* source route's mask bits */ unsigned char dst_mask; /* destination route's mask bits */ u_int16_t pad_1; /* pad to word boundary */ }; /* NetFlow Export version 7 */ struct struct_export_v7 { u_int32_t srcaddr; /* Source IP Address */ u_int32_t dstaddr; /* Destination IP Address */ u_int32_t nexthop; /* Next hop router's IP Address */ u_int16_t input; /* Input interface index */ u_int16_t output; /* Output interface index */ u_int32_t dPkts; /* Packets sent in Duration */ u_int32_t dOctets; /* Octets sent in Duration. */ u_int32_t First; /* SysUptime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int16_t srcport; /* TCP/UDP source port number or equivalent */ u_int16_t dstport; /* TCP/UDP destination port number or equiv */ u_int8_t pad; u_int8_t tcp_flags; /* Cumulative OR of tcp flags */ u_int8_t prot; /* IP protocol, e.g., 6=TCP, 17=UDP, ... */ u_int8_t tos; /* IP Type-of-Service */ u_int16_t src_as; /* originating AS of source address */ u_int16_t dst_as; /* originating AS of destination address */ u_int8_t src_mask; /* source address prefix mask bits */ u_int8_t dst_mask; /* destination address prefix mask bits */ u_int16_t drops; u_int32_t router_sc; /* Router which is shortcut by switch */ }; struct struct_export_v8_1 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int16_t src_as; /* originating AS of source address */ u_int16_t dst_as; /* originating AS of destination address */ u_int16_t input; /* input interface index */ u_int16_t output; /* output interface index */ }; struct struct_export_v8_2 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int8_t prot; /* IP protocol */ u_int8_t pad; u_int16_t reserved; u_int16_t srcport; /* TCP/UDP source port number of equivalent */ u_int16_t dstport; /* TCP/UDP dst port number of equivalent */ }; struct struct_export_v8_3 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int32_t src_prefix; u_int8_t src_mask; u_int8_t pad; u_int16_t src_as; u_int16_t input; u_int16_t reserved; }; struct struct_export_v8_4 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int32_t dst_prefix; u_int8_t dst_mask; u_int8_t pad; u_int16_t dst_as; u_int16_t output; u_int16_t reserved; }; struct struct_export_v8_5 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int32_t src_prefix; u_int32_t dst_prefix; u_int8_t dst_mask; u_int8_t src_mask; u_int16_t reserved; u_int16_t src_as; u_int16_t dst_as; u_int16_t input; u_int16_t output; }; struct struct_export_v8_6 { u_int32_t dstaddr; /* destination IP address */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int16_t output; /* output interface index */ u_int8_t tos; /* tos */ u_int8_t marked_tos; /* tos of pkts that exceeded the contract */ u_int32_t extra_pkts; /* packets that exceed the contract */ u_int32_t router_sc; /* IP address of the router being shortcut */ }; struct struct_export_v8_7 { u_int32_t dstaddr; /* destination IP address */ u_int32_t srcaddr; /* source address */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int16_t output; /* output interface index */ u_int16_t input; /* input interface index */ u_int8_t tos; /* tos */ u_int8_t marked_tos; /* tos of pkts that exceeded the contract */ u_int16_t reserved; u_int32_t extra_pkts; /* packets that exceed the contract */ u_int32_t router_sc; /* IP address of the router being shortcut */ }; struct struct_export_v8_8 { u_int32_t dstaddr; /* destination IP address */ u_int32_t srcaddr; /* source IP address */ u_int16_t dstport; /* TCP/UDP destination port */ u_int16_t srcport; /* TCP/UDP source port */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int16_t output; /* output interface index */ u_int16_t input; /* input interface index */ u_int8_t tos; /* tos */ u_int8_t prot; /* protocol */ u_int8_t marked_tos; /* tos of pkts that exceeded the contract */ u_int8_t reserved; u_int32_t extra_pkts; /* packets that exceed the contract */ u_int32_t router_sc; /* IP address of the router being shortcut */ }; struct struct_export_v8_9 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int16_t src_as; /* originating AS of source address */ u_int16_t dst_as; /* originating AS of destination address */ u_int16_t input; /* input interface index */ u_int16_t output; /* output interface index */ u_int8_t tos; /* tos */ u_int8_t pad; u_int16_t reserved; }; struct struct_export_v8_10 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int8_t prot; /* IP protocol */ u_int8_t tos; /* tos */ u_int16_t reserved; u_int16_t srcport; /* TCP/UDP source port number of equivalent */ u_int16_t dstport; /* TCP/UDP dst port number of equivalent */ u_int16_t input; /* input interface */ u_int16_t output; /* output interface index */ }; struct struct_export_v8_11 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int32_t src_prefix; /* Source Prefix */ u_int8_t src_mask; /* Source Prefix mask length */ u_int8_t tos; /* tos */ u_int16_t src_as; /* Source AS */ u_int16_t input; /* input interface */ u_int16_t reserved; }; struct struct_export_v8_12 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int32_t dst_prefix; /* Destination Prefix */ u_int8_t dst_mask; /* Destination Prefix mask length */ u_int8_t tos; /* tos */ u_int16_t dst_as; /* Destination AS */ u_int16_t output; /* output interface */ u_int16_t reserved; }; struct struct_export_v8_13 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int32_t src_prefix; /* Source Prefix */ u_int32_t dst_prefix; /* Destination Prefix */ u_int8_t dst_mask; /* Destination Prefix mask length */ u_int8_t src_mask; /* Source Prefix mask length */ u_int8_t tos; /* tos */ u_int8_t pad; u_int16_t src_as; /* Source AS */ u_int16_t dst_as; /* Destination AS */ u_int16_t input; /* input interface */ u_int16_t output; /* output interface */ }; struct struct_export_v8_14 { u_int32_t dFlows; /* Number of flows */ u_int32_t dPkts; /* Packets sent in duration */ u_int32_t dOctets; /* Octets sent in duration */ u_int32_t First; /* SysUpTime at start of flow */ u_int32_t Last; /* and of last packet of flow */ u_int32_t src_prefix; /* Source Prefix */ u_int32_t dst_prefix; /* Destination Prefix */ u_int8_t dst_mask; /* Destination Prefix mask length */ u_int8_t src_mask; /* Source Prefix mask length */ u_int8_t tos; /* tos */ u_int8_t prot; /* protocol */ u_int16_t srcport; /* Source port */ u_int16_t dstport; /* Destination port */ u_int16_t input; /* input interface */ u_int16_t output; /* output interface */ }; /* NetFlow Export version 9 */ struct template_field_v9 { u_int16_t type; u_int16_t len; }; struct template_hdr_v9 { u_int16_t template_id; u_int16_t num; }; struct options_template_hdr_v9 { u_int16_t template_id; u_int16_t scope_len; u_int16_t option_len; }; /* IPFIX: option field count and scope field count apparently inverted compared to NetFlow v9 */ struct options_template_hdr_ipfix { u_int16_t template_id; u_int16_t option_count; u_int16_t scope_count; }; struct data_hdr_v9 { u_int16_t flow_id; /* == 0: template; == 1: options template; >= 256: data */ u_int16_t flow_len; }; /* defines */ #define DEFAULT_NFACCTD_PORT 2100 #define NETFLOW_MSG_SIZE 1550 #define V1_MAXFLOWS 24 /* max records in V1 packet */ #define V5_MAXFLOWS 30 /* max records in V5 packet */ #define V7_MAXFLOWS 27 /* max records in V7 packet */ #define V8_1_MAXFLOWS 51 /* max records in V8 AS packet */ #define V8_2_MAXFLOWS 51 /* max records in V8 PROTO PORT packet */ #define V8_3_MAXFLOWS 44 /* max records in V8 SRC PREFIX packet */ #define V8_4_MAXFLOWS 44 /* max records in V8 DST PREFIX packet */ #define V8_5_MAXFLOWS 35 /* max records in V8 PREFIX packet */ #define V8_6_MAXFLOWS 44 /* max records in V8 DESTONLY packet */ #define V8_7_MAXFLOWS 35 /* max records in V8 SRC_DEST packet */ #define V8_8_MAXFLOWS 32 /* max records in V8 FULL_FLOW packet */ #define V8_9_MAXFLOWS 44 /* max records in V8 AS_TOS packet */ #define V8_10_MAXFLOWS 44 /* max records in V8 PROT_PORT_TOS packet */ #define V8_11_MAXFLOWS 44 /* max records in V8 SRC_PREFIX_TOS packet */ #define V8_12_MAXFLOWS 44 /* max records in V8 DST_PREFIX_TOS packet */ #define V8_13_MAXFLOWS 35 /* max records in V8 PREFIX_TOS packet */ #define V8_14_MAXFLOWS 35 /* max records in V8 PREFIX_PORT_TOS packet */ #define TEMPLATE_CACHE_ENTRIES 255 #define NF_TIME_MSECS 0 /* times are in msecs */ #define NF_TIME_SECS 1 /* times are in secs */ #define NF_TIME_NEW 2 /* ignore netflow engine times and generate new ones */ #define IPFIX_TPL_EBIT 0x8000 /* IPFIX telmplate enterprise bit */ /* NetFlow V9 stuff */ #define NF9_TEMPLATE_FLOWSET_ID 0 #define NF9_OPTIONS_FLOWSET_ID 1 #define NF9_MIN_RECORD_FLOWSET_ID 256 #define NF9_MAX_DEFINED_FIELD 384 /* Flowset record types the we care about */ #define NF9_IN_BYTES 1 #define NF9_IN_PACKETS 2 #define NF9_FLOWS 3 #define NF9_L4_PROTOCOL 4 #define NF9_SRC_TOS 5 #define NF9_TCP_FLAGS 6 #define NF9_L4_SRC_PORT 7 #define NF9_IPV4_SRC_ADDR 8 #define NF9_SRC_MASK 9 #define NF9_INPUT_SNMP 10 #define NF9_L4_DST_PORT 11 #define NF9_IPV4_DST_ADDR 12 #define NF9_DST_MASK 13 #define NF9_OUTPUT_SNMP 14 #define NF9_IPV4_NEXT_HOP 15 #define NF9_SRC_AS 16 #define NF9_DST_AS 17 #define NF9_BGP_IPV4_NEXT_HOP 18 #define NF9_MUL_DST_PKTS 19 #define NF9_MUL_DST_BYTES 20 /* ... */ #define NF9_LAST_SWITCHED 21 #define NF9_FIRST_SWITCHED 22 #define NF9_OUT_BYTES 23 #define NF9_OUT_PACKETS 24 /* ... */ #define NF9_IPV6_SRC_ADDR 27 #define NF9_IPV6_DST_ADDR 28 #define NF9_IPV6_SRC_MASK 29 #define NF9_IPV6_DST_MASK 30 #define NF9_ICMP_TYPE 32 /* ... */ #define NF9_ENGINE_TYPE 38 #define NF9_ENGINE_ID 39 /* ... */ #define NF9_IN_SRC_MAC 56 #define NF9_OUT_DST_MAC 57 #define NF9_IN_VLAN 58 #define NF9_OUT_VLAN 59 #define NF9_IP_PROTOCOL_VERSION 60 #define NF9_DIRECTION 61 #define NF9_IPV6_NEXT_HOP 62 #define NF9_BGP_IPV6_NEXT_HOP 63 /* ... */ #define NF9_MPLS_LABEL_1 70 #define NF9_MPLS_LABEL_2 71 #define NF9_MPLS_LABEL_3 72 #define NF9_MPLS_LABEL_4 73 #define NF9_MPLS_LABEL_5 74 #define NF9_MPLS_LABEL_6 75 #define NF9_MPLS_LABEL_7 76 #define NF9_MPLS_LABEL_8 77 #define NF9_MPLS_LABEL_9 78 #define NF9_MPLS_LABEL_10 79 #define NF9_IN_DST_MAC 80 #define NF9_OUT_SRC_MAC 81 /* ... */ #define NF9_FLOW_BYTES 85 #define NF9_FLOW_PACKETS 86 /* ... */ #define NF9_PEER_SRC_AS 128 #define NF9_PEER_DST_AS 129 #define NF9_EXPORTER_IPV4_ADDRESS 130 #define NF9_EXPORTER_IPV6_ADDRESS 131 /* ... */ #define NF9_FIRST_SWITCHED_SEC 150 #define NF9_LAST_SWITCHED_SEC 151 #define NF9_FIRST_SWITCHED_MSEC 152 #define NF9_LAST_SWITCHED_MSEC 153 /* ... */ #define NF9_UDP_SRC_PORT 180 #define NF9_UDP_DST_PORT 181 #define NF9_TCP_SRC_PORT 182 #define NF9_TCP_DST_PORT 183 /* ... */ #define NF9_CUST_TAG 201 #define NF9_CUST_TAG2 202 /* ... */ #define NF9_XLATE_IPV4_SRC_ADDR 225 #define NF9_XLATE_IPV4_DST_ADDR 226 #define NF9_XLATE_L4_SRC_PORT 227 #define NF9_XLATE_L4_DST_PORT 228 /* ... */ #define NF9_ETHERTYPE 256 /* ... */ #define NF9_XLATE_IPV6_SRC_ADDR 281 #define NF9_XLATE_IPV6_DST_ADDR 282 /* ... */ #define NF9_OBSERVATION_TIME_SEC 322 #define NF9_OBSERVATION_TIME_MSEC 323 /* ... */ #define NF9_ASA_XLATE_IPV4_SRC_ADDR 40001 #define NF9_ASA_XLATE_IPV4_DST_ADDR 40002 #define NF9_ASA_XLATE_L4_SRC_PORT 40003 #define NF9_ASA_XLATE_L4_DST_PORT 40004 #define NF9_FTYPE_IPV4 0 #define NF9_FTYPE_IPV6 1 #define NF9_FTYPE_VLAN 5 #define NF9_FTYPE_VLAN_IPV4 5 #define NF9_FTYPE_VLAN_IPV6 6 #define NF9_FTYPE_MPLS 10 #define NF9_FTYPE_MPLS_IPV4 10 #define NF9_FTYPE_MPLS_IPV6 11 #define NF9_FTYPE_VLAN_MPLS 15 #define NF9_FTYPE_VLAN_MPLS_IPV4 15 #define NF9_FTYPE_VLAN_MPLS_IPV6 16 /* Sampling */ #define NF9_SAMPLING_INTERVAL 34 #define NF9_SAMPLING_ALGORITHM 35 #define NF9_FLOW_SAMPLER_ID 48 #define NF9_FLOW_SAMPLER_MODE 49 #define NF9_FLOW_SAMPLER_INTERVAL 50 /* Classification */ #define NF9_APPLICATION_DESC 94 #define NF9_APPLICATION_ID 95 #define NF9_APPLICATION_NAME 96 #define NF9_OPT_SCOPE_SYSTEM 1 #define NF9_OPT_SCOPE_IF 2 #define NF9_OPT_SCOPE_LC 3 #define NF9_OPT_SCOPE_CACHE 4 #define NF9_OPT_SCOPE_TPL 5 #define MAX_TPL_DESC_LIST 81 static char *tpl_desc_list[] = { "", "in bytes", "in packets", "flows", "L4 protocol", "tos", "tcp flags", "L4 src port", "IPv4 src addr", "IPv4 src mask", "input snmp", "L4 dst port", "IPv4 dst addr", "IPv4 dst mask", "output snmp", "IPv4 next hop", "src as", "dst as", "BGP IPv4 next hop", "", "", "last switched", "first switched", "out bytes", "out packets", "", "", "IPv6 src addr", "IPv6 dst addr", "IPv6 src mask", "IPv6 dst mask", "", "icmp type", "", "sampling interval", "sampling algorithm", "", "", "", "", "", "", "", "", "", "", "", "", "sampler ID", "sampler mode", "sampler interval", "", "", "", "", "", "in src mac", "out dst mac", "", "", "ip version", "direction", "IPv6 next hop", "IPv6 BGP next hop", "", "", "", "", "", "", "mpls label 1", "mpls label 2", "mpls label 3", "mpls label 4", "mpls label 5", "mpls label 6", "mpls label 7", "mpls label 8", "mpls label 9", "mpls label 10", "in dst mac", "out src mac" }; #define MAX_OPT_TPL_DESC_LIST 100 static char *opt_tpl_desc_list[] = { "", "scope", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "sampler ID", "sampler algorithm", "sampler interval", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "sampler name", "", "", "", "", "", "", "", "", "", "app desc", "app id", "app name", "", "", "", "" }; /* Ordered Template field */ struct otpl_field { u_int16_t off; u_int16_t len; }; struct template_cache_entry { struct host_addr agent; /* NetFlow Exporter agent */ u_int32_t source_id; /* Exporter Observation Domain */ u_int16_t template_id; /* template ID */ u_int16_t template_type; /* Data = 0, Options = 1 */ u_int16_t num; /* number of fields described into template */ u_int16_t len; /* total length of the described flowset */ struct otpl_field tpl[NF9_MAX_DEFINED_FIELD]; struct template_cache_entry *next; }; struct template_cache { u_int16_t num; struct template_cache_entry *c[TEMPLATE_CACHE_ENTRIES]; }; typedef void (*v8_filter_handler)(struct packet_ptrs *, void *); struct v8_handler_entry { u_int8_t max_flows; u_int8_t exp_size; v8_filter_handler fh; }; /* functions */ #if (!defined __NFACCTD_C) #define EXT extern #else #define EXT #endif EXT void process_v1_packet(unsigned char *, u_int16_t, struct packet_ptrs *, struct plugin_requests *); EXT void process_v5_packet(unsigned char *, u_int16_t, struct packet_ptrs *, struct plugin_requests *); EXT void process_v7_packet(unsigned char *, u_int16_t, struct packet_ptrs *, struct plugin_requests *); EXT void process_v8_packet(unsigned char *, u_int16_t, struct packet_ptrs *, struct plugin_requests *); EXT void process_v9_packet(unsigned char *, u_int16_t, struct packet_ptrs_vector *, struct plugin_requests *, u_int16_t); EXT void process_raw_packet(unsigned char *, u_int16_t, struct packet_ptrs_vector *, struct plugin_requests *); EXT u_int16_t NF_evaluate_flow_type(struct template_cache_entry *, struct packet_ptrs *); EXT u_int16_t NF_evaluate_direction(struct template_cache_entry *, struct packet_ptrs *); EXT void reset_mac(struct packet_ptrs *); EXT void reset_mac_vlan(struct packet_ptrs *); EXT void reset_ip4(struct packet_ptrs *); EXT void reset_ip6(struct packet_ptrs *); EXT void notify_malf_packet(short int, char *, struct sockaddr *); EXT void NF_find_id(struct id_table *, struct packet_ptrs *, pm_id_t *, pm_id_t *); EXT char *nfv578_check_status(struct packet_ptrs *); EXT char *nfv9_check_status(struct packet_ptrs *, u_int32_t, u_int32_t); EXT struct template_cache tpl_cache; EXT struct v8_handler_entry v8_handlers[15]; #undef EXT #if (!defined __NFV9_TEMPLATE_C) #define EXT extern #else #define EXT #endif EXT void handle_template_v9(struct template_hdr_v9 *, struct packet_ptrs *, u_int16_t, u_int32_t, u_int16_t *); EXT struct template_cache_entry *find_template_v9(u_int16_t, struct packet_ptrs *, u_int16_t, u_int32_t); EXT struct template_cache_entry *insert_template_v9(struct template_hdr_v9 *, struct packet_ptrs *, u_int16_t, u_int32_t, u_int16_t *); EXT void refresh_template_v9(struct template_hdr_v9 *, struct template_cache_entry *, struct packet_ptrs *, u_int16_t, u_int32_t, u_int16_t *); EXT void log_template_v9_header(struct template_cache_entry *, struct packet_ptrs *, u_int16_t, u_int32_t); EXT void log_opt_template_v9_field(u_int16_t, u_int16_t, u_int16_t); EXT void log_template_v9_field(u_int16_t, u_int16_t, u_int16_t); EXT void log_template_v9_footer(u_int16_t); EXT struct template_cache_entry *insert_opt_template_v9(void *, struct packet_ptrs *, u_int16_t, u_int32_t); EXT void refresh_opt_template_v9(void *, struct template_cache_entry *, struct packet_ptrs *, u_int16_t, u_int32_t); #undef EXT pmacct-0.14.0/src/network.h0000644000175000017500000003572111735323306014515 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "../include/extract.h" #include "../include/llc.h" #include "../include/sll.h" #include "../include/ieee802_11.h" #include "../include/fddi.h" #if defined ENABLE_IPV6 #include "../include/ip6.h" #include "../include/ah.h" #endif #define min(a,b) ((a)>(b)?(b):(a)) #define ETH_ADDR_LEN 6 /* Octets in one ethernet addr */ #define ETHER_HDRLEN 14 #define ETHERMTU 1500 #define IEEE8021Q_TAGLEN 4 #define IEEE8021AH_LEN 10 #define PPP_TAGLEN 2 #define MAX_MCAST_GROUPS 20 #define ROUTING_SEGMENT_MAX 16 /* 10Mb/s ethernet header */ struct eth_header { u_int8_t ether_dhost[ETH_ADDR_LEN]; /* destination eth addr */ u_int8_t ether_shost[ETH_ADDR_LEN]; /* source ether addr */ u_int16_t ether_type; /* packet type ID field */ }; #define TR_RIF_LENGTH(trp) ((ntohs((trp)->token_rcf) & 0x1f00) >> 8) #define TR_IS_SOURCE_ROUTED(trp) ((trp)->token_shost[0] & 0x80) #define TOKEN_FC_LLC 1 struct token_header { u_int8_t token_ac; u_int8_t token_fc; u_int8_t token_dhost[ETH_ADDR_LEN]; u_int8_t token_shost[ETH_ADDR_LEN]; u_int16_t token_rcf; u_int16_t token_rseg[ROUTING_SEGMENT_MAX]; }; /* Ethernet protocol ID's */ #define ETHERTYPE_IP 0x0800 /* IP */ #define ETHERTYPE_IPV6 0x86dd /* IPv6 */ #define ETHERTYPE_PPPOE 0x8864 /* pppoe (session stage) */ #define ETHERTYPE_8021Q 0x8100 /* 802.1Q */ #define ETHERTYPE_MPLS 0x8847 /* MPLS */ #define ETHERTYPE_MPLS_MULTI 0x8848 /* MPLS */ #define ETHERTYPE_8021AH 0x88A8 /* 802.1ah */ #define ETHERTYPE_ISO 0xFEFE /* OSI */ #define ETHERTYPE_GRE_ISO 0x00FE /* OSI over GRE */ /* PPP protocol definitions */ #define PPP_HDRLEN 4 /* octets for standard ppp header */ #define PPPOE_HDRLEN 6 /* octets for standard pppoe header */ #define PPP_IP 0x0021 /* Internet Protocol */ #define PPP_IPV6 0x0057 /* IPv6 */ #define PPP_MPLS_UCAST 0x0281 /* rfc 3032 */ #define PPP_MPLS_MCAST 0x0283 /* rfc 3022 */ #define PPP_ADDRESS 0xff /* The address byte value */ #define PPP_CONTROL 0x03 /* The control byte value */ /* CHDLC protocol definitions */ #define CHDLC_HDRLEN 4 /* additional protocol definitions */ #ifndef IPPROTO_HOPOPTS #define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */ #endif #ifndef IPPROTO_IPV6 #define IPPROTO_IPV6 41 #endif #ifndef IPPROTO_ROUTING #define IPPROTO_ROUTING 43 /* IPv6 routing header */ #endif #ifndef IPPROTO_FRAGMENT #define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */ #endif #ifndef IPPROTO_ESP #define IPPROTO_ESP 50 /* SIPP Encap Sec. Payload */ #endif #ifndef IPPROTO_AH #define IPPROTO_AH 51 /* SIPP Auth Header */ #endif #ifndef IPPROTO_NONE #define IPPROTO_NONE 59 /* IPv6 no next header */ #endif #ifndef IPPROTO_DSTOPTS #define IPPROTO_DSTOPTS 60 /* IPv6 destination options */ #endif #ifndef IPPROTO_IPCOMP #define IPPROTO_IPCOMP 108 #endif #ifndef IPPROTO_MOBILITY #define IPPROTO_MOBILITY 135 #endif struct my_iphdr { u_int8_t ip_vhl; /* header length, version */ #define IP_V(ip) (((ip)->ip_vhl & 0xf0) >> 4) #define IP_HL(ip) ((ip)->ip_vhl & 0x0f) u_int8_t ip_tos; /* type of service */ u_int16_t ip_len; /* total length */ u_int16_t ip_id; /* identification */ u_int16_t ip_off; /* fragment offset field */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_int8_t ip_ttl; /* time to live */ u_int8_t ip_p; /* protocol */ u_int16_t ip_sum; /* checksum */ struct in_addr ip_src; /* source and destination addresses */ struct in_addr ip_dst; }; typedef u_int32_t tcp_seq; struct my_tcphdr { u_int16_t th_sport; /* source port */ u_int16_t th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ #if defined IM_LITTLE_ENDIAN u_int8_t th_x2:4; /* (unused) */ u_int8_t th_off:4; /* data offset */ #endif #if defined IM_BIG_ENDIAN u_int8_t th_off:4; /* data offset */ u_int8_t th_x2:4; /* (unused) */ #endif u_int8_t th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 u_int16_t th_win; /* window */ u_int16_t th_sum; /* checksum */ u_int16_t th_urp; /* urgent pointer */ }; /* For TCP_MD5SIG socket option. */ #ifndef TCP_MD5SIG_MAXKEYLEN #define TCP_MD5SIG_MAXKEYLEN 80 #endif #ifndef TCP_MD5SIG #define TCP_MD5SIG 14 #endif struct my_tcp_md5sig { struct sockaddr_storage tcpm_addr; /* Address associated. */ u_int16_t __tcpm_pad1; /* Zero. */ u_int16_t tcpm_keylen; /* Key length. */ u_int32_t __tcpm_pad2; /* Zero. */ u_int8_t tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* Key (binary). */ }; struct my_udphdr { u_int16_t uh_sport; /* source port */ u_int16_t uh_dport; /* destination port */ u_int16_t uh_ulen; /* udp length */ u_int16_t uh_sum; /* udp checksum */ }; struct my_icmphdr { u_int8_t type; /* message type */ u_int8_t code; /* type sub-code */ u_int16_t checksum; union { struct { u_int16_t id; u_int16_t sequence; } echo; /* echo datagram */ u_int32_t gateway; /* gateway address */ struct { u_int16_t __unused; u_int16_t mtu; } frag; /* path mtu discovery */ } un; }; struct my_tlhdr { u_int16_t src_port; /* source and destination ports */ u_int16_t dst_port; }; struct my_gtphdr { u_int8_t flags; u_int8_t message; u_int16_t length; }; /* typedefs */ typedef u_int32_t as_t; typedef u_int16_t as16_t; #define RD_TYPE_AS 0 #define RD_TYPE_IP 1 #define RD_TYPE_AS4 2 struct rd_as { u_int16_t type; u_int16_t as; u_int32_t val; }; struct rd_ip { u_int16_t type; struct in_addr ip; u_int16_t val; }; struct rd_as4 { u_int16_t type; as_t as; u_int32_t val; }; /* Picking one of the three structures as rd_t for simplicity */ typedef struct rd_as rd_t; /* class status */ struct class_st { u_int8_t tentatives; struct timeval stamp; /* accumulator timestamp */ u_int32_t ba; /* bytes accumulator */ u_int16_t pa; /* packet accumulator */ u_int8_t fa; /* flow accumulator */ }; struct packet_ptrs { struct pcap_pkthdr *pkthdr; /* ptr to header structure passed by libpcap */ u_char *f_agent; /* ptr to flow export agent */ u_char *f_header; /* ptr to NetFlow packet header */ u_char *f_data; /* ptr to NetFlow data */ u_char *f_tpl; /* ptr to NetFlow V9 template */ u_char *f_status; /* ptr to status table entry */ u_char *idtable; /* ptr to pretag table map */ u_char *bpas_table; /* ptr to bgp_peer_as_src table map */ u_char *blp_table; /* ptr to bgp_src_local_pref table map */ u_char *bmed_table; /* ptr to bgp_src_med table map */ u_char *bta_table; /* ptr to bgp_to_agent table map */ u_char *bitr_table; /* ptr to bgp_iface_to_rd table map */ u_char *sampling_table; /* ptr to sampling_map table map */ u_char *packet_ptr; /* ptr to the whole packet */ u_char *mac_ptr; /* ptr to mac addresses */ u_int16_t l3_proto; /* layer-3 protocol: IPv4, IPv6 */ int (*l3_handler)(register struct packet_ptrs *); /* layer-3 protocol handler */ u_int16_t l4_proto; /* layer-4 protocol */ pm_id_t tag; /* pre tag id */ pm_id_t tag2; /* pre tag id2 */ pm_id_t bpas; /* bgp_peer_as_src */ pm_id_t blp; /* bgp_src_local_pref */ pm_id_t bmed; /* bgp_src_med */ pm_id_t bta; /* bgp_to_agent */ pm_id_t bitr; /* bgp_iface_to_rd */ pm_id_t st; /* sampling_map */ char *bgp_src; /* pointer to bgp_node structure for source prefix, if any */ char *bgp_dst; /* pointer to bgp_node structure for destination prefix, if any */ char *bgp_src_info; /* pointer to bgp_info structure for source prefix, if any */ char *bgp_dst_info; /* pointer to bgp_info structure for destination prefix, if any */ char *bgp_peer; /* record BGP peer's Router-ID */ char *bgp_nexthop_info; /* record bgp_info of BGP next-hop in case of follow-up */ char *igp_src; /* pointer to IGP node structure for source prefix, if any */ char *igp_dst; /* pointer to IGP node structure for destination prefix, if any */ char *igp_src_info; /* pointer to IGP node info structure for source prefix, if any */ char *igp_dst_info; /* pointer to IGP node info structure for destination prefix, if any */ u_int8_t lm_mask_src; /* Longest match for source prefix (network mask bits) */ u_int8_t lm_mask_dst; /* Longest match for destination prefix (network mask bits) */ u_int8_t lm_method_src; /* Longest match for source prefix (method: BGP, IGP, etc.) */ u_int8_t lm_method_dst; /* Longest match for destination prefix (method: BGP, IGP, etc.) */ u_int16_t pf; /* pending fragments or packets */ u_int8_t new_flow; /* pmacctd flows: part of a new flow ? */ u_int8_t tcp_flags; /* pmacctd flows: TCP packet flags; URG, PUSH filtered out */ u_char *vlan_ptr; /* ptr to vlan id */ u_char *mpls_ptr; /* ptr to base MPLS label */ u_char *iph_ptr; /* ptr to ip header */ u_char *tlh_ptr; /* ptr to transport level protocol header */ u_char *payload_ptr; /* classifiers: ptr to packet payload */ pm_class_t class; /* classifiers: class id */ struct class_st cst; /* classifiers: class status */ u_int8_t shadow; /* 0=the packet is being distributed for the 1st time 1=the packet is being distributed for the 2nd+ time */ u_int16_t ifindex_in; /* input ifindex; only used by ULOG for the time being */ u_int16_t ifindex_out; /* output ifindex; only used by ULOG for the time being */ u_int8_t tun_stack; /* tunnelling stack */ u_int8_t tun_layer; /* tunnelling layer count */ u_int32_t sample_type; /* sFlow sample type */ u_int32_t seqno; /* sFlow/NetFlow sequence number */ u_int16_t f_len; /* sFlow/NetFlow payload length */ u_int8_t renormalized; /* Is it renormalized yet ? */ }; struct host_addr { u_int8_t family; union { struct in_addr ipv4; #if defined ENABLE_IPV6 struct in6_addr ipv6; #endif } address; }; struct pkt_primitives { #if defined (HAVE_L2) u_int8_t eth_dhost[ETH_ADDR_LEN]; u_int8_t eth_shost[ETH_ADDR_LEN]; u_int16_t vlan_id; u_int8_t cos; u_int16_t etype; #endif struct host_addr src_ip; struct host_addr dst_ip; u_int8_t src_nmask; u_int8_t dst_nmask; as_t src_as; as_t dst_as; u_int16_t src_port; u_int16_t dst_port; u_int8_t tos; u_int8_t proto; u_int32_t ifindex_in; u_int32_t ifindex_out; pm_id_t id; pm_id_t id2; pm_class_t class; }; struct pkt_data { struct pkt_primitives primitives; pm_counter_t pkt_len; pm_counter_t pkt_num; pm_counter_t flo_num; u_int32_t tcp_flags; /* XXX */ struct timeval time_start; struct timeval time_end; struct class_st cst; }; struct pkt_payload { u_int16_t cap_len; pm_counter_t sample_pool; pm_counter_t pkt_len; pm_counter_t pkt_num; u_int32_t time_start; pm_class_t class; pm_id_t tag; pm_id_t tag2; struct host_addr src_ip; struct host_addr dst_ip; as_t src_as; as_t dst_as; u_int16_t ifindex_in; u_int16_t ifindex_out; u_int8_t src_nmask; u_int8_t dst_nmask; u_int16_t vlan; u_int8_t priority; struct host_addr bgp_next_hop; }; struct pkt_extras { u_int8_t tcp_flags; u_int32_t mpls_top_label; struct host_addr bgp_next_hop; }; #define PKT_MSG_SIZE 1550 struct pkt_msg { struct sockaddr agent; u_int32_t seqno; u_int16_t len; u_char payload[PKT_MSG_SIZE]; u_int16_t pad; }; /* START: BGP section */ #define MAX_BGP_STD_COMMS 96 #define MAX_BGP_EXT_COMMS 96 #define MAX_BGP_ASPATH 128 struct pkt_bgp_primitives { as_t peer_src_as; as_t peer_dst_as; struct host_addr peer_src_ip; struct host_addr peer_dst_ip; char std_comms[MAX_BGP_STD_COMMS]; char ext_comms[MAX_BGP_EXT_COMMS]; char as_path[MAX_BGP_ASPATH]; u_int32_t local_pref; u_int32_t med; char src_std_comms[MAX_BGP_STD_COMMS]; char src_ext_comms[MAX_BGP_EXT_COMMS]; char src_as_path[MAX_BGP_ASPATH]; u_int32_t src_local_pref; u_int32_t src_med; rd_t mpls_vpn_rd; u_int32_t pad; }; /* same as above but pointers in place of strings */ struct cache_bgp_primitives { as_t peer_src_as; as_t peer_dst_as; struct host_addr peer_src_ip; struct host_addr peer_dst_ip; char *std_comms; char *ext_comms; char *as_path; u_int32_t local_pref; u_int32_t med; char *src_std_comms; char *src_ext_comms; char *src_as_path; u_int32_t src_local_pref; u_int32_t src_med; rd_t mpls_vpn_rd; }; /* END: BGP section */ struct packet_ptrs_vector { struct packet_ptrs v4; struct packet_ptrs vlan4; struct packet_ptrs mpls4; struct packet_ptrs vlanmpls4; #if defined ENABLE_IPV6 struct packet_ptrs v6; struct packet_ptrs vlan6; struct packet_ptrs mpls6; struct packet_ptrs vlanmpls6; #endif }; struct hosts_table { short int num; struct host_addr table[MAX_MAP_ENTRIES]; }; struct bgp_md5_table_entry { struct host_addr addr; char key[TCP_MD5SIG_MAXKEYLEN]; }; struct bgp_md5_table { short int num; struct bgp_md5_table_entry table[BGP_MD5_MAP_ENTRIES]; }; #define TUNNEL_PROTO_STRING 16 #define TUNNEL_REGISTRY_STACKS 9 /* MAX + 1 */ #define TUNNEL_REGISTRY_ENTRIES 4 typedef int (*tunnel_func)(register struct packet_ptrs *); struct tunnel_handler { tunnel_func tf; u_int8_t proto; u_int16_t port; }; typedef int (*tunnel_configurator)(struct tunnel_handler *, char *); struct tunnel_entry { u_char type[TUNNEL_PROTO_STRING]; tunnel_func tf; tunnel_configurator tc; }; struct tunnel_handler tunnel_registry[TUNNEL_REGISTRY_STACKS][TUNNEL_REGISTRY_ENTRIES]; pmacct-0.14.0/src/xflow_status.h0000644000175000017500000000743311611553756015574 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2010 by Paolo Lucente */ /* 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. */ /* defines */ #define XFLOW_RESET_BOUNDARY 50 #define XFLOW_STATUS_TABLE_SZ 9973 #define XFLOW_STATUS_TABLE_MAX_ENTRIES 100000 /* structures */ struct xflow_status_entry_counters { u_int32_t good; u_int32_t jumps_f; u_int32_t jumps_b; }; struct xflow_status_entry_sampling { u_int32_t interface; /* sFlow/NetFlow v9: interface generating the sample */ u_int32_t sample_pool; /* sampling rate */ u_int32_t seqno; /* sFlow: flow samples sequence number */ u_int16_t sampler_id; /* NetFlow v9: flow sampler ID field */ struct xflow_status_entry_sampling *next; }; struct xflow_status_entry_class { pm_class_t class_id; /* NetFlow v9: classfier ID field */ pm_class_t class_int_id; /* NetFlow v9: internal classfier ID field */ char class_name[MAX_PROTOCOL_LEN]; /* NetFlow v9: classfier name field */ struct xflow_status_entry_class *next; }; struct xflow_status_entry { struct host_addr agent_addr; /* xFlow agent IP address */ u_int32_t seqno; /* Sequence number */ u_int32_t aux1; /* Some more distinguishing fields: NetFlow v5-v8: Engine Type + Engine ID NetFlow v9: Source ID sFlow v5: agentSubID */ u_int16_t inc; /* increment, NetFlow v5: required by flow sequence number */ u_int32_t peer_idx; /* last known BGP peer index */ struct xflow_status_entry_counters counters; struct xflow_status_entry_sampling *sampling; struct xflow_status_entry_class *class; struct xflow_status_entry *next; }; /* prototypes */ #if (!defined __XFLOW_STATUS_C) #define EXT extern #else #define EXT #endif EXT u_int32_t hash_status_table(u_int32_t, struct sockaddr *, u_int32_t); EXT struct xflow_status_entry *search_status_table(struct sockaddr *, u_int32_t, int, int); EXT void update_good_status_table(struct xflow_status_entry *, u_int32_t); EXT void update_bad_status_table(struct xflow_status_entry *); EXT void print_status_table(time_t, int); EXT struct xflow_status_entry_sampling *search_smp_if_status_table(struct xflow_status_entry_sampling *, u_int32_t); EXT struct xflow_status_entry_sampling *search_smp_id_status_table(struct xflow_status_entry_sampling *, u_int16_t, u_int8_t); EXT struct xflow_status_entry_sampling *create_smp_entry_status_table(struct xflow_status_entry *); EXT struct xflow_status_entry_class *search_class_id_status_table(struct xflow_status_entry_class *, pm_class_t); EXT struct xflow_status_entry_class *create_class_entry_status_table(struct xflow_status_entry *); EXT pm_class_t NF_evaluate_classifier(struct xflow_status_entry_class *, pm_class_t *); EXT struct xflow_status_entry *xflow_status_table[XFLOW_STATUS_TABLE_SZ]; EXT u_int32_t xflow_status_table_entries; EXT u_int8_t xflow_status_table_error; EXT u_int32_t xflow_tot_bad_datagrams; EXT u_int8_t smp_entry_status_table_memerr, class_entry_status_table_memerr; EXT void set_vector_f_status(struct packet_ptrs_vector *); #undef EXT pmacct-0.14.0/src/regexp.c0000644000175000017500000006637611273021602014312 0ustar paolopaolo/* * regcomp and regexec -- regsub regerror are elsewhere * @(#)regexp.c 1.3 of 18 April 87 * * Copyright (c) 1986 by University of Toronto. * Written by Henry Spencer. Not derived from licensed software. * * Permission is granted to anyone to use this software for any * purpose on any computer system, and to redistribute it freely, * subject to the following restrictions: * * 1. The author is not responsible for the consequences of use of * this software, no matter how awful, even if they arise * from defects in it. * * 2. The origin of this software must not be misrepresented, either * by explicit claim or by omission. * * 3. Altered versions must be plainly marked as such, and must not * be misrepresented as being the original software. * * Beware that some of this code is subtly aware of the way operator * precedence is structured in regular expressions. Serious changes in * regular-expression syntax might require a total rethink. * * This code was modified by Ethan Sommer to work within the kernel * (it now uses kmalloc etc..) * * Modified slightly by Matthew Strait to use more modern C. */ #include "pmacct.h" #include "pmacct-data.h" #include "regexp.h" #include "regmagic.h" void pm_regerror(char * s) { printf("<3>Regexp: %s\n", s); /* NOTREACHED */ } /* * The "internal use only" fields in regexp.h are present to pass info from * compile to execute that permits the execute phase to run lots faster on * simple cases. They are: * * regstart char that must begin a match; '\0' if none obvious * reganch is the match anchored (at beginning-of-line only)? * regmust string (pointer into program) that match must include, or NULL * regmlen length of regmust string * * Regstart and reganch permit very fast decisions on suitable starting points * for a match, cutting down the work a lot. Regmust permits fast rejection * of lines that cannot possibly match. The regmust tests are costly enough * that regcomp() supplies a regmust only if the r.e. contains something * potentially expensive (at present, the only such thing detected is * or + * at the start of the r.e., which can involve a lot of backup). Regmlen is * supplied because the test in regexec() needs it and regcomp() is computing * it anyway. */ /* * Structure for regexp "program". This is essentially a linear encoding * of a nondeterministic finite-state machine (aka syntax charts or * "railroad normal form" in parsing technology). Each node is an opcode * plus a "next" pointer, possibly plus an operand. "Next" pointers of * all nodes except BRANCH implement concatenation; a "next" pointer with * a BRANCH on both ends of it is connecting two alternatives. (Here we * have one of the subtle syntax dependencies: an individual BRANCH (as * opposed to a collection of them) is never concatenated with anything * because of operator precedence.) The operand of some types of node is * a literal string; for others, it is a node leading into a sub-FSM. In * particular, the operand of a BRANCH node is the first node of the branch. * (NB this is *not* a tree structure: the tail of the branch connects * to the thing following the set of BRANCHes.) The opcodes are: */ /* definition number opnd? meaning */ #define END 0 /* no End of program. */ #define BOL 1 /* no Match "" at beginning of line. */ #define EOL 2 /* no Match "" at end of line. */ #define ANY 3 /* no Match any one character. */ #define ANYOF 4 /* str Match any character in this string. */ #define ANYBUT 5 /* str Match any character not in this string. */ #define BRANCH 6 /* node Match this alternative, or the next... */ #define BACK 7 /* no Match "", "next" ptr points backward. */ #define EXACTLY 8 /* str Match this string. */ #define NOTHING 9 /* no Match empty string. */ #define STAR 10 /* node Match this (simple) thing 0 or more times. */ #define PLUS 11 /* node Match this (simple) thing 1 or more times. */ #define OPEN 20 /* no Mark this point in input as start of #n. */ /* OPEN+1 is number 1, etc. */ #define CLOSE 30 /* no Analogous to OPEN. */ /* * Opcode notes: * * BRANCH The set of branches constituting a single choice are hooked * together with their "next" pointers, since precedence prevents * anything being concatenated to any individual branch. The * "next" pointer of the last BRANCH in a choice points to the * thing following the whole choice. This is also where the * final "next" pointer of each individual branch points; each * branch starts with the operand node of a BRANCH node. * * BACK Normal "next" pointers all implicitly point forward; BACK * exists to make loop structures possible. * * STAR,PLUS '?', and complex '*' and '+', are implemented as circular * BRANCH structures using BACK. Simple cases (one character * per match) are implemented with STAR and PLUS for speed * and to minimize recursive plunges. * * OPEN,CLOSE ...are numbered at compile time. */ /* * A node is one char of opcode followed by two chars of "next" pointer. * "Next" pointers are stored as two 8-bit pieces, high order first. The * value is a positive offset from the opcode of the node containing it. * An operand, if any, simply follows the node. (Note that much of the * code generation knows about this implicit relationship.) * * Using two bytes for the "next" pointer is vast overkill for most things, * but allows patterns to get big without disasters. */ #define OP(p) (*(p)) #define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) #define OPERAND(p) ((p) + 3) /* * See regmagic.h for one further detail of program structure. */ /* * Utility definitions. */ #ifndef CHARBITS #define UCHARAT(p) ((int)*(unsigned char *)(p)) #else #define UCHARAT(p) ((int)*(p)&CHARBITS) #endif #define FAIL(m) { pm_regerror(m); return(NULL); } #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') #define META "^$.[()|?+*\\" /* * Flags to be passed up and down. */ #define HASWIDTH 01 /* Known never to match null string. */ #define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ #define SPSTART 04 /* Starts with * or +. */ #define WORST 0 /* Worst case. */ /* * Global work variables for regcomp(). */ static char *regparse; /* Input-scan pointer. */ static int regnpar; /* () count. */ static char regdummy; static char *regcode; /* Code-emit pointer; ®dummy = don't. */ static long regsize; /* Code size. */ /* * Forward declarations for regcomp()'s friends. */ #ifndef STATIC #define STATIC static #endif STATIC char *reg(int paren,int *flagp); STATIC char *regbranch(int *flagp); STATIC char *regpiece(int *flagp); STATIC char *regatom(int *flagp); STATIC char *regnode(char op); STATIC char *regnext(char *p); STATIC void regc(char b); STATIC void reginsert(char op, char *opnd); STATIC void regtail(char *p, char *val); STATIC void regoptail(char *p, char *val); size_t my_strcspn(const char *s1,const char *s2) { char *scan1; char *scan2; int count; count = 0; for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ if (*scan1 == *scan2++) return(count); count++; } return(count); } /* - regcomp - compile a regular expression into internal code * * We can't allocate space until we know how big the compiled form will be, * but we can't compile it (and thus know how big it is) until we've got a * place to put the code. So we cheat: we compile it twice, once with code * generation turned off and size counting turned on, and once "for real". * This also means that we don't allocate space until we are sure that the * thing really will compile successfully, and we never have to move the * code and thus invalidate pointers into it. (Note that it has to be in * one piece because free() must be able to free it all.) * * Beware that the optimization-preparation code in here knows about some * of the structure of the compiled regexp. */ regexp * pm_regcomp(char *exp,int *patternsize) { register regexp *r; register char *scan; register char *longest; register int len; int flags; /* commented out by ethan extern char *malloc(); */ if (exp == NULL) FAIL("NULL argument"); /* First pass: determine size, legality. */ regparse = exp; regnpar = 1; regsize = 0L; regcode = ®dummy; regc(MAGIC); if (reg(0, &flags) == NULL) return(NULL); /* Small enough for pointer-storage convention? */ if (regsize >= 32767L) /* Probably could be 65535L. */ FAIL("regexp too big"); /* Allocate space. */ *patternsize=sizeof(regexp) + (unsigned)regsize; r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); if (r == NULL) FAIL("out of space"); /* Second pass: emit code. */ regparse = exp; regnpar = 1; regcode = r->program; regc(MAGIC); if (reg(0, &flags) == NULL) { free(r); return(NULL); } /* Dig out information for optimizations. */ r->regstart = '\0'; /* Worst-case defaults. */ r->reganch = 0; r->regmust = NULL; r->regmlen = 0; scan = r->program+1; /* First BRANCH. */ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ scan = OPERAND(scan); /* Starting-point info. */ if (OP(scan) == EXACTLY) r->regstart = *OPERAND(scan); else if (OP(scan) == BOL) r->reganch++; /* * If there's something expensive in the r.e., find the * longest literal string that must appear and make it the * regmust. Resolve ties in favor of later strings, since * the regstart check works with the beginning of the r.e. * and avoiding duplication strengthens checking. Not a * strong reason, but sufficient in the absence of others. */ if (flags&SPSTART) { longest = NULL; len = 0; for (; scan != NULL; scan = regnext(scan)) if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { longest = OPERAND(scan); len = strlen(OPERAND(scan)); } r->regmust = longest; r->regmlen = len; } } return(r); } /* - reg - regular expression, i.e. main body or parenthesized thing * * Caller must absorb opening parenthesis. * * Combining parenthesis handling with the base level of regular expression * is a trifle forced, but the need to tie the tails of the branches to what * follows makes it hard to avoid. */ static char * reg(int paren, int *flagp /* Parenthesized? */ ) { register char *ret; register char *br; register char *ender; register int parno = 0; /* 0 makes gcc happy */ int flags; *flagp = HASWIDTH; /* Tentatively. */ /* Make an OPEN node, if parenthesized. */ if (paren) { if (regnpar >= NSUBEXP) FAIL("too many ()"); parno = regnpar; regnpar++; ret = regnode(OPEN+parno); } else ret = NULL; /* Pick up the branches, linking them together. */ br = regbranch(&flags); if (br == NULL) return(NULL); if (ret != NULL) regtail(ret, br); /* OPEN -> first. */ else ret = br; if (!(flags&HASWIDTH)) *flagp &= ~HASWIDTH; *flagp |= flags&SPSTART; while (*regparse == '|') { regparse++; br = regbranch(&flags); if (br == NULL) return(NULL); regtail(ret, br); /* BRANCH -> BRANCH. */ if (!(flags&HASWIDTH)) *flagp &= ~HASWIDTH; *flagp |= flags&SPSTART; } /* Make a closing node, and hook it on the end. */ ender = regnode((paren) ? CLOSE+parno : END); regtail(ret, ender); /* Hook the tails of the branches to the closing node. */ for (br = ret; br != NULL; br = regnext(br)) regoptail(br, ender); /* Check for proper termination. */ if (paren && *regparse++ != ')') { FAIL("unmatched ()"); } else if (!paren && *regparse != '\0') { if (*regparse == ')') { FAIL("unmatched ()"); } else FAIL("junk on end"); /* "Can't happen". */ /* NOTREACHED */ } return(ret); } /* - regbranch - one alternative of an | operator * * Implements the concatenation operator. */ static char * regbranch(int *flagp) { register char *ret; register char *chain; register char *latest; int flags; *flagp = WORST; /* Tentatively. */ ret = regnode(BRANCH); chain = NULL; while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { latest = regpiece(&flags); if (latest == NULL) return(NULL); *flagp |= flags&HASWIDTH; if (chain == NULL) /* First piece. */ *flagp |= flags&SPSTART; else regtail(chain, latest); chain = latest; } if (chain == NULL) /* Loop ran zero times. */ (void) regnode(NOTHING); return(ret); } /* - regpiece - something followed by possible [*+?] * * Note that the branching code sequences used for ? and the general cases * of * and + are somewhat optimized: they use the same NOTHING node as * both the endmarker for their branch list and the body of the last branch. * It might seem that this node could be dispensed with entirely, but the * endmarker role is not redundant. */ static char * regpiece(int *flagp) { register char *ret; register char op; register char *next; int flags; ret = regatom(&flags); if (ret == NULL) return(NULL); op = *regparse; if (!ISMULT(op)) { *flagp = flags; return(ret); } if (!(flags&HASWIDTH) && op != '?') FAIL("*+ operand could be empty"); *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); if (op == '*' && (flags&SIMPLE)) reginsert(STAR, ret); else if (op == '*') { /* Emit x* as (x&|), where & means "self". */ reginsert(BRANCH, ret); /* Either x */ regoptail(ret, regnode(BACK)); /* and loop */ regoptail(ret, ret); /* back */ regtail(ret, regnode(BRANCH)); /* or */ regtail(ret, regnode(NOTHING)); /* null. */ } else if (op == '+' && (flags&SIMPLE)) reginsert(PLUS, ret); else if (op == '+') { /* Emit x+ as x(&|), where & means "self". */ next = regnode(BRANCH); /* Either */ regtail(ret, next); regtail(regnode(BACK), ret); /* loop back */ regtail(next, regnode(BRANCH)); /* or */ regtail(ret, regnode(NOTHING)); /* null. */ } else if (op == '?') { /* Emit x? as (x|) */ reginsert(BRANCH, ret); /* Either x */ regtail(ret, regnode(BRANCH)); /* or */ next = regnode(NOTHING); /* null. */ regtail(ret, next); regoptail(ret, next); } regparse++; if (ISMULT(*regparse)) FAIL("nested *?+"); return(ret); } /* - regatom - the lowest level * * Optimization: gobbles an entire sequence of ordinary characters so that * it can turn them into a single node, which is smaller to store and * faster to run. Backslashed characters are exceptions, each becoming a * separate node; the code is simpler that way and it's not worth fixing. */ static char * regatom(int *flagp) { register char *ret; int flags; *flagp = WORST; /* Tentatively. */ switch (*regparse++) { case '^': ret = regnode(BOL); break; case '$': ret = regnode(EOL); break; case '.': ret = regnode(ANY); *flagp |= HASWIDTH|SIMPLE; break; case '[': { register int class; register int classend; if (*regparse == '^') { /* Complement of range. */ ret = regnode(ANYBUT); regparse++; } else ret = regnode(ANYOF); if (*regparse == ']' || *regparse == '-') regc(*regparse++); while (*regparse != '\0' && *regparse != ']') { if (*regparse == '-') { regparse++; if (*regparse == ']' || *regparse == '\0') regc('-'); else { class = UCHARAT(regparse-2)+1; classend = UCHARAT(regparse); if (class > classend+1) FAIL("invalid [] range"); for (; class <= classend; class++) regc(class); regparse++; } } else regc(*regparse++); } regc('\0'); if (*regparse != ']') FAIL("unmatched []"); regparse++; *flagp |= HASWIDTH|SIMPLE; } break; case '(': ret = reg(1, &flags); if (ret == NULL) return(NULL); *flagp |= flags&(HASWIDTH|SPSTART); break; case '\0': case '|': case ')': FAIL("internal urp"); /* Supposed to be caught earlier. */ break; case '?': case '+': case '*': FAIL("?+* follows nothing"); break; case '\\': if (*regparse == '\0') FAIL("trailing \\"); ret = regnode(EXACTLY); regc(*regparse++); regc('\0'); *flagp |= HASWIDTH|SIMPLE; break; default: { register int len; register char ender; regparse--; len = my_strcspn((const char *)regparse, (const char *)META); if (len <= 0) FAIL("internal disaster"); ender = *(regparse+len); if (len > 1 && ISMULT(ender)) len--; /* Back off clear of ?+* operand. */ *flagp |= HASWIDTH; if (len == 1) *flagp |= SIMPLE; ret = regnode(EXACTLY); while (len > 0) { regc(*regparse++); len--; } regc('\0'); } break; } return(ret); } /* - regnode - emit a node */ static char * /* Location. */ regnode(char op) { register char *ret; register char *ptr; ret = regcode; if (ret == ®dummy) { regsize += 3; return(ret); } ptr = ret; *ptr++ = op; *ptr++ = '\0'; /* Null "next" pointer. */ *ptr++ = '\0'; regcode = ptr; return(ret); } /* - regc - emit (if appropriate) a byte of code */ static void regc(char b) { if (regcode != ®dummy) *regcode++ = b; else regsize++; } /* - reginsert - insert an operator in front of already-emitted operand * * Means relocating the operand. */ static void reginsert(char op, char* opnd) { register char *src; register char *dst; register char *place; if (regcode == ®dummy) { regsize += 3; return; } src = regcode; regcode += 3; dst = regcode; while (src > opnd) *--dst = *--src; place = opnd; /* Op node, where operand used to be. */ *place++ = op; *place++ = '\0'; *place++ = '\0'; } /* - regtail - set the next-pointer at the end of a node chain */ static void regtail(char *p, char *val) { register char *scan; register char *temp; register int offset; if (p == ®dummy) return; /* Find last node. */ scan = p; for (;;) { temp = regnext(scan); if (temp == NULL) break; scan = temp; } if (OP(scan) == BACK) offset = scan - val; else offset = val - scan; *(scan+1) = (offset>>8)&0377; *(scan+2) = offset&0377; } /* - regoptail - regtail on operand of first argument; nop if operandless */ static void regoptail(char *p, char *val) { /* "Operandless" and "op != BRANCH" are synonymous in practice. */ if (p == NULL || p == ®dummy || OP(p) != BRANCH) return; regtail(OPERAND(p), val); } /* * regexec and friends */ /* * Global work variables for regexec(). */ static char *reginput; /* String-input pointer. */ static char *regbol; /* Beginning of input, for ^ check. */ static char **regstartp; /* Pointer to startp array. */ static char **regendp; /* Ditto for endp. */ /* * Forwards. */ STATIC int regtry(regexp *prog, char *string); STATIC int regmatch(char *prog); STATIC int regrepeat(char *p); #ifdef DEBUG int regnarrate = 0; void regdump(); STATIC char *regprop(char *op); #endif /* - regexec - match a regexp against a string */ int pm_regexec(regexp *prog, char *string) { register char *s; /* Be paranoid... */ if (prog == NULL || string == NULL) { printf("<3>Regexp: NULL parameter\n"); return(0); } /* Check validity of program. */ if (UCHARAT(prog->program) != MAGIC) { printf("<3>Regexp: corrupted program\n"); return(0); } /* If there is a "must appear" string, look for it. */ if (prog->regmust != NULL) { s = string; while ((s = strchr(s, prog->regmust[0])) != NULL) { if (strncmp(s, prog->regmust, prog->regmlen) == 0) break; /* Found it. */ s++; } if (s == NULL) /* Not present. */ return(0); } /* Mark beginning of line for ^ . */ regbol = string; /* Simplest case: anchored match need be tried only once. */ if (prog->reganch) return(regtry(prog, string)); /* Messy cases: unanchored match. */ s = string; if (prog->regstart != '\0') /* We know what char it must start with. */ while ((s = strchr(s, prog->regstart)) != NULL) { if (regtry(prog, s)) return(1); s++; } else /* We don't -- general case. */ do { if (regtry(prog, s)) return(1); } while (*s++ != '\0'); /* Failure. */ return(0); } /* - regtry - try match at specific point */ static int /* 0 failure, 1 success */ regtry(regexp *prog, char *string) { register int i; register char **sp; register char **ep; reginput = string; regstartp = prog->startp; regendp = prog->endp; sp = prog->startp; ep = prog->endp; for (i = NSUBEXP; i > 0; i--) { *sp++ = NULL; *ep++ = NULL; } if (regmatch(prog->program + 1)) { prog->startp[0] = string; prog->endp[0] = reginput; return(1); } else return(0); } /* - regmatch - main matching routine * * Conceptually the strategy is simple: check to see whether the current * node matches, call self recursively to see whether the rest matches, * and then act accordingly. In practice we make some effort to avoid * recursion, in particular by going through "ordinary" nodes (that don't * need to know whether the rest of the match failed) by a loop instead of * by recursion. */ static int /* 0 failure, 1 success */ regmatch(char *prog) { register char *scan = prog; /* Current node. */ char *next; /* Next node. */ #ifdef DEBUG if (scan != NULL && regnarrate) fprintf(stderr, "%s(\n", regprop(scan)); #endif while (scan != NULL) { #ifdef DEBUG if (regnarrate) fprintf(stderr, "%s...\n", regprop(scan)); #endif next = regnext(scan); switch (OP(scan)) { case BOL: if (reginput != regbol) return(0); break; case EOL: if (*reginput != '\0') return(0); break; case ANY: if (*reginput == '\0') return(0); reginput++; break; case EXACTLY: { register int len; register char *opnd; opnd = OPERAND(scan); /* Inline the first character, for speed. */ if (*opnd != *reginput) return(0); len = strlen(opnd); if (len > 1 && strncmp(opnd, reginput, len) != 0) return(0); reginput += len; } break; case ANYOF: if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL) return(0); reginput++; break; case ANYBUT: if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL) return(0); reginput++; break; case NOTHING: case BACK: break; case OPEN+1: case OPEN+2: case OPEN+3: case OPEN+4: case OPEN+5: case OPEN+6: case OPEN+7: case OPEN+8: case OPEN+9: { register int no; register char *save; no = OP(scan) - OPEN; save = reginput; if (regmatch(next)) { /* * Don't set startp if some later * invocation of the same parentheses * already has. */ if (regstartp[no] == NULL) regstartp[no] = save; return(1); } else return(0); } break; case CLOSE+1: case CLOSE+2: case CLOSE+3: case CLOSE+4: case CLOSE+5: case CLOSE+6: case CLOSE+7: case CLOSE+8: case CLOSE+9: { register int no; register char *save; no = OP(scan) - CLOSE; save = reginput; if (regmatch(next)) { /* * Don't set endp if some later * invocation of the same parentheses * already has. */ if (regendp[no] == NULL) regendp[no] = save; return(1); } else return(0); } break; case BRANCH: { register char *save; if (OP(next) != BRANCH) /* No choice. */ next = OPERAND(scan); /* Avoid recursion. */ else { do { save = reginput; if (regmatch(OPERAND(scan))) return(1); reginput = save; scan = regnext(scan); } while (scan != NULL && OP(scan) == BRANCH); return(0); /* NOTREACHED */ } } break; case STAR: case PLUS: { register char nextch; register int no; register char *save; register int min; /* * Lookahead to avoid useless match attempts * when we know what character comes next. */ nextch = '\0'; if (OP(next) == EXACTLY) nextch = *OPERAND(next); min = (OP(scan) == STAR) ? 0 : 1; save = reginput; no = regrepeat(OPERAND(scan)); while (no >= min) { /* If it could work, try it. */ if (nextch == '\0' || *reginput == nextch) if (regmatch(next)) return(1); /* Couldn't or didn't -- back up. */ no--; reginput = save + no; } return(0); } break; case END: return(1); /* Success! */ break; default: printf("<3>Regexp: memory corruption\n"); return(0); break; } scan = next; } /* * We get here only if there's trouble -- normally "case END" is * the terminating point. */ printf("<3>Regexp: corrupted pointers\n"); return(0); } /* - regrepeat - repeatedly match something simple, report how many */ static int regrepeat(char *p) { register int count = 0; register char *scan; register char *opnd; scan = reginput; opnd = OPERAND(p); switch (OP(p)) { case ANY: count = strlen(scan); scan += count; break; case EXACTLY: while (*opnd == *scan) { count++; scan++; } break; case ANYOF: while (*scan != '\0' && strchr(opnd, *scan) != NULL) { count++; scan++; } break; case ANYBUT: while (*scan != '\0' && strchr(opnd, *scan) == NULL) { count++; scan++; } break; default: /* Oh dear. Called inappropriately. */ printf("<3>Regexp: internal foulup\n"); count = 0; /* Best compromise. */ break; } reginput = scan; return(count); } /* - regnext - dig the "next" pointer out of a node */ static char* regnext(char *p) { register int offset; if (p == ®dummy) return(NULL); offset = NEXT(p); if (offset == 0) return(NULL); if (OP(p) == BACK) return(p-offset); else return(p+offset); } #ifdef DEBUG STATIC char *regprop(); /* - regdump - dump a regexp onto stdout in vaguely comprehensible form */ void regdump(regexp *r) { register char *s; register char op = EXACTLY; /* Arbitrary non-END op. */ register char *next; /* extern char *strchr(); */ s = r->program + 1; while (op != END) { /* While that wasn't END last time... */ op = OP(s); printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ next = regnext(s); if (next == NULL) /* Next ptr. */ printf("(0)"); else printf("(%d)", (s-r->program)+(next-s)); s += 3; if (op == ANYOF || op == ANYBUT || op == EXACTLY) { /* Literal string, where present. */ while (*s != '\0') { putchar(*s); s++; } s++; } putchar('\n'); } /* Header fields of interest. */ if (r->regstart != '\0') printf("start `%c' ", r->regstart); if (r->reganch) printf("anchored "); if (r->regmust != NULL) printf("must have \"%s\"", r->regmust); printf("\n"); } /* - regprop - printable representation of opcode */ static char * regprop(char *op) { #define BUFLEN 50 register char *p; static char buf[BUFLEN]; strcpy(buf, ":"); switch (OP(op)) { case BOL: p = "BOL"; break; case EOL: p = "EOL"; break; case ANY: p = "ANY"; break; case ANYOF: p = "ANYOF"; break; case ANYBUT: p = "ANYBUT"; break; case BRANCH: p = "BRANCH"; break; case EXACTLY: p = "EXACTLY"; break; case NOTHING: p = "NOTHING"; break; case BACK: p = "BACK"; break; case END: p = "END"; break; case OPEN+1: case OPEN+2: case OPEN+3: case OPEN+4: case OPEN+5: case OPEN+6: case OPEN+7: case OPEN+8: case OPEN+9: snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); p = NULL; break; case CLOSE+1: case CLOSE+2: case CLOSE+3: case CLOSE+4: case CLOSE+5: case CLOSE+6: case CLOSE+7: case CLOSE+8: case CLOSE+9: snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); p = NULL; break; case STAR: p = "STAR"; break; case PLUS: p = "PLUS"; break; default: printf("<3>Regexp: corrupted opcode\n"); break; } if (p != NULL) strncat(buf, p, BUFLEN-strlen(buf)); return(buf); } #endif pmacct-0.14.0/src/once.h0000644000175000017500000000131311515337133013735 0ustar paolopaolo#if defined __PMACCTD_C || defined __NFACCTD_C || defined __SFACCTD_C || defined __UACCTD_C #define EXT #else #define EXT extern #endif EXT u_int32_t PdataSz, ChBufHdrSz, CharPtrSz, CounterSz, HostAddrSz; EXT u_int32_t PpayloadSz, PextrasSz, PmsgSz; EXT u_int32_t NfHdrV5Sz, NfHdrV1Sz, NfHdrV7Sz, NfHdrV8Sz, NfHdrV9Sz; EXT u_int32_t IpFixHdrSz; EXT u_int32_t NfDataHdrV9Sz, NfTplHdrV9Sz, NfOptTplHdrV9Sz; EXT u_int32_t NfDataV1Sz, NfDataV5Sz, NfDataV7Sz; EXT u_int32_t IP4HdrSz, IP4TlSz, IP6HdrSz, IP6AddrSz, IP6TlSz; EXT u_int32_t MyTLHdrSz, TCPFlagOff; EXT u_int32_t SFSampleSz, SFLAddressSz, SFrenormEntrySz; EXT u_int32_t PptrsSz, UDPHdrSz, CSSz, MyTCPHdrSz, IpFlowCmnSz; EXT u_int16_t PbgpSz; #undef EXT pmacct-0.14.0/src/print_plugin.c0000644000175000017500000006266411741102677015542 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #define __PRINT_PLUGIN_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "print_plugin.h" #include "net_aggr.h" #include "ports_aggr.h" #include "ip_flow.h" #include "classifier.h" #include "crc32.c" /* Functions */ void print_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_data *data; struct ports_table pt; unsigned char *pipebuf; struct pollfd pfd; struct timezone tz; time_t t, now; int timeout, ret, num; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; unsigned char *rgptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; struct pkt_bgp_primitives *pbgp; char *dataptr; memcpy(&config, cfgptr, sizeof(struct configuration)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "Print Plugin", config.name); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile); } reload_map = FALSE; /* signal handling */ signal(SIGINT, P_exit_now); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, reload_maps); signal(SIGPIPE, SIG_IGN); #if !defined FBSD4 signal(SIGCHLD, SIG_IGN); #else signal(SIGCHLD, ignore_falling_child); #endif if (!config.print_refresh_time) config.print_refresh_time = DEFAULT_PRINT_REFRESH_TIME; if (!config.print_output) config.print_output = PRINT_OUTPUT_FORMATTED; timeout = config.print_refresh_time*1000; if (config.what_to_count & (COUNT_SUM_HOST|COUNT_SUM_NET)) insert_func = P_sum_host_insert; else if (config.what_to_count & COUNT_SUM_PORT) insert_func = P_sum_port_insert; else if (config.what_to_count & COUNT_SUM_AS) insert_func = P_sum_as_insert; #if defined (HAVE_L2) else if (config.what_to_count & COUNT_SUM_MAC) insert_func = P_sum_mac_insert; #endif else insert_func = P_cache_insert; /* Dirty but allows to save some IFs, centralizes checks and makes later comparison statements lean */ if (!(config.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_PEER_DST_IP| COUNT_SRC_STD_COMM|COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED| COUNT_SRC_LOCAL_PREF|COUNT_MPLS_VPN_RD))) PbgpSz = 0; memset(&nt, 0, sizeof(nt)); memset(&nc, 0, sizeof(nc)); memset(&pt, 0, sizeof(pt)); load_networks(config.networks_file, &nt, &nc); set_net_funcs(&nt); if (config.ports_file) load_ports(config.ports_file, &pt); pp_size = sizeof(struct pkt_primitives); pb_size = sizeof(struct pkt_bgp_primitives); dbc_size = sizeof(struct chained_cache); if (!config.print_cache_entries) config.print_cache_entries = PRINT_CACHE_ENTRIES; memset(&sa, 0, sizeof(struct scratch_area)); sa.num = config.print_cache_entries*AVERAGE_CHAIN_LEN; sa.size = sa.num*dbc_size; pipebuf = (unsigned char *) Malloc(config.buffer_size); cache = (struct chained_cache *) Malloc(config.print_cache_entries*dbc_size); queries_queue = (struct chained_cache **) Malloc((sa.num+config.print_cache_entries)*sizeof(struct chained_cache *)); sa.base = (unsigned char *) Malloc(sa.size); sa.ptr = sa.base; sa.next = NULL; pfd.fd = pipe_fd; pfd.events = POLLIN; setnonblocking(pipe_fd); now = time(NULL); /* print_refresh time init: deadline */ refresh_deadline = now; t = roundoff_time(refresh_deadline, config.sql_history_roundoff); while ((t+config.print_refresh_time) < refresh_deadline) t += config.print_refresh_time; refresh_deadline = t; refresh_deadline += config.print_refresh_time; /* it's a deadline not a basetime */ /* setting number of entries in _protocols structure */ while (_protocols[protocols_number].number != -1) protocols_number++; memset(pipebuf, 0, config.buffer_size); memset(cache, 0, config.print_cache_entries*sizeof(struct chained_cache)); memset(queries_queue, 0, (sa.num+config.print_cache_entries)*sizeof(struct chained_cache *)); memset(sa.base, 0, sa.size); memset(&flushtime, 0, sizeof(flushtime)); if (!config.sql_table && config.print_output == PRINT_OUTPUT_FORMATTED) P_write_stats_header_formatted(stdout); else if (!config.sql_table && config.print_output == PRINT_OUTPUT_CSV) P_write_stats_header_csv(stdout); /* plugin main loop */ for(;;) { poll_again: status->wakeup = TRUE; ret = poll(&pfd, 1, timeout); if (ret < 0) goto poll_again; now = time(NULL); switch (ret) { case 0: /* timeout */ if (qq_ptr) { switch (fork()) { case 0: /* Child */ P_cache_purge(queries_queue, qq_ptr); exit(0); default: /* Parent */ P_cache_flush(queries_queue, qq_ptr); gettimeofday(&flushtime, &tz); refresh_deadline += config.print_refresh_time; qq_ptr = FALSE; if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } break; } } break; default: /* we received data */ read_data: if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; goto poll_again; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%u'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%u'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); } seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); if ((rg->ptr+bufsz) >= rg->end) rg->ptr = rg->base; else rg->ptr += bufsz; /* lazy refresh time handling */ if (now > refresh_deadline) { if (qq_ptr) { switch (fork()) { case 0: /* Child */ P_cache_purge(queries_queue, qq_ptr); exit(0); default: /* Parent */ P_cache_flush(queries_queue, qq_ptr); gettimeofday(&flushtime, &tz); refresh_deadline += config.print_refresh_time; qq_ptr = FALSE; if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } break; } } } data = (struct pkt_data *) (pipebuf+sizeof(struct ch_buf_hdr)); while (((struct ch_buf_hdr *)pipebuf)->num) { for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &data->primitives); if (config.ports_file) { if (!pt.table[data->primitives.src_port]) data->primitives.src_port = 0; if (!pt.table[data->primitives.dst_port]) data->primitives.dst_port = 0; } if (PbgpSz) pbgp = (struct pkt_bgp_primitives *) ((u_char *)data+PdataSz); else pbgp = NULL; (*insert_func)(data, pbgp); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) data; dataptr += PdataSz + PbgpSz; data = (struct pkt_data *) dataptr; } } goto read_data; } } } unsigned int P_cache_modulo(struct pkt_primitives *srcdst, struct pkt_bgp_primitives *pbgp) { register unsigned int modulo; modulo = cache_crc32((unsigned char *)srcdst, pp_size); if (PbgpSz) { if (pbgp) modulo ^= cache_crc32((unsigned char *)pbgp, pb_size); } return modulo %= config.print_cache_entries; } struct chained_cache *P_cache_search(struct pkt_primitives *data, struct pkt_bgp_primitives *pbgp) { unsigned int modulo = P_cache_modulo(data, pbgp); struct chained_cache *cache_ptr = &cache[modulo]; int res_data = TRUE, res_bgp = TRUE; start: res_data = memcmp(&cache_ptr->primitives, data, sizeof(struct pkt_primitives)); if (PbgpSz) { if (cache_ptr->pbgp) res_bgp = memcmp(cache_ptr->pbgp, pbgp, sizeof(struct pkt_bgp_primitives)); } else res_bgp = FALSE; if (res_data || res_bgp) { if (cache_ptr->valid == TRUE) { if (cache_ptr->next) { cache_ptr = cache_ptr->next; goto start; } } } else return cache_ptr; return NULL; } void P_cache_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp) { unsigned int modulo = P_cache_modulo(&data->primitives, pbgp); struct chained_cache *cache_ptr = &cache[modulo]; struct pkt_primitives *srcdst = &data->primitives; int res_data, res_bgp; /* We are classifing packets. We have a non-zero bytes accumulator (ba) and a non-zero class. Before accounting ba to this class, we have to remove ba from class zero. */ if (config.what_to_count & COUNT_CLASS && data->cst.ba && data->primitives.class) { struct chained_cache *Cursor; pm_class_t lclass = data->primitives.class; data->primitives.class = 0; Cursor = P_cache_search(&data->primitives, pbgp); data->primitives.class = lclass; /* We can assign the flow to a new class only if we are able to subtract the accumulator from the zero-class. If this is not the case, we will discard the accumulators. The assumption is that accumulators are not retroactive */ if (Cursor) { if (timeval_cmp(&data->cst.stamp, &flushtime) >= 0) { /* MIN(): ToS issue */ Cursor->bytes_counter -= MIN(Cursor->bytes_counter, data->cst.ba); Cursor->packet_counter -= MIN(Cursor->packet_counter, data->cst.pa); Cursor->flow_counter -= MIN(Cursor->flow_counter, data->cst.fa); } else memset(&data->cst, 0, CSSz); } else memset(&data->cst, 0, CSSz); } start: res_data = res_bgp = TRUE; res_data = memcmp(&cache_ptr->primitives, srcdst, sizeof(struct pkt_primitives)); if (PbgpSz) { if (cache_ptr->pbgp) res_bgp = memcmp(cache_ptr->pbgp, pbgp, sizeof(struct pkt_bgp_primitives)); } else res_bgp = FALSE; if (res_data || res_bgp) { /* aliasing of entries */ if (cache_ptr->valid == TRUE) { if (cache_ptr->next) { cache_ptr = cache_ptr->next; goto start; } else { cache_ptr = P_cache_attach_new_node(cache_ptr); if (!cache_ptr) { Log(LOG_WARNING, "WARN ( %s/%s ): Unable to write data: try with a larger 'print_cache_entries' value.\n", config.name, config.type); return; } else { queries_queue[qq_ptr] = cache_ptr; qq_ptr++; } } } else { queries_queue[qq_ptr] = cache_ptr; qq_ptr++; } /* we add the new entry in the cache */ memcpy(&cache_ptr->primitives, srcdst, sizeof(struct pkt_primitives)); if (PbgpSz) { if (!cache_ptr->pbgp) cache_ptr->pbgp = (struct pkt_bgp_primitives *) malloc(PbgpSz); memcpy(cache_ptr->pbgp, pbgp, sizeof(struct pkt_bgp_primitives)); } else cache_ptr->pbgp = NULL; cache_ptr->packet_counter = data->pkt_num; cache_ptr->flow_counter = data->flo_num; cache_ptr->bytes_counter = data->pkt_len; cache_ptr->tcp_flags = data->tcp_flags; if (config.what_to_count & COUNT_CLASS) { cache_ptr->bytes_counter += data->cst.ba; cache_ptr->packet_counter += data->cst.pa; cache_ptr->flow_counter += data->cst.fa; } cache_ptr->valid = TRUE; } else { if (cache_ptr->valid == TRUE) { /* everything is ok; summing counters */ cache_ptr->packet_counter += data->pkt_num; cache_ptr->flow_counter += data->flo_num; cache_ptr->bytes_counter += data->pkt_len; cache_ptr->tcp_flags |= data->tcp_flags; if (config.what_to_count & COUNT_CLASS) { cache_ptr->bytes_counter += data->cst.ba; cache_ptr->packet_counter += data->cst.pa; cache_ptr->flow_counter += data->cst.fa; } } else { /* entry invalidated; restarting counters */ cache_ptr->packet_counter = data->pkt_num; cache_ptr->flow_counter = data->flo_num; cache_ptr->bytes_counter = data->pkt_len; cache_ptr->tcp_flags = data->tcp_flags; if (config.what_to_count & COUNT_CLASS) { cache_ptr->bytes_counter += data->cst.ba; cache_ptr->packet_counter += data->cst.pa; cache_ptr->flow_counter += data->cst.fa; } cache_ptr->valid = TRUE; queries_queue[qq_ptr] = cache_ptr; qq_ptr++; } } } void P_cache_flush(struct chained_cache *queue[], int index) { int j; for (j = 0; j < index; j++) { queue[j]->valid = FALSE; queue[j]->next = NULL; } /* rewinding scratch area stuff */ sa.ptr = sa.base; } struct chained_cache *P_cache_attach_new_node(struct chained_cache *elem) { if ((sa.ptr+sizeof(struct chained_cache)) <= (sa.base+sa.size)) { sa.ptr += sizeof(struct chained_cache); elem->next = (struct chained_cache *) sa.ptr; return (struct chained_cache *) sa.ptr; } else return NULL; /* XXX */ } void P_cache_purge(struct chained_cache *queue[], int index) { struct pkt_primitives *data = NULL; struct pkt_bgp_primitives *pbgp = NULL; struct pkt_bgp_primitives empty_pbgp; char src_mac[18], dst_mac[18], src_host[INET6_ADDRSTRLEN], dst_host[INET6_ADDRSTRLEN], ip_address[INET6_ADDRSTRLEN]; char rd_str[SRVBUFLEN]; char *as_path, *bgp_comm, empty_aspath[] = "^$"; FILE *f = NULL; int j; memset(&empty_pbgp, 0, sizeof(struct pkt_bgp_primitives)); if (config.sql_table) { f = open_print_output_file(config.sql_table, refresh_deadline-config.print_refresh_time); if (config.print_output == PRINT_OUTPUT_FORMATTED) P_write_stats_header_formatted(f); else if (config.print_output == PRINT_OUTPUT_CSV) P_write_stats_header_csv(f); } else f = stdout; /* write to standard output */ if (config.print_markers) fprintf(f, "--START (%ld+%d)--\n", refresh_deadline-config.print_refresh_time, config.print_refresh_time); for (j = 0; j < index; j++) { data = &queue[j]->primitives; if (queue[j]->pbgp) pbgp = queue[j]->pbgp; else pbgp = &empty_pbgp; if (!queue[j]->bytes_counter && !queue[j]->packet_counter && !queue[j]->flow_counter) continue; if (config.print_output == PRINT_OUTPUT_FORMATTED) { fprintf(f, "%-10llu ", data->id); fprintf(f, "%-10llu ", data->id2); fprintf(f, "%-16s ", ((data->class && class[(data->class)-1].id) ? class[(data->class)-1].protocol : "unknown" )); #if defined (HAVE_L2) etheraddr_string(data->eth_shost, src_mac); fprintf(f, "%-17s ", src_mac); etheraddr_string(data->eth_dhost, dst_mac); fprintf(f, "%-17s ", dst_mac); fprintf(f, "%-5u ", data->vlan_id); fprintf(f, "%-2u ", data->cos); fprintf(f, "%-5x ", data->etype); #endif fprintf(f, "%-10u ", data->src_as); fprintf(f, "%-10u ", data->dst_as); bgp_comm = pbgp->std_comms; while (bgp_comm) { bgp_comm = strchr(pbgp->std_comms, ' '); if (bgp_comm) *bgp_comm = '_'; } if (strlen(pbgp->std_comms)) fprintf(f, "%-22s ", pbgp->std_comms); else fprintf(f, "%-22u ", 0); as_path = pbgp->as_path; while (as_path) { as_path = strchr(pbgp->as_path, ' '); if (as_path) *as_path = '_'; } if (strlen(pbgp->as_path)) fprintf(f, "%-22s ", pbgp->as_path); else fprintf(f, "%-22s ", empty_aspath); fprintf(f, "%-5u ", pbgp->local_pref); fprintf(f, "%-5u ", pbgp->med); fprintf(f, "%-10u ", pbgp->peer_src_as); fprintf(f, "%-10u ", pbgp->peer_dst_as); addr_to_str(ip_address, &pbgp->peer_src_ip); #if defined ENABLE_IPV6 fprintf(f, "%-45s ", ip_address); #else fprintf(f, "%-15s ", ip_address); #endif addr_to_str(ip_address, &pbgp->peer_dst_ip); #if defined ENABLE_IPV6 fprintf(f, "%-45s ", ip_address); #else fprintf(f, "%-15s ", ip_address); #endif fprintf(f, "%-10u ", data->ifindex_in); fprintf(f, "%-10u ", data->ifindex_out); bgp_rd2str(rd_str, &pbgp->mpls_vpn_rd); fprintf(f, "%-18s ", rd_str); addr_to_str(src_host, &data->src_ip); #if defined ENABLE_IPV6 fprintf(f, "%-45s ", src_host); #else fprintf(f, "%-15s ", src_host); #endif addr_to_str(dst_host, &data->dst_ip); #if defined ENABLE_IPV6 fprintf(f, "%-45s ", dst_host); #else fprintf(f, "%-15s ", dst_host); #endif fprintf(f, "%-3u ", data->src_nmask); fprintf(f, "%-3u ", data->dst_nmask); fprintf(f, "%-5u ", data->src_port); fprintf(f, "%-5u ", data->dst_port); fprintf(f, "%-3u ", queue[j]->tcp_flags); if (!config.num_protos) fprintf(f, "%-10s ", _protocols[data->proto].name); else fprintf(f, "%-10d ", _protocols[data->proto].number); fprintf(f, "%-3u ", data->tos); #if defined HAVE_64BIT_COUNTERS fprintf(f, "%-20llu ", queue[j]->packet_counter); fprintf(f, "%-20llu ", queue[j]->flow_counter); fprintf(f, "%llu\n", queue[j]->bytes_counter); #else fprintf(f, "%-10lu ", queue[j]->packet_counter); fprintf(f, "%-10lu ", queue[j]->flow_counter); fprintf(f, "%lu\n", queue[j]->bytes_counter); #endif } else if (config.print_output == PRINT_OUTPUT_CSV) { fprintf(f, "%llu,", data->id); fprintf(f, "%llu,", data->id2); fprintf(f, "%s,", ((data->class && class[(data->class)-1].id) ? class[(data->class)-1].protocol : "unknown" )); #if defined (HAVE_L2) etheraddr_string(data->eth_shost, src_mac); fprintf(f, "%s,", src_mac); etheraddr_string(data->eth_dhost, dst_mac); fprintf(f, "%s,", dst_mac); fprintf(f, "%u,", data->vlan_id); fprintf(f, "%u,", data->cos); fprintf(f, "%x,", data->etype); #endif fprintf(f, "%u,", data->src_as); fprintf(f, "%u,", data->dst_as); bgp_comm = pbgp->std_comms; while (bgp_comm) { bgp_comm = strchr(pbgp->std_comms, ' '); if (bgp_comm) *bgp_comm = '_'; } if (strlen(pbgp->std_comms)) fprintf(f, "%s,", pbgp->std_comms); else fprintf(f, "%u,", 0); as_path = pbgp->as_path; while (as_path) { as_path = strchr(pbgp->as_path, ' '); if (as_path) *as_path = '_'; } fprintf(f, "%s,", pbgp->as_path); fprintf(f, "%u,", pbgp->local_pref); fprintf(f, "%u,", pbgp->med); fprintf(f, "%u,", pbgp->peer_src_as); fprintf(f, "%u,", pbgp->peer_dst_as); addr_to_str(ip_address, &pbgp->peer_src_ip); fprintf(f, "%s,", ip_address); addr_to_str(ip_address, &pbgp->peer_dst_ip); fprintf(f, "%s,", ip_address); fprintf(f, "%u,", data->ifindex_in); fprintf(f, "%u,", data->ifindex_out); bgp_rd2str(rd_str, &pbgp->mpls_vpn_rd); fprintf(f, "%s,", rd_str); addr_to_str(src_host, &data->src_ip); fprintf(f, "%s,", src_host); addr_to_str(dst_host, &data->dst_ip); fprintf(f, "%s,", dst_host); fprintf(f, "%u,", data->src_nmask); fprintf(f, "%u,", data->dst_nmask); fprintf(f, "%u,", data->src_port); fprintf(f, "%u,", data->dst_port); fprintf(f, "%u,", queue[j]->tcp_flags); if (!config.num_protos) fprintf(f, "%s,", _protocols[data->proto].name); else fprintf(f, "%d,", _protocols[data->proto].number); fprintf(f, "%u,", data->tos); #if defined HAVE_64BIT_COUNTERS fprintf(f, "%llu,", queue[j]->packet_counter); fprintf(f, "%llu,", queue[j]->flow_counter); fprintf(f, "%llu\n", queue[j]->bytes_counter); #else fprintf(f, "%lu,", queue[j]->packet_counter); fprintf(f, "%lu,", queue[j]->flow_counter); fprintf(f, "%lu\n", queue[j]->bytes_counter); #endif } } if (config.print_markers) fprintf(f, "--END--\n"); if (config.sql_table) fclose(f); if (config.sql_trigger_exec) P_trigger_exec(config.sql_trigger_exec); } void P_write_stats_header_formatted(FILE *f) { fprintf(f, "TAG "); fprintf(f, "TAG2 "); fprintf(f, "CLASS "); #if defined HAVE_L2 fprintf(f, "SRC_MAC "); fprintf(f, "DST_MAC "); fprintf(f, "VLAN "); fprintf(f, "COS "); fprintf(f, "ETYPE "); #endif fprintf(f, "SRC_AS "); fprintf(f, "DST_AS "); fprintf(f, "BGP_COMMS "); fprintf(f, "AS_PATH "); fprintf(f, "PREF "); fprintf(f, "MED "); fprintf(f, "PEER_SRC_AS "); fprintf(f, "PEER_DST_AS "); fprintf(f, "PEER_SRC_IP "); fprintf(f, "PEER_DST_IP "); fprintf(f, "IN_IFACE "); fprintf(f, "OUT_IFACE "); fprintf(f, "MPLS_VPN_RD "); #if defined ENABLE_IPV6 fprintf(f, "SRC_IP "); fprintf(f, "DST_IP "); #else fprintf(f, "SRC_IP "); fprintf(f, "DST_IP "); #endif fprintf(f, "SRC_MASK "); fprintf(f, "DST_MASK "); fprintf(f, "SRC_PORT "); fprintf(f, "DST_PORT "); fprintf(f, "TCP_FLAGS "); fprintf(f, "PROTOCOL "); fprintf(f, "TOS "); #if defined HAVE_64BIT_COUNTERS fprintf(f, "PACKETS "); fprintf(f, "FLOWS "); fprintf(f, "BYTES\n"); #else fprintf(f, "PACKETS "); fprintf(f, "FLOWS "); fprintf(f, "BYTES\n"); #endif } void P_write_stats_header_csv(FILE *f) { fprintf(f, "TAG,"); fprintf(f, "TAG2,"); fprintf(f, "CLASS,"); #if defined HAVE_L2 fprintf(f, "SRC_MAC,"); fprintf(f, "DST_MAC,"); fprintf(f, "VLAN,"); fprintf(f, "COS,"); fprintf(f, "ETYPE,"); #endif fprintf(f, "SRC_AS,"); fprintf(f, "DST_AS,"); fprintf(f, "BGP_COMMS,"); fprintf(f, "AS_PATH,"); fprintf(f, "PREF,"); fprintf(f, "MED,"); fprintf(f, "PEER_SRC_AS,"); fprintf(f, "PEER_DST_AS,"); fprintf(f, "PEER_SRC_IP,"); fprintf(f, "PEER_DST_IP,"); fprintf(f, "IN_IFACE,"); fprintf(f, "OUT_IFACE,"); fprintf(f, "MPLS_VPN_RD,"); fprintf(f, "SRC_IP,"); fprintf(f, "DST_IP,"); fprintf(f, "SRC_MASK,"); fprintf(f, "DST_MASK,"); fprintf(f, "SRC_PORT,"); fprintf(f, "DST_PORT,"); fprintf(f, "TCP_FLAGS,"); fprintf(f, "PROTOCOL,"); fprintf(f, "TOS,"); fprintf(f, "PACKETS,"); fprintf(f, "FLOWS,"); fprintf(f, "BYTES\n"); } void P_sum_host_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp) { struct in_addr ip; #if defined ENABLE_IPV6 struct in6_addr ip6; #endif if (data->primitives.dst_ip.family == AF_INET) { ip.s_addr = data->primitives.dst_ip.address.ipv4.s_addr; data->primitives.dst_ip.address.ipv4.s_addr = 0; data->primitives.dst_ip.family = 0; P_cache_insert(data, pbgp); data->primitives.src_ip.address.ipv4.s_addr = ip.s_addr; P_cache_insert(data, pbgp); } #if defined ENABLE_IPV6 if (data->primitives.dst_ip.family == AF_INET6) { memcpy(&ip6, &data->primitives.dst_ip.address.ipv6, sizeof(struct in6_addr)); memset(&data->primitives.dst_ip.address.ipv6, 0, sizeof(struct in6_addr)); data->primitives.dst_ip.family = 0; P_cache_insert(data, pbgp); memcpy(&data->primitives.src_ip.address.ipv6, &ip6, sizeof(struct in6_addr)); P_cache_insert(data, pbgp); return; } #endif } void P_sum_port_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp) { u_int16_t port; port = data->primitives.dst_port; data->primitives.dst_port = 0; P_cache_insert(data, pbgp); data->primitives.src_port = port; P_cache_insert(data, pbgp); } void P_sum_as_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp) { as_t asn; asn = data->primitives.dst_as; data->primitives.dst_as = 0; P_cache_insert(data, pbgp); data->primitives.src_as = asn; P_cache_insert(data, pbgp); } #if defined (HAVE_L2) void P_sum_mac_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp) { u_char macaddr[ETH_ADDR_LEN]; memcpy(macaddr, &data->primitives.eth_dhost, ETH_ADDR_LEN); memset(data->primitives.eth_dhost, 0, ETH_ADDR_LEN); P_cache_insert(data, pbgp); memcpy(&data->primitives.eth_shost, macaddr, ETH_ADDR_LEN); P_cache_insert(data, pbgp); } #endif void P_exit_now(int signum) { P_cache_purge(queries_queue, qq_ptr); wait(NULL); exit_plugin(0); } int P_trigger_exec(char *filename) { char *args[1]; int pid; memset(args, 0, sizeof(args)); switch (pid = vfork()) { case -1: return -1; case 0: execv(filename, args); exit(0); } return 0; } pmacct-0.14.0/src/pgsql_plugin.h0000644000175000017500000000533711453022224015520 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente */ /* 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. */ /* includes */ #include /* prototypes */ void pgsql_plugin(int, struct configuration *, void *); int PG_cache_dbop(struct DBdesc *, struct db_cache *, struct insert_data *); int PG_cache_dbop_copy(struct DBdesc *, struct db_cache *, struct insert_data *); void PG_cache_purge(struct db_cache *[], int, struct insert_data *); int PG_evaluate_history(int); int PG_compose_static_queries(); void PG_compose_conn_string(struct DBdesc *, char *); void PG_Lock(struct DBdesc *); void PG_file_close(struct logfile *); void PG_DB_Connect(struct DBdesc *, char *); void PG_DB_Close(struct BE_descs *); void PG_create_dyn_table(struct DBdesc *, char *); static int PG_affected_rows(PGresult *); void PG_create_backend(struct DBdesc *); void PG_set_callbacks(struct sqlfunc_cb_registry *); void PG_init_default_values(struct insert_data *); /* global vars */ int typed = TRUE; /* variables */ static char pgsql_user[] = "pmacct"; static char pgsql_pwd[] = "arealsmartpwd"; static char pgsql_db[] = "pmacct"; static char pgsql_table[] = "acct"; static char pgsql_table_v2[] = "acct_v2"; static char pgsql_table_v3[] = "acct_v3"; static char pgsql_table_v4[] = "acct_v4"; static char pgsql_table_v5[] = "acct_v5"; static char pgsql_table_v6[] = "acct_v6"; static char pgsql_table_v7[] = "acct_v7"; static char pgsql_table_v8[] = "acct_v8"; static char pgsql_table_bgp[] = "acct_bgp"; static char pgsql_table_uni[] = "acct_uni"; static char pgsql_table_uni_v2[] = "acct_uni_v2"; static char pgsql_table_uni_v3[] = "acct_uni_v3"; static char pgsql_table_uni_v4[] = "acct_uni_v4"; static char pgsql_table_uni_v5[] = "acct_uni_v5"; static char pgsql_table_as[] = "acct_as"; static char pgsql_table_as_v2[] = "acct_as_v2"; static char pgsql_table_as_v3[] = "acct_as_v3"; static char pgsql_table_as_v4[] = "acct_as_v4"; static char pgsql_table_as_v5[] = "acct_as_v5"; static char typed_str[] = "typed"; static char unified_str[] = "unified"; pmacct-0.14.0/src/pretag_handlers.h0000644000175000017500000001554411715256661016176 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* prototypes */ #if (!defined __PRETAG_HANDLERS_C) #define EXT extern #else #define EXT #endif EXT int PT_map_id_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_id2_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_ip_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_input_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_output_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_nexthop_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_bgp_nexthop_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_engine_type_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_engine_id_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_filter_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_v8agg_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_agent_id_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_sampling_rate_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_sample_type_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_direction_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_src_as_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_dst_as_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_peer_src_as_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_peer_dst_as_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_src_local_pref_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_local_pref_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_src_comms_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_comms_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_mpls_vpn_rd_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_label_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_jeq_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_return_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int PT_map_stack_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT pm_id_t PT_stack_sum(pm_id_t, pm_id_t); /* BPAS_*: bgp_peer_as_src map specific handlers */ EXT int BPAS_map_bgp_nexthop_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int BPAS_map_src_mac_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int BPAS_map_bgp_peer_dst_as_handler(char *, struct id_entry *, char *, struct plugin_requests *, int); EXT int pretag_input_handler(struct packet_ptrs *, void *, void *); EXT int pretag_id_handler(struct packet_ptrs *, void *, void *); EXT int pretag_id2_handler(struct packet_ptrs *, void *, void *); EXT int pretag_output_handler(struct packet_ptrs *, void *, void *); EXT int pretag_nexthop_handler(struct packet_ptrs *, void *, void *); EXT int pretag_bgp_nexthop_handler(struct packet_ptrs *, void *, void *); EXT int pretag_bgp_bgp_nexthop_handler(struct packet_ptrs *, void *, void *); EXT int pretag_engine_type_handler(struct packet_ptrs *, void *, void *); EXT int pretag_engine_id_handler(struct packet_ptrs *, void *, void *); EXT int pretag_filter_handler(struct packet_ptrs *, void *, void *); EXT int pretag_v8agg_handler(struct packet_ptrs *, void *, void *); EXT int pretag_src_as_handler(struct packet_ptrs *, void *, void *); EXT int pretag_dst_as_handler(struct packet_ptrs *, void *, void *); EXT int pretag_bgp_src_as_handler(struct packet_ptrs *, void *, void *); EXT int pretag_bgp_dst_as_handler(struct packet_ptrs *, void *, void *); EXT int pretag_peer_src_as_handler(struct packet_ptrs *, void *, void *); EXT int pretag_peer_dst_as_handler(struct packet_ptrs *, void *, void *); EXT int pretag_src_local_pref_handler(struct packet_ptrs *, void *, void *); EXT int pretag_local_pref_handler(struct packet_ptrs *, void *, void *); EXT int pretag_src_comms_handler(struct packet_ptrs *, void *, void *); EXT int pretag_comms_handler(struct packet_ptrs *, void *, void *); EXT int pretag_sampling_rate_handler(struct packet_ptrs *, void *, void *); EXT int pretag_direction_handler(struct packet_ptrs *, void *, void *); EXT int pretag_mpls_vpn_rd_handler(struct packet_ptrs *, void *, void *); EXT int SF_pretag_input_handler(struct packet_ptrs *, void *, void *); EXT int SF_pretag_output_handler(struct packet_ptrs *, void *, void *); EXT int SF_pretag_nexthop_handler(struct packet_ptrs *, void *, void *); EXT int SF_pretag_bgp_nexthop_handler(struct packet_ptrs *, void *, void *); EXT int SF_pretag_agent_id_handler(struct packet_ptrs *, void *, void *); EXT int SF_pretag_sampling_rate_handler(struct packet_ptrs *, void *, void *); EXT int SF_pretag_src_as_handler(struct packet_ptrs *, void *, void *); EXT int SF_pretag_dst_as_handler(struct packet_ptrs *, void *, void *); EXT int SF_pretag_dst_as_handler(struct packet_ptrs *, void *, void *); EXT int SF_pretag_sample_type_handler(struct packet_ptrs *, void *, void *); EXT int PM_pretag_src_as_handler(struct packet_ptrs *, void *, void *); EXT int PM_pretag_dst_as_handler(struct packet_ptrs *, void *, void *); EXT int PM_pretag_input_handler(struct packet_ptrs *, void *, void *); EXT int PM_pretag_output_handler(struct packet_ptrs *, void *, void *); EXT int BPAS_bgp_nexthop_handler(struct packet_ptrs *, void *, void *); EXT int BPAS_bgp_peer_dst_as_handler(struct packet_ptrs *, void *, void *); #undef EXT pmacct-0.14.0/src/acct.c0000644000175000017500000002347311306546535013737 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente */ /* 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. */ #define __ACCT_C /* includes */ #include "pmacct.h" #include "imt_plugin.h" #include "crc32.c" /* functions */ struct acc *search_accounting_structure(struct pkt_primitives *addr, struct pkt_bgp_primitives *pbgp) { struct acc *elem_acc; unsigned int hash, pos; unsigned int pp_size = sizeof(struct pkt_primitives); unsigned int pb_size = sizeof(struct pkt_bgp_primitives); hash = cache_crc32((unsigned char *)addr, pp_size); /* XXX: to be optimized? */ if (PbgpSz) { if (pbgp) hash ^= cache_crc32((unsigned char *)pbgp, pb_size); } pos = hash % config.buckets; Log(LOG_DEBUG, "DEBUG ( %s/%s ): Selecting bucket %u.\n", config.name, config.type, pos); elem_acc = (struct acc *) a; elem_acc += pos; while (elem_acc) { if (elem_acc->signature == hash) { if (compare_accounting_structure(elem_acc, addr, pbgp) == 0) return elem_acc; // if (!memcmp(&elem_acc->primitives, addr, sizeof(struct pkt_primitives))) return elem_acc; } elem_acc = elem_acc->next; } return NULL; } int compare_accounting_structure(struct acc *elem, struct pkt_primitives *data, struct pkt_bgp_primitives *pbgp) { int res_data = TRUE, res_bgp = TRUE; res_data = memcmp(&elem->primitives, data, sizeof(struct pkt_primitives)); /* XXX: to be optimized? */ if (PbgpSz) { if (elem->cbgp) { struct pkt_bgp_primitives tmp_pbgp; cache_to_pkt_bgp_primitives(&tmp_pbgp, elem->cbgp); res_bgp = memcmp(&tmp_pbgp, pbgp, sizeof(struct pkt_bgp_primitives)); } } else res_bgp = FALSE; return res_data | res_bgp; } void insert_accounting_structure(struct pkt_data *data, struct pkt_bgp_primitives *pbgp) { struct pkt_primitives *addr = &data->primitives; struct acc *elem_acc; unsigned char *elem, *new_elem; int solved = FALSE; unsigned int hash, pos; unsigned int pp_size = sizeof(struct pkt_primitives); unsigned int pb_size = sizeof(struct pkt_bgp_primitives); unsigned int cb_size = sizeof(struct cache_bgp_primitives); /* We are classifing packets. We have a non-zero bytes accumulator (ba) and a non-zero class. Before accounting ba to this class, we have to remove ba from class zero. */ if (config.what_to_count & COUNT_CLASS && data->cst.ba && data->primitives.class) { pm_class_t lclass = data->primitives.class; data->primitives.class = 0; elem_acc = search_accounting_structure(&data->primitives, pbgp); data->primitives.class = lclass; /* We can assign the flow to a new class only if we are able to subtract the accumulator from the zero-class. If this is not the case, we will discard the accumulators. The assumption is that accumulators are not retroactive */ if (elem_acc) { if (timeval_cmp(&data->cst.stamp, &elem_acc->rstamp) >= 0 && timeval_cmp(&data->cst.stamp, &table_reset_stamp) >= 0) { /* MIN(): ToS issue */ elem_acc->bytes_counter -= MIN(elem_acc->bytes_counter, data->cst.ba); elem_acc->packet_counter -= MIN(elem_acc->packet_counter, data->cst.pa); elem_acc->flow_counter -= MIN(elem_acc->flow_counter, data->cst.fa); } else memset(&data->cst, 0, CSSz); } else memset(&data->cst, 0, CSSz); } elem = a; hash = cache_crc32((unsigned char *)addr, pp_size); /* XXX: to be optimized? */ if (PbgpSz) { if (pbgp) hash ^= cache_crc32((unsigned char *)pbgp, pb_size); } pos = hash % config.buckets; Log(LOG_DEBUG, "DEBUG ( %s/%s ): Selecting bucket %u.\n", config.name, config.type, pos); /* 1st stage: compare data with last used element; 2nd stage: compare data with elements in the table, following chains */ if (lru_elem_ptr[pos]) { elem_acc = lru_elem_ptr[pos]; if (elem_acc->signature == hash) { if (compare_accounting_structure(elem_acc, addr, pbgp) == 0) { // if (memcmp(&elem_acc->primitives, addr, sizeof(struct pkt_primitives)) == 0) { if (elem_acc->reset_flag) reset_counters(elem_acc); elem_acc->packet_counter += data->pkt_num; elem_acc->flow_counter += data->flo_num; elem_acc->bytes_counter += data->pkt_len; elem_acc->tcp_flags |= data->tcp_flags; if (config.what_to_count & COUNT_CLASS) { elem_acc->packet_counter += data->cst.pa; elem_acc->bytes_counter += data->cst.ba; elem_acc->flow_counter += data->cst.fa; } return; } } } elem_acc = (struct acc *) elem; elem_acc += pos; while (solved == FALSE) { if (elem_acc->signature == hash) { if (compare_accounting_structure(elem_acc, addr, pbgp) == 0) { // if (memcmp(&elem_acc->primitives, addr, sizeof(struct pkt_primitives)) == 0) { if (elem_acc->reset_flag) reset_counters(elem_acc); elem_acc->packet_counter += data->pkt_num; elem_acc->flow_counter += data->flo_num; elem_acc->bytes_counter += data->pkt_len; elem_acc->tcp_flags |= data->tcp_flags; if (config.what_to_count & COUNT_CLASS) { elem_acc->packet_counter += data->cst.pa; elem_acc->bytes_counter += data->cst.ba; elem_acc->flow_counter += data->cst.fa; } lru_elem_ptr[config.buckets] = elem_acc; return; } } if (!elem_acc->bytes_counter && !elem_acc->packet_counter) { /* hmmm */ if (elem_acc->reset_flag) elem_acc->reset_flag = FALSE; memcpy(&elem_acc->primitives, addr, sizeof(struct pkt_primitives)); /* XXX: to be optimized? */ if (PbgpSz) { if (elem_acc->cbgp) { if (elem_acc->cbgp->std_comms) free(elem_acc->cbgp->std_comms); if (elem_acc->cbgp->ext_comms) free(elem_acc->cbgp->ext_comms); if (elem_acc->cbgp->as_path) free(elem_acc->cbgp->as_path); if (elem_acc->cbgp->src_std_comms) free(elem_acc->cbgp->src_std_comms); if (elem_acc->cbgp->src_ext_comms) free(elem_acc->cbgp->src_ext_comms); if (elem_acc->cbgp->src_as_path) free(elem_acc->cbgp->src_as_path); free(elem_acc->cbgp); } elem_acc->cbgp = (struct cache_bgp_primitives *) malloc(cb_size); memset(elem_acc->cbgp, 0, cb_size); pkt_to_cache_bgp_primitives(elem_acc->cbgp, pbgp, config.what_to_count); } elem_acc->packet_counter += data->pkt_num; elem_acc->flow_counter += data->flo_num; elem_acc->bytes_counter += data->pkt_len; elem_acc->tcp_flags |= data->tcp_flags; elem_acc->signature = hash; if (config.what_to_count & COUNT_CLASS) { elem_acc->packet_counter += data->cst.pa; elem_acc->bytes_counter += data->cst.ba; elem_acc->flow_counter += data->cst.fa; } lru_elem_ptr[config.buckets] = elem_acc; return; } /* Handling collisions */ else if (elem_acc->next != NULL) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): Walking through the collision-chain.\n", config.name, config.type); elem_acc = elem_acc->next; solved = FALSE; } else if (elem_acc->next == NULL) { /* We have to know if there is enough space for a new element; if not we are losing informations; conservative approach */ if (no_more_space) return; /* We have to allocate new space for this address */ Log(LOG_DEBUG, "DEBUG ( %s/%s ): Creating new element.\n", config.name, config.type); if (current_pool->space_left >= sizeof(struct acc)) { new_elem = current_pool->ptr; current_pool->space_left -= sizeof(struct acc); current_pool->ptr += sizeof(struct acc); } else { current_pool = request_memory_pool(config.memory_pool_size); if (current_pool == NULL) { Log(LOG_WARNING, "WARN ( %s/%s ): Unable to allocate more memory pools, clear stats manually!\n", config.name, config.type); no_more_space = TRUE; return; } else { new_elem = current_pool->ptr; current_pool->space_left -= sizeof(struct acc); current_pool->ptr += sizeof(struct acc); } } elem_acc->next = (struct acc *) new_elem; elem_acc = (struct acc *) new_elem; memcpy(&elem_acc->primitives, addr, sizeof(struct pkt_primitives)); /* XXX: to be optimized? */ if (PbgpSz) { elem_acc->cbgp = (struct cache_bgp_primitives *) malloc(cb_size); memset(elem_acc->cbgp, 0, cb_size); pkt_to_cache_bgp_primitives(elem_acc->cbgp, pbgp, config.what_to_count); } else elem_acc->cbgp = NULL; elem_acc->packet_counter += data->pkt_num; elem_acc->flow_counter += data->flo_num; elem_acc->bytes_counter += data->pkt_len; elem_acc->tcp_flags = data->tcp_flags; elem_acc->signature = hash; if (config.what_to_count & COUNT_CLASS) { elem_acc->packet_counter += data->cst.pa; elem_acc->bytes_counter += data->cst.ba; elem_acc->flow_counter += data->cst.fa; } elem_acc->next = NULL; lru_elem_ptr[config.buckets] = elem_acc; return; } } } void set_reset_flag(struct acc *elem) { elem->reset_flag = TRUE; } void reset_counters(struct acc *elem) { elem->reset_flag = FALSE; elem->packet_counter = 0; elem->bytes_counter = 0; elem->tcp_flags = 0; memcpy(&elem->rstamp, &cycle_stamp, sizeof(struct timeval)); } pmacct-0.14.0/src/signals.c0000644000175000017500000001264411737616342014465 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* defines */ #define __SIGNALS_C /* includes */ #include "pmacct.h" /* extern */ extern struct plugins_list_entry *plugin_list; /* functions */ /* Each signal handler contains a final signal() call that reinstalls the called handler again; such behaviour deals with SysV signal handling */ void startup_handle_falling_child() { int i, j; j = waitpid(-1, 0, WNOHANG); for (i = 0; i < MAX_N_PLUGINS; i++) { if (!failed_plugins[i]) { failed_plugins[i] = j; break; } } signal(SIGCHLD, startup_handle_falling_child); } void handle_falling_child() { struct plugins_list_entry *list = NULL; int j, ret; /* we first scan failed_plugins[] array for plugins failed during the startup phase: when we are building plugins_list, we cannot arbitrarily delete nodes (plugins) from it */ for (j = 0; j < MAX_N_PLUGINS; j++) { if (failed_plugins[j]) { list = search_plugin_by_pid(failed_plugins[j]); if (list) { Log(LOG_INFO, "INFO: connection lost to '%s-%s'; closing connection.\n", list->name, list->type.string); close(list->pipe[1]); delete_pipe_channel(list->pipe[1]); ret = delete_plugin_by_id(list->id); if (!ret) { Log(LOG_INFO, "INFO: no more plugins active. Shutting down.\n"); if (config.pidfile) remove_pid_file(config.pidfile); exit(1); } } failed_plugins[j] = 0; } else break; } j = waitpid(-1, 0, WNOHANG); list = search_plugin_by_pid(j); if (list) { Log(LOG_INFO, "INFO: connection lost to '%s-%s'; closing connection.\n", list->name, list->type.string); close(list->pipe[1]); delete_pipe_channel(list->pipe[1]); ret = delete_plugin_by_id(list->id); if (!ret) { Log(LOG_INFO, "INFO: no more plugins active. Shutting down.\n"); if (config.pidfile) remove_pid_file(config.pidfile); exit(1); } } signal(SIGCHLD, handle_falling_child); } void ignore_falling_child() { while (waitpid(-1, 0, WNOHANG) > 0) sql_writers.retired++; signal(SIGCHLD, ignore_falling_child); } void my_sigint_handler(int signum) { struct plugins_list_entry *list = plugins_list; if (config.syslog) closelog(); /* We are about to exit, but it may take a while - because of the wait() call. Let's release collector's socket to improve turn- around times when restarting the daemon */ if (config.acct_type == ACCT_NF || config.acct_type == ACCT_SF) close(config.sock); #if defined (IRIX) || (SOLARIS) signal(SIGCHLD, SIG_IGN); #else signal(SIGCHLD, ignore_falling_child); #endif signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); fill_pipe_buffer(); sleep(2); /* XXX: we should really choose an adaptive value here. It should be closely bound to, say, biggest plugin_buffer_size value */ while (list) { if (memcmp(list->type.string, "core", sizeof("core"))) kill(list->pid, SIGINT); list = list->next; } wait(NULL); Log(LOG_INFO, "OK: Exiting ...\n"); if (config.acct_type == ACCT_PM && !config.uacctd_group /* XXX */) { if (config.dev) { if (pcap_stats(glob_pcapt, &ps) < 0) printf("\npcap_stats: %s\n", pcap_geterr(glob_pcapt)); printf("\n%u packets received by filter\n", ps.ps_recv); printf("%u packets dropped by kernel\n", ps.ps_drop); } } if (config.pidfile) remove_pid_file(config.pidfile); exit(0); } void reload() { int logf; if (config.syslog) { closelog(); logf = parse_log_facility(config.syslog); if (logf == ERR) { config.syslog = NULL; Log(LOG_WARNING, "WARN: specified syslog facility is not supported; logging to console.\n"); } openlog(NULL, LOG_PID, logf); Log(LOG_INFO, "INFO: Start logging ...\n"); } else if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile); } signal(SIGHUP, reload); } void push_stats() { time_t now = time(NULL); if (config.acct_type == ACCT_PM) { if (config.dev) { if (pcap_stats(glob_pcapt, &ps) < 0) Log(LOG_INFO, "\npcap_stats: %s\n", pcap_geterr(glob_pcapt)); Log(LOG_NOTICE, "\n%s: (%u) %u packets received by filter\n", config.dev, now, ps.ps_recv); Log(LOG_NOTICE, "%s: (%u) %u packets dropped by kernel\n", config.dev, now, ps.ps_drop); } } else if (config.acct_type == ACCT_NF || config.acct_type == ACCT_SF) print_status_table(now, XFLOW_STATUS_TABLE_SZ); signal(SIGUSR1, push_stats); } void reload_maps() { reload_map = FALSE; reload_map_bgp_thread = FALSE; if (config.refresh_maps) { reload_map = TRUE; reload_map_bgp_thread = TRUE; } signal(SIGUSR2, reload_maps); } pmacct-0.14.0/src/xflow_status.c0000644000175000017500000002100511611553756015556 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #define __XFLOW_STATUS_C /* includes */ #include "pmacct.h" /* functions */ u_int32_t hash_status_table(u_int32_t data, struct sockaddr *sa, u_int32_t size) { int hash = -1; if (sa->sa_family == AF_INET) hash = (data ^ ((struct sockaddr_in *)sa)->sin_addr.s_addr) % size; #if defined ENABLE_IPV6 else if (sa->sa_family == AF_INET6) { u_int32_t tmp; memcpy(&tmp, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr+12, 4); hash = (data ^ tmp) % size; // hash = (data ^ ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr32[3]) % size; } #endif return hash; } struct xflow_status_entry *search_status_table(struct sockaddr *sa, u_int32_t aux1, int hash, int num_entries) { struct xflow_status_entry *entry = xflow_status_table[hash], *saved = NULL; u_int16_t port; cycle_again: if (entry) { saved = entry; if (!sa_addr_cmp(sa, &entry->agent_addr) && aux1 == entry->aux1); /* FOUND IT: we are finished */ else { entry = entry->next; goto cycle_again; } } else { if (xflow_status_table_entries < num_entries) { entry = malloc(sizeof(struct xflow_status_entry)); if (!entry) goto error; else { memset(entry, 0, sizeof(struct xflow_status_entry)); sa_to_addr(sa, &entry->agent_addr, &port); entry->aux1 = aux1; entry->seqno = 0; entry->next = FALSE; if (!saved) xflow_status_table[hash] = entry; else saved->next = entry; xflow_status_table_error = TRUE; xflow_status_table_entries++; } } else { error: if (xflow_status_table_error) { Log(LOG_ERR, "ERROR: unable to allocate more entries into the xFlow status table.\n"); xflow_status_table_error = FALSE; return NULL; } } } return entry; } void update_status_table(struct xflow_status_entry *entry, u_int32_t seqno) { if (!entry) return; if (!entry->seqno || config.nfacctd_disable_checks) { // entry->seqno = seqno; /* Init */ entry->counters.good++; } else { if (seqno == entry->seqno+entry->inc) { // entry->seqno = seqno; entry->counters.good++; } else { char agent_ip_address[INET6_ADDRSTRLEN]; char collector_ip_address[INET6_ADDRSTRLEN]; char null_ip_address[] = "0.0.0.0"; addr_to_str(agent_ip_address, &entry->agent_addr); if (config.nfacctd_ip) memcpy(collector_ip_address, config.nfacctd_ip, MAX(strlen(config.nfacctd_ip), INET6_ADDRSTRLEN)); else strcpy(collector_ip_address, null_ip_address); Log(LOG_WARNING, "WARN: expecting flow '%u' but received '%u' collector=%s:%u agent=%s:%u\n", entry->seqno+entry->inc, seqno, collector_ip_address, config.nfacctd_port, agent_ip_address, entry->aux1); if (seqno > entry->seqno+entry->inc) { // entry->counters.missed += (seqno-entry->seqno); entry->counters.jumps_f++; // entry->seqno = seqno; } else { entry->counters.jumps_b++; // entry->seqno--; // entry->seqno = seqno; } } } entry->seqno = seqno; } void print_status_table(time_t now, int buckets) { struct xflow_status_entry *entry; char nf [] = "NetFlow"; char sf [] = "sFlow"; char uf [] = "unknown"; char *ftype = uf; int idx; char agent_ip_address[INET6_ADDRSTRLEN]; char collector_ip_address[INET6_ADDRSTRLEN]; char null_ip_address[] = "0.0.0.0"; if (config.acct_type == ACCT_NF) ftype = nf; if (config.acct_type == ACCT_SF) ftype = sf; for (idx = 0; idx < buckets; idx++) { entry = xflow_status_table[idx]; bucket_cycle: if (entry) { addr_to_str(agent_ip_address, &entry->agent_addr); if (config.nfacctd_ip) memcpy(collector_ip_address, config.nfacctd_ip, MAX(strlen(config.nfacctd_ip), INET6_ADDRSTRLEN)); else strcpy(collector_ip_address, null_ip_address); Log(LOG_NOTICE, "\n+++\n"); Log(LOG_NOTICE, "%s statistics collector=%s:%u agent=%s:%u (%u):\n", ftype, collector_ip_address, config.nfacctd_port, agent_ip_address, entry->aux1, now); Log(LOG_NOTICE, "Good datagrams: %u\n", entry->counters.good); Log(LOG_NOTICE, "Forward jumps: %u\n", entry->counters.jumps_f); Log(LOG_NOTICE, "Backward jumps: %u\n", entry->counters.jumps_b); Log(LOG_NOTICE, "---\n"); if (entry->next) { entry = entry->next; goto bucket_cycle; } } } Log(LOG_NOTICE, "+++\n"); Log(LOG_NOTICE, "Total bad %s datagrams: %u (%u)\n", ftype, xflow_tot_bad_datagrams, now); Log(LOG_NOTICE, "---\n\n"); } struct xflow_status_entry_sampling * search_smp_if_status_table(struct xflow_status_entry_sampling *sentry, u_int32_t interface) { while (sentry) { if (sentry->interface == interface) return sentry; sentry = sentry->next; } return NULL; } struct xflow_status_entry_sampling * search_smp_id_status_table(struct xflow_status_entry_sampling *sentry, u_int16_t sampler_id, u_int8_t return_unequal) { /* Match a samplerID or, if samplerID within a data record is zero and no match was possible, then return the last samplerID defined -- last part is C7600 workaround */ while (sentry) { if (sentry->sampler_id == sampler_id || (return_unequal && !sampler_id && !sentry->next)) return sentry; sentry = sentry->next; } return NULL; } struct xflow_status_entry_sampling * create_smp_entry_status_table(struct xflow_status_entry *entry) { struct xflow_status_entry_sampling *sentry = entry->sampling, *new = NULL; if (sentry) { while (sentry->next) sentry = sentry->next; } if (xflow_status_table_entries < XFLOW_STATUS_TABLE_MAX_ENTRIES) { new = malloc(sizeof(struct xflow_status_entry_sampling)); if (!new) { if (smp_entry_status_table_memerr) { Log(LOG_ERR, "ERROR: unable to allocate more entries into the xflow renormalization table.\n"); smp_entry_status_table_memerr = FALSE; } } else { if (!entry->sampling) entry->sampling = new; if (sentry) sentry->next = new; new->next = FALSE; smp_entry_status_table_memerr = TRUE; xflow_status_table_entries++; } } return new; } struct xflow_status_entry_class * search_class_id_status_table(struct xflow_status_entry_class *centry, pm_class_t class_id) { while (centry) { if (centry->class_id == class_id) return centry; centry = centry->next; } return NULL; } struct xflow_status_entry_class * create_class_entry_status_table(struct xflow_status_entry *entry) { struct xflow_status_entry_class *centry = entry->class, *new = NULL; if (centry) { while (centry->next) centry = centry->next; } if (xflow_status_table_entries < XFLOW_STATUS_TABLE_MAX_ENTRIES) { new = malloc(sizeof(struct xflow_status_entry_class)); if (!new) { if (class_entry_status_table_memerr) { Log(LOG_ERR, "ERROR: unable to allocate more entries into the xflow classification table.\n"); class_entry_status_table_memerr = FALSE; } } else { if (!entry->class) entry->class = new; if (centry) centry->next = new; new->next = FALSE; class_entry_status_table_memerr = TRUE; xflow_status_table_entries++; } } return new; } pm_class_t NF_evaluate_classifiers(struct xflow_status_entry_class *entry, pm_class_t *class_id) { struct xflow_status_entry_class *centry; centry = search_class_id_status_table(entry, *class_id); if (centry) { return centry->class_int_id; } return 0; } void set_vector_f_status(struct packet_ptrs_vector *pptrsv) { pptrsv->vlan4.f_status = pptrsv->v4.f_status; pptrsv->mpls4.f_status = pptrsv->v4.f_status; pptrsv->vlanmpls4.f_status = pptrsv->v4.f_status; #if defined ENABLE_IPV6 pptrsv->v6.f_status = pptrsv->v4.f_status; pptrsv->vlan6.f_status = pptrsv->v4.f_status; pptrsv->vlanmpls6.f_status = pptrsv->v4.f_status; pptrsv->mpls6.f_status = pptrsv->v4.f_status; #endif } pmacct-0.14.0/src/nfv9_template.c0000644000175000017500000002715311521007256015570 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ /* defines */ #define __NFV9_TEMPLATE_C /* includes */ #include "pmacct.h" #include "nfacctd.h" #include "pmacct-data.h" void handle_template_v9(struct template_hdr_v9 *hdr, struct packet_ptrs *pptrs, u_int16_t tpl_type, u_int32_t sid, u_int16_t *pens) { struct template_cache_entry *tpl; if (pens) *pens = FALSE; /* 0 NetFlow v9, 2 IPFIX */ if (tpl_type == 0 || tpl_type == 2) { if (tpl = find_template_v9(hdr->template_id, pptrs, tpl_type, sid)) refresh_template_v9(hdr, tpl, pptrs, tpl_type, sid, pens); else insert_template_v9(hdr, pptrs, tpl_type, sid, pens); } /* 1 NetFlow v9, 3 IPFIX */ else if (tpl_type == 1 || tpl_type == 3) { if (tpl = find_template_v9(hdr->template_id, pptrs, tpl_type, sid)) refresh_opt_template_v9(hdr, tpl, pptrs, tpl_type, sid); else insert_opt_template_v9(hdr, pptrs, tpl_type, sid); } } struct template_cache_entry *find_template_v9(u_int16_t id, struct packet_ptrs *pptrs, u_int16_t tpl_type, u_int32_t sid) { struct template_cache_entry *ptr; u_int16_t modulo = (ntohs(id)%tpl_cache.num); ptr = tpl_cache.c[modulo]; while (ptr) { if ((ptr->template_id == id) && (!sa_addr_cmp((struct sockaddr *)pptrs->f_agent, &ptr->agent)) && (ptr->source_id == sid)) return ptr; else ptr = ptr->next; } return NULL; } struct template_cache_entry *insert_template_v9(struct template_hdr_v9 *hdr, struct packet_ptrs *pptrs, u_int16_t tpl_type, u_int32_t sid, u_int16_t *pens) { struct template_cache_entry *ptr, *prevptr = NULL; struct template_field_v9 *field; u_int16_t modulo = (ntohs(hdr->template_id)%tpl_cache.num), count; u_int16_t num = ntohs(hdr->num), type, port; u_int8_t ipfix_ebit; u_char *tpl; ptr = tpl_cache.c[modulo]; while (ptr) { prevptr = ptr; ptr = ptr->next; } ptr = malloc(sizeof(struct template_cache_entry)); if (!ptr) { Log(LOG_ERR, "ERROR ( default/core ): Unable to allocate enough memory for a new Template Cache Entry.\n"); return NULL; } memset(ptr, 0, sizeof(struct template_cache_entry)); sa_to_addr((struct sockaddr *)pptrs->f_agent, &ptr->agent, &port); ptr->source_id = sid; ptr->template_id = hdr->template_id; ptr->template_type = 0; ptr->num = num; log_template_v9_header(ptr, pptrs, tpl_type, sid); count = num; tpl = (u_char *) hdr; tpl += NfTplHdrV9Sz; field = (struct template_field_v9 *)tpl; while (count) { ipfix_ebit = FALSE; type = ntohs(field->type); if (type & IPFIX_TPL_EBIT) { ipfix_ebit = TRUE; type ^= IPFIX_TPL_EBIT; if (pens) (*pens)++; } log_template_v9_field(type, ptr->len, ntohs(field->len)); /* Cisco ASA hack */ switch (type) { case NF9_ASA_XLATE_IPV4_SRC_ADDR: type = NF9_XLATE_IPV4_SRC_ADDR; break; case NF9_ASA_XLATE_IPV4_DST_ADDR: type = NF9_XLATE_IPV4_DST_ADDR; break; case NF9_ASA_XLATE_L4_SRC_PORT: type = NF9_XLATE_L4_SRC_PORT; break; case NF9_ASA_XLATE_L4_DST_PORT: type = NF9_XLATE_L4_DST_PORT; break; default: break; } if (type < NF9_MAX_DEFINED_FIELD) { ptr->tpl[type].off = ptr->len; ptr->tpl[type].len = ntohs(field->len); ptr->len += ptr->tpl[type].len; } else ptr->len += ntohs(field->len); count--; if (ipfix_ebit) field++; /* skip 32-bits ahead */ field++; } if (prevptr) prevptr->next = ptr; else tpl_cache.c[modulo] = ptr; log_template_v9_footer(ptr->len); return ptr; } void refresh_template_v9(struct template_hdr_v9 *hdr, struct template_cache_entry *tpl, struct packet_ptrs *pptrs, u_int16_t tpl_type, u_int32_t sid, u_int16_t *pens) { struct template_cache_entry *next; struct template_field_v9 *field; u_int16_t count, num = ntohs(hdr->num), type, port; u_int8_t ipfix_ebit; u_char *ptr; next = tpl->next; memset(tpl, 0, sizeof(struct template_cache_entry)); sa_to_addr((struct sockaddr *)pptrs->f_agent, &tpl->agent, &port); tpl->source_id = sid; tpl->template_id = hdr->template_id; tpl->template_type = 0; tpl->num = num; tpl->next = next; log_template_v9_header(tpl, pptrs, tpl_type, sid); count = num; ptr = (u_char *) hdr; ptr += NfTplHdrV9Sz; field = (struct template_field_v9 *)ptr; while (count) { ipfix_ebit = FALSE; type = ntohs(field->type); if (type & IPFIX_TPL_EBIT) { ipfix_ebit = TRUE; type ^= IPFIX_TPL_EBIT; if (pens) (*pens)++; } log_template_v9_field(type, tpl->len, ntohs(field->len)); /* Cisco ASA hack */ switch (type) { case NF9_ASA_XLATE_IPV4_SRC_ADDR: type = NF9_XLATE_IPV4_SRC_ADDR; break; case NF9_ASA_XLATE_IPV4_DST_ADDR: type = NF9_XLATE_IPV4_DST_ADDR; break; case NF9_ASA_XLATE_L4_SRC_PORT: type = NF9_XLATE_L4_SRC_PORT; break; case NF9_ASA_XLATE_L4_DST_PORT: type = NF9_XLATE_L4_DST_PORT; break; default: break; } if (type < NF9_MAX_DEFINED_FIELD) { tpl->tpl[type].off = tpl->len; tpl->tpl[type].len = ntohs(field->len); tpl->len += tpl->tpl[type].len; } else tpl->len += ntohs(field->len); count--; if (ipfix_ebit) field++; /* skip 32-bits ahead */ field++; } log_template_v9_footer(tpl->len); } void log_template_v9_header(struct template_cache_entry *tpl, struct packet_ptrs *pptrs, u_int16_t tpl_type, u_int32_t sid) { struct host_addr a; u_char agent_addr[50]; u_int16_t agent_port, count, size; u_int8_t nf_version = 0; sa_to_addr((struct sockaddr *)pptrs->f_agent, &a, &agent_port); addr_to_str(agent_addr, &a); if (tpl_type == 0 || tpl_type == 1) nf_version = 9; else if (tpl_type == 2 || tpl_type == 3) nf_version = 10; Log(LOG_DEBUG, "DEBUG ( default/core ): NfV%u agent : %s:%u\n", nf_version, agent_addr, sid); Log(LOG_DEBUG, "DEBUG ( default/core ): NfV%u template type : %s\n", nf_version, ( tpl->template_type == 0 || tpl->template_type == 2 ) ? "flow" : "options"); Log(LOG_DEBUG, "DEBUG ( default/core ): NfV%u template ID : %u\n", nf_version, ntohs(tpl->template_id)); Log(LOG_DEBUG, "DEBUG ( default/core ): ----------------------------------------\n"); Log(LOG_DEBUG, "DEBUG ( default/core ): | field type | offset | size |\n"); } void log_template_v9_field(u_int16_t type, u_int16_t off, u_int16_t len) { if (type <= MAX_TPL_DESC_LIST && strlen(tpl_desc_list[type])) Log(LOG_DEBUG, "DEBUG ( default/core ): | %-18s | %6u | %6u |\n", tpl_desc_list[type], off, len); else Log(LOG_DEBUG, "DEBUG ( default/core ): | %-18u | %6u | %6u |\n", type, off, len); } void log_opt_template_v9_field(u_int16_t type, u_int16_t off, u_int16_t len) { if (type <= MAX_OPT_TPL_DESC_LIST && strlen(opt_tpl_desc_list[type])) Log(LOG_DEBUG, "DEBUG ( default/core ): | %-18s | %6u | %6u |\n", opt_tpl_desc_list[type], off, len); else Log(LOG_DEBUG, "DEBUG ( default/core ): | %-18u | %6u | %6u |\n", type, off, len); } void log_template_v9_footer(u_int16_t size) { Log(LOG_DEBUG, "DEBUG ( default/core ): ----------------------------------------\n"); Log(LOG_DEBUG, "DEBUG ( default/core ): Netflow V9/IPFIX record size : %u\n", size); Log(LOG_DEBUG, "DEBUG ( default/core ): \n"); } struct template_cache_entry *insert_opt_template_v9(void *hdr, struct packet_ptrs *pptrs, u_int16_t tpl_type, u_int32_t sid) { struct options_template_hdr_v9 *hdr_v9 = (struct options_template_hdr_v9 *) hdr; struct options_template_hdr_ipfix *hdr_v10 = (struct options_template_hdr_ipfix *) hdr; struct template_cache_entry *ptr, *prevptr = NULL; struct template_field_v9 *field; u_int16_t modulo, count, slen, olen, type, port, tid; u_char *tpl; /* NetFlow v9 */ if (tpl_type == 1) { modulo = ntohs(hdr_v9->template_id)%tpl_cache.num; tid = hdr_v9->template_id; slen = ntohs(hdr_v9->scope_len)/sizeof(struct template_field_v9); olen = ntohs(hdr_v9->option_len)/sizeof(struct template_field_v9); } /* IPFIX */ else if (tpl_type == 3) { modulo = ntohs(hdr_v10->template_id)%tpl_cache.num; tid = hdr_v10->template_id; slen = ntohs(hdr_v10->scope_count); olen = ntohs(hdr_v10->option_count)-slen; } ptr = tpl_cache.c[modulo]; while (ptr) { prevptr = ptr; ptr = ptr->next; } ptr = malloc(sizeof(struct template_cache_entry)); if (!ptr) { Log(LOG_ERR, "ERROR ( default/core ): Unable to allocate enough memory for a new Options Template Cache Entry.\n"); return NULL; } memset(ptr, 0, sizeof(struct template_cache_entry)); sa_to_addr((struct sockaddr *)pptrs->f_agent, &ptr->agent, &port); ptr->source_id = sid; ptr->template_id = tid; ptr->template_type = 1; ptr->num = olen+slen; log_template_v9_header(ptr, pptrs, tpl_type, sid); count = ptr->num; tpl = (u_char *) hdr; tpl += NfOptTplHdrV9Sz; field = (struct template_field_v9 *)tpl; while (count) { type = ntohs(field->type); log_opt_template_v9_field(type, ptr->len, ntohs(field->len)); if (type < NF9_MAX_DEFINED_FIELD) { ptr->tpl[type].off = ptr->len; ptr->tpl[type].len = ntohs(field->len); ptr->len += ptr->tpl[type].len; } else ptr->len += ntohs(field->len); count--; field++; } if (prevptr) prevptr->next = ptr; else tpl_cache.c[modulo] = ptr; log_template_v9_footer(ptr->len); return ptr; } void refresh_opt_template_v9(void *hdr, struct template_cache_entry *tpl, struct packet_ptrs *pptrs, u_int16_t tpl_type, u_int32_t sid) { struct options_template_hdr_v9 *hdr_v9 = (struct options_template_hdr_v9 *) hdr; struct options_template_hdr_ipfix *hdr_v10 = (struct options_template_hdr_ipfix *) hdr; struct template_cache_entry *next; struct template_field_v9 *field; u_int16_t slen, olen, count, type, port, tid; u_char *ptr; /* NetFlow v9 */ if (tpl_type == 1) { tid = hdr_v9->template_id; slen = ntohs(hdr_v9->scope_len)/sizeof(struct template_field_v9); olen = ntohs(hdr_v9->option_len)/sizeof(struct template_field_v9); } /* IPFIX */ else if (tpl_type == 3) { tid = hdr_v10->template_id; slen = ntohs(hdr_v10->scope_count); olen = ntohs(hdr_v10->option_count)-slen; } next = tpl->next; memset(tpl, 0, sizeof(struct template_cache_entry)); sa_to_addr((struct sockaddr *)pptrs->f_agent, &tpl->agent, &port); tpl->source_id = sid; tpl->template_id = tid; tpl->template_type = 1; tpl->num = olen+slen; tpl->next = next; log_template_v9_header(tpl, pptrs, tpl_type, sid); count = tpl->num; ptr = (u_char *) hdr; ptr += NfOptTplHdrV9Sz; field = (struct template_field_v9 *)ptr; while (count) { type = ntohs(field->type); log_opt_template_v9_field(type, tpl->len, ntohs(field->len)); if (type < NF9_MAX_DEFINED_FIELD) { tpl->tpl[type].off = tpl->len; tpl->tpl[type].len = ntohs(field->len); tpl->len += tpl->tpl[type].len; } else tpl->len += ntohs(field->len); count--; field++; } log_template_v9_footer(tpl->len); } pmacct-0.14.0/src/ip_flow.h0000644000175000017500000001037611241063623014455 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente */ /* 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. */ #ifndef _IP_FLOW_H_ #define _IP_FLOW_H_ /* defines */ #define FLOW_TABLE_HASHSZ 256 #define FLOW_GENERIC_LIFETIME 60 #define FLOW_TCPSYN_LIFETIME 60 #define FLOW_TCPEST_LIFETIME 432000 #define FLOW_TCPFIN_LIFETIME 30 #define FLOW_TCPRST_LIFETIME 10 #define FLOW_TABLE_PRUNE_INTERVAL 3600 #define FLOW_TABLE_EMER_PRUNE_INTERVAL 60 #define DEFAULT_FLOW_BUFFER_SIZE 16384000 /* 16 Mb */ struct context_chain { char *protocol; void *data; struct context_chain *next; }; /* structures */ struct ip_flow_common { /* [0] = forward flow data [1] = reverse flow data */ u_int16_t bucket; struct timeval last[2]; u_int32_t last_tcp_seq; u_int8_t tcp_flags[2]; u_int8_t proto; /* classifier hooks */ pm_class_t class[2]; struct class_st cst[2]; struct context_chain *cc[2]; /* conntrack hooks */ void (*conntrack_helper)(time_t, struct packet_ptrs *); }; struct ip_flow { struct ip_flow_common cmn; u_int32_t ip_src; u_int32_t ip_dst; u_int16_t port_src; u_int16_t port_dst; char *bgp_src; /* pointer to bgp_node structure for source prefix, if any */ char *bgp_dst; /* pointer to bgp_node structure for destination prefix, if any */ struct ip_flow *lru_next; struct ip_flow *lru_prev; struct ip_flow *next; struct ip_flow *prev; }; struct flow_lru_l { struct ip_flow *root; struct ip_flow *last; }; #if defined ENABLE_IPV6 struct ip_flow6 { struct ip_flow_common cmn; u_int32_t ip_src[4]; u_int32_t ip_dst[4]; u_int16_t port_src; u_int16_t port_dst; struct ip_flow6 *lru_next; struct ip_flow6 *lru_prev; struct ip_flow6 *next; struct ip_flow6 *prev; }; struct flow_lru_l6 { struct ip_flow6 *root; struct ip_flow6 *last; }; #endif #if (!defined __IP_FLOW_C) #define EXT extern #else #define EXT #endif /* prototypes */ EXT void init_ip_flow_handler(); /* wrapper */ EXT void init_ip4_flow_handler(); EXT void ip_flow_handler(struct packet_ptrs *); EXT void find_flow(struct timeval *, struct packet_ptrs *); EXT void create_flow(struct timeval *, struct ip_flow *, u_int8_t, unsigned int, struct packet_ptrs *, struct my_iphdr *, struct my_tlhdr *, unsigned int); EXT void prune_old_flows(struct timeval *); EXT unsigned int hash_flow(u_int32_t, u_int32_t, u_int16_t, u_int16_t, u_int8_t); EXT unsigned int normalize_flow(u_int32_t *, u_int32_t *, u_int16_t *, u_int16_t *); EXT unsigned int is_expired(struct timeval *, struct ip_flow_common *); EXT unsigned int is_expired_uni(struct timeval *, struct ip_flow_common *, unsigned int); EXT void evaluate_tcp_flags(struct timeval *, struct packet_ptrs *, struct ip_flow_common *, unsigned int); EXT void clear_tcp_flow_cmn(struct ip_flow_common *, unsigned int); #if defined ENABLE_IPV6 EXT void init_ip6_flow_handler(); EXT void ip_flow6_handler(struct packet_ptrs *); EXT unsigned int hash_flow6(u_int32_t, struct in6_addr *, struct in6_addr *); EXT unsigned int normalize_flow6(struct in6_addr *, struct in6_addr *, u_int16_t *, u_int16_t *); EXT void find_flow6(struct timeval *, struct packet_ptrs *); EXT void create_flow6(struct timeval *, struct ip_flow6 *, u_int8_t, unsigned int, struct packet_ptrs *, struct ip6_hdr *, struct my_tlhdr *, unsigned int); EXT void prune_old_flows6(struct timeval *); #endif /* global vars */ EXT struct ip_flow **ip_flow_table; EXT struct flow_lru_l flow_lru_list; #if defined ENABLE_IPV6 EXT struct ip_flow6 **ip_flow_table6; EXT struct flow_lru_l6 flow_lru_list6; #endif #undef EXT #endif /* _IP_FLOW_H_ */ pmacct-0.14.0/src/nfprobe_plugin/0000755000175000017500000000000011741267746015670 5ustar paolopaolopmacct-0.14.0/src/nfprobe_plugin/convtime.h0000644000175000017500000000345010530072467017654 0ustar paolopaolo/* * Copyright (c) 2001 Kevin Steves. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SFD_CONVTIME_H /* * Convert a time string into seconds; format is * a sequence of: * time[qualifier] * * Valid time qualifiers are: * seconds * s|S seconds * m|M minutes * h|H hours * d|D days * w|W weeks * * Examples: * 90m 90 minutes * 1h30m 90 minutes * 2d 2 days * 1w 1 week * * Return -1 if time string is invalid. */ long int convtime(const char *s); #endif /* _SFD_CONVTIME_H */ pmacct-0.14.0/src/nfprobe_plugin/netflow9.c0000644000175000017500000020216411741044542017573 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* * Originally based on softflowd which is: * * Copyright 2002 Damien Miller All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: netflow9.c,v 1.22 2012/04/10 14:53:22 paolo Exp $ */ #define __NFPROBE_NETFLOW9_C #include "common.h" #include "treetype.h" #include "nfprobe_plugin.h" #include "ip_flow.h" #include "classifier.h" /* Netflow v.9 */ struct NF9_HEADER { u_int16_t version, flows; u_int32_t uptime_ms, time_sec; u_int32_t package_sequence, source_id; } __packed; struct IPFIX_HEADER { u_int16_t version, len; u_int32_t time_sec; u_int32_t package_sequence, source_id; } __packed; struct NF9_FLOWSET_HEADER_COMMON { u_int16_t flowset_id, length; } __packed; struct NF9_TEMPLATE_FLOWSET_HEADER { struct NF9_FLOWSET_HEADER_COMMON c; u_int16_t template_id, count; } __packed; struct NF9_OPTIONS_TEMPLATE_FLOWSET_HEADER { struct NF9_FLOWSET_HEADER_COMMON c; u_int16_t template_id, scope_len; u_int16_t option_len; } __packed; struct NF9_TEMPLATE_FLOWSET_RECORD { u_int16_t type, length; } __packed; struct NF9_DATA_FLOWSET_HEADER { struct NF9_FLOWSET_HEADER_COMMON c; } __packed; #define NF9_TEMPLATE_FLOWSET_ID 0 #define NF9_OPTIONS_FLOWSET_ID 1 #define IPFIX_TEMPLATE_FLOWSET_ID 2 #define IPFIX_OPTIONS_FLOWSET_ID 3 #define NF9_MIN_RECORD_FLOWSET_ID 256 /* Flowset record types the we care about */ #define NF9_IN_BYTES 1 #define NF9_IN_PACKETS 2 #define NF9_FLOWS 3 #define NF9_IN_PROTOCOL 4 #define NF9_SRC_TOS 5 #define NF9_TCP_FLAGS 6 #define NF9_L4_SRC_PORT 7 #define NF9_IPV4_SRC_ADDR 8 #define NF9_SRC_MASK 9 #define NF9_INPUT_SNMP 10 /* ... */ #define NF9_L4_DST_PORT 11 #define NF9_IPV4_DST_ADDR 12 #define NF9_DST_MASK 13 #define NF9_OUTPUT_SNMP 14 /* ... */ #define NF9_SRC_AS 16 #define NF9_DST_AS 17 #define NF9_BGP_IPV4_NEXT_HOP 18 /* ... */ #define NF9_LAST_SWITCHED 21 #define NF9_FIRST_SWITCHED 22 #define NF9_OUT_BYTES 23 #define NF9_OUT_PACKETS 24 /* ... */ #define NF9_IPV6_SRC_ADDR 27 #define NF9_IPV6_DST_ADDR 28 /* ... */ #define NF9_FLOW_SAMPLER_ID 48 #define NF9_FLOW_SAMPLER_MODE 49 #define NF9_FLOW_SAMPLER_INTERVAL 50 #define NF9_IN_SRC_MAC 56 // #define NF9_OUT_DST_MAC 57 #define NF9_SRC_VLAN 58 #define NF9_DST_VLAN 59 #define NF9_IP_PROTOCOL_VERSION 60 #define NF9_DIRECTION 61 /* ... */ #define NF9_BGP_IPV6_NEXT_HOP 63 /* ... */ #define NF9_MPLS_LABEL_1 70 /* ... */ #define NF9_IN_DST_MAC 80 // #define NF9_OUT_SRC_MAC 81 /* ... */ #define NF9_FLOW_APPLICATION_DESC 94 #define NF9_FLOW_APPLICATION_ID 95 #define NF9_FLOW_APPLICATION_NAME 96 /* ... */ #define NF9_FLOW_EXPORTER 144 /* ... */ /* CUSTOM TYPES START HERE */ #define NF9_CUST_TAG 201 #define NF9_CUST_TAG2 202 /* CUSTOM TYPES END HERE */ /* OPTION SCOPES */ #define NF9_OPT_SCOPE_SYSTEM 1 /* Stuff pertaining to the templates that softflowd uses */ #define NF9_SOFTFLOWD_TEMPLATE_NRECORDS 35 struct NF9_SOFTFLOWD_TEMPLATE { struct NF9_TEMPLATE_FLOWSET_HEADER h; struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_TEMPLATE_NRECORDS]; u_int16_t tot_len; } __packed; #define NF9_OPTIONS_TEMPLATE_NRECORDS 4 struct NF9_OPTIONS_TEMPLATE { struct NF9_OPTIONS_TEMPLATE_FLOWSET_HEADER h; struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_OPTIONS_TEMPLATE_NRECORDS]; u_int16_t tot_len; } __packed; typedef void (*flow_to_flowset_handler) (char *, const struct FLOW *, int, int); struct NF9_INTERNAL_TEMPLATE_RECORD { flow_to_flowset_handler handler; u_int16_t length; }; struct NF9_INTERNAL_TEMPLATE { struct NF9_INTERNAL_TEMPLATE_RECORD r[NF9_SOFTFLOWD_TEMPLATE_NRECORDS]; u_int16_t tot_rec_len; }; struct NF9_INTERNAL_OPTIONS_TEMPLATE { struct NF9_INTERNAL_TEMPLATE_RECORD r[NF9_OPTIONS_TEMPLATE_NRECORDS]; u_int16_t tot_rec_len; }; /* softflowd data flowset types */ struct NF9_SOFTFLOWD_DATA_COMMON { u_int32_t last_switched, first_switched; u_int16_t ifindex_in, ifindex_out; u_int32_t bytes, packets, flows; u_int16_t src_port, dst_port; u_int8_t direction, protocol, tos; u_int8_t tcp_flags, ipproto; as_t src_as, dst_as; u_int8_t src_mac[6], dst_mac[6]; u_int16_t vlan; } __packed; struct NF9_SOFTFLOWD_DATA_V4 { u_int32_t src_addr, dst_addr, bgp_next_hop; struct NF9_SOFTFLOWD_DATA_COMMON c; } __packed; struct NF9_SOFTFLOWD_DATA_V6 { u_int8_t src_addr[16], dst_addr[16]; u_int8_t bgp_next_hop[16]; struct NF9_SOFTFLOWD_DATA_COMMON c; } __packed; /* Local data: templates and counters */ #define NF9_SOFTFLOWD_MAX_PACKET_SIZE 512 #define NF9_SOFTFLOWD_V4_TEMPLATE_ID 1024 #define NF9_SOFTFLOWD_V6_TEMPLATE_ID 2048 #define NF9_OPTIONS_TEMPLATE_ID 4096 #define NF9_DEFAULT_TEMPLATE_INTERVAL 18 static struct NF9_SOFTFLOWD_TEMPLATE v4_template; static struct NF9_INTERNAL_TEMPLATE v4_int_template; static struct NF9_SOFTFLOWD_TEMPLATE v4_template_out; static struct NF9_INTERNAL_TEMPLATE v4_int_template_out; static struct NF9_SOFTFLOWD_TEMPLATE v6_template; static struct NF9_INTERNAL_TEMPLATE v6_int_template; static struct NF9_SOFTFLOWD_TEMPLATE v6_template_out; static struct NF9_INTERNAL_TEMPLATE v6_int_template_out; static struct NF9_OPTIONS_TEMPLATE sampling_option_template; static struct NF9_INTERNAL_OPTIONS_TEMPLATE sampling_option_int_template; static struct NF9_OPTIONS_TEMPLATE class_option_template; static struct NF9_INTERNAL_OPTIONS_TEMPLATE class_option_int_template; static char ftoft_buf_0[sizeof(struct NF9_SOFTFLOWD_DATA_V6)]; static char ftoft_buf_1[sizeof(struct NF9_SOFTFLOWD_DATA_V6)]; static int nf9_pkts_until_template = -1; static u_int8_t send_options = FALSE; static u_int8_t send_sampling_option = FALSE; static u_int8_t send_class_option = FALSE; static void flow_to_flowset_input_handler(char *flowset, const struct FLOW *flow, int idx, int size) { u_int16_t rec16; rec16 = htons(flow->ifindex[idx]); memcpy(flowset, &rec16, size); } static void flow_to_flowset_output_handler(char *flowset, const struct FLOW *flow, int idx, int size) { u_int16_t rec16; rec16 = htons(flow->ifindex[idx ^ 1]); memcpy(flowset, &rec16, size); } static void flow_to_flowset_direction_handler(char *flowset, const struct FLOW *flow, int idx, int size) { u_int8_t rec8; rec8 = flow->direction[idx] ? (flow->direction[idx]-1) : 0; memcpy(flowset, &rec8, size); } static void flow_to_flowset_flows_handler(char *flowset, const struct FLOW *flow, int idx, int size) { u_int32_t rec32; rec32 = htonl(flow->flows[idx]); memcpy(flowset, &rec32, size); } static void flow_to_flowset_src_host_v4_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->addr[idx].v4, size); } static void flow_to_flowset_dst_host_v4_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->addr[idx ^ 1].v4, size); } static void flow_to_flowset_bgp_next_hop_v4_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->bgp_next_hop[idx].v4, size); } static void flow_to_flowset_src_nmask_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->mask[idx], size); } static void flow_to_flowset_dst_nmask_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->mask[idx ^ 1], size); } static void flow_to_flowset_src_host_v6_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->addr[idx].v6, size); } static void flow_to_flowset_dst_host_v6_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->addr[idx ^ 1].v6, size); } static void flow_to_flowset_bgp_next_hop_v6_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->bgp_next_hop[idx].v6, size); } static void flow_to_flowset_src_port_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->port[idx], size); } static void flow_to_flowset_dst_port_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->port[idx ^ 1], size); } static void flow_to_flowset_ip_tos_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->tos[idx], size); } static void flow_to_flowset_tcp_flags_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->tcp_flags[idx], size); } static void flow_to_flowset_ip_proto_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->protocol, size); } static void flow_to_flowset_src_as_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->as[idx], size); } static void flow_to_flowset_dst_as_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->as[idx ^ 1], size); } static void flow_to_flowset_src_mac_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->mac[idx], size); } static void flow_to_flowset_dst_mac_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->mac[idx ^ 1], size); } static void flow_to_flowset_vlan_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->vlan, size); } static void flow_to_flowset_mpls_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->mpls_label[idx], size); } static void flow_to_flowset_class_handler(char *flowset, const struct FLOW *flow, int idx, int size) { memcpy(flowset, &flow->class, size); } static void flow_to_flowset_tag_handler(char *flowset, const struct FLOW *flow, int idx, int size) { u_int32_t rec32; rec32 = htonl(flow->tag[idx]); memcpy(flowset, &rec32, size); } static void flow_to_flowset_tag2_handler(char *flowset, const struct FLOW *flow, int idx, int size) { u_int32_t rec32; rec32 = htonl(flow->tag2[idx]); memcpy(flowset, &rec32, size); } static void flow_to_flowset_sampler_id_handler(char *flowset, const struct FLOW *flow, int idx, int size) { u_int8_t rec8; rec8 = 1; memcpy(flowset, &rec8, size); } static void nf9_init_template(void) { int rcount, idx, flowset_id = 0; /* Let's enforce some defaults; if we are launched without an * aggregation, then let's choose one. If we don't have one or * more flow-distinguishing primitives, then let's add flow * aggregation info to the template */ if ( ! config.nfprobe_what_to_count ) { config.nfprobe_what_to_count |= COUNT_SRC_HOST; config.nfprobe_what_to_count |= COUNT_DST_HOST; config.nfprobe_what_to_count |= COUNT_SRC_PORT; config.nfprobe_what_to_count |= COUNT_DST_PORT; config.nfprobe_what_to_count |= COUNT_IP_PROTO; config.nfprobe_what_to_count |= COUNT_IP_TOS; } rcount = 0; bzero(&v4_template, sizeof(v4_template)); bzero(&v4_int_template, sizeof(v4_int_template)); bzero(&v4_template_out, sizeof(v4_template_out)); bzero(&v4_int_template_out, sizeof(v4_int_template_out)); if (config.nfprobe_version == 9) flowset_id = NF9_TEMPLATE_FLOWSET_ID; else if (config.nfprobe_version == 10) flowset_id = IPFIX_TEMPLATE_FLOWSET_ID; v4_template.r[rcount].type = htons(NF9_LAST_SWITCHED); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_LAST_SWITCHED); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].length = 4; rcount++; v4_template.r[rcount].type = htons(NF9_FIRST_SWITCHED); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_FIRST_SWITCHED); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].length = 4; rcount++; v4_template.r[rcount].type = htons(NF9_IN_BYTES); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].length = 4; // Cisco doesn't appear to do that (yet?) // v4_template_out.r[rcount].type = htons(NF9_OUT_BYTES); v4_template_out.r[rcount].type = htons(NF9_IN_BYTES); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].length = 4; rcount++; v4_template.r[rcount].type = htons(NF9_IN_PACKETS); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].length = 4; // Cisco doesn't appear to do that (yet?) // v4_template_out.r[rcount].type = htons(NF9_OUT_PACKETS); v4_template_out.r[rcount].type = htons(NF9_IN_PACKETS); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].length = 4; rcount++; v4_template.r[rcount].type = htons(NF9_IP_PROTOCOL_VERSION); v4_template.r[rcount].length = htons(1); v4_int_template.r[rcount].length = 1; v4_template_out.r[rcount].type = htons(NF9_IP_PROTOCOL_VERSION); v4_template_out.r[rcount].length = htons(1); v4_int_template_out.r[rcount].length = 1; rcount++; v4_template.r[rcount].type = htons(NF9_INPUT_SNMP); v4_template.r[rcount].length = htons(2); v4_int_template.r[rcount].handler = flow_to_flowset_input_handler; v4_int_template.r[rcount].length = 2; v4_template_out.r[rcount].type = htons(NF9_INPUT_SNMP); v4_template_out.r[rcount].length = htons(2); v4_int_template_out.r[rcount].handler = flow_to_flowset_input_handler; v4_int_template_out.r[rcount].length = 2; rcount++; v4_template.r[rcount].type = htons(NF9_OUTPUT_SNMP); v4_template.r[rcount].length = htons(2); v4_int_template.r[rcount].handler = flow_to_flowset_output_handler; v4_int_template.r[rcount].length = 2; v4_template_out.r[rcount].type = htons(NF9_OUTPUT_SNMP); v4_template_out.r[rcount].length = htons(2); v4_int_template_out.r[rcount].handler = flow_to_flowset_output_handler; v4_int_template_out.r[rcount].length = 2; rcount++; v4_template.r[rcount].type = htons(NF9_DIRECTION); v4_template.r[rcount].length = htons(1); v4_int_template.r[rcount].handler = flow_to_flowset_direction_handler; v4_int_template.r[rcount].length = 1; v4_template_out.r[rcount].type = htons(NF9_DIRECTION); v4_template_out.r[rcount].length = htons(1); v4_int_template_out.r[rcount].handler = flow_to_flowset_direction_handler; v4_int_template_out.r[rcount].length = 1; rcount++; if (config.nfprobe_what_to_count & COUNT_FLOWS) { v4_template.r[rcount].type = htons(NF9_FLOWS); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].handler = flow_to_flowset_flows_handler; v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_FLOWS); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].handler = flow_to_flowset_flows_handler; v4_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_SRC_HOST) { v4_template.r[rcount].type = htons(NF9_IPV4_SRC_ADDR); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].handler = flow_to_flowset_src_host_v4_handler; v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_IPV4_SRC_ADDR); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].handler = flow_to_flowset_src_host_v4_handler; v4_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_DST_HOST) { v4_template.r[rcount].type = htons(NF9_IPV4_DST_ADDR); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].handler = flow_to_flowset_dst_host_v4_handler; v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_IPV4_DST_ADDR); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].handler = flow_to_flowset_dst_host_v4_handler; v4_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_PEER_DST_IP) { v4_template.r[rcount].type = htons(NF9_BGP_IPV4_NEXT_HOP); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].handler = flow_to_flowset_bgp_next_hop_v4_handler; v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_BGP_IPV4_NEXT_HOP); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].handler = flow_to_flowset_bgp_next_hop_v4_handler; v4_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_SRC_NMASK) { v4_template.r[rcount].type = htons(NF9_SRC_MASK); v4_template.r[rcount].length = htons(1); v4_int_template.r[rcount].handler = flow_to_flowset_src_nmask_handler; v4_int_template.r[rcount].length = 1; v4_template_out.r[rcount].type = htons(NF9_SRC_MASK); v4_template_out.r[rcount].length = htons(1); v4_int_template_out.r[rcount].handler = flow_to_flowset_src_nmask_handler; v4_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & COUNT_DST_NMASK) { v4_template.r[rcount].type = htons(NF9_DST_MASK); v4_template.r[rcount].length = htons(1); v4_int_template.r[rcount].handler = flow_to_flowset_dst_nmask_handler; v4_int_template.r[rcount].length = 1; v4_template_out.r[rcount].type = htons(NF9_DST_MASK); v4_template_out.r[rcount].length = htons(1); v4_int_template_out.r[rcount].handler = flow_to_flowset_dst_nmask_handler; v4_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & COUNT_SRC_PORT) { v4_template.r[rcount].type = htons(NF9_L4_SRC_PORT); v4_template.r[rcount].length = htons(2); v4_int_template.r[rcount].handler = flow_to_flowset_src_port_handler; v4_int_template.r[rcount].length = 2; v4_template_out.r[rcount].type = htons(NF9_L4_SRC_PORT); v4_template_out.r[rcount].length = htons(2); v4_int_template_out.r[rcount].handler = flow_to_flowset_src_port_handler; v4_int_template_out.r[rcount].length = 2; rcount++; } if (config.nfprobe_what_to_count & COUNT_DST_PORT) { v4_template.r[rcount].type = htons(NF9_L4_DST_PORT); v4_template.r[rcount].length = htons(2); v4_int_template.r[rcount].handler = flow_to_flowset_dst_port_handler; v4_int_template.r[rcount].length = 2; v4_template_out.r[rcount].type = htons(NF9_L4_DST_PORT); v4_template_out.r[rcount].length = htons(2); v4_int_template_out.r[rcount].handler = flow_to_flowset_dst_port_handler; v4_int_template_out.r[rcount].length = 2; rcount++; } if (config.nfprobe_what_to_count & (COUNT_IP_TOS)) { v4_template.r[rcount].type = htons(NF9_SRC_TOS); v4_template.r[rcount].length = htons(1); v4_int_template.r[rcount].handler = flow_to_flowset_ip_tos_handler; v4_int_template.r[rcount].length = 1; v4_template_out.r[rcount].type = htons(NF9_SRC_TOS); v4_template_out.r[rcount].length = htons(1); v4_int_template_out.r[rcount].handler = flow_to_flowset_ip_tos_handler; v4_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & (COUNT_SRC_PORT|COUNT_DST_PORT)) { v4_template.r[rcount].type = htons(NF9_TCP_FLAGS); v4_template.r[rcount].length = htons(1); v4_int_template.r[rcount].handler = flow_to_flowset_tcp_flags_handler; v4_int_template.r[rcount].length = 1; v4_template_out.r[rcount].type = htons(NF9_TCP_FLAGS); v4_template_out.r[rcount].length = htons(1); v4_int_template_out.r[rcount].handler = flow_to_flowset_tcp_flags_handler; v4_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & COUNT_IP_PROTO) { v4_template.r[rcount].type = htons(NF9_IN_PROTOCOL); v4_template.r[rcount].length = htons(1); v4_int_template.r[rcount].handler = flow_to_flowset_ip_proto_handler; v4_int_template.r[rcount].length = 1; v4_template_out.r[rcount].type = htons(NF9_IN_PROTOCOL); v4_template_out.r[rcount].length = htons(1); v4_int_template_out.r[rcount].handler = flow_to_flowset_ip_proto_handler; v4_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & COUNT_SRC_AS) { v4_template.r[rcount].type = htons(NF9_SRC_AS); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].handler = flow_to_flowset_src_as_handler; v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_SRC_AS); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].handler = flow_to_flowset_src_as_handler; v4_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_DST_AS) { v4_template.r[rcount].type = htons(NF9_DST_AS); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].handler = flow_to_flowset_dst_as_handler; v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_DST_AS); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].handler = flow_to_flowset_dst_as_handler; v4_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_SRC_MAC) { v4_template.r[rcount].type = htons(NF9_IN_SRC_MAC); v4_template.r[rcount].length = htons(6); v4_int_template.r[rcount].handler = flow_to_flowset_src_mac_handler; v4_int_template.r[rcount].length = 6; v4_template_out.r[rcount].type = htons(NF9_OUT_SRC_MAC); v4_template_out.r[rcount].length = htons(6); v4_int_template_out.r[rcount].handler = flow_to_flowset_src_mac_handler; v4_int_template_out.r[rcount].length = 6; rcount++; } if (config.nfprobe_what_to_count & COUNT_DST_MAC) { v4_template.r[rcount].type = htons(NF9_IN_DST_MAC); v4_template.r[rcount].length = htons(6); v4_int_template.r[rcount].handler = flow_to_flowset_dst_mac_handler; v4_int_template.r[rcount].length = 6; v4_template_out.r[rcount].type = htons(NF9_OUT_DST_MAC); v4_template_out.r[rcount].length = htons(6); v4_int_template_out.r[rcount].handler = flow_to_flowset_dst_mac_handler; v4_int_template_out.r[rcount].length = 6; rcount++; } if (config.nfprobe_what_to_count & COUNT_VLAN) { v4_template.r[rcount].type = htons(NF9_SRC_VLAN); v4_template.r[rcount].length = htons(2); v4_int_template.r[rcount].handler = flow_to_flowset_vlan_handler; v4_int_template.r[rcount].length = 2; v4_template_out.r[rcount].type = htons(NF9_DST_VLAN); v4_template_out.r[rcount].length = htons(2); v4_int_template_out.r[rcount].handler = flow_to_flowset_vlan_handler; v4_int_template_out.r[rcount].length = 2; rcount++; } if (config.nfprobe_what_to_count & COUNT_ID) { v4_template.r[rcount].type = htons(NF9_CUST_TAG); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].handler = flow_to_flowset_tag_handler; v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_CUST_TAG); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].handler = flow_to_flowset_tag_handler; v4_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_ID2) { v4_template.r[rcount].type = htons(NF9_CUST_TAG2); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].handler = flow_to_flowset_tag2_handler; v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_CUST_TAG2); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].handler = flow_to_flowset_tag2_handler; v4_int_template_out.r[rcount].length = 4; rcount++; } if (config.sampling_rate || config.ext_sampling_rate) { v4_template.r[rcount].type = htons(NF9_FLOW_SAMPLER_ID); v4_template.r[rcount].length = htons(1); v4_int_template.r[rcount].handler = flow_to_flowset_sampler_id_handler; v4_int_template.r[rcount].length = 1; v4_template_out.r[rcount].type = htons(NF9_FLOW_SAMPLER_ID); v4_template_out.r[rcount].length = htons(1); v4_int_template_out.r[rcount].handler = flow_to_flowset_sampler_id_handler; v4_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & COUNT_CLASS) { v4_template.r[rcount].type = htons(NF9_FLOW_APPLICATION_ID); v4_template.r[rcount].length = htons(4); v4_int_template.r[rcount].handler = flow_to_flowset_class_handler; v4_int_template.r[rcount].length = 4; v4_template_out.r[rcount].type = htons(NF9_FLOW_APPLICATION_ID); v4_template_out.r[rcount].length = htons(4); v4_int_template_out.r[rcount].handler = flow_to_flowset_class_handler; v4_int_template_out.r[rcount].length = 4; rcount++; } v4_template.h.c.flowset_id = htons(flowset_id); v4_template.h.c.length = htons( sizeof(struct NF9_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount) ); v4_template.h.template_id = htons(NF9_SOFTFLOWD_V4_TEMPLATE_ID + config.nfprobe_id); v4_template.h.count = htons(rcount); v4_template.tot_len = sizeof(struct NF9_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount); v4_template_out.h.c.flowset_id = htons(flowset_id); v4_template_out.h.c.length = htons( sizeof(struct NF9_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount) ); v4_template_out.h.template_id = htons(NF9_SOFTFLOWD_V4_TEMPLATE_ID + config.nfprobe_id + 1); v4_template_out.h.count = htons(rcount); v4_template_out.tot_len = sizeof(struct NF9_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount); assert(rcount < NF9_SOFTFLOWD_TEMPLATE_NRECORDS); for (idx = 0, v4_int_template.tot_rec_len = 0, v4_int_template_out.tot_rec_len = 0; idx < rcount; idx++) { v4_int_template.tot_rec_len += v4_int_template.r[idx].length; v4_int_template_out.tot_rec_len += v4_int_template_out.r[idx].length; } rcount = 0; bzero(&v6_template, sizeof(v6_template)); bzero(&v6_int_template, sizeof(v6_int_template)); bzero(&v6_template_out, sizeof(v6_template_out)); bzero(&v6_int_template_out, sizeof(v6_int_template_out)); v6_template.r[rcount].type = htons(NF9_LAST_SWITCHED); v6_template.r[rcount].length = htons(4); v6_int_template.r[rcount].length = 4; v6_template_out.r[rcount].type = htons(NF9_LAST_SWITCHED); v6_template_out.r[rcount].length = htons(4); v6_int_template_out.r[rcount].length = 4; rcount++; v6_template.r[rcount].type = htons(NF9_FIRST_SWITCHED); v6_template.r[rcount].length = htons(4); v6_int_template.r[rcount].length = 4; v6_template_out.r[rcount].type = htons(NF9_FIRST_SWITCHED); v6_template_out.r[rcount].length = htons(4); v6_int_template_out.r[rcount].length = 4; rcount++; v6_template.r[rcount].type = htons(NF9_IN_BYTES); v6_template.r[rcount].length = htons(4); v6_int_template.r[rcount].length = 4; // Cisco doesn't appear to do that (yet?) // v6_template_out.r[rcount].type = htons(NF9_OUT_BYTES); v6_template_out.r[rcount].type = htons(NF9_IN_BYTES); v6_template_out.r[rcount].length = htons(4); v6_int_template_out.r[rcount].length = 4; rcount++; v6_template.r[rcount].type = htons(NF9_IN_PACKETS); v6_template.r[rcount].length = htons(4); v6_int_template.r[rcount].length = 4; // Cisco doesn't appear to do that (yet?) // v6_template_out.r[rcount].type = htons(NF9_OUT_PACKETS); v6_template_out.r[rcount].type = htons(NF9_IN_PACKETS); v6_template_out.r[rcount].length = htons(4); v6_int_template_out.r[rcount].length = 4; rcount++; v6_template.r[rcount].type = htons(NF9_IP_PROTOCOL_VERSION); v6_template.r[rcount].length = htons(1); v6_int_template.r[rcount].length = 1; v6_template_out.r[rcount].type = htons(NF9_IP_PROTOCOL_VERSION); v6_template_out.r[rcount].length = htons(1); v6_int_template_out.r[rcount].length = 1; rcount++; v6_template.r[rcount].type = htons(NF9_INPUT_SNMP); v6_template.r[rcount].length = htons(2); v6_int_template.r[rcount].handler = flow_to_flowset_input_handler; v6_int_template.r[rcount].length = 2; v6_template_out.r[rcount].type = htons(NF9_INPUT_SNMP); v6_template_out.r[rcount].length = htons(2); v6_int_template_out.r[rcount].handler = flow_to_flowset_input_handler; v6_int_template_out.r[rcount].length = 2; rcount++; v6_template.r[rcount].type = htons(NF9_OUTPUT_SNMP); v6_template.r[rcount].length = htons(2); v6_int_template.r[rcount].handler = flow_to_flowset_output_handler; v6_int_template.r[rcount].length = 2; v6_template_out.r[rcount].type = htons(NF9_OUTPUT_SNMP); v6_template_out.r[rcount].length = htons(2); v6_int_template_out.r[rcount].handler = flow_to_flowset_output_handler; v6_int_template_out.r[rcount].length = 2; rcount++; v6_template.r[rcount].type = htons(NF9_DIRECTION); v6_template.r[rcount].length = htons(1); v6_int_template.r[rcount].handler = flow_to_flowset_direction_handler; v6_int_template.r[rcount].length = 1; v6_template_out.r[rcount].type = htons(NF9_DIRECTION); v6_template_out.r[rcount].length = htons(1); v6_int_template_out.r[rcount].handler = flow_to_flowset_direction_handler; v6_int_template_out.r[rcount].length = 1; rcount++; if (config.nfprobe_what_to_count & COUNT_FLOWS) { v6_template.r[rcount].type = htons(NF9_FLOWS); v6_template.r[rcount].length = htons(4); v6_int_template.r[rcount].handler = flow_to_flowset_flows_handler; v6_int_template.r[rcount].length = 4; v6_template_out.r[rcount].type = htons(NF9_FLOWS); v6_template_out.r[rcount].length = htons(4); v6_int_template_out.r[rcount].handler = flow_to_flowset_flows_handler; v6_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_SRC_HOST) { v6_template.r[rcount].type = htons(NF9_IPV6_SRC_ADDR); v6_template.r[rcount].length = htons(16); v6_int_template.r[rcount].handler = flow_to_flowset_src_host_v6_handler; v6_int_template.r[rcount].length = 16; v6_template_out.r[rcount].type = htons(NF9_IPV6_SRC_ADDR); v6_template_out.r[rcount].length = htons(16); v6_int_template_out.r[rcount].handler = flow_to_flowset_src_host_v6_handler; v6_int_template_out.r[rcount].length = 16; rcount++; } if (config.nfprobe_what_to_count & COUNT_DST_HOST) { v6_template.r[rcount].type = htons(NF9_IPV6_DST_ADDR); v6_template.r[rcount].length = htons(16); v6_int_template.r[rcount].handler = flow_to_flowset_dst_host_v6_handler; v6_int_template.r[rcount].length = 16; v6_template_out.r[rcount].type = htons(NF9_IPV6_DST_ADDR); v6_template_out.r[rcount].length = htons(16); v6_int_template_out.r[rcount].handler = flow_to_flowset_dst_host_v6_handler; v6_int_template_out.r[rcount].length = 16; rcount++; } if (config.nfprobe_what_to_count & COUNT_PEER_DST_IP) { v6_template.r[rcount].type = htons(NF9_BGP_IPV6_NEXT_HOP); v6_template.r[rcount].length = htons(16); v6_int_template.r[rcount].handler = flow_to_flowset_bgp_next_hop_v6_handler; v6_int_template.r[rcount].length = 16; v6_template_out.r[rcount].type = htons(NF9_BGP_IPV6_NEXT_HOP); v6_template_out.r[rcount].length = htons(16); v6_int_template_out.r[rcount].handler = flow_to_flowset_bgp_next_hop_v6_handler; v6_int_template_out.r[rcount].length = 16; rcount++; } if (config.nfprobe_what_to_count & COUNT_SRC_NMASK) { v6_template.r[rcount].type = htons(NF9_SRC_MASK); v6_template.r[rcount].length = htons(1); v6_int_template.r[rcount].handler = flow_to_flowset_src_nmask_handler; v6_int_template.r[rcount].length = 1; v6_template_out.r[rcount].type = htons(NF9_SRC_MASK); v6_template_out.r[rcount].length = htons(1); v6_int_template_out.r[rcount].handler = flow_to_flowset_src_nmask_handler; v6_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & COUNT_DST_NMASK) { v6_template.r[rcount].type = htons(NF9_DST_MASK); v6_template.r[rcount].length = htons(1); v6_int_template.r[rcount].handler = flow_to_flowset_dst_nmask_handler; v6_int_template.r[rcount].length = 1; v6_template_out.r[rcount].type = htons(NF9_DST_MASK); v6_template_out.r[rcount].length = htons(1); v6_int_template_out.r[rcount].handler = flow_to_flowset_dst_nmask_handler; v6_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & (COUNT_IP_TOS)) { v6_template.r[rcount].type = htons(NF9_SRC_TOS); v6_template.r[rcount].length = htons(1); v6_int_template.r[rcount].handler = flow_to_flowset_ip_tos_handler; v6_int_template.r[rcount].length = 1; v6_template_out.r[rcount].type = htons(NF9_SRC_TOS); v6_template_out.r[rcount].length = htons(1); v6_int_template_out.r[rcount].handler = flow_to_flowset_ip_tos_handler; v6_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & COUNT_SRC_PORT) { v6_template.r[rcount].type = htons(NF9_L4_SRC_PORT); v6_template.r[rcount].length = htons(2); v6_int_template.r[rcount].handler = flow_to_flowset_src_port_handler; v6_int_template.r[rcount].length = 2; v6_template_out.r[rcount].type = htons(NF9_L4_SRC_PORT); v6_template_out.r[rcount].length = htons(2); v6_int_template_out.r[rcount].handler = flow_to_flowset_src_port_handler; v6_int_template_out.r[rcount].length = 2; rcount++; } if (config.nfprobe_what_to_count & COUNT_DST_PORT) { v6_template.r[rcount].type = htons(NF9_L4_DST_PORT); v6_template.r[rcount].length = htons(2); v6_int_template.r[rcount].handler = flow_to_flowset_dst_port_handler; v6_int_template.r[rcount].length = 2; v6_template_out.r[rcount].type = htons(NF9_L4_DST_PORT); v6_template_out.r[rcount].length = htons(2); v6_int_template_out.r[rcount].handler = flow_to_flowset_dst_port_handler; v6_int_template_out.r[rcount].length = 2; rcount++; } if (config.nfprobe_what_to_count & (COUNT_SRC_PORT|COUNT_DST_PORT)) { v6_template.r[rcount].type = htons(NF9_TCP_FLAGS); v6_template.r[rcount].length = htons(1); v6_int_template.r[rcount].handler = flow_to_flowset_tcp_flags_handler; v6_int_template.r[rcount].length = 1; v6_template_out.r[rcount].type = htons(NF9_TCP_FLAGS); v6_template_out.r[rcount].length = htons(1); v6_int_template_out.r[rcount].handler = flow_to_flowset_tcp_flags_handler; v6_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & COUNT_IP_PROTO) { v6_template.r[rcount].type = htons(NF9_IN_PROTOCOL); v6_template.r[rcount].length = htons(1); v6_int_template.r[rcount].handler = flow_to_flowset_ip_proto_handler; v6_int_template.r[rcount].length = 1; v6_template_out.r[rcount].type = htons(NF9_IN_PROTOCOL); v6_template_out.r[rcount].length = htons(1); v6_int_template_out.r[rcount].handler = flow_to_flowset_ip_proto_handler; v6_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & COUNT_SRC_AS) { v6_template.r[rcount].type = htons(NF9_SRC_AS); v6_template.r[rcount].length = htons(4); v6_int_template.r[rcount].handler = flow_to_flowset_src_as_handler; v6_int_template.r[rcount].length = 4; v6_template_out.r[rcount].type = htons(NF9_SRC_AS); v6_template_out.r[rcount].length = htons(4); v6_int_template_out.r[rcount].handler = flow_to_flowset_src_as_handler; v6_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_DST_AS) { v6_template.r[rcount].type = htons(NF9_DST_AS); v6_template.r[rcount].length = htons(4); v6_int_template.r[rcount].handler = flow_to_flowset_dst_as_handler; v6_int_template.r[rcount].length = 4; v6_template_out.r[rcount].type = htons(NF9_DST_AS); v6_template_out.r[rcount].length = htons(4); v6_int_template_out.r[rcount].handler = flow_to_flowset_dst_as_handler; v6_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_SRC_MAC) { v6_template.r[rcount].type = htons(NF9_IN_SRC_MAC); v6_template.r[rcount].length = htons(6); v6_int_template.r[rcount].handler = flow_to_flowset_src_mac_handler; v6_int_template.r[rcount].length = 6; v6_template_out.r[rcount].type = htons(NF9_OUT_SRC_MAC); v6_template_out.r[rcount].length = htons(6); v6_int_template_out.r[rcount].handler = flow_to_flowset_src_mac_handler; v6_int_template_out.r[rcount].length = 6; rcount++; } if (config.nfprobe_what_to_count & COUNT_DST_MAC) { v6_template.r[rcount].type = htons(NF9_IN_DST_MAC); v6_template.r[rcount].length = htons(6); v6_int_template.r[rcount].handler = flow_to_flowset_dst_mac_handler; v6_int_template.r[rcount].length = 6; v6_template_out.r[rcount].type = htons(NF9_OUT_DST_MAC); v6_template_out.r[rcount].length = htons(6); v6_int_template_out.r[rcount].handler = flow_to_flowset_dst_mac_handler; v6_int_template_out.r[rcount].length = 6; rcount++; } if (config.nfprobe_what_to_count & COUNT_VLAN) { v6_template.r[rcount].type = htons(NF9_SRC_VLAN); v6_template.r[rcount].length = htons(2); v6_int_template.r[rcount].handler = flow_to_flowset_vlan_handler; v6_int_template.r[rcount].length = 2; v6_template_out.r[rcount].type = htons(NF9_DST_VLAN); v6_template_out.r[rcount].length = htons(2); v6_int_template_out.r[rcount].handler = flow_to_flowset_vlan_handler; v6_int_template_out.r[rcount].length = 2; rcount++; } if (config.nfprobe_what_to_count & COUNT_ID) { v6_template.r[rcount].type = htons(NF9_CUST_TAG); v6_template.r[rcount].length = htons(4); v6_int_template.r[rcount].handler = flow_to_flowset_tag_handler; v6_int_template.r[rcount].length = 4; v6_template_out.r[rcount].type = htons(NF9_CUST_TAG); v6_template_out.r[rcount].length = htons(4); v6_int_template_out.r[rcount].handler = flow_to_flowset_tag_handler; v6_int_template_out.r[rcount].length = 4; rcount++; } if (config.nfprobe_what_to_count & COUNT_ID2) { v6_template.r[rcount].type = htons(NF9_CUST_TAG2); v6_template.r[rcount].length = htons(4); v6_int_template.r[rcount].handler = flow_to_flowset_tag2_handler; v6_int_template.r[rcount].length = 4; v6_template_out.r[rcount].type = htons(NF9_CUST_TAG2); v6_template_out.r[rcount].length = htons(4); v6_int_template_out.r[rcount].handler = flow_to_flowset_tag2_handler; v6_int_template_out.r[rcount].length = 4; rcount++; } if (config.sampling_rate || config.ext_sampling_rate) { v6_template.r[rcount].type = htons(NF9_FLOW_SAMPLER_ID); v6_template.r[rcount].length = htons(1); v6_int_template.r[rcount].handler = flow_to_flowset_sampler_id_handler; v6_int_template.r[rcount].length = 1; v6_template_out.r[rcount].type = htons(NF9_FLOW_SAMPLER_ID); v6_template_out.r[rcount].length = htons(1); v6_int_template_out.r[rcount].handler = flow_to_flowset_sampler_id_handler; v6_int_template_out.r[rcount].length = 1; rcount++; } if (config.nfprobe_what_to_count & COUNT_CLASS) { v6_template.r[rcount].type = htons(NF9_FLOW_APPLICATION_ID); v6_template.r[rcount].length = htons(4); v6_int_template.r[rcount].handler = flow_to_flowset_class_handler; v6_int_template.r[rcount].length = 4; v6_template_out.r[rcount].type = htons(NF9_FLOW_APPLICATION_ID); v6_template_out.r[rcount].length = htons(4); v6_int_template_out.r[rcount].handler = flow_to_flowset_class_handler; v6_int_template_out.r[rcount].length = 4; rcount++; } v6_template.h.c.flowset_id = htons(flowset_id); v6_template.h.c.length = htons( sizeof(struct NF9_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount) ); v6_template.h.template_id = htons(NF9_SOFTFLOWD_V6_TEMPLATE_ID + config.nfprobe_id); v6_template.h.count = htons(rcount); v6_template.tot_len = sizeof(struct NF9_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount); v6_template_out.h.c.flowset_id = htons(flowset_id); v6_template_out.h.c.length = htons( sizeof(struct NF9_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount) ); v6_template_out.h.template_id = htons(NF9_SOFTFLOWD_V6_TEMPLATE_ID + config.nfprobe_id + 1); v6_template_out.h.count = htons(rcount); v6_template_out.tot_len = sizeof(struct NF9_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount); assert(rcount < NF9_SOFTFLOWD_TEMPLATE_NRECORDS); for (idx = 0, v6_int_template.tot_rec_len = 0, v6_int_template_out.tot_rec_len = 0; idx < rcount; idx++) { v6_int_template.tot_rec_len += v6_int_template.r[idx].length; v6_int_template_out.tot_rec_len += v6_int_template_out.r[idx].length; } } static void nf9_init_options_template(void) { int rcount, idx, slen = 0, flowset_id = 0, scope = 0; switch (config.nfprobe_source_ha.family) { case AF_INET: slen = 4; break; case AF_INET6: slen = 16; break; default: slen = 4; break; } rcount = 0; bzero(&sampling_option_template, sizeof(sampling_option_template)); bzero(&sampling_option_int_template, sizeof(sampling_option_int_template)); if (config.nfprobe_version == 9) { flowset_id = NF9_OPTIONS_FLOWSET_ID; scope = NF9_OPT_SCOPE_SYSTEM; } else if (config.nfprobe_version == 10) { flowset_id = IPFIX_OPTIONS_FLOWSET_ID; scope = NF9_FLOW_EXPORTER; } sampling_option_template.r[rcount].type = htons(scope); sampling_option_template.r[rcount].length = htons(slen); sampling_option_int_template.r[rcount].length = slen; rcount++; sampling_option_template.r[rcount].type = htons(NF9_FLOW_SAMPLER_ID); sampling_option_template.r[rcount].length = htons(1); sampling_option_int_template.r[rcount].length = 1; rcount++; sampling_option_template.r[rcount].type = htons(NF9_FLOW_SAMPLER_MODE); sampling_option_template.r[rcount].length = htons(1); sampling_option_int_template.r[rcount].length = 1; rcount++; sampling_option_template.r[rcount].type = htons(NF9_FLOW_SAMPLER_INTERVAL); sampling_option_template.r[rcount].length = htons(4); sampling_option_int_template.r[rcount].length = 4; rcount++; sampling_option_template.h.c.flowset_id = htons(flowset_id); sampling_option_template.h.c.length = htons( sizeof(struct NF9_OPTIONS_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount) ); sampling_option_template.h.template_id = htons(NF9_OPTIONS_TEMPLATE_ID + config.nfprobe_id ); if (config.nfprobe_version == 9) { sampling_option_template.h.scope_len = htons(4); /* NF9_OPT_SCOPE_SYSTEM */ sampling_option_template.h.option_len = htons(12); /* NF9_FLOW_SAMPLER_ID + NF9_FLOW_SAMPLER_MODE + NF9_FLOW_SAMPLER_INTERVAL */ } else if (config.nfprobe_version == 10) { sampling_option_template.h.scope_len = htons(3+1); /* IPFIX twist: NF9_FLOW_SAMPLER_ID + NF9_FLOW_SAMPLER_MODE + NF9_FLOW_SAMPLER_INTERVAL + NF9_OPT_SCOPE_SYSTEM */ sampling_option_template.h.option_len = htons(1); /* IPFIX twist: NF9_OPT_SCOPE_SYSTEM */ } sampling_option_template.tot_len = sizeof(struct NF9_OPTIONS_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount); for (idx = 0, sampling_option_int_template.tot_rec_len = 0; idx < rcount; idx++) sampling_option_int_template.tot_rec_len += sampling_option_int_template.r[idx].length; rcount = 0; bzero(&class_option_template, sizeof(class_option_template)); bzero(&class_option_int_template, sizeof(class_option_int_template)); class_option_template.r[rcount].type = htons(scope); class_option_template.r[rcount].length = htons(4); class_option_int_template.r[rcount].length = 4; rcount++; class_option_template.r[rcount].type = htons(NF9_FLOW_APPLICATION_ID); class_option_template.r[rcount].length = htons(4); class_option_int_template.r[rcount].length = 4; rcount++; class_option_template.r[rcount].type = htons(NF9_FLOW_APPLICATION_NAME); class_option_template.r[rcount].length = htons(16); class_option_int_template.r[rcount].length = 16; rcount++; class_option_template.h.c.flowset_id = htons(flowset_id); class_option_template.h.c.length = htons( sizeof(struct NF9_OPTIONS_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount) ); class_option_template.h.template_id = htons(NF9_OPTIONS_TEMPLATE_ID + 1 + config.nfprobe_id ); if (config.nfprobe_version == 9) { class_option_template.h.scope_len = htons(4); /* NF9_OPT_SCOPE_SYSTEM */ class_option_template.h.option_len = htons(8); /* NF9_FLOW_APPLICATION_ID + NF9_FLOW_APPLICATION_NAME */ } else if (config.nfprobe_version == 10) { class_option_template.h.scope_len = htons(2+1); /* IPFIX twist: NF9_FLOW_APPLICATION_ID + NF9_FLOW_APPLICATION_NAME + NF9_OPT_SCOPE_SYSTEM */ class_option_template.h.option_len = htons(1); /* IPFIX twist: NF9_OPT_SCOPE_SYSTEM */ } class_option_template.tot_len = sizeof(struct NF9_OPTIONS_TEMPLATE_FLOWSET_HEADER) + (sizeof(struct NF9_TEMPLATE_FLOWSET_RECORD) * rcount); for (idx = 0, class_option_int_template.tot_rec_len = 0; idx < rcount; idx++) class_option_int_template.tot_rec_len += class_option_int_template.r[idx].length; } static int nf_flow_to_flowset(const struct FLOW *flow, u_char *packet, u_int len, const struct timeval *system_boot_time, u_int *len_used, int direction) { u_int freclen, ret_len, nflows, idx; u_int32_t rec32; u_int8_t rec8; char *ftoft_ptr_0 = ftoft_buf_0; char *ftoft_ptr_1 = ftoft_buf_1; int flow_direction[2]; bzero(ftoft_buf_0, sizeof(ftoft_buf_0)); bzero(ftoft_buf_1, sizeof(ftoft_buf_1)); *len_used = nflows = ret_len = 0; flow_direction[0] = (flow->direction[0] == DIRECTION_UNKNOWN) ? DIRECTION_IN : flow->direction[0]; flow_direction[1] = (flow->direction[1] == DIRECTION_UNKNOWN) ? DIRECTION_IN : flow->direction[1]; if (direction == flow_direction[0]) { rec32 = htonl(timeval_sub_ms(&flow->flow_last, system_boot_time)); memcpy(ftoft_ptr_0, &rec32, 4); ftoft_ptr_0 += 4; rec32 = htonl(timeval_sub_ms(&flow->flow_start, system_boot_time)); memcpy(ftoft_ptr_0, &rec32, 4); ftoft_ptr_0 += 4; rec32 = htonl(flow->octets[0]); memcpy(ftoft_ptr_0, &rec32, 4); ftoft_ptr_0 += 4; rec32 = htonl(flow->packets[0]); memcpy(ftoft_ptr_0, &rec32, 4); ftoft_ptr_0 += 4; switch (flow->af) { case AF_INET: rec8 = 4; memcpy(ftoft_ptr_0, &rec8, 1); ftoft_ptr_0 += 1; if (flow_direction[0] == DIRECTION_IN) { for (idx = 5; v4_int_template.r[idx].length; idx++) { v4_int_template.r[idx].handler(ftoft_ptr_0, flow, 0, v4_int_template.r[idx].length); ftoft_ptr_0 += v4_int_template.r[idx].length; } freclen = v4_int_template.tot_rec_len; } else if (flow_direction[0] == DIRECTION_OUT) { for (idx = 5; v4_int_template_out.r[idx].length; idx++) { v4_int_template_out.r[idx].handler(ftoft_ptr_0, flow, 0, v4_int_template_out.r[idx].length); ftoft_ptr_0 += v4_int_template_out.r[idx].length; } freclen = v4_int_template_out.tot_rec_len; } break; case AF_INET6: rec8 = 6; memcpy(ftoft_ptr_0, &rec8, 1); ftoft_ptr_0 += 1; if (flow_direction[0] == DIRECTION_IN) { for (idx = 5; v6_int_template.r[idx].length; idx++) { v6_int_template.r[idx].handler(ftoft_ptr_0, flow, 0, v6_int_template.r[idx].length); ftoft_ptr_0 += v6_int_template.r[idx].length; } freclen = v6_int_template.tot_rec_len; } else if (flow_direction[0] == DIRECTION_OUT) { for (idx = 5; v6_int_template_out.r[idx].length; idx++) { v6_int_template_out.r[idx].handler(ftoft_ptr_0, flow, 0, v6_int_template_out.r[idx].length); ftoft_ptr_0 += v6_int_template_out.r[idx].length; } freclen = v6_int_template_out.tot_rec_len; } break; default: return (-1); } } if (direction == flow_direction[1]) { rec32 = htonl(timeval_sub_ms(&flow->flow_last, system_boot_time)); memcpy(ftoft_ptr_1, &rec32, 4); ftoft_ptr_1 += 4; rec32 = htonl(timeval_sub_ms(&flow->flow_start, system_boot_time)); memcpy(ftoft_ptr_1, &rec32, 4); ftoft_ptr_1 += 4; rec32 = htonl(flow->octets[1]); memcpy(ftoft_ptr_1, &rec32, 4); ftoft_ptr_1 += 4; rec32 = htonl(flow->packets[1]); memcpy(ftoft_ptr_1, &rec32, 4); ftoft_ptr_1 += 4; switch (flow->af) { case AF_INET: rec8 = 4; memcpy(ftoft_ptr_1, &rec8, 1); ftoft_ptr_1 += 1; if (flow_direction[1] == DIRECTION_IN) { for (idx = 5; v4_int_template.r[idx].length; idx++) { v4_int_template.r[idx].handler(ftoft_ptr_1, flow, 1, v4_int_template.r[idx].length); ftoft_ptr_1 += v4_int_template.r[idx].length; } freclen = v4_int_template.tot_rec_len; } else if (flow_direction[1] == DIRECTION_OUT) { for (idx = 5; v4_int_template_out.r[idx].length; idx++) { v4_int_template_out.r[idx].handler(ftoft_ptr_1, flow, 1, v4_int_template_out.r[idx].length); ftoft_ptr_1 += v4_int_template_out.r[idx].length; } freclen = v4_int_template_out.tot_rec_len; } break; case AF_INET6: rec8 = 6; memcpy(ftoft_ptr_1, &rec8, 1); ftoft_ptr_1 += 1; if (flow_direction[1] == DIRECTION_IN) { for (idx = 5; v6_int_template.r[idx].length; idx++) { v6_int_template.r[idx].handler(ftoft_ptr_1, flow, 1, v6_int_template.r[idx].length); ftoft_ptr_1 += v6_int_template.r[idx].length; } freclen = v6_int_template.tot_rec_len; } else if (flow_direction[1] == DIRECTION_OUT) { for (idx = 5; v6_int_template_out.r[idx].length; idx++) { v6_int_template_out.r[idx].handler(ftoft_ptr_1, flow, 1, v6_int_template_out.r[idx].length); ftoft_ptr_1 += v6_int_template_out.r[idx].length; } freclen = v6_int_template_out.tot_rec_len; } break; default: return (-1); } } if (flow->octets[0] > 0 && direction == flow_direction[0]) { if (ret_len + freclen > len) return (-1); memcpy(packet + ret_len, ftoft_buf_0, freclen); ret_len += freclen; nflows++; } if (flow->octets[1] > 0 && direction == flow_direction[1]) { if (ret_len + freclen > len) return (-1); memcpy(packet + ret_len, ftoft_buf_1, freclen); ret_len += freclen; nflows++; } *len_used = ret_len; return (nflows); } static int nf_sampling_option_to_flowset(u_char *packet, u_int len, const struct timeval *system_boot_time, u_int *len_used) { u_int freclen, ret_len, nflows; u_int32_t rec32; u_int8_t rec8; char *ftoft_ptr_0 = ftoft_buf_0; bzero(ftoft_buf_0, sizeof(ftoft_buf_0)); *len_used = nflows = ret_len = 0; /* NF9_OPT_SCOPE_SYSTEM */ switch (config.nfprobe_source_ha.family) { case AF_INET: memcpy(ftoft_ptr_0, &config.nfprobe_source_ha.address.ipv4, 4); ftoft_ptr_0 += 4; break; #if defined ENABLE_IPV6 case AF_INET6: memcpy(ftoft_ptr_0, &config.nfprobe_source_ha.address.ipv6, 16); ftoft_ptr_0 += 16; break; #endif default: memset(ftoft_ptr_0, 0, 4); ftoft_ptr_0 += 4; break; } rec8 = 1; /* NF9_FLOW_SAMPLER_ID */ memcpy(ftoft_ptr_0, &rec8, 1); ftoft_ptr_0 += 1; rec8 = 0x02; /* NF9_FLOW_SAMPLER_MODE */ memcpy(ftoft_ptr_0, &rec8, 1); ftoft_ptr_0 += 1; if (config.sampling_rate) rec32 = htonl(config.sampling_rate); /* NF9_FLOW_SAMPLER_INTERVAL */ else if (config.ext_sampling_rate) rec32 = htonl(config.ext_sampling_rate); /* NF9_FLOW_SAMPLER_INTERVAL */ memcpy(ftoft_ptr_0, &rec32, 4); ftoft_ptr_0 += 4; freclen = sampling_option_int_template.tot_rec_len; if (ret_len + freclen > len) return (-1); memcpy(packet + ret_len, ftoft_buf_0, freclen); ret_len += freclen; nflows++; *len_used = ret_len; return (nflows); } static int nf_class_option_to_flowset(u_int idx, u_char *packet, u_int len, const struct timeval *system_boot_time, u_int *len_used) { u_int freclen, ret_len, nflows; char *ftoft_ptr_0 = ftoft_buf_0; bzero(ftoft_buf_0, sizeof(ftoft_buf_0)); *len_used = nflows = ret_len = 0; /* NF9_OPT_SCOPE_SYSTEM */ switch (config.nfprobe_source_ha.family) { case AF_INET: memcpy(ftoft_ptr_0, &config.nfprobe_source_ha.address.ipv4, 4); ftoft_ptr_0 += 4; break; #if defined ENABLE_IPV6 case AF_INET6: memcpy(ftoft_ptr_0, &config.nfprobe_source_ha.address.ipv6, 16); ftoft_ptr_0 += 16; break; #endif default: memset(ftoft_ptr_0, 0, 4); ftoft_ptr_0 += 4; break; } /* NF9_FLOW_APPLICATION_ID */ memcpy(ftoft_ptr_0, &class[idx].id, 4); ftoft_ptr_0 += 4; /* NF9_FLOW_APPLICATION_NAME */ strlcpy(ftoft_ptr_0, class[idx].protocol, 16); ftoft_ptr_0 += 16; freclen = class_option_int_template.tot_rec_len; if (ret_len + freclen > len) return (-1); memcpy(packet + ret_len, ftoft_buf_0, freclen); ret_len += freclen; nflows++; *len_used = ret_len; return (nflows); } /* * Given an array of expired flows, send netflow v9 report packets * Returns number of packets sent or -1 on error */ int send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock, u_int64_t *flows_exported, struct timeval *system_boot_time, int verbose_flag, u_int8_t engine_type, u_int8_t engine_id) { struct NF9_HEADER *nf9; struct IPFIX_HEADER *nf10; struct NF9_DATA_FLOWSET_HEADER *dh; struct timeval now; u_int offset, last_af, flow_j, num_packets, inc, last_valid; u_int num_class, class_j; int direction, new_direction; socklen_t errsz; int err, r, flow_i, class_i; u_char packet[NF9_SOFTFLOWD_MAX_PACKET_SIZE]; u_int8_t *sid_ptr; gettimeofday(&now, NULL); if (nf9_pkts_until_template == -1) { nf9_init_template(); nf9_init_options_template(); nf9_pkts_until_template = 0; } num_packets = 0; num_class = pmct_find_first_free(); for (direction = DIRECTION_IN; direction <= DIRECTION_OUT; direction++) { last_valid = 0; new_direction = TRUE; for (flow_j = 0, class_j = 0; flow_j < num_flows;) { bzero(packet, sizeof(packet)); if (config.nfprobe_version == 9) { nf9 = (struct NF9_HEADER *)packet; nf9->version = htons(9); nf9->flows = 0; /* Filled as we go, htons at end */ nf9->uptime_ms = htonl(timeval_sub_ms(&now, system_boot_time)); nf9->time_sec = htonl(time(NULL)); nf9->package_sequence = htonl(++(*flows_exported)); nf9->source_id = 0; sid_ptr = (u_int8_t *) &nf9->source_id; sid_ptr[2] = engine_type; sid_ptr[3] = engine_id; offset = sizeof(*nf9); } else if (config.nfprobe_version == 10) { nf10 = (struct IPFIX_HEADER *)packet; nf10->version = htons(10); nf10->len = 0; nf10->time_sec = htonl(time(NULL)); nf10->package_sequence = htonl(++(*flows_exported)); nf10->source_id = 0; sid_ptr = (u_int8_t *) &nf10->source_id; sid_ptr[2] = engine_type; sid_ptr[3] = engine_id; offset = sizeof(*nf10); } /* Refresh template headers if we need to */ if (nf9_pkts_until_template <= 0) { u_int16_t flows = 0, tot_len = 0; memcpy(packet + offset, &v4_template, v4_template.tot_len); offset += v4_template.tot_len; flows++; tot_len += v4_template.tot_len; memcpy(packet + offset, &v4_template_out, v4_template_out.tot_len); offset += v4_template_out.tot_len; flows++; tot_len += v4_template_out.tot_len; memcpy(packet + offset, &v6_template, v6_template.tot_len); offset += v6_template.tot_len; flows++; /*XXX: shall v6 templates be issued only if v6 is enabled? */ tot_len += v6_template.tot_len; memcpy(packet + offset, &v6_template_out, v6_template_out.tot_len); offset += v6_template_out.tot_len; flows++; tot_len += v6_template_out.tot_len; if (config.sampling_rate || config.ext_sampling_rate) { memcpy(packet + offset, &sampling_option_template, sampling_option_template.tot_len); offset += sampling_option_template.tot_len; flows++; tot_len += sampling_option_template.tot_len; send_options = TRUE; send_sampling_option = TRUE; } if (config.nfprobe_what_to_count & COUNT_CLASS) { memcpy(packet + offset, &class_option_template, class_option_template.tot_len); offset += class_option_template.tot_len; flows++; tot_len += class_option_template.tot_len; send_options = TRUE; send_options = TRUE; send_class_option = TRUE; } nf9_pkts_until_template = NF9_DEFAULT_TEMPLATE_INTERVAL; if (config.nfprobe_version == 9) nf9->flows = flows; } dh = NULL; last_af = 0; for (flow_i = 0, class_i = 0; flow_i + flow_j < num_flows; flow_i++) { /* Shall we send a new flowset header? */ if (dh == NULL || (!send_options && (flows[flow_i + flow_j]->af != last_af || new_direction)) || send_sampling_option || (send_class_option && !class_i) ) { if (dh != NULL) { if (offset % 4 != 0) { /* Pad to multiple of 4 */ dh->c.length += 4 - (offset % 4); offset += 4 - (offset % 4); } /* Finalise last header */ dh->c.length = htons(dh->c.length); } if (offset + sizeof(*dh) > sizeof(packet)) { /* Mark header is finished */ dh = NULL; break; } dh = (struct NF9_DATA_FLOWSET_HEADER *) (packet + offset); if (send_options) { if (send_sampling_option) { dh->c.flowset_id = sampling_option_template.h.template_id; // last_af = 0; new_direction = TRUE; } else if (send_class_option) { dh->c.flowset_id = class_option_template.h.template_id; // last_af = 0; new_direction = TRUE; } } else { if (flows[flow_i + flow_j]->af == AF_INET) { if (direction == DIRECTION_IN) dh->c.flowset_id = v4_template.h.template_id; else if (direction == DIRECTION_OUT) dh->c.flowset_id = v4_template_out.h.template_id; } else if (flows[flow_i + flow_j]->af == AF_INET6) { if (direction == DIRECTION_IN) dh->c.flowset_id = v6_template.h.template_id; else if (direction == DIRECTION_OUT) dh->c.flowset_id = v6_template_out.h.template_id; } // last_af = flows[flow_i + flow_j]->af; /* XXX */ } last_valid = offset; new_direction = FALSE; dh->c.length = sizeof(*dh); /* Filled as we go */ offset += sizeof(*dh); } /* Send flowset data over */ if (send_options) { if (send_sampling_option) { r = nf_sampling_option_to_flowset(packet + offset, sizeof(packet) - offset, system_boot_time, &inc); send_sampling_option = FALSE; } else if (send_class_option) { r = nf_class_option_to_flowset(class_i + class_j, packet + offset, sizeof(packet) - offset, system_boot_time, &inc); if (r > 0) class_i += r; if (class_i + class_j >= num_class) send_class_option = FALSE; } } else r = nf_flow_to_flowset(flows[flow_i + flow_j], packet + offset, sizeof(packet) - offset, system_boot_time, &inc, direction); /* Wrap up */ if (r <= 0) { /* yank off data header, if we had to go back */ if (last_valid) offset = last_valid; if (r < 0) break; } else { offset += inc; dh->c.length += inc; if (config.nfprobe_version == 9) nf9->flows += r; last_valid = 0; /* Don't clobber this header now */ if (verbose_flag) { if (config.nfprobe_version == 9) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): Building NetFlow v9 packet: offset = %d, template ID = %d, total len = %d, # elements = %d\n", config.name, config.type, offset, ntohs(dh->c.flowset_id), dh->c.length, nf9->flows); } else if (config.nfprobe_version == 10) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): Building IPFIX packet: offset = %d, template ID = %d, total len = %d\n", config.name, config.type, offset, ntohs(dh->c.flowset_id), dh->c.length); } } if (send_options) { if (!send_sampling_option && !send_class_option) { send_options = FALSE; } flow_i--; } else last_af = flows[flow_i + flow_j]->af; /* XXX */ } } /* Don't finish header if it has already been done */ if (dh != NULL) { if (offset % 4 != 0) { /* Pad to multiple of 4 */ dh->c.length += 4 - (offset % 4); offset += 4 - (offset % 4); } /* Finalise last header */ dh->c.length = htons(dh->c.length); } if ((config.nfprobe_version == 9 && nf9->flows > 0) || (config.nfprobe_version == 10 && offset > 20)) { /* 20: IPFIX header + IPFIX Flowset header */ if (config.nfprobe_version == 9) nf9->flows = htons(nf9->flows); else if (config.nfprobe_version == 10) nf10->len = htons(offset); if (verbose_flag) Log(LOG_DEBUG, "DEBUG ( %s/%s ): Sending NetFlow v9/IPFIX packet: len = %d\n", config.name, config.type, offset); errsz = sizeof(err); /* Clear ICMP errors */ getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz); if (send(nfsock, packet, (size_t)offset, 0) == -1) return (-1); num_packets++; nf9_pkts_until_template--; } else --(*flows_exported); class_j += class_i; flow_j += flow_i; } } return (num_packets); } pmacct-0.14.0/src/nfprobe_plugin/Makefile.in0000644000175000017500000000210210530072467017715 0ustar paolopaolo# $Id: Makefile.in,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $ prefix=@prefix@ exec_prefix=@exec_prefix@ bindir=@bindir@ sbindir=@sbindir@ libexecdir=@libexecdir@ datadir=@datadir@ mandir=@mandir@ sysconfdir=@sysconfdir@ srcdir=@srcdir@ top_srcdir=@top_srcdir@ VPATH=@srcdir@ CC=@CC@ DEFS=@DEFS@ LDFLAGS=@LDFLAGS@ CFLAGS=$(DEFS) -I$(srcdir) -I.. @CFLAGS@ CPPFLAGS=@CPPFLAGS@ LIBS=@LIBS@ INSTALL=@INSTALL@ RANLIB=@RANLIB@ #CFLAGS+=-DFLOW_RB # Use red-black tree for flows CFLAGS+=-DFLOW_SPLAY # Use splay tree for flows CFLAGS+=-DEXPIRY_RB # Use red-black tree for expiry events #CFLAGS+=-DEXPIRY_SPLAY # Use splay tree for expiry events TARGETS=libnfprobe_plugin.a COMMON=convtime.o strlcat.o all: $(TARGETS) libnfprobe_plugin.a: nfprobe_plugin.o netflow1.o netflow5.o netflow9.o $(COMMON) ar rc $@ netflow1.o netflow5.o netflow9.o nfprobe_plugin.o $(COMMON) $(RANLIB) $@ clean: rm -f $(TARGETS) *.o core *.core realclean: clean rm -rf autom4te.cache Makefile config.log config.status distclean: realclean rm -f config.h* configure strip: strip $(TARGETS) install: all pmacct-0.14.0/src/nfprobe_plugin/strlcat.c0000644000175000017500000000402010530072467017471 0ustar paolopaolo/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "common.h" #ifndef HAVE_STRLCAT RCSID("$Id: strlcat.c,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $"); #if defined(LIBC_SCCS) && !defined(lint) static char *rcsid = "$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $"; #endif /* LIBC_SCCS and not lint */ #include #include /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { register char *d = dst; register const char *s = src; register size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } #endif /* !HAVE_STRLCAT */ pmacct-0.14.0/src/nfprobe_plugin/netflow5.c0000644000175000017500000001624611741044542017573 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* * Originally based on softflowd which is: * * Copyright 2002 Damien Miller All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: netflow5.c,v 1.9 2012/04/10 14:53:22 paolo Exp $ */ #include "common.h" #include "treetype.h" #include "nfprobe_plugin.h" RCSID("$Id: netflow5.c,v 1.9 2012/04/10 14:53:22 paolo Exp $"); /* * This is the Cisco Netflow(tm) version 5 packet format * Based on: * http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_0/nfc_ug/nfcform.htm */ struct NF5_HEADER { u_int16_t version, flows; u_int32_t uptime_ms, time_sec, time_nanosec, flow_sequence; u_int8_t engine_type, engine_id; u_int16_t sampling; }; struct NF5_FLOW { u_int32_t src_ip, dest_ip, nexthop_ip; u_int16_t if_index_in, if_index_out; u_int32_t flow_packets, flow_octets; u_int32_t flow_start, flow_finish; u_int16_t src_port, dest_port; u_int8_t pad1; u_int8_t tcp_flags, protocol, tos; u_int16_t src_as, dest_as; u_int8_t src_mask, dst_mask; u_int16_t pad2; }; #define NF5_MAXFLOWS 30 #define NF5_MAXPACKET_SIZE (sizeof(struct NF5_HEADER) + \ (NF5_MAXFLOWS * sizeof(struct NF5_FLOW))) /* * Given an array of expired flows, send netflow v5 report packets * Returns number of packets sent or -1 on error */ int send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock, u_int64_t *flows_exported, struct timeval *system_boot_time, int verbose_flag, u_int8_t engine_type, u_int8_t engine_id) { struct timeval now; u_int32_t uptime_ms; u_int8_t packet[NF5_MAXPACKET_SIZE]; /* Maximum allowed packet size (24 flows) */ struct NF5_HEADER *hdr = NULL; struct NF5_FLOW *flw = NULL; int i, j, offset, num_packets, err; socklen_t errsz; gettimeofday(&now, NULL); uptime_ms = timeval_sub_ms(&now, system_boot_time); hdr = (struct NF5_HEADER *)packet; for(num_packets = offset = j = i = 0; i < num_flows; i++) { if (j >= NF5_MAXFLOWS - 1) { if (verbose_flag) Log(LOG_DEBUG, "DEBUG ( %s/%s ): Sending NetFlow v5 packet: len = %d\n", config.name, config.type, offset); hdr->flows = htons(hdr->flows); errsz = sizeof(err); getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz); /* Clear ICMP errors */ if (send(nfsock, packet, (size_t)offset, 0) == -1) return (-1); *flows_exported += j; j = 0; num_packets++; } if (j == 0) { memset(&packet, '\0', sizeof(packet)); hdr->version = htons(5); hdr->flows = 0; /* Filled in as we go */ hdr->uptime_ms = htonl(uptime_ms); hdr->time_sec = htonl(now.tv_sec); hdr->time_nanosec = htonl(now.tv_usec * 1000); hdr->flow_sequence = htonl(*flows_exported); hdr->engine_type = engine_type; hdr->engine_id = engine_id; if (config.sampling_rate) hdr->sampling = htons(config.sampling_rate & 0x3FFF); else if (config.ext_sampling_rate) hdr->sampling = htons(config.ext_sampling_rate & 0x3FFF); if (hdr->sampling) hdr->sampling |= (htons(1) >> 2); offset = sizeof(*hdr); } flw = (struct NF5_FLOW *)(packet + offset); /* NetFlow v.5 doesn't do IPv6 */ if (flows[i]->af != AF_INET) continue; if (flows[i]->octets[0] > 0) { flw->src_ip = flows[i]->addr[0].v4.s_addr; flw->dest_ip = flows[i]->addr[1].v4.s_addr; flw->src_mask = flows[i]->mask[0]; flw->dst_mask = flows[i]->mask[1]; flw->src_port = flows[i]->port[0]; flw->dest_port = flows[i]->port[1]; flw->if_index_in = htons(flows[i]->ifindex[0]); flw->if_index_out = htons(flows[i]->ifindex[1]); { as_t tmp_as; tmp_as = ntohl(flows[i]->as[0]); if (tmp_as > 65535) flw->src_as = htons(23456); else flw->src_as = htons(tmp_as); tmp_as = ntohl(flows[i]->as[1]); if (tmp_as > 65535) flw->dest_as = htons(23456); else flw->dest_as = htons(tmp_as); } flw->flow_packets = htonl(flows[i]->packets[0]); flw->flow_octets = htonl(flows[i]->octets[0]); flw->flow_start = htonl(timeval_sub_ms(&flows[i]->flow_start, system_boot_time)); flw->flow_finish = htonl(timeval_sub_ms(&flows[i]->flow_last, system_boot_time)); flw->tcp_flags = flows[i]->tcp_flags[0]; flw->protocol = flows[i]->protocol; flw->tos = flows[i]->tos[0]; offset += sizeof(*flw); j++; hdr->flows++; } flw = (struct NF5_FLOW *)(packet + offset); if (flows[i]->octets[1] > 0) { flw->src_ip = flows[i]->addr[1].v4.s_addr; flw->dest_ip = flows[i]->addr[0].v4.s_addr; flw->src_mask = flows[i]->mask[1]; flw->dst_mask = flows[i]->mask[0]; flw->src_port = flows[i]->port[1]; flw->dest_port = flows[i]->port[0]; flw->if_index_in = htons(flows[i]->ifindex[1]); flw->if_index_out = htons(flows[i]->ifindex[0]); { as_t tmp_as; tmp_as = ntohl(flows[i]->as[1]); if (tmp_as > 65535) flw->src_as = htons(23456); else flw->src_as = htons(tmp_as); tmp_as = ntohl(flows[i]->as[0]); if (tmp_as > 65535) flw->dest_as = htons(23456); else flw->dest_as = htons(tmp_as); } flw->flow_packets = htonl(flows[i]->packets[1]); flw->flow_octets = htonl(flows[i]->octets[1]); flw->flow_start = htonl(timeval_sub_ms(&flows[i]->flow_start, system_boot_time)); flw->flow_finish = htonl(timeval_sub_ms(&flows[i]->flow_last, system_boot_time)); flw->tcp_flags = flows[i]->tcp_flags[1]; flw->protocol = flows[i]->protocol; flw->tos = flows[i]->tos[1]; offset += sizeof(*flw); j++; hdr->flows++; } } /* Send any leftovers */ if (j != 0) { if (verbose_flag) Log(LOG_DEBUG, "DEBUG ( %s/%s ): Sending NetFlow v5 packet: len = %d\n", config.name, config.type, offset); hdr->flows = htons(hdr->flows); errsz = sizeof(err); getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz); /* Clear ICMP errors */ if (send(nfsock, packet, (size_t)offset, 0) == -1) return (-1); num_packets++; } *flows_exported += j; return (num_packets); } pmacct-0.14.0/src/nfprobe_plugin/nfprobe_plugin.c0000644000175000017500000013255511741023314021035 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* * Originally based on softflowd which is: * * Copyright 2002 Damien Miller All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define __NFPROBE_PLUGIN_C /* * This is software implementation of Cisco's NetFlow(tm) traffic * reporting system. It operates by listening (via libpcap) on a * promiscuous interface and tracking traffic flows. * * Traffic flows are recorded by source/destination/protocol IP address or, in the * case of TCP and UDP, by src_addr:src_port/dest_addr:dest_port/protocol * * Flows expire automatically after a period of inactivity (default: 1 hour) * They may also be evicted (in order of age) in situations where there are * more flows than slots available. * * Netflow version 1 compatible packets are sent to a specified target * host upon flow expiry. * * As this implementation watches traffic promiscuously, it is likely to * place significant load on hosts or gateways on which it is installed. */ #include "common.h" #include "sys-tree.h" #include "convtime.h" #include "../nfacctd.h" #include "nfprobe_plugin.h" #include "treetype.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "net_aggr.h" #include "ports_aggr.h" /* Global variables */ static int verbose_flag = 0; /* Debugging flag */ static int timeout = 0; struct FLOWTRACK *glob_flowtrack = NULL; /* Prototypes */ static void force_expire(struct FLOWTRACK *, u_int32_t); /* Signal handler flags */ static int graceful_shutdown_request = 0; /* Context for libpcap callback functions */ struct CB_CTXT { struct FLOWTRACK *ft; int linktype; int fatal; int want_v6; }; /* Netflow send functions */ typedef int (netflow_send_func_t)(struct FLOW **, int, int, u_int64_t *, struct timeval *, int, u_int8_t, u_int8_t); struct NETFLOW_SENDER { int version; netflow_send_func_t *func; int v6_capable; }; /* Array of NetFlow export function that we know of. NB. nf[0] is default */ static const struct NETFLOW_SENDER nf[] = { { 5, send_netflow_v5, 0 }, { 1, send_netflow_v1, 0 }, { 9, send_netflow_v9, 1 }, { 10, send_netflow_v9, 1 }, { -1, NULL, 0 }, }; /* Describes a location where we send NetFlow packets to */ struct NETFLOW_TARGET { int fd; const struct NETFLOW_SENDER *dialect; }; void nfprobe_exit_gracefully(int signum) { signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); if (config.pcap_savefile) timeout = 3*1000; graceful_shutdown_request = TRUE; } /* * This is the flow comparison function. */ static int flow_compare(struct FLOW *a, struct FLOW *b) { /* Be careful to avoid signed vs unsigned issues here */ int r; if (a->af != b->af) return (a->af > b->af ? 1 : -1); if ((r = memcmp(&a->addr[0], &b->addr[0], sizeof(a->addr[0]))) != 0) return (r > 0 ? 1 : -1); if ((r = memcmp(&a->addr[1], &b->addr[1], sizeof(a->addr[1]))) != 0) return (r > 0 ? 1 : -1); #ifdef notyet if (a->ip6_flowlabel[0] != 0 && b->ip6_flowlabel[0] != 0 && a->ip6_flowlabel[0] != b->ip6_flowlabel[0]) return (a->ip6_flowlabel[0] > b->ip6_flowlabel[0] ? 1 : -1); if (a->ip6_flowlabel[1] != 0 && b->ip6_flowlabel[1] != 0 && a->ip6_flowlabel[1] != b->ip6_flowlabel[1]) return (a->ip6_flowlabel[1] > b->ip6_flowlabel[1] ? 1 : -1); #endif if (a->protocol != b->protocol) return (a->protocol > b->protocol ? 1 : -1); if (a->port[0] != b->port[0]) return (ntohs(a->port[0]) > ntohs(b->port[0]) ? 1 : -1); if (a->port[1] != b->port[1]) return (ntohs(a->port[1]) > ntohs(b->port[1]) ? 1 : -1); return (0); } /* Generate functions for flow tree */ FLOW_PROTOTYPE(FLOWS, FLOW, trp, flow_compare); FLOW_GENERATE(FLOWS, FLOW, trp, flow_compare); /* * This is the expiry comparison function. */ static int expiry_compare(struct EXPIRY *a, struct EXPIRY *b) { if (a->expires_at != b->expires_at) return (a->expires_at > b->expires_at ? 1 : -1); /* Make expiry entries unique by comparing flow sequence */ if (a->flow->flow_seq != b->flow->flow_seq) return (a->flow->flow_seq > b->flow->flow_seq ? 1 : -1); return (0); } /* Generate functions for flow tree */ EXPIRY_PROTOTYPE(EXPIRIES, EXPIRY, trp, expiry_compare); EXPIRY_GENERATE(EXPIRIES, EXPIRY, trp, expiry_compare); /* Format a time in an ISOish format */ static const char * format_time(time_t t) { struct tm *tm; static char buf[20]; tm = localtime(&t); strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", tm); return (buf); } /* Format a flow in a verbose and ugly way */ static const char * format_flow(struct FLOW *flow) { char addr1[64], addr2[64], stime[20], ftime[20]; static char buf[1024]; inet_ntop(flow->af, &flow->addr[0], addr1, sizeof(addr1)); inet_ntop(flow->af, &flow->addr[1], addr2, sizeof(addr2)); snprintf(stime, sizeof(ftime), "%s", format_time(flow->flow_start.tv_sec)); snprintf(ftime, sizeof(ftime), "%s", format_time(flow->flow_last.tv_sec)); snprintf(buf, sizeof(buf), "seq:%llu [%s]:%hu <> [%s]:%hu proto:%u " "octets>:%u packets>:%u octets<:%u packets<:%u " "start:%s.%03ld finish:%s.%03ld tcp>:%02x tcp<:%02x " "flowlabel>:%08x flowlabel<:%08x ", flow->flow_seq, addr1, ntohs(flow->port[0]), addr2, ntohs(flow->port[1]), (int)flow->protocol, flow->octets[0], flow->packets[0], flow->octets[1], flow->packets[1], stime, (flow->flow_start.tv_usec + 500) / 1000, ftime, (flow->flow_start.tv_usec + 500) / 1000, flow->tcp_flags[0], flow->tcp_flags[1], flow->ip6_flowlabel[0], flow->ip6_flowlabel[1]); return (buf); } /* Format a flow in a brief way */ static const char * format_flow_brief(struct FLOW *flow) { char addr1[64], addr2[64]; static char buf[1024]; inet_ntop(flow->af, &flow->addr[0], addr1, sizeof(addr1)); inet_ntop(flow->af, &flow->addr[1], addr2, sizeof(addr2)); snprintf(buf, sizeof(buf), "seq:%llu [%s]:%hu <> [%s]:%hu proto:%u", flow->flow_seq, addr1, ntohs(flow->port[0]), addr2, ntohs(flow->port[1]), (int)flow->protocol); return (buf); } /* Need to preprocess data because packet handlers have automagically * swapped byte ordering for some primitives */ void handle_hostbyteorder_packet(struct pkt_data *data) { #if defined HAVE_L2 data->primitives.vlan_id = htons(data->primitives.vlan_id); #endif data->primitives.src_port = htons(data->primitives.src_port); data->primitives.dst_port = htons(data->primitives.dst_port); data->primitives.src_as = htonl(data->primitives.src_as); data->primitives.dst_as = htonl(data->primitives.dst_as); } /* Fill in transport-layer (tcp/udp) portions of flow record */ static int transport_to_flowrec(struct FLOW *flow, struct pkt_data *data, struct pkt_extras *extras, int protocol, int ndx) { struct pkt_primitives *p = &data->primitives; /* * XXX to keep flow in proper canonical format, it may be necessary * to swap the array slots based on the order of the port numbers * does this matter in practice??? I don't think so - return flows will * always match, because of their symmetrical addr/ports */ switch (protocol) { case IPPROTO_TCP: /* Check for runt packet, but don't error out on short frags */ flow->port[ndx] = p->src_port; flow->port[ndx ^ 1] = p->dst_port; flow->tcp_flags[ndx] |= extras->tcp_flags; break; case IPPROTO_UDP: /* Check for runt packet, but don't error out on short frags */ flow->port[ndx] = p->src_port; flow->port[ndx ^ 1] = p->dst_port; break; } return (0); } static int l2_to_flowrec(struct FLOW *flow, struct pkt_data *data, struct pkt_extras *extras, int ndx) { struct pkt_primitives *p = &data->primitives; int direction = 0; if (config.nfprobe_direction) { switch (config.nfprobe_direction) { case DIRECTION_IN: case DIRECTION_OUT: direction = config.nfprobe_direction; break; case DIRECTION_TAG: if (p->id == 1) direction = DIRECTION_IN; else if (p->id == 2) direction = DIRECTION_OUT; break; case DIRECTION_TAG2: if (p->id2 == 1) direction = DIRECTION_IN; else if (p->id2 == 2) direction = DIRECTION_OUT; break; } if (direction == DIRECTION_IN) { flow->direction[ndx] = DIRECTION_IN; flow->direction[ndx ^ 1] = 0; } else if (direction == DIRECTION_OUT) { flow->direction[ndx] = DIRECTION_OUT; flow->direction[ndx ^ 1] = 0; } } #if defined HAVE_L2 memcpy(&flow->mac[ndx][0], &p->eth_shost, 6); memcpy(&flow->mac[ndx ^ 1][0], &p->eth_dhost, 6); flow->vlan = p->vlan_id; flow->mpls_label[ndx] = extras->mpls_top_label; #endif if (!p->ifindex_in && !p->ifindex_out) { if (config.nfprobe_ifindex_type) { switch (config.nfprobe_ifindex_type) { case IFINDEX_STATIC: flow->ifindex[ndx] = (direction == DIRECTION_IN) ? config.nfprobe_ifindex : 0; flow->ifindex[ndx ^ 1] = (direction == DIRECTION_OUT) ? config.nfprobe_ifindex : 0; break; case IFINDEX_TAG: flow->ifindex[ndx] = (direction == DIRECTION_IN) ? p->id : 0; flow->ifindex[ndx ^ 1] = (direction == DIRECTION_OUT) ? p->id : 0; break; case IFINDEX_TAG2: flow->ifindex[ndx] = (direction == DIRECTION_IN) ? p->id2 : 0; flow->ifindex[ndx ^ 1] = (direction == DIRECTION_OUT) ? p->id2 : 0; break; default: flow->ifindex[ndx] = 0; flow->ifindex[ndx ^ 1] = 0; } } } else { flow->ifindex[ndx] = p->ifindex_in; flow->ifindex[ndx ^ 1] = p->ifindex_out; } return (0); } static int l2_to_flowrec_update(struct FLOW *flow, struct pkt_data *data, struct pkt_extras *extras, int ndx) { struct pkt_primitives *p = &data->primitives; int direction = 0; if (config.nfprobe_direction) { switch (config.nfprobe_direction) { case DIRECTION_TAG: if (p->id == 1) direction = DIRECTION_IN; else if (p->id == 2) direction = DIRECTION_OUT; break; case DIRECTION_TAG2: if (p->id2 == 1) direction = DIRECTION_IN; else if (p->id2 == 2) direction = DIRECTION_OUT; break; } if (direction == DIRECTION_IN) { if (!flow->direction[ndx]) flow->direction[ndx] = DIRECTION_IN; } else if (direction == DIRECTION_OUT) { if (!flow->direction[ndx]) flow->direction[ndx] = DIRECTION_OUT; } } } static int ASN_to_flowrec(struct FLOW *flow, struct pkt_data *data, int ndx) { struct pkt_primitives *p = &data->primitives; flow->as[ndx] = p->src_as; flow->as[ndx ^ 1] = p->dst_as; return (0); } /* Convert a IPv4 packet to a partial flow record (used for comparison) */ static int ipv4_to_flowrec(struct FLOW *flow, struct pkt_data *data, struct pkt_extras *extras, int *isfrag, int af) { struct pkt_primitives *p = &data->primitives; int ndx; /* Prepare to store flow in canonical format */ ndx = memcmp(&p->src_ip.address.ipv4, &p->dst_ip.address.ipv4, sizeof(p->src_ip.address.ipv4)) > 0 ? 1 : 0; flow->af = af; flow->addr[ndx].v4 = p->src_ip.address.ipv4; flow->addr[ndx ^ 1].v4 = p->dst_ip.address.ipv4; flow->bgp_next_hop[ndx].v4 = extras->bgp_next_hop.address.ipv4; flow->mask[ndx] = p->src_nmask; flow->mask[ndx ^ 1] = p->dst_nmask; flow->tos[ndx] = p->tos; flow->protocol = p->proto; flow->octets[ndx] = data->pkt_len; flow->packets[ndx] = data->pkt_num; flow->flows[ndx] = data->flo_num; flow->class = p->class; flow->tag[ndx] = p->id; flow->tag2[ndx] = p->id2; *isfrag = 0; l2_to_flowrec(flow, data, extras, ndx); ASN_to_flowrec(flow, data, ndx); return (transport_to_flowrec(flow, data, extras, p->proto, ndx)); // return (0); } static int ipv4_to_flowrec_update(struct FLOW *flow, struct pkt_data *data, struct pkt_extras *extras, int *isfrag, int af) { struct pkt_primitives *p = &data->primitives; int ndx; /* Prepare to store flow in canonical format */ ndx = memcmp(&p->src_ip.address.ipv4, &p->dst_ip.address.ipv4, sizeof(p->src_ip.address.ipv4)) > 0 ? 1 : 0; if (!flow->bgp_next_hop[ndx].v4.s_addr) flow->bgp_next_hop[ndx].v4 = extras->bgp_next_hop.address.ipv4; l2_to_flowrec_update(flow, data, extras, ndx); } #if defined ENABLE_IPV6 /* Convert a IPv6 packet to a partial flow record (used for comparison) */ static int ipv6_to_flowrec(struct FLOW *flow, struct pkt_data *data, struct pkt_extras *extras, int *isfrag, int af) { struct pkt_primitives *p = &data->primitives; int ndx, nxt; /* Prepare to store flow in canonical format */ ndx = memcmp(&p->src_ip.address.ipv6, &p->dst_ip.address.ipv6, sizeof(p->src_ip.address.ipv6)) > 0 ? 1 : 0; flow->af = af; /* XXX: flow->ip6_flowlabel[ndx] = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; */ flow->ip6_flowlabel[ndx] = 0; flow->addr[ndx].v6 = p->src_ip.address.ipv6; flow->addr[ndx ^ 1].v6 = p->dst_ip.address.ipv6; flow->bgp_next_hop[ndx].v6 = extras->bgp_next_hop.address.ipv6; flow->mask[ndx] = p->src_nmask; flow->mask[ndx ^ 1] = p->dst_nmask; flow->octets[ndx] = data->pkt_len; flow->packets[ndx] = data->pkt_num; flow->flows[ndx] = data->flo_num; flow->class = p->class; flow->tag[ndx] = p->id; flow->tag2[ndx] = p->id2; *isfrag = 0; l2_to_flowrec(flow, data, extras, ndx); ASN_to_flowrec(flow, data, ndx); return (transport_to_flowrec(flow, data, extras, p->proto, ndx)); // return (0); } static int ipv6_to_flowrec_update(struct FLOW *flow, struct pkt_data *data, struct pkt_extras *extras, int *isfrag, int af) { struct pkt_primitives *p = &data->primitives; struct in6_addr dummy_ipv6; int ndx; /* Prepare to store flow in canonical format */ memset(&dummy_ipv6, 0, sizeof(dummy_ipv6)); ndx = memcmp(&p->src_ip.address.ipv6, &p->dst_ip.address.ipv6, sizeof(p->src_ip.address.ipv6)) > 0 ? 1 : 0; if (!memcmp(&dummy_ipv6, &flow->bgp_next_hop[ndx].v6, sizeof(dummy_ipv6))) flow->bgp_next_hop[ndx].v6 = extras->bgp_next_hop.address.ipv6; l2_to_flowrec_update(flow, data, extras, ndx); } #endif static void flow_update_expiry(struct FLOWTRACK *ft, struct FLOW *flow) { EXPIRY_REMOVE(EXPIRIES, &ft->expiries, flow->expiry); /* Flows over 2Gb traffic */ if (flow->octets[0] > (1U << 31) || flow->octets[1] > (1U << 31)) { flow->expiry->expires_at = 0; flow->expiry->reason = R_OVERBYTES; goto out; } /* Flows over maximum life seconds */ if (ft->maximum_lifetime != 0 && flow->flow_last.tv_sec - flow->flow_start.tv_sec > ft->maximum_lifetime) { flow->expiry->expires_at = 0; flow->expiry->reason = R_MAXLIFE; goto out; } if (flow->protocol == IPPROTO_TCP) { /* Reset TCP flows */ if (ft->tcp_rst_timeout != 0 && ((flow->tcp_flags[0] & TH_RST) || (flow->tcp_flags[1] & TH_RST))) { flow->expiry->expires_at = flow->flow_last.tv_sec + ft->tcp_rst_timeout; flow->expiry->reason = R_TCP_RST; goto out; } /* Finished TCP flows */ if (ft->tcp_fin_timeout != 0 && ((flow->tcp_flags[0] & TH_FIN) && (flow->tcp_flags[1] & TH_FIN))) { flow->expiry->expires_at = flow->flow_last.tv_sec + ft->tcp_fin_timeout; flow->expiry->reason = R_TCP_FIN; goto out; } /* TCP flows */ if (ft->tcp_timeout != 0) { flow->expiry->expires_at = flow->flow_last.tv_sec + ft->tcp_timeout; flow->expiry->reason = R_TCP; goto out; } } if (ft->udp_timeout != 0 && flow->protocol == IPPROTO_UDP) { /* UDP flows */ flow->expiry->expires_at = flow->flow_last.tv_sec + ft->udp_timeout; flow->expiry->reason = R_UDP; goto out; } if (ft->icmp_timeout != 0 && ((flow->af == AF_INET && flow->protocol == IPPROTO_ICMP) #if defined ENABLE_IPV6 || ((flow->af == AF_INET6 && flow->protocol == IPPROTO_ICMPV6)) #endif )) { /* UDP flows */ flow->expiry->expires_at = flow->flow_last.tv_sec + ft->icmp_timeout; flow->expiry->reason = R_ICMP; goto out; } /* Everything else */ flow->expiry->expires_at = flow->flow_last.tv_sec + ft->general_timeout; flow->expiry->reason = R_GENERAL; out: EXPIRY_INSERT(EXPIRIES, &ft->expiries, flow->expiry); } /* Return values from process_packet */ #define PP_OK 0 #define PP_BAD_PACKET -2 #define PP_MALLOC_FAIL -3 /* * Main per-packet processing function. Take a packet (provided by * libpcap) and attempt to find a matching flow. If no such flow exists, * then create one. * * Also marks flows for fast expiry, based on flow or packet attributes * (the actual expiry is performed elsewhere) */ static int process_packet(struct FLOWTRACK *ft, struct pkt_data *data, struct pkt_extras *extras, const struct timeval *received_time) { struct FLOW tmp, *flow; int frag, af, dont_summarize = (config.acct_type == ACCT_NF ? 1 : 0); ft->total_packets += data->pkt_num; af = (data->primitives.src_ip.family == 0 ? AF_INET : data->primitives.src_ip.family); /* Convert the IP packet to a flow identity */ memset(&tmp, 0, sizeof(tmp)); switch (af) { case AF_INET: if (ipv4_to_flowrec(&tmp, data, extras, &frag, af) == -1) goto bad; break; #if defined ENABLE_IPV6 case AF_INET6: if (ipv6_to_flowrec(&tmp, data, extras, &frag, af) == -1) goto bad; break; #endif default: bad: ft->bad_packets += data->pkt_num; return (PP_BAD_PACKET); } if (frag) ft->frag_packets += data->pkt_num; /* If a matching flow does not exist, create and insert one */ if (dont_summarize || ((flow = FLOW_FIND(FLOWS, &ft->flows, &tmp)) == NULL)) { /* Allocate and fill in the flow */ if ((flow = malloc(sizeof(*flow))) == NULL) return (PP_MALLOC_FAIL); memcpy(flow, &tmp, sizeof(*flow)); memcpy(&flow->flow_start, received_time, sizeof(flow->flow_start)); flow->flow_seq = ft->next_flow_seq++; FLOW_INSERT(FLOWS, &ft->flows, flow); /* Allocate and fill in the associated expiry event */ if ((flow->expiry = malloc(sizeof(*flow->expiry))) == NULL) return (PP_MALLOC_FAIL); flow->expiry->flow = flow; /* Expiration note: 0 means expire immediately; we prefer this to happen when attaching to nfacctd - ie. dont_summarize is TRUE */ if (!dont_summarize) flow->expiry->expires_at = 1; else flow->expiry->expires_at = 0; flow->expiry->reason = R_GENERAL; EXPIRY_INSERT(EXPIRIES, &ft->expiries, flow->expiry); if (data->flo_num) ft->num_flows += data->flo_num; else ft->num_flows++; if (verbose_flag) Log(LOG_DEBUG, "DEBUG ( %s/%s ): ADD FLOW %s\n", config.name, config.type, format_flow_brief(flow)); } else { /* Update flow statistics */ flow->packets[0] += tmp.packets[0]; flow->octets[0] += tmp.octets[0]; flow->flows[0] += tmp.flows[0]; flow->tcp_flags[0] |= tmp.tcp_flags[0]; flow->tos[0] = tmp.tos[0]; // XXX flow->packets[1] += tmp.packets[1]; flow->octets[1] += tmp.octets[1]; flow->flows[1] += tmp.flows[1]; flow->tcp_flags[1] |= tmp.tcp_flags[1]; flow->tos[1] = tmp.tos[1]; // XXX /* Address family dependent items to update */ switch (flow->af) { case AF_INET: ipv4_to_flowrec_update(flow, data, extras, &frag, af); break; #if defined ENABLE_IPV6 case AF_INET6: ipv6_to_flowrec_update(flow, data, extras, &frag, af); break; #endif } if (!flow->class) flow->class = tmp.class; if (!flow->tag[0]) flow->tag[0] = tmp.tag[0]; if (!flow->tag[1]) flow->tag[1] = tmp.tag[1]; if (!flow->tag2[0]) flow->tag2[0] = tmp.tag2[0]; if (!flow->tag2[1]) flow->tag2[1] = tmp.tag2[1]; } memcpy(&flow->flow_last, received_time, sizeof(flow->flow_last)); if (flow->expiry->expires_at != 0) flow_update_expiry(ft, flow); return (PP_OK); } /* * Subtract two timevals. Returns (t1 - t2) in milliseconds. */ u_int32_t timeval_sub_ms(const struct timeval *t1, const struct timeval *t2) { struct timeval res; res.tv_sec = t1->tv_sec - t2->tv_sec; res.tv_usec = t1->tv_usec - t2->tv_usec; if (res.tv_usec < 0) { res.tv_usec += 1000000L; res.tv_sec--; } return ((u_int32_t)res.tv_sec * 1000 + (u_int32_t)res.tv_usec / 1000); } static void update_statistic(struct STATISTIC *s, double new, double n) { if (n == 1.0) { s->min = s->mean = s->max = new; return; } s->min = MIN(s->min, new); s->max = MAX(s->max, new); s->mean = s->mean + ((new - s->mean) / n); } /* Update global statistics */ static void update_statistics(struct FLOWTRACK *ft, struct FLOW *flow) { double tmp; static double n = 1.0; ft->flows_expired++; ft->flows_pp[flow->protocol % 256]++; tmp = (double)flow->flow_last.tv_sec + ((double)flow->flow_last.tv_usec / 1000000.0); tmp -= (double)flow->flow_start.tv_sec + ((double)flow->flow_start.tv_usec / 1000000.0); if (tmp < 0.0) tmp = 0.0; update_statistic(&ft->duration, tmp, n); update_statistic(&ft->duration_pp[flow->protocol], tmp, (double)ft->flows_pp[flow->protocol % 256]); tmp = flow->octets[0] + flow->octets[1]; update_statistic(&ft->octets, tmp, n); ft->octets_pp[flow->protocol % 256] += tmp; tmp = flow->packets[0] + flow->packets[1]; update_statistic(&ft->packets, tmp, n); ft->packets_pp[flow->protocol % 256] += tmp; n++; } static void update_expiry_stats(struct FLOWTRACK *ft, struct EXPIRY *e) { switch (e->reason) { case R_GENERAL: ft->expired_general++; break; case R_TCP: ft->expired_tcp++; break; case R_TCP_RST: ft->expired_tcp_rst++; break; case R_TCP_FIN: ft->expired_tcp_fin++; break; case R_UDP: ft->expired_udp++; break; case R_ICMP: ft->expired_icmp++; break; case R_MAXLIFE: ft->expired_maxlife++; break; case R_OVERBYTES: ft->expired_overbytes++; break; case R_OVERFLOWS: ft->expired_maxflows++; break; case R_FLUSH: ft->expired_flush++; break; } } /* How long before the next expiry event in millisecond */ static int next_expire(struct FLOWTRACK *ft) { struct EXPIRY *expiry; struct timeval now; u_int32_t expires_at, ret, fudge; gettimeofday(&now, NULL); if ((expiry = EXPIRY_MIN(EXPIRIES, &ft->expiries)) == NULL) return (-1); /* indefinite */ expires_at = expiry->expires_at; /* Don't cluster urgent expiries */ if (expires_at == 0 && (expiry->reason == R_OVERBYTES || expiry->reason == R_OVERFLOWS || expiry->reason == R_FLUSH)) return (0); /* Now */ /* Cluster expiries by expiry_interval */ if (ft->expiry_interval > 1) { if ((fudge = expires_at % ft->expiry_interval) > 0) expires_at += ft->expiry_interval - fudge; } if (expires_at < now.tv_sec) return (0); /* Now */ ret = 999 + (expires_at - now.tv_sec) * 1000; return (ret); } /* * Scan the tree of expiry events and process expired flows. If zap_all * is set, then forcibly expire all flows. */ #define CE_EXPIRE_NORMAL 0 /* Normal expiry processing */ #define CE_EXPIRE_ALL -1 /* Expire all flows immediately */ #define CE_EXPIRE_FORCED 1 /* Only expire force-expired flows */ static int check_expired(struct FLOWTRACK *ft, struct NETFLOW_TARGET *target, int ex, u_int8_t engine_type, u_int8_t engine_id) { struct FLOW **expired_flows, **oldexp; int num_expired, i, r; struct timeval now; struct EXPIRY *expiry, *nexpiry; gettimeofday(&now, NULL); r = 0; num_expired = 0; expired_flows = NULL; if (verbose_flag) Log(LOG_DEBUG, "DEBUG ( %s/%s ): Starting expiry scan: mode %d\n", config.name, config.type, ex); for(expiry = EXPIRY_MIN(EXPIRIES, &ft->expiries); expiry != NULL; expiry = nexpiry) { nexpiry = EXPIRY_NEXT(EXPIRIES, &ft->expiries, expiry); if ((expiry->expires_at == 0) || (ex == CE_EXPIRE_ALL) || (ex != CE_EXPIRE_FORCED && (expiry->expires_at < now.tv_sec))) { /* Flow has expired */ if (verbose_flag) Log(LOG_DEBUG, "DEBUG ( %s/%s ): Queuing flow seq:%llu (%p) for expiry\n", config.name, config.type, expiry->flow->flow_seq, expiry->flow); /* Add to array of expired flows */ oldexp = expired_flows; expired_flows = realloc(expired_flows, sizeof(*expired_flows) * (num_expired + 1)); /* Don't fatal on realloc failures */ if (expired_flows == NULL) expired_flows = oldexp; else { expired_flows[num_expired] = expiry->flow; num_expired++; } if (ex == CE_EXPIRE_ALL) expiry->reason = R_FLUSH; update_expiry_stats(ft, expiry); /* Remove from flow tree, destroy expiry event */ FLOW_REMOVE(FLOWS, &ft->flows, expiry->flow); EXPIRY_REMOVE(EXPIRIES, &ft->expiries, expiry); expiry->flow->expiry = NULL; free(expiry); ft->num_flows--; } } if (verbose_flag) Log(LOG_DEBUG, "DEBUG ( %s/%s ): Finished scan %d flow(s) to be evicted\n", config.name, config.type, num_expired); /* Processing for expired flows */ if (num_expired > 0) { if (target != NULL && target->fd != -1) { r = target->dialect->func(expired_flows, num_expired, target->fd, &ft->flows_exported, // &ft->next_datagram_seq, &ft->system_boot_time, verbose_flag, engine_type, engine_id); if (verbose_flag) Log(LOG_DEBUG, "DEBUG ( %s/%s ): Sent %d netflow packets\n", config.name, config.type, r); if (r > 0) { ft->packets_sent += r; /* XXX what if r < num_expired * 2 ? */ } else { ft->flows_dropped += num_expired * 2; } } for (i = 0; i < num_expired; i++) { if (verbose_flag) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): EXPIRED: %s (%p)\n", config.name, config.type, format_flow(expired_flows[i]), expired_flows[i]); } update_statistics(ft, expired_flows[i]); free(expired_flows[i]); } free(expired_flows); } return (r == -1 ? -1 : num_expired); } /* * Force expiry of num_to_expire flows (e.g. when flow table overfull) */ static void force_expire(struct FLOWTRACK *ft, u_int32_t num_to_expire) { struct EXPIRY *expiry, **expiryv; int i; /* XXX move all overflow processing here (maybe) */ if (verbose_flag) Log(LOG_INFO, "INFO ( %s/%s ): Forcing expiry of %d flows\n", config.name, config.type, num_to_expire); /* * Do this in two steps, as it is dangerous to change a key on * a tree entry without first removing it and then re-adding it. * It is even worse when this has to be done during a FOREACH :) * To get around this, we make a list of expired flows and _then_ * alter them */ if ((expiryv = malloc(sizeof(*expiryv) * num_to_expire)) == NULL) { /* * On malloc failure, expire ALL flows. I assume that * setting all the keys in a tree to the same value is * safe. */ Log(LOG_ERR, "ERROR ( %s/%s ): Out of memory while expiring flows\n", config.name, config.type); EXPIRY_FOREACH(expiry, EXPIRIES, &ft->expiries) { expiry->expires_at = 0; expiry->reason = R_OVERFLOWS; ft->flows_force_expired++; } return; } /* Make the list of flows to expire */ i = 0; EXPIRY_FOREACH(expiry, EXPIRIES, &ft->expiries) { if (i >= num_to_expire) break; expiryv[i++] = expiry; } if (i < num_to_expire) { Log(LOG_ERR, "ERROR ( %s/%s ): Needed to expire %d flows, but only %d active.\n", config.name, config.type, num_to_expire, i); num_to_expire = i; } for(i = 0; i < num_to_expire; i++) { EXPIRY_REMOVE(EXPIRIES, &ft->expiries, expiryv[i]); expiryv[i]->expires_at = 0; expiryv[i]->reason = R_OVERFLOWS; EXPIRY_INSERT(EXPIRIES, &ft->expiries, expiryv[i]); } ft->flows_force_expired += num_to_expire; free(expiryv); /* XXX - this is overcomplicated, perhaps use a separate queue */ } /* Delete all flows that we know about without processing */ static int delete_all_flows(struct FLOWTRACK *ft) { struct FLOW *flow, *nflow; int i; i = 0; for(flow = FLOW_MIN(FLOWS, &ft->flows); flow != NULL; flow = nflow) { nflow = FLOW_NEXT(FLOWS, &ft->flows, flow); FLOW_REMOVE(FLOWS, &ft->flows, flow); EXPIRY_REMOVE(EXPIRIES, &ft->expiries, flow->expiry); free(flow->expiry); ft->num_flows--; free(flow); i++; } return (i); } /* * Per-packet callback function from libpcap. Pass the packet (if it is IP) * sans datalink headers to process_packet. */ static void flow_cb(u_char *user_data, struct pkt_data *data, struct pkt_extras *extras) { struct CB_CTXT *cb_ctxt = (struct CB_CTXT *)user_data; struct timeval tv; tv.tv_sec = data->time_start.tv_sec; tv.tv_usec = data->time_start.tv_usec; if (process_packet(cb_ctxt->ft, data, extras, &tv) == PP_MALLOC_FAIL) cb_ctxt->fatal = 1; } static void print_timeouts(struct FLOWTRACK *ft) { Log(LOG_INFO, "INFO ( %s/%s ): TCP timeout: %ds\n", config.name, config.type, ft->tcp_timeout); Log(LOG_INFO, "INFO ( %s/%s ): TCP post-RST timeout: %ds\n", config.name, config.type, ft->tcp_rst_timeout); Log(LOG_INFO, "INFO ( %s/%s ): TCP post-FIN timeout: %ds\n", config.name, config.type, ft->tcp_fin_timeout); Log(LOG_INFO, "INFO ( %s/%s ): UDP timeout: %ds\n", config.name, config.type, ft->udp_timeout); Log(LOG_INFO, "INFO ( %s/%s ): ICMP timeout: %ds\n", config.name, config.type, ft->icmp_timeout); Log(LOG_INFO, "INFO ( %s/%s ): General timeout: %ds\n", config.name, config.type, ft->general_timeout); Log(LOG_INFO, "INFO ( %s/%s ): Maximum lifetime: %ds\n", config.name, config.type, ft->maximum_lifetime); Log(LOG_INFO, "INFO ( %s/%s ): Expiry interval: %ds\n", config.name, config.type, ft->expiry_interval); } static int connsock(struct sockaddr_storage *addr, socklen_t len, int hoplimit) { int s, ret = 0; unsigned int h6; unsigned char h4; struct sockaddr_in *in4 = (struct sockaddr_in *)addr; #if defined ENABLE_IPV6 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr; #endif struct sockaddr ssource_ip; if (config.nfprobe_source_ip) { ret = str_to_addr(config.nfprobe_source_ip, &config.nfprobe_source_ha); addr_to_sa(&ssource_ip, &config.nfprobe_source_ha, 0); } if ((s = socket(addr->ss_family, SOCK_DGRAM, 0)) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): socket() failed: %s\n", config.name, config.type, strerror(errno)); exit_plugin(1); } if (config.nfprobe_ipprec) { int opt = config.nfprobe_ipprec << 5; int rc; rc = setsockopt(s, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)); if (rc < 0) Log(LOG_WARNING, "WARN ( %s/%s ): setsockopt() failed for IP_TOS: %s\n", config.name, config.type, strerror(errno)); } if (config.pipe_size) { int rc; rc = Setsocksize(s, SOL_SOCKET, SO_SNDBUF, &config.pipe_size, sizeof(config.pipe_size)); if (rc < 0) Log(LOG_WARNING, "WARN ( %s/%s ): setsockopt() failed for SOL_SNDBUF: %s\n", config.name, config.type, strerror(errno)); } if (ret && bind(s, (struct sockaddr *) &ssource_ip, sizeof(ssource_ip)) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): bind() failed: %s\n", config.name, config.type, strerror(errno)); exit_plugin(1); } if (connect(s, (struct sockaddr*)addr, len) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): connect() failed: %s\n", config.name, config.type, strerror(errno)); exit_plugin(1); } switch (addr->ss_family) { case AF_INET: /* Default to link-local TTL for multicast addresses */ if (hoplimit == -1 && IN_MULTICAST(in4->sin_addr.s_addr)) hoplimit = 1; if (hoplimit == -1) break; h4 = hoplimit; if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &h4, sizeof(h4)) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): setsockopt() failed for IP_MULTICAST_TTL: %s\n", config.name, config.type, strerror(errno)); exit_plugin(1); } break; #if defined ENABLE_IPV6 case AF_INET6: /* Default to link-local hoplimit for multicast addresses */ if (hoplimit == -1 && IN6_IS_ADDR_MULTICAST(&in6->sin6_addr)) hoplimit = 1; if (hoplimit == -1) break; h6 = hoplimit; if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &h6, sizeof(h6)) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): setsockopt() failed for IPV6_MULTICAST_HOPS: %s\n", config.name, config.type, strerror(errno)); exit_plugin(1); } #endif } return(s); } static void init_flowtrack(struct FLOWTRACK *ft) { /* Set up flow-tracking structure */ memset(ft, '\0', sizeof(*ft)); ft->next_flow_seq = 1; FLOW_INIT(&ft->flows); EXPIRY_INIT(&ft->expiries); ft->tcp_timeout = DEFAULT_TCP_TIMEOUT; ft->tcp_rst_timeout = DEFAULT_TCP_RST_TIMEOUT; ft->tcp_fin_timeout = DEFAULT_TCP_FIN_TIMEOUT; ft->udp_timeout = DEFAULT_UDP_TIMEOUT; ft->icmp_timeout = DEFAULT_ICMP_TIMEOUT; ft->general_timeout = DEFAULT_GENERAL_TIMEOUT; ft->maximum_lifetime = DEFAULT_MAXIMUM_LIFETIME; ft->expiry_interval = DEFAULT_EXPIRY_INTERVAL; } static void set_timeout(struct FLOWTRACK *ft, const char *to_spec) { char *name, *value; int timeout; if ((name = strdup(to_spec)) == NULL) return; if ((value = strchr(name, '=')) == NULL || *(++value) == '\0') goto bad; *(value - 1) = '\0'; timeout = convtime(value); if (timeout < 0) { Log(LOG_ERR, "ERROR ( %s/%s ): Invalid 'nfprobe_timeouts' value: '%s'\n", config.name, config.type, value); goto free_mem; } if (strcmp(name, "tcp") == 0) ft->tcp_timeout = timeout; else if (strcmp(name, "tcp.rst") == 0) ft->tcp_rst_timeout = timeout; else if (strcmp(name, "tcp.fin") == 0) ft->tcp_fin_timeout = timeout; else if (strcmp(name, "udp") == 0) ft->udp_timeout = timeout; else if (strcmp(name, "icmp") == 0) ft->icmp_timeout = timeout; else if (strcmp(name, "general") == 0) ft->general_timeout = timeout; else if (strcmp(name, "maxlife") == 0) ft->maximum_lifetime = timeout; else if (strcmp(name, "expint") == 0) ft->expiry_interval = timeout; else { bad: Log(LOG_ERR, "ERROR ( %s/%s ): Invalid nfprobe_timeouts option: '%s'\n", config.name, config.type, name); goto free_mem; } if (ft->general_timeout == 0) { Log(LOG_ERR, "ERROR ( %s/%s ): 'general' flow timeout must be greater than zero.\n", config.name, config.type); ft->general_timeout = DEFAULT_GENERAL_TIMEOUT; goto free_mem; } free_mem: free(name); } static void handle_timeouts(struct FLOWTRACK *ft, char *to_spec) { char *sep, *current = to_spec; trim_spaces(current); while (sep = strchr(current, ':')) { *sep = '\0'; set_timeout(ft, current); *sep = ':'; current = ++sep; } set_timeout(ft, current); } static void parse_hostport(const char *s, struct sockaddr *addr, socklen_t *len) { char *orig, *host, *port; struct addrinfo hints, *res; int herr; if ((host = orig = strdup(s)) == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): parse_hostport(), strdup() out of memory\n", config.name, config.type); exit_plugin(1); } trim_spaces(host); trim_spaces(orig); if ((port = strrchr(host, ':')) == NULL || *(++port) == '\0' || *host == '\0') { Log(LOG_ERR, "ERROR ( %s/%s ): parse_hostport(), invalid 'nfprobe_receiver' argument\n", config.name, config.type); exit_plugin(1); } *(port - 1) = '\0'; /* Accept [host]:port for numeric IPv6 addresses */ if (*host == '[' && *(port - 2) == ']') { host++; *(port - 2) = '\0'; } memset(&hints, '\0', sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; if ((herr = getaddrinfo(host, port, &hints, &res)) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): parse_hostport(), address lookup failed\n", config.name, config.type); exit_plugin(1); } if (res == NULL || res->ai_addr == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): parse_hostport(), no addresses found for [%s]:%s\n", config.name, config.type, host, port); exit_plugin(1); } if (res->ai_addrlen > *len) { Log(LOG_ERR, "ERROR ( %s/%s ): parse_hostport(), address too long.\n", config.name, config.type); exit_plugin(1); } memcpy(addr, res->ai_addr, res->ai_addrlen); free(orig); *len = res->ai_addrlen; } static void parse_engine(char *s, u_int8_t *engine_type, u_int8_t *engine_id) { char *delim, *ptr; trim_spaces(s); delim = strchr(s, ':'); if (delim) { *delim = '\0'; ptr = delim+1; *engine_type = atoi(s); *engine_id = atoi(ptr); *delim = ':'; } else { *engine_type = 0; *engine_id = 0; Log(LOG_WARNING, "WARN ( %s/%s ): Engine Type/ID '%s' is not valid. Ignoring.\n", config.name, config.type, s); } } void nfprobe_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_data *data, dummy; struct pkt_extras *extras; struct ports_table pt; struct pollfd pfd; struct timezone tz; unsigned char *pipebuf; time_t now, refresh_deadline; int ret, num; char default_receiver[] = "127.0.0.1:2100"; char default_engine[] = "0:0"; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; unsigned char *rgptr, *dataptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; char *capfile = NULL, dest_addr[256], dest_serv[256]; int ch, linktype, ctlsock, i, r, err, always_v6; int max_flows, stop_collection_flag, exit_request, hoplimit; struct sockaddr_storage dest; struct FLOWTRACK flowtrack; socklen_t dest_len; struct NETFLOW_TARGET target; struct CB_CTXT cb_ctxt; u_int8_t engine_type, engine_id; /* XXX: glue */ memcpy(&config, cfgptr, sizeof(struct configuration)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "Netflow Probe Plugin", config.name); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile); } Log(LOG_INFO, "INFO ( %s/%s ): NetFlow probe plugin is originally based on softflowd 0.9.7 software, Copyright 2002 Damien Miller All rights reserved.\n", config.name, config.type); reload_map = FALSE; /* signal handling */ signal(SIGINT, nfprobe_exit_gracefully); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, reload_maps); signal(SIGPIPE, SIG_IGN); #if !defined FBSD4 signal(SIGCHLD, SIG_IGN); #else signal(SIGCHLD, ignore_falling_child); #endif memset(&cb_ctxt, '\0', sizeof(cb_ctxt)); init_flowtrack(&flowtrack); memset(&dest, '\0', sizeof(dest)); memset(&target, '\0', sizeof(target)); target.fd = -1; target.dialect = &nf[0]; always_v6 = 0; glob_flowtrack = &flowtrack; if (config.nfprobe_timeouts) handle_timeouts(&flowtrack, config.nfprobe_timeouts); timeout = flowtrack.expiry_interval * 1000; print_timeouts(&flowtrack); if (!config.nfprobe_hoplimit) hoplimit = -1; else hoplimit = config.nfprobe_hoplimit; if (!config.nfprobe_maxflows) max_flows = DEFAULT_MAX_FLOWS; else max_flows = config.nfprobe_maxflows; if (config.debug) verbose_flag = TRUE; if (config.pcap_savefile) capfile = config.pcap_savefile; dest_len = sizeof(dest); if (!config.nfprobe_receiver) config.nfprobe_receiver = default_receiver; if (!config.nfprobe_engine) config.nfprobe_engine = default_engine; parse_hostport(config.nfprobe_receiver, (struct sockaddr *)&dest, &dest_len); parse_engine(config.nfprobe_engine, &engine_type, &engine_id); sort_version: for (i = 0, r = config.nfprobe_version; nf[i].version != -1; i++) { if (nf[i].version == r) break; } if (nf[i].version == -1) { config.nfprobe_version = 5; goto sort_version; } target.dialect = &nf[i]; /* Netflow send socket */ if (dest.ss_family != 0) { if ((err = getnameinfo((struct sockaddr *)&dest, dest_len, dest_addr, sizeof(dest_addr), dest_serv, sizeof(dest_serv), NI_NUMERICHOST)) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): getnameinfo: %d\n", config.name, config.type, err); exit_plugin(1); } target.fd = connsock(&dest, dest_len, hoplimit); } if (dest.ss_family != 0) Log(LOG_INFO, "INFO ( %s/%s ): Exporting flows to [%s]:%s\n", config.name, config.type, dest_addr, dest_serv); /* Main processing loop */ gettimeofday(&flowtrack.system_boot_time, NULL); stop_collection_flag = 0; cb_ctxt.ft = &flowtrack; cb_ctxt.linktype = linktype; cb_ctxt.want_v6 = target.dialect->v6_capable || always_v6; memset(&nt, 0, sizeof(nt)); memset(&nc, 0, sizeof(nc)); memset(&pt, 0, sizeof(pt)); memset(&dummy, 0, sizeof(dummy)); load_networks(config.networks_file, &nt, &nc); set_net_funcs(&nt); if (config.ports_file) load_ports(config.ports_file, &pt); pipebuf = (unsigned char *) Malloc(config.buffer_size); memset(pipebuf, 0, config.buffer_size); pfd.fd = pipe_fd; pfd.events = POLLIN; setnonblocking(pipe_fd); for(;;) { poll_again: status->wakeup = TRUE; ret = poll(&pfd, 1, timeout); if (ret < 0) goto poll_again; /* Fatal error from per-packet functions */ if (cb_ctxt.fatal) { Log(LOG_ERR, "ERROR ( %s/%s ): Fatal error - exiting immediately.\n", config.name, config.type); break; } if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } if (ret > 0) { /* we received data */ read_data: if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; goto handle_flow_expiration; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%u'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%u'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); } seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); if ((rg->ptr+bufsz) >= rg->end) rg->ptr = rg->base; else rg->ptr += bufsz; data = (struct pkt_data *) (pipebuf+sizeof(struct ch_buf_hdr)); while (((struct ch_buf_hdr *)pipebuf)->num) { if (config.networks_file) { memcpy(&dummy.primitives.src_ip, &data->primitives.src_ip, HostAddrSz); memcpy(&dummy.primitives.dst_ip, &data->primitives.dst_ip, HostAddrSz); for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &dummy.primitives); if (config.nfacctd_as == NF_AS_NEW) { data->primitives.src_as = dummy.primitives.src_as; data->primitives.dst_as = dummy.primitives.dst_as; } if (config.nfacctd_net == NF_NET_NEW) { data->primitives.src_nmask = dummy.primitives.src_nmask; data->primitives.dst_nmask = dummy.primitives.dst_nmask; } } if (config.ports_file) { if (!pt.table[data->primitives.src_port]) data->primitives.src_port = 0; if (!pt.table[data->primitives.dst_port]) data->primitives.dst_port = 0; } extras = (struct pkt_extras *) ((u_char *)data+PdataSz); handle_hostbyteorder_packet(data); flow_cb((void *)&cb_ctxt, data, extras); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) data; dataptr += PdataSz + PextrasSz; data = (struct pkt_data *) dataptr; } } goto read_data; } handle_flow_expiration: /* * Expiry processing happens every recheck_rate seconds * or whenever we have exceeded the maximum number of active * flows */ if (flowtrack.num_flows > max_flows || next_expire(&flowtrack) == 0) { expiry_check: /* * If we are reading from a capture file, we never * expire flows based on time - instead we only * expire flows when the flow table is full. */ if (check_expired(&flowtrack, &target, capfile == NULL ? CE_EXPIRE_NORMAL : CE_EXPIRE_FORCED, engine_type, engine_id) < 0) { Log(LOG_WARNING, "WARN ( %s/%s ): Unable to export flows.\n", config.name, config.type); /* Let's try to sleep a bit and re-open the NetFlow send socket */ if (dest.ss_family != 0) { sleep(5); target.fd = connsock(&dest, dest_len, hoplimit); Log(LOG_INFO, "INFO ( %s/%s ): Exporting flows to [%s]:%s\n", config.name, config.type, dest_addr, dest_serv); } } /* * If we are over max_flows, force-expire the oldest * out first and immediately reprocess to evict them */ if (flowtrack.num_flows > max_flows) { force_expire(&flowtrack, flowtrack.num_flows - max_flows); goto expiry_check; } } /* Flags set by signal handlers or control socket */ if (graceful_shutdown_request) { Log(LOG_WARNING, "WARN ( %s/%s ): Shutting down on user request.\n", config.name, config.type); check_expired(&flowtrack, &target, CE_EXPIRE_ALL, engine_type, engine_id); goto exit_lane; } } exit_lane: if (!graceful_shutdown_request) Log(LOG_ERR, "ERROR ( %s/%s ): Exiting immediately on internal error.\n", config.name, config.type); if (target.fd != -1) close(target.fd); } pmacct-0.14.0/src/nfprobe_plugin/netflow1.c0000644000175000017500000001352711304475420017564 0ustar paolopaolo/* * Copyright 2002 Damien Miller All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: netflow1.c,v 1.2 2009/11/29 13:45:52 paolo Exp $ */ #include "common.h" #include "treetype.h" #include "nfprobe_plugin.h" RCSID("$Id: netflow1.c,v 1.2 2009/11/29 13:45:52 paolo Exp $"); /* * This is the Cisco Netflow(tm) version 1 packet format * Based on: * http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_0/nfc_ug/nfcform.htm */ struct NF1_HEADER { u_int16_t version, flows; u_int32_t uptime_ms, time_sec, time_nanosec; }; struct NF1_FLOW { u_int32_t src_ip, dest_ip, nexthop_ip; u_int16_t if_index_in, if_index_out; u_int32_t flow_packets, flow_octets; u_int32_t flow_start, flow_finish; u_int16_t src_port, dest_port; u_int16_t pad1; u_int8_t protocol, tos, tcp_flags; u_int8_t pad2, pad3, pad4; u_int32_t reserved1; #if 0 u_int8_t reserved2; /* XXX: no longer used */ #endif }; /* Maximum of 24 flows per packet */ #define NF1_MAXFLOWS 24 #define NF1_MAXPACKET_SIZE (sizeof(struct NF1_HEADER) + \ (NF1_MAXFLOWS * sizeof(struct NF1_FLOW))) /* * Given an array of expired flows, send netflow v1 report packets * Returns number of packets sent or -1 on error */ int send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock, u_int64_t *flows_exported, struct timeval *system_boot_time, int verbose_flag, u_int8_t engine_type, u_int8_t engine_id) { struct timeval now; u_int32_t uptime_ms; u_int8_t packet[NF1_MAXPACKET_SIZE]; /* Maximum allowed packet size (24 flows) */ struct NF1_HEADER *hdr = NULL; struct NF1_FLOW *flw = NULL; int i, j, offset, num_packets, err; socklen_t errsz; gettimeofday(&now, NULL); uptime_ms = timeval_sub_ms(&now, system_boot_time); hdr = (struct NF1_HEADER *)packet; for(num_packets = offset = j = i = 0; i < num_flows; i++) { if (j >= NF1_MAXFLOWS - 1) { if (verbose_flag) Log(LOG_DEBUG, "Sending flow packet len = %d\n", offset); hdr->flows = htons(hdr->flows); errsz = sizeof(err); getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz); /* Clear ICMP errors */ if (send(nfsock, packet, (size_t)offset, 0) == -1) return (-1); *flows_exported += j; j = 0; num_packets++; } if (j == 0) { memset(&packet, '\0', sizeof(packet)); hdr->version = htons(1); hdr->flows = 0; /* Filled in as we go */ hdr->uptime_ms = htonl(uptime_ms); hdr->time_sec = htonl(now.tv_sec); hdr->time_nanosec = htonl(now.tv_usec * 1000); offset = sizeof(*hdr); } flw = (struct NF1_FLOW *)(packet + offset); /* NetFlow v.1 doesn't do IPv6 */ if (flows[i]->af != AF_INET) continue; if (flows[i]->octets[0] > 0) { flw->src_ip = flows[i]->addr[0].v4.s_addr; flw->dest_ip = flows[i]->addr[1].v4.s_addr; flw->src_port = flows[i]->port[0]; flw->dest_port = flows[i]->port[1]; flw->if_index_in = htons(flows[i]->ifindex[0]); flw->if_index_out = htons(flows[i]->ifindex[1]); flw->flow_packets = htonl(flows[i]->packets[0]); flw->flow_octets = htonl(flows[i]->octets[0]); flw->flow_start = htonl(timeval_sub_ms(&flows[i]->flow_start, system_boot_time)); flw->flow_finish = htonl(timeval_sub_ms(&flows[i]->flow_last, system_boot_time)); flw->protocol = flows[i]->protocol; flw->tos = flows[i]->tos[0]; flw->tcp_flags = flows[i]->tcp_flags[0]; offset += sizeof(*flw); j++; hdr->flows++; } flw = (struct NF1_FLOW *)(packet + offset); if (flows[i]->octets[1] > 0) { flw->src_ip = flows[i]->addr[1].v4.s_addr; flw->dest_ip = flows[i]->addr[0].v4.s_addr; flw->src_port = flows[i]->port[1]; flw->dest_port = flows[i]->port[0]; flw->if_index_in = htons(flows[i]->ifindex[1]); flw->if_index_out = htons(flows[i]->ifindex[0]); flw->flow_packets = htonl(flows[i]->packets[1]); flw->flow_octets = htonl(flows[i]->octets[1]); flw->flow_start = htonl(timeval_sub_ms(&flows[i]->flow_start, system_boot_time)); flw->flow_finish = htonl(timeval_sub_ms(&flows[i]->flow_last, system_boot_time)); flw->protocol = flows[i]->protocol; flw->tos = flows[i]->tos[1]; flw->tcp_flags = flows[i]->tcp_flags[1]; offset += sizeof(*flw); j++; hdr->flows++; } } /* Send any leftovers */ if (j != 0) { if (verbose_flag) Log(LOG_DEBUG, "Sending flow packet len = %d\n", offset); hdr->flows = htons(hdr->flows); errsz = sizeof(err); getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz); /* Clear ICMP errors */ if (send(nfsock, packet, (size_t)offset, 0) == -1) return (-1); num_packets++; } *flows_exported += j; return (num_packets); } pmacct-0.14.0/src/nfprobe_plugin/convtime.c0000644000175000017500000000435610530072467017655 0ustar paolopaolo/* * Copyright (c) 2001 Kevin Steves. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "common.h" #include "convtime.h" RCSID("$Id: convtime.c,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $"); #define SECONDS 1 #define MINUTES (SECONDS * 60) #define HOURS (MINUTES * 60) #define DAYS (HOURS * 24) #define WEEKS (DAYS * 7) long int convtime(const char *s) { long total, secs; const char *p; char *endp; errno = 0; total = 0; p = s; if (p == NULL || *p == '\0') return -1; while (*p) { secs = strtol(p, &endp, 10); if (p == endp || (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || secs < 0) return -1; switch (*endp++) { case '\0': endp--; case 's': case 'S': break; case 'm': case 'M': secs *= MINUTES; break; case 'h': case 'H': secs *= HOURS; break; case 'd': case 'D': secs *= DAYS; break; case 'w': case 'W': secs *= WEEKS; break; default: return -1; } total += secs; if (total < 0) return -1; p = endp; } return total; } pmacct-0.14.0/src/nfprobe_plugin/nfprobe_plugin.h0000644000175000017500000002002111741023273021026 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* * Originally based on softflowd which is: * * Copyright (c) 2002 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SOFTFLOWD_H #define _SOFTFLOWD_H #include "common.h" #include "sys-tree.h" #include "treetype.h" /* User to setuid to and directory to chroot to when we drop privs */ #ifndef PRIVDROP_USER # define PRIVDROP_USER "nobody" #endif #define PRIVDROP_CHROOT_DIR "/var/empty" /* * Capture length for libpcap: Must fit the link layer header, plus * a maximally sized ip/ipv6 header and most of a TCP header */ #define LIBPCAP_SNAPLEN_V4 96 #define LIBPCAP_SNAPLEN_V6 160 /* * Timeouts */ #define DEFAULT_TCP_TIMEOUT 3600 #define DEFAULT_TCP_RST_TIMEOUT 120 #define DEFAULT_TCP_FIN_TIMEOUT 300 #define DEFAULT_UDP_TIMEOUT 300 #define DEFAULT_ICMP_TIMEOUT 300 #define DEFAULT_GENERAL_TIMEOUT 3600 #define DEFAULT_MAXIMUM_LIFETIME (3600*24*7) #define DEFAULT_EXPIRY_INTERVAL 60 /* * Default maximum number of flow to track simultaneously * 8192 corresponds to just under 1Mb of flow data */ #define DEFAULT_MAX_FLOWS 8192 /* Store a couple of statistics, maybe more in the future */ struct STATISTIC { double min, mean, max; }; /* * This structure is the root of the flow tracking system. * It holds the root of the tree of active flows and the head of the * tree of expiry events. It also collects miscellaneous statistics */ struct FLOWTRACK { /* The flows and their expiry events */ FLOW_HEAD(FLOWS, FLOW) flows; /* Top of flow tree */ EXPIRY_HEAD(EXPIRIES, EXPIRY) expiries; /* Top of expiries tree */ unsigned int num_flows; /* # of active flows */ u_int64_t next_flow_seq; /* Next flow ID */ u_int32_t next_datagram_seq; /* Stuff related to flow export */ struct timeval system_boot_time; /* SysUptime */ /* Flow timeouts */ int tcp_timeout; /* Open TCP connections */ int tcp_rst_timeout; /* TCP flows after RST */ int tcp_fin_timeout; /* TCP flows after bidi FIN */ int udp_timeout; /* UDP flows */ int icmp_timeout; /* ICMP flows */ int general_timeout; /* Everything else */ int maximum_lifetime; /* Maximum life for flows */ int expiry_interval; /* Interval between expiries */ /* Statistics */ u_int64_t total_packets; /* # of good packets */ u_int64_t frag_packets; /* # of fragmented packets */ u_int64_t non_ip_packets; /* # of not-IP packets */ u_int64_t bad_packets; /* # of bad packets */ u_int64_t flows_expired; /* # expired */ u_int64_t flows_exported; /* # of flows sent */ u_int64_t flows_dropped; /* # of flows dropped */ u_int64_t flows_force_expired; /* # of flows forced out */ u_int64_t packets_sent; /* # netflow packets sent */ struct STATISTIC duration; /* Flow duration */ struct STATISTIC octets; /* Bytes (bidir) */ struct STATISTIC packets; /* Packets (bidir) */ /* Per protocol statistics */ u_int64_t flows_pp[256]; u_int64_t octets_pp[256]; u_int64_t packets_pp[256]; struct STATISTIC duration_pp[256]; /* Timeout statistics */ u_int64_t expired_general; u_int64_t expired_tcp; u_int64_t expired_tcp_rst; u_int64_t expired_tcp_fin; u_int64_t expired_udp; u_int64_t expired_icmp; u_int64_t expired_maxlife; u_int64_t expired_overbytes; u_int64_t expired_maxflows; u_int64_t expired_flush; }; /* * This structure is an entry in the tree of flows that we are * currently tracking. * * Because flows are matched _bi-directionally_, they must be stored in * a canonical format: the numerically lowest address and port number must * be stored in the first address and port array slot respectively. */ struct FLOW { /* Housekeeping */ struct EXPIRY *expiry; /* Pointer to expiry record */ FLOW_ENTRY(FLOW) trp; /* Tree pointer */ /* Flow identity (all are in network byte order) */ int af; /* Address family of flow */ u_int8_t direction[2]; /* Flow direction */ u_int32_t ip6_flowlabel[2]; /* IPv6 Flowlabel */ union { struct in_addr v4; struct in6_addr v6; } addr[2]; /* Endpoint addresses */ u_int8_t mask[2]; /* Network masks */ u_int16_t port[2]; /* Endpoint ports */ u_int8_t tcp_flags[2]; /* Cumulative OR of flags */ u_int8_t protocol; /* Protocol */ u_int8_t tos[2]; /* ToS/DSCP */ /* ASN/BGP stuff */ as_t as[2]; /* Autonomous System numbers */ union { struct in_addr v4; struct in6_addr v6; } bgp_next_hop[2]; /* L2 stuff */ u_int8_t mac[2][6]; /* Endpoint L2/Ethernet MAC addresses */ u_int16_t vlan; /* VLAN ID */ u_int32_t mpls_label[2]; /* MPLS top label */ u_int16_t ifindex[2]; /* input/output ifindex */ /* classification stuff */ pm_class_t class; /* Classification internal ID */ pm_id_t tag[2]; /* Tag */ pm_id_t tag2[2]; /* Tag2 */ /* Per-flow statistics (all in _host_ byte order) */ u_int64_t flow_seq; /* Flow ID */ struct timeval flow_start; /* Time of creation */ struct timeval flow_last; /* Time of last traffic */ /* Per-endpoint statistics (all in _host_ byte order) */ u_int32_t octets[2]; /* Octets so far */ u_int32_t packets[2]; /* Packets so far */ u_int32_t flows[2]; /* Flows so far */ }; /* * This is an entry in the tree of expiry events. The tree is used to * avoid traversion the whole tree of active flows looking for ones to * expire. "expires_at" is the time at which the flow should be discarded, * or zero if it is scheduled for immediate disposal. * * When a flow which hasn't been scheduled for immediate expiry registers * traffic, it is deleted from its current position in the tree and * re-inserted (subject to its updated timeout). * * Expiry scans operate by starting at the head of the tree and expiring * each entry with expires_at < now * */ struct EXPIRY { EXPIRY_ENTRY(EXPIRY) trp; /* Tree pointer */ struct FLOW *flow; /* pointer to flow */ u_int32_t expires_at; /* time_t */ enum { R_GENERAL, R_TCP, R_TCP_RST, R_TCP_FIN, R_UDP, R_ICMP, R_MAXLIFE, R_OVERBYTES, R_OVERFLOWS, R_FLUSH } reason; }; /* Prototype for functions shared from softflowd.c */ u_int32_t timeval_sub_ms(const struct timeval *t1, const struct timeval *t2); /* Prototypes for functions to send NetFlow packets, from netflow*.c */ int send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock, u_int64_t *flows_exported, struct timeval *system_boot_time, int verbose_flag, u_int8_t engine_type, u_int8_t engine_id); int send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock, u_int64_t *flows_exported, struct timeval *system_boot_time, int verbose_flag, u_int8_t engine_type, u_int8_t engine_id); int send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock, u_int64_t *flows_exported, struct timeval *system_boot_time, int verbose_flag, u_int8_t engine_type, u_int8_t engine_id); #endif /* _SOFTFLOWD_H */ pmacct-0.14.0/src/nfprobe_plugin/common.h0000644000175000017500000001053710530072467017324 0ustar paolopaolo/* * Copyright (c) 2002 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SFD_COMMON_H #define _SFD_COMMON_H #define _BSD_SOURCE /* Needed for BSD-style struct ip,tcp,udp on Linux */ #include "pmacct.h" #include #include #include #include #include #include #include #include #include #include #if defined ENABLE_IPV6 #include "../../include/ip6.h" #include "../../include/ah.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_NET_BPF_H) #include #elif defined(HAVE_PCAP_BPF_H) #include #endif #if defined(HAVE_INTTYPES_H) #include #endif /* The name of the program */ // #define PROGNAME "softflowd" /* The name of the program */ #define PROGVER "0.9.7" /* Default pidfile */ #define DEFAULT_PIDFILE "/var/run/" PROGNAME ".pid" /* Default control socket */ #define DEFAULT_CTLSOCK "/var/run/" PROGNAME ".ctl" #define RCSID(msg) \ static /**/const char *const flowd_rcsid[] = \ { (const char *)flowd_rcsid, "\100(#)" msg } \ /* #ifndef IP_OFFMASK # define IP_OFFMASK 0x1fff #endif #ifndef IPV6_VERSION #define IPV6_VERSION 0x60 #endif #ifndef IPV6_VERSION_MASK #define IPV6_VERSION_MASK 0xf0 #endif #ifndef IPV6_FLOWINFO_MASK #define IPV6_FLOWINFO_MASK ntohl(0x0fffffff) #endif #ifndef IPV6_FLOWLABEL_MASK #define IPV6_FLOWLABEL_MASK ntohl(0x000fffff) #endif */ #ifndef _PATH_DEVNULL # define _PATH_DEVNULL "/dev/null" #endif #ifndef MIN # define MIN(a,b) (((a)<(b))?(a):(b)) #endif #ifndef MAX # define MAX(a,b) (((a)>(b))?(a):(b)) #endif #ifndef offsetof # define offsetof(type, member) ((size_t) &((type *)0)->member) #endif #if defined(__GNUC__) # ifndef __dead # define __dead __attribute__((__noreturn__)) # endif # ifndef __packed # define __packed __attribute__((__packed__)) # endif #endif #if !defined(HAVE_INT8_T) && defined(OUR_CFG_INT8_T) typedef OUR_CFG_INT8_T int8_t; #endif #if !defined(HAVE_INT16_T) && defined(OUR_CFG_INT16_T) typedef OUR_CFG_INT16_T int16_t; #endif #if !defined(HAVE_INT32_T) && defined(OUR_CFG_INT32_T) typedef OUR_CFG_INT32_T int32_t; #endif #if !defined(HAVE_INT64_T) && defined(OUR_CFG_INT64_T) typedef OUR_CFG_INT64_T int64_t; #endif #if !defined(HAVE_U_INT8_T) && defined(OUR_CFG_U_INT8_T) typedef OUR_CFG_U_INT8_T u_int8_t; #endif #if !defined(HAVE_U_INT16_T) && defined(OUR_CFG_U_INT16_T) typedef OUR_CFG_U_INT16_T u_int16_t; #endif #if !defined(HAVE_U_INT32_T) && defined(OUR_CFG_U_INT32_T) typedef OUR_CFG_U_INT32_T u_int32_t; #endif #if !defined(HAVE_U_INT64_T) && defined(OUR_CFG_U_INT64_T) typedef OUR_CFG_U_INT64_T u_int64_t; #endif #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t siz); #endif #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t siz); #endif #endif /* _SFD_COMMON_H */ pmacct-0.14.0/src/nfprobe_plugin/sys-tree.h0000644000175000017500000005414110530072467017606 0ustar paolopaolo/* $OpenBSD: tree.h,v 1.6 2002/06/11 22:09:52 provos Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-back tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ RB_AUGMENT(RB_PARENT(elm, field)); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ RB_AUGMENT(RB_PARENT(elm, field)); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ } while (0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ void name##_RB_INSERT_COLOR(struct name *, struct type *); \ void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ struct type *name##_RB_REMOVE(struct name *, struct type *); \ struct type *name##_RB_INSERT(struct name *, struct type *); \ struct type *name##_RB_FIND(struct name *, struct type *); \ struct type *name##_RB_NEXT(struct name *, struct type *); \ struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)))\ RB_COLOR(oleft, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field);\ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_LEFT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)))\ RB_COLOR(oright, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field);\ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_RIGHT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old)\ RB_LEFT(RB_PARENT(old, field), field) = elm;\ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm;\ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ struct type * \ name##_RB_NEXT(struct name *head, struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(x, y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(head, x)) #endif /* _SYS_TREE_H_ */ pmacct-0.14.0/src/nfprobe_plugin/treetype.h0000644000175000017500000000572610530072467017701 0ustar paolopaolo/* * Copyright 2002 Damien Miller All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: treetype.h,v 1.1.1.1 2006/11/19 15:16:07 paolo Exp $ */ /* Select our tree types for various data structures */ #if defined(FLOW_RB) #define FLOW_HEAD RB_HEAD #define FLOW_ENTRY RB_ENTRY #define FLOW_PROTOTYPE RB_PROTOTYPE #define FLOW_GENERATE RB_GENERATE #define FLOW_INSERT RB_INSERT #define FLOW_FIND RB_FIND #define FLOW_REMOVE RB_REMOVE #define FLOW_FOREACH RB_FOREACH #define FLOW_MIN RB_MIN #define FLOW_NEXT RB_NEXT #define FLOW_INIT RB_INIT #elif defined(FLOW_SPLAY) #define FLOW_HEAD SPLAY_HEAD #define FLOW_ENTRY SPLAY_ENTRY #define FLOW_PROTOTYPE SPLAY_PROTOTYPE #define FLOW_GENERATE SPLAY_GENERATE #define FLOW_INSERT SPLAY_INSERT #define FLOW_FIND SPLAY_FIND #define FLOW_REMOVE SPLAY_REMOVE #define FLOW_FOREACH SPLAY_FOREACH #define FLOW_MIN SPLAY_MIN #define FLOW_NEXT SPLAY_NEXT #define FLOW_INIT SPLAY_INIT #else #error No flow tree type defined #endif #if defined(EXPIRY_RB) #define EXPIRY_HEAD RB_HEAD #define EXPIRY_ENTRY RB_ENTRY #define EXPIRY_PROTOTYPE RB_PROTOTYPE #define EXPIRY_GENERATE RB_GENERATE #define EXPIRY_INSERT RB_INSERT #define EXPIRY_FIND RB_FIND #define EXPIRY_REMOVE RB_REMOVE #define EXPIRY_FOREACH RB_FOREACH #define EXPIRY_MIN RB_MIN #define EXPIRY_NEXT RB_NEXT #define EXPIRY_INIT RB_INIT #elif defined(EXPIRY_SPLAY) #define EXPIRY_HEAD SPLAY_HEAD #define EXPIRY_ENTRY SPLAY_ENTRY #define EXPIRY_PROTOTYPE SPLAY_PROTOTYPE #define EXPIRY_GENERATE SPLAY_GENERATE #define EXPIRY_INSERT SPLAY_INSERT #define EXPIRY_FIND SPLAY_FIND #define EXPIRY_REMOVE SPLAY_REMOVE #define EXPIRY_FOREACH SPLAY_FOREACH #define EXPIRY_MIN SPLAY_MIN #define EXPIRY_NEXT SPLAY_NEXT #define EXPIRY_INIT SPLAY_INIT #else #error No expiry tree type defined #endif pmacct-0.14.0/src/pretag.c0000644000175000017500000004047111663736164014311 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ /* defines */ #define __PRETAG_C /* includes */ #include "pmacct.h" #include "nfacctd.h" #include "pretag_handlers.h" #include "pretag-data.h" void load_id_file(int acct_type, char *filename, struct id_table *t, struct plugin_requests *req, int *map_allocated) { struct id_table tmp; struct id_entry *ptr, *ptr2; FILE *file; char buf[SRVBUFLEN]; int v4_num = 0, x, tot_lines = 0, err, index, label_solved, sz; struct stat st; #if defined ENABLE_IPV6 int v6_num = 0; #endif if (!map_allocated) return; /* parsing engine vars */ char *start, *key = NULL, *value = NULL; int len; Log(LOG_INFO, "INFO ( default/core ): Trying to (re)load map: %s\n", filename); memset(&st, 0, sizeof(st)); if (!config.pre_tag_map_entries) config.pre_tag_map_entries = MAX_PRETAG_MAP_ENTRIES; if (filename) { if ((file = fopen(filename, "r")) == NULL) { Log(LOG_ERR, "ERROR: map '%s' not found.\n", filename); goto handle_error; } sz = sizeof(struct id_entry)*config.pre_tag_map_entries; if (*map_allocated == 0) { memset(t, 0, sizeof(struct id_table)); t->e = (struct id_entry *) malloc(sz); *map_allocated = TRUE; } else { ptr = t->e ; memset(t, 0, sizeof(struct id_table)); t->e = ptr ; } memset(&tmp, 0, sizeof(struct id_table)); tmp.e = (struct id_entry *) malloc(sz); memset(tmp.e, 0, sz); memset(t->e, 0, sz); /* first stage: reading Agent ID file and arranging it in a temporary memory table */ while (!feof(file)) { tot_lines++; if (tmp.num >= config.pre_tag_map_entries) { Log(LOG_WARNING, "WARN ( default/core ): map '%s' cut to the first %u entries. Number of entries can be configured via 'pre_tag_map_etries'.\n", filename, config.pre_tag_map_entries); break; } memset(buf, 0, SRVBUFLEN); if (fgets(buf, SRVBUFLEN, file)) { if (!iscomment(buf) && !isblankline(buf)) { if (!check_not_valid_char(filename, buf, '|')) { mark_columns(buf); trim_all_spaces(buf); strip_quotes(buf); /* resetting the entry and enforcing defaults */ memset(&tmp.e[tmp.num], 0, sizeof(struct id_entry)); tmp.e[tmp.num].ret = FALSE; err = FALSE; key = NULL; value = NULL; start = buf; len = strlen(buf); for (x = 0; x <= len; x++) { if (buf[x] == '=') { if (start == &buf[x]) continue; if (!key) { buf[x] = '\0'; key = start; x++; start = &buf[x]; } } if ((buf[x] == '|') || (buf[x] == '\0')) { if (start == &buf[x]) continue; buf[x] = '\0'; if (value || !key) { Log(LOG_ERR, "ERROR ( %s ): malformed line %d. Ignored.\n", filename, tot_lines); err = TRUE; break; } else value = start; x++; start = &buf[x]; } if (key && value) { int dindex; /* dictionary index */ /* Processing of source BGP-related primitives kept consistent; This can indeed be split as required in future */ if (acct_type == MAP_BGP_PEER_AS_SRC || acct_type == MAP_BGP_SRC_LOCAL_PREF || acct_type == MAP_BGP_SRC_MED) { for (dindex = 0; strcmp(bpas_map_dictionary[dindex].key, ""); dindex++) { if (!strcmp(bpas_map_dictionary[dindex].key, key)) { err = (*bpas_map_dictionary[dindex].func)(filename, &tmp.e[tmp.num], value, req, acct_type); break; } else err = E_NOTFOUND; /* key not found */ } if (err) { if (err == E_NOTFOUND) Log(LOG_ERR, "ERROR ( %s ): unknown key '%s' at line %d. Ignored.\n", filename, key, tot_lines); else Log(LOG_ERR, "Line %d ignored.\n", tot_lines); break; } key = NULL; value = NULL; } else if (acct_type == MAP_BGP_TO_XFLOW_AGENT) { for (dindex = 0; strcmp(bta_map_dictionary[dindex].key, ""); dindex++) { if (!strcmp(bta_map_dictionary[dindex].key, key)) { err = (*bta_map_dictionary[dindex].func)(filename, &tmp.e[tmp.num], value, req, acct_type); break; } else err = E_NOTFOUND; /* key not found */ } if (err) { if (err == E_NOTFOUND) Log(LOG_ERR, "ERROR ( %s ): unknown key '%s' at line %d. Ignored.\n", filename, key, tot_lines); else Log(LOG_ERR, "Line %d ignored.\n", tot_lines); break; } key = NULL; value = NULL; } else if (acct_type == MAP_BGP_IFACE_TO_RD) { for (dindex = 0; strcmp(bitr_map_dictionary[dindex].key, ""); dindex++) { if (!strcmp(bitr_map_dictionary[dindex].key, key)) { err = (*bitr_map_dictionary[dindex].func)(filename, &tmp.e[tmp.num], value, req, acct_type); break; } else err = E_NOTFOUND; /* key not found */ } if (err) { if (err == E_NOTFOUND) Log(LOG_ERR, "ERROR ( %s ): unknown key '%s' at line %d. Ignored.\n", filename, key, tot_lines); else Log(LOG_ERR, "Line %d ignored.\n", tot_lines); break; } key = NULL; value = NULL; } else if (acct_type == MAP_SAMPLING) { for (dindex = 0; strcmp(sampling_map_dictionary[dindex].key, ""); dindex++) { if (!strcmp(sampling_map_dictionary[dindex].key, key)) { err = (*sampling_map_dictionary[dindex].func)(filename, &tmp.e[tmp.num], value, req, acct_type); break; } else err = E_NOTFOUND; /* key not found */ } if (err) { if (err == E_NOTFOUND) Log(LOG_ERR, "ERROR ( %s ): unknown key '%s' at line %d. Ignored.\n", filename, key, tot_lines); else Log(LOG_ERR, "Line %d ignored.\n", tot_lines); break; } key = NULL; value = NULL; } else { if (tee_plugins) { for (dindex = 0; strcmp(tag_map_tee_dictionary[dindex].key, ""); dindex++) { if (!strcmp(tag_map_tee_dictionary[dindex].key, key)) { err = (*tag_map_tee_dictionary[dindex].func)(filename, &tmp.e[tmp.num], value, req, acct_type); break; } else err = E_NOTFOUND; /* key not found */ } } else { for (dindex = 0; strcmp(tag_map_dictionary[dindex].key, ""); dindex++) { if (!strcmp(tag_map_dictionary[dindex].key, key)) { err = (*tag_map_dictionary[dindex].func)(filename, &tmp.e[tmp.num], value, req, acct_type); break; } else err = E_NOTFOUND; /* key not found */ } } if (err) { if (err == E_NOTFOUND) Log(LOG_ERR, "ERROR ( %s ): unknown key '%s' at line %d. Ignored.\n", filename, key, tot_lines); else Log(LOG_ERR, "Line %d ignored.\n", tot_lines); break; } key = NULL; value = NULL; } } } /* verifying errors and required fields */ if (acct_type == ACCT_NF || acct_type == ACCT_SF) { if (tmp.e[tmp.num].id && tmp.e[tmp.num].id2) Log(LOG_ERR, "ERROR ( %s ): 'id' and 'id2' are mutual exclusive at line: %d.\n", filename, tot_lines); else if (!err && (tmp.e[tmp.num].id || tmp.e[tmp.num].id2) && tmp.e[tmp.num].agent_ip.a.family) { int j; for (j = 0; tmp.e[tmp.num].func[j]; j++); if (tmp.e[tmp.num].id) tmp.e[tmp.num].func[j] = pretag_id_handler; else if (tmp.e[tmp.num].id2) tmp.e[tmp.num].func[j] = pretag_id2_handler; if (tmp.e[tmp.num].agent_ip.a.family == AF_INET) v4_num++; #if defined ENABLE_IPV6 else if (tmp.e[tmp.num].agent_ip.a.family == AF_INET6) v6_num++; #endif tmp.num++; } /* if any required field is missing and other errors have been signalled before we will trap an error message */ else if (((!tmp.e[tmp.num].id && !tmp.e[tmp.num].id2) || !tmp.e[tmp.num].agent_ip.a.family) && !err) Log(LOG_ERR, "ERROR ( %s ): required key missing at line: %d. Required keys are: 'id', 'ip'.\n", filename, tot_lines); } else if (acct_type == ACCT_PM) { if (tmp.e[tmp.num].id && tmp.e[tmp.num].id2) Log(LOG_ERR, "ERROR ( %s ): 'id' and 'id2' are mutual exclusive at line: %d.\n", filename, tot_lines); else if (tmp.e[tmp.num].agent_ip.a.family) Log(LOG_ERR, "ERROR ( %s ): key 'ip' not applicable here. Invalid line: %d.\n", filename, tot_lines); else if (!err && (tmp.e[tmp.num].id || tmp.e[tmp.num].id2)) { int j; for (j = 0; tmp.e[tmp.num].func[j]; j++); tmp.e[tmp.num].agent_ip.a.family = AF_INET; /* we emulate a dummy '0.0.0.0' IPv4 address */ if (tmp.e[tmp.num].id) tmp.e[tmp.num].func[j] = pretag_id_handler; else if (tmp.e[tmp.num].id2) tmp.e[tmp.num].func[j] = pretag_id2_handler; v4_num++; tmp.num++; } } else if (acct_type == MAP_BGP_PEER_AS_SRC || acct_type == MAP_BGP_SRC_LOCAL_PREF || acct_type == MAP_BGP_SRC_MED) { if (!err && (tmp.e[tmp.num].id || tmp.e[tmp.num].flags) && tmp.e[tmp.num].agent_ip.a.family) { int j; for (j = 0; tmp.e[tmp.num].func[j]; j++); tmp.e[tmp.num].func[j] = pretag_id_handler; if (tmp.e[tmp.num].agent_ip.a.family == AF_INET) v4_num++; #if defined ENABLE_IPV6 else if (tmp.e[tmp.num].agent_ip.a.family == AF_INET6) v6_num++; #endif tmp.num++; } else if ((!tmp.e[tmp.num].id || !tmp.e[tmp.num].agent_ip.a.family) && !err) Log(LOG_ERR, "ERROR ( %s ): required key missing at line: %d. Required keys are: 'id', 'ip'.\n", filename, tot_lines); } else if (acct_type == MAP_BGP_TO_XFLOW_AGENT) { if (!err && tmp.e[tmp.num].id && tmp.e[tmp.num].agent_ip.a.family) { int j; for (j = 0; tmp.e[tmp.num].func[j]; j++); tmp.e[tmp.num].func[j] = pretag_id_handler; if (tmp.e[tmp.num].agent_ip.a.family == AF_INET) v4_num++; #if defined ENABLE_IPV6 else if (tmp.e[tmp.num].agent_ip.a.family == AF_INET6) v6_num++; #endif tmp.num++; } else if ((!tmp.e[tmp.num].id || !tmp.e[tmp.num].agent_ip.a.family) && !err) Log(LOG_ERR, "ERROR ( %s ): required key missing at line: %d. Required keys are: 'id', 'ip'.\n", filename, tot_lines); } else if (acct_type == MAP_BGP_IFACE_TO_RD) { if (!err && tmp.e[tmp.num].id && tmp.e[tmp.num].agent_ip.a.family) { int j; for (j = 0; tmp.e[tmp.num].func[j]; j++); tmp.e[tmp.num].func[j] = pretag_id_handler; if (tmp.e[tmp.num].agent_ip.a.family == AF_INET) v4_num++; #if defined ENABLE_IPV6 else if (tmp.e[tmp.num].agent_ip.a.family == AF_INET6) v6_num++; #endif tmp.num++; } } else if (acct_type == MAP_SAMPLING) { if (!err && tmp.e[tmp.num].id && tmp.e[tmp.num].agent_ip.a.family) { int j; for (j = 0; tmp.e[tmp.num].func[j]; j++); tmp.e[tmp.num].func[j] = pretag_id_handler; if (tmp.e[tmp.num].agent_ip.a.family == AF_INET) v4_num++; #if defined ENABLE_IPV6 else if (tmp.e[tmp.num].agent_ip.a.family == AF_INET6) v6_num++; #endif tmp.num++; } else if ((!tmp.e[tmp.num].id || !tmp.e[tmp.num].agent_ip.a.family) && !err) Log(LOG_ERR, "ERROR ( %s ): required key missing at line: %d. Required keys are: 'id', 'ip'.\n", filename, tot_lines); } } else Log(LOG_ERR, "ERROR ( %s ): malformed line: %d. Ignored.\n", filename, tot_lines); } } } fclose(file); stat(filename, &st); t->timestamp = st.st_mtime; /* second stage: segregating IPv4, IPv6. No further reordering in order to deal smoothly with jumps (ie. JEQs) */ x = 0; t->num = tmp.num; t->ipv4_num = v4_num; t->ipv4_base = &t->e[x]; for (index = 0; index < tmp.num; index++) { if (tmp.e[index].agent_ip.a.family == AF_INET) { memcpy(&t->e[x], &tmp.e[index], sizeof(struct id_entry)); t->e[x].pos = x; x++; } } #if defined ENABLE_IPV6 t->ipv6_num = v6_num; t->ipv6_base = &t->e[x]; for (index = 0; index < tmp.num; index++) { if (tmp.e[index].agent_ip.a.family == AF_INET6) { memcpy(&t->e[x], &tmp.e[index], sizeof(struct id_entry)); t->e[x].pos = x; x++; } } #endif /* third stage: building short circuits basing on jumps and labels. Only forward references are solved. Backward and unsolved references will generate errors. */ for (ptr = t->ipv4_base, x = 0; x < t->ipv4_num; ptr++, x++) { if (ptr->jeq.label) { label_solved = FALSE; /* honouring reserved labels (ie. "next"). Then resolving unknown labels */ if (!strcmp(ptr->jeq.label, "next")) { ptr->jeq.ptr = ptr+1; label_solved = TRUE; } else { for (ptr2 = ptr+1, index = x+1; index < t->ipv4_num; ptr2++, index++) { if (!strcmp(ptr->jeq.label, ptr2->label)) { ptr->jeq.ptr = ptr2; label_solved = TRUE; } } } if (!label_solved) { ptr->jeq.ptr = NULL; Log(LOG_ERR, "ERROR ( %s ): Unresolved label '%s'. Ignoring it.\n", filename, ptr->jeq.label); } free(ptr->jeq.label); ptr->jeq.label = NULL; } } #if defined ENABLE_IPV6 for (ptr = t->ipv6_base, x = 0; x < t->ipv6_num; ptr++, x++) { if (ptr->jeq.label) { label_solved = FALSE; for (ptr2 = ptr+1, index = x+1; index < t->ipv6_num; ptr2++, index++) { if (!strcmp(ptr->jeq.label, ptr2->label)) { ptr->jeq.ptr = ptr2; label_solved = TRUE; } } if (!label_solved) { ptr->jeq.ptr = NULL; Log(LOG_ERR, "ERROR ( %s ): Unresolved label '%s'. Ignoring it.\n", filename, ptr->jeq.label); } free(ptr->jeq.label); ptr->jeq.label = NULL; } } #endif } free(tmp.e) ; Log(LOG_INFO, "INFO ( default/core ): map '%s' successfully (re)loaded.\n", filename); return; handle_error: if (*map_allocated && tmp.e) free(tmp.e) ; if (t->timestamp) { Log(LOG_WARNING, "WARN: Rolling back the old map '%s'.\n", filename); /* we update the timestamp to avoid loops */ stat(filename, &st); t->timestamp = st.st_mtime; } else exit_all(1); } u_int8_t pt_check_neg(char **value) { if (**value == '-') { (*value)++; return TRUE; } else return FALSE; } char *pt_check_range(char *str) { char *ptr; if (ptr = strchr(str, '-')) { *ptr = '\0'; ptr++; return ptr; } else return NULL; } pmacct-0.14.0/src/cfg_handlers.h0000644000175000017500000002300011734666122015433 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* prototypes */ #if (!defined __CFG_HANDLERS_C) #define EXT extern #else #define EXT #endif EXT int parse_truefalse(char *); EXT int cfg_key_debug(char *, char *, char *); EXT int cfg_key_syslog(char *, char *, char *); EXT int cfg_key_logfile(char *, char *, char *); EXT int cfg_key_pidfile(char *, char *, char *); EXT int cfg_key_daemonize(char *, char *, char *); EXT int cfg_key_aggregate(char *, char *, char *); EXT int cfg_key_snaplen(char *, char *, char *); EXT int cfg_key_aggregate_filter(char *, char *, char *); EXT int cfg_key_pcap_filter(char *, char *, char *); EXT int cfg_key_interface(char *, char *, char *); EXT int cfg_key_interface_wait(char *, char *, char *); EXT int cfg_key_files_umask(char *, char *, char *); EXT int cfg_key_files_uid(char *, char *, char *); EXT int cfg_key_files_gid(char *, char *, char *); EXT int cfg_key_savefile_wait(char *, char *, char *); EXT int cfg_key_promisc(char *, char *, char *); EXT int cfg_key_num_protos(char *, char *, char *); EXT int cfg_key_num_hosts(char *, char *, char *); EXT int cfg_key_imt_path(char *, char *, char *); EXT int cfg_key_imt_passwd(char *, char *, char *); EXT int cfg_key_imt_buckets(char *, char *, char *); EXT int cfg_key_imt_mem_pools_number(char *, char *, char *); EXT int cfg_key_imt_mem_pools_size(char *, char *, char *); EXT int cfg_key_sql_db(char *, char *, char *); EXT int cfg_key_sql_table(char *, char *, char *); EXT int cfg_key_sql_table_schema(char *, char *, char *); EXT int cfg_key_sql_table_version(char *, char *, char *); EXT int cfg_key_sql_table_type(char *, char *, char *); EXT int cfg_key_sql_host(char *, char *, char *); EXT int cfg_key_sql_data(char *, char *, char *); EXT int cfg_key_sql_user(char *, char *, char *); EXT int cfg_key_sql_passwd(char *, char *, char *); EXT int cfg_key_sql_refresh_time(char *, char *, char *); EXT int cfg_key_sql_startup_delay(char *, char *, char *); EXT int cfg_key_sql_optimize_clauses(char *, char *, char *); EXT int cfg_key_sql_history(char *, char *, char *); EXT int cfg_key_sql_history_roundoff(char *, char *, char *); EXT int cfg_key_sql_history_since_epoch(char *, char *, char *); EXT int cfg_key_sql_recovery_logfile(char *, char *, char *); EXT int cfg_key_sql_recovery_backup_host(char *, char *, char *); EXT int cfg_key_sql_max_writers(char *, char *, char *); EXT int cfg_key_sql_trigger_exec(char *, char *, char *); EXT int cfg_key_sql_trigger_time(char *, char *, char *); EXT int cfg_key_sql_cache_entries(char *, char *, char *); EXT int cfg_key_sql_dont_try_update(char *, char *, char *); EXT int cfg_key_sql_preprocess(char *, char *, char *); EXT int cfg_key_sql_preprocess_type(char *, char *, char *); EXT int cfg_key_sql_multi_values(char *, char *, char *); EXT int cfg_key_sql_aggressive_classification(char *, char *, char *); EXT int cfg_key_sql_locking_style(char *, char *, char *); EXT int cfg_key_sql_use_copy(char *, char *, char *); EXT int cfg_key_sql_delimiter(char *, char *, char *); EXT int cfg_key_plugin_pipe_size(char *, char *, char *); EXT int cfg_key_plugin_pipe_backlog(char *, char *, char *); EXT int cfg_key_plugin_buffer_size(char *, char *, char *); EXT int cfg_key_networks_mask(char *, char *, char *); EXT int cfg_key_networks_file(char *, char *, char *); EXT int cfg_key_networks_cache_entries(char *, char *, char *); EXT int cfg_key_ports_file(char *, char *, char *); EXT int cfg_key_refresh_maps(char *, char *, char *); EXT int cfg_key_print_refresh_time(char *, char *, char *); EXT int cfg_key_print_cache_entries(char *, char *, char *); EXT int cfg_key_print_markers(char *, char *, char *); EXT int cfg_key_print_output(char *, char *, char *); EXT int cfg_key_nfacctd_port(char *, char *, char *); EXT int cfg_key_nfacctd_ip(char *, char *, char *); EXT int cfg_key_nfacctd_allow_file(char *, char *, char *); EXT int cfg_key_nfacctd_time_secs(char *, char *, char *); EXT int cfg_key_nfacctd_time_new(char *, char *, char *); EXT int cfg_key_nfacctd_as_new(char *, char *, char *); EXT int cfg_key_nfacctd_net(char *, char *, char *); EXT int cfg_key_nfacctd_disable_checks(char *, char *, char *); EXT int cfg_key_nfacctd_mcast_groups(char *, char *, char *); EXT int cfg_key_nfacctd_sql_log(char *, char *, char *); EXT int cfg_key_pmacctd_force_frag_handling(char *, char *, char *); EXT int cfg_key_pmacctd_frag_buffer_size(char *, char *, char *); EXT int cfg_key_pmacctd_flow_buffer_size(char *, char *, char *); EXT int cfg_key_pmacctd_flow_buffer_buckets(char *, char *, char *); EXT int cfg_key_pmacctd_conntrack_buffer_size(char *, char *, char *); EXT int cfg_key_pmacctd_flow_lifetime(char *, char *, char *); EXT int cfg_key_pmacctd_ext_sampling_rate(char *, char *, char *); EXT int cfg_key_sfacctd_renormalize(char *, char *, char *); EXT int cfg_key_pcap_savefile(char *, char *, char *); EXT int cfg_key_pre_tag_map(char *, char *, char *); EXT int cfg_key_pre_tag_map_entries(char *, char *, char *); EXT int cfg_key_pre_tag_filter(char *, char *, char *); EXT int cfg_key_pre_tag2_filter(char *, char *, char *); EXT int cfg_key_post_tag(char *, char *, char *); EXT int cfg_key_sampling_rate(char *, char *, char *); EXT int cfg_key_sampling_map(char *, char *, char *); EXT int cfg_key_classifiers(char *, char *, char *); EXT int cfg_key_classifier_tentatives(char *, char *, char *); EXT int cfg_key_classifier_table_num(char *, char *, char *); EXT int cfg_key_nfprobe_timeouts(char *, char *, char *); EXT int cfg_key_nfprobe_hoplimit(char *, char *, char *); EXT int cfg_key_nfprobe_maxflows(char *, char *, char *); EXT int cfg_key_nfprobe_receiver(char *, char *, char *); EXT int cfg_key_nfprobe_version(char *, char *, char *); EXT int cfg_key_nfprobe_engine(char *, char *, char *); EXT int cfg_key_nfprobe_peer_as(char *, char *, char *); EXT int cfg_key_nfprobe_source_ip(char *, char *, char *); EXT int cfg_key_nfprobe_ip_precedence(char *, char *, char *); EXT int cfg_key_nfprobe_direction(char *, char *, char *); EXT int cfg_key_nfprobe_ifindex(char *, char *, char *); EXT int cfg_key_sfprobe_receiver(char *, char *, char *); EXT int cfg_key_sfprobe_agentip(char *, char *, char *); EXT int cfg_key_sfprobe_agentsubid(char *, char *, char *); EXT int cfg_key_sfprobe_ifspeed(char *, char *, char *); EXT int cfg_key_tee_transparent(char *, char *, char *); EXT int cfg_key_nfacctd_bgp(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_msglog(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_max_peers(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_ip(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_port(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_ip_precedence(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_allow_file(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_aspath_radius(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_stdcomm_pattern(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_extcomm_pattern(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_stdcomm_pattern_to_asn(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_peer_src_as_type(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_src_std_comm_type(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_src_ext_comm_type(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_src_as_path_type(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_src_local_pref_type(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_src_med_type(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_peer_as_skip_subas(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_peer_src_as_map(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_src_local_pref_map(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_src_med_map(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_to_agent_map(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_iface_to_rd_map(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_follow_default(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_follow_nexthop(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_neighbors_file(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_md5_file(char *, char *, char *); EXT int cfg_key_nfacctd_bgp_table_peer_buckets(char *, char *, char *); EXT int cfg_key_nfacctd_isis(char *, char *, char *); EXT int cfg_key_nfacctd_isis_ip(char *, char *, char *); EXT int cfg_key_nfacctd_isis_net(char *, char *, char *); EXT int cfg_key_nfacctd_isis_iface(char *, char *, char *); EXT int cfg_key_nfacctd_isis_mtu(char *, char *, char *); EXT int cfg_key_nfacctd_isis_msglog(char *, char *, char *); EXT int cfg_key_uacctd_group(char *, char *, char *); EXT int cfg_key_uacctd_nl_size(char *, char *, char *); EXT int cfg_key_tunnel_0(char *, char *, char *); EXT int cfg_key_xlate_src(char *, char *, char *); EXT int cfg_key_xlate_dst(char *, char *, char *); EXT void parse_time(char *, char *, int *, int *); #undef EXT pmacct-0.14.0/src/thread_pool.c0000644000175000017500000001216210534423663015314 0ustar paolopaolo/* Thread pool implementation for pmacct Copyright (C) 2006 Francois Deppierraz */ /* 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. */ #define __THREAD_POOL_C /* includes */ #include "pmacct.h" #include "thread_pool.h" #if THREAD_DEBUG int debug_pthread_mutex_lock(pthread_mutex_t *mutex) { printf("Locking mutex 0x%x\n", (unsigned int) mutex); fflush(stdout); return pthread_mutex_lock(mutex); } int debug_pthread_mutex_unlock(pthread_mutex_t *mutex) { printf("Unlocking mutex 0x%x\n", (unsigned int) mutex); fflush(stdout); return pthread_mutex_unlock(mutex); } #define pthread_mutex_lock debug_pthread_mutex_lock #define pthread_mutex_unlock debug_pthread_mutex_unlock #endif thread_pool_t *allocate_thread_pool(int count) { int i, rc; thread_pool_t *pool; thread_pool_item_t *worker; // Allocate pool pool = malloc(sizeof(thread_pool_t)); assert(pool); // Allocate pool mutex pool->mutex = malloc(sizeof (pthread_mutex_t)); assert(pool->mutex); pthread_mutex_init(pool->mutex, NULL); // Allocate pool condition pool->cond = malloc(sizeof (pthread_cond_t)); assert(pool->mutex); pthread_cond_init(pool->cond, NULL); pool->count = count; /* Threads lists */ pool->free_list = NULL; for (i = 0; i < pool->count; i++) { worker = malloc(sizeof(thread_pool_item_t)); assert(worker); worker->id = i; worker->owner = pool; worker->mutex = malloc(sizeof(pthread_mutex_t)); pthread_mutex_init(worker->mutex, NULL); worker->cond = malloc(sizeof(pthread_cond_t)); pthread_cond_init(worker->cond, NULL); /* Default state */ worker->go = -1; worker->quit = 0; worker->usage = 0; /* Create the thread */ worker->thread = malloc(sizeof(pthread_t)); rc = pthread_create(worker->thread, NULL, thread_runner, worker); if (rc) { printf("ERROR: thread creation failed: %s\n", strerror(rc)); } // Wait for thread init pthread_mutex_lock(worker->mutex); while (worker->go != 0) pthread_cond_wait(worker->cond, worker->mutex); pthread_mutex_unlock(worker->mutex); // Add to free list worker->next = pool->free_list; pool->free_list = worker; } return pool; } void desallocate_thread_pool(thread_pool_t *pool) { thread_pool_item_t *worker; while (worker) { /* Let him finish */ pthread_mutex_lock(worker->mutex); worker->quit = 1; pthread_mutex_unlock(worker->mutex); pthread_join(*worker->thread, NULL); /* Free memory */ pthread_mutex_destroy(worker->mutex); free(worker->mutex); pthread_cond_destroy(worker->cond); free(worker->cond); free(worker->thread); worker = worker->next; free(worker); } free(pool); } void *thread_runner(void *arg) { thread_pool_item_t *self = (thread_pool_item_t *) arg; #if DEBUG_TIMING struct mytimer t1; #endif pthread_mutex_lock(self->mutex); self->go = 0; pthread_cond_signal(self->cond); pthread_mutex_unlock(self->mutex); while (!self->quit) { /* Wait for some work */ pthread_mutex_lock(self->mutex); while (!self->go) pthread_cond_wait(self->cond, self->mutex); #if DEBUG fprintf(stderr, "[R] Thread 0x%x is working\n", self); #endif #if DEBUG_TIMING start_timer(&t1); #endif (*self->function)(self->data); #if DEBUG_TIMING stop_timer(&t1, "function:0x%x", self); #endif #if DEBUG fprintf(stderr, "[R] Thread 0x%x has finished\n", self); #endif self->usage++; self->go = 0; pthread_mutex_unlock(self->mutex); pthread_mutex_lock(self->owner->mutex); self->next = self->owner->free_list; self->owner->free_list = self; pthread_cond_signal(self->owner->cond); pthread_mutex_unlock(self->owner->mutex); } pthread_exit(NULL); } void send_to_pool(thread_pool_t *pool, void *function, struct packet_ptrs *data) { thread_pool_item_t *worker; #if DEBUG_TIMING struct mytimer t0; #endif #if DEBUG_TIMING start_timer(&t0); #endif pthread_mutex_lock(pool->mutex); while (pool->free_list == NULL) pthread_cond_wait(pool->cond, pool->mutex); /* Get a free thread */ worker = pool->free_list; pool->free_list = worker->next; pthread_mutex_unlock(pool->mutex); /* Give it some work to do */ pthread_mutex_lock(worker->mutex); worker->function = function; worker->data = data; worker->go = 1; pthread_cond_signal(worker->cond); pthread_mutex_unlock(worker->mutex); #if DEBUG_TIMING stop_timer(&t0, "send_to_pool:"); #endif } pmacct-0.14.0/src/mysql_plugin.h0000644000175000017500000000410711453022224015531 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente */ /* 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. */ /* includes */ #if defined CUT_MYSQLINCLUDES_DIR #include #else #include #endif /* prototypes */ void mysql_plugin(int, struct configuration *, void *); int MY_cache_dbop(struct DBdesc *, struct db_cache *, struct insert_data *); void MY_cache_purge(struct db_cache *[], int, struct insert_data *); int MY_evaluate_history(int); int MY_compose_static_queries(); void MY_Lock(struct DBdesc *); void MY_Unlock(struct BE_descs *); void MY_DB_Connect(struct DBdesc *, char *); void MY_DB_Close(struct BE_descs *); void MY_create_dyn_table(struct DBdesc *, char *); void MY_get_errmsg(struct DBdesc *); void MY_create_backend(struct DBdesc *); void MY_set_callbacks(struct sqlfunc_cb_registry *); void MY_init_default_values(struct insert_data *); /* variables */ static char mysql_user[] = "pmacct"; static char mysql_pwd[] = "arealsmartpwd"; static char mysql_db[] = "pmacct"; static char mysql_table[] = "acct"; static char mysql_table_v2[] = "acct_v2"; static char mysql_table_v3[] = "acct_v3"; static char mysql_table_v4[] = "acct_v4"; static char mysql_table_v5[] = "acct_v5"; static char mysql_table_v6[] = "acct_v6"; static char mysql_table_v7[] = "acct_v7"; static char mysql_table_v8[] = "acct_v8"; static char mysql_table_bgp[] = "acct_bgp"; pmacct-0.14.0/src/imt_plugin.c0000644000175000017500000003675711664553344015210 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #define __IMT_PLUGIN_C /* includes */ #include "pmacct.h" #include "plugin_hooks.h" #include "imt_plugin.h" #include "net_aggr.h" #include "ports_aggr.h" /* Functions */ void imt_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { int maxqsize = (MAX_QUERIES*sizeof(struct pkt_primitives))+sizeof(struct query_header)+2; struct sockaddr cAddr; struct pkt_data *data; struct ports_table pt; struct timezone tz; unsigned char srvbuf[maxqsize]; unsigned char *srvbufptr; struct query_header *qh; unsigned char *pipebuf; char path[] = "/tmp/collect.pipe"; short int go_to_clear = FALSE; u_int32_t request, sz; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; unsigned char *rgptr; int pollagain = 0; u_int32_t seq = 0; int rg_err_count = 0; struct pkt_bgp_primitives *pbgp; fd_set read_descs, bkp_read_descs; /* select() stuff */ int select_fd, lock = FALSE; int cLen, num, sd, sd2; char *dataptr; memcpy(&config, cfgptr, sizeof(struct configuration)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "IMT Plugin", config.name); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile); } reload_map = FALSE; status->wakeup = TRUE; /* a bunch of default definitions and post-checks */ pipebuf = (unsigned char *) malloc(config.buffer_size); setnonblocking(pipe_fd); memset(pipebuf, 0, config.buffer_size); no_more_space = FALSE; if (config.what_to_count & (COUNT_SUM_HOST|COUNT_SUM_NET)) insert_func = sum_host_insert; else if (config.what_to_count & COUNT_SUM_PORT) insert_func = sum_port_insert; else if (config.what_to_count & COUNT_SUM_AS) insert_func = sum_as_insert; #if defined (HAVE_L2) else if (config.what_to_count & COUNT_SUM_MAC) insert_func = sum_mac_insert; #endif else insert_func = insert_accounting_structure; /* Dirty but allows to save some IFs, centralizes checks and makes later comparison statements lean */ if (!(config.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_PEER_DST_IP| COUNT_SRC_AS_PATH|COUNT_SRC_STD_COMM|COUNT_SRC_EXT_COMM|COUNT_SRC_MED| COUNT_SRC_LOCAL_PREF|COUNT_MPLS_VPN_RD))) PbgpSz = 0; memset(&nt, 0, sizeof(nt)); memset(&nc, 0, sizeof(nc)); memset(&pt, 0, sizeof(pt)); load_networks(config.networks_file, &nt, &nc); set_net_funcs(&nt); if (config.ports_file) load_ports(config.ports_file, &pt); if ((!config.num_memory_pools) && (!have_num_memory_pools)) config.num_memory_pools = NUM_MEMORY_POOLS; if (!config.memory_pool_size) config.memory_pool_size = MEMORY_POOL_SIZE; else { if (config.memory_pool_size < sizeof(struct acc)) { Log(LOG_WARNING, "WARN ( %s/%s ): enforcing memory pool's minimum size, %d bytes.\n", config.name, config.type, sizeof(struct acc)); config.memory_pool_size = MEMORY_POOL_SIZE; } } if (!config.imt_plugin_path) config.imt_plugin_path = path; if (!config.buckets) config.buckets = MAX_HOSTS; init_memory_pool_table(config); if (mpd == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate memory pools table\n", config.name, config.type); exit_plugin(1); } current_pool = request_memory_pool(config.buckets*sizeof(struct acc)); if (current_pool == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate first memory pool, try with larger value.\n", config.name, config.type); exit_plugin(1); } a = current_pool->base_ptr; lru_elem_ptr = malloc(config.buckets*sizeof(struct acc *)); if (lru_elem_ptr == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate LRU element pointers.\n", config.name, config.type); exit_plugin(1); } else memset(lru_elem_ptr, 0, config.buckets*sizeof(struct acc *)); current_pool = request_memory_pool(config.memory_pool_size); if (current_pool == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate more memory pools, try with larger value.\n", config.name, config.type); exit_plugin(1); } signal(SIGHUP, reload); /* handles reopening of syslog channel */ signal(SIGINT, exit_now); /* exit lane */ signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, reload_maps); signal(SIGPIPE, SIG_IGN); #if !defined FBSD4 signal(SIGCHLD, SIG_IGN); #else signal(SIGCHLD, ignore_falling_child); #endif /* building a server for interrogations by clients */ sd = build_query_server(config.imt_plugin_path); cLen = sizeof(cAddr); /* preparing for synchronous I/O multiplexing */ select_fd = 0; FD_ZERO(&read_descs); FD_SET(sd, &read_descs); if (sd > select_fd) select_fd = sd; FD_SET(pipe_fd, &read_descs); if (pipe_fd > select_fd) select_fd = pipe_fd; select_fd++; memcpy(&bkp_read_descs, &read_descs, sizeof(read_descs)); qh = (struct query_header *) srvbuf; /* plugin main loop */ for(;;) { select_again: memcpy(&read_descs, &bkp_read_descs, sizeof(bkp_read_descs)); num = select(select_fd, &read_descs, NULL, NULL, NULL); if (num < 0) goto select_again; gettimeofday(&cycle_stamp, &tz); /* doing server tasks */ if (FD_ISSET(sd, &read_descs)) { struct pollfd pfd; int ret; sd2 = accept(sd, &cAddr, &cLen); setblocking(sd2); srvbufptr = srvbuf; sz = maxqsize; pfd.fd = sd2; pfd.events = POLLIN; recv_again: ret = poll(&pfd, 1, 1000); if (ret == 0) { Log(LOG_WARNING, "WARN ( %s/%s ): Timed out while processing fragmented query.\n", config.name, config.type); close(sd2); goto select_again; } else { num = recv(sd2, srvbufptr, sz, 0); if (srvbufptr[num-1] != '\x4') { srvbufptr += num; sz -= num; goto recv_again; /* fragmented query */ } } num = num+(maxqsize-sz); if (qh->num > MAX_QUERIES) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): request discarded. Too much queries.\n", config.name, config.type); close(sd2); continue; } request = qh->type; if (request & WANT_RESET) request ^= WANT_RESET; if (request & WANT_LOCK_OP) { lock = TRUE; request ^= WANT_LOCK_OP; } /* - if explicitely required, we do not fork: query obtains exclusive control - lock - over the memory table; - operations that may cause inconsistencies (full erasure, counter reset for individual entries, etc.) are entitled of an exclusive lock. - if query is matter of just a single short-lived walk through the table, we avoid fork(): the plugin will serve the request; - in all other cases, we fork; the newly created child will serve queries asyncronously. */ if (request & WANT_ERASE) { request ^= WANT_ERASE; if (request) { if (num > 0) process_query_data(sd2, srvbuf, num, FALSE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. Errno: %d\n", config.name, config.type, num, errno); } Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); go_to_clear = TRUE; } else if (((request == WANT_COUNTER) || (request == WANT_MATCH)) && (qh->num == 1) && (qh->what_to_count == config.what_to_count)) { if (num > 0) process_query_data(sd2, srvbuf, num, FALSE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. ERRNO: %d\n", config.name, config.type, num, errno); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); } else if (request == WANT_CLASS_TABLE) { if (num > 0) process_query_data(sd2, srvbuf, num, FALSE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. ERRNO: %d\n", config.name, config.type, num, errno); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); } else { if (lock) { if (num > 0) process_query_data(sd2, srvbuf, num, FALSE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. Errno: %d\n", config.name, config.type, num, errno); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); } else { switch (fork()) { case 0: /* Child */ close(sd); pm_setproctitle("%s [%s]", "IMT Plugin -- serving client", config.name); if (num > 0) process_query_data(sd2, srvbuf, num, TRUE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. Errno: %d\n", config.name, config.type, num, errno); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); close(sd2); exit(0); default: /* Parent */ break; } } } close(sd2); } /* clearing stats if requested */ if (go_to_clear) { /* When using extended BGP features we need to free() up memory allocations before erasing */ if (PbgpSz) free_bgp_allocs(); clear_memory_pool_table(); current_pool = request_memory_pool(config.buckets*sizeof(struct acc)); if (current_pool == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): Cannot allocate my first memory pool, try with larger value.\n", config.name, config.type); exit_plugin(1); } a = current_pool->base_ptr; current_pool = request_memory_pool(config.memory_pool_size); if (current_pool == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): Cannot allocate more memory pools, try with larger value.\n", config.name, config.type); exit_plugin(1); } go_to_clear = FALSE; no_more_space = FALSE; memcpy(&table_reset_stamp, &cycle_stamp, sizeof(struct timeval)); } if (FD_ISSET(pipe_fd, &read_descs)) { if (!pollagain) { seq++; seq %= MAX_SEQNUM; } pollagain = FALSE; if ((num = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ if (num < 0) { pollagain = TRUE; goto select_again; } memcpy(pipebuf, rgptr, config.buffer_size); if (((struct ch_buf_hdr *)pipebuf)->seq != seq) { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%d'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%d'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); seq = ((struct ch_buf_hdr *)pipebuf)->seq; } } if (num > 0) { data = (struct pkt_data *) (pipebuf+sizeof(struct ch_buf_hdr)); while (((struct ch_buf_hdr *)pipebuf)->num) { for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &data->primitives); if (config.ports_file) { if (!pt.table[data->primitives.src_port]) data->primitives.src_port = 0; if (!pt.table[data->primitives.dst_port]) data->primitives.dst_port = 0; } /* XXX: can be optimized? */ if (PbgpSz) pbgp = (struct pkt_bgp_primitives *) ((u_char *)data+PdataSz); else pbgp = NULL; (*insert_func)(data, pbgp); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) data; dataptr += PdataSz + PbgpSz; data = (struct pkt_data *) dataptr; } } } } if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } } } void exit_now(int signum) { exit_plugin(0); } void sum_host_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp) { struct in_addr ip; #if defined ENABLE_IPV6 struct in6_addr ip6; #endif if (data->primitives.dst_ip.family == AF_INET) { ip.s_addr = data->primitives.dst_ip.address.ipv4.s_addr; data->primitives.dst_ip.address.ipv4.s_addr = 0; data->primitives.dst_ip.family = 0; insert_accounting_structure(data, pbgp); data->primitives.src_ip.address.ipv4.s_addr = ip.s_addr; insert_accounting_structure(data, pbgp); return; } #if defined ENABLE_IPV6 if (data->primitives.dst_ip.family == AF_INET6) { memcpy(&ip6, &data->primitives.dst_ip.address.ipv6, sizeof(struct in6_addr)); memset(&data->primitives.dst_ip.address.ipv6, 0, sizeof(struct in6_addr)); data->primitives.dst_ip.family = 0; insert_accounting_structure(data, pbgp); memcpy(&data->primitives.src_ip.address.ipv6, &ip6, sizeof(struct in6_addr)); insert_accounting_structure(data, pbgp); return; } #endif } void sum_port_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp) { u_int16_t port; port = data->primitives.dst_port; data->primitives.dst_port = 0; insert_accounting_structure(data, pbgp); data->primitives.src_port = port; insert_accounting_structure(data, pbgp); } void sum_as_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp) { as_t asn; asn = data->primitives.dst_as; data->primitives.dst_as = 0; insert_accounting_structure(data, pbgp); data->primitives.src_as = asn; insert_accounting_structure(data, pbgp); } #if defined (HAVE_L2) void sum_mac_insert(struct pkt_data *data, struct pkt_bgp_primitives *pbgp) { u_char macaddr[ETH_ADDR_LEN]; memcpy(macaddr, &data->primitives.eth_dhost, ETH_ADDR_LEN); memset(data->primitives.eth_dhost, 0, ETH_ADDR_LEN); insert_accounting_structure(data, pbgp); memcpy(&data->primitives.eth_shost, macaddr, ETH_ADDR_LEN); insert_accounting_structure(data, pbgp); } #endif void free_bgp_allocs() { struct acc *acc_elem = NULL; unsigned char *elem; int following_chain = FALSE; unsigned int idx; elem = (unsigned char *) a; for (idx = 0; idx < config.buckets; idx++) { if (!following_chain) acc_elem = (struct acc *) elem; if (acc_elem->cbgp) { if (acc_elem->cbgp->std_comms) free(acc_elem->cbgp->std_comms); if (acc_elem->cbgp->ext_comms) free(acc_elem->cbgp->ext_comms); if (acc_elem->cbgp->as_path) free(acc_elem->cbgp->as_path); if (acc_elem->cbgp->src_std_comms) free(acc_elem->cbgp->src_std_comms); if (acc_elem->cbgp->src_ext_comms) free(acc_elem->cbgp->src_ext_comms); if (acc_elem->cbgp->src_as_path) free(acc_elem->cbgp->src_as_path); free(acc_elem->cbgp); } if (acc_elem->next) { acc_elem = acc_elem->next; following_chain = TRUE; idx--; } else { elem += sizeof(struct acc); following_chain = FALSE; } } } pmacct-0.14.0/src/sfacctd.h0000644000175000017500000002007111715256661014432 0ustar paolopaolo/* defines */ #define DEFAULT_SFACCTD_PORT 6343 #define SFLOW_MIN_MSG_SIZE 200 #define SFLOW_MAX_MSG_SIZE 65536 /* inflated ? */ #if (!defined NF9_FTYPE_IPV4) #define NF9_FTYPE_IPV4 0 #define NF9_FTYPE_IPV6 1 #define NF9_FTYPE_VLAN 5 #define NF9_FTYPE_VLAN_IPV4 5 #define NF9_FTYPE_VLAN_IPV6 6 #define NF9_FTYPE_MPLS 10 #define NF9_FTYPE_MPLS_IPV4 10 #define NF9_FTYPE_MPLS_IPV6 11 #define NF9_FTYPE_VLAN_MPLS 15 #define NF9_FTYPE_VLAN_MPLS_IPV4 15 #define NF9_FTYPE_VLAN_MPLS_IPV6 16 #endif enum INMPacket_information_type { INMPACKETTYPE_HEADER = 1, /* Packet headers are sampled */ INMPACKETTYPE_IPV4 = 2, /* IP version 4 data */ INMPACKETTYPE_IPV6 = 3 /* IP version 4 data */ }; enum INMExtended_information_type { INMEXTENDED_SWITCH = 1, /* Extended switch information */ INMEXTENDED_ROUTER = 2, /* Extended router information */ INMEXTENDED_GATEWAY = 3, /* Extended gateway router information */ INMEXTENDED_USER = 4, /* Extended TACAS/RADIUS user information */ INMEXTENDED_URL = 5 /* Extended URL information */ }; enum INMCounters_version { INMCOUNTERSVERSION_GENERIC = 1, INMCOUNTERSVERSION_ETHERNET = 2, INMCOUNTERSVERSION_TOKENRING = 3, INMCOUNTERSVERSION_FDDI = 4, INMCOUNTERSVERSION_VG = 5, INMCOUNTERSVERSION_WAN = 6, INMCOUNTERSVERSION_VLAN = 7 }; typedef struct _SFSample { struct in_addr sourceIP; SFLAddress agent_addr; u_int32_t agentSubId; /* the raw pdu */ u_char *rawSample; u_int32_t rawSampleLen; u_char *endp; u_int32_t *datap; u_int32_t datagramVersion; u_int32_t sampleType; u_int32_t ds_class; u_int32_t ds_index; /* sample stream info */ u_int32_t sysUpTime; /* XXX: suffers cleanup */ u_int32_t sequenceNo; /* XXX: suffers cleanup */ u_int32_t sampledPacketSize; u_int32_t samplesGenerated; u_int32_t meanSkipCount; u_int32_t samplePool; u_int32_t dropEvents; /* the sampled header */ u_int32_t packet_data_tag; u_int32_t headerProtocol; u_char *header; int headerLen; u_int32_t stripped; /* header decode */ int gotIPV4; int offsetToIPV4; int gotIPV6; int offsetToIPV6; struct in_addr dcd_srcIP; struct in_addr dcd_dstIP; u_int32_t dcd_ipProtocol; u_int32_t dcd_ipTos; u_int32_t dcd_ipTTL; u_int32_t dcd_sport; u_int32_t dcd_dport; u_int32_t dcd_tcpFlags; u_int32_t ip_fragmentOffset; u_int32_t udp_pduLen; /* ports */ u_int32_t inputPortFormat; u_int32_t outputPortFormat; u_int32_t inputPort; u_int32_t outputPort; /* ethernet */ u_int32_t eth_type; u_int32_t eth_len; u_char eth_src[8]; u_char eth_dst[8]; /* vlan */ u_int32_t in_vlan; u_int32_t in_priority; u_int32_t internalPriority; u_int32_t out_vlan; u_int32_t out_priority; /* MPLS hack */ SFLLabelStack lstk; /* extended data fields */ u_int32_t num_extended; u_int32_t extended_data_tag; #define SASAMPLE_EXTENDED_DATA_SWITCH 1 #define SASAMPLE_EXTENDED_DATA_ROUTER 4 #define SASAMPLE_EXTENDED_DATA_GATEWAY 8 #define SASAMPLE_EXTENDED_DATA_USER 16 #define SASAMPLE_EXTENDED_DATA_URL 32 #define SASAMPLE_EXTENDED_DATA_MPLS 64 #define SASAMPLE_EXTENDED_DATA_NAT 128 #define SASAMPLE_EXTENDED_DATA_MPLS_TUNNEL 256 #define SASAMPLE_EXTENDED_DATA_MPLS_VC 512 #define SASAMPLE_EXTENDED_DATA_MPLS_FTN 1024 #define SASAMPLE_EXTENDED_DATA_MPLS_LDP_FEC 2048 #define SASAMPLE_EXTENDED_DATA_VLAN_TUNNEL 4096 /* IP forwarding info */ SFLAddress nextHop; u_int32_t srcMask; u_int32_t dstMask; /* BGP info */ SFLAddress bgp_nextHop; u_int32_t my_as; u_int32_t src_as; u_int32_t src_peer_as; u_int32_t dst_as_path_len; char dst_as_path[MAX_BGP_ASPATH]; u_int32_t dst_peer_as; u_int32_t dst_as; u_int32_t communities_len; char comms[MAX_BGP_STD_COMMS]; u_int32_t localpref; /* user id */ #define SA_MAX_EXTENDED_USER_LEN 200 u_int32_t src_user_charset; u_int32_t src_user_len; char src_user[SA_MAX_EXTENDED_USER_LEN+1]; u_int32_t dst_user_charset; u_int32_t dst_user_len; char dst_user[SA_MAX_EXTENDED_USER_LEN+1]; /* url */ #define SA_MAX_EXTENDED_URL_LEN 200 #define SA_MAX_EXTENDED_HOST_LEN 200 u_int32_t url_direction; u_int32_t url_len; char url[SA_MAX_EXTENDED_URL_LEN+1]; u_int32_t host_len; char host[SA_MAX_EXTENDED_HOST_LEN+1]; /* mpls */ SFLAddress mpls_nextHop; /* nat */ SFLAddress nat_src; SFLAddress nat_dst; /* counter blocks */ u_int32_t statsSamplingInterval; u_int32_t counterBlockVersion; /* classification */ pm_class_t class; pm_id_t tag; pm_id_t tag2; SFLAddress ipsrc; SFLAddress ipdst; } SFSample; /* define my own IP header struct - to ease portability */ struct SF_iphdr { u_int8_t version_and_headerLen; u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; }; /* same for tcp */ struct SF_tcphdr { u_int16_t th_sport; u_int16_t th_dport; u_int32_t th_seq; u_int32_t th_ack; u_int8_t th_off_and_unused; u_int8_t th_flags; u_int16_t th_win; u_int16_t th_sum; u_int16_t th_urp; }; /* and UDP */ struct SF_udphdr { u_int16_t uh_sport; u_int16_t uh_dport; u_int16_t uh_ulen; u_int16_t uh_sum; }; /* and ICMP */ struct SF_icmphdr { u_int8_t type; u_int8_t code; /* ignore the rest */ }; #if (!defined __SFACCTD_C) #define EXT extern #else #define EXT #endif EXT u_int16_t SF_evaluate_flow_type(struct packet_ptrs *); EXT void set_vector_sample_type(struct packet_ptrs_vector *, u_int32_t); EXT void reset_mac(struct packet_ptrs *); EXT void reset_mac_vlan(struct packet_ptrs *); EXT void reset_ip4(struct packet_ptrs *); EXT void reset_ip6(struct packet_ptrs *); EXT void notify_malf_packet(short int, char *, struct sockaddr *); EXT void SF_find_id(struct id_table *, struct packet_ptrs *, pm_id_t *, pm_id_t *); EXT u_int32_t getData32(SFSample *); EXT u_int32_t getData32_nobswap(SFSample *); EXT u_int32_t getAddress(SFSample *, SFLAddress *); EXT void skipBytes(SFSample *, int); EXT int lengthCheck(SFSample *, u_char *, int); EXT void process_SFv2v4_packet(SFSample *, struct packet_ptrs_vector *, struct plugin_requests *, struct sockaddr *); EXT void process_SFv5_packet(SFSample *, struct packet_ptrs_vector *, struct plugin_requests *, struct sockaddr *); EXT void process_SF_raw_packet(SFSample *, struct packet_ptrs_vector *, struct plugin_requests *, struct sockaddr *); EXT void readv2v4FlowSample(SFSample *, struct packet_ptrs_vector *, struct plugin_requests *); EXT void readv5FlowSample(SFSample *, int, struct packet_ptrs_vector *, struct plugin_requests *); EXT void readv2v4CountersSample(SFSample *); EXT void readv5CountersSample(SFSample *); EXT void finalizeSample(SFSample *, struct packet_ptrs_vector *, struct plugin_requests *); EXT void InterSampleCleanup(SFSample *); EXT void decodeMpls(SFSample *); EXT void decodePPP(SFSample *); EXT void decodeLinkLayer(SFSample *); EXT void decodeIPLayer4(SFSample *, u_char *, u_int32_t); EXT void decodeIPV4(SFSample *); EXT void decodeIPV6(SFSample *); EXT void readExtendedSwitch(SFSample *); EXT void readExtendedRouter(SFSample *); EXT void readExtendedGateway_v2(SFSample *); EXT void readExtendedGateway(SFSample *); EXT void readExtendedUser(SFSample *); EXT void readExtendedUrl(SFSample *); EXT void mplsLabelStack(SFSample *, char *); EXT void readExtendedMpls(SFSample *); EXT void readExtendedNat(SFSample *); EXT void readExtendedMplsTunnel(SFSample *); EXT void readExtendedMplsVC(SFSample *); EXT void readExtendedMplsFTN(SFSample *); EXT void readExtendedMplsLDP_FEC(SFSample *); EXT void readExtendedVlanTunnel(SFSample *); EXT void readExtendedProcess(SFSample *); EXT void readFlowSample_header(SFSample *); EXT void readFlowSample_ethernet(SFSample *); EXT void readFlowSample_IPv4(SFSample *); EXT void readFlowSample_IPv6(SFSample *); EXT char *sfv245_check_status(SFSample *spp, struct sockaddr *); EXT void usage_daemon(char *); EXT void compute_once(); #undef EXT pmacct-0.14.0/src/net_aggr.c0000644000175000017500000011537311672203713014606 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #define __NET_AGGR_C /* includes */ #include "pmacct.h" #include "nfacctd.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "net_aggr.h" #include "addr.h" #include "jhash.h" void load_networks(char *filename, struct networks_table *nt, struct networks_cache *nc) { load_networks4(filename, nt, nc); #if defined ENABLE_IPV6 load_networks6(filename, nt, nc); #endif } void load_networks4(char *filename, struct networks_table *nt, struct networks_cache *nc) { FILE *file; struct networks_table tmp, *tmpt = &tmp; struct networks_table bkt; struct networks_table_metadata *mdt = NULL; char buf[SRVBUFLEN], *bufptr, *delim, *as, *net, *mask; int rows, eff_rows = 0, j, buflen; unsigned int index; struct stat st; memset(&dummy_entry, 0, sizeof(struct networks_table_entry)); memset(&bkt, 0, sizeof(bkt)); memset(&tmp, 0, sizeof(tmp)); memset(&st, 0, sizeof(st)); default_route_in_networks4_table = FALSE; /* backing up pre-existing table and cache */ if (nt->num) { bkt.table = nt->table; bkt.num = nt->num; bkt.timestamp = nt->timestamp; nt->table = NULL; nt->num = 0; nt->timestamp = 0; } if (filename) { if ((file = fopen(filename,"r")) == NULL) { if (((config.acct_type == ACCT_NF || config.acct_type == ACCT_SF) && (config.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS)) && (!(config.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET))) && (config.nfacctd_as & NF_AS_KEEP || config.nfacctd_as & NF_AS_BGP)) || (config.acct_type == ACCT_PM && (config.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS)) && (!(config.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET))) && config.nfacctd_as & NF_AS_BGP)) return; Log(LOG_ERR, "ERROR: network file '%s' not found\n", filename); goto handle_error; } else { rows = 0; /* 1st step: count rows for table allocation */ while (!feof(file)) { if (fgets(buf, SRVBUFLEN, file) && !iscomment(buf)) rows++; } /* 2nd step: loading data into a temporary table */ if (!freopen(filename, "r", file)) { Log(LOG_ERR, "ERROR: freopen() failed: %s\n", strerror(errno)); goto handle_error; } /* We have no (valid) rows. We build a zeroed single-row table aimed to complete successfully any further lookup */ if (!rows) rows++; nt->table = malloc(rows*sizeof(struct networks_table_entry)); if (!nt->table) { Log(LOG_ERR, "ERROR: malloc() failed while building Networks Table.\n"); goto handle_error; } tmpt->table = malloc(rows*sizeof(struct networks_table_entry)); if (!tmpt->table) { Log(LOG_ERR, "ERROR: malloc() failed while building Temporary Networks Table.\n"); goto handle_error; } memset(nt->table, 0, rows*sizeof(struct networks_table_entry)); memset(tmpt->table, 0, rows*sizeof(struct networks_table_entry)); rows = 1; while (!feof(file)) { bufptr = buf; memset(buf, 0, SRVBUFLEN); if (fgets(buf, SRVBUFLEN, file)) { if (iscomment(buf)) continue; if (delim = strchr(buf, ',')) { char *endptr; as = buf; *delim = '\0'; bufptr = delim+1; tmpt->table[eff_rows].as = strtoul(as, &endptr, 10); } else tmpt->table[eff_rows].as = 0; if (!sanitize_buf_net(filename, bufptr, rows)) { delim = strchr(bufptr, '/'); *delim = '\0'; net = bufptr; mask = delim+1; if (!inet_aton(net, (struct in_addr *) &tmpt->table[eff_rows].net)) goto cycle_end; buflen = strlen(mask); for (j = 0; j < buflen; j++) { if (!isdigit(mask[j])) { Log(LOG_ERR, "ERROR ( %s ): Invalid network mask '%s' at line: %u.\n", filename, mask, rows); goto cycle_end; } } index = atoi(mask); if (index > 32) { Log(LOG_ERR, "ERROR ( %s ): Invalid network mask '%d' at line: %u.\n", filename, index, rows); goto cycle_end; } tmpt->table[eff_rows].net = ntohl(tmpt->table[eff_rows].net); tmpt->table[eff_rows].mask = (index == 32) ? 0xffffffffUL : ~(0xffffffffUL >> index); tmpt->table[eff_rows].masknum = index; tmpt->table[eff_rows].net &= tmpt->table[eff_rows].mask; /* enforcing mask on given network */ eff_rows++; } } cycle_end: rows++; } fclose(file); stat(filename, &st); /* We have no (valid) rows. We build a zeroed single-row table aimed to complete successfully any further lookup */ if (!eff_rows) eff_rows++; /* 3rd step: sorting table */ merge_sort(filename, tmpt->table, 0, eff_rows); tmpt->num = eff_rows; /* 4th step: collecting informations in the sorted table; we wish to handle networks-in-networks hierarchically */ /* 4a building hierarchies */ mdt = malloc(tmpt->num*sizeof(struct networks_table_metadata)); if (!mdt) { Log(LOG_ERR, "ERROR: malloc() failed while building Metadata Networks Table.\n"); goto handle_error; } memset(mdt, 0, tmpt->num*sizeof(struct networks_table_metadata)); if (tmpt->num) { for (index = 0; index < (tmpt->num-1); index++) { u_int32_t net; int x; for (x = index+1; x < tmpt->num; x++) { net = tmpt->table[x].net; net &= tmpt->table[index].mask; if (net == tmpt->table[index].net) { mdt[x].level++; mdt[index].childs++; } else break; } } } /* 4b retrieving root entries number */ for (index = 0, eff_rows = 0; index < tmpt->num; index++) { if (mdt[index].level == 0) eff_rows++; } nt->num = eff_rows; /* 4c adjusting child counters: each parent has to know only the number of its directly attached childs and not the whole hierarchy */ for (index = 0; index < tmpt->num; index++) { int x, eff_childs = 0; for (x = index+1; x < tmpt->num; x++) { if (mdt[index].level == mdt[x].level) break; else if (mdt[index].level == (mdt[x].level-1)) eff_childs++; } mdt[index].childs = eff_childs; } /* 5a step: building final networks table */ for (index = 0; index < tmpt->num; index++) { int current, next, prev[128]; if (!index) { current = 0; next = eff_rows; memset(&prev, 0, 32); memcpy(&nt->table[current], &tmpt->table[index], sizeof(struct networks_table_entry)); } else { if (mdt[index].level == mdt[index-1].level) current++; /* do nothing: we have only to copy our element */ else if (mdt[index].level > mdt[index-1].level) { /* we encountered a child */ nt->table[current].childs_table.table = &nt->table[next]; nt->table[current].childs_table.num = mdt[index-1].childs; prev[mdt[index-1].level] = current; current = next; next += mdt[index-1].childs; } else { /* going back to parent level */ current = prev[mdt[index].level]; current++; } memcpy(&nt->table[current], &tmpt->table[index], sizeof(struct networks_table_entry)); } } /* 5b step: debug and default route detection */ index = 0; while (index < tmpt->num) { if (config.debug) Log(LOG_DEBUG, "DEBUG ( %s ): (networks table IPv4) AS: %x, net: %x, mask (bit): %x, mask (num): %x\n", filename, nt->table[index].as, nt->table[index].net, nt->table[index].mask, nt->table[index].masknum); if (!nt->table[index].mask) default_route_in_networks4_table = TRUE; index++; } /* 6th step: create networks cache BUT only for the first time */ if (!nc->cache) { if (!config.networks_cache_entries) nc->num = NETWORKS_CACHE_ENTRIES; else nc->num = config.networks_cache_entries; nc->cache = (struct networks_cache_entry *) malloc(nc->num*sizeof(struct networks_cache_entry)); if (!nc->cache) { Log(LOG_ERR, "ERROR: malloc() failed while building Networks Cache.\n"); goto handle_error; } else Log(LOG_DEBUG, "DEBUG ( %s ): IPv4 Networks Cache successfully created: %u entries.\n", filename, nc->num); } /* 7th step: freeing resources */ memset(nc->cache, 0, nc->num*sizeof(struct networks_cache_entry)); free(tmpt->table); free(mdt); if (bkt.table) free(bkt.table); /* 8th step: setting timestamp */ nt->timestamp = st.st_mtime; } } return; /* error handling: if we have a copy of the old table we will rollback it; otherwise we just take the exit lane. XXX: actually we are just able to recover malloc() troubles and missing files; efforts should be pushed in the validation of the new table. */ handle_error: if (tmpt->table) free(tmpt->table); if (mdt) free(mdt); if (bkt.num) { if (!nt->table) { memcpy(nt, &bkt, sizeof(nt)); Log(LOG_WARNING, "WARN: Rolling back the old Networks Table.\n"); /* we update the timestamp to avoid loops */ stat(filename, &st); nt->timestamp = st.st_mtime; } } else exit_plugin(1); } /* sort the (sub)array v from start to end */ void merge_sort(char *filename, struct networks_table_entry *table, int start, int end) { int middle; /* no elements to sort */ if ((start == end) || (start == end-1)) return; /* find the middle of the array, splitting it into two subarrays */ middle = (start+end)/2; /* sort the subarray from start..middle */ merge_sort(filename, table, start, middle); /* sort the subarray from middle..end */ merge_sort(filename, table, middle, end); /* merge the two sorted halves */ merge(filename, table, start, middle, end); } /* merge the subarray v[start..middle] with v[middle..end], placing the result back into v. */ void merge(char *filename, struct networks_table_entry *table, int start, int middle, int end) { struct networks_table_entry *v1, *v2; int v1_n, v2_n, v1_index, v2_index, i, s = sizeof(struct networks_table_entry); v1_n = middle-start; v2_n = end-middle; v1 = malloc(v1_n*s); v2 = malloc(v2_n*s); if ((!v1) || (!v2)) Log(LOG_ERR, "ERROR ( %s ): Memory sold out when allocating 'networks table'\n", filename); for (i=0; inum-1; u_int32_t net, addrh = ntohl(a->address.ipv4.s_addr), addr = a->address.ipv4.s_addr; struct networks_table_entry *ret; ret = networks_cache_search(nc, &addr); if (ret) return ret; while (low <= high) { mid = (low+high)/2; net = addrh; net &= nt->table[mid].mask; if (net < nt->table[mid].net) { high = mid-1; continue; } else if (net > nt->table[mid].net) { low = mid+1; continue; } /* It's assumed we've found our element */ if (nt->table[mid].childs_table.table) { ret = binsearch(&nt->table[mid].childs_table, nc, a); if (!ret) { ret = &nt->table[mid]; networks_cache_insert(nc, &addr, &nt->table[mid]); } } else { ret = &nt->table[mid]; networks_cache_insert(nc, &addr, &nt->table[mid]); } return ret; } networks_cache_insert(nc, &addr, &dummy_entry); return NULL; } void networks_cache_insert(struct networks_cache *nc, u_int32_t *key, struct networks_table_entry *result) { struct networks_cache_entry *ptr; ptr = &nc->cache[*key % nc->num]; ptr->key = *key; ptr->result = result; } struct networks_table_entry *networks_cache_search(struct networks_cache *nc, u_int32_t *key) { struct networks_cache_entry *ptr; ptr = &nc->cache[*key % nc->num]; if (ptr->key == *key) return ptr->result; else return NULL; } void set_net_funcs(struct networks_table *nt) { u_int8_t count = 0; memset(&net_funcs, 0, sizeof(net_funcs)); if ((config.nfacctd_net & NF_NET_STATIC) && config.networks_mask) { int j, index = config.networks_mask; memset(nt->maskbits, 0, sizeof(nt->maskbits)); for (j = 0; j < 4 && index >= 32; j++, index -= 32) nt->maskbits[j] = 0xffffffffU; if (j < 4 && index) nt->maskbits[j] = ~(0xffffffffU >> index); if (config.what_to_count & (COUNT_SRC_NET|COUNT_SUM_NET)) { net_funcs[count] = mask_src_ipaddr; count++; } if (config.what_to_count & COUNT_DST_NET) { net_funcs[count] = mask_dst_ipaddr; count++; } if (config.what_to_count & COUNT_SRC_NMASK) { net_funcs[count] = copy_src_mask; count++; } if (config.what_to_count & COUNT_DST_NMASK) { net_funcs[count] = copy_dst_mask; count++; } } #if defined ENABLE_IPV6 if ((!nt->num) && (!nt->num6)) return; #else if (!nt->num) return; #endif if (config.what_to_count & (COUNT_SRC_HOST|COUNT_SUM_HOST)) { net_funcs[count] = search_src_host; count++; } if (config.what_to_count & COUNT_SRC_NMASK) { net_funcs[count] = search_src_nmask; count++; } if ((config.nfacctd_net & NF_NET_NEW) && config.what_to_count & (COUNT_SRC_NET|COUNT_SUM_NET)) { net_funcs[count] = search_src_net; count++; } if (config.what_to_count & (COUNT_SRC_AS|COUNT_SUM_AS)) { if (((config.acct_type == ACCT_NF || config.acct_type == ACCT_SF) && (config.nfacctd_as & NF_AS_KEEP || config.nfacctd_as & NF_AS_BGP)) || (config.acct_type == ACCT_PM && config.nfacctd_as & NF_AS_BGP)); else { net_funcs[count] = search_src_as; count++; if (!(config.what_to_count & (COUNT_SRC_HOST|COUNT_SUM_HOST|COUNT_SRC_NET|COUNT_SUM_NET))) { net_funcs[count] = drop_src_host; count++; } } } if (config.what_to_count & (COUNT_DST_HOST|COUNT_SUM_HOST)) { net_funcs[count] = search_dst_host; count++; } if (config.what_to_count & COUNT_DST_NMASK) { net_funcs[count] = search_dst_nmask; count++; } if ((config.nfacctd_net & NF_NET_NEW) && config.what_to_count & (COUNT_DST_NET|COUNT_SUM_NET)) { net_funcs[count] = search_dst_net; count++; } if (config.what_to_count & (COUNT_DST_AS|COUNT_SUM_AS)) { if (((config.acct_type == ACCT_NF || config.acct_type == ACCT_SF) && (config.nfacctd_as & NF_AS_KEEP || config.nfacctd_as & NF_AS_BGP)) || (config.acct_type == ACCT_PM && config.nfacctd_as & NF_AS_BGP)); else { net_funcs[count] = search_dst_as; count++; if (!(config.what_to_count & (COUNT_DST_HOST|COUNT_DST_NET))) { net_funcs[count] = drop_dst_host; count++; } } } } void mask_src_ipaddr(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { u_int32_t addrh[4]; u_int8_t j; if (p->src_ip.family == AF_INET) { addrh[0] = ntohl(p->src_ip.address.ipv4.s_addr); addrh[0] &= nt->maskbits[0]; p->src_ip.address.ipv4.s_addr = htonl(addrh[0]); } #if defined ENABLE_IPV6 else if (p->src_ip.family == AF_INET6) { memcpy(&addrh, (void *) pm_ntohl6(&p->src_ip.address.ipv6), IP6AddrSz); for (j = 0; j < 4; j++) addrh[j] &= nt->maskbits[j]; memcpy(&p->src_ip.address.ipv6, (void *) pm_htonl6(addrh), IP6AddrSz); } #endif } void mask_dst_ipaddr(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { u_int32_t addrh[4]; u_int8_t j; if (p->dst_ip.family == AF_INET) { addrh[0] = ntohl(p->dst_ip.address.ipv4.s_addr); addrh[0] &= nt->maskbits[0]; p->dst_ip.address.ipv4.s_addr = htonl(addrh[0]); } #if defined ENABLE_IPV6 else if (p->dst_ip.family == AF_INET6) { memcpy(&addrh, (void *) pm_ntohl6(&p->dst_ip.address.ipv6), IP6AddrSz); for (j = 0; j < 4; j++) addrh[j] &= nt->maskbits[j]; memcpy(&p->dst_ip.address.ipv6, (void *) pm_htonl6(addrh), IP6AddrSz); } #endif } void copy_src_mask(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { p->src_nmask = config.networks_mask; } void copy_dst_mask(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { p->dst_nmask = config.networks_mask; } void search_src_host(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { struct networks_table_entry *res; #if defined ENABLE_IPV6 struct networks6_table_entry *res6; #endif if (p->src_ip.family == AF_INET) { res = binsearch(nt, nc, &p->src_ip); if (!res) p->src_ip.address.ipv4.s_addr = 0; else if (!res->net && !default_route_in_networks4_table) p->src_ip.address.ipv4.s_addr = 0; /* it may have been cached */ } #if defined ENABLE_IPV6 else if (p->src_ip.family == AF_INET6) { res6 = binsearch6(nt, nc, &p->src_ip); if (!res6) memset(&p->src_ip.address.ipv6, 0, IP6AddrSz); else if (!res6->net[0] && !default_route_in_networks6_table) memset(&p->src_ip.address.ipv6, 0, IP6AddrSz); /* it may have been cached */ } #endif } void search_dst_host(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { struct networks_table_entry *res; #if defined ENABLE_IPV6 struct networks6_table_entry *res6; #endif if (p->dst_ip.family == AF_INET) { res = binsearch(nt, nc, &p->dst_ip); if (!res) p->dst_ip.address.ipv4.s_addr = 0; else if (!res->net && !default_route_in_networks4_table) p->dst_ip.address.ipv4.s_addr = 0; /* it may have been cached */ } #if defined ENABLE_IPV6 else if (p->dst_ip.family == AF_INET6) { res6 = binsearch6(nt, nc, &p->dst_ip); if (!res6) memset(&p->dst_ip.address.ipv6, 0, IP6AddrSz); else if (!res6->net[0] && !default_route_in_networks6_table) memset(&p->dst_ip.address.ipv6, 0, IP6AddrSz); /* it may have been cached */ } #endif } void search_src_net(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { struct networks_table_entry *res; #if defined ENABLE_IPV6 struct networks6_table_entry *res6; #endif if (p->src_ip.family == AF_INET) { res = binsearch(nt, nc, &p->src_ip); if (!res) p->src_ip.address.ipv4.s_addr = 0; else p->src_ip.address.ipv4.s_addr = htonl(res->net); } #if defined ENABLE_IPV6 else if (p->src_ip.family == AF_INET6) { res6 = binsearch6(nt, nc, &p->src_ip); if (!res6) memset(&p->src_ip.address.ipv6, 0, IP6AddrSz); else memcpy(&p->src_ip.address.ipv6, (void *)pm_htonl6(res6->net), IP6AddrSz); } #endif } void search_dst_net(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { struct networks_table_entry *res; #if defined ENABLE_IPV6 struct networks6_table_entry *res6; #endif if (p->dst_ip.family == AF_INET) { res = binsearch(nt, nc, &p->dst_ip); if (!res) p->dst_ip.address.ipv4.s_addr = 0; else p->dst_ip.address.ipv4.s_addr = htonl(res->net); } #if defined ENABLE_IPV6 else if (p->dst_ip.family == AF_INET6) { res6 = binsearch6(nt, nc, &p->dst_ip); if (!res6) memset(&p->dst_ip.address.ipv6, 0, IP6AddrSz); else memcpy(&p->dst_ip.address.ipv6, (void *)pm_htonl6(res6->net), IP6AddrSz); } #endif } void search_src_nmask(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { struct networks_table_entry *res; #if defined ENABLE_IPV6 struct networks6_table_entry *res6; #endif if (p->src_ip.family == AF_INET) { res = binsearch(nt, nc, &p->src_ip); if (!res) p->src_nmask = 0; else p->src_nmask = res->masknum; } #if defined ENABLE_IPV6 else if (p->src_ip.family == AF_INET6) { res6 = binsearch6(nt, nc, &p->src_ip); if (!res6) p->src_nmask = 0; else p->src_nmask = res->masknum; } #endif } void search_dst_nmask(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { struct networks_table_entry *res; #if defined ENABLE_IPV6 struct networks6_table_entry *res6; #endif if (p->dst_ip.family == AF_INET) { res = binsearch(nt, nc, &p->dst_ip); if (!res) p->dst_nmask = 0; else p->dst_nmask = res->masknum; } #if defined ENABLE_IPV6 else if (p->dst_ip.family == AF_INET6) { res6 = binsearch6(nt, nc, &p->dst_ip); if (!res6) p->dst_nmask = 0; else p->dst_nmask = res->masknum; } #endif } void search_src_as(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { struct networks_table_entry *res; #if defined ENABLE_IPV6 struct networks6_table_entry *res6; #endif if (p->src_ip.family == AF_INET) { res = binsearch(nt, nc, &p->src_ip); if (res) p->src_as = res->as; } #if defined ENABLE_IPV6 else if (p->src_ip.family == AF_INET6) { res6 = binsearch6(nt, nc, &p->src_ip); if (res6) p->src_as = res6->as; } #endif } void search_dst_as(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { struct networks_table_entry *res; #if defined ENABLE_IPV6 struct networks6_table_entry *res6; #endif if (p->dst_ip.family == AF_INET) { res = binsearch(nt, nc, &p->dst_ip); if (res) p->dst_as = res->as; } #if defined ENABLE_IPV6 else if (p->dst_ip.family == AF_INET6) { res6 = binsearch6(nt, nc, &p->dst_ip); if (res6) p->dst_as = res6->as; } #endif } void drop_src_host(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { memset(&p->src_ip, 0, HostAddrSz); } void drop_dst_host(struct networks_table *nt, struct networks_cache *nc, struct pkt_primitives *p) { memset(&p->dst_ip, 0, HostAddrSz); } as_t search_pretag_src_as(struct networks_table *nt, struct networks_cache *nc, struct packet_ptrs *pptrs) { struct networks_table_entry *res; struct host_addr addr; #if defined ENABLE_IPV6 struct networks6_table_entry *res6; #endif if (pptrs->l3_proto == ETHERTYPE_IP) { addr.family = AF_INET; addr.address.ipv4.s_addr = ((struct my_iphdr *) pptrs->iph_ptr)->ip_src.s_addr; res = binsearch(nt, nc, &addr); if (!res) return 0; else return res->as; } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { addr.family = AF_INET6; memcpy(&addr.address.ipv6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_src, IP6AddrSz); res6 = binsearch6(nt, nc, &addr); if (!res6) return 0; else return res6->as; } #endif } as_t search_pretag_dst_as(struct networks_table *nt, struct networks_cache *nc, struct packet_ptrs *pptrs) { struct networks_table_entry *res; struct host_addr addr; #if defined ENABLE_IPV6 struct networks6_table_entry *res6; #endif if (pptrs->l3_proto == ETHERTYPE_IP) { addr.family = AF_INET; addr.address.ipv4.s_addr = ((struct my_iphdr *) pptrs->iph_ptr)->ip_dst.s_addr; res = binsearch(nt, nc, &addr); if (!res) return 0; else return res->as; } #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) { addr.family = AF_INET6; memcpy(&addr.address.ipv6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_dst, IP6AddrSz); res6 = binsearch6(nt, nc, &addr); if (!res6) return 0; else return res6->as; } #endif } #if defined ENABLE_IPV6 void load_networks6(char *filename, struct networks_table *nt, struct networks_cache *nc) { FILE *file; struct networks_table tmp, *tmpt = &tmp; struct networks_table bkt; struct networks_table_metadata *mdt = 0; char buf[SRVBUFLEN], *bufptr, *delim, *as, *net, *mask; int rows, eff_rows = 0, j, buflen; unsigned int index; u_int32_t tmpmask[4], tmpnet[4]; struct stat st; memset(&dummy_entry6, 0, sizeof(struct networks6_table_entry)); memset(&bkt, 0, sizeof(bkt)); memset(&tmp, 0, sizeof(tmp)); memset(&st, 0, sizeof(st)); default_route_in_networks6_table = FALSE; /* backing up pre-existing table and cache */ if (nt->num6) { bkt.table6 = nt->table6; bkt.num6 = nt->num6; bkt.timestamp = nt->timestamp; nt->table6 = 0; nt->num6 = 0; nt->timestamp = 0; } if (filename) { if ((file = fopen(filename,"r")) == NULL) { if (((config.acct_type == ACCT_NF) && (config.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS)) && (!(config.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET))) && (config.nfacctd_as & NF_AS_KEEP || config.nfacctd_as & NF_AS_BGP)) || (config.acct_type == ACCT_PM && (config.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS)) && (!(config.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET))) && config.nfacctd_as & NF_AS_BGP)) return; Log(LOG_ERR, "ERROR: network file '%s' not found\n", filename); exit_plugin(1); } else { rows = 0; /* 1st step: count rows for table allocation */ while (!feof(file)) { if (fgets(buf, SRVBUFLEN, file)) rows++; } /* 2nd step: loading data into a temporary table */ if (!freopen(filename, "r", file)) { Log(LOG_ERR, "ERROR: freopen() failed: %s\n", strerror(errno)); goto handle_error; } /* We have no (valid) rows. We build a zeroed single-row table aimed to complete successfully any further lookup */ if (!rows) rows++; nt->table6 = malloc(rows*sizeof(struct networks6_table_entry)); if (!nt->table6) { Log(LOG_ERR, "ERROR: malloc() failed while building Networks Table.\n"); goto handle_error; } tmpt->table6 = malloc(rows*sizeof(struct networks6_table_entry)); if (!tmpt->table6) { Log(LOG_ERR, "ERROR: malloc() failed while building Temporary Networks Table.\n"); goto handle_error; } memset(nt->table6, 0, rows*sizeof(struct networks6_table_entry)); memset(tmpt->table6, 0, rows*sizeof(struct networks6_table_entry)); rows = 1; while (!feof(file)) { bufptr = buf; memset(buf, 0, SRVBUFLEN); if (fgets(buf, SRVBUFLEN, file)) { if (delim = strchr(buf, ',')) { char *endptr; as = buf; *delim = '\0'; bufptr = delim+1; tmpt->table6[eff_rows].as = strtoul(as, &endptr, 10); } else tmpt->table6[eff_rows].as = 0; if (!sanitize_buf_net(filename, bufptr, rows)) { delim = strchr(bufptr, '/'); *delim = '\0'; net = bufptr; mask = delim+1; /* XXX: error signallation */ if (!inet_pton(AF_INET6, net, &tmpnet)) goto cycle_end; buflen = strlen(mask); for (j = 0; j < buflen; j++) { if (!isdigit(mask[j])) { Log(LOG_ERR, "ERROR ( %s ): Invalid network mask '%s' at line: %u.\n", filename, mask, rows); goto cycle_end; } } index = atoi(mask); if (index > 128) { Log(LOG_ERR, "ERROR ( %s ): Invalid network mask '%d' at line: %u.\n", filename, index, rows); goto cycle_end; } memset(&tmpmask, 0, sizeof(tmpmask)); for (j = 0; j < 4 && index >= 32; j++, index -= 32) tmpmask[j] = 0xffffffffU; if (j < 4 && index) tmpmask[j] = ~(0xffffffffU >> index); for (j = 0; j < 4; j++) tmpnet[j] = ntohl(tmpnet[j]); for (j = 0; j < 4; j++) tmpnet[j] &= tmpmask[j]; /* enforcing mask on given network */ memcpy(&tmpt->table6[eff_rows].net, tmpnet, IP6AddrSz); memcpy(&tmpt->table6[eff_rows].mask, tmpmask, IP6AddrSz); tmpt->table6[eff_rows].masknum = index; eff_rows++; } } cycle_end: rows++; } fclose(file); stat(filename, &st); /* We have no (valid) rows. We build a zeroed single-row table aimed to complete successfully any further lookup */ if (!eff_rows) eff_rows++; /* 3rd step: sorting table */ merge_sort6(filename, tmpt->table6, 0, eff_rows); tmpt->num6 = eff_rows; /* 4th step: collecting informations in the sorted table; we wish to handle networks-in-networks hierarchically */ /* 4a building hierarchies */ mdt = malloc(tmpt->num6*sizeof(struct networks_table_metadata)); if (!mdt) { Log(LOG_ERR, "ERROR: malloc() failed while building Metadata Networks Table.\n"); goto handle_error; } memset(mdt, 0, tmpt->num6*sizeof(struct networks_table_metadata)); if (tmpt->num6) { for (index = 0; index < (tmpt->num6-1); index++) { u_int32_t net[4]; int x, chunk; for (x = index+1; x < tmpt->num6; x++) { memcpy(&net, &tmpt->table6[x].net, IP6AddrSz); for (chunk = 0; chunk < 4; chunk++) net[chunk] &= tmpt->table6[index].mask[chunk]; for (chunk = 0; chunk < 4; chunk++) { if (net[chunk] == tmpt->table6[index].net[chunk]) { if (chunk == 3) { mdt[x].level++; mdt[index].childs++; } } else break; } } } } /* 4b retrieving root entries number */ for (index = 0, eff_rows = 0; index < tmpt->num6; index++) { if (mdt[index].level == 0) eff_rows++; } nt->num6 = eff_rows; /* 4c adjusting child counters: each parent has to know only the number of its directly attached childs and not the whole hierarchy */ for (index = 0; index < tmpt->num6; index++) { int x, eff_childs = 0; for (x = index+1; x < tmpt->num6; x++) { if (mdt[index].level == mdt[x].level) break; else if (mdt[index].level == (mdt[x].level-1)) eff_childs++; } mdt[index].childs = eff_childs; } /* 5a step: building final networks table */ for (index = 0; index < tmpt->num6; index++) { int current, next, prev[128]; if (!index) { current = 0; next = eff_rows; memset(&prev, 0, 32); memcpy(&nt->table6[current], &tmpt->table6[index], sizeof(struct networks6_table_entry)); } else { if (mdt[index].level == mdt[index-1].level) current++; /* do nothing: we have only to copy our element */ else if (mdt[index].level > mdt[index-1].level) { /* we encountered a child */ nt->table6[current].childs_table.table6 = &nt->table6[next]; nt->table6[current].childs_table.num6 = mdt[index-1].childs; prev[mdt[index-1].level] = current; current = next; next += mdt[index-1].childs; } else { /* going back to parent level */ current = prev[mdt[index].level]; current++; } memcpy(&nt->table6[current], &tmpt->table6[index], sizeof(struct networks6_table_entry)); } } /* 5b step: debug and default route detection */ index = 0; while (index < tmpt->num6) { if (config.debug) Log(LOG_DEBUG, "DEBUG ( %s ): (networks table IPv6) AS: %x, net: %x:%x:%x:%x, mask (bit): %x:%x:%x:%x, mask (num): %x\n", filename, nt->table6[index].as, nt->table6[index].net[0], nt->table6[index].net[1], nt->table6[index].net[2], nt->table6[index].net[3], nt->table6[index].mask[0], nt->table6[index].mask[1], nt->table6[index].mask[2], nt->table6[index].mask[3], nt->table6[index].masknum); if (!nt->table6[index].mask[0] && !nt->table6[index].mask[1] && !nt->table6[index].mask[2] && !nt->table6[index].mask[3]) default_route_in_networks6_table = TRUE; index++; } /* 6th step: create networks cache BUT only for the first time */ if (!nc->cache6) { if (!config.networks_cache_entries) nc->num6 = NETWORKS6_CACHE_ENTRIES; else nc->num6 = config.networks_cache_entries; nc->cache6 = (struct networks6_cache_entry *) malloc(nc->num6*sizeof(struct networks6_cache_entry)); if (!nc->cache6) { Log(LOG_ERR, "ERROR: malloc() failed while building Networks Cache.\n"); goto handle_error; } else Log(LOG_DEBUG, "DEBUG ( %s ): IPv6 Networks Cache successfully created: %u entries.\n", filename, nc->num6); } /* 7th step: freeing resources */ memset(nc->cache6, 0, nc->num6*sizeof(struct networks6_cache_entry)); free(tmpt->table6); free(mdt); if (bkt.table6) free(bkt.table6); /* 8th step: setting timestamp */ nt->timestamp = st.st_mtime; } } return; /* error handling: if we have a copy of the old table we will rollback it; otherwise we just take the exit lane. XXX: actually we are just able to recover malloc() troubles; efforts should be pushed in the validation of the new table. */ handle_error: if (tmpt->table6) free(tmpt->table6); if (mdt) free(mdt); if (bkt.num6) { if (!nt->table6) { memcpy(nt, &bkt, sizeof(nt)); Log(LOG_WARNING, "WARN: Rolling back the old Networks Table.\n"); /* we update the timestamp to avoid loops */ stat(filename, &st); nt->timestamp = st.st_mtime; } } else exit_plugin(1); } /* sort the (sub)array v from start to end */ void merge_sort6(char * filename, struct networks6_table_entry *table, int start, int end) { int middle; /* no elements to sort */ if ((start == end) || (start == end-1)) return; /* find the middle of the array, splitting it into two subarrays */ middle = (start+end)/2; /* sort the subarray from start..middle */ merge_sort6(filename, table, start, middle); /* sort the subarray from middle..end */ merge_sort6(filename, table, middle, end); /* merge the two sorted halves */ merge6(filename, table, start, middle, end); } /* merge the subarray v[start..middle] with v[middle..end], placing the result back into v. */ void merge6(char *filename, struct networks6_table_entry *table, int start, int middle, int end) { struct networks6_table_entry *v1, *v2; int v1_n, v2_n, v1_index, v2_index, i, x, s = sizeof(struct networks6_table_entry); int chunk; v1_n = middle-start; v2_n = end-middle; v1 = malloc(v1_n*s); v2 = malloc(v2_n*s); if ((!v1) || (!v2)) Log(LOG_ERR, "ERROR ( %s ): Memory sold out when allocating 'networks table'\n", filename); for (i=0; i v2[v2_index].net[chunk]) { memcpy(&table[start+i], &v2[v2_index++], s); break; } if (v1[v1_index].net[chunk] == v2[v2_index].net[chunk]) { if (chunk != 3) continue; else { /* two network prefixes are identical; let's compare their masks */ for (x = 0; x < 4; x++) { if (v1[v1_index].mask[x] < v2[v2_index].mask[x]) { memcpy(&table[start+i], &v1[v1_index++], s); break; } if (v1[v1_index].mask[x] > v2[v2_index].mask[x]) { memcpy(&table[start+i], &v2[v2_index++], s); break; } if (v1[v1_index].mask[x] == v2[v2_index].mask[x]) { if (x != 3) continue; else memcpy(&table[start+i], &v1[v1_index++], s); } } } } } } /* clean up; either v1 or v2 may have stuff left in it */ for (; v1_index < v1_n; i++) memcpy(&table[start+i], &v1[v1_index++], s); for (; v2_index < v2_n; i++) memcpy(&table[start+i], &v2[v2_index++], s); free(v1); free(v2); } struct networks6_table_entry *binsearch6(struct networks_table *nt, struct networks_cache *nc, struct host_addr *a) { int low = 0, mid, high = nt->num6-1, chunk; u_int32_t net[4], addrh[4], addr[4]; struct networks6_table_entry *ret; memcpy(&addr, &a->address.ipv6, IP6AddrSz); memcpy(&addrh, &a->address.ipv6, IP6AddrSz); memcpy(&addrh, (void *) pm_ntohl6(addrh), IP6AddrSz); ret = networks_cache_search6(nc, addr); if (ret) return ret; binsearch_loop: while (low <= high) { mid = (low+high)/2; memcpy(&net, &addrh, IP6AddrSz); for (chunk = 0; chunk < 4; chunk++) net[chunk] &= nt->table6[mid].mask[chunk]; for (chunk = 0; chunk < 4; chunk++) { if (net[chunk] < nt->table6[mid].net[chunk]) { high = mid-1; goto binsearch_loop; } else if (net[chunk] > nt->table6[mid].net[chunk]) { low = mid+1; goto binsearch_loop; } } /* It's assumed we've found our element */ if (nt->table6[mid].childs_table.table6) { ret = binsearch6(&nt->table6[mid].childs_table, nc, a); if (!ret) { ret = &nt->table6[mid]; networks_cache_insert6(nc, addr, &nt->table6[mid]); } } else { ret = &nt->table6[mid]; networks_cache_insert6(nc, addr, &nt->table6[mid]); } return ret; } networks_cache_insert6(nc, addr, &dummy_entry6); return NULL; } void networks_cache_insert6(struct networks_cache *nc, void *key, struct networks6_table_entry *result) { struct networks6_cache_entry *ptr; unsigned int hash; u_int32_t *keyptr = key; int chunk; hash = networks_cache_hash6(key); ptr = &nc->cache6[hash % nc->num6]; memcpy(ptr->key, keyptr, IP6AddrSz); ptr->result = result; } struct networks6_table_entry *networks_cache_search6(struct networks_cache *nc, void *key) { struct networks6_cache_entry *ptr; unsigned int hash; u_int32_t *keyptr = key; int chunk; hash = networks_cache_hash6(key); ptr = &nc->cache6[hash % nc->num6]; for (chunk = 0; chunk < 4; chunk++) { if (ptr->key[chunk] == keyptr[chunk]) continue; else return NULL; } return ptr->result; } unsigned int networks_cache_hash6(void *key) { u_int32_t a, b, c; u_int32_t *keyptr = (u_int32_t *)key; a = keyptr[0]; b = keyptr[1]; c = keyptr[2]; a += JHASH_GOLDEN_RATIO; b += JHASH_GOLDEN_RATIO; c += 140281; /* trivial hash rnd */ __jhash_mix(a, b, c); return c; } #endif pmacct-0.14.0/src/ip_frag.h0000644000175000017500000000665111037474163014436 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente */ /* 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. */ /* defines */ #define IPFT_HASHSZ 256 #define IPF_TIMEOUT 60 #define PRUNE_INTERVAL 7200 #define EMER_PRUNE_INTERVAL 60 #define PRUNE_OFFSET 1800 #define DEFAULT_FRAG_BUFFER_SIZE 4096000 /* 4 Mb */ /* structures */ struct ip_fragment { unsigned char tlhdr[8]; /* upper level info */ u_int8_t got_first; /* got first packet ? */ u_int16_t a; /* bytes accumulator */ u_int16_t pa; /* packets accumulator */ time_t deadline; /* timeout timestamp */ u_int16_t ip_id; u_int8_t ip_p; u_int32_t ip_src; u_int32_t ip_dst; u_int16_t bucket; struct ip_fragment *lru_next; struct ip_fragment *lru_prev; struct ip_fragment *next; struct ip_fragment *prev; }; struct lru_l { struct ip_fragment *root; struct ip_fragment *last; }; #if defined ENABLE_IPV6 struct ip6_fragment { unsigned char tlhdr[8]; /* upper level info */ u_int8_t got_first; /* got first packet ? */ u_int16_t a; /* bytes accumulator */ u_int16_t pa; /* packets accumulator */ time_t deadline; /* timeout timestamp */ u_int32_t id; u_int32_t src[4]; u_int32_t dst[4]; u_int16_t bucket; struct ip6_fragment *lru_next; struct ip6_fragment *lru_prev; struct ip6_fragment *next; struct ip6_fragment *prev; }; struct lru_l6 { struct ip6_fragment *root; struct ip6_fragment *last; }; #endif /* global vars */ struct ip_fragment *ipft[IPFT_HASHSZ]; struct lru_l lru_list; #if defined ENABLE_IPV6 struct ip6_fragment *ipft6[IPFT_HASHSZ]; struct lru_l6 lru_list6; #endif /* prototypes */ #if (!defined __IP_FRAG_C) #define EXT extern #else #define EXT #endif EXT void init_ip_fragment_handler(); /* wrapper */ EXT void init_ip4_fragment_handler(); EXT int ip_fragment_handler(struct packet_ptrs *); EXT int find_fragment(u_int32_t, struct packet_ptrs *); EXT int create_fragment(u_int32_t, struct ip_fragment *, u_int8_t, unsigned int, struct packet_ptrs *); EXT unsigned int hash_fragment(u_int16_t, u_int32_t, u_int32_t, u_int8_t); EXT void prune_old_fragments(u_int32_t, u_int32_t); EXT void notify_orphan_fragment(struct ip_fragment *); #if defined ENABLE_IPV6 EXT void init_ip6_fragment_handler(); EXT int ip6_fragment_handler6(struct packet_ptrs *, struct ip6_frag *); EXT unsigned int hash_fragment6(u_int32_t, struct in6_addr *, struct in6_addr *); EXT int find_fragment6(u_int32_t, struct packet_ptrs *, struct ip6_frag *); EXT int create_fragment6(u_int32_t, struct ip6_fragment *, u_int8_t, unsigned int, struct packet_ptrs *, struct ip6_frag *); EXT void prune_old_fragments6(u_int32_t, u_int32_t); EXT void notify_orphan_fragment6(struct ip6_fragment *); #endif #undef EXT pmacct-0.14.0/src/plugin_hooks.h0000644000175000017500000001104011663676014015520 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ #define DEFAULT_CHBUFLEN 4096 #define DEFAULT_PIPE_SIZE 65535 #define DEFAULT_PLOAD_SIZE 256 #define WARNING_PIPE_SIZE 16384000 /* 16 Mb */ #define MAX_FAILS 5 #define MAX_SEQNUM 65536 #define MAX_RG_COUNT_ERR 3 struct channels_list_entry; typedef void (*pkt_handler) (struct channels_list_entry *, struct packet_ptrs *, char **); typedef int (*ring_cleaner) (void *); typedef pm_counter_t (*skip_func) (pm_counter_t); struct ring { u_int32_t seq; char *base; char *ptr; char *end; }; struct ch_buf_hdr { u_int32_t seq; int num; }; struct ch_status { u_int8_t wakeup; /* plugin is polling */ u_int32_t backlog; }; struct sampling { pm_counter_t rate; pm_counter_t counter; pm_counter_t sample_pool; pm_counter_t sampled_pkts; skip_func sf; }; struct aggregate_filter { int *num; struct bpf_program **table; }; struct channels_list_entry { u_int64_t aggregation; u_int32_t buf; /* buffer base */ u_int32_t bufptr; /* buffer current */ u_int32_t bufend; /* buffer end; max 4Gb */ struct ring rg; struct ch_buf_hdr hdr; struct ch_status *status; ring_cleaner clean_func; u_int8_t request; /* does the plugin support on-request wakeup ? */ u_int8_t reprocess; /* do we need to jump back for packet reprocessing ? */ int bufsize; int same_aggregate; pkt_handler phandler[N_PRIMITIVES]; int pipe; pm_id_t id; /* post-tagging id */ struct pretag_filter tag_filter; /* filter aggregates basing on their tag */ struct pretag_filter tag2_filter; /* filter aggregates basing on their tag2 */ struct aggregate_filter agg_filter; /* filter aggregates basing on L2-L4 primitives */ struct sampling s; struct plugins_list_entry *plugin; /* backpointer to the plugin the actual channel belongs to */ }; #if (defined __PLUGIN_HOOKS_C) extern struct channels_list_entry channels_list[MAX_N_PLUGINS]; #endif /* Function prototypes */ #if (!defined __PLUGIN_HOOKS_C) #define EXT extern #else #define EXT #endif EXT void load_plugins(struct plugin_requests *); EXT void exec_plugins(struct packet_ptrs *pptrs); EXT void load_plugin_filters(int); EXT struct channels_list_entry *insert_pipe_channel(int, struct configuration *, int); EXT void delete_pipe_channel(int); EXT void sort_pipe_channels(); EXT void init_pipe_channels(); EXT int evaluate_tags(struct pretag_filter *, pm_id_t); EXT int evaluate_filters(struct aggregate_filter *, char *, struct pcap_pkthdr *); EXT void recollect_pipe_memory(struct channels_list_entry *); EXT void init_random_seed(); EXT void fill_pipe_buffer(); EXT int check_shadow_status(struct packet_ptrs *, struct channels_list_entry *); EXT int pkt_data_clean(void *); EXT int pkt_payload_clean(void *); EXT int pkt_msg_clean(void *); EXT int pkt_extras_clean(void *); EXT int pkt_bgp_clean(void *); EXT void evaluate_sampling(struct sampling *, pm_counter_t *, pm_counter_t *, pm_counter_t *); EXT pm_counter_t take_simple_random_skip(pm_counter_t); EXT pm_counter_t take_simple_systematic_skip(pm_counter_t); #undef EXT #if (defined __PLUGIN_HOOKS_C) #define EXT extern #else #define EXT #endif EXT void imt_plugin(int, struct configuration *, void *); EXT void print_plugin(int, struct configuration *, void *); EXT void nfprobe_plugin(int, struct configuration *, void *); EXT void sfprobe_plugin(int, struct configuration *, void *); EXT void tee_plugin(int, struct configuration *, void *); #ifdef WITH_MYSQL EXT void mysql_plugin(int, struct configuration *, void *); #endif #ifdef WITH_PGSQL EXT void pgsql_plugin(int, struct configuration *, void *); #endif #ifdef WITH_SQLITE3 EXT void sqlite3_plugin(int, struct configuration *, void *); #endif EXT void stats_plugin(int, struct configuration *, void *); EXT char *extract_token(char **, int); #undef EXT pmacct-0.14.0/src/sql_handlers.c0000644000175000017500000005421211712352255015472 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #define __SQL_HANDLERS_C /* PG_* functions are used only by PostgreSQL plugin; MY_* functions are used only by MySQL plugin; count_* functions are used by more than one plugin; fake_* functions are used to supply static zero-filled values; */ /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "sql_common.h" #include "ip_flow.h" #include "classifier.h" static const char fake_mac[] = "0:0:0:0:0:0"; static const char fake_host[] = "0.0.0.0"; static const char fake_as[] = "0"; static const char fake_comm[] = ""; static const char fake_as_path[] = ""; /* Functions */ #if defined (HAVE_L2) void count_src_mac_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { char sbuf[18]; u_int8_t ubuf[ETH_ADDR_LEN]; memcpy(&ubuf, &cache_elem->primitives.eth_shost, ETH_ADDR_LEN); etheraddr_string(ubuf, sbuf); snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, sbuf); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, sbuf); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, sbuf); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_dst_mac_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { char sbuf[18]; u_int8_t ubuf[ETH_ADDR_LEN]; memcpy(ubuf, &cache_elem->primitives.eth_dhost, ETH_ADDR_LEN); etheraddr_string(ubuf, sbuf); snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, sbuf); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, sbuf); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_vlan_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.vlan_id); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.vlan_id); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_cos_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.cos); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.cos); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_etype_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.etype); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.etype); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } #endif void count_src_host_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { char ptr[INET6_ADDRSTRLEN]; addr_to_str(ptr, &cache_elem->primitives.src_ip); snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, ptr); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, ptr); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_src_as_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.src_as); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.src_as); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_dst_host_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { char ptr[INET6_ADDRSTRLEN]; addr_to_str(ptr, &cache_elem->primitives.dst_ip); snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, ptr); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, ptr); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_dst_as_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.dst_as); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.dst_as); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_in_iface_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.ifindex_in); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.ifindex_in); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_out_iface_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.ifindex_out); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.ifindex_out); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_src_nmask_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.src_nmask); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.src_nmask); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_dst_nmask_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.dst_nmask); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.dst_nmask); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_std_comm_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->std_comms); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->std_comms); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_ext_comm_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->ext_comms); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->ext_comms); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_as_path_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->as_path); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->as_path); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_src_std_comm_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->src_std_comms); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->src_std_comms); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_src_ext_comm_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->src_ext_comms); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->src_ext_comms); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_src_as_path_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->src_as_path); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->src_as_path); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_local_pref_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->local_pref); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->local_pref); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_src_local_pref_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->src_local_pref); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->src_local_pref); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_med_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->med); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->med); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_src_med_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->src_med); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->src_med); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_mpls_vpn_rd_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { char ptr[SRVBUFLEN]; bgp_rd2str(ptr, &cache_elem->cbgp->mpls_vpn_rd); snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, ptr); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, ptr); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_peer_src_as_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->peer_src_as); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->peer_src_as); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_peer_dst_as_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->cbgp->peer_dst_as); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->cbgp->peer_dst_as); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_peer_src_ip_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { char ptr[INET6_ADDRSTRLEN]; addr_to_str(ptr, &cache_elem->cbgp->peer_src_ip); snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, ptr); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, ptr); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_peer_dst_ip_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { char ptr[INET6_ADDRSTRLEN], *indirect_ptr = ptr; addr_to_str(ptr, &cache_elem->cbgp->peer_dst_ip); if (!strlen(ptr)) indirect_ptr = (char *) fake_host; snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, indirect_ptr); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, indirect_ptr); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_src_port_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.src_port); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.src_port); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_dst_port_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.dst_port); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.dst_port); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_tcpflags_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->tcp_flags); *ptr_values += strlen(*ptr_values); } void count_ip_tos_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.tos); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.tos); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void MY_count_ip_proto_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { if (cache_elem->primitives.proto < protocols_number) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, _protocols[cache_elem->primitives.proto].name); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, _protocols[cache_elem->primitives.proto].name); } else { char proto_str[PROTO_LEN]; snprintf(proto_str, sizeof(proto_str), "%d", cache_elem->primitives.proto); snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, proto_str); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, proto_str); } *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void PG_count_ip_proto_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.proto); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.proto); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_copy_timestamp_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { static char btime_str[LONGSRVBUFLEN], now_str[LONGSRVBUFLEN]; struct tm *tme; tme = localtime(&cache_elem->basetime); strftime(btime_str, LONGSRVBUFLEN, "%Y-%m-%d %H:%M:%S", tme); if (!glob_nfacctd_sql_log) tme = localtime(&idata->now); else tme = localtime(&cache_elem->endtime); strftime(now_str, LONGSRVBUFLEN, "%Y-%m-%d %H:%M:%S", tme); snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->basetime); // dummy snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, now_str, btime_str); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_timestamp_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { time_t tme = idata->now; if (glob_nfacctd_sql_log) tme = cache_elem->endtime; snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->basetime); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, tme, cache_elem->basetime); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_id_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.id); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.id); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_id2_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, cache_elem->primitives.id2); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, cache_elem->primitives.id2); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_class_id_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { char buf[MAX_PROTOCOL_LEN+1]; memset(buf, 0, MAX_PROTOCOL_LEN+1); if (cache_elem->primitives.class && class[cache_elem->primitives.class-1].id) { strlcpy(buf, class[cache_elem->primitives.class-1].protocol, MAX_PROTOCOL_LEN); buf[sizeof(buf)-1] = '\0'; } else strlcpy(buf, "unknown", MAX_PROTOCOL_LEN); snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, buf); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, buf); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void count_counters_setclause_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_set, char **ptr_none) { snprintf(*ptr_set, SPACELEFT(set_clause), set[num].string, cache_elem->packet_counter, cache_elem->bytes_counter); *ptr_set += strlen(*ptr_set); } void count_flows_setclause_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_set, char **ptr_none) { snprintf(*ptr_set, SPACELEFT(set_clause), set[num].string, cache_elem->flows_counter); *ptr_set += strlen(*ptr_set); } void count_timestamp_setclause_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_set, char **ptr_none) { snprintf(*ptr_set, SPACELEFT(set_clause), set[num].string, cache_elem->endtime); *ptr_set += strlen(*ptr_set); } void count_tcpflags_setclause_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_set, char **ptr_none) { snprintf(*ptr_set, SPACELEFT(set_clause), set[num].string, cache_elem->tcp_flags); *ptr_set += strlen(*ptr_set); } void count_noop_setclause_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_set, char **ptr_none) { strncpy(*ptr_set, set[num].string, SPACELEFT(set_clause)); *ptr_set += strlen(*ptr_set); } /* Fake handlers next */ void fake_mac_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, fake_mac); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, fake_mac); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void fake_host_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, fake_host); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, fake_host); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void fake_as_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, fake_as); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, fake_as); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void fake_comms_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, fake_comm); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, fake_comm); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } void fake_as_path_handler(const struct db_cache *cache_elem, const struct insert_data *idata, int num, char **ptr_values, char **ptr_where) { snprintf(*ptr_where, SPACELEFT(where_clause), where[num].string, fake_as_path); snprintf(*ptr_values, SPACELEFT(values_clause), values[num].string, fake_as_path); *ptr_where += strlen(*ptr_where); *ptr_values += strlen(*ptr_values); } pmacct-0.14.0/src/preprocess.h0000644000175000017500000000564311250743543015212 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente */ /* 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. */ struct preprocess { u_int32_t qnum; u_int16_t minp; u_int16_t minf; u_int32_t minb; u_int16_t maxp; u_int16_t maxf; u_int32_t maxb; u_int16_t maxbpp; u_int16_t maxppf; u_int16_t minbpp; u_int16_t minppf; u_int32_t fss; /* threshold: flow size (flow size dependent sampling) */ u_int32_t fsrc; /* threshold: flows number (flow sampling with resource constraints) */ int usrf; /* renormalization factor for uniform sampling methods */ int adjb; /* adjusts bytes counter by 'adjb' bytes */ u_int8_t recover; u_int8_t num; /* total number of preprocess clauses specified: actions + checks */ u_int8_t checkno; /* number of checks */ u_int8_t actionno; /* number of actions */ }; struct fsrc_queue_elem { struct fsrc_queue_elem *next; struct db_cache *cache_ptr; float z; }; struct _fsrc_queue { struct fsrc_queue_elem head; u_int32_t num; }; #if (!defined __PREPROCESS_C) #define EXT extern #else #define EXT #endif EXT void set_preprocess_funcs(char *, struct preprocess *); EXT int cond_qnum(struct db_cache *[], int *, int); EXT int check_minp(struct db_cache *[], int *, int); EXT int check_minb(struct db_cache *[], int *, int); EXT int check_minf(struct db_cache *[], int *, int); EXT int check_maxp(struct db_cache *[], int *, int); EXT int check_maxb(struct db_cache *[], int *, int); EXT int check_maxf(struct db_cache *[], int *, int); EXT int check_maxbpp(struct db_cache *[], int *, int); EXT int check_maxppf(struct db_cache *[], int *, int); EXT int check_minbpp(struct db_cache *[], int *, int); EXT int check_minppf(struct db_cache *[], int *, int); EXT int check_fss(struct db_cache *[], int *, int); EXT int check_fsrc(struct db_cache *[], int *, int); EXT int action_usrf(struct db_cache *[], int *, int); EXT int action_adjb(struct db_cache *[], int *, int); EXT int mandatory_invalidate(struct db_cache *[], int *, int); EXT int mandatory_validate(struct db_cache *[], int *, int); EXT void check_validity(struct db_cache *, int); EXT preprocess_func preprocess_funcs[2*N_FUNCS]; /* 20 */ EXT struct preprocess prep; EXT struct _fsrc_queue fsrc_queue; #undef EXT pmacct-0.14.0/src/nfacctd.c0000644000175000017500000026275511741105272014427 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ /* defines */ #define __NFACCTD_C /* includes */ #include "pmacct.h" #include "nfacctd.h" #include "pretag_handlers.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "pkt_handlers.h" #include "ip_flow.h" #include "classifier.h" #include "net_aggr.h" #include "bgp/bgp_packet.h" #include "bgp/bgp.h" /* variables to be exported away */ int debug; struct configuration config; /* global configuration */ struct plugins_list_entry *plugins_list = NULL; /* linked list of each plugin configuration */ struct channels_list_entry channels_list[MAX_N_PLUGINS]; /* communication channels: core <-> plugins */ int have_num_memory_pools; /* global getopt() stuff */ pid_t failed_plugins[MAX_N_PLUGINS]; /* plugins failed during startup phase */ /* Functions */ void usage_daemon(char *prog_name) { printf("%s\n", NFACCTD_USAGE_HEADER); printf("Usage: %s [ -D | -d ] [ -L IP address ] [ -l port ] [ -c primitive [ , ... ] ] [ -P plugin [ , ... ] ]\n", prog_name); printf(" %s [ -f config_file ]\n", prog_name); printf(" %s [ -h ]\n", prog_name); printf("\nGeneral options:\n"); printf(" -h \tShow this page\n"); printf(" -L \tBind to the specified IP address\n"); printf(" -l \tListen on the specified UDP port\n"); printf(" -f \tLoad configuration from the specified file\n"); printf(" -c \t[ src_mac | dst_mac | vlan | src_host | dst_host | src_net | dst_net | src_port | dst_port |\n\t tos | proto | src_as | dst_as | sum_mac | sum_host | sum_net | sum_as | sum_port | tag |\n\t tag2 | flows | class | tcpflags | in_iface | out_iface | src_mask | dst_mask | cos | etype | none ] \n\tAggregation string (DEFAULT: src_host)\n"); printf(" -D \tDaemonize\n"); printf(" -n \tPath to a file containing Network definitions\n"); printf(" -o \tPath to a file containing Port definitions\n"); printf(" -P \t[ memory | print | mysql | pgsql | sqlite3 | tee ] \n\tActivate plugin\n"); printf(" -d \tEnable debug\n"); printf(" -S \t[ auth | mail | daemon | kern | user | local[0-7] ] \n\tLog to the specified syslog facility\n"); printf(" -F \tWrite Core Process PID into the specified file\n"); printf(" -R \tRenormalize sampled data\n"); printf(" -u \tLeave IP protocols in numerical format\n"); printf("\nMemory plugin (-P memory) options:\n"); printf(" -p \tSocket for client-server communication (DEFAULT: /tmp/collect.pipe)\n"); printf(" -b \tNumber of buckets\n"); printf(" -m \tNumber of memory pools\n"); printf(" -s \tMemory pool size\n"); printf("\nPostgreSQL (-P pgsql)/MySQL (-P mysql)/SQLite (-P sqlite3) plugin options:\n"); printf(" -r \tRefresh time (in seconds)\n"); printf(" -v \t[ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ] \n\tTable version\n"); printf("\nPrint plugin (-P print) plugin options:\n"); printf(" -r \tRefresh time (in seconds)\n"); printf(" -O \t[ formatted | csv ] \n\tOutput format\n"); printf("\n"); printf(" See EXAMPLES or visit http://wiki.pmacct.net/ for examples.\n"); printf("\n"); printf("For suggestions, critics, bugs, contact me: %s.\n", MANTAINER); } int main(int argc,char **argv, char **envp) { struct plugins_list_entry *list; struct plugin_requests req; struct packet_ptrs_vector pptrs; char config_file[SRVBUFLEN]; unsigned char netflow_packet[NETFLOW_MSG_SIZE]; int logf, rc, yes=1, allowed; struct host_addr addr; struct hosts_table allow; struct id_table idt; struct id_table bpas_table; struct id_table blp_table; struct id_table bmed_table; struct id_table biss_table; struct id_table bta_table; struct id_table bitr_table; struct id_table sampling_table; u_int32_t idx; u_int16_t ret; #if defined ENABLE_IPV6 struct sockaddr_storage server, client; struct ipv6_mreq multi_req6; #else struct sockaddr server, client; #endif int clen = sizeof(client), slen; struct ip_mreq multi_req4; unsigned char dummy_packet[64]; unsigned char dummy_packet_vlan[64]; unsigned char dummy_packet_mpls[128]; unsigned char dummy_packet_vlan_mpls[128]; struct pcap_pkthdr dummy_pkthdr; struct pcap_pkthdr dummy_pkthdr_vlan; struct pcap_pkthdr dummy_pkthdr_mpls; struct pcap_pkthdr dummy_pkthdr_vlan_mpls; #if defined ENABLE_IPV6 unsigned char dummy_packet6[92]; unsigned char dummy_packet_vlan6[92]; unsigned char dummy_packet_mpls6[128]; unsigned char dummy_packet_vlan_mpls6[128]; struct pcap_pkthdr dummy_pkthdr6; struct pcap_pkthdr dummy_pkthdr_vlan6; struct pcap_pkthdr dummy_pkthdr_mpls6; struct pcap_pkthdr dummy_pkthdr_vlan_mpls6; #endif /* getopt() stuff */ extern char *optarg; extern int optind, opterr, optopt; int errflag, cp; umask(077); compute_once(); /* a bunch of default definitions */ have_num_memory_pools = FALSE; reload_map = FALSE; tag_map_allocated = FALSE; sampling_map_allocated = FALSE; bpas_map_allocated = FALSE; blp_map_allocated = FALSE; bmed_map_allocated = FALSE; biss_map_allocated = FALSE; bta_map_allocated = FALSE; bitr_map_allocated = FALSE; find_id_func = NF_find_id; data_plugins = 0; tee_plugins = 0; xflow_status_table_entries = 0; xflow_tot_bad_datagrams = 0; errflag = 0; memset(cfg_cmdline, 0, sizeof(cfg_cmdline)); memset(&server, 0, sizeof(server)); memset(&config, 0, sizeof(struct configuration)); memset(&config_file, 0, sizeof(config_file)); memset(&failed_plugins, 0, sizeof(failed_plugins)); memset(&pptrs, 0, sizeof(pptrs)); memset(&req, 0, sizeof(req)); memset(&class, 0, sizeof(class)); memset(&xflow_status_table, 0, sizeof(xflow_status_table)); memset(&idt, 0, sizeof(idt)); memset(&bpas_table, 0, sizeof(bpas_table)); memset(&blp_table, 0, sizeof(blp_table)); memset(&bmed_table, 0, sizeof(bmed_table)); memset(&biss_table, 0, sizeof(biss_table)); memset(&bta_table, 0, sizeof(bta_table)); memset(&bitr_table, 0, sizeof(bitr_table)); memset(&sampling_table, 0, sizeof(sampling_table)); config.acct_type = ACCT_NF; rows = 0; glob_pcapt = NULL; /* getting commandline values */ while (!errflag && ((cp = getopt(argc, argv, ARGS_NFACCTD)) != -1)) { cfg_cmdline[rows] = malloc(SRVBUFLEN); switch (cp) { case 'L': strlcpy(cfg_cmdline[rows], "nfacctd_ip: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'l': strlcpy(cfg_cmdline[rows], "nfacctd_port: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'P': strlcpy(cfg_cmdline[rows], "plugins: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'D': strlcpy(cfg_cmdline[rows], "daemonize: true", SRVBUFLEN); rows++; break; case 'd': debug = TRUE; strlcpy(cfg_cmdline[rows], "debug: true", SRVBUFLEN); rows++; break; case 'n': strlcpy(cfg_cmdline[rows], "networks_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'o': strlcpy(cfg_cmdline[rows], "ports_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'O': strlcpy(cfg_cmdline[rows], "print_output: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'u': strlcpy(cfg_cmdline[rows], "print_num_protos: true", SRVBUFLEN); rows++; break; case 'f': strlcpy(config_file, optarg, sizeof(config_file)); break; case 'F': strlcpy(cfg_cmdline[rows], "pidfile: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'c': strlcpy(cfg_cmdline[rows], "aggregate: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'b': strlcpy(cfg_cmdline[rows], "imt_buckets: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'm': strlcpy(cfg_cmdline[rows], "imt_mem_pools_number: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); have_num_memory_pools = TRUE; rows++; break; case 'p': strlcpy(cfg_cmdline[rows], "imt_path: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'r': strlcpy(cfg_cmdline[rows], "sql_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; cfg_cmdline[rows] = malloc(SRVBUFLEN); strlcpy(cfg_cmdline[rows], "print_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'v': strlcpy(cfg_cmdline[rows], "sql_table_version: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 's': strlcpy(cfg_cmdline[rows], "imt_mem_pools_size: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'S': strlcpy(cfg_cmdline[rows], "syslog: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'R': strlcpy(cfg_cmdline[rows], "sfacctd_renormalize: true", SRVBUFLEN); rows++; break; case 'h': usage_daemon(argv[0]); exit(0); break; default: usage_daemon(argv[0]); exit(1); break; } } /* post-checks and resolving conflicts */ if (strlen(config_file)) { if (parse_configuration_file(config_file) != SUCCESS) exit(1); } else { if (parse_configuration_file(NULL) != SUCCESS) exit(1); } /* XXX: glue; i'm conscious it's a dirty solution from an engineering viewpoint; someday later i'll fix this */ list = plugins_list; while(list) { list->cfg.acct_type = ACCT_NF; set_default_preferences(&list->cfg); if (!strcmp(list->name, "default") && !strcmp(list->type.string, "core")) memcpy(&config, &list->cfg, sizeof(struct configuration)); list = list->next; } if (config.files_umask) umask(config.files_umask); if (config.daemon) { list = plugins_list; while (list) { if (!strcmp(list->type.string, "print")) printf("WARN ( default/core ): Daemonizing. Hmm, bye bye screen.\n"); list = list->next; } if (debug || config.debug) printf("WARN ( default/core ): debug is enabled; forking in background. Console logging will get lost.\n"); daemonize(); } initsetproctitle(argc, argv, envp); if (config.syslog) { logf = parse_log_facility(config.syslog); if (logf == ERR) { config.syslog = NULL; printf("WARN ( default/core ): specified syslog facility is not supported; logging to console.\n"); } else openlog(NULL, LOG_PID, logf); Log(LOG_INFO, "INFO ( default/core ): Start logging ...\n"); } if (config.logfile) { config.logfile_fd = open_logfile(config.logfile); list = plugins_list; while (list) { list->cfg.logfile_fd = config.logfile_fd ; list = list->next; } } /* Enforcing policies over aggregation methods */ list = plugins_list; while (list) { if (list->type.id != PLUGIN_ID_CORE) { /* applies to all plugins */ if (list->cfg.sampling_rate && config.ext_sampling_rate) { Log(LOG_ERR, "ERROR ( default/core ): Internal packet sampling and external packet sampling are mutual exclusive.\n"); exit(1); } if (list->type.id == PLUGIN_ID_NFPROBE || list->type.id == PLUGIN_ID_SFPROBE) { Log(LOG_ERR, "ERROR ( default/core ): 'nfprobe' and 'sfprobe' plugins not supported in 'nfacctd'.\n"); exit(1); } else if (list->type.id == PLUGIN_ID_TEE) { tee_plugins++; list->cfg.what_to_count = COUNT_NONE; list->cfg.data_type = PIPE_TYPE_MSG; } else { list->cfg.data_type = PIPE_TYPE_METADATA; evaluate_sums(&list->cfg.what_to_count, list->name, list->type.string); if (!list->cfg.what_to_count) { Log(LOG_WARNING, "WARN ( %s/%s ): defaulting to SRC HOST aggregation.\n", list->name, list->type.string); list->cfg.what_to_count |= COUNT_SRC_HOST; } if ((list->cfg.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) && !list->cfg.networks_file && list->cfg.nfacctd_as & NF_AS_NEW) { Log(LOG_ERR, "ERROR ( %s/%s ): AS aggregation selected but NO 'networks_file' specified. Exiting...\n\n", list->name, list->type.string); exit(1); } if ((list->cfg.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) && !list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP) { Log(LOG_ERR, "ERROR ( %s/%s ): AS aggregation selected but 'bgp_daemon' is not enabled. Exiting...\n\n", list->name, list->type.string); exit(1); } if (list->cfg.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET|COUNT_SUM_NET|COUNT_SRC_NMASK|COUNT_DST_NMASK|COUNT_PEER_DST_IP)) { if (!list->cfg.nfacctd_net) { if (list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; if (list->cfg.networks_mask) list->cfg.nfacctd_net |= NF_NET_STATIC; if (!list->cfg.nfacctd_net) list->cfg.nfacctd_net = NF_NET_KEEP; } else { if ((list->cfg.nfacctd_net == NF_NET_NEW && !list->cfg.networks_file) || (list->cfg.nfacctd_net == NF_NET_STATIC && !list->cfg.networks_mask) || (list->cfg.nfacctd_net == NF_NET_BGP && !list->cfg.nfacctd_bgp) || (list->cfg.nfacctd_net == NF_NET_IGP && !list->cfg.nfacctd_isis)) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'bgp_daemon', 'isis_daemon', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } } } bgp_config_checks(&list->cfg); data_plugins++; list->cfg.what_to_count |= COUNT_COUNTERS; } } list = list->next; } if (tee_plugins && data_plugins) { Log(LOG_ERR, "ERROR: 'tee' plugins are not compatible with data (memory/mysql/pgsql/etc.) plugins. Exiting...\n\n"); exit(1); } /* signal handling we want to inherit to plugins (when not re-defined elsewhere) */ signal(SIGCHLD, startup_handle_falling_child); /* takes note of plugins failed during startup phase */ signal(SIGHUP, reload); /* handles reopening of syslog channel */ signal(SIGUSR1, push_stats); /* logs various statistics via Log() calls */ signal(SIGUSR2, reload_maps); /* sets to true the reload_maps flag */ signal(SIGPIPE, SIG_IGN); /* we want to exit gracefully when a pipe is broken */ /* If no IP address is supplied, let's set our default behaviour: IPv4 address, INADDR_ANY, port 2100 */ if (!config.nfacctd_port) config.nfacctd_port = DEFAULT_NFACCTD_PORT; #if (defined ENABLE_IPV6 && defined V4_MAPPED) if (!config.nfacctd_ip) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&server; sa6->sin6_family = AF_INET6; sa6->sin6_port = htons(config.nfacctd_port); slen = sizeof(struct sockaddr_in6); } #else if (!config.nfacctd_ip) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&server; sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = htonl(0); sa4->sin_port = htons(config.nfacctd_port); slen = sizeof(struct sockaddr_in); } #endif else { trim_spaces(config.nfacctd_ip); ret = str_to_addr(config.nfacctd_ip, &addr); if (!ret) { Log(LOG_ERR, "ERROR ( default/core ): 'nfacctd_ip' value is not valid. Exiting.\n"); exit(1); } slen = addr_to_sa((struct sockaddr *)&server, &addr, config.nfacctd_port); } /* socket creation */ config.sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_DGRAM, 0); if (config.sock < 0) { Log(LOG_ERR, "ERROR ( default/core ): socket() failed.\n"); exit(1); } /* bind socket to port */ rc = Setsocksize(config.sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)); if (rc < 0) Log(LOG_ERR, "WARN ( default/core ): Setsocksize() failed for SO_REUSEADDR.\n"); if (config.pipe_size) { rc = Setsocksize(config.sock, SOL_SOCKET, SO_RCVBUF, &config.pipe_size, sizeof(config.pipe_size)); if (rc < 0) Log(LOG_ERR, "WARN ( default/core ): Setsocksize() failed for 'plugin_pipe_size' = '%d'.\n", config.pipe_size); } /* Multicast: memberships handling */ for (idx = 0; mcast_groups[idx].family && idx < MAX_MCAST_GROUPS; idx++) { if (mcast_groups[idx].family == AF_INET) { memset(&multi_req4, 0, sizeof(multi_req4)); multi_req4.imr_multiaddr.s_addr = mcast_groups[idx].address.ipv4.s_addr; if (setsockopt(config.sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multi_req4, sizeof(multi_req4)) < 0) { Log(LOG_ERR, "ERROR: IPv4 multicast address - ADD membership failed.\n"); exit(1); } } #if defined ENABLE_IPV6 if (mcast_groups[idx].family == AF_INET6) { memset(&multi_req6, 0, sizeof(multi_req6)); ip6_addr_cpy(&multi_req6.ipv6mr_multiaddr, &mcast_groups[idx].address.ipv6); if (setsockopt(config.sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&multi_req6, sizeof(multi_req6)) < 0) { Log(LOG_ERR, "ERROR: IPv6 multicast address - ADD membership failed.\n"); exit(1); } } #endif } if (config.nfacctd_allow_file) load_allow_file(config.nfacctd_allow_file, &allow); else memset(&allow, 0, sizeof(allow)); if (config.pre_tag_map) { load_id_file(config.acct_type, config.pre_tag_map, &idt, &req, &tag_map_allocated); pptrs.v4.idtable = (u_char *) &idt; } else pptrs.v4.idtable = NULL; if (config.sampling_map) { load_id_file(MAP_SAMPLING, config.sampling_map, &sampling_table, &req, &sampling_map_allocated); set_sampling_table(&pptrs, (u_char *) &sampling_table); } else set_sampling_table(&pptrs, NULL); #if defined ENABLE_THREADS /* starting the ISIS threa */ if (config.nfacctd_isis) { req.bpf_filter = TRUE; nfacctd_isis_wrapper(); /* Let's give the ISIS thread some advantage to create its structures */ sleep(5); } /* starting the BGP thread */ if (config.nfacctd_bgp) { req.bpf_filter = TRUE; load_comm_patterns(&config.nfacctd_bgp_stdcomm_pattern, &config.nfacctd_bgp_extcomm_pattern, &config.nfacctd_bgp_stdcomm_pattern_to_asn); if (config.nfacctd_bgp_peer_as_src_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_peer_as_src_map) { load_id_file(MAP_BGP_PEER_AS_SRC, config.nfacctd_bgp_peer_as_src_map, &bpas_table, &req, &bpas_map_allocated); pptrs.v4.bpas_table = (u_char *) &bpas_table; } else { Log(LOG_ERR, "ERROR: bgp_peer_as_src_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else pptrs.v4.bpas_table = NULL; if (config.nfacctd_bgp_src_local_pref_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_local_pref_map) { load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.nfacctd_bgp_src_local_pref_map, &blp_table, &req, &blp_map_allocated); pptrs.v4.blp_table = (u_char *) &blp_table; } else { Log(LOG_ERR, "ERROR: bgp_src_local_pref_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else pptrs.v4.blp_table = NULL; if (config.nfacctd_bgp_src_med_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_med_map) { load_id_file(MAP_BGP_SRC_MED, config.nfacctd_bgp_src_med_map, &bmed_table, &req, &bmed_map_allocated); pptrs.v4.bmed_table = (u_char *) &bmed_table; } else { Log(LOG_ERR, "ERROR: bgp_src_med_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else pptrs.v4.bmed_table = NULL; if (config.nfacctd_bgp_to_agent_map) { load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.nfacctd_bgp_to_agent_map, &bta_table, &req, &bta_map_allocated); pptrs.v4.bta_table = (u_char *) &bta_table; } else pptrs.v4.bta_table = NULL; if (config.nfacctd_bgp_iface_to_rd_map) { load_id_file(MAP_BGP_IFACE_TO_RD, config.nfacctd_bgp_iface_to_rd_map, &bitr_table, &req, &bitr_map_allocated); pptrs.v4.bitr_table = (u_char *) &bitr_table; } else pptrs.v4.bitr_table = NULL; nfacctd_bgp_wrapper(); /* Let's give the BGP thread some advantage to create its structures */ sleep(5); } #else if (config.nfacctd_isis) { Log(LOG_ERR, "ERROR ( default/core ): 'isis_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } if (config.nfacctd_bgp) { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } #endif rc = bind(config.sock, (struct sockaddr *) &server, slen); if (rc < 0) { Log(LOG_ERR, "ERROR ( default/core ): bind() to ip=%s port=%d/udp failed (errno: %d).\n", config.nfacctd_ip, config.nfacctd_port, errno); exit(1); } load_nfv8_handlers(); init_classifiers(NULL); /* plugins glue: creation */ load_plugins(&req); load_plugin_filters(1); evaluate_packet_handlers(); pm_setproctitle("%s [%s]", "Core Process", "default"); if (config.pidfile) write_pid_file(config.pidfile); load_networks(config.networks_file, &nt, &nc); /* signals to be handled only by pmacctd; we set proper handlers after plugin creation */ signal(SIGINT, my_sigint_handler); signal(SIGTERM, my_sigint_handler); signal(SIGCHLD, handle_falling_child); kill(getpid(), SIGCHLD); /* initializing template cache */ memset(&tpl_cache, 0, sizeof(tpl_cache)); tpl_cache.num = TEMPLATE_CACHE_ENTRIES; /* arranging static pointers to dummy packet; to speed up things into the main loop we mantain two packet_ptrs structures when IPv6 is enabled: we will sync here 'pptrs6' for common tables and pointers */ memset(dummy_packet, 0, sizeof(dummy_packet)); pptrs.v4.f_agent = (u_char *) &client; pptrs.v4.packet_ptr = dummy_packet; pptrs.v4.pkthdr = &dummy_pkthdr; Assign16(((struct eth_header *)pptrs.v4.packet_ptr)->ether_type, htons(ETHERTYPE_IP)); /* 0x800 */ pptrs.v4.mac_ptr = (u_char *)((struct eth_header *)pptrs.v4.packet_ptr)->ether_dhost; pptrs.v4.iph_ptr = pptrs.v4.packet_ptr + ETHER_HDRLEN; pptrs.v4.tlh_ptr = pptrs.v4.packet_ptr + ETHER_HDRLEN + sizeof(struct my_iphdr); Assign8(((struct my_iphdr *)pptrs.v4.iph_ptr)->ip_vhl, 5); // pptrs.v4.pkthdr->caplen = 38; /* eth_header + my_iphdr + my_tlhdr */ pptrs.v4.pkthdr->caplen = 55; pptrs.v4.pkthdr->len = 100; /* fake len */ pptrs.v4.l3_proto = ETHERTYPE_IP; memset(dummy_packet_vlan, 0, sizeof(dummy_packet_vlan)); pptrs.vlan4.idtable = pptrs.v4.idtable; pptrs.vlan4.f_agent = (u_char *) &client; pptrs.vlan4.packet_ptr = dummy_packet_vlan; pptrs.vlan4.pkthdr = &dummy_pkthdr_vlan; Assign16(((struct eth_header *)pptrs.vlan4.packet_ptr)->ether_type, htons(ETHERTYPE_8021Q)); pptrs.vlan4.mac_ptr = (u_char *)((struct eth_header *)pptrs.vlan4.packet_ptr)->ether_dhost; pptrs.vlan4.vlan_ptr = pptrs.vlan4.packet_ptr + ETHER_HDRLEN; Assign16(*(pptrs.vlan4.vlan_ptr+2), htons(ETHERTYPE_IP)); pptrs.vlan4.iph_ptr = pptrs.vlan4.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN; pptrs.vlan4.tlh_ptr = pptrs.vlan4.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN + sizeof(struct my_iphdr); Assign8(((struct my_iphdr *)pptrs.vlan4.iph_ptr)->ip_vhl, 5); // pptrs.vlan4.pkthdr->caplen = 42; /* eth_header + vlan + my_iphdr + my_tlhdr */ pptrs.vlan4.pkthdr->caplen = 59; pptrs.vlan4.pkthdr->len = 100; /* fake len */ pptrs.vlan4.l3_proto = ETHERTYPE_IP; memset(dummy_packet_mpls, 0, sizeof(dummy_packet_mpls)); pptrs.mpls4.idtable = pptrs.v4.idtable; pptrs.mpls4.f_agent = (u_char *) &client; pptrs.mpls4.packet_ptr = dummy_packet_mpls; pptrs.mpls4.pkthdr = &dummy_pkthdr_mpls; Assign16(((struct eth_header *)pptrs.mpls4.packet_ptr)->ether_type, htons(ETHERTYPE_MPLS)); pptrs.mpls4.mac_ptr = (u_char *)((struct eth_header *)pptrs.mpls4.packet_ptr)->ether_dhost; pptrs.mpls4.mpls_ptr = pptrs.mpls4.packet_ptr + ETHER_HDRLEN; // pptrs.mpls4.pkthdr->caplen = 78; /* eth_header + upto 10 MPLS labels + my_iphdr + my_tlhdr */ pptrs.mpls4.pkthdr->caplen = 95; pptrs.mpls4.pkthdr->len = 100; /* fake len */ pptrs.mpls4.l3_proto = ETHERTYPE_IP; memset(dummy_packet_vlan_mpls, 0, sizeof(dummy_packet_vlan_mpls)); pptrs.vlanmpls4.idtable = pptrs.v4.idtable; pptrs.vlanmpls4.f_agent = (u_char *) &client; pptrs.vlanmpls4.packet_ptr = dummy_packet_vlan_mpls; pptrs.vlanmpls4.pkthdr = &dummy_pkthdr_vlan_mpls; Assign16(((struct eth_header *)pptrs.vlanmpls4.packet_ptr)->ether_type, htons(ETHERTYPE_8021Q)); pptrs.vlanmpls4.mac_ptr = (u_char *)((struct eth_header *)pptrs.vlanmpls4.packet_ptr)->ether_dhost; pptrs.vlanmpls4.vlan_ptr = pptrs.vlanmpls4.packet_ptr + ETHER_HDRLEN; Assign16(*(pptrs.vlanmpls4.vlan_ptr+2), htons(ETHERTYPE_MPLS)); pptrs.vlanmpls4.mpls_ptr = pptrs.vlanmpls4.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN; // pptrs.vlanmpls4.pkthdr->caplen = 82; /* eth_header + vlan + upto 10 MPLS labels + my_iphdr + my_tlhdr */ pptrs.vlanmpls4.pkthdr->caplen = 99; pptrs.vlanmpls4.pkthdr->len = 100; /* fake len */ pptrs.vlanmpls4.l3_proto = ETHERTYPE_IP; #if defined ENABLE_IPV6 memset(dummy_packet6, 0, sizeof(dummy_packet6)); pptrs.v6.idtable = pptrs.v4.idtable; pptrs.v6.f_agent = (u_char *) &client; pptrs.v6.packet_ptr = dummy_packet6; pptrs.v6.pkthdr = &dummy_pkthdr6; Assign16(((struct eth_header *)pptrs.v6.packet_ptr)->ether_type, htons(ETHERTYPE_IPV6)); pptrs.v6.mac_ptr = (u_char *)((struct eth_header *)pptrs.v6.packet_ptr)->ether_dhost; pptrs.v6.iph_ptr = pptrs.v6.packet_ptr + ETHER_HDRLEN; pptrs.v6.tlh_ptr = pptrs.v6.packet_ptr + ETHER_HDRLEN + sizeof(struct ip6_hdr); Assign16(((struct ip6_hdr *)pptrs.v6.iph_ptr)->ip6_plen, htons(100)); Assign16(((struct ip6_hdr *)pptrs.v6.iph_ptr)->ip6_hlim, htons(64)); // pptrs.v6.pkthdr->caplen = 60; /* eth_header + ip6_hdr + my_tlhdr */ pptrs.v6.pkthdr->caplen = 77; pptrs.v6.pkthdr->len = 100; /* fake len */ pptrs.v6.l3_proto = ETHERTYPE_IPV6; memset(dummy_packet_vlan6, 0, sizeof(dummy_packet_vlan6)); pptrs.vlan6.idtable = pptrs.v4.idtable; pptrs.vlan6.f_agent = (u_char *) &client; pptrs.vlan6.packet_ptr = dummy_packet_vlan6; pptrs.vlan6.pkthdr = &dummy_pkthdr_vlan6; Assign16(((struct eth_header *)pptrs.vlan6.packet_ptr)->ether_type, htons(ETHERTYPE_8021Q)); pptrs.vlan6.mac_ptr = (u_char *)((struct eth_header *)pptrs.vlan6.packet_ptr)->ether_dhost; pptrs.vlan6.vlan_ptr = pptrs.vlan6.packet_ptr + ETHER_HDRLEN; Assign8(*(pptrs.vlan6.vlan_ptr+2), 0x86); Assign8(*(pptrs.vlan6.vlan_ptr+3), 0xDD); pptrs.vlan6.iph_ptr = pptrs.vlan6.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN; pptrs.vlan6.tlh_ptr = pptrs.vlan6.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN + sizeof(struct ip6_hdr); Assign16(((struct ip6_hdr *)pptrs.vlan6.iph_ptr)->ip6_plen, htons(100)); Assign16(((struct ip6_hdr *)pptrs.vlan6.iph_ptr)->ip6_hlim, htons(64)); // pptrs.vlan6.pkthdr->caplen = 64; /* eth_header + vlan + ip6_hdr + my_tlhdr */ pptrs.vlan6.pkthdr->caplen = 81; pptrs.vlan6.pkthdr->len = 100; /* fake len */ pptrs.vlan6.l3_proto = ETHERTYPE_IPV6; memset(dummy_packet_mpls6, 0, sizeof(dummy_packet_mpls6)); pptrs.mpls6.idtable = pptrs.v4.idtable; pptrs.mpls6.f_agent = (u_char *) &client; pptrs.mpls6.packet_ptr = dummy_packet_mpls6; pptrs.mpls6.pkthdr = &dummy_pkthdr_mpls6; Assign16(((struct eth_header *)pptrs.mpls6.packet_ptr)->ether_type, htons(ETHERTYPE_MPLS)); pptrs.mpls6.mac_ptr = (u_char *)((struct eth_header *)pptrs.mpls6.packet_ptr)->ether_dhost; pptrs.mpls6.mpls_ptr = pptrs.mpls6.packet_ptr + ETHER_HDRLEN; // pptrs.mpls6.pkthdr->caplen = 100; /* eth_header + upto 10 MPLS labels + ip6_hdr + my_tlhdr */ pptrs.mpls6.pkthdr->caplen = 117; pptrs.mpls6.pkthdr->len = 128; /* fake len */ pptrs.mpls6.l3_proto = ETHERTYPE_IPV6; memset(dummy_packet_vlan_mpls6, 0, sizeof(dummy_packet_vlan_mpls6)); pptrs.vlanmpls6.idtable = pptrs.v4.idtable; pptrs.vlanmpls6.f_agent = (u_char *) &client; pptrs.vlanmpls6.packet_ptr = dummy_packet_vlan_mpls6; pptrs.vlanmpls6.pkthdr = &dummy_pkthdr_vlan_mpls6; Assign16(((struct eth_header *)pptrs.vlanmpls6.packet_ptr)->ether_type, htons(ETHERTYPE_8021Q)); pptrs.vlanmpls6.mac_ptr = (u_char *)((struct eth_header *)pptrs.vlanmpls6.packet_ptr)->ether_dhost; pptrs.vlanmpls6.vlan_ptr = pptrs.vlanmpls6.packet_ptr + ETHER_HDRLEN; Assign8(*(pptrs.vlanmpls6.vlan_ptr+2), 0x88); Assign8(*(pptrs.vlanmpls6.vlan_ptr+3), 0x47); pptrs.vlanmpls6.mpls_ptr = pptrs.vlanmpls6.packet_ptr + ETHER_HDRLEN + IEEE8021Q_TAGLEN; // pptrs.vlanmpls6.pkthdr->caplen = 104; /* eth_header + vlan + upto 10 MPLS labels + ip6_hdr + my_tlhdr */ pptrs.vlanmpls6.pkthdr->caplen = 121; pptrs.vlanmpls6.pkthdr->len = 128; /* fake len */ pptrs.vlanmpls6.l3_proto = ETHERTYPE_IPV6; #endif { char srv_string[INET6_ADDRSTRLEN]; struct host_addr srv_addr; u_int16_t srv_port; sa_to_addr(&server, &srv_addr, &srv_port); addr_to_str(srv_string, &srv_addr); Log(LOG_INFO, "INFO ( default/core ): waiting for NetFlow data on %s:%u\n", srv_string, srv_port); allowed = TRUE; } /* Main loop */ for(;;) { ret = recvfrom(config.sock, netflow_packet, NETFLOW_MSG_SIZE, 0, (struct sockaddr *) &client, &clen); if (ret < 1) continue; /* we don't have enough data to decode the version */ pptrs.v4.f_len = ret; /* check if Hosts Allow Table is loaded; if it is, we will enforce rules */ if (allow.num) allowed = check_allow(&allow, (struct sockaddr *)&client); if (!allowed) continue; if (reload_map) { load_networks(config.networks_file, &nt, &nc); if (config.nfacctd_bgp && config.nfacctd_bgp_peer_as_src_map) load_id_file(MAP_BGP_PEER_AS_SRC, config.nfacctd_bgp_peer_as_src_map, &bpas_table, &req, &bpas_map_allocated); if (config.nfacctd_bgp && config.nfacctd_bgp_src_local_pref_map) load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.nfacctd_bgp_src_local_pref_map, &blp_table, &req, &blp_map_allocated); if (config.nfacctd_bgp && config.nfacctd_bgp_src_med_map) load_id_file(MAP_BGP_SRC_MED, config.nfacctd_bgp_src_med_map, &bmed_table, &req, &bmed_map_allocated); if (config.nfacctd_bgp && config.nfacctd_bgp_to_agent_map) load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.nfacctd_bgp_to_agent_map, &bta_table, &req, &bta_map_allocated); if (config.nfacctd_bgp && config.nfacctd_bgp_iface_to_rd_map) load_id_file(MAP_BGP_IFACE_TO_RD, config.nfacctd_bgp_iface_to_rd_map, &bitr_table, &req, &bitr_map_allocated); if (config.pre_tag_map) load_id_file(config.acct_type, config.pre_tag_map, &idt, &req, &tag_map_allocated); if (config.sampling_map) { load_id_file(MAP_SAMPLING, config.sampling_map, &sampling_table, &req, &sampling_map_allocated); set_sampling_table(&pptrs, (u_char *) &sampling_table); } reload_map = FALSE; } if (data_plugins) { /* We will change byte ordering in order to avoid a bunch of ntohs() calls */ ((struct struct_header_v5 *)netflow_packet)->version = ntohs(((struct struct_header_v5 *)netflow_packet)->version); reset_tag_status(&pptrs); reset_shadow_status(&pptrs); switch(((struct struct_header_v5 *)netflow_packet)->version) { case 1: process_v1_packet(netflow_packet, ret, &pptrs.v4, &req); break; case 5: process_v5_packet(netflow_packet, ret, &pptrs.v4, &req); break; case 7: process_v7_packet(netflow_packet, ret, &pptrs.v4, &req); break; case 8: process_v8_packet(netflow_packet, ret, &pptrs.v4, &req); break; /* NetFlow v9 + IPFIX */ case 9: case 10: process_v9_packet(netflow_packet, ret, &pptrs, &req, ((struct struct_header_v5 *)netflow_packet)->version); break; default: notify_malf_packet(LOG_INFO, "INFO: Discarding unknown packet", (struct sockaddr *) pptrs.v4.f_agent); xflow_tot_bad_datagrams++; break; } } else if (tee_plugins) { process_raw_packet(netflow_packet, ret, &pptrs, &req); } } } void process_v1_packet(unsigned char *pkt, u_int16_t len, struct packet_ptrs *pptrs, struct plugin_requests *req) { struct struct_header_v1 *hdr_v1 = (struct struct_header_v1 *)pkt; struct struct_export_v1 *exp_v1; unsigned short int count = ntohs(hdr_v1->count); if (len < NfHdrV1Sz) { notify_malf_packet(LOG_INFO, "INFO: discarding short NetFlow v1 packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } pptrs->f_header = pkt; pkt += NfHdrV1Sz; exp_v1 = (struct struct_export_v1 *)pkt; reset_mac(pptrs); if ((count <= V1_MAXFLOWS) && ((count*NfDataV1Sz)+NfHdrV1Sz == len)) { while (count) { reset_net_status(pptrs); pptrs->f_data = (unsigned char *) exp_v1; if (req->bpf_filter) { Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_src.s_addr, exp_v1->srcaddr.s_addr); Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp_v1->dstaddr.s_addr); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_p, exp_v1->prot); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp_v1->tos); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->src_port, exp_v1->srcport); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port, exp_v1->dstport); } /* Let's copy some relevant field */ pptrs->l4_proto = exp_v1->prot; /* IP header's id field is unused; we will use it to transport our id */ if (config.nfacctd_isis) isis_srcdst_lookup(pptrs); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, pptrs, &pptrs->bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, pptrs, &pptrs->bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(pptrs); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, pptrs, &pptrs->bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, pptrs, &pptrs->blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, pptrs, &pptrs->bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, pptrs, &pptrs->tag, &pptrs->tag2); exec_plugins(pptrs); exp_v1++; count--; } } else { notify_malf_packet(LOG_INFO, "INFO: discarding malformed NetFlow v1 packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } } void process_v5_packet(unsigned char *pkt, u_int16_t len, struct packet_ptrs *pptrs, struct plugin_requests *req) { struct struct_header_v5 *hdr_v5 = (struct struct_header_v5 *)pkt; struct struct_export_v5 *exp_v5; unsigned short int count = ntohs(hdr_v5->count); if (len < NfHdrV5Sz) { notify_malf_packet(LOG_INFO, "INFO: discarding short NetFlow v5 packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } pptrs->f_header = pkt; pkt += NfHdrV5Sz; exp_v5 = (struct struct_export_v5 *)pkt; pptrs->f_status = nfv578_check_status(pptrs); reset_mac(pptrs); if ((count <= V5_MAXFLOWS) && ((count*NfDataV5Sz)+NfHdrV5Sz == len)) { while (count) { reset_net_status(pptrs); pptrs->f_data = (unsigned char *) exp_v5; if (req->bpf_filter) { Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_src.s_addr, exp_v5->srcaddr.s_addr); Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp_v5->dstaddr.s_addr); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_p, exp_v5->prot); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp_v5->tos); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->src_port, exp_v5->srcport); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port, exp_v5->dstport); Assign8(((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags, exp_v5->tcp_flags); } pptrs->lm_mask_src = exp_v5->src_mask; pptrs->lm_mask_dst = exp_v5->dst_mask; pptrs->lm_method_src = NF_NET_KEEP; pptrs->lm_method_dst = NF_NET_KEEP; /* Let's copy some relevant field */ pptrs->l4_proto = exp_v5->prot; /* IP header's id field is unused; we will use it to transport our id */ if (config.nfacctd_isis) isis_srcdst_lookup(pptrs); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, pptrs, &pptrs->bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, pptrs, &pptrs->bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(pptrs); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, pptrs, &pptrs->bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, pptrs, &pptrs->blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, pptrs, &pptrs->bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, pptrs, &pptrs->tag, &pptrs->tag2); exec_plugins(pptrs); exp_v5++; count--; } } else { notify_malf_packet(LOG_INFO, "INFO: discarding malformed NetFlow v5 packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } } void process_v7_packet(unsigned char *pkt, u_int16_t len, struct packet_ptrs *pptrs, struct plugin_requests *req) { struct struct_header_v7 *hdr_v7 = (struct struct_header_v7 *)pkt; struct struct_export_v7 *exp_v7; unsigned short int count = ntohs(hdr_v7->count); if (len < NfHdrV7Sz) { notify_malf_packet(LOG_INFO, "INFO: discarding short NetFlow v7 packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } pptrs->f_header = pkt; pkt += NfHdrV7Sz; exp_v7 = (struct struct_export_v7 *)pkt; pptrs->f_status = nfv578_check_status(pptrs); reset_mac(pptrs); if ((count <= V7_MAXFLOWS) && ((count*NfDataV7Sz)+NfHdrV7Sz == len)) { while (count) { reset_net_status(pptrs); pptrs->f_data = (unsigned char *) exp_v7; if (req->bpf_filter) { Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_src.s_addr, exp_v7->srcaddr); Assign32(((struct my_iphdr *)pptrs->iph_ptr)->ip_dst.s_addr, exp_v7->dstaddr); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_p, exp_v7->prot); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, exp_v7->tos); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->src_port, exp_v7->srcport); Assign16(((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port, exp_v7->dstport); Assign8(((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags, exp_v7->tcp_flags); } pptrs->lm_mask_src = exp_v7->src_mask; pptrs->lm_mask_dst = exp_v7->dst_mask; pptrs->lm_method_src = NF_NET_KEEP; pptrs->lm_method_dst = NF_NET_KEEP; /* Let's copy some relevant field */ pptrs->l4_proto = exp_v7->prot; /* IP header's id field is unused; we will use it to transport our id */ if (config.nfacctd_isis) isis_srcdst_lookup(pptrs); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, pptrs, &pptrs->bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, pptrs, &pptrs->bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(pptrs); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, pptrs, &pptrs->bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, pptrs, &pptrs->blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, pptrs, &pptrs->bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, pptrs, &pptrs->tag, &pptrs->tag2); exec_plugins(pptrs); exp_v7++; count--; } } else { notify_malf_packet(LOG_INFO, "INFO: discarding malformed NetFlow v7 packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } } void process_v8_packet(unsigned char *pkt, u_int16_t len, struct packet_ptrs *pptrs, struct plugin_requests *req) { struct struct_header_v8 *hdr_v8 = (struct struct_header_v8 *)pkt; unsigned char *exp_v8; unsigned short int count = ntohs(hdr_v8->count); if (len < NfHdrV8Sz) { notify_malf_packet(LOG_INFO, "INFO: discarding short NetFlow v8 packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } pptrs->f_header = pkt; pkt += NfHdrV8Sz; exp_v8 = pkt; pptrs->f_status = nfv578_check_status(pptrs); reset_mac(pptrs); reset_ip4(pptrs); if ((count <= v8_handlers[hdr_v8->aggregation].max_flows) && ((count*v8_handlers[hdr_v8->aggregation].exp_size)+NfHdrV8Sz <= len)) { while (count) { reset_net_status(pptrs); pptrs->f_data = exp_v8; if (req->bpf_filter) { /* XXX: nfacctd_net: network masks should be looked up here */ v8_handlers[hdr_v8->aggregation].fh(pptrs, exp_v8); } /* IP header's id field is unused; we will use it to transport our id */ if (config.nfacctd_isis) isis_srcdst_lookup(pptrs); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, pptrs, &pptrs->bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, pptrs, &pptrs->bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(pptrs); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, pptrs, &pptrs->bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, pptrs, &pptrs->blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, pptrs, &pptrs->bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, pptrs, &pptrs->tag, &pptrs->tag2); exec_plugins(pptrs); exp_v8 += v8_handlers[hdr_v8->aggregation].exp_size; count--; } } else { notify_malf_packet(LOG_INFO, "INFO: discarding malformed NetFlow v8 packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } } void process_v9_packet(unsigned char *pkt, u_int16_t len, struct packet_ptrs_vector *pptrsv, struct plugin_requests *req, u_int16_t version) { struct struct_header_v9 *hdr_v9 = (struct struct_header_v9 *)pkt; struct struct_header_ipfix *hdr_v10 = (struct struct_header_ipfix *)pkt; struct template_hdr_v9 *template_hdr; struct options_template_hdr_v9 *opt_template_hdr; struct template_cache_entry *tpl; struct data_hdr_v9 *data_hdr; struct packet_ptrs *pptrs = &pptrsv->v4; u_int16_t fid, off = 0, flowoff, flowsetlen, flow_type, direction, FlowSeqInc = 0; u_int32_t HdrSz = 0, SourceId = 0, FlowSeq = 0; if (version == 9) { HdrSz = NfHdrV9Sz; SourceId = ntohl(hdr_v9->source_id); FlowSeq = ntohl(hdr_v9->flow_sequence); } else if (version == 10) { HdrSz = IpFixHdrSz; SourceId = ntohl(hdr_v10->source_id); FlowSeq = ntohl(hdr_v10->flow_sequence); } if (len < HdrSz) { notify_malf_packet(LOG_INFO, "INFO: discarding short NetFlow v9/IPFIX packet", (struct sockaddr *) pptrsv->v4.f_agent); xflow_tot_bad_datagrams++; return; } pptrs->f_header = pkt; pkt += HdrSz; off += HdrSz; pptrsv->v4.f_status = nfv9_check_status(pptrs, SourceId, FlowSeq); set_vector_f_status(pptrsv); process_flowset: if (off+NfDataHdrV9Sz >= len) { notify_malf_packet(LOG_INFO, "INFO: unable to read next Flowset; incomplete NetFlow v9/IPFIX packet", (struct sockaddr *) pptrsv->v4.f_agent); xflow_tot_bad_datagrams++; return; } data_hdr = (struct data_hdr_v9 *)pkt; fid = ntohs(data_hdr->flow_id); if (fid == 0 || fid == 2) { /* template: 0 NetFlow v9, 2 IPFIX */ unsigned char *tpl_ptr = pkt; u_int16_t pens = 0; flowoff = 0; tpl_ptr += NfDataHdrV9Sz; flowoff += NfDataHdrV9Sz; flowsetlen = ntohs(data_hdr->flow_len); while (flowoff < flowsetlen) { template_hdr = (struct template_hdr_v9 *) tpl_ptr; if (off+flowsetlen > len) { notify_malf_packet(LOG_INFO, "INFO: unable to read next Template Flowset; incomplete NetFlow v9/IPFIX packet", (struct sockaddr *) pptrsv->v4.f_agent); xflow_tot_bad_datagrams++; return; } handle_template_v9(template_hdr, pptrs, fid, SourceId, &pens); tpl_ptr += sizeof(struct template_hdr_v9)+(ntohs(template_hdr->num)*sizeof(struct template_field_v9))+(pens*sizeof(u_int32_t)); flowoff += sizeof(struct template_hdr_v9)+(ntohs(template_hdr->num)*sizeof(struct template_field_v9))+(pens*sizeof(u_int32_t)); } pkt += flowsetlen; off += flowsetlen; } else if (fid == 1 || fid == 3) { /* options template: 1 NetFlow v9, 3 IPFIX */ unsigned char *tpl_ptr = pkt; flowoff = 0; tpl_ptr += NfDataHdrV9Sz; flowoff += NfDataHdrV9Sz; flowsetlen = ntohs(data_hdr->flow_len); while (flowoff < flowsetlen) { opt_template_hdr = (struct options_template_hdr_v9 *) tpl_ptr; if (off+flowsetlen > len) { notify_malf_packet(LOG_INFO, "INFO: unable to read next Options Template Flowset; incomplete NetFlow v9/IPFIX packet", (struct sockaddr *) pptrsv->v4.f_agent); xflow_tot_bad_datagrams++; return; } handle_template_v9((struct template_hdr_v9 *)opt_template_hdr, pptrs, fid, SourceId, NULL); /* Increment is not precise for NetFlow v9 but will work */ tpl_ptr += sizeof(struct options_template_hdr_v9)+((ntohs(opt_template_hdr->scope_len)+ntohs(opt_template_hdr->option_len))*sizeof(struct template_field_v9)); flowoff += sizeof(struct options_template_hdr_v9)+((ntohs(opt_template_hdr->scope_len)+ntohs(opt_template_hdr->option_len))*sizeof(struct template_field_v9)); } pkt += flowsetlen; off += flowsetlen; } else if (fid >= 256) { /* data */ flowsetlen = ntohs(data_hdr->flow_len); if (off+flowsetlen > len) { notify_malf_packet(LOG_INFO, "INFO: unable to read next Data Flowset (incomplete NetFlow v9/IPFIX packet)", (struct sockaddr *) pptrsv->v4.f_agent); xflow_tot_bad_datagrams++; return; } flowoff = 0; pkt += NfDataHdrV9Sz; flowoff += NfDataHdrV9Sz; tpl = find_template_v9(data_hdr->flow_id, pptrs, fid, SourceId); if (!tpl) { struct host_addr a; u_char agent_addr[50]; u_int16_t agent_port; sa_to_addr((struct sockaddr *)pptrs->f_agent, &a, &agent_port); addr_to_str(agent_addr, &a); Log(LOG_DEBUG, "DEBUG ( default/core ): Discarded NetFlow v9/IPFIX packet (R: unknown template %u [%s:%u])\n", fid, agent_addr, SourceId); pkt += flowsetlen-NfDataHdrV9Sz; off += flowsetlen; } else if (tpl->template_type == 1) { /* Options coming */ struct xflow_status_entry *entry; struct xflow_status_entry_sampling *sentry, *ssaved; struct xflow_status_entry_class *centry, *csaved; while (flowoff+tpl->len <= flowsetlen) { entry = (struct xflow_status_entry *) pptrs->f_status; sentry = NULL, ssaved = NULL; centry = NULL, csaved = NULL; /* Is this option about sampling? */ if (tpl->tpl[NF9_FLOW_SAMPLER_ID].len || tpl->tpl[NF9_SAMPLING_INTERVAL].len == 4) { u_int8_t t8 = 0; u_int16_t sampler_id = 0, t16 = 0; if (tpl->tpl[NF9_FLOW_SAMPLER_ID].len == 1) { memcpy(&t8, pkt+tpl->tpl[NF9_FLOW_SAMPLER_ID].off, 1); sampler_id = t8; } else if (tpl->tpl[NF9_FLOW_SAMPLER_ID].len == 2) { memcpy(&sampler_id, pkt+tpl->tpl[NF9_FLOW_SAMPLER_ID].off, 2); sampler_id = ntohs(t16); } if (entry) sentry = search_smp_id_status_table(entry->sampling, sampler_id, FALSE); if (!sentry) sentry = create_smp_entry_status_table(entry); else ssaved = sentry->next; if (sentry) { memset(sentry, 0, sizeof(struct xflow_status_entry_sampling)); if (tpl->tpl[NF9_SAMPLING_INTERVAL].len == 4) memcpy(&sentry->sample_pool, pkt+tpl->tpl[NF9_SAMPLING_INTERVAL].off, 4); if (tpl->tpl[NF9_FLOW_SAMPLER_INTERVAL].len == 4) memcpy(&sentry->sample_pool, pkt+tpl->tpl[NF9_FLOW_SAMPLER_INTERVAL].off, 4); sentry->sampler_id = sampler_id; sentry->sample_pool = ntohl(sentry->sample_pool); if (ssaved) sentry->next = ssaved; } } else if (tpl->tpl[NF9_APPLICATION_ID].len == 4) { struct pkt_classifier css; pm_class_t class_id = 0, class_int_id = 0; memcpy(&class_id, pkt+tpl->tpl[NF9_APPLICATION_ID].off, 4); if (entry) centry = search_class_id_status_table(entry->class, class_id); if (!centry) { centry = create_class_entry_status_table(entry); class_int_id = pmct_find_first_free(); } else { csaved = centry->next; class_int_id = centry->class_int_id; pmct_unregister(centry->class_int_id); } if (centry) { memset(centry, 0, sizeof(struct xflow_status_entry_class)); memset(&css, 0, sizeof(struct pkt_classifier)); if (tpl->tpl[NF9_APPLICATION_NAME].len > 0) memcpy(¢ry->class_name, pkt+tpl->tpl[NF9_APPLICATION_NAME].off, MIN((MAX_PROTOCOL_LEN-1), tpl->tpl[NF9_APPLICATION_NAME].len)); centry->class_id = class_id; centry->class_int_id = class_int_id; if (csaved) centry->next = csaved; css.id = centry->class_int_id; strlcpy(css.protocol, centry->class_name, MAX_PROTOCOL_LEN); pmct_register(&css); } } pkt += tpl->len; flowoff += tpl->len; FlowSeqInc++; } pkt += flowsetlen-flowoff; /* handling padding */ off += flowsetlen; } else { while (flowoff+tpl->len <= flowsetlen) { pptrs->f_data = pkt; pptrs->f_tpl = (u_char *) tpl; reset_net_status_v(pptrsv); flow_type = NF_evaluate_flow_type(tpl, pptrs); direction = NF_evaluate_direction(tpl, pptrs); /* we need to understand the IP protocol version in order to build the fake packet */ switch (flow_type) { case NF9_FTYPE_IPV4: if (req->bpf_filter) { reset_mac(pptrs); reset_ip4(pptrs); if (direction == DIRECTION_IN) { memcpy(pptrs->mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_IN_SRC_MAC].off, tpl->tpl[NF9_IN_SRC_MAC].len); memcpy(pptrs->mac_ptr, pkt+tpl->tpl[NF9_IN_DST_MAC].off, tpl->tpl[NF9_IN_DST_MAC].len); } else if (direction == DIRECTION_OUT) { memcpy(pptrs->mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_OUT_SRC_MAC].off, tpl->tpl[NF9_OUT_SRC_MAC].len); memcpy(pptrs->mac_ptr, pkt+tpl->tpl[NF9_OUT_DST_MAC].off, tpl->tpl[NF9_OUT_DST_MAC].len); } ((struct my_iphdr *)pptrs->iph_ptr)->ip_vhl = 0x45; memcpy(&((struct my_iphdr *)pptrs->iph_ptr)->ip_src, pkt+tpl->tpl[NF9_IPV4_SRC_ADDR].off, tpl->tpl[NF9_IPV4_SRC_ADDR].len); memcpy(&((struct my_iphdr *)pptrs->iph_ptr)->ip_dst, pkt+tpl->tpl[NF9_IPV4_DST_ADDR].off, tpl->tpl[NF9_IPV4_DST_ADDR].len); memcpy(&((struct my_iphdr *)pptrs->iph_ptr)->ip_p, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); memcpy(&((struct my_iphdr *)pptrs->iph_ptr)->ip_tos, pkt+tpl->tpl[NF9_SRC_TOS].off, tpl->tpl[NF9_SRC_TOS].len); memcpy(&((struct my_tlhdr *)pptrs->tlh_ptr)->src_port, pkt+tpl->tpl[NF9_L4_SRC_PORT].off, tpl->tpl[NF9_L4_SRC_PORT].len); memcpy(&((struct my_tlhdr *)pptrs->tlh_ptr)->dst_port, pkt+tpl->tpl[NF9_L4_DST_PORT].off, tpl->tpl[NF9_L4_DST_PORT].len); memcpy(&((struct my_tcphdr *)pptrs->tlh_ptr)->th_flags, pkt+tpl->tpl[NF9_TCP_FLAGS].off, tpl->tpl[NF9_TCP_FLAGS].len); } memcpy(&pptrs->lm_mask_src, pkt+tpl->tpl[NF9_SRC_MASK].off, tpl->tpl[NF9_SRC_MASK].len); memcpy(&pptrs->lm_mask_dst, pkt+tpl->tpl[NF9_DST_MASK].off, tpl->tpl[NF9_DST_MASK].len); pptrs->lm_method_src = NF_NET_KEEP; pptrs->lm_method_dst = NF_NET_KEEP; /* Let's copy some relevant field */ pptrs->l4_proto = 0; memcpy(&pptrs->l4_proto, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); if (tpl->tpl[NF9_APPLICATION_ID].len == 4) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrs->f_status; if (entry) pptrs->class = NF_evaluate_classifiers(entry->class, pptrs->f_data+tpl->tpl[NF9_APPLICATION_ID].off); } if (config.nfacctd_isis) isis_srcdst_lookup(pptrs); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, pptrs, &pptrs->bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, pptrs, &pptrs->bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(pptrs); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, pptrs, &pptrs->bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, pptrs, &pptrs->blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, pptrs, &pptrs->bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, pptrs, &pptrs->tag, &pptrs->tag2); exec_plugins(pptrs); break; #if defined ENABLE_IPV6 case NF9_FTYPE_IPV6: pptrsv->v6.f_header = pptrs->f_header; pptrsv->v6.f_data = pptrs->f_data; pptrsv->v6.f_tpl = pptrs->f_tpl; if (req->bpf_filter) { reset_mac(&pptrsv->v6); reset_ip6(&pptrsv->v6); if (direction == DIRECTION_IN) { memcpy(pptrsv->v6.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_IN_SRC_MAC].off, tpl->tpl[NF9_IN_SRC_MAC].len); memcpy(pptrsv->v6.mac_ptr, pkt+tpl->tpl[NF9_IN_DST_MAC].off, tpl->tpl[NF9_IN_DST_MAC].len); } else if (direction == DIRECTION_OUT) { memcpy(pptrsv->v6.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_OUT_SRC_MAC].off, tpl->tpl[NF9_OUT_SRC_MAC].len); memcpy(pptrsv->v6.mac_ptr, pkt+tpl->tpl[NF9_OUT_DST_MAC].off, tpl->tpl[NF9_OUT_DST_MAC].len); } ((struct ip6_hdr *)pptrsv->v6.iph_ptr)->ip6_ctlun.ip6_un2_vfc = 0x60; memcpy(&((struct ip6_hdr *)pptrsv->v6.iph_ptr)->ip6_src, pkt+tpl->tpl[NF9_IPV6_SRC_ADDR].off, tpl->tpl[NF9_IPV6_SRC_ADDR].len); memcpy(&((struct ip6_hdr *)pptrsv->v6.iph_ptr)->ip6_dst, pkt+tpl->tpl[NF9_IPV6_DST_ADDR].off, tpl->tpl[NF9_IPV6_DST_ADDR].len); memcpy(&((struct ip6_hdr *)pptrsv->v6.iph_ptr)->ip6_nxt, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); /* XXX: class ID ? */ memcpy(&((struct my_tlhdr *)pptrsv->v6.tlh_ptr)->src_port, pkt+tpl->tpl[NF9_L4_SRC_PORT].off, tpl->tpl[NF9_L4_SRC_PORT].len); memcpy(&((struct my_tlhdr *)pptrsv->v6.tlh_ptr)->dst_port, pkt+tpl->tpl[NF9_L4_DST_PORT].off, tpl->tpl[NF9_L4_DST_PORT].len); memcpy(&((struct my_tcphdr *)pptrsv->v6.tlh_ptr)->th_flags, pkt+tpl->tpl[NF9_TCP_FLAGS].off, tpl->tpl[NF9_TCP_FLAGS].len); } memcpy(&pptrsv->v6.lm_mask_src, pkt+tpl->tpl[NF9_SRC_MASK].off, tpl->tpl[NF9_SRC_MASK].len); memcpy(&pptrsv->v6.lm_mask_dst, pkt+tpl->tpl[NF9_DST_MASK].off, tpl->tpl[NF9_DST_MASK].len); pptrsv->v6.lm_method_src = NF_NET_KEEP; pptrsv->v6.lm_method_dst = NF_NET_KEEP; /* Let's copy some relevant field */ pptrsv->v6.l4_proto = 0; memcpy(&pptrsv->v6.l4_proto, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); if (tpl->tpl[NF9_APPLICATION_ID].len == 4) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrs->f_status; if (entry) pptrsv->v6.class = NF_evaluate_classifiers(entry->class, pptrsv->v6.f_data+tpl->tpl[NF9_APPLICATION_ID].off); } if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->v6); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->v6, &pptrsv->v6.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->v6, &pptrsv->v6.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->v6); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->v6, &pptrsv->v6.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->v6, &pptrsv->v6.blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->v6, &pptrsv->v6.bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, &pptrsv->v6, &pptrsv->v6.tag, &pptrsv->v6.tag2); exec_plugins(&pptrsv->v6); break; #endif case NF9_FTYPE_VLAN_IPV4: pptrsv->vlan4.f_header = pptrs->f_header; pptrsv->vlan4.f_data = pptrs->f_data; pptrsv->vlan4.f_tpl = pptrs->f_tpl; if (req->bpf_filter) { reset_mac_vlan(&pptrsv->vlan4); reset_ip4(&pptrsv->vlan4); if (direction == DIRECTION_IN) { memcpy(pptrsv->vlan4.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_IN_SRC_MAC].off, tpl->tpl[NF9_IN_SRC_MAC].len); memcpy(pptrsv->vlan4.mac_ptr, pkt+tpl->tpl[NF9_IN_DST_MAC].off, tpl->tpl[NF9_IN_DST_MAC].len); memcpy(pptrsv->vlan4.vlan_ptr, pkt+tpl->tpl[NF9_IN_VLAN].off, tpl->tpl[NF9_IN_VLAN].len); } else if (direction == DIRECTION_OUT) { memcpy(pptrsv->vlan4.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_OUT_SRC_MAC].off, tpl->tpl[NF9_OUT_SRC_MAC].len); memcpy(pptrsv->vlan4.mac_ptr, pkt+tpl->tpl[NF9_OUT_DST_MAC].off, tpl->tpl[NF9_OUT_DST_MAC].len); memcpy(pptrsv->vlan4.vlan_ptr, pkt+tpl->tpl[NF9_OUT_VLAN].off, tpl->tpl[NF9_OUT_VLAN].len); } ((struct my_iphdr *)pptrsv->vlan4.iph_ptr)->ip_vhl = 0x45; memcpy(&((struct my_iphdr *)pptrsv->vlan4.iph_ptr)->ip_src, pkt+tpl->tpl[NF9_IPV4_SRC_ADDR].off, tpl->tpl[NF9_IPV4_SRC_ADDR].len); memcpy(&((struct my_iphdr *)pptrsv->vlan4.iph_ptr)->ip_dst, pkt+tpl->tpl[NF9_IPV4_DST_ADDR].off, tpl->tpl[NF9_IPV4_DST_ADDR].len); memcpy(&((struct my_iphdr *)pptrsv->vlan4.iph_ptr)->ip_p, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); memcpy(&((struct my_iphdr *)pptrsv->vlan4.iph_ptr)->ip_tos, pkt+tpl->tpl[NF9_SRC_TOS].off, tpl->tpl[NF9_SRC_TOS].len); memcpy(&((struct my_tlhdr *)pptrsv->vlan4.tlh_ptr)->src_port, pkt+tpl->tpl[NF9_L4_SRC_PORT].off, tpl->tpl[NF9_L4_SRC_PORT].len); memcpy(&((struct my_tlhdr *)pptrsv->vlan4.tlh_ptr)->dst_port, pkt+tpl->tpl[NF9_L4_DST_PORT].off, tpl->tpl[NF9_L4_DST_PORT].len); memcpy(&((struct my_tcphdr *)pptrsv->vlan4.tlh_ptr)->th_flags, pkt+tpl->tpl[NF9_TCP_FLAGS].off, tpl->tpl[NF9_TCP_FLAGS].len); } memcpy(&pptrsv->vlan4.lm_mask_src, pkt+tpl->tpl[NF9_SRC_MASK].off, tpl->tpl[NF9_SRC_MASK].len); memcpy(&pptrsv->vlan4.lm_mask_dst, pkt+tpl->tpl[NF9_DST_MASK].off, tpl->tpl[NF9_DST_MASK].len); pptrsv->vlan4.lm_method_src = NF_NET_KEEP; pptrsv->vlan4.lm_method_dst = NF_NET_KEEP; /* Let's copy some relevant field */ pptrsv->vlan4.l4_proto = 0; memcpy(&pptrsv->vlan4.l4_proto, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); if (tpl->tpl[NF9_APPLICATION_ID].len == 4) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrs->f_status; if (entry) pptrsv->vlan4.class = NF_evaluate_classifiers(entry->class, pptrsv->vlan4.f_data+tpl->tpl[NF9_APPLICATION_ID].off); } if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->vlan4); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->vlan4, &pptrsv->vlan4.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->vlan4, &pptrsv->vlan4.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->vlan4); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->vlan4, &pptrsv->vlan4.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->vlan4, &pptrsv->vlan4.blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->vlan4, &pptrsv->vlan4.bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, &pptrsv->vlan4, &pptrsv->vlan4.tag, &pptrsv->vlan4.tag2); exec_plugins(&pptrsv->vlan4); break; #if defined ENABLE_IPV6 case NF9_FTYPE_VLAN_IPV6: pptrsv->vlan6.f_header = pptrs->f_header; pptrsv->vlan6.f_data = pptrs->f_data; pptrsv->vlan6.f_tpl = pptrs->f_tpl; if (req->bpf_filter) { reset_mac_vlan(&pptrsv->vlan6); reset_ip6(&pptrsv->vlan6); if (direction == DIRECTION_IN) { memcpy(pptrsv->vlan6.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_IN_SRC_MAC].off, tpl->tpl[NF9_IN_SRC_MAC].len); memcpy(pptrsv->vlan6.mac_ptr, pkt+tpl->tpl[NF9_IN_DST_MAC].off, tpl->tpl[NF9_IN_DST_MAC].len); memcpy(pptrsv->vlan6.vlan_ptr, pkt+tpl->tpl[NF9_IN_VLAN].off, tpl->tpl[NF9_IN_VLAN].len); } else if (direction == DIRECTION_OUT) { memcpy(pptrsv->vlan6.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_OUT_SRC_MAC].off, tpl->tpl[NF9_OUT_SRC_MAC].len); memcpy(pptrsv->vlan6.mac_ptr, pkt+tpl->tpl[NF9_OUT_DST_MAC].off, tpl->tpl[NF9_OUT_DST_MAC].len); memcpy(pptrsv->vlan6.vlan_ptr, pkt+tpl->tpl[NF9_OUT_VLAN].off, tpl->tpl[NF9_OUT_VLAN].len); } ((struct ip6_hdr *)pptrsv->vlan6.iph_ptr)->ip6_ctlun.ip6_un2_vfc = 0x60; memcpy(&((struct ip6_hdr *)pptrsv->vlan6.iph_ptr)->ip6_src, pkt+tpl->tpl[NF9_IPV6_SRC_ADDR].off, tpl->tpl[NF9_IPV6_SRC_ADDR].len); memcpy(&((struct ip6_hdr *)pptrsv->vlan6.iph_ptr)->ip6_dst, pkt+tpl->tpl[NF9_IPV6_DST_ADDR].off, tpl->tpl[NF9_IPV6_DST_ADDR].len); memcpy(&((struct ip6_hdr *)pptrsv->vlan6.iph_ptr)->ip6_nxt, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); /* XXX: class ID ? */ memcpy(&((struct my_tlhdr *)pptrsv->vlan6.tlh_ptr)->src_port, pkt+tpl->tpl[NF9_L4_SRC_PORT].off, tpl->tpl[NF9_L4_SRC_PORT].len); memcpy(&((struct my_tlhdr *)pptrsv->vlan6.tlh_ptr)->dst_port, pkt+tpl->tpl[NF9_L4_DST_PORT].off, tpl->tpl[NF9_L4_DST_PORT].len); memcpy(&((struct my_tcphdr *)pptrsv->vlan6.tlh_ptr)->th_flags, pkt+tpl->tpl[NF9_TCP_FLAGS].off, tpl->tpl[NF9_TCP_FLAGS].len); } memcpy(&pptrsv->vlan6.lm_mask_src, pkt+tpl->tpl[NF9_SRC_MASK].off, tpl->tpl[NF9_SRC_MASK].len); memcpy(&pptrsv->vlan6.lm_mask_dst, pkt+tpl->tpl[NF9_DST_MASK].off, tpl->tpl[NF9_DST_MASK].len); pptrsv->vlan6.lm_method_src = NF_NET_KEEP; pptrsv->vlan6.lm_method_dst = NF_NET_KEEP; /* Let's copy some relevant field */ pptrsv->vlan6.l4_proto = 0; memcpy(&pptrsv->vlan6.l4_proto, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); if (tpl->tpl[NF9_APPLICATION_ID].len == 4) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrs->f_status; if (entry) pptrsv->vlan6.class = NF_evaluate_classifiers(entry->class, pptrsv->vlan6.f_data+tpl->tpl[NF9_APPLICATION_ID].off); } if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->vlan6); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->vlan6, &pptrsv->vlan6.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->vlan6, &pptrsv->vlan6.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->vlan6); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->vlan6, &pptrsv->vlan6.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->vlan6, &pptrsv->vlan6.blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->vlan6, &pptrsv->vlan6.bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, &pptrsv->vlan6, &pptrsv->vlan6.tag, &pptrsv->vlan6.tag2); exec_plugins(&pptrsv->vlan6); break; #endif case NF9_FTYPE_MPLS_IPV4: pptrsv->mpls4.f_header = pptrs->f_header; pptrsv->mpls4.f_data = pptrs->f_data; pptrsv->mpls4.f_tpl = pptrs->f_tpl; if (req->bpf_filter) { u_char *ptr = pptrsv->mpls4.mpls_ptr; u_int32_t idx; /* XXX: fix caplen */ reset_mac(&pptrsv->mpls4); if (direction == DIRECTION_IN) { memcpy(pptrsv->mpls4.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_IN_SRC_MAC].off, tpl->tpl[NF9_IN_SRC_MAC].len); memcpy(pptrsv->mpls4.mac_ptr, pkt+tpl->tpl[NF9_IN_DST_MAC].off, tpl->tpl[NF9_IN_DST_MAC].len); } else if (direction == DIRECTION_OUT) { memcpy(pptrsv->mpls4.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_OUT_SRC_MAC].off, tpl->tpl[NF9_OUT_SRC_MAC].len); memcpy(pptrsv->mpls4.mac_ptr, pkt+tpl->tpl[NF9_OUT_DST_MAC].off, tpl->tpl[NF9_OUT_DST_MAC].len); } for (idx = NF9_MPLS_LABEL_1; idx <= NF9_MPLS_LABEL_10 && tpl->tpl[idx].len; idx++, ptr += 4) { memset(ptr, 0, 4); memcpy(ptr, pkt+tpl->tpl[idx].off, tpl->tpl[idx].len); } stick_bosbit(ptr-4); pptrsv->mpls4.iph_ptr = ptr; pptrsv->mpls4.tlh_ptr = ptr + IP4HdrSz; reset_ip4(&pptrsv->mpls4); ((struct my_iphdr *)pptrsv->mpls4.iph_ptr)->ip_vhl = 0x45; memcpy(&((struct my_iphdr *)pptrsv->mpls4.iph_ptr)->ip_src, pkt+tpl->tpl[NF9_IPV4_SRC_ADDR].off, tpl->tpl[NF9_IPV4_SRC_ADDR].len); memcpy(&((struct my_iphdr *)pptrsv->mpls4.iph_ptr)->ip_dst, pkt+tpl->tpl[NF9_IPV4_DST_ADDR].off, tpl->tpl[NF9_IPV4_DST_ADDR].len); memcpy(&((struct my_iphdr *)pptrsv->mpls4.iph_ptr)->ip_p, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); memcpy(&((struct my_iphdr *)pptrsv->mpls4.iph_ptr)->ip_tos, pkt+tpl->tpl[NF9_SRC_TOS].off, tpl->tpl[NF9_SRC_TOS].len); memcpy(&((struct my_tlhdr *)pptrsv->mpls4.tlh_ptr)->src_port, pkt+tpl->tpl[NF9_L4_SRC_PORT].off, tpl->tpl[NF9_L4_SRC_PORT].len); memcpy(&((struct my_tlhdr *)pptrsv->mpls4.tlh_ptr)->dst_port, pkt+tpl->tpl[NF9_L4_DST_PORT].off, tpl->tpl[NF9_L4_DST_PORT].len); memcpy(&((struct my_tcphdr *)pptrsv->mpls4.tlh_ptr)->th_flags, pkt+tpl->tpl[NF9_TCP_FLAGS].off, tpl->tpl[NF9_TCP_FLAGS].len); } memcpy(&pptrsv->mpls4.lm_mask_src, pkt+tpl->tpl[NF9_SRC_MASK].off, tpl->tpl[NF9_SRC_MASK].len); memcpy(&pptrsv->mpls4.lm_mask_dst, pkt+tpl->tpl[NF9_DST_MASK].off, tpl->tpl[NF9_DST_MASK].len); pptrsv->mpls4.lm_method_src = NF_NET_KEEP; pptrsv->mpls4.lm_method_dst = NF_NET_KEEP; /* Let's copy some relevant field */ pptrsv->mpls4.l4_proto = 0; memcpy(&pptrsv->mpls4.l4_proto, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); if (tpl->tpl[NF9_APPLICATION_ID].len == 4) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrs->f_status; if (entry) pptrsv->mpls4.class = NF_evaluate_classifiers(entry->class, pptrsv->mpls4.f_data+tpl->tpl[NF9_APPLICATION_ID].off); } if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->mpls4); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->mpls4, &pptrsv->mpls4.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->mpls4, &pptrsv->mpls4.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->mpls4); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->mpls4, &pptrsv->mpls4.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->mpls4, &pptrsv->mpls4.blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->mpls4, &pptrsv->mpls4.bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, &pptrsv->mpls4, &pptrsv->mpls4.tag, &pptrsv->mpls4.tag2); exec_plugins(&pptrsv->mpls4); break; #if defined ENABLE_IPV6 case NF9_FTYPE_MPLS_IPV6: pptrsv->mpls6.f_header = pptrs->f_header; pptrsv->mpls6.f_data = pptrs->f_data; pptrsv->mpls6.f_tpl = pptrs->f_tpl; if (req->bpf_filter) { u_char *ptr = pptrsv->mpls6.mpls_ptr; u_int32_t idx; /* XXX: fix caplen */ reset_mac(&pptrsv->mpls6); if (direction == DIRECTION_IN) { memcpy(pptrsv->mpls6.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_IN_SRC_MAC].off, tpl->tpl[NF9_IN_SRC_MAC].len); memcpy(pptrsv->mpls6.mac_ptr, pkt+tpl->tpl[NF9_IN_DST_MAC].off, tpl->tpl[NF9_IN_DST_MAC].len); } else if (direction == DIRECTION_OUT) { memcpy(pptrsv->mpls6.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_OUT_SRC_MAC].off, tpl->tpl[NF9_OUT_SRC_MAC].len); memcpy(pptrsv->mpls6.mac_ptr, pkt+tpl->tpl[NF9_OUT_DST_MAC].off, tpl->tpl[NF9_OUT_DST_MAC].len); } for (idx = NF9_MPLS_LABEL_1; idx <= NF9_MPLS_LABEL_10 && tpl->tpl[idx].len; idx++, ptr += 4) { memset(ptr, 0, 4); memcpy(ptr, pkt+tpl->tpl[idx].off, tpl->tpl[idx].len); } stick_bosbit(ptr-4); pptrsv->mpls6.iph_ptr = ptr; pptrsv->mpls6.tlh_ptr = ptr + IP6HdrSz; reset_ip6(&pptrsv->mpls6); ((struct ip6_hdr *)pptrsv->mpls6.iph_ptr)->ip6_ctlun.ip6_un2_vfc = 0x60; memcpy(&((struct ip6_hdr *)pptrsv->mpls6.iph_ptr)->ip6_src, pkt+tpl->tpl[NF9_IPV6_SRC_ADDR].off, tpl->tpl[NF9_IPV6_SRC_ADDR].len); memcpy(&((struct ip6_hdr *)pptrsv->mpls6.iph_ptr)->ip6_dst, pkt+tpl->tpl[NF9_IPV6_DST_ADDR].off, tpl->tpl[NF9_IPV6_DST_ADDR].len); memcpy(&((struct ip6_hdr *)pptrsv->mpls6.iph_ptr)->ip6_nxt, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); /* XXX: class ID ? */ memcpy(&((struct my_tlhdr *)pptrsv->mpls6.tlh_ptr)->src_port, pkt+tpl->tpl[NF9_L4_SRC_PORT].off, tpl->tpl[NF9_L4_SRC_PORT].len); memcpy(&((struct my_tlhdr *)pptrsv->mpls6.tlh_ptr)->dst_port, pkt+tpl->tpl[NF9_L4_DST_PORT].off, tpl->tpl[NF9_L4_DST_PORT].len); memcpy(&((struct my_tcphdr *)pptrsv->mpls6.tlh_ptr)->th_flags, pkt+tpl->tpl[NF9_TCP_FLAGS].off, tpl->tpl[NF9_TCP_FLAGS].len); } memcpy(&pptrsv->mpls6.lm_mask_src, pkt+tpl->tpl[NF9_SRC_MASK].off, tpl->tpl[NF9_SRC_MASK].len); memcpy(&pptrsv->mpls6.lm_mask_dst, pkt+tpl->tpl[NF9_DST_MASK].off, tpl->tpl[NF9_DST_MASK].len); pptrsv->mpls6.lm_method_src = NF_NET_KEEP; pptrsv->mpls6.lm_method_dst = NF_NET_KEEP; /* Let's copy some relevant field */ pptrsv->mpls6.l4_proto = 0; memcpy(&pptrsv->mpls6.l4_proto, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); if (tpl->tpl[NF9_APPLICATION_ID].len == 4) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrs->f_status; if (entry) pptrsv->mpls6.class = NF_evaluate_classifiers(entry->class, pptrsv->mpls6.f_data+tpl->tpl[NF9_APPLICATION_ID].off); } if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->mpls6); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->mpls6, &pptrsv->mpls6.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->mpls6, &pptrsv->mpls6.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->mpls6); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->mpls6, &pptrsv->mpls6.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->mpls6, &pptrsv->mpls6.blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->mpls6, &pptrsv->mpls6.bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, &pptrsv->mpls6, &pptrsv->mpls6.tag, &pptrsv->mpls6.tag2); exec_plugins(&pptrsv->mpls6); break; #endif case NF9_FTYPE_VLAN_MPLS_IPV4: pptrsv->vlanmpls4.f_header = pptrs->f_header; pptrsv->vlanmpls4.f_data = pptrs->f_data; pptrsv->vlanmpls4.f_tpl = pptrs->f_tpl; if (req->bpf_filter) { u_char *ptr = pptrsv->vlanmpls4.mpls_ptr; u_int32_t idx; /* XXX: fix caplen */ reset_mac_vlan(&pptrsv->vlanmpls4); if (direction == DIRECTION_IN) { memcpy(pptrsv->vlanmpls4.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_IN_SRC_MAC].off, tpl->tpl[NF9_IN_SRC_MAC].len); memcpy(pptrsv->vlanmpls4.mac_ptr, pkt+tpl->tpl[NF9_IN_DST_MAC].off, tpl->tpl[NF9_IN_DST_MAC].len); memcpy(pptrsv->vlanmpls4.vlan_ptr, pkt+tpl->tpl[NF9_IN_VLAN].off, tpl->tpl[NF9_IN_VLAN].len); } else if (direction == DIRECTION_OUT) { memcpy(pptrsv->vlanmpls4.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_OUT_SRC_MAC].off, tpl->tpl[NF9_OUT_SRC_MAC].len); memcpy(pptrsv->vlanmpls4.mac_ptr, pkt+tpl->tpl[NF9_OUT_DST_MAC].off, tpl->tpl[NF9_OUT_DST_MAC].len); memcpy(pptrsv->vlanmpls4.vlan_ptr, pkt+tpl->tpl[NF9_OUT_VLAN].off, tpl->tpl[NF9_OUT_VLAN].len); } for (idx = NF9_MPLS_LABEL_1; idx <= NF9_MPLS_LABEL_10 && tpl->tpl[idx].len; idx++, ptr += 4) { memset(ptr, 0, 4); memcpy(ptr, pkt+tpl->tpl[idx].off, tpl->tpl[idx].len); } stick_bosbit(ptr-4); pptrsv->vlanmpls4.iph_ptr = ptr; pptrsv->vlanmpls4.tlh_ptr = ptr + IP4HdrSz; reset_ip4(&pptrsv->vlanmpls4); ((struct my_iphdr *)pptrsv->vlanmpls4.iph_ptr)->ip_vhl = 0x45; memcpy(&((struct my_iphdr *)pptrsv->vlanmpls4.iph_ptr)->ip_src, pkt+tpl->tpl[NF9_IPV4_SRC_ADDR].off, tpl->tpl[NF9_IPV4_SRC_ADDR].len); memcpy(&((struct my_iphdr *)pptrsv->vlanmpls4.iph_ptr)->ip_dst, pkt+tpl->tpl[NF9_IPV4_DST_ADDR].off, tpl->tpl[NF9_IPV4_DST_ADDR].len); memcpy(&((struct my_iphdr *)pptrsv->vlanmpls4.iph_ptr)->ip_p, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); memcpy(&((struct my_iphdr *)pptrsv->vlanmpls4.iph_ptr)->ip_tos, pkt+tpl->tpl[NF9_SRC_TOS].off, tpl->tpl[NF9_SRC_TOS].len); memcpy(&((struct my_tlhdr *)pptrsv->vlanmpls4.tlh_ptr)->src_port, pkt+tpl->tpl[NF9_L4_SRC_PORT].off, tpl->tpl[NF9_L4_SRC_PORT].len); memcpy(&((struct my_tlhdr *)pptrsv->vlanmpls4.tlh_ptr)->dst_port, pkt+tpl->tpl[NF9_L4_DST_PORT].off, tpl->tpl[NF9_L4_DST_PORT].len); memcpy(&((struct my_tcphdr *)pptrsv->vlanmpls4.tlh_ptr)->th_flags, pkt+tpl->tpl[NF9_TCP_FLAGS].off, tpl->tpl[NF9_TCP_FLAGS].len); } memcpy(&pptrsv->vlanmpls4.lm_mask_src, pkt+tpl->tpl[NF9_SRC_MASK].off, tpl->tpl[NF9_SRC_MASK].len); memcpy(&pptrsv->vlanmpls4.lm_mask_dst, pkt+tpl->tpl[NF9_DST_MASK].off, tpl->tpl[NF9_DST_MASK].len); pptrsv->vlanmpls4.lm_method_src = NF_NET_KEEP; pptrsv->vlanmpls4.lm_method_dst = NF_NET_KEEP; /* Let's copy some relevant field */ pptrsv->vlanmpls4.l4_proto = 0; memcpy(&pptrsv->vlanmpls4.l4_proto, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); if (tpl->tpl[NF9_APPLICATION_ID].len == 4) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrs->f_status; if (entry) pptrsv->vlanmpls4.class = NF_evaluate_classifiers(entry->class, pptrsv->vlanmpls4.f_data+tpl->tpl[NF9_APPLICATION_ID].off); } if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->vlanmpls4); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->vlanmpls4); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, &pptrsv->vlanmpls4, &pptrsv->vlanmpls4.tag, &pptrsv->vlanmpls4.tag2); exec_plugins(&pptrsv->vlanmpls4); break; #if defined ENABLE_IPV6 case NF9_FTYPE_VLAN_MPLS_IPV6: pptrsv->vlanmpls6.f_header = pptrs->f_header; pptrsv->vlanmpls6.f_data = pptrs->f_data; pptrsv->vlanmpls6.f_tpl = pptrs->f_tpl; if (req->bpf_filter) { u_char *ptr = pptrsv->vlanmpls6.mpls_ptr; u_int32_t idx; /* XXX: fix caplen */ reset_mac_vlan(&pptrsv->vlanmpls6); if (direction == DIRECTION_IN) { memcpy(pptrsv->vlanmpls6.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_IN_SRC_MAC].off, tpl->tpl[NF9_IN_SRC_MAC].len); memcpy(pptrsv->vlanmpls6.mac_ptr, pkt+tpl->tpl[NF9_IN_DST_MAC].off, tpl->tpl[NF9_IN_DST_MAC].len); memcpy(pptrsv->vlanmpls6.vlan_ptr, pkt+tpl->tpl[NF9_IN_VLAN].off, tpl->tpl[NF9_IN_VLAN].len); } else if (direction == DIRECTION_OUT) { memcpy(pptrsv->vlanmpls6.mac_ptr+ETH_ADDR_LEN, pkt+tpl->tpl[NF9_OUT_SRC_MAC].off, tpl->tpl[NF9_OUT_SRC_MAC].len); memcpy(pptrsv->vlanmpls6.mac_ptr, pkt+tpl->tpl[NF9_OUT_DST_MAC].off, tpl->tpl[NF9_OUT_DST_MAC].len); memcpy(pptrsv->vlanmpls6.vlan_ptr, pkt+tpl->tpl[NF9_OUT_VLAN].off, tpl->tpl[NF9_OUT_VLAN].len); } for (idx = NF9_MPLS_LABEL_1; idx <= NF9_MPLS_LABEL_10 && tpl->tpl[idx].len; idx++, ptr += 4) { memset(ptr, 0, 4); memcpy(ptr, pkt+tpl->tpl[idx].off, tpl->tpl[idx].len); } stick_bosbit(ptr-4); pptrsv->vlanmpls6.iph_ptr = ptr; pptrsv->vlanmpls6.tlh_ptr = ptr + IP6HdrSz; reset_ip6(&pptrsv->vlanmpls6); ((struct ip6_hdr *)pptrsv->vlanmpls6.iph_ptr)->ip6_ctlun.ip6_un2_vfc = 0x60; memcpy(&((struct ip6_hdr *)pptrsv->vlanmpls6.iph_ptr)->ip6_src, pkt+tpl->tpl[NF9_IPV6_SRC_ADDR].off, tpl->tpl[NF9_IPV6_SRC_ADDR].len); memcpy(&((struct ip6_hdr *)pptrsv->vlanmpls6.iph_ptr)->ip6_dst, pkt+tpl->tpl[NF9_IPV6_DST_ADDR].off, tpl->tpl[NF9_IPV6_DST_ADDR].len); memcpy(&((struct ip6_hdr *)pptrsv->vlanmpls6.iph_ptr)->ip6_nxt, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); /* XXX: class ID ? */ memcpy(&((struct my_tlhdr *)pptrsv->vlanmpls6.tlh_ptr)->src_port, pkt+tpl->tpl[NF9_L4_SRC_PORT].off, tpl->tpl[NF9_L4_SRC_PORT].len); memcpy(&((struct my_tlhdr *)pptrsv->vlanmpls6.tlh_ptr)->dst_port, pkt+tpl->tpl[NF9_L4_DST_PORT].off, tpl->tpl[NF9_L4_DST_PORT].len); memcpy(&((struct my_tcphdr *)pptrsv->vlanmpls6.tlh_ptr)->th_flags, pkt+tpl->tpl[NF9_TCP_FLAGS].off, tpl->tpl[NF9_TCP_FLAGS].len); } memcpy(&pptrsv->vlanmpls6.lm_mask_src, pkt+tpl->tpl[NF9_SRC_MASK].off, tpl->tpl[NF9_SRC_MASK].len); memcpy(&pptrsv->vlanmpls6.lm_mask_dst, pkt+tpl->tpl[NF9_DST_MASK].off, tpl->tpl[NF9_DST_MASK].len); pptrsv->vlanmpls6.lm_method_src = NF_NET_KEEP; pptrsv->vlanmpls6.lm_method_dst = NF_NET_KEEP; /* Let's copy some relevant field */ pptrsv->vlanmpls6.l4_proto = 0; memcpy(&pptrsv->vlanmpls6.l4_proto, pkt+tpl->tpl[NF9_L4_PROTOCOL].off, tpl->tpl[NF9_L4_PROTOCOL].len); if (tpl->tpl[NF9_APPLICATION_ID].len == 4) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrs->f_status; if (entry) pptrsv->vlanmpls6.class = NF_evaluate_classifiers(entry->class, pptrsv->vlanmpls6.f_data+tpl->tpl[NF9_APPLICATION_ID].off); } if (config.nfacctd_isis) isis_srcdst_lookup(&pptrsv->vlanmpls6); if (config.nfacctd_bgp_to_agent_map) NF_find_id((struct id_table *)pptrs->bta_table, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.bta, NULL); if (config.nfacctd_bgp_iface_to_rd_map) NF_find_id((struct id_table *)pptrs->bitr_table, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.bitr, NULL); if (config.nfacctd_bgp) bgp_srcdst_lookup(&pptrsv->vlanmpls6); if (config.nfacctd_bgp_peer_as_src_map) NF_find_id((struct id_table *)pptrs->bpas_table, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.bpas, NULL); if (config.nfacctd_bgp_src_local_pref_map) NF_find_id((struct id_table *)pptrs->blp_table, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.blp, NULL); if (config.nfacctd_bgp_src_med_map) NF_find_id((struct id_table *)pptrs->bmed_table, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.bmed, NULL); if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, &pptrsv->vlanmpls6, &pptrsv->vlanmpls6.tag, &pptrsv->vlanmpls6.tag2); exec_plugins(&pptrsv->vlanmpls6); break; #endif default: break; } pkt += tpl->len; flowoff += tpl->len; FlowSeqInc++; } pkt += flowsetlen-flowoff; /* handling padding */ off += flowsetlen; } } else { /* unsupported flowset */ data_hdr = (struct data_hdr_v9 *)pkt; flowsetlen = ntohs(data_hdr->flow_len); if (off+flowsetlen > len) { Log(LOG_DEBUG, "DEBUG ( default/core ): unable to read unsupported Flowset (ID: '%u').\n", fid); return; } pkt += flowsetlen; off += flowsetlen; } if (off < len) goto process_flowset; /* Set IPFIX Sequence number increment */ if (version == 10) { struct xflow_status_entry *entry = (struct xflow_status_entry *) pptrsv->v4.f_status; entry->inc = FlowSeqInc; } } void process_raw_packet(unsigned char *pkt, u_int16_t len, struct packet_ptrs_vector *pptrsv, struct plugin_requests *req) { struct packet_ptrs *pptrs = &pptrsv->v4; u_int16_t nfv; /* basic length check against longest NetFlow header */ if (len < NfHdrV8Sz) { notify_malf_packet(LOG_INFO, "INFO: discarding short NetFlow packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } nfv = ntohs(((struct struct_header_v5 *)pkt)->version); if (nfv != 1 && nfv != 5 && nfv != 7 && nfv != 8 && nfv != 9 && nfv != 10) { notify_malf_packet(LOG_INFO, "INFO: discarding unknown NetFlow packet", (struct sockaddr *) pptrs->f_agent); xflow_tot_bad_datagrams++; return; } pptrs->f_header = pkt; switch (nfv) { case 5: case 7: case 8: pptrs->seqno = ntohl(((struct struct_header_v5 *)pkt)->flow_sequence); break; case 9: pptrs->seqno = ntohl(((struct struct_header_v9 *)pkt)->flow_sequence); break; case 10: pptrs->seqno = ntohl(((struct struct_header_ipfix *)pkt)->flow_sequence); default: pptrs->seqno = 0; break; } if (config.debug) { struct host_addr a; u_char agent_addr[50]; u_int16_t agent_port; sa_to_addr((struct sockaddr *)pptrs->f_agent, &a, &agent_port); addr_to_str(agent_addr, &a); Log(LOG_DEBUG, "DEBUG ( default/core ): Received NetFlow packet from [%s:%u] version [%u] seqno [%u]\n", agent_addr, agent_port, nfv, pptrsv->v4.seqno); } if (config.pre_tag_map) NF_find_id((struct id_table *)pptrs->idtable, pptrs, &pptrs->tag, &pptrs->tag2); exec_plugins(pptrs); } void compute_once() { struct pkt_data dummy; CounterSz = sizeof(dummy.pkt_len); PdataSz = sizeof(struct pkt_data); PpayloadSz = sizeof(struct pkt_payload); PmsgSz = sizeof(struct pkt_msg); PextrasSz = sizeof(struct pkt_extras); PbgpSz = sizeof(struct pkt_bgp_primitives); ChBufHdrSz = sizeof(struct ch_buf_hdr); CharPtrSz = sizeof(char *); NfHdrV1Sz = sizeof(struct struct_header_v1); NfHdrV5Sz = sizeof(struct struct_header_v5); NfHdrV7Sz = sizeof(struct struct_header_v7); NfHdrV8Sz = sizeof(struct struct_header_v8); NfHdrV9Sz = sizeof(struct struct_header_v9); NfDataHdrV9Sz = sizeof(struct data_hdr_v9); NfTplHdrV9Sz = sizeof(struct template_hdr_v9); NfOptTplHdrV9Sz = sizeof(struct options_template_hdr_v9); NfDataV1Sz = sizeof(struct struct_export_v1); NfDataV5Sz = sizeof(struct struct_export_v5); NfDataV7Sz = sizeof(struct struct_export_v7); IP4HdrSz = sizeof(struct my_iphdr); IP4TlSz = sizeof(struct my_iphdr)+sizeof(struct my_tlhdr); PptrsSz = sizeof(struct packet_ptrs); CSSz = sizeof(struct class_st); HostAddrSz = sizeof(struct host_addr); UDPHdrSz = sizeof(struct my_udphdr); IpFixHdrSz = sizeof(struct struct_header_ipfix); #if defined ENABLE_IPV6 IP6HdrSz = sizeof(struct ip6_hdr); IP6AddrSz = sizeof(struct in6_addr); IP6TlSz = sizeof(struct ip6_hdr)+sizeof(struct my_tlhdr); #endif } u_int16_t NF_evaluate_flow_type(struct template_cache_entry *tpl, struct packet_ptrs *pptrs) { u_int16_t ret=0; if ((tpl->tpl[NF9_IN_VLAN].len && *(pptrs->f_data+tpl->tpl[NF9_IN_VLAN].off) > 0) || (tpl->tpl[NF9_OUT_VLAN].len && *(pptrs->f_data+tpl->tpl[NF9_OUT_VLAN].off) > 0)) ret += NF9_FTYPE_VLAN; if (tpl->tpl[NF9_MPLS_LABEL_1].len /* check: value > 0 ? */) ret += NF9_FTYPE_MPLS; /* Explicit IP protocol definition first; a bit of heuristics as fallback */ if (*(pptrs->f_data+tpl->tpl[NF9_IP_PROTOCOL_VERSION].off) == 4); else if (*(pptrs->f_data+tpl->tpl[NF9_IP_PROTOCOL_VERSION].off) == 6) ret += NF9_FTYPE_IPV6; else if (tpl->tpl[NF9_IPV4_SRC_ADDR].len > 0); else if (tpl->tpl[NF9_IPV6_SRC_ADDR].len > 0) ret += NF9_FTYPE_IPV6; return ret; } u_int16_t NF_evaluate_direction(struct template_cache_entry *tpl, struct packet_ptrs *pptrs) { u_int16_t ret = DIRECTION_IN; if (tpl->tpl[NF9_DIRECTION].len && *(pptrs->f_data+tpl->tpl[NF9_DIRECTION].off) == 1) ret = DIRECTION_OUT; return ret; } void reset_mac(struct packet_ptrs *pptrs) { memset(pptrs->mac_ptr, 0, 2*ETH_ADDR_LEN); } void reset_mac_vlan(struct packet_ptrs *pptrs) { memset(pptrs->mac_ptr, 0, 2*ETH_ADDR_LEN); memset(pptrs->vlan_ptr, 0, 2); } void reset_ip4(struct packet_ptrs *pptrs) { memset(pptrs->iph_ptr, 0, IP4TlSz); Assign8(((struct my_iphdr *)pptrs->iph_ptr)->ip_vhl, 5); } #if defined ENABLE_IPV6 void reset_ip6(struct packet_ptrs *pptrs) { memset(pptrs->iph_ptr, 0, IP6TlSz); Assign16(((struct ip6_hdr *)pptrs->iph_ptr)->ip6_plen, htons(100)); Assign16(((struct ip6_hdr *)pptrs->iph_ptr)->ip6_hlim, htons(64)); } #endif void notify_malf_packet(short int severity, char *ostr, struct sockaddr *sa) { struct host_addr a; u_char errstr[SRVBUFLEN]; u_char agent_addr[50] /* able to fit an IPv6 string aswell */, any[]="0.0.0.0"; u_int16_t agent_port; sa_to_addr(sa, &a, &agent_port); addr_to_str(agent_addr, &a); if (!config.nfacctd_ip) config.nfacctd_ip = any; snprintf(errstr, SRVBUFLEN, "%s: nfacctd=%s:%u agent=%s:%u \n", ostr, config.nfacctd_ip, config.nfacctd_port, agent_addr, agent_port); Log(severity, errstr); } void NF_find_id(struct id_table *t, struct packet_ptrs *pptrs, pm_id_t *tag, pm_id_t *tag2) { int x, j, stop; struct sockaddr *sa = (struct sockaddr *) pptrs->f_agent; pm_id_t id; if (!t) return; /* The id_table is shared between by IPv4 and IPv6 NetFlow agents. IPv4 ones are in the lower part (0..x), IPv6 ones are in the upper part (x+1..end) */ id = 0; if (tag) *tag = 0; if (tag2) *tag2 = 0; if (sa->sa_family == AF_INET) { for (x = 0; x < t->ipv4_num; x++) { if (t->e[x].agent_ip.a.address.ipv4.s_addr == ((struct sockaddr_in *)sa)->sin_addr.s_addr) { t->e[x].last_matched = FALSE; for (j = 0, stop = 0; !stop; j++) stop = (*t->e[x].func[j])(pptrs, &id, &t->e[x]); if (id) { if (stop == PRETAG_MAP_RCODE_ID) { if (t->e[x].stack.func) id = (*t->e[x].stack.func)(id, *tag); *tag = id; } else if (stop == PRETAG_MAP_RCODE_ID2) { if (t->e[x].stack.func) id = (*t->e[x].stack.func)(id, *tag2); *tag2 = id; } if (t->e[x].jeq.ptr) { if (t->e[x].ret) { exec_plugins(pptrs); set_shadow_status(pptrs); *tag = 0; *tag2 = 0; } x = t->e[x].jeq.ptr->pos; x--; /* yes, it will be automagically incremented by the for() cycle */ id = 0; } else break; } } } } #if defined ENABLE_IPV6 else if (sa->sa_family == AF_INET6) { for (x = (t->num-t->ipv6_num); x < t->num; x++) { if (!ip6_addr_cmp(&t->e[x].agent_ip.a.address.ipv6, &((struct sockaddr_in6 *)sa)->sin6_addr)) { for (j = 0, stop = 0; !stop; j++) stop = (*t->e[x].func[j])(pptrs, &id, &t->e[x]); if (id) { if (stop == PRETAG_MAP_RCODE_ID) { if (t->e[x].stack.func) id = (*t->e[x].stack.func)(id, *tag); *tag = id; } else if (stop == PRETAG_MAP_RCODE_ID2) { if (t->e[x].stack.func) id = (*t->e[x].stack.func)(id, *tag2); *tag2 = id; } if (t->e[x].jeq.ptr) { if (t->e[x].ret) { exec_plugins(pptrs); set_shadow_status(pptrs); *tag = 0; *tag2 = 0; } x = t->e[x].jeq.ptr->pos; x--; /* yes, it will be automagically incremented by the for() cycle */ id = 0; } else break; } } } } #endif } char *nfv578_check_status(struct packet_ptrs *pptrs) { struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct sockaddr *sa = (struct sockaddr *) pptrs->f_agent; u_int32_t aux1 = (hdr->engine_id << 8 | hdr->engine_type); int hash = hash_status_table(aux1, sa, XFLOW_STATUS_TABLE_SZ); struct xflow_status_entry *entry = NULL; if (hash >= 0) { entry = search_status_table(sa, aux1, hash, XFLOW_STATUS_TABLE_MAX_ENTRIES); if (entry) { update_status_table(entry, ntohl(hdr->flow_sequence)); entry->inc = ntohs(hdr->count); } } return (char *) entry; } char *nfv9_check_status(struct packet_ptrs *pptrs, u_int32_t sid, u_int32_t seq) { struct sockaddr *sa = (struct sockaddr *) pptrs->f_agent; int hash = hash_status_table(sid, sa, XFLOW_STATUS_TABLE_SZ); struct xflow_status_entry *entry = NULL; if (hash >= 0) { entry = search_status_table(sa, sid, hash, XFLOW_STATUS_TABLE_MAX_ENTRIES); if (entry) { update_status_table(entry, seq); entry->inc = 1; } } return (char *) entry; } /* Dummy objects here - ugly to see but well portable */ void SF_find_id(struct id_table *t, struct packet_ptrs *pptrs, pm_id_t *tag, pm_id_t *tag2) { } pmacct-0.14.0/src/classifier.c0000644000175000017500000004373611460653755015161 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2010 by Paolo Lucente */ /* 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. NOTE: because the main idea is to make the pmacct classifier FULLY compatibile with the patterns of the L7-filter project, very little parts of this file have been grabbed - and carefully adapted in order to work into the new home :) - by the L7-filter project source code. They are marked as "l7code" and protected as follows: By Matthew Strait , Oct 2003. http://l7-filter.sf.net 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. http://www.gnu.org/licenses/gpl.txt */ #define __CLASSIFIER_C #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "ip_flow.h" #include "classifier.h" #include "jhash.h" #if defined HAVE_DLOPEN #include #endif u_int32_t class_trivial_hash_rnd = 140281; void init_classifiers(char *path) { char fname[MAX_FN_LEN]; struct dirent **namelist; struct stat st; struct pkt_classifier css; int entries = 0, n = 0, x = 0, ret; int max = pmct_get_num_entries(); if (!config.classifier_tentatives) config.classifier_tentatives = DEFAULT_TENTATIVES; class = map_shared(0, sizeof(struct pkt_classifier)*max, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); memset(class, 0, sizeof(struct pkt_classifier)*max); /* valid case for nfacctd; NULL path checks are performed in daemons requiring it - ie. pmacctd, uacctd, etc. */ if (!path) return; entries = pm_scandir(path, &namelist, 0, pm_alphasort); if (entries > 0) { while (n < entries) { memset(&css, 0, sizeof(struct pkt_classifier)); snprintf(fname, sizeof(fname), "%s/%s", path, namelist[n]->d_name); ret = stat(fname, &st); if (ret < 0) Log(LOG_ERR, "ERROR: Unable to stat(): '%s'. Skipped.\n", fname); else { if (S_ISREG(st.st_mode) && (dot_pat(fname) || dot_so(fname))) { if (x > max) { Log(LOG_DEBUG, "DEBUG: No more room for classifiers.\n"); break; } Log(LOG_DEBUG, "DEBUG: reading %s classifier.\n", fname); if (dot_pat(fname)) ret = parse_pattern_file(fname, &css); else if (dot_so(fname)) ret = parse_shared_object(fname, &css); if (ret) { css.id = x+1; /* id are >= 1 */ pmct_register(&css); x++; } else memset(&css, 0, sizeof(struct pkt_classifier)); } } free(namelist[n]); n++; } free(namelist); Log(LOG_DEBUG, "DEBUG: %d classifiers successfully loaded.\n", x); } else { Log(LOG_ERR, "ERROR: Unable to open: '%s'\n", path); exit(1); } } pm_class_t SF_evaluate_classifiers(char *string) { int j = 0, max = pmct_get_num_entries(); while (class[j].id && j < max) { if ( !strcmp(class[j].protocol, string) ) return class[j].id; j++; } return 0; } void evaluate_classifiers(struct packet_ptrs *pptrs, struct ip_flow_common *fp, unsigned int idx) { struct pkt_classifier_data data; int plen = (config.snaplen ? config.snaplen : DEFAULT_SNAPLEN); unsigned int reverse = idx ? 0 : 1; char payload[plen+1]; int j = 0, ret, cidx; int max = pmct_get_num_entries(); void *cc_node = NULL, *cc_rev_node = NULL, *context = NULL; prepare_classifier_data(&data, fp, idx, pptrs); if (pptrs->new_flow) { init_class_accumulators(pptrs, fp, idx); search_conntrack(fp, pptrs, idx); } /* Short circuit: a) if we have a class; b) if we have no more tentatives to classify the packet. Otherwise continue */ if (fp->class[idx] || !fp->cst[idx].tentatives) { pptrs->class = fp->class[idx]; handle_class_accumulators(pptrs, fp, idx); /* do we have an helper ? If yes, let's run it ! */ if (fp->conntrack_helper) fp->conntrack_helper(fp->last[idx].tv_sec, pptrs); return; } /* We will pre-process the payload section of the snapshot */ if (pptrs->payload_ptr) { int caplen = ((struct pcap_pkthdr *)pptrs->pkthdr)->caplen - (pptrs->payload_ptr - pptrs->packet_ptr), x = 0, y = 0; while (x < caplen && y < plen) { if (pptrs->payload_ptr[x] != '\0') { if (isascii(pptrs->payload_ptr[x])) payload[y] = tolower(pptrs->payload_ptr[x]); else payload[y] = pptrs->payload_ptr[x]; y++; } x++; } payload[y] = '\0'; while (class[j].id && j < max) { if (class[j].pattern) ret = pm_regexec(class[j].pattern, payload); else if (*class[j].func) { cc_node = search_context_chain(fp, idx, class[j].protocol); cc_rev_node = search_context_chain(fp, reverse, class[j].protocol); context = cc_node; ret = (*class[j].func)(&data, caplen, &context, &cc_rev_node, &class[j].extra); if (context && !cc_node) insert_context_chain(fp, idx, class[j].protocol, context); } if (ret) { if (ret > 1 && ret < max && class[ret-1].id) cidx = ret-1; else cidx = j; fp->class[0] = class[cidx].id; fp->class[1] = class[cidx].id; pptrs->class = class[cidx].id; handle_class_accumulators(pptrs, fp, idx); if (class[cidx].ct_helper) { fp->conntrack_helper = class[cidx].ct_helper; fp->conntrack_helper(fp->last[idx].tv_sec, pptrs); } else fp->conntrack_helper = NULL; return; } j++; } } fp->class[idx] = FALSE; pptrs->class = FALSE; handle_class_accumulators(pptrs, fp, idx); } void init_class_accumulators(struct packet_ptrs *pptrs, struct ip_flow_common *fp, unsigned int idx) { unsigned int reverse = idx ? 0 : 1; struct timeval now; memset(&pptrs->cst, 0, CSSz); memset(&fp->cst[idx], 0, CSSz); clear_context_chain(fp, idx); fp->cst[idx].tentatives = config.classifier_tentatives; fp->class[idx] = FALSE; memcpy(&fp->cst[idx].stamp, &fp->last[idx], sizeof(struct timeval)); /* If the reciprocal of this flow a) is not expired and b) has a class then let's inherit it */ now.tv_sec = fp->last[idx].tv_sec; now.tv_usec = 0; if (!is_expired_uni(&now, fp, reverse)) fp->class[idx] = fp->class[reverse]; else fp->conntrack_helper = NULL; } void handle_class_accumulators(struct packet_ptrs *pptrs, struct ip_flow_common *fp, unsigned int idx) { struct my_iphdr *iphp = (struct my_iphdr *)pptrs->iph_ptr; #if defined ENABLE_IPV6 struct ip6_hdr *ip6hp = (struct ip6_hdr *)pptrs->iph_ptr; #endif /* The flow doesn't have a class yet */ if (!fp->class[idx]) { /* We have more chances to classify the flow */ if (fp->cst[idx].tentatives) { memset(&pptrs->cst, 0, CSSz); pptrs->cst.tentatives = fp->cst[idx].tentatives; // XXX if (pptrs->l3_proto == ETHERTYPE_IP) fp->cst[idx].ba += ntohs(iphp->ip_len); #if defined ENABLE_IPV6 else if (pptrs->l3_proto == ETHERTYPE_IPV6) fp->cst[idx].ba += (IP6HdrSz+ntohs(ip6hp->ip6_plen)); #endif if (pptrs->new_flow) fp->cst[idx].fa++; if (pptrs->pf) { fp->cst[idx].pa += pptrs->pf; pptrs->pf = 0; } fp->cst[idx].pa++; if (pptrs->payload_ptr) fp->cst[idx].tentatives--; } /* We finished tentatives, flow class is unknown */ else { memset(&pptrs->cst, 0, CSSz); memset(&fp->cst[idx], 0, CSSz); clear_context_chain(fp, idx); } } else { memcpy(&pptrs->cst, &fp->cst[idx], CSSz); memset(&fp->cst[idx], 0, CSSz); clear_context_chain(fp, idx); } } /* dot_pat(): checks that the supplied fname ends with the '.pat' suffix */ int dot_pat(char *fname) { int len = strlen(fname); if (fname[len-4] == '.' && fname[len-3] == 'p' && fname[len-2] == 'a' && fname[len-1] == 't') return 1; return 0; } /* dot_so(): checks that the supplied fname ends with the '.so' suffix */ int dot_so(char *fname) { int len = strlen(fname); if (fname[len-3] == '.' && fname[len-2] == 's' && fname[len-1] == 'o') return 1; return 0; } /* l7code */ static int hex2dec(char c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return c - '0'; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return c - 'a' + 10; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return c - 'A' + 10; default: Log(LOG_ERR, "hex2dec(): bad value!\n"); return 0; } } /* l7code: takes a string with \xHH escapes and returns one with the characters they stand for */ static char * pre_process(char * s) { char *result = malloc(strlen(s) + 1); int sindex = 0, rindex = 0; while( sindex < strlen(s) ) { if( sindex + 3 < strlen(s) && s[sindex] == '\\' && s[sindex+1] == 'x' && isxdigit(s[sindex + 2]) && isxdigit(s[sindex + 3]) ) { /* carefully remember to call tolower here... */ result[rindex] = tolower( hex2dec(s[sindex + 2])*16 + hex2dec(s[sindex + 3] ) ); sindex += 3; /* 4 total */ } else result[rindex] = tolower(s[sindex]); sindex++; rindex++; } result[rindex] = '\0'; return result; } /* l7code */ int parse_pattern_file(char *fname, struct pkt_classifier *css) { FILE *f; char line[MAX_PATTERN_LEN]; int len = 0, linelen = 0, ret; enum { protocol, pattern, done } datatype = protocol; f = fopen(fname, "r"); if (!f) return 0; while ( fgets(line, MAX_PATTERN_LEN, f) ) { linelen = strlen(line); if(linelen < 2 || line[0] == '#') continue; /* strip the pesky newline... */ if (line[linelen - 1] == '\n') line[linelen - 1] = '\0'; if (datatype == protocol) { if (linelen >= MAX_PROTOCOL_LEN) { Log(LOG_ERR, "ERROR: Protocol name in %s too long. A maximum of %d chars is allowed.\n", fname, MAX_PROTOCOL_LEN); return 0; } strncpy(css->protocol, line, MAX_PROTOCOL_LEN); datatype = pattern; } else if (datatype == pattern) { if (linelen >= MAX_PATTERN_LEN) { Log(LOG_ERR, "ERROR: Pattern in %s too long. A maximum of %d chars is allowed.\n", fname, MAX_PATTERN_LEN); return 0; } css->pattern = pm_regcomp(pre_process(line), &linelen); if (!css->pattern) { Log(LOG_ERR, "ERROR: Failed compiling regular expression for protocol '%s'\n", css->protocol); return 0; } datatype = done; break; } else { Log(LOG_ERR, "ERROR: parse_pattern_file(): internal error\n"); return 0; } } if (datatype != done) { Log(LOG_ERR, "ERROR: Failed to get all needed data from %s\n", fname); return 0; } fclose(f); link_conntrack_helper(css); css->func = NULL; return 1; } int parse_shared_object(char *fname, struct pkt_classifier *css) { #if defined HAVE_DLOPEN int ret, (*init)(void **) = NULL; char *proto, *err, *type; void *handler; dlerror(); handler = dlopen(fname, RTLD_NOW); if (!handler) { Log(LOG_ERR, "ERROR: Failed loading classifier from %s (%s)\n", fname, dlerror()); return 0; } type = dlsym(handler, "type"); if (!type || strncmp(type, "classifier", 10)) { dlclose(handler); return 0; } proto = dlsym(handler, "protocol"); if (!proto) { Log(LOG_ERR, "ERROR: Unable to find protocol descriptor in %s\n", fname); dlclose(handler); return 0; } if (strlen(proto) >= MAX_PROTOCOL_LEN) { Log(LOG_ERR, "ERROR: Protocol name in %s too long. A maximum of %d chars is allowed.\n", fname, MAX_PROTOCOL_LEN); dlclose(handler); return 0; } strncpy(css->protocol, proto, MAX_PROTOCOL_LEN); *(void **) (&init) = dlsym(handler, "init"); if (*init) { ret = (*init)(&css->extra); if (!ret) { Log(LOG_ERR, "ERROR: Failed initialization of classifier from %s\n", fname); dlclose(handler); return 0; } } dlerror(); *(void **) (&css->func) = dlsym(handler, "classifier"); if (err = dlerror()) { Log(LOG_ERR, "ERROR: Unable to load classifier routine from %s (%s)\n", fname, err); dlclose(handler); return 0; } link_conntrack_helper(css); css->pattern = NULL; return 1; #else return 0; #endif } int pm_scandir(const char *dir, struct dirent ***namelist, int (*select)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)) { DIR *d; struct dirent *entry; register int i = 0; size_t entrysize; if ((d=opendir(dir)) == NULL) return(-1); *namelist = NULL; while ((entry=readdir(d)) != NULL) { if (select == NULL || (select != NULL && (*select)(entry))) { *namelist=(struct dirent **)realloc((void *)(*namelist), (size_t)((i+1)*sizeof(struct dirent *))); if (*namelist == NULL) { closedir(d); return(-1); } entrysize=sizeof(struct dirent)-sizeof(entry->d_name)+strlen(entry->d_name)+1; (*namelist)[i]=(struct dirent *)malloc(entrysize); if ((*namelist)[i] == NULL) { closedir(d); return(-1); } memcpy((*namelist)[i], entry, entrysize); i++; } } if (closedir(d)) return(-1); if (i == 0) return(-1); if (compar != NULL) qsort((void *)(*namelist), (size_t)i, sizeof(struct dirent *), compar); return(i); } int pm_alphasort(const struct dirent **a, const struct dirent **b) { return(strcmp((*a)->d_name, (*b)->d_name)); } void link_conntrack_helper(struct pkt_classifier *css) { int index = 0; while (strcmp(conntrack_helper_list[index].protocol, "")) { if (!strcmp(css->protocol, conntrack_helper_list[index].protocol)) { css->ct_helper = conntrack_helper_list[index].ct_helper; break; } index++; } } void *search_context_chain(struct ip_flow_common *fp, unsigned int idx, char *proto) { struct context_chain *chain = fp->cc[idx]; if (!chain) return NULL; while (chain->protocol) { if (!strncmp(chain->protocol, proto, MAX_PROTOCOL_LEN)) return chain->data; else { if (chain->next) chain = chain->next; else return NULL; } } return NULL; } void insert_context_chain(struct ip_flow_common *fp, unsigned int idx, char *proto, void *context) { struct context_chain *chain = fp->cc[idx]; while (chain) { if (chain->next) chain = chain->next; else break; } if (!fp->cc[idx]) { fp->cc[idx] = malloc(sizeof(struct context_chain)); if (!fp->cc[idx]) return; chain = fp->cc[idx]; } else { chain->next = malloc(sizeof(struct context_chain)); if (!chain->next) return; chain = chain->next; } chain->protocol = proto; chain->data = context; chain->next = NULL; } void clear_context_chain(struct ip_flow_common *fp, unsigned int idx) { struct context_chain *chain = fp->cc[idx], *aux; while (chain) { if (chain->next) { aux = chain; chain = chain->next; free(aux->data); free(aux); } else { free(chain->data); free(chain); break; } } fp->cc[idx] = NULL; } void prepare_classifier_data(struct pkt_classifier_data *data, struct ip_flow_common *fp, unsigned int idx, struct packet_ptrs *pptrs) { data->stamp.tv_sec = fp->cst[idx].stamp.tv_sec; data->stamp.tv_usec = fp->cst[idx].stamp.tv_usec; data->packet_ptr = pptrs->packet_ptr; data->l3_ptr = pptrs->iph_ptr; data->l4_ptr = pptrs->tlh_ptr; data->payload_ptr = pptrs->payload_ptr; data->l3_proto = pptrs->l3_proto; data->l4_proto = pptrs->l4_proto; data->plen = ((struct pcap_pkthdr *)pptrs->pkthdr)->len - (pptrs->payload_ptr - pptrs->packet_ptr); data->tentatives = fp->cst[idx].tentatives; data->sampling_rate = 1; } /* pmct library function set follows: PMacct Classifier Table. Unique assumption is the global "class" Classifier Table */ pm_class_t pmct_register(struct pkt_classifier *css) { int max = pmct_get_num_entries(); if (!css) return 0; /* let's check that a) a valid class ID has been supplied, b) the class ID is still available. If this is the case, let's proceed with this entry, otherwise we will switch to a default behaviour. */ if (!strcmp(css->protocol, "")) return 0; if (css->id && css->id <= max && !class[css->id-1].id) { memcpy(&class[css->id-1], css, sizeof(struct pkt_classifier)); return css->id; } else return 0; } void pmct_unregister(pm_class_t id) { int max = pmct_get_num_entries(); if (id && id <= max && class[id-1].id) memset(&class[id-1], 0, sizeof(struct pkt_classifier)); } pm_class_t pmct_find_first_free() { int ret, idx = 0, num = pmct_get_num_entries(); while (idx < num) { ret = pmct_isfree(idx+1); if (ret > 0) return idx+1; else if (ret < 0) return 0; idx++; } return 0; } pm_class_t pmct_find_last_free() { int ret, idx = pmct_get_num_entries(); idx--; while (idx) { ret = pmct_isfree(idx+1); if (ret > 0) return idx+1; else if (ret < 0) return 0; idx--; } return 0; } int pmct_isfree(pm_class_t id) { int max = pmct_get_num_entries(); if (!class) return -1; if (id && id <= max) { if (!class[id-1].id) return 1; else return 0; } else return -1; } int pmct_get(pm_class_t id, struct pkt_classifier *css) { int max = pmct_get_num_entries(); if (!css) return 0; if (id && id <= max && class[id-1].id) { memcpy(css, &class[id-1], sizeof(struct pkt_classifier)); return 1; } else { memset(css, 0, sizeof(struct pkt_classifier)); return 1; } } int pmct_get_num_entries() { if (config.classifier_table_num) return config.classifier_table_num; else return MAX_CLASSIFIERS; } pmacct-0.14.0/src/pretag_handlers.c0000644000175000017500000015720111716470265016165 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2012 by Paolo Lucente */ /* 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. */ #define __PRETAG_HANDLERS_C #include "pmacct.h" #include "nfacctd.h" #include "sflow.h" #include "sfacctd.h" #include "pretag_handlers.h" #include "net_aggr.h" #include "bgp/bgp.h" int PT_map_id_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { struct host_addr a; char *endptr = NULL; pm_id_t j = 0; e->id = 0; e->flags = FALSE; /* If we parse a bgp_agent_map and spot a '.' within the string let's check if we are given a valid IP address */ if (acct_type == MAP_BGP_TO_XFLOW_AGENT && strchr(value, '.')) { memset(&a, 0, sizeof(a)); str_to_addr(value, &a); if (a.family == AF_INET) j = a.address.ipv4.s_addr; else { Log(LOG_ERR, "ERROR ( %s ): Agent ID does not appear to be a valid IPv4 address. ", filename); return TRUE; } } else if (acct_type == MAP_BGP_IFACE_TO_RD && strchr(value, ':')) { rd_t rd; bgp_str2rd(&rd, value); memcpy(&j, &rd, sizeof(rd)); } /* If we spot the word "bgp", let's check this is a map that supports it */ else if ((acct_type == MAP_BGP_PEER_AS_SRC || acct_type == MAP_BGP_SRC_LOCAL_PREF || acct_type == MAP_BGP_SRC_MED) && !strncmp(value, "bgp", strlen("bgp"))) { e->flags = BPAS_MAP_RCODE_BGP; } else { j = strtoull(value, &endptr, 10); if (!j || j > UINT32_MAX) { Log(LOG_ERR, "ERROR ( %s ): Invalid Agent ID specified. ", filename); return TRUE; } } e->id = j; return FALSE; } int PT_map_id2_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { char *endptr = NULL; pm_id_t j; j = strtoull(value, &endptr, 10); if (!j || j > UINT32_MAX) { Log(LOG_ERR, "ERROR ( %s ): Invalid Agent ID2 specified. ", filename); return TRUE; } e->id2 = j; return FALSE; } int PT_map_ip_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { if (!str_to_addr(value, &e->agent_ip.a)) { Log(LOG_ERR, "ERROR ( %s ): Bad IP address '%s'. ", filename, value); return TRUE; } return FALSE; } int PT_map_input_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0, len; char *endptr; e->input.neg = pt_check_neg(&value); len = strlen(value); while (x < len) { if (!isdigit(value[x])) { Log(LOG_ERR, "ERROR ( %s ): bad 'in' value: '%s'. ", filename, value); return TRUE; } x++; } e->input.n = strtoul(value, &endptr, 10); for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_IN_IFACE) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'input' clauses part of the same statement. ", filename); return TRUE; } } if (config.acct_type == ACCT_NF) e->func[x] = pretag_input_handler; else if (config.acct_type == ACCT_SF) e->func[x] = SF_pretag_input_handler; else if (config.acct_type == ACCT_PM) e->func[x] = PM_pretag_input_handler; if (e->func[x]) e->func_type[x] = PRETAG_IN_IFACE; return FALSE; } int PT_map_output_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0, len; char *endptr; e->output.neg = pt_check_neg(&value); len = strlen(value); while (x < len) { if (!isdigit(value[x])) { Log(LOG_ERR, "ERROR ( %s ): bad 'out' value: '%s'. ", filename, value); return TRUE; } x++; } e->output.n = strtoul(value, &endptr, 10); for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_OUT_IFACE) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'output' clauses part of the same statement. ", filename); return TRUE; } } if (config.acct_type == ACCT_NF) e->func[x] = pretag_output_handler; else if (config.acct_type == ACCT_SF) e->func[x] = SF_pretag_output_handler; else if (config.acct_type == ACCT_PM) e->func[x] = PM_pretag_output_handler; if (e->func[x]) e->func_type[x] = PRETAG_OUT_IFACE; return FALSE; } int PT_map_nexthop_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0; e->nexthop.neg = pt_check_neg(&value); if (!str_to_addr(value, &e->nexthop.a)) { Log(LOG_ERR, "ERROR ( %s ): Bad nexthop address '%s'. ", filename, value); return TRUE; } for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_NEXTHOP) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'nexthop' clauses part of the same statement. ", filename); return TRUE; } } if (config.acct_type == ACCT_NF) e->func[x] = pretag_nexthop_handler; else if (config.acct_type == ACCT_SF) e->func[x] = SF_pretag_nexthop_handler; if (e->func[x]) e->func_type[x] = PRETAG_NEXTHOP; return FALSE; } int PT_map_bgp_nexthop_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0, have_bgp = 0; e->bgp_nexthop.neg = pt_check_neg(&value); if (!str_to_addr(value, &e->bgp_nexthop.a)) { Log(LOG_ERR, "ERROR ( %s ): Bad BGP nexthop address '%s'. ", filename, value); return TRUE; } for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_BGP_NEXTHOP) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'bgp_nexthop' clauses part of the same statement. ", filename); return TRUE; } } if (config.nfacctd_as & NF_AS_BGP) { e->func[x] = pretag_bgp_bgp_nexthop_handler; have_bgp = TRUE; e->func_type[x] = PRETAG_BGP_NEXTHOP; x++; } if (config.nfacctd_as & NF_AS_KEEP && config.acct_type == ACCT_NF) { e->func[x] = pretag_bgp_nexthop_handler; e->func_type[x] = PRETAG_BGP_NEXTHOP; return FALSE; } else if (config.nfacctd_as & NF_AS_KEEP && config.acct_type == ACCT_SF) { e->func[x] = SF_pretag_bgp_nexthop_handler; e->func_type[x] = PRETAG_BGP_NEXTHOP; return FALSE; } if (have_bgp) return FALSE; Log(LOG_ERR, "ERROR ( %s ): 'bgp_nexthop' is not supported when a 'networks_file' is specified or by the 'pmacctd' daemon. ", filename); return TRUE; } int BPAS_map_bgp_nexthop_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0; e->bgp_nexthop.neg = pt_check_neg(&value); if (!str_to_addr(value, &e->bgp_nexthop.a)) { Log(LOG_ERR, "ERROR ( %s ): Bad BGP nexthop address '%s'. ", filename, value); return TRUE; } for (x = 0; e->func[x]; x++); if (config.nfacctd_bgp) e->func[x] = BPAS_bgp_nexthop_handler; return FALSE; } int BPAS_map_bgp_peer_dst_as_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { as_t tmp; int x = 0; char *endptr; e->peer_dst_as.neg = pt_check_neg(&value); tmp = strtoul(value, &endptr, 10); e->peer_dst_as.n = tmp; for (x = 0; e->func[x]; x++); if (config.nfacctd_bgp) e->func[x] = BPAS_bgp_peer_dst_as_handler; return FALSE; } int BPAS_map_src_mac_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { struct pcap_device device; bpf_u_int32 localnet, netmask; /* pcap library stuff */ char errbuf[PCAP_ERRBUF_SIZE]; char filter_str[28]; int x, link_type; memset(&device, 0, sizeof(struct pcap_device)); if (glob_pcapt) device.link_type = pcap_datalink(glob_pcapt); else device.link_type = 1; device.dev_desc = pcap_open_dead(device.link_type, 128); /* snaplen=eth_header+my_iphdr+my_tlhdr */ pcap_lookupnet(config.dev, &localnet, &netmask, errbuf); memset(filter_str, 0, sizeof(filter_str)); snprintf(filter_str, sizeof(filter_str), "ether src %s", value); if (pcap_compile(device.dev_desc, &e->filter, filter_str, 0, netmask) < 0) { Log(LOG_ERR, "ERROR ( %s ): malformed src_mac filter: %s\n", filename, pcap_geterr(device.dev_desc)); return TRUE; } pcap_close(device.dev_desc); for (x = 0; e->func[x]; x++); e->func[x] = pretag_filter_handler; req->bpf_filter = TRUE; return FALSE; } int PT_map_engine_type_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0, j, len; e->engine_type.neg = pt_check_neg(&value); len = strlen(value); while (x < len) { if (!isdigit(value[x])) { Log(LOG_ERR, "ERROR ( %s ): bad 'engine_type' value: '%s'. ", filename, value); return TRUE; } x++; } j = atoi(value); if (j > 255) { Log(LOG_ERR, "ERROR ( %s ): bad 'engine_type' value (range: 0 >= value > 256). ", filename); return TRUE; } e->engine_type.n = j; for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_ENGINE_TYPE) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'engine_type' clauses part of the same statement. ", filename); return TRUE; } } if (config.acct_type == ACCT_NF) e->func[x] = pretag_engine_type_handler; if (e->func[x]) e->func_type[x] = PRETAG_ENGINE_TYPE; return FALSE; } int PT_map_engine_id_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0, j, len; e->engine_id.neg = pt_check_neg(&value); len = strlen(value); while (x < len) { if (!isdigit(value[x])) { Log(LOG_ERR, "ERROR ( %s ): bad 'engine_id' value: '%s'. ", filename, value); return TRUE; } x++; } j = atoi(value); if (j > 255) { Log(LOG_ERR, "ERROR ( %s ): bad 'engine_id' value (range: 0 >= value > 256). ", filename); return TRUE; } e->engine_id.n = j; for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_ENGINE_ID) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'bgp_nexthop' clauses part of the same statement. ", filename); return TRUE; } } if (config.acct_type == ACCT_NF) e->func[x] = pretag_engine_id_handler; if (e->func[x]) e->func_type[x] = PRETAG_ENGINE_ID; return FALSE; } int PT_map_filter_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { struct pcap_device device; bpf_u_int32 localnet, netmask; /* pcap library stuff */ char errbuf[PCAP_ERRBUF_SIZE]; int x, link_type; memset(&device, 0, sizeof(struct pcap_device)); if (glob_pcapt) device.link_type = pcap_datalink(glob_pcapt); else if (config.uacctd_group) device.link_type = DLT_RAW; else device.link_type = 1; device.dev_desc = pcap_open_dead(device.link_type, 128); /* snaplen=eth_header+my_iphdr+my_tlhdr */ pcap_lookupnet(config.dev, &localnet, &netmask, errbuf); if (pcap_compile(device.dev_desc, &e->filter, value, 0, netmask) < 0) { Log(LOG_ERR, "ERROR ( %s ): malformed filter: %s\n", filename, pcap_geterr(device.dev_desc)); return TRUE; } pcap_close(device.dev_desc); for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_FILTER) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'filter' clauses part of the same statement. ", filename); return TRUE; } } e->func[x] = pretag_filter_handler; if (e->func[x]) e->func_type[x] = PRETAG_FILTER; req->bpf_filter = TRUE; return FALSE; } int PT_map_v8agg_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int tmp, x = 0, len; e->v8agg.neg = pt_check_neg(&value); len = strlen(value); while (x < len) { if (!isdigit(value[x])) { Log(LOG_ERR, "ERROR ( %s ): bad 'v8agg' value: '%s'. ", filename, value); return TRUE; } x++; } tmp = atoi(value); if (tmp < 1 || tmp > 14) { Log(LOG_ERR, "ERROR ( %s ): 'v8agg' need to be in the following range: 0 > value > 15. ", filename); return TRUE; } e->v8agg.n = tmp; for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_NFV8_AGG) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'v8agg' clauses part of the same statement. ", filename); return TRUE; } } if (config.acct_type == ACCT_NF) e->func[x] = pretag_v8agg_handler; if (e->func[x]) e->func_type[x] = PRETAG_NFV8_AGG; return FALSE; } int PT_map_agent_id_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0; e->agent_id.neg = pt_check_neg(&value); e->agent_id.n = atoi(value); for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_SF_AGENTID) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'agent_id' clauses part of the same statement. ", filename); return TRUE; } } if (config.acct_type == ACCT_SF) e->func[x] = SF_pretag_agent_id_handler; if (e->func[x]) e->func_type[x] = PRETAG_SF_AGENTID; return FALSE; } int PT_map_sampling_rate_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0; e->sampling_rate.neg = pt_check_neg(&value); e->sampling_rate.n = atoi(value); for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_SAMPLING_RATE) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'sampling_rate' clauses part of the same statement. ", filename); return TRUE; } } if (config.acct_type == ACCT_NF) e->func[x] = pretag_sampling_rate_handler; else if (config.acct_type == ACCT_SF) e->func[x] = SF_pretag_sampling_rate_handler; if (e->func[x]) e->func_type[x] = PRETAG_SAMPLING_RATE; return FALSE; } int PT_map_sample_type_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { char *token = NULL; u_int32_t tmp; int x = 0; e->sample_type.neg = pt_check_neg(&value); while (token = extract_token(&value, ':')) { switch (x) { case 0: tmp = atoi(token); if (tmp > 1048575) { // 2^20-1: 20 bit Enterprise value Log(LOG_WARNING, "WARN ( %s ): Invalid 'sample_type' value. ", filename); return TRUE; } e->sample_type.n = tmp; e->sample_type.n <<= 12; break; case 1: tmp = atoi(token); if (tmp > 4095) { // 2^12-1: 12 bit Format value Log(LOG_WARNING, "WARN ( %s ): Invalid 'sample_type' value. ", filename); return TRUE; } e->sample_type.n |= tmp; break; default: Log(LOG_WARNING, "WARN ( %s ): Invalid 'sample_type' value. ", filename); return TRUE; } x++; } for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_SAMPLE_TYPE) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'sample_type' clauses part of the same statement. ", filename); return TRUE; } } if (config.acct_type == ACCT_SF) e->func[x] = SF_pretag_sample_type_handler; if (e->func[x]) e->func_type[x] = PRETAG_SAMPLE_TYPE; return FALSE; } int PT_map_direction_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0; e->direction.neg = pt_check_neg(&value); e->direction.n = atoi(value); for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_DIRECTION) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'direction' clauses part of the same statement. ", filename); return TRUE; } } if (config.acct_type == ACCT_NF) e->func[x] = pretag_direction_handler; if (e->func[x]) e->func_type[x] = PRETAG_DIRECTION; return FALSE; } int PT_map_src_as_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { as_t tmp; int x = 0, have_bgp = 0; char *endptr; e->src_as.neg = pt_check_neg(&value); tmp = strtoul(value, &endptr, 10); e->src_as.n = tmp; for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_SRC_AS) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'src_as' clauses part of the same statement. ", filename); return TRUE; } } if (config.nfacctd_as & NF_AS_BGP) { e->func[x] = pretag_bgp_src_as_handler; e->func_type[x] = PRETAG_SRC_AS; have_bgp = TRUE; x++; } if ((config.nfacctd_as & NF_AS_NEW || config.acct_type == ACCT_PM) && config.networks_file) { req->bpf_filter = TRUE; e->func[x] = PM_pretag_src_as_handler; e->func_type[x] = PRETAG_SRC_AS; return FALSE; } else if (config.nfacctd_as & NF_AS_KEEP && config.acct_type == ACCT_NF) { e->func[x] = pretag_src_as_handler; e->func_type[x] = PRETAG_SRC_AS; return FALSE; } else if (config.nfacctd_as & NF_AS_KEEP && config.acct_type == ACCT_SF) { e->func[x] = SF_pretag_src_as_handler; e->func_type[x] = PRETAG_SRC_AS; return FALSE; } if (have_bgp) return FALSE; Log(LOG_ERR, "ERROR ( %s ): 'src_as' requires either 'networks_file' or 'nf|sfacctd_as_new: false' to be specified. ", filename); return TRUE; } int PT_map_dst_as_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { as_t tmp; int x = 0, have_bgp = 0; char *endptr; e->dst_as.neg = pt_check_neg(&value); tmp = strtoul(value, &endptr, 10); e->dst_as.n = tmp; for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_DST_AS) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'dst_as' clauses part of the same statement. ", filename); return TRUE; } } if (config.nfacctd_as & NF_AS_BGP) { e->func[x] = pretag_bgp_dst_as_handler; e->func_type[x] = PRETAG_DST_AS; have_bgp = TRUE; x++; } if ((config.nfacctd_as & NF_AS_NEW || config.acct_type == ACCT_PM) && config.networks_file) { req->bpf_filter = TRUE; e->func[x] = PM_pretag_dst_as_handler; e->func_type[x] = PRETAG_DST_AS; return FALSE; } else if (config.nfacctd_as & NF_AS_KEEP && config.acct_type == ACCT_NF) { e->func[x] = pretag_dst_as_handler; e->func_type[x] = PRETAG_DST_AS; return FALSE; } else if (config.nfacctd_as & NF_AS_KEEP && config.acct_type == ACCT_SF) { e->func[x] = SF_pretag_dst_as_handler; e->func_type[x] = PRETAG_DST_AS; return FALSE; } if (have_bgp) return FALSE; Log(LOG_ERR, "ERROR ( %s ): 'dst_as' requires either 'networks_file' or 'nf|sfacctd_as_new: false' to be specified. ", filename); return TRUE; } int PT_map_peer_src_as_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { as_t tmp; int x = 0; char *endptr; e->peer_src_as.neg = pt_check_neg(&value); tmp = strtoul(value, &endptr, 10); e->peer_src_as.n = tmp; for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_PEER_SRC_AS) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'peer_src_as' clauses part of the same statement. ", filename); return TRUE; } } if (config.nfacctd_as & NF_AS_BGP) { e->func[x] = pretag_peer_src_as_handler; e->func_type[x] = PRETAG_PEER_SRC_AS; return FALSE; } Log(LOG_ERR, "ERROR ( %s ): 'peer_src_as' requires '[nf|sf]acctd_as_new: [ bgp | fallback ]' to be specified. ", filename); return TRUE; } int PT_map_peer_dst_as_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { as_t tmp; int x = 0; char *endptr; e->peer_dst_as.neg = pt_check_neg(&value); tmp = strtoul(value, &endptr, 10); e->peer_dst_as.n = tmp; for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_PEER_DST_AS) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'peer_dst_as' clauses part of the same statement. ", filename); return TRUE; } } if (config.nfacctd_as & NF_AS_BGP) { e->func[x] = pretag_peer_dst_as_handler; e->func_type[x] = PRETAG_PEER_DST_AS; return FALSE; } Log(LOG_ERR, "ERROR ( %s ): 'peer_dst_as' requires '[nf|sf]acctd_as_new: [ bgp | fallback ]' to be specified. ", filename); return TRUE; } int PT_map_src_local_pref_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { u_int32_t tmp; int x = 0; char *endptr; e->src_local_pref.neg = pt_check_neg(&value); tmp = strtoul(value, &endptr, 10); e->src_local_pref.n = tmp; for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_SRC_LOCAL_PREF) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'src_local_pref' clauses part of the same statement. ", filename); return TRUE; } } if (config.nfacctd_as & NF_AS_BGP) { e->func[x] = pretag_src_local_pref_handler; e->func_type[x] = PRETAG_SRC_LOCAL_PREF; return FALSE; } Log(LOG_ERR, "ERROR ( %s ): 'src_local_pref' requires '[nf|sf]acctd_as_new: [ bgp | fallback ]' to be specified. ", filename); return TRUE; } int PT_map_local_pref_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { u_int32_t tmp; int x = 0; char *endptr; e->local_pref.neg = pt_check_neg(&value); tmp = strtoul(value, &endptr, 10); e->local_pref.n = tmp; for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_LOCAL_PREF) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'local_pref' clauses part of the same statement. ", filename); return TRUE; } } if (config.nfacctd_as & NF_AS_BGP) { e->func[x] = pretag_local_pref_handler; e->func_type[x] = PRETAG_LOCAL_PREF; return FALSE; } Log(LOG_ERR, "ERROR ( %s ): 'local_pref' requires '[nf|sf]acctd_as_new: [ bgp | fallback ]' to be specified. ", filename); return TRUE; } int PT_map_src_comms_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0, idx = 0; char *endptr, *token; memset(e->src_comms, 0, sizeof(e->src_comms)); /* Negation not supported here */ while ( (token = extract_token(&value, ',')) && idx < MAX_BGP_COMM_PATTERNS ) { e->src_comms[idx] = malloc(MAX_BGP_STD_COMMS); strlcpy(e->src_comms[idx], token, MAX_BGP_STD_COMMS); trim_spaces(e->src_comms[idx]); idx++; } for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_SRC_STD_COMM) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'src_comms' clauses part of the same statement. ", filename); return TRUE; } } if (config.nfacctd_as & NF_AS_BGP && e->src_comms[0]) { e->func[x] = pretag_src_comms_handler; e->func_type[x] = PRETAG_SRC_STD_COMM; return FALSE; } Log(LOG_ERR, "ERROR ( %s ): 'src_comms' requires '[nf|sf]acctd_as_new: [ bgp | fallback ]' to be specified. ", filename); return TRUE; } int PT_map_comms_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0, idx = 0; char *endptr, *token; memset(e->comms, 0, sizeof(e->comms)); /* Negation not supported here */ while ( (token = extract_token(&value, ',')) && idx < MAX_BGP_COMM_PATTERNS ) { e->comms[idx] = malloc(MAX_BGP_STD_COMMS); strlcpy(e->comms[idx], token, MAX_BGP_STD_COMMS); trim_spaces(e->comms[idx]); idx++; } for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_STD_COMM) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'comms' clauses part of the same statement. ", filename); return TRUE; } } if (config.nfacctd_as & NF_AS_BGP && e->comms[0]) { e->func[x] = pretag_comms_handler; e->func_type[x] = PRETAG_STD_COMM; return FALSE; } Log(LOG_ERR, "ERROR ( %s ): 'comms' requires '[nf|sf]acctd_as_new: [ bgp | fallback ]' to be specified. ", filename); return TRUE; } int PT_map_mpls_vpn_rd_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int x = 0, ret; char *endptr, *token; memset(&e->mpls_vpn_rd, 0, sizeof(e->mpls_vpn_rd)); e->mpls_vpn_rd.neg = pt_check_neg(&value); ret = bgp_str2rd(&e->mpls_vpn_rd.rd, value); for (x = 0; e->func[x]; x++) { if (e->func_type[x] == PRETAG_MPLS_VPN_RD) { Log(LOG_ERR, "ERROR ( %s ): Multiple 'mpls_vpn_rd' clauses part of the same statement. ", filename); return TRUE; } } if (ret) { e->func[x] = pretag_mpls_vpn_rd_handler; e->func_type[x] = PRETAG_MPLS_VPN_RD; return FALSE; } else return TRUE; } int PT_map_label_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { strlcpy(e->label, value, MAX_LABEL_LEN); return FALSE; } int PT_map_jeq_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { e->jeq.label = malloc(MAX_LABEL_LEN); if (e->jeq.label) strlcpy(e->jeq.label, value, MAX_LABEL_LEN); else Log(LOG_ERR, "ERROR ( %s ): Not enough memory to allocate JEQ '%s'\n", filename, value); return FALSE; } int PT_map_return_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { int res = parse_truefalse(value); if (res < 0) Log(LOG_ERR, "ERROR ( %s ): Unknown RETURN value: '%s'. Ignoring.\n", filename, value); else e->ret = res; return FALSE; } int PT_map_stack_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type) { e->stack.func = NULL; if (*value == '+') e->stack.func = PT_stack_sum; else Log(LOG_ERR, "ERROR ( %s ): Unknown STACK operator: '%c'. Ignoring.\n", filename, value); return FALSE; } int pretag_input_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t input16 = htons(entry->input.n); u_int32_t input32 = htonl(entry->input.n); u_int8_t neg = entry->input.neg; switch(hdr->version) { case 9: if (tpl->tpl[NF9_INPUT_SNMP].len == 2) { if (!memcmp(&input16, pptrs->f_data+tpl->tpl[NF9_INPUT_SNMP].off, tpl->tpl[NF9_INPUT_SNMP].len)) return (FALSE | neg); } else if (tpl->tpl[NF9_INPUT_SNMP].len == 4) { if (!memcmp(&input32, pptrs->f_data+tpl->tpl[NF9_INPUT_SNMP].off, tpl->tpl[NF9_INPUT_SNMP].len)) return (FALSE | neg); } else return (TRUE ^ neg); case 8: switch(hdr->aggregation) { case 1: if (input16 == ((struct struct_export_v8_1 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); case 3: if (input16 == ((struct struct_export_v8_3 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); case 5: if (input16 == ((struct struct_export_v8_5 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); case 7: if (input16 == ((struct struct_export_v8_7 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); case 8: if (input16 == ((struct struct_export_v8_8 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); case 9: if (input16 == ((struct struct_export_v8_9 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); case 10: if (input16 == ((struct struct_export_v8_10 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); case 11: if (input16 == ((struct struct_export_v8_11 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); case 13: if (input16 == ((struct struct_export_v8_13 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); case 14: if (input16 == ((struct struct_export_v8_14 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); default: return (TRUE ^ neg); } default: if (input16 == ((struct struct_export_v5 *)pptrs->f_data)->input) return (FALSE | neg); else return (TRUE ^ neg); } } int pretag_output_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t output16 = htons(entry->output.n); u_int32_t output32 = htonl(entry->output.n); u_int8_t neg = entry->output.neg; switch(hdr->version) { case 9: if (tpl->tpl[NF9_OUTPUT_SNMP].len == 2) { if (!memcmp(&output16, pptrs->f_data+tpl->tpl[NF9_OUTPUT_SNMP].off, tpl->tpl[NF9_OUTPUT_SNMP].len)) return (FALSE | neg); } else if (tpl->tpl[NF9_OUTPUT_SNMP].len == 4) { if (!memcmp(&output32, pptrs->f_data+tpl->tpl[NF9_OUTPUT_SNMP].off, tpl->tpl[NF9_OUTPUT_SNMP].len)) return (FALSE | neg); } else return (TRUE ^ neg); case 8: switch(hdr->aggregation) { case 1: if (output16 == ((struct struct_export_v8_1 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); case 4: if (output16 == ((struct struct_export_v8_4 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); case 5: if (output16 == ((struct struct_export_v8_5 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); case 6: if (output16 == ((struct struct_export_v8_6 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); case 7: if (output16 == ((struct struct_export_v8_7 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); case 8: if (output16 == ((struct struct_export_v8_8 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); case 9: if (output16 == ((struct struct_export_v8_9 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); case 10: if (output16 == ((struct struct_export_v8_10 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); case 12: if (output16 == ((struct struct_export_v8_12 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); case 13: if (output16 == ((struct struct_export_v8_13 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); case 14: if (output16 == ((struct struct_export_v8_14 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); default: return (TRUE ^ neg); } default: if (output16 == ((struct struct_export_v5 *)pptrs->f_data)->output) return (FALSE | neg); else return (TRUE ^ neg); } } int pretag_nexthop_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; switch(hdr->version) { case 9: if (entry->nexthop.a.family == AF_INET) { if (!memcmp(&entry->nexthop.a.address.ipv4, pptrs->f_data+tpl->tpl[NF9_IPV4_NEXT_HOP].off, tpl->tpl[NF9_IPV4_NEXT_HOP].len)) return (FALSE | entry->nexthop.neg); } #if defined ENABLE_IPV6 else if (entry->nexthop.a.family == AF_INET6) { if (!memcmp(&entry->nexthop.a.address.ipv6, pptrs->f_data+tpl->tpl[NF9_IPV6_NEXT_HOP].off, tpl->tpl[NF9_IPV6_NEXT_HOP].len)) return (FALSE | entry->nexthop.neg); } #endif else return (TRUE ^ entry->nexthop.neg); case 8: /* NetFlow v8 does not seem to contain any nexthop field */ return TRUE; default: if (entry->nexthop.a.address.ipv4.s_addr == ((struct struct_export_v5 *)pptrs->f_data)->nexthop.s_addr) return (FALSE | entry->nexthop.neg); else return (TRUE ^ entry->nexthop.neg); } } int pretag_bgp_nexthop_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; if (entry->last_matched == PRETAG_BGP_NEXTHOP) return FALSE; switch(hdr->version) { case 9: if (entry->bgp_nexthop.a.family == AF_INET) { if (!memcmp(&entry->bgp_nexthop.a.address.ipv4, pptrs->f_data+tpl->tpl[NF9_BGP_IPV4_NEXT_HOP].off, tpl->tpl[NF9_BGP_IPV4_NEXT_HOP].len)) return (FALSE | entry->bgp_nexthop.neg); } #if defined ENABLE_IPV6 else if (entry->nexthop.a.family == AF_INET6) { if (!memcmp(&entry->bgp_nexthop.a.address.ipv6, pptrs->f_data+tpl->tpl[NF9_BGP_IPV6_NEXT_HOP].off, tpl->tpl[NF9_BGP_IPV6_NEXT_HOP].len)) return (FALSE | entry->bgp_nexthop.neg); } #endif else return (TRUE ^ entry->bgp_nexthop.neg); case 8: /* NetFlow v8 does not seem to contain any nexthop field */ return TRUE; default: if (entry->bgp_nexthop.a.address.ipv4.s_addr == ((struct struct_export_v5 *)pptrs->f_data)->nexthop.s_addr) return (FALSE | entry->bgp_nexthop.neg); else return (TRUE ^ entry->bgp_nexthop.neg); } } int pretag_bgp_bgp_nexthop_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *dst_ret = (struct bgp_node *) pptrs->bgp_dst; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; int ret = -1; if (dst_ret) { if (pptrs->bgp_nexthop_info) info = (struct bgp_info *) pptrs->bgp_nexthop_info; else info = (struct bgp_info *) pptrs->bgp_dst_info; if (info && info->attr) { if (info->attr->mp_nexthop.family == AF_INET) { ret = memcmp(&entry->bgp_nexthop.a.address.ipv4, &info->attr->mp_nexthop.address.ipv4, 4); } #if defined ENABLE_IPV6 else if (info->attr->mp_nexthop.family == AF_INET6) { ret = memcmp(&entry->bgp_nexthop.a.address.ipv6, &info->attr->mp_nexthop.address.ipv6, 16); } #endif else { ret = memcmp(&entry->bgp_nexthop.a.address.ipv4, &info->attr->nexthop, 4); } } } if (!ret) { entry->last_matched = PRETAG_BGP_NEXTHOP; return (FALSE | entry->bgp_nexthop.neg); } else if (config.nfacctd_as & NF_AS_KEEP) return FALSE; else return (TRUE ^ entry->bgp_nexthop.neg); } int pretag_engine_type_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_char value[4]; switch(hdr->version) { case 10: { struct struct_header_ipfix *hdr = (struct struct_header_ipfix *) pptrs->f_header; memcpy(value, &hdr->source_id, 4); if (entry->engine_type.n == (u_int8_t)value[2]) return (FALSE | entry->engine_type.neg); else return (TRUE ^ entry->engine_type.neg); } case 9: { struct struct_header_v9 *hdr = (struct struct_header_v9 *) pptrs->f_header; memcpy(value, &hdr->source_id, 4); if (entry->engine_type.n == (u_int8_t)value[2]) return (FALSE | entry->engine_type.neg); else return (TRUE ^ entry->engine_type.neg); } case 8: if (entry->engine_type.n == ((struct struct_header_v8 *)pptrs->f_header)->engine_type) return (FALSE | entry->engine_type.neg); else return (TRUE ^ entry->engine_type.neg); case 5: if (entry->engine_type.n == ((struct struct_header_v5 *)pptrs->f_header)->engine_type) return (FALSE | entry->engine_type.neg); else return (TRUE ^ entry->engine_type.neg); default: return TRUE; /* this field does not exist: condition is always true */ } } int pretag_engine_id_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_char value[4]; switch(hdr->version) { case 10: { struct struct_header_ipfix *hdr = (struct struct_header_ipfix *) pptrs->f_header; memcpy(value, &hdr->source_id, 4); if (entry->engine_id.n == (u_int8_t)value[3]) return (FALSE | entry->engine_id.neg); else return (TRUE ^ entry->engine_id.neg); } case 9: { struct struct_header_v9 *hdr = (struct struct_header_v9 *) pptrs->f_header; memcpy(value, &hdr->source_id, 4); if (entry->engine_id.n == (u_int8_t)value[3]) return (FALSE | entry->engine_id.neg); else return (TRUE ^ entry->engine_id.neg); } case 8: if (entry->engine_id.n == ((struct struct_header_v8 *)pptrs->f_header)->engine_id) return (FALSE | entry->engine_id.neg); else return (TRUE ^ entry->engine_id.neg); case 5: if (entry->engine_id.n == ((struct struct_header_v5 *)pptrs->f_header)->engine_id) return (FALSE | entry->engine_id.neg); else return (TRUE ^ entry->engine_id.neg); default: return TRUE; /* this field does not exist: condition is always true */ } } int pretag_filter_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; if (bpf_filter(entry->filter.bf_insns, pptrs->packet_ptr, pptrs->pkthdr->len, pptrs->pkthdr->caplen)) return FALSE; /* matched filter */ else return TRUE; } int pretag_v8agg_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; switch(hdr->version) { case 8: if (entry->v8agg.n == ((struct struct_header_v8 *)pptrs->f_header)->aggregation) return (FALSE | entry->v8agg.neg); else return (TRUE ^ entry->v8agg.neg); default: return TRUE; /* this field does not exist: condition is always true */ } } int pretag_src_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t asn16 = 0; u_int32_t asn32 = 0; if (entry->last_matched == PRETAG_SRC_AS) return FALSE; switch(hdr->version) { case 9: if (tpl->tpl[NF9_SRC_AS].len == 2) { memcpy(&asn16, pptrs->f_data+tpl->tpl[NF9_SRC_AS].off, 2); asn32 = ntohs(asn16); } else if (tpl->tpl[NF9_SRC_AS].len == 4) { memcpy(&asn32, pptrs->f_data+tpl->tpl[NF9_SRC_AS].off, 4); asn32 = ntohl(asn32); } break; case 8: switch(hdr->aggregation) { case 1: asn32 = ntohs(((struct struct_export_v8_1 *) pptrs->f_data)->src_as); break; case 3: asn32 = ntohs(((struct struct_export_v8_3 *) pptrs->f_data)->src_as); break; case 5: asn32 = ntohs(((struct struct_export_v8_5 *) pptrs->f_data)->src_as); break; case 9: asn32 = ntohs(((struct struct_export_v8_9 *) pptrs->f_data)->src_as); break; case 11: asn32 = ntohs(((struct struct_export_v8_11 *) pptrs->f_data)->src_as); break; case 13: asn32 = ntohs(((struct struct_export_v8_13 *) pptrs->f_data)->src_as); break; default: break; } break; default: asn32 = ntohs(((struct struct_export_v5 *) pptrs->f_data)->src_as); break; } if (entry->src_as.n == asn32) return (FALSE | entry->src_as.neg); else return (TRUE ^ entry->src_as.neg); } int pretag_bgp_src_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; as_t asn = 0; if (src_ret) { info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr) { if (info->attr->aspath) { asn = evaluate_last_asn(info->attr->aspath); } } } if (entry->src_as.n == asn) { entry->last_matched = PRETAG_SRC_AS; return (FALSE | entry->src_as.neg); } else if (config.nfacctd_as & NF_AS_KEEP) return FALSE; else return (TRUE ^ entry->src_as.neg); } int pretag_dst_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t asn16 = 0; u_int32_t asn32 = 0; if (entry->last_matched == PRETAG_DST_AS) return FALSE; switch(hdr->version) { case 9: if (tpl->tpl[NF9_DST_AS].len == 2) { memcpy(&asn16, pptrs->f_data+tpl->tpl[NF9_DST_AS].off, 2); asn32 = ntohs(asn16); } else if (tpl->tpl[NF9_DST_AS].len == 4) { memcpy(&asn32, pptrs->f_data+tpl->tpl[NF9_DST_AS].off, 4); asn32 = ntohl(asn32); } break; case 8: switch(hdr->aggregation) { case 1: asn32 = ntohs(((struct struct_export_v8_1 *) pptrs->f_data)->dst_as); break; case 4: asn32 = ntohs(((struct struct_export_v8_4 *) pptrs->f_data)->dst_as); break; case 5: asn32 = ntohs(((struct struct_export_v8_5 *) pptrs->f_data)->dst_as); break; case 9: asn32 = ntohs(((struct struct_export_v8_9 *) pptrs->f_data)->dst_as); break; case 12: asn32 = ntohs(((struct struct_export_v8_12 *) pptrs->f_data)->dst_as); break; case 13: asn32 = ntohs(((struct struct_export_v8_13 *) pptrs->f_data)->dst_as); break; default: break; } break; default: asn32 = ntohs(((struct struct_export_v5 *) pptrs->f_data)->dst_as); break; } if (entry->dst_as.n == asn32) return (FALSE | entry->dst_as.neg); else return (TRUE ^ entry->dst_as.neg); } int pretag_bgp_dst_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *dst_ret = (struct bgp_node *) pptrs->bgp_dst; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; as_t asn = 0; if (dst_ret) { info = (struct bgp_info *) pptrs->bgp_dst_info; if (info && info->attr) { if (info->attr->aspath) { asn = evaluate_last_asn(info->attr->aspath); } } } if (entry->dst_as.n == asn) { entry->last_matched = PRETAG_DST_AS; return (FALSE | entry->dst_as.neg); } else if (config.nfacctd_as & NF_AS_KEEP) return FALSE; else return (TRUE ^ entry->dst_as.neg); } int pretag_peer_src_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; as_t asn = 0; if (config.nfacctd_bgp_peer_as_src_type == BGP_SRC_PRIMITIVES_MAP) { asn = pptrs->bpas; } else if (config.nfacctd_bgp_peer_as_src_type & BGP_SRC_PRIMITIVES_BGP) { if (src_ret) { info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr) { if (info->attr->aspath && info->attr->aspath->str) { asn = evaluate_first_asn(info->attr->aspath->str); } } } } if (entry->peer_src_as.n == asn) return (FALSE | entry->peer_src_as.neg); else return (TRUE ^ entry->peer_src_as.neg); } int pretag_peer_dst_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *dst_ret = (struct bgp_node *) pptrs->bgp_dst; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; as_t asn = 0; if (dst_ret) { info = (struct bgp_info *) pptrs->bgp_dst_info; if (info && info->attr) { if (info->attr->aspath && info->attr->aspath->str) { asn = evaluate_first_asn(info->attr->aspath->str); } } } if (entry->peer_dst_as.n == asn) return (FALSE | entry->peer_dst_as.neg); else return (TRUE ^ entry->peer_dst_as.neg); } int pretag_src_local_pref_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; u_int32_t local_pref = 0; if (config.nfacctd_bgp_src_local_pref_type == BGP_SRC_PRIMITIVES_MAP) { local_pref = pptrs->blp; } else if (config.nfacctd_bgp_src_local_pref_type & BGP_SRC_PRIMITIVES_BGP) { if (src_ret) { info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr) { local_pref = info->attr->local_pref; } } } if (entry->src_local_pref.n == local_pref) return (FALSE | entry->src_local_pref.neg); else return (TRUE ^ entry->src_local_pref.neg); } int pretag_local_pref_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *dst_ret = (struct bgp_node *) pptrs->bgp_dst; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; u_int32_t local_pref = 0; if (dst_ret) { info = (struct bgp_info *) pptrs->bgp_dst_info; if (info && info->attr) { local_pref = info->attr->local_pref; } } if (entry->local_pref.n == local_pref) return (FALSE | entry->local_pref.neg); else return (TRUE ^ entry->local_pref.neg); } int pretag_src_comms_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; char tmp_stdcomms[MAX_BGP_STD_COMMS]; memset(tmp_stdcomms, 0, sizeof(tmp_stdcomms)); if (src_ret) { info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr && info->attr->community && info->attr->community->str) { evaluate_comm_patterns(tmp_stdcomms, info->attr->community->str, entry->src_comms, MAX_BGP_STD_COMMS); } } if (strlen(tmp_stdcomms)) return FALSE; else return TRUE; } int pretag_comms_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *dst_ret = (struct bgp_node *) pptrs->bgp_dst; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; char tmp_stdcomms[MAX_BGP_STD_COMMS]; memset(tmp_stdcomms, 0, sizeof(tmp_stdcomms)); if (dst_ret) { info = (struct bgp_info *) pptrs->bgp_dst_info; if (info && info->attr && info->attr->community && info->attr->community->str) { evaluate_comm_patterns(tmp_stdcomms, info->attr->community->str, entry->comms, MAX_BGP_STD_COMMS); } } if (strlen(tmp_stdcomms)) return FALSE; else return TRUE; } int pretag_sampling_rate_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct struct_header_v5 *hdr5 = (struct struct_header_v5 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t srate = 0; switch (hdr->version) { case 5: hdr5 = (struct struct_header_v5 *) pptrs->f_header; srate = ( ntohs(hdr5->sampling) & 0x3FFF ); if (entry->sampling_rate.n == srate) return (FALSE | entry->sampling_rate.neg); else return (TRUE ^ entry->sampling_rate.neg); default: return TRUE; /* this field might not apply: condition is always true */ } } int pretag_direction_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct struct_header_v8 *hdr = (struct struct_header_v8 *) pptrs->f_header; struct template_cache_entry *tpl = (struct template_cache_entry *) pptrs->f_tpl; u_int16_t direction = 0; switch (hdr->version) { case 9: if (tpl->tpl[NF9_DIRECTION].len == 1) { memcpy(&direction, pptrs->f_data+tpl->tpl[NF9_DIRECTION].off, 1); } if (entry->direction.n == direction) return (FALSE | entry->direction.neg); else return (TRUE ^ entry->direction.neg); default: return TRUE; /* this field does not exist: condition is always true */ } } int pretag_mpls_vpn_rd_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *dst_ret = (struct bgp_node *) pptrs->bgp_dst; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; int ret = -1; if (dst_ret) { info = (struct bgp_info *) pptrs->bgp_dst_info; if (info && info->extra) { ret = memcmp(&entry->mpls_vpn_rd.rd, &info->extra->rd, sizeof(rd_t)); } } if (!ret) return (FALSE | entry->mpls_vpn_rd.neg); else return (TRUE ^ entry->mpls_vpn_rd.neg); } int pretag_id_handler(struct packet_ptrs *pptrs, void *id, void *e) { struct id_entry *entry = e; pm_id_t *tid = id; *tid = entry->id; if (!entry->id && entry->flags == BPAS_MAP_RCODE_BGP) { struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; if (src_ret) { info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr) { if (info->attr->aspath && info->attr->aspath->str) { *tid = evaluate_first_asn(info->attr->aspath->str); if (!(*tid) && config.nfacctd_bgp_stdcomm_pattern_to_asn) { char tmp_stdcomms[MAX_BGP_STD_COMMS]; if (info->attr->community && info->attr->community->str) { evaluate_comm_patterns(tmp_stdcomms, info->attr->community->str, std_comm_patterns_to_asn, MAX_BGP_STD_COMMS); copy_stdcomm_to_asn(tmp_stdcomms, (as_t *)tid, FALSE); } } } } } } return PRETAG_MAP_RCODE_ID; /* cap */ } int pretag_id2_handler(struct packet_ptrs *pptrs, void *id, void *e) { struct id_entry *entry = e; pm_id_t *tid = id; *tid = entry->id2; return PRETAG_MAP_RCODE_ID2; /* cap */ } int SF_pretag_input_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; SFSample *sample = (SFSample *) pptrs->f_data; if (entry->input.n == sample->inputPort) return (FALSE | entry->input.neg); else return (TRUE ^ entry->input.neg); } int SF_pretag_output_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; SFSample *sample = (SFSample *) pptrs->f_data; if (entry->output.n == sample->outputPort) return (FALSE | entry->output.neg); else return (TRUE ^ entry->output.neg); } int SF_pretag_nexthop_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; SFSample *sample = (SFSample *) pptrs->f_data; if (entry->nexthop.a.family == AF_INET) { if (!memcmp(&entry->nexthop.a.address.ipv4, &sample->nextHop.address.ip_v4, 4)) return (FALSE | entry->nexthop.neg); } #if defined ENABLE_IPV6 else if (entry->nexthop.a.family == AF_INET6) { if (!memcmp(&entry->nexthop.a.address.ipv6, &sample->nextHop.address.ip_v6, IP6AddrSz)) return (FALSE | entry->nexthop.neg); } #endif else return (TRUE ^ entry->nexthop.neg); } int SF_pretag_bgp_nexthop_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; SFSample *sample = (SFSample *) pptrs->f_data; /* If in a fallback scenario, ie. NF_AS_BGP + NF_AS_KEEP set, check BGP first */ if (config.nfacctd_as & NF_AS_BGP && pptrs->bgp_dst) return FALSE; if (entry->bgp_nexthop.a.family == AF_INET) { if (!memcmp(&entry->bgp_nexthop.a.address.ipv4, &sample->bgp_nextHop.address.ip_v4, 4)) return (FALSE | entry->bgp_nexthop.neg); } #if defined ENABLE_IPV6 else if (entry->bgp_nexthop.a.family == AF_INET6) { if (!memcmp(&entry->bgp_nexthop.a.address.ipv6, &sample->bgp_nextHop.address.ip_v6, IP6AddrSz)) return (FALSE | entry->bgp_nexthop.neg); } #endif else return (TRUE ^ entry->bgp_nexthop.neg); } int SF_pretag_agent_id_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; SFSample *sample = (SFSample *) pptrs->f_data; if (entry->agent_id.n == sample->agentSubId) return (FALSE | entry->agent_id.neg); else return (TRUE ^ entry->agent_id.neg); } int SF_pretag_sampling_rate_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; SFSample *sample = (SFSample *) pptrs->f_data; if (entry->sampling_rate.n == sample->meanSkipCount) return (FALSE | entry->sampling_rate.neg); else return (TRUE ^ entry->sampling_rate.neg); } int SF_pretag_sample_type_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; SFSample *sample = (SFSample *) pptrs->f_data; if (entry->sample_type.n == pptrs->sample_type) return (FALSE | entry->sample_type.neg); else return (TRUE ^ entry->sample_type.neg); } int SF_pretag_src_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; SFSample *sample = (SFSample *) pptrs->f_data; /* If in a fallback scenario, ie. NF_AS_BGP + NF_AS_KEEP set, check BGP first */ if (config.nfacctd_as & NF_AS_BGP && pptrs->bgp_src) return FALSE; if (entry->src_as.n == sample->src_as) return (FALSE | entry->src_as.neg); else return (TRUE ^ entry->src_as.neg); } int SF_pretag_dst_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; SFSample *sample = (SFSample *) pptrs->f_data; /* If in a fallback scenario, ie. NF_AS_BGP + NF_AS_KEEP set, check BGP first */ if (config.nfacctd_as & NF_AS_BGP && pptrs->bgp_dst) return FALSE; if (entry->dst_as.n == sample->dst_as) return (FALSE | entry->dst_as.neg); else return (TRUE ^ entry->dst_as.neg); } int PM_pretag_src_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; as_t res = search_pretag_src_as(&nt, &nc, pptrs); if (entry->src_as.n == res) return (FALSE | entry->src_as.neg); else return (TRUE ^ entry->src_as.neg); } int PM_pretag_dst_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; as_t res = search_pretag_dst_as(&nt, &nc, pptrs); if (entry->dst_as.n == res) return (FALSE | entry->dst_as.neg); else return (TRUE ^ entry->dst_as.neg); } int PM_pretag_input_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; if (entry->input.n == pptrs->ifindex_in) return (FALSE | entry->input.neg); else return (TRUE ^ entry->input.neg); } int PM_pretag_output_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; if (entry->output.n == pptrs->ifindex_out) return (FALSE | entry->output.neg); else return (TRUE ^ entry->output.neg); } pm_id_t PT_stack_sum(pm_id_t tag, pm_id_t pre) { return tag + pre; } int BPAS_bgp_nexthop_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; if (src_ret) { info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr) { if (entry->bgp_nexthop.a.family == AF_INET) { if (info->attr->mp_nexthop.family == AF_INET) { if (!memcmp(&entry->bgp_nexthop.a.address.ipv4, &info->attr->mp_nexthop.address.ipv4, 4)) return (FALSE | entry->bgp_nexthop.neg); } else { if (!memcmp(&entry->bgp_nexthop.a.address.ipv4, &info->attr->nexthop, 4)) return (FALSE | entry->bgp_nexthop.neg); } } #if defined ENABLE_IPV6 else if (entry->nexthop.a.family == AF_INET6) { if (!memcmp(&entry->bgp_nexthop.a.address.ipv6, &info->attr->mp_nexthop.address.ipv6, 16)) return (FALSE | entry->bgp_nexthop.neg); } #endif } } return (TRUE ^ entry->bgp_nexthop.neg); } int BPAS_bgp_peer_dst_as_handler(struct packet_ptrs *pptrs, void *unused, void *e) { struct id_entry *entry = e; struct bgp_node *src_ret = (struct bgp_node *) pptrs->bgp_src; struct bgp_peer *peer = (struct bgp_peer *) pptrs->bgp_peer; struct bgp_info *info; as_t asn = 0; if (src_ret) { info = (struct bgp_info *) pptrs->bgp_src_info; if (info && info->attr) { if (info->attr->aspath && info->attr->aspath->str) { asn = evaluate_first_asn(info->attr->aspath->str); if (!asn && config.nfacctd_bgp_stdcomm_pattern_to_asn) { char tmp_stdcomms[MAX_BGP_STD_COMMS]; if (info->attr->community && info->attr->community->str) { evaluate_comm_patterns(tmp_stdcomms, info->attr->community->str, std_comm_patterns_to_asn, MAX_BGP_STD_COMMS); copy_stdcomm_to_asn(tmp_stdcomms, &asn, FALSE); } } } } } if (entry->peer_dst_as.n == asn) return (FALSE | entry->peer_dst_as.neg); else return (TRUE ^ entry->peer_dst_as.neg); } pmacct-0.14.0/src/setproctitle.c0000644000175000017500000001204611266616433015540 0ustar paolopaolo/* * setproctitle()-related routines in this file are derived from Sendmail * 8.13.5 which is: * * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * */ #include "pmacct.h" /* * SETPROCTITLE -- set process title for ps */ #define SPT_NONE 0 /* don't use it at all */ #define SPT_REUSEARGV 1 /* cover argv with title information */ #define SPT_BUILTIN 2 /* use libc builtin */ #define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */ #include "setproctitle.h" #define MAXLINE (2*LONGLONGSRVBUFLEN) #define SPACELEFT(x) (sizeof(x)-strlen(x)) #if defined PROGNAME && SPT_TYPE == SPT_REUSEARGV extern char *__progname; #endif char pmacctd_globstr[] = "pmacctd\0"; char nfacctd_globstr[] = "nfacctd\0"; char sfacctd_globstr[] = "sfacctd\0"; char uacctd_globstr[] = "uacctd\0"; /* * NEWSTR -- Create a copy of a C string */ char *spt_newstr(s) const char *s; { size_t l; char *n; l = strlen(s); n = malloc(l + 1); strlcpy(n, s, l + 1); return n; } #ifndef SPT_TYPE # define SPT_TYPE SPT_NONE #endif /* ! SPT_TYPE */ #if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN #if SPT_TYPE == SPT_PSTAT # include #endif /* SPT_TYPE == SPT_PSTAT */ #ifndef SPT_PADCHAR # define SPT_PADCHAR ' ' #endif /* ! SPT_PADCHAR */ #endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */ #ifndef SPT_BUFSIZE # define SPT_BUFSIZE MAXLINE #endif /* ! SPT_BUFSIZE */ /* * Pointers for setproctitle. * This allows "ps" listings to give more useful information. */ static char **Argv = NULL; /* pointer to argument vector */ static char *LastArgv = NULL; /* end of argv */ void initsetproctitle(argc, argv, envp) int argc; char **argv; char **envp; { register int i; int align; extern char **environ; /* ** Move the environment so setproctitle can use the space at ** the top of memory. */ if (envp != NULL) { for (i = 0; envp[i] != NULL; i++) continue; environ = (char **) malloc(sizeof (char *) * (i + 1)); for (i = 0; envp[i] != NULL; i++) environ[i] = spt_newstr(envp[i]); environ[i] = NULL; } /* ** Save start and extent of argv for setproctitle. */ Argv = argv; /* ** Determine how much space we can use for setproctitle. ** Use all contiguous argv and envp pointers starting at argv[0] */ for (i = 0; i < argc; i++) { if (i == 0 || LastArgv + 1 == argv[i]) LastArgv = argv[i] + strlen(argv[i]); } for (i = 0; LastArgv != NULL && envp != NULL && envp[i] != NULL; i++) { if (LastArgv + 1 == envp[i]) LastArgv = envp[i] + strlen(envp[i]); } #if defined PROGNAME && SPT_TYPE == SPT_REUSEARGV if (config.acct_type == ACCT_PM) __progname = pmacctd_globstr; else if (config.acct_type == ACCT_NF) __progname = nfacctd_globstr; else if (config.acct_type == ACCT_SF) __progname = sfacctd_globstr; else if (config.acct_type == ACCT_UL) __progname = uacctd_globstr; #endif } #if SPT_TYPE != SPT_BUILTIN /*VARARGS1*/ static void # ifdef __STDC__ setproctitle(const char *fmt, ...) # else /* __STDC__ */ setproctitle(fmt, va_alist) const char *fmt; va_dcl # endif /* __STDC__ */ { # if SPT_TYPE != SPT_NONE register int i; register char *p; char buf[SPT_BUFSIZE]; va_list ap; # if SPT_TYPE == SPT_PSTAT union pstun pst; # endif /* SPT_TYPE == SPT_PSTAT */ p = buf; va_start(ap, fmt); vsnprintf(p, SPACELEFT(buf), fmt, ap); va_end(ap); i = (int) strlen(buf); if (i < 0) return; # if SPT_TYPE == SPT_PSTAT pst.pst_command = buf; pstat(PSTAT_SETCMD, pst, i, 0, 0); # endif /* SPT_TYPE == SPT_PSTAT */ # if SPT_TYPE == SPT_REUSEARGV if (LastArgv == NULL) return; if (i > (LastArgv - Argv[0]) - 2) { i = (LastArgv - Argv[0]) - 2; buf[i] = '\0'; } (void) strlcpy(Argv[0], buf, i + 1); p = &Argv[0][i]; while (p < LastArgv) *p++ = SPT_PADCHAR; Argv[1] = NULL; # endif /* SPT_TYPE == SPT_REUSEARGV */ # endif /* SPT_TYPE != SPT_NONE */ } #endif /* SPT_TYPE != SPT_BUILTIN */ /* * PM_SETPROCTITLE -- set process task and set process title for ps */ /*VARARGS2*/ void #ifdef __STDC__ pm_setproctitle(const char *fmt, ...) #else /* __STDC__ */ pm_setproctitle(fmt, va_alist) const char *fmt; va_dcl #endif /* __STDC__ */ { char buf[SPT_BUFSIZE]; char prefix[10]; va_list ap; memset(prefix, 0, sizeof(prefix)); memset(buf, 0, sizeof(buf)); if (config.uacctd_group) strcpy(prefix, uacctd_globstr); /* XXX: hack */ else if (config.acct_type == ACCT_PM) strcpy(prefix, pmacctd_globstr); else if (config.acct_type == ACCT_NF) strcpy(prefix, nfacctd_globstr); else if (config.acct_type == ACCT_SF) strcpy(prefix, sfacctd_globstr); va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); #if SPT_TYPE != SPT_BUILTIN setproctitle("%s: %s", prefix, buf); #else setproctitle("%s", buf); #endif } pmacct-0.14.0/src/tee_plugin/0000755000175000017500000000000011741267746015012 5ustar paolopaolopmacct-0.14.0/src/tee_plugin/tee_plugin.c0000644000175000017500000002530511373033502017274 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2010 by Paolo Lucente */ /* 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. */ #define __TEE_PLUGIN_C #include "../pmacct.h" #include "tee_plugin.h" #include "pmacct-data.h" #include "plugin_hooks.h" void tee_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_msg *msg; unsigned char *pipebuf; struct pollfd pfd; int timeout, err; int ret, num, fd; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; char *dataptr, dest_addr[256], dest_serv[256]; struct sockaddr dest; socklen_t dest_len; unsigned char *rgptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; /* XXX: glue */ memcpy(&config, cfgptr, sizeof(struct configuration)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "Tee Plugin", config.name); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); /* signal handling */ signal(SIGINT, Tee_exit_now); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); signal(SIGPIPE, SIG_IGN); #if !defined FBSD4 signal(SIGCHLD, SIG_IGN); #else signal(SIGCHLD, ignore_falling_child); #endif if (config.tee_transparent && getuid() != 0) { Log(LOG_ERR, "ERROR ( %s/%s ): Transparent mode requires super-user permissions. Exiting ...\n", config.name, config.type); exit_plugin(1); } if (!config.nfprobe_receiver) { Log(LOG_ERR, "ERROR ( %s/%s ): tee_receiver is not specified. Exiting ...\n", config.name, config.type); exit_plugin(1); } memset(&dest, 0, sizeof(dest)); dest_len = sizeof(dest); Tee_parse_hostport(config.nfprobe_receiver, (struct sockaddr *)&dest, &dest_len); config.print_refresh_time = DEFAULT_TEE_REFRESH_TIME; timeout = config.print_refresh_time*1000; pipebuf = (unsigned char *) Malloc(config.buffer_size); pfd.fd = pipe_fd; pfd.events = POLLIN; setnonblocking(pipe_fd); memset(pipebuf, 0, config.buffer_size); /* Arrange send socket */ if (dest.sa_family != 0) { if ((err = getnameinfo((struct sockaddr *) &dest, dest_len, dest_addr, sizeof(dest_addr), dest_serv, sizeof(dest_serv), NI_NUMERICHOST)) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): getnameinfo: %d\n", config.name, config.type, err); exit_plugin(1); } fd = Tee_prepare_sock(&dest, dest_len); } /* plugin main loop */ for (;;) { poll_again: status->wakeup = TRUE; ret = poll(&pfd, 1, timeout); if (ret < 0) goto poll_again; switch (ret) { case 0: /* timeout */ /* reserved for future since we don't currently cache/batch/etc */ break; default: /* we received data */ read_data: if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; goto poll_again; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%u'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%u'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); } seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); if ((rg->ptr+bufsz) >= rg->end) rg->ptr = rg->base; else rg->ptr += bufsz; msg = (struct pkt_msg *) (pipebuf+sizeof(struct ch_buf_hdr)); while (((struct ch_buf_hdr *)pipebuf)->num) { Tee_send(msg, &dest, fd); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) msg; dataptr += PmsgSz; msg = (struct pkt_msg *) dataptr; } } goto read_data; } } } void Tee_exit_now(int signum) { wait(NULL); exit_plugin(0); } void Tee_send(struct pkt_msg *msg, struct sockaddr *target, int fd) { if (config.debug) { struct host_addr a; u_char agent_addr[50]; u_int16_t agent_port; sa_to_addr((struct sockaddr *)msg, &a, &agent_port); addr_to_str(agent_addr, &a); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Sending NetFlow packet from [%s:%u] seqno [%u] to [%s]\n", config.name, config.type, agent_addr, agent_port, msg->seqno, config.nfprobe_receiver); } if (!config.tee_transparent) { if (send(fd, msg->payload, msg->len, 0) == -1) Log(LOG_ERR, "ERROR ( %s/%s ): send() to [%s] failed (%s)\n", config.name, config.type, config.nfprobe_receiver, strerror(errno)); } else { char *buf_ptr = tee_send_buf; struct sockaddr_in *sa = (struct sockaddr_in *) &msg->agent; struct my_iphdr *i4h = (struct my_iphdr *) buf_ptr; #if defined ENABLE_IPV6 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &msg->agent; struct ip6_hdr *i6h = (struct ip6_hdr *) buf_ptr; #endif struct my_udphdr *uh; if (msg->agent.sa_family == target->sa_family) { /* UDP header first */ if (target->sa_family == AF_INET) { buf_ptr += IP4HdrSz; uh = (struct my_udphdr *) buf_ptr; uh->uh_sport = sa->sin_port; uh->uh_dport = ((struct sockaddr_in *)target)->sin_port; } #if defined ENABLE_IPV6 else if (target->sa_family == AF_INET6) { buf_ptr += IP6HdrSz; uh = (struct my_udphdr *) buf_ptr; uh->uh_sport = sa6->sin6_port; uh->uh_dport = ((struct sockaddr_in6 *)target)->sin6_port; } #endif uh->uh_ulen = htons(msg->len+UDPHdrSz); uh->uh_sum = 0; /* IP header then */ if (target->sa_family == AF_INET) { i4h->ip_vhl = 4; i4h->ip_vhl <<= 4; i4h->ip_vhl |= (IP4HdrSz/4); i4h->ip_tos = 0; i4h->ip_len = htons(IP4HdrSz+UDPHdrSz+msg->len); i4h->ip_id = 0; i4h->ip_off = 0; i4h->ip_ttl = 255; i4h->ip_p = IPPROTO_UDP; i4h->ip_sum = 0; i4h->ip_src.s_addr = sa->sin_addr.s_addr; i4h->ip_dst.s_addr = ((struct sockaddr_in *)target)->sin_addr.s_addr; } #if defined ENABLE_IPV6 else if (target->sa_family == AF_INET6) { i6h->ip6_vfc = 6; i6h->ip6_vfc <<= 4; i6h->ip6_plen = htons(UDPHdrSz+msg->len); i6h->ip6_nxt = IPPROTO_UDP; i6h->ip6_hlim = 255; memcpy(&i6h->ip6_src, &sa6->sin6_addr, IP6AddrSz); memcpy(&i6h->ip6_dst, &((struct sockaddr_in6 *)target)->sin6_addr, IP6AddrSz); } #endif /* Put everything together and send */ buf_ptr += UDPHdrSz; memcpy(buf_ptr, msg->payload, msg->len); if (send(fd, tee_send_buf, IP4HdrSz+UDPHdrSz+msg->len, 0) == -1) Log(LOG_ERR, "ERROR ( %s/%s ): raw send() to [%s] failed (%s)\n", config.name, config.type, config.nfprobe_receiver, strerror(errno)); } else { Log(LOG_ERR, "ERROR ( %s/%s ): Can't bridge Address Families when in transparent mode. Exiting ...\n", config.name, config.type); exit_plugin(1); } } } int Tee_prepare_sock(struct sockaddr *addr, socklen_t len) { int s, ret = 0; if (!config.tee_transparent) { struct host_addr source_ip; struct sockaddr ssource_ip; if (config.nfprobe_source_ip) { ret = str_to_addr(config.nfprobe_source_ip, &source_ip); addr_to_sa(&ssource_ip, &source_ip, 0); } if ((s = socket(addr->sa_family, SOCK_DGRAM, 0)) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): socket() error: %s\n", config.name, config.type, strerror(errno)); exit_plugin(1); } if (ret && bind(s, (struct sockaddr *) &ssource_ip, sizeof(ssource_ip)) == -1) Log(LOG_ERR, "ERROR ( %s/%s ): bind() error: %s\n", config.name, config.type, strerror(errno)); } else { if ((s = socket(addr->sa_family, SOCK_RAW, IPPROTO_RAW)) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): socket() error: %s\n", config.name, config.type, strerror(errno)); exit_plugin(1); } } /* XXX: SNDBUF tuning? */ if (connect(s, (struct sockaddr *)addr, len) == -1) { Log(LOG_ERR, "ERROR ( %s/%s ): connect() error: %s\n", config.name, config.type, strerror(errno)); exit_plugin(1); } return(s); } // XXX: duplicate function void Tee_parse_hostport(const char *s, struct sockaddr *addr, socklen_t *len) { char *orig, *host, *port; struct addrinfo hints, *res; int herr; if ((host = orig = strdup(s)) == NULL) { fprintf(stderr, "Out of memory\n"); exit_plugin(1); } trim_spaces(host); trim_spaces(orig); if ((port = strrchr(host, ':')) == NULL || *(++port) == '\0' || *host == '\0') { fprintf(stderr, "Invalid -n argument.\n"); exit_plugin(1); } *(port - 1) = '\0'; /* Accept [host]:port for numeric IPv6 addresses */ if (*host == '[' && *(port - 2) == ']') { host++; *(port - 2) = '\0'; } memset(&hints, '\0', sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; if ((herr = getaddrinfo(host, port, &hints, &res)) == -1) { fprintf(stderr, "Address lookup failed: %s\n", gai_strerror(herr)); exit_plugin(1); } if (res == NULL || res->ai_addr == NULL) { fprintf(stderr, "No addresses found for [%s]:%s\n", host, port); exit_plugin(1); } if (res->ai_addrlen > *len) { Log(LOG_ERR, "ERROR ( %s/%s ): Address too long.\n", config.name, config.type); exit_plugin(1); } memcpy(addr, res->ai_addr, res->ai_addrlen); free(orig); *len = res->ai_addrlen; } pmacct-0.14.0/src/tee_plugin/Makefile.in0000644000175000017500000000133611370001406017033 0ustar paolopaolo# $Id: Makefile.in,v 1.1 2010/05/04 11:20:38 paolo Exp $ prefix=@prefix@ exec_prefix=@exec_prefix@ bindir=@bindir@ sbindir=@sbindir@ libexecdir=@libexecdir@ datadir=@datadir@ mandir=@mandir@ sysconfdir=@sysconfdir@ srcdir=@srcdir@ top_srcdir=@top_srcdir@ VPATH=@srcdir@ CC=@CC@ DEFS=@DEFS@ LDFLAGS=@LDFLAGS@ CFLAGS=$(DEFS) -I$(srcdir) -I.. @CFLAGS@ CPPFLAGS=@CPPFLAGS@ LIBS=@LIBS@ INSTALL=@INSTALL@ RANLIB=@RANLIB@ TARGETS=libtee_plugin.a all: $(TARGETS) libtee_plugin.a: tee_plugin.o ar rc $@ tee_plugin.o $(RANLIB) $@ clean: rm -f $(TARGETS) *.o core *.core realclean: clean rm -rf autom4te.cache Makefile config.log config.status distclean: realclean rm -f config.h* configure strip: strip $(TARGETS) install: all pmacct-0.14.0/src/tee_plugin/tee_plugin.h0000644000175000017500000000246011373033502017276 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2010 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* includes */ #include #include #include /* defines */ #define DEFAULT_TEE_REFRESH_TIME 10 /* prototypes */ #if (!defined __TEE_PLUGIN_C) #define EXT extern #else #define EXT #endif EXT void Tee_exit_now(int); EXT void Tee_send(struct pkt_msg *, struct sockaddr *, int); EXT int Tee_prepare_sock(struct sockaddr *, socklen_t); EXT void Tee_parse_hostport(const char *, struct sockaddr *, socklen_t *); EXT char tee_send_buf[65535]; #undef EXT pmacct-0.14.0/src/ip_frag.c0000644000175000017500000003704311370307430014420 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2010 by Paolo Lucente */ /* 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 no, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define __IP_FRAG_C /* includes */ #include "pmacct.h" #include "pmacct-data.h" #include "plugin_hooks.h" #include "ip_frag.h" #include "jhash.h" u_int32_t ipft_total_nodes; time_t prune_deadline; time_t emergency_prune; u_int32_t trivial_hash_rnd = 140281; /* ummmh */ #if defined ENABLE_IPV6 u_int32_t ipft6_total_nodes; time_t prune_deadline6; time_t emergency_prune6; #endif void init_ip_fragment_handler() { init_ip4_fragment_handler(); #if defined ENABLE_IPV6 init_ip6_fragment_handler(); #endif } void init_ip4_fragment_handler() { if (config.frag_bufsz) ipft_total_nodes = config.frag_bufsz / sizeof(struct ip_fragment); else ipft_total_nodes = DEFAULT_FRAG_BUFFER_SIZE / sizeof(struct ip_fragment); memset(ipft, 0, sizeof(ipft)); lru_list.root = (struct ip_fragment *) malloc(sizeof(struct ip_fragment)); lru_list.last = lru_list.root; memset(lru_list.root, 0, sizeof(struct ip_fragment)); prune_deadline = time(NULL)+PRUNE_INTERVAL; emergency_prune = 0; } int ip_fragment_handler(struct packet_ptrs *pptrs) { u_int32_t now = time(NULL); if (now > prune_deadline) { prune_old_fragments(now, PRUNE_OFFSET); prune_deadline = now+PRUNE_INTERVAL; } return find_fragment(now, pptrs); } int find_fragment(u_int32_t now, struct packet_ptrs *pptrs) { struct my_iphdr *iphp = (struct my_iphdr *)pptrs->iph_ptr; struct ip_fragment *fp, *candidate = NULL, *last_seen = NULL; unsigned int bucket = hash_fragment(iphp->ip_id, iphp->ip_src.s_addr, iphp->ip_dst.s_addr, iphp->ip_p); for (fp = ipft[bucket]; fp; fp = fp->next) { if (fp->ip_id == iphp->ip_id && fp->ip_src == iphp->ip_src.s_addr && fp->ip_dst == iphp->ip_dst.s_addr && fp->ip_p == iphp->ip_p) { /* fragment found; will check for its deadline */ if (fp->deadline > now) { if (fp->got_first) { // pptrs->tlh_ptr = fp->tlhdr; memcpy(pptrs->tlh_ptr, fp->tlhdr, MyTLHdrSz); return TRUE; } else { if (!(iphp->ip_off & htons(IP_OFFMASK))) { /* we got our first fragment */ fp->got_first = TRUE; memcpy(fp->tlhdr, pptrs->tlh_ptr, MyTLHdrSz); fp->a += ntohs(iphp->ip_len); iphp->ip_len = htons(fp->a); pptrs->pf = fp->pa; fp->pa = 0; fp->a = 0; return TRUE; } else { /* we still don't have the first fragment; increase accumulators */ if (!config.ext_sampling_rate) { fp->pa++; fp->a += ntohs(iphp->ip_len); } return FALSE; } } } else { candidate = fp; if (!candidate->got_first) notify_orphan_fragment(candidate); goto create; } } if ((fp->deadline < now) && !candidate) { candidate = fp; if (!candidate->got_first) notify_orphan_fragment(candidate); } last_seen = fp; } create: if (candidate) return create_fragment(now, candidate, TRUE, bucket, pptrs); else return create_fragment(now, last_seen, FALSE, bucket, pptrs); } int create_fragment(u_int32_t now, struct ip_fragment *fp, u_int8_t is_candidate, unsigned int bucket, struct packet_ptrs *pptrs) { struct my_iphdr *iphp = (struct my_iphdr *)pptrs->iph_ptr; struct ip_fragment *newf; if (!ipft_total_nodes) { if (now > emergency_prune+EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Fragment/4 buffer full. Skipping fragments.\n"); emergency_prune = now; prune_old_fragments(now, 0); } return FALSE; } if (fp) { /* a 'not candidate' is simply the tail (last node) of the list. We need to allocate a new node */ if (!is_candidate) { newf = (struct ip_fragment *) malloc(sizeof(struct ip_fragment)); if (!newf) { if (now > emergency_prune+EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Fragment/4 buffer full. Skipping fragments.\n"); emergency_prune = now; prune_old_fragments(now, 0); } return FALSE; } else ipft_total_nodes--; memset(newf, 0, sizeof(struct ip_fragment)); fp->next = newf; newf->prev = fp; lru_list.last->lru_next = newf; /* placing new node as LRU tail */ newf->lru_prev = lru_list.last; lru_list.last = newf; fp = newf; } else { if (fp->lru_next) { /* if fp->lru_next==NULL the node is already the tail */ fp->lru_prev->lru_next = fp->lru_next; fp->lru_next->lru_prev = fp->lru_prev; lru_list.last->lru_next = fp; fp->lru_prev = lru_list.last; fp->lru_next = NULL; lru_list.last = fp; } } } else { /* we don't have any fragment pointer; this is because current bucket doesn't contain any node; we'll allocate first one */ fp = (struct ip_fragment *) malloc(sizeof(struct ip_fragment)); if (!fp) { if (now > emergency_prune+EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Fragment/4 buffer full. Skipping fragments.\n"); emergency_prune = now; prune_old_fragments(now, 0); } return FALSE; } else ipft_total_nodes--; memset(fp, 0, sizeof(struct ip_fragment)); ipft[bucket] = fp; lru_list.last->lru_next = fp; /* placing new node as LRU tail */ fp->lru_prev = lru_list.last; lru_list.last = fp; } fp->deadline = now+IPF_TIMEOUT; fp->ip_id = iphp->ip_id; fp->ip_p = iphp->ip_p; fp->ip_src = iphp->ip_src.s_addr; fp->ip_dst = iphp->ip_dst.s_addr; fp->bucket = bucket; if (!(iphp->ip_off & htons(IP_OFFMASK))) { /* it's a first fragment */ fp->got_first = TRUE; memcpy(fp->tlhdr, pptrs->tlh_ptr, MyTLHdrSz); return TRUE; } else { /* not a first fragment; increase accumulators */ if (!config.ext_sampling_rate) { fp->pa++; fp->a = ntohs(iphp->ip_len); } return FALSE; } } void prune_old_fragments(u_int32_t now, u_int32_t off) { struct ip_fragment *fp, *temp; u_int32_t deadline = now-off; fp = lru_list.root->lru_next; while (fp) { if (deadline > fp->deadline) { /* we found a stale element; we'll prune it */ if (fp->lru_next) temp = fp->lru_next; else temp = NULL; /* rearranging bucket's pointers */ if (fp->prev && fp->next) { fp->prev->next = fp->next; fp->next->prev = fp->prev; } else if (fp->prev) fp->prev->next = NULL; else if (fp->next) { ipft[fp->bucket] = fp->next; fp->next->prev = NULL; } else ipft[fp->bucket] = NULL; free(fp); ipft_total_nodes++; if (temp) fp = temp; else fp = NULL; } else break; } if (fp) { fp->lru_prev = lru_list.root; lru_list.root->lru_next = fp; } else lru_list.last = lru_list.root; } /* hash_fragment() is taken (it has another name there) from Linux kernel 2.4; see full credits contained in jhash.h */ unsigned int hash_fragment(u_int16_t id, u_int32_t src, u_int32_t dst, u_int8_t proto) { return jhash_3words((u_int32_t)id << 16 | proto, src, dst, trivial_hash_rnd) & (IPFT_HASHSZ-1); } void notify_orphan_fragment(struct ip_fragment *frag) { struct host_addr a; u_char src_host[INET_ADDRSTRLEN], dst_host[INET_ADDRSTRLEN]; u_int16_t id; a.family = AF_INET; memcpy(&a.address.ipv4, &frag->ip_src, 4); addr_to_str(src_host, &a); memcpy(&a.address.ipv4, &frag->ip_dst, 4); addr_to_str(dst_host, &a); id = ntohs(frag->ip_id); Log(LOG_DEBUG, "DEBUG ( default/core ): Expiring orphan fragment: ip_src=%s ip_dst=%s proto=%u id=%u\n", src_host, dst_host, frag->ip_p, id); } #if defined ENABLE_IPV6 void init_ip6_fragment_handler() { if (config.frag_bufsz) ipft6_total_nodes = config.frag_bufsz / sizeof(struct ip6_fragment); else ipft6_total_nodes = DEFAULT_FRAG_BUFFER_SIZE / sizeof(struct ip6_fragment); memset(ipft6, 0, sizeof(ipft6)); lru_list6.root = (struct ip6_fragment *) malloc(sizeof(struct ip6_fragment)); lru_list6.last = lru_list6.root; memset(lru_list6.root, 0, sizeof(struct ip6_fragment)); prune_deadline6 = time(NULL)+PRUNE_INTERVAL; emergency_prune6 = 0; } int ip6_fragment_handler(struct packet_ptrs *pptrs, struct ip6_frag *fhdr) { u_int32_t now = time(NULL); if (now > prune_deadline6) { prune_old_fragments6(now, PRUNE_OFFSET); prune_deadline6 = now+PRUNE_INTERVAL; } return find_fragment6(now, pptrs, fhdr); } unsigned int hash_fragment6(u_int32_t id, struct in6_addr *saddr, struct in6_addr *daddr) { u_int32_t a, b, c; u_int32_t *src = (u_int32_t *)saddr, *dst = (u_int32_t *)daddr; a = src[0]; b = src[1]; c = src[2]; a += JHASH_GOLDEN_RATIO; b += JHASH_GOLDEN_RATIO; c += trivial_hash_rnd; __jhash_mix(a, b, c); a += src[3]; b += dst[0]; c += dst[1]; __jhash_mix(a, b, c); a += dst[2]; b += dst[3]; c += id; __jhash_mix(a, b, c); return c & (IPFT_HASHSZ - 1); } int find_fragment6(u_int32_t now, struct packet_ptrs *pptrs, struct ip6_frag *fhdr) { struct ip6_hdr *iphp = (struct ip6_hdr *)pptrs->iph_ptr; struct ip6_fragment *fp, *candidate = NULL, *last_seen = NULL; unsigned int bucket = hash_fragment6(fhdr->ip6f_ident, &iphp->ip6_src, &iphp->ip6_dst); for (fp = ipft6[bucket]; fp; fp = fp->next) { if (fp->id == fhdr->ip6f_ident && !ip6_addr_cmp(&fp->src, &iphp->ip6_src) && !ip6_addr_cmp(&fp->dst, &iphp->ip6_dst)) { /* fragment found; will check for its deadline */ if (fp->deadline > now) { if (fp->got_first) { // pptrs->tlh_ptr = fp->tlhdr; memcpy(pptrs->tlh_ptr, fp->tlhdr, MyTLHdrSz); return TRUE; } else { if (!(fhdr->ip6f_offlg & htons(IP6F_OFF_MASK))) { /* we got our first fragment */ fp->got_first = TRUE; memcpy(fp->tlhdr, pptrs->tlh_ptr, MyTLHdrSz); fp->a += ntohs(iphp->ip6_plen); /* IPv6 Header length will be added later */ iphp->ip6_plen = htons(fp->a); pptrs->pf = fp->pa; fp->pa = 0; fp->a = 0; return TRUE; } else { /* we still don't have the first fragment; increase accumulators */ if (!config.ext_sampling_rate) { fp->pa++; fp->a += IP6HdrSz+ntohs(iphp->ip6_plen); } return FALSE; } } } else { candidate = fp; if (!candidate->got_first) notify_orphan_fragment6(candidate); goto create; } } if ((fp->deadline < now) && !candidate) { candidate = fp; if (!candidate->got_first) notify_orphan_fragment6(candidate); } last_seen = fp; } create: if (candidate) return create_fragment6(now, candidate, TRUE, bucket, pptrs, fhdr); else return create_fragment6(now, last_seen, FALSE, bucket, pptrs, fhdr); } int create_fragment6(u_int32_t now, struct ip6_fragment *fp, u_int8_t is_candidate, unsigned int bucket, struct packet_ptrs *pptrs, struct ip6_frag *fhdr) { struct ip6_hdr *iphp = (struct ip6_hdr *)pptrs->iph_ptr; struct ip6_fragment *newf; if (!ipft6_total_nodes) { if (now > emergency_prune6+EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Fragment/6 buffer full. Skipping fragments.\n"); emergency_prune6 = now; prune_old_fragments6(now, 0); } return FALSE; } if (fp) { /* a 'not candidate' is simply the tail (last node) of the list. We need to allocate a new node */ if (!is_candidate) { newf = (struct ip6_fragment *) malloc(sizeof(struct ip6_fragment)); if (!newf) { if (now > emergency_prune6+EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Fragment/6 buffer full. Skipping fragments.\n"); emergency_prune6 = now; prune_old_fragments6(now, 0); } return FALSE; } else ipft6_total_nodes--; memset(newf, 0, sizeof(struct ip6_fragment)); fp->next = newf; newf->prev = fp; lru_list6.last->lru_next = newf; /* placing new node as LRU tail */ newf->lru_prev = lru_list6.last; lru_list6.last = newf; fp = newf; } else { if (fp->lru_next) { /* if fp->lru_next==NULL the node is already the tail */ fp->lru_prev->lru_next = fp->lru_next; fp->lru_next->lru_prev = fp->lru_prev; lru_list6.last->lru_next = fp; fp->lru_prev = lru_list6.last; fp->lru_next = NULL; lru_list6.last = fp; } } } else { /* we don't have any fragment pointer; this is because current bucket doesn't contain any node; we'll allocate first one */ fp = (struct ip6_fragment *) malloc(sizeof(struct ip6_fragment)); if (!fp) { if (now > emergency_prune6+EMER_PRUNE_INTERVAL) { Log(LOG_INFO, "INFO ( default/core ): Fragment/6 buffer full. Skipping fragments.\n"); emergency_prune6 = now; prune_old_fragments6(now, 0); } return FALSE; } else ipft6_total_nodes--; memset(fp, 0, sizeof(struct ip6_fragment)); ipft6[bucket] = fp; lru_list6.last->lru_next = fp; /* placing new node as LRU tail */ fp->lru_prev = lru_list6.last; lru_list6.last = fp; } fp->deadline = now+IPF_TIMEOUT; fp->id = fhdr->ip6f_ident; ip6_addr_cpy(&fp->src, &iphp->ip6_src); ip6_addr_cpy(&fp->dst, &iphp->ip6_dst); fp->bucket = bucket; if (!(fhdr->ip6f_offlg & htons(IP6F_OFF_MASK))) { /* it's a first fragment */ fp->got_first = TRUE; memcpy(fp->tlhdr, pptrs->tlh_ptr, MyTLHdrSz); return TRUE; } else { /* not a first fragment; increase accumulators */ if (!config.ext_sampling_rate) { fp->pa++; fp->a = IP6HdrSz+ntohs(iphp->ip6_plen); } return FALSE; } } void prune_old_fragments6(u_int32_t now, u_int32_t off) { struct ip6_fragment *fp, *temp; u_int32_t deadline = now-off; fp = lru_list6.root->lru_next; while (fp) { if (deadline > fp->deadline) { /* we found a stale element; we'll prune it */ if (fp->lru_next) temp = fp->lru_next; else temp = NULL; /* rearranging bucket's pointers */ if (fp->prev && fp->next) { fp->prev->next = fp->next; fp->next->prev = fp->prev; } else if (fp->prev) fp->prev->next = NULL; else if (fp->next) { ipft6[fp->bucket] = fp->next; fp->next->prev = NULL; } else ipft6[fp->bucket] = NULL; free(fp); ipft6_total_nodes++; if (temp) fp = temp; else fp = NULL; } else break; } if (fp) { fp->lru_prev = lru_list6.root; lru_list6.root->lru_next = fp; } else lru_list6.last = lru_list6.root; } void notify_orphan_fragment6(struct ip6_fragment *frag) { struct host_addr a; u_char src_host[INET6_ADDRSTRLEN], dst_host[INET6_ADDRSTRLEN]; u_int32_t id; a.family = AF_INET6; ip6_addr_cpy(&a.address.ipv6, &frag->src); addr_to_str(src_host, &a); ip6_addr_cpy(&a.address.ipv6, &frag->dst); addr_to_str(dst_host, &a); id = ntohl(frag->id); Log(LOG_DEBUG, "DEBUG ( default/core ): Expiring orphan fragment: ip_src=%s ip_dst=%s id=%u\n", src_host, dst_host, id); } #endif pmacct-0.14.0/src/mpls.h0000644000175000017500000000073710530072467013777 0ustar paolopaolo#define LABEL_MASK 0xfffff000 #define LABEL_SHIFT 12 #define EXP_MASK 0x00000e00 #define EXP_SHIFT 9 #define STACK_MASK 0x00000100 #define STACK_SHIFT 8 #define TTL_MASK 0x000000ff #define TTL_SHIFT 0 #define MPLS_LABEL(x) (((x) & LABEL_MASK) >> LABEL_SHIFT) #define MPLS_EXP(x) (((x) & EXP_MASK) >> EXP_SHIFT) #define MPLS_STACK(x) (((x) & STACK_MASK) >> STACK_SHIFT) #define MPLS_TTL(x) (((x) & TTL_MASK) >> TTL_SHIFT) pmacct-0.14.0/src/print_plugin.h0000644000175000017500000000537611654731437015551 0ustar paolopaolo/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente */ /* 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. */ /* includes */ #include /* defines */ #define DEFAULT_PRINT_REFRESH_TIME 10 #define AVERAGE_CHAIN_LEN 10 #define PRINT_CACHE_ENTRIES 16411 /* structures */ struct scratch_area { unsigned char *base; unsigned char *ptr; int num; int size; struct scratch_area *next; }; struct chained_cache { struct pkt_primitives primitives; pm_counter_t bytes_counter; pm_counter_t packet_counter; pm_counter_t flow_counter; u_int32_t tcp_flags; struct pkt_bgp_primitives *pbgp; int valid; struct chained_cache *next; }; /* prototypes */ #if (!defined __PRINT_PLUGIN_C) #define EXT extern #else #define EXT #endif EXT void print_plugin(int, struct configuration *, void *); EXT struct chained_cache *P_cache_attach_new_node(struct chained_cache *); EXT unsigned int P_cache_modulo(struct pkt_primitives *, struct pkt_bgp_primitives *); EXT void P_sum_host_insert(struct pkt_data *, struct pkt_bgp_primitives *); EXT void P_sum_port_insert(struct pkt_data *, struct pkt_bgp_primitives *); EXT void P_sum_as_insert(struct pkt_data *, struct pkt_bgp_primitives *); #if defined (HAVE_L2) EXT void P_sum_mac_insert(struct pkt_data *, struct pkt_bgp_primitives *); #endif EXT struct chained_cache *P_cache_search(struct pkt_primitives *, struct pkt_bgp_primitives *); EXT void P_cache_insert(struct pkt_data *, struct pkt_bgp_primitives *); EXT void P_cache_flush(struct chained_cache *[], int); EXT void P_cache_purge(struct chained_cache *[], int); EXT void P_write_stats_header_formatted(FILE *); EXT void P_write_stats_header_csv(FILE *); EXT void P_exit_now(int); EXT int P_trigger_exec(char *); /* global vars */ EXT void (*insert_func)(struct pkt_data *, struct pkt_bgp_primitives *); /* pointer to INSERT function */ EXT struct scratch_area sa; EXT struct chained_cache *cache; EXT struct chained_cache **queries_queue; EXT struct timeval flushtime; EXT int qq_ptr, pp_size, pb_size, dbc_size, quit; EXT time_t refresh_deadline; #undef EXT pmacct-0.14.0/INSTALL0000644000175000017500000001734611233347331013115 0ustar paolopaolopmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente QUICK INSTALLATION: 1) tar xvfz -x.y.z.tar.gz (*) 2) cd -x.y.z 3) ./configure (./configure --help for help) 4) make 5) make install (you must be root if you want to install in default prefix) Read accepted options with " -h" and run with " [options]". (*) x.y.z is the release version. The rationale behind pmacct compilation is that by default all features are turned off (IPv6, MySQL, PostgreSQL, SQLite, multi-threading, 64bit counters) making the package dependant only on a working libpcap. So if you need any of these don't forget to turn them on manually. Some features will get turned on by default in future once they will pick up sufficient ground (ie. IPv6) or will be sufficiently tested to be considered stable (ie. multi-threading). BASIC INSTALLATION: These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.in' is used to create `configure' by a program called `autoconf'. You only need `configure.in' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. COMPILERS AND OPTIONS: Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure COMPILING FOR MULTIPLE ARCHITECTURES: You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. INSTALLATION NAMES: By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. SPECIFYING THE SYSTEM TYPE: There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. SHARING DEFAULTS: If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. OPERATION CONTROLS: `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. pmacct-0.14.0/KNOWN-BUGS0000644000175000017500000000015211037474162013431 0ustar paolopaolopmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2008 by Paolo Lucente None. pmacct-0.14.0/EXAMPLES0000644000175000017500000012075511740524665013236 0ustar paolopaolopmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2011 by Paolo Lucente TABLE OF CONTENTS: I. Plugins included with pmacct distribution II. Configuring pmacct for compilation III. Brief SQL (MySQL, PostgreSQL, SQLite 3.x) setup examples IV. Running the libpcap-based daemon (pmacctd) V. Running the NetFlow and sFlow daemons (nfacctd/sfacctd) VI. Running the ULOG-based daemon (uacctd) VII. Running the pmacct client (pmacct) VIII. Running the logfile players (pmmyplay/pmpgplay) IX. Quickstart guide to packet/stream classifiers X. Quickstart guide to setup a NetFlow agent/probe XI. Quickstart guide to setup a sFlow agent/probe XII. Quickstart guide to setup the BGP daemon XIII. Quickstart guide to setup a NetFlow/sFlow replicator XIV. Quickstart guide to setup the IS-IS daemon XV. Running the print plugin to write to flat-files I. Plugins included with pmacct distribution Given its open and pluggable architecture, pmacct is easily extensible by writing new plugins. Here is a list of plugins included within the official pmacct distribution. 'memory': data are stored in a memory table and can be fetched via the pmacct client tool, 'pmacct'. It also allows easily data injection into 3rd party tools like GNUplot, MRTG, RRDtool or a Net-SNMP server 'mysql': a working MySQL installation can be used for data storage 'pgsql': a working PostgreSQL installation can be used for data storage 'sqlite3': a working SQLite 3.x or BerkeleyDB 5.x (compiled in with the SQLite API) installation can be used for data storage 'print': data are printed at regular intervals to flat-files or standard output II. Configuring pmacct for compilation The simplest way to configure the package for compilation is to let the configure script to probe default headers and libraries for you. By default SQL plugins are not enabled; much like multi-threading (pre-requisite for compiling the BGP daemon code), IPv6 support and 64 bits counters. A few examples will follow; as usual to get the list of available switches, you can use the following command-line: shell> ./configure --help Examples on how to enable the support for (1) MySQL, (2) PostgreSQL, (3) SQLite and any (4) mixed compilation: (1) shell> ./configure --enable-mysql (2) shell> ./configure --enable-pgsql (3) shell> ./configure --enable-sqlite3 (4) shell> ./configure --enable-mysql --enable-pgsql III. Brief SQL setup examples Scripts for setting up databases (MySQL, PostgreSQL and SQLite) are into the 'sql/' tree. For further guidance read the relevant README files in such directory. One of the crucial concepts to deal with, when using default IP or BGP SQL tables, is table versioning: please read more about it in the FAQS document (Q16). IIIa. MySQL examples shell> cd sql/ - To create v1 tables: shell> mysql -u root -p < pmacct-create-db_v1.mysql shell> mysql -u root -p < pmacct-grant-db.mysql Data will be available in 'acct' table of 'pmacct' DB. - To create v2 tables: shell> mysql -u root -p < pmacct-create-db_v2.mysql shell> mysql -u root -p < pmacct-grant-db.mysql Data will be available in 'acct_v2' table of 'pmacct' DB. ... And so on for the newer versions. IIIb. PostgreSQL examples Which user has to execute the following two scripts and how to autenticate with the PostgreSQL server depends upon your current configuration. Keep in mind that both scripts need postgres superuser permissions to execute some commands successfully: shell> cp -p *.pgsql /tmp shell> su - postgres To create v1 tables: shell> psql -d template1 -f /tmp/pmacct-create-db.pgsql shell> psql -d pmacct -f /tmp/pmacct-create-table_v1.pgsql To create v2 tables: shell> psql -d template1 -f /tmp/pmacct-create-db.pgsql shell> psql -d pmacct -f /tmp/pmacct-create-table_v2.pgsql ... And so on for the newer versions. A few tables will be created into 'pmacct' DB. 'acct' ('acct_v2' or 'acct_v3') table is the default table where data will be written when in 'typed' mode (see 'sql_data' option in CONFIG-KEYS document; default value is 'typed'); 'acct_uni' ('acct_uni_v2' or 'acct_uni_v3') is the default table where data will be written when in 'unified' mode. Since v6, PostgreSQL tables are greatly simplified: unified mode is no longer supported and an unique table ('acct_v6', for example) is created instead. IIIc. SQLite examples shell> cd sql/ - To create v1 tables: shell> sqlite3 /tmp/pmacct.db < pmacct-create-table.sqlite3 Data will be available in 'acct' table of '/tmp/pmacct.db' DB. Of course, you can change the database filename basing on your preferences. - To create v2 tables: shell> sqlite3 /tmp/pmacct.db < pmacct-create-table_v2.sqlite3 Data will be available in 'acct_v2' table of '/tmp/pmacct.db' DB. ... And so on for the newer versions. IIId. Custom SQL tables Custom SQL tables can be built by creating your own SQL schema and indexes. This allows to freely mix-and-match the primitives relevant to your accounting scenario. Specifying SQL table version and type (sql_table_version, sql_table_type) is not required; whereas a new directive, sql_optimize_clauses, is introduced to flag table customization to pmacct. This is a simple configuration snippet: sql_optimize_clauses: true sql_table: aggregate: IIIe. Historical accounting Enabling historical accounting allows to aggregate data over time (ie. 5 mins, hourly, daily) in a flexible and fully configurable way. Timestamps are lodged into two fields: 'stamp_inserted' which represents the basetime of the timeslot and 'stamp_updated' which says when a given timeslot was updated for the last time. Following there is a pretty standard configuration fragment to slice data into nicely aligned (or rounded-off) 5 minutes timeslots: sql_history: 5m sql_history_roundoff: m IIIf. INSERTs-only UPDATE queries are demanding in terms of resources; this is why, even if they are supported by pmacct, a savy approach is to cache data for longer times in memory and write them off once per timeslot (sql_history): this produces a much lighter INSERTs- only environemnt. This is an example based on 5 minutes timeslots: sql_refresh_time: 300 sql_history: 5m sql_history_roundoff: m sql_dont_try_update: true Note that sql_refresh_time is always expressed in seconds. IV. Running the libpcap-based daemon (pmacctd) pmacctd, like the other daemons, can be run with commandline options, using a config file or a mix of the two. Sample configuration files are in examples/ tree. Note also that most of the new features are available only as configuration directives. To be aware of the existing configuration directives, please read the CONFIG-KEYS document. Show all available pmacctd commandline switches: shell> pmacctd -h Run pmacctd reading configuration from a specified file (see examples/ tree for a brief list of some commonly useed keys; divert your eyes to CONFIG-KEYS for the full list). This example applies to all daemons: shell> pmacctd -f pmacctd.conf Daemonize the process; listen on eth0; aggregate data by src_host/dst_host; write to a MySQL server; limit traffic matching only source ip network 10.0.0.0/16; note that filters work the same as tcpdump. So, refer to libpcap/tcpdump man pages for examples and further reading. shell> pmacctd -D -c src_host,dst_host -i eth0 -P mysql src net 10.0.0.0/16 Or written the configuration way: ! daemonize: true plugins: mysql aggregate: src_host, dst_host interface: eth0 pcap_filter: src net 10.0.0.0/16 ! ... Print collected traffic data aggregated by src_host/dst_host over the screen; refresh data every 30 seconds and listen on eth0. shell> pmacctd -P print -r 30 -i eth0 -c src_host,dst_host Or written the configuration way: ! plugins: print print_refresh_time: 30 aggregate: src_host, dst_host interface: eth0 ! ... Daemonize the process; let pmacct aggregate traffic in order to show in vs out traffic for network 192.168.0.0/16; send data to a PostgreSQL server. This configuration is not possible via commandline switches; the corresponding configuration follows: ! daemonize: true plugins: pgsql[in], pgsql[out] aggregate[in]: dst_host aggregate[out]: src_host aggregate_filter[in]: dst net 192.168.0.0/16 aggregate_filter[out]: src net 192.168.0.0/16 sql_table[in]: acct_in sql_table[out]: acct_out ! ... The previous example looks nice! But how to make data historical ? Simple enough, let's suppose you want to split traffic by hour and write data into the DB every 60 seconds. ! daemonize: true plugins: pgsql[in], pgsql[out] aggregate[in]: dst_host aggregate[out]: src_host aggregate_filter[in]: dst net 192.168.0.0/16 aggregate_filter[out]: src net 192.168.0.0/16 sql_table[in]: acct_in sql_table[out]: acct_out sql_refresh_time: 60 sql_history: 1h sql_history_roundoff: h ! ... Let's now translate the same example in the memory plugin world. It's use is valuable expecially when it's required to feed bytes/packets/flows counters to external programs. Examples about the client program will follow later in this document. Now, note that each memory table need its own pipe file in order to get correctly contacted by the client: ! daemonize: true plugins: memory[in], memory[out] aggregate[in]: dst_host aggregate[out]: src_host aggregate_filter[in]: dst net 192.168.0.0/16 aggregate_filter[out]: src net 192.168.0.0/16 imt_path[in]: /tmp/pmacct_in.pipe imt_path[out]: /tmp/pmacct_out.pipe ! ... As a further note, check the CONFIG-KEYS document about more imt_* directives as they will support in the task of fine tuning the size and boundaries of memory tables, if default values are not ok for your setup. Now, fire multiple instances of pmacctd, each on a different interface; again, because each instance will have its own memory table, it will require its own pipe file for client queries aswell (as explained in the previous examples): shell> pmacctd -D -i eth0 -m 8 -s 65535 -p /tmp/pipe.eth0 shell> pmacctd -D -i ppp0 -m 0 -s 32768 -p /tmp/pipe.ppp0 Run pmacctd logging what happens to syslog and using "local2" facility: shell> pmacctd -c src_host,dst_host -S local2 NOTE: superuser privileges are needed to execute pmacctd correctly. V. Running the NetFlow and sFlow daemons (nfacctd/sfacctd) All examples about pmacctd are also valid for nfacctd and sfacctd with the exception of directives that apply exclusively to libpcap. If you've skipped examples in section 'IV', please read them before continuing. All configuration keys available are in the CONFIG-KEYS document. Some examples: Run nfacctd reading configuration from a specified file. shell> nfacctd -f nfacctd.conf Daemonize the process; aggregate data by sum_host (by host, summing inbound + outbound traffic); write to a local MySQL server. Listen on port 5678 for incoming Netflow datagrams (from one or multiple NetFlow agents). Let's make pmacct refresh data each two minutes and let's make data historical, divided into timeslots of 10 minutes each. Finally, let's make use of a SQL table, version 4. shell> nfacctd -D -c sum_host -P mysql -l 5678 And now written the configuration way: ! daemonize: true plugins: mysql aggregate: sum_host nfacctd_port: 5678 sql_refresh_time: 120 sql_history: 10m sql_history_roundoff: mh sql_table_version: 4 ! ... VI. Running the ULOG-based daemon (uacctd) All examples about pmacctd are also valid for uacctd with the exception of directives that apply exclusively to libpcap. If you've skipped examples in section 'IV', please read them before continuing. All configuration keys available are in the CONFIG-KEYS document. The Linux ULOG infrastructure requires a couple parameters in order to work properly. These are the ULOG multicast group (uacctd_group) to which captured packets have to be sent to and the Netlink buffer size (uacctd_nl_size). The default buffer settings (4KB) typically works OK for small environments. If the uacctd user is not already familiar with the iptables ULOG target, it is adviceable to start with a tutorial, like the one at the following URL ("6.5.15. ULOG target" section): http://www.faqs.org/docs/iptables/targets.html Apart from determining how and what traffic to capture with iptables, which is topic outside the scope of this document, the most relevant point is the "--ulog-nlgroup" iptables setting has to match with the "uacctd_group" uacctd one. A couple examples follow: Run uacctd reading configuration from a specified file. shell> uacctd -f uacctd.conf Daemonize the process; aggregate data by sum_host (by host, summing inbound + outbound traffic); write to a local MySQL server. Listen on ULOG multicast group #5. Let's make pmacct divide data into historical time-bins of 5 minutes. Let's disable UPDATE queries and hence align refresh time with the timeslot length. Finally, let's make use of a SQL table, version 4: ! uacctd_group: 5 daemonize: true plugins: mysql aggregate: sum_host sql_refresh_time: 300 sql_history: 5m sql_history_roundoff: mh sql_table_version: 4 sql_dont_try_update: true ! ... VII. Running the pmacct client (pmacct) The pmacct client is used to retrieve data from memory tables. Requests and answers are exchanged via a pipe file: authorization is strictly connected to permissions on the pipe file. Note: while writing queries commandline, it may happen to write chars with a special meaning for the shell itself (ie. ; or *). Mind to either escape ( \; or \* ) them or put in quotes ( " ). Show all available pmacct client commandline switches: shell> pmacct -h Fetch data stored into the memory table: shell> pmacct -s Match data between source IP 192.168.0.10 and destination IP 192.168.0.3 and return a formatted output; display all fields (-a), this way the output is easy to be parsed by tools like awk/sed; each unused field will be zero-filled: shell> pmacct -c src_host,dst_host -M 192.168.0.10,192.168.0.3 -a Similar to the previous example; it is requested to reset data for matched entries; the server will return the actual counters to the client, then will reset them: shell> pmacct -c src_host,dst_host -M 192.168.0.10,192.168.0.3 -r Fetch data for IP address dst_host 10.0.1.200; we also ask for a 'counter only' output ('-N') suitable, this time, for injecting data in tools like MRTG or RRDtool (sample scripts are in the examples/ tree). Bytes counter will be returned (but the '-n' switch allows also select which counter to display). If multiple entries match the request (ie because the query is based on dst_host but the daemon is actually aggregating traffic as "src_host, dst_host") their counters will be summed: shell> pmacct -c dst_host -N 10.0.1.200 Another query; this time let's contact the server listening on pipe file /tmp/pipe.eth0: shell> pmacct -c sum_port -N 80 -p /tmp/pipe.eth0 Find all data matching host 192.168.84.133 as either their source or destination address. In particular, this example shows how to use wildcards and how to spawn multiple queries (each separated by the ';' symbol). Take care to follow the same order when specifying the primitive name (-c) and its actual value ('-M' or '-N'): shell> pmacct -c src_host,dst_host -N "192.168.84.133,*;*,192.168.84.133" Find all web and smtp traffic; we are interested in have just the total of such traffic (for example, to split legal network usage from the total); the output will be a unique counter, sum of the partial (coming from each query) values. shell> pmacct -c src_port,dst_port -N "25,*;*,25;80,*;*,80" -S Show traffic between the specified hosts; this aims to be a simple example of a batch query; note that as value of both '-N' and '-M' switches it can be supplied a value like: 'file:/home/paolo/queries.list': actual values will be read from the specified file (and they need to be written into it, one per line) instead of commandline: shell> pmacct -c src_host,dst_host -N "10.0.0.10,10.0.0.1;10.0.0.9,10.0.0.1;10.0.0.8,10.0.0.1" shell> pmacct -c src_host,dst_host -N "file:/home/paolo/queries.list" VIII. Running the logfile players (pmmyplay and pmpgplay) For brevity, examples in this section will revolve around "pmmyplay" tool but the same are applicable to "pmpgplay". Currently, two different methods are supported as failover action when the RDBMS fails: logfiles or backup DB. While planning for a recovery method, consider that the logfile method is being discontinued and you are encouraged to use the backup DB option. Display online help and available options: shell> pmmyplay -h Play the whole specified file, inserting elements in the DB and enabling debug: shell> pmmyplay -d -f /tmp/pmacct-recovery.dat Just see on the screen the content of the supplied logfile; that is, do not interact with the DB: shell> pmmyplay -d -t -f /tmp/pmacct-recovery.dat Play a single (-n 1) element (the fifth) from the specified file (useful if for example a previous player execution was aborted due to a failure): shell> pmmyplay -o 5 -n 1 -f /tmp/pmacct-recovery.dat Play all elements until the end of file, starting from element number six: shell> pmmyplay -o 6 -f /tmp/pmacct-recovery.dat -p ohwhatanicepwrd IX. Quickstart guide to packet classifiers pmacct 0.10.0 sees the introduction of a packet classification feature. The approach is fully extensible: classification patterns are based over regular expressions (RE), must be placed into a common directory and have a .pat file extension. Patterns for well-known protocols are available and are just a click away. Furthermore, you can write your own patterns (and share them with the active L7-filter project's community). Below the quickstarter guide: a) download pmacct shell> wget http://www.pmacct.net/pmacct-x.y.z.tar.gz b) compile pmacct shell> cd pmacct-x.y.z; ./configure && make && make install c-1) download regular expression (RE) classifiers as-you-need them: you just need to point your browser to http://l7-filter.sourceforge.net/protocols/ then: shell> cd /path/to/classifiers/ shell> wget http://l7-filter.sourceforge.net/layer7-protocols/protocols/[ protocol ].pat c-2) download all the RE classifiers available: you just need to point your browser to http://sourceforge.net/projects/l7-filter (and take to the latest L7-protocol definitions tarball). Pay attention to remove potential catch-all patterns which might be part of the downloaded package (ie. unknown.pat and unset.pat). c-3) download shared object (SO) classifiers (written in C) as-you-need them: you need just to point your browser to http://www.pmacct.net/classification/ , download the available package, extract files and compile things following INSTALL instructions. When everything is finished, install the produced shared objects: shell> mv *.so /path/to/classifiers/ d-1) build pmacct configuration, a memory table example: ! daemonize: true interface: eth0 aggregate: flows, class plugins: memory classifiers: /path/to/classifiers/ snaplen: 700 !... d-2) build pmacct configuration, a SQL example: ! daemonize: true interface: eth0 aggregate: flows, class plugins: mysql classifiers: /path/to/classifiers/ snaplen: 700 sql_history: 1h sql_history_roundoff: h sql_table_version: 5 sql_aggressive_classification: true !... e) Ok, we are done! Fire the pmacct collector daemon: shell> pmacctd -f /path/to/configuration/file You can now play with the SQL or pmacct client; furthermore, you can add/remove/write patterns and load them by restarting the pmacct daemon. If using the memory plugin you can check out the list of loaded plugins with 'pmacct -C'. Don't underestimate the importance of 'snaplen', 'pmacctd_flow_buffer_size', 'pmacctd_flow_buffer_buckets' values; get the time to take a read about them in the CONFIG-KEYS document. X. Quickstart guide to setup a NetFlow agent/probe pmacct 0.11.0 sees the introduction of traffic data export capabilities, through both NetFlow and sFlow protocols. While NetFlow v5 is fixed by nature, v9 adds flexibility by allowing to transport custom informations (for example, L7-classification tags to a remote collector). Below the quickstarter guide: a) usual initial steps: download pmacct, unpack it, compile it. b) build NetFlow probe configuration, using pmacctd: ! daemonize: true interface: eth0 aggregate: src_host, dst_host, src_port, dst_port, proto, tos plugins: nfprobe nfprobe_receiver: 1.2.3.4:2100 nfprobe_version: 9 ! nfprobe_engine: 1:1 ! nfprobe_timeouts: tcp=120:maxlife=3600 ! ! networks_file: /path/to/networks.lst !... This is a basic working configuration. Additional features include: 1) generate ASNs by using a networks_file pointing to a valid Networks File (see examples/ directory) and adding src_as, dst_as primitives to the 'aggregate' directive; alternatively, as of release 0.12.0rc2, it's possible to generate ASNs from the pmacctd BGP thread. The following fragment can be added to the configuration above: pmacctd_as: bgp bgp_daemon: true bgp_daemon_ip: 127.0.0.1 bgp_agent_map: /path/to/agent_to_peer.map bgp_daemon_port: 17917 The bgp_daemon_port can be changed from the standard BGP port (179/TCP) in order to co-exist with other BGP routing software which might be running on the same host. Furthermore, they can safely peer each other by using 127.0.0.1 as bgp_daemon_ip. In pmacctd, bgp_agent_map does the trick of mapping 0.0.0.0 to the IP address of the BGP peer (ie. 127.0.0.1: 'id=127.0.0.1 ip=0.0.0.0'); this setup, while generic, was tested working in conjunction with Quagga 0.99.14. Following a relevant fragment of the Quagga configuration: router bgp Y bgp router-id X.X.X.X neighbor 127.0.0.1 remote-as Y neighbor 127.0.0.1 port 17917 neighbor 127.0.0.1 update-source X.X.X.X ! 2) encode flow classification information in NetFlow v9 like Cisco does with its NBAR/NetFlow v9 tie-up. This can be done by introducing the 'class' primitive to the afore mentioned 'aggregate' and add the extra configuration directives: aggregate: class, src_host, dst_host, src_port, dst_port, proto, tos classifiers: /path/to/classifiers/ snaplen: 700 Further information on this topic can be found in the section of this document about stream classification; 3) add direction (ingress, egress) awareness to measured IP traffic flows. Direction can be inferred either statically (in, out) or dinamically (tag, tag2) via nfprobe_direction directive. Let's look at a dynamic example using tag2; first, add the following lines to the daemon configuration: nfprobe_direction: tag2 pre_tag_map: /path/to/pretag.map then edit the tag map as follows. A return value of '1' means ingress while '2' is translated to egress. It is possible to employ L2 and/or L3 addresses to recognize flow directions. The 'id2' primitive (tag2) will be used to carry the return value: id=1 filter='dst host XXX.XXX.XXX.XXX' id=2 filter='src host XXX.XXX.XXX.XXX' id=1 filter='ether src XX:XX:XX:XX:XX:XX' id=2 filter='ether dst XX:XX:XX:XX:XX:XX' Indeed in such a case, the 'id' primitive (tag) can be leveraged to other uses (ie. filter sub-set of the traffic for flow export); 4) add interface (input, output) awareness to measured IP traffic flowsi - in addition to direction awareness, as just discussed. Interface can be inferred either statically (<1-4294967295>) or dynamically (tag, tag2) via nfprobe_ifindex directive. Let's look at a dynamic example using tag; first add the following lines to the daemon configuration: nfprobe_direction: tag pre_tag_map: /path/to/pretag.map then edit the tag map as follows. It is possible to employ L2 and/or L3 addresses to recognize flow directions. The 'id' primitive (tag) will be used to carry the return value: id=100 filter='dst host XXX.XXX.XXX.XXX' id=100 filter='src host XXX.XXX.XXX.XXX' id=200 filter='dst host YYY.YYY.YYY.YYY' id=200 filter='src host YYY.YYY.YYY.YYY' id=200 filter='ether src YY:YY:YY:YY:YY:YY' id=200 filter='ether dst YY:YY:YY:YY:YY:YY' c) build NetFlow collector configuration, using nfacctd: ! daemonize: true nfacctd_ip: 1.2.3.4 nfacctd_port: 2100 plugins: memory[display] aggregate[display]: src_host, dst_host, src_port, dst_port, proto ! ! classifiers: /path/to/classifiers d) Ok, we are done ! Now fire both daemons: shell a> pmacctd -f /path/to/configuration/pmacctd-nfprobe.conf shell b> nfacctd -f /path/to/configuration/nfacctd-memory.conf XI. Quickstart guide to setup a sFlow agent/probe pmacct 0.11.0 sees the introduction of traffic data export capabilities via sFlow; such protocol is quite different from NetFlow: in short, it works by exporting portions of sampled packets rather than building uni-directional flows as it happens in NetFlow; this less-stateful approach makes sFlow a light export protocol well-tailored for high- speed networks. Further, sFlow v5 can be extended much like NetFlow v9: meaning, ie., L7 classification or basic Extended Gateway information (ie. src_as, dst_as) can be embedded in the record structure being exported. Below the quickstarter guide: b) build sFlow probe configuration, using pmacctd: ! daemonize: true interface: eth0 plugins: sfprobe sampling_rate: 20 sfprobe_agentsubid: 1402 sfprobe_receiver: 1.2.3.4:6343 ! ! networks_file: /path/to/networks.lst ! classifiers: /path/to/classifiers/ ! snaplen: 700 !... XII. Quickstart guide to setup the BGP daemon pmacct 0.12.0 integrates a BGP daemon into the IP accounting collectors part of the toolset. Such daemon is run as a thread within the collector core process. The idea is to receive data-plane information, ie. via NetFlow, sFlow, etc., and control plane information, ie. full routing tables via BGP from edge routers. Per-peer BGP RIBs are maintained to ensure local or regional views of the network (ie. in case of large networks which are partitioned in BGP clusters or federations). In case of routers with default-only or partial BGP views, the default route can be followed up (bgp_default_follow); also it might be desirable in certain situations, for example to save resources, to entirely map one or a set of agents to a BGP peer (bgp_agent_map). Pre-requisite is that the pmacct package has to be configured for compilation with threads, this line will do it: ./configure --enable-threads The following configuration fragment is alone sufficient to set up a BGP daemon which will bind to an IP address and will support up to a maximum number of 100 peers. Once PE routers start sending NetFlow datagrams and peer up, it should be possible to see the BGP-related fields, ie. src_as, dst_as, as_path, peer_as_dst, local_pref, MED, etc., correctly populated while querying the memory table: bgp_daemon: true bgp_daemon_ip: X.X.X.X bgp_daemon_max_peers: 100 nfacctd_as_new: bgp [ ... ] plugins: memory aggregation: src_as, dst_as, local_pref, med, as_path, peer_dst_as The BGP daemon reads the remote ASN upon receipt of a BGP OPEN message and dynamically presents itself as part of the same Autonomous System - to ensure an iBGP relationship is established all the times. Also, the BGP daemon acts as a passive BGP neighbor and hence will never try to re-establish a fallen peering session. For debugging purposes related to the BGP feed(s), the bgp_daemon_msglog configuration directive can be enabled in order to log UPDATE and WITHDRAW BGP events. XIIa. Limiting AS-PATH and BGP community attributes length AS-PATH and BGP communities can by nature get easily long, when represented as strings. Sometimes only a small portion of their content is relevant to the accounting task and hence a filtering layer was developed to take special care of these attributes. The bgp_aspath_radius cuts the AS-PATH down after a specified amount of hops; whereas the bgp_stdcomm_pattern does a simple sub-string matching against standard BGP communities, filtering in only those that match (optionally, for better precision, a pre-defined number of characters can be wildcarded by employing the '.' symbol, like in regular expressions). See a typical usage example below: bgp_aspath_radius: 3 bgp_stdcomm_pattern: 12345: A detailed description of these configuration directives is, as usual, included in the CONFIG-KEYS document. XIIb. The source peer AS case The peer_src_as primitive adds useful insight in understanding where traffic enters the observed routing domain; but asymmetric routing impacts accuracy delivered by devices configured with either NetFlow or sFlow and the peer-as feature (as it only performs a reverse lookup, ie. a lookup on the source IP address, in the BGP table hence saying where it would route such traffic). pmacct offers a few ways to perform some mapping to tackle this issue and easily model both private and public peerings, both bi-lateral or multi-lateral. Find below how to use a map, reloadable at runtime, and its contents (for full syntax guide lines, please see the 'peers.map.example' file within the examples section): bgp_peer_src_as_type: map bgp_peer_src_as_map: /path/to/peers.map [/path/to/peers.map] id=12345 ip=1.2.3.4 in=10 bgp_nexthop=3.4.5.6 id=34567 ip=1.2.3.4 in=10 id=45678 ip=2.3.4.5 in=20 src_mac=00:11:22:33:44:55 id=56789 ip=2.3.4.5 in=20 src_mac=00:22:33:44:55:66 Even though all this mapping is static, it can be auto-provisioned to a good degree by means of external scripts running at regular intervals and, for example, querying relevant routers via SNMP. In this sense, the bgpPeerTable MIB is a good starting point. Alternatively pmacct also offers the option to perform reverse BGP lookups. NOTES: * When mapping, the peer_src_as primitive doesn't really apply to egress NetFlow (or egress sFlow) as it mainly relies on either the input interface index (ifIndex), the source MAC address, a reverse BGP next-hop lookup or a combination of these. * "Source" MED, local preference, communities and AS-PATH have all been dedicated an aggregation primitives. Each carries its own peculiarities but the general concepts highlighed in this paragraph apply to these aswell. Check CONFIG-KEYS out for the src_[med|local_pref|as_path|std_comm|ext_comm]_[type|map] configuration directives. XIIc. Tracking entities on the own IP address space It might happen that not all entities attached to the service provider network are speaking BGP but rather they get IP prefixes redistributed into iBGP (different routing protocols, statics, directly connected, etc.). These can be private IP addresses or segments of the SP address space. The common factor to all of them is that while being present in iBGP, these prefixes can't be tracked any further due to the lack of attributes like AS-PATH or an ASN. To overcome this situation the simplest approach is to employ a bgp_peer_src_as_map directive, described previously (ie. making use of interface descriptions as a possible way to automate the process). Alterntively, the bgp_stdcomm_pattern_to_asn directive was developed to fit into this scenario: assuming procedures of a SP are (or can be changed) to label every relevant non-BGP speaking entity IP prefixes uniquely with a BGP standard community, this directive allows to map the community to a peer AS/origin AS couple as per the following example: XXXXX:YYYYY => Peer-AS=XXXXX, Origin-AS=YYYYY. XIId. Preparing the router to BGP peer Once the collector is configured and started up the remaining step is to let routers to export traffic samples to the collector and BGP peer with it. Configuring the same source IP address across both NetFlow and BGP features allows the pmacct collector to perform the required correlations. Also, setting the BGP Router ID accordingly allows for more clear log messages. It's adviceable to configure the collector at the routers as a Route-Reflector (RR) client. A relevant configuration example for a Cisco router follows: ip flow-export source Loopback12345 ip flow-export version 5 ip flow-export destination X.X.X.X 2100 ! router bgp 12345 neighbor X.X.X.X remote-as 12345 neighbor X.X.X.X update-source Loopback12345 neighbor X.X.X.X version 4 neighbor X.X.X.X send-community neighbor X.X.X.X route-reflector-client neighbor X.X.X.X description nfacctd A relevant configuration example for a Juniper router follows: forwarding-options { sampling { output { cflowd X.X.X.X { port 2100; source-address Y.Y.Y.Y; version 5; } } } } protocols bgp { group rr-netflow { type internal; local-address Y.Y.Y.Y; family inet { any; } cluster Y.Y.Y.Y; neighbor X.X.X.X { description "nfacctd"; } } } XIIe. A working configuration example writing to a MySQL database The following setup is a realistic example for a MPLS-enabled IP carrier network divided in multiple BGP clusters. Samples are aggregated in a way which is suitable to get an overview of traffic trajectories, collecting much information where these enter the AS and where they get out. daemonize: true nfacctd_port: 2100 nfacctd_time_new: true plugins: mysql[5mins], mysql[hourly] sql_optimize_clauses: true sql_dont_try_update: true sql_multi_values: 1024000 sql_history_roundoff[5mins]: m sql_history[5mins]: 5m sql_refresh_time[5mins]: 300 sql_table[5mins]: acct_bgp_5mins sql_history_roundoff[hourly]: h sql_history[hourly]: 1h sql_refresh_time[hourly]: 3600 sql_table[hourly]: acct_bgp_1hr bgp_daemon: true bgp_daemon_ip: X.X.X.X bgp_daemon_max_peers: 100 bgp_aspath_radius: 3 bgp_follow_default: 1 nfacctd_as_new: bgp bgp_peer_src_as_type: map bgp_peer_src_as_map: /path/to/peers.map plugin_buffer_size: 10240 plugin_pipe_size: 1024000 aggregate: tag, src_as, dst_as, peer_src_as, peer_dst_as, peer_src_ip, peer_dst_ip, local_pref, as_path pre_tag_map: /path/to/pretag.map refresh_maps: true pre_tag_map_entries: 3840 The content of the maps (bgp_peer_src_as_map, pre_tag_map) is meant to be pretty standard and will not be shown. As it can be grasped from the above configuration, the SQL schema was customized. Below a suggestion on how this can be modified for more efficiency - with additional INDEXes, to speed up specific queries response time, remaining to be worked out: create table acct_bgp_5mins ( id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT, agent_id INT(4) UNSIGNED NOT NULL, as_src INT(4) UNSIGNED NOT NULL, as_dst INT(4) UNSIGNED NOT NULL, peer_as_src INT(4) UNSIGNED NOT NULL, peer_as_dst INT(4) UNSIGNED NOT NULL, peer_ip_src CHAR(15) NOT NULL, peer_ip_dst CHAR(15) NOT NULL, as_path CHAR(21) NOT NULL, local_pref INT(4) UNSIGNED NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (id), INDEX ... ) TYPE=MyISAM AUTO_INCREMENT=1; create table acct_bgp_1hr ( id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT, agent_id INT(4) UNSIGNED NOT NULL, as_src INT(4) UNSIGNED NOT NULL, as_dst INT(4) UNSIGNED NOT NULL, peer_as_src INT(4) UNSIGNED NOT NULL, peer_as_dst INT(4) UNSIGNED NOT NULL, peer_ip_src CHAR(15) NOT NULL, peer_ip_dst CHAR(15) NOT NULL, as_path CHAR(21) NOT NULL, local_pref INT(4) UNSIGNED NOT NULL, packets INT UNSIGNED NOT NULL, bytes BIGINT UNSIGNED NOT NULL, stamp_inserted DATETIME NOT NULL, stamp_updated DATETIME, PRIMARY KEY (id), INDEX ... ) TYPE=MyISAM AUTO_INCREMENT=1; XIIf. BGP daemon implementation concluding notes The implementation supports both 4-bytes ASN and IPv6 (MP-BGP); IPv6 peers are not supported yet though. When storing data via SQL, BGP primitives can be freely mix- and-matched with other primitives (ie. L2/L3/L4) when customizing the SQL table (sql_optimize_clauses: true). Environments making use of BGP Multi-Path are not currently supported; if you are using this and would like to see it implemented, please get in touch. TCP MD5 signature for BGP messages is not yet supported but on the radar. For a review of all the configurable knobs and features see the CONFIG-KEYS document. XIII. Quickstart guide to setup a NetFlow/sFlow replicator pmacct 0.12 (>= 0.12.2) includes a new 'tee' plugin which is meant to replicate NetFlow/sFlow data to N remote collectors. The plugin can also act transparently by preserving the original IP address of the datagrams. Setting up a replicator is very easy. All is needed is where to listen to for incoming packets, where to replicate them to and optionally a filtering layer, if required. Filtering bases on the standard pre_tag_map infrastructure; only coarse-grained filtering against original source IP address is possible. nfacctd_port: 2100 nfacctd_ip: X.X.X.X plugins: tee[a], tee[b] tee_receiver[a]: Y.Y.Y.Y:2100 tee_receiver[b]: Z.Z.Z.Z:2100 ! tee_transparent: true ! pre_tag_map: /path/to/pretag.map ! pre_tag_filter[b]: 0 plugin_buffer_size: 10240 plugin_pipe_size: 1024000 The pre_tag_filter in the above configuration snapshot applies only to the 'tee' plugin instance 'b' and filters out any NetFlow/sFlow packets marked with a non- zero 'id'. Plugin instance 'a' would normally receive all packets instead. An example of the pre_tag_map content follows: id=1 ip=A.A.A.A id=1 ip=B.B.B.B id=1 ip=C.C.C.C To enable the transparent mode, the tee_transparent should be commented out. It preserves the original IP address of the NetFlow/sFlow sender while replicating by essentially spoofing it. This feature is not global and can be freely enabled only on a subset of the active replicators. It requires super-user permissions in order to run. Concluding note: 'tee' plugin is not compatible with different plugins - within the same daemon instance. So if in the need of using pmacct for both collecting and replicating data, two separate instances must be used (intuitively with the replicator instance feeding the collector one). XIV. Quickstart guide to setup the IS-IS daemon pmacct 0.14.0 integrates an IS-IS daemon into the IP accounting collectors part of the toolset. Such daemon is run as a thread within the collector core process. The idea is to receive data-plane information, ie. via NetFlow, sFlow, etc., and control-plane information via IS-IS. Currently a single L2 P2P neighborship, ie. over a GRE tunnel, is supported. The daemon is currently used for the purpose of route resolution. A sample scenario could be that more specific internal routes might be configured to get summarized in BGP while crossing cluster boundaries. Pre-requisite for the use of the IS-IS daemon is that the pmacct package has to be configured for compilation with threads, this line will do it: ./configure --enable-threads XIVa. Preparing the collector for the L2 P2P IS-IS neighborship It's assumed the collector sits on an Ethernet segment and has not direct link (L2) connectivity to an IS-IS speaker, hence the need to establish a GRE tunnel. While extensive literature and OS specific examples exist on the topic, a brief example for Linux, consistent with rest of the chapter, is provided below: ip tunnel add gre2 mode gre remote 10.0.1.2 local 10.0.1.1 ttl 255 ip link set gre2 up The following configuration fragment is sufficient to set up an IS-IS daemon which will bind to a network interface gre2 configured with IP address 10.0.1.1 in an IS-IS area 49.0001 and a CLNS MTU set to 1400: isis_daemon: true isis_daemon_ip: 10.0.1.1 isis_daemon_net: 49.0001.0100.0000.1001.00 isis_daemon_iface: gre2 isis_daemon_mtu: 1400 ! isis_daemon_msglog: true XIVb. Preparing the router for the L2 P2P IS-IS neighborship Once the collector is ready, the remaining step is to configure a remote router for the L2 P2P IS-IS neighborship. The following bit of configuration (based on Cisco IOS) will match the above fragment of configuration for the IS-IS daemon: interface Tunnel0 ip address 10.0.1.2 255.255.255.252 ip router isis tunnel source FastEthernet0 tunnel destination XXX.XXX.XXX.XXX clns mtu 1400 isis metric 1000 ! router isis net 49.0001.0100.0000.1002.00 is-type level-2-only metric-style wide log-adjacency-changes passive-interface Loopback0 ! XV. Running the print plugin to write to flat-files Print plugin was originally conceived to display data on standard output; with pmacct 0.14 a new 'print_output_file' configuration directive is introduced to allow the plugin to write to flat-files aswell. Dynamic filenames are supported. Output is text-based (no binary proprietary format) and can be either CSV or formatted ('print_output' directive). When to write to disk can be configured via the 'print_refresh_time' directive. An example follows on how to write to files on a 15 mins basis in CSV format: print_refresh_time: 900 print_output: csv print_output_file: /path/to/file-%Y%m%d-%H%M.txt print_time_roundoff: m Which, over time, would produce a would produce a series of files as follows: -rw------- 1 paolo paolo 2067 Nov 21 00:15 blabla-20111121-0000.txt -rw------- 1 paolo paolo 2772 Nov 21 00:30 blabla-20111121-0015.txt -rw------- 1 paolo paolo 1916 Nov 21 00:45 blabla-20111121-0030.txt -rw------- 1 paolo paolo 2940 Nov 21 01:00 blabla-20111121-0045.txt pmacct-0.14.0/AUTHORS0000644000175000017500000000161211740776151013132 0ustar paolopaolopmacct (Promiscuous mode IP Accounting package) v0.14.0 pmacct is Copyright (C) 2003-2012 by Paolo Lucente Founder: Paolo Lucente Contributors: Francois Deppierraz Package maintainers: Marcelo Goes (Gentoo) Ryan Steinmetz (FreeBSD) Jamie Wilkinson (Debian) KP Kirchdoerfer (LEAF Bering-uClibc) Peter Nixon (OpenSUSE RPMs) Slava Dubrovskiy (ALTLinux) Thanks to the following people for their strong support along the time: Martin Anderberg Sven Anderson Joerg Behrens Robert Blechinger Arnaud De-Bermingham Marcello Di Leonardo Rich Gade Aaron Glenn Gianluca Guida Wim Kerkhoff Peter Nixon Karl O. Pinc Vasiliy Ponomarev Martin Pot A.O. Prokofiev Anik Rahman pmacct-0.14.0/TODO0000644000175000017500000000077311233030361012540 0ustar paolopaolopmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2009 by Paolo Lucente + Continue the integration trend just started with BGP; by either remaining in the same area (routing protocols) or by addressing other topical areas. + Improvements to the plugins architecture; it would be great if one day, people could write plugins in other languages than C/C++. + A never-ending task: find and evaluate appealing traffic collection methods and backends to implement. pmacct-0.14.0/README0000644000175000017500000000104111233347331012725 0ustar paolopaoloDOCUMENTATION: - Online: * http://wiki.pmacct.net/ - Distribution tarball: * ChangeLog: History of features version by version * CONFIG-KEYS: Available configuration directives explained * EXAMPLES: Examples, command-lines, quickstart guides * FAQS: FAQ document * INSTALL: basic installation guide * docs/: Miscellaneous internals, UNIX signals, SQL triggers documents * examples/: Sample pmacct and 3rd party tools configurations; sample maps * sql/: SQL schemas for various pmacct tables; IPv6 and 64bit counters hacks pmacct-0.14.0/mkinstalldirs0000755000175000017500000000132710556243724014672 0ustar paolopaolo#! /bin/sh # mkinstalldirs --- make directory hierarchy # Author: Noah Friedman # Created: 1993-05-16 # Public domain # $Id: mkinstalldirs,v 1.1.1.1 2007/01/26 00:14:12 paolo Exp $ errstatus=0 for file do set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` shift pathcomp= for d do pathcomp="$pathcomp$d" case "$pathcomp" in -* ) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr fi fi pathcomp="$pathcomp/" done done exit $errstatus # mkinstalldirs ends here