├── collector ├── etc │ ├── .gitignore │ ├── version.in │ ├── collector.conf.sample │ └── log4perl.conf.sample ├── lib │ └── IPTV │ │ └── Analyzer │ │ ├── .gitignore │ │ ├── Version.pm.in │ │ ├── Log4perlTrapper.pm │ │ ├── snmptrap.pm │ │ └── Config.pm ├── .gitignore ├── input │ ├── README │ └── proc_rule_test001.input ├── MANIFEST ├── README.wiki ├── Makefile.PL └── bin │ ├── generate-test-snmptrap.pl │ └── iptv-collector ├── m4 └── .gitignore ├── autogen.sh ├── webfrontend ├── www │ ├── webfrontend.ini │ ├── js │ │ └── epoch_v202_en │ │ │ ├── images │ │ │ ├── sp2_warning.gif │ │ │ └── eventsandholidays.gif │ │ │ ├── minimum_code.html │ │ │ ├── epoch_styles.css │ │ │ └── gettingstarted.html │ ├── .htaccess │ ├── index.php │ ├── functions.js │ ├── pages │ │ ├── probe_overview.php │ │ └── channel_view.php │ ├── design.inc.php │ ├── css │ │ └── motorola.css │ ├── include │ │ ├── graph_probe_drops_bar01.php │ │ ├── graph_channel_drops_bar01.php │ │ └── graph_probe_drops_bar02.php │ ├── staging │ │ └── pie01.php │ └── graphs.inc.php ├── TODO └── README.dependencies ├── iptables-module ├── .gitignore ├── compat_nfinetaddr.h ├── compat_skbuff.h ├── reload_module.sh ├── xt_mpeg2ts.h ├── README ├── compat_xtables.h ├── Makefile.in ├── compat_xtnu.h ├── README.compile └── libxt_mpeg2ts.c ├── .gitignore ├── snmp └── mibs │ ├── CXNET-CORE-MIB.txt │ └── IPTV-ANALYZER-MIB.txt ├── database ├── README.wiki ├── database-upgrade-0.9.0-to-0.9.1.sql ├── changes-between-versions.wiki ├── database-schema-0.9.0.sql ├── database-schema-0.9.1.sql └── database-schema-latest.sql ├── TODO ├── README.wiki ├── Makefile.am ├── doc ├── labsetup.wiki └── snmptrap.wiki ├── release.sh ├── ChangeLog └── configure.ac /collector/etc/.gitignore: -------------------------------------------------------------------------------- 1 | version 2 | -------------------------------------------------------------------------------- /m4/.gitignore: -------------------------------------------------------------------------------- 1 | /libtool.m4 2 | /lt*.m4 3 | -------------------------------------------------------------------------------- /collector/lib/IPTV/Analyzer/.gitignore: -------------------------------------------------------------------------------- 1 | Version.pm 2 | -------------------------------------------------------------------------------- /collector/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile.old 2 | blib/ 3 | pm_to_blib 4 | TAGS 5 | *~ 6 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | autoreconf -fi; 4 | rm -Rf autom4te*.cache; 5 | -------------------------------------------------------------------------------- /collector/input/README: -------------------------------------------------------------------------------- 1 | 2 | The input directory contains some examples of the proc file contents 3 | generated by the iptables kernel module. 4 | 5 | -------------------------------------------------------------------------------- /webfrontend/www/webfrontend.ini: -------------------------------------------------------------------------------- 1 | [DB droplog] 2 | dbhost = database.server.dk 3 | dbname = iptvprobe 4 | dbuser = iptvprobe 5 | dbpass = CHANGEPASSWD 6 | 7 | -------------------------------------------------------------------------------- /iptables-module/.gitignore: -------------------------------------------------------------------------------- 1 | *.ko 2 | *.so 3 | *.o 4 | *~ 5 | *.mod.c 6 | Module.symvers 7 | modules.order 8 | .*.cmd 9 | .xt_mpeg2ts.o.d 10 | .tmp_versions 11 | -------------------------------------------------------------------------------- /webfrontend/www/js/epoch_v202_en/images/sp2_warning.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netoptimizer/IPTV-Analyzer/HEAD/webfrontend/www/js/epoch_v202_en/images/sp2_warning.gif -------------------------------------------------------------------------------- /webfrontend/www/js/epoch_v202_en/images/eventsandholidays.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netoptimizer/IPTV-Analyzer/HEAD/webfrontend/www/js/epoch_v202_en/images/eventsandholidays.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.la 2 | *.lo 3 | *.loT 4 | *.o 5 | .deps 6 | .libs 7 | Makefile 8 | Makefile.in 9 | 10 | /.*.lst 11 | 12 | /aclocal.m4 13 | /autom4te*.cache 14 | /compile 15 | /config.* 16 | /configure 17 | /depcomp 18 | /install-sh 19 | /libtool 20 | /ltmain.sh 21 | /missing 22 | /stamp-h1 23 | release 24 | -------------------------------------------------------------------------------- /webfrontend/www/.htaccess: -------------------------------------------------------------------------------- 1 | # 2 | # For this to work, remember to enable: 3 | # AllowOverride Limit 4 | # 5 | 6 | 7 | Order deny,allow 8 | Deny from all 9 | 10 | 11 | 12 | Order deny,allow 13 | Deny from all 14 | 15 | -------------------------------------------------------------------------------- /iptables-module/compat_nfinetaddr.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMPAT_NFINETADDR_H 2 | #define _COMPAT_NFINETADDR_H 1 3 | 4 | #include 5 | #include 6 | 7 | union nf_inet_addr { 8 | __be32 ip; 9 | __be32 ip6[4]; 10 | struct in_addr in; 11 | struct in6_addr in6; 12 | }; 13 | 14 | #endif /* _COMPAT_NFINETADDR_H */ 15 | -------------------------------------------------------------------------------- /collector/etc/version.in: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by configure 3 | # @configure_input@ 4 | # 5 | # This file provide an easy accessable version information 6 | # for the IPTV-Analyzer package. The plan is to use this 7 | # for detecting the current running version, when upgrading. 8 | # 9 | version=@PACKAGE_VERSION@ 10 | -------------------------------------------------------------------------------- /collector/MANIFEST: -------------------------------------------------------------------------------- 1 | bin/iptv-collector 2 | etc/collector.conf.sample 3 | etc/log4perl.conf.sample 4 | etc/version 5 | etc/version.in 6 | input/proc_rule_test001.input 7 | input/README 8 | lib/IPTV/Analyzer/Log4perlTrapper.pm 9 | lib/IPTV/Analyzer/mpeg2ts.pm 10 | lib/IPTV/Analyzer/Version.pm 11 | lib/IPTV/Analyzer/Version.pm.in 12 | Makefile.PL 13 | MANIFEST This list of files 14 | README.wiki 15 | -------------------------------------------------------------------------------- /webfrontend/TODO: -------------------------------------------------------------------------------- 1 | 2 | 3 | == Issue: writable graphs dir == 4 | 5 | There need to exist a writable directory under pages/graphs/. 6 | This poses a problems (1) security issues, (2) cleanup problem, 7 | as the graph file names are timestamped. 8 | 9 | == Protection of config file == 10 | 11 | The config file 'webfrontend.ini' contains the database password. 12 | Currently, its placed directly in the public webdir. The current way 13 | to protect this file, is by using a .htaccess file in this directory. 14 | But the sysadm must have enabled Apache config "AllowOverride Limit". 15 | -------------------------------------------------------------------------------- /webfrontend/www/index.php: -------------------------------------------------------------------------------- 1 | FALSE)); 6 | 7 | echo "

$title

"; 8 | 9 | echo "

IPTV drops statistics

"; 10 | 11 | echo "\n"; 12 | echo "Probe overview"; 13 | echo " collective signal per probe
\n"; 14 | 15 | echo "\n"; 16 | echo "View a specific channel\n"; 17 | echo "
\n"; 18 | 19 | 20 | doFooter(); 21 | ?> 22 | -------------------------------------------------------------------------------- /collector/input/proc_rule_test001.input: -------------------------------------------------------------------------------- 1 | # info:version module:xt_mpeg2ts version:0.4.1-devel 2 | # info:time created:1263215354.724570789 now:1263217148.157113795 delta:1793.432543006 3 | # info:dynamic rule_id:1 streams:4 streams_check:4 max_list_search:0 rnd:4252654206 4 | # info:config htable_size:100 max-streams:0 list_search_warn_level:20 5 | bucket:0 dst:224.123.173.2 src:87.72.136.107 dport:5500 sport:50000 pids:10 skips:1023870 discontinuity:108252 6 | bucket:17 dst:224.123.173.10 src:87.72.133.2 dport:5500 sport:5500 pids:9 skips:2466903 discontinuity:268590 7 | bucket:49 dst:224.123.173.11 src:87.72.133.2 dport:5500 sport:5500 pids:8 skips:537583 discontinuity:57082 8 | -------------------------------------------------------------------------------- /collector/lib/IPTV/Analyzer/Version.pm.in: -------------------------------------------------------------------------------- 1 | # 2 | # Configure/autotools version integration trick to Perl 3 | # 4 | # The real version number is set in configure.ac via AC_INIT macro. 5 | # And the configure system expand/create this Version.pm file. 6 | # 7 | # @configure_input@ 8 | # 9 | package IPTV::Analyzer::Version; 10 | 11 | BEGIN { 12 | use Exporter (); 13 | our ($VERSION, $PACKAGE_VERSION, $PACKAGE_NAME, $PACKAGE_BUGREPORT); 14 | 15 | # Package version info expanded by configure 16 | $VERSION = "@PACKAGE_VERSION@"; 17 | $PACKAGE_VERSION = "@PACKAGE_VERSION@"; 18 | $PACKAGE_NAME = "@PACKAGE_NAME@"; 19 | } 20 | 21 | 1; 22 | -------------------------------------------------------------------------------- /snmp/mibs/CXNET-CORE-MIB.txt: -------------------------------------------------------------------------------- 1 | CXNET-CORE-MIB DEFINITIONS ::= BEGIN 2 | 3 | -- 4 | -- Definitions for CXNET-CORE-MIB. 5 | -- 6 | -- $Id: CXNET-CORE-MIB.mib 3652 2006-08-31 10:18:05Z jln $ 7 | -- 8 | -- ©2006 ComX Networks A/S 9 | -- 10 | -- Tais M. Hansen 11 | -- 12 | 13 | IMPORTS 14 | enterprises, MODULE-IDENTITY 15 | FROM SNMPv2-SMI; 16 | 17 | cxnet MODULE-IDENTITY 18 | LAST-UPDATED "200608021543Z" 19 | ORGANIZATION "ComX Networks A/S" 20 | CONTACT-INFO "email: " 21 | DESCRIPTION "" 22 | REVISION "200608021538Z" 23 | DESCRIPTION "First draft" 24 | ::= { enterprises 26124 } 25 | 26 | -- cxnet core 27 | 28 | cxexperimental OBJECT IDENTIFIER ::= { cxnet 42 } 29 | 30 | END 31 | -------------------------------------------------------------------------------- /webfrontend/www/functions.js: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | 3 | /* 4 | * This file contains some common javascript function used by the 5 | * tvprobe webfrontend. 6 | */ 7 | 8 | /* This file can be included from another file using: 9 | 10 | 12 | 13 | */ 14 | 15 | function expandRow(probeId) { 16 | var i = 1; 17 | while (document.getElementById(probeId+'_'+i) != null) { 18 | if (document.getElementById(probeId+'_'+i).style.display == 'none') { 19 | document.getElementById(probeId+'_'+i).style.display = 'table-row'; 20 | } else { 21 | document.getElementById(probeId+'_'+i).style.display = 'none'; 22 | } 23 | i++; 24 | } 25 | return true; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /database/README.wiki: -------------------------------------------------------------------------------- 1 | 2 | = Quick database HOWTO = 3 | 4 | == Create the database == 5 | 6 | On database server call: 7 | 8 | mysql -u root -p 9 | 10 | At the mysql prompt, write: 11 | 12 | drop database if exists tvprobe; 13 | create database tvprobe; 14 | 15 | 16 | == Grant access to database == 17 | 18 | At the mysql prompt, write: 19 | 20 | grant all on tvprobe.* to 'tvprobe'@'localhost' identified by 'CHANGEPASSWORD'; 21 | flush privileges; 22 | 23 | If you want to, grant remote access: 24 | 25 | grant SELECT, INSERT, UPDATE, EXECUTE on tvprobe.* 26 | to 'tvprobe'@'HOSTNAME.yourdomain.dk' 27 | identified by 'CHANGEPASSWORD'; 28 | flush privileges; 29 | 30 | 31 | == Create tables == 32 | 33 | At the UNIX prompt write: 34 | 35 | mysql -u root -p tvprobe < database-schema-latest.sql 36 | 37 | -------------------------------------------------------------------------------- /collector/lib/IPTV/Analyzer/Log4perlTrapper.pm: -------------------------------------------------------------------------------- 1 | # 2 | # Trick to catch stderr messages from e.g. DB calls into Log4perl 3 | # 4 | # From: 5 | # 6 | # http://search.cpan.org/~mschilli/Log-Log4perl-1.26/lib/Log/Log4perl/FAQ.pm#Some_module_prints_messages_to_STDERR._How_can_I_funnel_them_to_Log::Log4perl? 7 | # 8 | 9 | ######################################## 10 | package IPTV::Analyzer::Log4perlTrapper; 11 | ######################################## 12 | 13 | use Log::Log4perl qw(:easy); 14 | 15 | sub TIEHANDLE { 16 | my $class = shift; 17 | bless [], $class; 18 | } 19 | 20 | sub PRINT { 21 | my $self = shift; 22 | $Log::Log4perl::caller_depth++; 23 | ERROR @_; 24 | $Log::Log4perl::caller_depth--; 25 | } 26 | 27 | #sub DESTROY { 28 | # my $self = shift; 29 | # print @_; 30 | #} 31 | 32 | sub DESTROY {} 33 | 34 | 1; 35 | -------------------------------------------------------------------------------- /webfrontend/www/pages/probe_overview.php: -------------------------------------------------------------------------------- 1 | TRUE)); 6 | 7 | echo "

$title

"; 8 | 9 | require_once("functions.inc.php"); 10 | #$displayTiming = TRUE; 11 | $starttime=getMicroTime(); 12 | 13 | db_connect(); 14 | 15 | $probes = probes_info_query(); 16 | echo "\n

Please select a probe:

\n"; 17 | probes_info_form_tabel($probes, $probeid); 18 | 19 | # Trick incl the graph php script 20 | #include("../include/graph_probe_drops_bar01.php"); 21 | include("../include/graph_probe_drops_bar02.php"); 22 | 23 | include("../staging/pie01.php"); 24 | 25 | db_disconnect(); 26 | 27 | doFooter(); 28 | displayTimingInfo($starttime, getMicroTime(), $displayTiming, "php done"); 29 | ?> 30 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 2 | == Before release == 3 | 4 | Stuff I/we need to change before a release 5 | 6 | * Currently no show stoppers for a release. 7 | 8 | 9 | == Future == 10 | 11 | Use of JSON encoding in proc file? I foresee/predict that we need to 12 | relay more advanced information via the kernel proc file, to 13 | userspace. The easiest way would be to encode the proc file string as 14 | a JSON string. 15 | 16 | More advanced info would be need, when starting to implement/record 17 | packet timestamping, and the PCR clock signals. Each stream can have 18 | different threshold levels, regarding averate inter-packet arrival 19 | time (IAT) and "allowed" bursts, dependend on the bandwidth and the 20 | PCR clock. E.g. for a high-bandwidth HD channel is normal to see IAT 21 | down to 0.3 ms, while for a low-bandwidth channel this would be 22 | considered a burst. 23 | 24 | 25 | == iptables-module == 26 | 27 | Implement byte and packet counters, for no-signal detection. 28 | 29 | 30 | == Database == 31 | 32 | Document the tables, right now there is only the notes I made during 33 | the evolution of the code. 34 | 35 | 36 | == Collector == 37 | 38 | Implement no-signal detection, based on the byte and packet counters 39 | from the kernel module. 40 | 41 | == Homepage == 42 | 43 | == Ideas == 44 | 45 | To measure the collective burstiness behavior for all of the streams / 46 | the entire network-stack, we could "just" look at the number of 47 | packets being processed in NAPI mode. 48 | 49 | -------------------------------------------------------------------------------- /README.wiki: -------------------------------------------------------------------------------- 1 | 2 | = IPTV-Analyzer = 3 | 4 | The IPTV-Analyzer is a continuous/real-time tool for analyzing the 5 | contents of MPEG2 Transport Stream (TS) packets, which is commonly 6 | used for IPTV multicast signals. 7 | 8 | The main purpose is continuous quality measurement, with a focus on 9 | detecting MPEG2 TS/CC packet drops. 10 | 11 | It scales to hundreds of IPTV channels, even on small ATOM based CPUs. 12 | 13 | == Private vs. Enterprise users == 14 | 15 | Private users can also take advantage of this software, as you can now 16 | document IPTV problems towards your provider, without having to buy 17 | commercial analyzer tools that are way to expensive to home users. 18 | 19 | Enterprise users can save a lot of money, as alternative commercial 20 | analyzer tools are extremely expensive. The IPTV-Analyzer is mostly 21 | target for enterprise usage, with several probes/measurement-points at 22 | strategic points in your network. 23 | 24 | == Elements / Architecture == 25 | 26 | === iptables-module: mpeg2ts === 27 | 28 | The core component is an iptables (Linux) kernel module, named 29 | "mpeg2ts". This kernel module performs the real-time deep packet 30 | inspection of the MPEG2-TS packets. Its highly performance optimized, 31 | written for parallel processing across CPU cores (via RCU locking) and 32 | hash tables are used for handling large number of streams. Statistics 33 | are exported via the proc filesystem (scalability is achieved via use 34 | of the seq_file proc API). 35 | 36 | === Collector === 37 | 38 | The statistics are collected by a iptv-collector daemon, and stored in 39 | a MySQL database. 40 | 41 | == Price == 42 | 43 | If you agree to contribute back fixes, improvements, bug-reports, 44 | insults etc., then its FREE, as its Licensed under GPL. 45 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # -*- Makefile -*- 2 | # 3 | # @configure_input@ 4 | # 5 | # This file is NOT autogenerated, and need to be maintained. Automake 6 | # will generate the Makefile based on this file where all the 7 | # appropiate variables are defined inside. 8 | # 9 | # 10 | ACLOCAL_AMFLAGS = -I m4 11 | 12 | # Adding to SUBDIRS will automatically call/invoke the Makefile in 13 | # these dirs 14 | SUBDIRS = iptables-module collector 15 | 16 | #man_MANS := xtables-addons.8 17 | 18 | .PHONY: FORCE 19 | FORCE: 20 | 21 | #xtables-addons.8: FORCE 22 | # ${MAKE} -f Makefile.mans all; 23 | 24 | install-exec-hook: 25 | depmod -a || :; 26 | 27 | #config.status: Makefile.iptrules.in 28 | 29 | tmpdir := $(shell mktemp -dtu) 30 | packer = gzip 31 | packext = .tar.gz 32 | 33 | GIT_VERSION = $(shell if [ -d .git ]; then git describe; fi) 34 | 35 | testver: 36 | @echo Detected GIT_VERSION:\"${GIT_VERSION}\" 37 | 38 | .PHONY: tarball 39 | 40 | # 41 | # The tarball target is to be used together with the Git repository 42 | # and uses the git "version" provided by 'git describe' 43 | # 44 | tarball: 45 | # do not use mkdir_p here. 46 | @if [ -z "${GIT_VERSION}" ]; then \ 47 | echo " *** ERROR: Sorry target tarball require the git tree"; \ 48 | exit 42;\ 49 | else \ 50 | echo " -- Creating tarball GIT version: ${GIT_VERSION}"; \ 51 | fi 52 | @mkdir ${tmpdir} 53 | pushd ${top_srcdir} && git archive --prefix=${PACKAGE_NAME}-${GIT_VERSION}/ HEAD | tar -C ${tmpdir} -x && popd; 54 | pushd ${tmpdir}/${PACKAGE_NAME}-${GIT_VERSION} && ./autogen.sh && popd; 55 | tar --use=${packer} -C ${tmpdir} -cf ${PACKAGE_NAME}-${GIT_VERSION}${packext} --owner=root --group=root ${PACKAGE_NAME}-${GIT_VERSION}/; 56 | @rm -Rf ${tmpdir}; 57 | @echo " -- Tarball ${PACKAGE_NAME}-${GIT_VERSION}${packext} ready" 58 | -------------------------------------------------------------------------------- /doc/labsetup.wiki: -------------------------------------------------------------------------------- 1 | 2 | = Lab setup = 3 | 4 | Its possible to setup a simple IPTV testlab using Open Source 5 | components. And you can even introduce and control the packets drops. 6 | 7 | Here are some useful commands to make a testlab setup, where we can 8 | generate a multicast signal (MPEG2 Transport Stream) signal and 9 | control the loss rate. 10 | 11 | 12 | == Create a mpeg2 ts packet multicast stream == 13 | 14 | Using VLC to create a multicast stream from a file: 15 | 16 | vlc ~/dvdrip/Far_Til_Fire.ts -I rc --sout \ 17 | '#duplicate{dst=std{access=udp,mux=ts,dst=239.254.1.1:5500}}' 18 | 19 | 20 | Recode a stream from a file: 21 | 22 | vlc ~/Xx27RyOnruU.flv --ttl 5 -I rc --sout \ 23 | '#transcode{vcodec=mp4v,vb=1024,scale=1,acodec=mpga,ab=192,channels=2}:duplicate{dst=std{access=udp,mux=ts,dst=239.254.1.1:5500}}' 24 | 25 | 26 | Send a incoming multicast stream out again using another multicast 27 | address: 28 | 29 | 30 | vlc --ttl 10 udp/ts://@239.123.173.3:5500 --sout \ 31 | '#duplicate{dst=std{access=udp,mux=ts,dst=239.254.1.1:5500}}' -I rc 32 | 33 | 34 | 35 | == Dropping packets on purpose == 36 | 37 | Create a 0.1 percent packet loss on eth6. Notice, we also need to 38 | introduce a delay, so the packet drop/loss have packets to "select" 39 | from. 40 | 41 | tc qdisc add dev eth6 root netem delay 20 ms loss 0.1% 42 | 43 | 44 | Next, change the packet loss percent on eth6 to 1.1 percent, and 45 | introduce a "burst" of loss 46 | 47 | tc qdisc change dev eth6 root netem loss 1.1% 25% 48 | 49 | Think it means give it a 25 percent chance that the next packet drop 50 | occur close to this one. (see explanation: [1] section: "Packet loss"). 51 | With their formulation, this will cause 1.1% of packets to be lost, 52 | and each successive probability depends by a quarter on the last one. 53 | 54 | [1] http://www.linuxfoundation.org/collaborate/workgroups/networking/netem 55 | -------------------------------------------------------------------------------- /iptables-module/compat_skbuff.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_SKBUFF_H 2 | #define COMPAT_SKBUFF_H 1 3 | 4 | struct tcphdr; 5 | struct udphdr; 6 | 7 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30) 8 | static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst) 9 | { 10 | skb->dst = dst; 11 | } 12 | 13 | static inline struct dst_entry *skb_dst(const struct sk_buff *skb) 14 | { 15 | return skb->dst; 16 | } 17 | 18 | static inline struct rtable *skb_rtable(const struct sk_buff *skb) 19 | { 20 | return (void *)skb->dst; 21 | } 22 | #endif 23 | 24 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19) 25 | # define skb_ifindex(skb) \ 26 | (((skb)->input_dev != NULL) ? (skb)->input_dev->ifindex : 0) 27 | # define skb_nfmark(skb) (((struct sk_buff *)(skb))->nfmark) 28 | #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) 29 | # define skb_ifindex(skb) (skb)->iif 30 | # define skb_nfmark(skb) (((struct sk_buff *)(skb))->mark) 31 | #else 32 | # define skb_ifindex(skb) (skb)->skb_iif 33 | # define skb_nfmark(skb) (((struct sk_buff *)(skb))->mark) 34 | #endif 35 | 36 | #ifdef CONFIG_NETWORK_SECMARK 37 | # define skb_secmark(skb) ((skb)->secmark) 38 | #else 39 | # define skb_secmark(skb) 0 40 | #endif 41 | 42 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 21) 43 | # define ip_hdr(skb) ((skb)->nh.iph) 44 | # define ip_hdrlen(skb) (ip_hdr(skb)->ihl * 4) 45 | # define ipv6_hdr(skb) ((skb)->nh.ipv6h) 46 | # define skb_network_header(skb) ((skb)->nh.raw) 47 | # define skb_transport_header(skb) ((skb)->h.raw) 48 | static inline void skb_reset_network_header(struct sk_buff *skb) 49 | { 50 | skb->nh.raw = skb->data; 51 | } 52 | static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb) 53 | { 54 | return (void *)skb_transport_header(skb); 55 | } 56 | static inline struct udphdr *udp_hdr(const struct sk_buff *skb) 57 | { 58 | return (void *)skb_transport_header(skb); 59 | } 60 | #endif 61 | 62 | #endif /* COMPAT_SKBUFF_H */ 63 | -------------------------------------------------------------------------------- /database/database-upgrade-0.9.0-to-0.9.1.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Upgrading Database scheme 3 | -- From version: 0.9.0 4 | -- To version: 0.9.1 5 | -- 6 | 7 | DROP TABLE IF EXISTS event_type; 8 | CREATE TABLE event_type ( 9 | bitmask int(10) unsigned NOT NULL default '0', 10 | label varchar(15) NOT NULL, 11 | description varchar(255), 12 | PRIMARY KEY (bitmask) 13 | ) ENGINE=InnoDB; 14 | 15 | LOCK TABLES event_type WRITE; 16 | INSERT INTO event_type (bitmask, label, description) VALUES 17 | ( 1, "new_stream", "New stream detected"), 18 | ( 2, "drop" , "Drops detected, both skips and discon"), 19 | ( 4, "no_signal" , "Stream have stopped transmitting data"), 20 | ( 32, "transition", "The event_state changed since last poll"), 21 | ( 64, "heartbeat" , "Heartbeat event to monitor status"), 22 | (128, "invalid" , "Some invalid event situation arose"); 23 | UNLOCK TABLES; 24 | 25 | ALTER TABLE log_event 26 | MODIFY event_type int(10) unsigned NOT NULL default '0'; 27 | 28 | ALTER TABLE log_event 29 | MODIFY delta_poll float unsigned default NULL; 30 | 31 | ALTER TABLE log_event 32 | ADD last_update timestamp NOT NULL default '0000-00-00 00:00:00', 33 | ADD delta_update float unsigned default NULL; 34 | 35 | -- Add delta colums for payload_bytes and packets 36 | 37 | ALTER TABLE log_event 38 | ADD `delta_payload_bytes` int(10) unsigned NOT NULL default '0' AFTER packets, 39 | ADD `delta_packets` int(10) unsigned NOT NULL default '0' AFTER packets; 40 | 41 | -- Rename column "bytes" to "payload_bytes" 42 | 43 | ALTER TABLE log_event 44 | CHANGE `bytes` `payload_bytes` bigint(20) unsigned default '0'; 45 | 46 | -- Fix the last renaming from mp2t to mpeg2ts 47 | 48 | ALTER TABLE daemon_session 49 | CHANGE `mp2t_created` `mpeg2ts_created` timestamp NOT NULL default '0000-00-00 00:00:00', 50 | CHANGE `mp2t_version` `mpeg2ts_version` varchar(50) default NULL; 51 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Script for making releases, where you are forced to create a git tag 4 | # before you can make a release. If you want tarballs without using a 5 | # git tag, then use the make tarball system. 6 | # 7 | set -e 8 | NAME=iptv-analyzer 9 | VERSION=0.9.4 10 | PREV_VERSION=0.9.3 11 | GPGKEY="3B37BCED" 12 | 13 | echo "Creating tarball for release: $NAME-$VERSION" 14 | echo "=============================" 15 | 16 | # Create a unique tempdir, to avoid leftovers from older release builds 17 | TMPDIR=`mktemp -dt $NAME.XXXXXXXXXX` 18 | trap 'rm -rf $TMPDIR' EXIT 19 | #echo TMPDIR:$TMPDIR 20 | PKGDIR="$TMPDIR/${NAME}-${VERSION}" 21 | #echo PKGDIR:$PKGDIR 22 | RELDIR=release 23 | if [ ! -d $RELDIR ]; then 24 | mkdir -p $RELDIR 25 | fi 26 | VERSION_TAG="v${VERSION}" 27 | #VERSION_TAG=HEAD #HACK for testing 28 | 29 | # Compression packer tool 30 | packer=gzip 31 | packext=gz 32 | 33 | #PATCH="$RELDIR/patch-$NAME-$PREV_VERSION-$VERSION.$packext"; 34 | TARBALL="$RELDIR/$NAME-$VERSION.tar.$packext"; 35 | CHANGES="$RELDIR/changes-$NAME-$PREV_VERSION-$VERSION.txt"; 36 | 37 | #mkdir -p "$TMPDIR" 38 | echo " -- Git shortlog v$PREV_VERSION..$VERSION_TAG" 39 | git shortlog "v$PREV_VERSION..$VERSION_TAG" > "$CHANGES" 40 | #git diff "v$PREV_VERSION..$VERSION_TAG" | $packer > "$PATCH" 41 | echo " -- Git archiving version tag $VERSION_TAG" 42 | git archive --prefix="$NAME-$VERSION/" "$VERSION_TAG" | tar -xC "$TMPDIR/" 43 | 44 | pushd "$PKGDIR" > /dev/null && { 45 | echo " -- Generating configure scripts..." 46 | sh autogen.sh 47 | popd > /dev/null 48 | } 49 | 50 | echo " -- Creating tarball $TARBALL" 51 | tar --use=${packer} -C "$TMPDIR" -cf "$TARBALL" "$NAME-$VERSION"; 52 | 53 | echo " -- Calculating checksums" 54 | md5sum "$TARBALL" >"${TARBALL}.md5sum"; 55 | sha1sum "$TARBALL" >"${TARBALL}.sha1sum"; 56 | 57 | echo " -- You need to sign the tarball" 58 | gpg -u "$GPGKEY" -sb "$TARBALL"; 59 | 60 | #gpg -u "$GPGKEY" -sb "$PATCH"; 61 | #md5sum "$PATCH" >"${PATCH}.md5sum"; 62 | #sha1sum "$PATCH" >"${PATCH}.sha1sum"; 63 | -------------------------------------------------------------------------------- /iptables-module/reload_module.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Quick script for reloading the kernel module, from the current 4 | # directory. 5 | 6 | MODULE=xt_mpeg2ts 7 | 8 | echo "Reloading kernel module:" 9 | 10 | # Test if a module is loaded 11 | module_loaded() { 12 | module=$1 13 | echo " -- Test if module:$module is loaded" 14 | egrep -q ^${module} /proc/modules 2> /dev/null 15 | res=$? 16 | return $res 17 | } 18 | 19 | module_rmmod() { 20 | module=$1 21 | echo " -- Unloading module: $module" 22 | sudo rmmod $module 23 | res=$? 24 | return $res 25 | } 26 | 27 | module_insmod() { 28 | module=$1 29 | lmodule=${module}.ko 30 | echo " -- Loading module: ${lmodule}" 31 | if [ -e ${lmodule} ]; then 32 | sudo insmod ./${lmodule} 33 | else 34 | echo "ERROR: cannot find module file: ${lmodule}" 35 | fi 36 | res=$? 37 | return $res 38 | } 39 | 40 | iptables_flush() { 41 | echo " -- Flushing iptables rules" 42 | sudo iptables -F 43 | } 44 | 45 | iptables_create_rules() { 46 | echo " -- Creating iptables rules using mpeg2ts" 47 | sudo iptables -I INPUT -p udp -m mpeg2ts --name input 48 | } 49 | 50 | echo " -- Check if I need to load compat_xtables" 51 | compat=compat_xtables 52 | if (module_loaded $compat); then 53 | echo " ---- OK, compat_xtables already loaded" 54 | else 55 | echo " ---- Loading compat_xtables" 56 | sudo insmod ./${compat}.ko 57 | fi 58 | 59 | if (module_rmmod $MODULE); then 60 | echo " ---- Successfully removed module" 61 | if (! module_insmod $MODULE); then 62 | echo "ERROR(2): Cannot load module, FAILING!" 63 | exit 2 64 | fi 65 | else 66 | echo " ---- Cannot remove module" 67 | echo " ---- Assuming iptables rules have references" 68 | iptables_flush 69 | # Try again 70 | if (module_rmmod $MODULE); then 71 | echo " ---- Successfully removed module" 72 | if (! module_insmod $MODULE); then 73 | echo "ERROR(3): Cannot load module, FAILING!" 74 | exit 3 75 | fi 76 | else 77 | echo "ERROR(4): Still cannot remove module, FAILING!" 78 | exit 4 79 | fi 80 | fi 81 | 82 | iptables_create_rules 83 | -------------------------------------------------------------------------------- /webfrontend/www/js/epoch_v202_en/minimum_code.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Minimum Required Code - Epoch Prime 7 | 8 | 9 | 20 | 21 | 22 |

