]> gitweb.michael.orlitzky.com - hath.git/blob - src/Cidr.hs
Implemented the combine_all function for Cidrs containing one another.
[hath.git] / src / Cidr.hs
1 module Cidr
2 ( Cidr(..),
3 cidr_from_string,
4 combine_all
5 ) where
6
7 import IPv4Address
8 import ListUtils
9 import Maskable
10 import Maskbits
11 import Octet
12
13
14 data Cidr = None | Cidr { ipv4address :: IPv4Address,
15 maskbits :: Maskbits }
16 deriving (Eq, Show)
17
18
19 -- Returns the mask portion of a CIDR address. That is, everything
20 -- after the trailing slash.
21 maskbits_from_cidr_string :: String -> Maskbits
22 maskbits_from_cidr_string s =
23 maskbits_from_string ((splitWith (`elem` "/") s) !! 1)
24
25
26 -- Takes an IP address String in CIDR notation, and returns a list of
27 -- its octets (as Ints).
28 octets_from_cidr_string :: String -> [Octet]
29 octets_from_cidr_string s =
30 map octet_from_string (take 4 (splitWith (`elem` "./") s))
31
32
33 cidr_from_string :: String -> Cidr
34 cidr_from_string s
35 | addr == IPv4Address.None = Cidr.None
36 | mbits == Maskbits.None = Cidr.None
37 | otherwise = Cidr addr mbits
38 where
39 addr = ipv4address_from_octets (oct1) (oct2) (oct3) (oct4)
40 oct1 = (octs !! 0)
41 oct2 = (octs !! 1)
42 oct3 = (octs !! 2)
43 oct4 = (octs !! 3)
44 octs = octets_from_cidr_string s
45 mbits = maskbits_from_cidr_string s
46
47
48
49 -- Return true if the first argument (a CIDR range) contains the
50 -- second (another CIDR range). There are a lot of ways we can be fed
51 -- junk here. For lack of a better alternative, just return False when
52 -- we are given nonsense.
53 contains :: Cidr -> Cidr -> Bool
54 contains Cidr.None _ = False
55 contains _ Cidr.None = False
56 contains (Cidr _ Maskbits.None) _ = False
57 contains (Cidr IPv4Address.None _) _ = False
58 contains _ (Cidr _ Maskbits.None) = False
59 contains _ (Cidr IPv4Address.None _) = False
60
61 -- If the number of bits in the network part of the first address is
62 -- larger than the number of bits in the second, there is no way that
63 -- the first range can contain the second. For, if the number of
64 -- network bits is larger, then the number of host bits must be
65 -- smaller, and if cidr1 has fewer hosts than cidr2, cidr1 most
66 -- certainly does not contain cidr2.
67 --
68 -- On the other hand, if the first argument (cidr1) has fewer (or the
69 -- same number of) network bits as the second, it can contain the
70 -- second. In this case, we need to check that every host in cidr2 is
71 -- contained in cidr1. If a host in cidr2 is contained in cidr1, then
72 -- at least mbits1 of an address in cidr2 will match cidr1. For
73 -- example,
74 --
75 -- cidr1 = 192.168.1.0/23, cidr2 = 192.168.1.100/24
76 --
77 -- Here, cidr2 contains all of 192.168.1.0 through
78 -- 192.168.1.255. However, cidr1 contains BOTH 192.168.0.0 through
79 -- 192.168.0.255 and 192.168.1.0 through 192.168.1.255. In essence,
80 -- what we want to check is that cidr2 "begins with" something that
81 -- cidr1 CAN begin with. Since cidr1 can begin with 192.168.1, and
82 -- cidr2 DOES, cidr1 contains cidr2..
83 --
84 -- The way that we check this is to apply cidr1's mask to cidr2's
85 -- address and see if the result is the same as cidr1's mask applied
86 -- to cidr1's address.
87 --
88 contains (Cidr addr1 mbits1) (Cidr addr2 mbits2)
89 | mbits1 > mbits2 = False
90 | otherwise = addr1masked == addr2masked
91 where
92 addr1masked = apply_mask addr1 mbits1
93 addr2masked = apply_mask addr2 mbits1
94
95
96 contains_proper :: Cidr -> Cidr -> Bool
97 contains_proper cidr1 cidr2 =
98 (cidr1 `contains` cidr2) && (not (cidr1 == cidr2))
99
100
101 -- A CIDR range is redundant (with respect to the given list) if
102 -- another CIDR range in that list properly contains it.
103 redundant :: [Cidr] -> Cidr -> Bool
104 redundant cidrlist cidr = any ((flip contains_proper) cidr) cidrlist
105
106
107 -- We want to get rid of all the Cidrs that are properly contained
108 -- in some other Cidr.
109 combine_all :: [Cidr] -> [Cidr]
110 combine_all cidrs =
111 filter (not . (redundant cidrs)) cidrs