blob: fe89532ab65c33189fb0e4149cb465ab7e599de7 [file] [log] [blame]
The Android Open Source Projectf7c54212009-03-03 19:29:22 -08001/*
2 * dhcpcd - DHCP client daemon
Dmitry Shmidta3a22602012-07-23 16:45:46 -07003 * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
The Android Open Source Projectf7c54212009-03-03 19:29:22 -08004 * 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>
Dmitry Shmidte86eee12011-01-24 16:27:51 -080029#include <sys/uio.h>
The Android Open Source Projectf7c54212009-03-03 19:29:22 -080030#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>
Dmitry Shmidte86eee12011-01-24 16:27:51 -080039#include <string.h>
40#include <syslog.h>
The Android Open Source Projectf7c54212009-03-03 19:29:22 -080041#include <unistd.h>
42
43#include "config.h"
44#include "common.h"
45#include "configure.h"
46#include "dhcp.h"
Dmitry Shmidte86eee12011-01-24 16:27:51 -080047#include "if-options.h"
48#include "if-pref.h"
Dmitry Shmidta3a22602012-07-23 16:45:46 -070049#include "ipv6rs.h"
The Android Open Source Projectf7c54212009-03-03 19:29:22 -080050#include "net.h"
51#include "signals.h"
52
53#define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
54
Dmitry Shmidte86eee12011-01-24 16:27:51 -080055/* Some systems have route metrics */
56#ifndef HAVE_ROUTE_METRIC
57# ifdef __linux__
58# define HAVE_ROUTE_METRIC 1
59# endif
60# ifndef HAVE_ROUTE_METRIC
61# define HAVE_ROUTE_METRIC 0
62# endif
63#endif
64
65static struct rt *routes;
66
The Android Open Source Projectf7c54212009-03-03 19:29:22 -080067static int
68exec_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:
Dmitry Shmidte86eee12011-01-24 16:27:51 -080081 syslog(LOG_ERR, "vfork: %m");
The Android Open Source Projectf7c54212009-03-03 19:29:22 -080082 break;
83 case 0:
84 sigprocmask(SIG_SETMASK, &old, NULL);
85 execve(argv[0], argv, env);
Dmitry Shmidte86eee12011-01-24 16:27:51 -080086 syslog(LOG_ERR, "%s: %m", argv[0]);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -080087 _exit(127);
88 /* NOTREACHED */
89 }
90
91 /* Restore our signals */
92 signal_setup();
93 sigprocmask(SIG_SETMASK, &old, NULL);
94 return pid;
95}
96
Dmitry Shmidte86eee12011-01-24 16:27:51 -080097static char *
98make_var(const char *prefix, const char *var)
The Android Open Source Projectf7c54212009-03-03 19:29:22 -080099{
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800100 size_t len;
101 char *v;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800102
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800103 len = strlen(prefix) + strlen(var) + 2;
104 v = xmalloc(len);
105 snprintf(v, len, "%s_%s", prefix, var);
106 return v;
107}
108
109
110static void
111append_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
143static size_t
144arraytostr(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
165static ssize_t
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700166make_env(const struct interface *iface, const char *reason, char ***argv)
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800167{
168 char **env, *p;
169 ssize_t e, elen, l;
170 const struct if_options *ifo = iface->state->options;
171 const struct interface *ifp;
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700172 int dhcp, ra;
173
174 dhcp = ra = 0;
175 if (strcmp(reason, "ROUTERADVERT") == 0)
176 ra = 1;
177 else
178 dhcp = 1;
179
180 /* When dumping the lease, we only want to report interface and
181 reason - the other interface variables are meaningless */
182 if (options & DHCPCD_DUMPLEASE)
183 elen = 2;
184 else
185 elen = 10;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800186
187 /* Make our env */
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800188 env = xmalloc(sizeof(char *) * (elen + 1));
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800189 e = strlen("interface") + strlen(iface->name) + 2;
190 env[0] = xmalloc(e);
191 snprintf(env[0], e, "interface=%s", iface->name);
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700192 e = strlen("reason") + strlen(reason) + 2;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800193 env[1] = xmalloc(e);
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700194 snprintf(env[1], e, "reason=%s", reason);
195 if (options & DHCPCD_DUMPLEASE)
196 goto dumplease;
197
198 e = 20;
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800199 env[2] = xmalloc(e);
200 snprintf(env[2], e, "pid=%d", getpid());
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800201 env[3] = xmalloc(e);
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800202 snprintf(env[3], e, "ifmetric=%d", iface->metric);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800203 env[4] = xmalloc(e);
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800204 snprintf(env[4], e, "ifwireless=%d", iface->wireless);
205 env[5] = xmalloc(e);
206 snprintf(env[5], e, "ifflags=%u", iface->flags);
207 env[6] = xmalloc(e);
208 snprintf(env[6], e, "ifmtu=%d", get_mtu(iface->name));
209 l = e = strlen("interface_order=");
210 for (ifp = ifaces; ifp; ifp = ifp->next)
211 e += strlen(ifp->name) + 1;
212 p = env[7] = xmalloc(e);
213 strlcpy(p, "interface_order=", e);
214 e -= l;
215 p += l;
216 for (ifp = ifaces; ifp; ifp = ifp->next) {
217 l = strlcpy(p, ifp->name, e);
218 p += l;
219 e -= l;
220 *p++ = ' ';
221 e--;
222 }
223 *--p = '\0';
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700224 if ((dhcp && iface->state->new) || (ra && iface->ras)) {
225 env[8] = strdup("if_up=true");
226 env[9] = strdup("if_down=false");
227 } else {
228 env[8] = strdup("if_up=false");
229 env[9] = strdup("if_down=true");
230 }
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800231 if (*iface->state->profile) {
232 e = strlen("profile=") + strlen(iface->state->profile) + 2;
233 env[elen] = xmalloc(e);
234 snprintf(env[elen++], e, "profile=%s", iface->state->profile);
235 }
236 if (iface->wireless) {
237 e = strlen("new_ssid=") + strlen(iface->ssid) + 2;
238 if (iface->state->new != NULL ||
239 strcmp(iface->state->reason, "CARRIER") == 0)
240 {
241 env = xrealloc(env, sizeof(char *) * (elen + 2));
242 env[elen] = xmalloc(e);
243 snprintf(env[elen++], e, "new_ssid=%s", iface->ssid);
244 }
245 if (iface->state->old != NULL ||
246 strcmp(iface->state->reason, "NOCARRIER") == 0)
247 {
248 env = xrealloc(env, sizeof(char *) * (elen + 2));
249 env[elen] = xmalloc(e);
250 snprintf(env[elen++], e, "old_ssid=%s", iface->ssid);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800251 }
252 }
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700253 if (dhcp && iface->state->old) {
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800254 e = configure_env(NULL, NULL, iface->state->old, ifo);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800255 if (e > 0) {
256 env = xrealloc(env, sizeof(char *) * (elen + e + 1));
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800257 elen += configure_env(env + elen, "old",
258 iface->state->old, ifo);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800259 }
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800260 append_config(&env, &elen, "old",
261 (const char *const *)ifo->config);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800262 }
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700263
264dumplease:
265 if (dhcp && iface->state->new) {
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800266 e = configure_env(NULL, NULL, iface->state->new, ifo);
267 if (e > 0) {
268 env = xrealloc(env, sizeof(char *) * (elen + e + 1));
269 elen += configure_env(env + elen, "new",
270 iface->state->new, ifo);
271 }
272 append_config(&env, &elen, "new",
273 (const char *const *)ifo->config);
274 }
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700275 if (ra) {
276 e = ipv6rs_env(NULL, NULL, iface);
277 if (e > 0) {
278 env = xrealloc(env, sizeof(char *) * (elen + e + 1));
279 elen += ipv6rs_env(env + elen, NULL, iface);
280 }
281 }
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800282
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800283 /* Add our base environment */
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800284 if (ifo->environ) {
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800285 e = 0;
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800286 while (ifo->environ[e++])
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800287 ;
288 env = xrealloc(env, sizeof(char *) * (elen + e + 1));
289 e = 0;
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800290 while (ifo->environ[e]) {
291 env[elen + e] = xstrdup(ifo->environ[e]);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800292 e++;
293 }
294 elen += e;
295 }
296 env[elen] = '\0';
297
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800298 *argv = env;
299 return elen;
300}
301
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700302static int
303send_interface1(int fd, const struct interface *iface, const char *reason)
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800304{
305 char **env, **ep, *s;
306 ssize_t elen;
307 struct iovec iov[2];
308 int retval;
309
310 retval = 0;
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700311 make_env(iface, reason, &env);
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800312 elen = arraytostr((const char *const *)env, &s);
313 iov[0].iov_base = &elen;
314 iov[0].iov_len = sizeof(ssize_t);
315 iov[1].iov_base = s;
316 iov[1].iov_len = elen;
317 retval = writev(fd, iov, 2);
318 ep = env;
319 while (*ep)
320 free(*ep++);
321 free(env);
322 free(s);
323 return retval;
324}
325
326int
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700327send_interface(int fd, const struct interface *iface)
328{
329 int retval = 0;
330 if (send_interface1(fd, iface, iface->state->reason) == -1)
331 retval = -1;
332 if (iface->ras) {
333 if (send_interface1(fd, iface, "ROUTERADVERT") == -1)
334 retval = -1;
335 }
336 return retval;
337}
338
339int
340run_script_reason(const struct interface *iface, const char *reason)
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800341{
342 char *const argv[2] = { UNCONST(iface->state->options->script), NULL };
343 char **env = NULL, **ep;
344 char *path, *bigenv;
345 ssize_t e, elen = 0;
346 pid_t pid;
347 int status = 0;
348 const struct fd_list *fd;
349 struct iovec iov[2];
350
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700351 if (iface->state->options->script == NULL ||
352 iface->state->options->script[0] == '\0' ||
353 strcmp(iface->state->options->script, "/dev/null") == 0)
354 return 0;
355
356 if (reason == NULL)
357 reason = iface->state->reason;
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800358 syslog(LOG_DEBUG, "%s: executing `%s', reason %s",
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700359 iface->name, argv[0], reason);
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800360
361 /* Make our env */
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700362 elen = make_env(iface, reason, &env);
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800363 env = xrealloc(env, sizeof(char *) * (elen + 2));
364 /* Add path to it */
365 path = getenv("PATH");
366 if (path) {
367 e = strlen("PATH") + strlen(path) + 2;
368 env[elen] = xmalloc(e);
369 snprintf(env[elen], e, "PATH=%s", path);
370 } else
371 env[elen] = xstrdup(DEFAULT_PATH);
372 env[++elen] = '\0';
373
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800374 pid = exec_script(argv, env);
375 if (pid == -1)
376 status = -1;
377 else if (pid != 0) {
378 /* Wait for the script to finish */
379 while (waitpid(pid, &status, 0) == -1) {
380 if (errno != EINTR) {
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800381 syslog(LOG_ERR, "waitpid: %m");
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800382 status = -1;
383 break;
384 }
385 }
386 }
387
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800388 /* Send to our listeners */
389 bigenv = NULL;
390 for (fd = fds; fd != NULL; fd = fd->next) {
391 if (fd->listener) {
392 if (bigenv == NULL) {
393 elen = arraytostr((const char *const *)env,
394 &bigenv);
395 iov[0].iov_base = &elen;
396 iov[0].iov_len = sizeof(ssize_t);
397 iov[1].iov_base = bigenv;
398 iov[1].iov_len = elen;
399 }
400 if (writev(fd->fd, iov, 2) == -1)
401 syslog(LOG_ERR, "writev: %m");
402 }
403 }
404 free(bigenv);
405
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800406 /* Cleanup */
407 ep = env;
408 while (*ep)
409 free(*ep++);
410 free(env);
411 return status;
412}
413
414static struct rt *
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800415find_route(struct rt *rts, const struct rt *r, struct rt **lrt,
416 const struct rt *srt)
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800417{
418 struct rt *rt;
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800419
420 if (lrt)
421 *lrt = NULL;
422 for (rt = rts; rt; rt = rt->next) {
423 if (rt->dest.s_addr == r->dest.s_addr &&
424#if HAVE_ROUTE_METRIC
425 (srt || (!rt->iface ||
426 rt->iface->metric == r->iface->metric)) &&
427#endif
428 (!srt || srt != rt) &&
429 rt->net.s_addr == r->net.s_addr)
430 return rt;
431 if (lrt)
432 *lrt = rt;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800433 }
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800434 return NULL;
435}
436
437static void
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700438desc_route(const char *cmd, const struct rt *rt)
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800439{
440 char addr[sizeof("000.000.000.000") + 1];
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700441 const char *ifname = rt->iface->name;
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800442
443 strlcpy(addr, inet_ntoa(rt->dest), sizeof(addr));
444 if (rt->gate.s_addr == INADDR_ANY)
445 syslog(LOG_DEBUG, "%s: %s route to %s/%d", ifname, cmd,
446 addr, inet_ntocidr(rt->net));
447 else if (rt->gate.s_addr == rt->dest.s_addr &&
448 rt->net.s_addr == INADDR_BROADCAST)
449 syslog(LOG_DEBUG, "%s: %s host route to %s", ifname, cmd,
450 addr);
451 else if (rt->dest.s_addr == INADDR_ANY && rt->net.s_addr == INADDR_ANY)
452 syslog(LOG_DEBUG, "%s: %s default route via %s", ifname, cmd,
453 inet_ntoa(rt->gate));
454 else
455 syslog(LOG_DEBUG, "%s: %s route to %s/%d via %s", ifname, cmd,
456 addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
457}
458
459/* If something other than dhcpcd removes a route,
460 * we need to remove it from our internal table. */
461int
462route_deleted(const struct rt *rt)
463{
464 struct rt *f, *l;
465
466 f = find_route(routes, rt, &l, NULL);
467 if (f == NULL)
468 return 0;
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700469 desc_route("removing", f);
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800470 if (l)
471 l->next = f->next;
472 else
473 routes = f->next;
474 free(f);
475 return 1;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800476}
477
478static int
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700479n_route(struct rt *rt)
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800480{
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800481 /* Don't set default routes if not asked to */
482 if (rt->dest.s_addr == 0 &&
483 rt->net.s_addr == 0 &&
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700484 !(rt->iface->state->options->options & DHCPCD_GATEWAY))
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800485 return -1;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800486
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700487 desc_route("adding", rt);
488 if (!add_route(rt))
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800489 return 0;
490 if (errno == EEXIST) {
491 /* Pretend we added the subnet route */
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700492 if (rt->dest.s_addr ==
493 (rt->iface->addr.s_addr & rt->iface->net.s_addr) &&
494 rt->net.s_addr == rt->iface->net.s_addr &&
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800495 rt->gate.s_addr == 0)
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800496 return 0;
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800497 else
498 return -1;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800499 }
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700500 syslog(LOG_ERR, "%s: add_route: %m", rt->iface->name);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800501 return -1;
502}
503
504static int
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700505c_route(struct rt *ort, struct rt *nrt)
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800506{
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800507 /* Don't set default routes if not asked to */
508 if (nrt->dest.s_addr == 0 &&
509 nrt->net.s_addr == 0 &&
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700510 !(nrt->iface->state->options->options & DHCPCD_GATEWAY))
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800511 return -1;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800512
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700513 desc_route("changing", nrt);
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800514 /* We delete and add the route so that we can change metric.
515 * This also has the nice side effect of flushing ARP entries so
516 * we don't have to do that manually. */
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700517 del_route(ort);
518 if (!add_route(nrt))
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800519 return 0;
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700520 syslog(LOG_ERR, "%s: add_route: %m", nrt->iface->name);
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800521 return -1;
522}
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800523
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800524static int
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700525d_route(struct rt *rt)
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800526{
527 int retval;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800528
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700529 desc_route("deleting", rt);
530 retval = del_route(rt);
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800531 if (retval != 0 && errno != ENOENT && errno != ESRCH)
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700532 syslog(LOG_ERR,"%s: del_route: %m", rt->iface->name);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800533 return retval;
534}
535
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800536static struct rt *
537get_subnet_route(struct dhcp_message *dhcp)
538{
539 in_addr_t addr;
540 struct in_addr net;
541 struct rt *rt;
542
543 addr = dhcp->yiaddr;
544 if (addr == 0)
545 addr = dhcp->ciaddr;
546 /* Ensure we have all the needed values */
547 if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1)
548 net.s_addr = get_netmask(addr);
549 if (net.s_addr == INADDR_BROADCAST || net.s_addr == INADDR_ANY)
550 return NULL;
551 rt = malloc(sizeof(*rt));
552 rt->dest.s_addr = addr & net.s_addr;
553 rt->net.s_addr = net.s_addr;
554 rt->gate.s_addr = 0;
555 return rt;
556}
557
558static struct rt *
559add_subnet_route(struct rt *rt, const struct interface *iface)
560{
561 struct rt *r;
562
563 if (iface->net.s_addr == INADDR_BROADCAST ||
564 iface->net.s_addr == INADDR_ANY ||
565 (iface->state->options->options &
566 (DHCPCD_INFORM | DHCPCD_STATIC) &&
567 iface->state->options->req_addr.s_addr == INADDR_ANY))
568 return rt;
569
570 r = xmalloc(sizeof(*r));
571 r->dest.s_addr = iface->addr.s_addr & iface->net.s_addr;
572 r->net.s_addr = iface->net.s_addr;
573 r->gate.s_addr = 0;
574 r->next = rt;
575 return r;
576}
577
578static struct rt *
579get_routes(const struct interface *iface)
580{
581 struct rt *rt, *nrt = NULL, *r = NULL;
582
583 if (iface->state->options->routes != NULL) {
584 for (rt = iface->state->options->routes;
585 rt != NULL;
586 rt = rt->next)
587 {
588 if (rt->gate.s_addr == 0)
589 break;
590 if (r == NULL)
591 r = nrt = xmalloc(sizeof(*r));
592 else {
593 r->next = xmalloc(sizeof(*r));
594 r = r->next;
595 }
596 memcpy(r, rt, sizeof(*r));
597 r->next = NULL;
598 }
599 return nrt;
600 }
601
602 return get_option_routes(iface->state->new,
603 iface->name, &iface->state->options->options);
604}
605
606/* Some DHCP servers add set host routes by setting the gateway
607 * to the assinged IP address. This differs from our notion of a host route
608 * where the gateway is the destination address, so we fix it. */
609static struct rt *
610massage_host_routes(struct rt *rt, const struct interface *iface)
611{
612 struct rt *r;
613
614 for (r = rt; r; r = r->next)
615 if (r->gate.s_addr == iface->addr.s_addr &&
616 r->net.s_addr == INADDR_BROADCAST)
617 r->gate.s_addr = r->dest.s_addr;
618 return rt;
619}
620
621static struct rt *
622add_destination_route(struct rt *rt, const struct interface *iface)
623{
624 struct rt *r;
625
626 if (!(iface->flags & IFF_POINTOPOINT) ||
627 !has_option_mask(iface->state->options->dstmask, DHO_ROUTER))
628 return rt;
629 r = xmalloc(sizeof(*r));
630 r->dest.s_addr = INADDR_ANY;
631 r->net.s_addr = INADDR_ANY;
632 r->gate.s_addr = iface->dst.s_addr;
633 r->next = rt;
634 return r;
635}
636
637/* We should check to ensure the routers are on the same subnet
638 * OR supply a host route. If not, warn and add a host route. */
639static struct rt *
640add_router_host_route(struct rt *rt, const struct interface *ifp)
641{
642 struct rt *rtp, *rtl, *rtn;
643 const char *cp, *cp2, *cp3, *cplim;
644
645 for (rtp = rt, rtl = NULL; rtp; rtl = rtp, rtp = rtp->next) {
646 if (rtp->dest.s_addr != INADDR_ANY)
647 continue;
648 /* Scan for a route to match */
649 for (rtn = rt; rtn != rtp; rtn = rtn->next) {
650 /* match host */
651 if (rtn->dest.s_addr == rtp->gate.s_addr)
652 break;
653 /* match subnet */
654 cp = (const char *)&rtp->gate.s_addr;
655 cp2 = (const char *)&rtn->dest.s_addr;
656 cp3 = (const char *)&rtn->net.s_addr;
657 cplim = cp3 + sizeof(rtn->net.s_addr);
658 while (cp3 < cplim) {
659 if ((*cp++ ^ *cp2++) & *cp3++)
660 break;
661 }
662 if (cp3 == cplim)
663 break;
664 }
665 if (rtn != rtp)
666 continue;
667 if (ifp->flags & IFF_NOARP) {
668 syslog(LOG_WARNING,
669 "%s: forcing router %s through interface",
670 ifp->name, inet_ntoa(rtp->gate));
671 rtp->gate.s_addr = 0;
672 continue;
673 }
674 syslog(LOG_WARNING, "%s: router %s requires a host route",
675 ifp->name, inet_ntoa(rtp->gate));
676 rtn = xmalloc(sizeof(*rtn));
677 rtn->dest.s_addr = rtp->gate.s_addr;
678 rtn->net.s_addr = INADDR_BROADCAST;
679 rtn->gate.s_addr = rtp->gate.s_addr;
680 rtn->next = rtp;
681 if (rtl == NULL)
682 rt = rtn;
683 else
684 rtl->next = rtn;
685 }
686 return rt;
687}
688
689void
690build_routes(void)
691{
692 struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl, *lrt = NULL;
693 const struct interface *ifp;
694
repo sync0d3a47d2011-08-10 15:44:47 -0700695 if (avoid_routes) return;
696
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800697 for (ifp = ifaces; ifp; ifp = ifp->next) {
698 if (ifp->state->new == NULL)
699 continue;
700 dnr = get_routes(ifp);
701 dnr = massage_host_routes(dnr, ifp);
702 dnr = add_subnet_route(dnr, ifp);
703 dnr = add_router_host_route(dnr, ifp);
704 dnr = add_destination_route(dnr, ifp);
705 for (rt = dnr; rt && (rtn = rt->next, 1); lrt = rt, rt = rtn) {
706 rt->iface = ifp;
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700707 rt->metric = ifp->metric;
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800708 /* Is this route already in our table? */
709 if ((find_route(nrs, rt, NULL, NULL)) != NULL)
710 continue;
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700711 rt->src.s_addr = ifp->addr.s_addr;
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800712 /* Do we already manage it? */
713 if ((or = find_route(routes, rt, &rtl, NULL))) {
714 if (or->iface != ifp ||
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700715 or->src.s_addr != ifp->addr.s_addr ||
716 rt->gate.s_addr != or->gate.s_addr ||
717 rt->metric != or->metric)
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800718 {
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700719 if (c_route(or, rt) != 0)
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800720 continue;
721 }
722 if (rtl != NULL)
723 rtl->next = or->next;
724 else
725 routes = or->next;
726 free(or);
727 } else {
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700728 if (n_route(rt) != 0)
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800729 continue;
730 }
731 if (dnr == rt)
732 dnr = rtn;
733 else if (lrt)
734 lrt->next = rtn;
735 rt->next = nrs;
736 nrs = rt;
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700737 rt = lrt; /* When we loop this makes lrt correct */
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800738 }
739 free_routes(dnr);
740 }
741
742 /* Remove old routes we used to manage */
743 for (rt = routes; rt; rt = rt->next) {
744 if (find_route(nrs, rt, NULL, NULL) == NULL)
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700745 d_route(rt);
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800746 }
747
748 free_routes(routes);
749 routes = nrs;
750}
751
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800752static int
753delete_address(struct interface *iface)
754{
755 int retval;
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800756 struct if_options *ifo;
757
758 ifo = iface->state->options;
759 if (ifo->options & DHCPCD_INFORM ||
760 (ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0))
761 return 0;
762 syslog(LOG_DEBUG, "%s: deleting IP address %s/%d",
763 iface->name,
764 inet_ntoa(iface->addr),
765 inet_ntocidr(iface->net));
766 retval = del_address(iface, &iface->addr, &iface->net);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800767 if (retval == -1 && errno != EADDRNOTAVAIL)
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800768 syslog(LOG_ERR, "del_address: %m");
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800769 iface->addr.s_addr = 0;
770 iface->net.s_addr = 0;
771 return retval;
772}
773
774int
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800775configure(struct interface *iface)
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800776{
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800777 struct dhcp_message *dhcp = iface->state->new;
778 struct dhcp_lease *lease = &iface->state->lease;
779 struct if_options *ifo = iface->state->options;
780 struct rt *rt;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800781
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800782 /* As we are now adjusting an interface, we need to ensure
783 * we have them in the right order for routing and configuration. */
784 sort_interfaces();
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800785
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800786 if (dhcp == NULL) {
787 if (!(ifo->options & DHCPCD_PERSISTENT)) {
788 build_routes();
789 if (iface->addr.s_addr != 0)
790 delete_address(iface);
791 run_script(iface);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800792 }
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800793 return 0;
794 }
795
796 /* This also changes netmask */
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800797 if (!(ifo->options & DHCPCD_INFORM) ||
798 !has_address(iface->name, &lease->addr, &lease->net))
799 {
800 syslog(LOG_DEBUG, "%s: adding IP address %s/%d",
801 iface->name, inet_ntoa(lease->addr),
802 inet_ntocidr(lease->net));
803 if (add_address(iface,
804 &lease->addr, &lease->net, &lease->brd) == -1 &&
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800805 errno != EEXIST)
806 {
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800807 syslog(LOG_ERR, "add_address: %m");
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800808 return -1;
809 }
810 }
811
812 /* Now delete the old address if different */
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800813 if (iface->addr.s_addr != lease->addr.s_addr &&
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800814 iface->addr.s_addr != 0)
815 delete_address(iface);
816
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800817 iface->addr.s_addr = lease->addr.s_addr;
818 iface->net.s_addr = lease->net.s_addr;
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800819
repo sync0d3a47d2011-08-10 15:44:47 -0700820 if (!avoid_routes) {
821 /* We need to delete the subnet route to have our metric or
822 * prefer the interface. */
823 rt = get_subnet_route(dhcp);
824 if (rt != NULL) {
825 rt->iface = iface;
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700826 rt->metric = 0;
repo sync0d3a47d2011-08-10 15:44:47 -0700827 if (!find_route(routes, rt, NULL, NULL))
Dmitry Shmidta3a22602012-07-23 16:45:46 -0700828 del_route(rt);
repo sync0d3a47d2011-08-10 15:44:47 -0700829 free(rt);
830 }
831
832 build_routes();
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800833 }
834
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800835 if (!iface->state->lease.frominfo &&
836 !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800837 if (write_lease(iface, dhcp) == -1)
Dmitry Shmidte86eee12011-01-24 16:27:51 -0800838 syslog(LOG_ERR, "write_lease: %m");
839 run_script(iface);
The Android Open Source Projectf7c54212009-03-03 19:29:22 -0800840 return 0;
841}