1 module libsweatyballs.engine.core;
2 
3 import libsweatyballs.router.core : Router;
4 import libsweatyballs.router.table;
5 import libsweatyballs.zwitch.core : Switch;
6 import libsweatyballs.zwitch.neighbor;
7 import libsweatyballs.link.core : Link;
8 import core.sync.mutex : Mutex;
9 import libsweatyballs.engine.configuration : Config;
10 import std.conv : to;
11 import gogga;
12 import core.thread : Thread, dur;
13 import libsweatyballs.router.table : Route;
14 import std.socket : Address, parseAddress;
15 import libsweatyballs.link.message.core;
16 import std.container.slist;
17 import std.range;
18 import libsweatyballs.engine.handlers : engine, advHandler, pktHandler, defaultHandler;
19 import std.datetime : Clock;
20 
21 /* TODO: Import for config thing */
22 
23 /**
24 * Engine
25 *
26 * Description: TODO
27 */
28 public final class Engine : Thread
29 {
30     /**
31     * Network components
32     */
33     private Router router;
34     private Switch zwitch;
35 
36     /**
37     * Links the router can advertise over
38     */
39     private Link[] links;
40     private Mutex linksMutex;
41 
42     /**
43     * Received packets
44     */
45     private SList!(Packet) packetQueue;
46     private Mutex packetQueueLock;
47 
48     /**
49     * 1. This must read config given to it
50     * 2. Setups links
51     * 3. Create new Router with these links
52     * 4. Spawn a Switch that handles packet in/out
53     * 5. Pass the Switch the router
54     * 6. Start the switch
55     * 7. We must then mainloop and collect statistics and handle shutdown etc
56     */
57     this(Config config)
58     {
59         /* TODO: Add comment */
60         super(&worker);
61 
62         /* TODO: Read config */
63         parseConfig(config);
64 
65         /* Initialize locks */
66         initMutexes();
67 
68         /* Initialize the handler's module globals */
69         initEngine();
70     }
71 
72     /**
73     * Initializes all the mutexes
74     */
75     private void initMutexes()
76     {
77         linksMutex = new Mutex();
78         packetQueueLock = new Mutex();
79     }
80 
81     public Link[] getLinks()
82     {
83         Link[] copy;
84 
85         linksMutex.lock();
86         foreach(Link link; links)
87         {
88             copy ~= link;
89         }
90         linksMutex.unlock();
91 
92         return copy;
93     }
94 
95     /**
96     * This function will do an import which
97     * will initialize the module, any other
98     * thread who referes to a mobule's
99     * member also inits their own copy
100     *
101     * There is however a global field
102     * `__gshared` that all code using
103     * it refers to one entity, not
104     * several static members per each
105     * module
106     *
107     * This must be set here
108     */
109     private void initEngine()
110     {
111         engine = this;
112     }
113 
114     private void initLinkHandler(Link link)
115     {
116         /* Register a handler for advertisements */
117         link.registerHandler(&advHandler, 0);
118 
119         /* Register a handler for packets */
120         link.registerHandler(&pktHandler, 1);
121 
122         /* Register default handler */
123         link.setDefaultHandler(&defaultHandler);
124     }
125 
126     private void parseConfig(Config config)
127     {
128         /* TODO: Setup the tun adapter */
129         tun = new TUNAdapter("sweatyballs0", AdapterType.TUN);
130 
131         /* TODO: Set configuration parameter */
132 
133         /* Setup links */
134         links = createLinks(config.links);
135         setupLinks(links);
136 
137         /**
138         * Setup a new Router
139         */
140         router = new Router(this, config.routerIdentity);
141         
142         
143         
144 
145         /* Setup a new Switch */
146         zwitch = new Switch(this);
147 
148         /* Add self neighbor to any link (try the first, TODO: Atleast one link is needed) */
149         Address address = parseAddress("::", links[0].getR2RPort());
150         Neighbor selfNeighbor = new Neighbor(router.getIdentity().getKeys().publicKey, address, links[0]);
151 
152 
153         Route route = new Route(router.getIdentity().getKeys().publicKey, selfNeighbor, Clock.currTime());
154         route.setAgeibility(false);
155 
156         router.getTable().addRoute(route);
157         
158     }
159 
160     public Router getRouter()
161     {
162         return router;
163     }
164 
165     public Switch getSwitch()
166     {
167         return zwitch;
168     }
169 
170     public void newPacket(Packet packet)
171     {
172         packetQueueLock.lock();
173         packetQueue.insertAfter(packetQueue[], packet);
174         packetQueueLock.unlock();
175     }
176 
177     private Packet checkPacket()
178     {
179         Packet received;
180 
181         /* Check for packet */
182         packetQueueLock.lock();
183         if(!packetQueue.empty())
184         {
185             /**
186             * Dequeue a packet
187             *
188             * Get the Range internal
189             * (use auto as this is some butchered
190             * fucking templatised shit)
191             */
192             received = (packetQueue[]).front();
193             (packetQueue[]).popFront();
194         }
195         packetQueueLock.unlock();
196 
197 
198         
199         return received;
200     
201     }
202 
203     /**
204     * processPacket
205     *
206     * This method is used when a packet arrives to the engine
207     * and is used to decide what to do with the packet, this
208     * could be:
209     *
210     * 1. If the packet's payload is recognizable as a Session
211     *    control command, then it will attempt to get a new
212     *    Session created
213     * 2. If not a Session control message then we (as of now)
214     *    drop the packet. (TODO: We could keep it but eh, use
215     *    Sessions rather please)
216     */
217     private void processPacket(Packet packet)
218     {
219 
220     }
221 
222 
223     /**
224     * FIXME: Remove this and set it up elsewhere
225     *
226     * This function is here to setup a tun adapter which will
227     * then push shit in and out or something
228     */
229     import libtun.adapter;
230  
231 
232 
233     private TUNAdapter tun;
234 
235     private void worker()
236     {
237         
238 
239         
240         
241         while(true)
242         {
243             byte[] poes;
244         
245         tun.receive(poes);
246             gprintln("TUNRecieve: "~to!(string)(poes));
247 
248             /**
249             * FIXME: Remove this, this is just testing code
250             */
251             router.getTable().lockTable();
252             Route[] routes = router.getTable().getRoutes();
253             foreach(Route route; routes)
254             {
255                 zwitch.sendPacket(route.getAddress(), cast(byte[])"Hello world");    
256             }
257             router.getTable().unlockTable();
258 
259             /**
260             * Check receiveve queue
261             */
262             Packet packet = checkPacket();
263             if(packet)
264             {
265                 processPacket(packet);
266             }
267 
268             Thread.sleep(dur!("seconds")(1));
269         }
270     }
271 
272     private Link[] createLinks(string[] interfaces)
273     {
274         Link[] createdLinks;
275 
276         foreach(string interfaceName; interfaces)
277         {
278             createdLinks ~= new Link(interfaceName, this);
279         }
280 
281         return createdLinks;
282     }
283 
284     private void setupLinks(Link[] links)
285     {
286         gprintln("Begin link initailization");
287         foreach(Link link; links)
288         {
289             gprintln("Initializing link "~to!(string)(link)~" ...");
290             initLinkHandler(link);
291             link.launch();
292         }
293         gprintln("Links have been initialized");
294     }
295 
296     public void launch()
297     {
298         /* Start the engine */
299         start();
300 
301         /* Start router */
302         router.launch();
303 
304         /* Start switch */
305         zwitch.launch();
306 
307         /* Start collector */
308         /* TODO: Add me */
309 
310         gprintln("Engine has started all threads and is now going to finish and return to constructor thread control");
311     }
312 }