1 /* $OpenBSD: interface.c,v 1.8 2009/09/26 18:24:58 michele Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> 5 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 6 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/ioctl.h> 23 #include <sys/time.h> 24 #include <sys/socket.h> 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 #include <net/if.h> 28 #include <net/if_types.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 "ripd.h" 38 #include "log.h" 39 #include "rip.h" 40 #include "rde.h" 41 #include "ripe.h" 42 43 extern struct ripd_conf *conf; 44 45 int if_act_start(struct iface *); 46 int if_act_reset(struct iface *); 47 48 struct { 49 int state; 50 enum iface_event event; 51 enum iface_action action; 52 int new_state; 53 } iface_fsm[] = { 54 /* current state event that happened action to take resulting state */ 55 {IF_STA_DOWN, IF_EVT_UP, IF_ACT_STRT, 0}, 56 {IF_STA_ANY, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN}, 57 {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0}, 58 }; 59 60 const char * const if_action_names[] = { 61 "NOTHING", 62 "START", 63 "RESET" 64 }; 65 66 static const char * const if_event_names[] = { 67 "NOTHING", 68 "UP", 69 "DOWN", 70 }; 71 72 void 73 if_init(struct ripd_conf *xconf, struct iface *iface) 74 { 75 struct ifreq ifr; 76 u_int rdomain; 77 78 /* XXX as in ospfd I would like to kill that. This is a design error */ 79 iface->fd = xconf->rip_socket; 80 81 strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name)); 82 if (ioctl(iface->fd, SIOCGIFRTABLEID, (caddr_t)&ifr) == -1) 83 rdomain = 0; 84 else { 85 rdomain = ifr.ifr_rdomainid; 86 if (setsockopt(iface->fd, IPPROTO_IP, SO_RDOMAIN, &rdomain, 87 sizeof(rdomain)) == -1) 88 fatal("failed to set rdomain"); 89 } 90 if (rdomain != xconf->rdomain) 91 fatalx("interface rdomain mismatch"); 92 93 ripe_demote_iface(iface, 0); 94 } 95 96 int 97 if_fsm(struct iface *iface, enum iface_event event) 98 { 99 int old_state; 100 int new_state = 0; 101 int i, ret = 0; 102 103 old_state = iface->state; 104 105 for (i = 0; iface_fsm[i].state != -1; i++) 106 if ((iface_fsm[i].state & old_state) && 107 (iface_fsm[i].event == event)) { 108 new_state = iface_fsm[i].new_state; 109 break; 110 } 111 112 if (iface_fsm[i].state == -1) { 113 /* event outside of the defined fsm, ignore it. */ 114 log_debug("if_fsm: interface %s, " 115 "event '%s' not expected in state '%s'", iface->name, 116 if_event_name(event), if_state_name(old_state)); 117 return (0); 118 } 119 120 switch (iface_fsm[i].action) { 121 case IF_ACT_STRT: 122 ret = if_act_start(iface); 123 break; 124 case IF_ACT_RST: 125 ret = if_act_reset(iface); 126 break; 127 case IF_ACT_NOTHING: 128 /* do nothing */ 129 break; 130 } 131 132 if (ret) { 133 log_debug("if_fsm: error changing state for interface %s, " 134 "event '%s', state '%s'", iface->name, if_event_name(event), 135 if_state_name(old_state)); 136 return (0); 137 } 138 139 if (new_state != 0) 140 iface->state = new_state; 141 142 if (old_state == IF_STA_ACTIVE && iface->state == IF_STA_DOWN) 143 ripe_demote_iface(iface, 0); 144 if (old_state & IF_STA_DOWN && iface->state == IF_STA_ACTIVE) 145 ripe_demote_iface(iface, 1); 146 147 log_debug("if_fsm: event '%s' resulted in action '%s' and changing " 148 "state for interface %s from '%s' to '%s'", 149 if_event_name(event), if_action_name(iface_fsm[i].action), 150 iface->name, if_state_name(old_state), if_state_name(iface->state)); 151 152 return (ret); 153 } 154 155 struct iface * 156 if_find_index(u_short ifindex) 157 { 158 struct iface *iface; 159 160 LIST_FOREACH(iface, &conf->iface_list, entry) { 161 if (iface->ifindex == ifindex) 162 return (iface); 163 } 164 165 return (NULL); 166 } 167 168 169 /* actions */ 170 int 171 if_act_start(struct iface *iface) 172 { 173 struct in_addr addr; 174 struct timeval now; 175 176 if (iface->passive) { 177 log_debug("if_act_start: cannot start passive interface %s", 178 iface->name); 179 return (0); 180 } 181 182 if (!((iface->flags & IFF_UP) && 183 (LINK_STATE_IS_UP(iface->linkstate) || 184 (iface->linkstate == LINK_STATE_UNKNOWN && 185 iface->media_type != IFT_CARP)))) { 186 log_debug("if_act_start: interface %s link down", 187 iface->name); 188 return (0); 189 } 190 191 gettimeofday(&now, NULL); 192 iface->uptime = now.tv_sec; 193 194 switch (iface->type) { 195 case IF_TYPE_POINTOPOINT: 196 case IF_TYPE_BROADCAST: 197 inet_aton(ALL_RIP_ROUTERS, &addr); 198 if (if_join_group(iface, &addr)) { 199 log_warn("if_act_start: error joining group %s, " 200 "interface %s", inet_ntoa(addr), iface->name); 201 return (-1); 202 } 203 204 iface->state = IF_STA_ACTIVE; 205 break; 206 default: 207 fatalx("if_act_start: unknown interface type"); 208 } 209 210 return (0); 211 } 212 213 int 214 if_act_reset(struct iface *iface) 215 { 216 struct nbr *nbr = NULL; 217 struct in_addr addr; 218 219 if (iface->passive) 220 return (0); 221 222 switch (iface->type) { 223 case IF_TYPE_POINTOPOINT: 224 case IF_TYPE_BROADCAST: 225 inet_aton(ALL_RIP_ROUTERS, &addr); 226 if (if_leave_group(iface, &addr)) { 227 log_warn("if_act_reset: error leaving group %s, " 228 "interface %s", inet_ntoa(addr), iface->name); 229 } 230 break; 231 default: 232 fatalx("if_act_reset: unknown interface type"); 233 } 234 235 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 236 if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) { 237 log_debug("if_act_reset: error killing neighbor %s", 238 inet_ntoa(nbr->id)); 239 } 240 } 241 242 return (0); 243 } 244 245 const char * 246 if_event_name(int event) 247 { 248 return (if_event_names[event]); 249 } 250 251 const char * 252 if_action_name(int action) 253 { 254 return (if_action_names[action]); 255 } 256 257 /* misc */ 258 int 259 if_set_mcast_ttl(int fd, u_int8_t ttl) 260 { 261 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, 262 (char *)&ttl, sizeof(ttl)) < 0) { 263 log_warn("if_set_mcast_ttl: error setting " 264 "IP_MULTICAST_TTL to %d", ttl); 265 return (-1); 266 } 267 268 return (0); 269 } 270 271 int 272 if_set_opt(int fd) 273 { 274 int yes = 1; 275 276 if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &yes, 277 sizeof(int)) < 0) { 278 log_warn("if_set_opt: error setting IP_RECVIF"); 279 return (-1); 280 } 281 282 return (0); 283 } 284 285 int 286 if_set_tos(int fd, int tos) 287 { 288 if (setsockopt(fd, IPPROTO_IP, IP_TOS, 289 (int *)&tos, sizeof(tos)) < 0) { 290 log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos); 291 return (-1); 292 } 293 294 return (0); 295 } 296 297 int 298 if_set_mcast(struct iface *iface) 299 { 300 switch (iface->type) { 301 case IF_TYPE_POINTOPOINT: 302 case IF_TYPE_BROADCAST: 303 if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF, 304 &iface->addr.s_addr, sizeof(iface->addr.s_addr)) < 0) { 305 log_debug("if_set_mcast: error setting " 306 "IP_MULTICAST_IF, interface %s", iface->name); 307 return (-1); 308 } 309 break; 310 default: 311 fatalx("if_set_mcast: unknown interface type"); 312 } 313 314 return (0); 315 } 316 317 int 318 if_set_mcast_loop(int fd) 319 { 320 u_int8_t loop = 0; 321 322 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 323 (char *)&loop, sizeof(loop)) < 0) { 324 log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP"); 325 return (-1); 326 } 327 328 return (0); 329 } 330 331 void 332 if_set_recvbuf(int fd) 333 { 334 int bsize; 335 336 bsize = 65535; 337 while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, 338 sizeof(bsize)) == -1) 339 bsize /= 2; 340 } 341 342 int 343 if_join_group(struct iface *iface, struct in_addr *addr) 344 { 345 struct ip_mreq mreq; 346 347 switch (iface->type) { 348 case IF_TYPE_POINTOPOINT: 349 case IF_TYPE_BROADCAST: 350 mreq.imr_multiaddr.s_addr = addr->s_addr; 351 mreq.imr_interface.s_addr = iface->addr.s_addr; 352 353 if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, 354 (void *)&mreq, sizeof(mreq)) < 0) 355 return (-1); 356 break; 357 default: 358 fatalx("if_join_group: unknown interface type"); 359 } 360 361 return (0); 362 } 363 364 int 365 if_leave_group(struct iface *iface, struct in_addr *addr) 366 { 367 struct ip_mreq mreq; 368 369 switch (iface->type) { 370 case IF_TYPE_POINTOPOINT: 371 case IF_TYPE_BROADCAST: 372 mreq.imr_multiaddr.s_addr = addr->s_addr; 373 mreq.imr_interface.s_addr = iface->addr.s_addr; 374 375 if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, 376 (void *)&mreq, sizeof(mreq)) < 0) 377 return (-1); 378 break; 379 default: 380 fatalx("if_leave_group: unknown interface type"); 381 } 382 383 return (0); 384 } 385 386 struct iface * 387 if_new(struct kif *kif) 388 { 389 struct sockaddr_in *sain; 390 struct iface *iface; 391 struct ifreq *ifr; 392 int s; 393 394 if ((iface = calloc(1, sizeof(*iface))) == NULL) 395 err(1, "if_new: calloc"); 396 397 iface->state = IF_STA_DOWN; 398 399 LIST_INIT(&iface->nbr_list); 400 TAILQ_INIT(&iface->rp_list); 401 TAILQ_INIT(&iface->rq_list); 402 403 strlcpy(iface->name, kif->ifname, sizeof(iface->name)); 404 405 if ((ifr = calloc(1, sizeof(*ifr))) == NULL) 406 err(1, "if_new: calloc"); 407 408 /* set up ifreq */ 409 strlcpy(ifr->ifr_name, kif->ifname, sizeof(ifr->ifr_name)); 410 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 411 err(1, "if_new: socket"); 412 413 /* get type */ 414 if (kif->flags & IFF_POINTOPOINT) 415 iface->type = IF_TYPE_POINTOPOINT; 416 if (kif->flags & IFF_BROADCAST && 417 kif->flags & IFF_MULTICAST) 418 iface->type = IF_TYPE_BROADCAST; 419 if (kif->flags & IFF_LOOPBACK) { 420 iface->type = IF_TYPE_POINTOPOINT; 421 /* XXX protect loopback from sending packets over lo? */ 422 } 423 424 /* get mtu, index and flags */ 425 iface->mtu = kif->mtu; 426 iface->ifindex = kif->ifindex; 427 iface->flags = kif->flags; 428 iface->linkstate = kif->link_state; 429 iface->media_type = kif->media_type; 430 iface->baudrate = kif->baudrate; 431 432 /* get address */ 433 if (ioctl(s, SIOCGIFADDR, ifr) < 0) 434 err(1, "if_new: cannot get address"); 435 sain = (struct sockaddr_in *)&ifr->ifr_addr; 436 iface->addr = sain->sin_addr; 437 438 /* get mask */ 439 if (ioctl(s, SIOCGIFNETMASK, ifr) < 0) 440 err(1, "if_new: cannot get mask"); 441 sain = (struct sockaddr_in *)&ifr->ifr_addr; 442 iface->mask = sain->sin_addr; 443 444 /* get p2p dst address */ 445 if (kif->flags & IFF_POINTOPOINT) { 446 if (ioctl(s, SIOCGIFDSTADDR, ifr) < 0) 447 err(1, "if_new: cannot get dst addr"); 448 sain = (struct sockaddr_in *)&ifr->ifr_addr; 449 iface->dst = sain->sin_addr; 450 } 451 452 free(ifr); 453 close(s); 454 455 return (iface); 456 } 457 458 void 459 if_del(struct iface *iface) 460 { 461 struct nbr *nbr; 462 463 log_debug("if_del: interface %s", iface->name); 464 465 /* revert the demotion when the interface is deleted */ 466 if (iface->state == IF_STA_DOWN) 467 ripe_demote_iface(iface, 1); 468 469 /* clear lists etc */ 470 while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) 471 nbr_act_del(nbr); 472 473 /* XXX rq_list, rp_list */ 474 475 free(iface); 476 } 477 478 struct ctl_iface * 479 if_to_ctl(struct iface *iface) 480 { 481 static struct ctl_iface ictl; 482 struct timeval now; 483 484 memcpy(ictl.name, iface->name, sizeof(ictl.name)); 485 memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr)); 486 memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask)); 487 488 ictl.ifindex = iface->ifindex; 489 ictl.state = iface->state; 490 ictl.mtu = iface->mtu; 491 492 ictl.baudrate = iface->baudrate; 493 ictl.flags = iface->flags; 494 ictl.metric = iface->cost; 495 ictl.type = iface->type; 496 ictl.linkstate = iface->linkstate; 497 ictl.passive = iface->passive; 498 ictl.mediatype = iface->media_type; 499 500 gettimeofday(&now, NULL); 501 502 if (iface->state != IF_STA_DOWN) { 503 ictl.uptime = now.tv_sec - iface->uptime; 504 } else 505 ictl.uptime = 0; 506 507 return (&ictl); 508 } 509