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