Minimum Required Code

23 |

This page shows the minimum amount of code required to use Epoch Prime in each mode. Right-click anywhere in the page and select "View Source" to see.

24 |

Internet Explorer Users on Windows XP with Service Pack 2: If you don't see the calendar when you load the page, make sure to click "Allow Blocked Content"—you'll only see it if you're viewing this file on your computer's file system.

25 | 26 | Back to the Getting Started Page 27 | 28 |

Basic

29 |
30 | 31 |

Multiselect

32 |
33 | 34 |

Popup

35 |
36 | 37 |




38 |

Other Resources

39 |

See our online documentation for more help and examples.

40 |

You can also ask a question on our support forums.

41 | 42 | 43 | -------------------------------------------------------------------------------- /webfrontend/www/pages/channel_view.php: -------------------------------------------------------------------------------- 1 | TRUE, 'staging' => FALSE)); 6 | 7 | echo "

Channel view

\n"; 8 | 9 | require_once("functions.inc.php"); 10 | #$displayTiming = TRUE; 11 | $starttime=getMicroTime(); 12 | 13 | $_REQUEST = cleanup_input_request(); 14 | $probeid = $_REQUEST['probeid']; 15 | $maxy = $_REQUEST['maxy']; 16 | $bucketsz = $_REQUEST['bucketsz']; 17 | $tstampF = $_REQUEST['tstampF']; 18 | $tstampT = $_REQUEST['tstampT']; 19 | 20 | 21 | $channel = $_REQUEST['channel']; 22 | if (!isset($channel)) { 23 | echo "

Please select a channel

\n"; 24 | } else { 25 | echo "

Multicast channel:" 26 | ." $channel

\n"; 27 | } 28 | 29 | db_connect(); 30 | 31 | # Select all channel within the period 32 | $channels =& multicast_list_query(); 33 | #multicast_list_form_select($channels); 34 | 35 | $probesinfo = probes_info_query(); 36 | 37 | form_channel_selection($channels, $probesinfo); 38 | # 39 | # Side effects of form converts the input fromT and toT 40 | # to a timestamp and store its in the $_REQUEST variable 41 | # 42 | $tstampF = $_REQUEST['tstampF']; 43 | $tstampT = $_REQUEST['tstampT']; 44 | 45 | 46 | if (isset($channel)) { 47 | // Get info on multicast channel 48 | $startqt=getMicroTime(); 49 | $data =& one_channel_info_query_ts($channel, $tstampF, $tstampT); 50 | displayTimingInfo($startqt, getMicroTime(), $displayTiming, "query_table"); 51 | 52 | #print_r($data); 53 | 54 | one_channel_info_show_table($data, $tstampF, $tstampT); 55 | 56 | // Trick incl the graph php script 57 | include("include/graph_channel_drops_bar01.php"); 58 | } else { 59 | echo "

Cannot generate a channel graph without a channel

"; 60 | } 61 | 62 | db_disconnect(); 63 | 64 | function linkto_probe_overview($probename, & $req) 65 | { 66 | $query = http_build_query($req); 67 | echo "\n

"; 68 | echo "Goto Probe overview: "; 69 | echo ""; 71 | echo $probename; 72 | echo "

\n"; 73 | } 74 | linkto_probe_overview($probename, $_REQUEST); 75 | 76 | displayTimingInfo($starttime, getMicroTime(), $displayTiming, "php done"); 77 | ?> 78 | -------------------------------------------------------------------------------- /iptables-module/xt_mpeg2ts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Header file for MPEG2 TS match extension "mpeg2ts" for Xtables. 3 | * 4 | * Copyright (c) Jesper Dangaard Brouer , 2009-2013 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License; either 8 | * version 2 of the License, or any later version, as published by the 9 | * Free Software Foundation. 10 | * 11 | */ 12 | #ifndef _LINUX_NETFILTER_XT_MPEG2TS_MATCH_H 13 | #define _LINUX_NETFILTER_XT_MPEG2TS_MATCH_H 1 14 | 15 | /* Get version number PACKAGE_VERSION from configure system */ 16 | #include "../config.h" 17 | 18 | /* XT_MODULE_NAME could be replaced by KBUILD_MODNAME, if this version 19 | info were only used the kernel module, but we also use it in 20 | userspace. 21 | */ 22 | #define XT_MODULE_NAME "xt_mpeg2ts" 23 | #define XT_MODULE_VERSION PACKAGE_VERSION 24 | #define PFX XT_MODULE_NAME ": " 25 | 26 | static char version[] = 27 | XT_MODULE_NAME ".c:v" XT_MODULE_VERSION \ 28 | " (part of " PACKAGE_NAME ")"; 29 | 30 | enum { 31 | XT_MPEG2TS_DETECT_DROP = 1 << 0, 32 | XT_MPEG2TS_MAX_STREAMS = 1 << 1, 33 | XT_MPEG2TS_PARAM_NAME = 1 << 2, 34 | XT_MPEG2TS_MATCH_DROP = 1 << 3, 35 | /*Future:*/ 36 | XT_MPEG2TS_RECORD_COUNTERS = 1 << 4, 37 | }; 38 | 39 | /* Details of this hash structure is hidden in kernel space xt_mpeg2ts.c */ 40 | struct xt_rule_mpeg2ts_conn_htable; 41 | 42 | struct mpeg2ts_cfg { 43 | 44 | /* Hash table setup */ 45 | __u32 size; /* how many hash buckets */ 46 | __u32 max; /* max number of entries */ 47 | __u32 max_list; /* warn if list searches exceed this number */ 48 | }; 49 | 50 | 51 | struct xt_mpeg2ts_mtinfo { 52 | __u16 flags; 53 | 54 | /* FIXME: 55 | 56 | I need to fix the problem, where I have to reallocated data 57 | each time a single rule change occur. 58 | 59 | The idea with rule_name and rule_id is that the name is 60 | optional, simply to provide a name in /proc/, the rule_id 61 | is the real lookup-key in the internal kernel list of the 62 | rules associated dynamic-allocated-data. 63 | 64 | */ 65 | char rule_name[IFNAMSIZ]; 66 | 67 | struct mpeg2ts_cfg cfg; 68 | 69 | /** Below used internally by the kernel **/ 70 | __u32 rule_id; 71 | 72 | /* Hash table pointer */ 73 | struct xt_rule_mpeg2ts_conn_htable *hinfo __attribute__((aligned(8))); 74 | }; 75 | 76 | #endif /* _LINUX_NETFILTER_XT_MPEG2TS_MATCH_H */ 77 | -------------------------------------------------------------------------------- /collector/etc/collector.conf.sample: -------------------------------------------------------------------------------- 1 | # 2 | # Config file location: /etc/iptv-analyzer/collector.conf 3 | # 4 | 5 | # DB setup 6 | dbhost = localhost 7 | dbname = tvprobe 8 | dbuser = tvprobe 9 | dbpass = tvprobepasswd 10 | db_reconnect_delay=30 11 | db_reconnect_tries=10 12 | 13 | # Collector setup 14 | collector_interval=10 15 | collector_heartbeat_ticks=60 16 | 17 | # For emailing on error, you can use the log4perl.conf file. 18 | 19 | # Identification of the probe (the probe_ip is the main identifier 20 | # together with input[hashkey]) 21 | probe_ip = 192.168.16.42 22 | probe_name = tvprobe-dev2 23 | 24 | # Notice the location is tied to the given 'input'. The probe can 25 | # measure on several interfaces, and the measurement points could (via 26 | # e.g. fiber) be far a part. 27 | 28 | # SNMP trap setup 29 | snmptraphost = 87.72.129.222 30 | snmpcommunity = public 31 | 32 | # Input files to parse 33 | # -------------------- 34 | # The keys used for identifying an input in the DB is: 35 | # 1. probe_ip 36 | # 2. the input[hashkey] e.g. 'rule_input_eth0' 37 | # 3. the short location [shortloc] value 38 | # 4. the switch name [switch] value 39 | # 40 | # If any of these keys are changed, an new DB record will be created, 41 | # with a new id number in the table 'probes'. Its allowed to update the 42 | # other keys without changing the id. 43 | # 44 | # Required option, which proc file to read 45 | input[rule_in_eth0][procfile] = /proc/net/xt_mpeg2ts/rule_in_eth0 46 | # 47 | # The proc file is created with: 48 | # iptables -t mangle -I PREROUTING -i eth0 -p udp -m mpeg2ts --name in_eth0 49 | # 50 | # REQUIRED OPTIONS that identifies this input 51 | input[rule_in_eth0][shortloc] = alb 52 | input[rule_in_eth0][switch] = albcr3 53 | # 54 | # Optional update-able config settings 55 | input[rule_in_eth0][description]= Main signal 56 | input[rule_in_eth0][distance] = 2 57 | input[rule_in_eth0][location] = Serverrum A 58 | input[rule_in_eth0][address] = Herstedvang 42, 2620 Albertslund 59 | input[rule_in_eth0][switchport] = e1/4 60 | input[rule_in_eth0][switchtype] = HP9300 61 | input[rule_in_eth0][input_ip] = 192.168.16.42 62 | input[rule_in_eth0][input_dev] = eth42 63 | 64 | # Remember: Change to use complete path as the daemon chdir's to "/" 65 | #input[test1][procfile] = ../input/proc_rule_test001.input 66 | #input[test1][shortloc] = cph 67 | #input[test1][switch] = switch002a 68 | #input[test1][distance] = 44 69 | #input[test1][description] = Test case 70 | #input[test1][hidden] = yes 71 | -------------------------------------------------------------------------------- /webfrontend/README.dependencies: -------------------------------------------------------------------------------- 1 | ~~ -*-text-*- 2 | 3 | ------------------------------------------------------- 4 | Web-interface dependencies 5 | (for our own IPTV probes) 6 | ------------------------------------------------------- 7 | Jesper Dangaard Brouer (jdb@comx.dk) 8 | ------------------------------------------------------- 9 | $LastChangedRevision$ 10 | $Date$ 11 | ------------------------------------------------------- 12 | 13 | Intro 14 | ~~~~~ 15 | 16 | This document describe/list the Debian package dependencies for the 17 | web frontend interface (for our own IPTV probes). 18 | 19 | 20 | Packages and software 21 | ~~~~~~~~~~~~~~~~~~~~~ 22 | 23 | * Webserver and PHP 24 | ~~~~~~~~~~~~~~~~~~~ 25 | 26 | Apache and PHP 27 | 28 | sudo aptitude install php5 php5-gd php5-mysql 29 | 30 | 31 | * PEAR - PHP Extension and Application Repository 32 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 33 | 34 | Need to install PEAR. 35 | 36 | sudo aptitude install php-pear 37 | 38 | 39 | ** PEAR module: Image_Graph 40 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 41 | 42 | The module "Image_Graph" is used for generating the graphs. 43 | 44 | pear/Image_Graph dependencies: 45 | pear/Image_Canvas, 46 | pear/Numbers_Roman, 47 | pear/Numbers_Words 48 | 49 | 50 | Need to call "upgrade-all" to get upgraded the installer, else its 51 | not possible to install "Image_Canvas". 52 | 53 | Pear install commands: 54 | 55 | +-------- 56 | sudo pear upgrade-all 57 | 58 | sudo pear install --alldeps Image_Color-alpha 59 | 60 | sudo pear install --alldeps Image_Canvas-alpha 61 | 62 | sudo pear install --alldeps Image_Graph-alpha 63 | 64 | sudo pear install --alldeps Log 65 | +-------- 66 | 67 | 68 | ** Image_Graph: Issues with fonts under Linux: 69 | ~~~~~~~~~~~~~~ 70 | 71 | http://web.archive.org/web/20071123171133rn_1/www.outwardmotion.com/outwardmotion/imagegraph.php 72 | 73 | aptitude install ttf-freefont 74 | 75 | +------ 76 | $myfont = '/usr/share/fonts/truetype/freefont/FreeSerif.ttf'; 77 | $Font =& $Graph->addNew('font', $myfont); 78 | //$Font =& $Graph->addNew('font', 'Verdana'); 79 | //$Font =& $Graph->addNew('font', 'Helvetica'); 80 | // set the font size 81 | $Font->setSize(8); 82 | $Graph->setFont($Font); 83 | +------ 84 | 85 | 86 | Docs on the PHP graph module 87 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 88 | 89 | PHP graph module 90 | 91 | http://pear.veggerby.dk/wiki/image_graph:getting_started_guide 92 | 93 | http://pear.php.net/package/Image_Graph 94 | 95 | -------------------------------------------------------------------------------- /webfrontend/www/design.inc.php: -------------------------------------------------------------------------------- 1 | 22 | 24 | 25 | 26 | TVPROBE <?=$title?> 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 39 | 42 | 45 | 46 | 48 | 49 | 50 | 52 | 53 | 66 | 69 | 70 | 71 | \n"; 77 | echo "\n"; 78 | } 79 | ?> 80 | -------------------------------------------------------------------------------- /collector/README.wiki: -------------------------------------------------------------------------------- 1 | 2 | = IPTV-collector daemon = 3 | 4 | The IPTV-collector daemon is part of the IPTV-analyzer project. 5 | 6 | The IPTV-collector daemon is written in Perl. To follow through on 7 | the Perl path, we use a Perl CPAN style MakeMaker setup. 8 | 9 | The basic command to produce a makefile is: 10 | 11 | perl Makefile.PL 12 | 13 | 14 | == INSTALLATION == 15 | 16 | To install this module type the following: 17 | 18 | perl Makefile.PL 19 | make 20 | make install 21 | 22 | To install this software/module in a controllable fashion, I recommend 23 | using 'stow'. Using stow requires the ability to install the binaries 24 | to an alternative path. Use the following commands: 25 | 26 | perl Makefile.PL PREFIX=/usr/local/ 27 | make 28 | make install PREFIX=/usr/local/stow/iptv-collector-0.3.x 29 | 30 | The stow part: 31 | 32 | cd /usr/local/stow/ 33 | stow iptv-collector-0.3.x 34 | 35 | 36 | == DAEMON == 37 | 38 | The collector daemon is call 'iptv-collector'. 39 | 40 | Its simply started by calling 'iptv-collector', which will 41 | automatically spawn a daemon process. 42 | 43 | The daemon can be restarted by simple starting another 44 | 'iptv-collector' process, which will detect and kill the other daemon 45 | process. The reason this is implemented, is because we cannot allow 46 | two collector processes to operating at the same time, as they would 47 | record drop count twice into the database. 48 | 49 | TODO: Describe the pid file perms problem 50 | 51 | 52 | === CONFIG FILES === 53 | 54 | The iptv-collector daemon depends on two config files: 55 | 56 | /etc/iptv-analyzer/collector.conf 57 | 58 | and 59 | 60 | /etc/log4perl.conf 61 | 62 | Example/sample config files are available under "etc" directory. 63 | 64 | 65 | == DEPENDENCIES == 66 | 67 | The dependencies will hopefully be detected for you when generating 68 | the makefile. 69 | 70 | === Debian packages === 71 | 72 | The following Debian packages are needed by the collector daemon: 73 | 74 | * libproc-daemon-perl 75 | * libproc-pid-file-perl 76 | * libconfig-file-perl 77 | * libdata-compare-perl 78 | (libnumber-compare-perl libtext-glob-perl libfile-find-rule-perl) 79 | * libdbi-perl 80 | (libnet-daemon-perl libplrpc-perl) 81 | * libdbd-mysql-perl 82 | * liblog-log4perl-perl 83 | (libipc-shareable-perl liblog-dispatch-perl libparams-validate-perl) 84 | * mysql-client 85 | * mysql-server 86 | * liblog-dispatch-perl (for email extention to Log4perl) 87 | * libnet-snmp-perl 88 | 89 | == COPYRIGHT AND LICENCE == 90 | 91 | Copyright (C) 2009-2013 by Jesper Dangaard Brouer 92 | 93 | This software is licensed under the terms of the GNU General Public 94 | License 2.0. or newer. See . 95 | -------------------------------------------------------------------------------- /collector/Makefile.PL: -------------------------------------------------------------------------------- 1 | use ExtUtils::MakeMaker; 2 | 3 | # EXE_FILES is a bit strange, we need it getting the bin/ files 4 | # copied, but is also means that these files are getting copied to the 5 | # dir blib/script/ (which I dont understand the purpose of). 6 | our @exe_files; 7 | push @exe_files, 'bin/iptv-collector'; 8 | 9 | ## Automatically find files in "bin" directory 10 | #my $bindir="bin"; 11 | #opendir (DIR, $bindir) or warn "Couldn't open directory, $!"; 12 | #while ($file = readdir(DIR)) 13 | #{ 14 | # next if ($file =~ m/^\./); 15 | # print "Found bin file: $file\n"; 16 | # push @exe_files, "$bindir/$file"; 17 | #} 18 | #closedir DIR; 19 | 20 | # Add some extra to the generated Makefile 21 | { 22 | package MY; 23 | sub postamble { 24 | ' 25 | INST_ETC = /etc/iptv-analyzer 26 | 27 | install:: 28 | $(NOECHO) $(ECHO) Installing sample config in: $(INST_ETC) 29 | $(NOECHO) test -d $(INST_ETC) || $(MKPATH) $(INST_ETC) 30 | $(CP) etc/log4perl.conf.sample $(INST_ETC)/ 31 | $(CP) etc/collector.conf.sample $(INST_ETC)/ 32 | $(CP) etc/version $(INST_ETC)/ 33 | 34 | # Trick to regenerate Version.pm if configure got updated 35 | lib/IPTV/Analyzer/Version.pm: lib/IPTV/Analyzer/Version.pm.in ../config.status 36 | cd .. && ./config.status collector/$@ 37 | 38 | # Trick to regenerate etc/version if configure got updated 39 | etc/version: etv/version.in ../config.status 40 | cd .. && ./config.status collector/$@ 41 | 42 | # Also check if config.status need to be updated 43 | ../config.status: ../configure 44 | cd .. && ./config.status --recheck 45 | 46 | ' 47 | } 48 | } 49 | # TODO: Add trick to activate ../configure to update version 50 | # if changes to configure.ac happened. 51 | # And let Makefile(.PL) depend on changes to Version.pm 52 | 53 | my %attr = ( 54 | NAME => 'iptv-collector', 55 | # VERSION => '0.x.x', 56 | # VERSION_FROM => 'lib/IPTV/Analyzer/mpeg2ts.pm', # finds $VERSION 57 | VERSION_FROM => 'lib/IPTV/Analyzer/Version.pm', # finds $VERSION 58 | PREREQ_PM => { 59 | 'Log::Log4perl' => 0, 60 | 'Config::File' => 0, 61 | 'Proc::Daemon' => 0, 62 | 'Proc::PID::File' => 0, 63 | 'Data::Compare' => 0, 64 | 'File::Basename' => 0, 65 | 'DBI' => 0, 66 | 'DBD::mysql' => 0, 67 | 'Log::Dispatch::Email' => 0, 68 | 'Net::SNMP' => 0, 69 | }, 70 | PL_FILES => {}, # Prevent old MakeMaker from running Build.PL 71 | EXE_FILES => \@exe_files, 72 | # 'dist' => {COMPRESS => 'gzip', SUFFIX => 'gz'}, 73 | # realclean => {FILES => join ' ', @clean_files}, 74 | AUTHOR => 'Jesper Dangaard Brouer', 75 | ABSTRACT => 'IPTV-analyzer mpeg2ts Collector daemon', 76 | # INSTALLDIRS => 'site', 77 | ); 78 | 79 | (my $mmv = ExtUtils::MakeMaker->VERSION) =~ s/_//g; 80 | $mmv >= 6.31 and $attr{LICENSE} = 'GPL'; 81 | 82 | WriteMakefile (%attr); 83 | -------------------------------------------------------------------------------- /collector/bin/generate-test-snmptrap.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w -I../lib/ 2 | 3 | =head1 NAME 4 | 5 | generate-test-snmptrap - Manually generating a collector snmptrap 6 | 7 | =head1 SYNOPSIS 8 | 9 | generate-test-snmptrap [options] 10 | 11 | This program can be used for test-generating different types of SNMP 12 | traps, which is generated by the iptv-collector in different error 13 | situations. This is very useful when adjusting you infrastructure to 14 | handle these different types of traps, without having to provoke these 15 | errors in production. 16 | 17 | =cut 18 | 19 | use strict; 20 | use warnings; 21 | use Data::Dumper; 22 | 23 | use IPTV::Analyzer::Config; 24 | my $cfg = get_config(); 25 | #my $cfg = IPTV::Analyzer::Config->new(); 26 | 27 | #use IPTV::Analyzer::mpeg2ts; 28 | use IPTV::Analyzer::snmptrap; 29 | 30 | 31 | use Getopt::Long qw(:config no_ignore_case); 32 | use Pod::Usage; 33 | 34 | my %opt; 35 | my ($opt_help, $opt_man); 36 | 37 | GetOptions( 38 | 'traphost|h=s' => \$opt{traphost}, 39 | 'community=s' => \$opt{community}, 40 | 'multicast=s' => \$opt{multicast}, 41 | 'src_ip=s' => \$opt{src_ip}, 42 | 'timeticks=i' => \$opt{src_ip}, 43 | 'help!' => \$opt_help, 44 | 'man!' => \$opt_man 45 | ) or pod2usage(-verbose => 0); 46 | 47 | pod2usage(-verbose => 1) if defined $opt_help; 48 | pod2usage(-verbose => 2) if defined $opt_man; 49 | 50 | # Assign default values if not defined 51 | $opt{traphost} = $opt{traphost} || '127.0.0.1'; 52 | $opt{community} = $opt{community} || 'public'; 53 | $opt{multicast} = $opt{multicast} || '224.224.224.224'; 54 | $opt{src_ip} = $opt{src_ip} || '10.10.10.42'; 55 | $opt{timeticks} = $opt{timeticks} || 6000; 56 | 57 | =head2 OPTIONS 58 | 59 | --traphost|-h Who to send the trap to (default: 127.0.0.1) 60 | --community The SNMPv2 community (default: public) 61 | --multicast The multicast dst address having issues (default: 224.1.2.3) 62 | --src_ip The streams source IP-address (default:10.10.10.42) 63 | --timeticks The uptime reported in SNMP-v2 trap (default: 6000(=1min)) 64 | 65 | =cut 66 | 67 | print "open_snmp_session() to $opt{traphost}\n"; 68 | my $res_ses = open_snmp_session($opt{traphost}, $opt{community}); 69 | #open_snmp_session("127.0.0.1", "public"); 70 | if (!$res_ses) { 71 | print " - ERROR: cannot open session\n"; 72 | } else { 73 | print " - Established SNMP session\n"; 74 | } 75 | 76 | 77 | print "send_snmptrap()\n"; 78 | my $res = send_snmptrap("no_signal", "critical", "rule_input", 79 | $opt{timeticks}, $opt{multicast}, $opt{src_ip}); 80 | if (!$res) { 81 | print " - ERROR: cannot send snmptrap\n"; 82 | } else { 83 | print " - SNMP trap transmitted\n"; 84 | } 85 | 86 | 87 | print "close_snmp_session()\n"; 88 | close_snmp_session(); 89 | 90 | #print "send_snmptrap()\n"; 91 | #send_snmptrap(); 92 | 93 | print "END\n"; 94 | -------------------------------------------------------------------------------- /webfrontend/www/js/epoch_v202_en/epoch_styles.css: -------------------------------------------------------------------------------- 1 | /*!! 2 | Epoch DHTML JavaScript Calendar - Version 2.0.2 3 | English Edition 4 | CSS Style File 5 | (c) 2006-2007 MeanFreePath 6 | Free for NON-COMMERCIAL use - see website for details and updates 7 | http://www.meanfreepath.com/javascript_calendar/index.html 8 | !!*/ 9 | 10 | table.calendar { 11 | font-family: Helvetica, Arial, sans-serif; 12 | font-size: 0.8em; 13 | border-collapse: collapse; 14 | background-color: white; 15 | border: solid #999999 1px; 16 | background-color: white; 17 | width: 215px; 18 | text-align: center; 19 | /*prevent user from selecting text in Mozilla & Safari - check calendar constructor for IE code)*/ 20 | -moz-user-select: none; 21 | /*-khtml-user-select: none;*/ 22 | } 23 | table.calendar a { 24 | } 25 | table.calendar a:hover { 26 | } 27 | table.calendar input, table.calendar select { 28 | font-size: 10px; 29 | } 30 | table.calendar td, table.calendar th { 31 | border: 0; 32 | font-size: 10px; 33 | text-align: center; 34 | } 35 | div.mainheading { 36 | margin: 2px; 37 | } 38 | .closeBtn { 39 | /*float: right; 40 | width: 15px; 41 | /*font-size: 1.5em; 42 | height: 13px; 43 | 44 | padding: 0 0 3px 0; 45 | margin: 1px 8px 0 0; 46 | border: solid black 1px;*/ 47 | } 48 | /*all styles related to the main calendar grid*/ 49 | table.cells { 50 | border-collapse: collapse; 51 | border: solid #CCCCCC 1px; 52 | cursor: pointer; 53 | empty-cells: show; 54 | margin: 0 6px 0 6px; 55 | } 56 | /*the day headings*/ 57 | table.cells th { 58 | border: solid #CCCCCC 1px; 59 | text-align: left; 60 | font-weight: bold; 61 | color: #0054E3; 62 | width: 22px; 63 | } 64 | table.cells th.wkhead { 65 | border-right: double #CCCCCC 3px; 66 | cursor: default; 67 | width: 22px; 68 | } 69 | /*The date cells*/ 70 | table.cells td { 71 | border: solid #CCCCCC 1px; 72 | vertical-align: top; 73 | text-align: left; 74 | font-weight: bold; 75 | height: 20px; /*IE doesn't like ems*/ 76 | } 77 | table.cells td.wkhead { 78 | background-color: white; 79 | text-align: center; 80 | border-right: double #CCCCCC 3px; 81 | color: #0054E3; 82 | } 83 | table.cells td.noselect { 84 | background-color: #EEEEEE; 85 | color: #BBBBBB; 86 | text-decoration: line-through; 87 | cursor: default; 88 | } 89 | table.cells td.hlday { 90 | background-color: #99FF99; 91 | } 92 | table.cells td.wkday { 93 | background-color: #DDDDDD; 94 | } 95 | table.cells td.wkend { 96 | background-color: #DDDDDD; 97 | } 98 | table.cells td.curdate { 99 | 100 | } 101 | table.cells td.cell_selected { 102 | background-color: #99CCFF; 103 | color: black; 104 | } 105 | table.cells td.notmnth { 106 | background-color: #FFFFFF; 107 | color: #CCCCCC; 108 | } 109 | table.cells td.notallowed { 110 | background-color: white; 111 | color: #EEEEEE; 112 | font-style: italic; 113 | } 114 | table.cells td.hover { 115 | background-color: #999999; 116 | } 117 | table.cells td div { 118 | padding: 1px; 119 | margin: 0; 120 | } 121 | -------------------------------------------------------------------------------- /doc/snmptrap.wiki: -------------------------------------------------------------------------------- 1 | 2 | = SNMP traps = 3 | 4 | The snmptrap feature is used for event based reporting, like no-signal 5 | detection. 6 | 7 | Currently implemented: 8 | 9 | * "no_signal" / streamNoSignal trap (cleared with eventSeverity=0) 10 | 11 | A future/planned trap type is "ttl_change", which traps on changes 12 | in the Time-To-Live (TTL). 13 | 14 | == Config options == 15 | 16 | Add the following lines to your config "/etc/iptv-analyzer/collector.conf": 17 | 18 | snmptraphost = your-trap-host 19 | snmpcommunity = public 20 | 21 | == Base OID == 22 | 23 | Use enterprise OID from ComX Networks A/S (IANA assigned) as base OID: 24 | 25 | .1.3.6.1.4.1.26124 26 | 27 | Using sub OID 43 for the IPTV-Analyzer: 28 | 29 | .1.3.6.1.4.1.26124.43 30 | 31 | == The MIB definition == 32 | 33 | The MIB is located in: 34 | snmp/mibs/ 35 | 36 | Two MIB files IPTV-ANALYZER-MIB.txt and CXNET-CORE-MIB.txt 37 | 38 | == Installing MIBs == 39 | 40 | === Manual installing the MIBs === 41 | 42 | Manual installing the MIBs, for usage with snmp tools: 43 | 44 | cd snmp/mibs 45 | cp CXNET-CORE-MIB.txt /usr/share/snmp/mibs/ 46 | cp IPTV-ANALYZER-MIB.txt /usr/share/snmp/mibs/ 47 | 48 | Testing they can be decoded: 49 | 50 | snmptranslate -m CXNET-CORE-MIB 1.3.6.1.4.enterprises.cxnet 51 | snmptranslate -m IPTV-ANALYZER-MIB -Of 1.3.6.1.4.1.26124.43.1.1 52 | snmptranslate -m IPTV-ANALYZER-MIB -Of 1.3.6.1.4.1.26124.43.2.2.2 53 | 54 | === ZenOSS and MIBs === 55 | 56 | Importing the MIBs into ZenOSS. 57 | 58 | First copy file to where ZenOSS keps its MIB files. (Notice I renamed 59 | the files to make them look more like the ZenOSS naming scheme.) 60 | 61 | export ZENOSS_MIBS=/usr/local/zenoss/share/mibs/site/ 62 | cd snmp/mibs 63 | cp CXNET-CORE-MIB.txt ${ZENOSS_MIBS}/cxnet-core.mib 64 | cp IPTV-ANALYZER-MIB.txt ${ZENOSS_MIBS}/iptv-analyzer.mib 65 | 66 | Import these into ZenOSS: 67 | 68 | su - zenoss 69 | export ZENOSS_MIBS=/usr/local/zenoss/share/mibs/site/ 70 | cd ${ZENOSS_MIBS} 71 | zenmib run -v 10 cxnet-core.mib iptv-analyzer.mib 72 | 73 | Notice, that both files need to be specified together, or else it 74 | cannot find the Enterprice OID mapping, and stuff does not work. 75 | 76 | See if the MIB got imported and decoded via your webbrowser: 77 | 78 | http://zenoss.yourdomain.dk:8080/zport/dmd/Mibs/mibs/IPTV-ANALYZER-MIB/ 79 | 80 | == Sending an SNMP test trap == 81 | 82 | In "collector/bin" there is a script for generating test traps: 83 | 84 | cd collector/bin 85 | ./generate-test-snmptrap.pl --help 86 | 87 | 88 | = NOTES = 89 | 90 | == Debian software packages == 91 | 92 | === Important === 93 | 94 | libnet-snmp-perl -- Net::SNMP 95 | 96 | === Unimportant === 97 | 98 | snmp - SNMP (Simple Network Management Protocol) applications 99 | snmp-mibs-downloader 100 | 101 | 102 | == Hints on Writing a MIB file == 103 | 104 | Helper links used when writing the MIB file describing the traps. 105 | 106 | SNMP v2 traps uses "NOTIFICATION-TYPE" for traps. 107 | [http://www.freesoft.org/CIE/RFC/1902/59.htm] 108 | 109 | [http://www.net-snmp.org/tutorial/tutorial-5/commands/snmptrap.html] 110 | 111 | Example see: /usr/share/mibs/ietf/OSPF-TRAP-MIB 112 | -------------------------------------------------------------------------------- /collector/etc/log4perl.conf.sample: -------------------------------------------------------------------------------- 1 | # 2 | # Log4perl configuration example file to the IPTV-analyzer 3 | # iptv-collector daemon 4 | # 5 | 6 | log4perl.logger = DEBUG, Screen1 7 | log4perl.logger.IPTV.Analyzer = INFO, File1 8 | 9 | 10 | # Output: Screen1 11 | # --------------- 12 | # log4perl.appender.Screen1 = Log::Log4perl::Appender::Screen 13 | log4perl.appender.Screen1 = Log::Log4perl::Appender::ScreenColoredLevels 14 | # 15 | # Don't print to STDERR because in the daemon 16 | # we TIE STDERR from other programs to log4perl 17 | log4perl.appender.Screen1.stderr = 0 18 | # log4perl.appender.Screen1.Threshold = WARN 19 | # log4perl.appender.Screen1.layout = Log::Log4perl::Layout::SimpleLayout 20 | log4perl.appender.Screen1.layout = Log::Log4perl::Layout::PatternLayout 21 | log4perl.appender.Screen1.layout.ConversionPattern = %d [%P] %c %p> %m%n 22 | 23 | # Output: File1 24 | # ------------- 25 | log4perl.appender.File1 = Log::Log4perl::Appender::File 26 | log4perl.appender.File1.filename = /var/log/tvprobe.log 27 | log4perl.appender.File1.mode = append 28 | log4perl.appender.File1.umask = 0002 29 | # log4perl.appender.File1.Threshold = WARN 30 | # log4perl.appender.File1.Additivity = 0 31 | # log4perl.appender.File1.mode = write 32 | # log4perl.appender.File1.binmode = ":utf8" 33 | # log4perl.appender.File1.layout = Log::Log4perl::Layout::SimpleLayout 34 | log4perl.appender.File1.layout = Log::Log4perl::Layout::PatternLayout 35 | log4perl.appender.File1.layout.ConversionPattern = %d [pid:%P] %C %l %p> %m%n 36 | # log4perl.appender.File1.layout.ConversionPattern = %d [pid:%P] %c %l %p>\t%m%n 37 | # log4perl.appender.File1.layout.ConversionPattern = %d [pid:%P] %c [file:%F{1}] %p>\t%m%n 38 | 39 | 40 | # Emailing log messages over a given level 41 | # -------- 42 | log4perl.category = FATAL, Mailer 43 | log4perl.appender.Mailer = Log::Dispatch::Email::MailSend 44 | log4perl.appender.Mailer.Threshold = ERROR 45 | log4perl.appender.Mailer.to = netoptimizer@brouer.com 46 | log4perl.appender.Mailer.subject = FAILURE in mpeg2ts iptv-collector daemon 47 | log4perl.appender.Mailer.layout = SimpleLayout 48 | #log4perl.appender.Mailer.buffered = 0 49 | 50 | 51 | # http://search.cpan.org/~mschilli/Log-Log4perl-1.04/lib/Log/Log4perl/Layout/PatternLayout.pm 52 | # 53 | # layout.ConversionPattern: 54 | # ------------------------ 55 | # %c Category of the logging event. 56 | # %C Fully qualified package (or class) name of the caller 57 | # %d Current date in yyyy/MM/dd hh:mm:ss format 58 | # %F File where the logging event occurred 59 | # %H Hostname (if Sys::Hostname is available) 60 | # %l Fully qualified name of the calling method followed by the 61 | # callers source the file name and line number between 62 | # parentheses. 63 | # %L Line number within the file where the log statement was issued 64 | # %m The message to be logged 65 | # %M Method or function where the logging request was issued 66 | # %n Newline (OS-independent) 67 | # %p Priority of the logging event 68 | # %P pid of the current process 69 | # %r Number of milliseconds elapsed from program start to logging 70 | # event 71 | # %T A stack trace of functions called 72 | # %x The topmost NDC (see below) 73 | # %X{key} The entry 'key' of the MDC (see below) 74 | # %% A literal percent (%) sign 75 | -------------------------------------------------------------------------------- /webfrontend/www/css/motorola.css: -------------------------------------------------------------------------------- 1 | body { font-family: Verdana, Arial, sans-serif; 2 | font-size: 11px; 3 | color: #000000; 4 | background: #FFFFFF } 5 | a { text-decoration: none; } 6 | a:link { color: #0000FF; background: transparent } 7 | a:visited { color: #000077; background: transparent } 8 | a:hover { text-decoration: underline; } 9 | a:active { color: #FF0000; background: transparent } 10 | 11 | body.menuleft { background: #FFFFFF url('../general/column_leftside-dontscalewidth.png') repeat-y fixed; } 12 | body.menuright { background: #FFFFFF url('../general/column_rightshadow-dontscalewidth.png') repeat-y fixed; } 13 | 14 | /* All headings share the same color */ 15 | h1, h2, h3, h4, h5, h6 { color: #6F889C; background: transparent } 16 | h1 { font-size: 24px; margin-top: 32px; margin-bottom: 12px } 17 | h2 { font-size: 19px; margin-top: 32px; margin-bottom: 10px } 18 | h3 { font-size: 16px; margin-top: 16px; margin-bottom: 2px } 19 | h4 { font-size: 14px; margin-top: 12px; margin-bottom: 0px } 20 | h5 { font-size: 13px; margin-top: 10px; margin-bottom: 0px } 21 | 22 | p { margin-top: 0px; margin-bottom: 12px } 23 | ul { margin-top: 12px; margin-bottom: 12px } 24 | li { margin-top: 12px; margin-bottom: 12px } 25 | kbd, code, samp { font-family: Courier, monospace } 26 | 27 | pre { font-family: Courier, monospace; 28 | padding-top: 1ex; padding-bottom: 1ex; 29 | padding-left: 1ex; padding-right: 1ex; 30 | color: #000000; background: #F4F4F4; 31 | border: 1px solid #D0D0D0; } 32 | pre code em { color: red; background: #E8E8E8; font-style: normal } 33 | pre code del { font-style: normal; text-decoration: line-through } 34 | 35 | p.center { text-align: center } 36 | p.note { padding: 0.5ex; color: #000000; background: #AFC8DC; } 37 | p.figure { text-align: center } 38 | p.figuredesc { text-align: center; font-style: italic; 39 | margin-left: 10%; margin-right: 10% } 40 | p.caption { font-style: italic; margin-bottom: 5px } 41 | 42 | caption, th { color: #6F889C; background: transparent } 43 | caption { font-size: 12px; font-weight: bold; 44 | margin-top: 12px; margin-bottom: 4px } 45 | 46 | table { font-size: 11px; border-collapse: collapse; 47 | margin-top: 0px; margin-bottom: 12px; border-color: #6F889C; } 48 | th, td { border-width: 1px; border-style: solid; 49 | vertical-align: baseline; border-color: #6F889C; } 50 | th { padding: 3px; background: #E8E8E8 } 51 | th.hidden { border-style: none; color: #FFFFFF; background: #FFFFFF } 52 | th.noframe { border-style: none; text-align: left; 53 | padding-left: 0px; padding-right: 6px; 54 | color: #000000; background: #FFFFFF } 55 | td { padding: 2px } 56 | td.noframe { border-style: none; background: #FFFFFF } 57 | 58 | /* List elements in tables */ 59 | td ol { margin-top: 0px; margin-bottom: 0px } 60 | td li { margin-top: 0px; margin-bottom: 0px } 61 | td p { margin-top: 12px; margin-bottom: 0px; } 62 | 63 | div.main { width: 850px; 64 | height: 81px; 65 | background: url('../general/main.jpg') no-repeat; } 66 | 67 | div.group { clear: both } 68 | div.left { float: left; margin-right: 10px } 69 | div.right { float: right; margin-left: 10px } 70 | 71 | img.left { float: left } 72 | img.right { float: right } 73 | img.dialog-left { margin-right: 10px; float: left } 74 | img.dialog-right { margin-left: 10px; float: right } 75 | img.inline { vertical-align: top } 76 | 77 | -------------------------------------------------------------------------------- /webfrontend/www/include/graph_probe_drops_bar01.php: -------------------------------------------------------------------------------- 1 | $err\n"; 34 | exit; 35 | /* 36 | db_connect(); 37 | $probes = probes_info_query(); 38 | echo "\n

