03046abf7b0984e0aa51080b8d3d06372d9c4cda
[project/relayd.git] / route.c
1 /*
2  *   Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License v2 as published by
6  *   the Free Software Foundation.
7  *
8  *   This program is distributed in the hope that it will be useful,
9  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *   GNU General Public License for more details.
12  *
13  *   You should have received a copy of the GNU General Public License
14  *   along with this program; if not, write to the Free Software
15  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
16  */
17
18 #include <sys/socket.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <time.h>
25
26 #include <linux/fib_rules.h>
27
28 #include "relayd.h"
29
30 static struct uloop_fd rtnl_sock;
31 static unsigned int rtnl_seq, rtnl_dump_seq;
32 int route_table = 16800;
33
34 static void rtnl_flush(void)
35 {
36         int fd;
37
38         fd = open("/proc/sys/net/ipv4/route/flush", O_WRONLY);
39         if (fd < 0)
40                 return;
41
42         write(fd, "-1", 2);
43         close(fd);
44 }
45
46 enum {
47         RULE_F_ADD = (1 << 0),
48         RULE_F_DEFGW_WORKAROUND = (1 << 1),
49 };
50
51 static int get_route_table(struct relayd_interface *rif)
52 {
53         if (rif)
54                 return rif->rt_table;
55         else
56                 return local_route_table;
57 }
58
59 static void
60 rtnl_rule_request(struct relayd_interface *rif, int flags)
61 {
62         static struct {
63                 struct nlmsghdr nl;
64                 struct rtmsg rt;
65                 struct {
66                         struct rtattr rta;
67                         int table;
68                 } __packed table;
69                 struct {
70                         struct rtattr rta;
71                         char ifname[IFNAMSIZ + 1];
72                 } __packed dev;
73         } __packed req = {
74                 .rt = {
75                         .rtm_family = AF_INET,
76                         .rtm_table = RT_TABLE_UNSPEC,
77                         .rtm_scope = RT_SCOPE_UNIVERSE,
78                         .rtm_protocol = RTPROT_BOOT,
79                 },
80                 .table.rta = {
81                         .rta_type = FRA_TABLE,
82                         .rta_len = sizeof(req.table),
83                 },
84         };
85         const char *ifname = "lo";
86         int padding = sizeof(req.dev.ifname);
87
88         if (rif)
89                 ifname = rif->ifname;
90
91         if (!(flags & RULE_F_DEFGW_WORKAROUND)) {
92                 req.dev.rta.rta_type = FRA_IFNAME;
93                 padding -= strlen(ifname) + 1;
94                 strcpy(req.dev.ifname, ifname);
95                 req.dev.rta.rta_len = sizeof(req.dev.rta) + strlen(ifname) + 1;
96         } else {
97                 req.dev.rta.rta_type = FRA_PRIORITY;
98                 req.dev.rta.rta_len = sizeof(req.dev.rta) + sizeof(uint32_t);
99                 padding -= sizeof(uint32_t);
100                 *((uint32_t *) &req.dev.ifname) = 1;
101         }
102         req.table.table = get_route_table(rif);
103         req.nl.nlmsg_len = sizeof(req) - padding;
104
105         req.nl.nlmsg_flags = NLM_F_REQUEST;
106         if (flags & RULE_F_ADD) {
107                 req.nl.nlmsg_type = RTM_NEWRULE;
108                 req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
109
110                 req.rt.rtm_type = RTN_UNICAST;
111         } else {
112                 req.nl.nlmsg_type = RTM_DELRULE;
113                 req.rt.rtm_type = RTN_UNSPEC;
114         }
115
116         send(rtnl_sock.fd, &req, req.nl.nlmsg_len, 0);
117         rtnl_flush();
118 }
119
120 struct rtnl_addr {
121         struct rtattr rta;
122         uint8_t ipaddr[4];
123 } __packed;
124
125 static struct rtnl_addr *
126 rtnl_add_addr(struct rtnl_addr *addr, int *len, int type, const uint8_t *ipaddr)
127 {
128         addr->rta.rta_type = type;
129         memcpy(addr->ipaddr, ipaddr, 4);
130         *len += sizeof(*addr);
131         return addr + 1;
132 }
133
134 static void
135 rtnl_route_request(struct relayd_interface *rif, struct relayd_host *host,
136                    struct relayd_route *route, bool add)
137 {
138         static struct {
139                 struct nlmsghdr nl;
140                 struct rtmsg rt;
141                 struct {
142                         struct rtattr rta;
143                         int table;
144                 } __packed table;
145                 struct {
146                         struct rtattr rta;
147                         int ifindex;
148                 } __packed dev;
149                 struct rtnl_addr addr[3];
150         } __packed req = {
151                 .rt = {
152                         .rtm_family = AF_INET,
153                         .rtm_dst_len = 32,
154                         .rtm_table = RT_TABLE_MAIN,
155                 },
156                 .table.rta = {
157                         .rta_type = RTA_TABLE,
158                         .rta_len = sizeof(req.table),
159                 },
160                 .dev.rta = {
161                         .rta_type = RTA_OIF,
162                         .rta_len = sizeof(req.dev),
163                 },
164                 .addr[0].rta.rta_len = sizeof(struct rtnl_addr),
165                 .addr[1].rta.rta_len = sizeof(struct rtnl_addr),
166                 .addr[2].rta.rta_len = sizeof(struct rtnl_addr),
167         };
168         int pktlen = sizeof(req) - sizeof(req.addr);
169         struct rtnl_addr *addr = &req.addr[0];
170         const char *ifname = "loopback";
171
172         req.dev.ifindex = host->rif->sll.sll_ifindex;
173         req.table.table = get_route_table(rif);
174
175         req.nl.nlmsg_flags = NLM_F_REQUEST;
176         if (add) {
177                 req.nl.nlmsg_type = RTM_NEWROUTE;
178                 req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
179
180                 req.rt.rtm_protocol = RTPROT_BOOT;
181                 if (route) {
182                         req.rt.rtm_scope = RT_SCOPE_UNIVERSE;
183                 } else {
184                         req.rt.rtm_scope = RT_SCOPE_LINK;
185                 }
186                 req.rt.rtm_type = RTN_UNICAST;
187         } else {
188                 req.nl.nlmsg_type = RTM_DELROUTE;
189                 req.rt.rtm_scope = RT_SCOPE_NOWHERE;
190         }
191
192         if (rif)
193                 ifname = rif->ifname;
194
195         if (route) {
196                 DPRINTF(2, "%s: add route to "IP_FMT"/%d via "IP_FMT" (%s)\n", ifname,
197                         IP_BUF(route->dest), route->mask, IP_BUF(host->ipaddr),
198                         host->rif->ifname);
199
200                 req.rt.rtm_dst_len = route->mask;
201                 if (route->mask)
202                         addr = rtnl_add_addr(addr, &pktlen, RTA_DST, route->dest);
203                 addr = rtnl_add_addr(addr, &pktlen, RTA_GATEWAY, host->ipaddr);
204         } else {
205                 DPRINTF(2, "%s: add host route to "IP_FMT" (%s)\n", ifname,
206                         IP_BUF(host->ipaddr), host->rif->ifname);
207                 addr = rtnl_add_addr(addr, &pktlen, RTA_DST, host->ipaddr);
208                 req.rt.rtm_dst_len = 32;
209         }
210
211         /* local route */
212         if (!rif)
213                 addr = rtnl_add_addr(addr, &pktlen, RTA_PREFSRC, local_addr);
214
215         req.nl.nlmsg_len = pktlen;
216         if (route)
217                 rtnl_rule_request(rif, RULE_F_DEFGW_WORKAROUND | RULE_F_ADD);
218         send(rtnl_sock.fd, &req, pktlen, 0);
219         if (route)
220                 rtnl_rule_request(rif, RULE_F_DEFGW_WORKAROUND);
221         rtnl_flush();
222 }
223
224 void
225 rtnl_route_set(struct relayd_host *host, struct relayd_route *route, bool add)
226 {
227         struct relayd_interface *rif;
228
229         list_for_each_entry(rif, &interfaces, list) {
230                 if (rif == host->rif)
231                         continue;
232
233                 rtnl_route_request(rif, host, route, add);
234         }
235         if (local_route_table)
236                 rtnl_route_request(NULL, host, route, add);
237 }
238
239 void relayd_add_interface_routes(struct relayd_interface *rif)
240 {
241         rif->rt_table = route_table++;
242         rtnl_rule_request(rif, RULE_F_ADD);
243 }
244
245 void relayd_del_interface_routes(struct relayd_interface *rif)
246 {
247         rtnl_rule_request(rif, 0);
248 }
249
250 #ifndef NDA_RTA
251 #define NDA_RTA(r) \
252     ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
253 #endif
254
255 static void rtnl_parse_newneigh(struct nlmsghdr *h)
256 {
257         struct relayd_interface *rif = NULL;
258         struct ndmsg *r = NLMSG_DATA(h);
259         const uint8_t *lladdr = NULL;
260         const uint8_t *ipaddr = NULL;
261         struct rtattr *rta;
262         int len;
263
264         if (r->ndm_family != AF_INET)
265                 return;
266
267         list_for_each_entry(rif, &interfaces, list) {
268                 if (rif->sll.sll_ifindex == r->ndm_ifindex)
269                         goto found_interface;
270         }
271         return;
272
273 found_interface:
274         len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
275         for (rta = NDA_RTA(r); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
276                 switch(rta->rta_type) {
277                 case NDA_LLADDR:
278                         lladdr = RTA_DATA(rta);
279                         break;
280                 case NDA_DST:
281                         ipaddr = RTA_DATA(rta);
282                         break;
283                 default:
284                         break;
285                 }
286         }
287
288         if (!lladdr || !ipaddr || (r->ndm_state & (NUD_INCOMPLETE|NUD_FAILED)))
289                 return;
290
291         if (!memcmp(lladdr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN))
292                 return;
293
294         DPRINTF(1, "%s: Found ARP cache entry for host "IP_FMT" ("MAC_FMT")\n",
295                 rif->ifname, IP_BUF(ipaddr), MAC_BUF(lladdr));
296         relayd_refresh_host(rif, lladdr, ipaddr);
297 }
298
299 static void rtnl_parse_packet(void *data, int len)
300 {
301         struct nlmsghdr *h;
302
303         for (h = data; NLMSG_OK(h, len); h = NLMSG_NEXT(h, len)) {
304                 if (h->nlmsg_type == NLMSG_DONE ||
305                     h->nlmsg_type == NLMSG_ERROR)
306                         return;
307
308                 if (h->nlmsg_seq != rtnl_dump_seq)
309                         continue;
310
311                 if (h->nlmsg_type == RTM_NEWNEIGH)
312                         rtnl_parse_newneigh(h);
313         }
314 }
315
316 static void rtnl_cb(struct uloop_fd *fd, unsigned int events)
317 {
318         struct sockaddr_nl nladdr;
319         static uint8_t buf[16384];
320         struct iovec iov = {
321                 .iov_base = buf,
322                 .iov_len = sizeof(buf),
323         };
324         struct msghdr msg = {
325                 .msg_name = &nladdr,
326                 .msg_namelen = sizeof(nladdr),
327                 .msg_iov = &iov,
328                 .msg_iovlen = 1,
329         };
330
331         do {
332                 int len;
333
334                 len = recvmsg(rtnl_sock.fd, &msg, 0);
335                 if (len < 0) {
336                         if (errno == EINTR)
337                                 continue;
338
339                         return;
340                 }
341
342                 if (!len)
343                         break;
344
345                 if (nladdr.nl_pid != 0)
346                         continue;
347
348                 rtnl_parse_packet(buf, len);
349         } while (1);
350 }
351
352 static void rtnl_dump_request(int nlmsg_type)
353 {
354         static struct {
355                 struct nlmsghdr nlh;
356                 struct rtgenmsg g;
357         } req = {
358                 .nlh = {
359                         .nlmsg_len = sizeof(req),
360                         .nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,
361                         .nlmsg_pid = 0,
362                 },
363                 .g.rtgen_family = AF_INET,
364         };
365         req.nlh.nlmsg_type = nlmsg_type;
366         req.nlh.nlmsg_seq = rtnl_seq;
367         send(rtnl_sock.fd, &req, sizeof(req), 0);
368         rtnl_seq++;
369 }
370
371 int relayd_rtnl_init(void)
372 {
373         struct sockaddr_nl snl_local;
374
375         rtnl_sock.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
376         if (rtnl_sock.fd < 0) {
377                 perror("socket(AF_NETLINK)");
378                 return -1;
379         }
380
381         snl_local.nl_family = AF_NETLINK;
382
383         if (bind(rtnl_sock.fd, (struct sockaddr *) &snl_local, sizeof(struct sockaddr_nl)) < 0) {
384                 perror("bind");
385                 close(rtnl_sock.fd);
386                 return -1;
387         }
388
389         rtnl_sock.cb = rtnl_cb;
390         uloop_fd_add(&rtnl_sock, ULOOP_READ | ULOOP_EDGE_TRIGGER);
391
392         rtnl_seq = time(NULL);
393         rtnl_dump_seq = rtnl_seq;
394         rtnl_dump_request(RTM_GETNEIGH);
395         rtnl_rule_request(NULL, RULE_F_ADD);
396
397         return 0;
398 }
399
400 void relayd_rtnl_done(void)
401 {
402         rtnl_rule_request(NULL, 0);
403         uloop_fd_delete(&rtnl_sock);
404         close(rtnl_sock.fd);
405 }