| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * dhcpcd - DHCP client daemon | |
| 3 * Copyright (c) 2006-2009 Roy Marples <roy@marples.name> | |
| 4 * All rights reserved | |
| 5 | |
| 6 * Redistribution and use in source and binary forms, with or without | |
| 7 * modification, are permitted provided that the following conditions | |
| 8 * are met: | |
| 9 * 1. Redistributions of source code must retain the above copyright | |
| 10 * notice, this list of conditions and the following disclaimer. | |
| 11 * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 * notice, this list of conditions and the following disclaimer in the | |
| 13 * documentation and/or other materials provided with the distribution. | |
| 14 * | |
| 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 25 * SUCH DAMAGE. | |
| 26 */ | |
| 27 | |
| 28 #include <sys/stat.h> | |
| 29 #include <sys/uio.h> | |
| 30 #include <sys/wait.h> | |
| 31 | |
| 32 #include <netinet/in.h> | |
| 33 #include <arpa/inet.h> | |
| 34 | |
| 35 #include <ctype.h> | |
| 36 #include <errno.h> | |
| 37 #include <signal.h> | |
| 38 #include <stdlib.h> | |
| 39 #include <string.h> | |
| 40 #include <syslog.h> | |
| 41 #include <unistd.h> | |
| 42 | |
| 43 #include "config.h" | |
| 44 #include "common.h" | |
| 45 #include "configure.h" | |
| 46 #include "dhcp.h" | |
| 47 #include "if-options.h" | |
| 48 #include "if-pref.h" | |
| 49 #include "net.h" | |
| 50 #include "signals.h" | |
| 51 | |
| 52 #define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" | |
| 53 | |
| 54 /* Some systems have route metrics */ | |
| 55 #ifndef HAVE_ROUTE_METRIC | |
| 56 # ifdef __linux__ | |
| 57 # define HAVE_ROUTE_METRIC 1 | |
| 58 # endif | |
| 59 # ifndef HAVE_ROUTE_METRIC | |
| 60 # define HAVE_ROUTE_METRIC 0 | |
| 61 # endif | |
| 62 #endif | |
| 63 | |
| 64 static struct rt *routes; | |
| 65 | |
| 66 | |
| 67 static int | |
| 68 exec_script(char *const *argv, char *const *env) | |
| 69 { | |
| 70 pid_t pid; | |
| 71 sigset_t full; | |
| 72 sigset_t old; | |
| 73 | |
| 74 /* OK, we need to block signals */ | |
| 75 sigfillset(&full); | |
| 76 sigprocmask(SIG_SETMASK, &full, &old); | |
| 77 signal_reset(); | |
| 78 | |
| 79 switch (pid = vfork()) { | |
| 80 case -1: | |
| 81 syslog(LOG_ERR, "vfork: %m"); | |
| 82 break; | |
| 83 case 0: | |
| 84 sigprocmask(SIG_SETMASK, &old, NULL); | |
| 85 execve(argv[0], argv, env); | |
| 86 syslog(LOG_ERR, "%s: %m", argv[0]); | |
| 87 _exit(127); | |
| 88 /* NOTREACHED */ | |
| 89 } | |
| 90 | |
| 91 /* Restore our signals */ | |
| 92 signal_setup(); | |
| 93 sigprocmask(SIG_SETMASK, &old, NULL); | |
| 94 return pid; | |
| 95 } | |
| 96 | |
| 97 static char * | |
| 98 make_var(const char *prefix, const char *var) | |
| 99 { | |
| 100 size_t len; | |
| 101 char *v; | |
| 102 | |
| 103 len = strlen(prefix) + strlen(var) + 2; | |
| 104 v = xmalloc(len); | |
| 105 snprintf(v, len, "%s_%s", prefix, var); | |
| 106 return v; | |
| 107 } | |
| 108 | |
| 109 | |
| 110 static void | |
| 111 append_config(char ***env, ssize_t *len, | |
| 112 const char *prefix, const char *const *config) | |
| 113 { | |
| 114 ssize_t i, j, e1; | |
| 115 char **ne, *eq; | |
| 116 | |
| 117 if (config == NULL) | |
| 118 return; | |
| 119 | |
| 120 ne = *env; | |
| 121 for (i = 0; config[i] != NULL; i++) { | |
| 122 eq = strchr(config[i], '='); | |
| 123 e1 = eq - config[i] + 1; | |
| 124 for (j = 0; j < *len; j++) { | |
| 125 if (strncmp(ne[j] + strlen(prefix) + 1, | |
| 126 config[i], e1) == 0) | |
| 127 { | |
| 128 free(ne[j]); | |
| 129 ne[j] = make_var(prefix, config[i]); | |
| 130 break; | |
| 131 } | |
| 132 } | |
| 133 if (j == *len) { | |
| 134 j++; | |
| 135 ne = xrealloc(ne, sizeof(char *) * (j + 1)); | |
| 136 ne[j - 1] = make_var(prefix, config[i]); | |
| 137 *len = j; | |
| 138 } | |
| 139 } | |
| 140 *env = ne; | |
| 141 } | |
| 142 | |
| 143 static size_t | |
| 144 arraytostr(const char *const *argv, char **s) | |
| 145 { | |
| 146 const char *const *ap; | |
| 147 char *p; | |
| 148 size_t len, l; | |
| 149 | |
| 150 len = 0; | |
| 151 ap = argv; | |
| 152 while (*ap) | |
| 153 len += strlen(*ap++) + 1; | |
| 154 *s = p = xmalloc(len); | |
| 155 ap = argv; | |
| 156 while (*ap) { | |
| 157 l = strlen(*ap) + 1; | |
| 158 memcpy(p, *ap, l); | |
| 159 p += l; | |
| 160 ap++; | |
| 161 } | |
| 162 return len; | |
| 163 } | |
| 164 | |
| 165 static ssize_t | |
| 166 make_env(const struct interface *iface, char ***argv) | |
| 167 { | |
| 168 char **env, *p; | |
| 169 ssize_t e, elen, l; | |
| 170 const struct if_options *ifo = iface->state->options; | |
| 171 const struct interface *ifp; | |
| 172 | |
| 173 /* Make our env */ | |
| 174 elen = 8; | |
| 175 env = xmalloc(sizeof(char *) * (elen + 1)); | |
| 176 e = strlen("interface") + strlen(iface->name) + 2; | |
| 177 env[0] = xmalloc(e); | |
| 178 snprintf(env[0], e, "interface=%s", iface->name); | |
| 179 e = strlen("reason") + strlen(iface->state->reason) + 2; | |
| 180 env[1] = xmalloc(e); | |
| 181 snprintf(env[1], e, "reason=%s", iface->state->reason); | |
| 182 e = 20; | |
| 183 env[2] = xmalloc(e); | |
| 184 snprintf(env[2], e, "pid=%d", getpid()); | |
| 185 env[3] = xmalloc(e); | |
| 186 snprintf(env[3], e, "ifmetric=%d", iface->metric); | |
| 187 env[4] = xmalloc(e); | |
| 188 snprintf(env[4], e, "ifwireless=%d", iface->wireless); | |
| 189 env[5] = xmalloc(e); | |
| 190 snprintf(env[5], e, "ifflags=%u", iface->flags); | |
| 191 env[6] = xmalloc(e); | |
| 192 snprintf(env[6], e, "ifmtu=%d", get_mtu(iface->name)); | |
| 193 l = e = strlen("interface_order="); | |
| 194 for (ifp = ifaces; ifp; ifp = ifp->next) | |
| 195 e += strlen(ifp->name) + 1; | |
| 196 p = env[7] = xmalloc(e); | |
| 197 strlcpy(p, "interface_order=", e); | |
| 198 e -= l; | |
| 199 p += l; | |
| 200 for (ifp = ifaces; ifp; ifp = ifp->next) { | |
| 201 l = strlcpy(p, ifp->name, e); | |
| 202 p += l; | |
| 203 e -= l; | |
| 204 *p++ = ' '; | |
| 205 e--; | |
| 206 } | |
| 207 *--p = '\0'; | |
| 208 if (*iface->state->profile) { | |
| 209 e = strlen("profile=") + strlen(iface->state->profile) + 2; | |
| 210 env[elen] = xmalloc(e); | |
| 211 snprintf(env[elen++], e, "profile=%s", iface->state->profile); | |
| 212 } | |
| 213 if (iface->wireless) { | |
| 214 e = strlen("new_ssid=") + strlen(iface->ssid) + 2; | |
| 215 if (iface->state->new != NULL || | |
| 216 strcmp(iface->state->reason, "CARRIER") == 0) | |
| 217 { | |
| 218 env = xrealloc(env, sizeof(char *) * (elen + 2)); | |
| 219 env[elen] = xmalloc(e); | |
| 220 snprintf(env[elen++], e, "new_ssid=%s", iface->ssid); | |
| 221 } | |
| 222 if (iface->state->old != NULL || | |
| 223 strcmp(iface->state->reason, "NOCARRIER") == 0) | |
| 224 { | |
| 225 env = xrealloc(env, sizeof(char *) * (elen + 2)); | |
| 226 env[elen] = xmalloc(e); | |
| 227 snprintf(env[elen++], e, "old_ssid=%s", iface->ssid); | |
| 228 } | |
| 229 } | |
| 230 if (iface->state->old) { | |
| 231 e = configure_env(NULL, NULL, iface->state->old, ifo); | |
| 232 if (e > 0) { | |
| 233 env = xrealloc(env, sizeof(char *) * (elen + e + 1)); | |
| 234 elen += configure_env(env + elen, "old", | |
| 235 iface->state->old, ifo); | |
| 236 } | |
| 237 append_config(&env, &elen, "old", | |
| 238 (const char *const *)ifo->config); | |
| 239 } | |
| 240 if (iface->state->new) { | |
| 241 e = configure_env(NULL, NULL, iface->state->new, ifo); | |
| 242 if (e > 0) { | |
| 243 env = xrealloc(env, sizeof(char *) * (elen + e + 1)); | |
| 244 elen += configure_env(env + elen, "new", | |
| 245 iface->state->new, ifo); | |
| 246 } | |
| 247 append_config(&env, &elen, "new", | |
| 248 (const char *const *)ifo->config); | |
| 249 } | |
| 250 | |
| 251 /* Add our base environment */ | |
| 252 if (ifo->environ) { | |
| 253 e = 0; | |
| 254 while (ifo->environ[e++]) | |
| 255 ; | |
| 256 env = xrealloc(env, sizeof(char *) * (elen + e + 1)); | |
| 257 e = 0; | |
| 258 while (ifo->environ[e]) { | |
| 259 env[elen + e] = xstrdup(ifo->environ[e]); | |
| 260 e++; | |
| 261 } | |
| 262 elen += e; | |
| 263 } | |
| 264 env[elen] = '\0'; | |
| 265 | |
| 266 *argv = env; | |
| 267 return elen; | |
| 268 } | |
| 269 | |
| 270 int | |
| 271 send_interface(int fd, const struct interface *iface) | |
| 272 { | |
| 273 char **env, **ep, *s; | |
| 274 ssize_t elen; | |
| 275 struct iovec iov[2]; | |
| 276 int retval; | |
| 277 | |
| 278 retval = 0; | |
| 279 make_env(iface, &env); | |
| 280 elen = arraytostr((const char *const *)env, &s); | |
| 281 iov[0].iov_base = &elen; | |
| 282 iov[0].iov_len = sizeof(ssize_t); | |
| 283 iov[1].iov_base = s; | |
| 284 iov[1].iov_len = elen; | |
| 285 retval = writev(fd, iov, 2); | |
| 286 ep = env; | |
| 287 while (*ep) | |
| 288 free(*ep++); | |
| 289 free(env); | |
| 290 free(s); | |
| 291 return retval; | |
| 292 } | |
| 293 | |
| 294 int | |
| 295 run_script(const struct interface *iface) | |
| 296 { | |
| 297 char *const argv[2] = { UNCONST(iface->state->options->script), NULL }; | |
| 298 char **env = NULL, **ep; | |
| 299 char *path, *bigenv; | |
| 300 ssize_t e, elen = 0; | |
| 301 pid_t pid; | |
| 302 int status = 0; | |
| 303 const struct fd_list *fd; | |
| 304 struct iovec iov[2]; | |
| 305 | |
| 306 syslog(LOG_DEBUG, "%s: executing `%s', reason %s", | |
| 307 iface->name, argv[0], iface->state->reason); | |
| 308 | |
| 309 /* Make our env */ | |
| 310 elen = make_env(iface, &env); | |
| 311 env = xrealloc(env, sizeof(char *) * (elen + 2)); | |
| 312 /* Add path to it */ | |
| 313 path = getenv("PATH"); | |
| 314 if (path) { | |
| 315 e = strlen("PATH") + strlen(path) + 2; | |
| 316 env[elen] = xmalloc(e); | |
| 317 snprintf(env[elen], e, "PATH=%s", path); | |
| 318 } else | |
| 319 env[elen] = xstrdup(DEFAULT_PATH); | |
| 320 env[++elen] = '\0'; | |
| 321 | |
| 322 pid = exec_script(argv, env); | |
| 323 if (pid == -1) | |
| 324 status = -1; | |
| 325 else if (pid != 0) { | |
| 326 /* Wait for the script to finish */ | |
| 327 while (waitpid(pid, &status, 0) == -1) { | |
| 328 if (errno != EINTR) { | |
| 329 syslog(LOG_ERR, "waitpid: %m"); | |
| 330 status = -1; | |
| 331 break; | |
| 332 } | |
| 333 } | |
| 334 } | |
| 335 | |
| 336 /* Send to our listeners */ | |
| 337 bigenv = NULL; | |
| 338 for (fd = fds; fd != NULL; fd = fd->next) { | |
| 339 if (fd->listener) { | |
| 340 if (bigenv == NULL) { | |
| 341 elen = arraytostr((const char *const *)env, | |
| 342 &bigenv); | |
| 343 iov[0].iov_base = &elen; | |
| 344 iov[0].iov_len = sizeof(ssize_t); | |
| 345 iov[1].iov_base = bigenv; | |
| 346 iov[1].iov_len = elen; | |
| 347 } | |
| 348 if (writev(fd->fd, iov, 2) == -1) | |
| 349 syslog(LOG_ERR, "writev: %m"); | |
| 350 } | |
| 351 } | |
| 352 free(bigenv); | |
| 353 | |
| 354 /* Cleanup */ | |
| 355 ep = env; | |
| 356 while (*ep) | |
| 357 free(*ep++); | |
| 358 free(env); | |
| 359 return status; | |
| 360 } | |
| 361 | |
| 362 static struct rt * | |
| 363 find_route(struct rt *rts, const struct rt *r, struct rt **lrt, | |
| 364 const struct rt *srt) | |
| 365 { | |
| 366 struct rt *rt; | |
| 367 | |
| 368 if (lrt) | |
| 369 *lrt = NULL; | |
| 370 for (rt = rts; rt; rt = rt->next) { | |
| 371 if (rt->dest.s_addr == r->dest.s_addr && | |
| 372 #if HAVE_ROUTE_METRIC | |
| 373 (srt || (!rt->iface || | |
| 374 rt->iface->metric == r->iface->metric)) && | |
| 375 #endif | |
| 376 (!srt || srt != rt) && | |
| 377 rt->net.s_addr == r->net.s_addr) | |
| 378 return rt; | |
| 379 if (lrt) | |
| 380 *lrt = rt; | |
| 381 } | |
| 382 return NULL; | |
| 383 } | |
| 384 | |
| 385 static void | |
| 386 desc_route(const char *cmd, const struct rt *rt, const char *ifname) | |
| 387 { | |
| 388 char addr[sizeof("000.000.000.000") + 1]; | |
| 389 | |
| 390 strlcpy(addr, inet_ntoa(rt->dest), sizeof(addr)); | |
| 391 if (rt->gate.s_addr == INADDR_ANY) | |
| 392 syslog(LOG_DEBUG, "%s: %s route to %s/%d", ifname, cmd, | |
| 393 addr, inet_ntocidr(rt->net)); | |
| 394 else if (rt->gate.s_addr == rt->dest.s_addr && | |
| 395 rt->net.s_addr == INADDR_BROADCAST) | |
| 396 syslog(LOG_DEBUG, "%s: %s host route to %s", ifname, cmd, | |
| 397 addr); | |
| 398 else if (rt->dest.s_addr == INADDR_ANY && rt->net.s_addr == INADDR_ANY) | |
| 399 syslog(LOG_DEBUG, "%s: %s default route via %s", ifname, cmd, | |
| 400 inet_ntoa(rt->gate)); | |
| 401 else | |
| 402 syslog(LOG_DEBUG, "%s: %s route to %s/%d via %s", ifname, cmd, | |
| 403 addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate)); | |
| 404 } | |
| 405 | |
| 406 /* If something other than dhcpcd removes a route, | |
| 407 * we need to remove it from our internal table. */ | |
| 408 int | |
| 409 route_deleted(const struct rt *rt) | |
| 410 { | |
| 411 struct rt *f, *l; | |
| 412 | |
| 413 f = find_route(routes, rt, &l, NULL); | |
| 414 if (f == NULL) | |
| 415 return 0; | |
| 416 desc_route("removing", f, f->iface->name); | |
| 417 if (l) | |
| 418 l->next = f->next; | |
| 419 else | |
| 420 routes = f->next; | |
| 421 free(f); | |
| 422 return 1; | |
| 423 } | |
| 424 | |
| 425 static int | |
| 426 n_route(struct rt *rt, const struct interface *iface) | |
| 427 { | |
| 428 /* Don't set default routes if not asked to */ | |
| 429 if (rt->dest.s_addr == 0 && | |
| 430 rt->net.s_addr == 0 && | |
| 431 !(iface->state->options->options & DHCPCD_GATEWAY)) | |
| 432 return -1; | |
| 433 | |
| 434 desc_route("adding", rt, iface->name); | |
| 435 if (!add_route(iface, &rt->dest, &rt->net, &rt->gate, iface->metric)) | |
| 436 return 0; | |
| 437 if (errno == EEXIST) { | |
| 438 /* Pretend we added the subnet route */ | |
| 439 if (rt->dest.s_addr == (iface->addr.s_addr & iface->net.s_addr)
&& | |
| 440 rt->net.s_addr == iface->net.s_addr && | |
| 441 rt->gate.s_addr == 0) | |
| 442 return 0; | |
| 443 else | |
| 444 return -1; | |
| 445 } | |
| 446 syslog(LOG_ERR, "%s: add_route: %m", iface->name); | |
| 447 return -1; | |
| 448 } | |
| 449 | |
| 450 static int | |
| 451 c_route(struct rt *ort, struct rt *nrt, const struct interface *iface) | |
| 452 { | |
| 453 /* Don't set default routes if not asked to */ | |
| 454 if (nrt->dest.s_addr == 0 && | |
| 455 nrt->net.s_addr == 0 && | |
| 456 !(iface->state->options->options & DHCPCD_GATEWAY)) | |
| 457 return -1; | |
| 458 | |
| 459 desc_route("changing", nrt, iface->name); | |
| 460 /* We delete and add the route so that we can change metric. | |
| 461 * This also has the nice side effect of flushing ARP entries so | |
| 462 * we don't have to do that manually. */ | |
| 463 del_route(ort->iface, &ort->dest, &ort->net, &ort->gate, | |
| 464 ort->iface->metric); | |
| 465 if (!add_route(iface, &nrt->dest, &nrt->net, &nrt->gate, | |
| 466 iface->metric)) | |
| 467 return 0; | |
| 468 syslog(LOG_ERR, "%s: add_route: %m", iface->name); | |
| 469 return -1; | |
| 470 } | |
| 471 | |
| 472 static int | |
| 473 d_route(struct rt *rt, const struct interface *iface, int metric) | |
| 474 { | |
| 475 int retval; | |
| 476 | |
| 477 desc_route("deleting", rt, iface->name); | |
| 478 retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric); | |
| 479 if (retval != 0 && errno != ENOENT && errno != ESRCH) | |
| 480 syslog(LOG_ERR,"%s: del_route: %m", iface->name); | |
| 481 return retval; | |
| 482 } | |
| 483 | |
| 484 static struct rt * | |
| 485 get_subnet_route(struct dhcp_message *dhcp) | |
| 486 { | |
| 487 in_addr_t addr; | |
| 488 struct in_addr net; | |
| 489 struct rt *rt; | |
| 490 | |
| 491 addr = dhcp->yiaddr; | |
| 492 if (addr == 0) | |
| 493 addr = dhcp->ciaddr; | |
| 494 /* Ensure we have all the needed values */ | |
| 495 if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1) | |
| 496 net.s_addr = get_netmask(addr); | |
| 497 if (net.s_addr == INADDR_BROADCAST || net.s_addr == INADDR_ANY) | |
| 498 return NULL; | |
| 499 rt = malloc(sizeof(*rt)); | |
| 500 rt->dest.s_addr = addr & net.s_addr; | |
| 501 rt->net.s_addr = net.s_addr; | |
| 502 rt->gate.s_addr = 0; | |
| 503 return rt; | |
| 504 } | |
| 505 | |
| 506 static struct rt * | |
| 507 add_subnet_route(struct rt *rt, const struct interface *iface) | |
| 508 { | |
| 509 struct rt *r; | |
| 510 | |
| 511 if (iface->net.s_addr == INADDR_BROADCAST || | |
| 512 iface->net.s_addr == INADDR_ANY || | |
| 513 (iface->state->options->options & | |
| 514 (DHCPCD_INFORM | DHCPCD_STATIC) && | |
| 515 iface->state->options->req_addr.s_addr == INADDR_ANY)) | |
| 516 return rt; | |
| 517 | |
| 518 r = xmalloc(sizeof(*r)); | |
| 519 r->dest.s_addr = iface->addr.s_addr & iface->net.s_addr; | |
| 520 r->net.s_addr = iface->net.s_addr; | |
| 521 r->gate.s_addr = 0; | |
| 522 r->next = rt; | |
| 523 return r; | |
| 524 } | |
| 525 | |
| 526 static struct rt * | |
| 527 get_routes(const struct interface *iface) | |
| 528 { | |
| 529 struct rt *rt, *nrt = NULL, *r = NULL; | |
| 530 | |
| 531 if (iface->state->options->routes != NULL) { | |
| 532 for (rt = iface->state->options->routes; | |
| 533 rt != NULL; | |
| 534 rt = rt->next) | |
| 535 { | |
| 536 if (rt->gate.s_addr == 0) | |
| 537 break; | |
| 538 if (r == NULL) | |
| 539 r = nrt = xmalloc(sizeof(*r)); | |
| 540 else { | |
| 541 r->next = xmalloc(sizeof(*r)); | |
| 542 r = r->next; | |
| 543 } | |
| 544 memcpy(r, rt, sizeof(*r)); | |
| 545 r->next = NULL; | |
| 546 } | |
| 547 return nrt; | |
| 548 } | |
| 549 | |
| 550 return get_option_routes(iface->state->new, | |
| 551 iface->name, &iface->state->options->options); | |
| 552 } | |
| 553 | |
| 554 static struct rt * | |
| 555 add_destination_route(struct rt *rt, const struct interface *iface) | |
| 556 { | |
| 557 struct rt *r; | |
| 558 | |
| 559 if (!(iface->flags & IFF_POINTOPOINT) || | |
| 560 !has_option_mask(iface->state->options->dstmask, DHO_ROUTER)) | |
| 561 return rt; | |
| 562 r = xmalloc(sizeof(*r)); | |
| 563 r->dest.s_addr = INADDR_ANY; | |
| 564 r->net.s_addr = INADDR_ANY; | |
| 565 r->gate.s_addr = iface->dst.s_addr; | |
| 566 r->next = rt; | |
| 567 return r; | |
| 568 } | |
| 569 | |
| 570 void | |
| 571 build_routes(void) | |
| 572 { | |
| 573 struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl, *lrt = NULL; | |
| 574 const struct interface *ifp; | |
| 575 | |
| 576 for (ifp = ifaces; ifp; ifp = ifp->next) { | |
| 577 if (ifp->state->new == NULL) | |
| 578 continue; | |
| 579 dnr = get_routes(ifp); | |
| 580 dnr = add_subnet_route(dnr, ifp); | |
| 581 dnr = add_destination_route(dnr, ifp); | |
| 582 for (rt = dnr; rt && (rtn = rt->next, 1); lrt = rt, rt = rtn) { | |
| 583 rt->iface = ifp; | |
| 584 /* Is this route already in our table? */ | |
| 585 if ((find_route(nrs, rt, NULL, NULL)) != NULL) | |
| 586 continue; | |
| 587 /* Do we already manage it? */ | |
| 588 if ((or = find_route(routes, rt, &rtl, NULL))) { | |
| 589 if (or->iface != ifp || | |
| 590 rt->gate.s_addr != or->gate.s_addr) | |
| 591 { | |
| 592 if (c_route(or, rt, ifp) != 0) | |
| 593 continue; | |
| 594 } | |
| 595 if (rtl != NULL) | |
| 596 rtl->next = or->next; | |
| 597 else | |
| 598 routes = or->next; | |
| 599 free(or); | |
| 600 } else { | |
| 601 if (n_route(rt, ifp) != 0) | |
| 602 continue; | |
| 603 } | |
| 604 if (dnr == rt) | |
| 605 dnr = rtn; | |
| 606 else if (lrt) | |
| 607 lrt->next = rtn; | |
| 608 rt->next = nrs; | |
| 609 nrs = rt; | |
| 610 } | |
| 611 free_routes(dnr); | |
| 612 } | |
| 613 | |
| 614 /* Remove old routes we used to manage */ | |
| 615 for (rt = routes; rt; rt = rt->next) { | |
| 616 if (find_route(nrs, rt, NULL, NULL) == NULL) | |
| 617 d_route(rt, rt->iface, rt->iface->metric); | |
| 618 } | |
| 619 | |
| 620 free_routes(routes); | |
| 621 routes = nrs; | |
| 622 } | |
| 623 | |
| 624 static int | |
| 625 delete_address(struct interface *iface) | |
| 626 { | |
| 627 int retval; | |
| 628 struct if_options *ifo; | |
| 629 | |
| 630 ifo = iface->state->options; | |
| 631 if (ifo->options & DHCPCD_INFORM || | |
| 632 (ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0)) | |
| 633 return 0; | |
| 634 syslog(LOG_DEBUG, "%s: deleting IP address %s/%d", | |
| 635 iface->name, | |
| 636 inet_ntoa(iface->addr), | |
| 637 inet_ntocidr(iface->net)); | |
| 638 retval = del_address(iface, &iface->addr, &iface->net); | |
| 639 if (retval == -1 && errno != EADDRNOTAVAIL) | |
| 640 syslog(LOG_ERR, "del_address: %m"); | |
| 641 iface->addr.s_addr = 0; | |
| 642 iface->net.s_addr = 0; | |
| 643 return retval; | |
| 644 } | |
| 645 | |
| 646 int | |
| 647 configure(struct interface *iface) | |
| 648 { | |
| 649 struct dhcp_message *dhcp = iface->state->new; | |
| 650 struct dhcp_lease *lease = &iface->state->lease; | |
| 651 struct if_options *ifo = iface->state->options; | |
| 652 struct rt *rt; | |
| 653 | |
| 654 /* As we are now adjusting an interface, we need to ensure | |
| 655 * we have them in the right order for routing and configuration. */ | |
| 656 sort_interfaces(); | |
| 657 | |
| 658 if (dhcp == NULL) { | |
| 659 if (!(ifo->options & DHCPCD_PERSISTENT)) { | |
| 660 build_routes(); | |
| 661 if (iface->addr.s_addr != 0) | |
| 662 delete_address(iface); | |
| 663 run_script(iface); | |
| 664 } | |
| 665 return 0; | |
| 666 } | |
| 667 | |
| 668 /* This also changes netmask */ | |
| 669 if (!(ifo->options & DHCPCD_INFORM) || | |
| 670 !has_address(iface->name, &lease->addr, &lease->net)) | |
| 671 { | |
| 672 syslog(LOG_DEBUG, "%s: adding IP address %s/%d", | |
| 673 iface->name, inet_ntoa(lease->addr), | |
| 674 inet_ntocidr(lease->net)); | |
| 675 if (add_address(iface, | |
| 676 &lease->addr, &lease->net, &lease->brd) == -1 && | |
| 677 errno != EEXIST) | |
| 678 { | |
| 679 syslog(LOG_ERR, "add_address: %m"); | |
| 680 return -1; | |
| 681 } | |
| 682 } | |
| 683 | |
| 684 /* Now delete the old address if different */ | |
| 685 if (iface->addr.s_addr != lease->addr.s_addr && | |
| 686 iface->addr.s_addr != 0) | |
| 687 delete_address(iface); | |
| 688 | |
| 689 iface->addr.s_addr = lease->addr.s_addr; | |
| 690 iface->net.s_addr = lease->net.s_addr; | |
| 691 | |
| 692 /* We need to delete the subnet route to have our metric or | |
| 693 * prefer the interface. */ | |
| 694 rt = get_subnet_route(dhcp); | |
| 695 if (rt != NULL) { | |
| 696 rt->iface = iface; | |
| 697 if (!find_route(routes, rt, NULL, NULL)) | |
| 698 del_route(iface, &rt->dest, &rt->net, &rt->gate, 0); | |
| 699 free(rt); | |
| 700 } | |
| 701 | |
| 702 build_routes(); | |
| 703 if (!iface->state->lease.frominfo && | |
| 704 !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))) | |
| 705 if (write_lease(iface, dhcp) == -1) | |
| 706 syslog(LOG_ERR, "write_lease: %m"); | |
| 707 run_script(iface); | |
| 708 return 0; | |
| 709 } | |
| OLD | NEW |