Please select a probe:

\n"; 39 | probes_info_form_tabel($probes, $probeid); 40 | */ 41 | } 42 | 43 | # INPUT parsing 44 | # ------------- 45 | $valid = true; 46 | if (!$probeid) { 47 | $valid = false; 48 | $err = "

Please choose a valid probe

"; 49 | die("$err"); 50 | } else { 51 | $probe = $probes[$probeid]; 52 | if (!is_array($probe)) { 53 | $valid = false; 54 | } else { 55 | // extract probename, switch and shortloc 56 | $probename = $probe['name']; 57 | $switch = $probe['switch']; 58 | $shortloc = $probe['shortloc']; 59 | } 60 | } 61 | if ($probename == "") { 62 | $probename = "probeID:$probeid"; 63 | } 64 | 65 | $droptype = $_REQUEST['droptype']; 66 | if ($droptype == "") { 67 | // Default drop type 68 | $droptype = 'drops'; 69 | } elseif (($droptype != 'skips') && ($droptype != 'drops')) { 70 | $droptype = "INVALID-DROPTYPE"; 71 | $valid = false; 72 | } 73 | $title = "$droptype on $probename/$switch/$shortloc"; 74 | 75 | 76 | # GRAPH creation 77 | # -------------- 78 | $Graph = & create_graph_usemap01(); 79 | $Font = & $Graph->_font; 80 | $Plotarea = &create_plotarea_with_title01($Graph, $Font, $title); 81 | // Create the dataset 82 | $Dataset =& Image_Graph::factory('dataset'); 83 | // Create the 1st plot as 'bar' chart using the 1st dataset 84 | $Plot =& $Plotarea->addNew('bar', array(&$Dataset)); 85 | 86 | //$Plotarea->hideAxis('x'); 87 | $Plotarea->setAxisPadding(3); 88 | 89 | $Fill =& Image_Graph::factory('Image_Graph_Fill_Array'); 90 | $Fill->addColor('red', 'DROPS'); 91 | $Fill->addColor('darkred', 'EXCESS'); 92 | $Plot->setFillStyle($Fill); 93 | 94 | // set a line color 95 | $Plot->setLineColor('darkred'); 96 | //set a standard fill style 97 | //$Plot->setFillColor('red@0.9'); 98 | 99 | $AxisX =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_X); 100 | $AxisX->setFontAngle(70); 101 | # 102 | # Needs auto adjustment 103 | #$AxisX->setLabelOption('dateformat', 'Y-m-d (\HH)'); 104 | $AxisX->setLabelOption('dateformat', 'Y-m-d'); 105 | #$AxisX->setLabelInterval(200); 106 | 107 | # DATA addPoints 108 | # -------------- 109 | $max_y_value=5000; 110 | 111 | # query data 112 | if (is_numeric($bucketsz)) { 113 | $sec = $bucketsz; 114 | } else { 115 | $sec = 24 * 60 * 60; // One day 116 | } 117 | $data = probe_data_query_ts($probeid, $sec, $tstampF, $tstampT); 118 | //probe_data_show_table($data); 119 | //db_disconnect(); 120 | 121 | $urldata['bucketsz']=$bucketsz; 122 | $urldata['probeid'] =$probeid; 123 | 124 | if ($valid == true) { 125 | // Call data_addPoints() 126 | $records = data_addPoints01($Dataset, $data, $droptype, $max_y_value, $urldata); 127 | } else { 128 | $err = "Invalid input data cannot proceed"; 129 | trigger_error("$err", E_USER_ERROR); 130 | } 131 | 132 | $filename = generateFilename("probe_drops_bar01", $_REQUEST, 'png'); 133 | 134 | // Special output the Graph 135 | $output = $Graph->done( 136 | array( 137 | 'tohtml' => True, 138 | 'showtime' => True, 139 | 'border' => 0, 140 | 'filename' => $filename, 141 | 'filepath' => './graphs/', 142 | 'urlpath' => 'graphs/' 143 | ) 144 | ); 145 | 146 | if ($records > 0) { 147 | print $output; 148 | } else { 149 | echo "

Graph: No data available in choosen period

"; 150 | } 151 | ?> 152 | -------------------------------------------------------------------------------- /iptables-module/README: -------------------------------------------------------------------------------- 1 | 2 | = iptables kernel module = 3 | 4 | This xt_mpeg2ts iptables kernel module analyses the contents of MPEG2 5 | Transport Stream (TS) packets, and can detect TS/CC packet drops. 6 | (Previous name of kernel module were xt_mp2t) 7 | 8 | == Dependencies == 9 | 10 | Minimum iptables version >= 1.4.3, is required by iptables userspace 11 | part of the module. 12 | 13 | The iptables developer include files are also required (specifically 14 | xtables.h). 15 | 16 | No patching of the Linux kernel is required, but the kernel source is 17 | required. The module takes advantage of the kernel build/make system. 18 | 19 | Minimum kernel version required is 2.6.28 (due to some RCU changes). 20 | 21 | See: the README.compile file for further instructions. 22 | 23 | == Example: iptables rule == 24 | 25 | An iptables rules that matches all MPEG2 TS traffic. 26 | 27 | iptables -t mangle -I PREROUTING -p udp -m mpeg2ts 28 | 29 | Notice how its necessary to add '-p udp' as the match module only 30 | accepts UDP traffic. 31 | 32 | 33 | == Proc file containing stats == 34 | 35 | When creating a 'mpeg2ts' iptables rule, a corresponding proc file is 36 | created in /proc/net/xt_mpeg2ts/rule_NAME. 37 | 38 | * This file contains the stats. 39 | 40 | When creating the iptables rule, its possible to specify a '--name' 41 | for the proc file, which will replace the "NAME" (in rule_NAME). 42 | 43 | If no '--name' is specified then the rule "NAME" will be an 44 | increasing number, that corrosponds the number of "mpeg2ts" rules that 45 | has been created (since the kernel module "xt_mpeg2ts" were loaded). 46 | 47 | [FIXME: Needs fixing] 48 | (Currently) If the same name is specified more than once, then the 49 | kernel will give a WARNING, but the proc file will still be valid, 50 | and will simply "cover" the previous one. (Then this rule is deleted 51 | again, then the old proc file will again be accessable. But be warned 52 | don't delete them in the wrong direction, then "BUG: soft lockup" 53 | will occur!!!) 54 | 55 | 56 | WARNING: at fs/proc/generic.c:590 proc_register+0x161/0x1a4() 57 | proc_dir_entry 'xt_mpeg2ts/rule_TEST' already registered 58 | 59 | 60 | == Logging levels: msg_level == 61 | 62 | The xt_mpeg2ts kernel module has different levels of logging 63 | statements, which can be adjusted runtime for debugging 64 | purposes. The levels are based upon the kernels netdevice log 65 | infrastructure system, mostly used by Ethernet device drivers. 66 | 67 | The levels are adjusted via a bit-vector called "msg_level". 68 | 69 | The "msg_level" is runtime adjustable via: 70 | 71 | /sys/module/xt_mpeg2ts/parameters/msg_level 72 | 73 | And its possible to set it when loading the module: 74 | 75 | insmod xt_mpeg2ts msg_level=0x877 76 | 77 | The possible bit values can be see in kernel source version of: 78 | 79 | kernel-src/include/linux/netdevice.h 80 | 81 | 82 | === Syslogging drops === 83 | 84 | Enabling the RX_STATUS bit (0x0800) in msg_level, results in the 85 | module will printk when it detects CC-skips and 86 | discontinuity (where a discontinuity cover several CC skips). 87 | 88 | * CC-skips, is printed at KERN_INFO level. 89 | 90 | * Discontinuities, is printed at KERN_NOTICE level. 91 | 92 | Notice, that these syslog messages are rate limited (via 93 | net_ratelimit) and therefor CANNOT be trused to be used for 94 | parsing to obtain the drops. These messages are for debugging 95 | purposes to corrolate specific issues within streams where 96 | detailed level is required (e.g. per PID drop info). 97 | 98 | On the TODO list is a Netlink implementation, which will provide 99 | the required information stability and give highres 100 | timestamping. 101 | 102 | 103 | === Possible logging levels === 104 | 105 | From 'include/linux/netdevice.h' 106 | 107 | enum { 108 | NETIF_MSG_DRV = 0x0001, 109 | NETIF_MSG_PROBE = 0x0002, 110 | NETIF_MSG_LINK = 0x0004, 111 | NETIF_MSG_TIMER = 0x0008, 112 | NETIF_MSG_IFDOWN = 0x0010, 113 | NETIF_MSG_IFUP = 0x0020, 114 | NETIF_MSG_RX_ERR = 0x0040, 115 | NETIF_MSG_TX_ERR = 0x0080, 116 | NETIF_MSG_TX_QUEUED = 0x0100, 117 | NETIF_MSG_INTR = 0x0200, 118 | NETIF_MSG_TX_DONE = 0x0400, 119 | NETIF_MSG_RX_STATUS = 0x0800, 120 | NETIF_MSG_PKTDATA = 0x1000, 121 | NETIF_MSG_HW = 0x2000, 122 | NETIF_MSG_WOL = 0x4000, 123 | }; 124 | 125 | -------------------------------------------------------------------------------- /database/changes-between-versions.wiki: -------------------------------------------------------------------------------- 1 | 2 | = DB changes between version = 3 | 4 | Document the DB changes between versions 5 | 6 | == Future planned changes === 7 | 8 | The future plan is to, implement a TTL snmptrap event. 9 | 10 | Add a TTL event to table "event_type". 11 | 12 | INSERT INTO event_type (bitmask, label, description) VALUES 13 | ( 16, "ttl_change", "Indicate TTL changed"); 14 | 15 | 16 | == From: 0.9.0 to 0.9.1 === 17 | 18 | To apply changes described below run: 19 | 20 | cd database/ 21 | mysql -u root -p tvprobe < database-upgrade-0.9.0-to-0.9.1.sql 22 | 23 | Need to support packet and byte counters (named "packets" and 24 | "payload_bytes"). The column "packets" already exist, and the column 25 | "bytes" need to be renamed to "payload_bytes". 26 | 27 | ALTER TABLE log_event 28 | CHANGE `bytes` `payload_bytes` BIGINT(20) unsigned default '0'; 29 | 30 | Also store the delta for payload_bytes and packets. This can be 31 | easily used for calculation a number of things, e.g. bandwidth 32 | combined with delta time since last record (delta_poll is not usable 33 | for this purpose), if TS mapping 7 is used or not. 34 | 35 | ALTER TABLE log_event 36 | ADD `delta_payload_bytes` int(10) unsigned NOT NULL default '0', 37 | ADD `delta_packets` int(10) unsigned NOT NULL default '0'; 38 | 39 | To relate the delta counters, to something, we need to know when the 40 | last record were updated, so we can determine the seconds these delta 41 | counters account for. This way is possible to calculate bandwidth and 42 | packets per sec. 43 | 44 | Add a "last_update" timestamp and a "delta_update" time: 45 | 46 | ALTER TABLE log_event 47 | ADD last_update timestamp NOT NULL default '0000-00-00 00:00:00', 48 | ADD delta_update float unsigned default NULL; 49 | 50 | Change "delta_poll" to use type "float" instead of "int(11)" 51 | (eventhough the precision of "delta_poll" is not as important as 52 | "delta_update"). 53 | 54 | ALTER TABLE log_event 55 | MODIFY delta_poll float unsigned default NULL; 56 | 57 | === Event types === 58 | 59 | Introducing new event types in this version. 60 | 61 | * New event type(4): "no-signal" detection. 62 | * New event type(32): "transition". 63 | * New event type(64): "heartbeat". 64 | * New event type(128): "invalid". 65 | 66 | Old event types: 67 | 68 | * Event type(2): "drop". 69 | * Event type(1): "new_stream". 70 | 71 | Need a side table describing the event types, as there previously were 72 | only one event type (drops) (and implicit "new_stream" event). 73 | 74 | Event datatype, design discussion: We also need to consider that, 75 | potentially/theoretically, is possible for two events type to occur in 76 | the same poll cycle. Thus, either we need some kind of bit-vector so 77 | several events can be recorded together, or else the software needs to 78 | record two log_event records. Its possible to use the MySQL 79 | "SET-type", to create a SQL bit-vector. The problem with this 80 | approach is, that each time a new event type is created, the database 81 | needs to be altered. 82 | 83 | Future event types, could be related to burst, jitter, PCR-clock issues. 84 | 85 | Change the datatype of "event_type", to support a large bit-vector 86 | number. Choose to simply use an 32-bit INT type (instead of MySQLs BIT 87 | type, as its strange to use, and it does not convert well). To view 88 | the bits, MySQL select supports the "bin" cast 89 | (e.g. "select bin(event_type) from log_event;"). 90 | 91 | ALTER TABLE log_event 92 | MODIFY event_type INT(10) unsigned NOT NULL default '0'; 93 | 94 | Create side table: 95 | 96 | DROP TABLE IF EXISTS event_type; 97 | CREATE TABLE event_type ( 98 | bitmask int(10) unsigned NOT NULL default '0', 99 | label varchar(15) NOT NULL, 100 | description varchar(255), 101 | PRIMARY KEY (bitmask) 102 | ) ENGINE=InnoDB 103 | 104 | INSERT INTO event_type (bitmask, label, description) VALUES 105 | ( 1, "new_stream", "New stream detected"), 106 | ( 2, "drop" , "Drops detected, both skips and discon"), 107 | ( 4, "no_signal" , "Stream have stopped transmitting data"), 108 | ( 32, "transition", "The event_state changed since last poll"), 109 | ( 64, "heartbeat" , "Heartbeat event to monitor status"), 110 | (128, "invalid" , "Some invalid event situation arose"); 111 | 112 | === Extra change === 113 | 114 | As the database gets update anyhow, also complete the renaming from 115 | mp2t to mpeg2ts. 116 | 117 | ALTER TABLE daemon_session 118 | CHANGE `mp2t_created` `mpeg2ts_created` timestamp NOT NULL default '0000-00-00 00:00:00', 119 | CHANGE `mp2t_version` `mpeg2ts_version` varchar(50) default NULL; 120 | 121 | 122 | -------------------------------------------------------------------------------- /iptables-module/compat_xtables.h: -------------------------------------------------------------------------------- 1 | #ifndef _XTABLES_COMPAT_H 2 | #define _XTABLES_COMPAT_H 1 3 | 4 | #include 5 | #include 6 | #include "compat_skbuff.h" 7 | #include "compat_xtnu.h" 8 | 9 | #define DEBUGP Use__pr_debug__instead 10 | 11 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17) 12 | # warning Kernels below 2.6.17 not supported. 13 | #endif 14 | 15 | #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) 16 | # if !defined(CONFIG_NF_CONNTRACK_MARK) 17 | # warning You have CONFIG_NF_CONNTRACK enabled, but CONFIG_NF_CONNTRACK_MARK is not (please enable). 18 | # endif 19 | # include 20 | #elif defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) 21 | # if !defined(CONFIG_IP_NF_CONNTRACK_MARK) 22 | # warning You have CONFIG_IP_NF_CONNTRACK enabled, but CONFIG_IP_NF_CONNTRACK_MARK is not (please enable). 23 | # endif 24 | # include 25 | # define nf_conn ip_conntrack 26 | # define nf_ct_get ip_conntrack_get 27 | # define nf_conntrack_untracked ip_conntrack_untracked 28 | #else 29 | # warning You need either CONFIG_NF_CONNTRACK or CONFIG_IP_NF_CONNTRACK. 30 | #endif 31 | 32 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 17) 33 | # define skb_init_secmark(skb) 34 | # define skb_linearize xtnu_skb_linearize 35 | #endif 36 | 37 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19) 38 | # define neigh_hh_output xtnu_neigh_hh_output 39 | # define IPPROTO_UDPLITE 136 40 | # define CSUM_MANGLED_0 ((__force __sum16)0xffff) 41 | #endif 42 | 43 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) 44 | # define NF_INET_PRE_ROUTING NF_IP_PRE_ROUTING 45 | # define NF_INET_LOCAL_IN NF_IP_LOCAL_IN 46 | # define NF_INET_FORWARD NF_IP_FORWARD 47 | # define NF_INET_LOCAL_OUT NF_IP_LOCAL_OUT 48 | # define NF_INET_POST_ROUTING NF_IP_POST_ROUTING 49 | # define ip_local_out xtnu_ip_local_out 50 | # define ip_route_output_key xtnu_ip_route_output_key 51 | # include "compat_nfinetaddr.h" 52 | #endif 53 | 54 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23) 55 | # define init_net xtnu_ip_route_output_key /* yes */ 56 | # define init_net__loopback_dev (&loopback_dev) 57 | # define init_net__proc_net proc_net 58 | #else 59 | # define init_net__loopback_dev init_net.loopback_dev 60 | # define init_net__proc_net init_net.proc_net 61 | #endif 62 | 63 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 34) 64 | # define xt_match xtnu_match 65 | # define xt_register_match xtnu_register_match 66 | # define xt_unregister_match xtnu_unregister_match 67 | # define xt_register_matches xtnu_register_matches 68 | # define xt_unregister_matches xtnu_unregister_matches 69 | #endif 70 | 71 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19) 72 | # define csum_replace2 xtnu_csum_replace2 73 | # define csum_replace4 xtnu_csum_replace4 74 | # define inet_proto_csum_replace4 xtnu_proto_csum_replace4 75 | #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) 76 | # define csum_replace2 nf_csum_replace2 77 | # define csum_replace4 nf_csum_replace4 78 | # define inet_proto_csum_replace4 xtnu_proto_csum_replace4 79 | #endif 80 | 81 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) 82 | # define ipt_unregister_table(tbl) ipt_unregister_table(&init_net, (tbl)) 83 | # define ip6t_unregister_table(tbl) ip6t_unregister_table(&init_net, (tbl)) 84 | #else 85 | # define ipt_unregister_table(tbl) ipt_unregister_table(tbl) 86 | # define ip6t_unregister_table(tbl) ip6t_unregister_table(tbl) 87 | #endif 88 | 89 | 90 | #if !defined(NIP6) && !defined(NIP6_FMT) 91 | # define NIP6(addr) \ 92 | ntohs((addr).s6_addr16[0]), \ 93 | ntohs((addr).s6_addr16[1]), \ 94 | ntohs((addr).s6_addr16[2]), \ 95 | ntohs((addr).s6_addr16[3]), \ 96 | ntohs((addr).s6_addr16[4]), \ 97 | ntohs((addr).s6_addr16[5]), \ 98 | ntohs((addr).s6_addr16[6]), \ 99 | ntohs((addr).s6_addr16[7]) 100 | # define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" 101 | #endif 102 | #if !defined(NIPQUAD) && !defined(NIPQUAD_FMT) 103 | # define NIPQUAD(addr) \ 104 | ((const unsigned char *)&addr)[0], \ 105 | ((const unsigned char *)&addr)[1], \ 106 | ((const unsigned char *)&addr)[2], \ 107 | ((const unsigned char *)&addr)[3] 108 | # define NIPQUAD_FMT "%u.%u.%u.%u" 109 | #endif 110 | 111 | #define ip_route_me_harder xtnu_ip_route_me_harder 112 | #define skb_make_writable xtnu_skb_make_writable 113 | #define xt_target xtnu_target 114 | #define xt_register_target xtnu_register_target 115 | #define xt_unregister_target xtnu_unregister_target 116 | #define xt_register_targets xtnu_register_targets 117 | #define xt_unregister_targets xtnu_unregister_targets 118 | 119 | #define xt_request_find_match xtnu_request_find_match 120 | 121 | #endif /* _XTABLES_COMPAT_H */ 122 | -------------------------------------------------------------------------------- /iptables-module/Makefile.in: -------------------------------------------------------------------------------- 1 | # @configure_input@ 2 | # 3 | # Makefile for iptables MPEG2 TS kernel module 4 | # and iptables userspace shared library. 5 | # 6 | # Author: Jesper Dangaard Brouer 7 | # 8 | 9 | # Configure substitution variables 10 | # ================================ 11 | package = @PACKAGE_NAME@ 12 | version = @PACKAGE_VERSION@ 13 | # (Note the version comes from configure.ac AC_INIT) 14 | prefix = @prefix@ 15 | exec_prefix = @exec_prefix@ 16 | libexecdir = @libexecdir@ 17 | # 18 | # Detecting of xtlibdir has been moved to configure script 19 | xtlibdir = @xtlibdir@ 20 | # 21 | # Kernel builddir detected (via /lib/modules/$(shell uname -r)/build"]) 22 | # or set by configure [--with-kbuild=PATH] 23 | kbuilddir = @kbuilddir@ 24 | # 25 | # iptables variables detected by configure 26 | iptables_bin = @IPTABLES@ 27 | iptables_dir = @IPTABLES_DIR@ 28 | # (Include path to locate xtables.h) 29 | iptables_inc = @IPTABLES_INC@ 30 | 31 | 32 | # Targets 33 | # ------- 34 | LIB_TARGETS := libxt_mpeg2ts.so 35 | obj-m += xt_mpeg2ts.o 36 | #obj-m += seq_example.o 37 | 38 | # Jan Engelhardt's compat_xtables module stolen from xtables-addon project 39 | # git://xtables-addons.git.sf.net/gitroot/xtables-addons/xtables-addons/ 40 | obj-m += compat_xtables.o 41 | 42 | # Need a directory with the kernel source/makefiles 43 | # - Use kbuilddir if its defined, else detect it our self 44 | ifdef kbuilddir 45 | KERNEL_DIR ?= $(kbuilddir) 46 | else 47 | KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build 48 | endif 49 | 50 | # Environment variables with ?= can be overwritten/redefined 51 | # when calling make 52 | LIB_CFLAGS ?=-O2 -ggdb3 -Wall -fPIC 53 | 54 | # Trick I found in kernel/Documentation/kbuild/modules.txt: 55 | # 56 | # Use make variable KBUILD_EXTRA_SYMBOLS in the Makefile 57 | # If it is impractical to copy Module.symvers from another 58 | # module, you can assign a space separated list of files to 59 | # KBUILD_EXTRA_SYMBOLS in your Makfile. These files will be 60 | # loaded by modpost during the initialisation of its symbol 61 | # tables. 62 | # 63 | 64 | # Its possible to compile the module against a 64-bit kernel, 65 | # on a 32-bit system, by define ARCH=x86_64 66 | # 67 | # You can simply call: 68 | # make ARCH=x86_64 69 | # 70 | # ARCH ?= x86_64 71 | # export ARCH 72 | 73 | # Trick to modify kernels CFLAGS 74 | #KBUILD_CFLAGS+=-g -O0 75 | #KBUILD_CFLAGS+=-g -O3 76 | KBUILD_CFLAGS+=-g -O2 -fno-inline 77 | 78 | PWD = $(shell pwd) 79 | CP = cp -v 80 | 81 | # Detect iptables version 82 | iptables_version = $(shell ($(iptables_bin) -V | awk {'print $$2'} | cut -c 2-)) 83 | IPT_FLAGS = -DIPTABLES_VERSION=\"$(iptables_version)\" 84 | 85 | # TODO: What about the -DXTABLES_LIBDIR=\"$(xtlibdir)\" 86 | 87 | # Configure does a version check, that tell/warn people if the iptables 88 | # version is below 1.4.3 (as we depend >= 1.4.3) 89 | 90 | all: lib modules 91 | 92 | #testxtlibdir: 93 | # echo xtlibdir: $(xtlibdir) 94 | # echo $(PATH) 95 | 96 | modules: 97 | @echo "\n -=-=-=-=- Kernel modules -=-=-=-=-" 98 | $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules 99 | 100 | modules_install: modules 101 | @echo "\n -=-=-=-=- INSTALL Kernel modules -=-=-=-=-" 102 | $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install 103 | 104 | sparse: 105 | @echo "\n -=-=-=-=- SPARSE CHECK: Kernel modules -=-=-=-=-" 106 | $(MAKE) -C $(KERNEL_DIR) M=$(PWD) C=1 modules 107 | 108 | # Install target for userspace lib 109 | lib_install: lib 110 | @echo "\n -=-=-=-=- INSTALL Userspace lib -=-=-=-=-" 111 | @if test -d $(xtlibdir); then \ 112 | $(CP) $(LIB_TARGETS) $(xtlibdir); \ 113 | else \ 114 | echo "***ERROR*** - cannot find libexec (xtlibdir) install dir"; \ 115 | exit 2; \ 116 | fi 117 | 118 | install: lib_install modules_install 119 | 120 | 121 | clean: 122 | # $(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean 123 | rm -rf .tmp_versions 124 | rm -f *.o *~ *.so *.ko 125 | rm -f *.mod.c Module.symvers modules.order 126 | rm -f .*.cmd 127 | rm -f .xt_mpeg2ts.o.d 128 | 129 | 130 | lib: $(LIB_TARGETS) 131 | 132 | lib%.so: lib%.o 133 | gcc -shared -o $@ $^; 134 | 135 | lib%.o: lib%.c %.h 136 | @echo "\n -=-=-=-=- Userspace lib -=-=-=-=-" 137 | gcc $(IPT_FLAGS) ${LIB_CFLAGS} -I$(iptables_inc) -D_INIT=lib$*_init -c -o $@ $<; 138 | 139 | 140 | # Old approach 141 | lib_manual: libxt_mpeg2ts.c xt_mpeg2ts.h 142 | gcc $(IPT_FLAGS) libxt_mpeg2ts.c -D_INIT=libxt_mpeg2ts_init \ 143 | -fPIC -I$(iptables_inc) -c -o libxt_mpeg2ts.o 144 | gcc -shared -o libxt_mpeg2ts.so libxt_mpeg2ts.o 145 | 146 | 147 | # Configure/autotools trick to detect if Makefile.in is changed and 148 | # a rebuild in needed. 149 | # 150 | Makefile: Makefile.in ../config.status 151 | cd .. && ./config.status iptables-module/$@ 152 | 153 | ../config.status: ../configure 154 | cd .. && ./config.status --recheck 155 | -------------------------------------------------------------------------------- /iptables-module/compat_xtnu.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMPAT_XTNU_H 2 | #define _COMPAT_XTNU_H 1 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) 9 | typedef _Bool bool; 10 | enum { false = 0, true = 1, }; 11 | #endif 12 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19) 13 | typedef __u16 __bitwise __sum16; 14 | typedef __u32 __bitwise __wsum; 15 | #endif 16 | 17 | struct flowi; 18 | struct hh_cache; 19 | struct module; 20 | struct net_device; 21 | struct rtable; 22 | struct sk_buff; 23 | 24 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 27) 25 | enum { 26 | NFPROTO_UNSPEC = 0, 27 | NFPROTO_IPV4 = 2, 28 | NFPROTO_ARP = 3, 29 | NFPROTO_BRIDGE = 7, 30 | NFPROTO_IPV6 = 10, 31 | NFPROTO_DECNET = 12, 32 | NFPROTO_NUMPROTO, 33 | }; 34 | 35 | struct xt_mtchk_param { 36 | const char *table; 37 | const void *entryinfo; 38 | const struct xt_match *match; 39 | void *matchinfo; 40 | unsigned int hook_mask; 41 | u_int8_t family; 42 | }; 43 | 44 | struct xt_mtdtor_param { 45 | const struct xt_match *match; 46 | void *matchinfo; 47 | u_int8_t family; 48 | }; 49 | 50 | struct xt_target_param { 51 | const struct net_device *in, *out; 52 | unsigned int hooknum; 53 | const struct xt_target *target; 54 | const void *targinfo; 55 | u_int8_t family; 56 | }; 57 | 58 | struct xt_tgchk_param { 59 | const char *table; 60 | const void *entryinfo; 61 | const struct xt_target *target; 62 | void *targinfo; 63 | unsigned int hook_mask; 64 | u_int8_t family; 65 | }; 66 | 67 | struct xt_tgdtor_param { 68 | const struct xt_target *target; 69 | void *targinfo; 70 | u_int8_t family; 71 | }; 72 | #endif 73 | 74 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 34) 75 | struct xt_action_param { 76 | union { 77 | const struct xt_match *match; 78 | const struct xt_target *target; 79 | }; 80 | union { 81 | const void *matchinfo, *targinfo; 82 | }; 83 | const struct net_device *in, *out; 84 | int fragoff; 85 | unsigned int thoff, hooknum; 86 | u_int8_t family; 87 | bool hotdrop; 88 | }; 89 | #endif 90 | 91 | struct xtnu_match { 92 | /* 93 | * Making it smaller by sizeof(void *) on purpose to catch 94 | * lossy translation, if any. 95 | */ 96 | char name[sizeof(((struct xt_match *)NULL)->name) - 1 - sizeof(void *)]; 97 | uint8_t revision; 98 | bool (*match)(const struct sk_buff *, struct xt_action_param *); 99 | int (*checkentry)(const struct xt_mtchk_param *); 100 | void (*destroy)(const struct xt_mtdtor_param *); 101 | struct module *me; 102 | const char *table; 103 | unsigned int matchsize, hooks; 104 | unsigned short proto, family; 105 | 106 | void *__compat_match; 107 | }; 108 | 109 | struct xtnu_target { 110 | char name[sizeof(((struct xt_target *)NULL)->name) - 1 - sizeof(void *)]; 111 | uint8_t revision; 112 | unsigned int (*target)(struct sk_buff **, 113 | const struct xt_action_param *); 114 | int (*checkentry)(const struct xt_tgchk_param *); 115 | void (*destroy)(const struct xt_tgdtor_param *); 116 | struct module *me; 117 | const char *table; 118 | unsigned int targetsize, hooks; 119 | unsigned short proto, family; 120 | 121 | void *__compat_target; 122 | }; 123 | 124 | static inline struct xtnu_match *xtcompat_numatch(const struct xt_match *m) 125 | { 126 | void *q; 127 | memcpy(&q, m->name + sizeof(m->name) - sizeof(void *), sizeof(void *)); 128 | return q; 129 | } 130 | 131 | static inline struct xtnu_target *xtcompat_nutarget(const struct xt_target *t) 132 | { 133 | void *q; 134 | memcpy(&q, t->name + sizeof(t->name) - sizeof(void *), sizeof(void *)); 135 | return q; 136 | } 137 | 138 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19) 139 | static inline __wsum csum_unfold(__sum16 n) 140 | { 141 | return (__force __wsum)n; 142 | } 143 | #endif 144 | 145 | extern int xtnu_ip_local_out(struct sk_buff *); 146 | extern int xtnu_ip_route_me_harder(struct sk_buff **, unsigned int); 147 | extern int xtnu_skb_make_writable(struct sk_buff **, unsigned int); 148 | extern int xtnu_register_match(struct xtnu_match *); 149 | extern int xtnu_ip_route_output_key(void *, struct rtable **, struct flowi *); 150 | extern void xtnu_unregister_match(struct xtnu_match *); 151 | extern int xtnu_register_matches(struct xtnu_match *, unsigned int); 152 | extern void xtnu_unregister_matches(struct xtnu_match *, unsigned int); 153 | extern int xtnu_register_target(struct xtnu_target *); 154 | extern void xtnu_unregister_target(struct xtnu_target *); 155 | extern int xtnu_register_targets(struct xtnu_target *, unsigned int); 156 | extern void xtnu_unregister_targets(struct xtnu_target *, unsigned int); 157 | extern struct xt_match *xtnu_request_find_match(unsigned int, 158 | const char *, uint8_t); 159 | extern int xtnu_neigh_hh_output(struct hh_cache *, struct sk_buff *); 160 | extern void xtnu_csum_replace2(__u16 __bitwise *, __be16, __be16); 161 | extern void xtnu_csum_replace4(__u16 __bitwise *, __be32, __be32); 162 | extern void xtnu_proto_csum_replace4(__u16 __bitwise *, struct sk_buff *, 163 | __be32, __be32, bool); 164 | extern int xtnu_skb_linearize(struct sk_buff *); 165 | 166 | extern void *HX_memmem(const void *, size_t, const void *, size_t); 167 | 168 | #endif /* _COMPAT_XTNU_H */ 169 | -------------------------------------------------------------------------------- /iptables-module/README.compile: -------------------------------------------------------------------------------- 1 | 2 | = Howto Compile = 3 | 4 | This is a quick howto, on getting the iptables module compiled. 5 | Including howto install an newer/compatible iptables version. 6 | 7 | I'm using GNU tool 'stow' to handle my out-of-distro installed 8 | software. To follow these compile instructions, you should have stow 9 | installed on your machine. 10 | 11 | 12 | == Iptables dependency == 13 | 14 | This iptables module requires minimum iptables 1.4.3. 15 | 16 | 17 | == Developement tree == 18 | 19 | Get the development git development tree here: 20 | (FIXME: Move this to the new domain iptv-analyzer.org) 21 | 22 | git clone git://people.netfilter.org/hawk/iptv-analyzer.git 23 | 24 | 25 | === Compiling the userspace iptables module === 26 | 27 | The default make command tries to compile both the iptables userspace 28 | library and kernel module. If you only want to compile the iptables 29 | userspace library call: 30 | 31 | make lib 32 | 33 | Every iptables extension/module is based upon a dynamically loadable 34 | library file (suffix .so). These libraries/modules are installed in 35 | e.g. /usr/local/libexec/xtables/ and are loaded by iptables when 36 | needed. 37 | 38 | Thus, the makefile trick is "simply" to create an iptables dynamically 39 | loadable library "out-of-tree". 40 | 41 | All the details can be found in the excellent document titled: 42 | "Writing Netfilter modules" by Jan Engelhardt: 43 | 44 | http://jengelh.medozas.de/documents/Netfilter_Modules.pdf 45 | 46 | 47 | === Trick: Fake stow install === 48 | 49 | TODO: Currently the Makefile does not contain an install target for 50 | the userspace iptables library. Below is the manual procedure. 51 | 52 | Make a fake stow software package for the development version of the 53 | source. 54 | 55 | Create directory: 56 | 57 | mkdir /usr/local/stow/mpeg2ts_development_fake/ 58 | 59 | And create the directories libexec/xtables/ within. 60 | 61 | mkdir -p /usr/local/stow/mpeg2ts_development_fake/libexec/xtables/ 62 | cd /usr/local/stow/mpeg2ts_development_fake/libexec/xtables/ 63 | 64 | Create a symlink from this directory to your development edition of 65 | the file 'libxt_mpeg2ts.so' 66 | 67 | ln -s ~/git/iptv-analyzer/iptables-module/libxt_mpeg2ts.so . 68 | 69 | Activate the stow software package 'mpeg2ts_development_fake'. 70 | 71 | cd /usr/local/stow 72 | stow mpeg2ts_development_fake 73 | 74 | 75 | == Compiling the kernel module == 76 | 77 | No patching of the Linux kernel is required, but the kernel source is 78 | required. The module takes advantage of the kernel build/make system. 79 | 80 | Minimum kernel version required is 2.6.28 (due to some RCU changes). 81 | 82 | If you only want to compile the kernel module call: 83 | 84 | make modules 85 | 86 | The Makefile tries to detect the kernel source via the symlink in: 87 | 88 | /lib/modules/2.6.xx/build 89 | 90 | The kernel version is extracted by calling 'uname -r' in the makefile. 91 | 92 | But its also possibly to compile against another kernel than the 93 | current running version, by calling: 94 | 95 | make KERNEL_DIR=~/git/kernel/davem/net-next-2.6/ 96 | 97 | If you have a fresh kernel source you might need to call: 98 | 99 | make oldconfig 100 | make prepare 101 | make modules_prepare 102 | 103 | TODO/Issue?: Is it possible to link against a fresh source? As the 104 | module needs to know the C "extern" adresses. 105 | 106 | === Loading the kernel module manually === 107 | 108 | Its possibly to load the kernel module via insmod, without installing 109 | the module into /lib/modules/2.6.xx/ (see below howto perform the 110 | install). 111 | 112 | * But be warned that the systems does not automatically load 113 | dependencies. 114 | 115 | First of all you need to make sure that the iptables module have been 116 | loaded (e.g x_tables, ip_tables, iptable_mangle, iptable_filter etc.). 117 | The easiest way to get these modules loaded is simple to invoke the 118 | 'iptables -L' command line tool. 119 | 120 | The kernel module compile, actually produced two kernel modules: 121 | 122 | * xt_mpeg2ts.ko 123 | * compat_xtables.ko 124 | 125 | If you kernel version <= 2.6.34, then you also need to load the kernel 126 | module "compat_xtables.ko" 127 | 128 | insmod ./compat_xtables.ko 129 | 130 | Next, you need to load the real kernel module: 131 | 132 | insmod ./xt_mpeg2ts.ko 133 | 134 | If the loading failed with (e.g. "Unknown symbol in module") use 135 | 'dmesg' to see the real error message. 136 | 137 | 138 | === Reloading kernel module === 139 | 140 | When making modifications to the kernel module remember to manually 141 | rmmod and insmod the kernel module again. 142 | 143 | rmmod xt_mpeg2ts 144 | insmod ./xt_mpeg2ts.ko 145 | 146 | 147 | === Installing the kernel module === 148 | 149 | When the kernel module has been compiled successfully, its possibly to 150 | install the kernel module, as root, via: 151 | 152 | make modules_install 153 | 154 | This will install the kernel module into /lib/modules/2.6.xx/extra/ 155 | (via a call to the kernel build system, this might call depmod for 156 | you). 157 | 158 | After the install you should call (as root): 159 | 160 | depmod -a 161 | 162 | If its another kernel than the running kernel you need to add the 163 | kernel-version as an argument to depmod 164 | 165 | A small test to see if the kernel module got installed and depmod 166 | correctly call: 167 | 168 | modinfo -k $KERNEL_VER xt_mpeg2ts 169 | 170 | 171 | -------------------------------------------------------------------------------- /webfrontend/www/include/graph_channel_drops_bar01.php: -------------------------------------------------------------------------------- 1 | $err1\n"; 26 | echo "

