1/* $OpenBSD: kroute.c,v 1.14 2017/07/24 11:00:01 friehm Exp $ */ 2 3/* 4 * Copyright (c) 2004 Esben Norby <norby@openbsd.org> 5 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <sys/types.h> 21#include <sys/socket.h> 22#include <sys/sysctl.h> 23#include <sys/tree.h> 24#include <netinet/in.h> 25#include <arpa/inet.h> 26#include <net/if.h> 27#include <net/if_dl.h> 28#include <net/if_types.h> 29#include <net/route.h> 30#include <err.h> 31#include <errno.h> 32#include <fcntl.h> 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <unistd.h> 37 38#include "dvmrp.h" 39#include "dvmrpd.h" 40#include "log.h" 41 42struct { 43 u_int32_t rtseq; 44 pid_t pid; 45 struct event ev; 46} kr_state; 47 48struct kif_node { 49 RB_ENTRY(kif_node) entry; 50 struct kif k; 51}; 52 53int kif_compare(struct kif_node *, struct kif_node *); 54 55struct kif_node *kif_find(int); 56int kif_insert(struct kif_node *); 57int kif_remove(struct kif_node *); 58void kif_clear(void); 59 60in_addr_t prefixlen2mask(u_int8_t); 61void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 62void if_change(u_short, int, struct if_data *); 63void if_announce(void *); 64 65int fetchifs(int); 66 67RB_HEAD(kif_tree, kif_node) kit; 68RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare) 69RB_GENERATE(kif_tree, kif_node, entry, kif_compare) 70 71int 72kif_init(void) 73{ 74 RB_INIT(&kit); 75 76 if (fetchifs(0) == -1) 77 return (-1); 78 79 return (0); 80} 81 82int 83kr_init(void) 84{ 85 int opt, fd; 86 87 if ((fd = socket(AF_ROUTE, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 88 0)) == -1) { 89 log_warn("kr_init: socket"); 90 return (-1); 91 } 92 93 /* not interested in my own messages */ 94 if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, &opt, sizeof(opt)) == -1) 95 log_warn("kr_init: setsockopt"); /* not fatal */ 96 97 kr_state.pid = getpid(); 98 kr_state.rtseq = 1; 99 100 event_set(&kr_state.ev, fd, EV_READ | EV_PERSIST, 101 kr_dispatch_msg, NULL); 102 event_add(&kr_state.ev, NULL); 103 104 return (0); 105} 106 107void 108kr_shutdown(void) 109{ 110 kif_clear(); 111 close(EVENT_FD((&kr_state.ev))); 112} 113 114void 115kr_ifinfo(char *ifname) 116{ 117 struct kif_node *kif; 118 119 RB_FOREACH(kif, kif_tree, &kit) 120 if (!strcmp(ifname, kif->k.ifname)) { 121 main_imsg_compose_dvmrpe(IMSG_CTL_IFINFO, 0, 122 &kif->k, sizeof(kif->k)); 123 return; 124 } 125} 126 127/* rb-tree compare */ 128int 129kif_compare(struct kif_node *a, struct kif_node *b) 130{ 131 return (b->k.ifindex - a->k.ifindex); 132} 133 134struct kif_node * 135kif_find(int ifindex) 136{ 137 struct kif_node s; 138 139 memset(&s, 0, sizeof(s)); 140 s.k.ifindex = ifindex; 141 142 return (RB_FIND(kif_tree, &kit, &s)); 143} 144 145struct kif * 146kif_findname(char *ifname) 147{ 148 struct kif_node *kif; 149 150 RB_FOREACH(kif, kif_tree, &kit) 151 if (!strcmp(ifname, kif->k.ifname)) 152 return (&kif->k); 153 154 return (NULL); 155} 156 157int 158kif_insert(struct kif_node *kif) 159{ 160 if (RB_INSERT(kif_tree, &kit, kif) != NULL) { 161 log_warnx("RB_INSERT(kif_tree, &kit, kif)"); 162 free(kif); 163 return (-1); 164 } 165 166 return (0); 167} 168 169int 170kif_remove(struct kif_node *kif) 171{ 172 if (RB_REMOVE(kif_tree, &kit, kif) == NULL) { 173 log_warnx("RB_REMOVE(kif_tree, &kit, kif)"); 174 return (-1); 175 } 176 177 free(kif); 178 return (0); 179} 180 181void 182kif_clear(void) 183{ 184 struct kif_node *kif; 185 186 while ((kif = RB_MIN(kif_tree, &kit)) != NULL) 187 kif_remove(kif); 188} 189 190/* misc */ 191u_int8_t 192prefixlen_classful(in_addr_t ina) 193{ 194 /* it hurt to write this. */ 195 196 if (ina >= 0xf0000000U) /* class E */ 197 return (32); 198 else if (ina >= 0xe0000000U) /* class D */ 199 return (4); 200 else if (ina >= 0xc0000000U) /* class C */ 201 return (24); 202 else if (ina >= 0x80000000U) /* class B */ 203 return (16); 204 else /* class A */ 205 return (8); 206} 207 208u_int8_t 209mask2prefixlen(in_addr_t ina) 210{ 211 if (ina == 0) 212 return (0); 213 else 214 return (33 - ffs(ntohl(ina))); 215} 216 217in_addr_t 218prefixlen2mask(u_int8_t prefixlen) 219{ 220 if (prefixlen == 0) 221 return (0); 222 223 return (0xffffffff << (32 - prefixlen)); 224} 225 226void 227if_change(u_short ifindex, int flags, struct if_data *ifd) 228{ 229 struct kif_node *kif; 230 u_int8_t reachable; 231 232 if ((kif = kif_find(ifindex)) == NULL) { 233 log_warnx("interface with index %u not found", 234 ifindex); 235 return; 236 } 237 238 kif->k.flags = flags; 239 kif->k.link_state = ifd->ifi_link_state; 240 kif->k.if_type = ifd->ifi_type; 241 kif->k.baudrate = ifd->ifi_baudrate; 242 243 if ((reachable = (flags & IFF_UP) && 244 LINK_STATE_IS_UP(ifd->ifi_link_state)) == kif->k.nh_reachable) 245 return; /* nothing changed wrt nexthop validity */ 246 247 kif->k.nh_reachable = reachable; 248 main_imsg_compose_dvmrpe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k)); 249} 250 251void 252if_announce(void *msg) 253{ 254 struct if_announcemsghdr *ifan; 255 struct kif_node *kif; 256 257 ifan = msg; 258 259 switch (ifan->ifan_what) { 260 case IFAN_ARRIVAL: 261 if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) { 262 log_warn("if_announce"); 263 return; 264 } 265 266 kif->k.ifindex = ifan->ifan_index; 267 strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname)); 268 kif_insert(kif); 269 break; 270 case IFAN_DEPARTURE: 271 kif = kif_find(ifan->ifan_index); 272 kif_remove(kif); 273 break; 274 } 275} 276 277/* rtsock */ 278#define ROUNDUP(a) \ 279 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 280 281void 282get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 283{ 284 int i; 285 286 for (i = 0; i < RTAX_MAX; i++) { 287 if (addrs & (1 << i)) { 288 rti_info[i] = sa; 289 sa = (struct sockaddr *)((char *)(sa) + 290 ROUNDUP(sa->sa_len)); 291 } else 292 rti_info[i] = NULL; 293 } 294} 295 296int 297fetchifs(int ifindex) 298{ 299 size_t len; 300 int mib[6]; 301 char *buf, *next, *lim; 302 struct if_msghdr ifm; 303 struct kif_node *kif; 304 struct sockaddr *sa, *rti_info[RTAX_MAX]; 305 struct sockaddr_dl *sdl; 306 307 mib[0] = CTL_NET; 308 mib[1] = PF_ROUTE; 309 mib[2] = 0; 310 mib[3] = AF_INET; 311 mib[4] = NET_RT_IFLIST; 312 mib[5] = ifindex; 313 314 if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { 315 log_warn("sysctl"); 316 return (-1); 317 } 318 if ((buf = malloc(len)) == NULL) { 319 log_warn("fetchif"); 320 return (-1); 321 } 322 if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) { 323 log_warn("sysctl"); 324 free(buf); 325 return (-1); 326 } 327 328 lim = buf + len; 329 for (next = buf; next < lim; next += ifm.ifm_msglen) { 330 memcpy(&ifm, next, sizeof(ifm)); 331 sa = (struct sockaddr *)(next + sizeof(ifm)); 332 get_rtaddrs(ifm.ifm_addrs, sa, rti_info); 333 334 if (ifm.ifm_version != RTM_VERSION) 335 continue; 336 if (ifm.ifm_type != RTM_IFINFO) 337 continue; 338 339 if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) { 340 log_warn("fetchifs"); 341 free(buf); 342 return (-1); 343 } 344 345 kif->k.ifindex = ifm.ifm_index; 346 kif->k.flags = ifm.ifm_flags; 347 kif->k.link_state = ifm.ifm_data.ifi_link_state; 348 kif->k.if_type = ifm.ifm_data.ifi_type; 349 kif->k.baudrate = ifm.ifm_data.ifi_baudrate; 350 kif->k.mtu = ifm.ifm_data.ifi_mtu; 351 kif->k.nh_reachable = (kif->k.flags & IFF_UP) && 352 LINK_STATE_IS_UP(ifm.ifm_data.ifi_link_state); 353 if ((sa = rti_info[RTAX_IFP]) != NULL) 354 if (sa->sa_family == AF_LINK) { 355 sdl = (struct sockaddr_dl *)sa; 356 if (sdl->sdl_nlen >= sizeof(kif->k.ifname)) 357 memcpy(kif->k.ifname, sdl->sdl_data, 358 sizeof(kif->k.ifname) - 1); 359 else if (sdl->sdl_nlen > 0) 360 memcpy(kif->k.ifname, sdl->sdl_data, 361 sdl->sdl_nlen); 362 /* string already terminated via calloc() */ 363 } 364 365 kif_insert(kif); 366 } 367 free(buf); 368 return (0); 369} 370 371void 372kr_dispatch_msg(int fd, short event, void *bula) 373{ 374 char buf[RT_BUF_SIZE]; 375 ssize_t n; 376 char *next, *lim; 377 struct rt_msghdr *rtm; 378 struct if_msghdr ifm; 379 380 if ((n = read(fd, &buf, sizeof(buf))) == -1) { 381 if (errno == EAGAIN || errno == EINTR) 382 return; 383 fatal("dispatch_rtmsg: read error"); 384 } 385 386 if (n == 0) 387 fatalx("routing socket closed"); 388 389 lim = buf + n; 390 for (next = buf; next < lim; next += rtm->rtm_msglen) { 391 rtm = (struct rt_msghdr *)next; 392 if (lim < next + sizeof(u_short) || 393 lim < next + rtm->rtm_msglen) 394 fatalx("dispatch_rtmsg: partial rtm in buffer"); 395 if (rtm->rtm_version != RTM_VERSION) 396 continue; 397 398 switch (rtm->rtm_type) { 399 case RTM_IFINFO: 400 memcpy(&ifm, next, sizeof(ifm)); 401 if_change(ifm.ifm_index, ifm.ifm_flags, 402 &ifm.ifm_data); 403 break; 404 case RTM_IFANNOUNCE: 405 if_announce(next); 406 break; 407 default: 408 /* ignore for now */ 409 break; 410 } 411 } 412} 413