├── .document ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ └── verify.yml ├── .gitignore ├── .mailmap ├── .rspec ├── .travis.yml ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bench ├── after-2012-07-28.txt ├── before-2012-07-28.txt ├── benchit.rb ├── calc_delta.rb ├── octets.rb ├── octets_after.txt ├── octets_after_refactor.txt └── octets_before.txt ├── certs └── todb.pem ├── examples ├── 100kpackets.rb ├── ackscan.rb ├── arp.rb ├── arphood.rb ├── dissect_thinger.rb ├── ethernet.rb ├── ids.rb ├── idsv2.rb ├── ifconfig.rb ├── new-simple-stats.rb ├── oui.txt ├── packetfu-shell.rb ├── pcap2pcapng.rb ├── ping.rb ├── readpcap.rb ├── simple-sniffer.rb ├── simple-stats.rb ├── slammer.rb ├── tcp_handshake.rb └── uniqpcap.rb ├── gem-public_cert.pem ├── lib ├── packetfu.rb └── packetfu │ ├── capture.rb │ ├── common.rb │ ├── config.rb │ ├── inject.rb │ ├── packet.rb │ ├── pcap.rb │ ├── pcapng.rb │ ├── pcapng │ ├── block.rb │ ├── epb.rb │ ├── file.rb │ ├── idb.rb │ ├── shb.rb │ ├── spb.rb │ └── unknown_block.rb │ ├── protos.rb │ ├── protos │ ├── arp.rb │ ├── arp │ │ ├── header.rb │ │ └── mixin.rb │ ├── eth.rb │ ├── eth │ │ ├── header.rb │ │ └── mixin.rb │ ├── hsrp.rb │ ├── hsrp │ │ ├── header.rb │ │ └── mixin.rb │ ├── icmp.rb │ ├── icmp │ │ ├── header.rb │ │ └── mixin.rb │ ├── icmpv6.rb │ ├── icmpv6 │ │ ├── header.rb │ │ └── mixin.rb │ ├── invalid.rb │ ├── ip.rb │ ├── ip │ │ ├── header.rb │ │ └── mixin.rb │ ├── ipv6.rb │ ├── ipv6 │ │ ├── header.rb │ │ └── mixin.rb │ ├── lldp.rb │ ├── lldp │ │ ├── header.rb │ │ └── mixin.rb │ ├── tcp.rb │ ├── tcp │ │ ├── ecn.rb │ │ ├── flags.rb │ │ ├── header.rb │ │ ├── hlen.rb │ │ ├── mixin.rb │ │ ├── option.rb │ │ ├── options.rb │ │ └── reserved.rb │ ├── udp.rb │ └── udp │ │ ├── header.rb │ │ └── mixin.rb │ ├── structfu.rb │ ├── utils.rb │ └── version.rb ├── packetfu.gemspec ├── spec ├── arp_spec.rb ├── capture_spec.rb ├── eth_spec.rb ├── fake_packets.rb ├── hsrp_spec.rb ├── icmp_spec.rb ├── icmpv6_spec.rb ├── inject_spec.rb ├── invalid_spec.rb ├── ip_spec.rb ├── ipv4_icmp.pcap ├── ipv4_udp.pcap ├── ipv6_icmp.pcap ├── ipv6_spec.rb ├── ipv6_udp.pcap ├── lldp_spec.rb ├── octets_spec.rb ├── packet_spec.rb ├── packet_subclasses_spec.rb ├── packetfu_spec.rb ├── pcap_spec.rb ├── pcapng │ ├── epb_spec.rb │ ├── file_spec.rb │ ├── file_spec_helper.rb │ ├── idb_spec.rb │ ├── shb_spec.rb │ ├── spb_spec.rb │ └── unknown_block_spec.rb ├── sample.pcap ├── sample2.pcap ├── sample3.pcap ├── spec_helper.rb ├── structfu_spec.rb ├── tcp_spec.rb ├── udp_spec.rb ├── utils_spec.rb └── vlan-pcapr.cap └── test ├── all_tests.rb ├── pcapng-test ├── output_be │ ├── advanced │ │ ├── test100.pcapng │ │ ├── test100.txt │ │ ├── test101.pcapng │ │ ├── test101.txt │ │ ├── test102.pcapng │ │ └── test102.txt │ ├── basic │ │ ├── test001.pcapng │ │ ├── test001.txt │ │ ├── test002.pcapng │ │ ├── test002.txt │ │ ├── test003.pcapng │ │ ├── test003.txt │ │ ├── test004.pcapng │ │ ├── test004.txt │ │ ├── test005.pcapng │ │ ├── test005.txt │ │ ├── test006.pcapng │ │ ├── test006.txt │ │ ├── test007.pcapng │ │ ├── test007.txt │ │ ├── test008.pcapng │ │ ├── test008.txt │ │ ├── test009.pcapng │ │ ├── test009.txt │ │ ├── test010.pcapng │ │ ├── test010.txt │ │ ├── test011.pcapng │ │ ├── test011.txt │ │ ├── test012.pcapng │ │ ├── test012.txt │ │ ├── test013.pcapng │ │ ├── test013.txt │ │ ├── test014.pcapng │ │ ├── test014.txt │ │ ├── test015.pcapng │ │ ├── test015.txt │ │ ├── test016.pcapng │ │ ├── test016.txt │ │ ├── test017.pcapng │ │ ├── test017.txt │ │ ├── test018.pcapng │ │ └── test018.txt │ └── difficult │ │ ├── test200.pcapng │ │ ├── test200.txt │ │ ├── test201.pcapng │ │ ├── test201.txt │ │ ├── test202.pcapng │ │ └── test202.txt └── output_le │ ├── advanced │ ├── test100.pcapng │ ├── test100.txt │ ├── test101.pcapng │ ├── test101.txt │ ├── test102.pcapng │ └── test102.txt │ ├── basic │ ├── test001.pcapng │ ├── test001.txt │ ├── test002.pcapng │ ├── test002.txt │ ├── test003.pcapng │ ├── test003.txt │ ├── test004.pcapng │ ├── test004.txt │ ├── test005.pcapng │ ├── test005.txt │ ├── test006.pcapng │ ├── test006.txt │ ├── test007.pcapng │ ├── test007.txt │ ├── test008.pcapng │ ├── test008.txt │ ├── test009.pcapng │ ├── test009.txt │ ├── test010.pcapng │ ├── test010.txt │ ├── test011.pcapng │ ├── test011.txt │ ├── test012.pcapng │ ├── test012.txt │ ├── test013.pcapng │ ├── test013.txt │ ├── test014.pcapng │ ├── test014.txt │ ├── test015.pcapng │ ├── test015.txt │ ├── test016.pcapng │ ├── test016.txt │ ├── test017.pcapng │ ├── test017.txt │ ├── test018.pcapng │ └── test018.txt │ └── difficult │ ├── test200.pcapng │ ├── test200.txt │ ├── test201.pcapng │ ├── test201.txt │ ├── test202.pcapng │ └── test202.txt ├── sample-ipv6.pcap ├── sample-ipv6.pcapng ├── sample-spb.pcapng ├── sample.pcap ├── sample.pcapng ├── sample2.pcap ├── sample2.pcapng ├── sample_hsrp_pcapr.cap ├── sample_lldp.pcap ├── test_packet.rb ├── test_tcp.rb └── vlan-pcapr.cap /.document: -------------------------------------------------------------------------------- 1 | lib/packetfu.rb 2 | lib/packetfu/ 3 | - 4 | LICENSE.txt 5 | INSTALL.rdoc 6 | README.rdoc 7 | examples/*.rb -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Steps to reproduce 2 | 3 | How'd you do it? 4 | 5 | 1. ... 6 | 2. ... 7 | 8 | If relevant, please attach relevant PoC code or PCAPs to help with speedy reproduction. 9 | 10 | ## Expected behavior 11 | 12 | What should happen? 13 | 14 | ## Current behavior 15 | 16 | What happens instead? 17 | 18 | ## System stuff 19 | 20 | ### Versions 21 | 22 | - Ruby Version (ruby -v): FILL_ME_IN 23 | - PacketFu Version (gem list|grep packetfu -or- git log -1): FILL_ME_IN 24 | - OS Version: FILL_ME_IN 25 | 26 | ### I installed PacketFu from: 27 | - [ ] Git Source 28 | - [ ] Gem 29 | 30 | -------------------------------------------------------------------------------- /.github/workflows/verify.yml: -------------------------------------------------------------------------------- 1 | name: Verify 2 | 3 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions 4 | permissions: 5 | actions: none 6 | checks: none 7 | contents: none 8 | deployments: none 9 | id-token: none 10 | issues: none 11 | discussions: none 12 | packages: none 13 | pages: none 14 | pull-requests: none 15 | repository-projects: none 16 | security-events: none 17 | statuses: none 18 | 19 | on: 20 | push: 21 | branches: 22 | - '*' 23 | pull_request: 24 | branches: 25 | - '*' 26 | 27 | jobs: 28 | verify: 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | os: 33 | - macos-latest 34 | - ubuntu-latest 35 | ruby: 36 | - '2.7' 37 | - '3.0' 38 | - '3.1' 39 | - '3.2' 40 | - '3.3.0-preview1' 41 | runs-on: ${{ matrix.os }} 42 | timeout-minutes: 25 43 | 44 | name: ${{ matrix.os }} - Ruby ${{ matrix.ruby }} 45 | 46 | env: 47 | COVERALLS_SKIP: "true" 48 | 49 | steps: 50 | - name: Checkout code 51 | uses: actions/checkout@v3 52 | with: 53 | fetch-depth: 0 54 | 55 | - name: Install system dependencies (Linux) 56 | if: runner.os == 'Linux' 57 | run: | 58 | sudo apt-get install libpcap-dev 59 | 60 | - name: Setup Ruby 61 | env: 62 | BUNDLE_WITHOUT: "coverage development" 63 | uses: ruby/setup-ruby@v1 64 | with: 65 | ruby-version: ${{ matrix.ruby }} 66 | bundler-cache: false 67 | 68 | - name: Run tests 69 | run: | 70 | bundle install 71 | bundle exec rspec 72 | 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.swp 3 | *~ 4 | doc/ 5 | .yardoc/ 6 | pkg/ 7 | test/*test.pcap 8 | Gemfile.lock 9 | .ruby-gemset* 10 | .ruby-version* 11 | coverage 12 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Tod Beardsley Tod Beardsley 2 | Tod Beardsley Tod Beardsley 3 | Tod Beardsley Tod Beardsley 4 | Tod Beardsley todb 5 | 6 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | addons: 3 | apt: 4 | packages: 5 | - libpcap-dev 6 | os: 7 | - linux 8 | - osx 9 | rvm: 10 | - 2.4.1 11 | - 2.4.0 12 | - 2.3.4 13 | - 2.2.7 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to PacketFu 2 | 3 | Thanks for your interest in contributing to PacketFu. 4 | 5 | If you could follow the following guidelines, you will make it much easier for 6 | us to give feedback, help you find whatever problem you have and fix it. 7 | 8 | ## Issues 9 | 10 | If you have questions of any kind, or are unsure of how something works, please 11 | [create an issue](https://github.com/packetfu/packetfu/issues/new). 12 | 13 | Please try to answer the following questions in your issue: 14 | 15 | - What did you do? 16 | - What did you expect to happen? 17 | - What happened instead? 18 | 19 | If you have identified a bug, it would be very helpful if you could include a 20 | way to replicate the bug. Ideally a failing test would be perfect, but even a 21 | simple script demonstrating the error would suffice. 22 | 23 | Feature requests are great and if submitted they will be considered for 24 | inclusion, but sending a pull request is much more awesome. 25 | 26 | ## Pull Requests 27 | 28 | If you want your pull requests to be accepted, please follow the following guidelines: 29 | 30 | - [**Add tests!**](http://rspec.info/) Your patch won't be accepted (or will be delayed) if it doesn't have tests. 31 | 32 | - [**Document any change in behaviour**](http://yardoc.org/) Make sure the README and any other 33 | relevant documentation are kept up-to-date. 34 | 35 | - [**Create topic branches**](https://github.com/dchelimsky/rspec/wiki/Topic-Branches) Don't ask us to pull from your master branch. 36 | 37 | - [**One pull request per feature**](https://help.github.com/articles/using-pull-requests) If you want to do more than one thing, send 38 | multiple pull requests. 39 | 40 | - [**Send coherent history**](http://stackoverflow.com/questions/6934752/git-combining-multiple-commits-before-pushing) Make sure each individual commit in your pull 41 | request is meaningful. If you had to make multiple intermediate commits while 42 | developing, please squash them before sending them to us. 43 | 44 | - [**Follow coding conventions**](https://github.com/styleguide/ruby) The standard Ruby stuff, two spaces indent, 45 | don't omit parens unless you have a good reason. 46 | 47 | Thank you so much for contributing! -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in packetfu.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2017, Tod Beardsley 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of Tod Beardsley nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY TOD BEARDSLEY ''AS IS'' AND ANY 17 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL TOD BEARDSLEY BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | 2 | begin 3 | require 'rspec/core/rake_task' 4 | rescue LoadError 5 | $stderr.puts "rspec not available, so can't set up spec tasks." 6 | else 7 | RSpec::Core::RakeTask.new 8 | 9 | task :default => :spec 10 | end 11 | 12 | -------------------------------------------------------------------------------- /bench/after-2012-07-28.txt: -------------------------------------------------------------------------------- 1 | Parsing a TCP Packet... 2 | user system total real 3 | PacketFu::Packet.parse() 1.670000 0.010000 1.680000 ( 1.664486) 4 | PacketFu::EthPacket.new.read() 0.180000 0.000000 0.180000 ( 0.179386) 5 | PacketFu::IPPacket.new.read() 0.410000 0.000000 0.410000 ( 0.415767) 6 | PacketFu::TCPPacket.new.read() 1.380000 0.000000 1.380000 ( 1.375509) 7 | 8 | Parsing a UDP Packet... 9 | user system total real 10 | PacketFu::Packet.parse() 1.750000 0.000000 1.750000 ( 1.757758) 11 | PacketFu::EthPacket.new.read() 0.180000 0.000000 0.180000 ( 0.179951) 12 | PacketFu::IPPacket.new.read() 0.420000 0.000000 0.420000 ( 0.416224) 13 | PacketFu::UDPPacket.new.read() 0.720000 0.000000 0.720000 ( 0.715579) 14 | 15 | Parsing a ARP Packet... 16 | user system total real 17 | PacketFu::Packet.parse() 0.820000 0.000000 0.820000 ( 0.823303) 18 | PacketFu::EthPacket.new.read() 0.180000 0.000000 0.180000 ( 0.182247) 19 | PacketFu::ARPPacket.new.read() 0.670000 0.000000 0.670000 ( 0.672712) 20 | 21 | Parsing a IPv6 Packet... 22 | user system total real 23 | PacketFu::Packet.parse() 0.700000 0.000000 0.700000 ( 0.695721) 24 | PacketFu::EthPacket.new.read() 0.180000 0.000000 0.180000 ( 0.180608) 25 | PacketFu::IPv6Packet.new.read() 0.530000 0.000000 0.530000 ( 0.527124) 26 | -------------------------------------------------------------------------------- /bench/before-2012-07-28.txt: -------------------------------------------------------------------------------- 1 | Parsing a TCP Packet... 2 | user system total real 3 | PacketFu::Packet.parse() 3.290000 0.000000 3.290000 ( 3.290771) 4 | PacketFu::EthPacket.new.read() 0.180000 0.000000 0.180000 ( 0.181850) 5 | PacketFu::IPPacket.new.read() 0.490000 0.000000 0.490000 ( 0.484150) 6 | PacketFu::TCPPacket.new.read() 2.970000 0.000000 2.970000 ( 2.977186) 7 | 8 | Parsing a UDP Packet... 9 | user system total real 10 | PacketFu::Packet.parse() 2.530000 0.010000 2.540000 ( 2.529333) 11 | PacketFu::EthPacket.new.read() 0.180000 0.000000 0.180000 ( 0.179065) 12 | PacketFu::IPPacket.new.read() 0.490000 0.000000 0.490000 ( 0.487412) 13 | PacketFu::UDPPacket.new.read() 1.060000 0.000000 1.060000 ( 1.067339) 14 | 15 | Parsing a ARP Packet... 16 | user system total real 17 | PacketFu::Packet.parse() 0.860000 0.000000 0.860000 ( 0.851042) 18 | PacketFu::EthPacket.new.read() 0.180000 0.000000 0.180000 ( 0.183220) 19 | PacketFu::ARPPacket.new.read() 0.720000 0.000000 0.720000 ( 0.718533) 20 | 21 | Parsing a IPv6 Packet... 22 | user system total real 23 | PacketFu::Packet.parse() 0.750000 0.000000 0.750000 ( 0.753657) 24 | PacketFu::EthPacket.new.read() 0.180000 0.000000 0.180000 ( 0.182015) 25 | PacketFu::IPv6Packet.new.read() 0.610000 0.000000 0.610000 ( 0.611805) 26 | -------------------------------------------------------------------------------- /bench/benchit.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib") 2 | require 'packetfu' 3 | require 'benchmark' 4 | class String 5 | def bin 6 | self.scan(/../).map {|x| x.to_i(16).chr}.join 7 | end 8 | end 9 | 10 | file_pfx = ARGV.shift 11 | 12 | IPV6_PACKET = "3333000000fb442a60c14d7b86dd60000000006611fffe80000000000000462a60fffec14d7bff0200000000000000000000000000fb14e914e900664ed1000000000002000000020000145542432d437573746f6d6572732d695061642d33056c6f63616c0000ff0001c00c00ff0001c00c001c0001000000780010fe80000000000000462a60fffec14d7bc00c0001000100000078000448d77827".bin 13 | 14 | ARP_PACKET = "ffffffffffff001e6837bcf708060001080006040001001e6837bcf748d7780100000000000048d779f7000000000000000000000000000000000000".bin 15 | 16 | UDP_PACKET = "01005e7ffffa100ba9eb63400800450000a12d7c0000011159b446a5fb7ceffffffacdf3076c008d516e4d2d534541524348202a20485454502f312e310d0a486f73743a3233392e3235352e3235352e3235303a313930300d0a53543a75726e3a736368656d61732d75706e702d6f72673a6465766963653a496e7465726e6574476174657761794465766963653a310d0a4d616e3a22737364703a646973636f766572220d0a4d583a330d0a0d0a".bin 17 | 18 | TCP_PACKET = "e0f8472161a600254ba0760608004500004403554000400651d0c0a83207c0a832370224c1d22d94847f0b07c4ba8018ffff30ba00000101080a8731821433564b8c01027165000000000000200000000000".bin 19 | 20 | iters = 5_000 21 | data = [] 22 | data = [] 23 | data = [] 24 | data = [] 25 | puts "Parsing a TCP Packet..." 26 | require 'pp' 27 | Benchmark.bm do |bm| 28 | data << bm.report("PacketFu::Packet.parse() ") { iters.times {PacketFu::Packet.parse(TCP_PACKET)} } 29 | data << bm.report("PacketFu::EthPacket.new.read() ") { iters.times {PacketFu::EthPacket.new.read(TCP_PACKET)} } 30 | data << bm.report("PacketFu::IPPacket.new.read() ") { iters.times {PacketFu::IPPacket.new.read(TCP_PACKET)} } 31 | data << bm.report("PacketFu::TCPPacket.new.read() ") { iters.times {PacketFu::TCPPacket.new.read(TCP_PACKET)} } 32 | nil 33 | end 34 | 35 | puts "" 36 | puts "Parsing a UDP Packet..." 37 | Benchmark.bm do |bm| 38 | data << bm.report("PacketFu::Packet.parse() ") { iters.times {PacketFu::Packet.parse(UDP_PACKET)} } 39 | data << bm.report("PacketFu::EthPacket.new.read() ") { iters.times {PacketFu::EthPacket.new.read(UDP_PACKET)} } 40 | data << bm.report("PacketFu::IPPacket.new.read() ") { iters.times {PacketFu::IPPacket.new.read(UDP_PACKET)} } 41 | data << bm.report("PacketFu::UDPPacket.new.read() ") { iters.times {PacketFu::UDPPacket.new.read(UDP_PACKET)} } 42 | nil 43 | end 44 | 45 | puts "" 46 | puts "Parsing a ARP Packet..." 47 | Benchmark.bm do |bm| 48 | data << bm.report("PacketFu::Packet.parse() ") { iters.times {PacketFu::Packet.parse(ARP_PACKET)} } 49 | data << bm.report("PacketFu::EthPacket.new.read() ") { iters.times {PacketFu::EthPacket.new.read(ARP_PACKET)} } 50 | data << bm.report("PacketFu::ARPPacket.new.read() ") { iters.times {PacketFu::ARPPacket.new.read(ARP_PACKET)} } 51 | nil 52 | end 53 | 54 | puts "" 55 | puts "Parsing a IPv6 Packet..." 56 | Benchmark.bm do |bm| 57 | data << bm.report("PacketFu::Packet.parse() ") { iters.times {PacketFu::Packet.parse(IPV6_PACKET)} } 58 | data << bm.report("PacketFu::EthPacket.new.read() ") { iters.times {PacketFu::EthPacket.new.read(IPV6_PACKET)} } 59 | data << bm.report("PacketFu::IPv6Packet.new.read() ") { iters.times {PacketFu::IPv6Packet.new.read(IPV6_PACKET)} } 60 | nil 61 | end 62 | if file_pfx 63 | filename = "#{file_pfx}.dat" 64 | puts "dumping data to #{filename}" 65 | fio = File.open(filename, "w") 66 | Marshal.dump(data, fio) 67 | fio.close 68 | end 69 | -------------------------------------------------------------------------------- /bench/calc_delta.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | require 'pp' 3 | 4 | before = ARGV.shift 5 | after = ARGV.shift 6 | 7 | fio = File.open(before) 8 | before_data = Marshal.load(fio) 9 | fio.close 10 | 11 | fio = File.open(after) 12 | after_data = Marshal.load(fio) 13 | fio.close 14 | 15 | before_data.each_with_index do |data, i| 16 | puts (data.total / after_data[i].total) 17 | end 18 | -------------------------------------------------------------------------------- /bench/octets.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib") 3 | require 'packetfu' 4 | 5 | IPV4_RAW = "\x01\x02\x03\x04" 6 | IPV4_STR = "1.2.3.4" 7 | 8 | 9 | iters = 50_000 10 | Benchmark.bm do |bm| 11 | bm.report("Octets.new.read(...) ") {iters.times {PacketFu::Octets.new.read(IPV4_RAW)}} 12 | bm.report("Octets.new.read_quad(...) ") {iters.times {PacketFu::Octets.new.read_quad(IPV4_STR)}} 13 | 14 | octets = PacketFu::Octets.new 15 | bm.report("octets#read(...) ") {iters.times {octets.read(IPV4_RAW)}} 16 | bm.report("octets#read_quad(...) ") {iters.times {octets.read_quad(IPV4_STR)}} 17 | 18 | octets.read(IPV4_RAW) 19 | bm.report("octets#to_x() ") {iters.times {octets.to_x}} 20 | bm.report("octets#to_i() ") {iters.times {octets.to_i}} 21 | bm.report("octets#to_s() ") {iters.times {octets.to_s}} 22 | end 23 | -------------------------------------------------------------------------------- /bench/octets_after.txt: -------------------------------------------------------------------------------- 1 | user system total real 2 | Octets.new.read(...) 0.480000 0.000000 0.480000 ( 0.485710) 3 | Octets.new.read_quad(...) 0.480000 0.000000 0.480000 ( 0.480071) 4 | octets#read(...) 0.180000 0.000000 0.180000 ( 0.184558) 5 | octets#read_quad(...) 0.180000 0.000000 0.180000 ( 0.175781) 6 | octets#to_x() 0.120000 0.000000 0.120000 ( 0.120217) 7 | octets#to_i() 0.040000 0.000000 0.040000 ( 0.043496) 8 | octets#to_s() 0.100000 0.010000 0.110000 ( 0.093341) 9 | -------------------------------------------------------------------------------- /bench/octets_after_refactor.txt: -------------------------------------------------------------------------------- 1 | user system total real 2 | Octets.new.read(...) 0.200000 0.000000 0.200000 ( 0.207577) 3 | Octets.new.read_quad(...) 0.290000 0.000000 0.290000 ( 0.285364) 4 | octets#read(...) 0.080000 0.000000 0.080000 ( 0.074826) 5 | octets#read_quad(...) 0.140000 0.000000 0.140000 ( 0.145170) 6 | octets#to_x() 0.140000 0.000000 0.140000 ( 0.139427) 7 | octets#to_i() 0.020000 0.000000 0.020000 ( 0.016444) 8 | octets#to_s() 0.040000 0.000000 0.040000 ( 0.042925) 9 | -------------------------------------------------------------------------------- /bench/octets_before.txt: -------------------------------------------------------------------------------- 1 | user system total real 2 | Octets.new.read(...) 0.480000 0.000000 0.480000 ( 0.482913) 3 | Octets.new.read_quad(...) 1.390000 0.460000 1.850000 ( 1.858652) 4 | octets#read(...) 0.190000 0.000000 0.190000 ( 0.186785) 5 | octets#read_quad(...) 1.030000 0.400000 1.430000 ( 1.435857) 6 | octets#to_x() 1.010000 0.470000 1.480000 ( 1.480452) 7 | octets#to_i() 0.830000 0.420000 1.250000 ( 1.250348) 8 | octets#to_s() 0.150000 0.000000 0.150000 ( 0.157041) 9 | -------------------------------------------------------------------------------- /certs/todb.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIENDCCApygAwIBAgIBATANBgkqhkiG9w0BAQsFADAiMSAwHgYDVQQDDBd0b2Ri 3 | L0RDPXBhY2tldGZ1L0RDPWNvbTAeFw0yMzA2MjcwMDExMjdaFw0yNDA2MjYwMDEx 4 | MjdaMCIxIDAeBgNVBAMMF3RvZGIvREM9cGFja2V0ZnUvREM9Y29tMIIBojANBgkq 5 | hkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyz2+nfp+Vv+JVHrJMy5Ck3qWBkiZmE0w 6 | 7bR1I/bNXrCtv75l1GxTdRAoxKcXpXyC8elJQ9PEjEEDtdGBYQ13BKcQbDJ36etD 7 | wjbhRs5SBXgIilJAiR3i/cVnNoNJKOpiJZPufkOpag7Sg8Ze+cWbsc0gYN9nyCmz 8 | LYwWDC6Ji0KgJFw5YxFvIxVeOx86Ccfd64Wsa3EhkZd6fOpDE3029GWDqZwZTBIX 9 | RzJP4M7QZHZjq3gbHgSKFCFv0MqsjnQzUhPyB/U27c/n+wfRzZNx4Y1eRVm7gwPP 10 | LJDzt6mvtlXqc6pQ1NsR9hv3sieFLZDDPU1AaWiOAckIKcVoXB8sGnuFMczMW97o 11 | OZLfqfZUAN6LSY939T2sCyOcGhjxZIQbXtn/R/RAJ7UTfJld9UdthKm1W/f0cjwW 12 | yqoVsFL+nda6xfta9EnRqqFLOJqyX/wwA2cPO9B8EyM8vFEE8AG8wLu1tF3ggoMg 13 | rKut/WQADc3ymKAtsr9bOWajj7bIQVJnAgMBAAGjdTBzMAkGA1UdEwQCMAAwCwYD 14 | VR0PBAQDAgSwMB0GA1UdDgQWBBR2kf7ATPt3O7Lpc0uyyCiow3P88DAcBgNVHREE 15 | FTATgRF0b2RiQHBhY2tldGZ1LmNvbTAcBgNVHRIEFTATgRF0b2RiQHBhY2tldGZ1 16 | LmNvbTANBgkqhkiG9w0BAQsFAAOCAYEAZMp8nY7WzGOPO6TrAlZg37D3s3Rcm0/z 17 | 6DBzFcY4F5CF3xq1Z/DZ3JwhjILaHPAZTvVT4uj91K4BYh/QgteS52C+O/9qsZ25 18 | L3Ocu4Yp+aU40KpjW+IjlzgTS3E21pCBrBTgT7NuTHmTmoNmHfE6Gbbig3a68C9z 19 | LcXj2RaEQuhOKrq5vw/0AV34wRieClM/oW8kWAKJDQ8/WEocHQpO1K/dhQ9hHNir 20 | lMpjKXsWuxdAZPyvNj15w9fw5a4gZgW26P4VBNJUD/iCe7QYhwXrhdhxf+cygW2A 21 | gBCt2UC6yISUDFiajyTw8cTJB1UyfLIADS4hiOEShx7hvVee444bgmOA99C+YuzT 22 | FFUt9KVtWsXKD0R6GBvbAUW4/LjmXCCM+Z3uWo1Ph6zljlNHz6/tg+SB7DVgsI3i 23 | XuSkzAmFsPisZ7uZk/7gJVlmyaqIxdrPVt9ZOTeSc/8pgSoRurHEJ7KlUXv4kcYM 24 | F3a8dA5tl/TC0vkHlCtghhLuD46SlAmH 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /examples/100kpackets.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: binary -*- 3 | 4 | # Used mainly to test for memory leaks and to demo the preferred ways of 5 | # reading and writing packets to and from pcap files. 6 | 7 | # Usage: 8 | # ruby examples/100kpackets.rb 9 | 10 | # Path setting slight of hand: 11 | $: << File.expand_path("../../lib", __FILE__) 12 | require 'packetfu' 13 | 14 | puts "Generating packets... (#{Time.now.utc})" 15 | 16 | File.unlink("/tmp/out.pcap") if File.exist? "/tmp/out.pcap" 17 | start_time = Time.now.utc 18 | count = 0 19 | 20 | 100.times do 21 | @pcaps = [] 22 | 1000.times do 23 | u = PacketFu::UDPPacket.new 24 | u.ip_src = [rand(2**32-1)].pack("N") 25 | u.ip_dst = [rand(2**32-1)].pack("N") 26 | u.recalc 27 | @pcaps << u 28 | end 29 | pfile = PacketFu::PcapFile.new 30 | res = pfile.array_to_file(:filename => "/tmp/out.pcap", :array => @pcaps, :append => true) 31 | count += res.last 32 | puts "Wrote #{count} packets in #{Time.now.utc - start_time} seconds" 33 | end 34 | 35 | read_bytes_start = Time.now.utc 36 | puts "Reading packet bytes..." 37 | packet_bytes = PacketFu::PcapFile.read_packet_bytes "/tmp/out.pcap" 38 | puts "Read #{packet_bytes.size} packet byte blobs in #{Time.now.utc - read_bytes_start} seconds." 39 | 40 | read_packets_start = Time.now.utc 41 | puts "Reading packets..." 42 | packet_bytes = PacketFu::PcapFile.read_packets "/tmp/out.pcap" 43 | puts "Read #{packet_bytes.size} parsed packets in #{Time.now.utc - read_packets_start} seconds." 44 | -------------------------------------------------------------------------------- /examples/ackscan.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: binary -*- 3 | 4 | # Path setting slight of hand: 5 | $: << File.expand_path("../../lib", __FILE__) 6 | require 'packetfu' 7 | 8 | # Portscanning! 9 | # Run this on one machine 10 | #cap = Capture.new(:iface=>'wlan0') # or whatever your interface is 11 | #cap.show_live(:filter => 'src net 209.85.165') 12 | # Run this on another: 13 | #cap = Capture.new(:iface=>'wlan0') # or whatever your interface is 14 | #cap = Capture.new(:iface=>'wlan0') # or whatever your interface is 15 | # Run this on the third 16 | def do_scan 17 | puts "Generating packets..." 18 | pkt_array = gen_packets.sort_by {rand} 19 | puts "Dumping them on the wire..." 20 | inj = PacketFu::Inject.new(:iface => ARGV[0]) 21 | inj.array_to_wire(:array=>pkt_array) 22 | puts "Done!" 23 | end 24 | 25 | def gen_packets 26 | config = PacketFu::Utils.whoami?(:iface=>ARGV[0]) 27 | pkt = PacketFu::TCPPacket.new(:config=>config, :flavor=>"Windows") 28 | pkt.payload ="all I wanna do is ACK ACK ACK and a RST and take your money" 29 | pkt.ip_daddr="209.85.165.0" # One of Google's networks 30 | pkt.tcp_flags.ack=1 31 | pkt.tcp_dst=81 32 | pkt_array = [] 33 | 256.times do |i| 34 | oa = PacketFu::IPHeader.octet_array(pkt.ip_dst)[0,3] + ["#{i}"] 35 | pkt.ip_dst = IPAddr.new(oa.join '.').to_i 36 | pkt.tcp_src = rand(5000 - 1025) + 1025 37 | pkt.recalc 38 | pkt_array << pkt.to_s 39 | end 40 | pkt_array 41 | end 42 | 43 | do_scan 44 | -------------------------------------------------------------------------------- /examples/arp.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | # This is a somewhat contrived and verbose demonstration of how to implement ARP manually. 3 | # 4 | # It's contrived because this is really how PacketFu::Utils got born; something similiar 5 | # (and a wee bit cleaner) is already available as Packet::Utils::arp, since knowing the 6 | # MAC address of a target IP turns out to be pretty useful day-to-day. 7 | 8 | # Path setting slight of hand: 9 | $: << File.expand_path("../../lib", __FILE__) 10 | require 'packetfu' 11 | 12 | def usage 13 | if ARGV[0].nil? 14 | raise ArgumentError, "You need an IP address to start with." 15 | elsif !Process.euid.zero? 16 | raise SecurityError, "You need to be root to run this." 17 | end 18 | end 19 | 20 | usage unless target_ip = ARGV[0] # Need a target IP. 21 | usage unless Process.euid.zero? # Need to be root. 22 | IPAddr.new(target_ip) # Check to see it's really an IP address, and not a herring or something. 23 | 24 | $packetfu_default = PacketFu::Config.new(PacketFu::Utils.whoami?).config 25 | 26 | def arp(target_ip) 27 | 28 | arp_pkt = PacketFu::ARPPacket.new(:flavor => "Windows") 29 | arp_pkt.eth_saddr = arp_pkt.arp_saddr_mac = $packetfu_default[:eth_saddr] 30 | arp_pkt.eth_daddr = "ff:ff:ff:ff:ff:ff" 31 | arp_pkt.arp_daddr_mac = "00:00:00:00:00:00" 32 | 33 | arp_pkt.arp_saddr_ip = $packetfu_default[:ip_saddr] 34 | arp_pkt.arp_daddr_ip = target_ip 35 | 36 | # Stick the Capture object in its own thread. 37 | 38 | cap_thread = Thread.new do 39 | cap = PacketFu::Capture.new(:start => true, 40 | :filter => "arp src #{target_ip} and ether dst #{arp_pkt.eth_saddr}") 41 | arp_pkt.to_w # Shorthand for sending single packets to the default interface. 42 | target_mac = nil 43 | while target_mac.nil? 44 | if cap.save > 0 45 | arp_response = PacketFu::Packet.parse(cap.array[0]) 46 | target_mac = arp_response.arp_saddr_mac if arp_response.arp_saddr_ip = target_ip 47 | end 48 | sleep 0.1 # Check for a response ten times per second. 49 | end 50 | puts "#{target_ip} is-at #{target_mac}" 51 | # That's all we need. 52 | exit 0 53 | end 54 | 55 | # Timeout for cap_thread 56 | sleep 3; puts "Oh noes! Couldn't get an arp out of #{target_ip}. Maybe it's not here." 57 | exit 1 58 | end 59 | 60 | arp(target_ip) 61 | -------------------------------------------------------------------------------- /examples/arphood.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: binary -*- 3 | 4 | # A simple local network fingerprinter. Uses the OUI list. 5 | # Usage: 6 | # rvmsudo examples/arphood.rb [iface] [network] 7 | 8 | # Path setting slight of hand: 9 | $: << File.expand_path("../../lib", __FILE__) 10 | require 'packetfu' 11 | require 'open-uri' 12 | 13 | $oui_prefixes = {} 14 | $arp_results = [] 15 | def build_oui_list 16 | if ARGV[2].nil? 17 | puts "Fetching the oui.txt from IEEE, it'll be a second. Avoid this with #{$0} [iface] [network] ." 18 | oui_file = open("http://standards.ieee.org/regauth/oui/oui.txt") 19 | else 20 | oui_file = File.open(ARGV[2], "rb") 21 | end 22 | oui_file.each do |oui_line| 23 | maybe_oui = oui_line.scan(/^[0-9a-f]{2}\-[0-9a-f]{2}\-[0-9a-f]{2}/i)[0] 24 | unless maybe_oui.nil? 25 | oui_value = maybe_oui 26 | oui_vendor = oui_line.split(/\(hex\)\s*/n)[1] || "PRIVATE" 27 | $oui_prefixes[oui_value] = oui_vendor.chomp 28 | end 29 | end 30 | end 31 | 32 | build_oui_list 33 | 34 | $root_ok = true if Process.euid.zero? 35 | 36 | def arp_everyone 37 | my_net = PacketFu::Config.new(PacketFu::Utils.whoami?(:iface =>(ARGV[0] || 'wlan0'))) 38 | threads = [] 39 | network = ARGV[1] || "192.168.2" 40 | print "Arping around..." 41 | 253.times do |i| 42 | threads[i] = Thread.new do 43 | this_host = network + ".#{i+1}" 44 | print "." 45 | colon_mac = PacketFu::Utils.arp(this_host,my_net.config) 46 | unless colon_mac.nil? 47 | hyphen_mac = colon_mac.tr(':','-').upcase[0,8] 48 | else 49 | hyphen_mac = colon_mac = "NOTHERE" 50 | end 51 | $arp_results << "%s : %s / %s" % [this_host,colon_mac,$oui_prefixes[hyphen_mac]] 52 | end 53 | end 54 | threads.each {|thr| thr.join} 55 | end 56 | 57 | if $root_ok 58 | arp_everyone 59 | puts "\n" 60 | sleep 3 61 | $arp_results.sort.each {|a| puts a unless a =~ /NOTHERE/} 62 | end 63 | -------------------------------------------------------------------------------- /examples/dissect_thinger.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: binary -*- 3 | # This just allows you to eyeball the dissection stuff to make sure it's all right. 4 | 5 | # Usage: 6 | # ruby examples/ethernet.rb 7 | 8 | # Path setting slight of hand: 9 | $: << File.expand_path("../../lib", __FILE__) 10 | require 'packetfu' 11 | include PacketFu 12 | 13 | fname = ARGV[0] || "test/sample.pcap" 14 | sleep_interval = ARGV[1] || 1 15 | 16 | puts "Loaded: PacketFu v#{PacketFu.version}" 17 | 18 | packets = PacketFu::PcapFile.file_to_array fname 19 | packets.each do |packet| 20 | puts "_" * 75 21 | puts packet.inspect 22 | puts "_" * 75 23 | pkt = Packet.parse(packet) 24 | puts pkt.dissect 25 | sleep sleep_interval 26 | end 27 | -------------------------------------------------------------------------------- /examples/ethernet.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | 3 | # Usage: 4 | # ruby examples/ethernet.rb 5 | 6 | # Path setting slight of hand: 7 | $: << File.expand_path("../../lib", __FILE__) 8 | require 'packetfu' 9 | 10 | eth_pkt = PacketFu::EthPacket.new 11 | eth_pkt.eth_saddr="01:02:03:04:05:06" 12 | eth_pkt.eth_daddr="0a:0b:0c:0d:0e:0f" 13 | eth_pkt.payload="I'm a lonely little eth packet with no real protocol information to speak of." 14 | eth_pkt.recalc 15 | puts eth_pkt.inspect 16 | puts eth_pkt.to_f('/tmp/ethernet.pcap').inspect 17 | -------------------------------------------------------------------------------- /examples/ids.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: binary -*- 3 | 4 | # Usage: 5 | # rvmsudo ruby examples/idsv2.rb 6 | 7 | # Path setting slight of hand: 8 | $: << File.expand_path("../../lib", __FILE__) 9 | require 'packetfu' 10 | 11 | iface = ARGV[0] || PacketFu::Utils.default_int 12 | 13 | cap = PacketFu::Capture.new(:iface => iface, :start => true, :filter => "ip") 14 | 15 | loop do 16 | cap.stream.each do |pkt| 17 | packet = PacketFu::Packet.parse(pkt) 18 | if packet.payload =~ /^\x04\x01{50}/ 19 | p "#{Time.now}: %s slammed %s" % [packet.ip_saddr, packet.ip_daddr] 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /examples/idsv2.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: binary -*- 3 | 4 | # Usage: 5 | # rvmsudo ruby examples/idsv2.rb 6 | 7 | # Path setting slight of hand: 8 | $: << File.expand_path("../../lib", __FILE__) 9 | require 'packetfu' 10 | 11 | iface = ARGV[0] || PacketFu::Utils.default_int 12 | 13 | cap = PacketFu::Capture.new(:iface => iface, :start => true, :filter => "ip") 14 | 15 | attack_patterns = ["^gotcha", "owned!*$", "^\x04[^\x00]{50}"] 16 | 17 | loop do 18 | cap.stream.each do |pkt| 19 | packet = PacketFu::Packet.parse(pkt) 20 | attack_patterns.each do |sig| 21 | hit = packet.payload.scan(/#{sig}/i) || nil 22 | puts "#{Time.now}: %s attacked %s [%s]" % [packet.ip_saddr, packet.ip_daddr, sig.inspect] unless hit.size.zero? 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /examples/ifconfig.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | # Usage: 3 | # ruby examples/ifconfig.rb 4 | 5 | # Path setting slight of hand: 6 | $: << File.expand_path("../../lib", __FILE__) 7 | require 'packetfu' 8 | 9 | iface = ARGV[0] || PacketFu::Utils.default_int 10 | config = PacketFu::Utils.ifconfig(iface) 11 | print "#{RUBY_PLATFORM} => " 12 | p config 13 | -------------------------------------------------------------------------------- /examples/new-simple-stats.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: binary -*- 3 | 4 | # new-simple-stats.rb demonstrates the performance difference 5 | # between the old and busted way to parse pcap files and the 6 | # new hotness of stream parsing. Spoiler alert: Against a pcap 7 | # file of 1GB, the old way would eat all your memory and take 8 | # forever. This still takes kinda forever, but at 5000 packets 9 | # every 11 seconds (my own benchmark) for this script, at least 10 | # it doesn't hog up all your memory. 11 | 12 | # Usage: 13 | # ruby examples/new-simple-stats.rb test/sample.pcap 14 | 15 | # Path setting slight of hand: 16 | $: << File.expand_path("../../lib", __FILE__) 17 | require 'packetfu' 18 | 19 | def print_results(stats) 20 | stats.each_pair { |k,v| puts "%-12s: %10d" % [k,v] } 21 | end 22 | 23 | # Takes a file name, parses the packets, and records the packet 24 | # type based on its PacketFu class. 25 | def count_packet_types(file) 26 | stats = {} 27 | count = 0 28 | elapsed = 0 29 | start_time = Time.now 30 | PacketFu::PcapFile.read_packets(file) do |pkt| 31 | kind = pkt.proto.last.to_sym 32 | stats[kind] ? stats[kind] += 1 : stats[kind] = 1 33 | count += 1 34 | elapsed = (Time.now - start_time).to_i 35 | if count % 5_000 == 0 36 | puts "After #{count} packets (#{elapsed} seconds elapsed):" 37 | print_results(stats) 38 | end 39 | end 40 | puts "Final results for #{count} packets (#{elapsed} seconds elapsed):" 41 | print_results(stats) 42 | end 43 | 44 | if File.readable?(infile = (ARGV[0] || 'in.pcap')) 45 | title = "Packets by packet type in '#{infile}'" 46 | puts "-" * title.size 47 | puts title 48 | puts "-" * title.size 49 | count_packet_types(infile) 50 | else 51 | raise RuntimeError, "Need an infile, like so: #{$0} in.pcap" 52 | end 53 | -------------------------------------------------------------------------------- /examples/oui.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/examples/oui.txt -------------------------------------------------------------------------------- /examples/packetfu-shell.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | 3 | # Usage: 4 | # rvmsudo ruby examples/packetfu-shell.rb 5 | 6 | # Path setting slight of hand: 7 | $: << File.expand_path("../../lib", __FILE__) 8 | 9 | require 'packetfu' 10 | require 'irb' 11 | 12 | module PacketFu 13 | def whoami?(args={}) 14 | Utils.whoami?(args) 15 | end 16 | def arp(arg) 17 | Utils.arp(arg) 18 | end 19 | end 20 | 21 | include PacketFu 22 | 23 | # Draws a picture. Includes a nunchuck, so you know that it's serious. 24 | # I /think/ this is how you're supposed to spell it in a kana charset. 25 | # http://jisho.org/words?jap=+%E3%83%91%E3%82%B1%E3%83%83%E3%83%88%E3%83%95&eng=&dict=edict 26 | # 27 | def packetfu_ascii_art 28 | puts <>> PacketFu Shell #{PacketFu.version}." 53 | if Process.euid.zero? && @pcaprub_loaded 54 | puts ">>> Use $packetfu_default.config for salient networking details." 55 | print "IP: %-15s Mac: %s" % [$packetfu_default.ip_saddr, $packetfu_default.eth_saddr] 56 | puts " Gateway: %s" % $packetfu_default.eth_daddr 57 | print "Net: %-15s" % [Pcap.lookupnet($packetfu_default.iface)][0] 58 | print " " * 13 59 | puts "Iface: %s" % [($packetfu_default.iface)] 60 | puts ">>> Packet capturing/injecting enabled." 61 | else 62 | print ">>> Packet capturing/injecting disabled. " 63 | puts Process.euid.zero? ? "(no PcapRub)" : "(not root)" 64 | end 65 | puts "<>" * 36 66 | end 67 | 68 | # Silly wlan0 workaround 69 | begin 70 | $packetfu_default = PacketFu::Config.new(Utils.whoami?) if(@pcaprub_loaded && Process.euid.zero?) 71 | rescue RuntimeError 72 | $packetfu_default = PacketFu::Config.new(Utils.whoami?(:iface => 'wlan0')) if(@pcaprub_loaded && Process.euid.zero?) 73 | end 74 | 75 | banner 76 | 77 | IRB.start 78 | -------------------------------------------------------------------------------- /examples/pcap2pcapng.rb: -------------------------------------------------------------------------------- 1 | # Usage: 2 | # rvmsudo ruby examples/pcap2pcapng.rb test.pcap test.pcapng 3 | 4 | # Path setting slight of hand: 5 | $: << File.expand_path("../../lib", __FILE__) 6 | 7 | require 'packetfu' 8 | 9 | pcap_filename = ARGV[0].chomp 10 | pcapng_filename = ARGV[1].chomp 11 | 12 | unless File.exist?(pcap_filename) 13 | puts "PCAP input file #{pcap_filename} could not be found" 14 | end 15 | 16 | if File.exist?(pcapng_filename) 17 | puts "PCAP-NG output file #{pcap_filename} already exists" 18 | puts "Do you wish to overwrite the file? (Y/N, Default = N)" 19 | STDOUT.flush 20 | response = $stdin.gets.chomp 21 | unless response == "Y" 22 | puts "Aborting..." 23 | exit 0 24 | end 25 | end 26 | 27 | puts "Reading PCAP to packet array from #{File.expand_path(pcap_filename)}" 28 | packet_array = PacketFu::PcapFile.file_to_array(pcap_filename) 29 | 30 | puts "Writing packet array to PCAP-NG at #{File.expand_path(pcapng_filename)}" 31 | pcapng_file = PacketFu::PcapNG::File.new() 32 | pcapng_file.array_to_file(:array => packet_array, :file => pcapng_filename) 33 | -------------------------------------------------------------------------------- /examples/ping.rb: -------------------------------------------------------------------------------- 1 | # Usage: 2 | # rvmsudo ruby examples/ping.rb 8.8.8.8 3 | 4 | # Path setting slight of hand: 5 | $: << File.expand_path("../../lib", __FILE__) 6 | 7 | require 'packetfu' 8 | 9 | ip = ARGV[0].chomp 10 | 11 | config = PacketFu::Utils.whoami?() 12 | 13 | icmp_packet = PacketFu::ICMPPacket.new(:config => config) 14 | icmp_packet.ip_daddr = ip 15 | icmp_packet.payload = "I'm sending ICMP packets using PacketFu!!!" 16 | icmp_packet.icmp_type = 8 17 | icmp_packet.recalc 18 | 19 | capture_thread = Thread.new do 20 | begin 21 | Timeout::timeout(3) { 22 | cap = PacketFu::Capture.new(:iface => config[:iface], :start => true) 23 | cap.stream.each do |p| 24 | pkt = PacketFu::Packet.parse p 25 | next unless pkt.is_icmp? 26 | if pkt.ip_saddr == ip and pkt.icmp_type == 0 27 | puts "Got ICMP echo reply from #{ip}" 28 | break 29 | end 30 | end 31 | } 32 | rescue Timeout::Error 33 | puts "ICMP echo request timed out" 34 | end 35 | end 36 | 37 | puts "Sending ICMP echo request to #{ip}" 38 | icmp_packet.to_w 39 | 40 | capture_thread.join 41 | -------------------------------------------------------------------------------- /examples/readpcap.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Usage: 3 | # rvmsudo ruby examples/readpcap.rb test.pcap test.pcap 4 | 5 | # Path setting slight of hand: 6 | $: << File.expand_path("../../lib", __FILE__) 7 | 8 | require 'packetfu' 9 | include PacketFu 10 | 11 | pcap_filename = ARGV[0] || 'test/sample.pcap' 12 | 13 | unless File.exist?(pcap_filename) 14 | puts "PCAP input file '#{pcap_filename}' could not be found" 15 | exit 1 16 | end 17 | 18 | puts "Loaded: PacketFu v#{PacketFu.version}" 19 | 20 | puts "Reading PCAP to packet array from #{File.expand_path(pcap_filename)}" 21 | packet_array = PacketFu::PcapFile.file_to_array(pcap_filename) 22 | 23 | packet_array.each do |pkt| 24 | packet = PacketFu::Packet.parse(pkt) 25 | 26 | # Do some stuff here (really any thing you want) 27 | puts packet.class 28 | end 29 | -------------------------------------------------------------------------------- /examples/simple-sniffer.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: binary -*- 3 | 4 | # Usage: 5 | # rvmsudo ruby examples/simple-sniffer.rb 6 | 7 | # Path setting slight of hand: 8 | $: << File.expand_path("../../lib", __FILE__) 9 | require 'packetfu' 10 | 11 | puts "Simple sniffer for PacketFu #{PacketFu.version}" 12 | include PacketFu 13 | iface = ARGV[0] || PacketFu::Utils.default_int 14 | 15 | def sniff(iface) 16 | cap = Capture.new(:iface => iface, :start => true) 17 | cap.stream.each do |p| 18 | pkt = Packet.parse p 19 | if pkt.is_ip? 20 | next if pkt.ip_saddr == Utils.ifconfig(iface)[:ip_saddr] 21 | packet_info = [pkt.ip_saddr, pkt.ip_daddr, pkt.size, pkt.proto.last] 22 | puts "%-15s -> %-15s %-4d %s" % packet_info 23 | end 24 | end 25 | end 26 | 27 | sniff(iface) 28 | 29 | =begin 30 | Results look like this: 31 | 145.58.33.95 -> 192.168.11.70 1514 TCP 32 | 212.233.158.76 -> 192.168.11.70 110 UDP 33 | 88.174.164.147 -> 192.168.11.70 110 UDP 34 | 145.58.33.95 -> 192.168.11.70 1514 TCP 35 | 145.58.33.95 -> 192.168.11.70 1514 TCP 36 | 145.58.33.95 -> 192.168.11.70 1514 TCP 37 | 145.58.33.95 -> 192.168.11.70 1514 TCP 38 | 8.8.8.8 -> 192.168.11.70 143 UDP 39 | 41.237.73.186 -> 192.168.11.70 60 TCP 40 | 145.58.33.95 -> 192.168.11.70 1514 TCP 41 | 145.58.33.95 -> 192.168.11.70 1514 TCP 42 | 8.8.8.8 -> 192.168.11.70 143 UDP 43 | 8.8.8.8 -> 192.168.11.70 128 UDP 44 | 8.8.8.8 -> 192.168.11.70 187 UDP 45 | 24.45.247.232 -> 192.168.11.70 70 TCP 46 | =end 47 | -------------------------------------------------------------------------------- /examples/simple-stats.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: binary -*- 3 | 4 | # Simple-stats.rb takes a pcap file, and gives some simple 5 | # stastics on the protocols found. It's mainly used to 6 | # demonstrate a method to parse pcap files. 7 | # 8 | # XXX: DO NOT USE THIS METHOD TO READ PCAP FILES. 9 | # 10 | # See new-simple-stats.rb for an example of the streaming 11 | # parsing method. 12 | 13 | # Usage: 14 | # ruby examples/simple-stats.rb test/sample.pcap 15 | 16 | # Path setting slight of hand: 17 | $: << File.expand_path("../../lib", __FILE__) 18 | require 'packetfu' 19 | 20 | # Takes a file name, parses the packets, and records the packet 21 | # type based on its PacketFu class. 22 | def count_packet_types(file) 23 | file = File.open(file) {|f| f.read} 24 | stats = {} 25 | count = 0 26 | pcapfile = PacketFu::PcapPackets.new 27 | pcapfile.read(file) 28 | pcapfile.each do |p| 29 | # Now it's a PacketFu packet struct. 30 | pkt = PacketFu::Packet.parse(p.data) 31 | kind = pkt.class.to_s.split("::").last 32 | if stats[kind] 33 | stats[kind] += 1 34 | else 35 | stats[kind] = 0 36 | end 37 | count += 1 38 | break if count >= 1_000 39 | end 40 | stats.each_pair { |k,v| puts "%-12s: %4d" % [k,v] } 41 | end 42 | 43 | if File.readable?(infile = (ARGV[0] || 'in.pcap')) 44 | title = "Packets by packet type in '#{infile}'" 45 | puts title 46 | puts "-" * title.size 47 | count_packet_types(infile) 48 | else 49 | raise RuntimeError, "Need an infile, like so: #{$0} in.pcap" 50 | end 51 | -------------------------------------------------------------------------------- /examples/slammer.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: binary -*- 3 | 4 | # Fires off a slammer packet to an unsuspecting target. This code does not 5 | # break real devices! (To do that, you'll need to fix up the targetting) 6 | target = ARGV[0] 7 | raise RuntimeError, "Need a target" unless target 8 | action = ARGV[1] 9 | raise RuntimeError, "Need an action. Try file or your interface." unless action 10 | 11 | # Path setting slight of hand: 12 | $: << File.expand_path("../../lib", __FILE__) 13 | require 'packetfu' 14 | include PacketFu 15 | 16 | slammer = "\004\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\334\311\260B\353\016" + "\001\001\001\001\001\001\001p\256B\001p\256B\220\220\220\220\220\220\220\220h\334\311\260B\270\001\001" + "\001\0011\311\261\030P\342\3755\001\001\001\005P\211\345Qh.dllhel32hkernQhounthickChGetTf" + "\271llQh32.dhws2_f\271etQhsockf\271toQhsend\276\030\020\256B\215E\324P\377\026P\215E\340P\215E\360P\377" + "\026P\276\020\020\256B\213\036\213\003=U\213\354Qt\005\276\034\020\256B\377\026\377\3201\311QQP\201\361" + "\003\001\004\233\201\361\001\001\001\001Q\215E\314P\213E\300P\377\026j\021j\002j\002\377\320P\215E\304P" + "\213E\300P\377\026\211\306\t\333\201\363 config) 21 | syn_packet.ip_daddr = ip 22 | syn_packet.tcp_dst = 80 23 | syn_packet.tcp_flags.syn = 1 24 | syn_packet.recalc 25 | 26 | capture_thread = Thread.new do 27 | begin 28 | Timeout::timeout(3) { 29 | cap = PacketFu::Capture.new(:iface => config[:iface], :start => true) 30 | cap.stream.each do |p| 31 | pkt = PacketFu::Packet.parse p 32 | next unless pkt.is_tcp? 33 | 34 | if pkt.ip_saddr == ip && 35 | pkt.tcp_flags.syn == 1 && 36 | pkt.tcp_flags.ack == 1 37 | 38 | puts "Got SYN/ACK reply from #{ip}" 39 | 40 | syn_ack_packet = pkt 41 | ack_packet = syn_packet.dup 42 | ack_packet.tcp_flags.syn = 0 43 | ack_packet.tcp_flags.ack = 1 44 | ack_packet.tcp_ack = syn_ack_packet.tcp_seq + 1 45 | ack_packet.tcp_seq = syn_ack_packet.tcp_ack 46 | 47 | puts "Sending ACK reply to #{ip}" 48 | ack_packet.to_w 49 | break 50 | end 51 | end 52 | } 53 | rescue Timeout::Error 54 | puts "SYN request timed out" 55 | end 56 | end 57 | 58 | puts "Sending SYN request to #{ip}" 59 | syn_packet.to_w 60 | 61 | capture_thread.join 62 | -------------------------------------------------------------------------------- /examples/uniqpcap.rb: -------------------------------------------------------------------------------- 1 | # Uniqpcap.rb takes a pcap file, strips out duplicate packets, and 2 | # writes them to a file. 3 | # 4 | # The duplicate pcap problem is common when I'm capturing 5 | # traffic to/from a VMWare image, for some reason. 6 | # 7 | # Currently, the timestamp information is lost due to PcapRub's 8 | # file read. For me, this isn't a big deal. Future versions 9 | # will deal with timestamps correctly. 10 | 11 | # Usage: 12 | # ruby examples/uniqcap.rb test/sample.pcap 13 | 14 | # Path setting slight of hand: 15 | $: << File.expand_path("../../lib", __FILE__) 16 | require 'packetfu' 17 | 18 | pcap_file = ARGV[0].chomp 19 | 20 | in_array = PacketFu::Read.f2a(:file => pcap_file) 21 | 22 | puts "Original Packets: #{in_array.size}" 23 | puts "Uniq'd Packets: #{in_array.uniq.size}" 24 | 25 | puts PacketFu::Write.a2f(:file => pcap_file + ".uniq", :arr => in_array.uniq).inspect 26 | -------------------------------------------------------------------------------- /gem-public_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDbDCCAlSgAwIBAgIBATANBgkqhkiG9w0BAQUFADA+MQ0wCwYDVQQDDAR0b2Ri 3 | MRgwFgYKCZImiZPyLGQBGRYIcGFja2V0ZnUxEzARBgoJkiaJk/IsZAEZFgNjb20w 4 | HhcNMTcwMjAzMTc1MjAyWhcNMTgwMjAzMTc1MjAyWjA+MQ0wCwYDVQQDDAR0b2Ri 5 | MRgwFgYKCZImiZPyLGQBGRYIcGFja2V0ZnUxEzARBgoJkiaJk/IsZAEZFgNjb20w 6 | ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDig9ogvD2veEAEcmJt92br 7 | 5RhDcUv6VobqIazGJKdXhEYU6wk1353IxEfRTEWOwSKlFjJqwuV/Bm+jmZktoTQV 8 | ry8IZAfTxdHBSyWiXBGgg5UA3QS5ZH8gJIv7z9YQLWy8XORo76Xjpt0tr33z5+TU 9 | 8N3hh0ede6CAlM+TtCPJ6/GYvusJ1ThKjTWKMftllBFwFkbxMjrla6tfFu02tw/D 10 | bIeaC9kLliH+3exUzVDRqXZjHaD4edUPAId1QiFpLOgQBtqMCSm2aN3Cwdtb/4rc 11 | 7NSF7js+i+tgeAqNWogimx7P0/SKjE+UJ+qj7PK4bvPDqOxCwBprVzeQACQd/wNJ 12 | AgMBAAGjdTBzMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBS0hsMH 13 | SbakJkxOU8dncu6xO+S/6zAcBgNVHREEFTATgRF0b2RiQHBhY2tldGZ1LmNvbTAc 14 | BgNVHRIEFTATgRF0b2RiQHBhY2tldGZ1LmNvbTANBgkqhkiG9w0BAQUFAAOCAQEA 15 | JuFlw48r9fmUML3z9e82Ldx+zh8Y5ry6Pf/fgacLpP9fpn62Dc/lZlCE6jYAyHo1 16 | hK40RP+CJAm/6pGfCX66nX3+CTWQimZLRMpaBoAvSFEZ6ksBqXnzFH0YZlJccTxe 17 | FIyxZQgxjsXfAbmkFfSh8zGGN0Yk1TSjnS54b9sQpmmopRShM2nMFimu6381SmLU 18 | LZVT1SzAMqjK5HKaFwBwmb9i+vPycblrL8ngjSg4TWqMt4PLXOTvyiCy2IQRXb+o 19 | aNODmav4FDa8Yham3QhPACahVVxZczCpbDHFvojjk127uca93t3gc2IGx1jYO0mv 20 | bGMb9KcX7MTVhg4J4u5k6A== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /lib/packetfu.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'ipaddr' 3 | require 'packetfu/common' 4 | require 'packetfu/structfu' 5 | require 'packetfu/version' 6 | require 'packetfu/pcap' 7 | require 'packetfu/packet' 8 | require 'packetfu/protos' 9 | require 'packetfu/utils' 10 | require 'packetfu/config' 11 | require 'packetfu/pcapng' 12 | -------------------------------------------------------------------------------- /lib/packetfu/config.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | 4 | # The Config class holds various bits of useful default information 5 | # for packet creation. If initialized without arguments, @iface will be 6 | # set to ENV['IFACE'] or Pcap.lookupdev (or lo), and the @pcapfile will 7 | # be set to "/tmp/out.pcap" # (yes, it's Linux-biased, sorry, fixing 8 | # this is a TODO.) 9 | # 10 | # Any number of instance variables can be passed in to the intialize function (as a 11 | # hash), though only the expected network-related variables will be readable and 12 | # writeable directly. 13 | # 14 | # == Examples 15 | # 16 | # PacketFu::Config.new(:ip_saddr => "1.2.3.4").ip_saddr #=> "1.2.3.4" 17 | # PacketFu::Config.new(:foo=>"bar").foo #=> NomethodError: undefined method `foo'... 18 | # 19 | # The config() function, however, does provide access to custom variables: 20 | # 21 | # PacketFu::Config.new(:foo=>"bar").config[:foo] #=> "bar" 22 | # obj = PacketFu::Config.new(:foo=>"bar") 23 | # obj.config(:baz => "bat") 24 | # obj.config #=> {:iface=>"eth0", :baz=>"bat", :pcapfile=>"/tmp/out.pcap", :foo=>"bar"} 25 | class Config 26 | attr_accessor :eth_saddr, # The discovered eth_saddr 27 | :eth_daddr, # The discovered eth_daddr (ie, the gateway) 28 | :eth_src, # The discovered eth_src in binary form. 29 | :eth_dst, # The discovered eth_dst (gateway) in binary form. 30 | :ip_saddr, # The discovered ip_saddr 31 | :ip_src, # The discovered ip_src in binary form. 32 | :iface, # The declared interface. 33 | :pcapfile # A declared default file to write to. 34 | 35 | def initialize(args={}) 36 | if Process.euid.zero? 37 | @iface = args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo" 38 | end 39 | @pcapfile = "/tmp/out.pcap" 40 | args.each_pair { |k,v| self.instance_variable_set(("@#{k}"),v) } 41 | end 42 | 43 | # Returns all instance variables as a hash (including custom variables set at initialization). 44 | def config(arg=nil) 45 | if arg 46 | arg.each_pair {|k,v| self.instance_variable_set(("@" + k.to_s).to_sym, v)} 47 | else 48 | config_hash = {} 49 | self.instance_variables.each do |v| 50 | key = v.to_s.gsub(/^@/,"").to_sym 51 | config_hash[key] = self.instance_variable_get(v) 52 | end 53 | config_hash 54 | end 55 | end 56 | 57 | end 58 | 59 | end 60 | -------------------------------------------------------------------------------- /lib/packetfu/inject.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | 4 | # The Inject class handles injecting arrays of binary data on the wire. 5 | # 6 | # To inject single packets, use PacketFu::Packet.to_w() instead. 7 | class Inject 8 | attr_accessor :array, :stream, :show_live # Leave these public and open. 9 | attr_reader :iface, :snaplen, :promisc, :timeout # Cant change after the init. 10 | 11 | def initialize(args={}) 12 | @array = [] # Where the packet array goes. 13 | @stream = [] # Where the stream goes. 14 | @iface = args[:iface] || ENV['IFACE'] || PacketFu::Utils.default_int || "lo" 15 | @snaplen = args[:snaplen] || 0xffff 16 | @promisc = args[:promisc] || false # Sensible for some Intel wifi cards 17 | @timeout = args[:timeout] || 1 18 | @show_live = nil 19 | end 20 | 21 | # Takes an array, and injects them onto an interface. Note that 22 | # complete packets (Ethernet headers on down) are expected. 23 | # 24 | # === Parameters 25 | # 26 | # :array || arr 27 | # An array of binary data (usually packet.to_s style). 28 | # :int || sleep 29 | # Number of seconds to sleep between injections (in float format) 30 | # :show_live || :live 31 | # If true, puts data about what was injected to stdout. 32 | # 33 | # === Example 34 | # 35 | # inj = PacketFu::Inject.new 36 | # inj.array_to_wire(:array => [pkt1, pkt2, pkt3], :sleep => 0.1) 37 | # 38 | def array_to_wire(args={}) 39 | pkt_array = args[:array] || args[:arr] || @array 40 | interval = args[:int] || args[:sleep] 41 | show_live = args[:show_live] || args[:live] || @show_live 42 | 43 | @stream = Pcap.open_live(@iface,@snaplen,@promisc,@timeout) 44 | pkt_count = 0 45 | pkt_array.each do |pkt| 46 | @stream.inject(pkt) 47 | sleep interval if interval 48 | pkt_count +=1 49 | puts "Sent Packet \##{pkt_count} (#{pkt.size})" if show_live 50 | end 51 | # Return # of packets sent, array size, and array total size 52 | [pkt_count, pkt_array.size, pkt_array.join.size] 53 | end 54 | 55 | # Equivalent to array_to_wire 56 | def a2w(args={}) 57 | array_to_wire(args) 58 | end 59 | 60 | # Equivalent to array_to_wire 61 | def inject(args={}) 62 | array_to_wire(args) 63 | end 64 | 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/packetfu/pcapng.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | 3 | module PacketFu 4 | 5 | # Module to handle PCAP-NG file format. 6 | # See http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#format_idb 7 | module PcapNG 8 | 9 | # Section Header Block type number 10 | SHB_TYPE = StructFu::Int32.new(0x0A0D0D0A, :little) 11 | # Interface Description Block type number 12 | IDB_TYPE = StructFu::Int32.new(1, :little) 13 | # Simple Packet Block type number 14 | SPB_TYPE = StructFu::Int32.new(3, :little) 15 | # Enhanced Packet Block type number 16 | EPB_TYPE = StructFu::Int32.new(6, :little) 17 | 18 | # Various LINKTYPE values from http://www.tcpdump.org/linktypes.html 19 | # FIXME: only ETHERNET type is defined as this is the only link layer 20 | # type supported by PacketFu 21 | LINKTYPE_ETHERNET = 1 22 | 23 | class Error < StandardError; end 24 | class InvalidFileError < Error; end 25 | 26 | end 27 | 28 | end 29 | 30 | 31 | require_relative 'pcapng/block.rb' 32 | require_relative 'pcapng/unknown_block.rb' 33 | require_relative 'pcapng/shb.rb' 34 | require_relative 'pcapng/idb.rb' 35 | require_relative 'pcapng/epb.rb' 36 | require_relative 'pcapng/spb.rb' 37 | require_relative 'pcapng/file.rb' 38 | -------------------------------------------------------------------------------- /lib/packetfu/pcapng/block.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | module PcapNG 4 | 5 | module Block 6 | 7 | # Calculate block length and update :block_len and block_len2 fields 8 | def recalc_block_len 9 | len = to_a.map(&:to_s).join.size 10 | self[:block_len].value = self[:block_len2].value = len 11 | end 12 | 13 | # Pad given field to 32 bit boundary, if needed 14 | def pad_field(*fields) 15 | fields.each do |field| 16 | unless self[field].size % 4 == 0 17 | self[field] << "\x00" * (4 - (self[field].size % 4)) 18 | end 19 | end 20 | end 21 | 22 | end 23 | 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/packetfu/pcapng/epb.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | 3 | module PacketFu 4 | module PcapNG 5 | 6 | # Pcapng::EPB represents a Extended Packet Block (EPB) of a pcapng file. 7 | # 8 | # == Pcapng::EPB Definition 9 | # Int32 :type Default: 0x00000006 10 | # Int32 :block_len 11 | # Int32 :interface_id 12 | # Int32 :tsh (timestamp high) 13 | # Int32 :tsl (timestamp low) 14 | # Int32 :cap_len 15 | # Int32 :orig_len 16 | # String :data 17 | # String :options 18 | # Int32 :block_len2 19 | class EPB < Struct.new(:type, :block_len, :interface_id, :tsh, :tsl, 20 | :cap_len, :orig_len, :data, :options, :block_len2) 21 | include StructFu 22 | include Block 23 | attr_accessor :endian 24 | attr_accessor :interface 25 | 26 | MIN_SIZE = 8*4 27 | 28 | def initialize(args={}) 29 | @endian = set_endianness(args[:endian] || :little) 30 | init_fields(args) 31 | super(args[:type], args[:block_len], args[:interface_id], args[:tsh], 32 | args[:tsl], args[:cap_len], args[:orig_len], args[:data], 33 | args[:options], args[:block_len2]) 34 | end 35 | 36 | # Used by #initialize to set the initial fields 37 | def init_fields(args={}) 38 | args[:type] = @int32.new(args[:type] || PcapNG::EPB_TYPE.to_i) 39 | args[:block_len] = @int32.new(args[:block_len] || MIN_SIZE) 40 | args[:interface_id] = @int32.new(args[:interface_id] || 0) 41 | args[:tsh] = @int32.new(args[:tsh] || 0) 42 | args[:tsl] = @int32.new(args[:tsl] || 0) 43 | args[:cap_len] = @int32.new(args[:cap_len] || 0) 44 | args[:orig_len] = @int32.new(args[:orig_len] || 0) 45 | args[:data] = StructFu::String.new(args[:data] || '') 46 | args[:options] = StructFu::String.new(args[:options] || '') 47 | args[:block_len2] = @int32.new(args[:block_len2] || MIN_SIZE) 48 | args 49 | end 50 | 51 | def has_options? 52 | self[:options].size > 0 53 | end 54 | 55 | # Reads a String or a IO to populate the object 56 | def read(str_or_io) 57 | if str_or_io.respond_to? :read 58 | io = str_or_io 59 | else 60 | io = StringIO.new(force_binary(str_or_io.to_s)) 61 | end 62 | return self if io.eof? 63 | 64 | self[:type].read io.read(4) 65 | self[:block_len].read io.read(4) 66 | self[:interface_id].read io.read(4) 67 | self[:tsh].read io.read(4) 68 | self[:tsl].read io.read(4) 69 | self[:cap_len].read io.read(4) 70 | self[:orig_len].read io.read(4) 71 | self[:data].read io.read(self[:cap_len].to_i) 72 | data_pad_len = (4 - (self[:cap_len].to_i % 4)) % 4 73 | io.read data_pad_len 74 | options_len = self[:block_len].to_i - self[:cap_len].to_i - data_pad_len 75 | options_len -= MIN_SIZE 76 | self[:options].read io.read(options_len) 77 | self[:block_len2].read io.read(4) 78 | 79 | unless self[:block_len].to_i == self[:block_len2].to_i 80 | raise InvalidFileError, 'Incoherency in Extended Packet Block' 81 | end 82 | 83 | self 84 | end 85 | 86 | # Return timestamp as a Time object 87 | def timestamp 88 | Time.at((self[:tsh].to_i << 32 | self[:tsl].to_i) * ts_resol) 89 | end 90 | 91 | # Return the object as a String 92 | def to_s 93 | pad_field :data, :options 94 | recalc_block_len 95 | to_a.map(&:to_s).join 96 | end 97 | 98 | 99 | private 100 | 101 | def ts_resol 102 | if @interface.nil? 103 | 1E-6 104 | else 105 | @interface.ts_resol 106 | end 107 | end 108 | 109 | end 110 | 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /lib/packetfu/pcapng/idb.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | 3 | module PacketFu 4 | module PcapNG 5 | 6 | # Pcapng::IDB represents a Interface Description Block (IDB) of a pcapng file. 7 | # 8 | # == Pcapng::IDB Definition 9 | # Int32 :type Default: 0x00000001 10 | # Int32 :block_len 11 | # Int16 :link_type Default: 1 12 | # Int16 :reserved Default: 0 13 | # Int64 :snaplen Default: 0 (no limit) 14 | # String :options 15 | # Int32 :block_len2 16 | class IDB < Struct.new(:type, :block_len, :link_type, :reserved, 17 | :snaplen, :options, :block_len2) 18 | include StructFu 19 | include Block 20 | attr_accessor :endian 21 | attr_accessor :section 22 | attr_accessor :packets 23 | 24 | MIN_SIZE = 5*4 25 | 26 | # Option code for if_tsresol option 27 | OPTION_IF_TSRESOL = 9 28 | 29 | def initialize(args={}) 30 | @endian = set_endianness(args[:endian] || :little) 31 | @packets = [] 32 | @options_decoded = false 33 | init_fields(args) 34 | super(args[:type], args[:block_len], args[:link_type], args[:reserved], 35 | args[:snaplen], args[:options], args[:block_len2]) 36 | end 37 | 38 | # Used by #initialize to set the initial fields 39 | def init_fields(args={}) 40 | args[:type] = @int32.new(args[:type] || PcapNG::IDB_TYPE.to_i) 41 | args[:block_len] = @int32.new(args[:block_len] || MIN_SIZE) 42 | args[:link_type] = @int16.new(args[:link_type] || 1) 43 | args[:reserved] = @int16.new(args[:reserved] || 0) 44 | args[:snaplen] = @int32.new(args[:snaplen] || 0) 45 | args[:options] = StructFu::String.new(args[:options] || '') 46 | args[:block_len2] = @int32.new(args[:block_len2] || MIN_SIZE) 47 | args 48 | end 49 | 50 | def has_options? 51 | self[:options].size > 0 52 | end 53 | 54 | # Reads a String or a IO to populate the object 55 | def read(str_or_io) 56 | if str_or_io.respond_to? :read 57 | io = str_or_io 58 | else 59 | io = StringIO.new(force_binary(str_or_io.to_s)) 60 | end 61 | return self if io.eof? 62 | 63 | self[:type].read io.read(4) 64 | self[:block_len].read io.read(4) 65 | self[:link_type].read io.read(2) 66 | self[:reserved].read io.read(2) 67 | self[:snaplen].read io.read(4) 68 | self[:options].read io.read(self[:block_len].to_i - MIN_SIZE) 69 | self[:block_len2].read io.read(4) 70 | 71 | unless self[:block_len].to_i == self[:block_len2].to_i 72 | raise InvalidFileError, 'Incoherency in Interface Description Block' 73 | end 74 | 75 | self 76 | end 77 | 78 | # Add a xPB to this section 79 | def <<(xpb) 80 | @packets << xpb 81 | end 82 | 83 | # Give timestamp resolution for this interface 84 | def ts_resol(force=false) 85 | if @options_decoded and not force 86 | @ts_resol 87 | else 88 | packstr = (@endian == :little) ? 'v' : 'n' 89 | idx = 0 90 | options = self[:options] 91 | opt_code = opt_len = 0 92 | 93 | while idx < options.length do 94 | opt_code, opt_len = options[idx, 4].unpack("#{packstr}2") 95 | if opt_code == OPTION_IF_TSRESOL and opt_len == 1 96 | tsresol = options[idx+4, 1].unpack('C').first 97 | if tsresol & 0x80 == 0 98 | @ts_resol = 10 ** -tsresol 99 | else 100 | @ts_resol = 2 ** -(tsresol & 0x7f) 101 | end 102 | 103 | @options_decoded = true 104 | return @ts_resol 105 | else 106 | idx += 4 + opt_len 107 | end 108 | end 109 | 110 | @options_decoded = true 111 | @ts_resol = 1E-6 # default value 112 | end 113 | end 114 | 115 | # Return the object as a String 116 | def to_s 117 | pad_field :options 118 | recalc_block_len 119 | to_a.map(&:to_s).join + @packets.map(&:to_s).join 120 | end 121 | 122 | end 123 | 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /lib/packetfu/pcapng/spb.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | 3 | module PacketFu 4 | module PcapNG 5 | 6 | # Pcapng::SPB represents a Section Simple Packet Block (SPB) of a pcapng file. 7 | # 8 | # == Pcapng::SPB Definition 9 | # Int32 :type Default: 0x00000003 10 | # Int32 :block_len 11 | # Int32 :orig_len 12 | # String :data 13 | # Int32 :block_len2 14 | class SPB < Struct.new(:type, :block_len, :orig_len, :data, :block_len2) 15 | include StructFu 16 | include Block 17 | attr_accessor :endian 18 | attr_accessor :interface 19 | 20 | MIN_SIZE = 4*4 21 | 22 | def initialize(args={}) 23 | @endian = set_endianness(args[:endian] || :little) 24 | init_fields(args) 25 | super(args[:type], args[:block_len], args[:orig_len], args[:data], 26 | args[:block_len2]) 27 | end 28 | 29 | # Used by #initialize to set the initial fields 30 | def init_fields(args={}) 31 | args[:type] = @int32.new(args[:type] || PcapNG::SPB_TYPE.to_i) 32 | args[:block_len] = @int32.new(args[:block_len] || MIN_SIZE) 33 | args[:orig_len] = @int32.new(args[:orig_len] || 0) 34 | args[:data] = StructFu::String.new(args[:data] || '') 35 | args[:block_len2] = @int32.new(args[:block_len2] || MIN_SIZE) 36 | args 37 | end 38 | 39 | def has_options? 40 | false 41 | end 42 | 43 | def read(str_or_io) 44 | if str_or_io.respond_to? :read 45 | io = str_or_io 46 | else 47 | io = StringIO.new(force_binary(str_or_io.to_s)) 48 | end 49 | return self if io.eof? 50 | 51 | self[:type].read io.read(4) 52 | self[:block_len].read io.read(4) 53 | self[:orig_len].read io.read(4) 54 | # Take care of IDB snaplen 55 | # CAUTION: snaplen == 0 -> no capture limit 56 | if interface and interface.snaplen.to_i > 0 57 | data_len = [self[:orig_len].to_i, interface.snaplen.to_i].min 58 | else 59 | data_len = self[:orig_len].to_i 60 | end 61 | data_pad_len = (4 - (data_len % 4)) % 4 62 | self[:data].read io.read(data_len) 63 | io.read data_pad_len 64 | self[:block_len2].read io.read(4) 65 | 66 | unless self[:block_len].to_i == self[:block_len2].to_i 67 | raise InvalidFileError, 'Incoherency in Simple Packet Block' 68 | end 69 | 70 | self 71 | end 72 | 73 | # Return the object as a String 74 | def to_s 75 | pad_field :data 76 | recalc_block_len 77 | to_a.map(&:to_s).join 78 | end 79 | 80 | end 81 | 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/packetfu/pcapng/unknown_block.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | 3 | module PacketFu 4 | module PcapNG 5 | 6 | # Pcapng::UnknownBlock is used to handle unsupported blocks of a pcapng file. 7 | class UnknownBlock < Struct.new(:type, :block_len, :body, :block_len2) 8 | include StructFu 9 | include Block 10 | attr_accessor :endian 11 | attr_accessor :section 12 | 13 | MIN_SIZE = 12 14 | 15 | def initialize(args={}) 16 | @endian = set_endianness(args[:endian] || :little) 17 | init_fields(args) 18 | super(args[:type], args[:block_len], args[:body], args[:block_len2]) 19 | end 20 | 21 | # Used by #initialize to set the initial fields 22 | def init_fields(args={}) 23 | args[:type] = @int32.new(args[:type] || 0) 24 | args[:block_len] = @int32.new(args[:block_len] || MIN_SIZE) 25 | args[:body] = StructFu::String.new(args[:body] || '') 26 | args[:block_len2] = @int32.new(args[:block_len2] || MIN_SIZE) 27 | args 28 | end 29 | 30 | def read(str_or_io) 31 | if str_or_io.respond_to? :read 32 | io = str_or_io 33 | else 34 | io = StringIO.new(force_binary(str_or_io.to_s)) 35 | end 36 | return self if io.eof? 37 | 38 | self[:type].read io.read(4) 39 | self[:block_len].read io.read(4) 40 | self[:body].read io.read(self[:block_len].to_i - MIN_SIZE) 41 | self[:block_len2].read io.read(4) 42 | 43 | unless self[:block_len].to_i == self[:block_len2].to_i 44 | raise InvalidFileError, 'Incoherency in Header Block' 45 | end 46 | 47 | self 48 | end 49 | 50 | # Return the object as a String 51 | def to_s 52 | pad_field :body 53 | recalc_block_len 54 | to_a.map(&:to_s).join 55 | end 56 | 57 | end 58 | 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/packetfu/protos.rb: -------------------------------------------------------------------------------- 1 | # Picks up all the protocols defined in the protos subdirectory 2 | path = File.expand_path(File.join(File::dirname(__FILE__), "protos", "*.rb")) 3 | Dir.glob(path).each do |file| 4 | require file 5 | end 6 | -------------------------------------------------------------------------------- /lib/packetfu/protos/arp.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'packetfu/common' 3 | require 'packetfu/protos/eth/header' 4 | require 'packetfu/protos/eth/mixin' 5 | require 'packetfu/protos/arp/header' 6 | require 'packetfu/protos/arp/mixin' 7 | 8 | module PacketFu 9 | 10 | # ARPPacket is used to construct ARP packets. They contain an EthHeader and an ARPHeader. 11 | # == Example 12 | # 13 | # require 'packetfu' 14 | # arp_pkt = PacketFu::ARPPacket.new(:flavor => "Windows") 15 | # arp_pkt.arp_saddr_mac="00:1c:23:44:55:66" # Your hardware address 16 | # arp_pkt.arp_saddr_ip="10.10.10.17" # Your IP address 17 | # arp_pkt.arp_daddr_ip="10.10.10.1" # Target IP address 18 | # arp_pkt.arp_opcode=1 # Request 19 | # 20 | # arp_pkt.to_w('eth0') # Inject on the wire. (requires root) 21 | # arp_pkt.to_f('/tmp/arp.pcap') # Write to a file. 22 | # 23 | # == Parameters 24 | # 25 | # :flavor 26 | # Sets the "flavor" of the ARP packet. Choices are currently: 27 | # :windows, :linux, :hp_deskjet 28 | # :eth 29 | # A pre-generated EthHeader object. If not specified, a new one will be created. 30 | # :arp 31 | # A pre-generated ARPHeader object. If not specificed, a new one will be created. 32 | # :config 33 | # A hash of return address details, often the output of Utils.whoami? 34 | class ARPPacket < Packet 35 | include ::PacketFu::EthHeaderMixin 36 | include ::PacketFu::ARPHeaderMixin 37 | 38 | attr_accessor :eth_header, :arp_header 39 | 40 | def self.can_parse?(str) 41 | return false unless EthPacket.can_parse? str 42 | return false unless str.size >= 28 43 | return false unless str[12,2] == "\x08\x06" 44 | true 45 | end 46 | 47 | def initialize(args={}) 48 | @eth_header = EthHeader.new(args).read(args[:eth]) 49 | @arp_header = ARPHeader.new(args).read(args[:arp]) 50 | @eth_header.eth_proto = "\x08\x06" 51 | @eth_header.body=@arp_header 52 | 53 | # Please send more flavors to todb+packetfu@planb-security.net. 54 | # Most of these initial fingerprints come from one (1) sample. 55 | case (args[:flavor].nil?) ? :nil : args[:flavor].to_s.downcase.to_sym 56 | when :windows; @arp_header.body = "\x00" * 64 # 64 bytes of padding 57 | when :linux; @arp_header.body = "\x00" * 4 + # 32 bytes of padding 58 | "\x00\x07\x5c\x14" + "\x00" * 4 + 59 | "\x00\x0f\x83\x34" + "\x00\x0f\x83\x74" + 60 | "\x01\x11\x83\x78" + "\x00\x00\x00\x0c" + 61 | "\x00\x00\x00\x00" 62 | when :hp_deskjet; # Pads up to 60 bytes. 63 | @arp_header.body = "\xe0\x90\x0d\x6c" + 64 | "\xff\xff\xee\xee" + "\x00" * 4 + 65 | "\xe0\x8f\xfa\x18\x00\x20" 66 | else; @arp_header.body = "\x00" * 18 # Pads up to 60 bytes. 67 | end 68 | 69 | @headers = [@eth_header, @arp_header] 70 | super 71 | end 72 | 73 | # Generates summary data for ARP packets. 74 | def peek_format 75 | peek_data = ["A "] 76 | peek_data << "%-5d" % self.to_s.size 77 | peek_data << arp_saddr_mac 78 | peek_data << "(#{arp_saddr_ip})" 79 | peek_data << "->" 80 | peek_data << case arp_daddr_mac 81 | when "00:00:00:00:00:00"; "Bcast00" 82 | when "ff:ff:ff:ff:ff:ff"; "BcastFF" 83 | else; arp_daddr_mac 84 | end 85 | peek_data << "(#{arp_daddr_ip})" 86 | peek_data << ":" 87 | peek_data << case arp_opcode 88 | when 1; "Requ" 89 | when 2; "Repl" 90 | when 3; "RReq" 91 | when 4; "RRpl" 92 | when 5; "IReq" 93 | when 6; "IRpl" 94 | else; "0x%02x" % arp_opcode 95 | end 96 | peek_data.join 97 | end 98 | 99 | # While there are lengths in ARPPackets, there's not 100 | # much to do with them. 101 | def recalc(args={}) 102 | @headers[0].inspect 103 | end 104 | 105 | end 106 | 107 | end 108 | 109 | # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby 110 | -------------------------------------------------------------------------------- /lib/packetfu/protos/arp/mixin.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # This Mixin simplifies access to the ARPHeaders. Mix this in with your 4 | # packet interface, and it will add methods that essentially delegate to 5 | # the 'arp_header' method (assuming that it is a ARPHeader object) 6 | module ARPHeaderMixin 7 | def arp_hw=(v); self.arp_header.arp_hw= v; end 8 | def arp_hw; self.arp_header.arp_hw; end 9 | def arp_proto=(v); self.arp_header.arp_proto= v; end 10 | def arp_proto; self.arp_header.arp_proto; end 11 | def arp_hw_len=(v); self.arp_header.arp_hw_len= v; end 12 | def arp_hw_len; self.arp_header.arp_hw_len; end 13 | def arp_proto_len=(v); self.arp_header.arp_proto_len= v; end 14 | def arp_proto_len; self.arp_header.arp_proto_len; end 15 | def arp_opcode=(v); self.arp_header.arp_opcode= v; end 16 | def arp_opcode; self.arp_header.arp_opcode; end 17 | def arp_src_mac=(v); self.arp_header.arp_src_mac= v; end 18 | def arp_src_mac; self.arp_header.arp_src_mac; end 19 | def arp_src_ip=(v); self.arp_header.arp_src_ip= v; end 20 | def arp_src_ip; self.arp_header.arp_src_ip; end 21 | def arp_dst_mac=(v); self.arp_header.arp_dst_mac= v; end 22 | def arp_dst_mac; self.arp_header.arp_dst_mac; end 23 | def arp_dst_ip=(v); self.arp_header.arp_dst_ip= v; end 24 | def arp_dst_ip; self.arp_header.arp_dst_ip; end 25 | def arp_saddr_mac=(v); self.arp_header.arp_saddr_mac= v; end 26 | def arp_saddr_mac; self.arp_header.arp_saddr_mac; end 27 | def arp_daddr_mac=(v); self.arp_header.arp_daddr_mac= v; end 28 | def arp_daddr_mac; self.arp_header.arp_daddr_mac; end 29 | def arp_saddr_ip=(v); self.arp_header.arp_saddr_ip= v; end 30 | def arp_saddr_ip; self.arp_header.arp_saddr_ip; end 31 | def arp_daddr_ip=(v); self.arp_header.arp_daddr_ip= v; end 32 | def arp_daddr_ip; self.arp_header.arp_daddr_ip; end 33 | def arp_src_mac_readable; self.arp_header.arp_src_mac_readable; end 34 | def arp_dst_mac_readable; self.arp_header.arp_dst_mac_readable; end 35 | def arp_src_ip_readable; self.arp_header.arp_src_ip_readable; end 36 | def arp_dst_ip_readable; self.arp_header.arp_dst_ip_readable; end 37 | def arp_proto_readable; self.arp_header.arp_proto_readable; end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/packetfu/protos/eth.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'packetfu/protos/eth/header' 3 | require 'packetfu/protos/eth/mixin' 4 | 5 | module PacketFu 6 | # EthPacket is used to construct Ethernet packets. They contain an 7 | # Ethernet header, and that's about it. 8 | # 9 | # == Example 10 | # 11 | # require 'packetfu' 12 | # eth_pkt = PacketFu::EthPacket.new 13 | # eth_pkt.eth_saddr="00:1c:23:44:55:66" 14 | # eth_pkt.eth_daddr="00:1c:24:aa:bb:cc" 15 | # 16 | # eth_pkt.to_w('eth0') # Inject on the wire. (require root) 17 | # 18 | class EthPacket < Packet 19 | include ::PacketFu::EthHeaderMixin 20 | 21 | attr_accessor :eth_header 22 | 23 | def self.can_parse?(str) 24 | # XXX Temporary fix. Need to extend the EthHeader class to handle more. 25 | valid_eth_types = [0x0800, 0x0806, 0x86dd, 0x88cc] 26 | return false unless str.size >= 14 27 | type = str[12,2].unpack("n").first rescue nil 28 | return false unless valid_eth_types.include? type 29 | true 30 | end 31 | 32 | # Does nothing, really, since there's no length or 33 | # checksum to calculate for a straight Ethernet packet. 34 | def recalc(args={}) 35 | @headers[0].inspect 36 | end 37 | 38 | def initialize(args={}) 39 | @eth_header = EthHeader.new(args).read(args[:eth]) 40 | @headers = [@eth_header] 41 | super 42 | end 43 | 44 | end 45 | 46 | end 47 | 48 | # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby 49 | -------------------------------------------------------------------------------- /lib/packetfu/protos/eth/mixin.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # This Mixin simplifies access to the EthHeaders. Mix this in with your 4 | # packet interface, and it will add methods that essentially delegate to 5 | # the 'eth_header' method (assuming that it is a EthHeader object) 6 | module EthHeaderMixin 7 | def eth_daddr; self.eth_header.eth_daddr ; end 8 | def eth_daddr=(v); self.eth_header.eth_daddr= v; end 9 | def eth_dst; self.eth_header.eth_dst ; end 10 | def eth_dst=(v); self.eth_header.eth_dst= v; end 11 | def eth_dst_readable; self.eth_header.eth_dst_readable ; end 12 | def eth_proto; self.eth_header.eth_proto ; end 13 | def eth_proto=(v); self.eth_header.eth_proto= v; end 14 | def eth_proto_readable; self.eth_header.eth_proto_readable ; end 15 | def eth_saddr; self.eth_header.eth_saddr ; end 16 | def eth_saddr=(v); self.eth_header.eth_saddr= v; end 17 | def eth_src; self.eth_header.eth_src ; end 18 | def eth_src=(v); self.eth_header.eth_src= v; end 19 | def eth_src_readable; self.eth_header.eth_src_readable ; end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/packetfu/protos/hsrp.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'packetfu/protos/eth/header' 3 | require 'packetfu/protos/eth/mixin' 4 | 5 | require 'packetfu/protos/ip/header' 6 | require 'packetfu/protos/ip/mixin' 7 | 8 | require 'packetfu/protos/udp/header' 9 | require 'packetfu/protos/udp/mixin' 10 | 11 | require 'packetfu/protos/hsrp/header' 12 | require 'packetfu/protos/hsrp/mixin' 13 | 14 | module PacketFu 15 | # HSRPPacket is used to construct HSRP Packets. They contain an EthHeader, an IPHeader, and a UDPHeader. 16 | # 17 | # == Example 18 | # 19 | # hsrp_pkt.new 20 | # hsrp_pkt.hsrp_opcode = 0 21 | # hsrp_pkt.hsrp_state = 16 22 | # hsrp_pkt.hsrp_priority = 254 23 | # hsrp_pkt.hsrp_group = 1 24 | # hsrp_pkt.hsrp_vip = 10.100.100.254 25 | # hsrp_pkt.recalc 26 | # hsrp_pkt.to_f('/tmp/hsrp.pcap') 27 | # 28 | # == Parameters 29 | # 30 | # :eth 31 | # A pre-generated EthHeader object. 32 | # :ip 33 | # A pre-generated IPHeader object. 34 | # :udp 35 | # A pre-generated UDPHeader object. 36 | # :flavor 37 | # TODO: HSRP packets don't tend have any flavor. 38 | # :config 39 | # A hash of return address details, often the output of Utils.whoami? 40 | class HSRPPacket < Packet 41 | include ::PacketFu::EthHeaderMixin 42 | include ::PacketFu::IPHeaderMixin 43 | include ::PacketFu::UDPHeaderMixin 44 | include ::PacketFu::HSRPHeaderMixin 45 | 46 | attr_accessor :eth_header, :ip_header, :udp_header, :hsrp_header 47 | 48 | def self.can_parse?(str) 49 | return false unless str.size >= 54 50 | return false unless EthPacket.can_parse? str 51 | return false unless IPPacket.can_parse? str 52 | return false unless UDPPacket.can_parse? str 53 | temp_packet = UDPPacket.new 54 | temp_packet.read(str) 55 | if temp_packet.ip_ttl == 1 and [temp_packet.udp_sport,temp_packet.udp_dport] == [1985,1985] 56 | return true 57 | else 58 | return false 59 | end 60 | end 61 | 62 | def initialize(args={}) 63 | @eth_header = EthHeader.new(args).read(args[:eth]) 64 | @ip_header = IPHeader.new(args).read(args[:ip]) 65 | @ip_header.ip_proto = 0x11 66 | @udp_header = UDPHeader.new(args).read(args[:udp]) 67 | @hsrp_header = HSRPHeader.new(args).read(args[:hsrp]) 68 | @udp_header.body = @hsrp_header 69 | @ip_header.body = @udp_header 70 | @eth_header.body = @ip_header 71 | @headers = [@eth_header, @ip_header, @udp_header, @hsrp_header] 72 | super 73 | end 74 | 75 | # Peek provides summary data on packet contents. 76 | def peek_format 77 | peek_data = ["UH "] 78 | peek_data << "%-5d" % self.to_s.size 79 | peek_data << "%-16s" % self.hsrp_addr 80 | peek_data << "%-4d" % self.hsrp_group 81 | peek_data << "%-35s" % self.hsrp_password_readable 82 | peek_data << "%-15s" % self.ip_saddr 83 | peek_data.join 84 | end 85 | 86 | end 87 | 88 | end 89 | 90 | # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby 91 | -------------------------------------------------------------------------------- /lib/packetfu/protos/hsrp/header.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # HSRPHeader is a complete HSRP struct, used in HSRPPacket. HSRP is typically used for 4 | # fault-tolerant default gateway in IP routing environment. 5 | # 6 | # For more on HSRP packets, see http://www.networksorcery.com/enp/protocol/hsrp.htm 7 | # 8 | # Submitted by fropert@packetfault.org. Thanks, Francois! 9 | # 10 | # ==== Header Definition 11 | # 12 | # Int8 :hsrp_version Default: 0 # Version 13 | # Int8 :hsrp_opcode # Opcode 14 | # Int8 :hsrp_state # State 15 | # Int8 :hsrp_hellotime Default: 3 # Hello Time 16 | # Int8 :hsrp_holdtime Default: 10 # Hold Time 17 | # Int8 :hsrp_priority # Priority 18 | # Int8 :hsrp_group # Group 19 | # Int8 :hsrp_reserved Default: 0 # Reserved 20 | # String :hsrp_password # Authentication Data 21 | # Octets :hsrp_vip # Virtual IP Address 22 | # String :body 23 | class HSRPHeader < Struct.new(:hsrp_version, :hsrp_opcode, :hsrp_state, 24 | :hsrp_hellotime, :hsrp_holdtime, 25 | :hsrp_priority, :hsrp_group, 26 | :hsrp_reserved, :hsrp_password, 27 | :hsrp_vip, :body) 28 | 29 | include StructFu 30 | 31 | def initialize(args={}) 32 | super( 33 | Int8.new(args[:hsrp_version] || 0), 34 | Int8.new(args[:hsrp_opcode]), 35 | Int8.new(args[:hsrp_state]), 36 | Int8.new(args[:hsrp_hellotime] || 3), 37 | Int8.new(args[:hsrp_holdtime] || 10), 38 | Int8.new(args[:hsrp_priority]), 39 | Int8.new(args[:hsrp_group]), 40 | Int8.new(args[:hsrp_reserved] || 0), 41 | StructFu::String.new.read(args[:hsrp_password] || "cisco\x00\x00\x00"), 42 | Octets.new.read(args[:hsrp_vip] || ("\x00" * 4)), 43 | StructFu::String.new.read(args[:body]) 44 | ) 45 | end 46 | 47 | # Returns the object in string form. 48 | def to_s 49 | self.to_a.map {|x| x.to_s}.join 50 | end 51 | 52 | # Reads a string to populate the object. 53 | def read(str) 54 | force_binary(str) 55 | return self if str.nil? 56 | self[:hsrp_version].read(str[0,1]) 57 | self[:hsrp_opcode].read(str[1,1]) 58 | self[:hsrp_state].read(str[2,1]) 59 | self[:hsrp_hellotime].read(str[3,1]) 60 | self[:hsrp_holdtime].read(str[4,1]) 61 | self[:hsrp_priority].read(str[5,1]) 62 | self[:hsrp_group].read(str[6,1]) 63 | self[:hsrp_reserved].read(str[7,1]) 64 | self[:hsrp_password].read(str[8,8]) 65 | self[:hsrp_vip].read(str[16,4]) 66 | self[:body].read(str[20,str.size]) if str.size > 20 67 | self 68 | end 69 | 70 | # Setter for the type. 71 | def hsrp_version=(i); typecast i; end 72 | # Getter for the type. 73 | def hsrp_version; self[:hsrp_version].to_i; end 74 | # Setter for the type. 75 | def hsrp_opcode=(i); typecast i; end 76 | # Getter for the type. 77 | def hsrp_opcode; self[:hsrp_opcode].to_i; end 78 | # Setter for the type. 79 | def hsrp_state=(i); typecast i; end 80 | # Getter for the type. 81 | def hsrp_state; self[:hsrp_state].to_i; end 82 | # Setter for the type. 83 | def hsrp_hellotime=(i); typecast i; end 84 | # Getter for the type. 85 | def hsrp_hellotime; self[:hsrp_hellotime].to_i; end 86 | # Setter for the type. 87 | def hsrp_holdtime=(i); typecast i; end 88 | # Getter for the type. 89 | def hsrp_holdtime; self[:hsrp_holdtime].to_i; end 90 | # Setter for the type. 91 | def hsrp_priority=(i); typecast i; end 92 | # Getter for the type. 93 | def hsrp_priority; self[:hsrp_priority].to_i; end 94 | # Setter for the type. 95 | def hsrp_group=(i); typecast i; end 96 | # Getter for the type. 97 | def hsrp_group; self[:hsrp_group].to_i; end 98 | # Setter for the type. 99 | def hsrp_reserved=(i); typecast i; end 100 | # Getter for the type. 101 | def hsrp_reserved; self[:hsrp_reserved].to_i; end 102 | 103 | def hsrp_addr=(addr) 104 | self[:hsrp_vip].read_quad(addr) 105 | end 106 | 107 | # Returns a more readable IP source address. 108 | def hsrp_addr 109 | self[:hsrp_vip].to_x 110 | end 111 | 112 | # Readability aliases 113 | 114 | alias :hsrp_vip_readable :hsrp_addr 115 | 116 | def hsrp_password_readable 117 | hsrp_password.to_s.inspect 118 | end 119 | 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /lib/packetfu/protos/hsrp/mixin.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # This Mixin simplifies access to the HSRPHeaders. Mix this in with your 4 | # packet interface, and it will add methods that essentially delegate to 5 | # the 'hsrp_header' method (assuming that it is a HSRPHeader object) 6 | module HSRPHeaderMixin 7 | def hsrp_version=(v); self.hsrp_header.hsrp_version= v; end 8 | def hsrp_version; self.hsrp_header.hsrp_version; end 9 | def hsrp_opcode=(v); self.hsrp_header.hsrp_opcode= v; end 10 | def hsrp_opcode; self.hsrp_header.hsrp_opcode; end 11 | def hsrp_state=(v); self.hsrp_header.hsrp_state= v; end 12 | def hsrp_state; self.hsrp_header.hsrp_state; end 13 | def hsrp_hellotime=(v); self.hsrp_header.hsrp_hellotime= v; end 14 | def hsrp_hellotime; self.hsrp_header.hsrp_hellotime; end 15 | def hsrp_holdtime=(v); self.hsrp_header.hsrp_holdtime= v; end 16 | def hsrp_holdtime; self.hsrp_header.hsrp_holdtime; end 17 | def hsrp_priority=(v); self.hsrp_header.hsrp_priority= v; end 18 | def hsrp_priority; self.hsrp_header.hsrp_priority; end 19 | def hsrp_group=(v); self.hsrp_header.hsrp_group= v; end 20 | def hsrp_group; self.hsrp_header.hsrp_group; end 21 | def hsrp_reserved=(v); self.hsrp_header.hsrp_reserved= v; end 22 | def hsrp_reserved; self.hsrp_header.hsrp_reserved; end 23 | def hsrp_addr=(v); self.hsrp_header.hsrp_addr= v; end 24 | def hsrp_addr; self.hsrp_header.hsrp_addr; end 25 | def hsrp_vip_readable; self.hsrp_header.hsrp_vip_readable; end 26 | def hsrp_password_readable; self.hsrp_header.hsrp_password_readable; end 27 | def hsrp_password; self.hsrp_header.hsrp_password; end 28 | def hsrp_password=(v); self.hsrp_header.hsrp_password= v; end 29 | def hsrp_vip; self.hsrp_header.hsrp_vip; end 30 | def hsrp_vip=(v); self.hsrp_header.hsrp_vip= v; end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/packetfu/protos/icmp.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'packetfu/protos/eth/header' 3 | require 'packetfu/protos/eth/mixin' 4 | 5 | require 'packetfu/protos/ip/header' 6 | require 'packetfu/protos/ip/mixin' 7 | 8 | require 'packetfu/protos/icmp/header' 9 | require 'packetfu/protos/icmp/mixin' 10 | 11 | module PacketFu 12 | # ICMPPacket is used to construct ICMP Packets. They contain an EthHeader, an IPHeader, and a ICMPHeader. 13 | # 14 | # == Example 15 | # 16 | # icmp_pkt.new 17 | # icmp_pkt.icmp_type = 8 18 | # icmp_pkt.icmp_code = 0 19 | # icmp_pkt.payload = "ABC, easy as 123. As simple as do-re-mi. ABC, 123, baby, you and me!" 20 | # 21 | # icmp_pkt.ip_saddr="1.2.3.4" 22 | # icmp_pkt.ip_daddr="5.6.7.8" 23 | # 24 | # icmp_pkt.recalc 25 | # icmp_pkt.to_f('/tmp/icmp.pcap') 26 | # 27 | # == Parameters 28 | # 29 | # :eth 30 | # A pre-generated EthHeader object. 31 | # :ip 32 | # A pre-generated IPHeader object. 33 | # :flavor 34 | # TODO: Sets the "flavor" of the ICMP packet. Pings, in particular, often betray their true 35 | # OS. 36 | # :config 37 | # A hash of return address details, often the output of Utils.whoami? 38 | class ICMPPacket < Packet 39 | include ::PacketFu::EthHeaderMixin 40 | include ::PacketFu::IPHeaderMixin 41 | include ::PacketFu::ICMPHeaderMixin 42 | 43 | attr_accessor :eth_header, :ip_header, :icmp_header 44 | 45 | def self.can_parse?(str) 46 | return false unless str.size >= 38 47 | return false unless EthPacket.can_parse? str 48 | return false unless IPPacket.can_parse? str 49 | return false unless str[23,1] == "\x01" 50 | return true 51 | end 52 | 53 | def initialize(args={}) 54 | @eth_header = EthHeader.new(args).read(args[:eth]) 55 | @ip_header = IPHeader.new(args).read(args[:ip]) 56 | @ip_header.ip_proto = 1 57 | @icmp_header = ICMPHeader.new(args).read(args[:icmp]) 58 | 59 | @ip_header.body = @icmp_header 60 | @eth_header.body = @ip_header 61 | 62 | @headers = [@eth_header, @ip_header, @icmp_header] 63 | super 64 | end 65 | 66 | # Peek provides summary data on packet contents. 67 | def peek_format 68 | peek_data = ["IC "] # I is taken by IP 69 | peek_data << "%-5d" % self.to_s.size 70 | type = case self.icmp_type.to_i 71 | when 8 72 | "ping" 73 | when 0 74 | "pong" 75 | else 76 | "%02x-%02x" % [self.icmp_type, self.icmp_code] 77 | end 78 | peek_data << "%-21s" % "#{self.ip_saddr}:#{type}" 79 | peek_data << "->" 80 | peek_data << "%21s" % "#{self.ip_daddr}" 81 | peek_data << "%23s" % "I:" 82 | peek_data << "%04x" % self.ip_id 83 | peek_data.join 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/packetfu/protos/icmp/header.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # ICMPHeader is a complete ICMP struct, used in ICMPPacket. ICMP is 4 | # typically used for network administration and connectivity testing. 5 | # 6 | # For more on ICMP packets, see 7 | # http://www.networksorcery.com/enp/protocol/icmp.htm 8 | # 9 | # ==== Header Definition 10 | # 11 | # Int8 :icmp_type # Type 12 | # Int8 :icmp_code # Code 13 | # Int16 :icmp_sum Default: calculated # Checksum 14 | # String :body 15 | class ICMPHeader < Struct.new(:icmp_type, :icmp_code, :icmp_sum, :body) 16 | 17 | include StructFu 18 | 19 | def initialize(args={}) 20 | super( 21 | Int8.new(args[:icmp_type]), 22 | Int8.new(args[:icmp_code]), 23 | Int16.new(args[:icmp_sum] || icmp_calc_sum), 24 | StructFu::String.new.read(args[:body]) 25 | ) 26 | end 27 | 28 | # Returns the object in string form. 29 | def to_s 30 | self.to_a.map {|x| x.to_s}.join 31 | end 32 | 33 | # Reads a string to populate the object. 34 | def read(str) 35 | force_binary(str) 36 | return self if str.nil? 37 | self[:icmp_type].read(str[0,1]) 38 | self[:icmp_code].read(str[1,1]) 39 | self[:icmp_sum].read(str[2,2]) 40 | self[:body].read(str[4,str.size]) 41 | self 42 | end 43 | 44 | # Setter for the type. 45 | def icmp_type=(i); typecast i; end 46 | # Getter for the type. 47 | def icmp_type; self[:icmp_type].to_i; end 48 | # Setter for the code. 49 | def icmp_code=(i); typecast i; end 50 | # Getter for the code. 51 | def icmp_code; self[:icmp_code].to_i; end 52 | # Setter for the checksum. Note, this is calculated automatically with 53 | # icmp_calc_sum. 54 | def icmp_sum=(i); typecast i; end 55 | # Getter for the checksum. 56 | def icmp_sum; self[:icmp_sum].to_i; end 57 | 58 | # Calculates and sets the checksum for the object. 59 | def icmp_calc_sum 60 | checksum = (icmp_type.to_i << 8) + icmp_code.to_i 61 | chk_body = (body.to_s.size % 2 == 0 ? body.to_s : body.to_s + "\x00") 62 | if 1.respond_to? :ord 63 | chk_body.split("").each_slice(2).map { |x| (x[0].ord << 8) + x[1].ord }.each { |y| checksum += y } 64 | else 65 | chk_body.split("").each_slice(2).map { |x| (x[0] << 8) + x[1] }.each { |y| checksum += y } 66 | end 67 | checksum = checksum % 0xffff 68 | checksum = 0xffff - checksum 69 | checksum == 0 ? 0xffff : checksum 70 | end 71 | 72 | # Recalculates the calculatable fields for ICMP. 73 | def icmp_recalc(arg = :all) 74 | case arg.to_sym 75 | when :icmp_sum 76 | self.icmp_sum=icmp_calc_sum 77 | when :all 78 | self.icmp_sum=icmp_calc_sum 79 | else 80 | raise ArgumentError, "No such field `#{arg}'" 81 | end 82 | end 83 | 84 | # Readability aliases 85 | 86 | def icmp_sum_readable 87 | "0x%04x" % icmp_sum 88 | end 89 | 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/packetfu/protos/icmp/mixin.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # This Mixin simplifies access to the ICMPHeaders. Mix this in with your 4 | # packet interface, and it will add methods that essentially delegate to 5 | # the 'icmp_header' method (assuming that it is a ICMPHeader object) 6 | module ICMPHeaderMixin 7 | def icmp_type=(v); self.icmp_header.icmp_type= v; end 8 | def icmp_type; self.icmp_header.icmp_type; end 9 | def icmp_code=(v); self.icmp_header.icmp_code= v; end 10 | def icmp_code; self.icmp_header.icmp_code; end 11 | def icmp_sum=(v); self.icmp_header.icmp_sum= v; end 12 | def icmp_sum; self.icmp_header.icmp_sum; end 13 | def icmp_calc_sum; self.icmp_header.icmp_calc_sum; end 14 | def icmp_recalc(*v); self.icmp_header.icmp_recalc(*v); end 15 | def icmp_sum_readable; self.icmp_header.icmp_sum_readable; end 16 | end 17 | end 18 | 19 | -------------------------------------------------------------------------------- /lib/packetfu/protos/icmpv6.rb: -------------------------------------------------------------------------------- 1 | # coding: binary 2 | require 'packetfu/protos/eth/header' 3 | require 'packetfu/protos/eth/mixin' 4 | 5 | require 'packetfu/protos/ipv6/header' 6 | require 'packetfu/protos/ipv6/mixin' 7 | 8 | require 'packetfu/protos/icmpv6/header' 9 | require 'packetfu/protos/icmpv6/mixin' 10 | 11 | module PacketFu 12 | 13 | # ICMPv6Packet is used to construct ICMPv6 Packets. They contain an EthHeader, 14 | # an IPv6Header, and a ICMPv6Header. 15 | # 16 | # == Example 17 | # 18 | # icmpv6_pkt.new 19 | # icmpv6_pkt.icmpv6_type = 8 20 | # icmpv6_pkt.icmpv6_code = 0 21 | # icmpv6_pkt.payload = "ABC, easy as 123. As simple as do-re-mi. ABC, 123, baby, you and me!" 22 | # 23 | # icmpv6_pkt.ipv6_saddr="2000::1234" 24 | # icmpv6_pkt.ipv6_daddr="2000::5678" 25 | # 26 | # icmpv6_pkt.recalc 27 | # icmpv6_pkt.to_f('/tmp/icmpv6.pcap') 28 | # 29 | # == Parameters 30 | # 31 | # :eth 32 | # A pre-generated EthHeader object. 33 | # :ipv6 34 | # A pre-generated IPv6Header object. 35 | # :icmpv6 36 | # A pre-generated ICMPv6Header object. 37 | class ICMPv6Packet < Packet 38 | include ::PacketFu::EthHeaderMixin 39 | include ::PacketFu::IPv6HeaderMixin 40 | include ::PacketFu::ICMPv6HeaderMixin 41 | 42 | attr_accessor :eth_header, :ipv6_header, :icmpv6_header 43 | 44 | def self.can_parse?(str) 45 | return false unless str.size >= 58 46 | return false unless EthPacket.can_parse? str 47 | return false unless IPv6Packet.can_parse? str 48 | return false unless str[20,1] == [PacketFu::ICMPv6Header::PROTOCOL_NUMBER].pack('C') 49 | return true 50 | end 51 | 52 | def initialize(args={}) 53 | @eth_header = EthHeader.new(args).read(args[:eth]) 54 | @ipv6_header = IPv6Header.new(args).read(args[:ipv6]) 55 | @ipv6_header.ipv6_next = PacketFu::ICMPv6Header::PROTOCOL_NUMBER 56 | @icmpv6_header = ICMPv6Header.new(args).read(args[:icmpv6]) 57 | 58 | @ipv6_header.body = @icmpv6_header 59 | @eth_header.body = @ipv6_header 60 | 61 | @headers = [@eth_header, @ipv6_header, @icmpv6_header] 62 | super 63 | icmpv6_calc_sum 64 | end 65 | 66 | # Calculates the checksum for the object. 67 | def icmpv6_calc_sum 68 | checksum = ipv6_calc_sum_on_addr 69 | checksum += PacketFu::ICMPv6Header::PROTOCOL_NUMBER 70 | checksum += ipv6_len 71 | # Then compute it on ICMPv6 header + payload 72 | checksum += (icmpv6_type.to_i << 8) + icmpv6_code.to_i 73 | chk_body = (payload.to_s.size % 2 == 0 ? payload.to_s : payload.to_s + "\x00") 74 | if 1.respond_to? :ord 75 | chk_body.split("").each_slice(2).map { |x| (x[0].ord << 8) + x[1].ord }. 76 | each { |y| checksum += y } 77 | else 78 | chk_body.split("").each_slice(2).map { |x| (x[0] << 8) + x[1] }. 79 | each { |y| checksum += y } 80 | end 81 | checksum = checksum % 0xffff 82 | checksum = 0xffff - checksum 83 | checksum == 0 ? 0xffff : checksum 84 | end 85 | 86 | # Recalculates the calculatable fields for ICMPv6. 87 | def icmpv6_recalc(arg = :all) 88 | case arg.to_sym 89 | when :icmpv6_sum 90 | self.icmpv6_sum = icmpv6_calc_sum 91 | when :all 92 | self.icmpv6_sum = icmpv6_calc_sum 93 | else 94 | raise ArgumentError, "No such field `#{arg}'" 95 | end 96 | end 97 | 98 | # Peek provides summary data on packet contents. 99 | def peek_format 100 | peek_data = ["6C "] 101 | peek_data << "%-5d" % self.to_s.size 102 | type = case self.icmpv6_type.to_i 103 | when 128 104 | "ping" 105 | when 129 106 | "pong" 107 | else 108 | "%02x-%02x" % [self.icmpv6_type, self.icmpv6_code] 109 | end 110 | peek_data << "%-21s" % "#{self.ipv6_saddr}:#{type}" 111 | peek_data << "->" 112 | peek_data << "%21s" % "#{self.ipv6_daddr}" 113 | peek_data.join 114 | end 115 | 116 | end 117 | 118 | end 119 | -------------------------------------------------------------------------------- /lib/packetfu/protos/icmpv6/header.rb: -------------------------------------------------------------------------------- 1 | require 'packetfu/protos/ipv6/header' 2 | require 'packetfu/protos/ipv6/mixin' 3 | 4 | module PacketFu 5 | 6 | # ICMPv6Header is a complete ICMPv6 struct, used in ICMPv6Packet. 7 | # ICMPv6 is typically used for network administration and connectivity 8 | # testing. 9 | # 10 | # For more on ICMP packets, see 11 | # http://www.networksorcery.com/enp/protocol/icmpv6.htm 12 | # 13 | # ==== Header Definition 14 | # 15 | # Int8 :icmp_type # Type 16 | # Int8 :icmp_code # Code 17 | # Int16 :icmp_sum Default: calculated # Checksum 18 | # String :body 19 | class ICMPv6Header < Struct.new(:icmpv6_type, :icmpv6_code, :icmpv6_sum, :body) 20 | include StructFu 21 | 22 | PROTOCOL_NUMBER = 58 23 | 24 | def initialize(args={}) 25 | super( 26 | Int8.new(args[:icmpv6_type]), 27 | Int8.new(args[:icmpv6_code]), 28 | Int16.new(args[:icmpv6_sum]), 29 | StructFu::String.new.read(args[:body]) 30 | ) 31 | end 32 | 33 | # Returns the object in string form. 34 | def to_s 35 | self.to_a.map {|x| x.to_s}.join 36 | end 37 | 38 | # Reads a string to populate the object. 39 | def read(str) 40 | force_binary(str) 41 | return self if str.nil? 42 | self[:icmpv6_type].read(str[0,1]) 43 | self[:icmpv6_code].read(str[1,1]) 44 | self[:icmpv6_sum].read(str[2,2]) 45 | self[:body].read(str[4,str.size]) 46 | self 47 | end 48 | 49 | # Setter for the type. 50 | def icmpv6_type=(i); typecast i; end 51 | # Getter for the type. 52 | def icmpv6_type; self[:icmpv6_type].to_i; end 53 | # Setter for the code. 54 | def icmpv6_code=(i); typecast i; end 55 | # Getter for the code. 56 | def icmpv6_code; self[:icmpv6_code].to_i; end 57 | # Setter for the checksum. Note, this is calculated automatically with 58 | # icmpv6_calc_sum. 59 | def icmpv6_sum=(i); typecast i; end 60 | # Getter for the checksum. 61 | def icmpv6_sum; self[:icmpv6_sum].to_i; end 62 | 63 | def icmpv6_sum_readable 64 | "0x%04x" % icmpv6_sum 65 | end 66 | 67 | end 68 | 69 | end 70 | -------------------------------------------------------------------------------- /lib/packetfu/protos/icmpv6/mixin.rb: -------------------------------------------------------------------------------- 1 | module PacketFu 2 | # This Mixin simplifies access to the ICMPv6Headers. Mix this in with your 3 | # packet interface, and it will add methods that essentially delegate to 4 | # the 'icmpv6_header' method (assuming that it is a ICMPv6Header object) 5 | module ICMPv6HeaderMixin 6 | def icmpv6_type=(v); self.icmpv6_header.icmpv6_type= v; end 7 | def icmpv6_type; self.icmpv6_header.icmpv6_type; end 8 | def icmpv6_code=(v); self.icmpv6_header.icmpv6_code= v; end 9 | def icmpv6_code; self.icmpv6_header.icmpv6_code; end 10 | def icmpv6_sum=(v); self.icmpv6_header.icmpv6_sum= v; end 11 | def icmpv6_sum; self.icmpv6_header.icmpv6_sum; end 12 | def icmpv6_sum_readable; self.icmpv6_header.icmpv6_sum_readable; end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/packetfu/protos/invalid.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | 4 | # InvalidHeader catches all packets that we don't already have a Struct for, 5 | # or for whatever reason, violates some basic packet rules for other packet 6 | # types. 7 | class InvalidHeader < Struct.new(:body) 8 | include StructFu 9 | 10 | def initialize(args={}) 11 | args[:body] ||= StructFu::String.new 12 | super(args[:body]) 13 | end 14 | 15 | # Returns the object in string form. 16 | def to_s 17 | self.to_a.map {|x| x.to_s}.join 18 | end 19 | 20 | # Reads a string to populate the object. 21 | def read(str) 22 | force_binary(str) 23 | return self if str.nil? 24 | self[:body].read str 25 | self 26 | end 27 | 28 | end 29 | 30 | # You probably don't want to write invalid packets on purpose. 31 | class InvalidPacket < Packet 32 | attr_accessor :invalid_header 33 | 34 | # Any packet is potentially an invalid packet 35 | def self.can_parse?(str) 36 | true 37 | end 38 | 39 | def self.layer 40 | 0 41 | end 42 | 43 | def read(str=nil,args={}) 44 | @invalid_header.read(str) 45 | self 46 | end 47 | 48 | def initialize(args={}) 49 | @invalid_header = (args[:invalid] || InvalidHeader.new) 50 | @headers = [@invalid_header] 51 | end 52 | end 53 | 54 | end # module PacketFu 55 | 56 | # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby 57 | -------------------------------------------------------------------------------- /lib/packetfu/protos/ip.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'packetfu/protos/eth/header' 3 | require 'packetfu/protos/eth/mixin' 4 | require 'packetfu/protos/ip/header' 5 | require 'packetfu/protos/ip/mixin' 6 | 7 | module PacketFu 8 | 9 | # IPPacket is used to construct IP packets. They contain an EthHeader, an IPHeader, and usually 10 | # a transport-layer protocol such as UDPHeader, TCPHeader, or ICMPHeader. 11 | # 12 | # == Example 13 | # 14 | # require 'packetfu' 15 | # ip_pkt = PacketFu::IPPacket.new 16 | # ip_pkt.ip_saddr="10.20.30.40" 17 | # ip_pkt.ip_daddr="192.168.1.1" 18 | # ip_pkt.ip_proto=1 19 | # ip_pkt.ip_payload="\x00\x00\x12\x34\x00\x01\x00\x01"+ 20 | # "Lovingly hand-crafted echo responses delivered directly to your door." 21 | # ip_pkt.recalc 22 | # ip_pkt.to_f('/tmp/ip.pcap') 23 | # 24 | # == Parameters 25 | # 26 | # :eth 27 | # A pre-generated EthHeader object. 28 | # :ip 29 | # A pre-generated IPHeader object. 30 | # :flavor 31 | # TODO: Sets the "flavor" of the IP packet. This might include known sets of IP options, and 32 | # certainly known starting TTLs. 33 | # :config 34 | # A hash of return address details, often the output of Utils.whoami? 35 | class IPPacket < Packet 36 | include ::PacketFu::EthHeaderMixin 37 | include ::PacketFu::IPHeaderMixin 38 | 39 | attr_accessor :eth_header, :ip_header 40 | 41 | def self.can_parse?(str) 42 | return false unless str.size >= 34 43 | return false unless EthPacket.can_parse? str 44 | if str[12,2] == "\x08\x00" 45 | if 1.respond_to? :ord 46 | ipv = str[14,1][0].ord >> 4 47 | else 48 | ipv = str[14,1][0] >> 4 49 | end 50 | return true if ipv == 4 51 | else 52 | return false 53 | end 54 | end 55 | 56 | # Creates a new IPPacket object. 57 | def initialize(args={}) 58 | @eth_header = EthHeader.new(args).read(args[:eth]) 59 | @ip_header = IPHeader.new(args).read(args[:ip]) 60 | @eth_header.body=@ip_header 61 | 62 | @headers = [@eth_header, @ip_header] 63 | super 64 | end 65 | 66 | # Peek provides summary data on packet contents. 67 | def peek_format 68 | peek_data = ["I "] 69 | peek_data << "%-5d" % to_s.size 70 | peek_data << "%-21s" % "#{ip_saddr}" 71 | peek_data << "->" 72 | peek_data << "%21s" % "#{ip_daddr}" 73 | peek_data << "%23s" % "I:" 74 | peek_data << "%04x" % ip_id.to_i 75 | peek_data.join 76 | end 77 | 78 | end 79 | 80 | end 81 | 82 | # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby 83 | -------------------------------------------------------------------------------- /lib/packetfu/protos/ip/mixin.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | 3 | module PacketFu 4 | # This Mixin simplifies access to the IPHeaders. Mix this in with your 5 | # packet interface, and it will add methods that essentially delegate to 6 | # the 'ip_header' method (assuming that it is a IPHeader object) 7 | module IPHeaderMixin 8 | def ip_calc_id; self.ip_header.ip_calc_id ; end 9 | def ip_calc_len; self.ip_header.ip_calc_len ; end 10 | def ip_calc_sum; self.ip_header.ip_calc_sum ; end 11 | def ip_daddr; self.ip_header.ip_daddr ; end 12 | def ip_daddr=(v); self.ip_header.ip_daddr= v; end 13 | def ip_dst; self.ip_header.ip_dst ; end 14 | def ip_dst=(v); self.ip_header.ip_dst= v; end 15 | def ip_dst_readable; self.ip_header.ip_dst_readable ; end 16 | def ip_frag; self.ip_header.ip_frag ; end 17 | def ip_frag=(v); self.ip_header.ip_frag= v; end 18 | def ip_hl; self.ip_header.ip_hl ; end 19 | def ip_hl=(v); self.ip_header.ip_hl= v; end 20 | def ip_hlen; self.ip_header.ip_hlen ; end 21 | def ip_id; self.ip_header.ip_id ; end 22 | def ip_id=(v); self.ip_header.ip_id= v; end 23 | def ip_id_readable; self.ip_header.ip_id_readable ; end 24 | def ip_len; self.ip_header.ip_len ; end 25 | def ip_len=(v); self.ip_header.ip_len= v; end 26 | def ip_proto; self.ip_header.ip_proto ; end 27 | def ip_proto=(v); self.ip_header.ip_proto= v; end 28 | def ip_recalc(*args); self.ip_header.ip_recalc(*args) ; end 29 | def ip_saddr; self.ip_header.ip_saddr ; end 30 | def ip_saddr=(v); self.ip_header.ip_saddr= v; end 31 | def ip_src; self.ip_header.ip_src ; end 32 | def ip_src=(v); self.ip_header.ip_src= v; end 33 | def ip_src_readable; self.ip_header.ip_src_readable ; end 34 | def ip_sum; self.ip_header.ip_sum ; end 35 | def ip_sum=(v); self.ip_header.ip_sum= v; end 36 | def ip_sum_readable; self.ip_header.ip_sum_readable ; end 37 | def ip_tos; self.ip_header.ip_tos ; end 38 | def ip_tos=(v); self.ip_header.ip_tos= v; end 39 | def ip_ttl; self.ip_header.ip_ttl ; end 40 | def ip_ttl=(v); self.ip_header.ip_ttl= v; end 41 | def ip_v; self.ip_header.ip_v ; end 42 | def ip_v=(v); self.ip_header.ip_v= v; end 43 | 44 | # Add method to each packet class on IP to ease checksum computation 45 | def ip_calc_sum_on_addr(cksum=0) 46 | checksum = cksum 47 | checksum += (ip_src.to_i >> 16) 48 | checksum += (ip_src.to_i & 0xffff) 49 | checksum += (ip_dst.to_i >> 16) 50 | checksum += (ip_dst.to_i & 0xffff) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/packetfu/protos/ipv6.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'packetfu/protos/eth/header' 3 | require 'packetfu/protos/eth/mixin' 4 | 5 | require 'packetfu/protos/ipv6/header' 6 | require 'packetfu/protos/ipv6/mixin' 7 | 8 | module PacketFu 9 | # IPv6Packet is used to construct IPv6 Packets. They contain an EthHeader and an IPv6Header, and in 10 | # the distant, unknowable future, will take interesting IPv6ish payloads. 11 | # 12 | # This mostly complete, but not very useful. It's intended primarily as an example protocol. 13 | # 14 | # == Parameters 15 | # 16 | # :eth 17 | # A pre-generated EthHeader object. 18 | # :ip 19 | # A pre-generated IPHeader object. 20 | # :flavor 21 | # TODO: Sets the "flavor" of the IPv6 packet. No idea what this will look like, haven't done much IPv6 fingerprinting. 22 | # :config 23 | # A hash of return address details, often the output of Utils.whoami? 24 | class IPv6Packet < Packet 25 | include ::PacketFu::EthHeaderMixin 26 | include ::PacketFu::IPv6HeaderMixin 27 | 28 | attr_accessor :eth_header, :ipv6_header 29 | 30 | def self.can_parse?(str) 31 | return false unless EthPacket.can_parse? str 32 | return false unless str.size >= 54 33 | return false unless str[12,2] == "\x86\xdd" 34 | true 35 | end 36 | 37 | def initialize(args={}) 38 | @eth_header = (args[:eth] || EthHeader.new) 39 | @ipv6_header = (args[:ipv6] || IPv6Header.new) 40 | @eth_header.eth_proto = 0x86dd 41 | @eth_header.body=@ipv6_header 42 | @headers = [@eth_header, @ipv6_header] 43 | super 44 | end 45 | 46 | # Peek provides summary data on packet contents. 47 | def peek(args={}) 48 | peek_data = ["6 "] 49 | peek_data << "%-5d" % self.to_s.size 50 | peek_data << "%-31s" % self.ipv6_saddr 51 | peek_data << "-> " 52 | peek_data << "%-31s" % self.ipv6_daddr 53 | peek_data << " N:" 54 | peek_data << self.ipv6_next.to_s(16) 55 | peek_data.join 56 | end 57 | 58 | end 59 | 60 | end 61 | -------------------------------------------------------------------------------- /lib/packetfu/protos/ipv6/mixin.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # This Mixin simplifies access to the IPv6Headers. Mix this in with your 4 | # packet interface, and it will add methods that essentially delegate to 5 | # the 'ipv6_header' method (assuming that it is a IPv6Header object) 6 | module IPv6HeaderMixin 7 | def ipv6_v=(v); self.ipv6_header.ipv6_v= v; end 8 | def ipv6_v; self.ipv6_header.ipv6_v; end 9 | def ipv6_class=(v); self.ipv6_header.ipv6_class= v; end 10 | def ipv6_class; self.ipv6_header.ipv6_class; end 11 | def ipv6_label=(v); self.ipv6_header.ipv6_label= v; end 12 | def ipv6_label; self.ipv6_header.ipv6_label; end 13 | def ipv6_len=(v); self.ipv6_header.ipv6_len= v; end 14 | def ipv6_len; self.ipv6_header.ipv6_len; end 15 | def ipv6_next=(v); self.ipv6_header.ipv6_next= v; end 16 | def ipv6_next; self.ipv6_header.ipv6_next; end 17 | def ipv6_hop=(v); self.ipv6_header.ipv6_hop= v; end 18 | def ipv6_hop; self.ipv6_header.ipv6_hop; end 19 | def ipv6_src=(v); self.ipv6_header.ipv6_src= v; end 20 | def ipv6_src; self.ipv6_header.ipv6_src; end 21 | def ipv6_dst=(v); self.ipv6_header.ipv6_dst= v; end 22 | def ipv6_dst; self.ipv6_header.ipv6_dst; end 23 | def ipv6_calc_len; self.ipv6_header.ipv6_calc_len; end 24 | def ipv6_recalc(*v); self.ipv6_header.ipv6_recalc(*v); end 25 | def ipv6_saddr; self.ipv6_header.ipv6_saddr; end 26 | def ipv6_saddr=(v); self.ipv6_header.ipv6_saddr= v; end 27 | def ipv6_daddr; self.ipv6_header.ipv6_daddr; end 28 | def ipv6_daddr=(v); self.ipv6_header.ipv6_daddr= v; end 29 | def ipv6_src_readable; self.ipv6_header.ipv6_src_readable; end 30 | def ipv6_dst_readable; self.ipv6_header.ipv6_dst_readable; end 31 | def ipv6?; not self.ipv6_header.nil?; end 32 | 33 | # Add method to each packet class on IPv6 to ease checksum computation 34 | def ipv6_calc_sum_on_addr(cksum=0) 35 | checksum = cksum 36 | [ipv6_src, ipv6_dst].each do |iaddr| 37 | 8.times do |i| 38 | checksum += (iaddr >> (i * 16)) & 0xffff 39 | end 40 | end 41 | checksum 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/packetfu/protos/lldp.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'packetfu/protos/eth/header' 3 | require 'packetfu/protos/eth/mixin' 4 | 5 | require 'packetfu/protos/lldp/header' 6 | require 'packetfu/protos/lldp/mixin' 7 | 8 | module PacketFu 9 | 10 | class LLDPPacket < Packet 11 | MAGIC = Regexp.new("^\x01\x80\xc2\x00\x00[\x0e\x03\x00]".force_encoding('ASCII-8BIT'), Regexp::NOENCODING) 12 | include ::PacketFu::EthHeaderMixin 13 | include ::PacketFu::LLDPHeaderMixin 14 | 15 | attr_accessor :eth_header, :lldp_header 16 | 17 | def self.can_parse?(str) 18 | return false unless EthPacket.can_parse? str 19 | return false unless str.size >= 6 20 | return false unless str[12,2] == "\x88\xcc" 21 | return false unless str =~ MAGIC 22 | true 23 | end 24 | 25 | def initialize(args={}) 26 | @eth_header = EthHeader.new(args).read(args[:eth]) 27 | @lldp_header = LLDPHeader.new(args).read(args[:lldp]) 28 | @eth_header.eth_proto = "\x88\xCC" 29 | @eth_header.body=@lldp_header 30 | 31 | @headers = [@eth_header, @lldp_header] 32 | super 33 | end 34 | 35 | # Generates summary data for LLDP packets. 36 | def peek_format 37 | peek_data = ["A "] 38 | peek_data << "%-5d" % self.to_s.size 39 | peek_data << lldp_saddr_mac 40 | peek_data << "(#{lldp_saddr_mac})" 41 | peek_data << "->" 42 | peek_data << "01:80:c2:00:00:0e" 43 | peek_data.join 44 | end 45 | 46 | # While there are lengths in LLDPPackets, there's not 47 | # much to do with them. 48 | def recalc(args={}) 49 | @headers[0].inspect 50 | end 51 | end 52 | end 53 | 54 | # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby 55 | -------------------------------------------------------------------------------- /lib/packetfu/protos/lldp/mixin.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # This Mixin simplifies access to the LLDPHeaders. Mix this in with your 4 | # packet interface, and it will add methods that essentially delegate to 5 | # the 'lldp_header' method (assuming that it is a LLDPHeader object) 6 | module LLDPHeaderMixin 7 | def lldp_chassis_id_type=(v); self.lldp_header.lldp_chassis_id_type= v; end 8 | def lldp_chassis_id_type; self.lldp_header.lldp_chassis_id_type; end 9 | def lldp_chassis_id=(v); self.lldp_header.lldp_chassis_id= v; end 10 | def lldp_chassis_id; self.lldp_header.lldp_chassis_id_readable(); end 11 | 12 | def lldp_port_id_type=(v); self.lldp_header.lldp_port_id_type= v; end 13 | def lldp_port_id_type; self.lldp_header.lldp_port_id_type; end 14 | def lldp_port_id=(v); self.lldp_header.lldp_port_id= v; end 15 | def lldp_port_id; self.lldp_header.lldp_port_id_readable(); end 16 | 17 | def lldp_ttl=(v); self.lldp_header.lldp_ttl= v; end 18 | def lldp_ttl; self.lldp_header.lldp_ttl; end 19 | 20 | def lldp_port_description=(v); self.lldp_header.lldp_port_description= v; end 21 | def lldp_port_description; self.lldp_header.lldp_port_description; end 22 | 23 | def lldp_system_name=(v); self.lldp_header.lldp_system_name= v; end 24 | def lldp_system_name; self.lldp_header.lldp_system_name; end 25 | 26 | def lldp_system_description=(v); self.lldp_header.lldp_system_description= v; end 27 | def lldp_system_description; self.lldp_header.lldp_system_description; end 28 | 29 | def lldp_capabilty=(v); self.lldp_header.lldp_capabilty= v; end 30 | def lldp_capabilty; self.lldp_header.lldp_capabilty_readable(); end 31 | 32 | def lldp_enabled_capability=(v); self.lldp_header.lldp_enabled_capability= v; end 33 | def lldp_enabled_capability; self.lldp_header.lldp_enabled_capability_readable(); end 34 | 35 | def lldp_address_type=(v); self.lldp_header.lldp_address_type= v; end 36 | def lldp_address_type; self.lldp_header.lldp_address_type; end 37 | 38 | def lldp_address=(v); self.lldp_header.lldp_saddr_ip= v; end 39 | def lldp_address; self.lldp_header.lldp_saddr_ip(); end 40 | 41 | def lldp_interface_type=(v); self.lldp_header.lldp_interface_type= v; end 42 | def lldp_interface_type; self.lldp_header.lldp_interface_type; end 43 | 44 | def lldp_interface=(v); self.lldp_header.lldp_interface= v; end 45 | def lldp_interface; self.lldp_header.lldp_interface; end 46 | 47 | def lldp_oid=(v); self.lldp_header.lldp_oid= v; end 48 | def lldp_oid; self.lldp_header.lldp_oid; end 49 | 50 | def lldp_saddr_mac=(v); self.lldp_header.lldp_saddr_mac= v; end 51 | def lldp_saddr_mac; self.lldp_header.lldp_saddr_mac; end 52 | def lldp_saddr_ip=(v); self.lldp_header.lldp_saddr_ip= v; end 53 | def lldp_saddr_ip; self.lldp_header.lldp_saddr_ip(); end 54 | 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/packetfu/protos/tcp/ecn.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # Implements the Explict Congestion Notification for TCPHeader. 4 | # 5 | # ==== Header Definition 6 | # 7 | # 8 | # Integer(1 bit) :n 9 | # Integer(1 bit) :c 10 | # Integer(1 bit) :e 11 | class TcpEcn < Struct.new(:n, :c, :e) 12 | 13 | include StructFu 14 | 15 | def initialize(args={}) 16 | super(args[:n], args[:c], args[:e]) if args 17 | end 18 | 19 | # Returns the TcpEcn field as an integer... even though it's going 20 | # to be split across a byte boundary. 21 | def to_i 22 | (n.to_i << 2) + (c.to_i << 1) + e.to_i 23 | end 24 | 25 | # Reads a string to populate the object. 26 | def read(str) 27 | force_binary(str) 28 | return self if str.nil? || str.size < 2 29 | if 1.respond_to? :ord 30 | byte1 = str[0].ord 31 | byte2 = str[1].ord 32 | else 33 | byte1 = str[0] 34 | byte2 = str[1] 35 | end 36 | self[:n] = byte1 & 0b00000001 == 0b00000001 ? 1 : 0 37 | self[:c] = byte2 & 0b10000000 == 0b10000000 ? 1 : 0 38 | self[:e] = byte2 & 0b01000000 == 0b01000000 ? 1 : 0 39 | self 40 | end 41 | 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/packetfu/protos/tcp/flags.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # Implements flags for TCPHeader. 4 | # 5 | # ==== Header Definition 6 | # 7 | # Integer(1 bit) :urg 8 | # Integer(1 bit) :ack 9 | # Integer(1 bit) :psh 10 | # Integer(1 bit) :rst 11 | # Integer(1 bit) :syn 12 | # Integer(1 bit) :fin 13 | # 14 | # Flags can typically be set by setting them either to 1 or 0, or to true or false. 15 | class TcpFlags < Struct.new(:urg, :ack, :psh, :rst, :syn, :fin) 16 | 17 | include StructFu 18 | 19 | def initialize(args={}) 20 | # This technique attemts to ensure that flags are always 0 (off) 21 | # or 1 (on). Statements like nil and false shouldn't be lurking in here. 22 | if args.nil? || args.size.zero? 23 | super( 0, 0, 0, 0, 0, 0) 24 | else 25 | super( 26 | (args[:urg] ? 1 : 0), 27 | (args[:ack] ? 1 : 0), 28 | (args[:psh] ? 1 : 0), 29 | (args[:rst] ? 1 : 0), 30 | (args[:syn] ? 1 : 0), 31 | (args[:fin] ? 1 : 0) 32 | ) 33 | end 34 | end 35 | 36 | # Returns the TcpFlags as an integer. 37 | # Also not a great candidate for to_s due to the short bitspace. 38 | def to_i 39 | (urg.to_i << 5) + (ack.to_i << 4) + (psh.to_i << 3) + 40 | (rst.to_i << 2) + (syn.to_i << 1) + fin.to_i 41 | end 42 | 43 | # Helper to determine if this flag is a 1 or a 0. 44 | def zero_or_one(i=0) 45 | if i == 0 || i == false || i == nil 46 | 0 47 | else 48 | 1 49 | end 50 | end 51 | 52 | # Setter for the Urgent flag. 53 | def urg=(i); self[:urg] = zero_or_one(i); end 54 | # Setter for the Acknowlege flag. 55 | def ack=(i); self[:ack] = zero_or_one(i); end 56 | # Setter for the Push flag. 57 | def psh=(i); self[:psh] = zero_or_one(i); end 58 | # Setter for the Reset flag. 59 | def rst=(i); self[:rst] = zero_or_one(i); end 60 | # Setter for the Synchronize flag. 61 | def syn=(i); self[:syn] = zero_or_one(i); end 62 | # Setter for the Finish flag. 63 | def fin=(i); self[:fin] = zero_or_one(i); end 64 | 65 | # Reads a string to populate the object. 66 | def read(str) 67 | force_binary(str) 68 | return self if str.nil? 69 | if 1.respond_to? :ord 70 | byte = str[0].ord 71 | else 72 | byte = str[0] 73 | end 74 | self[:urg] = byte & 0b00100000 == 0b00100000 ? 1 : 0 75 | self[:ack] = byte & 0b00010000 == 0b00010000 ? 1 : 0 76 | self[:psh] = byte & 0b00001000 == 0b00001000 ? 1 : 0 77 | self[:rst] = byte & 0b00000100 == 0b00000100 ? 1 : 0 78 | self[:syn] = byte & 0b00000010 == 0b00000010 ? 1 : 0 79 | self[:fin] = byte & 0b00000001 == 0b00000001 ? 1 : 0 80 | self 81 | end 82 | 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/packetfu/protos/tcp/hlen.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # Implements the Header Length for TCPHeader. 4 | # 5 | # ==== Header Definition 6 | # 7 | # Integer(4 bits) :hlen 8 | class TcpHlen < Struct.new(:hlen) 9 | 10 | include StructFu 11 | 12 | def initialize(args={}) 13 | super(args[:hlen]) 14 | end 15 | 16 | # Returns the TcpHlen field as an integer. Note these will become the high 17 | # bits at the TCP header's offset, even though the lower 4 bits 18 | # will be further chopped up. 19 | def to_i 20 | hlen.to_i & 0b1111 21 | end 22 | 23 | # Reads a string to populate the object. 24 | def read(str) 25 | force_binary(str) 26 | return self if str.nil? || str.size.zero? 27 | if 1.respond_to? :ord 28 | self[:hlen] = (str[0].ord & 0b11110000) >> 4 29 | else 30 | self[:hlen] = (str[0] & 0b11110000) >> 4 31 | end 32 | self 33 | end 34 | 35 | # Returns the object in string form. 36 | def to_s 37 | [self.to_i].pack("C") 38 | end 39 | 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/packetfu/protos/tcp/mixin.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # This Mixin simplifies access to the TCPHeaders. Mix this in with your 4 | # packet interface, and it will add methods that essentially delegate to 5 | # the 'tcp_header' method (assuming that it is a TCPHeader object) 6 | module TCPHeaderMixin 7 | def tcp_src=(v); self.tcp_header.tcp_src= v; end 8 | def tcp_src; self.tcp_header.tcp_src; end 9 | def tcp_dst=(v); self.tcp_header.tcp_dst= v; end 10 | def tcp_dst; self.tcp_header.tcp_dst; end 11 | def tcp_seq=(v); self.tcp_header.tcp_seq= v; end 12 | def tcp_seq; self.tcp_header.tcp_seq; end 13 | def tcp_ack=(v); self.tcp_header.tcp_ack= v; end 14 | def tcp_ack; self.tcp_header.tcp_ack; end 15 | def tcp_win=(v); self.tcp_header.tcp_win= v; end 16 | def tcp_win; self.tcp_header.tcp_win; end 17 | def tcp_sum=(v); self.tcp_header.tcp_sum= v; end 18 | def tcp_sum; self.tcp_header.tcp_sum; end 19 | def tcp_urg=(v); self.tcp_header.tcp_urg= v; end 20 | def tcp_urg; self.tcp_header.tcp_urg; end 21 | def tcp_hlen; self.tcp_header.tcp_hlen; end 22 | def tcp_hlen=(v); self.tcp_header.tcp_hlen= v; end 23 | def tcp_reserved; self.tcp_header.tcp_reserved; end 24 | def tcp_reserved=(v); self.tcp_header.tcp_reserved= v; end 25 | def tcp_ecn; self.tcp_header.tcp_ecn; end 26 | def tcp_ecn=(v); self.tcp_header.tcp_ecn= v; end 27 | def tcp_opts; self.tcp_header.tcp_opts; end 28 | def tcp_opts=(v); self.tcp_header.tcp_opts= v; end 29 | def tcp_calc_seq; self.tcp_header.tcp_calc_seq; end 30 | def tcp_calc_src; self.tcp_header.tcp_calc_src; end 31 | def tcp_opts_len; self.tcp_header.tcp_opts_len; end 32 | def tcp_calc_hlen; self.tcp_header.tcp_calc_hlen; end 33 | def tcp_options; self.tcp_header.tcp_options; end 34 | def tcp_flags_dotmap; self.tcp_header.tcp_flags_dotmap; end 35 | def tcp_options=(v); self.tcp_header.tcp_options= v; end 36 | def tcp_sport; self.tcp_header.tcp_sport; end 37 | def tcp_sport=(v); self.tcp_header.tcp_sport= v; end 38 | def tcp_dport; self.tcp_header.tcp_dport; end 39 | def tcp_dport=(v); self.tcp_header.tcp_dport= v; end 40 | def tcp_recalc(*v); self.tcp_header.tcp_recalc(*v); end 41 | def tcp_flags_readable; self.tcp_header.tcp_flags_readable; end 42 | def tcp_ack_readable; self.tcp_header.tcp_ack_readable; end 43 | def tcp_seq_readable; self.tcp_header.tcp_seq_readable; end 44 | def tcp_sum_readable; self.tcp_header.tcp_sum_readable; end 45 | def tcp_opts_readable; self.tcp_header.tcp_opts_readable; end 46 | def tcp_flags; self.tcp_header.tcp_flags; end 47 | def tcp_flags=(v); self.tcp_header.tcp_flags= v; end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/packetfu/protos/tcp/options.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'packetfu/protos/tcp/option' 3 | 4 | module PacketFu 5 | 6 | class TcpOptions < Array 7 | 8 | include StructFu 9 | 10 | # If args[:pad] is set, the options line is automatically padded out 11 | # with NOPs. 12 | def to_s(args={}) 13 | opts = self.map {|x| x.to_s}.join 14 | if args[:pad] 15 | unless (opts.size % 4).zero? 16 | (4 - (opts.size % 4)).times { opts << "\x01" } 17 | end 18 | end 19 | opts 20 | end 21 | 22 | # Reads a string to populate the object. 23 | def read(str) 24 | self.clear 25 | PacketFu.force_binary(str) 26 | return self if(!str.respond_to? :to_s || str.nil?) 27 | i = 0 28 | while i < str.to_s.size 29 | this_opt = case str[i,1].unpack("C").first 30 | when 0; ::PacketFu::TcpOption::EOL.new 31 | when 1; ::PacketFu::TcpOption::NOP.new 32 | when 2; ::PacketFu::TcpOption::MSS.new 33 | when 3; ::PacketFu::TcpOption::WS.new 34 | when 4; ::PacketFu::TcpOption::SACKOK.new 35 | when 5; ::PacketFu::TcpOption::SACK.new 36 | when 6; ::PacketFu::TcpOption::ECHO.new 37 | when 7; ::PacketFu::TcpOption::ECHOREPLY.new 38 | when 8; ::PacketFu::TcpOption::TS.new 39 | else; ::PacketFu::TcpOption.new 40 | end 41 | this_opt.read str[i,str.size] 42 | unless this_opt.has_optlen? 43 | this_opt.value = nil 44 | this_opt.optlen = nil 45 | end 46 | self << this_opt 47 | i += this_opt.sz 48 | end 49 | self 50 | end 51 | 52 | # Decode parses the TcpOptions object's member options, and produces a 53 | # human-readable string by iterating over each element's decode() function. 54 | # If TcpOptions elements were not initially created as TcpOptions, an 55 | # attempt will be made to convert them. 56 | # 57 | # The output of decode is suitable as input for TcpOptions#encode. 58 | def decode 59 | decoded = self.map do |x| 60 | if x.kind_of? TcpOption 61 | x.decode 62 | else 63 | x = TcpOptions.new.read(x).decode 64 | end 65 | end 66 | decoded.join(",") 67 | end 68 | 69 | # Encode takes a human-readable string and appends the corresponding 70 | # binary options to the TcpOptions object. To completely replace the contents 71 | # of the object, use TcpOptions#encode! instead. 72 | # 73 | # Options are comma-delimited, and are identical to the output of the 74 | # TcpOptions#decode function. Note that the syntax can be unforgiving, so 75 | # it may be easier to create the subclassed TcpOptions themselves directly, 76 | # but this method can be less typing if you know what you're doing. 77 | # 78 | # Note that by using TcpOptions#encode, strings supplied as values which 79 | # can be converted to numbers will be converted first. 80 | # 81 | # === Example 82 | # 83 | # t = TcpOptions.new 84 | # t.encode("MS:1460,WS:6") 85 | # t.to_s # => "\002\004\005\264\002\003\006" 86 | # t.encode("NOP") 87 | # t.to_s # => "\002\004\005\264\002\003\006\001" 88 | def encode(str) 89 | opts = str.split(/[\s]*,[\s]*/) 90 | opts.each do |o| 91 | kind,value = o.split(/[\s]*:[\s]*/) 92 | klass = TcpOption.const_get(kind.upcase) 93 | value = value.to_i if value =~ /^[0-9]+$/ 94 | this_opt = klass.new 95 | this_opt.encode(value) 96 | self << this_opt 97 | end 98 | self 99 | end 100 | 101 | # Like TcpOption#encode, except the entire contents are replaced. 102 | def encode!(str) 103 | self.clear if self.size > 0 104 | encode(str) 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /lib/packetfu/protos/tcp/reserved.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # Implements the Reserved bits for TCPHeader. 4 | # 5 | # ==== Header Definition 6 | # 7 | # 8 | # Integer(1 bit) :r1 9 | # Integer(1 bit) :r2 10 | # Integer(1 bit) :r3 11 | class TcpReserved < Struct.new(:r1, :r2, :r3) 12 | 13 | include StructFu 14 | 15 | def initialize(args={}) 16 | super( 17 | args[:r1] || 0, 18 | args[:r2] || 0, 19 | args[:r3] || 0) if args.kind_of? Hash 20 | end 21 | 22 | # Returns the Reserved field as an integer. 23 | def to_i 24 | (r1.to_i << 2) + (r2.to_i << 1) + r3.to_i 25 | end 26 | 27 | # Reads a string to populate the object. 28 | def read(str) 29 | force_binary(str) 30 | return self if str.nil? || str.size.zero? 31 | if 1.respond_to? :ord 32 | byte = str[0].ord 33 | else 34 | byte = str[0] 35 | end 36 | self[:r1] = byte & 0b00000100 == 0b00000100 ? 1 : 0 37 | self[:r2] = byte & 0b00000010 == 0b00000010 ? 1 : 0 38 | self[:r3] = byte & 0b00000001 == 0b00000001 ? 1 : 0 39 | self 40 | end 41 | 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/packetfu/protos/udp/header.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | 4 | # UDPHeader is a complete UDP struct, used in UDPPacket. Many Internet-critical protocols 5 | # rely on UDP, such as DNS and World of Warcraft. 6 | # 7 | # For more on UDP packets, see http://www.networksorcery.com/enp/protocol/udp.htm 8 | # 9 | # ==== Header Definition 10 | # Int16 :udp_src 11 | # Int16 :udp_dst 12 | # Int16 :udp_len Default: calculated 13 | # Int16 :udp_sum Default: 0. Often calculated. 14 | # String :body 15 | class UDPHeader < Struct.new(:udp_src, :udp_dst, :udp_len, :udp_sum, :body) 16 | 17 | include StructFu 18 | 19 | def initialize(args={}) 20 | super( 21 | Int16.new(args[:udp_src]), 22 | Int16.new(args[:udp_dst]), 23 | Int16.new(args[:udp_len] || udp_calc_len), 24 | Int16.new(args[:udp_sum]), 25 | StructFu::String.new.read(args[:body]) 26 | ) 27 | end 28 | 29 | # Returns the object in string form. 30 | def to_s 31 | self.to_a.map {|x| x.to_s}.join 32 | end 33 | 34 | # Reads a string to populate the object. 35 | def read(str) 36 | force_binary(str) 37 | return self if str.nil? 38 | self[:udp_src].read(str[0,2]) 39 | self[:udp_dst].read(str[2,2]) 40 | self[:udp_len].read(str[4,2]) 41 | self[:udp_sum].read(str[6,2]) 42 | self[:body].read(str[8,str.size]) 43 | self 44 | end 45 | 46 | # Setter for the UDP source port. 47 | def udp_src=(i); typecast i; end 48 | # Getter for the UDP source port. 49 | def udp_src; self[:udp_src].to_i; end 50 | # Setter for the UDP destination port. 51 | def udp_dst=(i); typecast i; end 52 | # Getter for the UDP destination port. 53 | def udp_dst; self[:udp_dst].to_i; end 54 | # Setter for the length field. Usually should be recalc()'ed instead. 55 | def udp_len=(i); typecast i; end 56 | # Getter for the length field. 57 | def udp_len; self[:udp_len].to_i; end 58 | # Setter for the checksum. Usually should be recalc()'ed instad. 59 | def udp_sum=(i); typecast i; end 60 | # Getter for the checksum. 61 | def udp_sum; self[:udp_sum].to_i; end 62 | 63 | # Returns the true length of the UDP packet. 64 | def udp_calc_len 65 | body.to_s.size + 8 66 | end 67 | 68 | # Recalculates calculated fields for UDP. 69 | def udp_recalc(arg = :all) 70 | case arg.to_sym 71 | when :udp_len 72 | self.udp_len = udp_calc_len 73 | when :all 74 | self.udp_recalc(:udp_len) 75 | else 76 | raise ArgumentError, "No such field `#{arg}'" 77 | end 78 | end 79 | 80 | # Equivalent to udp_src.to_i 81 | def udp_sport 82 | self.udp_src 83 | end 84 | 85 | # Equivalent to udp_src= 86 | def udp_sport=(arg) 87 | self.udp_src=(arg) 88 | end 89 | 90 | # Equivalent to udp_dst 91 | def udp_dport 92 | self.udp_dst 93 | end 94 | 95 | # Equivalent to udp_dst= 96 | def udp_dport=(arg) 97 | self.udp_dst=(arg) 98 | end 99 | 100 | # Readability aliases 101 | 102 | def udp_sum_readable 103 | "0x%04x" % udp_sum 104 | end 105 | 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /lib/packetfu/protos/udp/mixin.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | # This Mixin simplifies access to the UDPHeaders. Mix this in with your 4 | # packet interface, and it will add methods that essentially delegate to 5 | # the 'udp_header' method (assuming that it is a UDPHeader object) 6 | module UDPHeaderMixin 7 | def udp_src=(v); self.udp_header.udp_src= v; end 8 | def udp_src; self.udp_header.udp_src; end 9 | def udp_dst=(v); self.udp_header.udp_dst= v; end 10 | def udp_dst; self.udp_header.udp_dst; end 11 | def udp_len=(v); self.udp_header.udp_len= v; end 12 | def udp_len; self.udp_header.udp_len; end 13 | def udp_sum=(v); self.udp_header.udp_sum= v; end 14 | def udp_sum; self.udp_header.udp_sum; end 15 | def udp_calc_len; self.udp_header.udp_calc_len; end 16 | def udp_recalc(*v); self.udp_header.udp_recalc(*v); end 17 | def udp_sport; self.udp_header.udp_sport; end 18 | def udp_sport=(v); self.udp_header.udp_sport= v; end 19 | def udp_dport; self.udp_header.udp_dport; end 20 | def udp_dport=(v); self.udp_header.udp_dport= v; end 21 | def udp_sum_readable; self.udp_header.udp_sum_readable; end 22 | end 23 | end 24 | 25 | -------------------------------------------------------------------------------- /lib/packetfu/version.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | module PacketFu 3 | 4 | # Check the repo's for version release histories 5 | VERSION = "2.0.0" 6 | 7 | # Returns PacketFu::VERSION 8 | def self.version 9 | VERSION 10 | end 11 | 12 | # Returns a version string in a binary format for easy comparisons. 13 | def self.binarize_version(str) 14 | if(str.respond_to?(:split) && str =~ /^[0-9]+(\.([0-9]+)(\.[0-9]+)?)?\..+$/) 15 | bin_major,bin_minor,bin_teeny = str.split(/\x2e/).map {|x| x.to_i} 16 | bin_version = (bin_major.to_i << 16) + (bin_minor.to_i << 8) + bin_teeny.to_i 17 | else 18 | raise ArgumentError, "Compare version malformed. Should be \x22x.y.z\x22" 19 | end 20 | end 21 | 22 | # Returns true if the version is equal to or greater than the compare version. 23 | # If the current version of PacketFu is "0.3.1" for example: 24 | # 25 | # PacketFu.at_least? "0" # => true 26 | # PacketFu.at_least? "0.2.9" # => true 27 | # PacketFu.at_least? "0.3" # => true 28 | # PacketFu.at_least? "1" # => true after 1.0's release 29 | # PacketFu.at_least? "1.12" # => false 30 | # PacketFu.at_least? "2" # => false 31 | def self.at_least?(str) 32 | this_version = binarize_version(self.version) 33 | ask_version = binarize_version(str) 34 | this_version >= ask_version 35 | end 36 | 37 | # Returns true if the current version is older than the compare version. 38 | def self.older_than?(str) 39 | return false if str == self.version 40 | this_version = binarize_version(self.version) 41 | ask_version = binarize_version(str) 42 | this_version < ask_version 43 | end 44 | 45 | # Returns true if the current version is newer than the compare version. 46 | def self.newer_than?(str) 47 | return false if str == self.version 48 | !self.older_than?(str) 49 | end 50 | 51 | end 52 | -------------------------------------------------------------------------------- /packetfu.gemspec: -------------------------------------------------------------------------------- 1 | require './lib/packetfu/version' 2 | 3 | Gem::Specification.new do |s| 4 | s.name = 'packetfu' 5 | s.version = PacketFu::VERSION 6 | s.authors = ['Tod Beardsley', 'Jonathan Claudius'] 7 | s.email = ['todb@packetfu.com', 'claudijd@yahoo.com'] 8 | s.summary = 'PacketFu is a mid-level packet manipulation library.' 9 | s.homepage = 'https://github.com/packetfu/packetfu' 10 | s.description = %q{ 11 | PacketFu is a mid-level packet manipulation library for Ruby. With 12 | it, users can read, parse, and write network packets with the level of 13 | ease and fun they expect from Ruby. 14 | } 15 | s.files = `git ls-files`.split($/) 16 | s.license = 'BSD-3-Clause' 17 | s.required_ruby_version = '>= 2.7.0' 18 | s.add_dependency('pcaprub', '~> 0.13.1') 19 | s.add_development_dependency('rake') 20 | s.add_development_dependency('rspec', '~> 3.0') 21 | s.add_development_dependency('rspec-its', '~> 1.2') 22 | s.add_development_dependency('sdoc', '~> 0.4') 23 | s.add_development_dependency('pry-byebug') 24 | s.add_development_dependency('coveralls', '~> 0.8') 25 | 26 | s.extra_rdoc_files = %w[.document README.md] 27 | s.test_files = (s.files & (Dir['spec/**/*_spec.rb'] + Dir['test/test_*.rb']) ) 28 | 29 | s.cert_chain = ['certs/todb.pem'] 30 | s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/ 31 | end 32 | -------------------------------------------------------------------------------- /spec/fake_packets.rb: -------------------------------------------------------------------------------- 1 | module FakePacket 2 | def layer 3 | 7 4 | end 5 | end 6 | 7 | class PacketFu::FooPacket < PacketFu::Packet 8 | extend FakePacket 9 | end 10 | 11 | class PacketFu::BarPacket < PacketFu::Packet 12 | extend FakePacket 13 | end 14 | 15 | class PacketBaz 16 | end 17 | 18 | def add_fake_packets 19 | PacketFu.add_packet_class(PacketFu::FooPacket) 20 | PacketFu.add_packet_class(PacketFu::BarPacket) 21 | end 22 | 23 | def remove_fake_packets 24 | PacketFu.remove_packet_class(PacketFu::FooPacket) 25 | PacketFu.remove_packet_class(PacketFu::BarPacket) 26 | end 27 | 28 | remove_fake_packets 29 | -------------------------------------------------------------------------------- /spec/hsrp_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'spec_helper' 3 | require 'packetfu' 4 | 5 | include PacketFu 6 | 7 | context "when parsing HSRP traffic from pcap" do 8 | it "should detect that it's HSRP traffic" do 9 | sample_packet = PcapFile.new.file_to_array(:f => 'test/sample_hsrp_pcapr.cap')[0] 10 | pkt = Packet.parse(sample_packet) 11 | expect(pkt.is_hsrp?).to be(true) 12 | expect(pkt.is_udp?).to be(true) 13 | expect(pkt.udp_sum.to_i).to eql(0x2d8d) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/icmp_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'spec_helper' 3 | require 'packetfu/protos/eth' 4 | require 'packetfu/protos/ip' 5 | require 'packetfu/protos/ipv6' 6 | require 'packetfu/protos/icmp' 7 | require 'packetfu/pcap' 8 | require 'tempfile' 9 | 10 | include PacketFu 11 | 12 | describe ICMPPacket, "when read from a pcap file" do 13 | before :all do 14 | parsed_packets = PcapFile.read_packets(File.join(File.dirname(__FILE__),"sample.pcap")) 15 | @icmp_packet = parsed_packets[3] 16 | 17 | parsed_packets3 = PcapFile.read_packets(File.join(File.dirname(__FILE__),"sample3.pcap")) 18 | @icmp_packet2 = parsed_packets3[8] # contains 0x0A byte in payload 19 | end 20 | 21 | it "should be recognized as an icmp packet" do 22 | @icmp_packet.is_icmp?.should be true 23 | end 24 | 25 | it "should report the right seq number" do 26 | @icmp_packet.payload[2..3].unpack("H*")[0].should eq "0003" 27 | end 28 | 29 | it "should be recognized as an icmp reply packet" do 30 | @icmp_packet.icmp_type.should eq 0 31 | end 32 | 33 | it "should have the right checksum" do 34 | @icmp_packet.icmp_sum.to_s(16).should eq @icmp_packet.icmp_calc_sum.to_s(16) 35 | end 36 | 37 | it "should have the right checksum even with 0xOA byte in payload" do 38 | @icmp_packet2.icmp_sum.to_s(16).should eq @icmp_packet2.icmp_calc_sum.to_s(16) 39 | end 40 | 41 | context "when initializing ICMPHeader from scratch" do 42 | before :each do 43 | @icmp_header = ICMPHeader.new 44 | end 45 | 46 | it "should have the right instance variables" do 47 | expect(@icmp_header.to_s).to eql("\x00\x00\xff\xff") 48 | expect(@icmp_header.icmp_type).to eql(0) 49 | end 50 | 51 | it "should allow setting of the type" do 52 | @icmp_header.icmp_type = 1 53 | expect(@icmp_header.icmp_type).to eql(1) 54 | @icmp_header.icmp_recalc 55 | expect(@icmp_header.to_s).to eql("\x01\x00\xfe\xff") 56 | end 57 | end 58 | 59 | context "when initializing ICMPPacket from scratch" do 60 | before :each do 61 | @icmp_packet = ICMPPacket.new 62 | end 63 | 64 | it "should support peak functionality" do 65 | @icmp_packet.ip_saddr = "10.20.30.40" 66 | @icmp_packet.ip_daddr = "50.60.70.80" 67 | @icmp_packet.payload = "abcdefghijklmnopqrstuvwxyz" 68 | @icmp_packet.recalc 69 | expect(@icmp_packet.peek).to match(/IC 64\s+10.20.30.40:pong\s+->\s+50.60.70.80\s+I:[a-z0-9]{4}/) 70 | end 71 | end 72 | 73 | context "when reading/writing ICMPPacket to disk" do 74 | before :each do 75 | @icmp_packet = ICMPPacket.new 76 | @temp_file = Tempfile.new('icmp_pcap') 77 | end 78 | 79 | after(:each) { @temp_file.close; @temp_file.unlink } 80 | 81 | it "should write a PCAP file to disk" do 82 | @icmp_packet.ip_saddr = "10.20.30.40" 83 | @icmp_packet.ip_daddr = "50.60.70.80" 84 | @icmp_packet.payload = "abcdefghijklmnopqrstuvwxyz" 85 | @icmp_packet.recalc 86 | 87 | expect(@temp_file.read).to eql("") 88 | 89 | @icmp_packet.to_f(@temp_file.path, 'a') 90 | expect(File.exist?(@temp_file.path)) 91 | expect(@temp_file.read.size).to be >= 79 92 | end 93 | 94 | it "should read a PCAP file from disk" do 95 | sample_packet = PcapFile.new.file_to_array(:f => './test/sample.pcap')[2] 96 | pkt = Packet.parse(sample_packet) 97 | 98 | expect(pkt.is_icmp?).to be true 99 | expect(pkt.class).to eql(PacketFu::ICMPPacket) 100 | expect(pkt.icmp_sum.to_i).to eql(0x4d58) 101 | expect(pkt.icmp_type.to_i).to eql(8) 102 | end 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /spec/icmpv6_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'spec_helper' 3 | require 'packetfu/protos/eth' 4 | require 'packetfu/protos/ipv6' 5 | require 'packetfu/protos/icmpv6' 6 | require 'packetfu/pcap' 7 | require 'tempfile' 8 | 9 | include PacketFu 10 | 11 | describe ICMPv6Packet, "when read from a pcap file" do 12 | before(:all) do 13 | parsed_packets = PcapFile.read_packets(File.join(File.dirname(__FILE__), 14 | "ipv6_icmp.pcap")) 15 | @icmpv6_packet = parsed_packets.first 16 | end 17 | 18 | it 'should be recognized as an icmp packet' do 19 | expect(@icmpv6_packet.is_icmpv6?).to be(true) 20 | end 21 | 22 | it "should report the right seq number" do 23 | expect(@icmpv6_packet.payload[2..3].unpack("H*")[0]).to eq("0001") 24 | end 25 | 26 | it "should be recognized as an icmp reply packet" do 27 | expect(@icmpv6_packet.icmpv6_type).to eq(128) 28 | end 29 | 30 | it "should have the right checksum" do 31 | expect(@icmpv6_packet.icmpv6_sum.to_s(16)).to eq(@icmpv6_packet.icmpv6_calc_sum.to_s(16)) 32 | end 33 | 34 | 35 | context "when initializing ICMPv6Header from scratch" do 36 | before :each do 37 | @icmpv6_header = ICMPv6Header.new 38 | end 39 | 40 | it "should have the right instance variables" do 41 | expect(@icmpv6_header.to_s).to eql("\x00\x00\x00\x00") 42 | expect(@icmpv6_header.icmpv6_type).to eql(0) 43 | end 44 | 45 | it "should allow setting of the type" do 46 | @icmpv6_header.icmpv6_type = 1 47 | expect(@icmpv6_header.icmpv6_type).to eql(1) 48 | end 49 | end 50 | 51 | context "when initializing ICMPv6Packet from scratch" do 52 | before :each do 53 | @icmpv6_packet = ICMPv6Packet.new 54 | end 55 | 56 | it "should support peak functionality" do 57 | @icmpv6_packet.ipv6_saddr = "::1:1020:3040" 58 | @icmpv6_packet.ipv6_daddr = "::1:5060:7080" 59 | @icmpv6_packet.icmpv6_type = 129 60 | @icmpv6_packet.payload = "abcdefghijklmnopqrstuvwxyz" 61 | @icmpv6_packet.recalc 62 | expect(@icmpv6_packet.peek).to match(/6C 84\s+::1:1020:3040:pong\s+->\s+::1:5060:7080/) 63 | end 64 | end 65 | 66 | context "when reading/writing ICMPv6Packet to disk" do 67 | before :each do 68 | @icmpv6_packet = ICMPv6Packet.new 69 | @temp_file = Tempfile.new('icmpv6_pcap') 70 | end 71 | 72 | after(:each) { @temp_file.close; @temp_file.unlink } 73 | 74 | it "should write a PCAP file to disk" do 75 | @icmpv6_packet.ipv6_saddr = "::1:1020:3040" 76 | @icmpv6_packet.ipv6_daddr = "::1:5060:7080" 77 | @icmpv6_packet.payload = "abcdefghijklmnopqrstuvwxyz" 78 | @icmpv6_packet.recalc 79 | 80 | expect(@temp_file.read).to eql("") 81 | 82 | @icmpv6_packet.to_f(@temp_file.path, 'a') 83 | expect(File.exist?(@temp_file.path)) 84 | expect(@temp_file.read.size).to be >= 79 85 | end 86 | 87 | it "should read a PCAP file from disk" do 88 | sample_packet = PcapFile.new.file_to_array(:f => './spec/ipv6_icmp.pcap').first 89 | pkt = Packet.parse(sample_packet) 90 | 91 | expect(pkt.is_icmpv6?).to be true 92 | expect(pkt.class).to eql(PacketFu::ICMPv6Packet) 93 | expect(pkt.icmpv6_sum.to_i).to eql(0x24a5) 94 | expect(pkt.icmpv6_type.to_i).to eql(128) 95 | end 96 | end 97 | 98 | end 99 | -------------------------------------------------------------------------------- /spec/inject_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'spec_helper' 3 | require 'packetfu/protos/eth' 4 | require 'packetfu/protos/ip' 5 | require 'packetfu/protos/ipv6' 6 | require 'packetfu/protos/tcp' 7 | require 'packetfu/protos/udp' 8 | require 'packetfu/protos/icmp' 9 | require 'packetfu/config' 10 | require 'packetfu/pcap' 11 | require 'packetfu/utils' 12 | require 'tempfile' 13 | 14 | include PacketFu 15 | 16 | describe Inject do 17 | context "when creating an object from scratch" do 18 | before :each do 19 | @inject = PacketFu::Inject.new 20 | end 21 | 22 | it "should have sane defaults" do 23 | expect(@inject.array).to be_kind_of(Array) 24 | expect(@inject.stream).to be_kind_of(Array) 25 | expect(@inject.iface).to be_kind_of(String) 26 | expect(@inject.snaplen).to eql(65535) 27 | expect(@inject.promisc).to eql(false) 28 | expect(@inject.timeout).to eql(1) 29 | end 30 | 31 | it "should allow creating an inject object with non-std attributes" do 32 | # Can only run this if we're root 33 | if Process.uid == 0 34 | options = { 35 | :iface => PacketFu::Utils::default_int, 36 | :snaplen => 0xfffe, 37 | :promisc => true, 38 | :timeout => 5, 39 | } 40 | @inject = PacketFu::Capture.new(options) 41 | 42 | expect(@inject.array).to be_kind_of(Array) 43 | expect(@inject.stream).to be_kind_of(Array) 44 | expect(@inject.iface).to eql(options[:iface]) 45 | expect(@inject.snaplen).to eql(options[:snaplen]) 46 | expect(@inject.promisc).to eql(options[:promisc]) 47 | expect(@inject.timeout).to eql(options[:timeout]) 48 | end 49 | end 50 | end 51 | 52 | context "when injecting on the wire" do 53 | before :each do 54 | @inject = PacketFu::Inject.new 55 | end 56 | 57 | it "should have sane defaults" do 58 | expect(@inject.array).to be_kind_of(Array) 59 | expect(@inject.stream).to be_kind_of(Array) 60 | expect(@inject.iface).to be_kind_of(String) 61 | expect(@inject.snaplen).to eql(65535) 62 | expect(@inject.promisc).to eql(false) 63 | expect(@inject.timeout).to eql(1) 64 | end 65 | 66 | # Can only run these if we're root 67 | if Process.uid == 0 68 | it "should allow creating an inject object with non-std attributes" do 69 | udp_packet = PacketFu::UDPPacket.new(:iface => PacketFu::Utils::default_int) 70 | udp_packet.ip_dst = PacketFu::Utils.rand_routable_daddr.to_s 71 | udp_packet.udp_dport = 12345 72 | udp_packet.udp_sport = 12345 73 | udp_packet.payload = "PacketFu test packet" 74 | udp_packet.recalc 75 | 76 | expect(udp_packet.to_w).to eql([1, 1, 62]) 77 | end 78 | 79 | it "should allow creating an inject object with non-std attributes" do 80 | packet_array = [] 81 | 82 | udp_packet = PacketFu::UDPPacket.new(:iface => PacketFu::Utils::default_int) 83 | udp_packet.ip_dst = PacketFu::Utils.rand_routable_daddr.to_s 84 | udp_packet.udp_dport = 12345 85 | udp_packet.udp_sport = 12345 86 | udp_packet.payload = "PacketFu test packet" 87 | udp_packet.recalc 88 | 3.times { packet_array << udp_packet.to_s} 89 | 90 | inject = PacketFu::Inject.new(:iface => PacketFu::Utils::default_int) 91 | expect(inject.array_to_wire(:array => packet_array)).to eql([3, 3, 186]) 92 | end 93 | end 94 | end 95 | end -------------------------------------------------------------------------------- /spec/invalid_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'spec_helper' 3 | require 'packetfu' 4 | 5 | include PacketFu 6 | 7 | describe InvalidPacket, "when read from a pcap file" do 8 | context "when initializing" do 9 | it "should have sane defaults (little)" do 10 | invalid_packet = InvalidPacket.new 11 | expect(invalid_packet).to be_kind_of(InvalidPacket) 12 | expect(invalid_packet).to be_kind_of(Packet) 13 | expect(invalid_packet.is_invalid?).to be(true) 14 | expect(invalid_packet.is_eth?).to be(false) 15 | expect(invalid_packet.class).not_to eql(EthPacket) 16 | end 17 | end 18 | 19 | context "when reading" do 20 | # Sadly, the only way to generate an "InvalidPacket" is 21 | # to read a packet that's less than 14 bytes. Otherwise, 22 | # it's presumed to be an EthPacket. TODO: Fix this assumption! 23 | it "should be an invalid packet" do 24 | invalid_packet = Packet.parse("A" * 13) 25 | expect(invalid_packet).to be_kind_of(InvalidPacket) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/ip_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'packetfu/protos/eth' 3 | require 'packetfu/protos/ip' 4 | require 'packetfu/pcap' 5 | require 'tempfile' 6 | 7 | include PacketFu 8 | 9 | describe IPHeader do 10 | context "when initializing" do 11 | before :each do 12 | @ip_header = IPHeader.new 13 | end 14 | 15 | it "should have sane defaults" do 16 | expect(@ip_header.ip_v).to eql(4) 17 | expect(@ip_header.ip_hl).to eql(5) 18 | expect(@ip_header.ip_tos).to eql(0) 19 | expect(@ip_header.ip_len).to eql(20) 20 | expect(@ip_header.ip_id).to be_kind_of(Integer) 21 | expect(@ip_header.ip_frag).to eql(0) 22 | expect(@ip_header.ip_proto).to eql(0) 23 | expect(@ip_header.ip_sum).to eql(65535) 24 | expect(@ip_header.ip_src).to eql(0) 25 | expect(@ip_header.ip_dst).to eql(0) 26 | expect(@ip_header.ip_src).to be_a(Integer) 27 | expect(@ip_header.ip_dst).to be_a(Integer) 28 | expect(@ip_header.body).to eql("") 29 | end 30 | 31 | it "should parse a raw IPHeader" do 32 | raw_header = "\x45\x10\x00\x4f\x16\xa9\x40\x00\x40\x06\xa2\x9c\xc0\xa8\x00\x02\xc0\xa8\x00\x01" 33 | @ip_header.read(raw_header) 34 | 35 | expect(@ip_header.ip_v).to eql(4) 36 | expect(@ip_header.ip_hl).to eql(5) 37 | expect(@ip_header.ip_tos).to eql(16) 38 | expect(@ip_header.ip_len).to eql(79) 39 | expect(@ip_header.ip_id).to be_kind_of(Integer) 40 | expect(@ip_header.ip_frag).to eql(16384) 41 | expect(@ip_header.ip_proto).to eql(6) 42 | expect(@ip_header.ip_sum).to eql(41628) 43 | expect(@ip_header.ip_src).to eql(3232235522) 44 | expect(@ip_header.ip_dst).to eql(3232235521) 45 | expect(@ip_header.ip_src).to be_a(Integer) 46 | expect(@ip_header.ip_dst).to be_a(Integer) 47 | expect(@ip_header.body).to eql("") 48 | end 49 | 50 | end 51 | end 52 | 53 | describe IPPacket do 54 | context "when initializing" do 55 | before :each do 56 | @ip_packet = IPPacket.new 57 | end 58 | 59 | it "should have sane defaults" do 60 | expect(@ip_packet.ip_v).to eql(4) 61 | expect(@ip_packet.ip_hl).to eql(5) 62 | expect(@ip_packet.ip_tos).to eql(0) 63 | expect(@ip_packet.ip_len).to eql(20) 64 | expect(@ip_packet.ip_id).to be_kind_of(Integer) 65 | expect(@ip_packet.ip_frag).to eql(0) 66 | expect(@ip_packet.ip_proto).to eql(0) 67 | expect(@ip_packet.ip_sum).to eql(65535) 68 | expect(@ip_packet.ip_src).to eql(0) 69 | expect(@ip_packet.ip_dst).to eql(0) 70 | expect(@ip_packet.payload).to eql("") 71 | expect(@ip_packet.is_ip?).to be true 72 | end 73 | 74 | it "should support peek functionality" do 75 | @ip_packet.ip_saddr = "1.2.3.4" 76 | @ip_packet.ip_daddr = "5.6.7.8" 77 | @ip_packet.ip_proto = 94 78 | @ip_packet.payload = '\x00' * 30 79 | @ip_packet.recalc 80 | 81 | expect(@ip_packet.peek).to match(/I\s+154\s+1\.2\.3\.4\s+\->\s+5\.6\.7\.8\s+I:[0-9a-z]{4}/) 82 | end 83 | end 84 | 85 | context "when writing a PCAP file to disk" do 86 | before :each do 87 | @ip_packet = IPPacket.new 88 | @temp_file = Tempfile.new('ip_pcap') 89 | end 90 | 91 | after(:each) { @temp_file.close; @temp_file.unlink } 92 | 93 | it "should write a PCAP file to disk" do 94 | @ip_packet.ip_saddr = "10.20.30.40" 95 | @ip_packet.ip_daddr = "50.60.70.80" 96 | @ip_packet.recalc 97 | 98 | expect(@temp_file.read).to eql("") 99 | 100 | @ip_packet.to_f(@temp_file.path, 'a') 101 | expect(File.exist?(@temp_file.path)) 102 | expect(@temp_file.read.size).to be >= 49 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /spec/ipv4_icmp.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/spec/ipv4_icmp.pcap -------------------------------------------------------------------------------- /spec/ipv4_udp.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/spec/ipv4_udp.pcap -------------------------------------------------------------------------------- /spec/ipv6_icmp.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/spec/ipv6_icmp.pcap -------------------------------------------------------------------------------- /spec/ipv6_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'packetfu/protos/eth' 3 | require 'packetfu/protos/ip' 4 | require 'packetfu/protos/ipv6' 5 | require 'packetfu/pcap' 6 | 7 | include PacketFu 8 | 9 | describe IPv6Header do 10 | context "when initializing an IPv6Header" do 11 | before :each do 12 | @ipv6_header = IPv6Header.new 13 | end 14 | 15 | it "should contain sane defaults" do 16 | expect(@ipv6_header.ipv6_v).to eql(6) 17 | expect(@ipv6_header.ipv6_len).to eql(0) 18 | expect(@ipv6_header.ipv6_src).to eql(0) 19 | expect(@ipv6_header.ipv6_dst).to eql(0) 20 | expect(@ipv6_header.ipv6_hop).to eql(255) 21 | expect(@ipv6_header.ipv6_next).to eql(0) 22 | end 23 | end 24 | end 25 | 26 | describe AddrIpv6 do 27 | context "when parsing IPv6 from wire" do 28 | before :each do 29 | @address_ipv6 = AddrIpv6.new 30 | end 31 | 32 | it "should parse an IPv6 address from string i/o" do 33 | raw_addr_ipv6 = "\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x1a\xc5\xff\xfe\x00\x01\x52" 34 | @address_ipv6.read(raw_addr_ipv6) 35 | 36 | expect(@address_ipv6.to_i).to eql(338288524927261089654170548082086773074) 37 | expect(@address_ipv6.to_x).to eql("fe80::21a:c5ff:fe00:152") 38 | end 39 | 40 | it "should parse an IPv6 address from octet string" do 41 | ipv6_string = "fe80::21a:c5ff:fe00:152" 42 | @address_ipv6.read_x(ipv6_string) 43 | 44 | expect(@address_ipv6.to_x).to eql(ipv6_string) 45 | end 46 | end 47 | end 48 | 49 | describe IPv6Packet do 50 | context "when initializing an IPv6Packet" do 51 | before :each do 52 | @ipv6_packet = IPv6Packet.new 53 | end 54 | 55 | it "should contain sane defaults" do 56 | expect(@ipv6_packet.ipv6_v).to eql(6) 57 | expect(@ipv6_packet.payload).to eql("") 58 | expect(@ipv6_packet.is_ipv6?).to be true 59 | end 60 | 61 | it "should support peek functionality" do 62 | expect(@ipv6_packet.peek).to match(/6\s+54\s+::\s+\->\s+::\s+N:0/) 63 | end 64 | 65 | it 'should set payload size on #recalc' do 66 | @ipv6_packet.payload = "\0" * 14 67 | @ipv6_packet.recalc 68 | expect(@ipv6_packet.ipv6_len).to eq(14) 69 | 70 | @ipv6_packet.payload = "\0" * 255 71 | @ipv6_packet.recalc(:ipv6) 72 | expect(@ipv6_packet.ipv6_len).to eq(255) 73 | end 74 | 75 | it 'should set payload size on #ipv6_recalc' do 76 | @ipv6_packet.payload = "\0" * 3 77 | @ipv6_packet.ipv6_recalc 78 | expect(@ipv6_packet.ipv6_len).to eq(3) 79 | 80 | @ipv6_packet.payload = "\xff" * 12 81 | @ipv6_packet.ipv6_recalc(:ipv6_len) 82 | expect(@ipv6_packet.ipv6_len).to eq(12) 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /spec/ipv6_udp.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/spec/ipv6_udp.pcap -------------------------------------------------------------------------------- /spec/lldp_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'packetfu' 3 | require 'packetfu/protos/lldp' 4 | 5 | include PacketFu 6 | 7 | describe LLDPPacket do 8 | context "when initializing LLDPPacket" do 9 | it "should have sane defaults" do 10 | lldp_packet = LLDPPacket.new 11 | expect(lldp_packet).to be_kind_of(LLDPPacket) 12 | end 13 | end 14 | 15 | context "when reading" do 16 | it "should read from PCAP and detect LLDP packets" do 17 | cap = PacketFu::PcapFile.new.file_to_array(:filename => "./test/sample_lldp.pcap") 18 | 19 | lldap_packet1 = PacketFu::Packet.parse(cap[0]) 20 | expect(lldap_packet1).to be_kind_of(LLDPPacket) 21 | expect(lldap_packet1.is_lldp?).to be(true) 22 | expect(lldap_packet1.proto.last).to eql("LLDP") 23 | expect(lldap_packet1.lldp_capabilty).to eql("0x0080") 24 | expect(lldap_packet1.lldp_address_type_readable).to eql("IPv4") 25 | expect(lldap_packet1.lldp_address).to eql("lldp_address") 26 | expect(lldap_packet1.lldp_interface_type).to eql(2) 27 | expect(lldap_packet1.lldp_interface).to eql(0) 28 | 29 | lldap_packet2 = PacketFu::Packet.parse(cap[1]) 30 | expect(lldap_packet2).to be_kind_of(LLDPPacket) 31 | 32 | lldap_packet3 = PacketFu::Packet.parse(cap[2]) 33 | expect(lldap_packet3).to be_kind_of(LLDPPacket) 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/octets_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'tempfile' 3 | require 'packetfu/protos/ip' 4 | 5 | include PacketFu 6 | 7 | describe Octets do 8 | context "when initializing" do 9 | before :each do 10 | @octets = Octets.new 11 | end 12 | 13 | it "should have sane defaults" do 14 | expect(@octets.to_x).to eql("0.0.0.0") 15 | end 16 | end 17 | 18 | context "when reading from the wire" do 19 | before :each do 20 | @octets = Octets.new 21 | end 22 | 23 | it "should #read from string i/o" do 24 | @octets.read("\x04\x03\x02\x01") 25 | expect(@octets.to_x).to eql("4.3.2.1") 26 | end 27 | 28 | it "should #read_quad from string i/o" do 29 | @octets.read_quad("1.2.3.4") 30 | expect(@octets.to_x).to eql("1.2.3.4") 31 | expect(@octets.to_s).to eql("\x01\x02\x03\x04") 32 | expect(@octets.to_i).to eql(0x01020304) 33 | end 34 | 35 | it "should #read from string i/o (single octet)" do 36 | @octets.read("ABCD") 37 | expect(@octets.o1).to eql(0x41) 38 | expect(@octets.o2).to eql(0x42) 39 | expect(@octets.o3).to eql(0x43) 40 | expect(@octets.o4).to eql(0x44) 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/packet_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'packetfu/packet' 3 | require 'packetfu/pcap' 4 | require 'packetfu/protos/eth' 5 | require 'packetfu/protos/ip' 6 | require 'packetfu/protos/ipv6' 7 | require 'packetfu/protos/tcp' 8 | require 'packetfu/protos/icmp' 9 | require 'fake_packets' 10 | 11 | describe PacketFu::Packet, "abstract packet class behavior" do 12 | 13 | before(:all) do 14 | add_fake_packets 15 | end 16 | 17 | after(:all) do 18 | remove_fake_packets 19 | end 20 | 21 | it "should not be instantiated" do 22 | expect { PacketFu::Packet.new }.to raise_error(NoMethodError) 23 | end 24 | 25 | it "should allow subclasses to instantiate" do 26 | expect(PacketFu::FooPacket.new).to be 27 | PacketFu.packet_classes.include?(PacketFu::FooPacket).should be true 28 | end 29 | 30 | it "should register packet classes with PacketFu" do 31 | PacketFu.packet_classes {should include(FooPacket) } 32 | PacketFu.packet_classes {should include(BarPacket) } 33 | end 34 | 35 | it "should disallow badly named subclasses" do 36 | expect { 37 | class PacketFu::PacketNot < PacketFu::Packet 38 | end 39 | }.to raise_error(RuntimeError, "Packet classes should be named 'ProtoPacket'") 40 | PacketFu.packet_classes.include?(PacketFu::PacketNot).should be false 41 | PacketFu.packet_classes {should_not include(PacketNot) } 42 | end 43 | 44 | before(:each) do 45 | @tcp_packet = PacketFu::TCPPacket.new 46 | @tcp_packet.ip_saddr = "10.10.10.10" 47 | end 48 | 49 | it "should shallow copy with dup()" do 50 | p2 = @tcp_packet.dup 51 | p2.ip_saddr = "20.20.20.20" 52 | p2.ip_saddr.should == @tcp_packet.ip_saddr 53 | p2.headers[1].object_id.should == @tcp_packet.headers[1].object_id 54 | end 55 | 56 | it "should deep copy with clone()" do 57 | p3 = @tcp_packet.clone 58 | p3.ip_saddr = "30.30.30.30" 59 | p3.ip_saddr.should_not == @tcp_packet.ip_saddr 60 | p3.headers[1].object_id.should_not == @tcp_packet.headers[1].object_id 61 | end 62 | 63 | it "should have senisble equality" do 64 | p4 = @tcp_packet.dup 65 | p4.should == @tcp_packet 66 | p5 = @tcp_packet.clone 67 | p5.should == @tcp_packet 68 | end 69 | 70 | # It's actually kinda hard to manually create identical TCP packets 71 | it "should be possible to manually create identical packets" do 72 | p6 = @tcp_packet.clone 73 | p6.should == @tcp_packet 74 | p7 = PacketFu::TCPPacket.new 75 | p7.ip_saddr = p6.ip_saddr 76 | p7.ip_id = p6.ip_id 77 | p7.tcp_seq = p6.tcp_seq 78 | p7.tcp_src = p6.tcp_src 79 | p7.tcp_sum = p6.tcp_sum 80 | p7.should == p6 81 | end 82 | 83 | it "should parse IPv4 packets" do 84 | packets = PacketFu::PcapFile.read(File.join(File.dirname(__FILE__), 'ipv4_icmp.pcap')) 85 | packets.size.should == 1 86 | packet = PacketFu::Packet.parse(packets.first.data.to_s) 87 | packet.should be_a(PacketFu::ICMPPacket) 88 | packet.headers[1].should be_a(PacketFu::IPHeader) 89 | end 90 | 91 | it "should parse IPv6 packets" do 92 | packets = PacketFu::PcapFile.read(File.join(File.dirname(__FILE__), 'ipv6_udp.pcap')) 93 | packets.size.should == 1 94 | packet = PacketFu::Packet.parse(packets.first.data.to_s) 95 | packet.should be_a(PacketFu::UDPPacket) 96 | packet.headers[1].should be_a(PacketFu::IPv6Header) 97 | end 98 | 99 | end 100 | -------------------------------------------------------------------------------- /spec/packet_subclasses_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | PacketFu.packet_classes.each do |pclass| 4 | describe pclass, "peek format" do 5 | it "will display sensible peek information" do 6 | p = pclass.new 7 | p.respond_to?(:peek).should be true 8 | p.peek.size.should be <= 80, p.peek.inspect 9 | p.peek.should match(/^[A-Z0-9?]../) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/packetfu_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'packetfu/protos/eth' 3 | require 'packetfu/protos/ip' 4 | require 'packetfu/protos/tcp' 5 | require 'packetfu/version' 6 | require 'fake_packets' 7 | 8 | describe PacketFu, "version information" do 9 | it "reports a version number" do 10 | PacketFu::VERSION.should match /^[0123]\.[0-9]+\.[0-9]+(.pre)?$/ 11 | end 12 | its(:version) {should eq PacketFu::VERSION} 13 | 14 | it "can compare version strings" do 15 | PacketFu.binarize_version("1.2.3").should == 0x010203 16 | PacketFu.binarize_version("3.0").should == 0x030000 17 | PacketFu.at_least?("1.0").should be true 18 | PacketFu.at_least?("4.0").should be false 19 | PacketFu.older_than?("4.0").should be true 20 | PacketFu.newer_than?("1.0").should be true 21 | end 22 | 23 | it "can handle .pre versions" do 24 | PacketFu.binarize_version("1.7.6.pre").should == 0x010706 25 | PacketFu.at_least?("0.9.0.pre").should be true 26 | end 27 | end 28 | 29 | describe PacketFu, "instance variables" do 30 | it "should have a bunch of instance variables" do 31 | PacketFu.instance_variable_get(:@byte_order).should == :little 32 | PacketFu.instance_variable_get(:@pcaprub_loaded).should_not be_nil 33 | end 34 | end 35 | 36 | describe PacketFu, "pcaprub deps" do 37 | it "should check for pcaprub" do 38 | begin 39 | has_pcap = false 40 | require 'pcaprub' 41 | has_pcap = true 42 | rescue LoadError 43 | end 44 | if has_pcap 45 | PacketFu.instance_variable_get(:@pcaprub_loaded).should be true 46 | else 47 | PacketFu.instance_variable_get(:@pcaprub_loaded).should be false 48 | end 49 | end 50 | end 51 | 52 | describe PacketFu, "protocol requires" do 53 | it "should have some protocols defined" do 54 | PacketFu::EthPacket.should_not be_nil 55 | PacketFu::IPPacket.should_not be_nil 56 | PacketFu::TCPPacket.should_not be_nil 57 | expect { PacketFu::FakePacket }.to raise_error(NameError, /uninitialized constant PacketFu::FakePacket/) 58 | end 59 | end 60 | 61 | describe PacketFu, "packet class list management" do 62 | 63 | it "should allow packet class registration" do 64 | PacketFu.add_packet_class(PacketFu::FooPacket).should be_kind_of Array 65 | PacketFu.add_packet_class(PacketFu::BarPacket).should be_kind_of Array 66 | end 67 | 68 | its(:packet_classes) {should include(PacketFu::FooPacket)} 69 | 70 | it "should disallow non-classes as packet classes" do 71 | expect { PacketFu.add_packet_class("A String") }.to raise_error(RuntimeError, "Need a class") 72 | end 73 | 74 | its(:packet_prefixes) {should include("bar")} 75 | 76 | # Don't really have much utility for this right now. 77 | it "should allow packet class deregistration" do 78 | PacketFu.remove_packet_class(PacketFu::BarPacket) 79 | PacketFu.packet_prefixes.should_not include("bar") 80 | PacketFu.add_packet_class(PacketFu::BarPacket) 81 | end 82 | 83 | end 84 | -------------------------------------------------------------------------------- /spec/pcapng/epb_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'spec_helper' 3 | require 'packetfu' 4 | 5 | module PacketFu 6 | module PcapNG 7 | describe EPB do 8 | before(:each) { @epb = EPB.new } 9 | 10 | it 'should have correct initialization values' do 11 | expect(@epb).to be_a(EPB) 12 | expect(@epb.endian).to eq(:little) 13 | expect(@epb.type.to_i).to eq(PcapNG::EPB_TYPE.to_i) 14 | expect(@epb.interface_id.to_i).to eq(0) 15 | expect(@epb.tsh.to_i).to eq(0) 16 | expect(@epb.tsl.to_i).to eq(0) 17 | expect(@epb.cap_len.to_i).to eq(0) 18 | expect(@epb.orig_len.to_i).to eq(0) 19 | expect(@epb.block_len.to_i).to eq(EPB::MIN_SIZE) 20 | expect(@epb.block_len2).to eq(@epb.block_len) 21 | end 22 | 23 | context 'when reading' do 24 | it 'should accept a String' do 25 | str = ::File.read(::File.join(__dir__, '../..', 'test', 'sample.pcapng'))[84, 112] 26 | expect { @epb.read(str) }.to_not raise_error 27 | expect(@epb.type.to_i).to eq(PcapNG::EPB_TYPE.to_i) 28 | expect(@epb.block_len.to_i).to eq(112) 29 | expect(@epb.interface_id.to_i).to eq(0) 30 | expect(@epb.tsh.to_i).to eq(0x475ad) 31 | expect(@epb.tsl.to_i).to eq(0xd392be6a) 32 | expect(@epb.cap_len.to_i).to eq(78) 33 | expect(@epb.orig_len.to_i).to eq(@epb.cap_len.to_i) 34 | expect(@epb.has_options?).to be(false) 35 | end 36 | 37 | it 'should accept an IO' do 38 | ::File.open(::File.join(__dir__, '../..', 'test', 'sample.pcapng')) do |f| 39 | f.seek(84, :CUR) 40 | @epb.read f 41 | end 42 | expect(@epb.type.to_i).to eq(PcapNG::EPB_TYPE.to_i) 43 | expect(@epb.block_len.to_i).to eq(112) 44 | expect(@epb.interface_id.to_i).to eq(0) 45 | expect(@epb.tsh.to_i).to eq(0x475ad) 46 | expect(@epb.tsl.to_i).to eq(0xd392be6a) 47 | expect(@epb.cap_len.to_i).to eq(78) 48 | expect(@epb.orig_len.to_i).to eq(@epb.cap_len.to_i) 49 | expect(@epb.has_options?).to be(false) 50 | end 51 | 52 | end 53 | 54 | it 'should decode packet timestamp with default resolution' do 55 | ::File.open(::File.join(__dir__, '../..', 'test', 'sample.pcapng')) do |f| 56 | f.seek(84, :CUR) 57 | @epb.read f 58 | end 59 | 60 | expect(@epb.timestamp.round).to eq(Time.utc(2009, 10, 11, 19, 29, 6)) 61 | end 62 | 63 | it 'should decode packet timestamp with interface resolution' do 64 | ::File.open(::File.join(__dir__, '../..', 'test', 'sample.pcapng')) do |f| 65 | f.seek(84, :CUR) 66 | @epb.read f 67 | end 68 | 69 | idb = IDB.new 70 | ::File.open(::File.join(__dir__, '../..', 'test', 'sample.pcapng')) do |f| 71 | f.seek(52, :CUR) 72 | idb.read f 73 | end 74 | idb << @epb 75 | @epb.interface = idb 76 | 77 | expect(@epb.timestamp.round).to eq(Time.utc(2009, 10, 11, 19, 29, 6)) 78 | end 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /spec/pcapng/file_spec_helper.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | 3 | module PacketFu 4 | module PcapNG 5 | 6 | # Hash containing attended structure for each test file. 7 | # Hash's values are arrays. Each element of these arrays are a section in 8 | # pcapng file. A section is described as a hash which keys are block types 9 | # and values number of each type in a section. 10 | PCAPNG_TEST_FILES = { 11 | "basic/test001.pcapng"=>[{:idb=>1, :epb=>4, :spb=>0, :unknown=>0}], 12 | "basic/test002.pcapng"=>[{:idb=>0, :epb=>0, :spb=>0, :unknown=>0}], 13 | "basic/test003.pcapng"=>[{:idb=>1, :epb=>0, :spb=>0, :unknown=>0}], 14 | "basic/test004.pcapng"=>[{:idb=>2, :epb=>4, :spb=>0, :unknown=>0}], 15 | "basic/test005.pcapng"=>[{:idb=>2, :epb=>4, :spb=>0, :unknown=>0}], 16 | "basic/test006.pcapng"=>[{:idb=>2, :epb=>5, :spb=>0, :unknown=>0}], 17 | "basic/test007.pcapng"=>[{:idb=>1, :epb=>1, :spb=>0, :unknown=>0}], 18 | "basic/test008.pcapng"=>[{:idb=>2, :epb=>4, :spb=>0, :unknown=>0}], 19 | "basic/test009.pcapng"=>[{:idb=>1, :epb=>2, :spb=>0, :unknown=>0}], 20 | "basic/test010.pcapng"=>[{:idb=>1, :epb=>0, :spb=>4, :unknown=>0}], 21 | "basic/test011.pcapng"=>[{:idb=>1, :epb=>2, :spb=>2, :unknown=>0}], 22 | "basic/test012.pcapng"=>[{:idb=>1, :epb=>2, :spb=>2, :unknown=>0}], 23 | "basic/test013.pcapng"=>[{:idb=>1, :epb=>0, :spb=>0, :unknown=>1}], 24 | "basic/test014.pcapng"=>[{:idb=>3, :epb=>0, :spb=>0, :unknown=>3}], 25 | "basic/test015.pcapng"=>[{:idb=>1, :epb=>0, :spb=>0, :unknown=>1}], 26 | "basic/test016.pcapng"=>[{:idb=>1, :epb=>2, :spb=>2, :unknown=>3}], 27 | "basic/test017.pcapng"=>[{:idb=>0, :epb=>0, :spb=>0, :unknown=>4}], 28 | "basic/test018.pcapng"=>[{:idb=>1, :epb=>2, :spb=>2, :unknown=>4}], 29 | "advanced/test100.pcapng"=>[{:idb=>3, :epb=>3, :spb=>2, :unknown=>5}], 30 | "advanced/test101.pcapng"=>[{:idb=>3, :epb=>3, :spb=>1, :unknown=>6}], 31 | "advanced/test102.pcapng"=>[{:idb=>3, :epb=>4, :spb=>1, :unknown=>12}], 32 | "difficult/test200.pcapng"=>[{:idb=>1, :epb=>0, :spb=>0, :unknown=>0}, 33 | {:idb=>1, :epb=>0, :spb=>0, :unknown=>0}, 34 | {:idb=>1, :epb=>0, :spb=>0, :unknown=>0}], 35 | "difficult/test201.pcapng"=>[{:idb=>2, :epb=>1, :spb=>0, :unknown=>1}, 36 | {:idb=>1, :epb=>1, :spb=>1, :unknown=>1}, 37 | {:idb=>2, :epb=>1, :spb=>0, :unknown=>2}], 38 | "difficult/test202.pcapng"=>[{:idb=>2, :epb=>3, :spb=>0, :unknown=>4}, 39 | {:idb=>1, :epb=>2, :spb=>2, :unknown=>4}, 40 | {:idb=>2, :epb=>1, :spb=>0, :unknown=>4}] 41 | } 42 | 43 | end 44 | end 45 | 46 | -------------------------------------------------------------------------------- /spec/pcapng/idb_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'spec_helper' 3 | require 'packetfu' 4 | 5 | module PacketFu 6 | module PcapNG 7 | describe IDB do 8 | before(:each) { @idb = IDB.new } 9 | 10 | it 'should have correct initialization values' do 11 | expect(@idb).to be_a(IDB) 12 | expect(@idb.endian).to eq(:little) 13 | expect(@idb.type.to_i).to eq(PcapNG::IDB_TYPE.to_i) 14 | expect(@idb.link_type.to_i).to eq(PcapNG::LINKTYPE_ETHERNET) 15 | expect(@idb.snaplen.to_i).to eq(0) 16 | expect(@idb.block_len.to_i).to eq(IDB::MIN_SIZE) 17 | expect(@idb.block_len2).to eq(@idb.block_len) 18 | end 19 | 20 | it 'should decode tsresol on demand from its options' do 21 | @idb.options.read [9, 1, 4].pack('vvC') 22 | expect(@idb.ts_resol).to eq(1E-4) 23 | 24 | @idb.options.read [9, 1, 0x83].pack('vvC') 25 | expect(@idb.ts_resol(true)).to eq(2**-3) 26 | end 27 | 28 | context 'when reading' do 29 | it 'should accept a String' do 30 | str = ::File.read(::File.join(__dir__, '../..', 'test', 'sample.pcapng'))[52, 32] 31 | expect { @idb.read(str) }.to_not raise_error 32 | expect(@idb.type.to_i).to eq(PcapNG::IDB_TYPE.to_i) 33 | expect(@idb.block_len.to_i).to eq(32) 34 | expect(@idb.link_type.to_i).to eq(PcapNG::LINKTYPE_ETHERNET) 35 | expect(@idb.snaplen.to_i).to eq(0xffff) 36 | expect(@idb.has_options?).to be(true) 37 | end 38 | 39 | it 'should accept an IO' do 40 | ::File.open(::File.join(__dir__, '../..', 'test', 'sample.pcapng')) do |f| 41 | f.seek(52, :CUR) 42 | @idb.read f 43 | end 44 | expect(@idb.type.to_i).to eq(PcapNG::IDB_TYPE.to_i) 45 | expect(@idb.block_len.to_i).to eq(32) 46 | expect(@idb.link_type.to_i).to eq(PcapNG::LINKTYPE_ETHERNET) 47 | expect(@idb.snaplen.to_i).to eq(0xffff) 48 | expect(@idb.has_options?).to be(true) 49 | end 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/pcapng/shb_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'spec_helper' 3 | require 'packetfu' 4 | 5 | module PacketFu 6 | module PcapNG 7 | describe SHB do 8 | before(:each) { @shb = SHB.new } 9 | 10 | it 'should have correct initialization values' do 11 | expect(@shb).to be_a(SHB) 12 | expect(@shb.endian).to eq(:little) 13 | expect(@shb.type.to_i).to eq(PcapNG::SHB_TYPE.to_i) 14 | expect(@shb.block_len.to_i).to eq(SHB::MIN_SIZE) 15 | expect(@shb.magic.to_s).to eq(SHB::MAGIC_LITTLE) 16 | expect(@shb.ver_major.to_i).to eq(1) 17 | expect(@shb.ver_minor.to_i).to eq(0) 18 | expect(@shb.section_len.to_i).to eq(0xffffffff_ffffffff) 19 | expect(@shb.block_len2).to eq(@shb.block_len) 20 | expect(@shb.interfaces).to eq([]) 21 | expect(@shb.unknown_blocks).to eq([]) 22 | end 23 | 24 | context 'when reading' do 25 | it 'should accept a String' do 26 | str = ::File.read(::File.join(__dir__, '../..', 'test', 'sample.pcapng'), 52) 27 | expect { @shb.read(str) }.to_not raise_error 28 | expect(@shb.block_len.to_i).to eq(52) 29 | expect(@shb.has_options?).to be(true) 30 | end 31 | 32 | it 'should accept an IO' do 33 | ::File.open(::File.join(__dir__, '../..', 'test', 'sample.pcapng')) do |f| 34 | @shb.read(f) 35 | end 36 | expect(@shb.block_len.to_i).to eq(52) 37 | expect(@shb.has_options?).to be(true) 38 | end 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/pcapng/spb_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'spec_helper' 3 | require 'packetfu' 4 | 5 | module PacketFu 6 | module PcapNG 7 | describe SPB do 8 | before(:each) { @spb = SPB.new } 9 | 10 | it 'should have correct initialization values' do 11 | expect(@spb).to be_a(SPB) 12 | expect(@spb.endian).to eq(:little) 13 | expect(@spb.type.to_i).to eq(PcapNG::SPB_TYPE.to_i) 14 | expect(@spb.orig_len.to_i).to eq(0) 15 | expect(@spb.block_len.to_i).to eq(SPB::MIN_SIZE) 16 | expect(@spb.block_len2).to eq(@spb.block_len) 17 | end 18 | 19 | context 'when reading' do 20 | it 'should accept a String' do 21 | str = ::File.read(::File.join(__dir__, '../..', 'test', 22 | 'sample-spb.pcapng'))[128, 0x14c] 23 | expect { @spb.read str }.to_not raise_error 24 | expect(@spb.type.to_i).to eq(PcapNG::SPB_TYPE.to_i) 25 | expect(@spb.block_len.to_i).to eq(0x14c) 26 | expect(@spb.orig_len.to_i).to eq(0x13a) 27 | expect(@spb.data.size).to eq(0x13a) 28 | end 29 | 30 | it 'should accept an IO' do 31 | ::File.open(::File.join(__dir__, '../..', 'test', 'sample-spb.pcapng')) do |f| 32 | f.seek(128, :CUR) 33 | @spb.read f 34 | end 35 | expect(@spb.type.to_i).to eq(PcapNG::SPB_TYPE.to_i) 36 | expect(@spb.block_len.to_i).to eq(0x14c) 37 | expect(@spb.orig_len.to_i).to eq(0x13a) 38 | expect(@spb.data.size).to eq(0x13a) 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/pcapng/unknown_block_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: binary -*- 2 | require 'spec_helper' 3 | require 'packetfu' 4 | 5 | module PacketFu 6 | module PcapNG 7 | describe UnknownBlock do 8 | before(:each) { @ub = UnknownBlock.new } 9 | 10 | it 'should have correct initialization values' do 11 | expect(@ub).to be_a(UnknownBlock) 12 | expect(@ub.endian).to eq(:little) 13 | expect(@ub.type.to_i).to eq(0) 14 | expect(@ub.block_len.to_i).to eq(UnknownBlock::MIN_SIZE) 15 | expect(@ub.block_len2).to eq(@ub.block_len) 16 | end 17 | 18 | context 'when reading' do 19 | it 'should accept a String' do 20 | str = "\xff\xff\xff\xff\x0c\x00\x00\x00\x0c\x00\x00\x00" 21 | expect { @ub.read(str) }.to_not raise_error 22 | expect(@ub.type.to_i).to eq(0xffffffff) 23 | expect(@ub.block_len.to_i).to eq(12) 24 | end 25 | 26 | it 'should accept an IO' do 27 | ::File.open(::File.join(__dir__, '../..', 'test', 'sample.pcapng')) do |f| 28 | @ub.read(f) 29 | end 30 | expect(@ub.type.to_i).to eq(0x0a0d0d0a) 31 | expect(@ub.block_len.to_i).to eq(52) 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/sample.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/spec/sample.pcap -------------------------------------------------------------------------------- /spec/sample2.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/spec/sample2.pcap -------------------------------------------------------------------------------- /spec/sample3.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/spec/sample3.pcap -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | unless ENV['SKIP_COVERALLS'] 2 | require 'coveralls' 3 | Coveralls.wear! 4 | end 5 | 6 | puts "rspec #{RSpec::Core::Version::STRING}" 7 | if RSpec::Core::Version::STRING[0] == '3' 8 | require 'rspec/its' 9 | RSpec.configure do |config| 10 | #config.raise_errors_for_deprecations! 11 | config.expect_with :rspec do |c| 12 | c.syntax = [:expect, :should] 13 | end 14 | end 15 | end 16 | 17 | require 'packetfu/common' 18 | 19 | -------------------------------------------------------------------------------- /spec/vlan-pcapr.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/spec/vlan-pcapr.cap -------------------------------------------------------------------------------- /test/all_tests.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Tested on: 4 | # 5 | # ruby-1.9.3-head [ x86_64 ] 6 | # ruby-1.9.3-p484 [ x86_64 ] 7 | 8 | # Okay so the regular test/unit stuff screws up some of my 9 | # meta magic. I need to move these over to spec and see 10 | # if they're any better. In the meantime, behold my 11 | # ghetto test exec()'er. It all passes with this, 12 | # so I'm just going to go ahead and assume the testing 13 | # methodolgy is flawed. TODO: rewrite all this for spec 14 | # and incidentally get the gem to test like it's supposed 15 | # to. 16 | 17 | $:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib/") 18 | require 'packetfu' 19 | puts "Testing PacketFu v#{PacketFu::VERSION}" 20 | dir = Dir.new(File.dirname(__FILE__)) 21 | 22 | dir.each { |file| 23 | next unless File.file? file 24 | next unless file[/^test_.*rb$/] 25 | next if file == $0 26 | puts "Running #{file}..." 27 | cmd = %x{ruby #{file}} 28 | if cmd[/ 0 failures/] && cmd[/ 0 errors/] 29 | puts "#{file}: All passed" 30 | else 31 | puts "File: #{file} had failures or errors:" 32 | puts "-" * 80 33 | puts cmd 34 | puts "-" * 80 35 | end 36 | } 37 | 38 | # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby 39 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/advanced/test100.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/advanced/test100.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/advanced/test100.txt: -------------------------------------------------------------------------------- 1 | Description: NRB with IPv4+6, unknown types, duplicate entries, etc. 2 | Category: advanced 3 | 4 | Block counts: 5 | EPB: 3 6 | IDB: 3 7 | NRB: 5 8 | SHB: 1 9 | SPB: 2 10 | 11 | Block sequence: SHB, IDB, NRB, SPB, NRB, IDB, NRB, EPB, IDB, NRB, SPB, EPB, EPB, NRB 12 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/advanced/test101.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/advanced/test101.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/advanced/test101.txt: -------------------------------------------------------------------------------- 1 | Description: Duplicate ISBs with various options, intermixed in EPB/SPB 2 | Category: advanced 3 | 4 | Block counts: 5 | EPB: 3 6 | IDB: 3 7 | ISB: 6 8 | SHB: 1 9 | SPB: 1 10 | 11 | Block sequence: SHB, IDB, IDB, EPB, ISB, ISB, ISB, IDB, EPB, ISB, SPB, ISB, EPB, ISB 12 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/advanced/test102.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/advanced/test102.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/advanced/test102.txt: -------------------------------------------------------------------------------- 1 | Description: All block types, intermixed 2 | Category: advanced 3 | 4 | Block counts: 5 | CB: 1 6 | DCB: 2 7 | EPB: 4 8 | IDB: 3 9 | ISB: 6 10 | NRB: 3 11 | SHB: 1 12 | SPB: 1 13 | 14 | Block sequence: SHB, CB, NRB, IDB, IDB, ISB, EPB, ISB, ISB, DCB, IDB, EPB, ISB, SPB, NRB, EPB, ISB, EPB, DCB, NRB, ISB 15 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test001.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test001.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test001.txt: -------------------------------------------------------------------------------- 1 | Description: Basic normal pcapng file 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 4 6 | IDB: 1 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, EPB, EPB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test002.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test002.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test002.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB 2 | Category: basic 3 | 4 | Block counts: 5 | SHB: 1 6 | 7 | Block sequence: SHB 8 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test003.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test003.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test003.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB and IDB 2 | Category: basic 3 | 4 | Block counts: 5 | IDB: 1 6 | SHB: 1 7 | 8 | Block sequence: SHB, IDB 9 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test004.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test004.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test004.txt: -------------------------------------------------------------------------------- 1 | Description: Two IDBs same linktype, different snaplen 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 4 6 | IDB: 2 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, IDB, EPB, EPB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test005.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test005.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test005.txt: -------------------------------------------------------------------------------- 1 | Description: 2 IDBs separated by EPB 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 4 6 | IDB: 2 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, EPB, IDB, EPB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test006.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test006.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test006.txt: -------------------------------------------------------------------------------- 1 | Description: Two IDBs different linktype 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 5 6 | IDB: 2 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, IDB, EPB, EPB, EPB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test007.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test007.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test007.txt: -------------------------------------------------------------------------------- 1 | Description: SHB with all options 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 1 6 | IDB: 1 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test008.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test008.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test008.txt: -------------------------------------------------------------------------------- 1 | Description: 2 IDBs with all options 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 4 6 | IDB: 2 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, EPB, IDB, EPB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test009.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test009.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test009.txt: -------------------------------------------------------------------------------- 1 | Description: EPBs with all options 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 2 6 | IDB: 1 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test010.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test010.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test010.txt: -------------------------------------------------------------------------------- 1 | Description: SPBs 2 | Category: basic 3 | 4 | Block counts: 5 | IDB: 1 6 | SHB: 1 7 | SPB: 4 8 | 9 | Block sequence: SHB, IDB, SPB, SPB, SPB, SPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test011.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test011.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test011.txt: -------------------------------------------------------------------------------- 1 | Description: Mix of SPBs and EPBs 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 2 6 | IDB: 1 7 | SHB: 1 8 | SPB: 2 9 | 10 | Block sequence: SHB, IDB, SPB, EPB, SPB, EPB 11 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test012.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test012.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test012.txt: -------------------------------------------------------------------------------- 1 | Description: SPBs and EPBs with IDB snaplen bigger than some, less than others 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 2 6 | IDB: 1 7 | SHB: 1 8 | SPB: 2 9 | 10 | Block sequence: SHB, IDB, SPB, SPB, EPB, EPB 11 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test013.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test013.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test013.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB, IDB, and ISB 2 | Category: basic 3 | 4 | Block counts: 5 | IDB: 1 6 | ISB: 1 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, ISB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test014.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test014.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test014.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB, multiple IDB and ISB 2 | Category: basic 3 | 4 | Block counts: 5 | IDB: 3 6 | ISB: 3 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, IDB, ISB, ISB, IDB, ISB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test015.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test015.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test015.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB, IDB, and NRB 2 | Category: basic 3 | 4 | Block counts: 5 | IDB: 1 6 | NRB: 1 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, NRB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test016.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test016.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test016.txt: -------------------------------------------------------------------------------- 1 | Description: Multiple NRB among SPB/EPB 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 2 6 | IDB: 1 7 | NRB: 3 8 | SHB: 1 9 | SPB: 2 10 | 11 | Block sequence: SHB, IDB, NRB, SPB, EPB, NRB, SPB, EPB, NRB 12 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test017.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test017.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test017.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB and CB/DCB 2 | Category: basic 3 | 4 | Block counts: 5 | CB: 2 6 | DCB: 2 7 | SHB: 1 8 | 9 | Block sequence: SHB, CB, DCB, CB, DCB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test018.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/basic/test018.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/basic/test018.txt: -------------------------------------------------------------------------------- 1 | Description: Multiple CB/DCB among SPB/EPB 2 | Category: basic 3 | 4 | Block counts: 5 | CB: 2 6 | DCB: 2 7 | EPB: 2 8 | IDB: 1 9 | SHB: 1 10 | SPB: 2 11 | 12 | Block sequence: SHB, IDB, CB, SPB, EPB, DCB, SPB, CB, EPB, DCB 13 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/difficult/test200.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/difficult/test200.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/difficult/test200.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB and IDB, but repeated so multiple SHB 2 | Category: difficult 3 | 4 | Block counts: 5 | IDB: 3 6 | SHB: 3 7 | 8 | Block sequence: SHB, IDB, SHB, IDB, SHB, IDB 9 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/difficult/test201.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/difficult/test201.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/difficult/test201.txt: -------------------------------------------------------------------------------- 1 | Description: ISBs with various options, in different SHB sections 2 | Category: difficult 3 | 4 | Block counts: 5 | EPB: 3 6 | IDB: 5 7 | ISB: 4 8 | SHB: 3 9 | SPB: 1 10 | 11 | Block sequence: SHB, IDB, IDB, EPB, ISB, SHB, IDB, EPB, ISB, SPB, SHB, IDB, IDB, ISB, EPB, ISB 12 | -------------------------------------------------------------------------------- /test/pcapng-test/output_be/difficult/test202.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_be/difficult/test202.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_be/difficult/test202.txt: -------------------------------------------------------------------------------- 1 | Description: Multiple SHB sections of different endianness, all other blocks 2 | Category: difficult 3 | 4 | Block counts: 5 | CB: 1 6 | DCB: 2 7 | EPB: 6 8 | IDB: 5 9 | ISB: 4 10 | NRB: 5 11 | SHB: 3 12 | SPB: 2 13 | 14 | Block sequence: SHB, IDB, NRB, IDB, CB, NRB, EPB, ISB, EPB, EPB, SHB, IDB, SPB, EPB, DCB, NRB, NRB, ISB, SPB, EPB, SHB, NRB, IDB, DCB, IDB, ISB, ISB, EPB 15 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/advanced/test100.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/advanced/test100.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/advanced/test100.txt: -------------------------------------------------------------------------------- 1 | Description: NRB with IPv4+6, unknown types, duplicate entries, etc. 2 | Category: advanced 3 | 4 | Block counts: 5 | EPB: 3 6 | IDB: 3 7 | NRB: 5 8 | SHB: 1 9 | SPB: 2 10 | 11 | Block sequence: SHB, IDB, NRB, SPB, NRB, IDB, NRB, EPB, IDB, NRB, SPB, EPB, EPB, NRB 12 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/advanced/test101.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/advanced/test101.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/advanced/test101.txt: -------------------------------------------------------------------------------- 1 | Description: Duplicate ISBs with various options, intermixed in EPB/SPB 2 | Category: advanced 3 | 4 | Block counts: 5 | EPB: 3 6 | IDB: 3 7 | ISB: 6 8 | SHB: 1 9 | SPB: 1 10 | 11 | Block sequence: SHB, IDB, IDB, EPB, ISB, ISB, ISB, IDB, EPB, ISB, SPB, ISB, EPB, ISB 12 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/advanced/test102.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/advanced/test102.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/advanced/test102.txt: -------------------------------------------------------------------------------- 1 | Description: All block types, intermixed 2 | Category: advanced 3 | 4 | Block counts: 5 | CB: 1 6 | DCB: 2 7 | EPB: 4 8 | IDB: 3 9 | ISB: 6 10 | NRB: 3 11 | SHB: 1 12 | SPB: 1 13 | 14 | Block sequence: SHB, CB, NRB, IDB, IDB, ISB, EPB, ISB, ISB, DCB, IDB, EPB, ISB, SPB, NRB, EPB, ISB, EPB, DCB, NRB, ISB 15 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test001.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test001.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test001.txt: -------------------------------------------------------------------------------- 1 | Description: Basic normal pcapng file 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 4 6 | IDB: 1 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, EPB, EPB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test002.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test002.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test002.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB 2 | Category: basic 3 | 4 | Block counts: 5 | SHB: 1 6 | 7 | Block sequence: SHB 8 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test003.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test003.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test003.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB and IDB 2 | Category: basic 3 | 4 | Block counts: 5 | IDB: 1 6 | SHB: 1 7 | 8 | Block sequence: SHB, IDB 9 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test004.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test004.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test004.txt: -------------------------------------------------------------------------------- 1 | Description: Two IDBs same linktype, different snaplen 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 4 6 | IDB: 2 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, IDB, EPB, EPB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test005.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test005.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test005.txt: -------------------------------------------------------------------------------- 1 | Description: 2 IDBs separated by EPB 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 4 6 | IDB: 2 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, EPB, IDB, EPB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test006.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test006.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test006.txt: -------------------------------------------------------------------------------- 1 | Description: Two IDBs different linktype 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 5 6 | IDB: 2 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, IDB, EPB, EPB, EPB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test007.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test007.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test007.txt: -------------------------------------------------------------------------------- 1 | Description: SHB with all options 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 1 6 | IDB: 1 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test008.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test008.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test008.txt: -------------------------------------------------------------------------------- 1 | Description: 2 IDBs with all options 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 4 6 | IDB: 2 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, EPB, IDB, EPB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test009.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test009.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test009.txt: -------------------------------------------------------------------------------- 1 | Description: EPBs with all options 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 2 6 | IDB: 1 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, EPB, EPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test010.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test010.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test010.txt: -------------------------------------------------------------------------------- 1 | Description: SPBs 2 | Category: basic 3 | 4 | Block counts: 5 | IDB: 1 6 | SHB: 1 7 | SPB: 4 8 | 9 | Block sequence: SHB, IDB, SPB, SPB, SPB, SPB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test011.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test011.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test011.txt: -------------------------------------------------------------------------------- 1 | Description: Mix of SPBs and EPBs 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 2 6 | IDB: 1 7 | SHB: 1 8 | SPB: 2 9 | 10 | Block sequence: SHB, IDB, SPB, EPB, SPB, EPB 11 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test012.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test012.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test012.txt: -------------------------------------------------------------------------------- 1 | Description: SPBs and EPBs with IDB snaplen bigger than some, less than others 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 2 6 | IDB: 1 7 | SHB: 1 8 | SPB: 2 9 | 10 | Block sequence: SHB, IDB, SPB, SPB, EPB, EPB 11 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test013.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test013.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test013.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB, IDB, and ISB 2 | Category: basic 3 | 4 | Block counts: 5 | IDB: 1 6 | ISB: 1 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, ISB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test014.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test014.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test014.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB, multiple IDB and ISB 2 | Category: basic 3 | 4 | Block counts: 5 | IDB: 3 6 | ISB: 3 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, IDB, ISB, ISB, IDB, ISB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test015.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test015.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test015.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB, IDB, and NRB 2 | Category: basic 3 | 4 | Block counts: 5 | IDB: 1 6 | NRB: 1 7 | SHB: 1 8 | 9 | Block sequence: SHB, IDB, NRB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test016.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test016.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test016.txt: -------------------------------------------------------------------------------- 1 | Description: Multiple NRB among SPB/EPB 2 | Category: basic 3 | 4 | Block counts: 5 | EPB: 2 6 | IDB: 1 7 | NRB: 3 8 | SHB: 1 9 | SPB: 2 10 | 11 | Block sequence: SHB, IDB, NRB, SPB, EPB, NRB, SPB, EPB, NRB 12 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test017.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test017.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test017.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB and CB/DCB 2 | Category: basic 3 | 4 | Block counts: 5 | CB: 2 6 | DCB: 2 7 | SHB: 1 8 | 9 | Block sequence: SHB, CB, DCB, CB, DCB 10 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test018.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/basic/test018.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/basic/test018.txt: -------------------------------------------------------------------------------- 1 | Description: Multiple CB/DCB among SPB/EPB 2 | Category: basic 3 | 4 | Block counts: 5 | CB: 2 6 | DCB: 2 7 | EPB: 2 8 | IDB: 1 9 | SHB: 1 10 | SPB: 2 11 | 12 | Block sequence: SHB, IDB, CB, SPB, EPB, DCB, SPB, CB, EPB, DCB 13 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/difficult/test200.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/difficult/test200.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/difficult/test200.txt: -------------------------------------------------------------------------------- 1 | Description: Empty - only SHB and IDB, but repeated so multiple SHB 2 | Category: difficult 3 | 4 | Block counts: 5 | IDB: 3 6 | SHB: 3 7 | 8 | Block sequence: SHB, IDB, SHB, IDB, SHB, IDB 9 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/difficult/test201.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/difficult/test201.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/difficult/test201.txt: -------------------------------------------------------------------------------- 1 | Description: ISBs with various options, in different SHB sections 2 | Category: difficult 3 | 4 | Block counts: 5 | EPB: 3 6 | IDB: 5 7 | ISB: 4 8 | SHB: 3 9 | SPB: 1 10 | 11 | Block sequence: SHB, IDB, IDB, EPB, ISB, SHB, IDB, EPB, ISB, SPB, SHB, IDB, IDB, ISB, EPB, ISB 12 | -------------------------------------------------------------------------------- /test/pcapng-test/output_le/difficult/test202.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/pcapng-test/output_le/difficult/test202.pcapng -------------------------------------------------------------------------------- /test/pcapng-test/output_le/difficult/test202.txt: -------------------------------------------------------------------------------- 1 | Description: Multiple SHB sections of different endianness, all other blocks 2 | Category: difficult 3 | 4 | Block counts: 5 | CB: 1 6 | DCB: 2 7 | EPB: 6 8 | IDB: 5 9 | ISB: 4 10 | NRB: 5 11 | SHB: 3 12 | SPB: 2 13 | 14 | Block sequence: SHB, IDB, NRB, IDB, CB, NRB, EPB, ISB, EPB, EPB, SHB, IDB, SPB, EPB, DCB, NRB, NRB, ISB, SPB, EPB, SHB, NRB, IDB, DCB, IDB, ISB, ISB, EPB 15 | -------------------------------------------------------------------------------- /test/sample-ipv6.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/sample-ipv6.pcap -------------------------------------------------------------------------------- /test/sample-ipv6.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/sample-ipv6.pcapng -------------------------------------------------------------------------------- /test/sample-spb.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/sample-spb.pcapng -------------------------------------------------------------------------------- /test/sample.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/sample.pcap -------------------------------------------------------------------------------- /test/sample.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/sample.pcapng -------------------------------------------------------------------------------- /test/sample2.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/sample2.pcap -------------------------------------------------------------------------------- /test/sample2.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/sample2.pcapng -------------------------------------------------------------------------------- /test/sample_hsrp_pcapr.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/sample_hsrp_pcapr.cap -------------------------------------------------------------------------------- /test/sample_lldp.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/sample_lldp.pcap -------------------------------------------------------------------------------- /test/vlan-pcapr.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todb/packetfu/0c1f524fd1aa0715df0caa014b45be93812b4bf1/test/vlan-pcapr.cap --------------------------------------------------------------------------------