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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d08bf3611fcb1af6c5e794d4582fe373b4887c02
|
4
|
+
data.tar.gz: 7149de75ef5a5ebaca9d9b90239ffce8619c38c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82804e0c46bc17d30c6bc151a24b3fc415c476aa1159239330611d2b8ef3ef7d2013dc7078bb40e298d29c0e81f3e758f53e82ff280acc0548b37dc78ff7d8be
|
7
|
+
data.tar.gz: 6f4c84f64e8728af30a61d56b22dce9eb34fa574984eafe427d48cebdac1446344f1f5dd37370604d58b9df4bb1af587135348ad682e15d3ce97d45cfd769ed1
|
data/.travis.yml
CHANGED
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
|
-
|
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
|
|
data/lib/packetgen/header.rb
CHANGED
@@ -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'
|
@@ -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
|