rules: process rules after the local table to ensure that local access still works
[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                         int prio;
72                 } __packed prio;
73                 struct {
74                         struct rtattr rta;
75                         char ifname[IFNAMSIZ + 1];
76                 } __packed dev;
77         } __packed req = {
78                 .rt = {
79                         .rtm_family = AF_INET,
80                         .rtm_table = RT_TABLE_UNSPEC,
81                         .rtm_scope = RT_SCOPE_UNIVERSE,
82                         .rtm_protocol = RTPROT_BOOT,
83                 },
84                 .prio = {
85                         .rta.rta_type = FRA_PRIORITY,
86                         .rta.rta_len = sizeof(req.prio),
87                         .prio = 2,
88                 },
89                 .table.rta = {
90                         .rta_type = FRA_TABLE,
91                         .rta_len = sizeof(req.table),
92                 },
93         };
94         const char *ifname = "lo";
95         int padding = sizeof(req.dev.ifname);
96
97         if (rif)
98                 ifname = rif->ifname;
99
100         if (!(flags & RULE_F_DEFGW_WORKAROUND)) {
101                 req.dev.rta.rta_type = FRA_IFNAME;
102                 padding -= strlen(ifname) + 1;
103                 strcpy(req.dev.ifname, ifname);
104                 req.dev.rta.rta_len = sizeof(req.dev.rta) + strlen(ifname) + 1;
105         } else {
106                 padding = sizeof(req.dev);
107                 req.prio.prio--;
108         }
109         req.table.table = get_route_table(rif);
110         req.nl.nlmsg_len = sizeof(req) - padding;
111
112         req.nl.nlmsg_flags = NLM_F_REQUEST;
113         if (flags & RULE_F_ADD) {
114                 req.nl.nlmsg_type = RTM_NEWRULE;
115                 req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
116
117                 req.rt.rtm_type = RTN_UNICAST;
118         } else {
119                 req.nl.nlmsg_type = RTM_DELRULE;
120                 req.rt.rtm_type = RTN_UNSPEC;
121         }
122
123         send(rtnl_sock.fd, &req, req.nl.nlmsg_len, 0);
124         rtnl_flush();
125 }
126
127 struct rtnl_addr {
128         struct rtattr rta;
129         uint8_t ipaddr[4];
130 } __packed;
131
132 static struct rtnl_addr *
133 rtnl_add_addr(struct rtnl_addr *addr, int *len, int type, const uint8_t *ipaddr)
134 {
135         addr->rta.rta_type = type;
136         memcpy(addr->ipaddr, ipaddr, 4);
137         *len += sizeof(*addr);
138         return addr + 1;
139 }
140
141 static void
142 rtnl_route_request(struct relayd_interface *rif, struct relayd_host *host,
143                    struct relayd_route *route, bool add)
144 {
145         static struct {
146                 struct nlmsghdr nl;
147                 struct rtmsg rt;
148                 struct {
149                         struct rtattr rta;
150                         int table;
151                 } __packed table;
152                 struct {
153                         struct rtattr rta;
154                         int ifindex;
155                 } __packed dev;
156                 struct rtnl_addr addr[3];
157         } __packed req = {
158                 .rt = {
159                         .rtm_family = AF_INET,
160                         .rtm_dst_len = 32,
161                         .rtm_table = RT_TABLE_MAIN,
162                 },
163                 .table.rta = {
164                         .rta_type = RTA_TABLE,
165                         .rta_len = sizeof(req.table),
166                 },
167                 .dev.rta = {
168                         .rta_type = RTA_OIF,
169                         .rta_len = sizeof(req.dev),
170                 },
171                 .addr[0].rta.rta_len = sizeof(struct rtnl_addr),
172                 .addr[1].rta.rta_len = sizeof(struct rtnl_addr),
173                 .addr[2].rta.rta_len = sizeof(struct rtnl_addr),
174         };
175         int pktlen = sizeof(req) - sizeof(req.addr);
176         struct rtnl_addr *addr = &req.addr[0];
177         const char *ifname = "loopback";
178
179         req.dev.ifindex = host->rif->sll.sll_ifindex;
180         req.table.table = get_route_table(rif);
181
182         req.nl.nlmsg_flags = NLM_F_REQUEST;
183         if (add) {
184                 req.nl.nlmsg_type = RTM_NEWROUTE;
185                 req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
186
187                 req.rt.rtm_protocol = RTPROT_BOOT;
188                 if (route) {
189                         req.rt.rtm_scope = RT_SCOPE_UNIVERSE;
190                 } else {
191                         req.rt.rtm_scope = RT_SCOPE_LINK;
192                 }
193                 req.rt.rtm_type = RTN_UNICAST;
194         } else {
195                 req.nl.nlmsg_type = RTM_DELROUTE;
196                 req.rt.rtm_scope = RT_SCOPE_NOWHERE;
197         }
198
199         if (rif)
200                 ifname = rif->ifname;
201
202         if (route) {
203                 DPRINTF(2, "%s: add route to "IP_FMT"/%d via "IP_FMT" (%s)\n", ifname,
204                         IP_BUF(route->dest), route->mask, IP_BUF(host->ipaddr),
205                         host->rif->ifname);
206
207                 req.rt.rtm_dst_len = route->mask;
208                 if (route->mask)
209                         addr = rtnl_add_addr(addr, &pktlen, RTA_DST, route->dest);
210                 addr = rtnl_add_addr(addr, &pktlen, RTA_GATEWAY, host->ipaddr);
211         } else {
212                 DPRINTF(2, "%s: add host route to "IP_FMT" (%s)\n", ifname,
213                         IP_BUF(host->ipaddr), host->rif->ifname);
214                 addr = rtnl_add_addr(addr, &pktlen, RTA_DST, host->ipaddr);
215                 req.rt.rtm_dst_len = 32;
216         }
217
218         /* local route */
219         if (!rif)
220                 addr = rtnl_add_addr(addr, &pktlen, RTA_PREFSRC, local_addr);
221
222         req.nl.nlmsg_len = pktlen;
223         if (route)
224                 rtnl_rule_request(rif, RULE_F_DEFGW_WORKAROUND | RULE_F_ADD);
225         send(rtnl_sock.fd, &req, pktlen, 0);
226         if (route)
227                 rtnl_rule_request(rif, RULE_F_DEFGW_WORKAROUND);
228         rtnl_flush();
229 }
230
231 void
232 rtnl_route_set(struct relayd_host *host, struct relayd_route *route, bool add)
233 {
234         struct relayd_interface *rif;
235
236         list_for_each_entry(rif, &interfaces, list) {
237                 if (rif == host->rif)
238                         continue;
239
240                 rtnl_route_request(rif, host, route, add);
241         }
242         if (local_route_table)
243                 rtnl_route_request(NULL, host, route, add);
244 }
245
246 void relayd_add_interface_routes(struct relayd_interface *rif)
247 {
248         rif->rt_table = route_table++;
249         rtnl_rule_request(rif, RULE_F_ADD);
250 }
251
252 void relayd_del_interface_routes(struct relayd_interface *rif)
253 {
254         rtnl_rule_request(rif, 0);
255 }
256
257 #ifndef NDA_RTA
258 #define NDA_RTA(r) \
259     ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
260 #endif
261
262 static void rtnl_parse_newneigh(struct nlmsghdr *h)
263 {
264         struct relayd_interface *rif = NULL;
265         struct ndmsg *r = NLMSG_DATA(h);
266         const uint8_t *lladdr = NULL;
267         const uint8_t *ipaddr = NULL;
268         struct rtattr *rta;
269         int len;
270
271         if (r->ndm_family != AF_INET)
272                 return;
273
274         list_for_each_entry(rif, &interfaces, list) {
275                 if (rif->sll.sll_ifindex == r->ndm_ifindex)
276                         goto found_interface;
277         }
278         return;
279
280 found_interface:
281         len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
282         for (rta = NDA_RTA(r); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
283                 switch(rta->rta_type) {
284                 case NDA_LLADDR:
285                         lladdr = RTA_DATA(rta);
286                         break;
287                 case NDA_DST:
288                         ipaddr = RTA_DATA(rta);
289                         break;
290                 default:
291                         break;
292                 }
293         }
294
295         if (!lladdr || !ipaddr || (r->ndm_state & (NUD_INCOMPLETE|NUD_FAILED)))
296                 return;
297
298         if (!memcmp(lladdr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN))
299                 return;
300
301         DPRINTF(1, "%s: Found ARP cache entry for host "IP_FMT" ("MAC_FMT")\n",
302                 rif->ifname, IP_BUF(ipaddr), MAC_BUF(lladdr));
303         relayd_refresh_host(rif, lladdr, ipaddr);
304 }
305
306 static void rtnl_parse_packet(void *data, int len)
307 {
308         struct nlmsghdr *h;
309
310         for (h = data; NLMSG_OK(h, len); h = NLMSG_NEXT(h, len)) {
311                 if (h->nlmsg_type == NLMSG_DONE ||
312                     h->nlmsg_type == NLMSG_ERROR)
313                         return;
314
315                 if (h->nlmsg_seq != rtnl_dump_seq)
316                         continue;
317
318                 if (h->nlmsg_type == RTM_NEWNEIGH)
319                         rtnl_parse_newneigh(h);
320         }
321 }
322
323 static void rtnl_cb(struct uloop_fd *fd, unsigned int events)
324 {
325         struct sockaddr_nl nladdr;
326         static uint8_t buf[16384];
327         struct iovec iov = {
328                 .iov_base = buf,
329                 .iov_len = sizeof(buf),
330         };
331         struct msghdr msg = {
332                 .msg_name = &nladdr,
333                 .msg_namelen = sizeof(nladdr),
334                 .msg_iov = &iov,
335                 .msg_iovlen = 1,
336         };
337
338         do {
339                 int len;
340
341                 len = recvmsg(rtnl_sock.fd, &msg, 0);
342                 if (len < 0) {
343                         if (errno == EINTR)
344                                 continue;
345
346                         return;
347                 }
348
349                 if (!len)
350                         break;
351
352                 if (nladdr.nl_pid != 0)
353                         continue;
354
355                 rtnl_parse_packet(buf, len);
356         } while (1);
357 }
358
359 static void rtnl_dump_request(int nlmsg_type)
360 {
361         static struct {
362                 struct nlmsghdr nlh;
363                 struct rtgenmsg g;
364         } req = {
365                 .nlh = {
366                         .nlmsg_len = sizeof(req),
367                         .nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,
368                         .nlmsg_pid = 0,
369                 },
370                 .g.rtgen_family = AF_INET,
371         };
372         req.nlh.nlmsg_type = nlmsg_type;
373         req.nlh.nlmsg_seq = rtnl_seq;
374         send(rtnl_sock.fd, &req, sizeof(req), 0);
375         rtnl_seq++;
376 }
377
378 int relayd_rtnl_init(void)
379 {
380         struct sockaddr_nl snl_local = {};
381
382         rtnl_sock.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
383         if (rtnl_sock.fd < 0) {
384                 perror("socket(AF_NETLINK)");
385                 return -1;
386         }
387
388         snl_local.nl_family = AF_NETLINK;
389
390         if (bind(rtnl_sock.fd, (struct sockaddr *) &snl_local, sizeof(struct sockaddr_nl)) < 0) {
391                 perror("bind");
392                 close(rtnl_sock.fd);
393                 return -1;
394         }
395
396         rtnl_sock.cb = rtnl_cb;
397         uloop_fd_add(&rtnl_sock, ULOOP_READ | ULOOP_EDGE_TRIGGER);
398
399         rtnl_seq = time(NULL);
400         rtnl_dump_seq = rtnl_seq;
401         rtnl_dump_request(RTM_GETNEIGH);
402         rtnl_rule_request(NULL, RULE_F_ADD);
403
404         return 0;
405 }
406
407 void relayd_rtnl_done(void)
408 {
409         rtnl_rule_request(NULL, 0);
410         uloop_fd_delete(&rtnl_sock);
411         close(rtnl_sock.fd);
412 }