1*018a085aSanton /* $OpenBSD: kroute.c,v 1.70 2025/01/02 06:35:57 anton Exp $ */ 2a1a4e97bSnorby 3a1a4e97bSnorby /* 4a1a4e97bSnorby * Copyright (c) 2004 Esben Norby <norby@openbsd.org> 5a1a4e97bSnorby * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 6a1a4e97bSnorby * 7a1a4e97bSnorby * Permission to use, copy, modify, and distribute this software for any 8a1a4e97bSnorby * purpose with or without fee is hereby granted, provided that the above 9a1a4e97bSnorby * copyright notice and this permission notice appear in all copies. 10a1a4e97bSnorby * 11a1a4e97bSnorby * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12a1a4e97bSnorby * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13a1a4e97bSnorby * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14a1a4e97bSnorby * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15a1a4e97bSnorby * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16a1a4e97bSnorby * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17a1a4e97bSnorby * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18a1a4e97bSnorby */ 19a1a4e97bSnorby 20a1a4e97bSnorby #include <sys/types.h> 21a1a4e97bSnorby #include <sys/socket.h> 22a1a4e97bSnorby #include <sys/sysctl.h> 23a1a4e97bSnorby #include <sys/tree.h> 24a1a4e97bSnorby #include <sys/uio.h> 25a1a4e97bSnorby #include <netinet/in.h> 26a1a4e97bSnorby #include <arpa/inet.h> 27a1a4e97bSnorby #include <net/if.h> 28a1a4e97bSnorby #include <net/if_dl.h> 29a1a4e97bSnorby #include <net/if_types.h> 30a1a4e97bSnorby #include <net/route.h> 31a1a4e97bSnorby #include <err.h> 32a1a4e97bSnorby #include <errno.h> 33a1a4e97bSnorby #include <fcntl.h> 34a1a4e97bSnorby #include <stdio.h> 35a1a4e97bSnorby #include <stdlib.h> 36a1a4e97bSnorby #include <string.h> 37a1a4e97bSnorby #include <unistd.h> 38b9fc9a72Sderaadt #include <limits.h> 39a1a4e97bSnorby 40a1a4e97bSnorby #include "ospf6d.h" 416c9e7a5bSclaudio #include "ospfe.h" 42a1a4e97bSnorby #include "log.h" 43a1a4e97bSnorby 44a1a4e97bSnorby struct { 45a1a4e97bSnorby u_int32_t rtseq; 46a1a4e97bSnorby pid_t pid; 47a1a4e97bSnorby int fib_sync; 48dd3b9a80Ssthen int fib_serial; 49d1e43ac2Sremi u_int8_t fib_prio; 50a1a4e97bSnorby int fd; 51a1a4e97bSnorby struct event ev; 52dd3b9a80Ssthen struct event reload; 535d393f89Sremi u_int rdomain; 54dd3b9a80Ssthen #define KR_RELOAD_IDLE 0 55dd3b9a80Ssthen #define KR_RELOAD_FETCH 1 56dd3b9a80Ssthen #define KR_RELOAD_HOLD 2 57dd3b9a80Ssthen int reload_state; 58a1a4e97bSnorby } kr_state; 59a1a4e97bSnorby 60a1a4e97bSnorby struct kroute_node { 61a1a4e97bSnorby RB_ENTRY(kroute_node) entry; 62a1a4e97bSnorby struct kroute_node *next; 63451959f0Sjca struct kroute r; 64dd3b9a80Ssthen int serial; 65a1a4e97bSnorby }; 66a1a4e97bSnorby 67a1a4e97bSnorby void kr_redist_remove(struct kroute_node *, struct kroute_node *); 68451959f0Sjca int kr_redist_eval(struct kroute *, struct kroute *); 69a1a4e97bSnorby void kr_redistribute(struct kroute_node *); 70a1a4e97bSnorby int kroute_compare(struct kroute_node *, struct kroute_node *); 71722a4e30Sfriehm int kr_change_fib(struct kroute_node *, struct kroute *, int, int); 72722a4e30Sfriehm int kr_delete_fib(struct kroute_node *); 73a1a4e97bSnorby 74afd0ab5dSfriehm struct kroute_node *kroute_find(const struct in6_addr *, u_int8_t, 75afd0ab5dSfriehm u_int8_t); 76f8efa005Sclaudio struct kroute_node *kroute_matchgw(struct kroute_node *, 77eecab31bSclaudio struct in6_addr *, unsigned int); 78a1a4e97bSnorby int kroute_insert(struct kroute_node *); 79a1a4e97bSnorby int kroute_remove(struct kroute_node *); 80a1a4e97bSnorby void kroute_clear(void); 81a1a4e97bSnorby 826c9e7a5bSclaudio struct iface *kif_update(u_short, int, struct if_data *, 83a1a4e97bSnorby struct sockaddr_dl *); 84a1a4e97bSnorby int kif_validate(u_short); 85a1a4e97bSnorby 86f8efa005Sclaudio struct kroute_node *kroute_match(struct in6_addr *); 87a1a4e97bSnorby 88a1a4e97bSnorby int protect_lo(void); 89a1a4e97bSnorby void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 90bf9d8b16Sdenis void if_change(u_short, int, struct if_data *, struct sockaddr_dl *); 91a1a4e97bSnorby void if_newaddr(u_short, struct sockaddr_in6 *, 92a1a4e97bSnorby struct sockaddr_in6 *, struct sockaddr_in6 *); 938e1674f3Sbluhm void if_deladdr(u_short, struct sockaddr_in6 *, 948e1674f3Sbluhm struct sockaddr_in6 *, struct sockaddr_in6 *); 95a1a4e97bSnorby void if_announce(void *); 96a1a4e97bSnorby 97a1a4e97bSnorby int send_rtmsg(int, int, struct kroute *); 98a1a4e97bSnorby int dispatch_rtmsg(void); 99a1a4e97bSnorby int fetchtable(void); 100bf9d8b16Sdenis int rtmsg_process(char *, size_t); 101dd3b9a80Ssthen void kr_fib_reload_timer(int, short, void *); 102dd3b9a80Ssthen void kr_fib_reload_arm_timer(int); 103a1a4e97bSnorby 104a1a4e97bSnorby RB_HEAD(kroute_tree, kroute_node) krt; 105a1a4e97bSnorby RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare) 106a1a4e97bSnorby RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare) 107a1a4e97bSnorby 108a1a4e97bSnorby int 1094cf51530Sdenis kr_init(int fs, u_int rdomain, int redis_label_or_prefix, u_int8_t fib_prio) 110a1a4e97bSnorby { 111a1a4e97bSnorby int opt = 0, rcvbuf, default_rcvbuf; 112a1a4e97bSnorby socklen_t optlen; 1134cf51530Sdenis int filter_prio = fib_prio; 1141e07feb9Sjmatthew int filter_flags = RTF_LLINFO | RTF_BROADCAST; 115a1a4e97bSnorby 116a1a4e97bSnorby kr_state.fib_sync = fs; 1175d393f89Sremi kr_state.rdomain = rdomain; 118d1e43ac2Sremi kr_state.fib_prio = fib_prio; 119a1a4e97bSnorby 12035251ca5Sclaudio if ((kr_state.fd = socket(AF_ROUTE, 1211b9a1fc7Sbenno SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, AF_INET6)) == -1) { 122a1a4e97bSnorby log_warn("kr_init: socket"); 123a1a4e97bSnorby return (-1); 124a1a4e97bSnorby } 125a1a4e97bSnorby 126a1a4e97bSnorby /* not interested in my own messages */ 127a1a4e97bSnorby if (setsockopt(kr_state.fd, SOL_SOCKET, SO_USELOOPBACK, 128a1a4e97bSnorby &opt, sizeof(opt)) == -1) 129a1a4e97bSnorby log_warn("kr_init: setsockopt"); /* not fatal */ 130a1a4e97bSnorby 1314cf51530Sdenis if (redis_label_or_prefix) { 1324cf51530Sdenis filter_prio = 0; 1334cf51530Sdenis log_info("%s: priority filter disabled", __func__); 1344cf51530Sdenis } else 1354cf51530Sdenis log_debug("%s: priority filter enabled", __func__); 1364cf51530Sdenis 1374cf51530Sdenis if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_PRIOFILTER, &filter_prio, 1384cf51530Sdenis sizeof(filter_prio)) == -1) { 1394cf51530Sdenis log_warn("%s: setsockopt AF_ROUTE ROUTE_PRIOFILTER", __func__); 1404cf51530Sdenis /* not fatal */ 1414cf51530Sdenis } 1424cf51530Sdenis 1431e07feb9Sjmatthew if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_FLAGFILTER, &filter_flags, 1441e07feb9Sjmatthew sizeof(filter_flags)) == -1) { 1451e07feb9Sjmatthew log_warn("%s: setsockopt AF_ROUTE ROUTE_FLAGFILTER", __func__); 1461e07feb9Sjmatthew /* not fatal */ 1471e07feb9Sjmatthew } 1481e07feb9Sjmatthew 149a1a4e97bSnorby /* grow receive buffer, don't wanna miss messages */ 150a1a4e97bSnorby optlen = sizeof(default_rcvbuf); 151a1a4e97bSnorby if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, 152a1a4e97bSnorby &default_rcvbuf, &optlen) == -1) 153a1a4e97bSnorby log_warn("kr_init getsockopt SOL_SOCKET SO_RCVBUF"); 154a1a4e97bSnorby else 155a1a4e97bSnorby for (rcvbuf = MAX_RTSOCK_BUF; 156a1a4e97bSnorby rcvbuf > default_rcvbuf && 157a1a4e97bSnorby setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, 158a1a4e97bSnorby &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS; 159a1a4e97bSnorby rcvbuf /= 2) 160a1a4e97bSnorby ; /* nothing */ 161a1a4e97bSnorby 162a1a4e97bSnorby kr_state.pid = getpid(); 163a1a4e97bSnorby kr_state.rtseq = 1; 164a1a4e97bSnorby 165a1a4e97bSnorby RB_INIT(&krt); 166a1a4e97bSnorby 167a1a4e97bSnorby if (fetchtable() == -1) 168a1a4e97bSnorby return (-1); 169a1a4e97bSnorby 170a1a4e97bSnorby if (protect_lo() == -1) 171a1a4e97bSnorby return (-1); 172a1a4e97bSnorby 173a1a4e97bSnorby event_set(&kr_state.ev, kr_state.fd, EV_READ | EV_PERSIST, 174a1a4e97bSnorby kr_dispatch_msg, NULL); 175a1a4e97bSnorby event_add(&kr_state.ev, NULL); 176a1a4e97bSnorby 177dd3b9a80Ssthen kr_state.reload_state = KR_RELOAD_IDLE; 178dd3b9a80Ssthen evtimer_set(&kr_state.reload, kr_fib_reload_timer, NULL); 179dd3b9a80Ssthen 180a1a4e97bSnorby return (0); 181a1a4e97bSnorby } 182a1a4e97bSnorby 183a1a4e97bSnorby int 184722a4e30Sfriehm kr_change_fib(struct kroute_node *kr, struct kroute *kroute, int krcount, 185722a4e30Sfriehm int action) 186722a4e30Sfriehm { 187722a4e30Sfriehm int i; 188722a4e30Sfriehm struct kroute_node *kn, *nkn; 189722a4e30Sfriehm 190722a4e30Sfriehm if (action == RTM_ADD) { 191722a4e30Sfriehm /* 192722a4e30Sfriehm * First remove all stale multipath routes. 193722a4e30Sfriehm * This step must be skipped when the action is RTM_CHANGE 194722a4e30Sfriehm * because it is already a single path route that will be 195722a4e30Sfriehm * changed. 196722a4e30Sfriehm */ 197722a4e30Sfriehm for (kn = kr; kn != NULL; kn = nkn) { 198722a4e30Sfriehm for (i = 0; i < krcount; i++) { 199722a4e30Sfriehm if (kn->r.scope == kroute[i].scope && 200722a4e30Sfriehm IN6_ARE_ADDR_EQUAL(&kn->r.nexthop, 201722a4e30Sfriehm &kroute[i].nexthop)) 202722a4e30Sfriehm break; 203722a4e30Sfriehm } 204722a4e30Sfriehm nkn = kn->next; 205722a4e30Sfriehm if (i == krcount) { 206722a4e30Sfriehm /* stale route */ 207722a4e30Sfriehm if (kr_delete_fib(kn) == -1) 208722a4e30Sfriehm log_warnx("kr_delete_fib failed"); 209722a4e30Sfriehm /* 210722a4e30Sfriehm * if head element was removed we need to adjust 211722a4e30Sfriehm * the head 212722a4e30Sfriehm */ 213722a4e30Sfriehm if (kr == kn) 214722a4e30Sfriehm kr = nkn; 215722a4e30Sfriehm } 216722a4e30Sfriehm } 217722a4e30Sfriehm } 218722a4e30Sfriehm 219722a4e30Sfriehm /* 220722a4e30Sfriehm * now add or change the route 221722a4e30Sfriehm */ 222722a4e30Sfriehm for (i = 0; i < krcount; i++) { 223722a4e30Sfriehm /* nexthop ::1 -> ignore silently */ 224722a4e30Sfriehm if (IN6_IS_ADDR_LOOPBACK(&kroute[i].nexthop)) 225722a4e30Sfriehm continue; 226722a4e30Sfriehm 227722a4e30Sfriehm if (action == RTM_ADD && kr) { 228722a4e30Sfriehm for (kn = kr; kn != NULL; kn = kn->next) { 229722a4e30Sfriehm if (kn->r.scope == kroute[i].scope && 230722a4e30Sfriehm IN6_ARE_ADDR_EQUAL(&kn->r.nexthop, 231722a4e30Sfriehm &kroute[i].nexthop)) 232722a4e30Sfriehm break; 233722a4e30Sfriehm } 234722a4e30Sfriehm 235722a4e30Sfriehm if (kn != NULL) 236722a4e30Sfriehm /* nexthop already present, skip it */ 237722a4e30Sfriehm continue; 238722a4e30Sfriehm } else 239722a4e30Sfriehm /* modify first entry */ 240722a4e30Sfriehm kn = kr; 241722a4e30Sfriehm 242722a4e30Sfriehm /* send update */ 243722a4e30Sfriehm if (send_rtmsg(kr_state.fd, action, &kroute[i]) == -1) 244722a4e30Sfriehm return (-1); 245722a4e30Sfriehm 246722a4e30Sfriehm /* create new entry unless we are changing the first entry */ 247722a4e30Sfriehm if (action == RTM_ADD) 248722a4e30Sfriehm if ((kn = calloc(1, sizeof(*kn))) == NULL) 249722a4e30Sfriehm fatal(NULL); 250722a4e30Sfriehm 251722a4e30Sfriehm kn->r.prefix = kroute[i].prefix; 252722a4e30Sfriehm kn->r.prefixlen = kroute[i].prefixlen; 253722a4e30Sfriehm kn->r.nexthop = kroute[i].nexthop; 254722a4e30Sfriehm kn->r.scope = kroute[i].scope; 255722a4e30Sfriehm kn->r.flags = kroute[i].flags | F_OSPFD_INSERTED; 256d1e43ac2Sremi kn->r.priority = kr_state.fib_prio; 257722a4e30Sfriehm kn->r.ext_tag = kroute[i].ext_tag; 258722a4e30Sfriehm rtlabel_unref(kn->r.rtlabel); /* for RTM_CHANGE */ 259722a4e30Sfriehm kn->r.rtlabel = kroute[i].rtlabel; 260722a4e30Sfriehm 261722a4e30Sfriehm if (action == RTM_ADD) 262722a4e30Sfriehm if (kroute_insert(kn) == -1) { 263722a4e30Sfriehm log_debug("kr_update_fib: cannot insert %s", 264722a4e30Sfriehm log_in6addr(&kn->r.nexthop)); 265722a4e30Sfriehm free(kn); 266722a4e30Sfriehm } 267722a4e30Sfriehm action = RTM_ADD; 268722a4e30Sfriehm } 269722a4e30Sfriehm return (0); 270722a4e30Sfriehm } 271722a4e30Sfriehm 272722a4e30Sfriehm int 273722a4e30Sfriehm kr_change(struct kroute *kroute, int krcount) 274a1a4e97bSnorby { 275a1a4e97bSnorby struct kroute_node *kr; 276a1a4e97bSnorby int action = RTM_ADD; 277a1a4e97bSnorby 278a1a4e97bSnorby kroute->rtlabel = rtlabel_tag2id(kroute->ext_tag); 279a1a4e97bSnorby 280d1e43ac2Sremi kr = kroute_find(&kroute->prefix, kroute->prefixlen, kr_state.fib_prio); 28100c1ad6aSfriehm if (kr != NULL && kr->next == NULL && krcount == 1) { 28200c1ad6aSfriehm /* 28300c1ad6aSfriehm * single path OSPF route. 28400c1ad6aSfriehm * The kernel does not allow to change a gateway route to a 28500c1ad6aSfriehm * cloning route or contrary. In this case remove and add the 28600c1ad6aSfriehm * route, otherwise change the existing one. 28700c1ad6aSfriehm */ 28800c1ad6aSfriehm if ((IN6_IS_ADDR_UNSPECIFIED(&kroute->nexthop) && 28900c1ad6aSfriehm !IN6_IS_ADDR_UNSPECIFIED(&kr->r.nexthop)) || 29000c1ad6aSfriehm (!IN6_IS_ADDR_UNSPECIFIED(&kroute->nexthop) && 29100c1ad6aSfriehm IN6_IS_ADDR_UNSPECIFIED(&kr->r.nexthop))) { 29200c1ad6aSfriehm if (kr_delete_fib(kr) == 0) 29300c1ad6aSfriehm kr = NULL; 29400c1ad6aSfriehm else { 29500c1ad6aSfriehm log_warn("kr_change: failed to remove route: " 29600c1ad6aSfriehm "%s/%d", log_in6addr(&kr->r.prefix), 29700c1ad6aSfriehm kr->r.prefixlen); 29800c1ad6aSfriehm return (-1); 29900c1ad6aSfriehm } 30000c1ad6aSfriehm } else 301a1a4e97bSnorby action = RTM_CHANGE; 30200c1ad6aSfriehm } 303a1a4e97bSnorby 304722a4e30Sfriehm return (kr_change_fib(kr, kroute, krcount, action)); 305722a4e30Sfriehm } 306a1a4e97bSnorby 307722a4e30Sfriehm int 308722a4e30Sfriehm kr_delete_fib(struct kroute_node *kr) 309722a4e30Sfriehm { 310d1e43ac2Sremi if (kr->r.priority != kr_state.fib_prio) 311afd0ab5dSfriehm log_warn("kr_delete_fib: %s/%d has wrong priority %d", 312afd0ab5dSfriehm log_in6addr(&kr->r.prefix), kr->r.prefixlen, 313afd0ab5dSfriehm kr->r.priority); 314722a4e30Sfriehm 315722a4e30Sfriehm if (send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r) == -1) 316a1a4e97bSnorby return (-1); 317a1a4e97bSnorby 318722a4e30Sfriehm if (kroute_remove(kr) == -1) 319a1a4e97bSnorby return (-1); 320a1a4e97bSnorby 321a1a4e97bSnorby return (0); 322a1a4e97bSnorby } 323a1a4e97bSnorby 324a1a4e97bSnorby int 325a1a4e97bSnorby kr_delete(struct kroute *kroute) 326a1a4e97bSnorby { 327722a4e30Sfriehm struct kroute_node *kr, *nkr; 328a1a4e97bSnorby 329afd0ab5dSfriehm if ((kr = kroute_find(&kroute->prefix, kroute->prefixlen, 330d1e43ac2Sremi kr_state.fib_prio)) == NULL) 331a1a4e97bSnorby return (0); 332a1a4e97bSnorby 333722a4e30Sfriehm while (kr != NULL) { 334722a4e30Sfriehm nkr = kr->next; 335722a4e30Sfriehm if (kr_delete_fib(kr) == -1) 336722a4e30Sfriehm return (-1); 337722a4e30Sfriehm kr = nkr; 338a1a4e97bSnorby } 339a1a4e97bSnorby 340a1a4e97bSnorby return (0); 341a1a4e97bSnorby } 342a1a4e97bSnorby 343a1a4e97bSnorby void 344a1a4e97bSnorby kr_shutdown(void) 345a1a4e97bSnorby { 346a1a4e97bSnorby kr_fib_decouple(); 347a1a4e97bSnorby kroute_clear(); 348a1a4e97bSnorby } 349a1a4e97bSnorby 350a1a4e97bSnorby void 351a1a4e97bSnorby kr_fib_couple(void) 352a1a4e97bSnorby { 353a1a4e97bSnorby struct kroute_node *kr; 354722a4e30Sfriehm struct kroute_node *kn; 355a1a4e97bSnorby 356a1a4e97bSnorby if (kr_state.fib_sync == 1) /* already coupled */ 357a1a4e97bSnorby return; 358a1a4e97bSnorby 359a1a4e97bSnorby kr_state.fib_sync = 1; 360a1a4e97bSnorby 361a1a4e97bSnorby RB_FOREACH(kr, kroute_tree, &krt) 362d1e43ac2Sremi if (kr->r.priority == kr_state.fib_prio) 363afd0ab5dSfriehm for (kn = kr; kn != NULL; kn = kn->next) 364722a4e30Sfriehm send_rtmsg(kr_state.fd, RTM_ADD, &kn->r); 365a1a4e97bSnorby 366a1a4e97bSnorby log_info("kernel routing table coupled"); 367a1a4e97bSnorby } 368a1a4e97bSnorby 369a1a4e97bSnorby void 370a1a4e97bSnorby kr_fib_decouple(void) 371a1a4e97bSnorby { 372a1a4e97bSnorby struct kroute_node *kr; 373722a4e30Sfriehm struct kroute_node *kn; 374a1a4e97bSnorby 375a1a4e97bSnorby if (kr_state.fib_sync == 0) /* already decoupled */ 376a1a4e97bSnorby return; 377a1a4e97bSnorby 378afd0ab5dSfriehm RB_FOREACH(kr, kroute_tree, &krt) 379d1e43ac2Sremi if (kr->r.priority == kr_state.fib_prio) 380afd0ab5dSfriehm for (kn = kr; kn != NULL; kn = kn->next) 381722a4e30Sfriehm send_rtmsg(kr_state.fd, RTM_DELETE, &kn->r); 382a1a4e97bSnorby 383a1a4e97bSnorby kr_state.fib_sync = 0; 384a1a4e97bSnorby 385a1a4e97bSnorby log_info("kernel routing table decoupled"); 386a1a4e97bSnorby } 387a1a4e97bSnorby 3884cf51530Sdenis void 389dd3b9a80Ssthen kr_fib_reload_timer(int fd, short event, void *bula) 390dd3b9a80Ssthen { 391dd3b9a80Ssthen if (kr_state.reload_state == KR_RELOAD_FETCH) { 392dd3b9a80Ssthen kr_fib_reload(); 393dd3b9a80Ssthen kr_state.reload_state = KR_RELOAD_HOLD; 394dd3b9a80Ssthen kr_fib_reload_arm_timer(KR_RELOAD_HOLD_TIMER); 395dd3b9a80Ssthen } else { 396dd3b9a80Ssthen kr_state.reload_state = KR_RELOAD_IDLE; 397dd3b9a80Ssthen } 398dd3b9a80Ssthen } 399dd3b9a80Ssthen 400dd3b9a80Ssthen void 401dd3b9a80Ssthen kr_fib_reload_arm_timer(int delay) 402dd3b9a80Ssthen { 403dd3b9a80Ssthen struct timeval tv; 404dd3b9a80Ssthen 405dd3b9a80Ssthen timerclear(&tv); 406dd3b9a80Ssthen tv.tv_sec = delay / 1000; 407dd3b9a80Ssthen tv.tv_usec = (delay % 1000) * 1000; 408dd3b9a80Ssthen 409dd3b9a80Ssthen if (evtimer_add(&kr_state.reload, &tv) == -1) 410dd3b9a80Ssthen fatal("add_reload_timer"); 411dd3b9a80Ssthen } 412dd3b9a80Ssthen 413dd3b9a80Ssthen void 414dd3b9a80Ssthen kr_fib_reload(void) 415dd3b9a80Ssthen { 416dd3b9a80Ssthen struct kroute_node *krn, *kr, *kn; 417dd3b9a80Ssthen 418dd3b9a80Ssthen log_info("reloading interface list and routing table"); 419dd3b9a80Ssthen 420dd3b9a80Ssthen kr_state.fib_serial++; 421dd3b9a80Ssthen 422dd3b9a80Ssthen if (fetchifs(0) != 0 || fetchtable() != 0) 423dd3b9a80Ssthen return; 424dd3b9a80Ssthen 425dd3b9a80Ssthen for (kr = RB_MIN(kroute_tree, &krt); kr != NULL; kr = krn) { 426dd3b9a80Ssthen krn = RB_NEXT(kroute_tree, &krt, kr); 427dd3b9a80Ssthen 428dd3b9a80Ssthen do { 429dd3b9a80Ssthen kn = kr->next; 430dd3b9a80Ssthen 431dd3b9a80Ssthen if (kr->serial != kr_state.fib_serial) { 432dd3b9a80Ssthen 433dd3b9a80Ssthen if (kr->r.priority == kr_state.fib_prio) { 434dd3b9a80Ssthen kr->serial = kr_state.fib_serial; 435dd3b9a80Ssthen if (send_rtmsg(kr_state.fd, 436dd3b9a80Ssthen RTM_ADD, &kr->r) != 0) 437dd3b9a80Ssthen break; 438dd3b9a80Ssthen } else 439dd3b9a80Ssthen kroute_remove(kr); 440dd3b9a80Ssthen } 441dd3b9a80Ssthen 442dd3b9a80Ssthen } while ((kr = kn) != NULL); 443dd3b9a80Ssthen } 444dd3b9a80Ssthen } 445dd3b9a80Ssthen 446dd3b9a80Ssthen void 4474cf51530Sdenis kr_fib_update_prio(u_int8_t fib_prio) 4484cf51530Sdenis { 4494cf51530Sdenis struct kroute_node *kr; 4504cf51530Sdenis 4514cf51530Sdenis RB_FOREACH(kr, kroute_tree, &krt) 4524cf51530Sdenis if ((kr->r.flags & F_OSPFD_INSERTED)) 4534cf51530Sdenis kr->r.priority = fib_prio; 4544cf51530Sdenis 4554cf51530Sdenis log_info("fib priority changed from %hhu to %hhu", kr_state.fib_prio, 4564cf51530Sdenis fib_prio); 4574cf51530Sdenis 4584cf51530Sdenis kr_state.fib_prio = fib_prio; 4594cf51530Sdenis } 4604cf51530Sdenis 461a1a4e97bSnorby void 462a1a4e97bSnorby kr_dispatch_msg(int fd, short event, void *bula) 463a1a4e97bSnorby { 464afd0ab5dSfriehm /* XXX this is stupid */ 465a1a4e97bSnorby dispatch_rtmsg(); 466a1a4e97bSnorby } 467a1a4e97bSnorby 468a1a4e97bSnorby void 469a1a4e97bSnorby kr_show_route(struct imsg *imsg) 470a1a4e97bSnorby { 471a1a4e97bSnorby struct kroute_node *kr; 472a1a4e97bSnorby struct kroute_node *kn; 473a1a4e97bSnorby int flags; 474f8efa005Sclaudio struct in6_addr addr; 475a1a4e97bSnorby 476a1a4e97bSnorby switch (imsg->hdr.type) { 477a1a4e97bSnorby case IMSG_CTL_KROUTE: 478a1a4e97bSnorby if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags)) { 479a1a4e97bSnorby log_warnx("kr_show_route: wrong imsg len"); 480a1a4e97bSnorby return; 481a1a4e97bSnorby } 482a1a4e97bSnorby memcpy(&flags, imsg->data, sizeof(flags)); 483a1a4e97bSnorby RB_FOREACH(kr, kroute_tree, &krt) 484a1a4e97bSnorby if (!flags || kr->r.flags & flags) { 485a1a4e97bSnorby kn = kr; 486a1a4e97bSnorby do { 487a1a4e97bSnorby main_imsg_compose_ospfe(IMSG_CTL_KROUTE, 488a1a4e97bSnorby imsg->hdr.pid, 489a1a4e97bSnorby &kn->r, sizeof(kn->r)); 490a1a4e97bSnorby } while ((kn = kn->next) != NULL); 491a1a4e97bSnorby } 492a1a4e97bSnorby break; 493a1a4e97bSnorby case IMSG_CTL_KROUTE_ADDR: 494a1a4e97bSnorby if (imsg->hdr.len != IMSG_HEADER_SIZE + 495f8efa005Sclaudio sizeof(struct in6_addr)) { 496a1a4e97bSnorby log_warnx("kr_show_route: wrong imsg len"); 497a1a4e97bSnorby return; 498a1a4e97bSnorby } 499a1a4e97bSnorby memcpy(&addr, imsg->data, sizeof(addr)); 500f8efa005Sclaudio kr = kroute_match(&addr); 501a1a4e97bSnorby if (kr != NULL) 502a1a4e97bSnorby main_imsg_compose_ospfe(IMSG_CTL_KROUTE, imsg->hdr.pid, 503a1a4e97bSnorby &kr->r, sizeof(kr->r)); 504a1a4e97bSnorby break; 505a1a4e97bSnorby default: 506a1a4e97bSnorby log_debug("kr_show_route: error handling imsg"); 507a1a4e97bSnorby break; 508a1a4e97bSnorby } 509a1a4e97bSnorby 510a1a4e97bSnorby main_imsg_compose_ospfe(IMSG_CTL_END, imsg->hdr.pid, NULL, 0); 511a1a4e97bSnorby } 512a1a4e97bSnorby 513a1a4e97bSnorby void 514a1a4e97bSnorby kr_redist_remove(struct kroute_node *kh, struct kroute_node *kn) 515a1a4e97bSnorby { 516451959f0Sjca struct kroute *kr; 517a1a4e97bSnorby 518a1a4e97bSnorby /* was the route redistributed? */ 519a1a4e97bSnorby if ((kn->r.flags & F_REDISTRIBUTED) == 0) 520a1a4e97bSnorby return; 521a1a4e97bSnorby 522a1a4e97bSnorby /* remove redistributed flag */ 523a1a4e97bSnorby kn->r.flags &= ~F_REDISTRIBUTED; 524451959f0Sjca kr = &kn->r; 525a1a4e97bSnorby 526a1a4e97bSnorby /* probably inform the RDE (check if no other path is redistributed) */ 527a1a4e97bSnorby for (kn = kh; kn; kn = kn->next) 528a1a4e97bSnorby if (kn->r.flags & F_REDISTRIBUTED) 529a1a4e97bSnorby break; 530a1a4e97bSnorby 531a1a4e97bSnorby if (kn == NULL) 532451959f0Sjca main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, kr, 533451959f0Sjca sizeof(struct kroute)); 534a1a4e97bSnorby } 535a1a4e97bSnorby 536a1a4e97bSnorby int 537451959f0Sjca kr_redist_eval(struct kroute *kr, struct kroute *new_kr) 538a1a4e97bSnorby { 539f8efa005Sclaudio u_int32_t metric = 0; 540a1a4e97bSnorby 541a1a4e97bSnorby /* Only non-ospfd routes are considered for redistribution. */ 542a1a4e97bSnorby if (!(kr->flags & F_KERNEL)) 543a1a4e97bSnorby goto dont_redistribute; 544a1a4e97bSnorby 545a1a4e97bSnorby /* Dynamic routes are not redistributable. */ 546a1a4e97bSnorby if (kr->flags & F_DYNAMIC) 547a1a4e97bSnorby goto dont_redistribute; 548a1a4e97bSnorby 549a1a4e97bSnorby /* interface is not up and running so don't announce */ 550a1a4e97bSnorby if (kr->flags & F_DOWN) 551a1a4e97bSnorby goto dont_redistribute; 552a1a4e97bSnorby 553a1a4e97bSnorby /* 55493c5cf97Sbluhm * We consider loopback, multicast, link- and site-local, 555f8efa005Sclaudio * IPv4 mapped and IPv4 compatible addresses as not redistributable. 556a1a4e97bSnorby */ 55793c5cf97Sbluhm if (IN6_IS_ADDR_LOOPBACK(&kr->prefix) || 558f8efa005Sclaudio IN6_IS_ADDR_MULTICAST(&kr->prefix) || 559f8efa005Sclaudio IN6_IS_ADDR_LINKLOCAL(&kr->prefix) || 560f8efa005Sclaudio IN6_IS_ADDR_SITELOCAL(&kr->prefix) || 561f8efa005Sclaudio IN6_IS_ADDR_V4MAPPED(&kr->prefix) || 562f8efa005Sclaudio IN6_IS_ADDR_V4COMPAT(&kr->prefix)) 563a1a4e97bSnorby goto dont_redistribute; 564a1a4e97bSnorby /* 565001befcbSflorian * Consider networks with nexthop loopback as not redistributable 566001befcbSflorian * unless it is a reject or blackhole route. 567a1a4e97bSnorby */ 568001befcbSflorian if (IN6_IS_ADDR_LOOPBACK(&kr->nexthop) && 569001befcbSflorian !(kr->flags & (F_BLACKHOLE|F_REJECT))) 570a1a4e97bSnorby goto dont_redistribute; 571a1a4e97bSnorby 5723afd87ebSsthen /* Should we redistribute this route? */ 573a1a4e97bSnorby if (!ospf_redistribute(kr, &metric)) 574a1a4e97bSnorby goto dont_redistribute; 575a1a4e97bSnorby 576a1a4e97bSnorby /* prefix should be redistributed */ 577a1a4e97bSnorby kr->flags |= F_REDISTRIBUTED; 578a1a4e97bSnorby /* 5793afd87ebSsthen * only one of all multipath routes can be redistributed so 580a1a4e97bSnorby * redistribute the best one. 581a1a4e97bSnorby */ 582451959f0Sjca if (new_kr->metric > metric) { 583451959f0Sjca *new_kr = *kr; 584451959f0Sjca new_kr->metric = metric; 585a1a4e97bSnorby } 5863afd87ebSsthen 587a1a4e97bSnorby return (1); 588a1a4e97bSnorby 589a1a4e97bSnorby dont_redistribute: 590a1a4e97bSnorby /* was the route redistributed? */ 591a1a4e97bSnorby if ((kr->flags & F_REDISTRIBUTED) == 0) 592a1a4e97bSnorby return (0); 593a1a4e97bSnorby 594a1a4e97bSnorby kr->flags &= ~F_REDISTRIBUTED; 595a1a4e97bSnorby return (1); 596a1a4e97bSnorby } 597a1a4e97bSnorby 598a1a4e97bSnorby void 599a1a4e97bSnorby kr_redistribute(struct kroute_node *kh) 600a1a4e97bSnorby { 601a1a4e97bSnorby struct kroute_node *kn; 602451959f0Sjca struct kroute kr; 603a1a4e97bSnorby int redistribute = 0; 604a1a4e97bSnorby 605afd0ab5dSfriehm /* only the highest prio route can be redistributed */ 606afd0ab5dSfriehm if (kroute_find(&kh->r.prefix, kh->r.prefixlen, RTP_ANY) != kh) 607afd0ab5dSfriehm return; 608afd0ab5dSfriehm 609451959f0Sjca bzero(&kr, sizeof(kr)); 610451959f0Sjca kr.metric = UINT_MAX; 611a1a4e97bSnorby for (kn = kh; kn; kn = kn->next) 612451959f0Sjca if (kr_redist_eval(&kn->r, &kr)) 613a1a4e97bSnorby redistribute = 1; 614a1a4e97bSnorby 615a1a4e97bSnorby if (!redistribute) 616a1a4e97bSnorby return; 617a1a4e97bSnorby 618451959f0Sjca if (kr.flags & F_REDISTRIBUTED) { 619451959f0Sjca main_imsg_compose_rde(IMSG_NETWORK_ADD, 0, &kr, 620451959f0Sjca sizeof(struct kroute)); 621a1a4e97bSnorby } else { 622451959f0Sjca kr = kh->r; 623451959f0Sjca main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, &kr, 624451959f0Sjca sizeof(struct kroute)); 625a1a4e97bSnorby } 626a1a4e97bSnorby } 627a1a4e97bSnorby 628a1a4e97bSnorby void 6294cf51530Sdenis kr_reload(int redis_label_or_prefix) 630a1a4e97bSnorby { 631a1a4e97bSnorby struct kroute_node *kr, *kn; 632a1a4e97bSnorby u_int32_t dummy; 633a1a4e97bSnorby int r; 6344cf51530Sdenis int filter_prio = kr_state.fib_prio; 6354cf51530Sdenis 6364cf51530Sdenis /* update the priority filter */ 6374cf51530Sdenis if (redis_label_or_prefix) { 6384cf51530Sdenis filter_prio = 0; 6394cf51530Sdenis log_info("%s: priority filter disabled", __func__); 6404cf51530Sdenis } else 6414cf51530Sdenis log_debug("%s: priority filter enabled", __func__); 6424cf51530Sdenis 6434cf51530Sdenis if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_PRIOFILTER, &filter_prio, 6444cf51530Sdenis sizeof(filter_prio)) == -1) { 6454cf51530Sdenis log_warn("%s: setsockopt AF_ROUTE ROUTE_PRIOFILTER", __func__); 6464cf51530Sdenis /* not fatal */ 6474cf51530Sdenis } 648a1a4e97bSnorby 649a1a4e97bSnorby RB_FOREACH(kr, kroute_tree, &krt) { 650a1a4e97bSnorby for (kn = kr; kn; kn = kn->next) { 651a1a4e97bSnorby r = ospf_redistribute(&kn->r, &dummy); 652a1a4e97bSnorby /* 653a1a4e97bSnorby * if it is redistributed, redistribute again metric 654a1a4e97bSnorby * may have changed. 655a1a4e97bSnorby */ 656a1a4e97bSnorby if ((kn->r.flags & F_REDISTRIBUTED && !r) || r) 657a1a4e97bSnorby break; 658a1a4e97bSnorby } 659a1a4e97bSnorby if (kn) { 660a1a4e97bSnorby /* 661a1a4e97bSnorby * kr_redistribute copes with removes and RDE with 662a1a4e97bSnorby * duplicates 663a1a4e97bSnorby */ 664a1a4e97bSnorby kr_redistribute(kr); 665a1a4e97bSnorby } 666a1a4e97bSnorby } 667a1a4e97bSnorby } 668a1a4e97bSnorby 669a1a4e97bSnorby /* rb-tree compare */ 670a1a4e97bSnorby int 671a1a4e97bSnorby kroute_compare(struct kroute_node *a, struct kroute_node *b) 672a1a4e97bSnorby { 673f8efa005Sclaudio int i; 674f8efa005Sclaudio 675f8efa005Sclaudio /* XXX maybe switch a & b */ 676f8efa005Sclaudio i = memcmp(&a->r.prefix, &b->r.prefix, sizeof(a->r.prefix)); 677f8efa005Sclaudio if (i) 678f8efa005Sclaudio return (i); 679a1a4e97bSnorby if (a->r.prefixlen < b->r.prefixlen) 680a1a4e97bSnorby return (-1); 681a1a4e97bSnorby if (a->r.prefixlen > b->r.prefixlen) 682a1a4e97bSnorby return (1); 683afd0ab5dSfriehm 684afd0ab5dSfriehm /* if the priority is RTP_ANY finish on the first address hit */ 685afd0ab5dSfriehm if (a->r.priority == RTP_ANY || b->r.priority == RTP_ANY) 686afd0ab5dSfriehm return (0); 687afd0ab5dSfriehm if (a->r.priority < b->r.priority) 688afd0ab5dSfriehm return (-1); 689afd0ab5dSfriehm if (a->r.priority > b->r.priority) 690afd0ab5dSfriehm return (1); 691a1a4e97bSnorby return (0); 692a1a4e97bSnorby } 693a1a4e97bSnorby 694a1a4e97bSnorby /* tree management */ 695a1a4e97bSnorby struct kroute_node * 696afd0ab5dSfriehm kroute_find(const struct in6_addr *prefix, u_int8_t prefixlen, u_int8_t prio) 697a1a4e97bSnorby { 698a1a4e97bSnorby struct kroute_node s; 699afd0ab5dSfriehm struct kroute_node *kn, *tmp; 700a1a4e97bSnorby 701f8efa005Sclaudio s.r.prefix = *prefix; 702a1a4e97bSnorby s.r.prefixlen = prefixlen; 703afd0ab5dSfriehm s.r.priority = prio; 704a1a4e97bSnorby 705afd0ab5dSfriehm kn = RB_FIND(kroute_tree, &krt, &s); 706afd0ab5dSfriehm if (kn && prio == RTP_ANY) { 707afd0ab5dSfriehm tmp = RB_PREV(kroute_tree, &krt, kn); 708afd0ab5dSfriehm while (tmp) { 709afd0ab5dSfriehm if (kroute_compare(&s, tmp) == 0) 710afd0ab5dSfriehm kn = tmp; 711afd0ab5dSfriehm else 712afd0ab5dSfriehm break; 713afd0ab5dSfriehm tmp = RB_PREV(kroute_tree, &krt, kn); 714afd0ab5dSfriehm } 715afd0ab5dSfriehm } 716afd0ab5dSfriehm return (kn); 717a1a4e97bSnorby } 718a1a4e97bSnorby 719a1a4e97bSnorby struct kroute_node * 720eecab31bSclaudio kroute_matchgw(struct kroute_node *kr, struct in6_addr *nh, unsigned int scope) 721a1a4e97bSnorby { 722a1a4e97bSnorby while (kr) { 723eecab31bSclaudio if (scope == kr->r.scope && 724eecab31bSclaudio IN6_ARE_ADDR_EQUAL(&kr->r.nexthop, nh)) 725a1a4e97bSnorby return (kr); 726a1a4e97bSnorby kr = kr->next; 727a1a4e97bSnorby } 728a1a4e97bSnorby 729a1a4e97bSnorby return (NULL); 730a1a4e97bSnorby } 731a1a4e97bSnorby 732a1a4e97bSnorby int 733a1a4e97bSnorby kroute_insert(struct kroute_node *kr) 734a1a4e97bSnorby { 735c9757b53Sclaudio struct kroute_node *krm, *krh; 736a1a4e97bSnorby 737dd3b9a80Ssthen kr->serial = kr_state.fib_serial; 738dd3b9a80Ssthen 739c9757b53Sclaudio if ((krh = RB_INSERT(kroute_tree, &krt, kr)) != NULL) { 740a1a4e97bSnorby /* 741afd0ab5dSfriehm * Multipath route, add at end of list. 742a1a4e97bSnorby */ 743c9757b53Sclaudio krm = krh; 744a1a4e97bSnorby while (krm->next != NULL) 745a1a4e97bSnorby krm = krm->next; 746a1a4e97bSnorby krm->next = kr; 747a1a4e97bSnorby kr->next = NULL; /* to be sure */ 748a1a4e97bSnorby } else 749c9757b53Sclaudio krh = kr; 750a1a4e97bSnorby 751a1a4e97bSnorby if (!(kr->r.flags & F_KERNEL)) { 752a1a4e97bSnorby /* don't validate or redistribute ospf route */ 753a1a4e97bSnorby kr->r.flags &= ~F_DOWN; 754a1a4e97bSnorby return (0); 755a1a4e97bSnorby } 756a1a4e97bSnorby 757a1a4e97bSnorby if (kif_validate(kr->r.ifindex)) 758a1a4e97bSnorby kr->r.flags &= ~F_DOWN; 759a1a4e97bSnorby else 760a1a4e97bSnorby kr->r.flags |= F_DOWN; 761a1a4e97bSnorby 762c9757b53Sclaudio kr_redistribute(krh); 763a1a4e97bSnorby return (0); 764a1a4e97bSnorby } 765a1a4e97bSnorby 766a1a4e97bSnorby int 767a1a4e97bSnorby kroute_remove(struct kroute_node *kr) 768a1a4e97bSnorby { 769a1a4e97bSnorby struct kroute_node *krm; 770a1a4e97bSnorby 771a1a4e97bSnorby if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) { 772a1a4e97bSnorby log_warnx("kroute_remove failed to find %s/%u", 773f8efa005Sclaudio log_in6addr(&kr->r.prefix), kr->r.prefixlen); 774a1a4e97bSnorby return (-1); 775a1a4e97bSnorby } 776a1a4e97bSnorby 777a1a4e97bSnorby if (krm == kr) { 778a1a4e97bSnorby /* head element */ 779a1a4e97bSnorby if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) { 780a1a4e97bSnorby log_warnx("kroute_remove failed for %s/%u", 781f8efa005Sclaudio log_in6addr(&kr->r.prefix), kr->r.prefixlen); 782a1a4e97bSnorby return (-1); 783a1a4e97bSnorby } 784a1a4e97bSnorby if (kr->next != NULL) { 785a1a4e97bSnorby if (RB_INSERT(kroute_tree, &krt, kr->next) != NULL) { 786a1a4e97bSnorby log_warnx("kroute_remove failed to add %s/%u", 787f8efa005Sclaudio log_in6addr(&kr->r.prefix), 788f8efa005Sclaudio kr->r.prefixlen); 789a1a4e97bSnorby return (-1); 790a1a4e97bSnorby } 791a1a4e97bSnorby } 792a1a4e97bSnorby } else { 793a1a4e97bSnorby /* somewhere in the list */ 794a1a4e97bSnorby while (krm->next != kr && krm->next != NULL) 795a1a4e97bSnorby krm = krm->next; 796a1a4e97bSnorby if (krm->next == NULL) { 797a1a4e97bSnorby log_warnx("kroute_remove multipath list corrupted " 798f8efa005Sclaudio "for %s/%u", log_in6addr(&kr->r.prefix), 799a1a4e97bSnorby kr->r.prefixlen); 800a1a4e97bSnorby return (-1); 801a1a4e97bSnorby } 802a1a4e97bSnorby krm->next = kr->next; 803a1a4e97bSnorby } 804a1a4e97bSnorby 805a1a4e97bSnorby kr_redist_remove(krm, kr); 806a1a4e97bSnorby rtlabel_unref(kr->r.rtlabel); 807a1a4e97bSnorby 808a1a4e97bSnorby free(kr); 809a1a4e97bSnorby return (0); 810a1a4e97bSnorby } 811a1a4e97bSnorby 812a1a4e97bSnorby void 813a1a4e97bSnorby kroute_clear(void) 814a1a4e97bSnorby { 815a1a4e97bSnorby struct kroute_node *kr; 816a1a4e97bSnorby 817a1a4e97bSnorby while ((kr = RB_MIN(kroute_tree, &krt)) != NULL) 818a1a4e97bSnorby kroute_remove(kr); 819a1a4e97bSnorby } 820a1a4e97bSnorby 8216c9e7a5bSclaudio struct iface * 822a1a4e97bSnorby kif_update(u_short ifindex, int flags, struct if_data *ifd, 823a1a4e97bSnorby struct sockaddr_dl *sdl) 824a1a4e97bSnorby { 8256c9e7a5bSclaudio struct iface *iface; 8266c9e7a5bSclaudio char ifname[IF_NAMESIZE]; 827a1a4e97bSnorby 8286c9e7a5bSclaudio if ((iface = if_find(ifindex)) == NULL) { 8296c9e7a5bSclaudio bzero(ifname, sizeof(ifname)); 8309beedae0Sstsp if (sdl && sdl->sdl_family == AF_LINK) { 8316c9e7a5bSclaudio if (sdl->sdl_nlen >= sizeof(ifname)) 8326c9e7a5bSclaudio memcpy(ifname, sdl->sdl_data, 8336c9e7a5bSclaudio sizeof(ifname) - 1); 834a1a4e97bSnorby else if (sdl->sdl_nlen > 0) 8356c9e7a5bSclaudio memcpy(ifname, sdl->sdl_data, sdl->sdl_nlen); 8369beedae0Sstsp else 8379beedae0Sstsp return (NULL); 8389beedae0Sstsp } else 8399beedae0Sstsp return (NULL); 8406c9e7a5bSclaudio if ((iface = if_new(ifindex, ifname)) == NULL) 8416c9e7a5bSclaudio return (NULL); 8426c9e7a5bSclaudio } 8436c9e7a5bSclaudio 8446c9e7a5bSclaudio if_update(iface, ifd->ifi_mtu, flags, ifd->ifi_type, 8455d393f89Sremi ifd->ifi_link_state, ifd->ifi_baudrate, ifd->ifi_rdomain); 8466c9e7a5bSclaudio 8476c9e7a5bSclaudio return (iface); 848a1a4e97bSnorby } 849a1a4e97bSnorby 850a1a4e97bSnorby int 851a1a4e97bSnorby kif_validate(u_short ifindex) 852a1a4e97bSnorby { 8536c9e7a5bSclaudio struct iface *iface; 854a1a4e97bSnorby 8556c9e7a5bSclaudio if ((iface = if_find(ifindex)) == NULL) { 856a1a4e97bSnorby log_warnx("interface with index %u not found", ifindex); 857376a7614Sclaudio return (-1); 858a1a4e97bSnorby } 859a1a4e97bSnorby 8606ccc0b94Sclaudio return ((iface->flags & IFF_UP) && LINK_STATE_IS_UP(iface->linkstate)); 861a1a4e97bSnorby } 862a1a4e97bSnorby 863a1a4e97bSnorby struct kroute_node * 864f8efa005Sclaudio kroute_match(struct in6_addr *key) 865a1a4e97bSnorby { 866a1a4e97bSnorby int i; 867a1a4e97bSnorby struct kroute_node *kr; 868f8efa005Sclaudio struct in6_addr ina; 869a1a4e97bSnorby 870a1a4e97bSnorby /* we will never match the default route */ 871f8efa005Sclaudio for (i = 128; i > 0; i--) { 872f8efa005Sclaudio inet6applymask(&ina, key, i); 873afd0ab5dSfriehm if ((kr = kroute_find(&ina, i, RTP_ANY)) != NULL) 874a1a4e97bSnorby return (kr); 875f8efa005Sclaudio } 876a1a4e97bSnorby 877a1a4e97bSnorby /* if we don't have a match yet, try to find a default route */ 878afd0ab5dSfriehm if ((kr = kroute_find(&in6addr_any, 0, RTP_ANY)) != NULL) 879a1a4e97bSnorby return (kr); 880a1a4e97bSnorby 881a1a4e97bSnorby return (NULL); 882a1a4e97bSnorby } 883a1a4e97bSnorby 884a1a4e97bSnorby /* misc */ 885a1a4e97bSnorby int 886a1a4e97bSnorby protect_lo(void) 887a1a4e97bSnorby { 888a1a4e97bSnorby struct kroute_node *kr; 889a1a4e97bSnorby 890f8efa005Sclaudio /* special protection for loopback */ 891a1a4e97bSnorby if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { 892a1a4e97bSnorby log_warn("protect_lo"); 893a1a4e97bSnorby return (-1); 894a1a4e97bSnorby } 895f8efa005Sclaudio memcpy(&kr->r.prefix, &in6addr_loopback, sizeof(kr->r.prefix)); 896f8efa005Sclaudio kr->r.prefixlen = 128; 897a1a4e97bSnorby kr->r.flags = F_KERNEL|F_CONNECTED; 898a1a4e97bSnorby 899a1a4e97bSnorby if (RB_INSERT(kroute_tree, &krt, kr) != NULL) 900a1a4e97bSnorby free(kr); /* kernel route already there, no problem */ 901a1a4e97bSnorby 902a1a4e97bSnorby return (0); 903a1a4e97bSnorby } 904a1a4e97bSnorby 905aa03706cSclaudio #define ROUNDUP(a) \ 90693c5cf97Sbluhm ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 907a1a4e97bSnorby 908a1a4e97bSnorby void 909a1a4e97bSnorby get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 910a1a4e97bSnorby { 911a1a4e97bSnorby int i; 912a1a4e97bSnorby 913a1a4e97bSnorby for (i = 0; i < RTAX_MAX; i++) { 914a1a4e97bSnorby if (addrs & (1 << i)) { 915a1a4e97bSnorby rti_info[i] = sa; 916a1a4e97bSnorby sa = (struct sockaddr *)((char *)(sa) + 917aa03706cSclaudio ROUNDUP(sa->sa_len)); 918a1a4e97bSnorby } else 919a1a4e97bSnorby rti_info[i] = NULL; 920a1a4e97bSnorby } 921a1a4e97bSnorby } 922a1a4e97bSnorby 923a1a4e97bSnorby void 924bf9d8b16Sdenis if_change(u_short ifindex, int flags, struct if_data *ifd, 925bf9d8b16Sdenis struct sockaddr_dl *sdl) 926a1a4e97bSnorby { 927a1a4e97bSnorby struct kroute_node *kr, *tkr; 9286c9e7a5bSclaudio struct iface *iface; 9296ccc0b94Sclaudio u_int8_t wasvalid, isvalid; 9306ccc0b94Sclaudio 9316ccc0b94Sclaudio wasvalid = kif_validate(ifindex); 932a1a4e97bSnorby 933bf9d8b16Sdenis if ((iface = kif_update(ifindex, flags, ifd, sdl)) == NULL) { 934a1a4e97bSnorby log_warn("if_change: kif_update(%u)", ifindex); 935a1a4e97bSnorby return; 936a1a4e97bSnorby } 937a1a4e97bSnorby 938a66c91f2Sremi /* inform engine and rde about state change */ 939a66c91f2Sremi main_imsg_compose_rde(IMSG_IFINFO, 0, iface, sizeof(struct iface)); 940a66c91f2Sremi main_imsg_compose_ospfe(IMSG_IFINFO, 0, iface, sizeof(struct iface)); 941a1a4e97bSnorby 94264fe7c7fSjca isvalid = (iface->flags & IFF_UP) && 94364fe7c7fSjca LINK_STATE_IS_UP(iface->linkstate); 94464fe7c7fSjca 94564fe7c7fSjca if (wasvalid == isvalid) 94664fe7c7fSjca return; /* nothing changed wrt validity */ 94764fe7c7fSjca 948a1a4e97bSnorby /* update redistribute list */ 949a1a4e97bSnorby RB_FOREACH(kr, kroute_tree, &krt) { 950a1a4e97bSnorby for (tkr = kr; tkr != NULL; tkr = tkr->next) { 951a1a4e97bSnorby if (tkr->r.ifindex == ifindex) { 9526ccc0b94Sclaudio if (isvalid) 953a1a4e97bSnorby tkr->r.flags &= ~F_DOWN; 954a1a4e97bSnorby else 955a1a4e97bSnorby tkr->r.flags |= F_DOWN; 956a1a4e97bSnorby 957a1a4e97bSnorby } 958a1a4e97bSnorby } 959a1a4e97bSnorby kr_redistribute(kr); 960a1a4e97bSnorby } 961a1a4e97bSnorby } 962a1a4e97bSnorby 963a1a4e97bSnorby void 964a1a4e97bSnorby if_newaddr(u_short ifindex, struct sockaddr_in6 *ifa, struct sockaddr_in6 *mask, 965a1a4e97bSnorby struct sockaddr_in6 *brd) 966a1a4e97bSnorby { 9676c9e7a5bSclaudio struct iface *iface; 9686c9e7a5bSclaudio struct iface_addr *ia; 9698e1674f3Sbluhm struct ifaddrchange ifc; 970a1a4e97bSnorby 971a1a4e97bSnorby if (ifa == NULL || ifa->sin6_family != AF_INET6) 972a1a4e97bSnorby return; 9736c9e7a5bSclaudio if ((iface = if_find(ifindex)) == NULL) { 974fae4ffeaSderaadt log_warnx("if_newaddr: corresponding if %d not found", ifindex); 975a1a4e97bSnorby return; 976a1a4e97bSnorby } 97795ff14dfSstsp 97895ff14dfSstsp /* We only care about link-local and global-scope. */ 97995ff14dfSstsp if (IN6_IS_ADDR_UNSPECIFIED(&ifa->sin6_addr) || 98095ff14dfSstsp IN6_IS_ADDR_LOOPBACK(&ifa->sin6_addr) || 98195ff14dfSstsp IN6_IS_ADDR_MULTICAST(&ifa->sin6_addr) || 98295ff14dfSstsp IN6_IS_ADDR_SITELOCAL(&ifa->sin6_addr) || 98395ff14dfSstsp IN6_IS_ADDR_V4MAPPED(&ifa->sin6_addr) || 98495ff14dfSstsp IN6_IS_ADDR_V4COMPAT(&ifa->sin6_addr)) 98595ff14dfSstsp return; 98695ff14dfSstsp 98753cbafe3Sbluhm clearscope(&ifa->sin6_addr); 988f52b4fa0Sclaudio 9893bea1752Sclaudio if (IN6_IS_ADDR_LINKLOCAL(&ifa->sin6_addr) || 9903bea1752Sclaudio iface->flags & IFF_LOOPBACK) 9916c9e7a5bSclaudio iface->addr = ifa->sin6_addr; 9926c9e7a5bSclaudio 9936c9e7a5bSclaudio if ((ia = calloc(1, sizeof(struct iface_addr))) == NULL) 9946c9e7a5bSclaudio fatal("if_newaddr"); 9956c9e7a5bSclaudio 9966c9e7a5bSclaudio ia->addr = ifa->sin6_addr; 9976c9e7a5bSclaudio 998a1a4e97bSnorby if (mask) 99977a40b56Sstsp ia->prefixlen = mask2prefixlen(mask); 1000a1a4e97bSnorby else 10016c9e7a5bSclaudio ia->prefixlen = 0; 100277a40b56Sstsp if (brd && brd->sin6_family == AF_INET6) 100377a40b56Sstsp ia->dstbrd = brd->sin6_addr; 1004a1a4e97bSnorby else 100577a40b56Sstsp bzero(&ia->dstbrd, sizeof(ia->dstbrd)); 1006a1a4e97bSnorby 100777a40b56Sstsp switch (iface->type) { 100877a40b56Sstsp case IF_TYPE_BROADCAST: 100977a40b56Sstsp case IF_TYPE_NBMA: 101077a40b56Sstsp log_debug("if_newaddr: ifindex %u, addr %s/%d", 101177a40b56Sstsp ifindex, log_in6addr(&ia->addr), ia->prefixlen); 101277a40b56Sstsp break; 101377a40b56Sstsp case IF_TYPE_VIRTUALLINK: /* FIXME */ 101477a40b56Sstsp break; 101577a40b56Sstsp case IF_TYPE_POINTOPOINT: 101677a40b56Sstsp case IF_TYPE_POINTOMULTIPOINT: 101777a40b56Sstsp log_debug("if_newaddr: ifindex %u, addr %s/%d, " 101877a40b56Sstsp "dest %s", ifindex, log_in6addr(&ia->addr), 101977a40b56Sstsp ia->prefixlen, log_in6addr(&ia->dstbrd)); 102077a40b56Sstsp break; 102177a40b56Sstsp default: 102277a40b56Sstsp fatalx("if_newaddr: unknown interface type"); 102377a40b56Sstsp } 102477a40b56Sstsp 10256c9e7a5bSclaudio TAILQ_INSERT_TAIL(&iface->ifa_list, ia, entry); 102689388a06Sclaudio /* inform engine and rde if interface is used */ 102789388a06Sclaudio if (iface->cflags & F_IFACE_CONFIGURED) { 10288e1674f3Sbluhm ifc.addr = ia->addr; 10298e1674f3Sbluhm ifc.dstbrd = ia->dstbrd; 10308e1674f3Sbluhm ifc.prefixlen = ia->prefixlen; 10318e1674f3Sbluhm ifc.ifindex = ifindex; 10328e1674f3Sbluhm main_imsg_compose_ospfe(IMSG_IFADDRNEW, 0, &ifc, sizeof(ifc)); 1033787da5a7Sbluhm main_imsg_compose_rde(IMSG_IFADDRNEW, 0, &ifc, sizeof(ifc)); 10348e1674f3Sbluhm } 103589388a06Sclaudio } 10368e1674f3Sbluhm 10378e1674f3Sbluhm void 10388e1674f3Sbluhm if_deladdr(u_short ifindex, struct sockaddr_in6 *ifa, struct sockaddr_in6 *mask, 10398e1674f3Sbluhm struct sockaddr_in6 *brd) 10408e1674f3Sbluhm { 10418e1674f3Sbluhm struct iface *iface; 10428e1674f3Sbluhm struct iface_addr *ia, *nia; 10438e1674f3Sbluhm struct ifaddrchange ifc; 10448e1674f3Sbluhm 10458e1674f3Sbluhm if (ifa == NULL || ifa->sin6_family != AF_INET6) 10468e1674f3Sbluhm return; 10478e1674f3Sbluhm if ((iface = if_find(ifindex)) == NULL) { 1048fae4ffeaSderaadt log_warnx("if_deladdr: corresponding if %d not found", ifindex); 10498e1674f3Sbluhm return; 10508e1674f3Sbluhm } 10518e1674f3Sbluhm 10528e1674f3Sbluhm /* We only care about link-local and global-scope. */ 10538e1674f3Sbluhm if (IN6_IS_ADDR_UNSPECIFIED(&ifa->sin6_addr) || 10548e1674f3Sbluhm IN6_IS_ADDR_LOOPBACK(&ifa->sin6_addr) || 10558e1674f3Sbluhm IN6_IS_ADDR_MULTICAST(&ifa->sin6_addr) || 10568e1674f3Sbluhm IN6_IS_ADDR_SITELOCAL(&ifa->sin6_addr) || 10578e1674f3Sbluhm IN6_IS_ADDR_V4MAPPED(&ifa->sin6_addr) || 10588e1674f3Sbluhm IN6_IS_ADDR_V4COMPAT(&ifa->sin6_addr)) 10598e1674f3Sbluhm return; 10608e1674f3Sbluhm 106153cbafe3Sbluhm clearscope(&ifa->sin6_addr); 10628e1674f3Sbluhm 10638e1674f3Sbluhm for (ia = TAILQ_FIRST(&iface->ifa_list); ia != NULL; ia = nia) { 10648e1674f3Sbluhm nia = TAILQ_NEXT(ia, entry); 10658e1674f3Sbluhm 10668e1674f3Sbluhm if (IN6_ARE_ADDR_EQUAL(&ia->addr, &ifa->sin6_addr)) { 10678e1674f3Sbluhm log_debug("if_deladdr: ifindex %u, addr %s/%d", 10688e1674f3Sbluhm ifindex, log_in6addr(&ia->addr), ia->prefixlen); 10698e1674f3Sbluhm TAILQ_REMOVE(&iface->ifa_list, ia, entry); 107089388a06Sclaudio /* inform engine and rde if interface is used */ 107189388a06Sclaudio if (iface->cflags & F_IFACE_CONFIGURED) { 10728e1674f3Sbluhm ifc.addr = ia->addr; 10738e1674f3Sbluhm ifc.dstbrd = ia->dstbrd; 10748e1674f3Sbluhm ifc.prefixlen = ia->prefixlen; 10758e1674f3Sbluhm ifc.ifindex = ifindex; 10768e1674f3Sbluhm main_imsg_compose_ospfe(IMSG_IFADDRDEL, 0, &ifc, 10778e1674f3Sbluhm sizeof(ifc)); 1078787da5a7Sbluhm main_imsg_compose_rde(IMSG_IFADDRDEL, 0, &ifc, 1079787da5a7Sbluhm sizeof(ifc)); 108089388a06Sclaudio } 10818e1674f3Sbluhm free(ia); 10828e1674f3Sbluhm return; 10838e1674f3Sbluhm } 10848e1674f3Sbluhm } 1085a1a4e97bSnorby } 1086a1a4e97bSnorby 1087a1a4e97bSnorby void 1088a1a4e97bSnorby if_announce(void *msg) 1089a1a4e97bSnorby { 1090a1a4e97bSnorby struct if_announcemsghdr *ifan; 10916c9e7a5bSclaudio struct iface *iface; 1092a1a4e97bSnorby 1093a1a4e97bSnorby ifan = msg; 1094a1a4e97bSnorby 1095a1a4e97bSnorby switch (ifan->ifan_what) { 1096a1a4e97bSnorby case IFAN_ARRIVAL: 10976c9e7a5bSclaudio if ((iface = if_new(ifan->ifan_index, ifan->ifan_name)) == NULL) 10983af4e127Snorby fatal("if_announce failed"); 1099a1a4e97bSnorby break; 1100a1a4e97bSnorby case IFAN_DEPARTURE: 11016c9e7a5bSclaudio iface = if_find(ifan->ifan_index); 1102*018a085aSanton if (iface != NULL) 11036c9e7a5bSclaudio if_del(iface); 1104a1a4e97bSnorby break; 1105a1a4e97bSnorby } 1106a1a4e97bSnorby } 1107a1a4e97bSnorby 1108a1a4e97bSnorby /* rtsock */ 1109a1a4e97bSnorby int 1110a1a4e97bSnorby send_rtmsg(int fd, int action, struct kroute *kroute) 1111a1a4e97bSnorby { 1112a1a4e97bSnorby struct iovec iov[5]; 1113a1a4e97bSnorby struct rt_msghdr hdr; 1114aa03706cSclaudio struct pad { 1115aa03706cSclaudio struct sockaddr_in6 addr; 1116aa03706cSclaudio char pad[sizeof(long)]; /* thank you IPv6 */ 1117aa03706cSclaudio } prefix, nexthop, mask; 111846bb488dSbluhm struct { 111946bb488dSbluhm struct sockaddr_dl addr; 112046bb488dSbluhm char pad[sizeof(long)]; 112146bb488dSbluhm } ifp; 1122a1a4e97bSnorby struct sockaddr_rtlabel sa_rl; 1123a1a4e97bSnorby int iovcnt = 0; 1124a1a4e97bSnorby const char *label; 1125a1a4e97bSnorby 1126a1a4e97bSnorby if (kr_state.fib_sync == 0) 1127a1a4e97bSnorby return (0); 1128a1a4e97bSnorby 1129a1a4e97bSnorby /* initialize header */ 1130a1a4e97bSnorby bzero(&hdr, sizeof(hdr)); 1131a1a4e97bSnorby hdr.rtm_version = RTM_VERSION; 1132a1a4e97bSnorby hdr.rtm_type = action; 1133d1e43ac2Sremi hdr.rtm_priority = kr_state.fib_prio; 11345d393f89Sremi hdr.rtm_tableid = kr_state.rdomain; /* rtableid */ 1135aa03706cSclaudio if (action == RTM_CHANGE) 1136aa03706cSclaudio hdr.rtm_fmask = RTF_REJECT|RTF_BLACKHOLE; 1137afd0ab5dSfriehm else 1138afd0ab5dSfriehm hdr.rtm_flags = RTF_MPATH; 1139a1a4e97bSnorby hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ 1140aa03706cSclaudio hdr.rtm_hdrlen = sizeof(hdr); 1141a1a4e97bSnorby hdr.rtm_msglen = sizeof(hdr); 1142a1a4e97bSnorby /* adjust iovec */ 1143a1a4e97bSnorby iov[iovcnt].iov_base = &hdr; 1144a1a4e97bSnorby iov[iovcnt++].iov_len = sizeof(hdr); 1145a1a4e97bSnorby 1146a1a4e97bSnorby bzero(&prefix, sizeof(prefix)); 1147aa03706cSclaudio prefix.addr.sin6_len = sizeof(struct sockaddr_in6); 1148aa03706cSclaudio prefix.addr.sin6_family = AF_INET6; 1149aa03706cSclaudio prefix.addr.sin6_addr = kroute->prefix; 1150a1a4e97bSnorby /* adjust header */ 1151a1a4e97bSnorby hdr.rtm_addrs |= RTA_DST; 1152aa03706cSclaudio hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6)); 1153a1a4e97bSnorby /* adjust iovec */ 1154a1a4e97bSnorby iov[iovcnt].iov_base = &prefix; 1155aa03706cSclaudio iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6)); 1156a1a4e97bSnorby 1157f8efa005Sclaudio if (!IN6_IS_ADDR_UNSPECIFIED(&kroute->nexthop)) { 1158a1a4e97bSnorby bzero(&nexthop, sizeof(nexthop)); 1159aa03706cSclaudio nexthop.addr.sin6_len = sizeof(struct sockaddr_in6); 1160aa03706cSclaudio nexthop.addr.sin6_family = AF_INET6; 1161aa03706cSclaudio nexthop.addr.sin6_addr = kroute->nexthop; 116253cbafe3Sbluhm nexthop.addr.sin6_scope_id = kroute->scope; 11637cb1404fSclaudio /* 11647cb1404fSclaudio * XXX we should set the sin6_scope_id but the kernel 11657cb1404fSclaudio * XXX does not expect it that way. It must be fiddled 11667cb1404fSclaudio * XXX into the sin6_addr. Welcome to the typical 11677cb1404fSclaudio * XXX IPv6 insanity and all without wine bottles. 11687cb1404fSclaudio */ 116953cbafe3Sbluhm embedscope(&nexthop.addr); 117053cbafe3Sbluhm 1171a1a4e97bSnorby /* adjust header */ 1172a1a4e97bSnorby hdr.rtm_flags |= RTF_GATEWAY; 1173a1a4e97bSnorby hdr.rtm_addrs |= RTA_GATEWAY; 1174aa03706cSclaudio hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6)); 1175a1a4e97bSnorby /* adjust iovec */ 1176a1a4e97bSnorby iov[iovcnt].iov_base = &nexthop; 1177aa03706cSclaudio iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6)); 117846bb488dSbluhm } else if (kroute->ifindex) { 117946bb488dSbluhm /* 118046bb488dSbluhm * We don't have an interface address in that network, 118146bb488dSbluhm * so we install a cloning route. The kernel will then 11829cc19ef1Ssthen * do neighbor discovery. 118346bb488dSbluhm */ 118446bb488dSbluhm bzero(&ifp, sizeof(ifp)); 118546bb488dSbluhm ifp.addr.sdl_len = sizeof(struct sockaddr_dl); 11869529e7dcSclaudio ifp.addr.sdl_family = AF_LINK; 11876ccc0b94Sclaudio 118846bb488dSbluhm ifp.addr.sdl_index = kroute->ifindex; 118946bb488dSbluhm /* adjust header */ 119046bb488dSbluhm hdr.rtm_flags |= RTF_CLONING; 119146bb488dSbluhm hdr.rtm_addrs |= RTA_GATEWAY; 119246bb488dSbluhm hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_dl)); 119346bb488dSbluhm /* adjust iovec */ 119446bb488dSbluhm iov[iovcnt].iov_base = &ifp; 119546bb488dSbluhm iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_dl)); 1196a1a4e97bSnorby } 1197a1a4e97bSnorby 1198a1a4e97bSnorby bzero(&mask, sizeof(mask)); 1199aa03706cSclaudio mask.addr.sin6_len = sizeof(struct sockaddr_in6); 1200aa03706cSclaudio mask.addr.sin6_family = AF_INET6; 1201aa03706cSclaudio mask.addr.sin6_addr = *prefixlen2mask(kroute->prefixlen); 1202a1a4e97bSnorby /* adjust header */ 1203eecab31bSclaudio if (kroute->prefixlen == 128) 1204eecab31bSclaudio hdr.rtm_flags |= RTF_HOST; 1205a1a4e97bSnorby hdr.rtm_addrs |= RTA_NETMASK; 1206aa03706cSclaudio hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6)); 1207a1a4e97bSnorby /* adjust iovec */ 1208a1a4e97bSnorby iov[iovcnt].iov_base = &mask; 1209aa03706cSclaudio iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6)); 1210a1a4e97bSnorby 1211a1a4e97bSnorby if (kroute->rtlabel != 0) { 1212a1a4e97bSnorby sa_rl.sr_len = sizeof(sa_rl); 1213a1a4e97bSnorby sa_rl.sr_family = AF_UNSPEC; 1214a1a4e97bSnorby label = rtlabel_id2name(kroute->rtlabel); 1215a1a4e97bSnorby if (strlcpy(sa_rl.sr_label, label, 1216a1a4e97bSnorby sizeof(sa_rl.sr_label)) >= sizeof(sa_rl.sr_label)) { 1217a1a4e97bSnorby log_warnx("send_rtmsg: invalid rtlabel"); 1218a1a4e97bSnorby return (-1); 1219a1a4e97bSnorby } 1220a1a4e97bSnorby /* adjust header */ 1221a1a4e97bSnorby hdr.rtm_addrs |= RTA_LABEL; 1222a1a4e97bSnorby hdr.rtm_msglen += sizeof(sa_rl); 1223a1a4e97bSnorby /* adjust iovec */ 1224a1a4e97bSnorby iov[iovcnt].iov_base = &sa_rl; 1225a1a4e97bSnorby iov[iovcnt++].iov_len = sizeof(sa_rl); 1226a1a4e97bSnorby } 1227a1a4e97bSnorby 1228a1a4e97bSnorby retry: 1229a1a4e97bSnorby if (writev(fd, iov, iovcnt) == -1) { 12304324a051Sbluhm if (errno == ESRCH) { 1231a1a4e97bSnorby if (hdr.rtm_type == RTM_CHANGE) { 1232a1a4e97bSnorby hdr.rtm_type = RTM_ADD; 1233a1a4e97bSnorby goto retry; 1234a1a4e97bSnorby } else if (hdr.rtm_type == RTM_DELETE) { 1235a1a4e97bSnorby log_info("route %s/%u vanished before delete", 1236f8efa005Sclaudio log_sockaddr(&prefix), kroute->prefixlen); 1237a1a4e97bSnorby return (0); 12384324a051Sbluhm } 12394324a051Sbluhm } 12404324a051Sbluhm log_warn("send_rtmsg: action %u, prefix %s/%u", hdr.rtm_type, 1241f8efa005Sclaudio log_sockaddr(&prefix), kroute->prefixlen); 1242a1a4e97bSnorby return (0); 1243a1a4e97bSnorby } 1244a1a4e97bSnorby 1245a1a4e97bSnorby return (0); 1246a1a4e97bSnorby } 1247a1a4e97bSnorby 1248a1a4e97bSnorby int 1249a1a4e97bSnorby fetchtable(void) 1250a1a4e97bSnorby { 1251a1a4e97bSnorby size_t len; 1252a1a4e97bSnorby int mib[7]; 1253bf9d8b16Sdenis char *buf; 1254bf9d8b16Sdenis int rv; 1255a1a4e97bSnorby 1256a1a4e97bSnorby mib[0] = CTL_NET; 1257149dc9fcSguenther mib[1] = PF_ROUTE; 1258a1a4e97bSnorby mib[2] = 0; 1259fb95416dSclaudio mib[3] = AF_INET6; 1260a1a4e97bSnorby mib[4] = NET_RT_DUMP; 1261a1a4e97bSnorby mib[5] = 0; 12625d393f89Sremi mib[6] = kr_state.rdomain; /* rtableid */ 1263a1a4e97bSnorby 1264a1a4e97bSnorby if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) { 1265a1a4e97bSnorby log_warn("sysctl"); 1266a1a4e97bSnorby return (-1); 1267a1a4e97bSnorby } 1268a1a4e97bSnorby if ((buf = malloc(len)) == NULL) { 1269a1a4e97bSnorby log_warn("fetchtable"); 1270a1a4e97bSnorby return (-1); 1271a1a4e97bSnorby } 1272a1a4e97bSnorby if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) { 1273a1a4e97bSnorby log_warn("sysctl"); 1274a1a4e97bSnorby free(buf); 1275a1a4e97bSnorby return (-1); 1276a1a4e97bSnorby } 1277a1a4e97bSnorby 1278bf9d8b16Sdenis rv = rtmsg_process(buf, len); 1279a1a4e97bSnorby free(buf); 1280a1a4e97bSnorby 1281bf9d8b16Sdenis return (rv); 1282a1a4e97bSnorby } 1283a1a4e97bSnorby 1284a1a4e97bSnorby int 1285a1a4e97bSnorby fetchifs(u_short ifindex) 1286a1a4e97bSnorby { 1287a1a4e97bSnorby size_t len; 1288a1a4e97bSnorby int mib[6]; 1289bf9d8b16Sdenis char *buf; 1290bf9d8b16Sdenis int rv; 1291a1a4e97bSnorby 1292a1a4e97bSnorby mib[0] = CTL_NET; 1293149dc9fcSguenther mib[1] = PF_ROUTE; 1294a1a4e97bSnorby mib[2] = 0; 1295a1a4e97bSnorby mib[3] = AF_INET6; 1296a1a4e97bSnorby mib[4] = NET_RT_IFLIST; 1297a1a4e97bSnorby mib[5] = ifindex; 1298a1a4e97bSnorby 1299a1a4e97bSnorby if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { 1300a1a4e97bSnorby log_warn("sysctl"); 1301a1a4e97bSnorby return (-1); 1302a1a4e97bSnorby } 1303a1a4e97bSnorby if ((buf = malloc(len)) == NULL) { 1304376a7614Sclaudio log_warn("fetchifs"); 1305a1a4e97bSnorby return (-1); 1306a1a4e97bSnorby } 1307a1a4e97bSnorby if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) { 1308a1a4e97bSnorby log_warn("sysctl"); 1309a1a4e97bSnorby free(buf); 1310a1a4e97bSnorby return (-1); 1311a1a4e97bSnorby } 1312a1a4e97bSnorby 1313bf9d8b16Sdenis rv = rtmsg_process(buf, len); 1314a1a4e97bSnorby free(buf); 1315bf9d8b16Sdenis 1316bf9d8b16Sdenis return (rv); 1317a1a4e97bSnorby } 1318a1a4e97bSnorby 1319a1a4e97bSnorby int 1320a1a4e97bSnorby dispatch_rtmsg(void) 1321a1a4e97bSnorby { 1322a1a4e97bSnorby char buf[RT_BUF_SIZE]; 1323a1a4e97bSnorby ssize_t n; 1324a1a4e97bSnorby 1325a1a4e97bSnorby if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) { 132635251ca5Sclaudio if (errno == EAGAIN || errno == EINTR) 132735251ca5Sclaudio return (0); 1328a1a4e97bSnorby log_warn("dispatch_rtmsg: read error"); 1329a1a4e97bSnorby return (-1); 1330a1a4e97bSnorby } 1331a1a4e97bSnorby 1332a1a4e97bSnorby if (n == 0) { 1333a1a4e97bSnorby log_warnx("routing socket closed"); 1334a1a4e97bSnorby return (-1); 1335a1a4e97bSnorby } 1336a1a4e97bSnorby 1337bf9d8b16Sdenis return (rtmsg_process(buf, n)); 1338bf9d8b16Sdenis } 1339bf9d8b16Sdenis 1340bf9d8b16Sdenis int 1341bf9d8b16Sdenis rtmsg_process(char *buf, size_t len) 1342bf9d8b16Sdenis { 1343bf9d8b16Sdenis struct rt_msghdr *rtm; 1344bf9d8b16Sdenis struct if_msghdr ifm; 1345bf9d8b16Sdenis struct ifa_msghdr *ifam; 1346bf9d8b16Sdenis struct sockaddr *sa, *rti_info[RTAX_MAX]; 1347bf9d8b16Sdenis struct sockaddr_in6 *sa_in6; 1348bf9d8b16Sdenis struct sockaddr_rtlabel *label; 1349bf9d8b16Sdenis struct kroute_node *kr, *okr; 1350bf9d8b16Sdenis struct in6_addr prefix, nexthop; 1351bf9d8b16Sdenis u_int8_t prefixlen, prio; 1352bf9d8b16Sdenis int flags, mpath; 1353bf9d8b16Sdenis unsigned int scope; 1354bf9d8b16Sdenis u_short ifindex = 0; 1355dd3b9a80Ssthen int rv, delay; 1356bf9d8b16Sdenis size_t offset; 1357bf9d8b16Sdenis char *next; 1358bf9d8b16Sdenis 1359bf9d8b16Sdenis for (offset = 0; offset < len; offset += rtm->rtm_msglen) { 1360bf9d8b16Sdenis next = buf + offset; 1361a1a4e97bSnorby rtm = (struct rt_msghdr *)next; 1362bf9d8b16Sdenis if (len < offset + sizeof(u_short) || 1363bf9d8b16Sdenis len < offset + rtm->rtm_msglen) 1364bf9d8b16Sdenis fatalx("rtmsg_process: partial rtm in buffer"); 1365dddf3188Sclaudio if (rtm->rtm_version != RTM_VERSION) 1366dddf3188Sclaudio continue; 1367a1a4e97bSnorby 1368f8efa005Sclaudio bzero(&prefix, sizeof(prefix)); 1369f8efa005Sclaudio bzero(&nexthop, sizeof(nexthop)); 1370eecab31bSclaudio scope = 0; 1371a1a4e97bSnorby prefixlen = 0; 1372a1a4e97bSnorby flags = F_KERNEL; 1373a1a4e97bSnorby mpath = 0; 1374afd0ab5dSfriehm prio = 0; 1375a1a4e97bSnorby 13767a13f2d5Schris sa = (struct sockaddr *)(next + rtm->rtm_hdrlen); 1377a1a4e97bSnorby get_rtaddrs(rtm->rtm_addrs, sa, rti_info); 1378a1a4e97bSnorby 1379bf9d8b16Sdenis switch (rtm->rtm_type) { 1380bf9d8b16Sdenis case RTM_ADD: 1381bf9d8b16Sdenis case RTM_GET: 1382bf9d8b16Sdenis case RTM_CHANGE: 1383bf9d8b16Sdenis case RTM_DELETE: 1384bf9d8b16Sdenis if (rtm->rtm_errno) /* failed attempts... */ 1385bf9d8b16Sdenis continue; 1386bf9d8b16Sdenis 13875d393f89Sremi if (rtm->rtm_tableid != kr_state.rdomain) 1388a1a4e97bSnorby continue; 1389a1a4e97bSnorby 1390bf9d8b16Sdenis if (rtm->rtm_type == RTM_GET && 1391bf9d8b16Sdenis rtm->rtm_pid != kr_state.pid) /* caused by us */ 1392a1a4e97bSnorby continue; 1393a1a4e97bSnorby 1394bf9d8b16Sdenis if ((sa = rti_info[RTAX_DST]) == NULL) 1395a1a4e97bSnorby continue; 1396a1a4e97bSnorby 13974783dd8fSclaudio /* Skip ARP/ND cache and broadcast routes. */ 13984783dd8fSclaudio if (rtm->rtm_flags & (RTF_LLINFO|RTF_BROADCAST)) 1399a1a4e97bSnorby continue; 1400a1a4e97bSnorby 1401a1a4e97bSnorby if (rtm->rtm_flags & RTF_MPATH) 1402a1a4e97bSnorby mpath = 1; 1403afd0ab5dSfriehm prio = rtm->rtm_priority; 1404bf9d8b16Sdenis flags = (prio == kr_state.fib_prio) ? 1405bf9d8b16Sdenis F_OSPFD_INSERTED : F_KERNEL; 1406afd0ab5dSfriehm 1407a1a4e97bSnorby switch (sa->sa_family) { 1408f8efa005Sclaudio case AF_INET6: 1409f8efa005Sclaudio prefix = 1410f8efa005Sclaudio ((struct sockaddr_in6 *)sa)->sin6_addr; 1411f8efa005Sclaudio sa_in6 = (struct sockaddr_in6 *) 1412a1a4e97bSnorby rti_info[RTAX_NETMASK]; 1413f8efa005Sclaudio if (sa_in6 != NULL) { 1414f8efa005Sclaudio if (sa_in6->sin6_len != 0) 1415a1a4e97bSnorby prefixlen = mask2prefixlen( 1416f8efa005Sclaudio sa_in6); 1417a1a4e97bSnorby } else if (rtm->rtm_flags & RTF_HOST) 1418f8efa005Sclaudio prefixlen = 128; 1419a1a4e97bSnorby else 1420f8efa005Sclaudio fatalx("classful IPv6 address?!!"); 1421a1a4e97bSnorby if (rtm->rtm_flags & RTF_STATIC) 1422a1a4e97bSnorby flags |= F_STATIC; 1423001befcbSflorian if (rtm->rtm_flags & RTF_BLACKHOLE) 1424001befcbSflorian flags |= F_BLACKHOLE; 1425001befcbSflorian if (rtm->rtm_flags & RTF_REJECT) 1426001befcbSflorian flags |= F_REJECT; 1427a1a4e97bSnorby if (rtm->rtm_flags & RTF_DYNAMIC) 1428a1a4e97bSnorby flags |= F_DYNAMIC; 1429a1a4e97bSnorby break; 1430a1a4e97bSnorby default: 1431a1a4e97bSnorby continue; 1432a1a4e97bSnorby } 1433a1a4e97bSnorby 1434a1a4e97bSnorby ifindex = rtm->rtm_index; 1435a1a4e97bSnorby if ((sa = rti_info[RTAX_GATEWAY]) != NULL) { 1436a1a4e97bSnorby switch (sa->sa_family) { 143741bcda5aSclaudio case AF_INET6: 1438bf9d8b16Sdenis if (rtm->rtm_flags & RTF_CONNECTED) 1439bf9d8b16Sdenis flags |= F_CONNECTED; 1440bf9d8b16Sdenis 14414195f7faSbluhm sa_in6 = (struct sockaddr_in6 *)sa; 14424195f7faSbluhm /* 14434195f7faSbluhm * XXX The kernel provides the scope 14444195f7faSbluhm * XXX via the kame hack instead of 14454195f7faSbluhm * XXX the scope_id field. 14464195f7faSbluhm */ 14474195f7faSbluhm recoverscope(sa_in6); 14484195f7faSbluhm nexthop = sa_in6->sin6_addr; 14494195f7faSbluhm scope = sa_in6->sin6_scope_id; 1450a1a4e97bSnorby break; 1451a1a4e97bSnorby case AF_LINK: 1452a1a4e97bSnorby flags |= F_CONNECTED; 1453a1a4e97bSnorby break; 1454a1a4e97bSnorby } 1455a1a4e97bSnorby } 1456a1a4e97bSnorby } 1457a1a4e97bSnorby 1458a1a4e97bSnorby switch (rtm->rtm_type) { 1459a1a4e97bSnorby case RTM_ADD: 1460bf9d8b16Sdenis case RTM_GET: 1461a1a4e97bSnorby case RTM_CHANGE: 14623f4a5440Sbluhm if (IN6_IS_ADDR_UNSPECIFIED(&nexthop) && 1463f8efa005Sclaudio !(flags & F_CONNECTED)) { 1464bf9d8b16Sdenis log_warnx("rtmsg_process no nexthop for %s/%u", 1465f8efa005Sclaudio log_in6addr(&prefix), prefixlen); 1466a1a4e97bSnorby continue; 1467a1a4e97bSnorby } 1468a1a4e97bSnorby 1469afd0ab5dSfriehm if ((okr = kroute_find(&prefix, prefixlen, prio)) 1470afd0ab5dSfriehm != NULL) { 1471a1a4e97bSnorby kr = okr; 1472dd3b9a80Ssthen if ((mpath || prio == kr_state.fib_prio) && 1473dd3b9a80Ssthen (kr = kroute_matchgw(okr, &nexthop, scope)) == 1474dd3b9a80Ssthen NULL) { 1475bf9d8b16Sdenis log_warnx("rtmsg_process: mpath route" 1476a1a4e97bSnorby " not found"); 1477a1a4e97bSnorby /* add routes we missed out earlier */ 1478a1a4e97bSnorby goto add; 1479a1a4e97bSnorby } 1480a1a4e97bSnorby 1481a1a4e97bSnorby if (kr->r.flags & F_REDISTRIBUTED) 1482a1a4e97bSnorby flags |= F_REDISTRIBUTED; 1483f8efa005Sclaudio kr->r.nexthop = nexthop; 1484eecab31bSclaudio kr->r.scope = scope; 1485a1a4e97bSnorby kr->r.flags = flags; 1486a1a4e97bSnorby kr->r.ifindex = ifindex; 1487a1a4e97bSnorby 1488a1a4e97bSnorby rtlabel_unref(kr->r.rtlabel); 1489a1a4e97bSnorby kr->r.rtlabel = 0; 1490a1a4e97bSnorby kr->r.ext_tag = 0; 1491a1a4e97bSnorby if ((label = (struct sockaddr_rtlabel *) 1492a1a4e97bSnorby rti_info[RTAX_LABEL]) != NULL) { 1493a1a4e97bSnorby kr->r.rtlabel = 1494a1a4e97bSnorby rtlabel_name2id(label->sr_label); 1495a1a4e97bSnorby kr->r.ext_tag = 1496a1a4e97bSnorby rtlabel_id2tag(kr->r.rtlabel); 1497a1a4e97bSnorby } 1498a1a4e97bSnorby 1499a1a4e97bSnorby if (kif_validate(kr->r.ifindex)) 1500a1a4e97bSnorby kr->r.flags &= ~F_DOWN; 1501a1a4e97bSnorby else 1502a1a4e97bSnorby kr->r.flags |= F_DOWN; 1503a1a4e97bSnorby 1504a1a4e97bSnorby /* just readd, the RDE will care */ 1505dd3b9a80Ssthen kr->serial = kr_state.fib_serial; 1506dd3b9a80Ssthen kr_redistribute(kr); 1507a1a4e97bSnorby } else { 1508a1a4e97bSnorby add: 1509a1a4e97bSnorby if ((kr = calloc(1, 1510a1a4e97bSnorby sizeof(struct kroute_node))) == NULL) { 1511bf9d8b16Sdenis log_warn("rtmsg_process calloc"); 1512a1a4e97bSnorby return (-1); 1513a1a4e97bSnorby } 1514f8efa005Sclaudio kr->r.prefix = prefix; 1515a1a4e97bSnorby kr->r.prefixlen = prefixlen; 1516f8efa005Sclaudio kr->r.nexthop = nexthop; 1517eecab31bSclaudio kr->r.scope = scope; 1518a1a4e97bSnorby kr->r.flags = flags; 1519a1a4e97bSnorby kr->r.ifindex = ifindex; 1520afd0ab5dSfriehm kr->r.priority = prio; 1521a1a4e97bSnorby 1522d4ae837bSremi if (rtm->rtm_priority == kr_state.fib_prio) { 1523d4ae837bSremi log_warnx("alien OSPF route %s/%d", 1524d4ae837bSremi log_in6addr(&prefix), prefixlen); 1525d4ae837bSremi rv = send_rtmsg(kr_state.fd, 1526d4ae837bSremi RTM_DELETE, &kr->r); 1527d4ae837bSremi free(kr); 1528d4ae837bSremi if (rv == -1) 1529d4ae837bSremi return (-1); 1530d4ae837bSremi } else { 1531a1a4e97bSnorby if ((label = (struct sockaddr_rtlabel *) 1532a1a4e97bSnorby rti_info[RTAX_LABEL]) != NULL) { 1533a1a4e97bSnorby kr->r.rtlabel = 1534d4ae837bSremi rtlabel_name2id( 1535d4ae837bSremi label->sr_label); 1536a1a4e97bSnorby kr->r.ext_tag = 1537d4ae837bSremi rtlabel_id2tag( 1538d4ae837bSremi kr->r.rtlabel); 1539a1a4e97bSnorby } 1540a1a4e97bSnorby 1541a1a4e97bSnorby kroute_insert(kr); 1542a1a4e97bSnorby } 1543d4ae837bSremi } 1544a1a4e97bSnorby break; 1545a1a4e97bSnorby case RTM_DELETE: 1546afd0ab5dSfriehm if ((kr = kroute_find(&prefix, prefixlen, prio)) == 1547a1a4e97bSnorby NULL) 1548a1a4e97bSnorby continue; 1549bf9d8b16Sdenis if (!(kr->r.flags & F_KERNEL)) 1550bf9d8b16Sdenis continue; 1551a1a4e97bSnorby /* get the correct route */ 1552a1a4e97bSnorby okr = kr; 1553eecab31bSclaudio if (mpath && (kr = kroute_matchgw(kr, &nexthop, 1554eecab31bSclaudio scope)) == NULL) { 1555bf9d8b16Sdenis log_warnx("rtmsg_process mpath route" 1556a1a4e97bSnorby " not found"); 1557a1a4e97bSnorby return (-1); 1558a1a4e97bSnorby } 1559a1a4e97bSnorby if (kroute_remove(kr) == -1) 1560a1a4e97bSnorby return (-1); 1561a1a4e97bSnorby break; 1562a1a4e97bSnorby case RTM_IFINFO: 1563a1a4e97bSnorby memcpy(&ifm, next, sizeof(ifm)); 1564bf9d8b16Sdenis if_change(ifm.ifm_index, ifm.ifm_flags, &ifm.ifm_data, 1565bf9d8b16Sdenis (struct sockaddr_dl *)rti_info[RTAX_IFP]); 1566a1a4e97bSnorby break; 1567a1a4e97bSnorby case RTM_NEWADDR: 1568a1a4e97bSnorby ifam = (struct ifa_msghdr *)rtm; 1569a1a4e97bSnorby if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | 1570a1a4e97bSnorby RTA_BRD)) == 0) 1571a1a4e97bSnorby break; 1572a1a4e97bSnorby 1573a1a4e97bSnorby if_newaddr(ifam->ifam_index, 1574a1a4e97bSnorby (struct sockaddr_in6 *)rti_info[RTAX_IFA], 1575a1a4e97bSnorby (struct sockaddr_in6 *)rti_info[RTAX_NETMASK], 1576a1a4e97bSnorby (struct sockaddr_in6 *)rti_info[RTAX_BRD]); 1577a1a4e97bSnorby break; 15788e1674f3Sbluhm case RTM_DELADDR: 15798e1674f3Sbluhm ifam = (struct ifa_msghdr *)rtm; 15808e1674f3Sbluhm if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | 15818e1674f3Sbluhm RTA_BRD)) == 0) 15828e1674f3Sbluhm break; 15838e1674f3Sbluhm 15848e1674f3Sbluhm if_deladdr(ifam->ifam_index, 15858e1674f3Sbluhm (struct sockaddr_in6 *)rti_info[RTAX_IFA], 15868e1674f3Sbluhm (struct sockaddr_in6 *)rti_info[RTAX_NETMASK], 15878e1674f3Sbluhm (struct sockaddr_in6 *)rti_info[RTAX_BRD]); 15888e1674f3Sbluhm break; 1589a1a4e97bSnorby case RTM_IFANNOUNCE: 1590a1a4e97bSnorby if_announce(next); 1591a1a4e97bSnorby break; 1592dd3b9a80Ssthen case RTM_DESYNC: 1593dd3b9a80Ssthen /* 1594dd3b9a80Ssthen * We lost some routing packets. Schedule a reload 1595dd3b9a80Ssthen * of the kernel route/interface information. 1596dd3b9a80Ssthen */ 1597dd3b9a80Ssthen if (kr_state.reload_state == KR_RELOAD_IDLE) { 1598dd3b9a80Ssthen delay = KR_RELOAD_TIMER; 1599dd3b9a80Ssthen log_info("desync; scheduling fib reload"); 1600dd3b9a80Ssthen } else { 1601dd3b9a80Ssthen delay = KR_RELOAD_HOLD_TIMER; 1602dd3b9a80Ssthen log_debug("desync during KR_RELOAD_%s", 1603dd3b9a80Ssthen kr_state.reload_state == 1604dd3b9a80Ssthen KR_RELOAD_FETCH ? "FETCH" : "HOLD"); 1605dd3b9a80Ssthen } 1606dd3b9a80Ssthen kr_state.reload_state = KR_RELOAD_FETCH; 1607dd3b9a80Ssthen kr_fib_reload_arm_timer(delay); 1608dd3b9a80Ssthen break; 1609a1a4e97bSnorby default: 1610a1a4e97bSnorby /* ignore for now */ 1611a1a4e97bSnorby break; 1612a1a4e97bSnorby } 1613a1a4e97bSnorby } 1614bf9d8b16Sdenis return (offset); 1615a1a4e97bSnorby } 1616