pktanon-master/0000755000000000000000000000000012701423271012516 5ustar rootrootpktanon-master/bootstrap0000755000000000000000000000003312701423271014455 0ustar rootroot#! /bin/sh autoreconf -i pktanon-master/profiles/0000755000000000000000000000000012701423271014341 5ustar rootrootpktanon-master/profiles/profile.xml0000644000000000000000000000603712701423271016531 0ustar rootroot pktanon-master/profiles/profile-identity.xml0000644000000000000000000000554112701423271020357 0ustar rootroot pktanon-master/profiles/sample.pcap0000644000000000000000000000174612701423271016477 0ustar rootrootò&Rc**'FUǦǦ2R#NN#xфN`:zN#8@zNh9S JJPVPV0?E<@@! уmr h9S> <<PV0?PVE,7; g6Om`;Bh9S> 66PVPV0?E(@@4 уmg6OPrS?$bbPVPV0?ET@@J ,)S+$ !"#$%&'()*+,-./01234567SbbPV0?PVETǭ ,)S+$ !"#$%&'()*+,-./01234567C"S?`33 pZ.x`'W lpIM-SEARCH * HTTP/1.1 Host:[FF02::C]:1900 ST:urn:Microsoft Windows Peer Name Resolution Protocol: V4:IPV6:LinkLocal Man:"ssdp:discover" MX:3 D"S+[ vv33@U9:n@:BU9:Mb@H,)2@U9:@' :* pktanon-master/tests/0000755000000000000000000000000012701423271013660 5ustar rootrootpktanon-master/tests/transformations/0000755000000000000000000000000012701423271017111 5ustar rootrootpktanon-master/tests/transformations/ip6/0000755000000000000000000000000012701423271017607 5ustar rootrootpktanon-master/tests/transformations/ip6/tcp_checksum.xml0000644000000000000000000000242312701423271023002 0ustar rootroot pktanon-master/tests/transformations/ip6/mix.xml0000644000000000000000000000161212701423271021126 0ustar rootroot pktanon-master/tests/transformations/ip6/udp_checksum.xml0000644000000000000000000000272212701423271023006 0ustar rootroot pktanon-master/tests/transformations/ip6/ip6.exp0000644000000000000000000000070212701423271021022 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "IPv6" if { ![exec_pktanon mix.xml ip6.pcap] } { break; } test_field "ipv6.version" 6 test_field "ipv6.class" 0x00000015 test_field "ipv6.flow" 0x00081818 test_field "ipv6.plen" 0 test_field "ipv6.nxt" 58 test_field "ipv6.hlim" 5 test_field "ipv6.src" "abab:abab:abab:abab:abab:abab:abab:abab" test_field "ipv6.dst" "cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd" pktanon-master/tests/transformations/ip6/iden.xml0000644000000000000000000000144712701423271021256 0ustar rootroot pktanon-master/tests/transformations/ip6/ip6_tcp.pcap0000644000000000000000000000272612701423271022027 0ustar rootrootòE"S 'FU@U9:`x5  * +RO{ъNS P[/S$ p[x/#c"K`MdfBkĒQ$-p^O^kCwyln]pD=N CN/T{5O'"rTLBV~ E5ہ$˫Xk lC2E%J bG#F ?:ca9P!Kivzy)b(D@T='ݲcVmmg^!GAkM^W}'6 sr>5GiH(F1,IodY=l !9Ũ¤Ir#S?ˇE,u@ LTKԸVR&#ڪcB?N2g>(vB^ak@,|wP-٥5vOޥԅC T2BUD6**u6:vƟ;\`vH pxcvзGxd : MLt\8x@ -i j>3=գ,]䥾w^o%Tt^5"0,xt #u.L5\Pn5藛df±=d[{pMuErH*{Smo`qҧks8Yy1IaRu^PsnCQo  ;³=0Cio]kRO隃&M}O pktanon-master/tests/transformations/ethernet/ethernet.pcap0000644000000000000000000000012212701423271023405 0ustar rootrootò&Rc**'FUǦ(2 pktanon-master/tests/transformations/ethernet/iden.xml0000644000000000000000000000067212701423271022375 0ustar rootroot pktanon-master/tests/transformations/icmp6/0000755000000000000000000000000012701423271020127 5ustar rootrootpktanon-master/tests/transformations/icmp6/icmp_na_iden.exp0000644000000000000000000000067012701423271023255 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "ICMP6-NA-IDEN" if { ![exec_pktanon iden_na.xml icmp6_na.pcap] } { break; } test_field "icmpv6.type" 136 test_field "icmpv6.code" 0 test_field "icmpv6.checksum" "0x78fd" test_checksum_r "icmpv6" test_field "icmpv6.nd.na.flag" "40:00:00:00" test_field "icmpv6.nd.na.target_address" "fe80::7ae7:d1ff:fe84:4e06" warning "what to do with RSO flags?" pktanon-master/tests/transformations/icmp6/icmp_ns_iden.exp0000644000000000000000000000063412701423271023277 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "ICMP6-NS-IDEN" if { ![exec_pktanon iden_ns.xml icmp6_ns.pcap] } { break; } test_field "icmpv6.type" 135 test_field "icmpv6.code" 0 test_field "icmpv6.checksum" "0xb0fa" test_checksum_r "icmpv6" test_field "icmpv6.reserved" "00:00:00:00" test_field "icmpv6.nd.ns.target_address" "2a00:1398:2:4005:102a:fc8b:6d76:bb6e" pktanon-master/tests/transformations/icmp6/icmp6_ns.pcap0000644000000000000000000000017612701423271022516 0ustar rootrootò2RNVV33vn0Hā:` :0Hā:vnv*@*mvn0Hā:pktanon-master/tests/transformations/icmp6/mix_ns.xml0000644000000000000000000000204312701423271022145 0ustar rootroot pktanon-master/tests/transformations/icmp6/icmp_ns.exp0000644000000000000000000000062512701423271022300 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "ICMP6-NS" if { ![exec_pktanon mix_ns.xml icmp6_ns.pcap] } { break; } test_field "icmpv6.type" 135 test_field "icmpv6.code" 0 test_field "icmpv6.checksum" "0xb14a" test_checksum_r "icmpv6" test_field "icmpv6.reserved" "00:00:00:00" test_field "icmpv6.nd.ns.target_address" "2a00:1398:2:4005:102a:fc8b:6d76:bb6e" pktanon-master/tests/transformations/icmp6/icmp_na.exp0000644000000000000000000000066212701423271022257 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "ICMP6-NA" if { ![exec_pktanon mix_na.xml icmp6_na.pcap] } { break; } test_field "icmpv6.type" 136 test_field "icmpv6.code" 0 test_field "icmpv6.checksum" "0x78fd" test_checksum_r "icmpv6" test_field "icmpv6.nd.na.flag" "40:00:00:00" test_field "icmpv6.nd.na.target_address" "fe80::7ae7:d1ff:fe84:4e06" warning "what to do with RSO flags?" pktanon-master/tests/transformations/icmp6/icmp6_na.pcap0000644000000000000000000000016612701423271022473 0ustar rootrootò2R#NN#xфN`:zN#8@zNpktanon-master/tests/transformations/icmp6/iden_ns.xml0000644000000000000000000000204412701423271022270 0ustar rootroot pktanon-master/tests/transformations/icmp6/mix_na.xml0000644000000000000000000000205012701423271022121 0ustar rootroot pktanon-master/tests/transformations/icmp6/iden_na.xml0000644000000000000000000000204412701423271022246 0ustar rootroot pktanon-master/tests/transformations/arp/0000755000000000000000000000000012701423271017673 5ustar rootrootpktanon-master/tests/transformations/arp/arp_iden.exp0000644000000000000000000000077112701423271022177 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "ARP-IDEN" if { ![exec_pktanon iden.xml arp.pcap] } { break; } test_field "arp.hw.type" 1 test_field "arp.proto.type" 0x0800 test_field "arp.hw.size" 6 test_field "arp.proto.size" 4 test_field "arp.opcode" 1 test_field "arp.src.hw_mac" "9c:c7:a6:d0:eb:f9" test_field "arp.src.proto_ipv4" "192.168.178.1" test_field "arp.dst.hw_mac" "00:00:00:00:00:00" test_field "arp.dst.proto_ipv4" "192.168.178.20" pktanon-master/tests/transformations/arp/mix.xml0000644000000000000000000000205712701423271021216 0ustar rootroot pktanon-master/tests/transformations/arp/arp.exp0000644000000000000000000000075312701423271021200 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "ARP" if { ![exec_pktanon mix.xml arp.pcap] } { break; } test_field "arp.hw.type" 1 test_field "arp.proto.type" 0x0800 test_field "arp.hw.size" 6 test_field "arp.proto.size" 4 test_field "arp.opcode" 3855 test_field "arp.src.hw_mac" "f7:28:b2:8a:32:13" test_field "arp.src.proto_ipv4" "192.168.0.0" test_field "arp.dst.hw_mac" "ff:ff:ff:ff:ff:ff" test_field "arp.dst.proto_ipv4" "192.168.10.10" pktanon-master/tests/transformations/arp/arp.pcap0000644000000000000000000000012212701423271021315 0ustar rootrootò&Rc**'FUǦǦpktanon-master/tests/transformations/arp/iden.xml0000644000000000000000000000170712701423271021341 0ustar rootroot pktanon-master/tests/transformations/icmp/0000755000000000000000000000000012701423271020041 5ustar rootrootpktanon-master/tests/transformations/icmp/icmp.exp0000644000000000000000000000065312701423271021513 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "ICMP" if { ![exec_pktanon mix.xml icmp_echo_repl.pcap] } { break; } test_field "icmp.type" 0 test_field "icmp.code" 171 test_field "icmp.checksum" "0x0359" # test_checksum "icmp" if { $verbose > 0 } { puts "\nThese 2 fields shoul be transformed by 'rest' anon" } test_field "icmp.ident" "65021,65021" test_field "icmp.seq" "65021" pktanon-master/tests/transformations/icmp/mix.xml0000644000000000000000000000222312701423271021357 0ustar rootroot pktanon-master/tests/transformations/icmp/icmp_echo_req.pcap0000644000000000000000000000021212701423271023476 0ustar rootrootòS?$bbPVPV0?ET@@J ,)S+$ !"#$%&'()*+,-./01234567pktanon-master/tests/transformations/icmp/iden.xml0000644000000000000000000000215612701423271021506 0ustar rootroot pktanon-master/tests/transformations/icmp/icmp_iden.exp0000644000000000000000000000067412701423271022515 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "ICMP-IDEN" if { ![exec_pktanon iden.xml icmp_echo_req.pcap] } { break; } test_field "icmp.type" 8 test_field "icmp.code" 0 test_field "icmp.checksum" 0xce41 vputs "\nThese 2 fields shoul be transformed by 'rest' anon" test_field "icmp.ident" "10685,48425" # expect ONE at the beginning of the line TODO: end of line test_field "icmp.seq" "^1" pktanon-master/tests/transformations/icmp/icmp_echo_repl.pcap0000644000000000000000000000021212701423271023651 0ustar rootrootòSbbPV0?PVETǭ ,)S+$ !"#$%&'()*+,-./01234567pktanon-master/tests/transformations/udp/0000755000000000000000000000000012701423271017701 5ustar rootrootpktanon-master/tests/transformations/udp/mix.xml0000644000000000000000000000215412701423271021222 0ustar rootroot pktanon-master/tests/transformations/udp/udp.exp0000644000000000000000000000045012701423271021206 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "UDP" if { ![exec_pktanon mix.xml udp.pcap] } { break; } test_field "udp.srcport" 43690 test_field "udp.dstport" 48059 test_field "udp.length" 8 test_field "udp.checksum" "0x19a4" test_checksum "udp" pktanon-master/tests/transformations/udp/udp_iden.exp0000644000000000000000000000045312701423271022210 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "UDP-IDEN" if { ![exec_pktanon iden.xml udp.pcap] } { break; } test_field "udp.srcport" 53 test_field "udp.dstport" 45469 test_field "udp.length" 8 test_field "udp.checksum" "0xce37" test_checksum "udp" pktanon-master/tests/transformations/udp/udp.pcap0000644000000000000000000000032212701423271021333 0ustar rootrootòi9S PV0?PVEš5Qwwwgooglecom Eh E Eg Ej Ei Ecpktanon-master/tests/transformations/udp/iden.xml0000644000000000000000000000211212701423271021336 0ustar rootroot pktanon-master/tests/transformations/ip/0000755000000000000000000000000012701423271017521 5ustar rootrootpktanon-master/tests/transformations/ip/ip.exp0000644000000000000000000000106012701423271020644 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "IP" if { ![exec_pktanon mix.xml ip.pcap] } { break; } test_field "ip.version" 4 test_field "ip.hdr_len" 20 test_field "ip.dsfield" 0 test_field "ip.len" 20 test_field "ip.id" 0x0101 test_field "ip.flags" 0x07 test_field "ip.frag_offset" 0 test_field "ip.ttl" 2 test_field "ip.proto" 6 test_field "ip.checksum" 0xd7e3 test_checksum "ip" test_field "ip.src" "240.240.240.240" test_field "ip.dst" "15.15.15.15" warning "TODO: flags are not transformed correctly" pktanon-master/tests/transformations/ip/ip_iden.exp0000644000000000000000000000100512701423271021642 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "IP-IDEN" if { ![exec_pktanon iden.xml ip.pcap] } { break; } test_field "ip.version" 4 test_field "ip.hdr_len" 20 test_field "ip.dsfield" 0 test_field "ip.len" 20 test_field "ip.id" 0x0edf test_field "ip.flags" 0x02 test_field "ip.frag_offset" 0 test_field "ip.ttl" 64 test_field "ip.proto" 6 test_field "ip.checksum" 0x1d49 test_checksum "ip" test_field "ip.src" "192.168.127.128" test_field "ip.dst" "173.194.32.209" pktanon-master/tests/transformations/ip/mix.xml0000644000000000000000000000203212701423271021035 0ustar rootroot pktanon-master/tests/transformations/ip/ip.pcap0000644000000000000000000000016212701423271020775 0ustar rootrootòh9S JJPVPV0?E<@@! уmr pktanon-master/tests/transformations/ip/iden.xml0000644000000000000000000000166112701423271021166 0ustar rootroot pktanon-master/tests/transformations/tcp/0000755000000000000000000000000012701423271017677 5ustar rootrootpktanon-master/tests/transformations/tcp/tcp_syn.pcap0000644000000000000000000000016212701423271022222 0ustar rootrootòh9S JJPVPV0?E<@@! уmr pktanon-master/tests/transformations/tcp/tcp.exp0000644000000000000000000000065312701423271021207 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "TCP" if { ![exec_pktanon mix.xml tcp.pcap] } { break; } test_field "tcp.srcport" 257 test_field "tcp.dstport" 514 test_field "tcp.seq" 50529027 test_field "tcp.ack" 67372036 test_field "tcp.hdr_len" 20 test_field "tcp.flags" 0x0018 test_field "tcp.window_size_value" 1285 test_field "tcp.checksum" 0x8afa test_checksum "tcp" pktanon-master/tests/transformations/tcp/tcp_ack.pcap0000644000000000000000000000013612701423271022150 0ustar rootrootòh9S> 66PVPV0?E(@@4 уmg6OPrpktanon-master/tests/transformations/tcp/mix.xml0000644000000000000000000000274312701423271021224 0ustar rootroot pktanon-master/tests/transformations/tcp/tcp_syn_ack.pcap0000644000000000000000000000014412701423271023040 0ustar rootrootòh9S> <<PV0?PVE,7; g6Om`;Bpktanon-master/tests/transformations/tcp/iden.xml0000644000000000000000000000257412701423271021350 0ustar rootroot pktanon-master/tests/transformations/tcp/tcp_iden.exp0000644000000000000000000000067112701423271022206 0ustar rootrootsource [file join [file dirname [info script]] ../../lib/transftestlib.exp] set test "TCP-IDEN" if { ![exec_pktanon iden.xml tcp.pcap] } { break; } test_field "tcp.srcport" 33777 test_field "tcp.dstport" 443 test_field "tcp.seq" 2439873758 test_field "tcp.ack" 1731612665 test_field "tcp.hdr_len" 20 test_field "tcp.flags" 0x0018 test_field "tcp.window_size_value" 29200 test_field "tcp.checksum" 0xdbd7 test_checksum "tcp" pktanon-master/tests/transformations/tcp/tcp.pcap0000644000000000000000000000073512701423271021337 0ustar rootrootòh9S@ PVPV0?E@@ уmg6OPrVzvS9hze@q23K$p|es)X2 : $(+°EJjTI d2H'0]s-ӼH 985 ED32 A/  www.google.com  #8ĉlGםV<*qFNV%P1#L++WhjjKQPJ|Bɐ9^'HZC쭸".yyk<<QO#Zţ!d^iqćA0K6nֈ'ux&\LI.Ow  Fא3tpktanon-master/tests/anonprimitives/0000755000000000000000000000000012701423271016727 5ustar rootrootpktanon-master/tests/anonprimitives/chainstest.exp0000644000000000000000000000205112701423271021610 0ustar rootrootset script_dir [file dirname [info script]] source [file join $script_dir "../lib/anontestlib.exp"] set test [info script] vputs "\n>>> AnonBroadcastHandler/AnonConstOverwrite ========================================\n" set params {"value" "0x2b"} set anons {"AnonBroadcastHandler" "AnonConstOverwrite"} anon_chain_test $params $anons "-----" "+++++" # # TODO set params {"value" "0x2b" "hex_output" "1"} anon_chain_test $params $anons "#ffffffffff" "ffffffffff" vputs "\n>>> AnonConstOverwrite/AnonShorten ========================================\n" set params {"value" "0x2b" "newlen" "3"} set anons {"AnonConstOverwrite" "AnonShorten"} anon_chain_test $params $anons "-----" "+++" set anons {"AnonShorten" "AnonConstOverwrite"} anon_chain_test $params $anons "-----" "+++" vputs "\n>>> several primitives ========================================\n" set params {"value" "0x2b" "newlen" "3"} set anons {"AnonBroadcastHandler" "AnonBroadcastHandler" "AnonBroadcastHandler" "AnonConstOverwrite" "AnonShorten"} anon_chain_test $params $anons "-----" "+++"pktanon-master/tests/anonprimitives/AnonTester.h0000644000000000000000000000135212701423271021163 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef ANONTESTER_H #define ANONTESTER_H // #include #include #include using namespace pktanon; class AnonTester { public: AnonTester(); ~AnonTester(); void addParam(std::string name, std::string value); void addAnon ( std::string anon_class ); void eval(std::string val); private: Params params; AnonPrimitive* anon; AnonPrimitive* last_anon; bool hex_output; }; #endif // ANONTESTER_H pktanon-master/tests/anonprimitives/basictest.exp0000644000000000000000000000523012701423271021426 0ustar rootrootset script_dir [file dirname [info script]] source [file join $script_dir "../lib/anontestlib.exp"] set test [info script] vputs "\n>>> AnonConstOverwrite ========================================\n" #0x3a - ascii value for ':' set params {"value" "0x3a"} anon_value_test "AnonConstOverwrite" $params "12345" ":::::" #0x2b - ascii value for '+' set params {"value" "0x2b"} anon_value_test "AnonConstOverwrite" $params "12345678" "++++++++" vputs "\n>>> AnonConstOverwriteRange ========================================\n" set params {"value" "0x3a" "range-begin" "0" "range-length" "9"} anon_value_test "AnonConstOverwriteRange" $params "123456789" ":::::::::" set params {"value" "0x3a" "range-begin" "5" "range-length" "9"} anon_value_test "AnonConstOverwriteRange" $params "123456789" "12345::::" set params {"value" "0x3a" "range-begin" "0" "range-length" "5"} anon_value_test "AnonConstOverwriteRange" $params "123456789" ":::::6789" set params {"value" "0x3a" "range-begin" "5" "range-length" "2"} anon_value_test "AnonConstOverwriteRange" $params "123456789" "12345::89" vputs "\n>>> AnonHashSha1/Sha256 ========================================\n" set params {"hex_output" "1"} # echo -n "value" | openssl dgst -sha1 # (stdin)= f32b67c7e2(6342af42efabc674d441dca0a281c) anon_value_test "AnonHashSha1" $params "value" "f32b67c7e2" set big_string "aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_aaaa_" # echo -n $big_string | openssl dgst -sha1 # (stdin)= 48420d4cbbf021d3517bb8aae0dca45be28d344a anon_value_test "AnonHashSha1" $params $big_string "48420d4cbbf021d3517bb8aae0dca45be28d344a48420d4cbbf021d3517bb8aae0dca45be28d344a" # TODO test wrap hash around on values not multiple of 20 # echo -n "value" | openssl dgst -sha256 # (stdin)= cd42404d52(ad55ccfa9aca4adc828aa5800ad9d385a0671fbcbf724118320619) anon_value_test "AnonHashSha256" $params "value" "cd42404d52" vputs "\n>>> AnonIdentity ========================================\n" set params {"hex_output" "0"} anon_value_test "AnonIdentity" $params "val" "val" anon_value_test "AnonIdentity" $params "bignonceval123" "bignonceval123" vputs "\n>>> AnonShorten ========================================\n" set params {"newlen" "3"} anon_value_test "AnonShorten" $params "value" "val" set params {"newlen" "0"} anon_value_test "AnonShorten" $params "value" "" # they seem to be impossible to test without whole pktanon, the transformations are not set # vputs "\n>>> AnonKnownProtocolHandler ========================================\n" # anon_value_test "AnonKnownProtocolHandler" $params "123" "123" # vputs "\n>>> AnonKnownEthertypeHandler ========================================\n" # anon_value_test "AnonKnownEthertypeHandler" $params "123" "123"pktanon-master/tests/anonprimitives/AnonTester.cpp0000644000000000000000000000742412701423271021524 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonTester.h" #include #include #include #include #include #include #include using namespace pktanon; void hex_dump ( char *buff, int len ) { for ( unsigned j = 0; j < len; j++ ) { printf ( "%02hhx", buff[j] ); // std::cout << std::hex << std::setw(2) << dst_buff[j]; } } int ch2dec ( char c ) { switch ( c ) { case 'a' : return 10; case 'b' : return 11; case 'c' : return 12; case 'd' : return 13; case 'e' : return 14; case 'f' : return 15; default: return atoi ( &c ); } } AnonTester::~AnonTester() { } AnonTester::AnonTester() : anon ( nullptr ), params(), hex_output ( 0 ) { } void AnonTester::addParam ( string name, string value ) { if ( name.compare ( "hex_output" ) == 0 ) { hex_output = ( value.at ( 0 ) == '1' ); } else { params.add_param ( std::move ( name ), std::move ( value ) ); } } void AnonTester::addAnon ( std::string anon_class ) { AnonPrimitive* prim = AnonFactory::create_anon ( anon_class, params ); if ( anon == nullptr ) { // TRACEV(anon_class); anon = prim; last_anon = prim; } else { // TRACEV(anon_class); last_anon->setNext ( prim ); last_anon = prim; } } void AnonTester::eval ( string val ) { auto src_buff = const_cast ( val.data() ); auto src_len = val.size(); if ( val.at ( 0 ) == '#' ) // the rest of the string should be interpreted as hex value { src_len = ( val.size()-1 ) /2; src_buff = new char[src_len]; for ( int j = 1; j < val.size(); j+=2 ) { int index = ( j-1 ) /2; int ch_val = ch2dec ( val.at ( j ) ) *16 + ch2dec ( val.at ( j+1 ) ); src_buff[index] = ( char ) ch_val; } // hex_dump ( src_buff, src_len ); } char dst_buff[src_len +1]; memset ( &dst_buff, '\0', src_len +1 ); auto result_len = anon->anonimyze ( src_buff, dst_buff, src_len ); // TRACE(result_len); if ( hex_output ) { std::cout << "result = {"; hex_dump ( dst_buff, result_len ); std::cout << std::dec << "}" << std::endl; } else { auto dst_str = std::string(dst_buff).substr(0,result_len); _plg_info ( "result = {" << std::hex << dst_str << "}" ); } } int main ( int argc, char* argv[] ) { AnonTester tester; // _plg_info ( "=== AnonTester ===" ); // _plg_info ( "a ; p ; v ;q" ); std::string command; std::string anon_class; std::string param_name; std::string param_value; std::string buffer; while ( 1 ) { _plg_info ( "enter command:" ); std::cin >> command; // TRACEV ( command ); switch ( command.at ( 0 ) ) { case 'a': std::cin >> anon_class; // TRACEV ( anon_class ); tester.addAnon ( anon_class ); break; case 'p': std::cin >> param_name; std::cin >> param_value; // TRACEV(param_name); // TRACEV(param_value); tester.addParam ( param_name, param_value ); break; case 'v': std::cin >> buffer; // TRACEV ( buffer ); tester.eval ( buffer ); break; case 'q': return 0; default: _plg_error ( "unnown command, use a, p, v, or q" ); } } // std::string anon; // std::string buffer; // // _plg_info ( "Enter Anon Class:" ); // std::cin >> anon; // // // _plg_info ( "Enter Anon Value:" ); // std::cin >> buffer; // // TRACEV ( anon ) // TRACEV ( buffer ) } pktanon-master/tests/pktanon-runtime/0000755000000000000000000000000012701423271017013 5ustar rootrootpktanon-master/tests/pktanon-runtime/libpcap_live__dump.exp0000644000000000000000000000374512701423271023357 0ustar rootroot# # test pktanon with live capture by libpcap # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] # test sudo working: ###################################################### set status [test_sudo_nopwd] if { $status != 0 } { unsupported "$test: cannot execute sudo without passwords (status = $status)" break } # spawn pktanon: ########################################################### spawn sudo $PKTANON -c $anon_profile -i lo -U $out_file expect { initialized { match_io_logs "libpcap" } eof {fail "$test pktanon failed to initialize"; break;} timeout {fail "$test pktanon failed to initialize"; break;} } set pktanon_spawn_id $spawn_id # send packets to lo using tcpreplay: ###################################### log_user 0 spawn sudo tcpreplay -i lo -t -l 1 $trace_file expect { -re "Successful packets:\[ \]*(\[0-9\]+)" { set pkts_sent [expr { $expect_out(1,string) }]; wait; } eof { set pkts_sent unknown unsupported "$test - failed to send packets with tcpreplay" } } # wait some time and terminate pktanon: ########################################## sleep 1 log_user $log_user_original set spawn_id $pktanon_spawn_id send \x03 expect { -re "processed packets:\[ \]*(\[0-9\]+)" { set pkts_processed [expr { $expect_out(1,string) }]; wait; } eof {fail "$test pktanon failed"} timeout {fail "$test pktanon failed"} } # count number of frames in result: ########################################## set pkts_rcvd [count_frames $out_file] # test that pktanon received all the packets: ########################################## if { $verbose > 0 } { puts "\n\nnumber of pkts sent = $pkts_sent" puts "number of pkts rcved = $pkts_rcvd" } if { $pkts_sent == $pkts_rcvd} {pass $test} { fail "$test: expected $pkts_sent packets, actual $pkts_rcvd packets" } # compare some statistics in trace.pcap and out.pcap -> ensure that fields are indeed copied compare_stats $trace_file $out_filepktanon-master/tests/pktanon-runtime/profile.xml0000644000000000000000000000554212701423271021203 0ustar rootroot pktanon-master/tests/pktanon-runtime/libpcap_dump__inject.exp0000644000000000000000000000224112701423271023662 0ustar rootrootset script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] ### file - libpcap-inct ############################################ # start tshark spawn -noecho sudo tshark -i lo -q -z io,stat,0 expect "Loopback" # doesn't work without it sleep 2 set tshark_spawn_id $spawn_id # start pktanon spawn sudo $PKTANON -c $anon_profile -n lo $trace_file expect { -re "processed packets:\[ \]*(\[0-9\]+)" { set pkts_sent [expr { $expect_out(1,string) }]; wait; } eof {fail "$test pktanon failed to initialize"; break;} timeout {fail "$test pktanon failed to initialize"; break;} } set spawn_id $tshark_spawn_id # doesn't work without it sleep 2 send \x03 send \x03 expect eof set buff $expect_out(buffer) set found [regexp -line -lineanchor {^\|\s0\.000[ <>0-9.]+\|\s*(\d+)} $buff match pkts_captured ] if { $found != 1} { fail $test } set pkts_in_trace_file [count_frames $trace_file] if { $pkts_sent != $pkts_in_trace_file } { fail $test "pktanon sent less files that there are in $trace_file"} if { $pkts_captured != $pkts_in_trace_file } { fail $test "less packets were captured on lo than in $trace_file"} pktanon-master/tests/pktanon-runtime/libpcap_live_filter.exp.todo0000644000000000000000000000321712701423271024476 0ustar rootrootset test "test libpcap live input" puts "warning: this test requires root privileges" source lib/tshark_utils.exp log_user 0 if { $verbose >= 0 } { log_user 1 } if { $verbose > 0 } { puts "================== $test =================="} # test sudo working: ###################################################### try { set results [exec sudo -nv ] set status 0 } trap CHILDSTATUS {results options} { set status [lindex [dict get $options -errorcode] 2] } if {$status != 0} {fail "$test: cannot execute sudo pktanon"} # spawn pktanon: ########################################################### spawn sudo ../../src/pktanon -i lo -e "not arp" ../out.pcap expect { initialized { } eof {fail $test; exit 1} timeout { fail $test } } set spawn_pktanon $spawn_id # send packets to lo using tcpreplay: ###################################### spawn sudo tcpreplay -i lo -t -l 1 trace.pcap expect { -re "Successful packets:\[ \]*(\[0-9\]+)" { set pkts_sent [expr { $expect_out(1,string) }]; wait } eof { set pkts_sent unknown} } # wait some time and kill pktanon: ########################################## sleep 1 exec sudo killall pktanon # count number of frames in result and number of frames matching filter in original file: set pkts_matched [count_frames trace.pcap "not arp"] set pkts_rcvd [count_frames ../out.pcap] # test that pktanon received all the packets: ########################################## if { $verbose > 0 } { puts "\n\nnumber of pkts sent = $pkts_matched" puts "number of pkts rcved = $pkts_rcvd" } if { $pkts_matched == $pkts_rcvd} {pass $test} { fail "$test: expected $pkts_matched packets, actual $pkts_rcvd packets" }pktanon-master/tests/pktanon-runtime/gzip_pipe.sh0000755000000000000000000000005112701423271021334 0ustar rootroot#!/bin/bash zcat trace.pcap.gz | $1 - $2 pktanon-master/tests/pktanon-runtime/libpcap_dump.exp.todo0000644000000000000000000000056512701423271023142 0ustar rootrootset test "test libpcap-file-io" set pktopts {-c profile_iden.xml -l trace.pcap ../out.pcap} source common.exp expect { "total packets: 100" { # expect { # -re "elapsed time.us.: (\[0-9\]+)" { set profile_iden [expr { $expect_out(1,string) }] } # } wait fail " $test; >> i have no idea how to test if it is libpcap " } eof { fail $test} }pktanon-master/tests/pktanon-runtime/libpcap_dump__dump.exp0000644000000000000000000000351412701423271023357 0ustar rootroot# # test basic functions of LibpcapLoop on file-file # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] ## test1: ## run pktanon with default profile spawn $PKTANON -l -c $anon_profile $trace_file $out_file expect { "complete" { match_io_logs "libpcap" expect eof } eof { fail "$test: failed running pktanon with default profile" break; } } ## read number of packets and time set packets_iden 0 set time_iden 0 regexp {processed packets:\s*(\d+)} $expect_out(buffer) match packets_iden; regexp {\s(\d+)us} $expect_out(buffer) match time_iden; ## test that identity works compare_stats $trace_file $out_file ## test2: ## run pktanon with non-default profile spawn $PKTANON -l -c $anon_profile_mix $trace_file $out_file expect { "complete" { match_io_logs "libpcap" expect eof } eof { fail "$test: failed running pktanon with non-default profile" break; } } ## read number of packets and time set packets_mix 0 set time_mix 0 regexp {processed packets:\s*(\d+)} $expect_out(buffer) match packets_mix; regexp {\s(\d+)us} $expect_out(buffer) match time_mix; set expected_pkts 100; if {$verbose > 0} { puts "\n" puts "expected pkts: $expected_pkts" puts "results:" puts " \tiden\tmix" puts "pkts: \t$packets_iden\t$packets_mix" puts "time: \t$time_iden\t$time_mix" puts "\n" } if {$packets_iden != $expected_pkts} { fail "$test - expected $expected_pkts, processed $packets_iden packets"; set test_passed 0;} if {$packets_mix != $expected_pkts} { fail "$test - expected $expected_pkts, processed $packets_mix packets"; set test_passed 0;} if {$time_mix <= $time_iden} { fail "$test - mixed profile shoud be slower than identity profile, actual times(us): iden=$time_iden,mix=$time_mix"; set test_passed 0;} pktanon-master/tests/pktanon-runtime/raw_socket__ostream.exp0000644000000000000000000000432312701423271023565 0ustar rootrootset script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] # test sudo working: ###################################################### set status [test_sudo_nopwd] if { $status != 0 } { unsupported "$test: cannot execute sudo without passwords (status = $status)" break } # spawn pktanon: ########################################################### spawn sudo $PKTANON -c $anon_profile -i lo -r $out_file expect { initialized { match_io_logs "socket-raw" match_io_logs "ostream" } eof {fail "$test pktanon failed to initialize"; break;} timeout {fail "$test pktanon failed to initialize"; break;} } set spawn_pktanon $spawn_id # send packets to lo using tcpreplay: ###################################### log_user 0 spawn sudo tcpreplay -i lo -t -l 1 $trace_file expect { -re "Successful packets:\[ \]*(\[0-9\]+)" { set pkts_sent [expr { $expect_out(1,string) }]; wait; } eof { set pkts_sent unknown unsupported "$test - failed to send packets with tcpreplay" } } log_user $log_user_original # wait some time and terminate pktanon: ########################################## sleep 1 set spawn_id $spawn_pktanon send \x03 expect { -re "processed packets:\[ \]*(\[0-9\]+)" { set pkts_processed [expr { $expect_out(1,string) }]; wait; } eof {fail "$test pktanon failed"} timeout {fail "$test pktanon failed"} } # count number of frames in result: ########################################## set pkts_rcvd [count_frames $out_file] # test that pktanon received all the packets: ########################################## if { $verbose > 0 } { puts "\n\nnumber of pkts sent = $pkts_sent" puts "number of pkts processed = $pkts_processed" puts "number of pkts rcved = $pkts_rcvd" } if { $pkts_sent == $pkts_rcvd} {pass $test} { fail "$test: expected $pkts_sent packets, actual $pkts_rcvd packets" } if { $pkts_sent == $pkts_processed} {pass $test} { fail "$test: expected $pkts_sent processed packets packets, actual $pkts_processed packets" } ### check that packets are filled with data compare_stats $trace_file $out_file ### TODO packets are doubled ### TODO packets are not filled despite that they seem to be written normallypktanon-master/tests/pktanon-runtime/istream__ostream.exp0000644000000000000000000000361212701423271023070 0ustar rootroot# # test pktanon infile/outfile with std::io # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] ## test1: ## run pktanon with default profile spawn $PKTANON -c $anon_profile $trace_file $out_file expect { "complete" { match_io_logs "istream" match_io_logs "ostream" expect eof } eof { fail "$test: failed running pktanon with default profile" break } } ## read number of packets and time set packets_iden 0 set time_iden 0 regexp {processed packets:\s*(\d+)} $expect_out(buffer) match packets_iden; regexp {\s(\d+)us} $expect_out(buffer) match time_iden; ## compare stats in trace file and output file compare_stats $trace_file $out_file ## test2: ## run pktanon with non-default profile spawn $PKTANON -c $anon_profile_mix $trace_file $out_file expect { "complete" { match_io_logs "istream" match_io_logs "ostream" expect eof } eof { fail "$test: failed running pktanon with non-default profile" break; } } ## read number of packets and time set packets_mix 0 set time_mix 0 regexp {processed packets:\s*(\d+)} $expect_out(buffer) match packets_mix; regexp {\s(\d+)us} $expect_out(buffer) match time_mix; set expected_pkts 100; if {$verbose > 0} { puts "\n" puts "expected pkts: $expected_pkts" puts "results:" puts " \tiden\tmix" puts "pkts: \t$packets_iden\t$packets_mix" puts "time: \t$time_iden\t$time_mix" puts "\n" } if {$packets_iden != $expected_pkts} { fail "$test - expected $expected_pkts, processed $packets_iden packets"; set test_passed 0;} if {$packets_mix != $expected_pkts} { fail "$test - expected $expected_pkts, processed $packets_mix packets"; set test_passed 0;} if {$time_mix <= $time_iden} { fail "$test - mixed profile shoud be slower than identity profile, actual times(us): iden=$time_iden,mix=$time_mix"; set test_passed 0;} pktanon-master/tests/pktanon-runtime/libpcap_live__socket_output_udp.exp0000644000000000000000000000461312701423271026165 0ustar rootroot# # test libpcap-capture - udp-socket combination # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] set pkt_ifname lo set port 8888 #### test pktanon listening to interface and sending output to udp socket # start nmap in ipv6/udp mode spawn -noecho ncat -6 -u -l $port # spawn pktanon spawn sudo $PKTANON -c $anon_profile -i $pkt_ifname -o ::1:$port -6 -u expect { initialized { set pktanon_initialized 1 # match_io_logs "libpcap" } eof {fail "$test pktanon failed to initialize"; break;} timeout {fail "$test pktanon failed to initialize"; break;} } set pktanon_spawn_id $spawn_id log_user 0 # start tshark that counts frames spawn -noecho sudo tshark -i lo -q -z io,stat,0 expect "Loopback" # doesn't work without it sleep 2 set tshark_spawn_id $spawn_id # send trace_file on lo and count the packets sent spawn sudo tcpreplay -i lo -t -l 1 $trace_file expect { -re "Successful packets:\[ \]*(\[0-9\]+)" { set pkts_sent [expr { $expect_out(1,string) }]; wait; } eof { set pkts_sent unknown unsupported "$test - failed to send packets with tcpreplay" } } # stop pktanon and count packets sent log_user $log_user_original set spawn_id $pktanon_spawn_id send \x03 send \x03 expect { -re "processed packets:\[ \]*(\[0-9\]+)" { set pkts_processed [expr { $expect_out(1,string) }]; wait; } eof {fail "$test pktanon failed"; break; } timeout {fail "$test pktanon failed"; break;} } log_user 0 set spawn_id $tshark_spawn_id # doesn't work without it sleep 2 send \x03 send \x03 expect eof set pkts_captured [count_frames_in_output $expect_out(buffer)] ## for some reasons pktanon processes twice the number of pkts being sent ## and doesn't go into infinite loop if { $verbose > 0} { puts "\npkts sent = $pkts_sent" puts "pkts processed = $pkts_processed" puts "pkts captured = $pkts_captured" } if { $pkts_processed < $pkts_sent} { fail " pktanon didn't process enough packets"} if { $pkts_captured < ($pkts_sent + $pkts_processed) } { fail " pktanon didn't sent enough packets on output interface"} log_user $log_user_original # TODO: this # TODO: test with stdin,stdout and io pipes # TODO: ipv6 forwarding header # TODO: disallow libpcap-dump to not libpcap # TODO: remove / move error-codes to pktanon / something so that there is no need to include something other than packetanon / error-codes pktanon-master/tests/pktanon-runtime/console.exp0000644000000000000000000000200412701423271021167 0ustar rootroot# # small tests to test correct parsing of console options # proc expect_error_message {} { global spawn_id global test expect { "Error:" { expect { "initializing PktAnon" { fail "$test - pktanon should have exited" } eof { pass $test } } } eof {fail "$test - expected error message"} } } #---------------------------------------------------- set test "test help message" spawn $PKTANON -h expect { "Usage" { expect { "verbose" { pass "$test - help message"} eof { fail "$test - expected --verbose help string"} } } eof { fail "$test - expected pktanon help" } } #---------------------------------------------------- set test "test missing output file" spawn $PKTANON infile expect_error_message #---------------------------------------------------- set test "test too many files" spawn $PKTANON outfile expect_error_message #---------------------------------------------------- set test "snaplen NaN" spawn $PKTANON -s abc infile outfile expect_error_messagepktanon-master/tests/pktanon-runtime/istream__socket_output_ip6_tcp.exp0000644000000000000000000000341512701423271025753 0ustar rootroot# # test for socket-output (test ip6/tcp output) # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] set port 8888 ### ipv6 socket (ipv6, tcp) ############################################ # start nmap in ipv4/tcp mode spawn ncat -6 -l $port # start tshark spawn -noecho sudo tshark -i lo -q -z io,stat,0,ip,ipv6,tcp,udp expect "Loopback" # doesn't work without it sleep 2 set tshark_spawn_id $spawn_id # start pktanon spawn $PKTANON -c $anon_profile -o ::1:$port -6 $trace_file expect { -re "processed packets:\[ \]*(\[0-9\]+)" { set pkts_sent [expr { $expect_out(1,string) }]; wait; } eof {fail "$test pktanon failed"; break;} timeout {fail "$test pktanon failed"; break;} } log_user 0 set spawn_id $tshark_spawn_id # doesn't work without it sleep 2 send \x03 send \x03 expect eof set buff $expect_out(buffer) set tshark_stats [count_stats_in_output $buff] set ip_frames [lindex $tshark_stats 0] set ip6_frames [lindex $tshark_stats 1] set tcp_frames [lindex $tshark_stats 2] set udp_frames [lindex $tshark_stats 3] vputs "captured sent frames:" vputs " stat ip4_frames: expected - none, result - $ip_frames" vputs " stat ip6_frames: expected - some, result - $ip6_frames" vputs " stat tcp_frames: expected - some, result - $tcp_frames" vputs " stat udp_frames: expected - none, result - $udp_frames" # puts "fc = $ip_frames" # puts "fc = $ip6_frames" # puts "fc = $tcp_frames" # puts "fc = $udp_frames" if {$ip_frames > 0} { fail "$test there should not be any ip4 frames"} if {$ip6_frames <= 0} { fail "$test there should be ip6 frames"} if {$tcp_frames <= 0} { fail "$test there should be tcp frames"} if {$udp_frames > 0} { fail "$test there should not be any udp frames"} log_user $log_user_originalpktanon-master/tests/pktanon-runtime/istream__socket_output_ip6_udp.exp0000644000000000000000000000335712701423271025762 0ustar rootroot# # test for socket-output (test ip6/udp output) # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] set port 8888 ### ipv6 socket (ipv6, tcp) ############################################ log_user 0 # start nmap in ipv4/tcp mode spawn -noecho ncat -6 -u -l $port # start tshark spawn -noecho sudo tshark -i lo -q -z io,stat,0,ip,ipv6,tcp,udp expect "Loopback" # doesn't work without it sleep 2 set tshark_spawn_id $spawn_id log_user $log_user_original # start pktanon spawn $PKTANON -c $anon_profile -o ::1:$port -6 -u $trace_file expect { -re "processed packets:\[ \]*(\[0-9\]+)" { set pkts_sent [expr { $expect_out(1,string) }]; wait; } eof {fail "$test pktanon failed to initialize"; break;} timeout {fail "$test pktanon failed to initialize"; break;} } log_user 0 set spawn_id $tshark_spawn_id # doesn't work without it sleep 2 send \x03 send \x03 expect eof set buff $expect_out(buffer) set tshark_stats [count_stats_in_output $buff] set ip_frames [lindex $tshark_stats 0] set ip6_frames [lindex $tshark_stats 1] set tcp_frames [lindex $tshark_stats 2] set udp_frames [lindex $tshark_stats 3] vputs "\ncaptured sent frames:" vputs " stat ip4_frames: expected - none, result - $ip_frames" vputs " stat ip6_frames: expected - 100, result - $ip6_frames" vputs " stat tcp_frames: expected - none, result - $tcp_frames" vputs " stat udp_frames: expected - 100, result - $udp_frames" if {$ip_frames > 0} { fail "$test there should be no ip frames"} if {$ip6_frames != 100} { fail "$test there should be ipv6 frames"} if {$tcp_frames > 0} { fail "$test there should be no frames"} if {$udp_frames != 100} { fail "$test there should be udp frames"} log_user $log_user_originalpktanon-master/tests/pktanon-runtime/libpcap_live__inject.exp0000644000000000000000000000346412701423271023664 0ustar rootroot# # test libpcap-capture - libpcap-inject combination # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] ### file - libpcap-life ############################################ # start tshark spawn -noecho sudo tshark -i lo -q -z io,stat,0 expect "Loopback" # doesn't work without it sleep 2 set tshark_spawn_id $spawn_id # start pktanon spawn sudo $PKTANON -c $anon_profile -i lo -n lo $trace_file set pktanon_spawn_id $spawn_id # start tcpreplay, send trace_file on lo and count the packets sent log_user 0 spawn sudo tcpreplay -i lo -t -l 1 $trace_file expect { -re "Successful packets:\[ \]*(\[0-9\]+)" { set pkts_sent [expr { $expect_out(1,string) }]; wait; } eof { set pkts_sent unknown unsupported "$test - failed to send packets with tcpreplay" } } # interrupt pktanon and count packets sent sleep 0 log_user $log_user_original set spawn_id $pktanon_spawn_id send \x03 expect { -re "processed packets:\[ \]*(\[0-9\]+)" { set pkts_processed [expr { $expect_out(1,string) }]; wait; } eof {fail "$test pktanon failed"; break; } timeout {fail "$test pktanon failed"; break;} } log_user 0 set spawn_id $tshark_spawn_id # doesn't work without it sleep 2 send \x03 send \x03 expect eof set pkts_captured [count_frames_in_output $expect_out(buffer)] ## for some reasons pktanon processes twice the number of pkts being sent ## and doesn't go into infinite loop if { $verbose > 0} { puts "\npkts sent = $pkts_sent" puts "pkts processed = $pkts_processed" puts "pkts captured = $pkts_captured" } if { $pkts_processed < $pkts_sent} { fail " pktanon didn't process enough packets"} if { $pkts_captured < ($pkts_sent + $pkts_processed) } { fail " pktanon didn't sent enough packets on output interface"} log_user $log_user_originalpktanon-master/tests/pktanon-runtime/iostreams_tshark_input.exp.todo0000644000000000000000000000166312701423271025304 0ustar rootroot# # # # TODO # # # # set script_dir [file dirname [info script]] # source [file join $script_dir "../lib/lib.exp"] # # make check runs scripts from another directory, all file pathes are relative to $script_dir # set curr_dir [pwd] # # cd $script_dir # # set_test [info script] # # ## test1: # ## run pktanon with default profile # set test_passed 1; # # spawn ./gzip_pipe.sh $PKTANON # expect { # "complete" { # match_io_logs "istream" # match_io_logs "ostream" # expect eof # } # eof { # set test_passed 0 # fail "$test: failed running pktanon with default profile" # } # } # # ## read number of packets and time # set packets_iden 0 # set time_iden 0 # # regexp {processed packets:\s*(\d+)} $expect_out(buffer) match packets_iden; # regexp {\s(\d+)us} $expect_out(buffer) match time_iden; # # ## compare stats in trace file and output file # compare_stats "trace.pcap" "../out.pcap" # # cd $curr_dir pktanon-master/tests/pktanon-runtime/profile_mix.xml0000644000000000000000000000603612701423271022057 0ustar rootroot pktanon-master/tests/pktanon-runtime/istream_gzip_stdin__ostream.exp0000644000000000000000000000156512701423271025327 0ustar rootroot# # test std::io with pipe # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] set curr_dir [pwd] cd $script_dir ## test pipe with compressed file as input # compress trace.pcap exec gzip -k -f trace.pcap set test_passed 1; spawn ./gzip_pipe.sh $PKTANON ../out.pcap expect { "complete" { match_io_logs "istream" match_io_logs "ostream" expect eof } eof { fail "$test: failed running pktanon with default profile" cd $curr_dir break; } } ## read number of packets and time set packets_iden 0 set time_iden 0 regexp {processed packets:\s*(\d+)} $expect_out(buffer) match packets_iden; regexp {\s(\d+)us} $expect_out(buffer) match time_iden; ## compare stats in trace file and output file compare_stats trace.pcap ../out.pcap exec rm trace.pcap.gz cd $curr_dir pktanon-master/tests/pktanon-runtime/trace.pcap0000644000000000000000000002431312701423271020761 0ustar rootrootòTW@OOԚ uEA@u[-Z'BC 32scc-wkit-clx-227-138.scc.kit.eduT~@<<܅>I܅>I6T@<<@U9:@U9:TOOԚ uEA @11-bBC 32scc-wkit-clx-227-138.scc.kit.eduT<<(G{(G{lT$wl8E@@..D\D\]<{"host_int": 2239754224, "version": [1, 8], "displayname": "", "port": 17500, "namespaces": [21212393, 49465841, 52918784, 131578347]}T$wl8E@@:.D\D\8{"host_int": 2239754224, "version": [1, 8], "displayname": "", "port": 17500, "namespaces": [21212393, 49465841, 52918784, 131578347]}T7`OOԚ uEAt@-kBC 32scc-wkit-clx-227-138.scc.kit.eduTF`<< d26 d26TM`OOԚ uEA@5-BC 32scc-wkit-clx-227-138.scc.kit.eduT::Ԛ uE,,V@`ڍ㊍!w=BJNBT::^Ԛ uE,L9×!;`BNJBT\\S.IEN<<2"2"TL<<|&QP|&QPT,@@^t E2ZPwpadT@@ }2  <>@U9:>@U9:>T<<1A1A/T<<4Yw .RRHP AP station roamedT#L<< m6 m6T6L<<|&QP|&QPT`m <<SST ::HE,@ _!BJNBTI<<S.o"sS.o"sЮTT<<`67{u`67{uTi|#:(E@@YmlgM-SEARCH * HTTP/1.1 HOST: 239.255.255.250:1900 MAN: "ssdp:discover" MX: 1 ST: urn:schemas-upnp-org:device:ZonePlayer:1 X-RINCON-HOUSEHOLD: Sonos_0ocKxbf8RMclla3gcs7k1Regzr T{<<@U9:@U9:Tp= OOԚ uEAsb@-SBC 32scc-wkit-clx-227-138.scc.kit.eduTD OOԚ uEA@ B-7BC 32scc-wkit-clx-227-138.scc.kit.eduT88QezQezT<<Ĉ巡.RRHP AP station connectedT<<S.o"sS.o"sЮTFF33`67{u`:Џ!]`67{uTOOԚ uEAT@"p-FyBC 32scc-wkit-clx-227-138.scc.kit.eduT9\\䔘EN+W[:OW FHFAEBEECACACACACACACACACACACAAA T9OOԚ uEAb@M-{BC 32scc-wkit-clx-227-138.scc.kit.eduT9|#:(E@@YmlgM-SEARCH * HTTP/1.1 HOST: 239.255.255.250:1900 MAN: "ssdp:discover" MX: 1 ST: urn:schemas-upnp-org:device:ZonePlayer:1 X-RINCON-HOUSEHOLD: Sonos_0ocKxbf8RMclla3gcs7k1Regzr T8<<@U9:@U9:ITY<<t/h:!t/h:!%TY<<8< df8< df@TYOOԚ uEA;@QG-lBC 32scc-wkit-clx-227-138.scc.kit.eduTa<<@U9:@U9:TtVV33:\ [T` :* 2ތ:R\BU9:\ [TT@@ }2  <>@U9:>@U9:>TOOԚ uEA@*-nBC 32scc-wkit-clx-227-138.scc.kit.eduT<<0D0D[TFz<<܅>I܅>I6TZzOOԚ uEA@&C-YBC 32scc-wkit-clx-227-138.scc.kit.eduToz88"A"AޫTwz|SE!?D\D\{e{"host_int": 1312442970, "version": [1, 8], "displayname": "", "port": 17500, "namespaces": [193595714, 559360770]}T <<@U9:@U9:?T << d26 d26T OOԚ uEA0w@٧-VeBC 32scc-wkit-clx-227-138.scc.kit.eduT 88QezQezT z.;E@@צsml1M-SEARCH * HTTP/1.1 HOST: 239.255.255.250:1900 MAN: "ssdp:discover" MX: 1 ST: urn:schemas-upnp-org:device:ZonePlayer:1 X-RINCON-HOUSEHOLD: Sonos_rsLnD7TjjlORPKncjdgtbwkQIY T 88QezQezޫT$ <<@U9:@U9:{T* OOԚ uEA@>-kBC 32scc-wkit-clx-227-138.scc.kit.eduT6* rr^c5Edq1PA Axels iPhone _device-info_tcplocal model=N42APTC* 33c5` P GN(PH Axels iPhone _device-info_tcplocal model=N42APTM* VV33,'@U9:n :BU9:,'8* x:%,'@U9:Tk* OOԚ uEA|@ӿ-o*BC 32scc-wkit-clx-227-138.scc.kit.eduT OOԚ uEA?@Wߍn-e{BC 32scc-wkit-clx-227-138.scc.kit.eduT <<t/h:!t/h:!%T2<<S.o"sS.o"sЮTHOOԚ uEA@|&-fBC 32scc-wkit-clx-227-138.scc.kit.eduT<<߽T<<سwسwӖTOOԚ uEA@U-mBC 32scc-wkit-clx-227-138.scc.kit.eduT/<<0D0D[T<88"A"Aޫpktanon-master/tests/pktanon-runtime/istream__socket_output_ip_tcp.exp0000644000000000000000000000316412701423271025666 0ustar rootroot# # test for socket-output (test ip4/tcp output) # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/runtimetestlib.exp"] set_test [info script] set port 8888 ### basic socket (ipv4, tcp) ############################################ log_user 0 # start nmap in ipv4/tcp mode spawn -noecho ncat -4 -l $port # start tshark spawn -noecho sudo tshark -i lo -q -z io,stat,0,ip,ipv6,tcp,udp expect "Loopback" # doesn't work without it sleep 2 set tshark_spawn_id $spawn_id log_user $log_user_original # start pktanon spawn $PKTANON -c $anon_profile -o 127.0.0.1:$port $trace_file expect { -re "processed packets:\[ \]*(\[0-9\]+)" { set pkts_sent [expr { $expect_out(1,string) }]; wait; } eof {fail "$test pktanon failed to initialize"; break;} timeout {fail "$test pktanon failed to initialize"; break;} } log_user 0 set spawn_id $tshark_spawn_id # doesn't work without it sleep 2 send \x03 send \x03 expect eof set buff $expect_out(buffer) set tshark_stats [count_stats_in_output $buff] set ip_frames [lindex $tshark_stats 0] set ip6_frames [lindex $tshark_stats 1] set tcp_frames [lindex $tshark_stats 2] set udp_frames [lindex $tshark_stats 3] vputs "\ncaptured sent frames:" vputs " stat ip4_frames: expected - some, result - $ip_frames" vputs " stat ip6_frames: expected - none, result - $ip6_frames" vputs " stat tcp_frames: expected - some, result - $tcp_frames" vputs " stat udp_frames: expected - none, result - $udp_frames" if {$ip_frames <= 0} { fail $test} if {$ip6_frames > 0} { fail $test} if {$tcp_frames <= 0} { fail $test} if {$udp_frames > 0} { fail $test} log_user $log_user_originalpktanon-master/tests/errors/0000755000000000000000000000000012701423271015174 5ustar rootrootpktanon-master/tests/errors/eth_ip6.xml0000644000000000000000000000144712701423271017262 0ustar rootroot pktanon-master/tests/errors/profile.xml0000644000000000000000000000554212701423271017364 0ustar rootroot pktanon-master/tests/errors/bad_udp.pcap0000644000000000000000000000125512701423271017442 0ustar rootrootòd((E<Ş@ cu,,E<Ş@ c6gJȡ<]<<E<Ş@ c^ׯyvJy{瓌g33E<Ş@ cAՠ~,prJ 4K,,E<Ş@ c*O:/NNE<Ş@ c%s:{FpJ %OA  OL.gl/33E<Ş@ c`) pktanon-master/tests/errors/bad_arp.pcap0000644000000000000000000004415412701423271017441 0ustar rootrootò2% ]%%d#XN%s0#Ԃ엓 _*k1Y׬%s%sH%so"Xu5,Cn2X{WoϏ(:R%s`̖e5_BzE%(p31%s@w/=%s}Nt0%.8RU'[c%s#%sךgu't=%G^#K{))i#c"`{Ƃ%sH3RtϘ_(pYf(fY)OC%s%sOP)=OtYeF9pmopm|J(wa3XF׀*Aub# #G?J'_a2kf+%sH)&󓫳S MZR@<&דl_7]őv%suUC%s%s3ѐ*C%sf %sو=Q8ۢ1'iG]B+뢄  2%_\Â,2N-vL.;%sU}W%s %ss+ 9N!quQ!~4T%sΔ%sKZwOLkA2tM/saay7o 粩#<Ũi&nS$#`Ol"Xdsoq"Pyj1#@;~ -䪦%s%s@Ө0ޖ%sLxjqN Kv@{ܝŲx$,lөƨ2!%s HpWr{'z%s1%sl0Ee2xj ,%׀}9uE2_PV>vo=Vzwa&գB-^8h%sX{1wdڥ b"i%ss0<0Ag\2%s F%%s` fF.J%slhw#+%s)}ak07V%seL8)5~S3k>{һ @%s !bj&ھj_3Me}G=vxmc%sHvNhxRøfHK9N+#؎_[*0[5YK$T*&:VKMZ%svA`tLd,MC)T=dXb^"h؜^D$Ln{pe/xܞ:ڄ]dJ>ʔBsY9D76olza"`moqt%s- > e ݏ`1;&h;_)?nҔ։%s/~y82%s,%sM%ZAg:銄/"vGw@j]ecB=-U΍N%saA^T G߭cdmxr5LBTK. I;k$x(~1šBQn7lnA~m̗Z 7+q%sz`ثܘ]tcog1kK8 Q%s\?anl%sl QQìw\85 {˕ʤ%s$OK4A[?hh} TR,{%s%sbq E*Z{%s(],X5n\%sy%s#ɭCoP'PF::hiv&7iq4]i0S|W&21F%s.7+ yX%Yd؟%s%srϠSJpv%_%sWp8{նv: $ 2yJkkSZ8I9 hWIKE%KboŁ'%sMwm%s=̤J 9# hu4ǜkQ%soX)7b۝.%s[t*fl#%sƄt>Gi]x1 džr 7JpA%s%s'㤾d0xWبSwe­:L%s $zz%s7 xܼj.NS%s--_R!߱kWRe!kQw"\=1Kx[ &ze=S%sZf C5]$ sކz xR~Xρs'WBHjY[mZ_qv9G>;`=*ИrA2%% nU;jzuQ/^t/,%s&asiCDi<$jE`7cc[4MpCJ,r`D Ri[k"A" r`HTdnci\b[*˄.(V%sLwrODf$^nٟ%sL#"@^7oy=5؇o+p|;up*;"Z`ֱB1{XO;J͒ 7DF?&8foh}%sS0,ٰZItR&%sRD,> { %s(ٞf$%smBr6C_%so&$FwjT1qU P_jt9rT2֓#& o@ju%s8?yJ*c %sl>Smq 'v0 ?Rt<L_]Jse58$f!DR~%sC&o-%sS>HQcJ`N35a4I<>z^H|^{-pٞE8Xt">Z<:ӰeuFQ B^Q?- zQ$cI,]f-d%_Ն -i5 B(m-H%s.Zc%g6VɈ$#;K%sMɔLAǩ!pJOF`BxVS: %sP|g2+V,G\3y,_%sф$$c!4$t !O&:I h(2R9IH%sɹA=h (%sÝ>fDˎԦqjJ%sȰŢL1 qEqr%sVJփpQpLK)ׇj(zά I!%scX$մXT"ѭt%sj*2.7^%s%s}6f%sK"аVC|:\k} %7،'Y7D(/|bp9~Tr՞],*E8kV]Pr',VY4u~1x:CC(ekM$b\%sqcdy=iLЗ&71D pM65vɧ`֜`  1 Ϋz)NN)ˣ5>%s4{ gL[K(_M6Sm=ݿ\$y5,. 2'74Iv|GuZTv(dv0nD N+O޿'z!bMz$%skqѧG^+Wv&To%/YVv}om(z"fNȡom, |<:|"%stK%MXSKLb/uCCwCktp2qoBWfx/]P>.Lj=s24Վ:*gqD%s%s=M%s(1tiu%s#8 fC<}:Y1@J+fgA sB%s(%oLAƒ*Q8feJ F%s1;qt'w}-Sbz$mLe'\|3Gf#ԭF& C*l6fa+ $Y0ro%sd@X)kQ %s$x?>$U%segVZ %sJ%s{%sdM>{w 3䥱܈}%stg "K"nc_%xSHTO48;a͸ sSZJcT9L>,%&bf3k):4Zzӡ%s!g 5DqfC3%sNj%s^C%s*cٕ͑'ZZA%suR`hZ%sQ6z %s*%s.b 5Tg{kYnXX%sv>UVtqt9-^3Vr{^+E eivRK<%sTztEuδ>%sRсA@3[%s_zA\ BhTڎefȞ~&ذ3](%s_\Zc PO'㔚PG'jcŌI !%sHIU,ZŹ #>ܚښayx^PHA>ӥ` ulαiĶTnpZ^n%s)d<{%sM5 *;"沅>ܫ %`:%sGau%s%sQөL".,n;%s˄p6,?y(Gd?RO%smh3E,ճ:Z oc‰[3 "aqi1 DGJ1͋%soa9 dUYb>uP*?@Dot%sQKl$%sh!]>smU)a5 z ӵ[\zPC6.}|R|'-kK"ggL%saB} %ap/dgd \"CN{Ɩs'򊛳҄ ,brGQPy|Ze IKs7 B]qTXQLq-p$ܞ%skJ%scM4 ~5lW %29Mk:9E݄2%s1JA5l%saopfaU4],`L0c_v񊄫q1u, P-pT@k/DƁw `l3gPe8zpVz5uQ yu82%su\f(|,%.%sǃ%s?ۣH>i,%s||YVJ7GJiW&45b>t-:%s%s4cO~2iJj{ց۔ i~Nl{%ml0 zLZmlq߆eHtNn'%s4^])l@m vx+d126]WdUc.&X%s@KJ92f p±$y]O"%sB[e(J=%sD]R%sZL6ZQ N{}y'^}qb`#AUT.o S-{Y6!tx| K]zВՕ0,1>%s@ ذY6]U@oS.B0-\Al%sUzgJ+t{t8˳ ƪPx %s֢q%&`x; VwƖQEG%3FJY}C`kQ5h7| n#_]ȃhQ];[I,w b Kga=MQ}q>:'>CVZJTUrC""T"W %sl-VT(1-|2 [U2Q{"SQNBe%sZ܁!D %shTUCb%sb%sc|VN&Ң0b~h! ڰLZXM%s.DJN %s,[J ;Eb%sň\S%s Clz "ុ}%s%s%s)Icv,o\*C| %s9EM(20[)%l1.oV}%s%s6.%s&(.JM@,ю wuڹp 9*8ec NL2F F'Ww%sh?ֹp%sZ;iWP6-g{v]pd'MjI1myRV GܟPz=9uLԣMݰ5Ra?l=W1$%s6%*%shfN%s%s%so 9Oа"4*Br$Es77uToY/2%sj;e%s%s@a6n%s_4!+~'-.Nsm\Q9֘%s?>UXx32h0 %s%s_ma7f 0/ &_N%s&CB`"Z%s諍v y̌Nia'}s2)WΒ;%s4?_3K3t%su'%s_R6ïׁ^^Fqh.K;0%sԑp%s6sÖ|=2{üb&FLc9Gn42%72%Zȣ6krͨj0h v%sn'`ǒ=VV#:%svC9CAWmQu5%s![>%s#J[7֤K"%*q?C Ṷ̋5KskH;/%sZ6x! "%3-%s 1ϙQ]~Ўdw[{X\f/3 זwy\~ /$\Kҧl %sl 2,YB>1%sʆ6Uo%sSX̜%s D,> G? D*&8^}=G_Xu.%sԇoC%smjX1Z*(+v%s7agI?8mX}+s9),mAl4y`5^6%s7p@ %sLo FmyL-םS:]FxB ;ETNhT.y5cG^SidU~a%s^I>RЈŸ]fO ܺ|r2@tNvۋC Tfw7%sNJ< |Os}.jCPx[ YvRڟv0,=Eq4zT@깛G9DT#%s0CZ%s!+p3"%s@QQjei1Cy%snsœ$v~6M)+ ҐNbV4n6N\.AncN"Bq-\>%sxM09QA%s%%s!M 7m@bEzS3c#%sA8l $1Մ+*y@l _@%sq*(Mb6ͣ.jP#b!dUt'{#N4%sS%s-j͝16ko/~,af d[N"yGY?prVTv9R K"CGJU6q 3iP_`a >yem4˩/SRYz҄Oۓ~Xٯ /r.8z4M ̵%s688oYr"sK;ҟ39q gM>7z%sݼSdgS@qi;p-,Bh'>Dz-P}Ln7^mĀxy?kT`R9n"-9nH}Cȱ%s@LܣB3 9pF(@%sKVQy t~(jYyFJɾg.ƞ*i1-mFE^|UL:2l%sx5JLyk:rt`x]%sT/%sd'}7%֬$ %sߗMAݶo%'^޳xtŁjBV %].O'ɼ&PoU0%Q%soPF#)T$%sMgӍWDK XΙ+N9~'3ktH z|4e#d{%E?H1U t#gOUOI_`{+% o4YT=%s%sY?&YߋD%s/{%shUN>[4 fY(@xZ%s [l`A%sŸ{rg8:SծKQy.Y+{fquf{y'@x/Йc4'Dlv ;~ZI 9h@%r nREg5V"֒H}2҂]FWɊ*:D1W J1%swRYŘB\3Ho%si^X%s+PgVb%sxvr$K[ٙ%s`%sDB%slvg2Z{+p4I2RgWP3n"SA%sP/)gO(%s.տQ[ f0yvg9-|ղdyIڳfWkIoM%s23޼#*05QɄ@~ѽR8_'X:.>"aC% :Li4@*9K%s?;%sxu0Zf%s\&qX}|x$k\g?g 'lE:^T0$(g3ù!I*Ҩ2%%s Z2»9=BsYPb)Cx%spÊnֿ=3֭Ufŝ-$nk.j<6%sdX͘{3>i%S?)%s])Q趤TakYu%s{,;̋DhHD%sBLGٮq Iz~FsaE9SudE%sQe3V5-$zTpv AYR)z&O/&%sPѴ}Q}%sa%sHÙf[s%s%sba,yns,/aEejY5_2}mG*fk kN BLl_3&ݜr$vG-D}%s%s] %sb ".lċ~ʔ]{Koe+%s%s; #p丞+ᶑx-`%s%s~ox\k%sW9Ne#mJ%sc %aaP!J2d[uʬУr:7%s6|6SCFew!%s $; 4L%%ss3S4%sM,:?#%s!)$)wR3QF7ȰVKI2a\`r,>ia;F'vD*H%sggһDX+We=N8NIKrp|S%sd K%sh1d:Bb,ϗ,>DrD|?<%s\%s%s>%sT3?l&TAǾSJ%|k/XQZN;R`%sVCȱ 8Kk6pDC!8P7[>%d}uz_:ԪӞGi@%s{Q]%sBi2by|(֎O]] j%smU=<$O9K%s%s;<"_k_rP{%s*&,%s$! ď (ɦ[>.o\?X[%sL3#b:@%sc\#Д8v%sW%s- s.,]'a{>+=I%s(/*/=)&Cy_]4x_M%sX plYy%s=.@j%sA](Gεu\;4Y%s}ډ,Ib1i %s|{-RwP%sͬ)f4%sݯiVC3`%s_%sCRF`I6aj2A{1 `aŴ Io"+Iq*VS5;btuf,a%6莋QڌTv #t %s%sĎ%JVu*v<,a-!ļحZĥt;ub:GEy@ˣ=2mv[x!d٩ ݽH󼞯)Gf .n e{$`zoTXTi2PLBT(D1?l`Ylkakcm==gYv:-{ OZq`ΐƓR(%stᬬs/ SsDa(Pm10%s2]0d9љzŒxꇿ읡s%s P1^lU+U%s>KF7!@ue8YՓ:vz0l$9qiNGn|ȳd|Ax#dU(,Rht[v'lITX(7;B=װ%sKlkj9L_kiiO-o'g+!uTD)BZW0Iu (%s u(E"=P?&fK{%s+=}:EpQqW*.wcLCcT_:3zV' Ep{H -MGL25׿ppp~+d%sbşlC[=.%st:bv =ӫ—"dC Ds2:\%s蹑O(q}%s XjPaij U-%s7^91fmܜe( _%s 8 !2ݹhfxx_̻ \Wen^voxp`нP%s)]H_Gv0 LV|l)%sxOkYϓ*%se?dk? t3%G.%sI%s u0,skyHم?n10 %s{?i%sznz9 %s!%sF Kx0hC",|Xw':{f|W!f]-uW>%sBX9]Yssb%0%u IZPMv WQ^4nl)Fb%sx6n*8n%s 3csEr%RqS%sA"o0ɝVBL )nk3n+ 75G[ %s&ENWl:ۂ%sPB0%s"*_i%sWxXGoC7Λ3J"9|ǎ"2;mi*7:=m=od5XejO~\Vu%sŚYތu({|%sՊ+L%sq?|/Gvk.L\KQ#OĞ@0b&Rc@dU Jܨ9)7JsZ0Pa|%khY%sb i3/$ %s*̎9"o K#kJ+5%s@<%K |u%sa`ɖ Eʐ0\TKTnޯϥ/[Wp(.p~ǛIXxzE:%s[4%sE,U SYlWzk%s{<&:Rj΀Ͻ%sL*F%s`C調9iX%st.om=[p'Rb|#v4,%sXަu?ΒaF%sØ,qf?eh(Н:\vЫ 9։ʮ'q ;$H΄&Ӷ#lCHJ\%sV?:;(7d‡sRQ0H_ 0 ޟ;~f%s_ TKSZHoaWINFeI>H|dW9:s?eP7 _#;%sz'Ywtx%sY%se%sX~cuc\>:+RfKDd)7TnKSP҂`*WKؙ֧Z7͑82y%%s..ulF%s^J66z7xl ً6v8ٝU%rK,kHmEARw`<ad{#WDECjٹ@ϴpO"\lܪƺ=l!3ש6 &yGR9 .e3%. vSW+7]Yf#rd-Cm+``~lz cҪ p%s;F9DmQzg5=4efɂ{'/yMUPD{xa %s^%sKpsi n%s=*,(=ZL&i?%s8{1y%sˡޛ7JLl~I-+M+}A>E//wq]'"&_|F3i|F4l 6 .Q%sH$3v k?IBN<~U]%s9*&!%sLqi~%sMg )%s:E;%sh-%sՒҲ^ie%sMZEO2IQ%sN윪%"UX$/SZgx*Q<Lv`%sur8x%s(%s@L+P%s%s9yk_6볅%scOThWcϤRBk duԏB]nEZhZ4P>d%s%s! r+FUd'Wd;sn|[p+(ij;JcNY<rCD%s %sT|i8%s0D||m *A.|k@l%s?[q~INpktanon-master/tests/errors/test_bad_ip.exp0000644000000000000000000000144212701423271020170 0ustar rootroot# # test for correct messages by mailformed packets # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/errorstestlib.exp"] set trace_file [file join $script_dir bad_ip.pcap] set_test [info script] spawn $PKTANON -c $anon_profile $trace_file $out_file expect_error_in_packet 1 "packet is fragmented" expect_error_in_packet 2 "bogus ip4 header length" expect_error_in_packet 3 "packet is fragmented" expect_error_in_packet 4 "packet is fragmented" expect_error_in_packet 5 "packet is fragmented" expect_error_in_packet 6 "packet is fragmented" expect_error_in_packet 7 "packet is fragmented" expect_error_in_packet 8 "bogus ip4 header length" expect_error_in_packet 9 "packet is fragmented" expect_error_in_packet 10 "packet is fragmented" expect_total_errors 10 waitpktanon-master/tests/errors/eth_ip6_tcp_udp.xml0000644000000000000000000000266012701423271020776 0ustar rootroot pktanon-master/tests/errors/test_bad_ethernet.exp0000644000000000000000000000065212701423271021400 0ustar rootroot# # test for correct messages by mailformed packets # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/errorstestlib.exp"] set trace_file [file join $script_dir bad_ethernet.pcap] set_test [info script] spawn $PKTANON -c $anon_profile $trace_file $out_file expect_error_in_packet 2 "ethernet header too short" expect_error_in_packet 3 "ethernet header too short" expect_total_errors 2 waitpktanon-master/tests/errors/bad_ethernet.pcap0000644000000000000000000000115212701423271020464 0ustar rootrootòd 2~nCaK\`$Ɋ FÀo%so0`NIIu>WGBnISKҵQ?oBZ%@[ϳ 1sc??%sy%s4c\pm%sTߓi8cO! <`nvM&IIEg`%s8wE}F}%sC-AuTFq<4e}22sBUOj*Xe@Ep9jr0J2^'%s##P%s (2gvG%2t`kvq?%s%s33=Ҽ eX{P<O%sb 6dS|<=(%s_w 33ps%W\gA0d1!w/ö/ m*#]pktanon-master/tests/errors/test_bad_udp.exp0000644000000000000000000000134112701423271020346 0ustar rootroot# # test for correct messages by mailformed packets # set script_dir [file dirname [info script]] source [file join $script_dir "../lib/errorstestlib.exp"] set trace_file [file join $script_dir bad_udp.pcap] set_test [info script] spawn $PKTANON -c $anon_profile $trace_file $out_file expect_error_in_packet 1 "bad udp length" expect_error_in_packet 2 "bad udp length" expect_error_in_packet 3 "bad udp length" expect_error_in_packet 4 "bad udp length" expect_error_in_packet 5 "bad udp length" expect_error_in_packet 6 "bad udp length" expect_error_in_packet 7 "bad udp length" expect_error_in_packet 8 "bad udp length" expect_error_in_packet 9 "bad udp length" expect_error_in_packet 10 "bad udp length" expect_total_errors 10 waitpktanon-master/tests/errors/test_ip6_hop_by_hop_opt.exp0000644000000000000000000000223112701423271022535 0ustar rootroot# # test that there is no error on traces with IPv6 Hop-by-Hop option # Hop-by-Hop option has next_header value of 0 and causes problems when # finding transformations in hash table PHT.h # set script_dir [file dirname [info script]] set trace_file [file join $script_dir trace_hop_by_hop_opt.pcap] set out_file [file join $script_dir .. out.pcap] set profile1 [file join $script_dir eth_ip6.xml] set profile2 [file join $script_dir eth_ip6_icmp6.xml] set profile3 [file join $script_dir eth_ip6_tcp_udp.xml] set test [file rootname [ file tail [info script]]] #### test with eth_ip6 profile spawn $PKTANON -v -c $profile1 $trace_file $out_file expect { "complete" { expect eof } eof { fail "$test: failed running pktanon with $profile1" # break } } # spawn $PKTANON -c $profile2 $trace_file $out_file # expect { # "complete" { # expect eof # } # eof { # fail "$test: failed running pktanon with $profile1" # # break # } # } # # spawn $PKTANON -c $profile3 $trace_file $out_file # expect { # "complete" { # expect eof # } # eof { # fail "$test: failed running pktanon with $profile1" # # break # } # }pktanon-master/tests/errors/trace_hop_by_hop_opt.pcap0000644000000000000000000005277112701423271022243 0ustar rootrootò5>TlZZ33xІ`$8y!Fb:5>T>>33 .ȃ`: .ȃ5>Tj`nn33g`8g: ˔g5>TTrVV33~TZZ33&G`$e4m :T! 5>TZZ33H$?I`$N:w/#:< 5>TZZ33Lmm`$xlF3[:%~}5>TZZ33Y`$Il߅Ù@:Ù@5>T33^`L^::5>TVV33 t/h` :Y ut =u t/h5>TVV33~TZZ338D `$z/:V 5>TZZ33rJ`$)Ώ=:b5>TZZ33$w&D`$; : 5>T]ZZ33 -`${8k:k5>TnZZ33O,x,`$rr):]e75>Tnn33zt}`8zt}:W1$mt}5>Tnn33 d2UN`8"d2UN:gڀUN5>TaZZ33`$ |6%:X 5>TbZZ33S.݆`$@|c?:=:EE5>T*4nn3309& B`829& B:d+01D B5>T04ZZ33@?S`$B?S::5>TV9ZZ33@?S`$B?S::5>TZ9nn33!\@'`8u :R  5>TͿ 33@U9:nBU9::|q :5>Tؿ VV33 @U9:n :BU9: Ƈ* ؍!K @U9:5>T VV336@U9:n :BU9:6_* _E.6@U9:5>TO nn33\QO3f`8^QO3f:^3f5>TO FF33/`://5>T VV33~T VV33 Q@U9:n :BU9: Q#o* " Q@U9:5>T ZZ33&^R]6`$񗘝"v:"v5>Tm33X5lJ$`LZ5lJ$:NK=lJ$5>Tn331I`Lw[I:5W5>TDrnn33+4`8KbݻA :GIƌ5>TIr331 iM`tHP!~:cG4kuI!~5>TLrZZ33vig`$U7Mz: 5>Tunn33S.݆`8@|c?:ѥ? 5>TuVV33 t/h` :Y ut =u t/h5>Tu33<z"`thY Q:̐q~ Q5>TZZ33Vi`$VY :  5>T33x9І`Lz9:%P5>T ZZ33ĕF`$F:9F5>TZZ33H$?I`$N:w/#:D5>TZZ33Y`$Il߅Ù@:S 5>TOvv33@U9:n@:BU9:Mb@H,)2@U9:@' :* 5>TOZZ33&kA`$&kA:&kA5>TPmZZ33t`$t:f t5>TWmZZ33<`$<:]5>TYmZZ33&^R]6`$񗘝"v:0 5>TZZ33$>`$D^޹\:޹\5>TZZ33N6C`$ <>L 6&:5>T ZZ33rJ`$)Ώ=:͟=5>TK VV33 @U9:n :BU9: Ƈ* ؍!K @U9:5>TR VV336@U9:n :BU9:6_* _E.6@U9:5>TY VV33 t/h` :Y ut =u t/h5>T\ ZZ33O,x,`$rr):1)5>T( ZZ33&`$eR0D:5>T- FF33<ʬ`:Y86<ʬ5>TgD 3385Rhd`L85Rhd:Kw Rhd5>T>>33OՑ'(`:ȃgcd_5>TZZ33Vi`$VY :ۻ05>T"nn33`!Ɔ`8b!:^\5>TU=ZZ33̯xsyφ`$2~a,:'5>TCb>>33\[` c(:[Ugq5>T>>33TZZ33Y`$Il߅Ù@:S5>T { ZZ33$w&D`$; :F5>T~ nn33}q`8}q:}q5>T~ VV33'6@U9:n :BU9:'6* '6@U9:5>T} nn33v`8Y Fo:T4 Foװ5>T VV33 @U9:n :BU9: Ƈ* ؍!K @U9:5>T FF33<ʬ`:Y86<ʬ5>T VV33 t/h` :Y ut =u t/h5>T ZZ33H$Mچ`$G[V : }Q5>T$ nn33-#`8-#:t{(#5>T6 ZZ33$>`$D^޹\:5],5>TeZZ33^U%`$^U%:PU%5>T$+ZZ33vig`$U7Mz:0ki5>T7+33h`Lh:Jnh5>TF+ZZ33S.݆`$@|c?:5>TN+FF33OՑ'(`:ȃgcdOՑ'(5>TX+VV33&@U9:n :BU9:&p* s&@U9:5>T 2ZZ33N6C`$ <>L 6&:bu%5>T.233P`t Ĉ̾:HyA!\R5>TH2nn33\[`8[Ugq: vV5>TxZZ33*p`$m1: 5>T1xVV33]@U9:n :BU9:]1* ?O]@U9:5>Ti{ZZ338D `$z/:s/5>T"ZZ33$w&D`$; :> 5>T>FF33^{r`:^{r-^{r5>TfZZ338 @2:.`$:5*2:.5>T5FF33:l`::lv:l5>TaZZ33H$Mچ`$G[V :[c5>T+VV33H$Y` J$Y:5>T+ZZ33$>`$D^޹\:a! 5>T%+VV336@U9:n :BU9:6_* _E.6@U9:5>T.+ZZ33䔘`$%c:<`5>TѿFF33S.o`:;:INvS.o5>Tynn33ą`8ƅ:W|15>TFF33T}I ZZ33Y`$Il߅Ù@:R5>T,j ZZ33&`$eR0D:'E}5>T5j FF33<ʬ`:Y86<ʬ5>T- nn33=1`8=1:LY15>Ty33&ި``&ި:iu0ި5>T33`tQ͑t.:T":0.5>T33ؖ%^`tA>:zԧ>5>TBFF33OՑ'(`:ȃgcdOՑ'(5>TC33V/4.`LV/4.:0/4.5>TI33\[` U n[Ugqnlq Mohameds iPad _device-info_tcplocal model=J72AP)|\[\[5>Tnn330f4`82f4: R!f45>TZZ33*p`$m1:5>TbVV33]@U9:n :BU9:]1* ?O]@U9:5>Tfnn33\$)o`8^$)o:hJ)o5>Tmvv33@U9:n@:BU9:Mb@H,)2@U9:@' :* 5>TopZZ33̯xsyφ`$2~a,:a,5>T33pVbӆ`rVb:)փv^6q/b5>TZZ33H$Mچ`$G[V :V 5>Tnn33Fe҆`8Fe:\Kr5>TZZ33uV`$xv7.: 5>T ZZ33rJ`$)Ώ=:bw 5>T VV33:̯x` :Jڝ9R:BU9:̯x5>Tf< nn33 "`8 ":u"5>T@ VV33'f@U9:n :BU9:'f* Ȧ'f@U9:5>TD ZZ33̯xsyφ`$2~a,: 5>TD FF33^{r`:^{r-^{r5>TJVZZ33`$ |6%: %5>T^ZZ33vig`$U7Mz:5>T_bb33\[`,[Ugq"#, U\[5>T_FF33hêچ`:haRhê5>T_ZZ33U!2`$~<:~<5>T_nn33x:!`80*:t_rj5>T933XD`t 'O:f(O 5>TA33\[``[Ugq:y)vVB`7gq5>TC33\[`L[Ugq:P)vVgq5>TF>>33\=`:| X5>TK33\[` U n[Ugqnlq Mohameds iPad _device-info_tcplocal model=J72AP)|\[\[5>TZZ33&G`$e4m :5>TZZ33Lmm`$xlF3[:[5>T'AVV33YH$Y` J$YY:Y5>TNN33vV\[`:vVN* 'vV5>TVV33]@U9:n :BU9:]1* ?O]@U9:5>TZZ33&`$eR0D: 5>TVNN33)\[`:)* Y`vip)5>T* FF33&q҆`:Ӻ> &q5>T ZZ33 -`${8k:k5>T ZZ33v`$Y Fo:KY 5>T VV33'f@U9:n :BU9:'f* Ȧ'f@U9:5>T# 33!jqJ`L!jqJ:iNqJ5>T$ >>33\qĆ`: ;-5>Tb* nn33Ώ$Qކ`8Ώ$Q:k*>5>Tl* nn335t`8.): 5>Tp* 33#S `L#S :Q(9pS 5>Ts* 33@j`L@j:ϖjvՇj5>T833hmQ``jmQ:R.Q5>T@ZZ33`$ |6%:`5>TBZZ33䔘`$%c:&5>TZZ33Vi`$VY : ` 5>Tnn33$8`8$8:V$85>T ZZ33&G`$e4m :@ 5>T ZZ33¶g`$ g:g5>TYZZ33rJ`$)Ώ=:a5>T33⇾A`LA:4KAA5>T{'ZZ33v`$Y Fo:Ka5>T'ZZ33&`$eR0D:5>T?-VV33]@U9:n :BU9:]1* ?O]@U9:5>TV33!jq̆`L!jq:\q5>TyVV33H$` J$:߆5>TZZ33&^R]6`$񗘝"v:2ik5>TFF338`:885>TŹVV33:t/h` :Y ut:ݟBU9:t/h5>T۹VV33:t/h` :* 4pJf.:6BU9:t/h5>TExx33t/h`BY ut"#Br zCt/h7MSFT 5.0 5>T>EVV33` @U9:n :BU9:` * HZ` @U9:5>TJEZZ33t/h`$Y ut:'B5>TdEnn33:a8`8:a8:N.֌85>TqEVV33It+}|` 9D\ЦI:PI5>TEZZ33t/h`$Y ut:&B5>TEZZ33t/h`$Y ut:'B5>TEVV33:t/h` :Y ut:ݟBU9:t/h5>T%MZZ33U!2`$~<:t5>T7MZZ33t/h`$Y ut:&B5>TEMWW33t/h`!Y ut6! MedBook5>TVMZZ33N6C`$ <>L 6&: 5>T`MFF33R}DĆ`:R}D xR}D5>TwMNN33tt/h`:taY ut5>TMNN33tt/h`:t@* Y ut5>TMNN33Jf.t/h`:Jf.Ҁ* 4pJf.5>TMFF33t/h`:Y utt/h5>TM33t/h``Y ut:Cqt Jf.5>TMZZ33Vi`$VY : hpktanon-master/tests/errors/bad_ip.pcap0000644000000000000000000000133112701423271017255 0ustar rootrootòd88{tn[a1&}B1{%s }6ڦz$$~#& %| ʤ-AA{{I/ Ɔ@%=%s%+AS%sKw rJT7sFQ66W.N`r%sRiloπ[ A\N f,11|_u@%s(qYQpu[K}KKmhkgPꉳ1]Q/J.`00~ R}!:~B N'YvS""łO\)nz00 BǚNт<^Wa++Z PP, HR[9 fէO#DM)b%s^sCt泊5xZUpktanon-master/tests/errors/bad_tcp.pcap0000644000000000000000000000154112701423271017436 0ustar rootrootòd55@hihgE( @ 7'"<3VsWŊzVV@hihgE( @ 7'"<:N]/8c.׽v=|44$@9z68pSS@hihgE( @ 7'"<4}d9;cy6mf(:cd9 crz_kPSS@hihgE( @ 7'"E3SS@hihgE( @ 7'"IK?77@hihgE( @ 7'" 0 } { puts $msg } } proc set_test { script_name } { global verbose global test set test [file rootname [ file tail $script_name]] if { [info exists test] && $verbose > 0 } { puts "================== $test ==================" } } proc match_io_logs {io_log_prefix} { global test global expect_out if {![regexp -line -lineanchor "^$io_log_prefix:" $expect_out(buffer)]} { fail "$test pktanon is not using correct io"} } # count number of frames in pcap file # # tshark output is something like this # =================================== # | IO Statistics | # | | # | Duration: 0.044 secs | # | Interval: 0.044 secs | # | | # | Col 1: FRAMES | # |---------------------------------| # | |1 | | # | Interval | FRAMES | | # |-------------------------| | # | 0.000 <> 0.044 | 9976 | | # =================================== proc count_frames_in_output { buff } { set frames_cntr unknown regexp -line -lineanchor {^\|\s0\.00?0?[ <>0-9.]+\s\|\s*(\d+)} $buff match frames_cntr regexp -line -lineanchor {^\|\s0\.000[ <>0-9.]+\|\s*(\d+)} $buff match frames_cntr return $frames_cntr } proc count_frames { pcap_file {filter FRAMES} } { set log_user_original [log_user -info] log_user 0 spawn -noecho tshark -r $pcap_file -q -z io,stat,0,$filter expect { eof {set buff $expect_out(buffer)} } log_user $log_user_original return [count_frames_in_output $buff] } proc count_stats_in_output { buff } { set found [regexp -line -lineanchor {^\|\s*0\.0+[ <>0-9.]+\|\s*(\d+)\s\|\s*\d+\s\|\s*(\d+)\s\|\s+\d+\s\|\s+(\d+)\s\|\s+\d+\s\|\s+(\d+)} $buff match ip_frames ip6_frames tcp_frames udp_frames ] if { $found != 1} { return {} } set rlist [list $ip_frames $ip6_frames $tcp_frames $udp_frames] return $rlist } proc count_stats { pcap_file } { # TODO log_user 0 spawn -noecho tshark -r $pcap_file -q -z io,stat,0,ip,ipv6,tcp,udp expect { eof {set buff $expect_out(buffer)} } count_stats_in_output $buff } proc compare_stats { expected_pcap_file result_pcap_file } { global test set log_user_original [log_user -info] log_user 0 if { ![file exists $expected_pcap_file] } { fail "$test - file $expected_pcap_file does not exist" } if { ![file exists $expected_pcap_file] } { fail "$test - file $result_pcap_file does not exist" } set stats_exp_pcap [count_stats $expected_pcap_file] set stats_out_pcap [count_stats $result_pcap_file] set stats_names {"ip4" "ip6" "tcp" "udp"} vputs "\nframes counter:" for {set i 0} {$i < [llength $stats_exp_pcap]} {incr i} { set stat_expected [lindex $stats_exp_pcap $i] set stat_result [lindex $stats_out_pcap $i] vputs " stat [lindex $stats_names $i]: expected - $stat_expected, result - $stat_result" if { $stat_expected != $stat_result} { fail "$test, wrong stat $i; expected: $stat_expected, result: $stat_result"} } log_user $log_user_original } proc test_sudo_nopwd { } { try { set results [exec sudo -nv ] set status 0 } trap CHILDSTATUS {results options} { set status [lindex [dict get $options -errorcode] 2] } return $status }pktanon-master/tests/lib/errorstestlib.exp0000644000000000000000000000162112701423271020047 0ustar rootroot### initialize variables set anon_profile [file join $script_dir profile.xml] set out_file [file join $script_dir .. out.pcap] set log_user_original [log_user -info] proc set_test { script_name } { global verbose global test set test [file rootname [ file tail $script_name]] if { [info exists test] && $verbose > 0 } { puts "================== $test ==================" } } proc expect_error_in_packet { pkt_num error_text} { global test; if { [catch { expect { "warning: error in packet $pkt_num: $error_text" {} eof {fail "$test on pkt_num = $pkt_num";} } } ] } {fail "$test on pkt_num = $pkt_num";} } proc expect_total_errors { total_errors } { global test; if { [catch { expect { -re "errors in packets:.$total_errors" {} eof {fail "$test on total errors = $total_errors";} } } ] } {fail "$test on total errors = $total_errors";} }pktanon-master/tests/lib/anonprimitives.exp0000644000000000000000000000041412701423271020212 0ustar rootrootputs "" puts "============================================================================" puts "=================== test suite for anon primitives =========================" puts "============================================================================" puts "" pktanon-master/tests/lib/transformations.exp0000644000000000000000000000041412701423271020374 0ustar rootrootputs "" puts "============================================================================" puts "=================== test suite for transformations =========================" puts "============================================================================" puts "" pktanon-master/tests/lib/transftestlib.exp0000644000000000000000000000442412701423271020034 0ustar rootrootproc vputs { msg } { global verbose if { $verbose > 0 } { puts $msg } } proc exec_pktanon { anon_profile in_file } { global test global PKTANON set base_dir [file dirname [info script]] set anon_profile [file join $base_dir $anon_profile] set in_file [file join $base_dir $in_file] set out_file [file join $base_dir ../out.pcap] spawn $PKTANON -c $anon_profile $in_file $out_file expect { "complete" { expect eof; vputs "\n"; return 1} eof { fail "$test: pktanon failed to run" return 0 } } } proc test_field {field value} { global test global verbose set log_user_original [log_user -info] log_user 0 set base_dir [file dirname [info script]] set out_file [file join $base_dir ../out.pcap] if { [string first 0x $value ] == 0 } { set value [expr $value] } spawn -noecho tshark -T fields -e $field -o tcp.analyze_sequence_numbers:False -r $out_file expect { $value { wait vputs "PASS: $test $field = $value" pass "$test $field" } eof { fail "$test: test for field $field failed: \n expected: $value\n actual: $expect_out(buffer)" } } log_user $log_user_original } proc test_checksum {proto} { global test global verbose set log_user_original [log_user -info] log_user 0 set base_dir [file dirname [info script]] set out_file [file join $base_dir ../out.pcap] spawn -noecho tshark -T fields -e $proto.checksum_good -o tcp.check_checksum:True -r $out_file expect { "1" { vputs "PASS: $test $proto.checksum_good" pass "$test $proto.checksum_good\n" } "0" { fail "$test: test for $proto.checksum failed: checksum is bad\n" } } log_user $log_user_original } proc test_checksum_r {proto} { global test global verbose set log_user_original [log_user -info] log_user 0 set base_dir [file dirname [info script]] set out_file [file join $base_dir ../out.pcap] spawn tshark -T fields -e $proto.checksum_bad -o tcp.check_checksum:True -r $out_file expect { "0" { vputs "PASS: $test !$proto.checksum_bad" pass "$test $proto.checksum_good\n" } "1" { fail "$test: test for !$proto.checksum_bad failed: checksum is bad\n" } } log_user $log_user_original }pktanon-master/tests/lib/anontestlib.exp0000644000000000000000000000235412701423271017472 0ustar rootrootproc anon_value_test { anon_class params src exp_dst } { global test global ANONTESTER spawn $ANONTESTER foreach param_name [dict keys $params] { set param_value [dict get $params $param_name] expect "enter command:" send "p $param_name $param_value\n" } expect "enter command:" send "a $anon_class\n" expect "enter command:" send "v $src\n" expect -re "result = {(.*)}" { set value [expr { $expect_out(1,string) }];} expect "enter command:" send "q" if { $value == $exp_dst } { pass $test } { fail "$test\texp: {$exp_dst}\n\t\tres: {$value}" } } proc anon_chain_test { params anons buffer expected_result } { global test global ANONTESTER spawn $ANONTESTER foreach param_name [dict keys $params] { set param_value [dict get $params $param_name] expect "enter command:" send "p $param_name $param_value\n" } foreach anon_class $anons { expect "enter command:" send "a $anon_class\n" } expect "enter command:" send "v $buffer\n" expect -re "result = {(.*)}" { set value [expr { $expect_out(1,string) }];} expect "enter command:" send "q" if { $value == $expected_result } { pass $test } { fail "$test\texp: {$expected_result}\n\t\tres: {$value}" } }pktanon-master/tests/lib/pktanon-runtime.exp0000644000000000000000000000041412701423271020276 0ustar rootrootputs "" puts "============================================================================" puts "=================== test suite for pktanon console app =====================" puts "============================================================================" puts "" pktanon-master/tests/config/0000755000000000000000000000000012701423271015125 5ustar rootrootpktanon-master/tests/config/unix.exp0000644000000000000000000000203712701423271016630 0ustar rootrootif {![info exists TOP_SRC_DIR]} { set pwd [pwd]; set pwd [file split $pwd]; set pktanon_dir list foreach path_element $pwd { lappend pktanon_dir $path_element if { $path_element == "pktanon"} { break;} } set pktanon_dir [file join {*}$pktanon_dir] } else { set pktanon_dir [file join [pwd] $TOP_SRC_DIR] } set pktanon_exec [file join $pktanon_dir pktanon] set top_tests_dir [file join $pktanon_dir tests] set anontester_exec [file join $top_tests_dir anontester] if { [file exists $pktanon_exec] } { set PKTANON $pktanon_exec} { error "path to pktanon not found, if TOP_SRC_DIR is not pktanon, set TOP_SRC_DIR variable" } if { [file exists $anontester_exec] } { set ANONTESTER $anontester_exec} { error "path to pktanon not found, if TOP_SRC_DIR is not pktanon, set TOP_SRC_DIR variable" } set TESTS_DIR $top_tests_dir if {$verbose > 0} { log_user 1 } unset pktanon_dir unset pktanon_exec unset anontester_exec unset top_tests_dir proc vputs { msg } { global verbose if { $verbose > 0 } { puts $msg } }pktanon-master/libpktanon/0000755000000000000000000000000012701423271014657 5ustar rootrootpktanon-master/libpktanon/transformations/0000755000000000000000000000000012701423271020110 5ustar rootrootpktanon-master/libpktanon/transformations/LinuxCookedCaptureTransformation.h0000644000000000000000000000245212701423271026763 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_LINUXCOOKEDCAPTURETRANSFORMATION_H #define PKTANON_LINUXCOOKEDCAPTURETRANSFORMATION_H # include "Transformation.h" namespace pktanon { # pragma pack (1) struct LINUX_SLL_HEADER { uint16_t packet_type; uint16_t hardware_type; uint16_t link_layer_addr_len; uint8_t link_layer_addr[8]; uint16_t protocol_type; }; # pragma pack () class LinuxCookedCaptureTransformation : public Transformation { public: LinuxCookedCaptureTransformation( AnonPrimitive* anon_packet_type, AnonPrimitive* anon_hardware_type, AnonPrimitive* anon_link_layer_addr, AnonPrimitive* anon_protocol_type ); virtual int transform(const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const noexcept; private: const AnonPrimitive* const anon_packet_type; const AnonPrimitive* const anon_hardware_type; const AnonPrimitive* const anon_link_layer_addr; const AnonPrimitive* const anon_protocol_type; }; } #endif // PKTANON_LINUXCOOKEDCAPTURETRANSFORMATION_H pktanon-master/libpktanon/transformations/EthernetPacketTransformation.h0000644000000000000000000000237612701423271026126 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ETHERNETPACKETTRANSFORMATION_H #define PKTANON__ETHERNETPACKETTRANSFORMATION_H #include "Transformation.h" #include "transformations/Addresses.h" namespace pktanon { # pragma pack (1) struct ETHERNET_HEADER { ETHERNET_HEADER() : type (0) {}; MAC_ADDR destination; MAC_ADDR source; uint16_t type; }; # pragma pack () class EthernetPacketTransformation : public Transformation { public: enum class PAD { ALWAYS, KEEP_LEN, NEVER, }; ~EthernetPacketTransformation(); EthernetPacketTransformation (AnonPrimitive* anon_dest_mac, AnonPrimitive* anon_source_mac, AnonPrimitive* anon_ethertype, PAD pad_mode); virtual int transform (const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const noexcept; private: const AnonPrimitive* const anon_dest_mac; const AnonPrimitive* const anon_source_mac; const AnonPrimitive* const anon_ethertype; const PAD pad_mode; }; } #endif // PKTANON_ETHERNETPACKETTRANSFORMATION_H pktanon-master/libpktanon/transformations/UdpPacketTransformation.h0000644000000000000000000000234412701423271025073 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_UDPPACKETTRANSFORMATION_H #define PKTANON_UDPPACKETTRANSFORMATION_H # include "transformations/PseudoheaderAwareTransformation.h" namespace pktanon { # pragma pack (1) struct UDP_HEADER { uint16_t source_port; uint16_t destination_port; uint16_t length; uint16_t checksum; }; # pragma pack () class UdpPacketTransformation : public PseudoheaderAwareTransformation { public: virtual ~UdpPacketTransformation(); UdpPacketTransformation(AnonPrimitive* anon_source_port, AnonPrimitive* anon_destination_port); virtual int transform(const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const noexcept; virtual int calculate_checksum(uint8_t* source_address, uint8_t* destination_address, unsigned address_length, uint16_t ip_length, uint8_t* sink_next_header) const noexcept; private: AnonPrimitive* anon_source_port; AnonPrimitive* anon_destination_port; }; } #endif // PKTANON_UDPPACKETTRANSFORMATION_H pktanon-master/libpktanon/transformations/UdpLitePacketTransformation.h0000644000000000000000000000214112701423271025704 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_UDPLITEPACKETTRANSFORMATION_H #define PKTANON_UDPLITEPACKETTRANSFORMATION_H # include namespace pktanon { # pragma pack (1) struct UDPLITE_HEADER { uint16_t source_port; uint16_t destination_port; uint16_t checksum_coverage; uint16_t checksum; }; # pragma pack () class UdpLitePacketTransformation : public Transformation { public: virtual ~UdpLitePacketTransformation(); UdpLitePacketTransformation( AnonPrimitive* anon_source_port, AnonPrimitive* anon_destination_port, AnonPrimitive* anon_checksum_coverage ); size_t transform(uint8_t* source_buffer, uint8_t* destination_buffer) const; private: AnonPrimitive* anon_source_port; AnonPrimitive* anon_destination_port; AnonPrimitive* anon_checksum_coverage; }; } #endif // PKTANON_UDPLITEPACKETTRANSFORMATION_H pktanon-master/libpktanon/transformations/IPv4PacketTransformation.h0000644000000000000000000000614212701423271025125 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_IPV4TRANSFORMATION_H #define PKTANON_IPV4TRANSFORMATION_H # include "Transformation.h" namespace pktanon { # pragma pack (1) struct IPv4_HEADER { uint8_t version__header_len; uint8_t tos; uint16_t total_length; uint16_t identification; uint16_t flags__fragment_offset; uint8_t ttl; uint8_t protocol; uint16_t header_checksum; in_addr source_ip; in_addr destination_ip; }; # pragma pack () class IPv4PacketTransformation : public Transformation { public: ~IPv4PacketTransformation(); /** * If you have a procedure with 10 parameters, you probably missed some. */ IPv4PacketTransformation ( AnonPrimitive* anon_tos, AnonPrimitive* anon_identification, AnonPrimitive* anon_flags, AnonPrimitive* anon_fragment_offset, AnonPrimitive* anon_ttl, AnonPrimitive* anon_protocol, AnonPrimitive* anon_source_ip, AnonPrimitive* anon_destination_ip, AnonPrimitive* anon_options, bool recalculate_ipv4_header_checksum, bool recalculate_payload_checksums ); virtual int transform (const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const noexcept; static uint8_t get_version (IPv4_HEADER* header) { return ( (header->version__header_len & 0xF0) >> 4); } static void set_version (uint8_t version, IPv4_HEADER* header) { header->version__header_len = ( (version & 0xF) << 4) | (get_header_len (header) / 4); } static uint8_t get_header_len (IPv4_HEADER* header) { return ( (header->version__header_len & 0x0F) << 2); } static void set_header_len (uint16_t len, IPv4_HEADER* header) { header->version__header_len = (get_version (header) << 4) | ( ( (len / 4) & 0xF)); } static uint16_t get_flags (IPv4_HEADER* header) { return ( (header->flags__fragment_offset & 0xE000) >> 13); } static void set_flags (uint16_t flags, IPv4_HEADER* header) { header->flags__fragment_offset = ( (uint16_t) (flags & 0x07) << 13) | get_fragment_offset (header); } static uint16_t get_fragment_offset (IPv4_HEADER* header) { return ( (header->flags__fragment_offset & 0x1FFF)) ; } static void set_fragment_offset (uint16_t fragment_offset, IPv4_HEADER* header) { header->flags__fragment_offset = ( (get_flags (header) & 0x07) << 13) | (0xD & fragment_offset); } private: const AnonPrimitive* const anon_tos; const AnonPrimitive* const anon_identification; const AnonPrimitive* const anon_flags; const AnonPrimitive* const anon_fragment_offset; const AnonPrimitive* const anon_ttl; const AnonPrimitive* const anon_protocol; const AnonPrimitive* const anon_source_ip; const AnonPrimitive* const anon_destination_ip; const AnonPrimitive* const anon_options; const bool recalculate_ipv4_header_checksum; const bool recalculate_payload_checksums; }; } #endif // PKTANON_IPV4TRANSFORMATION_H pktanon-master/libpktanon/transformations/PayloadTransformation.cpp0000644000000000000000000000165212701423271025140 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "PayloadTransformation.h" using namespace pktanon; PayloadTransformation::~PayloadTransformation() { delete payload_anon; } PayloadTransformation::PayloadTransformation ( AnonPrimitive* payload_anon ) : payload_anon ( payload_anon ) {} namespace pktanon { int PayloadTransformation::transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length ) const noexcept { // size_t new_length = transform_field (payload_anon, source_buffer, destination_buffer, max_packet_length); size_t new_length = payload_anon->anonimyze ( source_buffer, destination_buffer, max_packet_length ); return new_length; } } pktanon-master/libpktanon/transformations/UdpLitePacketConfigurator.cpp0000644000000000000000000000051512701423271025676 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "UdpLitePacketConfigurator.h" using namespace pktanon; pktanon-master/libpktanon/transformations/PseudoheaderAwareTransformation.h0000644000000000000000000000201612701423271026577 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_PSEUDOHEADER_AWARETRANSFORMATION_H # define PKTANON_PSEUDOHEADER_AWARETRANSFORMATION_H # include "Transformation.h" namespace pktanon { class PseudoheaderAwareTransformation : public Transformation { public: virtual ~PseudoheaderAwareTransformation(); public: // virtual size_t verify_checksum(uint8_t protocol, uint16_t length, uint8_t* source_address, uint8_t* destination_address, size_t address_length, uint8_t* source_next_header); virtual int calculate_checksum(uint8_t* source_address, uint8_t* destination_address, unsigned address_length, uint16_t ip_length, uint8_t* sink_next_header) const noexcept = 0; }; inline PseudoheaderAwareTransformation::~PseudoheaderAwareTransformation() {} } #endif // PKTANON_TRANSFORMATION_H pktanon-master/libpktanon/transformations/Addresses.h0000644000000000000000000000071212701423271022176 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_ADDRESSES_H #define PKTANON_ADDRESSES_H namespace pktanon { static const unsigned int ETHER_ADDR_LEN = 6; struct MAC_ADDR { uint8_t addr[ETHER_ADDR_LEN]; }; } #endif pktanon-master/libpktanon/transformations/IPv4PacketTransformation.cpp0000644000000000000000000001625212701423271025463 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "IPv4PacketTransformation.h" #include "PseudoheaderAwareTransformation.h" #include "ErrorCodes.h" //------------------------------------------------------------ #include "debug.h" #include using namespace pktanon; IPv4PacketTransformation::~IPv4PacketTransformation() { delete anon_tos; delete anon_identification; delete anon_flags; delete anon_fragment_offset; delete anon_ttl; delete anon_protocol; delete anon_source_ip; delete anon_destination_ip; delete anon_options; } IPv4PacketTransformation::IPv4PacketTransformation ( AnonPrimitive* anon_tos, AnonPrimitive* anon_identification, AnonPrimitive* anon_flags, AnonPrimitive* anon_fragment_offset, AnonPrimitive* anon_ttl, AnonPrimitive* anon_protocol, AnonPrimitive* anon_source_ip, AnonPrimitive* anon_destination_ip, AnonPrimitive* anon_options, bool recalculate_ipv4_header_checksum, bool recalculate_payload_checksums ) : anon_tos (anon_tos), anon_identification (anon_identification), anon_flags (anon_flags), anon_fragment_offset (anon_fragment_offset), anon_ttl (anon_ttl), anon_protocol (anon_protocol), anon_source_ip (anon_source_ip), anon_destination_ip (anon_destination_ip), anon_options (anon_options), recalculate_ipv4_header_checksum(recalculate_ipv4_header_checksum), recalculate_payload_checksums(recalculate_payload_checksums) { } /// convert fields to host byte order static inline void prepare_input_header (IPv4_HEADER* input_header) { Transformation::ntoh16 (input_header->total_length); Transformation::ntoh16 (input_header->identification); Transformation::ntoh16 (input_header->flags__fragment_offset); Transformation::ntoh16 (input_header->header_checksum); } /// convert fields back to network byte order and recalculate header_checksum static inline void prepare_output_header (IPv4_HEADER* output_header, bool recalculate_checksum = true) { Transformation::hton16 (output_header->total_length); Transformation::hton16 (output_header->identification); Transformation::hton16 (output_header->flags__fragment_offset); // TODO set to random value on error? output_header->header_checksum = 0; if (recalculate_checksum) { // recalculate header checksum Checksum checksum; checksum.update (output_header, sizeof (IPv4_HEADER)); output_header->header_checksum = checksum.final(); Transformation::hton16(output_header->header_checksum); // TRACE(std::hex << output_header->header_checksum); } Transformation::hton16 (output_header->header_checksum); } int IPv4PacketTransformation::transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const noexcept { size_t ip_header_length = sizeof (IPv4_HEADER); if (max_packet_length < ip_header_length) { return error_codes::ip4_header_too_short; } IPv4_HEADER* input_header = (IPv4_HEADER*) source_buffer; IPv4_HEADER* output_header = (IPv4_HEADER*) destination_buffer; prepare_input_header (input_header); // save required fields size_t original_header_length = get_header_len (input_header); uint8_t transport_protocol = input_header->protocol; // transform packet set_version (4, output_header); transform_field (anon_tos, &input_header->tos, &output_header->tos, sizeof (uint8_t)); transform_field (anon_identification, &input_header->identification, &output_header->identification, sizeof (uint16_t)); uint16_t flags = get_flags (input_header); uint16_t flags_output = 0; transform_field (anon_flags, &flags, &flags_output, sizeof (uint16_t)); // TRACEV(std::hex << flags_output); set_flags (flags_output, output_header); // TRACEV(std::hex << get_flags (output_header)); // TRACEV(std::hex << output_header->flags__fragment_offset); // TRACEV(std::hex << ((uint16_t) (flags & 0x07) << 13)); transform_field (anon_ttl, &input_header->ttl, &output_header->ttl, sizeof (uint8_t)); transform_field (anon_protocol, &input_header->protocol, &output_header->protocol, sizeof (uint8_t)); transform_field (anon_source_ip, &input_header->source_ip, &output_header->source_ip, sizeof (in_addr)); transform_field (anon_destination_ip, &input_header->destination_ip, &output_header->destination_ip, sizeof (in_addr)); // if the header length field of packet has too small value, we can't go any further // TODO anonymize checksum ?!? if (original_header_length < ip_header_length) { set_header_len (original_header_length, output_header); prepare_output_header (output_header, false); return ADD_LENGTH_VALUE (error_codes::ip4_header_lenght_value_too_short, ip_header_length); } // transform options and set header length size_t options_length = get_header_len (input_header) - ip_header_length; size_t new_options_length = transform_field (anon_options, source_buffer + ip_header_length, destination_buffer + ip_header_length, options_length); uint16_t new_header_length = (uint16_t) (ip_header_length + new_options_length); set_header_len (new_header_length, output_header); //TODO fragment offset // check if this is a fragment, if this is a fragment -> stop transformation uint16_t fragment_offset = get_fragment_offset(input_header); set_fragment_offset(fragment_offset, output_header); if (fragment_offset != 0) { prepare_output_header (output_header, true); return ADD_LENGTH_VALUE(error_codes::ip4_packet_fragmented, ip_header_length); // TODO call payload transformation? } size_t original_payload_length = input_header->total_length - original_header_length; // transform next layer const Transformation* next_transformation = Transformation::getTransformationProtocol (transport_protocol); ssize_t transformed_payload_length = next_transformation->transform (source_buffer + original_header_length, destination_buffer + ip_header_length, original_payload_length); if (transformed_payload_length < 0) { output_header->total_length = (uint16_t (GET_LENGTH_VALUE (transformed_payload_length) + get_header_len (output_header))); prepare_output_header (output_header, false); return ADD_LENGTH_VALUE (transformed_payload_length, new_header_length); } // set payload length output_header->total_length = (uint16_t (transformed_payload_length + get_header_len (output_header))); // if the payload needs to calculate checksum with pseudoheader - feed ip information into it if (recalculate_payload_checksums && Transformation::hasPseudoHeader (transport_protocol)) { const PseudoheaderAwareTransformation* p_trans = static_cast (next_transformation); p_trans->calculate_checksum ( (uint8_t*) &output_header->source_ip, (uint8_t*) &output_header->destination_ip, sizeof (in_addr), output_header->total_length - ip_header_length, destination_buffer + ip_header_length); } prepare_output_header (output_header, recalculate_ipv4_header_checksum); return new_header_length + transformed_payload_length; } pktanon-master/libpktanon/transformations/UdpPacketTransformation.cpp0000644000000000000000000001137112701423271025426 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "UdpPacketTransformation.h" #include "ErrorCodes.h" #include #include #include #include using namespace pktanon; UdpPacketTransformation::~UdpPacketTransformation() { delete anon_source_port; delete anon_destination_port; } UdpPacketTransformation::UdpPacketTransformation(AnonPrimitive* anon_source_port, AnonPrimitive* anon_destination_port): anon_source_port(anon_source_port), anon_destination_port(anon_destination_port) {} static inline void prepare_input_header(UDP_HEADER* input_header) { Transformation::ntoh16(input_header->source_port); Transformation::ntoh16(input_header->destination_port); Transformation::ntoh16(input_header->length); Transformation::ntoh16(input_header->checksum); } static inline void prepare_output_header(UDP_HEADER* output_header) { UdpPacketTransformation::hton16(output_header->source_port); UdpPacketTransformation::hton16(output_header->destination_port); UdpPacketTransformation::hton16(output_header->length); output_header->checksum = 0; } int UdpPacketTransformation::transform( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const noexcept { const size_t udp_header_length = sizeof(UDP_HEADER); if(unlikely(max_packet_length - udp_header_length < 0)) return error_codes::udp_header_too_short; UDP_HEADER* input_header = (UDP_HEADER*) source_buffer; UDP_HEADER* output_header = (UDP_HEADER*) destination_buffer; prepare_input_header(input_header); //TODO what to do if max_packet_length < length? transform_field(anon_source_port, &input_header->source_port, &output_header->source_port, sizeof(uint16_t)); transform_field(anon_destination_port, &input_header->destination_port, &output_header->destination_port, sizeof(uint16_t)); // if input_header->length is bigger than ip payload length than packet is malicious // don't anonymize further if (unlikely(input_header->length > max_packet_length)) // length field is length of both header and data! { output_header->length = input_header->length; prepare_output_header(output_header); return ADD_LENGTH_VALUE(error_codes::udp_header_length_too_long, udp_header_length); } const auto payload_transformation = Transformation::getPayloadTransformation(); const size_t new_payload_length = payload_transformation->transform(source_buffer+udp_header_length, destination_buffer+udp_header_length, input_header->length); output_header->length = udp_header_length + new_payload_length; prepare_output_header(output_header); return udp_header_length + new_payload_length; } int UdpPacketTransformation::calculate_checksum( uint8_t* source_address, uint8_t* destination_address, unsigned int address_length, uint16_t ip_length, uint8_t* sink_next_header ) const noexcept { UDP_HEADER* udp_header = (UDP_HEADER*) sink_next_header; size_t header_length = sizeof(UDP_HEADER); size_t udp_length = ntohs(udp_header->length); size_t payload_length = udp_length - header_length; assert(udp_header->checksum == 0); uint16_t padded_protocol = htons(IPPROTO_UDP & 0x00ff); Checksum checksum; // source address checksum.update(source_address, address_length); // destination address checksum.update(destination_address, address_length); // zero, protocol and udp length checksum.update(&udp_header->length, 2); checksum.update(&padded_protocol, 2); // add udp header checksum.update(udp_header, header_length); // add packet payload checksum.update(sink_next_header+header_length, payload_length); // complete udp_header->checksum = checksum.final(); // hton16(udp_header->checksum); return 0; } // udp checksum for ipv4: // pseudoheader: // 0 7 8 15 16 23 24 31 // +--------+--------+--------+--------+ // | source address | // +--------+--------+--------+--------+ // | destination address | // +--------+--------+--------+--------+ // | zero |protocol| UDP length | // +--------+--------+--------+--------+ // udp packet // +--------+--------+--------+--------+ // | Source | Destination | // | Port | Port | // +--------+--------+--------+--------+ // | | | // | Length | Checksum | // +--------+--------+--------+--------+ // | // | data octets ... // +---------------- ... pktanon-master/libpktanon/transformations/PayloadTransformation.h0000644000000000000000000000137612701423271024610 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_PAYLOADTRANSFORMATION_H #define PKTANON_PAYLOADTRANSFORMATION_H # include "Transformation.h" namespace pktanon { class PayloadTransformation : public Transformation { public: ~PayloadTransformation(); PayloadTransformation(AnonPrimitive* payload_anon); virtual int transform(const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const noexcept; private: AnonPrimitive* payload_anon; }; } #endif // PKTANON_PAYLOADTRANSFORMATION_H pktanon-master/libpktanon/transformations/IcmpPacketTransformation.cpp0000644000000000000000000000272012701423271025564 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "IcmpPacketTransformation.h" #include using namespace pktanon; IcmpPacketTransformation::~IcmpPacketTransformation() { delete anon_type; delete anon_code; delete anon_misc; } IcmpPacketTransformation::IcmpPacketTransformation( AnonPrimitive* anon_type, AnonPrimitive* anon_code, AnonPrimitive* anon_misc ) : anon_type(anon_type), anon_code(anon_code), anon_misc(anon_misc) { } int IcmpPacketTransformation::transform( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const noexcept { ICMP_HEADER* input_header = (ICMP_HEADER*) source_buffer; ICMP_HEADER* output_header = (ICMP_HEADER*) destination_buffer; ntoh32(input_header->misc); transform_field(anon_type,&input_header->type,&output_header->type,sizeof(uint8_t)); transform_field(anon_code,&input_header->code,&output_header->code,sizeof(uint8_t)); transform_field(anon_misc,&input_header->misc,&output_header->misc,sizeof(uint32_t)); hton32(output_header->misc); Checksum checksum; checksum.update(output_header, sizeof(ICMP_HEADER)); output_header->checksum = checksum.final(); // hton16(output_header->checksum); return sizeof(ICMP_HEADER); } pktanon-master/libpktanon/transformations/VlanTagTransformation.cpp0000644000000000000000000000422012701423271025075 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "VlanTagTransformation.h" using namespace pktanon; VlanTagTransformation::~VlanTagTransformation() { delete anon_priority; delete anon_cfi; delete anon_vlan_id; delete anon_type; } VlanTagTransformation::VlanTagTransformation ( AnonPrimitive* anon_priority, AnonPrimitive* anon_cfi, AnonPrimitive* anon_vlan_id, AnonPrimitive* anon_type ) : anon_priority (anon_priority), anon_cfi (anon_cfi), anon_vlan_id (anon_vlan_id), anon_type (anon_type) {} int VlanTagTransformation::transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const noexcept { size_t vlan_tag_length = sizeof (VLAN_TAG); VLAN_TAG* input_header = (VLAN_TAG*) source_buffer; VLAN_TAG* output_header = (VLAN_TAG*) destination_buffer; ntoh16 (input_header->priority_cfi_id); ntoh16 (input_header->type); uint16_t ethertype = input_header->type; // priority uint16_t priority = get_priority (input_header); uint16_t priority_anon = 0; transform_field (anon_priority, &priority, &priority_anon, sizeof (uint16_t)); set_priority (output_header, priority_anon); // cfi uint16_t cfi = get_cfi (input_header); uint16_t cfi_anon = 0; transform_field (anon_cfi, &cfi, &cfi_anon, sizeof (uint16_t)); set_cfi (output_header, cfi_anon); // vlan_id uint16_t vlan_id = get_vlan_id (input_header); uint16_t vlan_id_anon = 0; transform_field (anon_vlan_id, &vlan_id, &vlan_id_anon, sizeof (uint16_t)); set_vlan_id (output_header, vlan_id); // (ether)type transform_field (anon_type, &input_header->type, &output_header->type, sizeof (uint16_t)); const Transformation* next_transformation = Transformation::getTransformationEthertype (ethertype); vlan_tag_length += next_transformation->transform (source_buffer + vlan_tag_length, destination_buffer + vlan_tag_length, max_packet_length - vlan_tag_length); return vlan_tag_length; } pktanon-master/libpktanon/transformations/ArpPacketTransformation.cpp0000644000000000000000000000543212701423271025421 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "transformations/ArpPacketTransformation.h" #include "ErrorCodes.h" #include "debug.h" namespace pktanon { ArpPacketTransformation::~ArpPacketTransformation() { delete anon_hardware_type; delete anon_protocol_type; delete anon_hardware_size; delete anon_protocol_size; delete anon_opcode; delete anon_sender_mac; delete anon_sender_ip; delete anon_target_mac; delete anon_target_ip; } ArpPacketTransformation::ArpPacketTransformation( AnonPrimitive* anon_hardware_type, AnonPrimitive* anon_protocol_type, AnonPrimitive* anon_hardware_size, AnonPrimitive* anon_protocol_size, AnonPrimitive* anon_opcode, AnonPrimitive* anon_sender_mac, AnonPrimitive* anon_sender_ip, AnonPrimitive* anon_target_mac, AnonPrimitive* anon_target_ip): anon_hardware_type(anon_hardware_type), anon_protocol_type(anon_protocol_type), anon_hardware_size(anon_hardware_size), anon_protocol_size(anon_protocol_size), anon_opcode(anon_opcode), anon_sender_mac(anon_sender_mac), anon_sender_ip(anon_sender_ip), anon_target_mac(anon_target_mac), anon_target_ip(anon_target_ip) { } int ArpPacketTransformation::transform( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const noexcept { if (max_packet_length < sizeof(ARP_HEADER)) { HB(); return error_codes::arp_packet_too_short; }; // TODO check hardware_type && hardware_size? ARP_HEADER* input_header = (ARP_HEADER*) source_buffer; ARP_HEADER* output_header = (ARP_HEADER*) destination_buffer; transform_field(anon_hardware_type, &input_header->hardware_type, &output_header->hardware_type, sizeof(uint16_t)); transform_field(anon_protocol_type, &input_header->protocol_type, &output_header->protocol_type, sizeof(uint16_t)); transform_field(anon_hardware_size, &input_header->hardware_size, &output_header->hardware_size, sizeof(uint8_t)); transform_field(anon_protocol_size, &input_header->protocol_size, &output_header->protocol_size, sizeof(uint8_t)); transform_field(anon_opcode, &input_header->opcode, &output_header->opcode, sizeof(uint16_t)); transform_field(anon_sender_mac, &input_header->sender_mac, &output_header->sender_mac, sizeof(MAC_ADDR)); transform_field(anon_sender_ip, &input_header->sender_ip, &output_header->sender_ip, sizeof(in_addr)); transform_field(anon_target_mac, &input_header->target_mac, &output_header->target_mac, sizeof(MAC_ADDR)); transform_field(anon_target_ip, &input_header->target_ip, &output_header->target_ip, sizeof(in_addr)); return sizeof(ARP_HEADER); } } pktanon-master/libpktanon/transformations/ErrorCodes.cpp0000644000000000000000000000124712701423271022667 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "ErrorCodes.h" namespace pktanon { const char* const error_strings[][3] = { { "ethernet header too short", 0, 0 }, { "arp header too short", 0 , 0 }, { "ip4 header too short", "bogus ip4 header length", "packet is fragmented" }, { "ip6 header too short", 0, 0 }, { "tcp header too short", 0, 0 }, { "udp header too short", "bad udp length", 0 } }; } pktanon-master/libpktanon/transformations/VlanTagTransformation.h0000644000000000000000000000365712701423271024557 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_VLANTAGTRANSFORMATION_H #define PKTANON_VLANTAGTRANSFORMATION_H # include "Transformation.h" namespace pktanon { # pragma pack (1) struct VLAN_TAG { uint16_t priority_cfi_id;// priority, canonical format identifier, vlan identifier uint16_t type;// next header, based on ethernet protocol fields }; # pragma pack () class VlanTagTransformation : public Transformation { public: ~VlanTagTransformation(); VlanTagTransformation ( AnonPrimitive* anon_priority, AnonPrimitive* anon_cfi, AnonPrimitive* anon_vlan_id, AnonPrimitive* anon_type ); virtual int transform (const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const noexcept; private: static uint16_t get_priority (VLAN_TAG* header) { return ( (header->priority_cfi_id & 0xE000) >> 13); } static void set_priority (VLAN_TAG* header, uint16_t priority) { header->priority_cfi_id = (priority & 0xE000) | (header->priority_cfi_id & 0x1FFF); } static uint16_t get_cfi (VLAN_TAG* header) { return ( (header->priority_cfi_id & 0x1000) != 0); } static void set_cfi (VLAN_TAG* header, uint16_t cfi) { header->priority_cfi_id = (cfi & 0x1000) | (header->priority_cfi_id & 0xEFFF); } static uint16_t get_vlan_id (VLAN_TAG* header) { return (header->priority_cfi_id & 0x0FFF); } static void set_vlan_id (VLAN_TAG* header, uint16_t vlan_id) { header->priority_cfi_id = (vlan_id & 0x0FFF) | (header->priority_cfi_id & 0xF000); } AnonPrimitive* anon_priority; AnonPrimitive* anon_cfi; AnonPrimitive* anon_vlan_id; AnonPrimitive* anon_type; }; } #endif // PKTANON_VLANTAGTRANSFORMATION_H pktanon-master/libpktanon/transformations/IPv6PacketTransformation.h0000644000000000000000000000553612701423271025135 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_IPV6PACKETTRANSFORMATION_H #define PKTANON_IPV6PACKETTRANSFORMATION_H # include "Transformation.h" namespace pktanon { struct IPv6_HEADER { uint32_t version__traffic_class__flow_label; // configuration item Ipv6Trafficclass and Ipv6Flowlabel uint16_t payload_length; uint8_t next_header; uint8_t hop_limit; // configuration item Ipv6Hoplimit in6_addr source_addr; // configuration item Ipv6Sourceaddr in6_addr destination_addr; // configuration item Ipv6Destaddr }; class IPv6PacketTransformation : public Transformation { public: ~IPv6PacketTransformation(); IPv6PacketTransformation( AnonPrimitive* anon_traffic_class, AnonPrimitive* anon_flow_label, AnonPrimitive* anon_next_header, AnonPrimitive* anon_hop_limit, AnonPrimitive* anon_source_addr, AnonPrimitive* anon_destination_addr, bool recalculate_payload_checksums ); virtual int transform(const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const noexcept; private: static uint32_t get_version(IPv6_HEADER* header) { return (header->version__traffic_class__flow_label & 0xF0000000) >> 28; } static void set_version(uint32_t version, IPv6_HEADER* header) { header->version__traffic_class__flow_label = ((version & 0xF) << 28) | (header->version__traffic_class__flow_label & 0x0FFFFFFF); } static uint32_t get_traffic_class(IPv6_HEADER* header) { return (header->version__traffic_class__flow_label & 0x0FF00000) >> 20; }; static void set_traffic_class(uint32_t traffic_class, IPv6_HEADER* header) { header->version__traffic_class__flow_label = ((traffic_class << 20) & 0x0FF00000) | (header->version__traffic_class__flow_label & 0xF00FFFFF); } static uint32_t get_flow_label(IPv6_HEADER* header) { return (header->version__traffic_class__flow_label & 0x000FFFFF); }; static void set_flow_label(uint32_t flow_label, IPv6_HEADER* header) { header->version__traffic_class__flow_label = (flow_label & 0x000FFFFF) | (header->version__traffic_class__flow_label & 0xFFF00000); }; const AnonPrimitive* const anon_traffic_class; const AnonPrimitive* const anon_flow_label; const AnonPrimitive* const anon_next_header; const AnonPrimitive* const anon_hop_limit; const AnonPrimitive* const anon_source_addr; const AnonPrimitive* const anon_destination_addr; const bool recalculate_payload_checksums; }; } #endif // PKTANON_IPV6PACKETTRANSFORMATION_H pktanon-master/libpktanon/transformations/UdpLitePacketTransformation.cpp0000644000000000000000000000452312701423271026245 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "UdpLitePacketTransformation.h" using namespace pktanon; UdpLitePacketTransformation::~UdpLitePacketTransformation() { delete anon_source_port; delete anon_destination_port; delete anon_checksum_coverage; } UdpLitePacketTransformation::UdpLitePacketTransformation( AnonPrimitive* anon_source_port, AnonPrimitive* anon_destination_port, AnonPrimitive* anon_checksum_coverage): anon_source_port(anon_source_port), anon_destination_port(anon_destination_port), anon_checksum_coverage(anon_checksum_coverage) { } size_t UdpLitePacketTransformation::transform(uint8_t* source_buffer, uint8_t* destination_buffer) const { UDPLITE_HEADER* input_header = (UDPLITE_HEADER*) source_buffer; UDPLITE_HEADER* output_header = (UDPLITE_HEADER*) destination_buffer; ntoh16(input_header->source_port); ntoh16(input_header->destination_port); ntoh16(input_header->checksum_coverage); transform_field(anon_source_port, &input_header->source_port, &output_header->source_port, sizeof(uint16_t)); transform_field(anon_destination_port, &input_header->destination_port, &output_header->destination_port, sizeof(uint16_t)); transform_field(anon_checksum_coverage, &input_header->checksum_coverage, &output_header->checksum_coverage, sizeof(uint16_t)); output_header->checksum = 0; auto payload_transformation = Transformation::getPayloadTransformation(); size_t new_payload_length = payload_transformation->transform(source_buffer+sizeof(UDPLITE_HEADER), destination_buffer+sizeof(UDPLITE_HEADER)); uint16_t new_packet_length = sizeof(UDPLITE_HEADER) + new_payload_length; //checksum coverage must be at least header output_header->checksum_coverage = std::max (output_header->checksum_coverage, sizeof(UDPLITE_HEADER)); // checksum_coverage must be smaller than packet length output_header->checksum_coverage = std::min(output_header->checksum_coverage, new_packet_length); hton16(output_header->source_port); hton16(output_header->destination_port); hton16(output_header->checksum_coverage); return sizeof(UDPLITE_HEADER) + new_payload_length; } pktanon-master/libpktanon/transformations/EthernetPacketTransformation.cpp0000644000000000000000000000532112701423271026452 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "EthernetPacketTransformation.h" #include "ErrorCodes.h" //----------------------------------------------------------------------------- #include "optmacros.h" #include "debug.h" namespace pktanon { EthernetPacketTransformation::~EthernetPacketTransformation() { delete anon_dest_mac; delete anon_source_mac; delete anon_ethertype; }; EthernetPacketTransformation::EthernetPacketTransformation (pktanon::AnonPrimitive* anon_dest_mac, pktanon::AnonPrimitive* anon_source_mac, pktanon::AnonPrimitive* anon_ethertype, pktanon::EthernetPacketTransformation::PAD pad_mode) : anon_dest_mac (anon_dest_mac), anon_source_mac (anon_source_mac), anon_ethertype (anon_ethertype), pad_mode (pad_mode) {} int EthernetPacketTransformation::transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const noexcept { const size_t ether_header_len = sizeof (ETHERNET_HEADER); if (unlikely (max_packet_length < ether_header_len)) { return error_codes::ethernet_header_too_short; } ETHERNET_HEADER* input_header = (ETHERNET_HEADER*) source_buffer; ETHERNET_HEADER* output_header = (ETHERNET_HEADER*) destination_buffer; ntoh16 (input_header->type); const uint16_t ether_type = input_header->type; transform_field (anon_dest_mac, &input_header->destination, &output_header->destination, sizeof (MAC_ADDR)); transform_field (anon_source_mac, &input_header->source, &output_header->source, sizeof (MAC_ADDR)); transform_field (anon_ethertype, &input_header->type, &output_header->type, sizeof (uint16_t)); hton16 (output_header->type); const Transformation* next_transformation = Transformation::getTransformationEthertype (ether_type); ssize_t payload_len = next_transformation->transform ( source_buffer + ether_header_len, destination_buffer + ether_header_len, max_packet_length - ether_header_len); if (unlikely (payload_len < 0)) return ADD_LENGTH_VALUE (payload_len, ether_header_len); if (pad_mode != PAD::NEVER) { // TODO? here we should pad to the minimum 46 length, but if the original // packet is smaller than 46, than padding can create overflow in the // destination_buffer. if (payload_len < 46 && ( pad_mode == PAD::ALWAYS || (max_packet_length >= ether_header_len + 46))) { payload_len = 46; } } return ether_header_len + payload_len; } } pktanon-master/libpktanon/transformations/UdpLitePacketConfigurator.h0000644000000000000000000000074712701423271025352 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_UDPLITEPACKETCONFIGURATOR_H #define PKTANON_UDPLITEPACKETCONFIGURATOR_H namespace pktanon { class UdpLitePacketConfigurator { public: static void }; } #endif // PKTANON_UDPLITEPACKETCONFIGURATOR_H pktanon-master/libpktanon/transformations/IPv6PacketTransformation.cpp0000644000000000000000000001062212701423271025460 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "IPv6PacketTransformation.h" #include "PseudoheaderAwareTransformation.h" #include "ErrorCodes.h" #include "optmacros.h" #include "debug.h" using namespace pktanon; IPv6PacketTransformation::~IPv6PacketTransformation() { delete anon_traffic_class; delete anon_flow_label; delete anon_hop_limit; delete anon_source_addr; delete anon_destination_addr; } IPv6PacketTransformation::IPv6PacketTransformation ( AnonPrimitive* anon_traffic_class, AnonPrimitive* anon_flow_label, AnonPrimitive* anon_next_header, AnonPrimitive* anon_hop_limit, AnonPrimitive* anon_source_addr, AnonPrimitive* anon_destination_addr, bool recalculate_payload_checksums ) : anon_traffic_class ( anon_traffic_class ), anon_flow_label ( anon_flow_label ), anon_next_header ( anon_next_header ), anon_hop_limit ( anon_hop_limit ), anon_source_addr ( anon_source_addr ), anon_destination_addr ( anon_destination_addr ), recalculate_payload_checksums ( recalculate_payload_checksums ) { } int IPv6PacketTransformation::transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const noexcept { const size_t ipv6_header_length = sizeof ( IPv6_HEADER ); if ( unlikely ( max_packet_length < ipv6_header_length ) ) return error_codes::ip6_header_too_short; IPv6_HEADER* input_header = ( IPv6_HEADER* ) source_buffer; IPv6_HEADER* output_header = ( IPv6_HEADER* ) destination_buffer; const uint8_t next_header = input_header->next_header; ntoh32 ( input_header->version__traffic_class__flow_label ); ntoh16 ( input_header->payload_length ); //version is always 6 set_version ( 6u, output_header ); //anonymize traffic class uint32_t input_traffic_class = get_traffic_class ( input_header ); uint32_t output_traffic_class = 0; transform_field ( anon_traffic_class, &input_traffic_class, &output_traffic_class, sizeof ( uint32_t ) ); set_traffic_class ( output_traffic_class, output_header ); //anonymize flow label uint32_t input_flow_label = get_flow_label ( input_header ); uint32_t output_flow_label = 0; transform_field ( anon_flow_label, &input_flow_label, &output_flow_label, sizeof ( uint32_t ) ); set_flow_label ( output_flow_label, output_header ); //payload_length is recalculated below //rest of the fields transform_field ( anon_next_header, &input_header->next_header, &output_header->next_header, sizeof ( uint8_t ) ); transform_field ( anon_hop_limit, &input_header->hop_limit, &output_header->hop_limit, sizeof ( uint8_t ) ); transform_field ( anon_source_addr, &input_header->source_addr, &output_header->source_addr, sizeof ( in6_addr ) ); transform_field ( anon_destination_addr, &input_header->destination_addr, &output_header->destination_addr, sizeof ( in6_addr ) ); // payload size_t payload_length; const Transformation* next_transformation; next_transformation = Transformation::getTransformationProtocol ( next_header ); payload_length = next_transformation->transform ( source_buffer + ipv6_header_length, destination_buffer + ipv6_header_length, input_header->payload_length ); if ( payload_length < 0 ) { output_header->payload_length = GET_LENGTH_VALUE ( payload_length ); hton32 ( output_header->version__traffic_class__flow_label ); hton16 ( output_header->payload_length ); return ADD_LENGTH_VALUE ( payload_length, ipv6_header_length ); }; output_header->payload_length = payload_length; // if the payload needs to calculate checksum with pseudoheader - feed ip information into it if ( recalculate_payload_checksums && Transformation::hasPseudoHeader ( next_header ) ) { const PseudoheaderAwareTransformation* p_trans = static_cast ( next_transformation ); p_trans->calculate_checksum ( ( uint8_t* ) &output_header->source_addr, ( uint8_t* ) &output_header->destination_addr, sizeof ( in6_addr ), output_header->payload_length, destination_buffer + ipv6_header_length ); } hton32 ( output_header->version__traffic_class__flow_label ); hton16 ( output_header->payload_length ); return ipv6_header_length + payload_length; } pktanon-master/libpktanon/transformations/ICMPv6PacketTransformation.cpp0000644000000000000000000001035612701423271025704 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "ICMPv6PacketTransformation.h" #include #include using namespace pktanon; ICMPv6PacketTransformation::~ICMPv6PacketTransformation() { delete anon_type; delete anon_code; delete anon_misc; delete anon_target_address; } ICMPv6PacketTransformation::ICMPv6PacketTransformation ( AnonPrimitive* anon_type, AnonPrimitive* anon_code, AnonPrimitive* anon_misc, AnonPrimitive* anon_target_address ) : anon_type (anon_type), anon_code (anon_code), anon_misc (anon_misc), anon_target_address (anon_misc) { } int ICMPv6PacketTransformation::transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const noexcept { if (max_packet_length < sizeof (ICMPv6_HEADER)) return -1; ICMPv6_HEADER* input_header = (ICMPv6_HEADER*) source_buffer; ICMPv6_HEADER* output_header = (ICMPv6_HEADER*) destination_buffer; uint8_t type = input_header->type; transform_field (anon_type, &input_header->type, &output_header->type, sizeof (uint8_t)); transform_field (anon_code, &input_header->code, &output_header->code, sizeof (uint8_t)); size_t message_length = 0; if (type == ICMPv6_Types::NDP_NSM) { message_length += nsm_transform (source_buffer + sizeof (uint32_t), destination_buffer + sizeof (uint32_t), max_packet_length - sizeof (uint32_t)); } else if (type == ICMPv6_Types::NDP_NAM) { message_length += nam_transform (source_buffer + sizeof (uint32_t), destination_buffer + sizeof (uint32_t), max_packet_length - sizeof (uint32_t)); } else { transform_field (anon_misc, &input_header->misc, &output_header->misc, sizeof (uint32_t)); message_length = sizeof (uint32_t); } if (message_length < 0) return message_length; // Checksum checksum; // output_header->checksum = 0; // checksum.update(destination_buffer, sizeof(uint32_t) + message_length); // output_header->checksum = checksum.final(); // // hton16(output_header->checksum); return sizeof (ICMPv6_HEADER) + message_length; } int ICMPv6PacketTransformation::nam_transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const { if (max_packet_length < sizeof (ICMPv6_NAM_HEADER)) return -1; ICMPv6_NAM_HEADER* input_header = (ICMPv6_NAM_HEADER*) source_buffer; ICMPv6_NAM_HEADER* output_header = (ICMPv6_NAM_HEADER*) destination_buffer; // TODO what to do with RSO? output_header->rso__reserved = 0; transform_field (anon_target_address, &input_header->target_address, &output_header->target_address, sizeof (in6_addr)); return sizeof (ICMPv6_NAM_HEADER); } int ICMPv6PacketTransformation::nsm_transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const { if (max_packet_length < sizeof (ICMPv6_NSM_HEADER)) return -1; ICMPv6_NSM_HEADER* input_header = (ICMPv6_NSM_HEADER*) source_buffer; ICMPv6_NSM_HEADER* output_header = (ICMPv6_NSM_HEADER*) destination_buffer; output_header->reserved = 0; transform_field (anon_target_address, &input_header->target_address, &output_header->target_address, sizeof (in6_addr)); return sizeof (ICMPv6_NSM_HEADER); } int ICMPv6PacketTransformation::calculate_checksum ( uint8_t* source_address, uint8_t* destination_address, unsigned int address_length, uint16_t ip_length, uint8_t* sink_next_header ) const noexcept { ICMPv6_HEADER* output_header = (ICMPv6_HEADER*) sink_next_header; // set checksum to 0 output_header->checksum = 0; Checksum checksum; // IPv6 pseudoheader checksum.update (source_address, address_length); checksum.update (destination_address, address_length); checksum.update (&ip_length, sizeof (uint16_t)); uint16_t padded_protocol = htons (IPPROTO_ICMPV6 & 0x00ff); checksum.update (&padded_protocol, sizeof (uint16_t)); // icmp6 packet of length as in ipv6 header checksum.update(sink_next_header, ip_length); // get final checksum output_header->checksum = checksum.final(); hton16 (output_header->checksum); return 0; } pktanon-master/libpktanon/transformations/ErrorCodes.h0000644000000000000000000000371712701423271022340 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ # ifndef PKTANON__ERRORCODES_H # define PKTANON__ERRORCODES_H namespace pktanon { /** * error code structure: left-most bit '1' to indicate negative value, next 7 bits are packet number, next 8 bits * is error code within the packet, next 2 / 6 bytes is length of the transformed value. * * this is a preparation for dynamically adding error codes in TransformationConfigurator */ extern const char* const error_strings[][3]; enum error_codes : int { ethernet_header_too_short = 0x8000 << sizeof ( int ) * 8 - 16, arp_packet_too_short = 0x8100 << sizeof ( int ) * 8 - 16, ip4_header_too_short = 0x8200 << sizeof ( int ) * 8 - 16, ip4_header_lenght_value_too_short = 0x8201 << sizeof ( int ) * 8 - 16, ip4_packet_fragmented = 0x8202 << sizeof ( int ) * 8 - 16, ip6_header_too_short = 0x8300 << sizeof ( int ) * 8 - 16, tcp_header_too_short = 0x8400 << sizeof ( int ) * 8 - 16, udp_header_too_short = 0x8500 << sizeof ( int ) * 8 - 16, udp_header_length_too_long = 0x8501 << sizeof ( int ) * 8 - 16, linux_llc_header_too_short = 0x8600 << sizeof ( int ) * 8 - 16, }; inline long int GET_ERROR_CODE ( long int error_value ) { return error_value >> sizeof ( int ) * 8 - 16; } inline const char* GET_ERROR_STRING ( int error_value ) { auto pkt_error_code = error_value >> sizeof ( int ) * 8 - 16; auto pkt = ( pkt_error_code >> 8 ) & 0x6f; auto error_code = pkt_error_code & 0xff; return error_strings[pkt][error_code]; } inline int GET_LENGTH_VALUE ( int error_value ) { return error_value & 0xffff; } inline int ADD_LENGTH_VALUE ( int error_value, unsigned int length_value ) { // assume that length_value < max_short return error_value + length_value; } } # endif pktanon-master/libpktanon/transformations/IcmpPacketTransformation.h0000644000000000000000000000202012701423271025222 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_ICMPPACKETTRANSFORMATION_H #define PKTANON_ICMPPACKETTRANSFORMATION_H # include "Transformation.h" namespace pktanon { # pragma pack (1) struct ICMP_HEADER { uint8_t type; uint8_t code; uint16_t checksum; uint32_t misc; } ; # pragma pack () class IcmpPacketTransformation : public Transformation { public: virtual ~IcmpPacketTransformation(); IcmpPacketTransformation( AnonPrimitive* anon_type, AnonPrimitive* anon_code, AnonPrimitive* anon_misc ); virtual int transform(const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const noexcept; private: AnonPrimitive* anon_type; AnonPrimitive* anon_code; AnonPrimitive* anon_misc; }; } #endif // PKTANON_ICMPPACKETTRANSFORMATION_H pktanon-master/libpktanon/transformations/DefaultTransformationsConfigurator.h0000644000000000000000000000362112701423271027344 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__DEFAULTTRANSFORMATIONSCONFIGURATOR_H #define PKTANON__DEFAULTTRANSFORMATIONSCONFIGURATOR_H #include namespace pktanon { class DefaultTransformationsConfigurator { public: static void configure_ethernet_packet(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); static void configure_vlan_tag(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); static void configure_linux_sll_packet(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); static void configure_arp_packet(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); static void configure_ipv4_packet(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); static void configure_ipv6_packet(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); static void configure_icmp_packet(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); static void configure_icmpv6_packet(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); static void configure_tcp_packet(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); static void configure_udp_packet(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); static void configure_payload_packet(const PktAnonConfig::PacketConfig& packet_config, const pktanon::PktAnonConfig& config); }; } #endif // PKTANON_DEFAULTTRANSFORMATIONSCONFIGURATOR_H pktanon-master/libpktanon/transformations/TcpPacketTransformation.h0000644000000000000000000000573512701423271025100 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_TCPPACKETTRANSFORMATION_H #define PKTANON_TCPPACKETTRANSFORMATION_H # include "transformations/PseudoheaderAwareTransformation.h" namespace pktanon { # pragma pack (1) struct TCP_HEADER { uint16_t source_port; // source port. configuration item TcpSourceport uint16_t dest_port; // destination port. configuration item TcpDestport uint32_t seq_num; // sequence number. configuration item TcpSeqnum uint32_t ack_num; // acknowledgement number. configuration item TcpAcknum uint16_t header_len__flags; //header length 4 bit, 6 bit reserved, 6 bit flags. configuration item TcpFlags. none for header length uint16_t window_size; // window size. configuration item TcpWindowsize uint16_t checksum; // checksum uint16_t urgent_pointer; // urgent pointer. configuration item TcpUrgentpnt }; # pragma pack () class TcpPacketTransformation : public PseudoheaderAwareTransformation { public: virtual ~TcpPacketTransformation(); TcpPacketTransformation( AnonPrimitive* anon_source_port, AnonPrimitive* anon_dest_port, AnonPrimitive* anon_seq_num, AnonPrimitive* anon_ack_num, AnonPrimitive* anon_flags, AnonPrimitive* anon_window_size, AnonPrimitive* anon_urgent_pointer, AnonPrimitive* anon_options ); virtual int transform(const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const noexcept; virtual int calculate_checksum(uint8_t* source_address, uint8_t* destination_address, unsigned address_length, uint16_t length, uint8_t* sink_next_header) const noexcept; private: static uint16_t get_header_len(TCP_HEADER* header) { return (((header->header_len__flags & 0xF000) >> 12) << 2); } static void set_header_len(uint16_t header_len, TCP_HEADER* header) { header->header_len__flags = (((header_len / 4) & 0xF) << 12) | (get_flags(header) & 0x3F); } static uint16_t get_flags(TCP_HEADER* header) { return ((header->header_len__flags & 0x3F )); } static void set_flags(uint16_t flags, TCP_HEADER* header) { header->header_len__flags = (((get_header_len(header) / 4) & 0xF) << 12) | (flags & 0x3F); } AnonPrimitive* anon_source_port; AnonPrimitive* anon_dest_port; AnonPrimitive* anon_seq_num; AnonPrimitive* anon_ack_num; AnonPrimitive* anon_flags; AnonPrimitive* anon_window_size; AnonPrimitive* anon_urgent_pointer; AnonPrimitive* anon_options; }; } #endif // PKTANON_TCPPACKETTRANSFORMATION_H pktanon-master/libpktanon/transformations/TransformationsConfigurator.cpp0000644000000000000000000001215512701423271026374 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "TransformationsConfigurator.h" //---------------------------------------------------------------------------------------------- #include "AnonFactory.h" #include "anonprimitives/AnonPrimitive.h" #include "transformations/PseudoheaderAwareTransformation.h" #include #include #include "log.h" #include "debug.h" using namespace pktanon; TransformationsConfigurator& TransformationsConfigurator::instance() { static TransformationsConfigurator tc; return tc; } void TransformationsConfigurator::register_configurator(std::string config_name, TransformationsConfigurator::Configurator configurator) { instance().supported_packets.emplace(config_name, configurator); } void TransformationsConfigurator::configure_packet_transformations( const PktAnonConfig& config ) { // PRINT("REGISTERED PACKETS[" << supported_packets.size() << "]"); // for (auto& packet:supported_packets) // PRINT(packet.first); for (const auto& packet_config: config.get_packets()) { auto it = supported_packets.find(packet_config.get_protocol()); if (it != supported_packets.end()) { (*it).second(packet_config, config); } else { _plg_error("unknown packet: " << packet_config.get_protocol()); } } } void TransformationsConfigurator::configure_transformations_lookups() { //TODO remove me! if (!payload_transformation) { throw std::runtime_error("payload packet must be configured"); } // linktype lookup is done only once, so just copy the data there Transformation::linktype_lookup_table = std::move(included_linktypes); // configure lookup by ethertype { size_t table_size; auto hash_fn = generate_phf(included_ethertypes, table_size); PHT ethetype_lookup_table(hash_fn, table_size); for (auto included_ethertype : included_ethertypes) { ethetype_lookup_table.insert(included_ethertype.first, included_ethertype.second); } Transformation::ethetype_lookup_table =std::move(ethetype_lookup_table); //!MOVE! } //configure lookup by protocol { std::unordered_map checksum_protocols; checksum_protocols.reserve(included_protocols.size()); for (auto included_protocol : included_protocols) { if (dynamic_cast(included_protocol.second) != nullptr) { checksum_protocols.emplace(included_protocol.first, included_protocol.second); } } // IPv6 Hop-by-Hop Option has proto value 0 and is not checksum aware // p is 31, the value 31 * 16 is not valid proto value and will map to 0 checksum_protocols.emplace(prime * 16, nullptr); size_t table_size; size_t cp_table_size; auto hash_fn = generate_phf(included_protocols, table_size); auto cp_hash_fn = generate_phf(checksum_protocols, cp_table_size); PHT protocol_lookup_table(hash_fn, table_size); PHT checksums_lookup_table(cp_hash_fn, cp_table_size); for (auto included_protocol : included_protocols) { protocol_lookup_table.insert(included_protocol.first, included_protocol.second); // TRACEV((int)included_protocol.first); // if (dynamic_cast(included_protocol.second) != nullptr) // { // checksums_lookup_table.insert(included_protocol.first, true); // } } for (auto checksum_protocol : checksum_protocols) { checksums_lookup_table.insert(checksum_protocol.first, true); // TRACEV(checksum_protocol.first); // TRACEV(cp_hash_fn(checksum_protocol.first)); } // TRACEV(cp_hash_fn(0)); Transformation::protocol_lookup_table = std::move(protocol_lookup_table); Transformation::checksums_lookup_table = std::move(checksums_lookup_table); } // add payload packet Transformation::payloadTransformation = payload_transformation; } AnonPrimitive* TransformationsConfigurator::configure_packet_field(const string& field, const PktAnonConfig::PacketConfig& packet_config) { if (!packet_config.has_field(field)) return nullptr; auto& anons_config = packet_config.get_anon_config(field); if (anons_config.size() == 0) throw std::runtime_error("empty configuration for field: " + field); //TODO exception or nullptr _plg_verbose ("\t\t" << field << "[0]: " << anons_config[0].get_anon_class()); AnonPrimitive* first = AnonFactory::create_anon(anons_config[0].get_anon_class(), anons_config[0].get_anon_params()); AnonPrimitive* prev = first; for (int j = 1; j < anons_config.size() ; j++) { _plg_verbose("\t\t" << field << "[" << j <<"]: " << anons_config[j].get_anon_class()); AnonPrimitive* next = AnonFactory::create_anon(anons_config[j].get_anon_class(), anons_config[j].get_anon_params()); prev->setNext(next); prev = next; } return first; } pktanon-master/libpktanon/transformations/LinuxCookedCaptureTransformation.cpp0000644000000000000000000000554712701423271027326 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "LinuxCookedCaptureTransformation.h" #include "optmacros.h" #include "transformations/ErrorCodes.h" #include using namespace pktanon; LinuxCookedCaptureTransformation::LinuxCookedCaptureTransformation( AnonPrimitive* anon_packet_type, AnonPrimitive* anon_hardware_type, AnonPrimitive* anon_link_layer_addr, AnonPrimitive* anon_protocol_type ) : anon_packet_type(anon_packet_type), anon_hardware_type(anon_hardware_type), anon_link_layer_addr(anon_link_layer_addr), anon_protocol_type(anon_protocol_type) { } int LinuxCookedCaptureTransformation::transform( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const noexcept { const size_t linux_sll_header_len = sizeof (LINUX_SLL_HEADER); if (unlikely (max_packet_length < linux_sll_header_len)) { return error_codes::linux_llc_header_too_short; } LINUX_SLL_HEADER* input_header = (LINUX_SLL_HEADER*) source_buffer; LINUX_SLL_HEADER* output_header = (LINUX_SLL_HEADER*) destination_buffer; // network->host byteorder ntoh16(input_header->packet_type); ntoh16(input_header->hardware_type); ntoh16(input_header->link_layer_addr_len); ntoh16(input_header->protocol_type); const uint16_t ether_type = input_header->protocol_type; // anonymize packet transform_field (anon_packet_type, &input_header->packet_type, &output_header->packet_type, sizeof (uint16_t)); transform_field (anon_hardware_type,&input_header->hardware_type, &output_header->hardware_type, sizeof (uint16_t)); output_header->link_layer_addr_len = transform_field (anon_link_layer_addr,&input_header->link_layer_addr,&output_header->link_layer_addr, std::min((uint16_t)8, input_header->link_layer_addr_len)); transform_field (anon_protocol_type, &input_header->protocol_type, &output_header->protocol_type, sizeof (uint16_t)); // host->network byteorder hton16(output_header->packet_type); hton16(output_header->hardware_type); hton16(output_header->link_layer_addr_len); hton16(output_header->protocol_type); // call next transformation const Transformation* next_transformation = Transformation::getTransformationEthertype (ether_type); ssize_t payload_len = next_transformation->transform ( source_buffer + linux_sll_header_len, destination_buffer + linux_sll_header_len, max_packet_length - linux_sll_header_len); if (unlikely (payload_len < 0)) return ADD_LENGTH_VALUE (payload_len, linux_sll_header_len); return linux_sll_header_len + payload_len; } pktanon-master/libpktanon/transformations/Transformation.cpp0000644000000000000000000000166712701423271023634 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "Transformation.h" #include namespace pktanon { Transformation::~Transformation() = default; std::unordered_map Transformation::linktype_lookup_table; PHT Transformation::ethetype_lookup_table; PHT Transformation::protocol_lookup_table; PHT Transformation::checksums_lookup_table; Transformation* Transformation::payloadTransformation = nullptr; Transformation* Transformation::getTransformationLinktype(uint32_t linktype) { auto iter = linktype_lookup_table.find(linktype); if (iter != linktype_lookup_table.end()) return (*iter).second; return nullptr; } } pktanon-master/libpktanon/transformations/ArpPacketTransformation.h0000644000000000000000000000501112701423271025057 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ARPPACKETTRANSFORMATION_H #define PKTANON__ARPPACKETTRANSFORMATION_H #include "Transformation.h" #include #include namespace pktanon { # pragma pack (1) struct ARP_HEADER { uint16_t hardware_type; // 0x0001 for ethernet. configuration item ArpHardwaretp uint16_t protocol_type; // 0x0800 for ip. configuration item ArpPrototp uint8_t hardware_size; // 0x6 for ethernet mac addressess. configuration item ArpHardwareaddrlen uint8_t protocol_size; // 0x4 for ip addresses. configuration item ArpProtoaddrlen uint16_t opcode; // see defines below. configuration item ArpOpcode MAC_ADDR sender_mac; // configuration item ArpSourcemac in_addr sender_ip; // configuration item ArpSourceip MAC_ADDR target_mac; // configuration item ArpDestmac in_addr target_ip; // configuration item ArpDestip }; # pragma pack () class ArpPacketTransformation : public Transformation { public: ~ArpPacketTransformation(); ArpPacketTransformation ( AnonPrimitive* anon_hardware_type, AnonPrimitive* anon_protocol_type, AnonPrimitive* anon_hardware_size, AnonPrimitive* anon_protocol_size, AnonPrimitive* anon_opcode, AnonPrimitive* anon_sender_mac, AnonPrimitive* anon_sender_ip, AnonPrimitive* anon_target_mac, AnonPrimitive* anon_target_ip ); virtual int transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length ) const noexcept; private: const AnonPrimitive* const anon_hardware_type; const AnonPrimitive* const anon_protocol_type; const AnonPrimitive* const anon_hardware_size; const AnonPrimitive* const anon_protocol_size; const AnonPrimitive* const anon_opcode; const AnonPrimitive* const anon_sender_mac; const AnonPrimitive* const anon_sender_ip; const AnonPrimitive* const anon_target_mac; const AnonPrimitive* const anon_target_ip; }; } #endif // PKTANON_ARPPACKETTRANSFORMATION_H pktanon-master/libpktanon/transformations/TransformationsConfigurator.h0000644000000000000000000000452212701423271026040 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_TRANSFORMATIONCONFIGURATOR_H #define PKTANON_TRANSFORMATIONCONFIGURATOR_H # include # include # include # include namespace pktanon { #define REGISTER_PACKET(PACKET, FUNC) \ TransformationsConfigurator::regger PACKET##regger(#PACKET, FUNC); class AnonPrimitive; class Transformation; class TransformationsConfigurator { public: typedef std::function Configurator; static TransformationsConfigurator& instance(); // static void destroy(); static void register_configurator(std::string config_name, Configurator configurator); struct regger { regger(std::string config_name, Configurator configurator) { register_configurator(config_name, configurator);}; }; private: TransformationsConfigurator() : payload_transformation(nullptr) {}; //---------------------------------------- public: void configure_packet_transformations(const PktAnonConfig& config); void configure_transformations_lookups(); void add_linktype(uint32_t linktype, Transformation* trans) {included_linktypes.emplace(linktype, trans);}; void add_ethertype(uint16_t ethertype, Transformation* trans) {included_ethertypes.emplace(ethertype, trans);}; void add_protocol(uint8_t protocol, Transformation* trans) {included_protocols.emplace(protocol, trans);}; void add_payload_transformation(Transformation* payload_transformation) {this->payload_transformation = payload_transformation;}; public: static AnonPrimitive* configure_packet_field(const std::string& field, const PktAnonConfig::PacketConfig& packet_config); private: static TransformationsConfigurator* _instance; std::unordered_map supported_packets; std::unordered_map included_linktypes; std::unordered_map included_ethertypes; std::unordered_map included_protocols; Transformation* payload_transformation; }; } #endif // PKTANON_TRANSFORMATIONCONFIGURATOR_H pktanon-master/libpktanon/transformations/TcpPacketTransformation.cpp0000644000000000000000000001476612701423271025437 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "TcpPacketTransformation.h" #include "ErrorCodes.h" #include "Checksum.h" #include "debug.h" using namespace pktanon; TcpPacketTransformation::~TcpPacketTransformation() { delete anon_source_port; delete anon_dest_port; delete anon_seq_num; delete anon_ack_num; delete anon_flags; delete anon_window_size; delete anon_urgent_pointer; delete anon_options; } TcpPacketTransformation::TcpPacketTransformation ( AnonPrimitive* anon_source_port, AnonPrimitive* anon_dest_port, AnonPrimitive* anon_seq_num, AnonPrimitive* anon_ack_num, AnonPrimitive* anon_flags, AnonPrimitive* anon_window_size, AnonPrimitive* anon_urgent_pointer, AnonPrimitive* anon_options ) : anon_source_port (anon_source_port), anon_dest_port (anon_dest_port), anon_seq_num (anon_seq_num), anon_ack_num (anon_ack_num), anon_flags (anon_flags), anon_window_size (anon_window_size), anon_urgent_pointer (anon_urgent_pointer), anon_options (anon_options) { } int TcpPacketTransformation::transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) const noexcept { size_t tcp_header_length = sizeof (TCP_HEADER); if (max_packet_length < tcp_header_length) return error_codes::tcp_header_too_short; TCP_HEADER* input_header = (TCP_HEADER*) source_buffer; TCP_HEADER* output_header = (TCP_HEADER*) destination_buffer; ntoh16 (input_header->source_port); ntoh16 (input_header->dest_port); ntoh32 (input_header->seq_num); ntoh32 (input_header->ack_num); ntoh16 (input_header->header_len__flags); ntoh16 (input_header->window_size); ntoh16 (input_header->checksum); ntoh16 (input_header->urgent_pointer); // anon_source_port->anonimyze(&input_header->source_port, &output_header->source_port, sizeof(uint16_t)); transform_field (anon_source_port,&input_header->source_port,&output_header->source_port, sizeof (uint16_t)); transform_field (anon_dest_port, &input_header->dest_port, &output_header->dest_port, sizeof (uint16_t)); transform_field (anon_seq_num, &input_header->seq_num, &output_header->seq_num, sizeof (uint32_t)); transform_field (anon_ack_num, &input_header->ack_num, &output_header->ack_num, sizeof (uint32_t)); uint16_t flags_original = get_flags (input_header); uint16_t flags_transformed = 0; transform_field (anon_flags, &flags_original, &flags_transformed, sizeof (uint16_t)); set_flags (flags_transformed, output_header); transform_field (anon_window_size, &input_header->window_size, &output_header->window_size, sizeof (uint16_t)); transform_field (anon_urgent_pointer, &input_header->urgent_pointer, &output_header->urgent_pointer, sizeof (uint16_t)); size_t options_length = get_header_len (input_header) - tcp_header_length; size_t new_options_length = transform_field (anon_options, source_buffer + tcp_header_length, destination_buffer + tcp_header_length, options_length); set_header_len (tcp_header_length + new_options_length, output_header); // transform payload packet auto payload_transformation = Transformation::getPayloadTransformation(); size_t new_payload_length = payload_transformation->transform (source_buffer + get_header_len(input_header), destination_buffer + get_header_len(output_header), max_packet_length - get_header_len(input_header) ); hton16 (output_header->source_port); hton16 (output_header->dest_port); hton32 (output_header->seq_num); hton32 (output_header->ack_num); hton16 (output_header->header_len__flags); hton16 (output_header->window_size); hton16 (output_header->checksum); hton16 (output_header->urgent_pointer); return tcp_header_length + new_options_length + new_payload_length; } int TcpPacketTransformation::calculate_checksum ( uint8_t* source_address, uint8_t* destination_address, unsigned int address_length, uint16_t length, uint8_t* sink_next_header ) const noexcept { TCP_HEADER* tcp_header = (TCP_HEADER*) sink_next_header; tcp_header->checksum = 1; size_t header_length = sizeof (TCP_HEADER); size_t payload_length = length - header_length; // TRACE(header_length); // TRACE(payload_length); // TRACE(length); hton16 (length); tcp_header->checksum = 0; uint16_t padded_protocol = htons (IPPROTO_TCP & 0x00ff); Checksum checksum; // source address checksum.update (source_address, address_length); // destination address checksum.update (destination_address, address_length); // zero, protocol and length checksum.update (&length, 2); checksum.update (&padded_protocol, 2); // add udp header checksum.update (tcp_header, header_length); // add packet payload checksum.update (sink_next_header + header_length, payload_length); // complete tcp_header->checksum = checksum.final(); // hton16 (tcp_header->checksum); return 0; } // TCP Checksum Calculation // pseudoheader // +--------+--------+--------+--------+ // | Source Address | // +--------+--------+--------+--------+ // | Destination Address | // +--------+--------+--------+--------+ // | zero | PTCL | TCP Length | // +--------+--------+--------+--------+ // tcp header // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Source Port | Destination Port | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Sequence Number | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Acknowledgment Number | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Data | |U|A|P|R|S|F| | // | Offset| Reserved |R|C|S|S|Y|I| Window | // | | |G|K|H|T|N|N| | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Checksum | Urgent Pointer | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Options | Padding | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | data | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ pktanon-master/libpktanon/transformations/Transformation.h0000644000000000000000000000425212701423271023272 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_TRANSFORMATION_H #define PKTANON_TRANSFORMATION_H #include #include #include "anonprimitives/AnonPrimitive.h" #include "PHT.h" namespace pktanon { class Transformation { public: virtual ~Transformation(); static Transformation* getTransformationLinktype ( uint32_t linktype ); static const Transformation* const getTransformationEthertype ( uint16_t ethertype ) { return ethetype_lookup_table.lookup ( ethertype, payloadTransformation ); }; static const Transformation* const getTransformationProtocol ( uint8_t protocol ) { return protocol_lookup_table.lookup ( protocol, payloadTransformation ); }; static Transformation* getPayloadTransformation() {return payloadTransformation;}; static bool hasPseudoHeader ( uint8_t protocol ) { return checksums_lookup_table.contains ( protocol ); }; private: friend class TransformationsConfigurator; static std::unordered_map linktype_lookup_table; static PHT ethetype_lookup_table; static PHT protocol_lookup_table; static PHT checksums_lookup_table; static Transformation* payloadTransformation; public: virtual int transform ( const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length ) const noexcept = 0; static size_t transform_field ( AnonPrimitive const * anon, const void* input_buffer, void* output_buffer, size_t field_orig_length ) { if ( anon == nullptr ) return 0; return anon->anonimyze ( input_buffer, output_buffer, field_orig_length ); }; static void ntoh16 ( uint16_t& val ) { val = ntohs ( val );} static void ntoh32 ( uint32_t& val ) { val = ntohl ( val );} static void hton16 ( uint16_t& val ) { val = htons ( val );} static void hton32 ( uint32_t& val ) { val = htonl ( val );} }; } #endif // PKTANON_TRANSFORMATION_H pktanon-master/libpktanon/transformations/ICMPv6PacketTransformation.h0000644000000000000000000000423012701423271025343 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_ICMPV6PACKETTRANSFORMATION_H #define PKTANON_ICMPV6PACKETTRANSFORMATION_H # include "Transformation.h" #include "PseudoheaderAwareTransformation.h" namespace pktanon { # pragma pack (1) struct ICMPv6_HEADER { uint8_t type; uint8_t code; uint16_t checksum; uint32_t misc; } ; # pragma pack () # pragma pack (1) struct ICMPv6_NSM_HEADER //NSM = neighbor solicitation message { uint32_t reserved; in6_addr target_address; } ; # pragma pack () # pragma pack (1) struct ICMPv6_NAM_HEADER //NAM = neighbor advertisement message { uint32_t rso__reserved; in6_addr target_address; } ; # pragma pack () enum ICMPv6_Types{ NDP_NSM = 135, // Neighbor Solicitation (NDP) NDP_NAM = 136, // Neighbor Advertisement (NDP) }; class ICMPv6PacketTransformation : public PseudoheaderAwareTransformation { public: virtual ~ICMPv6PacketTransformation(); ICMPv6PacketTransformation ( AnonPrimitive* anon_type, AnonPrimitive* anon_code, AnonPrimitive* anon_misc, AnonPrimitive* anon_target_address ); virtual int transform (const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const noexcept; virtual int calculate_checksum (uint8_t* source_address, uint8_t* destination_address, unsigned address_length, uint16_t ip_length, uint8_t* sink_next_header) const noexcept; private: int nsm_transform(const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const; int nam_transform(const uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length) const; AnonPrimitive* anon_type; AnonPrimitive* anon_code; AnonPrimitive* anon_misc; AnonPrimitive* anon_target_address; }; } #endif // PKTANON_ICMPV6PACKETTRANSFORMATION_H pktanon-master/libpktanon/transformations/DefaultTransformationsConfigurator.cpp0000644000000000000000000003756512701423271027715 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "DefaultTransformationsConfigurator.h" //--------------------------------------------------------------------------------- #include "log.h" #include "AnonFactory.h" #include "anonprimitives/AnonPrimitive.h" #include "anonprimitives/AnonIdentity.h" #include "transformations/TransformationsConfigurator.h" #include "Transformation.h" #include "ProtocolUtils.h" #include "EthernetPacketTransformation.h" #include "VlanTagTransformation.h" #include "LinuxCookedCaptureTransformation.h" #include "ArpPacketTransformation.h" #include "IPv4PacketTransformation.h" #include "IPv6PacketTransformation.h" #include "UdpPacketTransformation.h" #include "TcpPacketTransformation.h" #include "IcmpPacketTransformation.h" #include "ICMPv6PacketTransformation.h" #include "PayloadTransformation.h" using namespace pktanon; void DefaultTransformationsConfigurator::configure_ethernet_packet ( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring ethernet packet:"); AnonPrimitive* anon_mac_source = TransformationsConfigurator::configure_packet_field ("mac-source", packet_config); AnonPrimitive* anon_mac_destination = TransformationsConfigurator::configure_packet_field ("mac-dest", packet_config); AnonPrimitive* anon_ethertype = TransformationsConfigurator::configure_packet_field ("ethertype", packet_config); EthernetPacketTransformation::PAD pad_mode = EthernetPacketTransformation::PAD::KEEP_LEN; const auto& params = config.get_global_parameters(); if (params.has_param ("pad-ethernet-packets")) { const string& pad_config = params.get_param ("pad-ethernet-packets"); if (pad_config.compare ("always") == 0) pad_mode = EthernetPacketTransformation::PAD::ALWAYS; else if (pad_config.compare ("keep-length") == 0) pad_mode = EthernetPacketTransformation::PAD::KEEP_LEN; else if (pad_config.compare ("never") == 0) pad_mode = EthernetPacketTransformation::PAD::NEVER; else throw std::runtime_error ("wrong value for 'pad-ethernet-packets' parameter: use always/keep-length/never"); _plg_verbose ("\t\tpad-ethernet-packets = " << pad_config); } EthernetPacketTransformation* ether_tr = new EthernetPacketTransformation (anon_mac_source, anon_mac_destination, anon_ethertype, pad_mode); TransformationsConfigurator::instance().add_linktype (1, ether_tr); } void DefaultTransformationsConfigurator::configure_vlan_tag ( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring vlan tag:"); AnonPrimitive* anon_priority = TransformationsConfigurator::configure_packet_field ("priority", packet_config); AnonPrimitive* anon_cfi = TransformationsConfigurator::configure_packet_field ("cfi", packet_config); AnonPrimitive* anon_vlan_id = TransformationsConfigurator::configure_packet_field ("id", packet_config); AnonPrimitive* anon_type = TransformationsConfigurator::configure_packet_field ("ethertype", packet_config); VlanTagTransformation* vlan_tr = new VlanTagTransformation (anon_priority, anon_cfi, anon_vlan_id, anon_type); TransformationsConfigurator::instance().add_ethertype (EtherTypes::ETHERTYPE_VLAN, vlan_tr); } void DefaultTransformationsConfigurator::configure_linux_sll_packet( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring linux cooked capture packet:"); AnonPrimitive* anon_packet_type = TransformationsConfigurator::configure_packet_field ("packet-type", packet_config); AnonPrimitive* anon_hardware_type = TransformationsConfigurator::configure_packet_field ("ll-address-type", packet_config); AnonPrimitive* anon_link_layer_addr = TransformationsConfigurator::configure_packet_field ("ll-address", packet_config); AnonPrimitive* anon_protocol_type = TransformationsConfigurator::configure_packet_field ("protocol", packet_config); LinuxCookedCaptureTransformation* sll_tr = new LinuxCookedCaptureTransformation (anon_packet_type, anon_hardware_type, anon_link_layer_addr, anon_protocol_type); TransformationsConfigurator::instance().add_linktype (113, sll_tr); } void DefaultTransformationsConfigurator::configure_arp_packet ( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring arp packet:"); AnonPrimitive* anon_hardware_type = TransformationsConfigurator::configure_packet_field ("hardware-type", packet_config); AnonPrimitive* anon_protocol_type = TransformationsConfigurator::configure_packet_field ("protocol-type", packet_config); AnonPrimitive* anon_hardware_size = TransformationsConfigurator::configure_packet_field ("hardware-size", packet_config); AnonPrimitive* anon_protocol_size = TransformationsConfigurator::configure_packet_field ("protocol-size", packet_config); AnonPrimitive* anon_opcode = TransformationsConfigurator::configure_packet_field ("opcode", packet_config); AnonPrimitive* anon_sender_mac = TransformationsConfigurator::configure_packet_field ("sender-mac", packet_config); AnonPrimitive* anon_sender_ip = TransformationsConfigurator::configure_packet_field ("sender-ip", packet_config); AnonPrimitive* anon_target_mac = TransformationsConfigurator::configure_packet_field ("target-mac", packet_config); AnonPrimitive* anon_target_ip = TransformationsConfigurator::configure_packet_field ("target-ip", packet_config); ArpPacketTransformation* arp_tr = new ArpPacketTransformation ( anon_hardware_type, anon_protocol_type, anon_hardware_size, anon_protocol_size, anon_opcode, anon_sender_mac, anon_sender_ip, anon_target_mac, anon_target_ip ); TransformationsConfigurator::instance().add_ethertype (EtherTypes::ETHERTYPE_ARP, arp_tr); } void DefaultTransformationsConfigurator::configure_ipv4_packet ( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring ip(v4) packet:"); AnonPrimitive* anon_tos = TransformationsConfigurator::configure_packet_field ("tos", packet_config); AnonPrimitive* anon_identification = TransformationsConfigurator::configure_packet_field ("identification", packet_config); AnonPrimitive* anon_flags = TransformationsConfigurator::configure_packet_field ("flags", packet_config); AnonPrimitive* anon_fragment_offset = TransformationsConfigurator::configure_packet_field ("fragment", packet_config); AnonPrimitive* anon_ttl = TransformationsConfigurator::configure_packet_field ("ttl", packet_config); AnonPrimitive* anon_protocol = TransformationsConfigurator::configure_packet_field ("protocol", packet_config); AnonPrimitive* anon_source_ip = TransformationsConfigurator::configure_packet_field ("src-ip", packet_config); AnonPrimitive* anon_destination_ip = TransformationsConfigurator::configure_packet_field ("dest-ip", packet_config); AnonPrimitive* anon_options = TransformationsConfigurator::configure_packet_field ("options", packet_config); bool recalculate_ipv4_header_checksum = true; bool recalculate_payload_checksums = true; const auto& params = config.get_global_parameters(); if (params.has_param ("recalculate-ipv4-header-checksum")) { const string& pad_config = params.get_param ("recalculate-ipv4-header-checksum"); if (pad_config.compare ("yes") == 0) recalculate_ipv4_header_checksum = true; else if (pad_config.compare ("no") == 0) recalculate_ipv4_header_checksum = false; else throw std::runtime_error ("wrong value for 'recalculate-ipv4-header-checksum' parameter: use yes/no"); _plg_verbose ("\t\trecalculate-ipv4-header-checksum = " << recalculate_ipv4_header_checksum); } if (params.has_param ("recalculate-payload-checksums")) { const string& pad_config = params.get_param ("recalculate-payload-checksums"); if (pad_config.compare ("yes") == 0) recalculate_payload_checksums = true; else if (pad_config.compare ("no") == 0) recalculate_payload_checksums = false; else throw std::runtime_error ("wrong value for 'recalculate-payload-checksums' parameter: use yes/no"); _plg_verbose ("\t\trecalculate-payload-checksums = " << recalculate_payload_checksums); } IPv4PacketTransformation* ip4_tr = new IPv4PacketTransformation ( anon_tos, anon_identification, anon_flags, anon_fragment_offset, anon_ttl, anon_protocol, anon_source_ip, anon_destination_ip, anon_options, recalculate_ipv4_header_checksum, recalculate_payload_checksums ); TransformationsConfigurator::instance().add_ethertype (EtherTypes::ETHERTYPE_IP, ip4_tr); TransformationsConfigurator::instance().add_protocol (IPPROTO_IPIP, ip4_tr); } void DefaultTransformationsConfigurator::configure_ipv6_packet ( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring ipv6 packet:"); AnonPrimitive* anon_traffic_class = TransformationsConfigurator::configure_packet_field ("traffic-class", packet_config);; AnonPrimitive* anon_flow_label = TransformationsConfigurator::configure_packet_field ("flow-label", packet_config);; AnonPrimitive* anon_next_header = TransformationsConfigurator::configure_packet_field ("next-header", packet_config);; AnonPrimitive* anon_hop_limit = TransformationsConfigurator::configure_packet_field ("hop-limit", packet_config);; AnonPrimitive* anon_source_addr = TransformationsConfigurator::configure_packet_field ("src-ip", packet_config);; AnonPrimitive* anon_destination_addr = TransformationsConfigurator::configure_packet_field ("dest-ip", packet_config);; if (anon_next_header == nullptr) _plg_warn ("next-header field will be absent in the trace"); bool recalculate_payload_checksums = true; const auto& params = config.get_global_parameters(); if (params.has_param ("recalculate-payload-checksums")) { const string& pad_config = params.get_param ("recalculate-payload-checksums"); if (pad_config.compare ("yes") == 0) recalculate_payload_checksums = true; else if (pad_config.compare ("no") == 0) recalculate_payload_checksums = false; else throw std::runtime_error ("wrong value for 'recalculate-payload-checksums' parameter: use yes/no"); _plg_verbose ("\t\trecalculate-payload-checksums = " << recalculate_payload_checksums); } IPv6PacketTransformation* ipv6_tr = new IPv6PacketTransformation ( anon_traffic_class, anon_flow_label, anon_next_header, anon_hop_limit, anon_source_addr, anon_destination_addr, recalculate_payload_checksums ); TransformationsConfigurator::instance().add_ethertype (EtherTypes::ETHERTYPE_IPV6, ipv6_tr); TransformationsConfigurator::instance().add_protocol (IPPROTO_IPV6, ipv6_tr); } void DefaultTransformationsConfigurator::configure_icmp_packet ( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring icmp(v4) packet:"); AnonPrimitive* anon_type = TransformationsConfigurator::configure_packet_field ("type", packet_config); AnonPrimitive* anon_code = TransformationsConfigurator::configure_packet_field ("code", packet_config); AnonPrimitive* anon_misc = TransformationsConfigurator::configure_packet_field ("rest", packet_config); IcmpPacketTransformation* icmp_tr = new IcmpPacketTransformation ( anon_type, anon_code, anon_misc ); TransformationsConfigurator::instance().add_protocol (IPPROTO_ICMP, icmp_tr); } void DefaultTransformationsConfigurator::configure_icmpv6_packet ( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring icmp(v6) packet:"); AnonPrimitive* anon_type = TransformationsConfigurator::configure_packet_field ("type", packet_config); AnonPrimitive* anon_code = TransformationsConfigurator::configure_packet_field ("code", packet_config); AnonPrimitive* anon_misc = TransformationsConfigurator::configure_packet_field ("rest", packet_config); AnonPrimitive* anon_target_address = TransformationsConfigurator::configure_packet_field ("target-address", packet_config); ICMPv6PacketTransformation* icmp_tr = new ICMPv6PacketTransformation ( anon_type, anon_code, anon_misc, anon_target_address ); TransformationsConfigurator::instance().add_protocol (IPPROTO_ICMPV6, icmp_tr); } void DefaultTransformationsConfigurator::configure_tcp_packet ( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring tcp packet:"); AnonPrimitive* anon_source_port = TransformationsConfigurator::configure_packet_field ("source-port", packet_config);; AnonPrimitive* anon_dest_port = TransformationsConfigurator::configure_packet_field ("dest-port", packet_config);; AnonPrimitive* anon_seq_num = TransformationsConfigurator::configure_packet_field ("seq", packet_config);; AnonPrimitive* anon_ack_num = TransformationsConfigurator::configure_packet_field ("ack", packet_config);; AnonPrimitive* anon_flags = TransformationsConfigurator::configure_packet_field ("flags", packet_config);; AnonPrimitive* anon_window_size = TransformationsConfigurator::configure_packet_field ("window-size", packet_config);; AnonPrimitive* anon_urgent_pointer = TransformationsConfigurator::configure_packet_field ("urgent-pointer", packet_config);; AnonPrimitive* anon_options = TransformationsConfigurator::configure_packet_field ("options", packet_config);; TcpPacketTransformation* tcp_tr = new TcpPacketTransformation ( anon_source_port, anon_dest_port, anon_seq_num, anon_ack_num, anon_flags, anon_window_size, anon_urgent_pointer, anon_options ); TransformationsConfigurator::instance().add_protocol (IPPROTO_TCP, tcp_tr); } void DefaultTransformationsConfigurator::configure_udp_packet ( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring udp packet:"); AnonPrimitive* anon_source_port = TransformationsConfigurator::configure_packet_field ("source-port", packet_config); AnonPrimitive* anon_destination_port = TransformationsConfigurator::configure_packet_field ("dest-port", packet_config); UdpPacketTransformation* udp_tr = new UdpPacketTransformation (anon_source_port, anon_destination_port); TransformationsConfigurator::instance().add_protocol (IPPROTO_UDP, udp_tr); } void DefaultTransformationsConfigurator::configure_payload_packet ( const PktAnonConfig::PacketConfig& packet_config, const PktAnonConfig& config ) { _plg_verbose ("\tconfiguring payload packet:"); AnonPrimitive* anon_payload = TransformationsConfigurator::configure_packet_field ("payload", packet_config); PayloadTransformation* payload_tr = new PayloadTransformation (anon_payload); TransformationsConfigurator::instance().add_payload_transformation (payload_tr); } REGISTER_PACKET (ethernet, DefaultTransformationsConfigurator::configure_ethernet_packet); REGISTER_PACKET (vlan, DefaultTransformationsConfigurator::configure_vlan_tag); REGISTER_PACKET (linux_sll, DefaultTransformationsConfigurator::configure_linux_sll_packet); REGISTER_PACKET (arp, DefaultTransformationsConfigurator::configure_arp_packet); REGISTER_PACKET (ip, DefaultTransformationsConfigurator::configure_ipv4_packet); REGISTER_PACKET (ipv4, DefaultTransformationsConfigurator::configure_ipv4_packet); REGISTER_PACKET (ipv6, DefaultTransformationsConfigurator::configure_ipv6_packet); REGISTER_PACKET (icmp, DefaultTransformationsConfigurator::configure_icmp_packet); REGISTER_PACKET (icmpv6, DefaultTransformationsConfigurator::configure_icmpv6_packet); REGISTER_PACKET (tcp, DefaultTransformationsConfigurator::configure_tcp_packet); REGISTER_PACKET (udp, DefaultTransformationsConfigurator::configure_udp_packet); REGISTER_PACKET (payload, DefaultTransformationsConfigurator::configure_payload_packet); pktanon-master/libpktanon/anonprimitives/0000755000000000000000000000000012701423271017726 5ustar rootrootpktanon-master/libpktanon/anonprimitives/AnonConstOverwriteRange.h0000644000000000000000000000172612701423271024673 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANONCONSTOVERWRITERANGE_H #define PKTANON__ANONCONSTOVERWRITERANGE_H #include "AnonPrimitive.h" namespace pktanon { class AnonConstOverwriteRange : public AnonPrimitive { public: static AnonConstOverwriteRange* construct ( const pktanon::Params& params ); AnonConstOverwriteRange ( int range_begin, int range_length, uint8_t value ); virtual ~AnonConstOverwriteRange(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; private: int range_begin; int range_length; uint8_t value; }; } #endif // PKTANON_ANONCONSTOVERWRITERANGE_H pktanon-master/libpktanon/anonprimitives/AnonCryptoPan.cpp0000644000000000000000000000264512701423271023174 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonCryptoPan.h" #include #include #include namespace pktanon { AnonCryptoPan* AnonCryptoPan::construct(pktanon::Params& params) { if (!params.has_param("key")) throw std::runtime_error("incomplete configuration for AnonCryptoPan: missing key attribute"); return new AnonCryptoPan(params.get_param("key")); } AnonCryptoPan::AnonCryptoPan(string _key) : key(_key), cryptopan((const UINT8*) key.c_str()) { } AnonCryptoPan::~AnonCryptoPan() { } AnonPrimitive::ANON_RESULT AnonCryptoPan::anonymize_chain(void* buf, unsigned int len) const { assert(len == sizeof (UINT32)); UINT32 orig = 0; memcpy(&orig, buf, sizeof(UINT32)); orig = const_cast(this)->cryptopan.anonymize(orig); memcpy(buf, &orig, sizeof(UINT32)); return ANON_RESULT(len); } AnonPrimitive::ANON_RESULT AnonCryptoPan::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { assert(len == sizeof (UINT32)); UINT32 orig = * (UINT32*) src_buff; orig = const_cast(this)->cryptopan.anonymize(orig); * (UINT32*) dst_buff = orig; return ANON_RESULT(len); } } pktanon-master/libpktanon/anonprimitives/AnonHashSha256_Nettle.cpp0000644000000000000000000000415712701423271024344 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "anonprimitives/AnonHashSha256.h" #include #include #include namespace pktanon { AnonHashSha256::AnonHashSha256 ( bool truncate ) : truncate ( truncate ) { } AnonPrimitive::ANON_RESULT AnonHashSha256::anonymize_chain ( void* buf, unsigned int len ) const { struct sha256_ctx ctx; sha256_init ( &ctx ); sha256_update ( &ctx, len, ( unsigned char* ) buf ); sha256_digest ( &ctx, std::min ( ( int ) len, SHA256_DIGEST_SIZE ), ( unsigned char* ) buf ); if ( SHA256_DIGEST_SIZE >= len || truncate ) { return ANON_RESULT ( std::min ( ( int ) len, SHA256_DIGEST_SIZE ) ); } int pos = SHA256_DIGEST_SIZE; int remaining = len - SHA256_DIGEST_SIZE; while ( remaining > 0 ) { memcpy ( ( unsigned char* ) buf + pos, buf, std::min ( remaining, SHA256_DIGEST_SIZE ) ); pos+= SHA256_DIGEST_SIZE; remaining-=SHA256_DIGEST_SIZE; } return ANON_RESULT ( len ); } AnonPrimitive::ANON_RESULT AnonHashSha256::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { struct sha256_ctx ctx; nettle_sha256_init ( &ctx ); nettle_sha256_update ( &ctx, len, ( unsigned char* ) src_buff ); nettle_sha256_digest ( &ctx, std::min ( ( int ) len, SHA256_DIGEST_SIZE ), ( unsigned char* ) dst_buff ); if ( SHA256_DIGEST_SIZE >= len || truncate ) { return ANON_RESULT ( std::min ( ( int ) len, SHA256_DIGEST_SIZE ) ); } int pos = SHA256_DIGEST_SIZE; int remaining = len - SHA256_DIGEST_SIZE; while ( remaining > 0 ) { memcpy ( ( unsigned char* ) dst_buff + pos, dst_buff, std::min ( remaining, SHA256_DIGEST_SIZE ) ); pos+= SHA256_DIGEST_SIZE; remaining-=SHA256_DIGEST_SIZE; } return ANON_RESULT ( len ); } REGISTER_ANON ( AnonHashSha256 ); } pktanon-master/libpktanon/anonprimitives/AnonBroadcastHandler.h0000644000000000000000000000163212701423271024115 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_BROADCAST_HANDLER_H #define PKTANON__ANON_BROADCAST_HANDLER_H #include "AnonPrimitive.h" namespace pktanon { /** * prevents anonymization of strings consisting of 0xFF chars * * can be used to prevent anonymization of broadcast IP and MAC addresses */ class AnonBroadcastHandler: public AnonPrimitive { public: AnonBroadcastHandler(); virtual ~AnonBroadcastHandler(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const ; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; }; } #endif // __ANON_BROADCAST_HANDLER_H pktanon-master/libpktanon/anonprimitives/AnonHashSha1.h0000644000000000000000000000137712701423271022323 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_HASH_SHA1_H #define PKTANON__ANON_HASH_SHA1_H #include "AnonPrimitive.h" namespace pktanon { class AnonHashSha1: public AnonPrimitive { public: AnonHashSha1 ( bool truncate = false ); virtual ~AnonHashSha1(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const; virtual ANON_RESULT anonymize_internal ( const void* const src_buff, void* dst_buff, unsigned int len ) const; private: bool truncate; }; } #endif // __ANON_HASH_SHA1_H pktanon-master/libpktanon/anonprimitives/AnonContinuousChar.cpp0000644000000000000000000000122012701423271024205 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonContinuousChar.h" #include #include "AnonFactory.h" namespace pktanon { AnonContinuousChar::AnonContinuousChar() : current(0) { } AnonContinuousChar::~AnonContinuousChar() { } AnonPrimitive::ANON_RESULT AnonContinuousChar::anonymize_chain(void* buf, unsigned int len) const { memset(buf, current++, len); return 0; } REGISTER_ANON(AnonContinuousChar); } pktanon-master/libpktanon/anonprimitives/AnonConstOverwriteRange.cpp0000644000000000000000000000421212701423271025217 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonConstOverwriteRange.h" #include "AnonFactory.h" #include #include #include using namespace pktanon; AnonConstOverwriteRange* AnonConstOverwriteRange::construct(const pktanon::Params& params) { if (!params.has_param("range-begin")) throw std::runtime_error("incomplete configuration for AnonConstOverwriteRange - missing attribute: range-begin"); if (!params.has_param("range-length")) throw std::runtime_error("incomplete configuration for AnonConstOverwriteRange - missing attribute: range-length"); if (!params.has_param("value")) throw std::runtime_error("incomplete configuration for AnonConstOverwriteRange - missing attribute: value"); int range_begin = std::stoi(params.get_param("range-begin")); int range_length = std::stoi(params.get_param("range-length")); uint8_t value = std::stoul(params.get_param("value"), nullptr, 16); // TRACE(range_begin); // TRACE(range_length); // TRACE(value); return new AnonConstOverwriteRange(range_begin, range_length, value); } AnonConstOverwriteRange::AnonConstOverwriteRange(int range_begin, int range_length, uint8_t value): range_begin(range_begin), range_length(range_length), value(value) {} AnonConstOverwriteRange::~AnonConstOverwriteRange() = default; AnonPrimitive::ANON_RESULT AnonConstOverwriteRange::anonymize_chain(void* buf, unsigned int len) const { memset(((uint8_t*)buf)+range_begin, value, std::min(range_length, (int) len-range_begin)); return ANON_RESULT(len); } AnonPrimitive::ANON_RESULT AnonConstOverwriteRange::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { memcpy(dst_buff, src_buff, len); memset(((uint8_t*)dst_buff)+range_begin, value, std::min(range_length, (int) len-range_begin)); return ANON_RESULT(len); } REGISTER_ANON_PARAM(AnonConstOverwriteRange, AnonConstOverwriteRange::construct); pktanon-master/libpktanon/anonprimitives/AnonBytewiseHashSha1.cpp0000644000000000000000000000144712701423271024370 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonBytewiseHashSha1.h" #include "AnonHashSha1.h" #include "AnonFactory.h" namespace pktanon { AnonBytewiseHashSha1::AnonBytewiseHashSha1() { fillTable(); } AnonBytewiseHashSha1::~AnonBytewiseHashSha1() { } void AnonBytewiseHashSha1::fillTable() { unsigned char data; AnonHashSha1 hashobj; for (unsigned short i = 0; i < 256; i++) { data = (unsigned char) i; hashobj.anonimyze (&data, 1); anonbytes[i] = data; } // for (unsigned short i = 0; i<256; i++) } REGISTER_ANON (AnonBytewiseHashSha1); } pktanon-master/libpktanon/anonprimitives/AnonBytewiseHashHmacSha1.cpp0000644000000000000000000000245212701423271025156 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonBytewiseHashHmacSha1.h" #include "AnonHashHmacSha1.h" #include #include "AnonFactory.h" namespace pktanon { AnonBytewiseHashHmacSha1* AnonBytewiseHashHmacSha1::construct (std::unordered_map attrmap) { auto it = attrmap.find ("key"); string key; if (it == attrmap.end()) { throw std::runtime_error ("incomplete configuration for AnonBytewiseHashHmacSha1: missing key attribute"); } key = it->second; return new AnonBytewiseHashHmacSha1 (key); } AnonBytewiseHashHmacSha1::AnonBytewiseHashHmacSha1 (string key) { fillTable (key); } AnonBytewiseHashHmacSha1::~AnonBytewiseHashHmacSha1() { } void AnonBytewiseHashHmacSha1::fillTable (string& hmackey) { unsigned char data; AnonHashHmacSha1 hashobj (hmackey); for (unsigned short i = 0; i < 256; i++) { data = (unsigned char) i; hashobj.anonimyze (&data, 1); anonbytes[i] = data; } // for (unsigned short i = 0; i<256; i++) } REGISTER_ANON_PARAM (AnonBytewiseHashHmacSha1, AnonBytewiseHashHmacSha1::construct); } pktanon-master/libpktanon/anonprimitives/AnonBytewiseHashHmacSha1.h0000644000000000000000000000135712701423271024626 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef __ANON_BYTEWISE_HASH_HMAC_SHA1_H #define __ANON_BYTEWISE_HASH_HMAC_SHA1_H #include "AnonBytewise.h" # include "unordered_map" namespace pktanon { class AnonBytewiseHashHmacSha1: public AnonBytewise { public: static AnonBytewiseHashHmacSha1* construct(std::unordered_map attrmap); AnonBytewiseHashHmacSha1(string key); virtual ~AnonBytewiseHashHmacSha1(); private: void fillTable(string& hmackey); }; } #endif // __ANON_BYTEWISE_HASH_HMAC_SHA1_H pktanon-master/libpktanon/anonprimitives/AnonContinuousChar.h0000644000000000000000000000124312701423271023657 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_CONTINUOUS_CHAR_H #define PKTANON__ANON_CONTINUOUS_CHAR_H #include "AnonPrimitive.h" namespace pktanon { class AnonContinuousChar: public AnonPrimitive { public: AnonContinuousChar(); virtual ~AnonContinuousChar(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const; private: unsigned char current; }; } #endif // __ANON_CONTINUOUS_CHAR_H pktanon-master/libpktanon/anonprimitives/AnonBytewise.h0000644000000000000000000000116412701423271022510 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_BYTEWISE_H #define PKTANON__ANON_BYTEWISE_H #include "anonprimitives/AnonPrimitive.h" namespace pktanon { class AnonBytewise: public AnonPrimitive { public: AnonBytewise(); virtual ~AnonBytewise(); protected: ANON_RESULT anonymize (void* buf, unsigned int len); unsigned char anonbytes[256]; }; } #endif // __ANON_BYTEWISE_H pktanon-master/libpktanon/anonprimitives/AnonKnownProtocolHandler.cpp0000644000000000000000000000275012701423271025366 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonKnownProtocolHandler.h" #include "AnonFactory.h" #include "transformations/Transformation.h" using namespace pktanon; AnonKnownProtocolHandler::~AnonKnownProtocolHandler() = default; AnonKnownProtocolHandler::AnonKnownProtocolHandler() {} AnonPrimitive::ANON_RESULT AnonKnownProtocolHandler::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { memcpy(dst_buff, src_buff, len); return anonymize_chain(dst_buff, len); } AnonPrimitive::ANON_RESULT AnonKnownProtocolHandler::anonymize_chain ( void* buf, unsigned int len ) const { uint8_t protocol = ((uint8_t*)buf)[0]; if(Transformation::getTransformationProtocol(protocol) == Transformation::getPayloadTransformation()) { return ANON_RESULT(len, false); } else { return ANON_RESULT(len); } } // AnonPrimitive::ANON_RESULT AnonKnownProtocolHandler::anonymize ( void* buf, unsigned int len ) const // { // uint8_t protocol = ((uint8_t*)buf)[0]; // if(Transformation::getTransformationProtocol(protocol) == Transformation::getPayloadTransformation()) // { // return ANON_RESULT(len, false); // } // else // { // return ANON_RESULT(len); // } // } REGISTER_ANON(AnonKnownProtocolHandler); pktanon-master/libpktanon/anonprimitives/AnonRandomize.cpp0000644000000000000000000000167712701423271023211 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonRandomize.h" #include #include namespace pktanon { AnonRandomize::~AnonRandomize() = default; AnonRandomize::AnonRandomize() { } AnonPrimitive::ANON_RESULT AnonRandomize::anonymize_chain(void* buf, unsigned int len) const { RandomNumberGenerator::generate_random_bytes (reinterpret_cast(buf), len); return ANON_RESULT (len); } AnonPrimitive::ANON_RESULT AnonRandomize::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { RandomNumberGenerator::generate_random_bytes (reinterpret_cast(dst_buff), len); return ANON_RESULT (len); } REGISTER_ANON (AnonRandomize); } pktanon-master/libpktanon/anonprimitives/AnonKnownEthertypeHandler.h0000644000000000000000000000142712701423271025203 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_ANONKNOWNETHERTYPEHANDLER_H #define PKTANON_ANONKNOWNETHERTYPEHANDLER_H #include "AnonPrimitive.h" namespace pktanon { class AnonKnownEthertypeHandler : public AnonPrimitive { public: AnonKnownEthertypeHandler(); virtual ~AnonKnownEthertypeHandler(); protected: virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const; }; } #endif // PKTANON_ANONKNOWNETHERTYPEHANDLER_H pktanon-master/libpktanon/anonprimitives/AnonKnownEthertypeHandler.cpp0000644000000000000000000000220312701423271025527 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonKnownEthertypeHandler.h" #include "AnonFactory.h" #include "transformations/Transformation.h" namespace pktanon { AnonKnownEthertypeHandler::~AnonKnownEthertypeHandler() = default; AnonKnownEthertypeHandler::AnonKnownEthertypeHandler() {} AnonPrimitive::ANON_RESULT AnonKnownEthertypeHandler::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { memcpy(dst_buff, src_buff, len); return anonymize_chain(dst_buff, len); } AnonPrimitive::ANON_RESULT AnonKnownEthertypeHandler::anonymize_chain ( void* buf, unsigned int len ) const { uint16_t* ethertype = ( uint16_t* ) buf; if ( Transformation::getTransformationEthertype ( *ethertype ) == Transformation::getPayloadTransformation() ) { return ANON_RESULT ( len, false ); } else { return ANON_RESULT ( len ); } } REGISTER_ANON(AnonKnownEthertypeHandler) }pktanon-master/libpktanon/anonprimitives/AnonWhitenoise.h0000644000000000000000000000151512701423271023033 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef __ANON_WHITENOISE_H #define __ANON_WHITENOISE_H #include "AnonPrimitive.h" namespace pktanon { class AnonWhitenoise: public AnonPrimitive { public: static AnonWhitenoise* construct ( const pktanon::Params& params ); AnonWhitenoise ( unsigned char strengthval ); virtual ~AnonWhitenoise(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; private: unsigned char strength; }; } #endif // __ANON_WHITENOISE_H pktanon-master/libpktanon/anonprimitives/AnonBytewiseHashSha1.h0000644000000000000000000000112312701423271024024 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_BYTEWISE_HASH_SHA1_H # define PKTANON__ANON_BYTEWISE_HASH_SHA1_H #include "AnonBytewise.h" namespace pktanon { class AnonBytewiseHashSha1: public AnonBytewise { public: AnonBytewiseHashSha1(); virtual ~AnonBytewiseHashSha1(); private: void fillTable(); }; } #endif // __ANON_BYTEWISE_HASH_SHA1_H pktanon-master/libpktanon/anonprimitives/AnonConstOverwrite.cpp0000644000000000000000000000243112701423271024243 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonConstOverwrite.h" #include "AnonFactory.h" #include #include namespace pktanon { AnonConstOverwrite* AnonConstOverwrite::construct(const Params& params) { if (!params.has_param("value")) throw std::runtime_error("incomplete configuration for AnonConstOverwrite: missing attribute 'value'"); unsigned char byte = strtoul(params.get_param("value").c_str(), nullptr, 16); return new AnonConstOverwrite(byte); } AnonConstOverwrite::AnonConstOverwrite(unsigned char byte) : byteval(byte) { } AnonConstOverwrite::~AnonConstOverwrite() { } AnonPrimitive::ANON_RESULT AnonConstOverwrite::anonymize_chain(void* buf, unsigned int len) const { memset(buf, byteval, len); return ANON_RESULT(len); } AnonPrimitive::ANON_RESULT AnonConstOverwrite::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { // HB(); memset(dst_buff, byteval, len); return ANON_RESULT(len); } REGISTER_ANON_PARAM(AnonConstOverwrite, AnonConstOverwrite::construct); } pktanon-master/libpktanon/anonprimitives/AnonShuffle.cpp0000644000000000000000000000202112701423271022635 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonShuffle.h" #include #include #include "AnonFactory.h" namespace pktanon { AnonShuffle::~AnonShuffle() = default; AnonShuffle::AnonShuffle() { } AnonPrimitive::ANON_RESULT AnonShuffle::anonymize_chain ( void* buf, unsigned int len ) const { unsigned char* buf1 = reinterpret_cast ( buf ); std::random_shuffle ( buf1, buf1+len ); return ANON_RESULT ( len ); } AnonPrimitive::ANON_RESULT AnonShuffle::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { memcpy ( dst_buff, src_buff, len ); unsigned char* buff = reinterpret_cast ( dst_buff ); std::random_shuffle ( buff, buff+len ); return ANON_RESULT ( len ); } REGISTER_ANON ( AnonShuffle ); } pktanon-master/libpktanon/anonprimitives/AnonHashSha1_Nettle.cpp0000644000000000000000000000370512701423271024166 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonHashSha1.h" #include "AnonFactory.h" #include #include namespace pktanon { AnonHashSha1::AnonHashSha1(bool truncate) { } AnonHashSha1::~AnonHashSha1() { } AnonPrimitive::ANON_RESULT AnonHashSha1::anonymize_chain (void* buf, unsigned int len) const { thread_local static struct sha1_ctx ctx; sha1_init (&ctx); sha1_update (&ctx, len, (unsigned char*) buf); sha1_digest (&ctx, std::min ( (int) len, SHA1_DIGEST_SIZE), (unsigned char*) buf); if (SHA1_DIGEST_SIZE >= len || truncate) { return ANON_RESULT (std::min ( (int) len, SHA1_DIGEST_SIZE)); } int pos = SHA1_DIGEST_SIZE; int remaining = len - SHA1_DIGEST_SIZE; while (remaining > 0) { memcpy ( (unsigned char*) buf + pos, buf, std::min (remaining, SHA1_DIGEST_SIZE)); pos += SHA1_DIGEST_SIZE; remaining -= SHA1_DIGEST_SIZE; } return ANON_RESULT (len); } AnonPrimitive::ANON_RESULT AnonHashSha1::anonymize_internal ( const void* const src_buff, void* dst_buff, unsigned int len ) const { thread_local static struct sha1_ctx ctx; nettle_sha1_init (&ctx); nettle_sha1_update (&ctx, len, (unsigned char*) src_buff); nettle_sha1_digest (&ctx, std::min ( (int) len, SHA1_DIGEST_SIZE), (unsigned char*) dst_buff); if (SHA1_DIGEST_SIZE >= len || truncate) { return ANON_RESULT (std::min ( (int) len, SHA1_DIGEST_SIZE)); } int pos = SHA1_DIGEST_SIZE; int remaining = len - SHA1_DIGEST_SIZE; while (remaining > 0) { memcpy ( (unsigned char*) dst_buff + pos, dst_buff, std::min (remaining, SHA1_DIGEST_SIZE)); pos += SHA1_DIGEST_SIZE; remaining -= SHA1_DIGEST_SIZE; } return ANON_RESULT (len); } REGISTER_ANON (AnonHashSha1) } pktanon-master/libpktanon/anonprimitives/AnonIdentity.h0000644000000000000000000000132112701423271022501 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_IDENTITY_H #define PKTANON__ANON_IDENTITY_H #include "AnonPrimitive.h" namespace pktanon { class AnonIdentity: public AnonPrimitive { public: AnonIdentity(); virtual ~AnonIdentity(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const ; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; }; } #endif // PKTANON__ANON_IDENTITY_H pktanon-master/libpktanon/anonprimitives/AnonBroadcastHandler.cpp0000644000000000000000000000244512701423271024453 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonBroadcastHandler.h" #include "AnonFactory.h" #include #include namespace pktanon { AnonBroadcastHandler::AnonBroadcastHandler() {} AnonBroadcastHandler::~AnonBroadcastHandler() = default; AnonPrimitive::ANON_RESULT AnonBroadcastHandler::anonymize_chain (void* buf, unsigned int len) const { //TODO try with std::find_if_not ... unsigned char* buf1 = reinterpret_cast (buf); bool continue_anon = std::any_of (buf1, buf1 + len, [](uint8_t j) { return j != 0xFF; }); return ANON_RESULT(len, continue_anon); } AnonPrimitive::ANON_RESULT AnonBroadcastHandler::anonymize_internal ( const void* const src_buff, void* dst_buff, unsigned int len ) const { const unsigned char* buf1 = reinterpret_cast (src_buff); bool continue_anon = std::any_of (buf1, buf1 + len, [](uint8_t j) { return j != 0xFF; }); memcpy(dst_buff, src_buff, len); return ANON_RESULT(len, continue_anon); } REGISTER_ANON (AnonBroadcastHandler); } pktanon-master/libpktanon/anonprimitives/AnonRandomize.h0000644000000000000000000000133312701423271022643 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_RANDOMIZE_H # define PKTANON__ANON_RANDOMIZE_H #include "AnonPrimitive.h" namespace pktanon { class AnonRandomize: public AnonPrimitive { public: AnonRandomize(); virtual ~AnonRandomize(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const ; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; }; } #endif // __ANON_RANDOMIZE_H pktanon-master/libpktanon/anonprimitives/AnonShorten.cpp0000644000000000000000000000261212701423271022671 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonShorten.h" #include "AnonFactory.h" #include "log.h" #include "debug.h" #include namespace pktanon { AnonShorten* AnonShorten::construct ( const Params& params ) { if ( !params.has_param ( "newlen" ) ) throw std::runtime_error ( "incomplete configuration for AnonShorten: missing newlen attribute" ); unsigned int len = strtoul ( params.get_param ( "newlen" ).c_str(), nullptr, 10 ); _plg_verbose ( "\t\t\t" << "newlen: " << len ); return new AnonShorten ( len ); } AnonShorten::AnonShorten ( unsigned int length ) : newlen ( length ) { } AnonShorten::~AnonShorten() { } AnonPrimitive::ANON_RESULT AnonShorten::anonymize_chain ( void* buf, unsigned int len ) const { // HB(); return ANON_RESULT ( std::min ( newlen, len ) ); } AnonPrimitive::ANON_RESULT AnonShorten::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { // HB(); if ( newlen == 0 ) return ANON_RESULT ( 0 ); memcpy ( dst_buff, src_buff, std::min ( newlen, len ) ); return ANON_RESULT ( std::min ( newlen, len ) ); } REGISTER_ANON_PARAM ( AnonShorten, AnonShorten::construct ); } pktanon-master/libpktanon/anonprimitives/AnonHashHmacSha1_Nettle.cpp0000644000000000000000000000554412701423271024762 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonHashHmacSha1.h" #include "AnonFactory.h" #include #include namespace pktanon { AnonHashHmacSha1* AnonHashHmacSha1::construct ( const Params& params ) { if ( !params.has_param ( "key" ) ) throw std::runtime_error ( "incomplete configuration for AnonHashHmacSha1: missing key attribute" ); return new AnonHashHmacSha1 ( params.get_param ( "key" ) ); } AnonHashHmacSha1::AnonHashHmacSha1 ( string hmackey ) { keylen = ( unsigned int ) hmackey.length(); key = new uint8_t[keylen + 1]; #ifdef WIN32 strcpy ( ( char* ) key, hmackey.c_str () ); #else strcpy ( ( char* ) key, hmackey.c_str() ); #endif } AnonHashHmacSha1::~AnonHashHmacSha1() { delete[] key; } AnonPrimitive::ANON_RESULT AnonHashHmacSha1::anonymize_chain ( void* buf, unsigned int len ) const { struct hmac_sha1_ctx hmac_ctx; hmac_sha1_set_key ( &hmac_ctx, keylen, key ); hmac_sha1_update ( &hmac_ctx, len, ( unsigned char* ) buf ); hmac_sha1_digest ( &hmac_ctx, std::min ( ( int ) len, SHA1_DIGEST_SIZE ), ( unsigned char* ) buf ); if ( SHA1_DIGEST_SIZE >= len ) { return ANON_RESULT ( std::min ( ( int ) len, SHA1_DIGEST_SIZE ) ); } // HmacSha1::sha1_hmac ( (uint8_t*) &digest, key, keylen, (uint8_t*) buf, len); int remaining = len; int pos = 0; while ( remaining > 0 ) { memcpy ( ( unsigned char* ) buf + pos, buf, std::min ( remaining, SHA1_DIGEST_SIZE ) ); remaining -= SHA1_DIGEST_SIZE; pos += SHA1_DIGEST_SIZE; } // while (remaining != 0) return ANON_RESULT ( len ); } AnonPrimitive::ANON_RESULT AnonHashHmacSha1::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { struct hmac_sha1_ctx hmac_ctx; nettle_hmac_sha1_set_key ( &hmac_ctx, keylen, key ); nettle_hmac_sha1_update ( &hmac_ctx, len, ( unsigned char* ) src_buff ); nettle_hmac_sha1_digest ( &hmac_ctx, std::min ( ( int ) len, SHA1_DIGEST_SIZE ), ( unsigned char* ) dst_buff ); if ( SHA1_DIGEST_SIZE >= len ) { return ANON_RESULT ( std::min ( ( int ) len, SHA1_DIGEST_SIZE ) ); } // HmacSha1::sha1_hmac ( (uint8_t*) &digest, key, keylen, (uint8_t*) buf, len); int remaining = len; int pos = 0; while ( remaining > 0 ) { memcpy ( ( unsigned char* ) dst_buff + pos, dst_buff, std::min ( remaining, SHA1_DIGEST_SIZE ) ); remaining -= SHA1_DIGEST_SIZE; pos += SHA1_DIGEST_SIZE; } // while (remaining != 0) return ANON_RESULT ( len ); } REGISTER_ANON_PARAM ( AnonHashHmacSha1, AnonHashHmacSha1::construct ); } pktanon-master/libpktanon/anonprimitives/AnonPrimitive.h0000644000000000000000000000277712701423271022700 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_PRIMITIVE_H #define PKTANON__ANON_PRIMITIVE_H #include using std::string; using std::min; namespace pktanon { class Params; class AnonPrimitive { public: AnonPrimitive(); virtual ~AnonPrimitive(); AnonPrimitive(const AnonPrimitive& other) = delete; AnonPrimitive& operator= (const AnonPrimitive& other) = delete; unsigned int anonimyze(const void* const src_buff, void* dst_buff, unsigned int len) const; AnonPrimitive* getNext() const; void setNext(AnonPrimitive* nextprim); protected: struct ANON_RESULT { unsigned int newlength; bool call_next_anon; ANON_RESULT() : newlength(0), call_next_anon(false) { } ANON_RESULT(unsigned int newlength, bool call_next_anon = true) : newlength(newlength), call_next_anon(call_next_anon) {} ANON_RESULT& operator&=(const ANON_RESULT& rhs) { newlength = std::min(newlength, rhs.newlength); call_next_anon &= rhs.call_next_anon; return *this; } }; virtual ANON_RESULT anonymize_internal(const void* const src_buff, void* dst_buff, unsigned int len) const; virtual ANON_RESULT anonymize_chain(void* buf, unsigned int len) const = 0; private: AnonPrimitive* next; }; } #endif // __ANON_PRIMITIVE_H pktanon-master/libpktanon/anonprimitives/AnonHashSha256.h0000644000000000000000000000143412701423271022471 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANONHASHSHA256_H #define PKTANON__ANONHASHSHA256_H #include "AnonPrimitive.h" namespace pktanon { class AnonHashSha256 : public AnonPrimitive { public: AnonHashSha256 ( bool truncate = false ); virtual ~AnonHashSha256() = default; protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const ; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; private: bool truncate; }; } #endif // ANONHASHSHA256_H pktanon-master/libpktanon/anonprimitives/AnonHashHmacSha1.h0000644000000000000000000000157512701423271023114 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_HASH_HMAC_SHA1_H #define PKTANON__ANON_HASH_HMAC_SHA1_H #include "AnonPrimitive.h" namespace pktanon { class AnonHashHmacSha1: public AnonPrimitive { public: static AnonHashHmacSha1* construct ( const pktanon::Params& attrmap ); AnonHashHmacSha1 ( string hmackey ); virtual ~AnonHashHmacSha1(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const ; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; private: uint8_t* key; unsigned short keylen; }; } #endif // PKTANON__ANON_HASH_HMAC_SHA1_H pktanon-master/libpktanon/anonprimitives/AnonConstOverwrite.h0000644000000000000000000000157112701423271023714 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_CONST_OVERWRITE_H #define PKTANON__ANON_CONST_OVERWRITE_H #include "AnonPrimitive.h" namespace pktanon { class Params; class AnonConstOverwrite: public AnonPrimitive { public: static AnonConstOverwrite* construct ( const Params& params ); AnonConstOverwrite ( unsigned char byte ); virtual ~AnonConstOverwrite(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; private: unsigned char byteval; }; } #endif // __ANON_CONST_OVERWRITE_H pktanon-master/libpktanon/anonprimitives/AnonIdentity.cpp0000644000000000000000000000150312701423271023036 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonIdentity.h" #include #include //-------------------------------------------- namespace pktanon { AnonIdentity::AnonIdentity() {} AnonIdentity::~AnonIdentity() = default; AnonPrimitive::ANON_RESULT AnonIdentity::anonymize_chain(void* buf, unsigned int len) const { return ANON_RESULT(len); } AnonPrimitive::ANON_RESULT AnonIdentity::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { memcpy(dst_buff, src_buff, len); return ANON_RESULT(len); } REGISTER_ANON(AnonIdentity) } pktanon-master/libpktanon/anonprimitives/AnonKnownProtocolHandler.h0000644000000000000000000000142212701423271025026 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_ANONKNOWNPROTOCOLHANDLER_H #define PKTANON_ANONKNOWNPROTOCOLHANDLER_H #include "AnonPrimitive.h" namespace pktanon { class AnonKnownProtocolHandler : public AnonPrimitive { public: AnonKnownProtocolHandler(); virtual ~AnonKnownProtocolHandler(); protected: virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const; }; } #endif // PKTANON_ANONKNOWNETHERTYPEHANDLER_H pktanon-master/libpktanon/anonprimitives/AnonShuffle.h0000644000000000000000000000130312701423271022304 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_SHUFFLE_H #define PKTANON__ANON_SHUFFLE_H #include "AnonPrimitive.h" namespace pktanon { class AnonShuffle: public AnonPrimitive { public: AnonShuffle(); virtual ~AnonShuffle(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; }; } #endif // __ANON_SHUFFLE_H pktanon-master/libpktanon/anonprimitives/AnonCryptoPan.h0000644000000000000000000000157612701423271022643 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_CRYPTO_PAN_H #define PKTANON__ANON_CRYPTO_PAN_H # include "AnonPrimitive.h" # include "cryptopan/panonymizer.h" namespace pktanon { class AnonCryptoPan: public AnonPrimitive { public: static AnonCryptoPan* construct ( Params& params ); AnonCryptoPan ( std::string _key ); virtual ~AnonCryptoPan(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const ; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; private: std::string key; PAnonymizer cryptopan; }; } #endif // PKTANON__ANON_CRYPTO_PAN_H pktanon-master/libpktanon/anonprimitives/AnonWhitenoise.cpp0000644000000000000000000000451512701423271023371 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonWhitenoise.h" #include "AnonFactory.h" #include #include "RandomNumberGenerator.h" namespace pktanon { AnonWhitenoise* AnonWhitenoise::construct ( const Params& params ) { if ( !params.has_param ( "strength" ) ) throw std::runtime_error ( "incomplete configuration for AnonWhitenoise: missing strength attribute" ); unsigned char istrength = std::strtoul ( params.get_param ( "strength" ).c_str(), nullptr, 10 ); return new AnonWhitenoise ( istrength ); } AnonWhitenoise::AnonWhitenoise ( unsigned char strengthval ) : strength ( strengthval ) { } AnonWhitenoise::~AnonWhitenoise() { } AnonPrimitive::ANON_RESULT AnonWhitenoise::anonymize_chain ( void* buf, unsigned int len ) const { // how many bits are in the buffer? unsigned int bits = len * 8; // how many bits of these will we flip? Generate a random number and apply the given strength. unsigned int randbits = RandomNumberGenerator::generate ( 0, bits ); randbits = ( unsigned int ) ceil ( ( ( double ) randbits / 100.0 ) * ( strength * 10 ) ); // in any case we will flip at least one bit, if we have any bits if ( bits > 0 && randbits == 0 ) randbits++; // // now we flip randbits in the buffer at random positions // unsigned int flipbit; unsigned char* byte; unsigned char mask; for ( unsigned int i = 0; i < randbits; i++ ) { flipbit = RandomNumberGenerator::generate ( 0, bits - 1 ); byte = ( ( unsigned char* ) buf ) + ( flipbit / 8 ); mask = 1 << ( flipbit % 8 ); if ( ( *byte & mask ) == 0 ) *byte |= mask; // set bit to 1 else *byte &= ~mask; // set bit to 0 } // for (unsigned int i=0; i #include namespace pktanon { AnonBytewise::AnonBytewise() { } AnonBytewise::AnonBytewise ( const AnonBytewise& other ) : AnonPrimitive ( other ) { memcpy ( anonbytes, other.anonbytes, 256*sizeof ( unsigned char ) ); } AnonBytewise::~AnonBytewise() { } AnonPrimitive::ANON_RESULT AnonBytewise::anonymize ( void* buf, unsigned int len ) { unsigned char* pnt; for ( unsigned int i = 0; i < len; i++ ) { pnt = ( unsigned char* ) buf + i; memset ( pnt, anonbytes[*pnt], 1 ); } // for (unsigned int i=0; inext; while ( unlikely ( next && result.call_next_anon ) ) { result &= next->anonymize_chain ( dst_buff, result.newlength ); next = next->getNext(); } return result.newlength; } AnonPrimitive::ANON_RESULT AnonPrimitive::anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const { memcpy(dst_buff, src_buff, len); return anonymize_chain(dst_buff, len); } } pktanon-master/libpktanon/anonprimitives/AnonShorten.h0000644000000000000000000000177412701423271022346 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_SHORTEN_H # define PKTANON__ANON_SHORTEN_H #include "AnonPrimitive.h" namespace pktanon { /** * shortens the buffer. * * if the parameter 'length' is 0, buffer is completely removed * if the parameter 'length' is larger than the size of the buffer, buffer is unchanged */ class AnonShorten: public AnonPrimitive { public: static AnonShorten* construct ( const pktanon::Params& params ); AnonShorten ( unsigned int length ); virtual ~AnonShorten(); protected: virtual ANON_RESULT anonymize_chain ( void* buf, unsigned int len ) const; virtual ANON_RESULT anonymize_internal ( const void*const src_buff, void* dst_buff, unsigned int len ) const; private: unsigned int newlen; }; } #endif // __ANON_SHORTEN_H pktanon-master/libpktanon/PktAnon.cpp0000644000000000000000000000500512701423271016735 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "PktAnon.h" #include #include #include "ConfigSAXParser.h" #include "transformations/Transformation.h" #include "transformations/TransformationsConfigurator.h" #include "transformations/ErrorCodes.h" #include "RandomNumberGenerator.h" #include "log.h" #include "debug.h" #ifdef NOXMLCONFIG #include "HardCodedConfig.h" #endif namespace pktanon { // define global variables bool log_initialization = false; bool _plg_quiet = false; bool _plg_verbose = false; # ifdef TRACE_ENABLED std::mutex dbg_mutex; # endif Transformation* PktAnon::link_layer_transformation = nullptr; void PktAnon::initialize ( std::string config_filename ) { _plg_info ( "initializing PktAnon, configuration = " << config_filename ); _plg_verbose ( "parsing configuration file..." ); PktAnonConfig config; # ifndef NOXMLCONFIG try { ConfigSAXParser::instance().parseConfigFromFile ( config_filename, config); } catch ( std::exception& e ) { _plg_error ( "failed to parse configuration: " << e.what() ); throw e; } ConfigSAXParser::destroy(); _plg_verbose ( "parsed configuration file." ); _plg_verbose ( "configuring transformations..." ); # else // TODO fix this /*config =*/ HardCodedConfig::fill_pktanon_config(); # endif auto& tc = TransformationsConfigurator::instance(); tc.configure_packet_transformations(config); tc.configure_transformations_lookups(); // TransformationsConfigurator::destroy(); RandomNumberGenerator::init(); _plg_verbose ( "configured" ); } bool PktAnon::set_link_type ( uint32_t link_type ) noexcept { // TRACEV(link_type); link_layer_transformation = Transformation::getTransformationLinktype ( link_type ); return link_layer_transformation != nullptr; } int PktAnon::transform_packet ( const std::uint8_t* source_buffer, std::uint8_t* destination_buffer, unsigned int max_packet_length ) noexcept { assert ( link_layer_transformation != nullptr ); return link_layer_transformation->transform ( source_buffer, destination_buffer, max_packet_length ); } int PktAnon::get_erroneus_packet_length ( int packet_length ) { return GET_LENGTH_VALUE(packet_length); } const char* PktAnon::get_error_string ( const int packet_length ) { return GET_ERROR_STRING(packet_length); } } pktanon-master/libpktanon/RandomNumberGenerator.h0000644000000000000000000000127412701423271021274 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef __RANDOM_NUMBER_GENERATOR_H #define __RANDOM_NUMBER_GENERATOR_H #include #include namespace pktanon { class RandomNumberGenerator { public: static void init(); static unsigned int generate (unsigned int rangemin, unsigned int rangemax); static void generate_random_bytes (uint8_t* buffer, size_t length); private: static std::random_device engine; }; } #endif // __RANDOM_NUMBER_GENERATOR_H pktanon-master/libpktanon/PktAnonConfig.h0000644000000000000000000001037512701423271017536 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_PKTANONCONFIG_H #define PKTANON_PKTANONCONFIG_H # include # include # include # include # include # include "Params.h" namespace pktanon { struct io_config; /** * PktAnon Configuration */ class PktAnonConfig { public: ~PktAnonConfig(); PktAnonConfig(); // structures class AnonConfig { public: AnonConfig() {}; AnonConfig(std::string&& anon_class, Params&& attributes) : anon_class(std::move(anon_class)), anon_params(std::move(attributes)) {}; const std::string& get_anon_class() const { return anon_class;}; const Params& get_anon_params() const { return anon_params;}; private: friend class PktAnonConfig; std::string anon_class; Params anon_params; }; class PacketConfig { public: PacketConfig(std::string&& protocol) : protocol(std::move(protocol)) {} const std::string& get_protocol() const {return protocol;} bool has_field(const std::string& field_name) const { return fields.find(field_name) != fields.end();} const std::vector& get_anon_config(const std::string& field_name) const { assert(has_field(field_name)); return (*fields.find(field_name)).second; } private: friend class PktAnonConfig; const std::string protocol; std::unordered_map> fields; }; // reading const Params& get_global_parameters() const {return global_parameters;}; // const std::string& get_input_type() const; // const Params& get_input_params() const; // const std::string& get_output_type() const; // const Params& get_output_params() const; const std::vector& get_packets() const {return packets;}; static bool has_value(const std::string& param_value, const std::string&& expected_value) { return param_value.compare(expected_value) == 0;} // modification void add_global_param(std::string&& key, std::string&& value); // void set_input_type(std::string&& type); // void add_input_param(std::string&& key, std::string&& value); // void set_output_type(std::string&& type); // void add_output_param(std::string&& key, std::string&& value); void add_packet(std::string&& protocol); void add_field_to_last_added_packet(std::string&& field_name); void add_field_to_last_added_packet(std::string&& field_name, AnonConfig&& anon_config); void add_anon_to_field_to_last_added_packet(const std::string& field_name, AnonConfig&& anon_config); static void set_anon_class(AnonConfig& anon_config, std::string&& class_name); static void add_anon_param(AnonConfig& anon_config, std::string&& param_name, std::string&& param_value); private: // io_config* input_config; // io_config* output_config; Params global_parameters; std::vector packets; }; // inline std::ostream& operator<<(std::ostream& os, PktAnonConfig& cfg) // { // os << "PacketAnon Configuration: " << std::endl; // os << "\tinput file name: " << cfg.input_file << std::endl; // os << "\toutput file name: " << cfg.output_file << std::endl; // os << "\tparameters(size = " << cfg.parameters.size() << "): " << std::endl; // for (auto& param : cfg.parameters) // { // os << "\t\t" << param.first << ": " << param.second << std::endl; // } // os << "\tpackets configuration(size = " << cfg.packets.size() << "): " << std::endl; // for (auto& pkt : cfg.packets) // { // os << "\t\t" << pkt.pkt_name << " (fieldsnum= " << pkt.fields.size() << "):"<< std::endl; // for (auto& field: pkt.fields) // { // os << "\t\t\t" << field.first << std::endl; // for (auto& anon: field.second) // { // os << "\t\t\t\t" << anon.anon_class << std::endl; // for (auto& attr: anon.attributes) // { // os << "\t\t\t\t\t" << attr.first << ": " << attr.second << std::endl; // } // } // } // } // // return os; // } } #endif // PKTANON_PKTANONCONFIG_H pktanon-master/libpktanon/optmacros.h0000644000000000000000000000033712701423271017042 0ustar rootroot#ifndef PKTANON_OPTMACROS_H #define PKTANON_OPTMACROS_H namespace pktanon { // branch predictions # define unlikely(expr) __builtin_expect(!!(expr), 0) # define likely(expr) __builtin_expect(!!(expr), 1) } #endif pktanon-master/libpktanon/RandomNumberGenerator.cpp0000644000000000000000000000174112701423271021626 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "RandomNumberGenerator.h" #include #include using namespace pktanon; std::random_device RandomNumberGenerator::engine; void RandomNumberGenerator::init() { // DO NOTHING } unsigned int RandomNumberGenerator::generate (unsigned int min, unsigned int max) { std::uniform_int_distribution dist (min, max); return dist (engine); } void RandomNumberGenerator::generate_random_bytes (uint8_t* buffer, size_t length) { std::uniform_int_distribution distribution (0, 0xff); for(int j = 0; j < length ; j++) { buffer[j] = distribution(engine); } // std::generate (buffer, buffer + length, [distribution,engine]() {return distribution (engine);}); } pktanon-master/libpktanon/PHF.h0000644000000000000000000000467612701423271015462 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_PHF_H #define PKTANON_PHF_H # include #include #include #include #include #include namespace pktanon { static const unsigned prime = 31; /// family of functions h = ((a*key + b) mod p) mod size /// see Ramakrishna, M. V. "A Simple Perfect Hashing Method for Static Sets." ICCI. 1992. unsigned int phf_family(unsigned a, unsigned b, unsigned size, unsigned key) { // size will be power of two // return ((a * key + b) % prime ) % size; return ((a * key + b) % prime ) & (size - 1); } std::size_t round_to_pow2(std::size_t size) { // check if size is a power of two (http://graphics.stanford.edu/~seander/bithacks.html#ModulusDivisionEasy) if ((size & (size - 1)) == 0) { return size; } // round to the pow2 size--; size |= size >> 1; size |= size >> 2; size |= size >> 4; size |= size >> 8; size |= size >> 16; size++; return size; } template std::function generate_phf(std::unordered_map& keys_values, std::size_t& table_size) { if (keys_values.size() == 0) { table_size = 0; return [](unsigned key) {return 0;}; } // round size to the next power of two table_size = round_to_pow2(keys_values.size()); // TRACEV(table_size); // calculate a and b std::set hashes; std::function h; unsigned a_0 = 1; unsigned b_0 = 0; unsigned size = table_size; for (unsigned a = a_0; a <= prime; a++) { for (unsigned b = b_0; b <= prime; b++) { hashes.clear(); bool collision = false; h = std::bind(phf_family, a, b, size, std::placeholders::_1); for (auto& key_value_pair: keys_values) { auto hash = h(key_value_pair.first); if (hashes.find(hash) == hashes.end()) { hashes.insert(hash); } else { collision = true; break; } } if (!collision) { // TRACEV(a); // TRACEV(b); // TRACEV(size); return h; } } } throw std::runtime_error("phf not found"); } } #endif // PKTANON_PHF_H pktanon-master/libpktanon/AnonFactory.h0000644000000000000000000000274212701423271017260 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__ANON_FACTORY_H #define PKTANON__ANON_FACTORY_H # include # include # include # include "anonprimitives/AnonPrimitive.h" #include "Params.h" namespace pktanon { #define REGISTER_ANON(TYPE) \ AnonFactory::creator TYPE##creator(#TYPE, [](const Params& params) {return new TYPE();}); #define REGISTER_ANON_PARAM(TYPE, FUNC) \ AnonFactory::creator TYPE##creator(#TYPE, FUNC); class AnonFactory { public: AnonFactory(); ~AnonFactory(); typedef std::function AnonConstructor; static void register_anon(string anon_name, AnonConstructor constructor); // static AnonPrimitive* create_anon(std::string&& anon_name, std::unordered_map attrmap); static AnonPrimitive* create_anon(const std::string& anon_name, const Params& params); struct creator { creator(std::string anon_name, AnonConstructor constructor) { register_anon(anon_name, constructor); } }; static std::unordered_map& get_mappings() { static std::unordered_map mappings; return mappings; } private: }; } #endif // PKTANON__ANON_FACTORY_H pktanon-master/libpktanon/PktAnonConfig.cpp0000644000000000000000000000571212701423271020070 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "PktAnonConfig.h" #include "debug.h" namespace pktanon { struct io_config { std::string type; Params params; }; PktAnonConfig::~PktAnonConfig() { // delete input_config; // delete output_config; } PktAnonConfig::PktAnonConfig() /*:*/ // input_config(new io_config()), output_config(new io_config()) { } //getters ------------------------------------------------------------ // const std::string& PktAnonConfig::get_input_type() const { return input_config->type; } // const Params& PktAnonConfig::get_input_params() const { return input_config->params; } // const std::string& PktAnonConfig::get_output_type() const { return output_config->type; } // const Params& PktAnonConfig::get_output_params() const { return output_config->params; } //setters ------------------------------------------------------------ # define ADD_PARAM(KEY, VALUE) add_param(std::move(KEY), std::move(VALUE)) void PktAnonConfig::add_global_param ( std::string&& key, std::string&& value ) { global_parameters.ADD_PARAM ( key, value ); } // void PktAnonConfig::set_input_type(std::string&& type) // { // input_config->type = type.data(); // } // // void PktAnonConfig::add_input_param(std::string&& key, std::string&& value) // { // input_config->params.ADD_PARAM(key, value); // } // // // void PktAnonConfig::set_output_type(std::string&& type) // { // output_config->type = type; // } // // void PktAnonConfig::add_output_param(std::string&& key, std::string&& value) // { // output_config->params.ADD_PARAM(key, value); // } void PktAnonConfig::add_packet ( std::string&& protocol ) { packets.emplace_back ( std::move ( protocol ) ); } void PktAnonConfig::add_field_to_last_added_packet ( std::string&& field_name ) { assert ( packets.size() > 0 ); packets[packets.size() - 1].fields[field_name] = std::vector(); } void PktAnonConfig::add_field_to_last_added_packet ( std::string&& field_name, PktAnonConfig::AnonConfig&& anon_config ) { packets[packets.size() - 1].fields[field_name] = std::vector(); packets[packets.size() - 1].fields[field_name].emplace_back ( anon_config ); } void PktAnonConfig::set_anon_class ( AnonConfig& anon_config, std::string&& class_name ) { anon_config.anon_class = std::move ( class_name ); } void PktAnonConfig::add_anon_param ( PktAnonConfig::AnonConfig& anon_config, std::string&& param_name, std::string&& param_value ) { anon_config.anon_params.add_param ( std::move ( param_name ), std::move ( param_value ) ); } void PktAnonConfig::add_anon_to_field_to_last_added_packet ( const std::string& field_name, pktanon::PktAnonConfig::AnonConfig&& anon_config ) { packets[packets.size() - 1].fields[field_name].emplace_back ( anon_config ); } } pktanon-master/libpktanon/PHT.h0000644000000000000000000000525512701423271015472 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_PHT_H #define PKTANON_PHT_H // # include # include #include # include namespace pktanon { template class PHT { public: typedef std::pair pht_entry; typedef std::function phf; ~PHT(); PHT(); PHT(phf h, std::size_t num_entries); PHT(PHT&& other); PHT(const PHT& other) = delete; PHT& operator=(PHT& other) = delete; PHT& operator=(PHT&& other); void insert(Key key, Value value); Value lookup(Key key, Value default_value) const; bool contains(Key key) const; private: pht_entry* entries; phf h; }; template PHT::~PHT() { delete[] entries; } template PHT::PHT(phf h, std::size_t num_entries): h(h), entries(new pht_entry[num_entries]) { memset(entries, 0xff, num_entries * sizeof(pht_entry)); } template PHT::PHT(): h(), entries(nullptr) { } template PHT::PHT(PHT&& other): h(other.h), entries(other.entries) { other.entries = nullptr; } template PHT& PHT::operator=(PHT&& other) { h = other.h; delete[] entries; entries = other.entries; other.entries = nullptr; return *this; } //---------------------------------------------------------------------------------------------------------------------- template void PHT::insert(Key key, Value value) { // TRACEV((int)key << " " << value); auto index = h(key); entries[index] = std::make_pair(key, value); } // template // void PHT::insert_raw(size_t index, Key key, Value value) // { // // TRACE(index << " " << (int)key << " " << value); // entries[index] = std::make_pair(key, value); // } template Value PHT::lookup(Key key, Value default_value) const { auto index = h(key); if (entries[index].first == key) { return entries[index].second; } else { return default_value; } } template bool PHT::contains(Key key) const { auto index = h(key); // TRACEV((int)key); // TRACEV((int)entries[index].first); return (entries[index].first == key); } } #endif // PKTANON_PHF_H pktanon-master/libpktanon/HardCodedConfig.cpp0000644000000000000000000003244212701423271020333 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "HardCodedConfig.h" #include "PktAnonConfig.h" // #include "globals.h" using namespace pktanon; static PktAnonConfig::AnonConfig create_anon_identity_config() { PktAnonConfig::AnonConfig anon_config; PktAnonConfig::set_anon_class(anon_config,"AnonIdentity"); return anon_config; } static PktAnonConfig::AnonConfig create_anon_shorten_config() { PktAnonConfig::AnonConfig anon_config; PktAnonConfig::set_anon_class(anon_config,"AnonShorten"); PktAnonConfig::add_anon_param(anon_config,"newlen", "0"); return anon_config; } static PktAnonConfig::AnonConfig create_anon_hash_sha256_config() { PktAnonConfig::AnonConfig anon_config; PktAnonConfig::set_anon_class(anon_config,"AnonHashSha256"); // PktAnonConfig::add_anon_param(anon_config,"newlen", "0"); return anon_config; } static PktAnonConfig::AnonConfig create_anon_white_noise_config() { PktAnonConfig::AnonConfig anon_config; PktAnonConfig::set_anon_class(anon_config,"AnonWhitenoise"); PktAnonConfig::add_anon_param(anon_config,"strength", "4"); return anon_config; } static void config_identity(PktAnonConfig* config); static void config_high(PktAnonConfig* config); PktAnonConfig* HardCodedConfig::fill_pktanon_config() { // TODO PktAnonConfig* conf = nullptr; // const_cast(&config); ////// input = file (filename) // conf->set_input_type("file"); // conf->add_input_param("filename", "bigFlows.pcap"); // conf->add_input_param("filename", "smallFlows.pcap"); // conf->add_input_param("filename", "traces/ipv6trace.pcap"); // conf->add_input_param("filename", "traces/file.pcap"); // linux-coocked-captures // conf->add_input_param("filename", "traces/iptest.pcap"); // something small // conf->add_input_param("filename", "traces/arp_big.pcap"); // something small // conf->add_input_param("filename", "-"); ///// input = live libpcap capture // conf->set_input_type("libpcap-live"); // conf->add_input_param("interface", "enp2s1"); // interface name/any // conf->add_input_param("filter", "tcp"); // filter (as in tcpdump or wireshark) // conf->add_input_param("promiscuous", "yes"); // requires specififed value for interface // conf->add_input_param("pcap-timeout", 1000); // timeout value for pcap_open_life // conf->set_input_type("socket-raw"); // conf->add_input_param("interface", "enp2s1"); // name/any (no auto!) // conf->add_input_param("promiscuous", "yes"); // requires specififed value for interface // conf->set_output_type("file"); // conf->add_output_param("filename", "out.pcap"); // conf->add_output_param("flush-after-burst", "yes"); // conf->add_output_param("filename", "-"); // conf->set_output_type("libpcap-file"); // conf->add_output_param("filename", "out.pcap"); // conf->add_output_param("flush-after-burst", "yes"); // conf->set_output_type("libpcap-inject"); // conf->add_output_param("interface", "enp2s1"); // filter (as in tcpdump or wireshark) // conf->set_output_type("socket"); // conf->add_output_param("use-ipv6", "yes"); // yes|no (default no) // conf->add_output_param("protocol", "udp"); // yes|no (default no) // conf->add_output_param("dst-host", "::1"); // conf->add_output_param("dst-port", "19009"); // make sure that ethernet payload is at least 46 bytes conf->add_global_param("pad-ethernet-packets", "never"); // always,keep-length,never conf->add_global_param("recalculate-ipv4-header-checksum", "yes"); // yes/no // ip and ipv6 will call checksum methods on PseudoheaderAwareTransformations conf->add_global_param("recalculate-payload-checksums", "yes"); // yes/no // don't read more than @value@ bytes from input source for each packet conf->add_global_param("pkt-burst-size", "32"); // numerical values conf->add_global_param("pkt-burst-timeout", "1000"); // numerical values conf->add_global_param("trim-input-packets", "256"); // numerical values //---------------------------------------------------------------------------- // execution model selection // conf->add_global_param("multiple-threads", "yes"); // disable concurrency conf->add_global_param("threads-number", "1"); // auto == std::thread::hardware_concurrency() // conf->add_global_param("separate-input-thread", "no"); // auto == separate thread for live sources conf->add_global_param("separate-output-thread", "no"); // auto == no //---------------------------------------------------------------------------- // execution model configuration (env_config) //---------------------------------------------------------------------------- // config_identity(conf); config_high(conf); return conf; } void config_identity(PktAnonConfig* config) { config->add_packet("ethernet"); config->add_field_to_last_added_packet("mac-source", create_anon_identity_config()); config->add_field_to_last_added_packet("mac-dest", create_anon_identity_config()); config->add_field_to_last_added_packet("ethertype", create_anon_identity_config()); config->add_packet("linux_sll"); config->add_field_to_last_added_packet("packet-type", create_anon_identity_config()); config->add_field_to_last_added_packet("ll-address-type", create_anon_identity_config()); config->add_field_to_last_added_packet("ll-address", create_anon_identity_config()); config->add_field_to_last_added_packet("protocol", create_anon_identity_config()); config->add_packet("ipv4"); config->add_field_to_last_added_packet("tos", create_anon_identity_config()); config->add_field_to_last_added_packet("identification", create_anon_identity_config()); config->add_field_to_last_added_packet("flags", create_anon_identity_config()); config->add_field_to_last_added_packet("fragment", create_anon_identity_config()); config->add_field_to_last_added_packet("ttl", create_anon_identity_config()); config->add_field_to_last_added_packet("protocol", create_anon_identity_config()); config->add_field_to_last_added_packet("src-ip", create_anon_identity_config()); config->add_field_to_last_added_packet("dest-ip", create_anon_identity_config()); config->add_field_to_last_added_packet("options", create_anon_shorten_config()); /* */ config->add_packet("ipv6"); config->add_field_to_last_added_packet("traffic-class", create_anon_identity_config()); config->add_field_to_last_added_packet("flow-label", create_anon_identity_config()); config->add_field_to_last_added_packet("hop-limit", create_anon_identity_config()); config->add_field_to_last_added_packet("src-ip", create_anon_identity_config()); config->add_field_to_last_added_packet("dest-ip", create_anon_identity_config()); config->add_field_to_last_added_packet("next-header", create_anon_identity_config()); // // // // config->add_packet("udp"); config->add_field_to_last_added_packet("source-port", create_anon_identity_config()); config->add_field_to_last_added_packet("dest-port", create_anon_identity_config()); // // // // // // // // // // config->add_packet("tcp"); config->add_field_to_last_added_packet("source-port", create_anon_identity_config()); config->add_field_to_last_added_packet("dest-port", create_anon_identity_config()); config->add_field_to_last_added_packet("seq", create_anon_identity_config()); config->add_field_to_last_added_packet("ack", create_anon_identity_config()); config->add_field_to_last_added_packet("flags", create_anon_identity_config()); config->add_field_to_last_added_packet("window-size", create_anon_identity_config()); config->add_field_to_last_added_packet("urgent-pointer", create_anon_identity_config()); config->add_field_to_last_added_packet("options", create_anon_shorten_config()); // payload must be present config->add_packet("payload"); config->add_field_to_last_added_packet("payload", create_anon_shorten_config()); } void config_high(PktAnonConfig* config) { config->add_packet("ethernet"); config->add_field_to_last_added_packet("mac-source", create_anon_hash_sha256_config()); config->add_field_to_last_added_packet("mac-dest", create_anon_hash_sha256_config()); config->add_field_to_last_added_packet("ethertype", create_anon_identity_config()); config->add_packet("linux_sll"); config->add_field_to_last_added_packet("packet-type", create_anon_identity_config()); config->add_field_to_last_added_packet("ll-address-type", create_anon_identity_config()); config->add_field_to_last_added_packet("ll-address", create_anon_identity_config()); config->add_field_to_last_added_packet("protocol", create_anon_identity_config()); config->add_packet("ipv4"); config->add_field_to_last_added_packet("tos", create_anon_identity_config()); config->add_field_to_last_added_packet("identification", create_anon_identity_config()); config->add_field_to_last_added_packet("flags", create_anon_identity_config()); config->add_field_to_last_added_packet("fragment", create_anon_identity_config()); config->add_field_to_last_added_packet("ttl", create_anon_identity_config()); config->add_field_to_last_added_packet("protocol", create_anon_identity_config()); config->add_field_to_last_added_packet("src-ip", create_anon_identity_config()); config->add_field_to_last_added_packet("dest-ip", create_anon_identity_config()); config->add_field_to_last_added_packet("options", create_anon_shorten_config()); /* */ config->add_packet("ipv6"); config->add_field_to_last_added_packet("traffic-class", create_anon_identity_config()); config->add_field_to_last_added_packet("flow-label", create_anon_identity_config()); config->add_field_to_last_added_packet("hop-limit", create_anon_identity_config()); config->add_field_to_last_added_packet("src-ip", create_anon_hash_sha256_config()); config->add_field_to_last_added_packet("dest-ip", create_anon_hash_sha256_config()); config->add_field_to_last_added_packet("next-header", create_anon_identity_config()); // // // // config->add_packet("udp"); config->add_field_to_last_added_packet("source-port", create_anon_hash_sha256_config()); config->add_field_to_last_added_packet("dest-port", create_anon_hash_sha256_config()); // // // // // // // // // // config->add_packet("tcp"); config->add_field_to_last_added_packet("source-port", create_anon_hash_sha256_config()); config->add_field_to_last_added_packet("dest-port", create_anon_hash_sha256_config()); config->add_field_to_last_added_packet("seq", create_anon_hash_sha256_config()); config->add_field_to_last_added_packet("ack", create_anon_hash_sha256_config()); config->add_field_to_last_added_packet("flags", create_anon_identity_config()); config->add_field_to_last_added_packet("window-size", create_anon_identity_config()); config->add_field_to_last_added_packet("urgent-pointer", create_anon_hash_sha256_config()); config->add_field_to_last_added_packet("options", create_anon_shorten_config()); // payload must be present config->add_packet("payload"); config->add_field_to_last_added_packet("payload", create_anon_shorten_config()); } pktanon-master/libpktanon/ProtocolUtils.h0000644000000000000000000000112612701423271017652 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_PROTOCOLUTILS_H #define PKTANON_PROTOCOLUTILS_H # include namespace pktanon { static const unsigned int ETHERNET_MTU = 1500; enum EtherTypes : uint16_t { ETHERTYPE_IP = 0x0800, ETHERTYPE_ARP = 0x0806, ETHERTYPE_IPV6 = 0x86DD, ETHERTYPE_VLAN = 0x8100 }; } #endif // PKTANON_PROTOCOLUTILS_H pktanon-master/libpktanon/Checksum.h0000644000000000000000000000107212701423271016572 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_CHECKSUM_H #define PKTANON_CHECKSUM_H #include #include namespace pktanon { class Checksum { public: Checksum() : sum(0) {}; void update(void* buffer, size_t length); uint16_t final(); private: uint32_t sum; }; } #endif // PKTANON_CHECKSUM_H pktanon-master/libpktanon/log.h0000644000000000000000000000142212701423271015610 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_LOG_H #define PKTANON_LOG_H # include namespace pktanon { extern bool _plg_quiet; extern bool _plg_verbose; # define LOG_STREAM std::cerr # define _plg_error(val) \ LOG_STREAM << "error: " << val << std::endl; # define _plg_warn(val) \ LOG_STREAM << "warning: " << val << std::endl; # define _plg_info(val) \ if (!_plg_quiet) {LOG_STREAM << val << std::endl;} # define _plg_verbose(val) \ if (_plg_verbose) {LOG_STREAM << val << std::endl;} } #endif // PKTANON_LOG_H pktanon-master/libpktanon/ConfigSAXParser.cpp0000644000000000000000000001716412701423271020332 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "ConfigSAXParser.h" //---------------------------------------------------------------- #include #include "debug.h" #include #include #include #include using namespace xercesc; namespace pktanon { class ConfigParserHandler : public xercesc::DefaultHandler { public: virtual ~ConfigParserHandler(); ConfigParserHandler(PktAnonConfig& config); void startElement ( const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname, const xercesc::Attributes& attrs ); virtual void endElement ( const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname ); virtual void characters ( const XMLCh* const chars, const XMLSize_t length ); void fatalError ( const xercesc::SAXParseException& ); private: PktAnonConfig& config; std::string last_field_name; size_t current_element; std::string param_name; }; ConfigParserHandler::ConfigParserHandler( PktAnonConfig& config ) : config ( config ) { } ConfigParserHandler::~ConfigParserHandler() = default; const size_t element_hash_base = 512; const size_t e_PKTANON_config = 195; const size_t e_parameters = 439; const size_t e_param = 51; const size_t e_anonymizations = 122; const size_t e_packet = 36; const size_t e_field = 152; const size_t e_anon = 184; void ConfigParserHandler::startElement ( const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname, const xercesc_3_1::Attributes& attrs ) { XMLString::lowerCase ( const_cast ( localname ) ); size_t element = XMLString::hash ( localname, element_hash_base ); XMLCh* attr_NAME = XMLString::transcode ( "name" ); XMLCh* attr_ANON = XMLString::transcode ( "anon" ); XMLCh* attr_TYPE = XMLString::transcode ( "type" ); current_element = element; switch ( current_element ) { case e_PKTANON_config: break; case e_parameters: break; case e_param: { param_name = XMLString::transcode ( attrs.getValue ( attr_NAME ) ); break; } case e_anonymizations: break; case e_packet: { std::string packet_name = XMLString::transcode ( attrs.getValue ( ( XMLSize_t ) 0 ) ); config.add_packet ( std::move ( packet_name ) ); break; } case e_field: { if ( attrs.getLength() == 1 ) { std::string field_name = XMLString::transcode ( attrs.getValue ( attr_NAME ) ); last_field_name = field_name; config.add_field_to_last_added_packet ( std::move ( field_name ) ); } else { std::string field_name; PktAnonConfig::AnonConfig anon_config; for ( XMLSize_t j = 0; j < attrs.getLength(); j++ ) { std::string att1 = XMLString::transcode ( attrs.getLocalName ( ( XMLSize_t ) j ) ); std::string att2 = XMLString::transcode ( attrs.getValue ( ( XMLSize_t ) j ) ); auto* attr_name = attrs.getLocalName ( j ); auto* attr_value = attrs.getValue ( j ); if ( XMLString::compareString ( attr_NAME, attr_name ) == 0 ) { field_name = std::string ( XMLString::transcode ( attr_value ) ); } else if ( XMLString::compareString ( attr_ANON, attr_name ) == 0 ) { config.set_anon_class ( anon_config, std::string ( XMLString::transcode ( attr_value ) ) ); } else { config.add_anon_param ( anon_config, std::string ( XMLString::transcode ( attr_name ) ), std::string ( XMLString::transcode ( attr_value ) ) ); } } config.add_field_to_last_added_packet ( std::move ( field_name ), std::move ( anon_config ) ); } break; case e_anon: { PktAnonConfig::AnonConfig anon_config; for ( XMLSize_t j = 0; j < attrs.getLength(); j++ ) { std::string att1 = XMLString::transcode ( attrs.getLocalName ( ( XMLSize_t ) j ) ); std::string att2 = XMLString::transcode ( attrs.getValue ( ( XMLSize_t ) j ) ); auto* attr_name = attrs.getLocalName ( j ); auto* attr_value = attrs.getValue ( j ); if ( XMLString::compareString ( attr_NAME, attr_name ) == 0 ) { config.set_anon_class ( anon_config, std::string ( XMLString::transcode ( attr_value ) ) ); } else { config.add_anon_param ( anon_config, std::string ( XMLString::transcode ( attr_name ) ), std::string ( XMLString::transcode ( attr_value ) ) ); } } config.add_anon_to_field_to_last_added_packet ( last_field_name, std::move ( anon_config ) ); } break; default: char* message = XMLString::transcode ( localname ); PRINT ( "unknown element: " << message << ": " << element ); XMLString::release ( &message ); } } } void ConfigParserHandler::endElement ( const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname ) { current_element = 0; } void ConfigParserHandler::characters ( const XMLCh* const chars, const XMLSize_t length ) { switch ( current_element ) { case e_param: { std::string param_value ( XMLString::transcode ( chars ) ); config.add_global_param ( std::move ( param_name ), std::move ( param_value ) ); } } current_element = 0; } void ConfigParserHandler::fatalError ( const xercesc::SAXParseException& exception ) { throw exception; // char* message = XMLString::transcode(exception.getMessage()); // throw std::runtime_error(std::string("Fatal Error: ") + message + " at line: " + std::to_string(exception.getLineNumber())); } //------------------------------------------------------------------------------------------------------------- // ConfigParser //------------------------------------------------------------------------------------------------------------- ConfigSAXParser* ConfigSAXParser::pInstance = new ConfigSAXParser(); ConfigSAXParser::~ConfigSAXParser() = default; ConfigSAXParser::ConfigSAXParser() {} void ConfigSAXParser::parseConfigFromFile ( const std::string& filename, pktanon::PktAnonConfig& config ) { try { XMLPlatformUtils::Initialize(); } catch ( const XMLException& e ) { char* message = XMLString::transcode ( e.getMessage() ); throw std::runtime_error ( message ); } std::unique_ptr parser = std::unique_ptr ( XMLReaderFactory::createXMLReader() ); parser->setFeature ( XMLUni::fgSAX2CoreValidation, true ); parser->setFeature ( XMLUni::fgSAX2CoreNameSpaces, true ); // optional ConfigParserHandler* config_parser = new ConfigParserHandler(config); parser->setContentHandler ( config_parser ); parser->setErrorHandler ( config_parser ); try { parser->parse ( filename.c_str() ); } catch ( const XMLException& e ) { char* message = XMLString::transcode ( e.getMessage() ); throw std::runtime_error ( message ); } catch ( const SAXParseException& e ) { char* message = XMLString::transcode ( e.getMessage() ); throw std::runtime_error ( std::string ( "Fatal Error: " ) + message + " at line: " + std::to_string ( e.getLineNumber() ) ); } catch ( const std::exception& e ) { throw e; } catch ( ... ) { throw std::runtime_error ( "unexpected exception at parseConfigFromFile" ); } } } pktanon-master/libpktanon/Makefile.am0000644000000000000000000000627012701423271016720 0ustar rootrootinclude $(top_srcdir)/include.mk lib_LIBRARIES = libpktanon.a ############################################################################### ### pktanon library ############################################################################### pktanon_sources = $(sources) $(transformation_sources) $(anonprim_sources) $(c_sources) libpktanon_a_SOURCES = $(pktanon_sources) # included anon primitives: anonprim_sources = anonprim_sources += AnonFactory.cpp anonprim_sources += anonprimitives/AnonPrimitive.cpp anonprim_sources += anonprimitives/AnonConstOverwrite.cpp anonprim_sources += anonprimitives/AnonConstOverwriteRange.cpp # anonprim_sources += anonprimitives/AnonContinuousChar.cpp anonprim_sources += anonprimitives/AnonIdentity.cpp anonprim_sources += anonprimitives/AnonRandomize.cpp anonprim_sources += anonprimitives/AnonShorten.cpp anonprim_sources += anonprimitives/AnonShuffle.cpp anonprim_sources += anonprimitives/AnonWhitenoise.cpp anonprim_sources += anonprimitives/AnonBroadcastHandler.cpp anonprim_sources += anonprimitives/AnonKnownEthertypeHandler.cpp anonprim_sources += anonprimitives/AnonKnownProtocolHandler.cpp anonprim_sources += anonprimitives/cryptopan/rijndael.cpp anonprim_sources += anonprimitives/cryptopan/panonymizer.cpp anonprim_sources += anonprimitives/AnonCryptoPan.cpp if USE_NETTLE anonprim_sources += anonprimitives/AnonHashSha1_Nettle.cpp anonprim_sources += anonprimitives/AnonHashSha256_Nettle.cpp anonprim_sources += anonprimitives/AnonHashHmacSha1_Nettle.cpp endif if BYTEWISEHASHANONS anonprim_sources += anonprimitives/AnonBytewise.cpp anonprim_sources += anonprimitives/AnonBytewiseHashHmacSha1.cpp anonprim_sources += anonprimitives/AnonBytewiseHashSha1.cpp # anonprim_sources += anonprimitives/hmacsha1/sha1_hmac.cpp endif # included transformations: transformation_sources = transformation_sources += transformations/Transformation.cpp transformation_sources += transformations/EthernetPacketTransformation.cpp transformation_sources += transformations/VlanTagTransformation.cpp transformation_sources += transformations/LinuxCookedCaptureTransformation.cpp transformation_sources += transformations/ArpPacketTransformation.cpp transformation_sources += transformations/IPv4PacketTransformation.cpp transformation_sources += transformations/IPv6PacketTransformation.cpp transformation_sources += transformations/UdpPacketTransformation.cpp transformation_sources += transformations/TcpPacketTransformation.cpp transformation_sources += transformations/IcmpPacketTransformation.cpp transformation_sources += transformations/ICMPv6PacketTransformation.cpp transformation_sources += transformations/PayloadTransformation.cpp transformation_sources += transformations/TransformationsConfigurator.cpp transformation_sources += transformations/DefaultTransformationsConfigurator.cpp transformation_sources += transformations/ErrorCodes.cpp sources = # utilities: sources += Checksum.cpp sources += RandomNumberGenerator.cpp # configuration: sources += ConfigSAXParser.cpp sources += PktAnonConfig.cpp sources += PktAnon.cpp # config for profiling: sources += HardCodedConfig.cpp # c sources: c_sources = c_sources += PktAnonC.cpppktanon-master/libpktanon/AnonFactory.cpp0000644000000000000000000000162012701423271017605 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "AnonFactory.h" #include #include #include "debug.h" namespace pktanon { AnonFactory::~AnonFactory() { } AnonFactory::AnonFactory() { } void AnonFactory::register_anon(string anon_name, pktanon::AnonFactory::AnonConstructor constructor) { get_mappings().emplace(anon_name, constructor); } AnonPrimitive* AnonFactory::create_anon(const string& anon_name, const pktanon::Params& params) { auto constructor = get_mappings().find(anon_name); if (constructor == get_mappings().end()) throw std::runtime_error("anon primitive " + anon_name + " not found"); return (*constructor).second(params); } } pktanon-master/libpktanon/Params.h0000644000000000000000000000777112701423271016267 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ # ifndef PKTANON_PARAMS_H # define PKTANON_PARAMS_H # include # include namespace pktanon { enum class TFD_VAL {TRUE, FALSE, DEFAULT}; struct ID_VAL { ID_VAL() : int_val(0), is_default(true) {} ID_VAL(unsigned long value) : int_val(value), is_default(false) {} unsigned long int_val; bool is_default; }; # define THROW_MISSING_VAL(key) throw std::runtime_error(std::string("missing parameter: ") + key); # define THROW_WRONG_VAL(key, wrong_value) throw std::runtime_error("illegal value for boolean parameter " + key + ": " + wrong_value); /** * wrapper arount hashtable (inspired by java.lang.Properties) */ class Params { public: ~Params() = default; Params() :props() {}; // Params(Params&& other) : props(std::move(other.props)) {}; bool has_param(const std::string& key) const { return props.find(key) != props.end(); } const std::string& get_param(const std::string& key) const { if (!has_param(key)) throw std::runtime_error(std::string("missing parameter: ") + key); return (*props.find(key)).second; } const std::string& get_param(const std::string& key, const std::string& default_value) const { if (has_param(key)) return (*props.find(key)).second; return default_value; } bool get_bool_param(const std::string& key) const { if (!has_param(key)) throw std::runtime_error(std::string("missing parameter: ") + key); const auto& str_val = (*props.find(key)).second; if (is_true_value(str_val)) { return true; } else if (is_false_value(str_val)) { return false; } else { THROW_WRONG_VAL(key, str_val); } } bool get_bool_param(const std::string& key, bool default_value) const { if (has_param(key)) { return get_bool_param(key); } return default_value; } ///param with values - true/false/default TFD_VAL get_tfd_param(const std::string& key) const { if (!has_param(key)) return TFD_VAL::DEFAULT; const auto& str_val = (*props.find(key)).second; if (is_default_value(str_val)) {return TFD_VAL::DEFAULT;} else if (is_true_value(str_val)) {return TFD_VAL::TRUE;} else if (is_false_value(str_val)) {return TFD_VAL::FALSE;} else {THROW_WRONG_VAL(key, str_val);} } unsigned long get_uint_param(const std::string& key) const { if (!has_param(key)) throw std::runtime_error(std::string("missing parameter: ") + key); const auto& str_val = (*props.find(key)).second; return std::stoul(str_val); } unsigned long get_uint_param(const std::string& key, unsigned long default_value) const { if (has_param(key)) { return get_uint_param(key); } return default_value; } ID_VAL get_uintdefault_param(const std::string& key) const { if (!has_param(key)) return ID_VAL(); const auto& str_val = (*props.find(key)).second; if (is_default_value(str_val)) {return ID_VAL();} else { return ID_VAL(std::stoul(str_val)); } } bool is_missing_or_has_default_value(const std::string& key) const { if (!has_param(key)) return true; const auto& str_val = (*props.find(key)).second; if (is_default_value(str_val)) return true; return false; } void add_param(const std::string&& key, const std::string&& value) { props.emplace(std::move(key), std::move(value)); } private: bool is_default_value(const std::string& value) const { return value.compare("auto") == 0 || value.compare("default") == 0;} bool is_true_value(const std::string& value) const { return value.compare("yes") == 0 || value.compare("on") == 0 || value.compare("1") == 0;} bool is_false_value(const std::string& value) const { return value.compare("no") == 0 || value.compare("off") == 0 || value.compare("0") == 0;} std::unordered_map props; }; } # endif pktanon-master/libpktanon/debug.h0000644000000000000000000000221112701423271016112 0ustar rootroot#ifndef PKTANON_DEBUG_H #define PKTANON_DEBUG_H # ifdef TRACE_ENABLED # include # include # include namespace pktanon { extern std::mutex dbg_mutex; # define TRACE(x) \ do {std::lock_guard _(dbg_mutex); std::cout << std::dec << __FILE__ << ":" << __LINE__ << ": " << x << std::endl;} while (0); # define TRACEV(x) \ do {std::lock_guard _(dbg_mutex); std::cout << std::dec << __FILE__ << ":" << __LINE__ << ": " << #x << '=' << x << std::endl;} while (0); # define PRINT(x) \ do { std::lock_guard _(dbg_mutex);std::cout << x << std::endl;} while (0); # define HB() \ do { std::lock_guard _(dbg_mutex);std::cout << __FILE__ << ":" << __LINE__ << ": " << __func__ << std::endl;} while (0); } // inline void display_hex(unsigned length, char *data) // { // unsigned i; // uint8_t* data2 = reinterpret_cast(data); // // for (i = 0; i #include using namespace pktanon; void Checksum::update(void* buffer, size_t length) { uint16_t* buff = (uint16_t*) buffer; const unsigned buff_len = length / 2; for (size_t nWords = (buff_len/4); nWords > 0; nWords-- ) { sum += *buff++; sum += *buff++; sum += *buff++; sum += *buff++; } for (size_t nWords = (buff_len & 0x03) ; nWords > 0; nWords-- ) sum += *buff++; if (unlikely(length & 1)) sum += *buff & htons(0xFF00); } uint16_t Checksum::final() { sum = (sum & 0xFFFF) + (sum >> 16); sum = (sum & 0xFFFF) + (sum >> 16); sum = ~sum; return sum; } // #pragma GCC pop_options // checksum algorithm // u16 tcp_sum_calc(u16 len_tcp, u16 src_addr[],u16 dest_addr[], BOOL padding, u16 buff[]) // { // u16 prot_tcp=6; // u16 padd=0; // u16 word16; // u32 sum; // // // Find out if the length of data is even or odd number. If odd, // // add a padding byte = 0 at the end of packet // if (padding&1==1){ // padd=1; // buff[len_tcp]=0; // } // // //initialize sum to zero // sum=0; // // // make 16 bit words out of every two adjacent 8 bit words and // // calculate the sum of all 16 vit words // for (i=0;i>16) // sum = (sum & 0xFFFF)+(sum >> 16); // // // Take the one's complement of sum // sum = ~sum; // // return ((unsigned short) sum); // } pktanon-master/libpktanon/HardCodedConfig.h0000644000000000000000000000076112701423271017777 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_HARDCODEDCONFIG_H # define PKTANON_HARDCODEDCONFIG_H namespace pktanon { class PktAnonConfig; class HardCodedConfig { public: static PktAnonConfig* fill_pktanon_config(); }; } #endif // PKTANON_TESTCONFIG_H pktanon-master/libpktanon/ConfigSAXParser.h0000644000000000000000000000133612701423271017771 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_CONFIGSAXPARSER_H #define PKTANON_CONFIGSAXPARSER_H #include "PktAnonConfig.h" namespace pktanon { class ConfigSAXParser { public: static ConfigSAXParser& instance() { return *pInstance; } static void destroy() { delete pInstance; } void parseConfigFromFile(const std::string& filename, PktAnonConfig& config); private: ~ConfigSAXParser(); ConfigSAXParser(); static ConfigSAXParser* pInstance; }; } #endif // CONFIGPARSER_H pktanon-master/libpktanon/PktAnonC.cpp0000644000000000000000000000226712701423271017047 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "PktAnonC.h" #include #include #include static pktanon::Transformation* link_layer_trans; void PKTANON__initialize ( char* config_file, int verbose) { // if (verbose) // pktanon::log_initialization = true; pktanon::PktAnon::initialize(config_file); } int PKTANON__set_link_type ( uint32_t linktype ) { return pktanon::PktAnon::set_link_type(linktype) ? 1 : 0; } int PKTANON__transform_packet ( uint8_t* source_buffer, uint8_t* destination_buffer, unsigned int max_packet_length ) { return pktanon::PktAnon::transform_packet(source_buffer, destination_buffer, max_packet_length); } int PKTANON__get_erroneus_packet_length ( const int packet_length ) { return pktanon::GET_LENGTH_VALUE(packet_length); } const char* PKTANON__get_error_string ( const int packet_length ) { return pktanon::GET_ERROR_STRING(packet_length); } pktanon-master/src/0000755000000000000000000000000012701423271013305 5ustar rootrootpktanon-master/src/LibpcapOutputSource.h0000644000000000000000000000127512701423271017437 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_LIBPCAPOUTPUTSOURCE_H #define PKTANON_LIBPCAPOUTPUTSOURCE_H #include #include namespace pktanon { class LibpcapOutputSource { public: ~LibpcapOutputSource(); LibpcapOutputSource () {}; virtual int write_packet (const struct pcap_pkthdr* pkt_header, const u_char* packet) = 0; }; inline LibpcapOutputSource::~LibpcapOutputSource() = default; } #endif // PKTANON_LIBPCAPOUTPUTSOURCE_H pktanon-master/src/chronometer.h0000644000000000000000000000244412701423271016007 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ # ifndef PKTANON_CHRONOMETER # define PKTANON_CHRONOMETER # include namespace pktanon { class chronometer { public: chronometer() : start (std::chrono::system_clock::now()) { } void reset () { start = std::chrono::system_clock::now(); } void lap_ns () const { std::chrono::duration elapsed_time = std::chrono::system_clock::now() - start; _plg_info("elapsed time(ns): " << std::chrono::duration_cast(elapsed_time).count() ); } void lap_ms () const { std::chrono::duration elapsed_time = std::chrono::system_clock::now() - start; _plg_info("elapsed time(mus): " << std::chrono::duration_cast(elapsed_time).count() ); } size_t get_us() const { std::chrono::duration elapsed_time = std::chrono::system_clock::now() - start; return std::chrono::duration_cast(elapsed_time).count(); } private: std::chrono::time_point start; }; } # endif pktanon-master/src/SocketOutput.h0000644000000000000000000000131512701423271016127 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_SOCKETOUTPUT_H #define PKTANON_SOCKETOUTPUT_H #include "OutputSource.h" namespace pktanon { class SocketOutput : public OutputSource { public: virtual ~SocketOutput(); SocketOutput(); virtual void write_file_header ( PCAP_FILE_HEADER* file_header ); virtual int write_packet ( PCAP_REC_HEADER* record_header, uint8_t* transformed_packet_buffer ); private: int socket_descriptor; }; } #endif // PKTANON_SOCKETOUTPUT_H pktanon-master/src/OutputSource.h0000644000000000000000000000141412701423271016137 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_OUTPUTSOURCE_H #define PKTANON_OUTPUTSOURCE_H #include namespace pktanon { struct PCAP_FILE_HEADER; struct PCAP_REC_HEADER; class OutputSource { public: virtual ~OutputSource(); OutputSource () {}; virtual void write_file_header(PCAP_FILE_HEADER* file_header) noexcept(false) = 0; virtual int write_packet ( PCAP_REC_HEADER* record_header, uint8_t* transformed_packet_buffer) = 0; protected: }; inline OutputSource::~OutputSource() = default; } #endif // INPUTSOURCE_H pktanon-master/src/console_utils.cpp0000644000000000000000000002242612701423271016701 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "console_utils.h" #include #include "log.h" #include "debug.h" #include "globals.h" #include "RuntimeConfig.h" namespace pktanon { static const char* default_config_file = "profile.xml"; static struct ::option long_options[] = { // config {"config", required_argument, 0, 'c'}, // input {"snaplen", required_argument, 0, 's'}, {"interface", required_argument, 0, 'i'}, {"no-promiscuous-mode", no_argument, 0, 'p'}, {"pcap-filter", required_argument, 0, 'e'}, {"use-raw-sockets", no_argument, 0, 'r'}, // output {"packet-buffered", no_argument, 0, 'U'}, {"socket", required_argument, 0, 'o'}, {"use-ipv6", no_argument, 0, '6'}, {"use-udp", no_argument, 0, 'u'}, {"network", required_argument, 0, 'n'}, // threads {"threads", optional_argument, 0, 't'}, {"separate-input", no_argument, 0, 'x'}, {"separate-output", no_argument, 0, 'y'}, // miscellaneous {"use-libpcap", no_argument, 0, 'l'}, // help {"help", no_argument, 0, 'h'}, {"usage", no_argument, 0, 'h'}, {"quiet", no_argument, 0, 'q'}, {"verbose", no_argument, 0, 'v'} }; static const char* short_options = "c:s:i:pe:rUo:n:s:6ut::xylhqv"; int get_console_opts ( int argc, char* argv[] ) { // options values std::string config_file = default_config_file; // input int snaplen = -1; std::string interface; bool promiscuous = true; std::string pcap_filter; bool use_raw_sockets = false; // output bool packet_buffered = false; std::string sockaddr_port; bool use_ipv6 = false; bool use_udp = false; std::string output_interface; // threads int threads = -1; bool separate_input = false; bool separate_output = false; // misc bool use_libpcap_for_file_io = false; // getoptlong ------------------------------------------------------------------------------- int opt; while ( 1 ) { int option_index = 0; opt = ::getopt_long ( argc, argv, short_options, long_options, &option_index ); if ( opt==-1 ) break; switch ( opt ) { case 'c': config_file = optarg; break; // input case 's': snaplen = std::atoi ( optarg ); // if optarg is not a digit string or snaplen is negative if ( snaplen <= 0 ) { print_error_message ( "invalid value for \"snaplen\", must be a positive number" ); return -1; } break; case 'i': interface += optarg; break; case 'p': promiscuous = false; break; case 'e': pcap_filter += optarg; break; case 'r': use_raw_sockets = true; break; // output case 'U': packet_buffered = true; break; case 'o': sockaddr_port += optarg; break; case '6': use_ipv6 = true; break; case 'u': use_udp = true; break; case 'n': output_interface += optarg; // threads ------------------------------------------------ // case 't': // if ( optarg == 0 ) // { // threads = 0; // } // else // { // threads = std::atoi ( optarg ); // } // break; // case 'x': // separate_input = true; // break; // case 'y': // separate_output = true; // break; // misc ------------------------------------------------ case 'l': use_libpcap_for_file_io = true; break; // help ------------------------------------------------ case 'h': print_usage(); return 0; break; case 'q': _plg_quiet = true; break; case 'v': _plg_verbose = true; break; case '?': // TRACE("? is returned"); return -1; break; default: std::cerr << "option " << opt << " is not yet implemented" << std::endl; return -1; } } // last two parameters ------------------------------------------------------------------------------- std::string infile; std::string outfile; // TRACE ( optind ); // TRACE ( argc ); // for ( int j = optind; j < argc ; j++ ) // { // TRACE ( argv[j] ); // } if ( optind < argc ) { infile += argv[optind++]; if ( optind < argc ) { outfile += argv[optind++]; if ( optind < argc ) { print_error_message ( "too many file names at the end" ); return -1; } } } // create pktanon-runtime config ------------------------------------------------------------------------------- RuntimeConfig* cfg = const_cast ( &runtime_config ); cfg->setAnonProfile ( std::move ( config_file ) ); // input: if ( interface.length() == 0 ) // input is files { if ( infile.length() == 0 ) { print_error_message ( "no input file given" ); } cfg->addInputFileConfig ( infile, use_libpcap_for_file_io ); } else { cfg->addInputInterfaceConfig ( interface, promiscuous, !use_raw_sockets, pcap_filter ); outfile = infile; } // TODO check that not all three variants are given // output: // check if socket is given: if ( sockaddr_port.length() != 0 ) { // parse addr and port auto colon_pos = sockaddr_port.rfind ( ':' ); if ( colon_pos == std::string::npos ) { print_error_message ( "no addr/port given, please specify addr:port" ); return -1; } std::string sockaddr = sockaddr_port.substr ( 0,colon_pos ); std::string sockport_str = sockaddr_port.substr ( colon_pos+1 ); uint16_t sockport = std::stoul ( sockport_str ); // TRACEV ( sockaddr ); // TRACEV ( sockport_str ); // TRACEV ( sockport ); cfg->addOutputSockConfig ( sockaddr, sockport, use_ipv6, use_udp ); } else if ( output_interface.length() != 0 ) { cfg->addOutputNetworConfig ( output_interface ); } else if ( outfile.length() == 0 ) { print_error_message ( "no output file given" ); return -1; } else { // TRACEV ( use_libpcap_for_file_io ); cfg->addOutputFileConfig ( outfile, packet_buffered, use_libpcap_for_file_io ); } if ( threads >= 0 ) { cfg->addThreadConfig ( threads, separate_input, separate_output ); } return 1; } void print_error_message ( const char* error ) { std::cout << "Error: " << error << std::endl; std::cout << "Use pktanon -h for help" << std::endl; } void print_usage() { std::cout << "pktanon -- profile-based traffic anonymizer" << std::endl; std::cout << "Usage: " << std::endl; std::cout << "\t pktanon [-c profile] input_file output_file" << std::endl; std::cout << "\t pktanon [options] [input file] [output file]" << std::endl; std::cout << "" << std::endl; std::cout << "Configuration:" <\t pktanon anonymization file [default: profile.xml]" <\tread first snaplen bytes from each packet" << std::endl; std::cout << " \t\t\t\t(for both captures and files) [default:256]" << std::endl; std::cout << " -i, --interface \tcapture pakcets from interface" << std::endl; std::cout << "" << std::endl; std::cout << " if '--interface' option is set:" << std::endl; std::cout << " -p, --no-promiscuous-mode\tdon't set interface in promiscuous mode" << std::endl; std::cout << " -e, --pcap-filter \tfilter specification for libpcap" << std::endl; std::cout << " -r, --use-raw-sockets\tuse linux raw sockets instead of libpcap" << std::endl; std::cout << " \t(-e option is not available)" << std::endl; std::cout << "" << std::endl; std::cout << "Output Parameters:" <\tsend packets to using libpcap-inject" << std::endl; std::cout << " \t\t\t\t(EXPERIMENTAL)" << std::endl; std::cout << " -o, --socket \tsend packets to Addr:Port using sockets" << std::endl; std::cout << " " << std::endl; std::cout << " if '--socket' option is set:" << std::endl; std::cout << " -6, --use-ipv6 \t" << std::endl; std::cout << " -u, --use-udp \t" << std::endl; std::cout << " " << std::endl; // std::cout << "Multithreaded Version (EXPERIMENTAL):" << std::endl; // std::cout << " *-t, --threads []\tstart multithreaded version with threads." << std::endl; // std::cout << " \t\t\t\tif no argument is given, hardware concurrency is used" << std::endl; // std::cout << " *-x, --separate-input\t\tseparate input thread" << std::endl; // std::cout << " *-y, --separate-output\tseparate output thread" << std::endl; // std::cout << " " << std::endl; std::cout << "Miscellaneous:" << std::endl; std::cout << " -l, --use-libpcap\t use libpcap for file i/o" << std::endl; std::cout << " -h, --help, --usage\t print this message and exit" << std::endl; std::cout << " -q, --quiet" << std::endl; std::cout << " -v, --verbose" << std::endl; std::cout << "" << std::endl; } } pktanon-master/src/includes.h0000644000000000000000000000015212701423271015262 0ustar rootroot#include #include #include #include #include pktanon-master/src/RuntimeFactory.h0000644000000000000000000000110112701423271016422 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_IOFACTORY_H #define PKTANON_IOFACTORY_H namespace pktanon { class Stats; class RecordsHandler; class OutputSource; class RuntimeFactory { public: static RecordsHandler* createRecordsHandler(Stats& stats); static OutputSource* createOutputSource(); }; } #endif // PKTANON_IOFACTORY_H pktanon-master/src/IstreamRecordsHandler.cpp0000644000000000000000000000703712701423271020244 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "IstreamRecordsHandler.h" #include "RuntimeFactory.h" #include #include #include #include #include "PcapUtils.h" #include "OutputSource.h" #include "Utils.h" #include #include #include using namespace pktanon; IstreamInput::~IstreamInput() { delete output_source; if ( input_stream != &std::cin ) { delete input_stream; } } IstreamInput::IstreamInput ( Stats& stats ) : RecordsHandler ( stats ), input_stream ( 0 ), output_source ( 0 ) { // open input file/stdin const auto* cfg = runtime_config.getInputConfig(); const std::string& input_file = cfg->file_name; if ( input_file.compare ( "stdin" ) == 0 || input_file.compare ( "-" ) == 0 ) { if ( freopen ( nullptr, "rb", stdin ) == 0 ) { throw std::runtime_error ( "cannot reopen std::cin in binary mode" ); } input_stream = &std::cin; _plg_info ( "istream: reading packets from stdin" ); } else { input_stream = new std::ifstream ( input_file, std::fstream::in | std::fstream::binary ); if ( !input_stream->good() ) throw std::runtime_error ( "failed to open input file: " + input_file ); _plg_info ( "istream: opened file " << input_file ); } snaplen = cfg->snaplen; output_source = RuntimeFactory::createOutputSource(); } void IstreamInput::process_file_header() { PCAP_FILE_HEADER* file_header = new PCAP_FILE_HEADER(); input_stream->read ( ( char* ) file_header, sizeof ( PCAP_FILE_HEADER ) ); if ( input_stream->gcount() != sizeof ( PCAP_FILE_HEADER ) ) { throw std::runtime_error ( "istream: failed to read pcap file header, aborting" ); } auto link_type = parse_pcap_file_header ( ( char* ) file_header ); if ( !PktAnon::set_link_type ( link_type ) ) { throw std::runtime_error ( "unsupported link type: " + std::to_string ( file_header->network ) ); } output_source->write_file_header ( file_header ); } void IstreamInput::read_packets() { process_file_header(); //------------------------------------------------------------- PCAP_REC_HEADER record_header; uint8_t* original_packet = ( uint8_t* ) alloca ( snaplen ); uint8_t* transformed_packet = ( uint8_t* ) alloca ( snaplen ); while ( 1 ) { // read pcap_rec_header input_stream->read ( ( char* ) &record_header, sizeof ( PCAP_REC_HEADER ) ); if ( input_stream->gcount() != sizeof ( PCAP_REC_HEADER ) ) { break; } // TRACEV ( record_header.incl_len ); // read packet length from header and read packet body (parts) const std::streamsize pkt_snaplen = std::min ( record_header.incl_len, this->snaplen ); input_stream->read ( ( char* ) original_packet, pkt_snaplen ); if ( input_stream->gcount() != pkt_snaplen ) { _plg_error ( "istream*: reading pcap record body from file" ); break; } // ignore rest of the packet if ( pkt_snaplen < record_header.incl_len ) { input_stream->ignore ( record_header.incl_len - pkt_snaplen ); } // transform packet transform_packet ( record_header, original_packet, transformed_packet, pkt_snaplen, stats ); // write transformed packet if ( output_source->write_packet ( &record_header, transformed_packet ) < 0 ) { break; } } return; } pktanon-master/src/PktAnonRuntime.h0000644000000000000000000000106012701423271016371 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_PKTANONEXEC_H #define PKTANON_PKTANONEXEC_H namespace pktanon { class PktAnonRuntime { public: static void initialize(); static void run(); static void cleanup(); static void interrupt(); static void print_statistics(); }; } #endif // PKTANON_PKTANONEXEC_H pktanon-master/src/RuntimeConfig.h0000644000000000000000000000675212701423271016241 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_RUNTIMECONFIG_H #define PKTANON_RUNTIMECONFIG_H #include namespace pktanon { enum class InputType { UNDEFINED, ISTREAM, LIBPCAP_DUMP, LIBPCAP_LIVE, RAW_SOCKETS }; enum class OutputType { UNDEFINED, OSTREAM, LIBPCAP_DUMP, SOCKET, LIBPCAP_INJECT //, RAW_SOCKETS }; struct default_values { static const int default_pkt_snaplen = 256; }; struct InputFileConfig { InputFileConfig ( std::string file_name, int snaplen = default_values::default_pkt_snaplen ); const std::string file_name; int snaplen; }; struct InputNetworkConfig { InputNetworkConfig ( std::string interface, bool promisc, std::string pcap_filter, int snaplen = default_values::default_pkt_snaplen ); const std::string interface; const bool promisc; const std::string pcap_filter; int snaplen; }; struct OutputFileConfig { OutputFileConfig ( std::string file_name, bool flush_regulary ); const std::string file_name; bool packet_buffered; }; struct OutputSocketConfig { OutputSocketConfig ( const std::string address, const uint16_t port, bool use_ipv6, bool use_udp ) : address ( address ), port ( port ), use_ipv6 ( use_ipv6 ), use_udp ( use_udp ) {}; const std::string address; const uint16_t port; const bool use_ipv6; const bool use_udp; }; struct OutputNetworkConfig { OutputNetworkConfig ( std::string interface ) : interface ( interface ) {}; const std::string interface; }; struct ThreadConfig { ThreadConfig ( int threads_number, bool separate_input, bool separate_output ) : threads_number ( threads_number ), separate_input ( separate_input ), separate_output ( separate_output ) {} int threads_number; bool separate_input; bool separate_output; }; class RuntimeConfig { public: RuntimeConfig(); const std::string& getAnonProfile() const { return anon_profile; }; InputType getInputType() const { return input_type; } OutputType getOutputType() const { return output_type; } bool is_multithreaded() const { return thread_config != nullptr; } template const InputConfig* const getInputConfig() const { return static_cast ( input_config ); }; template const OutputConfig* const getOutputConfig() const { return static_cast ( output_config ); }; const ThreadConfig* const getThreadConfig() const { return thread_config; }; void setAnonProfile ( std::string&& _anon_profile ) { anon_profile = _anon_profile; }; void addInputFileConfig ( std::string file_name, bool use_libpcap ); void addInputInterfaceConfig ( std::string interface, bool promisc, bool use_libpcap, std::string pcap_filter = "" ); void addOutputFileConfig ( std::string file_name, bool packet_buffered, bool use_libpcap ); void addOutputSockConfig ( std::string sock_addr, uint16_t sock_port,bool use_ipv6, bool use_udp ); void addOutputNetworConfig ( std::string interface ); void addThreadConfig ( int threads_number, bool separate_input, bool separate_output ); private: std::string anon_profile; InputType input_type; void* input_config; OutputType output_type; void* output_config; ThreadConfig* thread_config; }; } #endif // PKTANON_RUNTIMECONFIG_H pktanon-master/src/RuntimeConfig.cpp0000644000000000000000000000477112701423271016573 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "RuntimeConfig.h" using namespace pktanon; InputFileConfig::InputFileConfig ( std::string file_name, int snaplen ) : file_name ( file_name ), snaplen ( snaplen ) {} InputNetworkConfig::InputNetworkConfig ( std::string interface, bool promisc, std::string pcap_filter, int snaplen ) : interface ( interface ), promisc ( promisc ), pcap_filter ( pcap_filter ), snaplen ( snaplen ) { } OutputFileConfig::OutputFileConfig ( std::string file_name, bool flush_regulary ) : file_name ( file_name ), packet_buffered ( flush_regulary ) { } //-------------------------------------------------------------------------------------------------------------------------------- RuntimeConfig::RuntimeConfig() : input_type ( InputType::UNDEFINED ), input_config ( nullptr ), output_type ( OutputType::UNDEFINED ), output_config ( nullptr ), thread_config(nullptr) { } void RuntimeConfig::addInputFileConfig ( std::string file_name, bool use_libpcap ) { input_type = use_libpcap ? InputType::LIBPCAP_DUMP : InputType::ISTREAM; input_config= new InputFileConfig ( file_name ); } void RuntimeConfig::addInputInterfaceConfig ( std::string interface, bool promisc, bool use_libpcap, std::string pcap_filter ) { input_type = use_libpcap ? InputType::LIBPCAP_LIVE : InputType::RAW_SOCKETS; input_config = new InputNetworkConfig ( interface, promisc, pcap_filter ); } void RuntimeConfig::addOutputFileConfig ( std::string file_name, bool packet_buffered, bool use_libpcap ) { output_type = use_libpcap ? OutputType::LIBPCAP_DUMP : OutputType::OSTREAM; // TODO do not ignore use_libpcap output_config = new OutputFileConfig ( file_name, packet_buffered ); } void RuntimeConfig::addOutputSockConfig ( std::string sock_addr, uint16_t sock_port, bool use_ipv6, bool use_udp ) { output_type = OutputType::SOCKET; output_config = new OutputSocketConfig ( sock_addr, sock_port, use_ipv6, use_udp ); } void RuntimeConfig::addOutputNetworConfig ( std::string interface ) { output_type = OutputType::LIBPCAP_INJECT; output_config = new OutputNetworkConfig ( interface ); } void RuntimeConfig::addThreadConfig ( int threads_number, bool separate_input, bool separate_output ) { thread_config = new ThreadConfig( threads_number, separate_input, separate_output); } pktanon-master/src/SocketOutput.cpp0000644000000000000000000001012212701423271016456 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "SocketOutput.h" #include "includes.h" #include # include # include # include # include # include namespace pktanon { union inet_addr_t { in_addr ip4_addr; in6_addr ip6_addr; }; SocketOutput::~SocketOutput() { if ( socket_descriptor > 0 ); close ( socket_descriptor ); } SocketOutput::SocketOutput() : socket_descriptor ( -1 ) { const OutputSocketConfig* cfg = runtime_config.getOutputConfig(); int sock_domain = cfg->use_ipv6 ? AF_INET6 : AF_INET; int sock_type = cfg->use_udp ? SOCK_DGRAM : SOCK_STREAM; inet_addr_t sock_addr; // parse text address if ( inet_pton ( sock_domain, cfg->address.c_str(), &sock_addr ) <= 0 ) { throw std::runtime_error ( "failed to parse destinaton address. Is it valid?" ); } // open socket socket_descriptor = ::socket ( sock_domain , sock_type, 0 ); if ( socket_descriptor < 0 ) { throw std::runtime_error ( "failed to open socket: " + std::to_string ( errno ) ); } _plg_info ( "socket-output: opened socket " << socket_descriptor ); // // connect to remote host // struct sockaddr_in6 s_addr; memset ( &s_addr, 0, sizeof ( s_addr ) ); socklen_t sockaddr_len; if ( sock_domain == AF_INET ) { struct sockaddr_in* addr = ( struct sockaddr_in* ) &s_addr; addr->sin_family = sock_domain; addr->sin_addr = sock_addr.ip4_addr; addr->sin_port = htons ( cfg->port ); sockaddr_len = sizeof ( struct sockaddr_in ); } else { s_addr.sin6_family = sock_domain; memcpy ( &s_addr.sin6_addr, &sock_addr.ip6_addr, sizeof ( struct in6_addr ) ); s_addr.sin6_port = htons ( cfg->port ); sockaddr_len = sizeof ( struct sockaddr_in6 ); } if ( connect ( socket_descriptor, ( struct sockaddr* ) &s_addr,sockaddr_len ) < 0 ) { throw std::runtime_error ( "socket-output: failed to connect: " + std::to_string ( errno ) + ": " + strerror ( errno ) ); } _plg_info ( "socket-output: connected" ); } void SocketOutput::write_file_header ( PCAP_FILE_HEADER* file_header ) { // on tcp socket - send file header // on udp socket - don't send header since it is impossible to know whether it was delivered or not const OutputSocketConfig* cfg = runtime_config.getOutputConfig(); if ( !cfg->use_udp ) { if ( ::send ( socket_descriptor, ( const void * ) file_header, sizeof ( PCAP_FILE_HEADER ), MSG_NOSIGNAL ) < 0 ) { switch ( errno ) { case ECONNRESET: case EPIPE: case ECONNREFUSED: throw std::runtime_error ( std::string ( "socket-output: unrecoverable error " ) + strerror ( errno ) + ", exiting..." ); case EMSGSIZE: _plg_error ( "socket-output:message too long" ); break; default: _plg_error ( "socket-output: " << errno << " " << strerror ( errno ) ); } } } } int SocketOutput::write_packet ( PCAP_REC_HEADER* record_header, uint8_t* transformed_packet_buffer ) { struct msghdr message; memset ( &message, 0, sizeof ( msghdr ) ); struct iovec iov[2]; iov[0].iov_len = sizeof ( PCAP_REC_HEADER ); message.msg_iov = ( iov ); message.msg_iovlen = 2; iov[0].iov_base = record_header; iov[1].iov_base = transformed_packet_buffer; iov[1].iov_len = record_header->incl_len; if ( sendmsg ( socket_descriptor, &message, MSG_NOSIGNAL ) < 0 ) { switch ( errno ) { case ECONNRESET: case EPIPE: case ECONNREFUSED: _plg_error ( "socket-output: unrecoverable error " << strerror ( errno ) << ", exiting..." ); return -1; case EMSGSIZE: _plg_error ( "socket-output:message too long" ); break; default: _plg_error ( "socket-output: " << errno << " " << strerror ( errno ) ); }; } return 1; } } pktanon-master/src/OstreamOutput.cpp0000644000000000000000000000343612701423271016652 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "OstreamOutput.h" #include "includes.h" #include "PcapHeaders.h" #include using namespace pktanon; OstreamOutput::~OstreamOutput() { output_stream->flush(); if ( output_stream != &std::cout ) { delete output_stream; } } OstreamOutput::OstreamOutput() : output_stream ( 0 ), packet_buffered ( false ) { const auto* cfg = runtime_config.getOutputConfig(); const std::string& output_file = cfg->file_name; if ( output_file.compare ( "stdout" ) == 0 || output_file.compare ( "-" ) == 0 ) { output_stream = &std::cout; _plg_info ( "ostream: writing to stdout" ); } else { output_stream = new std::ofstream ( output_file, std::fstream::out | std::fstream::trunc ); if ( !output_stream->good() ) { throw std::runtime_error ( "failed to open output file: " + output_file ); } _plg_info ( "ostream: opened output file " << output_file ); } } void OstreamOutput::write_file_header ( PCAP_FILE_HEADER* file_header ) { output_stream->write ( ( char* ) file_header, sizeof ( PCAP_FILE_HEADER ) ); output_stream->flush(); if ( !output_stream->good()) { throw std::runtime_error ( "failed to write file header" ); } } int OstreamOutput::write_packet ( PCAP_REC_HEADER* record_header, uint8_t* transformed_packet_buffer ) { output_stream->write ( ( char* ) record_header, sizeof ( PCAP_REC_HEADER ) ); output_stream->write ( ( char* ) transformed_packet_buffer, record_header->incl_len ); return output_stream->good() ? 1 : -1; } pktanon-master/src/Stats.h0000644000000000000000000000326612701423271014563 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_STATS_H #define PKTANON_STATS_H # include # include namespace pktanon { class Stats { public: void start_collecting() { processed_packets = 0; processed_bytes = 0; errors_in_packets = 0; start = std::chrono::system_clock::now(); } void stop_collecting() { end = std::chrono::system_clock::now(); } void add_processed_packet ( unsigned int _processed_bytes = 0 ) { processed_packets++; processed_bytes+=_processed_bytes; } void add_error_in_packet() { errors_in_packets++; } void print_statistics(); unsigned int getProcessedPackets() { return processed_packets; }; unsigned int getErrorsInPackets() { return errors_in_packets; }; int getElapsedTimeUS() { return std::chrono::duration_cast ( end - start ).count(); }; float getElapsedTimeS() { using FpSeconds = std::chrono::duration; auto elapsed_seconds = FpSeconds ( end -start ); return elapsed_seconds.count(); }; float getMPPS() { return ( float ) processed_packets / (float) getElapsedTimeUS(); }; private: unsigned int processed_packets; unsigned int processed_bytes; unsigned int errors_in_packets; std::chrono::time_point start; std::chrono::time_point end; }; } #endif // PKTANON_MEMPOOL_IMPL_H pktanon-master/src/OstreamOutput.h0000644000000000000000000000140212701423271016306 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_OSTREAMOUTPUT_H #define PKTANON_OSTREAMOUTPUT_H #include "OutputSource.h" #include namespace pktanon { class OstreamOutput : public OutputSource { public: virtual ~OstreamOutput(); OstreamOutput(); virtual void write_file_header ( PCAP_FILE_HEADER* file_header ); virtual int write_packet ( PCAP_REC_HEADER* metadata, uint8_t* transformed_packet_buffer ); private: std::ostream* output_stream; bool packet_buffered; }; } #endif // PKTANON_OSTREAMOUTPUT_H pktanon-master/src/globals.h0000644000000000000000000000163512701423271015106 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ # ifndef PKTANON_GLOBALS_H # define PKTANON_GLOBALS_H # include # include # include namespace pktanon { // class PktAnonConfig; // class ExecutionModel; class RuntimeConfig; // extern PktAnonConfig config; // extern ExecutionModel* exec; extern const RuntimeConfig runtime_config; // TODO // extern std::atomic io_metadata;// TODO delete io_metadata (after writing?) // extern std::atomic link_type; // extern bool _plg_quiet; // extern bool _plg_verbose; using std::size_t; using std::uint32_t; using std::uint8_t; } #endif // PKTANON_GLOBALS_H pktanon-master/src/Main.cpp0000644000000000000000000000363212701423271014701 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include #include // #include "PktAnon.h" #include "PktAnonRuntime.h" #include "log.h" #include "console_utils.h" #include // std::mutex dbg_mutex; using namespace pktanon; volatile sig_atomic_t termination_in_progress = 0; void termination_signal_handler ( int signal ) { _plg_warn ( "interrupted: signal = " << signal ); if ( termination_in_progress ) raise ( signal ); termination_in_progress = 1; PktAnonRuntime::interrupt(); PktAnonRuntime::print_statistics(); PktAnonRuntime::cleanup(); raise ( signal ); } int main ( int argc, char* argv[] ) { // speed up std::cout std::ios_base::sync_with_stdio ( false ); // parse options int res = get_console_opts ( argc, argv ); if ( res < 0 ) { return EXIT_FAILURE; } else if ( res == 0 ) { return EXIT_SUCCESS; } // set signal handlers // TODO what to do with multithreaded version signal ( SIGTERM, termination_signal_handler ); signal ( SIGINT, termination_signal_handler ); signal ( SIGQUIT, termination_signal_handler ); // start pktanon _plg_info ( "-----------------------------------------------" ); _plg_info ( "pktanon --- profile-based traffic anonymization" ); _plg_info ( "-----------------------------------------------" ); int exit_status = EXIT_SUCCESS; try { PktAnonRuntime::initialize (); _plg_info ( "initialized" ); PktAnonRuntime::run(); _plg_info ( "complete" ); PktAnonRuntime::print_statistics(); } catch ( std::exception& e ) { _plg_error ( e.what() ); exit_status = EXIT_FAILURE; } PktAnonRuntime::cleanup(); return exit_status; } pktanon-master/src/PcapUtils.h0000644000000000000000000000513112701423271015362 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ # ifndef PKTANON_PCAPUTILS_H # define PKTANON_PCAPUTILS_H # include # include # include # include "PcapHeaders.h" # include "log.h" # include "debug.h" namespace pktanon { /** * parse pcap header and return link type, or link_type_unknown on error */ inline uint32_t parse_pcap_file_header(char* buffer) { PCAP_FILE_HEADER* file_header = (PCAP_FILE_HEADER*) buffer; uint32_t version_major = 2; uint32_t version_minor = 4; uint32_t link_type = file_header->network; // check magic_number if (file_header->magic_number == pcap_magic_number) { // TRACE("magic number ok!"); } else if (file_header->magic_number == pcap_swapped_magic_number) { // TRACE("magic number is swapped"); bswap_32(version_major); bswap_32(version_minor); bswap_32(link_type); } else { _plg_error("wrong magic number: " << file_header->magic_number); return link_type_unknown; } if (file_header->version_major != version_major && file_header->version_minor != version_minor) { _plg_error("wrong pcap file version: should be 2.4"); return link_type_unknown; }; return link_type; }; const uint32_t LINKTYPE_ETHERNET = 1; const uint32_t LINKTYPE_LINUX_SLL = 113; inline PCAP_FILE_HEADER* create_pcap_file_header(uint32_t snaplen, uint32_t network) { PCAP_FILE_HEADER* header = (PCAP_FILE_HEADER*)std::calloc(1, sizeof(PCAP_FILE_HEADER)); header->magic_number = pcap_magic_number; header->version_major = 2; header->version_minor = 4; header->thiszone = 0; // TODO header->sigfigs = 0; // TODO header->snaplen = snaplen; header->network = network; return header; } inline void fill_pcap_record_header(PCAP_REC_HEADER* header, uint32_t orig_len, uint32_t incl_len) { auto duration = std::chrono::system_clock::now().time_since_epoch(); auto sec = std::chrono::duration_cast(duration).count(); auto usec = std::chrono::duration_cast(duration).count() % 1000000; // TRACEV(sec << "." << usec); // TRACEV((int)rcv_addr.sll_pkttype); // TRACEV((int)rcv_addr.sll_ifindex); header->tv_sec = sec; header->tv_usec = usec; header->incl_len = incl_len; header->orig_len = orig_len; } } #endif // ifndef PKTANON_PCAPUTILS_H pktanon-master/src/console_utils.h0000644000000000000000000000076012701423271016343 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__CONSOLE_UTILS_H #define PKTANON__CONSOLE_UTILS_H #include namespace pktanon { int get_console_opts(int argc, char* argv[]); void print_error_message(const char* error); void print_usage(); } #endif pktanon-master/src/LibpcapInjectOutput.cpp0000644000000000000000000000203512701423271017741 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "LibpcapInjectOutput.h" #include "includes.h" using namespace pktanon; LibpcapInjectOutput::~LibpcapInjectOutput() { pcap_close ( output_handle ); } LibpcapInjectOutput::LibpcapInjectOutput() { char errbuf[PCAP_ERRBUF_SIZE]; const std::string& ifname = runtime_config.getOutputConfig()->interface; if ( ( output_handle = pcap_open_live ( ifname.c_str(), 0, 0, 0, errbuf ) ) == nullptr ) { throw std::runtime_error ( std::string ( "error opening interface for sending: " ) + errbuf ); } _plg_info("libpcap: sending packets to interface " << ifname); } int LibpcapInjectOutput::write_packet (const struct pcap_pkthdr* pkt_header, const u_char* packet ) { return pcap_sendpacket (output_handle, packet, pkt_header->caplen ); } pktanon-master/src/IstreamRecordsHandler.h0000644000000000000000000000133312701423271017702 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_ISTREAMINPUT_H #define PKTANON_ISTREAMINPUT_H #include "RecordsHandler.h" #include namespace pktanon { class OutputSource; class IstreamInput : public RecordsHandler { public: ~IstreamInput(); IstreamInput ( Stats& stats ); virtual void read_packets(); private: void process_file_header(); std::istream* input_stream; OutputSource* output_source; std::uint32_t snaplen; }; } #endif // PKTANON_ISTREAMINPUT_H pktanon-master/src/Makefile.am0000644000000000000000000000155512701423271015347 0ustar rootrootinclude $(top_srcdir)/include.mk AM_CPPFLAGS += -I$(top_srcdir)/libpktanon lib_LIBRARIES = libpktanonruntime.a ############################################################################### ### pktanon-runtime library ############################################################################### pktanon_runtime_sources = $(runtime_sources) libpktanonruntime_a_SOURCES = $(pktanon_runtime_sources) # runtime: runtime_sources = runtime_sources += PktAnonRuntime.cpp runtime_sources += RuntimeConfig.cpp runtime_sources += RuntimeFactory.cpp runtime_sources += IstreamRecordsHandler.cpp runtime_sources += RawSocketRecordsHandler.cpp runtime_sources += OstreamOutput.cpp runtime_sources += SocketOutput.cpp if USE_LIBPCAP runtime_sources += LibpcapRecordsHandler.cpp runtime_sources += LibpcapDumpOutput.cpp runtime_sources += LibpcapInjectOutput.cpp endifpktanon-master/src/PktAnonRuntime.cpp0000644000000000000000000000450712701423271016735 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "PktAnonRuntime.h" #include #include "log.h" #include "globals.h" #include "PktAnon.h" #include "PktAnonConfig.h" #include "RuntimeConfig.h" #include "Stats.h" #include "RecordsHandler.h" #include "RuntimeFactory.h" namespace pktanon { const RuntimeConfig runtime_config; // from globals.h static Stats stats; static RecordsHandler* records_handler; void PktAnonRuntime::initialize () { PktAnon::initialize ( runtime_config.getAnonProfile() ); records_handler = RuntimeFactory::createRecordsHandler(stats); } void PktAnonRuntime::run() { records_handler->start_collecting_stats(); records_handler->read_packets(); records_handler->stop_collecting_stats(); } void PktAnonRuntime::cleanup() { delete records_handler; } void PktAnonRuntime::interrupt() { records_handler->stop_collecting_stats(); delete records_handler; } void PktAnonRuntime::print_statistics() { // HB(); // Statistics for network device: lo // Attempted packets: 100 // Successful packets: 100 // Failed packets: 0 // Truncated packets: 0 // Retried packets (ENOBUFS): 0 // Retried packets (EAGAIN): 0 _plg_info ( "" ); switch ( runtime_config.getInputType() ) { case InputType::ISTREAM: case InputType::LIBPCAP_DUMP: _plg_info ( "statistics for input file '" << runtime_config.getInputConfig()->file_name << "'" ); break; } _plg_info ( " processed packets: " << stats.getProcessedPackets() ); _plg_info ( " errors in packets: " << stats.getErrorsInPackets() ); switch ( runtime_config.getInputType() ) { case InputType::ISTREAM: case InputType::LIBPCAP_DUMP: _plg_info ( " elapsed time: " << stats.getElapsedTimeUS() << "us" ); break; case InputType::LIBPCAP_LIVE: case InputType::RAW_SOCKETS: _plg_info ( " elapsed time: " << std::setprecision ( 3 ) << stats.getElapsedTimeS() << "s" ); break; } _plg_info ( " Mpps: " << std::setprecision ( 3 ) << stats.getMPPS() ); } } pktanon-master/src/LibpcapDumpOutput.h0000644000000000000000000000131112701423271017073 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_LIBPCAPDUMP_H #define PKTANON_LIBPCAPDUMP_H #include "LibpcapOutputSource.h" namespace pktanon { class LibpcapDumpOutput : public LibpcapOutputSource { public: ~LibpcapDumpOutput(); LibpcapDumpOutput(pcap_t* input_handle); virtual int write_packet (const struct pcap_pkthdr* pkt_header, const u_char* packet); private: pcap_dumper_t* output_handle; bool packet_buffered; }; } #endif // PKTANON_LIBPCAPDUMP_H pktanon-master/src/RawSocketRecordsHandler.cpp0000644000000000000000000001066212701423271020540 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "RawSocketRecordsHandler.h" #include "includes.h" #include "RuntimeFactory.h" #include "OutputSource.h" #include "PcapUtils.h" #include "PcapHeaders.h" #include "Utils.h" # include # include # include # include # include # include # include # include #include namespace pktanon { RawSocketRecordsHandler::~RawSocketRecordsHandler() { if ( socket_descriptor >= 0 ) close ( socket_descriptor ); delete output_source; } RawSocketRecordsHandler::RawSocketRecordsHandler ( Stats& stats ) : RecordsHandler ( stats ) , socket_descriptor ( -1 ), output_source ( nullptr ) { const InputNetworkConfig* cfg = runtime_config.getInputConfig(); snaplen = cfg->snaplen; auto ifname = cfg->interface.c_str(); // TODO support 'any' interface if ( strcmp ( ifname, "any" ) == 0 ) { throw std::logic_error ( "raw sockets: capturing on 'any' interface is currently not supported" ); } // TODO if interface is 'any' start capture in linux_sll socket_descriptor = ::socket ( AF_PACKET , SOCK_RAW , htons ( ETH_P_ALL ) ); if ( socket_descriptor < 0 ) { throw std::runtime_error ( "failed to open socket: (" + std::to_string ( errno ) + ") " + strerror ( errno ) ); } _plg_verbose ( "socket-raw: opened socket " << socket_descriptor ); // if ( strcmp ( ifname, "any" ) != 0 ) // { auto interface_index = if_nametoindex ( ifname ); if ( interface_index == 0 ) { throw std::runtime_error ( "unknown interface " + ( cfg->interface ) + " " + strerror ( errno ) ); } struct ::sockaddr_ll bind_addr; memset ( &bind_addr, '\0', sizeof ( struct sockaddr_ll ) ); bind_addr.sll_family = AF_PACKET; bind_addr.sll_ifindex = interface_index; bind_addr.sll_protocol = htons ( ETH_P_ALL ); if ( bind ( socket_descriptor, ( struct ::sockaddr* ) &bind_addr, sizeof ( struct sockaddr_ll ) ) < 0 ) { throw std::runtime_error ( "cannot bind socket to interface " + ( cfg->interface ) + " " + strerror ( errno ) ); } _plg_verbose ( "socket-raw: (not) bound socket to interface " << ( cfg->interface ) ); //set device in the promiscuous mode // TODO if if_name == any set all ifs in promiscuous mode? if ( cfg->promisc ) { struct ifreq ethreq; strcpy ( ethreq.ifr_name, ifname ); ioctl ( socket_descriptor, SIOCGIFFLAGS, ðreq ); ethreq.ifr_flags |= IFF_PROMISC; ioctl ( socket_descriptor, SIOCSIFFLAGS, ðreq ); _plg_verbose ( "socket-raw: set interface into promiscuous mode" ); } _plg_info ( "socket-raw: listening on " << cfg->interface ); // TODO ethernet or linux_sll PktAnon::set_link_type ( LINKTYPE_ETHERNET ); output_source = RuntimeFactory::createOutputSource(); } void RawSocketRecordsHandler::read_packets() { // create and write pcap file header PCAP_FILE_HEADER* pcap_file_header = create_pcap_file_header ( snaplen, LINKTYPE_ETHERNET ); output_source->write_file_header ( pcap_file_header ); //--------------------------------------------------------------------------------------------- PCAP_REC_HEADER record_header; uint8_t* original_packet = ( uint8_t* ) alloca ( snaplen ); uint8_t* transformed_packet = ( uint8_t* ) alloca ( snaplen ); struct sockaddr_ll sa; socklen_t sa_len = sizeof ( struct sockaddr_ll ); while ( 1 ) { auto rcv_bytes_count = recvfrom ( socket_descriptor, original_packet, snaplen, MSG_TRUNC, ( struct ::sockaddr* ) &sa, &sa_len ); // TRACEV ( rcvfrom_exit++ ); // TRACEV ( sa.sll_ifindex ); // TRACEV ( sa.sll_pkttype ); if ( rcv_bytes_count < 0 ) { if ( errno == EAGAIN || errno == EWOULDBLOCK ) { continue; } _plg_error ( "socket-raw: failed to receive data from socket" ); return; } auto pkt_caplen = std::min ( ( std::size_t ) rcv_bytes_count, snaplen ); fill_pcap_record_header ( &record_header, rcv_bytes_count, pkt_caplen ); transform_packet ( record_header, original_packet, transformed_packet, pkt_caplen, stats ); if ( output_source->write_packet ( &record_header, transformed_packet ) < 0 ) { return; } } } } pktanon-master/src/LibpcapRecordsHandler.cpp0000644000000000000000000001445512701423271020214 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "LibpcapRecordsHandler.h" #include "includes.h" #include "LibpcapDumpOutput.h" #include "LibpcapInjectOutput.h" #include "RuntimeFactory.h" #include "OutputSource.h" #include "Utils.h" #include using namespace pktanon; pcap_pkt::pcap_pkt ( unsigned int snaplen ) : snaplen ( snaplen ) { packet = new u_char[snaplen]; memset ( packet, '\0', snaplen ); } //-------------------------------------------------------------------------- LibpcapRecordsHandler::~LibpcapRecordsHandler() { pcap_close ( input_handle ); } LibpcapRecordsHandler::LibpcapRecordsHandler ( Stats& stats ) : RecordsHandler ( stats ), input_handle ( nullptr ), libpcap_output_source ( nullptr ) { int timeout_ms = -1; char errbuf[PCAP_ERRBUF_SIZE]; unsigned snaplen; if ( runtime_config.getInputType() == InputType::LIBPCAP_LIVE ) { // // open interface for capturing // const InputNetworkConfig* cfg = runtime_config.getInputConfig(); snaplen = cfg->snaplen; int promisc = cfg->promisc ? 1 : 0; input_handle = pcap_open_live ( cfg->interface.c_str(), snaplen , promisc, timeout_ms, errbuf ); if ( input_handle == nullptr ) { throw std::runtime_error ( std::string ( "failed to open interface: " ) + errbuf ); } _plg_info ( "libpcap: capturing on interface " << cfg->interface ); // compile filter if ( cfg->pcap_filter.length() > 0 ) { const char* filter_string = cfg->pcap_filter.c_str(); struct bpf_program bpfilter; if ( pcap_compile ( input_handle, &bpfilter, filter_string, 1, PCAP_NETMASK_UNKNOWN ) < 0 ) { throw std::runtime_error ( "error compiling bpf filter" ); } if ( pcap_setfilter ( input_handle , &bpfilter ) < 0 ) { throw std::runtime_error ( "error setting bpf filter" ); } _plg_info ( "libpcap: set filter \"" << filter_string << "\" on interface" ); } } else if ( runtime_config.getInputType() == InputType::LIBPCAP_DUMP || runtime_config.getInputType() == InputType::ISTREAM ) { const InputFileConfig* cfg = runtime_config.getInputConfig(); snaplen = cfg->snaplen; if ( ( input_handle = pcap_open_offline ( cfg->file_name.c_str(), errbuf ) ) == nullptr ) { throw std::runtime_error ( std::string ( "failed to open input file: " ) + errbuf ); } _plg_info ( "libpcap: opened input file " << cfg->file_name ); } else { throw std::logic_error ( std::string ( "this should never happen (" ) + __FILE__ + "," + std::to_string ( __LINE__ ) + ")" ); } // // initialize this and pktanon // transformed_packet = new pcap_pkt ( snaplen ); auto link_type = pcap_datalink ( input_handle ); if ( !PktAnon::set_link_type ( link_type ) ) { throw std::runtime_error ( "unsupported link type: " + std::to_string ( link_type ) ); } // // create appropriate output source // switch ( runtime_config.getOutputType() ) { case OutputType::LIBPCAP_DUMP: case OutputType::OSTREAM: libpcap_output_source = new LibpcapDumpOutput ( input_handle ); break; case OutputType::LIBPCAP_INJECT: libpcap_output_source = new LibpcapInjectOutput (); break; default: output_source = RuntimeFactory::createOutputSource(); }; } void LibpcapRecordsHandler::read_packets() { // int captured_packets; // // do // { // captured_packets = pcap_loop ( input_handle, -1, LibpcapCapture::pcap_receive_packet, ( u_char* ) this ); // TRACEV ( captured_packets ); // } // while ( captured_packets > 0 ); int ret; struct pcap_pkthdr *pkt_header; const u_char *pkt_data; while ( ( ret = pcap_next_ex ( input_handle, &pkt_header, &pkt_data ) ) >= 0 ) { if ( ret == 0 ) continue; // timeout memcpy ( &transformed_packet->pkt_header, pkt_header, sizeof ( struct pcap_pkthdr ) ); transform_packet ( &transformed_packet->pkt_header, pkt_data, transformed_packet->packet, stats ); int pkt_sent; if ( libpcap_output_source ) { pkt_sent = libpcap_output_source->write_packet ( &transformed_packet->pkt_header, transformed_packet->packet ); } else { PCAP_REC_HEADER header; header.tv_sec = transformed_packet->pkt_header.ts.tv_sec; header.tv_usec = transformed_packet->pkt_header.ts.tv_usec; header.incl_len = transformed_packet->pkt_header.caplen; header.orig_len = transformed_packet->pkt_header.caplen; pkt_sent = output_source->write_packet ( &header, transformed_packet->packet ); } if ( pkt_sent < 0 ) { TRACEV ( pkt_sent ); break; } memset ( transformed_packet->packet, '\0', transformed_packet->snaplen ); } if ( ret == -1 ) { _plg_error ( "Error reading the packets: " << pcap_geterr ( input_handle ) ); } } // void LibpcapCapture::pcap_receive_packet ( u_char* args, const struct pcap_pkthdr* pkt_header, const u_char* packet ) // { // LibpcapCapture* _this = ( LibpcapCapture* ) args; // // memcpy ( &_this->transformed_packet->pkt_header, pkt_header, sizeof ( struct pcap_pkthdr ) ); // // transform_packet ( &_this->transformed_packet->pkt_header, packet, _this->transformed_packet->packet, _this->stats ); // // int pkt_sent; // // if ( _this->libpcap_output_source ) // { // pkt_sent = _this->libpcap_output_source->write_packet ( &_this->transformed_packet->pkt_header, _this->transformed_packet->packet ); // } // else // { // PCAP_REC_HEADER header; // header.tv_sec = _this->transformed_packet->pkt_header.ts.tv_sec; // header.tv_usec = _this->transformed_packet->pkt_header.ts.tv_usec; // header.incl_len = _this->transformed_packet->pkt_header.caplen; // header.orig_len = _this->transformed_packet->pkt_header.caplen; // pkt_sent = _this->output_source->write_packet ( &header, _this->transformed_packet->packet ); // } // // if ( pkt_sent < 0 ) // { // TRACEV ( pkt_sent ); // pcap_breakloop ( _this->input_handle ); // } // // memset ( _this->transformed_packet->packet, '\0', _this->transformed_packet->snaplen ); // } // pktanon-master/src/RuntimeFactory.cpp0000644000000000000000000000255412701423271016772 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "RuntimeFactory.h" #include #include #include #include "IstreamRecordsHandler.h" #include "LibpcapRecordsHandler.h" #include "RawSocketRecordsHandler.h" #include "OstreamOutput.h" #include "SocketOutput.h" using namespace pktanon; RecordsHandler* RuntimeFactory::createRecordsHandler( Stats& stats ) { switch (runtime_config.getInputType()) { case InputType::ISTREAM: return new IstreamInput(stats); case InputType::LIBPCAP_DUMP: case InputType::LIBPCAP_LIVE: return new LibpcapRecordsHandler(stats); case InputType::RAW_SOCKETS: return new RawSocketRecordsHandler(stats); default: throw std::runtime_error("internal error: input source not found"); } } OutputSource* RuntimeFactory::createOutputSource() { switch (runtime_config.getOutputType()) { case OutputType::OSTREAM: return new OstreamOutput(); case OutputType::SOCKET: return new SocketOutput(); default: throw std::runtime_error("internal error: output source not found"); } } pktanon-master/src/LibpcapRecordsHandler.h0000644000000000000000000000206612701423271017654 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_LIBPCAPCAPTURE_H #define PKTANON_LIBPCAPCAPTURE_H #include "RecordsHandler.h" #include "pcap.h" namespace pktanon { class OutputSource; class LibpcapOutputSource; struct pcap_pkt { ~pcap_pkt() { delete[] packet;}; pcap_pkt ( unsigned snaplen ); unsigned snaplen; struct pcap_pkthdr pkt_header; u_char* packet; }; class LibpcapRecordsHandler : public RecordsHandler { public: ~LibpcapRecordsHandler(); LibpcapRecordsHandler ( Stats& stats ); virtual void read_packets(); private: // static void pcap_receive_packet ( u_char* args, const struct pcap_pkthdr* pkt_header, const u_char* packet ); pcap_t* input_handle; pcap_pkt* transformed_packet; LibpcapOutputSource* libpcap_output_source; OutputSource* output_source; }; } #endif // PKTANON_LIBPCAPCAPTURE_H pktanon-master/src/LibpcapInjectOutput.h0000644000000000000000000000124412701423271017407 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_LIBPCAPINJECT_H #define PKTANON_LIBPCAPINJECT_H #include "LibpcapOutputSource.h" namespace pktanon { class LibpcapInjectOutput : public LibpcapOutputSource { public: ~LibpcapInjectOutput(); LibpcapInjectOutput(); virtual int write_packet (const struct pcap_pkthdr* pkt_header, const u_char* packet ); private: pcap_t* output_handle; }; } #endif // PKTANON_LIBPCAPINJECT_H pktanon-master/src/Utils.h0000644000000000000000000000341212701423271014556 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_IO_UTILS_H #define PKTANON_IO_UTILS_H #include #include #include // #include namespace pktanon { inline void transform_packet ( PCAP_REC_HEADER& record_header, const uint8_t* original_packet, uint8_t* transformed_packet, unsigned packet_len, Stats& stats ) { auto new_pkt_len = PktAnon::transform_packet ( original_packet, transformed_packet, record_header.incl_len ); if ( new_pkt_len >= 0 ) { record_header.incl_len = new_pkt_len; } else { record_header.incl_len = PktAnon::get_erroneus_packet_length ( new_pkt_len ); _plg_warn("error in packet " << stats.getProcessedPackets() + 1 << ": " << PktAnon::get_error_string ( new_pkt_len )); stats.add_error_in_packet(); } stats.add_processed_packet ( record_header.orig_len ); } # ifdef HAVE_LIBPCAP # include inline void transform_packet ( struct pcap_pkthdr* pkt_header, const uint8_t* original_packet, uint8_t* transformed_packet, Stats& stats ) { auto new_pkt_len = PktAnon::transform_packet ( original_packet, transformed_packet, pkt_header->caplen ); if ( new_pkt_len < 0 ) { stats.add_error_in_packet(); pkt_header->caplen = PktAnon::get_erroneus_packet_length ( new_pkt_len ); _plg_warn("error in packet " << stats.getProcessedPackets() + 1 << ": " << PktAnon::get_error_string ( new_pkt_len )); } else { pkt_header->caplen = new_pkt_len; } stats.add_processed_packet ( pkt_header->len ); } # endif } #endif pktanon-master/src/PcapHeaders.h0000644000000000000000000000225612701423271015642 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_PCAPHEADERS_H #define PKTANON_PCAPHEADERS_H # include using std::uint32_t; using std::uint16_t; namespace pktanon { static const uint32_t pcap_magic_number = 0xa1b2c3d4; static const uint32_t pcap_swapped_magic_number = 0xd4c3b2a1; static const uint32_t link_type_unknown = 0xffff; # pragma pack (1) struct PCAP_FILE_HEADER { uint32_t magic_number; // always 0xa1b2c3d4 uint16_t version_major;// 2 for now uint16_t version_minor;// 4 for now uint32_t thiszone;// GMT to local correction uint32_t sigfigs;// accuracy of timestamps, 0 is fine uint32_t snaplen;// max length of captured packets uint32_t network;// data link type }; # pragma pack () # pragma pack (1) struct PCAP_REC_HEADER { uint32_t tv_sec;// timestamp uint32_t tv_usec; uint32_t incl_len;// number of octets of packet saved in file uint32_t orig_len;// actual length of packet }; # pragma pack () } #endif pktanon-master/src/RecordsHandler.h0000644000000000000000000000136712701423271016364 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_INPUTSOURCE_H #define PKTANON_INPUTSOURCE_H #include "Stats.h" namespace pktanon { class RecordsHandler { public: virtual ~RecordsHandler(); RecordsHandler ( Stats& stats ) : stats ( stats ) {}; void start_collecting_stats() { stats.start_collecting(); }; void stop_collecting_stats() { stats.stop_collecting(); }; virtual void read_packets() = 0; protected: Stats& stats; }; inline RecordsHandler::~RecordsHandler() = default; } #endif // INPUTSOURCE_H pktanon-master/src/RawSocketRecordsHandler.h0000644000000000000000000000131112701423271020174 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON_RAWSOCKETCAPTURE_H #define PKTANON_RAWSOCKETCAPTURE_H #include "RecordsHandler.h" namespace pktanon { class OutputSource; class RawSocketRecordsHandler : public RecordsHandler { public: ~RawSocketRecordsHandler(); RawSocketRecordsHandler ( Stats& stats ); virtual void read_packets(); private: int socket_descriptor; std::size_t snaplen; OutputSource* output_source; }; } #endif // PKTANON_RAWSOCKETCAPTURE_H pktanon-master/src/LibpcapDumpOutput.cpp0000644000000000000000000000233012701423271017430 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #include "LibpcapDumpOutput.h" #include "includes.h" using namespace pktanon; LibpcapDumpOutput::~LibpcapDumpOutput() { pcap_dump_flush ( output_handle ); pcap_dump_close ( output_handle ); } LibpcapDumpOutput::LibpcapDumpOutput ( pcap_t* input_handle ) : output_handle ( nullptr ) { char errbuf[PCAP_ERRBUF_SIZE]; const OutputFileConfig* cfg = runtime_config.getOutputConfig(); if ( ( output_handle = pcap_dump_open ( input_handle, cfg->file_name.c_str() ) ) == nullptr ) { throw std::runtime_error ( std::string ( "Error opening savefile for writing: " ) + errbuf ); } _plg_info ( "libpcap: opened output file " << cfg->file_name ); packet_buffered = cfg->packet_buffered; } int LibpcapDumpOutput::write_packet (const struct pcap_pkthdr* pkt_header, const u_char* packet) { pcap_dump ( ( u_char* ) output_handle, pkt_header, packet ); if ( packet_buffered ) pcap_dump_flush ( output_handle ); return 1; } pktanon-master/LICENSE.txt0000644000000000000000000000574712701423271014356 0ustar rootrootCopyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology (KIT) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.pktanon-master/doc/0000755000000000000000000000000012701423271013263 5ustar rootrootpktanon-master/doc/manual.pdf0000644000000000000000000100543712701423271015245 0ustar rootroot%PDF-1.5 % 32 0 obj << /Length 151 /Filter /FlateDecode >> stream xڅ 1D-^vR* (B́o0qpyK2xjaArN7sƄulיW A u'V~ܚwܙ#Y$țfRvE>T.s endstream endobj 46 0 obj << /Length 363 /Filter /FlateDecode >> stream x}=o0!߱RLd PdU{I;}<'?Qk \S)bɠķ)0*PyX0OfɸHFa+*P| 0m(WXf9]S3hG8fNhAfvxDQTKP/j&,Zo5ke%74n?8qK_m#mU][[Uk*l9bT θڶ* `:Gґ4oOclK#ҖZyƋ|7D^uw7+ϨqhbN.FIdqH[< endstream endobj 58 0 obj << /Length 2566 /Filter /FlateDecode >> stream xrܸ}oxM[v)V*} K<$Dz6?>^CimNjC$F_l:w޸+:*D )d;n+wv~#aDNVm:9Kb0+T^:o77T!Lzm6=H@^t}_Հp[tLmt"cءM+Siy0cUcZY~t}nHo{{LOP fPE;BIF)[" ;ŕ~/I%oIRL]%fMΠn,z8V3h@6t49WmeѦq iEi9e1pJ3\v?tOs MrzVLG(Hգ9P ]s2^:>m4]$G 3>^4T*7ngьwщqk#Hk4ƚ!Us%:Ģ u{q./>Ha1E]lk;+HzҮnV ؖ( B)?:cڛ6;[ek^޴]UcGHa3h֗ j ܙhi"Wm_bbǴ:,vSY3"/60Chy;Q8VYK]Q߭߮$_o,FKIZE%y}3fm^Q=v͹ʹ%7/SYXFER>N8r7لI<Cѓd|NmXxQ2WIR >[]|#8S3v dE  FCبĈHa:50yȰi{OKT,h*tmRI8|jJ4/prΦ>{i;6N\ca8^WHg668OߠY?EEL$Rx纄oY|a#pV#'+Y-*4BC-*$syՎ`F*ZmH͌!Jц:+ϹixE, v}XQ>e=k5-S@]aե?έYgZfE,j>[|C9Ka^"`);`*]Cm֦ j$ДeC$$7{P(NMiw'$ mwdž)#ٹ3q~_TE&XwC* n&?QU&B0JCeE:p\Cbr6+dj"WFa`ɻ:ԮaSn:oנ؛ Z#|NFqɫ}Lҙ jX#IrN k +@ADtfǃ:?RҶlDiiDdRP9'^+pf(a?hƙka]tH(L듕~#+FF. /TcL``c-_;Sї|U3fIc3ϙ1rP'8z6c@%,be.&E\5s≔0TN댠%?qaRRʛ:t-% r"JE?zPИ"VOvY:Ӟ4=x -j.Ͳe>x zX; p Z\Х'ז/lyOj$a9,VeLڵCٹm !{2h96]S"~gR@e  rL;dM_'O( uΞQϨ|`Ն'<`6uB?('_N&ux6&ID:&1=:FѾY^^+7̰f#XĮIOnnqjkOIrF-Z܈p2v/' ouִ^7 }TGY2b)O_i ߕC ;0o6oԃm^@-k2px !HW?2j[kMXF=j$z86'h^6%֙-i0X|a'Nx>> stream xwt8-7(& E4cQ Nrg;8gNl<;qSm` ޥUY /%w;NJ+`$|tNjjjjjjjj GA!SqM?OL]Sj'OgVo$(1F3SAI"}V|6iH|B?/O'An*^Uh /" 2$+H$H${>JˋJG$fC___~ .*w|`_!LE$ihzLppߔ)f/X0t@xUTFD \ 9N˗Sl3…ֹs3fBmo!i_2tF~/Nȉ w rH$yyN$VXf8W2*j 2rp**#"+%T.^ i]`uɓ{!-A|Q7d% lhukR=f j0*jP1DDYp*m] .]:d j`Ѣ ZS9o^??$ 0H;I(tE49s6Xyr` $jP!Ww"#TW4'1WSh j`js2@Nc40X/tX~M7 kZcb7B WAH(hj Ld ZM~Ng%IQ0&O>}p ȕuٳ*شiOOXT Nw\^o6oX r5\ 05uüzM?Iroi@(i4V &L͝;vee\Ϝ7c$y.K&e$eY,oD"W+h+VP׷W#I:8 (OO`f4YM@~/, wڴN>eOzBuFF& {sqce}'xxd/J|Qx)#t~?PeEDOZ.P_mq4C tR\ٷe d̀ʸީS-+Nݫ)ShWUr3u*%0j|j2#n.nqe]-\h %F^<@ŸEzL®GVMgEp8i2A4t]mXWtڸglFXT=E 40;] {t:)}U/㊢5iROF$VC4OכɸT}11=aa]:r!%)ˮpqi /F;6&O xNB? nN%U\eLՅNа$PYEP+b MbҢՂɓiWդI]'v5k4)"\ &4z޲wUjVˢEԉDqQYW*yZwkU7rE 4/>B-_޳s'$UOT $ɢr{r)AZ Մ U!ZU%q^"V؞](T͛NmGGur\I+|TJ `Kbǡ>WĉUrߢ՚՞(y$P}; F,h4* U@݈{^|ޯ~u>22F{~/[~{[^H_&-E:4Ġ6b!*-j&SgBb]u_ߢ7#T4Y300 QtHp ^`]ܪӫEiqZIg zc{YWM:]3H4/tb<phK[+X?SCѢ]5ׄN=ce3:lP!WĎ5k&[TUߦMwG'Y`ߏWaKтu˫NA=8 M]:׭kDp\q`OPA'Q H(҅Kkppҥ0#Z7hL5BLTT[Wڿu#:]#BUڵUUw;i2a,9Uv .P1:7onT\i4/87*!%=ܹOp53n_0iZ25`} "#-U7||.{D0mZׁMA%;w??00BTNϿ:r̪Uyj/^CBkpК;c2WZ- u^c#{10P)r5gEw$FXm^iks`RD >g*e`5UT\~Ph١ *ڶm:),wn@Lqbg*:×Hrdџqwfe ==G\yy A~|@7ߎ<"":`]ס'PڸEX@L?zIZ%ZK+rEIqĕTbu BC;uW*r՝=+bt6*J+,Ji,+rE4 V{~>uwӳGYWu!VΕXTTgByO Kֈ+=o\5h\WuCDb 0:YWM3g"T:7l׭cMC+rdٷB:W qe4yxޟɓ;d]][ c⾦&+XC<suArYZb%KK;&È+F{r5CC wڳhw6kV_UU?&_ n|c+cVE{{תU5ZO2޵%WL%%K:jG fά![W>>}(p\9_:7 ] Ӫ2^?`j#vlD~%6PaC.*Õ+bEqsVh v\i0]Q``]xxTrl! ׮m?thՁ5~~:~枠+b矷sl\m;E9WQ@ j3)şDb\5EDT%K] 7tH—K,hvẂ`]U-۫hN7_]MM6><*1dwOnnveW j>1W%ァԕc#ֈ+ɓ1]j46ڬqx 20'hW *U5Jl׹Fuhs#'1WUgU_ev^Ϻ+xbqzU[bbNyɒ.b9lv-[:fh45WM8~nm6Ÿ_p5 5WaE$:W]z [ ,W: ~ѥ&=ݫ>2 P!W{VtKKlĂEFW_>RT_ ?W'UF:.o۱z} _uS]=\~a[\Ξ-*Vt騹Rd}c;\}9rJpĢhiEEθrz~ɂ||WviÕ,͍2Mm d]-4ɌVhkVb]5Ǘ6_oAu5;NJ]jTpe>,]ź*mWvD㣏Υ]ɶwu+PV[~[YT^TdhQAt^(=ApUNh -][^^-jAUwoFcwC+W]/YvJ'|W59\^ߎ0mL]$U8؏ӯBΛZa PJD 1b)ĝK/]uR]A+|E&XAozb\]sw+ U射+V~lww7+WBLZpߗ=\A*ʒ(V *HqEf+pt@3|D1W6˫M:;1Q]شI ok f̰9'(TѳhWU[`?\*Uå+n++Y۶kڹR ,1ZunwCpW8'W^<-k/zk 7g!!r%]Z~[@ɒue~W9f'ϓQ>\kU*+&AԬ]Kj~ɚ;Wb{{ZZ7Z3%  Ɯ]>bX(-)Vhʐ[$4b}û~UY%BhUwuvڹR Gy}o9%e4'VĚ UWWWݼyŪ 8uUBnruh>I69º* ]I.ZV<㪿ZYĚ nzJfBŸ*p'mh,#GJZp%[~+[!Δ%uA!xZpGmXA+VF 83~t4, \5!W w#Te1AWɒOJ Ȗ)YWm/+ƕ[N@=|Um\,U+;Zm]LHBJ3`}}?7!Jvbk8 *VU1ru#UkְWf]IF;W,c^t1!AU̫pJ Btvv+U uwJ!xdEȕtɪ0ؔ,!}fQK8^bźpʕbF㪘qu۽uU>s&ו+]Nc@ ,vUZ*AW[: XA%_`KdŸ*BսUAϺ* -B$KN&[!α,i*9vLCߗ*ʅ ߖ/VU1؟}>{nLOnٹU{+,Zx6'ꋝ;1_,jDѣR:ʶʯOnj~qU]\irh=lGG% R]4| mE* W^]X*DGիPs-Yu>N.kG_JZXGd\,zci4EMF|//U#Ǖ,kKRZ}aӧ8I)YT.,V?Qڡ\"WWF+{7j@ %V_KƆ.:<(z93S1CeJXq\\sW \WhdKji)%,Q{[[%8I)P) dWo *:=\5 Whdiθr-A]\c#\L%2%bEz]H*D莓{Xtd]A{XZE"ݰ{c]9_R)AW_='25`%7&(@:>W@:ˋ@)YV;QjW^$-IT85_w@zcAM@yNq<[<<襒Uki.1]lt<[%SDrXAԾbl/1wen,LJJ[΄c%ZĀ ZgW滪~f%T5;`1X(*4D#v%K6&$Jôdu3k=[yiHrIdQq]2.^%6vYc9+AtɒV/\Ju{Ss^"N|QeJXEGR: Re]堗O"ۛuUwHN#^XZZmKUw%-L]q]vtXQq]45A\"W9ww{î|2?((U`r -]]}  xHkQm|5s\]W=O"!uU~\(עj/1P_r+@A~y{jx"[ǤU_ i9KXSnn3٘(Qq]/XWU6re{?UXQoE \9R\*YJ)-tIƆ? 1QUz{61Tv zU,Lw/YI0b__ø*BZjqC- .|`pLIj*-->Ne ** Jr۹#%XٳU rUk Vsϵp -]c&@B jxC*N7v:nWБ3 @E#@+ VѤI-MMvdg-;ZLXD_c JWDEX@L -}^;wҮ .̴u%Aqv(%]uʘ7/ iD)BՐޫ% 6 4A.C=u%AXm2ѣqhZ AZ.A]dLqQQ=lP BWr[9@h=D7>O?uúsؒ@`6hUJˢ]CF Ge]/]\$fQ(.i`1/ ۇrEщUrѤڝЁVX|9'*@ǤU-00'K$YQ5o⠢6^Ÿ-yo8{/ 2/44]CV||3J{3w8 e>njx+h4YL̻7j *ݰv%MUNJh9K]_xgFGoޜ7c~(!))_)aT 24 \2s&튢uPVM\KX[CCs |v͚SdyQbJtΚ],L>2q3Y=!UؿvUupqDݾ*A+m .i`veWeҊ$8E)Tտ]@5 LUNjkz|:nK2A._κ

;TP(Tz=2k4%E% ZsXy!I]11P%h/_b*_|xl\" 0z_Q`\^~GqҜիաCg h],1,>uRK|N|Qe?3KL̀3q\Odo1T*+W,-]-A]Čw˘%!++OLQ pWfp5/H.Ykְ`0d WiMo7cq7웼l^R8EE]]QxtC*`]!/|JUq kѕh]% ͒!a`*8I(zz2Ţ6ȕJ];>$dDD +Y!!bzbO,-PL2+YHz]R2EgP\Ib]JHx[H2GKpdߌdё*] G5> (;TfteJg^2w] ~7q`Az lѥWQ3^ExIhlFT׾]E9aWs}|3F J|̟ ZZI$Y*߻-FKpe/6cGHb 3ҨmTWP H(j0==/0PQE*ь\I~\G QQy@ILL*\]f•XwFh y| c*SS0ܼ0EhRe P!Wt!?z4套r{:;[7q".] % v2# 37$kAA* 3kXV[;.Q W-a͉&bQ>ID+3*$n/NnY\݆z+f] tF~?7FiͣoE]}TJ*UNgwG}V-I8.vu{>;o^ "]Eqq%%h4UU'6GI֞'wӮM4s/'kw|Pk̉V-Y]١=x`7o޼L%z*+hL&IsI@pp޽eA̾v a`Zlz[G=qL޴ZJOw:rќ } Z`u͞M`Pn%$Th9+?2.)~ՐU/IB4JiJdFăGy5,]%&Blڔy.V[3Mg竻@pT 8A? S5'hfkKWe=V KKLI2U}T'4ەJ*??p\u1ꗈ-]SzC-ܰqYaMI]yUǏQ5pA\7xׂxÈ{cw 0&h"W{Ĥxxд$t]رXN.dIF_:rj2=x7$p&pU\>hq\%3˙7ⷿ/sW]]䅇;ixmh'@Rn, 9^WhW}dXE@ S40ꜾTw/L2*_5.[Җ9ڮKLTUw2Gxc9T 6nŸ{'Ҵ˜4sQWs54Akʎ͞5 ߒtJaVB*0u* Za(}Cׯqsgܹ,-ER6MΏ+{-b))_p̀1H8DALAA*U ]I*xq^ՂuVIݻ!o"#u:.de=H3ϔ5+j,.two=<@͜81]%E@P1RLA~scUi֢E]JvZE7liȘHOϬ)Sr.߸`vȢC vXF],$QW8ßi*0pwH_>kd&a GKRzlD*Uĉ!!^^xG]T7 >>kׂBͅ6eΟ1g) ӥҢL]AB4*SB#)t*?RoWIiSr]Ae-^}V19b?^dؿ3O I2%-44 2u`bSN^,)mt|wȘ,ƍ3aJsI?p 8͜ XWiӦAsE]G}AUpaB'~}+*7l{䑜s]J^vOY3gHTaaijɓ(`tbH[;4'g.]CƍT2֯r:ة̎ -mʔ7.ILݠg̙cTU:b\d~^B0m?qwWgPWtN ]A>T3*+*\&wU2,M2q͠d?^^; >$J!dB&)B&Qm3,2o$|C3!S`,JĻ x o3\`Cʔ)K[c*':5krQQ(s"#$4*Wr Ȭ˳-rRHx1Qp! 9s=;},ZeEG/)<#lk͍Y2W8 222P1V PX|yeT**cb*-rB?qRM%0gŗ$k]GĠ%A-"zb5h y-X2P1"#VPa2/ZD%r6{6T t sOK.qcZݖ/BEhg> stream xkpTfs' gsِ!d/H1HPvZնSL/#~h;bVjk@@gp2 :C@2PKJys`9{o=gmg   ,}N˳j-5yE…#|h>q5w>}v.|n?1tq{mI>ui̦8q, .ah/u.0&Ӂ+M]_:tMy;LG^n әM[0t3y}}ńs_4RzŌ¸bȥuhd1 c1ds5q'w" P#kƮiTci4~ ݪhg3V3s.5?f&oE)7pXƙis?tH|vΥle&.1A{\nZ# QngBl_e:*EPL4ZԬQpQ$1gcBIFۤX6o0حc5~-Gدc_V1!Pj,/_c*WEcqH9&0*.lcΈqTG/'~*wz-ǰB5؄_܃PMt+n5<"ID+V0Vq {#{  nMxs556R= !yTU/1̓AX<*qlI[GZ<*k~d_{^s4W4vvx@ԭʁŪi'[+a%ܣGFh\\XP`" ~dhn̯#%}1ݺ7C7@Lo)p- ӡ H fUɝ%@4HpHDѫ޻/H*_¿_<߭S4| _kN4#E|YPLYӷx8F<O3Eu]g@ V*q ,U@CU~"HnoA8MX0SRl`5n /Igwa&C|2-RpJ SEKa?"E&CDyru< d4I .T88."Ž#HCx&Ю[yKs(o1oda(pFF\xv L M0ʚZ~vȓ_$`Q܂W 'C F8~D#u7vZ<PYAU?r/P+U\ /TQhna$Ҕ 29ihBLw`)Olp:x{x\ΟT)p9(8\儉P@SHܺ~ko2.NPs:$0 ` >Q\x39qTTP$f LWO]xб˕ѿw x thuAPN*T9EaEt^n킉/P* >.6 \3vʥ@7l? ƺdC~ "_j¦ׯLҕ#իaT\PFiC4 ?񠀻W s@^'.Q*E[uj, WT`V1z6j!- A|%tpL`\B.Tf:1t"&^ŧ+)C̅sA`F~\B '!KHQ$FHĆp(JAb@F! :|I C>I͈q>Levvv.`.(6\ B<@0 |Y< -v~ ,SvqAqQ8 \mJ$\"`R,c\OQ1SB. T&h@a*v%u&>`Ja %`\qIHE h$#$/l=~K 0/#۠NF&R8)n~F:oC $Nr ZNKG0DP> `ĥB3#TB26z7                                        B)a3{ endstream endobj 53 0 obj << /Type /XObject /Subtype /Image /Width 200 /Height 200 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 64 0 R /Length 13847 /Filter /FlateDecode >> stream xwt(pg]{lz2#Z! Q r@9'PB Z9, , aq~TZ{`uwOޯD )B )B )B )B )B74vR,v)9e M  :9ɯ!M;N  dB{9X6c>3M/g筊ꐶ $~HԩPS:|y!QLɉ3:|P`%W)`Fi͜0e-(Y]Ưr}`E`UwV*[~dk֏" wBSFDEɜaR-R=qO\mr^3nGx:'{w|+N}zm;vǵnl Y]2ER)ۼE">wŶئv9gy ;V0S8SNdD8dd1UGAC=҈߅a {0sf u̹P1~?\#^CƷB9:5qXXl$quZE( 'q]B2,o,,w2 ˣܠsF]Z^5˗)\Q*ﲭJ(Y '!cq ]/ከ]8] Ety'{SzmR;TO<ʗDNY~T)D{fEI%kl+gX _8h%ENycX ]A 4pqz.L} mV!K܄&EҾ6bYTmvN~!K T)t4>9Gh4s^uQ!3WtXv2ʌ+aV09ٽ# V+vRg0sȓ.6j4;8՟j z)ߜ!~X٩͉I8){e$XDqEu5. BftBjLkT v)uR/ AuJH)l)'oΰ(=v4prL罹`(O \A|$uZԘ9sK  7R-Ci]T""4])w@WxޘAS=![PbFE>|JT.cWcg ]':`ZS~N89o=D*-=uCr)&'.?N3ƥ = \ha Bgt3P99)Jܑۤҩ)1ɝA(,zO|iWaGr;z&m+K'I=Q>XmPd$Od+3xhfBTBŊب8-ғK-VCd,D%W0-pUtY,tb^*shQt^xsa\f\)($u^P`.8q^u=;aL;Z|iޫvKzS:m aQ:,.c Pm"HT)ؤ:@&]Q;`Bpi$Ӣ[#}<ӗ7/긕Vq5(SnH-x\d*Y ֑.[^Z$ѳ֪SS:ƓB':HWZ+TwM=|'/^j;%-ޒh%wFW!R\!Zvi]{Vz-7ֻ kOu'*&J'IW`9nTŊ d+FtH*j)!揬Ш /7܃sɤ\%K튠dl.X$8U*)s*V~7IE\ʖc]NNC $ͣSg-EB•v->kլUFT(ߦzd婲l*"waTbY@*~y~K?_sd7,Y! +(YN"Zv- Ec\D{O) 4*+;.VCwzn$"tOnoΰ`ծ:v8=t% JDq޼Ёq-RHTW>`v5S8]ыO`/^~>TqLij_{ Ü CV]vNvQ] V7ih2'ư:}X#oo|+&sTag_~7ڢ3gVԥQٮģШK$-iKxEFU\:Am^cģt ʕWVn@+ޮ_a@T2 K et9O[W-+@bΘ&[eőAuxc@]ײ@s 4tޓա*N6eF]8g--\~ryX숥v : r\&$ml0c\_Qø^ݖe!QIWwǶhdcXQ,V"EUDԥCɂWeںDhߊ.,}3VYV IUT(]ZRtxIb%_u "_/__zg\AojxIi't} xWy_CXlG0hK2{oNlC !J#]^xabŇDxZ,T^蜘;-$|te[usxnuUי]LU? J+zDOb()!^FZKީ{GҺŻ n-k+^ 4+sHH23YCɝ*bEXBGKji,Yt=ypjxW{Z'dYG7.u+_J6ϔvfp{Tq`A*d^hWPVMPb% j}u;ªW9pt'ҹdqh}P+ت,ۼ)z-ͫrv-js}!tuLxWן?NEѕŊՃ'%6⾽򰜞i,aW$-m -|t7jZ7x)(*L='ӕ&WQo뜸oP~{j<_ ٮzLxWWёRg?g16UH]d0*I1ZhW#_}J[W|MPXQQ}zنrcZ/p ]aZO}u,wHݱ-+TbЯv̎Wz:uTy4gGth.W-.>+Õ&(O+RZ߱TȒ[+uKS6|W*W*\n|ua8o90rL_8F:.Q"].Vz Nʜ5>[OiZ=1?S]Θ&ZǬ7.wMJUZGiڱ=Qܦk_apW&Y vG 8>팒% tqHWW0E{O,Y̜[# Ec+ÀJ O`Pmh9\1\aTw4-ծ\6b6'Osh']6Cq%k||+wwKQbu0{tߟvCj2+`]-> Ͽ#ή nVucP㻂P /w :\mj\P9Mؗ*t_ju$MK1^~g9s_L=y>Ȯ>{jpe= 嘒{$-ߴ_E`N?xcJ~W4ZCW+Ǖ:?W z'jTE*w:R/8˷OVL\Nu!W4bsq{+=KB9?8lЋv{ W) ӻyKBJpT' J*v w)TWsx\.QZ,jND"WOph ݜ=WwlcK\>yWIhvWv|nhlؕjGௌӅs=HWc[#WJ r:}\6υکիYYԷc\lduil̬ Mr+.YWƴbS/]E*ZB r2{( un6 bWj_ `8Sz . |VvOǜةMqٌNn&( W UnT,WuvoҶjUf+MUbTcRS\3XF D+K}zL-pύ1fJVߧH&H\C"\_ÞDZvdnF,pAG2Ǯ\Nvl ,YzG_&=vScœMa*Wiyo,Wwxev|zx]K{Tp+4TbӚ%W|* Bܸs_LRMU-vE{S.w_}T+X nUӪ+Y-_a-p5Cm?G+T6-|@+@'9F;E{'oEtvyD놐%L',,NΞ+fX-ޡX]թ]լa7&Y*Al6h,Y8]d Ӛ%W?RgT*b/;X>RA_ *| &WHZEC_CтmW38*M"Q>;\ժ]6dC[n AJW%\Eh%caU9peUWЭ!2 Bv廛V8EiDebQ>B퀜 #1W^.K.#]TR,ڱ,5=-Ώ{P3{ήʔaQ)VsjuҕՌ+k5͙wPb's*DoD-èJ,YYj*Y lWFEr:X}dBPAݷ~džw\ 9R&Ģ1 +d̔,Va#"hiJ+ZbtAIPJ@xTZDQ8 EqEWfT`a\i,Yau-\pbrIibʔaQ 7M t@5Z6`WA}0]՞U1r[0HZdq҂HsES ])UNw@ 7dZ`/7q^)UR1W#*ZnJ놹2hq?-r% JOTb\6q\&HvEF֑*Wc IWbە%K+Zu1j{"EGs]=v,4z ݘtuh 'gbJ/~Hl.\pXfDb}}d]VZfwq/\$#s*^GB%+]hkÄ~DZ"iԥ+aQbPĸ:Tk@ZbWjbhW塜}WkԨFM!5P!dhNCuhUtZbtQj`jZ(*V "|7XUQ;)о_F8޾% uDE )\Z9V]YbWhU\mΌV]n0'\ ZQJaWzҪpqǕ(1OTXw UWz. (2;<߄\hqvCՠuȒeZl]|1b?"_ bu]6*+:*Kv\Tk i :ߋQA:$³C}Dh .f}\i%J+T; DsfujYvF^t<όNH:iPAxbW3\@o.\ttu(2qPtunl{"Qz%{( ԭ! ?׻οNsO,+hiUtAĕ(1eJ<*vEEU#bWw2&(-v)DY A YSZXOKxIK.Nc:꛸DJBq׊V'Q>‡p'^lz%so ITJ,gM>h+~,ҨK6f(WeXTcWo"; ch4I<#+|[rHveT@ՕxZ.N]|t@磕(񨰫 1TT r 1?k.KÉk*fD]x] `:(b+K=,)W{ W2ƄޙX8hzZW] :t_%F07ݮ룻|PQDu=1 bPAguU`Q?ȷnŮEM rp5 Fwxn"iL0 -tqߣ'*"Q!8{HUXBjԮF#V"i)O aZ..`1]m҈ d ?**H]lzޥveۈ],W7>bdvcT靰H䣵Nu{ CKg]lc_>`c6]@ kkK#ցF\4b ТV{DLK] ŋD1 'fn?zIFdNAUjW"j)hm}gZA .>`z{,ҧLq~ֈ#q**"|V;.3E ( /ƮSZU8~OKήGoD\ QTj_29e+jxZ| [0 ּH\='(2ʼn "lHL¨ Ujop6A( Ո)ziJVDjl\' Jc12ucShxTɪ(V0S?`bΛ 4Ұ }*HNxifwNCd Ȯ% " "J*9q ܃6LBuS0/k:v;!cݟ A9qċb,SԁJJU V;ʨZ" \СT.<Ct9َ\bq oޙf-tٕ7mY E$u2AF41aG?^d;D` U*Z3zbi.\l]|1cQ'w}ϴs)T ^A.BW.:LShk|K]W*wdJ2ƞ[&nt't ]q>1DP="PĮBƧFr̳XhOޗNl¨^E]R V Ң 4見 ̸ ( ueP2EEڕ/RdeyJԏ]Adv*m7nOS>eFHD'(aT0U (e*TZde(7aǼ/\E]@%h%Atv[}^O]4cbB{VZuaQTTa;CZbE{<-V\%w^p`hNh.\aٝw5֥0xNEݐsF_C)ceJ=eJmQ@$s%mg 6-•"Z f'2bhUp̪K̙6`g2z:c^~7nޙ6\+HGO0QY]5YʖUhk_-5y cm=\_ZU t4~,p7O4 */ րPVx*kmN6I- h_/RZ?PE׵!eb5 EUPgcb?FYVȕ_9|s+m %W6޳,X]]um|#.=SˉY4›׆aYF? _.7ycA˫,\dNߞhrLK']niF }NɍlN"EA(,W_a̽XF?-Q&z|1U >4wKh5. 5Tqw^]QoށڻN8'#  _} fuXu䵈fgi]R\\x CWUW>**lQ+@q"QYTX0_%޶ \ϸalCXN/vx*4U.]tɫ"jxf'FPyYVBFЏ=\kM^xW2j%EGBސ^"lUAK3 /;m}cgQCyŠx JPŊ@4u*G1>wqR"*78F!0-J.-曨A,E!TDP+_B-s_7>:xqa[Sr!nZܺx#f?Z>9# Ni]M\T \jFz;6Y *, T*Rh2;L^9 ZKJP:\!Z=Az(1-a]"$Ə0<<(eb&P6+NTK܊Gߛf?Ho%n(% 6ԥ|q1+Vߜ߸ E2Jc5LH,K8 NX(]XL ]ﴉwMތ`Ӓb,*Ȁs.HZ3D/^crƓE}}W߼mcP]CQ_BI ഁHKbV/o.f}o*?|{vaKᎈ@nHNe%(ytq0fzw;|Pi`ޮ}~ SSUE8nߌV(0p($l@wO,!7D3U'%Y767/~K/0"A]*WY(D/?Z̽K0(̸ B%NNzf\eThqP]DMƛJu{l61s.\Sq\gͤsjҥ8imǛv'kDBRA"T|˖ѯ~|oOwcf?}t,IhCѹ' WZ 0qƴer>FK/bD\)D/!q&Rwߓ|﹓|KNy2p1eO©-01Alz%ף, { ,`*Q T"ѰkI#~%q ,bG 4!U6 e>S+?[zHR?_ȸ`\(ЛHSj ]q0ǦWr=2^K%iÿ1+'DUo$*@UJu0jrɏ@[ؓ(\bEJt;ٶ#vL1 HᇣZOXo T Xz:ehD/TS~/N4bWuD'p"haW۝SZKH]4I3d -s=1@Z%BJ1O|9ۖLKEUΠJ8Ч`.Ɵ݋Wy*}6VOEcw ur'Q,*V"Oǐ.+x緭?̯ W!,a9Dr"'Q kA}(*Hu)Š80߀b=DBD];"jJ?%-Rliw9aBQ"d+BT$('4?Qo86q+mbQ*"FQ"Pժ*QӆNAzn7^p(+HsX2ag\B Vu)\[ S T$@}b]"!m+XǨԮ("U(Fm h &(N}ԙ.> G&}暫\ a (eZ0B*%RU J FUPֹ9 VJaZ'Td>5>]'~􎹴gvK2TF)sF i|{j/U`Ŧ`%b4?aކoBŒ&){k!nerCPၬ}?+oLj>j l\2I.cM`wmiOH؏ 7y3~Bԯ-s3i>ϖؾ-#,3Xtw^xW_]dOVWEyaΎ_{M-IWt١,~ػMp[p(@6Nygl?|B )B )B )B )B )B )B )B )B )B )B )B )B )B8?W endstream endobj 64 0 obj << /Type /XObject /Subtype /Image /Width 200 /Height 200 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 3005 /Filter /FlateDecode >> stream x{pT7yA^d !I f  AV-2ԙ2VNAjK8kkPFUH)HyHH9߽{egs?=~{ދAAJ,Zzƍ?[`o F.^vν;{, {p[ _Z=C@Ml ju5W_fLS[\Mk\" H4;L޵5qJØکL*7"qD1E$Sg7i!hm5"XZ0`kޟF " J34k0`*.QLhxC_ wF`݀@ ,$J$P\IgW Ok=+[(%UY!€ahD{E%7Յ}_6 ƾ!ю}B)) C)X7 ˍ-+"/ 5ic*l<gv?IH@ݰd- yg`JMED:vw`u桠byׯiX2c4t HƊ Xx0`s9"G2M`Ux*^55[:v_H/޽[:C*Q+[JK3.߹ bJLefڼ|ٗw{o7Vn.)t|mC3Q}!Ag\ &<U4 &ӆ:F._\Au$T QA.s,&'80S. ڀE%?^/k Ä*,'=((7 -L`5XЅԡ 45?6@ȁI g(+j +H%_u)2uOCC4 ¡_HQkZ^A*`rHf|A!ǫ( r'x9+:P X 6eN88pAF:Ā [ 16>n0B)Bo~ N*I($,r1IVtR@2P* <48}@Hp)8k?g@PqţQBb"JOSJ ?d' ꍞ]QHII=" ^A(H9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq e@ endstream endobj 54 0 obj << /Type /XObject /Subtype /Image /Width 200 /Height 200 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 65 0 R /Length 12772 /Filter /FlateDecode >> stream xxK.v ED* ,(6 THTBz{ҕ*ti{rWޥ<>wޙɤB )B )B )B )B )B )B )PG>8')@q#/oox cIJ 8?U1$/_ h>$+`HLH`KfH:h$o{CmҹD$$$O2+un)ު15BcSԭz12&ǣH3խpKL9vlX-D(PGj(֭_?c_I$O}NYP$t)s{ X+,QDP|n>Aa Ekڃ!hZ(Zn$V߇V ܀{Њ)i>(-U.k[sƇ '} j̠D䒡4^օrܗFM'p4j0 cTfq. DwX_ʘ YS{+}N 8z:8m( %E[F[m,WQRAj@qҾ(YKωIWN@+ } 4+gK P7 wCh=(HYpd@=\PVhY,Jx.(A=0fx4ޑoN 2NFmR (0'x5V cɊrpز~A xs4^%M"Ja˫ sJSh8Q9qf(VZv:غz^qm$_y(O x܏B0tjIo[l'"b|1-+ȱ@ wP̱K_W-!Mj GE@B;_ * ID`XHDahPzAcogL-d2_rw,Ge=Z7z 7WC Asj&规!rxjMتlS%!zOѺ!u90Kl:D8]ނ,q\AQpEVy篎+i!KS#ͷE1hHeܘd\%BM.D.˕Aў(ԉz>nP芌4U =vB5+`50|"w9-.I mA=~^8MgҞWٮ^uk>QA*=C=#hUJ Σ&*!FX|Msr۠u=Qj#Px8Z`$NҸ- *=u/[,}߸ޜR>eP ,:P\/,Jպ*Ѵ:ho+("]Ou+k>:٢ОW0*+(BlPA um]3e;M'f5jscå믝K.wUz򑆳[O4}3yϘu)=@/CrG!6'r-LOA֝l^d BDޠ]ALS&&+R/4oaC 50?CZv43-haX֕VC7uƉI݅ YS{[MR6GJ 9dALQڴ?lşX,WZu\SHTs0;i-5QYP-КՌˣ8u_5!eo ,LW -Y,ϙƩks.(|ҺJJtdZmoUUUjM~]kmfn[L)KiPQ"g|Ju^E:pG<m1ATP򪲢**xPRg uqh(?uŽZbEi]Egř8cq~ߛ*E8ݣЁwhW۟E(BtjX熪*MBEnܘ!]7SUYYw~=ŻZL[|\%/uů-*uD;@u b͇UQYQ^Qu&.=0KGZW/ҕ܊p]AC Mvkf18(~ xvy|0*BO}QUZtTaT ҆c.dsYhA%u%e-yΔ /R46=FesU+5AJ'io87ih1)KMˉvac[*g;CGӮ g".o0^0 B.BZ+\>G+uD´Z-2&OnB[chW[B!V,TdZ6 ʾ&%VeAFRr]סM_Zs& 5Ϻ6h*/P.*[6UQVRA+TZ0ueU*ZvdGJ;Tz0|Q~ᇊ?(1+?EV擸#D-+`6s97SweuV^6Aޣ]`P5@rp܌ړifZISl4ΛƔQV\ظ}aѮ"PM\{>`ʲw^=}Α(D.UӞwʊ 5qe4YOnU%z}N Fd;6#썴#+#ݪ4SUZpt0WРԺ"g_=LVS7'y0ޢ]AYQjrYIqYYǕNPo%kޓE'T\hjojjj։3&%4s߉HY+*"Z9\$L-UOuFuO婎NiE?33*/ؤfcQ_z-pyv(-m;F+UEްv!gi1$^vu8UAeye jOb? L$ϸ}MP=]ď-倡JK(>e\ -+h!XuyZr Kс7hWۑj]ֿZM,\Vg@y$W>(ƺZ4Qˢ_#Y`Np\RڕE{"sL[ޤ]Uկ+禉3G.nψyZT~p *T$++KUeB+;د3\1^~聢ѱ+?aGJn_}ܕo7fDN;,gr6ͬs##xn0ĉ "o\ɓo*UaA~>DT]Ik:qa"*E2 tŠUBMGŹy!vHna:Z}%f+šwڮP*TVIƵB-jA]m|U$L Eg_&I*3y)NW%g+]YTͷ/ CӮ``HJXma%~g2$~MC/%t7D̓:AWOV NkuW W1=(T#P2Fa(tWBBA)-B]\rz/rYW )43]avgYbuwkwҮR2XK,)(-]]to}eYл/7gX\jWy];bng]mJU|M MIY$ 2y{I/NKDJ[b]Y})+975/F^]O-5?/JhWht7UN."ީ01Yu]緬H 6ЮjvJzfD2ZWn$=OhnE6m(jc@&+C@몳JwLɞ(:1A iv^Z5?eTYF;DѺNm"w"=AN}&&+qD1\{M蠔Și'Zu7 *?A+])Kz&?j`ʮ!hZ+v' W 4 >6]Ǖ>ZEW("e GNd,]H_;4ъ H&JVؕ%U.cHakJ?wBǮS>J*?;JDeK<9EO$L uyyym1ƓϜ;~#暓yvhwŦGEǤ,đ>2epJC̝TGiCQ*CBATGiV@XQzG[0YA~pb{%hW>]1h5l>O]!\AUS_'NXv7Ѩ̘ jM$@uUǾ=P] ]w|GlM,Ѵ2LYٗo++$BDŷ P9垅C:WZMkgeeyKȺ猾DC")TQ"Tؕ*CUGWmS'zX'qiED+L} -B]<8WDhDIG*+3-څU,)uW7|B-бqW2wxc$ӛBD0fp\e,23 'IQaKWKXn'M}٠Ю hUEi,]NdI(4%jũ/P1\cW5U9ާ1նGԮjZ;(K"hN\tq\9NhI/'!*#PvUN̥*=$\۟vuUBW1M5_آOҮe⥕wAD'.Cȁɨ(iUvee *pU9߾seº?vk\WZ d^Z^]z#/Jhb)*JtU2˩Swp(':quO>*)DjL%Dʹ2\NJ ҮXxmE`x!rtMզ ҡU *8fF޷A.cQiڕ%gM{oYE+|vu-<!'xh'.]]B)~T9 g7yPᑠ5*ܢxsyn8uA1'ZvuL|&"g!!'iJU'@+v+Y$fʟuf}5]OwŠU.A2 ?HxF`_\;#9—}Dʻ2܊HTfڊveYmoL$h{dccQ392Ӓ2.SR#ߛ(OƁALeUFFzG$ʆvnø靠zFJh FGh\CۆЪ89;^]8UF)i"NUFBPQɊ%Ē+Qpuu\_.5X-)p26HtfXjxgd~Y%Ħ)YWZR%+ڕe,VŞ3k]A=|~!#UH<"O_e9AHsSZ2(]Txn+?bQdrefe1?"whW_DiY" uۃ2 'QgA)LJ*ʺ5Ԓ9 LJoU<H.5|3"S_d&NUr蛂,D( _x-Y0*Ӯ"- >-)d];!V˴8'=xu)=A" 0Omݫ{3/ů(ܫ& Z%J*ʺq.ԷE=r5*hGCGwK g2F v0 ǃ2?qqREv敷 F",23+~d!s_أѮH4`dz4iG&G}q_Aqd[V, (+5~%:fJwk : ze|" vEl0!2EDzZχS,HY61eմԽ=LyO0RbiWѶ-! KOxȺO\g}f{jW`le%V䧥fv s¿vE;tne ۙMklZU -ՑxBߕA]ѽ6݉2'J~%R{Fq^u86J\< DG>]Gf%]̸E#3F"Em=uŬ!gcݻ %|pN:ZEJKGWr߂s_z`۴zHQx 9Ӯb-[B;;q NȻjWrZr'vJ@AUe'̳{^np_-Aue\հ鑼wwQ9xQ)P,ʙvSV]^cȞ-ߓx} qU-oLSA Uu} s:u*[ʸ5vum|K-xEo6$QY*֮iUr ֊?5|ڇmVhOGL<- ƍC E;\Y8iQ9S(WK}kɬ%zcY>rWF4u[XZ +Bִnpo2Ӻb:a091e2h",306ݢ18dit7\VAquhqô̡yHuT7:PCuu +hM+`mNu;F/NyXӥ߇~u9Z]xwv.{GhW^Ǵ?ImOURrz)Ѳo#`]҅'YUycںܻ,EPu%0*8q u7u!Wش>Z\A.> L۔Ѷ-+z5~<Ъ vkug7i^yReѢE[cN+h28p4`tesX9I!egsCmvcet%0+;[R7 P3]:\fMP1\ϊXCx<$BH!mymjy0ֈ۽mWzTB5@ 8Ż%ES&yzqt;+u1>,L%n{P+xq0ѭW[\Aڄn_l 9_^rTw8~U]'z}Z}s(tgr 7`̘] KіM!V GsVIqU?W=_+]9rjH7ΑxtN5 ̠ځ?g N\%!(UxQesK;HP8.G{-{CuxQ&}&Ep J0ykekjQr#%hY{x ^6S{]/|6)Au3qϋgF{_!_Knb17oz>kh08랋롐:[7ɃsewH> stream xkpTBJFfI'l&vZ*HGiXF{A;v*a2*Jk2":*rQ," X ys6'e9{{ kaaO1afwOwwWCu~?zgb}6?~$O(nq>k JKL^'6PK~ ,W&*&Iac}XLٿm@ M"*_p]"ev,ru5N"F[ڠn>(b;7t1F-gqW>Nd*&fS qg4^ęd;yTĝpH]/8jU8ƫq('dOq4 G9<).O8Ωk.p&"-}NTp1|"\pQ4.'3VD)Fg.pL4+\ht|i;*@_~^Jl E1*x|vU(oX.bm3*y˱^(Cv_ioȷ xՖb\۫vG o=*zB$V=vՋBQy:Kzt,YoY9G"ڼ=5_*{!5 =~dm=Um q1˜Ƿ_5Au_8ۦ4T-UGߌxi>=>2s&}<=#{<~"{Ǽa^8Q# b vH5PoxFhG {YJ_]7<">a8'{ސ1xN#&j<~cj*g/5E-VӿC]xdSUt@Qéuj X}jz`y^5=Z-?%[E׭?z m G;m7!Nj`bC W gQI@zWE!h+_Fu<=2uD_RDe.|<1æhYD@cDڨtEN;ƞHBb2jdWAcw8{":rtFV8R0>$ba id _WtqɄ")>*J 8㠑 ƻ$K'H^nf *7o|AkjlKK 7K[5.x Q]^f/qMp=Z*'@5W,eUa'C(Ҳ$|wjDK%*&_ ǰbYEW _PW[CzOR=94ŊΣ? HA搘B2 @8RO3I @!9ѕ.+6 *Qpx(p5IN#r ? T3#P"t>|a8,/'0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 sY 8q endstream endobj 55 0 obj << /Type /XObject /Subtype /Image /Width 200 /Height 200 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 66 0 R /Length 11561 /Filter /FlateDecode >> stream xwXTW/E`za:Ęhn6f&lI6eIL2 ]lшVP" -[;3̽{a<><Ýs>}sΥ(A $H A $H A $H Al4ELf)֌pSDzsXxdx9|eW "3Fok>-f 3D+zoo) 5ȓ"+&rÅd "$ -,"$ !`qnhhI5N{ h8Ac%P<"@"NhbMh0$_^_]m(9L?۠YP*JeE$y| TH1.$yC9bfzRDc3g^t<\}bDz(HtOKsE"w_v0 Y6.(0aa&s`SR)eBDC )[@eAD{qJ-r+BwU#88mVY*9r 9OሂtAs )aUʁ[^DBdbTrE O+0ed kZ/sz,7Ũp#\/֨^R@^γٔ+QPra Ø+]Ltk ,WL$Y*KEH(lbDt/$b(B O-NHÄ^)1TPP U 3]hՊ)JcQ`4#Y.I's*M!TkPƠtьZ0 U q6%:*MшrE֠4.SnMx#|0zcE!NC YP1³T.Bޑh@h+vQަfؔ6E#h)T(bhP(05l .ZLdUVAZvY ޸(飰6cTnNE@rD9ekPtqB=:Sqe\Ü [Ҥh$D)[;Ƭ?&TPTh V1-woV7o\ /Xr gSQ&zesf̜*(QNl 0&]L.E mUJ.(G詟.Q/5U;W,ڬ% TD95`NX*EH\IJ%2Ę1m]*>լs@hZצLcu̎/S2.}Wlq9;;-R/_k8qcڍ.qarM.ES=/hU`\ynSR6qŌD ռ|be坵j~kU;/~zU]L*hfY\,QcB܂P bHt*5֩dUCY5=]^S 4=- k1!ԲhI.JS`ҭ[QQ!($:Lq=zm"($. &y:^|ez&RdJ%$OgAE2ì4h^Нu}φjY.!ٻ;WdY!>?Bx'%D?P,\q4Ik_uvaB/颡}{UG}ý`Y.\}h2!vP\BőG+Wq9]΋=QnŃ+Z;@.FݤN*A/PXAw,7=D'VhvukwCЂB[-kH\4˄ʩ$98%WvB%sv_vTPHM VXu+9FU&b,@eh[nl*(TtGE6N~ZxCo^⍖KK^}#l[|t@Xx${h?9WÊߵnz*WHB"D m[nB\TlË7E~̔9L!DW]!T8!Z՗~_\TneS87x%s W^j W$Psthk(kŪl!5+h/!W rśP1Q/-v'uwruoLf*щwyգEW_rB+T\q"Z4hU\*^O} fТ`Û`[W(CyճcYAbCt1gY@_= W3EP\y_(Ţݔ'\M,v*~fEO.ZPjnP>@?{44רE(Oݮ+q2+P(j^\bB -eѸo|Jp%K޵4-h;O[;ݗ/_L 9~\zd؅v{4kzu{5.@z$\ޖm VL3X"`ś ] o׫_< z",{pq tg.XKe EY䇇h:5Zg< ~u,7W-#f\.mg b}Q<.Or˫_;n$h m#{yG숻G:ǵ=]$\.ݿ\M-Sr &rrH*i-o -6 vX=jʷyာYg?w\rUQ7GAmm1zB(E(\-X+\նu5OM@ (b6{dpFGP\A]H¦Xw3txW_3siOg_E$u>媱<-yr3(;(j $2 mHrAk^Ybgg'+ֺhysa}Փ sqom糊WPW"\Ҁ t,Stp% ;T +PChC{C]\q{7+]4qݕ+Gյ*9AdS) ݦ =\S,PS+\=ǩɣ^m#g\*a\C fֈP8`)rpuk'\97o)T'(W|UEk!nPa+Wm~hc1mƾ\"L@,ŚxzBGG+=<JCs< Eنs? U%WL@gN83~GX@-d'o e#IyUf~&(wY;5mxso0'#rjRlNfe-R,SyPxW$\BCK+ֽ+ EP3+~+'T@GfJH/20@ iYYFF<6w4"~敌|fz>9/y}AG5PQSo<(h= fYofPD+ql/8=R+\z6?B۬\9v 4h1\ⵢo)[@d.h%ƺriVqo(`Cݝ4ZÚHb؅D*/3:6B$tMb20 jݹ"\B+ZƝ:rʵ+{+T$=v 0vMq:dYao7V|ɬlb8yGF↢^q ]|&s^-[yՒK+Z+h=o T`'5+W&%u' ^J5`1wO~VRs q'qȡĬPAIkȍYӢ]-O s,;Zﻧ.|(g7q wVyUhǵA~ü0]Y9rا\eRE2_oBb㊋e?[WYӕMz8y*'<$nvܧju[5Bu"1+G;:Y`;2KЍ]5F^Zy9#+FϾ7Wmx0ZyX*RY1 D/f|\Aw7K8YԓиmY̛ѭ]IoWp=62ÝY#Ի7"0+0vU>.1J&q UFg,VL%W@ `*a(Ƭ@'KnV`i((.?2)$ZL˲x_- V4<+W[ ꚉ̟tf?!Qq}6# , rj松\&j;:&bw-ˉ֟L rֽ 9U;/'QY9O\I1r> ^Kq,;Z(vȬbs,40nc -BDBT_C*p{bV`$0gӹƅ+t4trA -0% %0|ڶ:'_gCVA'ĉЦȡ")Ӡqu~ytp%,JX,˙EeZrOWw HY2U׬,1=7T S3tHf*tDssѲZFX>[m:Vx| Y9FT|qpC뫓ӛZ3g!(f8[/xM")8*=Fs hm(WE|fԷk6{Ϝ,Z7OEQ!ĦXaV@o徆KfNu}D8$W4b|WB7g%=]l[·wcֈ"8 'B#&0VmCNr9_ҿ+h8fcxp/ͧA|gw'MvŠ-BYP!8ш \=!|ZJ<qlSV)cea͢ߢcꋎqDMu%uo{_ϯο'AEhVþ EU0c\"V/:%AhII65y GVc[(A8'fuD}d~Xb-Ű%ĦU":R-!)bQ<VJih<o-V˜3?rbgWq|%WWzzh/fT7 rpb T{5+0vdzAf R*nYl֪MvqMhhEʬei_3޼bkSBhl(ϡBs p:#:N3Gq^jPUWRqhMϝ+haYJ&C_@_ݕ32S:08:T@*hZU_16i7"L!Z{sjjkC3k^&S{W?KDؔPn"2n@Nm`}\Ҡ Mzl73"Gf0kmm(<, &НO$2FCwv8{1׵+=$\"Imd~U%Yٗڧdz6Hi44:XjY$ht< qKB2^\1~ǷF/4|k~xc{,vo9ͬ2s׬d˂C,Bd "Fr2.h} f_]]} n*/hNwL< hZG\M@1J T ,6gLc0Wf[r UU_xy%?l%}P*9:W1OCkճ6\Z͎rCPy}9rQcBک܋y߽ Pr/sEkʺ Ui]tcW@Lv΢/jt Z%sfeGb Ȫ;m W9-@сۢ8X|`Pr*J?0_=>8 dea.h9&pJF4ָNth *|ʦ8]'V%pe7l1K( Y94tdl$ZF]蒭 O.VX+APzYZ Q|yR0f5¬pXT 4&2kD_Jj3a(" k 5žG \E&WZln -&.`\4Vb5 H Slj( tor@#ex]N<00/ϳrTZ>eGn_#" å5(#5OenhuJYyM>N.ʹ-CÓ(T59~3Z6/І޸PW/]5mX=P_b]|.S&ͦP)`P>\xjc$wR N;%5u+N/S!M~0D! z rq2ekN` AlEcq־QRzk}Qt?O\]F&jPI?䳃hk9JkJ7J_ieJ('&Y0BGAI-4]~3l_9w bPx`6l^6}bcYaDK#Gr-NkZw1r$J"ȉJ>ːjF5h}W 8+'Zlܖ.P.=̖pŅ(П~rPy=.th)%=%A_f +V ė([K4*x9Zj +BX"{҇;>Z{ft ND9JT2(TtM!ѝ",2DA6HFg6=즁OƳzqNhD l-hsuIh6RtpәL3Τ=N$D@M(BeD\RN@m`6JhG֏J=t]2Xຑn,F7EbYy_BKCI*8eL*{sǟmvn;&jWHYD yD٠JAo#L+W H@5FӾ0+-}vn#?p}S -]'A1Ca?]ipf,!؆.fdDG`0+/l}.H졒^41R~~˳T)8aB({PlKvP!\+Ҥų)ntA[ X6j_|vAE>9Hg-K4ִRD4 ʝ(Ա VV[ T=hM7"\+L+'Rt002@S- :uol{7vL5OMp&@0D>Vbk$0FA-;r3 lѮ~R62:D`Sj3.ǁ`(cbJWZ"$gYBӽO0՟j!n0js֬QƊ$?`4ƘAIu~1xUToֱaҾ 2 Tz-T`P̠ G;E&JznN3HRImHx>`CFHz!%@#DD}f #ìRO50?άQE1AX;cmd yl:Z(F R 1fmh,vfGnV#&#H 5 x@ɻ>1#M=3QQJ`hh<&! Hbmgk/_De| G 8k 0&4t'}78P3E'yX0A}AР8fwu'eeU͛[e?L qդРaBaeQjr#Hff4딯>%W,*/t`Rim~8=~fӍڏ)Z$ aݦ |W!)h1rrÂC$H A $H A $H A $H A $H A $H / endstream endobj 66 0 obj << /Type /XObject /Subtype /Image /Width 200 /Height 200 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 2983 /Filter /FlateDecode >> stream x{pTwf~ټț7n d"Li3`A^v:-ցO"B0A%3s)AA4 ܺp` }gaa\: BaM{Oɸv|Ϛ$?\c}띱RJd qvmɒId,UfK-Ǣ4l)B"~,2{/5(pγ(qnLRמcQ!G3,~9G쭴"wэ6NuYƱ^41+ Q32w< h8.rZWqP}wx;<4wxܫ($hyBcD9$W mǽnhvK{oجcxpD[$c1|c߻[+% %U`q1dMa%@QVҰj`.,(\a!d ,u9BAuHž$lx a> NqB4DcCK\1hd "|\ ҠA9dFgP&w ?̀!nlIeM7)(xP>qhNaq^! *q?^AQÔM,.Nl7= <aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/%i endstream endobj 72 0 obj << /Length 2126 /Filter /FlateDecode >> stream xXI8WrU$k_rK//NW5CT,6ɒs_?ْL2X$an.޽ ?_bQUvQA"΂$Mve{sm$LL'%\=]=QeÊV4oXVxf_KM_EkQjF\k"3Rѵeό92.g#L"{ GQyP 272^Y՝45{F^j< [nFhfYƪ,f0{xEe@ `'x։~g,ϐ,Eꮃ \}8M$$7IR-w.?x>JAy4b'U$/ ٞ_p\yIH(2)~w'Nj8l~(h9|\Yŗz6o7t~C EFp3uG,v ,*&~k"p ~Y DCplkB҈Q4QK; hD8E>ZLw%nh]x-O8)iiJnTmt4NYAtޙfJ>Zgk$ FŒ=I^&) rg\sjθ;5QxMGnYhI lr0N` =JZxFnE(Gt0 3" Lxa&.E cnY5)L1*YUx.a/Z~4,pth$$ vB) $9:83h<(h,&Ƣ$uQ\[LA|61qNAcJq $ؑ7&;r0<'钿rgG5D|QFF]hQʡo1<p(4`.e#d; kf4BH>Bfۓ0hQe0cPʡ#' e4(lA+Vj񵺽S&A՝(ygO* a8״\CWRI.ni'AS4n2>d[ ڍ^}1,Ώ*;RiuW uuQCGڣY\E4C F BqGɕހYHPò=,'|C& @% 5mpc$/4.ǫaϰ]"\Cf<07ď{ pn{OX?.H$z{W`D;5By=UQ2[;DtjƢҩ:'{Mxa7$YԜ^7=XyiR8B9B4q@)'F]enM@cw Ϛdj^n1$:36ZrcYPg{Q$B 4܏"o?@/IQmQMYk0b=C~$w]ػExcV?Zg{q;$b ]Z[K}}?19)"Z @Uk1w,oeXNs*Hf'v[|HޏQK͜{m +sYc?|NN6އZuZٽ`ٿڱ7| j旊8l~^O endstream endobj 88 0 obj << /Length 1711 /Filter /FlateDecode >> stream xڵXw6 mD")J4~Em-p$L)N_whG 8 G>7waD\D! hE$H}N>{oÆMH*cIzK@r'1yԜ5F1+r0y3ٖ3~s%!md;#AC[#)[ v,lFΟM ]LV {]0]qhv뇱VH#ksYY1|6+.WR-}aYj0qt( R+l w).Y\qT4Y N<]V~fQ$1XBc&Kb6EӮGP)ⅴHEڳ !#}8-556 ]FdcGxBv@(T4I" Ud̮ʻ p^ZXr2qOL -Nt2E RCr]L8;p+!iŒMK8Y6i?>}Q58#w!WzAO,x wwJy ah@t-ƁwYXr>ټج֮'2SZ~zJNY@fvm~iC q^\k Ofim i^<8wE*3s1*̡^ xDc9[kq6Ð$"{~l<*g ǣMݠw^b7\a8'WDZi- lU5Eќ̛2.F1SAM޵%wo9Ƙ6vl#p/åftA}/|uD⋀Q ""bmVUORD&Mljͥub+!aԹ JNO" z"G mmY2/)`/ePLbrn;IE%+ū\zy BCfpqa Z>zQ~b,t, 4oĬ(r* е]y,ԲSq]6Xf'׃ QP}]:-q,\`xl4EEqF3kILfycOj&۬kmU/D}ҦĚ^__`0$  ,\`c&kؐyԆVDyN77$LWY" ~y@IIa@=H 7d br* 9 ;(WUNlǀ$ g}!jm(!+ivFwRVkvQ;p轨l8ihn$ c٠;jIN w!H5"c[.CoH"w3d!A*+r3aܦ^k"ʃ}]o8:=,T@.h93xo..O٩7_1A?Z3Ĝ @LO >!cb qb: oBe T;TG*.Ltajѐ@G+?tͻ-MOSPѶ;2E$T2d^t endstream endobj 101 0 obj << /Length 1010 /Filter /FlateDecode >> stream xڵVK6WP 2P|Hm)P n 5a"l~}"iӶ6^y! z@Ečo6mJ\,GZ\9Z 4E-;, wsR"ł,}-~5ƍh 'P 0Zؾ^oY.E 1J$cYAk4IȒ9ah QhzDڇ[k!4}8B+Y4+-D6ʎY:"Q[jǦb.ξ,g d/[xٻc50T $<jK^1V@sB).̪yr^rkri5"3jgUZZ}X}?ةsa-H5A;^>l#=Rogs*f1'Z=qI֨!-VAMjg|x5ܫI-%8e R.rS9 0zȬX-3U#S8{Ǝ}o8㤜aN1/E!JUtLz.)!\ }oD~q-lo/^bE+l g\1Zly; _`pd4"bL?]C/]6]k- OIРn )Q?u > endstream endobj 105 0 obj << /Length 1650 /Filter /FlateDecode >> stream xڵ˒8+@-l^{$SC6  ~v%[8l2傦R8qjb2,2 nnAkoe] 4]aq=%ۮi2J,s~tN%xDRVvO?E'YBQ}ݪ V/d%bIIɲZq4 v$jփQC71<wtAI|'ЊVva 4)V/=Q==)+-Q}Q5'`SQ,O%۷ȀW=se8{[~l1e,-;-#JO2wRMv=VòH߀C}ۡ%"!F1W2Hm7&d ؝xF*dKnq\}+B*D(3i DQb"| U`&2HD{"!S9KaYB,$Y*%sԀ^">3^IM1e2o`b 9n~+G"c9{duD٠@&J)N`R`q&<g1vv;/PPyR&NS~Or 3LhJK/S"[j\5Nڦ !X HLr;x[j_ e1ɼH|7 Wov"x1@3>_G^$J#'o4D"^g?[Rc~\ӒEm!߷_tb_`k5K1y +b:Eke&2/%q=nQTć+;KT.4wBzqu!vVRQ~lwn) &m jz^oۚPi-I$)^1"BR$Ghìif9X.6}R޷]G`϶M>*>.aڮZwmm . u1ZW:~=NݪF.0iA2 Sk*c"a85 80 㝍yj3ϻC}+ AtFVvc|[K 134)`r^oHWH%ǫgX!zlh \Ln\/$g3W'S±a$i¤HM#( ;?Nk endstream endobj 112 0 obj << /Length 1834 /Filter /FlateDecode >> stream xڵXKo6W"Z41m,qq_PZyIۋW$o^v{˵~p__\~S+iG`őpȊP uc˼mԫ8*=1Y6v@'?2L_ sip؏eU[Ѡdr} ˱ݎ4,^c?X{x&^ؗw;ϞƲm*:Dő:#S!RW n~l8m:Ssۃ pH"8w $ڮqŲ馑囂iIsԵ(X(^?gnd:`0y" A)J$}Y*j(폍+s&d7YJ,/0gPB n0gͭk"dXZ I<>]Qp]I}ѥۙQMD pF C ]`\91-lf*Il9\!n2h9:$nYө٩ؖgőEBf:r߁~0`J~ZSyy aWBw+>D%q3X\ÙN|4-?(c(T2O&뼟`ާ3`̑"x}+ϣA[̱+W w;601'F Iϧ9yRYq}/{%v8ݭb%hxVB/krX+ӌk1J额}G[ݎ;, !P~ e~> stream xڭUQo0~H R'SԇuDiR4~|&!Mn^}w}K=z,:."je!˅8e}ӕ}iۖ'G%Dқy38 O9,у.0⌳wBFxؒYJh'\/_P- :=3O(n̕ɮʖw]+aW Np#1X=HF=tv ЬjBB \m3l3_Yz4 'mTkd.+;]4]$W?Ʊglf{6II˦2Аq{`^9pH5t؀syb3X#ƝH/GS endstream endobj 126 0 obj << /Length 1671 /Filter /FlateDecode >> stream xZKs6WP>ޏzSI*N%✒= _n`ƻ0ZF H-՟Bh ::byt %VH+F]˫ww]|w8C7*ʶQmEgǡ6Еk 2Ssr #8U]]%¨2޵i_쉷EQ[9`4Q4"n]k5zG>n`A fءKQLW+EE`DqaUaêAS뤝X)m\X})mCE6L%Mg#@ǦL WHx+J׷lZl!G]Jj$}/FUc?F{G=1&m&$$Ϯ n{?}e1njHVhmYߑX/3cn vM!}@l}9F{MA#C3&'NN/DfM暏I`b97oa 4 Nuw{9'GL/ TL!Msߣ'V֓*5Pi-Dx̚nYe05NWV e! e;POA&M;l^}ݛaBY(GI^BKI"4d~Ճ` X(Ī"Ye #o+_.L%ք+>YMwk39n.1!5U_OSBL'mLc)9]?@c&`2Ljbt A=NRpPB> =:~ pU*TEX%фv%[Y,Q_4545cy`9dn E"#N8q6Kq*CfapRB)ù*5z[<r&|+DƉ6UYUN@ MaU`39n 4-vG/[ՔTUH9–gͮezApNfW}6쯅 # Hbg{/ _I4[jPłY\+"8;,E*H|JfD H>aE&$aWa0$,sf yծ]Vnzߞ6$>OWΦi$3-ϙn0`mȊ77GcSY„CM  pĦ%Lb1cg0e&ĕû{ÝP߉0iAn5be~Wk?_$M Y7߇໫ÿHyV^>2D!YG h#b鿯ky endstream endobj 5 0 obj << /Type /ObjStm /N 100 /First 809 /Length 1865 /Filter /FlateDecode >> stream xY[OG~_q̥"JJiZfK}  T)awo|˜{Ҕi䙌R&\"d5]&ɺ\Y&xJK&APijB >C!bMyffsbĒ-LɃ9Vc#A g`7rYK{PX#XKW2xWvR΁2VEX'K9B(D!7ZC0,3 (rAԈ:jID>( ,h5k FM28Ð=7 ΞS"XAhp_qGdsK*Ah `~ "8YB.'_;[Xc5~ʸ^Foi`iCՐˉ%`9 DlNz}_-/h5seӧգi7onN d|Jhݍubrvz=9klλvGӦ{DFL'I7yxjڣz{'O㬡zwtT5vʹ@B v17RHO~k'g:[A6ۣ I.m/M꯿O Ct 񧋳;QP!g|67!L?}^]C=kmDmvAfaޝC{MAk+uN{ToM-JiA ?òz UxϿNU7~nsT\Ř>c,RXl|V9dPҀڢI /K@ װ8 TFY6]_KmITZl"%7NglZEi'QƬh LVE^aQMJĊ.N7 {v1V]~^b&!~y77{/QC||Ғcq_quW_l/x]M9Z@76DlVc./T\7l677rUTwĕ`o%Gs%zIxg/$*]ٻ +{4+c78ssC8vIFSA}jrTڧ"vkr!4}ɧ+[lAշ}w˷}g:4߯]kc;5r(:%'?^@f(~\SuW,k/ڵ/&7",teX8Ř2(˛U(n* WDS e[#T к"C)O\ 4Ůχ2h`.Ģ2Wn[af endstream endobj 173 0 obj << /Length 2539 /Filter /FlateDecode >> stream xr]_Lx븙l;Sx[H-INH&kdf/wq׳4 "֋9KȋIa$~dk馝/T~QWJScYWBm+j;#2wm]5ʭn:?{?]-0%Lo`sD)1KP=pyXKDqEݢʋei7 u KQofԹ]`w\@ cϡ %RyL} XgO-2o',JBo!KÐ(ɪxzVWgG8Cv8h`Tk-h$mU@c\<`,<;}<5J7MLS'5rmv jqhEt.s]8a",r t=uR0@|\/uV>h\dIo{M7b#J8ىr}mˎ$w?I%ؑk?$Q Y!|ϟ G':fv&kr[mW[Fs5@Qh9?u3kDC+)1fvǮ8 q&Bǭ</"aa+0QImO'D(  S'(&&_o!)b;k#MUNP/:]J;M:bپOGȢ@JowّXg !NGq=.k_QI>{PTL@L[{9ޜsct%ɤD$/N=鉠_(K.;2It'.#G Q$_Bcl65ct~[VwӱhT95ףP˨&{ |uMq A0Q$tt:kITA$Y"/UޢEP B:AyaBcu;!S-z7 W')%X$C)/ 2扽 XJ5BX ,,fd04G4YH.`;9{\EJS&[vl2Շ'w׸^OjSɊ QP J&ptlxˊ)PEŏ0_⩦.3^2K[aLjCwϘېt_i|#<{wcqxŀL{p]BPzsA z73*ãVW_Y5BϾ`pyBuxs'ߑnIu`dp:4 Y5ݗ8=ܼ;ԶEªkoGv: T G&<S0;"HInC%tr>H;j{I>0Nki9P(k3nZ ]{X6[;IY$1F +llǥv/kKbnYܖ ,p淴6^u-uu׭lYhнlݮ[@I^mw޸cL{E5̎Ӣ+4CtYSjO 44pӐkCoxbFl@|HM `̻yE ʣ.s]uE;pv~*û'!ixI P& }o.I] 6F{Z.C457jb:^C l0r9[$qz]> }x0 endstream endobj 196 0 obj << /Length 1062 /Filter /FlateDecode >> stream xXKs6Wp|f" I=4{k[LB|($4weѕ;Id9^L`v 5MY}u|m*S:єI…Hnz\ KL ztav\e:uaZp}ʠ{Oa\mdmEiT?&b.Y" @,8YV>ǐcɥ AoFLs$!Jp⚢jp曰,?8^&\Kt*;DS>` IrRt Б\MK¨IzrL=Dҹd9M45e$T9lmLJΐXC[wwCL O6梟,䯰}g!/WRc_:o IOaC1_~ؐU8rO̭VIskE ReUy&[DDb.? h{߹vl0P*Ѩ17aID<^7Q}qwh;vc|56eU7aG?/nȬf,Z-03ez|WG0WcΈNg(URɣC*]oRePsRIc%?)9a&;-".%I)D0/J,}xRG> stream xZK8W6rU2̩{UsAmmU˒#~>dɒu6! H=yGzL!C=켈8 P1B=l|տV_W ؤ"*VomޫtLϧqWum)n 'kKH0*CY뀇+(&kbDN7RzY٦1mɅ( aR:bt@۵G%J KlHMs֢r"Sێe$J~\  ؤdm k x}Vh5JfmVR0Sg6LE(o34Ú7q΢ 8=}}B0oPpU+ jNA±־?a9gmPkjw_u(E/E?530nZ ~ɶ,yɔ4EXCJtP-B3F>Ď>N˭g>0 ;Tg[lG(w2/V~4p0)u<>w[Qۻt =2ˆ$43+% N֠6ggcİC3I7ҒKC~XO GGn7 U.Be)*I(Uƨzݼ:!Nd v_onCƕ"K A|m`PHaD iM* Q;bJ狺#TD1$])3ui 5 `gpYda QH p a$R6)D谎_o x}3ݬt<-g-D%$rK3p- ?Jl+!\yHX nm3/WAWfh*Y`DQlI.a]~YE@\y}_9fYj Hj`#DhpŊ; gU4^B-T,"NQ$}skkU$AfcDKdX<:5?@kV:^+Ƌ,.`26NqTdN!Sl괝CkRn: W$n n0>1]0[R[%q \d? d#|<ͼMR&$ÿ/2BEXd8 hJM#%D~ʞv؉% Tp&GEri$a2Mu} IO?Io]=CQ:LN+}P_T缕]8مX'0`/aRg,pXb&(L7+.2=d6sX`YdeIx78cftޟ ,8Aνׇיg߄Gq`8=Ut?w?^"Ao km9/7* {u:-mVNƦ81Sގϓa #,"t,.M \n*@ M N"];fE>cJ!/ ʧvߘ3 wۓir Z0L0qZ:%EzxM&Ϋ`3k qi1,-ngix8n1&aQv*{[{B5A| q<`E9<=9m:u7tJF|LSt.c:;}#~B:EsHt %!l`sgx0̋(xi@w SxKiMu8t_ _s}7e[\}Ľ57.60KN)##p[qjzfTOd*Ī^,ӛ96dmபG=f xFKG_\e{ك]ivh"M/Bwnd.PVe `F4Ms}c(^< }HnZ{;!-`}l־N6@H˧qb[dEquGiw$B\Oޒ[l]IoQ, 9<㗫$H endstream endobj 240 0 obj << /Length 2511 /Filter /FlateDecode >> stream xZm۸_!g.DEs@&i=,Ply--9 IؖoVCs9䐜gzM-WDHŽR)兜0zs8ejYRˋV Po5 S4 M<;j,ͧ)2["/MjQ뤴iT 4U}%>|^$e|*E_-]kެ,EqY;Md@Y;švLY/9o3+vvA/5^:#}s5H*&P_AkM0yu+N4PB}1`qaiߍ4+cWښ֛0>tmjEtnmP% bx;{?[{{lOmp(7 i*… n('"pi7> H.10F1TKU7ČobjƜ,&ˍM׳ԴbIn58:2m Z#Mp:-Z A"/Ϊ]7ivm4Pe xfn͊$ǟVA_@"u ιYK]INCɰ^J@\߉K0EV{:(:*r_6{<>@+B{_PԿ6YvUE$;%)n;YbБmMY4 8AQ^bC'q֧ΩW_z]oU6P>*|?si)5Syrgn .Xy7\vRATU@|,ʖinBPPn7' 'Y%ٵ-HB8NJp`qeߥNeWpp,3:OSH"nJ۹i܎Sn*jqSj)  1S؇n#Yc +f iƉHb^efܡ H64l=+k:(`@K!{&qKv2=v5bsDiv&8}*(O&M1p歮/PxmdMtxBز_ڇ8Ћk)wUpp2e"<9:đ1#=r>u g|oJ`҅P\(=C$dV1S wb 1!WO;Q F XhX2bl B3RD 6LJ Ky/]Wۯa6oPh M-{UX|st͜.`'\WKCi-b{ݸ9.Fs-jun yQ뾲@Gf gwITC r3b#Jo#Hg+Ǟbd6hwWT]~̓yl#AB%<:ƮRd_ {L:r'Q|ND w4 & 25!sZYZMcbGob)`jo0U  S` = .!GJ(6BF4RE/꤅gJ24|l@@GzO[< xGGVOu:ZKSWup3,6l!TugsulAفJnp_7w?D"b˵] >[Cs0ڵa!U-@ QnIQΐ`X3}5Egg %.)X;q(8=t+7n ^r!Z@ܾϳb3LaTS=Y&*]^v)8Sp 2bu4Z:}giqc{AǴ݊>!xUNqOR\\AyE+x9#aEGquDa6?G`5=AI2ѳ}敁9п!l62 l<`x -<\!EtQp_>}'BF=1|ƌ"銉}|< endstream endobj 166 0 obj << /Type /ObjStm /N 100 /First 872 /Length 1676 /Filter /FlateDecode >> stream xڽYMo7W^( 9$#@>4@qi lGhӺ!@fWv[AqIS֐rBkz @TH"Ap%oTJ^&T4dҚ`lpCi J#;R5*dn De8ΠSl=(~[Qoje4<b˸&}B7g»Ö'ذdLF0e1XENSLjPŨB,j83F >d @N;Cr:v)A$Fhd|aʐȰraX T nh0%O%&01u| !, h9n o#xA!0 O-j r7`~2lo2F>kv [1]~*bmC4274&)LDUr[]Xg hf)>W,l9<>~adu^LNj*12;FK)B_clN!q}X{oٚ&2_h`2,nV ~{l!r\;a%E#rq{*<>=|:Pcft8֑bwjxbnSΌ%ZXcBGrd@H! A'arf`o.?c(d1IQl JYkdhEE2QHZu@o TCRHTc ۋ|~1)M6h?!jH&v(РR}Xrsb>,!WT5al +{#ub7RN %ra&ǪF>w@gxsoci>w t9vJqa}֟~$9%ʺ^Zt/kĿ\Bzu}Tp7elC3M\+^ցlŢ#va#y"؅}(].,QJvbSLͧ3-Tٞb3&>7atȌAc4~6C7Ȝ=aܾ ޘ~e=X֣ǺzsǑEp*/XۺQۺNykò:i}r'#$ǪrF…4@pOQA@=$"RAD@'Th|b;9GpAo׵wDp+iFDpٰ HQOCzv5xO$Hwi X;"ұDp Xb Ob]K }'ػ^K\l~B9#m&MW٬}x؜ӣ@h6*oM}c J>uՇ-paΥ|Xi1VzGasBFKoo8fuʭvᾈ31㐒GrJ> T*e zy endstream endobj 265 0 obj << /Length 2371 /Filter /FlateDecode >> stream xZYo~ׯ $vGw63p]y#Q0!qfS}DʶD"!꣺?VU#+_HaDXh|}8'1EY2mZF-52i&)C@XurB&ikc!A+̄RUXq{U?r{ N~~H׶Y^"?lQ&kkfu[]=CeM66@DkK$߾o=;1Q)"p#ǻ;d$2eb-ߦ7rZ5&T-(&_go"d= {4+=xQ=MkǷΒt^?74 S߽1>KI0>+Ϸ?Tj KKV܎1͓x$O|W^FgAnԆ]5i_e.&iZPCu͖F?e[50zѧQ{VAR.A0)v5ZQ.2t*-[婃z9\:?XJI_&]ly,RO]$Rp25uhcY4cw_q^j(b0X?~f3`6X{Z.89MݚV]FGQ׿|DIp>ѵ}w4l2'U)[z]1CUZ,YJL!t5uU)!l{F pW}a% le^LȞǧ,D!YLJe*)\}R9\j"潡a"v%Ԯ|UħbӇ,j?} #)$8si0>8>JRU^Xֆj-K@YϟopXEnkyM.6Ma^۾߶;Ѿ;.ᵝIld7ӻ*o7ڛK?daCDc{U ` 8sBKS} @nF10kf B V30Ĵ237 NH!9-onċ$L"sH&` ;̑T 6e$$FZ" zˬFUTq-z굓` Y]b_:/ 3:P!r;aIw5g]A5j9r3MRc`le8=<+-PPRx󹏸fF|MGN3IoܼH Oڜ΁n:રE@Ő?eW 4LҐH'p\Q Og IJC  91Uܴp*':X_]aw>'ypq@'!u vnrΛʝ{C?8&a =[X ̄[uG1CÈ<Ǜ&.mr*UL`ShsֲycV{.AզF}V;-M Fo%qZu\\y{2*9ܽ\ &JQফKh_C`*.yfCi3}wSWq 0~U\:׀'Ωj[u׉P9t +H9Djdm$;o[ Ǔ-SP5dl$Ͼ/q i; ڸvt]?|5kGUqYAi؃^jEy:q~#,A=q,lq!9`L"Z>:C$aoh椸L [d>. @4‰3!݊F̬ /I}\{ieγ1i"bVx-kdl SƘMtqЀ~Y @$ۋPgNHPLlkC~ endstream endobj 292 0 obj << /Length 2015 /Filter /FlateDecode >> stream xYY6~_!+)ދ8c ؅'>$yШ݂R[xvSɴE)uNV Npx I$FJDaI}N`͜aDcbvk'"XoD#IlI*ə޿Y=:FD'ّL3ǒqvw mWI~p\ 6 F=LpG6ڑɌylڗuQ֐HgvIm 2kg?nީD#-2-8IfeX%wy6)t^ۼ1-;?֙Yx}&7'~][K$4t6}S49># 3U:Z6mkƞ6y_65 ~7h ۝:Ҡ4*6+nlgn~ Ka,}![gԴda: ěmfd|#+ .,0g$ ze"*Sh\1%%$c$4 fkEMU5VEe |YzUvE^7TV%DO|E&N610$KP"ՉD"-1ǨE|쓽]>*dE\}2&uYbJxj{:ߘWW}/AYEwݕ zu~?,Lݗ 9  EB_qe|c8BA4w'f?i;DqBғXk:eXհ6|V^r2 yU,f-S|Ó y(;W͑x ?hF[j*jTo4x{'?Jd=lw5myd+H7+S,|Zcc &{{kD:tCK.e~.)Zǀ (2Jb ( " fBd!\$,-(,R|ښ}l1 ]8!?"WgCT,a2E'6ɲsj). =5]lVXǮVapQأZ M/{7i :$!{}uZ;+T@\M{؜,@; Mæ`&{A@H(tX<$[1F{5)6 rU79OT88.V͈Gc♺ܲ-&rW*'n,#o)|o)cc麡6py JOH+^y_^M%ϕedwSԣț*:Bzҕף *hB#=C =Ҫ>#w>t"wdǗ!w,6@Lp ]BR!bQ AOxe]ߌǶa C&4iu,_}v=e2GhX)WkX2TeQQ"PW|pi3cu ;b0ۅ6FI3MKt5_C +d;\ws1&Q6$M&&쐜W]|^22''AϪ38Ha?: i endstream endobj 318 0 obj << /Length 2394 /Filter /FlateDecode >> stream x[ݏ6߿Bؗz+~Sť@^6i[$gE!)Yeo6m ~8L,0xy?^]0AD"TptHRfp!yj\,p2JBơS&U혺1AQ OZ]L18/ f&wNŒ{KkTdWrNyS*񋆮%7]ٝ+,Ka/7 %Ɵd/^THb(RJ")$r]LyԓgNdaݧe-t~qTULP9ypT+:VX08G@vphhx\[Ht  Imp.1 *wI)U/I TXÎ q^=PEvqLi<(ծ(n Hrt~ήX$M+p>96ܔZg?kuJff+$VЗ hM'߯(€6HTH(2g!ᑑmdM 4;EFaγL@Ls1FhY=$4 zKJ!J6͇Z6u\>``T\v~{yz[wLIxwՖL<C8VXB~E)` mҐORjQ0)@þ{Lȋ̊5)FL8Z6YKDZNǎv ~OvVV7#&NEr  #6)"80/"8{:4k*'N'.->bEقQ?FطfpaQ0qNBűǷl/=:X c>C ;ca%m5˼(||]qWopEFiiJcܦY@`]TyG/)z"=xyNg׫;Pc@yi-:?#0=WL00)F0ju8E0(|ꮃVA-8Pe=G6S)%x 1Fi06,;aQ kx&U`dMAA}Tw|Ek.6%Hm洮 ! PRAD=)LNލA]0 GNmcU[2g1mvCC{'"S'ߣ}? Hh,#­B5A X < ig~Ī7kL)LXQ"{z^@LΈb:"%B3Q<^'OϫbS(s g3}5O:/PA $ 2Xԑڧ<)NQC v۱5(Gא*r=(FCZEPI4 1isq&Ti0@A'N=SwQ$70{8z>tNnPkI 'QJ} nʥ[i^' T`'W`4宖փE>,J<eD/]5 <0*ʒ| nNeSs#)}bR Ҕ lD%3 FβPc$ݛ,/[Œ8u\L|zSj#}T0LK#=`8 oh6Fw8+ͩ0a #˰p >d=&g3]R^=f!2jݹ 0w-8ZQ>׿{.CmswqIoHaH͆ rkӓ`lt@&\j%m}kո "*<[ӻw@MˤZu:~Ȥ3},Joy{qj$i_bhN!VиC{4AB1F0p6g?tuRVVt6ex2D~([F 9!)6-O5"ߟM -?0yF?ȞɃٳ~)+l\WFФ[(L $JhR53 endstream endobj 333 0 obj << /Length 1727 /Filter /FlateDecode >> stream xYo6_Ae6P3H hѥaTIȒ#~w<ʖm9'ֶxGT]3}yݓG8F zL005dbtd?YUX;KO˫.Sy2Nnv2RuVIU LO~}ۃ rpi—!* At}a WyVdB/Z XMUݞЫʳcmޝ>@/Pbq1ӬixEy>0 W*z7@XJ:m"?nd~%WUP(vdMw16 |2U_ )v[ -Z[u]u3f1! Ujw#7+ОEH%@=͠4i+'aIKo *khު,,6jEf|c#AS-!*R\MUfRZ/m`*kNoN;眱=rJz/ߎ+7:=N'O?.{_Aj0"d}7 \*rA8iD.Rn Hn}YL@U"?_`iVKwEq.[Zs^nXHwKЀ *]͖PGpb}!8mhVdC iZg ڡ1PayV}ksؤ#gk: 2t8H0Ah7Mh95wUySVr]:3v+í[Rw1@(7 Z`SmDwlm< nې3ޅmCӷpst~| m;ny/V߅dִ VzU[<}HUc|)l7='=yC`oe;w:Ν8׹PC׺_o/յ?q?Ȑͽ DF9 fyw4Nj0/jАVu!FO8-H)60\5z>Wlx pd[rٱ|}[ XAՅ<'!vj0ܞN֘c̎ s,ڬ e/ 0#[g a58ljZzY{Ik_\߇srUSovudImyUسyCm3b։¡v G{<|N)* CŅF '剈p?SWߠ endstream endobj 359 0 obj << /Length 2307 /Filter /FlateDecode >> stream xڵ]۸ݿBؗI+>䀻 r/mчm ч#ɻI;ʒMM7on_#BOQ(r"%<}F?~i#`-TJכNk9'y2j%0?6w:3`gJgҴN헺W60qeˡ|%#_'R،|U)4!$3"!K@`bu&GԞ1h4bdSU[ǫ;YuL/>"U˥)wAhx:+t{;7.NlEQV!T)~Tv?WHEےaO]9 Iib DaFBX4%3o<:dڕir =hǬl@~.,ӱ$BYH״* dn(UZvM f!AJ fGzp4<54H%kTV_Xl7Xt䭧N?홹y+*REj Rp(l`0GMKu2sUFfM{R uu.<ǐ( =` f%Dg#AW 6,uѵ씒Ջ˜n0 d&|.녗F?ϒ%-OV1o$5Y~`=}Ko<l>bj>bޝ?ITDv~]嬵o𻝂8Y @Z=vD$;`LApS}d|,l>r *cJ'J,L#lkY>4OU.,B)/119~x1DԎ{BgA!, rpK|$ wH~)_}O4Unxʫ`'{#j> stream x͘Mo7 +tl/Q$QZ4sHkqAZg7جllIC {4GI4Y[H!k"!ZÃ1-PUK,Aѧ^g EҐKALMfpBr*r㩡Kų1_SC ̗[ pY70k źf]Hu40}nBv!$Ë*~jcdJú$;R3w(G; ɦS.8Ü*T%4 MCp/!~\@2&L9w/̼iS[S2G@#x>LCb:l#s2։ LM"O#RPK! 8Crim`YF56ρ#Eaܐ bf(xA\0*j*HBtFXH'` 0ݺ S[K.p JlJAM;Xyѽ b3"V5Xިpr29V9,/~#slf}}uu1ܻlXuuIÇln6۳w)~n7m!_{|[@xYIO.i{촡v|lboڨ#{/~{t䭹y=ykic ٴ iOv^Oí/n2^OL]%grz?W_WoV6¹ij!U{t-"۞{\Co/ώ#?d?.R)5f).V{-yM mxJhؚ4QP\صXFcĄpqLlQT*AaTUh;TNJ}~\ …nd{3MPN2AiyKYĦ}),eꔋEKYRA_cqnTcpJ>/H1 B5jyAR%VU;zY|:P)Qg.}X orX}Fa-Ou6g=fj_c[?co`Xxub'FNV"4سSֈ H^|,ӴRe5JbP^bN|kAqM#.ߛwR8ٟQ ;2Y/e?S˲ՎѧĨn.wW{ b3-?|3ER車VW*pT>?_(0˛_`}5mby|2><7T QU+;wA @8x/++x|?V/X"h^:Xmm=d ,iLUx~DK}cM7|:PO'7Cq endstream endobj 376 0 obj << /Length 3007 /Filter /FlateDecode >> stream x͛Ks-UB_ÌdWr΁hTHo|%I f BP) ΨWOJ|nsxn%Y)459ȻZ`$낐P"]+PgMVetƹ:˴igjuS$(@53N6+t "S2V [Y2"KqFdݴ¦]foЬ^k+U>tktvED\?8>=\Ev ^'xxm)Bl{KM"6 RBjQ {d=cBnv%+/6iTN>ъq^% zM~F D¯>Sv!!LQ2 ÐӀyi\1spae sOOR= n!z:ȻL +y.F؁gHDBwK3~^"U5usr8zdA<,ާ1\cx@fY~^eZX3p7>S$]/{M}@d oBgDȊ=$  ǹAˏłAeZX ik_;^hON-:[C|N|j1 V Lk"VLHwPv2S`"v|f) iah MmU,KV[Xe0m3 G AC?ָb\4D$^1Q#L;hp#H0Z_/Tˮs] (TU1Hj @5@XlE<1#,,VZVg0}_#gETυ;Mc^j)uA^&]1 ~a<ְ̩+ÄUeڪt=|!KkswJt毛:'?}01šw}n !uFa; \Q٪N/{VM-]_/Cܔ;Rlq_s*r~Ƨt B"IMε[%nm_n! N<5bx" ~\XS>m0o.S\L\pH #iSW"[ee_Y`&L&]a1w2t uW[Q73vq 78&4N[ȃHD@ߡEfMbz.݂'6@nMDsH%"ײ?*oQP!JtQu-8F6?IH ^{sd ܪT D?V/ X "[~:@@}vWtzG089GzH.ʮs|nG\SsԅFՖG6T_,VW הרCo UAPʻe"Oi=.3%jiƒN)=?]eh"xya E2T̲(^ 8Jv,3c`@YRiZ\\$Ne/˺紭tqk\O6E B"B˖w඘@_sUmM졧E?jgX)fY[s o2UEBeWX=4m mB l¸a3.CjB|]$ACCNS6jp/:Ә :g=Fxr] J2ƴOwz_ts_?MHȨe≜Wf*Ku^wʠ*M-|{5sP"^Wo09wB]?XZӈ.b` fP~CɌluOz=k7)竼͍R*`os'3N[4eyw}[ t{B :Fla4.x`Qwr7u닟zͺHq˿[tCwpxzq' Ex}ϓ/Ynk} ш(W+ }ovWdRn6C?^P~Ы-׻*k*-pUWu@#M[j-Fb8FqɈI{@xQJU``*FW`_7H\Nz$?R} |EYb d> stream x[ݏ6߿B/g#R(M Rd>}ZZ-9t[~3CRwA&H"rr[/\{-"s(%륧Gbu}E39s.UYo|-~"H% by"sπ{g('yqĺE0׀|繊fzlQ:2幈gw<3t1zykh_vUZô>e[[aUٰhi"]]eU9GuQ?Ϸ:F47?+ QW($˄!C*B "۝"7r7EcU2owu>:]Pj-(nH\.?]_|Pxܹ[_Ko@l^ #չGZO5D: <WG:d\X?rꡈc4pzGMjW` |`e|\M ?TTnBiMt,RfJm',`:Or/ě)n`al, E=)Bv5%OZq-J'z8 U\NUy,_eMaL0!C`ƍ$gqOp]GJ-G8߶jQ_^e |y{i+y(zL1V +P4z&y oЩqWcc;?+ٚjO|l;v<_!٥^gx،H9>@ݠ ƝL&,G, &tîJ t|[%%\6o oi&WT,̤aăe]m!/ʳ)@)W)Z '5NŤјM|i`cQZƷ N:.(bʝ~1`#/,pQ=ӴJp_^c|<"-qyҩjJO} % axU{ݿ <_,ԽwZ2,yxnacnCNP3A:߮4{-YڦQ&:gH7t)ڋԪ#P]?^dѢ{~_i!אITnK)g u7!pftŨޟu4{jݮ ӴًԨ%脾r?x &n<{'*(f!L ؞m 5Hu> T2 c[簙3n%b޹uk8=0uJUyq T#smӑ}*ֆ6u;1:][Eˎh!L.MТM?JmZ{fbA,2]A2tC]TkSFa "0=qCQ$W3NO@^BLj5Dcnޘ k#֊[XdRPNT -D[ zD u&=׃s;Du̱dž["nfd%@ +E>ro"zRW[hA ;3°d Cإ\CXG8rqi݌ "i>vƽ>3@AqޯXl-Gr|U5j\O۠wQ.;S!|\tAW q_ >޿*\]:lʭ7|`?1ቆ}ş~Oq⤃A?9NN`? 09Ɖ<.9Lm GǛxg7˞rd2<+ )2l`:9:Î8~:4,A1=N=`C@1;N-s!_D}nOm>kF>{jOw9@K&H< e;_Pjx'$,u,~";\9ч/ p'~J1U{c#=>-i#qrG|y0PЯjx$4 3%ڬGUثnZCp)A&[R s+F!9ѻLw-$M+Z22k2[֪3&<> stream xڽXo6ȗz [0 n CEBe*xٰ} evu}$I;ޏwxFx3oOHxnĮbTs,^ odtA |']?s JCQIId"?6g9Ab x2?Xm|/ ʠk@K)\ȕ)"H(g_m]vLzH)0">ZN."s؏r-,SGnbYWmSOg~ꬔ*~΂ p*o }g_VS[(Vew/ n;)RDڎ5T:#nEN^Tܹ7J3=̗77p38,8d\+ThYoh|@_wG ۚi-<^Ne3x;u ~zA<W[)~' kXXk1T8=Of&7[?ſjQ@,iU >jdj7PT}kp{s@{=Q$C61YY '2bIl- M"Ih$\.Ѱ[(8HƎb#0e*5k~cD+d{g҂9{2Y_ "if֊Q <*(&5;SC6/aSʄL~QׯeI/"6k)_ïgȯ'B[lph Dn>?Q9uf5?\ӗẩI N>Yǚb%б >H_I.{g8$DbTĽL>>9Էxh1<'e/z>*euq|s]x H ]S |L>Gk*ĺK&!9)7*/K\TB %?H@Ww endstream endobj 432 0 obj << /Length1 2172 /Length2 17306 /Length3 0 /Length 18598 /Filter /FlateDecode >> stream xڌP\ۺb!iw %@n!e-YWuouU~1fU+ L {zf&3\: d Q';M]Pdq09x9y,LL9Čݬ =\dea?*Sj377'a;=@h 2x *>KFFwwwc;g5t: P0;58r swc' `ke w~Wq7:޽T@ Kk[d`lieo0%\<\f :݌lM ! 0~9:Y9838[#_f,no& ڻ8n=?4\]by'Y]LLL\#aj5OL9z;i}́?pn@+Ep3+S w2_NV]c0m=bFaIm%_Mgag03s8|+*[;X7{ۿ'kC _ y;%[euoD%7<<}C&_ -5吅Q\:OoMBOCyܧܩ7"Z8߳bom؇*5n2lO3"G2XKe%[K9që 8$~JNr]hdݹNS J2Iґ2l|+RD-p3=g]4>q<1?&vfqE蘧Y׵H,IG.Àq"[7_lÉ .r|~D_B匹->)8Z(|vwB2];B&hNyx#$u$w'.SPo{A<@cFNpzqUMv;P&CJ1O#QZx1۸&%ʼnX3$F{񃐛MK 8WeNU&oWEY"FG+r)UEAʜl,w^p[v.x DGUB"ogxFx\fJލGN '䌄>~8fy2 U>8B!eEd=\s3g<҇=a$BW !p愇a* >Թj:|TAWٟ>-<[s אX JoE uyI5H Vtg`EM }e?rBvaH%-{˄-=FwG-콡e|An'Vdhw1--Zg`1U@[ʪ4Msf;7VFǩ劽f5&(og wGWW1hNͷ(5{6c5ajjIӑwT5fs(>s$iW UJz 7Ql)x`>Emk42//niGں&1!!ڢ="m HLNaQ\޾N5 `=j]mTPJi;8i'27Pn&7w9zxӰ&3BL"(x so1bZd-rL"$b6"!xE0 p+l`E}|O-V8oWe0ѨŚ$veJ&Q+"kT:zY<r-rILh7 2,$9g58)F]='n$ʾm7ڊQZ9I25.8T(ٕ\.=p'Q, <51AlQ@Gk\3-#Ƶej1vW%>Q.2T%lrB!]Š  RA 89B Yt;PFrc8WqIVm2gY/+Z,r o:/BMU%ql0?{P ~%x$[ۥʄ&\_x)cT%-a8q=4f˗PC GBޮ)C629 PK^:LyDDreH\( R&}n1>f3}UG[R |(2,y-gR_(~"[$,zts_TeH?h"9WbV: XE=a=q?drb9L FipgaDY 6c4ӥ}hVz!IKB[tptp.x*6tN: LuIժ & t NާȌӍ(ZK.0tF76#/DyJw  7&b7\΢[!T*`_d|V43~1*)4@L0>Yl9 %ciiQ)"N6;y!Gf}r:0%miVڌ!Met (<17(`{ K&P,Y5#<_b *g)Ѷ"na~ Ut&OBZ U)g+x= uz[-.͸1uڋIgm*n=` $OT"7퉄S8m =/QCO?S1N,-rwdךfjmGq'%D6TƒwvR.w yB:FBbSINCM,.k`FMr ;PK C1߹jk7Ȣ'꿝ݶ6uVD.dJy$@5Y~(E#΋0:j"w&DQ LX-x ˶:*7[^c? Xk*HM]Hn=/]%~`KL5z4/БO~tcNMy9U#.?eW ~A<8m0ZWJ8J,9!lM402&!Im/])'r_[ 5 QmTSg&g99HXpE CG8CPxm`4u,1I>yL~Ô P#lm~}u@3eN`L/[:Q YZ'h U{hi BHB8c.ɘЪvCSҎKHΆ)jvln{U.=6 _U. 0N͕=X{= 6e 0{ARXKU؇Lp>\e@o Ps yz*^&Oiyz,%vvN4jXw%xc2i湱.0sd~t:sn!ۺC݂!0`7"mɎ^ݫ7)w@ua =F{ӢRnz4aNSmDʋܚ8~"h zVυBV<;` i,]EuJvtԆ}]&w/udI@Ub2* ͜]H8 # >NE 8BjE:+ CCoyL!5'Z ^ G>0憉N k[ƎkYv `ܨʙTG-^TIf i!zU5[m1a>ڙ0/,cr_hoFq$6aV!#~#bkh@p)e$bc0KT5:IaO7.iJ8 v+6\Eb`4 #h+CHI#WūέaXg3>5<<(E{ԽF֥L%kŤj8\:ʘ됾P]rf J7X  gIcgOe) 9z )G=p%G*Lf@1DD0.|uX#a*~ٹ?yB.Wow'18vd·V.q74DED>x`v6,¡ca[|͎ntmj:FDfvH}BsK줸@eG Ȉ6UӢ" Y#Y_L+B"g]~b|YQuD<=)t6u?ϟ$U4HT9J5_gNN#.3h&6ԆaE]|6nkvuV&~YFK`ƗM[;}^)?O5Ha"ޥ|*Ņלb~:2P.AǞTRQ)h*BgJoO%9HrRv+y&H0Jy[@C)3p)#ӊVke .K-\ߟ \+GHTa[эJg _>B>=օOFK&0jc lP; :.Ơ Roe%3l9dQQY(R}dsbmH{~Sؖ7#"BLj6>\|ǂ&; %R}Y~HB091s9L~DxtG;mך>_Qsgʯ 8l~ӳSK1Q=֪gu2^X{K%%^R*Dz&jCj}h^WfQq$RUfS-Aay0q36N-<;6{ЪXp]:I $}jG4WR }e-_NMia(p} 0P yzR2 k7,Ec.ݭyD8&Ĵ hO $CcYY' TwzX$:eڹYtI$\b>,sAVMVohB̈ 5C#f0 3zh.Gt~}2XlK\՘%8"TQs< JYUVý,V"$;.1ٵEJ%}*5 #wV-me@=㴳02_Q5/VbD;D"`N=y5azUUV`bHWk2)מ& ʖ1^%))2}yZjf>}wL*%9uzC岍$8eJNyY PhnH :_#:Gfނ!*  QEЪт$Y׹5j\+ymEO0TC:52 r̐.v+^a>2>HBtHg5ڪ/)/mCq?aDNBfb3?){+FP7RdTp&DKWFH)f6U08|O&XL+׵1eJfj3=ba`|:#ZY]LkbFk@/);yuIaWbPcR@@GdcFzR^NIp “ABI0tx!lcyՊ*q5uU)Xcd,L\瓢Ⱦ3_P|ϙBA˦ԭjhLxB6//6#l<,EMI1'jy%A`:Õ!m %7)m2+`]̉NnIIMB:uG:J9BRN1ƣ069[5) 6=U\֠⋯,NH B^:NnoWZF1K 7GUA^vU!;$ t{}w+5 bf (,GsZ^4hiq.K%d^ cQ&wlUqCCqzOg`hld'n Jr:oӖ>aԥB2"$KRT!_1Q8ĥ`8ǧ=چf\U,u"em*N̤ɿ@qK62^"9/W#4so!J0?Lܔ:pExE3?_|pnē 2H!M ^O}qPd aPޖ'SpQ85CG!dncwŨ&`^\aCrыY[*qMl-G0/K'{]2KQ{|SP%gBJVqtZ'%֠2P''ȧbgM)<m]8H*Eih!h ) p8R˄>vvg)JjIqrC#'"⛝돐:=;e1mle\&jRYˍ>ZRQ EUKf)uU6){0%;HJJX6?Nxx<"}5'v+l +dЀ'>"E ^Ym"pEJ)`V/ {t""/)Q4cTCRQ wS_~%g/&E7K;^lO+Dj ΪkERcvE<ک&=tc$ӷUI22Ȕv62Cw6l/}j28 !]XDz)[Y]M,z)C1[uԪf8tEtBRsa"S2Ͼܚ6PQSby9{ϳO<5aFK_tkb4IVSm&}[b[]zs${7|\~K6O N`F'B(0\ b1,CjQa{dsHVɕ,-("f㬥 Cg'@qzԪ]͠㡽@^QAsDmtQeDLn8('3E(=E} ȢS:u RV@Pnim~8]ޕ3 Ď2_~TG%6$+EC чվ!Ъt5ֈl3bpv˜? ֖_Tp! KJ߳lzlU׿n9cT'a;.͆tm'xP- 갇vo0 gn =Jf\0SLBlB&%am|[?K"u jdžR/r_/x~G# Ea\q|0IG9vJ%@ɑkH~̴{^ J`,hҀuޟWX:wdZ^a$d@/Ffꎵ]_nmIIR4ͭ;JA|mtg:_ߜQHOYސi\ H.1iЈ0Oz)9o Dń[e.ŷo}3n헹{pJ9rT:lr3cLP;9ZGkVHO_o }`GdS${VZ4FIW<@1=.%lb#ݯ>\6H@!~$-#_*bR GVl_P(KIy~T4KH:K^즡;6m߄$)8X#Tl7rNH "rpt2DPv{x mjRUkǂCGPF/,DPWCZG>t42su |jZPX%bNáTg@xpqO<#&IBWIv|(هh0E_>su-nDBP!џۍ]Q_oÈzܽlSjwV=^N߂4|rCbSgФlvzTU5pX^q/aLׇ hH,Li>˴XHt&F*wZ쐀%;Mm+ x> `[`śy' +,gZE`TEH.wՂ)8D q p`QГ<]%18 t^:jgLMnQ3LDlw,-hep懄ٱԔOzG Gt 6F~A<>^$*?΍."agJ}@aPsF,l?SMk&3V Lϋb ZVH> "y|$![3I p\< hl)9fح&軆46ۮl߃wބ2^=4PDI꒐AA"]ɩս1l[ ]=>iD#9Ԛcu(6t."/>"@$BNe4Y&pGIP2#$F+eՔ*Mmaί|n rSbY`12~ )^qs^}հ6kp6- kOfd3~HP _ }Vw"saǓ&~z΁T٘5ZQ:!BpYWňTt OUBNYl^i|^ٝlkg F`Ȉ+ՙ_>Y0݁KdܿRśn"->e" ,qqA" q9ϸ5oe„ddu(Jvm!L㊨TN^Ѿ#TtHwi<*"$| 4M£ 2*y4*`5I= P3Z >\587YSQrq#|T1|m:OUc+5#J|\KjҚIz5v`v')aiEy ёVw.QME¹+t.uY_RAӭkHv"¾!Tי?}s HYƺYe=P][U 0ࣘ#L|B2\eі+`,&}Nop(\RHQZ 䨝#գȁ~OAHUBC(k#w̱?eql ^:Y9) ةMxa1GIm V[>)Ն 8A=k+Sf>>蠜Ưv*%xH3-œ&:]&Kzhb <_!z,ml>SHEPKjEj:ϓF=3"0:#='pPޥkCTzQsbNܙOv$c'J1;tZgtfNW[:?lw\E CVݷ,ھ Z@#!o#kU_ezdj-ccaB?UpumucDқYLŵ*'"!P!PU p.p?|i*W5v@ODBv8-$4 b%[=Y,>LvaGZTYхei˚5+vH"^/mzO1qQf?,\/Pzdj>S/cޔ%)[ӟY x㨓/VGGhEQz2v#k:/ -)?gfm-sOWtm~c8Mà-,ѧnwԚ%g,kU5h60'TPĎ:ݺ!wGE;<¶`m 0j]MBN`T}:f{4-,>JN,@{d#iK#[s>MMۑ$l(MTZ h5pj je HX> mpfY6}N[ N3g3]%8Sj}9nᮤn3fpt#,r] u~i13i2{CI OD Y0#ܦ*0R/,lI{Hssi-ѝcޘ}*>fh%QA5C ub.Z-&4C/}N53n I$G rɅ@r241nP Cxg6ɵQ7j\*_{E-9woeߟt.(aZgk\C}KݯhvLh:E/{ xX!i^38;65 >*Z*ғ%GŊ[øѴ@.5larQѱyG){&qek܁<%l{tA2GVal=0&С"OE01n[mL0Cp\8@weHV0?ڴkxȱIzF4)7搟]HFH!f'=v(߱Kʘh*.EfruC9nh~n6sا] 5 ogxy 7hl=vT<]|C;EҶ spOԟ0*>leWCu@I3&^ҢC#jJnc0+g^XK%< U'2up\ړAeJ<,^&]ɜbh`sSLTɥ9-+wH31wJ\'.T3,(eK@KCiD-d_z9g3ܢe_,Tr4?{ c`~<$Ƣ?H{l*R #Uɬ'Fz8b/T]#F ed5d$89}V@<i,UJHRPfUQeJSd^${'nݦN|H-fR$Ă2EypGW{ ŧKn9 8~GΠ<֨k4*6n 1kO4ʽGre{![,ZC_l'>wDUon*zDnrBs#kD]a`#ƙK`p `,_fM[D5ZU(}Rk%J$J!wZ h7a⣃Y9͎@(=</r#rXu:;*'hxz¡ɼN{j‘6;ov2/}7Aa,ֹ95>?GwE8_>_!;1 p0ų&縷S.̶"i͊MeU |:!]P"由 \0M/*K՞QՊv w`Wg7.|qڦzšىvSmErzS^lsOINd[0~XM{[9. JQ3Bu)#Y˖H/ڝѭufd)9NFeaQ YX$r|P.,kʯ>Fãw "ޔ1@@nX9-]Ȏ7;:x.)Fa*.Q.E[3;gU U< zh={e}ƶy0 ֽJw dܨpkJPM?C|_Pq@T92#r*yi19wv 2|ߐ?,S;r+Tƃb?I>@{W 'l{k+{#QRgsxֲt&|߫GHfͮe(c)eA3kĸlWש3}-|%'@7+\3Ճ7]\&8Gy,DCK3kI9,,Ö@'e :}rCM{I];joMҊY} +6 ErC(FX"P5hf{I'\3J jnl[&9VٓXӔl#եZ]f"q},sx͖Ì"+o uhRdЭnPx m!vzqqA׉E0v~_1,o,֫ 0:zOA~<8JE`8d6E%\'$,xhA-Fv'TUSbƛ~ ϓWzop!|RP"zUBS) bY~e=NL# _3GAu8ͧb6iNrx)@;F\jiq _b^fﯻ\D6ϥB={eg;N'5y ~`,o%*aao~wlD8!&ÕqӅ y{!<(YۛvBs}2QC3SmWxAN3H^lSMJ`р8gn[*If1Km( Eᘥ*Ik闥VxiHhˊmD@HɵgLc7nq;_@dbDm3->3[>H?VLh m)c cXM ,PF Q>zC[/2+7 2gDDS>eLdqs>SGl/Oz*:V-,ַ=N;]V쭣oȊzrPHIk,*D(~mV,&eXn+Pr i_ S^6_E(% ZW\ǞphO?BXlp4NW4v7ۜӇ6%*g"驻/Q3b@B}vѯccl%ÄHgOqab,WdBi>qzL?_*bړN(V~]ZF+E >.Aj*X֨<i; gb d.Z2oF\ ӡ=%9*u nrxq. sAzjsp,JFqF"):j` WXPC8ǻ?_]JTT,!G]EI.WQL"f&ͱNCϸ5Ta,> 3!Rٱ7bISe#SZ{wggv7Āy4i2H7)\uUAvonQ 0EIPu-cYc"n{ q[ͣ-uL3-;SnD]R endstream endobj 434 0 obj << /Length1 2312 /Length2 16981 /Length3 0 /Length 18331 /Filter /FlateDecode >> stream xڌPk wwww;CqhqHqšHqޯtϜ3Ir-ֽ$TdLbNfN@7&6fV~.;J?r*mK KSwۻ `qYYcHzZN@KW* 'go{|Кxv9ZlM%S7K 's[K7 A+hlcxں-]-A(M-M ic/).p5-,A 9E%_Ɗ2`96f_l;;9:m+[K"#h񗡩ӻٻߥ djG¼Y h!h tsE>I[{߽Y}@'O-/,Z@[wK9ۼȬ-\<|K _ 4-V%~ zaokeja p[S `ak0".~?@}},>b-qEe 9SR\ `bbqxXOD9_$޻"  3(;ϳ%r^]_Q_V$_?zSG[[ϳn(9oXkŝ,N}CĀmͿ?hjׅ`bce?3T\oJoJ)_ 0LX ^6x'r!u\DB?"X$ >+EbA?"gW޳+Aٕ{v"{v?=]hAhAhbbb_XWc_~D,?ZEXY9ims=/;M9rvyXlߩsG9[>пw;Qs~J,4Rׇ?z Yz9׻= w ~/{N'd[{$KпRϾ{~൴4GXYt2kz_'Fɴ7)4GNrDMt+2ڏ#E{#J{Y8I}fayghX1<ߋv=d;xU쮽zxĐ73Ծ⇩Ke% 1%VF].d=+cS[cx%*Pwf"նG9hva5ξ"uT ٻGf |r > ԘS4FA~lq3 *%2SӢԨH;Ubɦُ,j->UL5ZS9eI!)yƖ]ɡ7y|G{fX0]_iY8 G_2J%ܪgwkS0̐YvD;0m,QL=kpOƁ=/&r`WzWm]c>1`N?t=XZSr)_\S1*km#Ge*ѢAQup#[곧0 I5|]+7s4R-֨y88ry>B ^[{!rAh=,bG?s\9e`F)m>"Rjy*k[i|Vu/*8xw}>PgbsP2^CeaP+=ϔ瘟ɦɉ׮cB71a^o6'Ч0b@ğ{!ʽ=Ӷ؋v/OJ&\ń+BƢW<%%(L2+>caգZ8jat̔t=8_y},!Nej*Gk16pSer#a&:f?LfSO UVwRwɼ%A3ꮨuR,p"V!|W#G`Cf@$n[~M䖠qzJPgoI&t >s kRg.lSMt8 FDMj%'F^BS! ״D#7e艹AR*65_ pɯNGzY+)aLn)ɹ{PIUu~EQ*.+Xooh直k,ߺ@Hޱ84 sH|3!Lt=O XM7Ϳ|]#?kCWyL<NJD)(aw )C۞Vg)A,OvB76 [C?r3 1͂HVgq(=,e-I{Yr^6N8/-jEFTMFQ'?0A~C2FY/?\OR C$-1eAmIw{*h&Jʧv4=?wint^h'(D ]AIĸu%IpK!m\6{e5V>)V5C#_OrI<5sYmΦ*u쐧6n7*Me[ڄnj}^'hCQ5}h؊|#pV6Zh̉@*(zJ$/B$ coӋ.WH[@~N3<1ڦ&Aͮ,i*QW;mGk1x(}6_V%Xlغ;}HZKD1fb(c%ܴƂ=1"ՆUX~jBlb FyH'T3_rgت`KGr;]N]W%TmI3! CXTw$F/ n ž7Sh;#O/)Ÿ1fm,4QdU L̶v(oABt",%w|YNZhG+<5y\;x-;rb>Srی޹2`LF?7f i4f3yۂ_̌^۩.9ъs7b pJڏؘ-^ٶwFp60QΚ'<N"C'bCyA̸  eC:iуo06@ L49TЙ<-Qp(m  |PeS 7~Ǵح ESǼ0+< 7V$_"܀RʍHS KO$mZ1od'}i@+%{.ĄR*=$95c t\pE ϦRR(k=%GU}m9ǕS{BPlkj^^U3k!n Y 8`/J_a-Gw>C(X0g^J;S-f" zOk{ ydf1QJExtbaY`pwʋ=ȳ!RHǓ5m!A))4"w!'%9cHoh|nUрř_h*m[=k64K>Z95>S]v ?3qd=uSJ&|2"2_Ldi>RD'mB7_M>v!k Q!@pm+}TBw@o\_ߧdqpz֝a;ɿN1Bmx!Eʝtm?$c~HTM@EἙĘt!Q Бdsn#]kV S@1gc IDKZ I"`OTX[s +(4zJ֯g0Mj+!G9ԝMηn&@MEͶܐGv&8L؊ZFf5 -DŽ_*_ѾNs2#]dže,]gb=E"pC)9g*;`M| 0")g ;B.UHܢᶟrH[̱t̰P9|k%YzQ1x&Ȥ(G&["9R&8&hlkZRmoԝt+ VRL9t$xLu aSOJGcaQ=>0ݪ#G&:0YfY[ ’*3L: +vC_/1~s>1BWlc6a矧vONGˆ=p#uA=Ҽ;F|Y-c,XI9KYwg5 m'r+~Jn5{KϸN1.o+-xUm {YidF">4EX>[שsMJ퇈ĸqq)kX7p5+U^eVQSE&&+EyaC 8eKn+[x ZJj:N*m)w)UC5bI^282ۊVNE*I(a\Kŋ^N,r`eYSQuX'1ohy3,ŵ&32;~8em{~Pi!+EY>mUtMl $іNNlbZ\X3+&MVahTUᅪC>,4ߤWI E'q~[Ԅ}p e$(!b`]VV%mw͵, fݦ<97 |vt00z# m:WHDnjt1-V¢8sӒ~`pBeV2\+xXׇJ\`8Vup}~.cHh#'Ǔi_4]h.1ƶt|)&~~6{彈ȖyO=q/;hMNrѱK)+I]WL(ql0y|iv3N=C*!mIuv㩝d?hJw/†Rݚ;s' G 3gZ+ &l4Qd@K,`2zY|>Cc}.>wW/e@2I8XQR|,"/ֶ=!&DF㟮#9sJ ̈P݅'#) ٔk tD}bTƘ4\> |Dkwo3bf=(lkdgc!2FoAG\#VLqNBN_NbK|M+Iؙ8_:_+2?'0^_zf= kZi|r|فξYLJޙC%e+Ek_s5+n{x#S"2qlr3&ӓBGi̡^~l9s q I 6q7йm=+~0vAO/WnZإ@uwϊ|`>)wd.9KS'T:ah+CXRϢ+O`SHH (]iDORW$݊Ăٛqx~VqѢ>7U+6Vgo5dDtTDN5%6^ퟌ,j"~g!̚牭d'U/i:j*UpQxlDc4(C?dY U;928 2Ycr/`(SC/Go`c,a$~E era9禮 :X|U*Q a"+qFV/`y*laAVk9Mi>2 Wy"QEaAB|YU˪yw~/$wNHtgTD|Z {1wg1h)^}TiS̙bAArbXM=_\f[߸ Ͼ7râ&`` ߢ2%':$Ud 37ՋlcT5,MDDo.ީ ߬ o]S-K,vroc-&6_\qj6,U?uRhPC_jF&r}_#*;`8^qB5RPJ#HMwȍO=:2+&PI2ʀH^BI#EmT?QnWta;g*݈m(lã 0=4tQ5ZEtpD:][ʚ4Fo=gY^>Bd<>6o wo+x)js5l~ՙcԇ:u0!=&𖷅%y oGN!F,5\mfȓJU.S &Ω&N"e'x7r]LG>Ω n#2Z'l+T?K|2uh( H6أBOW:213:)|km7/Ӿ4g_L6VADT7qwI@.TLPȴ&tX፞R50)Eքs4=!m<▻~Yioxd_OބB^M&(dI/7>9g%)I*HnhtF {fO'Hb"[9ϳ eɩgʆV"M/IM?}9:w/"!ݫnWxs QRK7[, &EqjjMM*}Ӷ X8R<#>@K2&v $2^xbqؤ+~-aktv<L|6+2;v @;!X(XtXjB/>}gM ZLyN$&[V!xԁA[xյ5R7J3vAq#o;&ؐV]^XMKh]ZZn κ+,ߖ/XJǡ?׮Ivخj|VN_kmgܹ_䬫n#oO~9k^|C|517WĆ* {sҎQV i & W+چ>GLug燐]o4Yzu^FCU+Dp R#=GV~tюܑ\|=b< .2QLK(N3_BD@%11O;6<&ь7/dT3z]l!_7L9 1H o?[ 4RԡT|A Xw3' ~j^Tg{EVu wU CqdrI{mݿ! ?yc+Q^@_veFGq ҃QvڲpUTJ+:b>W `L q/E7baܔMs;F,:q 1~#<(`pS5>t:Tlǯ <|fղ7n#nb(duۚ m~8nB"g3dJaw=ÜCkKvƪK4u~o̾\5PS@J>k'm8D9c v|͞Y~V ixſix02gP5 RGlmZw( "ˋsFƆcֈ Y& 5,if:'ƒXg`V#㟏L*HG_NKa zh⅁+b0lIX/)}0*K N\}e"q7f0̵<ͶCsNb<{6+/7ѵדQ.WBh[|B'd-c0si<ҹaf9$̾!lCiU&~ ]Q}JW~;$_sۓ)\sM>P]ANrzEvq[ǤjTS+=?!`c-Qk݂ nt19URMO!v-+VD.e um^yD{:vL%z#׷c(Sgf}\*w[2`sLeی}/Gz\2ypD]|jJo~Ѕ*:nmL֔.؏ #{!ۨ0PCOgc% ic1܄jkVa_FaLdy `<&l+L}6OJb)*(Ɣ}Mtjd-qe6ܨ kG n;i}5K]{(e̒Z{TdC#ڙ[3Kbǣ_/(EOCM7U*k*[Q44%`'#5XvȱC~2Cj0I1g@zs@ˁđV׆-:s 0|OxF?=&h![l~gä: QAjGGs+N4̊p<*=)DAI3c:HqSlnKL_K"zX9jz_%գ4y`}m fhq&rG4~H3pXMf2JGv$63}pu;o\ 9Z+ًSz; )CUͣ(^"nc(3pvA?E' !6r~4? '@Iv:KPO4'[ ӟ%VgF'I4Ʈv(Ż BaR`&v]5J{VnC:/?bvBOMMX%Զ͎fD^Tħ^d()iަhb㿥L yncƈl!`<0?<0I=/깸I,^=nKfGm'L>\qDש#ξ8ŁXιpbTp Gg8Б(s{34]@-{bP} o}ZGA~J&tͬ0Ĵ q&\0ЏW7߬B(2<C>S)sYG#zImiV<4 ++)z_9ٸZ ց5>9>,q,#T&DGGy@VxLtXN3 X{ PkyL\)D@s,Է%zX3=>è mt"X]-K)AR+cit2~qRpU&,&=B⋨e%v0'HgOy2 ͰY@U{TR#o zX(xYKg?^,Dbj!=)Ru?&g,?=GaN^a12`ezX\r͛ɯEdE"{kF%u@P09?BZvۮ̽6A͝x#NTx@iPbL#N@S8q4ZbIWp\TƲU2enJdWK7Μ67 2/;цLJg5,~ᄓu}cE8O{˝Hw#P/.LCNI?.+kzΈ';#H 3fvpR$[CN8ºIb~.@E(t8D6)י/OWfL.h7iEF8 )``|zZ9Oii JMюRU`7$SW/['ZH<']d_]8A I9Hqz^5_b7ڵr5+- @@.F%'q 4u:EüUl}#ׄE#7V] fI9o'sWheC>(Vjyh=bFcNt/jܱ3,?=XU1`^uxƠ+ 3&ͅu6Jd~֔=Ey )nU<NpӎᅦZ2z2Z,#|>6"g.[hWˀ֞Jo܀-u 4MX]]aG1PK_/vbak vEذqI!/e30M~?S-}ӆO`.:Ѩdl2)tx2.9tQoV[2ON)Ll\onTg=D֧3Q1Mn=l/4)S6Dܲ~l,_Gby􅸡/)T 6="7`-ym/Wp:n rұsDb=AbJ94z?႙x'Ѩ#W@5'u0?<}s%XS)v k A\f^\VZv Qlr;Y-- c?/vLvm4u,nּB~ @-ouVRUԘ">C(dJPm޼ǚ~r +;j޳?:i$VtH!4NأZgJ;i<N o'+%͂sjIrrD}ĸ%fȟm>[⸷){9MboP$bqkEw ?^Gґ:[9 hmxD؞pN-o'~g__/d1Kz] ^ ?vP (g5|G tC^hQHI];JOŵ#!m T ,wgu]a&Q66Q p2MRO: vaRxҺ$4M\ r}Eg yI_f[dڇ`8V&~7k0IZsdyBoi 5L ,GNj) L4~dw?|ǣy :0ggk*S1 A/"x+.Y{_r'̖.~n/_-=l|ěb? /x2XϽ!G)#-]`yCd :®__x@umG{C3Jk9㡪K xT-2T޵n2g.Ӏ*QG?c)$įiCIB! uZ.H?L6,&H!*AdͶQ I.OEoN\AR0ح(>'ҎM*) 3@-ksJjNThG;QɆEb5EJ"Kg%d=UCl>$#pƄUJAVЯ%52>p\ŌF껠ǔM_{KHE@Llts=tH$+xcwRp@av.S"  mx>}wZY; 4,"F1lsaԥ endstream endobj 436 0 obj << /Length1 2605 /Length2 22178 /Length3 0 /Length 23644 /Filter /FlateDecode >> stream xڌTU %!]ݠH)]ҝ!p3ub-ϛ[{CI(jdrrtgdeb+XXؙXX))5l)n6N|MA2 Sw#@`ccaɕ aicPd99)ŝ}\mAi1r3uژ:Mݭu'sBX;13{yy1:19Z 2lܭj@7'`ʘ)6n՝,ݽL]p@ eg 0 px dPR`rvg:Z24wsz؛ bn U <7sWgw7&7_%2 겤 ? W9>O`iha gfMG?& o̿k8R*svrXXAL=wW`ߟ"xVV; he;:H j g+ϿOprm|Uĕ_7`dܠ+/?(hPW?kAA @7`d1b|_QVKMGm`ch=Ag:k 6W+n :QG+h&e Pq7{7Px{Gͯ 4_* RqrL]]M}A!N+B-6tr5Q.N/߈ ,q#o `q~#Vo`̲Y7q@\~#E7qQ񀸨Fj(oʮkFZ(L#_ ҙ;ك? /o_Sg׀ `af Th7qߡAͰ A@__66eG6D7;P6kgk Tu? z;2'tNA;&rvTo5(3h=3{F8{$q3l.N@ 3`o^4G_cVVP?kҿpB@? 2w}jwkڂZ(4=? AMw=\AqAKN D wg(w3i\x<"æ焮ފ nK܈,<6F?4NRm_(9OQCd_+SŃYޫOSh^=<Ϫix8YB9\2wF"8: oٛW$zxR? wsk5ln]xxzD7ST~br8 ~ޭ$H Ll6j{?z.}e)HS `|iJUnHtRlV6pw+`I8em]dFo„V^t1+|DUc]@YLTe4Ц~?YJXXx{G 8  mP1`nƥj֙qqQg.iIS-{*i6bI҇D!ys E)tBpAo1әVf{"g*I,ASȐXXDmu֡EWM ·_ՂܔEc!zߘjy:|N@|*P%+AI]/(d(Qڋ!X`/4`9k(sV ɍ /Fq_Dֻ0=M:2Mg_kP˂O"]~.Hb]KqcpdhJ ; :B)=|Jo#IB`AwjA.]pu hͣ7{?򧽥 U SΧ.L+|{v]DKg g~ -0G9ԐC C]4#5ˤ(J=R_8{U7L"Gxɮy9XoR V^dR ^?ԣ)_CT3LrYY*^??,|^^{ C0w16h~+o9+A{e$~֖WyC`6ekfn}p_͉) ]΃ 895gϲNT}(R3ǵ:op pw~OƎ'x1+ϴ*{4L̀#M4Z\OL: <1 r^?$w:>P>L SvN cym~d3t]? Zx9I5"-[Up݅v-I!U*8r_zQU2|ϓ=Nޡ}i8﷣@dT4si*"S<9r=Y[RjFɲ Mh0e=7 $[!(@'Fxh]=H yۢL2\,X&X8ڢS2Zw͎K"1L6 a$ŃWꂏF_LHDY s~6gm(*.LJ=C:ԼNIAz&B-j%hk&B1|/?WCn..?YE%T=TV4յjA=gX<C2~kucK8z{?|;|%;̺/1jVbxEA3H~7!ZZc3HuLy6 <?r| Tnh݌N,e;"&u*CrE)- T(K!`B"hEh%&AJR@X;QQo)Aߧ틨uw'wèXB/xXm,M{1)ga/nZsgEt`(V_d3J7W)q %FɲHw鹝 Ju8%VR W[ӥ6/iE?cLx0]yПm/=^ _07NQ;ׯxCdkJyOd^⛼|nѦr }q3ʝ=/r"n+\xR`9׊>WYUM)E//:MzAXS),xy:OEN#0PVݏQU+$$.iuXdeK5e\IKZXEU)v;x7(pjZbe>ғ!$"0a9FDkTl^{l ^k'ʼfŻdeƴ{ טL|T?N5PM!Z`0.V4 +T&ϝPG5ZsHTq4nQ7J~\ \S%bF>kE&I\OUdEviÝx5=Kq\ďxԳ+=?u\f׳  hE F&6)K5\e}q< >5Q/6[1-,ouڶWđr͚I z斆3pu騷I.Xy)L/gT\$I;Hxa9M%ӂq~|6JA ѿ50i/r|ק3dӸA̷[T~5l UBTi1)\|J),<=]uYNb5)oHs=IY>H"?UJGZޫxsD`jl_&D c\w)c@6SsϚ>ujΩt[,-.| ylEs]鍥ʀ._3"GFkȅISGkӄ9|hs]Ч&fD&fjyɸ`A7Sj}l<]{-JAPmIFR!iwԳc *mࡪyUV }Qwْڎ7ACsLK?D]QOWCz*vUfT@rL6mdb=oBaB^,{>^(S9)(h+99VѨoJw0IomX+.*S4[/P]w'I!7󰄌mT+Vj:S|}}]Fݲ}?Uz=DZ˩)lσeDe^;Ϡ`<!RZLkEI}|:~c晓{_@M(̿d `_O>)&LGݧTt%|yE)!xvvlTy+;Xˠ-BUrx%*jqk,pugӑ?DRjqs߼7i f 2kWi.>ks]'Ț _ hld. *GCL` xl1JEo ˋȖ3L<\#F6 Vjifĭ9pKfa#^YM {kQc:ξyt`BSh]I˝]{3/ɨ^ڃX:i ŝ9ne#~t~GZh' g=*+^k NJ~pCsEVi[~jvYWs vKu]4/S'躮D7| İjkQw4cƪDLڭ6Ӫ6 ^rW8}\^dp> JS/f0-Xb(Y[TN*.k.$_- ͉Dvx/5z|}%9MAK.Ap{|F\gy>SO&eCH11&_G%{OL  }l.Lql}y* F;nur{H\o/W{ =VeęQaj \D`XAc^t9V%9>`d-]2 %3/k𝺲:3y(U0%ЋRU3 H1du!fؓQ!~({QЩ!'S*49y[v*K11VO"RMYm,ɎWV:ٲ2g rucLG<uhkNnEkYvjzI@z/}]&]p˳A_C- nB+xپp6]C<>VKɆsKKc/-u\?JÃqoD"}v⢈-ɎbGBk&pTKBn?M%`\5fg@cHt"89U%:lCv|l $>$֛.#5A_(,׫jd&u*7y;+Q*[3w$Xfoʯg9DZCz ?tnw7>@r:}E/GePư1-{)3;v=I%w*(uӽQzC{& egOFS^ zJ-Xwj&ߞ iEӗMN@l0HX<#švJXh4uXv#ΧQsInzSaatIŤCbo) Q mAZmo|*$_(s 'y9"quMs R7T1>yۤPpV-UR/Sv*yVbج!_85¯ٗG[O:: Nߢݍ߂Q@:F#7a' ~@WE<1-Ձuct`/.Y]qk~4xaZ[ Ms>M}=-9c,y]&q}VռXY\{JQzyY-r/L:AƶmK j v'gLDnn'*3d0ܝ[9B"diͱ>`cYڏ0yi?\E#UixP1qN=mZ<)whO*W-+bY<,4\R>q0o0յNKl1+f>g # Q`/^|p]G,i' ؗ+`~ґ6I&eRʾf"}.HnQMZ=x~,r:!;djQAQ=Q,fV-gRpfN#nabs$#Xsw-iGa_|t+U/ϔ}yWaO lQ\ֺK3zKw8|r5K'.mFl~-ANrxQ׬G:!{詖lfsSi/<1{&yR7,h+{]l7~xzPskVR76OģglE5q$.wMRleLz8BEv.϶yƛ|pB^P]=Ĵ^~ss=Ex~u4 \'a:j ІZ-ftDdv7~nyUz[ܹ7M԰e5*]Ej1pr!?#q'e!ؿU=P~]:fd655b$-G&2A OアòsmL9Y]SQMr!wK7{9P?uEI3 .682i-Q7yrzq[-]cKiOlf$he[K"Pz@7lD )u3 C,絥Y =_ɿ,T':@㴴ӝXXyո vd˴D竛eiɽݠf{-$$(SßkS}}k*.Zh+OL  !Sc6ҪQ+qh+ՌK_bU&(B<-ϖcK5jhLЄk.SGm7W"TB~."IXB280Qu,.Dyo1~R! tJ:^e&HYP~m&Iy*4&NK$'n"أ%xȈŗmr]Wvi-~KTG4kD7mpϡ(rx64ݵE_ \ $Rpz5N"x <1mtJھxָ[ fj = W(oM aࣷ "?`Yj Oz ^1r-ycV4Twƍhh+?yx.퉷nZr \^at(=SI#N>o(u*Jh\d`@]`;j#? 4mB܆7S1:o,ó]Hl"OFr{R9m\Nsq8;6zov4ˑidgi^'+,cR$TOx@rL!bh C*݊ѐ vi~F'Me=R2w/P4ZͱˣgTuStSAKB8BY#N6g| މ%겷zH-4MF*"3l\;X42mn ŤIۤ`]BJWE[m;|` ?1/;=ɍ{JnwwY4Gy(rzu1 jzof{ 木 =їQQ]>u c[hmLpI(C;T3%u#gD .ݍcB"MKoyh%ZƵ_93$\NImFQ]d8su x-GV7WhOg+)AF?x^]+#LB"JS n_$MyX>L"t'׊]p8?dLHLG@MC(R?ᩕ3HU6 B(6Ov { 8 WUl@3@+6j.m@. \R[.Ze^N!Vc4]i^W=$>QX(W/c2v6Iw55vTQ`Ă 饽1F]BXPM2:}==9z4ߴiSHǺ jQ-4}DSzeI/AJ.x]3籘zrE8 gWJw}t38Hz(xF:9T=tTMz;"ȹǷՐ.= 3z|4ZaV/b/NxTRm߈C FOA=9Y{W')0+GkɎ((IdX ;>Y®O7\aEŏ*$Q`߬ٺd^ZH/aeQCRQqRYj^gWb]8eSЫmHJ仫C.(fč È qeY4jG^'K`XYp75 +ZI")xd*ihdXRӥXrT>↵gUI[Z} <1s+"Bq,7t}oU%ͫ3brS$:2DddD`ܚ/9) '1 qBpDQ'$EDozR k p?qvjY() ET.MVw,&ߎLo#n ?(򆄶6$Z|OR`|vլ9j~ʉ@Iٯx_%"@3ͯȎWS(o83|#cjScs-I Zw Kk0MF$eKI A,$lo,L1J8az8M1fDGgfVX0t%} FzX_]9X8g}Q+L &nB]Dyz"XvP;l~;ׇ*#}L|qj}F-k1펙=>@ɍ#kE\7"zoiqL )+Gw;I?6?x ʐڞ̠oC{ HN#"p'= 3Gz N@*'S9j@6ѫ_0qcN~Ŗ5,=#t<]Ha Prz{[lo+:D FeT -W,tW;E#+KD_oǧYNp+){`,dB4gIWKbGSm4:6[W2e)}H*i'P4==5;3=wB{7Β]l9c".-IFE:lf7:0WjʬV2h PZn<q4 'S6U]o{vg %`%*旬ҫh96EJsgJ7_z2lȝ&dLOezJ~ Il|+|O}.9QQW#бmN(v3 La'fɥ1$y41#V|CA%cbhfNlyK= Qa!I[g  /Yip~}َ Imj: ewt8]Gk8YLCKR8Q԰10UV 6)+'JdE$7ɰ\s؜iB !MMhb5仄n8.ZR'cau7Ul6Lr791yebDTBf~j+Խ(N/n;,s: `ؐ$b+=7d6WҖ샾J/iԍČѲr(WGGكD@všqg)&ovaHf~$ǵG' vh?ʶ`谩q;D6ĝdt|?7w@ MBDtJ0Z613g'Wq XCu5 HJۃLiMnVMƳeCqkL"riJz!Qã\Йʴ2{msrMpˇJ=E8PZ0c'0qAL 4m"Dy%]>$}>~YLP)C(Dj_Mkq[[P"MPSV{]H/Joh!{+S']%ew葄Og,[_ghh1c涽5I`LꬹuÚ)+TOd~ ?QO^jrqyhQ> R E7%<['*;Y}/Suжյ0's-&k9nDY~UՄÛk0=(z>tzDq1zT}-fˎtpl;cvUTD/Chcl;J9J?ϴS5S]&oCHͩ.5Go"?vlj=Y.t\-Ry)43݀ OlI~\"irCr"LBY}2c8Sb-̂w) Yb6wH- V?-$`ԩ;_YTÞ=PFaWP]e?iq䴪;YLq?}CQ E!bNk&0`>aT6L n? QZMtg"Kht]7GTp:lB&u? AlF/ \9t[yfGE Ck Q9]*a}!G "W/BAOENpSG]yjCi5Ykdߒs䉅rI9[ɳ&oOI= Y7H0ļ&k)sqQAa,j>|FÚF~6@xyZXI$Jֆ@Ԩ=ȎO?5λpw@-g?PY9o/دnE!E:YO~Cx\L,S`{PFD!RЧ~J;QBo@jSvx- 8b|N*}7NTӇp[ u FX4QwGznU2`)2\ `z.tV8kdk5LWeXNq\&OHg/SQ1#%Lk D.9i ݙJjqL)yT֋ U 뾻`2B4W )娱;cX3gG1QV9kPMa #_>鈁Of!vWCv)XU~gY,G;})z@}7MaƺӉ'M]pӖ5~Em5RZ8u|Xx S2 \Qʘm[ UHjb/?gwS ;M*&tڃm]x/f,^z9iz FlQPVzH{jy^|'H@RB5 vk;+Ym: &o: b 7$G_- |^(f#$rQ$+ͷ'fǷ&* {K-#nJbg6k6ZMJݥj Nx+NCITվ5 ^,o9'CGQ%큾C"`S@JaBG×ܿ joppXyC=P: $S:rKKO*PEB P:gS B()gR '" ^յe@2YoQ@֏5ߐYGY}|v^:VY@v͹Ka} kFRUrYUѸ ,ȠU\tP Y:vC815-QҾǭ88B;j^g^jM/Pp9bN ?}meH GR"RY* F_'㴚ph.wC_J +k-uzXiHzw.GE%RR XP1HGF\)۩5\@1^&)NnA'p7@0YI)Ӝ diLvLƂ4 @J4KZ[O)* WH5|IvX!jp+]՘/D̻As9E~޲]*s9t8ȱ^vL_&-Cc!Ֆ'VhM|31sUQ@ yLHHVNt/tI,@dG!?q%K-{2i׏0tzZ׿B? :xtkwS?jn Dqù%өw+9/ rs]M)K xqb≒X_u44ӼRNO uL(*;t d}#Ym48FZzU2PC£^30r.B1C||g)]2veRzM]Ȯ]7pu͉9I!'6 .!3?xDZ~[w%lTys+ouJ3Z+q@2KUW .&;+3nϛqCc<96٣呅l3[,RGK4Ǩl .U_C8i|ePI x?Z $HF E, 4D/)~.8KV-2fxm8f.#@nq_Q"g~RdzOś̺Qw{anpbٞ+aw.xuɿ t#҉/G:]5O--w r:WuDB;CU9s c\[a+/P'g.i&/-[QN Oj@*UDbwv^-JDdwUCyfhA)qJ kKϥqm׃Nu^⊇n t2c=\fb~Gq]L,oIֶjD{Κz'Z_T9Lv^S<4w%/l ֒?tyG>v#8{iM1Nc$]SOXVM@J10}x MX-YBO}{pd/\^qiA" L ,p"Lz1MbGA{rOdAn,DR@E4+L"E1[g|:FJF]MߧLiސʶ96FfiY2M@l'<hԪnrAo?)̙Os#f՜q6@WocA=c(: ͌vVy 3} ްDiS29À{D4UhQk(clc% T&Szoes61}$_50$.:d%z_EnJ1>>sc5 JqzjHNn`ՙ`tEgϫe vgI_C)&oQO BhA6Yӭ4xyV\O&Ele=4 J=@s3wP6uŊ&㌕BzUTVh,"͗u(\m2_!nCqO32U/%Q!BsI$z6y1X* 5d ixQN${w687OA=كsg,!:nV`Ф@SJ\8YCN6jF/KlB/kSЯF_sCi8/u2'ق}yX󁒕0 ePޢ >!ԭ*!)wPc4$GCVooD\S(mūC񈃠7ߎml5|AIݍ6FJxm̾UPY%$3\[ꯈg'=;%x6Ѵ&uj 3{RrDJHAa & ;@ ڤ`5NQ9i춝~L $gGN"6DjTbvљbbH+3fɢm~&Q)aTW`GLd`Q[6x| 5i~ bmOB_^)؉` \P@/fk5sg1O Upʂ>#ht`%*e"H+;=ůde5|Q48iӨ/]~Tt>!sk lb)P@,I2Gn K:ĴSo}xCnatEJA-dWFE$rXn=o;mƠoSaX+LT#{ח $b1 bɋ:NI3`A$/?IaiϖTtd,nTNO&J1ݝ=[Y)Mi[ٯG ˭H݆ễ!k/!2#bֆjT9yJaWʛ)BH+I: 25J&SiL=K%kait)࣌@|mYYXlIl "T 6cN}cЌ$.> u3nfZy$TDN/FAt^|^M7+)* ]'NId:h1=F"R) H%.*s4ɵ و١MLBᔮ\ȸal|Mv7fFF).>`IF?#x3W:eo|WY-P_VXJ9tFԀs& h6Dsp paRǐ:.}zM э1]? NJq4.Tv*s};Or'9P <_X +xV>ğ_׺%eTng\i2w,чW-J ^ b qy:%8*o 1nS!X%8;͒ӍPFaxwPM5/G>pLZ+=W߸:KJc3ޫE ώ k$= ɈGk$կXȾKJl4<lƠݚ!$`ػf1{kfG >-ϖ6LS^iWK(Wl_u׺?YDz*]s K biCLtz\ΗO-Ry5r÷t!S_u#gkw/XCγ# 0/ЧVTI*HMB. r晜ȸ:J/dC1NBh~^BHNopi*+"PsPx̀YVڴ Κ<܏z c´v^g7ry^dRVGL<5PT} Dn@_/ 92kѕׂ6qfyj>ɨ 4KO_U`1I_tcUiVS_ZW^aN RP6׽k/9:J~[xbCyl԰22i:txU{gBz2!DYq0H So3cNg$ @78 s_f|0)m[^"*&0ΨP65Ɩ7'H\tu|(p 6WVqp[|O Y\x6Ipցqy3Up 4)VhITI߻ҭ5 D \ 9 MloMߔ\+62l_0$r9 ,FwJ?<MW+Џ06>`TLfTv21s>szi8;)n TK kdMX>LJ+l j0G)=,ΚW5y@suCk~\L>tjO6edn "PJlIg5$T 3̱̼#&f쟢$wKIn+?GGVb@PCc7 f`Hyo"^ \<74DRQ$??Rtc^̀26VR1 Q]+mCy݄S)@ۗZ5EFx#. ~He-30p:W}=N,,iIhgdS$"ZtȓfTT"WGeiq'Bh] o>F ̉z% |7pVo>.Q8#AE`+zۚ,3Ao)c85sj̷gvFN2ϜtO->=rC*E7Y3˻z7ZF҃R$ An?qW 9_VNB {'8=".Wexm/CT9'~w萚}RHrDI4< ,K|+36Q;0rwv^OK. w,Km~JJs|=E (H-WU*m [ohDPnЊDfR/:[EȻI˩_"v&i`Ҟۚ މ<~2ZQz~̈SinLtyo6BӜe:]m{HC1` qE> stream xڍT-L#-0t !%)* 1Cw4!% 4Hw zoZ3sj2ipɚހ\@16/0t!6?ݘL`W< r|)aP +$+,@ѿap1bP `L&yba毿VS60r-1A GKCGS @f ;UNŅd [Hq\ m 6" ق`ƍе8ׁ;`b :l0v7?n sziCfH9A!N`?S\,A (,*3z jOy*icrr0WO *"7j   *X6;h W@AM{'N66ì0bgƒBM5`3FUA! {'W u 6(X x=웩{pWCuoKE) @p8  />AAÂ]+ 9>yap_7*,m>Xo<m Kߦ W\c ?x_?>W{?Z?M`+sv f*hUrY!Kµ9,9i1 ouEO`+O_&_XWd=XXf̙QҞe1sly{Y#7"2e;j]t)VwOm>*zuS4/)MWrz4G.G섇xg#^{y/V/9S0R F>#cIT%([&}%mZ}k꾩} =6ffc A&VyܤgO&~r,ƁS3ri#\-%qYJR=Dے$)BY͌i{Itq`ADw"owQOeH}/Ǹ]>>h_\3I,{0S*[FxLI댑#v"Lس=kc Zpar;1PpέOVс8!}"'wxSZyηZ * 7?4w=p\'}b5|y}'V͕z=O|/b=rkqD^¾ފ4Hْ1> "͝yn .x:x6FA^*" ^+v"2PMY/'bbK#FbJpjUpB\L"9^ٝ m7&+@θ"hڼrG=>-R@M s7[ w+5-GTe{ߧTH:gNĸS:6Li0$Sifuݘѵjc!K Me?Bk")V0#餌{Xq\d(e)]Rw)qj֣ QTbkh~#cp!` hfM҉LA(G*VO` UC'ֽTSXl~ϸ:2,"αfX)s\]uM'o{ h2WyݘaǭsƏqŶ7A +ÜDaS[iݸKpa:g-z.`3}H_jj;n6F˭sZl#՞+sk OϤ[8~`8uU 2RV\齿/k 4 |4;xCk FwAPM]@IDUH[)|fa0vi쪳9zبDRK+f5B1$]qptjJeD+yOS9FOZ 0Κ'_TQȩ(/ؙULuB:ym)M^FfjrN .}̀Jxt";=CcLs\dž| *vךbG_,nuT!r]1l{EcV2zӍ{IaUwDC̣BFe!T7#>2@EjP G~v2[&/xŭ)闄j:y\ѨGX )l &rL)Ef񤞥iL+,_Z$IzO{TbRd:;X^nξ(}tlX")M[pș˥}So&:$_?#6$Dz#_U<(";G$tdž2C%kbEw 'oAC2^{7a[ihXIiZ&v&w,ia$)q_$~H8Nljjv .ۨ(Sg-:wJ8*Q}>GNW|_njq&3mkm\ /8K#kB&ژ `9?0 "lW_WLZ];N=6QǽK ;i=$ '˖4̽8~ʚ2baYAIsIp/K}y/QްV`lrhڅj)vCo[dǪYl~aLT(Ym)Pd:FKr TggWBQZklDJ7ZLxUFLњ~>э6-kلRSA^u2GQ O P}+R"~rd1mA^/KwwF90yl$kdʱ7X0pU[Nll Ȟ~5w록?:m,}r;; <`G}u|];h~}`U n1 뱞5WD]\ 3"~.}&Ds~=,5{iuyqω縺u=IJ9vЖgwf!e1+emς"ޞK|}CT oݩ\shN9JX4oԑ8aesWFٝ$Ea+ml,$ʼnUkO0^peOW^MkINIKxt[Jp- k )f2$KQr_4mhywQBerC.h(./˳L[_.aH`yQRl*ge6>?ql4!? 9nZc`_fl0gM'vK\kW%ʟE*? O|M= IA#ak }5fԞ`9.Gc)ngbmTTĮCߑUPlN=G@!^L7B&񠉵כxG,r!UOJ!3ھ-y dk_FԸbY'm-SgU"+YW0ޮ{t߉pI4x 5Q6t2bGI|lg%2 w3 ,?JD^@=ຉ;n;  ҏS@&al1&ZJT&XqLRP}%gT]M2jϕ\8$d\|8wHjqhm&(˥(禕㚜h띹SzӒ{$MXw\OoKSbYKB ךۺ$ 3 '_ o+zAɩU{X7]lM Zn\0:[^4ʔʏX7Ɍ`eKH63bJz}^zUEZGR.CM۸b2ƴJ|V;NB" c Nň*{ruPK(jfaȵ1c?d|cGrrQrH=+D#XMkK00# U8r- ڐ& O(= p+kZX$JWMaOxқ,qiksҶrHWvN5 y3#*o 3Ǿ!Ol쿼N%9=k q=Ҏiԥ\:L ZSGݫ'}u7/:;2ŗaQI/u}RiXA.{y 7.Al[ser&6L3;80ӑb6uIA'gJu W=zRqhq5h; E뉽KUYM,,a{V7݋$e<@Q؞k;d0 9Bu`k`H~-ک@I*cPDI3 3kG o=~āfpQ@bZh~; lK)q]1u.wF4l-YlOyc^;N`57K[~"Ql%یhAuHJ7;r>oT>J:lqT#* ?^g[|+td4'Li[1S퀺fXJLp W .Ws |#P/o[ 8T>o4&"^T"?h"*Vc#u T.1j3*1/I~N^͌aKFaO sf$V^V.jp {PN#<zZ4QpaƙWK`4:K;ͥ]{ٽJµ,/J1 [m9)LKy4nayrbk2C0 ˅DȆ'hX˿hԙcq{y1(o0Z c?J蕯P=Y9Э\Y]`9`]2D]G/0<5adgasaaQ4iǕK8$.nx+7wBn oىZ~tPVg^H1ߎtXxP,%ʅDCZXіi)}#jyٚ{moNaȾjA,˵97|›Ҫ'φ 6J+c-;kR 2@󺋎`6TkΉHZYEFQb+[\X~UXW )|#~莾de3n)sE.z.?O^Ud9ӄ'#)%s|ҭExĖ1Ar`\تA^`46QpK'쥼"&(xߌ7l[Q{&( OqN endstream endobj 440 0 obj << /Length1 1516 /Length2 7535 /Length3 0 /Length 8536 /Filter /FlateDecode >> stream xڍTT.Lwt 4CIHwK 3tHw"R! HwAw#ֺw筽>^L)ea<\@ yu Pf,f= W3`>ĩae7(TGPE w@ ]e^[ׇeXx9~K r;IV[A׫ z) cC i`OfsnqCZ|.s'˙<ݝΧƷM7>fqZc[MX#y;R=4Ԝ:_}|?!)3wr!V)^6 m8gS h4lDgDY#tqX1|F^.S0Q }{-=QlƻC4A oΝrn$֬2=V@ݰJjcOpX׋_4×25_C <;4 ,|. RI!_˟^?rN"b3,~=zh#%T+mHh1!Na>=F`+RZ$qh3& gxAuoF 7gƼZ,^T%]; g(ƝyqKyG\_i]$v>П>~n;޼^K;/)+4}hȧG=o-m-|t)BDMveQUNE2׀5kBkj%-aIQܟ!گ^԰gـފ:(dSOUSvI!xAh^pKp 9wSN;6%7捤'u,^:9ag:/"ٴcIiݷGn˯6h[4v]F^Е!/MAO^b忍Rs+LBwK'ـmIb2dLB>F ѬpV,T#t&ge}B1%AL0eN$aÁ.*H ,JvGg/͔<ZH|] /eM/o1 S9zJ8=A?HNMXm`2]Ǟ5qVDXl|5%g ,/aXY+W7j)mGE9yMDBI R3 9>) jt Ә2q׺>bypؾ%/g[(3E[1!LV/HU1rzՄľCW23SК>ވ}~q%ѰÎ"$sql'&Zaibu)}&Q rE$Dk_MWO^ec;o) TټD|hX1"znf&3[Qtc?RIVyyb_aғ}=b3R۸^ =SyHKW_nd? ɗ% ,k0:/m7Y{ll r.[*9V:3'?Pb47 {Uժӭ,tW"6t1U]u5ZOBD\6zV|-3y'a1-ɤy匏zWSL/,2}S@dlߌmtrO|c/}[l d>>(&Zƪ4gk#a^B8hN7'jOz݁Yi4Ud?#U-p?;۽%7dSo*PJ wyrpz݃{FԒ)&}C,x(ݔn^M egLjV~CxQR62c&љ'MCsZpW!BC#*f}Jhef+,4BKKiT.f+8c)JoS8sLkq6{aWPź[K@Ydž!aj5ݺN]<ꌴͳB$B/Aг  z!~V ,#e$D [=mBѲo8`< uYGQ#Ew}XJИ!O^Yz<zi7dQaS ~T݈il>L/ӭ4C#$46.wlw],M4O+CSXk&:/ uO|MϷœ~jkAnPQ%|Ř8!TNlVU^ЕY΂Dzq5!<(&эqZU+?骗U~U&1Sr."s!oUV ,Q>ա"cHՈwO%ai= Nn nRkl<&2UY$LR.UYsgYku$~l $^2*JS"e#]٫ə5$oO O1gCGݶ?;IdvXp`EB~& 49I9. &Ӗ X [@e_)ڕ A36!3ԩt@A7 !z{#8}0)~ɧf=Kl崿%6{sd6kJ?_81]$\g_Z bdд +m=9Yƍ H~ZU dU[~ӌ-6`m_eeQpU00yQ   T Ŋ%#$`K&q٧_FYoT=j5# )QBu1sD#^Z ' !cp'Pxx: .k z۱+-seݳD;np?mm-<[ܫJȱJPW?d,։Y9؃riO/zse3l{8ٔ/{7O>;+xʃ)7@=:]# :;LpJlc~-/q%HD1}~R,t0YCʷK:°1?s)ݤbV}BS^vS0!3Ì>$nsgUikbT ǔ9jH3u0L{oQ,C/Ʌ.^=汔cL#ZC<ٻ>gBv7.2 uJXAU%.U Nh6Qeg}< ^6d:+hy³0AȩO&HWqODef| ̾!Sl@-$bͫc2T*lrN;߃%/k .KUwgM]8,W]]ZJ. _&udGO*yV S睲-Ռ_5)D!Y}n+z(;[ClP(Bt/q_ĞekM?YDI(HLykh<;U uOHEELgMeÄ4? yiv ]39nb6e܇N @&M0}:Vl$`fʠ֚>ʍ%wT#KmkV yw0j,tmk|{XhҔ6Dd._'uJ*IU5nI/@lC>0'y őf$:ƫ1!k+;;$W$K24 }A"UkRWoh;I!o3{WsgglGp01Śg 2)WsQi>okE_-JnLivvzJO&<nׅq礣5D_y u1BFiN=<"VG j3ƒs&o ]Qx[^v6 {5ѼRn+^=i( S jvJ (:t"ú MZ~;As5cpPl(hN`j/ӜdO>@f>lKt9jMZsE:#_M|{ؾMq#FzH%Db&p65UtYZBאͿ*vAT4CJ4++25|긒9Y)v^.y\! wX $$d'@\5!cA(k 7^}ǏT'^wi8_3r*%RiyCg-˽R3l~V:6&;|A2 vF& A!tB+1 B;ZǤ\{$&j2=-S?6Ew J\ed#1H{h%), ̔?NYP*I! c\X**KuG[U˲N|#[944jS811ʍ:5Sv <1#(JsIKsw?QmLXDås6ڙ}%'seP^=4F;fYZx3a{[9q%55Pj6p,@dT50>hCt[HLjEa$(pgGuX5)6cO^p2Y]e&a-)w(n88VҴA/>X$JS4;Π SKyg QuVo+{1?ŧ'&g:4WwǎbZ9^16|`ZB @?po.a;#!iT-Hn6Iq Nj2,fc~'Vvع$hcJHZk(@ ar$|DDKb%]Ѷط3F{Z0yK LN #EFW{?<^[U:(}s?P# Mݪ}ҍ_Em]a=otQ{}]6{J-ׅ ]RB"N՟<2u$󡱳m=I'=Qd6RA];:;+&Lp0>2U6Пy;傼|-WA80Գ;)ʹ d0˧k)ﱌAjTa޹UvR^6so qbu Rk^3y^>i;7;/bx<}~Ɲ Bq ]I)ȇl;agg @rc +$YB S#4z qsM9G %^ Clǔo BuX,-(۱/EKMHL-O[ A.팫hXDD3ma'qԴJ^rcMsG qj6 "ϙFClB!>!'<4SFwۼH[Ծ;!R|v'ϲI?Zc(lESV-᧗@sکѤIWRqYjO~|[%4;жr+|C!W}3 <٪U'{^4kSp٥q!B gsۙ-z)2"QYwl ~vcI؟)iqzEG'+{X  Vx4r'KK۳a-4_/# ՘{fZLrX{E vzzӼ ,`V!:'$C3 nx "byO.+f;{8ر{恒߅+EV/2NrNFTuGG앹^^HwSpZ%*bSuT͕㼛RO4oN0Cm3_=I{z 瞞Q/Rr EhM-0v 08^,;".QMŴrֳVtqElM=GinaF+e5sn|w˔aPb;6mP)]P|!w)M(k'DžUk0.h/Nת :h%\~|ATghA15#QrH4-,lDKi uK$Ot^;D;:偰SR#5ףؼZhTp >g)o.UW}b6z>wþ+ny~$̯=b8iSo<0cN#&WUuY?U f8>l$.cv endstream endobj 442 0 obj << /Length1 1379 /Length2 5945 /Length3 0 /Length 6881 /Filter /FlateDecode >> stream xڍWT컧S@QaF0 ƀnAB@B 4P@Ns=;g{ߧ{s06VqB:40X$P30@" hGR-a(o8!j(C83$Rr`i9 eD!p'@ySՐXee~T<`(8@Ю0\F(`ahB)ўr~~~"o$E_GLa0/ . L0w{!6C: ('pCao rtF0l ,;ܟ޿!P(.g; `/Ơ/C7!8߅C*&ߟ輡('[ 0&k Ԑ0ڛW}p :VXH?Dgg8'OQ NDH@ iY @]E7z~+ĸ<gXh,(+}Np(s#(Ü&c6 Я';w?濇+jc/*UU$ ,I`8@ww6?uHp s]` I_Qg=>|C cXm40'juf \n"[9P? p1Av {JqV)5Pӯ@P(7xM- Qs(__BPe=x\ֿ73H|ȶZ~˃ce3W>Ԥi5የ*i}};<`~#?MG_RL3vm<ws@07]` q!ݱ[-w'MVj(G,mJǁ9̜$hd04GǮ ]& Rm%X/%LWywpX3$eCcfQBzVR9E7Go!t=QX NJ#ƻ+# Jhy-xey}7FfiW ? )mZqX{-8}}e$~ dF*v'H>u2Ś샖ThzJZku;-FnZjZGYI2.OLH]] 4K/h&z$^d.{GX1{hW\5w~oͽvW59,=EkM4Dnǭۧa=[]<W*~eqy>/2{6Sr_6GJf )zu3` V"af~qnmYLgIBѫi{k}:}́k a$yy!!y^i1$xm4\zBSJ2 27ْ[Һ1лL FVne twS m+`'*& NL3>oh9+ )Min kfKD]ͧUczOG 0 ~eÇ(Њ/+Cr`) Ɏan]d|k ֙ΛGZN(V[g~Ay6ukFɎ j䬴C-ԥJD8yo0ί*Sbb"cQק~r{+MUYُ\k7.Α~L,d{!>܅*ak#e_΅ęk<2,#S; i1x}#&$by6W?>q ؠ#mVCڜ?VWl[D*#iXy;o Uzo#F}v{!IrFB -e: 6y$J!IxQaQAZީ0ѽ9AҗM#lsͭ3/ނKX,kilߞRTqu$Bs=I5PQ<4~ 6pqXy{-FM+~^T[bPRڐT]~E0qgb*Oa')7ɤ3z_C< LZw`VaFzmquzms8?+/RmS)-{nܩhSQT gSܙ .',uķlEOl}%ì(_x`sJPvKt>QI/OIq$$> ۾W5-*2m^Xo粎ꁪH,.]K2vS~7}jS >ן⥨ cJo4UG|Ч6 W\u;W4$ϖY^-A]ݑqɃU]u2ߨ2XQAwbd~э?y`ձàZ)[9|?CWKw-{-­̦Fv䩦݃8l:5'2&RaQ`NbUjxY @O?dHiƶޠWM:!$ՐJ)|rW#UZ}Bvuy#i!D=QwntHvu\Z3- /d AmrJǒ7~0͹>ʷ%-x b6/#=Q?XLNiavD^ V-4,2f?Ӿ;@ʥ8,s]ڒuxRoOm®-]R 0@XԡBf-7N!lcx\T𓗸 };toUP~*[<G. = )L)< ihˮߪ ݍ>$X"ՒȾP!NF[g7될[fbk׊_ċ#tJ]nXJEBzT^&tVQB ssX %=N$*(sPa[e)}lIAt/>%i,| /1ӱ)61фpL%g{3c^شbxOTҍ\weϙu*73|2h/4(FWnw8f|82;xckd|5}Y< ws K,fsTzUy^y:"5^4}ysvȌN8#92^ I`GwN%.iN7S֘Mm*2)6{;om?DwUEڡM fE6ff~B2#m }nb:m޳wuj$.㒉ğvM *GgŔggcP 4>$|Z! %V#}F'R2*;+K't<[B^cѥAXW]{!;]6_|":'%&_6XNOÚd1nzn׀cXKivWܸTloh'97G g'/dڏ)|ƅXӍ 4 ',rJ`m+|j}Tj_{ C}l)M*>"ٟc;V4T2 :LvZG E廒>&z+$c7SQP+p}w'4} S4\%=I%S| _j@{pЋI.rQ+p_ݼ!5{"` r v{BRL>J$b.G١>5^}l gc.}Cj69=Ssi* KT_@OJ1TlT!BkG(qcgws6#ԷK/\Xcއ)|ZliՎ r5(VTT!+sܸszv<pYiRг|P":eu?ʿ?L Y^IubKYvv1[s: m R:tLdNqMsr5zSM!Zw_!^ø\}jgu)MSzn[sMD<%ף.9WCԜ9HD*a{VTMQ#C韃* FYJM m^*Pj}}y;WۻU֞&Мj rt;_U,'J4$U>|:LjxZT C X-?zc@h^M;"p7N*)!A7`[ѢT*v>9}]aE^l$ T͋))) qYg3 #M9 0杦@[33s!V #P[/-LҞuU\Ajiq_!`zTbjpB\/at80n>BAyID A˗|1&&h.II]HB^BKrXșMLhg0/L|X1nam0.XSVz5* uG+[w{fo? endstream endobj 444 0 obj << /Length1 1379 /Length2 5946 /Length3 0 /Length 6883 /Filter /FlateDecode >> stream xڍVPݳI HG~R*(WMZ$j&*HSAtE҉H"ERyѯ{=gﹻ{'M-$0 ' ( "#H-8V5ĠEpD Gt3Ơ*@啠 J (*Z@`,`&7DTQQAw8B`p0<(bF8\ȿ(DxpJ`pPP /zJAH'`G`n/ -L $ Xz"0[`qA0, |pڟvC`bnB@lP)tF"Bp A=wc$ I0/G? !}`D:fOup,/xh7M @AΧ"[.7vGIp [~}?=&?6@  _! xp/p'J@#pD8ށP $;ьpcO< \ @~}^9{ wqF ih` )-Pii@7j Cy6?hw %#Ϟs\Dg097m  C!}Bĉ]#N18tA17d?Q}8h/鯃 F"qp?}h)$`Y{b~C(;6q5sr '<8n} 1 1XЯz*`c1<%=D0/zy^:ObQI)*MyV vW=ザȎ$1~UlCYaؑsb#h|U* Rر_ oz\K {AUw1cfK iKޱJtQ<"xS'G-Ʋ8;̒3po, _#S?驥+='˻ xtx$c4 WBɗ^v_B[׋ЍLt,Q~—Lni}i?WRR艂ޫg# s++\Lޞ WJ87a0~Ap!od G #XUG4n)Nշx($AxhWy{y] ZiFF}A"eY5ồ89nQM\hvJm~V~>";K i.wզvYӦUS^Q}N]!x`7GVtmC^D5W1"n4Ȋ%ֽ}iJgX>NsҚYݦ]n%ZmEa~m.ڴ2q=Yh 3-7qfԻ\!t+%E s3C&WNr<-8+/7{T_JҘ%e`:ػ=6zd*~@3rdL;J%$X`4p̿͘NڞGف>Igjܠ _?>~hUs׊6iU\O*H""Tnk~=:j˽ ]it|jLٌQ|}/ iĝԍWa{Otymkz~ؼyB}0<^ֲ*7Dy>(oK >xgx`dAjUyZ]*?_[z;;Uy%wؓd0*DٍC&RՑʹ7j y;@b5}QQṔѤW*Έ[i 'OV6_P(q_P?#4V!.S5*Gg#kK !/iepJ򊵓9O /12->wv6ky51,Nx}P1[Fh:K) q|L{aWocrs b,/'1yj4?1~r .S|&[ ۦ:݋xLhi|4)~^n" ܛ؅i{*e/PTa% DAE;hcG4Sٹ2A_=U@:QMydl4HJ/>$riWH퉆tܳS% `';&[˰'W֤C&_-Uhz ri?Nv:+›OLx 6eq썋,b_k.U?/[sYٕ%BڻqkRc(Y%6 z>SyKҜ6IWtBjzn= fQeD/>9gαT#y,hWijԵoz^SےMj6vȹ]WLbQQ4Vp'Ø~E:3jJ()_p wp`?`3N`#,Un}YL^ےxҦ\@5%~q]H:&La뫱P\85݁Ԕreu'MC\_C@car͇fkֶٛiRE:oñ5!v8'GvG55Qkڻ5Cyۈҷ.W3| Wʗ<;7iANIjJ 3Ϝt$ڴ+I^h|֋$"OKPk>/zHrY`ս" m}_ja#}G{YjL󒛵ZEsM1b_K3C؏ seTRdHT8٪ޖq"c OQDQ\|ݯ2 G u/TD5S@yRiV]"wPTo;N.rK̠Okʭ+Am=5?4@em;OḟrNZh#7mRw6 {&6Uo*ܼk"}F'Fut?Kj̺}olZ9ΧAL^Ä$𲄝Z>i3Ql*lmÒ]?J8;eTGyn!X} aYܖQh?_?TmKAy r#|6$F' ɜ7f #ɸ]}i hfK/OZX3LR݉3SpI,׎Df_@ʃ$⋌}/_C|P7EK]qV)d[.{w[FDš8B[gFԡH-,\Yfj6󭀭GNkcn=+-Zh7U]ܴzyGWdxE"+;g=Iۻ%+fv%",5˱)stdgP="em`1WZS:(7-8jLNs8TW`)IIKĤ*YsƕyrUë Pؑgb zKBT^TE|FU?]#ˢ8E4& *nu:(To[%\X'wQ:S*2pf ԣ}LM@:MἵƲ޹\[(8b1\a^h` `,Պؕ|&%!\x+_Bn9*d,Wn-0(3# <~'1oqg&}EeTO"NegHţivK,՟MSZvw92Vl3yTL铋p$F]RdTZq|{pFG¨$뛱KD., ݔcLCJM|_ױS|>SZ:S`y|S31X`%?%0/ÒǜW,5#]7_C.X3JX9d>2ܓR.űAjU6zzK2& Ltd\V1A+3CVzIF/6gfLwي-@T6+ѻ ux̧PZvv6vzDZ  =jGGbI.YگԎA޼NQE/5= ӊ$7;)Տ)G.iڃN Pi;)1s}j`[=I2,lbih짌)5_:矀-c062s5ʄ]DBd:_\ur^Qb6ĉk[.Yֽ5ȑ>N=oߦsZW<'1+U۞S78P=MnP]|+ZO"γ]("?w.]`Po"m+j۰`ԩrՐV L2X§C紐>͟ Zb>[}GM.Xql `B?-Dz ]u+XN}^(xL,]w |vwSUS}˵,ME(|a䰢r]8?_>w7Z,CЗ>1rkzclH$(?(jj=Fyvoney _iN^q}_:;#4tXG6%ie#F^)ESOF*GSV z²Sz丠uN`0(HOװc`x /^:# QsO٫hܳjȋP^Qѝ$0ʒgu&ۤk`%L͡I U ByMUv\{Ew<]a:K{7X{蜢p$4F(җ΃Y-/K2Skx{- ~D|+K'F<]p}oG86tTrD K^Ky{oz4p#Ϋ{LN֤Y@, i-T4绖&_c< ";1VLb8}ܩ:휌린P[͚%UۦomEv[JiC&1S}}$* jWCYaDqbiU|Vo](U e|Rs0pb};2rZ[7IrA#~n{Y2^S^zﰷ1}D^8Ėw>$z H1VLӳ.4M[B$L:?|HCH7mO='THW.9?K&<]xư} );V_p LKoLLIݭcX?ݧvj1 3; SkQz6,fKV3osݵk)B|@ɁH:P,]Nj.2IA>keotՎSs7ɝ`ȓsq TGp}) W/69P*+O^?(yꅠW *NDqltFNc`FLH~Z/`h~}^#_yxi¿xQArb/`ɧt_l*άknj¢褹"W5bL '1&a:4Еۮ) F.Dƚ1.~$9Z`ݶwL &8A\3Y>eaޖ'UWLqsA]XHo yniV3`W?&J _Tf +  c/#=PTc* {ˀlFh7 endstream endobj 446 0 obj << /Length1 1403 /Length2 6029 /Length3 0 /Length 6987 /Filter /FlateDecode >> stream xڍx4ֶ Zhчu{NDc0D{ !: G'JM=5k={_{p (!mHZ$(i PD&0пD&Pw ;ؔh P<@"H\ Re'-xD@QDJHWw#S[7Pp `@v`*Bp!}#*%$%vA "dy^0#{B(t.?8F0_C= ` p@aB<vPw:PC E Ht%!~!+C8ap(@WUKv`8 {ap-{`>a sEQ0/B`ڬSB@hѯ)ܡL}3{eCa*dy@5`0&h(PRLLG_|\~HW=4f|P@  B`gǘ1X1>(TL PSQ H H .. guoz`؟GF =  L&GƆ :HP\@!,{Gpo?_v} 0z@cfCCM 6^ 43# @P0o qKK!Q_L_>A1~g]ikE`wwF(U;oH4&G:X(@#1vxc0^u( !ABÜ^(0x l୬vF'E9g9jgM)ؽ37W11|Qwnrz>Ko]P`qI0&NqDfckb:s.#rPr(9%gMg@)ub?1ge_E?"naakhimn_Qfo؋J:*ytIPXJilt.86? ےD<to>~QY>b1.Dr99ڑ&]t(ZߋK \֞Bka/4?snLK ||(gv7]auZ/yҌ%qmPO! dpYG& &*bZYd5OB^TA~^[Cyɹq#Y#mLBsp)rRJ/L/= iI>^?@^~KMD[C!a+·6:\a'gZS=~o#IAB]CxtjdwL3_vpm{7\RI +D[-Z'=O,ΤmZ}j9pQᦨ M5.)B;S8$PmxC BѾh.@Sk9BqQ/ 8DU⇧ȇBfbo}]_n[1(hE[)=h(4O~Whru%n-rEK9R=͏D=IG5A߆$9?0aa2VI=*jI> gQyEmzɬgX_$DPRMi? Rpc.G}yahPeYAVY;8Ϫq+ԫwPFOJgu9!}r\?o"epc o*ItBYϋ5:$JCT&ȺEּdTZa륕*7eN4PJ+Wv$#%pMgkV8׳®Ϧ,Tu憜zHd 32Ө-Aч1n/N(h1ܬš[ rWËIlƥr'ur)3a➤2z TY|NաHZf}kG$2E' (>5ANF\tl_㇓~YYki;3P\J>k5/^[B%Rjn\t[74.91$}/!U,n9c%'pñX`;h4b5y|dI!OKhBpu %Ydm cV}[ 0d+NvaeM z%(CXX2Z'xP;>qVNi)7"5?(?1FzuE .O} ):p@}|j]) ج2Yg[|'?ES2Œu<{K >L4X껞v'2wK=Lտ9,LCOӠ*M<8HqKYV-)ɱqCX?զ }bjjx3rwSWUf@K#[a,!>.ع./jJ> 7!汫brԋ߷j~89n71Ii+ϹADi.F@x$wvmX6XDf'TkFKjYǴOu韝{W Ǭ8ȁ W{.amXd.ȩ{7[_k@ Oڏ:wA@B礱*w3"!,*d:G>GbMty/#xxH"OKa)5dEI"8tgX$s*8xĒjO&~1~i_<>>*[G,4qr%-B}S;f~seBЗB%*[tS.T3oԝZ̊ {D>7qFY-b U>$յZ[r灻(Bqb^2aL[@{Ȳ=Hud2'8Iɏ I3[ɻlكh;!حiTެEGaeW%RO?4 ~Z6J]$l~8fM/8r_:6GT_*[k)s|f /B S(`xηiꆌ9F@Be -tAjk ؑT/tc˃Hd|MZzdH>.Ef쇒*4N2DO,yݬ&9+V0Uwb֧=۫nXV^/Oka,Jzg=a1a_zMgEIKݞ6jўtz_3 zTFaSu$+RS"sE=+  A\.{YƟ%]Y;Kmp̔%+ydYq,b&Wn^y?HF;;sIQ._XtҎg"u;"rt a#n9hBix:ì{̚Q}zʷ(csR\S6~M}̀o׏<#rSI9HH ^͔m{*BUEK8'f-zB m)t\"(IXŢclsqayY5W/L%4d=K_,Jh!Q"䑲Yw迦O%Tku6b%,b]Z EJ6O]lGI;<'ҕskr;co$׎^t;(<"h["WH] iEt:Z=K&Ij}7śuР<ɚ{81%]Wv*wO{*j,rk,ו/NYL.i ~D"d>{mJ=s6O(oi<AG6V^8UDo|I!Ҋqǎ7M]3w^r#_= _w_Ub}#rȾc魖bw±\' LN }plHlプ:0B*\WtEo#̫zf9$^[ڕM=dV0Y ?4C!RL2 1Zt+%!.T ߳b, F<˃(v Z1SJ%^O!{ZN?㡏5+#;|ݺsj\b^GbfȻ5u#s,KL{,vƂTf"S"XflIL{iԼ|1 _{s"g,y ZtͰ3Pس Kc*u!{T#wbzAB/𾏢x9;|y4GX=#[lg\_YeE~h{۟[ML3%פR;s!LnPSO.K~xZU[^l:DxBFIC%2`Hjx^xYv56KߴYշ{?Z!NJs˕ssc {;2Sd՟=WE iƤ ]Z%u)r:Uzj턜7:83-nN|UNѓg\hԗ`;Hr0q/h,ӇZ=w^G9XpG+fvPh5b-hk ~jɗa˂ifAgќyK"'krTUGO(νʨPꥪ޷GKI:$g̬WxҒe` Y%WDS8pHG1R&v#SYSSĘG&5 _+,/w1r^+/_=}b+Ք6_:Q8U9dS'8vd`'=b7eTo F?liG:Vt?V^.}|>V6L+Vi> stream xڌPҀ$0;!XwzH߫kﵺPj0Y8Ll̬ %M96V++3++; 96ɑ M& *99l6n~6~VV;++ \@ 3@D%jce ~Zs:19[2N6 YX<==nNVtO5@rYj t5f$*ۿN`O+&19;Z\org㿌eؘqhn tqX؃*Ҋ`/0#h!]: -u]mnn6Wcrprp9ݐOdv,\;G'OG_mX;h9ڸ$m&B#\ enWMogJo=:;9,X!=@;%$669`qDM ݿmۄY89{1YteŵN^_&.;+RUͿGD9GK'߿x;4ɠ7<ߐo:"iw{2lm6PrzkB+,lV |1G9gbdfM dj6,2r8Tlzw޼XYm+[z۬+hd sq@o x#./ۮZq ֣?鯋%qXEXK<?`Co1Лޢ(-bhnz{-kx+Fo/͟]?-V|s\/g7?-ޢY{;[=,dv4vGucqo?B=T,NپCV?4[n{Sho[=ox 72^jm' dk\ ̶.Fȓigu" Ag[cO-DB#?fA{f7~눽Nx/=WC$Ñ;BW)iw+i:|F٬AꖪJSxcfw(PaIJU׊MtQ]N~/}p>0ε9XiNtxpu(yPt0M+16#,퇆@vU:@z:W/3Mc.ɒ_>\4e2B҈ Sau_1k5R G3T5.}٦GcB]mſ To;oo2*CE"C>$9. 8!g㑥;VD PC`hLdRoԗZ "3ZSI<E/ujuiRR攍shkX T;cOg:ikLF.u36g ZY?p&-UW_qMReSONy^AUZ>;Eyw{d踩?V{@~]&IV'뮝Z1%xKf3[2$(G3 DX_@tCz382p8X:nqG0/u_ uyzXIkc6V5EN*Q:Dt.FQ*{M]#o_ߴSbspLi!ќW=d ~dԡI |3$LQkS~+"5= VBHIsʡ,Moo\@ D7yBTM!\Z[F>D77F+{5DxKߝ#xukG =/BF(Zh?Ʒ7pKwA\{/ZlC_Zbʊ鼓[)k"/?9P]@bN4Mꔇ( x<n+LE闑v8_L~ >=ŻpT^AN{!qڴ8 p@ާE6{6nPFS,)<HaM5WWodY>kH`OY՚gJH#^伊{MRWW :  c|1pr|w%j? XψBjO"mlb/b;#׹HqDN%q[H;`!*O2S$"a KQJv8_sIԩFVxٖr':f6 J1I}Rhy h4R}&[?kWTOeўߨDXex[­!;? Y&v.ƶo-1c]ܘ`jٕPā)KM4ܟa}DNji*rk^`Z 0¸;t,VKg5̜S2b9+eDcz{%pL5L;խ)H>ƌ dSanla *[YrpK!dj%D_eB\'%ʔs u&BJq+K؀Ft5)B9 8z=p7ٚ b9q1݉^t?{?LDz tZvU#A ڙ;${~uԍ,?is\QRTXbZaQq]?kW#i[iGGz2 *s;ԏӉ.a:y֌uyßFl[4Xֽ=ÒC,o`~Vk !)sxu8`:"iQhI 'LU~(KAH꽲zkX;VGof^o4*o>d*rL66+-L%֒-7 hJӥm-pa#'1CTm@#s33@LvrKKL0SXr$}Ey'pϸ=d cz`]IaVdSc{tvjn(BÇNHװN: >z(|B#v *QnS3ɮ8Xݢj*6prg'@Pݕdm[T"8ܴnZ3:B$]+=#[_I*UG:OB4 (s LI+WE"b9{W/7~y`bAԓsHyH]:Ňi;vxk 6fZs18lvTG_?eo6N| !1p<$Ĭ)iryxyF jʾGEj|GIKYkjMԶs0[ȇg̈́gI05#MĬc1b#L$(1WNגUX|&{K^B!8ʴ_ k5QzV(y!A~ c3Vp&UsjP-%ŰYKZbO]&# +UgHaJ9\>(%O6/#jI枽w|tw40Is,NE~zq(#?7A&N.4τVkXSPRyoIN> 6S'}wp2>}1R>ҍǾEZQ /c݉h9\N} ߟ##=U8t7gPwT,K97V 9?ड़٘ dx@Bv{6=-y"~o .c1J:^zYʚ< (yj%Pӑ̾[NKGJ&2m< B-䫟}щAjlEc8ݿŭ&QmFZDSulNE,kR _1m(z2whWk^`*LǒsALGGV+ޔyQ|Z2q*$4;P5 I $Z k=T8wGi]㮣 Qld98im"W촰HH\EN'իMۂ8״iI(!^vw0'jEz{raRԮfx#5*3.|);- X?vadZϟթgdʐ?"˙:0"ҩ7. `e^AueaC:jKU^ dyC(Jcb3շ Chf[y,K#茱KanlnJ?d,\h' |"زOjI:Ui30'gik="bjȘkDR-È!> =LNR?<4fRJo ]-,NqVJrw)8!X, 0^ScnҘ&pDQ4~M(23'Z 5HIkpR^l~L=UyNbȌ[ˢga 亸S@ 3k@@Tk|a^WI@AE/s)Px* N!^G6A;#ۿXKx bK 0ty$ [VXS껺Hj},^1}%kOR2κ_frnrL 㳀M; Br^;e&Xy >gvۓm\t8Kȅ?dnLt9fVl+̠mt9n:>̣ՋTKaC:_FMK\ȁ`Z{/LDCqODĻ~ ?ٵ?<$P(, D7F-rlFS&6 D (wr9Z*<yBArLҢ~D㟅 ̖zKxsN䂏^r?cv;b= W8D;LkSޣ.&5˃S ).=ƝF@';G!CrsVf쫍/EJƼ?,m7v~WFS<%Xз8Lpbbt;i&>#>`nfGĞ !T|T晧 0x ?^TF1>Qй 1.X ^1|Y{+iP̬THnASѦ9"*eŭjx g*FXs5@ hHlniFɀ')Dŋ_6G_dfrsJu?粼pٴLduxhD#gU@,^=^@F6W:L)Pq\5$؀F(t]k!r [i,dz6_B]CdY|\;[1=/t u/Q&)pͯl ǣ2qO_hf=^ڳ`Y1M4u.kCyo24:K{dE"^J̴Pm.IT/gGBh^Ot 6 q+d U,GϿs &VR ½/T|͙EU|Ab۽޷~%)T;:gQOflV mιn?b."j89o.pC`F[G5%}?fu/4U-'lj0xid݀,,1U;M/vOVFuDeហ6:[O;^f5 khpJ`EƍR(g :/,p{)xJe;Av8 6&T "c)%4ئ8 &Cf y㵷7!7Cv6w)ٰM0 ׽ KDxx6)<\ƘCvъdAy p2D ߠ5_U&Yd_߉}Ao?UXF4P vIL fME>?D>==}gK=JVxkjW{>0\{_냇82{Coo܏nu}|9AMh_$&zdZVq縮HI?qz'JSOӘ Z6~ȱC܁8`;9!%ʶ\.{='^`k 43cOh(5v g:I:`KyA;C@[L E[>\XyvBJwDw̥^{㠓]7Ǐqsh?}Mdd`oK ?iEVFӰVl'|EVa*e?ɪQz*R[=MX*/P\ >eL:teyvԌ/J٘|0()().5ӵvytaʆM%kX_Y s:Z ?ϗdHB2o*v]vZ}gqN+8۟wEEsSEc/h]\+cQyV]%5ڬV(F+^VIZ=x~8-QJOx䴍 eR ŕaKm$CD_L2> #ɅkX_.7WzwY/F A/[.GGۡ ~RXGkU6z#v?A k߿w)o9vq%UC 443a˂CF0jg:Hn}S c#ynwM1xh5z+Ť:ַ~A_皈NZsUH3.KClu[1-!LeP4\j#3䠐KڳGUW 1 xin=!XL1R8P"k~7*srd/E.b_AK˘ä5e6̗D"t$Fh"HSO13>[qwZu( jLY ]Ŏ;&$zf  0 臂վO_A<ݨʕ*7L*/P.X=HQca~AzKEK*pFpɋ#U^=DNڨhl:hFѵ>$h}.j\CnꙖ!aY?2;&`Ze/zV5\b {S %Dʀkp[3#a" h}M Ũ+}m[wKň}T GIO?;mVi~QbQZ3HIȈQ~ܩ}9+&eo'CnDyJͥ#N޾e|so<7ۧRbknKFH>fh2 %Y"m჻7[|fk/b,+;HPʺ 1t S(NaCnIb1BQ'#vi3}=<:6vh c@&+T{NEQ0] LcHD:DסQㆤ\)$BCV/6\R;)єdAmLlB[7:p㱐Ade>pxX>.n I{&|DzK*{]C ?XAp%*ˀӵ$h>x)M$ 31&؏c]R4!Y=1 la=Ík}^{s81n{0$t}Gn8&|Zwk`o۩(!kc (hzavE9b%ryHչwd[fVZk6J6id,>dOU<쀞wZ k)a94yAY3>((lqQRGkI/cSxW|چ| >iISܖB&iw<{// VX>ꁞL A<%ι ޥ~Aݱ75bS>,oJvDyb9s @amkrr;53VU^D쵩prw"gI9*\3/$Ȣ#-wx%Ƚ.q|f-3;?Şg{\CGFMǼ-Wu"l?b-ۧ?ڰr/l/)ljLp`G\}0R ȝ$`'Jߣv5 b :El 5}-j \9,o灝Ӟ;Rw/A];LTΧ}τW 2?äXuzՂi)TЮrW@ھިs4݈>\03t9B xhΔ6Y h7.o /_{͝A+j/1f'"%ARDݜvj0({K|^&gߒ_230~5^n.`g%*ӶRA%TdZN4.7ۯaqgrN$Pl wHUA[;G]# \ G{$|z?0q 6Y8|Eƙ.v9?*?'U*쨏~aL kr-x@A(BVß?hZZ{zyhy.9~rrQUy&Zi]̸󉚆XQѷ?=nJ4]נUe;,q5O^V]Uh;üR%k 6+맴]iPV,矌~vdׄ3q-jA/lq0Cl=`麃HWj oGMD$#zP~LG7^"~,ulڸc`䔰-0 o mOCC$>S+/lvaw}$un'-o9۔:I;,|"*U~!P6k2Ô6!K> $tV:Iuӿ%hbϛ{=$[ fvr+MQjkZ9r5R]u }]+sc斁Vw>"x&z-t_.0Vs Ŧ4ʑ5 {Ba0UQ,v@W ^Z26|zF&lTֺK# >  LU#7J!wݨ1 %Gu.}`?EԦf-ўtim)jlX[|>^6X?se$Cg;VQ 71WGjvteeMqiSs]G>F,"?G,QG!5 Lu ^CEɠEHh6D]0¥pg3`gS'*; r)BO  'N>Hfd.q f!tٜo}(|rV9iuj@f Բ+ <`o3N[rquGT!U3Yd,6>+3V8֐1?m!|w8Lωj9,#9#&;+]ډꪒL̰\5O4j_4-{vTw5J9`hO3Eo"-qQ|ӫښ Y ~Xz ۶h ,BH? ]Kv[yƨ{cԁf6pJبőAv$Y[`Z7X :~>D0f?nQ@J=?x+-N"{Ǡ EuE\%մ}Į 2>s1´r{T/Wpf ^xIɈmyl| 5 I M#Yj2{IFQaq]Bג˫&s AcZ@3M]|?t,Qr"RK*uaā]ERxdluڛǔiA2%`$Ҥ'UL(ق ø+j//FrpA'"/kCNHcVXԞ6%w@kwMROǶ()(6{?&O9>jVɖÎ5u0T$a#niL~.2Żfb[xTP) 8]21&ӷZh"kB˘RXz 6 S+u`v{xvH{1AWտb69-Ts̥\]e֪fḚϾut8Ţ[=nAHg,'xI`J㉆򄶕"^6,g<I;O&5]k'iTP)*?H+ B+GT iJ˵wUX(UC:S4\{q@¥i`;I\W>QQ*xR3W9T+|C@"oڨ[@dpQ/uewݓ(qc;F^cZBaǚB;z\<-#pc^E7m[aJ*cB{:]׳Vu aqۺF]2p:{xF$'OSTD~PT{ mʡ+ӹ/ō8up&c8^V]e'gV1IG)̸/%4SUWhNfp7d2`j=i+ ]Q5s.5BW ­+"pظ:4+WME1VQ˲!.Oq:vy겫GU?eP\ V}Wfn3M8^;P MPɋlM {SYj$Ce8 :fw'Nᅢ|r>:ӑO6}~p$aq>m_{ k ߛL/Qrȷ1)6!_KZHPSO0O駱sX̉(qo`13P[RwDev<55*٣ꊱU?ŹGC&Vq`8*=[geWLx%d~X6_T.5XSk.#8*q{b(">A>+O(S/a|?M3+Q8t)s,?<VA~9&aL6LWSPуIty`ghvT =yu?d21V=dJ|XN1O;h,_ܣ.hS D<єǷR.$ L'OSTue|ƈ-v7ojTF_Їr/vsIr]ۆ#9wpXJ}~ %;RlJXQ/~ϞaKlaFuwH[?0O ;vvh@Nl% h]_yF Dc4z>F8Y!pgs&{),(S+oNk}I SɻϺ-+3GKj4oW4 MrY|sOR,ft:S x8ezrR: /ԝW×MRhATec>(jµ̱z3Ozdu?d0@,6t{D`d-F 5%s2Ö5Ij^6eTwoc]CE!1W<:*:CP?(4Fb )+G94SJ?1TͅH1Jy|k.4A{#s1690Ŧ̝Cп[Ľsz6x) -aRM2MlBP#HtC{qaЬzIJyT1 @KD,_UhF^k/!Qd])X!AT.\5'llJ6\EhInJ]I]AMy^1fm7Jǐ5<(J =w%I(W$FX)JIT;ftN%ƅH5˛=)qe8#Z3]U SDok%A=*q$'w1x}TK2jZ4H,־0lؘP$بCf~8.t 4kbV\roM/'G*upM'Nϒ#Yff. 5g,s1^Vja[.[D6߸= lOM:|o01;T~eʁR1Mnsoۙ߷o˒+#=ASd:?Hdv͟R cZ-0"\ݒ񨆫rY}`C i@Էhpoޠ$< ]:85OΆx‡J=*+d5Lն2Fix e3/䅟_i@vF:YXI16tef?w'eD+pPtbA{ R5JQ?һ_-1' v^BT-Xw%z$x0h7?"&˗kQElDfftI-0ְ?<_cC}Y,n 8aD\Q1 ʹ'wW(ܯ_)l}{b ;{g`N$ 3P D˧&y!N }S)XDTU*#P;7iA< [_@)Uhaě|ȱt7BO1 `Y\9;;2^$yHXayiSP Ոb_&T|@ aL1ݘ2_}0<@d%*Pjf3^w6w#h"WX]xv,z3A:`$427҂kՁν E/SN%#OuMF-O8w ỲJ\[P;QQEL/ endstream endobj 450 0 obj << /Length1 2190 /Length2 14475 /Length3 0 /Length 15797 /Filter /FlateDecode >> stream xڍtm F۶m5v&jll64jl;rz?}k֚c{ e53  #3/@LA]̊@Anb l` 1]&nn`uX8yYx<1tčݬ Y{3'=Ԧ4."v@=@h`jt..LLvΌ Az%@ Q(Mni/1xZ]\̀ {v<@h/c #@V;:9{Z[̭l%IyFz_ƶnV&n Q37?gS3_ f {31;;3_[}d;{[ٛEՑI(#ow :L%PtdK`Nke|Av6v\@@_*!̬L]&@ +{?@YytǏOf`o#fRWQ7*EE< V66Q?*[ē7w{CsA率oEi 3uoEe74o~_SMYhfj2."boaFZ9KZy͔\L-{t[+{_ WNq~?Uߌf+'2D`~/V7=m˻ /בrrD $L2л`R/f0)A_niqL w26.lkx/={#agKbg'_Me2/d|GƠXWӃ(1`0rpgr-x[n0?ehw?{lߩ_cG?V`=8CNd?ս}7aNO߳9:3aayg,t~*g[cg8g}\Y滇Yx;Onr|d}Fx= gL]A77N=K|ֵ?3 Ph0x/~>&TgnE?nKP/xÆ&=<ƫN!,NbO0 8| lqrFQøw+[ Q٭C|.fֈ (56ƅuv={X68[g5akBչW#! WYu,fF>b^]ytf[(o[SV|掚#mG:swwI<^0p[,ԍэI턮װ<jK/ .wOuF)ywıǮrSW؂4\C75zRe)ykF×i5h~ءZ9]QUdFFE7xp^7>CvKIbU_Cf!2mo V)5Wq"_${ էt/ϥ I}-VYUxRRF`Y ժMֺܵXp-'1'*h37B \3\:mvU]go?t]&.Gg Y^E4dn.B#ECB2| ̹()ј:/=2ǁy(o(p0k]#P1vÏԚJ-{ c|*|D l)OcuR.%Eb"pi-Oy*'V@U+÷oEN$OQ,w{LE9l[z߹VXh41n_DJxjSrK [n7܏sgA~g7ɖˌll>[zsxQTbnqLouADDM];W;^̠ߪc%+qn?0 J9)ʆA:^AM:"k@;&Bq{XoCڝ!g8_ԋ,Dm0y4^$eHu-r;J.B5 'r~ˇ ?B$<EU`l` QL%[Guuҳc`!uKGc$Hkc`N/ׂ8a >x+z5# :%sE=7vP .EP 6iMAwT^ӎd4}{UtII*u k ,D38Fj^LƨIGkAl A/eo1S}PCUShCM DU@ߤV>9gsh<[ yK{D@Xb朇!eؖ^!yo9 8d$5+'@7qݙ0/KdHQ. K~X(]لmߌ19\.mW8ɥ+X֣.{apF`eG{uM+⠅+<9[od(Kp9q#YiSQX}ٮdݑj}yV77\0)%>Ul N32Yyl! A! Ҿ Q.sӦəlPjD~ g)rj;GfsO7XL[6\w-~%FΟ~bML~0TYo͘`3d0}@69J%>5q0yWt*ȤpFs"snMw}јL%I0zPsj }!Q`1Sξ(XVD* wW]]1{r/}UQz -pQ_.Z!VNV_b¡)#vB9|p%MtZ>E|ʫ?́gW= %.l8 2SvҌ 5ĭ^uơu#%U!J{D r037@'67K-W'&n gu/;2h݂)ͣ-IХ)J@9&# nC_8}̝HꙪ(1:NbAƑQ˸Rxrh쭺7:ŠNbR4sR#ɫy}V} $cEAt9a=j:L V*]ěsV<7k줝] Dea~OZb&`lGa}ζ~2=Zm]e' C9ഫ NT[C*2L/zqxx`xv9VƬEvQV!KK.+=\Z+ ްKYk#/r )0r[fqq6CGI1̛R}ZF~;=gRmѩ}n>g{H' '1]FR,x]p Bx\Lw~#4vaB NYE,σIj7|D$7o5aSe7h>aRdv) \ae HTr fiUpFC$iAPE=\;Fzиjm`YFAg8qě}>zL?`}5 N+ךU{b@aK<1{>"JF>mTW(gX4a7M9Dn1CH7D snrMjBc:e?. $ 6ϒ͌r6IP7Zܢ=-yIXN*2HjhC2MQsnK\bx67[rJcf'*`S mG47s`{Q(.UU9R76@.xH1/UI7Ӥt|q5]ia'٧Y~"Id_dNX2eyN)db!Cj}1덦mht%Xf ڮi&ʐMn{B 6:k J낦sB!?㬟Hu >/6gȩHݶ҇hL^42Wd2r)F:`e,ytf+Z7qni k6CS¾rEۨdHPzȲxorbC^bl/ä#HA7A.Cf~J#ء}΂-k.'^݉Pu\qAf^/슾ռ`dNu(AUkGAu ";B8,[B0i0dLYfq6߄6RQ]x?0쬔yIO~$m; 89J]XcѿT @~Z]M%kj]VhΊ&&ǧa؍{NDGc68a7H[D-"+ٟtG7 CLK]nX}fLg߭6cz_?"]|_@iҘ6Jy0Y6c`63dڍrhB&-PFR֡Z?s݁q,Nk|g+̎Zp x/ח@~% ZfXoPu߰U xj)8D CMغUy*_/=EAzM曲3-Z}}-ev, s99.%%l&fݎ2R^sTey[+ֿ) t*~,1Ǜ9 D w@^.k >IuڼMM9S0{9 =^5|Wmi2K%aɴ I/Zm0BR򊴺AHWR]t$-KT)|⚢ErcwiFwUjGMnn#)' iz8:Ꝏ@X&]:1Q; 7ﳳ#ȸTmgI"Rb,v8\x_CHFI<ē7]ìTj ٻ!JVf+G<91XU}B/fvF+L]rf2fYz$o=;`ʐI"7UViM>~XNe*n_AIOKy,9"J(im^[qazNImZ";s|pXg+,DxէHb.M>~6J%官>>c`4_vYTV-d/cutwY;JqY_x3皺fo1Zx'>MxXBY Ȁ+ARߋ8x%9aޣU-i]%(1P3Y %VrX\ċ|2֦/uP\tr5ݎmS3k^<<~ؖ+^1:_LJً ]F:fͤ[s.u, >wmyF WRzP[Bٗ7%칯g 1GYz))bSruzC̏6 _cj\\rA'XhʤiKvP7Π\H?YQΰ0!Ԟ (Rcd阛v'qS*$-Lj ;r66# I8N} M֢Yκ( >㏲XN v1Y]HR9;]&՟0H/o6iIjo>ſoTPO /U{?63~04ИTu\}";sn2Rs, imceYl^RTc5QNWgRTUƫ0/nhdCi_Z%ʈ=KVjj.?P,l؅ޮ;nUhZ$#I" ls.~WI)L $;o穊%DJITXRmz‹oKb@v}QM7naڮSIpH}FEΨU2 큠Vۓ)еUӤgOW`w8yK8>[E<[6i%9#ɥuGQy">ǝG7 +^hV7B3ArW3]t258FLLȪi{L^g\& 0қv?ܔ_"\6^;b mtx Ak RX[CSUû=W^bt'|?˾V1n5&9LCbK4Znirj12uqLTƬ&rXLkuNiHROz!ŕXOwV$IN" 5L.RAg盵/r4`lt{|uYUy g/ CZLb^ۭ`Mi׶n)2sP' V殃-hk7mb=t09mG[v#RNʆƫ|*[֞!G^!f/8 ,a6<;-{< pG@5#.Lvx&^$UTcr2ޥOX)'w]Je@r015Ṅ DUv  >5fMې#y`z:ޱt诺8U}gI<\_bfumSny[,{GPIݏeZ" Ci[%Ih@Qj8p17eUwP B,;u"+,5~aa 6ϘeȆ+V}ﳎjGRhq'4k<~%*/(V6w}x <݈T۹#*e_GÂű4'ЎCZ/ry:0o]zv6.#a>) ؀H]n}_yp~( c?#>fN 0>0UjqԢ(%M¿HyZk iSJ.S!TdN!:>_XږcXե ܒmi[>玗,vX,߅C5YF5.uΏk`I14 n f-/LRyY Nʉ0b*H`V;?g*`teIl"h3y['QԘhHoQU5 {0{ǼO1R  wʱ@|9%D ;Ł*hUº8mt R>Z-#*.2HsFVmKjhzjnOB j^{A C HC]T>m)47]"Y S^i[n?Gzuʚ vr[Lwڗ/uym'gʃؒ j 1grz!]]: ?ETI,-ÂQӔ~C&t)myo*UVu[lKL4$ٴj dwi`Ȃ$1.vDKOB'0y.7s8\'-:85C-HŊ640#A":AWBL|w>[ 'dK6Z(Lrvm~H;#̍y,g~((9 T1+E%/Q/c9{DoΎ#*Έ5*#3ljJUi8p?fɈ;ˇ8:lH\f>1U I"혈fe {0 c AVF} .h 3sL;y}[:Ή*&8Z^s;8p>f)|aqSe:~J u8>P8.aD^ mz}>#c"Jzfm>G2?:8A9ak/o6({Ǭ,ە^.Io B1!!zehbпu%fF>}ߞKhΈ-J׉{g>4Q+E(uG'])b1eJ*^[ 0/H'?o/E"Kd;wàD_pu5zqBj?~D)jD>0(d, &<ƭ4p[-|G-P*S$R3Cf8] ^{Z >S#;L#0D-)q~S^?S9H.slmA{eBC1twP# }7dM=[xr]$z2tW$OŘ_9UEN8ONHu*p]nD $ܡbvOT𔠐ԏ֫V>.Mx7ZzfRQ1`J|4LZR/<@(@;k[ЪBP $ 2( juG&LL r5Bk "g~Xi_h!(*CaGB k}"1Q=xe޴ {{Z[ %1nڕQ1.]{< ~⎻U3nf~v-9}=YC- *bV3tgQŶvAumU- ZVҐTn`O/O)S(){&z)$h-5|lPTJ;=Ծ%&X23φs\vSPŬR\\ S*iGx͔5c-,TRl1yu8꘿SD[џ:{PTwL~ 瘑2D NshPs +\51m 0=#e)֠|,l^i9qy=lɅR!WƻZLE ir^ I=攖^ j8ӿߞP<8U|":U+p:/[qJ;<.W. gyIoxMy5c2HZ$g$I)RG#|;}9Smnғg KTKwZh8:Qv`i\>kȗ,`aSye6Ak,lx(nAl{Wt'ޮmFΐ}tߙA"l&u_@'Y*' ُnRI({_NOOpP] Yk& [ ,%6kvr;bNL?Q^(npӥQ&!c]lj85U=hǠ܂TD065#7.~R0eFd e/1#BJ͟˞c ń<+sAʉvv !3$=:Di/7.$wu5ԛ8]DV/fP8Jt.'sQrjzB/bq#nnV+ ]oV'E>l df”t )|3ciPk[ϟOp#݅~geᄤbABHSay]Gъ7-яaKb*Ԭ&X e_ TH>-Qj )XA }URÒbOr}V 0D<F"e?_xd˃/~+#g0V6p!aG{|қB+@WPppTٰ̫2R%H̃a҉ \ C~7b`@XwV?eg kw$+y jyK4~fkyg,~F5! U v2tJ/[X?>WmiM[!o!_6- P M]W9bL<A:Xrr}^gr)e.r kbj<(^#krLbL^_?}uHST!apcKe~_ı4$3bօNS+Z Ϣ:eVO_Z~8gh4clt|% YY 2s꺬y6@$ѨFp XMZF3IarG]>|ulVU.c\kdIȌ[>\4^Ej8&fo^;_RR<]aR<i Dh6sԔ[pypOp/ nzUM]d^Ud7WqװlWH_q+`WL |v(&d(cXo} f GAҾl-+{` `zK<1&4k?UT4Bڴo|lVL4 8asYkQֺ\ dq]IqGBE5xP'YQ~r=KxdO"|?lsL'Y!0d[rRqVySġ l:͏oI[2 vdD0sg|/yyFd&LX7L(:qc h,J(m1pѹg5-~Ly&hnUVդw OE e xwOH$5 ؛G槓8 lݜeN7$6궩^Uک˖*<6ʦ$gP{=,SyjQF ^" Ly_=TQ'{h=탶tݡ.fZXh@:"Ȧ߹!=XkW%!i&`}c: Sj!iOPc.P2d۽|5n(t/lUnqNwo_wrI4wara:RgubhO _t(hIr hݛ#wMe&]==2VQyA-Y@u0IՏEM4#/ǔS~"ɸ/?Cl6s7k+&n1zDXؗ+4&daҭ!Kl ɪrՅ30SV%{H԰Iz mPJ*YuI2@w/:<؝^ 2nǦ &?K^annIT} 8o kq)ȿTjp|qI"Ԏ.4V3h0l;#cἺY! jL$EJAqc\h6aeg i^C8QUJE6¿yMffLhRf]+h^BchcM*+p輗*\>'zCrw,G{`vOk |lTwAYwV[ze[rU`jTB^Am^R0$8!n2+ .W+?+Oڃ-?CǠ.nC̰ʿ{0J S3B^x&<<JBR,6w_oSߺE䜘[Q.|/,hՕ>g )dtz3( endstream endobj 452 0 obj << /Length1 2236 /Length2 8691 /Length3 0 /Length 10008 /Filter /FlateDecode >> stream xڍ46nժY{Tkڻ)$VK^Uj޵)ګH=(ʗs\纟.x|9u==1? ?1; e[LnuFp? 䜡F&AapC{XJ1D8K!0+:!E!=a6(L(/wf !([&%AQ gB9J@nnn|$>' @PgWg z0_r]5 `0K(p[A]5#_6cb6?_&i 1 pȿ-023"? fFti:1Q1cGlEBΎżc1;Vc䈙29bG? NZYሑ10U cHofLH{L1 ) 570=zHKa0AYy1yBLgP*ϲZ8;c_)fK(jI<= xZtZ.ƻ2( D}vDh@ﺦ'wW)7 =YL nfm9|Cl "\neCWv$=It2tn}IG6 `d8 N@LzslX Pzp%~/3E<0t &QUa(ry )hЊăX0\E2ղİ7v*s nC s\ŌIA3!t[UU@fVT:OSba./9= @X=RJPSq㕺Rh"dI\eP2v&ь;>ȂCCj-K{Fey9N?h2-ƫ)]{9R%=K1}}by<ŷI~;Cr.|[m[쇪G^xK6Ë{b) bEGX]z!4 Zn t=`E9jLU`_֫:'Y=G 'pNq os%/)I{Bv.:wy@Oo,ԽM'vPIYwpW{;@>~/dZy_Ƴ>z8oziOT**%q#MbD)1$9U+ ڋ?=xpb 3Rv2 hX8a){,Io>='Jc@yaq" #ޮ7J6o)9u &pRG8ʇYsPyL}nYK&n:wO1h8qG Nl4cJy΅ut.5 @R\{sV1$JAoQ~UYD|@]|z7sO{qoeLeKC1߱B4*^?H:Fݱw"{e֦bos'kʥyTuғ3.m8jG'=1"Q9Tq*x'+5wďFL2[ $*Q =xhm.LMX=Jܫ;hc,&0Þ!XIPwٞN\8qi>D?i/q^!\šg\-Qӊnqg|foMGġ}Xۂ%7:o[hC69#v B?08q(ڶwOVp [J'tyȝP$dIo]6b'65˂~^ɄSx^r3@k.O@!3k,#|b{X;fg㤮 C {im]I<qsj(WDT-C! 8_ ,r4C{ gv(Ý9ű͗ i55́S't{#OM2偢دYhgv9λw?aJ3 " 3 R̠̆kj`'D57}l׶<8x):| \n'7 aɄĊ ԖJƛN8^!Q+=h2]fՙ^{jMu yI!Eל k,&p5N}yzzSyͮtSU^eWLx[3+ʯRȕ_>#RC{t?ϩh{\9u5c3l*k~5%covKy*5a]{O`hvi4+J1FQ7%5ȒPDj \f%ɋ!EGߨ&,@mdpbR')5Nf\us/&7PG/8CO+ol23FV*wm$ v>X)'I(֯4[2Oxy,;H:Hڬd\ ej1(EpXy>U<܈43x|T&6؂++z(Cm}'Wco2u])ہ2= qYru|#¥ ͦ  ƫ#ɬ]9DvkN^>czi]5o 5vOfT&Nד~rLHGw`Ql2BOj_z4P 9䒅)&0dyvܸ^:A _m/lA_YLP3itE[KV:QVq \;42(M~hĀ m}uڬrIPWDgi‰c a»,| bS]dRqR, ڍؽ.-z7>y'JBN3(G39{oW_sF }_xaY%&rTؖr"ɕsvMlMJ~gMG;͚µF7;Q ON㖺^ʷ3%_#^h 9 $bc9d-%`dcUT'P]e"4}| e``1IgnzUTæY긦1NBymo4E׭ױDma\D(mYZ!i!&첆ӆ>kDg+7)~ }.˚fS&VL@B\Q:[g%> ^.: L 6aGS6EzK^XQ =Wikd}[POenuBʒj]& +o+Xvx(I)K(-0E]HL :^zLْYsb9}oo; dHldaʄ_MzYq [R:dRJNQ: o i_9 Nߟ q|w:]ݎ3 ʯTbDk:ՈϢof<c)g-vz.+;H+k:w"!A{d[DÃ9>,^3CE<9ފV|ĺw?0Qg}2n:(~&MVKhDOb'uY>0JLJHPM>r|-JP7"]E۬.C4<'n[1QQ,4JeL&lgMǻm#(䓌H Sd^^ GCflHtdy kdLN8vy 2ҍBѠ6Uasaz:D$Uч7=gzB:ChUlȯ%*>|0e.F=iw' Aj&&w" U :խzVިÄr͠M36M7>HsR\yPh2Z|'Yլ78mr 4/?uv|.xGXG.A._ USosIEru@00X:ؑ0&uMC| SZ"WlĊ)o\dT aRmn ?ģx`:!V{vQrZ|HnLЮDx`Cal80-5{P~aADO*N^m^o d8j,`Iw&u4L 2vsޝH1PO,*KRF^//,ƝΊB4e$Ho׳%cX NahYv'2L#9RDz<HQ'YI$AEkHұ*5q^ɢΠ=@H81sd{[+inEiBFBI3DEt7iJJIgv#p%p)"2RD)45%(!Y Q$QLh'>euG&# 㷲Lyʦ)D S-,hvE<<2ŏ Y2W]B){/(D?ُV ornҺt4KCf!uMQ۹]:e=RI)Ø[y09u32) 4~~Ncˣ/eKwL6E.B>ɯ<1dx{̏V%PgEǝ1~h,FYbc%>Jt_ln^$jnT?Ud!5j,煋[_e q .w\]^b<<+KJ>[I,{k$`+MMۡ17Lk8WK3O>鏬cj7nd!i VDҧs$URS?Ed '?_Uf`2'M{e%Dz:$I+~]_o&maQП1XN'&(m'x6/keỦqx2ZnB-1;ҮnI^\cVhǍRm/X{1絵K7MuŚk)*W|1xk؞i5TG)@|jgtNDyv(e|2f/3 zS~c΀NA~17qdB +*cʹf"DA*Q\ Q\l#l42J|၌v_1hi<f@)2c9UW۠H[w?sEgDk{#jiW,qn#w "G3J9J%Ȏ&MC%w'3D ŘB(.B_s(  PA\p;yy/V=XmnH.;_yPT|F_AʈZ|~$vE2'HSܻ%8Oǘ~ŕ8V 4n9K9G+S.d$H ͘1YswJ<9BT|!ҟͫUȇ:p4]]vܒdV<!Y%|:(51wCʒ3rg_gy[_ܻ*蹡Je?'B[[{sTQn"K.~HݻR&HOߦNI/@!v#1a_i7U[^7-_< a=*y1++O2[͗5-e {kNug5R#l_G&Sxy^; EKMn~K]lN!ݑjjzJtyI ^f2}o.>+37okT",'BV*+£!}E:ڐ5ç?lb'lfTU$\0;N*^ҦWzGo̕^];_Sr(%[Ʒ& ,f.gX9;f,`b\4ll( 1yhyIc9m3ۚR_ʟ5+K<$ƒ(43 fb%y{xv.%/^\i,l6N\˘<n'&y!mlN9:6gk/7T h(5}-3eӖ</ص` oK3QS}nDQG @@oW½7z߱r tO|e9hbqWAv ȗ{grZosS[JxD3ߓ_#ѹۺ}zN fIsE cWNp%+T*㊡w<ނt\߄|0uzQљ8ܴޥ.jŷWa.kAOEҦy`M-Hvsy?<*_+) &IQi`-vfR*}Xa\* n$ȫ=mH/!WI! FwӇ: N WGH(!3^$3U5P)mpd(C@ %);W~A8ԁ}y. @GڸGF苧$-ynB`ʕW„XxjN^ލĹs81^ݪ9}t$ &S4&^ܵ|vpzOу$۬s;&Mu(qlt#R{^93'U[yW=%-6j?JKAa0-~{ўg)x |-a[1$Dn15oMBoԵ/j: ̤W%I\D>$Kj[4`MН9N 򓛶yrTsz!oV+Vj&!A5-n,w\*y~fƎPQGOIR^$?vfTcty;V,pz΋b[%![gƦ,e1E.׹_D *ΰ{f.8: 3c3/ةtet׽ endstream endobj 373 0 obj << /Type /ObjStm /N 100 /First 889 /Length 3693 /Filter /FlateDecode >> stream x[YoG~ OX-[cg%%v, THq!9H{HؗbOOOu_UW! %lH9HIبZX+ {\wQDY. Yzc6TF =tBI , t0Z|NMLz6ft}d̊NkA u0Aa$+abìIBI hz6D9ͰMi"O"js34l592X, t% 0u)pvAD LdD[⌓ZN+Ui*gj3B*#YZ"SBiLp tqeUD j:@!`81#OE EdV8ILV ζ.rfNLdT ,C\sAkE\1NIa ~hlTk<@3ٞEh(~I Fh`?0XMUChϒRP]#I3#U|*NXGB@R 21hܝH<M+x85"؟>͞qC=y0B \# ĥX~&JNͅ[ k-^\q23q myRQ ;OI݋3Fz4"6=yTOǷA=C?gW'qJ,{O0h{2ꔳ#DٱSsؘl)7m9XzÌ_^~ɽ䬞|!)Q8wjr(ڣULsEk?"A=d,Lo?L1j1| rKm|FpEpwY&DoEOr dJvߦm{EtUv-*;ҪPdPf,j)VCӅijP={O,O}о]GCkdQZJ"-J4G?з_}e9Q*4mC;!%yQO׊ID@w|q*Ui U~svijӔ,BfO 'i M! zcƒs- ((j$Jns&>푠YR 6*m8k^h\P9-\O6jg@:@9ƃ( i{iҚX3ϡZivVK$E̲צBJpZmRlѢpuT7m!Ӣ(f1x|;(T<<,Xډ;>uh7c</pje=բziz3k근ǣ+>5K+*ub˖TҺZ (.cB+Jmji!rx!Pϼp-XRwytӚ)pU65TҲ6^ImfBF[ ݴeXtdva#ȔΪN(`9hm!-ZHiLDX 7'-fW6Zpyh׭~⛦7h m>R|\P yn7/o ZwlS&Oo<}74x9oC!Fb.%sעhqX>aҌ-@`΋1gZNF,X-1tf X`? Ašϼ,U䎈:B1]f q_ i+FmwuSDzK+^Ԗ-ם'=fKkoZk~^ H ƃ᭛2-|RtpUR\`bLÛ y~efnCӊq3uce󤛹 rrD-̕ KPNf*+ 5DCMCtҦi,5>dR) @6C_Rq^$)iJ`fn^Fb4\6O$mInŇp?39Ե>]~?-x\ gW~ ߟ^:>,-Z—>U~E|1b{R _-ZཊpgI>ބ9ԓY-?x ~n?N5ԗෝm<߭Dg㽾>pGmYupx&CB>[U~v}mpr~Se!hx+{W7WJ'-?n09b3lK\g&-&{6Y>iNbڛã/ɴ23_}x3٭ބVM)? I&M1^L);%O=)ǿlˤ&G>E ׊°lcQ}Pqj9nf5޼|OpE/{ZY+|3NMAkshʠq'y3kj픕NN^8|MlnWZJ @1_>c )7|>{6n[kH/&u:z:m7;b{+doGgd:O^wo iKI^8 1|!8_/ 7/'ZU_ }(i.ռhkزUA`ןjM֊13tF *c^'8{su6w,~d]A|]Z .=ޑWp]z^NKK:-[2mMҩlI-zw髕Ժ;QrK8,p_-}ekuwCnvߒb]zaůkaxU{Ϻ;Ag6">_Y-rǢΦ⴬TQL/Cc/Q>}:Z&;3)ǂ}W/dPq endstream endobj 514 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.14)/Keywords() /CreationDate (D:20141113162818+01'00') /ModDate (D:20141113162818+01'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.1415926-2.5-1.40.14 (TeX Live 2013) kpathsea version 6.1.1) >> endobj 455 0 obj << /Type /ObjStm /N 67 /First 591 /Length 2606 /Filter /FlateDecode >> stream xڕZK q;=Eb AoMN 0fz߇[K(#)R@6䌳`@2o4@2+Lp`3&0$C69oH.|dCaȓ2 X$11~Le1l-h{O>&2dH,wd;@S2lLS1 ueXW qMt,Eԣghbq|aKH$SLBOڋ5)" Il-&;iALfGbT;֜=A+Sp^D֛#)DD{Gۑ dBm}aI"G.8 |:dzqi6NXi?-iƭǁ>N q_Q _r~:ß~|<><oݞXmp-3s\<{z^Gz5>7{[=tY,YL~\QoE]۲`Rx캉֪.H%8BڷP8X-rqLsxpCopmjoCFJv"FʖҾA``@~qE-%(rB yPSjktJo3H Ry{z4@kbw ^kX Q|q* Mszeqr |xB3{+hs>kxU(Z M5+1٢pM#p3QC#AC2# #ZVRsbIyΡ9Z\8\PG!S^.-*Q ZTG%hQJH „ AhNsPA ZP<%jAlcԂ9(QKwώY^l uG%jQEmlbJu=<8i43^˫$hyfy @Pex'R² )jZ:Z)$?Ĭ"\B,T)F2;)5]TjH^CPK!\wi"b\v$%@"pdr vv̠!~2qFM!? ag4=\2  cպDMICE5Tplpl;vfiUcoÐ!\ڠ!!rH"<']YjJ,Nd6߁kPC8n8WRSbQb~5kcœks Sfq`/, j 9!i/d vIAH&ĶG%vJcPH툓H툓Ev)2Z4dҲX,bJ},[S* ƜR_.#t(5QfVVBf$ 5/!r£N.VĤ:.RyeD $hUxYĤA5ԐHАĈ&_)g ʳZ9HMIu.=K¦!2Zv zӀqgmr;F Qd2r]FC.(c+e!V+p*٨ATOtN-xa %(IhP춠T~2 Lgs)n׈ta$,(ll˂r]Er[ś´]jT*n*\M8 P[+aVNmW͡VcUG}[~-2YҲIBXHTܲiUyt*9z,[eR.GuLm24-m'kGJ= ~׹ܐV}\?IYOw-:Tl߯xV~9ȯmqH|S#ݫ~Z7ֽͦm5V^ƶ-ϯ' ;ʘUvf[zf93m̕m⇾l+M_l78l /,|p~!Jc֟"B_B;v" rf9EtGV ;!:11tvZpxna\ͮ~eq!,χY?^9={wWK~>~8}?4 endstream endobj 515 0 obj << /Type /XRef /Index [0 516] /Size 516 /W [1 3 1] /Root 513 0 R /Info 514 0 R /ID [<4D16052DDBB1A704285BBBF9414D5561> <4D16052DDBB1A704285BBBF9414D5561>] /Length 1205 /Filter /FlateDecode >> stream x9lUxNlۉgqı&IRh D"A h"CA(( !N̝;3/˲E).R3ͨcJ@ 3*{ Xb,86X\(b`5eA5w lՁzFbbfn nuɪ*Dd2R১1Z$+}mܱIjK7_*k,@"Rapq N`Y}$zEj?N\| g9Nܦ:Ouj 4\`Xƒ+$.WS`Ltwu3`,1ϼHqv, up`EBe  C[C[CVC3?CLCL 1v$4$4$4ԳjPjY \fpzApO\ >`u111,1,1,1,1,1,1,1,aܬOda<_ AbbbyW60°Ć%6HHHHH߰ĦA}Ydx<0ܰy{m*A|Q:*iDn"kĽKs s s s s s i i i i i,%iu˿av=l'n@J5ԁz4&@[J{A hDM@ȫ58 8 %>p4DI,o$ZFE@NExxv4hiqpLI0iD"-E֑ Xk:{'x݀<PPPPPPP46B &y{56 6AyŽ( rI8KZqHJmPP_\ah5'B&ObPP؅BcB N,!E E E E E E : EƞTIb́̕̕̕̕̕̕̕̕uFkgE^՜&G5/?G ލjQJ϶ZmT˒w6D"oQJQI}{T%ɟȡGZhhhhhhhDU'7'7g:: IIII888888888888n8n88A9A9A9A9x##'#G$'('('('('(00'dgz_c:1IE2 endstream endobj startxref 263530 %%EOF pktanon-master/Makefile.am0000644000000000000000000000121412701423271014550 0ustar rootroot# SUBDIRS = src . tests tests/transformations SUBDIRS = libpktanon src . tests # dist_doc_DATA = README include $(top_srcdir)/include.mk AM_CPPFLAGS += -I$(top_srcdir)/libpktanon ############################################################################### ### pktanon binary ############################################################################### bin_PROGRAMS = pktanon pktanon_SOURCES = src/Main.cpp src/console_utils.cpp pktanon_DEPENDENCIES = libpktanon/libpktanon.a src/libpktanonruntime.a pktanon_LDADD = src/libpktanonruntime.a $(LDADD) pktanon_LDFLAGS = -Wl,--whole-archive,libpktanon/libpktanon.a,--no-whole-archivepktanon-master/.gitignore0000644000000000000000000000100712701423271014504 0ustar rootroot# Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Autotools **/Makefile **/Makefile.in /aclocal.m4 /autom4te.cache/ /ar-lib /config.* /configure /depcomp /install-sh /libtool /ltmain.sh /missing /stamp-h? .deps/ .dirstamp # DejaGnu **/out.pcap *.log *.sum **/site.bak **/site.exp # KDeveloper *.kdev4 *.kdev_include_paths *.kate-swp # Compiled Dynamic libraries *.so *.dylib *.dll # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app pktanon anontester pktanon-master/include.mk0000644000000000000000000000055712701423271014501 0ustar rootrootAUTOMAKE_OPTIONS = subdir-objects AM_CPPFLAGS = -O2 -pipe -Werror=return-type -I$(top_srcdir)/include LDADD = $(libnettle_LIBS) $(libhogweed_LIBS) $(xerces_LIBS) # debug version: AM_CPPFLAGS += -g AM_LDFLAGS = -g # AM_CPPFLAGS += -DNOXMLCONFIG -DTRACE_ENABLED AM_CPPFLAGS += -DTRACE_ENABLED if USE_LIBPCAP AM_CPPFLAGS += -DHAVE_LIBPCAP LDADD += -lpcap endif pktanon-master/include/0000755000000000000000000000000012701423271014141 5ustar rootrootpktanon-master/include/PktAnon.h0000644000000000000000000000162212701423271015665 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ # ifndef PKTANON_PKTANON_H # define PKTANON_PKTANON_H # include # include namespace pktanon { class Transformation; class PktAnon { public: static void initialize ( std::string config_filename ); static bool set_link_type ( std::uint32_t link_type ) noexcept; static int transform_packet ( const std::uint8_t* source_buffer, std::uint8_t* destination_buffer, unsigned max_packet_length ) noexcept; static int get_erroneus_packet_length ( int packet_length ); static const char* get_error_string ( int packet_length ); private: static Transformation* link_layer_transformation; }; } #endif // PKTANON_PKTANON_H pktanon-master/include/PktAnonC.h0000644000000000000000000000147712701423271016000 0ustar rootroot/** * Copyright (c) 2014, Institute of Telematics, Karlsruhe Institute of Technology. * * This file is part of the PktAnon project. PktAnon is distributed under 2-clause BSD licence. * See LICENSE file found in the top-level directory of this distribution. */ #ifndef PKTANON__PKTANONC_H #define PKTANON__PKTANONC_H #include #include #ifdef __cplusplus extern "C" { #endif void PKTANON__initialize ( char* config_file, int verbose ); int PKTANON__set_link_type ( uint32_t linktype ); int PKTANON__transform_packet ( uint8_t* source_buffer, uint8_t* destination_buffer, unsigned max_packet_length ); int PKTANON__get_erroneus_packet_length ( const int packet_length ); const char* PKTANON__get_error_string ( const int packet_length ); #ifdef __cplusplus } #endif #endif // PKTANON__PKTANONC_H pktanon-master/configure.ac0000644000000000000000000000664112701423271015013 0ustar rootrootAC_PREREQ(2.68) AC_INIT([pktanon], [2.0-beta], [bless@kit.edu]) AM_INIT_AUTOMAKE([-Wall, -Werror foreign]) AC_CONFIG_SRCDIR([src/Main.cpp]) AC_CONFIG_MACRO_DIRS([m4]) ## by default non-verbose output, override with make V=1 AM_SILENT_RULES ################################################################################# # Source Code Config ################################################################################# AC_PROG_CXX AM_PROG_AR AC_PROG_RANLIB AC_PROG_INSTALL PKG_PROG_PKG_CONFIG AC_CHECK_HEADERS([stddef.h],[],[AC_MSG_ERROR(["missing required headers"])]) AX_CXX_COMPILE_STDCXX_11([noext],[mandatory]) #xerces-c is required for configuration PKG_CHECK_MODULES([xerces], [xerces-c]) # libnettle is required for hash functions PKG_CHECK_MODULES([libnettle], [nettle], [havenettle=true], [AC_MSG_WARN([[ ***** libnettle was not found... AnonHashSha* will not be available ***** ]])] ) AM_CONDITIONAL([USE_NETTLE], [test x$havenettle = xtrue]) # include bytewise hash primitives (disabled by default) AC_ARG_ENABLE(bytewisehashanons, [[ --enable-bytewisehashanons enables AnonBytewiseHashSha1 and AnonBytewiseHashHmacSha1 ** WARNING ** these primitives are likely not secure!! ]], [case "${enableval}" in yes) # check if crypto library is present if [test x$havenettle = xtrue]; then bytewisehashanons=true else AC_MSG_ERROR([[ ***** cannot enable AnonBytewiseHash* primitives... crypto library not found... please disable bytewisehashanons ***** ]]) fi ;; no) bytewisehashanons=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-bytewisehashanons) ;; esac], [bytewisehashanons=false]) AM_CONDITIONAL(BYTEWISEHASHANONS, test x$bytewisehashanons = xtrue) # check requirements for raw sockets # check requirements for (inet) sockets AC_CHECK_FUNCS([socket],[],[AC_MSG_ERROR(["require sockets"])]) AC_CHECK_HEADERS([sys/socket.h arpa/inet.h],[],[AC_MSG_ERROR(["missing headers required for sockets"])]) # TODO: include conditionally # # AM_CONDITIONAL([USE_SOCKETOUTPUT], [test "ac_cv_func_socket" = yes -a "ac_cv_header_sys_socket_h" = yes ]) # check requirements for raw sockets AC_CHECK_HEADERS([sys/ioctl.h netinet/in.h linux/if_ether.h netpacket/packet.h net/if.h sys/ioctl.h fcntl.h],[],[AC_MSG_ERROR(["missing headers required for raw sockets"])]) # TODO: include conditionally # # check libpcap AC_CHECK_HEADERS([pcap/pcap.h]) AC_CHECK_LIB([pcap],[pcap_create],[echo $ac_cv_lib_pcap_pcap_create]) AM_CONDITIONAL([USE_LIBPCAP], [test "$ac_cv_lib_pcap_pcap_create" = yes]) # libhogweed is a part of libnettle that depends on glib for bigint values, it might be needed for # public key cryptography # PKG_CHECK_MODULES([libhogweed], [hogweed], # [HAVE_HOGWEED=1], # [AC_MSG_WARN([[ # ***** # libnettle/libhogweed was not found... AnonHashHmac* will not be available # ***** # ]])] # ) # AM_CONDITIONAL([INC_NETTLE_HMACS], [test "$cryptolib" -eq "hogweed"]) ################################################################################# # Testing ##################################################################### #AC_ARG_ENABLE(debug) ################################################################################# # Last ##################################################################### AC_CONFIG_FILES([ Makefile src/Makefile libpktanon/Makefile tests/Makefile ]) AC_OUTPUT pktanon-master/m4/0000755000000000000000000000000012701423271013036 5ustar rootrootpktanon-master/m4/ax_cxx_compile_stdcxx_11.m40000644000000000000000000001044212701423271020201 0ustar rootroot# ============================================================================ # http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html # ============================================================================ # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the C++11 # standard; if necessary, add switches to CXXFLAGS to enable support. # # The first argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for an extended mode. # # The second argument, if specified 'mandatory' or if left unspecified, # indicates that baseline C++11 support is required and that the macro # should error out if no mode with that support is found. If specified # 'optional', then configuration proceeds regardless, after defining # HAVE_CXX11 if and only if a supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 3 m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [ template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; typedef check> right_angle_brackets; int a; decltype(a) b; typedef check check_type; check_type c; check_type&& cr = static_cast(c); auto d = a; ]) AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl m4_if([$1], [], [], [$1], [ext], [], [$1], [noext], [], [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], [$2], [optional], [ax_cxx_compile_cxx11_required=false], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])])dnl AC_LANG_PUSH([C++])dnl ac_success=no AC_CACHE_CHECK(whether $CXX supports C++11 features by default, ax_cv_cxx_compile_cxx11, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], [ax_cv_cxx_compile_cxx11=yes], [ax_cv_cxx_compile_cxx11=no])]) if test x$ax_cv_cxx_compile_cxx11 = xyes; then ac_success=yes fi m4_if([$1], [noext], [], [dnl if test x$ac_success = xno; then for switch in -std=gnu++11 -std=gnu++0x; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, $cachevar, [ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], [eval $cachevar=yes], [eval $cachevar=no]) CXXFLAGS="$ac_save_CXXFLAGS"]) if eval test x\$$cachevar = xyes; then CXXFLAGS="$CXXFLAGS $switch" ac_success=yes break fi done fi]) m4_if([$1], [ext], [], [dnl if test x$ac_success = xno; then for switch in -std=c++11 -std=c++0x; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, $cachevar, [ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], [eval $cachevar=yes], [eval $cachevar=no]) CXXFLAGS="$ac_save_CXXFLAGS"]) if eval test x\$$cachevar = xyes; then CXXFLAGS="$CXXFLAGS $switch" ac_success=yes break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx11_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) fi else if test x$ac_success = xno; then HAVE_CXX11=0 AC_MSG_NOTICE([No compiler with C++11 support was found]) else HAVE_CXX11=1 AC_DEFINE(HAVE_CXX11,1, [define if the compiler supports basic C++11 syntax]) fi AC_SUBST(HAVE_CXX11) fi ]) pktanon-master/m4/ax_lib_pcap.m40000644000000000000000000000416212701423271015544 0ustar rootroot# SYNOPSIS # # AX_LIB_PCAP([ACTION-IF-TRUE], [ACTION-IF-FALSE]) # # DESCRIPTION # # This macro will check for the existence of libpcap # (http://www.tcpdump.org/). It does this by checking for the header # file pcap/pcap.h and the pcap library object file. A --with-libpcap # option is supported as well. The following output variables are set # with AC_SUBST: # # PCAP_CPPFLAGS # PCAP_LDFLAGS # PCAP_LIBS # # You can use them like this in Makefile.am: # # AM_CPPFLAGS = $(PCAP_CPPFLAGS) # AM_LDFLAGS = $(PCAP_LDFLAGS) # program_LDADD = $(PCAP_LIBS) # # Additionally, the C preprocessor symbol HAVE_LIBPCAP will be defined # with AC_DEFINE if libpcap is available. # # LICENSE # # Derived from AX_LIB_UPNP which is Copyright (c) 2009 Oskar Liljeblad # by Philipp Kern in 2011. # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 5 AU_ALIAS([AC_LIB_PCAP], [AX_LIB_PCAP]) AC_DEFUN([AX_LIB_PCAP], [ AH_TEMPLATE([HAVE_LIBPCAP], [Define if libpcap is available]) AC_ARG_WITH(libpcap, [ --with-libpcap=DIR prefix for pcap library files and headers], [ if test "$withval" = "no"; then ac_pcap_path= $2 elif test "$withval" = "yes"; then ac_pcap_path=/usr else ac_pcap_path="$withval" fi ],[ac_pcap_path=/usr]) if test "$ac_pcap_path" != ""; then saved_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I$ac_pcap_path/include" AC_CHECK_HEADER([pcap/pcap.h], [ saved_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -L$ac_pcap_path/lib" AC_CHECK_LIB(pcap, pcap_create, [ AC_SUBST(PCAP_CPPFLAGS, [-I$ac_pcap_path/include]) AC_SUBST(PCAP_LDFLAGS, [-L$ac_pcap_path/lib]) AC_SUBST(PCAP_LIBS, [-lpcap]) AC_DEFINE([HAVE_LIBPCAP]) $1 ], [ : $2 ]) LDFLAGS="$saved_LDFLAGS" ], [ AC_MSG_RESULT([not found]) $2 ]) CPPFLAGS="$saved_CPPFLAGS" fi ])