construqt-ipaddress 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+