$err2

\n"; 27 | #exit; 28 | db_connect(); 29 | $localdb = TRUE; 30 | } 31 | 32 | if (is_array($probesinfo)) { 33 | if ($DEBUG == TRUE) 34 | echo "Probes info already available
\n"; 35 | } else { 36 | // This file is intended to be include, thus this query might 37 | // already have been performed by the including script 38 | $probesinfo = probes_info_query(); 39 | } 40 | 41 | if ($localdb == TRUE) { 42 | echo "\n

Please select a channel:

\n"; 43 | $channels =& multicast_list_query(); 44 | form_channel_selection($channels, $probesinfo); 45 | } 46 | 47 | 48 | $_REQUEST = cleanup_input_request(); 49 | $probeid = $_REQUEST['probeid']; 50 | $maxy = $_REQUEST['maxy']; 51 | $fromT = $_REQUEST['fromT']; 52 | $toT = $_REQUEST['toT']; 53 | $bucketsz = $_REQUEST['bucketsz']; 54 | $tstampF = $_REQUEST['tstampF']; 55 | $tstampT = $_REQUEST['tstampT']; 56 | 57 | $channel = $_REQUEST['channel']; 58 | 59 | 60 | # INPUT parsing 61 | # ------------- 62 | $valid = true; 63 | $probename = get_probename($probeid, $probesinfo); 64 | 65 | if (!$channel) { 66 | echo "

ERROR: cannot generate a channel graph without a channel

\n"; 67 | die(); 68 | } 69 | 70 | $droptype = $_REQUEST['droptype']; 71 | if (!isset($droptype)) 72 | $droptype = "drops"; 73 | 74 | $title = "$droptype on $channel (on $probename)"; 75 | 76 | 77 | # GRAPH creation 78 | # -------------- 79 | $Graph = & create_graph_usemap01(); 80 | $Font = & $Graph->_font; 81 | $Plotarea = &create_plotarea_with_title02($Graph, $Font, $title); 82 | // Create the dataset 83 | $Dataset =& Image_Graph::factory('dataset'); 84 | // Create the 1st plot as 'bar' chart using the 1st dataset 85 | $Plot =& $Plotarea->addNew('bar', array(&$Dataset)); 86 | 87 | //$Plotarea->hideAxis('x'); 88 | $Plotarea->setAxisPadding(3); 89 | 90 | $Fill =& Image_Graph::factory('Image_Graph_Fill_Array'); 91 | $Fill->addColor('red@0.2', 'DROPS'); 92 | $Fill->addColor('darkred', 'EXCESS'); 93 | $Plot->setFillStyle($Fill); 94 | 95 | // set a line color 96 | //$Plot->setLineColor('blue@0.7'); 97 | //$Plot->setLineColor('black'); 98 | $Plot->setLineColor('darkred'); 99 | //set a standard fill style 100 | //$Plot->setFillColor('red@0.9'); 101 | 102 | #$percent = calcBarWidthPercentage($bucketsz, $tstampF, $tstampT, $records); 103 | #$Plot->setBarWidth($percent,"%"); 104 | setBarWidth($Plot, $bucketsz, $tstampF, $tstampT, $records); 105 | 106 | # DATA addPoints 107 | # -------------- 108 | $max_y_value=5000; 109 | 110 | # query data 111 | if (is_numeric($bucketsz)) { 112 | $sec = $bucketsz; 113 | } else { 114 | $sec = 60 * 60; // One hour 115 | } 116 | if ($DEBUG == TRUE) { 117 | echo "bucketsz:$bucketsz
\n"; 118 | echo "tstampF:$tstampF ". date('Y-m-d H:i:s', $tstampF) ."
\n"; 119 | echo "tstampT:$tstampT ". date('Y-m-d H:i:s', $tstampT) ."
\n"; 120 | echo "probeid:$probeid
\n"; 121 | } 122 | 123 | $startqd=getMicroTime(); 124 | $data = one_channel_data_query_ts($channel, $probeid, $sec, $tstampF, $tstampT); 125 | displayTimingInfo($startqd, getMicroTime(), $displayTiming, "query_data"); 126 | 127 | //probe_data_show_table($data); 128 | #print_r($data); 129 | if ($localdb == TRUE) db_disconnect(); 130 | 131 | $urldata['bucketsz']=$bucketsz; 132 | $urldata['probeid'] =$probeid; 133 | $urldata['channel'] =$channel; 134 | 135 | /*** Data Points ***/ 136 | // Trick we add two datapoints, Start and End of the period we are plotting. 137 | $Dataset->addPoint($tstampF, 0); 138 | 139 | // Call data_addPoints() 140 | $records = data_addPoints01($Dataset, $data, $droptype, 141 | $maxy, $urldata); 142 | // End datapoint 143 | $Dataset->addPoint($tstampT, 0); 144 | 145 | // Fix the Y-axis max 146 | $AxisY =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_Y); 147 | if ($_REQUEST['maxy_fixed'] == "fixed") { 148 | if (is_numeric($maxy)) { 149 | $AxisY->forceMaximum($maxy); 150 | } 151 | } 152 | 153 | 154 | $AxisX =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_X); 155 | #$AxisX->setFontAngle(70); 156 | # 157 | # Needs auto adjustment 158 | $AxisX->setLabelOption('dateformat', "Y-m-d\nH:i:s"); 159 | #$AxisX->setLabelOption('dateformat', 'Y-m-d'); 160 | #$AxisX->setLabelInterval("auto"); 161 | #$AxisX->setLabelInterval(200); 162 | 163 | $filename = generateFilename("channel_drops_bar01", $_REQUEST, 'png'); 164 | 165 | // Special output the Graph 166 | $output = $Graph->done( 167 | array( 168 | 'tohtml' => True, 169 | 'showtime' => $displayTiming, 170 | 'border' => 0, 171 | 'filename' => $filename, 172 | 'filepath' => './graphs/', 173 | 'urlpath' => 'graphs/' 174 | ) 175 | ); 176 | 177 | if ($records > 0) { 178 | print $output; 179 | } else { 180 | echo "

Graph: No data available"; 181 | echo " in choosen period on probe: $probename

"; 182 | echo "

Have you choosen a probe in the table?

"; 183 | } 184 | ?> 185 | -------------------------------------------------------------------------------- /database/database-schema-0.9.0.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.11 2 | -- 3 | -- Host: localhost Database: tvprobe 4 | -- ------------------------------------------------------ 5 | -- Server version 5.0.51a-24+lenny4-log 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `daemon_session` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `daemon_session`; 23 | SET @saved_cs_client = @@character_set_client; 24 | SET character_set_client = utf8; 25 | CREATE TABLE `daemon_session` ( 26 | `id` int(10) unsigned NOT NULL auto_increment, 27 | `start_time` timestamp NOT NULL default CURRENT_TIMESTAMP, 28 | `stop_time` timestamp NOT NULL default '0000-00-00 00:00:00', 29 | `heartbeat` timestamp NOT NULL default '0000-00-00 00:00:00', 30 | `probe_id` int(11) default NULL, 31 | `daemon_pid` smallint(5) unsigned default NULL, 32 | `mp2t_created` timestamp NOT NULL default '0000-00-00 00:00:00', 33 | `mp2t_version` varchar(50) default NULL, 34 | PRIMARY KEY (`id`) 35 | ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=latin1; 36 | SET character_set_client = @saved_cs_client; 37 | 38 | -- 39 | -- Table structure for table `log_event` 40 | -- 41 | 42 | DROP TABLE IF EXISTS `log_event`; 43 | SET @saved_cs_client = @@character_set_client; 44 | SET character_set_client = utf8; 45 | CREATE TABLE `log_event` ( 46 | `id` bigint(20) unsigned NOT NULL auto_increment, 47 | `stream_session_id` bigint(20) unsigned NOT NULL default '0', 48 | `record_time` timestamp NOT NULL default CURRENT_TIMESTAMP, 49 | `probe_id` int(11) NOT NULL default '0', 50 | `daemon_session_id` int(10) unsigned NOT NULL default '0', 51 | `skips` bigint(20) unsigned NOT NULL default '0', 52 | `discontinuity` bigint(20) unsigned NOT NULL default '0', 53 | `errsec` int(10) unsigned NOT NULL default '0', 54 | `delta_skips` int(10) unsigned NOT NULL default '0', 55 | `delta_discon` int(10) unsigned NOT NULL default '0', 56 | `delta_errsec` int(10) unsigned NOT NULL default '0', 57 | `bytes` bigint(20) unsigned default '0', 58 | `packets` bigint(20) unsigned default '0', 59 | `event_type` smallint(5) unsigned NOT NULL default '1', 60 | `pids` smallint(5) unsigned NOT NULL default '0', 61 | `delta_poll` int(10) unsigned default NULL, 62 | `last_poll` timestamp NOT NULL default '0000-00-00 00:00:00', 63 | `probe_time` timestamp NOT NULL default '0000-00-00 00:00:00', 64 | `multicast_dst` char(15) NOT NULL, 65 | `ip_src` char(15) default NULL, 66 | `ttl` smallint(5) unsigned default '0', 67 | PRIMARY KEY (`id`) 68 | ) ENGINE=InnoDB AUTO_INCREMENT=1985836 DEFAULT CHARSET=latin1; 69 | SET character_set_client = @saved_cs_client; 70 | 71 | -- 72 | -- Table structure for table `probes` 73 | -- 74 | 75 | DROP TABLE IF EXISTS `probes`; 76 | SET @saved_cs_client = @@character_set_client; 77 | SET character_set_client = utf8; 78 | CREATE TABLE `probes` ( 79 | `id` int(11) NOT NULL auto_increment, 80 | `ip` varchar(15) NOT NULL, 81 | `input` varchar(100) NOT NULL, 82 | `shortloc` varchar(50) default 'unknown', 83 | `switch` varchar(50) default 'unknown', 84 | `name` varchar(100) default 'unknown', 85 | `description` varchar(100) default 'unknown', 86 | `location` varchar(100) default 'unknown', 87 | `address` varchar(100) default 'unknown', 88 | `distance` int(11) default '0', 89 | `input_ip` varchar(15) default '', 90 | `input_dev` varchar(16) default '', 91 | `procfile` varchar(100) default NULL, 92 | `switchport` varchar(50) default '', 93 | `switchtype` varchar(50) default '', 94 | `hidden` enum('no','yes') NOT NULL default 'no', 95 | PRIMARY KEY (`id`), 96 | KEY `idx_identify` (`ip`,`input`,`shortloc`,`switch`) 97 | ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1; 98 | SET character_set_client = @saved_cs_client; 99 | 100 | -- 101 | -- Table structure for table `stream_session` 102 | -- 103 | 104 | DROP TABLE IF EXISTS `stream_session`; 105 | SET @saved_cs_client = @@character_set_client; 106 | SET character_set_client = utf8; 107 | CREATE TABLE `stream_session` ( 108 | `id` bigint(20) unsigned NOT NULL auto_increment, 109 | `probe_id` int(11) NOT NULL default '0', 110 | `daemon_session_id` int(10) unsigned NOT NULL default '0', 111 | `start_time` timestamp NOT NULL default CURRENT_TIMESTAMP, 112 | `stop_time` timestamp NOT NULL default '0000-00-00 00:00:00', 113 | `multicast_dst` char(15) NOT NULL, 114 | `ip_src` char(15) default NULL, 115 | `port_dst` smallint(5) unsigned default NULL, 116 | `port_src` smallint(5) unsigned default NULL, 117 | `logid_begin` bigint(20) unsigned NOT NULL default '0', 118 | `logid_end` bigint(20) unsigned NOT NULL default '0', 119 | PRIMARY KEY (`id`) 120 | ) ENGINE=InnoDB AUTO_INCREMENT=4719 DEFAULT CHARSET=latin1; 121 | SET character_set_client = @saved_cs_client; 122 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 123 | 124 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 125 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 126 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 127 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 128 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 129 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 130 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 131 | 132 | -- Dump completed on 2010-11-01 14:26:23 133 | -------------------------------------------------------------------------------- /webfrontend/www/include/graph_probe_drops_bar02.php: -------------------------------------------------------------------------------- 1 | $err1\n"; 36 | echo "

$err2

\n"; 37 | #exit; 38 | db_connect(); 39 | $localdb = TRUE; 40 | $probes = probes_info_query(); 41 | echo "\n

Please select a probe:

\n"; 42 | probes_info_form_tabel($probes, $probeid); 43 | 44 | } 45 | $tstampF = $_REQUEST['tstampF']; 46 | $tstampT = $_REQUEST['tstampT']; 47 | 48 | # INPUT parsing 49 | # ------------- 50 | $valid = true; 51 | if (!$probeid) { 52 | $valid = false; 53 | $err = "

Please choose a valid probe

