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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed1c35530cd3ce2e62507e1a78c92fb5f764a49b
4
- data.tar.gz: 77d4baf7bc094aa0b04d7e6035e9420233135008
3
+ metadata.gz: d08bf3611fcb1af6c5e794d4582fe373b4887c02
4
+ data.tar.gz: 7149de75ef5a5ebaca9d9b90239ffce8619c38c4
5
5
  SHA512:
6
- metadata.gz: dbe78e47c236179d9d866ce997778c1b2b65633b751d47d72948bcb0f7f56ccb8bf52fbdd1dd1292844d064493502a11392a7c4c9582552b452522d235bf22b7
7
- data.tar.gz: 8c972a697a5bbd5c4654c973b1da378c8e733fb8c1887310b281acd0e36ef5de2e83259da0dfdca75a6da54f3c5af510d7cdc943af338aea839f661663ada014
6
+ metadata.gz: 82804e0c46bc17d30c6bc151a24b3fc415c476aa1159239330611d2b8ef3ef7d2013dc7078bb40e298d29c0e81f3e758f53e82ff280acc0548b37dc78ff7d8be
7
+ data.tar.gz: 6f4c84f64e8728af30a61d56b22dce9eb34fa574984eafe427d48cebdac1446344f1f5dd37370604d58b9df4bb1af587135348ad682e15d3ce97d45cfd769ed1
@@ -4,6 +4,7 @@ rvm:
4
4
  - 2.2.7
5
5
  - 2.3.5
6
6
  - 2.4.2
7
+ - 2.5.0
7
8
 
8
9
  install:
9
10
  - sudo apt-get update -qq
data/README.md CHANGED
@@ -7,7 +7,11 @@
7
7
  PacketGen provides simple ways to generate, send and capture network packets.
8
8
 
9
9
  ## Installation
10
- Via RubyGems:
10
+ PacketGen depends on PcapRub, which needs pcap development files to install. On Debian, you have to do:
11
+
12
+ $ sudo apt install libpcap-dev
13
+
14
+ Installation using RubyGems is then easy:
11
15
 
12
16
  $ gem install packetgen
13
17
 
@@ -103,3 +103,7 @@ require_relative 'header/bootp'
103
103
  require_relative 'header/dhcp'
104
104
  require_relative 'header/http'
105
105
  require_relative 'header/tftp'
106
+ require_relative 'header/igmp'
107
+ require_relative 'header/igmpv3'
108
+ require_relative 'header/mld'
109
+ require_relative 'header/mldv2'
@@ -52,7 +52,6 @@ module PacketGen
52
52
  end
53
53
 
54
54
  self.add_class ICMPv6
55
-
56
55
  IPv6.bind_header ICMPv6, next: ICMPv6::IP_PROTOCOL
57
56
  end
58
57
  end
