main: avoid adding redundant interfaces
[project/relayd.git] / main.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/ioctl.h>
19 #include <sys/socket.h>
20
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <stdint.h>
27 #include <stdbool.h>
28 #include <errno.h>
29 #include <signal.h>
30 #include <string.h>
31
32 #include "relayd.h"
33
34 static LIST_HEAD(pending_routes);
35 LIST_HEAD(interfaces);
36 int debug;
37
38 static int host_timeout;
39 static int host_ping_tries;
40 static int inet_sock;
41 static int forward_bcast;
42 static int forward_dhcp;
43 static int parse_dhcp;
44
45 uint8_t local_addr[4];
46 int local_route_table;
47
48 struct relayd_pending_route {
49         struct relayd_route rt;
50         struct uloop_timeout timeout;
51         uint8_t gateway[4];
52 };
53
54 static struct relayd_host *find_host_by_ipaddr(struct relayd_interface *rif, const uint8_t *ipaddr)
55 {
56         struct relayd_host *host;
57
58         if (!rif) {
59                 list_for_each_entry(rif, &interfaces, list) {
60                         host = find_host_by_ipaddr(rif, ipaddr);
61                         if (!host)
62                                 continue;
63
64                         return host;
65                 }
66                 return NULL;
67         }
68
69         list_for_each_entry(host, &rif->hosts, list) {
70                 if (memcmp(ipaddr, host->ipaddr, sizeof(host->ipaddr)) != 0)
71                         continue;
72
73                 return host;
74         }
75         return NULL;
76 }
77
78 static void add_arp(struct relayd_host *host)
79 {
80         struct sockaddr_in *sin;
81         struct arpreq arp;
82
83         strncpy(arp.arp_dev, host->rif->ifname, sizeof(arp.arp_dev));
84         arp.arp_flags = ATF_COM;
85
86         arp.arp_ha.sa_family = ARPHRD_ETHER;
87         memcpy(arp.arp_ha.sa_data, host->lladdr, ETH_ALEN);
88
89         sin = (struct sockaddr_in *) &arp.arp_pa;
90         sin->sin_family = AF_INET;
91         memcpy(&sin->sin_addr, host->ipaddr, sizeof(host->ipaddr));
92
93         ioctl(inet_sock, SIOCSARP, &arp);
94 }
95
96 static void timeout_host_route(struct uloop_timeout *timeout)
97 {
98         struct relayd_pending_route *rt;
99
100         rt = container_of(timeout, struct relayd_pending_route, timeout);
101         list_del(&rt->rt.list);
102         free(rt);
103 }
104
105 void relayd_add_host_route(struct relayd_host *host, const uint8_t *dest, uint8_t mask)
106 {
107         struct relayd_route *rt;
108
109         list_for_each_entry(rt, &host->routes, list) {
110                 if (!memcmp(rt->dest, dest, sizeof(rt->dest)) && rt->mask == mask)
111                         return;
112         }
113
114         rt = calloc(1, sizeof(*rt));
115         if (!rt)
116                 return;
117
118         list_add(&rt->list, &host->routes);
119         memcpy(rt->dest, dest, sizeof(rt->dest));
120         rt->mask = mask;
121         relayd_add_route(host, rt);
122 }
123
124 static void del_host(struct relayd_host *host)
125 {
126         struct relayd_route *route, *tmp;
127
128         DPRINTF(1, "%s: deleting host "IP_FMT" ("MAC_FMT")\n", host->rif->ifname,
129                 IP_BUF(host->ipaddr), MAC_BUF(host->lladdr));
130
131         list_for_each_entry_safe(route, tmp, &host->routes, list) {
132                 relayd_del_route(host, route);
133                 list_del(&route->list);
134                 free(route);
135         }
136         if (host->rif->managed)
137                 relayd_del_route(host, NULL);
138         uloop_timeout_cancel(&host->timeout);
139         list_del(&host->list);
140         free(host);
141 }
142
143 static void fill_arp_packet(struct arp_packet *pkt, struct relayd_interface *rif,
144                              const uint8_t spa[4], const uint8_t tpa[4])
145 {
146         memset(pkt, 0, sizeof(*pkt));
147
148         pkt->eth.ether_type = htons(ETHERTYPE_ARP);
149         memcpy(pkt->eth.ether_shost, rif->sll.sll_addr, ETH_ALEN);
150
151         memcpy(pkt->arp.arp_sha, rif->sll.sll_addr, ETH_ALEN);
152         memcpy(pkt->arp.arp_spa, spa, 4);
153         memcpy(pkt->arp.arp_tpa, tpa, 4);
154
155         pkt->arp.arp_hrd = htons(ARPHRD_ETHER);
156         pkt->arp.arp_pro = htons(ETH_P_IP);
157         pkt->arp.arp_hln = ETH_ALEN;
158         pkt->arp.arp_pln = 4;
159 }
160
161 static void send_arp_request(struct relayd_interface *rif, const uint8_t *ipaddr)
162 {
163         struct arp_packet pkt;
164
165         fill_arp_packet(&pkt, rif, rif->src_ip, ipaddr);
166
167         pkt.arp.arp_op = htons(ARPOP_REQUEST);
168         memcpy(pkt.arp.arp_spa, rif->src_ip, sizeof(pkt.arp.arp_spa));
169         memset(pkt.arp.arp_tha, 0, ETH_ALEN);
170         memset(pkt.eth.ether_dhost, 0xff, ETH_ALEN);
171
172         DPRINTF(2, "%s: sending ARP who-has "IP_FMT", tell "IP_FMT" ("MAC_FMT")\n",
173                 rif->ifname, IP_BUF(pkt.arp.arp_tpa),
174                 IP_BUF(pkt.arp.arp_spa), MAC_BUF(pkt.eth.ether_shost));
175
176         sendto(rif->fd.fd, &pkt, sizeof(pkt), 0,
177                 (struct sockaddr *) &rif->sll, sizeof(rif->sll));
178 }
179
180 void relayd_add_pending_route(const uint8_t *gateway, const uint8_t *dest, uint8_t mask, int timeout)
181 {
182         struct relayd_pending_route *rt;
183         struct relayd_interface *rif;
184         struct relayd_host *host;
185
186         host = find_host_by_ipaddr(NULL, gateway);
187         if (host) {
188                 relayd_add_host_route(host, dest, mask);
189                 return;
190         }
191
192         rt = calloc(1, sizeof(*rt));
193         if (!rt)
194                 return;
195
196         memcpy(rt->gateway, gateway, sizeof(rt->gateway));
197         memcpy(rt->rt.dest, dest, sizeof(rt->rt.dest));
198         rt->rt.mask = mask;
199         list_add(&rt->rt.list, &pending_routes);
200         if (timeout <= 0)
201                 return;
202
203         rt->timeout.cb = timeout_host_route;
204         uloop_timeout_set(&rt->timeout, 10000);
205         list_for_each_entry(rif, &interfaces, list) {
206                 send_arp_request(rif, gateway);
207         }
208 }
209
210 static void send_arp_reply(struct relayd_interface *rif, const uint8_t spa[4],
211                            const uint8_t tha[ETH_ALEN], const uint8_t tpa[4])
212 {
213         struct arp_packet pkt;
214
215         fill_arp_packet(&pkt, rif, spa, tpa);
216
217         if (tha) {
218                 pkt.arp.arp_op = htons(ARPOP_REPLY);
219                 memcpy(pkt.eth.ether_dhost, tha, ETH_ALEN);
220                 memcpy(pkt.arp.arp_tha, tha, ETH_ALEN);
221
222                 DPRINTF(2, "%s: sending ARP reply to "IP_FMT", "IP_FMT" is at ("MAC_FMT")\n",
223                         rif->ifname, IP_BUF(pkt.arp.arp_tpa),
224                         IP_BUF(pkt.arp.arp_spa), MAC_BUF(pkt.eth.ether_shost));
225         } else {
226                 pkt.arp.arp_op = htons(ARPOP_REQUEST);
227                 memset(pkt.eth.ether_dhost, 0xff, ETH_ALEN);
228                 memset(pkt.arp.arp_tha, 0xff, ETH_ALEN);
229
230                 DPRINTF(2, "%s: sending gratuitous ARP: "IP_FMT" is at ("MAC_FMT")\n",
231                         rif->ifname, IP_BUF(pkt.arp.arp_tpa),
232                         MAC_BUF(pkt.eth.ether_shost));
233         }
234
235         sendto(rif->fd.fd, &pkt, sizeof(pkt), 0,
236                 (struct sockaddr *) &rif->sll, sizeof(rif->sll));
237
238         if (tha)
239                 return;
240
241         /*
242          * Gratuitous ARP comes in two flavours, request and reply.
243          * Some operating systems only accept request, some only reply.
244          * Let's just send both...
245          */
246         pkt.arp.arp_op = htons(ARPOP_REPLY);
247
248         sendto(rif->fd.fd, &pkt, sizeof(pkt), 0,
249                 (struct sockaddr *) &rif->sll, sizeof(rif->sll));
250
251 }
252
253 static void host_entry_timeout(struct uloop_timeout *timeout)
254 {
255         struct relayd_host *host = container_of(timeout, struct relayd_host, timeout);
256         struct relayd_interface *rif;
257
258         /*
259          * When a host is behind a managed interface, we must not expire its host
260          * entry prematurely, as this will cause routes to the node to expire,
261          * leading to loss of connectivity from the other side.
262          * When the timeout is reached, try pinging the host a few times before
263          * giving up on it.
264          */
265         if (host->rif->managed && host->cleanup_pending < host_ping_tries) {
266                 list_for_each_entry(rif, &interfaces, list) {
267                         send_arp_request(rif, host->ipaddr);
268                 }
269                 host->cleanup_pending++;
270                 uloop_timeout_set(&host->timeout, 1000);
271                 return;
272         }
273         del_host(host);
274 }
275
276 static struct relayd_host *add_host(struct relayd_interface *rif, const uint8_t *lladdr, const uint8_t *ipaddr)
277 {
278         struct relayd_host *host;
279         struct relayd_pending_route *route, *rtmp;
280
281         DPRINTF(1, "%s: adding host "IP_FMT" ("MAC_FMT")\n", rif->ifname,
282                         IP_BUF(ipaddr), MAC_BUF(lladdr));
283
284         host = calloc(1, sizeof(*host));
285         INIT_LIST_HEAD(&host->routes);
286         host->rif = rif;
287         memcpy(host->ipaddr, ipaddr, sizeof(host->ipaddr));
288         memcpy(host->lladdr, lladdr, sizeof(host->lladdr));
289         list_add(&host->list, &rif->hosts);
290         host->timeout.cb = host_entry_timeout;
291         uloop_timeout_set(&host->timeout, host_timeout * 1000);
292
293         add_arp(host);
294         if (rif->managed)
295                 relayd_add_route(host, NULL);
296
297         list_for_each_entry_safe(route, rtmp, &pending_routes, rt.list) {
298                 if (memcmp(route->gateway, ipaddr, 4) != 0)
299                         continue;
300
301                 relayd_add_host_route(host, route->rt.dest, route->rt.mask);
302                 if (!route->timeout.pending)
303                         continue;
304
305                 uloop_timeout_cancel(&route->timeout);
306                 list_del(&route->rt.list);
307                 free(route);
308         }
309
310         return host;
311 }
312
313 static void send_gratuitous_arp(struct relayd_interface *rif, const uint8_t *spa)
314 {
315         struct relayd_interface *to_rif;
316
317         list_for_each_entry(to_rif, &interfaces, list) {
318                 if (rif == to_rif)
319                         continue;
320
321                 send_arp_reply(to_rif, spa, NULL, spa);
322         }
323 }
324
325
326 struct relayd_host *relayd_refresh_host(struct relayd_interface *rif, const uint8_t *lladdr, const uint8_t *ipaddr)
327 {
328         struct relayd_host *host;
329
330         host = find_host_by_ipaddr(rif, ipaddr);
331         if (!host) {
332                 host = find_host_by_ipaddr(NULL, ipaddr);
333
334                 /* 
335                  * When we suddenly see the host appearing on a different interface,
336                  * reduce the timeout to make the old entry expire faster, in case the
337                  * host has moved.
338                  * If the old entry is behind a managed interface, it will be pinged
339                  * before we expire it
340                  */
341                 if (host && !host->cleanup_pending) {
342                         uloop_timeout_set(&host->timeout, 1);
343                         return NULL;
344                 }
345
346                 host = add_host(rif, lladdr, ipaddr);
347         } else {
348                 host->cleanup_pending = false;
349                 uloop_timeout_set(&host->timeout, host_timeout * 1000);
350                 send_gratuitous_arp(rif, ipaddr);
351         }
352
353         return host;
354 }
355
356 static void relay_arp_request(struct relayd_interface *from_rif, struct arp_packet *pkt)
357 {
358         struct relayd_interface *rif;
359         struct arp_packet reqpkt;
360
361         memcpy(&reqpkt, pkt, sizeof(reqpkt));
362         list_for_each_entry(rif, &interfaces, list) {
363                 if (rif == from_rif)
364                         continue;
365
366                 memcpy(reqpkt.eth.ether_shost, rif->sll.sll_addr, ETH_ALEN);
367                 memset(reqpkt.eth.ether_dhost, 0xff, ETH_ALEN);
368                 memcpy(reqpkt.arp.arp_sha, rif->sll.sll_addr, ETH_ALEN);
369                 memset(reqpkt.arp.arp_tha, 0, ETH_ALEN);
370
371                 DPRINTF(2, "%s: sending ARP who-has "IP_FMT", tell "IP_FMT" ("MAC_FMT")\n",
372                         rif->ifname, IP_BUF(reqpkt.arp.arp_tpa),
373                         IP_BUF(reqpkt.arp.arp_spa), MAC_BUF(reqpkt.eth.ether_shost));
374
375                 sendto(rif->fd.fd, &reqpkt, sizeof(reqpkt), 0,
376                         (struct sockaddr *) &rif->sll, sizeof(rif->sll));
377         }
378 }
379
380 static void recv_arp_request(struct relayd_interface *rif, struct arp_packet *pkt)
381 {
382         struct relayd_host *host;
383
384         DPRINTF(2, "%s: ARP who-has "IP_FMT", tell "IP_FMT" ("MAC_FMT")\n",
385                 rif->ifname,
386                 IP_BUF(pkt->arp.arp_tpa),
387                 IP_BUF(pkt->arp.arp_spa),
388                 MAC_BUF(pkt->eth.ether_shost));
389
390         if (!memcmp(pkt->arp.arp_spa, "\x00\x00\x00\x00", 4))
391                 return;
392
393         host = find_host_by_ipaddr(NULL, pkt->arp.arp_spa);
394         if (!host || host->rif != rif)
395                 relayd_refresh_host(rif, pkt->eth.ether_shost, pkt->arp.arp_spa);
396
397         if (local_route_table && !memcmp(pkt->arp.arp_tpa, local_addr, sizeof(local_addr))) {
398                 send_arp_reply(rif, local_addr, pkt->arp.arp_sha, pkt->arp.arp_spa);
399                 return;
400         }
401
402         host = find_host_by_ipaddr(NULL, pkt->arp.arp_tpa);
403
404         /*
405          * If a host is being pinged because of a timeout, do not use the cached
406          * entry here. That way we can avoid giving out stale data in case the node
407          * has moved. We shouldn't relay requests here either, as we might miss our
408          * chance to create a host route.
409          */
410         if (host && host->cleanup_pending)
411                 return;
412
413         relay_arp_request(rif, pkt);
414 }
415
416 static void recv_arp_reply(struct relayd_interface *rif, struct arp_packet *pkt)
417 {
418         struct relayd_host *host;
419
420         DPRINTF(2, "%s: received ARP reply for "IP_FMT" from "MAC_FMT", deliver to "IP_FMT"\n",
421                 rif->ifname,
422                 IP_BUF(pkt->arp.arp_spa),
423                 MAC_BUF(pkt->eth.ether_shost),
424                 IP_BUF(pkt->arp.arp_tpa));
425
426         if (memcmp(pkt->arp.arp_sha, rif->sll.sll_addr, ETH_ALEN) != 0)
427                 relayd_refresh_host(rif, pkt->arp.arp_sha, pkt->arp.arp_spa);
428
429         host = find_host_by_ipaddr(NULL, pkt->arp.arp_tpa);
430         if (!host)
431                 return;
432
433         if (host->rif == rif)
434                 return;
435
436         send_arp_reply(host->rif, pkt->arp.arp_spa, host->lladdr, host->ipaddr);
437 }
438
439 static void recv_packet(struct uloop_fd *fd, unsigned int events)
440 {
441         struct relayd_interface *rif = container_of(fd, struct relayd_interface, fd);
442         struct arp_packet *pkt;
443         static char pktbuf[4096];
444         int pktlen;
445
446         do {
447                 if (rif->fd.error)
448                         uloop_end();
449
450                 pktlen = recv(rif->fd.fd, pktbuf, sizeof(pktbuf), 0);
451                 if (pktlen < 0) {
452                         if (errno == EINTR)
453                                 continue;
454
455                         break;
456                 }
457
458                 if (!pktlen)
459                         break;
460
461                 pkt = (void *)pktbuf;
462                 if (pkt->arp.arp_op == htons(ARPOP_REPLY))
463                         recv_arp_reply(rif, pkt);
464                 else if (pkt->arp.arp_op == htons(ARPOP_REQUEST))
465                         recv_arp_request(rif, pkt);
466                 else
467                         DPRINTF(1, "received unknown packet type: %04x\n", ntohs(pkt->arp.arp_op));
468
469         } while (1);
470 }
471
472 void relayd_forward_bcast_packet(struct relayd_interface *from_rif, void *packet, int len)
473 {
474         struct relayd_interface *rif;
475         struct ether_header *eth = packet;
476
477         list_for_each_entry(rif, &interfaces, list) {
478                 if (rif == from_rif)
479                         continue;
480
481                 DPRINTF(3, "%s: forwarding broadcast packet to %s\n", from_rif->ifname, rif->ifname);
482                 memcpy(eth->ether_shost, rif->sll.sll_addr, ETH_ALEN);
483                 send(rif->bcast_fd.fd, packet, len, 0);
484         }
485 }
486
487 static void recv_bcast_packet(struct uloop_fd *fd, unsigned int events)
488 {
489         struct relayd_interface *rif = container_of(fd, struct relayd_interface, bcast_fd);
490         static char pktbuf[4096];
491         int pktlen;
492
493         do {
494                 if (rif->fd.error)
495                         uloop_end();
496
497                 pktlen = recv(rif->bcast_fd.fd, pktbuf, sizeof(pktbuf), 0);
498                 if (pktlen < 0) {
499                         if (errno == EINTR)
500                                 continue;
501
502                         break;
503                 }
504
505                 if (!pktlen)
506                         break;
507
508                 if (!forward_bcast && !forward_dhcp)
509                         continue;
510
511                 if (relayd_handle_dhcp_packet(rif, pktbuf, pktlen, forward_dhcp, parse_dhcp))
512                         continue;
513
514                 if (forward_bcast)
515                         relayd_forward_bcast_packet(rif, pktbuf, pktlen);
516         } while (1);
517 }
518
519
520 static int init_interface(struct relayd_interface *rif)
521 {
522         struct sockaddr_ll *sll = &rif->sll;
523         struct sockaddr_in *sin;
524         struct ifreq ifr;
525         int fd = rif->fd.fd;
526 #ifdef PACKET_RECV_TYPE
527         unsigned int pkt_type;
528 #endif
529
530         fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
531         if (fd < 0)
532                 return -1;
533
534         rif->fd.fd = fd;
535
536         memset(&ifr, 0, sizeof(ifr));
537         strcpy(ifr.ifr_name, rif->ifname);
538
539         if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
540                 perror("ioctl(SIOCGIFHWADDR)");
541                 return -1;
542         }
543
544         memcpy(sll->sll_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
545         sll->sll_family = AF_PACKET;
546         sll->sll_protocol = htons(ETH_P_ARP);
547         sll->sll_pkttype = PACKET_BROADCAST;
548         sll->sll_hatype = ARPHRD_ETHER;
549         sll->sll_halen = ETH_ALEN;
550
551         if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
552                 perror("ioctl(SIOCGIFINDEX)");
553                 return -1;
554         }
555
556         sll->sll_ifindex = ifr.ifr_ifindex;
557
558         if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
559                 memcpy(rif->src_ip, DUMMY_IP, sizeof(rif->src_ip));
560         } else {
561                 sin = (struct sockaddr_in *) &ifr.ifr_addr;
562                 memcpy(rif->src_ip, &sin->sin_addr.s_addr, sizeof(rif->src_ip));
563         }
564
565         if (bind(fd, (struct sockaddr *)sll, sizeof(struct sockaddr_ll)) < 0) {
566                 perror("bind(ETH_P_ARP)");
567                 return -1;
568         }
569
570         rif->fd.cb = recv_packet;
571         uloop_fd_add(&rif->fd, ULOOP_READ | ULOOP_EDGE_TRIGGER);
572
573         if (!forward_bcast && !forward_dhcp)
574                 return 0;
575
576         fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
577         if (fd < 0)
578                 return 0;
579
580         rif->bcast_fd.fd = fd;
581         rif->bcast_fd.cb = recv_bcast_packet;
582
583         memcpy(&rif->bcast_sll, &rif->sll, sizeof(rif->bcast_sll));
584         sll = &rif->bcast_sll;
585         sll->sll_protocol = htons(ETH_P_IP);
586
587         if (bind(fd, (struct sockaddr *)sll, sizeof(struct sockaddr_ll)) < 0) {
588                 perror("bind(ETH_P_IP)");
589                 return 0;
590         }
591
592 #ifdef PACKET_RECV_TYPE
593         pkt_type = (1 << PACKET_BROADCAST) | (1 << PACKET_MULTICAST);
594         setsockopt(fd, SOL_PACKET, PACKET_RECV_TYPE, &pkt_type, sizeof(pkt_type));
595 #endif
596
597         uloop_fd_add(&rif->bcast_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER);
598         relayd_add_interface_routes(rif);
599         return 0;
600 }
601
602 static void ping_static_routes(void)
603 {
604         struct relayd_pending_route *rt;
605         struct relayd_interface *rif;
606
607         list_for_each_entry(rt, &pending_routes, rt.list)
608                 list_for_each_entry(rif, &interfaces, list)
609                         send_arp_request(rif, rt->gateway);
610 }
611
612 static int init_interfaces(void)
613 {
614         struct relayd_interface *rif;
615         int ret;
616
617         list_for_each_entry(rif, &interfaces, list) {
618                 ret = init_interface(rif);
619                 if (ret < 0)
620                         return ret;
621         }
622
623         return 0;
624 }
625
626 static void cleanup_hosts(void)
627 {
628         struct relayd_interface *rif;
629         struct relayd_host *host, *tmp;
630
631         list_for_each_entry(rif, &interfaces, list) {
632                 list_for_each_entry_safe(host, tmp, &rif->hosts, list) {
633                         del_host(host);
634                 }
635         }
636 }
637
638 static void free_interfaces(void)
639 {
640         struct relayd_interface *rif, *rtmp;
641
642         list_for_each_entry_safe(rif, rtmp, &interfaces, list) {
643                 relayd_del_interface_routes(rif);
644                 list_del(&rif->list);
645                 free(rif);
646         }
647 }
648
649 static struct relayd_interface *alloc_interface(const char *ifname, bool managed)
650 {
651         struct relayd_interface *rif;
652
653         if (strlen(ifname) >= IFNAMSIZ)
654                 return NULL;
655
656         list_for_each_entry(rif, &interfaces, list) {
657                 if (!strncmp(rif->ifname, ifname, IFNAMSIZ))
658                         return rif;
659         }
660
661         rif = calloc(1, sizeof(*rif));
662         if (!rif)
663                 return NULL;
664
665         INIT_LIST_HEAD(&rif->hosts);
666         strcpy(rif->ifname, ifname);
667         list_add(&rif->list, &interfaces);
668         rif->managed = managed;
669
670         return rif;
671 }
672
673 static void die(int signo)
674 {
675         /*
676          * When we hit SIGTERM, clean up interfaces directly, so that we
677          * won't leave our routing in an invalid state.
678          */
679         uloop_end();
680 }
681
682 static int usage(const char *progname)
683 {
684         fprintf(stderr, "Usage: %s <options>\n"
685                         "\n"
686                         "Options:\n"
687                         "       -d              Enable debug messages\n"
688                         "       -i <ifname>     Add an interface for relaying\n"
689                         "       -I <ifname>     Same as -i, except with ARP cache and host route management\n"
690                         "                       You need to specify at least two interfaces\n"
691                         "       -G <ip>         Set a gateway IP for clients\n"
692                         "       -R <gateway>:<net>/<mask>\n"
693                         "                       Add a static route for <net>/<mask> via <gateway>\n"
694                         "       -t <timeout>    Host entry expiry timeout\n"
695                         "       -p <tries>      Number of ARP ping attempts before considering a host dead\n"
696                         "       -T <table>      Set routing table number for automatically added routes\n"
697                         "       -B              Enable broadcast forwarding\n"
698                         "       -D              Enable DHCP forwarding\n"
699                         "       -P              Disable DHCP options parsing\n"
700                         "       -L <ipaddr>     Enable local access using <ipaddr> as source address\n"
701                         "\n",
702                 progname);
703         return -1;
704 }
705
706 int main(int argc, char **argv)
707 {
708         struct relayd_interface *rif = NULL;
709         struct in_addr addr, addr2;
710         bool local_addr_valid = false;
711         bool managed = false;
712         int ifnum = 0;
713         char *s, *s2;
714         int mask;
715         int ch;
716
717         debug = 0;
718         inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
719         if (inet_sock < 0) {
720                 perror("socket(AF_INET)");
721                 return 1;
722         }
723
724         host_timeout = 30;
725         host_ping_tries = 5;
726         forward_bcast = 0;
727         local_route_table = 0;
728         parse_dhcp = 1;
729         uloop_init();
730
731         while ((ch = getopt(argc, argv, "I:i:t:p:BDPdT:G:R:L:")) != -1) {
732                 switch(ch) {
733                 case 'I':
734                         managed = true;
735                         /* fall through */
736                 case 'i':
737                         ifnum++;
738                         rif = alloc_interface(optarg, managed);
739                         if (!rif)
740                                 return 1;
741
742                         managed = false;
743                         break;
744                 case 't':
745                         host_timeout = atoi(optarg);
746                         if (host_timeout <= 0)
747                                 return usage(argv[0]);
748                         break;
749                 case 'p':
750                         host_ping_tries = atoi(optarg);
751                         if (host_ping_tries <= 0)
752                                 return usage(argv[0]);
753                         break;
754                 case 'd':
755                         debug++;
756                         break;
757                 case 'B':
758                         forward_bcast = 1;
759                         break;
760                 case 'D':
761                         forward_dhcp = 1;
762                         break;
763                 case 'P':
764                         parse_dhcp = 0;
765                         break;
766                 case 'T':
767                         route_table = atoi(optarg);
768                         if (route_table <= 0)
769                                 return usage(argv[0]);
770                         break;
771                 case 'G':
772                         if (!inet_aton(optarg, &addr)) {
773                                 fprintf(stderr, "Address '%s' not found\n", optarg);
774                                 return 1;
775                         }
776                         relayd_add_pending_route((uint8_t *) &addr.s_addr, (const uint8_t *) "\x00\x00\x00\x00", 0, 0);
777                         break;
778                 case 'L':
779                         if (!inet_aton(optarg, &addr)) {
780                                 fprintf(stderr, "Address '%s' not found\n", optarg);
781                                 return 1;
782                         }
783                         memcpy(&local_addr, &addr.s_addr, sizeof(local_addr));
784                         local_addr_valid = true;
785                         break;
786                 case 'R':
787                         s = strchr(optarg, ':');
788                         if (!s)
789                                 return usage(argv[0]);
790
791                         *(s++) = 0;
792                         if (!inet_aton(optarg, &addr)) {
793                                 fprintf(stderr, "Address '%s' not found\n", optarg);
794                                 return 1;
795                         }
796
797                         s2 = strchr(s, '/');
798                         if (!s2)
799                                 return usage(argv[0]);
800
801                         *(s2++) = 0;
802                         if (!inet_aton(s, &addr2)) {
803                                 fprintf(stderr, "Address '%s' not found\n", s);
804                                 return 1;
805                         }
806
807                         mask = atoi(s2);
808                         if (mask < 0 || mask > 32)
809                                 return usage(argv[0]);
810
811                         relayd_add_pending_route((uint8_t *) &addr.s_addr, (uint8_t *) &addr2.s_addr, mask, 0);
812                         break;
813                 case '?':
814                 default:
815                         return usage(argv[0]);
816                 }
817         }
818
819         if (list_empty(&interfaces))
820                 return usage(argv[0]);
821
822         if (ifnum < 2) {
823                 fprintf(stderr, "ERROR: Need at least 2 interfaces for relaying\n");
824                 return -1;
825         }
826
827         argc -= optind;
828         argv += optind;
829
830         signal(SIGTERM, die);
831         signal(SIGHUP, die);
832         signal(SIGUSR1, die);
833         signal(SIGUSR2, die);
834
835         if (local_addr_valid)
836                 local_route_table = route_table++;
837
838         if (relayd_rtnl_init() < 0)
839                 return 1;
840
841         if (init_interfaces() < 0)
842                 return 1;
843
844         ping_static_routes();
845
846         uloop_run();
847         uloop_done();
848
849         cleanup_hosts();
850         free_interfaces();
851         relayd_rtnl_done();
852         close(inet_sock);
853
854         return 0;
855 }