"; 54 | die("$err"); 55 | } else { 56 | $probe = $probes[$probeid]; 57 | if (!is_array($probe)) { 58 | $valid = false; 59 | } else { 60 | // extract probename, switch and shortloc 61 | $probename = $probe['name']; 62 | $switch = $probe['switch']; 63 | $shortloc = $probe['shortloc']; 64 | } 65 | } 66 | if ($probename == "") { 67 | $probename = "probeID:$probeid"; 68 | } 69 | 70 | $droptype = $_REQUEST['droptype']; 71 | if ($droptype == "") { 72 | // Default drop type 73 | $droptype = 'drops'; 74 | } elseif (($droptype != 'skips') && ($droptype != 'drops')) { 75 | $droptype = "INVALID-DROPTYPE"; 76 | $valid = false; 77 | } 78 | $title = "$droptype on $probename/$switch/$shortloc"; 79 | 80 | 81 | # GRAPH creation 82 | # -------------- 83 | $Graph = & create_graph_usemap01(); 84 | $Font = & $Graph->_font; 85 | 86 | $Plotarea = &create_plotarea_with_title02($Graph, $Font, $title); 87 | #$Title = $Graph->addNew('title', array("$title", 10)); 88 | #$Plotarea = $Title->addNew('plotarea', array('axis', 'axis')); 89 | #$Plotarea->setFont($Font); 90 | 91 | // Create the dataset 92 | $Dataset =& Image_Graph::factory('dataset'); 93 | #$Dataset_fake =& Image_Graph::factory('dataset'); 94 | 95 | 96 | # DATA addPoints 97 | # -------------- 98 | $max_y_value = $_REQUEST['maxy']; 99 | 100 | # query data 101 | if (is_numeric($bucketsz)) { 102 | $sec = $bucketsz; 103 | } else { 104 | $sec = 24 * 60 * 60; // One day 105 | } 106 | $startq=getMicroTime(); 107 | $data = probe_data_query_ts($probeid, $sec, $tstampF, $tstampT); 108 | displayTimingInfo($startq, getMicroTime(), $displayTiming, "probe_data_query"); 109 | //probe_data_show_table($data); 110 | if ($localdb == TRUE) 111 | db_disconnect(); 112 | 113 | $urldata['bucketsz']=$bucketsz; 114 | $urldata['probeid'] =$probeid; 115 | 116 | if ($valid == true) { 117 | 118 | // Trick we add two datapoints, Start and End of the period we are plotting. 119 | $Dataset->addPoint($tstampF, 0); 120 | 121 | // Call data_addPoints() 122 | $records = data_addPoints01($Dataset, $data, $droptype, 123 | $max_y_value, $urldata); 124 | 125 | // End datapoint 126 | $Dataset->addPoint($tstampT, 0); 127 | 128 | # $fakerec = data_addPoints_fake($Dataset_fake, $bucketsz, 129 | # $tstampF, $tstampT, $urldata); 130 | 131 | } else { 132 | $err = "Invalid input data cannot proceed"; 133 | trigger_error("$err", E_USER_ERROR); 134 | } 135 | 136 | 137 | 138 | 139 | 140 | // Create the 1st plot as 'bar' chart using the 1st dataset 141 | #$Plot =& $Plotarea->addNew('line', array(&$Dataset)); 142 | #$Plot =& $Plotarea->addNew('area', array(&$Dataset)); 143 | #$Plot =& $Plotarea->addNew('step', array(&$Dataset)); 144 | #$Plot =& $Plotarea->addNew('Image_Graph_Plot_Band', array(&$Dataset)); 145 | #$Plot =& $Plotarea->addNew('impulse', array(&$Dataset)); 146 | $Plot =& $Plotarea->addNew('bar', array(&$Dataset)); 147 | #$Plot2 =& $Plotarea->addNew('bar', array(&$Dataset_fake)); 148 | 149 | #$percent = calcBarWidthPercentage($bucketsz, $tstampF, $tstampT, $records); 150 | #$Plot->setBarWidth($percent,"%"); 151 | setBarWidth($Plot, $bucketsz, $tstampF, $tstampT, $records); 152 | 153 | #echo "procent: $procent (rec: $records)
"; 154 | 155 | #$AllDatasets[0] =& $Dataset; 156 | #$AllDatasets[1] =& $Dataset_fake; 157 | #$Plot =& $Plotarea->addNew('Image_Graph_Plot_Bar', array($AllDatasets, 'stacked')); 158 | 159 | //$Plotarea->hideAxis('x'); 160 | $Plotarea->setAxisPadding(3); 161 | 162 | $Fill =& Image_Graph::factory('Image_Graph_Fill_Array'); 163 | $Fill->addColor('red', 'DROPS'); 164 | $Fill->addColor('darkred', 'EXCESS'); 165 | $Fill->addColor('yellow', 'NODROPS'); 166 | $Plot->setFillStyle($Fill); 167 | 168 | // set a line color 169 | $Plot->setLineColor('darkred'); 170 | //set a standard fill style 171 | //$Plot->setFillColor('red@0.9'); 172 | 173 | $AxisX =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_X); 174 | #$AxisX->setFontAngle(70); 175 | # 176 | # Needs auto adjustment 177 | $AxisX->setLabelOption('dateformat', "Y-m-d\nH:i:s"); 178 | #$AxisX->setLabelOption('dateformat', 'Y-m-d (\HH)'); 179 | #$AxisX->setLabelOption('dateformat', 'Y-m-d'); 180 | #$AxisX->setLabelInterval(200); 181 | 182 | // Fix the Y-axis max 183 | $AxisY =& $Plotarea->getAxis(IMAGE_GRAPH_AXIS_Y); 184 | if ($_REQUEST['maxy_fixed'] == "fixed") { 185 | if (is_numeric($max_y_value)) { 186 | $AxisY->forceMaximum($max_y_value); 187 | } 188 | } 189 | 190 | $filename = generateFilename("probe_drops_bar02", $_REQUEST, 'png'); 191 | 192 | // Special output the Graph 193 | $output = $Graph->done( 194 | array( 195 | 'tohtml' => True, 196 | 'showtime' => $displayTiming, 197 | 'border' => 0, 198 | 'filename' => $filename, 199 | 'filepath' => './graphs/', 200 | 'urlpath' => 'graphs/' 201 | ) 202 | ); 203 | 204 | if ($records > 0) { 205 | print $output; 206 | } else { 207 | echo "

Graph: No data available in choosen period

"; 208 | } 209 | displayTimingInfo($startg, getMicroTime(), $displayTiming, "graph_bar02 done"); 210 | ?> 211 | -------------------------------------------------------------------------------- /database/database-schema-0.9.1.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.11 2 | -- 3 | -- Host: localhost Database: tvprobe 4 | -- ------------------------------------------------------ 5 | -- Server version 5.0.51a-24+lenny4-log 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `daemon_session` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `daemon_session`; 23 | SET @saved_cs_client = @@character_set_client; 24 | SET character_set_client = utf8; 25 | CREATE TABLE `daemon_session` ( 26 | `id` int(10) unsigned NOT NULL auto_increment, 27 | `start_time` timestamp NOT NULL default CURRENT_TIMESTAMP, 28 | `stop_time` timestamp NOT NULL default '0000-00-00 00:00:00', 29 | `heartbeat` timestamp NOT NULL default '0000-00-00 00:00:00', 30 | `probe_id` int(11) default NULL, 31 | `daemon_pid` smallint(5) unsigned default NULL, 32 | `mpeg2ts_created` timestamp NOT NULL default '0000-00-00 00:00:00', 33 | `mpeg2ts_version` varchar(50) default NULL, 34 | PRIMARY KEY (`id`) 35 | ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=latin1; 36 | SET character_set_client = @saved_cs_client; 37 | 38 | -- 39 | -- Table structure for table `log_event` 40 | -- 41 | 42 | DROP TABLE IF EXISTS `log_event`; 43 | SET @saved_cs_client = @@character_set_client; 44 | SET character_set_client = utf8; 45 | CREATE TABLE `log_event` ( 46 | `id` bigint(20) unsigned NOT NULL auto_increment, 47 | `stream_session_id` bigint(20) unsigned NOT NULL default '0', 48 | `record_time` timestamp NOT NULL default CURRENT_TIMESTAMP, 49 | `probe_id` int(11) NOT NULL default '0', 50 | `daemon_session_id` int(10) unsigned NOT NULL default '0', 51 | `skips` bigint(20) unsigned NOT NULL default '0', 52 | `discontinuity` bigint(20) unsigned NOT NULL default '0', 53 | `errsec` int(10) unsigned NOT NULL default '0', 54 | `delta_skips` int(10) unsigned NOT NULL default '0', 55 | `delta_discon` int(10) unsigned NOT NULL default '0', 56 | `delta_errsec` int(10) unsigned NOT NULL default '0', 57 | `payload_bytes` bigint(20) unsigned default '0', 58 | `delta_payload_bytes` int(10) unsigned NOT NULL default '0', 59 | `packets` bigint(20) unsigned default '0', 60 | `delta_packets` int(10) unsigned NOT NULL default '0', 61 | `event_type` int(10) unsigned NOT NULL default '0', 62 | `pids` smallint(5) unsigned NOT NULL default '0', 63 | `delta_poll` int(10) unsigned default NULL, 64 | `last_poll` timestamp NOT NULL default '0000-00-00 00:00:00', 65 | `probe_time` timestamp NOT NULL default '0000-00-00 00:00:00', 66 | last_update timestamp NOT NULL default '0000-00-00 00:00:00', 67 | delta_update float unsigned default NULL, 68 | `multicast_dst` char(15) NOT NULL, 69 | `ip_src` char(15) default NULL, 70 | `ttl` smallint(5) unsigned default '0', 71 | PRIMARY KEY (`id`) 72 | ) ENGINE=InnoDB AUTO_INCREMENT=65536 DEFAULT CHARSET=latin1; 73 | SET character_set_client = @saved_cs_client; 74 | 75 | DROP TABLE IF EXISTS event_type; 76 | CREATE TABLE event_type ( 77 | bitmask int(10) unsigned NOT NULL default '0', 78 | label varchar(15) NOT NULL, 79 | description varchar(255), 80 | PRIMARY KEY (bitmask) 81 | ) ENGINE=InnoDB; 82 | 83 | LOCK TABLES event_type WRITE; 84 | INSERT INTO event_type (bitmask, label, description) VALUES 85 | ( 1, "new_stream", "New stream detected"), 86 | ( 2, "drop" , "Drops detected, both skips and discon"), 87 | ( 4, "no_signal" , "Stream have stopped transmitting data"), 88 | ( 32, "transition", "The event_state changed since last poll"), 89 | ( 64, "heartbeat" , "Heartbeat event to monitor status"), 90 | (128, "invalid" , "Some invalid event situation arose"); 91 | UNLOCK TABLES; 92 | 93 | -- 94 | -- Table structure for table `probes` 95 | -- 96 | 97 | DROP TABLE IF EXISTS `probes`; 98 | SET @saved_cs_client = @@character_set_client; 99 | SET character_set_client = utf8; 100 | CREATE TABLE `probes` ( 101 | `id` int(11) NOT NULL auto_increment, 102 | `ip` varchar(15) NOT NULL, 103 | `input` varchar(100) NOT NULL, 104 | `shortloc` varchar(50) default 'unknown', 105 | `switch` varchar(50) default 'unknown', 106 | `name` varchar(100) default 'unknown', 107 | `description` varchar(100) default 'unknown', 108 | `location` varchar(100) default 'unknown', 109 | `address` varchar(100) default 'unknown', 110 | `distance` int(11) default '0', 111 | `input_ip` varchar(15) default '', 112 | `input_dev` varchar(16) default '', 113 | `procfile` varchar(100) default NULL, 114 | `switchport` varchar(50) default '', 115 | `switchtype` varchar(50) default '', 116 | `hidden` enum('no','yes') NOT NULL default 'no', 117 | PRIMARY KEY (`id`), 118 | KEY `idx_identify` (`ip`,`input`,`shortloc`,`switch`) 119 | ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1; 120 | SET character_set_client = @saved_cs_client; 121 | 122 | -- 123 | -- Table structure for table `stream_session` 124 | -- 125 | 126 | DROP TABLE IF EXISTS `stream_session`; 127 | SET @saved_cs_client = @@character_set_client; 128 | SET character_set_client = utf8; 129 | CREATE TABLE `stream_session` ( 130 | `id` bigint(20) unsigned NOT NULL auto_increment, 131 | `probe_id` int(11) NOT NULL default '0', 132 | `daemon_session_id` int(10) unsigned NOT NULL default '0', 133 | `start_time` timestamp NOT NULL default CURRENT_TIMESTAMP, 134 | `stop_time` timestamp NOT NULL default '0000-00-00 00:00:00', 135 | `multicast_dst` char(15) NOT NULL, 136 | `ip_src` char(15) default NULL, 137 | `port_dst` smallint(5) unsigned default NULL, 138 | `port_src` smallint(5) unsigned default NULL, 139 | `logid_begin` bigint(20) unsigned NOT NULL default '0', 140 | `logid_end` bigint(20) unsigned NOT NULL default '0', 141 | PRIMARY KEY (`id`) 142 | ) ENGINE=InnoDB AUTO_INCREMENT=4719 DEFAULT CHARSET=latin1; 143 | SET character_set_client = @saved_cs_client; 144 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 145 | 146 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 147 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 148 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 149 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 150 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 151 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 152 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 153 | 154 | -- Dump completed on 2010-11-01 14:26:23 155 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2 | recent changes 3 | 4 | 2015-03-04 Jesper Dangaard Brouer 5 | 6 | * Version 0.9.4. 7 | 8 | It has been quite some time since the last release. 9 | 10 | This release primarily contains adjustments to adapt for changing 11 | kernel APIs over time. Did a small labtest of kernel 3.18.6. 12 | 13 | 2011-12-02 Jesper Dangaard Brouer 14 | 15 | * Version 0.9.3. 16 | 17 | This release is a minor update, with minor fixes. 18 | 19 | Some versions of Perl, gave a false positive warning: 20 | "Useless use of private variable in void context" 21 | Which is now fixed. 22 | 23 | Fix typo in database/database-schema-latest.sql. 24 | 25 | Give fatal warning, if config file is missing. 26 | 27 | 2011-06-01 Jesper Dangaard Brouer 28 | 29 | * Version 0.9.2. 30 | 31 | SNMP trap feature implemented. 32 | 33 | The collector sends SNMP "no_signal" traps, when the state 34 | transition from, signal to no-signal, and back from no-signal to 35 | signal. 36 | 37 | There is a SNMP MIB, describing the SNMP traps, located in 38 | "snmp/mibs/" named "IPTV-ANALYZER-MIB.txt". The traps are placed 39 | under Enterprise OID 26124, which belongs to ComX Networks A/S. 40 | The CXNET-CORE-MIB.txt file describe this Enterprise OID. 41 | 42 | The iptv-collector now depends on the CPAN module NET::SNMP. 43 | 44 | 2011-05-24 Jesper Dangaard Brouer 45 | 46 | * Version 0.9.1. 47 | 48 | New features include bytes and packet counters, no-signal detection, 49 | heartbeat system per stream, and new event types. 50 | 51 | Implemented packet and byte counters in the xt_mpeg2ts kernel 52 | module. The iptv-collector, collects these counters and record 53 | delta values and timestamps to the database. 54 | 55 | NOTICE: Database changes. Updated database layout to support 56 | counters and delta update timestamps. Its possible to upgrade 57 | the database schema without loosing your current data. 58 | 59 | The collector handels detection of state transitions from, signal 60 | to no-signal, and back from no-signal to signal. This is useful 61 | for (future) snmptrap facilities. 62 | 63 | Support periodic updating streams each heartbeat ticks. 64 | 65 | 2011-05-09 Jesper Dangaard Brouer 66 | 67 | * Version 0.9.0. 68 | 69 | Change default iptables match criteria, to match all mpeg2ts 70 | packets. 71 | 72 | To make the module more generic, change the default match criteria 73 | to match on all correct mpeg2 TS packets (test for the sync 0x47 74 | byte in all TS frames). 75 | 76 | Drop detection is still active, and stats on packet drops are 77 | available via the proc filesystem. These drop detection stats can 78 | be disabled via inverting the parameter --drop-detect, 79 | eg. "! --drop-detect". 80 | 81 | Its still possible to match on drop detection, via the parameter 82 | "--match-drop" (which is default off). 83 | 84 | 2011-05-06 Jesper Dangaard Brouer 85 | 86 | * Version 0.8.0. 87 | 88 | Major rewrite of the makefile system. 89 | 90 | Converted to project to use the well-known configure/autotools 91 | infrastructure, still in combination with the kernels Kbuild 92 | system. 93 | 94 | Also integrated configure with iptv-collector's Perl MakeMaker 95 | system. 96 | 97 | 2011-05-04 Jesper Dangaard Brouer 98 | 99 | * Version 0.7.1. 100 | 101 | Minor fixes to the Makefiles and docs. 102 | 103 | Want a release before starting on converting the project to use 104 | autotools/automake/configure. 105 | 106 | 2011-04-20 Jesper Dangaard Brouer 107 | 108 | * Version 0.7.0. 109 | 110 | Restructured the collector code. 111 | 112 | Notice: Collector config file renamed and changed location to: 113 | 114 | /etc/iptv-analyzer/collector.conf 115 | 116 | (previously /etc/tvprobe.conf). 117 | 118 | The collector, also changed the Perl module layout to 119 | IPTV::Analyzer (used to be called tvprobe). 120 | 121 | 2011-04-18 Jesper Dangaard Brouer 122 | 123 | * Version 0.6.0. 124 | 125 | Internal renaming from "mp2t" to "mpeg2ts" 126 | 127 | The new name "mpeg2ts" much better acronym/shortname for MPEG2 128 | Transport Stream. The old name "mp2t" came from the Wireshark 129 | acronym for this protocol. 130 | 131 | Renamed the iptables kernel module, from "mp2t" to "mpeg2ts". 132 | 133 | Renamed the Perl collector module, from "mp2t.pm" to "mpeg2ts.pm". 134 | 135 | 2011-04-14 Jesper Dangaard Brouer 136 | 137 | * Version 0.3.2. 138 | 139 | Finally integrated Jan Engelhardts patches to the iptables module, 140 | back from the Netfilter Workshop, Oct. 2010. These patches lived 141 | in the Xtables-addon branch named "mp2t". 142 | 143 | Making a release before: 144 | Renaming the kernel module, from "mp2t" to "mpeg2ts". 145 | 146 | 2010-11-17 Jesper Dangaard Brouer 147 | 148 | * Version 0.3.1. 149 | 150 | This version uses/integrates the compat_xtables kernel module from 151 | the Xtables-addons project. This should (hopefully) make the 152 | iptables module compatible with more kernel versions. In theory 153 | the kernel module should be compatible with kernel versions from 154 | 2.6.27 to 2.6.36 (although this is untested). 155 | 156 | 157 | 2010-11-02 Jesper Dangaard Brouer 158 | 159 | * Version 0.3.0-devel. 160 | 161 | This version change is only a developers release. I just want to 162 | make sure that the SVN and GIT version uses a different version, 163 | to avoid confusion. 164 | 165 | 2010-10-29 Jesper Dangaard Brouer 166 | 167 | * Converting from SVN to GIT 168 | 169 | The project is being prepared for going public. The internal SVN 170 | code is converted to a GIT repository, for making it easier to 171 | cooperate with external developers. 172 | 173 | An SVN tag has been created at revision 2303, to mark the point 174 | where no further changes should go into SVN. 175 | 176 | 2010-10-15 Jesper Dangaard Brouer 177 | 178 | * Version 0.2.1-devel. 179 | 180 | Update the iptables module to be compatible with kernel 2.6.35. 181 | Send the iptables code for review during Netfilter Workshop 2010. 182 | 183 | 2009-10-15 Jesper Dangaard Brouer 184 | 185 | * Version 0.2.0-devel. 186 | 187 | Significant changes to the proc file output. Now everything in 188 | the proc file consists of "key:value" pairs separated with 189 | spaces. 190 | 191 | 2009-10-15 Jesper Dangaard Brouer 192 | 193 | * SVN:[1555] Version 0.1.6. 194 | 195 | This version correspond to the version currently (14/10-2009) 196 | running in production on tvprobe002a and tvprobe003a.cxnet.dk. 197 | This version needs some bug fixes, but I want a checkpoint and 198 | a tagged SVN release. 199 | 200 | 2009-06-29 Jesper Dangaard Brouer 201 | 202 | * SVN:[1143] Version 0.1.5 203 | -------------------------------------------------------------------------------- /database/database-schema-latest.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.11 2 | -- 3 | -- Host: localhost Database: tvprobe 4 | -- ------------------------------------------------------ 5 | -- Server version 5.0.51a-24+lenny4-log 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `daemon_session` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `daemon_session`; 23 | SET @saved_cs_client = @@character_set_client; 24 | SET character_set_client = utf8; 25 | CREATE TABLE `daemon_session` ( 26 | `id` int(10) unsigned NOT NULL auto_increment, 27 | `start_time` timestamp NOT NULL default CURRENT_TIMESTAMP, 28 | `stop_time` timestamp NOT NULL default '0000-00-00 00:00:00', 29 | `heartbeat` timestamp NOT NULL default '0000-00-00 00:00:00', 30 | `probe_id` int(11) default NULL, 31 | `daemon_pid` smallint(5) unsigned default NULL, 32 | `mpeg2ts_created` timestamp NOT NULL default '0000-00-00 00:00:00', 33 | `mpeg2ts_version` varchar(50) default NULL, 34 | PRIMARY KEY (`id`) 35 | ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=latin1; 36 | SET character_set_client = @saved_cs_client; 37 | 38 | -- 39 | -- Table structure for table `log_event` 40 | -- 41 | 42 | DROP TABLE IF EXISTS `log_event`; 43 | SET @saved_cs_client = @@character_set_client; 44 | SET character_set_client = utf8; 45 | CREATE TABLE `log_event` ( 46 | `id` bigint(20) unsigned NOT NULL auto_increment, 47 | `stream_session_id` bigint(20) unsigned NOT NULL default '0', 48 | `record_time` timestamp NOT NULL default CURRENT_TIMESTAMP, 49 | `probe_id` int(11) NOT NULL default '0', 50 | `daemon_session_id` int(10) unsigned NOT NULL default '0', 51 | `skips` bigint(20) unsigned NOT NULL default '0', 52 | `discontinuity` bigint(20) unsigned NOT NULL default '0', 53 | `errsec` int(10) unsigned NOT NULL default '0', 54 | `delta_skips` int(10) unsigned NOT NULL default '0', 55 | `delta_discon` int(10) unsigned NOT NULL default '0', 56 | `delta_errsec` int(10) unsigned NOT NULL default '0', 57 | `payload_bytes` bigint(20) unsigned default '0', 58 | `delta_payload_bytes` int(10) unsigned NOT NULL default '0', 59 | `packets` bigint(20) unsigned default '0', 60 | `delta_packets` int(10) unsigned NOT NULL default '0', 61 | `event_type` int(10) unsigned NOT NULL default '0', 62 | `pids` smallint(5) unsigned NOT NULL default '0', 63 | `delta_poll` int(10) unsigned default NULL, 64 | `last_poll` timestamp NOT NULL default '0000-00-00 00:00:00', 65 | `probe_time` timestamp NOT NULL default '0000-00-00 00:00:00', 66 | last_update timestamp NOT NULL default '0000-00-00 00:00:00', 67 | delta_update float unsigned default NULL, 68 | `multicast_dst` char(15) NOT NULL, 69 | `ip_src` char(15) default NULL, 70 | `ttl` smallint(5) unsigned default '0', 71 | PRIMARY KEY (`id`) 72 | ) ENGINE=InnoDB AUTO_INCREMENT=65536 DEFAULT CHARSET=latin1; 73 | SET character_set_client = @saved_cs_client; 74 | 75 | DROP TABLE IF EXISTS event_type; 76 | CREATE TABLE event_type ( 77 | bitmask int(10) unsigned NOT NULL default '0', 78 | label varchar(15) NOT NULL, 79 | description varchar(255), 80 | PRIMARY KEY (bitmask) 81 | ) ENGINE=InnoDB; 82 | 83 | LOCK TABLES event_type WRITE; 84 | INSERT INTO event_type (bitmask, label, description) VALUES 85 | ( 1, "new_stream", "New stream detected"), 86 | ( 2, "drop" , "Drops detected, both skips and discon"), 87 | ( 4, "no_signal" , "Stream have stopped transmitting data"), 88 | ( 16, "ttl_change", "Indicate TTL changed"), 89 | ( 32, "transition", "The event_state changed since last poll"), 90 | ( 64, "heartbeat" , "Heartbeat event to monitor status"), 91 | (128, "invalid" , "Some invalid event situation arose"); 92 | UNLOCK TABLES; 93 | 94 | -- 95 | -- Table structure for table `probes` 96 | -- 97 | 98 | DROP TABLE IF EXISTS `probes`; 99 | SET @saved_cs_client = @@character_set_client; 100 | SET character_set_client = utf8; 101 | CREATE TABLE `probes` ( 102 | `id` int(11) NOT NULL auto_increment, 103 | `ip` varchar(15) NOT NULL, 104 | `input` varchar(100) NOT NULL, 105 | `shortloc` varchar(50) default 'unknown', 106 | `switch` varchar(50) default 'unknown', 107 | `name` varchar(100) default 'unknown', 108 | `description` varchar(100) default 'unknown', 109 | `location` varchar(100) default 'unknown', 110 | `address` varchar(100) default 'unknown', 111 | `distance` int(11) default '0', 112 | `input_ip` varchar(15) default '', 113 | `input_dev` varchar(16) default '', 114 | `procfile` varchar(100) default NULL, 115 | `switchport` varchar(50) default '', 116 | `switchtype` varchar(50) default '', 117 | `hidden` enum('no','yes') NOT NULL default 'no', 118 | PRIMARY KEY (`id`), 119 | KEY `idx_identify` (`ip`,`input`,`shortloc`,`switch`) 120 | ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1; 121 | SET character_set_client = @saved_cs_client; 122 | 123 | -- 124 | -- Table structure for table `stream_session` 125 | -- 126 | 127 | DROP TABLE IF EXISTS `stream_session`; 128 | SET @saved_cs_client = @@character_set_client; 129 | SET character_set_client = utf8; 130 | CREATE TABLE `stream_session` ( 131 | `id` bigint(20) unsigned NOT NULL auto_increment, 132 | `probe_id` int(11) NOT NULL default '0', 133 | `daemon_session_id` int(10) unsigned NOT NULL default '0', 134 | `start_time` timestamp NOT NULL default CURRENT_TIMESTAMP, 135 | `stop_time` timestamp NOT NULL default '0000-00-00 00:00:00', 136 | `multicast_dst` char(15) NOT NULL, 137 | `ip_src` char(15) default NULL, 138 | `port_dst` smallint(5) unsigned default NULL, 139 | `port_src` smallint(5) unsigned default NULL, 140 | `logid_begin` bigint(20) unsigned NOT NULL default '0', 141 | `logid_end` bigint(20) unsigned NOT NULL default '0', 142 | PRIMARY KEY (`id`) 143 | ) ENGINE=InnoDB AUTO_INCREMENT=4719 DEFAULT CHARSET=latin1; 144 | SET character_set_client = @saved_cs_client; 145 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 146 | 147 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 148 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 149 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 150 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 151 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 152 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 153 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 154 | 155 | -- Dump completed on 2010-11-01 14:26:23 156 | -------------------------------------------------------------------------------- /webfrontend/www/staging/pie01.php: -------------------------------------------------------------------------------- 1 | TEST:"; var_dump($CONFIG); 18 | 19 | //$_REQUEST = cleanup_input_request(); 20 | $probeid = $_REQUEST['probeid']; 21 | if (!is_numeric($probeid)) { 22 | die("Cannot proceed -- Missing probeid input"); 23 | } 24 | 25 | $tstampF = $_REQUEST['tstampF']; 26 | $tstampT = $_REQUEST['tstampT']; 27 | $bucketsz = $_REQUEST['bucketsz']; 28 | 29 | # Select all channel within the period 30 | $data =& multicast_probe_data_query_ts($probeid, $tstampF, $tstampT); 31 | 32 | //$data = multicast_probe_data_query($probeid, $fromT, $toT); 33 | //db_disconnect(); 34 | #print_r($data); 35 | 36 | $Canvas =& Image_Canvas::factory('png', 37 | array('width' => 400, 38 | 'height' => 350, 39 | 'usemap' => true)); 40 | 41 | // This is how you get the ImageMap object, 42 | // fx. to save map to file (using toHtml()) 43 | $Imagemap = $Canvas->getImageMap(); 44 | 45 | // create the graph 46 | //$Graph =& Image_Graph::factory('graph', array(400, 350)); 47 | $Graph =& Image_Graph::factory('graph', $Canvas); 48 | 49 | // add a TrueType font 50 | $myfont = '/usr/share/fonts/truetype/freefont/FreeSerif.ttf'; 51 | $Font =& $Graph->addNew('font', $myfont); 52 | //$Font =& $Graph->addNew('font', 'Verdana'); 53 | // set the font size to 11 pixels 54 | $Font->setSize(8); 55 | 56 | $Graph->setFont($Font); 57 | 58 | // setup the plotarea, legend and their layout 59 | $Graph->add( 60 | Image_Graph::vertical( 61 | Image_Graph::factory('title', array('Channel Drop Proportion', 12)), 62 | Image_Graph::vertical( 63 | $Plotarea = Image_Graph::factory('plotarea'), 64 | $Legend = Image_Graph::factory('legend'), 65 | 70 66 | ), 67 | 5 68 | ) 69 | ); 70 | $Legend->setPlotArea($Plotarea); 71 | $Plotarea->hideAxis(); 72 | 73 | // create the dataset 74 | $Dataset =& Image_Graph::factory('dataset'); 75 | 76 | function point($channel, $value, $Dataset, $urldata) 77 | { 78 | $title = "$channel (drops:$value)"; 79 | $colorid = NULL; 80 | if ($value < 10) { 81 | // BAD: This should really be dependend on the period... 82 | $colorid = "low"; 83 | } 84 | 85 | $url = "../pages/channel_view.php?channel=$channel"; 86 | 87 | $url .= "&tstampF=" . $urldata['tstampF']; 88 | $url .= "&tstampT=" . $urldata['tstampT']; 89 | 90 | $url .= "&probeid=" . $urldata['probeid']; 91 | 92 | $title .= " records:" . $urldata['records']; 93 | $title .= " period:" . $urldata['period'] ."s"; 94 | 95 | $Dataset->addPoint($channel, $value, 96 | array( 97 | 'id' => $colorid, 98 | 'url' => $url, 99 | 'alt' => $value, 100 | 'target' => '_blank', 101 | 'htmltags' => array('title' => $title) 102 | ) 103 | ); 104 | } 105 | 106 | # Get hold of the currently deselected channels 107 | $remove_channels =& $_POST['remove_channels']; 108 | 109 | $cnt = 0; 110 | $cnt_removed = 0; 111 | foreach ($data as $row) { 112 | $skips = $row['skips']; 113 | $drops = $row['drops']; 114 | 115 | $multicast_dst = $row['multicast_dst']; 116 | 117 | if (isset($remove_channels["$multicast_dst"])) { 118 | // Skip 119 | $cnt_removed++; 120 | continue; 121 | } 122 | 123 | #$urldata['bucketsz']= $bucketsz; 124 | $urldata['probeid'] = $probeid; 125 | $urldata['tstampF'] = $tstampF; 126 | $urldata['tstampT'] = $tstampT; 127 | 128 | $urldata['period'] = $tstampT - $tstampF; 129 | $urldata['records'] = $row['records'];; 130 | 131 | $cnt++; 132 | if ($cnt < 50 ){ 133 | point("$multicast_dst", $drops, $Dataset, $urldata); 134 | } 135 | } 136 | #echo "cnt:$cnt
\n"; 137 | 138 | // create the 1st plot as smoothed area chart using the 1st dataset 139 | $Plot =& $Plotarea->addNew('Image_Graph_Plot_Pie', $Dataset); 140 | 141 | //$Plot->setRestGroup(11, 'Other animals'); 142 | 143 | //FRA: plot_pie_rotate/ 144 | // create a Y data value marker 145 | $Marker =& $Plot->addNew('Image_Graph_Marker_Value', IMAGE_GRAPH_PCT_Y_TOTAL); 146 | // create a pin-point marker type 147 | $PointingMarker =& $Plot->addNew('Image_Graph_Marker_Pointing_Angular', array(20, &$Marker)); 148 | // and use the marker on the 1st plot 149 | $Plot->setMarker($PointingMarker); 150 | // format value marker labels as percentage values 151 | $Marker->setDataPreprocessor(Image_Graph::factory('Image_Graph_DataPreprocessor_Formatted', '%0.1f%%')); 152 | 153 | 154 | 155 | $Plot->Radius = 2; 156 | 157 | // set a line color 158 | $Plot->setLineColor('gray'); 159 | 160 | $Plot->setStartingAngle(-180); 161 | 162 | // set a standard fill style 163 | $FillArray =& Image_Graph::factory('Image_Graph_Fill_Array'); 164 | $Plot->setFillStyle($FillArray); 165 | 166 | $FillArray->addColor('red@0.2'); 167 | $FillArray->addColor('yellow@0.2'); 168 | $FillArray->addColor('orange@0.2'); 169 | $FillArray->addColor('blue@0.2'); 170 | $FillArray->addColor('black@0.2', 'rest'); 171 | $FillArray->addColor('green@0.2', 'low'); 172 | 173 | #$FillArray->addColor('green@0.2'); 174 | #$FillArray->addColor('blue@0.2'); 175 | #$FillArray->addColor('yellow@0.2'); 176 | #$FillArray->addColor('red@0.2'); 177 | #$FillArray->addColor('orange@0.2'); 178 | #$FillArray->addColor('black@0.2', 'rest'); 179 | 180 | 181 | $Plot->explode(10); 182 | 183 | 184 | // create a Y data value marker 185 | $Marker =& $Plot->addNew('Image_Graph_Marker_Value', IMAGE_GRAPH_PCT_Y_TOTAL); 186 | // fill it with white 187 | $Marker->setFillColor('white'); 188 | // and use black border 189 | $Marker->setBorderColor('black'); 190 | // and format it using a data preprocessor 191 | $Marker->setDataPreprocessor(Image_Graph::factory('Image_Graph_DataPreprocessor_Formatted', '%0.1f%%')); 192 | $Marker->setFontSize(7); 193 | 194 | // create a pin-point marker type 195 | $PointingMarker =& $Plot->addNew('Image_Graph_Marker_Pointing_Angular', 196 | array(20, &$Marker)); 197 | // and use the marker on the plot 198 | $Plot->setMarker($PointingMarker); 199 | 200 | // output the Graph 201 | //$Graph->done(); 202 | $filename = generateFilename("pie01", $_REQUEST, 'png'); 203 | 204 | // Special output the Graph 205 | $output = $Graph->done( 206 | array( 207 | 'tohtml' => True, 208 | 'showtime' => $displayTiming, 209 | 'border' => 0, 210 | 'filename' => $filename, 211 | 'filepath' => './graphs/', 212 | 'urlpath' => 'graphs/' 213 | ) 214 | ); 215 | 216 | print $output; 217 | #print '
' . htmlspecialchars($output) . '
'; 218 | 219 | echo "

