File: eui.rb

package info (click to toggle)
ruby-netaddr 1.5.1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 416 kB
  • sloc: ruby: 3,866; makefile: 9
file content (463 lines) | stat: -rwxr-xr-x 12,945 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
=begin rdoc
 Copyleft (c) 2006 Dustin Spinhirne

 Licensed under the same terms as Ruby, No Warranty is provided.
=end

module NetAddr

#=EUI - Extended Unique Identifier
#
#A class & series of methods for creating and manipulating Extended Unique Identifier
#(EUI) addresses. Two types of address formats are supported EUI-48 and EUI-64. The 
#most common use for this class will be to manipulate MAC addresses (which are essentially
#a type of EUI-48 address).
#
#EUI addresses are separated into two parts, the 
#Organizationally Unique Identifier (OUI) and the Extended Identifier (EI). The OUI
#is assigned by the IEEE and is used to identify a particular hardware manufacturer.
#The EI is assigned by the hardware manufacturer as a per device unique address.
#
#Probably the most useful feature of this class, and thus the reason it was created,
#is to help automate certain address assignments within IP. For example, IPv6
#Link Local addresses use MAC addresses for IP auto-assignment and multicast MAC addresses
#are determined based on the multicast IP address.
#
class EUI

private_class_method :new

#==============================================================================#
# initialize()
#==============================================================================#

#===Synopsis
# This method performs absolutely no error checking, and is meant to be used only by
# other internal methods for the sake of the speedier creation of EUI objects.
# Please consider using #create unless you know what you are doing with 100% certainty.
#
# Example:
# NetAddr::EUI48.new('aabbccddeeff')
#
#===Arguments:
#* EUI as a String or Integer. Strings should contain no formatting characters.
#
    def initialize(eui)

        if (eui.kind_of?(Integer))
            @eui_i = eui
            @eui = eui.to_s(16)
            if ( self.kind_of?(NetAddr::EUI48) )
                @eui = '0' * (12 - @eui.length) << @eui if (@eui.length < 12)
            else
                @eui = '0' * (16 - @eui.length) << @eui if (@eui.length < 16)
            end

        elsif(eui.kind_of?(String))
            @eui = eui
            @eui_i = eui.to_i(16)
        else
            raise ArgumentError, "Expected String or Integer, but #{eui.class} provided."
        end

        # set ei & oui
        if ( self.kind_of?(NetAddr::EUI48) )
            @ei = @eui.slice(6..11)
        else
            @ei = @eui.slice(6..15)
        end

        @oui = @eui.slice(0..5)

    end

#==============================================================================#
# create()
#==============================================================================#

#===Synopsis
#Create a new EUI48 or EUI64 object.
#
# Example:
# addr = NetAddr::EUI.create('aa-bb-cc-dd-ee-ff')
# addr = NetAddr::EUI.create('aa:bb:cc:dd:ee:ff')
# addr = NetAddr::EUI.create('aabb.ccdd.eeff')
# addr = NetAddr::EUI.create('aa-bb-cc-dd-ee-ff-00-01')
#
#===Arguments
#* eui = EUI as a String
#
#===Returns
#* EUI48 or EUI64 object
#
    def EUI.create(eui)
        if (!eui.kind_of? String)
            raise ArgumentError, "Expected String, but #{eui.class} provided."
        end

        # validate
        NetAddr.validate_eui(eui)

        # remove formatting characters
        eui.gsub!(/[\.\:\-]/, '')

        if (eui.length == 12)
            eui = NetAddr::EUI48.new(eui)
        else
            eui = NetAddr::EUI64.new(eui)
        end

        return(eui)
    end

#==============================================================================#
# address()
#==============================================================================#

