1*5b133f3fSguenther /* $OpenBSD: kroute.c,v 1.20 2023/03/08 04:43:13 guenther 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 <net/if.h>
2543509a12Srenato #include <net/if_dl.h>
2643509a12Srenato #include <net/route.h>
278072de9bSrenato #include <netinet/in.h>
288072de9bSrenato
298072de9bSrenato #include <arpa/inet.h>
3043509a12Srenato #include <errno.h>
3143509a12Srenato #include <stdlib.h>
3243509a12Srenato #include <string.h>
3343509a12Srenato #include <unistd.h>
3443509a12Srenato
3543509a12Srenato #include "eigrpd.h"
3643509a12Srenato #include "log.h"
3743509a12Srenato
38ab786365Srenato static struct {
3943509a12Srenato uint32_t rtseq;
4043509a12Srenato pid_t pid;
4143509a12Srenato int fib_sync;
4243509a12Srenato int fd;
4343509a12Srenato struct event ev;
4443509a12Srenato unsigned int rdomain;
4543509a12Srenato } kr_state;
4643509a12Srenato
4743509a12Srenato struct kroute_node {
4843509a12Srenato TAILQ_ENTRY(kroute_node) entry;
4943509a12Srenato struct kroute_priority *kprio; /* back pointer */
5043509a12Srenato struct kroute r;
5143509a12Srenato };
5243509a12Srenato
5343509a12Srenato struct kroute_priority {
5443509a12Srenato TAILQ_ENTRY(kroute_priority) entry;
5543509a12Srenato struct kroute_prefix *kp; /* back pointer */
5643509a12Srenato uint8_t priority;
5743509a12Srenato TAILQ_HEAD(, kroute_node) nexthops;
5843509a12Srenato };
5943509a12Srenato
6043509a12Srenato struct kroute_prefix {
6143509a12Srenato RB_ENTRY(kroute_prefix) entry;
6243509a12Srenato int af;
6343509a12Srenato union eigrpd_addr prefix;
6443509a12Srenato uint8_t prefixlen;
6543509a12Srenato TAILQ_HEAD(plist, kroute_priority) priorities;
6643509a12Srenato };
67ab786365Srenato RB_HEAD(kroute_tree, kroute_prefix);
68ab786365Srenato RB_PROTOTYPE(kroute_tree, kroute_prefix, entry, kroute_compare)
6943509a12Srenato
7043509a12Srenato struct kif_addr {
7143509a12Srenato TAILQ_ENTRY(kif_addr) entry;
7243509a12Srenato struct kaddr a;
7343509a12Srenato };
7443509a12Srenato
7543509a12Srenato struct kif_node {
7643509a12Srenato RB_ENTRY(kif_node) entry;
7743509a12Srenato TAILQ_HEAD(, kif_addr) addrs;
7843509a12Srenato struct kif k;
7943509a12Srenato };
80ab786365Srenato RB_HEAD(kif_tree, kif_node);
81ab786365Srenato RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
8243509a12Srenato
83ab786365Srenato static void kr_dispatch_msg(int, short, void *);
84ab786365Srenato static void kr_redist_remove(struct kroute *);
85ab786365Srenato static int kr_redist_eval(struct kroute *);
86ab786365Srenato static void kr_redistribute(struct kroute_prefix *);
87ab786365Srenato static __inline int kroute_compare(struct kroute_prefix *,
8843509a12Srenato struct kroute_prefix *);
89ab786365Srenato static struct kroute_prefix *kroute_find_prefix(int, union eigrpd_addr *,
90ab786365Srenato uint8_t);
91ab786365Srenato static struct kroute_priority *kroute_find_prio(struct kroute_prefix *,
92ab786365Srenato uint8_t);
93ab786365Srenato static struct kroute_node *kroute_find_gw(struct kroute_priority *,
9443509a12Srenato union eigrpd_addr *);
95ab786365Srenato static struct kroute_node *kroute_insert(struct kroute *);
96ab786365Srenato static int kroute_remove(struct kroute *);
97ab786365Srenato static void kroute_clear(void);
98ab786365Srenato static __inline int kif_compare(struct kif_node *, struct kif_node *);
99ab786365Srenato static struct kif_node *kif_find(unsigned short);
100ab786365Srenato static struct kif_node *kif_insert(unsigned short);
101ab786365Srenato static int kif_remove(struct kif_node *);
102ab786365Srenato static struct kif *kif_update(unsigned short, int, struct if_data *,
10343509a12Srenato struct sockaddr_dl *);
104ab786365Srenato static int kif_validate(unsigned short);
105ab786365Srenato static void protect_lo(void);
106ab786365Srenato static uint8_t prefixlen_classful(in_addr_t);
107ab786365Srenato static void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
108ab786365Srenato static void if_change(unsigned short, int, struct if_data *,
10943509a12Srenato struct sockaddr_dl *);
110ab786365Srenato static void if_newaddr(unsigned short, struct sockaddr *,
111ab786365Srenato struct sockaddr *, struct sockaddr *);
112ab786365Srenato static void if_deladdr(unsigned short, struct sockaddr *,
113ab786365Srenato struct sockaddr *, struct sockaddr *);
114ab786365Srenato static void if_announce(void *);
115ab786365Srenato static int send_rtmsg_v4(int, int, struct kroute *);
116ab786365Srenato static int send_rtmsg_v6(int, int, struct kroute *);
117ab786365Srenato static int send_rtmsg(int, int, struct kroute *);
118ab786365Srenato static int fetchtable(void);
119ab786365Srenato static int fetchifs(void);
120ab786365Srenato static int dispatch_rtmsg(void);
121ab786365Srenato static int rtmsg_process(char *, size_t);
122ab786365Srenato static int rtmsg_process_route(struct rt_msghdr *,
12343509a12Srenato struct sockaddr *[RTAX_MAX]);
12443509a12Srenato
12543509a12Srenato RB_GENERATE(kroute_tree, kroute_prefix, entry, kroute_compare)
12643509a12Srenato RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
12743509a12Srenato
128ab786365Srenato static struct kroute_tree krt = RB_INITIALIZER(&krt);
129ab786365Srenato static struct kif_tree kit = RB_INITIALIZER(&kit);
130ab786365Srenato
13143509a12Srenato int
kif_init(void)13243509a12Srenato kif_init(void)
13343509a12Srenato {
13443509a12Srenato if (fetchifs() == -1)
13543509a12Srenato return (-1);
13643509a12Srenato
13743509a12Srenato return (0);
13843509a12Srenato }
13943509a12Srenato
14043509a12Srenato int
kr_init(int fs,unsigned int rdomain)14143509a12Srenato kr_init(int fs, unsigned int rdomain)
14243509a12Srenato {
14343509a12Srenato int opt = 0, rcvbuf, default_rcvbuf;
14443509a12Srenato socklen_t optlen;
14543509a12Srenato
14643509a12Srenato kr_state.fib_sync = fs;
14743509a12Srenato kr_state.rdomain = rdomain;
14843509a12Srenato
14943509a12Srenato if ((kr_state.fd = socket(AF_ROUTE,
15043509a12Srenato SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1) {
15143509a12Srenato log_warn("%s: socket", __func__);
15243509a12Srenato return (-1);
15343509a12Srenato }
15443509a12Srenato
15543509a12Srenato /* not interested in my own messages */
15643509a12Srenato if (setsockopt(kr_state.fd, SOL_SOCKET, SO_USELOOPBACK,
15743509a12Srenato &opt, sizeof(opt)) == -1)
15844faa115Srenato log_warn("%s: setsockopt(SO_USELOOPBACK)", __func__);
15943509a12Srenato
16043509a12Srenato /* grow receive buffer, don't wanna miss messages */
16143509a12Srenato optlen = sizeof(default_rcvbuf);
16243509a12Srenato if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF,
16343509a12Srenato &default_rcvbuf, &optlen) == -1)
16443509a12Srenato log_warn("%s: getsockopt SOL_SOCKET SO_RCVBUF", __func__);
16543509a12Srenato else
16643509a12Srenato for (rcvbuf = MAX_RTSOCK_BUF;
16743509a12Srenato rcvbuf > default_rcvbuf &&
16843509a12Srenato setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF,
16943509a12Srenato &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS;
17043509a12Srenato rcvbuf /= 2)
17143509a12Srenato ; /* nothing */
17243509a12Srenato
17343509a12Srenato kr_state.pid = getpid();
17443509a12Srenato kr_state.rtseq = 1;
17543509a12Srenato
17643509a12Srenato if (fetchtable() == -1)
17743509a12Srenato return (-1);
17843509a12Srenato
17943509a12Srenato protect_lo();
18043509a12Srenato
18143509a12Srenato event_set(&kr_state.ev, kr_state.fd, EV_READ | EV_PERSIST,
18243509a12Srenato kr_dispatch_msg, NULL);
18343509a12Srenato event_add(&kr_state.ev, NULL);
18443509a12Srenato
18543509a12Srenato return (0);
18643509a12Srenato }
18743509a12Srenato
18843509a12Srenato void
kif_redistribute(void)18943509a12Srenato kif_redistribute(void)
19043509a12Srenato {
19143509a12Srenato struct kif_node *kif;
19243509a12Srenato struct kif_addr *ka;
19343509a12Srenato
194d6040158Srenato RB_FOREACH(kif, kif_tree, &kit) {
195d6040158Srenato main_imsg_compose_eigrpe(IMSG_IFINFO, 0, &kif->k,
196d6040158Srenato sizeof(struct kif));
197d6040158Srenato TAILQ_FOREACH(ka, &kif->addrs, entry) {
19843509a12Srenato main_imsg_compose_eigrpe(IMSG_NEWADDR, 0, &ka->a,
1992e046f62Srenato sizeof(ka->a));
20043509a12Srenato }
201d6040158Srenato }
202d6040158Srenato }
20343509a12Srenato
20443509a12Srenato int
kr_change(struct kroute * kr)20543509a12Srenato kr_change(struct kroute *kr)
20643509a12Srenato {
20743509a12Srenato struct kroute_prefix *kp;
20843509a12Srenato struct kroute_priority *kprio;
20943509a12Srenato struct kroute_node *kn;
21043509a12Srenato int action = RTM_ADD;
21143509a12Srenato
21243509a12Srenato kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen);
21343509a12Srenato if (kp == NULL)
21443509a12Srenato kn = kroute_insert(kr);
21543509a12Srenato else {
21643509a12Srenato kprio = kroute_find_prio(kp, kr->priority);
21743509a12Srenato if (kprio == NULL)
21843509a12Srenato kn = kroute_insert(kr);
21943509a12Srenato else {
22043509a12Srenato kn = kroute_find_gw(kprio, &kr->nexthop);
22143509a12Srenato if (kn == NULL)
22243509a12Srenato kn = kroute_insert(kr);
22343509a12Srenato else
22443509a12Srenato action = RTM_CHANGE;
22543509a12Srenato }
22643509a12Srenato }
22743509a12Srenato
22843509a12Srenato /* send update */
22943509a12Srenato if (send_rtmsg(kr_state.fd, action, kr) == -1)
23043509a12Srenato return (-1);
23143509a12Srenato
23243509a12Srenato kn->r.flags |= F_EIGRPD_INSERTED;
23343509a12Srenato
23443509a12Srenato return (0);
23543509a12Srenato }
23643509a12Srenato
23743509a12Srenato int
kr_delete(struct kroute * kr)23843509a12Srenato kr_delete(struct kroute *kr)
23943509a12Srenato {
24043509a12Srenato struct kroute_prefix *kp;
24143509a12Srenato struct kroute_priority *kprio;
24243509a12Srenato struct kroute_node *kn;
24343509a12Srenato
24443509a12Srenato kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen);
24543509a12Srenato if (kp == NULL)
24643509a12Srenato return (0);
24743509a12Srenato kprio = kroute_find_prio(kp, kr->priority);
24843509a12Srenato if (kprio == NULL)
24943509a12Srenato return (0);
25043509a12Srenato kn = kroute_find_gw(kprio, &kr->nexthop);
25143509a12Srenato if (kn == NULL)
25243509a12Srenato return (0);
25343509a12Srenato
25443509a12Srenato if (!(kn->r.flags & F_EIGRPD_INSERTED))
25543509a12Srenato return (0);
25643509a12Srenato
25743509a12Srenato if (send_rtmsg(kr_state.fd, RTM_DELETE, &kn->r) == -1)
25843509a12Srenato return (-1);
25943509a12Srenato
26043509a12Srenato if (kroute_remove(kr) == -1)
26143509a12Srenato return (-1);
26243509a12Srenato
26343509a12Srenato return (0);
26443509a12Srenato }
26543509a12Srenato
26643509a12Srenato void
kr_shutdown(void)26743509a12Srenato kr_shutdown(void)
26843509a12Srenato {
26943509a12Srenato kr_fib_decouple();
27043509a12Srenato kroute_clear();
27143509a12Srenato kif_clear();
27243509a12Srenato }
27343509a12Srenato
27443509a12Srenato void
kr_fib_couple(void)27543509a12Srenato kr_fib_couple(void)
27643509a12Srenato {
27743509a12Srenato struct kroute_prefix *kp;
27843509a12Srenato struct kroute_priority *kprio;
27943509a12Srenato struct kroute_node *kn;
28043509a12Srenato
28143509a12Srenato if (kr_state.fib_sync == 1) /* already coupled */
28243509a12Srenato return;
28343509a12Srenato
28443509a12Srenato kr_state.fib_sync = 1;
28543509a12Srenato
28643509a12Srenato RB_FOREACH(kp, kroute_tree, &krt)
28743509a12Srenato TAILQ_FOREACH(kprio, &kp->priorities, entry)
28843509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) {
28943509a12Srenato if (!(kn->r.flags & F_EIGRPD_INSERTED))
29043509a12Srenato continue;
29143509a12Srenato send_rtmsg(kr_state.fd, RTM_ADD, &kn->r);
29243509a12Srenato }
29343509a12Srenato
29443509a12Srenato log_info("kernel routing table coupled");
29543509a12Srenato }
29643509a12Srenato
29743509a12Srenato void
kr_fib_decouple(void)29843509a12Srenato kr_fib_decouple(void)
29943509a12Srenato {
30043509a12Srenato struct kroute_prefix *kp;
30143509a12Srenato struct kroute_priority *kprio;
30243509a12Srenato struct kroute_node *kn;
30343509a12Srenato
30443509a12Srenato if (kr_state.fib_sync == 0) /* already decoupled */
30543509a12Srenato return;
30643509a12Srenato
30743509a12Srenato RB_FOREACH(kp, kroute_tree, &krt)
30843509a12Srenato TAILQ_FOREACH(kprio, &kp->priorities, entry)
30943509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) {
31043509a12Srenato if (!(kn->r.flags & F_EIGRPD_INSERTED))
31143509a12Srenato continue;
31243509a12Srenato
31343509a12Srenato send_rtmsg(kr_state.fd, RTM_DELETE, &kn->r);
31443509a12Srenato }
31543509a12Srenato
31643509a12Srenato kr_state.fib_sync = 0;
31743509a12Srenato
31843509a12Srenato log_info("kernel routing table decoupled");
31943509a12Srenato }
32043509a12Srenato
321ab786365Srenato static void
kr_dispatch_msg(int fd,short event,void * bula)32243509a12Srenato kr_dispatch_msg(int fd, short event, void *bula)
32343509a12Srenato {
32443509a12Srenato if (dispatch_rtmsg() == -1)
32543509a12Srenato event_loopexit(NULL);
32643509a12Srenato }
32743509a12Srenato
32843509a12Srenato void
kr_show_route(struct imsg * imsg)32943509a12Srenato kr_show_route(struct imsg *imsg)
33043509a12Srenato {
33143509a12Srenato struct kroute_prefix *kp;
33243509a12Srenato struct kroute_priority *kprio;
33343509a12Srenato struct kroute_node *kn;
33443509a12Srenato struct kroute kr;
33543509a12Srenato int flags;
33643509a12Srenato
33743509a12Srenato if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags)) {
33843509a12Srenato log_warnx("%s: wrong imsg len", __func__);
33943509a12Srenato return;
34043509a12Srenato }
34143509a12Srenato memcpy(&flags, imsg->data, sizeof(flags));
34243509a12Srenato RB_FOREACH(kp, kroute_tree, &krt)
34343509a12Srenato TAILQ_FOREACH(kprio, &kp->priorities, entry)
34443509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) {
34543509a12Srenato if (flags && !(kn->r.flags & flags))
34643509a12Srenato continue;
34743509a12Srenato
3483eb03b29Srenato kr = kn->r;
34943509a12Srenato if (kr.priority ==
35043509a12Srenato eigrpd_conf->fib_priority_external)
35143509a12Srenato kr.flags |= F_CTL_EXTERNAL;
35243509a12Srenato main_imsg_compose_eigrpe(IMSG_CTL_KROUTE,
35343509a12Srenato imsg->hdr.pid, &kr, sizeof(kr));
35443509a12Srenato }
35543509a12Srenato
35643509a12Srenato main_imsg_compose_eigrpe(IMSG_CTL_END, imsg->hdr.pid, NULL, 0);
35743509a12Srenato }
35843509a12Srenato
35943509a12Srenato void
kr_ifinfo(char * ifname,pid_t pid)36043509a12Srenato kr_ifinfo(char *ifname, pid_t pid)
36143509a12Srenato {
36243509a12Srenato struct kif_node *kif;
36343509a12Srenato
36443509a12Srenato RB_FOREACH(kif, kif_tree, &kit)
36543509a12Srenato if (ifname == NULL || !strcmp(ifname, kif->k.ifname)) {
36643509a12Srenato main_imsg_compose_eigrpe(IMSG_CTL_IFINFO,
36743509a12Srenato pid, &kif->k, sizeof(kif->k));
36843509a12Srenato }
36943509a12Srenato
37043509a12Srenato main_imsg_compose_eigrpe(IMSG_CTL_END, pid, NULL, 0);
37143509a12Srenato }
37243509a12Srenato
373ab786365Srenato static void
kr_redist_remove(struct kroute * kr)37443509a12Srenato kr_redist_remove(struct kroute *kr)
37543509a12Srenato {
37643509a12Srenato /* was the route redistributed? */
37743509a12Srenato if (!(kr->flags & F_REDISTRIBUTED))
37843509a12Srenato return;
37943509a12Srenato
38043509a12Srenato /* remove redistributed flag */
38143509a12Srenato kr->flags &= ~F_REDISTRIBUTED;
38243509a12Srenato main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, kr, sizeof(*kr));
38343509a12Srenato }
38443509a12Srenato
385ab786365Srenato static int
kr_redist_eval(struct kroute * kr)38643509a12Srenato kr_redist_eval(struct kroute *kr)
38743509a12Srenato {
38843509a12Srenato /* Only non-eigrpd routes are considered for redistribution. */
38943509a12Srenato if (!(kr->flags & F_KERNEL))
39043509a12Srenato goto dont_redistribute;
39143509a12Srenato
39243509a12Srenato /* Dynamic routes are not redistributable. */
39343509a12Srenato if (kr->flags & F_DYNAMIC)
39443509a12Srenato goto dont_redistribute;
39543509a12Srenato
3966c428d2eSrenato /* filter-out non-redistributable addresses */
3976c428d2eSrenato if (bad_addr(kr->af, &kr->prefix) ||
3986c428d2eSrenato (kr->af == AF_INET6 && IN6_IS_SCOPE_EMBED(&kr->prefix.v6)))
3996c428d2eSrenato goto dont_redistribute;
4006c428d2eSrenato
40143509a12Srenato /* interface is not up and running so don't announce */
40243509a12Srenato if (kr->flags & F_DOWN)
40343509a12Srenato goto dont_redistribute;
40443509a12Srenato
4056c428d2eSrenato /*
4066c428d2eSrenato * Consider networks with nexthop loopback as not redistributable
4076c428d2eSrenato * unless it is a reject or blackhole route.
4086c428d2eSrenato */
40943509a12Srenato switch (kr->af) {
41043509a12Srenato case AF_INET:
41143509a12Srenato if (kr->nexthop.v4.s_addr == htonl(INADDR_LOOPBACK) &&
41243509a12Srenato !(kr->flags & (F_BLACKHOLE|F_REJECT)))
41343509a12Srenato goto dont_redistribute;
41443509a12Srenato break;
41543509a12Srenato case AF_INET6:
41643509a12Srenato if (IN6_IS_ADDR_LOOPBACK(&kr->nexthop.v6) &&
41743509a12Srenato !(kr->flags & (F_BLACKHOLE|F_REJECT)))
41843509a12Srenato goto dont_redistribute;
41943509a12Srenato break;
42043509a12Srenato default:
42143509a12Srenato log_debug("%s: unexpected address-family", __func__);
42243509a12Srenato break;
42343509a12Srenato }
42443509a12Srenato
42543509a12Srenato /* prefix should be redistributed */
42643509a12Srenato kr->flags |= F_REDISTRIBUTED;
42743509a12Srenato main_imsg_compose_rde(IMSG_NETWORK_ADD, 0, kr, sizeof(*kr));
42843509a12Srenato return (1);
42943509a12Srenato
43043509a12Srenato dont_redistribute:
43143509a12Srenato kr_redist_remove(kr);
43243509a12Srenato return (0);
43343509a12Srenato }
43443509a12Srenato
435ab786365Srenato static void
kr_redistribute(struct kroute_prefix * kp)43643509a12Srenato kr_redistribute(struct kroute_prefix *kp)
43743509a12Srenato {
43843509a12Srenato struct kroute_priority *kprio;
43943509a12Srenato struct kroute_node *kn;
44043509a12Srenato
44143509a12Srenato /* only the highest prio route can be redistributed */
44243509a12Srenato TAILQ_FOREACH_REVERSE(kprio, &kp->priorities, plist, entry) {
44343509a12Srenato if (kprio == TAILQ_FIRST(&kp->priorities)) {
44443509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry)
44543509a12Srenato /* pick just one entry in case of multipath */
44643509a12Srenato if (kr_redist_eval(&kn->r))
44743509a12Srenato break;
44843509a12Srenato } else {
44943509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry)
45043509a12Srenato kr_redist_remove(&kn->r);
45143509a12Srenato }
45243509a12Srenato }
45343509a12Srenato }
45443509a12Srenato
455ab786365Srenato static __inline int
kroute_compare(struct kroute_prefix * a,struct kroute_prefix * b)45643509a12Srenato kroute_compare(struct kroute_prefix *a, struct kroute_prefix *b)
45743509a12Srenato {
458e8918803Srenato int addrcmp;
459e8918803Srenato
46043509a12Srenato if (a->af < b->af)
46143509a12Srenato return (-1);
46243509a12Srenato if (a->af > b->af)
46343509a12Srenato return (1);
46443509a12Srenato
465e8918803Srenato addrcmp = eigrp_addrcmp(a->af, &a->prefix, &b->prefix);
466e8918803Srenato if (addrcmp != 0)
467e8918803Srenato return (addrcmp);
46843509a12Srenato
46943509a12Srenato if (a->prefixlen < b->prefixlen)
47043509a12Srenato return (-1);
47143509a12Srenato if (a->prefixlen > b->prefixlen)
47243509a12Srenato return (1);
47343509a12Srenato
47443509a12Srenato return (0);
47543509a12Srenato }
47643509a12Srenato
47743509a12Srenato /* tree management */
478ab786365Srenato static struct kroute_prefix *
kroute_find_prefix(int af,union eigrpd_addr * prefix,uint8_t prefixlen)47943509a12Srenato kroute_find_prefix(int af, union eigrpd_addr *prefix, uint8_t prefixlen)
48043509a12Srenato {
48143509a12Srenato struct kroute_prefix s;
48243509a12Srenato
48343509a12Srenato s.af = af;
4843eb03b29Srenato s.prefix = *prefix;
48543509a12Srenato s.prefixlen = prefixlen;
48643509a12Srenato
48743509a12Srenato return (RB_FIND(kroute_tree, &krt, &s));
48843509a12Srenato }
48943509a12Srenato
490ab786365Srenato static struct kroute_priority *
kroute_find_prio(struct kroute_prefix * kp,uint8_t prio)49143509a12Srenato kroute_find_prio(struct kroute_prefix *kp, uint8_t prio)
49243509a12Srenato {
49343509a12Srenato struct kroute_priority *kprio;
49443509a12Srenato
49543509a12Srenato /* RTP_ANY here picks the lowest priority node */
49643509a12Srenato if (prio == RTP_ANY)
49743509a12Srenato return (TAILQ_FIRST(&kp->priorities));
49843509a12Srenato
49943509a12Srenato TAILQ_FOREACH(kprio, &kp->priorities, entry)
50043509a12Srenato if (kprio->priority == prio)
50143509a12Srenato return (kprio);
50243509a12Srenato
50343509a12Srenato return (NULL);
50443509a12Srenato }
50543509a12Srenato
506ab786365Srenato static struct kroute_node *
kroute_find_gw(struct kroute_priority * kprio,union eigrpd_addr * nh)50743509a12Srenato kroute_find_gw(struct kroute_priority *kprio, union eigrpd_addr *nh)
50843509a12Srenato {
50943509a12Srenato struct kroute_node *kn;
51043509a12Srenato
51143509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry)
51243509a12Srenato if (eigrp_addrcmp(kprio->kp->af, &kn->r.nexthop, nh) == 0)
51343509a12Srenato return (kn);
51443509a12Srenato
51543509a12Srenato return (NULL);
51643509a12Srenato }
51743509a12Srenato
518ab786365Srenato static struct kroute_node *
kroute_insert(struct kroute * kr)51943509a12Srenato kroute_insert(struct kroute *kr)
52043509a12Srenato {
52143509a12Srenato struct kroute_prefix *kp;
52237212791Srenato struct kroute_priority *kprio, *tmp;
52343509a12Srenato struct kroute_node *kn;
52443509a12Srenato
52543509a12Srenato kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen);
52643509a12Srenato if (kp == NULL) {
52743509a12Srenato kp = calloc(1, sizeof((*kp)));
52843509a12Srenato if (kp == NULL)
52943509a12Srenato fatal("kroute_insert");
53043509a12Srenato kp->af = kr->af;
5313eb03b29Srenato kp->prefix = kr->prefix;
53243509a12Srenato kp->prefixlen = kr->prefixlen;
53343509a12Srenato TAILQ_INIT(&kp->priorities);
53443509a12Srenato RB_INSERT(kroute_tree, &krt, kp);
53543509a12Srenato }
53643509a12Srenato
53743509a12Srenato kprio = kroute_find_prio(kp, kr->priority);
53843509a12Srenato if (kprio == NULL) {
53943509a12Srenato kprio = calloc(1, sizeof(*kprio));
54043509a12Srenato if (kprio == NULL)
54143509a12Srenato fatal("kroute_insert");
54243509a12Srenato kprio->kp = kp;
54343509a12Srenato kprio->priority = kr->priority;
54443509a12Srenato TAILQ_INIT(&kprio->nexthops);
54543509a12Srenato
54643509a12Srenato /* lower priorities first */
54743509a12Srenato TAILQ_FOREACH(tmp, &kp->priorities, entry)
54843509a12Srenato if (tmp->priority > kprio->priority)
54943509a12Srenato break;
55043509a12Srenato if (tmp)
55143509a12Srenato TAILQ_INSERT_BEFORE(tmp, kprio, entry);
55243509a12Srenato else
55343509a12Srenato TAILQ_INSERT_TAIL(&kp->priorities, kprio, entry);
55443509a12Srenato }
55543509a12Srenato
55643509a12Srenato kn = kroute_find_gw(kprio, &kr->nexthop);
55743509a12Srenato if (kn == NULL) {
55843509a12Srenato kn = calloc(1, sizeof(*kn));
55943509a12Srenato if (kn == NULL)
56043509a12Srenato fatal("kroute_insert");
56143509a12Srenato kn->kprio = kprio;
5623eb03b29Srenato kn->r = *kr;
56343509a12Srenato TAILQ_INSERT_TAIL(&kprio->nexthops, kn, entry);
56443509a12Srenato }
56543509a12Srenato
56643509a12Srenato if (!(kr->flags & F_KERNEL)) {
56743509a12Srenato /* don't validate or redistribute eigrp route */
56843509a12Srenato kr->flags &= ~F_DOWN;
56943509a12Srenato return (kn);
57043509a12Srenato }
57143509a12Srenato
57243509a12Srenato if (kif_validate(kr->ifindex))
57343509a12Srenato kr->flags &= ~F_DOWN;
57443509a12Srenato else
57543509a12Srenato kr->flags |= F_DOWN;
57643509a12Srenato
57743509a12Srenato kr_redistribute(kp);
57843509a12Srenato return (kn);
57943509a12Srenato }
58043509a12Srenato
581ab786365Srenato static int
kroute_remove(struct kroute * kr)58243509a12Srenato kroute_remove(struct kroute *kr)
58343509a12Srenato {
58443509a12Srenato struct kroute_prefix *kp;
58543509a12Srenato struct kroute_priority *kprio;
58643509a12Srenato struct kroute_node *kn;
58743509a12Srenato
58843509a12Srenato kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen);
58943509a12Srenato if (kp == NULL)
59043509a12Srenato goto notfound;
59143509a12Srenato kprio = kroute_find_prio(kp, kr->priority);
59243509a12Srenato if (kprio == NULL)
59343509a12Srenato goto notfound;
59443509a12Srenato kn = kroute_find_gw(kprio, &kr->nexthop);
59543509a12Srenato if (kn == NULL)
59643509a12Srenato goto notfound;
59743509a12Srenato
59843509a12Srenato kr_redist_remove(&kn->r);
59943509a12Srenato
60043509a12Srenato TAILQ_REMOVE(&kprio->nexthops, kn, entry);
60143509a12Srenato free(kn);
60243509a12Srenato
60343509a12Srenato if (TAILQ_EMPTY(&kprio->nexthops)) {
60443509a12Srenato TAILQ_REMOVE(&kp->priorities, kprio, entry);
60543509a12Srenato free(kprio);
60643509a12Srenato }
60743509a12Srenato
60843509a12Srenato if (TAILQ_EMPTY(&kp->priorities)) {
60944faa115Srenato if (RB_REMOVE(kroute_tree, &krt, kp) == NULL) {
61043509a12Srenato log_warnx("%s failed for %s/%u", __func__,
61143509a12Srenato log_addr(kr->af, &kr->prefix), kp->prefixlen);
61244faa115Srenato return (-1);
61344faa115Srenato }
61443509a12Srenato free(kp);
61543509a12Srenato } else
61643509a12Srenato kr_redistribute(kp);
61743509a12Srenato
61843509a12Srenato return (0);
61943509a12Srenato
62043509a12Srenato notfound:
62143509a12Srenato log_warnx("%s failed to find %s/%u", __func__,
62243509a12Srenato log_addr(kr->af, &kr->prefix), kr->prefixlen);
62343509a12Srenato return (-1);
62443509a12Srenato }
62543509a12Srenato
626ab786365Srenato static void
kroute_clear(void)62743509a12Srenato kroute_clear(void)
62843509a12Srenato {
62943509a12Srenato struct kroute_prefix *kp;
63043509a12Srenato struct kroute_priority *kprio;
63143509a12Srenato struct kroute_node *kn;
63243509a12Srenato
63343509a12Srenato while ((kp = RB_MIN(kroute_tree, &krt)) != NULL) {
63443509a12Srenato while ((kprio = TAILQ_FIRST(&kp->priorities)) != NULL) {
63543509a12Srenato while ((kn = TAILQ_FIRST(&kprio->nexthops)) != NULL) {
63643509a12Srenato TAILQ_REMOVE(&kprio->nexthops, kn, entry);
63743509a12Srenato free(kn);
63843509a12Srenato }
63943509a12Srenato TAILQ_REMOVE(&kp->priorities, kprio, entry);
64043509a12Srenato free(kprio);
64143509a12Srenato }
64243509a12Srenato RB_REMOVE(kroute_tree, &krt, kp);
64343509a12Srenato free(kp);
64443509a12Srenato }
64543509a12Srenato }
64643509a12Srenato
647ab786365Srenato static __inline int
kif_compare(struct kif_node * a,struct kif_node * b)64843509a12Srenato kif_compare(struct kif_node *a, struct kif_node *b)
64943509a12Srenato {
65043509a12Srenato return (b->k.ifindex - a->k.ifindex);
65143509a12Srenato }
65243509a12Srenato
65343509a12Srenato /* tree management */
654ab786365Srenato static struct kif_node *
kif_find(unsigned short ifindex)65543509a12Srenato kif_find(unsigned short ifindex)
65643509a12Srenato {
65743509a12Srenato struct kif_node s;
65843509a12Srenato
65943509a12Srenato memset(&s, 0, sizeof(s));
66043509a12Srenato s.k.ifindex = ifindex;
66143509a12Srenato
66243509a12Srenato return (RB_FIND(kif_tree, &kit, &s));
66343509a12Srenato }
66443509a12Srenato
66543509a12Srenato struct kif *
kif_findname(char * ifname)66643509a12Srenato kif_findname(char *ifname)
66743509a12Srenato {
66843509a12Srenato struct kif_node *kif;
66943509a12Srenato
67043509a12Srenato RB_FOREACH(kif, kif_tree, &kit)
67143509a12Srenato if (!strcmp(ifname, kif->k.ifname))
67243509a12Srenato return (&kif->k);
67343509a12Srenato
67443509a12Srenato return (NULL);
67543509a12Srenato }
67643509a12Srenato
677ab786365Srenato static struct kif_node *
kif_insert(unsigned short ifindex)67843509a12Srenato kif_insert(unsigned short ifindex)
67943509a12Srenato {
68043509a12Srenato struct kif_node *kif;
68143509a12Srenato
68243509a12Srenato if ((kif = calloc(1, sizeof(struct kif_node))) == NULL)
68343509a12Srenato return (NULL);
68443509a12Srenato
68543509a12Srenato kif->k.ifindex = ifindex;
68643509a12Srenato TAILQ_INIT(&kif->addrs);
68743509a12Srenato
68843509a12Srenato if (RB_INSERT(kif_tree, &kit, kif) != NULL)
68943509a12Srenato fatalx("kif_insert: RB_INSERT");
69043509a12Srenato
69143509a12Srenato return (kif);
69243509a12Srenato }
69343509a12Srenato
694ab786365Srenato static int
kif_remove(struct kif_node * kif)69543509a12Srenato kif_remove(struct kif_node *kif)
69643509a12Srenato {
69743509a12Srenato struct kif_addr *ka;
69843509a12Srenato
69943509a12Srenato if (RB_REMOVE(kif_tree, &kit, kif) == NULL) {
7003a50f0a9Sjmc log_warnx("%s failed for interface %s", __func__, kif->k.ifname);
70143509a12Srenato return (-1);
70243509a12Srenato }
70343509a12Srenato
70443509a12Srenato while ((ka = TAILQ_FIRST(&kif->addrs)) != NULL) {
70543509a12Srenato TAILQ_REMOVE(&kif->addrs, ka, entry);
70643509a12Srenato free(ka);
70743509a12Srenato }
70843509a12Srenato free(kif);
70943509a12Srenato return (0);
71043509a12Srenato }
71143509a12Srenato
71243509a12Srenato void
kif_clear(void)71343509a12Srenato kif_clear(void)
71443509a12Srenato {
71543509a12Srenato struct kif_node *kif;
71643509a12Srenato
71743509a12Srenato while ((kif = RB_MIN(kif_tree, &kit)) != NULL)
71843509a12Srenato kif_remove(kif);
71943509a12Srenato }
72043509a12Srenato
721ab786365Srenato static struct kif *
kif_update(unsigned short ifindex,int flags,struct if_data * ifd,struct sockaddr_dl * sdl)72243509a12Srenato kif_update(unsigned short ifindex, int flags, struct if_data *ifd,
72343509a12Srenato struct sockaddr_dl *sdl)
72443509a12Srenato {
72543509a12Srenato struct kif_node *kif;
72643509a12Srenato
72743509a12Srenato if ((kif = kif_find(ifindex)) == NULL) {
72843509a12Srenato if ((kif = kif_insert(ifindex)) == NULL)
72943509a12Srenato return (NULL);
73043509a12Srenato kif->k.nh_reachable = (flags & IFF_UP) &&
73143509a12Srenato LINK_STATE_IS_UP(ifd->ifi_link_state);
73243509a12Srenato }
73343509a12Srenato
73443509a12Srenato kif->k.flags = flags;
73543509a12Srenato kif->k.link_state = ifd->ifi_link_state;
73643509a12Srenato kif->k.if_type = ifd->ifi_type;
73743509a12Srenato kif->k.baudrate = ifd->ifi_baudrate;
73843509a12Srenato kif->k.mtu = ifd->ifi_mtu;
73906c7f8f8Srenato kif->k.rdomain = ifd->ifi_rdomain;
74043509a12Srenato
74143509a12Srenato if (sdl && sdl->sdl_family == AF_LINK) {
74243509a12Srenato if (sdl->sdl_nlen >= sizeof(kif->k.ifname))
74343509a12Srenato memcpy(kif->k.ifname, sdl->sdl_data,
74443509a12Srenato sizeof(kif->k.ifname) - 1);
74543509a12Srenato else if (sdl->sdl_nlen > 0)
74643509a12Srenato memcpy(kif->k.ifname, sdl->sdl_data,
74743509a12Srenato sdl->sdl_nlen);
74843509a12Srenato /* string already terminated via calloc() */
74943509a12Srenato }
75043509a12Srenato
75143509a12Srenato return (&kif->k);
75243509a12Srenato }
75343509a12Srenato
754ab786365Srenato static int
kif_validate(unsigned short ifindex)75543509a12Srenato kif_validate(unsigned short ifindex)
75643509a12Srenato {
75743509a12Srenato struct kif_node *kif;
75843509a12Srenato
75943509a12Srenato if ((kif = kif_find(ifindex)) == NULL)
76043509a12Srenato return (0);
76143509a12Srenato
76243509a12Srenato return (kif->k.nh_reachable);
76343509a12Srenato }
76443509a12Srenato
76543509a12Srenato /* misc */
766ab786365Srenato static void
protect_lo(void)76743509a12Srenato protect_lo(void)
76843509a12Srenato {
76943509a12Srenato struct kroute kr4, kr6;
77043509a12Srenato
77143509a12Srenato /* special protection for 127/8 */
77243509a12Srenato memset(&kr4, 0, sizeof(kr4));
77343509a12Srenato kr4.af = AF_INET;
77443509a12Srenato kr4.prefix.v4.s_addr = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
77543509a12Srenato kr4.prefixlen = 8;
77643509a12Srenato kr4.flags = F_KERNEL|F_CONNECTED;
77743509a12Srenato kroute_insert(&kr4);
77843509a12Srenato
77943509a12Srenato /* special protection for ::1 */
78043509a12Srenato memset(&kr6, 0, sizeof(kr6));
78143509a12Srenato kr6.af = AF_INET6;
7823eb03b29Srenato kr6.prefix.v6 = in6addr_loopback;
78343509a12Srenato kr6.prefixlen = 128;
78443509a12Srenato kr6.flags = F_KERNEL|F_CONNECTED;
78543509a12Srenato kroute_insert(&kr6);
78643509a12Srenato }
78743509a12Srenato
78844faa115Srenato /* misc */
789ab786365Srenato static uint8_t
prefixlen_classful(in_addr_t ina)79043509a12Srenato prefixlen_classful(in_addr_t ina)
79143509a12Srenato {
79243509a12Srenato /* it hurt to write this. */
79343509a12Srenato
79443509a12Srenato if (ina >= 0xf0000000U) /* class E */
79543509a12Srenato return (32);
79643509a12Srenato else if (ina >= 0xe0000000U) /* class D */
79743509a12Srenato return (4);
79843509a12Srenato else if (ina >= 0xc0000000U) /* class C */
79943509a12Srenato return (24);
80043509a12Srenato else if (ina >= 0x80000000U) /* class B */
80143509a12Srenato return (16);
80243509a12Srenato else /* class A */
80343509a12Srenato return (8);
80443509a12Srenato }
80543509a12Srenato
80643509a12Srenato #define ROUNDUP(a) \
807064707deSfriehm ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
80843509a12Srenato
809ab786365Srenato static void
get_rtaddrs(int addrs,struct sockaddr * sa,struct sockaddr ** rti_info)81043509a12Srenato get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
81143509a12Srenato {
81243509a12Srenato int i;
81343509a12Srenato
81443509a12Srenato for (i = 0; i < RTAX_MAX; i++) {
81543509a12Srenato if (addrs & (1 << i)) {
81643509a12Srenato rti_info[i] = sa;
81743509a12Srenato sa = (struct sockaddr *)((char *)(sa) +
81843509a12Srenato ROUNDUP(sa->sa_len));
81943509a12Srenato } else
82043509a12Srenato rti_info[i] = NULL;
82143509a12Srenato }
82243509a12Srenato }
82343509a12Srenato
824ab786365Srenato static void
if_change(unsigned short ifindex,int flags,struct if_data * ifd,struct sockaddr_dl * sdl)82543509a12Srenato if_change(unsigned short ifindex, int flags, struct if_data *ifd,
82643509a12Srenato struct sockaddr_dl *sdl)
82743509a12Srenato {
82843509a12Srenato struct kroute_prefix *kp;
82943509a12Srenato struct kroute_priority *kprio;
83043509a12Srenato struct kroute_node *kn;
83143509a12Srenato struct kif *kif;
83243509a12Srenato uint8_t reachable;
83343509a12Srenato
83443509a12Srenato if ((kif = kif_update(ifindex, flags, ifd, sdl)) == NULL) {
83543509a12Srenato log_warn("%s: kif_update(%u)", __func__, ifindex);
83643509a12Srenato return;
83743509a12Srenato }
83843509a12Srenato
83943509a12Srenato reachable = (kif->flags & IFF_UP) &&
84043509a12Srenato LINK_STATE_IS_UP(kif->link_state);
84143509a12Srenato
84243509a12Srenato if (reachable == kif->nh_reachable)
84343509a12Srenato return; /* nothing changed wrt nexthop validity */
84443509a12Srenato
84543509a12Srenato kif->nh_reachable = reachable;
84643509a12Srenato
84743509a12Srenato /* notify eigrpe about link state */
84843509a12Srenato main_imsg_compose_eigrpe(IMSG_IFINFO, 0, kif, sizeof(struct kif));
84943509a12Srenato
85043509a12Srenato /* notify rde about link going down */
85143509a12Srenato if (!kif->nh_reachable)
85243509a12Srenato main_imsg_compose_rde(IMSG_IFDOWN, 0, kif, sizeof(struct kif));
85343509a12Srenato
85443509a12Srenato /* update redistribute list */
85543509a12Srenato RB_FOREACH(kp, kroute_tree, &krt) {
85643509a12Srenato TAILQ_FOREACH(kprio, &kp->priorities, entry) {
85743509a12Srenato TAILQ_FOREACH(kn, &kprio->nexthops, entry) {
85843509a12Srenato if (kn->r.ifindex != ifindex)
85943509a12Srenato continue;
86043509a12Srenato
86143509a12Srenato if (reachable)
86243509a12Srenato kn->r.flags &= ~F_DOWN;
86343509a12Srenato else
86443509a12Srenato kn->r.flags |= F_DOWN;
86543509a12Srenato }
86643509a12Srenato }
86743509a12Srenato kr_redistribute(kp);
86843509a12Srenato }
86943509a12Srenato }
87043509a12Srenato
871ab786365Srenato static void
if_newaddr(unsigned short ifindex,struct sockaddr * ifa,struct sockaddr * mask,struct sockaddr * brd)87243509a12Srenato if_newaddr(unsigned short ifindex, struct sockaddr *ifa, struct sockaddr *mask,
87343509a12Srenato struct sockaddr *brd)
87443509a12Srenato {
87543509a12Srenato struct kif_node *kif;
87643509a12Srenato struct sockaddr_in *ifa4, *mask4, *brd4;
87743509a12Srenato struct sockaddr_in6 *ifa6, *mask6, *brd6;
87843509a12Srenato struct kif_addr *ka;
87943509a12Srenato
88043509a12Srenato if (ifa == NULL)
88143509a12Srenato return;
88243509a12Srenato if ((kif = kif_find(ifindex)) == NULL) {
88343509a12Srenato log_warnx("%s: corresponding if %d not found", __func__,
88443509a12Srenato ifindex);
88543509a12Srenato return;
88643509a12Srenato }
88743509a12Srenato
88843509a12Srenato switch (ifa->sa_family) {
88943509a12Srenato case AF_INET:
89043509a12Srenato ifa4 = (struct sockaddr_in *) ifa;
89143509a12Srenato mask4 = (struct sockaddr_in *) mask;
89243509a12Srenato brd4 = (struct sockaddr_in *) brd;
89343509a12Srenato
89443509a12Srenato /* filter out unwanted addresses */
8956c428d2eSrenato if (bad_addr_v4(ifa4->sin_addr))
89643509a12Srenato return;
89743509a12Srenato
89843509a12Srenato if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL)
89943509a12Srenato fatal("if_newaddr");
900bce3b5b3Srenato ka->a.addr.v4 = ifa4->sin_addr;
90143509a12Srenato if (mask4)
90243509a12Srenato ka->a.prefixlen =
90343509a12Srenato mask2prefixlen(mask4->sin_addr.s_addr);
90443509a12Srenato if (brd4)
905bce3b5b3Srenato ka->a.dstbrd.v4 = brd4->sin_addr;
90643509a12Srenato break;
90743509a12Srenato case AF_INET6:
90843509a12Srenato ifa6 = (struct sockaddr_in6 *) ifa;
90943509a12Srenato mask6 = (struct sockaddr_in6 *) mask;
91043509a12Srenato brd6 = (struct sockaddr_in6 *) brd;
91143509a12Srenato
91243509a12Srenato /* We only care about link-local and global-scope. */
9136c428d2eSrenato if (bad_addr_v6(&ifa6->sin6_addr))
91443509a12Srenato return;
91543509a12Srenato
91643509a12Srenato clearscope(&ifa6->sin6_addr);
91743509a12Srenato
91843509a12Srenato if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL)
91943509a12Srenato fatal("if_newaddr");
92043509a12Srenato ka->a.addr.v6 = ifa6->sin6_addr;
92143509a12Srenato if (mask6)
92243509a12Srenato ka->a.prefixlen = mask2prefixlen6(mask6);
92343509a12Srenato if (brd6)
92443509a12Srenato ka->a.dstbrd.v6 = brd6->sin6_addr;
92543509a12Srenato break;
92643509a12Srenato default:
92743509a12Srenato return;
92843509a12Srenato }
92943509a12Srenato
93043509a12Srenato ka->a.ifindex = ifindex;
93143509a12Srenato ka->a.af = ifa->sa_family;
93243509a12Srenato TAILQ_INSERT_TAIL(&kif->addrs, ka, entry);
93343509a12Srenato
93443509a12Srenato /* notify eigrpe about new address */
9352e046f62Srenato main_imsg_compose_eigrpe(IMSG_NEWADDR, 0, &ka->a, sizeof(ka->a));
93643509a12Srenato }
93743509a12Srenato
938ab786365Srenato static void
if_deladdr(unsigned short ifindex,struct sockaddr * ifa,struct sockaddr * mask,struct sockaddr * brd)93943509a12Srenato if_deladdr(unsigned short ifindex, struct sockaddr *ifa, struct sockaddr *mask,
94043509a12Srenato struct sockaddr *brd)
94143509a12Srenato {
94243509a12Srenato struct kif_node *kif;
94343509a12Srenato struct sockaddr_in *ifa4, *mask4, *brd4;
94443509a12Srenato struct sockaddr_in6 *ifa6, *mask6, *brd6;
94543509a12Srenato struct kaddr k;
94643509a12Srenato struct kif_addr *ka, *nka;
94743509a12Srenato
94843509a12Srenato if (ifa == NULL)
94943509a12Srenato return;
95043509a12Srenato if ((kif = kif_find(ifindex)) == NULL) {
95143509a12Srenato log_warnx("%s: corresponding if %d not found", __func__,
95243509a12Srenato ifindex);
95343509a12Srenato return;
95443509a12Srenato }
95543509a12Srenato
95643509a12Srenato memset(&k, 0, sizeof(k));
95743509a12Srenato k.af = ifa->sa_family;
95843509a12Srenato switch (ifa->sa_family) {
95943509a12Srenato case AF_INET:
96043509a12Srenato ifa4 = (struct sockaddr_in *) ifa;
96143509a12Srenato mask4 = (struct sockaddr_in *) mask;
96243509a12Srenato brd4 = (struct sockaddr_in *) brd;
96343509a12Srenato
9646c428d2eSrenato /* filter out unwanted addresses */
9656c428d2eSrenato if (bad_addr_v4(ifa4->sin_addr))
9666c428d2eSrenato return;
9676c428d2eSrenato
968bce3b5b3Srenato k.addr.v4 = ifa4->sin_addr;
96943509a12Srenato if (mask4)
97043509a12Srenato k.prefixlen = mask2prefixlen(mask4->sin_addr.s_addr);
97143509a12Srenato if (brd4)
972bce3b5b3Srenato k.dstbrd.v4 = brd4->sin_addr;
97343509a12Srenato break;
97443509a12Srenato case AF_INET6:
97543509a12Srenato ifa6 = (struct sockaddr_in6 *) ifa;
97643509a12Srenato mask6 = (struct sockaddr_in6 *) mask;
97743509a12Srenato brd6 = (struct sockaddr_in6 *) brd;
97843509a12Srenato
97943509a12Srenato /* We only care about link-local and global-scope. */
9806c428d2eSrenato if (bad_addr_v6(&ifa6->sin6_addr))
98143509a12Srenato return;
98243509a12Srenato
98343509a12Srenato clearscope(&ifa6->sin6_addr);
98443509a12Srenato
98543509a12Srenato k.addr.v6 = ifa6->sin6_addr;
98643509a12Srenato if (mask6)
98743509a12Srenato k.prefixlen = mask2prefixlen6(mask6);
98843509a12Srenato if (brd6)
98943509a12Srenato k.dstbrd.v6 = brd6->sin6_addr;
99043509a12Srenato break;
99143509a12Srenato default:
99243509a12Srenato return;
99343509a12Srenato }
99443509a12Srenato
99543509a12Srenato for (ka = TAILQ_FIRST(&kif->addrs); ka != NULL; ka = nka) {
99643509a12Srenato nka = TAILQ_NEXT(ka, entry);
99743509a12Srenato
99843509a12Srenato if (ka->a.af != k.af ||
999bce3b5b3Srenato ka->a.prefixlen != k.prefixlen ||
1000bce3b5b3Srenato eigrp_addrcmp(ka->a.af, &ka->a.addr, &k.addr) ||
1001bce3b5b3Srenato eigrp_addrcmp(ka->a.af, &ka->a.dstbrd, &k.dstbrd))
100243509a12Srenato continue;
100343509a12Srenato
100443509a12Srenato /* notify eigrpe about removed address */
100543509a12Srenato main_imsg_compose_eigrpe(IMSG_DELADDR, 0, &ka->a,
10062e046f62Srenato sizeof(ka->a));
100743509a12Srenato TAILQ_REMOVE(&kif->addrs, ka, entry);
100843509a12Srenato free(ka);
100943509a12Srenato return;
101043509a12Srenato }
101143509a12Srenato }
101243509a12Srenato
1013ab786365Srenato static void
if_announce(void * msg)101443509a12Srenato if_announce(void *msg)
101543509a12Srenato {
101643509a12Srenato struct if_announcemsghdr *ifan;
101743509a12Srenato struct kif_node *kif;
101843509a12Srenato
101943509a12Srenato ifan = msg;
102043509a12Srenato
102143509a12Srenato switch (ifan->ifan_what) {
102243509a12Srenato case IFAN_ARRIVAL:
102343509a12Srenato kif = kif_insert(ifan->ifan_index);
102443509a12Srenato if (kif)
102543509a12Srenato strlcpy(kif->k.ifname, ifan->ifan_name,
102643509a12Srenato sizeof(kif->k.ifname));
102743509a12Srenato break;
102843509a12Srenato case IFAN_DEPARTURE:
102943509a12Srenato kif = kif_find(ifan->ifan_index);
103043509a12Srenato if (kif)
103143509a12Srenato kif_remove(kif);
103243509a12Srenato break;
103343509a12Srenato }
103443509a12Srenato }
103543509a12Srenato
103643509a12Srenato /* rtsock */
103743509a12Srenato static int
send_rtmsg_v4(int fd,int action,struct kroute * kr)103843509a12Srenato send_rtmsg_v4(int fd, int action, struct kroute *kr)
103943509a12Srenato {
104043509a12Srenato struct iovec iov[5];
104143509a12Srenato struct rt_msghdr hdr;
104243509a12Srenato struct sockaddr_in prefix;
104343509a12Srenato struct sockaddr_in nexthop;
104443509a12Srenato struct sockaddr_in mask;
104543509a12Srenato int iovcnt = 0;
104643509a12Srenato
104743509a12Srenato if (kr_state.fib_sync == 0)
104843509a12Srenato return (0);
104943509a12Srenato
105043509a12Srenato /* initialize header */
105143509a12Srenato memset(&hdr, 0, sizeof(hdr));
105243509a12Srenato hdr.rtm_version = RTM_VERSION;
105343509a12Srenato hdr.rtm_type = action;
105443509a12Srenato hdr.rtm_priority = kr->priority;
105543509a12Srenato hdr.rtm_tableid = kr_state.rdomain; /* rtableid */
105643509a12Srenato if (action == RTM_CHANGE)
105743509a12Srenato hdr.rtm_fmask = RTF_REJECT|RTF_BLACKHOLE;
105843509a12Srenato else
105943509a12Srenato hdr.rtm_flags = RTF_MPATH;
10607c245af9Srenato if (kr->flags & F_BLACKHOLE)
10617c245af9Srenato hdr.rtm_flags |= RTF_BLACKHOLE;
106243509a12Srenato hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */
106343509a12Srenato hdr.rtm_msglen = sizeof(hdr);
106443509a12Srenato /* adjust iovec */
106543509a12Srenato iov[iovcnt].iov_base = &hdr;
106643509a12Srenato iov[iovcnt++].iov_len = sizeof(hdr);
106743509a12Srenato
106843509a12Srenato memset(&prefix, 0, sizeof(prefix));
106943509a12Srenato prefix.sin_len = sizeof(prefix);
107043509a12Srenato prefix.sin_family = AF_INET;
1071bce3b5b3Srenato prefix.sin_addr = kr->prefix.v4;
107243509a12Srenato /* adjust header */
107343509a12Srenato hdr.rtm_addrs |= RTA_DST;
107443509a12Srenato hdr.rtm_msglen += sizeof(prefix);
107543509a12Srenato /* adjust iovec */
107643509a12Srenato iov[iovcnt].iov_base = &prefix;
107743509a12Srenato iov[iovcnt++].iov_len = sizeof(prefix);
107843509a12Srenato
107943509a12Srenato if (kr->nexthop.v4.s_addr != 0) {
108043509a12Srenato memset(&nexthop, 0, sizeof(nexthop));
108143509a12Srenato nexthop.sin_len = sizeof(nexthop);
108243509a12Srenato nexthop.sin_family = AF_INET;
1083bce3b5b3Srenato nexthop.sin_addr = kr->nexthop.v4;
108443509a12Srenato /* adjust header */
108543509a12Srenato hdr.rtm_flags |= RTF_GATEWAY;
108643509a12Srenato hdr.rtm_addrs |= RTA_GATEWAY;
108743509a12Srenato hdr.rtm_msglen += sizeof(nexthop);
108843509a12Srenato /* adjust iovec */
108943509a12Srenato iov[iovcnt].iov_base = &nexthop;
109043509a12Srenato iov[iovcnt++].iov_len = sizeof(nexthop);
109143509a12Srenato }
109243509a12Srenato
109343509a12Srenato memset(&mask, 0, sizeof(mask));
109443509a12Srenato mask.sin_len = sizeof(mask);
109543509a12Srenato mask.sin_family = AF_INET;
109643509a12Srenato mask.sin_addr.s_addr = prefixlen2mask(kr->prefixlen);
109743509a12Srenato /* adjust header */
109843509a12Srenato hdr.rtm_addrs |= RTA_NETMASK;
109943509a12Srenato hdr.rtm_msglen += sizeof(mask);
110043509a12Srenato /* adjust iovec */
110143509a12Srenato iov[iovcnt].iov_base = &mask;
110243509a12Srenato iov[iovcnt++].iov_len = sizeof(mask);
110343509a12Srenato
110443509a12Srenato retry:
110543509a12Srenato if (writev(fd, iov, iovcnt) == -1) {
110643509a12Srenato if (errno == ESRCH) {
110743509a12Srenato if (hdr.rtm_type == RTM_CHANGE) {
110843509a12Srenato hdr.rtm_type = RTM_ADD;
110943509a12Srenato goto retry;
111043509a12Srenato } else if (hdr.rtm_type == RTM_DELETE) {
111143509a12Srenato log_info("route %s/%u vanished before delete",
111243509a12Srenato inet_ntoa(kr->prefix.v4),
111343509a12Srenato kr->prefixlen);
111443509a12Srenato return (0);
111543509a12Srenato }
111643509a12Srenato }
111743509a12Srenato log_warn("%s: action %u, prefix %s/%u", __func__, hdr.rtm_type,
111843509a12Srenato inet_ntoa(kr->prefix.v4), kr->prefixlen);
111943509a12Srenato return (0);
112043509a12Srenato }
112143509a12Srenato
112243509a12Srenato return (0);
112343509a12Srenato }
112443509a12Srenato
112543509a12Srenato static int
send_rtmsg_v6(int fd,int action,struct kroute * kr)112643509a12Srenato send_rtmsg_v6(int fd, int action, struct kroute *kr)
112743509a12Srenato {
112843509a12Srenato struct iovec iov[5];
112943509a12Srenato struct rt_msghdr hdr;
113043509a12Srenato struct pad {
113143509a12Srenato struct sockaddr_in6 addr;
113243509a12Srenato char pad[sizeof(long)]; /* thank you IPv6 */
113343509a12Srenato } prefix, nexthop, mask;
113443509a12Srenato int iovcnt = 0;
113543509a12Srenato
113643509a12Srenato if (kr_state.fib_sync == 0)
113743509a12Srenato return (0);
113843509a12Srenato
113943509a12Srenato /* initialize header */
114043509a12Srenato memset(&hdr, 0, sizeof(hdr));
114143509a12Srenato hdr.rtm_version = RTM_VERSION;
114243509a12Srenato hdr.rtm_type = action;
114343509a12Srenato hdr.rtm_priority = kr->priority;
114443509a12Srenato hdr.rtm_tableid = kr_state.rdomain; /* rtableid */
114543509a12Srenato if (action == RTM_CHANGE)
114643509a12Srenato hdr.rtm_fmask = RTF_REJECT|RTF_BLACKHOLE;
114743509a12Srenato else
114843509a12Srenato hdr.rtm_flags = RTF_MPATH;
114943509a12Srenato hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */
115043509a12Srenato hdr.rtm_msglen = sizeof(hdr);
115143509a12Srenato /* adjust iovec */
115243509a12Srenato iov[iovcnt].iov_base = &hdr;
115343509a12Srenato iov[iovcnt++].iov_len = sizeof(hdr);
115443509a12Srenato
115543509a12Srenato memset(&prefix, 0, sizeof(prefix));
115643509a12Srenato prefix.addr.sin6_len = sizeof(struct sockaddr_in6);
115743509a12Srenato prefix.addr.sin6_family = AF_INET6;
115843509a12Srenato prefix.addr.sin6_addr = kr->prefix.v6;
115943509a12Srenato /* adjust header */
116043509a12Srenato hdr.rtm_addrs |= RTA_DST;
116143509a12Srenato hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6));
116243509a12Srenato /* adjust iovec */
116343509a12Srenato iov[iovcnt].iov_base = &prefix;
116443509a12Srenato iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
116543509a12Srenato
116643509a12Srenato if (!IN6_IS_ADDR_UNSPECIFIED(&kr->nexthop.v6)) {
116743509a12Srenato memset(&nexthop, 0, sizeof(nexthop));
116843509a12Srenato nexthop.addr.sin6_len = sizeof(struct sockaddr_in6);
116943509a12Srenato nexthop.addr.sin6_family = AF_INET6;
117043509a12Srenato nexthop.addr.sin6_addr = kr->nexthop.v6;
117143509a12Srenato nexthop.addr.sin6_scope_id = kr->ifindex;
117243509a12Srenato embedscope(&nexthop.addr);
117343509a12Srenato
117443509a12Srenato /* adjust header */
117543509a12Srenato hdr.rtm_flags |= RTF_GATEWAY;
117643509a12Srenato hdr.rtm_addrs |= RTA_GATEWAY;
117743509a12Srenato hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6));
117843509a12Srenato /* adjust iovec */
117943509a12Srenato iov[iovcnt].iov_base = &nexthop;
118043509a12Srenato iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
118143509a12Srenato }
118243509a12Srenato
118343509a12Srenato memset(&mask, 0, sizeof(mask));
118443509a12Srenato mask.addr.sin6_len = sizeof(struct sockaddr_in6);
118543509a12Srenato mask.addr.sin6_family = AF_INET6;
118643509a12Srenato mask.addr.sin6_addr = *prefixlen2mask6(kr->prefixlen);
118743509a12Srenato /* adjust header */
118843509a12Srenato if (kr->prefixlen == 128)
118943509a12Srenato hdr.rtm_flags |= RTF_HOST;
119043509a12Srenato hdr.rtm_addrs |= RTA_NETMASK;
119143509a12Srenato hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6));
119243509a12Srenato /* adjust iovec */
119343509a12Srenato iov[iovcnt].iov_base = &mask;
119443509a12Srenato iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
119543509a12Srenato
119643509a12Srenato retry:
119743509a12Srenato if (writev(fd, iov, iovcnt) == -1) {
119843509a12Srenato if (errno == ESRCH) {
119943509a12Srenato if (hdr.rtm_type == RTM_CHANGE) {
120043509a12Srenato hdr.rtm_type = RTM_ADD;
120143509a12Srenato goto retry;
120243509a12Srenato } else if (hdr.rtm_type == RTM_DELETE) {
120343509a12Srenato log_info("route %s/%u vanished before delete",
120443509a12Srenato log_in6addr(&kr->prefix.v6),
120543509a12Srenato kr->prefixlen);
120643509a12Srenato return (0);
120743509a12Srenato }
120843509a12Srenato }
120943509a12Srenato log_warn("%s: action %u, prefix %s/%u", __func__, hdr.rtm_type,
121043509a12Srenato log_in6addr(&kr->prefix.v6), kr->prefixlen);
121143509a12Srenato return (0);
121243509a12Srenato }
121343509a12Srenato
121443509a12Srenato return (0);
121543509a12Srenato }
121643509a12Srenato
1217ab786365Srenato static int
send_rtmsg(int fd,int action,struct kroute * kr)121843509a12Srenato send_rtmsg(int fd, int action, struct kroute *kr)
121943509a12Srenato {
122043509a12Srenato switch (kr->af) {
122143509a12Srenato case AF_INET:
122243509a12Srenato return (send_rtmsg_v4(fd, action, kr));
122343509a12Srenato case AF_INET6:
122443509a12Srenato return (send_rtmsg_v6(fd, action, kr));
122543509a12Srenato default:
122643509a12Srenato break;
122743509a12Srenato }
122843509a12Srenato
122943509a12Srenato return (-1);
123043509a12Srenato }
123143509a12Srenato
1232ab786365Srenato static int
fetchtable(void)123343509a12Srenato fetchtable(void)
123443509a12Srenato {
123543509a12Srenato size_t len;
123643509a12Srenato int mib[7];
123743509a12Srenato char *buf;
123843509a12Srenato int rv;
123943509a12Srenato
124043509a12Srenato mib[0] = CTL_NET;
124143509a12Srenato mib[1] = PF_ROUTE;
124243509a12Srenato mib[2] = 0;
124343509a12Srenato mib[3] = 0;
124443509a12Srenato mib[4] = NET_RT_DUMP;
124543509a12Srenato mib[5] = 0;
124643509a12Srenato mib[6] = kr_state.rdomain; /* rtableid */
124743509a12Srenato
124843509a12Srenato if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) {
124943509a12Srenato log_warn("sysctl");
125043509a12Srenato return (-1);
125143509a12Srenato }
125243509a12Srenato if ((buf = malloc(len)) == NULL) {
125344faa115Srenato log_warn("%s", __func__);
125443509a12Srenato return (-1);
125543509a12Srenato }
125643509a12Srenato if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
125743509a12Srenato log_warn("sysctl");
125843509a12Srenato free(buf);
125943509a12Srenato return (-1);
126043509a12Srenato }
126143509a12Srenato
126243509a12Srenato rv = rtmsg_process(buf, len);
126343509a12Srenato free(buf);
126443509a12Srenato
126543509a12Srenato return (rv);
126643509a12Srenato }
126743509a12Srenato
1268ab786365Srenato static int
fetchifs(void)126943509a12Srenato fetchifs(void)
127043509a12Srenato {
127143509a12Srenato size_t len;
127243509a12Srenato int mib[6];
127343509a12Srenato char *buf;
127443509a12Srenato int rv;
127543509a12Srenato
127643509a12Srenato mib[0] = CTL_NET;
127743509a12Srenato mib[1] = PF_ROUTE;
127843509a12Srenato mib[2] = 0;
127943509a12Srenato mib[3] = 0; /* wildcard */
128043509a12Srenato mib[4] = NET_RT_IFLIST;
128143509a12Srenato mib[5] = 0;
128243509a12Srenato
128343509a12Srenato if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
128443509a12Srenato log_warn("sysctl");
128543509a12Srenato return (-1);
128643509a12Srenato }
128743509a12Srenato if ((buf = malloc(len)) == NULL) {
128844faa115Srenato log_warn("%s", __func__);
128943509a12Srenato return (-1);
129043509a12Srenato }
129143509a12Srenato if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
129243509a12Srenato log_warn("sysctl");
129343509a12Srenato free(buf);
129443509a12Srenato return (-1);
129543509a12Srenato }
129643509a12Srenato
129743509a12Srenato rv = rtmsg_process(buf, len);
129843509a12Srenato free(buf);
129943509a12Srenato
130043509a12Srenato return (rv);
130143509a12Srenato }
130243509a12Srenato
1303ab786365Srenato static int
dispatch_rtmsg(void)130443509a12Srenato dispatch_rtmsg(void)
130543509a12Srenato {
130643509a12Srenato char buf[RT_BUF_SIZE];
130743509a12Srenato ssize_t n;
130843509a12Srenato
130943509a12Srenato if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) {
131043509a12Srenato if (errno == EAGAIN || errno == EINTR)
131143509a12Srenato return (0);
131243509a12Srenato log_warn("%s: read error", __func__);
131343509a12Srenato return (-1);
131443509a12Srenato }
131543509a12Srenato
131643509a12Srenato if (n == 0) {
131743509a12Srenato log_warnx("routing socket closed");
131843509a12Srenato return (-1);
131943509a12Srenato }
132043509a12Srenato
132143509a12Srenato return (rtmsg_process(buf, n));
132243509a12Srenato }
132343509a12Srenato
1324ab786365Srenato static int
rtmsg_process(char * buf,size_t len)132543509a12Srenato rtmsg_process(char *buf, size_t len)
132643509a12Srenato {
132743509a12Srenato struct rt_msghdr *rtm;
132843509a12Srenato struct if_msghdr ifm;
132943509a12Srenato struct ifa_msghdr *ifam;
133043509a12Srenato struct sockaddr *sa, *rti_info[RTAX_MAX];
133143509a12Srenato size_t offset;
133243509a12Srenato char *next;
133343509a12Srenato
133443509a12Srenato for (offset = 0; offset < len; offset += rtm->rtm_msglen) {
133543509a12Srenato next = buf + offset;
133643509a12Srenato rtm = (struct rt_msghdr *)next;
133743509a12Srenato if (len < offset + sizeof(unsigned short) ||
133843509a12Srenato len < offset + rtm->rtm_msglen)
133943509a12Srenato fatalx("rtmsg_process: partial rtm in buffer");
134043509a12Srenato if (rtm->rtm_version != RTM_VERSION)
134143509a12Srenato continue;
134243509a12Srenato
134343509a12Srenato sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
134443509a12Srenato get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
134543509a12Srenato
134643509a12Srenato switch (rtm->rtm_type) {
134743509a12Srenato case RTM_ADD:
134843509a12Srenato case RTM_GET:
134943509a12Srenato case RTM_CHANGE:
135043509a12Srenato case RTM_DELETE:
135143509a12Srenato if (rtm->rtm_errno) /* failed attempts... */
135243509a12Srenato continue;
135343509a12Srenato
135443509a12Srenato if (rtm->rtm_tableid != kr_state.rdomain)
135543509a12Srenato continue;
135643509a12Srenato
1357e2a2cc33Srenato if (rtm->rtm_type == RTM_GET &&
1358e2a2cc33Srenato rtm->rtm_pid != kr_state.pid)
1359e2a2cc33Srenato continue;
1360e2a2cc33Srenato
136143509a12Srenato /* Skip ARP/ND cache and broadcast routes. */
136243509a12Srenato if (rtm->rtm_flags & (RTF_LLINFO|RTF_BROADCAST))
136343509a12Srenato continue;
136443509a12Srenato
136543509a12Srenato if (rtmsg_process_route(rtm, rti_info) == -1)
136643509a12Srenato return (-1);
136743509a12Srenato }
136843509a12Srenato
136943509a12Srenato switch (rtm->rtm_type) {
137043509a12Srenato case RTM_IFINFO:
137143509a12Srenato memcpy(&ifm, next, sizeof(ifm));
137243509a12Srenato if_change(ifm.ifm_index, ifm.ifm_flags, &ifm.ifm_data,
137343509a12Srenato (struct sockaddr_dl *)rti_info[RTAX_IFP]);
137443509a12Srenato break;
137543509a12Srenato case RTM_NEWADDR:
137643509a12Srenato ifam = (struct ifa_msghdr *)rtm;
137743509a12Srenato if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
137843509a12Srenato RTA_BRD)) == 0)
137943509a12Srenato break;
138043509a12Srenato
138143509a12Srenato if_newaddr(ifam->ifam_index,
138243509a12Srenato (struct sockaddr *)rti_info[RTAX_IFA],
138343509a12Srenato (struct sockaddr *)rti_info[RTAX_NETMASK],
138443509a12Srenato (struct sockaddr *)rti_info[RTAX_BRD]);
138543509a12Srenato break;
138643509a12Srenato case RTM_DELADDR:
138743509a12Srenato ifam = (struct ifa_msghdr *)rtm;
138843509a12Srenato if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
138943509a12Srenato RTA_BRD)) == 0)
139043509a12Srenato break;
139143509a12Srenato
139243509a12Srenato if_deladdr(ifam->ifam_index,
139343509a12Srenato (struct sockaddr *)rti_info[RTAX_IFA],
139443509a12Srenato (struct sockaddr *)rti_info[RTAX_NETMASK],
139543509a12Srenato (struct sockaddr *)rti_info[RTAX_BRD]);
139643509a12Srenato break;
139743509a12Srenato case RTM_IFANNOUNCE:
139843509a12Srenato if_announce(next);
139943509a12Srenato break;
140043509a12Srenato default:
140143509a12Srenato /* ignore for now */
140243509a12Srenato break;
140343509a12Srenato }
140443509a12Srenato }
140543509a12Srenato
140643509a12Srenato return (offset);
140743509a12Srenato }
140843509a12Srenato
1409ab786365Srenato static int
rtmsg_process_route(struct rt_msghdr * rtm,struct sockaddr * rti_info[RTAX_MAX])141043509a12Srenato rtmsg_process_route(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX])
141143509a12Srenato {
141243509a12Srenato struct sockaddr *sa;
141343509a12Srenato struct sockaddr_in *sa_in;
141443509a12Srenato struct sockaddr_in6 *sa_in6;
141543509a12Srenato struct kroute kr;
141643509a12Srenato struct kroute_prefix *kp;
141743509a12Srenato struct kroute_priority *kprio;
141843509a12Srenato struct kroute_node *kn;
141943509a12Srenato
142043509a12Srenato if ((sa = rti_info[RTAX_DST]) == NULL)
142143509a12Srenato return (-1);
142243509a12Srenato
142343509a12Srenato memset(&kr, 0, sizeof(kr));
142443509a12Srenato kr.af = sa->sa_family;
142543509a12Srenato switch (kr.af) {
142643509a12Srenato case AF_INET:
1427bce3b5b3Srenato kr.prefix.v4 = ((struct sockaddr_in *)sa)->sin_addr;
142843509a12Srenato sa_in = (struct sockaddr_in *) rti_info[RTAX_NETMASK];
142943509a12Srenato if (sa_in != NULL && sa_in->sin_len != 0)
143043509a12Srenato kr.prefixlen = mask2prefixlen(sa_in->sin_addr.s_addr);
143143509a12Srenato else if (rtm->rtm_flags & RTF_HOST)
143243509a12Srenato kr.prefixlen = 32;
143343509a12Srenato else if (kr.prefix.v4.s_addr == INADDR_ANY)
143443509a12Srenato kr.prefixlen = 0;
143543509a12Srenato else
143643509a12Srenato kr.prefixlen = prefixlen_classful(kr.prefix.v4.s_addr);
143743509a12Srenato break;
143843509a12Srenato case AF_INET6:
14393eb03b29Srenato kr.prefix.v6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
144043509a12Srenato sa_in6 = (struct sockaddr_in6 *)rti_info[RTAX_NETMASK];
144143509a12Srenato if (sa_in6 != NULL && sa_in6->sin6_len != 0)
144243509a12Srenato kr.prefixlen = mask2prefixlen6(sa_in6);
144343509a12Srenato else if (rtm->rtm_flags & RTF_HOST)
144443509a12Srenato kr.prefixlen = 128;
144543509a12Srenato else if (IN6_IS_ADDR_UNSPECIFIED(&kr.prefix.v6))
144643509a12Srenato kr.prefixlen = 0;
144743509a12Srenato else
144843509a12Srenato fatalx("in6 net addr without netmask");
144943509a12Srenato break;
145043509a12Srenato default:
145143509a12Srenato return (0);
145243509a12Srenato }
145343509a12Srenato kr.ifindex = rtm->rtm_index;
145443509a12Srenato if ((sa = rti_info[RTAX_GATEWAY]) != NULL) {
145543509a12Srenato switch (sa->sa_family) {
145643509a12Srenato case AF_INET:
1457bce3b5b3Srenato kr.nexthop.v4 = ((struct sockaddr_in *)sa)->sin_addr;
145843509a12Srenato break;
145943509a12Srenato case AF_INET6:
146043509a12Srenato sa_in6 = (struct sockaddr_in6 *)sa;
146143509a12Srenato recoverscope(sa_in6);
146243509a12Srenato kr.nexthop.v6 = sa_in6->sin6_addr;
146343509a12Srenato if (sa_in6->sin6_scope_id)
146443509a12Srenato kr.ifindex = sa_in6->sin6_scope_id;
146543509a12Srenato break;
146643509a12Srenato case AF_LINK:
146743509a12Srenato kr.flags |= F_CONNECTED;
146843509a12Srenato break;
146943509a12Srenato }
147043509a12Srenato }
147143509a12Srenato kr.flags |= F_KERNEL;
147243509a12Srenato if (rtm->rtm_flags & RTF_STATIC)
147343509a12Srenato kr.flags |= F_STATIC;
147443509a12Srenato if (rtm->rtm_flags & RTF_BLACKHOLE)
147543509a12Srenato kr.flags |= F_BLACKHOLE;
147643509a12Srenato if (rtm->rtm_flags & RTF_REJECT)
147743509a12Srenato kr.flags |= F_REJECT;
147843509a12Srenato if (rtm->rtm_flags & RTF_DYNAMIC)
147943509a12Srenato kr.flags |= F_DYNAMIC;
148044faa115Srenato if (rtm->rtm_flags & RTF_CONNECTED)
148144faa115Srenato kr.flags |= F_CONNECTED;
148243509a12Srenato kr.priority = rtm->rtm_priority;
148343509a12Srenato
148443509a12Srenato if (rtm->rtm_type == RTM_CHANGE) {
148543509a12Srenato /*
148643509a12Srenato * The kernel doesn't allow RTM_CHANGE for multipath routes.
148743509a12Srenato * If we got this message we know that the route has only one
148843509a12Srenato * nexthop and we should remove it before installing the same
148943509a12Srenato * route with the new nexthop.
149043509a12Srenato */
149143509a12Srenato kp = kroute_find_prefix(kr.af, &kr.prefix, kr.prefixlen);
149243509a12Srenato if (kp) {
149343509a12Srenato kprio = kroute_find_prio(kp, kr.priority);
149443509a12Srenato if (kprio) {
149543509a12Srenato kn = TAILQ_FIRST(&kprio->nexthops);
14964f0b2092Srenato if (kn)
14974f0b2092Srenato kroute_remove(&kn->r);
149843509a12Srenato }
149943509a12Srenato }
150043509a12Srenato }
150143509a12Srenato
150243509a12Srenato kn = NULL;
150343509a12Srenato kp = kroute_find_prefix(kr.af, &kr.prefix, kr.prefixlen);
150443509a12Srenato if (kp) {
150543509a12Srenato kprio = kroute_find_prio(kp, kr.priority);
150643509a12Srenato if (kprio)
150743509a12Srenato kn = kroute_find_gw(kprio, &kr.nexthop);
150843509a12Srenato }
150943509a12Srenato
151043509a12Srenato if (rtm->rtm_type == RTM_DELETE) {
151143509a12Srenato if (kn == NULL || !(kn->r.flags & F_KERNEL))
151243509a12Srenato return (0);
151343509a12Srenato return (kroute_remove(&kr));
151443509a12Srenato }
151543509a12Srenato
151643509a12Srenato if (!eigrp_addrisset(kr.af, &kr.nexthop) && !(kr.flags & F_CONNECTED)) {
151743509a12Srenato log_warnx("%s: no nexthop for %s/%u", __func__,
151843509a12Srenato log_addr(kr.af, &kr.prefix), kr.prefixlen);
151943509a12Srenato return (-1);
152043509a12Srenato }
152143509a12Srenato
152243509a12Srenato if (kn != NULL) {
152343509a12Srenato /* update route */
15243eb03b29Srenato kn->r = kr;
152543509a12Srenato
152643509a12Srenato if (kif_validate(kn->r.ifindex))
152743509a12Srenato kn->r.flags &= ~F_DOWN;
152843509a12Srenato else
152943509a12Srenato kn->r.flags |= F_DOWN;
153043509a12Srenato
153143509a12Srenato kr_redistribute(kp);
15324f0b2092Srenato } else {
15334f0b2092Srenato if ((rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_GET) &&
15344f0b2092Srenato (kr.priority == eigrpd_conf->fib_priority_internal ||
15354f0b2092Srenato kr.priority == eigrpd_conf->fib_priority_external ||
15364f0b2092Srenato kr.priority == eigrpd_conf->fib_priority_summary)) {
15374f0b2092Srenato log_warnx("alien EIGRP route %s/%d", log_addr(kr.af,
15384f0b2092Srenato &kr.prefix), kr.prefixlen);
15394f0b2092Srenato return (send_rtmsg(kr_state.fd, RTM_DELETE, &kr));
15404f0b2092Srenato }
15414f0b2092Srenato
154243509a12Srenato kroute_insert(&kr);
15434f0b2092Srenato }
154443509a12Srenato
154543509a12Srenato return (0);
154643509a12Srenato }
1547