interface.c revision 1.4
1/* $OpenBSD: interface.c,v 1.4 2010/04/15 15:39:32 claudio Exp $ */ 2 3/* 4 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 5 * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@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/ioctl.h> 22#include <sys/time.h> 23#include <sys/socket.h> 24#include <netinet/in.h> 25#include <arpa/inet.h> 26#include <net/if.h> 27#include <net/if_types.h> 28#include <fcntl.h> 29#include <ctype.h> 30#include <err.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <unistd.h> 34#include <string.h> 35#include <event.h> 36 37#include "ldpd.h" 38#include "ldp.h" 39#include "log.h" 40#include "ldpe.h" 41 42void if_hello_timer(int, short, void *); 43void if_start_hello_timer(struct iface *); 44void if_stop_hello_timer(struct iface *); 45struct nbr *if_elect(struct nbr *, struct nbr *); 46 47struct { 48 int state; 49 enum iface_event event; 50 enum iface_action action; 51 int new_state; 52} iface_fsm[] = { 53 /* current state event that happened action to take resulting state */ 54 {IF_STA_DOWN, IF_EVT_UP, IF_ACT_STRT, IF_STA_ACTIVE}, 55 {IF_STA_LOOPBACK, IF_EVT_DOWN, IF_ACT_NOTHING, IF_STA_DOWN}, 56 {IF_STA_ANY, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN}, 57 {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0}, 58}; 59 60static int vlink_cnt = 0; 61 62const char * const if_event_names[] = { 63 "NOTHING", 64 "UP", 65 "DOWN" 66}; 67 68const char * const if_action_names[] = { 69 "NOTHING", 70 "START", 71 "RESET" 72}; 73 74int 75if_fsm(struct iface *iface, enum iface_event event) 76{ 77 int old_state; 78 int new_state = 0; 79 int i, ret = 0; 80 81 old_state = iface->state; 82 83 for (i = 0; iface_fsm[i].state != -1; i++) 84 if ((iface_fsm[i].state & old_state) && 85 (iface_fsm[i].event == event)) { 86 new_state = iface_fsm[i].new_state; 87 break; 88 } 89 90 if (iface_fsm[i].state == -1) { 91 /* event outside of the defined fsm, ignore it. */ 92 log_debug("if_fsm: interface %s, " 93 "event %s not expected in state %s", iface->name, 94 if_event_names[event], if_state_name(old_state)); 95 return (0); 96 } 97 98 switch (iface_fsm[i].action) { 99 case IF_ACT_STRT: 100 ret = if_act_start(iface); 101 break; 102 case IF_ACT_RST: 103 ret = if_act_reset(iface); 104 break; 105 case IF_ACT_NOTHING: 106 /* do nothing */ 107 break; 108 } 109 110 if (ret) { 111 log_debug("if_fsm: error changing state for interface %s, " 112 "event %s, state %s", iface->name, if_event_names[event], 113 if_state_name(old_state)); 114 return (-1); 115 } 116 117 if (new_state != 0) 118 iface->state = new_state; 119 120 log_debug("if_fsm: event %s resulted in action %s and changing " 121 "state for interface %s from %s to %s", 122 if_event_names[event], if_action_names[iface_fsm[i].action], 123 iface->name, if_state_name(old_state), if_state_name(iface->state)); 124 125 return (ret); 126} 127 128struct iface * 129if_new(struct kif *kif, struct kif_addr *ka) 130{ 131 struct iface *iface; 132 133 if ((iface = calloc(1, sizeof(*iface))) == NULL) 134 err(1, "if_new: calloc"); 135 136 iface->state = IF_STA_DOWN; 137 138 LIST_INIT(&iface->nbr_list); 139 LIST_INIT(&iface->lde_nbr_list); 140 141 if (kif == NULL) { 142 iface->type = IF_TYPE_VIRTUALLINK; 143 snprintf(iface->name, sizeof(iface->name), "vlink%d", 144 vlink_cnt++); 145 iface->flags |= IFF_UP; 146 iface->mtu = IP_MSS; 147 return (iface); 148 } 149 150 strlcpy(iface->name, kif->ifname, sizeof(iface->name)); 151 152 /* get type */ 153 if (kif->flags & IFF_POINTOPOINT) 154 iface->type = IF_TYPE_POINTOPOINT; 155 if (kif->flags & IFF_BROADCAST && 156 kif->flags & IFF_MULTICAST) 157 iface->type = IF_TYPE_BROADCAST; 158 if (kif->flags & IFF_LOOPBACK) { 159 iface->type = IF_TYPE_POINTOPOINT; 160 iface->state = IF_STA_LOOPBACK; 161 } 162 163 /* get mtu, index and flags */ 164 iface->mtu = kif->mtu; 165 iface->ifindex = kif->ifindex; 166 iface->flags = kif->flags; 167 iface->linkstate = kif->link_state; 168 iface->media_type = kif->media_type; 169 iface->baudrate = kif->baudrate; 170 171 /* set address, mask and p2p addr */ 172 iface->addr = ka->addr; 173 iface->mask = ka->mask; 174 if (kif->flags & IFF_POINTOPOINT) { 175 iface->dst = ka->dstbrd; 176 } 177 178 return (iface); 179} 180 181void 182if_del(struct iface *iface) 183{ 184 struct nbr *nbr = NULL; 185 186 log_debug("if_del: interface %s", iface->name); 187 188 /* clear lists etc */ 189 while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) 190 nbr_del(nbr); 191 192 if (evtimer_pending(&iface->hello_timer, NULL)) 193 evtimer_del(&iface->hello_timer); 194 195 free(iface); 196} 197 198void 199if_init(struct ldpd_conf *xconf, struct iface *iface) 200{ 201 /* init the dummy local neighbor */ 202 iface->self = nbr_new(ldpe_router_id(), iface->ifindex, iface, 1); 203 204 /* set event handlers for interface */ 205 evtimer_set(&iface->hello_timer, if_hello_timer, iface); 206 207 iface->discovery_fd = xconf->ldp_discovery_socket; 208} 209 210/* timers */ 211/* ARGSUSED */ 212void 213if_hello_timer(int fd, short event, void *arg) 214{ 215 struct iface *iface = arg; 216 struct timeval tv; 217 218 send_hello(iface); 219 220 /* reschedule hello_timer */ 221 timerclear(&tv); 222 tv.tv_sec = iface->hello_interval; 223 if (evtimer_add(&iface->hello_timer, &tv) == -1) 224 fatal("if_hello_timer"); 225} 226 227void 228if_start_hello_timer(struct iface *iface) 229{ 230 struct timeval tv; 231 232 timerclear(&tv); 233 tv.tv_sec = iface->hello_interval; 234 if (evtimer_add(&iface->hello_timer, &tv) == -1) 235 fatal("if_start_hello_timer"); 236} 237 238void 239if_stop_hello_timer(struct iface *iface) 240{ 241 if (evtimer_del(&iface->hello_timer) == -1) 242 fatal("if_stop_hello_timer"); 243} 244 245/* actions */ 246int 247if_act_start(struct iface *iface) 248{ 249 struct in_addr addr; 250 struct timeval now; 251 252 if (!((iface->flags & IFF_UP) && 253 (LINK_STATE_IS_UP(iface->linkstate) || 254 (iface->linkstate == LINK_STATE_UNKNOWN && 255 iface->media_type != IFT_CARP)))) { 256 log_debug("if_act_start: interface %s link down", 257 iface->name); 258 return (0); 259 } 260 261 if (iface->media_type == IFT_CARP && iface->passive == 0) { 262 /* force passive mode on carp interfaces */ 263 log_warnx("if_act_start: forcing interface %s to passive", 264 iface->name); 265 iface->passive = 1; 266 } 267 268 gettimeofday(&now, NULL); 269 iface->uptime = now.tv_sec; 270 271 switch (iface->type) { 272 case IF_TYPE_VIRTUALLINK: 273 case IF_TYPE_POINTOMULTIPOINT: 274 case IF_TYPE_NBMA: 275 log_debug("if_act_start: type %s not supported, interface %s", 276 if_type_name(iface->type), iface->name); 277 return (-1); 278 case IF_TYPE_POINTOPOINT: 279 case IF_TYPE_BROADCAST: 280 inet_aton(AllRouters, &addr); 281 if (if_join_group(iface, &addr)) 282 return (-1); 283 iface->state = IF_STA_DOWN; 284 break; 285 default: 286 fatalx("if_act_start: unknown interface type"); 287 } 288 289 /* hello timer needs to be started in any case */ 290 if_start_hello_timer(iface); 291 return (0); 292} 293 294int 295if_act_reset(struct iface *iface) 296{ 297/* struct nbr *nbr = NULL; */ 298 struct in_addr addr; 299 300 switch (iface->type) { 301 case IF_TYPE_POINTOPOINT: 302 case IF_TYPE_BROADCAST: 303 inet_aton(AllRouters, &addr); 304 if (if_leave_group(iface, &addr)) { 305 log_warnx("if_act_reset: error leaving group %s, " 306 "interface %s", inet_ntoa(addr), iface->name); 307 } 308 break; 309 case IF_TYPE_VIRTUALLINK: 310 /* nothing */ 311 break; 312 case IF_TYPE_NBMA: 313 case IF_TYPE_POINTOMULTIPOINT: 314 log_debug("if_act_reset: type %s not supported, interface %s", 315 if_type_name(iface->type), iface->name); 316 return (-1); 317 default: 318 fatalx("if_act_reset: unknown interface type"); 319 } 320/* 321 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 322 if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) { 323 log_debug("if_act_reset: error killing neighbor %s", 324 inet_ntoa(nbr->id)); 325 } 326 } 327*/ 328 return (0); 329} 330 331struct ctl_iface * 332if_to_ctl(struct iface *iface) 333{ 334 static struct ctl_iface ictl; 335 struct timeval tv, now, res; 336 struct nbr *nbr; 337 338 memcpy(ictl.name, iface->name, sizeof(ictl.name)); 339 memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr)); 340 memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask)); 341 ictl.rtr_id.s_addr = ldpe_router_id(); 342 ictl.ifindex = iface->ifindex; 343 ictl.state = iface->state; 344 ictl.mtu = iface->mtu; 345 ictl.nbr_cnt = 0; 346 ictl.adj_cnt = 0; 347 ictl.baudrate = iface->baudrate; 348 ictl.holdtime = iface->holdtime; 349 ictl.hello_interval = iface->hello_interval; 350 ictl.flags = iface->flags; 351 ictl.type = iface->type; 352 ictl.linkstate = iface->linkstate; 353 ictl.mediatype = iface->media_type; 354 ictl.priority = iface->priority; 355 ictl.passive = iface->passive; 356 357 gettimeofday(&now, NULL); 358 if (evtimer_pending(&iface->hello_timer, &tv)) { 359 timersub(&tv, &now, &res); 360 ictl.hello_timer = res.tv_sec; 361 } else 362 ictl.hello_timer = -1; 363 364 if (iface->state != IF_STA_DOWN) { 365 ictl.uptime = now.tv_sec - iface->uptime; 366 } else 367 ictl.uptime = 0; 368 369 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 370 if (nbr == iface->self) 371 continue; 372 ictl.nbr_cnt++; 373 } 374 375 return (&ictl); 376} 377 378/* misc */ 379int 380if_set_mcast_ttl(int fd, u_int8_t ttl) 381{ 382 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, 383 (char *)&ttl, sizeof(ttl)) < 0) { 384 log_warn("if_set_mcast_ttl: error setting " 385 "IP_MULTICAST_TTL to %d", ttl); 386 return (-1); 387 } 388 389 return (0); 390} 391 392int 393if_set_tos(int fd, int tos) 394{ 395 if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) { 396 log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos); 397 return (-1); 398 } 399 400 return (0); 401} 402 403int 404if_set_recvif(int fd, int enable) 405{ 406 if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable, 407 sizeof(enable)) < 0) { 408 log_warn("if_set_recvif: error setting IP_RECVIF"); 409 return (-1); 410 } 411 return (0); 412} 413 414void 415if_set_recvbuf(int fd) 416{ 417 int bsize; 418 419 bsize = 65535; 420 while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, 421 sizeof(bsize)) == -1) 422 bsize /= 2; 423} 424 425int 426if_set_reuse(int fd, int enable) 427{ 428 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, 429 sizeof(int)) < 0) { 430 log_warn("if_set_reuse: error setting SO_REUSEADDR"); 431 return (-1); 432 } 433 434 return (0); 435} 436 437/* 438 * only one JOIN or DROP per interface and address is allowed so we need 439 * to keep track of what is added and removed. 440 */ 441struct if_group_count { 442 LIST_ENTRY(if_group_count) entry; 443 struct in_addr addr; 444 unsigned int ifindex; 445 int count; 446}; 447 448LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist); 449 450int 451if_join_group(struct iface *iface, struct in_addr *addr) 452{ 453 struct ip_mreq mreq; 454 struct if_group_count *ifg; 455 456 switch (iface->type) { 457 case IF_TYPE_POINTOPOINT: 458 case IF_TYPE_BROADCAST: 459 LIST_FOREACH(ifg, &ifglist, entry) 460 if (iface->ifindex == ifg->ifindex && 461 addr->s_addr == ifg->addr.s_addr) 462 break; 463 if (ifg == NULL) { 464 if ((ifg = calloc(1, sizeof(*ifg))) == NULL) 465 fatal("if_join_group"); 466 ifg->addr.s_addr = addr->s_addr; 467 ifg->ifindex = iface->ifindex; 468 LIST_INSERT_HEAD(&ifglist, ifg, entry); 469 } 470 471 if (ifg->count++ != 0) 472 /* already joined */ 473 return (0); 474 475 mreq.imr_multiaddr.s_addr = addr->s_addr; 476 mreq.imr_interface.s_addr = iface->addr.s_addr; 477 478 if (setsockopt(iface->discovery_fd, IPPROTO_IP, 479 IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { 480 log_warn("if_join_group: error IP_ADD_MEMBERSHIP, " 481 "interface %s address %s", iface->name, 482 inet_ntoa(*addr)); 483 return (-1); 484 } 485 break; 486 case IF_TYPE_POINTOMULTIPOINT: 487 case IF_TYPE_VIRTUALLINK: 488 case IF_TYPE_NBMA: 489 log_debug("if_join_group: type %s not supported, interface %s", 490 if_type_name(iface->type), iface->name); 491 return (-1); 492 default: 493 fatalx("if_join_group: unknown interface type"); 494 } 495 496 return (0); 497} 498 499int 500if_leave_group(struct iface *iface, struct in_addr *addr) 501{ 502 struct ip_mreq mreq; 503 struct if_group_count *ifg; 504 505 switch (iface->type) { 506 case IF_TYPE_POINTOPOINT: 507 case IF_TYPE_BROADCAST: 508 LIST_FOREACH(ifg, &ifglist, entry) 509 if (iface->ifindex == ifg->ifindex && 510 addr->s_addr == ifg->addr.s_addr) 511 break; 512 513 /* if interface is not found just try to drop membership */ 514 if (ifg && --ifg->count != 0) 515 /* others still joined */ 516 return (0); 517 518 mreq.imr_multiaddr.s_addr = addr->s_addr; 519 mreq.imr_interface.s_addr = iface->addr.s_addr; 520 521 if (setsockopt(iface->discovery_fd, IPPROTO_IP, 522 IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { 523 log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, " 524 "interface %s address %s", iface->name, 525 inet_ntoa(*addr)); 526 return (-1); 527 } 528 529 if (ifg) { 530 LIST_REMOVE(ifg, entry); 531 free(ifg); 532 } 533 break; 534 case IF_TYPE_POINTOMULTIPOINT: 535 case IF_TYPE_VIRTUALLINK: 536 case IF_TYPE_NBMA: 537 log_debug("if_leave_group: type %s not supported, interface %s", 538 if_type_name(iface->type), iface->name); 539 return (-1); 540 default: 541 fatalx("if_leave_group: unknown interface type"); 542 } 543 544 return (0); 545} 546 547int 548if_set_mcast(struct iface *iface) 549{ 550 switch (iface->type) { 551 case IF_TYPE_POINTOPOINT: 552 case IF_TYPE_BROADCAST: 553 if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF, 554 &iface->addr.s_addr, sizeof(iface->addr.s_addr)) < 0) { 555 log_debug("if_set_mcast: error setting " 556 "IP_MULTICAST_IF, interface %s", iface->name); 557 return (-1); 558 } 559 break; 560 case IF_TYPE_POINTOMULTIPOINT: 561 case IF_TYPE_VIRTUALLINK: 562 case IF_TYPE_NBMA: 563 log_debug("if_set_mcast: type %s not supported, interface %s", 564 if_type_name(iface->type), iface->name); 565 return (-1); 566 default: 567 fatalx("if_set_mcast: unknown interface type"); 568 } 569 570 return (0); 571} 572 573int 574if_set_mcast_loop(int fd) 575{ 576 u_int8_t loop = 0; 577 578 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 579 (char *)&loop, sizeof(loop)) < 0) { 580 log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP"); 581 return (-1); 582 } 583 584 return (0); 585} 586