module Cidr ( Cidr(..), cidr_from_string, contains ) where import IPv4Address import ListUtils import Maskable import Maskbits import Octet data Cidr = None | Cidr { ipv4address :: IPv4Address, maskbits :: Maskbits } deriving (Eq, Show) -- Returns the mask portion of a CIDR address. That is, everything -- after the trailing slash. maskbits_from_cidr_string :: String -> Maskbits maskbits_from_cidr_string s = maskbits_from_string ((splitWith (`elem` "/") s) !! 1) -- Takes an IP address String in CIDR notation, and returns a list of -- its octets (as Ints). octets_from_cidr_string :: String -> [Octet] octets_from_cidr_string s = map octet_from_string (take 4 (splitWith (`elem` "./") s)) cidr_from_string :: String -> Cidr cidr_from_string s | addr == IPv4Address.None = Cidr.None | mbits == Maskbits.None = Cidr.None | otherwise = Cidr addr mbits where addr = ipv4address_from_octets (oct1) (oct2) (oct3) (oct4) oct1 = (octs !! 0) oct2 = (octs !! 1) oct3 = (octs !! 2) oct4 = (octs !! 3) octs = octets_from_cidr_string s mbits = maskbits_from_cidr_string s -- Return true if the first argument (a CIDR range) contains the -- second (another CIDR range). There are a lot of ways we can be fed -- junk here. For lack of a better alternative, just return False when -- we are given nonsense. contains :: Cidr -> Cidr -> Bool contains Cidr.None _ = False contains _ Cidr.None = False contains (Cidr _ Maskbits.None) _ = False contains (Cidr IPv4Address.None _) _ = False contains _ (Cidr _ Maskbits.None) = False contains _ (Cidr IPv4Address.None _) = False -- If the number of bits in the network part of the first address is -- larger than the number of bits in the second, there is no way that -- the first range can contain the second. For, if the number of -- network bits is larger, then the number of host bits must be -- smaller, and if cidr1 has fewer hosts than cidr2, cidr1 most -- certainly does not contain cidr2. -- -- On the other hand, if the first argument (cidr1) has fewer (or the -- same number of) network bits as the second, it can contain the -- second. In this case, we need to check that every host in cidr2 is -- contained in cidr1. If a host in cidr2 is contained in cidr1, then -- at least mbits1 of an address in cidr2 will match cidr1. For -- example, -- -- cidr1 = 192.168.1.0/23, cidr2 = 192.168.1.100/24 -- -- Here, cidr2 contains all of 192.168.1.0 through -- 192.168.1.255. However, cidr1 contains BOTH 192.168.0.0 through -- 192.168.0.255 and 192.168.1.0 through 192.168.1.255. In essence, -- what we want to check is that cidr2 "begins with" something that -- cidr1 CAN begin with. Since cidr1 can begin with 192.168.1, and -- cidr2 DOES, cidr1 contains cidr2.. -- -- The way that we check this is to apply cidr1's mask to cidr2's -- address and see if the result is the same as cidr1's mask applied -- to cidr1's address. -- contains (Cidr addr1 (Maskbits mbits1)) (Cidr addr2 (Maskbits mbits2)) | mbits1 > mbits2 = False | otherwise = addr1masked == addr2masked where addr1masked = apply_mask addr1 (Maskbits mbits1) addr2masked = apply_mask addr2 (Maskbits mbits1)