nl 0.3.0 → 0.4.2

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/Rakefile +7 -0
  4. data/lib/nl/async/dispatcher.rb +16 -16
  5. data/lib/nl/async/driver.rb +0 -2
  6. data/lib/nl/async/mailbox.rb +0 -2
  7. data/lib/nl/async/operation.rb +0 -2
  8. data/lib/nl/async.rb +0 -2
  9. data/lib/nl/attribute_set.rb +173 -0
  10. data/lib/nl/bitfield32.rb +86 -0
  11. data/lib/nl/blocking_transport.rb +12 -12
  12. data/lib/nl/connection.rb +7 -9
  13. data/lib/nl/datagram.rb +7 -9
  14. data/lib/nl/datatypes.rb +454 -0
  15. data/lib/nl/decoder.rb +4 -0
  16. data/lib/nl/endian.rb +9 -0
  17. data/lib/nl/exchange.rb +40 -18
  18. data/lib/nl/family.rb +50 -55
  19. data/lib/nl/genl/client.rb +105 -0
  20. data/lib/nl/genl/protocol.rb +59 -0
  21. data/lib/nl/genl/wire.rb +95 -0
  22. data/lib/nl/genl.rb +60 -62
  23. data/lib/nl/notification.rb +3 -6
  24. data/lib/nl/notification_router.rb +30 -23
  25. data/lib/nl/raw/client.rb +83 -0
  26. data/lib/nl/raw/protocol.rb +112 -0
  27. data/lib/nl/raw/wire.rb +147 -0
  28. data/lib/nl/raw.rb +104 -0
  29. data/lib/nl/sequence_allocator.rb +0 -2
  30. data/lib/nl/socket.rb +21 -5
  31. data/lib/nl/structured_payload.rb +61 -0
  32. data/lib/nl/sub_message.rb +94 -0
  33. data/lib/nl/version.rb +1 -1
  34. data/lib/nl.rb +5 -6
  35. data/sig/generated/nl/async/dispatcher.rbs +53 -0
  36. data/sig/generated/nl/async/driver.rbs +21 -0
  37. data/sig/generated/nl/async/mailbox.rbs +32 -0
  38. data/sig/generated/nl/async/operation.rbs +123 -0
  39. data/sig/generated/nl/async.rbs +9 -0
  40. data/sig/generated/nl/attribute_set.rbs +50 -0
  41. data/sig/generated/nl/bitfield32.rbs +41 -0
  42. data/sig/generated/nl/blocking_transport.rbs +33 -0
  43. data/sig/generated/nl/connection.rbs +26 -0
  44. data/sig/generated/nl/datagram.rbs +11 -0
  45. data/sig/generated/nl/datatypes.rbs +181 -0
  46. data/sig/generated/nl/decoder.rbs +38 -0
  47. data/sig/generated/nl/encoder.rbs +29 -0
  48. data/sig/generated/nl/endian.rbs +29 -0
  49. data/sig/generated/nl/error.rbs +27 -0
  50. data/sig/generated/nl/exchange.rbs +75 -0
  51. data/sig/generated/nl/family.rbs +89 -0
  52. data/sig/generated/nl/genl/client.rbs +74 -0
  53. data/sig/generated/nl/genl/protocol.rbs +37 -0
  54. data/sig/generated/nl/genl/wire.rbs +113 -0
  55. data/sig/generated/nl/genl.rbs +50 -0
  56. data/sig/generated/nl/notification.rbs +53 -0
  57. data/sig/generated/nl/notification_router.rbs +30 -0
  58. data/sig/generated/nl/raw/client.rbs +49 -0
  59. data/sig/generated/nl/raw/protocol.rbs +122 -0
  60. data/sig/generated/nl/raw/wire.rbs +151 -0
  61. data/sig/generated/nl/raw.rbs +70 -0
  62. data/sig/generated/nl/sequence_allocator.rbs +15 -0
  63. data/sig/generated/nl/socket.rbs +59 -0
  64. data/sig/generated/nl/structured_payload.rbs +25 -0
  65. data/sig/generated/nl/sub_message.rbs +74 -0
  66. data/sig/generated/nl/version.rbs +5 -0
  67. data/sig/generated/nl.rbs +2 -0
  68. metadata +48 -7
  69. data/lib/nl/core.rb +0 -94
  70. data/lib/nl/genl/connection.rb +0 -89
  71. data/lib/nl/protocols/genl.rb +0 -74
  72. data/lib/nl/protocols/raw.rb +0 -460
