1 /* 2 * dhcpcd - DHCP client daemon 3 * Copyright 2006-2008 Roy Marples <roy (at) 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/wait.h> 30 31 #include <netinet/in.h> 32 #include <arpa/inet.h> 33 34 #include <ctype.h> 35 #include <errno.h> 36 #include <signal.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 40 #include "config.h" 41 #include "common.h" 42 #include "configure.h" 43 #include "dhcp.h" 44 #include "dhcpcd.h" 45 #include "logger.h" 46 #include "net.h" 47 #include "signals.h" 48 49 #define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" 50 51 52 static int 53 exec_script(char *const *argv, char *const *env) 54 { 55 pid_t pid; 56 sigset_t full; 57 sigset_t old; 58 59 /* OK, we need to block signals */ 60 sigfillset(&full); 61 sigprocmask(SIG_SETMASK, &full, &old); 62 signal_reset(); 63 64 switch (pid = vfork()) { 65 case -1: 66 logger(LOG_ERR, "vfork: %s", strerror(errno)); 67 break; 68 case 0: 69 sigprocmask(SIG_SETMASK, &old, NULL); 70 execve(argv[0], argv, env); 71 logger(LOG_ERR, "%s: %s", argv[0], strerror(errno)); 72 _exit(127); 73 /* NOTREACHED */ 74 } 75 76 /* Restore our signals */ 77 signal_setup(); 78 sigprocmask(SIG_SETMASK, &old, NULL); 79 return pid; 80 } 81 82 int 83 run_script(const struct options *options, const char *iface, 84 const char *reason, 85 const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo) 86 { 87 char *const argv[2] = { UNCONST(options->script), NULL }; 88 char **env = NULL, **ep; 89 char *path; 90 ssize_t e, elen; 91 pid_t pid; 92 int status = 0; 93 94 logger(LOG_DEBUG, "executing `%s', reason %s", options->script, reason); 95 96 /* Make our env */ 97 elen = 5; 98 env = xmalloc(sizeof(char *) * (elen + 1)); 99 path = getenv("PATH"); 100 if (path) { 101 e = strlen("PATH") + strlen(path) + 2; 102 env[0] = xmalloc(e); 103 snprintf(env[0], e, "PATH=%s", path); 104 } else 105 env[0] = xstrdup(DEFAULT_PATH); 106 e = strlen("interface") + strlen(iface) + 2; 107 env[1] = xmalloc(e); 108 snprintf(env[1], e, "interface=%s", iface); 109 e = strlen("reason") + strlen(reason) + 2; 110 env[2] = xmalloc(e); 111 snprintf(env[2], e, "reason=%s", reason); 112 e = 20; 113 env[3] = xmalloc(e); 114 snprintf(env[3], e, "pid=%d", getpid()); 115 env[4] = xmalloc(e); 116 snprintf(env[4], e, "metric=%d", options->metric); 117 if (dhcpo) { 118 e = configure_env(NULL, NULL, dhcpo, options); 119 if (e > 0) { 120 env = xrealloc(env, sizeof(char *) * (elen + e + 1)); 121 elen += configure_env(env + elen, "old", dhcpo, options); 122 } 123 } 124 if (dhcpn) { 125 e = configure_env(NULL, NULL, dhcpn, options); 126 if (e > 0) { 127 env = xrealloc(env, sizeof(char *) * (elen + e + 1)); 128 elen += configure_env(env + elen, "new", dhcpn, options); 129 } 130 } 131 /* Add our base environment */ 132 if (options->environ) { 133 e = 0; 134 while (options->environ[e++]) 135 ; 136 env = xrealloc(env, sizeof(char *) * (elen + e + 1)); 137 e = 0; 138 while (options->environ[e]) { 139 env[elen + e] = xstrdup(options->environ[e]); 140 e++; 141 } 142 elen += e; 143 } 144 env[elen] = '\0'; 145 146 pid = exec_script(argv, env); 147 if (pid == -1) 148 status = -1; 149 else if (pid != 0) { 150 /* Wait for the script to finish */ 151 while (waitpid(pid, &status, 0) == -1) { 152 if (errno != EINTR) { 153 logger(LOG_ERR, "waitpid: %s", strerror(errno)); 154 status = -1; 155 break; 156 } 157 } 158 } 159 160 /* Cleanup */ 161 ep = env; 162 while (*ep) 163 free(*ep++); 164 free(env); 165 return status; 166 } 167 168 static struct rt * 169 reverse_routes(struct rt *routes) 170 { 171 struct rt *rt; 172 struct rt *rtn = NULL; 173 174 while (routes) { 175 rt = routes->next; 176 routes->next = rtn; 177 rtn = routes; 178 routes = rt; 179 } 180 return rtn; 181 } 182 183 static int 184 delete_route(const struct interface *iface, struct rt *rt, int metric) 185 { 186 char *addr; 187 int retval; 188 189 addr = xstrdup(inet_ntoa(rt->dest)); 190 logger(LOG_DEBUG, "deleting route %s/%d via %s", 191 addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate)); 192 free(addr); 193 retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric); 194 if (retval != 0 && errno != ENOENT && errno != ESRCH) 195 logger(LOG_ERR," del_route: %s", strerror(errno)); 196 return retval; 197 } 198 199 static int 200 delete_routes(struct interface *iface, int metric) 201 { 202 struct rt *rt; 203 struct rt *rtn; 204 int retval = 0; 205 206 rt = reverse_routes(iface->routes); 207 while (rt) { 208 rtn = rt->next; 209 retval += delete_route(iface, rt, metric); 210 free(rt); 211 rt = rtn; 212 } 213 iface->routes = NULL; 214 215 return retval; 216 } 217 218 static int 219 in_routes(const struct rt *routes, const struct rt *rt) 220 { 221 while (routes) { 222 if (routes->dest.s_addr == rt->dest.s_addr && 223 routes->net.s_addr == rt->net.s_addr && 224 routes->gate.s_addr == rt->gate.s_addr) 225 return 0; 226 routes = routes->next; 227 } 228 return -1; 229 } 230 231 static int 232 configure_routes(struct interface *iface, const struct dhcp_message *dhcp, 233 const struct options *options) 234 { 235 struct rt *rt, *ort; 236 struct rt *rtn = NULL, *nr = NULL; 237 int remember; 238 int retval = 0; 239 char *addr; 240 241 ort = get_option_routes(dhcp); 242 243 #ifdef IPV4LL_ALWAYSROUTE 244 if (options->options & DHCPCD_IPV4LL && 245 IN_PRIVATE(ntohl(dhcp->yiaddr))) 246 { 247 for (rt = ort; rt; rt = rt->next) { 248 /* Check if we have already got a link locale route 249 * dished out by the DHCP server */ 250 if (rt->dest.s_addr == htonl(LINKLOCAL_ADDR) && 251 rt->net.s_addr == htonl(LINKLOCAL_MASK)) 252 break; 253 rtn = rt; 254 } 255 256 if (!rt) { 257 rt = xmalloc(sizeof(*rt)); 258 rt->dest.s_addr = htonl(LINKLOCAL_ADDR); 259 rt->net.s_addr = htonl(LINKLOCAL_MASK); 260 rt->gate.s_addr = 0; 261 rt->next = NULL; 262 if (rtn) 263 rtn->next = rt; 264 else 265 ort = rt; 266 } 267 } 268 #endif 269 270 /* Now remove old routes we no longer use. 271 * We should do this in reverse order. */ 272 iface->routes = reverse_routes(iface->routes); 273 for (rt = iface->routes; rt; rt = rt->next) 274 if (in_routes(ort, rt) != 0) 275 delete_route(iface, rt, options->metric); 276 277 for (rt = ort; rt; rt = rt->next) { 278 /* Don't set default routes if not asked to */ 279 if (rt->dest.s_addr == 0 && 280 rt->net.s_addr == 0 && 281 !(options->options & DHCPCD_GATEWAY)) 282 continue; 283 284 addr = xstrdup(inet_ntoa(rt->dest)); 285 logger(LOG_DEBUG, "adding route to %s/%d via %s", 286 addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate)); 287 free(addr); 288 remember = add_route(iface, &rt->dest, 289 &rt->net, &rt->gate, 290 options->metric); 291 retval += remember; 292 293 /* If we failed to add the route, we may have already added it 294 ourselves. If so, remember it again. */ 295 if (remember < 0) { 296 if (errno != EEXIST) 297 logger(LOG_ERR, "add_route: %s", 298 strerror(errno)); 299 if (in_routes(iface->routes, rt) == 0) 300 remember = 1; 301 } 302 303 /* This login is split from above due to the #ifdef below */ 304 if (remember >= 0) { 305 if (nr) { 306 rtn->next = xmalloc(sizeof(*rtn)); 307 rtn = rtn->next; 308 } else { 309 nr = rtn = xmalloc(sizeof(*rtn)); 310 } 311 rtn->dest.s_addr = rt->dest.s_addr; 312 rtn->net.s_addr = rt->net.s_addr; 313 rtn->gate.s_addr = rt->gate.s_addr; 314 rtn->next = NULL; 315 } 316 } 317 free_routes(ort); 318 free_routes(iface->routes); 319 iface->routes = nr; 320 return retval; 321 } 322 323 static int 324 delete_address(struct interface *iface) 325 { 326 int retval; 327 logger(LOG_DEBUG, "deleting IP address %s/%d", 328 inet_ntoa(iface->addr), 329 inet_ntocidr(iface->net)); 330 retval = del_address(iface->name, &iface->addr, &iface->net); 331 if (retval == -1 && errno != EADDRNOTAVAIL) 332 logger(LOG_ERR, "del_address: %s", strerror(errno)); 333 iface->addr.s_addr = 0; 334 iface->net.s_addr = 0; 335 return retval; 336 } 337 338 int 339 configure(struct interface *iface, const char *reason, 340 const struct dhcp_message *dhcp, const struct dhcp_message *old, 341 const struct dhcp_lease *lease, const struct options *options, 342 int up) 343 { 344 struct in_addr addr; 345 struct in_addr net; 346 struct in_addr brd; 347 #ifdef __linux__ 348 struct in_addr dest; 349 struct in_addr gate; 350 #endif 351 352 /* Grab our IP config */ 353 if (dhcp == NULL) 354 up = 0; 355 else { 356 addr.s_addr = dhcp->yiaddr; 357 if (addr.s_addr == 0) 358 addr.s_addr = lease->addr.s_addr; 359 /* Ensure we have all the needed values */ 360 if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1) 361 net.s_addr = get_netmask(addr.s_addr); 362 if (get_option_addr(&brd, dhcp, DHO_BROADCAST) == -1) 363 brd.s_addr = addr.s_addr | ~net.s_addr; 364 } 365 366 /* If we aren't up, then reset the interface as much as we can */ 367 if (!up) { 368 /* Only reset things if we had set them before */ 369 if (iface->addr.s_addr != 0) { 370 delete_routes(iface, options->metric); 371 delete_address(iface); 372 } 373 374 run_script(options, iface->name, reason, NULL, old); 375 return 0; 376 } 377 378 /* This also changes netmask */ 379 if (!(options->options & DHCPCD_INFORM) || 380 !has_address(iface->name, &addr, &net)) { 381 logger(LOG_DEBUG, "adding IP address %s/%d", 382 inet_ntoa(addr), inet_ntocidr(net)); 383 if (add_address(iface->name, &addr, &net, &brd) == -1 && 384 errno != EEXIST) 385 { 386 logger(LOG_ERR, "add_address: %s", strerror(errno)); 387 return -1; 388 } 389 } 390 391 /* Now delete the old address if different */ 392 if (iface->addr.s_addr != addr.s_addr && 393 iface->addr.s_addr != 0) 394 delete_address(iface); 395 396 #ifdef __linux__ 397 /* On linux, we need to change the subnet route to have our metric. */ 398 if (iface->addr.s_addr != lease->addr.s_addr && 399 options->metric > 0 && net.s_addr != INADDR_BROADCAST) 400 { 401 dest.s_addr = addr.s_addr & net.s_addr; 402 gate.s_addr = 0; 403 add_route(iface, &dest, &net, &gate, options->metric); 404 del_route(iface, &dest, &net, &gate, 0); 405 } 406 #endif 407 408 iface->addr.s_addr = addr.s_addr; 409 iface->net.s_addr = net.s_addr; 410 configure_routes(iface, dhcp, options); 411 if (!lease->frominfo) 412 if (write_lease(iface, dhcp) == -1) 413 logger(LOG_ERR, "write_lease: %s", strerror(errno)); 414 run_script(options, iface->name, reason, dhcp, old); 415 return 0; 416 } 417