1*95d3cd23Santon /* $OpenBSD: kroute.c,v 1.118 2025/01/01 13:44:52 anton Exp $ */ 2204df0f8Sclaudio 3204df0f8Sclaudio /* 4367f601bSnorby * Copyright (c) 2004 Esben Norby <norby@openbsd.org> 5204df0f8Sclaudio * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 6204df0f8Sclaudio * 7204df0f8Sclaudio * Permission to use, copy, modify, and distribute this software for any 8204df0f8Sclaudio * purpose with or without fee is hereby granted, provided that the above 9204df0f8Sclaudio * copyright notice and this permission notice appear in all copies. 10204df0f8Sclaudio * 11204df0f8Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12204df0f8Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13204df0f8Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14204df0f8Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15204df0f8Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16204df0f8Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17204df0f8Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18204df0f8Sclaudio */ 19204df0f8Sclaudio 20204df0f8Sclaudio #include <sys/types.h> 21204df0f8Sclaudio #include <sys/socket.h> 22204df0f8Sclaudio #include <sys/sysctl.h> 23204df0f8Sclaudio #include <sys/tree.h> 242dbdedf7Sclaudio #include <sys/uio.h> 25204df0f8Sclaudio #include <netinet/in.h> 26204df0f8Sclaudio #include <arpa/inet.h> 27204df0f8Sclaudio #include <net/if.h> 28204df0f8Sclaudio #include <net/if_dl.h> 29d7a91d7eSclaudio #include <net/if_types.h> 30204df0f8Sclaudio #include <net/route.h> 31204df0f8Sclaudio #include <err.h> 32204df0f8Sclaudio #include <errno.h> 33204df0f8Sclaudio #include <fcntl.h> 34204df0f8Sclaudio #include <stdio.h> 35204df0f8Sclaudio #include <stdlib.h> 36204df0f8Sclaudio #include <string.h> 37204df0f8Sclaudio #include <unistd.h> 38b9fc9a72Sderaadt #include <limits.h> 39204df0f8Sclaudio 40204df0f8Sclaudio #include "ospfd.h" 41204df0f8Sclaudio #include "log.h" 42204df0f8Sclaudio 43204df0f8Sclaudio struct { 44204df0f8Sclaudio u_int32_t rtseq; 45204df0f8Sclaudio pid_t pid; 46204df0f8Sclaudio int fib_sync; 47119f0f1dSdlg int fib_serial; 484c260f66Sremi u_int8_t fib_prio; 49204df0f8Sclaudio int fd; 50204df0f8Sclaudio struct event ev; 51fddf39b9Ssthen struct event reload; 52e4caa3d9Sclaudio u_int rdomain; 53fddf39b9Ssthen #define KR_RELOAD_IDLE 0 54fddf39b9Ssthen #define KR_RELOAD_FETCH 1 55fddf39b9Ssthen #define KR_RELOAD_HOLD 2 56fddf39b9Ssthen int reload_state; 57204df0f8Sclaudio } kr_state; 58204df0f8Sclaudio 59204df0f8Sclaudio struct kroute_node { 60204df0f8Sclaudio RB_ENTRY(kroute_node) entry; 6129b0bbeeSclaudio struct kroute_node *next; 62afcfb5e2Sclaudio struct kroute r; 63119f0f1dSdlg int serial; 64204df0f8Sclaudio }; 65204df0f8Sclaudio 66204df0f8Sclaudio struct kif_node { 67204df0f8Sclaudio RB_ENTRY(kif_node) entry; 6866dd3991Sclaudio TAILQ_HEAD(, kif_addr) addrs; 69204df0f8Sclaudio struct kif k; 70204df0f8Sclaudio }; 71204df0f8Sclaudio 7229b0bbeeSclaudio void kr_redist_remove(struct kroute_node *, struct kroute_node *); 73afcfb5e2Sclaudio int kr_redist_eval(struct kroute *, struct kroute *); 7429b0bbeeSclaudio void kr_redistribute(struct kroute_node *); 75204df0f8Sclaudio int kroute_compare(struct kroute_node *, struct kroute_node *); 76204df0f8Sclaudio int kif_compare(struct kif_node *, struct kif_node *); 77d5218eb1Sclaudio int kr_change_fib(struct kroute_node *, struct kroute *, int, int); 78d5218eb1Sclaudio int kr_delete_fib(struct kroute_node *); 79204df0f8Sclaudio 801795d796Sclaudio struct kroute_node *kroute_find(in_addr_t, u_int8_t, u_int8_t); 8129b0bbeeSclaudio struct kroute_node *kroute_matchgw(struct kroute_node *, struct in_addr); 82204df0f8Sclaudio int kroute_insert(struct kroute_node *); 83204df0f8Sclaudio int kroute_remove(struct kroute_node *); 84204df0f8Sclaudio void kroute_clear(void); 85204df0f8Sclaudio 8673e34765Sclaudio struct kif_node *kif_find(u_short); 8773e34765Sclaudio struct kif_node *kif_insert(u_short); 88204df0f8Sclaudio int kif_remove(struct kif_node *); 8973e34765Sclaudio struct kif *kif_update(u_short, int, struct if_data *, 9073e34765Sclaudio struct sockaddr_dl *); 9173e34765Sclaudio int kif_validate(u_short); 92204df0f8Sclaudio 93204df0f8Sclaudio struct kroute_node *kroute_match(in_addr_t); 94204df0f8Sclaudio 95204df0f8Sclaudio int protect_lo(void); 96204df0f8Sclaudio u_int8_t prefixlen_classful(in_addr_t); 97204df0f8Sclaudio void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 981c282279Sclaudio void if_change(u_short, int, struct if_data *, struct sockaddr_dl *); 9973e34765Sclaudio void if_newaddr(u_short, struct sockaddr_in *, struct sockaddr_in *, 10073e34765Sclaudio struct sockaddr_in *); 101a60d5a8aSclaudio void if_deladdr(u_short, struct sockaddr_in *, struct sockaddr_in *, 102a60d5a8aSclaudio struct sockaddr_in *); 103204df0f8Sclaudio void if_announce(void *); 104204df0f8Sclaudio 105204df0f8Sclaudio int send_rtmsg(int, int, struct kroute *); 106204df0f8Sclaudio int dispatch_rtmsg(void); 107204df0f8Sclaudio int fetchtable(void); 10873e34765Sclaudio int fetchifs(u_short); 10958ef7452Sclaudio int rtmsg_process(char *, size_t); 110fddf39b9Ssthen void kr_fib_reload_timer(int, short, void *); 111fddf39b9Ssthen void kr_fib_reload_arm_timer(int); 112204df0f8Sclaudio 113358269bbSclaudio RB_HEAD(kroute_tree, kroute_node) krt = RB_INITIALIZER(&krt); 114204df0f8Sclaudio RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare) 115204df0f8Sclaudio RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare) 116204df0f8Sclaudio 117358269bbSclaudio RB_HEAD(kif_tree, kif_node) kit = RB_INITIALIZER(&kit); 118204df0f8Sclaudio RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare) 119204df0f8Sclaudio RB_GENERATE(kif_tree, kif_node, entry, kif_compare) 120204df0f8Sclaudio 121204df0f8Sclaudio int 122e9fa2173Sclaudio kif_init(void) 123e9fa2173Sclaudio { 124e9fa2173Sclaudio if (fetchifs(0) == -1) 125e9fa2173Sclaudio return (-1); 126e9fa2173Sclaudio 127e9fa2173Sclaudio return (0); 128e9fa2173Sclaudio } 129e9fa2173Sclaudio 130e9fa2173Sclaudio int 1314c260f66Sremi kr_init(int fs, u_int rdomain, int redis_label_or_prefix, u_int8_t fib_prio) 132204df0f8Sclaudio { 1331dcb316cShenning int opt = 0, rcvbuf, default_rcvbuf; 1341dcb316cShenning socklen_t optlen; 1354c260f66Sremi int filter_prio = fib_prio; 1365e36d456Sjmatthew int filter_flags = RTF_LLINFO | RTF_BROADCAST; 137204df0f8Sclaudio 138204df0f8Sclaudio kr_state.fib_sync = fs; 139e4caa3d9Sclaudio kr_state.rdomain = rdomain; 1404c260f66Sremi kr_state.fib_prio = fib_prio; 141204df0f8Sclaudio 14258ef7452Sclaudio if ((kr_state.fd = socket(AF_ROUTE, 14358ef7452Sclaudio SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, AF_INET)) == -1) { 144204df0f8Sclaudio log_warn("kr_init: socket"); 145204df0f8Sclaudio return (-1); 146204df0f8Sclaudio } 147204df0f8Sclaudio 148204df0f8Sclaudio /* not interested in my own messages */ 149204df0f8Sclaudio if (setsockopt(kr_state.fd, SOL_SOCKET, SO_USELOOPBACK, 150204df0f8Sclaudio &opt, sizeof(opt)) == -1) 151204df0f8Sclaudio log_warn("kr_init: setsockopt"); /* not fatal */ 152204df0f8Sclaudio 153ad4152efSbenno if (redis_label_or_prefix) { 154ad4152efSbenno filter_prio = 0; 155ad4152efSbenno log_info("%s: priority filter disabled", __func__); 156ad4152efSbenno } else 157ad4152efSbenno log_debug("%s: priority filter enabled", __func__); 158ad4152efSbenno 159ad4152efSbenno if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_PRIOFILTER, &filter_prio, 160ad4152efSbenno sizeof(filter_prio)) == -1) { 161ad4152efSbenno log_warn("%s: setsockopt AF_ROUTE ROUTE_PRIOFILTER", __func__); 162ad4152efSbenno /* not fatal */ 163ad4152efSbenno } 1645e36d456Sjmatthew if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_FLAGFILTER, &filter_flags, 1655e36d456Sjmatthew sizeof(filter_flags)) == -1) { 1665e36d456Sjmatthew log_warn("%s: setsockopt AF_ROUTE ROUTE_FLAGFILTER", __func__); 1675e36d456Sjmatthew /* not fatal */ 1685e36d456Sjmatthew } 169ad4152efSbenno 1701dcb316cShenning /* grow receive buffer, don't wanna miss messages */ 1711dcb316cShenning optlen = sizeof(default_rcvbuf); 1721dcb316cShenning if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, 1731dcb316cShenning &default_rcvbuf, &optlen) == -1) 1741dcb316cShenning log_warn("kr_init getsockopt SOL_SOCKET SO_RCVBUF"); 1751dcb316cShenning else 1761dcb316cShenning for (rcvbuf = MAX_RTSOCK_BUF; 1771dcb316cShenning rcvbuf > default_rcvbuf && 1781dcb316cShenning setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, 1791dcb316cShenning &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS; 1801dcb316cShenning rcvbuf /= 2) 1811dcb316cShenning ; /* nothing */ 1821dcb316cShenning 183204df0f8Sclaudio kr_state.pid = getpid(); 184204df0f8Sclaudio kr_state.rtseq = 1; 185204df0f8Sclaudio 186204df0f8Sclaudio if (fetchtable() == -1) 187204df0f8Sclaudio return (-1); 188204df0f8Sclaudio 189204df0f8Sclaudio if (protect_lo() == -1) 190204df0f8Sclaudio return (-1); 191204df0f8Sclaudio 192e9fa2173Sclaudio event_set(&kr_state.ev, kr_state.fd, EV_READ | EV_PERSIST, 193e9fa2173Sclaudio kr_dispatch_msg, NULL); 194204df0f8Sclaudio event_add(&kr_state.ev, NULL); 195204df0f8Sclaudio 196fddf39b9Ssthen kr_state.reload_state = KR_RELOAD_IDLE; 197fddf39b9Ssthen evtimer_set(&kr_state.reload, kr_fib_reload_timer, NULL); 198fddf39b9Ssthen 199204df0f8Sclaudio return (0); 200204df0f8Sclaudio } 201204df0f8Sclaudio 202204df0f8Sclaudio int 203d5218eb1Sclaudio kr_change_fib(struct kroute_node *kr, struct kroute *kroute, int krcount, 204d5218eb1Sclaudio int action) 205d5218eb1Sclaudio { 206d5218eb1Sclaudio int i; 207d5218eb1Sclaudio struct kroute_node *kn, *nkn; 208d5218eb1Sclaudio 209d5218eb1Sclaudio if (action == RTM_ADD) { 210d5218eb1Sclaudio /* 211d5218eb1Sclaudio * First remove all stale multipath routes. 212d5218eb1Sclaudio * This step must be skipped when the action is RTM_CHANGE 213d5218eb1Sclaudio * because it is already a single path route that will be 214d5218eb1Sclaudio * changed. 215d5218eb1Sclaudio */ 216d5218eb1Sclaudio for (kn = kr; kn != NULL; kn = nkn) { 217d5218eb1Sclaudio for (i = 0; i < krcount; i++) { 218d5218eb1Sclaudio if (kn->r.nexthop.s_addr == 219d5218eb1Sclaudio kroute[i].nexthop.s_addr) 220d5218eb1Sclaudio break; 221d5218eb1Sclaudio } 222d5218eb1Sclaudio nkn = kn->next; 223e938c347Sgollo if (i == krcount) { 224d5218eb1Sclaudio /* stale route */ 225d5218eb1Sclaudio if (kr_delete_fib(kn) == -1) 226d5218eb1Sclaudio log_warnx("kr_delete_fib failed"); 2278cee13bcSclaudio /* 2288cee13bcSclaudio * if head element was removed we need to adjust 2298cee13bcSclaudio * the head 2308cee13bcSclaudio */ 2318cee13bcSclaudio if (kr == kn) 2328cee13bcSclaudio kr = nkn; 233d5218eb1Sclaudio } 234d5218eb1Sclaudio } 235e938c347Sgollo } 236d5218eb1Sclaudio 237d5218eb1Sclaudio /* 238d5218eb1Sclaudio * now add or change the route 239d5218eb1Sclaudio */ 240d5218eb1Sclaudio for (i = 0; i < krcount; i++) { 241d5218eb1Sclaudio /* nexthop within 127/8 -> ignore silently */ 242d5218eb1Sclaudio if ((kroute[i].nexthop.s_addr & htonl(IN_CLASSA_NET)) == 243d5218eb1Sclaudio htonl(INADDR_LOOPBACK & IN_CLASSA_NET)) 244d5218eb1Sclaudio continue; 245d5218eb1Sclaudio 246d5218eb1Sclaudio if (action == RTM_ADD && kr) { 247d5218eb1Sclaudio for (kn = kr; kn != NULL; kn = kn->next) { 248d5218eb1Sclaudio if (kn->r.nexthop.s_addr == 249d5218eb1Sclaudio kroute[i].nexthop.s_addr) 250d5218eb1Sclaudio break; 251d5218eb1Sclaudio } 252d5218eb1Sclaudio 253d5218eb1Sclaudio if (kn != NULL) 254d5218eb1Sclaudio /* nexthop already present, skip it */ 255d5218eb1Sclaudio continue; 256d5218eb1Sclaudio } else 257d5218eb1Sclaudio /* modify first entry */ 258d5218eb1Sclaudio kn = kr; 259d5218eb1Sclaudio 260d5218eb1Sclaudio /* send update */ 261d5218eb1Sclaudio if (send_rtmsg(kr_state.fd, action, &kroute[i]) == -1) 262d5218eb1Sclaudio return (-1); 263d5218eb1Sclaudio 264d5218eb1Sclaudio /* create new entry unless we are changing the first entry */ 265d5218eb1Sclaudio if (action == RTM_ADD) 266d5218eb1Sclaudio if ((kn = calloc(1, sizeof(*kn))) == NULL) 267d5218eb1Sclaudio fatal(NULL); 268d5218eb1Sclaudio 269d5218eb1Sclaudio kn->r.prefix.s_addr = kroute[i].prefix.s_addr; 270d5218eb1Sclaudio kn->r.prefixlen = kroute[i].prefixlen; 271d5218eb1Sclaudio kn->r.nexthop.s_addr = kroute[i].nexthop.s_addr; 272d5218eb1Sclaudio kn->r.flags = kroute[i].flags | F_OSPFD_INSERTED; 2734c260f66Sremi kn->r.priority = kr_state.fib_prio; 274d5218eb1Sclaudio kn->r.ext_tag = kroute[i].ext_tag; 275d5218eb1Sclaudio rtlabel_unref(kn->r.rtlabel); /* for RTM_CHANGE */ 276d5218eb1Sclaudio kn->r.rtlabel = kroute[i].rtlabel; 277d5218eb1Sclaudio 278d5218eb1Sclaudio if (action == RTM_ADD) 279d5218eb1Sclaudio if (kroute_insert(kn) == -1) { 280d5218eb1Sclaudio log_debug("kr_update_fib: cannot insert %s", 281d5218eb1Sclaudio inet_ntoa(kn->r.nexthop)); 282d5218eb1Sclaudio free(kn); 283d5218eb1Sclaudio } 284d5218eb1Sclaudio action = RTM_ADD; 285d5218eb1Sclaudio } 286d5218eb1Sclaudio return (0); 287d5218eb1Sclaudio } 288d5218eb1Sclaudio 289d5218eb1Sclaudio int 290d5218eb1Sclaudio kr_change(struct kroute *kroute, int krcount) 291204df0f8Sclaudio { 292204df0f8Sclaudio struct kroute_node *kr; 293204df0f8Sclaudio int action = RTM_ADD; 294204df0f8Sclaudio 295fcb4545bSreyk kroute->rtlabel = rtlabel_tag2id(kroute->ext_tag); 296fcb4545bSreyk 2974c260f66Sremi kr = kroute_find(kroute->prefix.s_addr, kroute->prefixlen, 2984c260f66Sremi kr_state.fib_prio); 2991795d796Sclaudio if (kr != NULL && kr->next == NULL && krcount == 1) 3001795d796Sclaudio /* single path OSPF route */ 3011446e0e7Sclaudio action = RTM_CHANGE; 302204df0f8Sclaudio 303d5218eb1Sclaudio return (kr_change_fib(kr, kroute, krcount, action)); 304d5218eb1Sclaudio } 305d5218eb1Sclaudio 306d5218eb1Sclaudio int 307d5218eb1Sclaudio kr_delete_fib(struct kroute_node *kr) 308d5218eb1Sclaudio { 3094c260f66Sremi if (kr->r.priority != kr_state.fib_prio) 3101795d796Sclaudio log_warn("kr_delete_fib: %s/%d has wrong priority %d", 3111795d796Sclaudio inet_ntoa(kr->r.prefix), kr->r.prefixlen, kr->r.priority); 312204df0f8Sclaudio 313d5218eb1Sclaudio if (send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r) == -1) 314d5218eb1Sclaudio return (-1); 315d5218eb1Sclaudio 316d5218eb1Sclaudio if (kroute_remove(kr) == -1) 317d5218eb1Sclaudio return (-1); 318204df0f8Sclaudio 319204df0f8Sclaudio return (0); 320204df0f8Sclaudio } 321204df0f8Sclaudio 322204df0f8Sclaudio int 323204df0f8Sclaudio kr_delete(struct kroute *kroute) 324204df0f8Sclaudio { 325d5218eb1Sclaudio struct kroute_node *kr, *nkr; 326204df0f8Sclaudio 3271795d796Sclaudio if ((kr = kroute_find(kroute->prefix.s_addr, kroute->prefixlen, 3284c260f66Sremi kr_state.fib_prio)) == NULL) 329204df0f8Sclaudio return (0); 330204df0f8Sclaudio 331d5218eb1Sclaudio while (kr != NULL) { 332d5218eb1Sclaudio nkr = kr->next; 333d5218eb1Sclaudio if (kr_delete_fib(kr) == -1) 334d5218eb1Sclaudio return (-1); 335d5218eb1Sclaudio kr = nkr; 3361446e0e7Sclaudio } 337204df0f8Sclaudio return (0); 338204df0f8Sclaudio } 339204df0f8Sclaudio 340204df0f8Sclaudio void 341204df0f8Sclaudio kr_shutdown(void) 342204df0f8Sclaudio { 343204df0f8Sclaudio kr_fib_decouple(); 344204df0f8Sclaudio kroute_clear(); 345204df0f8Sclaudio kif_clear(); 346204df0f8Sclaudio } 347204df0f8Sclaudio 348204df0f8Sclaudio void 349204df0f8Sclaudio kr_fib_couple(void) 350204df0f8Sclaudio { 351204df0f8Sclaudio struct kroute_node *kr; 352d5218eb1Sclaudio struct kroute_node *kn; 353204df0f8Sclaudio 354204df0f8Sclaudio if (kr_state.fib_sync == 1) /* already coupled */ 355204df0f8Sclaudio return; 356204df0f8Sclaudio 357204df0f8Sclaudio kr_state.fib_sync = 1; 358204df0f8Sclaudio 359204df0f8Sclaudio RB_FOREACH(kr, kroute_tree, &krt) 3604c260f66Sremi if (kr->r.priority == kr_state.fib_prio) 3611795d796Sclaudio for (kn = kr; kn != NULL; kn = kn->next) 362d5218eb1Sclaudio send_rtmsg(kr_state.fd, RTM_ADD, &kn->r); 363204df0f8Sclaudio 364204df0f8Sclaudio log_info("kernel routing table coupled"); 365204df0f8Sclaudio } 366204df0f8Sclaudio 367204df0f8Sclaudio void 368204df0f8Sclaudio kr_fib_decouple(void) 369204df0f8Sclaudio { 370204df0f8Sclaudio struct kroute_node *kr; 371d5218eb1Sclaudio struct kroute_node *kn; 372204df0f8Sclaudio 373204df0f8Sclaudio if (kr_state.fib_sync == 0) /* already decoupled */ 374204df0f8Sclaudio return; 375204df0f8Sclaudio 3761795d796Sclaudio RB_FOREACH(kr, kroute_tree, &krt) 3774c260f66Sremi if (kr->r.priority == kr_state.fib_prio) 3781795d796Sclaudio for (kn = kr; kn != NULL; kn = kn->next) 379d5218eb1Sclaudio send_rtmsg(kr_state.fd, RTM_DELETE, &kn->r); 380204df0f8Sclaudio 381204df0f8Sclaudio kr_state.fib_sync = 0; 382204df0f8Sclaudio 383204df0f8Sclaudio log_info("kernel routing table decoupled"); 384204df0f8Sclaudio } 385204df0f8Sclaudio 386119f0f1dSdlg void 387fddf39b9Ssthen kr_fib_reload_timer(int fd, short event, void *bula) 388fddf39b9Ssthen { 389fddf39b9Ssthen if (kr_state.reload_state == KR_RELOAD_FETCH) { 390fddf39b9Ssthen kr_fib_reload(); 391fddf39b9Ssthen kr_state.reload_state = KR_RELOAD_HOLD; 392fddf39b9Ssthen kr_fib_reload_arm_timer(KR_RELOAD_HOLD_TIMER); 393fddf39b9Ssthen } else { 394fddf39b9Ssthen kr_state.reload_state = KR_RELOAD_IDLE; 395fddf39b9Ssthen } 396fddf39b9Ssthen } 397fddf39b9Ssthen 398fddf39b9Ssthen void 399fddf39b9Ssthen kr_fib_reload_arm_timer(int delay) 400fddf39b9Ssthen { 401fddf39b9Ssthen struct timeval tv; 402fddf39b9Ssthen 403fddf39b9Ssthen timerclear(&tv); 404fddf39b9Ssthen tv.tv_sec = delay / 1000; 405fddf39b9Ssthen tv.tv_usec = (delay % 1000) * 1000; 406fddf39b9Ssthen 407fddf39b9Ssthen if (evtimer_add(&kr_state.reload, &tv) == -1) 408fddf39b9Ssthen fatal("add_reload_timer"); 409fddf39b9Ssthen } 410fddf39b9Ssthen 411fddf39b9Ssthen void 41204fee684Stb kr_fib_reload(void) 413119f0f1dSdlg { 414119f0f1dSdlg struct kroute_node *krn, *kr, *kn; 415119f0f1dSdlg 416fa19e37cSdlg log_info("reloading interface list and routing table"); 417fa19e37cSdlg 418119f0f1dSdlg kr_state.fib_serial++; 419119f0f1dSdlg 420d11a1a20Sdlg if (fetchifs(0) == -1 || fetchtable() == -1) 421119f0f1dSdlg return; 422119f0f1dSdlg 423119f0f1dSdlg for (kr = RB_MIN(kroute_tree, &krt); kr != NULL; kr = krn) { 424119f0f1dSdlg krn = RB_NEXT(kroute_tree, &krt, kr); 425119f0f1dSdlg 426119f0f1dSdlg do { 427119f0f1dSdlg kn = kr->next; 428119f0f1dSdlg 429119f0f1dSdlg if (kr->serial != kr_state.fib_serial) { 4304c260f66Sremi if (kr->r.priority == kr_state.fib_prio) { 431119f0f1dSdlg kr->serial = kr_state.fib_serial; 432119f0f1dSdlg if (send_rtmsg(kr_state.fd, 433119f0f1dSdlg RTM_ADD, &kr->r) != 0) 434119f0f1dSdlg break; 435119f0f1dSdlg } else 436119f0f1dSdlg kroute_remove(kr); 437119f0f1dSdlg } 438119f0f1dSdlg 439119f0f1dSdlg } while ((kr = kn) != NULL); 440119f0f1dSdlg } 441119f0f1dSdlg } 442119f0f1dSdlg 4434c260f66Sremi void 4444c260f66Sremi kr_fib_update_prio(u_int8_t fib_prio) 4454c260f66Sremi { 4464c260f66Sremi struct kroute_node *kr; 4474c260f66Sremi 4484c260f66Sremi RB_FOREACH(kr, kroute_tree, &krt) 4494c260f66Sremi if ((kr->r.flags & F_OSPFD_INSERTED)) 4504c260f66Sremi kr->r.priority = fib_prio; 4514c260f66Sremi 4524c260f66Sremi log_info("fib priority changed from %hhu to %hhu", 4534c260f66Sremi kr_state.fib_prio, fib_prio); 4544c260f66Sremi 4554c260f66Sremi kr_state.fib_prio = fib_prio; 4564c260f66Sremi } 4574c260f66Sremi 458204df0f8Sclaudio void 459204df0f8Sclaudio kr_dispatch_msg(int fd, short event, void *bula) 460204df0f8Sclaudio { 4611795d796Sclaudio /* XXX this is stupid */ 462412a8077Sdlg if (dispatch_rtmsg() == -1) 463412a8077Sdlg event_loopexit(NULL); 464204df0f8Sclaudio } 465204df0f8Sclaudio 466204df0f8Sclaudio void 467204df0f8Sclaudio kr_show_route(struct imsg *imsg) 468204df0f8Sclaudio { 469b6b3a59aSclaudio struct kroute_node *kr; 4704ee6d016Spyr struct kroute_node *kn; 471b6b3a59aSclaudio int flags; 472b6b3a59aSclaudio struct in_addr addr; 473b6b3a59aSclaudio 474204df0f8Sclaudio switch (imsg->hdr.type) { 475b6b3a59aSclaudio case IMSG_CTL_KROUTE: 476b6b3a59aSclaudio if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags)) { 477b6b3a59aSclaudio log_warnx("kr_show_route: wrong imsg len"); 478b6b3a59aSclaudio return; 479b6b3a59aSclaudio } 480b6b3a59aSclaudio memcpy(&flags, imsg->data, sizeof(flags)); 481b6b3a59aSclaudio RB_FOREACH(kr, kroute_tree, &krt) 482b6b3a59aSclaudio if (!flags || kr->r.flags & flags) { 4834ee6d016Spyr kn = kr; 4844ee6d016Spyr do { 485b6b3a59aSclaudio main_imsg_compose_ospfe(IMSG_CTL_KROUTE, 4864ee6d016Spyr imsg->hdr.pid, 4874ee6d016Spyr &kn->r, sizeof(kn->r)); 4884ee6d016Spyr } while ((kn = kn->next) != NULL); 489b6b3a59aSclaudio } 490b6b3a59aSclaudio break; 491b6b3a59aSclaudio case IMSG_CTL_KROUTE_ADDR: 492b6b3a59aSclaudio if (imsg->hdr.len != IMSG_HEADER_SIZE + 493b6b3a59aSclaudio sizeof(struct in_addr)) { 494b6b3a59aSclaudio log_warnx("kr_show_route: wrong imsg len"); 495b6b3a59aSclaudio return; 496b6b3a59aSclaudio } 497b6b3a59aSclaudio memcpy(&addr, imsg->data, sizeof(addr)); 498b6b3a59aSclaudio kr = NULL; 499b6b3a59aSclaudio kr = kroute_match(addr.s_addr); 500b6b3a59aSclaudio if (kr != NULL) 501b6b3a59aSclaudio main_imsg_compose_ospfe(IMSG_CTL_KROUTE, imsg->hdr.pid, 502b6b3a59aSclaudio &kr->r, sizeof(kr->r)); 503b6b3a59aSclaudio break; 504204df0f8Sclaudio default: 505204df0f8Sclaudio log_debug("kr_show_route: error handling imsg"); 506204df0f8Sclaudio break; 507204df0f8Sclaudio } 508204df0f8Sclaudio 509204df0f8Sclaudio main_imsg_compose_ospfe(IMSG_CTL_END, imsg->hdr.pid, NULL, 0); 510204df0f8Sclaudio } 511204df0f8Sclaudio 512204df0f8Sclaudio void 51370955e50Sclaudio kr_ifinfo(char *ifname, pid_t pid) 514204df0f8Sclaudio { 515204df0f8Sclaudio struct kif_node *kif; 516204df0f8Sclaudio 517204df0f8Sclaudio RB_FOREACH(kif, kif_tree, &kit) 51870955e50Sclaudio if (ifname == NULL || !strcmp(ifname, kif->k.ifname)) { 51970955e50Sclaudio main_imsg_compose_ospfe(IMSG_CTL_IFINFO, 52070955e50Sclaudio pid, &kif->k, sizeof(kif->k)); 521204df0f8Sclaudio } 52270955e50Sclaudio 52370955e50Sclaudio main_imsg_compose_ospfe(IMSG_CTL_END, pid, NULL, 0); 524204df0f8Sclaudio } 525204df0f8Sclaudio 526e2993955Sclaudio void 52729b0bbeeSclaudio kr_redist_remove(struct kroute_node *kh, struct kroute_node *kn) 528e2993955Sclaudio { 529b1c8b220Sjca struct kroute *kr; 53044cb7d8eSclaudio 53144cb7d8eSclaudio /* was the route redistributed? */ 53229b0bbeeSclaudio if ((kn->r.flags & F_REDISTRIBUTED) == 0) 53329b0bbeeSclaudio return; 53429b0bbeeSclaudio 53529b0bbeeSclaudio /* remove redistributed flag */ 53629b0bbeeSclaudio kn->r.flags &= ~F_REDISTRIBUTED; 537b1c8b220Sjca kr = &kn->r; 53829b0bbeeSclaudio 53929b0bbeeSclaudio /* probably inform the RDE (check if no other path is redistributed) */ 54029b0bbeeSclaudio for (kn = kh; kn; kn = kn->next) 54129b0bbeeSclaudio if (kn->r.flags & F_REDISTRIBUTED) 54229b0bbeeSclaudio break; 54329b0bbeeSclaudio 54429b0bbeeSclaudio if (kn == NULL) 545b1c8b220Sjca main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, kr, 546afcfb5e2Sclaudio sizeof(struct kroute)); 54744cb7d8eSclaudio } 54829b0bbeeSclaudio 54929b0bbeeSclaudio int 550b1c8b220Sjca kr_redist_eval(struct kroute *kr, struct kroute *new_kr) 55129b0bbeeSclaudio { 55229b0bbeeSclaudio u_int32_t a, metric = 0; 55344cb7d8eSclaudio 55444cb7d8eSclaudio /* Only non-ospfd routes are considered for redistribution. */ 5551446e0e7Sclaudio if (!(kr->flags & F_KERNEL)) 556bbb232d2Sclaudio goto dont_redistribute; 55744cb7d8eSclaudio 55840df8bcdSclaudio /* Dynamic routes are not redistributable. */ 55940df8bcdSclaudio if (kr->flags & F_DYNAMIC) 560bbb232d2Sclaudio goto dont_redistribute; 56140df8bcdSclaudio 56244cb7d8eSclaudio /* interface is not up and running so don't announce */ 56344cb7d8eSclaudio if (kr->flags & F_DOWN) 564bbb232d2Sclaudio goto dont_redistribute; 56544cb7d8eSclaudio 566e2993955Sclaudio /* 567ef8f8f93Sclaudio * We consider the loopback net and multicast addresses 568e2993955Sclaudio * as not redistributable. 569e2993955Sclaudio */ 570e2993955Sclaudio a = ntohl(kr->prefix.s_addr); 571ef8f8f93Sclaudio if (IN_MULTICAST(a) || (a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) 572bbb232d2Sclaudio goto dont_redistribute; 573e2993955Sclaudio /* 574d9eec83cSclaudio * Consider networks with nexthop loopback as not redistributable 575d9eec83cSclaudio * unless it is a reject or blackhole route. 576e2993955Sclaudio */ 577d9eec83cSclaudio if (kr->nexthop.s_addr == htonl(INADDR_LOOPBACK) && 578d9eec83cSclaudio !(kr->flags & (F_BLACKHOLE|F_REJECT))) 579bbb232d2Sclaudio goto dont_redistribute; 580e2993955Sclaudio 581cc63418dSsthen /* Should we redistribute this route? */ 582f373ed5aSclaudio if (!ospf_redistribute(kr, &metric)) 583bbb232d2Sclaudio goto dont_redistribute; 58444cb7d8eSclaudio 58529b0bbeeSclaudio /* prefix should be redistributed */ 58644cb7d8eSclaudio kr->flags |= F_REDISTRIBUTED; 58729b0bbeeSclaudio /* 58832da7e04Sclaudio * only one of all multipath routes can be redistributed so 58929b0bbeeSclaudio * redistribute the best one. 59029b0bbeeSclaudio */ 591b1c8b220Sjca if (new_kr->metric > metric) { 592b1c8b220Sjca *new_kr = *kr; 593b1c8b220Sjca new_kr->metric = metric; 59429b0bbeeSclaudio } 595afcfb5e2Sclaudio 59629b0bbeeSclaudio return (1); 597f373ed5aSclaudio 59829b0bbeeSclaudio dont_redistribute: 59929b0bbeeSclaudio /* was the route redistributed? */ 60029b0bbeeSclaudio if ((kr->flags & F_REDISTRIBUTED) == 0) 60129b0bbeeSclaudio return (0); 60229b0bbeeSclaudio 60329b0bbeeSclaudio kr->flags &= ~F_REDISTRIBUTED; 60429b0bbeeSclaudio return (1); 60529b0bbeeSclaudio } 60629b0bbeeSclaudio 60729b0bbeeSclaudio void 60829b0bbeeSclaudio kr_redistribute(struct kroute_node *kh) 60929b0bbeeSclaudio { 61029b0bbeeSclaudio struct kroute_node *kn; 611b1c8b220Sjca struct kroute kr; 61229b0bbeeSclaudio int redistribute = 0; 61329b0bbeeSclaudio 6141795d796Sclaudio /* only the highest prio route can be redistributed */ 6151795d796Sclaudio if (kroute_find(kh->r.prefix.s_addr, kh->r.prefixlen, RTP_ANY) != kh) 6161795d796Sclaudio return; 6171795d796Sclaudio 618b1c8b220Sjca bzero(&kr, sizeof(kr)); 619b1c8b220Sjca kr.metric = UINT_MAX; 62029b0bbeeSclaudio for (kn = kh; kn; kn = kn->next) 621b1c8b220Sjca if (kr_redist_eval(&kn->r, &kr)) 62229b0bbeeSclaudio redistribute = 1; 62329b0bbeeSclaudio 62429b0bbeeSclaudio if (!redistribute) 62529b0bbeeSclaudio return; 62629b0bbeeSclaudio 627b1c8b220Sjca if (kr.flags & F_REDISTRIBUTED) { 628b1c8b220Sjca main_imsg_compose_rde(IMSG_NETWORK_ADD, 0, &kr, 629afcfb5e2Sclaudio sizeof(struct kroute)); 63029b0bbeeSclaudio } else { 631b1c8b220Sjca kr = kh->r; 632b1c8b220Sjca main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, &kr, 633afcfb5e2Sclaudio sizeof(struct kroute)); 63429b0bbeeSclaudio } 635e2993955Sclaudio } 636e2993955Sclaudio 637afefb162Sclaudio void 638ad4152efSbenno kr_reload(int redis_label_or_prefix) 639afefb162Sclaudio { 64029b0bbeeSclaudio struct kroute_node *kr, *kn; 641afefb162Sclaudio u_int32_t dummy; 642afefb162Sclaudio int r; 6434c260f66Sremi int filter_prio = kr_state.fib_prio; 644afefb162Sclaudio 645ad4152efSbenno /* update the priority filter */ 646ad4152efSbenno if (redis_label_or_prefix) { 647ad4152efSbenno filter_prio = 0; 648ad4152efSbenno log_info("%s: priority filter disabled", __func__); 649ad4152efSbenno } else 650ad4152efSbenno log_debug("%s: priority filter enabled", __func__); 651ad4152efSbenno 652ad4152efSbenno if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_PRIOFILTER, &filter_prio, 653ad4152efSbenno sizeof(filter_prio)) == -1) { 654ad4152efSbenno log_warn("%s: setsockopt AF_ROUTE ROUTE_PRIOFILTER", __func__); 655ad4152efSbenno /* not fatal */ 656ad4152efSbenno } 657ad4152efSbenno 658ad4152efSbenno /* update redistribute lists */ 659afefb162Sclaudio RB_FOREACH(kr, kroute_tree, &krt) { 66029b0bbeeSclaudio for (kn = kr; kn; kn = kn->next) { 66129b0bbeeSclaudio r = ospf_redistribute(&kn->r, &dummy); 66229b0bbeeSclaudio /* 66329b0bbeeSclaudio * if it is redistributed, redistribute again metric 66429b0bbeeSclaudio * may have changed. 66529b0bbeeSclaudio */ 66629b0bbeeSclaudio if ((kn->r.flags & F_REDISTRIBUTED && !r) || r) 66729b0bbeeSclaudio break; 66829b0bbeeSclaudio } 66929b0bbeeSclaudio if (kn) { 67029b0bbeeSclaudio /* 67129b0bbeeSclaudio * kr_redistribute copes with removes and RDE with 67229b0bbeeSclaudio * duplicates 67329b0bbeeSclaudio */ 67429b0bbeeSclaudio kr_redistribute(kr); 675afefb162Sclaudio } 676afefb162Sclaudio } 677afefb162Sclaudio } 678afefb162Sclaudio 679204df0f8Sclaudio /* rb-tree compare */ 680204df0f8Sclaudio int 681204df0f8Sclaudio kroute_compare(struct kroute_node *a, struct kroute_node *b) 682204df0f8Sclaudio { 683204df0f8Sclaudio if (ntohl(a->r.prefix.s_addr) < ntohl(b->r.prefix.s_addr)) 684204df0f8Sclaudio return (-1); 685204df0f8Sclaudio if (ntohl(a->r.prefix.s_addr) > ntohl(b->r.prefix.s_addr)) 686204df0f8Sclaudio return (1); 687204df0f8Sclaudio if (a->r.prefixlen < b->r.prefixlen) 688204df0f8Sclaudio return (-1); 689204df0f8Sclaudio if (a->r.prefixlen > b->r.prefixlen) 690204df0f8Sclaudio return (1); 6911795d796Sclaudio 6921795d796Sclaudio /* if the priority is RTP_ANY finish on the first address hit */ 6931795d796Sclaudio if (a->r.priority == RTP_ANY || b->r.priority == RTP_ANY) 6941795d796Sclaudio return (0); 6951795d796Sclaudio if (a->r.priority < b->r.priority) 6961795d796Sclaudio return (-1); 6971795d796Sclaudio if (a->r.priority > b->r.priority) 6981795d796Sclaudio return (1); 699204df0f8Sclaudio return (0); 700204df0f8Sclaudio } 701204df0f8Sclaudio 702204df0f8Sclaudio int 703204df0f8Sclaudio kif_compare(struct kif_node *a, struct kif_node *b) 704204df0f8Sclaudio { 705204df0f8Sclaudio return (b->k.ifindex - a->k.ifindex); 706204df0f8Sclaudio } 707204df0f8Sclaudio 708204df0f8Sclaudio /* tree management */ 709204df0f8Sclaudio struct kroute_node * 7101795d796Sclaudio kroute_find(in_addr_t prefix, u_int8_t prefixlen, u_int8_t prio) 711204df0f8Sclaudio { 712204df0f8Sclaudio struct kroute_node s; 7131795d796Sclaudio struct kroute_node *kn, *tmp; 714204df0f8Sclaudio 715204df0f8Sclaudio s.r.prefix.s_addr = prefix; 716204df0f8Sclaudio s.r.prefixlen = prefixlen; 7171795d796Sclaudio s.r.priority = prio; 718204df0f8Sclaudio 7191795d796Sclaudio kn = RB_FIND(kroute_tree, &krt, &s); 7201795d796Sclaudio if (kn && prio == RTP_ANY) { 7211795d796Sclaudio tmp = RB_PREV(kroute_tree, &krt, kn); 7221795d796Sclaudio while (tmp) { 7231795d796Sclaudio if (kroute_compare(&s, tmp) == 0) 7241795d796Sclaudio kn = tmp; 7251795d796Sclaudio else 7261795d796Sclaudio break; 7271795d796Sclaudio tmp = RB_PREV(kroute_tree, &krt, kn); 7281795d796Sclaudio } 7291795d796Sclaudio } 7301795d796Sclaudio return (kn); 731204df0f8Sclaudio } 732204df0f8Sclaudio 73329b0bbeeSclaudio struct kroute_node * 73429b0bbeeSclaudio kroute_matchgw(struct kroute_node *kr, struct in_addr nh) 73529b0bbeeSclaudio { 73629b0bbeeSclaudio in_addr_t nexthop; 73729b0bbeeSclaudio 73829b0bbeeSclaudio nexthop = nh.s_addr; 73929b0bbeeSclaudio 74029b0bbeeSclaudio while (kr) { 74129b0bbeeSclaudio if (kr->r.nexthop.s_addr == nexthop) 74229b0bbeeSclaudio return (kr); 74329b0bbeeSclaudio kr = kr->next; 74429b0bbeeSclaudio } 74529b0bbeeSclaudio 74629b0bbeeSclaudio return (NULL); 74729b0bbeeSclaudio } 74829b0bbeeSclaudio 749204df0f8Sclaudio int 750204df0f8Sclaudio kroute_insert(struct kroute_node *kr) 751204df0f8Sclaudio { 7529ae468ceSclaudio struct kroute_node *krm, *krh; 75329b0bbeeSclaudio 754119f0f1dSdlg kr->serial = kr_state.fib_serial; 755119f0f1dSdlg 7569ae468ceSclaudio if ((krh = RB_INSERT(kroute_tree, &krt, kr)) != NULL) { 75729b0bbeeSclaudio /* 75832da7e04Sclaudio * Multipath route, add at end of list. 75929b0bbeeSclaudio */ 7609ae468ceSclaudio krm = krh; 76129b0bbeeSclaudio while (krm->next != NULL) 76229b0bbeeSclaudio krm = krm->next; 76329b0bbeeSclaudio krm->next = kr; 76429b0bbeeSclaudio kr->next = NULL; /* to be sure */ 76529b0bbeeSclaudio } else 7669ae468ceSclaudio krh = kr; 767204df0f8Sclaudio 7681446e0e7Sclaudio if (!(kr->r.flags & F_KERNEL)) { 76944cb7d8eSclaudio /* don't validate or redistribute ospf route */ 77044cb7d8eSclaudio kr->r.flags &= ~F_DOWN; 77144cb7d8eSclaudio return (0); 77244cb7d8eSclaudio } 77344cb7d8eSclaudio 77444cb7d8eSclaudio if (kif_validate(kr->r.ifindex)) 77544cb7d8eSclaudio kr->r.flags &= ~F_DOWN; 77644cb7d8eSclaudio else 77744cb7d8eSclaudio kr->r.flags |= F_DOWN; 77844cb7d8eSclaudio 7799ae468ceSclaudio kr_redistribute(krh); 780204df0f8Sclaudio return (0); 781204df0f8Sclaudio } 782204df0f8Sclaudio 783204df0f8Sclaudio int 784204df0f8Sclaudio kroute_remove(struct kroute_node *kr) 785204df0f8Sclaudio { 78629b0bbeeSclaudio struct kroute_node *krm; 78729b0bbeeSclaudio 78829b0bbeeSclaudio if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) { 78929b0bbeeSclaudio log_warnx("kroute_remove failed to find %s/%u", 79029b0bbeeSclaudio inet_ntoa(kr->r.prefix), kr->r.prefixlen); 79129b0bbeeSclaudio return (-1); 79229b0bbeeSclaudio } 79329b0bbeeSclaudio 79429b0bbeeSclaudio if (krm == kr) { 79529b0bbeeSclaudio /* head element */ 796204df0f8Sclaudio if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) { 797204df0f8Sclaudio log_warnx("kroute_remove failed for %s/%u", 798204df0f8Sclaudio inet_ntoa(kr->r.prefix), kr->r.prefixlen); 799204df0f8Sclaudio return (-1); 800204df0f8Sclaudio } 80129b0bbeeSclaudio if (kr->next != NULL) { 80229b0bbeeSclaudio if (RB_INSERT(kroute_tree, &krt, kr->next) != NULL) { 80329b0bbeeSclaudio log_warnx("kroute_remove failed to add %s/%u", 80429b0bbeeSclaudio inet_ntoa(kr->r.prefix), kr->r.prefixlen); 80529b0bbeeSclaudio return (-1); 80629b0bbeeSclaudio } 80729b0bbeeSclaudio } 80829b0bbeeSclaudio } else { 80929b0bbeeSclaudio /* somewhere in the list */ 81029b0bbeeSclaudio while (krm->next != kr && krm->next != NULL) 81129b0bbeeSclaudio krm = krm->next; 81229b0bbeeSclaudio if (krm->next == NULL) { 81329b0bbeeSclaudio log_warnx("kroute_remove multipath list corrupted " 81429b0bbeeSclaudio "for %s/%u", inet_ntoa(kr->r.prefix), 81529b0bbeeSclaudio kr->r.prefixlen); 81629b0bbeeSclaudio return (-1); 81729b0bbeeSclaudio } 81829b0bbeeSclaudio krm->next = kr->next; 81929b0bbeeSclaudio } 820204df0f8Sclaudio 82129b0bbeeSclaudio kr_redist_remove(krm, kr); 822bbb232d2Sclaudio rtlabel_unref(kr->r.rtlabel); 823e2993955Sclaudio 824204df0f8Sclaudio free(kr); 825204df0f8Sclaudio return (0); 826204df0f8Sclaudio } 827204df0f8Sclaudio 828204df0f8Sclaudio void 829204df0f8Sclaudio kroute_clear(void) 830204df0f8Sclaudio { 831204df0f8Sclaudio struct kroute_node *kr; 832204df0f8Sclaudio 833204df0f8Sclaudio while ((kr = RB_MIN(kroute_tree, &krt)) != NULL) 834204df0f8Sclaudio kroute_remove(kr); 835204df0f8Sclaudio } 836204df0f8Sclaudio 837204df0f8Sclaudio struct kif_node * 83873e34765Sclaudio kif_find(u_short ifindex) 839204df0f8Sclaudio { 840204df0f8Sclaudio struct kif_node s; 841204df0f8Sclaudio 842204df0f8Sclaudio bzero(&s, sizeof(s)); 843204df0f8Sclaudio s.k.ifindex = ifindex; 844204df0f8Sclaudio 845204df0f8Sclaudio return (RB_FIND(kif_tree, &kit, &s)); 846204df0f8Sclaudio } 847204df0f8Sclaudio 848e9fa2173Sclaudio struct kif * 84966dd3991Sclaudio kif_findname(char *ifname, struct in_addr addr, struct kif_addr **kap) 850e9fa2173Sclaudio { 851e9fa2173Sclaudio struct kif_node *kif; 85266dd3991Sclaudio struct kif_addr *ka; 853e9fa2173Sclaudio 854e9fa2173Sclaudio RB_FOREACH(kif, kif_tree, &kit) 85566dd3991Sclaudio if (!strcmp(ifname, kif->k.ifname)) { 85666dd3991Sclaudio ka = TAILQ_FIRST(&kif->addrs); 85766dd3991Sclaudio if (addr.s_addr != 0) { 85866dd3991Sclaudio TAILQ_FOREACH(ka, &kif->addrs, entry) { 85966dd3991Sclaudio if (addr.s_addr == ka->addr.s_addr) 86066dd3991Sclaudio break; 86166dd3991Sclaudio } 86266dd3991Sclaudio } 86366dd3991Sclaudio if (kap != NULL) 86466dd3991Sclaudio *kap = ka; 865e9fa2173Sclaudio return (&kif->k); 86666dd3991Sclaudio } 867e9fa2173Sclaudio 868e9fa2173Sclaudio return (NULL); 869e9fa2173Sclaudio } 870e9fa2173Sclaudio 87173e34765Sclaudio struct kif_node * 87273e34765Sclaudio kif_insert(u_short ifindex) 873204df0f8Sclaudio { 87473e34765Sclaudio struct kif_node *kif; 875204df0f8Sclaudio 87673e34765Sclaudio if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) 87773e34765Sclaudio return (NULL); 87873e34765Sclaudio 87973e34765Sclaudio kif->k.ifindex = ifindex; 88073e34765Sclaudio TAILQ_INIT(&kif->addrs); 88173e34765Sclaudio 88273e34765Sclaudio if (RB_INSERT(kif_tree, &kit, kif) != NULL) 88373e34765Sclaudio fatalx("kif_insert: RB_INSERT"); 88473e34765Sclaudio 88573e34765Sclaudio return (kif); 886204df0f8Sclaudio } 887204df0f8Sclaudio 888204df0f8Sclaudio int 889204df0f8Sclaudio kif_remove(struct kif_node *kif) 890204df0f8Sclaudio { 89166dd3991Sclaudio struct kif_addr *ka; 89266dd3991Sclaudio 893204df0f8Sclaudio if (RB_REMOVE(kif_tree, &kit, kif) == NULL) { 894204df0f8Sclaudio log_warnx("RB_REMOVE(kif_tree, &kit, kif)"); 895204df0f8Sclaudio return (-1); 896204df0f8Sclaudio } 897204df0f8Sclaudio 89866dd3991Sclaudio while ((ka = TAILQ_FIRST(&kif->addrs)) != NULL) { 89966dd3991Sclaudio TAILQ_REMOVE(&kif->addrs, ka, entry); 90066dd3991Sclaudio free(ka); 90166dd3991Sclaudio } 902204df0f8Sclaudio free(kif); 903204df0f8Sclaudio return (0); 904204df0f8Sclaudio } 905204df0f8Sclaudio 906204df0f8Sclaudio void 907204df0f8Sclaudio kif_clear(void) 908204df0f8Sclaudio { 909204df0f8Sclaudio struct kif_node *kif; 910204df0f8Sclaudio 911204df0f8Sclaudio while ((kif = RB_MIN(kif_tree, &kit)) != NULL) 912204df0f8Sclaudio kif_remove(kif); 913204df0f8Sclaudio } 914204df0f8Sclaudio 91573e34765Sclaudio struct kif * 91673e34765Sclaudio kif_update(u_short ifindex, int flags, struct if_data *ifd, 91773e34765Sclaudio struct sockaddr_dl *sdl) 91873e34765Sclaudio { 91973e34765Sclaudio struct kif_node *kif; 92073e34765Sclaudio 9211c282279Sclaudio if ((kif = kif_find(ifindex)) == NULL) { 92273e34765Sclaudio if ((kif = kif_insert(ifindex)) == NULL) 92373e34765Sclaudio return (NULL); 9241c282279Sclaudio kif->k.nh_reachable = (flags & IFF_UP) && 9259a2e0324Sclaudio LINK_STATE_IS_UP(ifd->ifi_link_state); 9261c282279Sclaudio } 92773e34765Sclaudio 92873e34765Sclaudio kif->k.flags = flags; 92973e34765Sclaudio kif->k.link_state = ifd->ifi_link_state; 93018ffdd94Sstsp kif->k.if_type = ifd->ifi_type; 93173e34765Sclaudio kif->k.baudrate = ifd->ifi_baudrate; 93273e34765Sclaudio kif->k.mtu = ifd->ifi_mtu; 933358269bbSclaudio kif->k.rdomain = ifd->ifi_rdomain; 93473e34765Sclaudio 93573e34765Sclaudio if (sdl && sdl->sdl_family == AF_LINK) { 93673e34765Sclaudio if (sdl->sdl_nlen >= sizeof(kif->k.ifname)) 93773e34765Sclaudio memcpy(kif->k.ifname, sdl->sdl_data, 93873e34765Sclaudio sizeof(kif->k.ifname) - 1); 93973e34765Sclaudio else if (sdl->sdl_nlen > 0) 94073e34765Sclaudio memcpy(kif->k.ifname, sdl->sdl_data, 94173e34765Sclaudio sdl->sdl_nlen); 94273e34765Sclaudio /* string already terminated via calloc() */ 94373e34765Sclaudio } 94473e34765Sclaudio 94573e34765Sclaudio return (&kif->k); 94673e34765Sclaudio } 94773e34765Sclaudio 948f97d3cd4Sclaudio int 94973e34765Sclaudio kif_validate(u_short ifindex) 950f97d3cd4Sclaudio { 951f97d3cd4Sclaudio struct kif_node *kif; 952f97d3cd4Sclaudio 953f97d3cd4Sclaudio if ((kif = kif_find(ifindex)) == NULL) { 9549edfd913Sclaudio log_warnx("interface with index %u not found", ifindex); 955f97d3cd4Sclaudio return (1); 956f97d3cd4Sclaudio } 957f97d3cd4Sclaudio 958f97d3cd4Sclaudio return (kif->k.nh_reachable); 959f97d3cd4Sclaudio } 960f97d3cd4Sclaudio 961204df0f8Sclaudio struct kroute_node * 962204df0f8Sclaudio kroute_match(in_addr_t key) 963204df0f8Sclaudio { 964204df0f8Sclaudio int i; 965204df0f8Sclaudio struct kroute_node *kr; 966204df0f8Sclaudio 967204df0f8Sclaudio /* we will never match the default route */ 968204df0f8Sclaudio for (i = 32; i > 0; i--) 9691795d796Sclaudio if ((kr = kroute_find(key & prefixlen2mask(i), i, 9701795d796Sclaudio RTP_ANY)) != NULL) 971204df0f8Sclaudio return (kr); 972204df0f8Sclaudio 973204df0f8Sclaudio /* if we don't have a match yet, try to find a default route */ 9741795d796Sclaudio if ((kr = kroute_find(0, 0, RTP_ANY)) != NULL) 975204df0f8Sclaudio return (kr); 976204df0f8Sclaudio 977204df0f8Sclaudio return (NULL); 978204df0f8Sclaudio } 979204df0f8Sclaudio 980204df0f8Sclaudio /* misc */ 981204df0f8Sclaudio int 982204df0f8Sclaudio protect_lo(void) 983204df0f8Sclaudio { 984204df0f8Sclaudio struct kroute_node *kr; 985204df0f8Sclaudio 986204df0f8Sclaudio /* special protection for 127/8 */ 987204df0f8Sclaudio if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { 988204df0f8Sclaudio log_warn("protect_lo"); 989204df0f8Sclaudio return (-1); 990204df0f8Sclaudio } 991ca75fca8Sclaudio kr->r.prefix.s_addr = htonl(INADDR_LOOPBACK & IN_CLASSA_NET); 992204df0f8Sclaudio kr->r.prefixlen = 8; 993204df0f8Sclaudio kr->r.flags = F_KERNEL|F_CONNECTED; 994204df0f8Sclaudio 995204df0f8Sclaudio if (RB_INSERT(kroute_tree, &krt, kr) != NULL) 996204df0f8Sclaudio free(kr); /* kernel route already there, no problem */ 997204df0f8Sclaudio 998204df0f8Sclaudio return (0); 999204df0f8Sclaudio } 1000204df0f8Sclaudio 1001204df0f8Sclaudio u_int8_t 1002204df0f8Sclaudio prefixlen_classful(in_addr_t ina) 1003204df0f8Sclaudio { 1004204df0f8Sclaudio /* it hurt to write this. */ 1005204df0f8Sclaudio 1006204df0f8Sclaudio if (ina >= 0xf0000000U) /* class E */ 1007204df0f8Sclaudio return (32); 1008204df0f8Sclaudio else if (ina >= 0xe0000000U) /* class D */ 1009204df0f8Sclaudio return (4); 1010204df0f8Sclaudio else if (ina >= 0xc0000000U) /* class C */ 1011204df0f8Sclaudio return (24); 1012204df0f8Sclaudio else if (ina >= 0x80000000U) /* class B */ 1013204df0f8Sclaudio return (16); 1014204df0f8Sclaudio else /* class A */ 1015204df0f8Sclaudio return (8); 1016204df0f8Sclaudio } 1017204df0f8Sclaudio 1018204df0f8Sclaudio u_int8_t 1019204df0f8Sclaudio mask2prefixlen(in_addr_t ina) 1020204df0f8Sclaudio { 1021204df0f8Sclaudio if (ina == 0) 1022204df0f8Sclaudio return (0); 1023204df0f8Sclaudio else 1024204df0f8Sclaudio return (33 - ffs(ntohl(ina))); 1025204df0f8Sclaudio } 1026204df0f8Sclaudio 1027204df0f8Sclaudio in_addr_t 1028204df0f8Sclaudio prefixlen2mask(u_int8_t prefixlen) 1029204df0f8Sclaudio { 1030204df0f8Sclaudio if (prefixlen == 0) 1031204df0f8Sclaudio return (0); 1032204df0f8Sclaudio 103315ab6e8cSclaudio return (htonl(0xffffffff << (32 - prefixlen))); 1034204df0f8Sclaudio } 1035204df0f8Sclaudio 103648305c21Sclaudio #define ROUNDUP(a) \ 1037064707deSfriehm ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 1038204df0f8Sclaudio 1039204df0f8Sclaudio void 1040204df0f8Sclaudio get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 1041204df0f8Sclaudio { 1042204df0f8Sclaudio int i; 1043204df0f8Sclaudio 1044204df0f8Sclaudio for (i = 0; i < RTAX_MAX; i++) { 1045204df0f8Sclaudio if (addrs & (1 << i)) { 1046204df0f8Sclaudio rti_info[i] = sa; 1047204df0f8Sclaudio sa = (struct sockaddr *)((char *)(sa) + 104848305c21Sclaudio ROUNDUP(sa->sa_len)); 1049204df0f8Sclaudio } else 1050204df0f8Sclaudio rti_info[i] = NULL; 1051204df0f8Sclaudio } 1052204df0f8Sclaudio } 1053204df0f8Sclaudio 1054204df0f8Sclaudio void 10551c282279Sclaudio if_change(u_short ifindex, int flags, struct if_data *ifd, 10561c282279Sclaudio struct sockaddr_dl *sdl) 1057204df0f8Sclaudio { 105829b0bbeeSclaudio struct kroute_node *kr, *tkr; 105973e34765Sclaudio struct kif *kif; 1060204df0f8Sclaudio u_int8_t reachable; 1061204df0f8Sclaudio 10621c282279Sclaudio if ((kif = kif_update(ifindex, flags, ifd, sdl)) == NULL) { 106373e34765Sclaudio log_warn("if_change: kif_update(%u)", ifindex); 1064204df0f8Sclaudio return; 1065204df0f8Sclaudio } 1066204df0f8Sclaudio 10673a9b53d2Sbenno /* notify ospfe about interface link state */ 10683a9b53d2Sbenno main_imsg_compose_ospfe(IMSG_IFINFO, 0, kif, sizeof(struct kif)); 10693a9b53d2Sbenno 107073e34765Sclaudio reachable = (kif->flags & IFF_UP) && 10719a2e0324Sclaudio LINK_STATE_IS_UP(kif->link_state); 1072204df0f8Sclaudio 107373e34765Sclaudio if (reachable == kif->nh_reachable) 1074204df0f8Sclaudio return; /* nothing changed wrt nexthop validity */ 1075204df0f8Sclaudio 107673e34765Sclaudio kif->nh_reachable = reachable; 107744cb7d8eSclaudio 107844cb7d8eSclaudio /* update redistribute list */ 107929b0bbeeSclaudio RB_FOREACH(kr, kroute_tree, &krt) { 108029b0bbeeSclaudio for (tkr = kr; tkr != NULL; tkr = tkr->next) { 108129b0bbeeSclaudio if (tkr->r.ifindex == ifindex) { 108244cb7d8eSclaudio if (reachable) 108329b0bbeeSclaudio tkr->r.flags &= ~F_DOWN; 108444cb7d8eSclaudio else 108529b0bbeeSclaudio tkr->r.flags |= F_DOWN; 108644cb7d8eSclaudio 108744cb7d8eSclaudio } 1088204df0f8Sclaudio } 108929b0bbeeSclaudio kr_redistribute(kr); 109029b0bbeeSclaudio } 109173e34765Sclaudio } 109229b0bbeeSclaudio 109373e34765Sclaudio void 109473e34765Sclaudio if_newaddr(u_short ifindex, struct sockaddr_in *ifa, struct sockaddr_in *mask, 109573e34765Sclaudio struct sockaddr_in *brd) 109673e34765Sclaudio { 109773e34765Sclaudio struct kif_node *kif; 109873e34765Sclaudio struct kif_addr *ka; 10992dc8313fSremi struct ifaddrchange ifn; 110073e34765Sclaudio 110173e34765Sclaudio if (ifa == NULL || ifa->sin_family != AF_INET) 110273e34765Sclaudio return; 110373e34765Sclaudio if ((kif = kif_find(ifindex)) == NULL) { 1104fae4ffeaSderaadt log_warnx("if_newaddr: corresponding if %d not found", ifindex); 110572c35befSnorby return; 110673e34765Sclaudio } 110773e34765Sclaudio if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL) 110873e34765Sclaudio fatal("if_newaddr"); 110973e34765Sclaudio ka->addr = ifa->sin_addr; 111073e34765Sclaudio if (mask) 111173e34765Sclaudio ka->mask = mask->sin_addr; 111273e34765Sclaudio else 111373e34765Sclaudio ka->mask.s_addr = INADDR_NONE; 111473e34765Sclaudio if (brd) 111573e34765Sclaudio ka->dstbrd = brd->sin_addr; 111673e34765Sclaudio else 111773e34765Sclaudio ka->dstbrd.s_addr = INADDR_NONE; 111873e34765Sclaudio 111973e34765Sclaudio TAILQ_INSERT_TAIL(&kif->addrs, ka, entry); 11202dc8313fSremi 11212dc8313fSremi ifn.addr = ka->addr; 11222dc8313fSremi ifn.mask = ka->mask; 11232dc8313fSremi ifn.dst = ka->dstbrd; 11242dc8313fSremi ifn.ifindex = ifindex; 11252dc8313fSremi main_imsg_compose_ospfe(IMSG_IFADDRADD, 0, &ifn, sizeof(ifn)); 112629b0bbeeSclaudio } 1127204df0f8Sclaudio 1128204df0f8Sclaudio void 1129a60d5a8aSclaudio if_deladdr(u_short ifindex, struct sockaddr_in *ifa, struct sockaddr_in *mask, 1130a60d5a8aSclaudio struct sockaddr_in *brd) 1131a60d5a8aSclaudio { 1132a60d5a8aSclaudio struct kif_node *kif; 1133a60d5a8aSclaudio struct kif_addr *ka, *nka; 11342dc8313fSremi struct ifaddrchange ifc; 1135a60d5a8aSclaudio 1136a60d5a8aSclaudio if (ifa == NULL || ifa->sin_family != AF_INET) 1137a60d5a8aSclaudio return; 1138a60d5a8aSclaudio if ((kif = kif_find(ifindex)) == NULL) { 1139fae4ffeaSderaadt log_warnx("if_deladdr: corresponding if %d not found", ifindex); 1140a60d5a8aSclaudio return; 1141a60d5a8aSclaudio } 1142a60d5a8aSclaudio 1143a60d5a8aSclaudio for (ka = TAILQ_FIRST(&kif->addrs); ka != NULL; ka = nka) { 1144a60d5a8aSclaudio nka = TAILQ_NEXT(ka, entry); 1145a60d5a8aSclaudio 1146a60d5a8aSclaudio if (ka->addr.s_addr == ifa->sin_addr.s_addr) { 1147a60d5a8aSclaudio TAILQ_REMOVE(&kif->addrs, ka, entry); 1148a60d5a8aSclaudio ifc.addr = ifa->sin_addr; 1149a60d5a8aSclaudio ifc.ifindex = ifindex; 1150a60d5a8aSclaudio main_imsg_compose_ospfe(IMSG_IFADDRDEL, 0, &ifc, 1151a60d5a8aSclaudio sizeof(ifc)); 1152a60d5a8aSclaudio free(ka); 1153a60d5a8aSclaudio return; 1154a60d5a8aSclaudio } 1155a60d5a8aSclaudio } 1156a60d5a8aSclaudio } 1157a60d5a8aSclaudio 1158a60d5a8aSclaudio void 1159204df0f8Sclaudio if_announce(void *msg) 1160204df0f8Sclaudio { 1161204df0f8Sclaudio struct if_announcemsghdr *ifan; 1162204df0f8Sclaudio struct kif_node *kif; 1163204df0f8Sclaudio 1164204df0f8Sclaudio ifan = msg; 1165204df0f8Sclaudio 1166204df0f8Sclaudio switch (ifan->ifan_what) { 1167204df0f8Sclaudio case IFAN_ARRIVAL: 116873e34765Sclaudio kif = kif_insert(ifan->ifan_index); 1169204df0f8Sclaudio strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname)); 1170204df0f8Sclaudio break; 1171204df0f8Sclaudio case IFAN_DEPARTURE: 1172204df0f8Sclaudio kif = kif_find(ifan->ifan_index); 1173*95d3cd23Santon if (kif != NULL) 1174204df0f8Sclaudio kif_remove(kif); 1175204df0f8Sclaudio break; 1176204df0f8Sclaudio } 1177204df0f8Sclaudio } 1178204df0f8Sclaudio 1179204df0f8Sclaudio /* rtsock */ 1180204df0f8Sclaudio int 1181204df0f8Sclaudio send_rtmsg(int fd, int action, struct kroute *kroute) 1182204df0f8Sclaudio { 1183fcb4545bSreyk struct iovec iov[5]; 1184204df0f8Sclaudio struct rt_msghdr hdr; 1185204df0f8Sclaudio struct sockaddr_in prefix; 1186204df0f8Sclaudio struct sockaddr_in nexthop; 1187204df0f8Sclaudio struct sockaddr_in mask; 1188fcb4545bSreyk struct sockaddr_rtlabel sa_rl; 11892dbdedf7Sclaudio int iovcnt = 0; 1190fcb4545bSreyk const char *label; 1191204df0f8Sclaudio 1192204df0f8Sclaudio if (kr_state.fib_sync == 0) 1193204df0f8Sclaudio return (0); 1194204df0f8Sclaudio 11952dbdedf7Sclaudio /* initialize header */ 11962dbdedf7Sclaudio bzero(&hdr, sizeof(hdr)); 11972dbdedf7Sclaudio hdr.rtm_version = RTM_VERSION; 11982dbdedf7Sclaudio hdr.rtm_type = action; 11994c260f66Sremi hdr.rtm_priority = kr_state.fib_prio; 1200e4caa3d9Sclaudio hdr.rtm_tableid = kr_state.rdomain; /* rtableid */ 12011795d796Sclaudio if (action == RTM_CHANGE) 12021795d796Sclaudio hdr.rtm_fmask = RTF_REJECT|RTF_BLACKHOLE; 12031795d796Sclaudio else 12041795d796Sclaudio hdr.rtm_flags = RTF_MPATH; 12052dbdedf7Sclaudio hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ 12062dbdedf7Sclaudio hdr.rtm_msglen = sizeof(hdr); 12072dbdedf7Sclaudio /* adjust iovec */ 12082dbdedf7Sclaudio iov[iovcnt].iov_base = &hdr; 12092dbdedf7Sclaudio iov[iovcnt++].iov_len = sizeof(hdr); 1210204df0f8Sclaudio 12112dbdedf7Sclaudio bzero(&prefix, sizeof(prefix)); 12122dbdedf7Sclaudio prefix.sin_len = sizeof(prefix); 12132dbdedf7Sclaudio prefix.sin_family = AF_INET; 12142dbdedf7Sclaudio prefix.sin_addr.s_addr = kroute->prefix.s_addr; 12152dbdedf7Sclaudio /* adjust header */ 12162dbdedf7Sclaudio hdr.rtm_addrs |= RTA_DST; 12172dbdedf7Sclaudio hdr.rtm_msglen += sizeof(prefix); 12182dbdedf7Sclaudio /* adjust iovec */ 12192dbdedf7Sclaudio iov[iovcnt].iov_base = &prefix; 12202dbdedf7Sclaudio iov[iovcnt++].iov_len = sizeof(prefix); 1221204df0f8Sclaudio 12222dbdedf7Sclaudio if (kroute->nexthop.s_addr != 0) { 12232dbdedf7Sclaudio bzero(&nexthop, sizeof(nexthop)); 12242dbdedf7Sclaudio nexthop.sin_len = sizeof(nexthop); 12252dbdedf7Sclaudio nexthop.sin_family = AF_INET; 12262dbdedf7Sclaudio nexthop.sin_addr.s_addr = kroute->nexthop.s_addr; 12272dbdedf7Sclaudio /* adjust header */ 12282dbdedf7Sclaudio hdr.rtm_flags |= RTF_GATEWAY; 12292dbdedf7Sclaudio hdr.rtm_addrs |= RTA_GATEWAY; 12302dbdedf7Sclaudio hdr.rtm_msglen += sizeof(nexthop); 12312dbdedf7Sclaudio /* adjust iovec */ 12322dbdedf7Sclaudio iov[iovcnt].iov_base = &nexthop; 12332dbdedf7Sclaudio iov[iovcnt++].iov_len = sizeof(nexthop); 12342dbdedf7Sclaudio } 12352dbdedf7Sclaudio 12362dbdedf7Sclaudio bzero(&mask, sizeof(mask)); 12372dbdedf7Sclaudio mask.sin_len = sizeof(mask); 12382dbdedf7Sclaudio mask.sin_family = AF_INET; 12392dbdedf7Sclaudio mask.sin_addr.s_addr = prefixlen2mask(kroute->prefixlen); 12402dbdedf7Sclaudio /* adjust header */ 12412dbdedf7Sclaudio hdr.rtm_addrs |= RTA_NETMASK; 12422dbdedf7Sclaudio hdr.rtm_msglen += sizeof(mask); 12432dbdedf7Sclaudio /* adjust iovec */ 12442dbdedf7Sclaudio iov[iovcnt].iov_base = &mask; 12452dbdedf7Sclaudio iov[iovcnt++].iov_len = sizeof(mask); 1246204df0f8Sclaudio 1247fcb4545bSreyk if (kroute->rtlabel != 0) { 1248fcb4545bSreyk sa_rl.sr_len = sizeof(sa_rl); 1249fcb4545bSreyk sa_rl.sr_family = AF_UNSPEC; 1250fcb4545bSreyk label = rtlabel_id2name(kroute->rtlabel); 1251fcb4545bSreyk if (strlcpy(sa_rl.sr_label, label, 1252fcb4545bSreyk sizeof(sa_rl.sr_label)) >= sizeof(sa_rl.sr_label)) { 1253fcb4545bSreyk log_warnx("send_rtmsg: invalid rtlabel"); 1254fcb4545bSreyk return (-1); 1255fcb4545bSreyk } 1256fcb4545bSreyk /* adjust header */ 1257fcb4545bSreyk hdr.rtm_addrs |= RTA_LABEL; 1258fcb4545bSreyk hdr.rtm_msglen += sizeof(sa_rl); 1259fcb4545bSreyk /* adjust iovec */ 1260fcb4545bSreyk iov[iovcnt].iov_base = &sa_rl; 1261fcb4545bSreyk iov[iovcnt++].iov_len = sizeof(sa_rl); 1262fcb4545bSreyk } 1263fcb4545bSreyk 1264204df0f8Sclaudio retry: 12652dbdedf7Sclaudio if (writev(fd, iov, iovcnt) == -1) { 12664324a051Sbluhm if (errno == ESRCH) { 12672dbdedf7Sclaudio if (hdr.rtm_type == RTM_CHANGE) { 12682dbdedf7Sclaudio hdr.rtm_type = RTM_ADD; 1269204df0f8Sclaudio goto retry; 12702dbdedf7Sclaudio } else if (hdr.rtm_type == RTM_DELETE) { 1271204df0f8Sclaudio log_info("route %s/%u vanished before delete", 1272204df0f8Sclaudio inet_ntoa(kroute->prefix), 1273204df0f8Sclaudio kroute->prefixlen); 1274204df0f8Sclaudio return (0); 1275204df0f8Sclaudio } 1276204df0f8Sclaudio } 12774324a051Sbluhm log_warn("send_rtmsg: action %u, prefix %s/%u", hdr.rtm_type, 12784324a051Sbluhm inet_ntoa(kroute->prefix), kroute->prefixlen); 12794324a051Sbluhm return (0); 1280204df0f8Sclaudio } 1281204df0f8Sclaudio 1282204df0f8Sclaudio return (0); 1283204df0f8Sclaudio } 1284204df0f8Sclaudio 1285204df0f8Sclaudio int 1286204df0f8Sclaudio fetchtable(void) 1287204df0f8Sclaudio { 1288204df0f8Sclaudio size_t len; 1289836113cfShenning int mib[7]; 12905fba50a6Sclaudio char *buf; 12915fba50a6Sclaudio int rv; 1292119f0f1dSdlg 1293119f0f1dSdlg mib[0] = CTL_NET; 1294149dc9fcSguenther mib[1] = PF_ROUTE; 1295119f0f1dSdlg mib[2] = 0; 1296119f0f1dSdlg mib[3] = AF_INET; 1297119f0f1dSdlg mib[4] = NET_RT_DUMP; 1298119f0f1dSdlg mib[5] = 0; 1299119f0f1dSdlg mib[6] = kr_state.rdomain; /* rtableid */ 1300119f0f1dSdlg 1301119f0f1dSdlg if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) { 1302119f0f1dSdlg log_warn("sysctl"); 1303119f0f1dSdlg return (-1); 1304119f0f1dSdlg } 1305119f0f1dSdlg if ((buf = malloc(len)) == NULL) { 1306119f0f1dSdlg log_warn("fetchtable"); 1307119f0f1dSdlg return (-1); 1308119f0f1dSdlg } 1309119f0f1dSdlg if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) { 1310119f0f1dSdlg log_warn("sysctl"); 1311119f0f1dSdlg free(buf); 1312119f0f1dSdlg return (-1); 1313119f0f1dSdlg } 1314119f0f1dSdlg 1315c2a29d3bSdlg rv = rtmsg_process(buf, len); 1316119f0f1dSdlg free(buf); 1317c2a29d3bSdlg 1318119f0f1dSdlg return (rv); 1319119f0f1dSdlg } 1320119f0f1dSdlg 1321119f0f1dSdlg int 132273e34765Sclaudio fetchifs(u_short ifindex) 1323204df0f8Sclaudio { 1324204df0f8Sclaudio size_t len; 1325204df0f8Sclaudio int mib[6]; 13269d7a9d5fSdlg char *buf; 13279d7a9d5fSdlg int rv; 1328204df0f8Sclaudio 1329204df0f8Sclaudio mib[0] = CTL_NET; 1330149dc9fcSguenther mib[1] = PF_ROUTE; 1331204df0f8Sclaudio mib[2] = 0; 1332204df0f8Sclaudio mib[3] = AF_INET; 1333204df0f8Sclaudio mib[4] = NET_RT_IFLIST; 1334204df0f8Sclaudio mib[5] = ifindex; 1335204df0f8Sclaudio 1336204df0f8Sclaudio if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { 1337204df0f8Sclaudio log_warn("sysctl"); 1338204df0f8Sclaudio return (-1); 1339204df0f8Sclaudio } 1340204df0f8Sclaudio if ((buf = malloc(len)) == NULL) { 1341204df0f8Sclaudio log_warn("fetchif"); 1342204df0f8Sclaudio return (-1); 1343204df0f8Sclaudio } 1344204df0f8Sclaudio if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) { 1345204df0f8Sclaudio log_warn("sysctl"); 1346204df0f8Sclaudio free(buf); 1347204df0f8Sclaudio return (-1); 1348204df0f8Sclaudio } 1349204df0f8Sclaudio 13509d7a9d5fSdlg rv = rtmsg_process(buf, len); 1351204df0f8Sclaudio free(buf); 13529d7a9d5fSdlg 13539d7a9d5fSdlg return (rv); 1354204df0f8Sclaudio } 1355204df0f8Sclaudio 1356204df0f8Sclaudio int 1357204df0f8Sclaudio dispatch_rtmsg(void) 1358204df0f8Sclaudio { 1359204df0f8Sclaudio char buf[RT_BUF_SIZE]; 1360204df0f8Sclaudio ssize_t n; 13619d7a9d5fSdlg 13629d7a9d5fSdlg if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) { 1363f2c05001Sclaudio if (errno == EAGAIN || errno == EINTR) 1364f2c05001Sclaudio return (0); 13659d7a9d5fSdlg log_warn("dispatch_rtmsg: read error"); 13669d7a9d5fSdlg return (-1); 13679d7a9d5fSdlg } 13689d7a9d5fSdlg 13699d7a9d5fSdlg if (n == 0) { 13709d7a9d5fSdlg log_warnx("routing socket closed"); 13719d7a9d5fSdlg return (-1); 13729d7a9d5fSdlg } 13739d7a9d5fSdlg 13749d7a9d5fSdlg return (rtmsg_process(buf, n)); 13759d7a9d5fSdlg } 13769d7a9d5fSdlg 13779d7a9d5fSdlg int 137858ef7452Sclaudio rtmsg_process(char *buf, size_t len) 13799d7a9d5fSdlg { 1380204df0f8Sclaudio struct rt_msghdr *rtm; 1381204df0f8Sclaudio struct if_msghdr ifm; 138273e34765Sclaudio struct ifa_msghdr *ifam; 1383204df0f8Sclaudio struct sockaddr *sa, *rti_info[RTAX_MAX]; 1384204df0f8Sclaudio struct sockaddr_in *sa_in; 1385bbb232d2Sclaudio struct sockaddr_rtlabel *label; 138629b0bbeeSclaudio struct kroute_node *kr, *okr; 1387204df0f8Sclaudio struct in_addr prefix, nexthop; 13881795d796Sclaudio u_int8_t prefixlen, prio; 138929b0bbeeSclaudio int flags, mpath; 1390f08c543aShenning u_short ifindex = 0; 1391fddf39b9Ssthen int rv, delay; 1392204df0f8Sclaudio 139358ef7452Sclaudio size_t offset; 13949d7a9d5fSdlg char *next; 1395204df0f8Sclaudio 13969d7a9d5fSdlg for (offset = 0; offset < len; offset += rtm->rtm_msglen) { 13979d7a9d5fSdlg next = buf + offset; 1398204df0f8Sclaudio rtm = (struct rt_msghdr *)next; 1399242fc2efSclaudio if (len < offset + sizeof(u_short) || 140058ef7452Sclaudio len < offset + rtm->rtm_msglen) 14018a4d3917Sdenis fatalx("%s: partial rtm in buffer", __func__); 140218e1f14bSclaudio if (rtm->rtm_version != RTM_VERSION) 140318e1f14bSclaudio continue; 1404204df0f8Sclaudio 1405204df0f8Sclaudio prefix.s_addr = 0; 1406204df0f8Sclaudio prefixlen = 0; 1407204df0f8Sclaudio nexthop.s_addr = 0; 140829b0bbeeSclaudio mpath = 0; 14091795d796Sclaudio prio = 0; 141033afd907Sclaudio flags = F_KERNEL; 1411204df0f8Sclaudio 14125fba50a6Sclaudio sa = (struct sockaddr *)(next + rtm->rtm_hdrlen); 14135fba50a6Sclaudio get_rtaddrs(rtm->rtm_addrs, sa, rti_info); 14145fba50a6Sclaudio 1415c2a29d3bSdlg switch (rtm->rtm_type) { 1416c2a29d3bSdlg case RTM_ADD: 1417c2a29d3bSdlg case RTM_GET: 1418c2a29d3bSdlg case RTM_CHANGE: 1419c2a29d3bSdlg case RTM_DELETE: 1420ade6468bSsthen if (rtm->rtm_errno) /* failed attempts... */ 1421ade6468bSsthen continue; 1422ade6468bSsthen 1423e4caa3d9Sclaudio if (rtm->rtm_tableid != kr_state.rdomain) 1424a8b4fd7dSclaudio continue; 1425a8b4fd7dSclaudio 1426bef2e7d6Sclaudio if (rtm->rtm_type == RTM_GET && 1427c1040092Sclaudio rtm->rtm_pid != kr_state.pid) 1428bef2e7d6Sclaudio continue; 1429bef2e7d6Sclaudio 1430c2a29d3bSdlg if ((sa = rti_info[RTAX_DST]) == NULL) 1431204df0f8Sclaudio continue; 1432204df0f8Sclaudio 143334635c53Sclaudio /* Skip ARP/ND cache and broadcast routes. */ 143434635c53Sclaudio if (rtm->rtm_flags & (RTF_LLINFO|RTF_BROADCAST)) 1435204df0f8Sclaudio continue; 1436f08c543aShenning 143729b0bbeeSclaudio if (rtm->rtm_flags & RTF_MPATH) 143829b0bbeeSclaudio mpath = 1; 14391795d796Sclaudio prio = rtm->rtm_priority; 14404c260f66Sremi flags = (prio == kr_state.fib_prio) ? 1441c2a29d3bSdlg F_OSPFD_INSERTED : F_KERNEL; 14421795d796Sclaudio 1443204df0f8Sclaudio switch (sa->sa_family) { 1444204df0f8Sclaudio case AF_INET: 1445204df0f8Sclaudio prefix.s_addr = 1446204df0f8Sclaudio ((struct sockaddr_in *)sa)->sin_addr.s_addr; 1447204df0f8Sclaudio sa_in = (struct sockaddr_in *) 1448204df0f8Sclaudio rti_info[RTAX_NETMASK]; 1449204df0f8Sclaudio if (sa_in != NULL) { 1450204df0f8Sclaudio if (sa_in->sin_len != 0) 1451204df0f8Sclaudio prefixlen = mask2prefixlen( 1452204df0f8Sclaudio sa_in->sin_addr.s_addr); 1453204df0f8Sclaudio } else if (rtm->rtm_flags & RTF_HOST) 1454204df0f8Sclaudio prefixlen = 32; 1455204df0f8Sclaudio else 1456204df0f8Sclaudio prefixlen = 1457204df0f8Sclaudio prefixlen_classful(prefix.s_addr); 1458e2993955Sclaudio if (rtm->rtm_flags & RTF_STATIC) 1459e2993955Sclaudio flags |= F_STATIC; 1460d9eec83cSclaudio if (rtm->rtm_flags & RTF_BLACKHOLE) 1461d9eec83cSclaudio flags |= F_BLACKHOLE; 1462d9eec83cSclaudio if (rtm->rtm_flags & RTF_REJECT) 1463d9eec83cSclaudio flags |= F_REJECT; 146440df8bcdSclaudio if (rtm->rtm_flags & RTF_DYNAMIC) 146540df8bcdSclaudio flags |= F_DYNAMIC; 1466204df0f8Sclaudio break; 1467204df0f8Sclaudio default: 1468204df0f8Sclaudio continue; 1469204df0f8Sclaudio } 1470204df0f8Sclaudio 147107db309bSclaudio ifindex = rtm->rtm_index; 1472f08c543aShenning if ((sa = rti_info[RTAX_GATEWAY]) != NULL) { 1473204df0f8Sclaudio switch (sa->sa_family) { 1474204df0f8Sclaudio case AF_INET: 1475c90ecb6bSfriehm if (rtm->rtm_flags & RTF_CONNECTED) 147634635c53Sclaudio flags |= F_CONNECTED; 147734635c53Sclaudio 1478a66fd4eeShenning nexthop.s_addr = ((struct 1479a66fd4eeShenning sockaddr_in *)sa)->sin_addr.s_addr; 1480204df0f8Sclaudio break; 1481204df0f8Sclaudio case AF_LINK: 148234635c53Sclaudio /* 148334635c53Sclaudio * Traditional BSD connected routes have 148434635c53Sclaudio * a gateway of type AF_LINK. 148534635c53Sclaudio */ 1486204df0f8Sclaudio flags |= F_CONNECTED; 1487204df0f8Sclaudio break; 1488204df0f8Sclaudio } 1489f08c543aShenning } 1490f08c543aShenning } 1491204df0f8Sclaudio 1492204df0f8Sclaudio switch (rtm->rtm_type) { 1493204df0f8Sclaudio case RTM_ADD: 1494c2a29d3bSdlg case RTM_GET: 1495204df0f8Sclaudio case RTM_CHANGE: 1496204df0f8Sclaudio if (nexthop.s_addr == 0 && !(flags & F_CONNECTED)) { 1497c2a29d3bSdlg log_warnx("no nexthop for %s/%u", 1498204df0f8Sclaudio inet_ntoa(prefix), prefixlen); 1499204df0f8Sclaudio continue; 1500204df0f8Sclaudio } 1501204df0f8Sclaudio 15021795d796Sclaudio if ((okr = kroute_find(prefix.s_addr, prefixlen, prio)) 15031795d796Sclaudio != NULL) { 150429b0bbeeSclaudio /* get the correct route */ 150529b0bbeeSclaudio kr = okr; 15064c260f66Sremi if ((mpath || prio == kr_state.fib_prio) && 1507c2a29d3bSdlg (kr = kroute_matchgw(okr, nexthop)) == 1508c2a29d3bSdlg NULL) { 15098a4d3917Sdenis log_warnx("%s: mpath route not found", 15108a4d3917Sdenis __func__); 151129b0bbeeSclaudio /* add routes we missed out earlier */ 151229b0bbeeSclaudio goto add; 151329b0bbeeSclaudio } 151429b0bbeeSclaudio 1515bbb232d2Sclaudio if (kr->r.flags & F_REDISTRIBUTED) 1516bbb232d2Sclaudio flags |= F_REDISTRIBUTED; 1517204df0f8Sclaudio kr->r.nexthop.s_addr = nexthop.s_addr; 1518204df0f8Sclaudio kr->r.flags = flags; 15191446e0e7Sclaudio kr->r.ifindex = ifindex; 152044cb7d8eSclaudio 1521bbb232d2Sclaudio rtlabel_unref(kr->r.rtlabel); 1522bbb232d2Sclaudio kr->r.rtlabel = 0; 1523fcb4545bSreyk kr->r.ext_tag = 0; 1524bbb232d2Sclaudio if ((label = (struct sockaddr_rtlabel *) 1525fcb4545bSreyk rti_info[RTAX_LABEL]) != NULL) { 1526bbb232d2Sclaudio kr->r.rtlabel = 1527bbb232d2Sclaudio rtlabel_name2id(label->sr_label); 1528fcb4545bSreyk kr->r.ext_tag = 1529fcb4545bSreyk rtlabel_id2tag(kr->r.rtlabel); 1530fcb4545bSreyk } 1531bbb232d2Sclaudio 153244cb7d8eSclaudio if (kif_validate(kr->r.ifindex)) 153344cb7d8eSclaudio kr->r.flags &= ~F_DOWN; 153444cb7d8eSclaudio else 153544cb7d8eSclaudio kr->r.flags |= F_DOWN; 153644cb7d8eSclaudio 1537e2993955Sclaudio /* just readd, the RDE will care */ 1538c2a29d3bSdlg kr->serial = kr_state.fib_serial; 15399ae468ceSclaudio kr_redistribute(okr); 1540204df0f8Sclaudio } else { 154129b0bbeeSclaudio add: 1542204df0f8Sclaudio if ((kr = calloc(1, 1543204df0f8Sclaudio sizeof(struct kroute_node))) == NULL) { 15448a4d3917Sdenis log_warn("%s: calloc", __func__); 1545412a8077Sdlg return (-1); 1546204df0f8Sclaudio } 1547c2a29d3bSdlg 1548204df0f8Sclaudio kr->r.prefix.s_addr = prefix.s_addr; 1549204df0f8Sclaudio kr->r.prefixlen = prefixlen; 1550204df0f8Sclaudio kr->r.nexthop.s_addr = nexthop.s_addr; 1551204df0f8Sclaudio kr->r.flags = flags; 1552204df0f8Sclaudio kr->r.ifindex = ifindex; 15531795d796Sclaudio kr->r.priority = prio; 1554204df0f8Sclaudio 15554c260f66Sremi if (rtm->rtm_priority == kr_state.fib_prio) { 1556c2a29d3bSdlg log_warnx("alien OSPF route %s/%d", 1557c2a29d3bSdlg inet_ntoa(prefix), prefixlen); 1558c2a29d3bSdlg rv = send_rtmsg(kr_state.fd, 1559c2a29d3bSdlg RTM_DELETE, &kr->r); 1560c2a29d3bSdlg free(kr); 1561c2a29d3bSdlg if (rv == -1) 1562412a8077Sdlg return (-1); 1563c2a29d3bSdlg } else { 1564bbb232d2Sclaudio if ((label = (struct sockaddr_rtlabel *) 1565fcb4545bSreyk rti_info[RTAX_LABEL]) != NULL) { 1566bbb232d2Sclaudio kr->r.rtlabel = 1567c2a29d3bSdlg rtlabel_name2id( 1568c2a29d3bSdlg label->sr_label); 1569fcb4545bSreyk kr->r.ext_tag = 1570c2a29d3bSdlg rtlabel_id2tag( 1571c2a29d3bSdlg kr->r.rtlabel); 1572fcb4545bSreyk } 1573bbb232d2Sclaudio 1574204df0f8Sclaudio kroute_insert(kr); 1575204df0f8Sclaudio } 1576c2a29d3bSdlg } 1577204df0f8Sclaudio break; 1578204df0f8Sclaudio case RTM_DELETE: 15791795d796Sclaudio if ((kr = kroute_find(prefix.s_addr, prefixlen, prio)) 15801795d796Sclaudio == NULL) 1581204df0f8Sclaudio continue; 1582204df0f8Sclaudio if (!(kr->r.flags & F_KERNEL)) 1583204df0f8Sclaudio continue; 158429b0bbeeSclaudio /* get the correct route */ 158529b0bbeeSclaudio okr = kr; 158629b0bbeeSclaudio if (mpath && 158729b0bbeeSclaudio (kr = kroute_matchgw(kr, nexthop)) == NULL) { 15888a4d3917Sdenis log_warnx("%s: mpath route not found", 15898a4d3917Sdenis __func__); 159029b0bbeeSclaudio return (-1); 159129b0bbeeSclaudio } 1592204df0f8Sclaudio if (kroute_remove(kr) == -1) 1593204df0f8Sclaudio return (-1); 1594204df0f8Sclaudio break; 1595204df0f8Sclaudio case RTM_IFINFO: 1596204df0f8Sclaudio memcpy(&ifm, next, sizeof(ifm)); 15971c282279Sclaudio if_change(ifm.ifm_index, ifm.ifm_flags, &ifm.ifm_data, 15981c282279Sclaudio (struct sockaddr_dl *)rti_info[RTAX_IFP]); 1599204df0f8Sclaudio break; 160073e34765Sclaudio case RTM_NEWADDR: 160173e34765Sclaudio ifam = (struct ifa_msghdr *)rtm; 160273e34765Sclaudio if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | 160373e34765Sclaudio RTA_BRD)) == 0) 160473e34765Sclaudio break; 160573e34765Sclaudio 160673e34765Sclaudio if_newaddr(ifam->ifam_index, 160773e34765Sclaudio (struct sockaddr_in *)rti_info[RTAX_IFA], 160873e34765Sclaudio (struct sockaddr_in *)rti_info[RTAX_NETMASK], 160973e34765Sclaudio (struct sockaddr_in *)rti_info[RTAX_BRD]); 161073e34765Sclaudio break; 1611a60d5a8aSclaudio case RTM_DELADDR: 1612a60d5a8aSclaudio ifam = (struct ifa_msghdr *)rtm; 1613a60d5a8aSclaudio if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | 1614a60d5a8aSclaudio RTA_BRD)) == 0) 1615a60d5a8aSclaudio break; 1616a60d5a8aSclaudio 1617a60d5a8aSclaudio if_deladdr(ifam->ifam_index, 1618a60d5a8aSclaudio (struct sockaddr_in *)rti_info[RTAX_IFA], 1619a60d5a8aSclaudio (struct sockaddr_in *)rti_info[RTAX_NETMASK], 1620a60d5a8aSclaudio (struct sockaddr_in *)rti_info[RTAX_BRD]); 1621a60d5a8aSclaudio break; 1622204df0f8Sclaudio case RTM_IFANNOUNCE: 1623204df0f8Sclaudio if_announce(next); 1624204df0f8Sclaudio break; 1625fa19e37cSdlg case RTM_DESYNC: 1626fa19e37cSdlg /* 1627fddf39b9Ssthen * We lost some routing packets. Schedule a reload 1628fddf39b9Ssthen * of the kernel route/interface information. 1629fa19e37cSdlg */ 1630fddf39b9Ssthen if (kr_state.reload_state == KR_RELOAD_IDLE) { 1631fddf39b9Ssthen delay = KR_RELOAD_TIMER; 1632fddf39b9Ssthen log_info("desync; scheduling fib reload"); 1633fddf39b9Ssthen } else { 1634fddf39b9Ssthen delay = KR_RELOAD_HOLD_TIMER; 1635fddf39b9Ssthen log_debug("desync during KR_RELOAD_%s", 1636fddf39b9Ssthen kr_state.reload_state == 1637fddf39b9Ssthen KR_RELOAD_FETCH ? "FETCH" : "HOLD"); 1638fddf39b9Ssthen } 1639fddf39b9Ssthen kr_state.reload_state = KR_RELOAD_FETCH; 1640fddf39b9Ssthen kr_fib_reload_arm_timer(delay); 1641fa19e37cSdlg break; 1642204df0f8Sclaudio default: 1643204df0f8Sclaudio /* ignore for now */ 1644204df0f8Sclaudio break; 1645204df0f8Sclaudio } 1646204df0f8Sclaudio } 16479d7a9d5fSdlg 16489d7a9d5fSdlg return (offset); 1649204df0f8Sclaudio } 1650