packetgen 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/README.md +15 -0
- data/bin/pgconsole +41 -0
- data/lib/packetgen/capture.rb +61 -37
- data/lib/packetgen/config.rb +39 -0
- data/lib/packetgen/packet.rb +7 -6
- data/lib/packetgen/version.rb +1 -1
- data/lib/packetgen.rb +2 -2
- data/packetgen.gemspec +4 -2
- metadata +35 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba692327da7837d4de41a002ca756a778692c74b
|
4
|
+
data.tar.gz: 38fca26934167acbd7d92981f160071c7fd1387f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80d8155ad93876fcb14c5d1d85b3a374a1eb865784b914aed802dbb0ab07e97364d10e5affc2a094f4041f695774a41dd60a65e8da2fbdd92e27ca873ad60ddd
|
7
|
+
data.tar.gz: ac325e1dae38151f1f73ca05b6604a7373060aaee4a814e9d87c98b4de4e8e8aa64ba11f39930693d9cc8f9e4abb028918237d37e99f5a1becabdf93227effe9
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -131,6 +131,21 @@ pkt = Packet.gen('IP').add('MyHeader', field1: 0x12345678)
|
|
131
131
|
pkt.myheader.field2.read 0x01
|
132
132
|
```
|
133
133
|
|
134
|
+
## Interactive console
|
135
|
+
PacketGen provides an interactive console, based on `pry`: `pgconsole`.
|
136
|
+
|
137
|
+
In this console, context includes PacketGen module to give direct access to PacketGen
|
138
|
+
classes. A special `config` object gives local network configuration:
|
139
|
+
|
140
|
+
$ pgconsole
|
141
|
+
pg(main)> config
|
142
|
+
=> #<PacketGen::Config:0x00559f27d2afe8
|
143
|
+
@hwaddr="75:74:73:72:71:70",
|
144
|
+
@iface="eth0",
|
145
|
+
@ipaddr="192.168.0.2">
|
146
|
+
pg(main)> packets = capture(max: 5)
|
147
|
+
pg(main)> exit
|
148
|
+
|
134
149
|
## Pull requests?
|
135
150
|
|
136
151
|
yes
|
data/bin/pgconsole
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'packetgen'
|
5
|
+
require 'packetgen/config'
|
6
|
+
require 'pry'
|
7
|
+
|
8
|
+
include PacketGen
|
9
|
+
|
10
|
+
def parse(binary_str, first_header: nil)
|
11
|
+
Packet.parse binary_str, first_header: first_header
|
12
|
+
end
|
13
|
+
|
14
|
+
def capture(options={})
|
15
|
+
Packet.capture(options) { |packet| yield packet if block_given? }
|
16
|
+
end
|
17
|
+
|
18
|
+
def read(filename)
|
19
|
+
Packet.read filename
|
20
|
+
end
|
21
|
+
|
22
|
+
def write(filename, packets)
|
23
|
+
Packet.write filename, packets
|
24
|
+
end
|
25
|
+
|
26
|
+
@config = Config.new
|
27
|
+
|
28
|
+
def config
|
29
|
+
@config
|
30
|
+
end
|
31
|
+
|
32
|
+
Pry.config.prompt = [
|
33
|
+
proc { |target_self, nest_level, pry|
|
34
|
+
"#{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}> "
|
35
|
+
},
|
36
|
+
proc { |target_self, nest_level, pry|
|
37
|
+
"#{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}* "
|
38
|
+
}
|
39
|
+
]
|
40
|
+
Pry.config.prompt_name = 'pg'
|
41
|
+
Pry.start
|
data/lib/packetgen/capture.rb
CHANGED
@@ -7,46 +7,83 @@ module PacketGen
|
|
7
7
|
|
8
8
|
# Capture packets from wire
|
9
9
|
# @author Sylvain Daubert
|
10
|
+
# @author Kent 'picat' Gruber
|
10
11
|
class Capture
|
11
12
|
|
12
|
-
# Default snaplen to use if :snaplen option not defined
|
13
|
+
# Default snaplen to use if :snaplen option not defined.
|
13
14
|
DEFAULT_SNAPLEN = 0xffff
|
14
15
|
|
15
|
-
# Get captured packets
|
16
|
+
# Get captured packets.
|
16
17
|
# @return [Array<Packets>]
|
17
18
|
attr_reader :packets
|
18
19
|
|
19
|
-
# Get captured packet raw data
|
20
|
+
# Get captured packet raw data.
|
20
21
|
# @return [Array<String>]
|
21
22
|
attr_reader :raw_packets
|
22
23
|
|
23
|
-
#
|
24
|
-
# @
|
25
|
-
|
26
|
-
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
24
|
+
# Get interface name
|
25
|
+
# @return [String]
|
26
|
+
attr_reader :iface
|
27
|
+
|
28
|
+
# @overload initialize(iface=Pcap.lookupdev, options={})
|
29
|
+
# @param [String] iface interface on which capture packets
|
30
|
+
# @param [Hash] options
|
31
|
+
# @option options [Integer] :max maximum number of packets to capture.
|
32
|
+
# @option options [Integer] :timeout maximum number of seconds before end
|
33
|
+
# of capture. Default: +nil+ (no timeout)
|
34
|
+
# @option options [String] :filter bpf filter
|
35
|
+
# @option options [Boolean] :promiscuous (default: +false+)
|
36
|
+
# @option options [Boolean] :parse parse raw data to generate packets before
|
37
|
+
# yielding. Default: +true+
|
38
|
+
# @option options [Integer] :snaplen maximum number of bytes to capture for
|
39
|
+
# each packet.
|
40
|
+
# @overload initialize(options={})
|
41
|
+
# @param [Hash] options
|
42
|
+
# @option options [String] :iface interface on which capture
|
43
|
+
# packets on. Default: Use default interface lookup. If no interface found,
|
44
|
+
# use loopback one.
|
45
|
+
# @option options [Integer] :max maximum number of packets to capture.
|
46
|
+
# @option options [Integer] :timeout maximum number of seconds before end
|
47
|
+
# of capture. Default: +nil+ (no timeout)
|
48
|
+
# @option options [String] :filter bpf filter
|
49
|
+
# @option options [Boolean] :promiscuous (default: +false+)
|
50
|
+
# @option options [Boolean] :parse parse raw data to generate packets before
|
51
|
+
# yielding. Default: +true+
|
52
|
+
# @option options [Integer] :snaplen maximum number of bytes to capture for
|
53
|
+
# each packet.
|
54
|
+
def initialize(iface_or_options={}, options={})
|
55
|
+
begin
|
56
|
+
@iface = Pcap.lookupdev
|
57
|
+
rescue PCAPRUB::BindingError
|
58
|
+
@iface = 'lo'
|
59
|
+
end
|
60
|
+
|
61
|
+
case iface_or_options
|
62
|
+
when Hash
|
63
|
+
options = iface_or_options
|
64
|
+
else
|
65
|
+
warn "[deprecation] use of PacketGen::Capture#initialize with iface name as\n" \
|
66
|
+
" first argument is deprecated. Instead, use:\n" \
|
67
|
+
' PacketGen::Capture.new(iface: \'name\').'
|
68
|
+
@iface = iface_or_options.to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
@packets = []
|
36
72
|
@raw_packets = []
|
37
|
-
@
|
73
|
+
@promisc = false
|
74
|
+
@snaplen = DEFAULT_SNAPLEN
|
75
|
+
@parse = true
|
38
76
|
set_options options
|
39
77
|
end
|
40
78
|
|
41
79
|
# Start capture
|
42
80
|
# @param [Hash] options complete see {#initialize}.
|
43
81
|
# @yieldparam [Packet,String] packet if a block is given, yield each
|
44
|
-
# captured packet (Packet or raw data String, depending on +:parse+)
|
82
|
+
# captured packet (Packet or raw data String, depending on +:parse+ option)
|
45
83
|
def start(options={})
|
46
84
|
set_options options
|
47
85
|
@pcap = PCAPRUB::Pcap.open_live(@iface, @snaplen, @promisc, 1)
|
48
86
|
set_filter
|
49
|
-
|
50
87
|
@cap_thread = Thread.new do
|
51
88
|
@pcap.each do |packet_data|
|
52
89
|
@raw_packets << packet_data
|
@@ -57,9 +94,7 @@ module PacketGen
|
|
57
94
|
else
|
58
95
|
yield packet_data if block_given?
|
59
96
|
end
|
60
|
-
if @max
|
61
|
-
break if @raw_packets.size >= @max
|
62
|
-
end
|
97
|
+
break if @max and @raw_packets.size >= @max
|
63
98
|
end
|
64
99
|
end
|
65
100
|
@cap_thread.join(@timeout)
|
@@ -80,21 +115,10 @@ module PacketGen
|
|
80
115
|
@max = options[:max] if options[:max]
|
81
116
|
@filter = options[:filter] if options[:filter]
|
82
117
|
@timeout = options[:timeout] if options[:timeout]
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
if options[:snaplen]
|
89
|
-
@snaplen = options[:snaplen]
|
90
|
-
else
|
91
|
-
@snaplen ||= DEFAULT_SNAPLEN
|
92
|
-
end
|
93
|
-
if options[:parse].nil?
|
94
|
-
@parse = true if @parse.nil?
|
95
|
-
else
|
96
|
-
@parse = options[:parse]
|
97
|
-
end
|
118
|
+
@promisc = options[:promisc] if options.has_key? :promisc
|
119
|
+
@snaplen = options[:snaplen] if options[:snaplen]
|
120
|
+
@parse = options[:parse] if options.has_key? :parse
|
121
|
+
@iface = options[:iface] if options[:iface]
|
98
122
|
end
|
99
123
|
|
100
124
|
def set_filter
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'network_interface'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
module PacketGen
|
5
|
+
|
6
|
+
# Config class to provide +config+ object to pgconsole
|
7
|
+
# @author Sylvain Daubert
|
8
|
+
class Config
|
9
|
+
|
10
|
+
# Default network interface
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :iface
|
13
|
+
# MAC address of default interface
|
14
|
+
# @return [String]
|
15
|
+
attr_reader :hwaddr
|
16
|
+
# IP address of default interface
|
17
|
+
# @return [String]
|
18
|
+
attr_reader :ipaddr
|
19
|
+
# IPv6 address of default interface
|
20
|
+
# @return [String]
|
21
|
+
attr_reader :ip6addr
|
22
|
+
|
23
|
+
# Create a configuration object. If +iface+ is not set, get first network interface.
|
24
|
+
# If non exists, use loopback one.
|
25
|
+
# @param [String,nil] iface
|
26
|
+
def initialize(iface=nil)
|
27
|
+
if iface.nil?
|
28
|
+
iface = NetworkInterface.interfaces.select { |iface| iface != 'lo' }.first
|
29
|
+
iface = NetworkInterface.interfaces.first if iface.nil?
|
30
|
+
end
|
31
|
+
@iface = iface
|
32
|
+
|
33
|
+
addresses = NetworkInterface.addresses(iface)
|
34
|
+
@hwaddr = addresses[Socket::AF_PACKET][0]['addr'] if addresses[Socket::AF_PACKET]
|
35
|
+
@ipaddr = addresses[Socket::AF_INET][0]['addr'] if addresses[Socket::AF_INET]
|
36
|
+
@ip6addr = addresses[Socket::AF_INET6][0]['addr'] if addresses[Socket::AF_INET6]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/packetgen/packet.rb
CHANGED
@@ -33,10 +33,10 @@ module PacketGen
|
|
33
33
|
#
|
34
34
|
# == Get packets
|
35
35
|
# Packets may be captured from wire:
|
36
|
-
# Packet.capture
|
36
|
+
# Packet.capture do |packet|
|
37
37
|
# do_some_stuffs
|
38
38
|
# end
|
39
|
-
# packets = Packet.capture('eth0', max: 5) # get 5 packets
|
39
|
+
# packets = Packet.capture(iface: 'eth0', max: 5) # get 5 packets from eth0
|
40
40
|
#
|
41
41
|
# Packets may also be read from a file:
|
42
42
|
# packets = Packet.read(file.pcapng)
|
@@ -68,9 +68,10 @@ module PacketGen
|
|
68
68
|
new.parse binary_str, first_header: first_header
|
69
69
|
end
|
70
70
|
|
71
|
-
# Capture packets
|
72
|
-
# @param [String] iface interface name
|
71
|
+
# Capture packets
|
73
72
|
# @param [Hash] options capture options
|
73
|
+
# @option options [String] :iface interface on which capture
|
74
|
+
# packets on. Default: Use default interface lookup.
|
74
75
|
# @option options [Integer] :max maximum number of packets to capture
|
75
76
|
# @option options [Integer] :timeout maximum number of seconds before end
|
76
77
|
# of capture
|
@@ -78,8 +79,8 @@ module PacketGen
|
|
78
79
|
# @option options [Boolean] :promiscuous
|
79
80
|
# @yieldparam [Packet] packet if a block is given, yield each captured packet
|
80
81
|
# @return [Array<Packet>] captured packet
|
81
|
-
def self.capture(
|
82
|
-
capture = Capture.new(
|
82
|
+
def self.capture(options={})
|
83
|
+
capture = Capture.new(options)
|
83
84
|
if block_given?
|
84
85
|
capture.start { |packet| yield packet }
|
85
86
|
else
|
data/lib/packetgen/version.rb
CHANGED
data/lib/packetgen.rb
CHANGED
@@ -42,8 +42,8 @@ module PacketGen
|
|
42
42
|
# @param [Hash] options capture options. See {Packet.capture}.
|
43
43
|
# @yieldparam [Packet] packet
|
44
44
|
# @return [Array<Packet>]
|
45
|
-
def self.capture(
|
46
|
-
Packet.capture(
|
45
|
+
def self.capture(options={})
|
46
|
+
Packet.capture(options) { |packet| yield packet if block_given? }
|
47
47
|
end
|
48
48
|
|
49
49
|
# Shortcut for {Packet.read}
|
data/packetgen.gemspec
CHANGED
@@ -16,13 +16,15 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
17
|
f.match(%r{^(test|spec|features)/})
|
18
18
|
end
|
19
|
-
spec.bindir = '
|
20
|
-
spec.executables = spec.files.grep(%r{^
|
19
|
+
spec.bindir = 'bin'
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ['lib']
|
22
22
|
|
23
23
|
spec.required_ruby_version = '>= 2.1.0'
|
24
24
|
|
25
25
|
spec.add_dependency 'pcaprub', '~>0.12.4'
|
26
|
+
spec.add_dependency 'pry', '~>0.10'
|
27
|
+
spec.add_dependency 'network_interface'
|
26
28
|
|
27
29
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
28
30
|
spec.add_development_dependency 'rake', '~> 10.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: packetgen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Daubert
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pcaprub
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.12.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.10'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.10'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: network_interface
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: bundler
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -97,7 +125,8 @@ dependencies:
|
|
97
125
|
description:
|
98
126
|
email:
|
99
127
|
- sylvain.daubert@laposte.net
|
100
|
-
executables:
|
128
|
+
executables:
|
129
|
+
- pgconsole
|
101
130
|
extensions: []
|
102
131
|
extra_rdoc_files: []
|
103
132
|
files:
|
@@ -107,8 +136,10 @@ files:
|
|
107
136
|
- LICENSE
|
108
137
|
- README.md
|
109
138
|
- Rakefile
|
139
|
+
- bin/pgconsole
|
110
140
|
- lib/packetgen.rb
|
111
141
|
- lib/packetgen/capture.rb
|
142
|
+
- lib/packetgen/config.rb
|
112
143
|
- lib/packetgen/header.rb
|
113
144
|
- lib/packetgen/header/arp.rb
|
114
145
|
- lib/packetgen/header/base.rb
|