Channels in period:$cnt
\n"; 220 | echo "Removed channels:$cnt_removed\n"; 221 | echo "

\n"; 222 | 223 | form_remove_channels($data); 224 | 225 | ?> 226 | -------------------------------------------------------------------------------- /iptables-module/libxt_mpeg2ts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Userspace interface for MPEG2 TS match extension "mpeg2ts" for Xtables. 3 | * 4 | * Copyright (c) Jesper Dangaard Brouer , 2009-2013 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License; either 8 | * version 2 of the License, or any later version, as published by the 9 | * Free Software Foundation. See . 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include "xt_mpeg2ts.h" 23 | 24 | /* 25 | * Userspace iptables/xtables interface for mpeg2ts module. 26 | */ 27 | 28 | /* FIXME: don't think this compat check does not cover all versions */ 29 | #ifndef XTABLES_VERSION 30 | #define xtables_error exit_error 31 | #endif 32 | 33 | /* 34 | The default match criteria, since version 0.9.0, is to match on 35 | correct MPEG2 TS packets. Previously (<= 0.8.0) a rule would only 36 | match when a drop were detected. 37 | 38 | Its still possible to match on drop detection, via the parameter 39 | "--match-drop" (which is default off). 40 | 41 | Stats on packet drops are available via the proc filesystem. These 42 | drop detection stats can be disabled via inverting the parameter 43 | --drop-detect, eg. "! --drop-detect". 44 | 45 | */ 46 | static const struct option mpeg2ts_mt_opts[] = { 47 | {.name = "name", .has_arg = true, .val = 'n'}, 48 | {.name = "drop-detect", .has_arg = false, .val = 'd'}, 49 | {.name = "match-drop", .has_arg = false, .val = 'm'}, 50 | {.name = "max-streams", .has_arg = true, .val = 'x'}, 51 | {NULL}, 52 | }; 53 | 54 | static void mpeg2ts_mt_help(void) 55 | { 56 | printf( 57 | "mpeg2ts (MPEG2 Transport Stream) match options:\n" 58 | "VERSION %s\n" 59 | " [--name ] Name for proc file /proc/net/xt_mpeg2ts/rule_NAME\n" 60 | " [--match-drop] Match on lost TS frames (default: off)\n" 61 | " [--drop-detect] Detect TS frame loss and store stats (default: ON)\n" 62 | " [--max-streams ] Track 'max' number of streams (per rule)\n", 63 | version 64 | ); 65 | } 66 | 67 | static void mpeg2ts_mt_init(struct xt_entry_match *match) 68 | { 69 | struct xt_mpeg2ts_mtinfo *info = (void *)match->data; 70 | /* Enable drop detection per default */ 71 | info->flags = XT_MPEG2TS_DETECT_DROP; 72 | 73 | /* Match on drops is disabled per default */ 74 | /* XT_MPEG2TS_MATCH_DROP */ 75 | } 76 | 77 | static int mpeg2ts_mt_parse(int c, char **argv, int invert, unsigned int *flags, 78 | const void *entry, struct xt_entry_match **match) 79 | { 80 | struct xt_mpeg2ts_mtinfo *info = (void *)(*match)->data; 81 | uint32_t num; 82 | 83 | switch (c) { 84 | case 'n': /* --name */ 85 | xtables_param_act(XTF_ONLY_ONCE, "mpeg2ts", "--name", 86 | *flags & XT_MPEG2TS_PARAM_NAME); 87 | if (invert) 88 | xtables_error(PARAMETER_PROBLEM, "Inverting name?"); 89 | if (strlen(optarg) == 0) 90 | xtables_error(PARAMETER_PROBLEM, "Zero-length name?"); 91 | if (strchr(optarg, '"') != NULL) 92 | xtables_error(PARAMETER_PROBLEM, 93 | "Illegal character in name (\")!"); 94 | strncpy(info->rule_name, optarg, sizeof(info->rule_name)); 95 | info->flags |= XT_MPEG2TS_PARAM_NAME; 96 | *flags |= XT_MPEG2TS_PARAM_NAME; 97 | break; 98 | 99 | case 'd': /* --drop-detect */ 100 | if (*flags & XT_MPEG2TS_DETECT_DROP) 101 | xtables_error(PARAMETER_PROBLEM, 102 | "Can't specify --drop-detect option twice"); 103 | *flags |= XT_MPEG2TS_DETECT_DROP; 104 | 105 | if (invert) 106 | info->flags &= ~XT_MPEG2TS_DETECT_DROP; 107 | else 108 | info->flags |= XT_MPEG2TS_DETECT_DROP; 109 | 110 | break; 111 | 112 | case 'm': /* --match-drop */ 113 | if (*flags & XT_MPEG2TS_MATCH_DROP) 114 | xtables_error(PARAMETER_PROBLEM, 115 | "Can't specify --match-drop option twice"); 116 | *flags |= XT_MPEG2TS_MATCH_DROP; 117 | 118 | if (invert) 119 | info->flags &= ~XT_MPEG2TS_MATCH_DROP; 120 | else 121 | info->flags |= XT_MPEG2TS_MATCH_DROP; 122 | 123 | break; 124 | 125 | case 'x': /* --max-streams */ 126 | if (*flags & XT_MPEG2TS_MAX_STREAMS) 127 | xtables_error(PARAMETER_PROBLEM, 128 | "Can't specify --max-streams option twice"); 129 | *flags |= XT_MPEG2TS_MAX_STREAMS; 130 | 131 | if (invert) { 132 | info->cfg.max = 0; 133 | /* printf("inverted\n"); */ 134 | break; 135 | } 136 | 137 | /* OLD iptables style 138 | if (string_to_number(optarg, 0, 0xffffffff, &num) == -1) 139 | xtables_error(PARAMETER_PROBLEM, 140 | "bad --max-stream: `%s'", optarg); 141 | */ 142 | 143 | /* C-style 144 | char *end; 145 | num = strtoul(optarg, &end, 0); 146 | */ 147 | 148 | /* New xtables style */ 149 | if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) 150 | xtables_error(PARAMETER_PROBLEM, 151 | "bad --max-streams: `%s'", optarg); 152 | 153 | /* DEBUG: printf("--max-stream=%lu\n", num); */ 154 | info->flags |= XT_MPEG2TS_MAX_STREAMS; 155 | info->cfg.max = num; 156 | 157 | break; 158 | 159 | default: 160 | return false; 161 | } 162 | 163 | return true; 164 | } 165 | 166 | static void mpeg2ts_mt_print(const void *entry, 167 | const struct xt_entry_match *match, int numeric) 168 | { 169 | const struct xt_mpeg2ts_mtinfo *info = (const void *)(match->data); 170 | 171 | /* Always indicate this is a mpeg2ts match rule */ 172 | printf("mpeg2ts match"); 173 | 174 | if (info->flags & XT_MPEG2TS_PARAM_NAME) 175 | printf(" name:\"%s\"", info->rule_name); 176 | 177 | if (!(info->flags & XT_MPEG2TS_DETECT_DROP)) 178 | printf(" !drop-detect"); 179 | 180 | if ((info->flags & XT_MPEG2TS_MATCH_DROP)) 181 | printf(" match-drop"); 182 | 183 | if (info->flags & XT_MPEG2TS_MAX_STREAMS) 184 | printf(" max-streams:%u", info->cfg.max); 185 | } 186 | 187 | static void mpeg2ts_mt_save(const void *entry, 188 | const struct xt_entry_match *match) 189 | { 190 | const struct xt_mpeg2ts_mtinfo *info = (const void *)(match->data); 191 | 192 | /* We need to handle --name, --drop-detect, and --max-streams. */ 193 | if (info->flags & XT_MPEG2TS_PARAM_NAME) 194 | printf(" --name \"%s\"", info->rule_name); 195 | 196 | if (!(info->flags & XT_MPEG2TS_DETECT_DROP)) 197 | printf(" ! --drop-detect"); 198 | 199 | if (info->flags & XT_MPEG2TS_MATCH_DROP) 200 | printf(" --match-drop"); 201 | 202 | if (info->flags & XT_MPEG2TS_MAX_STREAMS) 203 | printf(" --max-streams %u", info->cfg.max); 204 | 205 | } 206 | 207 | static struct xtables_match mpeg2ts_mt_reg = { 208 | .version = XTABLES_VERSION, 209 | .name = "mpeg2ts", 210 | .revision = 0, 211 | .family = PF_UNSPEC, 212 | .size = XT_ALIGN(sizeof(struct xt_mpeg2ts_mtinfo)), 213 | .userspacesize = offsetof(struct xt_mpeg2ts_mtinfo, hinfo), 214 | .init = mpeg2ts_mt_init, 215 | .help = mpeg2ts_mt_help, 216 | .parse = mpeg2ts_mt_parse, 217 | /* .final_check = mpeg2ts_mt_check,*/ 218 | .print = mpeg2ts_mt_print, 219 | .save = mpeg2ts_mt_save, 220 | .extra_opts = mpeg2ts_mt_opts, 221 | }; 222 | 223 | static void _init(void) 224 | { 225 | xtables_register_match(&mpeg2ts_mt_reg); 226 | } 227 | -------------------------------------------------------------------------------- /webfrontend/www/graphs.inc.php: -------------------------------------------------------------------------------- 1 | setBarWidth($res, "%") 18 | # 19 | function calcBarWidthPercentage($bucketsz, $tstampF, $tstampT, $records=NULL) 20 | { 21 | $min_bucketsz = 10; 22 | $percent = 1.0; 23 | 24 | # Calc based on the number of data records 25 | if (is_numeric($records) && $records > 0) { 26 | $percent = 100 / $records; 27 | } 28 | 29 | # Calc based on the timeperiod and bucket size 30 | if (is_numeric($bucketsz) && $bucketsz > 0) { 31 | $period = $tstampT - $tstampF; 32 | if ($period > 0) { 33 | if ($bucketsz < $min_bucketsz) 34 | $bucketsz = $min_bucketsz; 35 | // Calc the max possible date records 36 | $max_records = $period / $bucketsz; 37 | if ($max_records > 0 && $max_records > $records) { 38 | // This is often the case 39 | $percent = 100 / $max_records; 40 | } 41 | } 42 | } 43 | 44 | return $percent; 45 | } 46 | 47 | function setBarWidth(& $Plot, $bucketsz, $tstampF, $tstampT, $records=NULL) 48 | { 49 | $percent = calcBarWidthPercentage($bucketsz, $tstampF, $tstampT, $records); 50 | $Plot->setBarWidth($percent,"%"); 51 | } 52 | 53 | ###### 54 | # Generate graph names 55 | 56 | function generateFilename($prefix, & $input, $suffix = 'png') 57 | { 58 | $str_parts=array(); 59 | 60 | if (!is_array($input)) { 61 | $err = "Cannot generate a filename, \$input must be an array"; 62 | trigger_error("$err", E_USER_ERROR); 63 | } 64 | 65 | if(isset($input['probeid'])) { 66 | $str_parts[]= 'probeid'.$input['probeid']; 67 | } 68 | if(isset($input['tstampF'])) { 69 | $str_parts[]= 'tstampF'.$input['tstampF']; 70 | } 71 | if(isset($input['tstampT'])) { 72 | $str_parts[]= 'tstampT'.$input['tstampT']; 73 | } 74 | if(isset($input['bucketsz'])) { 75 | $str_parts[]= 'bucketsz'.$input['bucketsz']; 76 | } 77 | if(isset($input['maxy'])) { 78 | $str_parts[]= 'maxy'.$input['maxy']; 79 | } 80 | 81 | $generated_string = implode('_',$str_parts); 82 | $name = $prefix . "__" . $generated_string .".$suffix"; 83 | return $name; 84 | } 85 | 86 | ###### 87 | # Creating the graph elements 88 | 89 | function create_graph_usemap01($width=700, $height=160, $fontsize=7) 90 | { 91 | $Canvas =& Image_Canvas::factory('png', 92 | array('width' => $width, 93 | 'height' => $height, 94 | 'usemap' => true)); 95 | 96 | // This is how you get the ImageMap object, 97 | // fx. to save map to file (using toHtml()) 98 | $Imagemap = $Canvas->getImageMap(); 99 | 100 | // Create the graph 101 | //$Graph =& Image_Graph::factory('graph', array(600, 140)); 102 | $Graph =& Image_Graph::factory('graph', $Canvas); 103 | 104 | // add a TrueType font 105 | //$myfont = '/usr/share/fonts/truetype/freefont/FreeSans.ttf'; 106 | $myfont = '/usr/share/fonts/truetype/freefont/FreeSerif.ttf'; 107 | 108 | $Font =& $Graph->addNew('font', $myfont); 109 | //$Font =& $Graph->addNew('font', 'Verdana'); 110 | //$Font =& $Graph->addNew('font', 'Helvetica'); 111 | 112 | // set the font size 113 | $Font->setSize($fontsize); 114 | 115 | $Graph->setFont($Font); 116 | #return array(&$Graph, &$Font); 117 | return $Graph; 118 | } 119 | 120 | function create_plotarea_with_title01(&$Graph, &$Font, $title) 121 | { 122 | /* How element are connected is a bit hard to understand, please read: 123 | http://pear.veggerby.dk/wiki/ 124 | image_graph:getting_started_guide#creating_the_building_blocks 125 | */ 126 | 127 | /* 128 | $Graph->add( 129 | Image_Graph::vertical( 130 | Image_Graph::factory('title', array("$title", 10)), 131 | Image_Graph::vertical( 132 | $Plotarea = Image_Graph::factory('plotarea'), 133 | $Legend = Image_Graph::factory('legend'), 134 | 90 135 | ), 136 | 5 137 | ) 138 | ); 139 | $Legend->setPlotarea($Plotarea); 140 | */ 141 | 142 | /* Here we start with the Title and add the Plotarea to the Title, 143 | its might seem a little odd, but it works... 144 | */ 145 | $Title = $Graph->addNew('title', array("$title", 10)); 146 | $Plotarea = $Title->addNew('plotarea'); 147 | $Plotarea->setFont($Font); 148 | 149 | return $Plotarea; 150 | } 151 | 152 | 153 | function create_plotarea_with_title02(&$Graph, &$Font, $title) 154 | { 155 | $Title = $Graph->addNew('title', array("$title", 10)); 156 | /* Notice the Axis settings on plotarea */ 157 | $Plotarea = $Title->addNew('plotarea', array('axis', 'axis')); 158 | $Plotarea->setFont($Font); 159 | return $Plotarea; 160 | } 161 | 162 | 163 | function data_addPoints01(& $Dataset, & $data, $droptype, $max_y_value, $urldata) 164 | { 165 | //Data comes from: $data = probe_data_query($probeid); 166 | 167 | $cnt=0; 168 | # LOOP: addPoints 169 | foreach ($data as $row) { 170 | $cnt++; 171 | 172 | #$date = $row['datoen']; 173 | $date = $row['timestamp']; 174 | $skips = $row['skips']; 175 | $drops = $row['drops']; 176 | $value = $row["$droptype"]; 177 | 178 | $timemin = $row['timemin']; 179 | $timemax = $row['timemax']; 180 | $period = $timemax - $timemin; 181 | 182 | $records = $row['records']; 183 | 184 | # "title" info in html map 185 | $title = "$droptype:$value"; 186 | $title .= " hour:" . date("H:i:s", $date); 187 | $title .= " (day:" . date("j.M", $date) . ")"; 188 | $title .= " period:{$period}s"; 189 | 190 | if (isset($records)) { 191 | $title .= " records:$records"; 192 | } 193 | 194 | //$title = "drops:$drops skips:$skips"; 195 | //echo "TEST: value:$value $title
\n"; 196 | 197 | # Color code if data value exceed max allowed data value 198 | $colorid = 'DROPS'; 199 | if ($value > $max_y_value) { 200 | $value = $max_y_value; 201 | $title .= " EXCESSIVE"; 202 | $colorid = 'EXCESS'; 203 | } 204 | 205 | # Create an URL 206 | $bucketsz = $urldata['bucketsz']; 207 | $probeid = $urldata['probeid']; 208 | $channel = $urldata['channel']; 209 | 210 | #echo "bucketsz:$bucketsz "; 211 | #echo "timemin:$timemin "; 212 | #echo "timemax:$timemax "; 213 | # For the link adjust $timemin/max, e.g. sub/add bucketsz period 214 | if (is_numeric($bucketsz)) { 215 | $timemin -= floor($bucketsz / 2); 216 | $timemax += floor($bucketsz / 2); 217 | } 218 | #echo "timemin2:$timemin "; 219 | #echo "timemax2:$timemax
\n"; 220 | 221 | 222 | # Calc new bucketsz, by saying how many elements I want on the graph 223 | $wanted_elements = 50; 224 | $zoom_period = $timemax - $timemin; 225 | $newsz = floor($zoom_period / $wanted_elements); 226 | if ($newsz < 10) { 227 | $newsz = 10; 228 | } 229 | 230 | $URL = "?tstampF=$timemin&tstampT=$timemax" 231 | . "&probeid=$probeid" 232 | . "&bucketsz=" . $newsz; 233 | 234 | if (isset($channel)) { 235 | $URL .= "&channel=$channel"; 236 | } 237 | 238 | # Add value points to the dataset 239 | $Dataset->addPoint($date, $value, 240 | array( 241 | 'id' => "$colorid", 242 | 'url' => "$URL", 243 | 'alt' => $value, 244 | 'target' => '_self', 245 | 'htmltags' => array('title' => $title) 246 | ) 247 | ); 248 | } 249 | return $cnt; 250 | } 251 | 252 | function data_addPoints_fake(& $Dataset, $bucketsz, 253 | $tstampF, $tstampT, $urldata=NULL) 254 | { 255 | $cnt=0; 256 | # LOOP: addPoints 257 | for ($i = $tstampF; $i < $tstampT; $i = $i + $bucketsz) { 258 | $cnt++; 259 | 260 | $date = $i; 261 | $value = 1; 262 | 263 | # "title" info in html map 264 | $title = "no drops"; 265 | $title .= " hour:" . date("H:i:s", $date); 266 | $title .= " (day:" . date("j.M", $date) . ")"; 267 | 268 | # Color code 269 | $colorid = 'NODROPS'; 270 | 271 | # Create an URL 272 | $bucketsz = $urldata['bucketsz']; 273 | $probeid = $urldata['probeid']; 274 | 275 | $URL = "?tstampF=$tstampF&tstampT=$tstampT" 276 | . "&probeid=$probeid&bucketsz=" 277 | . $bucketsz; 278 | 279 | # Add value points to the dataset 280 | $Dataset->addPoint($date, $value, 281 | array( 282 | 'id' => "$colorid", 283 | 'url' => "$URL", 284 | 'alt' => $value, 285 | 'target' => '_self', 286 | 'htmltags' => array('title' => $title) 287 | ) 288 | ); 289 | } 290 | return $cnt; 291 | } 292 | 293 | 294 | 295 | ?> -------------------------------------------------------------------------------- /snmp/mibs/IPTV-ANALYZER-MIB.txt: -------------------------------------------------------------------------------- 1 | IPTV-ANALYZER-MIB DEFINITIONS ::= BEGIN 2 | 3 | IMPORTS 4 | cxnet 5 | FROM CXNET-CORE-MIB 6 | MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, IpAddress 7 | FROM SNMPv2-SMI; 8 | 9 | 10 | iptvAnalyzer MODULE-IDENTITY 11 | LAST-UPDATED "201105300000Z" 12 | ORGANIZATION "ComX Networks A/S" 13 | CONTACT-INFO 14 | "email: netoptimizer@brouer.com 15 | Author: Jesper Dangaard Brouer" 16 | DESCRIPTION 17 | "Fixing syntax of the MIB file" 18 | REVISION "201105300000Z" 19 | DESCRIPTION 20 | "The MIB module is part of the IPTV-Analyzer 21 | project, and is used for documenting the 22 | snmptraps used by the iptv-collector." 23 | -- 1.3.6.1.4.1.26124.43 24 | ::= { cxnet 43 } 25 | 26 | 27 | -- The General Group is for global settings of the collector deamon 28 | 29 | -- The variables can be used for identifying the collector probe 30 | 31 | collectorGeneralGroup OBJECT IDENTIFIER ::= { iptvAnalyzer 1 } 32 | 33 | -- collector.conf: variable probe_ip 34 | collectorId OBJECT-TYPE 35 | SYNTAX IpAddress 36 | MAX-ACCESS read-write 37 | STATUS current 38 | DESCRIPTION 39 | "Main identifier of the IPTV-collector daemon, 40 | is an IP-address. This comes from the collector.conf 41 | variable probe_ip" 42 | -- 1.3.6.1.4.1.26124.43.1.1 43 | ::= { collectorGeneralGroup 1 } 44 | 45 | -- collector.conf: variable probe_name 46 | collectorName OBJECT-TYPE 47 | SYNTAX OCTET STRING 48 | MAX-ACCESS read-write 49 | STATUS current 50 | DESCRIPTION 51 | "Textual identifier of the IPTV-collector daemon. 52 | This comes from the collector.conf variable probe_name" 53 | -- 1.3.6.1.4.1.26124.43.1.2 54 | ::= { collectorGeneralGroup 2 } 55 | 56 | 57 | -- Trap section 58 | 59 | iptvAnalyzerTrap OBJECT IDENTIFIER ::= { iptvAnalyzer 2 } 60 | -- 1.3.6.1.4.1.26124.43.2 61 | 62 | -- Trap Support Objects 63 | -- The following are support objects for the traps. 64 | 65 | collectorTrapControl OBJECT IDENTIFIER ::= { iptvAnalyzerTrap 1 } 66 | -- 1.3.6.1.4.1.26124.43.2.1 67 | 68 | -- Sub divide: Event properties 69 | collectorEvent OBJECT IDENTIFIER ::= { collectorTrapControl 1 } 70 | -- 1.3.6.1.4.1.26124.43.2.1.1 71 | 72 | eventType OBJECT-TYPE 73 | -- Notice MIBs do NOT like underscores in definitions, thus the 74 | -- event type naming is converted to camel notation. 75 | SYNTAX INTEGER { 76 | newStream (1), -- New stream detected 77 | drop (2), -- Drops detected, both skips and discon 78 | noSignal (4), -- Stream have stopped transmitting data 79 | -- to clear "noSignal" use eventSeverity = 0 80 | ttlChange (16), -- Indicate TTL changed 81 | transition (32), -- The event_state changed since last poll 82 | heartbeat (64), -- Heartbeat event to monitor status 83 | invalid (128) -- Some invalid event situation arose 84 | } 85 | MAX-ACCESS read-only 86 | STATUS current 87 | DESCRIPTION 88 | "The different Event Types used by the collector." 89 | -- 1.3.6.1.4.1.26124.43.2.1.1.1 90 | ::= { collectorEvent 1 } 91 | 92 | eventName OBJECT-TYPE 93 | SYNTAX OCTET STRING ( SIZE(15) ) 94 | MAX-ACCESS read-only 95 | STATUS current 96 | DESCRIPTION 97 | "Textual name representatiog of the Event Type" 98 | -- 1.3.6.1.4.1.26124.43.2.1.1.2 99 | ::= { collectorEvent 2 } 100 | 101 | eventSeverity OBJECT-TYPE 102 | -- severity levels taken from ZenOSS 103 | -- http://community.zenoss.org/docs/DOC-4766#d0e6170 104 | -- Number Name Color in ZenOSS 105 | -- 0 Clear Green 106 | -- 1 Debug Grey 107 | -- 2 Info Blue 108 | -- 3 Warning Yellow 109 | -- 4 Error Orange 110 | -- 5 Critical Red 111 | SYNTAX INTEGER { 112 | clear (0), -- Clears the eventType 113 | debug (1), 114 | info (2), 115 | warning (3), 116 | error (4), 117 | critical (5) 118 | } 119 | MAX-ACCESS read-only 120 | STATUS current 121 | DESCRIPTION 122 | "The severity level of an event. Primarily used for 123 | clearing eventTypes, e.g. clear no_signal event." 124 | -- 1.3.6.1.4.1.26124.43.2.1.1.3 125 | ::= { collectorEvent 3 } 126 | 127 | 128 | -- Future: 129 | -- EventBitVector OBJECT-TYPE 130 | -- Can contain more events in its bitVector 131 | 132 | -- Sub divide: Stream properties 133 | streamEvent OBJECT IDENTIFIER ::= { collectorTrapControl 2 } 134 | -- 1.3.6.1.4.1.26124.43.2.1.2 135 | 136 | multicastDest OBJECT-TYPE 137 | SYNTAX IpAddress 138 | MAX-ACCESS read-only 139 | STATUS current 140 | DESCRIPTION 141 | "The multicast IP address of the mpeg2ts stream. 142 | This can also be a ordenary IP if multicast is not used." 143 | -- 1.3.6.1.4.1.26124.43.2.1.2.1 144 | ::= { streamEvent 1 } 145 | 146 | streamerSource OBJECT-TYPE 147 | SYNTAX IpAddress 148 | MAX-ACCESS read-only 149 | STATUS current 150 | DESCRIPTION 151 | "The multicast IP address of the mpeg2ts stream. 152 | This can also be a ordenary IP if multicast is not used." 153 | -- 1.3.6.1.4.1.26124.43.2.1.2.2 154 | ::= { streamEvent 2 } 155 | 156 | -- Future: 157 | -- streamTimeToLive OBJECT-TYPE 158 | -- streamDstPort OBJECT-TYPE 159 | -- streamSrcPort OBJECT-TYPE 160 | -- channelName OBJECT-TYPE 161 | 162 | 163 | -- Sub divide: Config "input" settings from collector.conf 164 | collectorInput OBJECT IDENTIFIER ::= { collectorTrapControl 3 } 165 | -- 1.3.6.1.4.1.26124.43.2.1.3 166 | 167 | inputKey OBJECT-TYPE 168 | SYNTAX OCTET STRING 169 | MAX-ACCESS read-only 170 | STATUS current 171 | DESCRIPTION 172 | "The input[key] used in collector.conf. This is used for 173 | referencing the mpeg2ts proc file input." 174 | -- 1.3.6.1.4.1.26124.43.2.1.3.1 175 | ::= { collectorInput 1 } 176 | 177 | inputShortloc OBJECT-TYPE 178 | SYNTAX OCTET STRING 179 | MAX-ACCESS read-only 180 | STATUS current 181 | DESCRIPTION 182 | "The input[key][shortloc] used in collector.conf. This is used for 183 | referencing input options." 184 | -- 1.3.6.1.4.1.26124.43.2.1.3.2 185 | ::= { collectorInput 2 } 186 | 187 | inputSwitch OBJECT-TYPE 188 | SYNTAX OCTET STRING 189 | MAX-ACCESS read-only 190 | STATUS current 191 | DESCRIPTION 192 | "The input[key][switch] used in collector.conf. This is used for 193 | referencing input options." 194 | -- 1.3.6.1.4.1.26124.43.2.1.3.3 195 | ::= { collectorInput 3 } 196 | 197 | -- Sub divide: collector information 198 | collectorInfo OBJECT IDENTIFIER ::= { collectorTrapControl 4 } 199 | -- 1.3.6.1.4.1.26124.43.2.1.4 200 | 201 | collectorVersion OBJECT-TYPE 202 | SYNTAX OCTET STRING 203 | MAX-ACCESS read-only 204 | STATUS current 205 | DESCRIPTION 206 | "The version of the IPTV-collector." 207 | -- 1.3.6.1.4.1.26124.43.2.1.4.1 208 | ::= { collectorInfo 1 } 209 | 210 | mpeg2tsVersion OBJECT-TYPE 211 | SYNTAX OCTET STRING 212 | MAX-ACCESS read-only 213 | STATUS current 214 | DESCRIPTION 215 | "The version of the mpeg2ts kernel module." 216 | -- 1.3.6.1.4.1.26124.43.2.1.4.2 217 | ::= { collectorInfo 2 } 218 | 219 | -- 220 | -- Traps 221 | -- Defining what objects a given trap contains 222 | -- 223 | collectorTraps OBJECT IDENTIFIER ::= { iptvAnalyzerTrap 2 } 224 | -- 1.3.6.1.4.1.26124.43.2.2 225 | 226 | generalEvent NOTIFICATION-TYPE 227 | OBJECTS { 228 | collectorVersion, 229 | collectorId, -- The originator of the trap 230 | collectorName, 231 | eventType, -- The event type 232 | eventName 233 | -- Perhaps use sysLocation from SNMPv2-MIB, and use input[key][location]? 234 | } 235 | STATUS current 236 | DESCRIPTION 237 | "General event" 238 | -- 1.3.6.1.4.1.26124.43.2.2.1 239 | ::= { collectorTraps 1 } 240 | 241 | streamNoSignal NOTIFICATION-TYPE 242 | OBJECTS { 243 | collectorVersion, 244 | collectorId, -- The originator of the trap 245 | collectorName, 246 | eventType, -- Event type: noSignal(4) 247 | eventName, -- "no_signal" 248 | eventSeverity, -- clear(0) indicate signal have returned 249 | multicastDest, 250 | streamerSource, 251 | inputKey, -- The collectors input[key] 252 | inputShortloc, 253 | inputSwitch 254 | -- Perhaps use: 255 | -- sysLocation from SNMPv2-MIB, and use input[key][location]? 256 | -- sysName which is normally FQDN/hostname 257 | } 258 | STATUS current 259 | DESCRIPTION 260 | "This trap should be generated when the collector detects 261 | no_signal for a given stream. The trap is generated both 262 | when the signal disappears (eventType==noSignal(4)) and when 263 | the signal starts transmitting again (eventType==okSignal(8))." 264 | -- 1.3.6.1.4.1.26124.43.2.2.2 265 | ::= { collectorTraps 2 } 266 | 267 | -- Future: 268 | -- detectNewStream NOTIFICATION-TYPE 269 | 270 | END 271 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # 2 | # Notice: The VERSION is defined/controlled by AC_INIT 3 | # 4 | AC_INIT([iptv-analyzer], [0.9.4], [netoptimizer@brouer.com], [], [www.iptv-analyzer.org]) 5 | #AC_INIT(package, version, [bug-report], [tarname], [url]) 6 | # 7 | AC_CONFIG_HEADERS([config.h]) 8 | AC_CONFIG_MACRO_DIR([m4]) 9 | #AC_PROG_INSTALL 10 | #AM_INIT_AUTOMAKE([1.10.2 -Wall foreign subdir-objects]) 11 | AM_INIT_AUTOMAKE([foreign]) 12 | #AC_DISABLE_STATIC 13 | #AC_PROG_LIBTOOL 14 | 15 | # Checks for programs. 16 | AC_PROG_AWK 17 | AC_PROG_CC 18 | AM_PROG_CC_C_O 19 | AC_PROG_CPP 20 | AC_PROG_INSTALL 21 | AC_PROG_LN_S 22 | AC_PROG_MAKE_SET 23 | AC_PROG_RANLIB 24 | 25 | # Checks for header files. 26 | AC_CHECK_HEADERS([netdb.h stddef.h stdlib.h string.h]) 27 | 28 | # Checks for typedefs, structures, and compiler characteristics. 29 | AC_HEADER_STDBOOL 30 | AC_C_INLINE 31 | AC_TYPE_INT16_T 32 | AC_TYPE_INT32_T 33 | AC_TYPE_INT8_T 34 | AC_TYPE_SIZE_T 35 | AC_TYPE_UINT16_T 36 | AC_TYPE_UINT32_T 37 | AC_TYPE_UINT64_T 38 | AC_TYPE_UINT8_T 39 | 40 | # Checks for library functions. 41 | AC_CHECK_FUNCS([strchr]) 42 | 43 | AC_ARG_WITH([kbuild], 44 | AS_HELP_STRING([--with-kbuild=PATH], 45 | [Path to kernel build directory [[/lib/modules/CURRENT/build]]]), 46 | [kbuilddir="$withval"], 47 | [kbuilddir="/lib/modules/$(uname -r)/build"]) 48 | # 49 | # check for --without-kbuild 50 | # 51 | if [[ "$kbuilddir" == no ]]; then 52 | kbuilddir=""; 53 | fi 54 | 55 | # Check iptables binary exist and set variable IPTABLES to the program 56 | # with full path. Add "sbin" dirs to the search PATH as the user 57 | # running configure might not have this in their path. 58 | # 59 | SBIN_PATH=$PATH:/usr/local/sbin:/usr/sbin:/sbin: 60 | AC_PATH_PROG([IPTABLES], [iptables], [no], $SBIN_PATH) 61 | if [[ "$IPTABLES" = "no" ]]; then 62 | AC_MSG_ERROR([Cannot find iptables binary - You need to install iptables]) 63 | fi 64 | # Extract iptables base dirname / path 65 | IPTABLES_BINDIR=`AS_DIRNAME([$IPTABLES])` 66 | IPTABLES_DIR=`AS_DIRNAME([$IPTABLES_BINDIR])` 67 | 68 | # Find the include path for xtables.h 69 | IPTABLES_INC="${IPTABLES_DIR}/include" 70 | if [[ "$IPTABLES_DIR" = "/" ]]; then 71 | IPTABLES_INC="/usr/include" 72 | fi 73 | 74 | # xtlibdir the Xtables extensions path 75 | AC_ARG_WITH([xtlibdir], 76 | AS_HELP_STRING([--with-xtlibdir=PATH], 77 | [Path where to install Xtables extensions. 78 | Configure will try to auto DETECT, based on iptables binary path]), 79 | [xtlibdir="$withval"], 80 | [xtlibdir="detect"]) 81 | # [xtlibdir='${libexecdir}/xtables']) 82 | 83 | # Default setting of xtlibdir is "detect". 84 | # ---------------------------------------- 85 | # Try to detect xtlibdir based upon the location of the detected 86 | # iptables binary IPTABLES_DIR, unless --with-xtlibdir is used. 87 | # 88 | # Problem statement: 89 | # 90 | # The userspace lib dir is differs on different Linux distributions, 91 | # further more the distributions choose not to follow the source code 92 | # (and just alter the exec_prefix to libexec). Instead they force 93 | # the usage of /lib/xtables/ or /lib64/xtables/, via 94 | # 95 | # ./configure --with-xtlibdir=/lib/xtables 96 | # 97 | # We also support this configure switch, but if none given, try to 98 | # detect the xtlibdir used by iptables on the system. 99 | # 100 | xtlibdir_check=""; 101 | if test "${with_xtlibdir+set}" = set; then 102 | # No detection and expantion needed, if specified via --with-xtlibdir 103 | xtlibdir_check=$xtlibdir; 104 | elif test "${xtlibdir}" = "detect"; then 105 | 106 | AC_MSG_NOTICE([Entering auto-detect mode for Xtables extensions dir xtlibdir]) 107 | test_dir="" 108 | if [[ "${IPTABLES_DIR}" = "/" -o "${IPTABLES_DIR}" = "/usr" ]]; then 109 | 110 | # First detect via pkt-config 111 | xtlibdir="$(pkg-config --variable=xtlibdir xtables)" 112 | if [[ -z "$xtlibdir" ]]; then 113 | # Try a more low level detect method 114 | # Need to test for /lib{,64}/xtables/ and /usr/lib{,64}/xtables/ 115 | if [[ -d /lib/xtables ]]; then 116 | xtlibdir=/lib/xtables 117 | elif [[ -d /lib64/xtables ]]; then 118 | xtlibdir=/lib64/xtables 119 | elif [[ -d /usr/lib/xtables ]]; then 120 | xtlibdir=/usr/lib/xtables 121 | elif [[ -d /usr/lib64/xtables ]]; then 122 | xtlibdir=/usr/lib64/xtables 123 | else 124 | xtlibdir="" 125 | test_dir="" 126 | fi 127 | fi 128 | if [[ -n "$xtlibdir" ]]; then 129 | AC_MSG_NOTICE([ - Found xtlibdir: $xtlibdir]) 130 | test_dir=$xtlibdir 131 | fi 132 | else 133 | test_dir=${IPTABLES_DIR}/libexec/xtables 134 | if [[ -d "$test_dir" ]]; then 135 | AC_MSG_NOTICE([ - Found xtlibdir: $test_dir]) 136 | xtlibdir=$test_dir 137 | 138 | # Handle the case where IPTABLES_DIR = $prefix and 139 | # then pass it along with a quoted '$prefix' 140 | myprefix=${prefix} 141 | if [[ "${myprefix}" = "NONE" ]]; then 142 | myprefix=$ac_default_prefix 143 | fi 144 | if [[ "${myprefix}" = "${IPTABLES_DIR}" ]]; then 145 | xtlibdir='${prefix}/libexec/xtables' 146 | fi 147 | # Handle if exec_prefix is set 148 | if [[ "${exec_prefix}" != "NONE" ]]; then 149 | if [[ "${exec_prefix}" = "${IPTABLES_DIR}" ]]; then 150 | xtlibdir='${libexecdir}/xtables' 151 | fi 152 | fi 153 | else 154 | xtlibdir="$(pkg-config --variable=xtlibdir xtables)" 155 | test_dir=$xtlibdir 156 | fi 157 | fi 158 | # Double check the $test_dir 159 | xtlibdir_check=$test_dir 160 | else 161 | # Skip/avoid check if "detect" is not in use/specified 162 | xtlibdir_check="/" 163 | fi 164 | # 165 | # Test directory exist 166 | if [[ ! -d "$xtlibdir_check" ]]; then 167 | AC_MSG_ERROR([Xtables extensions dir ${xtlibdir_check} does not exist], [2]) 168 | fi 169 | 170 | # This adds a dependency to pkg-config, alternativly we could define 171 | # libxtables_CFLAGS and libxtables_LIBS to avoid the need to call 172 | # pkg-config. 173 | PKG_CHECK_MODULES([libxtables], [xtables >= 1.4.3]) 174 | 175 | AC_CHECK_HEADERS([linux/netfilter/x_tables.h], [], 176 | [AC_MSG_ERROR([You need to have linux/netfilter/x_tables.h, see INSTALL file for details])]) 177 | 178 | regular_CFLAGS="-D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 \ 179 | -D_REENTRANT -Wall -Waggregate-return -Wmissing-declarations \ 180 | -Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \ 181 | -Winline -pipe -DXTABLES_LIBDIR=\\\"\${xtlibdir}\\\" \ 182 | "; 183 | 184 | # 185 | # Check kernel version 186 | # 187 | if grep -q "CentOS release 5\." /etc/redhat-release 2>/dev/null || 188 | grep -q "Red Hat Enterprise Linux Server release 5" /etc/redhat-release 2>/dev/null; then 189 | # 190 | # Well, just a warning. Maybe the admin updated the kernel. 191 | echo "WARNING: This distribution's shipped kernel is not supported."; 192 | fi; 193 | krel="$(make -sC ${kbuilddir} kernelrelease)"; 194 | krel="${krel%%-*}"; 195 | kmajor="${krel%%.*}"; 196 | krel="${krel#*.}"; 197 | kminor="${krel%%.*}"; 198 | krel="${krel#*.}"; 199 | kmicro="${krel%%.*}"; 200 | if test "$kmicro" = "$krel"; then 201 | kstable=0; 202 | else 203 | kstable="${krel#*.}"; 204 | if test -z "$kstable"; then 205 | kstable=0; 206 | fi; 207 | fi; 208 | echo "Found kernel version $kmajor.$kminor.$kmicro.$kstable in $kbuilddir"; 209 | if test "$kmajor" -gt 2 -o "$kminor" -gt 6 -o "$kmicro" -gt 38; then 210 | echo "WARNING: You are trying a newer kernel. Results may vary. :-)"; 211 | elif test \( "$kmajor" -lt 2 -o "$kminor" -lt 6 -o "$kmicro" -lt 28 \); then 212 | echo "ERROR: Your kernel version is not supported"; 213 | echo " Minimum kernel versions > 2.6.27"; 214 | echo " (as we need the RCU locking API)."; 215 | exit 1; 216 | fi; 217 | 218 | # Need perl for the iptv-collector daemon 219 | AC_PATH_PROG([PERL], [perl], [no]) 220 | if [[ "$PERL" = "no" ]]; then 221 | AC_MSG_ERROR([Cannot find perl - You need to install perl to run collector]) 222 | fi 223 | 224 | AC_SUBST([regular_CFLAGS]) 225 | AC_SUBST([kbuilddir]) 226 | AC_SUBST([xtlibdir]) 227 | AC_SUBST([IPTABLES]) 228 | AC_SUBST([IPTABLES_DIR]) 229 | AC_SUBST([IPTABLES_INC]) 230 | AC_CONFIG_FILES([ 231 | Makefile 232 | iptables-module/Makefile 233 | collector/lib/IPTV/Analyzer/Version.pm 234 | collector/etc/version 235 | ]) 236 | 237 | # 238 | # $prefix expand for perl collector 239 | PERL_PREFIX=""; 240 | if [[ "${prefix}" != "NONE" ]]; then 241 | PERL_PREFIX="PREFIX=$prefix" 242 | fi 243 | 244 | AC_OUTPUT 245 | 246 | # Configure the iptv-collector 247 | # 248 | # Which is based upon Perl MakeMaker system. To integrate, call the 249 | # MakeMaker system, from the configure script. 250 | # 251 | # Remember to pass $prefix parameter along 252 | echo " Processing Collector Daemon" 253 | echo " -- Calling Perl MakeMaker system" 254 | cd collector 255 | ${PERL} Makefile.PL $PERL_PREFIX 256 | cd .. 257 | 258 | echo "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-" 259 | echo " Visit the project homepage at:" 260 | echo " http://$PACKAGE_URL" 261 | echo "" 262 | echo " Create bugreports, patches and insults via:" 263 | echo " https://github.com/netoptimizer/IPTV-Analyzer/issues" 264 | echo " or email:" 265 | echo " $PACKAGE_BUGREPORT" 266 | echo "" 267 | echo "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-" 268 | -------------------------------------------------------------------------------- /collector/lib/IPTV/Analyzer/snmptrap.pm: -------------------------------------------------------------------------------- 1 | package IPTV::Analyzer::snmptrap; 2 | 3 | =head1 NAME 4 | 5 | IPTV::Analyzer::snmptrap - module for sending SNMP traps events 6 | 7 | =head1 SYNOPSIS 8 | 9 | The IPTV::Analyzer::snmptrap module is a helper module for sending 10 | SNMP traps, when e.g. events like no-signal occurs. 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | 17 | use Net::SNMP qw(:ALL); 18 | 19 | #use Config::File; 20 | use Data::Dumper; 21 | 22 | use Log::Log4perl qw(get_logger :levels); 23 | our $logger = get_logger(__PACKAGE__); 24 | 25 | use IPTV::Analyzer::Config; 26 | use IPTV::Analyzer::Version; 27 | 28 | BEGIN { 29 | use Exporter (); 30 | our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); 31 | 32 | # Package version 33 | $VERSION = $IPTV::Analyzer::Version::VERSION; 34 | 35 | @ISA = qw(Exporter); 36 | @EXPORT = qw( 37 | open_snmp_session 38 | close_snmp_session 39 | send_snmptrap 40 | ); 41 | 42 | } 43 | 44 | # Global vars 45 | our $snmp_session = undef; 46 | 47 | sub open_snmp_session($$) 48 | { 49 | my $host = shift; 50 | my $community = shift; 51 | my $version = 2; 52 | 53 | ($snmp_session, my $error) = 54 | Net::SNMP->session( 55 | -hostname => $host, 56 | -version => $version, 57 | -community => $community, 58 | -port => SNMP_TRAP_PORT 59 | ); 60 | 61 | if (!defined($snmp_session)) { 62 | $logger->fatal("Cannot open a SNMP session: $error"); 63 | return 0; 64 | #exit 1 65 | } 66 | return 1; 67 | } 68 | 69 | sub close_snmp_session() 70 | { 71 | if (defined $snmp_session) { 72 | $snmp_session->close(); 73 | } 74 | $snmp_session = undef; 75 | } 76 | 77 | # taken from snmptrapd-sendtest.pl / Net-SNMPTrapd-0.04 78 | #our %opt; 79 | #$opt{version} = $opt{version} || 2; 80 | #$opt{community} = $opt{community} || 'public'; 81 | #$opt{integer} = $opt{integer} || 1; 82 | #$opt{string} = $opt{string} || 'String'; 83 | #$opt{oid} = $opt{oid} || '1.2.3.4.5.6.7.8.9'; 84 | #$opt{ip} = $opt{ip} || '10.10.10.1'; 85 | #$opt{counter32} = $opt{counter32} || 32323232; 86 | #$opt{gauge32} = $opt{gauge32} || 42424242; 87 | #$opt{timeticks} = $opt{timeticks} || time(); 88 | #$opt{opaque} = $opt{opaque} || 'opaque data'; 89 | #$opt{inform} = $opt{inform} || 0; 90 | 91 | 92 | # Which config file input[] source does this trap concern 93 | sub construct_input_identification($) 94 | { 95 | my $inputKey = shift; 96 | my $cfg = get_config(); 97 | 98 | # Specific config for input this trap concerns 99 | my $inputShortloc = $cfg->get_input_value($inputKey, "shortloc"); 100 | my $inputSwitch = $cfg->get_input_value($inputKey, "switch"); 101 | 102 | my @array = ( 103 | # inputKey, -- The collectors input[key] 104 | '1.3.6.1.4.1.26124.43.2.1.3.1', OCTET_STRING, $inputKey, 105 | # inputShortloc input[key][shortloc] 106 | '1.3.6.1.4.1.26124.43.2.1.3.2', OCTET_STRING, $inputShortloc, 107 | # inputSwitch input[key][switch] 108 | '1.3.6.1.4.1.26124.43.2.1.3.3', OCTET_STRING, $inputSwitch 109 | ); 110 | return @array; 111 | } 112 | 113 | sub construct_version() 114 | { 115 | my $version = $IPTV::Analyzer::Version::VERSION || "0.0.0"; 116 | my @array = ( 117 | # collectorsVersion 118 | '1.3.6.1.4.1.26124.43.2.1.4.1', OCTET_STRING, $version, 119 | ); 120 | return @array; 121 | } 122 | 123 | 124 | sub construct_probe_identification() 125 | { 126 | my $cfg = get_config(); 127 | my $probe_ip = $cfg->get_probe_ip; 128 | my $probe_name = $cfg->get_probe_name; 129 | my @array = ( 130 | # CollectorId 131 | '1.3.6.1.4.1.26124.43.1.1', IPADDRESS, $probe_ip, 132 | # CollectorName 133 | '1.3.6.1.4.1.26124.43.1.2', OCTET_STRING, $probe_name, 134 | ); 135 | return @array; 136 | } 137 | 138 | sub construct_trap_oid($$) 139 | { 140 | my $trap_oid = shift; 141 | my $timeticks = shift || 0; 142 | 143 | # The first two variable-bindings fields in the snmpV2-trap are 144 | # specified by SNMPv2 and should be: 145 | # sysUpTime.0 - ('1.3.6.1.2.1.1.3.0', TIMETICKS, $timeticks) 146 | # snmpTrapOID.0 - ('1.3.6.1.6.3.1.1.4.1.0', OBJECT_IDENTIFIER, $oid) 147 | 148 | my @array = ( 149 | '1.3.6.1.2.1.1.3.0', TIMETICKS, $timeticks, 150 | '1.3.6.1.6.3.1.1.4.1.0', OBJECT_IDENTIFIER, $trap_oid, 151 | ); 152 | return @array; 153 | } 154 | 155 | sub construct_event($$$) 156 | { 157 | my $event_type = shift; 158 | my $event_name = shift; 159 | my $event_severity = shift; 160 | my @array = ( 161 | # eventType 162 | '1.3.6.1.4.1.26124.43.2.1.1.1', INTEGER, $event_type, 163 | # eventName 164 | '1.3.6.1.4.1.26124.43.2.1.1.2', OCTET_STRING, $event_name, 165 | # eventSeverity (indicate clear/ok signal) 166 | '1.3.6.1.4.1.26124.43.2.1.1.3', INTEGER, $event_severity, 167 | ); 168 | return @array; 169 | } 170 | 171 | sub construct_event_via_name($$) 172 | { 173 | my $event_name = shift; 174 | my $severity_name = shift; 175 | my $event_type = lookup_event($event_name); 176 | my $event_severity = lookup_severity($severity_name); 177 | my @array = construct_event($event_type, $event_name, $event_severity); 178 | return @array; 179 | } 180 | 181 | our $global_trap_oids = { 182 | 'no_signal' => '1.3.6.1.4.1.26124.43.2.2.2', # MIB: streamNoSignal 183 | 'generalEvent' => '1.3.6.1.4.1.26124.43.2.2.1', 184 | 'unknown' => '1.3.6.1.4.1.26124.43', 185 | }; 186 | 187 | sub lookup_trap($) 188 | { 189 | my $trapname = shift; 190 | my $oid; 191 | my $result=0; 192 | if (exists $global_trap_oids->{"$trapname"} ) { 193 | $oid = $global_trap_oids->{"$trapname"}; 194 | $result = 1; 195 | } else { 196 | $oid = $global_trap_oids->{"unknown"}; 197 | } 198 | #return ($oid, $result); 199 | return $oid; 200 | } 201 | 202 | sub send_snmptrap($$$$$$) 203 | { 204 | my $event_name = shift; 205 | my $severity_name = shift; 206 | my $inputkey = shift; 207 | my $timeticks = shift || 0; 208 | my $multicast = shift; 209 | my $src_ip = shift; 210 | 211 | my $cfg = get_config(); 212 | 213 | if ( !defined($snmp_session) ) { 214 | $logger->fatal("Cannot send SNMPtrap, no snmp session opened!"); 215 | return 0; 216 | } 217 | 218 | # The first two required variable-bindings fields in snmpV2-trap 219 | my $trap = lookup_trap($event_name); 220 | my @trap_oid = construct_trap_oid($trap, $timeticks); 221 | 222 | # The collectornVersion info 223 | my @version = construct_version(); 224 | 225 | # The event type 226 | my @event_oids = construct_event_via_name($event_name, $severity_name); 227 | 228 | # General identification of the probe 229 | my @ident_probe = construct_probe_identification(); 230 | 231 | # Specific identification of config input[key] 232 | my @ident_input = construct_input_identification($inputkey); 233 | 234 | # TODO: Stream identification 235 | 236 | my @oid_array = 237 | ( 238 | @trap_oid, 239 | @version, 240 | @ident_probe, 241 | @event_oids, 242 | 243 | # multicastDest 244 | '1.3.6.1.4.1.26124.43.2.1.2.1', IPADDRESS, $multicast, 245 | # streamerSource 246 | '1.3.6.1.4.1.26124.43.2.1.2.2', IPADDRESS, $src_ip, 247 | 248 | @ident_input 249 | ); 250 | 251 | 252 | # Check oid_array for undef's as snmpv2_trap cannot handle these 253 | for my $n (2 .. $#oid_array) 254 | { 255 | if (! defined $oid_array[$n] ) { 256 | my $theoid = $oid_array[$n-2]; 257 | my $log="OID $theoid contains undef (SNMP trap will fail)"; 258 | $logger->error($log); 259 | } 260 | } 261 | 262 | my $result = $snmp_session->snmpv2_trap( 263 | -varbindlist => \@oid_array 264 | ); 265 | 266 | if(!$result) { 267 | #print "snmperror, input oid_array:" . Dumper(\@oid_array) . "\n"; 268 | $logger->error("Could not send SNMP trap"); 269 | } 270 | 271 | return $result; 272 | } 273 | 274 | # Example of datatypes 275 | # '1.3.6.1.4.1.26124.42.3.3', INTEGER, $opt{integer}, 276 | # '1.3.6.1.4.1.26124.42.3.4', OCTET_STRING, $opt{string}, 277 | # '1.3.6.1.4.1.26124.42.3.5', OBJECT_IDENTIFIER, $opt{oid}, 278 | # '1.3.6.1.4.1.26124.42.3.6', IPADDRESS, $opt{ip}, 279 | # '1.3.6.1.4.1.26124.42.3.7', COUNTER32, $opt{counter32}, 280 | # '1.3.6.1.4.1.26124.42.3.8', GAUGE32, $opt{gauge32}, 281 | # '1.3.6.1.4.1.26124.42.3.9', TIMETICKS, $opt{timeticks}, 282 | # '1.3.6.1.4.1.26124.42.3.10', OPAQUE, $opt{opaque} 283 | 284 | 285 | 1; 286 | __END__ 287 | # Below is documentation for the module. 288 | # One way of reading it: "perldoc IPTV/Analyzer/mpeg2ts.pm" 289 | 290 | =head1 DESCRIPTION 291 | 292 | The MIB is defined in snmp/mibs/IPTV-ANALYZER-MIB.txt 293 | 294 | =head1 DEPENDENCIES 295 | 296 | This module uses the module L for sending snmptraps. 297 | 298 | =head1 AUTHOR 299 | 300 | Jesper Dangaard Brouer, Enetoptimizer@brouer.com. 301 | 302 | =head1 COPYRIGHT AND LICENSE 303 | 304 | Copyright (C) 2009-2013 by Jesper Dangaard Brouer. 305 | 306 | This file is licensed under the terms of the GNU General Public 307 | License 2.0. or newer. See . 308 | 309 | =cut 310 | -------------------------------------------------------------------------------- /collector/bin/iptv-collector: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w -I../lib/ 2 | # 3 | # Perl IPTV-Analyzer collector daemon 4 | # 5 | =pod 6 | 7 | =head1 NAME 8 | 9 | iptv-collector -- IPTV-Analyzer Collector daemon 10 | 11 | =head1 DESCRIPTION 12 | 13 | The IPTV-Analyzer Collector daemon, parse and process data from 14 | the iptables module mpeg2ts, via reading files under: 15 | 16 | /proc/net/xt_mpeg2ts/rule_XXX 17 | 18 | The daemon constantly compares the proc file input, to detect changes 19 | for a given multicast channel, e.g drop detection. Changes are 20 | recorded and stored into a MySQL database. 21 | 22 | =head1 CONFIG 23 | 24 | The config file: /etc/iptv-analyzer/collector.conf 25 | 26 | Sample/documentation of the config format see: 27 | 28 | man IPTV::Analyzer::mpeg2ts 29 | 30 | =cut 31 | 32 | use strict; 33 | use warnings; 34 | 35 | use POSIX qw(setsid); 36 | use sigtrap qw(handler sig_handler normal-signals); 37 | 38 | use Data::Dumper; 39 | 40 | use Proc::Daemon; # libproc-daemon-perl 41 | use Proc::PID::File; # libproc-pid-file-perl 42 | 43 | use File::Basename; 44 | use IPTV::Analyzer::mpeg2ts; 45 | use IPTV::Analyzer::snmptrap; 46 | 47 | # Trick to catch stderr messages from e.g. DB calls into Log4perl 48 | use IPTV::Analyzer::Log4perlTrapper; 49 | 50 | # Logger hack for changing the logging level runtime 51 | use Log::Log4perl qw(get_logger :levels); 52 | our $logger = get_logger("IPTV::Analyzer::mpeg2ts"); 53 | #$logger->level($DEBUG); 54 | 55 | # Config options 56 | use IPTV::Analyzer::Config; 57 | our $cfg = get_config(); 58 | our $interval = $cfg->{'collector_interval'} || 10; 59 | our $heartbeat_interval_ticks = $cfg->{'collector_heartbeat_ticks'} || 60; 60 | our $snmptraphost = $cfg->{'snmptraphost'} || "87.72.129.222"; 61 | our $snmpcommunity = $cfg->{'snmpcommunity'} || "public"; 62 | 63 | # State variables 64 | our $ticks = 0; 65 | our $run = 1; 66 | 67 | our $restart = 1; 68 | 69 | # We need to TIE STDERR to log4perl, because the DB can report strange 70 | # things. 71 | # 72 | # http://search.cpan.org/~mschilli/Log-Log4perl-1.26/lib/Log/Log4perl/FAQ.pm#Some_module_prints_messages_to_STDERR._How_can_I_funnel_them_to_Log::Log4perl? 73 | # 74 | 75 | 76 | sub daemon() 77 | { 78 | my $pid; 79 | my $rundir = "/var/run/"; 80 | my $pidfile = $rundir . basename($0) . ".pid"; 81 | if ( not -w $pidfile) { 82 | my $log = "Cannot write to PID file $pidfile"; 83 | $logger->logcroak($log); 84 | } 85 | 86 | # open STDIN, '< /dev/null' or die "Can't read /dev/null: $!"; 87 | # open STDOUT, '> /dev/null' or die "Can't write /dev/null: $!"; 88 | # open STDERR, '> /dev/null' or die "Can't write /dev/null: $!"; 89 | 90 | # man fork(2): On success, the PID of the child process is 91 | # returned in the parent, and 0 is returned in the child. 92 | # 93 | defined($pid = fork) or $logger->logcroak("Can't fork: $!"); 94 | if ($pid) { 95 | open(PID, "> $pidfile") 96 | or $logger->logcroak("Can't open pidfile $pidfile: $!"); 97 | print PID $pid; 98 | close(PID); 99 | exit 0; 100 | } 101 | 102 | setsid() or die "Can't start a new session: $!"; 103 | chdir '/' or die "Can't chdir to /: $!"; 104 | umask 0 or die "Can't umask: $!"; 105 | 106 | open STDIN, '< /dev/null' or die "Can't read /dev/null: $!"; 107 | open STDOUT, '> /dev/null' or die "Can't write /dev/null: $!"; 108 | # open STDERR, '> /dev/null' or die "Can't write /dev/null: $!"; 109 | } 110 | 111 | 112 | sub daemon2() 113 | { 114 | # Problem: perm to /var/run/$0.pid 115 | my $rundir = "/var/run/"; 116 | my $pidfile = $rundir . basename($0) . ".pid"; 117 | if ( not -w $pidfile) { 118 | my $log = "Cannot write to PID file $pidfile"; 119 | $logger->logcroak($log); 120 | } 121 | 122 | # Daemonize 123 | Proc::Daemon::Init(); 124 | # After this line we cannot output to stderr/stdout 125 | 126 | # Check if already running 127 | my $otherpid = Proc::PID::File->running(); 128 | if ($otherpid > 0) { 129 | my $log = "Daemon is already running as PID:[$otherpid]"; 130 | 131 | if ($restart) { 132 | my $cnt = kill("TERM", $otherpid); 133 | if ($cnt < 1) { 134 | $log .= " - could not kill current exit self!"; 135 | $logger->error($log); 136 | exit(2); 137 | } 138 | $log .= " - KILL and restart!"; 139 | $logger->warn($log); 140 | 141 | sleep(1); 142 | # Write new PID into pid file 143 | if (Proc::PID::File->running()) { 144 | $logger->error("Tried killing $otherpid but failed! - exit"); 145 | exit(3); 146 | } 147 | 148 | } else { 149 | $log .= " - exiting buy buy!"; 150 | $logger->warn($log); 151 | exit(1); 152 | } 153 | } else { 154 | $logger->info("Starting IPTV::Analyzer collector daemon2"); 155 | } 156 | } 157 | 158 | 159 | sub daemon3() 160 | { 161 | # Problem: perm to /var/run/$0.pid 162 | my $rundir = "/var/run/"; 163 | my $pidfile = $rundir . basename($0) . ".pid"; 164 | if ( -e $pidfile && (not -w $pidfile)) { 165 | my $log = "Cannot write to PID file $pidfile"; 166 | $logger->logcroak($log); 167 | } 168 | 169 | open(STDIN, "+>/dev/null"); 170 | open(STDOUT, "+>&STDIN"); 171 | # open(STDERR, "+>&STDIN"); 172 | # tie *STDERR, "Trapper"; 173 | 174 | 175 | # open STDIN, '< /dev/null' or die "Can't read /dev/null: $!"; 176 | # open STDOUT, '> /dev/null' or die "Can't write /dev/null: $!"; 177 | # open STDERR, '> /dev/null' or die "Can't write /dev/null: $!"; 178 | 179 | 180 | # Daemonize 181 | # man fork(2): On success, the PID of the child process is 182 | # returned in the parent, and 0 is returned in the child. 183 | # 184 | my $pid; 185 | defined($pid = fork) or $logger->logcroak("Can't fork: $!"); 186 | if ($pid) { 187 | exit(0); 188 | } 189 | 190 | setsid() or die "Can't start a new session: $!"; 191 | chdir '/' or die "Can't chdir to /: $!"; 192 | umask 0 or die "Can't umask: $!"; 193 | 194 | # open STDIN, '< /dev/null' or die "Can't read /dev/null: $!"; 195 | # open STDOUT, '> /dev/null' or die "Can't write /dev/null: $!"; 196 | # open STDERR, '> /dev/null' or die "Can't write /dev/null: $!"; 197 | 198 | # Check if already running 199 | my $otherpid = Proc::PID::File->running(); 200 | if ($otherpid > 0) { 201 | my $log = "Daemon is already running as PID:[$otherpid]"; 202 | 203 | if ($restart) { 204 | my $cnt = kill("TERM", $otherpid); 205 | if ($cnt < 1) { 206 | 207 | # Trying to kill again after a delay 208 | my $delay = 5; # = $interval (?) 209 | my $logtry = "could not kill, trying again in $delay sec"; 210 | $logger->error("$log - $logtry"); 211 | sleep($delay); 212 | # Test if the process is running before killing it again 213 | # (kill with signal 0 is just a test) 214 | if ($otherpid != $$ && kill(0, $otherpid)) { 215 | $cnt = kill("TERM", $otherpid); 216 | if ($cnt < 1) { 217 | # Giving up 218 | $log .= " - could NOT kill current daemon, exit self!"; 219 | $logger->fatal($log); 220 | exit(2); 221 | } 222 | } 223 | } 224 | my $delaydb = 3; 225 | $log .= " - KILL and restart (in $delaydb sec)"; 226 | $logger->warn($log); 227 | 228 | # Wait a bit for the other process to finish db sessions 229 | sleep($delaydb); 230 | # Write new PID into pid file 231 | if (Proc::PID::File->running()) { 232 | $logger->fatal("Tried killing pid:$otherpid but failed! - exit"); 233 | exit(3); 234 | } 235 | 236 | } else { 237 | $log .= " - exiting buy buy!"; 238 | $logger->warn($log); 239 | exit(1); 240 | } 241 | } else { 242 | my $ver = $IPTV::Analyzer::mpeg2ts::VERSION; 243 | $logger->info("Started IPTV::Analyzer collector daemon (child) (version:$ver)"); 244 | } 245 | } 246 | 247 | 248 | sub sig_handler { 249 | $logger->info("Going to exit due to kill signal"); 250 | heartbeat_update(); 251 | close_daemon_sessions(); 252 | close_stream_sessions(); 253 | close_snmp_session(); 254 | $run = 0; 255 | } 256 | 257 | #http://search.cpan.org/~mschilli/Log-Log4perl-1.26/lib/Log/Log4perl/FAQ.pm#My_program_already_uses_warn%28%29_and_die%28%29._How_can_I_switch_to_Log4perl? 258 | 259 | #$SIG{__WARN__} = sub { 260 | # local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1; 261 | ### WARN @_; 262 | #### $logger->warn(@_) if defined @_; 263 | # warn(@_) if defined @_; 264 | #}; 265 | 266 | $SIG{__DIE__} = sub { 267 | if($^S) { 268 | # We're in an eval {} and don't want log 269 | # this message but catch it later 270 | return; 271 | } 272 | $Log::Log4perl::caller_depth++; 273 | # LOGDIE @_; 274 | $logger->logdie(@_); 275 | }; 276 | 277 | 278 | #tie *STDERR, "Trapper"; 279 | #tie *STDERR, "IPTV::Analyzer::mpeg2ts"; 280 | 281 | ## Testing outputs 282 | #print "TEST1:stdout\n"; 283 | #print STDERR "TEST1:stderr\n"; 284 | #warn "TEST1:warn()\n"; 285 | 286 | my $ver = $IPTV::Analyzer::mpeg2ts::VERSION; 287 | $logger->info("Starting IPTV::Analyzer collector daemon (parent) (version:$ver)"); 288 | 289 | 290 | # Fork of this process 291 | #daemon(); 292 | #daemon2(); 293 | daemon3(); 294 | 295 | ## Testing output after the child process has been forked 296 | #print "TEST2:stdout\n"; 297 | #print STDERR "TEST2:stderr\n"; 298 | #warn "TEST2:warn()\n"; 299 | 300 | # IPTV::Analyzer::mpeg2ts 301 | db_connect(); 302 | 303 | open_snmp_session($snmptraphost, $snmpcommunity); 304 | 305 | # The loop 306 | while ($run) { 307 | 308 | # Process all inputs according to the config file 309 | process_inputs(); 310 | 311 | sleep($interval); 312 | $ticks++; 313 | 314 | if (($ticks % $heartbeat_interval_ticks) == 0) { 315 | heartbeat_state_active(); 316 | heartbeat_update(); 317 | } else { 318 | heartbeat_state_clear(); 319 | } 320 | } 321 | 322 | $logger->info("Stopping IPTV::Analyzer collector daemon (version:$ver)"); 323 | 324 | db_commit(); 325 | db_disconnect(); 326 | close_snmp_session(); 327 | exit; 328 | 329 | __END__ 330 | =pod 331 | 332 | =head1 COPYRIGHT AND LICENSE 333 | 334 | Copyright (C) 2009-2011+ by Jesper Dangaard Brouer, ComX Networks A/S. 335 | 336 | This file is licensed under the terms of the GNU General Public 337 | License 2.0. or newer. See . 338 | 339 | =cut 340 | -------------------------------------------------------------------------------- /webfrontend/www/js/epoch_v202_en/gettingstarted.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Getting Started with Epoch 8 | 9 | 10 | 11 | 12 | 59 | 60 | 61 |

