1 module libsweatyballs.router.table;
2 
3 import std.socket : Address;
4 import core.sync.mutex : Mutex;
5 import std.conv : to;
6 import std.string : cmp;
7 import std.datetime.stopwatch : StopWatch;
8 import std.datetime : Duration;
9 import gogga;
10 import libsweatyballs.zwitch.neighbor;
11 
12 /**
13 * Route
14 *
15 * Description: TODO
16 */
17 public final class Route
18 {
19     // We must know our self-route and add it too somewhere
20     //private __gshared Identity d;
21     private string address;
22     private Neighbor nexthop;
23     private uint metric;
24 
25     /**
26     * TODO: Set these and add a loop watcher to
27     * the table
28     */
29     private long timeout;
30     private StopWatch updateTime;
31 
32     private bool ageibility = true;
33     
34     this(string address, Neighbor nexthop, long timeout = 100, uint metric = 64)
35     {
36         this.address = address;
37         this.nexthop = nexthop;
38         this.timeout = timeout;
39         this.metric = metric;
40 
41         /* Start the stop watch */
42         updateTime.start();
43     }
44 
45     public void refreshTime()
46     {
47         /* Reset the timer */
48         updateTime.reset();
49     }
50 
51     public string getAddress()
52     {
53         return address;
54     }
55 
56     public Neighbor getNexthop()
57     {
58         return nexthop;
59     }
60 
61     public uint getMetric()
62     {
63         return metric;
64     }
65 
66     public void setAgeibility(bool age)
67     {
68         ageibility = age;
69     }
70 
71     public override string toString()
72     {
73         return "Route (To: "~address~", Via: "~to!(string)(nexthop)~", Metric: "~to!(string)(metric)~", Age: "~to!(string)(getAge())~")";
74     }
75 
76     public long getAge()
77     {
78         Duration elapsedTime = updateTime.peek();
79         return elapsedTime.total!("seconds");
80     }
81 
82     public bool isExpired()
83     {
84         Duration elapsedTime = updateTime.peek();
85         return (elapsedTime.total!("seconds") >= timeout) && ageibility;
86     }
87 
88     public override bool opEquals(Object other)
89     {
90         Route otherRoute = cast(Route)other;
91 
92         /* TODO: Add other comparators such as next hops */
93 
94         return cmp(otherRoute.getAddress(), this.getAddress()) == 0 &&
95                 otherRoute.getNexthop() == this.getNexthop() &&
96                 otherRoute.getMetric() == this.getMetric();
97     }
98 }
99 
100 /**
101 * Table
102 *
103 * Description: TODO
104 */
105 public final class Table
106 {
107     /**
108     * Routes
109     */
110     private Route[] routes;
111     private Mutex routeLock;
112 
113     this()
114     {
115         /* Initialize locks */
116         initMutexes();
117     }
118 
119     /**
120     * Initialize the mutexes
121     */
122     private void initMutexes()
123     {
124         routeLock =  new Mutex();
125     }
126 
127     /**
128     * Get routes
129     */
130     public Route[] getRoutes()
131     {
132         /* The copied routes */
133         Route[] copiedRoutes;
134 
135         /* Lock the routing table */
136         routeLock.lock();
137 
138         /* Copy each route */
139         foreach(Route route; routes)
140         {
141             copiedRoutes ~= route;
142         }
143 
144         /* Unlock the routing table */
145         routeLock.unlock();
146 
147         return copiedRoutes;
148     }
149 
150     /**
151     * Add a route 
152     */
153     public void addRoute(Route route)
154     {
155         /* Lock the routing table */
156         routeLock.lock();
157 
158         /* Add the route (only if it doesn't already exist) */
159         foreach(Route cRoute; routes)
160         {
161             /* FIXME: Make sure nexthop matches as well */
162             if(cRoute == route)
163             {
164                 /* Refresh the route */
165                 cRoute.refreshTime();
166 
167                 goto no_add_route;
168             }
169         }
170 
171         routes ~= route;
172         gprintln("Added route "~to!(string)(route));
173 
174         gprintln("TABLE IS HOW BIG MY NIGGER??!?!?: "~to!(string)(routes.length), DebugType.ERROR);
175 
176         
177         
178         no_add_route:
179 
180         /* Unlock the routing table */
181         routeLock.unlock();
182     }
183 
184     public Route lookup(string address)
185     {
186         /* The matched route (if any) */
187         Route match;
188 
189         /* Lock the routing table */
190         routeLock.lock();
191 
192         /* Add the route (only if it doesn't already exist) */
193         foreach(Route route; routes)
194         {
195             /* FIXME: Make sure nexthop matches as well */
196             if(cmp(route.getAddress(), address) == 0)
197             {
198                 match = route;
199             }
200         }
201 
202         /* Unlock the routing table */
203         routeLock.unlock();
204 
205         return match;
206     }
207 
208     /**
209     * Remove a route 
210     */
211     public void removeRoute(Route route)
212     {
213         /* New routing table */
214         Route[] newRoutes;
215 
216         /* Lock the routing table */
217         routeLock.lock();
218 
219         /* Add the route (only if it doesn't already exist) */
220         foreach(Route cRoute; routes)
221         {
222             /* FIXME: Make sure nexthop matches as well */
223             if(cRoute != route)
224             {
225                 newRoutes ~= cRoute;
226             }
227         }
228 
229         routes = newRoutes;
230 
231         /* Unlock the routing table */
232         routeLock.unlock();
233     }
234 }