diff --git a/usr.sbin/rtsold/dump.c b/usr.sbin/rtsold/dump.c index 7915d921b277..56f8afc3b074 100644 --- a/usr.sbin/rtsold/dump.c +++ b/usr.sbin/rtsold/dump.c @@ -76,6 +76,8 @@ dump_interface_status() } fprintf(fp, " interface status: %s\n", ifinfo->active > 0 ? "active" : "inactive"); + fprintf(fp, " other config: %s\n", + ifinfo->otherconfig ? "on" : "off"); fprintf(fp, " rtsold status: %s\n", ifstatstr[ifinfo->state]); fprintf(fp, " carrier detection: %s\n", ifinfo->mediareqok ? "available" : "unavailable"); diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c index 0543bcac06e9..923628daa175 100644 --- a/usr.sbin/rtsold/rtsol.c +++ b/usr.sbin/rtsold/rtsol.c @@ -35,7 +35,10 @@ #include #include #include +#include #include +#include +#include #include #include @@ -70,6 +73,9 @@ int rssock; static struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6}; +static void call_script __P((char *, char *)); +static int safefile __P((const char *)); + int sockopen() { @@ -233,6 +239,7 @@ rtsol_input(int s) struct cmsghdr *cm; struct in6_pktinfo *pi = NULL; struct ifinfo *ifi = NULL; + struct nd_router_advert *nd_ra; u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; /* get message */ @@ -330,6 +337,23 @@ rtsol_input(int s) INET6_ADDRSTRLEN), ifi->ifname, ifi->state); + nd_ra = (struct nd_router_advert *)icp; + + /* + * Process the "O bit." + * If the value of OtherConfigFlag changes from FALSE to TRUE, the + * host should invoke the stateful autoconfiguration protocol, + * requesting information. + * [RFC 2462 Section 5.5.3] + */ + if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) && + !ifi->otherconfig) { + warnmsg(LOG_DEBUG, __func__, + "OtherConfigFlag on %s is turned on", ifi->ifname); + ifi->otherconfig = 1; + call_script(otherconf_script, ifi->ifname); + } + ifi->racnt++; switch(ifi->state) { @@ -343,3 +367,106 @@ rtsol_input(int s) break; } } + +static void +call_script(scriptpath, ifname) + char *scriptpath, *ifname; +{ + pid_t pid, wpid; + + if (scriptpath == NULL) + return; + + /* launch the script */ + pid = fork(); + if (pid < 0) { + warnmsg(LOG_ERR, __func__, + "failed to fork: %s", strerror(errno)); + return; + } else if (pid) { + int wstatus; + + do { + wpid = wait(&wstatus); + } while (wpid != pid && wpid > 0); + + if (wpid < 0) + warnmsg(LOG_ERR, __func__, + "wait: %s", strerror(errno)); + else { + warnmsg(LOG_DEBUG, __func__, + "script \"%s\" terminated", scriptpath); + } + } else { + char *argv[3]; + int fd; + + argv[0] = scriptpath; + argv[1] = ifname; + argv[2] = NULL; + + if (safefile(scriptpath)) { + warnmsg(LOG_ERR, __func__, + "script \"%s\" cannot be executed safely", + scriptpath); + exit(1); + } + + if ((fd = open("/dev/null", O_RDWR)) != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + } + + execv(scriptpath, argv); + + warnmsg(LOG_ERR, __func__, "child: exec failed: %s", + strerror(errno)); + exit(0); + } + + return; +} + +static int +safefile(path) + const char *path; +{ + struct stat s; + uid_t myuid; + + /* no setuid */ + if (getuid() != geteuid()) { + warnmsg(LOG_NOTICE, __func__, + "setuid'ed execution not allowed\n"); + return (-1); + } + + if (lstat(path, &s) != 0) { + warnmsg(LOG_NOTICE, __func__, "lstat failed: %s", + strerror(errno)); + return (-1); + } + + /* the file must be owned by the running uid */ + myuid = getuid(); + if (s.st_uid != myuid) { + warnmsg(LOG_NOTICE, __func__, + "%s has invalid owner uid\n", path); + return (-1); + } + + switch (s.st_mode & S_IFMT) { + case S_IFREG: + break; + default: + warnmsg(LOG_NOTICE, __func__, + "%s is an invalid file type 0x%o\n", + path, (s.st_mode & S_IFMT)); + return (-1); + } + + return (0); +} diff --git a/usr.sbin/rtsold/rtsold.8 b/usr.sbin/rtsold/rtsold.8 index e72bb6a24a25..a3656377287a 100644 --- a/usr.sbin/rtsold/rtsold.8 +++ b/usr.sbin/rtsold/rtsold.8 @@ -40,15 +40,19 @@ .Sh SYNOPSIS .Nm .Op Fl dDfm1 +.Op Fl O Ar script-name .Ar interface ... .Nm .Op Fl dDfm1 +.Op Fl O Ar script-name .Fl a .Nm rtsol .Op Fl dD +.Op Fl O Ar script-name .Ar interface ... .Nm rtsol .Op Fl dD +.Op Fl O Ar script-name .Fl a .\" .Sh DESCRIPTION @@ -128,6 +132,21 @@ When sending a Router Solicitation on an interface, .Nm includes a Source Link-layer address option if the interface has a link-layer address. +.Lp +.Nm +manages a per-interface parameter to detect if a separate protocol is +needed for configuration parameters other than host's addresses. +At the invocation time, the flag is FALSE, and becomes TRUE when +the daemon receives a router advertisement with the OtherConfig flag +being set. +A script file can be specified to deal with the case +.Pq see below . +When +.Nm +start resending router solicitation messages by one of the conditions +events, +the daemon resets the parameter because the event may indicate a +change on the attached link. .Pp Upon receipt of signal .Dv SIGUSR1 , @@ -178,6 +197,20 @@ Transmit Router Solicitation packets until at least one valid Router Advertisement packet has arrived on each .Ar interface , then exit. +.It Fl O Ar script-name +Specifies a supplement script file to handle the Other Configuration +flag of the router advertisement. +When the flag changes from FALSE to TRUE, +.Nm +will invoke +.Ar script-name +with a single argument of the receiving interface name, +expecting the script will then start a protocol for the other +configuration. +.Ar script-name +must be the absolute path from root to the script file, be a regular +file, and be created by the same owner who runs +.Nm . .El .Sh DIAGNOSTICS .Ex -std rtsold rtsol diff --git a/usr.sbin/rtsold/rtsold.c b/usr.sbin/rtsold/rtsold.c index e3b9a618dbee..55b1942473a3 100644 --- a/usr.sbin/rtsold/rtsold.c +++ b/usr.sbin/rtsold/rtsold.c @@ -57,6 +57,7 @@ struct ifinfo *iflist; struct timeval tm_max = {0x7fffffff, 0x7fffffff}; int aflag = 0; int dflag = 0; +char *otherconf_script; static int log_upto = 999; static int fflag = 0; @@ -126,9 +127,9 @@ main(argc, argv) if (argv0 && argv0[strlen(argv0) - 1] != 'd') { fflag = 1; once = 1; - opts = "adD"; + opts = "adDO:"; } else - opts = "adDfm1"; + opts = "adDfm1O:"; while ((ch = getopt(argc, argv, opts)) != -1) { switch (ch) { @@ -150,6 +151,9 @@ main(argc, argv) case '1': once = 1; break; + case 'O': + otherconf_script = optarg; + break; default: usage(argv0); /*NOTREACHED*/ @@ -196,6 +200,11 @@ main(argc, argv) setlogmask(LOG_UPTO(log_upto)); } + if (otherconf_script && *otherconf_script != '/') { + errx(1, "configuration script (%s) must be an absolute path", + otherconf_script); + } + #ifndef HAVE_ARC4RANDOM /* random value initilization */ srandom((u_long)time(NULL)); @@ -524,6 +533,13 @@ rtsol_check_timer() ifinfo->state = IFS_PROBE; } + /* + * If we need a probe, clear the previous + * status wrt the "other" configuration. + */ + if (probe) + ifinfo->otherconfig = 0; + if (probe && mobile_node) defrouter_probe(ifinfo->sdl->sdl_index); break; diff --git a/usr.sbin/rtsold/rtsold.h b/usr.sbin/rtsold/rtsold.h index 52175faa724c..c309b68a8e75 100644 --- a/usr.sbin/rtsold/rtsold.h +++ b/usr.sbin/rtsold/rtsold.h @@ -40,6 +40,8 @@ struct ifinfo { int probeinterval; /* interval of probe timer(if necessary) */ int probetimer; /* rest of probe timer */ int mediareqok; /* wheter the IF supports SIOCGIFMEDIA */ + int otherconfig; /* need a separate protocol for the "other" + * configuration */ int state; int probes; int dadcount; @@ -63,6 +65,7 @@ struct ifinfo { /* rtsold.c */ extern struct timeval tm_max; extern int dflag; +extern char *otherconf_script; struct ifinfo *find_ifinfo __P((int ifindex)); void rtsol_timer_update __P((struct ifinfo *ifinfo)); extern void warnmsg __P((int, const char *, const char *, ...))