interface.c revision 1.14
1/* $OpenBSD: interface.c,v 1.14 2013/06/03 16:53:49 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 42extern struct ldpd_conf *leconf; 43 44int if_act_start(struct iface *); 45int if_act_reset(struct iface *); 46int if_act_update(struct iface *); 47void if_hello_timer(int, short, void *); 48void if_start_hello_timer(struct iface *); 49void if_stop_hello_timer(struct iface *); 50struct nbr *if_elect(struct nbr *, struct nbr *); 51 52struct { 53 int state; 54 enum iface_event event; 55 enum iface_action action; 56 int new_state; 57} iface_fsm[] = { 58 /* current state event that happened action to take resulting state */ 59 {IF_STA_DOWN, IF_EVT_DOWN, IF_ACT_NOTHING, 0}, 60 {IF_STA_DOWN, IF_EVT_UP, IF_ACT_UPDATE, 0}, 61 {IF_STA_DOWN, IF_EVT_NEWADDR, IF_ACT_UPDATE, 0}, 62 {IF_STA_DOWN, IF_EVT_DELADDR, IF_ACT_NOTHING, 0}, 63 {IF_STA_ACTIVE, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN}, 64 {IF_STA_ACTIVE, IF_EVT_NEWADDR, IF_ACT_NOTHING, 0}, 65 {IF_STA_ACTIVE, IF_EVT_DELADDR, IF_ACT_UPDATE, 0}, 66 {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0}, 67}; 68 69const char * const if_event_names[] = { 70 "NOTHING", 71 "UP", 72 "DOWN", 73 "NEWADDR", 74 "DELADDR" 75}; 76 77const char * const if_action_names[] = { 78 "NOTHING", 79 "UPDATE", 80 "RESET" 81}; 82 83int 84if_fsm(struct iface *iface, enum iface_event event) 85{ 86 int old_state; 87 int new_state = 0; 88 int i, ret = 0; 89 90 old_state = iface->state; 91 92 for (i = 0; iface_fsm[i].state != -1; i++) 93 if ((iface_fsm[i].state & old_state) && 94 (iface_fsm[i].event == event)) { 95 new_state = iface_fsm[i].new_state; 96 break; 97 } 98 99 if (iface_fsm[i].state == -1) { 100 /* event outside of the defined fsm, ignore it. */ 101 log_debug("if_fsm: interface %s, " 102 "event %s not expected in state %s", iface->name, 103 if_event_names[event], if_state_name(old_state)); 104 return (0); 105 } 106 107 switch (iface_fsm[i].action) { 108 case IF_ACT_UPDATE: 109 ret = if_act_update(iface); 110 break; 111 case IF_ACT_RST: 112 ret = if_act_reset(iface); 113 break; 114 case IF_ACT_NOTHING: 115 /* do nothing */ 116 break; 117 } 118 119 if (ret) { 120 log_debug("if_fsm: error changing state for interface %s, " 121 "event %s, state %s", iface->name, if_event_names[event], 122 if_state_name(old_state)); 123 return (-1); 124 } 125 126 if (new_state != 0) 127 iface->state = new_state; 128 129 log_debug("if_fsm: event %s resulted in action %s and changing " 130 "state for interface %s from %s to %s", 131 if_event_names[event], if_action_names[iface_fsm[i].action], 132 iface->name, if_state_name(old_state), if_state_name(iface->state)); 133 134 return (ret); 135} 136 137struct iface * 138if_new(struct kif *kif) 139{ 140 struct iface *iface; 141 142 if ((iface = calloc(1, sizeof(*iface))) == NULL) 143 err(1, "if_new: calloc"); 144 145 iface->state = IF_STA_DOWN; 146 147 LIST_INIT(&iface->lde_nbr_list); 148 149 strlcpy(iface->name, kif->ifname, sizeof(iface->name)); 150 151 /* get type */ 152 if (kif->flags & IFF_POINTOPOINT) 153 iface->type = IF_TYPE_POINTOPOINT; 154 if (kif->flags & IFF_BROADCAST && 155 kif->flags & IFF_MULTICAST) 156 iface->type = IF_TYPE_BROADCAST; 157 158 /* get mtu, index and flags */ 159 iface->mtu = kif->mtu; 160 iface->ifindex = kif->ifindex; 161 iface->flags = kif->flags; 162 iface->linkstate = kif->link_state; 163 iface->media_type = kif->media_type; 164 iface->baudrate = kif->baudrate; 165 166 return (iface); 167} 168 169void 170if_del(struct iface *iface) 171{ 172 struct if_addr *if_addr; 173 174 log_debug("if_del: interface %s", iface->name); 175 176 if_stop_hello_timer(iface); 177 178 while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL) 179 LIST_REMOVE(if_addr, iface_entry); 180 181 free(iface); 182} 183 184void 185if_init(struct ldpd_conf *xconf, struct iface *iface) 186{ 187 /* set event handlers for interface */ 188 evtimer_set(&iface->hello_timer, if_hello_timer, iface); 189 190 iface->discovery_fd = xconf->ldp_discovery_socket; 191} 192 193struct iface * 194if_lookup(u_short ifindex) 195{ 196 struct iface *iface; 197 198 LIST_FOREACH(iface, &leconf->iface_list, entry) 199 if (iface->ifindex == ifindex) 200 return (iface); 201 202 return (NULL); 203} 204 205/* timers */ 206/* ARGSUSED */ 207void 208if_hello_timer(int fd, short event, void *arg) 209{ 210 struct iface *iface = arg; 211 struct timeval tv; 212 213 send_hello(iface); 214 215 /* reschedule hello_timer */ 216 timerclear(&tv); 217 tv.tv_sec = iface->hello_interval; 218 if (evtimer_add(&iface->hello_timer, &tv) == -1) 219 fatal("if_hello_timer"); 220} 221 222void 223if_start_hello_timer(struct iface *iface) 224{ 225 struct timeval tv; 226 227 timerclear(&tv); 228 tv.tv_sec = iface->hello_interval; 229 if (evtimer_add(&iface->hello_timer, &tv) == -1) 230 fatal("if_start_hello_timer"); 231} 232 233void 234if_stop_hello_timer(struct iface *iface) 235{ 236 if (evtimer_pending(&iface->hello_timer, NULL) && 237 evtimer_del(&iface->hello_timer) == -1) 238 fatal("if_stop_hello_timer"); 239} 240 241/* actions */ 242int 243if_act_start(struct iface *iface) 244{ 245 struct in_addr addr; 246 struct timeval now; 247 248 if (!((iface->flags & IFF_UP) && 249 LINK_STATE_IS_UP(iface->linkstate))) { 250 log_debug("if_act_start: interface %s link down", 251 iface->name); 252 return (0); 253 } 254 255 gettimeofday(&now, NULL); 256 iface->uptime = now.tv_sec; 257 258 inet_aton(AllRouters, &addr); 259 if (if_join_group(iface, &addr)) 260 return (-1); 261 iface->state = IF_STA_ACTIVE; 262 263 /* hello timer needs to be started in any case */ 264 if_start_hello_timer(iface); 265 return (0); 266} 267 268int 269if_act_reset(struct iface *iface) 270{ 271 struct in_addr addr; 272 273 if_stop_hello_timer(iface); 274 275 /* try to cleanup */ 276 inet_aton(AllRouters, &addr); 277 if_leave_group(iface, &addr); 278 279 return (0); 280} 281 282int 283if_act_update(struct iface *iface) 284{ 285 int ret; 286 287 if (iface->state == IF_STA_DOWN) { 288 if (!((iface->flags & IFF_UP) && 289 LINK_STATE_IS_UP(iface->linkstate))) 290 return (0); 291 292 if (LIST_EMPTY(&iface->addr_list)) 293 return (0); 294 295 iface->state = IF_STA_ACTIVE; 296 ret = if_act_start(iface); 297 } else { 298 if (!LIST_EMPTY(&iface->addr_list)) 299 return (0); 300 301 iface->state = IF_STA_DOWN; 302 ret = if_act_reset(iface); 303 } 304 305 return (ret); 306} 307 308struct ctl_iface * 309if_to_ctl(struct iface *iface) 310{ 311 static struct ctl_iface ictl; 312 struct timeval tv, now, res; 313 314 memcpy(ictl.name, iface->name, sizeof(ictl.name)); 315 ictl.rtr_id.s_addr = ldpe_router_id(); 316 ictl.ifindex = iface->ifindex; 317 ictl.state = iface->state; 318 ictl.mtu = iface->mtu; 319 ictl.baudrate = iface->baudrate; 320 ictl.holdtime = iface->holdtime; 321 ictl.hello_interval = iface->hello_interval; 322 ictl.flags = iface->flags; 323 ictl.type = iface->type; 324 ictl.linkstate = iface->linkstate; 325 ictl.mediatype = iface->media_type; 326 ictl.priority = iface->priority; 327 328 gettimeofday(&now, NULL); 329 if (evtimer_pending(&iface->hello_timer, &tv)) { 330 timersub(&tv, &now, &res); 331 ictl.hello_timer = res.tv_sec; 332 } else 333 ictl.hello_timer = -1; 334 335 if (iface->state != IF_STA_DOWN && 336 iface->uptime != 0) { 337 ictl.uptime = now.tv_sec - iface->uptime; 338 } else 339 ictl.uptime = 0; 340 341 return (&ictl); 342} 343 344/* misc */ 345int 346if_set_mcast_ttl(int fd, u_int8_t ttl) 347{ 348 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, 349 (char *)&ttl, sizeof(ttl)) < 0) { 350 log_warn("if_set_mcast_ttl: error setting " 351 "IP_MULTICAST_TTL to %d", ttl); 352 return (-1); 353 } 354 355 return (0); 356} 357 358int 359if_set_tos(int fd, int tos) 360{ 361 if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) { 362 log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos); 363 return (-1); 364 } 365 366 return (0); 367} 368 369int 370if_set_recvif(int fd, int enable) 371{ 372 if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable, 373 sizeof(enable)) < 0) { 374 log_warn("if_set_recvif: error setting IP_RECVIF"); 375 return (-1); 376 } 377 return (0); 378} 379 380void 381if_set_recvbuf(int fd) 382{ 383 int bsize; 384 385 bsize = 65535; 386 while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, 387 sizeof(bsize)) == -1) 388 bsize /= 2; 389} 390 391int 392if_set_reuse(int fd, int enable) 393{ 394 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, 395 sizeof(int)) < 0) { 396 log_warn("if_set_reuse: error setting SO_REUSEADDR"); 397 return (-1); 398 } 399 400 return (0); 401} 402 403/* 404 * only one JOIN or DROP per interface and address is allowed so we need 405 * to keep track of what is added and removed. 406 */ 407struct if_group_count { 408 LIST_ENTRY(if_group_count) entry; 409 struct in_addr addr; 410 unsigned int ifindex; 411 int count; 412}; 413 414LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist); 415 416int 417if_join_group(struct iface *iface, struct in_addr *addr) 418{ 419 struct ip_mreq mreq; 420 struct if_group_count *ifg; 421 struct if_addr *if_addr; 422 423 LIST_FOREACH(ifg, &ifglist, entry) 424 if (iface->ifindex == ifg->ifindex && 425 addr->s_addr == ifg->addr.s_addr) 426 break; 427 if (ifg == NULL) { 428 if ((ifg = calloc(1, sizeof(*ifg))) == NULL) 429 fatal("if_join_group"); 430 ifg->addr.s_addr = addr->s_addr; 431 ifg->ifindex = iface->ifindex; 432 LIST_INSERT_HEAD(&ifglist, ifg, entry); 433 } 434 435 if (ifg->count++ != 0) 436 /* already joined */ 437 return (0); 438 439 if_addr = LIST_FIRST(&iface->addr_list); 440 mreq.imr_multiaddr.s_addr = addr->s_addr; 441 mreq.imr_interface.s_addr = if_addr->addr.s_addr; 442 443 if (setsockopt(iface->discovery_fd, IPPROTO_IP, 444 IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { 445 log_warn("if_join_group: error IP_ADD_MEMBERSHIP, " 446 "interface %s address %s", iface->name, 447 inet_ntoa(*addr)); 448 LIST_REMOVE(ifg, entry); 449 free(ifg); 450 return (-1); 451 } 452 return (0); 453} 454 455int 456if_leave_group(struct iface *iface, struct in_addr *addr) 457{ 458 struct ip_mreq mreq; 459 struct if_group_count *ifg; 460 struct if_addr *if_addr; 461 462 LIST_FOREACH(ifg, &ifglist, entry) 463 if (iface->ifindex == ifg->ifindex && 464 addr->s_addr == ifg->addr.s_addr) 465 break; 466 467 /* if interface is not found just try to drop membership */ 468 if (ifg) { 469 if (--ifg->count != 0) 470 /* others still joined */ 471 return (0); 472 473 LIST_REMOVE(ifg, entry); 474 free(ifg); 475 } 476 477 if_addr = LIST_FIRST(&iface->addr_list); 478 if (!if_addr) 479 return (0); 480 481 mreq.imr_multiaddr.s_addr = addr->s_addr; 482 mreq.imr_interface.s_addr = if_addr->addr.s_addr; 483 484 if (setsockopt(iface->discovery_fd, IPPROTO_IP, 485 IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { 486 log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, " 487 "interface %s address %s", iface->name, 488 inet_ntoa(*addr)); 489 return (-1); 490 } 491 492 return (0); 493} 494 495int 496if_set_mcast(struct iface *iface) 497{ 498 struct if_addr *if_addr; 499 500 if_addr = LIST_FIRST(&iface->addr_list); 501 502 if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF, 503 &if_addr->addr.s_addr, sizeof(if_addr->addr.s_addr)) < 0) { 504 log_debug("if_set_mcast: error setting " 505 "IP_MULTICAST_IF, interface %s", iface->name); 506 return (-1); 507 } 508 509 return (0); 510} 511 512int 513if_set_mcast_loop(int fd) 514{ 515 u_int8_t loop = 0; 516 517 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 518 (char *)&loop, sizeof(loop)) < 0) { 519 log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP"); 520 return (-1); 521 } 522 523 return (0); 524} 525