ipaddress_link_local 0.8.3

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,1114 @@
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
+
170
+ #
171
+ # Returns a string with the IP address in canonical
172
+ # form.
173
+ #
174
+ # ip = IPAddress("172.16.100.4/22")
175
+ #
176
+ # ip.to_string
177
+ # #=> "172.16.100.4/22"
178
+ #
179
+ def to_string
180
+ "#@address/#@prefix"
181
+ end
182
+
183
+ #
184
+ # Returns the prefix as a string in IP format
185
+ #
186
+ # ip = IPAddress("172.16.100.4/22")
187
+ #
188
+ # ip.netmask
189
+ # #=> "255.255.252.0"
190
+ #
191
+ def netmask
192
+ @prefix.to_ip
193
+ end
194
+
195
+ #
196
+ # Like IPv4#prefix=, this method allow you to
197
+ # change the prefix / netmask of an IP address
198
+ # object.
199
+ #
200
+ # ip = IPAddress("172.16.100.4")
201
+ #
202
+ # puts ip
203
+ # #=> 172.16.100.4/16
204
+ #
205
+ # ip.netmask = "255.255.252.0"
206
+ #
207
+ # puts ip
208
+ # #=> 172.16.100.4/22
209
+ #
210
+ def netmask=(addr)
211
+ @prefix = Prefix32.parse_netmask(addr)
212
+ end
213
+
214
+ #
215
+ # Returns the address portion in unsigned
216
+ # 32 bits integer format.
217
+ #
218
+ # This method is identical to the C function
219
+ # inet_pton to create a 32 bits address family
220
+ # structure.
221
+ #
222
+ # ip = IPAddress("10.0.0.0/8")
223
+ #
224
+ # ip.to_i
225
+ # #=> 167772160
226
+ #
227
+ def u32
228
+ @u32
229
+ end
230
+ alias_method :to_i, :u32
231
+ alias_method :to_u32, :u32
232
+
233
+ #
234
+ # Returns the address portion in
235
+ # hex
236
+ #
237
+ # ip = IPAddress("10.0.0.0")
238
+ #
239
+ # ip.to_h
240
+ # #=> 0a000000
241
+ #
242
+ def hex(space=true)
243
+ "%.4x%.4x" % [to_u32].pack("N").unpack("nn")
244
+ end
245
+ alias_method :to_h, :hex
246
+ alias_method :to_hex, :hex
247
+
248
+ #
249
+ # Returns the address portion of an IPv4 object
250
+ # in a network byte order format.
251
+ #
252
+ # ip = IPAddress("172.16.10.1/24")
253
+ #
254
+ # ip.data
255
+ # #=> "\254\020\n\001"
256
+ #
257
+ # It is usually used to include an IP address
258
+ # in a data packet to be sent over a socket
259
+ #
260
+ # a = Socket.open(params) # socket details here
261
+ # ip = IPAddress("10.1.1.0/24")
262
+ # binary_data = ["Address: "].pack("a*") + ip.data
263
+ #
264
+ # # Send binary data
265
+ # a.puts binary_data
266
+ #
267
+ def data
268
+ [@u32].pack("N")
269
+ end
270
+
271
+ #
272
+ # Returns the octet specified by index
273
+ #
274
+ # ip = IPAddress("172.16.100.50/24")
275
+ #
276
+ # ip[0]
277
+ # #=> 172
278
+ # ip[1]
279
+ # #=> 16
280
+ # ip[2]
281
+ # #=> 100
282
+ # ip[3]
283
+ # #=> 50
284
+ #
285
+ def [](index)
286
+ @octets[index]
287
+ end
288
+ alias_method :octet, :[]
289
+
290
+ #
291
+ # Updated the octet specified at index
292
+ #
293
+ # ip = IPAddress("172.16.100.50/24")
294
+ # ip[2] = 200
295
+ #
296
+ # #=> #<IPAddress::IPv4:0x00000000000000 @address="172.16.200.1",
297
+ # #=> @prefix=32, @octets=[172, 16, 200, 1], @u32=2886780929>
298
+ #
299
+ def []=(index, value)
300
+ @octets[index] = value.to_i
301
+ initialize("#{@octets.join('.')}/#{prefix}")
302
+ end
303
+ alias_method :octet=, :[]=
304
+
305
+ #
306
+ # Returns the address portion of an IP in binary format,
307
+ # as a string containing a sequence of 0 and 1
308
+ #
309
+ # ip = IPAddress("127.0.0.1")
310
+ #
311
+ # ip.bits
312
+ # #=> "01111111000000000000000000000001"
313
+ #
314
+ def bits
315
+ data.unpack("B*").first
316
+ end
317
+
318
+ #
319
+ # Returns the broadcast address for the given IP.
320
+ #
321
+ # ip = IPAddress("172.16.10.64/24")
322
+ #
323
+ # ip.broadcast.to_s
324
+ # #=> "172.16.10.255"
325
+ #
326
+ def broadcast
327
+ case
328
+ when prefix <= 30
329
+ self.class.parse_u32(broadcast_u32, @prefix)
330
+ when prefix == 31
331
+ self.class.parse_u32(-1, @prefix)
332
+ when prefix == 32
333
+ return self
334
+ end
335
+ end
336
+
337
+ #
338
+ # Checks if the IP address is actually a network
339
+ #
340
+ # ip = IPAddress("172.16.10.64/24")
341
+ #
342
+ # ip.network?
343
+ # #=> false
344
+ #
345
+ # ip = IPAddress("172.16.10.64/26")
346
+ #
347
+ # ip.network?
348
+ # #=> true
349
+ #
350
+ def network?
351
+ (@prefix < 32) && (@u32 | @prefix.to_u32 == @prefix.to_u32)
352
+ end
353
+
354
+ #
355
+ # Returns a new IPv4 object with the network number
356
+ # for the given IP.
357
+ #
358
+ # ip = IPAddress("172.16.10.64/24")
359
+ #
360
+ # ip.network.to_s
361
+ # #=> "172.16.10.0"
362
+ #
363
+ def network
364
+ self.class.parse_u32(network_u32, @prefix)
365
+ end
366
+
367
+ #
368
+ # Returns a new IPv4 object with the
369
+ # first host IP address in the range.
370
+ #
371
+ # Example: given the 192.168.100.0/24 network, the first
372
+ # host IP address is 192.168.100.1.
373
+ #
374
+ # ip = IPAddress("192.168.100.0/24")
375
+ #
376
+ # ip.first.to_s
377
+ # #=> "192.168.100.1"
378
+ #
379
+ # The object IP doesn't need to be a network: the method
380
+ # automatically gets the network number from it
381
+ #
382
+ # ip = IPAddress("192.168.100.50/24")
383
+ #
384
+ # ip.first.to_s
385
+ # #=> "192.168.100.1"
386
+ #
387
+ def first
388
+ case
389
+ when prefix <= 30
390
+ self.class.parse_u32(network_u32+1, @prefix)
391
+ when prefix == 31
392
+ self.class.parse_u32(network_u32, @prefix)
393
+ when prefix == 32
394
+ return self
395
+ end
396
+ end
397
+
398
+ #
399
+ # Like its sibling method IPv4#first, this method
400
+ # returns a new IPv4 object with the
401
+ # last host IP address in the range.
402
+ #
403
+ # Example: given the 192.168.100.0/24 network, the last
404
+ # host IP address is 192.168.100.254
405
+ #
406
+ # ip = IPAddress("192.168.100.0/24")
407
+ #
408
+ # ip.last.to_s
409
+ # #=> "192.168.100.254"
410
+ #
411
+ # The object IP doesn't need to be a network: the method
412
+ # automatically gets the network number from it
413
+ #
414
+ # ip = IPAddress("192.168.100.50/24")
415
+ #
416
+ # ip.last.to_s
417
+ # #=> "192.168.100.254"
418
+ #
419
+ def last
420
+ case
421
+ when prefix <= 30
422
+ self.class.parse_u32(broadcast_u32-1, @prefix)
423
+ when prefix == 31
424
+ self.class.parse_u32(broadcast_u32, @prefix)
425
+ when prefix == 32
426
+ return self
427
+ end
428
+ end
429
+
430
+ #
431
+ # Iterates over all the hosts IP addresses for the given
432
+ # network (or IP address).
433
+ #
434
+ # ip = IPAddress("10.0.0.1/29")
435
+ #
436
+ # ip.each_host do |i|
437
+ # p i.to_s
438
+ # end
439
+ # #=> "10.0.0.1"
440
+ # #=> "10.0.0.2"
441
+ # #=> "10.0.0.3"
442
+ # #=> "10.0.0.4"
443
+ # #=> "10.0.0.5"
444
+ # #=> "10.0.0.6"
445
+ #
446
+ def each_host
447
+ (network_u32+1..broadcast_u32-1).each do |i|
448
+ yield self.class.parse_u32(i, @prefix)
449
+ end
450
+ end
451
+
452
+ #
453
+ # Iterates over all the IP addresses for the given
454
+ # network (or IP address).
455
+ #
456
+ # The object yielded is a new IPv4 object created
457
+ # from the iteration.
458
+ #
459
+ # ip = IPAddress("10.0.0.1/29")
460
+ #
461
+ # ip.each do |i|
462
+ # p i.address
463
+ # end
464
+ # #=> "10.0.0.0"
465
+ # #=> "10.0.0.1"
466
+ # #=> "10.0.0.2"
467
+ # #=> "10.0.0.3"
468
+ # #=> "10.0.0.4"
469
+ # #=> "10.0.0.5"
470
+ # #=> "10.0.0.6"
471
+ # #=> "10.0.0.7"
472
+ #
473
+ def each
474
+ (network_u32..broadcast_u32).each do |i|
475
+ yield self.class.parse_u32(i, @prefix)
476
+ end
477
+ end
478
+
479
+ #
480
+ # Spaceship operator to compare IPv4 objects
481
+ #
482
+ # Comparing IPv4 addresses is useful to ordinate
483
+ # them into lists that match our intuitive
484
+ # perception of ordered IP addresses.
485
+ #
486
+ # The first comparison criteria is the u32 value.
487
+ # For example, 10.100.100.1 will be considered
488
+ # to be less than 172.16.0.1, because, in a ordered list,
489
+ # we expect 10.100.100.1 to come before 172.16.0.1.
490
+ #
491
+ # The second criteria, in case two IPv4 objects
492
+ # have identical addresses, is the prefix. An higher
493
+ # prefix will be considered greater than a lower
494
+ # prefix. This is because we expect to see
495
+ # 10.100.100.0/24 come before 10.100.100.0/25.
496
+ #
497
+ # Example:
498
+ #
499
+ # ip1 = IPAddress "10.100.100.1/8"
500
+ # ip2 = IPAddress "172.16.0.1/16"
501
+ # ip3 = IPAddress "10.100.100.1/16"
502
+ #
503
+ # ip1 < ip2
504
+ # #=> true
505
+ # ip1 > ip3
506
+ # #=> false
507
+ #
508
+ # [ip1,ip2,ip3].sort.map{|i| i.to_string}
509
+ # #=> ["10.100.100.1/8","10.100.100.1/16","172.16.0.1/16"]
510
+ #
511
+ def <=>(oth)
512
+ return prefix <=> oth.prefix if to_u32 == oth.to_u32
513
+ to_u32 <=> oth.to_u32
514
+ end
515
+
516
+ #
517
+ # Returns the number of IP addresses included
518
+ # in the network. It also counts the network
519
+ # address and the broadcast address.
520
+ #
521
+ # ip = IPAddress("10.0.0.1/29")
522
+ #
523
+ # ip.size
524
+ # #=> 8
525
+ #
526
+ def size
527
+ 2 ** @prefix.host_prefix
528
+ end
529
+
530
+ #
531
+ # Returns an array with the IP addresses of
532
+ # all the hosts in the network.
533
+ #
534
+ # ip = IPAddress("10.0.0.1/29")
535
+ #
536
+ # ip.hosts.map {|i| i.address}
537
+ # #=> ["10.0.0.1",
538
+ # #=> "10.0.0.2",
539
+ # #=> "10.0.0.3",
540
+ # #=> "10.0.0.4",
541
+ # #=> "10.0.0.5",
542
+ # #=> "10.0.0.6"]
543
+ #
544
+ def hosts
545
+ to_a[1..-2]
546
+ end
547
+
548
+ #
549
+ # Returns the network number in Unsigned 32bits format
550
+ #
551
+ # ip = IPAddress("10.0.0.1/29")
552
+ #
553
+ # ip.network_u32
554
+ # #=> 167772160
555
+ #
556
+ def network_u32
557
+ @u32 & @prefix.to_u32
558
+ end
559
+
560
+ #
561
+ # Returns the broadcast address in Unsigned 32bits format
562
+ #
563
+ # ip = IPaddress("10.0.0.1/29")
564
+ #
565
+ # ip.broadcast_u32
566
+ # #=> 167772167
567
+ #
568
+ def broadcast_u32
569
+ network_u32 + size - 1
570
+ end
571
+
572
+ #
573
+ # Checks whether a subnet includes the given IP address.
574
+ #
575
+ # Accepts an IPAddress::IPv4 object.
576
+ #
577
+ # ip = IPAddress("192.168.10.100/24")
578
+ #
579
+ # addr = IPAddress("192.168.10.102/24")
580
+ #
581
+ # ip.include? addr
582
+ # #=> true
583
+ #
584
+ # ip.include? IPAddress("172.16.0.48/16")
585
+ # #=> false
586
+ #
587
+ def include?(oth)
588
+ @prefix <= oth.prefix and network_u32 == (oth.to_u32 & @prefix.to_u32)
589
+ end
590
+
591
+ #
592
+ # Checks whether a subnet includes all the
593
+ # given IPv4 objects.
594
+ #
595
+ # ip = IPAddress("192.168.10.100/24")
596
+ #
597
+ # addr1 = IPAddress("192.168.10.102/24")
598
+ # addr2 = IPAddress("192.168.10.103/24")
599
+ #
600
+ # ip.include_all?(addr1,addr2)
601
+ # #=> true
602
+ #
603
+ def include_all?(*others)
604
+ others.all? {|oth| include?(oth)}
605
+ end
606
+
607
+ #
608
+ # Checks if an IPv4 address objects belongs
609
+ # to a private network RFC1918
610
+ #
611
+ # Example:
612
+ #
613
+ # ip = IPAddress "10.1.1.1/24"
614
+ # ip.private?
615
+ # #=> true
616
+ #
617
+ def private?
618
+ [self.class.new("10.0.0.0/8"),
619
+ self.class.new("172.16.0.0/12"),
620
+ self.class.new("192.168.0.0/16")].any? {|i| i.include? self}
621
+ end
622
+
623
+ #
624
+ # Checks if an IPv4 address objects belongs
625
+ # to a multicast network RFC3171
626
+ #
627
+ # Example:
628
+ #
629
+ # ip = IPAddress "224.0.0.0/4"
630
+ # ip.multicast?
631
+ # #=> true
632
+ #
633
+ def multicast?
634
+ [self.class.new("224.0.0.0/4")].any? {|i| i.include? self}
635
+ end
636
+
637
+ #
638
+ # Checks if an IPv4 address objects belongs
639
+ # to a loopback network RFC1122
640
+ #
641
+ # Example:
642
+ #
643
+ # ip = IPAddress "127.0.0.1"
644
+ # ip.loopback?
645
+ # #=> true
646
+ #
647
+ def loopback?
648
+ [self.class.new("127.0.0.0/8")].any? {|i| i.include? self}
649
+ end
650
+
651
+ #
652
+ # Checks if an IPv4 address objects belongs
653
+ # to a link-local network RFC3927
654
+ #
655
+ # Example:
656
+ #
657
+ # ip = IPAddress "169.254.0.1"
658
+ # ip.link_local?
659
+ # #=> true
660
+ #
661
+ def link_local?
662
+ [self.class.new("169.254.0.0/16")].any? {|i| i.include? self}
663
+ end
664
+
665
+ #
666
+ # Returns the IP address in in-addr.arpa format
667
+ # for DNS lookups
668
+ #
669
+ # ip = IPAddress("172.16.100.50/24")
670
+ #
671
+ # ip.reverse
672
+ # #=> "50.100.16.172.in-addr.arpa"
673
+ #
674
+ def reverse
675
+ @octets.reverse.join(".") + ".in-addr.arpa"
676
+ end
677
+ alias_method :arpa, :reverse
678
+
679
+ #
680
+ # Return a list of IP's between @address
681
+ # and the supplied IP
682
+ #
683
+ # ip = IPAddress("172.16.100.51/32")
684
+ #
685
+ # ip.to("172.16.100.100")
686
+ # #=> ["172.16.100.51",
687
+ # #=> "172.16.100.52",
688
+ # #=> ...
689
+ # #=> "172.16.100.99",
690
+ # #=> "172.16.100.100"]
691
+ #
692
+ def to(e)
693
+ unless e.is_a? IPAddress::IPv4
694
+ e = IPv4.new(e)
695
+ end
696
+
697
+ Range.new(@u32, e.to_u32).map{|i| IPAddress.ntoa(i) }
698
+ end
699
+ #
700
+ # Splits a network into different subnets
701
+ #
702
+ # If the IP Address is a network, it can be divided into
703
+ # multiple networks. If +self+ is not a network, this
704
+ # method will calculate the network from the IP and then
705
+ # subnet it.
706
+ #
707
+ # If +subnets+ is an power of two number, the resulting
708
+ # networks will be divided evenly from the supernet.
709
+ #
710
+ # network = IPAddress("172.16.10.0/24")
711
+ #
712
+ # network / 4 # implies map{|i| i.to_string}
713
+ # #=> ["172.16.10.0/26",
714
+ # #=> "172.16.10.64/26",
715
+ # #=> "172.16.10.128/26",
716
+ # #=> "172.16.10.192/26"]
717
+ #
718
+ # If +num+ is any other number, the supernet will be
719
+ # divided into some networks with a even number of hosts and
720
+ # other networks with the remaining addresses.
721
+ #
722
+ # network = IPAddress("172.16.10.0/24")
723
+ #
724
+ # network / 3 # implies map{|i| i.to_string}
725
+ # #=> ["172.16.10.0/26",
726
+ # #=> "172.16.10.64/26",
727
+ # #=> "172.16.10.128/25"]
728
+ #
729
+ # Returns an array of IPv4 objects
730
+ #
731
+ def split(subnets=2)
732
+ unless (1..(2**@prefix.host_prefix)).include? subnets
733
+ raise ArgumentError, "Value #{subnets} out of range"
734
+ end
735
+ networks = subnet(newprefix(subnets))
736
+ until networks.size == subnets
737
+ networks = sum_first_found(networks)
738
+ end
739
+ return networks
740
+ end
741
+ alias_method :/, :split
742
+
743
+ #
744
+ # Returns a new IPv4 object from the supernetting
745
+ # of the instance network.
746
+ #
747
+ # Supernetting is similar to subnetting, except
748
+ # that you getting as a result a network with a
749
+ # smaller prefix (bigger host space). For example,
750
+ # given the network
751
+ #
752
+ # ip = IPAddress("172.16.10.0/24")
753
+ #
754
+ # you can supernet it with a new /23 prefix
755
+ #
756
+ # ip.supernet(23).to_string
757
+ # #=> "172.16.10.0/23"
758
+ #
759
+ # However if you supernet it with a /22 prefix, the
760
+ # network address will change:
761
+ #
762
+ # ip.supernet(22).to_string
763
+ # #=> "172.16.8.0/22"
764
+ #
765
+ # If +new_prefix+ is less than 1, returns 0.0.0.0/0
766
+ #
767
+ def supernet(new_prefix)
768
+ raise ArgumentError, "New prefix must be smaller than existing prefix" if new_prefix >= @prefix.to_i
769
+ return self.class.new("0.0.0.0/0") if new_prefix < 1
770
+ return self.class.new(@address+"/#{new_prefix}").network
771
+ end
772
+
773
+ #
774
+ # This method implements the subnetting function
775
+ # similar to the one described in RFC3531.
776
+ #
777
+ # By specifying a new prefix, the method calculates
778
+ # the network number for the given IPv4 object
779
+ # and calculates the subnets associated to the new
780
+ # prefix.
781
+ #
782
+ # For example, given the following network:
783
+ #
784
+ # ip = IPAddress "172.16.10.0/24"
785
+ #
786
+ # we can calculate the subnets with a /26 prefix
787
+ #
788
+ # ip.subnets(26).map{&:to_string)
789
+ # #=> ["172.16.10.0/26", "172.16.10.64/26",
790
+ # "172.16.10.128/26", "172.16.10.192/26"]
791
+ #
792
+ # The resulting number of subnets will of course always be
793
+ # a power of two.
794
+ #
795
+ def subnet(subprefix)
796
+ unless ((@prefix.to_i)..32).include? subprefix
797
+ raise ArgumentError, "New prefix must be between #@prefix and 32"
798
+ end
799
+ Array.new(2**(subprefix-@prefix.to_i)) do |i|
800
+ self.class.parse_u32(network_u32+(i*(2**(32-subprefix))), subprefix)
801
+ end
802
+ end
803
+
804
+ #
805
+ # Returns the difference between two IP addresses
806
+ # in unsigned int 32 bits format
807
+ #
808
+ # Example:
809
+ #
810
+ # ip1 = IPAddress("172.16.10.0/24")
811
+ # ip2 = IPAddress("172.16.11.0/24")
812
+ #
813
+ # puts ip1 - ip2
814
+ # #=> 256
815
+ #
816
+ def -(oth)
817
+ return (to_u32 - oth.to_u32).abs
818
+ end
819
+
820
+ #
821
+ # Returns a new IPv4 object which is the result
822
+ # of the summarization, if possible, of the two
823
+ # objects
824
+ #
825
+ # Example:
826
+ #
827
+ # ip1 = IPAddress("172.16.10.1/24")
828
+ # ip2 = IPAddress("172.16.11.2/24")
829
+ #
830
+ # p (ip1 + ip2).map {|i| i.to_string}
831
+ # #=> ["172.16.10.0/23"]
832
+ #
833
+ # If the networks are not contiguous, returns
834
+ # the two network numbers from the objects
835
+ #
836
+ # ip1 = IPAddress("10.0.0.1/24")
837
+ # ip2 = IPAddress("10.0.2.1/24")
838
+ #
839
+ # p (ip1 + ip2).map {|i| i.to_string}
840
+ # #=> ["10.0.0.0/24","10.0.2.0/24"]
841
+ #
842
+ def +(oth)
843
+ aggregate(*[self,oth].sort.map{|i| i.network})
844
+ end
845
+
846
+ #
847
+ # Checks whether the ip address belongs to a
848
+ # RFC 791 CLASS A network, no matter
849
+ # what the subnet mask is.
850
+ #
851
+ # Example:
852
+ #
853
+ # ip = IPAddress("10.0.0.1/24")
854
+ #
855
+ # ip.a?
856
+ # #=> true
857
+ #
858
+ def a?
859
+ CLASSFUL.key(8) === bits
860
+ end
861
+
862
+ #
863
+ # Checks whether the ip address belongs to a
864
+ # RFC 791 CLASS B network, no matter
865
+ # what the subnet mask is.
866
+ #
867
+ # Example:
868
+ #
869
+ # ip = IPAddress("172.16.10.1/24")
870
+ #
871
+ # ip.b?
872
+ # #=> true
873
+ #
874
+ def b?
875
+ CLASSFUL.key(16) === bits
876
+ end
877
+
878
+ #
879
+ # Checks whether the ip address belongs to a
880
+ # RFC 791 CLASS C network, no matter
881
+ # what the subnet mask is.
882
+ #
883
+ # Example:
884
+ #
885
+ # ip = IPAddress("192.168.1.1/30")
886
+ #
887
+ # ip.c?
888
+ # #=> true
889
+ #
890
+ def c?
891
+ CLASSFUL.key(24) === bits
892
+ end
893
+
894
+ #
895
+ # Return the ip address in a format compatible
896
+ # with the IPv6 Mapped IPv4 addresses
897
+ #
898
+ # Example:
899
+ #
900
+ # ip = IPAddress("172.16.10.1/24")
901
+ #
902
+ # ip.to_ipv6
903
+ # #=> "ac10:0a01"
904
+ #
905
+ def to_ipv6
906
+ "%.4x:%.4x" % [to_u32].pack("N").unpack("nn")
907
+ end
908
+
909
+ #
910
+ # Creates a new IPv4 object from an
911
+ # unsigned 32bits integer.
912
+ #
913
+ # ip = IPAddress::IPv4::parse_u32(167772160)
914
+ #
915
+ # ip.prefix = 8
916
+ # ip.to_string
917
+ # #=> "10.0.0.0/8"
918
+ #
919
+ # The +prefix+ parameter is optional:
920
+ #
921
+ # ip = IPAddress::IPv4::parse_u32(167772160, 8)
922
+ #
923
+ # ip.to_string
924
+ # #=> "10.0.0.0/8"
925
+ #
926
+ def self.parse_u32(u32, prefix=32)
927
+ self.new([u32].pack("N").unpack("C4").join(".")+"/#{prefix}")
928
+ end
929
+
930
+ #
931
+ # Creates a new IPv4 object from binary data,
932
+ # like the one you get from a network stream.
933
+ #
934
+ # For example, on a network stream the IP 172.16.0.1
935
+ # is represented with the binary "\254\020\n\001".
936
+ #
937
+ # ip = IPAddress::IPv4::parse_data "\254\020\n\001"
938
+ # ip.prefix = 24
939
+ #
940
+ # ip.to_string
941
+ # #=> "172.16.10.1/24"
942
+ #
943
+ def self.parse_data(str, prefix=32)
944
+ self.new(str.unpack("C4").join(".")+"/#{prefix}")
945
+ end
946
+
947
+ #
948
+ # Extract an IPv4 address from a string and
949
+ # returns a new object
950
+ #
951
+ # Example:
952
+ #
953
+ # str = "foobar172.16.10.1barbaz"
954
+ # ip = IPAddress::IPv4::extract str
955
+ #
956
+ # ip.to_s
957
+ # #=> "172.16.10.1"
958
+ #
959
+ def self.extract(str)
960
+ self.new REGEXP.match(str).to_s
961
+ end
962
+
963
+ #
964
+ # Summarization (or aggregation) is the process when two or more
965
+ # networks are taken together to check if a supernet, including all
966
+ # and only these networks, exists. If it exists then this supernet
967
+ # is called the summarized (or aggregated) network.
968
+ #
969
+ # It is very important to understand that summarization can only
970
+ # occur if there are no holes in the aggregated network, or, in other
971
+ # words, if the given networks fill completely the address space
972
+ # of the supernet. So the two rules are:
973
+ #
974
+ # 1) The aggregate network must contain +all+ the IP addresses of the
975
+ # original networks;
976
+ # 2) The aggregate network must contain +only+ the IP addresses of the
977
+ # original networks;
978
+ #
979
+ # A few examples will help clarify the above. Let's consider for
980
+ # instance the following two networks:
981
+ #
982
+ # ip1 = IPAddress("172.16.10.0/24")
983
+ # ip2 = IPAddress("172.16.11.0/24")
984
+ #
985
+ # These two networks can be expressed using only one IP address
986
+ # network if we change the prefix. Let Ruby do the work:
987
+ #
988
+ # IPAddress::IPv4::summarize(ip1,ip2).to_s
989
+ # #=> "172.16.10.0/23"
990
+ #
991
+ # We note how the network "172.16.10.0/23" includes all the addresses
992
+ # specified in the above networks, and (more important) includes
993
+ # ONLY those addresses.
994
+ #
995
+ # If we summarized +ip1+ and +ip2+ with the following network:
996
+ #
997
+ # "172.16.0.0/16"
998
+ #
999
+ # we would have satisfied rule #1 above, but not rule #2. So "172.16.0.0/16"
1000
+ # is not an aggregate network for +ip1+ and +ip2+.
1001
+ #
1002
+ # If it's not possible to compute a single aggregated network for all the
1003
+ # original networks, the method returns an array with all the aggregate
1004
+ # networks found. For example, the following four networks can be
1005
+ # aggregated in a single /22:
1006
+ #
1007
+ # ip1 = IPAddress("10.0.0.1/24")
1008
+ # ip2 = IPAddress("10.0.1.1/24")
1009
+ # ip3 = IPAddress("10.0.2.1/24")
1010
+ # ip4 = IPAddress("10.0.3.1/24")
1011
+ #
1012
+ # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_string
1013
+ # #=> "10.0.0.0/22",
1014
+ #
1015
+ # But the following networks can't be summarized in a single network:
1016
+ #
1017
+ # ip1 = IPAddress("10.0.1.1/24")
1018
+ # ip2 = IPAddress("10.0.2.1/24")
1019
+ # ip3 = IPAddress("10.0.3.1/24")
1020
+ # ip4 = IPAddress("10.0.4.1/24")
1021
+ #
1022
+ # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
1023
+ # #=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
1024
+ #
1025
+ def self.summarize(*args)
1026
+ # one network? no need to summarize
1027
+ return [args.first.network] if args.size == 1
1028
+
1029
+ i = 0
1030
+ result = args.dup.sort.map{|ip| ip.network}
1031
+ while i < result.size-1
1032
+ sum = result[i] + result[i+1]
1033
+ result[i..i+1] = sum.first if sum.size == 1
1034
+ i += 1
1035
+ end
1036
+
1037
+ result.flatten!
1038
+ if result.size == args.size
1039
+ # nothing more to summarize
1040
+ return result
1041
+ else
1042
+ # keep on summarizing
1043
+ return self.summarize(*result)
1044
+ end
1045
+ end
1046
+
1047
+ #
1048
+ # Creates a new IPv4 address object by parsing the
1049
+ # address in a classful way.
1050
+ #
1051
+ # Classful addresses have a fixed netmask based on the
1052
+ # class they belong to:
1053
+ #
1054
+ # * Class A, from 0.0.0.0 to 127.255.255.255
1055
+ # * Class B, from 128.0.0.0 to 191.255.255.255
1056
+ # * Class C, D and E, from 192.0.0.0 to 255.255.255.254
1057
+ #
1058
+ # Example:
1059
+ #
1060
+ # ip = IPAddress::IPv4.parse_classful "10.0.0.1"
1061
+ #
1062
+ # ip.netmask
1063
+ # #=> "255.0.0.0"
1064
+ # ip.a?
1065
+ # #=> true
1066
+ #
1067
+ # Note that classes C, D and E will all have a default
1068
+ # prefix of /24 or 255.255.255.0
1069
+ #
1070
+ def self.parse_classful(ip)
1071
+ if IPAddress.valid_ipv4?(ip)
1072
+ address = ip.strip
1073
+ else
1074
+ raise ArgumentError, "Invalid IP #{ip.inspect}"
1075
+ end
1076
+ prefix = CLASSFUL.find{|h,k| h === ("%.8b" % address.to_i)}.last
1077
+ self.new "#{address}/#{prefix}"
1078
+ end
1079
+
1080
+ #
1081
+ # private methods
1082
+ #
1083
+ private
1084
+
1085
+ # Tweaked to remove the #upto(32)
1086
+ def newprefix(num)
1087
+ return @prefix + (Math::log2(num).ceil )
1088
+ end
1089
+
1090
+ def sum_first_found(arr)
1091
+ dup = arr.dup.reverse
1092
+ dup.each_with_index do |obj,i|
1093
+ a = [self.class.summarize(obj,dup[i+1])].flatten
1094
+ if a.size == 1
1095
+ dup[i..i+1] = a
1096
+ return dup.reverse
1097
+ end
1098
+ end
1099
+ return dup.reverse
1100
+ end
1101
+
1102
+ def aggregate(ip1,ip2)
1103
+ return [ip1] if ip1.include? ip2
1104
+
1105
+ snet = ip1.supernet(ip1.prefix-1)
1106
+ if snet.include_all?(ip1, ip2) && ((ip1.size + ip2.size) == snet.size)
1107
+ return [snet]
1108
+ else
1109
+ return [ip1, ip2]
1110
+ end
1111
+ end
1112
+ end # class IPv4
1113
+ end # module IPAddress
1114
+