File: mask32.rb

package info (click to toggle)
ruby-netaddr 2.0.6-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 296 kB
  • sloc: ruby: 2,022; makefile: 7
file content (105 lines) | stat: -rw-r--r-- 3,126 bytes parent folder | download
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
module NetAddr
	
	#Mask32 represents a 32-bit netmask. 
	class Mask32
		# mask is the Integer representation of this netmask
		attr_reader :mask
		
		# prefix_len is the Integer prefix length of this netmask
		attr_reader :prefix_len
		
		# Create a Mask32 from an Integer prefix length. Valid values are 0-32.
		# Throws ValidationError on error.
		def initialize(prefix_len)
			if (!prefix_len.kind_of?(Integer))
				raise ValidationError, "Expected an Integer for 'prefix_len' but got a #{prefix_len.class}."
			elsif ( (prefix_len < 0) || (prefix_len > 32) )
				raise ValidationError, "#{prefix_len} must be in the range of 0-32."
			end
			@prefix_len = prefix_len
			@mask = NetAddr::F32 ^ (NetAddr::F32 >> @prefix_len)
		end
		
		# parse will create an Mask32 from its string representation.
		# arguments:
		#	* mask - String representing a netmask (ie. "/24" or "255.255.255.0").
		#
		#	Throws ValidationError on error.
		def Mask32.parse(mask)
			mask = mask.strip
			if (mask.start_with?("/")) # cidr format
				mask = mask[1..-1] # remove "/"
			end

			if (!mask.include?("."))
				begin
					return Mask32.new(Integer(mask))
				rescue ArgumentError
					raise ValidationError, "#{mask} is not valid integer."
				end
			end
			
			# for extended netmask
			# determine length of netmask by cycling through bit by bit and looking
			# for the first '1' bit, tracking the length as we go. we also want to verify
			# that the mask is valid (ie. not something like 255.254.255.0). we do this
			# by creating a hostmask which covers the '0' bits of the mask. once we have
			# separated the net vs host mask we xor them together. the result should be that
			# all bits are now '1'. if not then we know we have an invalid netmask.
			maskI = Util.parse_IPv4(mask)
			prefix = 32
			hostmask = 1
			i = maskI 
			32.downto(1) do
				if (i&1 == 1)
					hostmask = hostmask >> 1
					if (maskI ^hostmask != NetAddr::F32)
						raise ValidationError, "#{mask} is invalid. It contains '1' bits in its host portion."
					end
					break
				end
				hostmask = (hostmask << 1) | 1
				i = i>>1
				prefix -= 1
			end
			return Mask32.new(prefix)
			
		end
		
		# extended returns the Mask32 in extended format (eg. x.x.x.x)
		def extended()
			Util.int_to_IPv4(@mask)
		end
		
		#cmp compares equality with another Mask32. Return:
		#* 1 if this Mask128 is larger in capacity
		#* 0 if the two are equal
		#* -1 if this Mask128 is smaller in capacity
		def cmp(other)
			if (!other.kind_of?(Mask32))
				raise ArgumentError, "Expected an Mask32 object for 'other' but got a #{other.class}."
			end
			if (self.prefix_len < other.prefix_len)
				return 1
			elsif (self.prefix_len > other.prefix_len)
				return -1
			end
			return 0
		end
		
		#len returns the number of IP addresses in this network. It will always return 0 for /0 networks.
		def len()
			if (self.prefix_len == 0)
				return 0
			end
			return (self.mask ^ NetAddr::F32) + 1 # bit flip the netmask and add 1
		end
		
		# to_s returns the Mask32 as a String
		def to_s()
			return "/#{@prefix_len}"
		end
		
	end # end class Mask32
	
end # end module