@@ -0,0 +1,126 @@
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
+
9
+ # This class supports IGMPv2 (RFC 2236).
10
+ #
11
+ # From RFC 2236, a IGMP 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
+ # | Type | Max Resp Time | Checksum |
16
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17
+ # | Group Address |
18
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19
+ #
20
+ # A IGMP header consists of:
21
+ # * a {#type} field ({Types::Int8Enum} type),
22
+ # * a {#max_resp_time} field ({Types::Int8} type),
23
+ # * a {#checksum} field ({Types::Int16} type),
24
+ # * a {#group_addr} field ({Header::IP::Addr} type),
25
+ # * and a {#body} (unused for IGMPv2).
26
+ #
27
+ # == Create a IGMP header
28
+ # # standalone
29
+ # igmp = PacketGen::Header::IGMP.new
30
+ # # in a packet
31
+ # pkt = PacketGen.gen('IP').add('IGMP')
32
+ # # access to IGMP header
33
+ # pkt.igmp # => PacketGen::Header::IGMP
34
+ #
35
+ # == IGMP attributes
36
+ # icmp.type = 'MembershipQuery' # or 0x11
37
+ # icmp.max_resp_time = 20
38
+ # icmp.checksum = 0x248a
39
+ # icmp.group_addr = '224.0.0.1'
40
+ # @author Sylvain Daubert
41
+ class IGMP < Base
42
+
43
+ # IGMP internet protocol number
44
+ IP_PROTOCOL = 2
45
+
46
+ # Known types
47
+ TYPES = {
48
+ 'MembershipQuery' => 0x11,
49
+ 'MembershipReportv1' => 0x12,
50
+ 'MembershipReportv2' => 0x16,
51
+ 'LeaveGroup' => 0x17,
52
+ }
53
+
54
+ # @!attribute type
55
+ # 8-bit IGMP Type
56
+ # @return [Integer]
57
+ define_field :type, Types::Int8Enum, enum: TYPES
58
+ # @!attribute max_resp_time
59
+ # 8-bit IGMP Max Response Time
60
+ # @return [Integer]
61
+ define_field :max_resp_time, Types::Int8
62
+ # @!attribute checksum
63
+ # 16-bit IGMP Checksum
64
+ # @return [Integer]
65
+ define_field :checksum, Types::Int16
66
+ # @!attribute group_addr
67
+ # IP Group address
68
+ # @return [IP::Addr]
69
+ define_field :group_addr, IP::Addr, default: '0.0.0.0'
70
+ # @!attribute body
71
+ # @return [String,Base]
72
+ define_field :body, Types::String
73
+
74
+ # @api private
75
+ # @note This method is used internally by PacketGen and should not be
76
+ # directly called
77
+ def added_to_packet(packet)
78
+ igmp_idx = packet.headers.size
79
+ packet.instance_eval "def igmpize() @headers[#{igmp_idx}].igmpize; end"
80
+ end
81
+
82
+ # Get human readbale type
83
+ # @return [String]
84
+ def human_type
85
+ self[:type].to_human
86
+ end
87
+
88
+ # Compute checksum and set +checksum+ field
89
+ # @return [Integer]
90
+ def calc_checksum
91
+ sum = (type << 8) | max_resp_time
92
+
93
+ payload = self[:group_addr].to_s + body.to_s
94
+ payload << "\x00" unless payload.size % 2 == 0
95
+ payload.unpack('n*').each { |x| sum += x; }
96
+
97
+ while sum > 0xffff do
98
+ sum = (sum & 0xffff) + (sum >> 16)
99
+ end
100
+ sum = ~sum & 0xffff
101
+ self[:checksum].value = (sum == 0) ? 0xffff : sum
102
+ end
103
+
104
+ # Fixup IP header according to RFC 2236:
105
+ # * set TTL to 1,
106
+ # * add Router Alert option,
107
+ # * recalculate checksum and length.
108
+ # This method may be called as:
109
+ # # first method
110
+ # pkt.igmp.igmpize
111
+ # # second method
112
+ # pkt.igmpize
113
+ # @return [void]
114
+ def igmpize
115
+ iph = ip_header(self)
116
+ iph.ttl = 1
117
+ iph.options << IP::RA.new
118
+ packet.calc
119
+ end
120
+ end
121
+
122
+ self.add_class IGMP
123
+ IP.bind_header IGMP, op: :and, protocol: IGMP::IP_PROTOCOL, frag: 0, ttl: 1,
124
+ tos: ->(v) { v.nil? ? 0 : v != 0xc0 }
125
+ end
126
+ end
@@ -0,0 +1,150 @@
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
+
9
+ # This class supports IGMPv3 (RFC3376).
10
+ #
11
+ # From RFC 3376, a IGMP 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
+ # | Type | Max Resp Code | Checksum |
16
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17
+ #
18
+ # A IGMP header consists of:
19
+ # * a {#type} field ({Types::Int8Enum} type),
20
+ # * a {#max_resp_time} field ({Types::Int8} type),
21
+ # * a {#checksum} field ({Types::Int16} type),
22
+ # * and a {#body}, containing more fields (see below).
23
+ #
24
+ # A IGMPv3 header may have additionnal fields. These fields are handled by
25
+ # additional headers (see {IGMPv3::MQ}).
26
+ #
27
+ # == Create a IGMPv3 header
28
+ # # standalone
29
+ # igmp = PacketGen::Header::IGMPv3.new
30
+ # # in a packet
31
+ # pkt = PacketGen.gen('IP').add('IGMPv3')
32
+ # # access to IGMPv3 header
33
+ # pkt.igmp # => PacketGen::Header::IGMPv3
34
+ #
35
+ # == IGMPv3 attributes
36
+ # igmp.type = 'MembershipQuery' # or 0x11
37
+ # igmp.max_resp_time = 20
38
+ # igmp.checksum = 0x248a
39
+ #
40
+ # == IGMPv3 specifics
41
+ # === Max Resp Code
42
+ # {#max_resp_code} field of IGMPv3 packets is encoded differently than
43
+ # previous versions. This encoding permits to set value up to 31743 (instead
44
+ # of 255 for IGMPv2).
45
+ #
46
+ # This encoding is handled by {#max_resp_code} accessors:
47
+ # igmp.max_resp_code = 10000
48
+ # igmp.max_resp_code #=> 9728 error due to encoding as a floating point value
49
+ #
50
+ # === IGMPv3 Membership Query
51
+ # With IGMPv3, a Membership Query packet has more fields than with IGMPv2. To
52
+ # handle those fields, an additional header should be used:
53
+ # pkt = PacketGen.gen('IP').add('IGMPv3', type: 'MembershipQuery').add('IGMPv3::MQ')
54
+ # pkt.igmpv3 #=> PacketGen::Header::IGMPv3
55
+ # pkt.igmpv3_mq #=> PacketGen::Header::IGMPv3::MQ
56
+ #
57
+ # === IGMPv3 Membership Report
58
+ # With IGMPv3, a Membership Report packet has more fields than with IGMPv2. To
59
+ # handle those fields, an additional header should be used:
60
+ # pkt = PacketGen.gen('IP').add('IGMPv3', type: 'MembershipQuery').add('IGMPv3::MR')
61
+ # pkt.igmpv3 #=> PacketGen::Header::IGMPv3
62
+ # pkt.igmpv3_mr #=> PacketGen::Header::IGMPv3::MR
63
+ # @author Sylvain Daubert
64
+ class IGMPv3 < IGMP
65
+
66
+ # Known types
67
+ TYPES = {
68
+ 'MembershipQuery' => 0x11,
69
+ 'MembershipReport' => 0x22,
70
+ }
71
+
72
+ delete_field :group_addr
73
+ #undef group_addr
74
+ #undef group_addr=
75
+
76
+ # Encode value for IGMPv3 Max Resp Code and QQIC.
77
+ # Value may be encoded as a float, so some error may occur.
78
+ # See RFC 3376 §4.1.1 and §4.1.7.
79
+ # @param [Integer] value
80
+ # @return [Integer]
81
+ def self.encode(value)
82
+ if value < 128
83
+ value
84
+ elsif value > 31743
85
+ 255
86
+ else
87
+ exp = 0
88
+ value >>= 3
89
+ while value > 31 do
90
+ exp += 1
91
+ value >>= 1
92
+ end
93
+ 0x80 | (exp << 4) | (value & 0xf)
94
+ end
95
+ end
96
+
97
+ # Decode value for IGMPv3 Max Resp Code and QQIC.
98
+ # See RFC 3376 §4.1.1 and §4.1.7.
99
+ # @param [Integer] value
100
+ # @return [Integer]
101
+ def self.decode(value)
102
+ if value < 128
103
+ value
104
+ else
105
+ mant = value & 0xf
106
+ exp = (value >> 4) & 0x7
107
+ (0x10 | mant) << (exp + 3)
108
+ end
109
+ end
110
+
111
+ # Getter for +max_resp_time+ for IGMPv3 packets. Use {.decode}.
112
+ # @return [Integer]
113
+ def max_resp_time
114
+ IGMPv3.decode(self[:max_resp_time].value || self[:max_resp_time].default)
115
+ end
116
+ alias max_resp_code max_resp_time
117
+
118
+ # Setter for +max_resp_time+ for IGMPv3 packets. Use {.encode}.
119
+ # @param [Integer] value
120
+ # @return [Integer]
121
+ def max_resp_time=(value)
122
+ self[:max_resp_time].value = IGMPv3.encode(value)
123
+ end
124
+ alias max_resp_code= max_resp_time=
125
+
126
+ # Compute checksum and set +checksum+ field
127
+ # @return [Integer]
128
+ def calc_checksum
129
+ sum = (type << 8) | max_resp_code
130
+
131
+ payload = body.to_s
132
+ payload << "\x00" unless payload.size % 2 == 0
133
+ payload.unpack('n*').each { |x| sum += x; }
134
+
135
+ while sum > 0xffff do
136
+ sum = (sum & 0xffff) + (sum >> 16)
137
+ end
138
+ sum = ~sum & 0xffff
139
+ self[:checksum].value = (sum == 0) ? 0xffff : sum
140
+ end
141
+ end
142
+
143
+ self.add_class IGMPv3
144
+ IP.bind_header IGMPv3, op: :and, protocol: IGMPv3::IP_PROTOCOL, frag: 0, ttl: 1,
145
+ tos: 0xc0
146
+ end
147
+ end
148
+
149
+ require_relative 'igmpv3/mq'
150
+ require_relative 'igmpv3/mr'
@@ -0,0 +1,98 @@
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 IGMPv3
9
+ # Class to handle IGMPv3 Group Records.
10
+ #
11
+ # A Group Record is a block of fields containing information
12
+ # pertaining to the sender's membership in a single multicast group on
13
+ # the interface from which the Report is sent.
14
+ #
15
+ # A Group Record has the following format:
16
+ # 0 1 2 3
17
+ # 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
18
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19
+ # | Record Type | Aux Data Len | Number of Sources (N) |
20
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21
+ # | Multicast Address |
22
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23
+ # | Source Address [1] |
24
+ # +- -+
25
+ # | Source Address [2] |
26
+ # +- -+
27
+ # . . .
28
+ # . . .
29
+ # . . .
30
+ # +- -+
31
+ # | Source Address [N] |
32
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33
+ # | |
34
+ # . .
35
+ # . Auxiliary Data .
36
+ # . .
37
+ # | |
38
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39
+ # @author Sylvain Daubert
40
+ class GroupRecord < Types::Fields
41
+
42
+ # Known record types
43
+ RECORD_TYPES = {
44
+ 'MODE_IS_INCLUDE' => 1,
45
+ 'MODE_IS_EXCLUDE' => 2,
46
+ 'CHANGE_TO_INCLUDE_MODE' => 3,
47
+ 'CHANGE_TO_EXCLUDE_MODE' => 4,
48
+ 'ALLOW_NEW_SOURCES' => 5,
49
+ 'BLOCK_OLD_SOURCES' => 6
50
+ }
51
+
52
+ # @!attribute type
53
+ # 8-bit record type
54
+ # @return [Integer]
55
+ define_field :type, Types::Int8Enum, enum: RECORD_TYPES
56
+ # @!attribute aux_data_len
57
+ # 8-bit length of of the Auxiliary Data field ({#aux_data}), in unit of
58
+ # 32-bit words
59
+ # @return [Integer]
60
+ define_field :aux_data_len, Types::Int8, default: 0
61
+ # @!attribute number_of_sources
62
+ # 16-bit Number of source addresses in {#source_addr}
63
+ # @return [Integer]
64
+ define_field :number_of_sources, Types::Int16, default: 0
65
+ # @!attribute multicast_addr
66
+ # IP multicast address to which this Group Record pertains
67
+ # @return [IP::Addr]
68
+ define_field :multicast_addr, IP::Addr, default: '0.0.0.0'
69
+ # @!attribute source_addr
70
+ # Array of source addresses
71
+ # @return [IP::ArrayOfAddr]
72
+ define_field :source_addr, IP::ArrayOfAddr,
73
+ builder: ->(h, t) { t.new(counter: h[:number_of_sources]) }
74
+ # @!attribute aux_data
75
+ # @return [String]
76
+ define_field :aux_data, Types::String,
77
+ builder: ->(h, t) { t.new(length_from: ->() { h[:aux_data_len].to_i * 4 }) }
78
+
79
+ def human_type
80
+ self[:type].to_human
81
+ end
82
+
83
+ def to_human
84
+ "#{human_type}(ma:#{multicast_addr}|src:#{source_addr.to_human})"
85
+ end
86
+ end
87
+
88
+ # Class to handle series of {GroupRecord}.
89
+ # @author Sylvain Daubert
90
+ class GroupRecords < Types::Array
91
+ set_of GroupRecord
92
+
93
+ # Separator used in {#to_human}.
94
+ HUMAN_SEPARATOR = ';'
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,96 @@
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 IGMPv3
9
+ # IGMPv3 Membership Query.
10
+ #
11
+ # This is a subpayload for IGMPv3 packets only. This kind of payload is
12
+ # sent by IP multicast routers to query the multicast reception state of
13
+ # neighboring interfaces. Queries has following format:
14
+ # 0 1 2 3
15
+ # 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
16
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17
+ # | Group Address |
18
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19
+ # | Resv |S| QRV | QQIC | Number of Sources (N) |
20
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21
+ # | Source Address [1] |
22
+ # +- -+
23
+ # | Source Address [2] |
24
+ # +- . -+
25
+ # . . .
26
+ # . . .
27
+ # +- -+
28
+ # | Source Address [N] |
29
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30
+ #
31
+ # Fields are:
32
+ # * 32-bit {#group_addr} field ({Header::IP::Addr} type),
33
+ # * 4-bit {#resv}, a reserved field,
34
+ # * 1-bit {#flag_s} (Suppress Router-Side Processing),
35
+ # * 3-bit {#qrv} (Querier's Robustness Variable),
36
+ # * 8-bit {#qqic} (Querier's Query Interval Code),
37
+ # * 16-bit {#number_of_sources},
38
+ # * {#source_addr} field, a {IP::ArrayOfAddr} to handle sources addresses.
39
+ # @author Sylvain Daubert
40
+ class MQ < Base
41
+ # @!attribute group_addr
42
+ # IP Group address
43
+ # @return [IP::Addr]
44
+ define_field :group_addr, IP::Addr, default: '0.0.0.0'
45
+ # @!attribute u8
46
+ # First 8-bit field, composed of {#resv}, {#flag_s} and {#qrv}
47
+ # @return [Integer]
48
+ define_field :u8, Types::Int8
49
+ # @!attribute qqic
50
+ # 8-bit QQIC
51
+ # @return [Integer,Float]
52
+ define_field :qqic, Types::Int8
53
+ # @!attribute number_of_sources
54
+ # 16-bit Number of Sources in {#source_addr}
55
+ # @return [Integer]
56
+ define_field :number_of_sources, Types::Int16
57
+
58
+ # @!attribute source_addr
59
+ # Array of IP source addresses
60
+ # @return [IP::ArrayOfAddr]
61
+ define_field :source_addr, IP::ArrayOfAddr,
62
+ builder: ->(h,t) { t.new(counter: h[:number_of_sources]) }
63
+
64
+ # @!attribute resv
65
+ # 4-bit reserved field in
66
+ # @return [Integer]
67
+ # @!attribute flag_s
68
+ # 1-bit S flag (Suppress Router-Side Processing)
69
+ # @return [Boolean]
70
+ # @!attribute qrv
71
+ # 3-bit Querier's Robustness Variable
72
+ # @return [Integer]
73
+ define_bit_fields_on :u8, :resv, 4, :flag_s, :qrv, 3
74
+
75
+ # Get QQIC value
76
+ # @note May return a different value from value previously set, as a
77
+ # float encoding is used to encode big values. See {IGMPv3.decode}.
78
+ # @return [Integer]
79
+ def qqic
80
+ IGMPv3.decode self[:qqic].to_i
81
+ end
82
+
83
+ # Set QQIC value
84
+ # @note See {IGMPv3.encode}.
85
+ # @param [Integer] value
86
+ # @return [Integer]
87
+ def qqic=(value)
88
+ self[:qqic].value = IGMPv3.encode(value)
89
+ end
90
+ end
91
+ end
92
+
93
+ self.add_class IGMPv3::MQ
94
+ IGMPv3.bind_header IGMPv3::MQ, type: 0x11
95
+ end
96
+ end