#===Synopsis
# Returns EUI address. The default address format is xxxx.xxxx.xxxx
#
# Example:
# addr = NetAddr::EUI.create('aabb.ccdd.eeff')
# addr.address(:Delimiter => '-') => "aa-bb-cc-dd-ee-ff"
# addr.address(:Delimiter => ':') => "aa:bb:cc:dd:ee:ff"
#
#===Arguments:
#* options = Hash with the following fields:
#    :Delimiter -- delimitation character. valid values are (- : .)
#
#===Returns:
#* String
#
    def address(options=nil)
        known_args = [:Delimiter]
        delimiter = '-'

        if (options)
            if (!options.kind_of? Hash)
                raise ArgumentError, "Expected Hash, but #{options.class} provided."
            end
            NetAddr.validate_args(options.keys,known_args)

            if (options.has_key?(:Delimiter))
                delimiter = options[:Delimiter]
                delimiter = '-' if (delimiter != ':' && delimiter != '.')
            end
        end

        if (delimiter == '-' || delimiter == ':')
            addr = octets.join(delimiter)
        elsif (delimiter == '.')
                toggle = 0
                octets.each do |x|
                    if (!addr)
                        addr = x
                        toggle = 1
                    elsif (toggle == 0)
                        addr = addr  << '.' << x
                        toggle = 1
                    else
                        addr = addr << x
                        toggle = 0
                    end
                end
        end

        return(addr)
    end

#==============================================================================#
# ei()
#==============================================================================#

#===Synopsis
#Returns Extended Identifier portion of an EUI address (the vendor assigned ID).
#The default address format is xx-xx-xx
#
# Example:
# addr = NetAddr::EUI.create('aabb.ccdd.eeff')
# addr.ei(:Delimiter => '-') => "dd-ee-ff"
#
#===Arguments:
#* options = Hash with the following fields:
#    :Delimiter -- delimitation character. valid values are (-, and :)
#
#===Returns:
#* String
#
    def ei(options=nil)
        known_args = [:Delimiter]
        delimiter = '-'

        if (options)
            if (!options.kind_of? Hash)
                raise ArgumentError, "Expected Hash, but #{options.class} provided."
            end
            NetAddr.validate_args(options.keys,known_args)

            if (options.has_key?(:Delimiter))
                if (options[:Delimiter] == ':')
                    delimiter = options[:Delimiter]
                end
            end
        end

        if ( self.kind_of?(NetAddr::EUI48) )
            ei = octets[3..5].join(delimiter)
        else
            ei = octets[3..7].join(delimiter)
        end

        return(ei)
    end

#==============================================================================#
# link_local()
#==============================================================================#

#===Synopsis
# Provide an IPv6 Link Local address based on the current EUI address.
#
# Example:
# addr = NetAddr::EUI.create('aabb.ccdd.eeff')
# addr.link_local() => "fe80:0000:0000:0000:aabb:ccff:fedd:eeff"
#
#===Arguments:
#* options = Hash with the following fields:
#    :Short -- if true, return IPv6 addresses in short-hand notation
#    :Objectify -- if true, return CIDR objects
#
#===Returns:
#* CIDR address String or an NetAddr::CIDR object
#
    def link_local(options=nil)
        return( self.to_ipv6('fe80::/64', options) )
    end

#==============================================================================#
# oui()
#==============================================================================#

#===Synopsis
#Returns Organizationally Unique Identifier portion of an EUI address (the vendor ID).
#The default address format is xx-xx-xx.
#
# Example:
# addr = NetAddr::EUI.create('aabb.ccdd.eeff')
# addr.oui(:Delimiter => '-') => "aa-bb-cc"
#
#===Arguments:
#* options = Hash with the following fields:
#    :Delimiter -- delimitation character. valid values are (-, and :)
#
#===Returns:
#* String
#
    def oui(options=nil)
        known_args = [:Delimiter]
        delimiter = '-'

        if (options)
            if (!options.kind_of? Hash)
                raise ArgumentError, "Expected Hash, but #{options.class} provided."
            end
            NetAddr.validate_args(options.keys,known_args)

            if (options.has_key?(:Delimiter))
                if (options[:Delimiter] == ':')
                    delimiter = options[:Delimiter]
                end
            end
        end
        oui = octets[0..2].join(delimiter)

        return(oui)
    end

#==============================================================================#
# to_i()
#==============================================================================#

#===Synopsis
#Returns the EUI as an Integer.
#
# Example:
# addr = NetAddr::EUI.create('aabb.ccdd.eeff')
# addr.to_i => 187723572702975
#
#===Arguments:
#* none
#
#===Returns:
#* Integer
#
    def to_i()
        return(@eui_i)
    end

