1*e2a2cc33Srenato /* $OpenBSD: kroute.c,v 1.8 2016/02/21 18:36:11 renato Exp $ */ 243509a12Srenato 343509a12Srenato /* 443509a12Srenato * Copyright (c) 2015 Renato Westphal <renato@openbsd.org> 543509a12Srenato * Copyright (c) 2004 Esben Norby <norby@openbsd.org> 643509a12Srenato * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 743509a12Srenato * 843509a12Srenato * Permission to use, copy, modify, and distribute this software for any 943509a12Srenato * purpose with or without fee is hereby granted, provided that the above 1043509a12Srenato * copyright notice and this permission notice appear in all copies. 1143509a12Srenato * 1243509a12Srenato * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1343509a12Srenato * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1443509a12Srenato * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1543509a12Srenato * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1643509a12Srenato * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1743509a12Srenato * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1843509a12Srenato * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1943509a12Srenato */ 2043509a12Srenato 2143509a12Srenato #include <sys/types.h> 2243509a12Srenato #include <sys/socket.h> 2343509a12Srenato #include <sys/sysctl.h> 2443509a12Srenato #include <sys/tree.h> 2543509a12Srenato #include <sys/uio.h> 2643509a12Srenato #include <netinet/in.h> 2743509a12Srenato #include <arpa/inet.h> 2843509a12Srenato #include <net/if.h> 2943509a12Srenato #include <net/if_dl.h> 3043509a12Srenato #include <net/if_types.h> 3143509a12Srenato #include <net/route.h> 3243509a12Srenato #include <err.h> 3343509a12Srenato #include <errno.h> 3443509a12Srenato #include <fcntl.h> 3543509a12Srenato #include <stdio.h> 3643509a12Srenato #include <stdlib.h> 3743509a12Srenato #include <string.h> 3843509a12Srenato #include <unistd.h> 3943509a12Srenato #include <limits.h> 4043509a12Srenato 4143509a12Srenato #include "eigrpd.h" 4243509a12Srenato #include "log.h" 4343509a12Srenato 4443509a12Srenato extern struct eigrpd_conf *eigrpd_conf; 4543509a12Srenato 4643509a12Srenato struct { 4743509a12Srenato uint32_t rtseq; 4843509a12Srenato pid_t pid; 4943509a12Srenato int fib_sync; 5043509a12Srenato int fd; 5143509a12Srenato struct event ev; 5243509a12Srenato unsigned int rdomain; 5343509a12Srenato } kr_state; 5443509a12Srenato 5543509a12Srenato struct kroute_node { 5643509a12Srenato TAILQ_ENTRY(kroute_node) entry; 5743509a12Srenato struct kroute_priority *kprio; /* back pointer */ 5843509a12Srenato struct kroute r; 5943509a12Srenato }; 6043509a12Srenato 6143509a12Srenato struct kroute_priority { 6243509a12Srenato TAILQ_ENTRY(kroute_priority) entry; 6343509a12Srenato struct kroute_prefix *kp; /* back pointer */ 6443509a12Srenato uint8_t priority; 6543509a12Srenato TAILQ_HEAD(, kroute_node) nexthops; 6643509a12Srenato }; 6743509a12Srenato 6843509a12Srenato struct kroute_prefix { 6943509a12Srenato RB_ENTRY(kroute_prefix) entry; 7043509a12Srenato int af; 7143509a12Srenato union eigrpd_addr prefix; 7243509a12Srenato uint8_t prefixlen; 7343509a12Srenato TAILQ_HEAD(plist, kroute_priority) priorities; 7443509a12Srenato }; 7543509a12Srenato 7643509a12Srenato struct kif_addr { 7743509a12Srenato TAILQ_ENTRY(kif_addr) entry; 7843509a12Srenato struct kaddr a; 7943509a12Srenato }; 8043509a12Srenato 8143509a12Srenato struct kif_node { 8243509a12Srenato RB_ENTRY(kif_node) entry; 8343509a12Srenato TAILQ_HEAD(, kif_addr) addrs; 8443509a12Srenato struct kif k; 8543509a12Srenato }; 8643509a12Srenato 8743509a12Srenato void kr_redist_remove(struct kroute *); 8843509a12Srenato int kr_redist_eval(struct kroute *); 8943509a12Srenato void kr_redistribute(struct kroute_prefix *); 9043509a12Srenato int kroute_compare(struct kroute_prefix *, 9143509a12Srenato struct kroute_prefix *); 9243509a12Srenato struct kroute_prefix *kroute_find_prefix(int, union eigrpd_addr *, uint8_t); 9343509a12Srenato struct kroute_priority *kroute_find_prio(struct kroute_prefix *, uint8_t); 9443509a12Srenato struct kroute_node *kroute_find_gw(struct kroute_priority *, 9543509a12Srenato union eigrpd_addr *); 9643509a12Srenato struct kroute_node *kroute_insert(struct kroute *); 9743509a12Srenato int kroute_remove(struct kroute *); 9843509a12Srenato void kroute_clear(void); 9943509a12Srenato 10043509a12Srenato int kif_compare(struct kif_node *, struct kif_node *); 10143509a12Srenato struct kif_node *kif_find(unsigned short); 10243509a12Srenato struct kif_node *kif_insert(unsigned short); 10343509a12Srenato int kif_remove(struct kif_node *); 10443509a12Srenato struct kif *kif_update(unsigned short, int, struct if_data *, 10543509a12Srenato struct sockaddr_dl *); 10643509a12Srenato int kif_validate(unsigned short); 10743509a12Srenato 10843509a12Srenato void protect_lo(void); 10943509a12Srenato uint8_t prefixlen_classful(in_addr_t); 11043509a12Srenato void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 11143509a12Srenato void if_change(unsigned short, int, struct if_data *, 11243509a12Srenato struct sockaddr_dl *); 11343509a12Srenato void if_newaddr(unsigned short, struct sockaddr *, struct sockaddr *, 11443509a12Srenato struct sockaddr *); 11543509a12Srenato void if_deladdr(unsigned short, struct sockaddr *, struct sockaddr *, 11643509a12Srenato struct sockaddr *); 11743509a12Srenato void if_announce(void *); 11843509a12Srenato 11943509a12Srenato int send_rtmsg(int, int, struct kroute *); 12043509a12Srenato int dispatch_rtmsg(void); 12143509a12Srenato int fetchtable(void); 12243509a12Srenato int fetchifs(void); 12343509a12Srenato int rtmsg_process(char *, size_t); 12443509a12Srenato int rtmsg_process_route(struct rt_msghdr *, 12543509a12Srenato struct sockaddr *[RTAX_MAX]); 12643509a12Srenato 12743509a12Srenato RB_HEAD(kroute_tree, kroute_prefix) krt; 12843509a12Srenato RB_PROTOTYPE(kroute_tree, kroute_prefix, entry, kroute_compare) 12943509a12Srenato RB_GENERATE(kroute_tree, kroute_prefix, entry, kroute_compare) 13043509a12Srenato 13143509a12Srenato RB_HEAD(kif_tree, kif_node) kit; 13243509a12Srenato RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare) 13343509a12Srenato RB_GENERATE(kif_tree, kif_node, entry, kif_compare) 13443509a12Srenato 13543509a12Srenato int 13643509a12Srenato kif_init(void) 13743509a12Srenato { 13843509a12Srenato RB_INIT(&kit); 13943509a12Srenato kr_state.fib_sync = 0; /* decoupled */ 14043509a12Srenato 14143509a12Srenato if (fetchifs() == -1) 14243509a12Srenato return (-1); 14343509a12Srenato 14443509a12Srenato return (0); 14543509a12Srenato } 14643509a12Srenato 14743509a12Srenato int 14843509a12Srenato kr_init(int fs, unsigned int rdomain) 14943509a12Srenato { 15043509a12Srenato int opt = 0, rcvbuf, default_rcvbuf; 15143509a12Srenato socklen_t optlen; 15243509a12Srenato 15343509a12Srenato kr_state.fib_sync = fs; 15443509a12Srenato kr_state.rdomain = rdomain; 15543509a12Srenato 15643509a12Srenato if ((kr_state.fd = socket(AF_ROUTE, 15743509a12Srenato SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1) { 15843509a12Srenato log_warn("%s: socket", __func__); 15943509a12Srenato return (-1); 16043509a12Srenato } 16143509a12Srenato 16243509a12Srenato /* not interested in my own messages */ 16343509a12Srenato if (setsockopt(kr_state.fd, SOL_SOCKET, SO_USELOOPBACK, 16443509a12Srenato &opt, sizeof(opt)) == -1) 16543509a12Srenato log_warn("%s: setsockopt", __func__); /* not fatal */ 16643509a12Srenato 16743509a12Srenato /* grow receive buffer, don't wanna miss messages */ 16843509a12Srenato optlen = sizeof(default_rcvbuf); 16943509a12Srenato if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, 17043509a12Srenato &default_rcvbuf, &optlen) == -1) 17143509a12Srenato log_warn("%s: getsockopt SOL_SOCKET SO_RCVBUF", __func__); 17243509a12Srenato else 17343509a12Srenato for (rcvbuf = MAX_RTSOCK_BUF; 17443509a12Srenato rcvbuf > default_rcvbuf && 17543509a12Srenato setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, 17643509a12Srenato &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS; 17743509a12Srenato rcvbuf /= 2) 17843509a12Srenato ; /* nothing */ 17943509a12Srenato 18043509a12Srenato kr_state.pid = getpid(); 18143509a12Srenato kr_state.rtseq = 1; 18243509a12Srenato 18343509a12Srenato if (fetchtable() == -1) 18443509a12Srenato return (-1); 18543509a12Srenato 18643509a12Srenato protect_lo(); 18743509a12Srenato 18843509a12Srenato event_set(&kr_state.ev, kr_state.fd, EV_READ | EV_PERSIST, 18943509a12Srenato kr_dispatch_msg, NULL); 19043509a12Srenato event_add(&kr_state.ev, NULL); 19143509a12Srenato 19243509a12Srenato return (0); 19343509a12Srenato } 19443509a12Srenato 19543509a12Srenato void 19643509a12Srenato kif_redistribute(void) 19743509a12Srenato { 19843509a12Srenato struct kif_node *kif; 19943509a12Srenato struct kif_addr *ka; 20043509a12Srenato 201d6040158Srenato RB_FOREACH(kif, kif_tree, &kit) { 202d6040158Srenato main_imsg_compose_eigrpe(IMSG_IFINFO, 0, &kif->k, 203d6040158Srenato sizeof(struct kif)); 204d6040158Srenato TAILQ_FOREACH(ka, &kif->addrs, entry) { 20543509a12Srenato main_imsg_compose_eigrpe(IMSG_NEWADDR, 0, &ka->a, 20643509a12Srenato sizeof(struct kaddr)); 20743509a12Srenato } 208d6040158Srenato } 209d6040158Srenato } 21043509a12Srenato 21143509a12Srenato int 21243509a12Srenato kr_change(struct kroute *kr) 21343509a12Srenato { 21443509a12Srenato struct kroute_prefix *kp; 21543509a12Srenato struct kroute_priority *kprio; 21643509a12Srenato struct kroute_node *kn; 21743509a12Srenato int action = RTM_ADD; 21843509a12Srenato 21943509a12Srenato kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen); 22043509a12Srenato if (kp == NULL) 22143509a12Srenato kn = kroute_insert(kr); 22243509a12Srenato else { 22343509a12Srenato kprio = kroute_find_prio(kp, kr->priority); 22443509a12Srenato if (kprio == NULL) 22543509a12Srenato kn = kroute_insert(kr); 22643509a12Srenato else { 22743509a12Srenato kn = kroute_find_gw(kprio, &kr->nexthop); 22843509a12Srenato if (kn == NULL) 22943509a12Srenato kn = kroute_insert(kr); 23043509a12Srenato else 23143509a12Srenato action = RTM_CHANGE; 23243509a12Srenato } 23343509a12Srenato } 23443509a12Srenato 23543509a12Srenato /* send update */ 23643509a12Srenato if (send_rtmsg(kr_state.fd, action, kr) == -1) 23743509a12Srenato return (-1); 23843509a12Srenato 23943509a12Srenato kn->r.flags |= F_EIGRPD_INSERTED; 24043509a12Srenato 24143509a12Srenato return (0); 24243509a12Srenato } 24343509a12Srenato 24443509a12Srenato int 24543509a12Srenato kr_delete(struct kroute *kr) 24643509a12Srenato { 24743509a12Srenato struct kroute_prefix *kp; 24843509a12Srenato struct kroute_priority *kprio; 24943509a12Srenato struct kroute_node *kn; 25043509a12Srenato 25143509a12Srenato kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen); 25243509a12Srenato if (kp == NULL) 25343509a12Srenato return (0); 25443509a12Srenato kprio = kroute_find_prio(kp, kr->priority); 25543509a12Srenato if (kprio == NULL) 25643509a12Srenato return (0); 25743509a12Srenato kn = kroute_find_gw(kprio, &kr->nexthop); 25843509a12Srenato if (kn == NULL) 25943509a12Srenato return (0); 26043509a12Srenato 26143509a12Srenato if (!(kn->r.flags & F_EIGRPD_INSERTED)) 26243509a12Srenato return (0); 26343509a12Srenato 26443509a12Srenato if (send_rtmsg(kr_state.fd, RTM_DELETE, &kn->r) == -1) 26543509a12Srenato return (-1); 26643509a12Srenato 26743509a12Srenato if (kroute_remove(kr) == -1) 26843509a12Srenato return (-1); 26943509a12Srenato 27043509a12Srenato return (0); 27143509a12Srenato } 27243509a12Srenato 27343509a12Srenato void 27443509a12Srenato kr_shutdown(void) 27543509a12Srenato { 27643509a12Srenato kr_fib_decouple(); 27743509a12Srenato kroute_clear(); 27843509a12Srenato kif_clear(); 27943509a12Srenato } 28043509a12Srenato 28143509a12Srenato void 28243509a12Srenato kr_fib_couple(void) 28343509a12Srenato { 28443509a12Srenato struct kroute_prefix *kp; 28543509a12Srenato struct kroute_priority *kprio; 28643509a12Srenato struct kroute_node *kn; 28743509a12Srenato 28843509a12Srenato if (kr_state.fib_sync == 1) /* already coupled */ 28943509a12Srenato return; 29043509a12Srenato 29143509a12Srenato kr_state.fib_sync = 1; 29243509a12Srenato 29343509a12Srenato RB_FOREACH(kp, kroute_tree, &krt) 29443509a12Srenato TAILQ_FOREACH(kprio, &kp->priorities, entry) 29543509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) { 29643509a12Srenato if (!(kn->r.flags & F_EIGRPD_INSERTED)) 29743509a12Srenato continue; 29843509a12Srenato send_rtmsg(kr_state.fd, RTM_ADD, &kn->r); 29943509a12Srenato } 30043509a12Srenato 30143509a12Srenato log_info("kernel routing table coupled"); 30243509a12Srenato } 30343509a12Srenato 30443509a12Srenato void 30543509a12Srenato kr_fib_decouple(void) 30643509a12Srenato { 30743509a12Srenato struct kroute_prefix *kp; 30843509a12Srenato struct kroute_priority *kprio; 30943509a12Srenato struct kroute_node *kn; 31043509a12Srenato 31143509a12Srenato if (kr_state.fib_sync == 0) /* already decoupled */ 31243509a12Srenato return; 31343509a12Srenato 31443509a12Srenato RB_FOREACH(kp, kroute_tree, &krt) 31543509a12Srenato TAILQ_FOREACH(kprio, &kp->priorities, entry) 31643509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) { 31743509a12Srenato if (!(kn->r.flags & F_EIGRPD_INSERTED)) 31843509a12Srenato continue; 31943509a12Srenato 32043509a12Srenato send_rtmsg(kr_state.fd, RTM_DELETE, &kn->r); 32143509a12Srenato } 32243509a12Srenato 32343509a12Srenato kr_state.fib_sync = 0; 32443509a12Srenato 32543509a12Srenato log_info("kernel routing table decoupled"); 32643509a12Srenato } 32743509a12Srenato 32843509a12Srenato /* ARGSUSED */ 32943509a12Srenato void 33043509a12Srenato kr_dispatch_msg(int fd, short event, void *bula) 33143509a12Srenato { 33243509a12Srenato if (dispatch_rtmsg() == -1) 33343509a12Srenato event_loopexit(NULL); 33443509a12Srenato } 33543509a12Srenato 33643509a12Srenato void 33743509a12Srenato kr_show_route(struct imsg *imsg) 33843509a12Srenato { 33943509a12Srenato struct kroute_prefix *kp; 34043509a12Srenato struct kroute_priority *kprio; 34143509a12Srenato struct kroute_node *kn; 34243509a12Srenato struct kroute kr; 34343509a12Srenato int flags; 34443509a12Srenato 34543509a12Srenato if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags)) { 34643509a12Srenato log_warnx("%s: wrong imsg len", __func__); 34743509a12Srenato return; 34843509a12Srenato } 34943509a12Srenato memcpy(&flags, imsg->data, sizeof(flags)); 35043509a12Srenato RB_FOREACH(kp, kroute_tree, &krt) 35143509a12Srenato TAILQ_FOREACH(kprio, &kp->priorities, entry) 35243509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) { 35343509a12Srenato if (flags && !(kn->r.flags & flags)) 35443509a12Srenato continue; 35543509a12Srenato 35643509a12Srenato memcpy(&kr, &kn->r, sizeof(kr)); 35743509a12Srenato if (kr.priority == 35843509a12Srenato eigrpd_conf->fib_priority_external) 35943509a12Srenato kr.flags |= F_CTL_EXTERNAL; 36043509a12Srenato main_imsg_compose_eigrpe(IMSG_CTL_KROUTE, 36143509a12Srenato imsg->hdr.pid, &kr, sizeof(kr)); 36243509a12Srenato } 36343509a12Srenato 36443509a12Srenato main_imsg_compose_eigrpe(IMSG_CTL_END, imsg->hdr.pid, NULL, 0); 36543509a12Srenato } 36643509a12Srenato 36743509a12Srenato void 36843509a12Srenato kr_ifinfo(char *ifname, pid_t pid) 36943509a12Srenato { 37043509a12Srenato struct kif_node *kif; 37143509a12Srenato 37243509a12Srenato RB_FOREACH(kif, kif_tree, &kit) 37343509a12Srenato if (ifname == NULL || !strcmp(ifname, kif->k.ifname)) { 37443509a12Srenato main_imsg_compose_eigrpe(IMSG_CTL_IFINFO, 37543509a12Srenato pid, &kif->k, sizeof(kif->k)); 37643509a12Srenato } 37743509a12Srenato 37843509a12Srenato main_imsg_compose_eigrpe(IMSG_CTL_END, pid, NULL, 0); 37943509a12Srenato } 38043509a12Srenato 38143509a12Srenato void 38243509a12Srenato kr_redist_remove(struct kroute *kr) 38343509a12Srenato { 38443509a12Srenato /* was the route redistributed? */ 38543509a12Srenato if (!(kr->flags & F_REDISTRIBUTED)) 38643509a12Srenato return; 38743509a12Srenato 38843509a12Srenato /* remove redistributed flag */ 38943509a12Srenato kr->flags &= ~F_REDISTRIBUTED; 39043509a12Srenato main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, kr, sizeof(*kr)); 39143509a12Srenato } 39243509a12Srenato 39343509a12Srenato int 39443509a12Srenato kr_redist_eval(struct kroute *kr) 39543509a12Srenato { 39643509a12Srenato in_addr_t a; 39743509a12Srenato 39843509a12Srenato /* Only non-eigrpd routes are considered for redistribution. */ 39943509a12Srenato if (!(kr->flags & F_KERNEL)) 40043509a12Srenato goto dont_redistribute; 40143509a12Srenato 40243509a12Srenato /* Dynamic routes are not redistributable. */ 40343509a12Srenato if (kr->flags & F_DYNAMIC) 40443509a12Srenato goto dont_redistribute; 40543509a12Srenato 40643509a12Srenato /* interface is not up and running so don't announce */ 40743509a12Srenato if (kr->flags & F_DOWN) 40843509a12Srenato goto dont_redistribute; 40943509a12Srenato 41043509a12Srenato /* filter-out non redistributable addresses */ 41143509a12Srenato switch (kr->af) { 41243509a12Srenato case AF_INET: 41343509a12Srenato a = ntohl(kr->prefix.v4.s_addr); 41443509a12Srenato if (IN_MULTICAST(a) || IN_BADCLASS(a) || 41543509a12Srenato (a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) 41643509a12Srenato goto dont_redistribute; 41743509a12Srenato 41843509a12Srenato if (kr->nexthop.v4.s_addr == htonl(INADDR_LOOPBACK) && 41943509a12Srenato !(kr->flags & (F_BLACKHOLE|F_REJECT))) 42043509a12Srenato goto dont_redistribute; 42143509a12Srenato break; 42243509a12Srenato case AF_INET6: 42343509a12Srenato if (IN6_IS_ADDR_LOOPBACK(&kr->prefix.v6) || 42443509a12Srenato IN6_IS_ADDR_MULTICAST(&kr->prefix.v6) || 42543509a12Srenato IN6_IS_ADDR_LINKLOCAL(&kr->prefix.v6) || 42643509a12Srenato IN6_IS_ADDR_SITELOCAL(&kr->prefix.v6) || 42743509a12Srenato IN6_IS_ADDR_V4MAPPED(&kr->prefix.v6) || 42843509a12Srenato IN6_IS_ADDR_V4COMPAT(&kr->prefix.v6)) 42943509a12Srenato goto dont_redistribute; 43043509a12Srenato 43143509a12Srenato if (IN6_IS_ADDR_LOOPBACK(&kr->nexthop.v6) && 43243509a12Srenato !(kr->flags & (F_BLACKHOLE|F_REJECT))) 43343509a12Srenato goto dont_redistribute; 43443509a12Srenato break; 43543509a12Srenato default: 43643509a12Srenato log_debug("%s: unexpected address-family", __func__); 43743509a12Srenato break; 43843509a12Srenato } 43943509a12Srenato 44043509a12Srenato /* prefix should be redistributed */ 44143509a12Srenato kr->flags |= F_REDISTRIBUTED; 44243509a12Srenato main_imsg_compose_rde(IMSG_NETWORK_ADD, 0, kr, sizeof(*kr)); 44343509a12Srenato return (1); 44443509a12Srenato 44543509a12Srenato dont_redistribute: 44643509a12Srenato kr_redist_remove(kr); 44743509a12Srenato return (0); 44843509a12Srenato } 44943509a12Srenato 45043509a12Srenato void 45143509a12Srenato kr_redistribute(struct kroute_prefix *kp) 45243509a12Srenato { 45343509a12Srenato struct kroute_priority *kprio; 45443509a12Srenato struct kroute_node *kn; 45543509a12Srenato 45643509a12Srenato /* only the highest prio route can be redistributed */ 45743509a12Srenato TAILQ_FOREACH_REVERSE(kprio, &kp->priorities, plist, entry) { 45843509a12Srenato if (kprio == TAILQ_FIRST(&kp->priorities)) { 45943509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) 46043509a12Srenato /* pick just one entry in case of multipath */ 46143509a12Srenato if (kr_redist_eval(&kn->r)) 46243509a12Srenato break; 46343509a12Srenato } else { 46443509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) 46543509a12Srenato kr_redist_remove(&kn->r); 46643509a12Srenato } 46743509a12Srenato } 46843509a12Srenato } 46943509a12Srenato 47043509a12Srenato int 47143509a12Srenato kroute_compare(struct kroute_prefix *a, struct kroute_prefix *b) 47243509a12Srenato { 47343509a12Srenato if (a->af < b->af) 47443509a12Srenato return (-1); 47543509a12Srenato if (a->af > b->af) 47643509a12Srenato return (1); 47743509a12Srenato 47843509a12Srenato switch (a->af) { 47943509a12Srenato case AF_INET: 48043509a12Srenato if (ntohl(a->prefix.v4.s_addr) < 48143509a12Srenato ntohl(b->prefix.v4.s_addr)) 48243509a12Srenato return (-1); 48343509a12Srenato if (ntohl(a->prefix.v4.s_addr) > 48443509a12Srenato ntohl(b->prefix.v4.s_addr)) 48543509a12Srenato return (1); 48643509a12Srenato break; 48743509a12Srenato case AF_INET6: 48843509a12Srenato if (memcmp(a->prefix.v6.s6_addr, 48943509a12Srenato b->prefix.v6.s6_addr, 16) < 0) 49043509a12Srenato return (-1); 49143509a12Srenato if (memcmp(a->prefix.v6.s6_addr, 49243509a12Srenato b->prefix.v6.s6_addr, 16) > 0) 49343509a12Srenato return (1); 49443509a12Srenato break; 49543509a12Srenato default: 49643509a12Srenato log_debug("%s: unexpected address-family", __func__); 49743509a12Srenato break; 49843509a12Srenato } 49943509a12Srenato 50043509a12Srenato if (a->prefixlen < b->prefixlen) 50143509a12Srenato return (-1); 50243509a12Srenato if (a->prefixlen > b->prefixlen) 50343509a12Srenato return (1); 50443509a12Srenato 50543509a12Srenato return (0); 50643509a12Srenato } 50743509a12Srenato 50843509a12Srenato /* tree management */ 50943509a12Srenato struct kroute_prefix * 51043509a12Srenato kroute_find_prefix(int af, union eigrpd_addr *prefix, uint8_t prefixlen) 51143509a12Srenato { 51243509a12Srenato struct kroute_prefix s; 51343509a12Srenato 51443509a12Srenato s.af = af; 51543509a12Srenato memcpy(&s.prefix, prefix, sizeof(s.prefix)); 51643509a12Srenato s.prefixlen = prefixlen; 51743509a12Srenato 51843509a12Srenato return (RB_FIND(kroute_tree, &krt, &s)); 51943509a12Srenato } 52043509a12Srenato 52143509a12Srenato struct kroute_priority * 52243509a12Srenato kroute_find_prio(struct kroute_prefix *kp, uint8_t prio) 52343509a12Srenato { 52443509a12Srenato struct kroute_priority *kprio; 52543509a12Srenato 52643509a12Srenato /* RTP_ANY here picks the lowest priority node */ 52743509a12Srenato if (prio == RTP_ANY) 52843509a12Srenato return (TAILQ_FIRST(&kp->priorities)); 52943509a12Srenato 53043509a12Srenato TAILQ_FOREACH(kprio, &kp->priorities, entry) 53143509a12Srenato if (kprio->priority == prio) 53243509a12Srenato return (kprio); 53343509a12Srenato 53443509a12Srenato return (NULL); 53543509a12Srenato } 53643509a12Srenato 53743509a12Srenato struct kroute_node * 53843509a12Srenato kroute_find_gw(struct kroute_priority *kprio, union eigrpd_addr *nh) 53943509a12Srenato { 54043509a12Srenato struct kroute_node *kn; 54143509a12Srenato 54243509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) 54343509a12Srenato if (eigrp_addrcmp(kprio->kp->af, &kn->r.nexthop, nh) == 0) 54443509a12Srenato return (kn); 54543509a12Srenato 54643509a12Srenato return (NULL); 54743509a12Srenato } 54843509a12Srenato 54943509a12Srenato struct kroute_node * 55043509a12Srenato kroute_insert(struct kroute *kr) 55143509a12Srenato { 55243509a12Srenato struct kroute_prefix *kp; 55337212791Srenato struct kroute_priority *kprio, *tmp; 55443509a12Srenato struct kroute_node *kn; 55543509a12Srenato 55643509a12Srenato kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen); 55743509a12Srenato if (kp == NULL) { 55843509a12Srenato kp = calloc(1, sizeof((*kp))); 55943509a12Srenato if (kp == NULL) 56043509a12Srenato fatal("kroute_insert"); 56143509a12Srenato kp->af = kr->af; 56243509a12Srenato memcpy(&kp->prefix, &kr->prefix, sizeof(kp->prefix)); 56343509a12Srenato kp->prefixlen = kr->prefixlen; 56443509a12Srenato TAILQ_INIT(&kp->priorities); 56543509a12Srenato RB_INSERT(kroute_tree, &krt, kp); 56643509a12Srenato } 56743509a12Srenato 56843509a12Srenato kprio = kroute_find_prio(kp, kr->priority); 56943509a12Srenato if (kprio == NULL) { 57043509a12Srenato kprio = calloc(1, sizeof(*kprio)); 57143509a12Srenato if (kprio == NULL) 57243509a12Srenato fatal("kroute_insert"); 57343509a12Srenato kprio->kp = kp; 57443509a12Srenato kprio->priority = kr->priority; 57543509a12Srenato TAILQ_INIT(&kprio->nexthops); 57643509a12Srenato 57743509a12Srenato /* lower priorities first */ 57843509a12Srenato TAILQ_FOREACH(tmp, &kp->priorities, entry) 57943509a12Srenato if (tmp->priority > kprio->priority) 58043509a12Srenato break; 58143509a12Srenato if (tmp) 58243509a12Srenato TAILQ_INSERT_BEFORE(tmp, kprio, entry); 58343509a12Srenato else 58443509a12Srenato TAILQ_INSERT_TAIL(&kp->priorities, kprio, entry); 58543509a12Srenato } 58643509a12Srenato 58743509a12Srenato kn = kroute_find_gw(kprio, &kr->nexthop); 58843509a12Srenato if (kn == NULL) { 58943509a12Srenato kn = calloc(1, sizeof(*kn)); 59043509a12Srenato if (kn == NULL) 59143509a12Srenato fatal("kroute_insert"); 59243509a12Srenato kn->kprio = kprio; 59343509a12Srenato memcpy(&kn->r, kr, sizeof(kn->r)); 59443509a12Srenato TAILQ_INSERT_TAIL(&kprio->nexthops, kn, entry); 59543509a12Srenato } 59643509a12Srenato 59743509a12Srenato if (!(kr->flags & F_KERNEL)) { 59843509a12Srenato /* don't validate or redistribute eigrp route */ 59943509a12Srenato kr->flags &= ~F_DOWN; 60043509a12Srenato return (kn); 60143509a12Srenato } 60243509a12Srenato 60343509a12Srenato if (kif_validate(kr->ifindex)) 60443509a12Srenato kr->flags &= ~F_DOWN; 60543509a12Srenato else 60643509a12Srenato kr->flags |= F_DOWN; 60743509a12Srenato 60843509a12Srenato kr_redistribute(kp); 60943509a12Srenato return (kn); 61043509a12Srenato } 61143509a12Srenato 61243509a12Srenato int 61343509a12Srenato kroute_remove(struct kroute *kr) 61443509a12Srenato { 61543509a12Srenato struct kroute_prefix *kp; 61643509a12Srenato struct kroute_priority *kprio; 61743509a12Srenato struct kroute_node *kn; 61843509a12Srenato 61943509a12Srenato kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen); 62043509a12Srenato if (kp == NULL) 62143509a12Srenato goto notfound; 62243509a12Srenato kprio = kroute_find_prio(kp, kr->priority); 62343509a12Srenato if (kprio == NULL) 62443509a12Srenato goto notfound; 62543509a12Srenato kn = kroute_find_gw(kprio, &kr->nexthop); 62643509a12Srenato if (kn == NULL) 62743509a12Srenato goto notfound; 62843509a12Srenato 62943509a12Srenato kr_redist_remove(&kn->r); 63043509a12Srenato 63143509a12Srenato TAILQ_REMOVE(&kprio->nexthops, kn, entry); 63243509a12Srenato free(kn); 63343509a12Srenato 63443509a12Srenato if (TAILQ_EMPTY(&kprio->nexthops)) { 63543509a12Srenato TAILQ_REMOVE(&kp->priorities, kprio, entry); 63643509a12Srenato free(kprio); 63743509a12Srenato } 63843509a12Srenato 63943509a12Srenato if (TAILQ_EMPTY(&kp->priorities)) { 64043509a12Srenato if (RB_REMOVE(kroute_tree, &krt, kp) == NULL) 64143509a12Srenato log_warnx("%s failed for %s/%u", __func__, 64243509a12Srenato log_addr(kr->af, &kr->prefix), kp->prefixlen); 64343509a12Srenato free(kp); 64443509a12Srenato } else 64543509a12Srenato kr_redistribute(kp); 64643509a12Srenato 64743509a12Srenato return (0); 64843509a12Srenato 64943509a12Srenato notfound: 65043509a12Srenato log_warnx("%s failed to find %s/%u", __func__, 65143509a12Srenato log_addr(kr->af, &kr->prefix), kr->prefixlen); 65243509a12Srenato return (-1); 65343509a12Srenato } 65443509a12Srenato 65543509a12Srenato void 65643509a12Srenato kroute_clear(void) 65743509a12Srenato { 65843509a12Srenato struct kroute_prefix *kp; 65943509a12Srenato struct kroute_priority *kprio; 66043509a12Srenato struct kroute_node *kn; 66143509a12Srenato 66243509a12Srenato while ((kp = RB_MIN(kroute_tree, &krt)) != NULL) { 66343509a12Srenato while ((kprio = TAILQ_FIRST(&kp->priorities)) != NULL) { 66443509a12Srenato while ((kn = TAILQ_FIRST(&kprio->nexthops)) != NULL) { 66543509a12Srenato TAILQ_REMOVE(&kprio->nexthops, kn, entry); 66643509a12Srenato free(kn); 66743509a12Srenato } 66843509a12Srenato TAILQ_REMOVE(&kp->priorities, kprio, entry); 66943509a12Srenato free(kprio); 67043509a12Srenato } 67143509a12Srenato RB_REMOVE(kroute_tree, &krt, kp); 67243509a12Srenato free(kp); 67343509a12Srenato } 67443509a12Srenato } 67543509a12Srenato 67643509a12Srenato int 67743509a12Srenato kif_compare(struct kif_node *a, struct kif_node *b) 67843509a12Srenato { 67943509a12Srenato return (b->k.ifindex - a->k.ifindex); 68043509a12Srenato } 68143509a12Srenato 68243509a12Srenato /* tree management */ 68343509a12Srenato struct kif_node * 68443509a12Srenato kif_find(unsigned short ifindex) 68543509a12Srenato { 68643509a12Srenato struct kif_node s; 68743509a12Srenato 68843509a12Srenato memset(&s, 0, sizeof(s)); 68943509a12Srenato s.k.ifindex = ifindex; 69043509a12Srenato 69143509a12Srenato return (RB_FIND(kif_tree, &kit, &s)); 69243509a12Srenato } 69343509a12Srenato 69443509a12Srenato struct kif * 69543509a12Srenato kif_findname(char *ifname) 69643509a12Srenato { 69743509a12Srenato struct kif_node *kif; 69843509a12Srenato 69943509a12Srenato RB_FOREACH(kif, kif_tree, &kit) 70043509a12Srenato if (!strcmp(ifname, kif->k.ifname)) 70143509a12Srenato return (&kif->k); 70243509a12Srenato 70343509a12Srenato return (NULL); 70443509a12Srenato } 70543509a12Srenato 70643509a12Srenato struct kif_node * 70743509a12Srenato kif_insert(unsigned short ifindex) 70843509a12Srenato { 70943509a12Srenato struct kif_node *kif; 71043509a12Srenato 71143509a12Srenato if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) 71243509a12Srenato return (NULL); 71343509a12Srenato 71443509a12Srenato kif->k.ifindex = ifindex; 71543509a12Srenato TAILQ_INIT(&kif->addrs); 71643509a12Srenato 71743509a12Srenato if (RB_INSERT(kif_tree, &kit, kif) != NULL) 71843509a12Srenato fatalx("kif_insert: RB_INSERT"); 71943509a12Srenato 72043509a12Srenato return (kif); 72143509a12Srenato } 72243509a12Srenato 72343509a12Srenato int 72443509a12Srenato kif_remove(struct kif_node *kif) 72543509a12Srenato { 72643509a12Srenato struct kif_addr *ka; 72743509a12Srenato 72843509a12Srenato if (RB_REMOVE(kif_tree, &kit, kif) == NULL) { 72943509a12Srenato log_warnx("%s failed for inteface %s", __func__, kif->k.ifname); 73043509a12Srenato return (-1); 73143509a12Srenato } 73243509a12Srenato 73343509a12Srenato while ((ka = TAILQ_FIRST(&kif->addrs)) != NULL) { 73443509a12Srenato TAILQ_REMOVE(&kif->addrs, ka, entry); 73543509a12Srenato free(ka); 73643509a12Srenato } 73743509a12Srenato free(kif); 73843509a12Srenato return (0); 73943509a12Srenato } 74043509a12Srenato 74143509a12Srenato void 74243509a12Srenato kif_clear(void) 74343509a12Srenato { 74443509a12Srenato struct kif_node *kif; 74543509a12Srenato 74643509a12Srenato while ((kif = RB_MIN(kif_tree, &kit)) != NULL) 74743509a12Srenato kif_remove(kif); 74843509a12Srenato } 74943509a12Srenato 75043509a12Srenato struct kif * 75143509a12Srenato kif_update(unsigned short ifindex, int flags, struct if_data *ifd, 75243509a12Srenato struct sockaddr_dl *sdl) 75343509a12Srenato { 75443509a12Srenato struct kif_node *kif; 75543509a12Srenato 75643509a12Srenato if ((kif = kif_find(ifindex)) == NULL) { 75743509a12Srenato if ((kif = kif_insert(ifindex)) == NULL) 75843509a12Srenato return (NULL); 75943509a12Srenato kif->k.nh_reachable = (flags & IFF_UP) && 76043509a12Srenato LINK_STATE_IS_UP(ifd->ifi_link_state); 76143509a12Srenato } 76243509a12Srenato 76343509a12Srenato kif->k.flags = flags; 76443509a12Srenato kif->k.link_state = ifd->ifi_link_state; 76543509a12Srenato kif->k.if_type = ifd->ifi_type; 76643509a12Srenato kif->k.baudrate = ifd->ifi_baudrate; 76743509a12Srenato kif->k.mtu = ifd->ifi_mtu; 76843509a12Srenato 76943509a12Srenato if (sdl && sdl->sdl_family == AF_LINK) { 77043509a12Srenato if (sdl->sdl_nlen >= sizeof(kif->k.ifname)) 77143509a12Srenato memcpy(kif->k.ifname, sdl->sdl_data, 77243509a12Srenato sizeof(kif->k.ifname) - 1); 77343509a12Srenato else if (sdl->sdl_nlen > 0) 77443509a12Srenato memcpy(kif->k.ifname, sdl->sdl_data, 77543509a12Srenato sdl->sdl_nlen); 77643509a12Srenato /* string already terminated via calloc() */ 77743509a12Srenato } 77843509a12Srenato 77943509a12Srenato return (&kif->k); 78043509a12Srenato } 78143509a12Srenato 78243509a12Srenato int 78343509a12Srenato kif_validate(unsigned short ifindex) 78443509a12Srenato { 78543509a12Srenato struct kif_node *kif; 78643509a12Srenato 78743509a12Srenato if ((kif = kif_find(ifindex)) == NULL) 78843509a12Srenato return (0); 78943509a12Srenato 79043509a12Srenato return (kif->k.nh_reachable); 79143509a12Srenato } 79243509a12Srenato 79343509a12Srenato /* misc */ 79443509a12Srenato void 79543509a12Srenato protect_lo(void) 79643509a12Srenato { 79743509a12Srenato struct kroute kr4, kr6; 79843509a12Srenato 79943509a12Srenato /* special protection for 127/8 */ 80043509a12Srenato memset(&kr4, 0, sizeof(kr4)); 80143509a12Srenato kr4.af = AF_INET; 80243509a12Srenato kr4.prefix.v4.s_addr = htonl(INADDR_LOOPBACK & IN_CLASSA_NET); 80343509a12Srenato kr4.prefixlen = 8; 80443509a12Srenato kr4.flags = F_KERNEL|F_CONNECTED; 80543509a12Srenato kroute_insert(&kr4); 80643509a12Srenato 80743509a12Srenato /* special protection for ::1 */ 80843509a12Srenato memset(&kr6, 0, sizeof(kr6)); 80943509a12Srenato kr6.af = AF_INET6; 81043509a12Srenato memcpy(&kr6.prefix.v6, &in6addr_loopback, sizeof(kr6.prefix.v6)); 81143509a12Srenato kr6.prefixlen = 128; 81243509a12Srenato kr6.flags = F_KERNEL|F_CONNECTED; 81343509a12Srenato kroute_insert(&kr6); 81443509a12Srenato } 81543509a12Srenato 81643509a12Srenato uint8_t 81743509a12Srenato prefixlen_classful(in_addr_t ina) 81843509a12Srenato { 81943509a12Srenato /* it hurt to write this. */ 82043509a12Srenato 82143509a12Srenato if (ina >= 0xf0000000U) /* class E */ 82243509a12Srenato return (32); 82343509a12Srenato else if (ina >= 0xe0000000U) /* class D */ 82443509a12Srenato return (4); 82543509a12Srenato else if (ina >= 0xc0000000U) /* class C */ 82643509a12Srenato return (24); 82743509a12Srenato else if (ina >= 0x80000000U) /* class B */ 82843509a12Srenato return (16); 82943509a12Srenato else /* class A */ 83043509a12Srenato return (8); 83143509a12Srenato } 83243509a12Srenato 83343509a12Srenato #define ROUNDUP(a) \ 83443509a12Srenato (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a)) 83543509a12Srenato 83643509a12Srenato void 83743509a12Srenato get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 83843509a12Srenato { 83943509a12Srenato int i; 84043509a12Srenato 84143509a12Srenato for (i = 0; i < RTAX_MAX; i++) { 84243509a12Srenato if (addrs & (1 << i)) { 84343509a12Srenato rti_info[i] = sa; 84443509a12Srenato sa = (struct sockaddr *)((char *)(sa) + 84543509a12Srenato ROUNDUP(sa->sa_len)); 84643509a12Srenato } else 84743509a12Srenato rti_info[i] = NULL; 84843509a12Srenato } 84943509a12Srenato } 85043509a12Srenato 85143509a12Srenato void 85243509a12Srenato if_change(unsigned short ifindex, int flags, struct if_data *ifd, 85343509a12Srenato struct sockaddr_dl *sdl) 85443509a12Srenato { 85543509a12Srenato struct kroute_prefix *kp; 85643509a12Srenato struct kroute_priority *kprio; 85743509a12Srenato struct kroute_node *kn; 85843509a12Srenato struct kif *kif; 85943509a12Srenato uint8_t reachable; 86043509a12Srenato 86143509a12Srenato if ((kif = kif_update(ifindex, flags, ifd, sdl)) == NULL) { 86243509a12Srenato log_warn("%s: kif_update(%u)", __func__, ifindex); 86343509a12Srenato return; 86443509a12Srenato } 86543509a12Srenato 86643509a12Srenato reachable = (kif->flags & IFF_UP) && 86743509a12Srenato LINK_STATE_IS_UP(kif->link_state); 86843509a12Srenato 86943509a12Srenato if (reachable == kif->nh_reachable) 87043509a12Srenato return; /* nothing changed wrt nexthop validity */ 87143509a12Srenato 87243509a12Srenato kif->nh_reachable = reachable; 87343509a12Srenato 87443509a12Srenato /* notify eigrpe about link state */ 87543509a12Srenato main_imsg_compose_eigrpe(IMSG_IFINFO, 0, kif, sizeof(struct kif)); 87643509a12Srenato 87743509a12Srenato /* notify rde about link going down */ 87843509a12Srenato if (!kif->nh_reachable) 87943509a12Srenato main_imsg_compose_rde(IMSG_IFDOWN, 0, kif, sizeof(struct kif)); 88043509a12Srenato 88143509a12Srenato /* update redistribute list */ 88243509a12Srenato RB_FOREACH(kp, kroute_tree, &krt) { 88343509a12Srenato TAILQ_FOREACH(kprio, &kp->priorities, entry) { 88443509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) { 88543509a12Srenato if (kn->r.ifindex != ifindex) 88643509a12Srenato continue; 88743509a12Srenato 88843509a12Srenato if (reachable) 88943509a12Srenato kn->r.flags &= ~F_DOWN; 89043509a12Srenato else 89143509a12Srenato kn->r.flags |= F_DOWN; 89243509a12Srenato } 89343509a12Srenato } 89443509a12Srenato kr_redistribute(kp); 89543509a12Srenato } 89643509a12Srenato } 89743509a12Srenato 89843509a12Srenato void 89943509a12Srenato if_newaddr(unsigned short ifindex, struct sockaddr *ifa, struct sockaddr *mask, 90043509a12Srenato struct sockaddr *brd) 90143509a12Srenato { 90243509a12Srenato struct kif_node *kif; 90343509a12Srenato struct sockaddr_in *ifa4, *mask4, *brd4; 90443509a12Srenato struct sockaddr_in6 *ifa6, *mask6, *brd6; 90543509a12Srenato struct kif_addr *ka; 90643509a12Srenato uint32_t a; 90743509a12Srenato 90843509a12Srenato if (ifa == NULL) 90943509a12Srenato return; 91043509a12Srenato if ((kif = kif_find(ifindex)) == NULL) { 91143509a12Srenato log_warnx("%s: corresponding if %d not found", __func__, 91243509a12Srenato ifindex); 91343509a12Srenato return; 91443509a12Srenato } 91543509a12Srenato 91643509a12Srenato switch (ifa->sa_family) { 91743509a12Srenato case AF_INET: 91843509a12Srenato ifa4 = (struct sockaddr_in *) ifa; 91943509a12Srenato mask4 = (struct sockaddr_in *) mask; 92043509a12Srenato brd4 = (struct sockaddr_in *) brd; 92143509a12Srenato 92243509a12Srenato /* filter out unwanted addresses */ 92343509a12Srenato a = ntohl(ifa4->sin_addr.s_addr); 92443509a12Srenato if (IN_MULTICAST(a) || IN_BADCLASS(a) || 92543509a12Srenato (a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) 92643509a12Srenato return; 92743509a12Srenato 92843509a12Srenato if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL) 92943509a12Srenato fatal("if_newaddr"); 93043509a12Srenato ka->a.addr.v4.s_addr = ifa4->sin_addr.s_addr; 93143509a12Srenato if (mask4) 93243509a12Srenato ka->a.prefixlen = 93343509a12Srenato mask2prefixlen(mask4->sin_addr.s_addr); 93443509a12Srenato if (brd4) 93543509a12Srenato ka->a.dstbrd.v4.s_addr = brd4->sin_addr.s_addr; 93643509a12Srenato break; 93743509a12Srenato case AF_INET6: 93843509a12Srenato ifa6 = (struct sockaddr_in6 *) ifa; 93943509a12Srenato mask6 = (struct sockaddr_in6 *) mask; 94043509a12Srenato brd6 = (struct sockaddr_in6 *) brd; 94143509a12Srenato 94243509a12Srenato /* We only care about link-local and global-scope. */ 94343509a12Srenato if (IN6_IS_ADDR_UNSPECIFIED(&ifa6->sin6_addr) || 94443509a12Srenato IN6_IS_ADDR_LOOPBACK(&ifa6->sin6_addr) || 94543509a12Srenato IN6_IS_ADDR_MULTICAST(&ifa6->sin6_addr) || 94643509a12Srenato IN6_IS_ADDR_SITELOCAL(&ifa6->sin6_addr) || 94743509a12Srenato IN6_IS_ADDR_V4MAPPED(&ifa6->sin6_addr) || 94843509a12Srenato IN6_IS_ADDR_V4COMPAT(&ifa6->sin6_addr)) 94943509a12Srenato return; 95043509a12Srenato 95143509a12Srenato clearscope(&ifa6->sin6_addr); 95243509a12Srenato 95343509a12Srenato if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL) 95443509a12Srenato fatal("if_newaddr"); 95543509a12Srenato ka->a.addr.v6 = ifa6->sin6_addr; 95643509a12Srenato if (mask6) 95743509a12Srenato ka->a.prefixlen = mask2prefixlen6(mask6); 95843509a12Srenato if (brd6) 95943509a12Srenato ka->a.dstbrd.v6 = brd6->sin6_addr; 96043509a12Srenato break; 96143509a12Srenato default: 96243509a12Srenato return; 96343509a12Srenato } 96443509a12Srenato 96543509a12Srenato ka->a.ifindex = ifindex; 96643509a12Srenato ka->a.af = ifa->sa_family; 96743509a12Srenato TAILQ_INSERT_TAIL(&kif->addrs, ka, entry); 96843509a12Srenato 96943509a12Srenato /* notify eigrpe about new address */ 97043509a12Srenato main_imsg_compose_eigrpe(IMSG_NEWADDR, 0, &ka->a, 97143509a12Srenato sizeof(struct kaddr)); 97243509a12Srenato } 97343509a12Srenato 97443509a12Srenato void 97543509a12Srenato if_deladdr(unsigned short ifindex, struct sockaddr *ifa, struct sockaddr *mask, 97643509a12Srenato struct sockaddr *brd) 97743509a12Srenato { 97843509a12Srenato struct kif_node *kif; 97943509a12Srenato struct sockaddr_in *ifa4, *mask4, *brd4; 98043509a12Srenato struct sockaddr_in6 *ifa6, *mask6, *brd6; 98143509a12Srenato struct kaddr k; 98243509a12Srenato struct kif_addr *ka, *nka; 98343509a12Srenato 98443509a12Srenato if (ifa == NULL) 98543509a12Srenato return; 98643509a12Srenato if ((kif = kif_find(ifindex)) == NULL) { 98743509a12Srenato log_warnx("%s: corresponding if %d not found", __func__, 98843509a12Srenato ifindex); 98943509a12Srenato return; 99043509a12Srenato } 99143509a12Srenato 99243509a12Srenato memset(&k, 0, sizeof(k)); 99343509a12Srenato k.af = ifa->sa_family; 99443509a12Srenato switch (ifa->sa_family) { 99543509a12Srenato case AF_INET: 99643509a12Srenato ifa4 = (struct sockaddr_in *) ifa; 99743509a12Srenato mask4 = (struct sockaddr_in *) mask; 99843509a12Srenato brd4 = (struct sockaddr_in *) brd; 99943509a12Srenato 100043509a12Srenato k.addr.v4.s_addr = ifa4->sin_addr.s_addr; 100143509a12Srenato if (mask4) 100243509a12Srenato k.prefixlen = mask2prefixlen(mask4->sin_addr.s_addr); 100343509a12Srenato if (brd4) 100443509a12Srenato k.dstbrd.v4.s_addr = brd4->sin_addr.s_addr; 100543509a12Srenato break; 100643509a12Srenato case AF_INET6: 100743509a12Srenato ifa6 = (struct sockaddr_in6 *) ifa; 100843509a12Srenato mask6 = (struct sockaddr_in6 *) mask; 100943509a12Srenato brd6 = (struct sockaddr_in6 *) brd; 101043509a12Srenato 101143509a12Srenato /* We only care about link-local and global-scope. */ 101243509a12Srenato if (IN6_IS_ADDR_UNSPECIFIED(&ifa6->sin6_addr) || 101343509a12Srenato IN6_IS_ADDR_LOOPBACK(&ifa6->sin6_addr) || 101443509a12Srenato IN6_IS_ADDR_MULTICAST(&ifa6->sin6_addr) || 101543509a12Srenato IN6_IS_ADDR_SITELOCAL(&ifa6->sin6_addr) || 101643509a12Srenato IN6_IS_ADDR_V4MAPPED(&ifa6->sin6_addr) || 101743509a12Srenato IN6_IS_ADDR_V4COMPAT(&ifa6->sin6_addr)) 101843509a12Srenato return; 101943509a12Srenato 102043509a12Srenato clearscope(&ifa6->sin6_addr); 102143509a12Srenato 102243509a12Srenato k.addr.v6 = ifa6->sin6_addr; 102343509a12Srenato if (mask6) 102443509a12Srenato k.prefixlen = mask2prefixlen6(mask6); 102543509a12Srenato if (brd6) 102643509a12Srenato k.dstbrd.v6 = brd6->sin6_addr; 102743509a12Srenato break; 102843509a12Srenato default: 102943509a12Srenato return; 103043509a12Srenato } 103143509a12Srenato 103243509a12Srenato for (ka = TAILQ_FIRST(&kif->addrs); ka != NULL; ka = nka) { 103343509a12Srenato nka = TAILQ_NEXT(ka, entry); 103443509a12Srenato 103543509a12Srenato if (ka->a.af != k.af || 103643509a12Srenato ka->a.prefixlen != k.prefixlen) 103743509a12Srenato continue; 103843509a12Srenato 103943509a12Srenato switch (ifa->sa_family) { 104043509a12Srenato case AF_INET: 104143509a12Srenato if (ka->a.addr.v4.s_addr != k.addr.v4.s_addr || 104243509a12Srenato ka->a.dstbrd.v4.s_addr != k.dstbrd.v4.s_addr) 104343509a12Srenato continue; 104443509a12Srenato break; 104543509a12Srenato case AF_INET6: 104643509a12Srenato if (!IN6_ARE_ADDR_EQUAL(&ka->a.addr.v6, &k.addr.v6) || 104743509a12Srenato !IN6_ARE_ADDR_EQUAL(&ka->a.dstbrd.v6, &k.dstbrd.v6)) 104843509a12Srenato continue; 104943509a12Srenato break; 105043509a12Srenato default: 105143509a12Srenato break; 105243509a12Srenato } 105343509a12Srenato 105443509a12Srenato /* notify eigrpe about removed address */ 105543509a12Srenato main_imsg_compose_eigrpe(IMSG_DELADDR, 0, &ka->a, 105643509a12Srenato sizeof(struct kaddr)); 105743509a12Srenato TAILQ_REMOVE(&kif->addrs, ka, entry); 105843509a12Srenato free(ka); 105943509a12Srenato return; 106043509a12Srenato } 106143509a12Srenato } 106243509a12Srenato 106343509a12Srenato void 106443509a12Srenato if_announce(void *msg) 106543509a12Srenato { 106643509a12Srenato struct if_announcemsghdr *ifan; 106743509a12Srenato struct kif_node *kif; 106843509a12Srenato 106943509a12Srenato ifan = msg; 107043509a12Srenato 107143509a12Srenato switch (ifan->ifan_what) { 107243509a12Srenato case IFAN_ARRIVAL: 107343509a12Srenato kif = kif_insert(ifan->ifan_index); 107443509a12Srenato if (kif) 107543509a12Srenato strlcpy(kif->k.ifname, ifan->ifan_name, 107643509a12Srenato sizeof(kif->k.ifname)); 107743509a12Srenato break; 107843509a12Srenato case IFAN_DEPARTURE: 107943509a12Srenato kif = kif_find(ifan->ifan_index); 108043509a12Srenato if (kif) 108143509a12Srenato kif_remove(kif); 108243509a12Srenato break; 108343509a12Srenato } 108443509a12Srenato } 108543509a12Srenato 108643509a12Srenato /* rtsock */ 108743509a12Srenato static int 108843509a12Srenato send_rtmsg_v4(int fd, int action, struct kroute *kr) 108943509a12Srenato { 109043509a12Srenato struct iovec iov[5]; 109143509a12Srenato struct rt_msghdr hdr; 109243509a12Srenato struct sockaddr_in prefix; 109343509a12Srenato struct sockaddr_in nexthop; 109443509a12Srenato struct sockaddr_in mask; 109543509a12Srenato int iovcnt = 0; 109643509a12Srenato 109743509a12Srenato if (kr_state.fib_sync == 0) 109843509a12Srenato return (0); 109943509a12Srenato 110043509a12Srenato /* initialize header */ 110143509a12Srenato memset(&hdr, 0, sizeof(hdr)); 110243509a12Srenato hdr.rtm_version = RTM_VERSION; 110343509a12Srenato hdr.rtm_type = action; 110443509a12Srenato hdr.rtm_priority = kr->priority; 110543509a12Srenato hdr.rtm_tableid = kr_state.rdomain; /* rtableid */ 110643509a12Srenato if (action == RTM_CHANGE) 110743509a12Srenato hdr.rtm_fmask = RTF_REJECT|RTF_BLACKHOLE; 110843509a12Srenato else 110943509a12Srenato hdr.rtm_flags = RTF_MPATH; 11107c245af9Srenato if (kr->flags & F_BLACKHOLE) 11117c245af9Srenato hdr.rtm_flags |= RTF_BLACKHOLE; 111243509a12Srenato hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ 111343509a12Srenato hdr.rtm_msglen = sizeof(hdr); 111443509a12Srenato /* adjust iovec */ 111543509a12Srenato iov[iovcnt].iov_base = &hdr; 111643509a12Srenato iov[iovcnt++].iov_len = sizeof(hdr); 111743509a12Srenato 111843509a12Srenato memset(&prefix, 0, sizeof(prefix)); 111943509a12Srenato prefix.sin_len = sizeof(prefix); 112043509a12Srenato prefix.sin_family = AF_INET; 112143509a12Srenato prefix.sin_addr.s_addr = kr->prefix.v4.s_addr; 112243509a12Srenato /* adjust header */ 112343509a12Srenato hdr.rtm_addrs |= RTA_DST; 112443509a12Srenato hdr.rtm_msglen += sizeof(prefix); 112543509a12Srenato /* adjust iovec */ 112643509a12Srenato iov[iovcnt].iov_base = &prefix; 112743509a12Srenato iov[iovcnt++].iov_len = sizeof(prefix); 112843509a12Srenato 112943509a12Srenato if (kr->nexthop.v4.s_addr != 0) { 113043509a12Srenato memset(&nexthop, 0, sizeof(nexthop)); 113143509a12Srenato nexthop.sin_len = sizeof(nexthop); 113243509a12Srenato nexthop.sin_family = AF_INET; 113343509a12Srenato nexthop.sin_addr.s_addr = kr->nexthop.v4.s_addr; 113443509a12Srenato /* adjust header */ 113543509a12Srenato hdr.rtm_flags |= RTF_GATEWAY; 113643509a12Srenato hdr.rtm_addrs |= RTA_GATEWAY; 113743509a12Srenato hdr.rtm_msglen += sizeof(nexthop); 113843509a12Srenato /* adjust iovec */ 113943509a12Srenato iov[iovcnt].iov_base = &nexthop; 114043509a12Srenato iov[iovcnt++].iov_len = sizeof(nexthop); 114143509a12Srenato } 114243509a12Srenato 114343509a12Srenato memset(&mask, 0, sizeof(mask)); 114443509a12Srenato mask.sin_len = sizeof(mask); 114543509a12Srenato mask.sin_family = AF_INET; 114643509a12Srenato mask.sin_addr.s_addr = prefixlen2mask(kr->prefixlen); 114743509a12Srenato /* adjust header */ 114843509a12Srenato hdr.rtm_addrs |= RTA_NETMASK; 114943509a12Srenato hdr.rtm_msglen += sizeof(mask); 115043509a12Srenato /* adjust iovec */ 115143509a12Srenato iov[iovcnt].iov_base = &mask; 115243509a12Srenato iov[iovcnt++].iov_len = sizeof(mask); 115343509a12Srenato 115443509a12Srenato retry: 115543509a12Srenato if (writev(fd, iov, iovcnt) == -1) { 115643509a12Srenato if (errno == ESRCH) { 115743509a12Srenato if (hdr.rtm_type == RTM_CHANGE) { 115843509a12Srenato hdr.rtm_type = RTM_ADD; 115943509a12Srenato goto retry; 116043509a12Srenato } else if (hdr.rtm_type == RTM_DELETE) { 116143509a12Srenato log_info("route %s/%u vanished before delete", 116243509a12Srenato inet_ntoa(kr->prefix.v4), 116343509a12Srenato kr->prefixlen); 116443509a12Srenato return (0); 116543509a12Srenato } 116643509a12Srenato } 116743509a12Srenato log_warn("%s: action %u, prefix %s/%u", __func__, hdr.rtm_type, 116843509a12Srenato inet_ntoa(kr->prefix.v4), kr->prefixlen); 116943509a12Srenato return (0); 117043509a12Srenato } 117143509a12Srenato 117243509a12Srenato return (0); 117343509a12Srenato } 117443509a12Srenato 117543509a12Srenato static int 117643509a12Srenato send_rtmsg_v6(int fd, int action, struct kroute *kr) 117743509a12Srenato { 117843509a12Srenato struct iovec iov[5]; 117943509a12Srenato struct rt_msghdr hdr; 118043509a12Srenato struct pad { 118143509a12Srenato struct sockaddr_in6 addr; 118243509a12Srenato char pad[sizeof(long)]; /* thank you IPv6 */ 118343509a12Srenato } prefix, nexthop, mask; 118443509a12Srenato int iovcnt = 0; 118543509a12Srenato 118643509a12Srenato if (kr_state.fib_sync == 0) 118743509a12Srenato return (0); 118843509a12Srenato 118943509a12Srenato /* initialize header */ 119043509a12Srenato memset(&hdr, 0, sizeof(hdr)); 119143509a12Srenato hdr.rtm_version = RTM_VERSION; 119243509a12Srenato hdr.rtm_type = action; 119343509a12Srenato hdr.rtm_priority = kr->priority; 119443509a12Srenato hdr.rtm_tableid = kr_state.rdomain; /* rtableid */ 119543509a12Srenato if (action == RTM_CHANGE) 119643509a12Srenato hdr.rtm_fmask = RTF_REJECT|RTF_BLACKHOLE; 119743509a12Srenato else 119843509a12Srenato hdr.rtm_flags = RTF_MPATH; 119943509a12Srenato hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ 120043509a12Srenato hdr.rtm_msglen = sizeof(hdr); 120143509a12Srenato /* adjust iovec */ 120243509a12Srenato iov[iovcnt].iov_base = &hdr; 120343509a12Srenato iov[iovcnt++].iov_len = sizeof(hdr); 120443509a12Srenato 120543509a12Srenato memset(&prefix, 0, sizeof(prefix)); 120643509a12Srenato prefix.addr.sin6_len = sizeof(struct sockaddr_in6); 120743509a12Srenato prefix.addr.sin6_family = AF_INET6; 120843509a12Srenato prefix.addr.sin6_addr = kr->prefix.v6; 120943509a12Srenato /* adjust header */ 121043509a12Srenato hdr.rtm_addrs |= RTA_DST; 121143509a12Srenato hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6)); 121243509a12Srenato /* adjust iovec */ 121343509a12Srenato iov[iovcnt].iov_base = &prefix; 121443509a12Srenato iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6)); 121543509a12Srenato 121643509a12Srenato if (!IN6_IS_ADDR_UNSPECIFIED(&kr->nexthop.v6)) { 121743509a12Srenato memset(&nexthop, 0, sizeof(nexthop)); 121843509a12Srenato nexthop.addr.sin6_len = sizeof(struct sockaddr_in6); 121943509a12Srenato nexthop.addr.sin6_family = AF_INET6; 122043509a12Srenato nexthop.addr.sin6_addr = kr->nexthop.v6; 122143509a12Srenato nexthop.addr.sin6_scope_id = kr->ifindex; 122243509a12Srenato embedscope(&nexthop.addr); 122343509a12Srenato 122443509a12Srenato /* adjust header */ 122543509a12Srenato hdr.rtm_flags |= RTF_GATEWAY; 122643509a12Srenato hdr.rtm_addrs |= RTA_GATEWAY; 122743509a12Srenato hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6)); 122843509a12Srenato /* adjust iovec */ 122943509a12Srenato iov[iovcnt].iov_base = &nexthop; 123043509a12Srenato iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6)); 123143509a12Srenato } 123243509a12Srenato 123343509a12Srenato memset(&mask, 0, sizeof(mask)); 123443509a12Srenato mask.addr.sin6_len = sizeof(struct sockaddr_in6); 123543509a12Srenato mask.addr.sin6_family = AF_INET6; 123643509a12Srenato mask.addr.sin6_addr = *prefixlen2mask6(kr->prefixlen); 123743509a12Srenato /* adjust header */ 123843509a12Srenato if (kr->prefixlen == 128) 123943509a12Srenato hdr.rtm_flags |= RTF_HOST; 124043509a12Srenato hdr.rtm_addrs |= RTA_NETMASK; 124143509a12Srenato hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6)); 124243509a12Srenato /* adjust iovec */ 124343509a12Srenato iov[iovcnt].iov_base = &mask; 124443509a12Srenato iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6)); 124543509a12Srenato 124643509a12Srenato retry: 124743509a12Srenato if (writev(fd, iov, iovcnt) == -1) { 124843509a12Srenato if (errno == ESRCH) { 124943509a12Srenato if (hdr.rtm_type == RTM_CHANGE) { 125043509a12Srenato hdr.rtm_type = RTM_ADD; 125143509a12Srenato goto retry; 125243509a12Srenato } else if (hdr.rtm_type == RTM_DELETE) { 125343509a12Srenato log_info("route %s/%u vanished before delete", 125443509a12Srenato log_in6addr(&kr->prefix.v6), 125543509a12Srenato kr->prefixlen); 125643509a12Srenato return (0); 125743509a12Srenato } 125843509a12Srenato } 125943509a12Srenato log_warn("%s: action %u, prefix %s/%u", __func__, hdr.rtm_type, 126043509a12Srenato log_in6addr(&kr->prefix.v6), kr->prefixlen); 126143509a12Srenato return (0); 126243509a12Srenato } 126343509a12Srenato 126443509a12Srenato return (0); 126543509a12Srenato } 126643509a12Srenato 126743509a12Srenato int 126843509a12Srenato send_rtmsg(int fd, int action, struct kroute *kr) 126943509a12Srenato { 127043509a12Srenato switch (kr->af) { 127143509a12Srenato case AF_INET: 127243509a12Srenato return (send_rtmsg_v4(fd, action, kr)); 127343509a12Srenato case AF_INET6: 127443509a12Srenato return (send_rtmsg_v6(fd, action, kr)); 127543509a12Srenato default: 127643509a12Srenato break; 127743509a12Srenato } 127843509a12Srenato 127943509a12Srenato return (-1); 128043509a12Srenato } 128143509a12Srenato 128243509a12Srenato int 128343509a12Srenato fetchtable(void) 128443509a12Srenato { 128543509a12Srenato size_t len; 128643509a12Srenato int mib[7]; 128743509a12Srenato char *buf; 128843509a12Srenato int rv; 128943509a12Srenato 129043509a12Srenato mib[0] = CTL_NET; 129143509a12Srenato mib[1] = PF_ROUTE; 129243509a12Srenato mib[2] = 0; 129343509a12Srenato mib[3] = 0; 129443509a12Srenato mib[4] = NET_RT_DUMP; 129543509a12Srenato mib[5] = 0; 129643509a12Srenato mib[6] = kr_state.rdomain; /* rtableid */ 129743509a12Srenato 129843509a12Srenato if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) { 129943509a12Srenato log_warn("sysctl"); 130043509a12Srenato return (-1); 130143509a12Srenato } 130243509a12Srenato if ((buf = malloc(len)) == NULL) { 130343509a12Srenato log_warn("fetchtable"); 130443509a12Srenato return (-1); 130543509a12Srenato } 130643509a12Srenato if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) { 130743509a12Srenato log_warn("sysctl"); 130843509a12Srenato free(buf); 130943509a12Srenato return (-1); 131043509a12Srenato } 131143509a12Srenato 131243509a12Srenato rv = rtmsg_process(buf, len); 131343509a12Srenato free(buf); 131443509a12Srenato 131543509a12Srenato return (rv); 131643509a12Srenato } 131743509a12Srenato 131843509a12Srenato int 131943509a12Srenato fetchifs(void) 132043509a12Srenato { 132143509a12Srenato size_t len; 132243509a12Srenato int mib[6]; 132343509a12Srenato char *buf; 132443509a12Srenato int rv; 132543509a12Srenato 132643509a12Srenato mib[0] = CTL_NET; 132743509a12Srenato mib[1] = PF_ROUTE; 132843509a12Srenato mib[2] = 0; 132943509a12Srenato mib[3] = 0; /* wildcard */ 133043509a12Srenato mib[4] = NET_RT_IFLIST; 133143509a12Srenato mib[5] = 0; 133243509a12Srenato 133343509a12Srenato if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { 133443509a12Srenato log_warn("sysctl"); 133543509a12Srenato return (-1); 133643509a12Srenato } 133743509a12Srenato if ((buf = malloc(len)) == NULL) { 133843509a12Srenato log_warn("fetchifs"); 133943509a12Srenato return (-1); 134043509a12Srenato } 134143509a12Srenato if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) { 134243509a12Srenato log_warn("sysctl"); 134343509a12Srenato free(buf); 134443509a12Srenato return (-1); 134543509a12Srenato } 134643509a12Srenato 134743509a12Srenato rv = rtmsg_process(buf, len); 134843509a12Srenato free(buf); 134943509a12Srenato 135043509a12Srenato return (rv); 135143509a12Srenato } 135243509a12Srenato 135343509a12Srenato int 135443509a12Srenato dispatch_rtmsg(void) 135543509a12Srenato { 135643509a12Srenato char buf[RT_BUF_SIZE]; 135743509a12Srenato ssize_t n; 135843509a12Srenato 135943509a12Srenato if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) { 136043509a12Srenato if (errno == EAGAIN || errno == EINTR) 136143509a12Srenato return (0); 136243509a12Srenato log_warn("%s: read error", __func__); 136343509a12Srenato return (-1); 136443509a12Srenato } 136543509a12Srenato 136643509a12Srenato if (n == 0) { 136743509a12Srenato log_warnx("routing socket closed"); 136843509a12Srenato return (-1); 136943509a12Srenato } 137043509a12Srenato 137143509a12Srenato return (rtmsg_process(buf, n)); 137243509a12Srenato } 137343509a12Srenato 137443509a12Srenato int 137543509a12Srenato rtmsg_process(char *buf, size_t len) 137643509a12Srenato { 137743509a12Srenato struct rt_msghdr *rtm; 137843509a12Srenato struct if_msghdr ifm; 137943509a12Srenato struct ifa_msghdr *ifam; 138043509a12Srenato struct sockaddr *sa, *rti_info[RTAX_MAX]; 138143509a12Srenato size_t offset; 138243509a12Srenato char *next; 138343509a12Srenato 138443509a12Srenato for (offset = 0; offset < len; offset += rtm->rtm_msglen) { 138543509a12Srenato next = buf + offset; 138643509a12Srenato rtm = (struct rt_msghdr *)next; 138743509a12Srenato if (len < offset + sizeof(unsigned short) || 138843509a12Srenato len < offset + rtm->rtm_msglen) 138943509a12Srenato fatalx("rtmsg_process: partial rtm in buffer"); 139043509a12Srenato if (rtm->rtm_version != RTM_VERSION) 139143509a12Srenato continue; 139243509a12Srenato 139343509a12Srenato sa = (struct sockaddr *)(next + rtm->rtm_hdrlen); 139443509a12Srenato get_rtaddrs(rtm->rtm_addrs, sa, rti_info); 139543509a12Srenato 139643509a12Srenato switch (rtm->rtm_type) { 139743509a12Srenato case RTM_ADD: 139843509a12Srenato case RTM_GET: 139943509a12Srenato case RTM_CHANGE: 140043509a12Srenato case RTM_DELETE: 140143509a12Srenato if (rtm->rtm_errno) /* failed attempts... */ 140243509a12Srenato continue; 140343509a12Srenato 140443509a12Srenato if (rtm->rtm_tableid != kr_state.rdomain) 140543509a12Srenato continue; 140643509a12Srenato 1407*e2a2cc33Srenato if (rtm->rtm_type == RTM_GET && 1408*e2a2cc33Srenato rtm->rtm_pid != kr_state.pid) 1409*e2a2cc33Srenato continue; 1410*e2a2cc33Srenato 141143509a12Srenato /* Skip ARP/ND cache and broadcast routes. */ 141243509a12Srenato if (rtm->rtm_flags & (RTF_LLINFO|RTF_BROADCAST)) 141343509a12Srenato continue; 141443509a12Srenato 141543509a12Srenato if (rtmsg_process_route(rtm, rti_info) == -1) 141643509a12Srenato return (-1); 141743509a12Srenato } 141843509a12Srenato 141943509a12Srenato switch (rtm->rtm_type) { 142043509a12Srenato case RTM_IFINFO: 142143509a12Srenato memcpy(&ifm, next, sizeof(ifm)); 142243509a12Srenato if_change(ifm.ifm_index, ifm.ifm_flags, &ifm.ifm_data, 142343509a12Srenato (struct sockaddr_dl *)rti_info[RTAX_IFP]); 142443509a12Srenato break; 142543509a12Srenato case RTM_NEWADDR: 142643509a12Srenato ifam = (struct ifa_msghdr *)rtm; 142743509a12Srenato if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | 142843509a12Srenato RTA_BRD)) == 0) 142943509a12Srenato break; 143043509a12Srenato 143143509a12Srenato if_newaddr(ifam->ifam_index, 143243509a12Srenato (struct sockaddr *)rti_info[RTAX_IFA], 143343509a12Srenato (struct sockaddr *)rti_info[RTAX_NETMASK], 143443509a12Srenato (struct sockaddr *)rti_info[RTAX_BRD]); 143543509a12Srenato break; 143643509a12Srenato case RTM_DELADDR: 143743509a12Srenato ifam = (struct ifa_msghdr *)rtm; 143843509a12Srenato if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | 143943509a12Srenato RTA_BRD)) == 0) 144043509a12Srenato break; 144143509a12Srenato 144243509a12Srenato if_deladdr(ifam->ifam_index, 144343509a12Srenato (struct sockaddr *)rti_info[RTAX_IFA], 144443509a12Srenato (struct sockaddr *)rti_info[RTAX_NETMASK], 144543509a12Srenato (struct sockaddr *)rti_info[RTAX_BRD]); 144643509a12Srenato break; 144743509a12Srenato case RTM_IFANNOUNCE: 144843509a12Srenato if_announce(next); 144943509a12Srenato break; 145043509a12Srenato default: 145143509a12Srenato /* ignore for now */ 145243509a12Srenato break; 145343509a12Srenato } 145443509a12Srenato } 145543509a12Srenato 145643509a12Srenato return (offset); 145743509a12Srenato } 145843509a12Srenato 145943509a12Srenato int 146043509a12Srenato rtmsg_process_route(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX]) 146143509a12Srenato { 146243509a12Srenato struct sockaddr *sa; 146343509a12Srenato struct sockaddr_in *sa_in; 146443509a12Srenato struct sockaddr_in6 *sa_in6; 146543509a12Srenato struct kroute kr; 146643509a12Srenato struct kroute_prefix *kp; 146743509a12Srenato struct kroute_priority *kprio; 146843509a12Srenato struct kroute_node *kn; 146943509a12Srenato 147043509a12Srenato if ((sa = rti_info[RTAX_DST]) == NULL) 147143509a12Srenato return (-1); 147243509a12Srenato 147343509a12Srenato memset(&kr, 0, sizeof(kr)); 147443509a12Srenato kr.af = sa->sa_family; 147543509a12Srenato switch (kr.af) { 147643509a12Srenato case AF_INET: 147743509a12Srenato kr.prefix.v4.s_addr = 147843509a12Srenato ((struct sockaddr_in *)sa)->sin_addr.s_addr; 147943509a12Srenato sa_in = (struct sockaddr_in *) rti_info[RTAX_NETMASK]; 148043509a12Srenato if (sa_in != NULL && sa_in->sin_len != 0) 148143509a12Srenato kr.prefixlen = mask2prefixlen(sa_in->sin_addr.s_addr); 148243509a12Srenato else if (rtm->rtm_flags & RTF_HOST) 148343509a12Srenato kr.prefixlen = 32; 148443509a12Srenato else if (kr.prefix.v4.s_addr == INADDR_ANY) 148543509a12Srenato kr.prefixlen = 0; 148643509a12Srenato else 148743509a12Srenato kr.prefixlen = prefixlen_classful(kr.prefix.v4.s_addr); 148843509a12Srenato break; 148943509a12Srenato case AF_INET6: 149043509a12Srenato memcpy(&kr.prefix.v6, &((struct sockaddr_in6 *)sa)->sin6_addr, 149143509a12Srenato sizeof(kr.prefix.v6)); 149243509a12Srenato sa_in6 = (struct sockaddr_in6 *)rti_info[RTAX_NETMASK]; 149343509a12Srenato if (sa_in6 != NULL && sa_in6->sin6_len != 0) 149443509a12Srenato kr.prefixlen = mask2prefixlen6(sa_in6); 149543509a12Srenato else if (rtm->rtm_flags & RTF_HOST) 149643509a12Srenato kr.prefixlen = 128; 149743509a12Srenato else if (IN6_IS_ADDR_UNSPECIFIED(&kr.prefix.v6)) 149843509a12Srenato kr.prefixlen = 0; 149943509a12Srenato else 150043509a12Srenato fatalx("in6 net addr without netmask"); 150143509a12Srenato break; 150243509a12Srenato default: 150343509a12Srenato return (0); 150443509a12Srenato } 150543509a12Srenato kr.ifindex = rtm->rtm_index; 150643509a12Srenato if ((sa = rti_info[RTAX_GATEWAY]) != NULL) { 150743509a12Srenato switch (sa->sa_family) { 150843509a12Srenato case AF_INET: 150943509a12Srenato kr.nexthop.v4.s_addr = 151043509a12Srenato ((struct sockaddr_in *)sa)->sin_addr.s_addr; 151143509a12Srenato break; 151243509a12Srenato case AF_INET6: 151343509a12Srenato sa_in6 = (struct sockaddr_in6 *)sa; 151443509a12Srenato recoverscope(sa_in6); 151543509a12Srenato kr.nexthop.v6 = sa_in6->sin6_addr; 151643509a12Srenato if (sa_in6->sin6_scope_id) 151743509a12Srenato kr.ifindex = sa_in6->sin6_scope_id; 151843509a12Srenato break; 151943509a12Srenato case AF_LINK: 152043509a12Srenato kr.flags |= F_CONNECTED; 152143509a12Srenato break; 152243509a12Srenato } 152343509a12Srenato } 152443509a12Srenato kr.flags |= F_KERNEL; 152543509a12Srenato if (rtm->rtm_flags & RTF_STATIC) 152643509a12Srenato kr.flags |= F_STATIC; 152743509a12Srenato if (rtm->rtm_flags & RTF_BLACKHOLE) 152843509a12Srenato kr.flags |= F_BLACKHOLE; 152943509a12Srenato if (rtm->rtm_flags & RTF_REJECT) 153043509a12Srenato kr.flags |= F_REJECT; 153143509a12Srenato if (rtm->rtm_flags & RTF_DYNAMIC) 153243509a12Srenato kr.flags |= F_DYNAMIC; 153343509a12Srenato kr.priority = rtm->rtm_priority; 153443509a12Srenato 153543509a12Srenato if (rtm->rtm_type == RTM_CHANGE) { 153643509a12Srenato /* 153743509a12Srenato * The kernel doesn't allow RTM_CHANGE for multipath routes. 153843509a12Srenato * If we got this message we know that the route has only one 153943509a12Srenato * nexthop and we should remove it before installing the same 154043509a12Srenato * route with the new nexthop. 154143509a12Srenato */ 154243509a12Srenato kp = kroute_find_prefix(kr.af, &kr.prefix, kr.prefixlen); 154343509a12Srenato if (kp) { 154443509a12Srenato kprio = kroute_find_prio(kp, kr.priority); 154543509a12Srenato if (kprio) { 154643509a12Srenato kn = TAILQ_FIRST(&kprio->nexthops); 15474f0b2092Srenato if (kn) 15484f0b2092Srenato kroute_remove(&kn->r); 154943509a12Srenato } 155043509a12Srenato } 155143509a12Srenato } 155243509a12Srenato 155343509a12Srenato kn = NULL; 155443509a12Srenato kp = kroute_find_prefix(kr.af, &kr.prefix, kr.prefixlen); 155543509a12Srenato if (kp) { 155643509a12Srenato kprio = kroute_find_prio(kp, kr.priority); 155743509a12Srenato if (kprio) 155843509a12Srenato kn = kroute_find_gw(kprio, &kr.nexthop); 155943509a12Srenato } 156043509a12Srenato 156143509a12Srenato if (rtm->rtm_type == RTM_DELETE) { 156243509a12Srenato if (kn == NULL || !(kn->r.flags & F_KERNEL)) 156343509a12Srenato return (0); 156443509a12Srenato return (kroute_remove(&kr)); 156543509a12Srenato } 156643509a12Srenato 156743509a12Srenato if (!eigrp_addrisset(kr.af, &kr.nexthop) && !(kr.flags & F_CONNECTED)) { 156843509a12Srenato log_warnx("%s: no nexthop for %s/%u", __func__, 156943509a12Srenato log_addr(kr.af, &kr.prefix), kr.prefixlen); 157043509a12Srenato return (-1); 157143509a12Srenato } 157243509a12Srenato 157343509a12Srenato if (kn != NULL) { 157443509a12Srenato /* update route */ 157543509a12Srenato memcpy(&kn->r, &kr, sizeof(kn->r)); 157643509a12Srenato 157743509a12Srenato if (kif_validate(kn->r.ifindex)) 157843509a12Srenato kn->r.flags &= ~F_DOWN; 157943509a12Srenato else 158043509a12Srenato kn->r.flags |= F_DOWN; 158143509a12Srenato 158243509a12Srenato kr_redistribute(kp); 15834f0b2092Srenato } else { 15844f0b2092Srenato if ((rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_GET) && 15854f0b2092Srenato (kr.priority == eigrpd_conf->fib_priority_internal || 15864f0b2092Srenato kr.priority == eigrpd_conf->fib_priority_external || 15874f0b2092Srenato kr.priority == eigrpd_conf->fib_priority_summary)) { 15884f0b2092Srenato log_warnx("alien EIGRP route %s/%d", log_addr(kr.af, 15894f0b2092Srenato &kr.prefix), kr.prefixlen); 15904f0b2092Srenato return (send_rtmsg(kr_state.fd, RTM_DELETE, &kr)); 15914f0b2092Srenato } 15924f0b2092Srenato 159343509a12Srenato kroute_insert(&kr); 15944f0b2092Srenato } 159543509a12Srenato 159643509a12Srenato return (0); 159743509a12Srenato } 1598