1 /* $OpenBSD: interface.c,v 1.20 2013/10/15 20:41:09 renato 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 42 extern struct ldpd_conf *leconf; 43 44 int if_act_start(struct iface *); 45 int if_act_reset(struct iface *); 46 int if_act_update(struct iface *); 47 void if_hello_timer(int, short, void *); 48 void if_start_hello_timer(struct iface *); 49 void if_stop_hello_timer(struct iface *); 50 struct nbr *if_elect(struct nbr *, struct nbr *); 51 52 struct { 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 69 const char * const if_event_names[] = { 70 "NOTHING", 71 "UP", 72 "DOWN", 73 "NEWADDR", 74 "DELADDR" 75 }; 76 77 const char * const if_action_names[] = { 78 "NOTHING", 79 "UPDATE", 80 "RESET" 81 }; 82 83 int 84 if_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 137 struct iface * 138 if_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 strlcpy(iface->name, kif->ifname, sizeof(iface->name)); 148 149 /* get type */ 150 if (kif->flags & IFF_POINTOPOINT) 151 iface->type = IF_TYPE_POINTOPOINT; 152 if (kif->flags & IFF_BROADCAST && 153 kif->flags & IFF_MULTICAST) 154 iface->type = IF_TYPE_BROADCAST; 155 156 /* get index and flags */ 157 iface->ifindex = kif->ifindex; 158 iface->flags = kif->flags; 159 iface->linkstate = kif->link_state; 160 iface->media_type = kif->media_type; 161 162 return (iface); 163 } 164 165 void 166 if_del(struct iface *iface) 167 { 168 struct adj *adj; 169 struct if_addr *if_addr; 170 171 log_debug("if_del: interface %s", iface->name); 172 173 while ((adj = LIST_FIRST(&iface->adj_list)) != NULL) { 174 LIST_REMOVE(adj, iface_entry); 175 adj_del(adj); 176 } 177 while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL) 178 LIST_REMOVE(if_addr, iface_entry); 179 180 free(iface); 181 } 182 183 void 184 if_init(struct ldpd_conf *xconf, struct iface *iface) 185 { 186 /* set event handlers for interface */ 187 evtimer_set(&iface->hello_timer, if_hello_timer, iface); 188 189 iface->discovery_fd = xconf->ldp_discovery_socket; 190 } 191 192 struct iface * 193 if_lookup(u_short ifindex) 194 { 195 struct iface *iface; 196 197 LIST_FOREACH(iface, &leconf->iface_list, entry) 198 if (iface->ifindex == ifindex) 199 return (iface); 200 201 return (NULL); 202 } 203 204 /* timers */ 205 /* ARGSUSED */ 206 void 207 if_hello_timer(int fd, short event, void *arg) 208 { 209 struct iface *iface = arg; 210 struct timeval tv; 211 212 send_hello(HELLO_LINK, iface, NULL); 213 214 /* reschedule hello_timer */ 215 timerclear(&tv); 216 tv.tv_sec = iface->hello_interval; 217 if (evtimer_add(&iface->hello_timer, &tv) == -1) 218 fatal("if_hello_timer"); 219 } 220 221 void 222 if_start_hello_timer(struct iface *iface) 223 { 224 struct timeval tv; 225 226 send_hello(HELLO_LINK, iface, NULL); 227 228 timerclear(&tv); 229 tv.tv_sec = iface->hello_interval; 230 if (evtimer_add(&iface->hello_timer, &tv) == -1) 231 fatal("if_start_hello_timer"); 232 } 233 234 void 235 if_stop_hello_timer(struct iface *iface) 236 { 237 if (evtimer_pending(&iface->hello_timer, NULL) && 238 evtimer_del(&iface->hello_timer) == -1) 239 fatal("if_stop_hello_timer"); 240 } 241 242 /* actions */ 243 int 244 if_act_start(struct iface *iface) 245 { 246 struct in_addr addr; 247 struct timeval now; 248 249 gettimeofday(&now, NULL); 250 iface->uptime = now.tv_sec; 251 252 inet_aton(AllRouters, &addr); 253 if (if_join_group(iface, &addr)) 254 return (-1); 255 256 /* hello timer needs to be started in any case */ 257 if_start_hello_timer(iface); 258 return (0); 259 } 260 261 int 262 if_act_reset(struct iface *iface) 263 { 264 struct in_addr addr; 265 266 if_stop_hello_timer(iface); 267 268 /* try to cleanup */ 269 inet_aton(AllRouters, &addr); 270 if_leave_group(iface, &addr); 271 272 return (0); 273 } 274 275 int 276 if_act_update(struct iface *iface) 277 { 278 int ret; 279 280 if (iface->state == IF_STA_DOWN) { 281 if (!((iface->flags & IFF_UP) && 282 LINK_STATE_IS_UP(iface->linkstate))) 283 return (0); 284 285 if (LIST_EMPTY(&iface->addr_list)) 286 return (0); 287 288 iface->state = IF_STA_ACTIVE; 289 ret = if_act_start(iface); 290 } else { 291 if (!LIST_EMPTY(&iface->addr_list)) 292 return (0); 293 294 iface->state = IF_STA_DOWN; 295 ret = if_act_reset(iface); 296 } 297 298 return (ret); 299 } 300 301 struct ctl_iface * 302 if_to_ctl(struct iface *iface) 303 { 304 static struct ctl_iface ictl; 305 struct timeval now; 306 struct adj *adj; 307 308 memcpy(ictl.name, iface->name, sizeof(ictl.name)); 309 ictl.ifindex = iface->ifindex; 310 ictl.state = iface->state; 311 ictl.hello_holdtime = iface->hello_holdtime; 312 ictl.hello_interval = iface->hello_interval; 313 ictl.flags = iface->flags; 314 ictl.type = iface->type; 315 ictl.linkstate = iface->linkstate; 316 ictl.mediatype = iface->media_type; 317 318 gettimeofday(&now, NULL); 319 if (iface->state != IF_STA_DOWN && 320 iface->uptime != 0) { 321 ictl.uptime = now.tv_sec - iface->uptime; 322 } else 323 ictl.uptime = 0; 324 325 ictl.adj_cnt = 0; 326 LIST_FOREACH(adj, &iface->adj_list, iface_entry) 327 ictl.adj_cnt++; 328 329 return (&ictl); 330 } 331 332 /* misc */ 333 int 334 if_set_mcast_ttl(int fd, u_int8_t ttl) 335 { 336 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, 337 (char *)&ttl, sizeof(ttl)) < 0) { 338 log_warn("if_set_mcast_ttl: error setting " 339 "IP_MULTICAST_TTL to %d", ttl); 340 return (-1); 341 } 342 343 return (0); 344 } 345 346 int 347 if_set_tos(int fd, int tos) 348 { 349 if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) { 350 log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos); 351 return (-1); 352 } 353 354 return (0); 355 } 356 357 int 358 if_set_recvif(int fd, int enable) 359 { 360 if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable, 361 sizeof(enable)) < 0) { 362 log_warn("if_set_recvif: error setting IP_RECVIF"); 363 return (-1); 364 } 365 return (0); 366 } 367 368 void 369 if_set_recvbuf(int fd) 370 { 371 int bsize; 372 373 bsize = 65535; 374 while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, 375 sizeof(bsize)) == -1) 376 bsize /= 2; 377 } 378 379 int 380 if_set_reuse(int fd, int enable) 381 { 382 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, 383 sizeof(int)) < 0) { 384 log_warn("if_set_reuse: error setting SO_REUSEADDR"); 385 return (-1); 386 } 387 388 return (0); 389 } 390 391 /* 392 * only one JOIN or DROP per interface and address is allowed so we need 393 * to keep track of what is added and removed. 394 */ 395 struct if_group_count { 396 LIST_ENTRY(if_group_count) entry; 397 struct in_addr addr; 398 unsigned int ifindex; 399 int count; 400 }; 401 402 LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist); 403 404 int 405 if_join_group(struct iface *iface, struct in_addr *addr) 406 { 407 struct ip_mreq mreq; 408 struct if_group_count *ifg; 409 struct if_addr *if_addr; 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 if_addr = LIST_FIRST(&iface->addr_list); 428 mreq.imr_multiaddr.s_addr = addr->s_addr; 429 mreq.imr_interface.s_addr = if_addr->addr.s_addr; 430 431 if (setsockopt(iface->discovery_fd, IPPROTO_IP, 432 IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { 433 log_warn("if_join_group: error IP_ADD_MEMBERSHIP, " 434 "interface %s address %s", iface->name, 435 inet_ntoa(*addr)); 436 LIST_REMOVE(ifg, entry); 437 free(ifg); 438 return (-1); 439 } 440 return (0); 441 } 442 443 int 444 if_leave_group(struct iface *iface, struct in_addr *addr) 445 { 446 struct ip_mreq mreq; 447 struct if_group_count *ifg; 448 struct if_addr *if_addr; 449 450 LIST_FOREACH(ifg, &ifglist, entry) 451 if (iface->ifindex == ifg->ifindex && 452 addr->s_addr == ifg->addr.s_addr) 453 break; 454 455 /* if interface is not found just try to drop membership */ 456 if (ifg) { 457 if (--ifg->count != 0) 458 /* others still joined */ 459 return (0); 460 461 LIST_REMOVE(ifg, entry); 462 free(ifg); 463 } 464 465 if_addr = LIST_FIRST(&iface->addr_list); 466 if (!if_addr) 467 return (0); 468 469 mreq.imr_multiaddr.s_addr = addr->s_addr; 470 mreq.imr_interface.s_addr = if_addr->addr.s_addr; 471 472 if (setsockopt(iface->discovery_fd, IPPROTO_IP, 473 IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { 474 log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, " 475 "interface %s address %s", iface->name, 476 inet_ntoa(*addr)); 477 return (-1); 478 } 479 480 return (0); 481 } 482 483 int 484 if_set_mcast(struct iface *iface) 485 { 486 struct if_addr *if_addr; 487 488 if_addr = LIST_FIRST(&iface->addr_list); 489 490 if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF, 491 &if_addr->addr.s_addr, sizeof(if_addr->addr.s_addr)) < 0) { 492 log_debug("if_set_mcast: error setting " 493 "IP_MULTICAST_IF, interface %s", iface->name); 494 return (-1); 495 } 496 497 return (0); 498 } 499 500 int 501 if_set_mcast_loop(int fd) 502 { 503 u_int8_t loop = 0; 504 505 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 506 (char *)&loop, sizeof(loop)) < 0) { 507 log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP"); 508 return (-1); 509 } 510 511 return (0); 512 } 513