#==============================================================================#
# to_ipv6
#==============================================================================#

#===Synopsis
# Given a valid IPv6 subnet, return an IPv6 address based on the current EUI.
#
# Example:
# addr = NetAddr::EUI.create('aabb.ccdd.eeff')
# addr.to_ipv6('3ffe::/64') => "3ffe:0000:0000:0000:a8bb:ccff:fedd:eeff"
#
#===Arguments:
#* options = Hash with the following fields:
#    :Short -- if true, return IPv6 addresses in short-hand notation
#    :Objectify -- if true, return CIDR objects
#
#===Returns:
#* IPv6 address String or an NetAddr::CIDRv6 object
#
    def to_ipv6(cidr, options=nil)
        known_args = [:Short, :Objectify]
        objectify = false
        short = false

        if ( !cidr.kind_of?(NetAddr::CIDR) )
            begin
                cidr = NetAddr::CIDR.create(cidr)
            rescue Exception => error
                raise ArgumentError, "CIDR raised the following errors: #{error}"
            end
        elsif (cidr.kind_of?(NetAddr::CIDRv4)  )
            raise ArgumentError, "Expected CIDRv6, but #{cidr.class} provided."
        end

        if (cidr.bits > 64)
            raise ValidationError, "Prefix length of provided CIDR must be /64 or less but was #{cidr.netmask}."
        end

        if (options)
            if (!options.kind_of? Hash)
                raise ArgumentError, "Expected Hash, but #{options.class} provided."
            end
            NetAddr.validate_args(options.keys,known_args)

            if (options.has_key?(:Objectify) && options[:Objectify] == true)
                objectify = true
            end

            if (options.has_key?(:Short) && options[:Short] == true)
                short = true
            end
        end

        # get integer equiv of addr. conver eui48 to eui64 if needed
        if ( self.kind_of?(NetAddr::EUI48) )
            eui_i = self.to_eui64.to_i
        else
            eui_i = self.to_i
        end
      
        # toggle u/l bit
        eui_i = eui_i ^ 0x0200000000000000

        # create ipv6 address
        ipv6 = cidr.to_i | eui_i

        if (!objectify)
            ipv6 = NetAddr.i_to_ip(ipv6, :Version => 6)
            ipv6 = NetAddr.shorten(ipv6) if (short)
        else
            ipv6 = NetAddr::CIDRv6.new(ipv6)
        end

        return(ipv6)
    end

#==============================================================================#
# to_s()
#==============================================================================#

#===Synopsis
#Returns the EUI as an unformatted String.
#
# Example:
# addr = NetAddr::EUI.create('aabb.ccdd.eeff')
# addr.to_s => "aabbccddeeff"
#
#===Arguments:
#* none
#
#===Returns:
#* String
#
    def to_s()
        return(@eui)
    end

private

#==============================================================================#
# octets()
#==============================================================================#

#Returns array with each element representing a single octet of the eui.
#
    def octets()
        return(@octets) if (@octets)

        @octets = []
        str = ''
        @eui.each_byte do |chr|
            str = str << chr
            if (str.length == 2)
                @octets.push(str)
                str = ''
            end
        end

        return(@octets)
    end

end



# EUI-48 Address - Inherits all methods from NetAddr::EUI. 
# Addresses of this class have a 24-bit OUI and a 24-bit EI.
class EUI48 < EUI

    public_class_method :new

#==============================================================================#
# to_eui64()
#==============================================================================#

#===Synopsis
#Return an EUI64 address based on the current EUI48 address.
#
# Example:
# addr = NetAddr::EUI.create('aabb.ccdd.eeff')
# addr.to_eui64 => NetAddr::EUI64
#
#===Arguments:
#* none
#
#===Returns:
#* NetAddr::EUI64 object
#
    def to_eui64()
        eui = @oui + 'fffe' + @ei
        return( NetAddr::EUI64.new(eui.to_i(16)) )
    end

end



# EUI-64 Address - Inherits all methods from NetAddr::EUI. 
# Addresses of this class have a 24-bit OUI and a 40-bit EI.
class EUI64 < EUI
    public_class_method :new
end


end # module NetAddr
__END__