Class: PacketGen::Header::SNMP

Inherits:
ASN1Base
  • Object
show all
Defined in:
lib/packetgen/header/snmp.rb

Overview

Simple Network Management Protocol (SNMP)

See github.com/lemontree55/packetgen/wiki/SNMP

SNMP is defined from ASN.1:

Message ::=
  SEQUENCE {
       version
          INTEGER {
              version-1(0)
          },
       community      -- community name
           OCTET STRING,
       data           -- e.g., PDUs if trivial
           ANY        -- authentication is being used
  }

It has 3 attributes, accessible through #[]:

  • :version, SNMP protocol version (type RASN1::Types::Integer with enumeration),

  • :community (type RASN1::Types::OctetString),

  • :data (type PDUs).

Examples:

snmp = PacketGen::Header::SNMP.new(version: "v3",
                                   chosen_pdu: PacketGen::Header::SNMP::PDU_SET,
                                   pdu: { id: 1, varbindlist: [{ name: '1.2.3.4' }] })
snmp.version        #=> "v3"
snmp.community      #=> "public"
snmp.pdu.class      #=> PacketGen::Header::SNMP::SetRequest
snmp.pdu[:id].value #=> 1
snmp.pdu[:varbindlist][0][:name].inspect #=> 'name OBJECT ID: "1.2.3.4"'
snmp.pdu[:varbindlist][0][:name].value   #=> "1.2.3.4"

Author:

  • Sylvain Daubert

  • LemonTree55

Since:

  • 2.0.0

Defined Under Namespace

Classes: Bulk, GetNextRequest, GetRequest, GetResponse, InformRequest, PDUs, Report, SetRequest, Trapv1, Trapv2, VarBind, VariableBindings

Constant Summary collapse

UDP_PORT1 =

Agents listen to this port

Since:

  • 2.0.0

161
UDP_PORT2 =

Configuration sinks listen to this port

Since:

  • 2.0.0

162
PDU_GET =

Implicit tag number for GetRequest PDU type

Since:

  • 2.0.0

0
PDU_NEXT =

Implicit tag number for GetNextRequest PDU type

Since:

  • 2.0.0

1
PDU_RESPONSE =

Implicit tag number for GetResponse PDU type

Since:

  • 2.0.0

2
PDU_SET =

Implicit tag number for SetRequest PDU type

Since:

  • 2.0.0

3
PDU_TRAPv1 =

Implicit tag number for Trapv1 PDU type

Since:

  • 2.0.0

4
PDU_BULK =

Implicit tag number for Bulk PDU type

Since:

  • 2.0.0

5
PDU_INFORM =

Implicit tag number for InformRequest PDU type

Since:

  • 2.0.0

6
PDU_TRAPv2 =

Implicit tag number for Trapv2 PDU type

Since:

  • 2.0.0

7
PDU_REPORT =

Implicit tag number for Report PDU type

Since:

  • 2.0.0

8
ERRORS =

Error types

Since:

  • 2.0.0