@@ -1,460 +0,0 @@
1
- require_relative '../encoder'
2
- require_relative '../decoder'
3
-
4
- module Nl
5
- module Protocols
6
- # The raw Netlink protocol
7
- class Raw
8
- class NotificationRouting
9
- def family_key(_protocol) = nil
10
- def frame_key(_header) = nil
11
- end
12
-
13
- NOTIFICATION_ROUTING = NotificationRouting.new.freeze
14
-
15
- AckFrame = Data.define(:header)
16
- ErrorFrame = Data.define(:header, :errno)
17
- DoneFrame = Data.define(:header, :errno)
18
- UnknownFrame = Data.define(:header)
19
- DataFrame = Data.define(:header, :message)
20
- Request = Data.define(:type, :flags, :message)
21
-
22
- attr_reader :name, :protonum
23
-
24
- def initialize(name, protonum)
25
- @name = name
26
- @protonum = protonum
27
- end
28
-
29
- def encode_message(encoder, request, seq:, pid:)
30
- header = Core::NlMsgHdr.new(0, request.type, request.flags, seq, pid)
31
- encoder.measure(Endian::Host::U16) do
32
- header.encode(encoder)
33
- request.message.encode(encoder)
34
- end
35
- end
36
-
37
- def decode_message(decoder, message_class)
38
- header = NlMsgHdr.decode(decoder)
39
- decoder.limit(header.len - Core::NLMSG_HDRLEN) do
40
- DataFrame.new(header:, message: message_class.decode(decoder, type: header.type))
41
- end
42
- end
43
-
44
- # Decodes one frame using the reply class associated with its sequence.
45
- def decode_frame(header, payload, message_class)
46
- decoder = Decoder.new(payload)
47
- if header.type < Core::NLMSG_MIN_TYPE
48
- case header.type
49
- when Core::NLMSG_ERROR
50
- errno = decoder.get_value(Endian::Host::SINT)
51
- if errno.positive?
52
- raise ProtocolViolation, "expected zero or negative NLMSG_ERROR errno, got #{errno}"
53
- end
54
-
55
- errno.zero? ? AckFrame.new(header:) : ErrorFrame.new(header:, errno: -errno)
56
- when Core::NLMSG_DONE
57
- return DoneFrame.new(header:, errno: nil) unless decoder.available?
58
-
59
- errno = decoder.get_value(Endian::Host::SINT)
60
- if errno.positive?
61
- raise ProtocolViolation, "expected zero or negative NLMSG_DONE errno, got #{errno}"
62
- end
63
-
64
- DoneFrame.new(header:, errno: errno.negative? ? -errno : nil)
65
- else
66
- UnknownFrame.new(header:)
67
- end
68
- else
69
- raise ArgumentError, 'reply class is required for a data message' unless message_class
70
-
71
- DataFrame.new(header:, message: message_class.decode(decoder, type: header.type))
72
- end
73
- end
74
-
75
- def send_message(socket, request, seq:, pid:)
76
- encoder = Encoder.new
77
- encode_message(encoder, request, seq:, pid:)
78
- socket.sendmsg(encoder.buffer.get_string, 0, Socket.sockaddr_nl(0, 0))
79
- nil
80
- end
81
-
82
- def build_request(kind, request_class, args)
83
- flags = Core::NLM_F_REQUEST
84
- flags |= kind == :dump ? Core::NLM_F_DUMP : Core::NLM_F_ACK
85
-
86
- message = request_class.from_params(args)
87
- Request.new(type: frame_type(request_class), flags:, message:)
88
- end
89
-
90
- # Raw Netlink sockets carry a single protocol family.
91
- def notification_routing = NOTIFICATION_ROUTING
92
-
93
- # Whether an unsolicited data frame belongs to this protocol.
94
- def notification_frame?(header, _payload)
95
- header.type >= Core::NLMSG_MIN_TYPE
96
- end
97
-
98
- # Looks up the generated notification class for an unsolicited frame.
99
- def notification_class(header, _payload, classes)
100
- classes[header.type]
101
- end
102
-
103
- def decode_notification(header, payload, message_class)
104
- decode_frame(header, payload, message_class).message
105
- end
106
-
107
- def multicast_group_id(name, value)
108
- value or raise UnresolvedMulticastGroupError,
109
- "multicast group #{name.inspect} has no fixed ID"
110
- end
111
-
112
- private def frame_type(message_class) = message_class::TYPE
113
-
114
- class AttributeSet
115
- Attribute = Struct.new(:value)
116
- class Attribute
117
- def self.decode(decoder)
118
- value = self::DATATYPE.decode(decoder)
119
- new(value)
120
- end
121
-
122
- def encode(encoder)
123
- self.class::DATATYPE.encode(encoder, self.value)
124
- end
125
- end
126
-
127
- def initialize(attributes)
128
- attr_class = self.class::Attribute
129
-
130
- attributes.each do |attr|
131
- unless attr.kind_of?(attr_class)
132
- raise TypeError, "attribute must be an instance of #{attr_class}"
133
- end
134
- end
135
-
136
- @attributes = Array(attributes)
137
- end
138
-
139
- def [](type)
140
- case type
141
- when Symbol
142
- attr_class = self.class.by_name(type)
143
- when Integer
144
- attr_class = self.class.by_type(type)
145
- else
146
- raise TypeError, "attribute type must be a Symbol or an Integer"
147
- end
148
-
149
- # TODO: multi-attr
150
- @attributes.find { it.kind_of?(attr_class) } rescue binding.irb
151
- end
152
-
153
- def <<(attr)
154
- attr_class = self.class::Attribute
155
- unless attr.kind_of?(attr_class)
156
- raise TypeError, "attribute must be an instance of #{attr_class}"
157
- end
158
-
159
- @attributes << attr
160
- end
161
-
162
- private def encode1(encoder, attr)
163
- datatype = attr.class::DATATYPE
164
- type = attr.class::TYPE | datatype.nlattr_type_flags
165
- nlattr = Core::NlAttr.new(0, type)
166
- encoder.measure(Endian::Host::U16) do
167
- nlattr.encode(encoder)
168
- attr.encode(encoder)
169
- end
170
- encoder.align_to(Core::NLA_ALIGNTO)
171
- end
172
-
173
- def encode(encoder)
174
- @attributes.each do |attr|
175
- encode1(encoder, attr)
176
- end
177
- end
178
-
179
- class << self
180
- private def decode1(decoder)
181
- nlattr = Core::NlAttr.decode(decoder)
182
- attr = decoder.limit(nlattr.len - Core::NLA_HDRLEN) do
183
- if attr_class = self::BY_TYPE[nlattr.type & Core::NLA_TYPE_MASK]
184
- attr_class.decode(decoder)
185
- else
186
- decoder.skip
187
- nil
188
- end
189
- end
190
- decoder.align_to(Core::NLA_ALIGNTO)
191
- attr
192
- end
193
-
194
- def decode(decoder)
195
- attrs = []
196
- while decoder.available?
197
- attr = decode1(decoder)
198
- attrs << attr
199
- end
200
- new(attrs.compact)
201
- end
202
-
203
- def build_attributes(**params)
204
- attrs = params.map do |name, value|
205
- attr_class = self::BY_NAME[name] or raise "Unknown attribute #{name}"
206
- attr_class.new(value)
207
- end
208
- new(attrs)
209
- end
210
- end
211
- end
212
-
213
- class Message
214
- attr_accessor :fixed_header, :attributes
215
-
216
- def initialize(fixed_header = nil, attributes = self.class::ATTRIBUTE_SET.new)
217
- @fixed_header = fixed_header
218
- @attributes = attributes
219
- end
220
-
221
- def self.from_params(params)
222
- if self::FIXED_HEADER
223
- header_params = params.slice(*self::FIXED_HEADER.members)
224
- fixed_header = self::FIXED_HEADER.new(**header_params)
225
- end
226
- attribute_params = params.slice(*self::ATTRIBUTES)
227
- attributes = self::ATTRIBUTE_SET.build_attributes(**attribute_params)
228
-
229
- unknown = params.keys - attribute_params.keys
230
- unknown -= header_params.keys if header_params
231
- unless unknown.empty?
232
- raise ArgumentError, "unknown parameters: #{unknown.join(', ')}"
233
- end
234
-
235
- new(fixed_header, attributes)
236
- end
237
-
238
- def append_attribute(attribute)
239
- @attributes << attribute
240
- end
241
-
242
- def encode(encoder)
243
- @fixed_header&.encode(encoder)
244
- @attributes.encode(encoder)
245
- end
246
-
247
- def self.decode(decoder, type:)
248
- unless self::TYPE == type
249
- raise "Expected message type #{self::TYPE}, got #{type}"
250
- end
251
-
252
- if fixed_header_class = self::FIXED_HEADER
253
- fixed_header = fixed_header_class.decode(decoder)
254
- end
255
-
256
- attributes = self::ATTRIBUTE_SET.decode(decoder)
257
-
258
- new(fixed_header, attributes)
259
- end
260
- end
261
-
262
- module DataTypes
263
- class Base
264
- def nlattr_type_flags
265
- 0
266
- end
267
- end
268
-
269
- class Scalar < Base
270
- def initialize(type, check)
271
- @type = type
272
- @check = check
273
- end
274
-
275
- def encode(encoder, value)
276
- value ||= 0
277
- encoder.put_value(@type, value.tap(&@check))
278
- end
279
-
280
- def decode(decoder)
281
- value = decoder.get_value(@type).tap(&@check)
282
- end
283
- end
284
-
285
- class String < Base
286
- def initialize(check)
287
- @check = check
288
- end
289
-
290
- def encode(encoder, value)
291
- encoder.put_zstring(value)
292
- end
293
-
294
- def decode(decoder)
295
- decoder.get_zstring
296
- end
297
- end
298
-
299
- class Binary < Base
300
- def initialize(check)
301
- @check = check
302
- end
303
-
304
- def encode(encoder, value)
305
- encoder.put_string(value)
306
- end
307
-
308
- def decode(decoder)
309
- decoder.get_string
310
- end
311
- end
312
-
313
- class Flag < Base
314
- def encode(encoder, value)
315
- # flag attribute has no payload; presence encodes true
316
- end
317
-
318
- def decode(decoder)
319
- true
320
- end
321
- end
322
-
323
- # A 32-bit value paired with a selector mask (8 bytes total: value u32 + selector u32)
324
- class Bitfield32 < Base
325
- def encode(encoder, value)
326
- v, selector = value.is_a?(Array) ? value : [value, 0xFFFFFFFF]
327
- encoder.put_value(Endian::Host::U32, v)
328
- encoder.put_value(Endian::Host::U32, selector)
329
- end
330
-
331
- def decode(decoder)
332
- value = decoder.get_value(Endian::Host::U32)
333
- selector = decoder.get_value(Endian::Host::U32)
334
- [value, selector]
335
- end
336
- end
337
-
338
- class Pad < Base
339
- def initialize(length = nil)
340
- @length = length
341
- end
342
-
343
- def encode(encoder, _value)
344
- encoder.put_string(?\0.b * @length) if @length
345
- end
346
-
347
- def decode(decoder)
348
- @length ? decoder.skip(@length) : decoder.skip
349
- nil
350
- end
351
- end
352
-
353
- class NestedAttributes < Base
354
- def initialize(attribute_set)
355
- @attribute_set = attribute_set
356
- end
357
-
358
- def nlattr_type_flags
359
- Core::NLA_F_NESTED
360
- end
361
-
362
- def encode(encoder, value)
363
- unless value.is_a?(@attribute_set)
364
- raise TypeError, "value must be an instance of #{@attribute_set}"
365
- end
366
-
367
- value.encode(encoder)
368
- end
369
-
370
- def decode(decoder)
371
- @attribute_set.decode(decoder)
372
- end
373
- end
374
-
375
- # Nested attributes whose type numbers are keys rather than members of
376
- # an attribute set. Each level is returned as an Integer-keyed Hash;
377
- # values at the innermost level are decoded as @attribute_set.
378
- class NestTypeValue < Base
379
- def initialize(attribute_set, levels)
380
- raise ArgumentError, 'levels must be positive' unless levels.positive?
381
-
382
- @attribute_set = attribute_set
383
- @levels = levels
384
- end
385
-
386
- def encode(encoder, value)
387
- encode_level(encoder, value, @levels)
388
- end
389
-
390
- def decode(decoder)
391
- decode_level(decoder, @levels)
392
- end
393
-
394
- private def encode_level(encoder, values, levels)
395
- values.each do |type, value|
396
- nlattr = Core::NlAttr.new(0, type | Core::NLA_F_NESTED)
397
- encoder.measure(Endian::Host::U16) do
398
- nlattr.encode(encoder)
399
- if levels == 1
400
- value.encode(encoder)
401
- else
402
- encode_level(encoder, value, levels - 1)
403
- end
404
- end
405
- encoder.align_to(Core::NLA_ALIGNTO)
406
- end
407
- end
408
-
409
- private def decode_level(decoder, levels)
410
- result = {}
411
- while decoder.available?(Core::NLA_HDRLEN)
412
- nlattr = Core::NlAttr.decode(decoder)
413
- type = nlattr.type & Core::NLA_TYPE_MASK
414
- value = decoder.limit(nlattr.len - Core::NLA_HDRLEN) do
415
- if levels == 1
416
- @attribute_set.decode(decoder)
417
- else
418
- decode_level(decoder, levels - 1)
419
- end
420
- end
421
- decoder.align_to(Core::NLA_ALIGNTO)
422
- result[type] = value
423
- end
424
- result
425
- end
426
- end
427
-
428
- class IndexedArray < Base
429
- def initialize(sub_type)
430
- @sub_type = sub_type
431
- end
432
-
433
- def encode(encoder, values)
434
- values.each_with_index do |value, i|
435
- nlattr = Core::NlAttr.new(0, i + 1)
436
- encoder.measure(Endian::Host::U16) do
437
- nlattr.encode(encoder)
438
- @sub_type.encode(encoder, value)
439
- end
440
- encoder.align_to(Core::NLA_ALIGNTO)
441
- end
442
- end
443
-
444
- def decode(decoder)
445
- result = []
446
- while decoder.available?
447
- nlattr = Core::NlAttr.decode(decoder)
448
- element = decoder.limit(nlattr.len - Core::NLA_HDRLEN) do
449
- @sub_type.decode(decoder)
450
- end
451
- decoder.align_to(Core::NLA_ALIGNTO)
452
- result << element
453
- end
454
- result
455
- end
456
- end
457
- end
458
- end
459
- end
460
- end