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 
90             /* Add to list of RouteEntry-s */
91             entries ~= newRouteEntry;
92         }
93 
94         /* Set entries */
95         advMsg.routes = entries;
96 
97         return advMsg;
98     }
99 
100 
101     /**
102     * Send an IPv6 Multicast advertisement via link-local
103     *
104     * Sends to `ff02::1%<interface>:6666`
105     *
106     * TODO: Advertise self (we should insert our own route too perhaps or just do it here (eaiser))
107     *
108     * TODO: We should split advertisements up, depending on the number, into seperate
109     * advertisements
110     */
111     private void advertise(Link link)
112     {
113         /**
114         * Advertise a set of routes over a link to all neighbors
115         * on said link
116         *
117         * TODO: Shard these (batch them)
118         */
119         Route[] routes = router.getTable().getRoutes();
120         advertiseRoute(link, routes);      
121     }
122 
123     /**
124     * Advertises the given `routes` on the given `link`
125     */
126     private void advertiseRoute(Link link, Route[] routes)
127     {
128         /**
129         * Construct the Advertisement message for the given Link and
130         * set of routes
131         */
132         Advertisement advMsg = makeAdvertisement(link, routes);
133 
134         /**
135         * Construct a LinkMessage with type=ADVERTISEMENT and
136         * the encoded message above
137         *
138         * Set the public key to ours
139         * Set the signature (TODO)
140         * Set neighbor port
141         */
142         LinkMessage linkMsg = new LinkMessage();
143         linkMsg.type = LinkMessageType.ADVERTISEMENT;
144         linkMsg.payload = array(toProtobuf(advMsg));
145         linkMsg.publicKey = router.getIdentity().getKeys().publicKey;
146         linkMsg.neighborPort = to!(string)(link.getR2RPort());
147         // linkMsg.signature = 
148 
149         /* Encode the LinkMessage */
150         byte[] messageBytes = cast(byte[])array(toProtobuf(linkMsg));
151         ulong stats = mcastSock.sendTo(messageBytes, parseAddress("ff02::1%"~link.getInterface(), 6666));
152         gprintln("Status"~to!(string)(stats));
153     }
154 }