construqt-ipaddress 0.8.1

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.
@@ -0,0 +1,1005 @@
1
+ require 'ipaddress/prefix'
2
+
3
+ module IPAddress;
4
+ #
5
+ # =Name
6
+ #
7
+ # IPAddress::IPv4 - IP version 4 address manipulation library
8
+ #
9
+ # =Synopsis
10
+ #
11
+ # require 'ipaddress'
12
+ #
13
+ # =Description
14
+ #
15
+ # Class IPAddress::IPv4 is used to handle IPv4 type addresses.
16
+ #
17
+ class IPv4
18
+
19
+ include IPAddress
20
+ include Enumerable
21
+ include Comparable
22
+
23
+ #
24
+ # This Hash contains the prefix values for Classful networks
25
+ #
26
+ # Note that classes C, D and E will all have a default
27
+ # prefix of /24 or 255.255.255.0
28
+ #
29
+ CLASSFUL = {
30
+ /^0../ => 8, # Class A, from 0.0.0.0 to 127.255.255.255
31
+ /^10./ => 16, # Class B, from 128.0.0.0 to 191.255.255.255
32
+ /^110/ => 24 # Class C, D and E, from 192.0.0.0 to 255.255.255.254
33
+ }
34
+
35
+ #
36
+ # Regular expression to match an IPv4 address
37
+ #
38
+ REGEXP = Regexp.new(/((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)/)
39
+
40
+ #
41
+ # Creates a new IPv4 address object.
42
+ #
43
+ # An IPv4 address can be expressed in any of the following forms:
44
+ #
45
+ # * "10.1.1.1/24": ip +address+ and +prefix+. This is the common and
46
+ # suggested way to create an object .
47
+ # * "10.1.1.1/255.255.255.0": ip +address+ and +netmask+. Although
48
+ # convenient sometimes, this format is less clear than the previous
49
+ # one.
50
+ # * "10.1.1.1": if the address alone is specified, the prefix will be
51
+ # set as default 32, also known as the host prefix
52
+ #
53
+ # Examples:
54
+ #
55
+ # # These two are the same
56
+ # ip = IPAddress::IPv4.new("10.0.0.1/24")
57
+ # ip = IPAddress("10.0.0.1/24")
58
+ #
59
+ # # These two are the same
60
+ # IPAddress::IPv4.new "10.0.0.1/8"
61
+ # IPAddress::IPv4.new "10.0.0.1/255.0.0.0"
62
+ #
63
+ def initialize(str)
64
+ ip, netmask = str.split("/")
65
+
66
+ # Check the ip and remove white space
67
+ if IPAddress.valid_ipv4?(ip)
68
+ @address = ip.strip
69
+ else
70
+ raise ArgumentError, "Invalid IP #{ip.inspect}"
71
+ end
72
+
73
+ # Check the netmask
74
+ if netmask # netmask is defined
75
+ netmask.strip!
76
+ if netmask =~ /^\d{1,2}$/ # netmask in cidr format
77
+ @prefix = Prefix32.new(netmask.to_i)
78
+ elsif IPAddress.valid_ipv4_netmask?(netmask) # netmask in IP format
79
+ @prefix = Prefix32.parse_netmask(netmask)
80
+ else # invalid netmask
81
+ raise ArgumentError, "Invalid netmask #{netmask}"
82
+ end
83
+ else # netmask is nil, reverting to defaul classful mask
84
+ @prefix = Prefix32.new(32)
85
+ end
86
+
87
+ # Array formed with the IP octets
88
+ @octets = @address.split(".").map{|i| i.to_i}
89
+ # 32 bits interger containing the address
90
+ @u32 = (@octets[0]<< 24) + (@octets[1]<< 16) + (@octets[2]<< 8) + (@octets[3])
91
+
92
+ end # def initialize
93
+
94
+ #
95
+ # Returns the address portion of the IPv4 object
96
+ # as a string.
97
+ #
98
+ # ip = IPAddress("172.16.100.4/22")
99
+ #
100
+ # ip.address
101
+ # #=> "172.16.100.4"
102
+ #
103
+ def address
104
+ @address
105
+ end
106
+
107
+ #
108
+ # Returns the prefix portion of the IPv4 object
109
+ # as a IPAddress::Prefix32 object
110
+ #
111
+ # ip = IPAddress("172.16.100.4/22")
112
+ #
113
+ # ip.prefix
114
+ # #=> 22
115
+ #
116
+ # ip.prefix.class
117
+ # #=> IPAddress::Prefix32
118
+ #
119
+ def prefix
120
+ @prefix
121
+ end
122
+
123
+ #
124
+ # Set a new prefix number for the object
125
+ #
126
+ # This is useful if you want to change the prefix
127
+ # to an object created with IPv4::parse_u32 or
128
+ # if the object was created using the classful
129
+ # mask.
130
+ #
131
+ # ip = IPAddress("172.16.100.4")
132
+ #
133
+ # puts ip
134
+ # #=> 172.16.100.4/16
135
+ #
136
+ # ip.prefix = 22
137
+ #
138
+ # puts ip
139
+ # #=> 172.16.100.4/22
140
+ #
141
+ def prefix=(num)
142
+ @prefix = Prefix32.new(num)
143
+ end
144
+
145
+ #
146
+ # Returns the address as an array of decimal values
147
+ #
148
+ # ip = IPAddress("172.16.100.4")
149
+ #
150
+ # ip.octets
151
+ # #=> [172, 16, 100, 4]
152
+ #
153
+ def octets
154
+ @octets
155
+ end
156
+
157
+ #
158
+ # Returns a string with the address portion of
159
+ # the IPv4 object
160
+ #
161
+ # ip = IPAddress("172.16.100.4/22")
162
+ #
163
+ # ip.to_s
164
+ # #=> "172.16.100.4"
165
+ #
166
+ def to_s
167
+ @address
168
+ end
169
+ # this exists in ipv6
170
+ alias_method :compressed, :to_s
171
+
172
+ #
173
+ # Returns a string with the IP address in canonical
174
+ # form.
175
+ #
176
+ # ip = IPAddress("172.16.100.4/22")
177
+ #
178
+ # ip.to_string
179
+ # #=> "172.16.100.4/22"
180
+ #
181
+ def to_string
182
+ "#@address/#@prefix"
183
+ end
184
+
185
+ #
186
+ # Returns the prefix as a string in IP format
187
+ #
188
+ # ip = IPAddress("172.16.100.4/22")
189
+ #
190
+ # ip.netmask
191
+ # #=> "255.255.252.0"
192
+ #
193
+ def netmask
194
+ @prefix.to_ip
195
+ end
196
+
197
+ #
198
+ # Like IPv4#prefix=, this method allow you to
199
+ # change the prefix / netmask of an IP address
200
+ # object.
201
+ #
202
+ # ip = IPAddress("172.16.100.4")
203
+ #
204
+ # puts ip
205
+ # #=> 172.16.100.4/16
206
+ #
207
+ # ip.netmask = "255.255.252.0"
208
+ #
209
+ # puts ip
210
+ # #=> 172.16.100.4/22
211
+ #
212
+ def netmask=(addr)
213
+ @prefix = Prefix32.parse_netmask(addr)
214
+ end
215
+
216
+ #
217
+ # Returns the address portion in unsigned
218
+ # 32 bits integer format.
219
+ #
220
+ # This method is identical to the C function
221
+ # inet_pton to create a 32 bits address family
222
+ # structure.
223
+ #
224
+ # ip = IPAddress("10.0.0.0/8")
225
+ #
226
+ # ip.to_i
227
+ # #=> 167772160
228
+ #
229
+ def u32
230
+ @u32
231
+ end
232
+ alias_method :to_i, :u32
233
+ alias_method :to_u32, :u32
234
+
235
+ #
236
+ # Returns the address portion of an IPv4 object
237
+ # in a network byte order format.
238
+ #
239
+ # ip = IPAddress("172.16.10.1/24")
240
+ #
241
+ # ip.data
242
+ # #=> "\254\020\n\001"
243
+ #
244
+ # It is usually used to include an IP address
245
+ # in a data packet to be sent over a socket
246
+ #
247
+ # a = Socket.open(params) # socket details here
248
+ # ip = IPAddress("10.1.1.0/24")
249
+ # binary_data = ["Address: "].pack("a*") + ip.data
250
+ #
251
+ # # Send binary data
252
+ # a.puts binary_data
253
+ #
254
+ def data
255
+ [@u32].pack("N")
256
+ end
257
+
258
+ #
259
+ # Returns the octet specified by index
260
+ #
261
+ # ip = IPAddress("172.16.100.50/24")
262
+ #
263
+ # ip[0]
264
+ # #=> 172
265
+ # ip[1]
266
+ # #=> 16
267
+ # ip[2]
268
+ # #=> 100
269
+ # ip[3]
270
+ # #=> 50
271
+ #
272
+ def [](index)
273
+ @octets[index]
274
+ end
275
+ alias_method :octet, :[]
276
+
277
+ #
278
+ # Returns the address portion of an IP in binary format,
279
+ # as a string containing a sequence of 0 and 1
280
+ #
281
+ # ip = IPAddress("127.0.0.1")
282
+ #
283
+ # ip.bits
284
+ # #=> "01111111000000000000000000000001"
285
+ #
286
+ def bits
287
+ data.unpack("B*").first
288
+ end
289
+
290
+ #
291
+ # Returns the broadcast address for the given IP.
292
+ #
293
+ # ip = IPAddress("172.16.10.64/24")
294
+ #
295
+ # ip.broadcast.to_s
296
+ # #=> "172.16.10.255"
297
+ #
298
+ def broadcast
299
+ self.class.parse_u32(broadcast_u32, @prefix)
300
+ end
301
+
302
+ #
303
+ # Checks if the IP address is actually a network
304
+ #
305
+ # ip = IPAddress("172.16.10.64/24")
306
+ #
307
+ # ip.network?
308
+ # #=> false
309
+ #
310
+ # ip = IPAddress("172.16.10.64/26")
311
+ #
312
+ # ip.network?
313
+ # #=> true
314
+ #
315
+ def network?
316
+ (@prefix < 32) && (@u32 | @prefix.to_u32 == @prefix.to_u32)
317
+ end
318
+
319
+ #
320
+ # Returns a new IPv4 object with the network number
321
+ # for the given IP.
322
+ #
323
+ # ip = IPAddress("172.16.10.64/24")
324
+ #
325
+ # ip.network.to_s
326
+ # #=> "172.16.10.0"
327
+ #
328
+ def network
329
+ self.class.parse_u32(network_u32, @prefix)
330
+ end
331
+
332
+ #
333
+ # Returns a new IPv4 object with the
334
+ # first host IP address in the range.
335
+ #
336
+ # Example: given the 192.168.100.0/24 network, the first
337
+ # host IP address is 192.168.100.1.
338
+ #
339
+ # ip = IPAddress("192.168.100.0/24")
340
+ #
341
+ # ip.first.to_s
342
+ # #=> "192.168.100.1"
343
+ #
344
+ # The object IP doesn't need to be a network: the method
345
+ # automatically gets the network number from it
346
+ #
347
+ # ip = IPAddress("192.168.100.50/24")
348
+ #
349
+ # ip.first.to_s
350
+ # #=> "192.168.100.1"
351
+ #
352
+ def first
353
+ self.class.parse_u32(network_u32+1, @prefix)
354
+ end
355
+
356
+ #
357
+ # Like its sibling method IPv4#first, this method
358
+ # returns a new IPv4 object with the
359
+ # last host IP address in the range.
360
+ #
361
+ # Example: given the 192.168.100.0/24 network, the last
362
+ # host IP address is 192.168.100.254
363
+ #
364
+ # ip = IPAddress("192.168.100.0/24")
365
+ #
366
+ # ip.last.to_s
367
+ # #=> "192.168.100.254"
368
+ #
369
+ # The object IP doesn't need to be a network: the method
370
+ # automatically gets the network number from it
371
+ #
372
+ # ip = IPAddress("192.168.100.50/24")
373
+ #
374
+ # ip.last.to_s
375
+ # #=> "192.168.100.254"
376
+ #
377
+ def last
378
+ self.class.parse_u32(broadcast_u32-1, @prefix)
379
+ end
380
+
381
+ #
382
+ # Iterates over all the hosts IP addresses for the given
383
+ # network (or IP address).
384
+ #
385
+ # ip = IPAddress("10.0.0.1/29")
386
+ #
387
+ # ip.each_host do |i|
388
+ # p i.to_s
389
+ # end
390
+ # #=> "10.0.0.1"
391
+ # #=> "10.0.0.2"
392
+ # #=> "10.0.0.3"
393
+ # #=> "10.0.0.4"
394
+ # #=> "10.0.0.5"
395
+ # #=> "10.0.0.6"
396
+ #
397
+ def each_host
398
+ (network_u32+1..broadcast_u32-1).each do |i|
399
+ yield self.class.parse_u32(i, @prefix)
400
+ end
401
+ end
402
+
403
+ #
404
+ # Iterates over all the IP addresses for the given
405
+ # network (or IP address).
406
+ #
407
+ # The object yielded is a new IPv4 object created
408
+ # from the iteration.
409
+ #
410
+ # ip = IPAddress("10.0.0.1/29")
411
+ #
412
+ # ip.each do |i|
413
+ # p i.address
414
+ # end
415
+ # #=> "10.0.0.0"
416
+ # #=> "10.0.0.1"
417
+ # #=> "10.0.0.2"
418
+ # #=> "10.0.0.3"
419
+ # #=> "10.0.0.4"
420
+ # #=> "10.0.0.5"
421
+ # #=> "10.0.0.6"
422
+ # #=> "10.0.0.7"
423
+ #
424
+ def each
425
+ (network_u32..broadcast_u32).each do |i|
426
+ yield self.class.parse_u32(i, @prefix)
427
+ end
428
+ end
429
+
430
+ #
431
+ # Spaceship operator to compare IPv4 objects
432
+ #
433
+ # Comparing IPv4 addresses is useful to ordinate
434
+ # them into lists that match our intuitive
435
+ # perception of ordered IP addresses.
436
+ #
437
+ # The first comparison criteria is the u32 value.
438
+ # For example, 10.100.100.1 will be considered
439
+ # to be less than 172.16.0.1, because, in a ordered list,
440
+ # we expect 10.100.100.1 to come before 172.16.0.1.
441
+ #
442
+ # The second criteria, in case two IPv4 objects
443
+ # have identical addresses, is the prefix. An higher
444
+ # prefix will be considered greater than a lower
445
+ # prefix. This is because we expect to see
446
+ # 10.100.100.0/24 come before 10.100.100.0/25.
447
+ #
448
+ # Example:
449
+ #
450
+ # ip1 = IPAddress "10.100.100.1/8"
451
+ # ip2 = IPAddress "172.16.0.1/16"
452
+ # ip3 = IPAddress "10.100.100.1/16"
453
+ #
454
+ # ip1 < ip2
455
+ # #=> true
456
+ # ip1 > ip3
457
+ # #=> false
458
+ #
459
+ # [ip1,ip2,ip3].sort.map{|i| i.to_string}
460
+ # #=> ["10.100.100.1/8","10.100.100.1/16","172.16.0.1/16"]
461
+ #
462
+ def <=>(oth)
463
+ return prefix <=> oth.prefix if to_u32 == oth.to_u32
464
+ to_u32 <=> oth.to_u32
465
+ end
466
+
467
+ #
468
+ # Returns the number of IP addresses included
469
+ # in the network. It also counts the network
470
+ # address and the broadcast address.
471
+ #
472
+ # ip = IPAddress("10.0.0.1/29")
473
+ #
474
+ # ip.size
475
+ # #=> 8
476
+ #
477
+ def size
478
+ 2 ** @prefix.host_prefix
479
+ end
480
+
481
+ #
482
+ # Returns an array with the IP addresses of
483
+ # all the hosts in the network.
484
+ #
485
+ # ip = IPAddress("10.0.0.1/29")
486
+ #
487
+ # ip.hosts.map {|i| i.address}
488
+ # #=> ["10.0.0.1",
489
+ # #=> "10.0.0.2",
490
+ # #=> "10.0.0.3",
491
+ # #=> "10.0.0.4",
492
+ # #=> "10.0.0.5",
493
+ # #=> "10.0.0.6"]
494
+ #
495
+ def hosts
496
+ to_a[1..-2]
497
+ end
498
+
499
+ #
500
+ # Returns the network number in Unsigned 32bits format
501
+ #
502
+ # ip = IPAddress("10.0.0.1/29")
503
+ #
504
+ # ip.network_u32
505
+ # #=> 167772160
506
+ #
507
+ def network_u32
508
+ @u32 & @prefix.to_u32
509
+ end
510
+
511
+ #
512
+ # Returns the broadcast address in Unsigned 32bits format
513
+ #
514
+ # ip = IPaddress("10.0.0.1/29")
515
+ #
516
+ # ip.broadcast_u32
517
+ # #=> 167772167
518
+ #
519
+ def broadcast_u32
520
+ network_u32 + size - 1
521
+ end
522
+
523
+ #
524
+ # Checks whether a subnet includes the given IP address.
525
+ #
526
+ # Accepts an IPAddress::IPv4 object.
527
+ #
528
+ # ip = IPAddress("192.168.10.100/24")
529
+ #
530
+ # addr = IPAddress("192.168.10.102/24")
531
+ #
532
+ # ip.include? addr
533
+ # #=> true
534
+ #
535
+ # ip.include? IPAddress("172.16.0.48/16")
536
+ # #=> false
537
+ #
538
+ def include?(oth)
539
+ @prefix <= oth.prefix and network_u32 == (oth.to_u32 & @prefix.to_u32)
540
+ end
541
+
542
+ #
543
+ # Checks whether a subnet includes all the
544
+ # given IPv4 objects.
545
+ #
546
+ # ip = IPAddress("192.168.10.100/24")
547
+ #
548
+ # addr1 = IPAddress("192.168.10.102/24")
549
+ # addr2 = IPAddress("192.168.10.103/24")
550
+ #
551
+ # ip.include_all?(addr1,addr2)
552
+ # #=> true
553
+ #
554
+ def include_all?(*others)
555
+ others.all? {|oth| include?(oth)}
556
+ end
557
+
558
+ #
559
+ # Checks if an IPv4 address objects belongs
560
+ # to a private network RFC1918
561
+ #
562
+ # Example:
563
+ #
564
+ # ip = IPAddress "10.1.1.1/24"
565
+ # ip.private?
566
+ # #=> true
567
+ #
568
+ def private?
569
+ [self.class.new("10.0.0.0/8"),
570
+ self.class.new("172.16.0.0/12"),
571
+ self.class.new("192.168.0.0/16")].any? {|i| i.include? self}
572
+ end
573
+
574
+ #
575
+ # Returns the IP address in in-addr.arpa format
576
+ # for DNS lookups
577
+ #
578
+ # ip = IPAddress("172.16.100.50/24")
579
+ #
580
+ # ip.reverse
581
+ # #=> "50.100.16.172.in-addr.arpa"
582
+ #
583
+ def reverse
584
+ @octets.reverse.join(".") + ".in-addr.arpa"
585
+ end
586
+ alias_method :arpa, :reverse
587
+
588
+ #
589
+ # Returns the IP address in in-addr.arpa format
590
+ # for DNS Domain definition entries like SOA Records
591
+ #
592
+ # ip = IPAddress("172.17.100.50/15")
593
+ #
594
+ # ip.rev_domains
595
+ # #=> ["16.172.in-addr.arpa","17.172.in-addr.arpa"]
596
+ #
597
+ def rev_domains
598
+ net = [ network ]
599
+ cut = 4-(prefix.to_i/8)
600
+ if prefix.to_i <= 8 # edge case class a
601
+ cut = 3
602
+ elsif prefix.to_i > 24 # edge case class c
603
+ cut = 1
604
+ net = [network.supernet(24)]
605
+ end
606
+ if prefix.to_i < 24 and (prefix.to_i % 8) != 0 # case class less
607
+ cut = 3-(prefix.to_i/8)
608
+ net = network.subnet(prefix.to_i+1)
609
+ end
610
+ net.map do |n|
611
+ n.reverse.split('.')[cut..-1].join('.')
612
+ end
613
+ end
614
+
615
+ #
616
+ # Splits a network into different subnets
617
+ #
618
+ # If the IP Address is a network, it can be divided into
619
+ # multiple networks. If +self+ is not a network, this
620
+ # method will calculate the network from the IP and then
621
+ # subnet it.
622
+ #
623
+ # If +subnets+ is an power of two number, the resulting
624
+ # networks will be divided evenly from the supernet.
625
+ #
626
+ # network = IPAddress("172.16.10.0/24")
627
+ #
628
+ # network / 4 # implies map{|i| i.to_string}
629
+ # #=> ["172.16.10.0/26",
630
+ # "172.16.10.64/26",
631
+ # "172.16.10.128/26",
632
+ # "172.16.10.192/26"]
633
+ #
634
+ # If +num+ is any other number, the supernet will be
635
+ # divided into some networks with a even number of hosts and
636
+ # other networks with the remaining addresses.
637
+ #
638
+ # network = IPAddress("172.16.10.0/24")
639
+ #
640
+ # network / 3 # implies map{|i| i.to_string}
641
+ # #=> ["172.16.10.0/26",
642
+ # "172.16.10.64/26",
643
+ # "172.16.10.128/25"]
644
+ #
645
+ # Returns an array of IPv4 objects
646
+ #
647
+ def split(subnets=2)
648
+ unless (1..(2**@prefix.host_prefix)).include? subnets
649
+ raise ArgumentError, "Value #{subnets} out of range"
650
+ end
651
+ networks = subnet(newprefix(subnets))
652
+ until networks.size == subnets
653
+ networks = sum_first_found(networks)
654
+ end
655
+ return networks
656
+ end
657
+ alias_method :/, :split
658
+
659
+ #
660
+ # Returns a new IPv4 object from the supernetting
661
+ # of the instance network.
662
+ #
663
+ # Supernetting is similar to subnetting, except
664
+ # that you getting as a result a network with a
665
+ # smaller prefix (bigger host space). For example,
666
+ # given the network
667
+ #
668
+ # ip = IPAddress("172.16.10.0/24")
669
+ #
670
+ # you can supernet it with a new /23 prefix
671
+ #
672
+ # ip.supernet(23).to_string
673
+ # #=> "172.16.10.0/23"
674
+ #
675
+ # However if you supernet it with a /22 prefix, the
676
+ # network address will change:
677
+ #
678
+ # ip.supernet(22).to_string
679
+ # #=> "172.16.8.0/22"
680
+ #
681
+ # If +new_prefix+ is less than 1, returns 0.0.0.0/0
682
+ #
683
+ def supernet(new_prefix)
684
+ raise ArgumentError, "New prefix must be smaller than existing prefix" if new_prefix >= @prefix.to_i
685
+ return self.class.new("0.0.0.0/0") if new_prefix < 1
686
+ return self.class.new(@address+"/#{new_prefix}").network
687
+ end
688
+
689
+ #
690
+ # This method implements the subnetting function
691
+ # similar to the one described in RFC3531.
692
+ #
693
+ # By specifying a new prefix, the method calculates
694
+ # the network number for the given IPv4 object
695
+ # and calculates the subnets associated to the new
696
+ # prefix.
697
+ #
698
+ # For example, given the following network:
699
+ #
700
+ # ip = IPAddress "172.16.10.0/24"
701
+ #
702
+ # we can calculate the subnets with a /26 prefix
703
+ #
704
+ # ip.subnets(26).map{&:to_string)
705
+ # #=> ["172.16.10.0/26", "172.16.10.64/26",
706
+ # "172.16.10.128/26", "172.16.10.192/26"]
707
+ #
708
+ # The resulting number of subnets will of course always be
709
+ # a power of two.
710
+ #
711
+ def subnet(subprefix)
712
+ unless ((@prefix.to_i)..32).include? subprefix
713
+ raise ArgumentError, "New prefix must be between #@prefix and 32"
714
+ end
715
+ Array.new(2**(subprefix-@prefix.to_i)) do |i|
716
+ self.class.parse_u32(network_u32+(i*(2**(32-subprefix))), subprefix)
717
+ end
718
+ end
719
+
720
+ #
721
+ # Returns the difference between two IP addresses
722
+ # in unsigned int 32 bits format
723
+ #
724
+ # Example:
725
+ #
726
+ # ip1 = IPAddress("172.16.10.0/24")
727
+ # ip2 = IPAddress("172.16.11.0/24")
728
+ #
729
+ # puts ip1 - ip2
730
+ # #=> 256
731
+ #
732
+ def -(oth)
733
+ return (to_u32 - oth.to_u32).abs
734
+ end
735
+
736
+ #
737
+ # Returns a new IPv4 object which is the result
738
+ # of the summarization, if possible, of the two
739
+ # objects
740
+ #
741
+ # Example:
742
+ #
743
+ # ip1 = IPAddress("172.16.10.1/24")
744
+ # ip2 = IPAddress("172.16.11.2/24")
745
+ #
746
+ # p (ip1 + ip2).map {|i| i.to_string}
747
+ # #=> ["172.16.10.0/23"]
748
+ #
749
+ # If the networks are not contiguous, returns
750
+ # the two network numbers from the objects
751
+ #
752
+ # ip1 = IPAddress("10.0.0.1/24")
753
+ # ip2 = IPAddress("10.0.2.1/24")
754
+ #
755
+ # p (ip1 + ip2).map {|i| i.to_string}
756
+ # #=> ["10.0.0.0/24","10.0.2.0/24"]
757
+ #
758
+ def +(oth)
759
+ IPAddress.summarize([self,oth])
760
+ end
761
+
762
+ #
763
+ # Checks whether the ip address belongs to a
764
+ # RFC 791 CLASS A network, no matter
765
+ # what the subnet mask is.
766
+ #
767
+ # Example:
768
+ #
769
+ # ip = IPAddress("10.0.0.1/24")
770
+ #
771
+ # ip.a?
772
+ # #=> true
773
+ #
774
+ def a?
775
+ CLASSFUL.key(8) === bits
776
+ end
777
+
778
+ #
779
+ # Checks whether the ip address belongs to a
780
+ # RFC 791 CLASS B network, no matter
781
+ # what the subnet mask is.
782
+ #
783
+ # Example:
784
+ #
785
+ # ip = IPAddress("172.16.10.1/24")
786
+ #
787
+ # ip.b?
788
+ # #=> true
789
+ #
790
+ def b?
791
+ CLASSFUL.key(16) === bits
792
+ end
793
+
794
+ #
795
+ # Checks whether the ip address belongs to a
796
+ # RFC 791 CLASS C network, no matter
797
+ # what the subnet mask is.
798
+ #
799
+ # Example:
800
+ #
801
+ # ip = IPAddress("192.168.1.1/30")
802
+ #
803
+ # ip.c?
804
+ # #=> true
805
+ #
806
+ def c?
807
+ CLASSFUL.key(24) === bits
808
+ end
809
+
810
+ #
811
+ # Return the ip address in a format compatible
812
+ # with the IPv6 Mapped IPv4 addresses
813
+ #
814
+ # Example:
815
+ #
816
+ # ip = IPAddress("172.16.10.1/24")
817
+ #
818
+ # ip.to_ipv6
819
+ # #=> "ac10:0a01"
820
+ #
821
+ def to_ipv6
822
+ "%.4x:%.4x" % [to_u32].pack("N").unpack("nn")
823
+ end
824
+
825
+ #
826
+ # Creates a new IPv4 object from an
827
+ # unsigned 32bits integer.
828
+ #
829
+ # ip = IPAddress::IPv4::parse_u32(167772160)
830
+ #
831
+ # ip.prefix = 8
832
+ # ip.to_string
833
+ # #=> "10.0.0.0/8"
834
+ #
835
+ # The +prefix+ parameter is optional:
836
+ #
837
+ # ip = IPAddress::IPv4::parse_u32(167772160, 8)
838
+ #
839
+ # ip.to_string
840
+ # #=> "10.0.0.0/8"
841
+ #
842
+ def self.parse_u32(u32, prefix=32)
843
+ self.new([u32].pack("N").unpack("C4").join(".")+"/#{prefix}")
844
+ end
845
+
846
+ #
847
+ # Creates a new IPv4 object from binary data,
848
+ # like the one you get from a network stream.
849
+ #
850
+ # For example, on a network stream the IP 172.16.0.1
851
+ # is represented with the binary "\254\020\n\001".
852
+ #
853
+ # ip = IPAddress::IPv4::parse_data "\254\020\n\001"
854
+ # ip.prefix = 24
855
+ #
856
+ # ip.to_string
857
+ # #=> "172.16.10.1/24"
858
+ #
859
+ def self.parse_data(str, prefix=32)
860
+ self.new(str.unpack("C4").join(".")+"/#{prefix}")
861
+ end
862
+
863
+ #
864
+ # Extract an IPv4 address from a string and
865
+ # returns a new object
866
+ #
867
+ # Example:
868
+ #
869
+ # str = "foobar172.16.10.1barbaz"
870
+ # ip = IPAddress::IPv4::extract str
871
+ #
872
+ # ip.to_s
873
+ # #=> "172.16.10.1"
874
+ #
875
+ def self.extract(str)
876
+ self.new REGEXP.match(str).to_s
877
+ end
878
+
879
+ #
880
+ # Summarization (or aggregation) is the process when two or more
881
+ # networks are taken together to check if a supernet, including all
882
+ # and only these networks, exists. If it exists then this supernet
883
+ # is called the summarized (or aggregated) network.
884
+ #
885
+ # It is very important to understand that summarization can only
886
+ # occur if there are no holes in the aggregated network, or, in other
887
+ # words, if the given networks fill completely the address space
888
+ # of the supernet. So the two rules are:
889
+ #
890
+ # 1) The aggregate network must contain +all+ the IP addresses of the
891
+ # original networks;
892
+ # 2) The aggregate network must contain +only+ the IP addresses of the
893
+ # original networks;
894
+ #
895
+ # A few examples will help clarify the above. Let's consider for
896
+ # instance the following two networks:
897
+ #
898
+ # ip1 = IPAddress("172.16.10.0/24")
899
+ # ip2 = IPAddress("172.16.11.0/24")
900
+ #
901
+ # These two networks can be expressed using only one IP address
902
+ # network if we change the prefix. Let Ruby do the work:
903
+ #
904
+ # IPAddress::IPv4::summarize(ip1,ip2).to_s
905
+ # #=> "172.16.10.0/23"
906
+ #
907
+ # We note how the network "172.16.10.0/23" includes all the addresses
908
+ # specified in the above networks, and (more important) includes
909
+ # ONLY those addresses.
910
+ #
911
+ # If we summarized +ip1+ and +ip2+ with the following network:
912
+ #
913
+ # "172.16.0.0/16"
914
+ #
915
+ # we would have satisfied rule #1 above, but not rule #2. So "172.16.0.0/16"
916
+ # is not an aggregate network for +ip1+ and +ip2+.
917
+ #
918
+ # If it's not possible to compute a single aggregated network for all the
919
+ # original networks, the method returns an array with all the aggregate
920
+ # networks found. For example, the following four networks can be
921
+ # aggregated in a single /22:
922
+ #
923
+ # ip1 = IPAddress("10.0.0.1/24")
924
+ # ip2 = IPAddress("10.0.1.1/24")
925
+ # ip3 = IPAddress("10.0.2.1/24")
926
+ # ip4 = IPAddress("10.0.3.1/24")
927
+ #
928
+ # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_string
929
+ # #=> "10.0.0.0/22",
930
+ #
931
+ # But the following networks can't be summarized in a single network:
932
+ #
933
+ # ip1 = IPAddress("10.0.1.1/24")
934
+ # ip2 = IPAddress("10.0.2.1/24")
935
+ # ip3 = IPAddress("10.0.3.1/24")
936
+ # ip4 = IPAddress("10.0.4.1/24")
937
+ #
938
+ # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
939
+ # #=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
940
+ #
941
+ def self.summarize(*args)
942
+ IPAddress.summarize(args)
943
+ end
944
+
945
+ #
946
+ # Creates a new IPv4 address object by parsing the
947
+ # address in a classful way.
948
+ #
949
+ # Classful addresses have a fixed netmask based on the
950
+ # class they belong to:
951
+ #
952
+ # * Class A, from 0.0.0.0 to 127.255.255.255
953
+ # * Class B, from 128.0.0.0 to 191.255.255.255
954
+ # * Class C, D and E, from 192.0.0.0 to 255.255.255.254
955
+ #
956
+ # Example:
957
+ #
958
+ # ip = IPAddress::IPv4.parse_classful "10.0.0.1"
959
+ #
960
+ # ip.netmask
961
+ # #=> "255.0.0.0"
962
+ # ip.a?
963
+ # #=> true
964
+ #
965
+ # Note that classes C, D and E will all have a default
966
+ # prefix of /24 or 255.255.255.0
967
+ #
968
+ def self.parse_classful(ip)
969
+ if IPAddress.valid_ipv4?(ip)
970
+ address = ip.strip
971
+ else
972
+ raise ArgumentError, "Invalid IP #{ip.inspect}"
973
+ end
974
+ prefix = CLASSFUL.find{|h,k| h === ("%.8b" % address.to_i)}.last
975
+ self.new "#{address}/#{prefix}"
976
+ end
977
+
978
+ #
979
+ # private methods
980
+ #
981
+ private
982
+
983
+ def newprefix(num)
984
+ num.upto(32) do |i|
985
+ if (a = Math::log2(i).to_i) == Math::log2(i)
986
+ return @prefix + a
987
+ end
988
+ end
989
+ end
990
+
991
+ def sum_first_found(arr)
992
+ dup = arr.dup.reverse
993
+ dup.each_with_index do |obj,i|
994
+ a = [self.class.summarize(obj,dup[i+1])].flatten
995
+ if a.size == 1
996
+ dup[i..i+1] = a
997
+ return dup.reverse
998
+ end
999
+ end
1000
+ return dup.reverse
1001
+ end
1002
+
1003
+ end # class IPv4
1004
+ end # module IPAddress
1005
+