Index: stable/10/etc/mtree/BSD.tests.dist =================================================================== --- stable/10/etc/mtree/BSD.tests.dist (revision 314424) +++ stable/10/etc/mtree/BSD.tests.dist (revision 314425) @@ -1,652 +1,654 @@ # $FreeBSD$ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 . bin cat .. chown .. date .. dd .. expr .. ls .. mv .. pax .. pkill .. sh builtins .. errors .. execution .. expansion .. parameters .. parser .. set-e .. .. sleep .. test .. .. cddl lib .. sbin .. usr.bin .. usr.sbin dtrace common aggs .. arithmetic .. arrays .. assocs .. begin .. bitfields .. buffering .. builtinvar .. cg .. clauses .. cpc .. decls .. drops .. dtraceUtil .. end .. enum .. error .. exit .. fbtprovider .. funcs .. grammar .. include .. inline .. io .. ip .. java_api .. json .. lexer .. llquantize .. mdb .. mib .. misc .. multiaggs .. offsetof .. operators .. pid .. plockstat .. pointers .. pragma .. predicates .. preprocessor .. print .. printa .. printf .. privs .. probes .. proc .. profile-n .. providers .. raise .. rates .. safety .. scalars .. sched .. scripting .. sdt .. sizeof .. speculation .. stability .. stack .. stackdepth .. stop .. strlen .. strtoll .. struct .. syscall .. sysevent .. tick-n .. trace .. tracemem .. translators .. typedef .. types .. uctf .. union .. usdt .. ustack .. vars .. version .. .. .. .. .. etc rc.d .. .. games .. gnu lib .. usr.bin diff .. .. .. lib atf libatf-c detail .. .. libatf-c++ detail .. .. test-programs .. .. libarchive .. libc c063 .. db .. gen execve .. posix_spawn .. .. hash data .. .. iconv .. inet .. locale .. net getaddrinfo data .. .. .. nss .. regex data .. .. resolv .. rpc .. ssp .. setjmp .. stdio .. stdlib .. string .. sys .. time .. tls dso .. .. termios .. ttyio .. .. libcrypt .. libmp .. libnv .. libpam .. librt .. libthr dlopen .. .. libutil .. msun .. .. libexec atf atf-check .. atf-sh .. .. .. sbin dhclient .. devd .. growfs .. mdconfig .. .. secure lib .. libexec .. usr.bin .. usr.sbin .. .. share examples tests atf .. plain .. .. .. .. sys acl .. aio .. fifo .. file .. fs tmpfs .. .. geom class concat .. eli .. gate .. gpt .. mirror .. nop .. raid3 .. shsec .. stripe .. uzip etalon .. .. .. .. kern acct .. execve .. pipe .. .. kqueue libkqueue .. .. mac bsdextended .. portacl .. .. mqueue .. netinet .. pjdfstest chflags .. chmod .. chown .. ftruncate .. granular .. link .. mkdir .. mkfifo .. mknod .. open .. rename .. rmdir .. symlink .. truncate .. unlink .. .. posixshm .. vfs .. vm .. .. usr.bin apply .. basename .. bmake archives fmt_44bsd .. fmt_44bsd_mod .. fmt_oldbsd .. .. basic t0 .. t1 .. t2 .. t3 .. .. execution ellipsis .. empty .. joberr .. plus .. .. shell builtin .. meta .. path .. path_select .. replace .. select .. .. suffixes basic .. src_wild1 .. src_wild2 .. .. syntax directive-t0 .. enl .. funny-targets .. semi .. .. sysmk t0 2 1 .. .. mk .. .. t1 2 1 .. .. mk .. .. t2 2 1 .. .. mk .. .. .. variables modifier_M .. modifier_t .. opt_V .. t0 .. .. .. bsdcat .. calendar .. cmp .. cpio .. col .. comm .. cut .. dirname .. file2c .. grep .. gzip .. join .. jot .. lastcomm .. limits .. m4 .. ncal .. opensm .. printf .. sed regress.multitest.out .. .. + tail + .. tar .. timeout .. tr .. truncate .. uudecode .. uuencode .. xargs .. yacc yacc .. .. .. usr.sbin etcupdate .. extattr .. fstyp .. makefs .. newsyslog .. nmtree .. pw .. rpcbind .. sa .. .. .. # vim: set expandtab ts=4 sw=4: Index: stable/10/sbin/devd/devd.cc =================================================================== --- stable/10/sbin/devd/devd.cc (revision 314424) +++ stable/10/sbin/devd/devd.cc (revision 314425) @@ -1,1254 +1,1266 @@ /*- * Copyright (c) 2002-2010 M. Warner Losh. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * my_system is a variation on lib/libc/stdlib/system.c: * * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * DEVD control daemon. */ // TODO list: // o devd.conf and devd man pages need a lot of help: // - devd needs to document the unix domain socket // - devd.conf needs more details on the supported statements. #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include "devd.h" /* C compatible definitions */ #include "devd.hh" /* C++ class definitions */ #define STREAMPIPE "/var/run/devd.pipe" #define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" #define CF "/etc/devd.conf" #define SYSCTL "hw.bus.devctl_queue" /* * Since the client socket is nonblocking, we must increase its send buffer to * handle brief event storms. On FreeBSD, AF_UNIX sockets don't have a receive * buffer, so the client can't increase the buffersize by itself. * * For example, when creating a ZFS pool, devd emits one 165 character * resource.fs.zfs.statechange message for each vdev in the pool. The kernel * allocates a 4608B mbuf for each message. Modern technology places a limit of * roughly 450 drives/rack, and it's unlikely that a zpool will ever be larger * than that. * * 450 drives * 165 bytes / drive = 74250B of data in the sockbuf * 450 drives * 4608B / drive = 2073600B of mbufs in the sockbuf * * We can't directly set the sockbuf's mbuf limit, but we can do it indirectly. * The kernel sets it to the minimum of a hard-coded maximum value and sbcc * * kern.ipc.sockbuf_waste_factor, where sbcc is the socket buffer size set by * the user. The default value of kern.ipc.sockbuf_waste_factor is 8. If we * set the bufsize to 256k and use the kern.ipc.sockbuf_waste_factor, then the * kernel will set the mbuf limit to 2MB, which is just large enough for 450 * drives. It also happens to be the same as the hardcoded maximum value. */ #define CLIENT_BUFSIZE 262144 using namespace std; typedef struct client { int fd; int socktype; } client_t; extern FILE *yyin; extern int lineno; static const char notify = '!'; static const char nomatch = '?'; static const char attach = '+'; static const char detach = '-'; static struct pidfh *pfh; static int no_daemon = 0; static int daemonize_quick = 0; static int quiet_mode = 0; static unsigned total_events = 0; static volatile sig_atomic_t got_siginfo = 0; static volatile sig_atomic_t romeo_must_die = 0; static const char *configfile = CF; static void devdlog(int priority, const char* message, ...) __printflike(2, 3); static void event_loop(void); static void usage(void); template void delete_and_clear(vector &v) { typename vector::const_iterator i; for (i = v.begin(); i != v.end(); ++i) delete *i; v.clear(); } config cfg; event_proc::event_proc() : _prio(-1) { _epsvec.reserve(4); } event_proc::~event_proc() { delete_and_clear(_epsvec); } void event_proc::add(eps *eps) { _epsvec.push_back(eps); } bool event_proc::matches(config &c) const { vector::const_iterator i; for (i = _epsvec.begin(); i != _epsvec.end(); ++i) if (!(*i)->do_match(c)) return (false); return (true); } bool event_proc::run(config &c) const { vector::const_iterator i; for (i = _epsvec.begin(); i != _epsvec.end(); ++i) if (!(*i)->do_action(c)) return (false); return (true); } action::action(const char *cmd) : _cmd(cmd) { // nothing } action::~action() { // nothing } static int my_system(const char *command) { pid_t pid, savedpid; int pstat; struct sigaction ign, intact, quitact; sigset_t newsigblock, oldsigblock; if (!command) /* just checking... */ return (1); /* * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save * existing signal dispositions. */ ign.sa_handler = SIG_IGN; ::sigemptyset(&ign.sa_mask); ign.sa_flags = 0; ::sigaction(SIGINT, &ign, &intact); ::sigaction(SIGQUIT, &ign, &quitact); ::sigemptyset(&newsigblock); ::sigaddset(&newsigblock, SIGCHLD); ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); switch (pid = ::fork()) { case -1: /* error */ break; case 0: /* child */ /* * Restore original signal dispositions and exec the command. */ ::sigaction(SIGINT, &intact, NULL); ::sigaction(SIGQUIT, &quitact, NULL); ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); /* * Close the PID file, and all other open descriptors. * Inherit std{in,out,err} only. */ cfg.close_pidfile(); ::closefrom(3); ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); ::_exit(127); default: /* parent */ savedpid = pid; do { pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); } while (pid == -1 && errno == EINTR); break; } ::sigaction(SIGINT, &intact, NULL); ::sigaction(SIGQUIT, &quitact, NULL); ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); return (pid == -1 ? -1 : pstat); } bool action::do_action(config &c) { string s = c.expand_string(_cmd.c_str()); devdlog(LOG_NOTICE, "Executing '%s'\n", s.c_str()); my_system(s.c_str()); return (true); } match::match(config &c, const char *var, const char *re) : _inv(re[0] == '!'), _var(var), _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) { regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); } match::~match() { regfree(&_regex); } bool match::do_match(config &c) { const string &value = c.get_variable(_var); bool retval; /* * This function gets called WAY too often to justify calling syslog() * each time, even at LOG_DEBUG. Because if syslogd isn't running, it * can consume excessive amounts of systime inside of connect(). Only * log when we're in -d mode. */ if (no_daemon) { devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", _var.c_str(), value.c_str(), _re.c_str(), _inv); } retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); if (_inv == 1) retval = (retval == 0) ? 1 : 0; return (retval); } #include #include #include media::media(config &, const char *var, const char *type) : _var(var), _type(-1) { static struct ifmedia_description media_types[] = { { IFM_ETHER, "Ethernet" }, { IFM_TOKEN, "Tokenring" }, { IFM_FDDI, "FDDI" }, { IFM_IEEE80211, "802.11" }, { IFM_ATM, "ATM" }, { -1, "unknown" }, { 0, NULL }, }; for (int i = 0; media_types[i].ifmt_string != NULL; ++i) if (strcasecmp(type, media_types[i].ifmt_string) == 0) { _type = media_types[i].ifmt_word; break; } } media::~media() { } bool media::do_match(config &c) { string value; struct ifmediareq ifmr; bool retval; int s; // Since we can be called from both a device attach/detach // context where device-name is defined and what we want, // as well as from a link status context, where subsystem is // the name of interest, first try device-name and fall back // to subsystem if none exists. value = c.get_variable("device-name"); if (value.empty()) value = c.get_variable("subsystem"); devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", value.c_str(), _type); retval = false; s = socket(PF_INET, SOCK_DGRAM, 0); if (s >= 0) { memset(&ifmr, 0, sizeof(ifmr)); - strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); + strlcpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && ifmr.ifm_status & IFM_AVALID) { devdlog(LOG_DEBUG, "%s has media type 0x%x\n", value.c_str(), IFM_TYPE(ifmr.ifm_active)); retval = (IFM_TYPE(ifmr.ifm_active) == _type); } else if (_type == -1) { devdlog(LOG_DEBUG, "%s has unknown media type\n", value.c_str()); retval = true; } close(s); } return (retval); } const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; const string var_list::nothing = ""; const string & var_list::get_variable(const string &var) const { map::const_iterator i; i = _vars.find(var); if (i == _vars.end()) return (var_list::bogus); return (i->second); } bool var_list::is_set(const string &var) const { return (_vars.find(var) != _vars.end()); } void var_list::set_variable(const string &var, const string &val) { /* * This function gets called WAY too often to justify calling syslog() * each time, even at LOG_DEBUG. Because if syslogd isn't running, it * can consume excessive amounts of systime inside of connect(). Only * log when we're in -d mode. */ if (no_daemon) devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); _vars[var] = val; } void config::reset(void) { _dir_list.clear(); delete_and_clear(_var_list_table); delete_and_clear(_attach_list); delete_and_clear(_detach_list); delete_and_clear(_nomatch_list); delete_and_clear(_notify_list); } void config::parse_one_file(const char *fn) { devdlog(LOG_DEBUG, "Parsing %s\n", fn); yyin = fopen(fn, "r"); if (yyin == NULL) err(1, "Cannot open config file %s", fn); lineno = 1; if (yyparse() != 0) errx(1, "Cannot parse %s at line %d", fn, lineno); fclose(yyin); } void config::parse_files_in_dir(const char *dirname) { DIR *dirp; struct dirent *dp; char path[PATH_MAX]; devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); dirp = opendir(dirname); if (dirp == NULL) return; readdir(dirp); /* Skip . */ readdir(dirp); /* Skip .. */ while ((dp = readdir(dirp)) != NULL) { if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { snprintf(path, sizeof(path), "%s/%s", dirname, dp->d_name); parse_one_file(path); } } closedir(dirp); } class epv_greater { public: int operator()(event_proc *const&l1, event_proc *const&l2) const { return (l1->get_priority() > l2->get_priority()); } }; void config::sort_vector(vector &v) { stable_sort(v.begin(), v.end(), epv_greater()); } void config::parse(void) { vector::const_iterator i; parse_one_file(configfile); for (i = _dir_list.begin(); i != _dir_list.end(); ++i) parse_files_in_dir((*i).c_str()); sort_vector(_attach_list); sort_vector(_detach_list); sort_vector(_nomatch_list); sort_vector(_notify_list); } void config::open_pidfile() { pid_t otherpid; if (_pidfile.empty()) return; pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); if (pfh == NULL) { if (errno == EEXIST) errx(1, "devd already running, pid: %d", (int)otherpid); warn("cannot open pid file"); } } void config::write_pidfile() { pidfile_write(pfh); } void config::close_pidfile() { pidfile_close(pfh); } void config::remove_pidfile() { pidfile_remove(pfh); } void config::add_attach(int prio, event_proc *p) { p->set_priority(prio); _attach_list.push_back(p); } void config::add_detach(int prio, event_proc *p) { p->set_priority(prio); _detach_list.push_back(p); } void config::add_directory(const char *dir) { _dir_list.push_back(string(dir)); } void config::add_nomatch(int prio, event_proc *p) { p->set_priority(prio); _nomatch_list.push_back(p); } void config::add_notify(int prio, event_proc *p) { p->set_priority(prio); _notify_list.push_back(p); } void config::set_pidfile(const char *fn) { _pidfile = fn; } void config::push_var_table() { var_list *vl; vl = new var_list(); _var_list_table.push_back(vl); devdlog(LOG_DEBUG, "Pushing table\n"); } void config::pop_var_table() { delete _var_list_table.back(); _var_list_table.pop_back(); devdlog(LOG_DEBUG, "Popping table\n"); } void config::set_variable(const char *var, const char *val) { _var_list_table.back()->set_variable(var, val); } const string & config::get_variable(const string &var) { vector::reverse_iterator i; for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { if ((*i)->is_set(var)) return ((*i)->get_variable(var)); } return (var_list::nothing); } bool config::is_id_char(char ch) const { return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || ch == '-')); } void config::expand_one(const char *&src, string &dst) { int count; string buffer; src++; // $$ -> $ if (*src == '$') { dst += *src++; return; } // $(foo) -> $(foo) // Not sure if I want to support this or not, so for now we just pass // it through. if (*src == '(') { dst += '$'; count = 1; /* If the string ends before ) is matched , return. */ while (count > 0 && *src) { if (*src == ')') count--; else if (*src == '(') count++; dst += *src++; } return; } // $[^A-Za-z] -> $\1 if (!isalpha(*src)) { dst += '$'; dst += *src++; return; } // $var -> replace with value do { buffer += *src++; } while (is_id_char(*src)); dst.append(get_variable(buffer)); } const string config::expand_string(const char *src, const char *prepend, const char *append) { const char *var_at; string dst; /* * 128 bytes is enough for 2427 of 2438 expansions that happen * while parsing config files, as tested on 2013-01-30. */ dst.reserve(128); if (prepend != NULL) dst = prepend; for (;;) { var_at = strchr(src, '$'); if (var_at == NULL) { dst.append(src); break; } dst.append(src, var_at - src); src = var_at; expand_one(src, dst); } if (append != NULL) dst.append(append); return (dst); } bool config::chop_var(char *&buffer, char *&lhs, char *&rhs) const { char *walker; if (*buffer == '\0') return (false); walker = lhs = buffer; while (is_id_char(*walker)) walker++; if (*walker != '=') return (false); walker++; // skip = if (*walker == '"') { walker++; // skip " rhs = walker; while (*walker && *walker != '"') walker++; if (*walker != '"') return (false); rhs[-2] = '\0'; *walker++ = '\0'; } else { rhs = walker; while (*walker && !isspace(*walker)) walker++; if (*walker != '\0') *walker++ = '\0'; rhs[-1] = '\0'; } while (isspace(*walker)) walker++; buffer = walker; return (true); } char * config::set_vars(char *buffer) { char *lhs; char *rhs; while (1) { if (!chop_var(buffer, lhs, rhs)) break; set_variable(lhs, rhs); } return (buffer); } void config::find_and_execute(char type) { vector *l; vector::const_iterator i; const char *s; switch (type) { default: return; case notify: l = &_notify_list; s = "notify"; break; case nomatch: l = &_nomatch_list; s = "nomatch"; break; case attach: l = &_attach_list; s = "attach"; break; case detach: l = &_detach_list; s = "detach"; break; } devdlog(LOG_DEBUG, "Processing %s event\n", s); for (i = l->begin(); i != l->end(); ++i) { if ((*i)->matches(*this)) { (*i)->run(*this); break; } } } static void process_event(char *buffer) { char type; char *sp; sp = buffer + 1; devdlog(LOG_INFO, "Processing event '%s'\n", buffer); type = *buffer++; cfg.push_var_table(); // No match doesn't have a device, and the format is a little // different, so handle it separately. switch (type) { case notify: sp = cfg.set_vars(sp); break; case nomatch: //? at location pnp-info on bus sp = strchr(sp, ' '); if (sp == NULL) return; /* Can't happen? */ *sp++ = '\0'; while (isspace(*sp)) sp++; if (strncmp(sp, "at ", 3) == 0) sp += 3; sp = cfg.set_vars(sp); while (isspace(*sp)) sp++; if (strncmp(sp, "on ", 3) == 0) cfg.set_variable("bus", sp + 3); break; case attach: /*FALLTHROUGH*/ case detach: sp = strchr(sp, ' '); if (sp == NULL) return; /* Can't happen? */ *sp++ = '\0'; cfg.set_variable("device-name", buffer); while (isspace(*sp)) sp++; if (strncmp(sp, "at ", 3) == 0) sp += 3; sp = cfg.set_vars(sp); while (isspace(*sp)) sp++; if (strncmp(sp, "on ", 3) == 0) cfg.set_variable("bus", sp + 3); break; } cfg.find_and_execute(type); cfg.pop_var_table(); } int create_socket(const char *name, int socktype) { int fd, slen; struct sockaddr_un sun; if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) err(1, "socket"); bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); slen = SUN_LEN(&sun); unlink(name); if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) err(1, "fcntl"); if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) err(1, "bind"); listen(fd, 4); - chown(name, 0, 0); /* XXX - root.wheel */ - chmod(name, 0666); + if (chown(name, 0, 0)) /* XXX - root.wheel */ + err(1, "chown"); + if (chmod(name, 0666)) + err(1, "chmod"); return (fd); } unsigned int max_clients = 10; /* Default, can be overridden on cmdline. */ unsigned int num_clients; list clients; void notify_clients(const char *data, int len) { list::iterator i; /* * Deliver the data to all clients. Throw clients overboard at the * first sign of trouble. This reaps clients who've died or closed * their sockets, and also clients who are alive but failing to keep up * (or who are maliciously not reading, to consume buffer space in * kernel memory or tie up the limited number of available connections). */ for (i = clients.begin(); i != clients.end(); ) { int flags; if (i->socktype == SOCK_SEQPACKET) flags = MSG_EOR; else flags = 0; if (send(i->fd, data, len, flags) != len) { --num_clients; close(i->fd); i = clients.erase(i); devdlog(LOG_WARNING, "notify_clients: send() failed; " "dropping unresponsive client\n"); } else ++i; } } void check_clients(void) { int s; struct pollfd pfd; list::iterator i; /* * Check all existing clients to see if any of them have disappeared. * Normally we reap clients when we get an error trying to send them an * event. This check eliminates the problem of an ever-growing list of * zombie clients because we're never writing to them on a system * without frequent device-change activity. */ pfd.events = 0; for (i = clients.begin(); i != clients.end(); ) { pfd.fd = i->fd; s = poll(&pfd, 1, 0); if ((s < 0 && s != EINTR ) || (s > 0 && (pfd.revents & POLLHUP))) { --num_clients; close(i->fd); i = clients.erase(i); devdlog(LOG_NOTICE, "check_clients: " "dropping disconnected client\n"); } else ++i; } } void new_client(int fd, int socktype) { client_t s; int sndbuf_size; /* * First go reap any zombie clients, then accept the connection, and * shut down the read side to stop clients from consuming kernel memory * by sending large buffers full of data we'll never read. */ check_clients(); s.socktype = socktype; s.fd = accept(fd, NULL, NULL); if (s.fd != -1) { sndbuf_size = CLIENT_BUFSIZE; if (setsockopt(s.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(sndbuf_size))) err(1, "setsockopt"); shutdown(s.fd, SHUT_RD); clients.push_back(s); ++num_clients; } else err(1, "accept"); } static void event_loop(void) { int rv; int fd; char buffer[DEVCTL_MAXBUF]; int once = 0; int stream_fd, seqpacket_fd, max_fd; int accepting; timeval tv; fd_set fds; fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); if (fd == -1) err(1, "Can't open devctl device %s", PATH_DEVCTL); stream_fd = create_socket(STREAMPIPE, SOCK_STREAM); seqpacket_fd = create_socket(SEQPACKETPIPE, SOCK_SEQPACKET); accepting = 1; max_fd = max(fd, max(stream_fd, seqpacket_fd)) + 1; while (!romeo_must_die) { if (!once && !no_daemon && !daemonize_quick) { // Check to see if we have any events pending. tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(fd, &fds); rv = select(fd + 1, &fds, &fds, &fds, &tv); // No events -> we've processed all pending events if (rv == 0) { devdlog(LOG_DEBUG, "Calling daemon\n"); cfg.remove_pidfile(); cfg.open_pidfile(); daemon(0, 0); cfg.write_pidfile(); once++; } } /* * When we've already got the max number of clients, stop * accepting new connections (don't put the listening sockets in * the set), shrink the accept() queue to reject connections * quickly, and poll the existing clients more often, so that we * notice more quickly when any of them disappear to free up * client slots. */ FD_ZERO(&fds); FD_SET(fd, &fds); if (num_clients < max_clients) { if (!accepting) { listen(stream_fd, max_clients); listen(seqpacket_fd, max_clients); accepting = 1; } FD_SET(stream_fd, &fds); FD_SET(seqpacket_fd, &fds); tv.tv_sec = 60; tv.tv_usec = 0; } else { if (accepting) { listen(stream_fd, 0); listen(seqpacket_fd, 0); accepting = 0; } tv.tv_sec = 2; tv.tv_usec = 0; } rv = select(max_fd, &fds, NULL, NULL, &tv); if (got_siginfo) { devdlog(LOG_NOTICE, "Events received so far=%u\n", total_events); got_siginfo = 0; } if (rv == -1) { if (errno == EINTR) continue; err(1, "select"); } else if (rv == 0) check_clients(); if (FD_ISSET(fd, &fds)) { rv = read(fd, buffer, sizeof(buffer) - 1); if (rv > 0) { total_events++; if (rv == sizeof(buffer) - 1) { devdlog(LOG_WARNING, "Warning: " "available event data exceeded " "buffer space\n"); } notify_clients(buffer, rv); buffer[rv] = '\0'; while (buffer[--rv] == '\n') buffer[rv] = '\0'; - process_event(buffer); + try { + process_event(buffer); + } + catch (std::length_error e) { + devdlog(LOG_ERR, "Dropping event %s " + "due to low memory", buffer); + } } else if (rv < 0) { if (errno != EINTR) break; } else { /* EOF */ break; } } if (FD_ISSET(stream_fd, &fds)) new_client(stream_fd, SOCK_STREAM); /* * Aside from the socket type, both sockets use the same * protocol, so we can process clients the same way. */ if (FD_ISSET(seqpacket_fd, &fds)) new_client(seqpacket_fd, SOCK_SEQPACKET); } + close(seqpacket_fd); + close(stream_fd); close(fd); } /* * functions that the parser uses. */ void add_attach(int prio, event_proc *p) { cfg.add_attach(prio, p); } void add_detach(int prio, event_proc *p) { cfg.add_detach(prio, p); } void add_directory(const char *dir) { cfg.add_directory(dir); free(const_cast(dir)); } void add_nomatch(int prio, event_proc *p) { cfg.add_nomatch(prio, p); } void add_notify(int prio, event_proc *p) { cfg.add_notify(prio, p); } event_proc * add_to_event_proc(event_proc *ep, eps *eps) { if (ep == NULL) ep = new event_proc(); ep->add(eps); return (ep); } eps * new_action(const char *cmd) { eps *e = new action(cmd); free(const_cast(cmd)); return (e); } eps * new_match(const char *var, const char *re) { eps *e = new match(cfg, var, re); free(const_cast(var)); free(const_cast(re)); return (e); } eps * new_media(const char *var, const char *re) { eps *e = new media(cfg, var, re); free(const_cast(var)); free(const_cast(re)); return (e); } void set_pidfile(const char *name) { cfg.set_pidfile(name); free(const_cast(name)); } void set_variable(const char *var, const char *val) { cfg.set_variable(var, val); free(const_cast(var)); free(const_cast(val)); } static void gensighand(int) { romeo_must_die = 1; } /* * SIGINFO handler. Will print useful statistics to the syslog or stderr * as appropriate */ static void siginfohand(int) { got_siginfo = 1; } /* * Local logging function. Prints to syslog if we're daemonized; syslog * otherwise. */ static void devdlog(int priority, const char* fmt, ...) { va_list argp; va_start(argp, fmt); if (no_daemon) vfprintf(stderr, fmt, argp); else if ((! quiet_mode) || (priority <= LOG_WARNING)) vsyslog(priority, fmt, argp); va_end(argp); } static void usage() { fprintf(stderr, "usage: %s [-dnq] [-l connlimit] [-f file]\n", getprogname()); exit(1); } static void check_devd_enabled() { int val = 0; size_t len; len = sizeof(val); if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) errx(1, "devctl sysctl missing from kernel!"); if (val == 0) { warnx("Setting " SYSCTL " to 1000"); val = 1000; - sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); + if (sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val))) + err(1, "sysctlbyname"); } } /* * main */ int main(int argc, char **argv) { int ch; check_devd_enabled(); while ((ch = getopt(argc, argv, "df:l:nq")) != -1) { switch (ch) { case 'd': no_daemon = 1; break; case 'f': configfile = optarg; break; case 'l': max_clients = MAX(1, strtoul(optarg, NULL, 0)); break; case 'n': daemonize_quick = 1; break; case 'q': quiet_mode = 1; break; default: usage(); } } cfg.parse(); if (!no_daemon && daemonize_quick) { cfg.open_pidfile(); daemon(0, 0); cfg.write_pidfile(); } signal(SIGPIPE, SIG_IGN); signal(SIGHUP, gensighand); signal(SIGINT, gensighand); signal(SIGTERM, gensighand); signal(SIGINFO, siginfohand); event_loop(); return (0); } Index: stable/10/usr.bin/cmp/special.c =================================================================== --- stable/10/usr.bin/cmp/special.c (revision 314424) +++ stable/10/usr.bin/cmp/special.c (revision 314425) @@ -1,104 +1,106 @@ /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static char sccsid[] = "@(#)special.c 8.3 (Berkeley) 4/2/94"; #endif #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "extern.h" void c_special(int fd1, const char *file1, off_t skip1, int fd2, const char *file2, off_t skip2) { int ch1, ch2; off_t byte, line; FILE *fp1, *fp2; int dfound; if ((fp1 = fdopen(fd1, "r")) == NULL) err(ERR_EXIT, "%s", file1); if ((fp2 = fdopen(fd2, "r")) == NULL) err(ERR_EXIT, "%s", file2); dfound = 0; while (skip1--) if (getc(fp1) == EOF) goto eof; while (skip2--) if (getc(fp2) == EOF) goto eof; for (byte = line = 1;; ++byte) { ch1 = getc(fp1); ch2 = getc(fp2); if (ch1 == EOF || ch2 == EOF) break; if (ch1 != ch2) { if (xflag) { dfound = 1; (void)printf("%08llx %02x %02x\n", (long long)byte - 1, ch1, ch2); } else if (lflag) { dfound = 1; (void)printf("%6lld %3o %3o\n", (long long)byte, ch1, ch2); } else { diffmsg(file1, file2, byte, line); /* NOTREACHED */ } } if (ch1 == '\n') ++line; } eof: if (ferror(fp1)) err(ERR_EXIT, "%s", file1); if (ferror(fp2)) err(ERR_EXIT, "%s", file2); if (feof(fp1)) { if (!feof(fp2)) eofmsg(file1); } else if (feof(fp2)) eofmsg(file2); + fclose(fp2); + fclose(fp1); if (dfound) exit(DIFF_EXIT); } Index: stable/10/usr.bin/cmp/tests/Makefile =================================================================== --- stable/10/usr.bin/cmp/tests/Makefile (revision 314424) +++ stable/10/usr.bin/cmp/tests/Makefile (revision 314425) @@ -1,9 +1,10 @@ # $FreeBSD$ .include +ATF_TESTS_SH+= cmp_test2 NETBSD_ATF_TESTS_SH= cmp_test .include .include Index: stable/10/usr.bin/cmp/tests/cmp_test2.sh =================================================================== --- stable/10/usr.bin/cmp/tests/cmp_test2.sh (nonexistent) +++ stable/10/usr.bin/cmp/tests/cmp_test2.sh (revision 314425) @@ -0,0 +1,67 @@ +# Copyright (c) 2017 Alan Somers +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# $FreeBSD$ + +atf_test_case special +special_head() { + atf_set "descr" "Test cmp(1)'s handling of non-regular files" +} +special_body() { + echo 0123456789abcdef > a + echo 0123456789abcdeg > b + cat a | atf_check -s exit:0 cmp a - + cat a | atf_check -s exit:0 cmp - a + cat b | atf_check -s not-exit:0 cmp a - + cat b | atf_check -s not-exit:0 cmp - a + true +} + +atf_test_case symlink +symlink_head() { + atf_set "descr" "Test cmp(1)'s handling of symlinks" +} +symlink_body() { + echo 0123456789abcdef > a + echo 0123456789abcdeg > b + ln -s a a.lnk + ln -s b b.lnk + ln -s a a2.lnk + cp a adup + ln -s adup adup.lnk + atf_check -s exit:0 cmp a a.lnk + atf_check -s exit:0 cmp a.lnk a + atf_check -s not-exit:0 -o ignore cmp a b.lnk + atf_check -s not-exit:0 -o ignore cmp b.lnk a + atf_check -s not-exit:0 -o ignore -e ignore cmp -h a a.lnk + atf_check -s not-exit:0 -o ignore -e ignore cmp -h a.lnk a + atf_check -s exit:0 cmp -h a.lnk a2.lnk + atf_check -s not-exit:0 -o ignore -e ignore cmp -h a.lnk adup.lnk +} + +atf_init_test_cases() +{ + atf_add_test_case special + atf_add_test_case symlink +} Property changes on: stable/10/usr.bin/cmp/tests/cmp_test2.sh ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/usr.bin/tail/Makefile =================================================================== --- stable/10/usr.bin/tail/Makefile (revision 314424) +++ stable/10/usr.bin/tail/Makefile (revision 314425) @@ -1,7 +1,13 @@ # $FreeBSD$ # @(#)Makefile 8.1 (Berkeley) 6/6/93 +.include + PROG= tail SRCS= forward.c misc.c read.c reverse.c tail.c + +.if ${MK_TESTS} != "no" +SUBDIR+= tests +.endif .include Index: stable/10/usr.bin/tail/reverse.c =================================================================== --- stable/10/usr.bin/tail/reverse.c (revision 314424) +++ stable/10/usr.bin/tail/reverse.c (revision 314425) @@ -1,291 +1,281 @@ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Edward Sze-Tyan Wang. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static char sccsid[] = "@(#)reverse.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include +#include #include #include #include #include #include #include #include #include #include #include #include "extern.h" static void r_buf(FILE *, const char *); static void r_reg(FILE *, const char *, enum STYLE, off_t, struct stat *); /* * reverse -- display input in reverse order by line. * * There are six separate cases for this -- regular and non-regular * files by bytes, lines or the whole file. * * BYTES display N bytes * REG mmap the file and display the lines * NOREG cyclically read characters into a wrap-around buffer * * LINES display N lines * REG mmap the file and display the lines * NOREG cyclically read lines into a wrap-around array of buffers * * FILE display the entire file * REG mmap the file and display the lines * NOREG cyclically read input into a linked list of buffers */ void reverse(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) { if (style != REVERSE && off == 0) return; if (S_ISREG(sbp->st_mode)) r_reg(fp, fn, style, off, sbp); else switch(style) { case FBYTES: case RBYTES: bytes(fp, fn, off); break; case FLINES: case RLINES: lines(fp, fn, off); break; case REVERSE: r_buf(fp, fn); break; default: break; } } /* * r_reg -- display a regular file in reverse order by line. */ static void r_reg(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) { struct mapinfo map; off_t curoff, size, lineend; int i; if (!(size = sbp->st_size)) return; map.start = NULL; map.mapoff = map.maxoff = size; map.fd = fileno(fp); map.maplen = 0; /* * Last char is special, ignore whether newline or not. Note that * size == 0 is dealt with above, and size == 1 sets curoff to -1. */ curoff = size - 2; lineend = size; while (curoff >= 0) { if (curoff < map.mapoff || curoff >= map.mapoff + (off_t)map.maplen) { if (maparound(&map, curoff) != 0) { ierr(fn); return; } } for (i = curoff - map.mapoff; i >= 0; i--) { if (style == RBYTES && --off == 0) break; if (map.start[i] == '\n') break; } /* `i' is either the map offset of a '\n', or -1. */ curoff = map.mapoff + i; if (i < 0) continue; /* Print the line and update offsets. */ if (mapprint(&map, curoff + 1, lineend - curoff - 1) != 0) { ierr(fn); return; } lineend = curoff + 1; curoff--; if (style == RLINES) off--; if (off == 0 && style != REVERSE) { /* Avoid printing anything below. */ curoff = 0; break; } } if (curoff < 0 && mapprint(&map, 0, lineend) != 0) { ierr(fn); return; } if (map.start != NULL && munmap(map.start, map.maplen)) ierr(fn); } -typedef struct bf { - struct bf *next; - struct bf *prev; - int len; - char *l; -} BF; +#define BSZ (128 * 1024) +typedef struct bfelem { + TAILQ_ENTRY(bfelem) entries; + size_t len; + char l[BSZ]; +} bfelem_t; /* * r_buf -- display a non-regular file in reverse order by line. * * This is the function that saves the entire input, storing the data in a * doubly linked list of buffers and then displays them in reverse order. * It has the usual nastiness of trying to find the newlines, as there's no * guarantee that a newline occurs anywhere in the file, let alone in any * particular buffer. If we run out of memory, input is discarded (and the * user warned). */ static void r_buf(FILE *fp, const char *fn) { - BF *mark, *tl, *tr; - int ch, len, llen; + struct bfelem *tl, *first = NULL; + size_t llen; char *p; - off_t enomem; + off_t enomem = 0; + TAILQ_HEAD(bfhead, bfelem) head; - tl = NULL; -#define BSZ (128 * 1024) - for (mark = NULL, enomem = 0;;) { + TAILQ_INIT(&head); + + while (!feof(fp)) { + size_t len; + /* * Allocate a new block and link it into place in a doubly * linked list. If out of memory, toss the LRU block and * keep going. */ - if (enomem || (tl = malloc(sizeof(BF))) == NULL || - (tl->l = malloc(BSZ)) == NULL) { - if (!mark) + while ((tl = malloc(sizeof(bfelem_t))) == NULL) { + first = TAILQ_FIRST(&head); + if (TAILQ_EMPTY(&head)) err(1, "malloc"); - if (enomem) - tl = tl->next; - else { - if (tl) - free(tl); - tl = mark; - } - enomem += tl->len; - } else if (mark) { - tl->next = mark; - tl->prev = mark->prev; - mark->prev->next = tl; - mark->prev = tl; - } else { - mark = tl; - mark->next = mark->prev = mark; + enomem += first->len; + TAILQ_REMOVE(&head, first, entries); + free(first); } + TAILQ_INSERT_TAIL(&head, tl, entries); /* Fill the block with input data. */ - for (p = tl->l, len = 0; - len < BSZ && (ch = getc(fp)) != EOF; ++len) - *p++ = ch; - - if (ferror(fp)) { - ierr(fn); - return; + len = 0; + while ((!feof(fp)) && len < BSZ) { + p = tl->l + len; + len += fread(p, 1, BSZ - len, fp); + if (ferror(fp)) { + ierr(fn); + return; + } } - /* - * If no input data for this block and we tossed some data, - * recover it. - */ - if (!len && enomem) { - enomem -= tl->len; - tl = tl->prev; - break; - } - tl->len = len; - if (ch == EOF) - break; } if (enomem) { warnx("warning: %jd bytes discarded", (intmax_t)enomem); rval = 1; } /* - * Step through the blocks in the reverse order read. The last char - * is special, ignore whether newline or not. + * Now print the lines in reverse order + * Outline: + * Scan backward for "\n", + * print forward to the end of the buffers + * free any buffers that start after the "\n" just found + * Loop */ - for (mark = tl;;) { - for (p = tl->l + (len = tl->len) - 1, llen = 0; len--; - --p, ++llen) - if (*p == '\n') { - if (llen) { + tl = TAILQ_LAST(&head, bfhead); + first = TAILQ_FIRST(&head); + while (tl != NULL) { + struct bfelem *temp; + + for (p = tl->l + tl->len - 1, llen = 0; p >= tl->l; + --p, ++llen) { + int start = (tl == first && p == tl->l); + + if ((*p == '\n') || start) { + struct bfelem *tr; + + if (start && llen) + WR(p, llen + 1); + else if (llen) WR(p + 1, llen); - llen = 0; + tr = TAILQ_NEXT(tl, entries); + llen = 0; + if (tr != NULL) { + TAILQ_FOREACH_FROM_SAFE(tr, &head, + entries, temp) { + if (tr->len) + WR(&tr->l, tr->len); + TAILQ_REMOVE(&head, tr, + entries); + free(tr); + } } - if (tl == mark) - continue; - for (tr = tl->next; tr->len; tr = tr->next) { - WR(tr->l, tr->len); - tr->len = 0; - if (tr == mark) - break; - } } + } tl->len = llen; - if ((tl = tl->prev) == mark) - break; + tl = TAILQ_PREV(tl, bfhead, entries); } - tl = tl->next; - if (tl->len) { - WR(tl->l, tl->len); - tl->len = 0; - } - while ((tl = tl->next)->len) { - WR(tl->l, tl->len); - tl->len = 0; - } + TAILQ_REMOVE(&head, first, entries); + free(first); } Index: stable/10/usr.bin/tail/tests/Makefile =================================================================== --- stable/10/usr.bin/tail/tests/Makefile (nonexistent) +++ stable/10/usr.bin/tail/tests/Makefile (revision 314425) @@ -0,0 +1,7 @@ +# $FreeBSD$ + +PACKAGE= tests + +ATF_TESTS_SH= tail_test + +.include Property changes on: stable/10/usr.bin/tail/tests/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/usr.bin/tail/tests/tail_test.sh =================================================================== --- stable/10/usr.bin/tail/tests/tail_test.sh (nonexistent) +++ stable/10/usr.bin/tail/tests/tail_test.sh (revision 314425) @@ -0,0 +1,233 @@ +# Copyright (c) 2016 Alan Somers +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +atf_test_case empty_r +empty_r_head() +{ + atf_set "descr" "Reverse an empty file" +} +empty_r_body() +{ + touch infile expectfile + tail -r infile > outfile + tail -r < infile > outpipe + atf_check cmp expectfile outfile + atf_check cmp expectfile outpipe +} + +atf_test_case file_r +file_r_head() +{ + atf_set "descr" "Reverse a file" +} +file_r_body() +{ + cat > infile < expectfile << HERE +This is the third line +This is the second line +This is the first line +HERE + tail -r infile > outfile + tail -r < infile > outpipe + atf_check cmp expectfile outfile + atf_check cmp expectfile outpipe +} + +atf_test_case file_rn2 +file_rn2_head() +{ + atf_set "descr" "Reverse the last two lines of a file" +} +file_rn2_body() +{ + cat > infile < expectfile << HERE +This is the third line +This is the second line +HERE + tail -rn2 infile > outfile + tail -rn2 < infile > outpipe + atf_check cmp expectfile outfile + atf_check cmp expectfile outpipe +} + +atf_test_case file_rc28 +file_rc28_head() +{ + atf_set "descr" "Reverse a file and display the last 28 characters" +} +file_rc28_body() +{ + cat > infile < expectfile << HERE +This is the third line +line +HERE + tail -rc28 infile > outfile + tail -rc28 < infile > outpipe + atf_check cmp expectfile outfile + atf_check cmp expectfile outpipe +} + +atf_test_case longfile_r +longfile_r_head() +{ + atf_set "descr" "Reverse a long file" +} +longfile_r_body() +{ + jot -w "%0511d" 1030 0 > infile + jot -w "%0511d" 1030 1029 0 -1 > expectfile + tail -r infile > outfile + tail -r < infile > outpipe + atf_check cmp expectfile outfile + atf_check cmp expectfile outpipe +} + +atf_test_case longfile_r_enomem +longfile_r_enomem_head() +{ + atf_set "descr" "Reverse a file that's too long to store in RAM" +} +longfile_r_enomem_body() +{ + # When we reverse a file that's too long for RAM, tail should drop the + # first part and just print what it can. We'll check that the last + # part is ok + { + ulimit -v 32768 || atf_skip "Can't adjust ulimit" + jot -w "%01023d" 32768 0 | tail -r > outfile ; + } + if [ "$?" -ne 1 ]; then + atf_skip "Didn't get ENOMEM. Adjust test parameters" + fi + # We don't know how much of the input we dropped. So just check that + # the first ten lines of tail's output are the same as the last ten of + # the input + jot -w "%01023d" 10 32767 0 -1 > expectfile + head -n 10 outfile > outtrunc + diff expectfile outtrunc + atf_check cmp expectfile outtrunc +} + +atf_test_case longfile_r_longlines +longfile_r_longlines_head() +{ + atf_set "descr" "Reverse a long file with extremely long lines" +} +longfile_r_longlines_body() +{ + jot -s " " -w "%07d" 18000 0 > infile + jot -s " " -w "%07d" 18000 18000 >> infile + jot -s " " -w "%07d" 18000 36000 >> infile + jot -s " " -w "%07d" 18000 36000 > expectfile + jot -s " " -w "%07d" 18000 18000 >> expectfile + jot -s " " -w "%07d" 18000 0 >> expectfile + tail -r infile > outfile + tail -r < infile > outpipe + atf_check cmp expectfile outfile + atf_check cmp expectfile outpipe +} + +atf_test_case longfile_rc135782 +longfile_rc135782_head() +{ + atf_set "descr" "Reverse a long file and print the last 135,782 bytes" +} +longfile_rc135782_body() +{ + jot -w "%063d" 9000 0 > infile + jot -w "%063d" 2121 8999 0 -1 > expectfile + echo "0000000000000000000000000000000006878" >> expectfile + tail -rc135782 infile > outfile + tail -rc135782 < infile > outpipe + atf_check cmp expectfile outfile + atf_check cmp expectfile outpipe +} + +atf_test_case longfile_rc145782_longlines +longfile_rc145782_longlines_head() +{ + atf_set "descr" "Reverse a long file with extremely long lines and print the last 145,782 bytes" +} +longfile_rc145782_longlines_body() +{ + jot -s " " -w "%07d" 18000 0 > infile + jot -s " " -w "%07d" 18000 18000 >> infile + jot -s " " -w "%07d" 18000 36000 >> infile + jot -s " " -w "%07d" 18000 36000 > expectfile + echo -n "35777 " >> expectfile + jot -s " " -w "%07d" 222 35778 >> expectfile + tail -rc145782 infile > outfile + tail -rc145782 < infile > outpipe + atf_check cmp expectfile outfile + atf_check cmp expectfile outpipe +} + +atf_test_case longfile_rn2500 +longfile_rn2500_head() +{ + atf_set "descr" "Reverse a long file and print the last 2,500 lines" +} +longfile_rn2500_body() +{ + jot -w "%063d" 9000 0 > infile + jot -w "%063d" 2500 8999 0 -1 > expectfile + tail -rn2500 infile > outfile + tail -rn2500 < infile > outpipe + atf_check cmp expectfile outfile + atf_check cmp expectfile outpipe +} + + +atf_init_test_cases() +{ + atf_add_test_case empty_r + atf_add_test_case file_r + atf_add_test_case file_rc28 + atf_add_test_case file_rn2 + # The longfile tests are designed to exercise behavior in r_buf(), + # which operates on 128KB blocks + atf_add_test_case longfile_r + atf_add_test_case longfile_r_enomem + atf_add_test_case longfile_r_longlines + atf_add_test_case longfile_rc135782 + atf_add_test_case longfile_rc145782_longlines + atf_add_test_case longfile_rn2500 +} Property changes on: stable/10/usr.bin/tail/tests/tail_test.sh ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/usr.sbin/route6d/route6d.c =================================================================== --- stable/10/usr.sbin/route6d/route6d.c (revision 314424) +++ stable/10/usr.sbin/route6d/route6d.c (revision 314425) @@ -1,3625 +1,3627 @@ /* $FreeBSD$ */ /* $KAME: route6d.c,v 1.104 2003/10/31 00:30:20 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char _rcsid[] = "$KAME: route6d.c,v 1.104 2003/10/31 00:30:20 itojun Exp $"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_POLL_H #include #endif #include #include #ifdef __STDC__ #include #else #include #endif #include #include #include #include #include #include #include "route6d.h" #define MAXFILTER 40 #define RT_DUMP_MAXRETRY 15 #ifdef DEBUG #define INIT_INTERVAL6 6 #else #define INIT_INTERVAL6 10 /* Wait to submit an initial riprequest */ #endif /* alignment constraint for routing socket */ #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) struct ifc { /* Configuration of an interface */ TAILQ_ENTRY(ifc) ifc_next; char ifc_name[IFNAMSIZ]; /* if name */ int ifc_index; /* if index */ int ifc_mtu; /* if mtu */ int ifc_metric; /* if metric */ u_int ifc_flags; /* flags */ short ifc_cflags; /* IFC_XXX */ struct in6_addr ifc_mylladdr; /* my link-local address */ struct sockaddr_in6 ifc_ripsin; /* rip multicast address */ TAILQ_HEAD(, ifac) ifc_ifac_head; /* list of AF_INET6 addrs */ TAILQ_HEAD(, iff) ifc_iff_head; /* list of filters */ int ifc_joined; /* joined to ff02::9 */ }; TAILQ_HEAD(, ifc) ifc_head = TAILQ_HEAD_INITIALIZER(ifc_head); struct ifac { /* Adddress associated to an interface */ TAILQ_ENTRY(ifac) ifac_next; struct ifc *ifac_ifc; /* back pointer */ struct in6_addr ifac_addr; /* address */ struct in6_addr ifac_raddr; /* remote address, valid in p2p */ int ifac_scope_id; /* scope id */ int ifac_plen; /* prefix length */ }; struct iff { /* Filters for an interface */ TAILQ_ENTRY(iff) iff_next; int iff_type; struct in6_addr iff_addr; int iff_plen; }; struct ifc **index2ifc; unsigned int nindex2ifc; struct ifc *loopifcp = NULL; /* pointing to loopback */ #ifdef HAVE_POLL_H struct pollfd set[2]; #else fd_set *sockvecp; /* vector to select() for receiving */ fd_set *recvecp; int fdmasks; int maxfd; /* maximum fd for select() */ #endif int rtsock; /* the routing socket */ int ripsock; /* socket to send/receive RIP datagram */ struct rip6 *ripbuf; /* packet buffer for sending */ /* * Maintain the routes in a linked list. When the number of the routes * grows, somebody would like to introduce a hash based or a radix tree * based structure. I believe the number of routes handled by RIP is * limited and I don't have to manage a complex data structure, however. * * One of the major drawbacks of the linear linked list is the difficulty * of representing the relationship between a couple of routes. This may * be a significant problem when we have to support route aggregation with * suppressing the specifics covered by the aggregate. */ struct riprt { TAILQ_ENTRY(riprt) rrt_next; /* next destination */ struct riprt *rrt_same; /* same destination - future use */ struct netinfo6 rrt_info; /* network info */ struct in6_addr rrt_gw; /* gateway */ u_long rrt_flags; /* kernel routing table flags */ u_long rrt_rflags; /* route6d routing table flags */ time_t rrt_t; /* when the route validated */ int rrt_index; /* ifindex from which this route got */ }; TAILQ_HEAD(, riprt) riprt_head = TAILQ_HEAD_INITIALIZER(riprt_head); int dflag = 0; /* debug flag */ int qflag = 0; /* quiet flag */ int nflag = 0; /* don't update kernel routing table */ int aflag = 0; /* age out even the statically defined routes */ int hflag = 0; /* don't split horizon */ int lflag = 0; /* exchange site local routes */ int Pflag = 0; /* don't age out routes with RTF_PROTO[123] */ int Qflag = RTF_PROTO2; /* set RTF_PROTO[123] flag to routes by RIPng */ int sflag = 0; /* announce static routes w/ split horizon */ int Sflag = 0; /* announce static routes to every interface */ unsigned long routetag = 0; /* route tag attached on originating case */ char *filter[MAXFILTER]; int filtertype[MAXFILTER]; int nfilter = 0; pid_t pid; struct sockaddr_storage ripsin; int interval = 1; time_t nextalarm = 0; time_t sup_trig_update = 0; FILE *rtlog = NULL; int logopened = 0; static int seq = 0; volatile sig_atomic_t seenalrm; volatile sig_atomic_t seenquit; volatile sig_atomic_t seenusr1; #define RRTF_AGGREGATE 0x08000000 #define RRTF_NOADVERTISE 0x10000000 #define RRTF_NH_NOT_LLADDR 0x20000000 #define RRTF_SENDANYWAY 0x40000000 #define RRTF_CHANGED 0x80000000 int main(int, char **); void sighandler(int); void ripalarm(void); void riprecv(void); void ripsend(struct ifc *, struct sockaddr_in6 *, int); int out_filter(struct riprt *, struct ifc *); void init(void); void sockopt(struct ifc *); void ifconfig(void); int ifconfig1(const char *, const struct sockaddr *, struct ifc *, int); void rtrecv(void); int rt_del(const struct sockaddr_in6 *, const struct sockaddr_in6 *, const struct sockaddr_in6 *); int rt_deladdr(struct ifc *, const struct sockaddr_in6 *, const struct sockaddr_in6 *); void filterconfig(void); int getifmtu(int); const char *rttypes(struct rt_msghdr *); const char *rtflags(struct rt_msghdr *); const char *ifflags(int); int ifrt(struct ifc *, int); void ifrt_p2p(struct ifc *, int); void applymask(struct in6_addr *, struct in6_addr *); void applyplen(struct in6_addr *, int); void ifrtdump(int); void ifdump(int); void ifdump0(FILE *, const struct ifc *); void ifremove(int); void rtdump(int); void rt_entry(struct rt_msghdr *, int); void rtdexit(void); void riprequest(struct ifc *, struct netinfo6 *, int, struct sockaddr_in6 *); void ripflush(struct ifc *, struct sockaddr_in6 *, int, struct netinfo6 *np); void sendrequest(struct ifc *); int sin6mask2len(const struct sockaddr_in6 *); int mask2len(const struct in6_addr *, int); int sendpacket(struct sockaddr_in6 *, int); int addroute(struct riprt *, const struct in6_addr *, struct ifc *); int delroute(struct netinfo6 *, struct in6_addr *); struct in6_addr *getroute(struct netinfo6 *, struct in6_addr *); void krtread(int); int tobeadv(struct riprt *, struct ifc *); char *allocopy(char *); char *hms(void); const char *inet6_n2p(const struct in6_addr *); struct ifac *ifa_match(const struct ifc *, const struct in6_addr *, int); struct in6_addr *plen2mask(int); struct riprt *rtsearch(struct netinfo6 *); int ripinterval(int); time_t ripsuptrig(void); void fatal(const char *, ...) __attribute__((__format__(__printf__, 1, 2))); void trace(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); void tracet(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); unsigned int if_maxindex(void); struct ifc *ifc_find(char *); struct iff *iff_find(struct ifc *, int); void setindex2ifc(int, struct ifc *); #define MALLOC(type) ((type *)malloc(sizeof(type))) #define IFIL_TYPE_ANY 0x0 #define IFIL_TYPE_A 'A' #define IFIL_TYPE_N 'N' #define IFIL_TYPE_T 'T' #define IFIL_TYPE_O 'O' #define IFIL_TYPE_L 'L' int main(int argc, char *argv[]) { int ch; int error = 0; unsigned long proto; struct ifc *ifcp; sigset_t mask, omask; const char *pidfile = ROUTE6D_PID; FILE *pidfh; char *progname; char *ep; progname = strrchr(*argv, '/'); if (progname) progname++; else progname = *argv; pid = getpid(); while ((ch = getopt(argc, argv, "A:N:O:R:T:L:t:adDhlnp:P:Q:qsS")) != -1) { switch (ch) { case 'A': case 'N': case 'O': case 'T': case 'L': if (nfilter >= MAXFILTER) { fatal("Exceeds MAXFILTER"); /*NOTREACHED*/ } filtertype[nfilter] = ch; filter[nfilter++] = allocopy(optarg); break; case 't': ep = NULL; routetag = strtoul(optarg, &ep, 0); if (!ep || *ep != '\0' || (routetag & ~0xffff) != 0) { fatal("invalid route tag"); /*NOTREACHED*/ } break; case 'p': pidfile = optarg; break; case 'P': ep = NULL; proto = strtoul(optarg, &ep, 0); if (!ep || *ep != '\0' || 3 < proto) { fatal("invalid P flag"); /*NOTREACHED*/ } if (proto == 0) Pflag = 0; if (proto == 1) Pflag |= RTF_PROTO1; if (proto == 2) Pflag |= RTF_PROTO2; if (proto == 3) Pflag |= RTF_PROTO3; break; case 'Q': ep = NULL; proto = strtoul(optarg, &ep, 0); if (!ep || *ep != '\0' || 3 < proto) { fatal("invalid Q flag"); /*NOTREACHED*/ } if (proto == 0) Qflag = 0; if (proto == 1) Qflag |= RTF_PROTO1; if (proto == 2) Qflag |= RTF_PROTO2; if (proto == 3) Qflag |= RTF_PROTO3; break; case 'R': if ((rtlog = fopen(optarg, "w")) == NULL) { fatal("Can not write to routelog"); /*NOTREACHED*/ } break; #define FLAG(c, flag, n) case c: do { flag = n; break; } while(0) FLAG('a', aflag, 1); break; FLAG('d', dflag, 1); break; FLAG('D', dflag, 2); break; FLAG('h', hflag, 1); break; FLAG('l', lflag, 1); break; FLAG('n', nflag, 1); break; FLAG('q', qflag, 1); break; FLAG('s', sflag, 1); break; FLAG('S', Sflag, 1); break; #undef FLAG default: fatal("Invalid option specified, terminating"); /*NOTREACHED*/ } } argc -= optind; argv += optind; if (argc > 0) { fatal("bogus extra arguments"); /*NOTREACHED*/ } if (geteuid()) { nflag = 1; fprintf(stderr, "No kernel update is allowed\n"); } if (dflag == 0) { if (daemon(0, 0) < 0) { fatal("daemon"); /*NOTREACHED*/ } } openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); logopened++; if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL) fatal("malloc"); memset(ripbuf, 0, RIP6_MAXMTU); ripbuf->rip6_cmd = RIP6_RESPONSE; ripbuf->rip6_vers = RIP6_VERSION; ripbuf->rip6_res1[0] = 0; ripbuf->rip6_res1[1] = 0; init(); ifconfig(); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_index < 0) { fprintf(stderr, "No ifindex found at %s " "(no link-local address?)\n", ifcp->ifc_name); error++; } } if (error) exit(1); if (loopifcp == NULL) { fatal("No loopback found"); /*NOTREACHED*/ } TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { ifrt(ifcp, 0); } filterconfig(); krtread(0); if (dflag) ifrtdump(0); pid = getpid(); if ((pidfh = fopen(pidfile, "w")) != NULL) { fprintf(pidfh, "%d\n", pid); fclose(pidfh); } if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } memset(ripbuf, 0, RIP6_MAXMTU); ripbuf->rip6_cmd = RIP6_RESPONSE; ripbuf->rip6_vers = RIP6_VERSION; ripbuf->rip6_res1[0] = 0; ripbuf->rip6_res1[1] = 0; if (signal(SIGALRM, sighandler) == SIG_ERR || signal(SIGQUIT, sighandler) == SIG_ERR || signal(SIGTERM, sighandler) == SIG_ERR || signal(SIGUSR1, sighandler) == SIG_ERR || signal(SIGHUP, sighandler) == SIG_ERR || signal(SIGINT, sighandler) == SIG_ERR) { fatal("signal"); /*NOTREACHED*/ } /* * To avoid rip packet congestion (not on a cable but in this * process), wait for a moment to send the first RIP6_RESPONSE * packets. */ alarm(ripinterval(INIT_INTERVAL6)); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (iff_find(ifcp, IFIL_TYPE_N) != NULL) continue; if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP)) sendrequest(ifcp); } syslog(LOG_INFO, "**** Started ****"); sigemptyset(&mask); sigaddset(&mask, SIGALRM); while (1) { if (seenalrm) { ripalarm(); seenalrm = 0; continue; } if (seenquit) { rtdexit(); seenquit = 0; continue; } if (seenusr1) { ifrtdump(SIGUSR1); seenusr1 = 0; continue; } #ifdef HAVE_POLL_H switch (poll(set, 2, INFTIM)) #else memcpy(recvecp, sockvecp, fdmasks); switch (select(maxfd + 1, recvecp, 0, 0, 0)) #endif { case -1: if (errno != EINTR) { fatal("select"); /*NOTREACHED*/ } continue; case 0: continue; default: #ifdef HAVE_POLL_H if (set[0].revents & POLLIN) #else if (FD_ISSET(ripsock, recvecp)) #endif { sigprocmask(SIG_BLOCK, &mask, &omask); riprecv(); sigprocmask(SIG_SETMASK, &omask, NULL); } #ifdef HAVE_POLL_H if (set[1].revents & POLLIN) #else if (FD_ISSET(rtsock, recvecp)) #endif { sigprocmask(SIG_BLOCK, &mask, &omask); rtrecv(); sigprocmask(SIG_SETMASK, &omask, NULL); } } } } void sighandler(int signo) { switch (signo) { case SIGALRM: seenalrm++; break; case SIGQUIT: case SIGTERM: seenquit++; break; case SIGUSR1: case SIGHUP: case SIGINT: seenusr1++; break; } } /* * gracefully exits after resetting sockopts. */ /* ARGSUSED */ void rtdexit(void) { struct riprt *rrt; alarm(0); TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_rflags & RRTF_AGGREGATE) { delroute(&rrt->rrt_info, &rrt->rrt_gw); } } close(ripsock); close(rtsock); syslog(LOG_INFO, "**** Terminated ****"); closelog(); exit(1); } /* * Called periodically: * 1. age out the learned route. remove it if necessary. * 2. submit RIP6_RESPONSE packets. * Invoked in every SUPPLY_INTERVAL6 (30) seconds. I believe we don't have * to invoke this function in every 1 or 5 or 10 seconds only to age the * routes more precisely. */ /* ARGSUSED */ void ripalarm(void) { struct ifc *ifcp; struct riprt *rrt, *rrt_tmp; time_t t_lifetime, t_holddown; /* age the RIP routes */ t_lifetime = time(NULL) - RIP_LIFETIME; t_holddown = t_lifetime - RIP_HOLDDOWN; TAILQ_FOREACH_SAFE(rrt, &riprt_head, rrt_next, rrt_tmp) { if (rrt->rrt_t == 0) continue; else if (rrt->rrt_t < t_holddown) { TAILQ_REMOVE(&riprt_head, rrt, rrt_next); delroute(&rrt->rrt_info, &rrt->rrt_gw); free(rrt); } else if (rrt->rrt_t < t_lifetime) rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; } /* Supply updates */ TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP)) ripsend(ifcp, &ifcp->ifc_ripsin, 0); } alarm(ripinterval(SUPPLY_INTERVAL6)); } void init(void) { int error; const int int0 = 0, int1 = 1, int255 = 255; struct addrinfo hints, *res; char port[NI_MAXSERV]; TAILQ_INIT(&ifc_head); nindex2ifc = 0; /*initial guess*/ index2ifc = NULL; snprintf(port, sizeof(port), "%u", RIP6_PORT); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_PASSIVE; error = getaddrinfo(NULL, port, &hints, &res); if (error) { fatal("%s", gai_strerror(error)); /*NOTREACHED*/ } if (res->ai_next) { fatal(":: resolved to multiple address"); /*NOTREACHED*/ } ripsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (ripsock < 0) { fatal("rip socket"); /*NOTREACHED*/ } #ifdef IPV6_V6ONLY if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_V6ONLY, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_V6ONLY"); /*NOTREACHED*/ } #endif if (bind(ripsock, res->ai_addr, res->ai_addrlen) < 0) { fatal("rip bind"); /*NOTREACHED*/ } if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &int255, sizeof(int255)) < 0) { fatal("rip IPV6_MULTICAST_HOPS"); /*NOTREACHED*/ } if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &int0, sizeof(int0)) < 0) { fatal("rip IPV6_MULTICAST_LOOP"); /*NOTREACHED*/ } #ifdef IPV6_RECVPKTINFO if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_RECVPKTINFO"); /*NOTREACHED*/ } #else /* old adv. API */ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_PKTINFO, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_PKTINFO"); /*NOTREACHED*/ } #endif #ifdef IPV6_RECVPKTINFO if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_RECVHOPLIMIT"); /*NOTREACHED*/ } #else /* old adv. API */ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_HOPLIMIT, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_HOPLIMIT"); /*NOTREACHED*/ } #endif memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; error = getaddrinfo(RIP6_DEST, port, &hints, &res); if (error) { fatal("%s", gai_strerror(error)); /*NOTREACHED*/ } if (res->ai_next) { fatal("%s resolved to multiple address", RIP6_DEST); /*NOTREACHED*/ } memcpy(&ripsin, res->ai_addr, res->ai_addrlen); #ifdef HAVE_POLL_H set[0].fd = ripsock; set[0].events = POLLIN; #else maxfd = ripsock; #endif if (nflag == 0) { if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { fatal("route socket"); /*NOTREACHED*/ } #ifdef HAVE_POLL_H set[1].fd = rtsock; set[1].events = POLLIN; #else if (rtsock > maxfd) maxfd = rtsock; #endif } else { #ifdef HAVE_POLL_H set[1].fd = -1; #else rtsock = -1; /*just for safety */ #endif } #ifndef HAVE_POLL_H fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask); if ((sockvecp = malloc(fdmasks)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } if ((recvecp = malloc(fdmasks)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } memset(sockvecp, 0, fdmasks); FD_SET(ripsock, sockvecp); if (rtsock >= 0) FD_SET(rtsock, sockvecp); #endif } #define RIPSIZE(n) \ (sizeof(struct rip6) + ((n)-1) * sizeof(struct netinfo6)) /* * ripflush flushes the rip datagram stored in the rip buffer */ void ripflush(struct ifc *ifcp, struct sockaddr_in6 *sin6, int nrt, struct netinfo6 *np) { int i; int error; if (ifcp) tracet(1, "Send(%s): info(%d) to %s.%d\n", ifcp->ifc_name, nrt, inet6_n2p(&sin6->sin6_addr), ntohs(sin6->sin6_port)); else tracet(1, "Send: info(%d) to %s.%d\n", nrt, inet6_n2p(&sin6->sin6_addr), ntohs(sin6->sin6_port)); if (dflag >= 2) { np = ripbuf->rip6_nets; for (i = 0; i < nrt; i++, np++) { if (np->rip6_metric == NEXTHOP_METRIC) { if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest)) trace(2, " NextHop reset"); else { trace(2, " NextHop %s", inet6_n2p(&np->rip6_dest)); } } else { trace(2, " %s/%d[%d]", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); } if (np->rip6_tag) { trace(2, " tag=0x%04x", ntohs(np->rip6_tag) & 0xffff); } trace(2, "\n"); } } error = sendpacket(sin6, RIPSIZE(nrt)); if (error == EAFNOSUPPORT) { /* Protocol not supported */ tracet(1, "Could not send info to %s (%s): " "set IFF_UP to 0\n", ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr)); ifcp->ifc_flags &= ~IFF_UP; /* As if down for AF_INET6 */ } } /* * Generate RIP6_RESPONSE packets and send them. */ void ripsend(struct ifc *ifcp, struct sockaddr_in6 *sin6, int flag) { struct riprt *rrt; struct in6_addr *nh; /* next hop */ struct netinfo6 *np; int maxrte; int nrt; if (qflag) return; if (ifcp == NULL) { /* * Request from non-link local address is not * a regular route6d update. */ maxrte = (IFMINMTU - sizeof(struct ip6_hdr) - sizeof(struct udphdr) - sizeof(struct rip6) + sizeof(struct netinfo6)) / sizeof(struct netinfo6); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_rflags & RRTF_NOADVERTISE) continue; /* Put the route to the buffer */ *np = rrt->rrt_info; np++; nrt++; if (nrt == maxrte) { ripflush(NULL, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } } if (nrt) /* Send last packet */ ripflush(NULL, sin6, nrt, np); return; } if ((flag & RRTF_SENDANYWAY) == 0 && (qflag || (ifcp->ifc_flags & IFF_LOOPBACK))) return; /* -N: no use */ if (iff_find(ifcp, IFIL_TYPE_N) != NULL) return; /* -T: generate default route only */ if (iff_find(ifcp, IFIL_TYPE_T) != NULL) { struct netinfo6 rrt_info; memset(&rrt_info, 0, sizeof(struct netinfo6)); rrt_info.rip6_dest = in6addr_any; rrt_info.rip6_plen = 0; rrt_info.rip6_metric = 1; rrt_info.rip6_metric += ifcp->ifc_metric; rrt_info.rip6_tag = htons(routetag & 0xffff); np = ripbuf->rip6_nets; *np = rrt_info; nrt = 1; ripflush(ifcp, sin6, nrt, np); return; } maxrte = (ifcp->ifc_mtu - sizeof(struct ip6_hdr) - sizeof(struct udphdr) - sizeof(struct rip6) + sizeof(struct netinfo6)) / sizeof(struct netinfo6); nrt = 0; np = ripbuf->rip6_nets; nh = NULL; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_rflags & RRTF_NOADVERTISE) continue; /* Need to check filter here */ if (out_filter(rrt, ifcp) == 0) continue; /* Check split horizon and other conditions */ if (tobeadv(rrt, ifcp) == 0) continue; /* Only considers the routes with flag if specified */ if ((flag & RRTF_CHANGED) && (rrt->rrt_rflags & RRTF_CHANGED) == 0) continue; /* Check nexthop */ if (rrt->rrt_index == ifcp->ifc_index && !IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_gw) && (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR) == 0) { if (nh == NULL || !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw)) { if (nrt == maxrte - 2) { ripflush(ifcp, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } np->rip6_dest = rrt->rrt_gw; np->rip6_plen = 0; np->rip6_tag = 0; np->rip6_metric = NEXTHOP_METRIC; nh = &rrt->rrt_gw; np++; nrt++; } } else if (nh && (rrt->rrt_index != ifcp->ifc_index || !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw) || rrt->rrt_rflags & RRTF_NH_NOT_LLADDR)) { /* Reset nexthop */ if (nrt == maxrte - 2) { ripflush(ifcp, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } memset(np, 0, sizeof(struct netinfo6)); np->rip6_metric = NEXTHOP_METRIC; nh = NULL; np++; nrt++; } /* Put the route to the buffer */ *np = rrt->rrt_info; np++; nrt++; if (nrt == maxrte) { ripflush(ifcp, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } } if (nrt) /* Send last packet */ ripflush(ifcp, sin6, nrt, np); } /* * outbound filter logic, per-route/interface. */ int out_filter(struct riprt *rrt, struct ifc *ifcp) { struct iff *iffp; struct in6_addr ia; int ok; /* * -A: filter out less specific routes, if we have aggregated * route configured. */ TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != 'A') continue; if (rrt->rrt_info.rip6_plen <= iffp->iff_plen) continue; ia = rrt->rrt_info.rip6_dest; applyplen(&ia, iffp->iff_plen); if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) return 0; } /* * if it is an aggregated route, advertise it only to the * interfaces specified on -A. */ if ((rrt->rrt_rflags & RRTF_AGGREGATE) != 0) { ok = 0; TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != 'A') continue; if (rrt->rrt_info.rip6_plen == iffp->iff_plen && IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest, &iffp->iff_addr)) { ok = 1; break; } } if (!ok) return 0; } /* * -O: advertise only if prefix matches the configured prefix. */ if (iff_find(ifcp, IFIL_TYPE_O) != NULL) { ok = 0; TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != 'O') continue; if (rrt->rrt_info.rip6_plen < iffp->iff_plen) continue; ia = rrt->rrt_info.rip6_dest; applyplen(&ia, iffp->iff_plen); if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) { ok = 1; break; } } if (!ok) return 0; } /* the prefix should be advertised */ return 1; } /* * Determine if the route is to be advertised on the specified interface. * It checks options specified in the arguments and the split horizon rule. */ int tobeadv(struct riprt *rrt, struct ifc *ifcp) { /* Special care for static routes */ if (rrt->rrt_flags & RTF_STATIC) { /* XXX don't advertise reject/blackhole routes */ if (rrt->rrt_flags & (RTF_REJECT | RTF_BLACKHOLE)) return 0; if (Sflag) /* Yes, advertise it anyway */ return 1; if (sflag && rrt->rrt_index != ifcp->ifc_index) return 1; return 0; } /* Regular split horizon */ if (hflag == 0 && rrt->rrt_index == ifcp->ifc_index) return 0; return 1; } /* * Send a rip packet actually. */ int sendpacket(struct sockaddr_in6 *sin6, int len) { struct msghdr m; struct cmsghdr *cm; struct iovec iov[2]; u_char cmsgbuf[256]; struct in6_pktinfo *pi; int idx; struct sockaddr_in6 sincopy; /* do not overwrite the given sin */ sincopy = *sin6; sin6 = &sincopy; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) idx = sin6->sin6_scope_id; else idx = 0; m.msg_name = (caddr_t)sin6; m.msg_namelen = sizeof(*sin6); iov[0].iov_base = (caddr_t)ripbuf; iov[0].iov_len = len; m.msg_iov = iov; m.msg_iovlen = 1; + m.msg_flags = 0; if (!idx) { m.msg_control = NULL; m.msg_controllen = 0; } else { memset(cmsgbuf, 0, sizeof(cmsgbuf)); cm = (struct cmsghdr *)cmsgbuf; m.msg_control = (caddr_t)cm; m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_PKTINFO; pi = (struct in6_pktinfo *)CMSG_DATA(cm); memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*::*/ pi->ipi6_ifindex = idx; } if (sendmsg(ripsock, &m, 0 /*MSG_DONTROUTE*/) < 0) { trace(1, "sendmsg: %s\n", strerror(errno)); return errno; } return 0; } /* * Receive and process RIP packets. Update the routes/kernel forwarding * table if necessary. */ void riprecv(void) { struct ifc *ifcp, *ic; struct sockaddr_in6 fsock; struct in6_addr nh; /* next hop */ struct rip6 *rp; struct netinfo6 *np, *nq; struct riprt *rrt; ssize_t len, nn; unsigned int need_trigger, idx; char buf[4 * RIP6_MAXMTU]; time_t t; struct msghdr m; struct cmsghdr *cm; struct iovec iov[2]; u_char cmsgbuf[256]; struct in6_pktinfo *pi = NULL; int *hlimp = NULL; struct iff *iffp; struct in6_addr ia; int ok; time_t t_half_lifetime; need_trigger = 0; m.msg_name = (caddr_t)&fsock; m.msg_namelen = sizeof(fsock); iov[0].iov_base = (caddr_t)buf; iov[0].iov_len = sizeof(buf); m.msg_iov = iov; m.msg_iovlen = 1; cm = (struct cmsghdr *)cmsgbuf; m.msg_control = (caddr_t)cm; m.msg_controllen = sizeof(cmsgbuf); + m.msg_flags = 0; if ((len = recvmsg(ripsock, &m, 0)) < 0) { fatal("recvmsg"); /*NOTREACHED*/ } idx = 0; for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m); cm; cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) { if (cm->cmsg_level != IPPROTO_IPV6) continue; switch (cm->cmsg_type) { case IPV6_PKTINFO: if (cm->cmsg_len != CMSG_LEN(sizeof(*pi))) { trace(1, "invalid cmsg length for IPV6_PKTINFO\n"); return; } pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); idx = pi->ipi6_ifindex; break; case IPV6_HOPLIMIT: if (cm->cmsg_len != CMSG_LEN(sizeof(int))) { trace(1, "invalid cmsg length for IPV6_HOPLIMIT\n"); return; } hlimp = (int *)CMSG_DATA(cm); break; } } if ((size_t)len < sizeof(struct rip6)) { trace(1, "Packet too short\n"); return; } if (pi == NULL || hlimp == NULL) { /* * This can happen when the kernel failed to allocate memory * for the ancillary data. Although we might be able to handle * some cases without this info, those are minor and not so * important, so it's better to discard the packet for safer * operation. */ trace(1, "IPv6 packet information cannot be retrieved\n"); return; } nh = fsock.sin6_addr; nn = (len - sizeof(struct rip6) + sizeof(struct netinfo6)) / sizeof(struct netinfo6); rp = (struct rip6 *)buf; np = rp->rip6_nets; if (rp->rip6_vers != RIP6_VERSION) { trace(1, "Incorrect RIP version %d\n", rp->rip6_vers); return; } if (rp->rip6_cmd == RIP6_REQUEST) { if (idx && idx < nindex2ifc) { ifcp = index2ifc[idx]; riprequest(ifcp, np, nn, &fsock); } else { riprequest(NULL, np, nn, &fsock); } return; } if (!IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr)) { trace(1, "Response from non-ll addr: %s\n", inet6_n2p(&fsock.sin6_addr)); return; /* Ignore packets from non-link-local addr */ } if (ntohs(fsock.sin6_port) != RIP6_PORT) { trace(1, "Response from non-rip port from %s\n", inet6_n2p(&fsock.sin6_addr)); return; } if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && *hlimp != 255) { trace(1, "Response packet with a smaller hop limit (%d) from %s\n", *hlimp, inet6_n2p(&fsock.sin6_addr)); return; } /* * Further validation: since this program does not send off-link * requests, an incoming response must always come from an on-link * node. Although this is normally ensured by the source address * check above, it may not 100% be safe because there are router * implementations that (invalidly) allow a packet with a link-local * source address to be forwarded to a different link. * So we also check whether the destination address is a link-local * address or the hop limit is 255. Note that RFC2080 does not require * the specific hop limit for a unicast response, so we cannot assume * the limitation. */ if (!IN6_IS_ADDR_LINKLOCAL(&pi->ipi6_addr) && *hlimp != 255) { trace(1, "Response packet possibly from an off-link node: " "from %s to %s hlim=%d\n", inet6_n2p(&fsock.sin6_addr), inet6_n2p(&pi->ipi6_addr), *hlimp); return; } idx = fsock.sin6_scope_id; ifcp = (idx < nindex2ifc) ? index2ifc[idx] : NULL; if (!ifcp) { trace(1, "Packets to unknown interface index %d\n", idx); return; /* Ignore it */ } if (IN6_ARE_ADDR_EQUAL(&ifcp->ifc_mylladdr, &fsock.sin6_addr)) return; /* The packet is from me; ignore */ if (rp->rip6_cmd != RIP6_RESPONSE) { trace(1, "Invalid command %d\n", rp->rip6_cmd); return; } /* -N: no use */ if (iff_find(ifcp, IFIL_TYPE_N) != NULL) return; tracet(1, "Recv(%s): from %s.%d info(%zd)\n", ifcp->ifc_name, inet6_n2p(&nh), ntohs(fsock.sin6_port), nn); t = time(NULL); t_half_lifetime = t - (RIP_LIFETIME/2); for (; nn; nn--, np++) { if (np->rip6_metric == NEXTHOP_METRIC) { /* modify neighbor address */ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) { nh = np->rip6_dest; trace(1, "\tNexthop: %s\n", inet6_n2p(&nh)); } else if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest)) { nh = fsock.sin6_addr; trace(1, "\tNexthop: %s\n", inet6_n2p(&nh)); } else { nh = fsock.sin6_addr; trace(1, "\tInvalid Nexthop: %s\n", inet6_n2p(&np->rip6_dest)); } continue; } if (IN6_IS_ADDR_MULTICAST(&np->rip6_dest)) { trace(1, "\tMulticast netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } if (IN6_IS_ADDR_LOOPBACK(&np->rip6_dest)) { trace(1, "\tLoopback netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) { trace(1, "\tLink Local netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } /* may need to pass sitelocal prefix in some case, however*/ if (IN6_IS_ADDR_SITELOCAL(&np->rip6_dest) && !lflag) { trace(1, "\tSite Local netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } trace(2, "\tnetinfo6: %s/%d [%d]", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); if (np->rip6_tag) trace(2, " tag=0x%04x", ntohs(np->rip6_tag) & 0xffff); if (dflag >= 2) { ia = np->rip6_dest; applyplen(&ia, np->rip6_plen); if (!IN6_ARE_ADDR_EQUAL(&ia, &np->rip6_dest)) trace(2, " [junk outside prefix]"); } /* * -L: listen only if the prefix matches the configuration */ ok = 1; /* if there's no L filter, it is ok */ TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != IFIL_TYPE_L) continue; ok = 0; if (np->rip6_plen < iffp->iff_plen) continue; /* special rule: ::/0 means default, not "in /0" */ if (iffp->iff_plen == 0 && np->rip6_plen > 0) continue; ia = np->rip6_dest; applyplen(&ia, iffp->iff_plen); if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) { ok = 1; break; } } if (!ok) { trace(2, " (filtered)\n"); continue; } trace(2, "\n"); np->rip6_metric++; np->rip6_metric += ifcp->ifc_metric; if (np->rip6_metric > HOPCNT_INFINITY6) np->rip6_metric = HOPCNT_INFINITY6; applyplen(&np->rip6_dest, np->rip6_plen); if ((rrt = rtsearch(np)) != NULL) { if (rrt->rrt_t == 0) continue; /* Intf route has priority */ nq = &rrt->rrt_info; if (nq->rip6_metric > np->rip6_metric) { if (rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) { /* Small metric from the same gateway */ nq->rip6_metric = np->rip6_metric; } else { /* Better route found */ rrt->rrt_index = ifcp->ifc_index; /* Update routing table */ delroute(nq, &rrt->rrt_gw); rrt->rrt_gw = nh; *nq = *np; addroute(rrt, &nh, ifcp); } rrt->rrt_rflags |= RRTF_CHANGED; rrt->rrt_t = t; need_trigger = 1; } else if (nq->rip6_metric < np->rip6_metric && rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) { /* Got worse route from same gw */ nq->rip6_metric = np->rip6_metric; rrt->rrt_t = t; rrt->rrt_rflags |= RRTF_CHANGED; need_trigger = 1; } else if (nq->rip6_metric == np->rip6_metric && np->rip6_metric < HOPCNT_INFINITY6) { if (rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) { /* same metric, same route from same gw */ rrt->rrt_t = t; } else if (rrt->rrt_t < t_half_lifetime) { /* Better route found */ rrt->rrt_index = ifcp->ifc_index; /* Update routing table */ delroute(nq, &rrt->rrt_gw); rrt->rrt_gw = nh; *nq = *np; addroute(rrt, &nh, ifcp); rrt->rrt_rflags |= RRTF_CHANGED; rrt->rrt_t = t; } } /* * if nq->rip6_metric == HOPCNT_INFINITY6 then * do not update age value. Do nothing. */ } else if (np->rip6_metric < HOPCNT_INFINITY6) { /* Got a new valid route */ if ((rrt = MALLOC(struct riprt)) == NULL) { fatal("malloc: struct riprt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(*rrt)); nq = &rrt->rrt_info; rrt->rrt_same = NULL; rrt->rrt_index = ifcp->ifc_index; rrt->rrt_flags = RTF_UP|RTF_GATEWAY; rrt->rrt_gw = nh; *nq = *np; applyplen(&nq->rip6_dest, nq->rip6_plen); if (nq->rip6_plen == sizeof(struct in6_addr) * 8) rrt->rrt_flags |= RTF_HOST; /* Update routing table */ addroute(rrt, &nh, ifcp); rrt->rrt_rflags |= RRTF_CHANGED; need_trigger = 1; rrt->rrt_t = t; /* Put the route to the list */ TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); } } /* XXX need to care the interval between triggered updates */ if (need_trigger) { if (nextalarm > time(NULL) + RIP_TRIG_INT6_MAX) { TAILQ_FOREACH(ic, &ifc_head, ifc_next) { if (ifcp->ifc_index == ic->ifc_index) continue; if (ic->ifc_flags & IFF_UP) ripsend(ic, &ic->ifc_ripsin, RRTF_CHANGED); } } /* Reset the flag */ TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { rrt->rrt_rflags &= ~RRTF_CHANGED; } } } /* * Send all routes request packet to the specified interface. */ void sendrequest(struct ifc *ifcp) { struct netinfo6 *np; int error; if (ifcp->ifc_flags & IFF_LOOPBACK) return; ripbuf->rip6_cmd = RIP6_REQUEST; np = ripbuf->rip6_nets; memset(np, 0, sizeof(struct netinfo6)); np->rip6_metric = HOPCNT_INFINITY6; tracet(1, "Send rtdump Request to %s (%s)\n", ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr)); error = sendpacket(&ifcp->ifc_ripsin, RIPSIZE(1)); if (error == EAFNOSUPPORT) { /* Protocol not supported */ tracet(1, "Could not send rtdump Request to %s (%s): " "set IFF_UP to 0\n", ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr)); ifcp->ifc_flags &= ~IFF_UP; /* As if down for AF_INET6 */ } ripbuf->rip6_cmd = RIP6_RESPONSE; } /* * Process a RIP6_REQUEST packet. */ void riprequest(struct ifc *ifcp, struct netinfo6 *np, int nn, struct sockaddr_in6 *sin6) { int i; struct riprt *rrt; if (!(nn == 1 && IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest) && np->rip6_plen == 0 && np->rip6_metric == HOPCNT_INFINITY6)) { /* Specific response, don't split-horizon */ trace(1, "\tRIP Request\n"); for (i = 0; i < nn; i++, np++) { rrt = rtsearch(np); if (rrt) np->rip6_metric = rrt->rrt_info.rip6_metric; else np->rip6_metric = HOPCNT_INFINITY6; } (void)sendpacket(sin6, RIPSIZE(nn)); return; } /* Whole routing table dump */ trace(1, "\tRIP Request -- whole routing table\n"); ripsend(ifcp, sin6, RRTF_SENDANYWAY); } /* * Get information of each interface. */ void ifconfig(void) { struct ifaddrs *ifap, *ifa; struct ifc *ifcp; struct ipv6_mreq mreq; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { fatal("socket"); /*NOTREACHED*/ } if (getifaddrs(&ifap) != 0) { fatal("getifaddrs"); /*NOTREACHED*/ } for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifcp = ifc_find(ifa->ifa_name); /* we are interested in multicast-capable interfaces */ if ((ifa->ifa_flags & IFF_MULTICAST) == 0) continue; if (!ifcp) { /* new interface */ if ((ifcp = MALLOC(struct ifc)) == NULL) { fatal("malloc: struct ifc"); /*NOTREACHED*/ } memset(ifcp, 0, sizeof(*ifcp)); ifcp->ifc_index = -1; strlcpy(ifcp->ifc_name, ifa->ifa_name, sizeof(ifcp->ifc_name)); TAILQ_INIT(&ifcp->ifc_ifac_head); TAILQ_INIT(&ifcp->ifc_iff_head); ifcp->ifc_flags = ifa->ifa_flags; TAILQ_INSERT_HEAD(&ifc_head, ifcp, ifc_next); trace(1, "newif %s <%s>\n", ifcp->ifc_name, ifflags(ifcp->ifc_flags)); if (!strcmp(ifcp->ifc_name, LOOPBACK_IF)) loopifcp = ifcp; } else { /* update flag, this may be up again */ if (ifcp->ifc_flags != ifa->ifa_flags) { trace(1, "%s: <%s> -> ", ifcp->ifc_name, ifflags(ifcp->ifc_flags)); trace(1, "<%s>\n", ifflags(ifa->ifa_flags)); ifcp->ifc_cflags |= IFC_CHANGED; } ifcp->ifc_flags = ifa->ifa_flags; } if (ifconfig1(ifa->ifa_name, ifa->ifa_addr, ifcp, s) < 0) { /* maybe temporary failure */ continue; } if ((ifcp->ifc_flags & (IFF_LOOPBACK | IFF_UP)) == IFF_UP && 0 < ifcp->ifc_index && !ifcp->ifc_joined) { mreq.ipv6mr_multiaddr = ifcp->ifc_ripsin.sin6_addr; mreq.ipv6mr_interface = ifcp->ifc_index; if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) { fatal("IPV6_JOIN_GROUP"); /*NOTREACHED*/ } trace(1, "join %s %s\n", ifcp->ifc_name, RIP6_DEST); ifcp->ifc_joined++; } } close(s); freeifaddrs(ifap); } int ifconfig1(const char *name, const struct sockaddr *sa, struct ifc *ifcp, int s) { struct in6_ifreq ifr; const struct sockaddr_in6 *sin6; struct ifac *ifac; int plen; char buf[BUFSIZ]; sin6 = (const struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) && !lflag) return (-1); ifr.ifr_addr = *sin6; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFNETMASK_IN6, (char *)&ifr) < 0) { syslog(LOG_INFO, "ioctl: SIOCGIFNETMASK_IN6"); return (-1); } plen = sin6mask2len(&ifr.ifr_addr); if ((ifac = ifa_match(ifcp, &sin6->sin6_addr, plen)) != NULL) { /* same interface found */ /* need check if something changed */ /* XXX not yet implemented */ return (-1); } /* * New address is found */ if ((ifac = MALLOC(struct ifac)) == NULL) { fatal("malloc: struct ifac"); /*NOTREACHED*/ } memset(ifac, 0, sizeof(*ifac)); ifac->ifac_ifc = ifcp; ifac->ifac_addr = sin6->sin6_addr; ifac->ifac_plen = plen; ifac->ifac_scope_id = sin6->sin6_scope_id; if (ifcp->ifc_flags & IFF_POINTOPOINT) { ifr.ifr_addr = *sin6; if (ioctl(s, SIOCGIFDSTADDR_IN6, (char *)&ifr) < 0) { fatal("ioctl: SIOCGIFDSTADDR_IN6"); /*NOTREACHED*/ } ifac->ifac_raddr = ifr.ifr_dstaddr.sin6_addr; inet_ntop(AF_INET6, (void *)&ifac->ifac_raddr, buf, sizeof(buf)); trace(1, "found address %s/%d -- %s\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen, buf); } else { trace(1, "found address %s/%d\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen); } if (ifcp->ifc_index < 0 && IN6_IS_ADDR_LINKLOCAL(&ifac->ifac_addr)) { ifcp->ifc_mylladdr = ifac->ifac_addr; ifcp->ifc_index = ifac->ifac_scope_id; memcpy(&ifcp->ifc_ripsin, &ripsin, ripsin.ss_len); ifcp->ifc_ripsin.sin6_scope_id = ifcp->ifc_index; setindex2ifc(ifcp->ifc_index, ifcp); ifcp->ifc_mtu = getifmtu(ifcp->ifc_index); if (ifcp->ifc_mtu > RIP6_MAXMTU) ifcp->ifc_mtu = RIP6_MAXMTU; if (ioctl(s, SIOCGIFMETRIC, (char *)&ifr) < 0) { fatal("ioctl: SIOCGIFMETRIC"); /*NOTREACHED*/ } ifcp->ifc_metric = ifr.ifr_metric; trace(1, "\tindex: %d, mtu: %d, metric: %d\n", ifcp->ifc_index, ifcp->ifc_mtu, ifcp->ifc_metric); } else ifcp->ifc_cflags |= IFC_CHANGED; TAILQ_INSERT_HEAD(&ifcp->ifc_ifac_head, ifac, ifac_next); return 0; } void ifremove(int ifindex) { struct ifc *ifcp; struct riprt *rrt; TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_index == ifindex) break; } if (ifcp == NULL) return; tracet(1, "ifremove: %s is departed.\n", ifcp->ifc_name); TAILQ_REMOVE(&ifc_head, ifcp, ifc_next); TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_index == ifcp->ifc_index && rrt->rrt_rflags & RRTF_AGGREGATE) delroute(&rrt->rrt_info, &rrt->rrt_gw); } free(ifcp); } /* * Receive and process routing messages. * Update interface information as necesssary. */ void rtrecv(void) { char buf[BUFSIZ]; char *p, *q = NULL; struct rt_msghdr *rtm; struct ifa_msghdr *ifam; struct if_msghdr *ifm; struct if_announcemsghdr *ifan; int len; struct ifc *ifcp, *ic; int iface = 0, rtable = 0; struct sockaddr_in6 *rta[RTAX_MAX]; struct sockaddr_in6 mask; int i, addrs = 0; struct riprt *rrt; if ((len = read(rtsock, buf, sizeof(buf))) < 0) { perror("read from rtsock"); exit(1); } if (len == 0) return; #if 0 if (len < sizeof(*rtm)) { trace(1, "short read from rtsock: %d (should be > %lu)\n", len, (u_long)sizeof(*rtm)); return; } #endif if (dflag >= 2) { fprintf(stderr, "rtmsg:\n"); for (i = 0; i < len; i++) { fprintf(stderr, "%02x ", buf[i] & 0xff); if (i % 16 == 15) fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } for (p = buf; p - buf < len; p += ((struct rt_msghdr *)p)->rtm_msglen) { if (((struct rt_msghdr *)p)->rtm_version != RTM_VERSION) continue; /* safety against bogus message */ if (((struct rt_msghdr *)p)->rtm_msglen <= 0) { trace(1, "bogus rtmsg: length=%d\n", ((struct rt_msghdr *)p)->rtm_msglen); break; } rtm = NULL; ifam = NULL; ifm = NULL; switch (((struct rt_msghdr *)p)->rtm_type) { case RTM_NEWADDR: case RTM_DELADDR: ifam = (struct ifa_msghdr *)p; addrs = ifam->ifam_addrs; q = (char *)(ifam + 1); break; case RTM_IFINFO: ifm = (struct if_msghdr *)p; addrs = ifm->ifm_addrs; q = (char *)(ifm + 1); break; case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)p; switch (ifan->ifan_what) { case IFAN_ARRIVAL: iface++; break; case IFAN_DEPARTURE: ifremove(ifan->ifan_index); iface++; break; } break; default: rtm = (struct rt_msghdr *)p; addrs = rtm->rtm_addrs; q = (char *)(rtm + 1); if (rtm->rtm_version != RTM_VERSION) { trace(1, "unexpected rtmsg version %d " "(should be %d)\n", rtm->rtm_version, RTM_VERSION); continue; } if (rtm->rtm_pid == pid) { #if 0 trace(1, "rtmsg looped back to me, ignored\n"); #endif continue; } break; } memset(&rta, 0, sizeof(rta)); for (i = 0; i < RTAX_MAX; i++) { if (addrs & (1 << i)) { rta[i] = (struct sockaddr_in6 *)q; q += ROUNDUP(rta[i]->sin6_len); } } trace(1, "rtsock: %s (addrs=%x)\n", rttypes((struct rt_msghdr *)p), addrs); if (dflag >= 2) { for (i = 0; i < ((struct rt_msghdr *)p)->rtm_msglen; i++) { fprintf(stderr, "%02x ", p[i] & 0xff); if (i % 16 == 15) fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } /* * Easy ones first. * * We may be able to optimize by using ifm->ifm_index or * ifam->ifam_index. For simplicity we don't do that here. */ switch (((struct rt_msghdr *)p)->rtm_type) { case RTM_NEWADDR: case RTM_IFINFO: iface++; continue; case RTM_ADD: rtable++; continue; case RTM_LOSING: case RTM_MISS: case RTM_GET: case RTM_LOCK: /* nothing to be done here */ trace(1, "\tnothing to be done, ignored\n"); continue; } #if 0 if (rta[RTAX_DST] == NULL) { trace(1, "\tno destination, ignored\n"); continue; } if (rta[RTAX_DST]->sin6_family != AF_INET6) { trace(1, "\taf mismatch, ignored\n"); continue; } if (IN6_IS_ADDR_LINKLOCAL(&rta[RTAX_DST]->sin6_addr)) { trace(1, "\tlinklocal destination, ignored\n"); continue; } if (IN6_ARE_ADDR_EQUAL(&rta[RTAX_DST]->sin6_addr, &in6addr_loopback)) { trace(1, "\tloopback destination, ignored\n"); continue; /* Loopback */ } if (IN6_IS_ADDR_MULTICAST(&rta[RTAX_DST]->sin6_addr)) { trace(1, "\tmulticast destination, ignored\n"); continue; } #endif /* hard ones */ switch (((struct rt_msghdr *)p)->rtm_type) { case RTM_NEWADDR: case RTM_IFINFO: case RTM_ADD: case RTM_LOSING: case RTM_MISS: case RTM_GET: case RTM_LOCK: /* should already be handled */ fatal("rtrecv: never reach here"); /*NOTREACHED*/ case RTM_DELETE: if (!rta[RTAX_DST] || !rta[RTAX_GATEWAY]) { trace(1, "\tsome of dst/gw/netamsk are " "unavailable, ignored\n"); break; } if ((rtm->rtm_flags & RTF_HOST) != 0) { mask.sin6_len = sizeof(mask); memset(&mask.sin6_addr, 0xff, sizeof(mask.sin6_addr)); rta[RTAX_NETMASK] = &mask; } else if (!rta[RTAX_NETMASK]) { trace(1, "\tsome of dst/gw/netamsk are " "unavailable, ignored\n"); break; } if (rt_del(rta[RTAX_DST], rta[RTAX_GATEWAY], rta[RTAX_NETMASK]) == 0) { rtable++; /*just to be sure*/ } break; case RTM_CHANGE: case RTM_REDIRECT: trace(1, "\tnot supported yet, ignored\n"); break; case RTM_DELADDR: if (!rta[RTAX_NETMASK] || !rta[RTAX_IFA]) { trace(1, "\tno netmask or ifa given, ignored\n"); break; } if (ifam->ifam_index < nindex2ifc) ifcp = index2ifc[ifam->ifam_index]; else ifcp = NULL; if (!ifcp) { trace(1, "\tinvalid ifam_index %d, ignored\n", ifam->ifam_index); break; } if (!rt_deladdr(ifcp, rta[RTAX_IFA], rta[RTAX_NETMASK])) iface++; break; } } if (iface) { trace(1, "rtsock: reconfigure interfaces, refresh interface routes\n"); ifconfig(); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_cflags & IFC_CHANGED) { if (ifrt(ifcp, 1)) { TAILQ_FOREACH(ic, &ifc_head, ifc_next) { if (ifcp->ifc_index == ic->ifc_index) continue; if (ic->ifc_flags & IFF_UP) ripsend(ic, &ic->ifc_ripsin, RRTF_CHANGED); } /* Reset the flag */ TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { rrt->rrt_rflags &= ~RRTF_CHANGED; } } ifcp->ifc_cflags &= ~IFC_CHANGED; } } } if (rtable) { trace(1, "rtsock: read routing table again\n"); krtread(1); } } /* * remove specified route from the internal routing table. */ int rt_del(const struct sockaddr_in6 *sdst, const struct sockaddr_in6 *sgw, const struct sockaddr_in6 *smask) { const struct in6_addr *dst = NULL; const struct in6_addr *gw = NULL; int prefix; struct netinfo6 ni6; struct riprt *rrt = NULL; time_t t_lifetime; if (sdst->sin6_family != AF_INET6) { trace(1, "\tother AF, ignored\n"); return -1; } if (IN6_IS_ADDR_LINKLOCAL(&sdst->sin6_addr) || IN6_ARE_ADDR_EQUAL(&sdst->sin6_addr, &in6addr_loopback) || IN6_IS_ADDR_MULTICAST(&sdst->sin6_addr)) { trace(1, "\taddress %s not interesting, ignored\n", inet6_n2p(&sdst->sin6_addr)); return -1; } dst = &sdst->sin6_addr; if (sgw->sin6_family == AF_INET6) { /* easy case */ gw = &sgw->sin6_addr; prefix = sin6mask2len(smask); } else if (sgw->sin6_family == AF_LINK) { /* * Interface route... a hard case. We need to get the prefix * length from the kernel, but we now are parsing rtmsg. * We'll purge matching routes from my list, then get the * fresh list. */ struct riprt *longest; trace(1, "\t%s is an interface route, guessing prefixlen\n", inet6_n2p(dst)); longest = NULL; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest, &sdst->sin6_addr) && IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw)) { if (!longest || longest->rrt_info.rip6_plen < rrt->rrt_info.rip6_plen) { longest = rrt; } } } rrt = longest; if (!rrt) { trace(1, "\tno matching interface route found\n"); return -1; } gw = &in6addr_loopback; prefix = rrt->rrt_info.rip6_plen; } else { trace(1, "\tunsupported af: (gw=%d)\n", sgw->sin6_family); return -1; } trace(1, "\tdeleting %s/%d ", inet6_n2p(dst), prefix); trace(1, "gw %s\n", inet6_n2p(gw)); t_lifetime = time(NULL) - RIP_LIFETIME; /* age route for interface address */ memset(&ni6, 0, sizeof(ni6)); ni6.rip6_dest = *dst; ni6.rip6_plen = prefix; applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/ trace(1, "\tfind route %s/%d\n", inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen); if (!rrt && (rrt = rtsearch(&ni6)) == NULL) { trace(1, "\tno route found\n"); return -1; } #if 0 if ((rrt->rrt_flags & RTF_STATIC) == 0) { trace(1, "\tyou can delete static routes only\n"); } else #endif if (!IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, gw)) { trace(1, "\tgw mismatch: %s <-> ", inet6_n2p(&rrt->rrt_gw)); trace(1, "%s\n", inet6_n2p(gw)); } else { trace(1, "\troute found, age it\n"); if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) { rrt->rrt_t = t_lifetime; rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; } } return 0; } /* * remove specified address from internal interface/routing table. */ int rt_deladdr(struct ifc *ifcp, const struct sockaddr_in6 *sifa, const struct sockaddr_in6 *smask) { const struct in6_addr *addr = NULL; int prefix; struct ifac *ifac = NULL; struct netinfo6 ni6; struct riprt *rrt = NULL; time_t t_lifetime; int updated = 0; if (sifa->sin6_family != AF_INET6) { trace(1, "\tother AF, ignored\n"); return -1; } addr = &sifa->sin6_addr; prefix = sin6mask2len(smask); trace(1, "\tdeleting %s/%d from %s\n", inet6_n2p(addr), prefix, ifcp->ifc_name); ifac = ifa_match(ifcp, addr, prefix); if (!ifac) { trace(1, "\tno matching ifa found for %s/%d on %s\n", inet6_n2p(addr), prefix, ifcp->ifc_name); return -1; } if (ifac->ifac_ifc != ifcp) { trace(1, "\taddress table corrupt: back pointer does not match " "(%s != %s)\n", ifcp->ifc_name, ifac->ifac_ifc->ifc_name); return -1; } TAILQ_REMOVE(&ifcp->ifc_ifac_head, ifac, ifac_next); t_lifetime = time(NULL) - RIP_LIFETIME; /* age route for interface address */ memset(&ni6, 0, sizeof(ni6)); ni6.rip6_dest = ifac->ifac_addr; ni6.rip6_plen = ifac->ifac_plen; applyplen(&ni6.rip6_dest, ni6.rip6_plen); trace(1, "\tfind interface route %s/%d on %d\n", inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen, ifcp->ifc_index); if ((rrt = rtsearch(&ni6)) != NULL) { struct in6_addr none; memset(&none, 0, sizeof(none)); if (rrt->rrt_index == ifcp->ifc_index && (IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &none) || IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw))) { trace(1, "\troute found, age it\n"); if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) { rrt->rrt_t = t_lifetime; rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; } updated++; } else { trace(1, "\tnon-interface route found: %s/%d on %d\n", inet6_n2p(&rrt->rrt_info.rip6_dest), rrt->rrt_info.rip6_plen, rrt->rrt_index); } } else trace(1, "\tno interface route found\n"); /* age route for p2p destination */ if (ifcp->ifc_flags & IFF_POINTOPOINT) { memset(&ni6, 0, sizeof(ni6)); ni6.rip6_dest = ifac->ifac_raddr; ni6.rip6_plen = 128; applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/ trace(1, "\tfind p2p route %s/%d on %d\n", inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen, ifcp->ifc_index); if ((rrt = rtsearch(&ni6)) != NULL) { if (rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &ifac->ifac_addr)) { trace(1, "\troute found, age it\n"); if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) { rrt->rrt_t = t_lifetime; rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; updated++; } } else { trace(1, "\tnon-p2p route found: %s/%d on %d\n", inet6_n2p(&rrt->rrt_info.rip6_dest), rrt->rrt_info.rip6_plen, rrt->rrt_index); } } else trace(1, "\tno p2p route found\n"); } free(ifac); return ((updated) ? 0 : -1); } /* * Get each interface address and put those interface routes to the route * list. */ int ifrt(struct ifc *ifcp, int again) { struct ifac *ifac; struct riprt *rrt = NULL, *search_rrt, *loop_rrt; struct netinfo6 *np; time_t t_lifetime; int need_trigger = 0; #if 0 if (ifcp->ifc_flags & IFF_LOOPBACK) return 0; /* ignore loopback */ #endif if (ifcp->ifc_flags & IFF_POINTOPOINT) { ifrt_p2p(ifcp, again); return 0; } TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { if (IN6_IS_ADDR_LINKLOCAL(&ifac->ifac_addr)) { #if 0 trace(1, "route: %s on %s: " "skip linklocal interface address\n", inet6_n2p(&ifac->ifac_addr), ifcp->ifc_name); #endif continue; } if (IN6_IS_ADDR_UNSPECIFIED(&ifac->ifac_addr)) { #if 0 trace(1, "route: %s: skip unspec interface address\n", ifcp->ifc_name); #endif continue; } if (IN6_IS_ADDR_LOOPBACK(&ifac->ifac_addr)) { #if 0 trace(1, "route: %s: skip loopback address\n", ifcp->ifc_name); #endif continue; } if (ifcp->ifc_flags & IFF_UP) { if ((rrt = MALLOC(struct riprt)) == NULL) fatal("malloc: struct riprt"); memset(rrt, 0, sizeof(*rrt)); rrt->rrt_same = NULL; rrt->rrt_index = ifcp->ifc_index; rrt->rrt_t = 0; /* don't age */ rrt->rrt_info.rip6_dest = ifac->ifac_addr; rrt->rrt_info.rip6_tag = htons(routetag & 0xffff); rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric; rrt->rrt_info.rip6_plen = ifac->ifac_plen; rrt->rrt_flags = RTF_HOST; rrt->rrt_rflags |= RRTF_CHANGED; applyplen(&rrt->rrt_info.rip6_dest, ifac->ifac_plen); memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr)); rrt->rrt_gw = ifac->ifac_addr; np = &rrt->rrt_info; search_rrt = rtsearch(np); if (search_rrt != NULL) { if (search_rrt->rrt_info.rip6_metric <= rrt->rrt_info.rip6_metric) { /* Already have better route */ if (!again) { trace(1, "route: %s/%d: " "already registered (%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, ifcp->ifc_name); } goto next; } TAILQ_REMOVE(&riprt_head, rrt, rrt_next); delroute(&rrt->rrt_info, &rrt->rrt_gw); } /* Attach the route to the list */ trace(1, "route: %s/%d: register route (%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, ifcp->ifc_name); TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); addroute(rrt, &rrt->rrt_gw, ifcp); rrt = NULL; sendrequest(ifcp); ripsend(ifcp, &ifcp->ifc_ripsin, 0); need_trigger = 1; } else { TAILQ_FOREACH(loop_rrt, &riprt_head, rrt_next) { if (loop_rrt->rrt_index == ifcp->ifc_index) { t_lifetime = time(NULL) - RIP_LIFETIME; if (loop_rrt->rrt_t == 0 || loop_rrt->rrt_t > t_lifetime) { loop_rrt->rrt_t = t_lifetime; loop_rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; loop_rrt->rrt_rflags |= RRTF_CHANGED; need_trigger = 1; } } } } next: if (rrt) free(rrt); } return need_trigger; } /* * there are couple of p2p interface routing models. "behavior" lets * you pick one. it looks that gated behavior fits best with BSDs, * since BSD kernels do not look at prefix length on p2p interfaces. */ void ifrt_p2p(struct ifc *ifcp, int again) { struct ifac *ifac; struct riprt *rrt, *orrt; struct netinfo6 *np; struct in6_addr addr, dest; int advert, ignore, i; #define P2PADVERT_NETWORK 1 #define P2PADVERT_ADDR 2 #define P2PADVERT_DEST 4 #define P2PADVERT_MAX 4 const enum { CISCO, GATED, ROUTE6D } behavior = GATED; const char *category = ""; const char *noadv; TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { addr = ifac->ifac_addr; dest = ifac->ifac_raddr; applyplen(&addr, ifac->ifac_plen); applyplen(&dest, ifac->ifac_plen); advert = ignore = 0; switch (behavior) { case CISCO: /* * honor addr/plen, just like normal shared medium * interface. this may cause trouble if you reuse * addr/plen on other interfaces. * * advertise addr/plen. */ advert |= P2PADVERT_NETWORK; break; case GATED: /* * prefixlen on p2p interface is meaningless. * advertise addr/128 and dest/128. * * do not install network route to route6d routing * table (if we do, it would prevent route installation * for other p2p interface that shares addr/plen). * * XXX what should we do if dest is ::? it will not * get announced anyways (see following filter), * but we need to think. */ advert |= P2PADVERT_ADDR; advert |= P2PADVERT_DEST; ignore |= P2PADVERT_NETWORK; break; case ROUTE6D: /* * just for testing. actually the code is redundant * given the current p2p interface address assignment * rule for kame kernel. * * intent: * A/n -> announce A/n * A B/n, A and B share prefix -> A/n (= B/n) * A B/n, do not share prefix -> A/128 and B/128 * actually, A/64 and A B/128 are the only cases * permitted by the kernel: * A/64 -> A/64 * A B/128 -> A/128 and B/128 */ if (!IN6_IS_ADDR_UNSPECIFIED(&ifac->ifac_raddr)) { if (IN6_ARE_ADDR_EQUAL(&addr, &dest)) advert |= P2PADVERT_NETWORK; else { advert |= P2PADVERT_ADDR; advert |= P2PADVERT_DEST; ignore |= P2PADVERT_NETWORK; } } else advert |= P2PADVERT_NETWORK; break; } for (i = 1; i <= P2PADVERT_MAX; i *= 2) { if ((ignore & i) != 0) continue; if ((rrt = MALLOC(struct riprt)) == NULL) { fatal("malloc: struct riprt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(*rrt)); rrt->rrt_same = NULL; rrt->rrt_index = ifcp->ifc_index; rrt->rrt_t = 0; /* don't age */ switch (i) { case P2PADVERT_NETWORK: rrt->rrt_info.rip6_dest = ifac->ifac_addr; rrt->rrt_info.rip6_plen = ifac->ifac_plen; applyplen(&rrt->rrt_info.rip6_dest, ifac->ifac_plen); category = "network"; break; case P2PADVERT_ADDR: rrt->rrt_info.rip6_dest = ifac->ifac_addr; rrt->rrt_info.rip6_plen = 128; rrt->rrt_gw = in6addr_loopback; category = "addr"; break; case P2PADVERT_DEST: rrt->rrt_info.rip6_dest = ifac->ifac_raddr; rrt->rrt_info.rip6_plen = 128; rrt->rrt_gw = ifac->ifac_addr; category = "dest"; break; } if (IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_info.rip6_dest) || IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_info.rip6_dest)) { #if 0 trace(1, "route: %s: skip unspec/linklocal " "(%s on %s)\n", category, ifcp->ifc_name); #endif free(rrt); continue; } if ((advert & i) == 0) { rrt->rrt_rflags |= RRTF_NOADVERTISE; noadv = ", NO-ADV"; } else noadv = ""; rrt->rrt_info.rip6_tag = htons(routetag & 0xffff); rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric; np = &rrt->rrt_info; orrt = rtsearch(np); if (!orrt) { /* Attach the route to the list */ trace(1, "route: %s/%d: register route " "(%s on %s%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, category, ifcp->ifc_name, noadv); TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); } else if (rrt->rrt_index != orrt->rrt_index || rrt->rrt_info.rip6_metric != orrt->rrt_info.rip6_metric) { /* replace route */ TAILQ_INSERT_BEFORE(orrt, rrt, rrt_next); TAILQ_REMOVE(&riprt_head, orrt, rrt_next); free(orrt); trace(1, "route: %s/%d: update (%s on %s%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, category, ifcp->ifc_name, noadv); } else { /* Already found */ if (!again) { trace(1, "route: %s/%d: " "already registered (%s on %s%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, category, ifcp->ifc_name, noadv); } free(rrt); } } } #undef P2PADVERT_NETWORK #undef P2PADVERT_ADDR #undef P2PADVERT_DEST #undef P2PADVERT_MAX } int getifmtu(int ifindex) { int mib[6]; char *buf; size_t msize; struct if_msghdr *ifm; int mtu; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET6; mib[4] = NET_RT_IFLIST; mib[5] = ifindex; if (sysctl(mib, nitems(mib), NULL, &msize, NULL, 0) < 0) { fatal("sysctl estimate NET_RT_IFLIST"); /*NOTREACHED*/ } if ((buf = malloc(msize)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } if (sysctl(mib, nitems(mib), buf, &msize, NULL, 0) < 0) { fatal("sysctl NET_RT_IFLIST"); /*NOTREACHED*/ } ifm = (struct if_msghdr *)buf; mtu = ifm->ifm_data.ifi_mtu; if (ifindex != ifm->ifm_index) { fatal("ifindex does not match with ifm_index"); /*NOTREACHED*/ } free(buf); return mtu; } const char * rttypes(struct rt_msghdr *rtm) { #define RTTYPE(s, f) \ do { \ if (rtm->rtm_type == (f)) \ return (s); \ } while (0) RTTYPE("ADD", RTM_ADD); RTTYPE("DELETE", RTM_DELETE); RTTYPE("CHANGE", RTM_CHANGE); RTTYPE("GET", RTM_GET); RTTYPE("LOSING", RTM_LOSING); RTTYPE("REDIRECT", RTM_REDIRECT); RTTYPE("MISS", RTM_MISS); RTTYPE("LOCK", RTM_LOCK); RTTYPE("NEWADDR", RTM_NEWADDR); RTTYPE("DELADDR", RTM_DELADDR); RTTYPE("IFINFO", RTM_IFINFO); #ifdef RTM_OIFINFO RTTYPE("OIFINFO", RTM_OIFINFO); #endif #ifdef RTM_IFANNOUNCE RTTYPE("IFANNOUNCE", RTM_IFANNOUNCE); #endif #ifdef RTM_NEWMADDR RTTYPE("NEWMADDR", RTM_NEWMADDR); #endif #ifdef RTM_DELMADDR RTTYPE("DELMADDR", RTM_DELMADDR); #endif #undef RTTYPE return NULL; } const char * rtflags(struct rt_msghdr *rtm) { static char buf[BUFSIZ]; /* * letter conflict should be okay. painful when *BSD diverges... */ strlcpy(buf, "", sizeof(buf)); #define RTFLAG(s, f) \ do { \ if (rtm->rtm_flags & (f)) \ strlcat(buf, (s), sizeof(buf)); \ } while (0) RTFLAG("U", RTF_UP); RTFLAG("G", RTF_GATEWAY); RTFLAG("H", RTF_HOST); RTFLAG("R", RTF_REJECT); RTFLAG("D", RTF_DYNAMIC); RTFLAG("M", RTF_MODIFIED); RTFLAG("d", RTF_DONE); #ifdef RTF_MASK RTFLAG("m", RTF_MASK); #endif #ifdef RTF_CLONED RTFLAG("c", RTF_CLONED); #endif RTFLAG("X", RTF_XRESOLVE); #ifdef RTF_LLINFO RTFLAG("L", RTF_LLINFO); #endif RTFLAG("S", RTF_STATIC); RTFLAG("B", RTF_BLACKHOLE); #ifdef RTF_PROTO3 RTFLAG("3", RTF_PROTO3); #endif RTFLAG("2", RTF_PROTO2); RTFLAG("1", RTF_PROTO1); #ifdef RTF_BROADCAST RTFLAG("b", RTF_BROADCAST); #endif #ifdef RTF_DEFAULT RTFLAG("d", RTF_DEFAULT); #endif #ifdef RTF_ISAROUTER RTFLAG("r", RTF_ISAROUTER); #endif #ifdef RTF_TUNNEL RTFLAG("T", RTF_TUNNEL); #endif #ifdef RTF_AUTH RTFLAG("A", RTF_AUTH); #endif #ifdef RTF_CRYPT RTFLAG("E", RTF_CRYPT); #endif #undef RTFLAG return buf; } const char * ifflags(int flags) { static char buf[BUFSIZ]; strlcpy(buf, "", sizeof(buf)); #define IFFLAG(s, f) \ do { \ if (flags & (f)) { \ if (buf[0]) \ strlcat(buf, ",", sizeof(buf)); \ strlcat(buf, (s), sizeof(buf)); \ } \ } while (0) IFFLAG("UP", IFF_UP); IFFLAG("BROADCAST", IFF_BROADCAST); IFFLAG("DEBUG", IFF_DEBUG); IFFLAG("LOOPBACK", IFF_LOOPBACK); IFFLAG("POINTOPOINT", IFF_POINTOPOINT); #ifdef IFF_NOTRAILERS IFFLAG("NOTRAILERS", IFF_NOTRAILERS); #endif #ifdef IFF_SMART IFFLAG("SMART", IFF_SMART); #endif IFFLAG("RUNNING", IFF_RUNNING); IFFLAG("NOARP", IFF_NOARP); IFFLAG("PROMISC", IFF_PROMISC); IFFLAG("ALLMULTI", IFF_ALLMULTI); IFFLAG("OACTIVE", IFF_OACTIVE); IFFLAG("SIMPLEX", IFF_SIMPLEX); IFFLAG("LINK0", IFF_LINK0); IFFLAG("LINK1", IFF_LINK1); IFFLAG("LINK2", IFF_LINK2); IFFLAG("MULTICAST", IFF_MULTICAST); #undef IFFLAG return buf; } void krtread(int again) { int mib[6]; size_t msize; char *buf, *p, *lim; struct rt_msghdr *rtm; int retry; const char *errmsg; retry = 0; buf = NULL; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET6; /* Address family */ mib[4] = NET_RT_DUMP; /* Dump the kernel routing table */ mib[5] = 0; /* No flags */ do { if (retry) sleep(1); retry++; errmsg = NULL; if (buf) { free(buf); buf = NULL; } if (sysctl(mib, nitems(mib), NULL, &msize, NULL, 0) < 0) { errmsg = "sysctl estimate"; continue; } if ((buf = malloc(msize)) == NULL) { errmsg = "malloc"; continue; } if (sysctl(mib, nitems(mib), buf, &msize, NULL, 0) < 0) { errmsg = "sysctl NET_RT_DUMP"; continue; } } while (retry < RT_DUMP_MAXRETRY && errmsg != NULL); if (errmsg) { fatal("%s (with %d retries, msize=%lu)", errmsg, retry, (u_long)msize); /*NOTREACHED*/ } else if (1 < retry) syslog(LOG_INFO, "NET_RT_DUMP %d retires", retry); lim = buf + msize; for (p = buf; p < lim; p += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)p; rt_entry(rtm, again); } free(buf); } void rt_entry(struct rt_msghdr *rtm, int again) { struct sockaddr_in6 *sin6_dst, *sin6_gw, *sin6_mask; struct sockaddr_in6 *sin6_genmask, *sin6_ifp; char *rtmp, *ifname = NULL; struct riprt *rrt, *orrt; struct netinfo6 *np; int ifindex; sin6_dst = sin6_gw = sin6_mask = sin6_genmask = sin6_ifp = 0; if ((rtm->rtm_flags & RTF_UP) == 0 || rtm->rtm_flags & (RTF_XRESOLVE|RTF_BLACKHOLE)) { return; /* not interested in the link route */ } /* do not look at cloned routes */ #ifdef RTF_WASCLONED if (rtm->rtm_flags & RTF_WASCLONED) return; #endif #ifdef RTF_CLONED if (rtm->rtm_flags & RTF_CLONED) return; #endif /* XXX: Ignore connected routes. */ if (!(rtm->rtm_flags & (RTF_GATEWAY|RTF_HOST|RTF_STATIC))) return; /* * do not look at dynamic routes. * netbsd/openbsd cloned routes have UGHD. */ if (rtm->rtm_flags & RTF_DYNAMIC) return; rtmp = (char *)(rtm + 1); /* Destination */ if ((rtm->rtm_addrs & RTA_DST) == 0) return; /* ignore routes without destination address */ sin6_dst = (struct sockaddr_in6 *)rtmp; rtmp += ROUNDUP(sin6_dst->sin6_len); if (rtm->rtm_addrs & RTA_GATEWAY) { sin6_gw = (struct sockaddr_in6 *)rtmp; rtmp += ROUNDUP(sin6_gw->sin6_len); } if (rtm->rtm_addrs & RTA_NETMASK) { sin6_mask = (struct sockaddr_in6 *)rtmp; rtmp += ROUNDUP(sin6_mask->sin6_len); } if (rtm->rtm_addrs & RTA_GENMASK) { sin6_genmask = (struct sockaddr_in6 *)rtmp; rtmp += ROUNDUP(sin6_genmask->sin6_len); } if (rtm->rtm_addrs & RTA_IFP) { sin6_ifp = (struct sockaddr_in6 *)rtmp; rtmp += ROUNDUP(sin6_ifp->sin6_len); } /* Destination */ if (sin6_dst->sin6_family != AF_INET6) return; if (IN6_IS_ADDR_LINKLOCAL(&sin6_dst->sin6_addr)) return; /* Link-local */ if (IN6_ARE_ADDR_EQUAL(&sin6_dst->sin6_addr, &in6addr_loopback)) return; /* Loopback */ if (IN6_IS_ADDR_MULTICAST(&sin6_dst->sin6_addr)) return; if ((rrt = MALLOC(struct riprt)) == NULL) { fatal("malloc: struct riprt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(*rrt)); np = &rrt->rrt_info; rrt->rrt_same = NULL; rrt->rrt_t = time(NULL); if (aflag == 0 && (rtm->rtm_flags & RTF_STATIC)) rrt->rrt_t = 0; /* Don't age static routes */ if (rtm->rtm_flags & Pflag) rrt->rrt_t = 0; /* Don't age PROTO[123] routes */ if ((rtm->rtm_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST) rrt->rrt_t = 0; /* Don't age non-gateway host routes */ np->rip6_tag = 0; np->rip6_metric = rtm->rtm_rmx.rmx_hopcount; if (np->rip6_metric < 1) np->rip6_metric = 1; rrt->rrt_flags = rtm->rtm_flags; np->rip6_dest = sin6_dst->sin6_addr; /* Mask or plen */ if (rtm->rtm_flags & RTF_HOST) np->rip6_plen = 128; /* Host route */ else if (sin6_mask) np->rip6_plen = sin6mask2len(sin6_mask); else np->rip6_plen = 0; orrt = rtsearch(np); if (orrt && orrt->rrt_info.rip6_metric != HOPCNT_INFINITY6) { /* Already found */ if (!again) { trace(1, "route: %s/%d flags %s: already registered\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm)); } free(rrt); return; } /* Gateway */ if (!sin6_gw) memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr)); else { if (sin6_gw->sin6_family == AF_INET6) rrt->rrt_gw = sin6_gw->sin6_addr; else if (sin6_gw->sin6_family == AF_LINK) { /* XXX in case ppp link? */ rrt->rrt_gw = in6addr_loopback; } else memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr)); } trace(1, "route: %s/%d flags %s", inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm)); trace(1, " gw %s", inet6_n2p(&rrt->rrt_gw)); /* Interface */ ifindex = rtm->rtm_index; if ((unsigned int)ifindex < nindex2ifc && index2ifc[ifindex]) ifname = index2ifc[ifindex]->ifc_name; else { trace(1, " not configured\n"); free(rrt); return; } trace(1, " if %s sock %d", ifname, ifindex); rrt->rrt_index = ifindex; trace(1, "\n"); /* Check gateway */ if (!IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_gw) && !IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw) && (rrt->rrt_flags & RTF_LOCAL) == 0) { trace(0, "***** Gateway %s is not a link-local address.\n", inet6_n2p(&rrt->rrt_gw)); trace(0, "***** dest(%s) if(%s) -- Not optimized.\n", inet6_n2p(&rrt->rrt_info.rip6_dest), ifname); rrt->rrt_rflags |= RRTF_NH_NOT_LLADDR; } /* Put it to the route list */ if (orrt && orrt->rrt_info.rip6_metric == HOPCNT_INFINITY6) { /* replace route list */ TAILQ_INSERT_BEFORE(orrt, rrt, rrt_next); TAILQ_REMOVE(&riprt_head, orrt, rrt_next); trace(1, "route: %s/%d flags %s: replace new route\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm)); free(orrt); } else TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); } int addroute(struct riprt *rrt, const struct in6_addr *gw, struct ifc *ifcp) { struct netinfo6 *np; u_char buf[BUFSIZ], buf1[BUFSIZ], buf2[BUFSIZ]; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; int len; np = &rrt->rrt_info; inet_ntop(AF_INET6, (const void *)gw, (char *)buf1, sizeof(buf1)); inet_ntop(AF_INET6, (void *)&ifcp->ifc_mylladdr, (char *)buf2, sizeof(buf2)); tracet(1, "ADD: %s/%d gw %s [%d] ifa %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1, np->rip6_metric - 1, buf2); if (rtlog) fprintf(rtlog, "%s: ADD: %s/%d gw %s [%d] ifa %s\n", hms(), inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1, np->rip6_metric - 1, buf2); if (nflag) return 0; memset(buf, 0, sizeof(buf)); rtm = (struct rt_msghdr *)buf; rtm->rtm_type = RTM_ADD; rtm->rtm_version = RTM_VERSION; rtm->rtm_seq = ++seq; rtm->rtm_pid = pid; rtm->rtm_flags = rrt->rrt_flags; rtm->rtm_flags |= Qflag; rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; rtm->rtm_rmx.rmx_hopcount = np->rip6_metric - 1; rtm->rtm_inits = RTV_HOPCOUNT; sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)]; /* Destination */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = np->rip6_dest; sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Gateway */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *gw; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) sin6->sin6_scope_id = ifcp->ifc_index; sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Netmask */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *(plen2mask(np->rip6_plen)); sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); len = (char *)sin6 - (char *)buf; rtm->rtm_msglen = len; if (write(rtsock, buf, len) > 0) return 0; if (errno == EEXIST) { trace(0, "ADD: Route already exists %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1); if (rtlog) fprintf(rtlog, "ADD: Route already exists %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1); } else { trace(0, "Can not write to rtsock (addroute): %s\n", strerror(errno)); if (rtlog) fprintf(rtlog, "\tCan not write to rtsock: %s\n", strerror(errno)); } return -1; } int delroute(struct netinfo6 *np, struct in6_addr *gw) { u_char buf[BUFSIZ], buf2[BUFSIZ]; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; int len; inet_ntop(AF_INET6, (void *)gw, (char *)buf2, sizeof(buf2)); tracet(1, "DEL: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); if (rtlog) fprintf(rtlog, "%s: DEL: %s/%d gw %s\n", hms(), inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); if (nflag) return 0; memset(buf, 0, sizeof(buf)); rtm = (struct rt_msghdr *)buf; rtm->rtm_type = RTM_DELETE; rtm->rtm_version = RTM_VERSION; rtm->rtm_seq = ++seq; rtm->rtm_pid = pid; rtm->rtm_flags = RTF_UP | RTF_GATEWAY; rtm->rtm_flags |= Qflag; if (np->rip6_plen == sizeof(struct in6_addr) * 8) rtm->rtm_flags |= RTF_HOST; rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)]; /* Destination */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = np->rip6_dest; sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Gateway */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *gw; sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Netmask */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *(plen2mask(np->rip6_plen)); sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); len = (char *)sin6 - (char *)buf; rtm->rtm_msglen = len; if (write(rtsock, buf, len) >= 0) return 0; if (errno == ESRCH) { trace(0, "RTDEL: Route does not exist: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); if (rtlog) fprintf(rtlog, "RTDEL: Route does not exist: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); } else { trace(0, "Can not write to rtsock (delroute): %s\n", strerror(errno)); if (rtlog) fprintf(rtlog, "\tCan not write to rtsock: %s\n", strerror(errno)); } return -1; } struct in6_addr * getroute(struct netinfo6 *np, struct in6_addr *gw) { u_char buf[BUFSIZ]; int myseq; int len; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; rtm = (struct rt_msghdr *)buf; len = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in6); memset(rtm, 0, len); rtm->rtm_type = RTM_GET; rtm->rtm_version = RTM_VERSION; myseq = ++seq; rtm->rtm_seq = myseq; rtm->rtm_addrs = RTA_DST; rtm->rtm_msglen = len; sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)]; sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = np->rip6_dest; if (write(rtsock, buf, len) < 0) { if (errno == ESRCH) /* No such route found */ return NULL; perror("write to rtsock"); exit(1); } do { if ((len = read(rtsock, buf, sizeof(buf))) < 0) { perror("read from rtsock"); exit(1); } rtm = (struct rt_msghdr *)buf; } while (rtm->rtm_seq != myseq || rtm->rtm_pid != pid); sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)]; if (rtm->rtm_addrs & RTA_DST) { sin6 = (struct sockaddr_in6 *) ((char *)sin6 + ROUNDUP(sin6->sin6_len)); } if (rtm->rtm_addrs & RTA_GATEWAY) { *gw = sin6->sin6_addr; return gw; } return NULL; } const char * inet6_n2p(const struct in6_addr *p) { static char buf[BUFSIZ]; return inet_ntop(AF_INET6, (const void *)p, buf, sizeof(buf)); } void ifrtdump(int sig) { ifdump(sig); rtdump(sig); } void ifdump(int sig) { struct ifc *ifcp; FILE *dump; int nifc = 0; if (sig == 0) dump = stderr; else if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL) dump = stderr; fprintf(dump, "%s: Interface Table Dump\n", hms()); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) nifc++; fprintf(dump, " Number of interfaces: %d\n", nifc); fprintf(dump, " advertising interfaces:\n"); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if ((ifcp->ifc_flags & IFF_UP) == 0) continue; if (iff_find(ifcp, IFIL_TYPE_N) != NULL) continue; ifdump0(dump, ifcp); } fprintf(dump, "\n"); fprintf(dump, " non-advertising interfaces:\n"); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if ((ifcp->ifc_flags & IFF_UP) && (iff_find(ifcp, IFIL_TYPE_N) == NULL)) continue; ifdump0(dump, ifcp); } fprintf(dump, "\n"); if (dump != stderr) fclose(dump); } void ifdump0(FILE *dump, const struct ifc *ifcp) { struct ifac *ifac; struct iff *iffp; char buf[BUFSIZ]; const char *ft; int addr; fprintf(dump, " %s: index(%d) flags(%s) addr(%s) mtu(%d) metric(%d)\n", ifcp->ifc_name, ifcp->ifc_index, ifflags(ifcp->ifc_flags), inet6_n2p(&ifcp->ifc_mylladdr), ifcp->ifc_mtu, ifcp->ifc_metric); TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { if (ifcp->ifc_flags & IFF_POINTOPOINT) { inet_ntop(AF_INET6, (void *)&ifac->ifac_raddr, buf, sizeof(buf)); fprintf(dump, "\t%s/%d -- %s\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen, buf); } else { fprintf(dump, "\t%s/%d\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen); } } fprintf(dump, "\tFilter:\n"); TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { addr = 0; switch (iffp->iff_type) { case IFIL_TYPE_A: ft = "Aggregate"; addr++; break; case IFIL_TYPE_N: ft = "No-use"; break; case IFIL_TYPE_O: ft = "Advertise-only"; addr++; break; case IFIL_TYPE_T: ft = "Default-only"; break; case IFIL_TYPE_L: ft = "Listen-only"; addr++; break; default: snprintf(buf, sizeof(buf), "Unknown-%c", iffp->iff_type); ft = buf; addr++; break; } fprintf(dump, "\t\t%s", ft); if (addr) fprintf(dump, "(%s/%d)", inet6_n2p(&iffp->iff_addr), iffp->iff_plen); fprintf(dump, "\n"); } fprintf(dump, "\n"); } void rtdump(int sig) { struct riprt *rrt; char buf[BUFSIZ]; FILE *dump; time_t t, age; if (sig == 0) dump = stderr; else if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL) dump = stderr; t = time(NULL); fprintf(dump, "\n%s: Routing Table Dump\n", hms()); TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_t == 0) age = 0; else age = t - rrt->rrt_t; inet_ntop(AF_INET6, (void *)&rrt->rrt_info.rip6_dest, buf, sizeof(buf)); fprintf(dump, " %s/%d if(%d:%s) gw(%s) [%d] age(%ld)", buf, rrt->rrt_info.rip6_plen, rrt->rrt_index, index2ifc[rrt->rrt_index]->ifc_name, inet6_n2p(&rrt->rrt_gw), rrt->rrt_info.rip6_metric, (long)age); if (rrt->rrt_info.rip6_tag) { fprintf(dump, " tag(0x%04x)", ntohs(rrt->rrt_info.rip6_tag) & 0xffff); } if (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR) fprintf(dump, " NOT-LL"); if (rrt->rrt_rflags & RRTF_NOADVERTISE) fprintf(dump, " NO-ADV"); fprintf(dump, "\n"); } fprintf(dump, "\n"); if (dump != stderr) fclose(dump); } /* * Parse the -A (and -O) options and put corresponding filter object to the * specified interface structures. Each of the -A/O option has the following * syntax: -A 5f09:c400::/32,ef0,ef1 (aggregate) * -O 5f09:c400::/32,ef0,ef1 (only when match) */ void filterconfig(void) { int i; char *p, *ap, *iflp, *ifname, *ep; struct iff iff, *iffp; struct ifc *ifcp; struct riprt *rrt; #if 0 struct in6_addr gw; #endif u_long plen; for (i = 0; i < nfilter; i++) { ap = filter[i]; iflp = NULL; iffp = ⇔ memset(iffp, 0, sizeof(*iffp)); if (filtertype[i] == 'N' || filtertype[i] == 'T') { iflp = ap; goto ifonly; } if ((p = strchr(ap, ',')) != NULL) { *p++ = '\0'; iflp = p; } if ((p = strchr(ap, '/')) == NULL) { fatal("no prefixlen specified for '%s'", ap); /*NOTREACHED*/ } *p++ = '\0'; if (inet_pton(AF_INET6, ap, &iffp->iff_addr) != 1) { fatal("invalid prefix specified for '%s'", ap); /*NOTREACHED*/ } errno = 0; ep = NULL; plen = strtoul(p, &ep, 10); if (errno || !*p || *ep || plen > sizeof(iffp->iff_addr) * 8) { fatal("invalid prefix length specified for '%s'", ap); /*NOTREACHED*/ } iffp->iff_plen = plen; applyplen(&iffp->iff_addr, iffp->iff_plen); ifonly: iffp->iff_type = filtertype[i]; if (iflp == NULL || *iflp == '\0') { fatal("no interface specified for '%s'", ap); /*NOTREACHED*/ } /* parse the interface listing portion */ while (iflp) { ifname = iflp; if ((iflp = strchr(iflp, ',')) != NULL) *iflp++ = '\0'; TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (fnmatch(ifname, ifcp->ifc_name, 0) != 0) continue; iffp = malloc(sizeof(*iffp)); if (iffp == NULL) { fatal("malloc of iff"); /*NOTREACHED*/ } memcpy(iffp, &iff, sizeof(*iffp)); #if 0 syslog(LOG_INFO, "Add filter: type %d, ifname %s.", iffp->iff_type, ifname); #endif TAILQ_INSERT_HEAD(&ifcp->ifc_iff_head, iffp, iff_next); } } /* * -A: aggregate configuration. */ if (filtertype[i] != IFIL_TYPE_A) continue; /* put the aggregate to the kernel routing table */ rrt = (struct riprt *)malloc(sizeof(struct riprt)); if (rrt == NULL) { fatal("malloc: rrt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(struct riprt)); rrt->rrt_info.rip6_dest = iff.iff_addr; rrt->rrt_info.rip6_plen = iff.iff_plen; rrt->rrt_info.rip6_metric = 1; rrt->rrt_info.rip6_tag = htons(routetag & 0xffff); rrt->rrt_gw = in6addr_loopback; rrt->rrt_flags = RTF_UP | RTF_REJECT; rrt->rrt_rflags = RRTF_AGGREGATE; rrt->rrt_t = 0; rrt->rrt_index = loopifcp->ifc_index; #if 0 if (getroute(&rrt->rrt_info, &gw)) { #if 0 /* * When the address has already been registered in the * kernel routing table, it should be removed */ delroute(&rrt->rrt_info, &gw); #else /* it is safer behavior */ errno = EINVAL; fatal("%s/%u already in routing table, " "cannot aggregate", inet6_n2p(&rrt->rrt_info.rip6_dest), rrt->rrt_info.rip6_plen); /*NOTREACHED*/ #endif } #endif /* Put the route to the list */ TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); trace(1, "Aggregate: %s/%d for %s\n", inet6_n2p(&iff.iff_addr), iff.iff_plen, loopifcp->ifc_name); /* Add this route to the kernel */ if (nflag) /* do not modify kernel routing table */ continue; addroute(rrt, &in6addr_loopback, loopifcp); } } /***************** utility functions *****************/ /* * Returns a pointer to ifac whose address and prefix length matches * with the address and prefix length specified in the arguments. */ struct ifac * ifa_match(const struct ifc *ifcp, const struct in6_addr *ia, int plen) { struct ifac *ifac; TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { if (IN6_ARE_ADDR_EQUAL(&ifac->ifac_addr, ia) && ifac->ifac_plen == plen) break; } return (ifac); } /* * Return a pointer to riprt structure whose address and prefix length * matches with the address and prefix length found in the argument. * Note: This is not a rtalloc(). Therefore exact match is necessary. */ struct riprt * rtsearch(struct netinfo6 *np) { struct riprt *rrt; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_info.rip6_plen == np->rip6_plen && IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest, &np->rip6_dest)) break; } return (rrt); } int sin6mask2len(const struct sockaddr_in6 *sin6) { return mask2len(&sin6->sin6_addr, sin6->sin6_len - offsetof(struct sockaddr_in6, sin6_addr)); } int mask2len(const struct in6_addr *addr, int lenlim) { int i = 0, j; const u_char *p = (const u_char *)addr; for (j = 0; j < lenlim; j++, p++) { if (*p != 0xff) break; i += 8; } if (j < lenlim) { switch (*p) { #define MASKLEN(m, l) case m: do { i += l; break; } while (0) MASKLEN(0xfe, 7); break; MASKLEN(0xfc, 6); break; MASKLEN(0xf8, 5); break; MASKLEN(0xf0, 4); break; MASKLEN(0xe0, 3); break; MASKLEN(0xc0, 2); break; MASKLEN(0x80, 1); break; #undef MASKLEN } } return i; } void applymask(struct in6_addr *addr, struct in6_addr *mask) { int i; u_long *p, *q; p = (u_long *)addr; q = (u_long *)mask; for (i = 0; i < 4; i++) *p++ &= *q++; } static const u_char plent[8] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe }; void applyplen(struct in6_addr *ia, int plen) { u_char *p; int i; p = ia->s6_addr; for (i = 0; i < 16; i++) { if (plen <= 0) *p = 0; else if (plen < 8) *p &= plent[plen]; p++, plen -= 8; } } static const int pl2m[9] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; struct in6_addr * plen2mask(int n) { static struct in6_addr ia; u_char *p; int i; memset(&ia, 0, sizeof(struct in6_addr)); p = (u_char *)&ia; for (i = 0; i < 16; i++, p++, n -= 8) { if (n >= 8) { *p = 0xff; continue; } *p = pl2m[n]; break; } return &ia; } char * allocopy(char *p) { int len = strlen(p) + 1; char *q = (char *)malloc(len); if (!q) { fatal("malloc"); /*NOTREACHED*/ } strlcpy(q, p, len); return q; } char * hms(void) { static char buf[BUFSIZ]; time_t t; struct tm *tm; t = time(NULL); if ((tm = localtime(&t)) == 0) { fatal("localtime"); /*NOTREACHED*/ } snprintf(buf, sizeof(buf), "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); return buf; } #define RIPRANDDEV 1.0 /* 30 +- 15, max - min = 30 */ int ripinterval(int timer) { double r = rand(); interval = (int)(timer + timer * RIPRANDDEV * (r / RAND_MAX - 0.5)); nextalarm = time(NULL) + interval; return interval; } time_t ripsuptrig(void) { time_t t; double r = rand(); t = (int)(RIP_TRIG_INT6_MIN + (RIP_TRIG_INT6_MAX - RIP_TRIG_INT6_MIN) * (r / RAND_MAX)); sup_trig_update = time(NULL) + t; return t; } void #ifdef __STDC__ fatal(const char *fmt, ...) #else fatal(fmt, va_alist) char *fmt; va_dcl #endif { va_list ap; char buf[1024]; #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); #endif vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); perror(buf); if (errno) syslog(LOG_ERR, "%s: %s", buf, strerror(errno)); else syslog(LOG_ERR, "%s", buf); rtdexit(); } void #ifdef __STDC__ tracet(int level, const char *fmt, ...) #else tracet(level, fmt, va_alist) int level; char *fmt; va_dcl #endif { va_list ap; if (level <= dflag) { #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); #endif fprintf(stderr, "%s: ", hms()); vfprintf(stderr, fmt, ap); va_end(ap); } if (dflag) { #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); #endif if (level > 0) vsyslog(LOG_DEBUG, fmt, ap); else vsyslog(LOG_WARNING, fmt, ap); va_end(ap); } } void #ifdef __STDC__ trace(int level, const char *fmt, ...) #else trace(level, fmt, va_alist) int level; char *fmt; va_dcl #endif { va_list ap; if (level <= dflag) { #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); #endif vfprintf(stderr, fmt, ap); va_end(ap); } if (dflag) { #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); #endif if (level > 0) vsyslog(LOG_DEBUG, fmt, ap); else vsyslog(LOG_WARNING, fmt, ap); va_end(ap); } } unsigned int if_maxindex(void) { struct if_nameindex *p, *p0; unsigned int max = 0; p0 = if_nameindex(); for (p = p0; p && p->if_index && p->if_name; p++) { if (max < p->if_index) max = p->if_index; } if_freenameindex(p0); return max; } struct ifc * ifc_find(char *name) { struct ifc *ifcp; TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (strcmp(name, ifcp->ifc_name) == 0) break; } return (ifcp); } struct iff * iff_find(struct ifc *ifcp, int type) { struct iff *iffp; TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (type == IFIL_TYPE_ANY || type == iffp->iff_type) break; } return (iffp); } void setindex2ifc(int idx, struct ifc *ifcp) { int n, nsize; struct ifc **p; if (!index2ifc) { nindex2ifc = 5; /*initial guess*/ index2ifc = (struct ifc **) malloc(sizeof(*index2ifc) * nindex2ifc); if (index2ifc == NULL) { fatal("malloc"); /*NOTREACHED*/ } memset(index2ifc, 0, sizeof(*index2ifc) * nindex2ifc); } n = nindex2ifc; for (nsize = nindex2ifc; nsize <= idx; nsize *= 2) ; if (n != nsize) { p = (struct ifc **)realloc(index2ifc, sizeof(*index2ifc) * nsize); if (p == NULL) { fatal("realloc"); /*NOTREACHED*/ } memset(p + n, 0, sizeof(*index2ifc) * (nindex2ifc - n)); index2ifc = p; nindex2ifc = nsize; } index2ifc[idx] = ifcp; } Index: stable/10 =================================================================== --- stable/10 (revision 314424) +++ stable/10 (revision 314425) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r311572,311895,311928,311985,312395,312417