packetgen 2.3.0 → 2.4.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 +4 -4
- data/.travis.yml +1 -0
- data/README.md +5 -1
- data/lib/packetgen/header.rb +4 -0
- data/lib/packetgen/header/icmpv6.rb +0 -1
- data/lib/packetgen/header/igmp.rb +126 -0
- data/lib/packetgen/header/igmpv3.rb +150 -0
- data/lib/packetgen/header/igmpv3/group_record.rb +98 -0
- data/lib/packetgen/header/igmpv3/mq.rb +96 -0
- data/lib/packetgen/header/igmpv3/mr.rb +65 -0
- data/lib/packetgen/header/ip.rb +40 -70
- data/lib/packetgen/header/ip/addr.rb +58 -0
- data/lib/packetgen/header/ip/option.rb +194 -0
- data/lib/packetgen/header/ip/options.rb +53 -0
- data/lib/packetgen/header/ipv6.rb +24 -69
- data/lib/packetgen/header/ipv6/addr.rb +96 -0
- data/lib/packetgen/header/ipv6/extension.rb +65 -0
- data/lib/packetgen/header/ipv6/hop_by_hop.rb +132 -0
- data/lib/packetgen/header/mld.rb +100 -0
- data/lib/packetgen/header/mldv2.rb +50 -0
- data/lib/packetgen/header/mldv2/mcast_address_record.rb +103 -0
- data/lib/packetgen/header/mldv2/mlq.rb +144 -0
- data/lib/packetgen/header/mldv2/mlr.rb +65 -0
- data/lib/packetgen/packet.rb +18 -0
- data/lib/packetgen/types/array.rb +1 -0
- data/lib/packetgen/types/string.rb +14 -13
- data/lib/packetgen/version.rb +1 -1
- metadata +18 -2
@@ -0,0 +1,53 @@
|
|
1
|
+
# This file is part of PacketGen
|
2
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
3
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
4
|
+
# This program is published under MIT license.
|
5
|
+
|
6
|
+
module PacketGen
|
7
|
+
module Header
|
8
|
+
class IP
|
9
|
+
|
10
|
+
# Class to handle IP options
|
11
|
+
# @author Sylvain Daubert
|
12
|
+
class Options < Types::Array
|
13
|
+
set_of Option
|
14
|
+
|
15
|
+
HUMAN_SEPARATOR = ';'
|
16
|
+
|
17
|
+
# Read IP header options from a string
|
18
|
+
# @param [String] str binary string
|
19
|
+
# @return [self]
|
20
|
+
def read(str)
|
21
|
+
clear
|
22
|
+
return self if str.nil?
|
23
|
+
PacketGen.force_binary str
|
24
|
+
|
25
|
+
i = 0
|
26
|
+
types = Option.types
|
27
|
+
while i < str.to_s.length
|
28
|
+
type = str[i, 1].unpack('C').first
|
29
|
+
this_option = if types[type].nil?
|
30
|
+
Option.new
|
31
|
+
else
|
32
|
+
types[type].new
|
33
|
+
end
|
34
|
+
this_option.read str[i, str.size]
|
35
|
+
self << this_option
|
36
|
+
i += this_option.sz
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# Get binary string
|
42
|
+
# @return [String]
|
43
|
+
def to_s
|
44
|
+
str = super
|
45
|
+
if str.length % 4 != 0
|
46
|
+
str += ([0] * (4 - (str.length % 4))).pack('C*')
|
47
|
+
end
|
48
|
+
str
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -43,77 +43,11 @@ module PacketGen
|
|
43
43
|
# ipv6.dst = '2001:1234:5678:abcd::123'
|
44
44
|
# ipv6.body.read 'this is a body'
|
45
45
|
# @author Sylvain Daubert
|
46
|
-
class IPv6 < Base
|
47
|
-
|
48
|
-
# IPv6 address, as a group of 8 2-byte words
|
49
|
-
# @author Sylvain Daubert
|
50
|
-
class Addr < Types::Fields
|
51
|
-
|
52
|
-
# @!attribute a1
|
53
|
-
# 1st 2-byte word of IPv6 address
|
54
|
-
# @return [Integer]
|
55
|
-
define_field :a1, Types::Int16
|
56
|
-
# @!attribute a2
|
57
|
-
# 2nd 2-byte word of IPv6 address
|
58
|
-
# @return [Integer]
|
59
|
-
define_field :a2, Types::Int16
|
60
|
-
# @!attribute a3
|
61
|
-
# 3rd 2-byte word of IPv6 address
|
62
|
-
# @return [Integer]
|
63
|
-
define_field :a3, Types::Int16
|
64
|
-
# @!attribute a4
|
65
|
-
# 4th 2-byte word of IPv6 address
|
66
|
-
# @return [Integer]
|
67
|
-
define_field :a4, Types::Int16
|
68
|
-
# @!attribute a5
|
69
|
-
# 5th 2-byte word of IPv6 address
|
70
|
-
# @return [Integer]
|
71
|
-
define_field :a5, Types::Int16
|
72
|
-
# @!attribute a6
|
73
|
-
# 6th 2-byte word of IPv6 address
|
74
|
-
# @return [Integer]
|
75
|
-
define_field :a6, Types::Int16
|
76
|
-
# @!attribute a7
|
77
|
-
# 7th 2-byte word of IPv6 address
|
78
|
-
# @return [Integer]
|
79
|
-
define_field :a7, Types::Int16
|
80
|
-
# @!attribute a8
|
81
|
-
# 8th 2-byte word of IPv6 address
|
82
|
-
# @return [Integer]
|
83
|
-
define_field :a8, Types::Int16
|
84
|
-
|
85
|
-
# Read a colon-delimited address
|
86
|
-
# @param [String] str
|
87
|
-
# @return [self]
|
88
|
-
def from_human(str)
|
89
|
-
return self if str.nil?
|
90
|
-
addr = IPAddr.new(str)
|
91
|
-
raise ArgumentError, 'string is not a IPv6 address' unless addr.ipv6?
|
92
|
-
addri = addr.to_i
|
93
|
-
self.a1 = addri >> 112
|
94
|
-
self.a2 = addri >> 96 & 0xffff
|
95
|
-
self.a3 = addri >> 80 & 0xffff
|
96
|
-
self.a4 = addri >> 64 & 0xffff
|
97
|
-
self.a5 = addri >> 48 & 0xffff
|
98
|
-
self.a6 = addri >> 32 & 0xffff
|
99
|
-
self.a7 = addri >> 16 & 0xffff
|
100
|
-
self.a8 = addri & 0xffff
|
101
|
-
self
|
102
|
-
end
|
103
|
-
|
104
|
-
# Addr6 in human readable form (colon-delimited hex string)
|
105
|
-
# @return [String]
|
106
|
-
def to_human
|
107
|
-
IPAddr.new(to_a.map { |a| a.to_i.to_s(16) }.join(':')).to_s
|
108
|
-
end
|
46
|
+
class IPv6 < Base;end
|
109
47
|
|
110
|
-
|
111
|
-
# @return [Array<Integer>]
|
112
|
-
def to_a
|
113
|
-
@fields.values
|
114
|
-
end
|
115
|
-
end
|
48
|
+
require_relative 'ipv6/addr'
|
116
49
|
|
50
|
+
class IPv6
|
117
51
|
# IPv6 Ether type
|
118
52
|
ETHERTYPE = 0x86dd
|
119
53
|
|
@@ -232,3 +166,24 @@ module PacketGen
|
|
232
166
|
IP.bind_header IPv6, protocol: 41 # 6to4
|
233
167
|
end
|
234
168
|
end
|
169
|
+
|
170
|
+
require_relative 'ipv6/extension'
|
171
|
+
|
172
|
+
module PacketGen
|
173
|
+
module Header
|
174
|
+
class IPv6
|
175
|
+
class << self
|
176
|
+
alias old_bind_header bind_header
|
177
|
+
|
178
|
+
# Bind a upper header to IPv6 and its defined extension headers.
|
179
|
+
# @see Base.bind_header
|
180
|
+
def bind_header(header_klass, args={})
|
181
|
+
IPv6.old_bind_header header_klass, args
|
182
|
+
[IPv6::HopByHop].each do |klass|
|
183
|
+
klass.bind_header header_klass, args
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# This file is part of PacketGen
|
3
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
4
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
5
|
+
# This program is published under MIT license.
|
6
|
+
require 'ipaddr'
|
7
|
+
|
8
|
+
module PacketGen
|
9
|
+
module Header
|
10
|
+
class IPv6
|
11
|
+
# IPv6 address, as a group of 8 2-byte words
|
12
|
+
# @author Sylvain Daubert
|
13
|
+
class Addr < Types::Fields
|
14
|
+
|
15
|
+
# @!attribute a1
|
16
|
+
# 1st 2-byte word of IPv6 address
|
17
|
+
# @return [Integer]
|
18
|
+
define_field :a1, Types::Int16
|
19
|
+
# @!attribute a2
|
20
|
+
# 2nd 2-byte word of IPv6 address
|
21
|
+
# @return [Integer]
|
22
|
+
define_field :a2, Types::Int16
|
23
|
+
# @!attribute a3
|
24
|
+
# 3rd 2-byte word of IPv6 address
|
25
|
+
# @return [Integer]
|
26
|
+
define_field :a3, Types::Int16
|
27
|
+
# @!attribute a4
|
28
|
+
# 4th 2-byte word of IPv6 address
|
29
|
+
# @return [Integer]
|
30
|
+
define_field :a4, Types::Int16
|
31
|
+
# @!attribute a5
|
32
|
+
# 5th 2-byte word of IPv6 address
|
33
|
+
# @return [Integer]
|
34
|
+
define_field :a5, Types::Int16
|
35
|
+
# @!attribute a6
|
36
|
+
# 6th 2-byte word of IPv6 address
|
37
|
+
# @return [Integer]
|
38
|
+
define_field :a6, Types::Int16
|
39
|
+
# @!attribute a7
|
40
|
+
# 7th 2-byte word of IPv6 address
|
41
|
+
# @return [Integer]
|
42
|
+
define_field :a7, Types::Int16
|
43
|
+
# @!attribute a8
|
44
|
+
# 8th 2-byte word of IPv6 address
|
45
|
+
# @return [Integer]
|
46
|
+
define_field :a8, Types::Int16
|
47
|
+
|
48
|
+
# Read a colon-delimited address
|
49
|
+
# @param [String] str
|
50
|
+
# @return [self]
|
51
|
+
def from_human(str)
|
52
|
+
return self if str.nil?
|
53
|
+
addr = IPAddr.new(str)
|
54
|
+
raise ArgumentError, 'string is not a IPv6 address' unless addr.ipv6?
|
55
|
+
addri = addr.to_i
|
56
|
+
self.a1 = addri >> 112
|
57
|
+
self.a2 = addri >> 96 & 0xffff
|
58
|
+
self.a3 = addri >> 80 & 0xffff
|
59
|
+
self.a4 = addri >> 64 & 0xffff
|
60
|
+
self.a5 = addri >> 48 & 0xffff
|
61
|
+
self.a6 = addri >> 32 & 0xffff
|
62
|
+
self.a7 = addri >> 16 & 0xffff
|
63
|
+
self.a8 = addri & 0xffff
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# Addr6 in human readable form (colon-delimited hex string)
|
68
|
+
# @return [String]
|
69
|
+
def to_human
|
70
|
+
IPAddr.new(to_a.map { |a| a.to_i.to_s(16) }.join(':')).to_s
|
71
|
+
end
|
72
|
+
|
73
|
+
# Return an array of address 16-bit words
|
74
|
+
# @return [Array<Integer>]
|
75
|
+
def to_a
|
76
|
+
@fields.values
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Class to handle series of IPv6 addresses
|
81
|
+
# @author Sylvain Daubert
|
82
|
+
class ArrayOfAddr < Types::Array
|
83
|
+
set_of IPv6::Addr
|
84
|
+
|
85
|
+
# Push a IPv6 address to the array
|
86
|
+
# @param [String,Addr] addr
|
87
|
+
# @return [self]
|
88
|
+
# array << '2001:1234::125'
|
89
|
+
def push(addr)
|
90
|
+
addr = addr.is_a?(Addr) ? addr : Addr.new.from_human(addr)
|
91
|
+
super(addr)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# This file is part of PacketGen
|
2
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
3
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
4
|
+
# This program is published under MIT license.
|
5
|
+
|
6
|
+
module PacketGen
|
7
|
+
module Header
|
8
|
+
class IPv6
|
9
|
+
# Base class to handle IPv6 extensions
|
10
|
+
# @abstract You should not use this class but its subclasses.
|
11
|
+
# A IPv6 extension header has the following format:
|
12
|
+
# 0 1 2 3
|
13
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
14
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
15
|
+
# | Next Header | Hdr Ext Len | |
|
16
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
|
17
|
+
# | |
|
18
|
+
# . .
|
19
|
+
# . Options .
|
20
|
+
# . .
|
21
|
+
# | |
|
22
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
23
|
+
#
|
24
|
+
# Such a header consists of:
|
25
|
+
# * a {#next} header field ({Types::Int8}),
|
26
|
+
# * a {#length} field ({Types::Int8}),
|
27
|
+
# * an {#options} field ({Types::String}),
|
28
|
+
# * and a {#body}, containing next header.
|
29
|
+
# @author Sylvain Daubert
|
30
|
+
class Extension < Base
|
31
|
+
# @!attribute next
|
32
|
+
# 8-bit Next header field
|
33
|
+
# @return [Integer]
|
34
|
+
define_field :next, Types::Int8
|
35
|
+
# @!attribute length
|
36
|
+
# 8-bit extension length, in 8-octets units, not including the
|
37
|
+
# first 8 octets.
|
38
|
+
# @return [Integer]
|
39
|
+
define_field :length, Types::Int8
|
40
|
+
# @!attribute options
|
41
|
+
# Specific options of extension header
|
42
|
+
# @return [String]
|
43
|
+
define_field :options, Types::String,
|
44
|
+
builder: ->(h, t) { t.new(length_from: ->() { h.real_length }) }
|
45
|
+
# @!attribute body
|
46
|
+
# @return [String,Base]
|
47
|
+
define_field :body, Types::String
|
48
|
+
|
49
|
+
# Get real extension header length
|
50
|
+
# @return [Integer]
|
51
|
+
def real_length
|
52
|
+
(length + 1) * 8
|
53
|
+
end
|
54
|
+
|
55
|
+
# Compute length and set +len+ field
|
56
|
+
# @return [Integer]
|
57
|
+
def calc_length
|
58
|
+
self.length = (options.sz + 2) / 8 - 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
require_relative 'hop_by_hop'
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# This file is part of PacketGen
|
2
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
3
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
4
|
+
# This program is published under MIT license.
|
5
|
+
|
6
|
+
module PacketGen
|
7
|
+
module Header
|
8
|
+
class IPv6
|
9
|
+
# Option for {HopByHop} IPv6 extension header
|
10
|
+
# @author Sylvain Daubert
|
11
|
+
class Option < Types::TLV
|
12
|
+
# Known option types
|
13
|
+
TYPES = {
|
14
|
+
1 => 'padn',
|
15
|
+
5 => 'router_alert'
|
16
|
+
}
|
17
|
+
|
18
|
+
# @return [String]
|
19
|
+
def to_human
|
20
|
+
case type
|
21
|
+
when 1
|
22
|
+
"pad#{self.sz}"
|
23
|
+
else
|
24
|
+
"#{human_type}(#{value.to_s.inspect})"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Special option pad1, for {HopByHop} IPv6 extension header
|
30
|
+
# @author Sylvain Daubert
|
31
|
+
class Pad1 < Types::Fields
|
32
|
+
# @!attribute pad
|
33
|
+
# @return [Integer]
|
34
|
+
define_field :pad, Types::Int8, default: 0
|
35
|
+
|
36
|
+
# @return [String]
|
37
|
+
def to_human
|
38
|
+
'pad1'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Array of {Option}, for {HopByHop} IPv6 extension header
|
43
|
+
# @author Sylvain Daubert
|
44
|
+
class Options < Types::Array
|
45
|
+
set_of Option
|
46
|
+
|
47
|
+
# Populate object from a binary string
|
48
|
+
# @param [String] str
|
49
|
+
# @return [self]
|
50
|
+
def read(str)
|
51
|
+
clear
|
52
|
+
return self if str.nil?
|
53
|
+
force_binary str
|
54
|
+
klass = self.class.class_eval { @klass }
|
55
|
+
while str.length > 0
|
56
|
+
obj = klass.new.read(str)
|
57
|
+
if obj.type == 0
|
58
|
+
obj = Pad1.new.read(str)
|
59
|
+
end
|
60
|
+
self.push obj
|
61
|
+
str.slice!(0, obj.sz)
|
62
|
+
end
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get options as a binary string. Add padding if needed.
|
67
|
+
# @return [String]
|
68
|
+
def to_s
|
69
|
+
str = super
|
70
|
+
case str.size % 8
|
71
|
+
when 0
|
72
|
+
return str
|
73
|
+
when 7
|
74
|
+
# only on byte needed: use pad1 option
|
75
|
+
str << [0].pack('C')
|
76
|
+
else
|
77
|
+
# use padn option
|
78
|
+
len = 8 - (str.size % 8) - 4
|
79
|
+
padn = Option.new(type: 'padn', value: "\x00" * len)
|
80
|
+
str << padn.to_s
|
81
|
+
end
|
82
|
+
str
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Hop-by-hop IPv6 extension
|
87
|
+
#
|
88
|
+
# 0 1 2 3
|
89
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
90
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
91
|
+
# | Next Header | Hdr Ext Len | |
|
92
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
|
93
|
+
# | |
|
94
|
+
# . .
|
95
|
+
# . Options .
|
96
|
+
# . .
|
97
|
+
# | |
|
98
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
99
|
+
#
|
100
|
+
# Hop-by-hop IPv6 extension header consists of:
|
101
|
+
# * a {#next} header field ({Types::Int8}),
|
102
|
+
# * a {#length} field ({Types::Int8}),
|
103
|
+
# * an {#options} field ({Options}),
|
104
|
+
# * and a {#body}, containing next header.
|
105
|
+
# @author Sylvain Daubert
|
106
|
+
class HopByHop < Extension
|
107
|
+
# redefine options field
|
108
|
+
delete_field :options
|
109
|
+
# @!attribute options
|
110
|
+
# Specific options of extension header
|
111
|
+
# @return [Options]
|
112
|
+
define_field_before :body, :options, Options
|
113
|
+
|
114
|
+
# Populate object from a binary string
|
115
|
+
# @param [String] str
|
116
|
+
# @return [self]
|
117
|
+
def read(str)
|
118
|
+
return self if str.nil?
|
119
|
+
force_binary str
|
120
|
+
self[:next].read str[0, 1]
|
121
|
+
self[:length].read str[1, 1]
|
122
|
+
self[:options].read str[2, real_length - 2]
|
123
|
+
self[:body].read str[real_length..-1]
|
124
|
+
self
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
self.add_class IPv6::HopByHop
|
130
|
+
IPv6.bind_header IPv6::HopByHop, next: 0
|
131
|
+
end
|
132
|
+
end
|