nl 0.1.0 → 0.2.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/CHANGELOG.md +4 -0
- data/Rakefile +0 -1
- data/lib/nl/encoder.rb +2 -2
- data/lib/nl/endian.rb +5 -3
- data/lib/nl/family.rb +14 -140
- data/lib/nl/genl.rb +33 -11
- data/lib/nl/protocols/genl.rb +46 -6
- data/lib/nl/protocols/raw.rb +239 -13
- data/lib/nl/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 52aff4b073cd86a4ce19bb2feee6f92de27bf253869e4f6f4bea673c90d7dade
|
|
4
|
+
data.tar.gz: 50fef7586bbdd1b050a20303fb106a29510fab9c6ea7df80631e1a727a36ea10
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bfb9af6ebc834dfd1e098220d74b0064c1bfc9fe38ecea9972cfe1dac6daae9058d046e8bfcb5a32ff2ca8da62a73e64e13b0efbe94627f9eb79f1f2f72f8045
|
|
7
|
+
data.tar.gz: 34584295bf54077889280d22be9937382914121e78607bbfe82649a8785e469f96d96136bbd41fa7a14878391bae780db71b0dc24f0c514759b92e3f69598ccc
|
data/CHANGELOG.md
CHANGED
data/Rakefile
CHANGED
data/lib/nl/encoder.rb
CHANGED
|
@@ -16,12 +16,12 @@ module Nl
|
|
|
16
16
|
|
|
17
17
|
def put_string(value)
|
|
18
18
|
reserve(value.bytesize)
|
|
19
|
-
@position
|
|
19
|
+
@position += @buffer.set_string(value, @position)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def put_zstring(value)
|
|
23
23
|
reserve(value.bytesize + 1)
|
|
24
|
-
@position
|
|
24
|
+
@position += @buffer.set_string(value, @position)
|
|
25
25
|
@position = @buffer.set_value(:U8, @position, 0)
|
|
26
26
|
end
|
|
27
27
|
|
data/lib/nl/endian.rb
CHANGED
|
@@ -20,16 +20,18 @@ module Nl
|
|
|
20
20
|
when 4; [U32, S32]
|
|
21
21
|
when 8; [U64, S64]
|
|
22
22
|
else raise "Unsupported 'int' size"
|
|
23
|
-
|
|
23
|
+
end
|
|
24
24
|
ULONG, SLONG = case SIZEOF_LONG
|
|
25
25
|
when 4; [U32, S32]
|
|
26
26
|
when 8; [U64, S64]
|
|
27
27
|
else raise "Unsupported 'long' size"
|
|
28
|
-
|
|
28
|
+
end
|
|
29
29
|
ULLONG, SLLONG = case SIZEOF_LLONG
|
|
30
30
|
when 8; [U64, S64]
|
|
31
31
|
else raise "Unsupported 'long long' size"
|
|
32
|
-
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
INT, LONG, LLONG = SINT, SLONG, SLLONG
|
|
33
35
|
end
|
|
34
36
|
end
|
|
35
37
|
end
|
data/lib/nl/family.rb
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# rbs_inline: enabled
|
|
1
3
|
require_relative 'socket'
|
|
2
4
|
|
|
3
5
|
module Nl
|
|
4
6
|
class Family
|
|
5
|
-
|
|
7
|
+
#--
|
|
8
|
+
# @rbs socket: Socket
|
|
9
|
+
# @rbs protocol: Protocol
|
|
10
|
+
# @rbs return: instance
|
|
11
|
+
def initialize(socket, protocol: self.class::PROTOCOL)
|
|
6
12
|
@socket = socket
|
|
13
|
+
@protocol = protocol
|
|
7
14
|
end
|
|
8
15
|
|
|
16
|
+
#--
|
|
17
|
+
# @rbs () -> instance
|
|
18
|
+
# | [R] () { (instance) -> R } -> R
|
|
9
19
|
def self.open
|
|
10
20
|
begin
|
|
11
|
-
socket = Socket.new(self::
|
|
21
|
+
socket = Socket.new(self::PROTOCOL.protonum)
|
|
12
22
|
socket.bind(Socket.sockaddr_nl(0, 0))
|
|
13
23
|
if block_given?
|
|
14
24
|
yield new(socket)
|
|
@@ -20,144 +30,8 @@ module Nl
|
|
|
20
30
|
end
|
|
21
31
|
end
|
|
22
32
|
|
|
23
|
-
def exchange_message(type, request_class, reply_class, args)
|
|
24
|
-
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
class Message
|
|
28
|
-
attr_accessor :header, :fixed_header, :attributes
|
|
29
|
-
|
|
30
|
-
def initialize(header, fixed_header = nil, attributes = [])
|
|
31
|
-
@header = header
|
|
32
|
-
@fixed_header = fixed_header
|
|
33
|
-
@attributes = attributes
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def self.from_params(params)
|
|
37
|
-
header_params = params.slice(*self::HEADER_PARAMS)
|
|
38
|
-
attribute_params = params.slice(*self::ATTRIBUTE_PARAMS)
|
|
39
|
-
# TODO: Reject unknown params
|
|
40
|
-
|
|
41
|
-
header = Core::NlMsgHdr.new(0, self::TYPE, nil, nil, nil)
|
|
42
|
-
fixed_header = self::FIXED_HEADER&.new(**header_params)
|
|
43
|
-
attributes = self::ATTRIBUTE_SET.build_attributes(**attribute_params)
|
|
44
|
-
new(header, fixed_header, attributes)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def append_attribute(attribute)
|
|
48
|
-
@attributes << attribute
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def encode(encoder)
|
|
52
|
-
validate!
|
|
53
|
-
|
|
54
|
-
encoder.measure(Endian::Host::U16) do
|
|
55
|
-
@header.encode(encoder)
|
|
56
|
-
@fixed_header.encode(encoder) if @fixed_header
|
|
57
|
-
@attributes.each do |attr|
|
|
58
|
-
attr.encode(encoder)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def self.decode(decoder, header)
|
|
64
|
-
unless self::TYPE == header.type
|
|
65
|
-
raise "Expected message type #{self::TYPE}, got #{header.type}"
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
if fixed_header_class = self::FIXED_HEADER
|
|
69
|
-
fixed_header = fixed_header_class.decode(decoder)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
attributes = self::ATTRIBUTE_SET.decode(decoder)
|
|
73
|
-
|
|
74
|
-
new(header, fixed_header, attributes).tap(&:validate!)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def validate!
|
|
78
|
-
unless self.class::TYPE == header.type
|
|
79
|
-
raise "Expected message type #{self.class::FIXED_HEADER}, got #{header.type}"
|
|
80
|
-
end
|
|
81
|
-
# TODO: Validate fixed header and attributes
|
|
82
|
-
# @fixed_header&.validate!
|
|
83
|
-
# @attributes.each(&:validate!)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def to_h
|
|
87
|
-
to_h_rec(@attributes, @fixed_header&.to_h || {})
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# FIXME:
|
|
91
|
-
private def to_h_rec(attributes, init = {})
|
|
92
|
-
attributes.each_with_object(init) do |attr, h|
|
|
93
|
-
if attr.class::DATATYPE.is_a?(Protocols::Raw::DataTypes::NestedAttributes)
|
|
94
|
-
h[attr.class::NAME] = to_h_rec(attr.value)
|
|
95
|
-
else
|
|
96
|
-
h[attr.class::NAME] = attr.value
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
class AttributeSet
|
|
103
|
-
Attribute = Struct.new(:value)
|
|
104
|
-
class Attribute
|
|
105
|
-
def self.decode(decoder)
|
|
106
|
-
value = self::DATATYPE.decode(decoder)
|
|
107
|
-
new(value)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def encode(encoder)
|
|
111
|
-
self.class::DATATYPE.encode(encoder, self.value)
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
class << self
|
|
116
|
-
private def decode1(decoder)
|
|
117
|
-
nlattr = Core::NlAttr.decode(decoder)
|
|
118
|
-
attr = decoder.limit(nlattr.len - Core::NLA_HDRLEN) do
|
|
119
|
-
if attr_class = self::BY_TYPE[nlattr.type & Core::NLA_TYPE_MASK]
|
|
120
|
-
attr_class.decode(decoder)
|
|
121
|
-
else
|
|
122
|
-
decoder.skip
|
|
123
|
-
nil
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
decoder.align_to(Core::NLA_ALIGNTO)
|
|
127
|
-
attr
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def decode(decoder)
|
|
131
|
-
attrs = []
|
|
132
|
-
while decoder.available?
|
|
133
|
-
attr = decode1(decoder)
|
|
134
|
-
attrs << attr
|
|
135
|
-
end
|
|
136
|
-
attrs.compact
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
private def encode1(encoder, attr)
|
|
140
|
-
nlattr = Core::NlAttr.new(attr.class::TYPE, 0)
|
|
141
|
-
encoder.measure(Endian::Host::U16) do
|
|
142
|
-
nlattr.encode(encoder)
|
|
143
|
-
attr.encode(encoder)
|
|
144
|
-
end
|
|
145
|
-
encoder.align_to(Core::NLA_ALIGNTO)
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
def encode(encoder, attrs)
|
|
149
|
-
attrs.each do |attr|
|
|
150
|
-
encode1(encoder, attr)
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def build_attributes(**params)
|
|
155
|
-
params.map do |name, value|
|
|
156
|
-
attr_class = self::BY_NAME[name] or raise "Unknown attribute #{name}"
|
|
157
|
-
attr_class.new(value)
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
end
|
|
33
|
+
private def exchange_message(type, request_class, reply_class, args, &block)
|
|
34
|
+
@protocol.exchange_message(@socket, type, request_class, reply_class, args, &block)
|
|
161
35
|
end
|
|
162
36
|
end
|
|
163
37
|
end
|
data/lib/nl/genl.rb
CHANGED
|
@@ -48,18 +48,40 @@ module Nl
|
|
|
48
48
|
end
|
|
49
49
|
include Constants
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
class Connection
|
|
52
|
+
def self.open(resolver:)
|
|
53
|
+
conn = new(resolver:)
|
|
54
|
+
if block_given?
|
|
55
|
+
begin
|
|
56
|
+
yield conn
|
|
57
|
+
ensure
|
|
58
|
+
conn.close
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
conn
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def initialize(resolver:)
|
|
66
|
+
@socket = Socket.new(Core::NETLINK_GENERIC)
|
|
67
|
+
@socket.bind(Socket.sockaddr_nl(0, 0))
|
|
68
|
+
@resolver = resolver
|
|
69
|
+
@id_cache = {}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def open(family_class)
|
|
73
|
+
proto = family_class::PROTOCOL
|
|
74
|
+
id = @id_cache[proto.name] ||= begin
|
|
75
|
+
proto.family_id
|
|
76
|
+
rescue NotImplementedError
|
|
77
|
+
@resolver.call(@socket, proto.name)
|
|
78
|
+
end
|
|
79
|
+
family_class.new(@socket, protocol: Protocols::Genl.new(proto.name, family_id: id))
|
|
80
|
+
end
|
|
59
81
|
|
|
60
|
-
def
|
|
61
|
-
|
|
82
|
+
def close
|
|
83
|
+
@socket.close
|
|
62
84
|
end
|
|
63
85
|
end
|
|
64
86
|
end
|
|
65
|
-
end
|
|
87
|
+
end
|
data/lib/nl/protocols/genl.rb
CHANGED
|
@@ -1,14 +1,54 @@
|
|
|
1
1
|
module Nl
|
|
2
2
|
module Protocols
|
|
3
3
|
# The Generic Netlink protocol
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
class Genl < Raw
|
|
5
|
+
GenlMsgHdr = ::Struct.new(:cmd, :version, :reserved)
|
|
6
|
+
class GenlMsgHdr
|
|
7
|
+
FORMAT = Ractor.make_shareable([
|
|
8
|
+
Endian::Host::U8,
|
|
9
|
+
Endian::Host::U8,
|
|
10
|
+
Endian::Host::U16,
|
|
11
|
+
])
|
|
12
|
+
|
|
13
|
+
def self.decode(decoder)
|
|
14
|
+
new(*decoder.get_values(FORMAT))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def encode(encoder)
|
|
18
|
+
encoder.put_values(FORMAT, to_a)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(name, family_id: nil)
|
|
23
|
+
super(name, Core::NETLINK_GENERIC)
|
|
24
|
+
@family_id = family_id || default_family_id(name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def family_id
|
|
28
|
+
@family_id or raise NotImplementedError, "Genetlink family ID for '#{name}' must be resolved via nlctrl"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def encode_message(encoder, message)
|
|
32
|
+
cmd = message.nlmsg_header.type
|
|
33
|
+
encoder.measure(Endian::Host::U16) do
|
|
34
|
+
message.nlmsg_header.type = family_id
|
|
35
|
+
message.nlmsg_header.encode(encoder)
|
|
36
|
+
message.nlmsg_header.type = cmd
|
|
37
|
+
GenlMsgHdr.new(cmd, 1, 0).encode(encoder)
|
|
38
|
+
message.fixed_header&.encode(encoder)
|
|
39
|
+
message.attributes.encode(encoder)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class Message < Raw::Message
|
|
44
|
+
def self.decode(decoder, header)
|
|
45
|
+
genlhdr = GenlMsgHdr.decode(decoder)
|
|
46
|
+
super(decoder, header, type: genlhdr.cmd)
|
|
47
|
+
end
|
|
7
48
|
end
|
|
8
49
|
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
genlmsg = GenlMsgHdr.parse(nlmsg.data, 0)
|
|
50
|
+
private def default_family_id(name)
|
|
51
|
+
Nl::Genl::GENL_ID_CTRL if name == 'nlctrl'
|
|
12
52
|
end
|
|
13
53
|
end
|
|
14
54
|
end
|
data/lib/nl/protocols/raw.rb
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require_relative '../encoder'
|
|
2
|
+
require_relative '../decoder'
|
|
3
|
+
|
|
1
4
|
module Nl
|
|
2
5
|
module Protocols
|
|
3
6
|
# The raw Netlink protocol
|
|
@@ -26,7 +29,7 @@ module Nl
|
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
def send_message(socket, message)
|
|
29
|
-
seq_pid = socket.complete(message.
|
|
32
|
+
seq_pid = socket.complete(message.nlmsg_header)
|
|
30
33
|
encoder = Encoder.new
|
|
31
34
|
encode_message(encoder, message)
|
|
32
35
|
socket.sendmsg(encoder.buffer.get_string, 0, Socket.sockaddr_nl(0, 0))
|
|
@@ -75,34 +78,187 @@ module Nl
|
|
|
75
78
|
# @param reply_class [Class] Reply message class
|
|
76
79
|
# @param args [Hash] Request arguments
|
|
77
80
|
def exchange_message(socket, type, request_class, reply_class, args)
|
|
78
|
-
flags = Core::NLM_F_REQUEST
|
|
79
|
-
flags |=
|
|
81
|
+
flags = Core::NLM_F_REQUEST
|
|
82
|
+
flags |= type == :dump ? Core::NLM_F_DUMP : Core::NLM_F_ACK
|
|
80
83
|
|
|
81
84
|
request = request_class.from_params(args)
|
|
82
|
-
request.
|
|
85
|
+
request.nlmsg_header.flags = flags
|
|
83
86
|
seq_pid = send_message(socket, request)
|
|
84
87
|
|
|
85
|
-
result = []
|
|
88
|
+
result = [] unless block_given?
|
|
86
89
|
|
|
87
90
|
done = false
|
|
88
|
-
acked = false
|
|
89
91
|
begin
|
|
90
92
|
recv_message(socket, seq_pid, reply_class) do |message|
|
|
91
93
|
case message
|
|
92
|
-
when Done
|
|
94
|
+
when Done, Ack
|
|
93
95
|
done = true
|
|
94
96
|
when Exception
|
|
95
|
-
raise
|
|
96
|
-
when Ack
|
|
97
|
-
acked = true
|
|
97
|
+
raise message
|
|
98
98
|
else
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
if block_given?
|
|
100
|
+
yield message
|
|
101
|
+
else
|
|
102
|
+
result << message
|
|
103
|
+
end
|
|
101
104
|
end
|
|
102
105
|
end
|
|
103
106
|
end until done
|
|
104
107
|
|
|
105
|
-
|
|
108
|
+
unless block_given?
|
|
109
|
+
type == :dump ? result : result.first
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class AttributeSet
|
|
114
|
+
Attribute = Struct.new(:value)
|
|
115
|
+
class Attribute
|
|
116
|
+
def self.decode(decoder)
|
|
117
|
+
value = self::DATATYPE.decode(decoder)
|
|
118
|
+
new(value)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def encode(encoder)
|
|
122
|
+
self.class::DATATYPE.encode(encoder, self.value)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def initialize(attributes)
|
|
127
|
+
attr_class = self.class::Attribute
|
|
128
|
+
|
|
129
|
+
attributes.each do |attr|
|
|
130
|
+
unless attr.kind_of?(attr_class)
|
|
131
|
+
raise TypeError, "attribute must be an instance of #{attr_class}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
@attributes = Array(attributes)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def [](type)
|
|
139
|
+
case type
|
|
140
|
+
when Symbol
|
|
141
|
+
attr_class = self.class.by_name(type)
|
|
142
|
+
when Integer
|
|
143
|
+
attr_class = self.class.by_type(type)
|
|
144
|
+
else
|
|
145
|
+
raise TypeError, "attribute type must be a Symbol or an Integer"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# TODO: multi-attr
|
|
149
|
+
@attributes.find { it.kind_of?(attr_class) } rescue binding.irb
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def <<(attr)
|
|
153
|
+
attr_class = self.class::Attribute
|
|
154
|
+
unless attr.kind_of?(attr_class)
|
|
155
|
+
raise TypeError, "attribute must be an instance of #{attr_class}"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
@attributes << attr
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
private def encode1(encoder, attr)
|
|
162
|
+
nlattr = Core::NlAttr.new(0, attr.class::TYPE)
|
|
163
|
+
encoder.measure(Endian::Host::U16) do
|
|
164
|
+
nlattr.encode(encoder)
|
|
165
|
+
attr.encode(encoder)
|
|
166
|
+
end
|
|
167
|
+
encoder.align_to(Core::NLA_ALIGNTO)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def encode(encoder)
|
|
171
|
+
@attributes.each do |attr|
|
|
172
|
+
encode1(encoder, attr)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
class << self
|
|
177
|
+
private def decode1(decoder)
|
|
178
|
+
nlattr = Core::NlAttr.decode(decoder)
|
|
179
|
+
attr = decoder.limit(nlattr.len - Core::NLA_HDRLEN) do
|
|
180
|
+
if attr_class = self::BY_TYPE[nlattr.type & Core::NLA_TYPE_MASK]
|
|
181
|
+
attr_class.decode(decoder)
|
|
182
|
+
else
|
|
183
|
+
decoder.skip
|
|
184
|
+
nil
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
decoder.align_to(Core::NLA_ALIGNTO)
|
|
188
|
+
attr
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def decode(decoder)
|
|
192
|
+
attrs = []
|
|
193
|
+
while decoder.available?
|
|
194
|
+
attr = decode1(decoder)
|
|
195
|
+
attrs << attr
|
|
196
|
+
end
|
|
197
|
+
new(attrs.compact)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def build_attributes(**params)
|
|
201
|
+
attrs = params.map do |name, value|
|
|
202
|
+
attr_class = self::BY_NAME[name] or raise "Unknown attribute #{name}"
|
|
203
|
+
attr_class.new(value)
|
|
204
|
+
end
|
|
205
|
+
new(attrs)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
class Message
|
|
211
|
+
attr_accessor :nlmsg_header, :fixed_header, :attributes
|
|
212
|
+
|
|
213
|
+
def initialize(header, fixed_header = nil, attributes = self.class::ATTRIBUTE_SET.new)
|
|
214
|
+
@nlmsg_header = header
|
|
215
|
+
@fixed_header = fixed_header
|
|
216
|
+
@attributes = attributes
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def self.from_params(params)
|
|
220
|
+
if self::FIXED_HEADER
|
|
221
|
+
header_params = params.slice(*self::FIXED_HEADER.members)
|
|
222
|
+
fixed_header = self::FIXED_HEADER.new(**header_params)
|
|
223
|
+
end
|
|
224
|
+
attribute_params = params.slice(*self::ATTRIBUTES)
|
|
225
|
+
attributes = self::ATTRIBUTE_SET.build_attributes(**attribute_params)
|
|
226
|
+
|
|
227
|
+
unknown = params.keys - attribute_params.keys
|
|
228
|
+
unknown -= header_params.keys if header_params
|
|
229
|
+
unless unknown.empty?
|
|
230
|
+
raise ArgumentError, "unknown parameters: #{unknown.join(', ')}"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
header = Core::NlMsgHdr.new(0, self::TYPE, nil, nil, nil)
|
|
234
|
+
new(header, fixed_header, attributes)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def append_attribute(attribute)
|
|
238
|
+
@attributes << attribute
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def encode(encoder)
|
|
242
|
+
encoder.measure(Endian::Host::U16) do
|
|
243
|
+
@nlmsg_header.encode(encoder)
|
|
244
|
+
@fixed_header&.encode(encoder)
|
|
245
|
+
@attributes.encode(encoder)
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def self.decode(decoder, header, type: header.type)
|
|
250
|
+
unless self::TYPE == type
|
|
251
|
+
raise "Expected message type #{self::TYPE}, got #{type}"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
if fixed_header_class = self::FIXED_HEADER
|
|
255
|
+
fixed_header = fixed_header_class.decode(decoder)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
attributes = self::ATTRIBUTE_SET.decode(decoder)
|
|
259
|
+
|
|
260
|
+
new(header, fixed_header, attributes)
|
|
261
|
+
end
|
|
106
262
|
end
|
|
107
263
|
|
|
108
264
|
module DataTypes
|
|
@@ -150,6 +306,46 @@ module Nl
|
|
|
150
306
|
end
|
|
151
307
|
end
|
|
152
308
|
|
|
309
|
+
class Flag
|
|
310
|
+
def encode(encoder, value)
|
|
311
|
+
# flag attribute has no payload; presence encodes true
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def decode(decoder)
|
|
315
|
+
true
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# A 32-bit value paired with a selector mask (8 bytes total: value u32 + selector u32)
|
|
320
|
+
class Bitfield32
|
|
321
|
+
def encode(encoder, value)
|
|
322
|
+
v, selector = value.is_a?(Array) ? value : [value, 0xFFFFFFFF]
|
|
323
|
+
encoder.put_value(Endian::Host::U32, v)
|
|
324
|
+
encoder.put_value(Endian::Host::U32, selector)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def decode(decoder)
|
|
328
|
+
value = decoder.get_value(Endian::Host::U32)
|
|
329
|
+
selector = decoder.get_value(Endian::Host::U32)
|
|
330
|
+
[value, selector]
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
class Pad
|
|
335
|
+
def initialize(length = nil)
|
|
336
|
+
@length = length
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def encode(encoder, _value)
|
|
340
|
+
encoder.put_string(?\0.b * @length) if @length
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def decode(decoder)
|
|
344
|
+
@length ? decoder.skip(@length) : decoder.skip
|
|
345
|
+
nil
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
153
349
|
class NestedAttributes
|
|
154
350
|
def initialize(attribute_set)
|
|
155
351
|
@attribute_set = attribute_set
|
|
@@ -163,6 +359,36 @@ module Nl
|
|
|
163
359
|
@attribute_set.decode(decoder)
|
|
164
360
|
end
|
|
165
361
|
end
|
|
362
|
+
|
|
363
|
+
class IndexedArray
|
|
364
|
+
def initialize(sub_type)
|
|
365
|
+
@sub_type = sub_type
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def encode(encoder, values)
|
|
369
|
+
values.each_with_index do |value, i|
|
|
370
|
+
nlattr = Core::NlAttr.new(0, i + 1)
|
|
371
|
+
encoder.measure(Endian::Host::U16) do
|
|
372
|
+
nlattr.encode(encoder)
|
|
373
|
+
@sub_type.encode(encoder, value)
|
|
374
|
+
end
|
|
375
|
+
encoder.align_to(Core::NLA_ALIGNTO)
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def decode(decoder)
|
|
380
|
+
result = []
|
|
381
|
+
while decoder.available?
|
|
382
|
+
nlattr = Core::NlAttr.decode(decoder)
|
|
383
|
+
element = decoder.limit(nlattr.len - Core::NLA_HDRLEN) do
|
|
384
|
+
@sub_type.decode(decoder)
|
|
385
|
+
end
|
|
386
|
+
decoder.align_to(Core::NLA_ALIGNTO)
|
|
387
|
+
result << element
|
|
388
|
+
end
|
|
389
|
+
result
|
|
390
|
+
end
|
|
391
|
+
end
|
|
166
392
|
end
|
|
167
393
|
end
|
|
168
394
|
end
|
data/lib/nl/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kasumi Hanazuki
|
|
@@ -37,7 +37,7 @@ licenses:
|
|
|
37
37
|
- MIT
|
|
38
38
|
metadata:
|
|
39
39
|
homepage_uri: https://github.com/hanazuki/nl
|
|
40
|
-
source_code_uri: https://github.com/hanazuki/nl/tree/v0.
|
|
40
|
+
source_code_uri: https://github.com/hanazuki/nl/tree/v0.2.0
|
|
41
41
|
changelog_uri: https://github.com/hanazuki/nl/blob/master/CHANGELOG.md
|
|
42
42
|
rdoc_options: []
|
|
43
43
|
require_paths:
|
|
@@ -53,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '0'
|
|
55
55
|
requirements: []
|
|
56
|
-
rubygems_version:
|
|
56
|
+
rubygems_version: 4.0.3
|
|
57
57
|
specification_version: 4
|
|
58
58
|
summary: Linux Netlink client
|
|
59
59
|
test_files: []
|