1 /* $OpenBSD: interface.c,v 1.22 2015/03/21 18:32:01 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 void if_hello_timer(int, short, void *); 45 void if_start_hello_timer(struct iface *); 46 void if_stop_hello_timer(struct iface *); 47 48 struct iface * 49 if_new(struct kif *kif) 50 { 51 struct iface *iface; 52 53 if ((iface = calloc(1, sizeof(*iface))) == NULL) 54 err(1, "if_new: calloc"); 55 56 iface->state = IF_STA_DOWN; 57 58 strlcpy(iface->name, kif->ifname, sizeof(iface->name)); 59 60 /* get type */ 61 if (kif->flags & IFF_POINTOPOINT) 62 iface->type = IF_TYPE_POINTOPOINT; 63 if (kif->flags & IFF_BROADCAST && 64 kif->flags & IFF_MULTICAST) 65 iface->type = IF_TYPE_BROADCAST; 66 67 /* get index and flags */ 68 iface->ifindex = kif->ifindex; 69 iface->flags = kif->flags; 70 iface->linkstate = kif->link_state; 71 iface->media_type = kif->media_type; 72 73 return (iface); 74 } 75 76 void 77 if_del(struct iface *iface) 78 { 79 struct if_addr *if_addr; 80 81 if (iface->state == IF_STA_ACTIVE) 82 if_reset(iface); 83 84 log_debug("if_del: interface %s", iface->name); 85 86 while ((if_addr = LIST_FIRST(&iface->addr_list)) != NULL) 87 LIST_REMOVE(if_addr, iface_entry); 88 89 free(iface); 90 } 91 92 void 93 if_init(struct ldpd_conf *xconf, struct iface *iface) 94 { 95 /* set event handlers for interface */ 96 evtimer_set(&iface->hello_timer, if_hello_timer, iface); 97 98 iface->discovery_fd = xconf->ldp_discovery_socket; 99 } 100 101 struct iface * 102 if_lookup(u_short ifindex) 103 { 104 struct iface *iface; 105 106 LIST_FOREACH(iface, &leconf->iface_list, entry) 107 if (iface->ifindex == ifindex) 108 return (iface); 109 110 return (NULL); 111 } 112 113 /* timers */ 114 /* ARGSUSED */ 115 void 116 if_hello_timer(int fd, short event, void *arg) 117 { 118 struct iface *iface = arg; 119 struct timeval tv; 120 121 send_hello(HELLO_LINK, iface, NULL); 122 123 /* reschedule hello_timer */ 124 timerclear(&tv); 125 tv.tv_sec = iface->hello_interval; 126 if (evtimer_add(&iface->hello_timer, &tv) == -1) 127 fatal("if_hello_timer"); 128 } 129 130 void 131 if_start_hello_timer(struct iface *iface) 132 { 133 struct timeval tv; 134 135 send_hello(HELLO_LINK, iface, NULL); 136 137 timerclear(&tv); 138 tv.tv_sec = iface->hello_interval; 139 if (evtimer_add(&iface->hello_timer, &tv) == -1) 140 fatal("if_start_hello_timer"); 141 } 142 143 void 144 if_stop_hello_timer(struct iface *iface) 145 { 146 if (evtimer_pending(&iface->hello_timer, NULL) && 147 evtimer_del(&iface->hello_timer) == -1) 148 fatal("if_stop_hello_timer"); 149 } 150 151 int 152 if_start(struct iface *iface) 153 { 154 struct in_addr addr; 155 struct timeval now; 156 157 log_debug("if_start: %s", iface->name); 158 159 gettimeofday(&now, NULL); 160 iface->uptime = now.tv_sec; 161 162 inet_aton(AllRouters, &addr); 163 if (if_join_group(iface, &addr)) 164 return (-1); 165 166 /* hello timer needs to be started in any case */ 167 if_start_hello_timer(iface); 168 return (0); 169 } 170 171 int 172 if_reset(struct iface *iface) 173 { 174 struct in_addr addr; 175 struct adj *adj; 176 177 log_debug("if_reset: %s", iface->name); 178 179 while ((adj = LIST_FIRST(&iface->adj_list)) != NULL) { 180 LIST_REMOVE(adj, iface_entry); 181 adj_del(adj); 182 } 183 184 if_stop_hello_timer(iface); 185 186 /* try to cleanup */ 187 inet_aton(AllRouters, &addr); 188 if_leave_group(iface, &addr); 189 190 return (0); 191 } 192 193 int 194 if_update(struct iface *iface) 195 { 196 int ret; 197 198 if (iface->state == IF_STA_DOWN) { 199 if (!(iface->flags & IFF_UP) || 200 !LINK_STATE_IS_UP(iface->linkstate) || 201 LIST_EMPTY(&iface->addr_list)) 202 return (0); 203 204 iface->state = IF_STA_ACTIVE; 205 ret = if_start(iface); 206 } else { 207 if ((iface->flags & IFF_UP) && 208 LINK_STATE_IS_UP(iface->linkstate) && 209 !LIST_EMPTY(&iface->addr_list)) 210 return (0); 211 212 iface->state = IF_STA_DOWN; 213 ret = if_reset(iface); 214 } 215 216 return (ret); 217 } 218 219 struct ctl_iface * 220 if_to_ctl(struct iface *iface) 221 { 222 static struct ctl_iface ictl; 223 struct timeval now; 224 struct adj *adj; 225 226 memcpy(ictl.name, iface->name, sizeof(ictl.name)); 227 ictl.ifindex = iface->ifindex; 228 ictl.state = iface->state; 229 ictl.hello_holdtime = iface->hello_holdtime; 230 ictl.hello_interval = iface->hello_interval; 231 ictl.flags = iface->flags; 232 ictl.type = iface->type; 233 ictl.linkstate = iface->linkstate; 234 ictl.mediatype = iface->media_type; 235 236 gettimeofday(&now, NULL); 237 if (iface->state != IF_STA_DOWN && 238 iface->uptime != 0) { 239 ictl.uptime = now.tv_sec - iface->uptime; 240 } else 241 ictl.uptime = 0; 242 243 ictl.adj_cnt = 0; 244 LIST_FOREACH(adj, &iface->adj_list, iface_entry) 245 ictl.adj_cnt++; 246 247 return (&ictl); 248 } 249 250 /* misc */ 251 int 252 if_set_mcast_ttl(int fd, u_int8_t ttl) 253 { 254 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, 255 (char *)&ttl, sizeof(ttl)) < 0) { 256 log_warn("if_set_mcast_ttl: error setting " 257 "IP_MULTICAST_TTL to %d", ttl); 258 return (-1); 259 } 260 261 return (0); 262 } 263 264 int 265 if_set_tos(int fd, int tos) 266 { 267 if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) { 268 log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos); 269 return (-1); 270 } 271 272 return (0); 273 } 274 275 int 276 if_set_recvif(int fd, int enable) 277 { 278 if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable, 279 sizeof(enable)) < 0) { 280 log_warn("if_set_recvif: error setting IP_RECVIF"); 281 return (-1); 282 } 283 return (0); 284 } 285 286 void 287 if_set_recvbuf(int fd) 288 { 289 int bsize; 290 291 bsize = 65535; 292 while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, 293 sizeof(bsize)) == -1) 294 bsize /= 2; 295 } 296 297 int 298 if_set_reuse(int fd, int enable) 299 { 300 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, 301 sizeof(int)) < 0) { 302 log_warn("if_set_reuse: error setting SO_REUSEADDR"); 303 return (-1); 304 } 305 306 return (0); 307 } 308 309 /* 310 * only one JOIN or DROP per interface and address is allowed so we need 311 * to keep track of what is added and removed. 312 */ 313 struct if_group_count { 314 LIST_ENTRY(if_group_count) entry; 315 struct in_addr addr; 316 unsigned int ifindex; 317 int count; 318 }; 319 320 LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist); 321 322 int 323 if_join_group(struct iface *iface, struct in_addr *addr) 324 { 325 struct ip_mreq mreq; 326 struct if_group_count *ifg; 327 struct if_addr *if_addr; 328 329 LIST_FOREACH(ifg, &ifglist, entry) 330 if (iface->ifindex == ifg->ifindex && 331 addr->s_addr == ifg->addr.s_addr) 332 break; 333 if (ifg == NULL) { 334 if ((ifg = calloc(1, sizeof(*ifg))) == NULL) 335 fatal("if_join_group"); 336 ifg->addr.s_addr = addr->s_addr; 337 ifg->ifindex = iface->ifindex; 338 LIST_INSERT_HEAD(&ifglist, ifg, entry); 339 } 340 341 if (ifg->count++ != 0) 342 /* already joined */ 343 return (0); 344 345 if_addr = LIST_FIRST(&iface->addr_list); 346 mreq.imr_multiaddr.s_addr = addr->s_addr; 347 mreq.imr_interface.s_addr = if_addr->addr.s_addr; 348 349 if (setsockopt(iface->discovery_fd, IPPROTO_IP, 350 IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { 351 log_warn("if_join_group: error IP_ADD_MEMBERSHIP, " 352 "interface %s address %s", iface->name, 353 inet_ntoa(*addr)); 354 LIST_REMOVE(ifg, entry); 355 free(ifg); 356 return (-1); 357 } 358 return (0); 359 } 360 361 int 362 if_leave_group(struct iface *iface, struct in_addr *addr) 363 { 364 struct ip_mreq mreq; 365 struct if_group_count *ifg; 366 struct if_addr *if_addr; 367 368 LIST_FOREACH(ifg, &ifglist, entry) 369 if (iface->ifindex == ifg->ifindex && 370 addr->s_addr == ifg->addr.s_addr) 371 break; 372 373 /* if interface is not found just try to drop membership */ 374 if (ifg) { 375 if (--ifg->count != 0) 376 /* others still joined */ 377 return (0); 378 379 LIST_REMOVE(ifg, entry); 380 free(ifg); 381 } 382 383 if_addr = LIST_FIRST(&iface->addr_list); 384 if (!if_addr) 385 return (0); 386 387 mreq.imr_multiaddr.s_addr = addr->s_addr; 388 mreq.imr_interface.s_addr = if_addr->addr.s_addr; 389 390 if (setsockopt(iface->discovery_fd, IPPROTO_IP, 391 IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) { 392 log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, " 393 "interface %s address %s", iface->name, 394 inet_ntoa(*addr)); 395 return (-1); 396 } 397 398 return (0); 399 } 400 401 int 402 if_set_mcast(struct iface *iface) 403 { 404 struct if_addr *if_addr; 405 406 if_addr = LIST_FIRST(&iface->addr_list); 407 408 if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF, 409 &if_addr->addr.s_addr, sizeof(if_addr->addr.s_addr)) < 0) { 410 log_debug("if_set_mcast: error setting " 411 "IP_MULTICAST_IF, interface %s", iface->name); 412 return (-1); 413 } 414 415 return (0); 416 } 417 418 int 419 if_set_mcast_loop(int fd) 420 { 421 u_int8_t loop = 0; 422 423 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 424 (char *)&loop, sizeof(loop)) < 0) { 425 log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP"); 426 return (-1); 427 } 428 429 return (0); 430 } 431