62 |

Epoch DHTML JavaScript Calendar – Getting Started

63 |

For those who want to dive in, check out the minimum code required page to see the bare minimum you need to run Epoch.

64 |

Introduction

65 |

Events and Holidays via EpochEpoch is a fast, low-footprint, high-powered JavaScript Calendar for use on websites which demand high interactivity from their users. Epoch can 66 | be used as a datepicker or inline calendar. Epoch is fully standards-compliant, with minimal allotments for obsolete browsers like IE4/Netscape 4, and commonly-used browsers that deviate from web standards. 67 | As a result, Epoch will work for virtually all users with JavaScript enabled – the dominant browsers out there all support it! More importantly, because of its 68 | standards-compliance, Epoch is future-compatible (confirmed with Internet Explorer 7!) and its use of many of the object-oriented features of JavaScript makes it easier for the developer to maintain 69 | and extend.

70 | 71 |

Thanks for your interest in Epoch!
72 | Nick Baicoianu
73 | Epoch Lead Developer

74 | 75 |

License

76 |

Epoch is free for non-commercial use. If you are using Epoch for a commercial website (e-commerce, corporate site, intranet, etc), 77 | please purchase a license. – you'll get free upgrades, full access to the Epoch 78 | source code, as well as email support in setting up your calendar. We think you'll find it's worth the time you saved implementing Epoch versus other solutions.

