Flush the route cache after changing routes
[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
25 #include "relayd.h"
26
27 static struct uloop_fd rtnl_sock;
28 static unsigned int rtnl_seq, rtnl_dump_seq;
29
30 static void rtnl_flush(void)
31 {
32         int fd;
33
34         fd = open("/proc/sys/net/ipv4/route/flush", O_WRONLY);
35         if (fd < 0)
36                 return;
37
38         write(fd, "-1", 2);
39         close(fd);
40 }
41
42 static void rtnl_route_set(struct relayd_host *host, bool add)
43 {
44         static struct {
45                 struct nlmsghdr nl;
46                 struct rtmsg rt;
47                 struct {
48                         struct rtattr rta;
49                         uint8_t ipaddr[4];
50                 } __packed dst;
51                 struct {
52                         struct rtattr rta;
53                         int ifindex;
54                 } __packed dev;
55         } __packed req;
56
57         memset(&req, 0, sizeof(req));
58
59         req.nl.nlmsg_len = sizeof(req);
60         req.rt.rtm_family = AF_INET;
61         req.rt.rtm_dst_len = 32;
62
63         req.dst.rta.rta_type = RTA_DST;
64         req.dst.rta.rta_len = sizeof(req.dst);
65         memcpy(req.dst.ipaddr, host->ipaddr, sizeof(req.dst.ipaddr));
66
67         req.dev.rta.rta_type = RTA_OIF;
68         req.dev.rta.rta_len = sizeof(req.dev);
69         req.dev.ifindex = host->rif->sll.sll_ifindex;
70
71         req.nl.nlmsg_flags = NLM_F_REQUEST;
72         req.rt.rtm_table = RT_TABLE_MAIN;
73         if (add) {
74                 req.nl.nlmsg_type = RTM_NEWROUTE;
75                 req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
76
77                 req.rt.rtm_protocol = RTPROT_BOOT;
78                 req.rt.rtm_scope = RT_SCOPE_LINK;
79                 req.rt.rtm_type = RTN_UNICAST;
80         } else {
81                 req.nl.nlmsg_type = RTM_DELROUTE;
82                 req.rt.rtm_scope = RT_SCOPE_NOWHERE;
83         }
84
85         send(rtnl_sock.fd, &req, sizeof(req), 0);
86         rtnl_flush();
87 }
88
89 void relayd_add_route(struct relayd_host *host)
90 {
91         rtnl_route_set(host, true);
92 }
93
94 void relayd_del_route(struct relayd_host *host)
95 {
96         rtnl_route_set(host, false);
97 }
98
99 #ifndef NDA_RTA
100 #define NDA_RTA(r) \
101     ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
102 #endif
103
104 static void rtnl_parse_newneigh(struct nlmsghdr *h)
105 {
106         struct relayd_interface *rif = NULL;
107         struct ndmsg *r = NLMSG_DATA(h);
108         const uint8_t *lladdr = NULL;
109         const uint8_t *ipaddr = NULL;
110         struct rtattr *rta;
111         int len;
112
113         if (r->ndm_family != AF_INET)
114                 return;
115
116         list_for_each_entry(rif, &interfaces, list) {
117                 if (rif->sll.sll_ifindex == r->ndm_ifindex)
118                         goto found_interface;
119         }
120         return;
121
122 found_interface:
123         len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
124         for (rta = NDA_RTA(r); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
125                 switch(rta->rta_type) {
126                 case NDA_LLADDR:
127                         lladdr = RTA_DATA(rta);
128                         break;
129                 case NDA_DST:
130                         ipaddr = RTA_DATA(rta);
131                         break;
132                 default:
133                         break;
134                 }
135         }
136
137         if (!lladdr || !ipaddr || (r->ndm_state & (NUD_INCOMPLETE|NUD_FAILED)))
138                 return;
139
140         if (!memcmp(lladdr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN))
141                 return;
142
143         DPRINTF(1, "%s: Found ARP cache entry for host "IP_FMT" ("MAC_FMT")\n",
144                 rif->ifname, IP_BUF(ipaddr), MAC_BUF(lladdr));
145         relayd_refresh_host(rif, lladdr, ipaddr);
146 }
147
148 static void rtnl_parse_packet(void *data, int len)
149 {
150         struct nlmsghdr *h;
151
152         for (h = data; NLMSG_OK(h, len); h = NLMSG_NEXT(h, len)) {
153                 if (h->nlmsg_type == NLMSG_DONE ||
154                     h->nlmsg_type == NLMSG_ERROR)
155                         return;
156
157                 if (h->nlmsg_seq != rtnl_dump_seq)
158                         continue;
159
160                 if (h->nlmsg_type == RTM_NEWNEIGH)
161                         rtnl_parse_newneigh(h);
162         }
163 }
164
165 static void rtnl_cb(struct uloop_fd *fd, unsigned int events)
166 {
167         struct sockaddr_nl nladdr;
168         static uint8_t buf[16384];
169         struct iovec iov = {
170                 .iov_base = buf,
171                 .iov_len = sizeof(buf),
172         };
173         struct msghdr msg = {
174                 .msg_name = &nladdr,
175                 .msg_namelen = sizeof(nladdr),
176                 .msg_iov = &iov,
177                 .msg_iovlen = 1,
178         };
179
180         do {
181                 int len;
182
183                 len = recvmsg(rtnl_sock.fd, &msg, 0);
184                 if (len < 0) {
185                         if (errno == EINTR)
186                                 continue;
187
188                         return;
189                 }
190
191                 if (!len)
192                         break;
193
194                 if (nladdr.nl_pid != 0)
195                         continue;
196
197                 rtnl_parse_packet(buf, len);
198         } while (1);
199 }
200
201 int relayd_rtnl_init(void)
202 {
203         struct sockaddr_nl snl_local;
204         static struct {
205                 struct nlmsghdr nlh;
206                 struct rtgenmsg g;
207         } req = {
208                 .nlh = {
209                         .nlmsg_len = sizeof(req),
210                         .nlmsg_type = RTM_GETNEIGH,
211                         .nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,
212                         .nlmsg_pid = 0,
213                 },
214                 .g.rtgen_family = AF_INET,
215         };
216
217         rtnl_sock.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
218         if (rtnl_sock.fd < 0) {
219                 perror("socket(AF_NETLINK)");
220                 return -1;
221         }
222
223         snl_local.nl_family = AF_NETLINK;
224
225         if (bind(rtnl_sock.fd, (struct sockaddr *) &snl_local, sizeof(struct sockaddr_nl)) < 0) {
226                 perror("bind");
227                 close(rtnl_sock.fd);
228                 return -1;
229         }
230
231         rtnl_sock.cb = rtnl_cb;
232         uloop_fd_add(&rtnl_sock, ULOOP_READ | ULOOP_EDGE_TRIGGER);
233
234         rtnl_seq = time(NULL);
235         rtnl_dump_seq = rtnl_seq;
236         req.nlh.nlmsg_seq = rtnl_seq;
237         send(rtnl_sock.fd, &req, sizeof(req), 0);
238
239         return 0;
240 }
241
242 void relayd_rtnl_done(void)
243 {
244         uloop_fd_delete(&rtnl_sock);
245         close(rtnl_sock.fd);
246 }