1 module libsweatyballs.router.advertiser;
2 
3 import core.thread : Thread, dur;
4 import libsweatyballs.router.core : Router;
5 import libsweatyballs.link.core : Link;
6 import std.socket;
7 import gogga;
8 import std.conv : to;
9 import bmessage;
10 import libsweatyballs.link.message.core;
11 import google.protobuf;
12 import std.array : array;
13 import libsweatyballs.router.table : Route;
14 
15 public final class Advertiser : Thread
16 {
17     private Router router;
18     private Socket mcastSock;
19 
20     this(Router router)
21     {
22         /* Set the thread's worker function */
23         super(&worker);
24 
25         this.router = router;
26 
27         /* Setup socket */
28         setupSocket();
29     }
30 
31     private void setupSocket()
32     {
33         /* TODO: Error handling */
34         mcastSock = new Socket(AddressFamily.INET6, SocketType.DGRAM, ProtocolType.UDP);
35 
36 
37     }
38 
39     private void worker()
40     {
41         /* TODO: Implement me */
42         while(true)
43         {
44             /*  Cycle through each link and advertise on them */
45             Link[] links = router.getEngine().getLinks();
46             foreach(Link link; links)
47             {
48                 gprintln("Sending advertisement on "~to!(string)(link)~" ...");
49                 advertise(link);
50             }
51 
52             sleep(dur!("seconds")(2));
53         }
54     }
55 
56     public void launch()
57     {
58         start();
59     }
60 
61     public void shutdown()
62     {
63         /* TODO: Implement me */
64 
65         /* Close the multicast socket */
66         mcastSock.close();
67     }
68 
69     /**
70     * TODO: Move this elsehwre
71     *
72     * Given a publicKey, nexthop this generates the advertisement message
73     */
74     private link.Advertisement makeAdvertisement(Link link, Route[] routes)
75     {
76         /* The advertisement message */
77         Advertisement advMsg = new Advertisement();
78 
79         /**
80         * Construct RouteEntry's
81         */
82         RouteEntry[] entries;
83         foreach(Route route; routes)
84         {
85             /* Copy Route's data to a new RouteEntry */
86             RouteEntry newRouteEntry = new RouteEntry();
87             newRouteEntry.address = route.getAddress();
88             newRouteEntry.metric = route.getMetric();
89             newRouteEntry.creationTime = route.getCreationTime().toISOString();
90 
91             /* Add to list of RouteEntry-s */
92             entries ~= newRouteEntry;
93         }
94 
95         /* Set entries */
96         advMsg.routes = entries;
97 
98         return advMsg;
99     }
100 
101 
102     /**
103     * Send an IPv6 Multicast advertisement via link-local
104     *
105     * Sends to `ff02::1%<interface>:6666`
106     *
107     * TODO: Advertise self (we should insert our own route too perhaps or just do it here (eaiser))
108     *
109     * TODO: We should split advertisements up, depending on the number, into seperate
110     * advertisements
111     */
112     private void advertise(Link link)
113     {
114         /**
115         * Advertise a set of routes over a link to all neighbors
116         * on said link
117         *
118         * TODO: Shard these (batch them)
119         */
120         router.getTable().lockTable();
121         Route[] routes = router.getTable().getRoutes();
122         router.getTable().unlockTable();
123         advertiseRoute(link, routes);      
124     }
125 
126     /**
127     * Advertises the given `routes` on the given `link`
128     */
129     private void advertiseRoute(Link link, Route[] routes)
130     {
131         /**
132         * Construct the Advertisement message for the given Link and
133         * set of routes
134         */
135         Advertisement advMsg = makeAdvertisement(link, routes);
136 
137         /**
138         * Construct a LinkMessage with type=ADVERTISEMENT and
139         * the encoded message above
140         *
141         * Set the public key to ours
142         * Set the signature (TODO)
143         * Set neighbor port
144         */
145         LinkMessage linkMsg = new LinkMessage();
146         linkMsg.type = LinkMessageType.ADVERTISEMENT;
147         linkMsg.payload = array(toProtobuf(advMsg));
148         linkMsg.publicKey = router.getIdentity().getKeys().publicKey;
149         linkMsg.neighborPort = to!(string)(link.getR2RPort());
150         // linkMsg.signature = 
151 
152         /* Encode the LinkMessage */
153         byte[] messageBytes = cast(byte[])array(toProtobuf(linkMsg));
154         ulong stats = mcastSock.sendTo(messageBytes, parseAddress("ff02::1%"~link.getInterface(), 6666));
155         gprintln("Status"~to!(string)(stats));
156     }
157 }