79 | 80 |

Who Epoch is For

81 |

Website Owners

82 |

Epoch's ease of deployment and maintenance makes it a prime choice for any website. Hoteliers and events websites will find the holiday and blackout date 83 | features of Epoch incredibly useful in detailing their offerings. Bloggers can use Epoch to navigate their archives with ease. 84 | High-traffic sites will appreciate the low bandwidth footprint (24k for all files - compressed) of Epoch when upgrading their user's experiences.

85 |

Never has it been so simple and inexpensive to upgrade your site than with Epoch.

86 | 87 |

Developers

88 |

Epoch is designed to be as easy for the developer to deploy as it is for the user to interact with it. Integrating Epoch into your web pages doesn't require any hacks or complicated code 89 | because of its object-oriented structure. If you aren't familiar with object-oriented programming in JavaScript (or any other language, for that matter), 90 | don't fret! Using Epoch's features requires no special knowledge outside of basic JavaScript and HTML. Everything you need to get Epoch running on your 91 | web pages is included in this file and the example file.

92 | 93 |

Your Site's Visitors

94 |

Before you add Epoch to your web pages, consider your audience: are they advanced users who use the latest technology, or less-experienced 95 | users who may not be using "modern browsers" (i.e. Internet Explorer 4 and below, Netscape 4 and below, IE for Mac)? You can easily check your users' 96 | browsers if your webserver has a web stats program installed – if you use cPanel you have several options available. If you think your users 97 | are the less-experienced type, we recommend you use Epoch only for non-critical applications or in popup mode—that way if your users' browsers 98 | can't run Epoch—or have switched off JavaScript entirely, they won't be left out in the cold.

99 | 100 |

The Basics

101 |

More information on setting up Epoch is available at the Epoch Setup Page.

102 |

For those who want to dive in, check out the minimum code required page to see the bare minimum you need to run Epoch.

103 |

1. Uploading Epoch

104 |

Before you can do anything, you must upload the Epoch files (via FTP or SCP/SFTP) to a web-accessible directory on your webserver. For example, if you site's root directory is /var/www/mysite/ (UNIX/Linux), or 105 | C:\inetpub\mysite\ (for Windows), you can place them in any subdirectory, i.e. /var/www/mysite/javascript/, or C:\inetpub\mysite\css\.

106 |

2. Linking to the Epoch files

107 |

Before you can begin using Epoch in your page, you need to include the files necessary for Epoch to function and look good. There are two files: 108 | a JavaScript file (epoch_classes.js), and a css file(epoch_styles.css).

109 |

The links to both files should go in the <HEAD> section of any webpage that uses Epoch – make sure they point to the correct subdirectory!

110 |
<html>
111 | <head>
112 | <title>My Cool Calendar Page</title>
113 | <link rel="stylesheet" type="text/css" href="[path_to_css]/epoch_styles.css" /> <!--Epoch's styles-->
114 | <script type="text/javascript" src="[path_to_javascript]/epoch_classes.js"></script> <!--Epoch's Code-->
115 | </head>
116 |

3. Initializing and using Epoch in your Page

117 |

Once you've added the 2 files in the page head, you can start using Epoch! To add an Epoch calendar to you page you will first need to create an 118 | empty HTML element in the page body to physically hold the calendar; <div>, <input>, <td>, <p> are all acceptable elements. 119 | The containing element should be placed at the exact location you want Epoch to appear.

120 |

Syntax

121 |

When you've created the container element, you need to initialize an Epoch calendar for each one. You can do this using a line of code like this:

122 |
calendar1 = new Epoch(name,mode,container,multiselect);
123 |

The Initialization Variables

124 |
125 |
name
126 |
A unique name for the calendar – this will become the base for the calendar's id attribute.
127 |
mode
128 |
The mode the calendar will run in – can be flat or popup
129 |
container
130 |
A JavaScript resource pointing to the HTML element that will hold the calendar. Usually accessed through the JavaScript method document.getElementById('container_id').
131 |
multiselect
132 |
Whether you can select multiple dates – true for yes, false for no.
133 |
134 |

This code must lie inside a <SCRIPT> tag.

135 |

Where to put it

136 |

You have a few options on where to put the Epoch initializations – all will work the same.

137 |
In the Page <HEAD>
138 |
<head>
139 | <title>My Cool Calendar Page</title>
140 | <link rel="stylesheet" type="text/css" href="/path_to_css/epoch_styles.css" />   <!--Epoch's styles-->
141 | <script type="text/javascript" src="/path_to_javascript/epoch_classes.js"></script>   <!--Epoch's Code-->
142 | <script type="text/javascript">
143 | var calendar1, calendar2, calendar3; /*must be declared in global scope*/
144 | /*put the calendar initializations in the window's onload() method*/
145 | window.onload = function() {
146 | 	calendar1 = new Epoch('cal1','flat',document.getElementById('calendar1_container'),false);
147 | 	calendar2 = new Epoch('cal2','popup',document.getElementById('calendar2_container'),false);
148 | 	calendar3 = new Epoch('cal3','flat',document.getElementById('calendar3_container'),true);
149 | 	...
150 | };
151 | </script>
152 | </head>
153 |
In a separate .js file (using the code in blue above)
154 |
<head>
155 | <title>My Cool Calendar Page</title>
156 | <link rel="stylesheet" type="text/css" href="[path_to_css]epoch_styles.css" />   <!--Epoch's styles-->
157 | <script type="text/javascript" src="[path_to_javascript]epoch_classes.js"></script>   <!--Epoch's Code-->
158 | <script type="text/javascript" src="[path_to_javascript]/my_declarations.js"></script>   <!--Epoch initializations-->
159 | </head>
160 |
Inside the containing HTML element (flat mode only)—not recommended
161 |
<body>
162 | <h1>My cool calendars</h1>
163 | <p>Check out our great events calendars!</p>
164 | <div id="calendar1_container">
165 |   <script type="text/javascript">new Epoch('cal1','flat',document.getElementById('calendar1_container'),false);</script>
166 | </div>
167 | <!--No "appropriate" way of associating a calendar with an inline script-->
168 | <input id="calendar2_container" type="text" name="date" />
169 | <div id="calendar3_container">
170 |   <script type="text/javascript">new Epoch('cal3','flat',document.getElementById('calendar3_container'),true);</script>
171 | </div>
172 | </body>
173 | 174 |

Summary

175 |

In short, integrating Epoch into your webpage requires only 4 basic steps:

176 |
    177 |
  1. Copy epoch_classes.js and epoch_styles.css to your web server's JavaScript and CSS directories.
  2. 178 |
  3. Include the Epoch files in your page's <head>—be sure to include the correct path to the files!
  4. 179 |
  5. Add containing elements in your page's body where you want Epoch to appear. Each element should have a unique id attribute.
  6. 180 |
  7. Initialize a calendar for each containing element.
  8. 181 |
182 |

For a demo that shows the minimum required code to use Epoch, click here.

183 |

Tools

184 |

The code in epoch_classes.js is heavily commented to make modification easier – developers for more highly-trafficked sites 185 | should compress their JavaScript and CSS files using MeanFreePath's Free JavaScript and CSS code compactor. 186 | The code compactor reduces your files sizes by up to 50% by removing comments and unecessary whitespace from your files. The results are faster loading times 187 | for your users, and JavaScript code that is extremely difficult for humans to read. We recommend you keep a non-compacted version of epoch_classes.js 188 | for your own reference.

189 | 190 |

Support and Other Resources

191 |

We at MeanFreePath want your development experience with Epoch to go as smoothly as possible. Here are some resources to 192 | help make installation and customization easier:

193 | 200 | 201 | 205 |
206 | 207 | -------------------------------------------------------------------------------- /collector/lib/IPTV/Analyzer/Config.pm: -------------------------------------------------------------------------------- 1 | package IPTV::Analyzer::Config; 2 | 3 | =head1 NAME 4 | 5 | IPTV::Analyzer::Config - module for reading config file "collector.conf" 6 | 7 | =head1 SYNOPSIS 8 | 9 | The IPTV::Analyzer::Config module is a helper module for reading 10 | config file "collector.conf". Notice, also loads log4perl.conf 11 | config file. 12 | 13 | =cut 14 | 15 | use strict; 16 | use warnings; 17 | use Carp; 18 | 19 | use Config::File; 20 | use Data::Dumper; 21 | 22 | use File::Basename; # dirname() for config file loading 23 | 24 | BEGIN { 25 | use Exporter (); 26 | our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); 27 | 28 | # Package version 29 | $VERSION = $IPTV::Analyzer::Version::VERSION; 30 | 31 | @ISA = qw(Exporter); 32 | @EXPORT = qw( 33 | get_config 34 | lookup_event 35 | lookup_severity 36 | ); 37 | } 38 | 39 | INIT { 40 | # Auto load the config, when this module is loaded. 41 | # This is done due to the mpeg2ts module, and how it access its $cfg 42 | new(); 43 | } 44 | 45 | # Global var 46 | our $singleton_cfg = undef; 47 | 48 | # Simply define event_types here 49 | # TODO: Replace with select from Database. 50 | # TODO: Move into object 51 | our $global_event_types = { 52 | 'unknown' => 0, # Unknown event name lookup 53 | 'new_stream' => 1, # New stream detected 54 | 'drop' => 2, # Drops detected, both skips and discon 55 | 'no_signal' => 4, # Stream have stopped transmitting data 56 | # 'ok_signal', => 8, # (NOT USED, see severity instead) 57 | 'ttl_change' => 16, # Indicate TTL changed 58 | 'transition' => 32, # The event_state changed since last poll 59 | 'heartbeat' => 64, # Heartbeat event to monitor status 60 | 'invalid' => 128, # Some invalid event situation arose 61 | }; 62 | 63 | # severity levels taken from ZenOSS 64 | # http://community.zenoss.org/docs/DOC-4766#d0e6170 65 | # Number Name Color in ZenOSS 66 | # 0 Clear Green 67 | # 1 Debug Grey 68 | # 2 Info Blue 69 | # 3 Warning Yellow 70 | # 4 Error Orange 71 | # 5 Critical Red 72 | # 73 | our $global_severity_levels = { 74 | 'clear' => 0, # Clears the eventType 75 | 'debug' => 1, 76 | 'info' => 2, 77 | 'warning' => 3, 78 | 'error' => 4, 79 | 'critical' => 5 80 | }; 81 | 82 | ### 83 | # Logging system 84 | # 85 | use Log::Log4perl qw(get_logger :levels); 86 | our $logger = get_logger(__PACKAGE__); 87 | 88 | # Load log4perl config. 89 | # 90 | # Notice need to load config file for logging system before loading 91 | # the collector.conf. 92 | # 93 | # TODO: Change default/fallback config, to log to /var/log, as the 94 | # iptv-collector daemon breaks off stdout and stderr. 95 | sub load_log4perl_config() 96 | { 97 | # - fallback logger config 98 | my $logger_fallback_config = qq( 99 | log4perl.logger = INFO, Screen1 100 | # log4perl.logger = DEBUG, Screen1 101 | log4perl.appender.Screen1 = Log::Log4perl::Appender::ScreenColoredLevels 102 | # log4perl.appender.Screen1.stderr = 1 103 | log4perl.appender.Screen1.stderr = 0 104 | log4perl.appender.Screen1.Threshold = DEBUG 105 | # log4perl.appender.Screen1.Threshold = WARN 106 | log4perl.appender.Screen1.layout = Log::Log4perl::Layout::SimpleLayout 107 | ); 108 | # - load logger config if available, try different locations 109 | my $log4perl_conf="log4perl.conf"; 110 | my $basedir = dirname($0); 111 | if ( -e "${basedir}/${log4perl_conf}" ) { 112 | # Try local script dir 113 | Log::Log4perl->init("${basedir}/${log4perl_conf}"); 114 | } elsif ( -e "/etc/iptv-analyzer/${log4perl_conf}" ) { 115 | # Try /etc/iptv-analyzer 116 | Log::Log4perl->init("/etc/iptv-analyzer/${log4perl_conf}"); 117 | } elsif ( -e "/etc/${log4perl_conf}" ) { 118 | # Try /etc/ 119 | Log::Log4perl->init("/etc/${log4perl_conf}"); 120 | } else { 121 | # Use fallback config 122 | my $log = "Could NOT find the log4perl config file:[$log4perl_conf]"; 123 | $log.= " - you will not see daemon log info!!!"; 124 | Log::Log4perl->init(\$logger_fallback_config); 125 | $logger->error($log); 126 | } 127 | } 128 | 129 | 130 | ### 131 | # Helper functions 132 | # 133 | sub lookup_event($) 134 | { 135 | my $event_name = shift; 136 | my $event_type; 137 | if (exists $global_event_types->{"$event_name"} ) { 138 | $event_type = $global_event_types->{"$event_name"}; 139 | } else { 140 | $logger->logcarp("Eventname $event_name, is unknown"); 141 | $event_type = $global_event_types->{"unknown"}; 142 | } 143 | return $event_type; 144 | } 145 | 146 | sub lookup_severity($) 147 | { 148 | my $name = shift; 149 | my $type; 150 | if (exists $global_severity_levels->{"$name"} ) { 151 | $type = $global_severity_levels->{"$name"}; 152 | } else { 153 | $logger->logcarp("Severity name $name, is unknown, using level debug"); 154 | $type = $global_severity_levels>{"debug"}; 155 | } 156 | return $type; 157 | } 158 | 159 | ### 160 | # Object related methods 161 | # 162 | 163 | # Create a new config object. 164 | sub new() 165 | { 166 | my $invocant = shift; 167 | my $class = ref($invocant) || $invocant; 168 | 169 | # Need to load the log4perl.conf first 170 | load_log4perl_config(); 171 | 172 | # Implement singleton object, to avoid loading config several times 173 | if (defined($singleton_cfg)) { 174 | $logger->logcarp("Config already loaded, returning current config." 175 | . " Use get_config() instead"); 176 | return $singleton_cfg; 177 | } 178 | else { 179 | $logger->debug("Loading config."); 180 | } 181 | 182 | # Loading config 183 | my $self = load_config(); 184 | 185 | #my $self = {}; 186 | if ($class) { 187 | bless($self, $class); 188 | } else { 189 | bless($self); 190 | } 191 | 192 | $self->validate_config(); # needs logging module loaded 193 | 194 | # Store singelton object 195 | $singleton_cfg = $self; 196 | 197 | return $self; 198 | } 199 | 200 | ### 201 | # Config file processing 202 | # 203 | 204 | sub load_config { 205 | my $cfgfile = shift || "collector.conf"; 206 | my $cfgdir; 207 | my $cfgdir_default = "/etc/iptv-analyzer"; 208 | my $cfgdir_local = dirname($0); # Current script dir 209 | if ( -e "${cfgdir_default}/${cfgfile}" ) { 210 | $cfgdir = $cfgdir_default; 211 | } elsif ( -e "${cfgdir_local}/${cfgfile}" ) { 212 | # Use the config from the dir of the script 213 | $cfgdir = $cfgdir_local; 214 | my $l = "Cannot find config file ${cfgdir_default}/${cfgfile} - "; 215 | $l.= "instead use config from dir of script: ${cfgdir}/${cfgfile}"; 216 | $logger->warn($l); 217 | } else { 218 | my $txtcfg="${cfgdir_default}/${cfgfile}"; 219 | my $log1="Cannot find the config file: ${txtcfg}"; 220 | my $log2="Please look at example config: etc/${cfgfile}.sample"; 221 | $logger->fatal($log1); 222 | $logger->fatal($log2); 223 | # Also print to STDERR, a misconfigured log4perl might hide the above 224 | print STDERR "\n - FATAL-ERROR - $log1\n\n"; 225 | exit(2); 226 | } 227 | 228 | my $cfg = Config::File::read_config_file("${cfgdir}/${cfgfile}"); 229 | return $cfg; 230 | } 231 | 232 | sub get_config { 233 | my $self = shift; 234 | if (defined($self)) { 235 | return $self; 236 | } else { 237 | if (defined($singleton_cfg)) { 238 | return $singleton_cfg; 239 | } else { 240 | # Create and load config if it didn't exist 241 | my $log="Force loading config, you didn't load it earlier"; 242 | carp("${log}, its needed"); # log4perl not init'ed yet 243 | return new(); 244 | } 245 | } 246 | } 247 | 248 | # Example of the $cfg hash being validated 249 | #my $cfg_example = { 250 | # 'probe_ip' => '10.10.10.42', 251 | # 'probe_name' => 'tvprobe42', 252 | # 'input' => { 253 | # 'rule_eth42' => { 254 | # 'procfile' => '/proc/net/xt_mpeg2ts/rule_test', 255 | # 'shortloc' => 'alb', 256 | # 'switch' => 'albcs35', 257 | # 'name' => 'Main signal', 258 | # 'distance' => '1', 259 | # 'location' => 'Albertslund Serverrum B', 260 | # 'address' => 'Herstedvang 8, 2620 Albertslund', 261 | # 'switchport' => 'e0/0', 262 | # 'switchtype' => 'Foundry FLS', 263 | # 'input_ip' => '192.168.16.42', 264 | # 'input_dev' => 'eth42', 265 | # 'hidden' => 'no', 266 | # } 267 | # }, 268 | # 'dbhost' => 'tvprobe004.comx.dk', 269 | # 'dbpass' => 'thepassword', 270 | # 'dbname' => 'tvprobedb', 271 | # 'dbuser' => 'tvprobe' 272 | #}; 273 | 274 | sub validate_config { 275 | my $cfg = shift; 276 | my $res = 1; 277 | 278 | # Check main identification values are defined 279 | if ( not exists $cfg->{'probe_ip'} ) { 280 | $logger->logcroak("Config missing 'probe_ip'"); 281 | } 282 | if ( not exists $cfg->{'probe_name'} ) { 283 | $logger->logcroak("Config missing 'probe_name'"); 284 | } 285 | 286 | # Check config defines what files to read 287 | if ( not exists $cfg->{'input'} ) { 288 | $logger->logcroak("Config does not contain any input files"); 289 | } 290 | 291 | # Check input contain elements (it must not be a simple value) 292 | my $inputs = $cfg->{'input'}; 293 | if (ref($inputs) ne 'HASH') { 294 | my $log = "Config is invalid, 'input' must not be a simple value"; 295 | $logger->logcroak($log); 296 | # Must die, else Perl dies when using deref of hash later 297 | } 298 | 299 | # Walk through each input hash value 300 | foreach my $key (keys %{$inputs}) { 301 | my $input = $inputs->{$key}; 302 | my $log = "input[$key]"; 303 | 304 | if (ref($input) ne 'HASH') { 305 | my $l = "Config is invalid, 'input[$key]' is a simple value"; 306 | $l .= " (OLD STYLE CONFIG?)"; 307 | $logger->logcroak($l); 308 | # Must die, else Perl dies when using deref of hash later 309 | } 310 | 311 | # Main input file 312 | if (exists $input->{'procfile'}) { 313 | my $file = $input->{'procfile'}; 314 | if ( ! -e "$file" ) { 315 | $logger->fatal("${log}[procfile]=$file file does not exists!"); 316 | $res = 0; 317 | } 318 | $logger->debug("${log}[procfile]=$file file exists"); 319 | } else { 320 | $logger->logcroak("Option ${log}[procfile] is required!"); 321 | } 322 | 323 | # Required identifiers 324 | if (not exists $input->{'shortloc'}) { 325 | my $l = "Config ${log}[shortloc] is a required identifier!"; 326 | $logger->logcroak($l); 327 | } 328 | if (not exists $input->{'switch'}) { 329 | my $l = "Config ${log}[switch] is a required identifier!"; 330 | $logger->logcroak($l); 331 | } 332 | 333 | # Construct a "description" if not configured 334 | if (not exists $input->{'description'}) { 335 | my $desc = ""; 336 | $desc .= $cfg->{'probe_name'}; 337 | if (exists $input->{'location'}) { 338 | $desc .= " at " . $input->{'location'}; 339 | } else { 340 | $desc .= " at " . $input->{'shortloc'}; 341 | } 342 | $input->{'description'} = $desc; 343 | } 344 | 345 | # Optional settings, initialize if empty 346 | $input->{'distance'} = -1 if (not exists $input->{'distance'}); 347 | $input->{'location'} = "Unknown" if (not exists $input->{'location'}); 348 | $input->{'address'} = "Unknown" if (not exists $input->{'address'}); 349 | $input->{'switchport'} = "" if (not exists $input->{'switchport'}); 350 | $input->{'switchtype'} = "" if (not exists $input->{'switchtype'}); 351 | $input->{'input_ip'} = "" if (not exists $input->{'input_ip'}); 352 | $input->{'input_dev'} = "" if (not exists $input->{'input_dev'}); 353 | 354 | # Handling the option "hidden" 355 | my $hidden_default = "no"; 356 | if (exists $input->{'hidden'}) { 357 | if (($input->{'hidden'} ne "yes") && ($input->{'hidden'} ne "no")) { 358 | my $l = "Invalid 'hidden' config value:["; 359 | $l .= $input->{'hidden'} . "] using $hidden_default instead"; 360 | $logger->error($l); 361 | $input->{'hidden'} = $hidden_default; 362 | } 363 | } else { 364 | # Default setting if not set 365 | $input->{'hidden'} = $hidden_default; 366 | } 367 | } 368 | 369 | # DB config checks 370 | $logger->logcroak("Config need 'dbhost'") if (not exists $cfg->{'dbhost'}); 371 | $logger->logcroak("Config need 'dbpass'") if (not exists $cfg->{'dbpass'}); 372 | $logger->logcroak("Config need 'dbname'") if (not exists $cfg->{'dbname'}); 373 | $logger->logcroak("Config need 'dbuser'") if (not exists $cfg->{'dbuser'}); 374 | 375 | return $res; 376 | } 377 | 378 | ### 379 | # Accessing elements 380 | # 381 | # Due to historical reasons, the mpeg2ts module simply access 382 | # elements directly, using the module as an hash. 383 | # 384 | # But for future usage, we want to be are bit more "clean" and 385 | # provide helper functions to access elements in the config "object". 386 | # 387 | sub get_probe_ip() 388 | { 389 | my $self = shift; 390 | my $value = $self->{'probe_ip'}; 391 | return $value; 392 | } 393 | 394 | sub get_probe_name() 395 | { 396 | my $self = shift; 397 | my $value = $self->{'probe_name'}; 398 | return $value; 399 | } 400 | 401 | # Part of $cfg 402 | #my $cfg_example2 = { 403 | # 'probe_ip' => '10.10.10.42', 404 | # 'probe_name' => 'tvprobe42', 405 | # 'input' => { 406 | # 'inputKey' => { 407 | # 'valueKey' => 'albcs35', 408 | # 'procfile' => '/proc/net/xt_mpeg2ts/rule_test', 409 | # 'shortloc' => 'alb', 410 | # 411 | sub get_input_value() 412 | { 413 | my $self = shift; 414 | my $inputKey = shift; 415 | my $valueKey = shift; 416 | 417 | my $value=""; 418 | if (exists $self->{'input'}->{"$inputKey"}) { 419 | if (exists $self->{'input'}->{"$inputKey"}->{"$valueKey"}) { 420 | $value=$self->{'input'}->{"$inputKey"}->{"$valueKey"}; 421 | } else { 422 | my $txtcfg = "input[$inputKey][$valueKey]"; 423 | $logger->warn("No config setting available for $txtcfg"); 424 | } 425 | } else { 426 | #my $txtcfg = "input[$inputKey] cannot read $valueKey"; 427 | #$logger->warn("No config for $txtcfg"); 428 | } 429 | return $value; 430 | } 431 | 432 | 433 | 434 | 1; 435 | __END__ 436 | # Below is documentation for the module. 437 | # One way of reading it: "perldoc IPTV/Analyzer/Config.pm" 438 | 439 | =head1 DESCRIPTION 440 | 441 | Config file being read and validated: /etc/iptv-analyzer/collector.conf 442 | 443 | =head1 DEPENDENCIES 444 | 445 | This module uses the module L for parsing input files. 446 | 447 | =head1 AUTHOR 448 | 449 | Jesper Dangaard Brouer, Enetoptimizer@brouer.comE. 450 | 451 | =head1 COPYRIGHT AND LICENSE 452 | 453 | Copyright (C) 2009-2013 by Jesper Dangaard Brouer 454 | 455 | This file is licensed under the terms of the GNU General Public 456 | License 2.0. or newer. See . 457 | 458 | =cut 459 | --------------------------------------------------------------------------------