[f24fc8d] | 1 | #ifndef ROUTE_TABLE_H |
---|
| 2 | #define ROUTE_TABLE_H |
---|
| 3 | |
---|
| 4 | #include "net.h" |
---|
| 5 | #include <vector> |
---|
| 6 | #include <set> |
---|
| 7 | #include <map> |
---|
| 8 | |
---|
| 9 | #include <algorithm> |
---|
| 10 | #include <functional> |
---|
| 11 | #include <ext/functional> |
---|
| 12 | |
---|
| 13 | using __gnu_cxx::compose1; |
---|
| 14 | |
---|
| 15 | class route_table { |
---|
| 16 | protected: |
---|
| 17 | typedef std::set<Net> net_set_t; |
---|
| 18 | typedef std::map<Net, Host> net_map_t; |
---|
| 19 | typedef std::map<Host, net_set_t> route_map_t; |
---|
| 20 | route_map_t rm; |
---|
| 21 | net_map_t nm; |
---|
| 22 | net_set_t empty; |
---|
| 23 | // Helper functor that just returns the first member of a |
---|
| 24 | // pair |
---|
| 25 | template <class p> |
---|
| 26 | class select1st { |
---|
| 27 | public: |
---|
| 28 | typename p::first_type operator()(p x) const { return x.first; } |
---|
| 29 | }; |
---|
| 30 | |
---|
| 31 | class contains : public std::unary_function<Net, bool> { |
---|
| 32 | protected: |
---|
| 33 | const Net& container; // Net that may contain test values |
---|
| 34 | public: |
---|
| 35 | contains(const Net& c) : container(c) { } |
---|
| 36 | bool operator()(const Net& n) const { |
---|
| 37 | return n.inside(container); |
---|
| 38 | } |
---|
| 39 | }; |
---|
| 40 | |
---|
| 41 | class set_remover : public std::unary_function<Net, void> { |
---|
| 42 | protected: |
---|
| 43 | net_set_t& s; |
---|
| 44 | public: |
---|
| 45 | set_remover(net_set_t& ss) : s(ss) { } |
---|
| 46 | void operator()(const Net& n) { s.erase(n); } |
---|
| 47 | }; |
---|
| 48 | |
---|
| 49 | public: |
---|
| 50 | route_table() : rm(), nm(), empty() { } |
---|
| 51 | void insert(const Net& dest, const Host& gw) { |
---|
| 52 | net_map_t::iterator n = nm.find(dest); |
---|
| 53 | |
---|
| 54 | if ( n == nm.end() || gw < (*n).second) { |
---|
| 55 | // never seen this network before, insert it, or it's a |
---|
| 56 | // duplicate and the next hop is a lower IP address than the |
---|
| 57 | // last attempted insert. Keep the lower GW for consistency |
---|
| 58 | // (Remove any dupes, though) |
---|
| 59 | if (gw < (*n).second) { |
---|
| 60 | remove(dest, (*n).second); |
---|
| 61 | } |
---|
| 62 | |
---|
| 63 | route_map_t::iterator r = rm.find(gw); |
---|
| 64 | |
---|
| 65 | if ( r != rm.end()) { |
---|
| 66 | (*r).second.insert(dest); |
---|
| 67 | } |
---|
| 68 | else { |
---|
| 69 | rm[gw] = net_set_t(); |
---|
| 70 | rm[gw].insert(dest); |
---|
| 71 | } |
---|
| 72 | nm.insert(net_map_t::value_type(dest, gw)); |
---|
| 73 | } |
---|
| 74 | } |
---|
| 75 | const net_set_t& get(const Host& gw) const { |
---|
| 76 | route_map_t::const_iterator r = rm.find(gw); |
---|
| 77 | |
---|
| 78 | if ( r != rm.end()) return (*r).second; |
---|
| 79 | else return empty; |
---|
| 80 | } |
---|
| 81 | |
---|
| 82 | void remove(const Net& dest, const Host& gw) { |
---|
| 83 | route_map_t::iterator r = rm.find(gw); |
---|
| 84 | |
---|
| 85 | if ( r != rm.end()) { |
---|
| 86 | (*r).second.erase(dest); |
---|
| 87 | // Remove the entry from the netmap |
---|
| 88 | nm.erase(dest); |
---|
| 89 | // If this gateway has no destinations, remove it from the map |
---|
| 90 | // all togther. |
---|
| 91 | if ((*r).second.empty()) |
---|
| 92 | rm.erase(gw); |
---|
| 93 | } |
---|
| 94 | } |
---|
| 95 | |
---|
| 96 | std::ostream& print(std::ostream& f) const { |
---|
| 97 | for (route_map_t::const_iterator i = rm.begin(); i != rm.end(); i++) |
---|
| 98 | for (net_set_t::const_iterator j = (*i).second.begin(); |
---|
| 99 | j != (*i).second.end(); j++) |
---|
| 100 | f << (*j) << " " << (*i).first << std::endl; |
---|
| 101 | return f; |
---|
| 102 | } |
---|
| 103 | |
---|
| 104 | bool collision(const Net& tryit, const Host& out) const { |
---|
| 105 | // Return true if there is a routing in rm that would be dirsupted |
---|
| 106 | // by adding the net in tryit to the out gateway. That is, if any |
---|
| 107 | // destination network on another interface is inside tryit, return |
---|
| 108 | // false. |
---|
| 109 | for (route_map_t::const_iterator r = rm.begin(); |
---|
| 110 | r != rm.end(); r++) { |
---|
| 111 | if ((*r).first == out) continue; |
---|
| 112 | for (net_set_t::const_iterator n = (*r).second.begin(); |
---|
| 113 | n != (*r).second.end(); n++) { |
---|
| 114 | if ((*n).inside(tryit)) return true; |
---|
| 115 | } |
---|
| 116 | } |
---|
| 117 | return false; |
---|
| 118 | } |
---|
| 119 | |
---|
| 120 | |
---|
| 121 | void collapse() { |
---|
| 122 | // Try to make rm as small as possible by expanding each |
---|
| 123 | // destination network until it either conflicts with a destination |
---|
| 124 | // network on another gateway or is a /8. |
---|
| 125 | std::vector<Host> gateways; |
---|
| 126 | |
---|
| 127 | transform(rm.begin(), rm.end(), back_inserter(gateways), |
---|
| 128 | select1st<route_map_t::value_type>()); |
---|
| 129 | |
---|
| 130 | for (std::vector<Host>::const_iterator gw = gateways.begin(); |
---|
| 131 | gw != gateways.end(); gw++) { |
---|
| 132 | // Try to expand all the destinations on gw. Copy them into |
---|
| 133 | // candidates for expansion and try all the candidates. |
---|
| 134 | net_set_t candidates = get(*gw); |
---|
| 135 | |
---|
| 136 | while (! candidates.empty()) { |
---|
| 137 | |
---|
| 138 | // Pop a value from the set. |
---|
| 139 | Net tryit = *candidates.begin(); |
---|
| 140 | candidates.erase(tryit); |
---|
| 141 | |
---|
| 142 | // While it grows, try to expand it and remove any networks |
---|
| 143 | // it expands over. |
---|
| 144 | bool growing = true; |
---|
| 145 | while (growing && tryit.get_mask() > 8) { |
---|
| 146 | tryit.set_mask(tryit.get_mask() - 1); |
---|
| 147 | |
---|
| 148 | if ( collision(tryit, *gw) ) { |
---|
| 149 | growing = false; |
---|
| 150 | } |
---|
| 151 | else { |
---|
| 152 | // Successfully got bigger. Remove nets inside the |
---|
| 153 | // new new from the routing table and candidate |
---|
| 154 | // set, and insert the larger net into the table. |
---|
| 155 | std::vector<Net> to_remove; |
---|
| 156 | |
---|
| 157 | // Collect the Nets to remove |
---|
| 158 | std::remove_copy_if(get(*gw).begin(), |
---|
| 159 | get(*gw).end(), |
---|
| 160 | std::back_inserter(to_remove), |
---|
| 161 | compose1(std::logical_not<bool>(), |
---|
| 162 | contains(tryit))); |
---|
| 163 | // Remove 'em - calling remove makes sure the |
---|
| 164 | // internal route_table data structures stay in |
---|
| 165 | // sync. This has been a bug source in the past. |
---|
| 166 | for (std::vector<Net>::const_iterator tr = |
---|
| 167 | to_remove.begin(); |
---|
| 168 | tr != to_remove.end(); tr++) { |
---|
| 169 | remove(*tr, *gw); |
---|
| 170 | candidates.erase(*tr); |
---|
| 171 | } |
---|
| 172 | // New bigger candidate gots into the route_table |
---|
| 173 | // here. |
---|
| 174 | insert(tryit, *gw); |
---|
| 175 | } |
---|
| 176 | } |
---|
| 177 | } |
---|
| 178 | } |
---|
| 179 | } |
---|
| 180 | }; |
---|
| 181 | |
---|
| 182 | inline std::ostream& operator<<(std::ostream& f, const route_table& rt ) { |
---|
| 183 | return rt.print(f); |
---|
| 184 | } |
---|
| 185 | |
---|
| 186 | #endif |
---|