{
  'no_error' => 0,
  'too_big' => 1,
  'no_such_name' => 2,
  'bad_value' => 3,
  'read_only' => 4,
  'generic_error' => 5,
  'no_access' => 6,
  'wrong_type' => 7,
  'wrong_length' => 8,
  'wrong_encoding' => 9,
  'wrong_value' => 10,
  'no_creation' => 11,
  'inconsistent_value' => 12,
  'ressource_unavailable' => 13,
  'commit_failed' => 14,
  'undo_failed' => 15,
  'authorization_error' => 16,
  'not_writable' => 17,
  'inconsistent_name' => 18
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from ASN1Base

define_attributes, known_headers, #read

Methods included from PacketGen::Headerable

included, #method_name, #packet, #packet=, #parse?, #protocol_name, #read, #to_s

Constructor Details

#initialize(options = {}) ⇒ SNMP

Returns a new instance of SNMP.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :version (Integer, String)
  • :community (String)
  • :chosen_pdu (Integer)

    Set PDU type

  • :pdu (Hash)

    Set PDU content

Since:

  • 2.0.0


378
379
380
381
382
383
384
385
# File 'lib/packetgen/header/snmp.rb', line 378

def initialize(options={})
  super
  data.chosen = options[:chosen_pdu] if options[:chosen_pdu]
  return unless options[:pdu]

  klass = data.root.chosen_value.class
  data.root.value[data.chosen] = klass.new(options[:pdu])
end

Instance Attribute Details

#communityString

community attribute. Shortcut for snmp[:community].value.

Returns:

  • (String)

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/packetgen/header/snmp.rb', line 57

class SNMP < ASN1Base
  # Agents listen to this port
  UDP_PORT1 = 161
  # Configuration sinks listen to this port
  UDP_PORT2 = 162

  # Implicit tag number for GetRequest PDU type
  PDU_GET      = 0
  # Implicit tag number for GetNextRequest PDU type
  PDU_NEXT     = 1
  # Implicit tag number for GetResponse PDU type
  PDU_RESPONSE = 2
  # Implicit tag number for SetRequest PDU type
  PDU_SET      = 3
  # Implicit tag number for Trapv1 PDU type
  PDU_TRAPv1   = 4
  # Implicit tag number for Bulk PDU type
  PDU_BULK     = 5
  # Implicit tag number for InformRequest PDU type
  PDU_INFORM   = 6
  # Implicit tag number for Trapv2 PDU type
  PDU_TRAPv2   = 7
  # Implicit tag number for Report PDU type
  PDU_REPORT   = 8

  # Error types
  ERRORS = {
    'no_error' => 0,
    'too_big' => 1,
    'no_such_name' => 2,
    'bad_value' => 3,
    'read_only' => 4,
    'generic_error' => 5,
    'no_access' => 6,
    'wrong_type' => 7,
    'wrong_length' => 8,
    'wrong_encoding' => 9,
    'wrong_value' => 10,
    'no_creation' => 11,
    'inconsistent_value' => 12,
    'ressource_unavailable' => 13,
    'commit_failed' => 14,
    'undo_failed' => 15,
    'authorization_error' => 16,
    'not_writable' => 17,
    'inconsistent_name' => 18
  }.freeze

  # Class to handle SNMP VarBind
  #  VarBind ::= SEQUENCE {
  #                name  OBJECT IDENTIFIER,
  #                value ANY     -- depends on name
  #              }
  # This class associates a +:name+ (type +RASN1::Types::ObjectId+) to a +:value+ (any RASN1 type).
  # @example
  #   vb = PacketGen::Header::SNMP::VarBind.new(name: "1.2.3.4", value: RASN1::Types::OctetString.new(value: "abc"))
  #   vb[:name].class  # => RASN1::Types::ObjectId
  #   vb[:name].value  # => "1.2.3.4"
  # @author Sylvain Daubert
  # @author LemonTree55
  class VarBind < RASN1::Model
    sequence :varbind,
             content: [objectid(:name),
                       any(:value)]
  end

  # Class to handle SNMP VariableBindingsList
  #  VarBindList ::= SEQUENCE (SIZE (0..max-bindings)) OF VarBind
  # This is a sequence of {VarBind}.
  # @example
  #  bindings = PacketGen::Header::SNMP::VariableBindings.new
  #  bindings << { name: "1.2.3.4", value: RASN1::Types::OctetString.new(value: "abc") }
  # @author Sylvain Daubert
  # @author LemonTree55
  class VariableBindings < RASN1::Model
    sequence_of :bindings, VarBind

    # Get 'idx'th element from the list
    # @return [VarBind,nil]
    def [](idx)
      value[idx]
    end

    # Get element counts in list
    # @return [Integer]
    def size
      value.size
    end
  end

  # Class to handle GetRequest PDU
  #  GetRequest-PDU ::= [0] IMPLICIT PDU
  #
  #  PDU ::= SEQUENCE {
  #              request-id INTEGER (-214783648..214783647),
  #
  #              error-status                -- sometimes ignored
  #                  INTEGER {
  #                      noError(0),
  #                      tooBig(1),
  #                      noSuchName(2),      -- for proxy compatibility
  #                      badValue(3),        -- for proxy compatibility
  #                      readOnly(4),        -- for proxy compatibility
  #                      genErr(5),
  #                      noAccess(6),
  #                      wrongType(7),
  #                      wrongLength(8),
  #                      wrongEncoding(9),
  #                      wrongValue(10),
  #                      noCreation(11),
  #                      inconsistentValue(12),
  #                      resourceUnavailable(13),
  #                      commitFailed(14),
  #                      undoFailed(15),
  #                      authorizationError(16),
  #                      notWritable(17),
  #                      inconsistentName(18)
  #                  },
  #
  #              error-index                 -- sometimes ignored
  #                  INTEGER (0..max-bindings),
  #
  #              variable-bindings           -- values are sometimes ignored
  #                  VarBindList
  #          }
  # This class defines a GetRequest SNMP PDU. It defines 4 attributes:
  # * an +:id+ (request-id, type +RASN1::Types::Integer+),
  # * an +:error+ (error-status, type +RASN1::Types::Integer with enumeration definition from {ERRORS}),
  # * an +:error_index+ (type +RASN1::Types::Integer+),
  # * a +:barbindlist+ (variable-bindings, type {VariableBindings}).
  #
  # @example
  #   req = PacketGen::Header::SNMP::GetRequest.new(id: 1, error: "no_error")
  #   req[:id].value    #=> 1
  #   req[:error].value #=> "no_error"
  #   req[:error].to_i  #=> 0
  #   req[:varbindlist] << { name: "1.2.3.4", value: RASN1::Types::OctetString.new(value: "abcd") }
  # @author Sylvain Daubert
  # @author LemonTree55
  class GetRequest < RASN1::Model
    sequence :pdu,
             implicit: SNMP::PDU_GET, constructed: true,
             content: [integer(:id, value: 0),
                       integer(:error, value: 0, enum: ERRORS),
                       integer(:error_index, value: 0),
                       model(:varbindlist, VariableBindings)]

    def initialize(args={})
      super
    end
  end

  # Class to handle GetNextRequest PDU
  #  GetNextRequest-PDU ::= [1] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class GetNextRequest < GetRequest
    root_options implicit: SNMP::PDU_NEXT
  end

  # Class to handle GetResponse PDU
  #  GetResponse-PDU ::= [2] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class GetResponse < GetRequest
    root_options implicit: SNMP::PDU_RESPONSE
  end

  # Class to handle SetRequest PDU
  #  SetRequest-PDU ::= [3] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class SetRequest < GetRequest
    root_options implicit: SNMP::PDU_SET
  end

  # Class to handle Trap from SNMPv1
  #  Trap-PDU ::= [4] IMPLICIT SEQUENCE {
  #                          enterprise OBJECT IDENTIFIER,
  #                          agent-addr NetworkAddress,
  #                          generic-trap      -- generic trap type
  #                              INTEGER {
  #                                  coldStart(0),
  #                                  warmStart(1),
  #                                  linkDown(2),
  #                                  linkUp(3),
  #                                  authenticationFailure(4),
  #                                  egpNeighborLoss(5),
  #                                  enterpriseSpecific(6)
  #                              },
  #                          specific-trap INTEGER,
  #                          time-stamp TimeTicks,
  #                          variable-bindings VarBindList
  #                   }
  # This class defines 6 attributes accessibles through +#[]+:
  # * +:enterprise+ for request-id (type +RASN1::Types::ObjectId+),
  # * +:agent_addr+ (type +RASN1::Types::Integer+),
  # * +:generic_trap+ (type +RASN1::Types::Integer+),
  # * +:specific_trap+ (type +RASN1::Types::Integer+),
  # * +:timestamp+ (type +RASN1::Types::Integer+),
  # * +:varbindlist+ for variable-bindings (type {VariableBindings}).
  # @author Sylvain Daubert
  # @author LemonTree55
  class Trapv1 < RASN1::Model
    sequence :trap,
             implicit: SNMP::PDU_TRAPv1, constructed: true,
             content: [objectid(:enterprise),
                       octet_string(:agent_addr),
                       integer(:generic_trap, enum: { 'cold_start' => 0,
                                                      'warm_start' => 1,
                                                      'link_down' => 2,
                                                      'link_up' => 3,
                                                      'auth_failure' => 4,
                                                      'egp_neighbor_loss' => 5,
                                                      'specific' => 6 }),
                       integer(:specific_trap),
                       integer(:timestamp),
                       model(:varbindlist, VariableBindings)]
  end

  # Class to handle Bulk PDU
  #  GetBulkRequest-PDU ::= [5] IMPLICIT BulkPDU
  #
  #  BulkPDU ::=                         -- must be identical in
  #        SEQUENCE {                    -- structure to PDU
  #            request-id      INTEGER (-214783648..214783647),
  #            non-repeaters   INTEGER (0..max-bindings),
  #            max-repetitions INTEGER (0..max-bindings),
  #            variable-bindings           -- values are ignored
  #                VarBindList
  #        }
  #
  # This class defines 4 values accessibles through +#[]+:
  # * +:id+ for request-id (type +RASN1::Types::Integer+),
  # * +:non_repeaters+ (type +RASN1::Types::Integer+),
  # * +:max_repetitions+ (type +RASN1::Types::Integer+),
  # * +varbindlist+ for variable-bindings (type {VariableBindings}).
  # @example
  #   bulk = PacketGen::Header::SNMP::Bulk.new(id: 1, non_repeaters: 2, max_repetitions: 2)
  #   bulk[:varbindlist] << { name: '1.2.3.4', value: RASN1::Types::OctetString.new(value: "abcd") }
  #   bulk[:id].inspect  # => "id INTEGER: 1"
  #   bulk[:id].value    # => 1
  #   bulk[:varbindlist][0][:name].value  # => "1.2.3.4"
  # @author Sylvain Daubert
  # @author LemonTree55
  class Bulk < RASN1::Model
    sequence :bulkpdu,
             implicit: SNMP::PDU_BULK, constructed: true,
             content: [integer(:id, value: 0),
                       integer(:non_repeaters),
                       integer(:max_repetitions),
                       model(:varbindlist, VariableBindings)]
  end

  # Class to handle InformRequest PDU
  #  InformRequest-PDU ::= [6] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class InformRequest < GetRequest
    root_options implicit: SNMP::PDU_INFORM
  end

  # Class to handle Trapv2 PDU
  #  SNMPv2-Trap-PDU ::= [7] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class Trapv2 < GetRequest
    root_options implicit: SNMP::PDU_TRAPv2
  end

  # Class to handle Report PDU
  #  Report-PDU ::= [8] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class Report < GetRequest
    root_options implicit: SNMP::PDU_REPORT
  end

  # Class to handle PDUs from SNMP packet
  #  PDUs ::= CHOICE {
  #             get-request      [0] IMPLICIT PDU,
  #             get-next-request [1] IMPLICIT PDU,
  #             get-response     [2] IMPLICIT PDU,
  #             set-request      [3] IMPLICIT PDU,
  #             snmpV1-trap      [4] IMPLICIT PDU,
  #             get-bulk-request [5] IMPLICIT PDU,
  #             inform-request   [6] IMPLICIT PDU,
  #             snmpV2-trap      [7] IMPLICIT PDU,
  #             report           [8] IMPLICIT PDU
  #           }
  # This class is a wrapper. It contains one of {GetRequest}, {GetNextRequest}, {GetResponse}, {SetRequest},
  # {Trapv1}, {Bulk}, {InformRequest}, {Trapv2} or {Report}.
  # @author Sylvain Daubert
  # @author LemonTree55
  class PDUs < RASN1::Model
    choice :pdus,
           content: [model(:get_request, GetRequest),
                     model(:get_next_request, GetNextRequest),
                     model(:get_response, GetResponse),
                     model(:set_request, SetRequest),
                     model(:trapv1, Trapv1),
                     model(:bulk, Bulk),
                     model(:inform, InformRequest),
                     model(:trapv2, Trapv2),
                     model(:report, Report)]
  end

  sequence :message,
           content: [integer(:version,
                             value: 'v2c',
                             enum: { 'v1' => 0, 'v2c' => 1, 'v2' => 2, 'v3' => 3 }),
                     octet_string(:community, value: 'public'),
                     model(:data, PDUs)]

  define_attributes :version, :community

  # @param [Hash] options
  # @option options [Integer,String] :version
  # @option options [String] :community
  # @option options [Integer] :chosen_pdu Set PDU type
  # @option options [Hash] :pdu Set PDU content
  def initialize(options={})
    super
    data.chosen = options[:chosen_pdu] if options[:chosen_pdu]
    return unless options[:pdu]

    klass = data.root.chosen_value.class
    data.root.value[data.chosen] = klass.new(options[:pdu])
  end

  # accessor to data payload. Shortcut for +snmp[:data]+.
  # @return [ASN1::Types::Choice]
  def data
    @elements[:data]
  end

  # shortcut to PDU (+snmp[:data].chosen_value+).
  # @return [GetRequest, Bulk, Trapv1, nil] return `nil` if no CHOICE was done
  def pdu
    if data.chosen.nil?
      nil
    else
      data.root.chosen_value
    end
  end

  # Inspect SNMP header
  # @return [String]
  def inspect
    str = super
    str << Inspect.shift_level
    str << if self[:data].chosen.nil?
             Inspect::FMT_ATTR % [self[:data].type, :data, '']
           else
             inspect_data
           end
  end

  # @api private
  # @note This method is used internally by PacketGen and should not be
  #       directly called
  # @param [Packet] packet
  # @return [void]
  # @since 2.7.0 Set UDP sport according to bindings, only if sport is not set yet (i.e. is zero).
  #  Needed by new bind API.
  def added_to_packet(packet)
    return unless packet.is?('UDP')
    return unless packet.udp.sport.zero?

    packet.udp.sport = packet.udp.dport
  end

  private

  def inspect_data
    data = self[:data]
    str = Inspect::FMT_ATTR % [data.type, :data, data.chosen_value.type]
    str << Inspect.dashed_line('ASN.1 content')
    str << data.chosen_value.inspect(1)
    begin
      str << Inspect.inspect_body(self[:message].to_der, 'ASN.1 DER')
    rescue StandardError => e
      raise unless e.message.match?(/TAG.*not handled/)
    end
    str
  end
end

#versionString

version attribute. Shortcut for snmp[:version].value. String values are: v1, v2, v2c and v3.

Returns:

  • (String)

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/packetgen/header/snmp.rb', line 57

class SNMP < ASN1Base
  # Agents listen to this port
  UDP_PORT1 = 161
  # Configuration sinks listen to this port
  UDP_PORT2 = 162

  # Implicit tag number for GetRequest PDU type
  PDU_GET      = 0
  # Implicit tag number for GetNextRequest PDU type
  PDU_NEXT     = 1
  # Implicit tag number for GetResponse PDU type
  PDU_RESPONSE = 2
  # Implicit tag number for SetRequest PDU type
  PDU_SET      = 3
  # Implicit tag number for Trapv1 PDU type
  PDU_TRAPv1   = 4
  # Implicit tag number for Bulk PDU type
  PDU_BULK     = 5
  # Implicit tag number for InformRequest PDU type
  PDU_INFORM   = 6
  # Implicit tag number for Trapv2 PDU type
  PDU_TRAPv2   = 7
  # Implicit tag number for Report PDU type
  PDU_REPORT   = 8

  # Error types
  ERRORS = {
    'no_error' => 0,
    'too_big' => 1,
    'no_such_name' => 2,
    'bad_value' => 3,
    'read_only' => 4,
    'generic_error' => 5,
    'no_access' => 6,
    'wrong_type' => 7,
    'wrong_length' => 8,
    'wrong_encoding' => 9,
    'wrong_value' => 10,
    'no_creation' => 11,
    'inconsistent_value' => 12,
    'ressource_unavailable' => 13,
    'commit_failed' => 14,
    'undo_failed' => 15,
    'authorization_error' => 16,
    'not_writable' => 17,
    'inconsistent_name' => 18
  }.freeze

  # Class to handle SNMP VarBind
  #  VarBind ::= SEQUENCE {
  #                name  OBJECT IDENTIFIER,
  #                value ANY     -- depends on name
  #              }
  # This class associates a +:name+ (type +RASN1::Types::ObjectId+) to a +:value+ (any RASN1 type).
  # @example
  #   vb = PacketGen::Header::SNMP::VarBind.new(name: "1.2.3.4", value: RASN1::Types::OctetString.new(value: "abc"))
  #   vb[:name].class  # => RASN1::Types::ObjectId
  #   vb[:name].value  # => "1.2.3.4"
  # @author Sylvain Daubert
  # @author LemonTree55
  class VarBind < RASN1::Model
    sequence :varbind,
             content: [objectid(:name),
                       any(:value)]
  end

  # Class to handle SNMP VariableBindingsList
  #  VarBindList ::= SEQUENCE (SIZE (0..max-bindings)) OF VarBind
  # This is a sequence of {VarBind}.
  # @example
  #  bindings = PacketGen::Header::SNMP::VariableBindings.new
  #  bindings << { name: "1.2.3.4", value: RASN1::Types::OctetString.new(value: "abc") }
  # @author Sylvain Daubert
  # @author LemonTree55
  class VariableBindings < RASN1::Model
    sequence_of :bindings, VarBind

    # Get 'idx'th element from the list
    # @return [VarBind,nil]
    def [](idx)
      value[idx]
    end

    # Get element counts in list
    # @return [Integer]
    def size
      value.size
    end
  end

  # Class to handle GetRequest PDU
  #  GetRequest-PDU ::= [0] IMPLICIT PDU
  #
  #  PDU ::= SEQUENCE {
  #              request-id INTEGER (-214783648..214783647),
  #
  #              error-status                -- sometimes ignored
  #                  INTEGER {
  #                      noError(0),
  #                      tooBig(1),
  #                      noSuchName(2),      -- for proxy compatibility
  #                      badValue(3),        -- for proxy compatibility
  #                      readOnly(4),        -- for proxy compatibility
  #                      genErr(5),
  #                      noAccess(6),
  #                      wrongType(7),
  #                      wrongLength(8),
  #                      wrongEncoding(9),
  #                      wrongValue(10),
  #                      noCreation(11),
  #                      inconsistentValue(12),
  #                      resourceUnavailable(13),
  #                      commitFailed(14),
  #                      undoFailed(15),
  #                      authorizationError(16),
  #                      notWritable(17),
  #                      inconsistentName(18)
  #                  },
  #
  #              error-index                 -- sometimes ignored
  #                  INTEGER (0..max-bindings),
  #
  #              variable-bindings           -- values are sometimes ignored
  #                  VarBindList
  #          }
  # This class defines a GetRequest SNMP PDU. It defines 4 attributes:
  # * an +:id+ (request-id, type +RASN1::Types::Integer+),
  # * an +:error+ (error-status, type +RASN1::Types::Integer with enumeration definition from {ERRORS}),
  # * an +:error_index+ (type +RASN1::Types::Integer+),
  # * a +:barbindlist+ (variable-bindings, type {VariableBindings}).
  #
  # @example
  #   req = PacketGen::Header::SNMP::GetRequest.new(id: 1, error: "no_error")
  #   req[:id].value    #=> 1
  #   req[:error].value #=> "no_error"
  #   req[:error].to_i  #=> 0
  #   req[:varbindlist] << { name: "1.2.3.4", value: RASN1::Types::OctetString.new(value: "abcd") }
  # @author Sylvain Daubert
  # @author LemonTree55
  class GetRequest < RASN1::Model
    sequence :pdu,
             implicit: SNMP::PDU_GET, constructed: true,
             content: [integer(:id, value: 0),
                       integer(:error, value: 0, enum: ERRORS),
                       integer(:error_index, value: 0),
                       model(:varbindlist, VariableBindings)]

    def initialize(args={})
      super
    end
  end

  # Class to handle GetNextRequest PDU
  #  GetNextRequest-PDU ::= [1] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class GetNextRequest < GetRequest
    root_options implicit: SNMP::PDU_NEXT
  end

  # Class to handle GetResponse PDU
  #  GetResponse-PDU ::= [2] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class GetResponse < GetRequest
    root_options implicit: SNMP::PDU_RESPONSE
  end

  # Class to handle SetRequest PDU
  #  SetRequest-PDU ::= [3] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class SetRequest < GetRequest
    root_options implicit: SNMP::PDU_SET
  end

  # Class to handle Trap from SNMPv1
  #  Trap-PDU ::= [4] IMPLICIT SEQUENCE {
  #                          enterprise OBJECT IDENTIFIER,
  #                          agent-addr NetworkAddress,
  #                          generic-trap      -- generic trap type
  #                              INTEGER {
  #                                  coldStart(0),
  #                                  warmStart(1),
  #                                  linkDown(2),
  #                                  linkUp(3),
  #                                  authenticationFailure(4),
  #                                  egpNeighborLoss(5),
  #                                  enterpriseSpecific(6)
  #                              },
  #                          specific-trap INTEGER,
  #                          time-stamp TimeTicks,
  #                          variable-bindings VarBindList
  #                   }
  # This class defines 6 attributes accessibles through +#[]+:
  # * +:enterprise+ for request-id (type +RASN1::Types::ObjectId+),
  # * +:agent_addr+ (type +RASN1::Types::Integer+),
  # * +:generic_trap+ (type +RASN1::Types::Integer+),
  # * +:specific_trap+ (type +RASN1::Types::Integer+),
  # * +:timestamp+ (type +RASN1::Types::Integer+),
  # * +:varbindlist+ for variable-bindings (type {VariableBindings}).
  # @author Sylvain Daubert
  # @author LemonTree55
  class Trapv1 < RASN1::Model
    sequence :trap,
             implicit: SNMP::PDU_TRAPv1, constructed: true,
             content: [objectid(:enterprise),
                       octet_string(:agent_addr),
                       integer(:generic_trap, enum: { 'cold_start' => 0,
                                                      'warm_start' => 1,
                                                      'link_down' => 2,
                                                      'link_up' => 3,
                                                      'auth_failure' => 4,
                                                      'egp_neighbor_loss' => 5,
                                                      'specific' => 6 }),
                       integer(:specific_trap),
                       integer(:timestamp),
                       model(:varbindlist, VariableBindings)]
  end

  # Class to handle Bulk PDU
  #  GetBulkRequest-PDU ::= [5] IMPLICIT BulkPDU
  #
  #  BulkPDU ::=                         -- must be identical in
  #        SEQUENCE {                    -- structure to PDU
  #            request-id      INTEGER (-214783648..214783647),
  #            non-repeaters   INTEGER (0..max-bindings),
  #            max-repetitions INTEGER (0..max-bindings),
  #            variable-bindings           -- values are ignored
  #                VarBindList
  #        }
  #
  # This class defines 4 values accessibles through +#[]+:
  # * +:id+ for request-id (type +RASN1::Types::Integer+),
  # * +:non_repeaters+ (type +RASN1::Types::Integer+),
  # * +:max_repetitions+ (type +RASN1::Types::Integer+),
  # * +varbindlist+ for variable-bindings (type {VariableBindings}).
  # @example
  #   bulk = PacketGen::Header::SNMP::Bulk.new(id: 1, non_repeaters: 2, max_repetitions: 2)
  #   bulk[:varbindlist] << { name: '1.2.3.4', value: RASN1::Types::OctetString.new(value: "abcd") }
  #   bulk[:id].inspect  # => "id INTEGER: 1"
  #   bulk[:id].value    # => 1
  #   bulk[:varbindlist][0][:name].value  # => "1.2.3.4"
  # @author Sylvain Daubert
  # @author LemonTree55
  class Bulk < RASN1::Model
    sequence :bulkpdu,
             implicit: SNMP::PDU_BULK, constructed: true,
             content: [integer(:id, value: 0),
                       integer(:non_repeaters),
                       integer(:max_repetitions),
                       model(:varbindlist, VariableBindings)]
  end

  # Class to handle InformRequest PDU
  #  InformRequest-PDU ::= [6] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class InformRequest < GetRequest
    root_options implicit: SNMP::PDU_INFORM
  end

  # Class to handle Trapv2 PDU
  #  SNMPv2-Trap-PDU ::= [7] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class Trapv2 < GetRequest
    root_options implicit: SNMP::PDU_TRAPv2
  end

  # Class to handle Report PDU
  #  Report-PDU ::= [8] IMPLICIT PDU   -- PDU definition: see GetRequest
  # @see GetRequest
  # @author Sylvain Daubert
  class Report < GetRequest
    root_options implicit: SNMP::PDU_REPORT
  end

  # Class to handle PDUs from SNMP packet
  #  PDUs ::= CHOICE {
  #             get-request      [0] IMPLICIT PDU,
  #             get-next-request [1] IMPLICIT PDU,
  #             get-response     [2] IMPLICIT PDU,
  #             set-request      [3] IMPLICIT PDU,
  #             snmpV1-trap      [4] IMPLICIT PDU,
  #             get-bulk-request [5] IMPLICIT PDU,
  #             inform-request   [6] IMPLICIT PDU,
  #             snmpV2-trap      [7] IMPLICIT PDU,
  #             report           [8] IMPLICIT PDU
  #           }
  # This class is a wrapper. It contains one of {GetRequest}, {GetNextRequest}, {GetResponse}, {SetRequest},
  # {Trapv1}, {Bulk}, {InformRequest}, {Trapv2} or {Report}.
  # @author Sylvain Daubert
  # @author LemonTree55
  class PDUs < RASN1::Model
    choice :pdus,
           content: [model(:get_request, GetRequest),
                     model(:get_next_request, GetNextRequest),
                     model(:get_response, GetResponse),
                     model(:set_request, SetRequest),
                     model(:trapv1, Trapv1),
                     model(:bulk, Bulk),
                     model(:inform, InformRequest),
                     model(:trapv2, Trapv2),
                     model(:report, Report)]
  end

  sequence :message,
           content: [integer(:version,
                             value: 'v2c',
                             enum: { 'v1' => 0, 'v2c' => 1, 'v2' => 2, 'v3' => 3 }),
                     octet_string(:community, value: 'public'),
                     model(:data, PDUs)]

  define_attributes :version, :community

  # @param [Hash] options
  # @option options [Integer,String] :version
  # @option options [String] :community
  # @option options [Integer] :chosen_pdu Set PDU type
  # @option options [Hash] :pdu Set PDU content
  def initialize(options={})
    super
    data.chosen = options[:chosen_pdu] if options[:chosen_pdu]
    return unless options[:pdu]

    klass = data.root.chosen_value.class
    data.root.value[data.chosen] = klass.new(options[:pdu])
  end

  # accessor to data payload. Shortcut for +snmp[:data]+.
  # @return [ASN1::Types::Choice]
  def data
    @elements[:data]
  end

  # shortcut to PDU (+snmp[:data].chosen_value+).
  # @return [GetRequest, Bulk, Trapv1, nil] return `nil` if no CHOICE was done
  def pdu
    if data.chosen.nil?
      nil
    else
      data.root.chosen_value
    end
  end

  # Inspect SNMP header
  # @return [String]
  def inspect
    str = super
    str << Inspect.shift_level
    str << if self[:data].chosen.nil?
             Inspect::FMT_ATTR % [self[:data].type, :data, '']
           else
             inspect_data
           end
  end

  # @api private
  # @note This method is used internally by PacketGen and should not be
  #       directly called
  # @param [Packet] packet
  # @return [void]
  # @since 2.7.0 Set UDP sport according to bindings, only if sport is not set yet (i.e. is zero).
  #  Needed by new bind API.
  def added_to_packet(packet)
    return unless packet.is?('UDP')
    return unless packet.udp.sport.zero?

    packet.udp.sport = packet.udp.dport
  end

  private

  def inspect_data
    data = self[:data]
    str = Inspect::FMT_ATTR % [data.type, :data, data.chosen_value.type]
    str << Inspect.dashed_line('ASN.1 content')
    str << data.chosen_value.inspect(1)
    begin
      str << Inspect.inspect_body(self[:message].to_der, 'ASN.1 DER')
    rescue StandardError => e
      raise unless e.message.match?(/TAG.*not handled/)
    end
    str
  end
end

Instance Method Details

#added_to_packet(packet) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

This method is used internally by PacketGen and should not be directly called

This method returns an undefined value.

Parameters:

Since:

  • 2.7.0 Set UDP sport according to bindings, only if sport is not set yet (i.e. is zero). Needed by new bind API.


422
423
424
425
426
427
# File 'lib/packetgen/header/snmp.rb', line 422

def added_to_packet(packet)
  return unless packet.is?('UDP')
  return unless packet.udp.sport.zero?

  packet.udp.sport = packet.udp.dport
end

#dataASN1::Types::Choice

accessor to data payload. Shortcut for snmp[:data].

Returns:

  • (ASN1::Types::Choice)

Since:

  • 2.0.0


389
390
391
# File 'lib/packetgen/header/snmp.rb', line 389

def data
  @elements[:data]
end

#inspectString

Inspect SNMP header

Returns:

  • (String)

Since:

  • 2.0.0


405
406
407
408
409
410
411
412
413
# File 'lib/packetgen/header/snmp.rb', line 405

def inspect
  str = super
  str << Inspect.shift_level
  str << if self[:data].chosen.nil?
           Inspect::FMT_ATTR % [self[:data].type, :data, '']
         else
           inspect_data
         end
end

#pduGetRequest, ...

shortcut to PDU (snmp[:data].chosen_value).

Returns:

Since:

  • 2.0.0


395
396
397
398
399
400
401
# File 'lib/packetgen/header/snmp.rb', line 395

def pdu
  if data.chosen.nil?
    nil
  else
    data.root.chosen_value
  end
end