interface.c revision 1.6
1/* $OpenBSD: interface.c,v 1.6 2010/05/19 15:28:51 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 /* set event handlers for interface */ 191 evtimer_set(&iface->hello_timer, if_hello_timer, iface); 192 193 iface->discovery_fd = xconf->ldp_discovery_socket; 194} 195 196/* timers */ 197/* ARGSUSED */ 198void 199if_hello_timer(int fd, short event, void *arg) 200{ 201 struct iface *iface = arg; 202 struct timeval tv; 203 204 send_hello(iface); 205 206 /* reschedule hello_timer */ 207 timerclear(&tv); 208 tv.tv_sec = iface->hello_interval; 209 if (evtimer_add(&iface->hello_timer, &tv) == -1) 210 fatal("if_hello_timer"); 211} 212 213void 214if_start_hello_timer(struct iface *iface) 215{ 216 struct timeval tv; 217 218 timerclear(&tv); 219 tv.tv_sec = iface->hello_interval; 220 if (evtimer_add(&iface->hello_timer, &tv) == -1) 221 fatal("if_start_hello_timer"); 222} 223 224void 225if_stop_hello_timer(struct iface *iface) 226{ 227 if (evtimer_del(&iface->hello_timer) == -1) 228 fatal("if_stop_hello_timer"); 229} 230 231/* actions */ 232int 233if_act_start(struct iface *iface) 234{ 235 struct in_addr addr; 236 struct timeval now; 237 238 if (!((iface->flags & IFF_UP) && 239 (LINK_STATE_IS_UP(iface->linkstate) || 240 (iface->linkstate == LINK_STATE_UNKNOWN && 241 iface->media_type != IFT_CARP)))) { 242 log_debug("if_act_start: interface %s link down", 243 iface->name); 244 return (0); 245 } 246 247 if (iface->media_type == IFT_CARP && iface->passive == 0) { 248 /* force passive mode on carp interfaces */ 249 log_warnx("if_act_start: forcing interface %s to passive", 250 iface->name); 251 iface->passive = 1; 252 } 253 254 gettimeofday(&now, NULL); 255 iface->uptime = now.tv_sec; 256 257 inet_aton(AllRouters, &addr); 258 if (if_join_group(iface, &addr)) 259 return (-1); 260 iface->state = IF_STA_DOWN; 261 262 /* hello timer needs to be started in any case */ 263 if_start_hello_timer(iface); 264 return (0); 265} 266 267int 268if_act_reset(struct iface *iface) 269{ 270/* struct nbr *nbr = NULL; */ 271 struct in_addr addr; 272 273 inet_aton(AllRouters, &addr); 274 if (if_leave_group(iface, &addr)) { 275 log_warnx("if_act_reset: error leaving group %s, " 276 "interface %s", inet_ntoa(addr), iface->name); 277 } 278/* 279 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 280 if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) { 281 log_debug("if_act_reset: error killing neighbor %s", 282 inet_ntoa(nbr->id)); 283 } 284 } 285*/ 286 return (0); 287} 288 289struct ctl_iface * 290if_to_ctl(struct iface *iface) 291{ 292 static struct ctl_iface ictl; 293 struct timeval tv, now, res; 294 struct nbr *nbr; 295 296 memcpy(ictl.name, iface->name, sizeof(ictl.name)); 297 memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr)); 298 memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask)); 299 ictl.rtr_id.s_addr = ldpe_router_id(); 300 ictl.ifindex = iface->ifindex; 301 ictl.state = iface->state; 302 ictl.mtu = iface->mtu; 303 ictl.nbr_cnt = 0; 304 ictl.adj_cnt = 0; 305 ictl.baudrate = iface->baudrate; 306 ictl.holdtime = iface->holdtime; 307 ictl.hello_interval = iface->hello_interval; 308 ictl.flags = iface->flags; 309 ictl.type = iface->type; 310 ictl.linkstate = iface->linkstate; 311 ictl.mediatype = iface->media_type; 312 ictl.priority = iface->priority; 313 ictl.passive = iface->passive; 314 315 gettimeofday(&now, NULL); 316 if (evtimer_pending(&iface->hello_timer, &tv)) { 317 timersub(&tv, &now, &res); 318 ictl.hello_timer = res.tv_sec; 319 } else 320 ictl.hello_timer = -1; 321 322 if (iface->state != IF_STA_DOWN) { 323 ictl.uptime = now.tv_sec - iface->uptime; 324 } else 325 ictl.uptime = 0; 326 327 LIST_FOREACH(nbr, &iface->nbr_list, entry) 328 ictl.nbr_cnt++; 329 330 return (&ictl); 331} 332 333/* misc */ 334int 335if_set_mcast_ttl(int fd, u_int8_t ttl) 336{ 337 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, 338 (char *)&ttl, sizeof(ttl)) < 0) { 339 log_warn("if_set_mcast_ttl: error setting " 340 "IP_MULTICAST_TTL to %d", ttl); 341 return (-1); 342 } 343 344 return (0); 345} 346 347int 348if_set_tos(int fd, int tos) 349{ 350 if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) { 351 log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos); 352 return (-1); 353 } 354 355 return (0); 356} 357 358int 359if_set_recvif(int fd, int enable) 360{ 361 if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable, 362 sizeof(enable)) < 0) { 363 log_warn("if_set_recvif: error setting IP_RECVIF"); 364 return (-1); 365 } 366 return (0); 367} 368 369void 370if_set_recvbuf(int fd) 371{ 372 int bsize; 373 374 bsize = 65535; 375 while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, 376 sizeof(bsize)) == -1) 377 bsize /= 2; 378} 379 380int 381if_set_reuse(int fd, int enable) 382{ 383 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, 384 sizeof(int)) < 0) { 385 log_warn("if_set_reuse: error setting SO_REUSEADDR"); 386 return (-1); 387 } 388 389 return (0); 390} 391 392/* 393 * only one JOIN or DROP per interface and address is allowed so we need 394 * to keep track of what is added and removed. 395 */ 396struct if_group_count { 397 LIST_ENTRY(if_group_count) entry; 398 struct in_addr addr; 399 unsigned int ifindex; 400 int count; 401}; 402 403LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist); 404 405int 406if_join_group(struct iface *iface, struct in_addr *addr) 407{ 408 struct ip_mreq mreq; 409 struct if_group_count *ifg; 410 411 LIST_FOREACH(ifg, &ifglist, entry) 412 if (iface->ifindex == ifg->ifindex && 413 addr->s_addr == ifg->addr.s_addr) 414 break; 415 if (ifg == NULL) { 416 if ((ifg = calloc(1, sizeof(*ifg))) == NULL) 417 fatal("if_join_group"); 418 ifg->addr.s_addr = addr->s_addr; 419 ifg->ifindex = iface->ifindex; 420 LIST_INSERT_HEAD(&ifglist, ifg, entry); 421 } 422 423 if (ifg->count++ != 0) 424 /* already joined */ 425 return (0); 426 427 mreq.imr_multiaddr.s_addr = addr->s_addr; 428 mreq.imr_interface.s_addr = iface->addr.s_addr; 429 430 if (setsockopt(iface->discovery_fd, IPPROTO_IP, 431 IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { 432 log_warn("if_join_group: error IP_ADD_MEMBERSHIP, " 433 "interface %s address %s", iface->name, 434 inet_ntoa(*addr)); 435 return (-1); 436 } 437 return (0); 438} 439 440int 441if_leave_group(struct iface *iface, struct in_addr *addr) 442{ 443 struct ip_mreq mreq; 444 struct if_group_count *ifg; 445 446 LIST_FOREACH(ifg, &ifglist, entry) 447 if (iface->ifindex == ifg->ifindex && 448 addr->s_addr == ifg->addr.s_addr) 449 break; 450 451 /* if interface is not found just try to drop membership */ 452 if (ifg && --ifg->count != 0) 453 /* others still joined */ 454 return (0); 455 456 mreq.imr_multiaddr.s_addr = addr->s_addr; 457 mreq.imr_interface.s_addr = iface->addr.s_addr; 458 459 if (setsockopt(iface->discovery_fd, IPPROTO_IP, 460 IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { 461 log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, " 462 "interface %s address %s", iface->name, 463 inet_ntoa(*addr)); 464 return (-1); 465 } 466 467 if (ifg) { 468 LIST_REMOVE(ifg, entry); 469 free(ifg); 470 } 471 return (0); 472} 473 474int 475if_set_mcast(struct iface *iface) 476{ 477 if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF, 478 &iface->addr.s_addr, sizeof(iface->addr.s_addr)) < 0) { 479 log_debug("if_set_mcast: error setting " 480 "IP_MULTICAST_IF, interface %s", iface->name); 481 return (-1); 482 } 483 484 return (0); 485} 486 487int 488if_set_mcast_loop(int fd) 489{ 490 u_int8_t loop = 0; 491 492 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 493 (char *)&loop, sizeof(loop)) < 0) { 494 log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP"); 495 return (-1); 496 } 497 498 return (0); 499} 500