packetgen 0.1.0
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 +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +14 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +116 -0
- data/Rakefile +18 -0
- data/lib/packetgen.rb +83 -0
- data/lib/packetgen/capture.rb +105 -0
- data/lib/packetgen/header.rb +21 -0
- data/lib/packetgen/header/arp.rb +148 -0
- data/lib/packetgen/header/eth.rb +155 -0
- data/lib/packetgen/header/header_class_methods.rb +28 -0
- data/lib/packetgen/header/header_methods.rb +51 -0
- data/lib/packetgen/header/ip.rb +283 -0
- data/lib/packetgen/header/ipv6.rb +215 -0
- data/lib/packetgen/header/udp.rb +133 -0
- data/lib/packetgen/packet.rb +357 -0
- data/lib/packetgen/pcapng.rb +39 -0
- data/lib/packetgen/pcapng/block.rb +32 -0
- data/lib/packetgen/pcapng/epb.rb +131 -0
- data/lib/packetgen/pcapng/file.rb +345 -0
- data/lib/packetgen/pcapng/idb.rb +145 -0
- data/lib/packetgen/pcapng/shb.rb +173 -0
- data/lib/packetgen/pcapng/spb.rb +103 -0
- data/lib/packetgen/pcapng/unknown_block.rb +80 -0
- data/lib/packetgen/structfu.rb +357 -0
- data/lib/packetgen/version.rb +7 -0
- data/packetgen.gemspec +30 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b95374a3ba00773c1a45e0464a797b1173aa4285
|
4
|
+
data.tar.gz: 4a6e2f8b12b6552de7afdca695e99221453439c1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 95ddd04f2758a1ef9f17d0b343ea9b00f718ad926adbf13bdd3bc9444e3d8a3cf980a8a8c7e3f5ebebf27a775da676f1754f86cf1dd15756c03fa76a1901184c
|
7
|
+
data.tar.gz: 145591b54094dcac9690e483138f123597029b12659c95d5ee1d7c14865f654003b17c24fd75a46647fe6c0864bccdff69bfd6e7e28623e2b3024628c447d073
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
language: ruby
|
2
|
+
sudo: required
|
3
|
+
rvm:
|
4
|
+
- 2.1
|
5
|
+
- 2.2
|
6
|
+
- 2.3.3
|
7
|
+
|
8
|
+
install:
|
9
|
+
- sudo apt-get update -qq
|
10
|
+
- sudo apt-get install libpcap-dev -qq
|
11
|
+
- bundler install --path vendor/bundle --jobs=3 --retry=3
|
12
|
+
script:
|
13
|
+
- bundler exec rake
|
14
|
+
- rvmsudo bundle exec rake spec:sudo
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Sylvain Daubert
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
|
2
|
+
[](https://travis-ci.org/sdaubert/packetgen)
|
3
|
+
|
4
|
+
# PacketGen
|
5
|
+
|
6
|
+
PacketGen aims at generate and capture network packets easily.
|
7
|
+
|
8
|
+
## Why PacketGen
|
9
|
+
Why create PacketGen ? There is already PacketFu!
|
10
|
+
|
11
|
+
Yes. But PacketFu is limited:
|
12
|
+
* upper protocols use fixed layers: TCP always uses IPv4, IP and IPv6 always uses Ethernet as MAC,...
|
13
|
+
* cannot handle tunneled packets (IP-in-IP, or deciphered ESP packets,...)
|
14
|
+
* cannot easily encapsulate or decapsulate packets
|
15
|
+
* parse packets top-down, and sometimes bad parse down layers
|
16
|
+
* cannot send packet on wire at IP/IPv6 level (Ethernet header is mandatory)
|
17
|
+
|
18
|
+
## use cases
|
19
|
+
|
20
|
+
For now, PacketGen is only a concept...
|
21
|
+
|
22
|
+
### Easily create packets
|
23
|
+
```
|
24
|
+
PacketGen.gen('IP') # generate a IP packet object
|
25
|
+
PacketGen.gen('TCP') # generate a TCP over IP packet object
|
26
|
+
PacketGen.gen('IP').add('TCP') # the same
|
27
|
+
PacketGen.gen('Eth') # generate a Ethernet packet object
|
28
|
+
PacketGen.gen('IP').add('IP') # generate a IP-in-IP tunnel packet object
|
29
|
+
|
30
|
+
# Generate a IP packet object, specifying addresses
|
31
|
+
PacketGen.gen('IP', src: '192.168.1.1', dst: '192.168.1.2')
|
32
|
+
|
33
|
+
# get binary packet
|
34
|
+
PacketGen.gen('IP').to_s
|
35
|
+
```
|
36
|
+
|
37
|
+
### Send packets on wire
|
38
|
+
need PcapRub for Ethernet packets. Need a C extension (use of C socket API) for IP packets.
|
39
|
+
|
40
|
+
```
|
41
|
+
# send Ethernet packet
|
42
|
+
PacketGen.gen('Eth', src: '00:00:00:00:01', dst: '00:00:00:00:02').to_w
|
43
|
+
# send IP packet
|
44
|
+
PacketGen.gen('IP', src: '192.168.1.1', dst: '192.168.1.2').to_w
|
45
|
+
# send forged IP packet over Ethernet
|
46
|
+
PacketGen.gen('Eth', src: '00:00:00:00:01', dst: '00:00:00:00:02').add('IP').to_w('eth1')
|
47
|
+
```
|
48
|
+
|
49
|
+
### Parse packets from binary data
|
50
|
+
```
|
51
|
+
packet = PacketGen.parse(binary_data)
|
52
|
+
```
|
53
|
+
|
54
|
+
### Capture packets from wire
|
55
|
+
need PCapRub.
|
56
|
+
|
57
|
+
```
|
58
|
+
# Capture packets, action from a block
|
59
|
+
PacketGen.capture('eth0') do |packet|
|
60
|
+
do_stuffs_with_packet
|
61
|
+
end
|
62
|
+
|
63
|
+
# Capture some packets, and act on them afterward
|
64
|
+
packets = PacketGen.capture('eth0', max: 10) # return when 10 packets were captured
|
65
|
+
|
66
|
+
# Use filters
|
67
|
+
packets = PacketGen.capture('eth0', filter: 'ip src 1.1.1.2', max: 1)
|
68
|
+
```
|
69
|
+
|
70
|
+
### Easily manipulate packets
|
71
|
+
```
|
72
|
+
# access header fields
|
73
|
+
pkt = PacketGen.gen('IP').add('TCP')
|
74
|
+
pkt.ip.src = '192.168.1.1'
|
75
|
+
pkt.ip(src: '192.168.1.1', ttl: 4)
|
76
|
+
pkt.tcp.dport = 80
|
77
|
+
|
78
|
+
# access header fields when multiple header of one kind exist
|
79
|
+
pkt = PacketGen.gen('IP').add('IP')
|
80
|
+
pkt.ip.src = '192.168.1.1' # set outer src field
|
81
|
+
pkt.ip(2).src = '10.0.0.1' # set inner src field
|
82
|
+
|
83
|
+
# test packet types
|
84
|
+
pkt = PacketGen.gen('IP').add('TCP')
|
85
|
+
pkt.is? 'TCP' # => true
|
86
|
+
pkt.is? 'IP' # => true
|
87
|
+
pkt.is? 'UDP' # => false
|
88
|
+
|
89
|
+
# encapulsate/decapsulate packets
|
90
|
+
pkt2 = PacketGen.gen('IP').add('ESP', spi: 1234)
|
91
|
+
pkt.encap pkt2 # pkt is now a IP/ESP/IP/TCP packet
|
92
|
+
# eq. to pkt.encap('IP', 'ESP', esp_spi: 1234)
|
93
|
+
pkt.decap('IP', 'ESP') # pkt is now inner IP/TCP packet
|
94
|
+
```
|
95
|
+
|
96
|
+
### Read/write PcapNG files
|
97
|
+
```
|
98
|
+
# read a PcapNG file, containing multiple packets
|
99
|
+
packets = PacketGen.read('file.pcapng')
|
100
|
+
packets.first.udp.sport = 65535
|
101
|
+
# write only one packet to a PcapNG file
|
102
|
+
pkt.write('one_packet.pcapng')
|
103
|
+
# write multiple packets to a PcapNG file
|
104
|
+
PacketGen.write('more_packets.pcapng', packets)
|
105
|
+
```
|
106
|
+
|
107
|
+
## License
|
108
|
+
MIT License (see [LICENSE](https://github.com/sdaubert/packetgen/LICENSE))
|
109
|
+
|
110
|
+
Copyright © 2016 Sylvain Daubert
|
111
|
+
|
112
|
+
### Other sources
|
113
|
+
All original code maintains its copyright from its original authors and licensing.
|
114
|
+
|
115
|
+
This is mainly for StrucFu (copied from [PacketFu](https://github.com/packetfu/packetfu))
|
116
|
+
and PcapNG module (also copied from PacketFu, but I am the author).
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'yard'
|
4
|
+
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new do |t|
|
8
|
+
t.rspec_opts = '-t ~sudo'
|
9
|
+
end
|
10
|
+
RSpec::Core::RakeTask.new('spec:sudo') do |t|
|
11
|
+
t.rspec_opts = '-t sudo'
|
12
|
+
end
|
13
|
+
|
14
|
+
YARD::Rake::YardocTask.new do |t|
|
15
|
+
t.options = ['--no-private']
|
16
|
+
t.files = ['lib/**/*.rb', '-', 'LICENSE']
|
17
|
+
end
|
18
|
+
|
data/lib/packetgen.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'packetgen/version'
|
2
|
+
|
3
|
+
# @author Sylvain Daubert
|
4
|
+
module PacketGen
|
5
|
+
|
6
|
+
# Base exception class for PacketGen exceptions
|
7
|
+
class Error < StandardError; end
|
8
|
+
|
9
|
+
# Packet badly formatted
|
10
|
+
class FormatError < Error; end
|
11
|
+
|
12
|
+
# Parsing error
|
13
|
+
class ParseError < Error; end
|
14
|
+
|
15
|
+
# Sending packet on wire error
|
16
|
+
class WireError < Error; end
|
17
|
+
|
18
|
+
# Shortcut for {Packet.gen}
|
19
|
+
# @param [String] protocol base protocol for packet
|
20
|
+
# @param [Hash] options specific options for +protocol+
|
21
|
+
# @return [Packet]
|
22
|
+
def self.gen(protocol, options={})
|
23
|
+
Packet.gen protocol, options
|
24
|
+
end
|
25
|
+
|
26
|
+
# Shortcut for {Packet.parse}
|
27
|
+
# @param [String] binary_str
|
28
|
+
# @param [String] first_header First protocol header
|
29
|
+
# @return [Packet]
|
30
|
+
def self.parse(binary_str, first_header: nil)
|
31
|
+
Packet.parse binary_str, first_header
|
32
|
+
end
|
33
|
+
|
34
|
+
# Shortcut for {Packet.capture}
|
35
|
+
# @param [String] iface interface name
|
36
|
+
# @param [Hash] options capture options. See {Packet.capture}.
|
37
|
+
# @yieldparam [Packet] packet
|
38
|
+
# @return [Array<Packet>]
|
39
|
+
def self.capture(iface, options={})
|
40
|
+
Packet.capture(iface, options) { |packet| yield packet }
|
41
|
+
end
|
42
|
+
|
43
|
+
# Shortcut for {Packet.read}
|
44
|
+
# @param [String] filename PcapNG file
|
45
|
+
# @return [Array<Packet>]
|
46
|
+
def self.read(filename)
|
47
|
+
Packet.read filename
|
48
|
+
end
|
49
|
+
|
50
|
+
# Shortcut for {Packet.write}
|
51
|
+
# @param [String] filename
|
52
|
+
# @param [Array<Packet>] packets packets to write
|
53
|
+
# @return [void]
|
54
|
+
def self.write(filename, packets)
|
55
|
+
Packet.write filename, packets
|
56
|
+
end
|
57
|
+
|
58
|
+
# Force binary encoding for +str+
|
59
|
+
# @param [String] str
|
60
|
+
# @return [String] binary encoded string
|
61
|
+
def self.force_binary(str)
|
62
|
+
str.force_encoding Encoding::BINARY
|
63
|
+
end
|
64
|
+
|
65
|
+
# Get default network interface (ie. first non-loopback declared interface)
|
66
|
+
# @return [String]
|
67
|
+
def self.default_iface
|
68
|
+
return @default_iface if @default_iface
|
69
|
+
|
70
|
+
ipaddr = `ip addr`.split("\n")
|
71
|
+
@default_iface = ipaddr.each_with_index do |line, i|
|
72
|
+
m = line.match(/^\d+: (\w+\d+):/)
|
73
|
+
next if m.nil?
|
74
|
+
next if m[1] == 'lo'
|
75
|
+
break m[1]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
require 'packetgen/structfu'
|
81
|
+
require 'packetgen/packet'
|
82
|
+
require 'packetgen/capture'
|
83
|
+
require 'packetgen/pcapng'
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module PacketGen
|
2
|
+
|
3
|
+
# Capture packets from wire
|
4
|
+
# @author Sylvain Daubert
|
5
|
+
class Capture
|
6
|
+
|
7
|
+
# Default snaplen to use if :snaplen option not defined
|
8
|
+
DEFAULT_SNAPLEN = 0xffff
|
9
|
+
|
10
|
+
# Get captured packets
|
11
|
+
# @return [Array<Packets>]
|
12
|
+
attr_reader :packets
|
13
|
+
|
14
|
+
# Get captured packet raw data
|
15
|
+
# @return [Array<String>]
|
16
|
+
attr_reader :raw_packets
|
17
|
+
|
18
|
+
# @param [String] iface interface on which capture packets
|
19
|
+
# @param [Hash] options
|
20
|
+
# @option options [Integer] :max maximum number of packets to capture
|
21
|
+
# @option options [Integer] :timeout maximum number of seconds before end
|
22
|
+
# of capture. Default: +nil+ (no timeout)
|
23
|
+
# @option options [String] :filter bpf filter
|
24
|
+
# @option options [Boolean] :promiscuous (default: +false+)
|
25
|
+
# @option options [Boolean] :parse parse raw data to generate packets before
|
26
|
+
# yielding. Default: +true+
|
27
|
+
# @option options [Integer] :snaplen maximum number of bytes to capture for
|
28
|
+
# each packet
|
29
|
+
def initialize(iface, options={})
|
30
|
+
@packets = []
|
31
|
+
@raw_packets = []
|
32
|
+
@iface = iface
|
33
|
+
set_options options
|
34
|
+
end
|
35
|
+
|
36
|
+
# Start capture
|
37
|
+
# @param [Hash] options complete see {#initialize}.
|
38
|
+
# @yieldparam [Packet,String] packet if a block is given, yield each
|
39
|
+
# captured packet (Packet or raw data String, depending on +:parse+)
|
40
|
+
def start(options={})
|
41
|
+
set_options options
|
42
|
+
@pcap = PCAPRUB::Pcap.open_live(@iface, @snaplen, @promisc, 1)
|
43
|
+
set_filter
|
44
|
+
|
45
|
+
@cap_thread = Thread.new do
|
46
|
+
@pcap.each do |packet_data|
|
47
|
+
@raw_packets << packet_data
|
48
|
+
if @parse
|
49
|
+
packet = Packet.parse(packet_data)
|
50
|
+
@packets << packet
|
51
|
+
yield packet if block_given?
|
52
|
+
else
|
53
|
+
yield packet_data if block_given?
|
54
|
+
end
|
55
|
+
if @max
|
56
|
+
break if @raw_packets.size >= @max
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
@cap_thread.join(@timeout)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Stop capture. Should be used from another thread, as {#start} blocs.
|
64
|
+
#
|
65
|
+
# BEWARE: multiple capture should not be started in different threads. No effort
|
66
|
+
# has been made to make Capture nor PacketGen thread-safe.
|
67
|
+
# @return [void]
|
68
|
+
def stop
|
69
|
+
@cap_thread.kill
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def set_options(options)
|
75
|
+
@max = options[:max] if options[:max]
|
76
|
+
@filter = options[:filter] if options[:filter]
|
77
|
+
if options[:timeout]
|
78
|
+
@timeout = options[:timeout]
|
79
|
+
else
|
80
|
+
@timeout ||= 0
|
81
|
+
end
|
82
|
+
if options[:promisc]
|
83
|
+
@promisc = options[:promisc]
|
84
|
+
else
|
85
|
+
@promisc ||= false
|
86
|
+
end
|
87
|
+
if options[:snaplen]
|
88
|
+
@snaplen = options[:snaplen]
|
89
|
+
else
|
90
|
+
@snaplen ||= DEFAULT_SNAPLEN
|
91
|
+
end
|
92
|
+
if options[:parse].nil?
|
93
|
+
@parse = true if @parse.nil?
|
94
|
+
else
|
95
|
+
@parse = options[:parse]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_filter
|
100
|
+
return if @filter.nil?
|
101
|
+
return if @filter.empty?
|
102
|
+
@pcap.setfilter @filter
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module PacketGen
|
2
|
+
# Namespace for protocol header classes
|
3
|
+
# @author Sylvain Daubert
|
4
|
+
module Header
|
5
|
+
|
6
|
+
# Get known header classes
|
7
|
+
# @return [Array<Class>]
|
8
|
+
def self.all
|
9
|
+
constants.map { |sym| const_get sym }.
|
10
|
+
select { |klass| klass < Struct && klass < HeaderMethods }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require_relative 'header/header_class_methods'
|
16
|
+
require_relative 'header/header_methods'
|
17
|
+
require_relative 'header/eth'
|
18
|
+
require_relative 'header/ip'
|
19
|
+
require_relative 'header/arp'
|
20
|
+
require_relative 'header/ipv6'
|
21
|
+
require_relative 'header/udp'
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module PacketGen
|
2
|
+
module Header
|
3
|
+
|
4
|
+
# ARP header class
|
5
|
+
# @author Sylvain Daubert
|
6
|
+
class ARP < Struct.new(:hw_type, :proto, :hw_len, :proto_len, :opcode,
|
7
|
+
:src_mac, :src_ip, :dst_mac, :dst_ip, :body)
|
8
|
+
include StructFu
|
9
|
+
include HeaderMethods
|
10
|
+
extend HeaderClassMethods
|
11
|
+
|
12
|
+
# @param [Hash] options
|
13
|
+
# @option options [Integer] :hw_type network protocol type (default: 1)
|
14
|
+
# @option options [Integer] :proto internet protocol type (default: 0x800)
|
15
|
+
# @option options [Integer] :hw_len length of hardware addresses (default: 6)
|
16
|
+
# @option options [Integer] :proto_len length of internet addresses (default: 4)
|
17
|
+
# @option options [Integer] :opcode operation performing by sender (default: 1).
|
18
|
+
# known values are +request+ (1) and +reply+ (2)
|
19
|
+
# @option options [String] :src_mac sender hardware address
|
20
|
+
# @option options [String] :src_ip sender internet address
|
21
|
+
# @option options [String] :dst_mac target hardware address
|
22
|
+
# @option options [String] :dst_ip targetr internet address
|
23
|
+
def initialize(options={})
|
24
|
+
super Int16.new(options[:hw_type] || 1),
|
25
|
+
Int16.new(options[:proto] || 0x800),
|
26
|
+
Int8.new(options[:hw_len] || 6),
|
27
|
+
Int8.new(options[:proto_len] || 4),
|
28
|
+
Int16.new(options[:opcode] || 1),
|
29
|
+
Eth::MacAddr.new.parse(options[:src_mac]),
|
30
|
+
IP::Addr.new.parse(options[:src_ip]),
|
31
|
+
Eth::MacAddr.new.parse(options[:dst_mac]),
|
32
|
+
IP::Addr.new.parse(options[:dst_ip]),
|
33
|
+
StructFu::String.new.read(options[:body])
|
34
|
+
end
|
35
|
+
|
36
|
+
# Read a ARP header from a string
|
37
|
+
# @param [String] str binary string
|
38
|
+
# @return [self]
|
39
|
+
def read(str)
|
40
|
+
force_binary str
|
41
|
+
raise ParseError, 'string too short for ARP' if str.size < self.sz
|
42
|
+
self[:hw_type].read str[0, 2]
|
43
|
+
self[:proto].read str[2, 2]
|
44
|
+
self[:hw_len].read str[4, 1]
|
45
|
+
self[:proto_len].read str[5, 1]
|
46
|
+
self[:opcode].read str[6, 2]
|
47
|
+
self[:src_mac].read str[8, 6]
|
48
|
+
self[:src_ip].read str[14, 4]
|
49
|
+
self[:dst_mac].read str[18, 6]
|
50
|
+
self[:dst_ip].read str[24, 4]
|
51
|
+
self[:body].read str[28..-1]
|
52
|
+
end
|
53
|
+
|
54
|
+
# @!attribute [rw] hw_type
|
55
|
+
# @return [Integer]
|
56
|
+
def hw_type
|
57
|
+
self[:hw_type].to_i
|
58
|
+
end
|
59
|
+
|
60
|
+
def hw_type=(i)
|
61
|
+
self[:hw_type].read i
|
62
|
+
end
|
63
|
+
|
64
|
+
# @!attribute [rw] proto
|
65
|
+
# @return [Integer]
|
66
|
+
def proto
|
67
|
+
self[:proto].to_i
|
68
|
+
end
|
69
|
+
|
70
|
+
def proto=(i)
|
71
|
+
self[:proto].read i
|
72
|
+
end
|
73
|
+
|
74
|
+
# @!attribute [rw] hw_len
|
75
|
+
# @return [Integer]
|
76
|
+
def hw_len
|
77
|
+
self[:hw_len].to_i
|
78
|
+
end
|
79
|
+
|
80
|
+
def hw_len=(i)
|
81
|
+
self[:hw_len].read i
|
82
|
+
end
|
83
|
+
|
84
|
+
# @!attribute [rw] proto_len
|
85
|
+
# @return [Integer]
|
86
|
+
def proto_len
|
87
|
+
self[:proto_len].to_i
|
88
|
+
end
|
89
|
+
|
90
|
+
def proto_len=(i)
|
91
|
+
self[:proto_len].read i
|
92
|
+
end
|
93
|
+
|
94
|
+
# @!attribute [rw] opcode
|
95
|
+
# @return [Integer]
|
96
|
+
def opcode
|
97
|
+
self[:opcode].to_i
|
98
|
+
end
|
99
|
+
|
100
|
+
def opcode=(i)
|
101
|
+
self[:opcode].read i
|
102
|
+
end
|
103
|
+
|
104
|
+
# @!attribute [rw] src_mac
|
105
|
+
# @return [String]
|
106
|
+
def src_mac
|
107
|
+
self[:src_mac].to_x
|
108
|
+
end
|
109
|
+
|
110
|
+
def src_mac=(addr)
|
111
|
+
self[:src_mac].parse addr
|
112
|
+
end
|
113
|
+
|
114
|
+
# @!attribute [rw] src_ip
|
115
|
+
# @return [String]
|
116
|
+
def src_ip
|
117
|
+
self[:src_ip].to_x
|
118
|
+
end
|
119
|
+
|
120
|
+
def src_ip=(addr)
|
121
|
+
self[:src_ip].parse addr
|
122
|
+
end
|
123
|
+
|
124
|
+
# @!attribute [rw] dst_mac
|
125
|
+
# @return [String]
|
126
|
+
def dst_mac
|
127
|
+
self[:dst_mac].to_x
|
128
|
+
end
|
129
|
+
|
130
|
+
def dst_mac=(addr)
|
131
|
+
self[:dst_mac].parse addr
|
132
|
+
end
|
133
|
+
|
134
|
+
# @!attribute [rw] dst_ip
|
135
|
+
# @return [String]
|
136
|
+
def dst_ip
|
137
|
+
self[:dst_ip].to_x
|
138
|
+
end
|
139
|
+
|
140
|
+
def dst_ip=(addr)
|
141
|
+
self[:dst_ip].parse addr
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
Eth.bind_header ARP, proto: 0x806
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|