1 /* $OpenBSD: kroute.c,v 1.6 2009/09/22 16:43:42 michele 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/param.h> 21 #include <sys/types.h> 22 #include <sys/socket.h> 23 #include <sys/sysctl.h> 24 #include <sys/tree.h> 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 #include <net/if.h> 28 #include <net/if_dl.h> 29 #include <net/if_types.h> 30 #include <net/route.h> 31 #include <err.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "dvmrp.h" 40 #include "dvmrpd.h" 41 #include "log.h" 42 43 struct { 44 u_int32_t rtseq; 45 pid_t pid; 46 struct event ev; 47 } kr_state; 48 49 struct kif_node { 50 RB_ENTRY(kif_node) entry; 51 struct kif k; 52 }; 53 54 int kif_compare(struct kif_node *, struct kif_node *); 55 56 struct kif_node *kif_find(int); 57 int kif_insert(struct kif_node *); 58 int kif_remove(struct kif_node *); 59 void kif_clear(void); 60 61 in_addr_t prefixlen2mask(u_int8_t); 62 void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 63 void if_change(u_short, int, struct if_data *); 64 void if_announce(void *); 65 66 int fetchifs(int); 67 68 RB_HEAD(kif_tree, kif_node) kit; 69 RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare) 70 RB_GENERATE(kif_tree, kif_node, entry, kif_compare) 71 72 int 73 kif_init(void) 74 { 75 RB_INIT(&kit); 76 77 if (fetchifs(0) == -1) 78 return (-1); 79 80 return (0); 81 } 82 83 int 84 kr_init(void) 85 { 86 int opt, fd; 87 88 if ((fd = socket(AF_ROUTE, SOCK_RAW, 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 107 void 108 kr_shutdown(void) 109 { 110 kif_clear(); 111 close(EVENT_FD((&kr_state.ev))); 112 } 113 114 void 115 kr_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 */ 128 int 129 kif_compare(struct kif_node *a, struct kif_node *b) 130 { 131 return (b->k.ifindex - a->k.ifindex); 132 } 133 134 struct kif_node * 135 kif_find(int ifindex) 136 { 137 struct kif_node s; 138 139 bzero(&s, sizeof(s)); 140 s.k.ifindex = ifindex; 141 142 return (RB_FIND(kif_tree, &kit, &s)); 143 } 144 145 struct kif * 146 kif_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 157 int 158 kif_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 169 int 170 kif_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 181 void 182 kif_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 */ 191 u_int8_t 192 prefixlen_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 208 u_int8_t 209 mask2prefixlen(in_addr_t ina) 210 { 211 if (ina == 0) 212 return (0); 213 else 214 return (33 - ffs(ntohl(ina))); 215 } 216 217 in_addr_t 218 prefixlen2mask(u_int8_t prefixlen) 219 { 220 if (prefixlen == 0) 221 return (0); 222 223 return (0xffffffff << (32 - prefixlen)); 224 } 225 226 void 227 if_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.media_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) || 245 (ifd->ifi_link_state == LINK_STATE_UNKNOWN && 246 ifd->ifi_type != IFT_CARP))) == kif->k.nh_reachable) 247 return; /* nothing changed wrt nexthop validity */ 248 249 kif->k.nh_reachable = reachable; 250 main_imsg_compose_dvmrpe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k)); 251 } 252 253 void 254 if_announce(void *msg) 255 { 256 struct if_announcemsghdr *ifan; 257 struct kif_node *kif; 258 259 ifan = msg; 260 261 switch (ifan->ifan_what) { 262 case IFAN_ARRIVAL: 263 if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) { 264 log_warn("if_announce"); 265 return; 266 } 267 268 kif->k.ifindex = ifan->ifan_index; 269 strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname)); 270 kif_insert(kif); 271 break; 272 case IFAN_DEPARTURE: 273 kif = kif_find(ifan->ifan_index); 274 kif_remove(kif); 275 break; 276 } 277 } 278 279 /* rtsock */ 280 #define ROUNDUP(a, size) \ 281 (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a)) 282 283 void 284 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 285 { 286 int i; 287 288 for (i = 0; i < RTAX_MAX; i++) { 289 if (addrs & (1 << i)) { 290 rti_info[i] = sa; 291 sa = (struct sockaddr *)((char *)(sa) + 292 ROUNDUP(sa->sa_len, sizeof(long))); 293 } else 294 rti_info[i] = NULL; 295 } 296 } 297 298 int 299 fetchifs(int ifindex) 300 { 301 size_t len; 302 int mib[6]; 303 char *buf, *next, *lim; 304 struct if_msghdr ifm; 305 struct kif_node *kif; 306 struct sockaddr *sa, *rti_info[RTAX_MAX]; 307 struct sockaddr_dl *sdl; 308 309 mib[0] = CTL_NET; 310 mib[1] = AF_ROUTE; 311 mib[2] = 0; 312 mib[3] = AF_INET; 313 mib[4] = NET_RT_IFLIST; 314 mib[5] = ifindex; 315 316 if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { 317 log_warn("sysctl"); 318 return (-1); 319 } 320 if ((buf = malloc(len)) == NULL) { 321 log_warn("fetchif"); 322 return (-1); 323 } 324 if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) { 325 log_warn("sysctl"); 326 free(buf); 327 return (-1); 328 } 329 330 lim = buf + len; 331 for (next = buf; next < lim; next += ifm.ifm_msglen) { 332 memcpy(&ifm, next, sizeof(ifm)); 333 sa = (struct sockaddr *)(next + sizeof(ifm)); 334 get_rtaddrs(ifm.ifm_addrs, sa, rti_info); 335 336 if (ifm.ifm_version != RTM_VERSION) 337 continue; 338 if (ifm.ifm_type != RTM_IFINFO) 339 continue; 340 341 if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) { 342 log_warn("fetchifs"); 343 free(buf); 344 return (-1); 345 } 346 347 kif->k.ifindex = ifm.ifm_index; 348 kif->k.flags = ifm.ifm_flags; 349 kif->k.link_state = ifm.ifm_data.ifi_link_state; 350 kif->k.media_type = ifm.ifm_data.ifi_type; 351 kif->k.baudrate = ifm.ifm_data.ifi_baudrate; 352 kif->k.mtu = ifm.ifm_data.ifi_mtu; 353 kif->k.nh_reachable = (kif->k.flags & IFF_UP) && 354 (LINK_STATE_IS_UP(ifm.ifm_data.ifi_link_state) || 355 (ifm.ifm_data.ifi_link_state == LINK_STATE_UNKNOWN && 356 ifm.ifm_data.ifi_type != IFT_CARP)); 357 if ((sa = rti_info[RTAX_IFP]) != NULL) 358 if (sa->sa_family == AF_LINK) { 359 sdl = (struct sockaddr_dl *)sa; 360 if (sdl->sdl_nlen >= sizeof(kif->k.ifname)) 361 memcpy(kif->k.ifname, sdl->sdl_data, 362 sizeof(kif->k.ifname) - 1); 363 else if (sdl->sdl_nlen > 0) 364 memcpy(kif->k.ifname, sdl->sdl_data, 365 sdl->sdl_nlen); 366 /* string already terminated via calloc() */ 367 } 368 369 kif_insert(kif); 370 } 371 free(buf); 372 return (0); 373 } 374 375 void 376 kr_dispatch_msg(int fd, short event, void *bula) 377 { 378 char buf[RT_BUF_SIZE]; 379 ssize_t n; 380 char *next, *lim; 381 struct rt_msghdr *rtm; 382 struct if_msghdr ifm; 383 384 if ((n = read(fd, &buf, sizeof(buf))) == -1) 385 fatal("dispatch_rtmsg: read error"); 386 387 if (n == 0) 388 fatalx("routing socket closed"); 389 390 lim = buf + n; 391 for (next = buf; next < lim; next += rtm->rtm_msglen) { 392 rtm = (struct rt_msghdr *)next; 393 if (rtm->rtm_version != RTM_VERSION) 394 continue; 395 396 switch (rtm->rtm_type) { 397 case RTM_IFINFO: 398 memcpy(&ifm, next, sizeof(ifm)); 399 if_change(ifm.ifm_index, ifm.ifm_flags, 400 &ifm.ifm_data); 401 break; 402 case RTM_IFANNOUNCE: 403 if_announce(next); 404 break; 405 default: 406 /* ignore for now */ 407 break; 408 } 409 } 410 } 411