From 05a40ea28dd925212c3a45e73c4080e2dcd4e13d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 11 Aug 2010 18:56:01 +0200 Subject: [PATCH] Use policy routing to limit the scope of the host routes to affected interfaces --- main.c | 37 ++++++++++++++++-------- relayd.h | 18 ++++++++++-- route.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 123 insertions(+), 19 deletions(-) diff --git a/main.c b/main.c index 81cc84d..975e1ff 100644 --- a/main.c +++ b/main.c @@ -535,6 +535,7 @@ static int init_interface(struct relayd_interface *rif) #endif uloop_fd_add(&rif->bcast_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER); + relayd_add_interface_routes(rif); return 0; } @@ -552,22 +553,26 @@ static int init_interfaces(void) return 0; } -static void del_interface(struct relayd_interface *rif) +static void cleanup_hosts(void) { - struct relayd_host *host, *htmp; + struct relayd_interface *rif; + struct relayd_host *host, *tmp; - list_for_each_entry_safe(host, htmp, &rif->hosts, list) { - del_host(host); + list_for_each_entry(rif, &interfaces, list) { + list_for_each_entry_safe(host, tmp, &rif->hosts, list) { + del_host(host); + } } - free(rif); } -static void cleanup_interfaces(void) +static void free_interfaces(void) { struct relayd_interface *rif, *rtmp; list_for_each_entry_safe(rif, rtmp, &interfaces, list) { - del_interface(rif); + relayd_del_interface_routes(rif); + list_del(&rif->list); + free(rif); } } @@ -597,7 +602,8 @@ static void die(int signo) * When we hit SIGTERM, clean up interfaces directly, so that we * won't leave our routing in an invalid state. */ - cleanup_interfaces(); + cleanup_hosts(); + free_interfaces(); exit(1); } @@ -611,6 +617,7 @@ static int usage(const char *progname) " -I Same as -i, except with ARP cache and host route management\n" " You need to specify at least two interfaces\n" " -t Host entry expiry timeout\n" + " -T Set routing table number for automatically added routes\n" " -B Enable broadcast forwarding\n" " -D Enable DHCP forwarding\n" "\n", @@ -635,7 +642,7 @@ int main(int argc, char **argv) forward_bcast = 0; uloop_init(); - while ((ch = getopt(argc, argv, "I:i:t:BDd")) != -1) { + while ((ch = getopt(argc, argv, "I:i:t:BDdT:")) != -1) { switch(ch) { case 'I': managed = true; @@ -661,6 +668,11 @@ int main(int argc, char **argv) case 'D': forward_dhcp = 1; break; + case 'T': + route_table = atoi(optarg); + if (route_table <= 0) + return usage(argv[0]); + break; case '?': default: return usage(argv[0]); @@ -683,16 +695,17 @@ int main(int argc, char **argv) signal(SIGUSR1, die); signal(SIGUSR2, die); - if (init_interfaces() < 0) + if (relayd_rtnl_init() < 0) return 1; - if (relayd_rtnl_init() < 0) + if (init_interfaces() < 0) return 1; uloop_run(); uloop_done(); - cleanup_interfaces(); + cleanup_hosts(); + free_interfaces(); relayd_rtnl_done(); close(inet_sock); diff --git a/relayd.h b/relayd.h index fd23833..c8b023c 100644 --- a/relayd.h +++ b/relayd.h @@ -68,6 +68,7 @@ struct relayd_interface { struct list_head hosts; uint8_t src_ip[4]; bool managed; + int rt_table; }; struct relayd_host { @@ -106,9 +107,22 @@ struct rtnl_req { extern struct list_head interfaces; extern int debug; +extern int route_table; -void relayd_add_route(struct relayd_host *host); -void relayd_del_route(struct relayd_host *host); +void rtnl_route_set(struct relayd_host *host, bool add); + +static inline void relayd_add_route(struct relayd_host *host) +{ + rtnl_route_set(host, true); +} + +static inline void relayd_del_route(struct relayd_host *host) +{ + rtnl_route_set(host, false); +} + +void relayd_add_interface_routes(struct relayd_interface *rif); +void relayd_del_interface_routes(struct relayd_interface *rif); int relayd_rtnl_init(void); void relayd_rtnl_done(void); diff --git a/route.c b/route.c index 3be2a97..c41100d 100644 --- a/route.c +++ b/route.c @@ -22,10 +22,13 @@ #include #include +#include + #include "relayd.h" static struct uloop_fd rtnl_sock; static unsigned int rtnl_seq, rtnl_dump_seq; +int route_table = 16800; static void rtnl_flush(void) { @@ -39,7 +42,7 @@ static void rtnl_flush(void) close(fd); } -static void rtnl_route_set(struct relayd_host *host, bool add) +static void rtnl_route_request(struct relayd_interface *rif, struct relayd_host *host, bool add) { static struct { struct nlmsghdr nl; @@ -48,6 +51,10 @@ static void rtnl_route_set(struct relayd_host *host, bool add) struct rtattr rta; uint8_t ipaddr[4]; } __packed dst; + struct { + struct rtattr rta; + int table; + } __packed table; struct { struct rtattr rta; int ifindex; @@ -61,6 +68,10 @@ static void rtnl_route_set(struct relayd_host *host, bool add) .rtm_dst_len = 32, .rtm_table = RT_TABLE_MAIN, }, + .table.rta = { + .rta_type = RTA_TABLE, + .rta_len = sizeof(req.table), + }, .dst.rta = { .rta_type = RTA_DST, .rta_len = sizeof(req.dst), @@ -73,6 +84,7 @@ static void rtnl_route_set(struct relayd_host *host, bool add) memcpy(req.dst.ipaddr, host->ipaddr, sizeof(req.dst.ipaddr)); req.dev.ifindex = host->rif->sll.sll_ifindex; + req.table.table = rif->rt_table; req.nl.nlmsg_flags = NLM_F_REQUEST; if (add) { @@ -91,14 +103,79 @@ static void rtnl_route_set(struct relayd_host *host, bool add) rtnl_flush(); } -void relayd_add_route(struct relayd_host *host) +static void rtnl_rule_request(struct relayd_interface *rif, bool add) +{ + static struct { + struct nlmsghdr nl; + struct rtmsg rt; + struct { + struct rtattr rta; + int table; + } __packed table; + struct { + struct rtattr rta; + char ifname[IFNAMSIZ + 1]; + } __packed dev; + } __packed req = { + .rt = { + .rtm_family = AF_INET, + .rtm_table = RT_TABLE_UNSPEC, + .rtm_scope = RT_SCOPE_UNIVERSE, + .rtm_protocol = RTPROT_BOOT, + }, + .table.rta = { + .rta_type = FRA_TABLE, + .rta_len = sizeof(req.table), + }, + .dev.rta = { + .rta_type = FRA_IFNAME, + }, + }; + + int padding = sizeof(req.dev.ifname); + padding -= strlen(rif->ifname) + 1; + + strcpy(req.dev.ifname, rif->ifname); + req.dev.rta.rta_len = sizeof(req.dev.rta) + strlen(rif->ifname) + 1; + req.table.table = rif->rt_table; + req.nl.nlmsg_len = sizeof(req) - padding; + + req.nl.nlmsg_flags = NLM_F_REQUEST; + if (add) { + req.nl.nlmsg_type = RTM_NEWRULE; + req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; + + req.rt.rtm_type = RTN_UNICAST; + } else { + req.nl.nlmsg_type = RTM_DELRULE; + req.rt.rtm_type = RTN_UNSPEC; + } + + send(rtnl_sock.fd, &req, req.nl.nlmsg_len, 0); + rtnl_flush(); +} + +void rtnl_route_set(struct relayd_host *host, bool add) +{ + struct relayd_interface *rif; + + list_for_each_entry(rif, &interfaces, list) { + if (rif == host->rif) + continue; + + rtnl_route_request(rif, host, add); + } +} + +void relayd_add_interface_routes(struct relayd_interface *rif) { - rtnl_route_set(host, true); + rif->rt_table = route_table++; + rtnl_rule_request(rif, true); } -void relayd_del_route(struct relayd_host *host) +void relayd_del_interface_routes(struct relayd_interface *rif) { - rtnl_route_set(host, false); + rtnl_rule_request(rif, false); } #ifndef NDA_RTA -- 2.25.1