X-Git-Url: http://git.archive.openwrt.org/?p=project%2Frelayd.git;a=blobdiff_plain;f=route.c;h=03046abf7b0984e0aa51080b8d3d06372d9c4cda;hp=3be2a97e1a389668a00ead2049f266b53573c42f;hb=4e8f1fa4ca2b176500362843a9e57ea5abd4b7a3;hpb=eac0b3af3c53564350065b807779e06f3a92580b diff --git a/route.c b/route.c index 3be2a97..03046ab 100644 --- a/route.c +++ b/route.c @@ -21,11 +21,15 @@ #include #include #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,40 +43,134 @@ static void rtnl_flush(void) close(fd); } -static void rtnl_route_set(struct relayd_host *host, bool add) +enum { + RULE_F_ADD = (1 << 0), + RULE_F_DEFGW_WORKAROUND = (1 << 1), +}; + +static int get_route_table(struct relayd_interface *rif) +{ + if (rif) + return rif->rt_table; + else + return local_route_table; +} + +static void +rtnl_rule_request(struct relayd_interface *rif, int flags) { static struct { struct nlmsghdr nl; struct rtmsg rt; struct { struct rtattr rta; - uint8_t ipaddr[4]; - } __packed dst; + int table; + } __packed table; struct { struct rtattr rta; - int ifindex; + char ifname[IFNAMSIZ + 1]; } __packed dev; } __packed req = { - .nl = { - .nlmsg_len = sizeof(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), + }, + }; + const char *ifname = "lo"; + int padding = sizeof(req.dev.ifname); + + if (rif) + ifname = rif->ifname; + + if (!(flags & RULE_F_DEFGW_WORKAROUND)) { + req.dev.rta.rta_type = FRA_IFNAME; + padding -= strlen(ifname) + 1; + strcpy(req.dev.ifname, ifname); + req.dev.rta.rta_len = sizeof(req.dev.rta) + strlen(ifname) + 1; + } else { + req.dev.rta.rta_type = FRA_PRIORITY; + req.dev.rta.rta_len = sizeof(req.dev.rta) + sizeof(uint32_t); + padding -= sizeof(uint32_t); + *((uint32_t *) &req.dev.ifname) = 1; + } + req.table.table = get_route_table(rif); + req.nl.nlmsg_len = sizeof(req) - padding; + + req.nl.nlmsg_flags = NLM_F_REQUEST; + if (flags & RULE_F_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(); +} + +struct rtnl_addr { + struct rtattr rta; + uint8_t ipaddr[4]; +} __packed; + +static struct rtnl_addr * +rtnl_add_addr(struct rtnl_addr *addr, int *len, int type, const uint8_t *ipaddr) +{ + addr->rta.rta_type = type; + memcpy(addr->ipaddr, ipaddr, 4); + *len += sizeof(*addr); + return addr + 1; +} + +static void +rtnl_route_request(struct relayd_interface *rif, struct relayd_host *host, + struct relayd_route *route, bool add) +{ + static struct { + struct nlmsghdr nl; + struct rtmsg rt; + struct { + struct rtattr rta; + int table; + } __packed table; + struct { + struct rtattr rta; + int ifindex; + } __packed dev; + struct rtnl_addr addr[3]; + } __packed req = { .rt = { .rtm_family = AF_INET, .rtm_dst_len = 32, .rtm_table = RT_TABLE_MAIN, }, - .dst.rta = { - .rta_type = RTA_DST, - .rta_len = sizeof(req.dst), + .table.rta = { + .rta_type = RTA_TABLE, + .rta_len = sizeof(req.table), }, .dev.rta = { .rta_type = RTA_OIF, .rta_len = sizeof(req.dev), }, + .addr[0].rta.rta_len = sizeof(struct rtnl_addr), + .addr[1].rta.rta_len = sizeof(struct rtnl_addr), + .addr[2].rta.rta_len = sizeof(struct rtnl_addr), }; + int pktlen = sizeof(req) - sizeof(req.addr); + struct rtnl_addr *addr = &req.addr[0]; + const char *ifname = "loopback"; - memcpy(req.dst.ipaddr, host->ipaddr, sizeof(req.dst.ipaddr)); req.dev.ifindex = host->rif->sll.sll_ifindex; + req.table.table = get_route_table(rif); req.nl.nlmsg_flags = NLM_F_REQUEST; if (add) { @@ -80,25 +178,73 @@ static void rtnl_route_set(struct relayd_host *host, bool add) req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; req.rt.rtm_protocol = RTPROT_BOOT; - req.rt.rtm_scope = RT_SCOPE_LINK; + if (route) { + req.rt.rtm_scope = RT_SCOPE_UNIVERSE; + } else { + req.rt.rtm_scope = RT_SCOPE_LINK; + } req.rt.rtm_type = RTN_UNICAST; } else { req.nl.nlmsg_type = RTM_DELROUTE; req.rt.rtm_scope = RT_SCOPE_NOWHERE; } - send(rtnl_sock.fd, &req, sizeof(req), 0); + if (rif) + ifname = rif->ifname; + + if (route) { + DPRINTF(2, "%s: add route to "IP_FMT"/%d via "IP_FMT" (%s)\n", ifname, + IP_BUF(route->dest), route->mask, IP_BUF(host->ipaddr), + host->rif->ifname); + + req.rt.rtm_dst_len = route->mask; + if (route->mask) + addr = rtnl_add_addr(addr, &pktlen, RTA_DST, route->dest); + addr = rtnl_add_addr(addr, &pktlen, RTA_GATEWAY, host->ipaddr); + } else { + DPRINTF(2, "%s: add host route to "IP_FMT" (%s)\n", ifname, + IP_BUF(host->ipaddr), host->rif->ifname); + addr = rtnl_add_addr(addr, &pktlen, RTA_DST, host->ipaddr); + req.rt.rtm_dst_len = 32; + } + + /* local route */ + if (!rif) + addr = rtnl_add_addr(addr, &pktlen, RTA_PREFSRC, local_addr); + + req.nl.nlmsg_len = pktlen; + if (route) + rtnl_rule_request(rif, RULE_F_DEFGW_WORKAROUND | RULE_F_ADD); + send(rtnl_sock.fd, &req, pktlen, 0); + if (route) + rtnl_rule_request(rif, RULE_F_DEFGW_WORKAROUND); rtnl_flush(); } -void relayd_add_route(struct relayd_host *host) +void +rtnl_route_set(struct relayd_host *host, struct relayd_route *route, bool add) { - rtnl_route_set(host, true); + struct relayd_interface *rif; + + list_for_each_entry(rif, &interfaces, list) { + if (rif == host->rif) + continue; + + rtnl_route_request(rif, host, route, add); + } + if (local_route_table) + rtnl_route_request(NULL, host, route, add); +} + +void relayd_add_interface_routes(struct relayd_interface *rif) +{ + rif->rt_table = route_table++; + rtnl_rule_request(rif, RULE_F_ADD); } -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, 0); } #ifndef NDA_RTA @@ -203,21 +349,28 @@ static void rtnl_cb(struct uloop_fd *fd, unsigned int events) } while (1); } -int relayd_rtnl_init(void) +static void rtnl_dump_request(int nlmsg_type) { - struct sockaddr_nl snl_local; static struct { struct nlmsghdr nlh; struct rtgenmsg g; } req = { .nlh = { .nlmsg_len = sizeof(req), - .nlmsg_type = RTM_GETNEIGH, .nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST, .nlmsg_pid = 0, }, .g.rtgen_family = AF_INET, }; + req.nlh.nlmsg_type = nlmsg_type; + req.nlh.nlmsg_seq = rtnl_seq; + send(rtnl_sock.fd, &req, sizeof(req), 0); + rtnl_seq++; +} + +int relayd_rtnl_init(void) +{ + struct sockaddr_nl snl_local; rtnl_sock.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (rtnl_sock.fd < 0) { @@ -238,14 +391,15 @@ int relayd_rtnl_init(void) rtnl_seq = time(NULL); rtnl_dump_seq = rtnl_seq; - req.nlh.nlmsg_seq = rtnl_seq; - send(rtnl_sock.fd, &req, sizeof(req), 0); + rtnl_dump_request(RTM_GETNEIGH); + rtnl_rule_request(NULL, RULE_F_ADD); return 0; } void relayd_rtnl_done(void) { + rtnl_rule_request(NULL, 0); uloop_fd_delete(&rtnl_sock); close(rtnl_sock.fd); }