diff --git a/lib/lib80211/lib80211_regdomain.c b/lib/lib80211/lib80211_regdomain.c index f5ed236467f5..189d4661c78b 100644 --- a/lib/lib80211/lib80211_regdomain.c +++ b/lib/lib80211/lib80211_regdomain.c @@ -1,740 +1,737 @@ /*- * Copyright (c) 2008 Sam Leffler, Errno Consulting * 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 ``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 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[] = "$FreeBSD$"; -#endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib80211_regdomain.h" #include #define MAXLEVEL 20 struct mystate { XML_Parser parser; struct regdata *rdp; struct regdomain *rd; /* current domain */ struct netband *netband; /* current netband */ struct freqband *freqband; /* current freqband */ struct country *country; /* current country */ netband_head *curband; /* current netband list */ int level; struct sbuf *sbuf[MAXLEVEL]; int nident; }; struct ident { const void *id; void *p; enum { DOMAIN, COUNTRY, FREQBAND } type; }; static void start_element(void *data, const char *name, const char **attr) { #define iseq(a,b) (strcasecmp(a,b) == 0) struct mystate *mt; const void *id, *ref, *mode; int i; mt = data; if (++mt->level == MAXLEVEL) { /* XXX force parser to abort */ return; } mt->sbuf[mt->level] = sbuf_new_auto(); id = ref = mode = NULL; for (i = 0; attr[i] != NULL; i += 2) { if (iseq(attr[i], "id")) { id = attr[i+1]; } else if (iseq(attr[i], "ref")) { ref = attr[i+1]; } else if (iseq(attr[i], "mode")) { mode = attr[i+1]; } else printf("%*.*s[%s = %s]\n", mt->level + 1, mt->level + 1, "", attr[i], attr[i+1]); } if (iseq(name, "rd") && mt->rd == NULL) { if (mt->country == NULL) { mt->rd = calloc(1, sizeof(struct regdomain)); mt->rd->name = strdup(id); mt->nident++; LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next); } else mt->country->rd = (void *)strdup(ref); return; } if (iseq(name, "defcc") && mt->rd != NULL) { mt->rd->cc = (void *)strdup(ref); return; } if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) { if (mode == NULL) { warnx("no mode for netband at line %ld", XML_GetCurrentLineNumber(mt->parser)); return; } if (iseq(mode, "11b")) mt->curband = &mt->rd->bands_11b; else if (iseq(mode, "11g")) mt->curband = &mt->rd->bands_11g; else if (iseq(mode, "11a")) mt->curband = &mt->rd->bands_11a; else if (iseq(mode, "11ng")) mt->curband = &mt->rd->bands_11ng; else if (iseq(mode, "11na")) mt->curband = &mt->rd->bands_11na; else if (iseq(mode, "11ac")) mt->curband = &mt->rd->bands_11ac; else if (iseq(mode, "11acg")) mt->curband = &mt->rd->bands_11acg; else warnx("unknown mode \"%s\" at line %ld", __DECONST(char *, mode), XML_GetCurrentLineNumber(mt->parser)); return; } if (iseq(name, "band") && mt->netband == NULL) { if (mt->curband == NULL) { warnx("band without enclosing netband at line %ld", XML_GetCurrentLineNumber(mt->parser)); return; } mt->netband = calloc(1, sizeof(struct netband)); LIST_INSERT_HEAD(mt->curband, mt->netband, next); return; } if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) { /* XXX handle inlines and merge into table? */ if (mt->netband->band != NULL) { warnx("duplicate freqband at line %ld ignored", XML_GetCurrentLineNumber(mt->parser)); /* XXX complain */ } else mt->netband->band = (void *)strdup(ref); return; } if (iseq(name, "country") && mt->country == NULL) { mt->country = calloc(1, sizeof(struct country)); mt->country->isoname = strdup(id); mt->country->code = NO_COUNTRY; mt->nident++; LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next); return; } if (iseq(name, "freqband") && mt->freqband == NULL) { mt->freqband = calloc(1, sizeof(struct freqband)); mt->freqband->id = strdup(id); mt->nident++; LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next); return; } #undef iseq } static int decode_flag(struct mystate *mt, const char *p, int len) { #define iseq(a,b) (strcasecmp(a,b) == 0) static const struct { const char *name; int len; uint32_t value; } flags[] = { #define FLAG(x) { #x, sizeof(#x)-1, x } FLAG(IEEE80211_CHAN_A), FLAG(IEEE80211_CHAN_B), FLAG(IEEE80211_CHAN_G), FLAG(IEEE80211_CHAN_HT20), FLAG(IEEE80211_CHAN_HT40), FLAG(IEEE80211_CHAN_VHT20), FLAG(IEEE80211_CHAN_VHT40), FLAG(IEEE80211_CHAN_VHT80), FLAG(IEEE80211_CHAN_VHT160), /* * XXX VHT80P80? This likely should be done by * 80MHz chan logic in net80211 / ifconfig. */ FLAG(IEEE80211_CHAN_ST), FLAG(IEEE80211_CHAN_TURBO), FLAG(IEEE80211_CHAN_PASSIVE), FLAG(IEEE80211_CHAN_DFS), FLAG(IEEE80211_CHAN_CCK), FLAG(IEEE80211_CHAN_OFDM), FLAG(IEEE80211_CHAN_2GHZ), FLAG(IEEE80211_CHAN_5GHZ), FLAG(IEEE80211_CHAN_DYN), FLAG(IEEE80211_CHAN_GFSK), FLAG(IEEE80211_CHAN_GSM), FLAG(IEEE80211_CHAN_STURBO), FLAG(IEEE80211_CHAN_HALF), FLAG(IEEE80211_CHAN_QUARTER), FLAG(IEEE80211_CHAN_HT40U), FLAG(IEEE80211_CHAN_HT40D), FLAG(IEEE80211_CHAN_4MSXMIT), FLAG(IEEE80211_CHAN_NOADHOC), FLAG(IEEE80211_CHAN_NOHOSTAP), FLAG(IEEE80211_CHAN_11D), FLAG(IEEE80211_CHAN_FHSS), FLAG(IEEE80211_CHAN_PUREG), FLAG(IEEE80211_CHAN_108A), FLAG(IEEE80211_CHAN_108G), #undef FLAG { "ECM", 3, REQ_ECM }, { "INDOOR", 6, REQ_INDOOR }, { "OUTDOOR", 7, REQ_OUTDOOR }, }; unsigned int i; for (i = 0; i < nitems(flags); i++) if (len == flags[i].len && iseq(p, flags[i].name)) return flags[i].value; warnx("unknown flag \"%.*s\" at line %ld ignored", len, p, XML_GetCurrentLineNumber(mt->parser)); return 0; #undef iseq } static void end_element(void *data, const char *name) { #define iseq(a,b) (strcasecmp(a,b) == 0) struct mystate *mt; int len; char *p; mt = data; sbuf_finish(mt->sbuf[mt->level]); p = sbuf_data(mt->sbuf[mt->level]); len = sbuf_len(mt->sbuf[mt->level]); /* ... */ if (iseq(name, "freqstart") && mt->freqband != NULL) { mt->freqband->freqStart = strtoul(p, NULL, 0); goto done; } if (iseq(name, "freqend") && mt->freqband != NULL) { mt->freqband->freqEnd = strtoul(p, NULL, 0); goto done; } if (iseq(name, "chanwidth") && mt->freqband != NULL) { mt->freqband->chanWidth = strtoul(p, NULL, 0); goto done; } if (iseq(name, "chansep") && mt->freqband != NULL) { mt->freqband->chanSep = strtoul(p, NULL, 0); goto done; } if (iseq(name, "flags")) { if (mt->freqband != NULL) mt->freqband->flags |= decode_flag(mt, p, len); else if (mt->netband != NULL) mt->netband->flags |= decode_flag(mt, p, len); else { warnx("flags without freqband or netband at line %ld ignored", XML_GetCurrentLineNumber(mt->parser)); } goto done; } /* ... */ if (iseq(name, "name") && mt->rd != NULL) { mt->rd->name = strdup(p); goto done; } if (iseq(name, "sku") && mt->rd != NULL) { mt->rd->sku = strtoul(p, NULL, 0); goto done; } if (iseq(name, "netband") && mt->rd != NULL) { mt->curband = NULL; goto done; } /* ... */ if (iseq(name, "freqband") && mt->netband != NULL) { /* XXX handle inline freqbands */ goto done; } if (iseq(name, "maxpower") && mt->netband != NULL) { mt->netband->maxPower = strtoul(p, NULL, 0); goto done; } if (iseq(name, "maxpowerdfs") && mt->netband != NULL) { mt->netband->maxPowerDFS = strtoul(p, NULL, 0); goto done; } if (iseq(name, "maxantgain") && mt->netband != NULL) { mt->netband->maxAntGain = strtoul(p, NULL, 0); goto done; } /* ... */ if (iseq(name, "isocc") && mt->country != NULL) { mt->country->code = strtoul(p, NULL, 0); goto done; } if (iseq(name, "name") && mt->country != NULL) { mt->country->name = strdup(p); goto done; } if (len != 0) { warnx("unexpected XML token \"%s\" data \"%s\" at line %ld", name, p, XML_GetCurrentLineNumber(mt->parser)); /* XXX goto done? */ } /* */ if (iseq(name, "freqband") && mt->freqband != NULL) { /* XXX must have start/end frequencies */ /* XXX must have channel width/sep */ mt->freqband = NULL; goto done; } /* */ if (iseq(name, "rd") && mt->rd != NULL) { mt->rd = NULL; goto done; } /* */ if (iseq(name, "band") && mt->netband != NULL) { if (mt->netband->band == NULL) { warnx("no freqbands for band at line %ld", XML_GetCurrentLineNumber(mt->parser)); } if (mt->netband->maxPower == 0) { warnx("no maxpower for band at line %ld", XML_GetCurrentLineNumber(mt->parser)); } /* default max power w/ DFS to max power */ if (mt->netband->maxPowerDFS == 0) mt->netband->maxPowerDFS = mt->netband->maxPower; mt->netband = NULL; goto done; } /* */ if (iseq(name, "netband") && mt->netband != NULL) { mt->curband = NULL; goto done; } /* */ if (iseq(name, "country") && mt->country != NULL) { /* XXX NO_COUNTRY should be in the net80211 country enum */ if ((int) mt->country->code == NO_COUNTRY) { warnx("no ISO cc for country at line %ld", XML_GetCurrentLineNumber(mt->parser)); } if (mt->country->name == NULL) { warnx("no name for country at line %ld", XML_GetCurrentLineNumber(mt->parser)); } if (mt->country->rd == NULL) { warnx("no regdomain reference for country at line %ld", XML_GetCurrentLineNumber(mt->parser)); } mt->country = NULL; goto done; } done: sbuf_delete(mt->sbuf[mt->level]); mt->sbuf[mt->level--] = NULL; #undef iseq } static void char_data(void *data, const XML_Char *s, int len) { struct mystate *mt; const char *b, *e; mt = data; b = s; e = s + len-1; for (; isspace(*b) && b < e; b++) ; for (; isspace(*e) && e > b; e++) ; if (e != b || (*b != '\0' && !isspace(*b))) sbuf_bcat(mt->sbuf[mt->level], b, e-b+1); } static void * findid(struct regdata *rdp, const void *id, int type) { struct ident *ip; for (ip = rdp->ident; ip->id != NULL; ip++) if ((int) ip->type == type && strcasecmp(ip->id, id) == 0) return ip->p; return NULL; } /* * Parse an regdomain XML configuration and build the internal representation. */ int lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len) { struct mystate *mt; struct regdomain *dp; struct country *cp; struct freqband *fp; struct netband *nb; const void *id; int i, errors; memset(rdp, 0, sizeof(struct regdata)); mt = calloc(1, sizeof(struct mystate)); if (mt == NULL) return ENOMEM; /* parse the XML input */ mt->rdp = rdp; mt->parser = XML_ParserCreate(NULL); XML_SetUserData(mt->parser, mt); XML_SetElementHandler(mt->parser, start_element, end_element); XML_SetCharacterDataHandler(mt->parser, char_data); if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) { warnx("%s: %s at line %ld", __func__, XML_ErrorString(XML_GetErrorCode(mt->parser)), XML_GetCurrentLineNumber(mt->parser)); return -1; } XML_ParserFree(mt->parser); /* setup the identifer table */ rdp->ident = calloc(sizeof(struct ident), mt->nident + 1); if (rdp->ident == NULL) return ENOMEM; free(mt); errors = 0; i = 0; LIST_FOREACH(dp, &rdp->domains, next) { rdp->ident[i].id = dp->name; rdp->ident[i].p = dp; rdp->ident[i].type = DOMAIN; i++; } LIST_FOREACH(fp, &rdp->freqbands, next) { rdp->ident[i].id = fp->id; rdp->ident[i].p = fp; rdp->ident[i].type = FREQBAND; i++; } LIST_FOREACH(cp, &rdp->countries, next) { rdp->ident[i].id = cp->isoname; rdp->ident[i].p = cp; rdp->ident[i].type = COUNTRY; i++; } /* patch references */ LIST_FOREACH(dp, &rdp->domains, next) { if (dp->cc != NULL) { id = dp->cc; dp->cc = findid(rdp, id, COUNTRY); if (dp->cc == NULL) { warnx("undefined country \"%s\"", __DECONST(char *, id)); errors++; } free(__DECONST(char *, id)); } LIST_FOREACH(nb, &dp->bands_11b, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11b band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11g, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11g band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11a, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11a band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11ng, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11ng band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11na, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11na band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11ac, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11ac band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } LIST_FOREACH(nb, &dp->bands_11acg, next) { id = findid(rdp, nb->band, FREQBAND); if (id == NULL) { warnx("undefined 11acg band \"%s\"", __DECONST(char *, nb->band)); errors++; } nb->band = id; } } LIST_FOREACH(cp, &rdp->countries, next) { id = cp->rd; cp->rd = findid(rdp, id, DOMAIN); if (cp->rd == NULL) { warnx("undefined country \"%s\"", __DECONST(char *, id)); errors++; } free(__DECONST(char *, id)); } return errors ? EINVAL : 0; } static void cleanup_bands(netband_head *head) { struct netband *nb; for (;;) { nb = LIST_FIRST(head); if (nb == NULL) break; LIST_REMOVE(nb, next); free(nb); } } /* * Cleanup state/resources for a previously parsed regdomain database. */ void lib80211_regdomain_cleanup(struct regdata *rdp) { free(rdp->ident); rdp->ident = NULL; for (;;) { struct regdomain *dp = LIST_FIRST(&rdp->domains); if (dp == NULL) break; LIST_REMOVE(dp, next); cleanup_bands(&dp->bands_11b); cleanup_bands(&dp->bands_11g); cleanup_bands(&dp->bands_11a); cleanup_bands(&dp->bands_11ng); cleanup_bands(&dp->bands_11na); cleanup_bands(&dp->bands_11ac); cleanup_bands(&dp->bands_11acg); if (dp->name != NULL) free(__DECONST(char *, dp->name)); } for (;;) { struct country *cp = LIST_FIRST(&rdp->countries); if (cp == NULL) break; LIST_REMOVE(cp, next); if (cp->name != NULL) free(__DECONST(char *, cp->name)); free(cp); } for (;;) { struct freqband *fp = LIST_FIRST(&rdp->freqbands); if (fp == NULL) break; LIST_REMOVE(fp, next); free(fp); } } struct regdata * lib80211_alloc_regdata(void) { struct regdata *rdp; struct stat sb; void *xml; int fd; rdp = calloc(1, sizeof(struct regdata)); fd = open(_PATH_REGDOMAIN, O_RDONLY); if (fd < 0) { #ifdef DEBUG warn("%s: open(%s)", __func__, _PATH_REGDOMAIN); #endif free(rdp); return NULL; } if (fstat(fd, &sb) < 0) { #ifdef DEBUG warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN); #endif close(fd); free(rdp); return NULL; } xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (xml == MAP_FAILED) { #ifdef DEBUG warn("%s: mmap", __func__); #endif close(fd); free(rdp); return NULL; } if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) { #ifdef DEBUG warn("%s: error reading regulatory database", __func__); #endif munmap(xml, sb.st_size); close(fd); free(rdp); return NULL; } munmap(xml, sb.st_size); close(fd); return rdp; } void lib80211_free_regdata(struct regdata *rdp) { lib80211_regdomain_cleanup(rdp); free(rdp); } /* * Lookup a regdomain by SKU. */ const struct regdomain * lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku) { const struct regdomain *dp; LIST_FOREACH(dp, &rdp->domains, next) { if (dp->sku == sku) return dp; } return NULL; } /* * Lookup a regdomain by name. */ const struct regdomain * lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name) { const struct regdomain *dp; LIST_FOREACH(dp, &rdp->domains, next) { if (strcasecmp(dp->name, name) == 0) return dp; } return NULL; } /* * Lookup a country by ISO country code. */ const struct country * lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc) { const struct country *cp; LIST_FOREACH(cp, &rdp->countries, next) { if (cp->code == cc) return cp; } return NULL; } /* * Lookup a country by ISO/long name. */ const struct country * lib80211_country_findbyname(const struct regdata *rdp, const char *name) { const struct country *cp; int len; len = strlen(name); LIST_FOREACH(cp, &rdp->countries, next) { if (strcasecmp(cp->isoname, name) == 0) return cp; } LIST_FOREACH(cp, &rdp->countries, next) { if (strncasecmp(cp->name, name, len) == 0) return cp; } return NULL; } diff --git a/lib/libc/net/nslexer.l b/lib/libc/net/nslexer.l index bd3c02dcacf4..ce0f18670e21 100644 --- a/lib/libc/net/nslexer.l +++ b/lib/libc/net/nslexer.l @@ -1,114 +1,108 @@ %{ /* $NetBSD: nslexer.l,v 1.3 1999/01/25 00:16:17 lukem Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Luke Mewburn. * * 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. */ -#include -#if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = - "$FreeBSD$"; -#endif /* LIBC_SCCS and not lint */ - #include "namespace.h" #include #define _NS_PRIVATE #include #include #include #include "un-namespace.h" #include "nsparser.h" %} %option never-interactive %option noinput %option nounput %option yylineno BLANK [ \t] CR \n STRING [a-zA-Z][a-zA-Z0-9_]* %% {BLANK}+ ; /* skip whitespace */ #.* ; /* skip comments */ \\{CR} ; /* allow continuation */ {CR} return NL; [sS][uU][cC][cC][eE][sS][sS] return SUCCESS; [uU][nN][aA][vV][aA][iI][lL] return UNAVAIL; [nN][oO][tT][fF][oO][uU][nN][dD] return NOTFOUND; [tT][rR][yY][aA][gG][aA][iI][nN] return TRYAGAIN; [rR][eE][tT][uU][rR][nN] return RETURN; [cC][oO][nN][tT][iI][nN][uU][eE] return CONTINUE; {STRING} { char *p; int i; if ((p = strdup(yytext)) == NULL) { syslog(LOG_ERR, "NSSWITCH(nslexer): memory allocation failure"); return ERRORTOKEN; } for (i = 0; i < strlen(p); i++) { if (isupper((unsigned char)p[i])) p[i] = tolower((unsigned char)p[i]); } _nsyylval.str = p; return STRING; } . return yytext[0]; %% #undef _nsyywrap int _nsyywrap(void) { return 1; } /* _nsyywrap */ void _nsyyerror(const char *msg) { syslog(LOG_ERR, "NSSWITCH(nslexer): %s line %d: %s at '%s'", _PATH_NS_CONF, yylineno, msg, yytext); } /* _nsyyerror */ diff --git a/lib/libc/powerpc/gen/syncicache.c b/lib/libc/powerpc/gen/syncicache.c index 5192d1356153..6376cb0e576d 100644 --- a/lib/libc/powerpc/gen/syncicache.c +++ b/lib/libc/powerpc/gen/syncicache.c @@ -1,105 +1,100 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1995-1997, 1999 Wolfgang Solfrank. * Copyright (C) 1995-1997, 1999 TooLs GmbH. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: syncicache.c,v 1.2 1999/05/05 12:36:40 tsubai Exp $ */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #if defined(_KERNEL) || defined(_STANDALONE) #include #include #include #endif #include #include #include #ifdef _STANDALONE int cacheline_size = 32; #endif #if !defined(_KERNEL) && !defined(_STANDALONE) #include int cacheline_size = 0; static void getcachelinesize(void); static void getcachelinesize() { static int cachemib[] = { CTL_MACHDEP, CPU_CACHELINE }; int clen; clen = sizeof(cacheline_size); if (sysctl(cachemib, nitems(cachemib), &cacheline_size, &clen, NULL, 0) < 0 || !cacheline_size) { abort(); } } #endif void __syncicache(void *from, int len) { int l, off; char *p; #if !defined(_KERNEL) && !defined(_STANDALONE) if (!cacheline_size) getcachelinesize(); #endif off = (u_int)from & (cacheline_size - 1); l = len += off; p = (char *)from - off; do { __asm __volatile ("dcbst 0,%0" :: "r"(p)); p += cacheline_size; } while ((l -= cacheline_size) > 0); __asm __volatile ("sync"); p = (char *)from - off; do { __asm __volatile ("icbi 0,%0" :: "r"(p)); p += cacheline_size; } while ((len -= cacheline_size) > 0); __asm __volatile ("sync; isync"); } diff --git a/lib/libc/powerpc64/gen/syncicache.c b/lib/libc/powerpc64/gen/syncicache.c index d96529bc3833..7885a36bd1d1 100644 --- a/lib/libc/powerpc64/gen/syncicache.c +++ b/lib/libc/powerpc64/gen/syncicache.c @@ -1,105 +1,100 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1995-1997, 1999 Wolfgang Solfrank. * Copyright (C) 1995-1997, 1999 TooLs GmbH. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: syncicache.c,v 1.2 1999/05/05 12:36:40 tsubai Exp $ */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #if defined(_KERNEL) || defined(_STANDALONE) #include #include #include #endif #include #include #include #ifdef _STANDALONE int cacheline_size = 32; #endif #if !defined(_KERNEL) && !defined(_STANDALONE) #include int cacheline_size = 0; static void getcachelinesize(void); static void getcachelinesize() { static int cachemib[] = { CTL_MACHDEP, CPU_CACHELINE }; long clen; clen = sizeof(cacheline_size); if (sysctl(cachemib, nitems(cachemib), &cacheline_size, &clen, NULL, 0) < 0 || !cacheline_size) { abort(); } } #endif void __syncicache(void *from, int len) { off_t l, off; char *p; #if !defined(_KERNEL) && !defined(_STANDALONE) if (!cacheline_size) getcachelinesize(); #endif off = (uintptr_t)from & (cacheline_size - 1); l = len += off; p = (char *)from - off; do { __asm __volatile ("dcbst 0,%0" :: "r"(p)); p += cacheline_size; } while ((l -= cacheline_size) > 0); __asm __volatile ("sync"); p = (char *)from - off; do { __asm __volatile ("icbi 0,%0" :: "r"(p)); p += cacheline_size; } while ((len -= cacheline_size) > 0); __asm __volatile ("sync; isync"); } diff --git a/lib/msun/src/e_sqrtf.c b/lib/msun/src/e_sqrtf.c index 1fd0cec447fb..f9e2a320f20e 100644 --- a/lib/msun/src/e_sqrtf.c +++ b/lib/msun/src/e_sqrtf.c @@ -1,97 +1,93 @@ /* e_sqrtf.c -- float version of e_sqrt.c. * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. */ /* * ==================================================== * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. * * Developed at SunPro, a Sun Microsystems, Inc. business. * Permission to use, copy, modify, and distribute this * software is freely granted, provided that this notice * is preserved. * ==================================================== */ -#ifndef lint -static char rcsid[] = "$FreeBSD$"; -#endif - #include "math.h" #include "math_private.h" #ifdef USE_BUILTIN_SQRTF float sqrtf(float x) { return (__builtin_sqrtf(x)); } #else static const float one = 1.0, tiny=1.0e-30; float sqrtf(float x) { float z; int32_t sign = (int)0x80000000; int32_t ix,s,q,m,t,i; u_int32_t r; GET_FLOAT_WORD(ix,x); /* take care of Inf and NaN */ if((ix&0x7f800000)==0x7f800000) { return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf sqrt(-inf)=sNaN */ } /* take care of zero */ if(ix<=0) { if((ix&(~sign))==0) return x;/* sqrt(+-0) = +-0 */ else if(ix<0) return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ } /* normalize x */ m = (ix>>23); if(m==0) { /* subnormal x */ for(i=0;(ix&0x00800000)==0;i++) ix<<=1; m -= i-1; } m -= 127; /* unbias exponent */ ix = (ix&0x007fffff)|0x00800000; if(m&1) /* odd m, double x to make it even */ ix += ix; m >>= 1; /* m = [m/2] */ /* generate sqrt(x) bit by bit */ ix += ix; q = s = 0; /* q = sqrt(x) */ r = 0x01000000; /* r = moving bit from right to left */ while(r!=0) { t = s+r; if(t<=ix) { s = t+r; ix -= t; q += r; } ix += ix; r>>=1; } /* use floating add to find out rounding direction */ if(ix!=0) { z = one-tiny; /* trigger inexact flag */ if (z>=one) { z = one+tiny; if (z>one) q += 2; else q += (q&1); } } ix = (q>>1)+0x3f000000; ix += (m <<23); SET_FLOAT_WORD(z,ix); return z; } #endif diff --git a/lib/msun/src/s_modf.c b/lib/msun/src/s_modf.c index ab13191b9004..ffb1702751fa 100644 --- a/lib/msun/src/s_modf.c +++ b/lib/msun/src/s_modf.c @@ -1,79 +1,75 @@ /* @(#)s_modf.c 5.1 93/09/24 */ /* * ==================================================== * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. * * Developed at SunPro, a Sun Microsystems, Inc. business. * Permission to use, copy, modify, and distribute this * software is freely granted, provided that this notice * is preserved. * ==================================================== */ -#ifndef lint -static char rcsid[] = "$FreeBSD$"; -#endif - /* * modf(double x, double *iptr) * return fraction part of x, and return x's integral part in *iptr. * Method: * Bit twiddling. * * Exception: * No exception. */ #include "math.h" #include "math_private.h" static const double one = 1.0; double modf(double x, double *iptr) { int32_t i0,i1,j0; u_int32_t i; EXTRACT_WORDS(i0,i1,x); j0 = ((i0>>20)&0x7ff)-0x3ff; /* exponent of x */ if(j0<20) { /* integer part in high x */ if(j0<0) { /* |x|<1 */ INSERT_WORDS(*iptr,i0&0x80000000,0); /* *iptr = +-0 */ return x; } else { i = (0x000fffff)>>j0; if(((i0&i)|i1)==0) { /* x is integral */ u_int32_t high; *iptr = x; GET_HIGH_WORD(high,x); INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ return x; } else { INSERT_WORDS(*iptr,i0&(~i),0); return x - *iptr; } } } else if (j0>51) { /* no fraction part */ u_int32_t high; if (j0 == 0x400) { /* inf/NaN */ *iptr = x; return 0.0 / x; } *iptr = x*one; GET_HIGH_WORD(high,x); INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ return x; } else { /* fraction part in low x */ i = ((u_int32_t)(0xffffffff))>>(j0-20); if((i1&i)==0) { /* x is integral */ u_int32_t high; *iptr = x; GET_HIGH_WORD(high,x); INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ return x; } else { INSERT_WORDS(*iptr,i0,i1&(~i)); return x - *iptr; } } } diff --git a/lib/msun/src/w_cabsf.c b/lib/msun/src/w_cabsf.c index b5065c8a5683..aedbdef217ca 100644 --- a/lib/msun/src/w_cabsf.c +++ b/lib/msun/src/w_cabsf.c @@ -1,22 +1,17 @@ /* * cabsf() wrapper for hypotf(). * * Written by J.T. Conklin, * Placed into the Public Domain, 1994. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include "math_private.h" float cabsf(float complex z) { return hypotf(crealf(z), cimagf(z)); } diff --git a/libexec/atrun/atrun.c b/libexec/atrun/atrun.c index e9e49146ba7a..ee312591ccd4 100644 --- a/libexec/atrun/atrun.c +++ b/libexec/atrun/atrun.c @@ -1,593 +1,588 @@ /*- * atrun.c - run jobs queued by at; run with root privileges. * * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1993, 1994 Thomas Koenig * * 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. The name of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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, WETHER 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[] = - "$FreeBSD$"; -#endif /* not lint */ - /* System Headers */ #include #include #include #include #ifdef __FreeBSD__ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #else #include #endif #ifdef LOGIN_CAP #include #endif #ifdef PAM #include #include #endif /* Local headers */ #include "gloadavg.h" #define MAIN #include "privs.h" /* Macros */ #ifndef ATJOB_DIR #define ATJOB_DIR "/usr/spool/atjobs/" #endif #ifndef ATSPOOL_DIR #define ATSPOOL_DIR "/usr/spool/atspool/" #endif #ifndef LOADAVG_MX #define LOADAVG_MX 1.5 #endif /* File scope variables */ static const char * const atrun = "atrun"; /* service name for syslog etc. */ static int debug = 0; void perr(const char *fmt, ...); void perrx(const char *fmt, ...); static void usage(void) __dead2; /* Local functions */ static int write_string(int fd, const char* a) { return write(fd, a, strlen(a)); } #undef DEBUG_FORK #ifdef DEBUG_FORK static pid_t myfork(void) { pid_t res; res = fork(); if (res == 0) kill(getpid(),SIGSTOP); return res; } #define fork myfork #endif static void run_file(const char *filename, uid_t uid, gid_t gid) { /* Run a file by spawning off a process which redirects I/O, * spawns a subshell, then waits for it to complete and sends * mail to the user. */ pid_t pid; int fd_out, fd_in; int queue; char mailbuf[MAXLOGNAME], fmt[64]; char *mailname = NULL; FILE *stream; int send_mail = 0; struct stat buf, lbuf; off_t size; struct passwd *pentry; int fflags; long nuid; long ngid; #ifdef PAM pam_handle_t *pamh = NULL; int pam_err; struct pam_conv pamc = { .conv = openpam_nullconv, .appdata_ptr = NULL }; #endif PRIV_START if (chmod(filename, S_IRUSR) != 0) { perr("cannot change file permissions"); } PRIV_END pid = fork(); if (pid == -1) perr("cannot fork"); else if (pid != 0) return; /* Let's see who we mail to. Hopefully, we can read it from * the command file; if not, send it to the owner, or, failing that, * to root. */ pentry = getpwuid(uid); if (pentry == NULL) perrx("Userid %lu not found - aborting job %s", (unsigned long) uid, filename); #ifdef PAM PRIV_START pam_err = pam_start(atrun, pentry->pw_name, &pamc, &pamh); if (pam_err != PAM_SUCCESS) perrx("cannot start PAM: %s", pam_strerror(pamh, pam_err)); pam_err = pam_acct_mgmt(pamh, PAM_SILENT); /* Expired password shouldn't prevent the job from running. */ if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD) perrx("Account %s (userid %lu) unavailable for job %s: %s", pentry->pw_name, (unsigned long)uid, filename, pam_strerror(pamh, pam_err)); pam_end(pamh, pam_err); PRIV_END #endif /* PAM */ PRIV_START stream=fopen(filename, "r"); PRIV_END if (stream == NULL) perr("cannot open input file %s", filename); if ((fd_in = dup(fileno(stream))) <0) perr("error duplicating input file descriptor"); if (fstat(fd_in, &buf) == -1) perr("error in fstat of input file descriptor"); if (lstat(filename, &lbuf) == -1) perr("error in fstat of input file"); if (S_ISLNK(lbuf.st_mode)) perrx("Symbolic link encountered in job %s - aborting", filename); if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) || (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) || (lbuf.st_size!=buf.st_size)) perrx("Somebody changed files from under us for job %s - aborting", filename); if (buf.st_nlink > 1) perrx("Somebody is trying to run a linked script for job %s", filename); if ((fflags = fcntl(fd_in, F_GETFD)) <0) perr("error in fcntl"); fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC); snprintf(fmt, sizeof(fmt), "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d", MAXLOGNAME - 1); if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4) perrx("File %s is in wrong format - aborting", filename); if (mailbuf[0] == '-') perrx("Illegal mail name %s in %s", mailbuf, filename); mailname = mailbuf; if (nuid != uid) perrx("Job %s - userid %ld does not match file uid %lu", filename, nuid, (unsigned long)uid); if (ngid != gid) perrx("Job %s - groupid %ld does not match file gid %lu", filename, ngid, (unsigned long)gid); fclose(stream); if (chdir(ATSPOOL_DIR) < 0) perr("cannot chdir to %s", ATSPOOL_DIR); /* Create a file to hold the output of the job we are about to run. * Write the mail header. */ if((fd_out=open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0) perr("cannot create output file"); write_string(fd_out, "Subject: Output from your job "); write_string(fd_out, filename); write_string(fd_out, "\n\n"); fstat(fd_out, &buf); size = buf.st_size; close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); pid = fork(); if (pid < 0) perr("error in fork"); else if (pid == 0) { char *nul = NULL; char **nenvp = &nul; /* Set up things for the child; we want standard input from the input file, * and standard output and error sent to our output file. */ if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0) perr("error in lseek"); if (dup(fd_in) != STDIN_FILENO) perr("error in I/O redirection"); if (dup(fd_out) != STDOUT_FILENO) perr("error in I/O redirection"); if (dup(fd_out) != STDERR_FILENO) perr("error in I/O redirection"); close(fd_in); close(fd_out); if (chdir(ATJOB_DIR) < 0) perr("cannot chdir to %s", ATJOB_DIR); queue = *filename; PRIV_START nice(tolower(queue) - 'a'); #ifdef LOGIN_CAP /* * For simplicity and safety, set all aspects of the user context * except for a selected subset: Don't set priority, which was * set based on the queue file name according to the tradition. * Don't bother to set environment, including path vars, either * because it will be discarded anyway. Although the job file * should set umask, preset it here just in case. */ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL & ~(LOGIN_SETPRIORITY | LOGIN_SETPATH | LOGIN_SETENV)) != 0) exit(EXIT_FAILURE); /* setusercontext() logged the error */ #else /* LOGIN_CAP */ if (initgroups(pentry->pw_name,pentry->pw_gid)) perr("cannot init group access list"); if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0) perr("cannot change group"); if (setlogin(pentry->pw_name)) perr("cannot set login name"); if (setuid(uid) < 0 || seteuid(uid) < 0) perr("cannot set user id"); #endif /* LOGIN_CAP */ if (chdir(pentry->pw_dir)) chdir("/"); if(execle("/bin/sh","sh",(char *) NULL, nenvp) != 0) perr("exec failed for /bin/sh"); PRIV_END } /* We're the parent. Let's wait. */ close(fd_in); close(fd_out); waitpid(pid, (int *) NULL, 0); /* Send mail. Unlink the output file first, so it is deleted after * the run. */ stat(filename, &buf); if (open(filename, O_RDONLY) != STDIN_FILENO) perr("open of jobfile failed"); unlink(filename); if ((buf.st_size != size) || send_mail) { PRIV_START #ifdef LOGIN_CAP /* * This time set full context to run the mailer. */ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL) != 0) exit(EXIT_FAILURE); /* setusercontext() logged the error */ #else /* LOGIN_CAP */ if (initgroups(pentry->pw_name,pentry->pw_gid)) perr("cannot init group access list"); if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0) perr("cannot change group"); if (setlogin(pentry->pw_name)) perr("cannot set login name"); if (setuid(uid) < 0 || seteuid(uid) < 0) perr("cannot set user id"); #endif /* LOGIN_CAP */ if (chdir(pentry->pw_dir)) chdir("/"); #ifdef __FreeBSD__ execl(_PATH_SENDMAIL, "sendmail", "-F", "Atrun Service", "-odi", "-oem", mailname, (char *) NULL); #else execl(MAIL_CMD, MAIL_CMD, mailname, (char *) NULL); #endif perr("exec failed for mail command"); PRIV_END } exit(EXIT_SUCCESS); } /* Global functions */ /* Needed in gloadavg.c */ void perr(const char *fmt, ...) { const char * const fmtadd = ": %m"; char nfmt[strlen(fmt) + strlen(fmtadd) + 1]; va_list ap; va_start(ap, fmt); if (debug) { vwarn(fmt, ap); } else { snprintf(nfmt, sizeof(nfmt), "%s%s", fmt, fmtadd); vsyslog(LOG_ERR, nfmt, ap); } va_end(ap); exit(EXIT_FAILURE); } void perrx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (debug) vwarnx(fmt, ap); else vsyslog(LOG_ERR, fmt, ap); va_end(ap); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { /* Browse through ATJOB_DIR, checking all the jobfiles wether they should * be executed and or deleted. The queue is coded into the first byte of * the job filename, the date (in minutes since Eon) as a hex number in the * following eight bytes, followed by a dot and a serial number. A file * which has not been executed yet is denoted by its execute - bit set. * For those files which are to be executed, run_file() is called, which forks * off a child which takes care of I/O redirection, forks off another child * for execution and yet another one, optionally, for sending mail. * Files which already have run are removed during the next invocation. */ DIR *spool; struct dirent *dirent; struct stat buf; unsigned long ctm; unsigned long jobno; char queue; time_t now, run_time; char batch_name[] = "Z2345678901234"; uid_t batch_uid; gid_t batch_gid; int c; int run_batch; #ifdef __FreeBSD__ size_t ncpusz; double load_avg = -1; int ncpu; #else double load_avg = LOADAVG_MX; #endif /* We don't need root privileges all the time; running under uid and gid daemon * is fine. */ RELINQUISH_PRIVS_ROOT(DAEMON_UID, DAEMON_GID) openlog(atrun, LOG_PID, LOG_CRON); opterr = 0; while((c=getopt(argc, argv, "dl:"))!= -1) { switch (c) { case 'l': if (sscanf(optarg, "%lf", &load_avg) != 1) perr("garbled option -l"); #ifndef __FreeBSD__ if (load_avg <= 0.) load_avg = LOADAVG_MX; #endif break; case 'd': debug ++; break; case '?': default: usage(); } } if (chdir(ATJOB_DIR) != 0) perr("cannot change to %s", ATJOB_DIR); #ifdef __FreeBSD__ if (load_avg <= 0.) { ncpusz = sizeof(size_t); if (sysctlbyname("hw.ncpu", &ncpu, &ncpusz, NULL, 0) < 0) ncpu = 1; load_avg = LOADAVG_MX * ncpu; } #endif /* Main loop. Open spool directory for reading and look over all the * files in there. If the filename indicates that the job should be run * and the x bit is set, fork off a child which sets its user and group * id to that of the files and exec a /bin/sh which executes the shell * script. Unlink older files if they should no longer be run. For * deletion, their r bit has to be turned on. * * Also, pick the oldest batch job to run, at most one per invocation of * atrun. */ if ((spool = opendir(".")) == NULL) perr("cannot read %s", ATJOB_DIR); if (flock(dirfd(spool), LOCK_EX) == -1) perr("cannot lock %s", ATJOB_DIR); now = time(NULL); run_batch = 0; batch_uid = (uid_t) -1; batch_gid = (gid_t) -1; while ((dirent = readdir(spool)) != NULL) { if (stat(dirent->d_name,&buf) != 0) perr("cannot stat in %s", ATJOB_DIR); /* We don't want directories */ if (!S_ISREG(buf.st_mode)) continue; if (sscanf(dirent->d_name,"%c%5lx%8lx",&queue,&jobno,&ctm) != 3) continue; run_time = (time_t) ctm*60; if ((S_IXUSR & buf.st_mode) && (run_time <=now)) { if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) { run_batch = 1; strlcpy(batch_name, dirent->d_name, sizeof(batch_name)); batch_uid = buf.st_uid; batch_gid = buf.st_gid; } /* The file is executable and old enough */ if (islower(queue)) run_file(dirent->d_name, buf.st_uid, buf.st_gid); } /* Delete older files */ if ((run_time < now) && !(S_IXUSR & buf.st_mode) && (S_IRUSR & buf.st_mode)) unlink(dirent->d_name); } /* run the single batch file, if any */ if (run_batch && (gloadavg() < load_avg)) run_file(batch_name, batch_uid, batch_gid); if (flock(dirfd(spool), LOCK_UN) == -1) perr("cannot unlock %s", ATJOB_DIR); if (closedir(spool) == -1) perr("cannot closedir %s", ATJOB_DIR); closelog(); exit(EXIT_SUCCESS); } static void usage(void) { if (debug) fprintf(stderr, "usage: atrun [-l load_avg] [-d]\n"); else syslog(LOG_ERR, "usage: atrun [-l load_avg] [-d]"); exit(EXIT_FAILURE); } diff --git a/libexec/atrun/gloadavg.c b/libexec/atrun/gloadavg.c index 86651aac3e67..e513183a391c 100644 --- a/libexec/atrun/gloadavg.c +++ b/libexec/atrun/gloadavg.c @@ -1,74 +1,69 @@ /*- * gloadavg.c - get load average for Linux * Copyright (C) 1993 Thomas Koenig * * SPDX-License-Identifier: BSD-2-Clause * * 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. The name of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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, WETHER 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #ifndef __FreeBSD__ #define _POSIX_SOURCE 1 /* System Headers */ #include #else #include #endif /* Local headers */ #include "gloadavg.h" /* Global functions */ void perr(const char *fmt, ...); double gloadavg(void) /* return the current load average as a floating point number, or <0 for * error */ { double result; #ifndef __FreeBSD__ FILE *fp; if((fp=fopen(PROC_DIR "loadavg","r")) == NULL) result = -1.0; else { if(fscanf(fp,"%lf",&result) != 1) result = -1.0; fclose(fp); } #else if (getloadavg(&result, 1) != 1) perr("error in getloadavg"); #endif return result; } diff --git a/libexec/atrun/gloadavg.h b/libexec/atrun/gloadavg.h index f0ae6b45e868..a202cf0b3700 100644 --- a/libexec/atrun/gloadavg.h +++ b/libexec/atrun/gloadavg.h @@ -1,31 +1,28 @@ /*- * gloadavg.h - header for atrun(8) * Copyright (C) 1993 Thomas Koenig * * SPDX-License-Identifier: BSD-2-Clause * * 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. The name of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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, WETHER 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. */ double gloadavg(void); -#if 0 -static char atrun_h_rcsid[] = "$FreeBSD$"; -#endif diff --git a/libexec/comsat/comsat.c b/libexec/comsat/comsat.c index c9d490d2cf6e..138881db9e4a 100644 --- a/libexec/comsat/comsat.c +++ b/libexec/comsat/comsat.c @@ -1,286 +1,284 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)comsat.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int debug = 0; #define dsyslog if (debug) syslog #define MAXIDLE 120 static char hostname[MAXHOSTNAMELEN]; static void jkfprintf(FILE *, char[], char[], off_t); static void mailfor(char *); static void notify(struct utmpx *, char[], off_t, int); static void reapchildren(int); int main(int argc __unused, char *argv[] __unused) { struct sockaddr_in from; socklen_t fromlen; int cc; char msgbuf[256]; /* verify proper invocation */ fromlen = sizeof(from); if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) err(1, "getsockname"); openlog("comsat", LOG_PID, LOG_DAEMON); if (chdir(_PATH_MAILDIR)) { syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAILDIR); (void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0); exit(1); } (void)gethostname(hostname, sizeof(hostname)); (void)signal(SIGTTOU, SIG_IGN); (void)signal(SIGCHLD, reapchildren); for (;;) { cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0); if (cc <= 0) { if (errno != EINTR) sleep(1); errno = 0; continue; } msgbuf[cc] = '\0'; mailfor(msgbuf); sigsetmask(0L); } } static void reapchildren(int signo __unused) { while (wait3(NULL, WNOHANG, NULL) > 0); } static void mailfor(char *name) { struct utmpx *utp; char *cp; char *file; off_t offset; int folder; char buf[sizeof(_PATH_MAILDIR) + sizeof(utp->ut_user) + 1]; char buf2[sizeof(_PATH_MAILDIR) + sizeof(utp->ut_user) + 1]; if (!(cp = strchr(name, '@'))) return; *cp = '\0'; offset = strtoll(cp + 1, NULL, 10); if (!(cp = strchr(cp + 1, ':'))) file = name; else file = cp + 1; sprintf(buf, "%s/%.*s", _PATH_MAILDIR, (int)sizeof(utp->ut_user), name); if (*file != '/') { sprintf(buf2, "%s/%.*s", _PATH_MAILDIR, (int)sizeof(utp->ut_user), file); file = buf2; } folder = strcmp(buf, file); setutxent(); while ((utp = getutxent()) != NULL) if (utp->ut_type == USER_PROCESS && !strcmp(utp->ut_user, name)) notify(utp, file, offset, folder); endutxent(); } static const char *cr; static void notify(struct utmpx *utp, char file[], off_t offset, int folder) { FILE *tp; struct stat stb; struct termios tio; char tty[20]; const char *s = utp->ut_line; if (strncmp(s, "pts/", 4) == 0) s += 4; if (strchr(s, '/')) { /* A slash is an attempt to break security... */ syslog(LOG_AUTH | LOG_NOTICE, "Unexpected `/' in `%s'", utp->ut_line); return; } (void)snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV, (int)sizeof(utp->ut_line), utp->ut_line); if (stat(tty, &stb) == -1 || !(stb.st_mode & (S_IXUSR | S_IXGRP))) { dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_user, tty); return; } dsyslog(LOG_DEBUG, "notify %s on %s", utp->ut_user, tty); switch (fork()) { case -1: syslog(LOG_NOTICE, "fork failed (%m)"); return; case 0: break; default: return; } if ((tp = fopen(tty, "w")) == NULL) { dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno)); _exit(1); } (void)tcgetattr(fileno(tp), &tio); cr = ((tio.c_oflag & (OPOST|ONLCR)) == (OPOST|ONLCR)) ? "\n" : "\n\r"; switch (stb.st_mode & (S_IXUSR | S_IXGRP)) { case S_IXUSR: case (S_IXUSR | S_IXGRP): (void)fprintf(tp, "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s", cr, utp->ut_user, (int)sizeof(hostname), hostname, folder ? cr : "", folder ? "to " : "", folder ? file : "", cr, cr); jkfprintf(tp, utp->ut_user, file, offset); break; case S_IXGRP: (void)fprintf(tp, "\007"); (void)fflush(tp); (void)sleep(1); (void)fprintf(tp, "\007"); break; default: break; } (void)fclose(tp); _exit(0); } static void jkfprintf(FILE *tp, char user[], char file[], off_t offset) { unsigned char *cp, ch; FILE *fi; int linecnt, charcnt, inheader; struct passwd *p; unsigned char line[BUFSIZ]; /* Set effective uid to user in case mail drop is on nfs */ if ((p = getpwnam(user)) != NULL) (void) setuid(p->pw_uid); if ((fi = fopen(file, "r")) == NULL) return; (void)fseeko(fi, offset, SEEK_CUR); /* * Print the first 7 lines or 560 characters of the new mail * (whichever comes first). Skip header crap other than * From, Subject, To, and Date. */ linecnt = 7; charcnt = 560; inheader = 1; while (fgets(line, sizeof(line), fi) != NULL) { if (inheader) { if (line[0] == '\n') { inheader = 0; continue; } if (line[0] == ' ' || line[0] == '\t' || (strncmp(line, "From:", 5) && strncmp(line, "Subject:", 8))) continue; } if (linecnt <= 0 || charcnt <= 0) { (void)fprintf(tp, "...more...%s", cr); (void)fclose(fi); return; } /* strip weird stuff so can't trojan horse stupid terminals */ for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) { /* disable upper controls and enable all other 8bit codes due to lack of locale knowledge */ if (((ch & 0x80) && ch < 0xA0) || (!(ch & 0x80) && !isprint(ch) && !isspace(ch) && ch != '\a' && ch != '\b') ) { if (ch & 0x80) { ch &= ~0x80; (void)fputs("M-", tp); } if (iscntrl(ch)) { ch ^= 0x40; (void)fputc('^', tp); } } (void)fputc(ch, tp); } (void)fputs(cr, tp); --linecnt; } (void)fprintf(tp, "----%s\n", cr); (void)fclose(fi); } diff --git a/libexec/fingerd/fingerd.c b/libexec/fingerd/fingerd.c index ff8d5046a577..dd51064a00a6 100644 --- a/libexec/fingerd/fingerd.c +++ b/libexec/fingerd/fingerd.c @@ -1,245 +1,243 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)fingerd.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #ifdef USE_BLACKLIST #include #endif void logerr(const char *, ...) __printflike(1, 2) __dead2; int main(int argc, char *argv[]) { FILE *fp; int ch; char *lp; struct sockaddr_storage ss; socklen_t sval; int p[2], debug, kflag, logging, pflag, secure; #define ENTRIES 50 char **ap, *av[ENTRIES + 1], **comp, line[1024], *prog; char rhost[MAXHOSTNAMELEN]; prog = _PATH_FINGER; debug = logging = kflag = pflag = secure = 0; openlog("fingerd", LOG_PID | LOG_CONS, LOG_DAEMON); opterr = 0; while ((ch = getopt(argc, argv, "dklp:s")) != -1) switch (ch) { case 'd': debug = 1; break; case 'k': kflag = 1; break; case 'l': logging = 1; break; case 'p': prog = optarg; pflag = 1; break; case 's': secure = 1; break; case '?': default: logerr("illegal option -- %c", optopt); } /* * Enable server-side Transaction TCP. */ if (!debug) { int one = 1; if (setsockopt(STDOUT_FILENO, IPPROTO_TCP, TCP_NOPUSH, &one, sizeof one) < 0) { logerr("setsockopt(TCP_NOPUSH) failed: %m"); } } if (!fgets(line, sizeof(line), stdin)) exit(1); if (!debug && (logging || pflag)) { sval = sizeof(ss); if (getpeername(0, (struct sockaddr *)&ss, &sval) < 0) logerr("getpeername: %s", strerror(errno)); realhostname_sa(rhost, sizeof rhost - 1, (struct sockaddr *)&ss, sval); rhost[sizeof(rhost) - 1] = '\0'; if (pflag) setenv("FINGERD_REMOTE_HOST", rhost, 1); } if (logging) { char *t; char *end; end = memchr(line, 0, sizeof(line)); if (end == NULL) { if ((t = malloc(sizeof(line) + 1)) == NULL) logerr("malloc: %s", strerror(errno)); memcpy(t, line, sizeof(line)); t[sizeof(line)] = 0; } else { if ((t = strdup(line)) == NULL) logerr("strdup: %s", strerror(errno)); } for (end = t; *end; end++) if (*end == '\n' || *end == '\r') *end = ' '; syslog(LOG_NOTICE, "query from %s: `%s'", rhost, t); } comp = &av[2]; av[3] = "--"; if (kflag) *comp-- = "-k"; for (lp = line, ap = &av[4];;) { *ap = strtok(lp, " \t\r\n"); if (!*ap) { if (secure && ap == &av[4]) { #ifdef USE_BLACKLIST blacklist(1, STDIN_FILENO, "nousername"); #endif puts("must provide username\r\n"); exit(1); } break; } if (secure && strchr(*ap, '@')) { #ifdef USE_BLACKLIST blacklist(1, STDIN_FILENO, "noforwarding"); #endif puts("forwarding service denied\r\n"); exit(1); } /* RFC742: "/[Ww]" == "-l" */ if ((*ap)[0] == '/' && ((*ap)[1] == 'W' || (*ap)[1] == 'w')) { *comp-- = "-l"; } else if (++ap == av + ENTRIES) { *ap = NULL; break; } lp = NULL; } if ((lp = strrchr(prog, '/')) != NULL) *comp = ++lp; else *comp = prog; if (pipe(p) < 0) logerr("pipe: %s", strerror(errno)); if (debug) { fprintf(stderr, "%s", prog); for (ap = comp; *ap != NULL; ++ap) fprintf(stderr, " %s", *ap); fprintf(stderr, "\n"); } switch(vfork()) { case 0: (void)close(p[0]); if (p[1] != STDOUT_FILENO) { (void)dup2(p[1], STDOUT_FILENO); (void)close(p[1]); } dup2(STDOUT_FILENO, STDERR_FILENO); #ifdef USE_BLACKLIST blacklist(0, STDIN_FILENO, "success"); #endif execv(prog, comp); write(STDERR_FILENO, prog, strlen(prog)); #define MSG ": cannot execute\n" write(STDERR_FILENO, MSG, strlen(MSG)); #undef MSG _exit(1); case -1: logerr("fork: %s", strerror(errno)); } (void)close(p[1]); if (!(fp = fdopen(p[0], "r"))) logerr("fdopen: %s", strerror(errno)); while ((ch = getc(fp)) != EOF) { if (ch == '\n') putchar('\r'); putchar(ch); } exit(0); } #include void logerr(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)vsyslog(LOG_ERR, fmt, ap); va_end(ap); exit(1); /* NOTREACHED */ } diff --git a/libexec/getty/init.c b/libexec/getty/init.c index 79b9601a2be1..e09cbf2c3e94 100644 --- a/libexec/getty/init.c +++ b/libexec/getty/init.c @@ -1,156 +1,154 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)from: init.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Getty table initializations. * * Melbourne getty. */ #include #include #include "gettytab.h" #include "extern.h" #include "pathnames.h" static char loginmsg[] = "login: "; static char nullstr[] = ""; static char loginprg[] = _PATH_LOGIN; static char datefmt[] = "%+"; #define M(a) (char *)(&omode.c_cc[a]) struct gettystrs gettystrs[] = { { "nx", NULL, NULL }, /* next table */ { "cl", NULL, NULL }, /* screen clear characters */ { "im", NULL, NULL }, /* initial message */ { "lm", loginmsg, NULL }, /* login message */ { "er", M(VERASE), NULL }, /* erase character */ { "kl", M(VKILL), NULL }, /* kill character */ { "et", M(VEOF), NULL }, /* eof chatacter (eot) */ { "pc", nullstr, NULL }, /* pad character */ { "tt", NULL, NULL }, /* terminal type */ { "ev", NULL, NULL }, /* environment */ { "lo", loginprg, NULL }, /* login program */ { "hn", hostname, NULL }, /* host name */ { "he", NULL, NULL }, /* host name edit */ { "in", M(VINTR), NULL }, /* interrupt char */ { "qu", M(VQUIT), NULL }, /* quit char */ { "xn", M(VSTART), NULL }, /* XON (start) char */ { "xf", M(VSTOP), NULL }, /* XOFF (stop) char */ { "bk", M(VEOL), NULL }, /* brk char (alt \n) */ { "su", M(VSUSP), NULL }, /* suspend char */ { "ds", M(VDSUSP), NULL }, /* delayed suspend */ { "rp", M(VREPRINT), NULL }, /* reprint char */ { "fl", M(VDISCARD), NULL }, /* flush output */ { "we", M(VWERASE), NULL }, /* word erase */ { "ln", M(VLNEXT), NULL }, /* literal next */ { "Lo", NULL, NULL }, /* locale for strftime() */ { "pp", NULL, NULL }, /* ppp login program */ { "if", NULL, NULL }, /* sysv-like 'issue' filename */ { "ic", NULL, NULL }, /* modem init-chat */ { "ac", NULL, NULL }, /* modem answer-chat */ { "al", NULL, NULL }, /* user to auto-login */ { "df", datefmt, NULL }, /* format for strftime() */ { "iM" , NULL, NULL }, /* initial message program */ { NULL, NULL, NULL } }; struct gettynums gettynums[] = { { "is", 0, 0, 0 }, /* input speed */ { "os", 0, 0, 0 }, /* output speed */ { "sp", 0, 0, 0 }, /* both speeds */ { "nd", 0, 0, 0 }, /* newline delay */ { "cd", 0, 0, 0 }, /* carriage-return delay */ { "td", 0, 0, 0 }, /* tab delay */ { "fd", 0, 0, 0 }, /* form-feed delay */ { "bd", 0, 0, 0 }, /* backspace delay */ { "to", 0, 0, 0 }, /* timeout */ { "f0", 0, 0, 0 }, /* output flags */ { "f1", 0, 0, 0 }, /* input flags */ { "f2", 0, 0, 0 }, /* user mode flags */ { "pf", 0, 0, 0 }, /* delay before flush at 1st prompt */ { "c0", 0, 0, 0 }, /* output c_flags */ { "c1", 0, 0, 0 }, /* input c_flags */ { "c2", 0, 0, 0 }, /* user mode c_flags */ { "i0", 0, 0, 0 }, /* output i_flags */ { "i1", 0, 0, 0 }, /* input i_flags */ { "i2", 0, 0, 0 }, /* user mode i_flags */ { "l0", 0, 0, 0 }, /* output l_flags */ { "l1", 0, 0, 0 }, /* input l_flags */ { "l2", 0, 0, 0 }, /* user mode l_flags */ { "o0", 0, 0, 0 }, /* output o_flags */ { "o1", 0, 0, 0 }, /* input o_flags */ { "o2", 0, 0, 0 }, /* user mode o_flags */ { "de", 0, 0, 0 }, /* delay before sending 1st prompt */ { "rt", 0, 0, 0 }, /* reset timeout */ { "ct", 0, 0, 0 }, /* chat script timeout */ { "dc", 0, 0, 0 }, /* debug chat script value */ { NULL, 0, 0, 0 } }; struct gettyflags gettyflags[] = { { "ht", 0, 0, 0, 0 }, /* has tabs */ { "nl", 1, 0, 0, 0 }, /* has newline char */ { "ep", 0, 0, 0, 0 }, /* even parity */ { "op", 0, 0, 0, 0 }, /* odd parity */ { "ap", 0, 0, 0, 0 }, /* any parity */ { "ec", 1, 0, 0, 0 }, /* no echo */ { "co", 0, 0, 0, 0 }, /* console special */ { "cb", 0, 0, 0, 0 }, /* crt backspace */ { "ck", 0, 0, 0, 0 }, /* crt kill */ { "ce", 0, 0, 0, 0 }, /* crt erase */ { "pe", 0, 0, 0, 0 }, /* printer erase */ { "rw", 1, 0, 0, 0 }, /* don't use raw */ { "xc", 1, 0, 0, 0 }, /* don't ^X ctl chars */ { "lc", 0, 0, 0, 0 }, /* terminal las lower case */ { "uc", 0, 0, 0, 0 }, /* terminal has no lower case */ { "ig", 0, 0, 0, 0 }, /* ignore garbage */ { "ps", 0, 0, 0, 0 }, /* do port selector speed select */ { "hc", 1, 0, 0, 0 }, /* don't set hangup on close */ { "ub", 0, 0, 0, 0 }, /* unbuffered output */ { "ab", 0, 0, 0, 0 }, /* auto-baud detect with '\r' */ { "dx", 0, 0, 0, 0 }, /* set decctlq */ { "np", 0, 0, 0, 0 }, /* no parity at all (8bit chars) */ { "mb", 0, 0, 0, 0 }, /* do MDMBUF flow control */ { "hw", 0, 0, 0, 0 }, /* do CTSRTS flow control */ { "nc", 0, 0, 0, 0 }, /* set clocal (no carrier) */ { "pl", 0, 0, 0, 0 }, /* use PPP instead of login(1) */ { NULL, 0, 0, 0, 0 } }; diff --git a/libexec/getty/subr.c b/libexec/getty/subr.c index 68682df6d5bd..2c262e0968ca 100644 --- a/libexec/getty/subr.c +++ b/libexec/getty/subr.c @@ -1,684 +1,682 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)from: subr.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Melbourne getty. */ #include #include #include #include #include #include #include #include #include #include #include "gettytab.h" #include "pathnames.h" #include "extern.h" /* * Get a table entry. */ void gettable(const char *name) { char *buf = NULL; struct gettystrs *sp; struct gettynums *np; struct gettyflags *fp; long n; int l; char *p; static char path_gettytab[PATH_MAX]; char *dba[2]; static int firsttime = 1; strlcpy(path_gettytab, _PATH_GETTYTAB, sizeof(path_gettytab)); dba[0] = path_gettytab; dba[1] = NULL; if (firsttime) { /* * we need to strdup() anything in the strings array * initially in order to simplify things later */ for (sp = gettystrs; sp->field; sp++) if (sp->value != NULL) { /* handle these ones more carefully */ if (sp >= &gettystrs[4] && sp <= &gettystrs[6]) l = 2; else l = strlen(sp->value) + 1; if ((p = malloc(l)) != NULL) strlcpy(p, sp->value, l); /* * replace, even if NULL, else we'll * have problems with free()ing static mem */ sp->value = p; } firsttime = 0; } switch (cgetent(&buf, dba, name)) { case 1: syslog(LOG_ERR, "getty: couldn't resolve 'tc=' in gettytab '%s'", name); return; case 0: break; case -1: syslog(LOG_ERR, "getty: unknown gettytab entry '%s'", name); return; case -2: syslog(LOG_ERR, "getty: retrieving gettytab entry '%s': %m", name); return; case -3: syslog(LOG_ERR, "getty: recursive 'tc=' reference gettytab entry '%s'", name); return; default: syslog(LOG_ERR, "getty: unexpected cgetent() error for entry '%s'", name); return; } for (sp = gettystrs; sp->field; sp++) { if ((l = cgetstr(buf, sp->field, &p)) >= 0) { if (sp->value) { /* prefer existing value */ if (strcmp(p, sp->value) != 0) free(sp->value); else { free(p); p = sp->value; } } sp->value = p; } else if (l == -1) { free(sp->value); sp->value = NULL; } } for (np = gettynums; np->field; np++) { if (cgetnum(buf, np->field, &n) == -1) np->set = 0; else { np->set = 1; np->value = n; } } for (fp = gettyflags; fp->field; fp++) { if (cgetcap(buf, fp->field, ':') == NULL) fp->set = 0; else { fp->set = 1; fp->value = 1 ^ fp->invrt; } } free(buf); } void gendefaults(void) { struct gettystrs *sp; struct gettynums *np; struct gettyflags *fp; for (sp = gettystrs; sp->field; sp++) if (sp->value) sp->defalt = strdup(sp->value); for (np = gettynums; np->field; np++) if (np->set) np->defalt = np->value; for (fp = gettyflags; fp->field; fp++) if (fp->set) fp->defalt = fp->value; else fp->defalt = fp->invrt; } void setdefaults(void) { struct gettystrs *sp; struct gettynums *np; struct gettyflags *fp; for (sp = gettystrs; sp->field; sp++) if (!sp->value) sp->value = !sp->defalt ? sp->defalt : strdup(sp->defalt); for (np = gettynums; np->field; np++) if (!np->set) np->value = np->defalt; for (fp = gettyflags; fp->field; fp++) if (!fp->set) fp->value = fp->defalt; } static char ** charnames[] = { &ER, &KL, &IN, &QU, &XN, &XF, &ET, &BK, &SU, &DS, &RP, &FL, &WE, &LN, 0 }; #define CV(a) (char *)(&tmode.c_cc[a]) static char * charvars[] = { CV(VERASE), CV(VKILL), CV(VINTR), CV(VQUIT), CV(VSTART), CV(VSTOP), CV(VEOF), CV(VEOL), CV(VSUSP), CV(VDSUSP), CV(VREPRINT), CV(VDISCARD), CV(VWERASE), CV(VLNEXT), 0 }; void setchars(void) { int i; const char *p; for (i = 0; charnames[i]; i++) { p = *charnames[i]; if (p && *p) *charvars[i] = *p; else *charvars[i] = _POSIX_VDISABLE; } } /* Macros to clear/set/test flags. */ #define SET(t, f) (t) |= (f) #define CLR(t, f) (t) &= ~(f) #define ISSET(t, f) ((t) & (f)) void set_flags(int n) { tcflag_t iflag, oflag, cflag, lflag; switch (n) { case 0: if (C0set && I0set && L0set && O0set) { tmode.c_cflag = C0; tmode.c_iflag = I0; tmode.c_lflag = L0; tmode.c_oflag = O0; return; } break; case 1: if (C1set && I1set && L1set && O1set) { tmode.c_cflag = C1; tmode.c_iflag = I1; tmode.c_lflag = L1; tmode.c_oflag = O1; return; } break; default: if (C2set && I2set && L2set && O2set) { tmode.c_cflag = C2; tmode.c_iflag = I2; tmode.c_lflag = L2; tmode.c_oflag = O2; return; } break; } iflag = omode.c_iflag; oflag = omode.c_oflag; cflag = omode.c_cflag; lflag = omode.c_lflag; if (NP) { CLR(cflag, CSIZE|PARENB); SET(cflag, CS8); CLR(iflag, ISTRIP|INPCK|IGNPAR); } else if (AP || EP || OP) { CLR(cflag, CSIZE); SET(cflag, CS7|PARENB); SET(iflag, ISTRIP); if (OP && !EP) { SET(iflag, INPCK|IGNPAR); SET(cflag, PARODD); if (AP) CLR(iflag, INPCK); } else if (EP && !OP) { SET(iflag, INPCK|IGNPAR); CLR(cflag, PARODD); if (AP) CLR(iflag, INPCK); } else if (AP || (EP && OP)) { CLR(iflag, INPCK|IGNPAR); CLR(cflag, PARODD); } } /* else, leave as is */ #if 0 if (UC) f |= LCASE; #endif if (HC) SET(cflag, HUPCL); else CLR(cflag, HUPCL); if (MB) SET(cflag, MDMBUF); else CLR(cflag, MDMBUF); if (HW) SET(cflag, CRTSCTS); else CLR(cflag, CRTSCTS); if (NL) { SET(iflag, ICRNL); SET(oflag, ONLCR|OPOST); } else { CLR(iflag, ICRNL); CLR(oflag, ONLCR); } if (!HT) SET(oflag, OXTABS|OPOST); else CLR(oflag, OXTABS); #ifdef XXX_DELAY SET(f, delaybits()); #endif if (n == 1) { /* read mode flags */ if (RW) { iflag = 0; CLR(oflag, OPOST); CLR(cflag, CSIZE|PARENB); SET(cflag, CS8); lflag = 0; } else { CLR(lflag, ICANON); } goto out; } if (n == 0) goto out; #if 0 if (CB) SET(f, CRTBS); #endif if (CE) SET(lflag, ECHOE); else CLR(lflag, ECHOE); if (CK) SET(lflag, ECHOKE); else CLR(lflag, ECHOKE); if (PE) SET(lflag, ECHOPRT); else CLR(lflag, ECHOPRT); if (EC) SET(lflag, ECHO); else CLR(lflag, ECHO); if (XC) SET(lflag, ECHOCTL); else CLR(lflag, ECHOCTL); if (DX) SET(lflag, IXANY); else CLR(lflag, IXANY); out: tmode.c_iflag = iflag; tmode.c_oflag = oflag; tmode.c_cflag = cflag; tmode.c_lflag = lflag; } #ifdef XXX_DELAY struct delayval { unsigned delay; /* delay in ms */ int bits; }; /* * below are random guesses, I can't be bothered checking */ struct delayval crdelay[] = { { 1, CR1 }, { 2, CR2 }, { 3, CR3 }, { 83, CR1 }, { 166, CR2 }, { 0, CR3 }, }; struct delayval nldelay[] = { { 1, NL1 }, /* special, calculated */ { 2, NL2 }, { 3, NL3 }, { 100, NL2 }, { 0, NL3 }, }; struct delayval bsdelay[] = { { 1, BS1 }, { 0, 0 }, }; struct delayval ffdelay[] = { { 1, FF1 }, { 1750, FF1 }, { 0, FF1 }, }; struct delayval tbdelay[] = { { 1, TAB1 }, { 2, TAB2 }, { 3, XTABS }, /* this is expand tabs */ { 100, TAB1 }, { 0, TAB2 }, }; int delaybits(void) { int f; f = adelay(CD, crdelay); f |= adelay(ND, nldelay); f |= adelay(FD, ffdelay); f |= adelay(TD, tbdelay); f |= adelay(BD, bsdelay); return (f); } int adelay(int ms, struct delayval *dp) { if (ms == 0) return (0); while (dp->delay && ms > dp->delay) dp++; return (dp->bits); } #endif char editedhost[MAXHOSTNAMELEN]; void edithost(const char *pattern) { regex_t regex; regmatch_t *match; int found; if (pattern == NULL || *pattern == '\0') goto copyasis; if (regcomp(®ex, pattern, REG_EXTENDED) != 0) goto copyasis; match = calloc(regex.re_nsub + 1, sizeof(*match)); if (match == NULL) { regfree(®ex); goto copyasis; } found = !regexec(®ex, HN, regex.re_nsub + 1, match, 0); if (found) { size_t subex, totalsize; /* * We found a match. If there were no parenthesized * subexpressions in the pattern, use entire matched * string as ``editedhost''; otherwise use the first * matched subexpression. */ subex = !!regex.re_nsub; totalsize = match[subex].rm_eo - match[subex].rm_so + 1; strlcpy(editedhost, HN + match[subex].rm_so, totalsize > sizeof(editedhost) ? sizeof(editedhost) : totalsize); } free(match); regfree(®ex); if (found) return; /* * In case of any errors, or if the pattern did not match, pass * the original hostname as is. */ copyasis: strlcpy(editedhost, HN, sizeof(editedhost)); } static struct speedtab { int speed; int uxname; } speedtab[] = { { 50, B50 }, { 75, B75 }, { 110, B110 }, { 134, B134 }, { 150, B150 }, { 200, B200 }, { 300, B300 }, { 600, B600 }, { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, { 4800, B4800 }, { 9600, B9600 }, { 19200, EXTA }, { 19, EXTA }, /* for people who say 19.2K */ { 38400, EXTB }, { 38, EXTB }, { 7200, EXTB }, /* alternative */ { 57600, B57600 }, { 115200, B115200 }, { 230400, B230400 }, { 0, 0 } }; int speed(int val) { struct speedtab *sp; if (val <= B230400) return (val); for (sp = speedtab; sp->speed; sp++) if (sp->speed == val) return (sp->uxname); return (B300); /* default in impossible cases */ } void makeenv(char *env[]) { static char termbuf[128] = "TERM="; char *p, *q; char **ep; ep = env; if (TT && *TT) { strlcat(termbuf, TT, sizeof(termbuf)); *ep++ = termbuf; } if ((p = EV)) { q = p; while ((q = strchr(q, ','))) { *q++ = '\0'; *ep++ = p; p = q; } if (*p) *ep++ = p; } *ep = (char *)0; } /* * This speed select mechanism is written for the Develcon DATASWITCH. * The Develcon sends a string of the form "B{speed}\n" at a predefined * baud rate. This string indicates the user's actual speed. * The routine below returns the terminal type mapped from derived speed. */ static struct portselect { const char *ps_baud; const char *ps_type; } portspeeds[] = { { "B110", "std.110" }, { "B134", "std.134" }, { "B150", "std.150" }, { "B300", "std.300" }, { "B600", "std.600" }, { "B1200", "std.1200" }, { "B2400", "std.2400" }, { "B4800", "std.4800" }, { "B9600", "std.9600" }, { "B19200", "std.19200" }, { NULL, NULL } }; const char * portselector(void) { char c, baud[20]; const char *type = "default"; struct portselect *ps; size_t len; alarm(5*60); for (len = 0; len < sizeof (baud) - 1; len++) { if (read(STDIN_FILENO, &c, 1) <= 0) break; c &= 0177; if (c == '\n' || c == '\r') break; if (c == 'B') len = 0; /* in case of leading garbage */ baud[len] = c; } baud[len] = '\0'; for (ps = portspeeds; ps->ps_baud; ps++) if (strcmp(ps->ps_baud, baud) == 0) { type = ps->ps_type; break; } sleep(2); /* wait for connection to complete */ return (type); } /* * This auto-baud speed select mechanism is written for the Micom 600 * portselector. Selection is done by looking at how the character '\r' * is garbled at the different speeds. */ const char * autobaud(void) { struct pollfd set[1]; struct timespec timeout; char c; const char *type = "9600-baud"; (void)tcflush(0, TCIOFLUSH); set[0].fd = STDIN_FILENO; set[0].events = POLLIN; if (poll(set, 1, 5000) <= 0) return (type); if (read(STDIN_FILENO, &c, sizeof(char)) != sizeof(char)) return (type); timeout.tv_sec = 0; timeout.tv_nsec = 20000; (void)nanosleep(&timeout, NULL); (void)tcflush(0, TCIOFLUSH); switch (c & 0377) { case 0200: /* 300-baud */ type = "300-baud"; break; case 0346: /* 1200-baud */ type = "1200-baud"; break; case 015: /* 2400-baud */ case 0215: type = "2400-baud"; break; default: /* 4800-baud */ type = "4800-baud"; break; case 0377: /* 9600-baud */ type = "9600-baud"; break; } return (type); } diff --git a/libexec/mknetid/hash.c b/libexec/mknetid/hash.c index d340142aad90..5375b80fbe3b 100644 --- a/libexec/mknetid/hash.c +++ b/libexec/mknetid/hash.c @@ -1,170 +1,165 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1995 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. */ #include #include #include #include #include "hash.h" -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - /* * This hash function is stolen directly from the * Berkeley DB package. It already exists inside libc, but * it's declared static which prevents us from calling it * from here. */ /* * OZ's original sdbm hash */ u_int32_t hash(const void *keyarg, size_t len) { const u_char *key; size_t loop; u_int32_t h; #define HASHC h = *key++ + 65599 * h h = 0; key = keyarg; if (len > 0) { loop = (len + 8 - 1) >> 3; switch (len & (8 - 1)) { case 0: do { HASHC; /* FALLTHROUGH */ case 7: HASHC; /* FALLTHROUGH */ case 6: HASHC; /* FALLTHROUGH */ case 5: HASHC; /* FALLTHROUGH */ case 4: HASHC; /* FALLTHROUGH */ case 3: HASHC; /* FALLTHROUGH */ case 2: HASHC; /* FALLTHROUGH */ case 1: HASHC; } while (--loop); } } return (h); } /* * Generate a hash value for a given key (character string). * We mask off all but the lower 8 bits since our table array * can only hole 256 elements. */ u_int32_t hashkey(char *key) { if (key == NULL) return (-1); return(hash((void *)key, strlen(key)) & HASH_MASK); } /* Find an entry in the hash table (may be hanging off a linked list). */ struct grouplist *lookup(struct member_entry *table[], char *key) { struct member_entry *cur; cur = table[hashkey(key)]; while (cur) { if (!strcmp(cur->key, key)) return(cur->groups); cur = cur->next; } return(NULL); } struct grouplist dummy = { 99999, NULL }; /* * Store a group member entry and/or update its grouplist. */ void mstore (struct member_entry *table[], char *key, int gid, int dup) { struct member_entry *cur, *new; struct grouplist *tmp; u_int32_t i; i = hashkey(key); cur = table[i]; if (!dup) { tmp = (struct grouplist *)malloc(sizeof(struct grouplist)); tmp->groupid = gid; tmp->next = NULL; } /* Check if all we have to do is insert a new groupname. */ while (cur) { if (!dup && !strcmp(cur->key, key)) { tmp->next = cur->groups; cur->groups = tmp; return; } cur = cur->next; } /* Didn't find a match -- add the whole mess to the table. */ new = (struct member_entry *)malloc(sizeof(struct member_entry)); new->key = strdup(key); if (!dup) new->groups = tmp; else new->groups = (struct grouplist *)&dummy; new->next = table[i]; table[i] = new; return; } diff --git a/libexec/mknetid/mknetid.c b/libexec/mknetid/mknetid.c index 3a39b4b3e675..a5c8281ef34d 100644 --- a/libexec/mknetid/mknetid.c +++ b/libexec/mknetid/mknetid.c @@ -1,308 +1,303 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1995, 1996 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. * * netid map generator program * * Written by Bill Paul * Center for Telecommunications Research * Columbia University, New York City */ #include #include #include #include #include #include #include #include #include #include #include #include #include "hash.h" -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #define LINSIZ 1024 #define OPSYS "unix" /* Default location of group file. */ char *groupfile = _PATH_GROUP; /* Default location of master.passwd file. */ char *passfile = _PATH_PASSWD; /* Default location of hosts file. */ char *hostsfile = _PATH_HOSTS; /* Default location of netid file */ char *netidfile = "/etc/netid"; /* * Stored hash table of 'reverse' group member database * which we will construct. */ struct member_entry *mtable[TABLESIZE]; /* * Dupe table: used to keep track of entries so we don't * print the same thing twice. */ struct member_entry *dtable[TABLESIZE]; extern struct group *_getgrent(void); extern int _setgrent(void); extern void _endgrent(void); static void usage(void) { fprintf (stderr, "%s\n%s\n", "usage: mknetid [-q] [-g group_file] [-p passwd_file] [-h hosts_file]", " [-n netid_file] [-d domain]"); exit(1); } extern FILE *_gr_fp; int main(int argc, char *argv[]) { FILE *gfp, *pfp, *hfp, *nfp; char readbuf[LINSIZ]; char writebuf[LINSIZ]; struct group *gr; struct grouplist *glist; char *domain; int ch; gid_t i; char *ptr, *pidptr, *gidptr, *hptr; int quiet = 0; domain = NULL; while ((ch = getopt(argc, argv, "g:p:h:n:d:q")) != -1) { switch(ch) { case 'g': groupfile = optarg; break; case 'p': passfile = optarg; break; case 'h': hostsfile = optarg; break; case 'n': netidfile = optarg; break; case 'd': domain = optarg; break; case 'q': quiet++; break; default: usage(); break; } } if (domain == NULL) { if (yp_get_default_domain(&domain)) errx(1, "no domain name specified and default \ domain not set"); } if ((gfp = fopen(groupfile, "r")) == NULL) { err(1, "%s", groupfile); } if ((pfp = fopen(passfile, "r")) == NULL) { err(1, "%s", passfile); } if ((hfp = fopen(hostsfile, "r")) == NULL) { err(1, "%s", hostsfile); } if ((nfp = fopen(netidfile, "r")) == NULL) { /* netid is optional -- just continue */ nfp = NULL; } _gr_fp = gfp; /* Load all the group membership info into a hash table. */ _setgrent(); while((gr = _getgrent()) != NULL) { while(*gr->gr_mem) { mstore(mtable, *gr->gr_mem, gr->gr_gid, 0); gr->gr_mem++; } } fclose(gfp); _endgrent(); /* * Now parse the passwd database, spewing out the extra * group information we just stored if necessary. */ while(fgets(readbuf, LINSIZ, pfp)) { /* Ignore comments: ^[ \t]*# */ for (ptr = readbuf; *ptr != '\0'; ptr++) if (*ptr != ' ' && *ptr != '\t') break; if (*ptr == '#' || *ptr == '\0') continue; if ((ptr = strchr(readbuf, ':')) == NULL) { warnx("bad passwd file entry: %s", readbuf); continue; } *ptr = '\0'; ptr++; if ((ptr = strchr(ptr, ':')) == NULL) { warnx("bad passwd file entry: %s", readbuf); continue; } *ptr = '\0'; ptr++; pidptr = ptr; if ((ptr = strchr(ptr, ':')) == NULL) { warnx("bad passwd file entry: %s", readbuf); continue; } *ptr = '\0'; ptr++; gidptr = ptr; if ((ptr = strchr(ptr, ':')) == NULL) { warnx("bad passwd file entry: %s", readbuf); continue; } *ptr = '\0'; i = atol(gidptr); snprintf(writebuf, sizeof(writebuf), "%s.%s@%s", OPSYS, pidptr, domain); if (lookup(dtable, writebuf)) { if (!quiet) warnx("duplicate netid '%s.%s@%s' -- skipping", OPSYS, pidptr, domain); continue; } else { mstore(dtable, writebuf, 0, 1); } printf("%s.%s@%s %s:%s", OPSYS, pidptr, domain, pidptr, gidptr); if ((glist = lookup(mtable, (char *)&readbuf)) != NULL) { while(glist) { if (glist->groupid != i) printf(",%lu", (u_long)glist->groupid); glist = glist->next; } } printf ("\n"); } fclose(pfp); /* * Now parse the hosts database (this part sucks). */ while ((ptr = fgets(readbuf, LINSIZ, hfp))) { if (*ptr == '#') continue; if (!(hptr = strpbrk(ptr, "#\n"))) continue; *hptr = '\0'; if (!(hptr = strpbrk(ptr, " \t"))) continue; *hptr++ = '\0'; ptr = hptr; while (*ptr == ' ' || *ptr == '\t') ptr++; if (!(hptr = strpbrk(ptr, " \t"))) continue; *hptr++ = '\0'; snprintf(writebuf, sizeof(writebuf), "%s.%s@%s", OPSYS, ptr, domain); if (lookup(dtable, (char *)&writebuf)) { if (!quiet) warnx("duplicate netid '%s' -- skipping", writebuf); continue; } else { mstore(dtable, (char *)&writebuf, 0, 1); } printf ("%s.%s@%s 0:%s\n", OPSYS, ptr, domain, ptr); } fclose(hfp); /* * Lastly, copy out any extra information in the netid * file. If it's not open, just ignore it: it's optional anyway. */ if (nfp != NULL) { while(fgets(readbuf, LINSIZ, nfp)) { if (readbuf[0] == '#') continue; if ((ptr = strpbrk((char*)&readbuf, " \t")) == NULL) { warnx("bad netid entry: '%s'", readbuf); continue; } writebuf[0] = *ptr; *ptr = '\0'; if (lookup(dtable, (char *)&readbuf)) { if (!quiet) warnx("duplicate netid '%s' -- skipping", readbuf); continue; } else { mstore(dtable, (char *)&readbuf, 0, 1); } *ptr = writebuf[0]; printf("%s",readbuf); } fclose(nfp); } exit(0); } diff --git a/libexec/mknetid/parse_group.c b/libexec/mknetid/parse_group.c index e876bd9f2ff0..59ebf44eab37 100644 --- a/libexec/mknetid/parse_group.c +++ b/libexec/mknetid/parse_group.c @@ -1,159 +1,157 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 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. * 3. 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. */ #ifndef lint #if 0 static const char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * This is a slightly modified chunk of getgrent(3). All the YP support * and unneeded functions have been stripped out. */ #include #include #include #include #include FILE *_gr_fp; static struct group _gr_group; static int _gr_stayopen; static int grscan(int, int); static int start_gr(void); #define MAXGRP 200 static char *members[MAXGRP]; #define MAXLINELENGTH 1024 static char line[MAXLINELENGTH]; struct group * _getgrent(void) { if (!_gr_fp && !start_gr()) { return NULL; } if (!grscan(0, 0)) return(NULL); return(&_gr_group); } static int start_gr(void) { return 1; } int _setgroupent(int stayopen) { if (!start_gr()) return(0); _gr_stayopen = stayopen; return(1); } int _setgrent(void) { return(_setgroupent(0)); } void _endgrent(void) { if (_gr_fp) { (void)fclose(_gr_fp); _gr_fp = NULL; } } static int grscan(int search, int gid) { char *cp, **m; char *bp; for (;;) { if (!fgets(line, sizeof(line), _gr_fp)) return(0); bp = line; /* skip lines that are too big */ if (!strchr(line, '\n')) { int ch; while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) ; continue; } if ((_gr_group.gr_name = strsep(&bp, ":\n")) == NULL) break; if (_gr_group.gr_name[0] == '+') continue; if ((_gr_group.gr_passwd = strsep(&bp, ":\n")) == NULL) break; if (!(cp = strsep(&bp, ":\n"))) continue; _gr_group.gr_gid = atoi(cp); if (search && _gr_group.gr_gid != gid) continue; cp = NULL; if (bp == NULL) /* !! Must check for this! */ break; for (m = _gr_group.gr_mem = members;; bp++) { if (m == &members[MAXGRP - 1]) break; if (*bp == ',') { if (cp) { *bp = '\0'; *m++ = cp; cp = NULL; } } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { if (cp) { *bp = '\0'; *m++ = cp; } break; } else if (cp == NULL) cp = bp; } *m = NULL; return(1); } /* NOTREACHED */ return (0); } diff --git a/libexec/rbootd/bpf.c b/libexec/rbootd/bpf.c index 0edcc06ccf7e..6fbe34111a67 100644 --- a/libexec/rbootd/bpf.c +++ b/libexec/rbootd/bpf.c @@ -1,408 +1,406 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * 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 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. * * from: @(#)bpf.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: bpf.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)bpf.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "defs.h" #include "pathnames.h" static int BpfFd = -1; static unsigned BpfLen = 0; static u_int8_t *BpfPkt = NULL; /* ** BpfOpen -- Open and initialize a BPF device. ** ** Parameters: ** None. ** ** Returns: ** File descriptor of opened BPF device (for select() etc). ** ** Side Effects: ** If an error is encountered, the program terminates here. */ int BpfOpen(void) { struct ifreq ifr; char bpfdev[32]; int n = 0; /* * Open the first available BPF device. */ do { (void) sprintf(bpfdev, _PATH_BPF, n++); BpfFd = open(bpfdev, O_RDWR); } while (BpfFd < 0 && (errno == EBUSY || errno == EPERM)); if (BpfFd < 0) { syslog(LOG_ERR, "bpf: no available devices: %m"); Exit(0); } /* * Set interface name for bpf device, get data link layer * type and make sure it's type Ethernet. */ (void) strncpy(ifr.ifr_name, IntfName, sizeof(ifr.ifr_name)); if (ioctl(BpfFd, BIOCSETIF, (caddr_t)&ifr) < 0) { syslog(LOG_ERR, "bpf: ioctl(BIOCSETIF,%s): %m", IntfName); Exit(0); } /* * Make sure we are dealing with an Ethernet device. */ if (ioctl(BpfFd, BIOCGDLT, (caddr_t)&n) < 0) { syslog(LOG_ERR, "bpf: ioctl(BIOCGDLT): %m"); Exit(0); } if (n != DLT_EN10MB) { syslog(LOG_ERR,"bpf: %s: data-link type %d unsupported", IntfName, n); Exit(0); } /* * On read(), return packets immediately (do not buffer them). */ n = 1; if (ioctl(BpfFd, BIOCIMMEDIATE, (caddr_t)&n) < 0) { syslog(LOG_ERR, "bpf: ioctl(BIOCIMMEDIATE): %m"); Exit(0); } /* * Try to enable the chip/driver's multicast address filter to * grab our RMP address. If this fails, try promiscuous mode. * If this fails, there's no way we are going to get any RMP * packets so just exit here. */ #ifdef MSG_EOR ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; #endif ifr.ifr_addr.sa_family = AF_UNSPEC; memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN); if (ioctl(BpfFd, BIOCPROMISC, (caddr_t)0) < 0) { syslog(LOG_ERR, "bpf: can't set promiscuous mode: %m"); Exit(0); } /* * Ask BPF how much buffer space it requires and allocate one. */ if (ioctl(BpfFd, BIOCGBLEN, (caddr_t)&BpfLen) < 0) { syslog(LOG_ERR, "bpf: ioctl(BIOCGBLEN): %m"); Exit(0); } if (BpfPkt == NULL) BpfPkt = (u_int8_t *)malloc(BpfLen); if (BpfPkt == NULL) { syslog(LOG_ERR, "bpf: out of memory (%u bytes for bpfpkt)", BpfLen); Exit(0); } /* * Write a little program to snarf RMP Boot packets and stuff * it down BPF's throat (i.e. set up the packet filter). */ { #define RMP ((struct rmp_packet *)0) static struct bpf_insn bpf_insn[] = { { BPF_LD|BPF_B|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dsap }, { BPF_JMP|BPF_JEQ|BPF_K, 0, 5, IEEE_DSAP_HP }, { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.cntrl }, { BPF_JMP|BPF_JEQ|BPF_K, 0, 3, IEEE_CNTL_HP }, { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dxsap }, { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, HPEXT_DXSAP }, { BPF_RET|BPF_K, 0, 0, RMP_MAX_PACKET }, { BPF_RET|BPF_K, 0, 0, 0x0 } }; #undef RMP static struct bpf_program bpf_pgm = { sizeof(bpf_insn)/sizeof(bpf_insn[0]), bpf_insn }; if (ioctl(BpfFd, BIOCSETF, (caddr_t)&bpf_pgm) < 0) { syslog(LOG_ERR, "bpf: ioctl(BIOCSETF): %m"); Exit(0); } } return(BpfFd); } /* ** BPF GetIntfName -- Return the name of a network interface attached to ** the system, or 0 if none can be found. The interface ** must be configured up; the lowest unit number is ** preferred; loopback is ignored. ** ** Parameters: ** errmsg - if no network interface found, *errmsg explains why. ** ** Returns: ** A (static) pointer to interface name, or NULL on error. ** ** Side Effects: ** None. */ char * BpfGetIntfName(char **errmsg) { struct ifreq ibuf[8], *ifrp, *ifend, *mp; struct ifconf ifc; int fd; int minunit, n; char *cp; static char device[sizeof(ifrp->ifr_name)]; static char errbuf[128] = "No Error!"; if (errmsg != NULL) *errmsg = errbuf; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { (void) strcpy(errbuf, "bpf: socket: %m"); return(NULL); } ifc.ifc_len = sizeof ibuf; ifc.ifc_buf = (caddr_t)ibuf; if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || ifc.ifc_len < sizeof(struct ifreq)) { (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m"); return(NULL); } ifrp = ibuf; ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); mp = NULL; minunit = 666; for (; ifrp < ifend; ++ifrp) { if (ioctl(fd, SIOCGIFFLAGS, (char *)ifrp) < 0) { (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFFLAGS): %m"); return(NULL); } /* * If interface is down or this is the loopback interface, * ignore it. */ if ((ifrp->ifr_flags & IFF_UP) == 0 || #ifdef IFF_LOOPBACK (ifrp->ifr_flags & IFF_LOOPBACK)) #else (strcmp(ifrp->ifr_name, "lo0") == 0)) #endif continue; for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) ; n = atoi(cp); if (n < minunit) { minunit = n; mp = ifrp; } } (void) close(fd); if (mp == NULL) { (void) strcpy(errbuf, "bpf: no interfaces found"); return(NULL); } (void) strcpy(device, mp->ifr_name); return(device); } /* ** BpfRead -- Read packets from a BPF device and fill in `rconn'. ** ** Parameters: ** rconn - filled in with next packet. ** doread - is True if we can issue a read() syscall. ** ** Returns: ** True if `rconn' contains a new packet, False otherwise. ** ** Side Effects: ** None. */ int BpfRead(RMPCONN *rconn, int doread) { int datlen, caplen, hdrlen; static u_int8_t *bp = NULL, *ep = NULL; int cc; /* * The read() may block, or it may return one or more packets. * We let the caller decide whether or not we can issue a read(). */ if (doread) { if ((cc = read(BpfFd, (char *)BpfPkt, (int)BpfLen)) < 0) { syslog(LOG_ERR, "bpf: read: %m"); return(0); } else { bp = BpfPkt; ep = BpfPkt + cc; } } #define bhp ((struct bpf_hdr *)bp) /* * If there is a new packet in the buffer, stuff it into `rconn' * and return a success indication. */ if (bp < ep) { datlen = bhp->bh_datalen; caplen = bhp->bh_caplen; hdrlen = bhp->bh_hdrlen; if (caplen != datlen) syslog(LOG_ERR, "bpf: short packet dropped (%d of %d bytes)", caplen, datlen); else if (caplen > sizeof(struct rmp_packet)) syslog(LOG_ERR, "bpf: large packet dropped (%d bytes)", caplen); else { rconn->rmplen = caplen; memmove((char *)&rconn->tstamp, (char *)&bhp->bh_tstamp, sizeof(struct timeval)); memmove((char *)&rconn->rmp, (char *)bp + hdrlen, caplen); } bp += BPF_WORDALIGN(caplen + hdrlen); return(1); } #undef bhp return(0); } /* ** BpfWrite -- Write packet to BPF device. ** ** Parameters: ** rconn - packet to send. ** ** Returns: ** True if write succeeded, False otherwise. ** ** Side Effects: ** None. */ int BpfWrite(RMPCONN *rconn) { if (write(BpfFd, (char *)&rconn->rmp, rconn->rmplen) < 0) { syslog(LOG_ERR, "write: %s: %m", EnetStr(rconn)); return(0); } return(1); } /* ** BpfClose -- Close a BPF device. ** ** Parameters: ** None. ** ** Returns: ** Nothing. ** ** Side Effects: ** None. */ void BpfClose(void) { struct ifreq ifr; if (BpfPkt != NULL) { free((char *)BpfPkt); BpfPkt = NULL; } if (BpfFd == -1) return; #ifdef MSG_EOR ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; #endif ifr.ifr_addr.sa_family = AF_UNSPEC; memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN); if (ioctl(BpfFd, SIOCDELMULTI, (caddr_t)&ifr) < 0) (void) ioctl(BpfFd, BIOCPROMISC, (caddr_t)0); (void) close(BpfFd); BpfFd = -1; } diff --git a/libexec/rbootd/conf.c b/libexec/rbootd/conf.c index af46f53c3c88..a48d3efdb9c1 100644 --- a/libexec/rbootd/conf.c +++ b/libexec/rbootd/conf.c @@ -1,91 +1,89 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * 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 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. * * from: @(#)conf.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: conf.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)conf.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include "defs.h" #include "pathnames.h" /* ** Define (and possibly initialize) global variables here. ** ** Caveat: ** The maximum number of bootable files (`char *BootFiles[]') is ** limited to C_MAXFILE (i.e. the maximum number of files that ** can be spec'd in the configuration file). This was done to ** simplify the boot file search code. */ char MyHost[MAXHOSTNAMELEN]; /* host name */ pid_t MyPid; /* process id */ int DebugFlg = 0; /* set true if debugging */ int BootAny = 0; /* set true if we boot anyone */ char *ConfigFile = NULL; /* configuration file */ char *DfltConfig = _PATH_RBOOTDCONF; /* default configuration file */ char *PidFile = _PATH_RBOOTDPID; /* file w/pid of server */ char *BootDir = _PATH_RBOOTDLIB; /* directory w/boot files */ char *DbgFile = _PATH_RBOOTDDBG; /* debug output file */ FILE *DbgFp = NULL; /* debug file pointer */ char *IntfName = NULL; /* intf we are attached to */ u_int16_t SessionID = 0; /* generated session ID */ char *BootFiles[C_MAXFILE]; /* list of boot files */ CLIENT *Clients = NULL; /* list of addrs we'll accept */ RMPCONN *RmpConns = NULL; /* list of active connections */ u_int8_t RmpMcastAddr[RMP_ADDRLEN] = RMP_ADDR; /* RMP multicast address */ diff --git a/libexec/rbootd/parseconf.c b/libexec/rbootd/parseconf.c index 9585e87185c6..262f81fb68d7 100644 --- a/libexec/rbootd/parseconf.c +++ b/libexec/rbootd/parseconf.c @@ -1,360 +1,358 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * 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 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. * * from: @(#)parseconf.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: parseconf.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)parseconf.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include "defs.h" /* ** ParseConfig -- parse the config file into linked list of clients. ** ** Parameters: ** None. ** ** Returns: ** 1 on success, 0 otherwise. ** ** Side Effects: ** - Linked list of clients will be (re)allocated. ** ** Warnings: ** - GetBootFiles() must be called before this routine ** to create a linked list of default boot files. */ int ParseConfig(void) { FILE *fp; CLIENT *client; u_int8_t *addr; char line[C_LINELEN]; char *cp, *bcp; int i, j; int omask, linecnt = 0; if (BootAny) /* ignore config file */ return(1); FreeClients(); /* delete old list of clients */ if ((fp = fopen(ConfigFile, "r")) == NULL) { syslog(LOG_ERR, "ParseConfig: can't open config file (%s)", ConfigFile); return(0); } /* * We've got to block SIGHUP to prevent reconfiguration while * dealing with the linked list of Clients. This can be done * when actually linking the new client into the list, but * this could have unexpected results if the server was HUP'd * whilst reconfiguring. Hence, it is done here. */ omask = sigblock(sigmask(SIGHUP)); /* * GETSTR positions `bcp' at the start of the current token, * and null terminates it. `cp' is positioned at the start * of the next token. spaces & commas are separators. */ #define GETSTR while (isspace(*cp) || *cp == ',') cp++; \ bcp = cp; \ while (*cp && *cp!=',' && !isspace(*cp)) cp++; \ if (*cp) *cp++ = '\0' /* * For each line, parse it into a new CLIENT struct. */ while (fgets(line, C_LINELEN, fp) != NULL) { linecnt++; /* line counter */ if (*line == '\0' || *line == '#') /* ignore comment */ continue; if ((cp = strchr(line,'#')) != NULL) /* trash comments */ *cp = '\0'; cp = line; /* init `cp' */ GETSTR; /* get RMP addr */ if (bcp == cp) /* all delimiters */ continue; /* * Get an RMP address from a string. Abort on failure. */ if ((addr = ParseAddr(bcp)) == NULL) { syslog(LOG_ERR, "ParseConfig: line %d: can't parse <%s>", linecnt, bcp); continue; } if ((client = NewClient(addr)) == NULL) /* alloc new client */ continue; GETSTR; /* get first file */ /* * If no boot files are spec'd, use the default list. * Otherwise, validate each file (`bcp') against the * list of boot-able files. */ i = 0; if (bcp == cp) /* no files spec'd */ for (; i < C_MAXFILE && BootFiles[i] != NULL; i++) client->files[i] = BootFiles[i]; else { do { /* * For each boot file spec'd, make sure it's * in our list. If so, include a pointer to * it in the CLIENT's list of boot files. */ for (j = 0; ; j++) { if (j==C_MAXFILE||BootFiles[j]==NULL) { syslog(LOG_ERR, "ParseConfig: line %d: no boot file (%s)", linecnt, bcp); break; } if (STREQN(BootFiles[j], bcp)) { if (i < C_MAXFILE) client->files[i++] = BootFiles[j]; else syslog(LOG_ERR, "ParseConfig: line %d: too many boot files (%s)", linecnt, bcp); break; } } GETSTR; /* get next file */ } while (bcp != cp); /* * Restricted list of boot files were spec'd, * however, none of them were found. Since we * apparently can't let them boot "just anything", * the entire record is invalidated. */ if (i == 0) { FreeClient(client); continue; } } /* * Link this client into the linked list of clients. * SIGHUP has already been blocked. */ if (Clients) client->next = Clients; Clients = client; } (void) fclose(fp); /* close config file */ (void) sigsetmask(omask); /* reset signal mask */ return(1); /* return success */ } /* ** ParseAddr -- Parse a string containing an RMP address. ** ** This routine is fairly liberal at parsing an RMP address. The ** address must contain 6 octets consisting of between 0 and 2 hex ** chars (upper/lower case) separated by colons. If two colons are ** together (e.g. "::", the octet between them is recorded as being ** zero. Hence, the following addrs are all valid and parse to the ** same thing: ** ** 08:00:09:00:66:ad 8::9:0:66:AD 8::9::66:aD ** ** For clarity, an RMP address is really an Ethernet address, but ** since the HP boot code uses IEEE 802.3, it's really an IEEE ** 802.3 address. Of course, all of these are identical. ** ** Parameters: ** str - string representation of an RMP address. ** ** Returns: ** pointer to a static array of RMP_ADDRLEN bytes. ** ** Side Effects: ** None. ** ** Warnings: ** - The return value points to a static buffer; it must ** be copied if it's to be saved. */ u_int8_t * ParseAddr(char *str) { static u_int8_t addr[RMP_ADDRLEN]; char *cp; unsigned i; int part, subpart; memset((char *)&addr[0], 0, RMP_ADDRLEN); /* zero static buffer */ part = subpart = 0; for (cp = str; *cp; cp++) { /* * A colon (`:') must be used to delimit each octet. */ if (*cp == ':') { if (++part == RMP_ADDRLEN) /* too many parts */ return(NULL); subpart = 0; continue; } /* * Convert hex character to an integer. */ if (isdigit(*cp)) i = *cp - '0'; else { i = (isupper(*cp)? tolower(*cp): *cp) - 'a' + 10; if (i < 10 || i > 15) /* not a hex char */ return(NULL); } if (subpart++) { if (subpart > 2) /* too many hex chars */ return(NULL); addr[part] <<= 4; } addr[part] |= i; } if (part != (RMP_ADDRLEN-1)) /* too few parts */ return(NULL); return(&addr[0]); } /* ** GetBootFiles -- record list of files in current (boot) directory. ** ** Parameters: ** None. ** ** Returns: ** Number of boot files on success, 0 on failure. ** ** Side Effects: ** Strings in `BootFiles' are freed/allocated. ** ** Warnings: ** - After this routine is called, ParseConfig() must be ** called to re-order it's list of boot file pointers. */ int GetBootFiles(void) { DIR *dfd; struct stat statb; struct dirent *dp; int i; /* * Free the current list of boot files. */ for (i = 0; i < C_MAXFILE && BootFiles[i] != NULL; i++) { FreeStr(BootFiles[i]); BootFiles[i] = NULL; } /* * Open current directory to read boot file names. */ if ((dfd = opendir(".")) == NULL) { /* open BootDir */ syslog(LOG_ERR, "GetBootFiles: can't open directory (%s)\n", BootDir); return(0); } /* * Read each boot file name and allocate space for it in the * list of boot files (BootFiles). All boot files read after * C_MAXFILE will be ignored. */ i = 0; for (dp = readdir(dfd); dp != NULL; dp = readdir(dfd)) { if (stat(dp->d_name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFREG) continue; if (i == C_MAXFILE) syslog(LOG_ERR, "GetBootFiles: too many boot files (%s ignored)", dp->d_name); else if ((BootFiles[i] = NewStr(dp->d_name)) != NULL) i++; } (void) closedir(dfd); /* close BootDir */ if (i == 0) /* can't find any boot files */ syslog(LOG_ERR, "GetBootFiles: no boot files (%s)\n", BootDir); return(i); } diff --git a/libexec/rbootd/rmpproto.c b/libexec/rbootd/rmpproto.c index a31ec1de8abf..2603e697d8b8 100644 --- a/libexec/rbootd/rmpproto.c +++ b/libexec/rbootd/rmpproto.c @@ -1,586 +1,584 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * 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 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. * * from: @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: rmpproto.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include "defs.h" /* ** ProcessPacket -- determine packet type and do what's required. ** ** An RMP BOOT packet has been received. Look at the type field ** and process Boot Requests, Read Requests, and Boot Complete ** packets. Any other type will be dropped with a warning msg. ** ** Parameters: ** rconn - the new connection ** client - list of files available to this host ** ** Returns: ** Nothing. ** ** Side Effects: ** - If this is a valid boot request, it will be added to ** the linked list of outstanding requests (RmpConns). ** - If this is a valid boot complete, its associated ** entry in RmpConns will be deleted. ** - Also, unless we run out of memory, a reply will be ** sent to the host that sent the packet. */ void ProcessPacket(RMPCONN *rconn, CLIENT *client) { struct rmp_packet *rmp; RMPCONN *rconnout; rmp = &rconn->rmp; /* cache pointer to RMP packet */ switch(rmp->r_type) { /* do what we came here to do */ case RMP_BOOT_REQ: /* boot request */ if ((rconnout = NewConn(rconn)) == NULL) return; /* * If the Session ID is 0xffff, this is a "probe" * packet and we do not want to add the connection * to the linked list of active connections. There * are two types of probe packets, if the Sequence * Number is 0 they want to know our host name, o/w * they want the name of the file associated with * the number spec'd by the Sequence Number. * * If this is an actual boot request, open the file * and send a reply. If SendBootRepl() does not * return 0, add the connection to the linked list * of active connections, otherwise delete it since * an error was encountered. */ if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) { if (WORDZE(rmp->r_brq.rmp_seqno)) (void) SendServerID(rconnout); else (void) SendFileNo(rmp, rconnout, client? client->files: BootFiles); FreeConn(rconnout); } else { if (SendBootRepl(rmp, rconnout, client? client->files: BootFiles)) AddConn(rconnout); else FreeConn(rconnout); } break; case RMP_BOOT_REPL: /* boot reply (not valid) */ syslog(LOG_WARNING, "%s: sent a boot reply", EnetStr(rconn)); break; case RMP_READ_REQ: /* read request */ /* * Send a portion of the boot file. */ (void) SendReadRepl(rconn); break; case RMP_READ_REPL: /* read reply (not valid) */ syslog(LOG_WARNING, "%s: sent a read reply", EnetStr(rconn)); break; case RMP_BOOT_DONE: /* boot complete */ /* * Remove the entry from the linked list of active * connections. */ (void) BootDone(rconn); break; default: /* unknown RMP packet type */ syslog(LOG_WARNING, "%s: unknown packet type (%u)", EnetStr(rconn), rmp->r_type); } } /* ** SendServerID -- send our host name to who ever requested it. ** ** Parameters: ** rconn - the reply packet to be formatted. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendServerID(RMPCONN *rconn) { struct rmp_packet *rpl; char *src, *dst; u_int8_t *size; rpl = &rconn->rmp; /* cache ptr to RMP packet */ /* * Set up assorted fields in reply packet. */ rpl->r_brpl.rmp_type = RMP_BOOT_REPL; rpl->r_brpl.rmp_retcode = RMP_E_OKAY; ZEROWORD(rpl->r_brpl.rmp_seqno); rpl->r_brpl.rmp_session = 0; rpl->r_brpl.rmp_version = htons(RMP_VERSION); size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of host name */ /* * Copy our host name into the reply packet incrementing the * length as we go. Stop at RMP_HOSTLEN or the first dot. */ src = MyHost; dst = (char *) &rpl->r_brpl.rmp_flnm; for (*size = 0; *size < RMP_HOSTLEN; (*size)++) { if (*src == '.' || *src == '\0') break; *dst++ = *src++; } rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ return(SendPacket(rconn)); /* send packet */ } /* ** SendFileNo -- send the name of a bootable file to the requester. ** ** Parameters: ** req - RMP BOOT packet containing the request. ** rconn - the reply packet to be formatted. ** filelist - list of files available to the requester. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendFileNo(struct rmp_packet *req, RMPCONN *rconn, char *filelist[]) { struct rmp_packet *rpl; char *src, *dst; u_int8_t *size; int i; GETWORD(req->r_brpl.rmp_seqno, i); /* SeqNo is really FileNo */ rpl = &rconn->rmp; /* cache ptr to RMP packet */ /* * Set up assorted fields in reply packet. */ rpl->r_brpl.rmp_type = RMP_BOOT_REPL; PUTWORD(i, rpl->r_brpl.rmp_seqno); i--; rpl->r_brpl.rmp_session = 0; rpl->r_brpl.rmp_version = htons(RMP_VERSION); size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of filename */ *size = 0; /* init length to zero */ /* * Copy the file name into the reply packet incrementing the * length as we go. Stop at end of string or when RMPBOOTDATA * characters have been copied. Also, set return code to * indicate success or "no more files". */ if (i < C_MAXFILE && filelist[i] != NULL) { src = filelist[i]; dst = (char *)&rpl->r_brpl.rmp_flnm; for (; *src && *size < RMPBOOTDATA; (*size)++) { if (*src == '\0') break; *dst++ = *src++; } rpl->r_brpl.rmp_retcode = RMP_E_OKAY; } else rpl->r_brpl.rmp_retcode = RMP_E_NODFLT; rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ return(SendPacket(rconn)); /* send packet */ } /* ** SendBootRepl -- open boot file and respond to boot request. ** ** Parameters: ** req - RMP BOOT packet containing the request. ** rconn - the reply packet to be formatted. ** filelist - list of files available to the requester. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendBootRepl(struct rmp_packet *req, RMPCONN *rconn, char *filelist[]) { int retval; char *filename, filepath[RMPBOOTDATA+1]; RMPCONN *oldconn; struct rmp_packet *rpl; char *src, *dst1, *dst2; u_int8_t i; /* * If another connection already exists, delete it since we * are obviously starting again. */ if ((oldconn = FindConn(rconn)) != NULL) { syslog(LOG_WARNING, "%s: dropping existing connection", EnetStr(oldconn)); RemoveConn(oldconn); } rpl = &rconn->rmp; /* cache ptr to RMP packet */ /* * Set up assorted fields in reply packet. */ rpl->r_brpl.rmp_type = RMP_BOOT_REPL; COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno); rpl->r_brpl.rmp_session = htons(GenSessID()); rpl->r_brpl.rmp_version = htons(RMP_VERSION); rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize; /* * Copy file name to `filepath' string, and into reply packet. */ src = &req->r_brq.rmp_flnm; dst1 = filepath; dst2 = &rpl->r_brpl.rmp_flnm; for (i = 0; i < req->r_brq.rmp_flnmsize; i++) *dst1++ = *dst2++ = *src++; *dst1 = '\0'; /* * If we are booting HP-UX machines, their secondary loader will * ask for files like "/hp-ux". As a security measure, we do not * allow boot files to lay outside the boot directory (unless they * are purposely link'd out. So, make `filename' become the path- * stripped file name and spoof the client into thinking that it * really got what it wanted. */ filename = strrchr(filepath,'/'); filename = filename? filename + 1: filepath; /* * Check that this is a valid boot file name. */ for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++) if (STREQN(filename, filelist[i])) goto match; /* * Invalid boot file name, set error and send reply packet. */ rpl->r_brpl.rmp_retcode = RMP_E_NOFILE; retval = 0; goto sendpkt; match: /* * This is a valid boot file. Open the file and save the file * descriptor associated with this connection and set success * indication. If the file couldnt be opened, set error: * "no such file or dir" - RMP_E_NOFILE * "file table overflow" - RMP_E_BUSY * "too many open files" - RMP_E_BUSY * anything else - RMP_E_OPENFILE */ if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) { rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE: (errno == EMFILE || errno == ENFILE)? RMP_E_BUSY: RMP_E_OPENFILE; retval = 0; } else { rpl->r_brpl.rmp_retcode = RMP_E_OKAY; retval = 1; } sendpkt: syslog(LOG_INFO, "%s: request to boot %s (%s)", EnetStr(rconn), filename, retval? "granted": "denied"); rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize); return (retval & SendPacket(rconn)); } /* ** SendReadRepl -- send a portion of the boot file to the requester. ** ** Parameters: ** rconn - the reply packet to be formatted. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendReadRepl(RMPCONN *rconn) { int retval = 0; RMPCONN *oldconn; struct rmp_packet *rpl, *req; int size = 0; int madeconn = 0; /* * Find the old connection. If one doesn't exist, create one only * to return the error code. */ if ((oldconn = FindConn(rconn)) == NULL) { if ((oldconn = NewConn(rconn)) == NULL) return(0); syslog(LOG_ERR, "SendReadRepl: no active connection (%s)", EnetStr(rconn)); madeconn++; } req = &rconn->rmp; /* cache ptr to request packet */ rpl = &oldconn->rmp; /* cache ptr to reply packet */ if (madeconn) { /* no active connection above; abort */ rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; retval = 1; goto sendpkt; } /* * Make sure Session ID's match. */ if (ntohs(req->r_rrq.rmp_session) != ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): ntohs(rpl->r_rrpl.rmp_session))) { syslog(LOG_ERR, "SendReadRepl: bad session id (%s)", EnetStr(rconn)); rpl->r_rrpl.rmp_retcode = RMP_E_BADSID; retval = 1; goto sendpkt; } /* * If the requester asks for more data than we can fit, * silently clamp the request size down to RMPREADDATA. * * N.B. I do not know if this is "legal", however it seems * to work. This is necessary for bpfwrite() on machines * with MCLBYTES less than 1514. */ if (ntohs(req->r_rrq.rmp_size) > RMPREADDATA) req->r_rrq.rmp_size = htons(RMPREADDATA); /* * Position read head on file according to info in request packet. */ GETWORD(req->r_rrq.rmp_offset, size); if (lseek(oldconn->bootfd, (off_t)size, SEEK_SET) < 0) { syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)", EnetStr(rconn)); rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; retval = 1; goto sendpkt; } /* * Read data directly into reply packet. */ if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data, (int) ntohs(req->r_rrq.rmp_size))) <= 0) { if (size < 0) { syslog(LOG_ERR, "SendReadRepl: read: %m (%s)", EnetStr(rconn)); rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; } else { rpl->r_rrpl.rmp_retcode = RMP_E_EOF; } retval = 1; goto sendpkt; } /* * Set success indication. */ rpl->r_rrpl.rmp_retcode = RMP_E_OKAY; sendpkt: /* * Set up assorted fields in reply packet. */ rpl->r_rrpl.rmp_type = RMP_READ_REPL; COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset); rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session; oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */ retval &= SendPacket(oldconn); /* send packet */ if (madeconn) /* clean up after ourself */ FreeConn(oldconn); return (retval); } /* ** BootDone -- free up memory allocated for a connection. ** ** Parameters: ** rconn - incoming boot complete packet. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int BootDone(RMPCONN *rconn) { RMPCONN *oldconn; struct rmp_packet *rpl; /* * If we can't find the connection, ignore the request. */ if ((oldconn = FindConn(rconn)) == NULL) { syslog(LOG_ERR, "BootDone: no existing connection (%s)", EnetStr(rconn)); return(0); } rpl = &oldconn->rmp; /* cache ptr to RMP packet */ /* * Make sure Session ID's match. */ if (ntohs(rconn->rmp.r_rrq.rmp_session) != ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): ntohs(rpl->r_rrpl.rmp_session))) { syslog(LOG_ERR, "BootDone: bad session id (%s)", EnetStr(rconn)); return(0); } RemoveConn(oldconn); /* remove connection */ syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn)); return(1); } /* ** SendPacket -- send an RMP packet to a remote host. ** ** Parameters: ** rconn - packet to be sent. ** ** Returns: ** 1 on success, 0 on failure. ** ** Side Effects: ** none. */ int SendPacket(RMPCONN *rconn) { /* * Set Ethernet Destination address to Source (BPF and the enet * driver will take care of getting our source address set). */ memmove((char *)&rconn->rmp.hp_hdr.daddr[0], (char *)&rconn->rmp.hp_hdr.saddr[0], RMP_ADDRLEN); rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr)); /* * Reverse 802.2/HP Extended Source & Destination Access Pts. */ rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP); rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP); /* * Last time this connection was active. */ (void)gettimeofday(&rconn->tstamp, NULL); if (DbgFp != NULL) /* display packet */ DispPkt(rconn,DIR_SENT); /* * Send RMP packet to remote host. */ return(BpfWrite(rconn)); } diff --git a/libexec/rbootd/utils.c b/libexec/rbootd/utils.c index f5c2d62d30d7..36a7116a05c4 100644 --- a/libexec/rbootd/utils.c +++ b/libexec/rbootd/utils.c @@ -1,546 +1,544 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1992 The University of Utah and the Center * for Software Science (CSS). * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Center for Software Science of the University of Utah Computer * Science Department. CSS requests users of this software to return * to css-dist@cs.utah.edu any improvements that they make and grant * CSS redistribution rights. * * 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 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. * * from: @(#)utils.c 8.1 (Berkeley) 6/4/93 * * From: Utah Hdr: utils.c 3.1 92/07/06 * Author: Jeff Forys, University of Utah CSS */ #ifndef lint #if 0 static const char sccsid[] = "@(#)utils.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include "defs.h" /* ** DispPkt -- Display the contents of an RMPCONN packet. ** ** Parameters: ** rconn - packet to be displayed. ** direct - direction packet is going (DIR_*). ** ** Returns: ** Nothing. ** ** Side Effects: ** None. */ void DispPkt(RMPCONN *rconn, int direct) { static const char BootFmt[] = "\t\tRetCode:%u SeqNo:%x SessID:%x Vers:%u"; static const char ReadFmt[] = "\t\tRetCode:%u Offset:%x SessID:%x\n"; struct tm *tmp; struct rmp_packet *rmp; int i, omask; u_int32_t t; /* * Since we will be working with RmpConns as well as DbgFp, we * must block signals that can affect either. */ omask = sigblock(sigmask(SIGHUP)|sigmask(SIGUSR1)|sigmask(SIGUSR2)); if (DbgFp == NULL) { /* sanity */ (void) sigsetmask(omask); return; } /* display direction packet is going using '>>>' or '<<<' */ fputs((direct==DIR_RCVD)?"<<< ":(direct==DIR_SENT)?">>> ":"", DbgFp); /* display packet timestamp */ tmp = localtime((time_t *)&rconn->tstamp.tv_sec); fprintf(DbgFp, "%02d:%02d:%02d.%06ld ", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, rconn->tstamp.tv_usec); /* display src or dst addr and information about network interface */ fprintf(DbgFp, "Addr: %s Intf: %s\n", EnetStr(rconn), IntfName); rmp = &rconn->rmp; /* display IEEE 802.2 Logical Link Control header */ (void) fprintf(DbgFp, "\t802.2 LLC: DSAP:%x SSAP:%x CTRL:%x\n", rmp->hp_llc.dsap, rmp->hp_llc.ssap, ntohs(rmp->hp_llc.cntrl)); /* display HP extensions to 802.2 Logical Link Control header */ (void) fprintf(DbgFp, "\tHP Ext: DXSAP:%x SXSAP:%x\n", ntohs(rmp->hp_llc.dxsap), ntohs(rmp->hp_llc.sxsap)); /* * Display information about RMP packet using type field to * determine what kind of packet this is. */ switch(rmp->r_type) { case RMP_BOOT_REQ: /* boot request */ (void) fprintf(DbgFp, "\tBoot Request:"); GETWORD(rmp->r_brq.rmp_seqno, t); if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) { if (WORDZE(rmp->r_brq.rmp_seqno)) fputs(" (Send Server ID)", DbgFp); else fprintf(DbgFp," (Send Filename #%u)",t); } (void) fputc('\n', DbgFp); (void) fprintf(DbgFp, BootFmt, rmp->r_brq.rmp_retcode, t, ntohs(rmp->r_brq.rmp_session), ntohs(rmp->r_brq.rmp_version)); (void) fprintf(DbgFp, "\n\t\tMachine Type: "); for (i = 0; i < RMP_MACHLEN; i++) (void) fputc(rmp->r_brq.rmp_machtype[i], DbgFp); DspFlnm(rmp->r_brq.rmp_flnmsize, &rmp->r_brq.rmp_flnm); break; case RMP_BOOT_REPL: /* boot reply */ fprintf(DbgFp, "\tBoot Reply:\n"); GETWORD(rmp->r_brpl.rmp_seqno, t); (void) fprintf(DbgFp, BootFmt, rmp->r_brpl.rmp_retcode, t, ntohs(rmp->r_brpl.rmp_session), ntohs(rmp->r_brpl.rmp_version)); DspFlnm(rmp->r_brpl.rmp_flnmsize,&rmp->r_brpl.rmp_flnm); break; case RMP_READ_REQ: /* read request */ (void) fprintf(DbgFp, "\tRead Request:\n"); GETWORD(rmp->r_rrq.rmp_offset, t); (void) fprintf(DbgFp, ReadFmt, rmp->r_rrq.rmp_retcode, t, ntohs(rmp->r_rrq.rmp_session)); (void) fprintf(DbgFp, "\t\tNoOfBytes: %u\n", ntohs(rmp->r_rrq.rmp_size)); break; case RMP_READ_REPL: /* read reply */ (void) fprintf(DbgFp, "\tRead Reply:\n"); GETWORD(rmp->r_rrpl.rmp_offset, t); (void) fprintf(DbgFp, ReadFmt, rmp->r_rrpl.rmp_retcode, t, ntohs(rmp->r_rrpl.rmp_session)); (void) fprintf(DbgFp, "\t\tNoOfBytesSent: %zu\n", rconn->rmplen - RMPREADSIZE(0)); break; case RMP_BOOT_DONE: /* boot complete */ (void) fprintf(DbgFp, "\tBoot Complete:\n"); (void) fprintf(DbgFp, "\t\tRetCode:%u SessID:%x\n", rmp->r_done.rmp_retcode, ntohs(rmp->r_done.rmp_session)); break; default: /* ??? */ (void) fprintf(DbgFp, "\tUnknown Type:(%d)\n", rmp->r_type); } (void) fputc('\n', DbgFp); (void) fflush(DbgFp); (void) sigsetmask(omask); /* reset old signal mask */ } /* ** GetEtherAddr -- convert an RMP (Ethernet) address into a string. ** ** An RMP BOOT packet has been received. Look at the type field ** and process Boot Requests, Read Requests, and Boot Complete ** packets. Any other type will be dropped with a warning msg. ** ** Parameters: ** addr - array of RMP_ADDRLEN bytes. ** ** Returns: ** Pointer to static string representation of `addr'. ** ** Side Effects: ** None. ** ** Warnings: ** - The return value points to a static buffer; it must ** be copied if it's to be saved. */ char * GetEtherAddr(u_int8_t *addr) { static char Hex[] = "0123456789abcdef"; static char etherstr[RMP_ADDRLEN*3]; int i; char *cp; /* * For each byte in `addr', convert it to ":". * The last byte does not get a trailing `:' appended. */ i = 0; cp = etherstr; for(;;) { *cp++ = Hex[*addr >> 4 & 0xf]; *cp++ = Hex[*addr++ & 0xf]; if (++i == RMP_ADDRLEN) break; *cp++ = ':'; } *cp = '\0'; return(etherstr); } /* ** DispFlnm -- Print a string of bytes to DbgFp (often, a file name). ** ** Parameters: ** size - number of bytes to print. ** flnm - address of first byte. ** ** Returns: ** Nothing. ** ** Side Effects: ** - Characters are sent to `DbgFp'. */ void DspFlnm(u_int size, char *flnm) { int i; (void) fprintf(DbgFp, "\n\t\tFile Name (%u): <", size); for (i = 0; i < size; i++) (void) fputc(*flnm++, DbgFp); (void) fputs(">\n", DbgFp); } /* ** NewClient -- allocate memory for a new CLIENT. ** ** Parameters: ** addr - RMP (Ethernet) address of new client. ** ** Returns: ** Ptr to new CLIENT or NULL if we ran out of memory. ** ** Side Effects: ** - Memory will be malloc'd for the new CLIENT. ** - If malloc() fails, a log message will be generated. */ CLIENT * NewClient(u_int8_t *addr) { CLIENT *ctmp; if ((ctmp = (CLIENT *) malloc(sizeof(CLIENT))) == NULL) { syslog(LOG_ERR, "NewClient: out of memory (%s)", GetEtherAddr(addr)); return(NULL); } memset(ctmp, 0, sizeof(CLIENT)); memmove(&ctmp->addr[0], addr, RMP_ADDRLEN); return(ctmp); } /* ** FreeClient -- free linked list of Clients. ** ** Parameters: ** None. ** ** Returns: ** Nothing. ** ** Side Effects: ** - All malloc'd memory associated with the linked list of ** CLIENTS will be free'd; `Clients' will be set to NULL. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ void FreeClients(void) { CLIENT *ctmp; while (Clients != NULL) { ctmp = Clients; Clients = Clients->next; FreeClient(ctmp); } } /* ** NewStr -- allocate memory for a character array. ** ** Parameters: ** str - null terminated character array. ** ** Returns: ** Ptr to new character array or NULL if we ran out of memory. ** ** Side Effects: ** - Memory will be malloc'd for the new character array. ** - If malloc() fails, a log message will be generated. */ char * NewStr(char *str) { char *stmp; if ((stmp = (char *)malloc((unsigned) (strlen(str)+1))) == NULL) { syslog(LOG_ERR, "NewStr: out of memory (%s)", str); return(NULL); } (void) strcpy(stmp, str); return(stmp); } /* ** To save time, NewConn and FreeConn maintain a cache of one RMPCONN ** in `LastFree' (defined below). */ static RMPCONN *LastFree = NULL; /* ** NewConn -- allocate memory for a new RMPCONN connection. ** ** Parameters: ** rconn - initialization template for new connection. ** ** Returns: ** Ptr to new RMPCONN or NULL if we ran out of memory. ** ** Side Effects: ** - Memory may be malloc'd for the new RMPCONN (if not cached). ** - If malloc() fails, a log message will be generated. */ RMPCONN * NewConn(RMPCONN *rconn) { RMPCONN *rtmp; if (LastFree == NULL) { /* nothing cached; make a new one */ if ((rtmp = (RMPCONN *) malloc(sizeof(RMPCONN))) == NULL) { syslog(LOG_ERR, "NewConn: out of memory (%s)", EnetStr(rconn)); return(NULL); } } else { /* use the cached RMPCONN */ rtmp = LastFree; LastFree = NULL; } /* * Copy template into `rtmp', init file descriptor to `-1' and * set ptr to next elem NULL. */ memmove((char *)rtmp, (char *)rconn, sizeof(RMPCONN)); rtmp->bootfd = -1; rtmp->next = NULL; return(rtmp); } /* ** FreeConn -- Free memory associated with an RMPCONN connection. ** ** Parameters: ** rtmp - ptr to RMPCONN to be free'd. ** ** Returns: ** Nothing. ** ** Side Effects: ** - Memory associated with `rtmp' may be free'd (or cached). ** - File desc associated with `rtmp->bootfd' will be closed. */ void FreeConn(RMPCONN *rtmp) { /* * If the file descriptor is in use, close the file. */ if (rtmp->bootfd >= 0) { (void) close(rtmp->bootfd); rtmp->bootfd = -1; } if (LastFree == NULL) /* cache for next time */ rtmp = LastFree; else /* already one cached; free this one */ free((char *)rtmp); } /* ** FreeConns -- free linked list of RMPCONN connections. ** ** Parameters: ** None. ** ** Returns: ** Nothing. ** ** Side Effects: ** - All malloc'd memory associated with the linked list of ** connections will be free'd; `RmpConns' will be set to NULL. ** - If LastFree is != NULL, it too will be free'd & NULL'd. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ void FreeConns(void) { RMPCONN *rtmp; while (RmpConns != NULL) { rtmp = RmpConns; RmpConns = RmpConns->next; FreeConn(rtmp); } if (LastFree != NULL) { free((char *)LastFree); LastFree = NULL; } } /* ** AddConn -- Add a connection to the linked list of connections. ** ** Parameters: ** rconn - connection to be added. ** ** Returns: ** Nothing. ** ** Side Effects: ** - RmpConn will point to new connection. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ void AddConn(RMPCONN *rconn) { if (RmpConns != NULL) rconn->next = RmpConns; RmpConns = rconn; } /* ** FindConn -- Find a connection in the linked list of connections. ** ** We use the RMP (Ethernet) address as the basis for determining ** if this is the same connection. According to the Remote Maint ** Protocol, we can only have one connection with any machine. ** ** Parameters: ** rconn - connection to be found. ** ** Returns: ** Matching connection from linked list or NULL if not found. ** ** Side Effects: ** None. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ RMPCONN * FindConn(RMPCONN *rconn) { RMPCONN *rtmp; for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next) if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0], (char *)&rtmp->rmp.hp_hdr.saddr[0], RMP_ADDRLEN) == 0) break; return(rtmp); } /* ** RemoveConn -- Remove a connection from the linked list of connections. ** ** Parameters: ** rconn - connection to be removed. ** ** Returns: ** Nothing. ** ** Side Effects: ** - If found, an RMPCONN will cease to exist and it will ** be removed from the linked list. ** ** Warnings: ** - This routine must be called with SIGHUP blocked. */ void RemoveConn(RMPCONN *rconn) { RMPCONN *thisrconn, *lastrconn; if (RmpConns == rconn) { /* easy case */ RmpConns = RmpConns->next; FreeConn(rconn); } else { /* must traverse linked list */ lastrconn = RmpConns; /* set back ptr */ thisrconn = lastrconn->next; /* set current ptr */ while (thisrconn != NULL) { if (rconn == thisrconn) { /* found it */ lastrconn->next = thisrconn->next; FreeConn(thisrconn); break; } lastrconn = thisrconn; thisrconn = thisrconn->next; } } } diff --git a/libexec/revnetgroup/hash.c b/libexec/revnetgroup/hash.c index 2dd1a3f91d74..db8e95e3040c 100644 --- a/libexec/revnetgroup/hash.c +++ b/libexec/revnetgroup/hash.c @@ -1,210 +1,205 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1995 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include "hash.h" /* * This hash function is stolen directly from the * Berkeley DB package. It already exists inside libc, but * it's declared static which prevents us from calling it * from here. */ /* * OZ's original sdbm hash */ u_int32_t hash(const void *keyarg, size_t len) { const u_char *key; size_t loop; u_int32_t h; #define HASHC h = *key++ + 65599 * h h = 0; key = keyarg; if (len > 0) { loop = (len + 8 - 1) >> 3; switch (len & (8 - 1)) { case 0: do { HASHC; /* FALLTHROUGH */ case 7: HASHC; /* FALLTHROUGH */ case 6: HASHC; /* FALLTHROUGH */ case 5: HASHC; /* FALLTHROUGH */ case 4: HASHC; /* FALLTHROUGH */ case 3: HASHC; /* FALLTHROUGH */ case 2: HASHC; /* FALLTHROUGH */ case 1: HASHC; } while (--loop); } } return (h); } /* * Generate a hash value for a given key (character string). * We mask off all but the lower 8 bits since our table array * can only hold 256 elements. */ u_int32_t hashkey(char *key) { if (key == NULL) return (-1); return(hash((void *)key, strlen(key)) & HASH_MASK); } /* Find an entry in the hash table (may be hanging off a linked list). */ char * lookup(struct group_entry *table[], char *key) { struct group_entry *cur; cur = table[hashkey(key)]; while (cur) { if (!strcmp(cur->key, key)) return(cur->data); cur = cur->next; } return(NULL); } /* * Store an entry in the main netgroup hash table. Here's how this * works: the table can only be so big when we initialize it (TABLESIZE) * but the number of netgroups in the /etc/netgroup file could easily be * much larger than the table. Since our hash values are adjusted to * never be greater than TABLESIZE too, this means it won't be long before * we find ourselves with two keys that hash to the same value. * * One way to deal with this is to malloc(2) a second table and start * doing indirection, but this is a pain in the butt and it's not worth * going to all that trouble for a dinky little program like this. Instead, * we turn each table entry into a linked list and simply link keys * with the same hash value together at the same index location within * the table. * * That's a lot of comment for such a small piece of code, isn't it. */ void store(struct group_entry *table[], char *key, char *data) { struct group_entry *new; u_int32_t i; i = hashkey(key); new = (struct group_entry *)malloc(sizeof(struct group_entry)); new->key = strdup(key); new->data = strdup(data); new->next = table[i]; table[i] = new; return; } /* * Store a group member entry and/or update its grouplist. This is * a bit more complicated than the previous function since we have to * maintain not only the hash table of group members, each group member * structure also has a linked list of groups hung off it. If handed * a member name that we haven't encountered before, we have to do * two things: add that member to the table (possibly hanging them * off the end of a linked list, as above), and add a group name to * the member's grouplist list. If we're handed a name that already has * an entry in the table, then we just have to do one thing, which is * to update its grouplist. */ void mstore(struct member_entry *table[], char *key, char *data, char *domain) { struct member_entry *cur, *new; struct grouplist *tmp; u_int32_t i; i = hashkey(key); cur = table[i]; tmp = (struct grouplist *)malloc(sizeof(struct grouplist)); tmp->groupname = strdup(data); tmp->next = NULL; /* Check if all we have to do is insert a new groupname. */ while (cur) { if (!strcmp(cur->key, key)) { tmp->next = cur->groups; cur->groups = tmp; return; } cur = cur->next; } /* Didn't find a match -- add the whole mess to the table. */ new = (struct member_entry *)malloc(sizeof(struct member_entry)); new->key = strdup(key); new->domain = domain ? strdup(domain) : "*"; new->groups = tmp; new->next = table[i]; table[i] = new; return; } diff --git a/libexec/revnetgroup/parse_netgroup.c b/libexec/revnetgroup/parse_netgroup.c index a1e954164ea9..3d6a7939fa1d 100644 --- a/libexec/revnetgroup/parse_netgroup.c +++ b/libexec/revnetgroup/parse_netgroup.c @@ -1,362 +1,357 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * 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 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - /* * This is a specially hacked-up version of getnetgrent.c used to parse * data from the stored hash table of netgroup info rather than from a * file. It's used mainly for the parse_netgroup() function. All the YP * stuff and file support has been stripped out since it isn't needed. */ #include #include #include #include #include #include "hash.h" /* * Static Variables and functions used by setnetgrent(), getnetgrent() and * __endnetgrent(). * There are two linked lists: * - linelist is just used by setnetgrent() to parse the net group file via. * parse_netgrp() * - netgrp is the list of entries for the current netgroup */ struct linelist { struct linelist *l_next; /* Chain ptr. */ int l_parsed; /* Flag for cycles */ char *l_groupname; /* Name of netgroup */ char *l_line; /* Netgroup entrie(s) to be parsed */ }; struct netgrp { struct netgrp *ng_next; /* Chain ptr */ char *ng_str[3]; /* Field pointers, see below */ }; #define NG_HOST 0 /* Host name */ #define NG_USER 1 /* User name */ #define NG_DOM 2 /* and Domain name */ static struct linelist *linehead = (struct linelist *)0; static struct netgrp *nextgrp = (struct netgrp *)0; static struct { struct netgrp *gr; char *grname; } grouphead = { (struct netgrp *)0, (char *)0, }; static int parse_netgrp(char *group); static struct linelist *read_for_group(char *group); extern struct group_entry *gtable[]; /* * setnetgrent() * Parse the netgroup file looking for the netgroup and build the list * of netgrp structures. Let parse_netgrp() and read_for_group() do * most of the work. */ void __setnetgrent(char *group) { /* Sanity check */ if (group == NULL || !strlen(group)) return; if (grouphead.gr == (struct netgrp *)0 || strcmp(group, grouphead.grname)) { __endnetgrent(); if (parse_netgrp(group)) __endnetgrent(); else { grouphead.grname = (char *) malloc(strlen(group) + 1); strcpy(grouphead.grname, group); } } nextgrp = grouphead.gr; } /* * Get the next netgroup off the list. */ int __getnetgrent(char **hostp, char **userp, char **domp) { if (nextgrp) { *hostp = nextgrp->ng_str[NG_HOST]; *userp = nextgrp->ng_str[NG_USER]; *domp = nextgrp->ng_str[NG_DOM]; nextgrp = nextgrp->ng_next; return (1); } return (0); } /* * __endnetgrent() - cleanup */ void __endnetgrent(void) { struct linelist *lp, *olp; struct netgrp *gp, *ogp; lp = linehead; while (lp) { olp = lp; lp = lp->l_next; free(olp->l_groupname); free(olp->l_line); free((char *)olp); } linehead = (struct linelist *)0; if (grouphead.grname) { free(grouphead.grname); grouphead.grname = (char *)0; } gp = grouphead.gr; while (gp) { ogp = gp; gp = gp->ng_next; if (ogp->ng_str[NG_HOST]) free(ogp->ng_str[NG_HOST]); if (ogp->ng_str[NG_USER]) free(ogp->ng_str[NG_USER]); if (ogp->ng_str[NG_DOM]) free(ogp->ng_str[NG_DOM]); free((char *)ogp); } grouphead.gr = (struct netgrp *)0; } /* * Parse the netgroup file setting up the linked lists. */ static int parse_netgrp(char *group) { char *spos, *epos; int len, strpos; #ifdef DEBUG int fields; #endif char *pos, *gpos; struct netgrp *grp; struct linelist *lp = linehead; /* * First, see if the line has already been read in. */ while (lp) { if (!strcmp(group, lp->l_groupname)) break; lp = lp->l_next; } if (lp == (struct linelist *)0 && (lp = read_for_group(group)) == (struct linelist *)0) return (1); if (lp->l_parsed) { #ifdef DEBUG /* * This error message is largely superfluous since the * code handles the error condition successfully, and * spewing it out from inside libc can actually hose * certain programs. */ warnx("cycle in netgroup %s", lp->l_groupname); #endif return (1); } else lp->l_parsed = 1; pos = lp->l_line; /* Watch for null pointer dereferences, dammit! */ while (pos != NULL && *pos != '\0') { if (*pos == '(') { grp = (struct netgrp *)malloc(sizeof (struct netgrp)); bzero((char *)grp, sizeof (struct netgrp)); grp->ng_next = grouphead.gr; grouphead.gr = grp; pos++; gpos = strsep(&pos, ")"); #ifdef DEBUG fields = 0; #endif for (strpos = 0; strpos < 3; strpos++) { if ((spos = strsep(&gpos, ","))) { #ifdef DEBUG fields++; #endif while (*spos == ' ' || *spos == '\t') spos++; if ((epos = strpbrk(spos, " \t"))) { *epos = '\0'; len = epos - spos; } else len = strlen(spos); if (len > 0) { grp->ng_str[strpos] = (char *) malloc(len + 1); bcopy(spos, grp->ng_str[strpos], len + 1); } } else { /* * All other systems I've tested * return NULL for empty netgroup * fields. It's up to user programs * to handle the NULLs appropriately. */ grp->ng_str[strpos] = NULL; } } #ifdef DEBUG /* * Note: on other platforms, malformed netgroup * entries are not normally flagged. While we * can catch bad entries and report them, we should * stay silent by default for compatibility's sake. */ if (fields < 3) warnx("bad entry (%s%s%s%s%s) in netgroup \"%s\"", grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST], grp->ng_str[NG_USER] == NULL ? "" : ",", grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER], grp->ng_str[NG_DOM] == NULL ? "" : ",", grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM], lp->l_groupname); #endif } else { spos = strsep(&pos, ", \t"); if (parse_netgrp(spos)) continue; } /* Watch for null pointer dereferences, dammit! */ if (pos != NULL) while (*pos == ' ' || *pos == ',' || *pos == '\t') pos++; } return (0); } /* * Read the netgroup file and save lines until the line for the netgroup * is found. Return 1 if eof is encountered. */ static struct linelist * read_for_group(char *group) { char *pos, *spos, *linep = NULL, *olinep = NULL; int len, olen; int cont; struct linelist *lp; char line[LINSIZ + 1]; char *data = NULL; data = lookup (gtable, group); sprintf(line, "%s %s", group, data); pos = (char *)&line; #ifdef CANT_HAPPEN if (*pos == '#') continue; #endif while (*pos == ' ' || *pos == '\t') pos++; spos = pos; while (*pos != ' ' && *pos != '\t' && *pos != '\n' && *pos != '\0') pos++; len = pos - spos; while (*pos == ' ' || *pos == '\t') pos++; if (*pos != '\n' && *pos != '\0') { lp = (struct linelist *)malloc(sizeof (*lp)); lp->l_parsed = 0; lp->l_groupname = (char *)malloc(len + 1); bcopy(spos, lp->l_groupname, len); *(lp->l_groupname + len) = '\0'; len = strlen(pos); olen = 0; /* * Loop around handling line continuations. */ do { if (*(pos + len - 1) == '\n') len--; if (*(pos + len - 1) == '\\') { len--; cont = 1; } else cont = 0; if (len > 0) { linep = (char *)malloc(olen + len + 1); if (olen > 0) { bcopy(olinep, linep, olen); free(olinep); } bcopy(pos, linep + olen, len); olen += len; *(linep + olen) = '\0'; olinep = linep; } #ifdef CANT_HAPPEN if (cont) { if (fgets(line, LINSIZ, netf)) { pos = line; len = strlen(pos); } else cont = 0; } #endif } while (cont); lp->l_line = linep; lp->l_next = linehead; linehead = lp; #ifdef CANT_HAPPEN /* * If this is the one we wanted, we are done. */ if (!strcmp(lp->l_groupname, group)) #endif return (lp); } return ((struct linelist *)0); } diff --git a/libexec/revnetgroup/revnetgroup.c b/libexec/revnetgroup/revnetgroup.c index 11fba515a7ba..34ec0d9491c4 100644 --- a/libexec/revnetgroup/revnetgroup.c +++ b/libexec/revnetgroup/revnetgroup.c @@ -1,181 +1,176 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1995 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. * * reverse netgroup map generator program * * Written by Bill Paul * Center for Telecommunications Research * Columbia University, New York City */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include "hash.h" /* Default location of netgroup file. */ char *netgroup = "/etc/netgroup"; /* Stored hash table version of 'forward' netgroup database. */ struct group_entry *gtable[TABLESIZE]; /* * Stored hash table of 'reverse' netgroup member database * which we will construct. */ struct member_entry *mtable[TABLESIZE]; static void usage(void) { fprintf (stderr,"usage: revnetgroup -u | -h [-f netgroup_file]\n"); exit(1); } int main(int argc, char *argv[]) { FILE *fp; char readbuf[LINSIZ]; struct group_entry *gcur; struct member_entry *mcur; char *host, *user, *domain; int ch; char *key = NULL, *data = NULL; int hosts = -1, i; if (argc < 2) usage(); while ((ch = getopt(argc, argv, "uhf:")) != -1) { switch(ch) { case 'u': if (hosts != -1) { warnx("please use only one of -u or -h"); usage(); } hosts = 0; break; case 'h': if (hosts != -1) { warnx("please use only one of -u or -h"); usage(); } hosts = 1; break; case 'f': netgroup = optarg; break; default: usage(); break; } } if (hosts == -1) usage(); if (strcmp(netgroup, "-")) { if ((fp = fopen(netgroup, "r")) == NULL) { err(1, "%s", netgroup); } } else { fp = stdin; } /* Stuff all the netgroup names and members into a hash table. */ while (fgets(readbuf, LINSIZ, fp)) { if (readbuf[0] == '#') continue; /* handle backslash line continuations */ while(readbuf[strlen(readbuf) - 2] == '\\') { fgets((char *)&readbuf[strlen(readbuf) - 2], sizeof(readbuf) - strlen(readbuf), fp); } data = NULL; if ((data = (char *)(strpbrk(readbuf, " \t") + 1)) < (char *)2) continue; key = (char *)&readbuf; *(data - 1) = '\0'; store(gtable, key, data); } fclose(fp); /* * Find all members of each netgroup and keep track of which * group they belong to. */ for (i = 0; i < TABLESIZE; i++) { gcur = gtable[i]; while(gcur) { __setnetgrent(gcur->key); while(__getnetgrent(&host, &user, &domain) != 0) { if (hosts ? host && strcmp(host,"-") : user && strcmp(user, "-")) mstore(mtable, hosts ? host : user, gcur->key, domain); } gcur = gcur->next; } } /* Release resources used by the netgroup parser code. */ __endnetgrent(); /* Spew out the results. */ for (i = 0; i < TABLESIZE; i++) { mcur = mtable[i]; while(mcur) { struct grouplist *tmp; printf ("%s.%s\t", mcur->key, mcur->domain); tmp = mcur->groups; while(tmp) { printf ("%s", tmp->groupname); tmp = tmp->next; if (tmp) printf(","); } mcur = mcur->next; printf ("\n"); } } /* Let the OS free all our resources. */ exit(0); } diff --git a/libexec/rpc.rstatd/rstat_proc.c b/libexec/rpc.rstatd/rstat_proc.c index 1f1bb7cf4048..cc488657c077 100644 --- a/libexec/rpc.rstatd/rstat_proc.c +++ b/libexec/rpc.rstatd/rstat_proc.c @@ -1,478 +1,476 @@ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #ifndef lint #if 0 static char sccsid[] = "from: @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro"; static char sccsid[] = "from: @(#)rstat_proc.c 2.2 88/08/01 4.0 RPCSRC"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* * rstat service: built with rstat.x and derived from rpc.rstatd.c * * Copyright (c) 1984 by Sun Microsystems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef FSHIFT /* Use protocol's shift and scale values */ #undef FSCALE #undef if_ipackets #undef if_ierrors #undef if_opackets #undef if_oerrors #undef if_collisions #include int haveadisk(void); void updatexfers(int, int *); int stats_service(void); extern int from_inetd; int sincelastreq = 0; /* number of alarms since last request */ extern int closedown; union { struct stats s1; struct statsswtch s2; struct statstime s3; } stats_all; void updatestat(); static int stat_is_init = 0; static int cp_time_xlat[RSTAT_CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE }; static long bsd_cp_time[CPUSTATES]; #ifndef FSCALE #define FSCALE (1 << 8) #endif void stat_init(void) { stat_is_init = 1; alarm(0); updatestat(); (void) signal(SIGALRM, updatestat); alarm(1); } statstime * rstatproc_stats_3_svc(void *argp, struct svc_req *rqstp) { if (! stat_is_init) stat_init(); sincelastreq = 0; return(&stats_all.s3); } statsswtch * rstatproc_stats_2_svc(void *argp, struct svc_req *rqstp) { if (! stat_is_init) stat_init(); sincelastreq = 0; return(&stats_all.s2); } stats * rstatproc_stats_1_svc(void *argp, struct svc_req *rqstp) { if (! stat_is_init) stat_init(); sincelastreq = 0; return(&stats_all.s1); } u_int * rstatproc_havedisk_3_svc(void *argp, struct svc_req *rqstp) { static u_int have; if (! stat_is_init) stat_init(); sincelastreq = 0; have = haveadisk(); return(&have); } u_int * rstatproc_havedisk_2_svc(void *argp, struct svc_req *rqstp) { return(rstatproc_havedisk_3_svc(argp, rqstp)); } u_int * rstatproc_havedisk_1_svc(void *argp, struct svc_req *rqstp) { return(rstatproc_havedisk_3_svc(argp, rqstp)); } void updatestat(void) { int i, hz; struct clockinfo clockrate; struct ifmibdata ifmd; double avrun[3]; struct timeval tm, btm; int mib[6]; size_t len; uint64_t val; int ifcount; #ifdef DEBUG fprintf(stderr, "entering updatestat\n"); #endif if (sincelastreq >= closedown) { #ifdef DEBUG fprintf(stderr, "about to closedown\n"); #endif if (from_inetd) exit(0); else { stat_is_init = 0; return; } } sincelastreq++; mib[0] = CTL_KERN; mib[1] = KERN_CLOCKRATE; len = sizeof clockrate; if (sysctl(mib, 2, &clockrate, &len, 0, 0) < 0) { syslog(LOG_ERR, "sysctl(kern.clockrate): %m"); exit(1); } hz = clockrate.hz; len = sizeof(bsd_cp_time); if (sysctlbyname("kern.cp_time", bsd_cp_time, &len, 0, 0) < 0) { syslog(LOG_ERR, "sysctl(kern.cp_time): %m"); exit(1); } for(i = 0; i < RSTAT_CPUSTATES ; i++) stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]]; (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0])); stats_all.s2.avenrun[0] = avrun[0] * FSCALE; stats_all.s2.avenrun[1] = avrun[1] * FSCALE; stats_all.s2.avenrun[2] = avrun[2] * FSCALE; mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; len = sizeof btm; if (sysctl(mib, 2, &btm, &len, 0, 0) < 0) { syslog(LOG_ERR, "sysctl(kern.boottime): %m"); exit(1); } stats_all.s2.boottime.tv_sec = btm.tv_sec; stats_all.s2.boottime.tv_usec = btm.tv_usec; #ifdef DEBUG fprintf(stderr, "%d %d %d %d\n", stats_all.s1.cp_time[0], stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], stats_all.s1.cp_time[3]); #endif #define FETCH_CNT(stat, cnt) do { \ len = sizeof(uint64_t); \ if (sysctlbyname("vm.stats." #cnt , &val, &len, NULL, 0) < 0) { \ syslog(LOG_ERR, "sysctl(vm.stats." #cnt "): %m"); \ exit(1); \ } \ stat = val; \ } while (0) FETCH_CNT(stats_all.s1.v_pgpgin, vm.v_vnodepgsin); FETCH_CNT(stats_all.s1.v_pgpgout, vm.v_vnodepgsout); FETCH_CNT(stats_all.s1.v_pswpin, vm.v_swappgsin); FETCH_CNT(stats_all.s1.v_pswpout, vm.v_swappgsout); FETCH_CNT(stats_all.s1.v_intr, sys.v_intr); FETCH_CNT(stats_all.s2.v_swtch, sys.v_swtch); (void)gettimeofday(&tm, NULL); stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + hz*(tm.tv_usec - btm.tv_usec)/1000000; /* update disk transfers */ updatexfers(RSTAT_DK_NDRIVE, stats_all.s1.dk_xfer); mib[0] = CTL_NET; mib[1] = PF_LINK; mib[2] = NETLINK_GENERIC; mib[3] = IFMIB_SYSTEM; mib[4] = IFMIB_IFCOUNT; len = sizeof ifcount; if (sysctl(mib, 5, &ifcount, &len, 0, 0) < 0) { syslog(LOG_ERR, "sysctl(net.link.generic.system.ifcount): %m"); exit(1); } stats_all.s1.if_ipackets = 0; stats_all.s1.if_opackets = 0; stats_all.s1.if_ierrors = 0; stats_all.s1.if_oerrors = 0; stats_all.s1.if_collisions = 0; for (i = 1; i <= ifcount; i++) { len = sizeof ifmd; mib[3] = IFMIB_IFDATA; mib[4] = i; mib[5] = IFDATA_GENERAL; if (sysctl(mib, 6, &ifmd, &len, 0, 0) < 0) { if (errno == ENOENT) continue; syslog(LOG_ERR, "sysctl(net.link.ifdata.%d.general)" ": %m", i); exit(1); } stats_all.s1.if_ipackets += ifmd.ifmd_data.ifi_ipackets; stats_all.s1.if_opackets += ifmd.ifmd_data.ifi_opackets; stats_all.s1.if_ierrors += ifmd.ifmd_data.ifi_ierrors; stats_all.s1.if_oerrors += ifmd.ifmd_data.ifi_oerrors; stats_all.s1.if_collisions += ifmd.ifmd_data.ifi_collisions; } (void)gettimeofday(&tm, NULL); stats_all.s3.curtime.tv_sec = tm.tv_sec; stats_all.s3.curtime.tv_usec = tm.tv_usec; alarm(1); } /* * returns true if have a disk */ int haveadisk(void) { register int i; struct statinfo stats; int num_devices, retval = 0; if ((num_devices = devstat_getnumdevs(NULL)) < 0) { syslog(LOG_ERR, "rstatd: can't get number of devices: %s", devstat_errbuf); exit(1); } if (devstat_checkversion(NULL) < 0) { syslog(LOG_ERR, "rstatd: %s", devstat_errbuf); exit(1); } stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); bzero(stats.dinfo, sizeof(struct devinfo)); if (devstat_getdevs(NULL, &stats) == -1) { syslog(LOG_ERR, "rstatd: can't get device list: %s", devstat_errbuf); exit(1); } for (i = 0; i < stats.dinfo->numdevs; i++) { if (((stats.dinfo->devices[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) && ((stats.dinfo->devices[i].device_type & DEVSTAT_TYPE_PASS) == 0)) { retval = 1; break; } } if (stats.dinfo->mem_ptr) free(stats.dinfo->mem_ptr); free(stats.dinfo); return(retval); } void updatexfers(int numdevs, int *devs) { register int i, j, k, t; struct statinfo stats; int num_devices = 0; u_int64_t total_transfers; if ((num_devices = devstat_getnumdevs(NULL)) < 0) { syslog(LOG_ERR, "rstatd: can't get number of devices: %s", devstat_errbuf); exit(1); } if (devstat_checkversion(NULL) < 0) { syslog(LOG_ERR, "rstatd: %s", devstat_errbuf); exit(1); } stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); bzero(stats.dinfo, sizeof(struct devinfo)); if (devstat_getdevs(NULL, &stats) == -1) { syslog(LOG_ERR, "rstatd: can't get device list: %s", devstat_errbuf); exit(1); } for (i = 0, j = 0; i < stats.dinfo->numdevs && j < numdevs; i++) { if (((stats.dinfo->devices[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) && ((stats.dinfo->devices[i].device_type & DEVSTAT_TYPE_PASS) == 0)) { total_transfers = 0; for (k = 0; k < DEVSTAT_N_TRANS_FLAGS; k++) total_transfers += stats.dinfo->devices[i].operations[k]; /* * XXX KDM If the total transfers for this device * are greater than the amount we can fit in a * signed integer, just set them to the maximum * amount we can fit in a signed integer. I have a * feeling that the rstat protocol assumes 32-bit * integers, so this could well break on a 64-bit * architecture like the Alpha. */ if (total_transfers > INT_MAX) t = INT_MAX; else t = total_transfers; devs[j] = t; j++; } } if (stats.dinfo->mem_ptr) free(stats.dinfo->mem_ptr); free(stats.dinfo); } void rstat_service(struct svc_req *rqstp, SVCXPRT *transp) { union { int fill; } argument; void *result; xdrproc_t xdr_argument, xdr_result; typedef void *(svc_cb)(void *arg, struct svc_req *rqstp); svc_cb *local; switch (rqstp->rq_proc) { case NULLPROC: (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); goto leave; case RSTATPROC_STATS: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_statstime; switch (rqstp->rq_vers) { case RSTATVERS_ORIG: local = (svc_cb *)rstatproc_stats_1_svc; break; case RSTATVERS_SWTCH: local = (svc_cb *)rstatproc_stats_2_svc; break; case RSTATVERS_TIME: local = (svc_cb *)rstatproc_stats_3_svc; break; default: svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); goto leave; /*NOTREACHED*/ } break; case RSTATPROC_HAVEDISK: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_u_int; switch (rqstp->rq_vers) { case RSTATVERS_ORIG: local = (svc_cb *)rstatproc_havedisk_1_svc; break; case RSTATVERS_SWTCH: local = (svc_cb *)rstatproc_havedisk_2_svc; break; case RSTATVERS_TIME: local = (svc_cb *)rstatproc_havedisk_3_svc; break; default: svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); goto leave; /*NOTREACHED*/ } break; default: svcerr_noproc(transp); goto leave; } bzero((char *)&argument, sizeof(argument)); if (!svc_getargs(transp, xdr_argument, &argument)) { svcerr_decode(transp); goto leave; } result = (*local)(&argument, rqstp); if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { svcerr_systemerr(transp); } if (!svc_freeargs(transp, xdr_argument, &argument)) errx(1, "unable to free arguments"); leave: if (from_inetd) exit(0); } diff --git a/libexec/rpc.rstatd/rstatd.c b/libexec/rpc.rstatd/rstatd.c index 6a6b09b9ae0c..7cc3bac71c5d 100644 --- a/libexec/rpc.rstatd/rstatd.c +++ b/libexec/rpc.rstatd/rstatd.c @@ -1,131 +1,126 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1993, John Brezak * 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 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include extern void rstat_service(struct svc_req *, SVCXPRT *); int from_inetd = 1; /* started from inetd ? */ int closedown = 20; /* how long to wait before going dormant */ void cleanup(int sig __unused) { (void) rpcb_unset(RSTATPROG, RSTATVERS_TIME, NULL); (void) rpcb_unset(RSTATPROG, RSTATVERS_SWTCH, NULL); (void) rpcb_unset(RSTATPROG, RSTATVERS_ORIG, NULL); exit(0); } int main(int argc, char *argv[]) { SVCXPRT *transp; int ok; struct sockaddr_storage from; socklen_t fromlen; if (argc == 2) closedown = atoi(argv[1]); if (closedown <= 0) closedown = 20; /* * See if inetd started us */ fromlen = sizeof(from); if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { from_inetd = 0; } if (!from_inetd) { daemon(0, 0); (void)rpcb_unset(RSTATPROG, RSTATVERS_TIME, NULL); (void)rpcb_unset(RSTATPROG, RSTATVERS_SWTCH, NULL); (void)rpcb_unset(RSTATPROG, RSTATVERS_ORIG, NULL); (void) signal(SIGINT, cleanup); (void) signal(SIGTERM, cleanup); (void) signal(SIGHUP, cleanup); } openlog("rpc.rstatd", LOG_CONS|LOG_PID, LOG_DAEMON); if (from_inetd) { transp = svc_tli_create(0, NULL, NULL, 0, 0); if (transp == NULL) { syslog(LOG_ERR, "cannot create udp service."); exit(1); } ok = svc_reg(transp, RSTATPROG, RSTATVERS_TIME, rstat_service, NULL); } else ok = svc_create(rstat_service, RSTATPROG, RSTATVERS_TIME, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_TIME, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } if (from_inetd) ok = svc_reg(transp, RSTATPROG, RSTATVERS_SWTCH, rstat_service, NULL); else ok = svc_create(rstat_service, RSTATPROG, RSTATVERS_SWTCH, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_SWTCH, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } if (from_inetd) ok = svc_reg(transp, RSTATPROG, RSTATVERS_ORIG, rstat_service, NULL); else ok = svc_create(rstat_service, RSTATPROG, RSTATVERS_ORIG, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_ORIG, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } svc_run(); syslog(LOG_ERR, "svc_run returned"); exit(1); } diff --git a/libexec/rpc.rusersd/rusers_proc.c b/libexec/rpc.rusersd/rusers_proc.c index bfb65dadb004..3bc4169a989f 100644 --- a/libexec/rpc.rusersd/rusers_proc.c +++ b/libexec/rpc.rusersd/rusers_proc.c @@ -1,334 +1,329 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1993, John Brezak * 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 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #ifdef DEBUG #include #endif #include #include #include #include #include #include #include #ifdef XIDLE #include #include #include #endif #include #include "extern.h" #ifndef _PATH_DEV #define _PATH_DEV "/dev" #endif static utmpidle utmp_idle[MAXUSERS]; static utmp old_utmp[MAXUSERS]; static struct utmpx utmp_list[MAXUSERS]; #ifdef XIDLE static Display *dpy; static jmp_buf openAbort; static void abortOpen(void) { longjmp (openAbort, 1); } XqueryIdle(char *display) { int first_event, first_error; Time IdleTime; (void) signal (SIGALRM, abortOpen); (void) alarm ((unsigned) 10); if (!setjmp (openAbort)) { if (!(dpy= XOpenDisplay(display))) { syslog(LOG_ERR, "Cannot open display %s", display); return(-1); } if (XidleQueryExtension(dpy, &first_event, &first_error)) { if (!XGetIdleTime(dpy, &IdleTime)) { syslog(LOG_ERR, "%s: unable to get idle time", display); return(-1); } } else { syslog(LOG_ERR, "%s: Xidle extension not loaded", display); return(-1); } XCloseDisplay(dpy); } else { syslog(LOG_ERR, "%s: server grabbed for over 10 seconds", display); return(-1); } (void) signal (SIGALRM, SIG_DFL); (void) alarm ((unsigned) 0); IdleTime /= 1000; return((IdleTime + 30) / 60); } #endif static u_int getidle(const char *tty, const char *display __unused) { struct stat st; char ttyname[PATH_MAX]; time_t now; u_long idle; /* * If this is an X terminal or console, then try the * XIdle extension */ #ifdef XIDLE if (display && *display && (idle = XqueryIdle(display)) >= 0) return(idle); #endif idle = 0; if (*tty == 'X') { u_long kbd_idle, mouse_idle; #if !defined(__FreeBSD__) kbd_idle = getidle("kbd", NULL); #else kbd_idle = getidle("vga", NULL); #endif mouse_idle = getidle("mouse", NULL); idle = (kbd_idle < mouse_idle)?kbd_idle:mouse_idle; } else { sprintf(ttyname, "%s/%s", _PATH_DEV, tty); if (stat(ttyname, &st) < 0) { #ifdef DEBUG printf("%s: %s\n", ttyname, strerror(errno)); #endif return(-1); } time(&now); #ifdef DEBUG printf("%s: now=%d atime=%d\n", ttyname, now, st.st_atime); #endif idle = now - st.st_atime; idle = (idle + 30) / 60; /* secs->mins */ } return(idle); } static utmpidlearr * do_names_2(void) { static utmpidlearr ut; struct utmpx *usr; int nusers = 0; memset(&ut, 0, sizeof(ut)); ut.utmpidlearr_val = &utmp_idle[0]; setutxent(); while ((usr = getutxent()) != NULL && nusers < MAXUSERS) { if (usr->ut_type != USER_PROCESS) continue; memcpy(&utmp_list[nusers], usr, sizeof(*usr)); utmp_idle[nusers].ui_utmp.ut_time = usr->ut_tv.tv_sec; utmp_idle[nusers].ui_idle = getidle(usr->ut_line, usr->ut_host); utmp_idle[nusers].ui_utmp.ut_line = utmp_list[nusers].ut_line; utmp_idle[nusers].ui_utmp.ut_name = utmp_list[nusers].ut_user; utmp_idle[nusers].ui_utmp.ut_host = utmp_list[nusers].ut_host; nusers++; } endutxent(); ut.utmpidlearr_len = nusers; return(&ut); } static int * rusers_num(void *argp __unused, struct svc_req *rqstp __unused) { static int num_users = 0; struct utmpx *usr; setutxent(); while ((usr = getutxent()) != NULL) { if (usr->ut_type != USER_PROCESS) continue; num_users++; } endutxent(); return(&num_users); } static utmparr * do_names_1(void) { utmpidlearr *utidle; static utmparr ut; unsigned int i; bzero((char *)&ut, sizeof(ut)); utidle = do_names_2(); if (utidle) { ut.utmparr_len = utidle->utmpidlearr_len; ut.utmparr_val = &old_utmp[0]; for (i = 0; i < ut.utmparr_len; i++) bcopy(&utmp_idle[i].ui_utmp, &old_utmp[i], sizeof(old_utmp[0])); } return(&ut); } utmpidlearr * rusersproc_names_2_svc(void *argp __unused, struct svc_req *rqstp __unused) { return (do_names_2()); } utmpidlearr * rusersproc_allnames_2_svc(void *argp __unused, struct svc_req *rqstp __unused) { return (do_names_2()); } utmparr * rusersproc_names_1_svc(void *argp __unused, struct svc_req *rqstp __unused) { return (do_names_1()); } utmparr * rusersproc_allnames_1_svc(void *argp __unused, struct svc_req *rqstp __unused) { return (do_names_1()); } typedef void *(*rusersproc_t)(void *, struct svc_req *); void rusers_service(struct svc_req *rqstp, SVCXPRT *transp) { union { int fill; } argument; char *result; xdrproc_t xdr_argument, xdr_result; rusersproc_t local; switch (rqstp->rq_proc) { case NULLPROC: (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); goto leave; case RUSERSPROC_NUM: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_int; local = (rusersproc_t)rusers_num; break; case RUSERSPROC_NAMES: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_utmpidlearr; switch (rqstp->rq_vers) { case RUSERSVERS_ORIG: local = (rusersproc_t)rusersproc_names_1_svc; break; case RUSERSVERS_IDLE: local = (rusersproc_t)rusersproc_names_2_svc; break; default: svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE); goto leave; /*NOTREACHED*/ } break; case RUSERSPROC_ALLNAMES: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_utmpidlearr; switch (rqstp->rq_vers) { case RUSERSVERS_ORIG: local = (rusersproc_t)rusersproc_allnames_1_svc; break; case RUSERSVERS_IDLE: local = (rusersproc_t)rusersproc_allnames_2_svc; break; default: svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE); goto leave; /*NOTREACHED*/ } break; default: svcerr_noproc(transp); goto leave; } bzero(&argument, sizeof(argument)); if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) { svcerr_decode(transp); goto leave; } result = (*local)(&argument, rqstp); if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) { svcerr_systemerr(transp); } if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, &argument)) { syslog(LOG_ERR, "unable to free arguments"); exit(1); } leave: if (from_inetd) exit(0); } diff --git a/libexec/rpc.rusersd/rusersd.c b/libexec/rpc.rusersd/rusersd.c index e1e77398c8ff..cf00dd8d181e 100644 --- a/libexec/rpc.rusersd/rusersd.c +++ b/libexec/rpc.rusersd/rusersd.c @@ -1,114 +1,109 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1993, John Brezak * 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 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include "extern.h" int from_inetd = 1; static void cleanup(int sig __unused) { (void) rpcb_unset(RUSERSPROG, RUSERSVERS_IDLE, NULL); (void) rpcb_unset(RUSERSPROG, RUSERSVERS_ORIG, NULL); exit(0); } int main(int argc __unused, char *argv[] __unused) { SVCXPRT *transp = NULL; /* Keep compiler happy. */ int ok; struct sockaddr_storage from; socklen_t fromlen; /* * See if inetd started us */ fromlen = sizeof(from); if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { from_inetd = 0; } if (!from_inetd) { daemon(0, 0); (void) rpcb_unset(RUSERSPROG, RUSERSVERS_IDLE, NULL); (void) rpcb_unset(RUSERSPROG, RUSERSVERS_ORIG, NULL); (void) signal(SIGINT, cleanup); (void) signal(SIGTERM, cleanup); (void) signal(SIGHUP, cleanup); } openlog("rpc.rusersd", LOG_CONS|LOG_PID, LOG_DAEMON); if (from_inetd) { transp = svc_tli_create(0, NULL, NULL, 0, 0); if (transp == NULL) { syslog(LOG_ERR, "cannot create udp service."); exit(1); } ok = svc_reg(transp, RUSERSPROG, RUSERSVERS_IDLE, rusers_service, NULL); } else ok = svc_create(rusers_service, RUSERSPROG, RUSERSVERS_IDLE, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_IDLE, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } if (from_inetd) ok = svc_reg(transp, RUSERSPROG, RUSERSVERS_ORIG, rusers_service, NULL); else ok = svc_create(rusers_service, RUSERSPROG, RUSERSVERS_ORIG, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_ORIG, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } svc_run(); syslog(LOG_ERR, "svc_run returned"); exit(1); } diff --git a/libexec/rpc.sprayd/sprayd.c b/libexec/rpc.sprayd/sprayd.c index 209d74af59af..2a71a93bf4ef 100644 --- a/libexec/rpc.sprayd/sprayd.c +++ b/libexec/rpc.sprayd/sprayd.c @@ -1,165 +1,160 @@ /* $NetBSD: sprayd.c,v 1.15 2009/10/21 01:07:46 snj Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1994 Christos Zoulas * 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 ``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 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include static void spray_service(struct svc_req *, SVCXPRT *); static int from_inetd = 1; #define timersub(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ if ((vvp)->tv_usec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000; \ } \ } while (0) #define TIMEOUT 120 static void cleanup(int sig __unused) { (void)rpcb_unset(SPRAYPROG, SPRAYVERS, NULL); exit(0); } static void die(int sig __unused) { exit(0); } int main(int argc __unused, char *argv[] __unused) { SVCXPRT *transp; int ok; struct sockaddr_storage from; socklen_t fromlen; /* * See if inetd started us */ fromlen = sizeof(from); if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { from_inetd = 0; } if (!from_inetd) { daemon(0, 0); (void)rpcb_unset(SPRAYPROG, SPRAYVERS, NULL); (void)signal(SIGINT, cleanup); (void)signal(SIGTERM, cleanup); (void)signal(SIGHUP, cleanup); } else { (void)signal(SIGALRM, die); alarm(TIMEOUT); } openlog("rpc.sprayd", LOG_PID, LOG_DAEMON); if (from_inetd) { transp = svc_tli_create(0, NULL, NULL, 0, 0); if (transp == NULL) { syslog(LOG_ERR, "cannot create udp service."); exit(1); } ok = svc_reg(transp, SPRAYPROG, SPRAYVERS, spray_service, NULL); } else ok = svc_create(spray_service, SPRAYPROG, SPRAYVERS, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (SPRAYPROG, SPRAYVERS, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } svc_run(); syslog(LOG_ERR, "svc_run returned"); return 1; } static void spray_service(struct svc_req *rqstp, SVCXPRT *transp) { static spraycumul scum; static struct timeval clear, get; switch (rqstp->rq_proc) { case SPRAYPROC_CLEAR: scum.counter = 0; (void)gettimeofday(&clear, 0); /*FALLTHROUGH*/ case NULLPROC: (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); return; case SPRAYPROC_SPRAY: scum.counter++; return; case SPRAYPROC_GET: (void)gettimeofday(&get, 0); timersub(&get, &clear, &get); scum.clock.sec = get.tv_sec; scum.clock.usec = get.tv_usec; break; default: svcerr_noproc(transp); return; } if (!svc_sendreply(transp, (xdrproc_t)xdr_spraycumul, &scum)) { svcerr_systemerr(transp); syslog(LOG_WARNING, "bad svc_sendreply"); } } diff --git a/libexec/rtld-elf/rtld_malloc.c b/libexec/rtld-elf/rtld_malloc.c index 4b5140551675..dafbc222322e 100644 --- a/libexec/rtld-elf/rtld_malloc.c +++ b/libexec/rtld-elf/rtld_malloc.c @@ -1,326 +1,325 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983 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. * 3. 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 defined(LIBC_SCCS) && !defined(lint) /*static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/ -static char *rcsid = "$FreeBSD$"; #endif /* LIBC_SCCS and not lint */ /* * malloc.c (Caltech) 2/21/82 * Chris Kingsley, kingsley@cit-20. * * This is a very fast storage allocator. It allocates blocks of a small * number of different sizes, and keeps free lists of each size. Blocks that * don't exactly fit are passed up to the next larger size. In this * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long. * This is designed for use in a virtual memory environment. */ #include #include #include #include #include #include #include #include #ifdef IN_RTLD #include "rtld.h" #include "rtld_printf.h" #include "rtld_paths.h" #endif #include "rtld_malloc.h" /* * Pre-allocate mmap'ed pages */ #define NPOOLPAGES (128*1024/pagesz) static caddr_t pagepool_start, pagepool_end; /* * The overhead on a block is at least 4 bytes. When free, this space * contains a pointer to the next free block, and the bottom two bits must * be zero. When in use, the first byte is set to MAGIC, and the second * byte is the size index. The remaining bytes are for alignment. */ union overhead { union overhead *ov_next; /* when free */ struct { uint16_t ovu_index; /* bucket # */ uint8_t ovu_magic; /* magic number */ } ovu; #define ov_magic ovu.ovu_magic #define ov_index ovu.ovu_index }; static void morecore(int bucket); static int morepages(int n); #define MAGIC 0xef /* magic # on accounting info */ #define AMAGIC 0xdf /* magic # for aligned alloc */ /* * nextf[i] is the pointer to the next free block of size * (FIRST_BUCKET_SIZE << i). The overhead information precedes the data * area returned to the user. */ #define LOW_BITS 3 #define FIRST_BUCKET_SIZE (1U << LOW_BITS) #define NBUCKETS 30 static union overhead *nextf[NBUCKETS]; static int pagesz; /* page size */ /* * The array of supported page sizes is provided by the user, i.e., the * program that calls this storage allocator. That program must initialize * the array before making its first call to allocate storage. The array * must contain at least one page size. The page sizes must be stored in * increasing order. */ static void * cp2op(void *cp) { return (((caddr_t)cp - sizeof(union overhead))); } void * __crt_malloc(size_t nbytes) { union overhead *op; int bucket; size_t amt; /* * First time malloc is called, setup page size. */ if (pagesz == 0) pagesz = pagesizes[0]; /* * Convert amount of memory requested into closest block size * stored in hash buckets which satisfies request. * Account for space used per block for accounting. */ amt = FIRST_BUCKET_SIZE; bucket = 0; while (nbytes > amt - sizeof(*op)) { amt <<= 1; bucket++; if (amt == 0 || bucket >= NBUCKETS) return (NULL); } /* * If nothing in hash bucket right now, * request more memory from the system. */ if ((op = nextf[bucket]) == NULL) { morecore(bucket); if ((op = nextf[bucket]) == NULL) return (NULL); } /* remove from linked list */ nextf[bucket] = op->ov_next; op->ov_magic = MAGIC; op->ov_index = bucket; return ((char *)(op + 1)); } void * __crt_calloc(size_t num, size_t size) { void *ret; if (size != 0 && (num * size) / size != num) { /* size_t overflow. */ return (NULL); } if ((ret = __crt_malloc(num * size)) != NULL) memset(ret, 0, num * size); return (ret); } void * __crt_aligned_alloc_offset(size_t align, size_t size, size_t offset) { void *mem, *ov; union overhead ov1; uintptr_t x; if (align < FIRST_BUCKET_SIZE) align = FIRST_BUCKET_SIZE; offset &= align - 1; mem = __crt_malloc(size + align + offset + sizeof(union overhead)); if (mem == NULL) return (NULL); x = roundup2((uintptr_t)mem + sizeof(union overhead), align); x += offset; ov = cp2op((void *)x); ov1.ov_magic = AMAGIC; ov1.ov_index = x - (uintptr_t)mem + sizeof(union overhead); memcpy(ov, &ov1, sizeof(ov1)); return ((void *)x); } /* * Allocate more memory to the indicated bucket. */ static void morecore(int bucket) { union overhead *op; int sz; /* size of desired block */ int amt; /* amount to allocate */ int nblks; /* how many blocks we get */ sz = FIRST_BUCKET_SIZE << bucket; if (sz < pagesz) { amt = pagesz; nblks = amt / sz; } else { amt = sz; nblks = 1; } if (amt > pagepool_end - pagepool_start) if (morepages(amt / pagesz + NPOOLPAGES) == 0 && /* Retry with min required size */ morepages(amt / pagesz) == 0) return; op = (union overhead *)pagepool_start; pagepool_start += amt; /* * Add new memory allocated to that on * free list for this hash bucket. */ nextf[bucket] = op; while (--nblks > 0) { op->ov_next = (union overhead *)((caddr_t)op + sz); op = (union overhead *)((caddr_t)op + sz); } } void __crt_free(void *cp) { union overhead *op, op1; void *opx; int size; if (cp == NULL) return; opx = cp2op(cp); memcpy(&op1, opx, sizeof(op1)); op = op1.ov_magic == AMAGIC ? (void *)((caddr_t)cp - op1.ov_index) : opx; if (op->ov_magic != MAGIC) return; /* sanity */ size = op->ov_index; op->ov_next = nextf[size]; /* also clobbers ov_magic */ nextf[size] = op; } void * __crt_realloc(void *cp, size_t nbytes) { u_int onb; int i; union overhead *op; char *res; if (cp == NULL) return (__crt_malloc(nbytes)); op = cp2op(cp); if (op->ov_magic != MAGIC) return (NULL); /* Double-free or bad argument */ i = op->ov_index; onb = 1 << (i + 3); if (onb < (u_int)pagesz) onb -= sizeof(*op); else onb += pagesz - sizeof(*op); /* avoid the copy if same size block */ if (i != 0) { i = 1 << (i + 2); if (i < pagesz) i -= sizeof(*op); else i += pagesz - sizeof(*op); } if (nbytes <= onb && nbytes > (size_t)i) return (cp); if ((res = __crt_malloc(nbytes)) == NULL) return (NULL); bcopy(cp, res, (nbytes < onb) ? nbytes : onb); __crt_free(cp); return (res); } static int morepages(int n) { caddr_t addr; int offset; if (pagepool_end - pagepool_start > pagesz) { addr = roundup2(pagepool_start, pagesz); if (munmap(addr, pagepool_end - addr) != 0) { #ifdef IN_RTLD rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": " "morepages: cannot munmap %p: %s\n", addr, rtld_strerror(errno)); #endif } } offset = (uintptr_t)pagepool_start - rounddown2( (uintptr_t)pagepool_start, pagesz); addr = mmap(0, n * pagesz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (addr == MAP_FAILED) { #ifdef IN_RTLD rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": morepages: " "cannot mmap anonymous memory: %s\n", rtld_strerror(errno)); #endif pagepool_start = pagepool_end = NULL; return (0); } pagepool_start = addr; pagepool_end = pagepool_start + n * pagesz; pagepool_start += offset; return (n); } diff --git a/libexec/talkd/announce.c b/libexec/talkd/announce.c index 9cf326669998..b1b1acc09553 100644 --- a/libexec/talkd/announce.c +++ b/libexec/talkd/announce.c @@ -1,167 +1,165 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)announce.c 8.3 (Berkeley) 4/28/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ttymsg.h" #include "extern.h" /* * Announce an invitation to talk. */ /* * See if the user is accepting messages. If so, announce that * a talk is requested. */ int announce(CTL_MSG *request, const char *remote_machine) { char full_tty[32]; struct stat stbuf; (void)snprintf(full_tty, sizeof(full_tty), "%s%s", _PATH_DEV, request->r_tty); if (stat(full_tty, &stbuf) < 0 || (stbuf.st_mode&020) == 0) return (PERMISSION_DENIED); return (print_mesg(request->r_tty, request, remote_machine)); } #define max(a,b) ( (a) > (b) ? (a) : (b) ) #define N_LINES 5 #define N_CHARS 256 /* * Build a block of characters containing the message. * It is sent blank filled and in a single block to * try to keep the message in one piece if the recipient * in vi at the time */ int print_mesg(const char *tty, CTL_MSG *request, const char *remote_machine) { struct timeval now; time_t clock_sec; struct tm *localclock; struct iovec iovec; char line_buf[N_LINES][N_CHARS]; int sizes[N_LINES]; char big_buf[N_LINES*N_CHARS]; char *bptr, *lptr, *vis_user; int i, j, max_size; i = 0; max_size = 0; gettimeofday(&now, NULL); clock_sec = now.tv_sec; localclock = localtime(&clock_sec); (void)snprintf(line_buf[i], N_CHARS, " "); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; (void)snprintf(line_buf[i], N_CHARS, "Message from Talk_Daemon@%s at %d:%02d on %d/%.2d/%.2d ...", hostname, localclock->tm_hour , localclock->tm_min, localclock->tm_year + 1900, localclock->tm_mon + 1, localclock->tm_mday); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; vis_user = malloc(strlen(request->l_name) * 4 + 1); strvis(vis_user, request->l_name, VIS_CSTYLE); (void)snprintf(line_buf[i], N_CHARS, "talk: connection requested by %s@%s", vis_user, remote_machine); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; (void)snprintf(line_buf[i], N_CHARS, "talk: respond with: talk %s@%s", vis_user, remote_machine); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; (void)snprintf(line_buf[i], N_CHARS, " "); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; bptr = big_buf; *bptr++ = '\007'; /* send something to wake them up */ *bptr++ = '\r'; /* add a \r in case of raw mode */ *bptr++ = '\n'; for (i = 0; i < N_LINES; i++) { /* copy the line into the big buffer */ lptr = line_buf[i]; while (*lptr != '\0') *(bptr++) = *(lptr++); /* pad out the rest of the lines with blanks */ for (j = sizes[i]; j < max_size + 2; j++) *(bptr++) = ' '; *(bptr++) = '\r'; /* add a \r in case of raw mode */ *(bptr++) = '\n'; } *bptr = '\0'; iovec.iov_base = big_buf; iovec.iov_len = bptr - big_buf; /* * we choose a timeout of RING_WAIT-5 seconds so that we don't * stack up processes trying to write messages to a tty * that is permanently blocked. */ if (ttymsg(&iovec, 1, tty, RING_WAIT - 5) != NULL) return (FAILED); return (SUCCESS); } diff --git a/libexec/talkd/print.c b/libexec/talkd/print.c index 4d10329dd7f8..47ccb89f20d6 100644 --- a/libexec/talkd/print.c +++ b/libexec/talkd/print.c @@ -1,91 +1,89 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* debug print routines */ #include #include #include #include #include #include #include "extern.h" static const char *types[] = { "leave_invite", "look_up", "delete", "announce" }; #define NTYPES (sizeof (types) / sizeof (types[0])) static const char *answers[] = { "success", "not_here", "failed", "machine_unknown", "permission_denied", "unknown_request", "badversion", "badaddr", "badctladdr" }; #define NANSWERS (sizeof (answers) / sizeof (answers[0])) void print_request(const char *cp, CTL_MSG *mp) { const char *tp; char tbuf[80]; if (mp->type > NTYPES) { (void)snprintf(tbuf, sizeof(tbuf), "type %d", mp->type); tp = tbuf; } else tp = types[mp->type]; syslog(LOG_DEBUG, "%s: %s: id %lu, l_user %s, r_user %s, r_tty %s", cp, tp, (long)mp->id_num, mp->l_name, mp->r_name, mp->r_tty); } void print_response(const char *cp, CTL_RESPONSE *rp) { const char *tp, *ap; char tbuf[80], abuf[80]; if (rp->type > NTYPES) { (void)snprintf(tbuf, sizeof(tbuf), "type %d", rp->type); tp = tbuf; } else tp = types[rp->type]; if (rp->answer > NANSWERS) { (void)snprintf(abuf, sizeof(abuf), "answer %d", rp->answer); ap = abuf; } else ap = answers[rp->answer]; syslog(LOG_DEBUG, "%s: %s: %s, id %d", cp, tp, ap, ntohl(rp->id_num)); } diff --git a/libexec/talkd/process.c b/libexec/talkd/process.c index 2bd22a1becb2..886fc038ab97 100644 --- a/libexec/talkd/process.c +++ b/libexec/talkd/process.c @@ -1,223 +1,221 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)process.c 8.2 (Berkeley) 11/16/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * process.c handles the requests, which can be of three types: * ANNOUNCE - announce to a user that a talk is wanted * LEAVE_INVITE - insert the request into the table * LOOK_UP - look up to see if a request is waiting in * in the table for the local user * DELETE - delete invitation */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" void process_request(CTL_MSG *mp, CTL_RESPONSE *rp) { CTL_MSG *ptr; char *s; rp->vers = TALK_VERSION; rp->type = mp->type; rp->id_num = htonl(0); if (mp->vers != TALK_VERSION) { syslog(LOG_WARNING, "bad protocol version %d", mp->vers); rp->answer = BADVERSION; return; } mp->id_num = ntohl(mp->id_num); mp->addr.sa_family = ntohs(mp->addr.sa_family); if (mp->addr.sa_family != AF_INET) { syslog(LOG_WARNING, "bad address, family %d", mp->addr.sa_family); rp->answer = BADADDR; return; } mp->ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family); if (mp->ctl_addr.sa_family != AF_INET) { syslog(LOG_WARNING, "bad control address, family %d", mp->ctl_addr.sa_family); rp->answer = BADCTLADDR; return; } for (s = mp->l_name; *s; s++) if (!isprint(*s)) { syslog(LOG_NOTICE, "illegal user name. Aborting"); rp->answer = FAILED; return; } mp->pid = ntohl(mp->pid); if (debug) print_request("process_request", mp); switch (mp->type) { case ANNOUNCE: do_announce(mp, rp); break; case LEAVE_INVITE: ptr = find_request(mp); if (ptr != (CTL_MSG *)0) { rp->id_num = htonl(ptr->id_num); rp->answer = SUCCESS; } else insert_table(mp, rp); break; case LOOK_UP: ptr = find_match(mp); if (ptr != (CTL_MSG *)0) { rp->id_num = htonl(ptr->id_num); rp->addr = ptr->addr; rp->addr.sa_family = htons(ptr->addr.sa_family); rp->answer = SUCCESS; } else rp->answer = NOT_HERE; break; case DELETE: rp->answer = delete_invite(mp->id_num); break; default: rp->answer = UNKNOWN_REQUEST; break; } if (debug) print_response("process_request", rp); } void do_announce(CTL_MSG *mp, CTL_RESPONSE *rp) { struct hostent *hp; CTL_MSG *ptr; int result; /* see if the user is logged */ result = find_user(mp->r_name, mp->r_tty); if (result != SUCCESS) { rp->answer = result; return; } #define satosin(sa) ((struct sockaddr_in *)(void *)(sa)) hp = gethostbyaddr(&satosin(&mp->ctl_addr)->sin_addr, sizeof (struct in_addr), AF_INET); if (hp == (struct hostent *)0) { rp->answer = MACHINE_UNKNOWN; return; } ptr = find_request(mp); if (ptr == (CTL_MSG *) 0) { insert_table(mp, rp); rp->answer = announce(mp, hp->h_name); return; } if (mp->id_num > ptr->id_num) { /* * This is an explicit re-announce, so update the id_num * field to avoid duplicates and re-announce the talk. */ ptr->id_num = new_id(); rp->id_num = htonl(ptr->id_num); rp->answer = announce(mp, hp->h_name); } else { /* a duplicated request, so ignore it */ rp->id_num = htonl(ptr->id_num); rp->answer = SUCCESS; } } /* * Search utmp for the local user */ int find_user(const char *name, char *tty) { struct utmpx *ut; int status; struct stat statb; time_t best = 0; char ftty[sizeof(_PATH_DEV) - 1 + sizeof(ut->ut_line)]; setutxent(); status = NOT_HERE; (void) strcpy(ftty, _PATH_DEV); while ((ut = getutxent()) != NULL) if (ut->ut_type == USER_PROCESS && strcmp(ut->ut_user, name) == 0) { if (*tty == '\0' || best != 0) { if (best == 0) status = PERMISSION_DENIED; /* no particular tty was requested */ (void) strcpy(ftty + sizeof(_PATH_DEV) - 1, ut->ut_line); if (stat(ftty, &statb) == 0) { if (!(statb.st_mode & 020)) continue; if (statb.st_atime > best) { best = statb.st_atime; (void) strcpy(tty, ut->ut_line); status = SUCCESS; continue; } } } if (strcmp(ut->ut_line, tty) == 0) { status = SUCCESS; break; } } endutxent(); return (status); } diff --git a/libexec/talkd/table.c b/libexec/talkd/table.c index ed1560f86ff8..495d63b02b06 100644 --- a/libexec/talkd/table.c +++ b/libexec/talkd/table.c @@ -1,235 +1,233 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)table.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Routines to handle insertion, deletion, etc on the table * of requests kept by the daemon. Nothing fancy here, linear * search on a double-linked list. A time is kept with each * entry so that overly old invitations can be eliminated. * * Consider this a mis-guided attempt at modularity */ #include #include #include #include #include #include #include #include #include #include #include "extern.h" #define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */ #define NIL ((TABLE_ENTRY *)0) static struct timespec ts; typedef struct table_entry TABLE_ENTRY; struct table_entry { CTL_MSG request; long time; TABLE_ENTRY *next; TABLE_ENTRY *last; }; static void delete(TABLE_ENTRY *); static TABLE_ENTRY *table = NIL; /* * Look in the table for an invitation that matches the current * request looking for an invitation */ CTL_MSG * find_match(CTL_MSG *request) { TABLE_ENTRY *ptr, *next; time_t current_time; clock_gettime(CLOCK_MONOTONIC_FAST, &ts); current_time = ts.tv_sec; if (debug) print_request("find_match", request); for (ptr = table; ptr != NIL; ptr = next) { next = ptr->next; if ((ptr->time - current_time) > MAX_LIFE) { /* the entry is too old */ if (debug) print_request("deleting expired entry", &ptr->request); delete(ptr); continue; } if (debug) print_request("", &ptr->request); if (strcmp(request->l_name, ptr->request.r_name) == 0 && strcmp(request->r_name, ptr->request.l_name) == 0 && ptr->request.type == LEAVE_INVITE) return (&ptr->request); } return ((CTL_MSG *)0); } /* * Look for an identical request, as opposed to a complimentary * one as find_match does */ CTL_MSG * find_request(CTL_MSG *request) { TABLE_ENTRY *ptr, *next; time_t current_time; clock_gettime(CLOCK_MONOTONIC_FAST, &ts); current_time = ts.tv_sec; /* * See if this is a repeated message, and check for * out of date entries in the table while we are it. */ if (debug) print_request("find_request", request); for (ptr = table; ptr != NIL; ptr = next) { next = ptr->next; if ((ptr->time - current_time) > MAX_LIFE) { /* the entry is too old */ if (debug) print_request("deleting expired entry", &ptr->request); delete(ptr); continue; } if (debug) print_request("", &ptr->request); if (strcmp(request->r_name, ptr->request.r_name) == 0 && strcmp(request->l_name, ptr->request.l_name) == 0 && request->type == ptr->request.type && request->pid == ptr->request.pid) { /* update the time if we 'touch' it */ ptr->time = current_time; return (&ptr->request); } } return ((CTL_MSG *)0); } void insert_table(CTL_MSG *request, CTL_RESPONSE *response) { TABLE_ENTRY *ptr; time_t current_time; clock_gettime(CLOCK_MONOTONIC_FAST, &ts); current_time = ts.tv_sec; request->id_num = new_id(); response->id_num = htonl(request->id_num); /* insert a new entry into the top of the list */ ptr = (TABLE_ENTRY *)malloc(sizeof(TABLE_ENTRY)); if (ptr == NIL) { syslog(LOG_ERR, "insert_table: Out of memory"); _exit(1); } ptr->time = current_time; ptr->request = *request; ptr->next = table; if (ptr->next != NIL) ptr->next->last = ptr; ptr->last = NIL; table = ptr; } /* * Generate a unique non-zero sequence number */ int new_id(void) { static int current_id = 0; current_id = (current_id + 1) % MAX_ID; /* 0 is reserved, helps to pick up bugs */ if (current_id == 0) current_id = 1; return (current_id); } /* * Delete the invitation with id 'id_num' */ int delete_invite(u_int32_t id_num) { TABLE_ENTRY *ptr; if (debug) syslog(LOG_DEBUG, "delete_invite(%d)", id_num); for (ptr = table; ptr != NIL; ptr = ptr->next) { if (ptr->request.id_num == id_num) break; if (debug) print_request("", &ptr->request); } if (ptr != NIL) { delete(ptr); return (SUCCESS); } return (NOT_HERE); } /* * Classic delete from a double-linked list */ static void delete(TABLE_ENTRY *ptr) { if (debug) print_request("delete", &ptr->request); if (table == ptr) table = ptr->next; else if (ptr->last != NIL) ptr->last->next = ptr->next; if (ptr->next != NIL) ptr->next->last = ptr->last; free((char *)ptr); } diff --git a/libexec/talkd/talkd.c b/libexec/talkd/talkd.c index eb609207b156..76d0ec5a09ad 100644 --- a/libexec/talkd/talkd.c +++ b/libexec/talkd/talkd.c @@ -1,139 +1,137 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)talkd.c 8.1 (Berkeley) 6/4/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * The top level of the daemon, the format is heavily borrowed * from rwhod.c. Basically: find out who and where you are; * disconnect all descriptors and ttys, and then endless * loop on waiting for and processing requests */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" static CTL_MSG request; static CTL_RESPONSE response; int debug = 0; static long lastmsgtime; char hostname[MAXHOSTNAMELEN]; #define TIMEOUT 30 #define MAXIDLE 120 int main(int argc, char *argv[]) { register CTL_MSG *mp = &request; int cc; struct sockaddr ctl_addr; #ifdef NOTDEF /* * removed so ntalkd can run in tty sandbox */ if (getuid()) errx(1, "getuid: not super-user"); #endif openlog("talkd", LOG_PID, LOG_DAEMON); if (gethostname(hostname, sizeof(hostname) - 1) < 0) { syslog(LOG_ERR, "gethostname: %m"); _exit(1); } hostname[sizeof(hostname) - 1] = '\0'; if (chdir(_PATH_DEV) < 0) { syslog(LOG_ERR, "chdir: %s: %m", _PATH_DEV); _exit(1); } if (argc > 1 && strcmp(argv[1], "-d") == 0) debug = 1; signal(SIGALRM, timeout); alarm(TIMEOUT); for (;;) { cc = recv(0, (char *)mp, sizeof(*mp), 0); if (cc != sizeof (*mp)) { if (cc < 0 && errno != EINTR) syslog(LOG_WARNING, "recv: %m"); continue; } lastmsgtime = time(0); (void)memcpy(&ctl_addr.sa_data, &mp->ctl_addr.sa_data, sizeof(ctl_addr.sa_data)); ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family); ctl_addr.sa_len = sizeof(ctl_addr); process_request(mp, &response); /* can block here, is this what I want? */ cc = sendto(STDIN_FILENO, (char *)&response, sizeof(response), 0, &ctl_addr, sizeof(ctl_addr)); if (cc != sizeof (response)) syslog(LOG_WARNING, "sendto: %m"); } } void timeout(int sig __unused) { int save_errno = errno; if (time(0) - lastmsgtime >= MAXIDLE) _exit(0); alarm(TIMEOUT); errno = save_errno; } diff --git a/sbin/dump/dumprmt.c b/sbin/dump/dumprmt.c index d1769faa3daf..a8b2d9c221da 100644 --- a/sbin/dump/dumprmt.c +++ b/sbin/dump/dumprmt.c @@ -1,376 +1,374 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)dumprmt.c 8.3 (Berkeley) 4/28/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "dump.h" #define TS_CLOSED 0 #define TS_OPEN 1 static int rmtstate = TS_CLOSED; static int rmtape; static char *rmtpeer; static int okname(const char *); static int rmtcall(const char *, const char *); static void rmtconnaborted(int); static int rmtgetb(void); static void rmtgetconn(void); static void rmtgets(char *, int); static int rmtreply(const char *); static int errfd = -1; int rmthost(const char *host) { rmtpeer = strdup(host); if (rmtpeer == NULL) return (0); signal(SIGPIPE, rmtconnaborted); rmtgetconn(); if (rmtape < 0) return (0); return (1); } static void rmtconnaborted(int sig __unused) { msg("Lost connection to remote host.\n"); if (errfd != -1) { fd_set r; struct timeval t; FD_ZERO(&r); FD_SET(errfd, &r); t.tv_sec = 0; t.tv_usec = 0; if (select(errfd + 1, &r, NULL, NULL, &t)) { int i; char buf[2048]; if ((i = read(errfd, buf, sizeof(buf) - 1)) > 0) { buf[i] = '\0'; msg("on %s: %s%s", rmtpeer, buf, buf[i - 1] == '\n' ? "" : "\n"); } } } exit(X_ABORT); } void rmtgetconn(void) { char *cp; const char *rmt; static struct servent *sp = NULL; static struct passwd *pwd = NULL; char *tuser; int size; int throughput; int on; if (sp == NULL) { sp = getservbyname("shell", "tcp"); if (sp == NULL) { msg("shell/tcp: unknown service\n"); exit(X_STARTUP); } pwd = getpwuid(getuid()); if (pwd == NULL) { msg("who are you?\n"); exit(X_STARTUP); } } if ((cp = strchr(rmtpeer, '@')) != NULL) { tuser = rmtpeer; *cp = '\0'; if (!okname(tuser)) exit(X_STARTUP); rmtpeer = ++cp; } else tuser = pwd->pw_name; if ((rmt = getenv("RMT")) == NULL) rmt = _PATH_RMT; msg("%s", ""); rmtape = rcmd(&rmtpeer, (u_short)sp->s_port, pwd->pw_name, tuser, rmt, &errfd); if (rmtape < 0) { msg("login to %s as %s failed.\n", rmtpeer, tuser); return; } (void)fprintf(stderr, "Connection to %s established.\n", rmtpeer); size = ntrec * TP_BSIZE; if (size > 60 * 1024) /* XXX */ size = 60 * 1024; /* Leave some space for rmt request/response protocol */ size += 2 * 1024; while (size > TP_BSIZE && setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0) size -= TP_BSIZE; (void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)); throughput = IPTOS_THROUGHPUT; if (setsockopt(rmtape, IPPROTO_IP, IP_TOS, &throughput, sizeof(throughput)) < 0) perror("IP_TOS:IPTOS_THROUGHPUT setsockopt"); on = 1; if (setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0) perror("TCP_NODELAY setsockopt"); } static int okname(const char *cp0) { const char *cp; int c; for (cp = cp0; *cp; cp++) { c = *cp; if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) { msg("invalid user name %s\n", cp0); return (0); } } return (1); } int rmtopen(const char *tape, int mode) { char buf[256]; (void)snprintf(buf, sizeof (buf), "O%.226s\n%d\n", tape, mode); rmtstate = TS_OPEN; return (rmtcall(tape, buf)); } void rmtclose(void) { if (rmtstate != TS_OPEN) return; rmtcall("close", "C\n"); rmtstate = TS_CLOSED; } int rmtread(char *buf, int count) { char line[30]; int n, i, cc; (void)snprintf(line, sizeof (line), "R%d\n", count); n = rmtcall("read", line); if (n < 0) /* rmtcall() properly sets errno for us on errors. */ return (n); for (i = 0; i < n; i += cc) { cc = read(rmtape, buf+i, n - i); if (cc <= 0) rmtconnaborted(0); } return (n); } int rmtwrite(const char *buf, int count) { char line[30]; (void)snprintf(line, sizeof (line), "W%d\n", count); write(rmtape, line, strlen(line)); write(rmtape, buf, count); return (rmtreply("write")); } void rmtwrite0(int count) { char line[30]; (void)snprintf(line, sizeof (line), "W%d\n", count); write(rmtape, line, strlen(line)); } void rmtwrite1(const char *buf, int count) { write(rmtape, buf, count); } int rmtwrite2(void) { return (rmtreply("write")); } int rmtseek(int offset, int pos) /* XXX off_t ? */ { char line[80]; (void)snprintf(line, sizeof (line), "L%d\n%d\n", offset, pos); return (rmtcall("seek", line)); } struct mtget mts; struct mtget * rmtstatus(void) { int i; char *cp; if (rmtstate != TS_OPEN) return (NULL); rmtcall("status", "S\n"); for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++) *cp++ = rmtgetb(); return (&mts); } int rmtioctl(int cmd, int count) { char buf[256]; if (count < 0) return (-1); (void)snprintf(buf, sizeof (buf), "I%d\n%d\n", cmd, count); return (rmtcall("ioctl", buf)); } static int rmtcall(const char *cmd, const char *buf) { if (write(rmtape, buf, strlen(buf)) != strlen(buf)) rmtconnaborted(0); return (rmtreply(cmd)); } static int rmtreply(const char *cmd) { char *cp; char code[30], emsg[BUFSIZ]; rmtgets(code, sizeof (code)); if (*code == 'E' || *code == 'F') { rmtgets(emsg, sizeof (emsg)); msg("%s: %s", cmd, emsg); errno = atoi(code + 1); if (*code == 'F') rmtstate = TS_CLOSED; return (-1); } if (*code != 'A') { /* Kill trailing newline */ cp = code + strlen(code); if (cp > code && *--cp == '\n') *cp = '\0'; msg("Protocol to remote tape server botched (code \"%s\").\n", code); rmtconnaborted(0); } return (atoi(code + 1)); } int rmtgetb(void) { char c; if (read(rmtape, &c, 1) != 1) rmtconnaborted(0); return (c); } /* Get a line (guaranteed to have a trailing newline). */ void rmtgets(char *line, int len) { char *cp = line; while (len > 1) { *cp = rmtgetb(); if (*cp == '\n') { cp[1] = '\0'; return; } cp++; len--; } *cp = '\0'; msg("Protocol to remote tape server botched.\n"); msg("(rmtgets got \"%s\").\n", line); rmtconnaborted(0); } diff --git a/sbin/dump/itime.c b/sbin/dump/itime.c index cb6d55625e6d..d9121e4df05a 100644 --- a/sbin/dump/itime.c +++ b/sbin/dump/itime.c @@ -1,268 +1,266 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)itime.c 8.1 (Berkeley) 6/5/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" struct dumptime { struct dumpdates dt_value; SLIST_ENTRY(dumptime) dt_list; }; SLIST_HEAD(dthead, dumptime) dthead = SLIST_HEAD_INITIALIZER(dthead); int nddates = 0; /* number of records (might be zero) */ struct dumpdates **ddatev; /* the arrayfied version */ char *dumpdates; /* name of the file containing dump date info */ int lastlevel; /* dump level of previous dump */ static void dumprecout(FILE *, const struct dumpdates *); static int getrecord(FILE *, struct dumpdates *); static int makedumpdate(struct dumpdates *, const char *); static void readdumptimes(FILE *); void initdumptimes(void) { FILE *df; if ((df = fopen(dumpdates, "r")) == NULL) { if (errno != ENOENT) { msg("WARNING: cannot read %s: %s\n", dumpdates, strerror(errno)); return; } /* * Dumpdates does not exist, make an empty one. */ msg("WARNING: no file `%s', making an empty one\n", dumpdates); if ((df = fopen(dumpdates, "w")) == NULL) { msg("WARNING: cannot create %s: %s\n", dumpdates, strerror(errno)); return; } (void) fclose(df); if ((df = fopen(dumpdates, "r")) == NULL) { quit("cannot read %s even after creating it: %s\n", dumpdates, strerror(errno)); /* NOTREACHED */ } } (void) flock(fileno(df), LOCK_SH); readdumptimes(df); (void) fclose(df); } static void readdumptimes(FILE *df) { int i; struct dumptime *dtwalk; for (;;) { dtwalk = (struct dumptime *)calloc(1, sizeof (struct dumptime)); if (getrecord(df, &(dtwalk->dt_value)) < 0) { free(dtwalk); break; } nddates++; SLIST_INSERT_HEAD(&dthead, dtwalk, dt_list); } /* * arrayify the list, leaving enough room for the additional * record that we may have to add to the ddate structure */ ddatev = calloc((unsigned) (nddates + 1), sizeof (struct dumpdates *)); dtwalk = SLIST_FIRST(&dthead); for (i = nddates - 1; i >= 0; i--, dtwalk = SLIST_NEXT(dtwalk, dt_list)) ddatev[i] = &dtwalk->dt_value; } void getdumptime(void) { struct dumpdates *ddp; int i; char *fname; fname = disk; #ifdef FDEBUG msg("Looking for name %s in dumpdates = %s for level = %d\n", fname, dumpdates, level); #endif spcl.c_ddate = 0; lastlevel = 0; initdumptimes(); /* * Go find the entry with the same name for a lower increment * and older date */ ITITERATE(i, ddp) { if (strncmp(fname, ddp->dd_name, sizeof (ddp->dd_name)) != 0) continue; if (ddp->dd_level >= level) continue; if (ddp->dd_ddate <= _time64_to_time(spcl.c_ddate)) continue; spcl.c_ddate = _time_to_time64(ddp->dd_ddate); lastlevel = ddp->dd_level; } } void putdumptime(void) { FILE *df; struct dumpdates *dtwalk; int i; int fd; char *fname; char *tmsg; if(uflag == 0) return; if ((df = fopen(dumpdates, "r+")) == NULL) quit("cannot rewrite %s: %s\n", dumpdates, strerror(errno)); fd = fileno(df); (void) flock(fd, LOCK_EX); fname = disk; free(ddatev); ddatev = NULL; nddates = 0; readdumptimes(df); if (fseek(df, 0L, 0) < 0) quit("fseek: %s\n", strerror(errno)); spcl.c_ddate = 0; ITITERATE(i, dtwalk) { if (strncmp(fname, dtwalk->dd_name, sizeof (dtwalk->dd_name)) != 0) continue; if (dtwalk->dd_level != level) continue; goto found; } /* * construct the new upper bound; * Enough room has been allocated. */ dtwalk = ddatev[nddates] = (struct dumpdates *)calloc(1, sizeof (struct dumpdates)); nddates += 1; found: (void) strncpy(dtwalk->dd_name, fname, sizeof (dtwalk->dd_name)); dtwalk->dd_level = level; dtwalk->dd_ddate = _time64_to_time(spcl.c_date); ITITERATE(i, dtwalk) { dumprecout(df, dtwalk); } if (fflush(df)) quit("%s: %s\n", dumpdates, strerror(errno)); if (ftruncate(fd, ftell(df))) quit("ftruncate (%s): %s\n", dumpdates, strerror(errno)); (void) fclose(df); if (spcl.c_date == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_date); tmsg = ctime(&t); } msg("level %d dump on %s", level, tmsg); } static void dumprecout(FILE *file, const struct dumpdates *what) { if (strlen(what->dd_name) > DUMPFMTLEN) quit("Name '%s' exceeds DUMPFMTLEN (%d) bytes\n", what->dd_name, DUMPFMTLEN); if (fprintf(file, DUMPOUTFMT, DUMPFMTLEN, what->dd_name, what->dd_level, ctime(&what->dd_ddate)) < 0) quit("%s: %s\n", dumpdates, strerror(errno)); } int recno; static int getrecord(FILE *df, struct dumpdates *ddatep) { char tbuf[BUFSIZ]; recno = 0; if ( (fgets(tbuf, sizeof (tbuf), df)) != tbuf) return(-1); recno++; if (makedumpdate(ddatep, tbuf) < 0) msg("Unknown intermediate format in %s, line %d\n", dumpdates, recno); #ifdef FDEBUG msg("getrecord: %s %d %s", ddatep->dd_name, ddatep->dd_level, ddatep->dd_ddate == 0 ? "the epoch\n" : ctime(&ddatep->dd_ddate)); #endif return(0); } static int makedumpdate(struct dumpdates *ddp, const char *tbuf) { char un_buf[128]; (void) sscanf(tbuf, DUMPINFMT, ddp->dd_name, &ddp->dd_level, un_buf); ddp->dd_ddate = unctime(un_buf); if (ddp->dd_ddate < 0) return(-1); return(0); } diff --git a/sbin/dump/main.c b/sbin/dump/main.c index 779db5fb4b43..a5b4eaa6f8ac 100644 --- a/sbin/dump/main.c +++ b/sbin/dump/main.c @@ -1,797 +1,795 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/1/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #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 "dump.h" #include "pathnames.h" int mapsize; /* size of the state maps */ char *usedinomap; /* map of allocated inodes */ char *dumpdirmap; /* map of directories to be dumped */ char *dumpinomap; /* map of files to be dumped */ char *disk; /* name of the disk file */ char *tape; /* name of the tape file */ char *popenout; /* popen(3) per-"tape" command */ int level; /* dump level of this dump */ int uflag; /* update flag */ int diskfd; /* disk file descriptor */ int pipeout; /* true => output to standard output */ int density = 0; /* density in bytes/0.1" " <- this is for hilit19 */ long tapesize; /* estimated tape size, blocks */ long tsize; /* tape size in 0.1" units */ int etapes; /* estimated number of tapes */ int nonodump; /* if set, do not honor UF_NODUMP user flags */ int unlimited; /* if set, write to end of medium */ int cachesize = 0; /* block cache size (in bytes), defaults to 0 */ int rsync_friendly; /* be friendly with rsync */ int notify = 0; /* notify operator flag */ int blockswritten = 0; /* number of blocks written on current tape */ int tapeno = 0; /* current tape number */ int ntrec = NTREC; /* # tape blocks in each tape record */ long blocksperfile; /* number of blocks per output file */ int cartridge = 0; /* Assume non-cartridge tape */ char *host = NULL; /* remote host (if any) */ time_t tstart_writing; /* when started writing the first tape block */ time_t tend_writing; /* after writing the last tape block */ int passno; /* current dump pass number */ struct fs *sblock; /* the file system super block */ long dev_bsize = 1; /* recalculated below */ int dev_bshift; /* log2(dev_bsize) */ int tp_bshift; /* log2(TP_BSIZE) */ int snapdump = 0; /* dumping live filesystem, so use snapshot */ static char *getmntpt(char *, int *); static long numarg(const char *, long, long); static void obsolete(int *, char **[]); static void usage(void) __dead2; int main(int argc, char *argv[]) { struct stat sb; ino_t ino; int dirty; union dinode *dp; struct fstab *dt; char *map, *mntpt; int ch, mode, mntflags; int i, ret, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1; int just_estimate = 0; ino_t maxino; char *tmsg; spcl.c_date = _time_to_time64(time(NULL)); tsize = 0; /* Default later, based on 'c' option for cart tapes */ dumpdates = _PATH_DUMPDATES; popenout = NULL; tape = NULL; if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); level = 0; rsync_friendly = 0; if (argc < 2) usage(); obsolete(&argc, &argv); while ((ch = getopt(argc, argv, "0123456789aB:b:C:cD:d:f:h:LnP:RrSs:T:uWw")) != -1) switch (ch) { /* dump level */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = 10 * level + ch - '0'; break; case 'a': /* `auto-size', Write to EOM. */ unlimited = 1; break; case 'B': /* blocks per output file */ blocksperfile = numarg("number of blocks per file", 1L, 0L); break; case 'b': /* blocks per tape write */ ntrec = numarg("number of blocks per write", 1L, 1000L); break; case 'C': cachesize = numarg("cachesize", 0, 0) * 1024 * 1024; break; case 'c': /* Tape is cart. not 9-track */ cartridge = 1; break; case 'D': dumpdates = optarg; break; case 'd': /* density, in bits per inch */ density = numarg("density", 10L, 327670L) / 10; if (density >= 625 && !bflag) ntrec = HIGHDENSITYTREC; break; case 'f': /* output file */ if (popenout != NULL) errx(X_STARTUP, "You cannot use the P and f " "flags together.\n"); tape = optarg; break; case 'h': honorlevel = numarg("honor level", 0L, 10L); break; case 'L': snapdump = 1; break; case 'n': /* notify operators */ notify = 1; break; case 'P': if (tape != NULL) errx(X_STARTUP, "You cannot use the P and f " "flags together.\n"); popenout = optarg; break; case 'r': /* store slightly less data to be friendly to rsync */ if (rsync_friendly < 1) rsync_friendly = 1; break; case 'R': /* store even less data to be friendlier to rsync */ if (rsync_friendly < 2) rsync_friendly = 2; break; case 'S': /* exit after estimating # of tapes */ just_estimate = 1; break; case 's': /* tape size, feet */ tsize = numarg("tape size", 1L, 0L) * 12 * 10; break; case 'T': /* time of last dump */ spcl.c_ddate = unctime(optarg); if (spcl.c_ddate < 0) { (void)fprintf(stderr, "bad time \"%s\"\n", optarg); exit(X_STARTUP); } Tflag = 1; lastlevel = -1; break; case 'u': /* update /etc/dumpdates */ uflag = 1; break; case 'W': /* what to do */ case 'w': lastdump(ch); exit(X_FINOK); /* do nothing else */ default: usage(); } argc -= optind; argv += optind; if (argc < 1) { (void)fprintf(stderr, "Must specify disk or file system\n"); exit(X_STARTUP); } disk = *argv++; argc--; if (argc >= 1) { (void)fprintf(stderr, "Unknown arguments to dump:"); while (argc--) (void)fprintf(stderr, " %s", *argv++); (void)fprintf(stderr, "\n"); exit(X_STARTUP); } if (rsync_friendly && (level > 0)) { (void)fprintf(stderr, "%s %s\n", "rsync friendly options", "can be used only with level 0 dumps."); exit(X_STARTUP); } if (Tflag && uflag) { (void)fprintf(stderr, "You cannot use the T and u flags together.\n"); exit(X_STARTUP); } if (popenout) { tape = "child pipeline process"; } else if (tape == NULL && (tape = getenv("TAPE")) == NULL) tape = _PATH_DEFTAPE; if (strcmp(tape, "-") == 0) { pipeout++; tape = "standard output"; } if (blocksperfile) blocksperfile = rounddown(blocksperfile, ntrec); else if (!unlimited) { /* * Determine how to default tape size and density * * density tape size * 9-track 1600 bpi (160 bytes/.1") 2300 ft. * 9-track 6250 bpi (625 bytes/.1") 2300 ft. * cartridge 8000 bpi (100 bytes/.1") 1700 ft. * (450*4 - slop) * hilit19 hits again: " */ if (density == 0) density = cartridge ? 100 : 160; if (tsize == 0) tsize = cartridge ? 1700L*120L : 2300L*120L; } if (strchr(tape, ':')) { host = tape; tape = strchr(host, ':'); *tape++ = '\0'; #ifdef RDUMP if (strchr(tape, '\n')) { (void)fprintf(stderr, "invalid characters in tape\n"); exit(X_STARTUP); } if (rmthost(host) == 0) exit(X_STARTUP); #else (void)fprintf(stderr, "remote dump not enabled\n"); exit(X_STARTUP); #endif } (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, sig); if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) signal(SIGTRAP, sig); if (signal(SIGFPE, SIG_IGN) != SIG_IGN) signal(SIGFPE, sig); if (signal(SIGBUS, SIG_IGN) != SIG_IGN) signal(SIGBUS, sig); if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) signal(SIGSEGV, sig); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sig); if (signal(SIGINT, interrupt) == SIG_IGN) signal(SIGINT, SIG_IGN); dump_getfstab(); /* /etc/fstab snarfed */ /* * disk can be either the full special file name, * the suffix of the special file name, * the special name missing the leading '/', * the file system name with or without the leading '/'. */ dt = fstabsearch(disk); if (dt != NULL) { disk = rawname(dt->fs_spec); if (disk == NULL) errx(X_STARTUP, "%s: unknown file system", dt->fs_spec); (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN); (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN); } else { (void)strncpy(spcl.c_dev, disk, NAMELEN); (void)strncpy(spcl.c_filesys, "an unlisted file system", NAMELEN); } spcl.c_dev[NAMELEN-1]='\0'; spcl.c_filesys[NAMELEN-1]='\0'; if ((mntpt = getmntpt(disk, &mntflags)) != NULL) { if (mntflags & MNT_RDONLY) { if (snapdump != 0) { msg("WARNING: %s\n", "-L ignored for read-only filesystem."); snapdump = 0; } } else if (snapdump == 0) { msg("WARNING: %s\n", "should use -L when dumping live read-write " "filesystems!"); } else { char snapname[BUFSIZ], snapcmd[BUFSIZ]; snprintf(snapname, sizeof snapname, "%s/.snap", mntpt); if ((stat(snapname, &sb) < 0) || !S_ISDIR(sb.st_mode)) { msg("WARNING: %s %s\n", "-L requested but snapshot location", snapname); msg(" %s: %s\n", "is not a directory", "dump downgraded, -L ignored"); snapdump = 0; } else { snprintf(snapname, sizeof snapname, "%s/.snap/dump_snapshot", mntpt); snprintf(snapcmd, sizeof snapcmd, "%s %s %s", _PATH_MKSNAP_FFS, mntpt, snapname); unlink(snapname); if (system(snapcmd) != 0) errx(X_STARTUP, "Cannot create %s: %s\n", snapname, strerror(errno)); if ((diskfd = open(snapname, O_RDONLY)) < 0) { unlink(snapname); errx(X_STARTUP, "Cannot open %s: %s\n", snapname, strerror(errno)); } unlink(snapname); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", snapname); spcl.c_date = _time_to_time64(sb.st_mtime); } } } else if (snapdump != 0) { msg("WARNING: Cannot use -L on an unmounted filesystem.\n"); snapdump = 0; } if (snapdump == 0) { if ((diskfd = open(disk, O_RDONLY)) < 0) err(X_STARTUP, "Cannot open %s", disk); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", disk); if (S_ISDIR(sb.st_mode)) errx(X_STARTUP, "%s: unknown file system", disk); } (void)strcpy(spcl.c_label, "none"); (void)gethostname(spcl.c_host, NAMELEN); spcl.c_level = level; spcl.c_type = TS_TAPE; if (rsync_friendly) { /* don't store real dump times */ spcl.c_date = 0; spcl.c_ddate = 0; } if (spcl.c_date == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_date); tmsg = ctime(&t); } msg("Date of this level %d dump: %s", level, tmsg); if (!Tflag && (!rsync_friendly)) getdumptime(); /* /etc/dumpdates snarfed */ if (spcl.c_ddate == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_ddate); tmsg = ctime(&t); } if (lastlevel < 0) msg("Date of last (level unknown) dump: %s", tmsg); else msg("Date of last level %d dump: %s", lastlevel, tmsg); msg("Dumping %s%s ", snapdump ? "snapshot of ": "", disk); if (dt != NULL) msgtail("(%s) ", dt->fs_file); if (host) msgtail("to %s on host %s\n", tape, host); else msgtail("to %s\n", tape); sync(); if ((ret = sbget(diskfd, &sblock, UFS_STDSB, UFS_NOCSUM)) != 0) { switch (ret) { case ENOENT: warn("Cannot find file system superblock"); return (1); default: warn("Unable to read file system superblock"); return (1); } } dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1); dev_bshift = ffs(dev_bsize) - 1; if (dev_bsize != (1 << dev_bshift)) quit("dev_bsize (%ld) is not a power of 2", dev_bsize); tp_bshift = ffs(TP_BSIZE) - 1; if (TP_BSIZE != (1 << tp_bshift)) quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); maxino = sblock->fs_ipg * sblock->fs_ncg; mapsize = roundup(howmany(maxino, CHAR_BIT), TP_BSIZE); usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); nonodump = spcl.c_level < honorlevel; passno = 1; setproctitle("%s: pass 1: regular files", disk); msg("mapping (Pass I) [regular files]\n"); anydirskipped = mapfiles(maxino, &tapesize); passno = 2; setproctitle("%s: pass 2: directories", disk); msg("mapping (Pass II) [directories]\n"); while (anydirskipped) { anydirskipped = mapdirs(maxino, &tapesize); } if (pipeout || unlimited) { tapesize += 10; /* 10 trailer blocks */ msg("estimated %ld tape blocks.\n", tapesize); } else { double fetapes; if (blocksperfile) fetapes = (double) tapesize / blocksperfile; else if (cartridge) { /* Estimate number of tapes, assuming streaming stops at the end of each block written, and not in mid-block. Assume no erroneous blocks; this can be compensated for with an artificially low tape size. */ fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes/block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* streaming-stops per block */ * 15.48 /* 0.1" / streaming-stop " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } else { /* Estimate number of tapes, for old fashioned 9-track tape */ int tenthsperirg = (density == 625) ? 3 : 7; fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes / block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* IRG's / block */ * tenthsperirg /* 0.1" / IRG " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } etapes = fetapes; /* truncating assignment */ etapes++; /* count the dumped inodes map on each additional tape */ tapesize += (etapes - 1) * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); tapesize += etapes + 10; /* headers + 10 trailer blks */ msg("estimated %ld tape blocks on %3.2f tape(s).\n", tapesize, fetapes); } /* * If the user only wants an estimate of the number of * tapes, exit now. */ if (just_estimate) exit(0); /* * Allocate tape buffer. */ if (!alloctape()) quit( "can't allocate tape buffers - try a smaller blocking factor.\n"); startnewtape(1); (void)time((time_t *)&(tstart_writing)); dumpmap(usedinomap, TS_CLRI, maxino - 1); passno = 3; setproctitle("%s: pass 3: directories", disk); msg("dumping (Pass III) [directories]\n"); dirty = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip directory inodes deleted and maybe reallocated */ dp = getino(ino, &mode); if (mode != IFDIR) continue; (void)dumpino(dp, ino); } passno = 4; setproctitle("%s: pass 4: regular files", disk); msg("dumping (Pass IV) [regular files]\n"); for (map = dumpinomap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip inodes deleted and reallocated as directories. */ dp = getino(ino, &mode); if (mode == IFDIR) continue; (void)dumpino(dp, ino); } (void)time((time_t *)&(tend_writing)); spcl.c_type = TS_END; for (i = 0; i < ntrec; i++) writeheader(maxino - 1); if (pipeout) msg("DUMP: %jd tape blocks\n", (intmax_t)spcl.c_tapea); else msg("DUMP: %jd tape blocks on %d volume%s\n", (intmax_t)spcl.c_tapea, spcl.c_volume, (spcl.c_volume == 1) ? "" : "s"); /* report dump performance, avoid division through zero */ if (tend_writing - tstart_writing == 0) msg("finished in less than a second\n"); else msg("finished in %jd seconds, throughput %jd KBytes/sec\n", (intmax_t)tend_writing - tstart_writing, (intmax_t)(spcl.c_tapea / (tend_writing - tstart_writing))); putdumptime(); trewind(); broadcast("DUMP IS DONE!\a\a\n"); msg("DUMP IS DONE\n"); Exit(X_FINOK); /* NOTREACHED */ } static void usage(void) { fprintf(stderr, "usage: dump [-0123456789acLnSu] [-B records] [-b blocksize] [-C cachesize]\n" " [-D dumpdates] [-d density] [-f file | -P pipecommand] [-h level]\n" " [-s feet] [-T date] filesystem\n" " dump -W | -w\n"); exit(X_STARTUP); } /* * Check to see if a disk is currently mounted. */ static char * getmntpt(char *name, int *mntflagsp) { long mntsize, i; struct statfs *mntbuf; mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); for (i = 0; i < mntsize; i++) { if (!strcmp(mntbuf[i].f_mntfromname, name)) { *mntflagsp = mntbuf[i].f_flags; return (mntbuf[i].f_mntonname); } } return (0); } /* * Pick up a numeric argument. It must be nonnegative and in the given * range (except that a vmax of 0 means unlimited). */ static long numarg(const char *meaning, long vmin, long vmax) { char *p; long val; val = strtol(optarg, &p, 10); if (*p) errx(1, "illegal %s -- %s", meaning, optarg); if (val < vmin || (vmax && val > vmax)) errx(1, "%s must be between %ld and %ld", meaning, vmin, vmax); return (val); } void sig(int signo) { switch(signo) { case SIGALRM: case SIGBUS: case SIGFPE: case SIGHUP: case SIGTERM: case SIGTRAP: if (pipeout) quit("Signal on pipe: cannot recover\n"); msg("Rewriting attempted as response to unknown signal.\n"); (void)fflush(stderr); (void)fflush(stdout); close_rewind(); exit(X_REWRITE); /* NOTREACHED */ case SIGSEGV: msg("SIGSEGV: ABORTING!\n"); (void)signal(SIGSEGV, SIG_DFL); (void)kill(0, SIGSEGV); /* NOTREACHED */ } } char * rawname(char *cp) { struct stat sb; /* * Ensure that the device passed in is a raw device. */ if (stat(cp, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFCHR) return (cp); /* * Since there's only one device type now, we can't construct any * better name, so we have to return NULL. */ return (NULL); } /* * obsolete -- * Change set of key letters and ordered arguments into something * getopt(3) will like. */ static void obsolete(int *argcp, char **argvp[]) { int argc, flags; char *ap, **argv, *flagsp, **nargv, *p; /* Setup. */ argv = *argvp; argc = *argcp; /* * Return if no arguments or first argument has leading * dash or slash. */ ap = argv[1]; if (argc == 1 || *ap == '-' || *ap == '/') return; /* Allocate space for new arguments. */ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL || (p = flagsp = malloc(strlen(ap) + 2)) == NULL) err(1, NULL); *nargv++ = *argv; argv += 2; for (flags = 0; *ap; ++ap) { switch (*ap) { case 'B': case 'b': case 'd': case 'f': case 'D': case 'C': case 'h': case 's': case 'T': if (*argv == NULL) { warnx("option requires an argument -- %c", *ap); usage(); } if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL) err(1, NULL); nargv[0][0] = '-'; nargv[0][1] = *ap; (void)strcpy(&nargv[0][2], *argv); ++argv; ++nargv; break; default: if (!flags) { *p++ = '-'; flags = 1; } *p++ = *ap; break; } } /* Terminate flags. */ if (flags) { *p = '\0'; *nargv++ = flagsp; } else free(flagsp); /* Copy remaining arguments. */ while ((*nargv++ = *argv++)); /* Update argument count. */ *argcp = nargv - *argvp - 1; } diff --git a/sbin/dump/optr.c b/sbin/dump/optr.c index 8eb163516c72..d3ca5b2a926e 100644 --- a/sbin/dump/optr.c +++ b/sbin/dump/optr.c @@ -1,431 +1,429 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" #include "pathnames.h" void alarmcatch(int); int datesort(const void *, const void *); /* * Query the operator; This previously-fascist piece of code * no longer requires an exact response. * It is intended to protect dump aborting by inquisitive * people banging on the console terminal to see what is * happening which might cause dump to croak, destroying * a large number of hours of work. * * Every 2 minutes we reprint the message, alerting others * that dump needs attention. */ static int timeout; static const char *attnmessage; /* attention message */ int query(const char *question) { char replybuffer[64]; int back, errcount; FILE *mytty; if ((mytty = fopen(_PATH_TTY, "r")) == NULL) quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); attnmessage = question; timeout = 0; alarmcatch(0); back = -1; errcount = 0; do { if (fgets(replybuffer, 63, mytty) == NULL) { clearerr(mytty); if (++errcount > 30) /* XXX ugly */ quit("excessive operator query failures\n"); } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { back = 1; } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { back = 0; } else { (void) fprintf(stderr, " DUMP: \"Yes\" or \"No\"?\n"); (void) fprintf(stderr, " DUMP: %s: (\"yes\" or \"no\") ", question); } } while (back < 0); /* * Turn off the alarm, and reset the signal to trap out.. */ (void) alarm(0); if (signal(SIGALRM, sig) == SIG_IGN) signal(SIGALRM, SIG_IGN); (void) fclose(mytty); return(back); } char lastmsg[BUFSIZ]; /* * Alert the console operator, and enable the alarm clock to * sleep for 2 minutes in case nobody comes to satisfy dump */ void alarmcatch(int sig __unused) { if (notify == 0) { if (timeout == 0) (void) fprintf(stderr, " DUMP: %s: (\"yes\" or \"no\") ", attnmessage); else msgtail("\a\a"); } else { if (timeout) { msgtail("\n"); broadcast(""); /* just print last msg */ } (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", attnmessage); } signal(SIGALRM, alarmcatch); (void) alarm(120); timeout = 1; } /* * Here if an inquisitive operator interrupts the dump program */ void interrupt(int signo __unused) { msg("Interrupt received.\n"); if (query("Do you want to abort dump?")) dumpabort(0); } /* * We now use wall(1) to do the actual broadcasting. */ void broadcast(const char *message) { FILE *fp; char buf[sizeof(_PATH_WALL) + sizeof(OPGRENT) + 3]; if (!notify) return; snprintf(buf, sizeof(buf), "%s -g %s", _PATH_WALL, OPGRENT); if ((fp = popen(buf, "w")) == NULL) return; (void) fputs("\a\a\aMessage from the dump program to all operators\n\nDUMP: NEEDS ATTENTION: ", fp); if (lastmsg[0]) (void) fputs(lastmsg, fp); if (message[0]) (void) fputs(message, fp); (void) pclose(fp); } /* * Print out an estimate of the amount of time left to do the dump */ time_t tschedule = 0; void timeest(void) { double percent; time_t tnow, tdone; char *tdone_str; int deltat, hours, mins; (void)time(&tnow); if (blockswritten > tapesize) { setproctitle("%s: 99.99%% done, finished soon", disk); if (tnow >= tschedule) { tschedule = tnow + 300; msg("99.99%% done, finished soon\n"); } } else { deltat = (blockswritten == 0) ? 0 : tstart_writing - tnow + (double)(tnow - tstart_writing) / blockswritten * tapesize; tdone = tnow + deltat; percent = (blockswritten * 100.0) / tapesize; hours = deltat / 3600; mins = (deltat % 3600) / 60; tdone_str = ctime(&tdone); tdone_str[strlen(tdone_str) - 1] = '\0'; setproctitle( "%s: pass %d: %3.2f%% done, finished in %d:%02d at %s", disk, passno, percent, hours, mins, tdone_str); if (tnow >= tschedule) { tschedule = tnow + 300; if (blockswritten < 500) return; msg("%3.2f%% done, finished in %d:%02d at %s\n", percent, hours, mins, tdone_str); } } } /* * Schedule a printout of the estimate in the next call to timeest(). */ void infosch(int signal __unused) { tschedule = 0; } void msg(const char *fmt, ...) { va_list ap; (void) fprintf(stderr," DUMP: "); #ifdef TDEBUG (void) fprintf(stderr, "pid=%d ", getpid()); #endif va_start(ap, fmt); (void) vfprintf(stderr, fmt, ap); va_end(ap); (void) fflush(stdout); (void) fflush(stderr); va_start(ap, fmt); (void) vsnprintf(lastmsg, sizeof(lastmsg), fmt, ap); va_end(ap); } void msgtail(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void) vfprintf(stderr, fmt, ap); va_end(ap); } void quit(const char *fmt, ...) { va_list ap; (void) fprintf(stderr," DUMP: "); #ifdef TDEBUG (void) fprintf(stderr, "pid=%d ", getpid()); #endif va_start(ap, fmt); (void) vfprintf(stderr, fmt, ap); va_end(ap); (void) fflush(stdout); (void) fflush(stderr); dumpabort(0); } /* * Tell the operator what has to be done; * we don't actually do it */ struct fstab * allocfsent(const struct fstab *fs) { struct fstab *new; new = (struct fstab *)malloc(sizeof (*fs)); if (new == NULL || (new->fs_file = strdup(fs->fs_file)) == NULL || (new->fs_type = strdup(fs->fs_type)) == NULL || (new->fs_spec = strdup(fs->fs_spec)) == NULL) quit("%s\n", strerror(errno)); new->fs_passno = fs->fs_passno; new->fs_freq = fs->fs_freq; return (new); } struct pfstab { SLIST_ENTRY(pfstab) pf_list; struct fstab *pf_fstab; }; static SLIST_HEAD(, pfstab) table; void dump_getfstab(void) { struct fstab *fs; struct pfstab *pf; if (setfsent() == 0) { msg("Can't open %s for dump table information: %s\n", _PATH_FSTAB, strerror(errno)); return; } while ((fs = getfsent()) != NULL) { if ((strcmp(fs->fs_type, FSTAB_RW) && strcmp(fs->fs_type, FSTAB_RO) && strcmp(fs->fs_type, FSTAB_RQ)) || strcmp(fs->fs_vfstype, "ufs")) continue; fs = allocfsent(fs); if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) quit("%s\n", strerror(errno)); pf->pf_fstab = fs; SLIST_INSERT_HEAD(&table, pf, pf_list); } (void) endfsent(); } /* * Search in the fstab for a file name. * This file name can be either the special or the path file name. * * The file name can omit the leading '/'. */ struct fstab * fstabsearch(const char *key) { struct pfstab *pf; struct fstab *fs; char *rn; SLIST_FOREACH(pf, &table, pf_list) { fs = pf->pf_fstab; if (strcmp(fs->fs_file, key) == 0 || strcmp(fs->fs_spec, key) == 0) return (fs); rn = rawname(fs->fs_spec); if (rn != NULL && strcmp(rn, key) == 0) return (fs); if (key[0] != '/') { if (*fs->fs_spec == '/' && strcmp(fs->fs_spec + 1, key) == 0) return (fs); if (*fs->fs_file == '/' && strcmp(fs->fs_file + 1, key) == 0) return (fs); } } return (NULL); } /* * Tell the operator what to do */ void lastdump(int arg) /* w ==> just what to do; W ==> most recent dumps */ { int i; struct fstab *dt; struct dumpdates *dtwalk; char *lastname, *date; int dumpme; time_t tnow; struct tm *tlast; (void) time(&tnow); dump_getfstab(); /* /etc/fstab input */ initdumptimes(); /* /etc/dumpdates input */ qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort); if (arg == 'w') (void) printf("Dump these file systems:\n"); else (void) printf("Last dump(s) done (Dump '>' file systems):\n"); lastname = "??"; ITITERATE(i, dtwalk) { if (strncmp(lastname, dtwalk->dd_name, sizeof(dtwalk->dd_name)) == 0) continue; date = (char *)ctime(&dtwalk->dd_ddate); date[16] = '\0'; /* blast away seconds and year */ lastname = dtwalk->dd_name; dt = fstabsearch(dtwalk->dd_name); dumpme = (dt != NULL && dt->fs_freq != 0); if (dumpme) { tlast = localtime(&dtwalk->dd_ddate); dumpme = tnow > (dtwalk->dd_ddate - (tlast->tm_hour * 3600) - (tlast->tm_min * 60) - tlast->tm_sec + (dt->fs_freq * 86400)); } if (arg != 'w' || dumpme) (void) printf( "%c %8s\t(%6s) Last dump: Level %d, Date %s\n", dumpme && (arg != 'w') ? '>' : ' ', dtwalk->dd_name, dt ? dt->fs_file : "", dtwalk->dd_level, date); } } int datesort(const void *a1, const void *a2) { struct dumpdates *d1 = *(struct dumpdates **)a1; struct dumpdates *d2 = *(struct dumpdates **)a2; int diff; diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); if (diff == 0) return (d2->dd_ddate - d1->dd_ddate); return (diff); } diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c index c123f7fa9404..3a3574f6d44d 100644 --- a/sbin/dump/tape.c +++ b/sbin/dump/tape.c @@ -1,901 +1,899 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1991, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)tape.c 8.4 (Berkeley) 5/1/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" ino_t curino; /* current inumber; used globally */ int newtape; /* new tape flag */ union u_spcl u_spcl; /* mapping of variables in a control block */ static int tapefd; /* tape file descriptor */ static long asize; /* number of 0.1" units written on cur tape */ static int writesize; /* size of malloc()ed buffer for tape */ static int64_t lastspclrec = -1; /* tape block number of last written header */ static int trecno = 0; /* next record to write in current block */ static long blocksthisvol; /* number of blocks on current output file */ static char *nexttape; static FILE *popenfp = NULL; static int atomic_read(int, void *, int); static int atomic_write(int, const void *, int); static void worker(int, int); static void create_workers(void); static void flushtape(void); static void killall(void); static void rollforward(void); /* * Concurrent dump mods (Caltech) - disk block reading and tape writing * are exported to several worker processes. While one worker writes the * tape, the others read disk blocks; they pass control of the tape in * a ring via signals. The parent process traverses the file system and * sends writeheader()'s and lists of daddr's to the workers via pipes. * The following structure defines the instruction packets sent to workers. */ struct req { ufs2_daddr_t dblk; int count; }; static int reqsiz; #define WORKERS 3 /* 1 worker writing, 1 reading, 1 for slack */ static struct worker { int64_t tapea; /* header number at start of this chunk */ int64_t firstrec; /* record number of this block */ int count; /* count to next header (used for TS_TAPE */ /* after EOT) */ int inode; /* inode that we are currently dealing with */ int fd; /* FD for this worker */ int pid; /* PID for this worker */ int sent; /* 1 == we've sent this worker requests */ char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ struct req *req; /* buffer for requests */ } workers[WORKERS+1]; static struct worker *wp; static char (*nextblock)[TP_BSIZE]; static int master; /* pid of master, for sending error signals */ static int tenths; /* length of tape used per block written */ static volatile sig_atomic_t caught; /* have we caught the signal to proceed? */ static volatile sig_atomic_t ready; /* reached the lock point without having */ /* received the SIGUSR2 signal from the prev worker? */ static jmp_buf jmpbuf; /* where to jump to if we are ready when the */ /* SIGUSR2 arrives from the previous worker */ int alloctape(void) { int pgoff = getpagesize() - 1; char *buf; int i; writesize = ntrec * TP_BSIZE; reqsiz = (ntrec + 1) * sizeof(struct req); /* * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require * repositioning after stopping, i.e, streaming mode, where the gap is * variable, 0.30" to 0.45". The gap is maximal when the tape stops. */ if (blocksperfile == 0 && !unlimited) tenths = writesize / density + (cartridge ? 16 : density == 625 ? 5 : 8); /* * Allocate tape buffer contiguous with the array of instruction * packets, so flushtape() can write them together with one write(). * Align tape buffer on page boundary to speed up tape write(). */ for (i = 0; i <= WORKERS; i++) { buf = (char *) malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE)); if (buf == NULL) return(0); workers[i].tblock = (char (*)[TP_BSIZE]) (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); workers[i].req = (struct req *)workers[i].tblock - ntrec - 1; } wp = &workers[0]; wp->count = 1; wp->tapea = 0; wp->firstrec = 0; nextblock = wp->tblock; return(1); } void writerec(char *dp, int isspcl) { wp->req[trecno].dblk = (ufs2_daddr_t)0; wp->req[trecno].count = 1; /* Can't do a structure assignment due to alignment problems */ bcopy(dp, *(nextblock)++, sizeof (union u_spcl)); if (isspcl) lastspclrec = spcl.c_tapea; trecno++; spcl.c_tapea++; if (trecno >= ntrec) flushtape(); } void dumpblock(ufs2_daddr_t blkno, int size) { int avail, tpblks; ufs2_daddr_t dblkno; dblkno = fsbtodb(sblock, blkno); tpblks = size >> tp_bshift; while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { wp->req[trecno].dblk = dblkno; wp->req[trecno].count = avail; trecno += avail; spcl.c_tapea += avail; if (trecno >= ntrec) flushtape(); dblkno += avail << (tp_bshift - dev_bshift); tpblks -= avail; } } int nogripe = 0; void tperror(int signo __unused) { if (pipeout) { msg("write error on %s\n", tape); quit("Cannot recover\n"); /* NOTREACHED */ } msg("write error %ld blocks into volume %d\n", blocksthisvol, tapeno); broadcast("DUMP WRITE ERROR!\n"); if (!query("Do you want to restart?")) dumpabort(0); msg("Closing this volume. Prepare to restart with new media;\n"); msg("this dump volume will be rewritten.\n"); killall(); nogripe = 1; close_rewind(); Exit(X_REWRITE); } void sigpipe(int signo __unused) { quit("Broken pipe\n"); } static void flushtape(void) { int i, blks, got; int64_t lastfirstrec; int siz = (char *)nextblock - (char *)wp->req; wp->req[trecno].count = 0; /* Sentinel */ if (atomic_write(wp->fd, (const void *)wp->req, siz) != siz) quit("error writing command pipe: %s\n", strerror(errno)); wp->sent = 1; /* we sent a request, read the response later */ lastfirstrec = wp->firstrec; if (++wp >= &workers[WORKERS]) wp = &workers[0]; /* Read results back from next worker */ if (wp->sent) { if (atomic_read(wp->fd, (void *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } wp->sent = 0; /* Check for end of tape */ if (got < writesize) { msg("End of tape detected\n"); /* * Drain the results, don't care what the values were. * If we read them here then trewind won't... */ for (i = 0; i < WORKERS; i++) { if (workers[i].sent) { if (atomic_read(workers[i].fd, (void *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } workers[i].sent = 0; } } close_rewind(); rollforward(); return; } } blks = 0; if (spcl.c_type != TS_END && spcl.c_type != TS_CLRI && spcl.c_type != TS_BITS) { assert(spcl.c_count <= TP_NINDIR); for (i = 0; i < spcl.c_count; i++) if (spcl.c_addr[i] != 0) blks++; } wp->count = lastspclrec + blks + 1 - spcl.c_tapea; wp->tapea = spcl.c_tapea; wp->firstrec = lastfirstrec + ntrec; wp->inode = curino; nextblock = wp->tblock; trecno = 0; asize += tenths; blockswritten += ntrec; blocksthisvol += ntrec; if (!pipeout && !unlimited && (blocksperfile ? (blocksthisvol >= blocksperfile) : (asize > tsize))) { close_rewind(); startnewtape(0); } timeest(); } void trewind(void) { struct stat sb; int f; int got; for (f = 0; f < WORKERS; f++) { /* * Drain the results, but unlike EOT we DO (or should) care * what the return values were, since if we detect EOT after * we think we've written the last blocks to the tape anyway, * we have to replay those blocks with rollforward. * * fixme: punt for now. */ if (workers[f].sent) { if (atomic_read(workers[f].fd, (void *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } workers[f].sent = 0; if (got != writesize) { msg("EOT detected in last 2 tape records!\n"); msg("Use a longer tape, decrease the size estimate\n"); quit("or use no size estimate at all.\n"); } } (void) close(workers[f].fd); } while (wait((int *)NULL) >= 0) /* wait for any signals from workers */ /* void */; if (pipeout) return; msg("Closing %s\n", tape); if (popenout) { tapefd = -1; (void)pclose(popenfp); popenfp = NULL; return; } #ifdef RDUMP if (host) { rmtclose(); while (rmtopen(tape, 0) < 0) sleep(10); rmtclose(); return; } #endif if (fstat(tapefd, &sb) == 0 && S_ISFIFO(sb.st_mode)) { (void)close(tapefd); return; } (void) close(tapefd); while ((f = open(tape, 0)) < 0) sleep (10); (void) close(f); } void close_rewind() { time_t tstart_changevol, tend_changevol; trewind(); if (nexttape) return; (void)time((time_t *)&(tstart_changevol)); if (!nogripe) { msg("Change Volumes: Mount volume #%d\n", tapeno+1); broadcast("CHANGE DUMP VOLUMES!\a\a\n"); } while (!query("Is the new volume mounted and ready to go?")) if (query("Do you want to abort?")) { dumpabort(0); /*NOTREACHED*/ } (void)time((time_t *)&(tend_changevol)); if ((tstart_changevol != (time_t)-1) && (tend_changevol != (time_t)-1)) tstart_writing += (tend_changevol - tstart_changevol); } void rollforward(void) { struct req *p, *q, *prev; struct worker *twp; int i, size, got; int64_t savedtapea; union u_spcl *ntb, *otb; twp = &workers[WORKERS]; ntb = (union u_spcl *)twp->tblock[1]; /* * Each of the N workers should have requests that need to * be replayed on the next tape. Use the extra worker buffers * (workers[WORKERS]) to construct request lists to be sent to * each worker in turn. */ for (i = 0; i < WORKERS; i++) { q = &twp->req[1]; otb = (union u_spcl *)wp->tblock; /* * For each request in the current worker, copy it to twp. */ prev = NULL; for (p = wp->req; p->count > 0; p += p->count) { *q = *p; if (p->dblk == 0) *ntb++ = *otb++; /* copy the datablock also */ prev = q; q += q->count; } if (prev == NULL) quit("rollforward: protocol botch"); if (prev->dblk != 0) prev->count -= 1; else ntb--; q -= 1; q->count = 0; q = &twp->req[0]; if (i == 0) { q->dblk = 0; q->count = 1; trecno = 0; nextblock = twp->tblock; savedtapea = spcl.c_tapea; spcl.c_tapea = wp->tapea; startnewtape(0); spcl.c_tapea = savedtapea; lastspclrec = savedtapea - 1; } size = (char *)ntb - (char *)q; if (atomic_write(wp->fd, (const void *)q, size) != size) { perror(" DUMP: error writing command pipe"); dumpabort(0); } wp->sent = 1; if (++wp >= &workers[WORKERS]) wp = &workers[0]; q->count = 1; if (prev->dblk != 0) { /* * If the last one was a disk block, make the * first of this one be the last bit of that disk * block... */ q->dblk = prev->dblk + prev->count * (TP_BSIZE / DEV_BSIZE); ntb = (union u_spcl *)twp->tblock; } else { /* * It wasn't a disk block. Copy the data to its * new location in the buffer. */ q->dblk = 0; *((union u_spcl *)twp->tblock) = *ntb; ntb = (union u_spcl *)twp->tblock[1]; } } wp->req[0] = *q; nextblock = wp->tblock; if (q->dblk == 0) nextblock++; trecno = 1; /* * Clear the first workers' response. One hopes that it * worked ok, otherwise the tape is much too short! */ if (wp->sent) { if (atomic_read(wp->fd, (void *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } wp->sent = 0; if (got != writesize) { quit("EOT detected at start of the tape!\n"); } } } /* * We implement taking and restoring checkpoints on the tape level. * When each tape is opened, a new process is created by forking; this * saves all of the necessary context in the parent. The child * continues the dump; the parent waits around, saving the context. * If the child returns X_REWRITE, then it had problems writing that tape; * this causes the parent to fork again, duplicating the context, and * everything continues as if nothing had happened. */ void startnewtape(int top) { int parentpid; int childpid; int status; char *p; sig_t interrupt_save; interrupt_save = signal(SIGINT, SIG_IGN); parentpid = getpid(); restore_check_point: (void)signal(SIGINT, interrupt_save); /* * All signals are inherited... */ setproctitle(NULL); /* Restore the proctitle. */ childpid = fork(); if (childpid < 0) { msg("Context save fork fails in parent %d\n", parentpid); Exit(X_ABORT); } if (childpid != 0) { /* * PARENT: * save the context by waiting * until the child doing all of the work returns. * don't catch the interrupt */ signal(SIGINT, SIG_IGN); #ifdef TDEBUG msg("Tape: %d; parent process: %d child process %d\n", tapeno+1, parentpid, childpid); #endif /* TDEBUG */ if (waitpid(childpid, &status, 0) == -1) msg("Waiting for child %d: %s\n", childpid, strerror(errno)); if (status & 0xFF) { msg("Child %d returns LOB status %o\n", childpid, status&0xFF); } status = (status >> 8) & 0xFF; #ifdef TDEBUG switch(status) { case X_FINOK: msg("Child %d finishes X_FINOK\n", childpid); break; case X_ABORT: msg("Child %d finishes X_ABORT\n", childpid); break; case X_REWRITE: msg("Child %d finishes X_REWRITE\n", childpid); break; default: msg("Child %d finishes unknown %d\n", childpid, status); break; } #endif /* TDEBUG */ switch(status) { case X_FINOK: Exit(X_FINOK); case X_ABORT: Exit(X_ABORT); case X_REWRITE: goto restore_check_point; default: msg("Bad return code from dump: %d\n", status); Exit(X_ABORT); } /*NOTREACHED*/ } else { /* we are the child; just continue */ #ifdef TDEBUG sleep(4); /* allow time for parent's message to get out */ msg("Child on Tape %d has parent %d, my pid = %d\n", tapeno+1, parentpid, getpid()); #endif /* TDEBUG */ /* * If we have a name like "/dev/rmt0,/dev/rmt1", * use the name before the comma first, and save * the remaining names for subsequent volumes. */ tapeno++; /* current tape sequence */ if (nexttape || strchr(tape, ',')) { if (nexttape && *nexttape) tape = nexttape; if ((p = strchr(tape, ',')) != NULL) { *p = '\0'; nexttape = p + 1; } else nexttape = NULL; msg("Dumping volume %d on %s\n", tapeno, tape); } if (pipeout) { tapefd = STDOUT_FILENO; } else if (popenout) { char volno[sizeof("2147483647")]; (void)sprintf(volno, "%d", spcl.c_volume + 1); if (setenv("DUMP_VOLUME", volno, 1) == -1) { msg("Cannot set $DUMP_VOLUME.\n"); dumpabort(0); } popenfp = popen(popenout, "w"); if (popenfp == NULL) { msg("Cannot open output pipeline \"%s\".\n", popenout); dumpabort(0); } tapefd = fileno(popenfp); } else { #ifdef RDUMP while ((tapefd = (host ? rmtopen(tape, 2) : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) #else while ((tapefd = open(tape, O_WRONLY|O_CREAT, 0666)) < 0) #endif { msg("Cannot open output \"%s\".\n", tape); if (!query("Do you want to retry the open?")) dumpabort(0); } } create_workers(); /* Share open tape file descriptor with workers */ if (popenout) close(tapefd); /* Give up our copy of it. */ signal(SIGINFO, infosch); asize = 0; blocksthisvol = 0; if (top) newtape++; /* new tape signal */ spcl.c_count = wp->count; /* * measure firstrec in TP_BSIZE units since restore doesn't * know the correct ntrec value... */ spcl.c_firstrec = wp->firstrec; spcl.c_volume++; spcl.c_type = TS_TAPE; writeheader((ino_t)wp->inode); if (tapeno > 1) msg("Volume %d begins with blocks from inode %d\n", tapeno, wp->inode); } } void dumpabort(int signo __unused) { if (master != 0 && master != getpid()) /* Signals master to call dumpabort */ (void) kill(master, SIGTERM); else { killall(); msg("The ENTIRE dump is aborted.\n"); } #ifdef RDUMP rmtclose(); #endif Exit(X_ABORT); } void Exit(int status) { #ifdef TDEBUG msg("pid = %d exits with status %d\n", getpid(), status); #endif /* TDEBUG */ exit(status); } /* * proceed - handler for SIGUSR2, used to synchronize IO between the workers. */ void proceed(int signo __unused) { if (ready) longjmp(jmpbuf, 1); caught++; } void create_workers(void) { int cmd[2]; int i, j; master = getpid(); signal(SIGTERM, dumpabort); /* Worker sends SIGTERM on dumpabort() */ signal(SIGPIPE, sigpipe); signal(SIGUSR1, tperror); /* Worker sends SIGUSR1 on tape errors */ signal(SIGUSR2, proceed); /* Worker sends SIGUSR2 to next worker */ for (i = 0; i < WORKERS; i++) { if (i == wp - &workers[0]) { caught = 1; } else { caught = 0; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || (workers[i].pid = fork()) < 0) quit("too many workers, %d (recompile smaller): %s\n", i, strerror(errno)); workers[i].fd = cmd[1]; workers[i].sent = 0; if (workers[i].pid == 0) { /* Worker starts up here */ for (j = 0; j <= i; j++) (void) close(workers[j].fd); signal(SIGINT, SIG_IGN); /* Master handles this */ worker(cmd[0], i); Exit(X_FINOK); } } for (i = 0; i < WORKERS; i++) (void) atomic_write(workers[i].fd, (const void *) &workers[(i + 1) % WORKERS].pid, sizeof workers[0].pid); master = 0; } void killall(void) { int i; for (i = 0; i < WORKERS; i++) if (workers[i].pid > 0) { (void) kill(workers[i].pid, SIGKILL); workers[i].sent = 0; } } /* * Synchronization - each process has a lockfile, and shares file * descriptors to the following process's lockfile. When our write * completes, we release our lock on the following process's lock- * file, allowing the following process to lock it and proceed. We * get the lock back for the next cycle by swapping descriptors. */ static void worker(int cmd, int worker_number) { int nread; int nextworker, size, wrote, eot_count; /* * Need our own seek pointer. */ (void) close(diskfd); if ((diskfd = open(disk, O_RDONLY)) < 0) quit("worker couldn't reopen disk: %s\n", strerror(errno)); /* * Need the pid of the next worker in the loop... */ if ((nread = atomic_read(cmd, (void *)&nextworker, sizeof nextworker)) != sizeof nextworker) { quit("master/worker protocol botched - didn't get pid of next worker.\n"); } /* * Get list of blocks to dump, read the blocks into tape buffer */ while ((nread = atomic_read(cmd, (void *)wp->req, reqsiz)) == reqsiz) { struct req *p = wp->req; for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { if (p->dblk) { blkread(p->dblk, wp->tblock[trecno], p->count * TP_BSIZE); } else { if (p->count != 1 || atomic_read(cmd, (void *)wp->tblock[trecno], TP_BSIZE) != TP_BSIZE) quit("master/worker protocol botched.\n"); } } if (setjmp(jmpbuf) == 0) { ready = 1; if (!caught) (void) pause(); } ready = 0; caught = 0; /* Try to write the data... */ eot_count = 0; size = 0; wrote = 0; while (eot_count < 10 && size < writesize) { #ifdef RDUMP if (host) wrote = rmtwrite(wp->tblock[0]+size, writesize-size); else #endif wrote = write(tapefd, wp->tblock[0]+size, writesize-size); #ifdef WRITEDEBUG printf("worker %d wrote %d\n", worker_number, wrote); #endif if (wrote < 0) break; if (wrote == 0) eot_count++; size += wrote; } #ifdef WRITEDEBUG if (size != writesize) printf("worker %d only wrote %d out of %d bytes and gave up.\n", worker_number, size, writesize); #endif /* * Handle ENOSPC as an EOT condition. */ if (wrote < 0 && errno == ENOSPC) { wrote = 0; eot_count++; } if (eot_count > 0) size = 0; if (wrote < 0) { (void) kill(master, SIGUSR1); for (;;) (void) sigpause(0); } else { /* * pass size of write back to master * (for EOT handling) */ (void)atomic_write(cmd, (const void *)&size, sizeof size); } /* * If partial write, don't want next worker to go. * Also jolts him awake. */ (void) kill(nextworker, SIGUSR2); } if (nread != 0) quit("error reading command pipe: %s\n", strerror(errno)); } /* * Since a read from a pipe may not return all we asked for, * loop until the count is satisfied (or error). */ static int atomic_read(int fd, void *buf, int count) { int got, need = count; while ((got = read(fd, buf, need)) > 0 && (need -= got) > 0) buf += got; return (got < 0 ? got : count - need); } /* * Since a write to a pipe may not write all we ask if we get a signal, * loop until the count is satisfied (or error). */ static int atomic_write(int fd, const void *buf, int count) { int got, need = count; while ((got = write(fd, buf, need)) > 0 && (need -= got) > 0) buf += got; return (got < 0 ? got : count - need); } diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c index 08e902667759..281cffcdf6f2 100644 --- a/sbin/dump/traverse.c +++ b/sbin/dump/traverse.c @@ -1,1010 +1,1008 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1988, 1991, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)traverse.c 8.7 (Berkeley) 6/15/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" union dinode { struct ufs1_dinode dp1; struct ufs2_dinode dp2; }; #define DIP(dp, field) \ ((sblock->fs_magic == FS_UFS1_MAGIC) ? \ (dp)->dp1.field : (dp)->dp2.field) #define DIP_SET(dp, field, val) do {\ if (sblock->fs_magic == FS_UFS1_MAGIC) \ (dp)->dp1.field = (val); \ else \ (dp)->dp2.field = (val); \ } while (0) #define HASDUMPEDFILE 0x1 #define HASSUBDIRS 0x2 static int dirindir(ino_t ino, ufs2_daddr_t blkno, int level, long *size, long *tapesize, int nodump, ino_t maxino); static void dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int level, off_t *size); static void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino); static void ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags, ino_t ino, int last); static int appendextdata(union dinode *dp); static void writeextdata(union dinode *dp, ino_t ino, int added); static int searchdir(ino_t ino, ufs2_daddr_t blkno, long size, long filesize, long *tapesize, int nodump, ino_t maxino); static long blockest(union dinode *dp); /* * This is an estimation of the number of TP_BSIZE blocks in the file. * It estimates the number of blocks in files with holes by assuming * that all of the blocks accounted for by di_blocks are data blocks * (when some of the blocks are usually used for indirect pointers); * hence the estimate may be high. */ static long blockest(union dinode *dp) { long blkest, sizeest; /* * dp->di_size is the size of the file in bytes. * dp->di_blocks stores the number of sectors actually in the file. * If there are more sectors than the size would indicate, this just * means that there are indirect blocks in the file or unused * sectors in the last file block; we can safely ignore these * (blkest = sizeest below). * If the file is bigger than the number of sectors would indicate, * then the file has holes in it. In this case we must use the * block count to estimate the number of data blocks used, but * we use the actual size for estimating the number of indirect * dump blocks (sizeest vs. blkest in the indirect block * calculation). */ if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) return (1); blkest = howmany(dbtob(DIP(dp, di_blocks)), TP_BSIZE); sizeest = howmany(DIP(dp, di_size), TP_BSIZE); if (blkest > sizeest) blkest = sizeest; if (DIP(dp, di_size) > sblock->fs_bsize * UFS_NDADDR) { /* calculate the number of indirect blocks on the dump tape */ blkest += howmany(sizeest - UFS_NDADDR * sblock->fs_bsize / TP_BSIZE, TP_NINDIR); } return (blkest + 1); } /* Auxiliary macro to pick up files changed since previous dump. */ #define CHANGEDSINCE(dp, t) \ (DIP(dp, di_mtime) >= (t) || DIP(dp, di_ctime) >= (t)) /* The WANTTODUMP macro decides whether a file should be dumped. */ #ifdef UF_NODUMP #define WANTTODUMP(dp) \ (CHANGEDSINCE(dp, spcl.c_ddate) && \ (nonodump || (DIP(dp, di_flags) & UF_NODUMP) != UF_NODUMP)) #else #define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate) #endif /* * Dump pass 1. * * Walk the inode list for a file system to find all allocated inodes * that have been modified since the previous dump time. Also, find all * the directories in the file system. */ int mapfiles(ino_t maxino, long *tapesize) { int i, cg, mode, inosused; int anydirskipped = 0; union dinode *dp; struct cg *cgp; ino_t ino; u_char *cp; if ((cgp = malloc(sblock->fs_cgsize)) == NULL) quit("mapfiles: cannot allocate memory.\n"); for (cg = 0; cg < sblock->fs_ncg; cg++) { ino = cg * sblock->fs_ipg; blkread(fsbtodb(sblock, cgtod(sblock, cg)), (char *)cgp, sblock->fs_cgsize); if (sblock->fs_magic == FS_UFS2_MAGIC) inosused = cgp->cg_initediblk; else inosused = sblock->fs_ipg; /* * If we are using soft updates, then we can trust the * cylinder group inode allocation maps to tell us which * inodes are allocated. We will scan the used inode map * to find the inodes that are really in use, and then * read only those inodes in from disk. */ if (sblock->fs_flags & FS_DOSOFTDEP) { if (!cg_chkmagic(cgp)) quit("mapfiles: cg %d: bad magic number\n", cg); cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT]; for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) { if (*cp == 0) continue; for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) { if (*cp & i) break; inosused--; } break; } if (inosused <= 0) continue; } for (i = 0; i < inosused; i++, ino++) { if (ino < UFS_ROOTINO || (dp = getino(ino, &mode)) == NULL || (mode & IFMT) == 0) continue; if (ino >= maxino) { msg("Skipping inode %ju >= maxino %ju\n", (uintmax_t)ino, (uintmax_t)maxino); continue; } /* * Everything must go in usedinomap so that a check * for "in dumpdirmap but not in usedinomap" to detect * dirs with nodump set has a chance of succeeding * (this is used in mapdirs()). */ SETINO(ino, usedinomap); if (mode == IFDIR) SETINO(ino, dumpdirmap); if (WANTTODUMP(dp)) { SETINO(ino, dumpinomap); if (mode != IFREG && mode != IFDIR && mode != IFLNK) *tapesize += 1; else *tapesize += blockest(dp); continue; } if (mode == IFDIR) { if (!nonodump && (DIP(dp, di_flags) & UF_NODUMP)) CLRINO(ino, usedinomap); anydirskipped = 1; } } } /* * Restore gets very upset if the root is not dumped, * so ensure that it always is dumped. */ SETINO(UFS_ROOTINO, dumpinomap); return (anydirskipped); } /* * Dump pass 2. * * Scan each directory on the file system to see if it has any modified * files in it. If it does, and has not already been added to the dump * list (because it was itself modified), then add it. If a directory * has not been modified itself, contains no modified files and has no * subdirectories, then it can be deleted from the dump list and from * the list of directories. By deleting it from the list of directories, * its parent may now qualify for the same treatment on this or a later * pass using this algorithm. */ int mapdirs(ino_t maxino, long *tapesize) { union dinode *dp; int i, isdir, nodump; char *map; ino_t ino; union dinode di; long filesize; int ret, change = 0; isdir = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ isdir = *map++; else isdir >>= 1; /* * If a directory has been removed from usedinomap, it * either has the nodump flag set, or has inherited * it. Although a directory can't be in dumpinomap if * it isn't in usedinomap, we have to go through it to * propagate the nodump flag. */ nodump = !nonodump && (TSTINO(ino, usedinomap) == 0); if ((isdir & 1) == 0 || (TSTINO(ino, dumpinomap) && !nodump)) continue; dp = getino(ino, &i); /* * inode buf may change in searchdir(). */ if (sblock->fs_magic == FS_UFS1_MAGIC) di.dp1 = dp->dp1; else di.dp2 = dp->dp2; filesize = DIP(&di, di_size); for (ret = 0, i = 0; filesize > 0 && i < UFS_NDADDR; i++) { if (DIP(&di, di_db[i]) != 0) ret |= searchdir(ino, DIP(&di, di_db[i]), (long)sblksize(sblock, DIP(&di, di_size), i), filesize, tapesize, nodump, maxino); if (ret & HASDUMPEDFILE) filesize = 0; else filesize -= sblock->fs_bsize; } for (i = 0; filesize > 0 && i < UFS_NIADDR; i++) { if (DIP(&di, di_ib[i]) == 0) continue; ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize, tapesize, nodump, maxino); } if (ret & HASDUMPEDFILE) { SETINO(ino, dumpinomap); *tapesize += blockest(&di); change = 1; continue; } if (nodump) { if (ret & HASSUBDIRS) change = 1; /* subdirs inherit nodump */ CLRINO(ino, dumpdirmap); } else if ((ret & HASSUBDIRS) == 0) if (!TSTINO(ino, dumpinomap)) { CLRINO(ino, dumpdirmap); change = 1; } } return (change); } /* * Read indirect blocks, and pass the data blocks to be searched * as directories. Quit as soon as any entry is found that will * require the directory to be dumped. */ static int dirindir( ino_t ino, ufs2_daddr_t blkno, int ind_level, long *filesize, long *tapesize, int nodump, ino_t maxino) { union { ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)]; ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)]; } idblk; int ret = 0; int i; blkread(fsbtodb(sblock, blkno), (char *)&idblk, (int)sblock->fs_bsize); if (ind_level <= 0) { for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { if (sblock->fs_magic == FS_UFS1_MAGIC) blkno = idblk.ufs1[i]; else blkno = idblk.ufs2[i]; if (blkno != 0) ret |= searchdir(ino, blkno, sblock->fs_bsize, *filesize, tapesize, nodump, maxino); if (ret & HASDUMPEDFILE) *filesize = 0; else *filesize -= sblock->fs_bsize; } return (ret); } ind_level--; for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) { if (sblock->fs_magic == FS_UFS1_MAGIC) blkno = idblk.ufs1[i]; else blkno = idblk.ufs2[i]; if (blkno != 0) ret |= dirindir(ino, blkno, ind_level, filesize, tapesize, nodump, maxino); } return (ret); } /* * Scan a disk block containing directory information looking to see if * any of the entries are on the dump list and to see if the directory * contains any subdirectories. */ static int searchdir( ino_t ino, ufs2_daddr_t blkno, long size, long filesize, long *tapesize, int nodump, ino_t maxino) { int mode; struct direct *dp; union dinode *ip; long loc, ret = 0; static caddr_t dblk; if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL) quit("searchdir: cannot allocate indirect memory.\n"); blkread(fsbtodb(sblock, blkno), dblk, (int)size); if (filesize < size) size = filesize; for (loc = 0; loc < size; ) { dp = (struct direct *)(dblk + loc); if (dp->d_reclen == 0) { msg("corrupted directory, inumber %ju\n", (uintmax_t)ino); break; } loc += dp->d_reclen; if (dp->d_ino == 0) continue; if (dp->d_ino >= maxino) { msg("corrupted directory entry, d_ino %ju >= %ju\n", (uintmax_t)dp->d_ino, (uintmax_t)maxino); break; } if (dp->d_name[0] == '.') { if (dp->d_name[1] == '\0') continue; if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') continue; } if (nodump) { ip = getino(dp->d_ino, &mode); if (TSTINO(dp->d_ino, dumpinomap)) { CLRINO(dp->d_ino, dumpinomap); *tapesize -= blockest(ip); } /* * Add back to dumpdirmap and remove from usedinomap * to propagate nodump. */ if (mode == IFDIR) { SETINO(dp->d_ino, dumpdirmap); CLRINO(dp->d_ino, usedinomap); ret |= HASSUBDIRS; } } else { if (TSTINO(dp->d_ino, dumpinomap)) { ret |= HASDUMPEDFILE; if (ret & HASSUBDIRS) break; } if (TSTINO(dp->d_ino, dumpdirmap)) { ret |= HASSUBDIRS; if (ret & HASDUMPEDFILE) break; } } } return (ret); } /* * Dump passes 3 and 4. * * Dump the contents of an inode to tape. */ void dumpino(union dinode *dp, ino_t ino) { int ind_level, cnt, last, added; off_t size; char buf[TP_BSIZE]; if (newtape) { newtape = 0; dumpmap(dumpinomap, TS_BITS, ino); } CLRINO(ino, dumpinomap); /* * Zero out the size of a snapshot so that it will be dumped * as a zero length file. */ if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) { DIP_SET(dp, di_size, 0); DIP_SET(dp, di_flags, DIP(dp, di_flags) & ~SF_SNAPSHOT); } if (sblock->fs_magic == FS_UFS1_MAGIC) { spcl.c_mode = dp->dp1.di_mode; spcl.c_size = dp->dp1.di_size; spcl.c_extsize = 0; spcl.c_atime = _time32_to_time(dp->dp1.di_atime); spcl.c_atimensec = dp->dp1.di_atimensec; spcl.c_mtime = _time32_to_time(dp->dp1.di_mtime); spcl.c_mtimensec = dp->dp1.di_mtimensec; spcl.c_birthtime = 0; spcl.c_birthtimensec = 0; spcl.c_rdev = dp->dp1.di_rdev; spcl.c_file_flags = dp->dp1.di_flags; spcl.c_uid = dp->dp1.di_uid; spcl.c_gid = dp->dp1.di_gid; } else { spcl.c_mode = dp->dp2.di_mode; spcl.c_size = dp->dp2.di_size; spcl.c_extsize = dp->dp2.di_extsize; spcl.c_atime = _time64_to_time(dp->dp2.di_atime); spcl.c_atimensec = dp->dp2.di_atimensec; spcl.c_mtime = _time64_to_time(dp->dp2.di_mtime); spcl.c_mtimensec = dp->dp2.di_mtimensec; spcl.c_birthtime = _time64_to_time(dp->dp2.di_birthtime); spcl.c_birthtimensec = dp->dp2.di_birthnsec; spcl.c_rdev = dp->dp2.di_rdev; spcl.c_file_flags = dp->dp2.di_flags; spcl.c_uid = dp->dp2.di_uid; spcl.c_gid = dp->dp2.di_gid; } spcl.c_type = TS_INODE; spcl.c_count = 0; switch (DIP(dp, di_mode) & S_IFMT) { case 0: /* * Freed inode. */ return; case S_IFLNK: /* * Check for short symbolic link. */ if (DIP(dp, di_size) > 0 && DIP(dp, di_size) < sblock->fs_maxsymlinklen) { spcl.c_addr[0] = 1; spcl.c_count = 1; added = appendextdata(dp); writeheader(ino); memmove(buf, DIP(dp, di_shortlink), (u_long)DIP(dp, di_size)); buf[DIP(dp, di_size)] = '\0'; writerec(buf, 0); writeextdata(dp, ino, added); return; } /* FALLTHROUGH */ case S_IFDIR: case S_IFREG: if (DIP(dp, di_size) > 0) break; /* FALLTHROUGH */ case S_IFIFO: case S_IFSOCK: case S_IFCHR: case S_IFBLK: added = appendextdata(dp); writeheader(ino); writeextdata(dp, ino, added); return; default: msg("Warning: undefined file type 0%o\n", DIP(dp, di_mode) & IFMT); return; } if (DIP(dp, di_size) > UFS_NDADDR * sblock->fs_bsize) { cnt = UFS_NDADDR * sblock->fs_frag; last = 0; } else { cnt = howmany(DIP(dp, di_size), sblock->fs_fsize); last = 1; } if (sblock->fs_magic == FS_UFS1_MAGIC) ufs1_blksout(&dp->dp1.di_db[0], cnt, ino); else ufs2_blksout(dp, &dp->dp2.di_db[0], cnt, ino, last); if ((size = DIP(dp, di_size) - UFS_NDADDR * sblock->fs_bsize) <= 0) return; for (ind_level = 0; ind_level < UFS_NIADDR; ind_level++) { dmpindir(dp, ino, DIP(dp, di_ib[ind_level]), ind_level, &size); if (size <= 0) return; } } /* * Read indirect blocks, and pass the data blocks to be dumped. */ static void dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int ind_level, off_t *size) { union { ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)]; ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)]; } idblk; int i, cnt, last; if (blk != 0) blkread(fsbtodb(sblock, blk), (char *)&idblk, (int)sblock->fs_bsize); else memset(&idblk, 0, sblock->fs_bsize); if (ind_level <= 0) { if (*size > NINDIR(sblock) * sblock->fs_bsize) { cnt = NINDIR(sblock) * sblock->fs_frag; last = 0; } else { cnt = howmany(*size, sblock->fs_fsize); last = 1; } *size -= NINDIR(sblock) * sblock->fs_bsize; if (sblock->fs_magic == FS_UFS1_MAGIC) ufs1_blksout(idblk.ufs1, cnt, ino); else ufs2_blksout(dp, idblk.ufs2, cnt, ino, last); return; } ind_level--; for (i = 0; i < NINDIR(sblock); i++) { if (sblock->fs_magic == FS_UFS1_MAGIC) dmpindir(dp, ino, idblk.ufs1[i], ind_level, size); else dmpindir(dp, ino, idblk.ufs2[i], ind_level, size); if (*size <= 0) return; } } /* * Collect up the data into tape record sized buffers and output them. */ static void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino) { ufs1_daddr_t *bp; int i, j, count, blks, tbperdb; blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); tbperdb = sblock->fs_bsize >> tp_bshift; for (i = 0; i < blks; i += TP_NINDIR) { if (i + TP_NINDIR > blks) count = blks; else count = i + TP_NINDIR; assert(count <= TP_NINDIR + i); for (j = i; j < count; j++) if (blkp[j / tbperdb] != 0) spcl.c_addr[j - i] = 1; else spcl.c_addr[j - i] = 0; spcl.c_count = count - i; writeheader(ino); bp = &blkp[i / tbperdb]; for (j = i; j < count; j += tbperdb, bp++) if (*bp != 0) { if (j + tbperdb <= count) dumpblock(*bp, (int)sblock->fs_bsize); else dumpblock(*bp, (count - j) * TP_BSIZE); } spcl.c_type = TS_ADDR; } } /* * Collect up the data into tape record sized buffers and output them. */ static void ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags, ino_t ino, int last) { ufs2_daddr_t *bp; int i, j, count, resid, blks, tbperdb, added; static int writingextdata = 0; /* * Calculate the number of TP_BSIZE blocks to be dumped. * For filesystems with a fragment size bigger than TP_BSIZE, * only part of the final fragment may need to be dumped. */ blks = howmany(frags * sblock->fs_fsize, TP_BSIZE); if (last) { if (writingextdata) resid = howmany(fragoff(sblock, spcl.c_extsize), TP_BSIZE); else resid = howmany(fragoff(sblock, dp->dp2.di_size), TP_BSIZE); if (resid > 0) blks -= howmany(sblock->fs_fsize, TP_BSIZE) - resid; } tbperdb = sblock->fs_bsize >> tp_bshift; for (i = 0; i < blks; i += TP_NINDIR) { if (i + TP_NINDIR > blks) count = blks; else count = i + TP_NINDIR; assert(count <= TP_NINDIR + i); for (j = i; j < count; j++) if (blkp[j / tbperdb] != 0) spcl.c_addr[j - i] = 1; else spcl.c_addr[j - i] = 0; spcl.c_count = count - i; if (last && count == blks && !writingextdata) added = appendextdata(dp); writeheader(ino); bp = &blkp[i / tbperdb]; for (j = i; j < count; j += tbperdb, bp++) if (*bp != 0) { if (j + tbperdb <= count) dumpblock(*bp, (int)sblock->fs_bsize); else dumpblock(*bp, (count - j) * TP_BSIZE); } spcl.c_type = TS_ADDR; spcl.c_count = 0; if (last && count == blks && !writingextdata) { writingextdata = 1; writeextdata(dp, ino, added); writingextdata = 0; } } } /* * If there is room in the current block for the extended attributes * as well as the file data, update the header to reflect the added * attribute data at the end. Attributes are placed at the end so that * old versions of restore will correctly restore the file and simply * discard the extra data at the end that it does not understand. * The attribute data is dumped following the file data by the * writeextdata() function (below). */ static int appendextdata(union dinode *dp) { int i, blks, tbperdb; /* * If no extended attributes, there is nothing to do. */ if (spcl.c_extsize == 0) return (0); /* * If there is not enough room at the end of this block * to add the extended attributes, then rather than putting * part of them here, we simply push them entirely into a * new block rather than putting some here and some later. */ if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize) blks = howmany(UFS_NXADDR * sblock->fs_bsize, TP_BSIZE); else blks = howmany(spcl.c_extsize, TP_BSIZE); if (spcl.c_count + blks > TP_NINDIR) return (0); /* * Update the block map in the header to indicate the added * extended attribute. They will be appended after the file * data by the writeextdata() routine. */ tbperdb = sblock->fs_bsize >> tp_bshift; assert(spcl.c_count + blks <= TP_NINDIR); for (i = 0; i < blks; i++) if (&dp->dp2.di_extb[i / tbperdb] != 0) spcl.c_addr[spcl.c_count + i] = 1; else spcl.c_addr[spcl.c_count + i] = 0; spcl.c_count += blks; return (blks); } /* * Dump the extended attribute data. If there was room in the file * header, then all we need to do is output the data blocks. If there * was not room in the file header, then an additional TS_ADDR header * is created to hold the attribute data. */ static void writeextdata(union dinode *dp, ino_t ino, int added) { int i, frags, blks, tbperdb, last; ufs2_daddr_t *bp; off_t size; /* * If no extended attributes, there is nothing to do. */ if (spcl.c_extsize == 0) return; /* * If there was no room in the file block for the attributes, * dump them out in a new block, otherwise just dump the data. */ if (added == 0) { if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize) { frags = UFS_NXADDR * sblock->fs_frag; last = 0; } else { frags = howmany(spcl.c_extsize, sblock->fs_fsize); last = 1; } ufs2_blksout(dp, &dp->dp2.di_extb[0], frags, ino, last); } else { if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize) blks = howmany(UFS_NXADDR * sblock->fs_bsize, TP_BSIZE); else blks = howmany(spcl.c_extsize, TP_BSIZE); tbperdb = sblock->fs_bsize >> tp_bshift; for (i = 0; i < blks; i += tbperdb) { bp = &dp->dp2.di_extb[i / tbperdb]; if (*bp != 0) { if (i + tbperdb <= blks) dumpblock(*bp, (int)sblock->fs_bsize); else dumpblock(*bp, (blks - i) * TP_BSIZE); } } } /* * If an indirect block is added for extended attributes, then * di_exti below should be changed to the structure element * that references the extended attribute indirect block. This * definition is here only to make it compile without complaint. */ #define di_exti di_spare[0] /* * If the extended attributes fall into an indirect block, * dump it as well. */ if ((size = spcl.c_extsize - UFS_NXADDR * sblock->fs_bsize) > 0) dmpindir(dp, ino, dp->dp2.di_exti, 0, &size); } /* * Dump a map to the tape. */ void dumpmap(char *map, int type, ino_t ino) { int i; char *cp; spcl.c_type = type; spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE); writeheader(ino); for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE) writerec(cp, 0); } /* * Write a header record to the dump tape. */ void writeheader(ino_t ino) { int32_t sum, cnt, *lp; if (rsync_friendly >= 2) { /* don't track changes to access time */ spcl.c_atime = spcl.c_mtime; spcl.c_atimensec = spcl.c_mtimensec; } spcl.c_inumber = ino; spcl.c_magic = FS_UFS2_MAGIC; spcl.c_checksum = 0; lp = (int32_t *)&spcl; sum = 0; cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t)); while (--cnt >= 0) { sum += *lp++; sum += *lp++; sum += *lp++; sum += *lp++; } spcl.c_checksum = CHECKSUM - sum; writerec((char *)&spcl, 1); } union dinode * getino(ino_t inum, int *modep) { static ino_t minino, maxino; static caddr_t inoblock; struct ufs1_dinode *dp1; struct ufs2_dinode *dp2; if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL) quit("cannot allocate inode memory.\n"); curino = inum; if (inum >= minino && inum < maxino) goto gotit; blkread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock, (int)sblock->fs_bsize); minino = inum - (inum % INOPB(sblock)); maxino = minino + INOPB(sblock); gotit: if (sblock->fs_magic == FS_UFS1_MAGIC) { dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino]; *modep = (dp1->di_mode & IFMT); return ((union dinode *)dp1); } dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino]; *modep = (dp2->di_mode & IFMT); return ((union dinode *)dp2); } /* * Read a chunk of data from the disk. * Try to recover from hard errors by reading in sector sized pieces. * Error recovery is attempted at most BREADEMAX times before seeking * consent from the operator to continue. */ int breaderrors = 0; #define BREADEMAX 32 void blkread(ufs2_daddr_t blkno, char *buf, int size) { int secsize, bytes, resid, xfer, base, cnt, i; static char *tmpbuf; off_t offset; loop: offset = blkno << dev_bshift; secsize = sblock->fs_fsize; base = offset % secsize; resid = size % secsize; /* * If the transfer request starts or ends on a non-sector * boundary, we must read the entire sector and copy out * just the part that we need. */ if (base == 0 && resid == 0) { cnt = cread(diskfd, buf, size, offset); if (cnt == size) return; } else { if (tmpbuf == NULL && (tmpbuf = malloc(secsize)) == NULL) quit("buffer malloc failed\n"); xfer = 0; bytes = size; if (base != 0) { cnt = cread(diskfd, tmpbuf, secsize, offset - base); if (cnt != secsize) goto bad; xfer = MIN(secsize - base, size); offset += xfer; bytes -= xfer; resid = bytes % secsize; memcpy(buf, &tmpbuf[base], xfer); } if (bytes >= secsize) { cnt = cread(diskfd, &buf[xfer], bytes - resid, offset); if (cnt != bytes - resid) goto bad; xfer += cnt; offset += cnt; } if (resid == 0) return; cnt = cread(diskfd, tmpbuf, secsize, offset); if (cnt == secsize) { memcpy(&buf[xfer], tmpbuf, resid); return; } } bad: if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) { /* * Trying to read the final fragment. * * NB - dump only works in TP_BSIZE blocks, hence * rounds `dev_bsize' fragments up to TP_BSIZE pieces. * It should be smarter about not actually trying to * read more than it can get, but for the time being * we punt and scale back the read only when it gets * us into trouble. (mkm 9/25/83) */ size -= dev_bsize; goto loop; } if (cnt == -1) msg("read error from %s: %s: [block %jd]: count=%d\n", disk, strerror(errno), (intmax_t)blkno, size); else msg("short read error from %s: [block %jd]: count=%d, got=%d\n", disk, (intmax_t)blkno, size, cnt); if (++breaderrors > BREADEMAX) { msg("More than %d block read errors from %s\n", BREADEMAX, disk); broadcast("DUMP IS AILING!\n"); msg("This is an unrecoverable error.\n"); if (!query("Do you want to attempt to continue?")){ dumpabort(0); /*NOTREACHED*/ } else breaderrors = 0; } /* * Zero buffer, then try to read each sector of buffer separately, * and bypass the cache. */ memset(buf, 0, size); for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) { if ((cnt = pread(diskfd, buf, (int)dev_bsize, ((off_t)blkno << dev_bshift))) == dev_bsize) continue; if (cnt == -1) { msg("read error from %s: %s: [sector %jd]: count=%ld\n", disk, strerror(errno), (intmax_t)blkno, dev_bsize); continue; } msg("short read from %s: [sector %jd]: count=%ld, got=%d\n", disk, (intmax_t)blkno, dev_bsize, cnt); } } diff --git a/sbin/dump/unctime.c b/sbin/dump/unctime.c index 3e03a65f1a7c..5f44c80f9358 100644 --- a/sbin/dump/unctime.c +++ b/sbin/dump/unctime.c @@ -1,58 +1,56 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)unctime.c 8.2 (Berkeley) 6/14/94"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include /* * Convert a ctime(3) format string into a system format date. * Return the date thus calculated. * * Return -1 if the string is not in ctime format. */ time_t unctime(char *str) { struct tm then; str = strptime(str, "%a %b %e %T %Y", &then); if (str == NULL || (*str != '\n' && *str != '\0')) return ((time_t)-1); then.tm_isdst = -1; return (mktime(&then)); } diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c index 739f281feb7f..7d2598c190e8 100644 --- a/sbin/dumpfs/dumpfs.c +++ b/sbin/dumpfs/dumpfs.c @@ -1,548 +1,546 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009 Robert N. M. Watson * All rights reserved. * * This software was developed at the University of Cambridge Computer * Laboratory with support from a grant from Google, Inc. * * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Marshall * Kirk McKusick and Network Associates Laboratories, the Security * Research Division of Network Associates, Inc. under DARPA/SPAWAR * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS * research program. * * Copyright (c) 1983, 1992, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1992, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)dumpfs.c 8.5 (Berkeley) 4/29/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define afs disk.d_fs #define acg disk.d_cg static struct uufsd disk; static int dumpfs(const char *, int); static int dumpfsid(void); static int dumpcg(void); static int dumpfreespace(const char *, int); static void dumpfreespacecg(int); static int marshal(const char *); static void pbits(void *, int); static void pblklist(void *, int, off_t, int); static const char *ufserr(void); static void usage(void) __dead2; int main(int argc, char *argv[]) { const char *name; int ch, dofreespace, domarshal, dolabel, dosb, eval; dofreespace = domarshal = dolabel = dosb = eval = 0; while ((ch = getopt(argc, argv, "lfms")) != -1) { switch (ch) { case 'f': dofreespace++; break; case 'm': domarshal = 1; break; case 'l': dolabel = 1; break; case 's': dosb = 1; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc < 1) usage(); if (dofreespace && domarshal) usage(); if (dofreespace > 2) usage(); while ((name = *argv++) != NULL) { if (ufs_disk_fillout_blank(&disk, name) == -1 || sbfind(&disk, 0) == -1) { printf("\n%s: %s\n", name, ufserr()); eval |= 1; continue; } if (dofreespace) eval |= dumpfreespace(name, dofreespace); else if (domarshal) eval |= marshal(name); else if (dolabel) eval |= dumpfsid(); else eval |= dumpfs(name, dosb); ufs_disk_close(&disk); } exit(eval); } static int dumpfsid(void) { printf("%sufsid/%08x%08x\n", _PATH_DEV, afs.fs_id[0], afs.fs_id[1]); return 0; } static int dumpfs(const char *name, int dosb) { time_t fstime, fsmtime; int64_t fssize; int32_t fsflags; int i, ret; switch (disk.d_ufs) { case 2: fssize = afs.fs_size; fstime = afs.fs_time; fsmtime = afs.fs_mtime; printf("magic\t%x (UFS2)\n", afs.fs_magic); printf("last mounted time\t%s", ctime(&fsmtime)); printf("last modified time\t%s", ctime(&fstime)); printf("superblock location\t%jd\tid\t[ %08x %08x ]\n", (intmax_t)afs.fs_sblockloc, afs.fs_id[0], afs.fs_id[1]); printf("ncg\t%d\tsize\t%jd\tblocks\t%jd\n", afs.fs_ncg, (intmax_t)fssize, (intmax_t)afs.fs_dsize); break; case 1: fssize = afs.fs_old_size; fstime = afs.fs_old_time; printf("magic\t%x (UFS1)\ttime\t%s", afs.fs_magic, ctime(&fstime)); printf("id\t[ %08x %08x ]\n", afs.fs_id[0], afs.fs_id[1]); printf("ncg\t%d\tsize\t%jd\tblocks\t%jd\n", afs.fs_ncg, (intmax_t)fssize, (intmax_t)afs.fs_dsize); break; default: printf("Unknown filesystem type %d\n", disk.d_ufs); return (1); } printf("bsize\t%d\tshift\t%d\tmask\t0x%08x\n", afs.fs_bsize, afs.fs_bshift, afs.fs_bmask); printf("fsize\t%d\tshift\t%d\tmask\t0x%08x\n", afs.fs_fsize, afs.fs_fshift, afs.fs_fmask); printf("frag\t%d\tshift\t%d\tfsbtodb\t%d\n", afs.fs_frag, afs.fs_fragshift, afs.fs_fsbtodb); printf("minfree\t%d%%\toptim\t%s\tsymlinklen %d\n", afs.fs_minfree, afs.fs_optim == FS_OPTSPACE ? "space" : "time", afs.fs_maxsymlinklen); switch (disk.d_ufs) { case 2: printf("%s %d\tmaxbpg\t%d\tmaxcontig %d\tcontigsumsize %d\n", "maxbsize", afs.fs_maxbsize, afs.fs_maxbpg, afs.fs_maxcontig, afs.fs_contigsumsize); printf("nbfree\t%jd\tndir\t%jd\tnifree\t%jd\tnffree\t%jd\n", (intmax_t)afs.fs_cstotal.cs_nbfree, (intmax_t)afs.fs_cstotal.cs_ndir, (intmax_t)afs.fs_cstotal.cs_nifree, (intmax_t)afs.fs_cstotal.cs_nffree); printf("bpg\t%d\tfpg\t%d\tipg\t%d\tunrefs\t%jd\n", afs.fs_fpg / afs.fs_frag, afs.fs_fpg, afs.fs_ipg, (intmax_t)afs.fs_unrefs); printf("nindir\t%d\tinopb\t%d\tmaxfilesize\t%ju\n", afs.fs_nindir, afs.fs_inopb, (uintmax_t)afs.fs_maxfilesize); printf("sbsize\t%d\tcgsize\t%d\tcsaddr\t%jd\tcssize\t%d\n", afs.fs_sbsize, afs.fs_cgsize, (intmax_t)afs.fs_csaddr, afs.fs_cssize); break; case 1: printf("maxbpg\t%d\tmaxcontig %d\tcontigsumsize %d\n", afs.fs_maxbpg, afs.fs_maxcontig, afs.fs_contigsumsize); printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n", afs.fs_old_cstotal.cs_nbfree, afs.fs_old_cstotal.cs_ndir, afs.fs_old_cstotal.cs_nifree, afs.fs_old_cstotal.cs_nffree); printf("cpg\t%d\tbpg\t%d\tfpg\t%d\tipg\t%d\n", afs.fs_old_cpg, afs.fs_fpg / afs.fs_frag, afs.fs_fpg, afs.fs_ipg); printf("nindir\t%d\tinopb\t%d\tnspf\t%d\tmaxfilesize\t%ju\n", afs.fs_nindir, afs.fs_inopb, afs.fs_old_nspf, (uintmax_t)afs.fs_maxfilesize); printf("sbsize\t%d\tcgsize\t%d\tcgoffset %d\tcgmask\t0x%08x\n", afs.fs_sbsize, afs.fs_cgsize, afs.fs_old_cgoffset, afs.fs_old_cgmask); printf("csaddr\t%jd\tcssize\t%d\n", (intmax_t)afs.fs_csaddr, afs.fs_cssize); printf("rotdelay %dms\trps\t%d\ttrackskew %d\tinterleave %d\n", afs.fs_old_rotdelay, afs.fs_old_rps, afs.fs_old_trackskew, afs.fs_old_interleave); printf("nsect\t%d\tnpsect\t%d\tspc\t%d\n", afs.fs_old_nsect, afs.fs_old_npsect, afs.fs_old_spc); break; default: printf("Unknown filesystem type %d\n", disk.d_ufs); return (1); } printf("old_cpg\t%d\tsize_cg\t%zu\tCGSIZE\t%zu\n", afs.fs_old_cpg, sizeof(struct cg), CGSIZE(&afs)); printf("sblkno\t%d\tcblkno\t%d\tiblkno\t%d\tdblkno\t%d\n", afs.fs_sblkno, afs.fs_cblkno, afs.fs_iblkno, afs.fs_dblkno); printf("cgrotor\t%d\tfmod\t%d\tronly\t%d\tclean\t%d\n", afs.fs_cgrotor, afs.fs_fmod, afs.fs_ronly, afs.fs_clean); printf("metaspace %jd\tavgfpdir %d\tavgfilesize %d\n", afs.fs_metaspace, afs.fs_avgfpdir, afs.fs_avgfilesize); printf("flags\t"); if (afs.fs_old_flags & FS_FLAGS_UPDATED) fsflags = afs.fs_flags; else fsflags = afs.fs_old_flags; if (fsflags == 0) printf("none"); if (fsflags & FS_UNCLEAN) printf("unclean "); if (fsflags & FS_DOSOFTDEP) printf("soft-updates%s ", (fsflags & FS_SUJ) ? "+journal" : ""); if (fsflags & FS_NEEDSFSCK) printf("needs-fsck-run "); if (fsflags & FS_INDEXDIRS) printf("indexed-directories "); if (fsflags & FS_ACLS) printf("acls "); if (fsflags & FS_MULTILABEL) printf("multilabel "); if (fsflags & FS_GJOURNAL) printf("gjournal "); if (fsflags & FS_FLAGS_UPDATED) printf("fs_flags-expanded "); if (fsflags & FS_NFS4ACLS) printf("nfsv4acls "); if (fsflags & FS_TRIM) printf("trim "); fsflags &= ~(FS_UNCLEAN | FS_DOSOFTDEP | FS_NEEDSFSCK | FS_METACKHASH | FS_ACLS | FS_MULTILABEL | FS_GJOURNAL | FS_FLAGS_UPDATED | FS_NFS4ACLS | FS_SUJ | FS_TRIM | FS_INDEXDIRS); if (fsflags != 0) printf("unknown-flags (%#x)", fsflags); putchar('\n'); if (afs.fs_flags & FS_METACKHASH) { printf("check hashes\t"); fsflags = afs.fs_metackhash; if (fsflags == 0) printf("none"); if (fsflags & CK_SUPERBLOCK) printf("superblock "); if (fsflags & CK_CYLGRP) printf("cylinder-groups "); if (fsflags & CK_INODE) printf("inodes "); if (fsflags & CK_INDIR) printf("indirect-blocks "); if (fsflags & CK_DIR) printf("directories "); } fsflags &= ~(CK_SUPERBLOCK | CK_CYLGRP | CK_INODE | CK_INDIR | CK_DIR); if (fsflags != 0) printf("unknown flags (%#x)", fsflags); putchar('\n'); printf("fsmnt\t%s\n", afs.fs_fsmnt); printf("volname\t%s\tswuid\t%ju\tprovidersize\t%ju\n", afs.fs_volname, (uintmax_t)afs.fs_swuid, (uintmax_t)afs.fs_providersize); printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t"); for (i = 0; i < afs.fs_ncg; i++) { struct csum *cs = &afs.fs_cs(&afs, i); if (i && i % 4 == 0) printf("\n\t"); printf("(%d,%d,%d,%d) ", cs->cs_nbfree, cs->cs_ndir, cs->cs_nifree, cs->cs_nffree); } printf("\n"); if (fssize % afs.fs_fpg) { if (disk.d_ufs == 1) printf("cylinders in last group %d\n", howmany(afs.fs_old_size % afs.fs_fpg, afs.fs_old_spc / afs.fs_old_nspf)); printf("blocks in last group %ld\n\n", (long)((fssize % afs.fs_fpg) / afs.fs_frag)); } if (dosb) return (0); ret = 0; while ((i = cgread(&disk)) != 0) { if (i == -1) { ret = 1; printf("\ncg %d: %s\n", disk.d_lcg, ufserr()); } else if (dumpcg()) ret = 1; } return (ret); } static int dumpcg(void) { time_t cgtime; off_t cur; int i, j; printf("\ncg %d:\n", disk.d_lcg); cur = fsbtodb(&afs, cgtod(&afs, disk.d_lcg)) * disk.d_bsize; switch (disk.d_ufs) { case 2: cgtime = acg.cg_time; printf("magic\t%x\ttell\t%jx\ttime\t%s", acg.cg_magic, (intmax_t)cur, ctime(&cgtime)); printf("cgx\t%d\tndblk\t%d\tniblk\t%d\tinitiblk %d\tunrefs %d\n", acg.cg_cgx, acg.cg_ndblk, acg.cg_niblk, acg.cg_initediblk, acg.cg_unrefs); break; case 1: cgtime = acg.cg_old_time; printf("magic\t%x\ttell\t%jx\ttime\t%s", acg.cg_magic, (intmax_t)cur, ctime(&cgtime)); printf("cgx\t%d\tncyl\t%d\tniblk\t%d\tndblk\t%d\n", acg.cg_cgx, acg.cg_old_ncyl, acg.cg_old_niblk, acg.cg_ndblk); break; default: break; } printf("nbfree\t%d\tndir\t%d\tnifree\t%d\tnffree\t%d\n", acg.cg_cs.cs_nbfree, acg.cg_cs.cs_ndir, acg.cg_cs.cs_nifree, acg.cg_cs.cs_nffree); printf("rotor\t%d\tirotor\t%d\tfrotor\t%d\nfrsum", acg.cg_rotor, acg.cg_irotor, acg.cg_frotor); for (i = 1, j = 0; i < afs.fs_frag; i++) { printf("\t%d", acg.cg_frsum[i]); j += i * acg.cg_frsum[i]; } printf("\nsum of frsum: %d", j); if (afs.fs_contigsumsize > 0) { for (i = 1; i < afs.fs_contigsumsize; i++) { if ((i - 1) % 8 == 0) printf("\nclusters %d-%d:", i, MIN(afs.fs_contigsumsize - 1, i + 7)); printf("\t%d", cg_clustersum(&acg)[i]); } printf("\nclusters size %d and over: %d\n", afs.fs_contigsumsize, cg_clustersum(&acg)[afs.fs_contigsumsize]); printf("clusters free:\t"); pbits(cg_clustersfree(&acg), acg.cg_nclusterblks); } else printf("\n"); printf("inodes used:\t"); pbits(cg_inosused(&acg), afs.fs_ipg); printf("blks free:\t"); pbits(cg_blksfree(&acg), afs.fs_fpg); return (0); } static int dumpfreespace(const char *name, int fflag) { intmax_t startblkno; int i, ret; ret = 0; while ((i = cgread(&disk)) != 0) { if (i != -1) { dumpfreespacecg(fflag); } else { startblkno = disk.d_lcg * afs.fs_fpg; printf("\nBlocks %jd-%jd of cg %d skipped: %s\n", startblkno, startblkno + afs.fs_fpg - 1, disk.d_lcg, ufserr()); ret = 1; } } return (ret); } static void dumpfreespacecg(int fflag) { pblklist(cg_blksfree(&acg), afs.fs_fpg, disk.d_lcg * afs.fs_fpg, fflag); } static int marshal(const char *name) { struct fs *fs; fs = &disk.d_fs; printf("# newfs command for %s (%s)\n", name, disk.d_name); printf("newfs "); if (fs->fs_volname[0] != '\0') printf("-L %s ", fs->fs_volname); printf("-O %d ", disk.d_ufs); if (fs->fs_flags & FS_DOSOFTDEP) printf("-U "); printf("-a %d ", fs->fs_maxcontig); printf("-b %d ", fs->fs_bsize); /* -c is dumb */ printf("-d %d ", fs->fs_maxbsize); printf("-e %d ", fs->fs_maxbpg); printf("-f %d ", fs->fs_fsize); printf("-g %d ", fs->fs_avgfilesize); printf("-h %d ", fs->fs_avgfpdir); printf("-i %jd ", fragroundup(fs, lblktosize(fs, fragstoblks(fs, fs->fs_fpg)) / fs->fs_ipg)); if (fs->fs_flags & FS_SUJ) printf("-j "); if (fs->fs_flags & FS_GJOURNAL) printf("-J "); printf("-k %jd ", fs->fs_metaspace); if (fs->fs_flags & FS_MULTILABEL) printf("-l "); printf("-m %d ", fs->fs_minfree); /* -n unimplemented */ printf("-o "); switch (fs->fs_optim) { case FS_OPTSPACE: printf("space "); break; case FS_OPTTIME: printf("time "); break; default: printf("unknown "); break; } /* -p..r unimplemented */ printf("-s %jd ", (intmax_t)fsbtodb(fs, fs->fs_size)); if (fs->fs_flags & FS_TRIM) printf("-t "); printf("%s ", disk.d_name); printf("\n"); return 0; } static void pbits(void *vp, int max) { int i; char *p; int count, j; for (count = i = 0, p = vp; i < max; i++) if (isset(p, i)) { if (count) printf(",%s", count % 6 ? " " : "\n\t"); count++; printf("%d", i); j = i; while ((i+1) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" /* *********************************************************** GLOBALS ***** */ #ifdef FS_DEBUG int _dbg_lvl_ = (DL_INFO); /* DL_TRC */ #endif /* FS_DEBUG */ static struct uufsd disk; #define sblock disk.d_fs #define acg disk.d_cg static union { struct fs fs; char pad[SBLOCKSIZE]; } fsun; #define osblock fsun.fs static char i1blk[MAXBSIZE]; static char i2blk[MAXBSIZE]; static char i3blk[MAXBSIZE]; static struct csum *fscs; /* ******************************************************** PROTOTYPES ***** */ static void usage(void); static void dump_whole_ufs1_inode(ino_t, int); static void dump_whole_ufs2_inode(ino_t, int); #define DUMP_WHOLE_INODE(A,B) \ ( disk.d_ufs == 1 \ ? dump_whole_ufs1_inode((A),(B)) : dump_whole_ufs2_inode((A),(B)) ) /* ************************************************************** main ***** */ /* * ffsinfo(8) is a tool to dump all metadata of a file system. It helps to find * errors is the file system much easier. You can run ffsinfo before and after * an fsck(8), and compare the two ascii dumps easy with diff, and you see * directly where the problem is. You can control how much detail you want to * see with some command line arguments. You can also easy check the status * of a file system, like is there is enough space for growing a file system, * or how many active snapshots do we have. It provides much more detailed * information then dumpfs. Snapshots, as they are very new, are not really * supported. They are just mentioned currently, but it is planned to run * also over active snapshots, to even get that output. */ int main(int argc, char **argv) { DBG_FUNC("main") char *device, *special; int ch; size_t len; struct stat st; struct csum *dbg_csp; int dbg_csc; char dbg_line[80]; int cylno,i; int cfg_cg, cfg_in, cfg_lv; int cg_start, cg_stop; ino_t in; char *out_file; DBG_ENTER; cfg_lv = 0xff; cfg_in = -2; cfg_cg = -2; out_file = strdup("-"); while ((ch = getopt(argc, argv, "g:i:l:o:")) != -1) { switch (ch) { case 'g': cfg_cg = strtol(optarg, NULL, 0); if (errno == EINVAL || errno == ERANGE) err(1, "%s", optarg); if (cfg_cg < -1) usage(); break; case 'i': cfg_in = strtol(optarg, NULL, 0); if (errno == EINVAL || errno == ERANGE) err(1, "%s", optarg); if (cfg_in < 0) usage(); break; case 'l': cfg_lv = strtol(optarg, NULL, 0); if (errno == EINVAL||errno == ERANGE) err(1, "%s", optarg); if (cfg_lv < 0x1 || cfg_lv > 0x3ff) usage(); break; case 'o': free(out_file); out_file = strdup(optarg); if (out_file == NULL) errx(1, "strdup failed"); break; case '?': /* FALLTHROUGH */ default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); device = *argv; /* * Now we try to guess the (raw)device name. */ if (0 == strrchr(device, '/') && stat(device, &st) == -1) { /*- * No path prefix was given, so try in this order: * /dev/r%s * /dev/%s * /dev/vinum/r%s * /dev/vinum/%s. * * FreeBSD now doesn't distinguish between raw and block * devices any longer, but it should still work this way. */ len = strlen(device) + strlen(_PATH_DEV) + 2 + strlen("vinum/"); special = (char *)malloc(len); if (special == NULL) errx(1, "malloc failed"); snprintf(special, len, "%sr%s", _PATH_DEV, device); if (stat(special, &st) == -1) { snprintf(special, len, "%s%s", _PATH_DEV, device); if (stat(special, &st) == -1) { snprintf(special, len, "%svinum/r%s", _PATH_DEV, device); if (stat(special, &st) == -1) /* For now this is the 'last resort' */ snprintf(special, len, "%svinum/%s", _PATH_DEV, device); } } device = special; } if (ufs_disk_fillout_blank(&disk, device) == -1 || sbfind(&disk, 0) == -1) err(1, "superblock fetch(%s) failed: %s", device, disk.d_error); DBG_OPEN(out_file); /* already here we need a superblock */ if (cfg_lv & 0x001) DBG_DUMP_FS(&sblock, "primary sblock"); /* Determine here what cylinder groups to dump */ if (cfg_cg==-2) { cg_start = 0; cg_stop = sblock.fs_ncg; } else if (cfg_cg == -1) { cg_start = sblock.fs_ncg - 1; cg_stop = sblock.fs_ncg; } else if (cfg_cg < sblock.fs_ncg) { cg_start = cfg_cg; cg_stop = cfg_cg + 1; } else { cg_start = sblock.fs_ncg; cg_stop = sblock.fs_ncg; } if (cfg_lv & 0x004) { fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize); if (fscs == NULL) errx(1, "calloc failed"); /* get the cylinder summary into the memory ... */ for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) { if (bread(&disk, fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)), (void *)(((char *)fscs)+i), (size_t)(sblock.fs_cssize-i < sblock.fs_bsize ? sblock.fs_cssize - i : sblock.fs_bsize)) == -1) err(1, "bread: %s", disk.d_error); } dbg_csp = fscs; /* ... and dump it */ for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) { snprintf(dbg_line, sizeof(dbg_line), "%d. csum in fscs", dbg_csc); DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++); } } if (cfg_lv & 0xf8) { /* for each requested cylinder group ... */ for (cylno = cg_start; cylno < cg_stop; cylno++) { snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno); if (cfg_lv & 0x002) { /* dump the superblock copies */ if (bread(&disk, fsbtodb(&sblock, cgsblock(&sblock, cylno)), (void *)&osblock, SBLOCKSIZE) == -1) err(1, "bread: %s", disk.d_error); DBG_DUMP_FS(&osblock, dbg_line); } /* * Read the cylinder group and dump whatever was * requested. */ if (bread(&disk, fsbtodb(&sblock, cgtod(&sblock, cylno)), (void *)&acg, (size_t)sblock.fs_cgsize) == -1) err(1, "bread: %s", disk.d_error); if (cfg_lv & 0x008) DBG_DUMP_CG(&sblock, dbg_line, &acg); if (cfg_lv & 0x010) DBG_DUMP_INMAP(&sblock, dbg_line, &acg); if (cfg_lv & 0x020) DBG_DUMP_FRMAP(&sblock, dbg_line, &acg); if (cfg_lv & 0x040) { DBG_DUMP_CLMAP(&sblock, dbg_line, &acg); DBG_DUMP_CLSUM(&sblock, dbg_line, &acg); } #ifdef NOT_CURRENTLY /* * See the comment in sbin/growfs/debug.c for why this * is currently disabled, and what needs to be done to * re-enable it. */ if (disk.d_ufs == 1 && cfg_lv & 0x080) DBG_DUMP_SPTBL(&sblock, dbg_line, &acg); #endif } } if (cfg_lv & 0x300) { /* Dump the requested inode(s) */ if (cfg_in != -2) DUMP_WHOLE_INODE((ino_t)cfg_in, cfg_lv); else { for (in = cg_start * sblock.fs_ipg; in < (ino_t)cg_stop * sblock.fs_ipg; in++) DUMP_WHOLE_INODE(in, cfg_lv); } } DBG_CLOSE; DBG_LEAVE; return 0; } /* ********************************************** dump_whole_ufs1_inode ***** */ /* * Here we dump a list of all blocks allocated by this inode. We follow * all indirect blocks. */ void dump_whole_ufs1_inode(ino_t inode, int level) { DBG_FUNC("dump_whole_ufs1_inode") union dinodep dp; int rb; unsigned int ind2ctr, ind3ctr; ufs1_daddr_t *ind2ptr, *ind3ptr; char comment[80]; DBG_ENTER; /* * Read the inode from disk/cache. */ if (getinode(&disk, &dp, inode) == -1) err(1, "getinode: %s", disk.d_error); if (dp.dp1->di_nlink == 0) { DBG_LEAVE; return; /* inode not in use */ } /* * Dump the main inode structure. */ snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode); if (level & 0x100) { DBG_DUMP_INO(&sblock, comment, dp.dp1); } if (!(level & 0x200)) { DBG_LEAVE; return; } /* * Ok, now prepare for dumping all direct and indirect pointers. */ rb = howmany(dp.dp1->di_size, sblock.fs_bsize) - UFS_NDADDR; if (rb > 0) { /* * Dump single indirect block. */ if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[0]), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0", (uintmax_t)inode); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); } if (rb > 0) { /* * Dump double indirect blocks. */ if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[1]), (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1", (uintmax_t)inode); DBG_DUMP_IBLK(&sblock, comment, i2blk, howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))); for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))) && (rb > 0)); ind2ctr++) { ind2ptr = &((ufs1_daddr_t *)(void *)&i2blk)[ind2ctr]; if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1->%d", (uintmax_t)inode, ind2ctr); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); } } if (rb > 0) { /* * Dump triple indirect blocks. */ if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[2]), (void *)&i3blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2", (uintmax_t)inode); #define SQUARE(a) ((a)*(a)) DBG_DUMP_IBLK(&sblock, comment, i3blk, howmany(rb, SQUARE(howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))))); #undef SQUARE for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))) && (rb > 0)); ind3ctr++) { ind3ptr = &((ufs1_daddr_t *)(void *)&i3blk)[ind3ctr]; if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2->%d", (uintmax_t)inode, ind3ctr); DBG_DUMP_IBLK(&sblock, comment, i2blk, howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))); for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))) && (rb > 0)); ind2ctr++) { ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk) [ind2ctr]; if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2->%d->%d", (uintmax_t)inode, ind3ctr, ind3ctr); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); } } } DBG_LEAVE; return; } /* ********************************************** dump_whole_ufs2_inode ***** */ /* * Here we dump a list of all blocks allocated by this inode. We follow * all indirect blocks. */ void dump_whole_ufs2_inode(ino_t inode, int level) { DBG_FUNC("dump_whole_ufs2_inode") union dinodep dp; int rb; unsigned int ind2ctr, ind3ctr; ufs2_daddr_t *ind2ptr, *ind3ptr; char comment[80]; DBG_ENTER; /* * Read the inode from disk/cache. */ if (getinode(&disk, &dp, inode) == -1) err(1, "getinode: %s", disk.d_error); if (dp.dp2->di_nlink == 0) { DBG_LEAVE; return; /* inode not in use */ } /* * Dump the main inode structure. */ snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode); if (level & 0x100) { DBG_DUMP_INO(&sblock, comment, dp.dp2); } if (!(level & 0x200)) { DBG_LEAVE; return; } /* * Ok, now prepare for dumping all direct and indirect pointers. */ rb = howmany(dp.dp2->di_size, sblock.fs_bsize) - UFS_NDADDR; if (rb > 0) { /* * Dump single indirect block. */ if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[0]), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0", (uintmax_t)inode); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); } if (rb > 0) { /* * Dump double indirect blocks. */ if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[1]), (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1", (uintmax_t)inode); DBG_DUMP_IBLK(&sblock, comment, i2blk, howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))); for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))) && (rb>0)); ind2ctr++) { ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk)[ind2ctr]; if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1->%d", (uintmax_t)inode, ind2ctr); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); } } if (rb > 0) { /* * Dump triple indirect blocks. */ if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[2]), (void *)&i3blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2", (uintmax_t)inode); #define SQUARE(a) ((a)*(a)) DBG_DUMP_IBLK(&sblock, comment, i3blk, howmany(rb, SQUARE(howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))))); #undef SQUARE for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))) && (rb > 0)); ind3ctr++) { ind3ptr = &((ufs2_daddr_t *)(void *)&i3blk)[ind3ctr]; if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2->%d", (uintmax_t)inode, ind3ctr); DBG_DUMP_IBLK(&sblock, comment, i2blk, howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))); for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))) && (rb > 0)); ind2ctr++) { ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk) [ind2ctr]; if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { err(1, "bread: %s", disk.d_error); } snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2->%d->%d", (uintmax_t)inode, ind3ctr, ind3ctr); DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); } } } DBG_LEAVE; return; } /* ************************************************************* usage ***** */ /* * Dump a line of usage. */ void usage(void) { DBG_FUNC("usage") DBG_ENTER; fprintf(stderr, "usage: ffsinfo [-g cylinder_group] [-i inode] [-l level] " "[-o outfile]\n" " special | file\n"); DBG_LEAVE; exit(1); } diff --git a/sbin/fsck_msdosfs/boot.c b/sbin/fsck_msdosfs/boot.c index 3d1657ad66f3..f91609470ad7 100644 --- a/sbin/fsck_msdosfs/boot.c +++ b/sbin/fsck_msdosfs/boot.c @@ -1,376 +1,374 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1995, 1997 Wolfgang Solfrank * Copyright (c) 1995 Martin Husemann * * 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 AUTHORS ``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 AUTHORS 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. */ #include #ifndef lint __RCSID("$NetBSD: boot.c,v 1.22 2020/01/11 16:29:07 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include "ext.h" #include "fsutil.h" int readboot(int dosfs, struct bootblock *boot) { u_char block[DOSBOOTBLOCKSIZE]; u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; int ret = FSOK; if ((size_t)read(dosfs, block, sizeof block) != sizeof block) { perr("could not read boot block"); return FSFATAL; } if (block[510] != 0x55 || block[511] != 0xaa) { pfatal("Invalid signature in boot block: %02x%02x", block[511], block[510]); return FSFATAL; } memset(boot, 0, sizeof *boot); boot->ValidFat = -1; /* Decode BIOS Parameter Block */ /* Bytes per sector: can only be 512, 1024, 2048 and 4096. */ boot->bpbBytesPerSec = block[11] + (block[12] << 8); if (boot->bpbBytesPerSec < DOSBOOTBLOCKSIZE_REAL || boot->bpbBytesPerSec > DOSBOOTBLOCKSIZE || !powerof2(boot->bpbBytesPerSec)) { pfatal("Invalid sector size: %u", boot->bpbBytesPerSec); return FSFATAL; } /* Sectors per cluster: can only be: 1, 2, 4, 8, 16, 32, 64, 128. */ boot->bpbSecPerClust = block[13]; if (boot->bpbSecPerClust == 0 || !powerof2(boot->bpbSecPerClust)) { pfatal("Invalid cluster size: %u", boot->bpbSecPerClust); return FSFATAL; } /* Reserved sectors: must be non-zero */ boot->bpbResSectors = block[14] + (block[15] << 8); if (boot->bpbResSectors < 1) { pfatal("Invalid reserved sectors: %u", boot->bpbResSectors); return FSFATAL; } /* Number of FATs */ boot->bpbFATs = block[16]; if (boot->bpbFATs == 0) { pfatal("Invalid number of FATs: %u", boot->bpbFATs); return FSFATAL; } /* Root directory entries for FAT12 and FAT16 */ boot->bpbRootDirEnts = block[17] + (block[18] << 8); if (!boot->bpbRootDirEnts) { /* bpbRootDirEnts = 0 suggests that we are FAT32 */ boot->flags |= FAT32; } /* Total sectors (16 bits) */ boot->bpbSectors = block[19] + (block[20] << 8); if (boot->bpbSectors != 0 && (boot->flags & FAT32)) { pfatal("Invalid 16-bit total sector count on FAT32: %u", boot->bpbSectors); return FSFATAL; } /* Media type: ignored */ boot->bpbMedia = block[21]; /* FAT12/FAT16: 16-bit count of sectors per FAT */ boot->bpbFATsmall = block[22] + (block[23] << 8); if (boot->bpbFATsmall != 0 && (boot->flags & FAT32)) { pfatal("Invalid 16-bit FAT sector count on FAT32: %u", boot->bpbFATsmall); return FSFATAL; } /* Legacy CHS geometry numbers: ignored */ boot->SecPerTrack = block[24] + (block[25] << 8); boot->bpbHeads = block[26] + (block[27] << 8); /* Hidden sectors: ignored */ boot->bpbHiddenSecs = block[28] + (block[29] << 8) + (block[30] << 16) + (block[31] << 24); /* Total sectors (32 bits) */ boot->bpbHugeSectors = block[32] + (block[33] << 8) + (block[34] << 16) + (block[35] << 24); if (boot->bpbHugeSectors == 0) { if (boot->flags & FAT32) { pfatal("FAT32 with sector count of zero"); return FSFATAL; } else if (boot->bpbSectors == 0) { pfatal("FAT with sector count of zero"); return FSFATAL; } boot->NumSectors = boot->bpbSectors; } else { if (boot->bpbSectors != 0) { pfatal("Invalid FAT sector count"); return FSFATAL; } boot->NumSectors = boot->bpbHugeSectors; } if (boot->flags & FAT32) { /* If the OEM Name field is EXFAT, it's not FAT32, so bail */ if (!memcmp(&block[3], "EXFAT ", 8)) { pfatal("exFAT filesystem is not supported."); return FSFATAL; } /* 32-bit count of sectors per FAT */ boot->FATsecs = block[36] + (block[37] << 8) + (block[38] << 16) + (block[39] << 24); if (block[40] & 0x80) boot->ValidFat = block[40] & 0x0f; /* FAT32 version, bail out if not 0.0 */ if (block[42] || block[43]) { pfatal("Unknown file system version: %x.%x", block[43], block[42]); return FSFATAL; } /* * Cluster number of the first cluster of root directory. * * Should be 2 but do not require it. */ boot->bpbRootClust = block[44] + (block[45] << 8) + (block[46] << 16) + (block[47] << 24); /* Sector number of the FSInfo structure, usually 1 */ boot->bpbFSInfo = block[48] + (block[49] << 8); /* Sector number of the backup boot block, ignored */ boot->bpbBackup = block[50] + (block[51] << 8); /* Check basic parameters */ if (boot->bpbFSInfo == 0) { /* * Either the BIOS Parameter Block has been corrupted, * or this is not a FAT32 filesystem, most likely an * exFAT filesystem. */ pfatal("Invalid FAT32 Extended BIOS Parameter Block"); return FSFATAL; } /* Read in and verify the FSInfo block */ if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { perr("could not read fsinfo block"); return FSFATAL; } if (memcmp(fsinfo, "RRaA", 4) || memcmp(fsinfo + 0x1e4, "rrAa", 4) || fsinfo[0x1fc] || fsinfo[0x1fd] || fsinfo[0x1fe] != 0x55 || fsinfo[0x1ff] != 0xaa || fsinfo[0x3fc] || fsinfo[0x3fd] || fsinfo[0x3fe] != 0x55 || fsinfo[0x3ff] != 0xaa) { pwarn("Invalid signature in fsinfo block\n"); if (ask(0, "Fix")) { memcpy(fsinfo, "RRaA", 4); memcpy(fsinfo + 0x1e4, "rrAa", 4); fsinfo[0x1fc] = fsinfo[0x1fd] = 0; fsinfo[0x1fe] = 0x55; fsinfo[0x1ff] = 0xaa; fsinfo[0x3fc] = fsinfo[0x3fd] = 0; fsinfo[0x3fe] = 0x55; fsinfo[0x3ff] = 0xaa; if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec || write(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { perr("Unable to write bpbFSInfo"); return FSFATAL; } ret = FSBOOTMOD; } else boot->bpbFSInfo = 0; } else { /* We appear to have a valid FSInfo block, decode */ boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8) + (fsinfo[0x1ea] << 16) + (fsinfo[0x1eb] << 24); boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8) + (fsinfo[0x1ee] << 16) + (fsinfo[0x1ef] << 24); } } else { /* !FAT32: FAT12/FAT16 */ boot->FATsecs = boot->bpbFATsmall; } if (boot->FATsecs < 1 || boot->FATsecs > UINT32_MAX / boot->bpbFATs) { pfatal("Invalid FATs(%u) with FATsecs(%zu)", boot->bpbFATs, (size_t)boot->FATsecs); return FSFATAL; } boot->FirstCluster = (boot->bpbRootDirEnts * 32 + boot->bpbBytesPerSec - 1) / boot->bpbBytesPerSec + boot->bpbResSectors + boot->bpbFATs * boot->FATsecs; if (boot->FirstCluster + boot->bpbSecPerClust > boot->NumSectors) { pfatal("Cluster offset too large (%u clusters)\n", boot->FirstCluster); return FSFATAL; } /* * The number of clusters is derived from available data sectors, * divided by sectors per cluster. */ boot->NumClusters = (boot->NumSectors - boot->FirstCluster) / boot->bpbSecPerClust; if (boot->flags & FAT32) { if (boot->NumClusters > (CLUST_RSRVD & CLUST32_MASK)) { pfatal("Filesystem too big (%u clusters) for FAT32 partition", boot->NumClusters); return FSFATAL; } if (boot->NumClusters < (CLUST_RSRVD & CLUST16_MASK)) { pfatal("Filesystem too small (%u clusters) for FAT32 partition", boot->NumClusters); return FSFATAL; } boot->ClustMask = CLUST32_MASK; if (boot->bpbRootClust < CLUST_FIRST || boot->bpbRootClust >= boot->NumClusters) { pfatal("Root directory starts with cluster out of range(%u)", boot->bpbRootClust); return FSFATAL; } } else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK)) { boot->ClustMask = CLUST12_MASK; } else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK)) { boot->ClustMask = CLUST16_MASK; } else { pfatal("Filesystem too big (%u clusters) for non-FAT32 partition", boot->NumClusters); return FSFATAL; } switch (boot->ClustMask) { case CLUST32_MASK: boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 4; break; case CLUST16_MASK: boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 2; break; default: boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec * 2) / 3; break; } if (boot->NumFatEntries < boot->NumClusters) { pfatal("FAT size too small, %u entries won't fit into %u sectors\n", boot->NumClusters, boot->FATsecs); return FSFATAL; } /* * There are two reserved clusters. To avoid adding CLUST_FIRST every * time we perform boundary checks, we increment the NumClusters by 2, * which is CLUST_FIRST to denote the first out-of-range cluster number. */ boot->NumClusters += CLUST_FIRST; boot->ClusterSize = boot->bpbBytesPerSec * boot->bpbSecPerClust; boot->NumFiles = 1; boot->NumFree = 0; return ret; } int writefsinfo(int dosfs, struct bootblock *boot) { u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { perr("could not read fsinfo block"); return FSFATAL; } fsinfo[0x1e8] = (u_char)boot->FSFree; fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8); fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16); fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24); fsinfo[0x1ec] = (u_char)boot->FSNext; fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8); fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16); fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24); if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec || write(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { perr("Unable to write bpbFSInfo"); return FSFATAL; } /* * Technically, we should return FSBOOTMOD here. * * However, since Win95 OSR2 (the first M$ OS that has * support for FAT32) doesn't maintain the FSINFO block * correctly, it has to be fixed pretty often. * * Therefore, we handle the FSINFO block only informally, * fixing it if necessary, but otherwise ignoring the * fact that it was incorrect. */ return 0; } diff --git a/sbin/fsck_msdosfs/check.c b/sbin/fsck_msdosfs/check.c index 654ceeb9c5ca..f672a2ac515c 100644 --- a/sbin/fsck_msdosfs/check.c +++ b/sbin/fsck_msdosfs/check.c @@ -1,193 +1,191 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank * Copyright (c) 1995 Martin Husemann * * 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 AUTHORS ``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 AUTHORS 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. */ #include #ifndef lint __RCSID("$NetBSD: check.c,v 1.14 2006/06/05 16:51:18 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #ifdef HAVE_LIBUTIL_H #include #endif #include #include #include #include #include #include "ext.h" #include "fsutil.h" int checkfilesys(const char *fname) { int dosfs; struct bootblock boot; struct fat_descriptor *fat = NULL; int finish_dosdirsection=0; int mod = 0; int ret = 8; int64_t freebytes; int64_t badbytes; rdonly = alwaysno; if (!preen) printf("** %s", fname); dosfs = open(fname, rdonly ? O_RDONLY : O_RDWR, 0); if (dosfs < 0 && !rdonly) { dosfs = open(fname, O_RDONLY, 0); if (dosfs >= 0) pwarn(" (NO WRITE)\n"); else if (!preen) printf("\n"); rdonly = 1; } else if (!preen) printf("\n"); if (dosfs < 0) { perr("Can't open `%s'", fname); printf("\n"); return 8; } if (readboot(dosfs, &boot) == FSFATAL) { close(dosfs); printf("\n"); return 8; } if (skipclean && preen && checkdirty(dosfs, &boot)) { printf("%s: ", fname); printf("FILESYSTEM CLEAN; SKIPPING CHECKS\n"); ret = 0; goto out; } if (!preen) { printf("** Phase 1 - Read FAT and checking connectivity\n"); } mod |= readfat(dosfs, &boot, &fat); if (mod & FSFATAL) { close(dosfs); return 8; } if (!preen) printf("** Phase 2 - Checking Directories\n"); mod |= resetDosDirSection(fat); finish_dosdirsection = 1; if (mod & FSFATAL) goto out; /* delay writing FATs */ mod |= handleDirTree(fat); if (mod & FSFATAL) goto out; if (!preen) printf("** Phase 3 - Checking for Lost Files\n"); mod |= checklost(fat); if (mod & FSFATAL) goto out; /* now write the FATs */ if (mod & FSFATMOD) { if (ask(1, "Update FATs")) { mod |= writefat(fat); if (mod & FSFATAL) goto out; } else mod |= FSERROR; } freebytes = (int64_t)boot.NumFree * boot.ClusterSize; badbytes = (int64_t)boot.NumBad * boot.ClusterSize; #ifdef HAVE_LIBUTIL_H char freestr[7], badstr[7]; humanize_number(freestr, sizeof(freestr), freebytes, "", HN_AUTOSCALE, HN_DECIMAL | HN_IEC_PREFIXES); if (boot.NumBad) { humanize_number(badstr, sizeof(badstr), badbytes, "", HN_AUTOSCALE, HN_B | HN_DECIMAL | HN_IEC_PREFIXES); pwarn("%d files, %sB free (%d clusters), %sB bad (%d clusters)\n", boot.NumFiles, freestr, boot.NumFree, badstr, boot.NumBad); } else { pwarn("%d files, %sB free (%d clusters)\n", boot.NumFiles, freestr, boot.NumFree); } #else if (boot.NumBad) pwarn("%d files, %jd KiB free (%d clusters), %jd KiB bad (%d clusters)\n", boot.NumFiles, (intmax_t)freebytes / 1024, boot.NumFree, (intmax_t)badbytes / 1024, boot.NumBad); else pwarn("%d files, %jd KiB free (%d clusters)\n", boot.NumFiles, (intmax_t)freebytes / 1024, boot.NumFree); #endif if (mod && (mod & FSERROR) == 0) { if (mod & FSDIRTY) { if (ask(1, "MARK FILE SYSTEM CLEAN") == 0) mod &= ~FSDIRTY; if (mod & FSDIRTY) { pwarn("MARKING FILE SYSTEM CLEAN\n"); mod |= cleardirty(fat); } else { pwarn("\n***** FILE SYSTEM IS LEFT MARKED AS DIRTY *****\n"); mod |= FSERROR; /* file system not clean */ } } } if (mod & (FSFATAL | FSERROR)) goto out; ret = 0; out: if (finish_dosdirsection) finishDosDirSection(); free(fat); close(dosfs); if (mod & (FSFATMOD|FSDIRMOD)) pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n"); return ret; } diff --git a/sbin/fsck_msdosfs/dir.c b/sbin/fsck_msdosfs/dir.c index 02fe07880e0e..c60eaab59b12 100644 --- a/sbin/fsck_msdosfs/dir.c +++ b/sbin/fsck_msdosfs/dir.c @@ -1,1181 +1,1179 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Google LLC * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank * Copyright (c) 1995 Martin Husemann * Some structure declaration borrowed from Paul Popelka * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference. * * 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 AUTHORS ``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 AUTHORS 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. */ #include #ifndef lint __RCSID("$NetBSD: dir.c,v 1.20 2006/06/05 16:51:18 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include "ext.h" #include "fsutil.h" #define SLOT_EMPTY 0x00 /* slot has never been used */ #define SLOT_E5 0x05 /* the real value is 0xe5 */ #define SLOT_DELETED 0xe5 /* file in this slot deleted */ #define ATTR_NORMAL 0x00 /* normal file */ #define ATTR_READONLY 0x01 /* file is readonly */ #define ATTR_HIDDEN 0x02 /* file is hidden */ #define ATTR_SYSTEM 0x04 /* file is a system file */ #define ATTR_VOLUME 0x08 /* entry is a volume label */ #define ATTR_DIRECTORY 0x10 /* entry is a directory name */ #define ATTR_ARCHIVE 0x20 /* file is new or modified */ #define ATTR_WIN95 0x0f /* long name record */ /* * This is the format of the contents of the deTime field in the direntry * structure. * We don't use bitfields because we don't know how compilers for * arbitrary machines will lay them out. */ #define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ #define DT_2SECONDS_SHIFT 0 #define DT_MINUTES_MASK 0x7E0 /* minutes */ #define DT_MINUTES_SHIFT 5 #define DT_HOURS_MASK 0xF800 /* hours */ #define DT_HOURS_SHIFT 11 /* * This is the format of the contents of the deDate field in the direntry * structure. */ #define DD_DAY_MASK 0x1F /* day of month */ #define DD_DAY_SHIFT 0 #define DD_MONTH_MASK 0x1E0 /* month */ #define DD_MONTH_SHIFT 5 #define DD_YEAR_MASK 0xFE00 /* year - 1980 */ #define DD_YEAR_SHIFT 9 /* dir.c */ static struct dosDirEntry *newDosDirEntry(void); static void freeDosDirEntry(struct dosDirEntry *); static struct dirTodoNode *newDirTodo(void); static void freeDirTodo(struct dirTodoNode *); static char *fullpath(struct dosDirEntry *); static u_char calcShortSum(u_char *); static int delete(struct fat_descriptor *, cl_t, int, cl_t, int, int); static int removede(struct fat_descriptor *, u_char *, u_char *, cl_t, cl_t, cl_t, char *, int); static int checksize(struct fat_descriptor *, u_char *, struct dosDirEntry *); static int readDosDirSection(struct fat_descriptor *, struct dosDirEntry *); /* * Manage free dosDirEntry structures. */ static struct dosDirEntry *freede; static struct dosDirEntry * newDosDirEntry(void) { struct dosDirEntry *de; if (!(de = freede)) { if (!(de = malloc(sizeof *de))) return (NULL); } else freede = de->next; return de; } static void freeDosDirEntry(struct dosDirEntry *de) { de->next = freede; freede = de; } /* * The same for dirTodoNode structures. */ static struct dirTodoNode *freedt; static struct dirTodoNode * newDirTodo(void) { struct dirTodoNode *dt; if (!(dt = freedt)) { if (!(dt = malloc(sizeof *dt))) return 0; } else freedt = dt->next; return dt; } static void freeDirTodo(struct dirTodoNode *dt) { dt->next = freedt; freedt = dt; } /* * The stack of unread directories */ static struct dirTodoNode *pendingDirectories = NULL; /* * Return the full pathname for a directory entry. */ static char * fullpath(struct dosDirEntry *dir) { static char namebuf[MAXPATHLEN + 1]; char *cp, *np; int nl; cp = namebuf + sizeof namebuf; *--cp = '\0'; for(;;) { np = dir->lname[0] ? dir->lname : dir->name; nl = strlen(np); if (cp <= namebuf + 1 + nl) { *--cp = '?'; break; } cp -= nl; memcpy(cp, np, nl); dir = dir->parent; if (!dir) break; *--cp = '/'; } return cp; } /* * Calculate a checksum over an 8.3 alias name */ static inline u_char calcShortSum(u_char *p) { u_char sum = 0; int i; for (i = 0; i < 11; i++) { sum = (sum << 7)|(sum >> 1); /* rotate right */ sum += p[i]; } return sum; } /* * Global variables temporarily used during a directory scan */ static char longName[DOSLONGNAMELEN] = ""; static u_char *buffer = NULL; static u_char *delbuf = NULL; static struct dosDirEntry *rootDir; static struct dosDirEntry *lostDir; /* * Init internal state for a new directory scan. */ int resetDosDirSection(struct fat_descriptor *fat) { int rootdir_size, cluster_size; int ret = FSOK; size_t len; struct bootblock *boot; boot = fat_get_boot(fat); rootdir_size = boot->bpbRootDirEnts * 32; cluster_size = boot->bpbSecPerClust * boot->bpbBytesPerSec; if ((buffer = malloc(len = MAX(rootdir_size, cluster_size))) == NULL) { perr("No space for directory buffer (%zu)", len); return FSFATAL; } if ((delbuf = malloc(len = cluster_size)) == NULL) { free(buffer); perr("No space for directory delbuf (%zu)", len); return FSFATAL; } if ((rootDir = newDosDirEntry()) == NULL) { free(buffer); free(delbuf); perr("No space for directory entry"); return FSFATAL; } memset(rootDir, 0, sizeof *rootDir); if (boot->flags & FAT32) { if (!fat_is_cl_head(fat, boot->bpbRootClust)) { pfatal("Root directory doesn't start a cluster chain"); return FSFATAL; } rootDir->head = boot->bpbRootClust; } return ret; } /* * Cleanup after a directory scan */ void finishDosDirSection(void) { struct dirTodoNode *p, *np; struct dosDirEntry *d, *nd; for (p = pendingDirectories; p; p = np) { np = p->next; freeDirTodo(p); } pendingDirectories = NULL; for (d = rootDir; d; d = nd) { if ((nd = d->child) != NULL) { d->child = 0; continue; } if (!(nd = d->next)) nd = d->parent; freeDosDirEntry(d); } rootDir = lostDir = NULL; free(buffer); free(delbuf); buffer = NULL; delbuf = NULL; } /* * Delete directory entries between startcl, startoff and endcl, endoff. */ static int delete(struct fat_descriptor *fat, cl_t startcl, int startoff, cl_t endcl, int endoff, int notlast) { u_char *s, *e; off_t off; int clsz, fd; struct bootblock *boot; boot = fat_get_boot(fat); fd = fat_get_fd(fat); clsz = boot->bpbSecPerClust * boot->bpbBytesPerSec; s = delbuf + startoff; e = delbuf + clsz; while (fat_is_valid_cl(fat, startcl)) { if (startcl == endcl) { if (notlast) break; e = delbuf + endoff; } off = (startcl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster; off *= boot->bpbBytesPerSec; if (lseek(fd, off, SEEK_SET) != off) { perr("Unable to lseek to %" PRId64, off); return FSFATAL; } if (read(fd, delbuf, clsz) != clsz) { perr("Unable to read directory"); return FSFATAL; } while (s < e) { *s = SLOT_DELETED; s += 32; } if (lseek(fd, off, SEEK_SET) != off) { perr("Unable to lseek to %" PRId64, off); return FSFATAL; } if (write(fd, delbuf, clsz) != clsz) { perr("Unable to write directory"); return FSFATAL; } if (startcl == endcl) break; startcl = fat_get_cl_next(fat, startcl); s = delbuf; } return FSOK; } static int removede(struct fat_descriptor *fat, u_char *start, u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, char *path, int type) { switch (type) { case 0: pwarn("Invalid long filename entry for %s\n", path); break; case 1: pwarn("Invalid long filename entry at end of directory %s\n", path); break; case 2: pwarn("Invalid long filename entry for volume label\n"); break; } if (ask(0, "Remove")) { if (startcl != curcl) { if (delete(fat, startcl, start - buffer, endcl, end - buffer, endcl == curcl) == FSFATAL) return FSFATAL; start = buffer; } /* startcl is < CLUST_FIRST for !FAT32 root */ if ((endcl == curcl) || (startcl < CLUST_FIRST)) for (; start < end; start += 32) *start = SLOT_DELETED; return FSDIRMOD; } return FSERROR; } /* * Check an in-memory file entry */ static int checksize(struct fat_descriptor *fat, u_char *p, struct dosDirEntry *dir) { int ret = FSOK; size_t chainsize; u_int64_t physicalSize; struct bootblock *boot; boot = fat_get_boot(fat); /* * Check size on ordinary files */ if (dir->head == CLUST_FREE) { physicalSize = 0; } else { if (!fat_is_valid_cl(fat, dir->head) || !fat_is_cl_head(fat, dir->head)) { pwarn("Directory entry %s of size %u referencing invalid cluster %u\n", fullpath(dir), dir->size, dir->head); if (ask(1, "Truncate")) { p[28] = p[29] = p[30] = p[31] = 0; p[26] = p[27] = 0; if (boot->ClustMask == CLUST32_MASK) p[20] = p[21] = 0; dir->size = 0; dir->head = CLUST_FREE; return FSDIRMOD; } else { return FSERROR; } } ret = checkchain(fat, dir->head, &chainsize); /* * Upon return, chainsize would hold the chain length * that checkchain() was able to validate, but if the user * refused the proposed repair, it would be unsafe to * proceed with directory entry fix, so bail out in that * case. */ if (ret == FSERROR) { return (FSERROR); } /* * The maximum file size on FAT32 is 4GiB - 1, which * will occupy a cluster chain of exactly 4GiB in * size. On 32-bit platforms, since size_t is 32-bit, * it would wrap back to 0. */ physicalSize = (u_int64_t)chainsize * boot->ClusterSize; } if (physicalSize < dir->size) { pwarn("size of %s is %u, should at most be %ju\n", fullpath(dir), dir->size, (uintmax_t)physicalSize); if (ask(1, "Truncate")) { dir->size = physicalSize; p[28] = (u_char)physicalSize; p[29] = (u_char)(physicalSize >> 8); p[30] = (u_char)(physicalSize >> 16); p[31] = (u_char)(physicalSize >> 24); return FSDIRMOD; } else return FSERROR; } else if (physicalSize - dir->size >= boot->ClusterSize) { pwarn("%s has too many clusters allocated\n", fullpath(dir)); if (ask(1, "Drop superfluous clusters")) { cl_t cl; u_int32_t sz, len; for (cl = dir->head, len = sz = 0; (sz += boot->ClusterSize) < dir->size; len++) cl = fat_get_cl_next(fat, cl); clearchain(fat, fat_get_cl_next(fat, cl)); ret = fat_set_cl_next(fat, cl, CLUST_EOF); return (FSFATMOD | ret); } else return FSERROR; } return FSOK; } static const u_char dot_name[11] = ". "; static const u_char dotdot_name[11] = ".. "; /* * Basic sanity check if the subdirectory have good '.' and '..' entries, * and they are directory entries. Further sanity checks are performed * when we traverse into it. */ static int check_subdirectory(struct fat_descriptor *fat, struct dosDirEntry *dir) { u_char *buf, *cp; off_t off; cl_t cl; int retval = FSOK; int fd; struct bootblock *boot; boot = fat_get_boot(fat); fd = fat_get_fd(fat); cl = dir->head; if (dir->parent && !fat_is_valid_cl(fat, cl)) { return FSERROR; } if (!(boot->flags & FAT32) && !dir->parent) { off = boot->bpbResSectors + boot->bpbFATs * boot->FATsecs; } else { off = (cl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster; } /* * We only need to check the first two entries of the directory, * which is found in the first sector of the directory entry, * so read in only the first sector. */ buf = malloc(boot->bpbBytesPerSec); if (buf == NULL) { perr("No space for directory buffer (%u)", boot->bpbBytesPerSec); return FSFATAL; } off *= boot->bpbBytesPerSec; if (lseek(fd, off, SEEK_SET) != off || read(fd, buf, boot->bpbBytesPerSec) != (ssize_t)boot->bpbBytesPerSec) { perr("Unable to read directory"); free(buf); return FSFATAL; } /* * Both `.' and `..' must be present and be the first two entries * and be ATTR_DIRECTORY of a valid subdirectory. */ cp = buf; if (memcmp(cp, dot_name, sizeof(dot_name)) != 0 || (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) { pwarn("%s: Incorrect `.' for %s.\n", __func__, dir->name); retval |= FSERROR; } cp += 32; if (memcmp(cp, dotdot_name, sizeof(dotdot_name)) != 0 || (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) { pwarn("%s: Incorrect `..' for %s. \n", __func__, dir->name); retval |= FSERROR; } free(buf); return retval; } /* * Read a directory and * - resolve long name records * - enter file and directory records into the parent's list * - push directories onto the todo-stack */ static int readDosDirSection(struct fat_descriptor *fat, struct dosDirEntry *dir) { struct bootblock *boot; struct dosDirEntry dirent, *d; u_char *p, *vallfn, *invlfn, *empty; off_t off; int fd, i, j, k, iosize, entries; bool is_legacyroot; cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0; char *t; u_int lidx = 0; int shortSum; int mod = FSOK; size_t dirclusters; #define THISMOD 0x8000 /* Only used within this routine */ boot = fat_get_boot(fat); fd = fat_get_fd(fat); cl = dir->head; if (dir->parent && (!fat_is_valid_cl(fat, cl))) { /* * Already handled somewhere else. */ return FSOK; } shortSum = -1; vallfn = invlfn = empty = NULL; /* * If we are checking the legacy root (for FAT12/FAT16), * we will operate on the whole directory; otherwise, we * will operate on one cluster at a time, and also take * this opportunity to examine the chain. * * Derive how many entries we are going to encounter from * the I/O size. */ is_legacyroot = (dir->parent == NULL && !(boot->flags & FAT32)); if (is_legacyroot) { iosize = boot->bpbRootDirEnts * 32; entries = boot->bpbRootDirEnts; } else { iosize = boot->bpbSecPerClust * boot->bpbBytesPerSec; entries = iosize / 32; mod |= checkchain(fat, dir->head, &dirclusters); } do { if (is_legacyroot) { /* * Special case for FAT12/FAT16 root -- read * in the whole root directory. */ off = boot->bpbResSectors + boot->bpbFATs * boot->FATsecs; } else { /* * Otherwise, read in a cluster of the * directory. */ off = (cl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster; } off *= boot->bpbBytesPerSec; if (lseek(fd, off, SEEK_SET) != off || read(fd, buffer, iosize) != iosize) { perr("Unable to read directory"); return FSFATAL; } for (p = buffer, i = 0; i < entries; i++, p += 32) { if (dir->fsckflags & DIREMPWARN) { *p = SLOT_EMPTY; continue; } if (*p == SLOT_EMPTY || *p == SLOT_DELETED) { if (*p == SLOT_EMPTY) { dir->fsckflags |= DIREMPTY; empty = p; empcl = cl; } continue; } if (dir->fsckflags & DIREMPTY) { if (!(dir->fsckflags & DIREMPWARN)) { pwarn("%s has entries after end of directory\n", fullpath(dir)); if (ask(1, "Extend")) { u_char *q; dir->fsckflags &= ~DIREMPTY; if (delete(fat, empcl, empty - buffer, cl, p - buffer, 1) == FSFATAL) return FSFATAL; q = ((empcl == cl) ? empty : buffer); assert(q != NULL); for (; q < p; q += 32) *q = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; } else if (ask(0, "Truncate")) dir->fsckflags |= DIREMPWARN; } if (dir->fsckflags & DIREMPWARN) { *p = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; continue; } else if (dir->fsckflags & DIREMPTY) mod |= FSERROR; empty = NULL; } if (p[11] == ATTR_WIN95) { if (*p & LRFIRST) { if (shortSum != -1) { if (!invlfn) { invlfn = vallfn; invcl = valcl; } } memset(longName, 0, sizeof longName); shortSum = p[13]; vallfn = p; valcl = cl; } else if (shortSum != p[13] || lidx != (*p & LRNOMASK)) { if (!invlfn) { invlfn = vallfn; invcl = valcl; } if (!invlfn) { invlfn = p; invcl = cl; } vallfn = NULL; } lidx = *p & LRNOMASK; if (lidx == 0) { pwarn("invalid long name\n"); if (!invlfn) { invlfn = vallfn; invcl = valcl; } vallfn = NULL; continue; } t = longName + --lidx * 13; for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) { if (!p[k] && !p[k + 1]) break; *t++ = p[k]; /* * Warn about those unusable chars in msdosfs here? XXX */ if (p[k + 1]) t[-1] = '?'; } if (k >= 11) for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) { if (!p[k] && !p[k + 1]) break; *t++ = p[k]; if (p[k + 1]) t[-1] = '?'; } if (k >= 26) for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) { if (!p[k] && !p[k + 1]) break; *t++ = p[k]; if (p[k + 1]) t[-1] = '?'; } if (t >= longName + sizeof(longName)) { pwarn("long filename too long\n"); if (!invlfn) { invlfn = vallfn; invcl = valcl; } vallfn = NULL; } if (p[26] | (p[27] << 8)) { pwarn("long filename record cluster start != 0\n"); if (!invlfn) { invlfn = vallfn; invcl = cl; } vallfn = NULL; } continue; /* long records don't carry further * information */ } /* * This is a standard msdosfs directory entry. */ memset(&dirent, 0, sizeof dirent); /* * it's a short name record, but we need to know * more, so get the flags first. */ dirent.flags = p[11]; /* * Translate from 850 to ISO here XXX */ for (j = 0; j < 8; j++) dirent.name[j] = p[j]; dirent.name[8] = '\0'; for (k = 7; k >= 0 && dirent.name[k] == ' '; k--) dirent.name[k] = '\0'; if (k < 0 || dirent.name[k] != '\0') k++; if (dirent.name[0] == SLOT_E5) dirent.name[0] = 0xe5; if (dirent.flags & ATTR_VOLUME) { if (vallfn || invlfn) { mod |= removede(fat, invlfn ? invlfn : vallfn, p, invlfn ? invcl : valcl, -1, 0, fullpath(dir), 2); vallfn = NULL; invlfn = NULL; } continue; } if (p[8] != ' ') dirent.name[k++] = '.'; for (j = 0; j < 3; j++) dirent.name[k++] = p[j+8]; dirent.name[k] = '\0'; for (k--; k >= 0 && dirent.name[k] == ' '; k--) dirent.name[k] = '\0'; if (vallfn && shortSum != calcShortSum(p)) { if (!invlfn) { invlfn = vallfn; invcl = valcl; } vallfn = NULL; } dirent.head = p[26] | (p[27] << 8); if (boot->ClustMask == CLUST32_MASK) dirent.head |= (p[20] << 16) | (p[21] << 24); dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24); if (vallfn) { strlcpy(dirent.lname, longName, sizeof(dirent.lname)); longName[0] = '\0'; shortSum = -1; } dirent.parent = dir; dirent.next = dir->child; if (invlfn) { mod |= k = removede(fat, invlfn, vallfn ? vallfn : p, invcl, vallfn ? valcl : cl, cl, fullpath(&dirent), 0); if (mod & FSFATAL) return FSFATAL; if (vallfn ? (valcl == cl && vallfn != buffer) : p != buffer) if (k & FSDIRMOD) mod |= THISMOD; } vallfn = NULL; /* not used any longer */ invlfn = NULL; /* * Check if the directory entry is sane. * * '.' and '..' are skipped, their sanity is * checked somewhere else. * * For everything else, check if we have a new, * valid cluster chain (beginning of a file or * directory that was never previously claimed * by another file) when it's a non-empty file * or a directory. The sanity of the cluster * chain is checked at a later time when we * traverse into the directory, or examine the * file's directory entry. * * The only possible fix is to delete the entry * if it's a directory; for file, we have to * truncate the size to 0. */ if (!(dirent.flags & ATTR_DIRECTORY) || (strcmp(dirent.name, ".") != 0 && strcmp(dirent.name, "..") != 0)) { if ((dirent.size != 0 || (dirent.flags & ATTR_DIRECTORY)) && ((!fat_is_valid_cl(fat, dirent.head) || !fat_is_cl_head(fat, dirent.head)))) { if (!fat_is_valid_cl(fat, dirent.head)) { pwarn("%s starts with cluster out of range(%u)\n", fullpath(&dirent), dirent.head); } else { pwarn("%s doesn't start a new cluster chain\n", fullpath(&dirent)); } if (dirent.flags & ATTR_DIRECTORY) { if (ask(0, "Remove")) { *p = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; continue; } else { if (ask(1, "Truncate")) { p[28] = p[29] = p[30] = p[31] = 0; p[26] = p[27] = 0; if (boot->ClustMask == CLUST32_MASK) p[20] = p[21] = 0; dirent.size = 0; dirent.head = 0; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; } } } if (dirent.flags & ATTR_DIRECTORY) { /* * gather more info for directories */ struct dirTodoNode *n; if (dirent.size) { pwarn("Directory %s has size != 0\n", fullpath(&dirent)); if (ask(1, "Correct")) { p[28] = p[29] = p[30] = p[31] = 0; dirent.size = 0; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; } /* * handle `.' and `..' specially */ if (strcmp(dirent.name, ".") == 0) { if (dirent.head != dir->head) { pwarn("`.' entry in %s has incorrect start cluster\n", fullpath(dir)); if (ask(1, "Correct")) { dirent.head = dir->head; p[26] = (u_char)dirent.head; p[27] = (u_char)(dirent.head >> 8); if (boot->ClustMask == CLUST32_MASK) { p[20] = (u_char)(dirent.head >> 16); p[21] = (u_char)(dirent.head >> 24); } mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; } continue; } else if (strcmp(dirent.name, "..") == 0) { if (dir->parent) { /* XXX */ if (!dir->parent->parent) { if (dirent.head) { pwarn("`..' entry in %s has non-zero start cluster\n", fullpath(dir)); if (ask(1, "Correct")) { dirent.head = 0; p[26] = p[27] = 0; if (boot->ClustMask == CLUST32_MASK) p[20] = p[21] = 0; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; } } else if (dirent.head != dir->parent->head) { pwarn("`..' entry in %s has incorrect start cluster\n", fullpath(dir)); if (ask(1, "Correct")) { dirent.head = dir->parent->head; p[26] = (u_char)dirent.head; p[27] = (u_char)(dirent.head >> 8); if (boot->ClustMask == CLUST32_MASK) { p[20] = (u_char)(dirent.head >> 16); p[21] = (u_char)(dirent.head >> 24); } mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; } } continue; } else { /* * Only one directory entry can point * to dir->head, it's '.'. */ if (dirent.head == dir->head) { pwarn("%s entry in %s has incorrect start cluster\n", dirent.name, fullpath(dir)); if (ask(1, "Remove")) { *p = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; continue; } else if ((check_subdirectory(fat, &dirent) & FSERROR) == FSERROR) { /* * A subdirectory should have * a dot (.) entry and a dot-dot * (..) entry of ATTR_DIRECTORY, * we will inspect further when * traversing into it. */ if (ask(1, "Remove")) { *p = SLOT_DELETED; mod |= THISMOD|FSDIRMOD; } else mod |= FSERROR; continue; } } /* create directory tree node */ if (!(d = newDosDirEntry())) { perr("No space for directory"); return FSFATAL; } memcpy(d, &dirent, sizeof(struct dosDirEntry)); /* link it into the tree */ dir->child = d; /* Enter this directory into the todo list */ if (!(n = newDirTodo())) { perr("No space for todo list"); return FSFATAL; } n->next = pendingDirectories; n->dir = d; pendingDirectories = n; } else { mod |= k = checksize(fat, p, &dirent); if (k & FSDIRMOD) mod |= THISMOD; } boot->NumFiles++; } if (is_legacyroot) { /* * Don't bother to write back right now because * we may continue to make modification to the * non-FAT32 root directory below. */ break; } else if (mod & THISMOD) { if (lseek(fd, off, SEEK_SET) != off || write(fd, buffer, iosize) != iosize) { perr("Unable to write directory"); return FSFATAL; } mod &= ~THISMOD; } } while (fat_is_valid_cl(fat, (cl = fat_get_cl_next(fat, cl)))); if (invlfn || vallfn) mod |= removede(fat, invlfn ? invlfn : vallfn, p, invlfn ? invcl : valcl, -1, 0, fullpath(dir), 1); /* * The root directory of non-FAT32 filesystems is in a special * area and may have been modified above removede() without * being written out. */ if ((mod & FSDIRMOD) && is_legacyroot) { if (lseek(fd, off, SEEK_SET) != off || write(fd, buffer, iosize) != iosize) { perr("Unable to write directory"); return FSFATAL; } mod &= ~THISMOD; } return mod & ~THISMOD; } int handleDirTree(struct fat_descriptor *fat) { int mod; mod = readDosDirSection(fat, rootDir); if (mod & FSFATAL) return FSFATAL; /* * process the directory todo list */ while (pendingDirectories) { struct dosDirEntry *dir = pendingDirectories->dir; struct dirTodoNode *n = pendingDirectories->next; /* * remove TODO entry now, the list might change during * directory reads */ freeDirTodo(pendingDirectories); pendingDirectories = n; /* * handle subdirectory */ mod |= readDosDirSection(fat, dir); if (mod & FSFATAL) return FSFATAL; } return mod; } /* * Try to reconnect a FAT chain into dir */ static u_char *lfbuf; static cl_t lfcl; static off_t lfoff; int reconnect(struct fat_descriptor *fat, cl_t head, size_t length) { struct bootblock *boot = fat_get_boot(fat); struct dosDirEntry d; int len, dosfs; u_char *p; dosfs = fat_get_fd(fat); if (!ask(1, "Reconnect")) return FSERROR; if (!lostDir) { for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) { if (!strcmp(lostDir->name, LOSTDIR)) break; } if (!lostDir) { /* Create LOSTDIR? XXX */ pwarn("No %s directory\n", LOSTDIR); return FSERROR; } } if (!lfbuf) { lfbuf = malloc(boot->ClusterSize); if (!lfbuf) { perr("No space for buffer"); return FSFATAL; } p = NULL; } else p = lfbuf; while (1) { if (p) for (; p < lfbuf + boot->ClusterSize; p += 32) if (*p == SLOT_EMPTY || *p == SLOT_DELETED) break; if (p && p < lfbuf + boot->ClusterSize) break; lfcl = p ? fat_get_cl_next(fat, lfcl) : lostDir->head; if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) { /* Extend LOSTDIR? XXX */ pwarn("No space in %s\n", LOSTDIR); lfcl = (lostDir->head < boot->NumClusters) ? lostDir->head : 0; return FSERROR; } lfoff = (lfcl - CLUST_FIRST) * boot->ClusterSize + boot->FirstCluster * boot->bpbBytesPerSec; if (lseek(dosfs, lfoff, SEEK_SET) != lfoff || (size_t)read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { perr("could not read LOST.DIR"); return FSFATAL; } p = lfbuf; } boot->NumFiles++; /* Ensure uniqueness of entry here! XXX */ memset(&d, 0, sizeof d); /* worst case -1 = 4294967295, 10 digits */ len = snprintf(d.name, sizeof(d.name), "%u", head); d.flags = 0; d.head = head; d.size = length * boot->ClusterSize; memcpy(p, d.name, len); memset(p + len, ' ', 11 - len); memset(p + 11, 0, 32 - 11); p[26] = (u_char)d.head; p[27] = (u_char)(d.head >> 8); if (boot->ClustMask == CLUST32_MASK) { p[20] = (u_char)(d.head >> 16); p[21] = (u_char)(d.head >> 24); } p[28] = (u_char)d.size; p[29] = (u_char)(d.size >> 8); p[30] = (u_char)(d.size >> 16); p[31] = (u_char)(d.size >> 24); if (lseek(dosfs, lfoff, SEEK_SET) != lfoff || (size_t)write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { perr("could not write LOST.DIR"); return FSFATAL; } return FSDIRMOD; } void finishlf(void) { if (lfbuf) free(lfbuf); lfbuf = NULL; } diff --git a/sbin/fsck_msdosfs/fat.c b/sbin/fsck_msdosfs/fat.c index e35e2f27d305..567bfcd428cb 100644 --- a/sbin/fsck_msdosfs/fat.c +++ b/sbin/fsck_msdosfs/fat.c @@ -1,1328 +1,1326 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Google LLC * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank * Copyright (c) 1995 Martin Husemann * * 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 AUTHORS ``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 AUTHORS 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. */ #include #ifndef lint __RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ext.h" #include "fsutil.h" static int _readfat(struct fat_descriptor *); static inline struct bootblock* boot_of_(struct fat_descriptor *); static inline int fd_of_(struct fat_descriptor *); static inline bool valid_cl(struct fat_descriptor *, cl_t); /* * Head bitmap for FAT scanning. * * FAT32 have up to 2^28 = 256M entries, and FAT16/12 have much less. * For each cluster, we use 1 bit to represent if it's a head cluster * (the first cluster of a cluster chain). * * Head bitmap * =========== * Initially, we set all bits to 1. In readfat(), we traverse the * whole FAT and mark each cluster identified as "next" cluster as * 0. After the scan, we have a bitmap with 1's to indicate the * corresponding cluster was a "head" cluster. * * We use head bitmap to identify lost chains: a head cluster that was * not being claimed by any file or directories is the head cluster of * a lost chain. * * Handle of lost chains * ===================== * At the end of scanning, we can easily find all lost chain's heads * by finding out the 1's in the head bitmap. */ typedef struct long_bitmap { unsigned long *map; size_t count; /* Total set bits in the map */ } long_bitmap_t; static inline void bitmap_clear(long_bitmap_t *lbp, cl_t cl) { cl_t i = cl / LONG_BIT; unsigned long clearmask = ~(1UL << (cl % LONG_BIT)); assert((lbp->map[i] & ~clearmask) != 0); lbp->map[i] &= clearmask; lbp->count--; } static inline bool bitmap_get(long_bitmap_t *lbp, cl_t cl) { cl_t i = cl / LONG_BIT; unsigned long usedbit = 1UL << (cl % LONG_BIT); return ((lbp->map[i] & usedbit) == usedbit); } static inline bool bitmap_none_in_range(long_bitmap_t *lbp, cl_t cl) { cl_t i = cl / LONG_BIT; return (lbp->map[i] == 0); } static inline size_t bitmap_count(long_bitmap_t *lbp) { return (lbp->count); } static int bitmap_ctor(long_bitmap_t *lbp, size_t bits, bool allone) { size_t bitmap_size = roundup2(bits, LONG_BIT) / (LONG_BIT / 8); free(lbp->map); lbp->map = calloc(1, bitmap_size); if (lbp->map == NULL) return FSFATAL; if (allone) { memset(lbp->map, 0xff, bitmap_size); lbp->count = bits; } else { lbp->count = 0; } return FSOK; } static void bitmap_dtor(long_bitmap_t *lbp) { free(lbp->map); lbp->map = NULL; } /* * FAT32 can be as big as 256MiB (2^26 entries * 4 bytes), when we * can not ask the kernel to manage the access, use a simple LRU * cache with chunk size of 128 KiB to manage it. */ struct fat32_cache_entry { TAILQ_ENTRY(fat32_cache_entry) entries; uint8_t *chunk; /* pointer to chunk */ off_t addr; /* offset */ bool dirty; /* dirty bit */ }; static const size_t fat32_cache_chunk_size = 131072; /* MAXPHYS */ static const size_t fat32_cache_size = 4194304; static const size_t fat32_cache_entries = 32; /* XXXgcc: cache_size / cache_chunk_size */ /* * FAT table descriptor, represents a FAT table that is already loaded * into memory. */ struct fat_descriptor { struct bootblock *boot; uint8_t *fatbuf; cl_t (*get)(struct fat_descriptor *, cl_t); int (*set)(struct fat_descriptor *, cl_t, cl_t); long_bitmap_t headbitmap; int fd; bool is_mmapped; bool use_cache; size_t fatsize; size_t fat32_cached_chunks; TAILQ_HEAD(cachehead, fat32_cache_entry) fat32_cache_head; struct fat32_cache_entry *fat32_cache_allentries; off_t fat32_offset; off_t fat32_lastaddr; }; void fat_clear_cl_head(struct fat_descriptor *fat, cl_t cl) { bitmap_clear(&fat->headbitmap, cl); } bool fat_is_cl_head(struct fat_descriptor *fat, cl_t cl) { return (bitmap_get(&fat->headbitmap, cl)); } static inline bool fat_is_cl_head_in_range(struct fat_descriptor *fat, cl_t cl) { return (!(bitmap_none_in_range(&fat->headbitmap, cl))); } static size_t fat_get_head_count(struct fat_descriptor *fat) { return (bitmap_count(&fat->headbitmap)); } /* * FAT12 accessors. * * FAT12s are sufficiently small, expect it to always fit in the RAM. */ static inline uint8_t * fat_get_fat12_ptr(struct fat_descriptor *fat, cl_t cl) { return (fat->fatbuf + ((cl + (cl >> 1)))); } static cl_t fat_get_fat12_next(struct fat_descriptor *fat, cl_t cl) { const uint8_t *p; cl_t retval; p = fat_get_fat12_ptr(fat, cl); retval = le16dec(p); /* Odd cluster: lower 4 bits belongs to the subsequent cluster */ if ((cl & 1) == 1) retval >>= 4; retval &= CLUST12_MASK; if (retval >= (CLUST_BAD & CLUST12_MASK)) retval |= ~CLUST12_MASK; return (retval); } static int fat_set_fat12_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl) { uint8_t *p; /* Truncate 'nextcl' value, if needed */ nextcl &= CLUST12_MASK; p = fat_get_fat12_ptr(fat, cl); /* * Read in the 4 bits from the subsequent (for even clusters) * or the preceding (for odd clusters) cluster and combine * it to the nextcl value for encoding */ if ((cl & 1) == 0) { nextcl |= ((p[1] & 0xf0) << 8); } else { nextcl <<= 4; nextcl |= (p[0] & 0x0f); } le16enc(p, (uint16_t)nextcl); return (0); } /* * FAT16 accessors. * * FAT16s are sufficiently small, expect it to always fit in the RAM. */ static inline uint8_t * fat_get_fat16_ptr(struct fat_descriptor *fat, cl_t cl) { return (fat->fatbuf + (cl << 1)); } static cl_t fat_get_fat16_next(struct fat_descriptor *fat, cl_t cl) { const uint8_t *p; cl_t retval; p = fat_get_fat16_ptr(fat, cl); retval = le16dec(p) & CLUST16_MASK; if (retval >= (CLUST_BAD & CLUST16_MASK)) retval |= ~CLUST16_MASK; return (retval); } static int fat_set_fat16_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl) { uint8_t *p; /* Truncate 'nextcl' value, if needed */ nextcl &= CLUST16_MASK; p = fat_get_fat16_ptr(fat, cl); le16enc(p, (uint16_t)nextcl); return (0); } /* * FAT32 accessors. */ static inline uint8_t * fat_get_fat32_ptr(struct fat_descriptor *fat, cl_t cl) { return (fat->fatbuf + (cl << 2)); } static cl_t fat_get_fat32_next(struct fat_descriptor *fat, cl_t cl) { const uint8_t *p; cl_t retval; p = fat_get_fat32_ptr(fat, cl); retval = le32dec(p) & CLUST32_MASK; if (retval >= (CLUST_BAD & CLUST32_MASK)) retval |= ~CLUST32_MASK; return (retval); } static int fat_set_fat32_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl) { uint8_t *p; /* Truncate 'nextcl' value, if needed */ nextcl &= CLUST32_MASK; p = fat_get_fat32_ptr(fat, cl); le32enc(p, (uint32_t)nextcl); return (0); } static inline size_t fat_get_iosize(struct fat_descriptor *fat, off_t address) { if (address == fat->fat32_lastaddr) { return (fat->fatsize & ((off_t)fat32_cache_chunk_size - 1)); } else { return (fat32_cache_chunk_size); } } static int fat_flush_fat32_cache_entry(struct fat_descriptor *fat, struct fat32_cache_entry *entry) { int fd; off_t fat_addr; size_t writesize; fd = fd_of_(fat); if (!entry->dirty) return (FSOK); writesize = fat_get_iosize(fat, entry->addr); fat_addr = fat->fat32_offset + entry->addr; if (lseek(fd, fat_addr, SEEK_SET) != fat_addr || (size_t)write(fd, entry->chunk, writesize) != writesize) { pfatal("Unable to write FAT"); return (FSFATAL); } entry->dirty = false; return (FSOK); } static struct fat32_cache_entry * fat_get_fat32_cache_entry(struct fat_descriptor *fat, off_t addr, bool writing) { int fd; struct fat32_cache_entry *entry, *first; off_t fat_addr; size_t rwsize; addr &= ~(fat32_cache_chunk_size - 1); first = TAILQ_FIRST(&fat->fat32_cache_head); /* * Cache hit: if we already have the chunk, move it to list head */ TAILQ_FOREACH(entry, &fat->fat32_cache_head, entries) { if (entry->addr == addr) { if (writing) { entry->dirty = true; } if (entry != first) { TAILQ_REMOVE(&fat->fat32_cache_head, entry, entries); TAILQ_INSERT_HEAD(&fat->fat32_cache_head, entry, entries); } return (entry); } } /* * Cache miss: detach the chunk at tail of list, overwrite with * the located chunk, and populate with data from disk. */ entry = TAILQ_LAST(&fat->fat32_cache_head, cachehead); TAILQ_REMOVE(&fat->fat32_cache_head, entry, entries); if (fat_flush_fat32_cache_entry(fat, entry) != FSOK) { return (NULL); } rwsize = fat_get_iosize(fat, addr); fat_addr = fat->fat32_offset + addr; entry->addr = addr; fd = fd_of_(fat); if (lseek(fd, fat_addr, SEEK_SET) != fat_addr || (size_t)read(fd, entry->chunk, rwsize) != rwsize) { pfatal("Unable to read FAT"); return (NULL); } if (writing) { entry->dirty = true; } TAILQ_INSERT_HEAD(&fat->fat32_cache_head, entry, entries); return (entry); } static inline uint8_t * fat_get_fat32_cached_ptr(struct fat_descriptor *fat, cl_t cl, bool writing) { off_t addr, off; struct fat32_cache_entry *entry; addr = cl << 2; entry = fat_get_fat32_cache_entry(fat, addr, writing); if (entry != NULL) { off = addr & (fat32_cache_chunk_size - 1); return (entry->chunk + off); } else { return (NULL); } } static cl_t fat_get_fat32_cached_next(struct fat_descriptor *fat, cl_t cl) { const uint8_t *p; cl_t retval; p = fat_get_fat32_cached_ptr(fat, cl, false); if (p != NULL) { retval = le32dec(p) & CLUST32_MASK; if (retval >= (CLUST_BAD & CLUST32_MASK)) retval |= ~CLUST32_MASK; } else { retval = CLUST_DEAD; } return (retval); } static int fat_set_fat32_cached_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl) { uint8_t *p; /* Truncate 'nextcl' value, if needed */ nextcl &= CLUST32_MASK; p = fat_get_fat32_cached_ptr(fat, cl, true); if (p != NULL) { le32enc(p, (uint32_t)nextcl); return FSOK; } else { return FSFATAL; } } cl_t fat_get_cl_next(struct fat_descriptor *fat, cl_t cl) { if (!valid_cl(fat, cl)) { pfatal("Invalid cluster: %ud", cl); return CLUST_DEAD; } return (fat->get(fat, cl)); } int fat_set_cl_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl) { if (rdonly) { pwarn(" (NO WRITE)\n"); return FSFATAL; } if (!valid_cl(fat, cl)) { pfatal("Invalid cluster: %ud", cl); return FSFATAL; } return (fat->set(fat, cl, nextcl)); } static inline struct bootblock* boot_of_(struct fat_descriptor *fat) { return (fat->boot); } struct bootblock* fat_get_boot(struct fat_descriptor *fat) { return (boot_of_(fat)); } static inline int fd_of_(struct fat_descriptor *fat) { return (fat->fd); } int fat_get_fd(struct fat_descriptor * fat) { return (fd_of_(fat)); } /* * Whether a cl is in valid data range. */ bool fat_is_valid_cl(struct fat_descriptor *fat, cl_t cl) { return (valid_cl(fat, cl)); } static inline bool valid_cl(struct fat_descriptor *fat, cl_t cl) { const struct bootblock *boot = boot_of_(fat); return (cl >= CLUST_FIRST && cl < boot->NumClusters); } /* * The first 2 FAT entries contain pseudo-cluster numbers with the following * layout: * * 31...... ........ ........ .......0 * rrrr1111 11111111 11111111 mmmmmmmm FAT32 entry 0 * rrrrsh11 11111111 11111111 11111xxx FAT32 entry 1 * * 11111111 mmmmmmmm FAT16 entry 0 * sh111111 11111xxx FAT16 entry 1 * * r = reserved * m = BPB media ID byte * s = clean flag (1 = dismounted; 0 = still mounted) * h = hard error flag (1 = ok; 0 = I/O error) * x = any value ok */ int checkdirty(int fs, struct bootblock *boot) { off_t off; u_char *buffer; int ret = 0; size_t len; if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK) return 0; off = boot->bpbResSectors; off *= boot->bpbBytesPerSec; buffer = malloc(len = boot->bpbBytesPerSec); if (buffer == NULL) { perr("No space for FAT sectors (%zu)", len); return 1; } if (lseek(fs, off, SEEK_SET) != off) { perr("Unable to read FAT"); goto err; } if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) != boot->bpbBytesPerSec) { perr("Unable to read FAT"); goto err; } /* * If we don't understand the FAT, then the file system must be * assumed to be unclean. */ if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff) goto err; if (boot->ClustMask == CLUST16_MASK) { if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f) goto err; } else { if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03) goto err; } /* * Now check the actual clean flag (and the no-error flag). */ if (boot->ClustMask == CLUST16_MASK) { if ((buffer[3] & 0xc0) == 0xc0) ret = 1; } else { if ((buffer[7] & 0x0c) == 0x0c) ret = 1; } err: free(buffer); return ret; } int cleardirty(struct fat_descriptor *fat) { int fd, ret = FSERROR; struct bootblock *boot; u_char *buffer; size_t len; off_t off; boot = boot_of_(fat); fd = fd_of_(fat); if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK) return 0; off = boot->bpbResSectors; off *= boot->bpbBytesPerSec; buffer = malloc(len = boot->bpbBytesPerSec); if (buffer == NULL) { perr("No memory for FAT sectors (%zu)", len); return 1; } if ((size_t)pread(fd, buffer, len, off) != len) { perr("Unable to read FAT"); goto err; } if (boot->ClustMask == CLUST16_MASK) { buffer[3] |= 0x80; } else { buffer[7] |= 0x08; } if ((size_t)pwrite(fd, buffer, len, off) != len) { perr("Unable to write FAT"); goto err; } ret = FSOK; err: free(buffer); return ret; } /* * Read a FAT from disk. Returns 1 if successful, 0 otherwise. */ static int _readfat(struct fat_descriptor *fat) { int fd; size_t i; off_t off; size_t readsize; struct bootblock *boot; struct fat32_cache_entry *entry; boot = boot_of_(fat); fd = fd_of_(fat); fat->fatsize = boot->FATsecs * boot->bpbBytesPerSec; off = boot->bpbResSectors; off *= boot->bpbBytesPerSec; fat->is_mmapped = false; fat->use_cache = false; /* Attempt to mmap() first */ if (allow_mmap) { fat->fatbuf = mmap(NULL, fat->fatsize, PROT_READ | (rdonly ? 0 : PROT_WRITE), MAP_SHARED, fd_of_(fat), off); if (fat->fatbuf != MAP_FAILED) { fat->is_mmapped = true; return 1; } } /* * Unfortunately, we were unable to mmap(). * * Only use the cache manager when it's necessary, that is, * when the FAT is sufficiently large; in that case, only * read in the first 4 MiB of FAT into memory, and split the * buffer into chunks and insert to the LRU queue to populate * the cache with data. */ if (boot->ClustMask == CLUST32_MASK && fat->fatsize >= fat32_cache_size) { readsize = fat32_cache_size; fat->use_cache = true; fat->fat32_offset = boot->bpbResSectors * boot->bpbBytesPerSec; fat->fat32_lastaddr = fat->fatsize & ~(fat32_cache_chunk_size); } else { readsize = fat->fatsize; } fat->fatbuf = malloc(readsize); if (fat->fatbuf == NULL) { perr("No space for FAT (%zu)", readsize); return 0; } if (lseek(fd, off, SEEK_SET) != off) { perr("Unable to read FAT"); goto err; } if ((size_t)read(fd, fat->fatbuf, readsize) != readsize) { perr("Unable to read FAT"); goto err; } /* * When cache is used, split the buffer into chunks, and * connect the buffer into the cache. */ if (fat->use_cache) { TAILQ_INIT(&fat->fat32_cache_head); entry = calloc(fat32_cache_entries, sizeof(*entry)); if (entry == NULL) { perr("No space for FAT cache (%zu of %zu)", fat32_cache_entries, sizeof(entry)); goto err; } for (i = 0; i < fat32_cache_entries; i++) { entry[i].addr = fat32_cache_chunk_size * i; entry[i].chunk = &fat->fatbuf[entry[i].addr]; TAILQ_INSERT_TAIL(&fat->fat32_cache_head, &entry[i], entries); } fat->fat32_cache_allentries = entry; } return 1; err: free(fat->fatbuf); fat->fatbuf = NULL; return 0; } static void releasefat(struct fat_descriptor *fat) { if (fat->is_mmapped) { munmap(fat->fatbuf, fat->fatsize); } else { if (fat->use_cache) { free(fat->fat32_cache_allentries); fat->fat32_cache_allentries = NULL; } free(fat->fatbuf); } fat->fatbuf = NULL; bitmap_dtor(&fat->headbitmap); } /* * Read or map a FAT and populate head bitmap */ int readfat(int fs, struct bootblock *boot, struct fat_descriptor **fp) { struct fat_descriptor *fat; u_char *buffer, *p; cl_t cl, nextcl; int ret = FSOK; boot->NumFree = boot->NumBad = 0; fat = calloc(1, sizeof(struct fat_descriptor)); if (fat == NULL) { perr("No space for FAT descriptor"); return FSFATAL; } fat->fd = fs; fat->boot = boot; if (!_readfat(fat)) { free(fat); return FSFATAL; } buffer = fat->fatbuf; /* Populate accessors */ switch(boot->ClustMask) { case CLUST12_MASK: fat->get = fat_get_fat12_next; fat->set = fat_set_fat12_next; break; case CLUST16_MASK: fat->get = fat_get_fat16_next; fat->set = fat_set_fat16_next; break; case CLUST32_MASK: if (fat->is_mmapped || !fat->use_cache) { fat->get = fat_get_fat32_next; fat->set = fat_set_fat32_next; } else { fat->get = fat_get_fat32_cached_next; fat->set = fat_set_fat32_cached_next; } break; default: pfatal("Invalid ClustMask: %d", boot->ClustMask); releasefat(fat); free(fat); return FSFATAL; } if (bitmap_ctor(&fat->headbitmap, boot->NumClusters, true) != FSOK) { perr("No space for head bitmap for FAT clusters (%zu)", (size_t)boot->NumClusters); releasefat(fat); free(fat); return FSFATAL; } if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff || buffer[2] != 0xff || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) || (boot->ClustMask == CLUST32_MASK && ((buffer[3]&0x0f) != 0x0f || buffer[4] != 0xff || buffer[5] != 0xff || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { /* Windows 95 OSR2 (and possibly any later) changes * the FAT signature to 0xXXffff7f for FAT16 and to * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the * file system is dirty if it doesn't reboot cleanly. * Check this special condition before errorring out. */ if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff && buffer[2] == 0xff && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) || (boot->ClustMask == CLUST32_MASK && buffer[3] == 0x0f && buffer[4] == 0xff && buffer[5] == 0xff && buffer[6] == 0xff && buffer[7] == 0x07))) ret |= FSDIRTY; else { /* just some odd byte sequence in FAT */ switch (boot->ClustMask) { case CLUST32_MASK: pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", "FAT starts with odd byte sequence", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]); break; case CLUST16_MASK: pwarn("%s (%02x%02x%02x%02x)\n", "FAT starts with odd byte sequence", buffer[0], buffer[1], buffer[2], buffer[3]); break; default: pwarn("%s (%02x%02x%02x)\n", "FAT starts with odd byte sequence", buffer[0], buffer[1], buffer[2]); break; } if (ask(1, "Correct")) { ret |= FSFATMOD; p = buffer; *p++ = (u_char)boot->bpbMedia; *p++ = 0xff; *p++ = 0xff; switch (boot->ClustMask) { case CLUST16_MASK: *p++ = 0xff; break; case CLUST32_MASK: *p++ = 0x0f; *p++ = 0xff; *p++ = 0xff; *p++ = 0xff; *p++ = 0x0f; break; default: break; } } } } /* * Traverse the FAT table and populate head map. Initially, we * consider all clusters as possible head cluster (beginning of * a file or directory), and traverse the whole allocation table * by marking every non-head nodes as such (detailed below) and * fix obvious issues while we walk. * * For each "next" cluster, the possible values are: * * a) CLUST_FREE or CLUST_BAD. The *current* cluster can't be a * head node. * b) An out-of-range value. The only fix would be to truncate at * the cluster. * c) A valid cluster. It means that cluster (nextcl) is not a * head cluster. Note that during the scan, every cluster is * expected to be seen for at most once, and when we saw them * twice, it means a cross-linked chain which should be * truncated at the current cluster. * * After scan, the remaining set bits indicates all possible * head nodes, because they were never claimed by any other * node as the next node, but we do not know if these chains * would end with a valid EOF marker. We will check that in * checkchain() at a later time when checking directories, * where these head nodes would be marked as non-head. * * In the final pass, all head nodes should be cleared, and if * there is still head nodes, these would be leaders of lost * chain. */ for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { nextcl = fat_get_cl_next(fat, cl); /* Check if the next cluster number is valid */ if (nextcl == CLUST_FREE) { /* Save a hint for next free cluster */ if (boot->FSNext == 0) { boot->FSNext = cl; } if (fat_is_cl_head(fat, cl)) { fat_clear_cl_head(fat, cl); } boot->NumFree++; } else if (nextcl == CLUST_BAD) { if (fat_is_cl_head(fat, cl)) { fat_clear_cl_head(fat, cl); } boot->NumBad++; } else if (!valid_cl(fat, nextcl) && nextcl < CLUST_RSRVD) { pwarn("Cluster %u continues with out of range " "cluster number %u\n", cl, nextcl & boot->ClustMask); if (ask(0, "Truncate")) { ret |= fat_set_cl_next(fat, cl, CLUST_EOF); ret |= FSFATMOD; } } else if (valid_cl(fat, nextcl)) { if (fat_is_cl_head(fat, nextcl)) { fat_clear_cl_head(fat, nextcl); } else { pwarn("Cluster %u crossed another chain at %u\n", cl, nextcl); if (ask(0, "Truncate")) { ret |= fat_set_cl_next(fat, cl, CLUST_EOF); ret |= FSFATMOD; } } } } if (ret & FSFATAL) { releasefat(fat); free(fat); *fp = NULL; } else *fp = fat; return ret; } /* * Get type of reserved cluster */ const char * rsrvdcltype(cl_t cl) { if (cl == CLUST_FREE) return "free"; if (cl < CLUST_BAD) return "reserved"; if (cl > CLUST_BAD) return "as EOF"; return "bad"; } /* * Examine a cluster chain for errors and count its size. */ int checkchain(struct fat_descriptor *fat, cl_t head, size_t *chainsize) { cl_t prev_cl, current_cl, next_cl; const char *op; /* * We expect that the caller to give us a real, unvisited 'head' * cluster, and it must be a valid cluster. While scanning the * FAT table, we already excluded all clusters that was claimed * as a "next" cluster. Assert all the three conditions. */ assert(valid_cl(fat, head)); assert(fat_is_cl_head(fat, head)); /* * Immediately mark the 'head' cluster that we are about to visit. */ fat_clear_cl_head(fat, head); /* * The allocation of a non-zero sized file or directory is * represented as a singly linked list, and the tail node * would be the EOF marker (>=CLUST_EOFS). * * With a valid head node at hand, we expect all subsequent * cluster to be either a not yet seen and valid cluster (we * would continue counting), or the EOF marker (we conclude * the scan of this chain). * * For all other cases, the chain is invalid, and the only * viable fix would be to truncate at the current node (mark * it as EOF) when the next node violates that. */ *chainsize = 0; prev_cl = current_cl = head; for (next_cl = fat_get_cl_next(fat, current_cl); valid_cl(fat, next_cl); prev_cl = current_cl, current_cl = next_cl, next_cl = fat_get_cl_next(fat, current_cl)) (*chainsize)++; /* A natural end */ if (next_cl >= CLUST_EOFS) { (*chainsize)++; return FSOK; } /* * The chain ended with an out-of-range cluster number. * * If the current node is e.g. CLUST_FREE, CLUST_BAD, etc., * it should not be present in a chain and we has to truncate * at the previous node. * * If the current cluster points to an invalid cluster, the * current cluster might have useful data and we truncate at * the current cluster instead. */ if (next_cl == CLUST_FREE || next_cl >= CLUST_RSRVD) { pwarn("Cluster chain starting at %u ends with cluster marked %s\n", head, rsrvdcltype(next_cl)); current_cl = prev_cl; } else { pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", head, next_cl & boot_of_(fat)->ClustMask); (*chainsize)++; } if (*chainsize > 0) { op = "Truncate"; next_cl = CLUST_EOF; } else { op = "Clear"; next_cl = CLUST_FREE; } if (ask(0, "%s", op)) { return (fat_set_cl_next(fat, current_cl, next_cl) | FSFATMOD); } else { return (FSERROR); } } /* * Clear cluster chain from head. */ void clearchain(struct fat_descriptor *fat, cl_t head) { cl_t current_cl, next_cl; struct bootblock *boot = boot_of_(fat); current_cl = head; while (valid_cl(fat, current_cl)) { next_cl = fat_get_cl_next(fat, current_cl); (void)fat_set_cl_next(fat, current_cl, CLUST_FREE); boot->NumFree++; current_cl = next_cl; } } /* * Overwrite the n-th FAT with FAT0 */ static int copyfat(struct fat_descriptor *fat, int n) { size_t rwsize, tailsize, blobs, i; off_t dst_off, src_off; struct bootblock *boot; int ret, fd; ret = FSOK; fd = fd_of_(fat); boot = boot_of_(fat); blobs = howmany(fat->fatsize, fat32_cache_size); tailsize = fat->fatsize % fat32_cache_size; if (tailsize == 0) { tailsize = fat32_cache_size; } rwsize = fat32_cache_size; src_off = fat->fat32_offset; dst_off = boot->bpbResSectors + n * boot->FATsecs; dst_off *= boot->bpbBytesPerSec; for (i = 0; i < blobs; i++, src_off += fat32_cache_size, dst_off += fat32_cache_size) { if (i == blobs - 1) { rwsize = tailsize; } if ((lseek(fd, src_off, SEEK_SET) != src_off || (size_t)read(fd, fat->fatbuf, rwsize) != rwsize) && ret == FSOK) { perr("Unable to read FAT0"); ret = FSFATAL; continue; } if ((lseek(fd, dst_off, SEEK_SET) != dst_off || (size_t)write(fd, fat->fatbuf, rwsize) != rwsize) && ret == FSOK) { perr("Unable to write FAT %d", n); ret = FSERROR; } } return (ret); } /* * Write out FAT */ int writefat(struct fat_descriptor *fat) { u_int i; size_t writesz; off_t dst_base; int ret = FSOK, fd; struct bootblock *boot; struct fat32_cache_entry *entry; boot = boot_of_(fat); fd = fd_of_(fat); if (fat->use_cache) { /* * Attempt to flush all in-flight cache, and bail out * if we encountered an error (but only emit error * message once). Stop proceeding with copyfat() * if any flush failed. */ TAILQ_FOREACH(entry, &fat->fat32_cache_head, entries) { if (fat_flush_fat32_cache_entry(fat, entry) != FSOK) { if (ret == FSOK) { perr("Unable to write FAT"); ret = FSFATAL; } } } if (ret != FSOK) return (ret); /* Update backup copies of FAT, error is not fatal */ for (i = 1; i < boot->bpbFATs; i++) { if (copyfat(fat, i) != FSOK) ret = FSERROR; } } else { writesz = fat->fatsize; for (i = fat->is_mmapped ? 1 : 0; i < boot->bpbFATs; i++) { dst_base = boot->bpbResSectors + i * boot->FATsecs; dst_base *= boot->bpbBytesPerSec; if ((lseek(fd, dst_base, SEEK_SET) != dst_base || (size_t)write(fd, fat->fatbuf, writesz) != writesz) && ret == FSOK) { perr("Unable to write FAT %d", i); ret = ((i == 0) ? FSFATAL : FSERROR); } } } return ret; } /* * Check a complete in-memory FAT for lost cluster chains */ int checklost(struct fat_descriptor *fat) { cl_t head; int mod = FSOK; int dosfs, ret; size_t chains, chainlength; struct bootblock *boot; dosfs = fd_of_(fat); boot = boot_of_(fat); /* * At this point, we have already traversed all directories. * All remaining chain heads in the bitmap are heads of lost * chains. */ chains = fat_get_head_count(fat); for (head = CLUST_FIRST; chains > 0 && head < boot->NumClusters; ) { /* * We expect the bitmap to be very sparse, so skip if * the range is full of 0's */ if (head % LONG_BIT == 0 && !fat_is_cl_head_in_range(fat, head)) { head += LONG_BIT; continue; } if (fat_is_cl_head(fat, head)) { ret = checkchain(fat, head, &chainlength); if (ret != FSERROR && chainlength > 0) { pwarn("Lost cluster chain at cluster %u\n" "%zd Cluster(s) lost\n", head, chainlength); mod |= ret = reconnect(fat, head, chainlength); } if (mod & FSFATAL) break; if (ret == FSERROR && ask(0, "Clear")) { clearchain(fat, head); mod |= FSFATMOD; } chains--; } head++; } finishlf(); if (boot->bpbFSInfo) { ret = 0; if (boot->FSFree != 0xffffffffU && boot->FSFree != boot->NumFree) { pwarn("Free space in FSInfo block (%u) not correct (%u)\n", boot->FSFree, boot->NumFree); if (ask(1, "Fix")) { boot->FSFree = boot->NumFree; ret = 1; } } if (boot->FSNext != 0xffffffffU && (boot->FSNext >= boot->NumClusters || (boot->NumFree && fat_get_cl_next(fat, boot->FSNext) != CLUST_FREE))) { pwarn("Next free cluster in FSInfo block (%u) %s\n", boot->FSNext, (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free"); if (ask(1, "Fix")) for (head = CLUST_FIRST; head < boot->NumClusters; head++) if (fat_get_cl_next(fat, head) == CLUST_FREE) { boot->FSNext = head; ret = 1; break; } } if (ret) mod |= writefsinfo(dosfs, boot); } return mod; } diff --git a/sbin/fsck_msdosfs/main.c b/sbin/fsck_msdosfs/main.c index de54cd18eae7..0713189daa2d 100644 --- a/sbin/fsck_msdosfs/main.c +++ b/sbin/fsck_msdosfs/main.c @@ -1,163 +1,161 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1995 Wolfgang Solfrank * Copyright (c) 1995 Martin Husemann * * 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 AUTHORS ``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 AUTHORS 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. */ #include #ifndef lint __RCSID("$NetBSD: main.c,v 1.10 1997/10/01 02:18:14 enami Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include "fsutil.h" #include "ext.h" int alwaysno; /* assume "no" for all questions */ int alwaysyes; /* assume "yes" for all questions */ int preen; /* set when preening */ int rdonly; /* device is opened read only (supersedes above) */ int skipclean; /* skip clean file systems if preening */ int allow_mmap; /* Allow the use of mmap(), if possible */ static void usage(void) __dead2; static void usage(void) { fprintf(stderr, "%s\n%s\n", "usage: fsck_msdosfs -p [-f] filesystem ...", " fsck_msdosfs [-ny] filesystem ..."); exit(1); } int main(int argc, char **argv) { int ret = 0, erg; int ch; skipclean = 1; allow_mmap = 1; while ((ch = getopt(argc, argv, "CfFnpyM")) != -1) { switch (ch) { case 'C': /* for fsck_ffs compatibility */ break; case 'f': skipclean = 0; break; case 'F': /* * We can never run in the background. We must exit * silently with a nonzero exit code so that fsck(8) * can probe our support for -F. The exit code * doesn't really matter, but we use an unusual one * in case someone tries -F directly. The -F flag * is intentionally left out of the usage message. */ exit(5); case 'n': alwaysno = 1; alwaysyes = 0; break; case 'y': alwaysyes = 1; alwaysno = 0; break; case 'p': preen = 1; break; case 'M': allow_mmap = 0; break; default: usage(); break; } } argc -= optind; argv += optind; if (!argc) usage(); while (--argc >= 0) { setcdevname(*argv, preen); erg = checkfilesys(*argv++); if (erg > ret) ret = erg; } return ret; } /*VARARGS*/ int ask(int def, const char *fmt, ...) { va_list ap; char prompt[256]; int c; if (alwaysyes || alwaysno || rdonly) def = (alwaysyes && !rdonly && !alwaysno); if (preen) { if (def) printf("FIXED\n"); return def; } va_start(ap, fmt); vsnprintf(prompt, sizeof(prompt), fmt, ap); va_end(ap); if (alwaysyes || alwaysno || rdonly) { printf("%s? %s\n", prompt, def ? "yes" : "no"); return def; } do { printf("%s? [yn] ", prompt); fflush(stdout); c = getchar(); while (c != '\n' && getchar() != '\n') if (feof(stdin)) return 0; } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); return c == 'y' || c == 'Y'; } diff --git a/sbin/fsdb/fsdb.c b/sbin/fsdb/fsdb.c index 2d8c75cce5fe..48526ad4044b 100644 --- a/sbin/fsdb/fsdb.c +++ b/sbin/fsdb/fsdb.c @@ -1,1288 +1,1283 @@ /* $NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995 John T. Kohl * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsdb.h" #include "fsck.h" static void usage(void) __dead2; int cmdloop(void); static int compare_blk32(uint32_t *wantedblk, uint32_t curblk); static int compare_blk64(uint64_t *wantedblk, uint64_t curblk); static int founddatablk(uint64_t blk); static int find_blks32(uint32_t *buf, int size, uint32_t *blknum); static int find_blks64(uint64_t *buf, int size, uint64_t *blknum); static int find_indirblks32(uint32_t blk, int ind_level, uint32_t *blknum); static int find_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum); /* * Track modifications to the filesystem. Two types of changes are tracked. * The first type of changes are those that are not critical to the integrity * of the filesystem such as owner, group, time stamps, access mode, and * generation number. The second type of changes are those that do affect * the integrity of the filesystem including zeroing inodes, changing block * pointers, directory entries, link counts, file lengths, file types and * file flags. * * When quitting having made no changes or only changes to data that is not * critical to filesystem integrity, the clean state of the filesystem is * left unchanged. But if filesystem critical data are changed then fsdb * will set the unclean flag which will require a full fsck to be run * before the filesystem can be mounted. */ static int fsnoncritmodified; /* filesystem non-critical modifications */ static int fscritmodified; /* filesystem integrity critical mods */ struct inode curip; union dinode *curinode; ino_t curinum, ocurrent; static void usage(void) { fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n"); exit(1); } /* * We suck in lots of fsck code, and just pick & choose the stuff we want. * * fsreadfd is set up to read from the file system, fswritefd to write to * the file system. */ int main(int argc, char *argv[]) { int ch, rval; char *fsys = NULL; while (-1 != (ch = getopt(argc, argv, "fdr"))) { switch (ch) { case 'f': /* The -f option is left for historical * reasons and has no meaning. */ break; case 'd': debug++; break; case 'r': nflag++; /* "no" in fsck, readonly for us */ break; default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); else fsys = argv[0]; sblock_init(); if (openfilesys(fsys) == 0 || readsb() == 0 || setup(fsys) == 0) errx(1, "cannot set up file system `%s'", fsys); if (fswritefd < 0) nflag++; printf("%s file system `%s'\nLast Mounted on %s\n", nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt); rval = cmdloop(); if (!nflag) { if (fscritmodified != 0) { sblock.fs_clean = 0; /* mark it dirty */ sbdirty(); } ckfini(fscritmodified ? 0 : sblock.fs_clean); if (fscritmodified == 0) exit(0); printf("*** FILE SYSTEM MARKED DIRTY\n"); printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); printf("*** IF IT IS MOUNTED, RE-MOUNT WITH -u -o reload\n"); } exit(rval); } #define CMDFUNC(func) int func(int argc, char *argv[]) #define CMDFUNCSTART(func) int func(int argc, char *argv[]) CMDFUNC(helpfn); CMDFUNC(focus); /* focus on inode */ CMDFUNC(active); /* print active inode */ CMDFUNC(blocks); /* print blocks for active inode */ CMDFUNC(focusname); /* focus by name */ CMDFUNC(zapi); /* clear inode */ CMDFUNC(uplink); /* incr link */ CMDFUNC(downlink); /* decr link */ CMDFUNC(linkcount); /* set link count */ CMDFUNC(quit); /* quit */ CMDFUNC(quitclean); /* quit with filesystem marked clean */ CMDFUNC(findblk); /* find block */ CMDFUNC(ls); /* list directory */ CMDFUNC(rm); /* remove name */ CMDFUNC(ln); /* add name */ CMDFUNC(newtype); /* change type */ CMDFUNC(chmode); /* change mode */ CMDFUNC(chlen); /* change length */ CMDFUNC(chaflags); /* change flags */ CMDFUNC(chgen); /* change generation */ CMDFUNC(chowner); /* change owner */ CMDFUNC(chgroup); /* Change group */ CMDFUNC(back); /* pop back to last ino */ CMDFUNC(chbtime); /* Change btime */ CMDFUNC(chmtime); /* Change mtime */ CMDFUNC(chctime); /* Change ctime */ CMDFUNC(chatime); /* Change atime */ CMDFUNC(chinum); /* Change inode # of dirent */ CMDFUNC(chname); /* Change dirname of dirent */ CMDFUNC(chsize); /* Change size */ CMDFUNC(chdb); /* Change direct block pointer */ struct cmdtable cmds[] = { { "help", "Print out help", 1, 1, FL_RO, helpfn }, { "?", "Print out help", 1, 1, FL_RO, helpfn }, { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus }, { "clri", "Clear inode INUM", 2, 2, FL_CWR, zapi }, { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname }, { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname }, { "back", "Go to previous active inode", 1, 1, FL_RO, back }, { "active", "Print active inode", 1, 1, FL_RO, active }, { "print", "Print active inode", 1, 1, FL_RO, active }, { "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks }, { "uplink", "Increment link count", 1, 1, FL_CWR, uplink }, { "downlink", "Decrement link count", 1, 1, FL_CWR, downlink }, { "linkcount", "Set link count to COUNT", 2, 2, FL_CWR, linkcount }, { "findblk", "Find inode owning disk block(s)", 2, 33, FL_RO, findblk}, { "ls", "List current inode as directory", 1, 1, FL_RO, ls }, { "rm", "Remove NAME from current inode directory", 2, 2, FL_CWR | FL_ST, rm }, { "del", "Remove NAME from current inode directory", 2, 2, FL_CWR | FL_ST, rm }, { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_CWR | FL_ST, ln }, { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_CWR, chinum }, { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR | FL_ST, chname }, { "chtype", "Change type of current inode to TYPE", 2, 2, FL_CWR, newtype }, { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode }, { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner }, { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup }, { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_CWR, chaflags }, { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen }, { "chsize", "Change size of current inode to SIZE", 2, 2, FL_CWR, chsize }, { "btime", "Change btime of current inode to BTIME", 2, 2, FL_WR, chbtime }, { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime }, { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime }, { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime }, { "chdb", "Change db pointer N of current inode to BLKNO", 3, 3, FL_CWR, chdb }, { "quitclean", "Exit with filesystem marked clean", 1, 1, FL_RO, quitclean }, { "quit", "Exit", 1, 1, FL_RO, quit }, { "q", "Exit", 1, 1, FL_RO, quit }, { "exit", "Exit", 1, 1, FL_RO, quit }, { NULL, 0, 0, 0, 0, NULL }, }; int helpfn(int argc, char *argv[]) { struct cmdtable *cmdtp; printf("Commands are:\n%-10s %5s %5s %s\n", "command", "min args", "max args", "what"); for (cmdtp = cmds; cmdtp->cmd; cmdtp++) printf("%-10s %5u %5u %s\n", cmdtp->cmd, cmdtp->minargc-1, cmdtp->maxargc-1, cmdtp->helptxt); return 0; } char * prompt(EditLine *el) { static char pstring[64]; snprintf(pstring, sizeof(pstring), "fsdb (inum: %ju)> ", (uintmax_t)curinum); return pstring; } static void setcurinode(ino_t inum) { if (curip.i_number != 0) irelse(&curip); ginode(inum, &curip); curinode = curip.i_dp; curinum = inum; } int cmdloop(void) { char *line; const char *elline; int cmd_argc, rval = 0, known; #define scratch known char **cmd_argv; struct cmdtable *cmdp; History *hist; EditLine *elptr; HistEvent he; setcurinode(UFS_ROOTINO); printactive(0); hist = history_init(); history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ elptr = el_init("fsdb", stdin, stdout, stderr); el_set(elptr, EL_EDITOR, "emacs"); el_set(elptr, EL_PROMPT, prompt); el_set(elptr, EL_HIST, history, hist); el_source(elptr, NULL); while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { if (debug) printf("command `%s'\n", elline); history(hist, &he, H_ENTER, elline); line = strdup(elline); cmd_argv = crack(line, &cmd_argc); /* * el_parse returns -1 to signal that it's not been handled * internally. */ if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1) continue; if (cmd_argc) { known = 0; for (cmdp = cmds; cmdp->cmd; cmdp++) { if (!strcmp(cmdp->cmd, cmd_argv[0])) { if ((cmdp->flags & (FL_CWR | FL_WR)) != 0 && nflag) warnx("`%s' requires write access", cmd_argv[0]), rval = 1; else if (cmd_argc >= cmdp->minargc && cmd_argc <= cmdp->maxargc) rval = (*cmdp->handler)(cmd_argc, cmd_argv); else if (cmd_argc >= cmdp->minargc && (cmdp->flags & FL_ST) == FL_ST) { strcpy(line, elline); cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc); rval = (*cmdp->handler)(cmd_argc, cmd_argv); } else rval = argcount(cmdp, cmd_argc, cmd_argv); known = 1; if (rval == 0) { if ((cmdp->flags & FL_WR) != 0) fsnoncritmodified = 1; if ((cmdp->flags & FL_CWR) != 0) fscritmodified = 1; } break; } } if (!known) warnx("unknown command `%s'", cmd_argv[0]), rval = 1; } else rval = 0; free(line); if (rval < 0) { /* user typed "quit" */ irelse(&curip); return 0; } if (rval) warnx("command failed, return value was %d", rval); } el_end(elptr); history_end(hist); irelse(&curip); return rval; } #define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \ if (inum < UFS_ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \ printf("inode %ju out of range; range is [%ju,%ju]\n", \ (uintmax_t)inum, (uintmax_t)UFS_ROOTINO, (uintmax_t)maxino);\ return 1; \ } /* * Focus on given inode number */ CMDFUNCSTART(focus) { ino_t inum; char *cp; GETINUM(1,inum); ocurrent = curinum; setcurinode(inum); printactive(0); return 0; } CMDFUNCSTART(back) { setcurinode(ocurrent); printactive(0); return 0; } CMDFUNCSTART(zapi) { struct inode ip; ino_t inum; char *cp; GETINUM(1,inum); ginode(inum, &ip); clearinode(ip.i_dp); inodirty(&ip); irelse(&ip); return 0; } CMDFUNCSTART(active) { printactive(0); return 0; } CMDFUNCSTART(blocks) { printactive(1); return 0; } CMDFUNCSTART(quit) { return -1; } CMDFUNCSTART(quitclean) { if (fscritmodified) { printf("Warning: modified filesystem marked clean\n"); fscritmodified = 0; sblock.fs_clean = 1; } return -1; } CMDFUNCSTART(uplink) { if (!checkactive()) return 1; DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1); printf("inode %ju link count now %d\n", (uintmax_t)curinum, DIP(curinode, di_nlink)); inodirty(&curip); return 0; } CMDFUNCSTART(downlink) { if (!checkactive()) return 1; DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1); printf("inode %ju link count now %d\n", (uintmax_t)curinum, DIP(curinode, di_nlink)); inodirty(&curip); return 0; } const char *typename[] = { "unknown", "fifo", "char special", "unregistered #3", "directory", "unregistered #5", "blk special", "unregistered #7", "regular", "unregistered #9", "symlink", "unregistered #11", "socket", "unregistered #13", "whiteout", }; int diroff; int slot; int scannames(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; printf("slot %d off %d ino %d reclen %d: %s, `%.*s'\n", slot++, diroff, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type], dirp->d_namlen, dirp->d_name); diroff += dirp->d_reclen; return (KEEPON); } CMDFUNCSTART(ls) { struct inodesc idesc; checkactivedir(); /* let it go on anyway */ slot = 0; diroff = 0; idesc.id_number = curinum; idesc.id_func = scannames; idesc.id_type = DATA; idesc.id_fix = IGNORE; ckinode(curinode, &idesc); return 0; } static int findblk_numtofind; static int wantedblksize; CMDFUNCSTART(findblk) { ino_t inum, inosused; uint32_t *wantedblk32; uint64_t *wantedblk64; struct bufarea *cgbp; struct cg *cgp; int c, i, is_ufs2; wantedblksize = (argc - 1); is_ufs2 = sblock.fs_magic == FS_UFS2_MAGIC; ocurrent = curinum; if (is_ufs2) { wantedblk64 = calloc(wantedblksize, sizeof(uint64_t)); if (wantedblk64 == NULL) err(1, "malloc"); for (i = 1; i < argc; i++) wantedblk64[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0)); } else { wantedblk32 = calloc(wantedblksize, sizeof(uint32_t)); if (wantedblk32 == NULL) err(1, "malloc"); for (i = 1; i < argc; i++) wantedblk32[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0)); } findblk_numtofind = wantedblksize; /* * sblock.fs_ncg holds a number of cylinder groups. * Iterate over all cylinder groups. */ for (c = 0; c < sblock.fs_ncg; c++) { /* * sblock.fs_ipg holds a number of inodes per cylinder group. * Calculate a highest inode number for a given cylinder group. */ inum = c * sblock.fs_ipg; /* Read cylinder group. */ cgbp = cglookup(c); cgp = cgbp->b_un.b_cg; /* * Get a highest used inode number for a given cylinder group. * For UFS1 all inodes initialized at the newfs stage. */ if (is_ufs2) inosused = cgp->cg_initediblk; else inosused = sblock.fs_ipg; for (; inosused > 0; inum++, inosused--) { /* Skip magic inodes: 0, UFS_WINO, UFS_ROOTINO. */ if (inum < UFS_ROOTINO) continue; /* * Check if the block we are looking for is just an inode block. * * ino_to_fsba() - get block containing inode from its number. * INOPB() - get a number of inodes in one disk block. */ if (is_ufs2 ? compare_blk64(wantedblk64, ino_to_fsba(&sblock, inum)) : compare_blk32(wantedblk32, ino_to_fsba(&sblock, inum))) { printf("block %llu: inode block (%ju-%ju)\n", (unsigned long long)fsbtodb(&sblock, ino_to_fsba(&sblock, inum)), (uintmax_t)(inum / INOPB(&sblock)) * INOPB(&sblock), (uintmax_t)(inum / INOPB(&sblock) + 1) * INOPB(&sblock)); findblk_numtofind--; if (findblk_numtofind == 0) goto end; } /* Get on-disk inode aka dinode. */ setcurinode(inum); /* Find IFLNK dinode with allocated data blocks. */ switch (DIP(curinode, di_mode) & IFMT) { case IFDIR: case IFREG: if (DIP(curinode, di_blocks) == 0) continue; break; case IFLNK: { uint64_t size = DIP(curinode, di_size); if (size > 0 && size < sblock.fs_maxsymlinklen && DIP(curinode, di_blocks) == 0) continue; else break; } default: continue; } /* Look through direct data blocks. */ if (is_ufs2 ? find_blks64(curinode->dp2.di_db, UFS_NDADDR, wantedblk64) : find_blks32(curinode->dp1.di_db, UFS_NDADDR, wantedblk32)) goto end; for (i = 0; i < UFS_NIADDR; i++) { /* * Does the block we are looking for belongs to the * indirect blocks? */ if (is_ufs2 ? compare_blk64(wantedblk64, curinode->dp2.di_ib[i]) : compare_blk32(wantedblk32, curinode->dp1.di_ib[i])) if (founddatablk(is_ufs2 ? curinode->dp2.di_ib[i] : curinode->dp1.di_ib[i])) goto end; /* * Search through indirect, double and triple indirect * data blocks. */ if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) : (curinode->dp1.di_ib[i] != 0)) if (is_ufs2 ? find_indirblks64(curinode->dp2.di_ib[i], i, wantedblk64) : find_indirblks32(curinode->dp1.di_ib[i], i, wantedblk32)) goto end; } } } end: setcurinode(ocurrent); if (is_ufs2) free(wantedblk64); else free(wantedblk32); return 0; } static int compare_blk32(uint32_t *wantedblk, uint32_t curblk) { int i; for (i = 0; i < wantedblksize; i++) { if (wantedblk[i] != 0 && wantedblk[i] == curblk) { wantedblk[i] = 0; return 1; } } return 0; } static int compare_blk64(uint64_t *wantedblk, uint64_t curblk) { int i; for (i = 0; i < wantedblksize; i++) { if (wantedblk[i] != 0 && wantedblk[i] == curblk) { wantedblk[i] = 0; return 1; } } return 0; } static int founddatablk(uint64_t blk) { printf("%llu: data block of inode %ju\n", (unsigned long long)fsbtodb(&sblock, blk), (uintmax_t)curinum); findblk_numtofind--; if (findblk_numtofind == 0) return 1; return 0; } static int find_blks32(uint32_t *buf, int size, uint32_t *wantedblk) { int blk; for (blk = 0; blk < size; blk++) { if (buf[blk] == 0) continue; if (compare_blk32(wantedblk, buf[blk])) { if (founddatablk(buf[blk])) return 1; } } return 0; } static int find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk) { #define MAXNINDIR (MAXBSIZE / sizeof(uint32_t)) uint32_t idblk[MAXNINDIR]; int i; blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize); if (ind_level <= 0) { if (find_blks32(idblk, sblock.fs_bsize / sizeof(uint32_t), wantedblk)) return 1; } else { ind_level--; for (i = 0; i < sblock.fs_bsize / sizeof(uint32_t); i++) { if (compare_blk32(wantedblk, idblk[i])) { if (founddatablk(idblk[i])) return 1; } if (idblk[i] != 0) if (find_indirblks32(idblk[i], ind_level, wantedblk)) return 1; } } #undef MAXNINDIR return 0; } static int find_blks64(uint64_t *buf, int size, uint64_t *wantedblk) { int blk; for (blk = 0; blk < size; blk++) { if (buf[blk] == 0) continue; if (compare_blk64(wantedblk, buf[blk])) { if (founddatablk(buf[blk])) return 1; } } return 0; } static int find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk) { #define MAXNINDIR (MAXBSIZE / sizeof(uint64_t)) uint64_t idblk[MAXNINDIR]; int i; blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize); if (ind_level <= 0) { if (find_blks64(idblk, sblock.fs_bsize / sizeof(uint64_t), wantedblk)) return 1; } else { ind_level--; for (i = 0; i < sblock.fs_bsize / sizeof(uint64_t); i++) { if (compare_blk64(wantedblk, idblk[i])) { if (founddatablk(idblk[i])) return 1; } if (idblk[i] != 0) if (find_indirblks64(idblk[i], ind_level, wantedblk)) return 1; } } #undef MAXNINDIR return 0; } int findino(struct inodesc *idesc); /* from fsck */ static int dolookup(char *name); static int dolookup(char *name) { struct inodesc idesc; if (!checkactivedir()) return 0; idesc.id_number = curinum; idesc.id_func = findino; idesc.id_name = name; idesc.id_type = DATA; idesc.id_fix = IGNORE; if (ckinode(curinode, &idesc) & FOUND) { setcurinode(idesc.id_parent); printactive(0); return 1; } else { warnx("name `%s' not found in current inode directory", name); return 0; } } CMDFUNCSTART(focusname) { char *p, *val; if (!checkactive()) return 1; ocurrent = curinum; if (argv[1][0] == '/') { setcurinode(UFS_ROOTINO); } else { if (!checkactivedir()) return 1; } for (p = argv[1]; p != NULL;) { while ((val = strsep(&p, "/")) != NULL && *val == '\0'); if (val) { printf("component `%s': ", val); fflush(stdout); if (!dolookup(val)) { return(1); } } } return 0; } CMDFUNCSTART(ln) { ino_t inum; int rval; char *cp; GETINUM(1,inum); if (!checkactivedir()) return 1; rval = makeentry(curinum, inum, argv[2]); if (rval) printf("Ino %ju entered as `%s'\n", (uintmax_t)inum, argv[2]); else printf("could not enter name? weird.\n"); return rval; } CMDFUNCSTART(rm) { int rval; if (!checkactivedir()) return 1; rval = changeino(curinum, argv[1], 0, 0); if (rval & ALTERED) { printf("Name `%s' removed\n", argv[1]); return 0; } else { printf("could not remove name ('%s')? weird.\n", argv[1]); return 1; } } long slotcount, desired; int chinumfunc(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; if (slotcount++ == desired) { dirp->d_ino = idesc->id_parent; return STOP|ALTERED|FOUND; } return KEEPON; } CMDFUNCSTART(chinum) { char *cp; ino_t inum; struct inodesc idesc; slotcount = 0; if (!checkactivedir()) return 1; GETINUM(2,inum); desired = strtol(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' || desired < 0) { printf("invalid slot number `%s'\n", argv[1]); return 1; } idesc.id_number = curinum; idesc.id_func = chinumfunc; idesc.id_fix = IGNORE; idesc.id_type = DATA; idesc.id_parent = inum; /* XXX convenient hiding place */ if (ckinode(curinode, &idesc) & FOUND) return 0; else { warnx("no %sth slot in current directory", argv[1]); return 1; } } int chnamefunc(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; struct direct testdir; if (slotcount++ == desired) { /* will name fit? */ testdir.d_namlen = strlen(idesc->id_name); if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) { dirp->d_namlen = testdir.d_namlen; strcpy(dirp->d_name, idesc->id_name); return STOP|ALTERED|FOUND; } else return STOP|FOUND; /* won't fit, so give up */ } return KEEPON; } CMDFUNCSTART(chname) { int rval; char *cp; struct inodesc idesc; slotcount = 0; if (!checkactivedir()) return 1; desired = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0') { printf("invalid slot number `%s'\n", argv[1]); return 1; } idesc.id_number = curinum; idesc.id_func = chnamefunc; idesc.id_fix = IGNORE; idesc.id_type = DATA; idesc.id_name = argv[2]; rval = ckinode(curinode, &idesc); if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED)) return 0; else if (rval & FOUND) { warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]); return 1; } else { warnx("no %sth slot in current directory", argv[1]); return 1; } } struct typemap { const char *typename; int typebits; } typenamemap[] = { {"file", IFREG}, {"dir", IFDIR}, {"socket", IFSOCK}, {"fifo", IFIFO}, }; CMDFUNCSTART(newtype) { int type; struct typemap *tp; if (!checkactive()) return 1; type = DIP(curinode, di_mode) & IFMT; for (tp = typenamemap; tp < &typenamemap[nitems(typenamemap)]; tp++) { if (!strcmp(argv[1], tp->typename)) { printf("setting type to %s\n", tp->typename); type = tp->typebits; break; } } if (tp == &typenamemap[nitems(typenamemap)]) { warnx("type `%s' not known", argv[1]); warnx("try one of `file', `dir', `socket', `fifo'"); return 1; } DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT); DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chmode) { long modebits; char *cp; if (!checkactive()) return 1; modebits = strtol(argv[1], &cp, 8); if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) { warnx("bad modebits `%s'", argv[1]); return 1; } DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777); DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chaflags) { u_long flags; char *cp; if (!checkactive()) return 1; flags = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { warnx("bad flags `%s'", argv[1]); return 1; } if (flags > UINT_MAX) { warnx("flags set beyond 32-bit range of field (%lx)\n", flags); return(1); } DIP_SET(curinode, di_flags, flags); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chgen) { long gen; char *cp; if (!checkactive()) return 1; gen = strtol(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { warnx("bad gen `%s'", argv[1]); return 1; } if (gen > UINT_MAX) { warnx("gen set beyond 32-bit range of field (0x%lx), max is 0x%x\n", gen, UINT_MAX); return(1); } DIP_SET(curinode, di_gen, gen); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chsize) { off_t size; char *cp; if (!checkactive()) return 1; size = strtoll(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0') { warnx("bad size `%s'", argv[1]); return 1; } if (size < 0) { warnx("size set to negative (%jd)\n", (intmax_t)size); return(1); } DIP_SET(curinode, di_size, size); inodirty(&curip); printactive(0); return 0; } CMDFUNC(chdb) { unsigned int idx; daddr_t bno; char *cp; if (!checkactive()) return 1; idx = strtoull(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0') { warnx("bad pointer idx `%s'", argv[1]); return 1; } bno = strtoll(argv[2], &cp, 0); if (cp == argv[2] || *cp != '\0') { warnx("bad block number `%s'", argv[2]); return 1; } if (idx >= UFS_NDADDR) { warnx("pointer index %d is out of range", idx); return 1; } DIP_SET(curinode, di_db[idx], bno); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(linkcount) { int lcnt; char *cp; if (!checkactive()) return 1; lcnt = strtol(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { warnx("bad link count `%s'", argv[1]); return 1; } if (lcnt > USHRT_MAX || lcnt < 0) { warnx("max link count is %d\n", USHRT_MAX); return 1; } DIP_SET(curinode, di_nlink, lcnt); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chowner) { unsigned long uid; char *cp; struct passwd *pwd; if (!checkactive()) return 1; uid = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { /* try looking up name */ if ((pwd = getpwnam(argv[1]))) { uid = pwd->pw_uid; } else { warnx("bad uid `%s'", argv[1]); return 1; } } DIP_SET(curinode, di_uid, uid); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chgroup) { unsigned long gid; char *cp; struct group *grp; if (!checkactive()) return 1; gid = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { if ((grp = getgrnam(argv[1]))) { gid = grp->gr_gid; } else { warnx("bad gid `%s'", argv[1]); return 1; } } DIP_SET(curinode, di_gid, gid); inodirty(&curip); printactive(0); return 0; } int dotime(char *name, time_t *secp, int32_t *nsecp) { char *p, *val; struct tm t; int32_t nsec; p = strchr(name, '.'); if (p) { *p = '\0'; nsec = strtoul(++p, &val, 0); if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { warnx("invalid nanoseconds"); goto badformat; } } else nsec = 0; if (strlen(name) != 14) { badformat: warnx("date format: YYYYMMDDHHMMSS[.nsec]"); return 1; } *nsecp = nsec; for (p = name; *p; p++) if (*p < '0' || *p > '9') goto badformat; p = name; #define VAL() ((*p++) - '0') t.tm_year = VAL(); t.tm_year = VAL() + t.tm_year * 10; t.tm_year = VAL() + t.tm_year * 10; t.tm_year = VAL() + t.tm_year * 10 - 1900; t.tm_mon = VAL(); t.tm_mon = VAL() + t.tm_mon * 10 - 1; t.tm_mday = VAL(); t.tm_mday = VAL() + t.tm_mday * 10; t.tm_hour = VAL(); t.tm_hour = VAL() + t.tm_hour * 10; t.tm_min = VAL(); t.tm_min = VAL() + t.tm_min * 10; t.tm_sec = VAL(); t.tm_sec = VAL() + t.tm_sec * 10; t.tm_isdst = -1; *secp = mktime(&t); if (*secp == -1) { warnx("date/time out of range"); return 1; } return 0; } CMDFUNCSTART(chbtime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) return 1; curinode->dp2.di_birthtime = _time_to_time64(secs); curinode->dp2.di_birthnsec = nsecs; inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chmtime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) curinode->dp1.di_mtime = _time_to_time32(secs); else curinode->dp2.di_mtime = _time_to_time64(secs); DIP_SET(curinode, di_mtimensec, nsecs); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chatime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) curinode->dp1.di_atime = _time_to_time32(secs); else curinode->dp2.di_atime = _time_to_time64(secs); DIP_SET(curinode, di_atimensec, nsecs); inodirty(&curip); printactive(0); return 0; } CMDFUNCSTART(chctime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) curinode->dp1.di_ctime = _time_to_time32(secs); else curinode->dp2.di_ctime = _time_to_time64(secs); DIP_SET(curinode, di_ctimensec, nsecs); inodirty(&curip); printactive(0); return 0; } diff --git a/sbin/fsdb/fsdbutil.c b/sbin/fsdb/fsdbutil.c index c8a3a8a525e3..737dabba643f 100644 --- a/sbin/fsdb/fsdbutil.c +++ b/sbin/fsdb/fsdbutil.c @@ -1,250 +1,245 @@ /* $NetBSD: fsdbutil.c,v 1.2 1995/10/08 23:18:12 thorpej Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995 John T. Kohl * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include "fsdb.h" #include "fsck.h" void prtblknos(struct fs *fs, union dinode *dp); char ** crack(char *line, int *argc) { static char *argv[8]; int i; char *p, *val; for (p = line, i = 0; p != NULL && i < 8; i++) { while ((val = strsep(&p, " \t\n")) != NULL && *val == '\0') /**/; if (val) argv[i] = val; else break; } *argc = i; return argv; } char ** recrack(char *line, int *argc, int argc_max) { static char *argv[8]; int i; char *p, *val; for (p = line, i = 0; p != NULL && i < 8 && i < argc_max - 1; i++) { while ((val = strsep(&p, " \t\n")) != NULL && *val == '\0') /**/; if (val) argv[i] = val; else break; } argv[i] = argv[i - 1] + strlen(argv[i - 1]) + 1; argv[i][strcspn(argv[i], "\n")] = '\0'; *argc = i + 1; return argv; } int argcount(struct cmdtable *cmdp, int argc, char *argv[]) { if (cmdp->minargc == cmdp->maxargc) warnx("command `%s' takes %u arguments, got %u", cmdp->cmd, cmdp->minargc-1, argc-1); else warnx("command `%s' takes from %u to %u arguments", cmdp->cmd, cmdp->minargc-1, cmdp->maxargc-1); warnx("usage: %s: %s", cmdp->cmd, cmdp->helptxt); return 1; } void printstat(const char *cp, ino_t inum, union dinode *dp) { struct group *grp; struct passwd *pw; ufs2_daddr_t blocks; int64_t gen; char *p; time_t t; printf("%s: ", cp); switch (DIP(dp, di_mode) & IFMT) { case IFDIR: puts("directory"); break; case IFREG: puts("regular file"); break; case IFBLK: printf("block special (%#jx)", (uintmax_t)DIP(dp, di_rdev)); break; case IFCHR: printf("character special (%#jx)", DIP(dp, di_rdev)); break; case IFLNK: fputs("symlink",stdout); if (DIP(dp, di_size) > 0 && DIP(dp, di_size) < sblock.fs_maxsymlinklen && DIP(dp, di_blocks) == 0) { printf(" to `%.*s'\n", (int) DIP(dp, di_size), DIP(dp, di_shortlink)); } else { putchar('\n'); } break; case IFSOCK: puts("socket"); break; case IFIFO: puts("fifo"); break; } printf("I=%ju MODE=%o SIZE=%ju", (uintmax_t)inum, DIP(dp, di_mode), (uintmax_t)DIP(dp, di_size)); if (sblock.fs_magic != FS_UFS1_MAGIC) { t = _time64_to_time(dp->dp2.di_birthtime); p = ctime(&t); printf("\n\tBTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20], dp->dp2.di_birthnsec); } if (sblock.fs_magic == FS_UFS1_MAGIC) t = _time32_to_time(dp->dp1.di_mtime); else t = _time64_to_time(dp->dp2.di_mtime); p = ctime(&t); printf("\n\tMTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20], DIP(dp, di_mtimensec)); if (sblock.fs_magic == FS_UFS1_MAGIC) t = _time32_to_time(dp->dp1.di_ctime); else t = _time64_to_time(dp->dp2.di_ctime); p = ctime(&t); printf("\n\tCTIME=%15.15s %4.4s [%d nsec]", &p[4], &p[20], DIP(dp, di_ctimensec)); if (sblock.fs_magic == FS_UFS1_MAGIC) t = _time32_to_time(dp->dp1.di_atime); else t = _time64_to_time(dp->dp2.di_atime); p = ctime(&t); printf("\n\tATIME=%15.15s %4.4s [%d nsec]\n", &p[4], &p[20], DIP(dp, di_atimensec)); if ((pw = getpwuid(DIP(dp, di_uid)))) printf("OWNER=%s ", pw->pw_name); else printf("OWNUID=%u ", DIP(dp, di_uid)); if ((grp = getgrgid(DIP(dp, di_gid)))) printf("GRP=%s ", grp->gr_name); else printf("GID=%u ", DIP(dp, di_gid)); blocks = DIP(dp, di_blocks); gen = DIP(dp, di_gen); printf("LINKCNT=%d FLAGS=%#x BLKCNT=%jx GEN=%jx\n", DIP(dp, di_nlink), DIP(dp, di_flags), (intmax_t)blocks, (intmax_t)gen); } int checkactive(void) { if (!curinode) { warnx("no current inode\n"); return 0; } return 1; } int checkactivedir(void) { if (!curinode) { warnx("no current inode\n"); return 0; } if ((DIP(curinode, di_mode) & IFMT) != IFDIR) { warnx("inode %ju not a directory", (uintmax_t)curinum); return 0; } return 1; } int printactive(int doblocks) { if (!checkactive()) return 1; switch (DIP(curinode, di_mode) & IFMT) { case IFDIR: case IFREG: case IFBLK: case IFCHR: case IFLNK: case IFSOCK: case IFIFO: if (doblocks) prtblknos(&sblock, curinode); else printstat("current inode", curinum, curinode); break; case 0: printf("current inode %ju: unallocated inode\n", (uintmax_t)curinum); break; default: printf("current inode %ju: screwy itype 0%o (mode 0%o)?\n", (uintmax_t)curinum, DIP(curinode, di_mode) & IFMT, DIP(curinode, di_mode)); break; } return 0; } diff --git a/sbin/fsirand/fsirand.c b/sbin/fsirand/fsirand.c index cd60093e3642..2a5eb0c5136d 100644 --- a/sbin/fsirand/fsirand.c +++ b/sbin/fsirand/fsirand.c @@ -1,240 +1,235 @@ /* $OpenBSD: fsirand.c,v 1.9 1997/02/28 00:46:33 millert Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1997 Todd C. Miller * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Todd C. Miller. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``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 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(void) __dead2; int fsirand(char *); static int printonly = 0, force = 0, ignorelabel = 0; int main(int argc, char *argv[]) { int n, ex = 0; struct rlimit rl; while ((n = getopt(argc, argv, "bfp")) != -1) { switch (n) { case 'b': ignorelabel++; break; case 'p': printonly++; break; case 'f': force++; break; default: usage(); } } if (argc - optind < 1) usage(); /* Increase our data size to the max */ if (getrlimit(RLIMIT_DATA, &rl) == 0) { rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_DATA, &rl) < 0) warn("can't get resource limit to max data size"); } else warn("can't get resource limit for data size"); for (n = optind; n < argc; n++) { if (argc - optind != 1) (void)puts(argv[n]); ex += fsirand(argv[n]); if (n < argc - 1) putchar('\n'); } exit(ex); } int fsirand(char *device) { struct ufs1_dinode *dp1; struct ufs2_dinode *dp2; caddr_t inodebuf; ssize_t ibufsize; struct fs *sblock; ino_t inumber; ufs2_daddr_t dblk; int devfd, n, cg; u_int32_t bsize = DEV_BSIZE; if ((devfd = open(device, printonly ? O_RDONLY : O_RDWR)) < 0) { warn("can't open %s", device); return (1); } dp1 = NULL; dp2 = NULL; /* Read in master superblock */ if ((errno = sbget(devfd, &sblock, UFS_STDSB, UFS_NOCSUM)) != 0) { switch (errno) { case ENOENT: warnx("Cannot find file system superblock"); return (1); default: warn("Unable to read file system superblock"); return (1); } } /* * Check for unclean filesystem. */ if (sblock->fs_clean == 0 || (sblock->fs_flags & (FS_UNCLEAN | FS_NEEDSFSCK)) != 0) errx(1, "%s is not clean - run fsck.\n", device); if (sblock->fs_magic == FS_UFS1_MAGIC && sblock->fs_old_inodefmt < FS_44INODEFMT) { warnx("file system format is too old, sorry"); return (1); } if (!force && !printonly && sblock->fs_clean != 1) { warnx("file system is not clean, fsck %s first", device); return (1); } /* XXX - should really cap buffer at 512kb or so */ if (sblock->fs_magic == FS_UFS1_MAGIC) ibufsize = sizeof(struct ufs1_dinode) * sblock->fs_ipg; else ibufsize = sizeof(struct ufs2_dinode) * sblock->fs_ipg; if ((inodebuf = malloc(ibufsize)) == NULL) errx(1, "can't allocate memory for inode buffer"); if (printonly && (sblock->fs_id[0] || sblock->fs_id[1])) { if (sblock->fs_id[0]) (void)printf("%s was randomized on %s", device, ctime((void *)&(sblock->fs_id[0]))); (void)printf("fsid: %x %x\n", sblock->fs_id[0], sblock->fs_id[1]); } /* Randomize fs_id unless old 4.2BSD file system */ if (!printonly) { /* Randomize fs_id and write out new sblock and backups */ sblock->fs_id[0] = (u_int32_t)time(NULL); sblock->fs_id[1] = arc4random(); if (sbput(devfd, sblock, sblock->fs_ncg) != 0) { warn("could not write updated superblock"); return (1); } } /* For each cylinder group, randomize inodes and update backup sblock */ for (cg = 0, inumber = UFS_ROOTINO; cg < (int)sblock->fs_ncg; cg++) { /* Read in inodes, then print or randomize generation nums */ dblk = fsbtodb(sblock, ino_to_fsba(sblock, inumber)); if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) { warn("can't seek to %jd", (intmax_t)dblk * bsize); return (1); } else if ((n = read(devfd, inodebuf, ibufsize)) != ibufsize) { warnx("can't read inodes: %s", (n < ibufsize) ? "short read" : strerror(errno)); return (1); } dp1 = (struct ufs1_dinode *)(void *)inodebuf; dp2 = (struct ufs2_dinode *)(void *)inodebuf; for (n = cg > 0 ? 0 : UFS_ROOTINO; n < (int)sblock->fs_ipg; n++, inumber++) { if (printonly) { (void)printf("ino %ju gen %08x\n", (uintmax_t)inumber, sblock->fs_magic == FS_UFS1_MAGIC ? dp1->di_gen : dp2->di_gen); } else if (sblock->fs_magic == FS_UFS1_MAGIC) { dp1->di_gen = arc4random(); dp1++; } else { dp2->di_gen = arc4random(); ffs_update_dinode_ckhash(sblock, dp2); dp2++; } } /* Write out modified inodes */ if (!printonly) { if (lseek(devfd, (off_t)dblk * bsize, SEEK_SET) < 0) { warn("can't seek to %jd", (intmax_t)dblk * bsize); return (1); } else if ((n = write(devfd, inodebuf, ibufsize)) != ibufsize) { warnx("can't write inodes: %s", (n != ibufsize) ? "short write" : strerror(errno)); return (1); } } } (void)close(devfd); return(0); } static void usage(void) { (void)fprintf(stderr, "usage: fsirand [-b] [-f] [-p] special [special ...]\n"); exit(1); } diff --git a/sbin/growfs/debug.c b/sbin/growfs/debug.c index e0dfc997fcf6..456e67dbc5c2 100644 --- a/sbin/growfs/debug.c +++ b/sbin/growfs/debug.c @@ -1,844 +1,839 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz * Copyright (c) 1980, 1989, 1993 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgment: * This product includes software developed by the University of * California, Berkeley and its contributors, as well as Christoph * Herrmann and Thomas-Henning von Kamptz. * 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. * * $TSHeader: src/sbin/growfs/debug.c,v 1.3 2000/12/12 19:31:00 tomsoft Exp $ * */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include "debug.h" #ifdef FS_DEBUG static FILE *dbg_log = NULL; static unsigned int indent = 0; /* * prototypes not done here, as they come with debug.h */ /* * Open the filehandle where all debug output has to go. */ void dbg_open(const char *fn) { if (strcmp(fn, "-") == 0) dbg_log = fopen("/dev/stdout", "a"); else dbg_log = fopen(fn, "a"); return; } /* * Close the filehandle where all debug output went to. */ void dbg_close(void) { if (dbg_log) { fclose(dbg_log); dbg_log = NULL; } return; } /* * Dump out a full file system block in hex. */ void dbg_dump_hex(struct fs *sb, const char *comment, unsigned char *mem) { int i, j, k; if (!dbg_log) return; fprintf(dbg_log, "===== START HEXDUMP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)mem, comment); indent++; for (i = 0; i < sb->fs_bsize; i += 24) { for (j = 0; j < 3; j++) { for (k = 0; k < 8; k++) fprintf(dbg_log, "%02x ", *mem++); fprintf(dbg_log, " "); } fprintf(dbg_log, "\n"); } indent--; fprintf(dbg_log, "===== END HEXDUMP =====\n"); return; } /* * Dump the superblock. */ void dbg_dump_fs(struct fs *sb, const char *comment) { int j; if (!dbg_log) return; fprintf(dbg_log, "===== START SUPERBLOCK =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)sb, comment); indent++; fprintf(dbg_log, "sblkno int32_t 0x%08x\n", sb->fs_sblkno); fprintf(dbg_log, "cblkno int32_t 0x%08x\n", sb->fs_cblkno); fprintf(dbg_log, "iblkno int32_t 0x%08x\n", sb->fs_iblkno); fprintf(dbg_log, "dblkno int32_t 0x%08x\n", sb->fs_dblkno); fprintf(dbg_log, "old_cgoffset int32_t 0x%08x\n", sb->fs_old_cgoffset); fprintf(dbg_log, "old_cgmask int32_t 0x%08x\n", sb->fs_old_cgmask); fprintf(dbg_log, "old_time int32_t %10u\n", (unsigned int)sb->fs_old_time); fprintf(dbg_log, "old_size int32_t 0x%08x\n", sb->fs_old_size); fprintf(dbg_log, "old_dsize int32_t 0x%08x\n", sb->fs_old_dsize); fprintf(dbg_log, "ncg int32_t 0x%08x\n", sb->fs_ncg); fprintf(dbg_log, "bsize int32_t 0x%08x\n", sb->fs_bsize); fprintf(dbg_log, "fsize int32_t 0x%08x\n", sb->fs_fsize); fprintf(dbg_log, "frag int32_t 0x%08x\n", sb->fs_frag); fprintf(dbg_log, "minfree int32_t 0x%08x\n", sb->fs_minfree); fprintf(dbg_log, "old_rotdelay int32_t 0x%08x\n", sb->fs_old_rotdelay); fprintf(dbg_log, "old_rps int32_t 0x%08x\n", sb->fs_old_rps); fprintf(dbg_log, "bmask int32_t 0x%08x\n", sb->fs_bmask); fprintf(dbg_log, "fmask int32_t 0x%08x\n", sb->fs_fmask); fprintf(dbg_log, "bshift int32_t 0x%08x\n", sb->fs_bshift); fprintf(dbg_log, "fshift int32_t 0x%08x\n", sb->fs_fshift); fprintf(dbg_log, "maxcontig int32_t 0x%08x\n", sb->fs_maxcontig); fprintf(dbg_log, "maxbpg int32_t 0x%08x\n", sb->fs_maxbpg); fprintf(dbg_log, "fragshift int32_t 0x%08x\n", sb->fs_fragshift); fprintf(dbg_log, "fsbtodb int32_t 0x%08x\n", sb->fs_fsbtodb); fprintf(dbg_log, "sbsize int32_t 0x%08x\n", sb->fs_sbsize); fprintf(dbg_log, "spare1 int32_t[2] 0x%08x 0x%08x\n", sb->fs_spare1[0], sb->fs_spare1[1]); fprintf(dbg_log, "nindir int32_t 0x%08x\n", sb->fs_nindir); fprintf(dbg_log, "inopb int32_t 0x%08x\n", sb->fs_inopb); fprintf(dbg_log, "old_nspf int32_t 0x%08x\n", sb->fs_old_nspf); fprintf(dbg_log, "optim int32_t 0x%08x\n", sb->fs_optim); fprintf(dbg_log, "old_npsect int32_t 0x%08x\n", sb->fs_old_npsect); fprintf(dbg_log, "old_interleave int32_t 0x%08x\n", sb->fs_old_interleave); fprintf(dbg_log, "old_trackskew int32_t 0x%08x\n", sb->fs_old_trackskew); fprintf(dbg_log, "id int32_t[2] 0x%08x 0x%08x\n", sb->fs_id[0], sb->fs_id[1]); fprintf(dbg_log, "old_csaddr int32_t 0x%08x\n", sb->fs_old_csaddr); fprintf(dbg_log, "cssize int32_t 0x%08x\n", sb->fs_cssize); fprintf(dbg_log, "cgsize int32_t 0x%08x\n", sb->fs_cgsize); fprintf(dbg_log, "spare2 int32_t 0x%08x\n", sb->fs_spare2); fprintf(dbg_log, "old_nsect int32_t 0x%08x\n", sb->fs_old_nsect); fprintf(dbg_log, "old_spc int32_t 0x%08x\n", sb->fs_old_spc); fprintf(dbg_log, "old_ncyl int32_t 0x%08x\n", sb->fs_old_ncyl); fprintf(dbg_log, "old_cpg int32_t 0x%08x\n", sb->fs_old_cpg); fprintf(dbg_log, "ipg int32_t 0x%08x\n", sb->fs_ipg); fprintf(dbg_log, "fpg int32_t 0x%08x\n", sb->fs_fpg); dbg_dump_csum("internal old_cstotal", &sb->fs_old_cstotal); fprintf(dbg_log, "fmod int8_t 0x%02x\n", sb->fs_fmod); fprintf(dbg_log, "clean int8_t 0x%02x\n", sb->fs_clean); fprintf(dbg_log, "ronly int8_t 0x%02x\n", sb->fs_ronly); fprintf(dbg_log, "old_flags int8_t 0x%02x\n", sb->fs_old_flags); fprintf(dbg_log, "fsmnt u_char[MAXMNTLEN] \"%s\"\n", sb->fs_fsmnt); fprintf(dbg_log, "volname u_char[MAXVOLLEN] \"%s\"\n", sb->fs_volname); fprintf(dbg_log, "swuid u_int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_swuid))[1], ((unsigned int *)&(sb->fs_swuid))[0]); fprintf(dbg_log, "pad int32_t 0x%08x\n", sb->fs_pad); fprintf(dbg_log, "cgrotor int32_t 0x%08x\n", sb->fs_cgrotor); /* * struct csum[MAXCSBUFS] - is only maintained in memory */ /* fprintf(dbg_log, " int32_t\n", sb->*fs_maxcluster);*/ fprintf(dbg_log, "old_cpc int32_t 0x%08x\n", sb->fs_old_cpc); /* * int16_t fs_opostbl[16][8] - is dumped when used in dbg_dump_sptbl */ fprintf(dbg_log, "maxbsize int32_t 0x%08x\n", sb->fs_maxbsize); fprintf(dbg_log, "unrefs int64_t 0x%08jx\n", sb->fs_unrefs); fprintf(dbg_log, "sblockloc int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_sblockloc))[1], ((unsigned int *)&(sb->fs_sblockloc))[0]); dbg_dump_csum_total("internal cstotal", &sb->fs_cstotal); fprintf(dbg_log, "time ufs_time_t %10u\n", (unsigned int)sb->fs_time); fprintf(dbg_log, "size int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_size))[1], ((unsigned int *)&(sb->fs_size))[0]); fprintf(dbg_log, "dsize int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_dsize))[1], ((unsigned int *)&(sb->fs_dsize))[0]); fprintf(dbg_log, "csaddr ufs2_daddr_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_csaddr))[1], ((unsigned int *)&(sb->fs_csaddr))[0]); fprintf(dbg_log, "pendingblocks int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_pendingblocks))[1], ((unsigned int *)&(sb->fs_pendingblocks))[0]); fprintf(dbg_log, "pendinginodes int32_t 0x%08x\n", sb->fs_pendinginodes); for (j = 0; j < FSMAXSNAP; j++) { fprintf(dbg_log, "snapinum int32_t[%2d] 0x%08x\n", j, sb->fs_snapinum[j]); if (!sb->fs_snapinum[j]) { /* list is dense */ break; } } fprintf(dbg_log, "avgfilesize int32_t 0x%08x\n", sb->fs_avgfilesize); fprintf(dbg_log, "avgfpdir int32_t 0x%08x\n", sb->fs_avgfpdir); fprintf(dbg_log, "save_cgsize int32_t 0x%08x\n", sb->fs_save_cgsize); fprintf(dbg_log, "flags int32_t 0x%08x\n", sb->fs_flags); fprintf(dbg_log, "contigsumsize int32_t 0x%08x\n", sb->fs_contigsumsize); fprintf(dbg_log, "maxsymlinklen int32_t 0x%08x\n", sb->fs_maxsymlinklen); fprintf(dbg_log, "old_inodefmt int32_t 0x%08x\n", sb->fs_old_inodefmt); fprintf(dbg_log, "maxfilesize u_int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_maxfilesize))[1], ((unsigned int *)&(sb->fs_maxfilesize))[0]); fprintf(dbg_log, "qbmask int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_qbmask))[1], ((unsigned int *)&(sb->fs_qbmask))[0]); fprintf(dbg_log, "qfmask int64_t 0x%08x%08x\n", ((unsigned int *)&(sb->fs_qfmask))[1], ((unsigned int *)&(sb->fs_qfmask))[0]); fprintf(dbg_log, "state int32_t 0x%08x\n", sb->fs_state); fprintf(dbg_log, "old_postblformat int32_t 0x%08x\n", sb->fs_old_postblformat); fprintf(dbg_log, "old_nrpos int32_t 0x%08x\n", sb->fs_old_nrpos); fprintf(dbg_log, "spare5 int32_t[2] 0x%08x 0x%08x\n", sb->fs_spare5[0], sb->fs_spare5[1]); fprintf(dbg_log, "magic int32_t 0x%08x\n", sb->fs_magic); indent--; fprintf(dbg_log, "===== END SUPERBLOCK =====\n"); return; } /* * Dump a cylinder group. */ void dbg_dump_cg(const char *comment, struct cg *cgr) { int j; if (!dbg_log) return; fprintf(dbg_log, "===== START CYLINDER GROUP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); indent++; fprintf(dbg_log, "magic int32_t 0x%08x\n", cgr->cg_magic); fprintf(dbg_log, "old_time int32_t 0x%08x\n", cgr->cg_old_time); fprintf(dbg_log, "cgx int32_t 0x%08x\n", cgr->cg_cgx); fprintf(dbg_log, "old_ncyl int16_t 0x%04x\n", cgr->cg_old_ncyl); fprintf(dbg_log, "old_niblk int16_t 0x%04x\n", cgr->cg_old_niblk); fprintf(dbg_log, "ndblk int32_t 0x%08x\n", cgr->cg_ndblk); dbg_dump_csum("internal cs", &cgr->cg_cs); fprintf(dbg_log, "rotor int32_t 0x%08x\n", cgr->cg_rotor); fprintf(dbg_log, "frotor int32_t 0x%08x\n", cgr->cg_frotor); fprintf(dbg_log, "irotor int32_t 0x%08x\n", cgr->cg_irotor); for (j = 0; j < MAXFRAG; j++) { fprintf(dbg_log, "frsum int32_t[%d] 0x%08x\n", j, cgr->cg_frsum[j]); } fprintf(dbg_log, "old_btotoff int32_t 0x%08x\n", cgr->cg_old_btotoff); fprintf(dbg_log, "old_boff int32_t 0x%08x\n", cgr->cg_old_boff); fprintf(dbg_log, "iusedoff int32_t 0x%08x\n", cgr->cg_iusedoff); fprintf(dbg_log, "freeoff int32_t 0x%08x\n", cgr->cg_freeoff); fprintf(dbg_log, "nextfreeoff int32_t 0x%08x\n", cgr->cg_nextfreeoff); fprintf(dbg_log, "clustersumoff int32_t 0x%08x\n", cgr->cg_clustersumoff); fprintf(dbg_log, "clusteroff int32_t 0x%08x\n", cgr->cg_clusteroff); fprintf(dbg_log, "nclusterblks int32_t 0x%08x\n", cgr->cg_nclusterblks); fprintf(dbg_log, "niblk int32_t 0x%08x\n", cgr->cg_niblk); fprintf(dbg_log, "initediblk int32_t 0x%08x\n", cgr->cg_initediblk); fprintf(dbg_log, "unrefs int32_t 0x%08x\n", cgr->cg_unrefs); fprintf(dbg_log, "time ufs_time_t %10u\n", (unsigned int)cgr->cg_initediblk); indent--; fprintf(dbg_log, "===== END CYLINDER GROUP =====\n"); return; } /* * Dump a cylinder summary. */ void dbg_dump_csum(const char *comment, struct csum *cs) { if (!dbg_log) return; fprintf(dbg_log, "===== START CYLINDER SUMMARY =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cs, comment); indent++; fprintf(dbg_log, "ndir int32_t 0x%08x\n", cs->cs_ndir); fprintf(dbg_log, "nbfree int32_t 0x%08x\n", cs->cs_nbfree); fprintf(dbg_log, "nifree int32_t 0x%08x\n", cs->cs_nifree); fprintf(dbg_log, "nffree int32_t 0x%08x\n", cs->cs_nffree); indent--; fprintf(dbg_log, "===== END CYLINDER SUMMARY =====\n"); return; } /* * Dump a cylinder summary. */ void dbg_dump_csum_total(const char *comment, struct csum_total *cs) { if (!dbg_log) return; fprintf(dbg_log, "===== START CYLINDER SUMMARY TOTAL =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cs, comment); indent++; fprintf(dbg_log, "ndir int64_t 0x%08x%08x\n", ((unsigned int *)&(cs->cs_ndir))[1], ((unsigned int *)&(cs->cs_ndir))[0]); fprintf(dbg_log, "nbfree int64_t 0x%08x%08x\n", ((unsigned int *)&(cs->cs_nbfree))[1], ((unsigned int *)&(cs->cs_nbfree))[0]); fprintf(dbg_log, "nifree int64_t 0x%08x%08x\n", ((unsigned int *)&(cs->cs_nifree))[1], ((unsigned int *)&(cs->cs_nifree))[0]); fprintf(dbg_log, "nffree int64_t 0x%08x%08x\n", ((unsigned int *)&(cs->cs_nffree))[1], ((unsigned int *)&(cs->cs_nffree))[0]); fprintf(dbg_log, "numclusters int64_t 0x%08x%08x\n", ((unsigned int *)&(cs->cs_numclusters))[1], ((unsigned int *)&(cs->cs_numclusters))[0]); indent--; fprintf(dbg_log, "===== END CYLINDER SUMMARY TOTAL =====\n"); return; } /* * Dump the inode allocation map in one cylinder group. */ void dbg_dump_inmap(struct fs *sb, const char *comment, struct cg *cgr) { int j,k,l,e; unsigned char *cp; if (!dbg_log) return; fprintf(dbg_log, "===== START INODE ALLOCATION MAP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); indent++; cp = (unsigned char *)cg_inosused(cgr); e = sb->fs_ipg / 8; for (j = 0; j < e; j += 32) { fprintf(dbg_log, "%08x: ", j); for (k = 0; k < 32; k += 8) { if (j + k + 8 < e) { fprintf(dbg_log, "%02x%02x%02x%02x%02x%02x%02x%02x ", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); } else { for (l = 0; (l < 8) && (j + k + l < e); l++) { fprintf(dbg_log, "%02x", cp[l]); } } cp += 8; } fprintf(dbg_log, "\n"); } indent--; fprintf(dbg_log, "===== END INODE ALLOCATION MAP =====\n"); return; } /* * Dump the fragment allocation map in one cylinder group. */ void dbg_dump_frmap(struct fs *sb, const char *comment, struct cg *cgr) { int j,k,l,e; unsigned char *cp; if (!dbg_log) return; fprintf(dbg_log, "===== START FRAGMENT ALLOCATION MAP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); indent++; cp = (unsigned char *)cg_blksfree(cgr); if (sb->fs_old_nspf) e = howmany(sb->fs_old_cpg * sb->fs_old_spc / sb->fs_old_nspf, CHAR_BIT); else e = 0; for (j = 0; j < e; j += 32) { fprintf(dbg_log, "%08x: ", j); for (k = 0; k < 32; k += 8) { if (j + k + 8 fs_old_nspf) e = howmany(sb->fs_old_cpg * sb->fs_old_spc / (sb->fs_old_nspf << sb->fs_fragshift), CHAR_BIT); else e = 0; for (j = 0; j < e; j += 32) { fprintf(dbg_log, "%08x: ", j); for (k = 0; k < 32; k += 8) { if (j + k + 8 < e) { fprintf(dbg_log, "%02x%02x%02x%02x%02x%02x%02x%02x ", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); } else { for (l = 0; (l < 8) && (j + k + l fs_contigsumsize; j++) { fprintf(dbg_log, "%02d: %8d\n", j, *ip++); } indent--; fprintf(dbg_log, "===== END CLUSTER SUMMARY =====\n"); return; } #ifdef NOT_CURRENTLY /* * This code dates from before the UFS2 integration, and doesn't compile * post-UFS2 due to the use of cg_blks(). I'm not sure how best to update * this for UFS2, where the rotational bits of UFS no longer apply, so * will leave it disabled for now; it should probably be re-enabled * specifically for UFS1. */ /* * Dump the block summary, and the rotational layout table. */ void dbg_dump_sptbl(struct fs *sb, const char *comment, struct cg *cgr) { int j,k; int *ip; if (!dbg_log) return; fprintf(dbg_log, "===== START BLOCK SUMMARY AND POSITION TABLE =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); indent++; ip = (int *)cg_blktot(cgr); for (j = 0; j < sb->fs_old_cpg; j++) { fprintf(dbg_log, "%2d: %5d = ", j, *ip++); for (k = 0; k < sb->fs_old_nrpos; k++) { fprintf(dbg_log, "%4d", cg_blks(sb, cgr, j)[k]); if (k < sb->fs_old_nrpos - 1) fprintf(dbg_log, " + "); } fprintf(dbg_log, "\n"); } indent--; fprintf(dbg_log, "===== END BLOCK SUMMARY AND POSITION TABLE =====\n"); return; } #endif /* * Dump a UFS1 inode structure. */ void dbg_dump_ufs1_ino(struct fs *sb, const char *comment, struct ufs1_dinode *ino) { int ictr; int remaining_blocks; if (!dbg_log) return; fprintf(dbg_log, "===== START UFS1 INODE DUMP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)ino, comment); indent++; fprintf(dbg_log, "mode u_int16_t 0%o\n", ino->di_mode); fprintf(dbg_log, "nlink int16_t 0x%04x\n", ino->di_nlink); fprintf(dbg_log, "size u_int64_t 0x%08x%08x\n", ((unsigned int *)&(ino->di_size))[1], ((unsigned int *)&(ino->di_size))[0]); fprintf(dbg_log, "atime int32_t 0x%08x\n", ino->di_atime); fprintf(dbg_log, "atimensec int32_t 0x%08x\n", ino->di_atimensec); fprintf(dbg_log, "mtime int32_t 0x%08x\n", ino->di_mtime); fprintf(dbg_log, "mtimensec int32_t 0x%08x\n", ino->di_mtimensec); fprintf(dbg_log, "ctime int32_t 0x%08x\n", ino->di_ctime); fprintf(dbg_log, "ctimensec int32_t 0x%08x\n", ino->di_ctimensec); remaining_blocks = howmany(ino->di_size, sb->fs_bsize); /* XXX ts - +1? */ for (ictr = 0; ictr < MIN(UFS_NDADDR, remaining_blocks); ictr++) { fprintf(dbg_log, "db ufs_daddr_t[%x] 0x%08x\n", ictr, ino->di_db[ictr]); } remaining_blocks -= UFS_NDADDR; if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs_daddr_t[0] 0x%08x\n", ino->di_ib[0]); } remaining_blocks -= howmany(sb->fs_bsize, sizeof(ufs1_daddr_t)); if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs_daddr_t[1] 0x%08x\n", ino->di_ib[1]); } #define SQUARE(a) ((a) * (a)) remaining_blocks -= SQUARE(howmany(sb->fs_bsize, sizeof(ufs1_daddr_t))); #undef SQUARE if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs_daddr_t[2] 0x%08x\n", ino->di_ib[2]); } fprintf(dbg_log, "flags u_int32_t 0x%08x\n", ino->di_flags); fprintf(dbg_log, "blocks int32_t 0x%08x\n", ino->di_blocks); fprintf(dbg_log, "gen int32_t 0x%08x\n", ino->di_gen); fprintf(dbg_log, "uid u_int32_t 0x%08x\n", ino->di_uid); fprintf(dbg_log, "gid u_int32_t 0x%08x\n", ino->di_gid); indent--; fprintf(dbg_log, "===== END UFS1 INODE DUMP =====\n"); return; } /* * Dump a UFS2 inode structure. */ void dbg_dump_ufs2_ino(struct fs *sb, const char *comment, struct ufs2_dinode *ino) { int ictr; int remaining_blocks; if (!dbg_log) return; fprintf(dbg_log, "===== START UFS2 INODE DUMP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)ino, comment); indent++; fprintf(dbg_log, "mode u_int16_t 0%o\n", ino->di_mode); fprintf(dbg_log, "nlink int16_t 0x%04x\n", ino->di_nlink); fprintf(dbg_log, "uid u_int32_t 0x%08x\n", ino->di_uid); fprintf(dbg_log, "gid u_int32_t 0x%08x\n", ino->di_gid); fprintf(dbg_log, "blksize u_int32_t 0x%08x\n", ino->di_blksize); fprintf(dbg_log, "size u_int64_t 0x%08x%08x\n", ((unsigned int *)&(ino->di_size))[1], ((unsigned int *)&(ino->di_size))[0]); fprintf(dbg_log, "blocks u_int64_t 0x%08x%08x\n", ((unsigned int *)&(ino->di_blocks))[1], ((unsigned int *)&(ino->di_blocks))[0]); fprintf(dbg_log, "atime ufs_time_t %10jd\n", ino->di_atime); fprintf(dbg_log, "mtime ufs_time_t %10jd\n", ino->di_mtime); fprintf(dbg_log, "ctime ufs_time_t %10jd\n", ino->di_ctime); fprintf(dbg_log, "birthtime ufs_time_t %10jd\n", ino->di_birthtime); fprintf(dbg_log, "mtimensec int32_t 0x%08x\n", ino->di_mtimensec); fprintf(dbg_log, "atimensec int32_t 0x%08x\n", ino->di_atimensec); fprintf(dbg_log, "ctimensec int32_t 0x%08x\n", ino->di_ctimensec); fprintf(dbg_log, "birthnsec int32_t 0x%08x\n", ino->di_birthnsec); fprintf(dbg_log, "gen int32_t 0x%08x\n", ino->di_gen); fprintf(dbg_log, "kernflags u_int32_t 0x%08x\n", ino->di_kernflags); fprintf(dbg_log, "flags u_int32_t 0x%08x\n", ino->di_flags); fprintf(dbg_log, "extsize u_int32_t 0x%08x\n", ino->di_extsize); /* XXX: What do we do with di_extb[UFS_NXADDR]? */ remaining_blocks = howmany(ino->di_size, sb->fs_bsize); /* XXX ts - +1? */ for (ictr = 0; ictr < MIN(UFS_NDADDR, remaining_blocks); ictr++) { fprintf(dbg_log, "db ufs2_daddr_t[%x] 0x%16jx\n", ictr, ino->di_db[ictr]); } remaining_blocks -= UFS_NDADDR; if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs2_daddr_t[0] 0x%16jx\n", ino->di_ib[0]); } remaining_blocks -= howmany(sb->fs_bsize, sizeof(ufs2_daddr_t)); if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs2_daddr_t[1] 0x%16jx\n", ino->di_ib[1]); } #define SQUARE(a) ((a) * (a)) remaining_blocks -= SQUARE(howmany(sb->fs_bsize, sizeof(ufs2_daddr_t))); #undef SQUARE if (remaining_blocks > 0) { fprintf(dbg_log, "ib ufs2_daddr_t[2] 0x%16jx\n", ino->di_ib[2]); } indent--; fprintf(dbg_log, "===== END UFS2 INODE DUMP =====\n"); return; } /* * Dump an indirect block. The iteration to dump a full file has to be * written around. */ void dbg_dump_iblk(struct fs *sb, const char *comment, char *block, size_t length) { unsigned int *mem, i, j, size; if (!dbg_log) return; fprintf(dbg_log, "===== START INDIRECT BLOCK DUMP =====\n"); fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)block, comment); indent++; if (sb->fs_magic == FS_UFS1_MAGIC) size = sizeof(ufs1_daddr_t); else size = sizeof(ufs2_daddr_t); mem = (unsigned int *)block; for (i = 0; (size_t)i < MIN(howmany(sb->fs_bsize, size), length); i += 8) { fprintf(dbg_log, "%04x: ", i); for (j = 0; j < 8; j++) { if ((size_t)(i + j) < length) fprintf(dbg_log, "%08X ", *mem++); } fprintf(dbg_log, "\n"); } indent--; fprintf(dbg_log, "===== END INDIRECT BLOCK DUMP =====\n"); return; } #endif /* FS_DEBUG */ diff --git a/sbin/ifconfig/af_inet.c b/sbin/ifconfig/af_inet.c index bcf2a8ab0ffd..5e3084165b33 100644 --- a/sbin/ifconfig/af_inet.c +++ b/sbin/ifconfig/af_inet.c @@ -1,582 +1,577 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #include "ifconfig_netlink.h" #ifdef WITHOUT_NETLINK static struct in_aliasreq in_addreq; static struct ifreq in_ridreq; #else struct in_px { struct in_addr addr; int plen; bool addrset; bool maskset; }; struct in_pdata { struct in_px addr; struct in_px dst_addr; struct in_px brd_addr; uint32_t flags; uint32_t vhid; }; static struct in_pdata in_add, in_del; #endif static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/ extern char *f_inet, *f_addr; static void print_addr(struct sockaddr_in *sin) { int error, n_flags; if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0) n_flags = 0; else if (f_addr != NULL && strcmp(f_addr, "host") == 0) n_flags = NI_NOFQDN; else n_flags = NI_NUMERICHOST; error = getnameinfo((struct sockaddr *)sin, sin->sin_len, addr_buf, sizeof(addr_buf), NULL, 0, n_flags); if (error) inet_ntop(AF_INET, &sin->sin_addr, addr_buf, sizeof(addr_buf)); printf("\tinet %s", addr_buf); } #ifdef WITHOUT_NETLINK static void in_status(if_ctx *ctx __unused, const struct ifaddrs *ifa) { struct sockaddr_in *sin, null_sin = {}; sin = satosin(ifa->ifa_addr); if (sin == NULL) return; print_addr(sin); if (ifa->ifa_flags & IFF_POINTOPOINT) { sin = satosin(ifa->ifa_dstaddr); if (sin == NULL) sin = &null_sin; printf(" --> %s", inet_ntoa(sin->sin_addr)); } sin = satosin(ifa->ifa_netmask); if (sin == NULL) sin = &null_sin; if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) { int cidr = 32; unsigned long smask; smask = ntohl(sin->sin_addr.s_addr); while ((smask & 1) == 0) { smask = smask >> 1; cidr--; if (cidr == 0) break; } printf("/%d", cidr); } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0) printf(" netmask %s", inet_ntoa(sin->sin_addr)); else printf(" netmask 0x%lx", (unsigned long)ntohl(sin->sin_addr.s_addr)); if (ifa->ifa_flags & IFF_BROADCAST) { sin = satosin(ifa->ifa_broadaddr); if (sin != NULL && sin->sin_addr.s_addr != 0) printf(" broadcast %s", inet_ntoa(sin->sin_addr)); } print_vhid(ifa); putchar('\n'); } #else static struct in_addr get_mask(int plen) { struct in_addr a; a.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0); return (a); } static void in_status_nl(if_ctx *ctx __unused, if_link_t *link, if_addr_t *ifa) { struct sockaddr_in *sin = satosin(ifa->ifa_local); int plen = ifa->ifa_prefixlen; print_addr(sin); if (link->ifi_flags & IFF_POINTOPOINT) { struct sockaddr_in *dst = satosin(ifa->ifa_address); printf(" --> %s", inet_ntoa(dst->sin_addr)); } if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) { printf("/%d", plen); } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0) printf(" netmask %s", inet_ntoa(get_mask(plen))); else printf(" netmask 0x%lx", (unsigned long)ntohl(get_mask(plen).s_addr)); if ((link->ifi_flags & IFF_BROADCAST) && plen != 0) { struct sockaddr_in *brd = satosin(ifa->ifa_broadcast); if (brd != NULL) printf(" broadcast %s", inet_ntoa(brd->sin_addr)); } if (ifa->ifaf_vhid != 0) printf(" vhid %d", ifa->ifaf_vhid); putchar('\n'); } #endif #ifdef WITHOUT_NETLINK #define SIN(x) ((struct sockaddr_in *) &(x)) static struct sockaddr_in *sintab[] = { SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr), SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr) }; static void in_copyaddr(if_ctx *ctx __unused, int to, int from) { memcpy(sintab[to], sintab[from], sizeof(struct sockaddr_in)); } static void in_getaddr(const char *s, int which) { struct sockaddr_in *sin = sintab[which]; struct hostent *hp; struct netent *np; sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { const char *errstr; /* address is `name/masklen' */ int masklen = 0; struct sockaddr_in *min = sintab[MASK]; *p = '\0'; if (!isdigit(*(p + 1))) errstr = "invalid"; else masklen = (int)strtonum(p + 1, 0, 32, &errstr); if (errstr != NULL) { *p = '/'; errx(1, "%s: bad value (width %s)", s, errstr); } min->sin_family = AF_INET; min->sin_len = sizeof(*min); min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) & 0xffffffff); } } if (inet_aton(s, &sin->sin_addr)) return; if ((hp = gethostbyname(s)) != NULL) bcopy(hp->h_addr, (char *)&sin->sin_addr, MIN((size_t)hp->h_length, sizeof(sin->sin_addr))); else if ((np = getnetbyname(s)) != NULL) sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); else errx(1, "%s: bad value", s); } #else static struct in_px *sintab_nl[] = { &in_del.addr, /* RIDADDR */ &in_add.addr, /* ADDR */ NULL, /* MASK */ &in_add.dst_addr, /* DSTADDR*/ &in_add.brd_addr, /* BRDADDR*/ }; static void in_copyaddr(if_ctx *ctx __unused, int to, int from) { sintab_nl[to]->addr = sintab_nl[from]->addr; sintab_nl[to]->addrset = sintab_nl[from]->addrset; } static void in_getip(const char *addr_str, struct in_addr *ip) { struct hostent *hp; struct netent *np; if (inet_aton(addr_str, ip)) return; if ((hp = gethostbyname(addr_str)) != NULL) bcopy(hp->h_addr, (char *)ip, MIN((size_t)hp->h_length, sizeof(ip))); else if ((np = getnetbyname(addr_str)) != NULL) *ip = inet_makeaddr(np->n_net, INADDR_ANY); else errx(1, "%s: bad value", addr_str); } static void in_getaddr(const char *s, int which) { struct in_px *px = sintab_nl[which]; if (which == MASK) { struct in_px *px_addr = sintab_nl[ADDR]; struct in_addr mask = {}; in_getip(s, &mask); px_addr->plen = __bitcount32(mask.s_addr); px_addr->maskset = true; return; } if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { const char *errstr; /* address is `name/masklen' */ int masklen; *p = '\0'; if (!isdigit(*(p + 1))) errstr = "invalid"; else masklen = (int)strtonum(p + 1, 0, 32, &errstr); if (errstr != NULL) { *p = '/'; errx(1, "%s: bad value (width %s)", s, errstr); } px->plen = masklen; px->maskset = true; } } in_getip(s, &px->addr); px->addrset = true; } /* * Deletes the first found IPv4 interface address for the interface. * * This function provides SIOCDIFADDR semantics missing in Netlink. * When no valid IPv4 address is specified (sin_family or sin_len is wrong) to * the SIOCDIFADDR call, it deletes the first found IPv4 address on the interface. * 'ifconfig IFNAME inet addr/prefix' relies on that behavior, as it * executes empty SIOCDIFADDR before adding a new address. */ static int in_delete_first_nl(if_ctx *ctx) { struct nlmsghdr *hdr; struct ifaddrmsg *ifahdr; uint32_t nlmsg_seq; struct in_addr addr; struct snl_writer nw = {}; struct snl_errmsg_data e = {}; struct snl_state *ss = ctx->io_ss; bool found = false; uint32_t ifindex = if_nametoindex_nl(ss, ctx->ifname); if (ifindex == 0) { /* No interface with the desired name, nothing to delete */ return (EADDRNOTAVAIL); } snl_init_writer(ss, &nw); hdr = snl_create_msg_request(&nw, NL_RTM_GETADDR); hdr->nlmsg_flags |= NLM_F_DUMP; ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET; ifahdr->ifa_index = ifindex; if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (EINVAL); nlmsg_seq = hdr->nlmsg_seq; while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) { struct snl_parsed_addr attrs = {}; if (snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs)) { addr = satosin(attrs.ifa_local)->sin_addr; ifindex = attrs.ifa_index; found = true; break; } else return (EINVAL); } if (e.error != 0) { if (e.error_str != NULL) warnx("%s(): %s", __func__, e.error_str); return (e.error); } if (!found) return (0); /* Try to delete the found address */ snl_init_writer(ss, &nw); hdr = snl_create_msg_request(&nw, NL_RTM_DELADDR); ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET; ifahdr->ifa_index = ifindex; snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &addr); if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (EINVAL); memset(&e, 0, sizeof(e)); snl_read_reply_code(ss, hdr->nlmsg_seq, &e); if (e.error_str != NULL) warnx("%s(): %s", __func__, e.error_str); return (e.error); } static int in_exec_nl(if_ctx *ctx, unsigned long action, void *data) { struct in_pdata *pdata = (struct in_pdata *)data; struct snl_writer nw = {}; if (action == NL_RTM_DELADDR && !pdata->addr.addrset) return (in_delete_first_nl(ctx)); snl_init_writer(ctx->io_ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, action); struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET; ifahdr->ifa_prefixlen = pdata->addr.plen; ifahdr->ifa_index = if_nametoindex_nl(ctx->io_ss, ctx->ifname); snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &pdata->addr.addr); if (action == NL_RTM_NEWADDR && pdata->dst_addr.addrset) snl_add_msg_attr_ip4(&nw, IFA_ADDRESS, &pdata->dst_addr.addr); if (action == NL_RTM_NEWADDR && pdata->brd_addr.addrset) snl_add_msg_attr_ip4(&nw, IFA_BROADCAST, &pdata->brd_addr.addr); int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags); if (pdata->vhid != 0) snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); snl_end_attr_nested(&nw, off); if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ctx->io_ss, hdr)) return (0); struct snl_errmsg_data e = {}; snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e); if (e.error_str != NULL) warnx("%s(): %s", __func__, e.error_str); return (e.error); } static void in_setdefaultmask_nl(void) { struct in_px *px = sintab_nl[ADDR]; in_addr_t i = ntohl(px->addr.s_addr); /* * If netmask isn't supplied, use historical default. * This is deprecated for interfaces other than loopback * or point-to-point; warn in other cases. In the future * we should return an error rather than warning. */ if (IN_CLASSA(i)) px->plen = IN_CLASSA_NSHIFT; else if (IN_CLASSB(i)) px->plen = IN_CLASSB_NSHIFT; else px->plen = IN_CLASSC_NSHIFT; px->maskset = true; } #endif static void warn_nomask(int ifflags) { if ((ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) { warnx("WARNING: setting interface address without mask " "is deprecated,\ndefault mask may not be correct."); } } static void in_postproc(if_ctx *ctx __unused, int newaddr, int ifflags) { #ifdef WITHOUT_NETLINK if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 && newaddr) { warn_nomask(ifflags); } #else if (sintab_nl[ADDR]->addrset && !sintab_nl[ADDR]->maskset && newaddr) { warn_nomask(ifflags); in_setdefaultmask_nl(); } #endif } static void in_status_tunnel(if_ctx *ctx) { char src[NI_MAXHOST]; char dst[NI_MAXHOST]; struct ifreq ifr; const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr; memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ctx->ifname, IFNAMSIZ); if (ioctl_ctx(ctx, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0) return; if (sa->sa_family != AF_INET) return; if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) src[0] = '\0'; if (ioctl_ctx(ctx, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0) return; if (sa->sa_family != AF_INET) return; if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) dst[0] = '\0'; printf("\ttunnel inet %s --> %s\n", src, dst); } static void in_set_tunnel(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres) { struct in_aliasreq addreq; memset(&addreq, 0, sizeof(addreq)); strlcpy(addreq.ifra_name, ctx->ifname, IFNAMSIZ); memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); if (ioctl_ctx(ctx, SIOCSIFPHYADDR, &addreq) < 0) warn("SIOCSIFPHYADDR"); } static void in_set_vhid(int vhid) { #ifdef WITHOUT_NETLINK in_addreq.ifra_vhid = vhid; #else in_add.vhid = (uint32_t)vhid; #endif } static struct afswtch af_inet = { .af_name = "inet", .af_af = AF_INET, #ifdef WITHOUT_NETLINK .af_status = in_status, #else .af_status = in_status_nl, #endif .af_getaddr = in_getaddr, .af_copyaddr = in_copyaddr, .af_postproc = in_postproc, .af_status_tunnel = in_status_tunnel, .af_settunnel = in_set_tunnel, .af_setvhid = in_set_vhid, #ifdef WITHOUT_NETLINK .af_difaddr = SIOCDIFADDR, .af_aifaddr = SIOCAIFADDR, .af_ridreq = &in_ridreq, .af_addreq = &in_addreq, .af_exec = af_exec_ioctl, #else .af_difaddr = NL_RTM_DELADDR, .af_aifaddr = NL_RTM_NEWADDR, .af_ridreq = &in_del, .af_addreq = &in_add, .af_exec = in_exec_nl, #endif }; static __constructor void inet_ctor(void) { #ifndef RESCUE if (!feature_present("inet")) return; #endif af_register(&af_inet); } diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c index b3da21758982..fcd04139a8c1 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -1,793 +1,788 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Define ND6_INFINITE_LIFETIME */ #include "ifconfig.h" #include "ifconfig_netlink.h" #ifndef WITHOUT_NETLINK struct in6_px { struct in6_addr addr; int plen; bool set; }; struct in6_pdata { struct in6_px addr; struct in6_px dst_addr; struct in6_addrlifetime lifetime; uint32_t flags; uint32_t vhid; }; static struct in6_pdata in6_del; static struct in6_pdata in6_add = { .lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME }, }; #else static struct in6_ifreq in6_ridreq; static struct in6_aliasreq in6_addreq = { .ifra_flags = 0, .ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; #endif static int ip6lifetime; #ifdef WITHOUT_NETLINK static int prefix(void *, int); #endif static char *sec2str(time_t); static int explicit_prefix = 0; extern char *f_inet6, *f_addr; extern void setnd6flags(if_ctx *, const char *, int); extern void setnd6defif(if_ctx *,const char *, int); extern void nd6_status(if_ctx *); static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/ static void setifprefixlen(if_ctx *ctx __netlink_unused, const char *addr, int dummy __unused) { #ifdef WITHOUT_NETLINK const struct afswtch *afp = ctx->afp; if (afp->af_getprefix != NULL) afp->af_getprefix(addr, MASK); #else int plen = strtol(addr, NULL, 10); if ((plen < 0) || (plen > 128)) errx(1, "%s: bad value", addr); in6_add.addr.plen = plen; #endif explicit_prefix = 1; } static void setip6flags(if_ctx *ctx, const char *dummyaddr __unused, int flag) { const struct afswtch *afp = ctx->afp; if (afp->af_af != AF_INET6) err(1, "address flags can be set only for inet6 addresses"); #ifdef WITHOUT_NETLINK if (flag < 0) in6_addreq.ifra_flags &= ~(-flag); else in6_addreq.ifra_flags |= flag; #else if (flag < 0) in6_add.flags &= ~(-flag); else in6_add.flags |= flag; #endif } static void setip6lifetime(if_ctx *ctx, const char *cmd, const char *val) { const struct afswtch *afp = ctx->afp; struct timespec now; time_t newval; char *ep; #ifdef WITHOUT_NETLINK struct in6_addrlifetime *lifetime = &in6_addreq.ifra_lifetime; #else struct in6_addrlifetime *lifetime = &in6_add.lifetime; #endif clock_gettime(CLOCK_MONOTONIC_FAST, &now); newval = (time_t)strtoul(val, &ep, 0); if (val == ep) errx(1, "invalid %s", cmd); if (afp->af_af != AF_INET6) errx(1, "%s not allowed for the AF", cmd); if (strcmp(cmd, "vltime") == 0) { lifetime->ia6t_expire = now.tv_sec + newval; lifetime->ia6t_vltime = newval; } else if (strcmp(cmd, "pltime") == 0) { lifetime->ia6t_preferred = now.tv_sec + newval; lifetime->ia6t_pltime = newval; } } static void setip6pltime(if_ctx *ctx, const char *seconds, int dummy __unused) { setip6lifetime(ctx, "pltime", seconds); } static void setip6vltime(if_ctx *ctx, const char *seconds, int dummy __unused) { setip6lifetime(ctx, "vltime", seconds); } static void setip6eui64(if_ctx *ctx, const char *cmd, int dummy __unused) { const struct afswtch *afp = ctx->afp; struct ifaddrs *ifap, *ifa; const struct sockaddr_in6 *sin6 = NULL; const struct in6_addr *lladdr = NULL; struct in6_addr *in6; if (afp->af_af != AF_INET6) errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); #ifdef WITHOUT_NETLINK in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; #else in6 = &in6_add.addr.addr; #endif if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) errx(EXIT_FAILURE, "interface index is already filled"); if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_INET6 && strcmp(ifa->ifa_name, ctx->ifname) == 0) { sin6 = (const struct sockaddr_in6 *)satosin6(ifa->ifa_addr); if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { lladdr = &sin6->sin6_addr; break; } } } if (!lladdr) errx(EXIT_FAILURE, "could not determine link local address"); memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); freeifaddrs(ifap); } static void print_addr(struct sockaddr_in6 *sin) { int error, n_flags; if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0) n_flags = 0; else if (f_addr != NULL && strcmp(f_addr, "host") == 0) n_flags = NI_NOFQDN; else n_flags = NI_NUMERICHOST; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, n_flags); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf("\tinet6 %s", addr_buf); } static void print_p2p(struct sockaddr_in6 *sin) { int error; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf(" --> %s", addr_buf); } static void print_mask(int plen) { if (f_inet6 != NULL && strcmp(f_inet6, "cidr") == 0) printf("/%d", plen); else printf(" prefixlen %d", plen); } static void print_flags(int flags6) { if ((flags6 & IN6_IFF_ANYCAST) != 0) printf(" anycast"); if ((flags6 & IN6_IFF_TENTATIVE) != 0) printf(" tentative"); if ((flags6 & IN6_IFF_DUPLICATED) != 0) printf(" duplicated"); if ((flags6 & IN6_IFF_DETACHED) != 0) printf(" detached"); if ((flags6 & IN6_IFF_DEPRECATED) != 0) printf(" deprecated"); if ((flags6 & IN6_IFF_AUTOCONF) != 0) printf(" autoconf"); if ((flags6 & IN6_IFF_TEMPORARY) != 0) printf(" temporary"); if ((flags6 & IN6_IFF_PREFER_SOURCE) != 0) printf(" prefer_source"); } static void print_lifetime(const char *prepend, time_t px_time, struct timespec *now) { printf(" %s", prepend); if (px_time == 0) printf(" infty"); printf(" %s", px_time < now->tv_sec ? "0" : sec2str(px_time - now->tv_sec)); } #ifdef WITHOUT_NETLINK static void in6_status(if_ctx *ctx, const struct ifaddrs *ifa) { struct sockaddr_in6 *sin, null_sin = {}; struct in6_ifreq ifr6; int s6; u_int32_t flags6; struct in6_addrlifetime lifetime; sin = satosin6(ifa->ifa_addr); if (sin == NULL) return; strlcpy(ifr6.ifr_name, ctx->ifname, sizeof(ifr6.ifr_name)); if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_INET6,SOCK_DGRAM)"); return; } ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFAFLAG_IN6)"); close(s6); return; } flags6 = ifr6.ifr_ifru.ifru_flags6; memset(&lifetime, 0, sizeof(lifetime)); ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFALIFETIME_IN6)"); close(s6); return; } lifetime = ifr6.ifr_ifru.ifru_lifetime; close(s6); print_addr(sin); if (ifa->ifa_flags & IFF_POINTOPOINT) { sin = satosin6(ifa->ifa_dstaddr); /* * some of the interfaces do not have valid destination * address. */ if (sin != NULL && sin->sin6_family == AF_INET6) print_p2p(sin); } sin = satosin6(ifa->ifa_netmask); if (sin == NULL) sin = &null_sin; print_mask(prefix(&sin->sin6_addr, sizeof(struct in6_addr))); print_flags(flags6); if ((satosin6(ifa->ifa_addr))->sin6_scope_id) printf(" scopeid 0x%x", (satosin6(ifa->ifa_addr))->sin6_scope_id); if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { struct timespec now; clock_gettime(CLOCK_MONOTONIC_FAST, &now); print_lifetime("pltime", lifetime.ia6t_preferred, &now); print_lifetime("vltime", lifetime.ia6t_expire, &now); } print_vhid(ifa); putchar('\n'); } #else static void show_lifetime(struct ifa_cacheinfo *ci) { struct timespec now; uint32_t pl, vl; if (ci == NULL) return; int count = ci->ifa_prefered != ND6_INFINITE_LIFETIME; count += ci->ifa_valid != ND6_INFINITE_LIFETIME; if (count == 0) return; pl = (ci->ifa_prefered == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_prefered; vl = (ci->ifa_valid == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_valid; clock_gettime(CLOCK_MONOTONIC_FAST, &now); print_lifetime("pltime", pl + now.tv_sec, &now); print_lifetime("vltime", vl + now.tv_sec, &now); } static void in6_status_nl(if_ctx *ctx __unused, if_link_t *link __unused, if_addr_t *ifa) { int plen = ifa->ifa_prefixlen; uint32_t scopeid; if (ifa->ifa_local == NULL) { /* Non-P2P address */ scopeid = satosin6(ifa->ifa_address)->sin6_scope_id; print_addr(satosin6(ifa->ifa_address)); } else { scopeid = satosin6(ifa->ifa_local)->sin6_scope_id; print_addr(satosin6(ifa->ifa_local)); print_p2p(satosin6(ifa->ifa_address)); } print_mask(plen); print_flags(ifa->ifaf_flags); if (scopeid != 0) printf(" scopeid 0x%x", scopeid); show_lifetime(ifa->ifa_cacheinfo); if (ifa->ifaf_vhid != 0) printf(" vhid %d", ifa->ifaf_vhid); putchar('\n'); } static struct in6_px *sin6tab_nl[] = { &in6_del.addr, /* RIDADDR */ &in6_add.addr, /* ADDR */ NULL, /* MASK */ &in6_add.dst_addr, /* DSTADDR*/ }; static void in6_copyaddr(if_ctx *ctx __unused, int to, int from) { sin6tab_nl[to]->addr = sin6tab_nl[from]->addr; sin6tab_nl[to]->set = sin6tab_nl[from]->set; } static void in6_getaddr(const char *addr_str, int which) { struct in6_px *px = sin6tab_nl[which]; px->set = true; px->plen = 128; if (which == ADDR) { char *p = NULL; if((p = strrchr(addr_str, '/')) != NULL) { *p = '\0'; int plen = strtol(p + 1, NULL, 10); if (plen < 0 || plen > 128) errx(1, "%s: bad value", p + 1); px->plen = plen; explicit_prefix = 1; } } struct addrinfo hints = { .ai_family = AF_INET6 }; struct addrinfo *res; int error = getaddrinfo(addr_str, NULL, &hints, &res); if (error != 0) { if (inet_pton(AF_INET6, addr_str, &px->addr) != 1) errx(1, "%s: bad value", addr_str); } else { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)(void *)res->ai_addr; px->addr = sin6->sin6_addr; freeaddrinfo(res); } } static int in6_exec_nl(if_ctx *ctx, unsigned long action, void *data) { struct in6_pdata *pdata = (struct in6_pdata *)data; struct snl_writer nw = {}; snl_init_writer(ctx->io_ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, action); struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET6; ifahdr->ifa_prefixlen = pdata->addr.plen; ifahdr->ifa_index = if_nametoindex_nl(ctx->io_ss, ctx->ifname); snl_add_msg_attr_ip6(&nw, IFA_LOCAL, &pdata->addr.addr); if (action == NL_RTM_NEWADDR && pdata->dst_addr.set) snl_add_msg_attr_ip6(&nw, IFA_ADDRESS, &pdata->dst_addr.addr); struct ifa_cacheinfo ci = { .ifa_prefered = pdata->lifetime.ia6t_pltime, .ifa_valid = pdata->lifetime.ia6t_vltime, }; snl_add_msg_attr(&nw, IFA_CACHEINFO, sizeof(ci), &ci); int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags); if (pdata->vhid != 0) snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); snl_end_attr_nested(&nw, off); if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ctx->io_ss, hdr)) return (0); struct snl_errmsg_data e = {}; snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e); return (e.error); } #endif #ifdef WITHOUT_NETLINK static struct sockaddr_in6 *sin6tab[] = { &in6_ridreq.ifr_addr, &in6_addreq.ifra_addr, &in6_addreq.ifra_prefixmask, &in6_addreq.ifra_dstaddr }; static void in6_copyaddr(if_ctx *ctx __unused, int to, int from) { memcpy(sin6tab[to], sin6tab[from], sizeof(struct sockaddr_in6)); } static void in6_getprefix(const char *plen, int which) { struct sockaddr_in6 *sin = sin6tab[which]; u_char *cp; int len = atoi(plen); if ((len < 0) || (len > 128)) errx(1, "%s: bad value", plen); sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if ((len == 0) || (len == 128)) { memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); return; } memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) *cp++ = 0xff; *cp = 0xff << (8 - len); } static void in6_getaddr(const char *s, int which) { struct sockaddr_in6 *sin = sin6tab[which]; struct addrinfo hints, *res; int error = -1; sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { *p = '\0'; in6_getprefix(p + 1, MASK); explicit_prefix = 1; } } if (sin->sin6_family == AF_INET6) { bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = AF_INET6; error = getaddrinfo(s, NULL, &hints, &res); if (error != 0) { if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) errx(1, "%s: bad value", s); } else { bcopy(res->ai_addr, sin, res->ai_addrlen); freeaddrinfo(res); } } } static int prefix(void *val, int size) { u_char *name = (u_char *)val; int byte, bit, plen = 0; for (byte = 0; byte < size; byte++, plen += 8) if (name[byte] != 0xff) break; if (byte == size) return (plen); for (bit = 7; bit != 0; bit--, plen++) if (!(name[byte] & (1 << bit))) break; for (; bit != 0; bit--) if (name[byte] & (1 << bit)) return(0); byte++; for (; byte < size; byte++) if (name[byte]) return(0); return (plen); } #endif static char * sec2str(time_t total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; if (0) { days = total / 3600 / 24; hours = (total / 3600) % 24; mins = (total / 60) % 60; secs = total % 60; if (days) { first = 0; p += sprintf(p, "%dd", days); } if (!first || hours) { first = 0; p += sprintf(p, "%dh", hours); } if (!first || mins) { first = 0; p += sprintf(p, "%dm", mins); } sprintf(p, "%ds", secs); } else sprintf(result, "%lu", (unsigned long)total); return(result); } static void in6_postproc(if_ctx *ctx, int newaddr __unused, int ifflags __unused) { if (explicit_prefix == 0) { /* Aggregatable address architecture defines all prefixes are 64. So, it is convenient to set prefixlen to 64 if it is not specified. */ setifprefixlen(ctx, "64", 0); /* in6_getprefix("64", MASK) if MASK is available here... */ } } static void in6_status_tunnel(if_ctx *ctx) { char src[NI_MAXHOST]; char dst[NI_MAXHOST]; struct in6_ifreq in6_ifr; const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr; memset(&in6_ifr, 0, sizeof(in6_ifr)); strlcpy(in6_ifr.ifr_name, ctx->ifname, sizeof(in6_ifr.ifr_name)); if (ioctl_ctx(ctx, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) src[0] = '\0'; if (ioctl_ctx(ctx, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) dst[0] = '\0'; printf("\ttunnel inet6 %s --> %s\n", src, dst); } static void in6_set_tunnel(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres) { struct in6_aliasreq in6_req = {}; strlcpy(in6_req.ifra_name, ctx->ifname, sizeof(in6_req.ifra_name)); memcpy(&in6_req.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); memcpy(&in6_req.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); if (ioctl_ctx(ctx, SIOCSIFPHYADDR_IN6, &in6_req) < 0) warn("SIOCSIFPHYADDR_IN6"); } static void in6_set_vhid(int vhid) { #ifdef WITHOUT_NETLINK in6_addreq.ifra_vhid = vhid; #else in6_add.vhid = (uint32_t)vhid; #endif } static struct cmd inet6_cmds[] = { DEF_CMD_ARG("prefixlen", setifprefixlen), DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags), DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("prefer_source",IN6_IFF_PREFER_SOURCE, setip6flags), DEF_CMD("-prefer_source",-IN6_IFF_PREFER_SOURCE,setip6flags), DEF_CMD("accept_rtadv", ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("-accept_rtadv",-ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("no_radr", ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("-no_radr", -ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("defaultif", 1, setnd6defif), DEF_CMD("-defaultif", -1, setnd6defif), DEF_CMD("ifdisabled", ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("auto_linklocal",ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("-auto_linklocal",-ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("no_prefer_iface",ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("-no_prefer_iface",-ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("no_dad", ND6_IFF_NO_DAD, setnd6flags), DEF_CMD("-no_dad", -ND6_IFF_NO_DAD, setnd6flags), DEF_CMD_ARG("pltime", setip6pltime), DEF_CMD_ARG("vltime", setip6vltime), DEF_CMD("eui64", 0, setip6eui64), #ifdef EXPERIMENTAL DEF_CMD("ipv6_only", ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), DEF_CMD("-ipv6_only", -ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), #endif }; static struct afswtch af_inet6 = { .af_name = "inet6", .af_af = AF_INET6, #ifdef WITHOUT_NETLINK .af_status = in6_status, #else .af_status = in6_status_nl, #endif .af_getaddr = in6_getaddr, .af_copyaddr = in6_copyaddr, #ifdef WITHOUT_NETLINK .af_getprefix = in6_getprefix, #endif .af_other_status = nd6_status, .af_postproc = in6_postproc, .af_status_tunnel = in6_status_tunnel, .af_settunnel = in6_set_tunnel, .af_setvhid = in6_set_vhid, #ifdef WITHOUT_NETLINK .af_difaddr = SIOCDIFADDR_IN6, .af_aifaddr = SIOCAIFADDR_IN6, .af_ridreq = &in6_addreq, .af_addreq = &in6_addreq, .af_exec = af_exec_ioctl, #else .af_difaddr = NL_RTM_DELADDR, .af_aifaddr = NL_RTM_NEWADDR, .af_ridreq = &in6_add, .af_addreq = &in6_add, .af_exec = in6_exec_nl, #endif }; static void in6_Lopt_cb(const char *arg __unused) { ip6lifetime++; /* print IPv6 address lifetime */ } static struct option in6_Lopt = { .opt = "L", .opt_usage = "[-L]", .cb = in6_Lopt_cb }; static __constructor void inet6_ctor(void) { size_t i; #ifndef RESCUE if (!feature_present("inet6")) return; #endif for (i = 0; i < nitems(inet6_cmds); i++) cmd_register(&inet6_cmds[i]); af_register(&af_inet6); opt_register(&in6_Lopt); } diff --git a/sbin/ifconfig/af_link.c b/sbin/ifconfig/af_link.c index 17de87539d9a..6c23f7016806 100644 --- a/sbin/ifconfig/af_link.c +++ b/sbin/ifconfig/af_link.c @@ -1,285 +1,280 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #include "ifconfig_netlink.h" static struct ifreq link_ridreq; extern char *f_ether; static void print_ether(const struct ether_addr *addr, const char *prefix) { char *ether_format = ether_ntoa(addr); if (f_ether != NULL) { if (strcmp(f_ether, "dash") == 0) { char *format_char; while ((format_char = strchr(ether_format, ':')) != NULL) { *format_char = '-'; } } else if (strcmp(f_ether, "dotted") == 0) { /* Indices 0 and 1 is kept as is. */ ether_format[ 2] = ether_format[ 3]; ether_format[ 3] = ether_format[ 4]; ether_format[ 4] = '.'; ether_format[ 5] = ether_format[ 6]; ether_format[ 6] = ether_format[ 7]; ether_format[ 7] = ether_format[ 9]; ether_format[ 8] = ether_format[10]; ether_format[ 9] = '.'; ether_format[10] = ether_format[12]; ether_format[11] = ether_format[13]; ether_format[12] = ether_format[15]; ether_format[13] = ether_format[16]; ether_format[14] = '\0'; } } printf("\t%s %s\n", prefix, ether_format); } static void print_lladdr(struct sockaddr_dl *sdl) { if (match_ether(sdl)) { print_ether((struct ether_addr *)LLADDR(sdl), "ether"); } else { int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; printf("\tlladdr %s\n", link_ntoa(sdl) + n); } } static void print_pcp(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGLANPCP, &ifr) == 0 && ifr.ifr_lan_pcp != IFNET_PCP_NONE) printf("\tpcp %d\n", ifr.ifr_lan_pcp); } #ifdef WITHOUT_NETLINK static void link_status(if_ctx *ctx, const struct ifaddrs *ifa) { /* XXX no const 'cuz LLADDR is defined wrong */ struct sockaddr_dl *sdl; struct ifreq ifr; int rc, sock_hw; static const u_char laggaddr[6] = {0}; sdl = satosdl(ifa->ifa_addr); if (sdl == NULL || sdl->sdl_alen == 0) return; print_lladdr(sdl); /* * Best-effort (i.e. failures are silent) to get original * hardware address, as read by NIC driver at attach time. Only * applies to Ethernet NICs (IFT_ETHER). However, laggX * interfaces claim to be IFT_ETHER, and re-type their component * Ethernet NICs as IFT_IEEE8023ADLAG. So, check for both. If * the MAC is zeroed, then it's actually a lagg. */ if ((sdl->sdl_type != IFT_ETHER && sdl->sdl_type != IFT_IEEE8023ADLAG) || sdl->sdl_alen != ETHER_ADDR_LEN) return; strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)); memcpy(&ifr.ifr_addr, ifa->ifa_addr, sizeof(ifa->ifa_addr->sa_len)); ifr.ifr_addr.sa_family = AF_LOCAL; if ((sock_hw = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_LOCAL,SOCK_DGRAM)"); return; } rc = ioctl(sock_hw, SIOCGHWADDR, &ifr); close(sock_hw); if (rc != 0) return; /* * If this is definitely a lagg device or the hwaddr * matches the link addr, don't bother. */ if (memcmp(ifr.ifr_addr.sa_data, laggaddr, sdl->sdl_alen) == 0 || memcmp(ifr.ifr_addr.sa_data, LLADDR(sdl), sdl->sdl_alen) == 0) goto pcp; print_ether((const struct ether_addr *)&ifr.ifr_addr.sa_data, "hwaddr"); pcp: print_pcp(ctx); } #else static uint8_t convert_iftype(uint8_t iftype) { switch (iftype) { case IFT_IEEE8023ADLAG: return (IFT_ETHER); case IFT_INFINIBANDLAG: return (IFT_INFINIBAND); } return (iftype); } static void link_status_nl(if_ctx *ctx, if_link_t *link, if_addr_t *ifa __unused) { if (link->ifla_address != NULL) { struct sockaddr_dl sdl = { .sdl_len = sizeof(struct sockaddr_dl), .sdl_family = AF_LINK, .sdl_type = convert_iftype(link->ifi_type), .sdl_alen = NLA_DATA_LEN(link->ifla_address), }; memcpy(LLADDR(&sdl), NLA_DATA(link->ifla_address), sdl.sdl_alen); print_lladdr(&sdl); if (link->iflaf_orig_hwaddr != NULL) { struct nlattr *hwaddr = link->iflaf_orig_hwaddr; if (memcmp(NLA_DATA(hwaddr), NLA_DATA(link->ifla_address), sdl.sdl_alen)) print_ether((struct ether_addr *)NLA_DATA(hwaddr), "hwaddr"); } } if (convert_iftype(link->ifi_type) == IFT_ETHER) print_pcp(ctx); } #endif static void link_getaddr(const char *addr, int which) { char *temp; struct sockaddr_dl sdl; struct sockaddr *sa = &link_ridreq.ifr_addr; if (which != ADDR) errx(1, "can't set link-level netmask or broadcast"); if (!strcmp(addr, "random")) { sdl.sdl_len = sizeof(sdl); sdl.sdl_alen = ETHER_ADDR_LEN; sdl.sdl_nlen = 0; sdl.sdl_family = AF_LINK; arc4random_buf(&sdl.sdl_data, ETHER_ADDR_LEN); /* Non-multicast and claim it is locally administered. */ sdl.sdl_data[0] &= 0xfc; sdl.sdl_data[0] |= 0x02; } else { if ((temp = malloc(strlen(addr) + 2)) == NULL) errx(1, "malloc failed"); temp[0] = ':'; strcpy(temp + 1, addr); sdl.sdl_len = sizeof(sdl); link_addr(temp, &sdl); free(temp); } if (sdl.sdl_alen > sizeof(sa->sa_data)) errx(1, "malformed link-level address"); sa->sa_family = AF_LINK; sa->sa_len = sdl.sdl_alen; bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen); } static struct afswtch af_link = { .af_name = "link", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, .af_exec = af_exec_ioctl, }; static struct afswtch af_ether = { .af_name = "ether", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, .af_exec = af_exec_ioctl, }; static struct afswtch af_lladdr = { .af_name = "lladdr", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, .af_exec = af_exec_ioctl, }; static __constructor void link_ctor(void) { af_register(&af_link); af_register(&af_ether); af_register(&af_lladdr); } diff --git a/sbin/ifconfig/af_nd6.c b/sbin/ifconfig/af_nd6.c index 7eeb86585197..73044e95740a 100644 --- a/sbin/ifconfig/af_nd6.c +++ b/sbin/ifconfig/af_nd6.c @@ -1,170 +1,165 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009 Hiroki Sato. 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 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #define MAX_SYSCTL_TRY 5 #ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG #define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \ "\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \ "\007NO_RADR\010NO_PREFER_IFACE\011NO_DAD" \ "\012IPV6_ONLY\013IPV6_ONLY_MANUAL" \ "\020DEFAULTIF" #else #define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \ "\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \ "\007NO_RADR\010NO_PREFER_IFACE\011NO_DAD\020DEFAULTIF" #endif static int isnd6defif(if_ctx *ctx, int s); void setnd6flags(if_ctx *, const char *, int); void setnd6defif(if_ctx *,const char *, int); void nd6_status(if_ctx *); void setnd6flags(if_ctx *ctx, const char *dummyaddr __unused, int d) { struct in6_ndireq nd = {}; int error; strlcpy(nd.ifname, ctx->ifname, sizeof(nd.ifname)); error = ioctl_ctx(ctx, SIOCGIFINFO_IN6, &nd); if (error) { warn("ioctl(SIOCGIFINFO_IN6)"); return; } if (d < 0) nd.ndi.flags &= ~(-d); else nd.ndi.flags |= d; error = ioctl_ctx(ctx, SIOCSIFINFO_IN6, (caddr_t)&nd); if (error) warn("ioctl(SIOCSIFINFO_IN6)"); } void setnd6defif(if_ctx *ctx, const char *dummyaddr __unused, int d) { struct in6_ndifreq ndifreq = {}; int ifindex; int error; strlcpy(ndifreq.ifname, ctx->ifname, sizeof(ndifreq.ifname)); if (d < 0) { if (isnd6defif(ctx, ctx->io_s)) { /* ifindex = 0 means to remove default if */ ifindex = 0; } else return; } else if ((ifindex = if_nametoindex(ndifreq.ifname)) == 0) { warn("if_nametoindex(%s)", ndifreq.ifname); return; } ndifreq.ifindex = ifindex; error = ioctl_ctx(ctx, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq); if (error) warn("ioctl(SIOCSDEFIFACE_IN6)"); } static int isnd6defif(if_ctx *ctx, int s) { struct in6_ndifreq ndifreq = {}; unsigned int ifindex; int error; strlcpy(ndifreq.ifname, ctx->ifname, sizeof(ndifreq.ifname)); ifindex = if_nametoindex(ndifreq.ifname); error = ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq); if (error) { warn("ioctl(SIOCGDEFIFACE_IN6)"); return (error); } return (ndifreq.ifindex == ifindex); } void nd6_status(if_ctx *ctx) { struct in6_ndireq nd = {}; int s6; int error; int isdefif; strlcpy(nd.ifname, ctx->ifname, sizeof(nd.ifname)); if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { if (errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) warn("socket(AF_INET6, SOCK_DGRAM)"); return; } error = ioctl(s6, SIOCGIFINFO_IN6, &nd); if (error) { if (errno != EPFNOSUPPORT) warn("ioctl(SIOCGIFINFO_IN6)"); close(s6); return; } isdefif = isnd6defif(ctx, s6); close(s6); if (nd.ndi.flags == 0 && !isdefif) return; printb("\tnd6 options", (unsigned int)(nd.ndi.flags | (isdefif << 15)), ND6BITS); putchar('\n'); } diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c index 57ab0c6ae81c..2d0af1255a73 100644 --- a/sbin/ifconfig/ifbridge.c +++ b/sbin/ifconfig/ifbridge.c @@ -1,683 +1,678 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Jason R. Thorpe for Wasabi Systems, Inc. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC * 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" static const char *stpstates[] = { STP_STATES }; static const char *stpproto[] = { STP_PROTOS }; static const char *stproles[] = { STP_ROLES }; static int get_val(const char *cp, u_long *valp) { char *endptr; u_long val; errno = 0; val = strtoul(cp, &endptr, 0); if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE) return (-1); *valp = val; return (0); } static int do_cmd(if_ctx *ctx, u_long op, void *arg, size_t argsize, int set) { struct ifdrv ifd = {}; strlcpy(ifd.ifd_name, ctx->ifname, sizeof(ifd.ifd_name)); ifd.ifd_cmd = op; ifd.ifd_len = argsize; ifd.ifd_data = arg; return (ioctl_ctx(ctx, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd)); } static void do_bridgeflag(if_ctx *ctx, const char *ifs, int flag, int set) { struct ifbreq req; strlcpy(req.ifbr_ifsname, ifs, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGGIFFLGS, &req, sizeof(req), 0) < 0) err(1, "unable to get bridge flags"); if (set) req.ifbr_ifsflags |= flag; else req.ifbr_ifsflags &= ~flag; if (do_cmd(ctx, BRDGSIFFLGS, &req, sizeof(req), 1) < 0) err(1, "unable to set bridge flags"); } static void bridge_addresses(if_ctx *ctx, const char *prefix) { struct ifbaconf ifbac; struct ifbareq *ifba; char *inbuf = NULL, *ninbuf; size_t len = 8192; struct ether_addr ea; for (;;) { ninbuf = realloc(inbuf, len); if (ninbuf == NULL) err(1, "unable to allocate address buffer"); ifbac.ifbac_len = len; ifbac.ifbac_buf = inbuf = ninbuf; if (do_cmd(ctx, BRDGRTS, &ifbac, sizeof(ifbac), 0) < 0) err(1, "unable to get address cache"); if ((ifbac.ifbac_len + sizeof(*ifba)) < len) break; len *= 2; } for (unsigned long i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) { ifba = ifbac.ifbac_req + i; memcpy(ea.octet, ifba->ifba_dst, sizeof(ea.octet)); printf("%s%s Vlan%d %s %lu ", prefix, ether_ntoa(&ea), ifba->ifba_vlan, ifba->ifba_ifsname, ifba->ifba_expire); printb("flags", ifba->ifba_flags, IFBAFBITS); printf("\n"); } free(inbuf); } static void bridge_status(if_ctx *ctx) { struct ifconfig_bridge_status *bridge; struct ifbropreq *params; const char *pad, *prefix; uint8_t lladdr[ETHER_ADDR_LEN]; uint16_t bprio; if (ifconfig_bridge_get_bridge_status(lifh, ctx->ifname, &bridge) == -1) return; params = bridge->params; PV2ID(params->ifbop_bridgeid, bprio, lladdr); printf("\tid %s priority %u hellotime %u fwddelay %u\n", ether_ntoa((struct ether_addr *)lladdr), params->ifbop_priority, params->ifbop_hellotime, params->ifbop_fwddelay); printf("\tmaxage %u holdcnt %u proto %s maxaddr %u timeout %u\n", params->ifbop_maxage, params->ifbop_holdcount, stpproto[params->ifbop_protocol], bridge->cache_size, bridge->cache_lifetime); PV2ID(params->ifbop_designated_root, bprio, lladdr); printf("\troot id %s priority %d ifcost %u port %u\n", ether_ntoa((struct ether_addr *)lladdr), bprio, params->ifbop_root_path_cost, params->ifbop_root_port & 0xfff); prefix = "\tmember: "; pad = "\t "; for (size_t i = 0; i < bridge->members_count; ++i) { struct ifbreq *member = &bridge->members[i]; printf("%s%s ", prefix, member->ifbr_ifsname); printb("flags", member->ifbr_ifsflags, IFBIFBITS); printf("\n%s", pad); printf("ifmaxaddr %u port %u priority %u path cost %u", member->ifbr_addrmax, member->ifbr_portno, member->ifbr_priority, member->ifbr_path_cost); if (member->ifbr_ifsflags & IFBIF_STP) { uint8_t proto = member->ifbr_proto; uint8_t role = member->ifbr_role; uint8_t state = member->ifbr_state; if (proto < nitems(stpproto)) printf(" proto %s", stpproto[proto]); else printf(" ", proto); printf("\n%s", pad); if (role < nitems(stproles)) printf("role %s", stproles[role]); else printf("", role); if (state < nitems(stpstates)) printf(" state %s", stpstates[state]); else printf(" ", state); } printf("\n"); } ifconfig_bridge_free_bridge_status(bridge); } static void setbridge_add(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGADD, &req, sizeof(req), 1) < 0) err(1, "BRDGADD %s", val); } static void setbridge_delete(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGDEL, &req, sizeof(req), 1) < 0) err(1, "BRDGDEL %s", val); } static void setbridge_discover(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_DISCOVER, 1); } static void unsetbridge_discover(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_DISCOVER, 0); } static void setbridge_learn(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_LEARNING, 1); } static void unsetbridge_learn(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_LEARNING, 0); } static void setbridge_sticky(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STICKY, 1); } static void unsetbridge_sticky(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STICKY, 0); } static void setbridge_span(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGADDS, &req, sizeof(req), 1) < 0) err(1, "BRDGADDS %s", val); } static void unsetbridge_span(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGDELS, &req, sizeof(req), 1) < 0) err(1, "BRDGDELS %s", val); } static void setbridge_stp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STP, 1); } static void unsetbridge_stp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STP, 0); } static void setbridge_edge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_EDGE, 1); } static void unsetbridge_edge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_EDGE, 0); } static void setbridge_autoedge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOEDGE, 1); } static void unsetbridge_autoedge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOEDGE, 0); } static void setbridge_ptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_PTP, 1); } static void unsetbridge_ptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_PTP, 0); } static void setbridge_autoptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOPTP, 1); } static void unsetbridge_autoptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOPTP, 0); } static void setbridge_flush(if_ctx *ctx, const char *val __unused, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); req.ifbr_ifsflags = IFBF_FLUSHDYN; if (do_cmd(ctx, BRDGFLUSH, &req, sizeof(req), 1) < 0) err(1, "BRDGFLUSH"); } static void setbridge_flushall(if_ctx *ctx, const char *val __unused, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); req.ifbr_ifsflags = IFBF_FLUSHALL; if (do_cmd(ctx, BRDGFLUSH, &req, sizeof(req), 1) < 0) err(1, "BRDGFLUSH"); } static void setbridge_static(if_ctx *ctx, const char *val, const char *mac) { struct ifbareq req; struct ether_addr *ea; memset(&req, 0, sizeof(req)); strlcpy(req.ifba_ifsname, val, sizeof(req.ifba_ifsname)); ea = ether_aton(mac); if (ea == NULL) errx(1, "%s: invalid address: %s", val, mac); memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); req.ifba_flags = IFBAF_STATIC; req.ifba_vlan = 0; /* XXX allow user to specify */ if (do_cmd(ctx, BRDGSADDR, &req, sizeof(req), 1) < 0) err(1, "BRDGSADDR %s", val); } static void setbridge_deladdr(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbareq req; struct ether_addr *ea; memset(&req, 0, sizeof(req)); ea = ether_aton(val); if (ea == NULL) errx(1, "invalid address: %s", val); memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); if (do_cmd(ctx, BRDGDADDR, &req, sizeof(req), 1) < 0) err(1, "BRDGDADDR %s", val); } static void setbridge_addr(if_ctx *ctx, const char *val __unused, int dummy __unused) { bridge_addresses(ctx, ""); } static void setbridge_maxaddr(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_csize = val & 0xffffffff; if (do_cmd(ctx, BRDGSCACHE, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSCACHE %s", arg); } static void setbridge_hellotime(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_hellotime = val & 0xff; if (do_cmd(ctx, BRDGSHT, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSHT %s", arg); } static void setbridge_fwddelay(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_fwddelay = val & 0xff; if (do_cmd(ctx, BRDGSFD, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSFD %s", arg); } static void setbridge_maxage(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_maxage = val & 0xff; if (do_cmd(ctx, BRDGSMA, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSMA %s", arg); } static void setbridge_priority(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xffff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_prio = val & 0xffff; if (do_cmd(ctx, BRDGSPRI, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSPRI %s", arg); } static void setbridge_protocol(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; if (strcasecmp(arg, "stp") == 0) { param.ifbrp_proto = 0; } else if (strcasecmp(arg, "rstp") == 0) { param.ifbrp_proto = 2; } else { errx(1, "unknown stp protocol"); } if (do_cmd(ctx, BRDGSPROTO, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSPROTO %s", arg); } static void setbridge_holdcount(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_txhc = val & 0xff; if (do_cmd(ctx, BRDGSTXHC, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSTXHC %s", arg); } static void setbridge_ifpriority(if_ctx *ctx, const char *ifn, const char *pri) { struct ifbreq req; u_long val; memset(&req, 0, sizeof(req)); if (get_val(pri, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", pri); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); req.ifbr_priority = val & 0xff; if (do_cmd(ctx, BRDGSIFPRIO, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFPRIO %s", pri); } static void setbridge_ifpathcost(if_ctx *ctx, const char *ifn, const char *cost) { struct ifbreq req; u_long val; memset(&req, 0, sizeof(req)); if (get_val(cost, &val) < 0) errx(1, "invalid value: %s", cost); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); req.ifbr_path_cost = val; if (do_cmd(ctx, BRDGSIFCOST, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFCOST %s", cost); } static void setbridge_ifmaxaddr(if_ctx *ctx, const char *ifn, const char *arg) { struct ifbreq req; u_long val; memset(&req, 0, sizeof(req)); if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) errx(1, "invalid value: %s", arg); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); req.ifbr_addrmax = val & 0xffffffff; if (do_cmd(ctx, BRDGSIFAMAX, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFAMAX %s", arg); } static void setbridge_timeout(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_ctime = val & 0xffffffff; if (do_cmd(ctx, BRDGSTO, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSTO %s", arg); } static void setbridge_private(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_PRIVATE, 1); } static void unsetbridge_private(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_PRIVATE, 0); } static struct cmd bridge_cmds[] = { DEF_CMD_ARG("addm", setbridge_add), DEF_CMD_ARG("deletem", setbridge_delete), DEF_CMD_ARG("discover", setbridge_discover), DEF_CMD_ARG("-discover", unsetbridge_discover), DEF_CMD_ARG("learn", setbridge_learn), DEF_CMD_ARG("-learn", unsetbridge_learn), DEF_CMD_ARG("sticky", setbridge_sticky), DEF_CMD_ARG("-sticky", unsetbridge_sticky), DEF_CMD_ARG("span", setbridge_span), DEF_CMD_ARG("-span", unsetbridge_span), DEF_CMD_ARG("stp", setbridge_stp), DEF_CMD_ARG("-stp", unsetbridge_stp), DEF_CMD_ARG("edge", setbridge_edge), DEF_CMD_ARG("-edge", unsetbridge_edge), DEF_CMD_ARG("autoedge", setbridge_autoedge), DEF_CMD_ARG("-autoedge", unsetbridge_autoedge), DEF_CMD_ARG("ptp", setbridge_ptp), DEF_CMD_ARG("-ptp", unsetbridge_ptp), DEF_CMD_ARG("autoptp", setbridge_autoptp), DEF_CMD_ARG("-autoptp", unsetbridge_autoptp), DEF_CMD("flush", 0, setbridge_flush), DEF_CMD("flushall", 0, setbridge_flushall), DEF_CMD_ARG2("static", setbridge_static), DEF_CMD_ARG("deladdr", setbridge_deladdr), DEF_CMD("addr", 1, setbridge_addr), DEF_CMD_ARG("maxaddr", setbridge_maxaddr), DEF_CMD_ARG("hellotime", setbridge_hellotime), DEF_CMD_ARG("fwddelay", setbridge_fwddelay), DEF_CMD_ARG("maxage", setbridge_maxage), DEF_CMD_ARG("priority", setbridge_priority), DEF_CMD_ARG("proto", setbridge_protocol), DEF_CMD_ARG("holdcnt", setbridge_holdcount), DEF_CMD_ARG2("ifpriority", setbridge_ifpriority), DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost), DEF_CMD_ARG2("ifmaxaddr", setbridge_ifmaxaddr), DEF_CMD_ARG("timeout", setbridge_timeout), DEF_CMD_ARG("private", setbridge_private), DEF_CMD_ARG("-private", unsetbridge_private), }; static struct afswtch af_bridge = { .af_name = "af_bridge", .af_af = AF_UNSPEC, .af_other_status = bridge_status, }; static __constructor void bridge_ctor(void) { for (size_t i = 0; i < nitems(bridge_cmds); i++) cmd_register(&bridge_cmds[i]); af_register(&af_bridge); } diff --git a/sbin/ifconfig/ifclone.c b/sbin/ifconfig/ifclone.c index 8b378cbe341f..f44d052c97ad 100644 --- a/sbin/ifconfig/ifclone.c +++ b/sbin/ifconfig/ifclone.c @@ -1,191 +1,186 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" typedef enum { MT_PREFIX, MT_FILTER, } clone_match_type; static void list_cloners(void) { char *cloners; size_t cloners_count; if (ifconfig_list_cloners(lifh, &cloners, &cloners_count) < 0) errc(1, ifconfig_err_errno(lifh), "unable to list cloners"); for (const char *name = cloners; name < cloners + cloners_count * IFNAMSIZ; name += IFNAMSIZ) { if (name > cloners) putchar(' '); printf("%s", name); } putchar('\n'); free(cloners); } struct clone_defcb { union { char ifprefix[IFNAMSIZ]; clone_match_func *ifmatch; }; clone_match_type clone_mt; clone_callback_func *clone_cb; SLIST_ENTRY(clone_defcb) next; }; static SLIST_HEAD(, clone_defcb) clone_defcbh = SLIST_HEAD_INITIALIZER(clone_defcbh); void clone_setdefcallback_prefix(const char *ifprefix, clone_callback_func *p) { struct clone_defcb *dcp; dcp = malloc(sizeof(*dcp)); strlcpy(dcp->ifprefix, ifprefix, IFNAMSIZ-1); dcp->clone_mt = MT_PREFIX; dcp->clone_cb = p; SLIST_INSERT_HEAD(&clone_defcbh, dcp, next); } void clone_setdefcallback_filter(clone_match_func *filter, clone_callback_func *p) { struct clone_defcb *dcp; dcp = malloc(sizeof(*dcp)); dcp->ifmatch = filter; dcp->clone_mt = MT_FILTER; dcp->clone_cb = p; SLIST_INSERT_HEAD(&clone_defcbh, dcp, next); } /* * Do the actual clone operation. Any parameters must have been * setup by now. If a callback has been setup to do the work * then defer to it; otherwise do a simple create operation with * no parameters. */ static void ifclonecreate(if_ctx *ctx, void *arg __unused) { struct ifreq ifr = {}; struct clone_defcb *dcp; strlcpy(ifr.ifr_name, ctx->ifname, sizeof(ifr.ifr_name)); /* Try to find a default callback by filter */ SLIST_FOREACH(dcp, &clone_defcbh, next) { if (dcp->clone_mt == MT_FILTER && dcp->ifmatch(ifr.ifr_name) != 0) break; } if (dcp == NULL) { /* Try to find a default callback by prefix */ SLIST_FOREACH(dcp, &clone_defcbh, next) { if (dcp->clone_mt == MT_PREFIX && strncmp(dcp->ifprefix, ifr.ifr_name, strlen(dcp->ifprefix)) == 0) break; } } if (dcp == NULL || dcp->clone_cb == NULL) { /* NB: no parameters */ ifcreate_ioctl(ctx, &ifr); } else { dcp->clone_cb(ctx, &ifr); } } static void clone_create(if_ctx *ctx __unused, const char *cmd __unused, int d __unused) { callback_register(ifclonecreate, NULL); } static void clone_destroy(if_ctx *ctx, const char *cmd __unused, int d __unused) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCIFDESTROY, &ifr) < 0) err(1, "SIOCIFDESTROY"); } static struct cmd clone_cmds[] = { DEF_CLONE_CMD("create", 0, clone_create), DEF_CMD("destroy", 0, clone_destroy), DEF_CLONE_CMD("plumb", 0, clone_create), DEF_CMD("unplumb", 0, clone_destroy), }; static void clone_Copt_cb(const char *arg __unused) { list_cloners(); exit(exit_code); } static struct option clone_Copt = { .opt = "C", .opt_usage = "[-C]", .cb = clone_Copt_cb }; static __constructor void clone_ctor(void) { size_t i; for (i = 0; i < nitems(clone_cmds); i++) cmd_register(&clone_cmds[i]); opt_register(&clone_Copt); } diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index b26fbaf82776..1a7823fedd67 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -1,2107 +1,2105 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #if 0 static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #ifdef JAIL #include #endif #include #include #include #include #include #include #include #include #include #include #include #include /* IP */ #include #include #include #include #include #include #include #include #include #include #ifdef JAIL #include #endif #include #include #include #include #include #include #include "ifconfig.h" ifconfig_handle_t *lifh; #ifdef WITHOUT_NETLINK static char *descr = NULL; static size_t descrlen = 64; #endif static int setaddr; static int setmask; static int doalias; static int clearaddr; static int newaddr = 1; int exit_code = 0; static char ifname_to_print[IFNAMSIZ]; /* Helper for printifnamemaybe() */ /* Formatter Strings */ char *f_inet, *f_inet6, *f_ether, *f_addr; #ifdef WITHOUT_NETLINK static void list_interfaces_ioctl(if_ctx *ctx); static void status(if_ctx *ctx, const struct sockaddr_dl *sdl, struct ifaddrs *ifa); #endif static _Noreturn void usage(void); static void Perrorc(const char *cmd, int error); static int getifflags(const char *ifname, int us, bool err_ok); static struct afswtch *af_getbyname(const char *name); static struct option *opts = NULL; struct ifa_order_elt { int if_order; int af_orders[255]; struct ifaddrs *ifa; TAILQ_ENTRY(ifa_order_elt) link; }; TAILQ_HEAD(ifa_queue, ifa_order_elt); static struct module_map_entry { const char *ifname; const char *kldname; } module_map[] = { { .ifname = "tun", .kldname = "if_tuntap", }, { .ifname = "tap", .kldname = "if_tuntap", }, { .ifname = "vmnet", .kldname = "if_tuntap", }, { .ifname = "ipsec", .kldname = "ipsec", }, { /* * This mapping exists because there is a conflicting enc module * in CAM. ifconfig's guessing behavior will attempt to match * the ifname to a module as well as if_${ifname} and clash with * CAM enc. This is an assertion of the correct module to load. */ .ifname = "enc", .kldname = "if_enc", }, }; void opt_register(struct option *p) { p->next = opts; opts = p; } static void usage(void) { char options[1024]; struct option *p; /* XXX not right but close enough for now */ options[0] = '\0'; for (p = opts; p != NULL; p = p->next) { strlcat(options, p->opt_usage, sizeof(options)); strlcat(options, " ", sizeof(options)); } fprintf(stderr, "usage: ifconfig [-j jail] [-f type:format] %sinterface address_family\n" " [address [dest_address]] [parameters]\n" " ifconfig [-j jail] interface create\n" " ifconfig [-j jail] -a %s[-d] [-m] [-u] [-v] [address_family]\n" " ifconfig [-j jail] -l [-d] [-u] [address_family]\n" " ifconfig [-j jail] %s[-d] [-m] [-u] [-v]\n", options, options, options); exit(1); } static void ifname_update(if_ctx *ctx, const char *name) { strlcpy(ctx->_ifname_storage_ioctl, name, sizeof(ctx->_ifname_storage_ioctl)); ctx->ifname = ctx->_ifname_storage_ioctl; strlcpy(ifname_to_print, name, sizeof(ifname_to_print)); } static void ifr_set_name(struct ifreq *ifr, const char *name) { strlcpy(ifr->ifr_name, name, sizeof(ifr->ifr_name)); } int ioctl_ctx_ifr(if_ctx *ctx, unsigned long cmd, struct ifreq *ifr) { ifr_set_name(ifr, ctx->ifname); return (ioctl_ctx(ctx, cmd, ifr)); } void ifcreate_ioctl(if_ctx *ctx, struct ifreq *ifr) { char ifname_orig[IFNAMSIZ]; strlcpy(ifname_orig, ifr->ifr_name, sizeof(ifname_orig)); if (ioctl(ctx->io_s, SIOCIFCREATE2, ifr) < 0) { switch (errno) { case EEXIST: errx(1, "interface %s already exists", ifr->ifr_name); default: err(1, "SIOCIFCREATE2 (%s)", ifr->ifr_name); } } if (strncmp(ifname_orig, ifr->ifr_name, sizeof(ifname_orig)) != 0) ifname_update(ctx, ifr->ifr_name); } #ifdef WITHOUT_NETLINK static int calcorders(struct ifaddrs *ifa, struct ifa_queue *q) { struct ifaddrs *prev; struct ifa_order_elt *cur; unsigned int ord, af, ifa_ord; prev = NULL; cur = NULL; ord = 0; ifa_ord = 0; while (ifa != NULL) { if (prev == NULL || strcmp(ifa->ifa_name, prev->ifa_name) != 0) { cur = calloc(1, sizeof(*cur)); if (cur == NULL) return (-1); TAILQ_INSERT_TAIL(q, cur, link); cur->if_order = ifa_ord ++; cur->ifa = ifa; ord = 0; } if (ifa->ifa_addr) { af = ifa->ifa_addr->sa_family; if (af < nitems(cur->af_orders) && cur->af_orders[af] == 0) cur->af_orders[af] = ++ord; } prev = ifa; ifa = ifa->ifa_next; } return (0); } static int cmpifaddrs(struct ifaddrs *a, struct ifaddrs *b, struct ifa_queue *q) { struct ifa_order_elt *cur, *e1, *e2; unsigned int af1, af2; int ret; e1 = e2 = NULL; ret = strcmp(a->ifa_name, b->ifa_name); if (ret != 0) { TAILQ_FOREACH(cur, q, link) { if (e1 && e2) break; if (strcmp(cur->ifa->ifa_name, a->ifa_name) == 0) e1 = cur; else if (strcmp(cur->ifa->ifa_name, b->ifa_name) == 0) e2 = cur; } if (!e1 || !e2) return (0); else return (e1->if_order - e2->if_order); } else if (a->ifa_addr != NULL && b->ifa_addr != NULL) { TAILQ_FOREACH(cur, q, link) { if (strcmp(cur->ifa->ifa_name, a->ifa_name) == 0) { e1 = cur; break; } } if (!e1) return (0); af1 = a->ifa_addr->sa_family; af2 = b->ifa_addr->sa_family; if (af1 < nitems(e1->af_orders) && af2 < nitems(e1->af_orders)) return (e1->af_orders[af1] - e1->af_orders[af2]); } return (0); } #endif static void freeformat(void) { if (f_inet != NULL) free(f_inet); if (f_inet6 != NULL) free(f_inet6); if (f_ether != NULL) free(f_ether); if (f_addr != NULL) free(f_addr); } static void setformat(char *input) { char *formatstr, *category, *modifier; formatstr = strdup(input); while ((category = strsep(&formatstr, ",")) != NULL) { modifier = strchr(category, ':'); if (modifier == NULL || modifier[1] == '\0') { warnx("Skipping invalid format specification: %s\n", category); continue; } /* Split the string on the separator, then seek past it */ modifier[0] = '\0'; modifier++; if (strcmp(category, "addr") == 0) f_addr = strdup(modifier); else if (strcmp(category, "ether") == 0) f_ether = strdup(modifier); else if (strcmp(category, "inet") == 0) f_inet = strdup(modifier); else if (strcmp(category, "inet6") == 0) f_inet6 = strdup(modifier); } free(formatstr); } #ifdef WITHOUT_NETLINK static struct ifaddrs * sortifaddrs(struct ifaddrs *list, int (*compare)(struct ifaddrs *, struct ifaddrs *, struct ifa_queue *), struct ifa_queue *q) { struct ifaddrs *right, *temp, *last, *result, *next, *tail; right = list; temp = list; last = list; result = NULL; next = NULL; tail = NULL; if (!list || !list->ifa_next) return (list); while (temp && temp->ifa_next) { last = right; right = right->ifa_next; temp = temp->ifa_next->ifa_next; } last->ifa_next = NULL; list = sortifaddrs(list, compare, q); right = sortifaddrs(right, compare, q); while (list || right) { if (!right) { next = list; list = list->ifa_next; } else if (!list) { next = right; right = right->ifa_next; } else if (compare(list, right, q) <= 0) { next = list; list = list->ifa_next; } else { next = right; right = right->ifa_next; } if (!result) result = next; else tail->ifa_next = next; tail = next; } return (result); } #endif static void printifnamemaybe(void) { if (ifname_to_print[0] != '\0') printf("%s\n", ifname_to_print); } static void list_interfaces(if_ctx *ctx) { #ifdef WITHOUT_NETLINK list_interfaces_ioctl(ctx); #else list_interfaces_nl(ctx->args); #endif } static char * args_peek(struct ifconfig_args *args) { if (args->argc > 0) return (args->argv[0]); return (NULL); } static char * args_pop(struct ifconfig_args *args) { if (args->argc == 0) return (NULL); char *arg = args->argv[0]; args->argc--; args->argv++; return (arg); } static void args_parse(struct ifconfig_args *args, int argc, char *argv[]) { char options[1024]; struct option *p; int c; /* Parse leading line options */ strlcpy(options, "G:adf:j:klmnuv", sizeof(options)); for (p = opts; p != NULL; p = p->next) strlcat(options, p->opt, sizeof(options)); while ((c = getopt(argc, argv, options)) != -1) { switch (c) { case 'a': /* scan all interfaces */ args->all = true; break; case 'd': /* restrict scan to "down" interfaces */ args->downonly = true; break; case 'f': if (optarg == NULL) usage(); setformat(optarg); break; case 'G': if (optarg == NULL || args->all == 0) usage(); args->nogroup = optarg; break; case 'j': #ifdef JAIL if (optarg == NULL) usage(); args->jail_name = optarg; #else Perror("not built with jail support"); #endif break; case 'k': args->printkeys = true; break; case 'l': /* scan interface names only */ args->namesonly = true; break; case 'm': /* show media choices in status */ args->supmedia = true; break; case 'n': /* suppress module loading */ args->noload = true; break; case 'u': /* restrict scan to "up" interfaces */ args->uponly = true; break; case 'v': args->verbose++; break; case 'g': if (args->all) { if (optarg == NULL) usage(); args->matchgroup = optarg; break; } /* FALLTHROUGH */ default: for (p = opts; p != NULL; p = p->next) if (p->opt[0] == c) { p->cb(optarg); break; } if (p == NULL) usage(); break; } } argc -= optind; argv += optind; /* -l cannot be used with -a or -m */ if (args->namesonly && (args->all || args->supmedia)) usage(); /* nonsense.. */ if (args->uponly && args->downonly) usage(); /* no arguments is equivalent to '-a' */ if (!args->namesonly && argc < 1) args->all = 1; /* -a and -l allow an address family arg to limit the output */ if (args->all || args->namesonly) { if (argc > 1) usage(); if (argc == 1) { const struct afswtch *afp = af_getbyname(*argv); if (afp == NULL) { warnx("Address family '%s' unknown.", *argv); usage(); } if (afp->af_name != NULL) argc--, argv++; /* leave with afp non-zero */ args->afp = afp; } } else { /* not listing, need an argument */ if (argc < 1) usage(); } args->argc = argc; args->argv = argv; } static int ifconfig(if_ctx *ctx, int iscreate, const struct afswtch *uafp) { #ifdef WITHOUT_NETLINK return (ifconfig_ioctl(ctx, iscreate, uafp)); #else return (ifconfig_nl(ctx, iscreate, uafp)); #endif } static bool isargcreate(const char *arg) { if (arg == NULL) return (false); if (strcmp(arg, "create") == 0 || strcmp(arg, "plumb") == 0) return (true); return (false); } static bool isnametoolong(const char *ifname) { return (strlen(ifname) >= IFNAMSIZ); } int main(int ac, char *av[]) { char *envformat; int flags; #ifdef JAIL int jid; #endif struct ifconfig_args _args = {}; struct ifconfig_args *args = &_args; struct ifconfig_context ctx = { .args = args, .io_s = -1, }; f_inet = f_inet6 = f_ether = f_addr = NULL; lifh = ifconfig_open(); if (lifh == NULL) err(EXIT_FAILURE, "ifconfig_open"); envformat = getenv("IFCONFIG_FORMAT"); if (envformat != NULL) setformat(envformat); /* * Ensure we print interface name when expected to, * even if we terminate early due to error. */ atexit(printifnamemaybe); args_parse(args, ac, av); #ifdef JAIL if (args->jail_name) { jid = jail_getid(args->jail_name); if (jid == -1) Perror("jail not found"); if (jail_attach(jid) != 0) Perror("cannot attach to jail"); } #endif if (!args->all && !args->namesonly) { /* not listing, need an argument */ args->ifname = args_pop(args); ctx.ifname = args->ifname; /* check and maybe load support for this interface */ ifmaybeload(args, args->ifname); char *arg = args_peek(args); if (if_nametoindex(args->ifname) == 0) { /* * NOTE: We must special-case the `create' command * right here as we would otherwise fail when trying * to find the interface. */ if (isargcreate(arg)) { if (isnametoolong(args->ifname)) errx(1, "%s: cloning name too long", args->ifname); ifconfig(&ctx, 1, NULL); exit(exit_code); } #ifdef JAIL /* * NOTE: We have to special-case the `-vnet' command * right here as we would otherwise fail when trying * to find the interface as it lives in another vnet. */ if (arg != NULL && (strcmp(arg, "-vnet") == 0)) { if (isnametoolong(args->ifname)) errx(1, "%s: interface name too long", args->ifname); ifconfig(&ctx, 0, NULL); exit(exit_code); } #endif errx(1, "interface %s does not exist", args->ifname); } else { /* * Do not allow use `create` command as hostname if * address family is not specified. */ if (isargcreate(arg)) { if (args->argc == 1) errx(1, "interface %s already exists", args->ifname); args_pop(args); } } } /* Check for address family */ if (args->argc > 0) { args->afp = af_getbyname(args_peek(args)); if (args->afp != NULL) args_pop(args); } /* * Check for a requested configuration action on a single interface, * which doesn't require building, sorting, and searching the entire * system address list */ if ((args->argc > 0) && (args->ifname != NULL)) { if (isnametoolong(args->ifname)) warnx("%s: interface name too long, skipping", args->ifname); else { flags = getifflags(args->ifname, -1, false); if (!(((flags & IFF_CANTCONFIG) != 0) || (args->downonly && (flags & IFF_UP) != 0) || (args->uponly && (flags & IFF_UP) == 0))) ifconfig(&ctx, 0, args->afp); } goto done; } args->allfamilies = args->afp == NULL; list_interfaces(&ctx); done: freeformat(); ifconfig_close(lifh); exit(exit_code); } bool match_ether(const struct sockaddr_dl *sdl) { switch (sdl->sdl_type) { case IFT_ETHER: case IFT_L2VLAN: case IFT_BRIDGE: if (sdl->sdl_alen == ETHER_ADDR_LEN) return (true); default: return (false); } } bool match_if_flags(struct ifconfig_args *args, int if_flags) { if ((if_flags & IFF_CANTCONFIG) != 0) return (false); if (args->downonly && (if_flags & IFF_UP) != 0) return (false); if (args->uponly && (if_flags & IFF_UP) == 0) return (false); return (true); } #ifdef WITHOUT_NETLINK static bool match_afp(const struct afswtch *afp, int sa_family, const struct sockaddr_dl *sdl) { if (afp == NULL) return (true); /* special case for "ether" address family */ if (!strcmp(afp->af_name, "ether")) { if (sdl == NULL && !match_ether(sdl)) return (false); return (true); } return (afp->af_af == sa_family); } static void list_interfaces_ioctl(if_ctx *ctx) { struct ifa_queue q = TAILQ_HEAD_INITIALIZER(q); struct ifaddrs *ifap, *sifap, *ifa; struct ifa_order_elt *cur, *tmp; char *namecp = NULL; int ifindex; struct ifconfig_args *args = ctx->args; if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); char *cp = NULL; if (calcorders(ifap, &q) != 0) err(EXIT_FAILURE, "calcorders"); sifap = sortifaddrs(ifap, cmpifaddrs, &q); TAILQ_FOREACH_SAFE(cur, &q, link, tmp) free(cur); ifindex = 0; for (ifa = sifap; ifa; ifa = ifa->ifa_next) { struct ifreq paifr = {}; const struct sockaddr_dl *sdl; strlcpy(paifr.ifr_name, ifa->ifa_name, sizeof(paifr.ifr_name)); if (sizeof(paifr.ifr_addr) >= ifa->ifa_addr->sa_len) { memcpy(&paifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len); } if (args->ifname != NULL && strcmp(args->ifname, ifa->ifa_name) != 0) continue; if (ifa->ifa_addr->sa_family == AF_LINK) sdl = satosdl_c(ifa->ifa_addr); else sdl = NULL; if (cp != NULL && strcmp(cp, ifa->ifa_name) == 0 && !args->namesonly) continue; if (isnametoolong(ifa->ifa_name)) { warnx("%s: interface name too long, skipping", ifa->ifa_name); continue; } cp = ifa->ifa_name; if (!match_if_flags(args, ifa->ifa_flags)) continue; if (!group_member(ifa->ifa_name, args->matchgroup, args->nogroup)) continue; ctx->ifname = cp; /* * Are we just listing the interfaces? */ if (args->namesonly) { if (namecp == cp) continue; if (!match_afp(args->afp, ifa->ifa_addr->sa_family, sdl)) continue; namecp = cp; ifindex++; if (ifindex > 1) printf(" "); fputs(cp, stdout); continue; } ifindex++; if (args->argc > 0) ifconfig(ctx, 0, args->afp); else status(ctx, sdl, ifa); } if (args->namesonly) printf("\n"); freeifaddrs(ifap); } #endif /* * Returns true if an interface should be listed because any its groups * matches shell pattern "match" and none of groups matches pattern "nomatch". * If any pattern is NULL, corresponding condition is skipped. */ bool group_member(const char *ifname, const char *match, const char *nomatch) { static int sock = -1; struct ifgroupreq ifgr; struct ifg_req *ifg; unsigned int len; bool matched, nomatched; /* Sanity checks. */ if (match == NULL && nomatch == NULL) return (true); if (ifname == NULL) return (false); memset(&ifgr, 0, sizeof(ifgr)); strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ); /* The socket is opened once. Let _exit() close it. */ if (sock == -1) { sock = socket(AF_LOCAL, SOCK_DGRAM, 0); if (sock == -1) errx(1, "%s: socket(AF_LOCAL,SOCK_DGRAM)", __func__); } /* Determine amount of memory for the list of groups. */ if (ioctl(sock, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) { if (errno == EINVAL || errno == ENOTTY) return (false); else errx(1, "%s: SIOCGIFGROUP", __func__); } /* Obtain the list of groups. */ len = ifgr.ifgr_len; ifgr.ifgr_groups = (struct ifg_req *)calloc(len / sizeof(*ifg), sizeof(*ifg)); if (ifgr.ifgr_groups == NULL) errx(1, "%s: no memory", __func__); if (ioctl(sock, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) errx(1, "%s: SIOCGIFGROUP", __func__); /* Perform matching. */ matched = false; nomatched = true; for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(*ifg); ifg++) { len -= sizeof(*ifg); if (match && !matched) matched = !fnmatch(match, ifg->ifgrq_group, 0); if (nomatch && nomatched) nomatched = fnmatch(nomatch, ifg->ifgrq_group, 0); } free(ifgr.ifgr_groups); if (match && !nomatch) return (matched); if (!match && nomatch) return (nomatched); return (matched && nomatched); } static struct afswtch *afs = NULL; void af_register(struct afswtch *p) { p->af_next = afs; afs = p; } static struct afswtch * af_getbyname(const char *name) { struct afswtch *afp; for (afp = afs; afp != NULL; afp = afp->af_next) if (strcmp(afp->af_name, name) == 0) return afp; return NULL; } struct afswtch * af_getbyfamily(int af) { struct afswtch *afp; for (afp = afs; afp != NULL; afp = afp->af_next) if (afp->af_af == af) return afp; return NULL; } void af_other_status(if_ctx *ctx) { struct afswtch *afp; uint8_t afmask[howmany(AF_MAX, NBBY)]; memset(afmask, 0, sizeof(afmask)); for (afp = afs; afp != NULL; afp = afp->af_next) { if (afp->af_other_status == NULL) continue; if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) continue; afp->af_other_status(ctx); setbit(afmask, afp->af_af); } } static void af_all_tunnel_status(if_ctx *ctx) { struct afswtch *afp; uint8_t afmask[howmany(AF_MAX, NBBY)]; memset(afmask, 0, sizeof(afmask)); for (afp = afs; afp != NULL; afp = afp->af_next) { if (afp->af_status_tunnel == NULL) continue; if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) continue; afp->af_status_tunnel(ctx); setbit(afmask, afp->af_af); } } static struct cmd *cmds = NULL; void cmd_register(struct cmd *p) { p->c_next = cmds; cmds = p; } static const struct cmd * cmd_lookup(const char *name, int iscreate) { const struct cmd *p; for (p = cmds; p != NULL; p = p->c_next) if (strcmp(name, p->c_name) == 0) { if (iscreate) { if (p->c_iscloneop) return p; } else { if (!p->c_iscloneop) return p; } } return NULL; } struct callback { callback_func *cb_func; void *cb_arg; struct callback *cb_next; }; static struct callback *callbacks = NULL; void callback_register(callback_func *func, void *arg) { struct callback *cb; cb = malloc(sizeof(struct callback)); if (cb == NULL) errx(1, "unable to allocate memory for callback"); cb->cb_func = func; cb->cb_arg = arg; cb->cb_next = callbacks; callbacks = cb; } /* specially-handled commands */ static void setifaddr(if_ctx *ctx, const char *addr, int param); static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr); static void setifdstaddr(if_ctx *ctx, const char *addr, int param __unused); static const struct cmd setifdstaddr_cmd = DEF_CMD("ifdstaddr", 0, setifdstaddr); int af_exec_ioctl(if_ctx *ctx, unsigned long action, void *data) { struct ifreq *req = (struct ifreq *)data; strlcpy(req->ifr_name, ctx->ifname, sizeof(req->ifr_name)); if (ioctl_ctx(ctx, action, req) == 0) return (0); return (errno); } static void delifaddr(if_ctx *ctx, const struct afswtch *afp) { int error; if (afp->af_exec == NULL) { warnx("interface %s cannot change %s addresses!", ctx->ifname, afp->af_name); clearaddr = 0; return; } error = afp->af_exec(ctx, afp->af_difaddr, afp->af_ridreq); if (error != 0) { if (error == EADDRNOTAVAIL && (doalias >= 0)) { /* means no previous address for interface */ } else Perrorc("ioctl (SIOCDIFADDR)", error); } } static void addifaddr(if_ctx *ctx, const struct afswtch *afp) { if (afp->af_exec == NULL) { warnx("interface %s cannot change %s addresses!", ctx->ifname, afp->af_name); newaddr = 0; return; } if (setaddr || setmask) { int error = afp->af_exec(ctx, afp->af_aifaddr, afp->af_addreq); if (error != 0) Perrorc("ioctl (SIOCAIFADDR)", error); } } int ifconfig_ioctl(if_ctx *orig_ctx, int iscreate, const struct afswtch *uafp) { const struct afswtch *afp, *nafp; const struct cmd *p; struct callback *cb; int s; int argc = orig_ctx->args->argc; char *const *argv = orig_ctx->args->argv; struct ifconfig_context _ctx = { .args = orig_ctx->args, .io_ss = orig_ctx->io_ss, .ifname = orig_ctx->ifname, }; struct ifconfig_context *ctx = &_ctx; struct ifreq ifr = {}; strlcpy(ifr.ifr_name, ctx->ifname, sizeof ifr.ifr_name); afp = NULL; if (uafp != NULL) afp = uafp; /* * This is the historical "accident" allowing users to configure IPv4 * addresses without the "inet" keyword which while a nice feature has * proven to complicate other things. We cannot remove this but only * make sure we will never have a similar implicit default for IPv6 or * any other address familiy. We need a fallback though for * ifconfig IF up/down etc. to work without INET support as people * never used ifconfig IF link up/down, etc. either. */ #ifndef RESCUE #ifdef INET if (afp == NULL && feature_present("inet")) afp = af_getbyname("inet"); #endif #endif if (afp == NULL) afp = af_getbyname("link"); if (afp == NULL) { warnx("Please specify an address_family."); usage(); } top: ifr.ifr_addr.sa_family = afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ? AF_LOCAL : afp->af_af; if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0 && (uafp != NULL || errno != EAFNOSUPPORT || (s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0)) err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); ctx->io_s = s; ctx->afp = afp; while (argc > 0) { p = cmd_lookup(*argv, iscreate); if (iscreate && p == NULL) { /* * Push the clone create callback so the new * device is created and can be used for any * remaining arguments. */ cb = callbacks; if (cb == NULL) errx(1, "internal error, no callback"); callbacks = cb->cb_next; cb->cb_func(ctx, cb->cb_arg); iscreate = 0; /* * Handle any address family spec that * immediately follows and potentially * recreate the socket. */ nafp = af_getbyname(*argv); if (nafp != NULL) { argc--, argv++; if (nafp != afp) { close(s); afp = nafp; goto top; } } /* * Look for a normal parameter. */ continue; } if (p == NULL) { /* * Not a recognized command, choose between setting * the interface address and the dst address. */ p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd); } if (p->c_parameter == NEXTARG && p->c_u.c_func) { if (argv[1] == NULL) errx(1, "'%s' requires argument", p->c_name); p->c_u.c_func(ctx, argv[1], 0); argc--, argv++; } else if (p->c_parameter == OPTARG && p->c_u.c_func) { p->c_u.c_func(ctx, argv[1], 0); if (argv[1] != NULL) argc--, argv++; } else if (p->c_parameter == NEXTARG2 && p->c_u.c_func2) { if (argc < 3) errx(1, "'%s' requires 2 arguments", p->c_name); p->c_u.c_func2(ctx, argv[1], argv[2]); argc -= 2, argv += 2; } else if (p->c_parameter == SPARAM && p->c_u.c_func3) { p->c_u.c_func3(ctx, *argv, p->c_sparameter); } else if (p->c_u.c_func) p->c_u.c_func(ctx, *argv, p->c_parameter); argc--, argv++; } /* * Do any post argument processing required by the address family. */ if (afp->af_postproc != NULL) afp->af_postproc(ctx, newaddr, getifflags(ctx->ifname, s, true)); /* * Do deferred callbacks registered while processing * command-line arguments. */ for (cb = callbacks; cb != NULL; cb = cb->cb_next) cb->cb_func(ctx, cb->cb_arg); /* * Do deferred operations. */ if (clearaddr) delifaddr(ctx, afp); if (newaddr) addifaddr(ctx, afp); close(s); return(0); } static void setifaddr(if_ctx *ctx, const char *addr, int param __unused) { const struct afswtch *afp = ctx->afp; if (afp->af_getaddr == NULL) return; /* * Delay the ioctl to set the interface addr until flags are all set. * The address interpretation may depend on the flags, * and the flags may change when the address is set. */ setaddr++; if (doalias == 0 && afp->af_af != AF_LINK) clearaddr = 1; afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR)); } static void settunnel(if_ctx *ctx, const char *src, const char *dst) { const struct afswtch *afp = ctx->afp; struct addrinfo *srcres, *dstres; int ecode; if (afp->af_settunnel == NULL) { warn("address family %s does not support tunnel setup", afp->af_name); return; } if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0) errx(1, "error in parsing address string: %s", gai_strerror(ecode)); if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0) errx(1, "error in parsing address string: %s", gai_strerror(ecode)); if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family) errx(1, "source and destination address families do not match"); afp->af_settunnel(ctx, srcres, dstres); freeaddrinfo(srcres); freeaddrinfo(dstres); } static void deletetunnel(if_ctx *ctx, const char *vname __unused, int param __unused) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCDIFPHYADDR, &ifr) < 0) err(1, "SIOCDIFPHYADDR"); } #ifdef JAIL static void setifvnet(if_ctx *ctx, const char *jname, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_jid = jail_getid(jname); if (ifr.ifr_jid < 0) errx(1, "%s", jail_errmsg); if (ioctl_ctx_ifr(ctx, SIOCSIFVNET, &ifr) < 0) err(1, "SIOCSIFVNET"); } static void setifrvnet(if_ctx *ctx, const char *jname, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_jid = jail_getid(jname); if (ifr.ifr_jid < 0) errx(1, "%s", jail_errmsg); if (ioctl_ctx_ifr(ctx, SIOCSIFRVNET, &ifr) < 0) err(1, "SIOCSIFRVNET(%d, %s)", ifr.ifr_jid, ifr.ifr_name); } #endif static void setifnetmask(if_ctx *ctx, const char *addr, int dummy __unused) { const struct afswtch *afp = ctx->afp; if (afp->af_getaddr != NULL) { setmask++; afp->af_getaddr(addr, MASK); } } static void setifbroadaddr(if_ctx *ctx, const char *addr, int dummy __unused) { const struct afswtch *afp = ctx->afp; if (afp->af_getaddr != NULL) afp->af_getaddr(addr, BRDADDR); } static void notealias(if_ctx *ctx, const char *addr __unused, int param) { const struct afswtch *afp = ctx->afp; if (setaddr && doalias == 0 && param < 0) { if (afp->af_copyaddr != NULL) afp->af_copyaddr(ctx, RIDADDR, ADDR); } doalias = param; if (param < 0) { clearaddr = 1; newaddr = 0; } else clearaddr = 0; } static void setifdstaddr(if_ctx *ctx, const char *addr, int param __unused) { const struct afswtch *afp = ctx->afp; if (afp->af_getaddr != NULL) afp->af_getaddr(addr, DSTADDR); } static int getifflags(const char *ifname, int us, bool err_ok) { struct ifreq my_ifr; int s; memset(&my_ifr, 0, sizeof(my_ifr)); (void) strlcpy(my_ifr.ifr_name, ifname, sizeof(my_ifr.ifr_name)); if (us < 0) { if ((s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) err(1, "socket(family AF_LOCAL,SOCK_DGRAM"); } else s = us; if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { if (!err_ok) { Perror("ioctl (SIOCGIFFLAGS)"); exit(1); } } if (us < 0) close(s); return ((my_ifr.ifr_flags & 0xffff) | (my_ifr.ifr_flagshigh << 16)); } /* * Note: doing an SIOCIGIFFLAGS scribbles on the union portion * of the ifreq structure, which may confuse other parts of ifconfig. * Make a private copy so we can avoid that. */ static void clearifflags(if_ctx *ctx, const char *vname, int value) { struct ifreq my_ifr; int flags; flags = getifflags(ctx->ifname, ctx->io_s, false); flags &= ~value; memset(&my_ifr, 0, sizeof(my_ifr)); strlcpy(my_ifr.ifr_name, ctx->ifname, sizeof(my_ifr.ifr_name)); my_ifr.ifr_flags = flags & 0xffff; my_ifr.ifr_flagshigh = flags >> 16; if (ioctl(ctx->io_s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) Perror(vname); } static void setifflags(if_ctx *ctx, const char *vname, int value) { struct ifreq my_ifr; int flags; flags = getifflags(ctx->ifname, ctx->io_s, false); flags |= value; memset(&my_ifr, 0, sizeof(my_ifr)); strlcpy(my_ifr.ifr_name, ctx->ifname, sizeof(my_ifr.ifr_name)); my_ifr.ifr_flags = flags & 0xffff; my_ifr.ifr_flagshigh = flags >> 16; if (ioctl(ctx->io_s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) Perror(vname); } void clearifcap(if_ctx *ctx, const char *vname, int value) { struct ifreq ifr = {}; int flags; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) < 0) { Perror("ioctl (SIOCGIFCAP)"); exit(1); } flags = ifr.ifr_curcap; flags &= ~value; flags &= ifr.ifr_reqcap; /* Check for no change in capabilities. */ if (ifr.ifr_curcap == flags) return; ifr.ifr_reqcap = flags; if (ioctl_ctx(ctx, SIOCSIFCAP, &ifr) < 0) Perror(vname); } void setifcap(if_ctx *ctx, const char *vname, int value) { struct ifreq ifr = {}; int flags; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) < 0) { Perror("ioctl (SIOCGIFCAP)"); exit(1); } flags = ifr.ifr_curcap; flags |= value; flags &= ifr.ifr_reqcap; /* Check for no change in capabilities. */ if (ifr.ifr_curcap == flags) return; ifr.ifr_reqcap = flags; if (ioctl_ctx(ctx, SIOCSIFCAP, &ifr) < 0) Perror(vname); } void setifcapnv(if_ctx *ctx, const char *vname, const char *arg) { nvlist_t *nvcap; void *buf; char *marg, *mopt; size_t nvbuflen; bool neg; struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) < 0) Perror("ioctl (SIOCGIFCAP)"); if ((ifr.ifr_curcap & IFCAP_NV) == 0) { warnx("IFCAP_NV not supported"); return; /* Not exit() */ } marg = strdup(arg); if (marg == NULL) Perror("strdup"); nvcap = nvlist_create(0); if (nvcap == NULL) Perror("nvlist_create"); while ((mopt = strsep(&marg, ",")) != NULL) { neg = *mopt == '-'; if (neg) mopt++; if (strcmp(mopt, "rxtls") == 0) { nvlist_add_bool(nvcap, "rxtls4", !neg); nvlist_add_bool(nvcap, "rxtls6", !neg); } else { nvlist_add_bool(nvcap, mopt, !neg); } } buf = nvlist_pack(nvcap, &nvbuflen); if (buf == NULL) { errx(1, "nvlist_pack error"); exit(1); } ifr.ifr_cap_nv.buf_length = ifr.ifr_cap_nv.length = nvbuflen; ifr.ifr_cap_nv.buffer = buf; if (ioctl_ctx(ctx, SIOCSIFCAPNV, (caddr_t)&ifr) < 0) Perror(vname); free(buf); nvlist_destroy(nvcap); free(marg); } static void setifmetric(if_ctx *ctx, const char *val, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_metric = atoi(val); if (ioctl_ctx_ifr(ctx, SIOCSIFMETRIC, &ifr) < 0) err(1, "ioctl SIOCSIFMETRIC (set metric)"); } static void setifmtu(if_ctx *ctx, const char *val, int dummy __unused) { struct ifreq ifr = {}; ifr.ifr_mtu = atoi(val); if (ioctl_ctx_ifr(ctx, SIOCSIFMTU, &ifr) < 0) err(1, "ioctl SIOCSIFMTU (set mtu)"); } static void setifpcp(if_ctx *ctx, const char *val, int arg __unused) { struct ifreq ifr = {}; u_long ul; char *endp; ul = strtoul(val, &endp, 0); if (*endp != '\0') errx(1, "invalid value for pcp"); if (ul > 7) errx(1, "value for pcp out of range"); ifr.ifr_lan_pcp = ul; if (ioctl_ctx_ifr(ctx, SIOCSLANPCP, &ifr) == -1) err(1, "SIOCSLANPCP"); } static void disableifpcp(if_ctx *ctx, const char *val __unused, int arg __unused) { struct ifreq ifr = {}; ifr.ifr_lan_pcp = IFNET_PCP_NONE; if (ioctl_ctx_ifr(ctx, SIOCSLANPCP, &ifr) == -1) err(1, "SIOCSLANPCP"); } static void setifname(if_ctx *ctx, const char *val, int dummy __unused) { struct ifreq ifr = {}; char *newname; ifr_set_name(&ifr, ctx->ifname); newname = strdup(val); if (newname == NULL) err(1, "no memory to set ifname"); ifr.ifr_data = newname; if (ioctl_ctx(ctx, SIOCSIFNAME, (caddr_t)&ifr) < 0) { free(newname); err(1, "ioctl SIOCSIFNAME (set name)"); } ifname_update(ctx, newname); free(newname); } static void setifdescr(if_ctx *ctx, const char *val, int dummy __unused) { struct ifreq ifr = {}; char *newdescr; ifr.ifr_buffer.length = strlen(val) + 1; if (ifr.ifr_buffer.length == 1) { ifr.ifr_buffer.buffer = newdescr = NULL; ifr.ifr_buffer.length = 0; } else { newdescr = strdup(val); ifr.ifr_buffer.buffer = newdescr; if (newdescr == NULL) { warn("no memory to set ifdescr"); return; } } if (ioctl_ctx_ifr(ctx, SIOCSIFDESCR, &ifr) < 0) err(1, "ioctl SIOCSIFDESCR (set descr)"); free(newdescr); } static void unsetifdescr(if_ctx *ctx, const char *val __unused, int value __unused) { setifdescr(ctx, "", 0); } #ifdef WITHOUT_NETLINK #define IFFBITS \ "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\7RUNNING" \ "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ "\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP\25STICKYARP" #define IFCAPBITS \ "\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" \ "\10VLAN_HWCSUM\11TSO4\12TSO6\13LRO\14WOL_UCAST\15WOL_MCAST\16WOL_MAGIC" \ "\17TOE4\20TOE6\21VLAN_HWFILTER\23VLAN_HWTSO\24LINKSTATE\25NETMAP" \ "\26RXCSUM_IPV6\27TXCSUM_IPV6\31TXRTLMT\32HWRXTSTMP\33NOMAP\34TXTLS4\35TXTLS6" \ "\36VXLAN_HWCSUM\37VXLAN_HWTSO\40TXTLS_RTLMT" static void print_ifcap_nv(if_ctx *ctx) { struct ifreq ifr = {}; nvlist_t *nvcap; const char *nvname; void *buf, *cookie; bool first, val; int type; buf = malloc(IFR_CAP_NV_MAXBUFSIZE); if (buf == NULL) Perror("malloc"); ifr.ifr_cap_nv.buffer = buf; ifr.ifr_cap_nv.buf_length = IFR_CAP_NV_MAXBUFSIZE; if (ioctl_ctx_ifr(ctx, SIOCGIFCAPNV, &ifr) != 0) Perror("ioctl (SIOCGIFCAPNV)"); nvcap = nvlist_unpack(ifr.ifr_cap_nv.buffer, ifr.ifr_cap_nv.length, 0); if (nvcap == NULL) Perror("nvlist_unpack"); printf("\toptions"); cookie = NULL; for (first = true;; first = false) { nvname = nvlist_next(nvcap, &type, &cookie); if (nvname == NULL) { printf("\n"); break; } if (type == NV_TYPE_BOOL) { val = nvlist_get_bool(nvcap, nvname); if (val) { printf("%c%s", first ? ' ' : ',', nvname); } } } if (ctx->args->supmedia) { printf("\tcapabilities"); cookie = NULL; for (first = true;; first = false) { nvname = nvlist_next(nvcap, &type, &cookie); if (nvname == NULL) { printf("\n"); break; } if (type == NV_TYPE_BOOL) printf("%c%s", first ? ' ' : ',', nvname); } } nvlist_destroy(nvcap); free(buf); if (ioctl_ctx(ctx, SIOCGIFCAP, (caddr_t)&ifr) != 0) Perror("ioctl (SIOCGIFCAP)"); } static void print_ifcap(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFCAP, &ifr) != 0) return; if ((ifr.ifr_curcap & IFCAP_NV) != 0) print_ifcap_nv(ctx); else { printb("\toptions", ifr.ifr_curcap, IFCAPBITS); putchar('\n'); if (ctx->args->supmedia && ifr.ifr_reqcap != 0) { printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); putchar('\n'); } } } #endif void print_ifstatus(if_ctx *ctx) { struct ifstat ifs; strlcpy(ifs.ifs_name, ctx->ifname, sizeof ifs.ifs_name); if (ioctl_ctx(ctx, SIOCGIFSTATUS, &ifs) == 0) printf("%s", ifs.ascii); } void print_metric(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFMETRIC, &ifr) != -1) printf(" metric %d", ifr.ifr_metric); } #ifdef WITHOUT_NETLINK static void print_mtu(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGIFMTU, &ifr) != -1) printf(" mtu %d", ifr.ifr_mtu); } static void print_description(if_ctx *ctx) { struct ifreq ifr = {}; ifr_set_name(&ifr, ctx->ifname); for (;;) { if ((descr = reallocf(descr, descrlen)) != NULL) { ifr.ifr_buffer.buffer = descr; ifr.ifr_buffer.length = descrlen; if (ioctl_ctx(ctx, SIOCGIFDESCR, &ifr) == 0) { if (ifr.ifr_buffer.buffer == descr) { if (strlen(descr) > 0) printf("\tdescription: %s\n", descr); } else if (ifr.ifr_buffer.length > descrlen) { descrlen = ifr.ifr_buffer.length; continue; } } } else warn("unable to allocate memory for interface" "description"); break; } } /* * Print the status of the interface. If an address family was * specified, show only it; otherwise, show them all. */ static void status(if_ctx *ctx, const struct sockaddr_dl *sdl __unused, struct ifaddrs *ifa) { struct ifaddrs *ift; int s, old_s; struct ifconfig_args *args = ctx->args; bool allfamilies = args->afp == NULL; struct ifreq ifr = {}; if (args->afp == NULL) ifr.ifr_addr.sa_family = AF_LOCAL; else ifr.ifr_addr.sa_family = args->afp->af_af == AF_LINK ? AF_LOCAL : args->afp->af_af; s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0); if (s < 0) err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); old_s = ctx->io_s; ctx->io_s = s; printf("%s: ", ctx->ifname); printb("flags", ifa->ifa_flags, IFFBITS); print_metric(ctx); print_mtu(ctx); putchar('\n'); print_description(ctx); print_ifcap(ctx); tunnel_status(ctx); for (ift = ifa; ift != NULL; ift = ift->ifa_next) { if (ift->ifa_addr == NULL) continue; if (strcmp(ifa->ifa_name, ift->ifa_name) != 0) continue; if (allfamilies) { const struct afswtch *p; p = af_getbyfamily(ift->ifa_addr->sa_family); if (p != NULL && p->af_status != NULL) p->af_status(ctx, ift); } else if (args->afp->af_af == ift->ifa_addr->sa_family) args->afp->af_status(ctx, ift); } #if 0 if (allfamilies || afp->af_af == AF_LINK) { const struct afswtch *lafp; /* * Hack; the link level address is received separately * from the routing information so any address is not * handled above. Cobble together an entry and invoke * the status method specially. */ lafp = af_getbyname("lladdr"); if (lafp != NULL) { info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl; lafp->af_status(s, &info); } } #endif if (allfamilies) af_other_status(ctx); else if (args->afp->af_other_status != NULL) args->afp->af_other_status(ctx); print_ifstatus(ctx); if (args->verbose > 0) sfp_status(ctx); close(s); ctx->io_s = old_s; return; } #endif void tunnel_status(if_ctx *ctx) { af_all_tunnel_status(ctx); } static void Perrorc(const char *cmd, int error) { switch (errno) { case ENXIO: errx(1, "%s: no such interface", cmd); break; case EPERM: errx(1, "%s: permission denied", cmd); break; default: errc(1, error, "%s", cmd); } } void Perror(const char *cmd) { Perrorc(cmd, errno); } /* * Print a value a la the %b format of the kernel's printf */ void printb(const char *s, unsigned v, const char *bits) { int i, any = 0; char c; if (bits && *bits == 8) printf("%s=%o", s, v); else printf("%s=%x", s, v); if (bits) { bits++; putchar('<'); while ((i = *bits++) != '\0') { if (v & (1u << (i-1))) { if (any) putchar(','); any = 1; for (; (c = *bits) > 32; bits++) putchar(c); } else for (; *bits > 32; bits++) ; } putchar('>'); } } void print_vhid(const struct ifaddrs *ifa) { struct if_data *ifd; if (ifa->ifa_data == NULL) return; ifd = ifa->ifa_data; if (ifd->ifi_vhid == 0) return; printf(" vhid %d", ifd->ifi_vhid); } void ifmaybeload(struct ifconfig_args *args, const char *name) { #define MOD_PREFIX_LEN 3 /* "if_" */ struct module_stat mstat; int fileid, modid; char ifkind[IFNAMSIZ + MOD_PREFIX_LEN], ifname[IFNAMSIZ], *dp; const char *cp; struct module_map_entry *mme; bool found; /* loading suppressed by the user */ if (args->noload) return; /* trim the interface number off the end */ strlcpy(ifname, name, sizeof(ifname)); dp = ifname + strlen(ifname) - 1; for (; dp > ifname; dp--) { if (isdigit(*dp)) *dp = '\0'; else break; } /* Either derive it from the map or guess otherwise */ *ifkind = '\0'; found = false; for (unsigned i = 0; i < nitems(module_map); ++i) { mme = &module_map[i]; if (strcmp(mme->ifname, ifname) == 0) { strlcpy(ifkind, mme->kldname, sizeof(ifkind)); found = true; break; } } /* We didn't have an alias for it... we'll guess. */ if (!found) { /* turn interface and unit into module name */ strlcpy(ifkind, "if_", sizeof(ifkind)); strlcat(ifkind, ifname, sizeof(ifkind)); } /* scan files in kernel */ mstat.version = sizeof(struct module_stat); for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { /* scan modules in file */ for (modid = kldfirstmod(fileid); modid > 0; modid = modfnext(modid)) { if (modstat(modid, &mstat) < 0) continue; /* strip bus name if present */ if ((cp = strchr(mstat.name, '/')) != NULL) { cp++; } else { cp = mstat.name; } /* * Is it already loaded? Don't compare with ifname if * we were specifically told which kld to use. Doing * so could lead to conflicts not trivially solved. */ if ((!found && strcmp(ifname, cp) == 0) || strcmp(ifkind, cp) == 0) return; } } /* * Try to load the module. But ignore failures, because ifconfig can't * infer the names of all drivers (eg mlx4en(4)). */ (void) kldload(ifkind); } static struct cmd basic_cmds[] = { DEF_CMD("up", IFF_UP, setifflags), DEF_CMD("down", IFF_UP, clearifflags), DEF_CMD("arp", IFF_NOARP, clearifflags), DEF_CMD("-arp", IFF_NOARP, setifflags), DEF_CMD("debug", IFF_DEBUG, setifflags), DEF_CMD("-debug", IFF_DEBUG, clearifflags), DEF_CMD_ARG("description", setifdescr), DEF_CMD_ARG("descr", setifdescr), DEF_CMD("-description", 0, unsetifdescr), DEF_CMD("-descr", 0, unsetifdescr), DEF_CMD("promisc", IFF_PPROMISC, setifflags), DEF_CMD("-promisc", IFF_PPROMISC, clearifflags), DEF_CMD("add", IFF_UP, notealias), DEF_CMD("alias", IFF_UP, notealias), DEF_CMD("-alias", -IFF_UP, notealias), DEF_CMD("delete", -IFF_UP, notealias), DEF_CMD("remove", -IFF_UP, notealias), #ifdef notdef #define EN_SWABIPS 0x1000 DEF_CMD("swabips", EN_SWABIPS, setifflags), DEF_CMD("-swabips", EN_SWABIPS, clearifflags), #endif DEF_CMD_ARG("netmask", setifnetmask), DEF_CMD_ARG("metric", setifmetric), DEF_CMD_ARG("broadcast", setifbroadaddr), DEF_CMD_ARG2("tunnel", settunnel), DEF_CMD("-tunnel", 0, deletetunnel), DEF_CMD("deletetunnel", 0, deletetunnel), #ifdef JAIL DEF_CMD_ARG("vnet", setifvnet), DEF_CMD_ARG("-vnet", setifrvnet), #endif DEF_CMD("link0", IFF_LINK0, setifflags), DEF_CMD("-link0", IFF_LINK0, clearifflags), DEF_CMD("link1", IFF_LINK1, setifflags), DEF_CMD("-link1", IFF_LINK1, clearifflags), DEF_CMD("link2", IFF_LINK2, setifflags), DEF_CMD("-link2", IFF_LINK2, clearifflags), DEF_CMD("monitor", IFF_MONITOR, setifflags), DEF_CMD("-monitor", IFF_MONITOR, clearifflags), DEF_CMD("mextpg", IFCAP_MEXTPG, setifcap), DEF_CMD("-mextpg", IFCAP_MEXTPG, clearifcap), DEF_CMD("staticarp", IFF_STATICARP, setifflags), DEF_CMD("-staticarp", IFF_STATICARP, clearifflags), DEF_CMD("stickyarp", IFF_STICKYARP, setifflags), DEF_CMD("-stickyarp", IFF_STICKYARP, clearifflags), DEF_CMD("rxcsum6", IFCAP_RXCSUM_IPV6, setifcap), DEF_CMD("-rxcsum6", IFCAP_RXCSUM_IPV6, clearifcap), DEF_CMD("txcsum6", IFCAP_TXCSUM_IPV6, setifcap), DEF_CMD("-txcsum6", IFCAP_TXCSUM_IPV6, clearifcap), DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), DEF_CMD("-rxcsum", IFCAP_RXCSUM, clearifcap), DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), DEF_CMD("-txcsum", IFCAP_TXCSUM, clearifcap), DEF_CMD("netcons", IFCAP_NETCONS, setifcap), DEF_CMD("-netcons", IFCAP_NETCONS, clearifcap), DEF_CMD_ARG("pcp", setifpcp), DEF_CMD("-pcp", 0, disableifpcp), DEF_CMD("polling", IFCAP_POLLING, setifcap), DEF_CMD("-polling", IFCAP_POLLING, clearifcap), DEF_CMD("tso6", IFCAP_TSO6, setifcap), DEF_CMD("-tso6", IFCAP_TSO6, clearifcap), DEF_CMD("tso4", IFCAP_TSO4, setifcap), DEF_CMD("-tso4", IFCAP_TSO4, clearifcap), DEF_CMD("tso", IFCAP_TSO, setifcap), DEF_CMD("-tso", IFCAP_TSO, clearifcap), DEF_CMD("toe", IFCAP_TOE, setifcap), DEF_CMD("-toe", IFCAP_TOE, clearifcap), DEF_CMD("lro", IFCAP_LRO, setifcap), DEF_CMD("-lro", IFCAP_LRO, clearifcap), DEF_CMD("txtls", IFCAP_TXTLS, setifcap), DEF_CMD("-txtls", IFCAP_TXTLS, clearifcap), DEF_CMD_SARG("rxtls", IFCAP2_RXTLS4_NAME "," IFCAP2_RXTLS6_NAME, setifcapnv), DEF_CMD_SARG("-rxtls", "-"IFCAP2_RXTLS4_NAME ",-" IFCAP2_RXTLS6_NAME, setifcapnv), DEF_CMD("wol", IFCAP_WOL, setifcap), DEF_CMD("-wol", IFCAP_WOL, clearifcap), DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), DEF_CMD("-wol_ucast", IFCAP_WOL_UCAST, clearifcap), DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), DEF_CMD("-wol_mcast", IFCAP_WOL_MCAST, clearifcap), DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), DEF_CMD("-wol_magic", IFCAP_WOL_MAGIC, clearifcap), DEF_CMD("txrtlmt", IFCAP_TXRTLMT, setifcap), DEF_CMD("-txrtlmt", IFCAP_TXRTLMT, clearifcap), DEF_CMD("txtlsrtlmt", IFCAP_TXTLS_RTLMT, setifcap), DEF_CMD("-txtlsrtlmt", IFCAP_TXTLS_RTLMT, clearifcap), DEF_CMD("hwrxtstmp", IFCAP_HWRXTSTMP, setifcap), DEF_CMD("-hwrxtstmp", IFCAP_HWRXTSTMP, clearifcap), DEF_CMD("normal", IFF_LINK0, clearifflags), DEF_CMD("compress", IFF_LINK0, setifflags), DEF_CMD("noicmp", IFF_LINK1, setifflags), DEF_CMD_ARG("mtu", setifmtu), DEF_CMD_ARG("name", setifname), }; static __constructor void ifconfig_ctor(void) { size_t i; for (i = 0; i < nitems(basic_cmds); i++) cmd_register(&basic_cmds[i]); } diff --git a/sbin/ifconfig/ifgif.c b/sbin/ifconfig/ifgif.c index 3a41ef63d1d3..6a4bb8b5a240 100644 --- a/sbin/ifconfig/ifgif.c +++ b/sbin/ifconfig/ifgif.c @@ -1,113 +1,108 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009 Hiroki Sato. 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 ``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 HIS RELATIVES 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 MIND, 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[] = - "$FreeBSD$"; -#endif - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #define GIFBITS "\020\2IGNORE_SOURCE" static void gif_status(if_ctx *ctx) { int opts; struct ifreq ifr = { .ifr_data = (caddr_t)&opts }; if (ioctl_ctx_ifr(ctx, GIFGOPTS, &ifr) == -1) return; if (opts == 0) return; printb("\toptions", opts, GIFBITS); putchar('\n'); } static void setgifopts(if_ctx *ctx, const char *val __unused, int d) { int opts; struct ifreq ifr = { .ifr_data = (caddr_t)&opts }; if (ioctl_ctx_ifr(ctx, GIFGOPTS, &ifr) == -1) { warn("ioctl(GIFGOPTS)"); return; } if (d < 0) opts &= ~(-d); else opts |= d; if (ioctl_ctx(ctx, GIFSOPTS, &ifr) == -1) { warn("ioctl(GIFSOPTS)"); return; } } static struct cmd gif_cmds[] = { DEF_CMD("ignore_source", GIF_IGNORE_SOURCE, setgifopts), DEF_CMD("-ignore_source", -GIF_IGNORE_SOURCE, setgifopts), }; static struct afswtch af_gif = { .af_name = "af_gif", .af_af = AF_UNSPEC, .af_other_status = gif_status, }; static __constructor void gif_ctor(void) { size_t i; for (i = 0; i < nitems(gif_cmds); i++) cmd_register(&gif_cmds[i]); af_register(&af_gif); } diff --git a/sbin/ifconfig/ifgroup.c b/sbin/ifconfig/ifgroup.c index 702e4732db7c..49cce678bb5e 100644 --- a/sbin/ifconfig/ifgroup.c +++ b/sbin/ifconfig/ifgroup.c @@ -1,169 +1,164 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2006 Max Laier. 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" static void setifgroup(if_ctx *ctx, const char *group_name, int dummy __unused) { struct ifgroupreq ifgr = {}; strlcpy(ifgr.ifgr_name, ctx->ifname, IFNAMSIZ); if (group_name[0] && isdigit(group_name[strlen(group_name) - 1])) errx(1, "setifgroup: group names may not end in a digit"); if (strlcpy(ifgr.ifgr_group, group_name, IFNAMSIZ) >= IFNAMSIZ) errx(1, "setifgroup: group name too long"); if (ioctl_ctx(ctx, SIOCAIFGROUP, (caddr_t)&ifgr) == -1 && errno != EEXIST) err(1," SIOCAIFGROUP"); } static void unsetifgroup(if_ctx *ctx, const char *group_name, int dummy __unused) { struct ifgroupreq ifgr = {}; strlcpy(ifgr.ifgr_name, ctx->ifname, IFNAMSIZ); if (group_name[0] && isdigit(group_name[strlen(group_name) - 1])) errx(1, "unsetifgroup: group names may not end in a digit"); if (strlcpy(ifgr.ifgr_group, group_name, IFNAMSIZ) >= IFNAMSIZ) errx(1, "unsetifgroup: group name too long"); if (ioctl_ctx(ctx, SIOCDIFGROUP, (caddr_t)&ifgr) == -1 && errno != ENOENT) err(1, "SIOCDIFGROUP"); } static void getifgroups(if_ctx *ctx) { struct ifgroupreq ifgr; size_t cnt; if (ifconfig_get_groups(lifh, ctx->ifname, &ifgr) == -1) return; cnt = 0; for (size_t i = 0; i < ifgr.ifgr_len / sizeof(struct ifg_req); ++i) { struct ifg_req *ifg = &ifgr.ifgr_groups[i]; if (strcmp(ifg->ifgrq_group, "all")) { if (cnt == 0) printf("\tgroups:"); cnt++; printf(" %s", ifg->ifgrq_group); } } if (cnt) printf("\n"); free(ifgr.ifgr_groups); } static void printgroup(const char *groupname) { struct ifgroupreq ifgr; struct ifg_req *ifg; unsigned int len; int s; s = socket(AF_LOCAL, SOCK_DGRAM, 0); if (s == -1) err(1, "socket(AF_LOCAL,SOCK_DGRAM)"); bzero(&ifgr, sizeof(ifgr)); strlcpy(ifgr.ifgr_name, groupname, sizeof(ifgr.ifgr_name)); if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) { if (errno == EINVAL || errno == ENOTTY || errno == ENOENT) exit(exit_code); else err(1, "SIOCGIFGMEMB"); } len = ifgr.ifgr_len; if ((ifgr.ifgr_groups = calloc(1, len)) == NULL) err(1, "printgroup"); if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) err(1, "SIOCGIFGMEMB"); for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req); ifg++) { len -= sizeof(struct ifg_req); printf("%s\n", ifg->ifgrq_member); } free(ifgr.ifgr_groups); exit(exit_code); } static struct cmd group_cmds[] = { DEF_CMD_ARG("group", setifgroup), DEF_CMD_ARG("-group", unsetifgroup), }; static struct afswtch af_group = { .af_name = "af_group", .af_af = AF_UNSPEC, .af_other_status = getifgroups, }; static struct option group_gopt = { .opt = "g:", .opt_usage = "[-g groupname]", .cb = printgroup, }; static __constructor void group_ctor(void) { for (size_t i = 0; i < nitems(group_cmds); i++) cmd_register(&group_cmds[i]); af_register(&af_group); opt_register(&group_gopt); } diff --git a/sbin/ifconfig/iflagg.c b/sbin/ifconfig/iflagg.c index 9d148c314119..4de437d25bd9 100644 --- a/sbin/ifconfig/iflagg.c +++ b/sbin/ifconfig/iflagg.c @@ -1,347 +1,342 @@ /*- */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" static struct iflaggparam params = { .lagg_type = LAGG_TYPE_DEFAULT, }; static char lacpbuf[120]; /* LACP peer '[(a,a,a),(p,p,p)]' */ static void setlaggport(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_reqport rp = {}; strlcpy(rp.rp_ifname, ctx->ifname, sizeof(rp.rp_ifname)); strlcpy(rp.rp_portname, val, sizeof(rp.rp_portname)); /* * Do not exit with an error here. Doing so permits a * failed NIC to take down an entire lagg. * * Don't error at all if the port is already in the lagg. */ if (ioctl_ctx(ctx, SIOCSLAGGPORT, &rp) && errno != EEXIST) { warnx("%s %s: SIOCSLAGGPORT: %s", ctx->ifname, val, strerror(errno)); exit_code = 1; } } static void unsetlaggport(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_reqport rp = {}; strlcpy(rp.rp_ifname, ctx->ifname, sizeof(rp.rp_ifname)); strlcpy(rp.rp_portname, val, sizeof(rp.rp_portname)); if (ioctl_ctx(ctx, SIOCSLAGGDELPORT, &rp)) err(1, "SIOCSLAGGDELPORT"); } static void setlaggproto(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_protos lpr[] = LAGG_PROTOS; struct lagg_reqall ra; bzero(&ra, sizeof(ra)); ra.ra_proto = LAGG_PROTO_MAX; for (size_t i = 0; i < nitems(lpr); i++) { if (strcmp(val, lpr[i].lpr_name) == 0) { ra.ra_proto = lpr[i].lpr_proto; break; } } if (ra.ra_proto == LAGG_PROTO_MAX) errx(1, "Invalid aggregation protocol: %s", val); strlcpy(ra.ra_ifname, ctx->ifname, sizeof(ra.ra_ifname)); if (ioctl_ctx(ctx, SIOCSLAGG, &ra) != 0) err(1, "SIOCSLAGG"); } static void setlaggflowidshift(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_reqopts ro = {}; ro.ro_opts = LAGG_OPT_FLOWIDSHIFT; strlcpy(ro.ro_ifname, ctx->ifname, sizeof(ro.ro_ifname)); ro.ro_flowid_shift = (int)strtol(val, NULL, 10); if (ro.ro_flowid_shift & ~LAGG_OPT_FLOWIDSHIFT_MASK) errx(1, "Invalid flowid_shift option: %s", val); if (ioctl_ctx(ctx, SIOCSLAGGOPTS, &ro) != 0) err(1, "SIOCSLAGGOPTS"); } static void setlaggrr_limit(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_reqopts ro = {}; strlcpy(ro.ro_ifname, ctx->ifname, sizeof(ro.ro_ifname)); ro.ro_opts = LAGG_OPT_RR_LIMIT; ro.ro_bkt = (uint32_t)strtoul(val, NULL, 10); if (ro.ro_bkt == 0) errx(1, "Invalid round-robin stride: %s", val); if (ioctl_ctx(ctx, SIOCSLAGGOPTS, &ro) != 0) err(1, "SIOCSLAGGOPTS"); } static void setlaggsetopt(if_ctx *ctx, const char *val __unused, int d) { struct lagg_reqopts ro = {}; ro.ro_opts = d; switch (ro.ro_opts) { case LAGG_OPT_USE_FLOWID: case -LAGG_OPT_USE_FLOWID: case LAGG_OPT_USE_NUMA: case -LAGG_OPT_USE_NUMA: case LAGG_OPT_LACP_STRICT: case -LAGG_OPT_LACP_STRICT: case LAGG_OPT_LACP_TXTEST: case -LAGG_OPT_LACP_TXTEST: case LAGG_OPT_LACP_RXTEST: case -LAGG_OPT_LACP_RXTEST: case LAGG_OPT_LACP_FAST_TIMO: case -LAGG_OPT_LACP_FAST_TIMO: break; default: err(1, "Invalid lagg option"); } strlcpy(ro.ro_ifname, ctx->ifname, sizeof(ro.ro_ifname)); if (ioctl_ctx(ctx, SIOCSLAGGOPTS, &ro) != 0) err(1, "SIOCSLAGGOPTS"); } static void setlagghash(if_ctx *ctx, const char *val, int dummy __unused) { struct lagg_reqflags rf; char *str, *tmp, *tok; rf.rf_flags = 0; str = tmp = strdup(val); while ((tok = strsep(&tmp, ",")) != NULL) { if (strcmp(tok, "l2") == 0) rf.rf_flags |= LAGG_F_HASHL2; else if (strcmp(tok, "l3") == 0) rf.rf_flags |= LAGG_F_HASHL3; else if (strcmp(tok, "l4") == 0) rf.rf_flags |= LAGG_F_HASHL4; else errx(1, "Invalid lagghash option: %s", tok); } free(str); if (rf.rf_flags == 0) errx(1, "No lagghash options supplied"); strlcpy(rf.rf_ifname, ctx->ifname, sizeof(rf.rf_ifname)); if (ioctl_ctx(ctx, SIOCSLAGGHASH, &rf)) err(1, "SIOCSLAGGHASH"); } static char * lacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) { snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", (int)mac[0], (int)mac[1], (int)mac[2], (int)mac[3], (int)mac[4], (int)mac[5]); return (buf); } static char * lacp_format_peer(struct lacp_opreq *req, const char *sep) { char macbuf1[20]; char macbuf2[20]; snprintf(lacpbuf, sizeof(lacpbuf), "[(%04X,%s,%04X,%04X,%04X),%s(%04X,%s,%04X,%04X,%04X)]", req->actor_prio, lacp_format_mac(req->actor_mac, macbuf1, sizeof(macbuf1)), req->actor_key, req->actor_portprio, req->actor_portno, sep, req->partner_prio, lacp_format_mac(req->partner_mac, macbuf2, sizeof(macbuf2)), req->partner_key, req->partner_portprio, req->partner_portno); return(lacpbuf); } static void lagg_status(if_ctx *ctx) { struct lagg_protos protos[] = LAGG_PROTOS; struct ifconfig_lagg_status *lagg; struct lagg_reqall *ra; struct lagg_reqflags *rf; struct lagg_reqopts *ro; struct lagg_reqport *ports; struct lacp_opreq *lp; const char *proto; const int verbose = ctx->args->verbose; if (ifconfig_lagg_get_lagg_status(lifh, ctx->ifname, &lagg) == -1) return; ra = lagg->ra; rf = lagg->rf; ro = lagg->ro; ports = ra->ra_port; proto = ""; for (size_t i = 0; i < nitems(protos); ++i) { if (ra->ra_proto == protos[i].lpr_proto) { proto = protos[i].lpr_name; break; } } printf("\tlaggproto %s", proto); if (rf->rf_flags & LAGG_F_HASHMASK) { const char *sep = ""; printf(" lagghash "); if (rf->rf_flags & LAGG_F_HASHL2) { printf("%sl2", sep); sep = ","; } if (rf->rf_flags & LAGG_F_HASHL3) { printf("%sl3", sep); sep = ","; } if (rf->rf_flags & LAGG_F_HASHL4) { printf("%sl4", sep); sep = ","; } } putchar('\n'); if (verbose) { printf("\tlagg options:\n"); printb("\t\tflags", ro->ro_opts, LAGG_OPT_BITS); putchar('\n'); printf("\t\tflowid_shift: %d\n", ro->ro_flowid_shift); if (ra->ra_proto == LAGG_PROTO_ROUNDROBIN) printf("\t\trr_limit: %d\n", ro->ro_bkt); printf("\tlagg statistics:\n"); printf("\t\tactive ports: %d\n", ro->ro_active); printf("\t\tflapping: %u\n", ro->ro_flapping); if (ra->ra_proto == LAGG_PROTO_LACP) { lp = &ra->ra_lacpreq; printf("\tlag id: %s\n", lacp_format_peer(lp, "\n\t\t ")); } } for (size_t i = 0; i < (size_t)ra->ra_ports; ++i) { lp = &ports[i].rp_lacpreq; printf("\tlaggport: %s ", ports[i].rp_portname); printb("flags", ports[i].rp_flags, LAGG_PORT_BITS); if (verbose && ra->ra_proto == LAGG_PROTO_LACP) printb(" state", lp->actor_state, LACP_STATE_BITS); putchar('\n'); if (verbose && ra->ra_proto == LAGG_PROTO_LACP) printf("\t\t%s\n", lacp_format_peer(lp, "\n\t\t ")); } ifconfig_lagg_free_lagg_status(lagg); } static void setlaggtype(if_ctx *ctx __unused, const char *arg, int dummy __unused) { static const struct lagg_types lt[] = LAGG_TYPES; for (size_t i = 0; i < nitems(lt); i++) { if (strcmp(arg, lt[i].lt_name) == 0) { params.lagg_type = lt[i].lt_value; return; } } errx(1, "invalid lagg type: %s", arg); } static void lagg_create(if_ctx *ctx, struct ifreq *ifr) { ifr->ifr_data = (caddr_t) ¶ms; ifcreate_ioctl(ctx, ifr); } static struct cmd lagg_cmds[] = { DEF_CLONE_CMD_ARG("laggtype", setlaggtype), DEF_CMD_ARG("laggport", setlaggport), DEF_CMD_ARG("-laggport", unsetlaggport), DEF_CMD_ARG("laggproto", setlaggproto), DEF_CMD_ARG("lagghash", setlagghash), DEF_CMD("use_flowid", LAGG_OPT_USE_FLOWID, setlaggsetopt), DEF_CMD("-use_flowid", -LAGG_OPT_USE_FLOWID, setlaggsetopt), DEF_CMD("use_numa", LAGG_OPT_USE_NUMA, setlaggsetopt), DEF_CMD("-use_numa", -LAGG_OPT_USE_NUMA, setlaggsetopt), DEF_CMD("lacp_strict", LAGG_OPT_LACP_STRICT, setlaggsetopt), DEF_CMD("-lacp_strict", -LAGG_OPT_LACP_STRICT, setlaggsetopt), DEF_CMD("lacp_txtest", LAGG_OPT_LACP_TXTEST, setlaggsetopt), DEF_CMD("-lacp_txtest", -LAGG_OPT_LACP_TXTEST, setlaggsetopt), DEF_CMD("lacp_rxtest", LAGG_OPT_LACP_RXTEST, setlaggsetopt), DEF_CMD("-lacp_rxtest", -LAGG_OPT_LACP_RXTEST, setlaggsetopt), DEF_CMD("lacp_fast_timeout", LAGG_OPT_LACP_FAST_TIMO, setlaggsetopt), DEF_CMD("-lacp_fast_timeout", -LAGG_OPT_LACP_FAST_TIMO, setlaggsetopt), DEF_CMD_ARG("flowid_shift", setlaggflowidshift), DEF_CMD_ARG("rr_limit", setlaggrr_limit), }; static struct afswtch af_lagg = { .af_name = "af_lagg", .af_af = AF_UNSPEC, .af_other_status = lagg_status, }; static __constructor void lagg_ctor(void) { for (size_t i = 0; i < nitems(lagg_cmds); i++) cmd_register(&lagg_cmds[i]); af_register(&af_lagg); clone_setdefcallback_prefix("lagg", lagg_create); } diff --git a/sbin/ifconfig/ifvlan.c b/sbin/ifconfig/ifvlan.c index 90854885b561..a79ea35bc14b 100644 --- a/sbin/ifconfig/ifvlan.c +++ b/sbin/ifconfig/ifvlan.c @@ -1,316 +1,311 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1999 Bill Paul * Copyright (c) 2012 ADARA Networks, Inc. * All rights reserved. * * Portions of this software were developed by Robert N. M. Watson under * contract to ADARA Networks, Inc. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif - #define NOTAG ((u_short) -1) static const char proto_8021Q[] = "802.1q"; static const char proto_8021ad[] = "802.1ad"; static const char proto_qinq[] = "qinq"; static struct vlanreq params = { .vlr_tag = NOTAG, .vlr_proto = ETHERTYPE_VLAN, }; static void vlan_status(if_ctx *ctx) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) == -1) return; printf("\tvlan: %d", vreq.vlr_tag); printf(" vlanproto: "); switch (vreq.vlr_proto) { case ETHERTYPE_VLAN: printf(proto_8021Q); break; case ETHERTYPE_QINQ: printf(proto_8021ad); break; default: printf("0x%04x", vreq.vlr_proto); } if (ioctl_ctx_ifr(ctx, SIOCGVLANPCP, &ifr) != -1) printf(" vlanpcp: %u", ifr.ifr_vlan_pcp); printf(" parent interface: %s", vreq.vlr_parent[0] == '\0' ? "" : vreq.vlr_parent); printf("\n"); } static int vlan_match_ethervid(const char *name) { return (strchr(name, '.') != NULL); } static void vlan_parse_ethervid(const char *name) { char ifname[IFNAMSIZ]; char *cp; unsigned int vid; strlcpy(ifname, name, IFNAMSIZ); if ((cp = strrchr(ifname, '.')) == NULL) return; /* * Derive params from interface name: "parent.vid". */ *cp++ = '\0'; if ((*cp < '1') || (*cp > '9')) errx(1, "invalid vlan tag"); vid = *cp++ - '0'; while ((*cp >= '0') && (*cp <= '9')) { vid = (vid * 10) + (*cp++ - '0'); if (vid >= 0xFFF) errx(1, "invalid vlan tag"); } if (*cp != '\0') errx(1, "invalid vlan tag"); /* * allow "devX.Y vlandev devX vlan Y" syntax */ if (params.vlr_tag == NOTAG || params.vlr_tag == vid) params.vlr_tag = vid; else errx(1, "ambiguous vlan specification"); /* Restrict overriding interface name */ if (params.vlr_parent[0] == '\0' || !strcmp(params.vlr_parent, ifname)) strlcpy(params.vlr_parent, ifname, IFNAMSIZ); else errx(1, "ambiguous vlan specification"); } static void vlan_create(if_ctx *ctx, struct ifreq *ifr) { vlan_parse_ethervid(ifr->ifr_name); if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') { /* * One or both parameters were specified, make sure both. */ if (params.vlr_tag == NOTAG) errx(1, "must specify a tag for vlan create"); if (params.vlr_parent[0] == '\0') errx(1, "must specify a parent device for vlan create"); ifr->ifr_data = (caddr_t) ¶ms; } ifcreate_ioctl(ctx, ifr); } static void vlan_cb(if_ctx *ctx __unused, void *arg __unused) { if ((params.vlr_tag != NOTAG) ^ (params.vlr_parent[0] != '\0')) errx(1, "both vlan and vlandev must be specified"); } static void vlan_set(int s, struct ifreq *ifr) { if (params.vlr_tag != NOTAG && params.vlr_parent[0] != '\0') { ifr->ifr_data = (caddr_t) ¶ms; if (ioctl(s, SIOCSETVLAN, (caddr_t)ifr) == -1) err(1, "SIOCSETVLAN"); } } static void setvlantag(if_ctx *ctx, const char *val, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; u_long ul; char *endp; ul = strtoul(val, &endp, 0); if (*endp != '\0') errx(1, "invalid value for vlan"); params.vlr_tag = ul; /* check if the value can be represented in vlr_tag */ if (params.vlr_tag != ul) errx(1, "value for vlan out of range"); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) { vreq.vlr_tag = params.vlr_tag; memcpy(¶ms, &vreq, sizeof(params)); vlan_set(ctx->io_s, &ifr); } } static void setvlandev(if_ctx *ctx, const char *val, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; strlcpy(params.vlr_parent, val, sizeof(params.vlr_parent)); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) vlan_set(ctx->io_s, &ifr); } static void setvlanproto(if_ctx *ctx, const char *val, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; if (strncasecmp(proto_8021Q, val, strlen(proto_8021Q)) == 0) { params.vlr_proto = ETHERTYPE_VLAN; } else if ((strncasecmp(proto_8021ad, val, strlen(proto_8021ad)) == 0) || (strncasecmp(proto_qinq, val, strlen(proto_qinq)) == 0)) { params.vlr_proto = ETHERTYPE_QINQ; } else errx(1, "invalid value for vlanproto"); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) { vreq.vlr_proto = params.vlr_proto; memcpy(¶ms, &vreq, sizeof(params)); vlan_set(ctx->io_s, &ifr); } } static void setvlanpcp(if_ctx *ctx, const char *val, int dummy __unused) { u_long ul; char *endp; struct ifreq ifr = {}; ul = strtoul(val, &endp, 0); if (*endp != '\0') errx(1, "invalid value for vlanpcp"); if (ul > 7) errx(1, "value for vlanpcp out of range"); ifr.ifr_vlan_pcp = ul; if (ioctl_ctx_ifr(ctx, SIOCSVLANPCP, &ifr) == -1) err(1, "SIOCSVLANPCP"); } static void unsetvlandev(if_ctx *ctx, const char *val __unused, int dummy __unused) { struct vlanreq vreq = {}; struct ifreq ifr = { .ifr_data = (caddr_t)&vreq }; if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) == -1) err(1, "SIOCGETVLAN"); bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent)); vreq.vlr_tag = 0; if (ioctl_ctx(ctx, SIOCSETVLAN, (caddr_t)&ifr) == -1) err(1, "SIOCSETVLAN"); } static struct cmd vlan_cmds[] = { DEF_CLONE_CMD_ARG("vlan", setvlantag), DEF_CLONE_CMD_ARG("vlandev", setvlandev), DEF_CLONE_CMD_ARG("vlanproto", setvlanproto), DEF_CMD_ARG("vlanpcp", setvlanpcp), /* NB: non-clone cmds */ DEF_CMD_ARG("vlan", setvlantag), DEF_CMD_ARG("vlandev", setvlandev), DEF_CMD_ARG("vlanproto", setvlanproto), /* XXX For compatibility. Should become DEF_CMD() some day. */ DEF_CMD_OPTARG("-vlandev", unsetvlandev), DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap), DEF_CMD("-vlanmtu", IFCAP_VLAN_MTU, clearifcap), DEF_CMD("vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap), DEF_CMD("-vlanhwtag", IFCAP_VLAN_HWTAGGING, clearifcap), DEF_CMD("vlanhwfilter", IFCAP_VLAN_HWFILTER, setifcap), DEF_CMD("-vlanhwfilter", IFCAP_VLAN_HWFILTER, clearifcap), DEF_CMD("vlanhwtso", IFCAP_VLAN_HWTSO, setifcap), DEF_CMD("-vlanhwtso", IFCAP_VLAN_HWTSO, clearifcap), DEF_CMD("vlanhwcsum", IFCAP_VLAN_HWCSUM, setifcap), DEF_CMD("-vlanhwcsum", IFCAP_VLAN_HWCSUM, clearifcap), }; static struct afswtch af_vlan = { .af_name = "af_vlan", .af_af = AF_UNSPEC, .af_other_status = vlan_status, }; static __constructor void vlan_ctor(void) { size_t i; for (i = 0; i < nitems(vlan_cmds); i++) cmd_register(&vlan_cmds[i]); af_register(&af_vlan); callback_register(vlan_cb, NULL); clone_setdefcallback_prefix("vlan", vlan_create); clone_setdefcallback_filter(vlan_match_ethervid, vlan_create); } diff --git a/sbin/ifconfig/sfp.c b/sbin/ifconfig/sfp.c index a357a2bbefdd..0dc1def751b1 100644 --- a/sbin/ifconfig/sfp.c +++ b/sbin/ifconfig/sfp.c @@ -1,132 +1,127 @@ /*- * Copyright (c) 2014 Alexander V. Chernikov. 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" void sfp_status(if_ctx *ctx) { struct ifconfig_sfp_info info; struct ifconfig_sfp_info_strings strings; struct ifconfig_sfp_vendor_info vendor_info; struct ifconfig_sfp_status status; size_t channel_count; int verbose = ctx->args->verbose; if (ifconfig_sfp_get_sfp_info(lifh, ctx->ifname, &info) == -1) return; ifconfig_sfp_get_sfp_info_strings(&info, &strings); printf("\tplugged: %s %s (%s)\n", ifconfig_sfp_id_display(info.sfp_id), ifconfig_sfp_physical_spec(&info, &strings), strings.sfp_conn); if (ifconfig_sfp_get_sfp_vendor_info(lifh, ctx->ifname, &vendor_info) == -1) return; printf("\tvendor: %s PN: %s SN: %s DATE: %s\n", vendor_info.name, vendor_info.pn, vendor_info.sn, vendor_info.date); if (ifconfig_sfp_id_is_qsfp(info.sfp_id)) { if (verbose > 1) printf("\tcompliance level: %s\n", strings.sfp_rev); } else { if (verbose > 5) { printf("Class: %s\n", ifconfig_sfp_physical_spec(&info, &strings)); printf("Length: %s\n", strings.sfp_fc_len); printf("Tech: %s\n", strings.sfp_cab_tech); printf("Media: %s\n", strings.sfp_fc_media); printf("Speed: %s\n", strings.sfp_fc_speed); } } if (ifconfig_sfp_get_sfp_status(lifh, ctx->ifname, &status) == 0) { if (ifconfig_sfp_id_is_qsfp(info.sfp_id) && verbose > 1) printf("\tnominal bitrate: %u Mbps\n", status.bitrate); printf("\tmodule temperature: %.2f C voltage: %.2f Volts\n", status.temp, status.voltage); channel_count = ifconfig_sfp_channel_count(&info); for (size_t chan = 0; chan < channel_count; ++chan) { uint16_t rx = status.channel[chan].rx; uint16_t tx = status.channel[chan].tx; printf("\tlane %zu: " "RX power: %.2f mW (%.2f dBm) TX bias: %.2f mA\n", chan + 1, power_mW(rx), power_dBm(rx), bias_mA(tx)); } ifconfig_sfp_free_sfp_status(&status); } if (verbose > 2) { struct ifconfig_sfp_dump dump; if (ifconfig_sfp_get_sfp_dump(lifh, ctx->ifname, &dump) == -1) return; if (ifconfig_sfp_id_is_qsfp(info.sfp_id)) { printf("\n\tSFF8436 DUMP (0xA0 128..255 range):\n"); hexdump(dump.data + QSFP_DUMP1_START, QSFP_DUMP1_SIZE, "\t", HD_OMIT_COUNT | HD_OMIT_CHARS); printf("\n\tSFF8436 DUMP (0xA0 0..81 range):\n"); hexdump(dump.data + QSFP_DUMP0_START, QSFP_DUMP0_SIZE, "\t", HD_OMIT_COUNT | HD_OMIT_CHARS); } else { printf("\n\tSFF8472 DUMP (0xA0 0..127 range):\n"); hexdump(dump.data + SFP_DUMP_START, SFP_DUMP_SIZE, "\t", HD_OMIT_COUNT | HD_OMIT_CHARS); } } } diff --git a/sbin/init/init.c b/sbin/init/init.c index dd8800d21910..d5200e4cfe35 100644 --- a/sbin/init/init.c +++ b/sbin/init/init.c @@ -1,2169 +1,2167 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Donn Seeley at Berkeley Software Design, Inc. * * 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 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1991, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #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 #ifdef SECURE #include #endif #ifdef LOGIN_CAP #include #endif #include "mntopts.h" #include "pathnames.h" /* * Sleep times; used to prevent thrashing. */ #define GETTY_SPACING 5 /* N secs minimum getty spacing */ #define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ #define GETTY_NSPACE 3 /* max. spacing count to bring reaction */ #define WINDOW_WAIT 3 /* wait N secs after starting window */ #define STALL_TIMEOUT 30 /* wait N secs after warning */ #define DEATH_WATCH 10 /* wait N secs for procs to die */ #define DEATH_SCRIPT 120 /* wait for 2min for /etc/rc.shutdown */ #define RESOURCE_RC "daemon" #define RESOURCE_WINDOW "default" #define RESOURCE_GETTY "default" #define SCRIPT_ARGV_SIZE 3 /* size of argv passed to execute_script, can be increased if needed */ static void handle(sig_t, ...); static void delset(sigset_t *, ...); static void stall(const char *, ...) __printflike(1, 2); static void warning(const char *, ...) __printflike(1, 2); static void emergency(const char *, ...) __printflike(1, 2); static void disaster(int); static void revoke_ttys(void); static int runshutdown(void); static char *strk(char *); static void runfinal(void); /* * We really need a recursive typedef... * The following at least guarantees that the return type of (*state_t)() * is sufficiently wide to hold a function pointer. */ typedef long (*state_func_t)(void); typedef state_func_t (*state_t)(void); static state_func_t single_user(void); static state_func_t runcom(void); static state_func_t read_ttys(void); static state_func_t multi_user(void); static state_func_t clean_ttys(void); static state_func_t catatonia(void); static state_func_t death(void); static state_func_t death_single(void); static state_func_t reroot(void); static state_func_t reroot_phase_two(void); static state_func_t run_script(const char *); static enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; static bool Reboot = false; static int howto = RB_AUTOBOOT; static bool devfs = false; static char *init_path_argv0; static void transition(state_t); static state_t requested_transition; static state_t current_state = death_single; static void execute_script(char *argv[]); static void open_console(void); static const char *get_shell(void); static void replace_init(char *path); static void write_stderr(const char *message); typedef struct init_session { pid_t se_process; /* controlling process */ time_t se_started; /* used to avoid thrashing */ int se_flags; /* status of session */ #define SE_SHUTDOWN 0x1 /* session won't be restarted */ #define SE_PRESENT 0x2 /* session is in /etc/ttys */ #define SE_IFEXISTS 0x4 /* session defined as "onifexists" */ #define SE_IFCONSOLE 0x8 /* session defined as "onifconsole" */ int se_nspace; /* spacing count */ char *se_device; /* filename of port */ char *se_getty; /* what to run on that port */ char *se_getty_argv_space; /* pre-parsed argument array space */ char **se_getty_argv; /* pre-parsed argument array */ char *se_window; /* window system (started only once) */ char *se_window_argv_space; /* pre-parsed argument array space */ char **se_window_argv; /* pre-parsed argument array */ char *se_type; /* default terminal type */ struct init_session *se_prev; struct init_session *se_next; } session_t; static void free_session(session_t *); static session_t *new_session(session_t *, struct ttyent *); static session_t *sessions; static char **construct_argv(char *); static void start_window_system(session_t *); static void collect_child(pid_t); static pid_t start_getty(session_t *); static void transition_handler(int); static void alrm_handler(int); static void setsecuritylevel(int); static int getsecuritylevel(void); static int setupargv(session_t *, struct ttyent *); #ifdef LOGIN_CAP static void setprocresources(const char *); #endif static bool clang; static int start_session_db(void); static void add_session(session_t *); static void del_session(session_t *); static session_t *find_session(pid_t); static DB *session_db; /* * The mother of all processes. */ int main(int argc, char *argv[]) { state_t initial_transition = runcom; char kenv_value[PATH_MAX]; int c, error; struct sigaction sa; sigset_t mask; /* Dispose of random users. */ if (getuid() != 0) errx(1, "%s", strerror(EPERM)); BOOTTRACE("init(8) starting..."); /* System V users like to reexec init. */ if (getpid() != 1) { #ifdef COMPAT_SYSV_INIT /* So give them what they want */ if (argc > 1) { if (strlen(argv[1]) == 1) { char runlevel = *argv[1]; int sig; switch (runlevel) { case '0': /* halt + poweroff */ sig = SIGUSR2; break; case '1': /* single-user */ sig = SIGTERM; break; case '6': /* reboot */ sig = SIGINT; break; case 'c': /* block further logins */ sig = SIGTSTP; break; case 'q': /* rescan /etc/ttys */ sig = SIGHUP; break; case 'r': /* remount root */ sig = SIGEMT; break; default: goto invalid; } kill(1, sig); _exit(0); } else invalid: errx(1, "invalid run-level ``%s''", argv[1]); } else #endif errx(1, "already running"); } init_path_argv0 = strdup(argv[0]); if (init_path_argv0 == NULL) err(1, "strdup"); /* * Note that this does NOT open a file... * Does 'init' deserve its own facility number? */ openlog("init", LOG_CONS, LOG_AUTH); /* * Create an initial session. */ if (setsid() < 0 && (errno != EPERM || getsid(0) != 1)) warning("initial setsid() failed: %m"); /* * Establish an initial user so that programs running * single user do not freak out and die (like passwd). */ if (setlogin("root") < 0) warning("setlogin() failed: %m"); /* * This code assumes that we always get arguments through flags, * never through bits set in some random machine register. */ while ((c = getopt(argc, argv, "dsfr")) != -1) switch (c) { case 'd': devfs = true; break; case 's': initial_transition = single_user; break; case 'f': runcom_mode = FASTBOOT; break; case 'r': initial_transition = reroot_phase_two; break; default: warning("unrecognized flag '-%c'", c); break; } if (optind != argc) warning("ignoring excess arguments"); /* * We catch or block signals rather than ignore them, * so that they get reset on exec. */ handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, 0); handle(transition_handler, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP, SIGUSR1, SIGUSR2, SIGWINCH, 0); handle(alrm_handler, SIGALRM, 0); sigfillset(&mask); delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP, SIGALRM, SIGUSR1, SIGUSR2, SIGWINCH, 0); sigprocmask(SIG_SETMASK, &mask, NULL); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; sigaction(SIGTTIN, &sa, NULL); sigaction(SIGTTOU, &sa, NULL); /* * Paranoia. */ close(0); close(1); close(2); if (kenv(KENV_GET, "init_exec", kenv_value, sizeof(kenv_value)) > 0) { replace_init(kenv_value); _exit(0); /* reboot */ } if (kenv(KENV_GET, "init_script", kenv_value, sizeof(kenv_value)) > 0) { state_func_t next_transition; if ((next_transition = run_script(kenv_value)) != NULL) initial_transition = (state_t) next_transition; } if (kenv(KENV_GET, "init_chroot", kenv_value, sizeof(kenv_value)) > 0) { if (chdir(kenv_value) != 0 || chroot(".") != 0) warning("Can't chroot to %s: %m", kenv_value); } /* * Additional check if devfs needs to be mounted: * If "/" and "/dev" have the same device number, * then it hasn't been mounted yet. */ if (!devfs) { struct stat stst; dev_t root_devno; stat("/", &stst); root_devno = stst.st_dev; if (stat("/dev", &stst) != 0) warning("Can't stat /dev: %m"); else if (stst.st_dev == root_devno) devfs = true; } if (devfs) { struct iovec iov[4]; char *s; int i; char _fstype[] = "fstype"; char _devfs[] = "devfs"; char _fspath[] = "fspath"; char _path_dev[]= _PATH_DEV; iov[0].iov_base = _fstype; iov[0].iov_len = sizeof(_fstype); iov[1].iov_base = _devfs; iov[1].iov_len = sizeof(_devfs); iov[2].iov_base = _fspath; iov[2].iov_len = sizeof(_fspath); /* * Try to avoid the trailing slash in _PATH_DEV. * Be *very* defensive. */ s = strdup(_PATH_DEV); if (s != NULL) { i = strlen(s); if (i > 0 && s[i - 1] == '/') s[i - 1] = '\0'; iov[3].iov_base = s; iov[3].iov_len = strlen(s) + 1; } else { iov[3].iov_base = _path_dev; iov[3].iov_len = sizeof(_path_dev); } nmount(iov, 4, 0); if (s != NULL) free(s); } if (initial_transition != reroot_phase_two) { /* * Unmount reroot leftovers. This runs after init(8) * gets reexecuted after reroot_phase_two() is done. */ error = unmount(_PATH_REROOT, MNT_FORCE); if (error != 0 && errno != EINVAL) warning("Cannot unmount %s: %m", _PATH_REROOT); } /* * Start the state machine. */ transition(initial_transition); /* * Should never reach here. */ return 1; } /* * Associate a function with a signal handler. */ static void handle(sig_t handler, ...) { int sig; struct sigaction sa; sigset_t mask_everything; va_list ap; va_start(ap, handler); sa.sa_handler = handler; sigfillset(&mask_everything); while ((sig = va_arg(ap, int)) != 0) { sa.sa_mask = mask_everything; /* XXX SA_RESTART? */ sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; sigaction(sig, &sa, NULL); } va_end(ap); } /* * Delete a set of signals from a mask. */ static void delset(sigset_t *maskp, ...) { int sig; va_list ap; va_start(ap, maskp); while ((sig = va_arg(ap, int)) != 0) sigdelset(maskp, sig); va_end(ap); } /* * Log a message and sleep for a while (to give someone an opportunity * to read it and to save log or hardcopy output if the problem is chronic). * NB: should send a message to the session logger to avoid blocking. */ static void stall(const char *message, ...) { va_list ap; va_start(ap, message); vsyslog(LOG_ALERT, message, ap); va_end(ap); sleep(STALL_TIMEOUT); } /* * Like stall(), but doesn't sleep. * If cpp had variadic macros, the two functions could be #defines for another. * NB: should send a message to the session logger to avoid blocking. */ static void warning(const char *message, ...) { va_list ap; va_start(ap, message); vsyslog(LOG_ALERT, message, ap); va_end(ap); } /* * Log an emergency message. * NB: should send a message to the session logger to avoid blocking. */ static void emergency(const char *message, ...) { va_list ap; va_start(ap, message); vsyslog(LOG_EMERG, message, ap); va_end(ap); } /* * Catch an unexpected signal. */ static void disaster(int sig) { emergency("fatal signal: %s", (unsigned)sig < NSIG ? sys_siglist[sig] : "unknown signal"); sleep(STALL_TIMEOUT); _exit(sig); /* reboot */ } /* * Get the security level of the kernel. */ static int getsecuritylevel(void) { #ifdef KERN_SECURELVL int name[2], curlevel; size_t len; name[0] = CTL_KERN; name[1] = KERN_SECURELVL; len = sizeof curlevel; if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { emergency("cannot get kernel security level: %m"); return (-1); } return (curlevel); #else return (-1); #endif } /* * Set the security level of the kernel. */ static void setsecuritylevel(int newlevel) { #ifdef KERN_SECURELVL int name[2], curlevel; curlevel = getsecuritylevel(); if (newlevel == curlevel) return; name[0] = CTL_KERN; name[1] = KERN_SECURELVL; if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { emergency( "cannot change kernel security level from %d to %d: %m", curlevel, newlevel); return; } #ifdef SECURE warning("kernel security level changed from %d to %d", curlevel, newlevel); #endif #endif } /* * Change states in the finite state machine. * The initial state is passed as an argument. */ static void transition(state_t s) { current_state = s; for (;;) current_state = (state_t) (*current_state)(); } /* * Start a session and allocate a controlling terminal. * Only called by children of init after forking. */ static void open_console(void) { int fd; /* * Try to open /dev/console. Open the device with O_NONBLOCK to * prevent potential blocking on a carrier. */ revoke(_PATH_CONSOLE); if ((fd = open(_PATH_CONSOLE, O_RDWR | O_NONBLOCK)) != -1) { (void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); if (login_tty(fd) == 0) return; close(fd); } /* No luck. Log output to file if possible. */ if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) { stall("cannot open null device."); _exit(1); } if (fd != STDIN_FILENO) { dup2(fd, STDIN_FILENO); close(fd); } fd = open(_PATH_INITLOG, O_WRONLY | O_APPEND | O_CREAT, 0644); if (fd == -1) dup2(STDIN_FILENO, STDOUT_FILENO); else if (fd != STDOUT_FILENO) { dup2(fd, STDOUT_FILENO); close(fd); } dup2(STDOUT_FILENO, STDERR_FILENO); } static const char * get_shell(void) { static char kenv_value[PATH_MAX]; if (kenv(KENV_GET, "init_shell", kenv_value, sizeof(kenv_value)) > 0) return kenv_value; else return _PATH_BSHELL; } static void write_stderr(const char *message) { write(STDERR_FILENO, message, strlen(message)); } static int read_file(const char *path, void **bufp, size_t *bufsizep) { struct stat sb; size_t bufsize; void *buf; ssize_t nbytes; int error, fd; fd = open(path, O_RDONLY); if (fd < 0) { emergency("%s: %m", path); return (-1); } error = fstat(fd, &sb); if (error != 0) { emergency("fstat: %m"); close(fd); return (error); } bufsize = sb.st_size; buf = malloc(bufsize); if (buf == NULL) { emergency("malloc: %m"); close(fd); return (error); } nbytes = read(fd, buf, bufsize); if (nbytes != (ssize_t)bufsize) { emergency("read: %m"); close(fd); free(buf); return (error); } error = close(fd); if (error != 0) { emergency("close: %m"); free(buf); return (error); } *bufp = buf; *bufsizep = bufsize; return (0); } static int create_file(const char *path, const void *buf, size_t bufsize) { ssize_t nbytes; int error, fd; fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0700); if (fd < 0) { emergency("%s: %m", path); return (-1); } nbytes = write(fd, buf, bufsize); if (nbytes != (ssize_t)bufsize) { emergency("write: %m"); close(fd); return (-1); } error = close(fd); if (error != 0) { emergency("close: %m"); return (-1); } return (0); } static int mount_tmpfs(const char *fspath) { struct iovec *iov; char errmsg[255]; int error, iovlen; iov = NULL; iovlen = 0; memset(errmsg, 0, sizeof(errmsg)); build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "tmpfs"), (size_t)-1); build_iovec(&iov, &iovlen, "fspath", __DECONST(void *, fspath), (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); error = nmount(iov, iovlen, 0); if (error != 0) { if (*errmsg != '\0') { emergency("cannot mount tmpfs on %s: %s: %m", fspath, errmsg); } else { emergency("cannot mount tmpfs on %s: %m", fspath); } return (error); } return (0); } static state_func_t reroot(void) { void *buf; size_t bufsize; int error; buf = NULL; bufsize = 0; revoke_ttys(); runshutdown(); /* * Make sure nobody can interfere with our scheme. * Ignore ESRCH, which can apparently happen when * there are no processes to kill. */ error = kill(-1, SIGKILL); if (error != 0 && errno != ESRCH) { emergency("kill(2) failed: %m"); goto out; } /* * Copy the init binary into tmpfs, so that we can unmount * the old rootfs without committing suicide. */ error = read_file(init_path_argv0, &buf, &bufsize); if (error != 0) goto out; error = mount_tmpfs(_PATH_REROOT); if (error != 0) goto out; error = create_file(_PATH_REROOT_INIT, buf, bufsize); if (error != 0) goto out; /* * Execute the temporary init. */ execl(_PATH_REROOT_INIT, _PATH_REROOT_INIT, "-r", NULL); emergency("cannot exec %s: %m", _PATH_REROOT_INIT); out: emergency("reroot failed; going to single user mode"); free(buf); return (state_func_t) single_user; } static state_func_t reroot_phase_two(void) { char init_path[PATH_MAX], *path, *path_component; size_t init_path_len; int nbytes, error; /* * Ask the kernel to mount the new rootfs. */ error = reboot(RB_REROOT); if (error != 0) { emergency("RB_REBOOT failed: %m"); goto out; } /* * Figure out where the destination init(8) binary is. Note that * the path could be different than what we've started with. Use * the value from kenv, if set, or the one from sysctl otherwise. * The latter defaults to a hardcoded value, but can be overridden * by a build time option. */ nbytes = kenv(KENV_GET, "init_path", init_path, sizeof(init_path)); if (nbytes <= 0) { init_path_len = sizeof(init_path); error = sysctlbyname("kern.init_path", init_path, &init_path_len, NULL, 0); if (error != 0) { emergency("failed to retrieve kern.init_path: %m"); goto out; } } /* * Repeat the init search logic from sys/kern/init_path.c */ path_component = init_path; while ((path = strsep(&path_component, ":")) != NULL) { /* * Execute init(8) from the new rootfs. */ execl(path, path, NULL); } emergency("cannot exec init from %s: %m", init_path); out: emergency("reroot failed; going to single user mode"); return (state_func_t) single_user; } /* * Bring the system up single user. */ static state_func_t single_user(void) { pid_t pid, wpid; int status; sigset_t mask; const char *shell; char *argv[2]; struct timeval tv, tn; #ifdef SECURE struct ttyent *typ; struct passwd *pp; static const char banner[] = "Enter root password, or ^D to go multi-user\n"; char *clear, *password; #endif #ifdef DEBUGSHELL char altshell[128]; #endif if (Reboot) { /* Instead of going single user, let's reboot the machine */ BOOTTRACE("shutting down the system"); sync(); /* Run scripts after all processes have been terminated. */ runfinal(); if (reboot(howto) == -1) { emergency("reboot(%#x) failed, %m", howto); _exit(1); /* panic and reboot */ } warning("reboot(%#x) returned", howto); _exit(0); /* panic as well */ } BOOTTRACE("going to single user mode"); shell = get_shell(); if ((pid = fork()) == 0) { /* * Start the single user session. */ open_console(); #ifdef SECURE /* * Check the root password. * We don't care if the console is 'on' by default; * it's the only tty that can be 'off' and 'secure'. */ typ = getttynam("console"); pp = getpwnam("root"); if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp && *pp->pw_passwd) { write_stderr(banner); for (;;) { clear = getpass("Password:"); if (clear == NULL || *clear == '\0') _exit(0); password = crypt(clear, pp->pw_passwd); explicit_bzero(clear, _PASSWORD_LEN); if (password != NULL && strcmp(password, pp->pw_passwd) == 0) break; warning("single-user login failed\n"); } } endttyent(); endpwent(); #endif /* SECURE */ #ifdef DEBUGSHELL { char *cp = altshell; int num; #define SHREQUEST "Enter full pathname of shell or RETURN for " write_stderr(SHREQUEST); write_stderr(shell); write_stderr(": "); while ((num = read(STDIN_FILENO, cp, 1)) != -1 && num != 0 && *cp != '\n' && cp < &altshell[127]) cp++; *cp = '\0'; if (altshell[0] != '\0') shell = altshell; } #endif /* DEBUGSHELL */ /* * Unblock signals. * We catch all the interesting ones, * and those are reset to SIG_DFL on exec. */ sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); /* * Fire off a shell. * If the default one doesn't work, try the Bourne shell. */ char name[] = "-sh"; argv[0] = name; argv[1] = NULL; execv(shell, argv); emergency("can't exec %s for single user: %m", shell); execv(_PATH_BSHELL, argv); emergency("can't exec %s for single user: %m", _PATH_BSHELL); sleep(STALL_TIMEOUT); _exit(1); } if (pid == -1) { /* * We are seriously hosed. Do our best. */ emergency("can't fork single-user shell, trying again"); while (waitpid(-1, (int *) 0, WNOHANG) > 0) continue; return (state_func_t) single_user; } requested_transition = 0; do { if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) collect_child(wpid); if (wpid == -1) { if (errno == EINTR) continue; warning("wait for single-user shell failed: %m; restarting"); return (state_func_t) single_user; } if (wpid == pid && WIFSTOPPED(status)) { warning("init: shell stopped, restarting\n"); kill(pid, SIGCONT); wpid = -1; } } while (wpid != pid && !requested_transition); if (requested_transition) return (state_func_t) requested_transition; if (!WIFEXITED(status)) { if (WTERMSIG(status) == SIGKILL) { /* * reboot(8) killed shell? */ warning("single user shell terminated."); gettimeofday(&tv, NULL); tn = tv; tv.tv_sec += STALL_TIMEOUT; while (tv.tv_sec > tn.tv_sec || (tv.tv_sec == tn.tv_sec && tv.tv_usec > tn.tv_usec)) { sleep(1); gettimeofday(&tn, NULL); } _exit(0); } else { warning("single user shell terminated, restarting"); return (state_func_t) single_user; } } runcom_mode = FASTBOOT; return (state_func_t) runcom; } /* * Run the system startup script. */ static state_func_t runcom(void) { state_func_t next_transition; BOOTTRACE("/etc/rc starting..."); if ((next_transition = run_script(_PATH_RUNCOM)) != NULL) return next_transition; BOOTTRACE("/etc/rc finished"); runcom_mode = AUTOBOOT; /* the default */ return (state_func_t) read_ttys; } static void execute_script(char *argv[]) { struct sigaction sa; char* sh_argv[3 + SCRIPT_ARGV_SIZE]; const char *shell, *script; int error, sh_argv_len, i; bzero(&sa, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sigaction(SIGTSTP, &sa, NULL); sigaction(SIGHUP, &sa, NULL); open_console(); sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL); #ifdef LOGIN_CAP setprocresources(RESOURCE_RC); #endif /* * Try to directly execute the script first. If it * fails, try the old method of passing the script path * to sh(1). Don't complain if it fails because of * the missing execute bit. */ script = argv[0]; error = access(script, X_OK); if (error == 0) { execv(script, argv); warning("can't directly exec %s: %m", script); } else if (errno != EACCES) { warning("can't access %s: %m", script); } shell = get_shell(); sh_argv[0] = __DECONST(char*, shell); sh_argv_len = 1; #ifdef SECURE if (strcmp(shell, _PATH_BSHELL) == 0) { sh_argv[1] = __DECONST(char*, "-o"); sh_argv[2] = __DECONST(char*, "verify"); sh_argv_len = 3; } #endif for (i = 0; i != SCRIPT_ARGV_SIZE; ++i) sh_argv[i + sh_argv_len] = argv[i]; execv(shell, sh_argv); stall("can't exec %s for %s: %m", shell, script); } /* * Execute binary, replacing init(8) as PID 1. */ static void replace_init(char *path) { char *argv[SCRIPT_ARGV_SIZE]; argv[0] = path; argv[1] = NULL; execute_script(argv); } /* * Run a shell script. * Returns 0 on success, otherwise the next transition to enter: * - single_user if fork/execv/waitpid failed, or if the script * terminated with a signal or exit code != 0. * - death_single if a SIGTERM was delivered to init(8). */ static state_func_t run_script(const char *script) { pid_t pid, wpid; int status; char *argv[SCRIPT_ARGV_SIZE]; const char *shell; shell = get_shell(); if ((pid = fork()) == 0) { char _autoboot[] = "autoboot"; argv[0] = __DECONST(char *, script); argv[1] = runcom_mode == AUTOBOOT ? _autoboot : NULL; argv[2] = NULL; execute_script(argv); sleep(STALL_TIMEOUT); _exit(1); /* force single user mode */ } if (pid == -1) { emergency("can't fork for %s on %s: %m", shell, script); while (waitpid(-1, (int *) 0, WNOHANG) > 0) continue; sleep(STALL_TIMEOUT); return (state_func_t) single_user; } /* * Copied from single_user(). This is a bit paranoid. */ requested_transition = 0; do { if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) collect_child(wpid); if (requested_transition == death_single || requested_transition == reroot) return (state_func_t) requested_transition; if (wpid == -1) { if (errno == EINTR) continue; warning("wait for %s on %s failed: %m; going to " "single user mode", shell, script); return (state_func_t) single_user; } if (wpid == pid && WIFSTOPPED(status)) { warning("init: %s on %s stopped, restarting\n", shell, script); kill(pid, SIGCONT); wpid = -1; } } while (wpid != pid); if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && requested_transition == catatonia) { /* /etc/rc executed /sbin/reboot; wait for the end quietly */ sigset_t s; sigfillset(&s); for (;;) sigsuspend(&s); } if (!WIFEXITED(status)) { warning("%s on %s terminated abnormally, going to single " "user mode", shell, script); return (state_func_t) single_user; } if (WEXITSTATUS(status)) return (state_func_t) single_user; return (state_func_t) 0; } /* * Open the session database. * * NB: We could pass in the size here; is it necessary? */ static int start_session_db(void) { if (session_db && (*session_db->close)(session_db)) emergency("session database close: %m"); if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == NULL) { emergency("session database open: %m"); return (1); } return (0); } /* * Add a new login session. */ static void add_session(session_t *sp) { DBT key; DBT data; key.data = &sp->se_process; key.size = sizeof sp->se_process; data.data = &sp; data.size = sizeof sp; if ((*session_db->put)(session_db, &key, &data, 0)) emergency("insert %d: %m", sp->se_process); } /* * Delete an old login session. */ static void del_session(session_t *sp) { DBT key; key.data = &sp->se_process; key.size = sizeof sp->se_process; if ((*session_db->del)(session_db, &key, 0)) emergency("delete %d: %m", sp->se_process); } /* * Look up a login session by pid. */ static session_t * find_session(pid_t pid) { DBT key; DBT data; session_t *ret; key.data = &pid; key.size = sizeof pid; if ((*session_db->get)(session_db, &key, &data, 0) != 0) return 0; bcopy(data.data, (char *)&ret, sizeof(ret)); return ret; } /* * Construct an argument vector from a command line. */ static char ** construct_argv(char *command) { int argc = 0; char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) * sizeof (char *)); if ((argv[argc++] = strk(command)) == NULL) { free(argv); return (NULL); } while ((argv[argc++] = strk((char *) 0)) != NULL) continue; return argv; } /* * Deallocate a session descriptor. */ static void free_session(session_t *sp) { free(sp->se_device); if (sp->se_getty) { free(sp->se_getty); free(sp->se_getty_argv_space); free(sp->se_getty_argv); } if (sp->se_window) { free(sp->se_window); free(sp->se_window_argv_space); free(sp->se_window_argv); } if (sp->se_type) free(sp->se_type); free(sp); } /* * Allocate a new session descriptor. * Mark it SE_PRESENT. */ static session_t * new_session(session_t *sprev, struct ttyent *typ) { session_t *sp; if ((typ->ty_status & TTY_ON) == 0 || typ->ty_name == 0 || typ->ty_getty == 0) return 0; sp = (session_t *) calloc(1, sizeof (session_t)); sp->se_flags |= SE_PRESENT; if ((typ->ty_status & TTY_IFEXISTS) != 0) sp->se_flags |= SE_IFEXISTS; if ((typ->ty_status & TTY_IFCONSOLE) != 0) sp->se_flags |= SE_IFCONSOLE; if (asprintf(&sp->se_device, "%s%s", _PATH_DEV, typ->ty_name) < 0) err(1, "asprintf"); if (setupargv(sp, typ) == 0) { free_session(sp); return (0); } sp->se_next = 0; if (sprev == NULL) { sessions = sp; sp->se_prev = 0; } else { sprev->se_next = sp; sp->se_prev = sprev; } return sp; } /* * Calculate getty and if useful window argv vectors. */ static int setupargv(session_t *sp, struct ttyent *typ) { if (sp->se_getty) { free(sp->se_getty); free(sp->se_getty_argv_space); free(sp->se_getty_argv); } if (asprintf(&sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name) < 0) err(1, "asprintf"); sp->se_getty_argv_space = strdup(sp->se_getty); sp->se_getty_argv = construct_argv(sp->se_getty_argv_space); if (sp->se_getty_argv == NULL) { warning("can't parse getty for port %s", sp->se_device); free(sp->se_getty); free(sp->se_getty_argv_space); sp->se_getty = sp->se_getty_argv_space = 0; return (0); } if (sp->se_window) { free(sp->se_window); free(sp->se_window_argv_space); free(sp->se_window_argv); } sp->se_window = sp->se_window_argv_space = 0; sp->se_window_argv = 0; if (typ->ty_window) { sp->se_window = strdup(typ->ty_window); sp->se_window_argv_space = strdup(sp->se_window); sp->se_window_argv = construct_argv(sp->se_window_argv_space); if (sp->se_window_argv == NULL) { warning("can't parse window for port %s", sp->se_device); free(sp->se_window_argv_space); free(sp->se_window); sp->se_window = sp->se_window_argv_space = 0; return (0); } } if (sp->se_type) free(sp->se_type); sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0; return (1); } /* * Walk the list of ttys and create sessions for each active line. */ static state_func_t read_ttys(void) { session_t *sp, *snext; struct ttyent *typ; /* * Destroy any previous session state. * There shouldn't be any, but just in case... */ for (sp = sessions; sp; sp = snext) { snext = sp->se_next; free_session(sp); } sessions = 0; if (start_session_db()) return (state_func_t) single_user; /* * Allocate a session entry for each active port. * Note that sp starts at 0. */ while ((typ = getttyent()) != NULL) if ((snext = new_session(sp, typ)) != NULL) sp = snext; endttyent(); return (state_func_t) multi_user; } /* * Start a window system running. */ static void start_window_system(session_t *sp) { pid_t pid; sigset_t mask; char term[64], *env[2]; int status; if ((pid = fork()) == -1) { emergency("can't fork for window system on port %s: %m", sp->se_device); /* hope that getty fails and we can try again */ return; } if (pid) { waitpid(-1, &status, 0); return; } /* reparent window process to the init to not make a zombie on exit */ if ((pid = fork()) == -1) { emergency("can't fork for window system on port %s: %m", sp->se_device); _exit(1); } if (pid) _exit(0); sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); if (setsid() < 0) emergency("setsid failed (window) %m"); #ifdef LOGIN_CAP setprocresources(RESOURCE_WINDOW); #endif if (sp->se_type) { /* Don't use malloc after fork */ strcpy(term, "TERM="); strlcat(term, sp->se_type, sizeof(term)); env[0] = term; env[1] = NULL; } else env[0] = NULL; execve(sp->se_window_argv[0], sp->se_window_argv, env); stall("can't exec window system '%s' for port %s: %m", sp->se_window_argv[0], sp->se_device); _exit(1); } /* * Start a login session running. */ static pid_t start_getty(session_t *sp) { pid_t pid; sigset_t mask; time_t current_time = time((time_t *) 0); int too_quick = 0; char term[64], *env[2]; if (current_time >= sp->se_started && current_time - sp->se_started < GETTY_SPACING) { if (++sp->se_nspace > GETTY_NSPACE) { sp->se_nspace = 0; too_quick = 1; } } else sp->se_nspace = 0; /* * fork(), not vfork() -- we can't afford to block. */ if ((pid = fork()) == -1) { emergency("can't fork for getty on port %s: %m", sp->se_device); return -1; } if (pid) return pid; if (too_quick) { warning("getty repeating too quickly on port %s, sleeping %d secs", sp->se_device, GETTY_SLEEP); sleep((unsigned) GETTY_SLEEP); } if (sp->se_window) { start_window_system(sp); sleep(WINDOW_WAIT); } sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); #ifdef LOGIN_CAP setprocresources(RESOURCE_GETTY); #endif if (sp->se_type) { /* Don't use malloc after fork */ strcpy(term, "TERM="); strlcat(term, sp->se_type, sizeof(term)); env[0] = term; env[1] = NULL; } else env[0] = NULL; execve(sp->se_getty_argv[0], sp->se_getty_argv, env); stall("can't exec getty '%s' for port %s: %m", sp->se_getty_argv[0], sp->se_device); _exit(1); } /* * Return 1 if the session is defined as "onifexists" * or "onifconsole" and the device node does not exist. */ static int session_has_no_tty(session_t *sp) { int fd; if ((sp->se_flags & SE_IFEXISTS) == 0 && (sp->se_flags & SE_IFCONSOLE) == 0) return (0); fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0); if (fd < 0) { if (errno == ENOENT) return (1); return (0); } close(fd); return (0); } /* * Collect exit status for a child. * If an exiting login, start a new login running. */ static void collect_child(pid_t pid) { session_t *sp, *sprev, *snext; if (! sessions) return; if (! (sp = find_session(pid))) return; del_session(sp); sp->se_process = 0; if (sp->se_flags & SE_SHUTDOWN || session_has_no_tty(sp)) { if ((sprev = sp->se_prev) != NULL) sprev->se_next = sp->se_next; else sessions = sp->se_next; if ((snext = sp->se_next) != NULL) snext->se_prev = sp->se_prev; free_session(sp); return; } if ((pid = start_getty(sp)) == -1) { /* serious trouble */ requested_transition = clean_ttys; return; } sp->se_process = pid; sp->se_started = time((time_t *) 0); add_session(sp); } static const char * get_current_state(void) { if (current_state == single_user) return ("single-user"); if (current_state == runcom) return ("runcom"); if (current_state == read_ttys) return ("read-ttys"); if (current_state == multi_user) return ("multi-user"); if (current_state == clean_ttys) return ("clean-ttys"); if (current_state == catatonia) return ("catatonia"); if (current_state == death) return ("death"); if (current_state == death_single) return ("death-single"); return ("unknown"); } static void boottrace_transition(int sig) { const char *action; switch (sig) { case SIGUSR2: action = "halt & poweroff"; break; case SIGUSR1: action = "halt"; break; case SIGINT: action = "reboot"; break; case SIGWINCH: action = "powercycle"; break; case SIGTERM: action = Reboot ? "reboot" : "single-user"; break; default: BOOTTRACE("signal %d from %s", sig, get_current_state()); return; } /* Trace the shutdown reason. */ SHUTTRACE("%s from %s", action, get_current_state()); } /* * Catch a signal and request a state transition. */ static void transition_handler(int sig) { boottrace_transition(sig); switch (sig) { case SIGHUP: if (current_state == read_ttys || current_state == multi_user || current_state == clean_ttys || current_state == catatonia) requested_transition = clean_ttys; break; case SIGUSR2: howto = RB_POWEROFF; case SIGUSR1: howto |= RB_HALT; case SIGWINCH: case SIGINT: if (sig == SIGWINCH) howto |= RB_POWERCYCLE; Reboot = true; case SIGTERM: if (current_state == read_ttys || current_state == multi_user || current_state == clean_ttys || current_state == catatonia) requested_transition = death; else requested_transition = death_single; break; case SIGTSTP: if (current_state == runcom || current_state == read_ttys || current_state == clean_ttys || current_state == multi_user || current_state == catatonia) requested_transition = catatonia; break; case SIGEMT: requested_transition = reroot; break; default: requested_transition = 0; break; } } /* * Take the system multiuser. */ static state_func_t multi_user(void) { static bool inmultiuser = false; pid_t pid; session_t *sp; requested_transition = 0; /* * If the administrator has not set the security level to -1 * to indicate that the kernel should not run multiuser in secure * mode, and the run script has not set a higher level of security * than level 1, then put the kernel into secure mode. */ if (getsecuritylevel() == 0) setsecuritylevel(1); for (sp = sessions; sp; sp = sp->se_next) { if (sp->se_process) continue; if (session_has_no_tty(sp)) continue; if ((pid = start_getty(sp)) == -1) { /* serious trouble */ requested_transition = clean_ttys; break; } sp->se_process = pid; sp->se_started = time((time_t *) 0); add_session(sp); } if (requested_transition == 0 && !inmultiuser) { inmultiuser = true; /* This marks the change from boot-time tracing to run-time. */ RUNTRACE("multi-user start"); } while (!requested_transition) if ((pid = waitpid(-1, (int *) 0, 0)) != -1) collect_child(pid); return (state_func_t) requested_transition; } /* * This is an (n*2)+(n^2) algorithm. We hope it isn't run often... */ static state_func_t clean_ttys(void) { session_t *sp, *sprev; struct ttyent *typ; int devlen; char *old_getty, *old_window, *old_type; /* * mark all sessions for death, (!SE_PRESENT) * as we find or create new ones they'll be marked as keepers, * we'll later nuke all the ones not found in /etc/ttys */ for (sp = sessions; sp != NULL; sp = sp->se_next) sp->se_flags &= ~SE_PRESENT; devlen = sizeof(_PATH_DEV) - 1; while ((typ = getttyent()) != NULL) { for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) break; if (sp) { /* we want this one to live */ sp->se_flags |= SE_PRESENT; if ((typ->ty_status & TTY_ON) == 0 || typ->ty_getty == 0) { sp->se_flags |= SE_SHUTDOWN; kill(sp->se_process, SIGHUP); continue; } sp->se_flags &= ~SE_SHUTDOWN; old_getty = sp->se_getty ? strdup(sp->se_getty) : 0; old_window = sp->se_window ? strdup(sp->se_window) : 0; old_type = sp->se_type ? strdup(sp->se_type) : 0; if (setupargv(sp, typ) == 0) { warning("can't parse getty for port %s", sp->se_device); sp->se_flags |= SE_SHUTDOWN; kill(sp->se_process, SIGHUP); } else if ( !old_getty || (!old_type && sp->se_type) || (old_type && !sp->se_type) || (!old_window && sp->se_window) || (old_window && !sp->se_window) || (strcmp(old_getty, sp->se_getty) != 0) || (old_window && strcmp(old_window, sp->se_window) != 0) || (old_type && strcmp(old_type, sp->se_type) != 0) ) { /* Don't set SE_SHUTDOWN here */ sp->se_nspace = 0; sp->se_started = 0; kill(sp->se_process, SIGHUP); } if (old_getty) free(old_getty); if (old_window) free(old_window); if (old_type) free(old_type); continue; } new_session(sprev, typ); } endttyent(); /* * sweep through and kill all deleted sessions * ones who's /etc/ttys line was deleted (SE_PRESENT unset) */ for (sp = sessions; sp != NULL; sp = sp->se_next) { if ((sp->se_flags & SE_PRESENT) == 0) { sp->se_flags |= SE_SHUTDOWN; kill(sp->se_process, SIGHUP); } } return (state_func_t) multi_user; } /* * Block further logins. */ static state_func_t catatonia(void) { session_t *sp; for (sp = sessions; sp; sp = sp->se_next) sp->se_flags |= SE_SHUTDOWN; return (state_func_t) multi_user; } /* * Note SIGALRM. */ static void alrm_handler(int sig) { (void)sig; clang = true; } /* * Bring the system down to single user. */ static state_func_t death(void) { int block, blocked; size_t len; /* Temporarily block suspend. */ len = sizeof(blocked); block = 1; if (sysctlbyname("kern.suspend_blocked", &blocked, &len, &block, sizeof(block)) == -1) blocked = 0; /* * Also revoke the TTY here. Because runshutdown() may reopen * the TTY whose getty we're killing here, there is no guarantee * runshutdown() will perform the initial open() call, causing * the terminal attributes to be misconfigured. */ revoke_ttys(); /* Try to run the rc.shutdown script within a period of time */ runshutdown(); /* Unblock suspend if we blocked it. */ if (!blocked) sysctlbyname("kern.suspend_blocked", NULL, NULL, &blocked, sizeof(blocked)); return (state_func_t) death_single; } /* * Do what is necessary to reinitialize single user mode or reboot * from an incomplete state. */ static state_func_t death_single(void) { int i; pid_t pid; static const int death_sigs[2] = { SIGTERM, SIGKILL }; revoke(_PATH_CONSOLE); BOOTTRACE("start killing user processes"); for (i = 0; i < 2; ++i) { if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) return (state_func_t) single_user; clang = false; alarm(DEATH_WATCH); do if ((pid = waitpid(-1, (int *)0, 0)) != -1) collect_child(pid); while (!clang && errno != ECHILD); if (errno == ECHILD) return (state_func_t) single_user; } warning("some processes would not die; ps axl advised"); return (state_func_t) single_user; } static void revoke_ttys(void) { session_t *sp; for (sp = sessions; sp; sp = sp->se_next) { sp->se_flags |= SE_SHUTDOWN; kill(sp->se_process, SIGHUP); revoke(sp->se_device); } } /* * Run the system shutdown script. * * Exit codes: XXX I should document more * -2 shutdown script terminated abnormally * -1 fatal error - can't run script * 0 good. * >0 some error (exit code) */ static int runshutdown(void) { pid_t pid, wpid; int status; int shutdowntimeout; size_t len; char *argv[SCRIPT_ARGV_SIZE]; struct stat sb; BOOTTRACE("init(8): start rc.shutdown"); /* * rc.shutdown is optional, so to prevent any unnecessary * complaints from the shell we simply don't run it if the * file does not exist. If the stat() here fails for other * reasons, we'll let the shell complain. */ if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT) return 0; if ((pid = fork()) == 0) { char _reboot[] = "reboot"; char _single[] = "single"; char _path_rundown[] = _PATH_RUNDOWN; argv[0] = _path_rundown; argv[1] = Reboot ? _reboot : _single; argv[2] = NULL; execute_script(argv); _exit(1); /* force single user mode */ } if (pid == -1) { emergency("can't fork for %s: %m", _PATH_RUNDOWN); while (waitpid(-1, (int *) 0, WNOHANG) > 0) continue; sleep(STALL_TIMEOUT); return -1; } len = sizeof(shutdowntimeout); if (sysctlbyname("kern.init_shutdown_timeout", &shutdowntimeout, &len, NULL, 0) == -1 || shutdowntimeout < 2) shutdowntimeout = DEATH_SCRIPT; alarm(shutdowntimeout); clang = false; /* * Copied from single_user(). This is a bit paranoid. * Use the same ALRM handler. */ do { if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) collect_child(wpid); if (clang) { /* we were waiting for the sub-shell */ kill(wpid, SIGTERM); warning("timeout expired for %s: %m; going to " "single user mode", _PATH_RUNDOWN); BOOTTRACE("rc.shutdown's %d sec timeout expired", shutdowntimeout); return -1; } if (wpid == -1) { if (errno == EINTR) continue; warning("wait for %s failed: %m; going to " "single user mode", _PATH_RUNDOWN); return -1; } if (wpid == pid && WIFSTOPPED(status)) { warning("init: %s stopped, restarting\n", _PATH_RUNDOWN); kill(pid, SIGCONT); wpid = -1; } } while (wpid != pid && !clang); /* Turn off the alarm */ alarm(0); if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && requested_transition == catatonia) { /* * /etc/rc.shutdown executed /sbin/reboot; * wait for the end quietly */ sigset_t s; sigfillset(&s); for (;;) sigsuspend(&s); } if (!WIFEXITED(status)) { warning("%s terminated abnormally, going to " "single user mode", _PATH_RUNDOWN); return -2; } if ((status = WEXITSTATUS(status)) != 0) warning("%s returned status %d", _PATH_RUNDOWN, status); return status; } static char * strk(char *p) { static char *t; char *q; int c; if (p) t = p; if (!t) return 0; c = *t; while (c == ' ' || c == '\t' ) c = *++t; if (!c) { t = 0; return 0; } q = t; if (c == '\'') { c = *++t; q = t; while (c && c != '\'') c = *++t; if (!c) /* unterminated string */ q = t = 0; else *t++ = 0; } else { while (c && c != ' ' && c != '\t' ) c = *++t; *t++ = 0; if (!c) t = 0; } return q; } #ifdef LOGIN_CAP static void setprocresources(const char *cname) { login_cap_t *lc; if ((lc = login_getclassbyname(cname, NULL)) != NULL) { setusercontext(lc, (struct passwd*)NULL, 0, LOGIN_SETENV | LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETLOGINCLASS | LOGIN_SETCPUMASK); login_close(lc); } } #endif /* * Run /etc/rc.final to execute scripts after all user processes have been * terminated. */ static void runfinal(void) { struct stat sb; pid_t other_pid, pid; sigset_t mask; /* Avoid any surprises. */ alarm(0); /* rc.final is optional. */ if (stat(_PATH_RUNFINAL, &sb) == -1 && errno == ENOENT) return; if (access(_PATH_RUNFINAL, X_OK) != 0) { warning("%s exists, but not executable", _PATH_RUNFINAL); return; } pid = fork(); if (pid == 0) { /* * Reopen stdin/stdout/stderr so that scripts can write to * console. */ close(0); open(_PATH_DEVNULL, O_RDONLY); close(1); close(2); open_console(); dup2(1, 2); sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); signal(SIGCHLD, SIG_DFL); execl(_PATH_RUNFINAL, _PATH_RUNFINAL, NULL); perror("execl(" _PATH_RUNFINAL ") failed"); exit(1); } /* Wait for rc.final script to exit */ while ((other_pid = waitpid(-1, NULL, 0)) != pid && other_pid > 0) { continue; } } diff --git a/sbin/mknod/mknod.c b/sbin/mknod/mknod.c index be7c97cf06fb..091c05cbfac2 100644 --- a/sbin/mknod/mknod.c +++ b/sbin/mknod/mknod.c @@ -1,169 +1,167 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kevin Fall. * * 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 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)mknod.c 8.1 (Berkeley) 6/5/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include static void usage(void) { (void)fprintf(stderr, "usage: mknod name\n" " mknod name [b | c] major minor [owner:group]\n"); exit(1); } static u_long id(const char *name, const char *type) { u_long val; char *ep; /* * XXX * We know that uid_t's and gid_t's are unsigned longs. */ errno = 0; val = strtoul(name, &ep, 10); if (errno) err(1, "%s", name); if (*ep != '\0') errx(1, "%s: illegal %s name", name, type); return (val); } static gid_t a_gid(const char *s) { struct group *gr; if (*s == '\0') /* Argument was "uid[:.]". */ errx(1, "group must be specified when the owner is"); return ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid; } static uid_t a_uid(const char *s) { struct passwd *pw; if (*s == '\0') /* Argument was "[:.]gid". */ errx(1, "owner must be specified when the group is"); return ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid; } int main(int argc, char **argv) { int range_error; uid_t uid; gid_t gid; mode_t mode; dev_t dev; char *cp, *endp; long mymajor, myminor; if (argc != 2 && argc != 5 && argc != 6) usage(); if (argc >= 5) { mode = 0666; if (argv[2][0] == 'c') mode |= S_IFCHR; else if (argv[2][0] == 'b') mode |= S_IFBLK; else errx(1, "node must be type 'b' or 'c'"); errno = 0; mymajor = (long)strtoul(argv[3], &endp, 0); if (endp == argv[3] || *endp != '\0') errx(1, "%s: non-numeric major number", argv[3]); range_error = errno; errno = 0; myminor = (long)strtoul(argv[4], &endp, 0); if (endp == argv[4] || *endp != '\0') errx(1, "%s: non-numeric minor number", argv[4]); range_error |= errno; dev = makedev(mymajor, myminor); if (range_error || major(dev) != mymajor || (long)(u_int)minor(dev) != myminor) errx(1, "major or minor number too large"); } else { mode = 0666 | S_IFCHR; dev = 0; } uid = gid = -1; if (6 == argc) { /* have owner:group */ if ((cp = strchr(argv[5], ':')) != NULL) { *cp++ = '\0'; gid = a_gid(cp); } else usage(); uid = a_uid(argv[5]); } if (mknod(argv[1], mode, dev) != 0) err(1, "%s", argv[1]); if (6 == argc) if (chown(argv[1], uid, gid)) err(1, "setting ownership on %s", argv[1]); exit(0); } diff --git a/sbin/mount/mount_fs.c b/sbin/mount/mount_fs.c index 5674e94594bf..7aac2b0ce104 100644 --- a/sbin/mount/mount_fs.c +++ b/sbin/mount/mount_fs.c @@ -1,141 +1,139 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * 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 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)mount_fs.c 8.6 (Berkeley) 4/26/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include "extern.h" #include "mntopts.h" static struct mntopt mopts[] = { MOPT_STDOPTS, MOPT_END }; static void usage(void) { (void)fprintf(stderr, "usage: mount [-t fstype] [-o options] target_fs mount_point\n"); exit(1); } int mount_fs(const char *vfstype, int argc, char *argv[]) { struct iovec *iov; int iovlen; int mntflags = 0; int ch; char *dev, *dir, mntpath[MAXPATHLEN]; char fstype[32]; char errmsg[255]; char *p, *val; strlcpy(fstype, vfstype, sizeof(fstype)); memset(errmsg, 0, sizeof(errmsg)); getmnt_silent = 1; iov = NULL; iovlen = 0; optind = optreset = 1; /* Reset for parse of new argv. */ while ((ch = getopt(argc, argv, "o:")) != -1) { switch(ch) { case 'o': getmntopts(optarg, mopts, &mntflags, 0); p = strchr(optarg, '='); val = NULL; if (p != NULL) { *p = '\0'; val = p + 1; } build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc != 2) usage(); dev = argv[0]; dir = argv[1]; if (checkpath(dir, mntpath) != 0) { warn("%s", mntpath); return (1); } (void)rmslashes(dev, dev); build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); build_iovec(&iov, &iovlen, "from", dev, (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); if (nmount(iov, iovlen, mntflags) == -1) { if (*errmsg != '\0') warn("%s: %s", dev, errmsg); else warn("%s", dev); return (1); } return (0); } diff --git a/sbin/mount_cd9660/mount_cd9660.c b/sbin/mount_cd9660/mount_cd9660.c index f322ac73a439..f867e94da417 100644 --- a/sbin/mount_cd9660/mount_cd9660.c +++ b/sbin/mount_cd9660/mount_cd9660.c @@ -1,272 +1,270 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension * Support code is derived from software contributed to Berkeley * by Atsushi Murai (amurai@spec.co.jp). * * 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 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. * * @(#)mount_cd9660.c 8.7 (Berkeley) 5/1/95 */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint /* static char sccsid[] = "@(#)mount_cd9660.c 8.7 (Berkeley) 5/1/95"; */ -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mntopts.h" static struct mntopt mopts[] = { MOPT_STDOPTS, MOPT_UPDATE, MOPT_END }; static int get_ssector(const char *dev); static int set_charset(struct iovec **, int *iovlen, const char *); void usage(void); int main(int argc, char **argv) { struct iovec *iov; int iovlen; int ch, mntflags; char *dev, *dir, *p, *val, mntpath[MAXPATHLEN]; int verbose; int ssector; /* starting sector, 0 for 1st session */ char fstype[] = "cd9660"; iov = NULL; iovlen = 0; mntflags = verbose = 0; ssector = -1; while ((ch = getopt(argc, argv, "begjo:rs:vC:")) != -1) switch (ch) { case 'b': build_iovec(&iov, &iovlen, "brokenjoliet", NULL, (size_t)-1); break; case 'e': build_iovec(&iov, &iovlen, "extatt", NULL, (size_t)-1); break; case 'g': build_iovec(&iov, &iovlen, "gens", NULL, (size_t)-1); break; case 'j': build_iovec(&iov, &iovlen, "nojoliet", NULL, (size_t)-1); break; case 'o': getmntopts(optarg, mopts, &mntflags, NULL); p = strchr(optarg, '='); val = NULL; if (p != NULL) { *p = '\0'; val = p + 1; } build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); break; case 'r': build_iovec(&iov, &iovlen, "norrip", NULL, (size_t)-1); break; case 's': ssector = atoi(optarg); break; case 'v': verbose++; break; case 'C': if (set_charset(&iov, &iovlen, optarg) == -1) err(EX_OSERR, "cd9660_iconv"); build_iovec(&iov, &iovlen, "kiconv", NULL, (size_t)-1); break; case '?': default: usage(); } argc -= optind; argv += optind; if (argc != 2) usage(); dev = argv[0]; dir = argv[1]; /* * Resolve the mountpoint with realpath(3) and remove unnecessary * slashes from the devicename if there are any. */ if (checkpath(dir, mntpath) != 0) err(1, "%s", mntpath); (void)rmslashes(dev, dev); if (ssector == -1) { /* * The start of the session has not been specified on * the command line. If we can successfully read the * TOC of a CD-ROM, use the last data track we find. * Otherwise, just use 0, in order to mount the very * first session. This is compatible with the * historic behaviour of mount_cd9660(8). If the user * has specified -s above, we don't get here * and leave the user's will. */ if ((ssector = get_ssector(dev)) == -1) { if (verbose) printf("could not determine starting sector, " "using very first session\n"); ssector = 0; } else if (verbose) printf("using starting sector %d\n", ssector); } mntflags |= MNT_RDONLY; build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); build_iovec(&iov, &iovlen, "from", dev, (size_t)-1); build_iovec_argf(&iov, &iovlen, "ssector", "%d", ssector); if (nmount(iov, iovlen, mntflags) < 0) err(1, "%s", dev); exit(0); } void usage(void) { (void)fprintf(stderr, "usage: mount_cd9660 [-begjrv] [-C charset] [-o options] [-s startsector]\n" " special node\n"); exit(EX_USAGE); } static int get_ssector(const char *dev) { struct ioc_toc_header h; struct ioc_read_toc_entry t; struct cd_toc_entry toc_buffer[100]; int fd, ntocentries, i; if ((fd = open(dev, O_RDONLY)) == -1) return -1; if (ioctl(fd, CDIOREADTOCHEADER, &h) == -1) { close(fd); return -1; } ntocentries = h.ending_track - h.starting_track + 1; if (ntocentries > 100) { /* unreasonable, only 100 allowed */ close(fd); return -1; } t.address_format = CD_LBA_FORMAT; t.starting_track = 0; t.data_len = ntocentries * sizeof(struct cd_toc_entry); t.data = toc_buffer; if (ioctl(fd, CDIOREADTOCENTRYS, (char *) &t) == -1) { close(fd); return -1; } close(fd); for (i = ntocentries - 1; i >= 0; i--) if ((toc_buffer[i].control & 4) != 0) /* found a data track */ break; if (i < 0) return -1; return ntohl(toc_buffer[i].addr.lba); } static int set_charset(struct iovec **iov, int *iovlen, const char *localcs) { int error; char *cs_disk; /* disk charset for Joliet cs conversion */ char *cs_local; /* local charset for Joliet cs conversion */ cs_disk = NULL; cs_local = NULL; if (modfind("cd9660_iconv") < 0) if (kldload("cd9660_iconv") < 0 || modfind("cd9660_iconv") < 0) { warnx( "cannot find or load \"cd9660_iconv\" kernel module"); return (-1); } if ((cs_disk = malloc(ICONV_CSNMAXLEN)) == NULL) return (-1); if ((cs_local = malloc(ICONV_CSNMAXLEN)) == NULL) { free(cs_disk); return (-1); } strncpy(cs_disk, ENCODING_UNICODE, ICONV_CSNMAXLEN); strncpy(cs_local, kiconv_quirkcs(localcs, KICONV_VENDOR_MICSFT), ICONV_CSNMAXLEN); error = kiconv_add_xlat16_cspairs(cs_disk, cs_local); if (error) return (-1); build_iovec(iov, iovlen, "cs_disk", cs_disk, (size_t)-1); build_iovec(iov, iovlen, "cs_local", cs_local, (size_t)-1); return (0); } diff --git a/sbin/mount_msdosfs/mount_msdosfs.c b/sbin/mount_msdosfs/mount_msdosfs.c index 36be6a161ecf..9128914bc2b0 100644 --- a/sbin/mount_msdosfs/mount_msdosfs.c +++ b/sbin/mount_msdosfs/mount_msdosfs.c @@ -1,327 +1,322 @@ /* $NetBSD: mount_msdos.c,v 1.18 1997/09/16 12:24:18 lukem Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1994 Christopher G. Demetriou * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Christopher G. Demetriou. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include /* must be after stdio to declare fparseln */ #include #include #include #include #include #include "mntopts.h" static gid_t a_gid(char *); static uid_t a_uid(char *); static mode_t a_mask(char *); static void usage(void) __dead2; static int set_charset(struct iovec **iov, int *iovlen, const char *, const char *); int main(int argc, char **argv) { struct iovec *iov = NULL; int iovlen = 0; struct stat sb; int c, set_gid, set_uid, set_mask, set_dirmask; char *dev, *dir, mntpath[MAXPATHLEN], *csp; char fstype[] = "msdosfs"; char errmsg[255] = {0}; char *cs_dos = NULL; char *cs_local = NULL; mode_t mask = 0, dirmask = 0; uid_t uid = 0; gid_t gid = 0; set_gid = set_uid = set_mask = set_dirmask = 0; while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:W:")) != -1) { switch (c) { case 's': build_iovec(&iov, &iovlen, "shortnames", NULL, (size_t)-1); break; case 'l': build_iovec(&iov, &iovlen, "longnames", NULL, (size_t)-1); break; case '9': build_iovec_argf(&iov, &iovlen, "nowin95", "", (size_t)-1); break; case 'u': uid = a_uid(optarg); set_uid = 1; break; case 'g': gid = a_gid(optarg); set_gid = 1; break; case 'm': mask = a_mask(optarg); set_mask = 1; break; case 'M': dirmask = a_mask(optarg); set_dirmask = 1; break; case 'L': { const char *quirk = NULL; if (setlocale(LC_CTYPE, optarg) == NULL) err(EX_CONFIG, "%s", optarg); csp = strchr(optarg,'.'); if (!csp) err(EX_CONFIG, "%s", optarg); quirk = kiconv_quirkcs(csp + 1, KICONV_VENDOR_MICSFT); build_iovec_argf(&iov, &iovlen, "cs_local", quirk); cs_local = strdup(quirk); } break; case 'D': cs_dos = strdup(optarg); build_iovec_argf(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1); break; case 'o': { char *p = NULL; char *val = strdup(""); p = strchr(optarg, '='); if (p != NULL) { free(val); *p = '\0'; val = p + 1; } build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); } break; case 'W': if (strcmp(optarg, "iso22dos") == 0) { cs_local = strdup("ISO8859-2"); cs_dos = strdup("CP852"); } else if (strcmp(optarg, "iso72dos") == 0) { cs_local = strdup("ISO8859-7"); cs_dos = strdup("CP737"); } else if (strcmp(optarg, "koi2dos") == 0) { cs_local = strdup("KOI8-R"); cs_dos = strdup("CP866"); } else if (strcmp(optarg, "koi8u2dos") == 0) { cs_local = strdup("KOI8-U"); cs_dos = strdup("CP866"); } else { err(EX_NOINPUT, "%s", optarg); } build_iovec(&iov, &iovlen, "cs_local", cs_local, (size_t)-1); build_iovec(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1); break; case '?': default: usage(); break; } } if (optind + 2 != argc) usage(); if (set_mask && !set_dirmask) { dirmask = mask; set_dirmask = 1; } else if (set_dirmask && !set_mask) { mask = dirmask; set_mask = 1; } dev = argv[optind]; dir = argv[optind + 1]; if (cs_local != NULL) { if (set_charset(&iov, &iovlen, cs_local, cs_dos) == -1) err(EX_OSERR, "msdosfs_iconv"); build_iovec_argf(&iov, &iovlen, "kiconv", ""); } else if (cs_dos != NULL) { build_iovec_argf(&iov, &iovlen, "cs_local", "ISO8859-1"); if (set_charset(&iov, &iovlen, "ISO8859-1", cs_dos) == -1) err(EX_OSERR, "msdosfs_iconv"); build_iovec_argf(&iov, &iovlen, "kiconv", ""); } /* * Resolve the mountpoint with realpath(3) and remove unnecessary * slashes from the devicename if there are any. */ if (checkpath(dir, mntpath) != 0) err(EX_USAGE, "%s", mntpath); (void)rmslashes(dev, dev); if (!set_gid || !set_uid || !set_mask) { if (stat(mntpath, &sb) == -1) err(EX_OSERR, "stat %s", mntpath); if (!set_uid) uid = sb.st_uid; if (!set_gid) gid = sb.st_gid; if (!set_mask) mask = dirmask = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); } build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); build_iovec(&iov, &iovlen, "from", dev, (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); build_iovec_argf(&iov, &iovlen, "uid", "%d", uid); build_iovec_argf(&iov, &iovlen, "gid", "%u", gid); build_iovec_argf(&iov, &iovlen, "mask", "%u", mask); build_iovec_argf(&iov, &iovlen, "dirmask", "%u", dirmask); if (nmount(iov, iovlen, 0) < 0) { if (errmsg[0]) err(1, "%s: %s", dev, errmsg); else err(1, "%s", dev); } exit (0); } gid_t a_gid(char *s) { struct group *gr; char *gname; gid_t gid; if ((gr = getgrnam(s)) != NULL) gid = gr->gr_gid; else { for (gname = s; *s && isdigit(*s); ++s); if (!*s) gid = atoi(gname); else errx(EX_NOUSER, "unknown group id: %s", gname); } return (gid); } uid_t a_uid(char *s) { struct passwd *pw; char *uname; uid_t uid; if ((pw = getpwnam(s)) != NULL) uid = pw->pw_uid; else { for (uname = s; *s && isdigit(*s); ++s); if (!*s) uid = atoi(uname); else errx(EX_NOUSER, "unknown user id: %s", uname); } return (uid); } mode_t a_mask(char *s) { int done, rv; char *ep; done = 0; rv = -1; if (*s >= '0' && *s <= '7') { done = 1; rv = strtol(optarg, &ep, 8); } if (!done || rv < 0 || *ep) errx(EX_USAGE, "invalid file mode: %s", s); return (rv); } void usage(void) { fprintf(stderr, "%s\n%s\n%s\n", "usage: mount_msdosfs [-9ls] [-D DOS_codepage] [-g gid] [-L locale]", " [-M mask] [-m mask] [-o options] [-u uid]", " [-W table] special node"); exit(EX_USAGE); } int set_charset(struct iovec **iov, int *iovlen, const char *cs_local, const char *cs_dos) { int error; if (modfind("msdosfs_iconv") < 0) if (kldload("msdosfs_iconv") < 0 || modfind("msdosfs_iconv") < 0) { warnx("cannot find or load \"msdosfs_iconv\" kernel module"); return (-1); } build_iovec_argf(iov, iovlen, "cs_win", ENCODING_UNICODE); error = kiconv_add_xlat16_cspairs(ENCODING_UNICODE, cs_local); if (error && errno != EEXIST) return (-1); if (cs_dos != NULL) { error = kiconv_add_xlat16_cspairs(cs_dos, cs_local); if (error && errno != EEXIST) return (-1); } else { build_iovec_argf(iov, iovlen, "cs_dos", cs_local); error = kiconv_add_xlat16_cspair(cs_local, cs_local, KICONV_FROM_UPPER | KICONV_LOWER); if (error && errno != EEXIST) return (-1); } return (0); } diff --git a/sbin/mount_nullfs/mount_nullfs.c b/sbin/mount_nullfs/mount_nullfs.c index 55d7ac982f70..c6bf09a1ca1c 100644 --- a/sbin/mount_nullfs/mount_nullfs.c +++ b/sbin/mount_nullfs/mount_nullfs.c @@ -1,144 +1,142 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * 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 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)mount_null.c 8.6 (Berkeley) 4/26/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include "mntopts.h" static void usage(void) __dead2; static int stat_realpath(const char *path, char *resolved, struct stat *sbp) { if (realpath(path, resolved) == NULL || stat(resolved, sbp) != 0) return (1); return (0); } int main(int argc, char *argv[]) { struct iovec *iov; char *p, *val; char mountpoint[MAXPATHLEN]; char target[MAXPATHLEN]; char errmsg[255]; int ch, iovlen; char nullfs[] = "nullfs"; struct stat target_stat; struct stat mountpoint_stat; iov = NULL; iovlen = 0; errmsg[0] = '\0'; while ((ch = getopt(argc, argv, "o:")) != -1) switch(ch) { case 'o': val = strdup(""); p = strchr(optarg, '='); if (p != NULL) { free(val); *p = '\0'; val = p + 1; } build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); break; case '?': default: usage(); } argc -= optind; argv += optind; if (argc != 2) usage(); /* resolve target and mountpoint with realpath(3) */ if (stat_realpath(argv[0], target, &target_stat) != 0) err(EX_USAGE, "%s", target); if (stat_realpath(argv[1], mountpoint, &mountpoint_stat) != 0) err(EX_USAGE, "%s", mountpoint); if (!S_ISDIR(target_stat.st_mode) && !S_ISREG(target_stat.st_mode)) errx(EX_USAGE, "%s: must be either a file or directory", target); if ((target_stat.st_mode & S_IFMT) != (mountpoint_stat.st_mode & S_IFMT)) errx(EX_USAGE, "%s: must be same type as %s (file or directory)", mountpoint, target); build_iovec(&iov, &iovlen, "fstype", nullfs, (size_t)-1); build_iovec(&iov, &iovlen, "fspath", mountpoint, (size_t)-1); build_iovec(&iov, &iovlen, "target", target, (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); if (nmount(iov, iovlen, 0) < 0) { if (errmsg[0] != 0) err(1, "%s: %s", mountpoint, errmsg); else err(1, "%s", mountpoint); } exit(0); } static void usage(void) { (void)fprintf(stderr, "usage: mount_nullfs [-o options] target mount-point\n"); exit(1); } diff --git a/sbin/mount_unionfs/mount_unionfs.c b/sbin/mount_unionfs/mount_unionfs.c index 9aafaf13d81f..01c32f267923 100644 --- a/sbin/mount_unionfs/mount_unionfs.c +++ b/sbin/mount_unionfs/mount_unionfs.c @@ -1,197 +1,194 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. * Copyright (c) 2005, 2006 Masanori Ozawa , ONGS Inc. * Copyright (c) 2006 Daichi Goto * All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * 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 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)mount_union.c 8.5 (Berkeley) 3/27/94"; -#else -static const char rcsid[] = - "$FreeBSD$"; #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include "mntopts.h" static int subdir(const char *p, const char *dir) { int l; l = strlen(dir); if (l <= 1) return (1); if ((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0')) return (1); return (0); } static void usage(void) { (void)fprintf(stderr, "usage: mount_unionfs [-o options] directory uniondir\n"); exit(EX_USAGE); } static void parse_gid(const char *s, char *buf, size_t bufsize) { struct group *gr; char *inval; if ((gr = getgrnam(s)) != NULL) snprintf(buf, bufsize, "%d", gr->gr_gid); else { strtol(s, &inval, 10); if (*inval != 0) { errx(EX_NOUSER, "unknown group id: %s", s); usage(); } else { strncpy(buf, s, bufsize); } } } static void parse_uid(const char *s, char *buf, size_t bufsize) { struct passwd *pw; char *inval; if ((pw = getpwnam(s)) != NULL) snprintf(buf, bufsize, "%d", pw->pw_uid); else { strtol(s, &inval, 10); if (*inval != 0) { errx(EX_NOUSER, "unknown user id: %s", s); usage(); } else { strncpy(buf, s, bufsize); } } } int main(int argc, char *argv[]) { struct iovec *iov; int ch, iovlen; char source [MAXPATHLEN], target[MAXPATHLEN], errmsg[255]; char uid_str[20], gid_str[20]; char fstype[] = "unionfs"; char *p, *val; iov = NULL; iovlen = 0; memset(errmsg, 0, sizeof(errmsg)); while ((ch = getopt(argc, argv, "bo:")) != -1) { switch (ch) { case 'b': printf("\n -b is deprecated. Use \"-o below\" instead\n"); build_iovec(&iov, &iovlen, "below", NULL, 0); break; case 'o': p = strchr(optarg, '='); val = NULL; if (p != NULL) { *p = '\0'; val = p + 1; if (strcmp(optarg, "gid") == 0) { parse_gid(val, gid_str, sizeof(gid_str)); val = gid_str; } else if (strcmp(optarg, "uid") == 0) { parse_uid(val, uid_str, sizeof(uid_str)); val = uid_str; } } build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); break; case '?': default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 2) usage(); /* resolve both target and source with realpath(3) */ if (checkpath(argv[0], target) != 0) err(EX_USAGE, "%s", target); if (checkpath(argv[1], source) != 0) err(EX_USAGE, "%s", source); if (subdir(target, source) || subdir(source, target)) errx(EX_USAGE, "%s (%s) and %s (%s) are not distinct paths", argv[0], target, argv[1], source); build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); build_iovec(&iov, &iovlen, "fspath", source, (size_t)-1); build_iovec(&iov, &iovlen, "from", target, (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); if (nmount(iov, iovlen, 0)) err(EX_OSERR, "%s: %s", source, errmsg); exit(0); } diff --git a/sbin/newfs_msdos/mkfs_msdos.c b/sbin/newfs_msdos/mkfs_msdos.c index 13a804c82625..065e3c5f4192 100644 --- a/sbin/newfs_msdos/mkfs_msdos.c +++ b/sbin/newfs_msdos/mkfs_msdos.c @@ -1,1103 +1,1098 @@ /* * Copyright (c) 1998 Robert Nordier * 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(S) ``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(S) 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #ifdef MAKEFS /* In the makefs case we only want struct disklabel */ #include #else #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mkfs_msdos.h" #define MAXU16 0xffff /* maximum unsigned 16-bit quantity */ #define BPN 4 /* bits per nibble */ #define NPB 2 /* nibbles per byte */ #define DOSMAGIC 0xaa55 /* DOS magic number */ #define MINBPS 512 /* minimum bytes per sector */ #define MAXBPS 4096 /* maximum bytes per sector */ #define MAXSPC 128 /* maximum sectors per cluster */ #define MAXNFT 16 /* maximum number of FATs */ #define DEFBLK 4096 /* default block size */ #define DEFBLK16 2048 /* default block size FAT16 */ #define DEFRDE 512 /* default root directory entries */ #define RESFTE 2 /* reserved FAT entries */ #define MINCLS12 1U /* minimum FAT12 clusters */ #define MINCLS16 0xff5U /* minimum FAT16 clusters */ #define MINCLS32 0xfff5U /* minimum FAT32 clusters */ #define MAXCLS12 0xff4U /* maximum FAT12 clusters */ #define MAXCLS16 0xfff4U /* maximum FAT16 clusters */ #define MAXCLS32 0xffffff4U /* maximum FAT32 clusters */ #define mincls(fat) ((fat) == 12 ? MINCLS12 : \ (fat) == 16 ? MINCLS16 : \ MINCLS32) #define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \ (fat) == 16 ? MAXCLS16 : \ MAXCLS32) #define mk1(p, x) \ (p) = (u_int8_t)(x) #define mk2(p, x) \ (p)[0] = (u_int8_t)(x), \ (p)[1] = (u_int8_t)((x) >> 010) #define mk4(p, x) \ (p)[0] = (u_int8_t)(x), \ (p)[1] = (u_int8_t)((x) >> 010), \ (p)[2] = (u_int8_t)((x) >> 020), \ (p)[3] = (u_int8_t)((x) >> 030) struct bs { u_int8_t bsJump[3]; /* bootstrap entry point */ u_int8_t bsOemName[8]; /* OEM name and version */ } __packed; struct bsbpb { u_int8_t bpbBytesPerSec[2]; /* bytes per sector */ u_int8_t bpbSecPerClust; /* sectors per cluster */ u_int8_t bpbResSectors[2]; /* reserved sectors */ u_int8_t bpbFATs; /* number of FATs */ u_int8_t bpbRootDirEnts[2]; /* root directory entries */ u_int8_t bpbSectors[2]; /* total sectors */ u_int8_t bpbMedia; /* media descriptor */ u_int8_t bpbFATsecs[2]; /* sectors per FAT */ u_int8_t bpbSecPerTrack[2]; /* sectors per track */ u_int8_t bpbHeads[2]; /* drive heads */ u_int8_t bpbHiddenSecs[4]; /* hidden sectors */ u_int8_t bpbHugeSectors[4]; /* big total sectors */ } __packed; struct bsxbpb { u_int8_t bpbBigFATsecs[4]; /* big sectors per FAT */ u_int8_t bpbExtFlags[2]; /* FAT control flags */ u_int8_t bpbFSVers[2]; /* file system version */ u_int8_t bpbRootClust[4]; /* root directory start cluster */ u_int8_t bpbFSInfo[2]; /* file system info sector */ u_int8_t bpbBackup[2]; /* backup boot sector */ u_int8_t bpbReserved[12]; /* reserved */ } __packed; struct bsx { u_int8_t exDriveNumber; /* drive number */ u_int8_t exReserved1; /* reserved */ u_int8_t exBootSignature; /* extended boot signature */ u_int8_t exVolumeID[4]; /* volume ID number */ u_int8_t exVolumeLabel[11]; /* volume label */ u_int8_t exFileSysType[8]; /* file system type */ } __packed; struct de { u_int8_t deName[11]; /* name and extension */ u_int8_t deAttributes; /* attributes */ u_int8_t rsvd[10]; /* reserved */ u_int8_t deMTime[2]; /* last-modified time */ u_int8_t deMDate[2]; /* last-modified date */ u_int8_t deStartCluster[2]; /* starting cluster */ u_int8_t deFileSize[4]; /* size */ } __packed; struct bpb { u_int bpbBytesPerSec; /* bytes per sector */ u_int bpbSecPerClust; /* sectors per cluster */ u_int bpbResSectors; /* reserved sectors */ u_int bpbFATs; /* number of FATs */ u_int bpbRootDirEnts; /* root directory entries */ u_int bpbSectors; /* total sectors */ u_int bpbMedia; /* media descriptor */ u_int bpbFATsecs; /* sectors per FAT */ u_int bpbSecPerTrack; /* sectors per track */ u_int bpbHeads; /* drive heads */ u_int bpbHiddenSecs; /* hidden sectors */ u_int bpbHugeSectors; /* big total sectors */ u_int bpbBigFATsecs; /* big sectors per FAT */ u_int bpbRootClust; /* root directory start cluster */ u_int bpbFSInfo; /* file system info sector */ u_int bpbBackup; /* backup boot sector */ }; #define BPBGAP 0, 0, 0, 0, 0, 0 static struct { const char *name; struct bpb bpb; } const stdfmt[] = { {"160", {512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1, BPBGAP}}, {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}}, {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}}, {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}}, {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}}, {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}}, {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}}, {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}}, {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}}, {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}} }; static const u_int8_t bootcode[] = { 0xfa, /* cli */ 0x31, 0xc0, /* xor ax,ax */ 0x8e, 0xd0, /* mov ss,ax */ 0xbc, 0x00, 0x7c, /* mov sp,7c00h */ 0xfb, /* sti */ 0x8e, 0xd8, /* mov ds,ax */ 0xe8, 0x00, 0x00, /* call $ + 3 */ 0x5e, /* pop si */ 0x83, 0xc6, 0x19, /* add si,+19h */ 0xbb, 0x07, 0x00, /* mov bx,0007h */ 0xfc, /* cld */ 0xac, /* lodsb */ 0x84, 0xc0, /* test al,al */ 0x74, 0x06, /* jz $ + 8 */ 0xb4, 0x0e, /* mov ah,0eh */ 0xcd, 0x10, /* int 10h */ 0xeb, 0xf5, /* jmp $ - 9 */ 0x30, 0xe4, /* xor ah,ah */ 0xcd, 0x16, /* int 16h */ 0xcd, 0x19, /* int 19h */ 0x0d, 0x0a, 'N', 'o', 'n', '-', 's', 'y', 's', 't', 'e', 'm', ' ', 'd', 'i', 's', 'k', 0x0d, 0x0a, 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o', ' ', 'r', 'e', 'b', 'o', 'o', 't', 0x0d, 0x0a, 0 }; static volatile sig_atomic_t got_siginfo; static void infohandler(int); #ifndef MAKEFS static int check_mounted(const char *, mode_t); #endif static ssize_t getchunksize(void); static int getstdfmt(const char *, struct bpb *); static int getdiskinfo(int, const char *, const char *, int, struct bpb *); static void print_bpb(struct bpb *); static int ckgeom(const char *, u_int, const char *); static void mklabel(u_int8_t *, const char *); static int oklabel(const char *); static void setstr(u_int8_t *, const char *, size_t); int mkfs_msdos(const char *fname, const char *dtype, const struct msdos_options *op) { char buf[MAXPATHLEN]; struct sigaction si_sa; struct stat sb; struct timeval tv; struct bpb bpb; struct tm *tm; struct bs *bs; struct bsbpb *bsbpb; struct bsxbpb *bsxbpb; struct bsx *bsx; struct de *de; u_int8_t *img; u_int8_t *physbuf, *physbuf_end; const char *bname; ssize_t n; time_t now; u_int fat, bss, rds, cls, dir, lsn, x, x1, x2; u_int extra_res, alignment, saved_x, attempts=0; bool set_res, set_spf, set_spc; int fd, fd1, rv; struct msdos_options o = *op; ssize_t chunksize; physbuf = NULL; rv = -1; fd = fd1 = -1; if (o.block_size && o.sectors_per_cluster) { warnx("Cannot specify both block size and sectors per cluster"); goto done; } if (o.OEM_string && strlen(o.OEM_string) > 8) { warnx("%s: bad OEM string", o.OEM_string); goto done; } if (o.create_size) { if (o.no_create) { warnx("create (-C) is incompatible with -N"); goto done; } fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644); if (fd == -1) { warnx("failed to create %s", fname); goto done; } if (ftruncate(fd, o.create_size)) { warnx("failed to initialize %jd bytes", (intmax_t)o.create_size); goto done; } } else if ((fd = open(fname, o.no_create ? O_RDONLY : O_RDWR)) == -1) { warn("%s", fname); goto done; } if (fstat(fd, &sb)) { warn("%s", fname); goto done; } if (o.create_size) { if (!S_ISREG(sb.st_mode)) warnx("warning, %s is not a regular file", fname); } else { #ifdef MAKEFS errx(1, "o.create_size must be set!"); #else if (!S_ISCHR(sb.st_mode)) warnx("warning, %s is not a character device", fname); #endif } #ifndef MAKEFS if (!o.no_create) if (check_mounted(fname, sb.st_mode) == -1) goto done; #endif if (o.offset && o.offset != lseek(fd, o.offset, SEEK_SET)) { warnx("cannot seek to %jd", (intmax_t)o.offset); goto done; } memset(&bpb, 0, sizeof(bpb)); if (o.floppy) { if (getstdfmt(o.floppy, &bpb) == -1) goto done; bpb.bpbHugeSectors = bpb.bpbSectors; bpb.bpbSectors = 0; bpb.bpbBigFATsecs = bpb.bpbFATsecs; bpb.bpbFATsecs = 0; } if (o.drive_heads) bpb.bpbHeads = o.drive_heads; if (o.sectors_per_track) bpb.bpbSecPerTrack = o.sectors_per_track; if (o.bytes_per_sector) bpb.bpbBytesPerSec = o.bytes_per_sector; if (o.size) bpb.bpbHugeSectors = o.size; if (o.hidden_sectors_set) bpb.bpbHiddenSecs = o.hidden_sectors; if (!(o.floppy || (o.drive_heads && o.sectors_per_track && o.bytes_per_sector && o.size && o.hidden_sectors_set))) { if (getdiskinfo(fd, fname, dtype, o.hidden_sectors_set, &bpb) == -1) goto done; bpb.bpbHugeSectors -= (o.offset / bpb.bpbBytesPerSec); if (bpb.bpbSecPerClust == 0) { /* set defaults */ if (bpb.bpbHugeSectors <= 6000) /* about 3MB -> 512 bytes */ bpb.bpbSecPerClust = 1; else if (bpb.bpbHugeSectors <= (1<<17)) /* 64M -> 4k */ bpb.bpbSecPerClust = 8; else if (bpb.bpbHugeSectors <= (1<<19)) /* 256M -> 8k */ bpb.bpbSecPerClust = 16; else if (bpb.bpbHugeSectors <= (1<<21)) /* 1G -> 16k */ bpb.bpbSecPerClust = 32; else bpb.bpbSecPerClust = 64; /* otherwise 32k */ } } if (bpb.bpbBytesPerSec < MINBPS || bpb.bpbBytesPerSec > MAXBPS || !powerof2(bpb.bpbBytesPerSec)) { warnx("Invalid bytes/sector (%u): must be 512, 1024, 2048 or 4096", bpb.bpbBytesPerSec); goto done; } if (o.volume_label && !oklabel(o.volume_label)) { warnx("%s: bad volume label", o.volume_label); goto done; } if (!(fat = o.fat_type)) { if (o.floppy) fat = 12; else if (!o.directory_entries && (o.info_sector || o.backup_sector)) fat = 32; } if ((fat == 32 && o.directory_entries) || (fat != 32 && (o.info_sector || o.backup_sector))) { warnx("-%c is not a legal FAT%s option", fat == 32 ? 'e' : o.info_sector ? 'i' : 'k', fat == 32 ? "32" : "12/16"); goto done; } if (o.floppy && fat == 32) bpb.bpbRootDirEnts = 0; if (fat != 0 && fat != 12 && fat != 16 && fat != 32) { warnx("%d: bad FAT type", fat); goto done; } if (o.block_size) { if (!powerof2(o.block_size)) { warnx("block size (%u) is not a power of 2", o.block_size); goto done; } if (o.block_size < bpb.bpbBytesPerSec) { warnx("block size (%u) is too small; minimum is %u", o.block_size, bpb.bpbBytesPerSec); goto done; } if (o.block_size > bpb.bpbBytesPerSec * MAXSPC) { warnx("block size (%u) is too large; maximum is %u", o.block_size, bpb.bpbBytesPerSec * MAXSPC); goto done; } bpb.bpbSecPerClust = o.block_size / bpb.bpbBytesPerSec; } if (o.sectors_per_cluster) { if (!powerof2(o.sectors_per_cluster)) { warnx("sectors/cluster (%u) is not a power of 2", o.sectors_per_cluster); goto done; } bpb.bpbSecPerClust = o.sectors_per_cluster; } if (o.reserved_sectors) bpb.bpbResSectors = o.reserved_sectors; if (o.num_FAT) { if (o.num_FAT > MAXNFT) { warnx("number of FATs (%u) is too large; maximum is %u", o.num_FAT, MAXNFT); goto done; } bpb.bpbFATs = o.num_FAT; } if (o.directory_entries) bpb.bpbRootDirEnts = o.directory_entries; if (o.media_descriptor_set) { if (o.media_descriptor < 0xf0) { warnx("illegal media descriptor (%#x)", o.media_descriptor); goto done; } bpb.bpbMedia = o.media_descriptor; } if (o.sectors_per_fat) bpb.bpbBigFATsecs = o.sectors_per_fat; if (o.info_sector) bpb.bpbFSInfo = o.info_sector; if (o.backup_sector) bpb.bpbBackup = o.backup_sector; bss = 1; bname = NULL; fd1 = -1; if (o.bootstrap) { bname = o.bootstrap; if (!strchr(bname, '/')) { snprintf(buf, sizeof(buf), "/boot/%s", bname); bname = buf; } if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) { warn("%s", bname); goto done; } if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bpbBytesPerSec || sb.st_size < bpb.bpbBytesPerSec || sb.st_size > bpb.bpbBytesPerSec * MAXU16) { warnx("%s: inappropriate file type or format", bname); goto done; } bss = sb.st_size / bpb.bpbBytesPerSec; } if (!bpb.bpbFATs) bpb.bpbFATs = 2; if (!fat) { if (bpb.bpbHugeSectors < (bpb.bpbResSectors ? bpb.bpbResSectors : bss) + howmany((RESFTE + (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1)) * (bpb.bpbSecPerClust ? 16 : 12) / BPN, bpb.bpbBytesPerSec * NPB) * bpb.bpbFATs + howmany(bpb.bpbRootDirEnts ? bpb.bpbRootDirEnts : DEFRDE, bpb.bpbBytesPerSec / sizeof(struct de)) + (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1) * (bpb.bpbSecPerClust ? bpb.bpbSecPerClust : howmany(DEFBLK, bpb.bpbBytesPerSec))) fat = 12; else if (bpb.bpbRootDirEnts || bpb.bpbHugeSectors < (bpb.bpbResSectors ? bpb.bpbResSectors : bss) + howmany((RESFTE + MAXCLS16) * 2, bpb.bpbBytesPerSec) * bpb.bpbFATs + howmany(DEFRDE, bpb.bpbBytesPerSec / sizeof(struct de)) + (MAXCLS16 + 1) * (bpb.bpbSecPerClust ? bpb.bpbSecPerClust : howmany(8192, bpb.bpbBytesPerSec))) fat = 16; else fat = 32; } x = bss; if (fat == 32) { if (!bpb.bpbFSInfo) { if (x == MAXU16 || x == bpb.bpbBackup) { warnx("no room for info sector"); goto done; } bpb.bpbFSInfo = x; } if (bpb.bpbFSInfo != MAXU16 && x <= bpb.bpbFSInfo) x = bpb.bpbFSInfo + 1; if (!bpb.bpbBackup) { if (x == MAXU16) { warnx("no room for backup sector"); goto done; } bpb.bpbBackup = x; } else if (bpb.bpbBackup != MAXU16 && bpb.bpbBackup == bpb.bpbFSInfo) { warnx("backup sector would overwrite info sector"); goto done; } if (bpb.bpbBackup != MAXU16 && x <= bpb.bpbBackup) x = bpb.bpbBackup + 1; } extra_res = 0; alignment = 0; set_res = (bpb.bpbResSectors == 0); set_spf = (bpb.bpbBigFATsecs == 0); set_spc = (bpb.bpbSecPerClust == 0); saved_x = x; /* * Attempt to align the root directory to cluster if o.align is set. * This is done by padding with reserved blocks. Note that this can * cause other factors to change, which can in turn change the alignment. * This should take at most 2 iterations, as increasing the reserved * amount may cause the FAT size to decrease by 1, requiring another * bpbFATs reserved blocks. If bpbSecPerClust changes, it will * be half of its previous size, and thus will not throw off alignment. */ do { x = saved_x; if (set_res) bpb.bpbResSectors = ((fat == 32) ? MAX(x, MAX(16384 / bpb.bpbBytesPerSec, 4)) : x) + extra_res; else if (bpb.bpbResSectors < x) { warnx("too few reserved sectors (need %d have %d)", x, bpb.bpbResSectors); goto done; } if (fat != 32 && !bpb.bpbRootDirEnts) bpb.bpbRootDirEnts = DEFRDE; rds = howmany(bpb.bpbRootDirEnts, bpb.bpbBytesPerSec / sizeof(struct de)); if (set_spc) { for (bpb.bpbSecPerClust = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bpbBytesPerSec); bpb.bpbSecPerClust < MAXSPC && (bpb.bpbResSectors + howmany((RESFTE + maxcls(fat)) * (fat / BPN), bpb.bpbBytesPerSec * NPB) * bpb.bpbFATs + rds + (u_int64_t) (maxcls(fat) + 1) * bpb.bpbSecPerClust) <= bpb.bpbHugeSectors; bpb.bpbSecPerClust <<= 1) continue; } if (fat != 32 && bpb.bpbBigFATsecs > MAXU16) { warnx("too many sectors/FAT for FAT12/16"); goto done; } x1 = bpb.bpbResSectors + rds; x = bpb.bpbBigFATsecs ? bpb.bpbBigFATsecs : 1; if (x1 + (u_int64_t)x * bpb.bpbFATs > bpb.bpbHugeSectors) { warnx("meta data exceeds file system size"); goto done; } x1 += x * bpb.bpbFATs; x = (u_int64_t)(bpb.bpbHugeSectors - x1) * bpb.bpbBytesPerSec * NPB / (bpb.bpbSecPerClust * bpb.bpbBytesPerSec * NPB + fat / BPN * bpb.bpbFATs); x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), bpb.bpbBytesPerSec * NPB); if (set_spf) { if (bpb.bpbBigFATsecs == 0) bpb.bpbBigFATsecs = x2; x1 += (bpb.bpbBigFATsecs - 1) * bpb.bpbFATs; } if (set_res) { /* attempt to align root directory */ alignment = (bpb.bpbResSectors + bpb.bpbBigFATsecs * bpb.bpbFATs) % bpb.bpbSecPerClust; if (o.align) extra_res += bpb.bpbSecPerClust - alignment; } attempts++; } while (o.align && alignment != 0 && attempts < 2); if (o.align && alignment != 0) warnx("warning: Alignment failed."); cls = (bpb.bpbHugeSectors - x1) / bpb.bpbSecPerClust; x = (u_int64_t)bpb.bpbBigFATsecs * bpb.bpbBytesPerSec * NPB / (fat / BPN) - RESFTE; if (cls > x) cls = x; if (bpb.bpbBigFATsecs < x2) warnx("warning: sectors/FAT limits file system to %u clusters", cls); if (cls < mincls(fat)) { warnx("%u clusters too few clusters for FAT%u, need %u", cls, fat, mincls(fat)); goto done; } if (cls > maxcls(fat)) { cls = maxcls(fat); bpb.bpbHugeSectors = x1 + (cls + 1) * bpb.bpbSecPerClust - 1; warnx("warning: FAT type limits file system to %u sectors", bpb.bpbHugeSectors); } printf("%s: %u sector%s in %u FAT%u cluster%s " "(%u bytes/cluster)\n", fname, cls * bpb.bpbSecPerClust, cls * bpb.bpbSecPerClust == 1 ? "" : "s", cls, fat, cls == 1 ? "" : "s", bpb.bpbBytesPerSec * bpb.bpbSecPerClust); if (!bpb.bpbMedia) bpb.bpbMedia = !bpb.bpbHiddenSecs ? 0xf0 : 0xf8; if (fat == 32) bpb.bpbRootClust = RESFTE; if (bpb.bpbHugeSectors <= MAXU16) { bpb.bpbSectors = bpb.bpbHugeSectors; bpb.bpbHugeSectors = 0; } if (fat != 32) { bpb.bpbFATsecs = bpb.bpbBigFATsecs; bpb.bpbBigFATsecs = 0; } print_bpb(&bpb); if (!o.no_create) { if (o.timestamp_set) { tv.tv_sec = now = o.timestamp; tv.tv_usec = 0; tm = gmtime(&now); } else { gettimeofday(&tv, NULL); now = tv.tv_sec; tm = localtime(&now); } chunksize = getchunksize(); physbuf = malloc(chunksize); if (physbuf == NULL) { warn(NULL); goto done; } physbuf_end = physbuf + chunksize; img = physbuf; dir = bpb.bpbResSectors + (bpb.bpbFATsecs ? bpb.bpbFATsecs : bpb.bpbBigFATsecs) * bpb.bpbFATs; memset(&si_sa, 0, sizeof(si_sa)); si_sa.sa_handler = infohandler; #ifdef SIGINFO if (sigaction(SIGINFO, &si_sa, NULL) == -1) { warn("sigaction SIGINFO"); goto done; } #endif for (lsn = 0; lsn < dir + (fat == 32 ? bpb.bpbSecPerClust : rds); lsn++) { if (got_siginfo) { fprintf(stderr,"%s: writing sector %u of %u (%u%%)\n", fname, lsn, (dir + (fat == 32 ? bpb.bpbSecPerClust: rds)), (lsn * 100) / (dir + (fat == 32 ? bpb.bpbSecPerClust: rds))); got_siginfo = 0; } x = lsn; if (o.bootstrap && fat == 32 && bpb.bpbBackup != MAXU16 && bss <= bpb.bpbBackup && x >= bpb.bpbBackup) { x -= bpb.bpbBackup; if (!x && lseek(fd1, o.offset, SEEK_SET)) { warn("%s", bname); goto done; } } if (o.bootstrap && x < bss) { if ((n = read(fd1, img, bpb.bpbBytesPerSec)) == -1) { warn("%s", bname); goto done; } if ((unsigned)n != bpb.bpbBytesPerSec) { warnx("%s: can't read sector %u", bname, x); goto done; } } else memset(img, 0, bpb.bpbBytesPerSec); if (!lsn || (fat == 32 && bpb.bpbBackup != MAXU16 && lsn == bpb.bpbBackup)) { x1 = sizeof(struct bs); bsbpb = (struct bsbpb *)(img + x1); mk2(bsbpb->bpbBytesPerSec, bpb.bpbBytesPerSec); mk1(bsbpb->bpbSecPerClust, bpb.bpbSecPerClust); mk2(bsbpb->bpbResSectors, bpb.bpbResSectors); mk1(bsbpb->bpbFATs, bpb.bpbFATs); mk2(bsbpb->bpbRootDirEnts, bpb.bpbRootDirEnts); mk2(bsbpb->bpbSectors, bpb.bpbSectors); mk1(bsbpb->bpbMedia, bpb.bpbMedia); mk2(bsbpb->bpbFATsecs, bpb.bpbFATsecs); mk2(bsbpb->bpbSecPerTrack, bpb.bpbSecPerTrack); mk2(bsbpb->bpbHeads, bpb.bpbHeads); mk4(bsbpb->bpbHiddenSecs, bpb.bpbHiddenSecs); mk4(bsbpb->bpbHugeSectors, bpb.bpbHugeSectors); x1 += sizeof(struct bsbpb); if (fat == 32) { bsxbpb = (struct bsxbpb *)(img + x1); mk4(bsxbpb->bpbBigFATsecs, bpb.bpbBigFATsecs); mk2(bsxbpb->bpbExtFlags, 0); mk2(bsxbpb->bpbFSVers, 0); mk4(bsxbpb->bpbRootClust, bpb.bpbRootClust); mk2(bsxbpb->bpbFSInfo, bpb.bpbFSInfo); mk2(bsxbpb->bpbBackup, bpb.bpbBackup); x1 += sizeof(struct bsxbpb); } bsx = (struct bsx *)(img + x1); mk1(bsx->exBootSignature, 0x29); if (o.volume_id_set) x = o.volume_id; else x = (((u_int)(1 + tm->tm_mon) << 8 | (u_int)tm->tm_mday) + ((u_int)tm->tm_sec << 8 | (u_int)(tv.tv_usec / 10))) << 16 | ((u_int)(1900 + tm->tm_year) + ((u_int)tm->tm_hour << 8 | (u_int)tm->tm_min)); mk4(bsx->exVolumeID, x); mklabel(bsx->exVolumeLabel, o.volume_label ? o.volume_label : "NO NAME"); snprintf(buf, sizeof(buf), "FAT%u", fat); setstr(bsx->exFileSysType, buf, sizeof(bsx->exFileSysType)); if (!o.bootstrap) { x1 += sizeof(struct bsx); bs = (struct bs *)img; mk1(bs->bsJump[0], 0xeb); mk1(bs->bsJump[1], x1 - 2); mk1(bs->bsJump[2], 0x90); setstr(bs->bsOemName, o.OEM_string ? o.OEM_string : "BSD4.4 ", sizeof(bs->bsOemName)); memcpy(img + x1, bootcode, sizeof(bootcode)); mk2(img + MINBPS - 2, DOSMAGIC); } } else if (fat == 32 && bpb.bpbFSInfo != MAXU16 && (lsn == bpb.bpbFSInfo || (bpb.bpbBackup != MAXU16 && lsn == bpb.bpbBackup + bpb.bpbFSInfo))) { mk4(img, 0x41615252); mk4(img + MINBPS - 28, 0x61417272); mk4(img + MINBPS - 24, 0xffffffff); mk4(img + MINBPS - 20, 0xffffffff); mk2(img + MINBPS - 2, DOSMAGIC); } else if (lsn >= bpb.bpbResSectors && lsn < dir && !((lsn - bpb.bpbResSectors) % (bpb.bpbFATsecs ? bpb.bpbFATsecs : bpb.bpbBigFATsecs))) { mk1(img[0], bpb.bpbMedia); for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++) mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff); } else if (lsn == dir && o.volume_label) { de = (struct de *)img; mklabel(de->deName, o.volume_label); mk1(de->deAttributes, 050); x = (u_int)tm->tm_hour << 11 | (u_int)tm->tm_min << 5 | (u_int)tm->tm_sec >> 1; mk2(de->deMTime, x); x = (u_int)(tm->tm_year - 80) << 9 | (u_int)(tm->tm_mon + 1) << 5 | (u_int)tm->tm_mday; mk2(de->deMDate, x); } /* * Issue a write of chunksize once we have collected * enough sectors. */ img += bpb.bpbBytesPerSec; if (img >= physbuf_end) { n = write(fd, physbuf, chunksize); if (n != chunksize) { warnx("%s: can't write sector %u", fname, lsn); goto done; } img = physbuf; } } /* * Write remaining sectors, if the last write didn't end * up filling a whole chunk. */ if (img != physbuf) { ssize_t tailsize = img - physbuf; n = write(fd, physbuf, tailsize); if (n != tailsize) { warnx("%s: can't write sector %u", fname, lsn); goto done; } } } rv = 0; done: free(physbuf); if (fd != -1) close(fd); if (fd1 != -1) close(fd1); return rv; } /* * return -1 with error if file system is mounted. */ #ifndef MAKEFS static int check_mounted(const char *fname, mode_t mode) { /* * If getmntinfo() is not available (e.g. Linux) don't check. This should * not be a problem since we will only be using makefs to create images. */ struct statfs *mp; const char *s1, *s2; size_t len; int n, r; if (!(n = getmntinfo(&mp, MNT_NOWAIT))) { warn("getmntinfo"); return -1; } len = strlen(_PATH_DEV); s1 = fname; if (!strncmp(s1, _PATH_DEV, len)) s1 += len; r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; for (; n--; mp++) { s2 = mp->f_mntfromname; if (!strncmp(s2, _PATH_DEV, len)) s2 += len; if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || !strcmp(s1, s2)) { warnx("%s is mounted on %s", fname, mp->f_mntonname); return -1; } } return 0; } #endif /* * Get optimal I/O size */ static ssize_t getchunksize(void) { static ssize_t chunksize; if (chunksize != 0) return (chunksize); #ifdef KERN_MAXPHYS int mib[2]; size_t len; mib[0] = CTL_KERN; mib[1] = KERN_MAXPHYS; len = sizeof(chunksize); if (sysctl(mib, 2, &chunksize, &len, NULL, 0) == -1) { warn("sysctl: KERN_MAXPHYS, using %zu", (size_t)MAXPHYS); chunksize = 0; } #endif if (chunksize == 0) chunksize = MAXPHYS; /* * For better performance, we want to write larger chunks instead of * individual sectors (the size can only be 512, 1024, 2048 or 4096 * bytes). Assert that chunksize can always hold an integer number of * sectors by asserting that both are power of two numbers and the * chunksize is greater than MAXBPS. */ static_assert(powerof2(MAXBPS), "MAXBPS is not power of 2"); assert(powerof2(chunksize)); assert(chunksize > MAXBPS); return (chunksize); } /* * Get a standard format. */ static int getstdfmt(const char *fmt, struct bpb *bpb) { u_int x, i; x = nitems(stdfmt); for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++); if (i == x) { warnx("%s: unknown standard format", fmt); return -1; } *bpb = stdfmt[i].bpb; return 0; } static void compute_geometry_from_file(int fd, const char *fname, struct disklabel *lp) { struct stat st; off_t ms; if (fstat(fd, &st)) err(1, "cannot get disk size"); if (!S_ISREG(st.st_mode)) errx(1, "%s is not a regular file", fname); ms = st.st_size; lp->d_secsize = 512; lp->d_nsectors = 63; lp->d_ntracks = 255; lp->d_secperunit = ms / lp->d_secsize; } /* * Get disk slice, partition, and geometry information. */ static int getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag, struct bpb *bpb) { struct disklabel *lp, dlp; off_t hs = 0; #ifndef MAKEFS off_t ms; struct fd_type type; lp = NULL; /* If the user specified a disk type, try to use that */ if (dtype != NULL) { lp = getdiskbyname(dtype); } /* Maybe it's a floppy drive */ if (lp == NULL) { if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) { /* create a fake geometry for a file image */ compute_geometry_from_file(fd, fname, &dlp); lp = &dlp; } else if (ioctl(fd, FD_GTYPE, &type) != -1) { dlp.d_secsize = 128 << type.secsize; dlp.d_nsectors = type.sectrac; dlp.d_ntracks = type.heads; dlp.d_secperunit = ms / dlp.d_secsize; lp = &dlp; } } /* Maybe it's a fixed drive */ if (lp == NULL) { if (bpb->bpbBytesPerSec) dlp.d_secsize = bpb->bpbBytesPerSec; if (bpb->bpbBytesPerSec == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1) err(1, "cannot get sector size"); dlp.d_secperunit = ms / dlp.d_secsize; if (bpb->bpbSecPerTrack == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) { warn("cannot get number of sectors per track"); dlp.d_nsectors = 63; } if (bpb->bpbHeads == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) { warn("cannot get number of heads"); if (dlp.d_secperunit <= 63*1*1024) dlp.d_ntracks = 1; else if (dlp.d_secperunit <= 63*16*1024) dlp.d_ntracks = 16; else dlp.d_ntracks = 255; } hs = (ms / dlp.d_secsize) - dlp.d_secperunit; lp = &dlp; } #else (void)dtype; /* In the makefs case we only support image files: */ compute_geometry_from_file(fd, fname, &dlp); lp = &dlp; #endif if (bpb->bpbBytesPerSec == 0) { if (ckgeom(fname, lp->d_secsize, "bytes/sector") == -1) return -1; bpb->bpbBytesPerSec = lp->d_secsize; } if (bpb->bpbSecPerTrack == 0) { if (ckgeom(fname, lp->d_nsectors, "sectors/track") == -1) return -1; bpb->bpbSecPerTrack = lp->d_nsectors; } if (bpb->bpbHeads == 0) { if (ckgeom(fname, lp->d_ntracks, "drive heads") == -1) return -1; bpb->bpbHeads = lp->d_ntracks; } if (bpb->bpbHugeSectors == 0) bpb->bpbHugeSectors = lp->d_secperunit; if (bpb->bpbHiddenSecs == 0) bpb->bpbHiddenSecs = hs; return 0; } /* * Print out BPB values. */ static void print_bpb(struct bpb *bpb) { printf("BytesPerSec=%u SecPerClust=%u ResSectors=%u FATs=%u", bpb->bpbBytesPerSec, bpb->bpbSecPerClust, bpb->bpbResSectors, bpb->bpbFATs); if (bpb->bpbRootDirEnts) printf(" RootDirEnts=%u", bpb->bpbRootDirEnts); if (bpb->bpbSectors) printf(" Sectors=%u", bpb->bpbSectors); printf(" Media=%#x", bpb->bpbMedia); if (bpb->bpbFATsecs) printf(" FATsecs=%u", bpb->bpbFATsecs); printf(" SecPerTrack=%u Heads=%u HiddenSecs=%u", bpb->bpbSecPerTrack, bpb->bpbHeads, bpb->bpbHiddenSecs); if (bpb->bpbHugeSectors) printf(" HugeSectors=%u", bpb->bpbHugeSectors); if (!bpb->bpbFATsecs) { printf(" FATsecs=%u RootCluster=%u", bpb->bpbBigFATsecs, bpb->bpbRootClust); printf(" FSInfo="); printf(bpb->bpbFSInfo == MAXU16 ? "%#x" : "%u", bpb->bpbFSInfo); printf(" Backup="); printf(bpb->bpbBackup == MAXU16 ? "%#x" : "%u", bpb->bpbBackup); } printf("\n"); } /* * Check a disk geometry value. */ static int ckgeom(const char *fname, u_int val, const char *msg) { if (!val) { warnx("%s: no default %s", fname, msg); return -1; } if (val > MAXU16) { warnx("%s: illegal %s %d", fname, msg, val); return -1; } return 0; } /* * Check a volume label. */ static int oklabel(const char *src) { int c, i; for (i = 0; i <= 11; i++) { c = (u_char)*src++; if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c)) break; } return i && !c; } /* * Make a volume label. */ static void mklabel(u_int8_t *dest, const char *src) { int c, i; for (i = 0; i < 11; i++) { c = *src ? toupper(*src++) : ' '; *dest++ = !i && c == '\xe5' ? 5 : c; } } /* * Copy string, padding with spaces. */ static void setstr(u_int8_t *dest, const char *src, size_t len) { while (len--) *dest++ = *src ? *src++ : ' '; } static void infohandler(int sig __unused) { got_siginfo = 1; } diff --git a/sbin/newfs_msdos/newfs_msdos.c b/sbin/newfs_msdos/newfs_msdos.c index 1ba399fe447e..312a862d9113 100644 --- a/sbin/newfs_msdos/newfs_msdos.c +++ b/sbin/newfs_msdos/newfs_msdos.c @@ -1,281 +1,276 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1998 Robert Nordier * 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(S) ``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(S) 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include "mkfs_msdos.h" #define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg) #define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg) #define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg) #define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg) static u_int argtou(const char *, u_int, u_int, const char *); static off_t argtooff(const char *, const char *); static void usage(void) __dead2; static time_t get_tstamp(const char *b) { struct stat st; char *eb; long long l; if (stat(b, &st) != -1) return (time_t)st.st_mtime; errno = 0; l = strtoll(b, &eb, 0); if (b == eb || *eb || errno) errx(EXIT_FAILURE, "Can't parse timestamp '%s'", b); return (time_t)l; } /* * Construct a FAT12, FAT16, or FAT32 file system. */ int main(int argc, char *argv[]) { static const char opts[] = "@:NAB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:T:u:"; struct msdos_options o; const char *fname, *dtype; char buf[MAXPATHLEN]; int ch; memset(&o, 0, sizeof(o)); while ((ch = getopt(argc, argv, opts)) != -1) switch (ch) { case '@': o.offset = argtooff(optarg, "offset"); break; case 'N': o.no_create = 1; break; case 'A': o.align = true; break; case 'B': o.bootstrap = optarg; break; case 'C': o.create_size = argtooff(optarg, "create size"); break; case 'F': if (strcmp(optarg, "12") && strcmp(optarg, "16") && strcmp(optarg, "32")) errx(1, "%s: bad FAT type", optarg); o.fat_type = atoi(optarg); break; case 'I': o.volume_id = argto4(optarg, 0, "volume ID"); o.volume_id_set = 1; break; case 'L': o.volume_label = optarg; break; case 'O': o.OEM_string = optarg; break; case 'S': o.bytes_per_sector = argto2(optarg, 1, "bytes/sector"); break; case 'a': o.sectors_per_fat = argto4(optarg, 1, "sectors/FAT"); break; case 'b': o.block_size = argtox(optarg, 1, "block size"); o.sectors_per_cluster = 0; break; case 'c': o.sectors_per_cluster = argto1(optarg, 1, "sectors/cluster"); o.block_size = 0; break; case 'e': o.directory_entries = argto2(optarg, 1, "directory entries"); break; case 'f': o.floppy = optarg; break; case 'h': o.drive_heads = argto2(optarg, 1, "drive heads"); break; case 'i': o.info_sector = argto2(optarg, 1, "info sector"); break; case 'k': o.backup_sector = argto2(optarg, 1, "backup sector"); break; case 'm': o.media_descriptor = argto1(optarg, 0, "media descriptor"); o.media_descriptor_set = 1; break; case 'n': o.num_FAT = argto1(optarg, 1, "number of FATs"); break; case 'o': o.hidden_sectors = argto4(optarg, 0, "hidden sectors"); o.hidden_sectors_set = 1; break; case 'r': o.reserved_sectors = argto2(optarg, 1, "reserved sectors"); break; case 's': o.size = argto4(optarg, 1, "file system size"); break; case 'T': o.timestamp_set = 1; o.timestamp = get_tstamp(optarg); break; case 'u': o.sectors_per_track = argto2(optarg, 1, "sectors/track"); break; default: usage(); } argc -= optind; argv += optind; if (argc < 1 || argc > 2) usage(); if (o.align) { if (o.reserved_sectors) errx(1, "align (-A) is incompatible with -r"); } fname = *argv++; if (!o.create_size && !strchr(fname, '/')) { snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname); fname = buf; } dtype = *argv; exit(!!mkfs_msdos(fname, dtype, &o)); } /* * Convert and check a numeric option argument. */ static u_int argtou(const char *arg, u_int lo, u_int hi, const char *msg) { char *s; u_long x; errno = 0; x = strtoul(arg, &s, 0); if (errno || !*arg || *s || x < lo || x > hi) errx(1, "%s: bad %s", arg, msg); return x; } /* * Same for off_t, with optional skmgpP suffix */ static off_t argtooff(const char *arg, const char *msg) { char *s; off_t x; errno = 0; x = strtoll(arg, &s, 0); /* allow at most one extra char */ if (errno || x < 0 || (s[0] && s[1]) ) errx(1, "%s: bad %s", arg, msg); if (*s) { /* the extra char is the multiplier */ switch (*s) { default: errx(1, "%s: bad %s", arg, msg); /* notreached */ case 's': /* sector */ case 'S': x <<= 9; /* times 512 */ break; case 'k': /* kilobyte */ case 'K': x <<= 10; /* times 1024 */ break; case 'm': /* megabyte */ case 'M': x <<= 20; /* times 1024*1024 */ break; case 'g': /* gigabyte */ case 'G': x <<= 30; /* times 1024*1024*1024 */ break; case 'p': /* partition start */ case 'P': case 'l': /* partition length */ case 'L': errx(1, "%s: not supported yet %s", arg, msg); /* notreached */ } } return x; } /* * Print usage message. */ static void usage(void) { fprintf(stderr, "usage: %s [ -options ] special [disktype]\n", getprogname()); fprintf(stderr, "where the options are:\n"); static struct { char o; const char *h; } opts[] = { #define AOPT(_opt, _type, _name, _min, _desc) { _opt, _desc }, ALLOPTS #undef AOPT }; for (size_t i = 0; i < nitems(opts); i++) fprintf(stderr, "\t-%c %s\n", opts[i].o, opts[i].h); exit(1); } diff --git a/sbin/nos-tun/nos-tun.c b/sbin/nos-tun/nos-tun.c index 509f928a2bb8..f3af62db265b 100644 --- a/sbin/nos-tun/nos-tun.c +++ b/sbin/nos-tun/nos-tun.c @@ -1,400 +1,395 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1996, Nickolay Dudorov * 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 unmodified, 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. * */ /* * 'nos-tun' program configure tunN interface as a point-to-point * connection with two "pseudo"-addresses between this host and * 'target'. * * It uses Ip-over-Ip incapsulation ( protocol number 94 - IPIP) * (known as NOS-incapsulation in CISCO-routers' terminology). * * 'nos-tun' can works with itself and CISCO-routers. * (It may also work with Linux 'nos-tun's, but * I have no Linux system here to test with). * * BUGS (or features ?): * - you must specify ONE of the target host's addresses * ( nos-tun sends and accepts packets only to/from this * address ) * - there can be only ONE tunnel between two hosts, * more precisely - between given host and (one of) * target hosts' address(es) * (and why do you want more ?) */ /* * Mar. 23 1999 by Isao SEKI * I added a new flag for ip protocol number. * We are using 4 as protocol number in ampr.org. * */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Tunnel interface configuration stuff */ static struct ifaliasreq ifra; static struct ifreq ifrq; /* Global descriptors */ int net; /* socket descriptor */ int tun; /* tunnel descriptor */ static void usage(void) __dead2; static int Set_address(char *addr, struct sockaddr_in *sin) { struct hostent *hp; bzero((char *)sin, sizeof(struct sockaddr)); sin->sin_family = AF_INET; if((sin->sin_addr.s_addr = inet_addr(addr)) == INADDR_NONE) { hp = gethostbyname(addr); if (!hp) { syslog(LOG_ERR,"unknown host %s", addr); return 1; } sin->sin_family = hp->h_addrtype; bcopy(hp->h_addr, (caddr_t)&sin->sin_addr, hp->h_length); } return 0; } static int tun_open(char *dev_name, struct sockaddr *ouraddr, char *theiraddr) { int s; struct sockaddr_in *sin; /* Open tun device */ tun = open(dev_name, O_RDWR); if (tun < 0) { syslog(LOG_ERR,"can't open %s - %m", dev_name); return(1); } /* * At first, name the interface. */ bzero((char *)&ifra, sizeof(ifra)); bzero((char *)&ifrq, sizeof(ifrq)); strncpy(ifrq.ifr_name, dev_name+5, IFNAMSIZ); strncpy(ifra.ifra_name, dev_name+5, IFNAMSIZ); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { syslog(LOG_ERR,"can't open socket - %m"); goto tunc_return; } /* * Delete (previous) addresses for interface * * !!!! * On FreeBSD this ioctl returns error * when tunN have no addresses, so - log and ignore it. * */ if (ioctl(s, SIOCDIFADDR, &ifra) < 0) { syslog(LOG_ERR,"SIOCDIFADDR - %m"); } /* * Set interface address */ sin = (struct sockaddr_in *)&(ifra.ifra_addr); bcopy(ouraddr, sin, sizeof(struct sockaddr_in)); sin->sin_len = sizeof(*sin); /* * Set destination address */ sin = (struct sockaddr_in *)&(ifra.ifra_broadaddr); if(Set_address(theiraddr,sin)) { syslog(LOG_ERR,"bad destination address: %s",theiraddr); goto stunc_return; } sin->sin_len = sizeof(*sin); if (ioctl(s, SIOCAIFADDR, &ifra) < 0) { syslog(LOG_ERR,"can't set interface address - %m"); goto stunc_return; } /* * Now, bring up the interface. */ if (ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) { syslog(LOG_ERR,"can't get interface flags - %m"); goto stunc_return; } ifrq.ifr_flags |= IFF_UP; if (!(ioctl(s, SIOCSIFFLAGS, &ifrq) < 0)) { close(s); return(0); } syslog(LOG_ERR,"can't set interface UP - %m"); stunc_return: close(s); tunc_return: close(tun); return(1); } static void Finish(int signum) { int s; syslog(LOG_INFO,"exiting"); close(net); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { syslog(LOG_ERR,"can't open socket - %m"); goto closing_tun; } /* * Shut down interface. */ if (ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) { syslog(LOG_ERR,"can't get interface flags - %m"); goto closing_fds; } ifrq.ifr_flags &= ~(IFF_UP|IFF_RUNNING); if (ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) { syslog(LOG_ERR,"can't set interface DOWN - %m"); goto closing_fds; } /* * Delete addresses for interface */ bzero(&ifra.ifra_addr, sizeof(ifra.ifra_addr)); bzero(&ifra.ifra_broadaddr, sizeof(ifra.ifra_addr)); bzero(&ifra.ifra_mask, sizeof(ifra.ifra_addr)); if (ioctl(s, SIOCDIFADDR, &ifra) < 0) { syslog(LOG_ERR,"can't delete interface's addresses - %m"); } closing_fds: close(s); closing_tun: close(tun); closelog(); exit(signum); } int main (int argc, char **argv) { int c, len, ipoff; char *dev_name = NULL; char *point_to = NULL; char *to_point = NULL; char *target; char *source = NULL; char *protocol = NULL; int protnum; struct sockaddr t_laddr; /* Source address of tunnel */ struct sockaddr whereto; /* Destination of tunnel */ struct sockaddr wherefrom; /* Source of tunnel */ struct sockaddr_in *to; char buf[0x2000]; /* Packets buffer */ struct ip *ip = (struct ip *)buf; fd_set rfds; /* File descriptors for select() */ int nfds; /* Return from select() */ int lastfd; /* highest fd we care about */ while ((c = getopt(argc, argv, "d:s:t:p:")) != -1) { switch (c) { case 'd': to_point = optarg; break; case 's': point_to = optarg; break; case 't': dev_name = optarg; break; case 'p': protocol = optarg; break; } } argc -= optind; argv += optind; if ((argc != 1 && argc != 2) || (dev_name == NULL) || (point_to == NULL) || (to_point == NULL)) { usage(); } if(protocol == NULL) protnum = 94; else protnum = atoi(protocol); if (argc == 1) { target = *argv; } else { source = *argv++; target = *argv; } /* Establish logging through 'syslog' */ openlog("nos-tun", LOG_PID, LOG_DAEMON); if(Set_address(point_to, (struct sockaddr_in *)&t_laddr)) { closelog(); exit(2); } if(tun_open(dev_name, &t_laddr, to_point)) { closelog(); exit(3); } to = (struct sockaddr_in *)&whereto; if(Set_address(target, to)) Finish(4); if ((net = socket(AF_INET, SOCK_RAW, protnum)) < 0) { syslog(LOG_ERR,"can't open socket - %m"); Finish(5); } if (source) { if (Set_address(source, (struct sockaddr_in *)&wherefrom)) Finish(9); if (bind(net, &wherefrom, sizeof(wherefrom)) < 0) { syslog(LOG_ERR, "can't bind source address - %m"); Finish(10); } } if (connect(net,&whereto,sizeof(struct sockaddr_in)) < 0 ) { syslog(LOG_ERR,"can't connect to target - %m"); close(net); Finish(6); } /* Demonize it */ daemon(0,0); /* Install signal handlers */ (void)signal(SIGHUP,Finish); (void)signal(SIGINT,Finish); (void)signal(SIGTERM,Finish); if (tun > net) lastfd = tun; else lastfd = net; for (;;) { /* Set file descriptors for select() */ FD_ZERO(&rfds); FD_SET(tun,&rfds); FD_SET(net,&rfds); nfds = select(lastfd+1,&rfds,NULL,NULL,NULL); if(nfds < 0) { syslog(LOG_ERR,"interrupted select"); close(net); Finish(7); } if(nfds == 0) { /* Impossible ? */ syslog(LOG_ERR,"timeout in select"); close(net); Finish(8); } if(FD_ISSET(net,&rfds)) { /* Read from socket ... */ len = read(net, buf, sizeof(buf)); /* Check if this is "our" packet */ if((ip->ip_src).s_addr == (to->sin_addr).s_addr) { /* ... skip encapsulation headers ... */ ipoff = (ip->ip_hl << 2); /* ... and write to tun-device */ write(tun,buf+ipoff,len-ipoff); } } if(FD_ISSET(tun,&rfds)) { /* Read from tun ... */ len = read(tun, buf, sizeof(buf)); /* ... and send to network */ if(send(net, buf, len,0) <= 0) { syslog(LOG_ERR,"can't send - %m"); } } } } static void usage(void) { fprintf(stderr, "usage: nos-tun -t tunnel -s source -d destination -p protocol_number [source] target\n"); exit(1); } diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c index 4a25b728e8a0..db9e05a0bc50 100644 --- a/sbin/restore/dirs.c +++ b/sbin/restore/dirs.c @@ -1,820 +1,818 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)dirs.c 8.7 (Berkeley) 5/1/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" /* * Symbol table of directories read from tape. */ #define HASHSIZE 1000 #define INOHASH(val) (val % HASHSIZE) struct inotab { struct inotab *t_next; ino_t t_ino; int32_t t_seekpt; int32_t t_size; }; static struct inotab *inotab[HASHSIZE]; /* * Information retained about directories. */ struct modeinfo { ino_t ino; struct timespec ctimep[2]; struct timespec mtimep[2]; mode_t mode; uid_t uid; gid_t gid; u_int flags; int extsize; }; /* * Definitions for library routines operating on directories. */ #undef DIRBLKSIZ #define DIRBLKSIZ 1024 struct rstdirdesc { int dd_fd; int32_t dd_loc; int32_t dd_size; char dd_buf[DIRBLKSIZ]; }; /* * Global variables for this file. */ static long seekpt; static FILE *df, *mf; static RST_DIR *dirp; static char dirfile[MAXPATHLEN] = "#"; /* No file */ static char modefile[MAXPATHLEN] = "#"; /* No file */ static char dot[2] = "."; /* So it can be modified */ static struct inotab *allocinotab(struct context *, long); static void flushent(void); static struct inotab *inotablookup(ino_t); static RST_DIR *opendirfile(const char *); static void putdir(char *, size_t); static void putdirattrs(char *, size_t); static void putent(struct direct *); static void rst_seekdir(RST_DIR *, long, long); static long rst_telldir(RST_DIR *); static struct direct *searchdir(ino_t, char *); static void fail_dirtmp(char *); /* * Extract directory contents, building up a directory structure * on disk for extraction by name. * If genmode is requested, save mode, owner, and times for all * directories on the tape. */ void extractdirs(int genmode) { struct inotab *itp; struct direct nulldir; int i, fd; const char *tmpdir; vprintf(stdout, "Extract directories from tape\n"); if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') tmpdir = _PATH_TMP; (void) snprintf(dirfile, sizeof(dirfile), "%s/rstdir%jd", tmpdir, (intmax_t)dumpdate); if (command != 'r' && command != 'R') { (void) strcat(dirfile, "-XXXXXX"); fd = mkstemp(dirfile); } else fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666); if (fd == -1 || (df = fdopen(fd, "w")) == NULL) { if (fd != -1) close(fd); warn("%s: cannot create directory database", dirfile); done(1); } if (genmode != 0) { (void) snprintf(modefile, sizeof(modefile), "%s/rstmode%jd", tmpdir, (intmax_t)dumpdate); if (command != 'r' && command != 'R') { (void) strcat(modefile, "-XXXXXX"); fd = mkstemp(modefile); } else fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666); if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) { if (fd != -1) close(fd); warn("%s: cannot create modefile", modefile); done(1); } } nulldir.d_ino = 0; nulldir.d_type = DT_DIR; nulldir.d_namlen = 1; (void) strcpy(nulldir.d_name, "/"); nulldir.d_reclen = DIRSIZ(0, &nulldir); for (;;) { curfile.name = ""; curfile.action = USING; if (curfile.mode == 0 || (curfile.mode & IFMT) != IFDIR) break; itp = allocinotab(&curfile, seekpt); getfile(putdir, putdirattrs, xtrnull); putent(&nulldir); flushent(); itp->t_size = seekpt - itp->t_seekpt; } if (fclose(df) != 0) fail_dirtmp(dirfile); dirp = opendirfile(dirfile); if (dirp == NULL) fprintf(stderr, "opendirfile: %s\n", strerror(errno)); if (mf != NULL && fclose(mf) != 0) fail_dirtmp(modefile); i = dirlookup(dot); if (i == 0) panic("Root directory is not on tape\n"); } /* * skip over all the directories on the tape */ void skipdirs(void) { while (curfile.ino && (curfile.mode & IFMT) == IFDIR) { skipfile(); } } /* * Recursively find names and inumbers of all files in subtree * pname and pass them off to be processed. */ void treescan(char *pname, ino_t ino, long (*todo)(char *, ino_t, int)) { struct inotab *itp; struct direct *dp; int namelen; long bpt; char locname[MAXPATHLEN]; itp = inotablookup(ino); if (itp == NULL) { /* * Pname is name of a simple file or an unchanged directory. */ (void) (*todo)(pname, ino, LEAF); return; } /* * Pname is a dumped directory name. */ if ((*todo)(pname, ino, NODE) == FAIL) return; /* * begin search through the directory * skipping over "." and ".." */ (void) strlcpy(locname, pname, sizeof(locname)); (void) strlcat(locname, "/", sizeof(locname)); namelen = strlen(locname); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dp = rst_readdir(dirp); /* "." */ if (dp != NULL && strcmp(dp->d_name, ".") == 0) dp = rst_readdir(dirp); /* ".." */ else fprintf(stderr, "Warning: `.' missing from directory %s\n", pname); if (dp != NULL && strcmp(dp->d_name, "..") == 0) dp = rst_readdir(dirp); /* first real entry */ else fprintf(stderr, "Warning: `..' missing from directory %s\n", pname); bpt = rst_telldir(dirp); /* * a zero inode signals end of directory */ while (dp != NULL) { locname[namelen] = '\0'; if (namelen + dp->d_namlen >= sizeof(locname)) { fprintf(stderr, "%s%s: name exceeds %zu char\n", locname, dp->d_name, sizeof(locname) - 1); } else { (void)strlcat(locname, dp->d_name, sizeof(locname)); treescan(locname, dp->d_ino, todo); rst_seekdir(dirp, bpt, itp->t_seekpt); } dp = rst_readdir(dirp); bpt = rst_telldir(dirp); } } /* * Lookup a pathname which is always assumed to start from the UFS_ROOTINO. */ struct direct * pathsearch(const char *pathname) { ino_t ino; struct direct *dp; char *path, *name, buffer[MAXPATHLEN]; strcpy(buffer, pathname); path = buffer; ino = UFS_ROOTINO; while (*path == '/') path++; dp = NULL; while ((name = strsep(&path, "/")) != NULL && *name != '\0') { if ((dp = searchdir(ino, name)) == NULL) return (NULL); ino = dp->d_ino; } return (dp); } /* * Lookup the requested name in directory inum. * Return its inode number if found, zero if it does not exist. */ static struct direct * searchdir(ino_t inum, char *name) { struct direct *dp; struct inotab *itp; int len; itp = inotablookup(inum); if (itp == NULL) return (NULL); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); len = strlen(name); do { dp = rst_readdir(dirp); if (dp == NULL) return (NULL); } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0); return (dp); } /* * Put the directory entries in the directory file */ static void putdir(char *buf, size_t size) { struct direct *dp; size_t loc, i; for (loc = 0; loc < size; ) { dp = (struct direct *)(buf + loc); if (Bcvt) swabst((u_char *)"ls", (u_char *) dp); if (oldinofmt && dp->d_ino != 0) { #if BYTE_ORDER == BIG_ENDIAN if (Bcvt) dp->d_namlen = dp->d_type; #else if (!Bcvt && dp->d_namlen == 0) dp->d_namlen = dp->d_type; #endif dp->d_type = DT_UNKNOWN; } i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); if ((dp->d_reclen & 0x3) != 0 || dp->d_reclen > i || dp->d_reclen < DIRSIZ(0, dp) #if NAME_MAX < 255 || dp->d_namlen > NAME_MAX #endif ) { vprintf(stdout, "Mangled directory: "); if ((dp->d_reclen & 0x3) != 0) vprintf(stdout, "reclen not multiple of 4 "); if (dp->d_reclen < DIRSIZ(0, dp)) vprintf(stdout, "reclen less than DIRSIZ (%u < %zu) ", dp->d_reclen, DIRSIZ(0, dp)); #if NAME_MAX < 255 if (dp->d_namlen > NAME_MAX) vprintf(stdout, "reclen name too big (%u > %u) ", dp->d_namlen, NAME_MAX); #endif vprintf(stdout, "\n"); loc += i; continue; } loc += dp->d_reclen; if (dp->d_ino != 0) { putent(dp); } } } /* * These variables are "local" to the following two functions. */ char dirbuf[DIRBLKSIZ]; long dirloc = 0; long prev = 0; /* * add a new directory entry to a file. */ static void putent(struct direct *dp) { dp->d_reclen = DIRSIZ(0, dp); if (dirloc + dp->d_reclen > DIRBLKSIZ) { ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; if (fwrite(dirbuf, DIRBLKSIZ, 1, df) != 1) fail_dirtmp(dirfile); dirloc = 0; } memmove(dirbuf + dirloc, dp, (long)dp->d_reclen); prev = dirloc; dirloc += dp->d_reclen; } /* * flush out a directory that is finished. */ static void flushent(void) { ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; if (fwrite(dirbuf, (int)dirloc, 1, df) != 1) fail_dirtmp(dirfile); seekpt = ftell(df); dirloc = 0; } /* * Save extended attributes for a directory entry to a file. */ static void putdirattrs(char *buf, size_t size) { if (mf != NULL && fwrite(buf, size, 1, mf) != 1) fail_dirtmp(modefile); } /* * Seek to an entry in a directory. * Only values returned by rst_telldir should be passed to rst_seekdir. * This routine handles many directories in a single file. * It takes the base of the directory in the file, plus * the desired seek offset into it. */ static void rst_seekdir(RST_DIR *dirp, long loc, long base) { if (loc == rst_telldir(dirp)) return; loc -= base; if (loc < 0) fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc); (void) lseek(dirp->dd_fd, base + rounddown2(loc, DIRBLKSIZ), SEEK_SET); dirp->dd_loc = loc & (DIRBLKSIZ - 1); if (dirp->dd_loc != 0) dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); } /* * get next entry in a directory. */ struct direct * rst_readdir(RST_DIR *dirp) { struct direct *dp; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) { dprintf(stderr, "error reading directory\n"); return (NULL); } } if (dirp->dd_loc >= dirp->dd_size) { dirp->dd_loc = 0; continue; } dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); if (dp->d_reclen == 0 || dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { dprintf(stderr, "corrupted directory: bad reclen %d\n", dp->d_reclen); return (NULL); } dirp->dd_loc += dp->d_reclen; if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0) return (NULL); if (dp->d_ino >= maxino) { dprintf(stderr, "corrupted directory: bad inum %d\n", dp->d_ino); continue; } return (dp); } } /* * Simulate the opening of a directory */ void * rst_opendir(const char *name) { struct inotab *itp; RST_DIR *dirp; ino_t ino; if ((ino = dirlookup(name)) > 0 && (itp = inotablookup(ino)) != NULL) { dirp = opendirfile(dirfile); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); return (dirp); } return (NULL); } /* * In our case, there is nothing to do when closing a directory. */ void rst_closedir(void *arg) { RST_DIR *dirp; dirp = arg; (void)close(dirp->dd_fd); free(dirp); return; } /* * Simulate finding the current offset in the directory. */ static long rst_telldir(RST_DIR *dirp) { return ((long)lseek(dirp->dd_fd, (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc); } /* * Open a directory file. */ static RST_DIR * opendirfile(const char *name) { RST_DIR *dirp; int fd; if ((fd = open(name, O_RDONLY)) == -1) return (NULL); if ((dirp = malloc(sizeof(RST_DIR))) == NULL) { (void)close(fd); return (NULL); } dirp->dd_fd = fd; dirp->dd_loc = 0; return (dirp); } /* * Set the mode, owner, and times for all new or changed directories */ void setdirmodes(int flags) { FILE *mf; struct modeinfo node; struct entry *ep; char *cp, *buf; const char *tmpdir; int bufsize; uid_t myuid; vprintf(stdout, "Set directory mode, owner, and times.\n"); if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') tmpdir = _PATH_TMP; if (command == 'r' || command == 'R') (void) snprintf(modefile, sizeof(modefile), "%s/rstmode%jd", tmpdir, (intmax_t)dumpdate); if (modefile[0] == '#') { panic("modefile not defined\n"); fprintf(stderr, "directory mode, owner, and times not set\n"); return; } mf = fopen(modefile, "r"); if (mf == NULL) { fprintf(stderr, "fopen: %s\n", strerror(errno)); fprintf(stderr, "cannot open mode file %s\n", modefile); fprintf(stderr, "directory mode, owner, and times not set\n"); return; } clearerr(mf); bufsize = 0; myuid = getuid(); for (;;) { (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); if (ferror(mf)) { warn("%s: cannot read modefile.", modefile); fprintf(stderr, "Mode, owner, and times not set.\n"); break; } if (feof(mf)) break; if (node.extsize > 0) { if (bufsize < node.extsize) { if (bufsize > 0) free(buf); if ((buf = malloc(node.extsize)) != NULL) { bufsize = node.extsize; } else { bufsize = 0; } } if (bufsize >= node.extsize) { (void) fread(buf, 1, node.extsize, mf); if (ferror(mf)) { warn("%s: cannot read modefile.", modefile); fprintf(stderr, "Not all external "); fprintf(stderr, "attributes set.\n"); break; } } else { (void) fseek(mf, node.extsize, SEEK_CUR); if (ferror(mf)) { warn("%s: cannot seek in modefile.", modefile); fprintf(stderr, "Not all directory "); fprintf(stderr, "attributes set.\n"); break; } } } ep = lookupino(node.ino); if (command == 'i' || command == 'x') { if (ep == NULL) continue; if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) { ep->e_flags &= ~NEW; continue; } if (node.ino == UFS_ROOTINO && reply("set owner/mode for '.'") == FAIL) continue; } if (ep == NULL) { panic("cannot find directory inode %ju\n", (uintmax_t)node.ino); continue; } cp = myname(ep); if (!Nflag) { if (myuid != 0) (void) chown(cp, myuid, node.gid); else (void) chown(cp, node.uid, node.gid); (void) chmod(cp, node.mode); if (node.extsize > 0) { if (bufsize >= node.extsize) { set_extattr(-1, cp, buf, node.extsize, SXA_FILE); } else { fprintf(stderr, "Cannot restore %s%s\n", "extended attributes for ", cp); } } utimensat(AT_FDCWD, cp, node.ctimep, 0); utimensat(AT_FDCWD, cp, node.mtimep, 0); (void) chflags(cp, node.flags); } ep->e_flags &= ~NEW; } if (bufsize > 0) free(buf); (void) fclose(mf); } /* * Generate a literal copy of a directory. */ int genliteraldir(char *name, ino_t ino) { struct inotab *itp; int ofile, dp, i, size; char buf[BUFSIZ]; itp = inotablookup(ino); if (itp == NULL) panic("Cannot find directory inode %ju named %s\n", (uintmax_t)ino, name); if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { fprintf(stderr, "%s: ", name); (void) fflush(stderr); fprintf(stderr, "cannot create file: %s\n", strerror(errno)); return (FAIL); } rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dp = dup(dirp->dd_fd); for (i = itp->t_size; i > 0; i -= BUFSIZ) { size = MIN(i, BUFSIZ); if (read(dp, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %ju, name %s\n", (uintmax_t)curfile.ino, curfile.name); fprintf(stderr, "read: %s\n", strerror(errno)); done(1); } if (!Nflag && write(ofile, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %ju, name %s\n", (uintmax_t)curfile.ino, curfile.name); fprintf(stderr, "write: %s\n", strerror(errno)); done(1); } } (void) close(dp); (void) close(ofile); return (GOOD); } /* * Determine the type of an inode */ int inodetype(ino_t ino) { struct inotab *itp; itp = inotablookup(ino); if (itp == NULL) return (LEAF); return (NODE); } /* * Allocate and initialize a directory inode entry. * If requested, save its pertinent mode, owner, and time info. */ static struct inotab * allocinotab(struct context *ctxp, long seekpt) { struct inotab *itp; struct modeinfo node; itp = calloc(1, sizeof(struct inotab)); if (itp == NULL) panic("no memory for directory table\n"); itp->t_next = inotab[INOHASH(ctxp->ino)]; inotab[INOHASH(ctxp->ino)] = itp; itp->t_ino = ctxp->ino; itp->t_seekpt = seekpt; if (mf == NULL) return (itp); node.ino = ctxp->ino; node.mtimep[0].tv_sec = ctxp->atime_sec; node.mtimep[0].tv_nsec = ctxp->atime_nsec; node.mtimep[1].tv_sec = ctxp->mtime_sec; node.mtimep[1].tv_nsec = ctxp->mtime_nsec; node.ctimep[0].tv_sec = ctxp->atime_sec; node.ctimep[0].tv_nsec = ctxp->atime_nsec; node.ctimep[1].tv_sec = ctxp->birthtime_sec; node.ctimep[1].tv_nsec = ctxp->birthtime_nsec; node.extsize = ctxp->extsize; node.mode = ctxp->mode; node.flags = ctxp->file_flags; node.uid = ctxp->uid; node.gid = ctxp->gid; if (fwrite((char *)&node, sizeof(struct modeinfo), 1, mf) != 1) fail_dirtmp(modefile); return (itp); } /* * Look up an inode in the table of directories */ static struct inotab * inotablookup(ino_t ino) { struct inotab *itp; for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) if (itp->t_ino == ino) return (itp); return (NULL); } /* * Clean up and exit */ void done(int exitcode) { closemt(); if (modefile[0] != '#') { (void) truncate(modefile, 0); (void) unlink(modefile); } if (dirfile[0] != '#') { (void) truncate(dirfile, 0); (void) unlink(dirfile); } exit(exitcode); } /* * Print out information about the failure to save directory, * extended attribute, and mode information. */ static void fail_dirtmp(char *filename) { const char *tmpdir; warn("%s: cannot write directory database", filename); if (errno == ENOSPC) { if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') tmpdir = _PATH_TMP; fprintf(stderr, "Try making space in %s, %s\n%s\n", tmpdir, "or set environment variable TMPDIR", "to an alternate location with more disk space."); } done(1); } diff --git a/sbin/restore/symtab.c b/sbin/restore/symtab.c index 49ed39dd2022..0172a2c593d0 100644 --- a/sbin/restore/symtab.c +++ b/sbin/restore/symtab.c @@ -1,617 +1,615 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)symtab.c 8.3 (Berkeley) 4/28/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * These routines maintain the symbol table which tracks the state * of the file system being restored. They provide lookup by either * name or inode number. They also provide for creation, deletion, * and renaming of entries. Because of the dynamic nature of pathnames, * names should not be saved, but always constructed just before they * are needed, by calling "myname". */ #include #include #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" /* * The following variables define the inode symbol table. * The primary hash table is dynamically allocated based on * the number of inodes in the file system (maxino), scaled by * HASHFACTOR. The variable "entry" points to the hash table; * the variable "entrytblsize" indicates its size (in entries). */ #define HASHFACTOR 5 static struct entry **entry; static long entrytblsize; static void addino(ino_t, struct entry *); static struct entry *lookupparent(char *); static void removeentry(struct entry *); /* * Look up an entry by inode number */ struct entry * lookupino(ino_t inum) { struct entry *ep; if (inum < UFS_WINO || inum >= maxino) return (NULL); for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next) if (ep->e_ino == inum) return (ep); return (NULL); } /* * Add an entry into the entry table */ static void addino(ino_t inum, struct entry *np) { struct entry **epp; if (inum < UFS_WINO || inum >= maxino) panic("addino: out of range %ju\n", (uintmax_t)inum); epp = &entry[inum % entrytblsize]; np->e_ino = inum; np->e_next = *epp; *epp = np; if (dflag) for (np = np->e_next; np != NULL; np = np->e_next) if (np->e_ino == inum) badentry(np, "duplicate inum"); } /* * Delete an entry from the entry table */ void deleteino(ino_t inum) { struct entry *next; struct entry **prev; if (inum < UFS_WINO || inum >= maxino) panic("deleteino: out of range %ju\n", (uintmax_t)inum); prev = &entry[inum % entrytblsize]; for (next = *prev; next != NULL; next = next->e_next) { if (next->e_ino == inum) { next->e_ino = 0; *prev = next->e_next; return; } prev = &next->e_next; } panic("deleteino: %ju not found\n", (uintmax_t)inum); } /* * Look up an entry by name */ struct entry * lookupname(char *name) { struct entry *ep; char *np, *cp; char buf[MAXPATHLEN]; cp = name; for (ep = lookupino(UFS_ROOTINO); ep != NULL; ep = ep->e_entries) { for (np = buf; *cp != '/' && *cp != '\0' && np < &buf[sizeof(buf)]; ) *np++ = *cp++; if (np == &buf[sizeof(buf)]) break; *np = '\0'; for ( ; ep != NULL; ep = ep->e_sibling) if (strcmp(ep->e_name, buf) == 0) break; if (ep == NULL) break; if (*cp++ == '\0') return (ep); } return (NULL); } /* * Look up the parent of a pathname */ static struct entry * lookupparent(char *name) { struct entry *ep; char *tailindex; tailindex = strrchr(name, '/'); if (tailindex == NULL) return (NULL); *tailindex = '\0'; ep = lookupname(name); *tailindex = '/'; if (ep == NULL) return (NULL); if (ep->e_type != NODE) panic("%s is not a directory\n", name); return (ep); } /* * Determine the current pathname of a node or leaf */ char * myname(struct entry *ep) { char *cp; static char namebuf[MAXPATHLEN]; for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) { cp -= ep->e_namlen; memmove(cp, ep->e_name, (long)ep->e_namlen); if (ep == lookupino(UFS_ROOTINO)) return (cp); *(--cp) = '/'; ep = ep->e_parent; } panic("%s: pathname too long\n", cp); return(cp); } /* * Unused symbol table entries are linked together on a free list * headed by the following pointer. */ static struct entry *freelist = NULL; /* * add an entry to the symbol table */ struct entry * addentry(char *name, ino_t inum, int type) { struct entry *np, *ep; if (freelist != NULL) { np = freelist; freelist = np->e_next; memset(np, 0, (long)sizeof(struct entry)); } else { np = (struct entry *)calloc(1, sizeof(struct entry)); if (np == NULL) panic("no memory to extend symbol table\n"); } np->e_type = type & ~LINK; ep = lookupparent(name); if (ep == NULL) { if (inum != UFS_ROOTINO || lookupino(UFS_ROOTINO) != NULL) panic("bad name to addentry %s\n", name); np->e_name = savename(name); np->e_namlen = strlen(name); np->e_parent = np; addino(UFS_ROOTINO, np); return (np); } np->e_name = savename(strrchr(name, '/') + 1); np->e_namlen = strlen(np->e_name); np->e_parent = ep; np->e_sibling = ep->e_entries; ep->e_entries = np; if (type & LINK) { ep = lookupino(inum); if (ep == NULL) panic("link to non-existent name\n"); np->e_ino = inum; np->e_links = ep->e_links; ep->e_links = np; } else if (inum != 0) { if (lookupino(inum) != NULL) panic("duplicate entry\n"); addino(inum, np); } return (np); } /* * delete an entry from the symbol table */ void freeentry(struct entry *ep) { struct entry *np; ino_t inum; if (ep->e_flags != REMOVED) badentry(ep, "not marked REMOVED"); if (ep->e_type == NODE) { if (ep->e_links != NULL) badentry(ep, "freeing referenced directory"); if (ep->e_entries != NULL) badentry(ep, "freeing non-empty directory"); } if (ep->e_ino != 0) { np = lookupino(ep->e_ino); if (np == NULL) badentry(ep, "lookupino failed"); if (np == ep) { inum = ep->e_ino; deleteino(inum); if (ep->e_links != NULL) addino(inum, ep->e_links); } else { for (; np != NULL; np = np->e_links) { if (np->e_links == ep) { np->e_links = ep->e_links; break; } } if (np == NULL) badentry(ep, "link not found"); } } removeentry(ep); freename(ep->e_name); ep->e_next = freelist; freelist = ep; } /* * Relocate an entry in the tree structure */ void moveentry(struct entry *ep, char *newname) { struct entry *np; char *cp; np = lookupparent(newname); if (np == NULL) badentry(ep, "cannot move ROOT"); if (np != ep->e_parent) { removeentry(ep); ep->e_parent = np; ep->e_sibling = np->e_entries; np->e_entries = ep; } cp = strrchr(newname, '/') + 1; freename(ep->e_name); ep->e_name = savename(cp); ep->e_namlen = strlen(cp); if (strcmp(gentempname(ep), ep->e_name) == 0) ep->e_flags |= TMPNAME; else ep->e_flags &= ~TMPNAME; } /* * Remove an entry in the tree structure */ static void removeentry(struct entry *ep) { struct entry *np; np = ep->e_parent; if (np->e_entries == ep) { np->e_entries = ep->e_sibling; } else { for (np = np->e_entries; np != NULL; np = np->e_sibling) { if (np->e_sibling == ep) { np->e_sibling = ep->e_sibling; break; } } if (np == NULL) badentry(ep, "cannot find entry in parent list"); } } /* * Table of unused string entries, sorted by length. * * Entries are allocated in STRTBLINCR sized pieces so that names * of similar lengths can use the same entry. The value of STRTBLINCR * is chosen so that every entry has at least enough space to hold * a "struct strtbl" header. Thus every entry can be linked onto an * appropriate free list. * * NB. The macro "allocsize" below assumes that "struct strhdr" * has a size that is a power of two. */ struct strhdr { struct strhdr *next; }; #define STRTBLINCR (sizeof(struct strhdr)) #define allocsize(size) roundup2((size) + 1, STRTBLINCR) static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR]; /* * Allocate space for a name. It first looks to see if it already * has an appropriate sized entry, and if not allocates a new one. */ char * savename(char *name) { struct strhdr *np; size_t len; char *cp; if (name == NULL) panic("bad name\n"); len = strlen(name); np = strtblhdr[len / STRTBLINCR].next; if (np != NULL) { strtblhdr[len / STRTBLINCR].next = np->next; cp = (char *)np; } else { cp = malloc(allocsize(len)); if (cp == NULL) panic("no space for string table\n"); } (void) strcpy(cp, name); return (cp); } /* * Free space for a name. The resulting entry is linked onto the * appropriate free list. */ void freename(char *name) { struct strhdr *tp, *np; tp = &strtblhdr[strlen(name) / STRTBLINCR]; np = (struct strhdr *)name; np->next = tp->next; tp->next = np; } /* * Useful quantities placed at the end of a dumped symbol table. */ struct symtableheader { int32_t volno; int32_t stringsize; int32_t entrytblsize; time_t dumptime; time_t dumpdate; ino_t maxino; int32_t ntrec; }; /* * dump a snapshot of the symbol table */ void dumpsymtable(char *filename, long checkpt) { struct entry *ep, *tep; ino_t i; struct entry temp, *tentry; long mynum = 1, stroff = 0; FILE *fd; struct symtableheader hdr; vprintf(stdout, "Checkpointing the restore\n"); if (Nflag) return; if ((fd = fopen(filename, "w")) == NULL) { fprintf(stderr, "fopen: %s\n", strerror(errno)); panic("cannot create save file %s for symbol table\n", filename); done(1); } clearerr(fd); /* * Assign indices to each entry * Write out the string entries */ for (i = UFS_WINO; i <= maxino; i++) { for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { ep->e_index = mynum++; (void) fwrite(ep->e_name, sizeof(char), (int)allocsize(ep->e_namlen), fd); } } /* * Convert pointers to indexes, and output */ tep = &temp; stroff = 0; for (i = UFS_WINO; i <= maxino; i++) { for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { memmove(tep, ep, (long)sizeof(struct entry)); tep->e_name = (char *)stroff; stroff += allocsize(ep->e_namlen); tep->e_parent = (struct entry *)ep->e_parent->e_index; if (ep->e_links != NULL) tep->e_links = (struct entry *)ep->e_links->e_index; if (ep->e_sibling != NULL) tep->e_sibling = (struct entry *)ep->e_sibling->e_index; if (ep->e_entries != NULL) tep->e_entries = (struct entry *)ep->e_entries->e_index; if (ep->e_next != NULL) tep->e_next = (struct entry *)ep->e_next->e_index; (void) fwrite((char *)tep, sizeof(struct entry), 1, fd); } } /* * Convert entry pointers to indexes, and output */ for (i = 0; i < entrytblsize; i++) { if (entry[i] == NULL) tentry = NULL; else tentry = (struct entry *)entry[i]->e_index; (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd); } hdr.volno = checkpt; hdr.maxino = maxino; hdr.entrytblsize = entrytblsize; hdr.stringsize = stroff; hdr.dumptime = dumptime; hdr.dumpdate = dumpdate; hdr.ntrec = ntrec; (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd); if (ferror(fd)) { fprintf(stderr, "fwrite: %s\n", strerror(errno)); panic("output error to file %s writing symbol table\n", filename); } (void) fclose(fd); } /* * Initialize a symbol table from a file */ void initsymtable(char *filename) { char *base; long tblsize; struct entry *ep; struct entry *baseep, *lep; struct symtableheader hdr; struct stat stbuf; long i; int fd; vprintf(stdout, "Initialize symbol table.\n"); if (filename == NULL) { entrytblsize = maxino / HASHFACTOR; entry = calloc((unsigned)entrytblsize, sizeof(struct entry *)); if (entry == NULL) panic("no memory for entry table\n"); ep = addentry(".", UFS_ROOTINO, NODE); ep->e_flags |= NEW; return; } if ((fd = open(filename, O_RDONLY, 0)) < 0) { fprintf(stderr, "open: %s\n", strerror(errno)); panic("cannot open symbol table file %s\n", filename); } if (fstat(fd, &stbuf) < 0) { fprintf(stderr, "stat: %s\n", strerror(errno)); panic("cannot stat symbol table file %s\n", filename); } tblsize = stbuf.st_size - sizeof(struct symtableheader); base = calloc(sizeof(char), (unsigned)tblsize); if (base == NULL) panic("cannot allocate space for symbol table\n"); if (read(fd, base, (int)tblsize) < 0 || read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) { fprintf(stderr, "read: %s\n", strerror(errno)); panic("cannot read symbol table file %s\n", filename); } (void)close(fd); switch (command) { case 'r': /* * For normal continuation, insure that we are using * the next incremental tape */ if (hdr.dumpdate != dumptime) { if (hdr.dumpdate < dumptime) fprintf(stderr, "Incremental tape too low\n"); else fprintf(stderr, "Incremental tape too high\n"); done(1); } break; case 'R': /* * For restart, insure that we are using the same tape */ curfile.action = SKIP; dumptime = hdr.dumptime; dumpdate = hdr.dumpdate; if (!bflag) newtapebuf(hdr.ntrec); getvol(hdr.volno); break; default: panic("initsymtable called from command %c\n", command); break; } maxino = hdr.maxino; entrytblsize = hdr.entrytblsize; entry = (struct entry **) (base + tblsize - (entrytblsize * sizeof(struct entry *))); baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry)); lep = (struct entry *)entry; for (i = 0; i < entrytblsize; i++) { if (entry[i] == NULL) continue; entry[i] = &baseep[(long)entry[i]]; } for (ep = &baseep[1]; ep < lep; ep++) { ep->e_name = base + (long)ep->e_name; ep->e_parent = &baseep[(long)ep->e_parent]; if (ep->e_sibling != NULL) ep->e_sibling = &baseep[(long)ep->e_sibling]; if (ep->e_links != NULL) ep->e_links = &baseep[(long)ep->e_links]; if (ep->e_entries != NULL) ep->e_entries = &baseep[(long)ep->e_entries]; if (ep->e_next != NULL) ep->e_next = &baseep[(long)ep->e_next]; } } diff --git a/sbin/restore/utilities.c b/sbin/restore/utilities.c index a53d42f9e600..d95330f6de4a 100644 --- a/sbin/restore/utilities.c +++ b/sbin/restore/utilities.c @@ -1,424 +1,422 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)utilities.c 8.5 (Berkeley) 4/28/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" /* * Insure that all the components of a pathname exist. */ void pathcheck(char *name) { char *cp; struct entry *ep; char *start; start = strchr(name, '/'); if (start == NULL) return; for (cp = start; *cp != '\0'; cp++) { if (*cp != '/') continue; *cp = '\0'; ep = lookupname(name); if (ep == NULL) { /* Safe; we know the pathname exists in the dump. */ ep = addentry(name, pathsearch(name)->d_ino, NODE); newnode(ep); } ep->e_flags |= NEW|KEEP; *cp = '/'; } } /* * Change a name to a unique temporary name. */ void mktempname(struct entry *ep) { char oldname[MAXPATHLEN]; if (ep->e_flags & TMPNAME) badentry(ep, "mktempname: called with TMPNAME"); ep->e_flags |= TMPNAME; (void) strcpy(oldname, myname(ep)); freename(ep->e_name); ep->e_name = savename(gentempname(ep)); ep->e_namlen = strlen(ep->e_name); renameit(oldname, myname(ep)); } /* * Generate a temporary name for an entry. */ char * gentempname(struct entry *ep) { static char name[MAXPATHLEN]; struct entry *np; long i = 0; for (np = lookupino(ep->e_ino); np != NULL && np != ep; np = np->e_links) i++; if (np == NULL) badentry(ep, "not on ino list"); (void) sprintf(name, "%s%ld%lu", TMPHDR, i, (u_long)ep->e_ino); return (name); } /* * Rename a file or directory. */ void renameit(char *from, char *to) { if (!Nflag && rename(from, to) < 0) { fprintf(stderr, "warning: cannot rename %s to %s: %s\n", from, to, strerror(errno)); return; } vprintf(stdout, "rename %s to %s\n", from, to); } /* * Create a new node (directory). */ void newnode(struct entry *np) { char *cp; if (np->e_type != NODE) badentry(np, "newnode: not a node"); cp = myname(np); if (!Nflag && mkdir(cp, 0777) < 0 && !uflag) { np->e_flags |= EXISTED; fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); return; } vprintf(stdout, "Make node %s\n", cp); } /* * Remove an old node (directory). */ void removenode(struct entry *ep) { char *cp; if (ep->e_type != NODE) badentry(ep, "removenode: not a node"); if (ep->e_entries != NULL) badentry(ep, "removenode: non-empty directory"); ep->e_flags |= REMOVED; ep->e_flags &= ~TMPNAME; cp = myname(ep); if (!Nflag && rmdir(cp) < 0) { fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); return; } vprintf(stdout, "Remove node %s\n", cp); } /* * Remove a leaf. */ void removeleaf(struct entry *ep) { char *cp; if (ep->e_type != LEAF) badentry(ep, "removeleaf: not a leaf"); ep->e_flags |= REMOVED; ep->e_flags &= ~TMPNAME; cp = myname(ep); if (!Nflag && unlink(cp) < 0) { fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno)); return; } vprintf(stdout, "Remove leaf %s\n", cp); } /* * Create a link. */ int linkit(char *existing, char *new, int type) { /* if we want to unlink first, do it now so *link() won't fail */ if (uflag && !Nflag) (void)unlink(new); if (type == SYMLINK) { if (!Nflag && symlink(existing, new) < 0) { fprintf(stderr, "warning: cannot create symbolic link %s->%s: %s\n", new, existing, strerror(errno)); return (FAIL); } } else if (type == HARDLINK) { int ret; if (!Nflag && (ret = link(existing, new)) < 0) { struct stat s; /* * Most likely, the schg flag is set. Clear the * flags and try again. */ if (stat(existing, &s) == 0 && s.st_flags != 0 && chflags(existing, 0) == 0) { ret = link(existing, new); chflags(existing, s.st_flags); } if (ret < 0) { fprintf(stderr, "warning: cannot create " "hard link %s->%s: %s\n", new, existing, strerror(errno)); return (FAIL); } } } else { panic("linkit: unknown type %d\n", type); return (FAIL); } vprintf(stdout, "Create %s link %s->%s\n", type == SYMLINK ? "symbolic" : "hard", new, existing); return (GOOD); } /* * Create a whiteout. */ int addwhiteout(char *name) { if (!Nflag && mknod(name, S_IFWHT, 0) < 0) { fprintf(stderr, "warning: cannot create whiteout %s: %s\n", name, strerror(errno)); return (FAIL); } vprintf(stdout, "Create whiteout %s\n", name); return (GOOD); } /* * Delete a whiteout. */ void delwhiteout(struct entry *ep) { char *name; if (ep->e_type != LEAF) badentry(ep, "delwhiteout: not a leaf"); ep->e_flags |= REMOVED; ep->e_flags &= ~TMPNAME; name = myname(ep); if (!Nflag && undelete(name) < 0) { fprintf(stderr, "warning: cannot delete whiteout %s: %s\n", name, strerror(errno)); return; } vprintf(stdout, "Delete whiteout %s\n", name); } /* * find lowest number file (above "start") that needs to be extracted */ ino_t lowerbnd(ino_t start) { struct entry *ep; for ( ; start < maxino; start++) { ep = lookupino(start); if (ep == NULL || ep->e_type == NODE) continue; if (ep->e_flags & (NEW|EXTRACT)) return (start); } return (start); } /* * find highest number file (below "start") that needs to be extracted */ ino_t upperbnd(ino_t start) { struct entry *ep; for ( ; start > UFS_ROOTINO; start--) { ep = lookupino(start); if (ep == NULL || ep->e_type == NODE) continue; if (ep->e_flags & (NEW|EXTRACT)) return (start); } return (start); } /* * report on a badly formed entry */ void badentry(struct entry *ep, char *msg) { fprintf(stderr, "bad entry: %s\n", msg); fprintf(stderr, "name: %s\n", myname(ep)); fprintf(stderr, "parent name %s\n", myname(ep->e_parent)); if (ep->e_sibling != NULL) fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling)); if (ep->e_entries != NULL) fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries)); if (ep->e_links != NULL) fprintf(stderr, "next link name: %s\n", myname(ep->e_links)); if (ep->e_next != NULL) fprintf(stderr, "next hashchain name: %s\n", myname(ep->e_next)); fprintf(stderr, "entry type: %s\n", ep->e_type == NODE ? "NODE" : "LEAF"); fprintf(stderr, "inode number: %lu\n", (u_long)ep->e_ino); panic("flags: %s\n", flagvalues(ep)); } /* * Construct a string indicating the active flag bits of an entry. */ char * flagvalues(struct entry *ep) { static char flagbuf[BUFSIZ]; (void) strcpy(flagbuf, "|NIL"); flagbuf[0] = '\0'; if (ep->e_flags & REMOVED) (void) strcat(flagbuf, "|REMOVED"); if (ep->e_flags & TMPNAME) (void) strcat(flagbuf, "|TMPNAME"); if (ep->e_flags & EXTRACT) (void) strcat(flagbuf, "|EXTRACT"); if (ep->e_flags & NEW) (void) strcat(flagbuf, "|NEW"); if (ep->e_flags & KEEP) (void) strcat(flagbuf, "|KEEP"); if (ep->e_flags & EXISTED) (void) strcat(flagbuf, "|EXISTED"); return (&flagbuf[1]); } /* * Check to see if a name is on a dump tape. */ ino_t dirlookup(const char *name) { struct direct *dp; ino_t ino; ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino; if (ino == 0 || TSTINO(ino, dumpmap) == 0) fprintf(stderr, "%s is not on the tape\n", name); return (ino); } /* * Elicit a reply. */ int reply(char *question) { int c; do { fprintf(stderr, "%s? [yn] ", question); (void) fflush(stderr); c = getc(terminal); while (c != '\n' && getc(terminal) != '\n') if (c == EOF) return (FAIL); } while (c != 'y' && c != 'n'); if (c == 'y') return (GOOD); return (FAIL); } /* * handle unexpected inconsistencies */ #include void panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); if (yflag) return; if (reply("abort") == GOOD) { if (reply("dump core") == GOOD) abort(); done(1); } } diff --git a/sbin/umount/umount.c b/sbin/umount/umount.c index 9119c5be0da1..aca7c201bc9b 100644 --- a/sbin/umount/umount.c +++ b/sbin/umount/umount.c @@ -1,657 +1,655 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1989, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)umount.c 8.8 (Berkeley) 5/8/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mounttab.h" typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat; static struct addrinfo *nfshost_ai = NULL; static int fflag, vflag; static char *nfshost; struct statfs *checkmntlist(char *); int checkvfsname (const char *, char **); struct statfs *getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what); char **makevfslist (const char *); size_t mntinfo (struct statfs **); int namematch (struct addrinfo *); int parsehexfsid(const char *hex, fsid_t *fsid); int sacmp (void *, void *); int umountall (char **); int checkname (char *, char **); int umountfs(struct statfs *sfs); void usage (void); int xdr_dir (XDR *, char *); int main(int argc, char *argv[]) { int all, errs, ch, mntsize, error, nfsforce, ret; char **typelist = NULL; struct statfs *mntbuf, *sfs; struct addrinfo hints; nfsforce = all = errs = 0; while ((ch = getopt(argc, argv, "AaF:fh:Nnt:v")) != -1) switch (ch) { case 'A': all = 2; break; case 'a': all = 1; break; case 'F': setfstab(optarg); break; case 'f': fflag |= MNT_FORCE; break; case 'h': /* -h implies -A. */ all = 2; nfshost = optarg; break; case 'N': nfsforce = 1; break; case 'n': fflag |= MNT_NONBUSY; break; case 't': if (typelist != NULL) err(1, "only one -t option may be specified"); typelist = makevfslist(optarg); break; case 'v': vflag = 1; break; default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; if ((fflag & MNT_FORCE) != 0 && (fflag & MNT_NONBUSY) != 0) err(1, "-f and -n are mutually exclusive"); if ((argc == 0 && !all) || (argc != 0 && all)) usage(); if (nfsforce != 0 && (argc == 0 || nfshost != NULL || typelist != NULL)) usage(); /* -h implies "-t nfs" if no -t flag. */ if ((nfshost != NULL) && (typelist == NULL)) typelist = makevfslist("nfs"); if (nfshost != NULL) { memset(&hints, 0, sizeof hints); error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai); if (error) errx(1, "%s: %s", nfshost, gai_strerror(error)); } switch (all) { case 2: if ((mntsize = mntinfo(&mntbuf)) <= 0) break; /* * We unmount the nfs-mounts in the reverse order * that they were mounted. */ for (errs = 0, mntsize--; mntsize > 0; mntsize--) { sfs = &mntbuf[mntsize]; if (checkvfsname(sfs->f_fstypename, typelist)) continue; if (strcmp(sfs->f_mntonname, "/dev") == 0) continue; if (umountfs(sfs) != 0) errs = 1; } free(mntbuf); break; case 1: if (setfsent() == 0) err(1, "%s", getfstab()); errs = umountall(typelist); break; case 0: for (errs = 0; *argv != NULL; ++argv) if (nfsforce != 0) { /* * First do the nfssvc() syscall to shut down * the mount point and then do the forced * dismount. */ ret = nfssvc(NFSSVC_FORCEDISM, *argv); if (ret >= 0) ret = unmount(*argv, MNT_FORCE); if (ret < 0) { warn("%s", *argv); errs = 1; } } else if (checkname(*argv, typelist) != 0) errs = 1; break; } exit(errs); } int umountall(char **typelist) { struct xvfsconf vfc; struct fstab *fs; int rval; char *cp; static int firstcall = 1; if ((fs = getfsent()) != NULL) firstcall = 0; else if (firstcall) errx(1, "fstab reading failure"); else return (0); do { /* Ignore the root. */ if (strcmp(fs->fs_file, "/") == 0) continue; /* * !!! * Historic practice: ignore unknown FSTAB_* fields. */ if (strcmp(fs->fs_type, FSTAB_RW) && strcmp(fs->fs_type, FSTAB_RO) && strcmp(fs->fs_type, FSTAB_RQ)) continue; /* Ignore unknown file system types. */ if (getvfsbyname(fs->fs_vfstype, &vfc) == -1) continue; if (checkvfsname(fs->fs_vfstype, typelist)) continue; /* * We want to unmount the file systems in the reverse order * that they were mounted. So, we save off the file name * in some allocated memory, and then call recursively. */ if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL) err(1, "malloc failed"); (void)strcpy(cp, fs->fs_file); rval = umountall(typelist); rval = checkname(cp, typelist) || rval; free(cp); return (rval); } while ((fs = getfsent()) != NULL); return (0); } /* * Do magic checks on mountpoint/device/fsid, and then call unmount(2). */ int checkname(char *mntname, char **typelist) { char buf[MAXPATHLEN]; struct statfs sfsbuf; struct stat sb; struct statfs *sfs; char *delimp; dev_t dev; int len; /* * 1. Check if the name exists in the mounttable. */ sfs = checkmntlist(mntname); /* * 2. Remove trailing slashes if there are any. After that * we look up the name in the mounttable again. */ if (sfs == NULL) { len = strlen(mntname); while (len > 1 && mntname[len - 1] == '/') mntname[--len] = '\0'; sfs = checkmntlist(mntname); } /* * 3. Check if the deprecated NFS syntax with an '@' has been used * and translate it to the ':' syntax. Look up the name in the * mount table again. */ if (sfs == NULL && (delimp = strrchr(mntname, '@')) != NULL) { snprintf(buf, sizeof(buf), "%s:%.*s", delimp + 1, (int)(delimp - mntname), mntname); len = strlen(buf); while (len > 1 && buf[len - 1] == '/') buf[--len] = '\0'; sfs = checkmntlist(buf); } /* * 4. Resort to a statfs(2) call. This is the last check so that * hung NFS filesystems for example can be unmounted without * potentially blocking forever in statfs() as long as the * filesystem is specified unambiguously. This covers all the * hard cases such as symlinks and mismatches between the * mount list and reality. * We also do this if an ambiguous mount point was specified. */ if (sfs == NULL || (getmntentry(NULL, mntname, NULL, FIND) != NULL && getmntentry(NULL, mntname, NULL, CHECKUNIQUE) == NULL)) { if (statfs(mntname, &sfsbuf) != 0) { warn("%s: statfs", mntname); } else if (stat(mntname, &sb) != 0) { warn("%s: stat", mntname); } else if (S_ISDIR(sb.st_mode)) { /* Check that `mntname' is the root directory. */ dev = sb.st_dev; snprintf(buf, sizeof(buf), "%s/..", mntname); if (stat(buf, &sb) != 0) { warn("%s: stat", buf); } else if (sb.st_dev == dev) { warnx("%s: not a file system root directory", mntname); return (1); } else sfs = &sfsbuf; } } if (sfs == NULL) { warnx("%s: unknown file system", mntname); return (1); } if (checkvfsname(sfs->f_fstypename, typelist)) return (1); return (umountfs(sfs)); } /* * NFS stuff and unmount(2) call */ int umountfs(struct statfs *sfs) { char fsidbuf[64]; enum clnt_stat clnt_stat; struct timeval try; struct addrinfo *ai, hints; int do_rpc; CLIENT *clp; char *nfsdirname, *orignfsdirname; char *hostp, *delimp; char buf[1024]; struct nfscl_dumpmntopts dumpmntopts; const char *proto_ptr = NULL; ai = NULL; do_rpc = 0; hostp = NULL; nfsdirname = delimp = orignfsdirname = NULL; memset(&hints, 0, sizeof hints); if (strcmp(sfs->f_fstypename, "nfs") == 0) { if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL) err(1, "strdup"); orignfsdirname = nfsdirname; if (*nfsdirname == '[' && (delimp = strchr(nfsdirname + 1, ']')) != NULL && *(delimp + 1) == ':') { hostp = nfsdirname + 1; nfsdirname = delimp + 2; } else if ((delimp = strrchr(nfsdirname, ':')) != NULL) { hostp = nfsdirname; nfsdirname = delimp + 1; } if (hostp != NULL) { *delimp = '\0'; getaddrinfo(hostp, NULL, &hints, &ai); if (ai == NULL) { warnx("can't get net id for host"); } } /* * Check if we have to start the rpc-call later. * If there are still identical nfs-names mounted, * we skip the rpc-call. Obviously this has to * happen before unmount(2), but it should happen * after the previous namecheck. * A non-NULL return means that this is the last * mount from mntfromname that is still mounted. */ if (getmntentry(sfs->f_mntfromname, NULL, NULL, CHECKUNIQUE) != NULL) { do_rpc = 1; proto_ptr = "udp"; /* * Try and find out whether this NFS mount is NFSv4 and * what protocol is being used. If this fails, the * default is NFSv2,3 and use UDP for the Unmount RPC. */ dumpmntopts.ndmnt_fname = sfs->f_mntonname; dumpmntopts.ndmnt_buf = buf; dumpmntopts.ndmnt_blen = sizeof(buf); if (nfssvc(NFSSVC_DUMPMNTOPTS, &dumpmntopts) >= 0) { if (strstr(buf, "nfsv4,") != NULL) do_rpc = 0; else if (strstr(buf, ",tcp,") != NULL) proto_ptr = "tcp"; } } } if (!namematch(ai)) { free(orignfsdirname); return (1); } /* First try to unmount using the file system ID. */ snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0], sfs->f_fsid.val[1]); if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) { /* XXX, non-root users get a zero fsid, so don't warn. */ if (errno != ENOENT || sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0) warn("unmount of %s failed", sfs->f_mntonname); if (errno != ENOENT) { free(orignfsdirname); return (1); } /* Compatibility for old kernels. */ if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0) warnx("retrying using path instead of file system ID"); if (unmount(sfs->f_mntonname, fflag) != 0) { warn("unmount of %s failed", sfs->f_mntonname); free(orignfsdirname); return (1); } } /* Mark this file system as unmounted. */ getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE); if (vflag) (void)printf("%s: unmount from %s\n", sfs->f_mntfromname, sfs->f_mntonname); /* * Report to mountd-server which nfsname * has been unmounted. */ if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) { clp = clnt_create(hostp, MOUNTPROG, MOUNTVERS3, proto_ptr); if (clp == NULL) { warnx("%s: %s", hostp, clnt_spcreateerror("MOUNTPROG")); free(orignfsdirname); return (1); } clp->cl_auth = authsys_create_default(); try.tv_sec = 20; try.tv_usec = 0; clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir, nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try); if (clnt_stat != RPC_SUCCESS) { warnx("%s: %s", hostp, clnt_sperror(clp, "RPCMNT_UMOUNT")); free(orignfsdirname); return (1); } /* * Remove the unmounted entry from /var/db/mounttab. */ if (read_mtab()) { clean_mtab(hostp, nfsdirname, vflag); if(!write_mtab(vflag)) warnx("cannot remove mounttab entry %s:%s", hostp, nfsdirname); free_mtab(); } auth_destroy(clp->cl_auth); clnt_destroy(clp); } free(orignfsdirname); return (0); } struct statfs * getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what) { static struct statfs *mntbuf; static size_t mntsize = 0; static int *mntcheck = NULL; struct statfs *sfs, *foundsfs; int i, count; if (mntsize <= 0) { if ((mntsize = mntinfo(&mntbuf)) <= 0) return (NULL); } if (mntcheck == NULL) { if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL) err(1, "calloc"); } /* * We want to get the file systems in the reverse order * that they were mounted. Unmounted file systems are marked * in a table called 'mntcheck'. */ count = 0; foundsfs = NULL; for (i = mntsize - 1; i >= 0; i--) { if (mntcheck[i]) continue; sfs = &mntbuf[i]; if (fromname != NULL && strcmp(sfs->f_mntfromname, fromname) != 0) continue; if (onname != NULL && strcmp(sfs->f_mntonname, onname) != 0) continue; if (fsid != NULL && fsidcmp(&sfs->f_fsid, fsid) != 0) continue; switch (what) { case CHECKUNIQUE: foundsfs = sfs; count++; continue; case REMOVE: mntcheck[i] = 1; break; default: break; } return (sfs); } if (what == CHECKUNIQUE && count == 1) return (foundsfs); return (NULL); } int sacmp(void *sa1, void *sa2) { void *p1, *p2; int len; if (((struct sockaddr *)sa1)->sa_family != ((struct sockaddr *)sa2)->sa_family) return (1); switch (((struct sockaddr *)sa1)->sa_family) { case AF_INET: p1 = &((struct sockaddr_in *)sa1)->sin_addr; p2 = &((struct sockaddr_in *)sa2)->sin_addr; len = 4; break; case AF_INET6: p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr; p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr; len = 16; if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != ((struct sockaddr_in6 *)sa2)->sin6_scope_id) return (1); break; default: return (1); } return memcmp(p1, p2, len); } int namematch(struct addrinfo *ai) { struct addrinfo *aip; if (nfshost == NULL || nfshost_ai == NULL) return (1); while (ai != NULL) { aip = nfshost_ai; while (aip != NULL) { if (sacmp(ai->ai_addr, aip->ai_addr) == 0) return (1); aip = aip->ai_next; } ai = ai->ai_next; } return (0); } struct statfs * checkmntlist(char *mntname) { struct statfs *sfs; fsid_t fsid; sfs = NULL; if (parsehexfsid(mntname, &fsid) == 0) sfs = getmntentry(NULL, NULL, &fsid, FIND); if (sfs == NULL) sfs = getmntentry(NULL, mntname, NULL, FIND); if (sfs == NULL) sfs = getmntentry(mntname, NULL, NULL, FIND); return (sfs); } size_t mntinfo(struct statfs **mntbuf) { static struct statfs *origbuf; size_t bufsize; int mntsize; mntsize = getfsstat(NULL, 0, MNT_NOWAIT); if (mntsize <= 0) return (0); bufsize = (mntsize + 1) * sizeof(struct statfs); if ((origbuf = malloc(bufsize)) == NULL) err(1, "malloc"); mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT); *mntbuf = origbuf; return (mntsize); } /* * Convert a hexadecimal filesystem ID to an fsid_t. * Returns 0 on success. */ int parsehexfsid(const char *hex, fsid_t *fsid) { char hexbuf[3]; int i; if (strlen(hex) != sizeof(*fsid) * 2) return (-1); hexbuf[2] = '\0'; for (i = 0; i < (int)sizeof(*fsid); i++) { hexbuf[0] = hex[i * 2]; hexbuf[1] = hex[i * 2 + 1]; if (!isxdigit(hexbuf[0]) || !isxdigit(hexbuf[1])) return (-1); ((u_char *)fsid)[i] = strtol(hexbuf, NULL, 16); } return (0); } /* * xdr routines for mount rpc's */ int xdr_dir(XDR *xdrsp, char *dirp) { return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); } void usage(void) { (void)fprintf(stderr, "%s\n%s\n", "usage: umount [-fNnv] special ... | node ... | fsid ...", " umount -a | -A [-F fstab] [-fnv] [-h host] [-t type]"); exit(1); } diff --git a/stand/libsa/powerpc/syncicache.c b/stand/libsa/powerpc/syncicache.c index 434dcec63416..0fcb1914a0da 100644 --- a/stand/libsa/powerpc/syncicache.c +++ b/stand/libsa/powerpc/syncicache.c @@ -1,103 +1,98 @@ /*- * Copyright (C) 1995-1997, 1999 Wolfgang Solfrank. * Copyright (C) 1995-1997, 1999 TooLs GmbH. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. * * $NetBSD: syncicache.c,v 1.2 1999/05/05 12:36:40 tsubai Exp $ */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #if defined(_KERNEL) || defined(_STANDALONE) #include #include #include #endif #include #include #include #ifdef _STANDALONE int cacheline_size = 32; #endif #if !defined(_KERNEL) && !defined(_STANDALONE) #include int cacheline_size = 0; static void getcachelinesize(void); static void getcachelinesize() { static int cachemib[] = { CTL_MACHDEP, CPU_CACHELINE }; int clen; clen = sizeof(cacheline_size); if (sysctl(cachemib, sizeof(cachemib) / sizeof(cachemib[0]), &cacheline_size, &clen, NULL, 0) < 0 || !cacheline_size) { abort(); } } #endif void __syncicache(void *from, int len) { int l, off; char *p; #if !defined(_KERNEL) && !defined(_STANDALONE) if (!cacheline_size) getcachelinesize(); #endif off = (u_int)from & (cacheline_size - 1); l = len += off; p = (char *)from - off; do { __asm __volatile ("dcbst 0,%0" :: "r"(p)); p += cacheline_size; } while ((l -= cacheline_size) > 0); __asm __volatile ("sync"); p = (char *)from - off; do { __asm __volatile ("icbi 0,%0" :: "r"(p)); p += cacheline_size; } while ((len -= cacheline_size) > 0); __asm __volatile ("sync; isync"); } diff --git a/sys/netpfil/ipfilter/netinet/fil.c b/sys/netpfil/ipfilter/netinet/fil.c index b04ec3496a65..76fde8622498 100644 --- a/sys/netpfil/ipfilter/netinet/fil.c +++ b/sys/netpfil/ipfilter/netinet/fil.c @@ -1,9959 +1,9958 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Copyright 2008 Sun Microsystems. * * $Id$ * */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #if defined(_KERNEL) && defined(__FreeBSD__) # if !defined(IPFILTER_LKM) # include "opt_inet6.h" # endif # include #else # include #endif #if defined(__SVR4) || defined(sun) /* SOLARIS */ # include #endif # include #if defined(_KERNEL) # include # include #else # include # include # include # include # include # define _KERNEL # include # undef _KERNEL #endif #if !defined(__SVR4) # include #else # include # if (SOLARIS2 < 5) && defined(sun) # include # endif #endif # include #include #include #ifdef sun # include #endif #include #include #include #include # include # include #include "netinet/ip_compat.h" #ifdef USE_INET6 # include # if !SOLARIS && defined(_KERNEL) # include # endif #endif #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_auth.h" #ifdef IPFILTER_SCAN # include "netinet/ip_scan.h" #endif #include "netinet/ip_sync.h" #include "netinet/ip_lookup.h" #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" #ifdef IPFILTER_COMPILED # include "netinet/ip_rules.h" #endif #if defined(IPFILTER_BPF) && defined(_KERNEL) # include #endif #if defined(__FreeBSD__) # include #endif #include "netinet/ipl.h" #if defined(__NetBSD__) && (__NetBSD_Version__ >= 104230000) # include extern struct callout ipf_slowtimer_ch; #endif /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: fil.c,v 2.243.2.125 2007/10/10 09:27:20 darrenr Exp $"; */ #endif #ifndef _KERNEL # include "ipf.h" # include "ipt.h" extern int opts; extern int blockreason; #endif /* _KERNEL */ #define FASTROUTE_RECURSION #define LBUMP(x) softc->x++ #define LBUMPD(x, y) do { softc->x.y++; DT(y); } while (0) static inline int ipf_check_ipf(fr_info_t *, frentry_t *, int); static u_32_t ipf_checkcipso(fr_info_t *, u_char *, int); static u_32_t ipf_checkripso(u_char *); static u_32_t ipf_decaps(fr_info_t *, u_32_t, int); #ifdef IPFILTER_LOG static frentry_t *ipf_dolog(fr_info_t *, u_32_t *); #endif static int ipf_flushlist(ipf_main_softc_t *, int *, frentry_t **); static int ipf_flush_groups(ipf_main_softc_t *, frgroup_t **, int); static ipfunc_t ipf_findfunc(ipfunc_t); static void *ipf_findlookup(ipf_main_softc_t *, int, frentry_t *, i6addr_t *, i6addr_t *); static frentry_t *ipf_firewall(fr_info_t *, u_32_t *); static int ipf_fr_matcharray(fr_info_t *, int *); static int ipf_frruleiter(ipf_main_softc_t *, void *, int, void *); static void ipf_funcfini(ipf_main_softc_t *, frentry_t *); static int ipf_funcinit(ipf_main_softc_t *, frentry_t *); static int ipf_geniter(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *); static void ipf_getstat(ipf_main_softc_t *, struct friostat *, int); static int ipf_group_flush(ipf_main_softc_t *, frgroup_t *); static void ipf_group_free(frgroup_t *); static int ipf_grpmapfini(struct ipf_main_softc_s *, frentry_t *); static int ipf_grpmapinit(struct ipf_main_softc_s *, frentry_t *); static frentry_t *ipf_nextrule(ipf_main_softc_t *, int, int, frentry_t *, int); static int ipf_portcheck(frpcmp_t *, u_32_t); static inline int ipf_pr_ah(fr_info_t *); static inline void ipf_pr_esp(fr_info_t *); static inline void ipf_pr_gre(fr_info_t *); static inline void ipf_pr_udp(fr_info_t *); static inline void ipf_pr_tcp(fr_info_t *); static inline void ipf_pr_icmp(fr_info_t *); static inline void ipf_pr_ipv4hdr(fr_info_t *); static inline void ipf_pr_short(fr_info_t *, int); static inline int ipf_pr_tcpcommon(fr_info_t *); static inline int ipf_pr_udpcommon(fr_info_t *); static void ipf_rule_delete(ipf_main_softc_t *, frentry_t *f, int, int); static void ipf_rule_expire_insert(ipf_main_softc_t *, frentry_t *, int); static int ipf_synclist(ipf_main_softc_t *, frentry_t *, void *); static void ipf_token_flush(ipf_main_softc_t *); static void ipf_token_unlink(ipf_main_softc_t *, ipftoken_t *); static ipftuneable_t *ipf_tune_findbyname(ipftuneable_t *, const char *); static ipftuneable_t *ipf_tune_findbycookie(ipftuneable_t **, void *, void **); static int ipf_updateipid(fr_info_t *); static int ipf_settimeout(struct ipf_main_softc_s *, struct ipftuneable *, ipftuneval_t *); #if !defined(_KERNEL) || SOLARIS static int ppsratecheck(struct timeval *, int *, int); #endif /* * bit values for identifying presence of individual IP options * All of these tables should be ordered by increasing key value on the left * hand side to allow for binary searching of the array and include a trailer * with a 0 for the bitmask for linear searches to easily find the end with. */ static const struct optlist ipopts[] = { { IPOPT_NOP, 0x000001 }, { IPOPT_RR, 0x000002 }, { IPOPT_ZSU, 0x000004 }, { IPOPT_MTUP, 0x000008 }, { IPOPT_MTUR, 0x000010 }, { IPOPT_ENCODE, 0x000020 }, { IPOPT_TS, 0x000040 }, { IPOPT_TR, 0x000080 }, { IPOPT_SECURITY, 0x000100 }, { IPOPT_LSRR, 0x000200 }, { IPOPT_E_SEC, 0x000400 }, { IPOPT_CIPSO, 0x000800 }, { IPOPT_SATID, 0x001000 }, { IPOPT_SSRR, 0x002000 }, { IPOPT_ADDEXT, 0x004000 }, { IPOPT_VISA, 0x008000 }, { IPOPT_IMITD, 0x010000 }, { IPOPT_EIP, 0x020000 }, { IPOPT_FINN, 0x040000 }, { 0, 0x000000 } }; #ifdef USE_INET6 static const struct optlist ip6exthdr[] = { { IPPROTO_HOPOPTS, 0x000001 }, { IPPROTO_IPV6, 0x000002 }, { IPPROTO_ROUTING, 0x000004 }, { IPPROTO_FRAGMENT, 0x000008 }, { IPPROTO_ESP, 0x000010 }, { IPPROTO_AH, 0x000020 }, { IPPROTO_NONE, 0x000040 }, { IPPROTO_DSTOPTS, 0x000080 }, { IPPROTO_MOBILITY, 0x000100 }, { 0, 0 } }; #endif /* * bit values for identifying presence of individual IP security options */ static const struct optlist secopt[] = { { IPSO_CLASS_RES4, 0x01 }, { IPSO_CLASS_TOPS, 0x02 }, { IPSO_CLASS_SECR, 0x04 }, { IPSO_CLASS_RES3, 0x08 }, { IPSO_CLASS_CONF, 0x10 }, { IPSO_CLASS_UNCL, 0x20 }, { IPSO_CLASS_RES2, 0x40 }, { IPSO_CLASS_RES1, 0x80 } }; char ipfilter_version[] = IPL_VERSION; int ipf_features = 0 #ifdef IPFILTER_LKM | IPF_FEAT_LKM #endif #ifdef IPFILTER_LOG | IPF_FEAT_LOG #endif | IPF_FEAT_LOOKUP #ifdef IPFILTER_BPF | IPF_FEAT_BPF #endif #ifdef IPFILTER_COMPILED | IPF_FEAT_COMPILED #endif #ifdef IPFILTER_CKSUM | IPF_FEAT_CKSUM #endif | IPF_FEAT_SYNC #ifdef IPFILTER_SCAN | IPF_FEAT_SCAN #endif #ifdef USE_INET6 | IPF_FEAT_IPV6 #endif ; /* * Table of functions available for use with call rules. */ static ipfunc_resolve_t ipf_availfuncs[] = { { "srcgrpmap", ipf_srcgrpmap, ipf_grpmapinit, ipf_grpmapfini }, { "dstgrpmap", ipf_dstgrpmap, ipf_grpmapinit, ipf_grpmapfini }, { "", NULL, NULL, NULL } }; static ipftuneable_t ipf_main_tuneables[] = { { { (void *)offsetof(struct ipf_main_softc_s, ipf_flags) }, "ipf_flags", 0, 0xffffffff, stsizeof(ipf_main_softc_t, ipf_flags), 0, NULL, NULL }, { { (void *)offsetof(struct ipf_main_softc_s, ipf_active) }, "active", 0, 0, stsizeof(ipf_main_softc_t, ipf_active), IPFT_RDONLY, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_control_forwarding) }, "control_forwarding", 0, 1, stsizeof(ipf_main_softc_t, ipf_control_forwarding), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_update_ipid) }, "update_ipid", 0, 1, stsizeof(ipf_main_softc_t, ipf_update_ipid), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_chksrc) }, "chksrc", 0, 1, stsizeof(ipf_main_softc_t, ipf_chksrc), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_minttl) }, "min_ttl", 0, 1, stsizeof(ipf_main_softc_t, ipf_minttl), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmpminfragmtu) }, "icmp_minfragmtu", 0, 1, stsizeof(ipf_main_softc_t, ipf_icmpminfragmtu), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_pass) }, "default_pass", 0, 0xffffffff, stsizeof(ipf_main_softc_t, ipf_pass), 0, NULL, NULL }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpidletimeout) }, "tcp_idle_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpidletimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosewait) }, "tcp_close_wait", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpclosewait), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcplastack) }, "tcp_last_ack", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcplastack), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimeout) }, "tcp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynsent) }, "tcp_syn_sent", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpsynsent), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpsynrecv) }, "tcp_syn_received", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpsynrecv), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcpclosed) }, "tcp_closed", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcpclosed), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcphalfclosed) }, "tcp_half_closed", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcphalfclosed), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_tcptimewait) }, "tcp_time_wait", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_tcptimewait), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_udptimeout) }, "udp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_udptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_udpacktimeout) }, "udp_ack_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_udpacktimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmptimeout) }, "icmp_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_icmptimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_icmpacktimeout) }, "icmp_ack_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_icmpacktimeout), 0, NULL, ipf_settimeout }, { { (void *)offsetof(ipf_main_softc_t, ipf_iptimeout) }, "ip_timeout", 1, 0x7fffffff, stsizeof(ipf_main_softc_t, ipf_iptimeout), 0, NULL, ipf_settimeout }, #if defined(INSTANCES) && defined(_KERNEL) { { (void *)offsetof(ipf_main_softc_t, ipf_get_loopback) }, "intercept_loopback", 0, 1, stsizeof(ipf_main_softc_t, ipf_get_loopback), 0, NULL, ipf_set_loopback }, #endif { { 0 }, NULL, 0, 0, 0, 0, NULL, NULL } }; /* * The next section of code is a collection of small routines that set * fields in the fr_info_t structure passed based on properties of the * current packet. There are different routines for the same protocol * for each of IPv4 and IPv6. Adding a new protocol, for which there * will "special" inspection for setup, is now more easily done by adding * a new routine and expanding the ipf_pr_ipinit*() function rather than by * adding more code to a growing switch statement. */ #ifdef USE_INET6 static inline int ipf_pr_ah6(fr_info_t *); static inline void ipf_pr_esp6(fr_info_t *); static inline void ipf_pr_gre6(fr_info_t *); static inline void ipf_pr_udp6(fr_info_t *); static inline void ipf_pr_tcp6(fr_info_t *); static inline void ipf_pr_icmp6(fr_info_t *); static inline void ipf_pr_ipv6hdr(fr_info_t *); static inline void ipf_pr_short6(fr_info_t *, int); static inline int ipf_pr_hopopts6(fr_info_t *); static inline int ipf_pr_mobility6(fr_info_t *); static inline int ipf_pr_routing6(fr_info_t *); static inline int ipf_pr_dstopts6(fr_info_t *); static inline int ipf_pr_fragment6(fr_info_t *); static inline struct ip6_ext *ipf_pr_ipv6exthdr(fr_info_t *, int, int); /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_short6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* xmin(I) - minimum header size */ /* */ /* IPv6 Only */ /* This is function enforces the 'is a packet too short to be legit' rule */ /* for IPv6 and marks the packet with FI_SHORT if so. See function comment */ /* for ipf_pr_short() for more details. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_short6(fr_info_t *fin, int xmin) { if (fin->fin_dlen < xmin) fin->fin_flx |= FI_SHORT; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv6hdr */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Copy values from the IPv6 header into the fr_info_t struct and call the */ /* per-protocol analyzer if it exists. In validating the packet, a protocol*/ /* analyzer may pullup or free the packet itself so we need to be vigiliant */ /* of that possibility arising. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_ipv6hdr(fr_info_t *fin) { ip6_t *ip6 = (ip6_t *)fin->fin_ip; int p, go = 1, i, hdrcount; fr_ip_t *fi = &fin->fin_fi; fin->fin_off = 0; fi->fi_tos = 0; fi->fi_optmsk = 0; fi->fi_secmsk = 0; fi->fi_auth = 0; p = ip6->ip6_nxt; fin->fin_crc = p; fi->fi_ttl = ip6->ip6_hlim; fi->fi_src.in6 = ip6->ip6_src; fin->fin_crc += fi->fi_src.i6[0]; fin->fin_crc += fi->fi_src.i6[1]; fin->fin_crc += fi->fi_src.i6[2]; fin->fin_crc += fi->fi_src.i6[3]; fi->fi_dst.in6 = ip6->ip6_dst; fin->fin_crc += fi->fi_dst.i6[0]; fin->fin_crc += fi->fi_dst.i6[1]; fin->fin_crc += fi->fi_dst.i6[2]; fin->fin_crc += fi->fi_dst.i6[3]; fin->fin_id = 0; if (IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) fin->fin_flx |= FI_MULTICAST|FI_MBCAST; hdrcount = 0; while (go && !(fin->fin_flx & FI_SHORT)) { switch (p) { case IPPROTO_UDP : ipf_pr_udp6(fin); go = 0; break; case IPPROTO_TCP : ipf_pr_tcp6(fin); go = 0; break; case IPPROTO_ICMPV6 : ipf_pr_icmp6(fin); go = 0; break; case IPPROTO_GRE : ipf_pr_gre6(fin); go = 0; break; case IPPROTO_HOPOPTS : p = ipf_pr_hopopts6(fin); break; case IPPROTO_MOBILITY : p = ipf_pr_mobility6(fin); break; case IPPROTO_DSTOPTS : p = ipf_pr_dstopts6(fin); break; case IPPROTO_ROUTING : p = ipf_pr_routing6(fin); break; case IPPROTO_AH : p = ipf_pr_ah6(fin); break; case IPPROTO_ESP : ipf_pr_esp6(fin); go = 0; break; case IPPROTO_IPV6 : for (i = 0; ip6exthdr[i].ol_bit != 0; i++) if (ip6exthdr[i].ol_val == p) { fin->fin_flx |= ip6exthdr[i].ol_bit; break; } go = 0; break; case IPPROTO_NONE : go = 0; break; case IPPROTO_FRAGMENT : p = ipf_pr_fragment6(fin); /* * Given that the only fragments we want to let through * (where fin_off != 0) are those where the non-first * fragments only have data, we can safely stop looking * at headers if this is a non-leading fragment. */ if (fin->fin_off != 0) go = 0; break; default : go = 0; break; } hdrcount++; /* * It is important to note that at this point, for the * extension headers (go != 0), the entire header may not have * been pulled up when the code gets to this point. This is * only done for "go != 0" because the other header handlers * will all pullup their complete header. The other indicator * of an incomplete packet is that this was just an extension * header. */ if ((go != 0) && (p != IPPROTO_NONE) && (ipf_pr_pullup(fin, 0) == -1)) { p = IPPROTO_NONE; break; } } /* * Some of the above functions, like ipf_pr_esp6(), can call ipf_pullup * and destroy whatever packet was here. The caller of this function * expects us to return if there is a problem with ipf_pullup. */ if (fin->fin_m == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_bad); return; } fi->fi_p = p; /* * IPv6 fragment case 1 - see comment for ipf_pr_fragment6(). * "go != 0" implies the above loop hasn't arrived at a layer 4 header. */ if ((go != 0) && (fin->fin_flx & FI_FRAG) && (fin->fin_off == 0)) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipv6_frag_1, fr_info_t *, fin, int, go); LBUMPD(ipf_stats[fin->fin_out], fr_v6_badfrag); LBUMP(ipf_stats[fin->fin_out].fr_v6_bad); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv6exthdr */ /* Returns: struct ip6_ext * - pointer to the start of the next header */ /* or NULL if there is a prolblem. */ /* Parameters: fin(I) - pointer to packet information */ /* multiple(I) - flag indicating yes/no if multiple occurances */ /* of this extension header are allowed. */ /* proto(I) - protocol number for this extension header */ /* */ /* IPv6 Only */ /* This function embodies a number of common checks that all IPv6 extension */ /* headers must be subjected to. For example, making sure the packet is */ /* big enough for it to be in, checking if it is repeated and setting a */ /* flag to indicate its presence. */ /* ------------------------------------------------------------------------ */ static inline struct ip6_ext * ipf_pr_ipv6exthdr(fr_info_t *fin, int multiple, int proto) { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_ext *hdr; u_short shift; int i; fin->fin_flx |= FI_V6EXTHDR; /* 8 is default length of extension hdr */ if ((fin->fin_dlen - 8) < 0) { fin->fin_flx |= FI_SHORT; LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_short); return (NULL); } if (ipf_pr_pullup(fin, 8) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_pullup); return (NULL); } hdr = fin->fin_dp; switch (proto) { case IPPROTO_FRAGMENT : shift = 8; break; default : shift = 8 + (hdr->ip6e_len << 3); break; } if (shift > fin->fin_dlen) { /* Nasty extension header length? */ fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_pr_ipv6exthdr_len, fr_info_t *, fin, u_short, shift, u_short, fin->fin_dlen); LBUMPD(ipf_stats[fin->fin_out], fr_v6_ext_hlen); return (NULL); } fin->fin_dp = (char *)fin->fin_dp + shift; fin->fin_dlen -= shift; /* * If we have seen a fragment header, do not set any flags to indicate * the presence of this extension header as it has no impact on the * end result until after it has been defragmented. */ if (fin->fin_flx & FI_FRAG) return (hdr); for (i = 0; ip6exthdr[i].ol_bit != 0; i++) if (ip6exthdr[i].ol_val == proto) { /* * Most IPv6 extension headers are only allowed once. */ if ((multiple == 0) && ((fin->fin_optmsk & ip6exthdr[i].ol_bit) != 0)) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipv6exthdr_once, fr_info_t *, fin, u_int, (fin->fin_optmsk & ip6exthdr[i].ol_bit)); } else fin->fin_optmsk |= ip6exthdr[i].ol_bit; break; } return (hdr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_hopopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending hop by hop options extension header */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_hopopts6(fr_info_t *fin) { struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); if (hdr == NULL) return (IPPROTO_NONE); return (hdr->ip6e_nxt); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_mobility6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks the IPv6 mobility extension header */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_mobility6(fr_info_t *fin) { struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_MOBILITY); if (hdr == NULL) return (IPPROTO_NONE); return (hdr->ip6e_nxt); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_routing6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending routing extension header */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_routing6(fr_info_t *fin) { struct ip6_routing *hdr; hdr = (struct ip6_routing *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_ROUTING); if (hdr == NULL) return (IPPROTO_NONE); switch (hdr->ip6r_type) { case 0 : /* * Nasty extension header length? */ if (((hdr->ip6r_len >> 1) < hdr->ip6r_segleft) || (hdr->ip6r_segleft && (hdr->ip6r_len & 1))) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_routing6, fr_info_t *, fin); LBUMPD(ipf_stats[fin->fin_out], fr_v6_rh_bad); return (IPPROTO_NONE); } break; default : break; } return (hdr->ip6r_nxt); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_fragment6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Examine the IPv6 fragment header and extract fragment offset information.*/ /* */ /* Fragments in IPv6 are extraordinarily difficult to deal with - much more */ /* so than in IPv4. There are 5 cases of fragments with IPv6 that all */ /* packets with a fragment header can fit into. They are as follows: */ /* */ /* 1. [IPv6][0-n EH][FH][0-n EH] (no L4HDR present) */ /* 2. [IPV6][0-n EH][FH][0-n EH][L4HDR part] (short) */ /* 3. [IPV6][0-n EH][FH][L4HDR part][0-n data] (short) */ /* 4. [IPV6][0-n EH][FH][0-n EH][L4HDR][0-n data] */ /* 5. [IPV6][0-n EH][FH][data] */ /* */ /* IPV6 = IPv6 header, FH = Fragment Header, */ /* 0-n EH = 0 or more extension headers, 0-n data = 0 or more bytes of data */ /* */ /* Packets that match 1, 2, 3 will be dropped as the only reasonable */ /* scenario in which they happen is in extreme circumstances that are most */ /* likely to be an indication of an attack rather than normal traffic. */ /* A type 3 packet may be sent by an attacked after a type 4 packet. There */ /* are two rules that can be used to guard against type 3 packets: L4 */ /* headers must always be in a packet that has the offset field set to 0 */ /* and no packet is allowed to overlay that where offset = 0. */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_fragment6(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_frag *frag; fin->fin_flx |= FI_FRAG; frag = (struct ip6_frag *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_FRAGMENT); if (frag == NULL) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_frag_bad); return (IPPROTO_NONE); } if ((frag->ip6f_offlg & IP6F_MORE_FRAG) != 0) { /* * Any fragment that isn't the last fragment must have its * length as a multiple of 8. */ if ((fin->fin_plen & 7) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_frag_not_8, fr_info_t *, fin, u_int, (fin->fin_plen & 7)); } } fin->fin_fraghdr = frag; fin->fin_id = frag->ip6f_ident; fin->fin_off = ntohs(frag->ip6f_offlg & IP6F_OFF_MASK); if (fin->fin_off != 0) fin->fin_flx |= FI_FRAGBODY; /* * Jumbograms aren't handled, so the max. length is 64k */ if ((fin->fin_off << 3) + fin->fin_dlen > 65535) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_jumbogram, fr_info_t *, fin, u_int, ((fin->fin_off << 3) + fin->fin_dlen)); } /* * We don't know where the transport layer header (or whatever is next * is), as it could be behind destination options (amongst others) so * return the fragment header as the type of packet this is. Note that * this effectively disables the fragment cache for > 1 protocol at a * time. */ return (frag->ip6f_nxt); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_dstopts6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This is function checks pending destination options extension header */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_dstopts6(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; struct ip6_ext *hdr; hdr = ipf_pr_ipv6exthdr(fin, 0, IPPROTO_DSTOPTS); if (hdr == NULL) { LBUMPD(ipf_stats[fin->fin_out], fr_v6_dst_bad); return (IPPROTO_NONE); } return (hdr->ip6e_nxt); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_icmp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* This routine is mainly concerned with determining the minimum valid size */ /* for an ICMPv6 packet. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_icmp6(fr_info_t *fin) { int minicmpsz = sizeof(struct icmp6_hdr); struct icmp6_hdr *icmp6; if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN - sizeof(ip6_t)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_icmp6_pullup); return; } if (fin->fin_dlen > 1) { ip6_t *ip6; icmp6 = fin->fin_dp; fin->fin_data[0] = *(u_short *)icmp6; if ((icmp6->icmp6_type & ICMP6_INFOMSG_MASK) != 0) fin->fin_flx |= FI_ICMPQUERY; switch (icmp6->icmp6_type) { case ICMP6_ECHO_REPLY : case ICMP6_ECHO_REQUEST : if (fin->fin_dlen >= 6) fin->fin_data[1] = icmp6->icmp6_id; minicmpsz = ICMP6ERR_MINPKTLEN - sizeof(ip6_t); break; case ICMP6_DST_UNREACH : case ICMP6_PACKET_TOO_BIG : case ICMP6_TIME_EXCEEDED : case ICMP6_PARAM_PROB : fin->fin_flx |= FI_ICMPERR; minicmpsz = ICMP6ERR_IPICMPHLEN - sizeof(ip6_t); if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) break; if (M_LEN(fin->fin_m) < fin->fin_plen) { if (ipf_coalesce(fin) != 1) return; } if (ipf_pr_pullup(fin, ICMP6ERR_MINPKTLEN) == -1) return; /* * If the destination of this packet doesn't match the * source of the original packet then this packet is * not correct. */ icmp6 = fin->fin_dp; ip6 = (ip6_t *)((char *)icmp6 + ICMPERR_ICMPHLEN); if (IP6_NEQ(&fin->fin_fi.fi_dst, (i6addr_t *)&ip6->ip6_src)) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_icmp6, fr_info_t *, fin); } break; default : break; } } ipf_pr_short6(fin, minicmpsz); if ((fin->fin_flx & (FI_SHORT|FI_BAD)) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_ICMPV6; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for IPv6/UDP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_udp6(fr_info_t *fin) { if (ipf_pr_udpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_UDP; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for IPv6/TCP properties. */ /* Is not expected to be called for fragmented packets. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_tcp6(fr_info_t *fin) { if (ipf_pr_tcpcommon(fin) == 0) { u_char p = fin->fin_p; fin->fin_p = IPPROTO_TCP; ipf_checkv6sum(fin); fin->fin_p = p; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_esp6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for ESP properties. */ /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ /* even though the newer ESP packets must also have a sequence number that */ /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_esp6(fr_info_t *fin) { if ((fin->fin_off == 0) && (ipf_pr_pullup(fin, 8) == -1)) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_esp_pullup); return; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ah6 */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv6 Only */ /* Analyse the packet for AH properties. */ /* The minimum length is taken to be the combination of all fields in the */ /* header being present and no authentication data (null algorithm used.) */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_ah6(fr_info_t *fin) { authhdr_t *ah; fin->fin_flx |= FI_AH; ah = (authhdr_t *)ipf_pr_ipv6exthdr(fin, 0, IPPROTO_HOPOPTS); if (ah == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_ah_bad); return (IPPROTO_NONE); } ipf_pr_short6(fin, sizeof(*ah)); /* * No need for another pullup, ipf_pr_ipv6exthdr() will pullup * enough data to satisfy ah_next (the very first one.) */ return (ah->ah_next); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_gre6 */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_gre6(fr_info_t *fin) { grehdr_t *gre; if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v6_gre_pullup); return; } gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) fin->fin_data[0] = gre->gr_call; } #endif /* USE_INET6 */ /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_pullup */ /* Returns: int - 0 == pullup succeeded, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* plen(I) - length (excluding L3 header) to pullup */ /* */ /* Short inline function to cut down on code duplication to perform a call */ /* to ipf_pullup to ensure there is the required amount of data, */ /* consecutively in the packet buffer. */ /* */ /* This function pulls up 'extra' data at the location of fin_dp. fin_dp */ /* points to the first byte after the complete layer 3 header, which will */ /* include all of the known extension headers for IPv6 or options for IPv4. */ /* */ /* Since fr_pullup() expects the total length of bytes to be pulled up, it */ /* is necessary to add those we can already assume to be pulled up (fin_dp */ /* - fin_ip) to what is passed through. */ /* ------------------------------------------------------------------------ */ int ipf_pr_pullup(fr_info_t *fin, int plen) { ipf_main_softc_t *softc = fin->fin_main_soft; if (fin->fin_m != NULL) { if (fin->fin_dp != NULL) plen += (char *)fin->fin_dp - ((char *)fin->fin_ip + fin->fin_hlen); plen += fin->fin_hlen; if (M_LEN(fin->fin_m) < plen + fin->fin_ipoff) { #if defined(_KERNEL) if (ipf_pullup(fin->fin_m, fin, plen) == NULL) { DT1(ipf_pullup_fail, fr_info_t *, fin); LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); fin->fin_reason = FRB_PULLUP; fin->fin_flx |= FI_BAD; return (-1); } LBUMP(ipf_stats[fin->fin_out].fr_pull[0]); #else LBUMP(ipf_stats[fin->fin_out].fr_pull[1]); /* * Fake ipf_pullup failing */ fin->fin_reason = FRB_PULLUP; *fin->fin_mp = NULL; fin->fin_m = NULL; fin->fin_ip = NULL; fin->fin_flx |= FI_BAD; return (-1); #endif } } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_short */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* xmin(I) - minimum header size */ /* */ /* Check if a packet is "short" as defined by xmin. The rule we are */ /* applying here is that the packet must not be fragmented within the layer */ /* 4 header. That is, it must not be a fragment that has its offset set to */ /* start within the layer 4 header (hdrmin) or if it is at offset 0, the */ /* entire layer 4 header must be present (min). */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_short(fr_info_t *fin, int xmin) { if (fin->fin_off == 0) { if (fin->fin_dlen < xmin) fin->fin_flx |= FI_SHORT; } else if (fin->fin_off < xmin) { fin->fin_flx |= FI_SHORT; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_icmp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Do a sanity check on the packet for ICMP (v4). In nearly all cases, */ /* except extrememly bad packets, both type and code will be present. */ /* The expected minimum size of an ICMP packet is very much dependent on */ /* the type of it. */ /* */ /* XXX - other ICMP sanity checks? */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_icmp(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; int minicmpsz = sizeof(struct icmp); icmphdr_t *icmp; ip_t *oip; ipf_pr_short(fin, ICMPERR_ICMPHLEN); if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_frag); return; } if (ipf_pr_pullup(fin, ICMPERR_ICMPHLEN) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_icmp_pullup); return; } icmp = fin->fin_dp; fin->fin_data[0] = *(u_short *)icmp; fin->fin_data[1] = icmp->icmp_id; switch (icmp->icmp_type) { case ICMP_ECHOREPLY : case ICMP_ECHO : /* Router discovery messaes - RFC 1256 */ case ICMP_ROUTERADVERT : case ICMP_ROUTERSOLICIT : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = ICMP_MINLEN; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + * 3 * timestamp(3 * 4) */ case ICMP_TSTAMP : case ICMP_TSTAMPREPLY : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = 20; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + * mask(4) */ case ICMP_IREQ : case ICMP_IREQREPLY : case ICMP_MASKREQ : case ICMP_MASKREPLY : fin->fin_flx |= FI_ICMPQUERY; minicmpsz = 12; break; /* * type(1) + code(1) + cksum(2) + id(2) seq(2) + ip(20+) */ case ICMP_UNREACH : #ifdef icmp_nextmtu if (icmp->icmp_code == ICMP_UNREACH_NEEDFRAG) { if (icmp->icmp_nextmtu < softc->ipf_icmpminfragmtu) { fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_icmp_nextmtu, fr_info_t *, fin, u_int, icmp->icmp_nextmtu, u_int, softc->ipf_icmpminfragmtu); } } #endif /* FALLTHROUGH */ case ICMP_SOURCEQUENCH : case ICMP_REDIRECT : case ICMP_TIMXCEED : case ICMP_PARAMPROB : fin->fin_flx |= FI_ICMPERR; if (ipf_coalesce(fin) != 1) { LBUMPD(ipf_stats[fin->fin_out], fr_icmp_coalesce); return; } /* * ICMP error packets should not be generated for IP * packets that are a fragment that isn't the first * fragment. */ oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); if ((ntohs(oip->ip_off) & IP_OFFMASK) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_icmp_err, fr_info_t, fin, u_int, (ntohs(oip->ip_off) & IP_OFFMASK)); } /* * If the destination of this packet doesn't match the * source of the original packet then this packet is * not correct. */ if (oip->ip_src.s_addr != fin->fin_daddr) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_src_ne_dst, fr_info_t *, fin); } break; default : break; } ipf_pr_short(fin, minicmpsz); ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet, -1 = buffer error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* TCP header sanity checking. Look for bad combinations of TCP flags, */ /* and make some checks with how they interact with other fields. */ /* If compiled with IPFILTER_CKSUM, check to see if the TCP checksum is */ /* valid and mark the packet as bad if not. */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_tcpcommon(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; int flags, tlen; tcphdr_t *tcp; fin->fin_flx |= FI_TCPUDP; if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_frag); return (0); } if (ipf_pr_pullup(fin, sizeof(*tcp)) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return (-1); } tcp = fin->fin_dp; if (fin->fin_dlen > 3) { fin->fin_sport = ntohs(tcp->th_sport); fin->fin_dport = ntohs(tcp->th_dport); } if ((fin->fin_flx & FI_SHORT) != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_short); return (1); } /* * Use of the TCP data offset *must* result in a value that is at * least the same size as the TCP header. */ tlen = TCP_OFF(tcp) << 2; if (tlen < sizeof(tcphdr_t)) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_small); fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_tlen, fr_info_t, fin, u_int, tlen, u_int, sizeof(tcphdr_t)); return (1); } flags = tcp->th_flags; fin->fin_tcpf = tcp->th_flags; /* * If the urgent flag is set, then the urgent pointer must * also be set and vice versa. Good TCP packets do not have * just one of these set. */ if ((flags & TH_URG) != 0 && (tcp->th_urp == 0)) { fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_th_urg, fr_info_t*, fin, u_int, (flags & TH_URG), u_int, tcp->th_urp); #if 0 } else if ((flags & TH_URG) == 0 && (tcp->th_urp != 0)) { /* * Ignore this case (#if 0) as it shows up in "real" * traffic with bogus values in the urgent pointer field. */ fin->fin_flx |= FI_BAD; DT3(ipf_fi_bad_th_urg0, fr_info_t *, fin, u_int, (flags & TH_URG), u_int, tcp->th_urp); #endif } else if (((flags & (TH_SYN|TH_FIN)) != 0) && ((flags & (TH_RST|TH_ACK)) == TH_RST)) { /* TH_FIN|TH_RST|TH_ACK seems to appear "naturally" */ fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_fin_rst_ack, fr_info_t, fin); #if 1 } else if (((flags & TH_SYN) != 0) && ((flags & (TH_URG|TH_PUSH)) != 0)) { /* * SYN with URG and PUSH set is not for normal TCP but it is * possible(?) with T/TCP...but who uses T/TCP? */ fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_syn_urg_psh, fr_info_t *, fin); #endif } else if (!(flags & TH_ACK)) { /* * If the ack bit isn't set, then either the SYN or * RST bit must be set. If the SYN bit is set, then * we expect the ACK field to be 0. If the ACK is * not set and if URG, PSH or FIN are set, consdier * that to indicate a bad TCP packet. */ if ((flags == TH_SYN) && (tcp->th_ack != 0)) { /* * Cisco PIX sets the ACK field to a random value. * In light of this, do not set FI_BAD until a patch * is available from Cisco to ensure that * interoperability between existing systems is * achieved. */ /*fin->fin_flx |= FI_BAD*/; /*DT1(ipf_fi_bad_th_syn_ack, fr_info_t *, fin);*/ } else if (!(flags & (TH_RST|TH_SYN))) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_rst_syn, fr_info_t *, fin); } else if ((flags & (TH_URG|TH_PUSH|TH_FIN)) != 0) { fin->fin_flx |= FI_BAD; DT1(ipf_fi_bad_th_urg_push_fin, fr_info_t *, fin); } } if (fin->fin_flx & FI_BAD) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_bad_flags); return (1); } /* * At this point, it's not exactly clear what is to be gained by * marking up which TCP options are and are not present. The one we * are most interested in is the TCP window scale. This is only in * a SYN packet [RFC1323] so we don't need this here...? * Now if we were to analyse the header for passive fingerprinting, * then that might add some weight to adding this... */ if (tlen == sizeof(tcphdr_t)) { return (0); } if (ipf_pr_pullup(fin, tlen) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_tcp_pullup); return (-1); } #if 0 tcp = fin->fin_dp; ip = fin->fin_ip; s = (u_char *)(tcp + 1); off = IP_HL(ip) << 2; # ifdef _KERNEL if (fin->fin_mp != NULL) { mb_t *m = *fin->fin_mp; if (off + tlen > M_LEN(m)) return; } # endif for (tlen -= (int)sizeof(*tcp); tlen > 0; ) { opt = *s; if (opt == '\0') break; else if (opt == TCPOPT_NOP) ol = 1; else { if (tlen < 2) break; ol = (int)*(s + 1); if (ol < 2 || ol > tlen) break; } for (i = 9, mv = 4; mv >= 0; ) { op = ipopts + i; if (opt == (u_char)op->ol_val) { optmsk |= op->ol_bit; break; } } tlen -= ol; s += ol; } #endif /* 0 */ return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udpcommon */ /* Returns: int - 0 = header ok, 1 = bad packet */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Extract the UDP source and destination ports, if present. If compiled */ /* with IPFILTER_CKSUM, check to see if the UDP checksum is valid. */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_udpcommon(fr_info_t *fin) { udphdr_t *udp; fin->fin_flx |= FI_TCPUDP; if (!fin->fin_off && (fin->fin_dlen > 3)) { if (ipf_pr_pullup(fin, sizeof(*udp)) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; fin->fin_flx |= FI_SHORT; LBUMPD(ipf_stats[fin->fin_out], fr_udp_pullup); return (1); } udp = fin->fin_dp; fin->fin_sport = ntohs(udp->uh_sport); fin->fin_dport = ntohs(udp->uh_dport); } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_tcp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyse the packet for IPv4/TCP properties. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_tcp(fr_info_t *fin) { ipf_pr_short(fin, sizeof(tcphdr_t)); if (ipf_pr_tcpcommon(fin) == 0) ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_udp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyse the packet for IPv4/UDP properties. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_udp(fr_info_t *fin) { ipf_pr_short(fin, sizeof(udphdr_t)); if (ipf_pr_udpcommon(fin) == 0) ipf_checkv4sum(fin); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_esp */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for ESP properties. */ /* The minimum length is taken to be the SPI (32bits) plus a tail (32bits) */ /* even though the newer ESP packets must also have a sequence number that */ /* is 32bits as well, it is not possible(?) to determine the version from a */ /* simple packet header. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_esp(fr_info_t *fin) { if (fin->fin_off == 0) { ipf_pr_short(fin, 8); if (ipf_pr_pullup(fin, 8) == -1) { ipf_main_softc_t *softc = fin->fin_main_soft; LBUMPD(ipf_stats[fin->fin_out], fr_v4_esp_pullup); } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ah */ /* Returns: int - value of the next header or IPPROTO_NONE if error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for AH properties. */ /* The minimum length is taken to be the combination of all fields in the */ /* header being present and no authentication data (null algorithm used.) */ /* ------------------------------------------------------------------------ */ static inline int ipf_pr_ah(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; authhdr_t *ah; int len; fin->fin_flx |= FI_AH; ipf_pr_short(fin, sizeof(*ah)); if (((fin->fin_flx & FI_SHORT) != 0) || (fin->fin_off != 0)) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_ah_bad); return (IPPROTO_NONE); } if (ipf_pr_pullup(fin, sizeof(*ah)) == -1) { DT(fr_v4_ah_pullup_1); LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); return (IPPROTO_NONE); } ah = (authhdr_t *)fin->fin_dp; len = (ah->ah_plen + 2) << 2; ipf_pr_short(fin, len); if (ipf_pr_pullup(fin, len) == -1) { DT(fr_v4_ah_pullup_2); LBUMP(ipf_stats[fin->fin_out].fr_v4_ah_pullup); return (IPPROTO_NONE); } /* * Adjust fin_dp and fin_dlen for skipping over the authentication * header. */ fin->fin_dp = (char *)fin->fin_dp + len; fin->fin_dlen -= len; return (ah->ah_next); } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_gre */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Analyse the packet for GRE properties. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_gre(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; grehdr_t *gre; ipf_pr_short(fin, sizeof(grehdr_t)); if (fin->fin_off != 0) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_frag); return; } if (ipf_pr_pullup(fin, sizeof(grehdr_t)) == -1) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_gre_pullup); return; } gre = fin->fin_dp; if (GRE_REV(gre->gr_flags) == 1) fin->fin_data[0] = gre->gr_call; } /* ------------------------------------------------------------------------ */ /* Function: ipf_pr_ipv4hdr */ /* Returns: void */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* IPv4 Only */ /* Analyze the IPv4 header and set fields in the fr_info_t structure. */ /* Check all options present and flag their presence if any exist. */ /* ------------------------------------------------------------------------ */ static inline void ipf_pr_ipv4hdr(fr_info_t *fin) { u_short optmsk = 0, secmsk = 0, auth = 0; int hlen, ol, mv, p, i; const struct optlist *op; u_char *s, opt; u_short off; fr_ip_t *fi; ip_t *ip; fi = &fin->fin_fi; hlen = fin->fin_hlen; ip = fin->fin_ip; p = ip->ip_p; fi->fi_p = p; fin->fin_crc = p; fi->fi_tos = ip->ip_tos; fin->fin_id = ntohs(ip->ip_id); off = ntohs(ip->ip_off); /* Get both TTL and protocol */ fi->fi_p = ip->ip_p; fi->fi_ttl = ip->ip_ttl; /* Zero out bits not used in IPv6 address */ fi->fi_src.i6[1] = 0; fi->fi_src.i6[2] = 0; fi->fi_src.i6[3] = 0; fi->fi_dst.i6[1] = 0; fi->fi_dst.i6[2] = 0; fi->fi_dst.i6[3] = 0; fi->fi_saddr = ip->ip_src.s_addr; fin->fin_crc += fi->fi_saddr; fi->fi_daddr = ip->ip_dst.s_addr; fin->fin_crc += fi->fi_daddr; if (IN_MULTICAST(ntohl(fi->fi_daddr))) fin->fin_flx |= FI_MULTICAST|FI_MBCAST; /* * set packet attribute flags based on the offset and * calculate the byte offset that it represents. */ off &= IP_MF|IP_OFFMASK; if (off != 0) { int morefrag = off & IP_MF; fi->fi_flx |= FI_FRAG; off &= IP_OFFMASK; if (off == 1 && p == IPPROTO_TCP) { fin->fin_flx |= FI_SHORT; /* RFC 3128 */ DT1(ipf_fi_tcp_frag_off_1, fr_info_t *, fin); } if (off != 0) { fin->fin_flx |= FI_FRAGBODY; off <<= 3; if ((off + fin->fin_dlen > 65535) || (fin->fin_dlen == 0) || ((morefrag != 0) && ((fin->fin_dlen & 7) != 0))) { /* * The length of the packet, starting at its * offset cannot exceed 65535 (0xffff) as the * length of an IP packet is only 16 bits. * * Any fragment that isn't the last fragment * must have a length greater than 0 and it * must be an even multiple of 8. */ fi->fi_flx |= FI_BAD; DT1(ipf_fi_bad_fragbody_gt_65535, fr_info_t *, fin); } } } fin->fin_off = off; /* * Call per-protocol setup and checking */ if (p == IPPROTO_AH) { /* * Treat AH differently because we expect there to be another * layer 4 header after it. */ p = ipf_pr_ah(fin); } switch (p) { case IPPROTO_UDP : ipf_pr_udp(fin); break; case IPPROTO_TCP : ipf_pr_tcp(fin); break; case IPPROTO_ICMP : ipf_pr_icmp(fin); break; case IPPROTO_ESP : ipf_pr_esp(fin); break; case IPPROTO_GRE : ipf_pr_gre(fin); break; } ip = fin->fin_ip; if (ip == NULL) return; /* * If it is a standard IP header (no options), set the flag fields * which relate to options to 0. */ if (hlen == sizeof(*ip)) { fi->fi_optmsk = 0; fi->fi_secmsk = 0; fi->fi_auth = 0; return; } /* * So the IP header has some IP options attached. Walk the entire * list of options present with this packet and set flags to indicate * which ones are here and which ones are not. For the somewhat out * of date and obscure security classification options, set a flag to * represent which classification is present. */ fi->fi_flx |= FI_OPTIONS; for (s = (u_char *)(ip + 1), hlen -= (int)sizeof(*ip); hlen > 0; ) { opt = *s; if (opt == '\0') break; else if (opt == IPOPT_NOP) ol = 1; else { if (hlen < 2) break; ol = (int)*(s + 1); if (ol < 2 || ol > hlen) break; } for (i = 9, mv = 4; mv >= 0; ) { op = ipopts + i; if ((opt == (u_char)op->ol_val) && (ol > 4)) { u_32_t doi; switch (opt) { case IPOPT_SECURITY : if (optmsk & op->ol_bit) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipopt_security, fr_info_t *, fin, u_short, (optmsk & op->ol_bit)); } else { doi = ipf_checkripso(s); secmsk = doi >> 16; auth = doi & 0xffff; } break; case IPOPT_CIPSO : if (optmsk & op->ol_bit) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_ipopt_cipso, fr_info_t *, fin, u_short, (optmsk & op->ol_bit)); } else { doi = ipf_checkcipso(fin, s, ol); secmsk = doi >> 16; auth = doi & 0xffff; } break; } optmsk |= op->ol_bit; } if (opt < op->ol_val) i -= mv; else i += mv; mv--; } hlen -= ol; s += ol; } /* * */ if (auth && !(auth & 0x0100)) auth &= 0xff00; fi->fi_optmsk = optmsk; fi->fi_secmsk = secmsk; fi->fi_auth = auth; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkripso */ /* Returns: void */ /* Parameters: s(I) - pointer to start of RIPSO option */ /* */ /* ------------------------------------------------------------------------ */ static u_32_t ipf_checkripso(u_char *s) { const struct optlist *sp; u_short secmsk = 0, auth = 0; u_char sec; int j, m; sec = *(s + 2); /* classification */ for (j = 3, m = 2; m >= 0; ) { sp = secopt + j; if (sec == sp->ol_val) { secmsk |= sp->ol_bit; auth = *(s + 3); auth *= 256; auth += *(s + 4); break; } if (sec < sp->ol_val) j -= m; else j += m; m--; } return (secmsk << 16) | auth; } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkcipso */ /* Returns: u_32_t - 0 = failure, else the doi from the header */ /* Parameters: fin(IO) - pointer to packet information */ /* s(I) - pointer to start of CIPSO option */ /* ol(I) - length of CIPSO option field */ /* */ /* This function returns the domain of integrity (DOI) field from the CIPSO */ /* header and returns that whilst also storing the highest sensitivity */ /* value found in the fr_info_t structure. */ /* */ /* No attempt is made to extract the category bitmaps as these are defined */ /* by the user (rather than the protocol) and can be rather numerous on the */ /* end nodes. */ /* ------------------------------------------------------------------------ */ static u_32_t ipf_checkcipso(fr_info_t *fin, u_char *s, int ol) { ipf_main_softc_t *softc = fin->fin_main_soft; fr_ip_t *fi; u_32_t doi; u_char *t, tag, tlen, sensitivity; int len; if (ol < 6 || ol > 40) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_bad); fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_ol, fr_info_t *, fin, u_int, ol); return (0); } fi = &fin->fin_fi; fi->fi_sensitivity = 0; /* * The DOI field MUST be there. */ bcopy(s + 2, &doi, sizeof(doi)); t = (u_char *)s + 6; for (len = ol - 6; len >= 2; len -= tlen, t+= tlen) { tag = *t; tlen = *(t + 1); if (tlen > len || tlen < 4 || tlen > 34) { LBUMPD(ipf_stats[fin->fin_out], fr_v4_cipso_tlen); fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tlen, fr_info_t *, fin, u_int, tlen); return (0); } sensitivity = 0; /* * Tag numbers 0, 1, 2, 5 are laid out in the CIPSO Internet * draft (16 July 1992) that has expired. */ if (tag == 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag, fr_info_t *, fin, u_int, tag); continue; } else if (tag == 1) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag1_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Category bitmap for categories 0-239 */ } else if (tag == 4) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag4_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Enumerated categories, 16bits each, upto 15 */ } else if (tag == 5) { if (*(t + 2) != 0) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag5_t2, fr_info_t *, fin, u_int, (*t + 2)); continue; } sensitivity = *(t + 3); /* Range of categories (2*16bits), up to 7 pairs */ } else if (tag > 127) { /* Custom defined DOI */ ; } else { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkcipso_tag127, fr_info_t *, fin, u_int, tag); continue; } if (sensitivity > fi->fi_sensitivity) fi->fi_sensitivity = sensitivity; } return (doi); } /* ------------------------------------------------------------------------ */ /* Function: ipf_makefrip */ /* Returns: int - 0 == packet ok, -1 == packet freed */ /* Parameters: hlen(I) - length of IP packet header */ /* ip(I) - pointer to the IP header */ /* fin(IO) - pointer to packet information */ /* */ /* Compact the IP header into a structure which contains just the info. */ /* which is useful for comparing IP headers with and store this information */ /* in the fr_info_t structure pointer to by fin. At present, it is assumed */ /* this function will be called with either an IPv4 or IPv6 packet. */ /* ------------------------------------------------------------------------ */ int ipf_makefrip(int hlen, ip_t *ip, fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; int v; fin->fin_depth = 0; fin->fin_hlen = (u_short)hlen; fin->fin_ip = ip; fin->fin_rule = 0xffffffff; fin->fin_group[0] = -1; fin->fin_group[1] = '\0'; fin->fin_dp = (char *)ip + hlen; v = fin->fin_v; if (v == 4) { fin->fin_plen = ntohs(ip->ip_len); fin->fin_dlen = fin->fin_plen - hlen; ipf_pr_ipv4hdr(fin); #ifdef USE_INET6 } else if (v == 6) { fin->fin_plen = ntohs(((ip6_t *)ip)->ip6_plen); fin->fin_dlen = fin->fin_plen; fin->fin_plen += hlen; ipf_pr_ipv6hdr(fin); #endif } if (fin->fin_ip == NULL) { LBUMP(ipf_stats[fin->fin_out].fr_ip_freed); return (-1); } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_portcheck */ /* Returns: int - 1 == port matched, 0 == port match failed */ /* Parameters: frp(I) - pointer to port check `expression' */ /* pop(I) - port number to evaluate */ /* */ /* Perform a comparison of a port number against some other(s), using a */ /* structure with compare information stored in it. */ /* ------------------------------------------------------------------------ */ static inline int ipf_portcheck(frpcmp_t *frp, u_32_t pop) { int err = 1; u_32_t po; po = frp->frp_port; /* * Do opposite test to that required and continue if that succeeds. */ switch (frp->frp_cmp) { case FR_EQUAL : if (pop != po) /* EQUAL */ err = 0; break; case FR_NEQUAL : if (pop == po) /* NOTEQUAL */ err = 0; break; case FR_LESST : if (pop >= po) /* LESSTHAN */ err = 0; break; case FR_GREATERT : if (pop <= po) /* GREATERTHAN */ err = 0; break; case FR_LESSTE : if (pop > po) /* LT or EQ */ err = 0; break; case FR_GREATERTE : if (pop < po) /* GT or EQ */ err = 0; break; case FR_OUTRANGE : if (pop >= po && pop <= frp->frp_top) /* Out of range */ err = 0; break; case FR_INRANGE : if (pop <= po || pop >= frp->frp_top) /* In range */ err = 0; break; case FR_INCRANGE : if (pop < po || pop > frp->frp_top) /* Inclusive range */ err = 0; break; default : break; } return (err); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tcpudpchk */ /* Returns: int - 1 == protocol matched, 0 == check failed */ /* Parameters: fda(I) - pointer to packet information */ /* ft(I) - pointer to structure with comparison data */ /* */ /* Compares the current pcket (assuming it is TCP/UDP) information with a */ /* structure containing information that we want to match against. */ /* ------------------------------------------------------------------------ */ int ipf_tcpudpchk(fr_ip_t *fi, frtuc_t *ft) { int err = 1; /* * Both ports should *always* be in the first fragment. * So far, I cannot find any cases where they can not be. * * compare destination ports */ if (ft->ftu_dcmp) err = ipf_portcheck(&ft->ftu_dst, fi->fi_ports[1]); /* * compare source ports */ if (err && ft->ftu_scmp) err = ipf_portcheck(&ft->ftu_src, fi->fi_ports[0]); /* * If we don't have all the TCP/UDP header, then how can we * expect to do any sort of match on it ? If we were looking for * TCP flags, then NO match. If not, then match (which should * satisfy the "short" class too). */ if (err && (fi->fi_p == IPPROTO_TCP)) { if (fi->fi_flx & FI_SHORT) return (!(ft->ftu_tcpf | ft->ftu_tcpfm)); /* * Match the flags ? If not, abort this match. */ if (ft->ftu_tcpfm && ft->ftu_tcpf != (fi->fi_tcpf & ft->ftu_tcpfm)) { FR_DEBUG(("f. %#x & %#x != %#x\n", fi->fi_tcpf, ft->ftu_tcpfm, ft->ftu_tcpf)); err = 0; } } return (err); } /* ------------------------------------------------------------------------ */ /* Function: ipf_check_ipf */ /* Returns: int - 0 == match, else no match */ /* Parameters: fin(I) - pointer to packet information */ /* fr(I) - pointer to filter rule */ /* portcmp(I) - flag indicating whether to attempt matching on */ /* TCP/UDP port data. */ /* */ /* Check to see if a packet matches an IPFilter rule. Checks of addresses, */ /* port numbers, etc, for "standard" IPFilter rules are all orchestrated in */ /* this function. */ /* ------------------------------------------------------------------------ */ static inline int ipf_check_ipf(fr_info_t *fin, frentry_t *fr, int portcmp) { u_32_t *ld, *lm, *lip; fripf_t *fri; fr_ip_t *fi; int i; fi = &fin->fin_fi; fri = fr->fr_ipf; lip = (u_32_t *)fi; lm = (u_32_t *)&fri->fri_mip; ld = (u_32_t *)&fri->fri_ip; /* * first 32 bits to check coversion: * IP version, TOS, TTL, protocol */ i = ((*lip & *lm) != *ld); FR_DEBUG(("0. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (i) return (1); /* * Next 32 bits is a constructed bitmask indicating which IP options * are present (if any) in this packet. */ lip++, lm++, ld++; i = ((*lip & *lm) != *ld); FR_DEBUG(("1. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (i != 0) return (1); lip++, lm++, ld++; /* * Unrolled loops (4 each, for 32 bits) for address checks. */ /* * Check the source address. */ if (fr->fr_satype == FRI_LOOKUP) { i = (*fr->fr_srcfunc)(fin->fin_main_soft, fr->fr_srcptr, fi->fi_v, lip, fin->fin_plen); if (i == -1) return (1); lip += 3; lm += 3; ld += 3; } else { i = ((*lip & *lm) != *ld); FR_DEBUG(("2a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2b. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2c. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("2d. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); } else { lip += 3; lm += 3; ld += 3; } } i ^= (fr->fr_flags & FR_NOTSRCIP) >> 6; if (i != 0) return (1); /* * Check the destination address. */ lip++, lm++, ld++; if (fr->fr_datype == FRI_LOOKUP) { i = (*fr->fr_dstfunc)(fin->fin_main_soft, fr->fr_dstptr, fi->fi_v, lip, fin->fin_plen); if (i == -1) return (1); lip += 3; lm += 3; ld += 3; } else { i = ((*lip & *lm) != *ld); FR_DEBUG(("3a. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); if (fi->fi_v == 6) { lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3b. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3c. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); lip++, lm++, ld++; i |= ((*lip & *lm) != *ld); FR_DEBUG(("3d. %#08x & %#08x != %#08x\n", ntohl(*lip), ntohl(*lm), ntohl(*ld))); } else { lip += 3; lm += 3; ld += 3; } } i ^= (fr->fr_flags & FR_NOTDSTIP) >> 7; if (i != 0) return (1); /* * IP addresses matched. The next 32bits contains: * mast of old IP header security & authentication bits. */ lip++, lm++, ld++; i = (*ld - (*lip & *lm)); FR_DEBUG(("4. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); /* * Next we have 32 bits of packet flags. */ lip++, lm++, ld++; i |= (*ld - (*lip & *lm)); FR_DEBUG(("5. %#08x & %#08x != %#08x\n", *lip, *lm, *ld)); if (i == 0) { /* * If a fragment, then only the first has what we're * looking for here... */ if (portcmp) { if (!ipf_tcpudpchk(&fin->fin_fi, &fr->fr_tuc)) i = 1; } else { if (fr->fr_dcmp || fr->fr_scmp || fr->fr_tcpf || fr->fr_tcpfm) i = 1; if (fr->fr_icmpm || fr->fr_icmp) { if (((fi->fi_p != IPPROTO_ICMP) && (fi->fi_p != IPPROTO_ICMPV6)) || fin->fin_off || (fin->fin_dlen < 2)) i = 1; else if ((fin->fin_data[0] & fr->fr_icmpm) != fr->fr_icmp) { FR_DEBUG(("i. %#x & %#x != %#x\n", fin->fin_data[0], fr->fr_icmpm, fr->fr_icmp)); i = 1; } } } } return (i); } /* ------------------------------------------------------------------------ */ /* Function: ipf_scanlist */ /* Returns: int - result flags of scanning filter list */ /* Parameters: fin(I) - pointer to packet information */ /* pass(I) - default result to return for filtering */ /* */ /* Check the input/output list of rules for a match to the current packet. */ /* If a match is found, the value of fr_flags from the rule becomes the */ /* return value and fin->fin_fr points to the matched rule. */ /* */ /* This function may be called recursively upto 16 times (limit inbuilt.) */ /* When unwinding, it should finish up with fin_depth as 0. */ /* */ /* Could be per interface, but this gets real nasty when you don't have, */ /* or can't easily change, the kernel source code to . */ /* ------------------------------------------------------------------------ */ int ipf_scanlist(fr_info_t *fin, u_32_t pass) { ipf_main_softc_t *softc = fin->fin_main_soft; int rulen, portcmp, off, skip; struct frentry *fr, *fnext; u_32_t passt, passo; /* * Do not allow nesting deeper than 16 levels. */ if (fin->fin_depth >= 16) return (pass); fr = fin->fin_fr; /* * If there are no rules in this list, return now. */ if (fr == NULL) return (pass); skip = 0; portcmp = 0; fin->fin_depth++; fin->fin_fr = NULL; off = fin->fin_off; if ((fin->fin_flx & FI_TCPUDP) && (fin->fin_dlen > 3) && !off) portcmp = 1; for (rulen = 0; fr; fr = fnext, rulen++) { fnext = fr->fr_next; if (skip != 0) { FR_VERBOSE(("SKIP %d (%#x)\n", skip, fr->fr_flags)); skip--; continue; } /* * In all checks below, a null (zero) value in the * filter struture is taken to mean a wildcard. * * check that we are working for the right interface */ #ifdef _KERNEL if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; #else if (opts & (OPT_VERBOSE|OPT_DEBUG)) printf("\n"); FR_VERBOSE(("%c", FR_ISSKIP(pass) ? 's' : FR_ISPASS(pass) ? 'p' : FR_ISACCOUNT(pass) ? 'A' : FR_ISAUTH(pass) ? 'a' : (pass & FR_NOMATCH) ? 'n' :'b')); if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp) continue; FR_VERBOSE((":i")); #endif switch (fr->fr_type) { case FR_T_IPF : case FR_T_IPF_BUILTIN : if (ipf_check_ipf(fin, fr, portcmp)) continue; break; #if defined(IPFILTER_BPF) case FR_T_BPFOPC : case FR_T_BPFOPC_BUILTIN : { u_char *mc; int wlen; if (*fin->fin_mp == NULL) continue; if (fin->fin_family != fr->fr_family) continue; mc = (u_char *)fin->fin_m; wlen = fin->fin_dlen + fin->fin_hlen; if (!bpf_filter(fr->fr_data, mc, wlen, 0)) continue; break; } #endif case FR_T_CALLFUNC_BUILTIN : { frentry_t *f; f = (*fr->fr_func)(fin, &pass); if (f != NULL) fr = f; else continue; break; } case FR_T_IPFEXPR : case FR_T_IPFEXPR_BUILTIN : if (fin->fin_family != fr->fr_family) continue; if (ipf_fr_matcharray(fin, fr->fr_data) == 0) continue; break; default : break; } if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { if (fin->fin_nattag == NULL) continue; if (ipf_matchtag(&fr->fr_nattag, fin->fin_nattag) == 0) continue; } FR_VERBOSE(("=%d/%d.%d *", fr->fr_grhead, fr->fr_group, rulen)); passt = fr->fr_flags; /* * If the rule is a "call now" rule, then call the function * in the rule, if it exists and use the results from that. * If the function pointer is bad, just make like we ignore * it, except for increasing the hit counter. */ if ((passt & FR_CALLNOW) != 0) { frentry_t *frs; ATOMIC_INC64(fr->fr_hits); if ((fr->fr_func == NULL) || (fr->fr_func == (ipfunc_t)-1)) continue; frs = fin->fin_fr; fin->fin_fr = fr; fr = (*fr->fr_func)(fin, &passt); if (fr == NULL) { fin->fin_fr = frs; continue; } passt = fr->fr_flags; } fin->fin_fr = fr; #ifdef IPFILTER_LOG /* * Just log this packet... */ if ((passt & FR_LOGMASK) == FR_LOG) { if (ipf_log_pkt(fin, passt) == -1) { if (passt & FR_LOGORBLOCK) { DT(frb_logfail); passt &= ~FR_CMDMASK; passt |= FR_BLOCK|FR_QUICK; fin->fin_reason = FRB_LOGFAIL; } } } #endif /* IPFILTER_LOG */ MUTEX_ENTER(&fr->fr_lock); fr->fr_bytes += (U_QUAD_T)fin->fin_plen; fr->fr_hits++; MUTEX_EXIT(&fr->fr_lock); fin->fin_rule = rulen; passo = pass; if (FR_ISSKIP(passt)) { skip = fr->fr_arg; continue; } else if (((passt & FR_LOGMASK) != FR_LOG) && ((passt & FR_LOGMASK) != FR_DECAPSULATE)) { pass = passt; } if (passt & (FR_RETICMP|FR_FAKEICMP)) fin->fin_icode = fr->fr_icode; if (fr->fr_group != -1) { (void) strncpy(fin->fin_group, FR_NAME(fr, fr_group), strlen(FR_NAME(fr, fr_group))); } else { fin->fin_group[0] = '\0'; } FR_DEBUG(("pass %#x/%#x/%x\n", passo, pass, passt)); if (fr->fr_grphead != NULL) { fin->fin_fr = fr->fr_grphead->fg_start; FR_VERBOSE(("group %s\n", FR_NAME(fr, fr_grhead))); if (FR_ISDECAPS(passt)) passt = ipf_decaps(fin, pass, fr->fr_icode); else passt = ipf_scanlist(fin, pass); if (fin->fin_fr == NULL) { fin->fin_rule = rulen; if (fr->fr_group != -1) (void) strncpy(fin->fin_group, fr->fr_names + fr->fr_group, strlen(fr->fr_names + fr->fr_group)); fin->fin_fr = fr; passt = pass; } pass = passt; } if (pass & FR_QUICK) { /* * Finally, if we've asked to track state for this * packet, set it up. Add state for "quick" rules * here so that if the action fails we can consider * the rule to "not match" and keep on processing * filter rules. */ if ((pass & FR_KEEPSTATE) && !FR_ISAUTH(pass) && !(fin->fin_flx & FI_STATE)) { int out = fin->fin_out; fin->fin_fr = fr; if (ipf_state_add(softc, fin, NULL, 0) == 0) { LBUMPD(ipf_stats[out], fr_ads); } else { LBUMPD(ipf_stats[out], fr_bads); pass = passo; continue; } } break; } } fin->fin_depth--; return (pass); } /* ------------------------------------------------------------------------ */ /* Function: ipf_acctpkt */ /* Returns: frentry_t* - always returns NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Checks a packet against accounting rules, if there are any for the given */ /* IP protocol version. */ /* */ /* N.B.: this function returns NULL to match the prototype used by other */ /* functions called from the IPFilter "mainline" in ipf_check(). */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_acctpkt(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; char group[FR_GROUPLEN]; frentry_t *fr, *frsave; u_32_t pass, rulen; passp = passp; fr = softc->ipf_acct[fin->fin_out][softc->ipf_active]; if (fr != NULL) { frsave = fin->fin_fr; bcopy(fin->fin_group, group, FR_GROUPLEN); rulen = fin->fin_rule; fin->fin_fr = fr; pass = ipf_scanlist(fin, FR_NOMATCH); if (FR_ISACCOUNT(pass)) { LBUMPD(ipf_stats[0], fr_acct); } fin->fin_fr = frsave; bcopy(group, fin->fin_group, FR_GROUPLEN); fin->fin_rule = rulen; } return (NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_firewall */ /* Returns: frentry_t* - returns pointer to matched rule, if no matches */ /* were found, returns NULL. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Applies an appropriate set of firewall rules to the packet, to see if */ /* there are any matches. The first check is to see if a match can be seen */ /* in the cache. If not, then search an appropriate list of rules. Once a */ /* matching rule is found, take any appropriate actions as defined by the */ /* rule - except logging. */ /* ------------------------------------------------------------------------ */ static frentry_t * ipf_firewall(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; frentry_t *fr; u_32_t pass; int out; out = fin->fin_out; pass = *passp; /* * This rule cache will only affect packets that are not being * statefully filtered. */ fin->fin_fr = softc->ipf_rules[out][softc->ipf_active]; if (fin->fin_fr != NULL) pass = ipf_scanlist(fin, softc->ipf_pass); if ((pass & FR_NOMATCH)) { LBUMPD(ipf_stats[out], fr_nom); } fr = fin->fin_fr; /* * Apply packets per second rate-limiting to a rule as required. */ if ((fr != NULL) && (fr->fr_pps != 0) && !ppsratecheck(&fr->fr_lastpkt, &fr->fr_curpps, fr->fr_pps)) { DT2(frb_ppsrate, fr_info_t *, fin, frentry_t *, fr); pass &= ~(FR_CMDMASK|FR_RETICMP|FR_RETRST); pass |= FR_BLOCK; LBUMPD(ipf_stats[out], fr_ppshit); fin->fin_reason = FRB_PPSRATE; } /* * If we fail to add a packet to the authorization queue, then we * drop the packet later. However, if it was added then pretend * we've dropped it already. */ if (FR_ISAUTH(pass)) { if (ipf_auth_new(fin->fin_m, fin) != 0) { DT1(frb_authnew, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; fin->fin_reason = FRB_AUTHNEW; fin->fin_error = 0; } else { IPFERROR(1); fin->fin_error = ENOSPC; } } if ((fr != NULL) && (fr->fr_func != NULL) && (fr->fr_func != (ipfunc_t)-1) && !(pass & FR_CALLNOW)) (void) (*fr->fr_func)(fin, &pass); /* * If a rule is a pre-auth rule, check again in the list of rules * loaded for authenticated use. It does not particulary matter * if this search fails because a "preauth" result, from a rule, * is treated as "not a pass", hence the packet is blocked. */ if (FR_ISPREAUTH(pass)) { pass = ipf_auth_pre_scanlist(softc, fin, pass); } /* * If the rule has "keep frag" and the packet is actually a fragment, * then create a fragment state entry. */ if (pass & FR_KEEPFRAG) { if (fin->fin_flx & FI_FRAG) { if (ipf_frag_new(softc, fin, pass) == -1) { LBUMP(ipf_stats[out].fr_bnfr); } else { LBUMP(ipf_stats[out].fr_nfr); } } else { LBUMP(ipf_stats[out].fr_cfr); } } fr = fin->fin_fr; *passp = pass; return (fr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_check */ /* Returns: int - 0 == packet allowed through, */ /* User space: */ /* -1 == packet blocked */ /* 1 == packet not matched */ /* -2 == requires authentication */ /* Kernel: */ /* > 0 == filter error # for packet */ /* Parameters: ctx(I) - pointer to the instance context */ /* ip(I) - pointer to start of IPv4/6 packet */ /* hlen(I) - length of header */ /* ifp(I) - pointer to interface this packet is on */ /* out(I) - 0 == packet going in, 1 == packet going out */ /* mp(IO) - pointer to caller's buffer pointer that holds this */ /* IP packet. */ /* Solaris: */ /* qpi(I) - pointer to STREAMS queue information for this */ /* interface & direction. */ /* */ /* ipf_check() is the master function for all IPFilter packet processing. */ /* It orchestrates: Network Address Translation (NAT), checking for packet */ /* authorisation (or pre-authorisation), presence of related state info., */ /* generating log entries, IP packet accounting, routing of packets as */ /* directed by firewall rules and of course whether or not to allow the */ /* packet to be further processed by the kernel. */ /* */ /* For packets blocked, the contents of "mp" will be NULL'd and the buffer */ /* freed. Packets passed may be returned with the pointer pointed to by */ /* by "mp" changed to a new buffer. */ /* ------------------------------------------------------------------------ */ int ipf_check(void *ctx, ip_t *ip, int hlen, struct ifnet *ifp, int out #if defined(_KERNEL) && SOLARIS , void* qif, mb_t **mp) #else , mb_t **mp) #endif { /* * The above really sucks, but short of writing a diff */ ipf_main_softc_t *softc = ctx; fr_info_t frinfo; fr_info_t *fin = &frinfo; u_32_t pass = softc->ipf_pass; frentry_t *fr = NULL; int v = IP_V(ip); mb_t *mc = NULL; mb_t *m; /* * The first part of ipf_check() deals with making sure that what goes * into the filtering engine makes some sense. Information about the * the packet is distilled, collected into a fr_info_t structure and * the an attempt to ensure the buffer the packet is in is big enough * to hold all the required packet headers. */ #ifdef _KERNEL # if SOLARIS qpktinfo_t *qpi = qif; # ifdef __sparc if ((u_int)ip & 0x3) return (2); # endif # else SPL_INT(s); # endif if (softc->ipf_running <= 0) { return (0); } bzero((char *)fin, sizeof(*fin)); # if SOLARIS if (qpi->qpi_flags & QF_BROADCAST) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; if (qpi->qpi_flags & QF_MULTICAST) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; m = qpi->qpi_m; fin->fin_qfm = m; fin->fin_qpi = qpi; # else /* SOLARIS */ m = *mp; # if defined(M_MCAST) if ((m->m_flags & M_MCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_MLOOP) if ((m->m_flags & M_MLOOP) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_BCAST) if ((m->m_flags & M_BCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; # endif # ifdef M_CANFASTFWD /* * XXX For now, IP Filter and fast-forwarding of cached flows * XXX are mutually exclusive. Eventually, IP Filter should * XXX get a "can-fast-forward" filter rule. */ m->m_flags &= ~M_CANFASTFWD; # endif /* M_CANFASTFWD */ # if defined(CSUM_DELAY_DATA) && !defined(__FreeBSD__) /* * disable delayed checksums. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } # endif /* CSUM_DELAY_DATA */ # endif /* SOLARIS */ #else bzero((char *)fin, sizeof(*fin)); m = *mp; # if defined(M_MCAST) if ((m->m_flags & M_MCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_MLOOP) if ((m->m_flags & M_MLOOP) != 0) fin->fin_flx |= FI_MBCAST|FI_MULTICAST; # endif # if defined(M_BCAST) if ((m->m_flags & M_BCAST) != 0) fin->fin_flx |= FI_MBCAST|FI_BROADCAST; # endif #endif /* _KERNEL */ fin->fin_v = v; fin->fin_m = m; fin->fin_ip = ip; fin->fin_mp = mp; fin->fin_out = out; fin->fin_ifp = ifp; fin->fin_error = ENETUNREACH; fin->fin_hlen = (u_short)hlen; fin->fin_dp = (char *)ip + hlen; fin->fin_main_soft = softc; fin->fin_ipoff = (char *)ip - MTOD(m, char *); SPL_NET(s); #ifdef USE_INET6 if (v == 6) { LBUMP(ipf_stats[out].fr_ipv6); /* * Jumbo grams are quite likely too big for internal buffer * structures to handle comfortably, for now, so just drop * them. */ if (((ip6_t *)ip)->ip6_plen == 0) { DT1(frb_jumbo, ip6_t *, (ip6_t *)ip); pass = FR_BLOCK|FR_NOMATCH; fin->fin_reason = FRB_JUMBO; goto finished; } fin->fin_family = AF_INET6; } else #endif { fin->fin_family = AF_INET; } if (ipf_makefrip(hlen, ip, fin) == -1) { DT1(frb_makefrip, fr_info_t *, fin); pass = FR_BLOCK|FR_NOMATCH; fin->fin_reason = FRB_MAKEFRIP; goto finished; } /* * For at least IPv6 packets, if a m_pullup() fails then this pointer * becomes NULL and so we have no packet to free. */ if (*fin->fin_mp == NULL) goto finished; if (!out) { if (v == 4) { if (softc->ipf_chksrc && !ipf_verifysrc(fin)) { LBUMPD(ipf_stats[0], fr_v4_badsrc); fin->fin_flx |= FI_BADSRC; } if (fin->fin_ip->ip_ttl < softc->ipf_minttl) { LBUMPD(ipf_stats[0], fr_v4_badttl); fin->fin_flx |= FI_LOWTTL; } } #ifdef USE_INET6 else if (v == 6) { if (((ip6_t *)ip)->ip6_hlim < softc->ipf_minttl) { LBUMPD(ipf_stats[0], fr_v6_badttl); fin->fin_flx |= FI_LOWTTL; } } #endif } if (fin->fin_flx & FI_SHORT) { LBUMPD(ipf_stats[out], fr_short); } READ_ENTER(&softc->ipf_mutex); if (!out) { switch (fin->fin_v) { case 4 : if (ipf_nat_checkin(fin, &pass) == -1) { goto filterdone; } break; #ifdef USE_INET6 case 6 : if (ipf_nat6_checkin(fin, &pass) == -1) { goto filterdone; } break; #endif default : break; } } /* * Check auth now. * If a packet is found in the auth table, then skip checking * the access lists for permission but we do need to consider * the result as if it were from the ACL's. In addition, being * found in the auth table means it has been seen before, so do * not pass it through accounting (again), lest it be counted twice. */ fr = ipf_auth_check(fin, &pass); if (!out && (fr == NULL)) (void) ipf_acctpkt(fin, NULL); if (fr == NULL) { if ((fin->fin_flx & FI_FRAG) != 0) fr = ipf_frag_known(fin, &pass); if (fr == NULL) fr = ipf_state_check(fin, &pass); } if ((pass & FR_NOMATCH) || (fr == NULL)) fr = ipf_firewall(fin, &pass); /* * If we've asked to track state for this packet, set it up. * Here rather than ipf_firewall because ipf_checkauth may decide * to return a packet for "keep state" */ if ((pass & FR_KEEPSTATE) && (fin->fin_m != NULL) && !(fin->fin_flx & FI_STATE)) { if (ipf_state_add(softc, fin, NULL, 0) == 0) { LBUMP(ipf_stats[out].fr_ads); } else { LBUMP(ipf_stats[out].fr_bads); if (FR_ISPASS(pass)) { DT(frb_stateadd); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_STATEADD; } } } fin->fin_fr = fr; if ((fr != NULL) && !(fin->fin_flx & FI_STATE)) { fin->fin_dif = &fr->fr_dif; fin->fin_tif = &fr->fr_tifs[fin->fin_rev]; } /* * Only count/translate packets which will be passed on, out the * interface. */ if (out && FR_ISPASS(pass)) { (void) ipf_acctpkt(fin, NULL); switch (fin->fin_v) { case 4 : if (ipf_nat_checkout(fin, &pass) == -1) { ; } else if ((softc->ipf_update_ipid != 0) && (v == 4)) { if (ipf_updateipid(fin) == -1) { DT(frb_updateipid); LBUMP(ipf_stats[1].fr_ipud); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_UPDATEIPID; } else { LBUMP(ipf_stats[0].fr_ipud); } } break; #ifdef USE_INET6 case 6 : (void) ipf_nat6_checkout(fin, &pass); break; #endif default : break; } } filterdone: #ifdef IPFILTER_LOG if ((softc->ipf_flags & FF_LOGGING) || (pass & FR_LOGMASK)) { (void) ipf_dolog(fin, &pass); } #endif /* * The FI_STATE flag is cleared here so that calling ipf_state_check * will work when called from inside of fr_fastroute. Although * there is a similar flag, FI_NATED, for NAT, it does have the same * impact on code execution. */ fin->fin_flx &= ~FI_STATE; #if defined(FASTROUTE_RECURSION) /* * Up the reference on fr_lock and exit ipf_mutex. The generation of * a packet below can sometimes cause a recursive call into IPFilter. * On those platforms where that does happen, we need to hang onto * the filter rule just in case someone decides to remove or flush it * in the meantime. */ if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } RWLOCK_EXIT(&softc->ipf_mutex); #endif if ((pass & FR_RETMASK) != 0) { /* * Should we return an ICMP packet to indicate error * status passing through the packet filter ? * WARNING: ICMP error packets AND TCP RST packets should * ONLY be sent in repsonse to incoming packets. Sending * them in response to outbound packets can result in a * panic on some operating systems. */ if (!out) { if (pass & FR_RETICMP) { int dst; if ((pass & FR_RETMASK) == FR_FAKEICMP) dst = 1; else dst = 0; (void) ipf_send_icmp_err(ICMP_UNREACH, fin, dst); LBUMP(ipf_stats[0].fr_ret); } else if (((pass & FR_RETMASK) == FR_RETRST) && !(fin->fin_flx & FI_SHORT)) { if (((fin->fin_flx & FI_OOW) != 0) || (ipf_send_reset(fin) == 0)) { LBUMP(ipf_stats[1].fr_ret); } } /* * When using return-* with auth rules, the auth code * takes over disposing of this packet. */ if (FR_ISAUTH(pass) && (fin->fin_m != NULL)) { DT1(frb_authcapture, fr_info_t *, fin); fin->fin_m = *fin->fin_mp = NULL; fin->fin_reason = FRB_AUTHCAPTURE; m = NULL; } } else { if (pass & FR_RETRST) { fin->fin_error = ECONNRESET; } } } /* * After the above so that ICMP unreachables and TCP RSTs get * created properly. */ if (FR_ISBLOCK(pass) && (fin->fin_flx & FI_NEWNAT)) ipf_nat_uncreate(fin); /* * If we didn't drop off the bottom of the list of rules (and thus * the 'current' rule fr is not NULL), then we may have some extra * instructions about what to do with a packet. * Once we're finished return to our caller, freeing the packet if * we are dropping it. */ if (fr != NULL) { frdest_t *fdp; /* * Generate a duplicated packet first because ipf_fastroute * can lead to fin_m being free'd... not good. */ fdp = fin->fin_dif; if ((fdp != NULL) && (fdp->fd_ptr != NULL) && (fdp->fd_ptr != (void *)-1)) { mc = M_COPY(fin->fin_m); if (mc != NULL) ipf_fastroute(mc, &mc, fin, fdp); } fdp = fin->fin_tif; if (!out && (pass & FR_FASTROUTE)) { /* * For fastroute rule, no destination interface defined * so pass NULL as the frdest_t parameter */ (void) ipf_fastroute(fin->fin_m, mp, fin, NULL); m = *mp = NULL; } else if ((fdp != NULL) && (fdp->fd_ptr != NULL) && (fdp->fd_ptr != (struct ifnet *)-1)) { /* this is for to rules: */ ipf_fastroute(fin->fin_m, mp, fin, fdp); m = *mp = NULL; } #if defined(FASTROUTE_RECURSION) (void) ipf_derefrule(softc, &fr); #endif } #if !defined(FASTROUTE_RECURSION) RWLOCK_EXIT(&softc->ipf_mutex); #endif finished: if (!FR_ISPASS(pass)) { LBUMP(ipf_stats[out].fr_block); if (*mp != NULL) { #ifdef _KERNEL FREE_MB_T(*mp); #endif m = *mp = NULL; } } else { LBUMP(ipf_stats[out].fr_pass); } SPL_X(s); if (fin->fin_m == NULL && fin->fin_flx & FI_BAD && fin->fin_reason == FRB_PULLUP) { /* m_pullup() has freed the mbuf */ LBUMP(ipf_stats[out].fr_blocked[fin->fin_reason]); return (-1); } #ifdef _KERNEL if (FR_ISPASS(pass)) return (0); LBUMP(ipf_stats[out].fr_blocked[fin->fin_reason]); return (fin->fin_error); #else /* _KERNEL */ if (*mp != NULL) (*mp)->mb_ifp = fin->fin_ifp; blockreason = fin->fin_reason; FR_VERBOSE(("fin_flx %#x pass %#x ", fin->fin_flx, pass)); /*if ((pass & FR_CMDMASK) == (softc->ipf_pass & FR_CMDMASK))*/ if ((pass & FR_NOMATCH) != 0) return (1); if ((pass & FR_RETMASK) != 0) switch (pass & FR_RETMASK) { case FR_RETRST : return (3); case FR_RETICMP : return (4); case FR_FAKEICMP : return (5); } switch (pass & FR_CMDMASK) { case FR_PASS : return (0); case FR_BLOCK : return (-1); case FR_AUTH : return (-2); case FR_ACCOUNT : return (-3); case FR_PREAUTH : return (-4); } return (2); #endif /* _KERNEL */ } #ifdef IPFILTER_LOG /* ------------------------------------------------------------------------ */ /* Function: ipf_dolog */ /* Returns: frentry_t* - returns contents of fin_fr (no change made) */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Checks flags set to see how a packet should be logged, if it is to be */ /* logged. Adjust statistics based on its success or not. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_dolog(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; u_32_t pass; int out; out = fin->fin_out; pass = *passp; if ((softc->ipf_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) { pass |= FF_LOGNOMATCH; LBUMPD(ipf_stats[out], fr_npkl); goto logit; } else if (((pass & FR_LOGMASK) == FR_LOGP) || (FR_ISPASS(pass) && (softc->ipf_flags & FF_LOGPASS))) { if ((pass & FR_LOGMASK) != FR_LOGP) pass |= FF_LOGPASS; LBUMPD(ipf_stats[out], fr_ppkl); goto logit; } else if (((pass & FR_LOGMASK) == FR_LOGB) || (FR_ISBLOCK(pass) && (softc->ipf_flags & FF_LOGBLOCK))) { if ((pass & FR_LOGMASK) != FR_LOGB) pass |= FF_LOGBLOCK; LBUMPD(ipf_stats[out], fr_bpkl); logit: if (ipf_log_pkt(fin, pass) == -1) { /* * If the "or-block" option has been used then * block the packet if we failed to log it. */ if ((pass & FR_LOGORBLOCK) && FR_ISPASS(pass)) { DT1(frb_logfail2, u_int, pass); pass &= ~FR_CMDMASK; pass |= FR_BLOCK; fin->fin_reason = FRB_LOGFAIL2; } } *passp = pass; } return (fin->fin_fr); } #endif /* IPFILTER_LOG */ /* ------------------------------------------------------------------------ */ /* Function: ipf_cksum */ /* Returns: u_short - IP header checksum */ /* Parameters: addr(I) - pointer to start of buffer to checksum */ /* len(I) - length of buffer in bytes */ /* */ /* Calculate the two's complement 16 bit checksum of the buffer passed. */ /* */ /* N.B.: addr should be 16bit aligned. */ /* ------------------------------------------------------------------------ */ u_short ipf_cksum(u_short *addr, int len) { u_32_t sum = 0; for (sum = 0; len > 1; len -= 2) sum += *addr++; /* mop up an odd byte, if necessary */ if (len == 1) sum += *(u_char *)addr; /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ return (u_short)(~sum); } /* ------------------------------------------------------------------------ */ /* Function: fr_cksum */ /* Returns: u_short - layer 4 checksum */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to IP header */ /* l4proto(I) - protocol to caclulate checksum for */ /* l4hdr(I) - pointer to layer 4 header */ /* */ /* Calculates the TCP checksum for the packet held in "m", using the data */ /* in the IP header "ip" to seed it. */ /* */ /* NB: This function assumes we've pullup'd enough for all of the IP header */ /* and the TCP header. We also assume that data blocks aren't allocated in */ /* odd sizes. */ /* */ /* Expects ip_len and ip_off to be in network byte order when called. */ /* ------------------------------------------------------------------------ */ u_short fr_cksum(fr_info_t *fin, ip_t *ip, int l4proto, void *l4hdr) { u_short *sp, slen, sumsave, *csump; u_int sum, sum2; int hlen; int off; #ifdef USE_INET6 ip6_t *ip6; #endif csump = NULL; sumsave = 0; sp = NULL; slen = 0; hlen = 0; sum = 0; sum = htons((u_short)l4proto); /* * Add up IP Header portion */ #ifdef USE_INET6 if (IP_V(ip) == 4) { #endif hlen = IP_HL(ip) << 2; off = hlen; sp = (u_short *)&ip->ip_src; sum += *sp++; /* ip_src */ sum += *sp++; sum += *sp++; /* ip_dst */ sum += *sp++; slen = fin->fin_plen - off; sum += htons(slen); #ifdef USE_INET6 } else if (IP_V(ip) == 6) { mb_t *m; m = fin->fin_m; ip6 = (ip6_t *)ip; off = ((caddr_t)ip6 - m->m_data) + sizeof(struct ip6_hdr); int len = ntohs(ip6->ip6_plen) - (off - sizeof(*ip6)); return (ipf_pcksum6(m, ip6, off, len)); } else { return (0xffff); } #endif switch (l4proto) { case IPPROTO_UDP : csump = &((udphdr_t *)l4hdr)->uh_sum; break; case IPPROTO_TCP : csump = &((tcphdr_t *)l4hdr)->th_sum; break; case IPPROTO_ICMP : csump = &((icmphdr_t *)l4hdr)->icmp_cksum; sum = 0; /* Pseudo-checksum is not included */ break; #ifdef USE_INET6 case IPPROTO_ICMPV6 : csump = &((struct icmp6_hdr *)l4hdr)->icmp6_cksum; break; #endif default : break; } if (csump != NULL) { sumsave = *csump; *csump = 0; } sum2 = ipf_pcksum(fin, off, sum); if (csump != NULL) *csump = sumsave; return (sum2); } /* ------------------------------------------------------------------------ */ /* Function: ipf_findgroup */ /* Returns: frgroup_t * - NULL = group not found, else pointer to group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* group(I) - group name to search for */ /* unit(I) - device to which this group belongs */ /* set(I) - which set of rules (inactive/inactive) this is */ /* fgpp(O) - pointer to place to store pointer to the pointer */ /* to where to add the next (last) group or where */ /* to delete group from. */ /* */ /* Search amongst the defined groups for a particular group number. */ /* ------------------------------------------------------------------------ */ frgroup_t * ipf_findgroup(ipf_main_softc_t *softc, char *group, minor_t unit, int set, frgroup_t ***fgpp) { frgroup_t *fg, **fgp; /* * Which list of groups to search in is dependent on which list of * rules are being operated on. */ fgp = &softc->ipf_groups[unit][set]; while ((fg = *fgp) != NULL) { if (strncmp(group, fg->fg_name, FR_GROUPLEN) == 0) break; else fgp = &fg->fg_next; } if (fgpp != NULL) *fgpp = fgp; return (fg); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_add */ /* Returns: frgroup_t * - NULL == did not create group, */ /* != NULL == pointer to the group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* num(I) - group number to add */ /* head(I) - rule pointer that is using this as the head */ /* flags(I) - rule flags which describe the type of rule it is */ /* unit(I) - device to which this group will belong to */ /* set(I) - which set of rules (inactive/inactive) this is */ /* Write Locks: ipf_mutex */ /* */ /* Add a new group head, or if it already exists, increase the reference */ /* count to it. */ /* ------------------------------------------------------------------------ */ frgroup_t * ipf_group_add(ipf_main_softc_t *softc, char *group, void *head, u_32_t flags, minor_t unit, int set) { frgroup_t *fg, **fgp; u_32_t gflags; if (group == NULL) return (NULL); if (unit == IPL_LOGIPF && *group == '\0') return (NULL); fgp = NULL; gflags = flags & FR_INOUT; fg = ipf_findgroup(softc, group, unit, set, &fgp); if (fg != NULL) { if (fg->fg_head == NULL && head != NULL) fg->fg_head = head; if (fg->fg_flags == 0) fg->fg_flags = gflags; else if (gflags != fg->fg_flags) return (NULL); fg->fg_ref++; return (fg); } KMALLOC(fg, frgroup_t *); if (fg != NULL) { fg->fg_head = head; fg->fg_start = NULL; fg->fg_next = *fgp; bcopy(group, fg->fg_name, strlen(group) + 1); fg->fg_flags = gflags; fg->fg_ref = 1; fg->fg_set = &softc->ipf_groups[unit][set]; *fgp = fg; } return (fg); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_del */ /* Returns: int - number of rules deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* group(I) - group name to delete */ /* fr(I) - filter rule from which group is referenced */ /* Write Locks: ipf_mutex */ /* */ /* This function is called whenever a reference to a group is to be dropped */ /* and thus its reference count needs to be lowered and the group free'd if */ /* the reference count reaches zero. Passing in fr is really for the sole */ /* purpose of knowing when the head rule is being deleted. */ /* ------------------------------------------------------------------------ */ void ipf_group_del(ipf_main_softc_t *softc, frgroup_t *group, frentry_t *fr) { if (group->fg_head == fr) group->fg_head = NULL; group->fg_ref--; if ((group->fg_ref == 0) && (group->fg_start == NULL)) ipf_group_free(group); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_free */ /* Returns: Nil */ /* Parameters: group(I) - pointer to filter rule group */ /* */ /* Remove the group from the list of groups and free it. */ /* ------------------------------------------------------------------------ */ static void ipf_group_free(frgroup_t *group) { frgroup_t **gp; for (gp = group->fg_set; *gp != NULL; gp = &(*gp)->fg_next) { if (*gp == group) { *gp = group->fg_next; break; } } KFREE(group); } /* ------------------------------------------------------------------------ */ /* Function: ipf_group_flush */ /* Returns: int - number of rules flush from group */ /* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: group(I) - pointer to filter rule group */ /* */ /* Remove all of the rules that currently are listed under the given group. */ /* ------------------------------------------------------------------------ */ static int ipf_group_flush(ipf_main_softc_t *softc, frgroup_t *group) { int gone = 0; (void) ipf_flushlist(softc, &gone, &group->fg_start); return (gone); } /* ------------------------------------------------------------------------ */ /* Function: ipf_getrulen */ /* Returns: frentry_t * - NULL == not found, else pointer to rule n */ /* Parameters: softc(I) - pointer to soft context main structure */ /* Parameters: unit(I) - device for which to count the rule's number */ /* flags(I) - which set of rules to find the rule in */ /* group(I) - group name */ /* n(I) - rule number to find */ /* */ /* Find rule # n in group # g and return a pointer to it. Return NULl if */ /* group # g doesn't exist or there are less than n rules in the group. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_getrulen(ipf_main_softc_t *softc, int unit, char *group, u_32_t n) { frentry_t *fr; frgroup_t *fg; fg = ipf_findgroup(softc, group, unit, softc->ipf_active, NULL); if (fg == NULL) return (NULL); for (fr = fg->fg_start; fr && n; fr = fr->fr_next, n--) ; if (n != 0) return (NULL); return (fr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_flushlist */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - pointer to soft context main structure */ /* nfreedp(O) - pointer to int where flush count is stored */ /* listp(I) - pointer to list to flush pointer */ /* Write Locks: ipf_mutex */ /* */ /* Recursively flush rules from the list, descending groups as they are */ /* encountered. if a rule is the head of a group and it has lost all its */ /* group members, then also delete the group reference. nfreedp is needed */ /* to store the accumulating count of rules removed, whereas the returned */ /* value is just the number removed from the current list. The latter is */ /* needed to correctly adjust reference counts on rules that define groups. */ /* */ /* NOTE: Rules not loaded from user space cannot be flushed. */ /* ------------------------------------------------------------------------ */ static int ipf_flushlist(ipf_main_softc_t *softc, int *nfreedp, frentry_t **listp) { int freed = 0; frentry_t *fp; while ((fp = *listp) != NULL) { if ((fp->fr_type & FR_T_BUILTIN) || !(fp->fr_flags & FR_COPIED)) { listp = &fp->fr_next; continue; } *listp = fp->fr_next; if (fp->fr_next != NULL) fp->fr_next->fr_pnext = fp->fr_pnext; fp->fr_pnext = NULL; if (fp->fr_grphead != NULL) { freed += ipf_group_flush(softc, fp->fr_grphead); fp->fr_names[fp->fr_grhead] = '\0'; } if (fp->fr_icmpgrp != NULL) { freed += ipf_group_flush(softc, fp->fr_icmpgrp); fp->fr_names[fp->fr_icmphead] = '\0'; } if (fp->fr_srctrack.ht_max_nodes) ipf_rb_ht_flush(&fp->fr_srctrack); fp->fr_next = NULL; ASSERT(fp->fr_ref > 0); if (ipf_derefrule(softc, &fp) == 0) freed++; } *nfreedp += freed; return (freed); } /* ------------------------------------------------------------------------ */ /* Function: ipf_flush */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - device for which to flush rules */ /* flags(I) - which set of rules to flush */ /* */ /* Calls flushlist() for all filter rules (accounting, firewall - both IPv4 */ /* and IPv6) as defined by the value of flags. */ /* ------------------------------------------------------------------------ */ int ipf_flush(ipf_main_softc_t *softc, minor_t unit, int flags) { int flushed = 0, set; WRITE_ENTER(&softc->ipf_mutex); set = softc->ipf_active; if ((flags & FR_INACTIVE) == FR_INACTIVE) set = 1 - set; if (flags & FR_OUTQUE) { ipf_flushlist(softc, &flushed, &softc->ipf_rules[1][set]); ipf_flushlist(softc, &flushed, &softc->ipf_acct[1][set]); } if (flags & FR_INQUE) { ipf_flushlist(softc, &flushed, &softc->ipf_rules[0][set]); ipf_flushlist(softc, &flushed, &softc->ipf_acct[0][set]); } flushed += ipf_flush_groups(softc, &softc->ipf_groups[unit][set], flags & (FR_INQUE|FR_OUTQUE)); RWLOCK_EXIT(&softc->ipf_mutex); if (unit == IPL_LOGIPF) { int tmp; tmp = ipf_flush(softc, IPL_LOGCOUNT, flags); if (tmp >= 0) flushed += tmp; } return (flushed); } /* ------------------------------------------------------------------------ */ /* Function: ipf_flush_groups */ /* Returns: int - >= 0 - number of flushed rules */ /* Parameters: softc(I) - soft context pointerto work with */ /* grhead(I) - pointer to the start of the group list to flush */ /* flags(I) - which set of rules to flush */ /* */ /* Walk through all of the groups under the given group head and remove all */ /* of those that match the flags passed in. The for loop here is bit more */ /* complicated than usual because the removal of a rule with ipf_derefrule */ /* may end up removing not only the structure pointed to by "fg" but also */ /* what is fg_next and fg_next after that. So if a filter rule is actually */ /* removed from the group then it is necessary to start again. */ /* ------------------------------------------------------------------------ */ static int ipf_flush_groups(ipf_main_softc_t *softc, frgroup_t **grhead, int flags) { frentry_t *fr, **frp; frgroup_t *fg, **fgp; int flushed = 0; int removed = 0; for (fgp = grhead; (fg = *fgp) != NULL; ) { while ((fg != NULL) && ((fg->fg_flags & flags) == 0)) fg = fg->fg_next; if (fg == NULL) break; removed = 0; frp = &fg->fg_start; while ((removed == 0) && ((fr = *frp) != NULL)) { if ((fr->fr_flags & flags) == 0) { frp = &fr->fr_next; } else { if (fr->fr_next != NULL) fr->fr_next->fr_pnext = fr->fr_pnext; *frp = fr->fr_next; fr->fr_pnext = NULL; fr->fr_next = NULL; (void) ipf_derefrule(softc, &fr); flushed++; removed++; } } if (removed == 0) fgp = &fg->fg_next; } return (flushed); } /* ------------------------------------------------------------------------ */ /* Function: memstr */ /* Returns: char * - NULL if failed, != NULL pointer to matching bytes */ /* Parameters: src(I) - pointer to byte sequence to match */ /* dst(I) - pointer to byte sequence to search */ /* slen(I) - match length */ /* dlen(I) - length available to search in */ /* */ /* Search dst for a sequence of bytes matching those at src and extend for */ /* slen bytes. */ /* ------------------------------------------------------------------------ */ char * memstr(const char *src, char *dst, size_t slen, size_t dlen) { char *s = NULL; while (dlen >= slen) { if (bcmp(src, dst, slen) == 0) { s = dst; break; } dst++; dlen--; } return (s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_fixskip */ /* Returns: Nil */ /* Parameters: listp(IO) - pointer to start of list with skip rule */ /* rp(I) - rule added/removed with skip in it. */ /* addremove(I) - adjustment (-1/+1) to make to skip count, */ /* depending on whether a rule was just added */ /* or removed. */ /* */ /* Adjust all the rules in a list which would have skip'd past the position */ /* where we are inserting to skip to the right place given the change. */ /* ------------------------------------------------------------------------ */ void ipf_fixskip(frentry_t **listp, frentry_t *rp, int addremove) { int rules, rn; frentry_t *fp; rules = 0; for (fp = *listp; (fp != NULL) && (fp != rp); fp = fp->fr_next) rules++; if (fp == NULL) return; for (rn = 0, fp = *listp; fp && (fp != rp); fp = fp->fr_next, rn++) if (FR_ISSKIP(fp->fr_flags) && (rn + fp->fr_arg >= rules)) fp->fr_arg += addremove; } #ifdef _KERNEL /* ------------------------------------------------------------------------ */ /* Function: count4bits */ /* Returns: int - >= 0 - number of consecutive bits in input */ /* Parameters: ip(I) - 32bit IP address */ /* */ /* IPv4 ONLY */ /* count consecutive 1's in bit mask. If the mask generated by counting */ /* consecutive 1's is different to that passed, return -1, else return # */ /* of bits. */ /* ------------------------------------------------------------------------ */ int count4bits(u_32_t ip) { u_32_t ipn; int cnt = 0, i, j; ip = ipn = ntohl(ip); for (i = 32; i; i--, ipn *= 2) if (ipn & 0x80000000) cnt++; else break; ipn = 0; for (i = 32, j = cnt; i; i--, j--) { ipn *= 2; if (j > 0) ipn++; } if (ipn == ip) return (cnt); return (-1); } /* ------------------------------------------------------------------------ */ /* Function: count6bits */ /* Returns: int - >= 0 - number of consecutive bits in input */ /* Parameters: msk(I) - pointer to start of IPv6 bitmask */ /* */ /* IPv6 ONLY */ /* count consecutive 1's in bit mask. */ /* ------------------------------------------------------------------------ */ # ifdef USE_INET6 int count6bits(u_32_t *msk) { int i = 0, k; u_32_t j; for (k = 3; k >= 0; k--) if (msk[k] == 0xffffffff) i += 32; else { for (j = msk[k]; j; j <<= 1) if (j & 0x80000000) i++; } return (i); } # endif #endif /* _KERNEL */ /* ------------------------------------------------------------------------ */ /* Function: ipf_synclist */ /* Returns: int - 0 = no failures, else indication of first failure */ /* Parameters: fr(I) - start of filter list to sync interface names for */ /* ifp(I) - interface pointer for limiting sync lookups */ /* Write Locks: ipf_mutex */ /* */ /* Walk through a list of filter rules and resolve any interface names into */ /* pointers. Where dynamic addresses are used, also update the IP address */ /* used in the rule. The interface pointer is used to limit the lookups to */ /* a specific set of matching names if it is non-NULL. */ /* Errors can occur when resolving the destination name of to/dup-to fields */ /* when the name points to a pool and that pool doest not exist. If this */ /* does happen then it is necessary to check if there are any lookup refs */ /* that need to be dropped before returning with an error. */ /* ------------------------------------------------------------------------ */ static int ipf_synclist(ipf_main_softc_t *softc, frentry_t *fr, void *ifp) { frentry_t *frt, *start = fr; frdest_t *fdp; char *name; int error; void *ifa; int v, i; error = 0; for (; fr; fr = fr->fr_next) { if (fr->fr_family == AF_INET) v = 4; else if (fr->fr_family == AF_INET6) v = 6; else v = 0; /* * Lookup all the interface names that are part of the rule. */ for (i = 0; i < FR_NUM(fr->fr_ifas); i++) { if ((ifp != NULL) && (fr->fr_ifas[i] != ifp)) continue; if (fr->fr_ifnames[i] == -1) continue; name = FR_NAME(fr, fr_ifnames[i]); fr->fr_ifas[i] = ipf_resolvenic(softc, name, v); } if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { if (fr->fr_satype != FRI_NORMAL && fr->fr_satype != FRI_LOOKUP) { ifa = ipf_resolvenic(softc, fr->fr_names + fr->fr_sifpidx, v); ipf_ifpaddr(softc, v, fr->fr_satype, ifa, &fr->fr_src6, &fr->fr_smsk6); } if (fr->fr_datype != FRI_NORMAL && fr->fr_datype != FRI_LOOKUP) { ifa = ipf_resolvenic(softc, fr->fr_names + fr->fr_sifpidx, v); ipf_ifpaddr(softc, v, fr->fr_datype, ifa, &fr->fr_dst6, &fr->fr_dmsk6); } } fdp = &fr->fr_tifs[0]; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } fdp = &fr->fr_tifs[1]; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } fdp = &fr->fr_dif; if ((ifp == NULL) || (fdp->fd_ptr == ifp)) { error = ipf_resolvedest(softc, fr->fr_names, fdp, v); if (error != 0) goto unwind; } if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (fr->fr_satype == FRI_LOOKUP) && (fr->fr_srcptr == NULL)) { fr->fr_srcptr = ipf_lookup_res_num(softc, fr->fr_srctype, IPL_LOGIPF, fr->fr_srcnum, &fr->fr_srcfunc); } if (((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (fr->fr_datype == FRI_LOOKUP) && (fr->fr_dstptr == NULL)) { fr->fr_dstptr = ipf_lookup_res_num(softc, fr->fr_dsttype, IPL_LOGIPF, fr->fr_dstnum, &fr->fr_dstfunc); } } return (0); unwind: for (frt = start; frt != fr; fr = fr->fr_next) { if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (frt->fr_satype == FRI_LOOKUP) && (frt->fr_srcptr != NULL)) ipf_lookup_deref(softc, frt->fr_srctype, frt->fr_srcptr); if (((frt->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) && (frt->fr_datype == FRI_LOOKUP) && (frt->fr_dstptr != NULL)) ipf_lookup_deref(softc, frt->fr_dsttype, frt->fr_dstptr); } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_sync */ /* Returns: void */ /* Parameters: Nil */ /* */ /* ipf_sync() is called when we suspect that the interface list or */ /* information about interfaces (like IP#) has changed. Go through all */ /* filter rules, NAT entries and the state table and check if anything */ /* needs to be changed/updated. */ /* ------------------------------------------------------------------------ */ int ipf_sync(ipf_main_softc_t *softc, void *ifp) { int i; #if !SOLARIS ipf_nat_sync(softc, ifp); ipf_state_sync(softc, ifp); ipf_lookup_sync(softc, ifp); #endif WRITE_ENTER(&softc->ipf_mutex); (void) ipf_synclist(softc, softc->ipf_acct[0][softc->ipf_active], ifp); (void) ipf_synclist(softc, softc->ipf_acct[1][softc->ipf_active], ifp); (void) ipf_synclist(softc, softc->ipf_rules[0][softc->ipf_active], ifp); (void) ipf_synclist(softc, softc->ipf_rules[1][softc->ipf_active], ifp); for (i = 0; i < IPL_LOGSIZE; i++) { frgroup_t *g; for (g = softc->ipf_groups[i][0]; g != NULL; g = g->fg_next) (void) ipf_synclist(softc, g->fg_start, ifp); for (g = softc->ipf_groups[i][1]; g != NULL; g = g->fg_next) (void) ipf_synclist(softc, g->fg_start, ifp); } RWLOCK_EXIT(&softc->ipf_mutex); return (0); } /* * In the functions below, bcopy() is called because the pointer being * copied _from_ in this instance is a pointer to a char buf (which could * end up being unaligned) and on the kernel's local stack. */ /* ------------------------------------------------------------------------ */ /* Function: copyinptr */ /* Returns: int - 0 = success, else failure */ /* Parameters: src(I) - pointer to the source address */ /* dst(I) - destination address */ /* size(I) - number of bytes to copy */ /* */ /* Copy a block of data in from user space, given a pointer to the pointer */ /* to start copying from (src) and a pointer to where to store it (dst). */ /* NB: src - pointer to user space pointer, dst - kernel space pointer */ /* ------------------------------------------------------------------------ */ int copyinptr(ipf_main_softc_t *softc, void *src, void *dst, size_t size) { caddr_t ca; int error; #if SOLARIS error = COPYIN(src, &ca, sizeof(ca)); if (error != 0) return (error); #else bcopy(src, (caddr_t)&ca, sizeof(ca)); #endif error = COPYIN(ca, dst, size); if (error != 0) { IPFERROR(3); error = EFAULT; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: copyoutptr */ /* Returns: int - 0 = success, else failure */ /* Parameters: src(I) - pointer to the source address */ /* dst(I) - destination address */ /* size(I) - number of bytes to copy */ /* */ /* Copy a block of data out to user space, given a pointer to the pointer */ /* to start copying from (src) and a pointer to where to store it (dst). */ /* NB: src - kernel space pointer, dst - pointer to user space pointer. */ /* ------------------------------------------------------------------------ */ int copyoutptr(ipf_main_softc_t *softc, void *src, void *dst, size_t size) { caddr_t ca; int error; bcopy(dst, (caddr_t)&ca, sizeof(ca)); error = COPYOUT(src, ca, size); if (error != 0) { IPFERROR(4); error = EFAULT; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_lock */ /* Returns: int - 0 = success, else error */ /* Parameters: data(I) - pointer to lock value to set */ /* lockp(O) - pointer to location to store old lock value */ /* */ /* Get the new value for the lock integer, set it and return the old value */ /* in *lockp. */ /* ------------------------------------------------------------------------ */ int ipf_lock(caddr_t data, int *lockp) { int arg, err; err = BCOPYIN(data, &arg, sizeof(arg)); if (err != 0) return (EFAULT); err = BCOPYOUT(lockp, data, sizeof(*lockp)); if (err != 0) return (EFAULT); *lockp = arg; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_getstat */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fiop(I) - pointer to ipfilter stats structure */ /* rev(I) - version claim by program doing ioctl */ /* */ /* Stores a copy of current pointers, counters, etc, in the friostat */ /* structure. */ /* If IPFILTER_COMPAT is compiled, we pretend to be whatever version the */ /* program is looking for. This ensure that validation of the version it */ /* expects will always succeed. Thus kernels with IPFILTER_COMPAT will */ /* allow older binaries to work but kernels without it will not. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static void ipf_getstat(ipf_main_softc_t *softc, friostat_t *fiop, int rev) { int i; bcopy((char *)softc->ipf_stats, (char *)fiop->f_st, sizeof(ipf_statistics_t) * 2); fiop->f_locks[IPL_LOGSTATE] = -1; fiop->f_locks[IPL_LOGNAT] = -1; fiop->f_locks[IPL_LOGIPF] = -1; fiop->f_locks[IPL_LOGAUTH] = -1; fiop->f_ipf[0][0] = softc->ipf_rules[0][0]; fiop->f_acct[0][0] = softc->ipf_acct[0][0]; fiop->f_ipf[0][1] = softc->ipf_rules[0][1]; fiop->f_acct[0][1] = softc->ipf_acct[0][1]; fiop->f_ipf[1][0] = softc->ipf_rules[1][0]; fiop->f_acct[1][0] = softc->ipf_acct[1][0]; fiop->f_ipf[1][1] = softc->ipf_rules[1][1]; fiop->f_acct[1][1] = softc->ipf_acct[1][1]; fiop->f_ticks = softc->ipf_ticks; fiop->f_active = softc->ipf_active; fiop->f_froute[0] = softc->ipf_frouteok[0]; fiop->f_froute[1] = softc->ipf_frouteok[1]; fiop->f_rb_no_mem = softc->ipf_rb_no_mem; fiop->f_rb_node_max = softc->ipf_rb_node_max; fiop->f_running = softc->ipf_running; for (i = 0; i < IPL_LOGSIZE; i++) { fiop->f_groups[i][0] = softc->ipf_groups[i][0]; fiop->f_groups[i][1] = softc->ipf_groups[i][1]; } #ifdef IPFILTER_LOG fiop->f_log_ok = ipf_log_logok(softc, IPL_LOGIPF); fiop->f_log_fail = ipf_log_failures(softc, IPL_LOGIPF); fiop->f_logging = 1; #else fiop->f_log_ok = 0; fiop->f_log_fail = 0; fiop->f_logging = 0; #endif fiop->f_defpass = softc->ipf_pass; fiop->f_features = ipf_features; #ifdef IPFILTER_COMPAT snprintf(fiop->f_version, sizeof(friostat.f_version), "IP Filter: v%d.%d.%d", (rev / 1000000) % 100, (rev / 10000) % 100, (rev / 100) % 100); #else rev = rev; (void) strncpy(fiop->f_version, ipfilter_version, sizeof(fiop->f_version)); #endif } #ifdef USE_INET6 int icmptoicmp6types[ICMP_MAXTYPE+1] = { ICMP6_ECHO_REPLY, /* 0: ICMP_ECHOREPLY */ -1, /* 1: UNUSED */ -1, /* 2: UNUSED */ ICMP6_DST_UNREACH, /* 3: ICMP_UNREACH */ -1, /* 4: ICMP_SOURCEQUENCH */ ND_REDIRECT, /* 5: ICMP_REDIRECT */ -1, /* 6: UNUSED */ -1, /* 7: UNUSED */ ICMP6_ECHO_REQUEST, /* 8: ICMP_ECHO */ -1, /* 9: UNUSED */ -1, /* 10: UNUSED */ ICMP6_TIME_EXCEEDED, /* 11: ICMP_TIMXCEED */ ICMP6_PARAM_PROB, /* 12: ICMP_PARAMPROB */ -1, /* 13: ICMP_TSTAMP */ -1, /* 14: ICMP_TSTAMPREPLY */ -1, /* 15: ICMP_IREQ */ -1, /* 16: ICMP_IREQREPLY */ -1, /* 17: ICMP_MASKREQ */ -1, /* 18: ICMP_MASKREPLY */ }; int icmptoicmp6unreach[ICMP_MAX_UNREACH] = { ICMP6_DST_UNREACH_ADDR, /* 0: ICMP_UNREACH_NET */ ICMP6_DST_UNREACH_ADDR, /* 1: ICMP_UNREACH_HOST */ -1, /* 2: ICMP_UNREACH_PROTOCOL */ ICMP6_DST_UNREACH_NOPORT, /* 3: ICMP_UNREACH_PORT */ -1, /* 4: ICMP_UNREACH_NEEDFRAG */ ICMP6_DST_UNREACH_NOTNEIGHBOR, /* 5: ICMP_UNREACH_SRCFAIL */ ICMP6_DST_UNREACH_ADDR, /* 6: ICMP_UNREACH_NET_UNKNOWN */ ICMP6_DST_UNREACH_ADDR, /* 7: ICMP_UNREACH_HOST_UNKNOWN */ -1, /* 8: ICMP_UNREACH_ISOLATED */ ICMP6_DST_UNREACH_ADMIN, /* 9: ICMP_UNREACH_NET_PROHIB */ ICMP6_DST_UNREACH_ADMIN, /* 10: ICMP_UNREACH_HOST_PROHIB */ -1, /* 11: ICMP_UNREACH_TOSNET */ -1, /* 12: ICMP_UNREACH_TOSHOST */ ICMP6_DST_UNREACH_ADMIN, /* 13: ICMP_UNREACH_ADMIN_PROHIBIT */ }; int icmpreplytype6[ICMP6_MAXTYPE + 1]; #endif int icmpreplytype4[ICMP_MAXTYPE + 1]; /* ------------------------------------------------------------------------ */ /* Function: ipf_matchicmpqueryreply */ /* Returns: int - 1 if "icmp" is a valid reply to "ic" else 0. */ /* Parameters: v(I) - IP protocol version (4 or 6) */ /* ic(I) - ICMP information */ /* icmp(I) - ICMP packet header */ /* rev(I) - direction (0 = forward/1 = reverse) of packet */ /* */ /* Check if the ICMP packet defined by the header pointed to by icmp is a */ /* reply to one as described by what's in ic. If it is a match, return 1, */ /* else return 0 for no match. */ /* ------------------------------------------------------------------------ */ int ipf_matchicmpqueryreply(int v, icmpinfo_t *ic, icmphdr_t *icmp, int rev) { int ictype; ictype = ic->ici_type; if (v == 4) { /* * If we matched its type on the way in, then when going out * it will still be the same type. */ if ((!rev && (icmp->icmp_type == ictype)) || (rev && (icmpreplytype4[ictype] == icmp->icmp_type))) { if (icmp->icmp_type != ICMP_ECHOREPLY) return (1); if (icmp->icmp_id == ic->ici_id) return (1); } } #ifdef USE_INET6 else if (v == 6) { if ((!rev && (icmp->icmp_type == ictype)) || (rev && (icmpreplytype6[ictype] == icmp->icmp_type))) { if (icmp->icmp_type != ICMP6_ECHO_REPLY) return (1); if (icmp->icmp_id == ic->ici_id) return (1); } } #endif return (0); } /* * IFNAMES are located in the variable length field starting at * frentry.fr_names. As pointers within the struct cannot be passed * to the kernel from ipf(8), an offset is used. An offset of -1 means it * is unused (invalid). If it is used (valid) it is an offset to the * character string of an interface name or a comment. The following * macros will assist those who follow to understand the code. */ #define IPF_IFNAME_VALID(_a) (_a != -1) #define IPF_IFNAME_INVALID(_a) (_a == -1) #define IPF_IFNAMES_DIFFERENT(_a) \ !((IPF_IFNAME_INVALID(fr1->_a) && \ IPF_IFNAME_INVALID(fr2->_a)) || \ (IPF_IFNAME_VALID(fr1->_a) && \ IPF_IFNAME_VALID(fr2->_a) && \ !strcmp(FR_NAME(fr1, _a), FR_NAME(fr2, _a)))) #define IPF_FRDEST_DIFFERENT(_a) \ (memcmp(&fr1->_a.fd_addr, &fr2->_a.fd_addr, \ offsetof(frdest_t, fd_name) - offsetof(frdest_t, fd_addr)) || \ IPF_IFNAMES_DIFFERENT(_a.fd_name)) /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_compare */ /* Parameters: fr1(I) - first rule structure to compare */ /* fr2(I) - second rule structure to compare */ /* Returns: int - 0 == rules are the same, else mismatch */ /* */ /* Compare two rules and return 0 if they match or a number indicating */ /* which of the individual checks failed. */ /* ------------------------------------------------------------------------ */ static int ipf_rule_compare(frentry_t *fr1, frentry_t *fr2) { int i; if (fr1->fr_cksum != fr2->fr_cksum) return (1); if (fr1->fr_size != fr2->fr_size) return (2); if (fr1->fr_dsize != fr2->fr_dsize) return (3); if (bcmp((char *)&fr1->fr_func, (char *)&fr2->fr_func, FR_CMPSIZ) != 0) return (4); /* * XXX: There is still a bug here as different rules with the * the same interfaces but in a different order will compare * differently. But since multiple interfaces in a rule doesn't * work anyway a simple straightforward compare is performed * here. Ultimately frentry_t creation will need to be * revisited in ipf_y.y. While the other issue, recognition * of only the first interface in a list of interfaces will * need to be separately addressed along with why only four. */ for (i = 0; i < FR_NUM(fr1->fr_ifnames); i++) { /* * XXX: It's either the same index or uninitialized. * We assume this because multiple interfaces * referenced by the same rule doesn't work anyway. */ if (IPF_IFNAMES_DIFFERENT(fr_ifnames[i])) return (5); } if (IPF_FRDEST_DIFFERENT(fr_tif)) return (6); if (IPF_FRDEST_DIFFERENT(fr_rif)) return (7); if (IPF_FRDEST_DIFFERENT(fr_dif)) return (8); if (!fr1->fr_data && !fr2->fr_data) return (0); /* move along, nothing to see here */ if (fr1->fr_data && fr2->fr_data) { if (bcmp(fr1->fr_caddr, fr2->fr_caddr, fr1->fr_dsize) == 0) return (0); /* same */ } return (9); } /* ------------------------------------------------------------------------ */ /* Function: frrequest */ /* Returns: int - 0 == success, > 0 == errno value */ /* Parameters: unit(I) - device for which this is for */ /* req(I) - ioctl command (SIOC*) */ /* data(I) - pointr to ioctl data */ /* set(I) - 1 or 0 (filter set) */ /* makecopy(I) - flag indicating whether data points to a rule */ /* in kernel space & hence doesn't need copying. */ /* */ /* This function handles all the requests which operate on the list of */ /* filter rules. This includes adding, deleting, insertion. It is also */ /* responsible for creating groups when a "head" rule is loaded. Interface */ /* names are resolved here and other sanity checks are made on the content */ /* of the rule structure being loaded. If a rule has user defined timeouts */ /* then make sure they are created and initialised before exiting. */ /* ------------------------------------------------------------------------ */ int frrequest(ipf_main_softc_t *softc, int unit, ioctlcmd_t req, caddr_t data, int set, int makecopy) { int error = 0, in, family, need_free = 0; enum { OP_ADD, /* add rule */ OP_REM, /* remove rule */ OP_ZERO /* zero statistics and counters */ } addrem = OP_ADD; frentry_t frd, *fp, *f, **fprev, **ftail; void *ptr, *uptr, *cptr; u_int *p, *pp; frgroup_t *fg; char *group; ptr = NULL; cptr = NULL; fg = NULL; fp = &frd; if (makecopy != 0) { bzero(fp, sizeof(frd)); error = ipf_inobj(softc, data, NULL, fp, IPFOBJ_FRENTRY); if (error) { return (error); } if ((fp->fr_type & FR_T_BUILTIN) != 0) { IPFERROR(6); return (EINVAL); } KMALLOCS(f, frentry_t *, fp->fr_size); if (f == NULL) { IPFERROR(131); return (ENOMEM); } bzero(f, fp->fr_size); error = ipf_inobjsz(softc, data, f, IPFOBJ_FRENTRY, fp->fr_size); if (error) { KFREES(f, fp->fr_size); return (error); } fp = f; f = NULL; fp->fr_next = NULL; fp->fr_dnext = NULL; fp->fr_pnext = NULL; fp->fr_pdnext = NULL; fp->fr_grp = NULL; fp->fr_grphead = NULL; fp->fr_icmpgrp = NULL; fp->fr_isc = (void *)-1; fp->fr_ptr = NULL; fp->fr_ref = 0; fp->fr_flags |= FR_COPIED; } else { fp = (frentry_t *)data; if ((fp->fr_type & FR_T_BUILTIN) == 0) { IPFERROR(7); return (EINVAL); } fp->fr_flags &= ~FR_COPIED; } if (((fp->fr_dsize == 0) && (fp->fr_data != NULL)) || ((fp->fr_dsize != 0) && (fp->fr_data == NULL))) { IPFERROR(8); error = EINVAL; goto donenolock; } family = fp->fr_family; uptr = fp->fr_data; if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR || req == (ioctlcmd_t)SIOCADAFR || req == (ioctlcmd_t)SIOCADIFR) addrem = OP_ADD; /* Add rule */ else if (req == (ioctlcmd_t)SIOCRMAFR || req == (ioctlcmd_t)SIOCRMIFR) addrem = OP_REM; /* Remove rule */ else if (req == (ioctlcmd_t)SIOCZRLST) addrem = OP_ZERO; /* Zero statistics and counters */ else { IPFERROR(9); error = EINVAL; goto donenolock; } /* * Only filter rules for IPv4 or IPv6 are accepted. */ if (family == AF_INET) { /*EMPTY*/; #ifdef USE_INET6 } else if (family == AF_INET6) { /*EMPTY*/; #endif } else if (family != 0) { IPFERROR(10); error = EINVAL; goto donenolock; } /* * If the rule is being loaded from user space, i.e. we had to copy it * into kernel space, then do not trust the function pointer in the * rule. */ if ((makecopy == 1) && (fp->fr_func != NULL)) { if (ipf_findfunc(fp->fr_func) == NULL) { IPFERROR(11); error = ESRCH; goto donenolock; } if (addrem == OP_ADD) { error = ipf_funcinit(softc, fp); if (error != 0) goto donenolock; } } if ((fp->fr_flags & FR_CALLNOW) && ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { IPFERROR(142); error = ESRCH; goto donenolock; } if (((fp->fr_flags & FR_CMDMASK) == FR_CALL) && ((fp->fr_func == NULL) || (fp->fr_func == (ipfunc_t)-1))) { IPFERROR(143); error = ESRCH; goto donenolock; } ptr = NULL; cptr = NULL; if (FR_ISACCOUNT(fp->fr_flags)) unit = IPL_LOGCOUNT; /* * Check that each group name in the rule has a start index that * is valid. */ if (fp->fr_icmphead != -1) { if ((fp->fr_icmphead < 0) || (fp->fr_icmphead >= fp->fr_namelen)) { IPFERROR(136); error = EINVAL; goto donenolock; } if (!strcmp(FR_NAME(fp, fr_icmphead), "0")) fp->fr_names[fp->fr_icmphead] = '\0'; } if (fp->fr_grhead != -1) { if ((fp->fr_grhead < 0) || (fp->fr_grhead >= fp->fr_namelen)) { IPFERROR(137); error = EINVAL; goto donenolock; } if (!strcmp(FR_NAME(fp, fr_grhead), "0")) fp->fr_names[fp->fr_grhead] = '\0'; } if (fp->fr_group != -1) { if ((fp->fr_group < 0) || (fp->fr_group >= fp->fr_namelen)) { IPFERROR(138); error = EINVAL; goto donenolock; } if ((req != (int)SIOCZRLST) && (fp->fr_group != -1)) { /* * Allow loading rules that are in groups to cause * them to be created if they don't already exit. */ group = FR_NAME(fp, fr_group); if (addrem == OP_ADD) { fg = ipf_group_add(softc, group, NULL, fp->fr_flags, unit, set); fp->fr_grp = fg; } else { fg = ipf_findgroup(softc, group, unit, set, NULL); if (fg == NULL) { IPFERROR(12); error = ESRCH; goto donenolock; } } if (fg->fg_flags == 0) { fg->fg_flags = fp->fr_flags & FR_INOUT; } else if (fg->fg_flags != (fp->fr_flags & FR_INOUT)) { IPFERROR(13); error = ESRCH; goto donenolock; } } } else { /* * If a rule is going to be part of a group then it does * not matter whether it is an in or out rule, but if it * isn't in a group, then it does... */ if ((fp->fr_flags & (FR_INQUE|FR_OUTQUE)) == 0) { IPFERROR(14); error = EINVAL; goto donenolock; } } in = (fp->fr_flags & FR_INQUE) ? 0 : 1; /* * Work out which rule list this change is being applied to. */ ftail = NULL; fprev = NULL; if (unit == IPL_LOGAUTH) { if ((fp->fr_tifs[0].fd_ptr != NULL) || (fp->fr_tifs[1].fd_ptr != NULL) || (fp->fr_dif.fd_ptr != NULL) || (fp->fr_flags & FR_FASTROUTE)) { softc->ipf_interror = 145; error = EINVAL; goto donenolock; } fprev = ipf_auth_rulehead(softc); } else { if (FR_ISACCOUNT(fp->fr_flags)) fprev = &softc->ipf_acct[in][set]; else if ((fp->fr_flags & (FR_OUTQUE|FR_INQUE)) != 0) fprev = &softc->ipf_rules[in][set]; } if (fprev == NULL) { IPFERROR(15); error = ESRCH; goto donenolock; } if (fg != NULL) fprev = &fg->fg_start; /* * Copy in extra data for the rule. */ if (fp->fr_dsize != 0) { if (makecopy != 0) { KMALLOCS(ptr, void *, fp->fr_dsize); if (ptr == NULL) { IPFERROR(16); error = ENOMEM; goto donenolock; } /* * The bcopy case is for when the data is appended * to the rule by ipf_in_compat(). */ if (uptr >= (void *)fp && uptr < (void *)((char *)fp + fp->fr_size)) { bcopy(uptr, ptr, fp->fr_dsize); error = 0; } else { error = COPYIN(uptr, ptr, fp->fr_dsize); if (error != 0) { IPFERROR(17); error = EFAULT; goto donenolock; } } } else { ptr = uptr; } fp->fr_data = ptr; } else { fp->fr_data = NULL; } /* * Perform per-rule type sanity checks of their members. * All code after this needs to be aware that allocated memory * may need to be free'd before exiting. */ switch (fp->fr_type & ~FR_T_BUILTIN) { #if defined(IPFILTER_BPF) case FR_T_BPFOPC : if (fp->fr_dsize == 0) { IPFERROR(19); error = EINVAL; break; } if (!bpf_validate(ptr, fp->fr_dsize/sizeof(struct bpf_insn))) { IPFERROR(20); error = EINVAL; break; } break; #endif case FR_T_IPF : /* * Preparation for error case at the bottom of this function. */ if (fp->fr_datype == FRI_LOOKUP) fp->fr_dstptr = NULL; if (fp->fr_satype == FRI_LOOKUP) fp->fr_srcptr = NULL; if (fp->fr_dsize != sizeof(fripf_t)) { IPFERROR(21); error = EINVAL; break; } /* * Allowing a rule with both "keep state" and "with oow" is * pointless because adding a state entry to the table will * fail with the out of window (oow) flag set. */ if ((fp->fr_flags & FR_KEEPSTATE) && (fp->fr_flx & FI_OOW)) { IPFERROR(22); error = EINVAL; break; } switch (fp->fr_satype) { case FRI_BROADCAST : case FRI_DYNAMIC : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (fp->fr_sifpidx < 0) { IPFERROR(23); error = EINVAL; } break; case FRI_LOOKUP : fp->fr_srcptr = ipf_findlookup(softc, unit, fp, &fp->fr_src6, &fp->fr_smsk6); if (fp->fr_srcfunc == NULL) { IPFERROR(132); error = ESRCH; break; } break; case FRI_NORMAL : break; default : IPFERROR(133); error = EINVAL; break; } if (error != 0) break; switch (fp->fr_datype) { case FRI_BROADCAST : case FRI_DYNAMIC : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (fp->fr_difpidx < 0) { IPFERROR(24); error = EINVAL; } break; case FRI_LOOKUP : fp->fr_dstptr = ipf_findlookup(softc, unit, fp, &fp->fr_dst6, &fp->fr_dmsk6); if (fp->fr_dstfunc == NULL) { IPFERROR(134); error = ESRCH; } break; case FRI_NORMAL : break; default : IPFERROR(135); error = EINVAL; } break; case FR_T_NONE : case FR_T_CALLFUNC : case FR_T_COMPIPF : break; case FR_T_IPFEXPR : if (ipf_matcharray_verify(fp->fr_data, fp->fr_dsize) == -1) { IPFERROR(25); error = EINVAL; } break; default : IPFERROR(26); error = EINVAL; break; } if (error != 0) goto donenolock; if (fp->fr_tif.fd_name != -1) { if ((fp->fr_tif.fd_name < 0) || (fp->fr_tif.fd_name >= fp->fr_namelen)) { IPFERROR(139); error = EINVAL; goto donenolock; } } if (fp->fr_dif.fd_name != -1) { if ((fp->fr_dif.fd_name < 0) || (fp->fr_dif.fd_name >= fp->fr_namelen)) { IPFERROR(140); error = EINVAL; goto donenolock; } } if (fp->fr_rif.fd_name != -1) { if ((fp->fr_rif.fd_name < 0) || (fp->fr_rif.fd_name >= fp->fr_namelen)) { IPFERROR(141); error = EINVAL; goto donenolock; } } /* * Lookup all the interface names that are part of the rule. */ error = ipf_synclist(softc, fp, NULL); if (error != 0) goto donenolock; fp->fr_statecnt = 0; if (fp->fr_srctrack.ht_max_nodes != 0) ipf_rb_ht_init(&fp->fr_srctrack); /* * Look for an existing matching filter rule, but don't include the * next or interface pointer in the comparison (fr_next, fr_ifa). * This elminates rules which are indentical being loaded. Checksum * the constant part of the filter rule to make comparisons quicker * (this meaning no pointers are included). */ pp = (u_int *)(fp->fr_caddr + fp->fr_dsize); for (fp->fr_cksum = 0, p = (u_int *)fp->fr_data; p < pp; p++) fp->fr_cksum += *p; WRITE_ENTER(&softc->ipf_mutex); /* * Now that the filter rule lists are locked, we can walk the * chain of them without fear. */ ftail = fprev; for (f = *ftail; (f = *ftail) != NULL; ftail = &f->fr_next) { if (fp->fr_collect <= f->fr_collect) { ftail = fprev; f = NULL; break; } fprev = ftail; } for (; (f = *ftail) != NULL; ftail = &f->fr_next) { if (ipf_rule_compare(fp, f) == 0) break; } /* * If zero'ing statistics, copy current to caller and zero. */ if (addrem == OP_ZERO) { if (f == NULL) { IPFERROR(27); error = ESRCH; } else { /* * Copy and reduce lock because of impending copyout. * Well we should, but if we do then the atomicity of * this call and the correctness of fr_hits and * fr_bytes cannot be guaranteed. As it is, this code * only resets them to 0 if they are successfully * copied out into user space. */ bcopy((char *)f, (char *)fp, f->fr_size); /* MUTEX_DOWNGRADE(&softc->ipf_mutex); */ /* * When we copy this rule back out, set the data * pointer to be what it was in user space. */ fp->fr_data = uptr; error = ipf_outobj(softc, data, fp, IPFOBJ_FRENTRY); if (error == 0) { if ((f->fr_dsize != 0) && (uptr != NULL)) { error = COPYOUT(f->fr_data, uptr, f->fr_dsize); if (error == 0) { f->fr_hits = 0; f->fr_bytes = 0; } else { IPFERROR(28); error = EFAULT; } } } } if (makecopy != 0) { if (ptr != NULL) { KFREES(ptr, fp->fr_dsize); } KFREES(fp, fp->fr_size); } RWLOCK_EXIT(&softc->ipf_mutex); return (error); } if (f == NULL) { /* * At the end of this, ftail must point to the place where the * new rule is to be saved/inserted/added. * For SIOCAD*FR, this should be the last rule in the group of * rules that have equal fr_collect fields. * For SIOCIN*FR, ... */ if (req == (ioctlcmd_t)SIOCADAFR || req == (ioctlcmd_t)SIOCADIFR) { for (ftail = fprev; (f = *ftail) != NULL; ) { if (f->fr_collect > fp->fr_collect) break; ftail = &f->fr_next; fprev = ftail; } ftail = fprev; f = NULL; ptr = NULL; } else if (req == (ioctlcmd_t)SIOCINAFR || req == (ioctlcmd_t)SIOCINIFR) { while ((f = *fprev) != NULL) { if (f->fr_collect >= fp->fr_collect) break; fprev = &f->fr_next; } ftail = fprev; if (fp->fr_hits != 0) { while (fp->fr_hits && (f = *ftail)) { if (f->fr_collect != fp->fr_collect) break; fprev = ftail; ftail = &f->fr_next; fp->fr_hits--; } } f = NULL; ptr = NULL; } } /* * Request to remove a rule. */ if (addrem == OP_REM) { if (f == NULL) { IPFERROR(29); error = ESRCH; } else { /* * Do not allow activity from user space to interfere * with rules not loaded that way. */ if ((makecopy == 1) && !(f->fr_flags & FR_COPIED)) { IPFERROR(30); error = EPERM; goto done; } /* * Return EBUSY if the rule is being reference by * something else (eg state information.) */ if (f->fr_ref > 1) { IPFERROR(31); error = EBUSY; goto done; } #ifdef IPFILTER_SCAN if (f->fr_isctag != -1 && (f->fr_isc != (struct ipscan *)-1)) ipf_scan_detachfr(f); #endif if (unit == IPL_LOGAUTH) { error = ipf_auth_precmd(softc, req, f, ftail); goto done; } ipf_rule_delete(softc, f, unit, set); need_free = makecopy; } } else { /* * Not removing, so we must be adding/inserting a rule. */ if (f != NULL) { IPFERROR(32); error = EEXIST; goto done; } if (unit == IPL_LOGAUTH) { error = ipf_auth_precmd(softc, req, fp, ftail); goto done; } MUTEX_NUKE(&fp->fr_lock); MUTEX_INIT(&fp->fr_lock, "filter rule lock"); if (fp->fr_die != 0) ipf_rule_expire_insert(softc, fp, set); fp->fr_hits = 0; if (makecopy != 0) fp->fr_ref = 1; fp->fr_pnext = ftail; fp->fr_next = *ftail; if (fp->fr_next != NULL) fp->fr_next->fr_pnext = &fp->fr_next; *ftail = fp; ipf_fixskip(ftail, fp, 1); fp->fr_icmpgrp = NULL; if (fp->fr_icmphead != -1) { group = FR_NAME(fp, fr_icmphead); fg = ipf_group_add(softc, group, fp, 0, unit, set); fp->fr_icmpgrp = fg; } fp->fr_grphead = NULL; if (fp->fr_grhead != -1) { group = FR_NAME(fp, fr_grhead); fg = ipf_group_add(softc, group, fp, fp->fr_flags, unit, set); fp->fr_grphead = fg; } } done: RWLOCK_EXIT(&softc->ipf_mutex); donenolock: if (need_free || (error != 0)) { if ((fp->fr_type & ~FR_T_BUILTIN) == FR_T_IPF) { if ((fp->fr_satype == FRI_LOOKUP) && (fp->fr_srcptr != NULL)) ipf_lookup_deref(softc, fp->fr_srctype, fp->fr_srcptr); if ((fp->fr_datype == FRI_LOOKUP) && (fp->fr_dstptr != NULL)) ipf_lookup_deref(softc, fp->fr_dsttype, fp->fr_dstptr); } if (fp->fr_grp != NULL) { WRITE_ENTER(&softc->ipf_mutex); ipf_group_del(softc, fp->fr_grp, fp); RWLOCK_EXIT(&softc->ipf_mutex); } if ((ptr != NULL) && (makecopy != 0)) { KFREES(ptr, fp->fr_dsize); } KFREES(fp, fp->fr_size); } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_delete */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* f(I) - pointer to the rule being deleted */ /* ftail(I) - pointer to the pointer to f */ /* unit(I) - device for which this is for */ /* set(I) - 1 or 0 (filter set) */ /* */ /* This function attempts to do what it can to delete a filter rule: remove */ /* it from any linked lists and remove any groups it is responsible for. */ /* But in the end, removing a rule can only drop the reference count - we */ /* must use that as the guide for whether or not it can be freed. */ /* ------------------------------------------------------------------------ */ static void ipf_rule_delete(ipf_main_softc_t *softc, frentry_t *f, int unit, int set) { /* * If fr_pdnext is set, then the rule is on the expire list, so * remove it from there. */ if (f->fr_pdnext != NULL) { *f->fr_pdnext = f->fr_dnext; if (f->fr_dnext != NULL) f->fr_dnext->fr_pdnext = f->fr_pdnext; f->fr_pdnext = NULL; f->fr_dnext = NULL; } ipf_fixskip(f->fr_pnext, f, -1); if (f->fr_pnext != NULL) *f->fr_pnext = f->fr_next; if (f->fr_next != NULL) f->fr_next->fr_pnext = f->fr_pnext; f->fr_pnext = NULL; f->fr_next = NULL; (void) ipf_derefrule(softc, &f); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_expire_insert */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* f(I) - pointer to rule to be added to expire list */ /* set(I) - 1 or 0 (filter set) */ /* */ /* If the new rule has a given expiration time, insert it into the list of */ /* expiring rules with the ones to be removed first added to the front of */ /* the list. The insertion is O(n) but it is kept sorted for quick scans at */ /* expiration interval checks. */ /* ------------------------------------------------------------------------ */ static void ipf_rule_expire_insert(ipf_main_softc_t *softc, frentry_t *f, int set) { frentry_t *fr; /* */ f->fr_die = softc->ipf_ticks + IPF_TTLVAL(f->fr_die); for (fr = softc->ipf_rule_explist[set]; fr != NULL; fr = fr->fr_dnext) { if (f->fr_die < fr->fr_die) break; if (fr->fr_dnext == NULL) { /* * We've got to the last rule and everything * wanted to be expired before this new node, * so we have to tack it on the end... */ fr->fr_dnext = f; f->fr_pdnext = &fr->fr_dnext; fr = NULL; break; } } if (softc->ipf_rule_explist[set] == NULL) { softc->ipf_rule_explist[set] = f; f->fr_pdnext = &softc->ipf_rule_explist[set]; } else if (fr != NULL) { f->fr_dnext = fr; f->fr_pdnext = fr->fr_pdnext; fr->fr_pdnext = &f->fr_dnext; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_findlookup */ /* Returns: NULL = failure, else success */ /* Parameters: softc(I) - pointer to soft context main structure */ /* unit(I) - ipf device we want to find match for */ /* fp(I) - rule for which lookup is for */ /* addrp(I) - pointer to lookup information in address struct */ /* maskp(O) - pointer to lookup information for storage */ /* */ /* When using pools and hash tables to store addresses for matching in */ /* rules, it is necessary to resolve both the object referred to by the */ /* name or address (and return that pointer) and also provide the means by */ /* which to determine if an address belongs to that object to make the */ /* packet matching quicker. */ /* ------------------------------------------------------------------------ */ static void * ipf_findlookup(ipf_main_softc_t *softc, int unit, frentry_t *fr, i6addr_t *addrp, i6addr_t *maskp) { void *ptr = NULL; switch (addrp->iplookupsubtype) { case 0 : ptr = ipf_lookup_res_num(softc, unit, addrp->iplookuptype, addrp->iplookupnum, &maskp->iplookupfunc); break; case 1 : if (addrp->iplookupname < 0) break; if (addrp->iplookupname >= fr->fr_namelen) break; ptr = ipf_lookup_res_name(softc, unit, addrp->iplookuptype, fr->fr_names + addrp->iplookupname, &maskp->iplookupfunc); break; default : break; } return (ptr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_funcinit */ /* Returns: int - 0 == success, else ESRCH: cannot resolve rule details */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to filter rule */ /* */ /* If a rule is a call rule, then check if the function it points to needs */ /* an init function to be called now the rule has been loaded. */ /* ------------------------------------------------------------------------ */ static int ipf_funcinit(ipf_main_softc_t *softc, frentry_t *fr) { ipfunc_resolve_t *ft; int err; IPFERROR(34); err = ESRCH; for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == fr->fr_func) { err = 0; if (ft->ipfu_init != NULL) err = (*ft->ipfu_init)(softc, fr); break; } return (err); } /* ------------------------------------------------------------------------ */ /* Function: ipf_funcfini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to filter rule */ /* */ /* For a given filter rule, call the matching "fini" function if the rule */ /* is using a known function that would have resulted in the "init" being */ /* called for ealier. */ /* ------------------------------------------------------------------------ */ static void ipf_funcfini(ipf_main_softc_t *softc, frentry_t *fr) { ipfunc_resolve_t *ft; for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == fr->fr_func) { if (ft->ipfu_fini != NULL) (void) (*ft->ipfu_fini)(softc, fr); break; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_findfunc */ /* Returns: ipfunc_t - pointer to function if found, else NULL */ /* Parameters: funcptr(I) - function pointer to lookup */ /* */ /* Look for a function in the table of known functions. */ /* ------------------------------------------------------------------------ */ static ipfunc_t ipf_findfunc(ipfunc_t funcptr) { ipfunc_resolve_t *ft; for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == funcptr) return (funcptr); return (NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_resolvefunc */ /* Returns: int - 0 == success, else error */ /* Parameters: data(IO) - ioctl data pointer to ipfunc_resolve_t struct */ /* */ /* Copy in a ipfunc_resolve_t structure and then fill in the missing field. */ /* This will either be the function name (if the pointer is set) or the */ /* function pointer if the name is set. When found, fill in the other one */ /* so that the entire, complete, structure can be copied back to user space.*/ /* ------------------------------------------------------------------------ */ int ipf_resolvefunc(ipf_main_softc_t *softc, void *data) { ipfunc_resolve_t res, *ft; int error; error = BCOPYIN(data, &res, sizeof(res)); if (error != 0) { IPFERROR(123); return (EFAULT); } if (res.ipfu_addr == NULL && res.ipfu_name[0] != '\0') { for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (strncmp(res.ipfu_name, ft->ipfu_name, sizeof(res.ipfu_name)) == 0) { res.ipfu_addr = ft->ipfu_addr; res.ipfu_init = ft->ipfu_init; if (COPYOUT(&res, data, sizeof(res)) != 0) { IPFERROR(35); return (EFAULT); } return (0); } } if (res.ipfu_addr != NULL && res.ipfu_name[0] == '\0') { for (ft = ipf_availfuncs; ft->ipfu_addr != NULL; ft++) if (ft->ipfu_addr == res.ipfu_addr) { (void) strncpy(res.ipfu_name, ft->ipfu_name, sizeof(res.ipfu_name)); res.ipfu_init = ft->ipfu_init; if (COPYOUT(&res, data, sizeof(res)) != 0) { IPFERROR(36); return (EFAULT); } return (0); } } IPFERROR(37); return (ESRCH); } #if !defined(_KERNEL) || SOLARIS /* * From: NetBSD * ppsratecheck(): packets (or events) per second limitation. */ int ppsratecheck(struct timeval *lasttime, int *curpps, int maxpps) /* maxpps: maximum pps allowed */ { struct timeval tv, delta; int rv; GETKTIME(&tv); delta.tv_sec = tv.tv_sec - lasttime->tv_sec; delta.tv_usec = tv.tv_usec - lasttime->tv_usec; if (delta.tv_usec < 0) { delta.tv_sec--; delta.tv_usec += 1000000; } /* * check for 0,0 is so that the message will be seen at least once. * if more than one second have passed since the last update of * lasttime, reset the counter. * * we do increment *curpps even in *curpps < maxpps case, as some may * try to use *curpps for stat purposes as well. */ if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) || delta.tv_sec >= 1) { *lasttime = tv; *curpps = 0; rv = 1; } else if (maxpps < 0) rv = 1; else if (*curpps < maxpps) rv = 1; else rv = 0; *curpps = *curpps + 1; return (rv); } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_derefrule */ /* Returns: int - 0 == rule freed up, else rule not freed */ /* Parameters: fr(I) - pointer to filter rule */ /* */ /* Decrement the reference counter to a rule by one. If it reaches zero, */ /* free it and any associated storage space being used by it. */ /* ------------------------------------------------------------------------ */ int ipf_derefrule(ipf_main_softc_t *softc, frentry_t **frp) { frentry_t *fr; frdest_t *fdp; fr = *frp; *frp = NULL; MUTEX_ENTER(&fr->fr_lock); fr->fr_ref--; if (fr->fr_ref == 0) { MUTEX_EXIT(&fr->fr_lock); MUTEX_DESTROY(&fr->fr_lock); ipf_funcfini(softc, fr); fdp = &fr->fr_tif; if (fdp->fd_type == FRD_DSTLIST) ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); fdp = &fr->fr_rif; if (fdp->fd_type == FRD_DSTLIST) ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); fdp = &fr->fr_dif; if (fdp->fd_type == FRD_DSTLIST) ipf_lookup_deref(softc, IPLT_DSTLIST, fdp->fd_ptr); if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && fr->fr_satype == FRI_LOOKUP) ipf_lookup_deref(softc, fr->fr_srctype, fr->fr_srcptr); if ((fr->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && fr->fr_datype == FRI_LOOKUP) ipf_lookup_deref(softc, fr->fr_dsttype, fr->fr_dstptr); if (fr->fr_grp != NULL) ipf_group_del(softc, fr->fr_grp, fr); if (fr->fr_grphead != NULL) ipf_group_del(softc, fr->fr_grphead, fr); if (fr->fr_icmpgrp != NULL) ipf_group_del(softc, fr->fr_icmpgrp, fr); if ((fr->fr_flags & FR_COPIED) != 0) { if (fr->fr_dsize) { KFREES(fr->fr_data, fr->fr_dsize); } KFREES(fr, fr->fr_size); return (0); } return (1); } else { MUTEX_EXIT(&fr->fr_lock); } return (-1); } /* ------------------------------------------------------------------------ */ /* Function: ipf_grpmapinit */ /* Returns: int - 0 == success, else ESRCH because table entry not found*/ /* Parameters: fr(I) - pointer to rule to find hash table for */ /* */ /* Looks for group hash table fr_arg and stores a pointer to it in fr_ptr. */ /* fr_ptr is later used by ipf_srcgrpmap and ipf_dstgrpmap. */ /* ------------------------------------------------------------------------ */ static int ipf_grpmapinit(ipf_main_softc_t *softc, frentry_t *fr) { char name[FR_GROUPLEN]; iphtable_t *iph; (void) snprintf(name, sizeof(name), "%d", fr->fr_arg); iph = ipf_lookup_find_htable(softc, IPL_LOGIPF, name); if (iph == NULL) { IPFERROR(38); return (ESRCH); } if ((iph->iph_flags & FR_INOUT) != (fr->fr_flags & FR_INOUT)) { IPFERROR(39); return (ESRCH); } iph->iph_ref++; fr->fr_ptr = iph; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_grpmapfini */ /* Returns: int - 0 == success, else ESRCH because table entry not found*/ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to rule to release hash table for */ /* */ /* For rules that have had ipf_grpmapinit called, ipf_lookup_deref needs to */ /* be called to undo what ipf_grpmapinit caused to be done. */ /* ------------------------------------------------------------------------ */ static int ipf_grpmapfini(ipf_main_softc_t *softc, frentry_t *fr) { iphtable_t *iph; iph = fr->fr_ptr; if (iph != NULL) ipf_lookup_deref(softc, IPLT_HASH, iph); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_srcgrpmap */ /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Look for a rule group head in a hash table, using the source address as */ /* the key, and descend into that group and continue matching rules against */ /* the packet. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_srcgrpmap(fr_info_t *fin, u_32_t *passp) { frgroup_t *fg; void *rval; rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, &fin->fin_src); if (rval == NULL) return (NULL); fg = rval; fin->fin_fr = fg->fg_start; (void) ipf_scanlist(fin, *passp); return (fin->fin_fr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_dstgrpmap */ /* Returns: frentry_t * - pointer to "new last matching" rule or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* passp(IO) - pointer to current/new filter decision (unused) */ /* */ /* Look for a rule group head in a hash table, using the destination */ /* address as the key, and descend into that group and continue matching */ /* rules against the packet. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_dstgrpmap(fr_info_t *fin, u_32_t *passp) { frgroup_t *fg; void *rval; rval = ipf_iphmfindgroup(fin->fin_main_soft, fin->fin_fr->fr_ptr, &fin->fin_dst); if (rval == NULL) return (NULL); fg = rval; fin->fin_fr = fg->fg_start; (void) ipf_scanlist(fin, *passp); return (fin->fin_fr); } /* * Queue functions * =============== * These functions manage objects on queues for efficient timeouts. There * are a number of system defined queues as well as user defined timeouts. * It is expected that a lock is held in the domain in which the queue * belongs (i.e. either state or NAT) when calling any of these functions * that prevents ipf_freetimeoutqueue() from being called at the same time * as any other. */ /* ------------------------------------------------------------------------ */ /* Function: ipf_addtimeoutqueue */ /* Returns: struct ifqtq * - NULL if malloc fails, else pointer to */ /* timeout queue with given interval. */ /* Parameters: parent(I) - pointer to pointer to parent node of this list */ /* of interface queues. */ /* seconds(I) - timeout value in seconds for this queue. */ /* */ /* This routine first looks for a timeout queue that matches the interval */ /* being requested. If it finds one, increments the reference counter and */ /* returns a pointer to it. If none are found, it allocates a new one and */ /* inserts it at the top of the list. */ /* */ /* Locking. */ /* It is assumed that the caller of this function has an appropriate lock */ /* held (exclusively) in the domain that encompases 'parent'. */ /* ------------------------------------------------------------------------ */ ipftq_t * ipf_addtimeoutqueue(ipf_main_softc_t *softc, ipftq_t **parent, u_int seconds) { ipftq_t *ifq; u_int period; period = seconds * IPF_HZ_DIVIDE; MUTEX_ENTER(&softc->ipf_timeoutlock); for (ifq = *parent; ifq != NULL; ifq = ifq->ifq_next) { if (ifq->ifq_ttl == period) { /* * Reset the delete flag, if set, so the structure * gets reused rather than freed and reallocated. */ MUTEX_ENTER(&ifq->ifq_lock); ifq->ifq_flags &= ~IFQF_DELETE; ifq->ifq_ref++; MUTEX_EXIT(&ifq->ifq_lock); MUTEX_EXIT(&softc->ipf_timeoutlock); return (ifq); } } KMALLOC(ifq, ipftq_t *); if (ifq != NULL) { MUTEX_NUKE(&ifq->ifq_lock); IPFTQ_INIT(ifq, period, "ipftq mutex"); ifq->ifq_next = *parent; ifq->ifq_pnext = parent; ifq->ifq_flags = IFQF_USER; ifq->ifq_ref++; *parent = ifq; softc->ipf_userifqs++; } MUTEX_EXIT(&softc->ipf_timeoutlock); return (ifq); } /* ------------------------------------------------------------------------ */ /* Function: ipf_deletetimeoutqueue */ /* Returns: int - new reference count value of the timeout queue */ /* Parameters: ifq(I) - timeout queue which is losing a reference. */ /* Locks: ifq->ifq_lock */ /* */ /* This routine must be called when we're discarding a pointer to a timeout */ /* queue object, taking care of the reference counter. */ /* */ /* Now that this just sets a DELETE flag, it requires the expire code to */ /* check the list of user defined timeout queues and call the free function */ /* below (currently commented out) to stop memory leaking. It is done this */ /* way because the locking may not be sufficient to safely do a free when */ /* this function is called. */ /* ------------------------------------------------------------------------ */ int ipf_deletetimeoutqueue(ipftq_t *ifq) { ifq->ifq_ref--; if ((ifq->ifq_ref == 0) && ((ifq->ifq_flags & IFQF_USER) != 0)) { ifq->ifq_flags |= IFQF_DELETE; } return (ifq->ifq_ref); } /* ------------------------------------------------------------------------ */ /* Function: ipf_freetimeoutqueue */ /* Parameters: ifq(I) - timeout queue which is losing a reference. */ /* Returns: Nil */ /* */ /* Locking: */ /* It is assumed that the caller of this function has an appropriate lock */ /* held (exclusively) in the domain that encompases the callers "domain". */ /* The ifq_lock for this structure should not be held. */ /* */ /* Remove a user defined timeout queue from the list of queues it is in and */ /* tidy up after this is done. */ /* ------------------------------------------------------------------------ */ void ipf_freetimeoutqueue(ipf_main_softc_t *softc, ipftq_t *ifq) { if (((ifq->ifq_flags & IFQF_DELETE) == 0) || (ifq->ifq_ref != 0) || ((ifq->ifq_flags & IFQF_USER) == 0)) { printf("ipf_freetimeoutqueue(%lx) flags 0x%x ttl %d ref %d\n", (u_long)ifq, ifq->ifq_flags, ifq->ifq_ttl, ifq->ifq_ref); return; } /* * Remove from its position in the list. */ *ifq->ifq_pnext = ifq->ifq_next; if (ifq->ifq_next != NULL) ifq->ifq_next->ifq_pnext = ifq->ifq_pnext; ifq->ifq_next = NULL; ifq->ifq_pnext = NULL; MUTEX_DESTROY(&ifq->ifq_lock); ATOMIC_DEC(softc->ipf_userifqs); KFREE(ifq); } /* ------------------------------------------------------------------------ */ /* Function: ipf_deletequeueentry */ /* Returns: Nil */ /* Parameters: tqe(I) - timeout queue entry to delete */ /* */ /* Remove a tail queue entry from its queue and make it an orphan. */ /* ipf_deletetimeoutqueue is called to make sure the reference count on the */ /* queue is correct. We can't, however, call ipf_freetimeoutqueue because */ /* the correct lock(s) may not be held that would make it safe to do so. */ /* ------------------------------------------------------------------------ */ void ipf_deletequeueentry(ipftqent_t *tqe) { ipftq_t *ifq; ifq = tqe->tqe_ifq; MUTEX_ENTER(&ifq->ifq_lock); if (tqe->tqe_pnext != NULL) { *tqe->tqe_pnext = tqe->tqe_next; if (tqe->tqe_next != NULL) tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; else /* we must be the tail anyway */ ifq->ifq_tail = tqe->tqe_pnext; tqe->tqe_pnext = NULL; tqe->tqe_ifq = NULL; } (void) ipf_deletetimeoutqueue(ifq); ASSERT(ifq->ifq_ref > 0); MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_queuefront */ /* Returns: Nil */ /* Parameters: tqe(I) - pointer to timeout queue entry */ /* */ /* Move a queue entry to the front of the queue, if it isn't already there. */ /* ------------------------------------------------------------------------ */ void ipf_queuefront(ipftqent_t *tqe) { ipftq_t *ifq; ifq = tqe->tqe_ifq; if (ifq == NULL) return; MUTEX_ENTER(&ifq->ifq_lock); if (ifq->ifq_head != tqe) { *tqe->tqe_pnext = tqe->tqe_next; if (tqe->tqe_next) tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; else ifq->ifq_tail = tqe->tqe_pnext; tqe->tqe_next = ifq->ifq_head; ifq->ifq_head->tqe_pnext = &tqe->tqe_next; ifq->ifq_head = tqe; tqe->tqe_pnext = &ifq->ifq_head; } MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_queueback */ /* Returns: Nil */ /* Parameters: ticks(I) - ipf tick time to use with this call */ /* tqe(I) - pointer to timeout queue entry */ /* */ /* Move a queue entry to the back of the queue, if it isn't already there. */ /* We use use ticks to calculate the expiration and mark for when we last */ /* touched the structure. */ /* ------------------------------------------------------------------------ */ void ipf_queueback(u_long ticks, ipftqent_t *tqe) { ipftq_t *ifq; ifq = tqe->tqe_ifq; if (ifq == NULL) return; tqe->tqe_die = ticks + ifq->ifq_ttl; tqe->tqe_touched = ticks; MUTEX_ENTER(&ifq->ifq_lock); if (tqe->tqe_next != NULL) { /* at the end already ? */ /* * Remove from list */ *tqe->tqe_pnext = tqe->tqe_next; tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; /* * Make it the last entry. */ tqe->tqe_next = NULL; tqe->tqe_pnext = ifq->ifq_tail; *ifq->ifq_tail = tqe; ifq->ifq_tail = &tqe->tqe_next; } MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_queueappend */ /* Returns: Nil */ /* Parameters: ticks(I) - ipf tick time to use with this call */ /* tqe(I) - pointer to timeout queue entry */ /* ifq(I) - pointer to timeout queue */ /* parent(I) - owing object pointer */ /* */ /* Add a new item to this queue and put it on the very end. */ /* We use use ticks to calculate the expiration and mark for when we last */ /* touched the structure. */ /* ------------------------------------------------------------------------ */ void ipf_queueappend(u_long ticks, ipftqent_t *tqe, ipftq_t *ifq, void *parent) { MUTEX_ENTER(&ifq->ifq_lock); tqe->tqe_parent = parent; tqe->tqe_pnext = ifq->ifq_tail; *ifq->ifq_tail = tqe; ifq->ifq_tail = &tqe->tqe_next; tqe->tqe_next = NULL; tqe->tqe_ifq = ifq; tqe->tqe_die = ticks + ifq->ifq_ttl; tqe->tqe_touched = ticks; ifq->ifq_ref++; MUTEX_EXIT(&ifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_movequeue */ /* Returns: Nil */ /* Parameters: tq(I) - pointer to timeout queue information */ /* oifp(I) - old timeout queue entry was on */ /* nifp(I) - new timeout queue to put entry on */ /* */ /* Move a queue entry from one timeout queue to another timeout queue. */ /* If it notices that the current entry is already last and does not need */ /* to move queue, the return. */ /* ------------------------------------------------------------------------ */ void ipf_movequeue(u_long ticks, ipftqent_t *tqe, ipftq_t *oifq, ipftq_t *nifq) { /* * If the queue hasn't changed and we last touched this entry at the * same ipf time, then we're not going to achieve anything by either * changing the ttl or moving it on the queue. */ if (oifq == nifq && tqe->tqe_touched == ticks) return; /* * For any of this to be outside the lock, there is a risk that two * packets entering simultaneously, with one changing to a different * queue and one not, could end up with things in a bizarre state. */ MUTEX_ENTER(&oifq->ifq_lock); tqe->tqe_touched = ticks; tqe->tqe_die = ticks + nifq->ifq_ttl; /* * Is the operation here going to be a no-op ? */ if (oifq == nifq) { if ((tqe->tqe_next == NULL) || (tqe->tqe_next->tqe_die == tqe->tqe_die)) { MUTEX_EXIT(&oifq->ifq_lock); return; } } /* * Remove from the old queue */ *tqe->tqe_pnext = tqe->tqe_next; if (tqe->tqe_next) tqe->tqe_next->tqe_pnext = tqe->tqe_pnext; else oifq->ifq_tail = tqe->tqe_pnext; tqe->tqe_next = NULL; /* * If we're moving from one queue to another, release the * lock on the old queue and get a lock on the new queue. * For user defined queues, if we're moving off it, call * delete in case it can now be freed. */ if (oifq != nifq) { tqe->tqe_ifq = NULL; (void) ipf_deletetimeoutqueue(oifq); MUTEX_EXIT(&oifq->ifq_lock); MUTEX_ENTER(&nifq->ifq_lock); tqe->tqe_ifq = nifq; nifq->ifq_ref++; } /* * Add to the bottom of the new queue */ tqe->tqe_pnext = nifq->ifq_tail; *nifq->ifq_tail = tqe; nifq->ifq_tail = &tqe->tqe_next; MUTEX_EXIT(&nifq->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_updateipid */ /* Returns: int - 0 == success, -1 == error (packet should be droppped) */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* When we are doing NAT, change the IP of every packet to represent a */ /* single sequence of packets coming from the host, hiding any host */ /* specific sequencing that might otherwise be revealed. If the packet is */ /* a fragment, then store the 'new' IPid in the fragment cache and look up */ /* the fragment cache for non-leading fragments. If a non-leading fragment */ /* has no match in the cache, return an error. */ /* ------------------------------------------------------------------------ */ static int ipf_updateipid(fr_info_t *fin) { u_short id, ido, sums; u_32_t sumd, sum; ip_t *ip; ip = fin->fin_ip; ido = ntohs(ip->ip_id); if (fin->fin_off != 0) { sum = ipf_frag_ipidknown(fin); if (sum == 0xffffffff) return (-1); sum &= 0xffff; id = (u_short)sum; ip->ip_id = htons(id); } else { ip_fillid(ip); id = ntohs(ip->ip_id); if ((fin->fin_flx & FI_FRAG) != 0) (void) ipf_frag_ipidnew(fin, (u_32_t)id); } if (id == ido) return (0); CALC_SUMD(ido, id, sumd); /* DESTRUCTIVE MACRO! id,ido change */ sum = (~ntohs(ip->ip_sum)) & 0xffff; sum += sumd; sum = (sum >> 16) + (sum & 0xffff); sum = (sum >> 16) + (sum & 0xffff); sums = ~(u_short)sum; ip->ip_sum = htons(sums); return (0); } #ifdef NEED_FRGETIFNAME /* ------------------------------------------------------------------------ */ /* Function: ipf_getifname */ /* Returns: char * - pointer to interface name */ /* Parameters: ifp(I) - pointer to network interface */ /* buffer(O) - pointer to where to store interface name */ /* */ /* Constructs an interface name in the buffer passed. The buffer passed is */ /* expected to be at least LIFNAMSIZ in bytes big. If buffer is passed in */ /* as a NULL pointer then return a pointer to a static array. */ /* ------------------------------------------------------------------------ */ char * ipf_getifname(struct ifnet *ifp, char *buffer) { static char namebuf[LIFNAMSIZ]; # if SOLARIS || defined(__FreeBSD__) int unit, space; char temp[20]; char *s; # endif if (buffer == NULL) buffer = namebuf; (void) strncpy(buffer, ifp->if_name, LIFNAMSIZ); buffer[LIFNAMSIZ - 1] = '\0'; # if SOLARIS || defined(__FreeBSD__) for (s = buffer; *s; s++) ; unit = ifp->if_unit; space = LIFNAMSIZ - (s - buffer); if ((space > 0) && (unit >= 0)) { (void) snprintf(temp, sizeof(name), "%d", unit); (void) strncpy(s, temp, space); } # endif return (buffer); } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_ioctlswitch */ /* Returns: int - -1 continue processing, else ioctl return value */ /* Parameters: unit(I) - device unit opened */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command */ /* mode(I) - mode value */ /* uid(I) - uid making the ioctl call */ /* ctx(I) - pointer to context data */ /* */ /* Based on the value of unit, call the appropriate ioctl handler or return */ /* EIO if ipfilter is not running. Also checks if write perms are req'd */ /* for the device in order to execute the ioctl. A special case is made */ /* SIOCIPFINTERROR so that the same code isn't required in every handler. */ /* The context data pointer is passed through as this is used as the key */ /* for locating a matching token for continued access for walking lists, */ /* etc. */ /* ------------------------------------------------------------------------ */ int ipf_ioctlswitch(ipf_main_softc_t *softc, int unit, void *data, ioctlcmd_t cmd, int mode, int uid, void *ctx) { int error = 0; switch (cmd) { case SIOCIPFINTERROR : error = BCOPYOUT(&softc->ipf_interror, data, sizeof(softc->ipf_interror)); if (error != 0) { IPFERROR(40); error = EFAULT; } return (error); default : break; } switch (unit) { case IPL_LOGIPF : error = ipf_ipf_ioctl(softc, data, cmd, mode, uid, ctx); break; case IPL_LOGNAT : if (softc->ipf_running > 0) { error = ipf_nat_ioctl(softc, data, cmd, mode, uid, ctx); } else { IPFERROR(42); error = EIO; } break; case IPL_LOGSTATE : if (softc->ipf_running > 0) { error = ipf_state_ioctl(softc, data, cmd, mode, uid, ctx); } else { IPFERROR(43); error = EIO; } break; case IPL_LOGAUTH : if (softc->ipf_running > 0) { error = ipf_auth_ioctl(softc, data, cmd, mode, uid, ctx); } else { IPFERROR(44); error = EIO; } break; case IPL_LOGSYNC : if (softc->ipf_running > 0) { error = ipf_sync_ioctl(softc, data, cmd, mode, uid, ctx); } else { error = EIO; IPFERROR(45); } break; case IPL_LOGSCAN : #ifdef IPFILTER_SCAN if (softc->ipf_running > 0) error = ipf_scan_ioctl(softc, data, cmd, mode, uid, ctx); else #endif { error = EIO; IPFERROR(46); } break; case IPL_LOGLOOKUP : if (softc->ipf_running > 0) { error = ipf_lookup_ioctl(softc, data, cmd, mode, uid, ctx); } else { error = EIO; IPFERROR(47); } break; default : IPFERROR(48); error = EIO; break; } return (error); } /* * This array defines the expected size of objects coming into the kernel * for the various recognised object types. The first column is flags (see * below), 2nd column is current size, 3rd column is the version number of * when the current size became current. * Flags: * 1 = minimum size, not absolute size */ static const int ipf_objbytes[IPFOBJ_COUNT][3] = { { 1, sizeof(struct frentry), 5010000 }, /* 0 */ { 1, sizeof(struct friostat), 5010000 }, { 0, sizeof(struct fr_info), 5010000 }, { 0, sizeof(struct ipf_authstat), 4010100 }, { 0, sizeof(struct ipfrstat), 5010000 }, { 1, sizeof(struct ipnat), 5010000 }, /* 5 */ { 0, sizeof(struct natstat), 5010000 }, { 0, sizeof(struct ipstate_save), 5010000 }, { 1, sizeof(struct nat_save), 5010000 }, { 0, sizeof(struct natlookup), 5010000 }, { 1, sizeof(struct ipstate), 5010000 }, /* 10 */ { 0, sizeof(struct ips_stat), 5010000 }, { 0, sizeof(struct frauth), 5010000 }, { 0, sizeof(struct ipftune), 4010100 }, { 0, sizeof(struct nat), 5010000 }, { 0, sizeof(struct ipfruleiter), 4011400 }, /* 15 */ { 0, sizeof(struct ipfgeniter), 4011400 }, { 0, sizeof(struct ipftable), 4011400 }, { 0, sizeof(struct ipflookupiter), 4011400 }, { 0, sizeof(struct ipftq) * IPF_TCP_NSTATES }, { 1, 0, 0 }, /* IPFEXPR */ { 0, 0, 0 }, /* PROXYCTL */ { 0, sizeof (struct fripf), 5010000 } }; /* ------------------------------------------------------------------------ */ /* Function: ipf_inobj */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* data(I) - pointer to ioctl data */ /* objp(O) - where to store ipfobj structure */ /* ptr(I) - pointer to data to copy out */ /* type(I) - type of structure being moved */ /* */ /* Copy in the contents of what the ipfobj_t points to. In future, we */ /* add things to check for version numbers, sizes, etc, to make it backward */ /* compatible at the ABI for user land. */ /* If objp is not NULL then we assume that the caller wants to see what is */ /* in the ipfobj_t structure being copied in. As an example, this can tell */ /* the caller what version of ipfilter the ioctl program was written to. */ /* ------------------------------------------------------------------------ */ int ipf_inobj(ipf_main_softc_t *softc, void *data, ipfobj_t *objp, void *ptr, int type) { ipfobj_t obj; int error; int size; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(49); return (EINVAL); } if (objp == NULL) objp = &obj; error = BCOPYIN(data, objp, sizeof(*objp)); if (error != 0) { IPFERROR(124); return (EFAULT); } if (objp->ipfo_type != type) { IPFERROR(50); return (EINVAL); } if (objp->ipfo_rev >= ipf_objbytes[type][2]) { if ((ipf_objbytes[type][0] & 1) != 0) { if (objp->ipfo_size < ipf_objbytes[type][1]) { IPFERROR(51); return (EINVAL); } size = ipf_objbytes[type][1]; } else if (objp->ipfo_size == ipf_objbytes[type][1]) { size = objp->ipfo_size; } else { IPFERROR(52); return (EINVAL); } error = COPYIN(objp->ipfo_ptr, ptr, size); if (error != 0) { IPFERROR(55); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_in_compat(softc, objp, ptr, 0); #else IPFERROR(54); error = EINVAL; #endif } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_inobjsz */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ /* type(I) - type of structure being moved */ /* sz(I) - size of data to copy */ /* */ /* As per ipf_inobj, except the size of the object to copy in is passed in */ /* but it must not be smaller than the size defined for the type and the */ /* type must allow for varied sized objects. The extra requirement here is */ /* that sz must match the size of the object being passed in - this is not */ /* not possible nor required in ipf_inobj(). */ /* ------------------------------------------------------------------------ */ int ipf_inobjsz(ipf_main_softc_t *softc, void *data, void *ptr, int type, int sz) { ipfobj_t obj; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(56); return (EINVAL); } error = BCOPYIN(data, &obj, sizeof(obj)); if (error != 0) { IPFERROR(125); return (EFAULT); } if (obj.ipfo_type != type) { IPFERROR(58); return (EINVAL); } if (obj.ipfo_rev >= ipf_objbytes[type][2]) { if (((ipf_objbytes[type][0] & 1) == 0) || (sz < ipf_objbytes[type][1])) { IPFERROR(57); return (EINVAL); } error = COPYIN(obj.ipfo_ptr, ptr, sz); if (error != 0) { IPFERROR(61); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_in_compat(softc, &obj, ptr, sz); #else IPFERROR(60); error = EINVAL; #endif } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_outobjsz */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ /* type(I) - type of structure being moved */ /* sz(I) - size of data to copy */ /* */ /* As per ipf_outobj, except the size of the object to copy out is passed in*/ /* but it must not be smaller than the size defined for the type and the */ /* type must allow for varied sized objects. The extra requirement here is */ /* that sz must match the size of the object being passed in - this is not */ /* not possible nor required in ipf_outobj(). */ /* ------------------------------------------------------------------------ */ int ipf_outobjsz(ipf_main_softc_t *softc, void *data, void *ptr, int type, int sz) { ipfobj_t obj; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(62); return (EINVAL); } error = BCOPYIN(data, &obj, sizeof(obj)); if (error != 0) { IPFERROR(127); return (EFAULT); } if (obj.ipfo_type != type) { IPFERROR(63); return (EINVAL); } if (obj.ipfo_rev >= ipf_objbytes[type][2]) { if (((ipf_objbytes[type][0] & 1) == 0) || (sz < ipf_objbytes[type][1])) { IPFERROR(146); return (EINVAL); } error = COPYOUT(ptr, obj.ipfo_ptr, sz); if (error != 0) { IPFERROR(66); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_out_compat(softc, &obj, ptr); #else IPFERROR(65); error = EINVAL; #endif } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_outobj */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(I) - pointer to ioctl data */ /* ptr(I) - pointer to store real data in */ /* type(I) - type of structure being moved */ /* */ /* Copy out the contents of what ptr is to where ipfobj points to. In */ /* future, we add things to check for version numbers, sizes, etc, to make */ /* it backward compatible at the ABI for user land. */ /* ------------------------------------------------------------------------ */ int ipf_outobj(ipf_main_softc_t *softc, void *data, void *ptr, int type) { ipfobj_t obj; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(67); return (EINVAL); } error = BCOPYIN(data, &obj, sizeof(obj)); if (error != 0) { IPFERROR(126); return (EFAULT); } if (obj.ipfo_type != type) { IPFERROR(68); return (EINVAL); } if (obj.ipfo_rev >= ipf_objbytes[type][2]) { if ((ipf_objbytes[type][0] & 1) != 0) { if (obj.ipfo_size < ipf_objbytes[type][1]) { IPFERROR(69); return (EINVAL); } } else if (obj.ipfo_size != ipf_objbytes[type][1]) { IPFERROR(70); return (EINVAL); } error = COPYOUT(ptr, obj.ipfo_ptr, obj.ipfo_size); if (error != 0) { IPFERROR(73); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_out_compat(softc, &obj, ptr); #else IPFERROR(72); error = EINVAL; #endif } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_outobjk */ /* Returns: int - 0 = success, else failure */ /* Parameters: obj(I) - pointer to data description structure */ /* ptr(I) - pointer to kernel data to copy out */ /* */ /* In the above functions, the ipfobj_t structure is copied into the kernel,*/ /* telling ipfilter how to copy out data. In this instance, the ipfobj_t is */ /* already populated with information and now we just need to use it. */ /* There is no need for this function to have a "type" parameter as there */ /* is no point in validating information that comes from the kernel with */ /* itself. */ /* ------------------------------------------------------------------------ */ int ipf_outobjk(ipf_main_softc_t *softc, ipfobj_t *obj, void *ptr) { int type = obj->ipfo_type; int error; if ((type < 0) || (type >= IPFOBJ_COUNT)) { IPFERROR(147); return (EINVAL); } if (obj->ipfo_rev >= ipf_objbytes[type][2]) { if ((ipf_objbytes[type][0] & 1) != 0) { if (obj->ipfo_size < ipf_objbytes[type][1]) { IPFERROR(148); return (EINVAL); } } else if (obj->ipfo_size != ipf_objbytes[type][1]) { IPFERROR(149); return (EINVAL); } error = COPYOUT(ptr, obj->ipfo_ptr, obj->ipfo_size); if (error != 0) { IPFERROR(150); error = EFAULT; } } else { #ifdef IPFILTER_COMPAT error = ipf_out_compat(softc, obj, ptr); #else IPFERROR(151); error = EINVAL; #endif } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_checkl4sum */ /* Returns: int - 0 = good, -1 = bad, 1 = cannot check */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* If possible, calculate the layer 4 checksum for the packet. If this is */ /* not possible, return without indicating a failure or success but in a */ /* way that is ditinguishable. This function should only be called by the */ /* ipf_checkv6sum() for each platform. */ /* ------------------------------------------------------------------------ */ inline int ipf_checkl4sum(fr_info_t *fin) { u_short sum, hdrsum, *csump; udphdr_t *udp; int dosum; /* * If the TCP packet isn't a fragment, isn't too short and otherwise * isn't already considered "bad", then validate the checksum. If * this check fails then considered the packet to be "bad". */ if ((fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) != 0) return (1); DT2(l4sumo, int, fin->fin_out, int, (int)fin->fin_p); if (fin->fin_out == 1) { fin->fin_cksum = FI_CK_SUMOK; return (0); } csump = NULL; hdrsum = 0; dosum = 0; sum = 0; switch (fin->fin_p) { case IPPROTO_TCP : csump = &((tcphdr_t *)fin->fin_dp)->th_sum; dosum = 1; break; case IPPROTO_UDP : udp = fin->fin_dp; if (udp->uh_sum != 0) { csump = &udp->uh_sum; dosum = 1; } break; #ifdef USE_INET6 case IPPROTO_ICMPV6 : csump = &((struct icmp6_hdr *)fin->fin_dp)->icmp6_cksum; dosum = 1; break; #endif case IPPROTO_ICMP : csump = &((struct icmp *)fin->fin_dp)->icmp_cksum; dosum = 1; break; default : return (1); /*NOTREACHED*/ } if (csump != NULL) { hdrsum = *csump; if (fin->fin_p == IPPROTO_UDP && hdrsum == 0xffff) hdrsum = 0x0000; } if (dosum) { sum = fr_cksum(fin, fin->fin_ip, fin->fin_p, fin->fin_dp); } #if !defined(_KERNEL) if (sum == hdrsum) { FR_DEBUG(("checkl4sum: %hx == %hx\n", sum, hdrsum)); } else { FR_DEBUG(("checkl4sum: %hx != %hx\n", sum, hdrsum)); } #endif DT3(l4sums, u_short, hdrsum, u_short, sum, fr_info_t *, fin); #ifdef USE_INET6 if (hdrsum == sum || (sum == 0 && IP_V(fin->fin_ip) == 6)) { #else if (hdrsum == sum) { #endif fin->fin_cksum = FI_CK_SUMOK; return (0); } fin->fin_cksum = FI_CK_BAD; return (-1); } /* ------------------------------------------------------------------------ */ /* Function: ipf_ifpfillv4addr */ /* Returns: int - 0 = address update, -1 = address not updated */ /* Parameters: atype(I) - type of network address update to perform */ /* sin(I) - pointer to source of address information */ /* mask(I) - pointer to source of netmask information */ /* inp(I) - pointer to destination address store */ /* inpmask(I) - pointer to destination netmask store */ /* */ /* Given a type of network address update (atype) to perform, copy */ /* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */ /* netmask update is performed unless FRI_NETMASKED is passed as atype, in */ /* which case the operation fails. For all values of atype other than */ /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ /* value. */ /* ------------------------------------------------------------------------ */ int ipf_ifpfillv4addr(int atype, struct sockaddr_in *sin, struct sockaddr_in *mask, struct in_addr *inp, struct in_addr *inpmask) { if (inpmask != NULL && atype != FRI_NETMASKED) inpmask->s_addr = 0xffffffff; if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { if (atype == FRI_NETMASKED) { if (inpmask == NULL) return (-1); inpmask->s_addr = mask->sin_addr.s_addr; } inp->s_addr = sin->sin_addr.s_addr & mask->sin_addr.s_addr; } else { inp->s_addr = sin->sin_addr.s_addr; } return (0); } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_ifpfillv6addr */ /* Returns: int - 0 = address update, -1 = address not updated */ /* Parameters: atype(I) - type of network address update to perform */ /* sin(I) - pointer to source of address information */ /* mask(I) - pointer to source of netmask information */ /* inp(I) - pointer to destination address store */ /* inpmask(I) - pointer to destination netmask store */ /* */ /* Given a type of network address update (atype) to perform, copy */ /* information from sin/mask into inp/inpmask. If ipnmask is NULL then no */ /* netmask update is performed unless FRI_NETMASKED is passed as atype, in */ /* which case the operation fails. For all values of atype other than */ /* FRI_NETMASKED, if inpmask is non-NULL then the mask is set to an all 1s */ /* value. */ /* ------------------------------------------------------------------------ */ int ipf_ifpfillv6addr(int atype, struct sockaddr_in6 *sin, struct sockaddr_in6 *mask, i6addr_t *inp, i6addr_t *inpmask) { i6addr_t *src, *and; src = (i6addr_t *)&sin->sin6_addr; and = (i6addr_t *)&mask->sin6_addr; if (inpmask != NULL && atype != FRI_NETMASKED) { inpmask->i6[0] = 0xffffffff; inpmask->i6[1] = 0xffffffff; inpmask->i6[2] = 0xffffffff; inpmask->i6[3] = 0xffffffff; } if (atype == FRI_NETWORK || atype == FRI_NETMASKED) { if (atype == FRI_NETMASKED) { if (inpmask == NULL) return (-1); inpmask->i6[0] = and->i6[0]; inpmask->i6[1] = and->i6[1]; inpmask->i6[2] = and->i6[2]; inpmask->i6[3] = and->i6[3]; } inp->i6[0] = src->i6[0] & and->i6[0]; inp->i6[1] = src->i6[1] & and->i6[1]; inp->i6[2] = src->i6[2] & and->i6[2]; inp->i6[3] = src->i6[3] & and->i6[3]; } else { inp->i6[0] = src->i6[0]; inp->i6[1] = src->i6[1]; inp->i6[2] = src->i6[2]; inp->i6[3] = src->i6[3]; } return (0); } #endif /* ------------------------------------------------------------------------ */ /* Function: ipf_matchtag */ /* Returns: 0 == mismatch, 1 == match. */ /* Parameters: tag1(I) - pointer to first tag to compare */ /* tag2(I) - pointer to second tag to compare */ /* */ /* Returns true (non-zero) or false(0) if the two tag structures can be */ /* considered to be a match or not match, respectively. The tag is 16 */ /* bytes long (16 characters) but that is overlayed with 4 32bit ints so */ /* compare the ints instead, for speed. tag1 is the master of the */ /* comparison. This function should only be called with both tag1 and tag2 */ /* as non-NULL pointers. */ /* ------------------------------------------------------------------------ */ int ipf_matchtag(ipftag_t *tag1, ipftag_t *tag2) { if (tag1 == tag2) return (1); if ((tag1->ipt_num[0] == 0) && (tag2->ipt_num[0] == 0)) return (1); if ((tag1->ipt_num[0] == tag2->ipt_num[0]) && (tag1->ipt_num[1] == tag2->ipt_num[1]) && (tag1->ipt_num[2] == tag2->ipt_num[2]) && (tag1->ipt_num[3] == tag2->ipt_num[3])) return (1); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_coalesce */ /* Returns: 1 == success, -1 == failure, 0 == no change */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Attempt to get all of the packet data into a single, contiguous buffer. */ /* If this call returns a failure then the buffers have also been freed. */ /* ------------------------------------------------------------------------ */ int ipf_coalesce(fr_info_t *fin) { if ((fin->fin_flx & FI_COALESCE) != 0) return (1); /* * If the mbuf pointers indicate that there is no mbuf to work with, * return but do not indicate success or failure. */ if (fin->fin_m == NULL || fin->fin_mp == NULL) return (0); #if defined(_KERNEL) if (ipf_pullup(fin->fin_m, fin, fin->fin_plen) == NULL) { ipf_main_softc_t *softc = fin->fin_main_soft; DT1(frb_coalesce, fr_info_t *, fin); LBUMP(ipf_stats[fin->fin_out].fr_badcoalesces); # if SOLARIS FREE_MB_T(*fin->fin_mp); # endif fin->fin_reason = FRB_COALESCE; *fin->fin_mp = NULL; fin->fin_m = NULL; return (-1); } #else fin = fin; /* LINT */ #endif return (1); } /* * The following table lists all of the tunable variables that can be * accessed via SIOCIPFGET/SIOCIPFSET/SIOCIPFGETNEXt. The format of each row * in the table below is as follows: * * pointer to value, name of value, minimum, maximum, size of the value's * container, value attribute flags * * For convienience, IPFT_RDONLY means the value is read-only, IPFT_WRDISABLED * means the value can only be written to when IPFilter is loaded but disabled. * The obvious implication is if neither of these are set then the value can be * changed at any time without harm. */ /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_findbycookie */ /* Returns: NULL = search failed, else pointer to tune struct */ /* Parameters: cookie(I) - cookie value to search for amongst tuneables */ /* next(O) - pointer to place to store the cookie for the */ /* "next" tuneable, if it is desired. */ /* */ /* This function is used to walk through all of the existing tunables with */ /* successive calls. It searches the known tunables for the one which has */ /* a matching value for "cookie" - ie its address. When returning a match, */ /* the next one to be found may be returned inside next. */ /* ------------------------------------------------------------------------ */ static ipftuneable_t * ipf_tune_findbycookie(ipftuneable_t **ptop, void *cookie, void **next) { ipftuneable_t *ta, **tap; for (ta = *ptop; ta->ipft_name != NULL; ta++) if (ta == cookie) { if (next != NULL) { /* * If the next entry in the array has a name * present, then return a pointer to it for * where to go next, else return a pointer to * the dynaminc list as a key to search there * next. This facilitates a weak linking of * the two "lists" together. */ if ((ta + 1)->ipft_name != NULL) *next = ta + 1; else *next = ptop; } return (ta); } for (tap = ptop; (ta = *tap) != NULL; tap = &ta->ipft_next) if (tap == cookie) { if (next != NULL) *next = &ta->ipft_next; return (ta); } if (next != NULL) *next = NULL; return (NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_findbyname */ /* Returns: NULL = search failed, else pointer to tune struct */ /* Parameters: name(I) - name of the tuneable entry to find. */ /* */ /* Search the static array of tuneables and the list of dynamic tuneables */ /* for an entry with a matching name. If we can find one, return a pointer */ /* to the matching structure. */ /* ------------------------------------------------------------------------ */ static ipftuneable_t * ipf_tune_findbyname(ipftuneable_t *top, const char *name) { ipftuneable_t *ta; for (ta = top; ta != NULL; ta = ta->ipft_next) if (!strcmp(ta->ipft_name, name)) { return (ta); } return (NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_add_array */ /* Returns: int - 0 == success, else failure */ /* Parameters: newtune - pointer to new tune array to add to tuneables */ /* */ /* Appends tune structures from the array passed in (newtune) to the end of */ /* the current list of "dynamic" tuneable parameters. */ /* If any entry to be added is already present (by name) then the operation */ /* is aborted - entries that have been added are removed before returning. */ /* An entry with no name (NULL) is used as the indication that the end of */ /* the array has been reached. */ /* ------------------------------------------------------------------------ */ int ipf_tune_add_array(ipf_main_softc_t *softc, ipftuneable_t *newtune) { ipftuneable_t *nt, *dt; int error = 0; for (nt = newtune; nt->ipft_name != NULL; nt++) { error = ipf_tune_add(softc, nt); if (error != 0) { for (dt = newtune; dt != nt; dt++) { (void) ipf_tune_del(softc, dt); } } } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_array_link */ /* Returns: 0 == success, -1 == failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* array(I) - pointer to an array of tuneables */ /* */ /* Given an array of tunables (array), append them to the current list of */ /* tuneables for this context (softc->ipf_tuners.) To properly prepare the */ /* the array for being appended to the list, initialise all of the next */ /* pointers so we don't need to walk parts of it with ++ and others with */ /* next. The array is expected to have an entry with a NULL name as the */ /* terminator. Trying to add an array with no non-NULL names will return as */ /* a failure. */ /* ------------------------------------------------------------------------ */ int ipf_tune_array_link(ipf_main_softc_t *softc, ipftuneable_t *array) { ipftuneable_t *t, **p; t = array; if (t->ipft_name == NULL) return (-1); for (; t[1].ipft_name != NULL; t++) t[0].ipft_next = &t[1]; t->ipft_next = NULL; /* * Since a pointer to the last entry isn't kept, we need to find it * each time we want to add new variables to the list. */ for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) if (t->ipft_name == NULL) break; *p = array; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_array_unlink */ /* Returns: 0 == success, -1 == failure */ /* Parameters: softc(I) - soft context pointerto work with */ /* array(I) - pointer to an array of tuneables */ /* */ /* ------------------------------------------------------------------------ */ int ipf_tune_array_unlink(ipf_main_softc_t *softc, ipftuneable_t *array) { ipftuneable_t *t, **p; for (p = &softc->ipf_tuners; (t = *p) != NULL; p = &t->ipft_next) if (t == array) break; if (t == NULL) return (-1); for (; t[1].ipft_name != NULL; t++) ; *p = t->ipft_next; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_array_copy */ /* Returns: NULL = failure, else pointer to new array */ /* Parameters: base(I) - pointer to structure base */ /* size(I) - size of the array at template */ /* template(I) - original array to copy */ /* */ /* Allocate memory for a new set of tuneable values and copy everything */ /* from template into the new region of memory. The new region is full of */ /* uninitialised pointers (ipft_next) so set them up. Now, ipftp_offset... */ /* */ /* NOTE: the following assumes that sizeof(long) == sizeof(void *) */ /* In the array template, ipftp_offset is the offset (in bytes) of the */ /* location of the tuneable value inside the structure pointed to by base. */ /* As ipftp_offset is a union over the pointers to the tuneable values, if */ /* we add base to the copy's ipftp_offset, copy ends up with a pointer in */ /* ipftp_void that points to the stored value. */ /* ------------------------------------------------------------------------ */ ipftuneable_t * ipf_tune_array_copy(void *base, size_t size, ipftuneable_t *template) { ipftuneable_t *copy; int i; KMALLOCS(copy, ipftuneable_t *, size); if (copy == NULL) { return (NULL); } bcopy(template, copy, size); for (i = 0; copy[i].ipft_name; i++) { copy[i].ipft_una.ipftp_offset += (u_long)base; copy[i].ipft_next = copy + i + 1; } return (copy); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_add */ /* Returns: int - 0 == success, else failure */ /* Parameters: newtune - pointer to new tune entry to add to tuneables */ /* */ /* Appends tune structures from the array passed in (newtune) to the end of */ /* the current list of "dynamic" tuneable parameters. Once added, the */ /* owner of the object is not expected to ever change "ipft_next". */ /* ------------------------------------------------------------------------ */ int ipf_tune_add(ipf_main_softc_t *softc, ipftuneable_t *newtune) { ipftuneable_t *ta, **tap; ta = ipf_tune_findbyname(softc->ipf_tuners, newtune->ipft_name); if (ta != NULL) { IPFERROR(74); return (EEXIST); } for (tap = &softc->ipf_tuners; *tap != NULL; tap = &(*tap)->ipft_next) ; newtune->ipft_next = NULL; *tap = newtune; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_del */ /* Returns: int - 0 == success, else failure */ /* Parameters: oldtune - pointer to tune entry to remove from the list of */ /* current dynamic tuneables */ /* */ /* Search for the tune structure, by pointer, in the list of those that are */ /* dynamically added at run time. If found, adjust the list so that this */ /* structure is no longer part of it. */ /* ------------------------------------------------------------------------ */ int ipf_tune_del(ipf_main_softc_t *softc, ipftuneable_t *oldtune) { ipftuneable_t *ta, **tap; int error = 0; for (tap = &softc->ipf_tuners; (ta = *tap) != NULL; tap = &ta->ipft_next) { if (ta == oldtune) { *tap = oldtune->ipft_next; oldtune->ipft_next = NULL; break; } } if (ta == NULL) { error = ESRCH; IPFERROR(75); } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune_del_array */ /* Returns: int - 0 == success, else failure */ /* Parameters: oldtune - pointer to tuneables array */ /* */ /* Remove each tuneable entry in the array from the list of "dynamic" */ /* tunables. If one entry should fail to be found, an error will be */ /* returned and no further ones removed. */ /* An entry with a NULL name is used as the indicator of the last entry in */ /* the array. */ /* ------------------------------------------------------------------------ */ int ipf_tune_del_array(ipf_main_softc_t *softc, ipftuneable_t *oldtune) { ipftuneable_t *ot; int error = 0; for (ot = oldtune; ot->ipft_name != NULL; ot++) { error = ipf_tune_del(softc, ot); if (error != 0) break; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_tune */ /* Returns: int - 0 == success, else failure */ /* Parameters: cmd(I) - ioctl command number */ /* data(I) - pointer to ioctl data structure */ /* */ /* Implement handling of SIOCIPFGETNEXT, SIOCIPFGET and SIOCIPFSET. These */ /* three ioctls provide the means to access and control global variables */ /* within IPFilter, allowing (for example) timeouts and table sizes to be */ /* changed without rebooting, reloading or recompiling. The initialisation */ /* and 'destruction' routines of the various components of ipfilter are all */ /* each responsible for handling their own values being too big. */ /* ------------------------------------------------------------------------ */ int ipf_ipftune(ipf_main_softc_t *softc, ioctlcmd_t cmd, void *data) { ipftuneable_t *ta; ipftune_t tu; void *cookie; int error; error = ipf_inobj(softc, data, NULL, &tu, IPFOBJ_TUNEABLE); if (error != 0) return (error); tu.ipft_name[sizeof(tu.ipft_name) - 1] = '\0'; cookie = tu.ipft_cookie; ta = NULL; switch (cmd) { case SIOCIPFGETNEXT : /* * If cookie is non-NULL, assume it to be a pointer to the last * entry we looked at, so find it (if possible) and return a * pointer to the next one after it. The last entry in the * the table is a NULL entry, so when we get to it, set cookie * to NULL and return that, indicating end of list, erstwhile * if we come in with cookie set to NULL, we are starting anew * at the front of the list. */ if (cookie != NULL) { ta = ipf_tune_findbycookie(&softc->ipf_tuners, cookie, &tu.ipft_cookie); } else { ta = softc->ipf_tuners; tu.ipft_cookie = ta + 1; } if (ta != NULL) { /* * Entry found, but does the data pointed to by that * row fit in what we can return? */ if (ta->ipft_sz > sizeof(tu.ipft_un)) { IPFERROR(76); return (EINVAL); } tu.ipft_vlong = 0; if (ta->ipft_sz == sizeof(u_long)) tu.ipft_vlong = *ta->ipft_plong; else if (ta->ipft_sz == sizeof(u_int)) tu.ipft_vint = *ta->ipft_pint; else if (ta->ipft_sz == sizeof(u_short)) tu.ipft_vshort = *ta->ipft_pshort; else if (ta->ipft_sz == sizeof(u_char)) tu.ipft_vchar = *ta->ipft_pchar; tu.ipft_sz = ta->ipft_sz; tu.ipft_min = ta->ipft_min; tu.ipft_max = ta->ipft_max; tu.ipft_flags = ta->ipft_flags; bcopy(ta->ipft_name, tu.ipft_name, MIN(sizeof(tu.ipft_name), strlen(ta->ipft_name) + 1)); } error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); break; case SIOCIPFGET : case SIOCIPFSET : /* * Search by name or by cookie value for a particular entry * in the tuning parameter table. */ IPFERROR(77); error = ESRCH; if (cookie != NULL) { ta = ipf_tune_findbycookie(&softc->ipf_tuners, cookie, NULL); if (ta != NULL) error = 0; } else if (tu.ipft_name[0] != '\0') { ta = ipf_tune_findbyname(softc->ipf_tuners, tu.ipft_name); if (ta != NULL) error = 0; } if (error != 0) break; if (cmd == (ioctlcmd_t)SIOCIPFGET) { /* * Fetch the tuning parameters for a particular value */ tu.ipft_vlong = 0; if (ta->ipft_sz == sizeof(u_long)) tu.ipft_vlong = *ta->ipft_plong; else if (ta->ipft_sz == sizeof(u_int)) tu.ipft_vint = *ta->ipft_pint; else if (ta->ipft_sz == sizeof(u_short)) tu.ipft_vshort = *ta->ipft_pshort; else if (ta->ipft_sz == sizeof(u_char)) tu.ipft_vchar = *ta->ipft_pchar; tu.ipft_cookie = ta; tu.ipft_sz = ta->ipft_sz; tu.ipft_min = ta->ipft_min; tu.ipft_max = ta->ipft_max; tu.ipft_flags = ta->ipft_flags; error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); } else if (cmd == (ioctlcmd_t)SIOCIPFSET) { /* * Set an internal parameter. The hard part here is * getting the new value safely and correctly out of * the kernel (given we only know its size, not type.) */ u_long in; if (((ta->ipft_flags & IPFT_WRDISABLED) != 0) && (softc->ipf_running > 0)) { IPFERROR(78); error = EBUSY; break; } in = tu.ipft_vlong; if (in < ta->ipft_min || in > ta->ipft_max) { IPFERROR(79); error = EINVAL; break; } if (ta->ipft_func != NULL) { SPL_INT(s); SPL_NET(s); error = (*ta->ipft_func)(softc, ta, &tu.ipft_un); SPL_X(s); } else if (ta->ipft_sz == sizeof(u_long)) { tu.ipft_vlong = *ta->ipft_plong; *ta->ipft_plong = in; } else if (ta->ipft_sz == sizeof(u_int)) { tu.ipft_vint = *ta->ipft_pint; *ta->ipft_pint = (u_int)(in & 0xffffffff); } else if (ta->ipft_sz == sizeof(u_short)) { tu.ipft_vshort = *ta->ipft_pshort; *ta->ipft_pshort = (u_short)(in & 0xffff); } else if (ta->ipft_sz == sizeof(u_char)) { tu.ipft_vchar = *ta->ipft_pchar; *ta->ipft_pchar = (u_char)(in & 0xff); } error = ipf_outobj(softc, data, &tu, IPFOBJ_TUNEABLE); } break; default : IPFERROR(80); error = EINVAL; break; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_zerostats */ /* Returns: int - 0 = success, else failure */ /* Parameters: data(O) - pointer to pointer for copying data back to */ /* */ /* Copies the current statistics out to userspace and then zero's the */ /* current ones in the kernel. The lock is only held across the bzero() as */ /* the copyout may result in paging (ie network activity.) */ /* ------------------------------------------------------------------------ */ int ipf_zerostats(ipf_main_softc_t *softc, caddr_t data) { friostat_t fio; ipfobj_t obj; int error; error = ipf_inobj(softc, data, &obj, &fio, IPFOBJ_IPFSTAT); if (error != 0) return (error); ipf_getstat(softc, &fio, obj.ipfo_rev); error = ipf_outobj(softc, data, &fio, IPFOBJ_IPFSTAT); if (error != 0) return (error); WRITE_ENTER(&softc->ipf_mutex); bzero(&softc->ipf_stats, sizeof(softc->ipf_stats)); RWLOCK_EXIT(&softc->ipf_mutex); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_resolvedest */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* base(I) - where strings are stored */ /* fdp(IO) - pointer to destination information to resolve */ /* v(I) - IP protocol version to match */ /* */ /* Looks up an interface name in the frdest structure pointed to by fdp and */ /* if a matching name can be found for the particular IP protocol version */ /* then store the interface pointer in the frdest struct. If no match is */ /* found, then set the interface pointer to be -1 as NULL is considered to */ /* indicate there is no information at all in the structure. */ /* ------------------------------------------------------------------------ */ int ipf_resolvedest(ipf_main_softc_t *softc, char *base, frdest_t *fdp, int v) { int errval = 0; void *ifp; ifp = NULL; if (fdp->fd_name != -1) { if (fdp->fd_type == FRD_DSTLIST) { ifp = ipf_lookup_res_name(softc, IPL_LOGIPF, IPLT_DSTLIST, base + fdp->fd_name, NULL); if (ifp == NULL) { IPFERROR(144); errval = ESRCH; } } else { ifp = GETIFP(base + fdp->fd_name, v); if (ifp == NULL) ifp = (void *)-1; } } fdp->fd_ptr = ifp; return (errval); } /* ------------------------------------------------------------------------ */ /* Function: ipf_resolvenic */ /* Returns: void* - NULL = wildcard name, -1 = failed to find NIC, else */ /* pointer to interface structure for NIC */ /* Parameters: softc(I)- pointer to soft context main structure */ /* name(I) - complete interface name */ /* v(I) - IP protocol version */ /* */ /* Look for a network interface structure that firstly has a matching name */ /* to that passed in and that is also being used for that IP protocol */ /* version (necessary on some platforms where there are separate listings */ /* for both IPv4 and IPv6 on the same physical NIC. */ /* ------------------------------------------------------------------------ */ void * ipf_resolvenic(ipf_main_softc_t *softc, char *name, int v) { void *nic; softc = softc; /* gcc -Wextra */ if (name[0] == '\0') return (NULL); if ((name[1] == '\0') && ((name[0] == '-') || (name[0] == '*'))) { return (NULL); } nic = GETIFP(name, v); if (nic == NULL) nic = (void *)-1; return (nic); } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_expire */ /* Returns: None. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* This function is run every ipf tick to see if there are any tokens that */ /* have been held for too long and need to be freed up. */ /* ------------------------------------------------------------------------ */ void ipf_token_expire(ipf_main_softc_t *softc) { ipftoken_t *it; WRITE_ENTER(&softc->ipf_tokens); while ((it = softc->ipf_token_head) != NULL) { if (it->ipt_die > softc->ipf_ticks) break; ipf_token_deref(softc, it); } RWLOCK_EXIT(&softc->ipf_tokens); } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_flush */ /* Returns: None. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Loop through all of the existing tokens and call deref to see if they */ /* can be freed. Normally a function like this might just loop on */ /* ipf_token_head but there is a chance that a token might have a ref count */ /* of greater than one and in that case the reference would drop twice */ /* by code that is only entitled to drop it once. */ /* ------------------------------------------------------------------------ */ static void ipf_token_flush(ipf_main_softc_t *softc) { ipftoken_t *it, *next; WRITE_ENTER(&softc->ipf_tokens); for (it = softc->ipf_token_head; it != NULL; it = next) { next = it->ipt_next; (void) ipf_token_deref(softc, it); } RWLOCK_EXIT(&softc->ipf_tokens); } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_del */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* type(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function looks for a token in the current list that matches up */ /* the fields (type, uid, ptr). If none is found, ESRCH is returned, else */ /* call ipf_token_dewref() to remove it from the list. In the event that */ /* the token has a reference held elsewhere, setting ipt_complete to 2 */ /* enables debugging to distinguish between the two paths that ultimately */ /* lead to a token to be deleted. */ /* ------------------------------------------------------------------------ */ int ipf_token_del(ipf_main_softc_t *softc, int type, int uid, void *ptr) { ipftoken_t *it; int error; IPFERROR(82); error = ESRCH; WRITE_ENTER(&softc->ipf_tokens); for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { if (ptr == it->ipt_ctx && type == it->ipt_type && uid == it->ipt_uid) { it->ipt_complete = 2; ipf_token_deref(softc, it); error = 0; break; } } RWLOCK_EXIT(&softc->ipf_tokens); return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_mark_complete */ /* Returns: None. */ /* Parameters: token(I) - pointer to token structure */ /* */ /* Mark a token as being ineligable for being found with ipf_token_find. */ /* ------------------------------------------------------------------------ */ void ipf_token_mark_complete(ipftoken_t *token) { if (token->ipt_complete == 0) token->ipt_complete = 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_find */ /* Returns: ipftoken_t * - NULL if no memory, else pointer to token */ /* Parameters: softc(I)- pointer to soft context main structure */ /* type(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function looks for a live token in the list of current tokens that */ /* matches the tuple (type, uid, ptr). If one cannot be found then one is */ /* allocated. If one is found then it is moved to the top of the list of */ /* currently active tokens. */ /* ------------------------------------------------------------------------ */ ipftoken_t * ipf_token_find(ipf_main_softc_t *softc, int type, int uid, void *ptr) { ipftoken_t *it, *new; WRITE_ENTER(&softc->ipf_tokens); for (it = softc->ipf_token_head; it != NULL; it = it->ipt_next) { if ((ptr == it->ipt_ctx) && (type == it->ipt_type) && (uid == it->ipt_uid) && (it->ipt_complete < 2)) break; } if (it == NULL) { KMALLOC(new, ipftoken_t *); if (new != NULL) bzero((char *)new, sizeof(*new)); it = new; new = NULL; if (it == NULL) { RWLOCK_EXIT(&softc->ipf_tokens); return (NULL); } it->ipt_ctx = ptr; it->ipt_uid = uid; it->ipt_type = type; it->ipt_ref = 1; } else { if (it->ipt_complete > 0) it = NULL; else ipf_token_unlink(softc, it); } if (it != NULL) { it->ipt_pnext = softc->ipf_token_tail; *softc->ipf_token_tail = it; softc->ipf_token_tail = &it->ipt_next; it->ipt_next = NULL; it->ipt_ref++; it->ipt_die = softc->ipf_ticks + 20; } RWLOCK_EXIT(&softc->ipf_tokens); return (it); } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_unlink */ /* Returns: None. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token structure */ /* Write Locks: ipf_tokens */ /* */ /* This function unlinks a token structure from the linked list of tokens */ /* that "own" it. The head pointer never needs to be explicitly adjusted */ /* but the tail does due to the linked list implementation. */ /* ------------------------------------------------------------------------ */ static void ipf_token_unlink(ipf_main_softc_t *softc, ipftoken_t *token) { if (softc->ipf_token_tail == &token->ipt_next) softc->ipf_token_tail = token->ipt_pnext; *token->ipt_pnext = token->ipt_next; if (token->ipt_next != NULL) token->ipt_next->ipt_pnext = token->ipt_pnext; token->ipt_next = NULL; token->ipt_pnext = NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_token_deref */ /* Returns: int - 0 == token freed, else reference count */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token structure */ /* Write Locks: ipf_tokens */ /* */ /* Drop the reference count on the token structure and if it drops to zero, */ /* call the dereference function for the token type because it is then */ /* possible to free the token data structure. */ /* ------------------------------------------------------------------------ */ int ipf_token_deref(ipf_main_softc_t *softc, ipftoken_t *token) { void *data, **datap; ASSERT(token->ipt_ref > 0); token->ipt_ref--; if (token->ipt_ref > 0) return (token->ipt_ref); data = token->ipt_data; datap = &data; if ((data != NULL) && (data != (void *)-1)) { switch (token->ipt_type) { case IPFGENITER_IPF : (void) ipf_derefrule(softc, (frentry_t **)datap); break; case IPFGENITER_IPNAT : WRITE_ENTER(&softc->ipf_nat); ipf_nat_rule_deref(softc, (ipnat_t **)datap); RWLOCK_EXIT(&softc->ipf_nat); break; case IPFGENITER_NAT : ipf_nat_deref(softc, (nat_t **)datap); break; case IPFGENITER_STATE : ipf_state_deref(softc, (ipstate_t **)datap); break; case IPFGENITER_FRAG : ipf_frag_pkt_deref(softc, (ipfr_t **)datap); break; case IPFGENITER_NATFRAG : ipf_frag_nat_deref(softc, (ipfr_t **)datap); break; case IPFGENITER_HOSTMAP : WRITE_ENTER(&softc->ipf_nat); ipf_nat_hostmapdel(softc, (hostmap_t **)datap); RWLOCK_EXIT(&softc->ipf_nat); break; default : ipf_lookup_iterderef(softc, token->ipt_type, data); break; } } ipf_token_unlink(softc, token); KFREE(token); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nextrule */ /* Returns: frentry_t * - NULL == no more rules, else pointer to next */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fr(I) - pointer to filter rule */ /* out(I) - 1 == out rules, 0 == input rules */ /* */ /* Starting with "fr", find the next rule to visit. This includes visiting */ /* the list of rule groups if either fr is NULL (empty list) or it is the */ /* last rule in the list. When walking rule lists, it is either input or */ /* output rules that are returned, never both. */ /* ------------------------------------------------------------------------ */ static frentry_t * ipf_nextrule(ipf_main_softc_t *softc, int active, int unit, frentry_t *fr, int out) { frentry_t *next; frgroup_t *fg; if (fr != NULL && fr->fr_group != -1) { fg = ipf_findgroup(softc, fr->fr_names + fr->fr_group, unit, active, NULL); if (fg != NULL) fg = fg->fg_next; } else { fg = softc->ipf_groups[unit][active]; } while (fg != NULL) { next = fg->fg_start; while (next != NULL) { if (out) { if (next->fr_flags & FR_OUTQUE) return (next); } else if (next->fr_flags & FR_INQUE) { return (next); } next = next->fr_next; } if (next == NULL) fg = fg->fg_next; } return (NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_getnextrule */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* t(I) - pointer to destination information to resolve */ /* ptr(I) - pointer to ipfobj_t to copyin from user space */ /* */ /* This function's first job is to bring in the ipfruleiter_t structure via */ /* the ipfobj_t structure to determine what should be the next rule to */ /* return. Once the ipfruleiter_t has been brought in, it then tries to */ /* find the 'next rule'. This may include searching rule group lists or */ /* just be as simple as looking at the 'next' field in the rule structure. */ /* When we have found the rule to return, increase its reference count and */ /* if we used an existing rule to get here, decrease its reference count. */ /* ------------------------------------------------------------------------ */ int ipf_getnextrule(ipf_main_softc_t *softc, ipftoken_t *t, void *ptr) { frentry_t *fr, *next, zero; ipfruleiter_t it; int error, out; frgroup_t *fg; ipfobj_t obj; int predict; char *dst; int unit; if (t == NULL || ptr == NULL) { IPFERROR(84); return (EFAULT); } error = ipf_inobj(softc, ptr, &obj, &it, IPFOBJ_IPFITER); if (error != 0) return (error); if ((it.iri_inout < 0) || (it.iri_inout > 3)) { IPFERROR(85); return (EINVAL); } if ((it.iri_active != 0) && (it.iri_active != 1)) { IPFERROR(86); return (EINVAL); } if (it.iri_nrules == 0) { IPFERROR(87); return (ENOSPC); } if (it.iri_rule == NULL) { IPFERROR(88); return (EFAULT); } fg = NULL; fr = t->ipt_data; if ((it.iri_inout & F_OUT) != 0) out = 1; else out = 0; if ((it.iri_inout & F_ACIN) != 0) unit = IPL_LOGCOUNT; else unit = IPL_LOGIPF; READ_ENTER(&softc->ipf_mutex); if (fr == NULL) { if (*it.iri_group == '\0') { if (unit == IPL_LOGCOUNT) { next = softc->ipf_acct[out][it.iri_active]; } else { next = softc->ipf_rules[out][it.iri_active]; } if (next == NULL) next = ipf_nextrule(softc, it.iri_active, unit, NULL, out); } else { fg = ipf_findgroup(softc, it.iri_group, unit, it.iri_active, NULL); if (fg != NULL) next = fg->fg_start; else next = NULL; } } else { next = fr->fr_next; if (next == NULL) next = ipf_nextrule(softc, it.iri_active, unit, fr, out); } if (next != NULL && next->fr_next != NULL) predict = 1; else if (ipf_nextrule(softc, it.iri_active, unit, next, out) != NULL) predict = 1; else predict = 0; if (fr != NULL) (void) ipf_derefrule(softc, &fr); obj.ipfo_type = IPFOBJ_FRENTRY; dst = (char *)it.iri_rule; if (next != NULL) { obj.ipfo_size = next->fr_size; MUTEX_ENTER(&next->fr_lock); next->fr_ref++; MUTEX_EXIT(&next->fr_lock); t->ipt_data = next; } else { obj.ipfo_size = sizeof(frentry_t); bzero(&zero, sizeof(zero)); next = &zero; t->ipt_data = NULL; } it.iri_rule = predict ? next : NULL; if (predict == 0) ipf_token_mark_complete(t); RWLOCK_EXIT(&softc->ipf_mutex); obj.ipfo_ptr = dst; error = ipf_outobjk(softc, &obj, next); if (error == 0 && t->ipt_data != NULL) { dst += obj.ipfo_size; if (next->fr_data != NULL) { ipfobj_t dobj; if (next->fr_type == FR_T_IPFEXPR) dobj.ipfo_type = IPFOBJ_IPFEXPR; else dobj.ipfo_type = IPFOBJ_FRIPF; dobj.ipfo_size = next->fr_dsize; dobj.ipfo_rev = obj.ipfo_rev; dobj.ipfo_ptr = dst; error = ipf_outobjk(softc, &dobj, next->fr_data); } } if ((fr != NULL) && (next == &zero)) (void) ipf_derefrule(softc, &fr); return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frruleiter */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* data(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function serves as a stepping stone between ipf_ipf_ioctl and */ /* ipf_getnextrule. It's role is to find the right token in the kernel for */ /* the process doing the ioctl and use that to ask for the next rule. */ /* ------------------------------------------------------------------------ */ static int ipf_frruleiter(ipf_main_softc_t *softc, void *data, int uid, void *ctx) { ipftoken_t *token; ipfruleiter_t it; ipfobj_t obj; int error; token = ipf_token_find(softc, IPFGENITER_IPF, uid, ctx); if (token != NULL) { error = ipf_getnextrule(softc, token, data); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } else { error = ipf_inobj(softc, data, &obj, &it, IPFOBJ_IPFITER); if (error != 0) return (error); it.iri_rule = NULL; error = ipf_outobj(softc, data, &it, IPFOBJ_IPFITER); } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_geniter */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to ipftoken_t structure */ /* itp(I) - pointer to iterator data */ /* */ /* Decide which iterator function to call using information passed through */ /* the ipfgeniter_t structure at itp. */ /* ------------------------------------------------------------------------ */ static int ipf_geniter(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) { int error; switch (itp->igi_type) { case IPFGENITER_FRAG : error = ipf_frag_pkt_next(softc, token, itp); break; default : IPFERROR(92); error = EINVAL; break; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_genericiter */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* data(I) - the token type to match */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* Handle the SIOCGENITER ioctl for the ipfilter device. The primary role */ /* ------------------------------------------------------------------------ */ int ipf_genericiter(ipf_main_softc_t *softc, void *data, int uid, void *ctx) { ipftoken_t *token; ipfgeniter_t iter; int error; error = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_GENITER); if (error != 0) return (error); token = ipf_token_find(softc, iter.igi_type, uid, ctx); if (token != NULL) { token->ipt_subtype = iter.igi_type; error = ipf_geniter(softc, token, &iter); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } else { IPFERROR(93); error = 0; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_ipf_ioctl */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I)- pointer to soft context main structure */ /* data(I) - the token type to match */ /* cmd(I) - the ioctl command number */ /* mode(I) - mode flags for the ioctl */ /* uid(I) - uid owning the token */ /* ptr(I) - context pointer for the token */ /* */ /* This function handles all of the ioctl command that are actually isssued */ /* to the /dev/ipl device. */ /* ------------------------------------------------------------------------ */ int ipf_ipf_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd, int mode, int uid, void *ctx) { friostat_t fio; int error, tmp; ipfobj_t obj; SPL_INT(s); switch (cmd) { case SIOCFRENB : if (!(mode & FWRITE)) { IPFERROR(94); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error != 0) { IPFERROR(95); error = EFAULT; break; } WRITE_ENTER(&softc->ipf_global); if (tmp) { if (softc->ipf_running > 0) error = 0; else error = ipfattach(softc); if (error == 0) softc->ipf_running = 1; else (void) ipfdetach(softc); } else { if (softc->ipf_running == 1) error = ipfdetach(softc); else error = 0; if (error == 0) softc->ipf_running = -1; } RWLOCK_EXIT(&softc->ipf_global); } break; case SIOCIPFSET : if (!(mode & FWRITE)) { IPFERROR(96); error = EPERM; break; } /* FALLTHRU */ case SIOCIPFGETNEXT : case SIOCIPFGET : error = ipf_ipftune(softc, cmd, (void *)data); break; case SIOCSETFF : if (!(mode & FWRITE)) { IPFERROR(97); error = EPERM; } else { error = BCOPYIN(data, &softc->ipf_flags, sizeof(softc->ipf_flags)); if (error != 0) { IPFERROR(98); error = EFAULT; } } break; case SIOCGETFF : error = BCOPYOUT(&softc->ipf_flags, data, sizeof(softc->ipf_flags)); if (error != 0) { IPFERROR(99); error = EFAULT; } break; case SIOCFUNCL : error = ipf_resolvefunc(softc, (void *)data); break; case SIOCINAFR : case SIOCRMAFR : case SIOCADAFR : case SIOCZRLST : if (!(mode & FWRITE)) { IPFERROR(100); error = EPERM; } else { error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, softc->ipf_active, 1); } break; case SIOCINIFR : case SIOCRMIFR : case SIOCADIFR : if (!(mode & FWRITE)) { IPFERROR(101); error = EPERM; } else { error = frrequest(softc, IPL_LOGIPF, cmd, (caddr_t)data, 1 - softc->ipf_active, 1); } break; case SIOCSWAPA : if (!(mode & FWRITE)) { IPFERROR(102); error = EPERM; } else { WRITE_ENTER(&softc->ipf_mutex); error = BCOPYOUT(&softc->ipf_active, data, sizeof(softc->ipf_active)); if (error != 0) { IPFERROR(103); error = EFAULT; } else { softc->ipf_active = 1 - softc->ipf_active; } RWLOCK_EXIT(&softc->ipf_mutex); } break; case SIOCGETFS : error = ipf_inobj(softc, (void *)data, &obj, &fio, IPFOBJ_IPFSTAT); if (error != 0) break; ipf_getstat(softc, &fio, obj.ipfo_rev); error = ipf_outobj(softc, (void *)data, &fio, IPFOBJ_IPFSTAT); break; case SIOCFRZST : if (!(mode & FWRITE)) { IPFERROR(104); error = EPERM; } else error = ipf_zerostats(softc, (caddr_t)data); break; case SIOCIPFFL : if (!(mode & FWRITE)) { IPFERROR(105); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (!error) { tmp = ipf_flush(softc, IPL_LOGIPF, tmp); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(106); error = EFAULT; } } else { IPFERROR(107); error = EFAULT; } } break; #ifdef USE_INET6 case SIOCIPFL6 : if (!(mode & FWRITE)) { IPFERROR(108); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (!error) { tmp = ipf_flush(softc, IPL_LOGIPF, tmp); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(109); error = EFAULT; } } else { IPFERROR(110); error = EFAULT; } } break; #endif case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(122); error = EPERM; } else { error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error == 0) { ipf_state_setlock(softc->ipf_state_soft, tmp); ipf_nat_setlock(softc->ipf_nat_soft, tmp); ipf_frag_setlock(softc->ipf_frag_soft, tmp); ipf_auth_setlock(softc->ipf_auth_soft, tmp); } else { IPFERROR(111); error = EFAULT; } } break; #ifdef IPFILTER_LOG case SIOCIPFFB : if (!(mode & FWRITE)) { IPFERROR(112); error = EPERM; } else { tmp = ipf_log_clear(softc, IPL_LOGIPF); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error) { IPFERROR(113); error = EFAULT; } } break; #endif /* IPFILTER_LOG */ case SIOCFRSYN : if (!(mode & FWRITE)) { IPFERROR(114); error = EPERM; } else { WRITE_ENTER(&softc->ipf_global); #if (SOLARIS && defined(_KERNEL)) && !defined(INSTANCES) error = ipfsync(); #else ipf_sync(softc, NULL); error = 0; #endif RWLOCK_EXIT(&softc->ipf_global); } break; case SIOCGFRST : error = ipf_outobj(softc, (void *)data, ipf_frag_stats(softc->ipf_frag_soft), IPFOBJ_FRAGSTAT); break; #ifdef IPFILTER_LOG case FIONREAD : tmp = ipf_log_bytesused(softc, IPL_LOGIPF); error = BCOPYOUT(&tmp, data, sizeof(tmp)); break; #endif case SIOCIPFITER : SPL_SCHED(s); error = ipf_frruleiter(softc, data, uid, ctx); SPL_X(s); break; case SIOCGENITER : SPL_SCHED(s); error = ipf_genericiter(softc, data, uid, ctx); SPL_X(s); break; case SIOCIPFDELTOK : error = BCOPYIN(data, &tmp, sizeof(tmp)); if (error == 0) { SPL_SCHED(s); error = ipf_token_del(softc, tmp, uid, ctx); SPL_X(s); } break; default : IPFERROR(115); error = EINVAL; break; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_decaps */ /* Returns: int - -1 == decapsulation failed, else bit mask of */ /* flags indicating packet filtering decision. */ /* Parameters: fin(I) - pointer to packet information */ /* pass(I) - IP protocol version to match */ /* l5proto(I) - layer 5 protocol to decode UDP data as. */ /* */ /* This function is called for packets that are wrapt up in other packets, */ /* for example, an IP packet that is the entire data segment for another IP */ /* packet. If the basic constraints for this are satisfied, change the */ /* buffer to point to the start of the inner packet and start processing */ /* rules belonging to the head group this rule specifies. */ /* ------------------------------------------------------------------------ */ u_32_t ipf_decaps(fr_info_t *fin, u_32_t pass, int l5proto) { fr_info_t fin2, *fino = NULL; int elen, hlen, nh; grehdr_t gre; ip_t *ip; mb_t *m; if ((fin->fin_flx & FI_COALESCE) == 0) if (ipf_coalesce(fin) == -1) goto cantdecaps; m = fin->fin_m; hlen = fin->fin_hlen; switch (fin->fin_p) { case IPPROTO_UDP : /* * In this case, the specific protocol being decapsulated * inside UDP frames comes from the rule. */ nh = fin->fin_fr->fr_icode; break; case IPPROTO_GRE : /* 47 */ bcopy(fin->fin_dp, (char *)&gre, sizeof(gre)); hlen += sizeof(grehdr_t); if (gre.gr_R|gre.gr_s) goto cantdecaps; if (gre.gr_C) hlen += 4; if (gre.gr_K) hlen += 4; if (gre.gr_S) hlen += 4; nh = IPPROTO_IP; /* * If the routing options flag is set, validate that it is * there and bounce over it. */ #if 0 /* This is really heavy weight and lots of room for error, */ /* so for now, put it off and get the simple stuff right. */ if (gre.gr_R) { u_char off, len, *s; u_short af; int end; end = 0; s = fin->fin_dp; s += hlen; aplen = fin->fin_plen - hlen; while (aplen > 3) { af = (s[0] << 8) | s[1]; off = s[2]; len = s[3]; aplen -= 4; s += 4; if (af == 0 && len == 0) { end = 1; break; } if (aplen < len) break; s += len; aplen -= len; } if (end != 1) goto cantdecaps; hlen = s - (u_char *)fin->fin_dp; } #endif break; #ifdef IPPROTO_IPIP case IPPROTO_IPIP : /* 4 */ #endif nh = IPPROTO_IP; break; default : /* Includes ESP, AH is special for IPv4 */ goto cantdecaps; } switch (nh) { case IPPROTO_IP : case IPPROTO_IPV6 : break; default : goto cantdecaps; } bcopy((char *)fin, (char *)&fin2, sizeof(fin2)); fino = fin; fin = &fin2; elen = hlen; #if SOLARIS && defined(_KERNEL) m->b_rptr += elen; #else m->m_data += elen; m->m_len -= elen; #endif fin->fin_plen -= elen; ip = (ip_t *)((char *)fin->fin_ip + elen); /* * Make sure we have at least enough data for the network layer * header. */ if (IP_V(ip) == 4) hlen = IP_HL(ip) << 2; #ifdef USE_INET6 else if (IP_V(ip) == 6) hlen = sizeof(ip6_t); #endif else goto cantdecaps2; if (fin->fin_plen < hlen) goto cantdecaps2; fin->fin_dp = (char *)ip + hlen; if (IP_V(ip) == 4) { /* * Perform IPv4 header checksum validation. */ if (ipf_cksum((u_short *)ip, hlen)) goto cantdecaps2; } if (ipf_makefrip(hlen, ip, fin) == -1) { cantdecaps2: if (m != NULL) { #if SOLARIS && defined(_KERNEL) m->b_rptr -= elen; #else m->m_data -= elen; m->m_len += elen; #endif } cantdecaps: DT1(frb_decapfrip, fr_info_t *, fin); pass &= ~FR_CMDMASK; pass |= FR_BLOCK|FR_QUICK; fin->fin_reason = FRB_DECAPFRIP; return (-1); } pass = ipf_scanlist(fin, pass); /* * Copy the packet filter "result" fields out of the fr_info_t struct * that is local to the decapsulation processing and back into the * one we were called with. */ fino->fin_flx = fin->fin_flx; fino->fin_rev = fin->fin_rev; fino->fin_icode = fin->fin_icode; fino->fin_rule = fin->fin_rule; (void) strncpy(fino->fin_group, fin->fin_group, FR_GROUPLEN); fino->fin_fr = fin->fin_fr; fino->fin_error = fin->fin_error; fino->fin_mp = fin->fin_mp; fino->fin_m = fin->fin_m; m = fin->fin_m; if (m != NULL) { #if SOLARIS && defined(_KERNEL) m->b_rptr -= elen; #else m->m_data -= elen; m->m_len += elen; #endif } return (pass); } /* ------------------------------------------------------------------------ */ /* Function: ipf_matcharray_load */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* objp(I) - ipfobj_t structure to load data into */ /* arrayptr(I) - pointer to location to store array pointer */ /* */ /* This function loads in a mathing array through the ipfobj_t struct that */ /* describes it. Sanity checking and array size limitations are enforced */ /* in this function to prevent userspace from trying to load in something */ /* that is insanely big. Once the size of the array is known, the memory */ /* required is malloc'd and returned through changing *arrayptr. The */ /* contents of the array are verified before returning. Only in the event */ /* of a successful call is the caller required to free up the malloc area. */ /* ------------------------------------------------------------------------ */ int ipf_matcharray_load(ipf_main_softc_t *softc, caddr_t data, ipfobj_t *objp, int **arrayptr) { int arraysize, *array, error; *arrayptr = NULL; error = BCOPYIN(data, objp, sizeof(*objp)); if (error != 0) { IPFERROR(116); return (EFAULT); } if (objp->ipfo_type != IPFOBJ_IPFEXPR) { IPFERROR(117); return (EINVAL); } if (((objp->ipfo_size & 3) != 0) || (objp->ipfo_size == 0) || (objp->ipfo_size > 1024)) { IPFERROR(118); return (EINVAL); } arraysize = objp->ipfo_size * sizeof(*array); KMALLOCS(array, int *, arraysize); if (array == NULL) { IPFERROR(119); return (ENOMEM); } error = COPYIN(objp->ipfo_ptr, array, arraysize); if (error != 0) { KFREES(array, arraysize); IPFERROR(120); return (EFAULT); } if (ipf_matcharray_verify(array, arraysize) != 0) { KFREES(array, arraysize); IPFERROR(121); return (EINVAL); } *arrayptr = array; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_matcharray_verify */ /* Returns: Nil */ /* Parameters: array(I) - pointer to matching array */ /* arraysize(I) - number of elements in the array */ /* */ /* Verify the contents of a matching array by stepping through each element */ /* in it. The actual commands in the array are not verified for */ /* correctness, only that all of the sizes are correctly within limits. */ /* ------------------------------------------------------------------------ */ int ipf_matcharray_verify(int *array, int arraysize) { int i, nelem, maxidx; ipfexp_t *e; nelem = arraysize / sizeof(*array); /* * Currently, it makes no sense to have an array less than 6 * elements long - the initial size at the from, a single operation * (minimum 4 in length) and a trailer, for a total of 6. */ if ((array[0] < 6) || (arraysize < 24) || (arraysize > 4096)) { return (-1); } /* * Verify the size of data pointed to by array with how long * the array claims to be itself. */ if (array[0] * sizeof(*array) != arraysize) { return (-1); } maxidx = nelem - 1; /* * The last opcode in this array should be an IPF_EXP_END. */ if (array[maxidx] != IPF_EXP_END) { return (-1); } for (i = 1; i < maxidx; ) { e = (ipfexp_t *)(array + i); /* * The length of the bits to check must be at least 1 * (or else there is nothing to comapre with!) and it * cannot exceed the length of the data present. */ if ((e->ipfe_size < 1 ) || (e->ipfe_size + i > maxidx)) { return (-1); } i += e->ipfe_size; } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_fr_matcharray */ /* Returns: int - 0 = match failed, else positive match */ /* Parameters: fin(I) - pointer to packet information */ /* array(I) - pointer to matching array */ /* */ /* This function is used to apply a matching array against a packet and */ /* return an indication of whether or not the packet successfully matches */ /* all of the commands in it. */ /* ------------------------------------------------------------------------ */ static int ipf_fr_matcharray(fr_info_t *fin, int *array) { int i, n, *x, rv, p; ipfexp_t *e; rv = 0; n = array[0]; x = array + 1; for (; n > 0; x += 3 + x[3], rv = 0) { e = (ipfexp_t *)x; if (e->ipfe_cmd == IPF_EXP_END) break; n -= e->ipfe_size; /* * The upper 16 bits currently store the protocol value. * This is currently used with TCP and UDP port compares and * allows "tcp.port = 80" without requiring an explicit " "ip.pr = tcp" first. */ p = e->ipfe_cmd >> 16; if ((p != 0) && (p != fin->fin_p)) break; switch (e->ipfe_cmd) { case IPF_EXP_IP_PR : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (fin->fin_p == e->ipfe_arg0[i]); } break; case IPF_EXP_IP_SRCADDR : if (fin->fin_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_DSTADDR : if (fin->fin_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_ADDR : if (fin->fin_v != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_saddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((fin->fin_daddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; #ifdef USE_INET6 case IPF_EXP_IP6_SRCADDR : if (fin->fin_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&fin->fin_src6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_DSTADDR : if (fin->fin_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&fin->fin_dst6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_ADDR : if (fin->fin_v != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&fin->fin_src6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&fin->fin_dst6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; #endif case IPF_EXP_UDP_PORT : case IPF_EXP_TCP_PORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (fin->fin_sport == e->ipfe_arg0[i]) || (fin->fin_dport == e->ipfe_arg0[i]); } break; case IPF_EXP_UDP_SPORT : case IPF_EXP_TCP_SPORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (fin->fin_sport == e->ipfe_arg0[i]); } break; case IPF_EXP_UDP_DPORT : case IPF_EXP_TCP_DPORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (fin->fin_dport == e->ipfe_arg0[i]); } break; case IPF_EXP_TCP_FLAGS : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((fin->fin_tcpf & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; } rv ^= e->ipfe_not; if (rv == 0) break; } return (rv); } /* ------------------------------------------------------------------------ */ /* Function: ipf_queueflush */ /* Returns: int - number of entries flushed (0 = none) */ /* Parameters: softc(I) - pointer to soft context main structure */ /* deletefn(I) - function to call to delete entry */ /* ipfqs(I) - top of the list of ipf internal queues */ /* userqs(I) - top of the list of user defined timeouts */ /* */ /* This fucntion gets called when the state/NAT hash tables fill up and we */ /* need to try a bit harder to free up some space. The algorithm used here */ /* split into two parts but both halves have the same goal: to reduce the */ /* number of connections considered to be "active" to the low watermark. */ /* There are two steps in doing this: */ /* 1) Remove any TCP connections that are already considered to be "closed" */ /* but have not yet been removed from the state table. The two states */ /* TCPS_TIME_WAIT and TCPS_CLOSED are considered to be the perfect */ /* candidates for this style of removal. If freeing up entries in */ /* CLOSED or both CLOSED and TIME_WAIT brings us to the low watermark, */ /* we do not go on to step 2. */ /* */ /* 2) Look for the oldest entries on each timeout queue and free them if */ /* they are within the given window we are considering. Where the */ /* window starts and the steps taken to increase its size depend upon */ /* how long ipf has been running (ipf_ticks.) Anything modified in the */ /* last 30 seconds is not touched. */ /* touched */ /* die ipf_ticks 30*1.5 1800*1.5 | 43200*1.5 */ /* | | | | | | */ /* future <--+----------+--------+-----------+-----+-----+-----------> past */ /* now \_int=30s_/ \_int=1hr_/ \_int=12hr */ /* */ /* Points to note: */ /* - tqe_die is the time, in the future, when entries die. */ /* - tqe_die - ipf_ticks is how long left the connection has to live in ipf */ /* ticks. */ /* - tqe_touched is when the entry was last used by NAT/state */ /* - the closer tqe_touched is to ipf_ticks, the further tqe_die will be */ /* ipf_ticks any given timeout queue and vice versa. */ /* - both tqe_die and tqe_touched increase over time */ /* - timeout queues are sorted with the highest value of tqe_die at the */ /* bottom and therefore the smallest values of each are at the top */ /* - the pointer passed in as ipfqs should point to an array of timeout */ /* queues representing each of the TCP states */ /* */ /* We start by setting up a maximum range to scan for things to move of */ /* iend (newest) to istart (oldest) in chunks of "interval". If nothing is */ /* found in that range, "interval" is adjusted (so long as it isn't 30) and */ /* we start again with a new value for "iend" and "istart". This is */ /* continued until we either finish the scan of 30 second intervals or the */ /* low water mark is reached. */ /* ------------------------------------------------------------------------ */ int ipf_queueflush(ipf_main_softc_t *softc, ipftq_delete_fn_t deletefn, ipftq_t *ipfqs, ipftq_t *userqs, u_int *activep, int size, int low) { u_long interval, istart, iend; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; int removed = 0; for (tqn = ipfqs[IPF_TCPS_CLOSED].ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } if ((*activep * 100 / size) > low) { for (tqn = ipfqs[IPF_TCPS_TIME_WAIT].ifq_head; ((tqe = tqn) != NULL); ) { tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } if ((*activep * 100 / size) <= low) { return (removed); } /* * NOTE: Use of "* 15 / 10" is required here because if "* 1.5" is * used then the operations are upgraded to floating point * and kernels don't like floating point... */ if (softc->ipf_ticks > IPF_TTLVAL(43200 * 15 / 10)) { istart = IPF_TTLVAL(86400 * 4); interval = IPF_TTLVAL(43200); } else if (softc->ipf_ticks > IPF_TTLVAL(1800 * 15 / 10)) { istart = IPF_TTLVAL(43200); interval = IPF_TTLVAL(1800); } else if (softc->ipf_ticks > IPF_TTLVAL(30 * 15 / 10)) { istart = IPF_TTLVAL(1800); interval = IPF_TTLVAL(30); } else { return (0); } if (istart > softc->ipf_ticks) { if (softc->ipf_ticks - interval < interval) istart = interval; else istart = (softc->ipf_ticks / interval) * interval; } iend = softc->ipf_ticks - interval; while ((*activep * 100 / size) > low) { u_long try; try = softc->ipf_ticks - istart; for (ifq = ipfqs; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (try < tqe->tqe_touched) break; tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } for (ifq = userqs; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { if (try < tqe->tqe_touched) break; tqn = tqe->tqe_next; if ((*deletefn)(softc, tqe->tqe_parent) == 0) removed++; } } if (try >= iend) { if (interval == IPF_TTLVAL(43200)) { interval = IPF_TTLVAL(1800); } else if (interval == IPF_TTLVAL(1800)) { interval = IPF_TTLVAL(30); } else { break; } if (interval >= softc->ipf_ticks) break; iend = softc->ipf_ticks - interval; } istart -= interval; } return (removed); } /* ------------------------------------------------------------------------ */ /* Function: ipf_deliverlocal */ /* Returns: int - 1 = local address, 0 = non-local address */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ipversion(I) - IP protocol version (4 or 6) */ /* ifp(I) - network interface pointer */ /* ipaddr(I) - IPv4/6 destination address */ /* */ /* This fucntion is used to determine in the address "ipaddr" belongs to */ /* the network interface represented by ifp. */ /* ------------------------------------------------------------------------ */ int ipf_deliverlocal(ipf_main_softc_t *softc, int ipversion, void *ifp, i6addr_t *ipaddr) { i6addr_t addr; int islocal = 0; if (ipversion == 4) { if (ipf_ifpaddr(softc, 4, FRI_NORMAL, ifp, &addr, NULL) == 0) { if (addr.in4.s_addr == ipaddr->in4.s_addr) islocal = 1; } #ifdef USE_INET6 } else if (ipversion == 6) { if (ipf_ifpaddr(softc, 6, FRI_NORMAL, ifp, &addr, NULL) == 0) { if (IP6_EQ(&addr, ipaddr)) islocal = 1; } #endif } return (islocal); } /* ------------------------------------------------------------------------ */ /* Function: ipf_settimeout */ /* Returns: int - 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tuneable array entry */ /* p(I) - pointer to values passed in to apply */ /* */ /* This function is called to set the timeout values for each distinct */ /* queue timeout that is available. When called, it calls into both the */ /* state and NAT code, telling them to update their timeout queues. */ /* ------------------------------------------------------------------------ */ static int ipf_settimeout(struct ipf_main_softc_s *softc, ipftuneable_t *t, ipftuneval_t *p) { /* * ipf_interror should be set by the functions called here, not * by this function - it's just a middle man. */ if (ipf_state_settimeout(softc, t, p) == -1) return (-1); if (ipf_nat_settimeout(softc, t, p) == -1) return (-1); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_apply_timeout */ /* Returns: int - 0 = success, -1 = failure */ /* Parameters: head(I) - pointer to tuneable array entry */ /* seconds(I) - pointer to values passed in to apply */ /* */ /* This function applies a timeout of "seconds" to the timeout queue that */ /* is pointed to by "head". All entries on this list have an expiration */ /* set to be the current tick value of ipf plus the ttl. Given that this */ /* function should only be called when the delta is non-zero, the task is */ /* to walk the entire list and apply the change. The sort order will not */ /* change. The only catch is that this is O(n) across the list, so if the */ /* queue has lots of entries (10s of thousands or 100s of thousands), it */ /* could take a relatively long time to work through them all. */ /* ------------------------------------------------------------------------ */ void ipf_apply_timeout(ipftq_t *head, u_int seconds) { u_int oldtimeout, newtimeout; ipftqent_t *tqe; int delta; MUTEX_ENTER(&head->ifq_lock); oldtimeout = head->ifq_ttl; newtimeout = IPF_TTLVAL(seconds); delta = oldtimeout - newtimeout; head->ifq_ttl = newtimeout; for (tqe = head->ifq_head; tqe != NULL; tqe = tqe->tqe_next) { tqe->tqe_die += delta; } MUTEX_EXIT(&head->ifq_lock); } /* ------------------------------------------------------------------------ */ /* Function: ipf_settimeout_tcp */ /* Returns: int - 0 = successfully applied, -1 = failed */ /* Parameters: t(I) - pointer to tuneable to change */ /* p(I) - pointer to new timeout information */ /* tab(I) - pointer to table of TCP queues */ /* */ /* This function applies the new timeout (p) to the TCP tunable (t) and */ /* updates all of the entries on the relevant timeout queue by calling */ /* ipf_apply_timeout(). */ /* ------------------------------------------------------------------------ */ int ipf_settimeout_tcp(ipftuneable_t *t, ipftuneval_t *p, ipftq_t *tab) { if (!strcmp(t->ipft_name, "tcp_idle_timeout") || !strcmp(t->ipft_name, "tcp_established")) { ipf_apply_timeout(&tab[IPF_TCPS_ESTABLISHED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_close_wait")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSE_WAIT], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_last_ack")) { ipf_apply_timeout(&tab[IPF_TCPS_LAST_ACK], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_timeout")) { ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_listen")) { ipf_apply_timeout(&tab[IPF_TCPS_LISTEN], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_half_established")) { ipf_apply_timeout(&tab[IPF_TCPS_HALF_ESTAB], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_closing")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSING], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_syn_received")) { ipf_apply_timeout(&tab[IPF_TCPS_SYN_RECEIVED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_syn_sent")) { ipf_apply_timeout(&tab[IPF_TCPS_SYN_SENT], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_closed")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_half_closed")) { ipf_apply_timeout(&tab[IPF_TCPS_CLOSED], p->ipftu_int); } else if (!strcmp(t->ipft_name, "tcp_time_wait")) { ipf_apply_timeout(&tab[IPF_TCPS_TIME_WAIT], p->ipftu_int); } else { /* * ipf_interror isn't set here because it should be set * by whatever called this function. */ return (-1); } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_create */ /* Returns: NULL = failure, else success */ /* Parameters: arg(I) - pointer to soft context structure if already allocd */ /* */ /* Create the foundation soft context structure. In circumstances where it */ /* is not required to dynamically allocate the context, a pointer can be */ /* passed in (rather than NULL) to a structure to be initialised. */ /* The main thing of interest is that a number of locks are initialised */ /* here instead of in the where might be expected - in the relevant create */ /* function elsewhere. This is done because the current locking design has */ /* some areas where these locks are used outside of their module. */ /* Possibly the most important exercise that is done here is setting of all */ /* the timeout values, allowing them to be changed before init(). */ /* ------------------------------------------------------------------------ */ void * ipf_main_soft_create(void *arg) { ipf_main_softc_t *softc; if (arg == NULL) { KMALLOC(softc, ipf_main_softc_t *); if (softc == NULL) return (NULL); } else { softc = arg; } bzero((char *)softc, sizeof(*softc)); /* * This serves as a flag as to whether or not the softc should be * free'd when _destroy is called. */ softc->ipf_dynamic_softc = (arg == NULL) ? 1 : 0; softc->ipf_tuners = ipf_tune_array_copy(softc, sizeof(ipf_main_tuneables), ipf_main_tuneables); if (softc->ipf_tuners == NULL) { ipf_main_soft_destroy(softc); return (NULL); } MUTEX_INIT(&softc->ipf_rw, "ipf rw mutex"); MUTEX_INIT(&softc->ipf_timeoutlock, "ipf timeout lock"); RWLOCK_INIT(&softc->ipf_global, "ipf filter load/unload mutex"); RWLOCK_INIT(&softc->ipf_mutex, "ipf filter rwlock"); RWLOCK_INIT(&softc->ipf_tokens, "ipf token rwlock"); RWLOCK_INIT(&softc->ipf_state, "ipf state rwlock"); RWLOCK_INIT(&softc->ipf_nat, "ipf IP NAT rwlock"); RWLOCK_INIT(&softc->ipf_poolrw, "ipf pool rwlock"); RWLOCK_INIT(&softc->ipf_frag, "ipf frag rwlock"); softc->ipf_token_head = NULL; softc->ipf_token_tail = &softc->ipf_token_head; softc->ipf_tcpidletimeout = FIVE_DAYS; softc->ipf_tcpclosewait = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcplastack = IPF_TTLVAL(30); softc->ipf_tcptimewait = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcptimeout = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcpsynsent = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcpsynrecv = IPF_TTLVAL(2 * TCP_MSL); softc->ipf_tcpclosed = IPF_TTLVAL(30); softc->ipf_tcphalfclosed = IPF_TTLVAL(2 * 3600); softc->ipf_udptimeout = IPF_TTLVAL(120); softc->ipf_udpacktimeout = IPF_TTLVAL(12); softc->ipf_icmptimeout = IPF_TTLVAL(60); softc->ipf_icmpacktimeout = IPF_TTLVAL(6); softc->ipf_iptimeout = IPF_TTLVAL(60); #if defined(IPFILTER_DEFAULT_BLOCK) softc->ipf_pass = FR_BLOCK|FR_NOMATCH; #else softc->ipf_pass = (IPF_DEFAULT_PASS)|FR_NOMATCH; #endif softc->ipf_minttl = 4; softc->ipf_icmpminfragmtu = 68; softc->ipf_flags = IPF_LOGGING; #ifdef LARGE_NAT softc->ipf_large_nat = 1; #endif ipf_fbsd_kenv_get(softc); return (softc); } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_init */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ int ipf_main_soft_init(ipf_main_softc_t *softc) { return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_destroy */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Undo everything that we did in ipf_main_soft_create. */ /* */ /* The most important check that needs to be made here is whether or not */ /* the structure was allocated by ipf_main_soft_create() by checking what */ /* value is stored in ipf_dynamic_main. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ void ipf_main_soft_destroy(ipf_main_softc_t *softc) { RW_DESTROY(&softc->ipf_frag); RW_DESTROY(&softc->ipf_poolrw); RW_DESTROY(&softc->ipf_nat); RW_DESTROY(&softc->ipf_state); RW_DESTROY(&softc->ipf_tokens); RW_DESTROY(&softc->ipf_mutex); RW_DESTROY(&softc->ipf_global); MUTEX_DESTROY(&softc->ipf_timeoutlock); MUTEX_DESTROY(&softc->ipf_rw); if (softc->ipf_tuners != NULL) { KFREES(softc->ipf_tuners, sizeof(ipf_main_tuneables)); } if (softc->ipf_dynamic_softc == 1) { KFREE(softc); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_soft_fini */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Clean out the rules which have been added since _init was last called, */ /* the only dynamic part of the mainline. */ /* ------------------------------------------------------------------------ */ int ipf_main_soft_fini(ipf_main_softc_t *softc) { (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE|FR_INACTIVE); (void) ipf_flush(softc, IPL_LOGIPF, FR_INQUE|FR_OUTQUE); (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE|FR_INACTIVE); (void) ipf_flush(softc, IPL_LOGCOUNT, FR_INQUE|FR_OUTQUE); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_load */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* Handle global initialisation that needs to be done for the base part of */ /* IPFilter. At present this just amounts to initialising some ICMP lookup */ /* arrays that get used by the state/NAT code. */ /* ------------------------------------------------------------------------ */ int ipf_main_load(void) { int i; /* fill icmp reply type table */ for (i = 0; i <= ICMP_MAXTYPE; i++) icmpreplytype4[i] = -1; icmpreplytype4[ICMP_ECHO] = ICMP_ECHOREPLY; icmpreplytype4[ICMP_TSTAMP] = ICMP_TSTAMPREPLY; icmpreplytype4[ICMP_IREQ] = ICMP_IREQREPLY; icmpreplytype4[ICMP_MASKREQ] = ICMP_MASKREPLY; #ifdef USE_INET6 /* fill icmp reply type table */ for (i = 0; i <= ICMP6_MAXTYPE; i++) icmpreplytype6[i] = -1; icmpreplytype6[ICMP6_ECHO_REQUEST] = ICMP6_ECHO_REPLY; icmpreplytype6[ICMP6_MEMBERSHIP_QUERY] = ICMP6_MEMBERSHIP_REPORT; icmpreplytype6[ICMP6_NI_QUERY] = ICMP6_NI_REPLY; icmpreplytype6[ND_ROUTER_SOLICIT] = ND_ROUTER_ADVERT; icmpreplytype6[ND_NEIGHBOR_SOLICIT] = ND_NEIGHBOR_ADVERT; #endif return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_main_unload */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_main_unload(void) { return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_load_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* Work through all of the subsystems inside IPFilter and call the load */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int ipf_load_all(void) { if (ipf_main_load() == -1) return (-1); if (ipf_state_main_load() == -1) return (-1); if (ipf_nat_main_load() == -1) return (-1); if (ipf_frag_main_load() == -1) return (-1); if (ipf_auth_main_load() == -1) return (-1); if (ipf_proxy_main_load() == -1) return (-1); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_unload_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: none */ /* */ /* Work through all of the subsystems inside IPFilter and call the unload */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int ipf_unload_all(void) { if (ipf_proxy_main_unload() == -1) return (-1); if (ipf_auth_main_unload() == -1) return (-1); if (ipf_frag_main_unload() == -1) return (-1); if (ipf_nat_main_unload() == -1) return (-1); if (ipf_state_main_unload() == -1) return (-1); if (ipf_main_unload() == -1) return (-1); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_create_all */ /* Returns: NULL = failure, else success */ /* Parameters: arg(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the create */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ ipf_main_softc_t * ipf_create_all(void *arg) { ipf_main_softc_t *softc; softc = ipf_main_soft_create(arg); if (softc == NULL) return (NULL); #ifdef IPFILTER_LOG softc->ipf_log_soft = ipf_log_soft_create(softc); if (softc->ipf_log_soft == NULL) { ipf_destroy_all(softc); return (NULL); } #endif softc->ipf_lookup_soft = ipf_lookup_soft_create(softc); if (softc->ipf_lookup_soft == NULL) { ipf_destroy_all(softc); return (NULL); } softc->ipf_sync_soft = ipf_sync_soft_create(softc); if (softc->ipf_sync_soft == NULL) { ipf_destroy_all(softc); return (NULL); } softc->ipf_state_soft = ipf_state_soft_create(softc); if (softc->ipf_state_soft == NULL) { ipf_destroy_all(softc); return (NULL); } softc->ipf_nat_soft = ipf_nat_soft_create(softc); if (softc->ipf_nat_soft == NULL) { ipf_destroy_all(softc); return (NULL); } softc->ipf_frag_soft = ipf_frag_soft_create(softc); if (softc->ipf_frag_soft == NULL) { ipf_destroy_all(softc); return (NULL); } softc->ipf_auth_soft = ipf_auth_soft_create(softc); if (softc->ipf_auth_soft == NULL) { ipf_destroy_all(softc); return (NULL); } softc->ipf_proxy_soft = ipf_proxy_soft_create(softc); if (softc->ipf_proxy_soft == NULL) { ipf_destroy_all(softc); return (NULL); } return (softc); } /* ------------------------------------------------------------------------ */ /* Function: ipf_destroy_all */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the destroy */ /* function for each in an order that won't lead to a crash :) */ /* */ /* Every one of these functions is expected to succeed, so there is no */ /* checking of return values. */ /* ------------------------------------------------------------------------ */ void ipf_destroy_all(ipf_main_softc_t *softc) { if (softc->ipf_state_soft != NULL) { ipf_state_soft_destroy(softc, softc->ipf_state_soft); softc->ipf_state_soft = NULL; } if (softc->ipf_nat_soft != NULL) { ipf_nat_soft_destroy(softc, softc->ipf_nat_soft); softc->ipf_nat_soft = NULL; } if (softc->ipf_frag_soft != NULL) { ipf_frag_soft_destroy(softc, softc->ipf_frag_soft); softc->ipf_frag_soft = NULL; } if (softc->ipf_auth_soft != NULL) { ipf_auth_soft_destroy(softc, softc->ipf_auth_soft); softc->ipf_auth_soft = NULL; } if (softc->ipf_proxy_soft != NULL) { ipf_proxy_soft_destroy(softc, softc->ipf_proxy_soft); softc->ipf_proxy_soft = NULL; } if (softc->ipf_sync_soft != NULL) { ipf_sync_soft_destroy(softc, softc->ipf_sync_soft); softc->ipf_sync_soft = NULL; } if (softc->ipf_lookup_soft != NULL) { ipf_lookup_soft_destroy(softc, softc->ipf_lookup_soft); softc->ipf_lookup_soft = NULL; } #ifdef IPFILTER_LOG if (softc->ipf_log_soft != NULL) { ipf_log_soft_destroy(softc, softc->ipf_log_soft); softc->ipf_log_soft = NULL; } #endif ipf_main_soft_destroy(softc); } /* ------------------------------------------------------------------------ */ /* Function: ipf_init_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the init */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int ipf_init_all(ipf_main_softc_t *softc) { if (ipf_main_soft_init(softc) == -1) return (-1); #ifdef IPFILTER_LOG if (ipf_log_soft_init(softc, softc->ipf_log_soft) == -1) return (-1); #endif if (ipf_lookup_soft_init(softc, softc->ipf_lookup_soft) == -1) return (-1); if (ipf_sync_soft_init(softc, softc->ipf_sync_soft) == -1) return (-1); if (ipf_state_soft_init(softc, softc->ipf_state_soft) == -1) return (-1); if (ipf_nat_soft_init(softc, softc->ipf_nat_soft) == -1) return (-1); if (ipf_frag_soft_init(softc, softc->ipf_frag_soft) == -1) return (-1); if (ipf_auth_soft_init(softc, softc->ipf_auth_soft) == -1) return (-1); if (ipf_proxy_soft_init(softc, softc->ipf_proxy_soft) == -1) return (-1); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_fini_all */ /* Returns: 0 = success, -1 = failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Work through all of the subsystems inside IPFilter and call the fini */ /* function for each in an order that won't lead to a crash :) */ /* ------------------------------------------------------------------------ */ int ipf_fini_all(ipf_main_softc_t *softc) { ipf_token_flush(softc); if (ipf_proxy_soft_fini(softc, softc->ipf_proxy_soft) == -1) return (-1); if (ipf_auth_soft_fini(softc, softc->ipf_auth_soft) == -1) return (-1); if (ipf_frag_soft_fini(softc, softc->ipf_frag_soft) == -1) return (-1); if (ipf_nat_soft_fini(softc, softc->ipf_nat_soft) == -1) return (-1); if (ipf_state_soft_fini(softc, softc->ipf_state_soft) == -1) return (-1); if (ipf_sync_soft_fini(softc, softc->ipf_sync_soft) == -1) return (-1); if (ipf_lookup_soft_fini(softc, softc->ipf_lookup_soft) == -1) return (-1); #ifdef IPFILTER_LOG if (ipf_log_soft_fini(softc, softc->ipf_log_soft) == -1) return (-1); #endif if (ipf_main_soft_fini(softc) == -1) return (-1); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rule_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* At present this function exists just to support temporary addition of */ /* firewall rules. Both inactive and active lists are scanned for items to */ /* purge, as by rights, the expiration is computed as soon as the rule is */ /* loaded in. */ /* ------------------------------------------------------------------------ */ void ipf_rule_expire(ipf_main_softc_t *softc) { frentry_t *fr; if ((softc->ipf_rule_explist[0] == NULL) && (softc->ipf_rule_explist[1] == NULL)) return; WRITE_ENTER(&softc->ipf_mutex); while ((fr = softc->ipf_rule_explist[0]) != NULL) { /* * Because the list is kept sorted on insertion, the fist * one that dies in the future means no more work to do. */ if (fr->fr_die > softc->ipf_ticks) break; ipf_rule_delete(softc, fr, IPL_LOGIPF, 0); } while ((fr = softc->ipf_rule_explist[1]) != NULL) { /* * Because the list is kept sorted on insertion, the fist * one that dies in the future means no more work to do. */ if (fr->fr_die > softc->ipf_ticks) break; ipf_rule_delete(softc, fr, IPL_LOGIPF, 1); } RWLOCK_EXIT(&softc->ipf_mutex); } static int ipf_ht_node_cmp(struct host_node_s *, struct host_node_s *); static void ipf_ht_node_make_key(host_track_t *, host_node_t *, int, i6addr_t *); host_node_t RBI_ZERO(ipf_rb); RBI_CODE(ipf_rb, host_node_t, hn_entry, ipf_ht_node_cmp) /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_cmp */ /* Returns: int - 0 == nodes are the same, .. */ /* Parameters: k1(I) - pointer to first key to compare */ /* k2(I) - pointer to second key to compare */ /* */ /* The "key" for the node is a combination of two fields: the address */ /* family and the address itself. */ /* */ /* Because we're not actually interpreting the address data, it isn't */ /* necessary to convert them to/from network/host byte order. The mask is */ /* just used to remove bits that aren't significant - it doesn't matter */ /* where they are, as long as they're always in the same place. */ /* */ /* As with IP6_EQ, comparing IPv6 addresses starts at the bottom because */ /* this is where individual ones will differ the most - but not true for */ /* for /48's, etc. */ /* ------------------------------------------------------------------------ */ static int ipf_ht_node_cmp(struct host_node_s *k1, struct host_node_s *k2) { int i; i = (k2->hn_addr.adf_family - k1->hn_addr.adf_family); if (i != 0) return (i); if (k1->hn_addr.adf_family == AF_INET) return (k2->hn_addr.adf_addr.in4.s_addr - k1->hn_addr.adf_addr.in4.s_addr); i = k2->hn_addr.adf_addr.i6[3] - k1->hn_addr.adf_addr.i6[3]; if (i != 0) return (i); i = k2->hn_addr.adf_addr.i6[2] - k1->hn_addr.adf_addr.i6[2]; if (i != 0) return (i); i = k2->hn_addr.adf_addr.i6[1] - k1->hn_addr.adf_addr.i6[1]; if (i != 0) return (i); i = k2->hn_addr.adf_addr.i6[0] - k1->hn_addr.adf_addr.i6[0]; return (i); } /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_make_key */ /* Returns: Nil */ /* parameters: htp(I) - pointer to address tracking structure */ /* key(I) - where to store masked address for lookup */ /* family(I) - protocol family of address */ /* addr(I) - pointer to network address */ /* */ /* Using the "netmask" (number of bits) stored parent host tracking struct, */ /* copy the address passed in into the key structure whilst masking out the */ /* bits that we don't want. */ /* */ /* Because the parser will set ht_netmask to 128 if there is no protocol */ /* specified (the parser doesn't know if it should be a v4 or v6 rule), we */ /* have to be wary of that and not allow 32-128 to happen. */ /* ------------------------------------------------------------------------ */ static void ipf_ht_node_make_key(host_track_t *htp, host_node_t *key, int family, i6addr_t *addr) { key->hn_addr.adf_family = family; if (family == AF_INET) { u_32_t mask; int bits; key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in4); bits = htp->ht_netmask; if (bits >= 32) { mask = 0xffffffff; } else { mask = htonl(0xffffffff << (32 - bits)); } key->hn_addr.adf_addr.in4.s_addr = addr->in4.s_addr & mask; #ifdef USE_INET6 } else { int bits = htp->ht_netmask; key->hn_addr.adf_len = sizeof(key->hn_addr.adf_addr.in6); if (bits > 96) { key->hn_addr.adf_addr.i6[3] = addr->i6[3] & htonl(0xffffffff << (128 - bits)); key->hn_addr.adf_addr.i6[2] = addr->i6[2]; key->hn_addr.adf_addr.i6[1] = addr->i6[2]; key->hn_addr.adf_addr.i6[0] = addr->i6[2]; } else if (bits > 64) { key->hn_addr.adf_addr.i6[3] = 0; key->hn_addr.adf_addr.i6[2] = addr->i6[2] & htonl(0xffffffff << (96 - bits)); key->hn_addr.adf_addr.i6[1] = addr->i6[1]; key->hn_addr.adf_addr.i6[0] = addr->i6[0]; } else if (bits > 32) { key->hn_addr.adf_addr.i6[3] = 0; key->hn_addr.adf_addr.i6[2] = 0; key->hn_addr.adf_addr.i6[1] = addr->i6[1] & htonl(0xffffffff << (64 - bits)); key->hn_addr.adf_addr.i6[0] = addr->i6[0]; } else { key->hn_addr.adf_addr.i6[3] = 0; key->hn_addr.adf_addr.i6[2] = 0; key->hn_addr.adf_addr.i6[1] = 0; key->hn_addr.adf_addr.i6[0] = addr->i6[0] & htonl(0xffffffff << (32 - bits)); } #endif } } /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_add */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* htp(I) - pointer to address tracking structure */ /* family(I) - protocol family of address */ /* addr(I) - pointer to network address */ /* */ /* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ /* ipf_ht_node_del FROM RUNNING CONCURRENTLY ON THE SAME htp. */ /* */ /* After preparing the key with the address information to find, look in */ /* the red-black tree to see if the address is known. A successful call to */ /* this function can mean one of two things: a new node was added to the */ /* tree or a matching node exists and we're able to bump up its activity. */ /* ------------------------------------------------------------------------ */ int ipf_ht_node_add(ipf_main_softc_t *softc, host_track_t *htp, int family, i6addr_t *addr) { host_node_t *h; host_node_t k; ipf_ht_node_make_key(htp, &k, family, addr); h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); if (h == NULL) { if (htp->ht_cur_nodes >= htp->ht_max_nodes) return (-1); KMALLOC(h, host_node_t *); if (h == NULL) { DT(ipf_rb_no_mem); LBUMP(ipf_rb_no_mem); return (-1); } /* * If there was a macro to initialise the RB node then that * would get used here, but there isn't... */ bzero((char *)h, sizeof(*h)); h->hn_addr = k.hn_addr; h->hn_addr.adf_family = k.hn_addr.adf_family; RBI_INSERT(ipf_rb, &htp->ht_root, h); htp->ht_cur_nodes++; } else { if ((htp->ht_max_per_node != 0) && (h->hn_active >= htp->ht_max_per_node)) { DT(ipf_rb_node_max); LBUMP(ipf_rb_node_max); return (-1); } } h->hn_active++; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_ht_node_del */ /* Returns: int - 0 == success, -1 == failure */ /* parameters: htp(I) - pointer to address tracking structure */ /* family(I) - protocol family of address */ /* addr(I) - pointer to network address */ /* */ /* NOTE: THIS FUNCTION MUST BE CALLED WITH AN EXCLUSIVE LOCK THAT PREVENTS */ /* ipf_ht_node_add FROM RUNNING CONCURRENTLY ON THE SAME htp. */ /* */ /* Try and find the address passed in amongst the leavese on this tree to */ /* be friend. If found then drop the active account for that node drops by */ /* one. If that count reaches 0, it is time to free it all up. */ /* ------------------------------------------------------------------------ */ int ipf_ht_node_del(host_track_t *htp, int family, i6addr_t *addr) { host_node_t *h; host_node_t k; ipf_ht_node_make_key(htp, &k, family, addr); h = RBI_SEARCH(ipf_rb, &htp->ht_root, &k); if (h == NULL) { return (-1); } else { h->hn_active--; if (h->hn_active == 0) { (void) RBI_DELETE(ipf_rb, &htp->ht_root, h); htp->ht_cur_nodes--; KFREE(h); } } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rb_ht_init */ /* Returns: Nil */ /* Parameters: head(I) - pointer to host tracking structure */ /* */ /* Initialise the host tracking structure to be ready for use above. */ /* ------------------------------------------------------------------------ */ void ipf_rb_ht_init(host_track_t *head) { RBI_INIT(ipf_rb, &head->ht_root); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rb_ht_freenode */ /* Returns: Nil */ /* Parameters: head(I) - pointer to host tracking structure */ /* arg(I) - additional argument from walk caller */ /* */ /* Free an actual host_node_t structure. */ /* ------------------------------------------------------------------------ */ void ipf_rb_ht_freenode(host_node_t *node, void *arg) { KFREE(node); } /* ------------------------------------------------------------------------ */ /* Function: ipf_rb_ht_flush */ /* Returns: Nil */ /* Parameters: head(I) - pointer to host tracking structure */ /* */ /* Remove all of the nodes in the tree tracking hosts by calling a walker */ /* and free'ing each one. */ /* ------------------------------------------------------------------------ */ void ipf_rb_ht_flush(host_track_t *head) { RBI_WALK(ipf_rb, &head->ht_root, ipf_rb_ht_freenode, NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_slowtimer */ /* Returns: Nil */ /* Parameters: ptr(I) - pointer to main ipf soft context structure */ /* */ /* Slowly expire held state for fragments. Timeouts are set * in */ /* expectation of this being called twice per second. */ /* ------------------------------------------------------------------------ */ void ipf_slowtimer(ipf_main_softc_t *softc) { ipf_token_expire(softc); ipf_frag_expire(softc); ipf_state_expire(softc); ipf_nat_expire(softc); ipf_auth_expire(softc); ipf_lookup_expire(softc); ipf_rule_expire(softc); ipf_sync_expire(softc); softc->ipf_ticks++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inet_mask_add */ /* Returns: Nil */ /* Parameters: bits(I) - pointer to nat context information */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* When called, bits represents the mask of a new NAT rule that has just */ /* been added. This function inserts a bitmask into the array of masks to */ /* search when searching for a matching NAT rule for a packet. */ /* Prevention of duplicate masks is achieved by checking the use count for */ /* a given netmask. */ /* ------------------------------------------------------------------------ */ void ipf_inet_mask_add(int bits, ipf_v4_masktab_t *mtab) { u_32_t mask; int i, j; mtab->imt4_masks[bits]++; if (mtab->imt4_masks[bits] > 1) return; if (bits == 0) mask = 0; else mask = 0xffffffff << (32 - bits); for (i = 0; i < 33; i++) { if (ntohl(mtab->imt4_active[i]) < mask) { for (j = 32; j > i; j--) mtab->imt4_active[j] = mtab->imt4_active[j - 1]; mtab->imt4_active[i] = htonl(mask); break; } } mtab->imt4_max++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inet_mask_del */ /* Returns: Nil */ /* Parameters: bits(I) - number of bits set in the netmask */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* Remove the 32bit bitmask represented by "bits" from the collection of */ /* netmasks stored inside of mtab. */ /* ------------------------------------------------------------------------ */ void ipf_inet_mask_del(int bits, ipf_v4_masktab_t *mtab) { u_32_t mask; int i, j; mtab->imt4_masks[bits]--; if (mtab->imt4_masks[bits] > 0) return; mask = htonl(0xffffffff << (32 - bits)); for (i = 0; i < 33; i++) { if (mtab->imt4_active[i] == mask) { for (j = i + 1; j < 33; j++) mtab->imt4_active[j - 1] = mtab->imt4_active[j]; break; } } mtab->imt4_max--; ASSERT(mtab->imt4_max >= 0); } #ifdef USE_INET6 /* ------------------------------------------------------------------------ */ /* Function: ipf_inet6_mask_add */ /* Returns: Nil */ /* Parameters: bits(I) - number of bits set in mask */ /* mask(I) - pointer to mask to add */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* When called, bitcount represents the mask of a IPv6 NAT map rule that */ /* has just been added. This function inserts a bitmask into the array of */ /* masks to search when searching for a matching NAT rule for a packet. */ /* Prevention of duplicate masks is achieved by checking the use count for */ /* a given netmask. */ /* ------------------------------------------------------------------------ */ void ipf_inet6_mask_add(int bits, i6addr_t *mask, ipf_v6_masktab_t *mtab) { i6addr_t zero; int i, j; mtab->imt6_masks[bits]++; if (mtab->imt6_masks[bits] > 1) return; if (bits == 0) { mask = &zero; zero.i6[0] = 0; zero.i6[1] = 0; zero.i6[2] = 0; zero.i6[3] = 0; } for (i = 0; i < 129; i++) { if (IP6_LT(&mtab->imt6_active[i], mask)) { for (j = 128; j > i; j--) mtab->imt6_active[j] = mtab->imt6_active[j - 1]; mtab->imt6_active[i] = *mask; break; } } mtab->imt6_max++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_inet6_mask_del */ /* Returns: Nil */ /* Parameters: bits(I) - number of bits set in mask */ /* mask(I) - pointer to mask to remove */ /* mtab(I) - pointer to mask hash table structure */ /* */ /* Remove the 128bit bitmask represented by "bits" from the collection of */ /* netmasks stored inside of mtab. */ /* ------------------------------------------------------------------------ */ void ipf_inet6_mask_del(int bits, i6addr_t *mask, ipf_v6_masktab_t *mtab) { i6addr_t zero; int i, j; mtab->imt6_masks[bits]--; if (mtab->imt6_masks[bits] > 0) return; if (bits == 0) mask = &zero; zero.i6[0] = 0; zero.i6[1] = 0; zero.i6[2] = 0; zero.i6[3] = 0; for (i = 0; i < 129; i++) { if (IP6_EQ(&mtab->imt6_active[i], mask)) { for (j = i + 1; j < 129; j++) { mtab->imt6_active[j - 1] = mtab->imt6_active[j]; if (IP6_EQ(&mtab->imt6_active[j - 1], &zero)) break; } break; } } mtab->imt6_max--; ASSERT(mtab->imt6_max >= 0); } #endif diff --git a/sys/netpfil/ipfilter/netinet/ip_auth.c b/sys/netpfil/ipfilter/netinet/ip_auth.c index 5ce89c93c199..1f2de1d34eb1 100644 --- a/sys/netpfil/ipfilter/netinet/ip_auth.c +++ b/sys/netpfil/ipfilter/netinet/ip_auth.c @@ -1,1207 +1,1206 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if !defined(_KERNEL) # include # include # ifdef _STDC_C99 # include # endif # include # define _KERNEL # include # undef _KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include # include #else # include #endif # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if defined(__SVR4) # include # include # ifdef _KERNEL # include # endif # include # include #endif #if defined(__FreeBSD__) # include #endif #if defined(__NetBSD__) # include #endif #if defined(_KERNEL) && defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) # include #endif #if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 400000) && \ !defined(_KERNEL) # include #endif #include #ifdef sun # include #endif #include #include #include # include #if !defined(_KERNEL) # define KERNEL # define _KERNEL # define NOT_KERNEL #endif #ifdef NOT_KERNEL # undef _KERNEL # undef KERNEL #endif #include #if defined(__FreeBSD__) # include # define IF_QFULL _IF_QFULL # define IF_DROP _IF_DROP #endif #include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_auth.h" #if !SOLARIS # include # ifdef __FreeBSD__ # include # endif #endif #if defined(__FreeBSD__) # include # if defined(_KERNEL) && !defined(IPFILTER_LKM) # include # include # endif #endif /* END OF INCLUDES */ #if !defined(lint) -static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.73.2.24 2007/09/09 11:32:04 darrenr Exp $"; */ #endif static void ipf_auth_deref(frauthent_t **); static void ipf_auth_deref_unlocked(ipf_auth_softc_t *, frauthent_t **); static int ipf_auth_geniter(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfobj_t *); static int ipf_auth_reply(ipf_main_softc_t *, ipf_auth_softc_t *, char *); static int ipf_auth_wait(ipf_main_softc_t *, ipf_auth_softc_t *, char *); static int ipf_auth_flush(void *); /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_main_load */ /* Returns: int - 0 == success, else error */ /* Parameters: None */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_auth_main_load(void) { return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_main_unload */ /* Returns: int - 0 == success, else error */ /* Parameters: None */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_auth_main_unload(void) { return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_create */ /* Returns: int - NULL = failure, else success */ /* Parameters: softc(I) - pointer to soft context data */ /* */ /* Create a structre to store all of the run-time data for packet auth in */ /* and initialise some fields to their defaults. */ /* ------------------------------------------------------------------------ */ void * ipf_auth_soft_create(ipf_main_softc_t *softc) { ipf_auth_softc_t *softa; KMALLOC(softa, ipf_auth_softc_t *); if (softa == NULL) return (NULL); bzero((char *)softa, sizeof(*softa)); softa->ipf_auth_size = FR_NUMAUTH; softa->ipf_auth_defaultage = 600; RWLOCK_INIT(&softa->ipf_authlk, "ipf IP User-Auth rwlock"); MUTEX_INIT(&softa->ipf_auth_mx, "ipf auth log mutex"); #if SOLARIS && defined(_KERNEL) cv_init(&softa->ipf_auth_wait, "ipf auth condvar", CV_DRIVER, NULL); #endif return (softa); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_init */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context data */ /* arg(I) - opaque pointer to auth context data */ /* */ /* Allocate memory and initialise data structures used in handling auth */ /* rules. */ /* ------------------------------------------------------------------------ */ int ipf_auth_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_auth_softc_t *softa = arg; KMALLOCS(softa->ipf_auth, frauth_t *, softa->ipf_auth_size * sizeof(*softa->ipf_auth)); if (softa->ipf_auth == NULL) return (-1); bzero((char *)softa->ipf_auth, softa->ipf_auth_size * sizeof(*softa->ipf_auth)); KMALLOCS(softa->ipf_auth_pkts, mb_t **, softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); if (softa->ipf_auth_pkts == NULL) return (-2); bzero((char *)softa->ipf_auth_pkts, softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_fini */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context data */ /* arg(I) - opaque pointer to auth context data */ /* */ /* Free all network buffer memory used to keep saved packets that have been */ /* connectedd to the soft soft context structure *but* do not free that: it */ /* is free'd by _destroy(). */ /* ------------------------------------------------------------------------ */ int ipf_auth_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_auth_softc_t *softa = arg; frauthent_t *fae, **faep; frentry_t *fr, **frp; mb_t *m; int i; if (softa->ipf_auth != NULL) { KFREES(softa->ipf_auth, softa->ipf_auth_size * sizeof(*softa->ipf_auth)); softa->ipf_auth = NULL; } if (softa->ipf_auth_pkts != NULL) { for (i = 0; i < softa->ipf_auth_size; i++) { m = softa->ipf_auth_pkts[i]; if (m != NULL) { FREE_MB_T(m); softa->ipf_auth_pkts[i] = NULL; } } KFREES(softa->ipf_auth_pkts, softa->ipf_auth_size * sizeof(*softa->ipf_auth_pkts)); softa->ipf_auth_pkts = NULL; } faep = &softa->ipf_auth_entries; while ((fae = *faep) != NULL) { *faep = fae->fae_next; KFREE(fae); } softa->ipf_auth_ip = NULL; if (softa->ipf_auth_rules != NULL) { for (frp = &softa->ipf_auth_rules; ((fr = *frp) != NULL); ) { if (fr->fr_ref == 1) { *frp = fr->fr_next; MUTEX_DESTROY(&fr->fr_lock); KFREE(fr); } else frp = &fr->fr_next; } } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_soft_destroy */ /* Returns: void */ /* Parameters: softc(I) - pointer to soft context data */ /* arg(I) - opaque pointer to auth context data */ /* */ /* Undo what was done in _create() - i.e. free the soft context data. */ /* ------------------------------------------------------------------------ */ void ipf_auth_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_auth_softc_t *softa = arg; #if SOLARIS && defined(_KERNEL) cv_destroy(&softa->ipf_auth_wait); #endif MUTEX_DESTROY(&softa->ipf_auth_mx); RW_DESTROY(&softa->ipf_authlk); KFREE(softa); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_setlock */ /* Returns: void */ /* Paramters: arg(I) - pointer to soft context data */ /* tmp(I) - value to assign to auth lock */ /* */ /* ------------------------------------------------------------------------ */ void ipf_auth_setlock(void *arg, int tmp) { ipf_auth_softc_t *softa = arg; softa->ipf_auth_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_check */ /* Returns: frentry_t* - pointer to ipf rule if match found, else NULL */ /* Parameters: fin(I) - pointer to ipftoken structure */ /* passp(I) - pointer to ipfgeniter structure */ /* */ /* Check if a packet has authorization. If the packet is found to match an */ /* authorization result and that would result in a feedback loop (i.e. it */ /* will end up returning FR_AUTH) then return FR_BLOCK instead. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_auth_check(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_auth_softc_t *softa = softc->ipf_auth_soft; frentry_t *fr; frauth_t *fra; u_32_t pass; u_short id; ip_t *ip; int i; if (softa->ipf_auth_lock || !softa->ipf_auth_used) return (NULL); ip = fin->fin_ip; id = ip->ip_id; READ_ENTER(&softa->ipf_authlk); for (i = softa->ipf_auth_start; i != softa->ipf_auth_end; ) { /* * index becomes -2 only after an SIOCAUTHW. Check this in * case the same packet gets sent again and it hasn't yet been * auth'd. */ fra = softa->ipf_auth + i; if ((fra->fra_index == -2) && (id == fra->fra_info.fin_id) && !bcmp((char *)fin, (char *)&fra->fra_info, FI_CSIZE)) { /* * Avoid feedback loop. */ if (!(pass = fra->fra_pass) || (FR_ISAUTH(pass))) { pass = FR_BLOCK; fin->fin_reason = FRB_AUTHFEEDBACK; } /* * Create a dummy rule for the stateful checking to * use and return. Zero out any values we don't * trust from userland! */ if ((pass & FR_KEEPSTATE) || ((pass & FR_KEEPFRAG) && (fin->fin_flx & FI_FRAG))) { KMALLOC(fr, frentry_t *); if (fr) { bcopy((char *)fra->fra_info.fin_fr, (char *)fr, sizeof(*fr)); fr->fr_grp = NULL; fr->fr_ifa = fin->fin_ifp; fr->fr_func = NULL; fr->fr_ref = 1; fr->fr_flags = pass; fr->fr_ifas[1] = NULL; fr->fr_ifas[2] = NULL; fr->fr_ifas[3] = NULL; MUTEX_INIT(&fr->fr_lock, "ipf auth rule"); } } else fr = fra->fra_info.fin_fr; fin->fin_fr = fr; fin->fin_flx |= fra->fra_flx; RWLOCK_EXIT(&softa->ipf_authlk); WRITE_ENTER(&softa->ipf_authlk); /* * ipf_auth_rules is populated with the rules malloc'd * above and only those. */ if ((fr != NULL) && (fr != fra->fra_info.fin_fr)) { fr->fr_next = softa->ipf_auth_rules; softa->ipf_auth_rules = fr; } softa->ipf_auth_stats.fas_hits++; fra->fra_index = -1; softa->ipf_auth_used--; softa->ipf_auth_replies--; if (i == softa->ipf_auth_start) { while (fra->fra_index == -1) { i++; fra++; if (i == softa->ipf_auth_size) { i = 0; fra = softa->ipf_auth; } softa->ipf_auth_start = i; if (i == softa->ipf_auth_end) break; } if (softa->ipf_auth_start == softa->ipf_auth_end) { softa->ipf_auth_next = 0; softa->ipf_auth_start = 0; softa->ipf_auth_end = 0; } } RWLOCK_EXIT(&softa->ipf_authlk); if (passp != NULL) *passp = pass; softa->ipf_auth_stats.fas_hits++; return (fr); } i++; if (i == softa->ipf_auth_size) i = 0; } RWLOCK_EXIT(&softa->ipf_authlk); softa->ipf_auth_stats.fas_miss++; return (NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_new */ /* Returns: int - 1 == success, 0 = did not put packet on auth queue */ /* Parameters: m(I) - pointer to mb_t with packet in it */ /* fin(I) - pointer to packet information */ /* */ /* Check if we have room in the auth array to hold details for another */ /* packet. If we do, store it and wake up any user programs which are */ /* waiting to hear about these events. */ /* ------------------------------------------------------------------------ */ int ipf_auth_new(mb_t *m, fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_auth_softc_t *softa = softc->ipf_auth_soft; #if defined(_KERNEL) && SOLARIS qpktinfo_t *qpi = fin->fin_qpi; #endif frauth_t *fra; #if !defined(sparc) && !defined(m68k) ip_t *ip; #endif int i; if (softa->ipf_auth_lock) return (0); WRITE_ENTER(&softa->ipf_authlk); if (((softa->ipf_auth_end + 1) % softa->ipf_auth_size) == softa->ipf_auth_start) { softa->ipf_auth_stats.fas_nospace++; RWLOCK_EXIT(&softa->ipf_authlk); return (0); } softa->ipf_auth_stats.fas_added++; softa->ipf_auth_used++; i = softa->ipf_auth_end++; if (softa->ipf_auth_end == softa->ipf_auth_size) softa->ipf_auth_end = 0; fra = softa->ipf_auth + i; fra->fra_index = i; if (fin->fin_fr != NULL) fra->fra_pass = fin->fin_fr->fr_flags; else fra->fra_pass = 0; fra->fra_age = softa->ipf_auth_defaultage; bcopy((char *)fin, (char *)&fra->fra_info, sizeof(*fin)); fra->fra_flx = fra->fra_info.fin_flx & (FI_STATE|FI_NATED); fra->fra_info.fin_flx &= ~(FI_STATE|FI_NATED); #if !defined(sparc) && !defined(m68k) /* * No need to copyback here as we want to undo the changes, not keep * them. */ ip = fin->fin_ip; # if SOLARIS && defined(_KERNEL) if ((ip == (ip_t *)m->b_rptr) && (fin->fin_v == 4)) # endif { register u_short bo; bo = ip->ip_len; ip->ip_len = htons(bo); bo = ip->ip_off; ip->ip_off = htons(bo); } #endif #if SOLARIS && defined(_KERNEL) COPYIFNAME(fin->fin_v, fin->fin_ifp, fra->fra_info.fin_ifname); m->b_rptr -= qpi->qpi_off; fra->fra_q = qpi->qpi_q; /* The queue can disappear! */ fra->fra_m = *fin->fin_mp; fra->fra_info.fin_mp = &fra->fra_m; softa->ipf_auth_pkts[i] = *(mblk_t **)fin->fin_mp; RWLOCK_EXIT(&softa->ipf_authlk); cv_signal(&softa->ipf_auth_wait); pollwakeup(&softc->ipf_poll_head[IPL_LOGAUTH], POLLIN|POLLRDNORM); #else softa->ipf_auth_pkts[i] = m; RWLOCK_EXIT(&softa->ipf_authlk); WAKEUP(&softa->ipf_auth_next, 0); #endif return (1); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_ioctl */ /* Returns: int - 0 == success, else error */ /* Parameters: data(IO) - pointer to ioctl data */ /* cmd(I) - ioctl command */ /* mode(I) - mode flags associated with open descriptor */ /* uid(I) - uid associatd with application making the call */ /* ctx(I) - pointer for context */ /* */ /* This function handles all of the ioctls recognised by the auth component */ /* in IPFilter - ie ioctls called on an open fd for /dev/ipf_auth */ /* ------------------------------------------------------------------------ */ int ipf_auth_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd, int mode, int uid, void *ctx) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; int error = 0, i; SPL_INT(s); switch (cmd) { case SIOCGENITER : { ipftoken_t *token; ipfgeniter_t iter; ipfobj_t obj; error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); token = ipf_token_find(softc, IPFGENITER_AUTH, uid, ctx); if (token != NULL) error = ipf_auth_geniter(softc, token, &iter, &obj); else { WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); IPFERROR(10001); error = ESRCH; } SPL_X(s); break; } case SIOCADAFR : case SIOCRMAFR : if (!(mode & FWRITE)) { IPFERROR(10002); error = EPERM; } else error = frrequest(softc, IPL_LOGAUTH, cmd, data, softc->ipf_active, 1); break; case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(10003); error = EPERM; } else { error = ipf_lock(data, &softa->ipf_auth_lock); } break; case SIOCATHST: softa->ipf_auth_stats.fas_faelist = softa->ipf_auth_entries; error = ipf_outobj(softc, data, &softa->ipf_auth_stats, IPFOBJ_AUTHSTAT); break; case SIOCIPFFL: SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); i = ipf_auth_flush(softa); RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); error = BCOPYOUT(&i, data, sizeof(i)); if (error != 0) { IPFERROR(10004); error = EFAULT; } break; case SIOCAUTHW: error = ipf_auth_wait(softc, softa, data); break; case SIOCAUTHR: error = ipf_auth_reply(softc, softa, data); break; default : IPFERROR(10005); error = EINVAL; break; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_expire */ /* Returns: None */ /* Parameters: None */ /* */ /* Slowly expire held auth records. Timeouts are set in expectation of */ /* this being called twice per second. */ /* ------------------------------------------------------------------------ */ void ipf_auth_expire(ipf_main_softc_t *softc) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, **faep; frentry_t *fr, **frp; frauth_t *fra; mb_t *m; int i; SPL_INT(s); if (softa->ipf_auth_lock) return; SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); for (i = 0, fra = softa->ipf_auth; i < softa->ipf_auth_size; i++, fra++) { fra->fra_age--; if ((fra->fra_age == 0) && (softa->ipf_auth[i].fra_index != -1)) { if ((m = softa->ipf_auth_pkts[i]) != NULL) { FREE_MB_T(m); softa->ipf_auth_pkts[i] = NULL; } else if (softa->ipf_auth[i].fra_index == -2) { softa->ipf_auth_replies--; } softa->ipf_auth[i].fra_index = -1; softa->ipf_auth_stats.fas_expire++; softa->ipf_auth_used--; } } /* * Expire pre-auth rules */ for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) { fae->fae_age--; if (fae->fae_age == 0) { ipf_auth_deref(&fae); softa->ipf_auth_stats.fas_expire++; } else faep = &fae->fae_next; } if (softa->ipf_auth_entries != NULL) softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr; else softa->ipf_auth_ip = NULL; for (frp = &softa->ipf_auth_rules; ((fr = *frp) != NULL); ) { if (fr->fr_ref == 1) { *frp = fr->fr_next; MUTEX_DESTROY(&fr->fr_lock); KFREE(fr); } else frp = &fr->fr_next; } RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_precmd */ /* Returns: int - 0 == success, else error */ /* Parameters: cmd(I) - ioctl command for rule */ /* fr(I) - pointer to ipf rule */ /* fptr(I) - pointer to caller's 'fr' */ /* */ /* ------------------------------------------------------------------------ */ int ipf_auth_precmd(ipf_main_softc_t *softc, ioctlcmd_t cmd, frentry_t *fr, frentry_t **frptr) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, **faep; int error = 0; SPL_INT(s); if ((cmd != SIOCADAFR) && (cmd != SIOCRMAFR)) { IPFERROR(10006); return (EIO); } for (faep = &softa->ipf_auth_entries; ((fae = *faep) != NULL); ) { if (&fae->fae_fr == fr) break; else faep = &fae->fae_next; } if (cmd == (ioctlcmd_t)SIOCRMAFR) { if (fr == NULL || frptr == NULL) { IPFERROR(10007); error = EINVAL; } else if (fae == NULL) { IPFERROR(10008); error = ESRCH; } else { SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); *faep = fae->fae_next; if (softa->ipf_auth_ip == &fae->fae_fr) softa->ipf_auth_ip = softa->ipf_auth_entries ? &softa->ipf_auth_entries->fae_fr : NULL; RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); KFREE(fae); } } else if (fr != NULL && frptr != NULL) { KMALLOC(fae, frauthent_t *); if (fae != NULL) { bcopy((char *)fr, (char *)&fae->fae_fr, sizeof(*fr)); SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); fae->fae_age = softa->ipf_auth_defaultage; fae->fae_fr.fr_hits = 0; fae->fae_fr.fr_next = *frptr; fae->fae_ref = 1; *frptr = &fae->fae_fr; fae->fae_next = *faep; *faep = fae; softa->ipf_auth_ip = &softa->ipf_auth_entries->fae_fr; RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); } else { IPFERROR(10009); error = ENOMEM; } } else { IPFERROR(10010); error = EINVAL; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_flush */ /* Returns: int - number of auth entries flushed */ /* Parameters: None */ /* Locks: WRITE(ipf_authlk) */ /* */ /* This function flushs the ipf_auth_pkts array of any packet data with */ /* references still there. */ /* It is expected that the caller has already acquired the correct locks or */ /* set the priority level correctly for this to block out other code paths */ /* into these data structures. */ /* ------------------------------------------------------------------------ */ static int ipf_auth_flush(void *arg) { ipf_auth_softc_t *softa = arg; int i, num_flushed; mb_t *m; if (softa->ipf_auth_lock) return (-1); num_flushed = 0; for (i = 0 ; i < softa->ipf_auth_size; i++) { if (softa->ipf_auth[i].fra_index != -1) { m = softa->ipf_auth_pkts[i]; if (m != NULL) { FREE_MB_T(m); softa->ipf_auth_pkts[i] = NULL; } softa->ipf_auth[i].fra_index = -1; /* perhaps add & use a flush counter inst.*/ softa->ipf_auth_stats.fas_expire++; num_flushed++; } } softa->ipf_auth_start = 0; softa->ipf_auth_end = 0; softa->ipf_auth_next = 0; softa->ipf_auth_used = 0; softa->ipf_auth_replies = 0; return (num_flushed); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_waiting */ /* Returns: int - number of packets in the auth queue */ /* Parameters: None */ /* */ /* Simple truth check to see if there are any packets waiting in the auth */ /* queue. */ /* ------------------------------------------------------------------------ */ int ipf_auth_waiting(ipf_main_softc_t *softc) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; return (softa->ipf_auth_used != 0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_geniter */ /* Returns: int - 0 == success, else error */ /* Parameters: token(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter structure */ /* objp(I) - pointer to ipf object destription */ /* */ /* Iterate through the list of entries in the auth queue list. */ /* objp is used here to get the location of where to do the copy out to. */ /* Stomping over various fields with new information will not harm anything */ /* ------------------------------------------------------------------------ */ static int ipf_auth_geniter(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp, ipfobj_t *objp) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; frauthent_t *fae, *next, zero; int error; if (itp->igi_data == NULL) { IPFERROR(10011); return (EFAULT); } if (itp->igi_type != IPFGENITER_AUTH) { IPFERROR(10012); return (EINVAL); } objp->ipfo_type = IPFOBJ_FRAUTH; objp->ipfo_ptr = itp->igi_data; objp->ipfo_size = sizeof(frauth_t); READ_ENTER(&softa->ipf_authlk); fae = token->ipt_data; if (fae == NULL) { next = softa->ipf_auth_entries; } else { next = fae->fae_next; } /* * If we found an auth entry to use, bump its reference count * so that it can be used for is_next when we come back. */ if (next != NULL) { ATOMIC_INC(next->fae_ref); token->ipt_data = next; } else { bzero(&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; } RWLOCK_EXIT(&softa->ipf_authlk); error = ipf_outobjk(softc, objp, next); if (fae != NULL) ipf_auth_deref_unlocked(softa, &fae); if (next->fae_next == NULL) ipf_token_mark_complete(token); return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_deref_unlocked */ /* Returns: None */ /* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */ /* */ /* Wrapper for ipf_auth_deref for when a write lock on ipf_authlk is not */ /* held. */ /* ------------------------------------------------------------------------ */ static void ipf_auth_deref_unlocked(ipf_auth_softc_t *softa, frauthent_t **faep) { WRITE_ENTER(&softa->ipf_authlk); ipf_auth_deref(faep); RWLOCK_EXIT(&softa->ipf_authlk); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_deref */ /* Returns: None */ /* Parameters: faep(IO) - pointer to caller's frauthent_t pointer */ /* Locks: WRITE(ipf_authlk) */ /* */ /* This function unconditionally sets the pointer in the caller to NULL, */ /* to make it clear that it should no longer use that pointer, and drops */ /* the reference count on the structure by 1. If it reaches 0, free it up. */ /* ------------------------------------------------------------------------ */ static void ipf_auth_deref(frauthent_t **faep) { frauthent_t *fae; fae = *faep; *faep = NULL; fae->fae_ref--; if (fae->fae_ref == 0) { KFREE(fae); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_wait_pkt */ /* Returns: int - 0 == success, else error */ /* Parameters: data(I) - pointer to data from ioctl call */ /* */ /* This function is called when an application is waiting for a packet to */ /* match an "auth" rule by issuing an SIOCAUTHW ioctl. If there is already */ /* a packet waiting on the queue then we will return that _one_ immediately.*/ /* If there are no packets present in the queue (ipf_auth_pkts) then we go */ /* to sleep. */ /* ------------------------------------------------------------------------ */ static int ipf_auth_wait(ipf_main_softc_t *softc, ipf_auth_softc_t *softa, char *data) { frauth_t auth, *au = &auth; int error, len, i; mb_t *m; char *t; SPL_INT(s); ipf_auth_ioctlloop: error = ipf_inobj(softc, data, NULL, au, IPFOBJ_FRAUTH); if (error != 0) return (error); /* * XXX Locks are held below over calls to copyout...a better * solution needs to be found so this isn't necessary. The situation * we are trying to guard against here is an error in the copyout * steps should not cause the packet to "disappear" from the queue. */ SPL_NET(s); READ_ENTER(&softa->ipf_authlk); /* * If ipf_auth_next is not equal to ipf_auth_end it will be because * there is a packet waiting to be delt with in the ipf_auth_pkts * array. We copy as much of that out to user space as requested. */ if (softa->ipf_auth_used > 0) { while (softa->ipf_auth_pkts[softa->ipf_auth_next] == NULL) { softa->ipf_auth_next++; if (softa->ipf_auth_next == softa->ipf_auth_size) softa->ipf_auth_next = 0; } error = ipf_outobj(softc, data, &softa->ipf_auth[softa->ipf_auth_next], IPFOBJ_FRAUTH); if (error != 0) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); return (error); } if (auth.fra_len != 0 && auth.fra_buf != NULL) { /* * Copy packet contents out to user space if * requested. Bail on an error. */ m = softa->ipf_auth_pkts[softa->ipf_auth_next]; len = MSGDSIZE(m); if (len > auth.fra_len) len = auth.fra_len; auth.fra_len = len; for (t = auth.fra_buf; m && (len > 0); ) { i = MIN(M_LEN(m), len); error = copyoutptr(softc, MTOD(m, char *), &t, i); len -= i; t += i; if (error != 0) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); return (error); } m = m->m_next; } } RWLOCK_EXIT(&softa->ipf_authlk); SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); softa->ipf_auth_next++; if (softa->ipf_auth_next == softa->ipf_auth_size) softa->ipf_auth_next = 0; RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); return (0); } RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); MUTEX_ENTER(&softa->ipf_auth_mx); #ifdef _KERNEL # if SOLARIS error = 0; if (!cv_wait_sig(&softa->ipf_auth_wait, &softa->ipf_auth_mx.ipf_lk)) { IPFERROR(10014); error = EINTR; } # else /* SOLARIS */ error = SLEEP(&softa->ipf_auth_next, "ipf_auth_next"); # endif /* SOLARIS */ #endif MUTEX_EXIT(&softa->ipf_auth_mx); if (error == 0) goto ipf_auth_ioctlloop; return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_auth_reply */ /* Returns: int - 0 == success, else error */ /* Parameters: data(I) - pointer to data from ioctl call */ /* */ /* This function is called by an application when it wants to return a */ /* decision on a packet using the SIOCAUTHR ioctl. This is after it has */ /* received information using an SIOCAUTHW. The decision returned in the */ /* form of flags, the same as those used in each rule. */ /* ------------------------------------------------------------------------ */ static int ipf_auth_reply(ipf_main_softc_t *softc, ipf_auth_softc_t *softa, char *data) { frauth_t auth, *au = &auth, *fra; fr_info_t fin; int error, i; mb_t *m; SPL_INT(s); error = ipf_inobj(softc, data, NULL, &auth, IPFOBJ_FRAUTH); if (error != 0) return (error); SPL_NET(s); WRITE_ENTER(&softa->ipf_authlk); i = au->fra_index; fra = softa->ipf_auth + i; error = 0; /* * Check the validity of the information being returned with two simple * checks. First, the auth index value should be within the size of * the array and second the packet id being returned should also match. */ if ((i < 0) || (i >= softa->ipf_auth_size)) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); IPFERROR(10015); return (ESRCH); } if (fra->fra_info.fin_id != au->fra_info.fin_id) { RWLOCK_EXIT(&softa->ipf_authlk); SPL_X(s); IPFERROR(10019); return (ESRCH); } m = softa->ipf_auth_pkts[i]; fra->fra_index = -2; fra->fra_pass = au->fra_pass; softa->ipf_auth_pkts[i] = NULL; softa->ipf_auth_replies++; bcopy(&fra->fra_info, &fin, sizeof(fin)); RWLOCK_EXIT(&softa->ipf_authlk); /* * Re-insert the packet back into the packet stream flowing through * the kernel in a manner that will mean IPFilter sees the packet * again. This is not the same as is done with fastroute, * deliberately, as we want to resume the normal packet processing * path for it. */ #ifdef _KERNEL if ((m != NULL) && (au->fra_info.fin_out != 0)) { error = ipf_inject(&fin, m); if (error != 0) { IPFERROR(10016); error = ENOBUFS; softa->ipf_auth_stats.fas_sendfail++; } else { softa->ipf_auth_stats.fas_sendok++; } } else if (m) { error = ipf_inject(&fin, m); if (error != 0) { IPFERROR(10017); error = ENOBUFS; softa->ipf_auth_stats.fas_quefail++; } else { softa->ipf_auth_stats.fas_queok++; } } else { IPFERROR(10018); error = EINVAL; } /* * If we experience an error which will result in the packet * not being processed, make sure we advance to the next one. */ if (error == ENOBUFS) { WRITE_ENTER(&softa->ipf_authlk); softa->ipf_auth_used--; fra->fra_index = -1; fra->fra_pass = 0; if (i == softa->ipf_auth_start) { while (fra->fra_index == -1) { i++; if (i == softa->ipf_auth_size) i = 0; softa->ipf_auth_start = i; if (i == softa->ipf_auth_end) break; } if (softa->ipf_auth_start == softa->ipf_auth_end) { softa->ipf_auth_next = 0; softa->ipf_auth_start = 0; softa->ipf_auth_end = 0; } } RWLOCK_EXIT(&softa->ipf_authlk); } #endif /* _KERNEL */ SPL_X(s); return (0); } u_32_t ipf_auth_pre_scanlist(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; if (softa->ipf_auth_ip != NULL) return (ipf_scanlist(fin, softc->ipf_pass)); return (pass); } frentry_t ** ipf_auth_rulehead(ipf_main_softc_t *softc) { ipf_auth_softc_t *softa = softc->ipf_auth_soft; return (&softa->ipf_auth_ip); } diff --git a/sys/netpfil/ipfilter/netinet/ip_frag.c b/sys/netpfil/ipfilter/netinet/ip_frag.c index 111b8cc1dc29..ca71714e3a56 100644 --- a/sys/netpfil/ipfilter/netinet/ip_frag.c +++ b/sys/netpfil/ipfilter/netinet/ip_frag.c @@ -1,1297 +1,1296 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if !defined(_KERNEL) # include # include # include # define _KERNEL # include # undef _KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include # include #else # include #endif # include #include #if defined(_KERNEL) # include # if !defined(__SVR4) # include # endif #endif #if !defined(__SVR4) # if defined(_KERNEL) # include # endif #else # include # ifdef _KERNEL # include # endif # include # include #endif #include #ifdef sun # include #endif #include #include #include # include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_auth.h" #include "netinet/ip_lookup.h" #include "netinet/ip_proxy.h" #include "netinet/ip_sync.h" /* END OF INCLUDES */ #if !defined(lint) static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: ip_frag.c,v 2.77.2.12 2007/09/20 12:51:51 darrenr Exp $"; */ #endif #ifdef USE_MUTEXES static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, u_32_t, ipfr_t **, ipfrwlock_t *); static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **, ipfrwlock_t *); static void ipf_frag_deref(void *, ipfr_t **, ipfrwlock_t *); static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfr_t **, ipfrwlock_t *); #else static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, u_32_t, ipfr_t **); static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **); static void ipf_frag_deref(void *, ipfr_t **); static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfr_t **); #endif static void ipf_frag_delete(ipf_main_softc_t *, ipfr_t *, ipfr_t ***); static void ipf_frag_free(ipf_frag_softc_t *, ipfr_t *); static frentry_t ipfr_block; static ipftuneable_t ipf_frag_tuneables[] = { { { (void *)offsetof(ipf_frag_softc_t, ipfr_size) }, "frag_size", 1, 0x7fffffff, stsizeof(ipf_frag_softc_t, ipfr_size), IPFT_WRDISABLED, NULL, NULL }, { { (void *)offsetof(ipf_frag_softc_t, ipfr_ttl) }, "frag_ttl", 1, 0x7fffffff, stsizeof(ipf_frag_softc_t, ipfr_ttl), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; #define FBUMP(x) softf->ipfr_stats.x++ #define FBUMPD(x) do { softf->ipfr_stats.x++; DT(x); } while (0) /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_main_load */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: Nil */ /* */ /* Initialise the filter rule associted with blocked packets - everyone can */ /* use it. */ /* ------------------------------------------------------------------------ */ int ipf_frag_main_load(void) { bzero((char *)&ipfr_block, sizeof(ipfr_block)); ipfr_block.fr_flags = FR_BLOCK|FR_QUICK; ipfr_block.fr_ref = 1; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_main_unload */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: Nil */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_frag_main_unload(void) { return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_create */ /* Returns: void * - NULL = failure, else pointer to local context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Allocate a new soft context structure to track fragment related info. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ void * ipf_frag_soft_create(ipf_main_softc_t *softc) { ipf_frag_softc_t *softf; KMALLOC(softf, ipf_frag_softc_t *); if (softf == NULL) return (NULL); bzero((char *)softf, sizeof(*softf)); RWLOCK_INIT(&softf->ipfr_ipidfrag, "frag ipid lock"); RWLOCK_INIT(&softf->ipfr_frag, "ipf fragment rwlock"); RWLOCK_INIT(&softf->ipfr_natfrag, "ipf NAT fragment rwlock"); softf->ipf_frag_tune = ipf_tune_array_copy(softf, sizeof(ipf_frag_tuneables), ipf_frag_tuneables); if (softf->ipf_frag_tune == NULL) { ipf_frag_soft_destroy(softc, softf); return (NULL); } if (ipf_tune_array_link(softc, softf->ipf_frag_tune) == -1) { ipf_frag_soft_destroy(softc, softf); return (NULL); } softf->ipfr_size = IPFT_SIZE; softf->ipfr_ttl = IPF_TTLVAL(60); softf->ipfr_lock = 1; softf->ipfr_tail = &softf->ipfr_list; softf->ipfr_nattail = &softf->ipfr_natlist; softf->ipfr_ipidtail = &softf->ipfr_ipidlist; return (softf); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the hash tables for the fragment cache lookups. */ /* ------------------------------------------------------------------------ */ void ipf_frag_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_frag_softc_t *softf = arg; RW_DESTROY(&softf->ipfr_ipidfrag); RW_DESTROY(&softf->ipfr_frag); RW_DESTROY(&softf->ipfr_natfrag); if (softf->ipf_frag_tune != NULL) { ipf_tune_array_unlink(softc, softf->ipf_frag_tune); KFREES(softf->ipf_frag_tune, sizeof(ipf_frag_tuneables)); softf->ipf_frag_tune = NULL; } KFREE(softf); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_init */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Initialise the hash tables for the fragment cache lookups. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ int ipf_frag_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_frag_softc_t *softf = arg; KMALLOCS(softf->ipfr_heads, ipfr_t **, softf->ipfr_size * sizeof(ipfr_t *)); if (softf->ipfr_heads == NULL) return (-1); bzero((char *)softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); KMALLOCS(softf->ipfr_nattab, ipfr_t **, softf->ipfr_size * sizeof(ipfr_t *)); if (softf->ipfr_nattab == NULL) return (-2); bzero((char *)softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); KMALLOCS(softf->ipfr_ipidtab, ipfr_t **, softf->ipfr_size * sizeof(ipfr_t *)); if (softf->ipfr_ipidtab == NULL) return (-3); bzero((char *)softf->ipfr_ipidtab, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_lock = 0; softf->ipfr_inited = 1; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_soft_fini */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to local context to use */ /* */ /* Free all memory allocated whilst running and from initialisation. */ /* ------------------------------------------------------------------------ */ int ipf_frag_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_frag_softc_t *softf = arg; softf->ipfr_lock = 1; if (softf->ipfr_inited == 1) { ipf_frag_clear(softc); softf->ipfr_inited = 0; } if (softf->ipfr_heads != NULL) KFREES(softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_heads = NULL; if (softf->ipfr_nattab != NULL) KFREES(softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_nattab = NULL; if (softf->ipfr_ipidtab != NULL) KFREES(softf->ipfr_ipidtab, softf->ipfr_size * sizeof(ipfr_t *)); softf->ipfr_ipidtab = NULL; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_set_lock */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to local context to use */ /* tmp(I) - new value for lock */ /* */ /* Stub function that allows for external manipulation of ipfr_lock */ /* ------------------------------------------------------------------------ */ void ipf_frag_setlock(void *arg, int tmp) { ipf_frag_softc_t *softf = arg; softf->ipfr_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_stats */ /* Returns: ipfrstat_t* - pointer to struct with current frag stats */ /* Parameters: arg(I) - pointer to local context to use */ /* */ /* Updates ipfr_stats with current information and returns a pointer to it */ /* ------------------------------------------------------------------------ */ ipfrstat_t * ipf_frag_stats(void *arg) { ipf_frag_softc_t *softf = arg; softf->ipfr_stats.ifs_table = softf->ipfr_heads; softf->ipfr_stats.ifs_nattab = softf->ipfr_nattab; return (&softf->ipfr_stats); } /* ------------------------------------------------------------------------ */ /* Function: ipfr_frag_new */ /* Returns: ipfr_t * - pointer to fragment cache state info or NULL */ /* Parameters: fin(I) - pointer to packet information */ /* table(I) - pointer to frag table to add to */ /* lock(I) - pointer to lock to get a write hold of */ /* */ /* Add a new entry to the fragment cache, registering it as having come */ /* through this box, with the result of the filter operation. */ /* */ /* If this function succeeds, it returns with a write lock held on "lock". */ /* If it fails, no lock is held on return. */ /* ------------------------------------------------------------------------ */ static ipfr_t * ipfr_frag_new(ipf_main_softc_t *softc, ipf_frag_softc_t *softf, fr_info_t *fin, u_32_t pass, ipfr_t *table[] #ifdef USE_MUTEXES , ipfrwlock_t *lock #endif ) { ipfr_t *fra, frag, *fran; u_int idx, off; frentry_t *fr; if (softf->ipfr_stats.ifs_inuse >= softf->ipfr_size) { FBUMPD(ifs_maximum); return (NULL); } if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) { FBUMPD(ifs_newbad); return (NULL); } if (pass & FR_FRSTRICT) { if (fin->fin_off != 0) { FBUMPD(ifs_newrestrictnot0); return (NULL); } } memset(&frag, 0, sizeof(frag)); frag.ipfr_v = fin->fin_v; idx = fin->fin_v; frag.ipfr_p = fin->fin_p; idx += fin->fin_p; frag.ipfr_id = fin->fin_id; idx += fin->fin_id; frag.ipfr_source = fin->fin_fi.fi_src; idx += frag.ipfr_src.s_addr; frag.ipfr_dest = fin->fin_fi.fi_dst; idx += frag.ipfr_dst.s_addr; frag.ipfr_ifp = fin->fin_ifp; idx *= 127; idx %= softf->ipfr_size; frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; frag.ipfr_auth = fin->fin_fi.fi_auth; off = fin->fin_off >> 3; if (off == 0) { char *ptr; int end; #ifdef USE_INET6 if (fin->fin_v == 6) { ptr = (char *)fin->fin_fraghdr + sizeof(struct ip6_frag); } else #endif { ptr = fin->fin_dp; } end = fin->fin_plen - (ptr - (char *)fin->fin_ip); frag.ipfr_firstend = end >> 3; } else { frag.ipfr_firstend = 0; } /* * allocate some memory, if possible, if not, just record that we * failed to do so. */ KMALLOC(fran, ipfr_t *); if (fran == NULL) { FBUMPD(ifs_nomem); return (NULL); } memset(fran, 0, sizeof(*fran)); WRITE_ENTER(lock); /* * first, make sure it isn't already there... */ for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext) if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ)) { RWLOCK_EXIT(lock); FBUMPD(ifs_exists); KFREE(fran); return (NULL); } fra = fran; fran = NULL; fr = fin->fin_fr; fra->ipfr_rule = fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } /* * Insert the fragment into the fragment table, copy the struct used * in the search using bcopy rather than reassign each field. * Set the ttl to the default. */ if ((fra->ipfr_hnext = table[idx]) != NULL) table[idx]->ipfr_hprev = &fra->ipfr_hnext; fra->ipfr_hprev = table + idx; fra->ipfr_data = NULL; table[idx] = fra; bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); fra->ipfr_v = fin->fin_v; fra->ipfr_p = fin->fin_p; fra->ipfr_ttl = softc->ipf_ticks + softf->ipfr_ttl; fra->ipfr_firstend = frag.ipfr_firstend; /* * Compute the offset of the expected start of the next packet. */ if (off == 0) fra->ipfr_seen0 = 1; fra->ipfr_off = off + (fin->fin_dlen >> 3); fra->ipfr_pass = pass; fra->ipfr_ref = 1; fra->ipfr_pkts = 1; fra->ipfr_bytes = fin->fin_plen; FBUMP(ifs_inuse); FBUMP(ifs_new); return (fra); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_new */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Add a new entry to the fragment cache table based on the current packet */ /* ------------------------------------------------------------------------ */ int ipf_frag_new(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; if (softf->ipfr_lock != 0) return (-1); #ifdef USE_MUTEXES fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads, &softc->ipf_frag); #else fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads); #endif if (fra != NULL) { *softf->ipfr_tail = fra; fra->ipfr_prev = softf->ipfr_tail; softf->ipfr_tail = &fra->ipfr_next; fra->ipfr_next = NULL; RWLOCK_EXIT(&softc->ipf_frag); } return (fra ? 0 : -1); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_natnew */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* */ /* Create a new NAT fragment cache entry based on the current packet and */ /* the NAT structure for this "session". */ /* ------------------------------------------------------------------------ */ int ipf_frag_natnew(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass, nat_t *nat) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; if (softf->ipfr_lock != 0) return (0); #ifdef USE_MUTEXES fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab, &softf->ipfr_natfrag); #else fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab); #endif if (fra != NULL) { fra->ipfr_data = nat; nat->nat_data = fra; *softf->ipfr_nattail = fra; fra->ipfr_prev = softf->ipfr_nattail; softf->ipfr_nattail = &fra->ipfr_next; fra->ipfr_next = NULL; RWLOCK_EXIT(&softf->ipfr_natfrag); return (0); } return (-1); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_ipidnew */ /* Returns: int - 0 == success, -1 == error */ /* Parameters: fin(I) - pointer to packet information */ /* ipid(I) - new IP ID for this fragmented packet */ /* */ /* Create a new fragment cache entry for this packet and store, as a data */ /* pointer, the new IP ID value. */ /* ------------------------------------------------------------------------ */ int ipf_frag_ipidnew(fr_info_t *fin, u_32_t ipid) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; if (softf->ipfr_lock) return (0); #ifdef USE_MUTEXES fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); #else fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab); #endif if (fra != NULL) { fra->ipfr_data = (void *)(intptr_t)ipid; *softf->ipfr_ipidtail = fra; fra->ipfr_prev = softf->ipfr_ipidtail; softf->ipfr_ipidtail = &fra->ipfr_next; fra->ipfr_next = NULL; RWLOCK_EXIT(&softf->ipfr_ipidfrag); } return (fra ? 0 : -1); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_lookup */ /* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */ /* matching entry in the frag table, else NULL */ /* Parameters: fin(I) - pointer to packet information */ /* table(I) - pointer to fragment cache table to search */ /* */ /* Check the fragment cache to see if there is already a record of this */ /* packet with its filter result known. */ /* */ /* If this function succeeds, it returns with a write lock held on "lock". */ /* If it fails, no lock is held on return. */ /* ------------------------------------------------------------------------ */ static ipfr_t * ipf_frag_lookup(ipf_main_softc_t *softc, ipf_frag_softc_t *softf, fr_info_t *fin, ipfr_t *table[] #ifdef USE_MUTEXES , ipfrwlock_t *lock #endif ) { ipfr_t *f, frag; u_int idx; /* * We don't want to let short packets match because they could be * compromising the security of other rules that want to match on * layer 4 fields (and can't because they have been fragmented off.) * Why do this check here? The counter acts as an indicator of this * kind of attack, whereas if it was elsewhere, it wouldn't know if * other matching packets had been seen. */ if (fin->fin_flx & FI_SHORT) { FBUMPD(ifs_short); return (NULL); } if ((fin->fin_flx & FI_BAD) != 0) { FBUMPD(ifs_bad); return (NULL); } /* * For fragments, we record protocol, packet id, TOS and both IP#'s * (these should all be the same for all fragments of a packet). * * build up a hash value to index the table with. */ memset(&frag, 0, sizeof(frag)); frag.ipfr_v = fin->fin_v; idx = fin->fin_v; frag.ipfr_p = fin->fin_p; idx += fin->fin_p; frag.ipfr_id = fin->fin_id; idx += fin->fin_id; frag.ipfr_source = fin->fin_fi.fi_src; idx += frag.ipfr_src.s_addr; frag.ipfr_dest = fin->fin_fi.fi_dst; idx += frag.ipfr_dst.s_addr; frag.ipfr_ifp = fin->fin_ifp; idx *= 127; idx %= softf->ipfr_size; frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; frag.ipfr_auth = fin->fin_fi.fi_auth; READ_ENTER(lock); /* * check the table, careful to only compare the right amount of data */ for (f = table[idx]; f; f = f->ipfr_hnext) { if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, IPFR_CMPSZ)) { u_short off; /* * XXX - We really need to be guarding against the * retransmission of (src,dst,id,offset-range) here * because a fragmented packet is never resent with * the same IP ID# (or shouldn't). */ off = fin->fin_off >> 3; if (f->ipfr_seen0) { if (off == 0) { FBUMPD(ifs_retrans0); continue; } /* * Case 3. See comment for frpr_fragment6. */ if ((f->ipfr_firstend != 0) && (off < f->ipfr_firstend)) { FBUMP(ifs_overlap); DT2(ifs_overlap, u_short, off, ipfr_t *, f); DT3(ipf_fi_bad_ifs_overlap, fr_info_t *, fin, u_short, off, ipfr_t *, f); fin->fin_flx |= FI_BAD; break; } } else if (off == 0) f->ipfr_seen0 = 1; if (f != table[idx] && MUTEX_TRY_UPGRADE(lock)) { ipfr_t **fp; /* * Move fragment info. to the top of the list * to speed up searches. First, delink... */ fp = f->ipfr_hprev; (*fp) = f->ipfr_hnext; if (f->ipfr_hnext != NULL) f->ipfr_hnext->ipfr_hprev = fp; /* * Then put back at the top of the chain. */ f->ipfr_hnext = table[idx]; table[idx]->ipfr_hprev = &f->ipfr_hnext; f->ipfr_hprev = table + idx; table[idx] = f; MUTEX_DOWNGRADE(lock); } /* * If we've follwed the fragments, and this is the * last (in order), shrink expiration time. */ if (off == f->ipfr_off) { f->ipfr_off = (fin->fin_dlen >> 3) + off; /* * Well, we could shrink the expiration time * but only if every fragment has been seen * in order upto this, the last. ipfr_badorder * is used here to count those out of order * and if it equals 0 when we get to the last * fragment then we can assume all of the * fragments have been seen and in order. */ #if 0 /* * Doing this properly requires moving it to * the head of the list which is infesible. */ if ((more == 0) && (f->ipfr_badorder == 0)) f->ipfr_ttl = softc->ipf_ticks + 1; #endif } else { f->ipfr_badorder++; FBUMPD(ifs_unordered); if (f->ipfr_pass & FR_FRSTRICT) { FBUMPD(ifs_strict); continue; } } f->ipfr_pkts++; f->ipfr_bytes += fin->fin_plen; FBUMP(ifs_hits); return (f); } } RWLOCK_EXIT(lock); FBUMP(ifs_miss); return (NULL); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_natknown */ /* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ /* match found, else NULL */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Functional interface for NAT lookups of the NAT fragment cache */ /* ------------------------------------------------------------------------ */ nat_t * ipf_frag_natknown(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; nat_t *nat; ipfr_t *ipf; if ((softf->ipfr_lock) || !softf->ipfr_natlist) return (NULL); #ifdef USE_MUTEXES ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab, &softf->ipfr_natfrag); #else ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab); #endif if (ipf != NULL) { nat = ipf->ipfr_data; /* * This is the last fragment for this packet. */ if ((ipf->ipfr_ttl == softc->ipf_ticks + 1) && (nat != NULL)) { nat->nat_data = NULL; ipf->ipfr_data = NULL; } RWLOCK_EXIT(&softf->ipfr_natfrag); } else nat = NULL; return (nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_ipidknown */ /* Returns: u_32_t - IPv4 ID for this packet if match found, else */ /* return 0xfffffff to indicate no match. */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Functional interface for IP ID lookups of the IP ID fragment cache */ /* ------------------------------------------------------------------------ */ u_32_t ipf_frag_ipidknown(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *ipf; u_32_t id; if (softf->ipfr_lock || !softf->ipfr_ipidlist) return (0xffffffff); #ifdef USE_MUTEXES ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); #else ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab); #endif if (ipf != NULL) { id = (u_32_t)(intptr_t)ipf->ipfr_data; RWLOCK_EXIT(&softf->ipfr_ipidfrag); } else id = 0xffffffff; return (id); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_known */ /* Returns: frentry_t* - pointer to filter rule if a match is found in */ /* the frag cache table, else NULL. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(O) - pointer to where to store rule flags resturned */ /* */ /* Functional interface for normal lookups of the fragment cache. If a */ /* match is found, return the rule pointer and flags from the rule, except */ /* that if FR_LOGFIRST is set, reset FR_LOG. */ /* ------------------------------------------------------------------------ */ frentry_t * ipf_frag_known(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_frag_softc_t *softf = softc->ipf_frag_soft; frentry_t *fr = NULL; ipfr_t *fra; u_32_t pass; if ((softf->ipfr_lock) || (softf->ipfr_list == NULL)) return (NULL); #ifdef USE_MUTEXES fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads, &softc->ipf_frag); #else fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads); #endif if (fra != NULL) { if (fin->fin_flx & FI_BAD) { fr = &ipfr_block; fin->fin_reason = FRB_BADFRAG; DT2(ipf_frb_badfrag, fr_info_t *, fin, uint, fra); } else { fr = fra->ipfr_rule; } fin->fin_fr = fr; if (fr != NULL) { pass = fr->fr_flags; if ((pass & FR_KEEPSTATE) != 0) { fin->fin_flx |= FI_STATE; /* * Reset the keep state flag here so that we * don't try and add a new state entry because * of a match here. That leads to blocking of * the packet later because the add fails. */ pass &= ~FR_KEEPSTATE; } if ((pass & FR_LOGFIRST) != 0) pass &= ~(FR_LOGFIRST|FR_LOG); *passp = pass; } RWLOCK_EXIT(&softc->ipf_frag); } return (fr); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_natforget */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ptr(I) - pointer to data structure */ /* */ /* Search through all of the fragment cache entries for NAT and wherever a */ /* pointer is found to match ptr, reset it to NULL. */ /* ------------------------------------------------------------------------ */ void ipf_frag_natforget(ipf_main_softc_t *softc, void *ptr) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fr; WRITE_ENTER(&softf->ipfr_natfrag); for (fr = softf->ipfr_natlist; fr; fr = fr->ipfr_next) if (fr->ipfr_data == ptr) fr->ipfr_data = NULL; RWLOCK_EXIT(&softf->ipfr_natfrag); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_delete */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* fra(I) - pointer to fragment structure to delete */ /* tail(IO) - pointer to the pointer to the tail of the frag */ /* list */ /* */ /* Remove a fragment cache table entry from the table & list. Also free */ /* the filter rule it is associated with it if it is no longer used as a */ /* result of decreasing the reference count. */ /* ------------------------------------------------------------------------ */ static void ipf_frag_delete(ipf_main_softc_t *softc, ipfr_t *fra, ipfr_t ***tail) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; if (fra->ipfr_next) fra->ipfr_next->ipfr_prev = fra->ipfr_prev; *fra->ipfr_prev = fra->ipfr_next; if (*tail == &fra->ipfr_next) *tail = fra->ipfr_prev; if (fra->ipfr_hnext) fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev; *fra->ipfr_hprev = fra->ipfr_hnext; if (fra->ipfr_rule != NULL) { (void) ipf_derefrule(softc, &fra->ipfr_rule); } if (fra->ipfr_ref <= 0) ipf_frag_free(softf, fra); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_free */ /* Returns: Nil */ /* Parameters: softf(I) - pointer to fragment context information */ /* fra(I) - pointer to fragment structure to free */ /* */ /* Free up a fragment cache entry and bump relevent statistics. */ /* ------------------------------------------------------------------------ */ static void ipf_frag_free(ipf_frag_softc_t *softf, ipfr_t *fra) { KFREE(fra); FBUMP(ifs_expire); softf->ipfr_stats.ifs_inuse--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_clear */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Free memory in use by fragment state information kept. Do the normal */ /* fragment state stuff first and then the NAT-fragment table. */ /* ------------------------------------------------------------------------ */ void ipf_frag_clear(ipf_main_softc_t *softc) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t *fra; nat_t *nat; WRITE_ENTER(&softc->ipf_frag); while ((fra = softf->ipfr_list) != NULL) { fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_tail); } softf->ipfr_tail = &softf->ipfr_list; RWLOCK_EXIT(&softc->ipf_frag); WRITE_ENTER(&softc->ipf_nat); WRITE_ENTER(&softf->ipfr_natfrag); while ((fra = softf->ipfr_natlist) != NULL) { nat = fra->ipfr_data; if (nat != NULL) { if (nat->nat_data == fra) nat->nat_data = NULL; } fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_nattail); } softf->ipfr_nattail = &softf->ipfr_natlist; RWLOCK_EXIT(&softf->ipfr_natfrag); RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Expire entries in the fragment cache table that have been there too long */ /* ------------------------------------------------------------------------ */ void ipf_frag_expire(ipf_main_softc_t *softc) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipfr_t **fp, *fra; nat_t *nat; SPL_INT(s); if (softf->ipfr_lock) return; SPL_NET(s); WRITE_ENTER(&softc->ipf_frag); /* * Go through the entire table, looking for entries to expire, * which is indicated by the ttl being less than or equal to ipf_ticks. */ for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) { if (fra->ipfr_ttl > softc->ipf_ticks) break; fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_tail); } RWLOCK_EXIT(&softc->ipf_frag); WRITE_ENTER(&softf->ipfr_ipidfrag); for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) { if (fra->ipfr_ttl > softc->ipf_ticks) break; fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail); } RWLOCK_EXIT(&softf->ipfr_ipidfrag); /* * Same again for the NAT table, except that if the structure also * still points to a NAT structure, and the NAT structure points back * at the one to be free'd, NULL the reference from the NAT struct. * NOTE: We need to grab both mutex's early, and in this order so as * to prevent a deadlock if both try to expire at the same time. * The extra if() statement here is because it locks out all NAT * operations - no need to do that if there are no entries in this * list, right? */ if (softf->ipfr_natlist != NULL) { WRITE_ENTER(&softc->ipf_nat); WRITE_ENTER(&softf->ipfr_natfrag); for (fp = &softf->ipfr_natlist; ((fra = *fp) != NULL); ) { if (fra->ipfr_ttl > softc->ipf_ticks) break; nat = fra->ipfr_data; if (nat != NULL) { if (nat->nat_data == fra) nat->nat_data = NULL; } fra->ipfr_ref--; ipf_frag_delete(softc, fra, &softf->ipfr_nattail); } RWLOCK_EXIT(&softf->ipfr_natfrag); RWLOCK_EXIT(&softc->ipf_nat); } SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_pkt_next */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* */ /* This function is used to step through the fragment cache list used for */ /* filter rules. The hard work is done by the more generic ipf_frag_next. */ /* ------------------------------------------------------------------------ */ int ipf_frag_pkt_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; #ifdef USE_MUTEXES return (ipf_frag_next(softc, token, itp, &softf->ipfr_list, &softf->ipfr_frag)); #else return (ipf_frag_next(softc, token, itp, &softf->ipfr_list)); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_nat_next */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* */ /* This function is used to step through the fragment cache list used for */ /* NAT. The hard work is done by the more generic ipf_frag_next. */ /* ------------------------------------------------------------------------ */ int ipf_frag_nat_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) { ipf_frag_softc_t *softf = softc->ipf_frag_soft; #ifdef USE_MUTEXES return (ipf_frag_next(softc, token, itp, &softf->ipfr_natlist, &softf->ipfr_natfrag)); #else return (ipf_frag_next(softc, token, itp, &softf->ipfr_natlist)); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_next */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to token information for this caller */ /* itp(I) - pointer to generic iterator from caller */ /* top(I) - top of the fragment list */ /* lock(I) - fragment cache lock */ /* */ /* This function is used to interate through the list of entries in the */ /* fragment cache. It increases the reference count on the one currently */ /* being returned so that the caller can come back and resume from it later.*/ /* */ /* This function is used for both the NAT fragment cache as well as the ipf */ /* fragment cache - hence the reason for passing in top and lock. */ /* ------------------------------------------------------------------------ */ static int ipf_frag_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp, ipfr_t **top #ifdef USE_MUTEXES , ipfrwlock_t *lock #endif ) { ipfr_t *frag, *next, zero; int error = 0; if (itp->igi_data == NULL) { IPFERROR(20001); return (EFAULT); } if (itp->igi_nitems != 1) { IPFERROR(20003); return (EFAULT); } frag = token->ipt_data; READ_ENTER(lock); if (frag == NULL) next = *top; else next = frag->ipfr_next; if (next != NULL) { ATOMIC_INC(next->ipfr_ref); token->ipt_data = next; } else { bzero(&zero, sizeof(zero)); next = &zero; token->ipt_data = NULL; } if (next->ipfr_next == NULL) ipf_token_mark_complete(token); RWLOCK_EXIT(lock); error = COPYOUT(next, itp->igi_data, sizeof(*next)); if (error != 0) IPFERROR(20002); if (frag != NULL) { #ifdef USE_MUTEXES ipf_frag_deref(softc, &frag, lock); #else ipf_frag_deref(softc, &frag); #endif } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_pkt_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to frag cache pointer */ /* */ /* This function is the external interface for dropping a reference to a */ /* fragment cache entry used by filter rules. */ /* ------------------------------------------------------------------------ */ void ipf_frag_pkt_deref(ipf_main_softc_t *softc, void *data) { ipfr_t **frp = data; #ifdef USE_MUTEXES ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_frag); #else ipf_frag_deref(softc->ipf_frag_soft, frp); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_nat_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to frag cache pointer */ /* */ /* This function is the external interface for dropping a reference to a */ /* fragment cache entry used by NAT table entries. */ /* ------------------------------------------------------------------------ */ void ipf_frag_nat_deref(ipf_main_softc_t *softc, void *data) { ipfr_t **frp = data; #ifdef USE_MUTEXES ipf_frag_softc_t *softf = softc->ipf_frag_soft; ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_natfrag); #else ipf_frag_deref(softc->ipf_frag_soft, frp); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_frag_deref */ /* Returns: Nil */ /* Parameters: frp(IO) - pointer to fragment structure to deference */ /* lock(I) - lock associated with the fragment */ /* */ /* This function dereferences a fragment structure (ipfr_t). The pointer */ /* passed in will always be reset back to NULL, even if the structure is */ /* not freed, to enforce the notion that the caller is no longer entitled */ /* to use the pointer it is dropping the reference to. */ /* ------------------------------------------------------------------------ */ static void ipf_frag_deref(void *arg, ipfr_t **frp #ifdef USE_MUTEXES , ipfrwlock_t *lock #endif ) { ipf_frag_softc_t *softf = arg; ipfr_t *fra; fra = *frp; *frp = NULL; WRITE_ENTER(lock); fra->ipfr_ref--; if (fra->ipfr_ref <= 0) ipf_frag_free(softf, fra); RWLOCK_EXIT(lock); } diff --git a/sys/netpfil/ipfilter/netinet/ip_nat.c b/sys/netpfil/ipfilter/netinet/ip_nat.c index 6163c9fa8191..136bc31ba0f4 100644 --- a/sys/netpfil/ipfilter/netinet/ip_nat.c +++ b/sys/netpfil/ipfilter/netinet/ip_nat.c @@ -1,8397 +1,8396 @@ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if defined(_KERNEL) && \ (defined(__NetBSD_Version) && (__NetBSD_Version >= 399002000)) # include #endif #if !defined(_KERNEL) # include # include # include # define KERNEL # ifdef _OpenBSD__ struct file; # endif # include # undef KERNEL #endif #if defined(_KERNEL) && defined(__FreeBSD__) # include # include #else # include #endif # include # include #include #if defined(_KERNEL) # include # if defined(__FreeBSD__) # include # endif # if !defined(__SVR4) # include # endif #endif #if defined(__SVR4) # include # include # ifdef KERNEL # include # endif # include # include #endif #if defined(__FreeBSD__) # include #endif #include #if defined(__FreeBSD__) # include #endif #ifdef sun # include #endif #include #include #include #ifdef RFC1825 # include # include extern struct ifnet vpnif; #endif # include #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ipl.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_lookup.h" #include "netinet/ip_dstlist.h" #include "netinet/ip_sync.h" #if defined(__FreeBSD__) # include #endif #ifdef HAS_SYS_MD5_H # include #else # include "md5.h" #endif /* END OF INCLUDES */ #undef SOCKADDR_IN #define SOCKADDR_IN struct sockaddr_in #if !defined(lint) static const char sccsid[] = "@(#)ip_nat.c 1.11 6/5/96 (C) 1995 Darren Reed"; -static const char rcsid[] = "@(#)$FreeBSD$"; /* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */ #endif #define NATFSUM(n,v,f) ((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \ (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3]) #define NBUMP(x) softn->(x)++ #define NBUMPD(x, y) do { \ softn->x.y++; \ DT(y); \ } while (0) #define NBUMPSIDE(y,x) softn->ipf_nat_stats.ns_side[y].x++ #define NBUMPSIDED(y,x) do { softn->ipf_nat_stats.ns_side[y].x++; \ DT(x); } while (0) #define NBUMPSIDEX(y,x,z) \ do { softn->ipf_nat_stats.ns_side[y].x++; \ DT(z); } while (0) #define NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \ DT1(x, fr_info_t *, fin); } while (0) static ipftuneable_t ipf_nat_tuneables[] = { /* nat */ { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) }, "nat_lock", 0, 1, stsizeof(ipf_nat_softc_t, ipf_nat_lock), IPFT_RDONLY, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) }, "nat_table_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_table_sz), 0, NULL, ipf_nat_rehash }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) }, "nat_table_max", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_table_max), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) }, "nat_rules_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz), 0, NULL, ipf_nat_rehash_rules }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) }, "rdr_rules_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), 0, NULL, ipf_nat_rehash_rules }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) }, "hostmap_size", 1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz), 0, NULL, ipf_nat_hostmap_rehash }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) }, "nat_maxbucket",1, 0x7fffffff, stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) }, "nat_logging", 0, 1, stsizeof(ipf_nat_softc_t, ipf_nat_logging), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) }, "nat_doflush", 0, 1, stsizeof(ipf_nat_softc_t, ipf_nat_doflush), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) }, "nat_table_wm_low", 1, 99, stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low), 0, NULL, NULL }, { { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) }, "nat_table_wm_high", 2, 100, stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high), 0, NULL, NULL }, { { 0 }, NULL, 0, 0, 0, 0, NULL, NULL } }; /* ======================================================================== */ /* How the NAT is organised and works. */ /* */ /* Inside (interface y) NAT Outside (interface x) */ /* -------------------- -+- ------------------------------------- */ /* Packet going | out, processsed by ipf_nat_checkout() for x */ /* ------------> | ------------> */ /* src=10.1.1.1 | src=192.1.1.1 */ /* | */ /* | in, processed by ipf_nat_checkin() for x */ /* <------------ | <------------ */ /* dst=10.1.1.1 | dst=192.1.1.1 */ /* -------------------- -+- ------------------------------------- */ /* ipf_nat_checkout() - changes ip_src and if required, sport */ /* - creates a new mapping, if required. */ /* ipf_nat_checkin() - changes ip_dst and if required, dport */ /* */ /* In the NAT table, internal source is recorded as "in" and externally */ /* seen as "out". */ /* ======================================================================== */ #if SOLARIS && !defined(INSTANCES) extern int pfil_delayed_copy; #endif static int ipf_nat_flush_entry(ipf_main_softc_t *, void *); static int ipf_nat_getent(ipf_main_softc_t *, caddr_t, int); static int ipf_nat_getsz(ipf_main_softc_t *, caddr_t, int); static int ipf_nat_putent(ipf_main_softc_t *, caddr_t, int); static void ipf_nat_addmap(ipf_nat_softc_t *, ipnat_t *); static void ipf_nat_addrdr(ipf_nat_softc_t *, ipnat_t *); static int ipf_nat_builddivertmp(ipf_nat_softc_t *, ipnat_t *); static int ipf_nat_clearlist(ipf_main_softc_t *, ipf_nat_softc_t *); static int ipf_nat_cmp_rules(ipnat_t *, ipnat_t *); static int ipf_nat_decap(fr_info_t *, nat_t *); static void ipf_nat_delrule(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *, int); static int ipf_nat_extraflush(ipf_main_softc_t *, ipf_nat_softc_t *, int); static int ipf_nat_finalise(fr_info_t *, nat_t *); static int ipf_nat_flushtable(ipf_main_softc_t *, ipf_nat_softc_t *); static int ipf_nat_getnext(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfobj_t *); static int ipf_nat_gettable(ipf_main_softc_t *, ipf_nat_softc_t *, char *); static hostmap_t *ipf_nat_hostmap(ipf_nat_softc_t *, ipnat_t *, struct in_addr, struct in_addr, struct in_addr, u_32_t); static int ipf_nat_icmpquerytype(int); static int ipf_nat_iterator(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, ipfobj_t *); static int ipf_nat_match(fr_info_t *, ipnat_t *); static int ipf_nat_matcharray(nat_t *, int *, u_long); static int ipf_nat_matchflush(ipf_main_softc_t *, ipf_nat_softc_t *, caddr_t); static void ipf_nat_mssclamp(tcphdr_t *, u_32_t, fr_info_t *, u_short *); static int ipf_nat_newmap(fr_info_t *, nat_t *, natinfo_t *); static int ipf_nat_newdivert(fr_info_t *, nat_t *, natinfo_t *); static int ipf_nat_newrdr(fr_info_t *, nat_t *, natinfo_t *); static int ipf_nat_newrewrite(fr_info_t *, nat_t *, natinfo_t *); static int ipf_nat_nextaddr(fr_info_t *, nat_addr_t *, u_32_t *, u_32_t *); static int ipf_nat_nextaddrinit(ipf_main_softc_t *, char *, nat_addr_t *, int, void *); static int ipf_nat_resolverule(ipf_main_softc_t *, ipnat_t *); static int ipf_nat_ruleaddrinit(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *); static void ipf_nat_rule_fini(ipf_main_softc_t *, ipnat_t *); static int ipf_nat_rule_init(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *); static int ipf_nat_siocaddnat(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *, int); static void ipf_nat_siocdelnat(ipf_main_softc_t *, ipf_nat_softc_t *, ipnat_t *, int); static void ipf_nat_tabmove(ipf_nat_softc_t *, nat_t *); /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_main_load */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* The only global NAT structure that needs to be initialised is the filter */ /* rule that is used with blocking packets. */ /* ------------------------------------------------------------------------ */ int ipf_nat_main_load(void) { return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_main_unload */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: Nil */ /* */ /* A null-op function that exists as a placeholder so that the flow in */ /* other functions is obvious. */ /* ------------------------------------------------------------------------ */ int ipf_nat_main_unload(void) { return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_soft_create */ /* Returns: void * - NULL = failure, else pointer to NAT context */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Allocate the initial soft context structure for NAT and populate it with */ /* some default values. Creating the tables is left until we call _init so */ /* that sizes can be changed before we get under way. */ /* ------------------------------------------------------------------------ */ void * ipf_nat_soft_create(ipf_main_softc_t *softc) { ipf_nat_softc_t *softn; KMALLOC(softn, ipf_nat_softc_t *); if (softn == NULL) return (NULL); bzero((char *)softn, sizeof(*softn)); softn->ipf_nat_tune = ipf_tune_array_copy(softn, sizeof(ipf_nat_tuneables), ipf_nat_tuneables); if (softn->ipf_nat_tune == NULL) { ipf_nat_soft_destroy(softc, softn); return (NULL); } if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) { ipf_nat_soft_destroy(softc, softn); return (NULL); } softn->ipf_nat_list_tail = &softn->ipf_nat_list; if (softc->ipf_large_nat) { softn->ipf_nat_table_max = NAT_TABLE_MAX_LARGE; softn->ipf_nat_table_sz = NAT_TABLE_SZ_LARGE; softn->ipf_nat_maprules_sz = NAT_SIZE_LARGE; softn->ipf_nat_rdrrules_sz = RDR_SIZE_LARGE; softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE_LARGE; } else { softn->ipf_nat_table_max = NAT_TABLE_MAX_NORMAL; softn->ipf_nat_table_sz = NAT_TABLE_SZ_NORMAL; softn->ipf_nat_maprules_sz = NAT_SIZE_NORMAL; softn->ipf_nat_rdrrules_sz = RDR_SIZE_NORMAL; softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE_NORMAL; } softn->ipf_nat_doflush = 0; #ifdef IPFILTER_LOG softn->ipf_nat_logging = 1; #else softn->ipf_nat_logging = 0; #endif softn->ipf_nat_defage = DEF_NAT_AGE; softn->ipf_nat_defipage = IPF_TTLVAL(60); softn->ipf_nat_deficmpage = IPF_TTLVAL(3); softn->ipf_nat_table_wm_high = 99; softn->ipf_nat_table_wm_low = 90; return (softn); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* ------------------------------------------------------------------------ */ void ipf_nat_soft_destroy(ipf_main_softc_t *softc, void *arg) { ipf_nat_softc_t *softn = arg; if (softn->ipf_nat_tune != NULL) { ipf_tune_array_unlink(softc, softn->ipf_nat_tune); KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables)); softn->ipf_nat_tune = NULL; } KFREE(softn); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_init */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Initialise all of the NAT locks, tables and other structures. */ /* ------------------------------------------------------------------------ */ int ipf_nat_soft_init(ipf_main_softc_t *softc, void *arg) { ipf_nat_softc_t *softn = arg; ipftq_t *tq; int i; KMALLOCS(softn->ipf_nat_table[0], nat_t **, \ sizeof(nat_t *) * softn->ipf_nat_table_sz); if (softn->ipf_nat_table[0] != NULL) { bzero((char *)softn->ipf_nat_table[0], softn->ipf_nat_table_sz * sizeof(nat_t *)); } else { return (-1); } KMALLOCS(softn->ipf_nat_table[1], nat_t **, \ sizeof(nat_t *) * softn->ipf_nat_table_sz); if (softn->ipf_nat_table[1] != NULL) { bzero((char *)softn->ipf_nat_table[1], softn->ipf_nat_table_sz * sizeof(nat_t *)); } else { return (-2); } KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \ sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); if (softn->ipf_nat_map_rules != NULL) { bzero((char *)softn->ipf_nat_map_rules, softn->ipf_nat_maprules_sz * sizeof(ipnat_t *)); } else { return (-3); } KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \ sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); if (softn->ipf_nat_rdr_rules != NULL) { bzero((char *)softn->ipf_nat_rdr_rules, softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *)); } else { return (-4); } KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \ sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); if (softn->ipf_hm_maptable != NULL) { bzero((char *)softn->ipf_hm_maptable, sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); } else { return (-5); } softn->ipf_hm_maplist = NULL; KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *, softn->ipf_nat_table_sz * sizeof(u_int)); if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) { return (-6); } bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *, softn->ipf_nat_table_sz * sizeof(u_int)); if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) { return (-7); } bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); if (softn->ipf_nat_maxbucket == 0) { for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1) softn->ipf_nat_maxbucket++; softn->ipf_nat_maxbucket *= 2; } ipf_sttab_init(softc, softn->ipf_nat_tcptq); /* * Increase this because we may have "keep state" following this too * and packet storms can occur if this is removed too quickly. */ softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next = &softn->ipf_nat_udptq; IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage, "nat ipftq udp tab"); softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq; IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage, "nat ipftq udpack tab"); softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq; IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage, "nat icmp ipftq tab"); softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq; IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage, "nat icmpack ipftq tab"); softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq; IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage, "nat ip ipftq tab"); softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending; IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab"); softn->ipf_nat_pending.ifq_next = NULL; for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) { if (tq->ifq_ttl < softn->ipf_nat_deficmpage) tq->ifq_ttl = softn->ipf_nat_deficmpage; else if (tq->ifq_ttl > softn->ipf_nat_defage && softc->ipf_large_nat) tq->ifq_ttl = softn->ipf_nat_defage; } /* * Increase this because we may have "keep state" following * this too and packet storms can occur if this is removed * too quickly. */ softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack; MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex"); MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex"); softn->ipf_nat_inited = 1; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_soft_fini */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Free all memory used by NAT structures allocated at runtime. */ /* ------------------------------------------------------------------------ */ int ipf_nat_soft_fini(ipf_main_softc_t *softc, void *arg) { ipf_nat_softc_t *softn = arg; ipftq_t *ifq, *ifqnext; (void) ipf_nat_clearlist(softc, softn); (void) ipf_nat_flushtable(softc, softn); /* * Proxy timeout queues are not cleaned here because although they * exist on the NAT list, ipf_proxy_unload is called after unload * and the proxies actually are responsible for them being created. * Should the proxy timeouts have their own list? There's no real * justification as this is the only complication. */ for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (ipf_deletetimeoutqueue(ifq) == 0) ipf_freetimeoutqueue(softc, ifq); } if (softn->ipf_nat_table[0] != NULL) { KFREES(softn->ipf_nat_table[0], sizeof(nat_t *) * softn->ipf_nat_table_sz); softn->ipf_nat_table[0] = NULL; } if (softn->ipf_nat_table[1] != NULL) { KFREES(softn->ipf_nat_table[1], sizeof(nat_t *) * softn->ipf_nat_table_sz); softn->ipf_nat_table[1] = NULL; } if (softn->ipf_nat_map_rules != NULL) { KFREES(softn->ipf_nat_map_rules, sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz); softn->ipf_nat_map_rules = NULL; } if (softn->ipf_nat_rdr_rules != NULL) { KFREES(softn->ipf_nat_rdr_rules, sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz); softn->ipf_nat_rdr_rules = NULL; } if (softn->ipf_hm_maptable != NULL) { KFREES(softn->ipf_hm_maptable, sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz); softn->ipf_hm_maptable = NULL; } if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, sizeof(u_int) * softn->ipf_nat_table_sz); softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL; } if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, sizeof(u_int) * softn->ipf_nat_table_sz); softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL; } if (softn->ipf_nat_inited == 1) { softn->ipf_nat_inited = 0; ipf_sttab_destroy(softn->ipf_nat_tcptq); MUTEX_DESTROY(&softn->ipf_nat_new); MUTEX_DESTROY(&softn->ipf_nat_io); MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock); MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock); } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_setlock */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to soft state information */ /* tmp(I) - new lock value */ /* */ /* Set the "lock status" of NAT to the value in tmp. */ /* ------------------------------------------------------------------------ */ void ipf_nat_setlock(void *arg, int tmp) { ipf_nat_softc_t *softn = arg; softn->ipf_nat_lock = tmp; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_addrdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ /* Adds a redirect rule to the hash table of redirect rules and the list of */ /* loaded NAT rules. Updates the bitmask indicating which netmasks are in */ /* use by redirect rules. */ /* ------------------------------------------------------------------------ */ static void ipf_nat_addrdr(ipf_nat_softc_t *softn, ipnat_t *n) { ipnat_t **np; u_32_t j; u_int hv; u_int rhv; int k; if (n->in_odstatype == FRI_NORMAL) { k = count4bits(n->in_odstmsk); ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask); j = (n->in_odstaddr & n->in_odstmsk); rhv = NAT_HASH_FN(j, 0, 0xffffffff); } else { ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask); j = 0; rhv = 0; } hv = rhv % softn->ipf_nat_rdrrules_sz; np = softn->ipf_nat_rdr_rules + hv; while (*np != NULL) np = &(*np)->in_rnext; n->in_rnext = NULL; n->in_prnext = np; n->in_hv[0] = hv; n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_addmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to add */ /* */ /* Adds a NAT map rule to the hash table of rules and the list of loaded */ /* NAT rules. Updates the bitmask indicating which netmasks are in use by */ /* redirect rules. */ /* ------------------------------------------------------------------------ */ static void ipf_nat_addmap(ipf_nat_softc_t *softn, ipnat_t *n) { ipnat_t **np; u_32_t j; u_int hv; u_int rhv; int k; if (n->in_osrcatype == FRI_NORMAL) { k = count4bits(n->in_osrcmsk); ipf_inet_mask_add(k, &softn->ipf_nat_map_mask); j = (n->in_osrcaddr & n->in_osrcmsk); rhv = NAT_HASH_FN(j, 0, 0xffffffff); } else { ipf_inet_mask_add(0, &softn->ipf_nat_map_mask); j = 0; rhv = 0; } hv = rhv % softn->ipf_nat_maprules_sz; np = softn->ipf_nat_map_rules + hv; while (*np != NULL) np = &(*np)->in_mnext; n->in_mnext = NULL; n->in_pmnext = np; n->in_hv[1] = rhv; n->in_use++; *np = n; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delrdr */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a redirect rule from the hash table of redirect rules. */ /* ------------------------------------------------------------------------ */ void ipf_nat_delrdr(ipf_nat_softc_t *softn, ipnat_t *n) { if (n->in_odstatype == FRI_NORMAL) { int k = count4bits(n->in_odstmsk); ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask); } else { ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask); } if (n->in_rnext) n->in_rnext->in_prnext = n->in_prnext; *n->in_prnext = n->in_rnext; n->in_use--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delmap */ /* Returns: Nil */ /* Parameters: n(I) - pointer to NAT rule to delete */ /* */ /* Removes a NAT map rule from the hash table of NAT map rules. */ /* ------------------------------------------------------------------------ */ void ipf_nat_delmap(ipf_nat_softc_t *softn, ipnat_t *n) { if (n->in_osrcatype == FRI_NORMAL) { int k = count4bits(n->in_osrcmsk); ipf_inet_mask_del(k, &softn->ipf_nat_map_mask); } else { ipf_inet_mask_del(0, &softn->ipf_nat_map_mask); } if (n->in_mnext != NULL) n->in_mnext->in_pmnext = n->in_pmnext; *n->in_pmnext = n->in_mnext; n->in_use--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hostmap */ /* Returns: struct hostmap* - NULL if no hostmap could be created, */ /* else a pointer to the hostmapping to use */ /* Parameters: np(I) - pointer to NAT rule */ /* real(I) - real IP address */ /* map(I) - mapped IP address */ /* port(I) - destination port number */ /* Write Locks: ipf_nat */ /* */ /* Check if an ip address has already been allocated for a given mapping */ /* that is not doing port based translation. If is not yet allocated, then */ /* create a new entry if a non-NULL NAT rule pointer has been supplied. */ /* ------------------------------------------------------------------------ */ static struct hostmap * ipf_nat_hostmap(ipf_nat_softc_t *softn, ipnat_t *np, struct in_addr src, struct in_addr dst, struct in_addr map, u_32_t port) { hostmap_t *hm; u_int hv, rhv; hv = (src.s_addr ^ dst.s_addr); hv += src.s_addr; hv += dst.s_addr; rhv = hv; hv %= softn->ipf_nat_hostmap_sz; for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext) if ((hm->hm_osrcip.s_addr == src.s_addr) && (hm->hm_odstip.s_addr == dst.s_addr) && ((np == NULL) || (np == hm->hm_ipnat)) && ((port == 0) || (port == hm->hm_port))) { softn->ipf_nat_stats.ns_hm_addref++; hm->hm_ref++; return (hm); } if (np == NULL) { softn->ipf_nat_stats.ns_hm_nullnp++; return (NULL); } KMALLOC(hm, hostmap_t *); if (hm) { hm->hm_next = softn->ipf_hm_maplist; hm->hm_pnext = &softn->ipf_hm_maplist; if (softn->ipf_hm_maplist != NULL) softn->ipf_hm_maplist->hm_pnext = &hm->hm_next; softn->ipf_hm_maplist = hm; hm->hm_hnext = softn->ipf_hm_maptable[hv]; hm->hm_phnext = softn->ipf_hm_maptable + hv; if (softn->ipf_hm_maptable[hv] != NULL) softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; softn->ipf_hm_maptable[hv] = hm; hm->hm_ipnat = np; np->in_use++; hm->hm_osrcip = src; hm->hm_odstip = dst; hm->hm_nsrcip = map; hm->hm_ndstip.s_addr = 0; hm->hm_ref = 1; hm->hm_port = port; hm->hm_hv = rhv; hm->hm_v = 4; softn->ipf_nat_stats.ns_hm_new++; } else { softn->ipf_nat_stats.ns_hm_newfail++; } return (hm); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hostmapdel */ /* Returns: Nil */ /* Parameters: hmp(I) - pointer to hostmap structure pointer */ /* Write Locks: ipf_nat */ /* */ /* Decrement the references to this hostmap structure by one. If this */ /* reaches zero then remove it and free it. */ /* ------------------------------------------------------------------------ */ void ipf_nat_hostmapdel(ipf_main_softc_t *softc, struct hostmap **hmp) { struct hostmap *hm; hm = *hmp; *hmp = NULL; hm->hm_ref--; if (hm->hm_ref == 0) { ipf_nat_rule_deref(softc, &hm->hm_ipnat); if (hm->hm_hnext) hm->hm_hnext->hm_phnext = hm->hm_phnext; *hm->hm_phnext = hm->hm_hnext; if (hm->hm_next) hm->hm_next->hm_pnext = hm->hm_pnext; *hm->hm_pnext = hm->hm_next; KFREE(hm); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_fix_outcksum */ /* Returns: Nil */ /* Parameters: cksum(I) - ipf_cksum_t, value of fin_cksum */ /* sp(I) - location of 16bit checksum to update */ /* n(I) - amount to adjust checksum by */ /* partial(I) - partial checksum */ /* */ /* Adjusts the 16bit checksum by "n" for packets going out. */ /* ------------------------------------------------------------------------ */ void ipf_fix_outcksum(int cksum, u_short *sp, u_32_t n, u_32_t partial) { u_short sumshort; u_32_t sum1; if (n == 0) return; if (cksum == 4) { *sp = 0; return; } if (cksum == 2) { sum1 = partial; sum1 = (sum1 & 0xffff) + (sum1 >> 16); *sp = htons(sum1); return; } sum1 = (~ntohs(*sp)) & 0xffff; sum1 += (n); sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ sum1 = (sum1 >> 16) + (sum1 & 0xffff); sumshort = ~(u_short)sum1; *(sp) = htons(sumshort); } /* ------------------------------------------------------------------------ */ /* Function: ipf_fix_incksum */ /* Returns: Nil */ /* Parameters: cksum(I) - ipf_cksum_t, value of fin_cksum */ /* sp(I) - location of 16bit checksum to update */ /* n(I) - amount to adjust checksum by */ /* partial(I) - partial checksum */ /* */ /* Adjusts the 16bit checksum by "n" for packets going in. */ /* ------------------------------------------------------------------------ */ void ipf_fix_incksum(int cksum, u_short *sp, u_32_t n, u_32_t partial) { u_short sumshort; u_32_t sum1; if (n == 0) return; if (cksum == 4) { *sp = 0; return; } if (cksum == 2) { sum1 = partial; sum1 = (sum1 & 0xffff) + (sum1 >> 16); *sp = htons(sum1); return; } sum1 = (~ntohs(*sp)) & 0xffff; sum1 += ~(n) & 0xffff; sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ sum1 = (sum1 >> 16) + (sum1 & 0xffff); sumshort = ~(u_short)sum1; *(sp) = htons(sumshort); } /* ------------------------------------------------------------------------ */ /* Function: ipf_fix_datacksum */ /* Returns: Nil */ /* Parameters: sp(I) - location of 16bit checksum to update */ /* n(I) - amount to adjust checksum by */ /* */ /* Fix_datacksum is used *only* for the adjustments of checksums in the */ /* data section of an IP packet. */ /* */ /* The only situation in which you need to do this is when NAT'ing an */ /* ICMP error message. Such a message, contains in its body the IP header */ /* of the original IP packet, that causes the error. */ /* */ /* You can't use fix_incksum or fix_outcksum in that case, because for the */ /* kernel the data section of the ICMP error is just data, and no special */ /* processing like hardware cksum or ntohs processing have been done by the */ /* kernel on the data section. */ /* ------------------------------------------------------------------------ */ void ipf_fix_datacksum(u_short *sp, u_32_t n) { u_short sumshort; u_32_t sum1; if (n == 0) return; sum1 = (~ntohs(*sp)) & 0xffff; sum1 += (n); sum1 = (sum1 >> 16) + (sum1 & 0xffff); /* Again */ sum1 = (sum1 >> 16) + (sum1 & 0xffff); sumshort = ~(u_short)sum1; *(sp) = htons(sumshort); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_ioctl */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command integer */ /* mode(I) - file mode bits used with open */ /* uid(I) - uid of calling process */ /* ctx(I) - pointer used as key for finding context */ /* */ /* Processes an ioctl call made to operate on the IP Filter NAT device. */ /* ------------------------------------------------------------------------ */ int ipf_nat_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd, int mode, int uid, void *ctx) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; int error = 0, ret, arg, getlock; ipnat_t *nat, *nt, *n; ipnat_t natd; SPL_INT(s); #if !SOLARIS && defined(_KERNEL) # if NETBSD_GE_REV(399002000) if ((mode & FWRITE) && kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL, KAUTH_REQ_NETWORK_FIREWALL_FW, NULL, NULL, NULL)) # else # if defined(__FreeBSD__) if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) # else if ((securelevel >= 3) && (mode & FWRITE)) # endif # endif { IPFERROR(60001); return (EPERM); } # if defined(__FreeBSD__) if (jailed_without_vnet(curthread->td_ucred)) { IPFERROR(60076); return (EOPNOTSUPP); } # endif #endif getlock = (mode & NAT_LOCKHELD) ? 0 : 1; n = NULL; nt = NULL; nat = NULL; if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) || (cmd == (ioctlcmd_t)SIOCPURGENAT)) { if (mode & NAT_SYSSPACE) { bcopy(data, (char *)&natd, sizeof(natd)); nat = &natd; error = 0; } else { bzero(&natd, sizeof(natd)); error = ipf_inobj(softc, data, NULL, &natd, IPFOBJ_IPNAT); if (error != 0) goto done; if (natd.in_size < sizeof(ipnat_t)) { error = EINVAL; goto done; } KMALLOCS(nt, ipnat_t *, natd.in_size); if (nt == NULL) { IPFERROR(60070); error = ENOMEM; goto done; } bzero(nt, natd.in_size); error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT, natd.in_size); if (error) goto done; nat = nt; } /* * For add/delete, look to see if the NAT entry is * already present */ nat->in_flags &= IPN_USERFLAGS; if ((nat->in_redir & NAT_MAPBLK) == 0) { if (nat->in_osrcatype == FRI_NORMAL || nat->in_osrcatype == FRI_NONE) nat->in_osrcaddr &= nat->in_osrcmsk; if (nat->in_odstatype == FRI_NORMAL || nat->in_odstatype == FRI_NONE) nat->in_odstaddr &= nat->in_odstmsk; if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) { if (nat->in_nsrcatype == FRI_NORMAL) nat->in_nsrcaddr &= nat->in_nsrcmsk; if (nat->in_ndstatype == FRI_NORMAL) nat->in_ndstaddr &= nat->in_ndstmsk; } } error = ipf_nat_rule_init(softc, softn, nat); if (error != 0) goto done; MUTEX_ENTER(&softn->ipf_nat_io); for (n = softn->ipf_nat_list; n != NULL; n = n->in_next) if (ipf_nat_cmp_rules(nat, n) == 0) break; } switch (cmd) { #ifdef IPFILTER_LOG case SIOCIPFFB : { int tmp; if (!(mode & FWRITE)) { IPFERROR(60002); error = EPERM; } else { tmp = ipf_log_clear(softc, IPL_LOGNAT); error = BCOPYOUT(&tmp, data, sizeof(tmp)); if (error != 0) { IPFERROR(60057); error = EFAULT; } } break; } case SIOCSETLG : if (!(mode & FWRITE)) { IPFERROR(60003); error = EPERM; } else { error = BCOPYIN(data, &softn->ipf_nat_logging, sizeof(softn->ipf_nat_logging)); if (error != 0) error = EFAULT; } break; case SIOCGETLG : error = BCOPYOUT(&softn->ipf_nat_logging, data, sizeof(softn->ipf_nat_logging)); if (error != 0) { IPFERROR(60004); error = EFAULT; } break; case FIONREAD : arg = ipf_log_bytesused(softc, IPL_LOGNAT); error = BCOPYOUT(&arg, data, sizeof(arg)); if (error != 0) { IPFERROR(60005); error = EFAULT; } break; #endif case SIOCADNAT : if (!(mode & FWRITE)) { IPFERROR(60006); error = EPERM; } else if (n != NULL) { natd.in_flineno = n->in_flineno; (void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT); IPFERROR(60007); error = EEXIST; } else if (nt == NULL) { IPFERROR(60008); error = ENOMEM; } if (error != 0) { MUTEX_EXIT(&softn->ipf_nat_io); break; } if (nat != nt) bcopy((char *)nat, (char *)nt, sizeof(*n)); error = ipf_nat_siocaddnat(softc, softn, nt, getlock); MUTEX_EXIT(&softn->ipf_nat_io); if (error == 0) { nat = NULL; nt = NULL; } break; case SIOCRMNAT : case SIOCPURGENAT : if (!(mode & FWRITE)) { IPFERROR(60009); error = EPERM; n = NULL; } else if (n == NULL) { IPFERROR(60010); error = ESRCH; } if (error != 0) { MUTEX_EXIT(&softn->ipf_nat_io); break; } if (cmd == (ioctlcmd_t)SIOCPURGENAT) { error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT, n->in_size); if (error) { MUTEX_EXIT(&softn->ipf_nat_io); goto done; } n->in_flags |= IPN_PURGE; } ipf_nat_siocdelnat(softc, softn, n, getlock); MUTEX_EXIT(&softn->ipf_nat_io); n = NULL; break; case SIOCGNATS : { natstat_t *nsp = &softn->ipf_nat_stats; nsp->ns_side[0].ns_table = softn->ipf_nat_table[0]; nsp->ns_side[1].ns_table = softn->ipf_nat_table[1]; nsp->ns_list = softn->ipf_nat_list; nsp->ns_maptable = softn->ipf_hm_maptable; nsp->ns_maplist = softn->ipf_hm_maplist; nsp->ns_nattab_sz = softn->ipf_nat_table_sz; nsp->ns_nattab_max = softn->ipf_nat_table_max; nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz; nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz; nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz; nsp->ns_instances = softn->ipf_nat_instances; nsp->ns_ticks = softc->ipf_ticks; #ifdef IPFILTER_LOGGING nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT); nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT); #else nsp->ns_log_ok = 0; nsp->ns_log_fail = 0; #endif error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT); break; } case SIOCGNATL : { natlookup_t nl; error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP); if (error == 0) { void *ptr; if (getlock) { READ_ENTER(&softc->ipf_nat); } switch (nl.nl_v) { case 4 : ptr = ipf_nat_lookupredir(&nl); break; #ifdef USE_INET6 case 6 : ptr = ipf_nat6_lookupredir(&nl); break; #endif default: ptr = NULL; break; } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (ptr != NULL) { error = ipf_outobj(softc, data, &nl, IPFOBJ_NATLOOKUP); } else { IPFERROR(60011); error = ESRCH; } } break; } case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */ if (!(mode & FWRITE)) { IPFERROR(60012); error = EPERM; break; } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } error = BCOPYIN(data, &arg, sizeof(arg)); if (error != 0) { IPFERROR(60013); error = EFAULT; } else { if (arg == 0) ret = ipf_nat_flushtable(softc, softn); else if (arg == 1) ret = ipf_nat_clearlist(softc, softn); else ret = ipf_nat_extraflush(softc, softn, arg); ipf_proxy_flush(softc->ipf_proxy_soft, arg); } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (error == 0) { error = BCOPYOUT(&ret, data, sizeof(ret)); } break; case SIOCMATCHFLUSH : if (!(mode & FWRITE)) { IPFERROR(60014); error = EPERM; break; } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } error = ipf_nat_matchflush(softc, softn, data); if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } break; case SIOCPROXY : error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx); break; case SIOCSTLCK : if (!(mode & FWRITE)) { IPFERROR(60015); error = EPERM; } else { error = ipf_lock(data, &softn->ipf_nat_lock); } break; case SIOCSTPUT : if ((mode & FWRITE) != 0) { error = ipf_nat_putent(softc, data, getlock); } else { IPFERROR(60016); error = EACCES; } break; case SIOCSTGSZ : if (softn->ipf_nat_lock) { error = ipf_nat_getsz(softc, data, getlock); } else { IPFERROR(60017); error = EACCES; } break; case SIOCSTGET : if (softn->ipf_nat_lock) { error = ipf_nat_getent(softc, data, getlock); } else { IPFERROR(60018); error = EACCES; } break; case SIOCGENITER : { ipfgeniter_t iter; ipftoken_t *token; ipfobj_t obj; error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER); if (error != 0) break; SPL_SCHED(s); token = ipf_token_find(softc, iter.igi_type, uid, ctx); if (token != NULL) { error = ipf_nat_iterator(softc, token, &iter, &obj); WRITE_ENTER(&softc->ipf_tokens); ipf_token_deref(softc, token); RWLOCK_EXIT(&softc->ipf_tokens); } SPL_X(s); break; } case SIOCIPFDELTOK : error = BCOPYIN(data, &arg, sizeof(arg)); if (error == 0) { SPL_SCHED(s); error = ipf_token_del(softc, arg, uid, ctx); SPL_X(s); } else { IPFERROR(60019); error = EFAULT; } break; case SIOCGTQTAB : error = ipf_outobj(softc, data, softn->ipf_nat_tcptq, IPFOBJ_STATETQTAB); break; case SIOCGTABL : error = ipf_nat_gettable(softc, softn, data); break; default : IPFERROR(60020); error = EINVAL; break; } done: if (nat != NULL) ipf_nat_rule_fini(softc, nat); if (nt != NULL) KFREES(nt, nt->in_size); return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_siocaddnat */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - pointer to new NAT rule */ /* np(I) - pointer to where to insert new NAT rule */ /* getlock(I) - flag indicating if lock on is held */ /* Mutex Locks: ipf_nat_io */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ static int ipf_nat_siocaddnat(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n, int getlock) { int error = 0; if (ipf_nat_resolverule(softc, n) != 0) { IPFERROR(60022); return (ENOENT); } if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) { IPFERROR(60023); return (EINVAL); } if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { /* * Prerecord whether or not the destination of the divert * is local or not to the interface the packet is going * to be sent out. */ n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], n->in_ifps[1], &n->in_ndstip6); } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } n->in_next = NULL; n->in_pnext = softn->ipf_nat_list_tail; *n->in_pnext = n; softn->ipf_nat_list_tail = &n->in_next; n->in_use++; if (n->in_redir & NAT_REDIRECT) { n->in_flags &= ~IPN_NOTDST; switch (n->in_v[0]) { case 4 : ipf_nat_addrdr(softn, n); break; #ifdef USE_INET6 case 6 : ipf_nat6_addrdr(softn, n); break; #endif default : break; } ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr); } if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { n->in_flags &= ~IPN_NOTSRC; switch (n->in_v[0]) { case 4 : ipf_nat_addmap(softn, n); break; #ifdef USE_INET6 case 6 : ipf_nat6_addmap(softn, n); break; #endif default : break; } ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map); } if (n->in_age[0] != 0) n->in_tqehead[0] = ipf_addtimeoutqueue(softc, &softn->ipf_nat_utqe, n->in_age[0]); if (n->in_age[1] != 0) n->in_tqehead[1] = ipf_addtimeoutqueue(softc, &softn->ipf_nat_utqe, n->in_age[1]); MUTEX_INIT(&n->in_lock, "ipnat rule lock"); n = NULL; ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); #if SOLARIS && !defined(INSTANCES) pfil_delayed_copy = 0; #endif if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); /* WRITE */ } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_ruleaddrinit */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - pointer to NAT rule */ /* */ /* Initialise all of the NAT address structures in a NAT rule. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_ruleaddrinit(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n) { int idx, error; if ((n->in_ndst.na_atype == FRI_LOOKUP) && (n->in_ndst.na_type != IPLT_DSTLIST)) { IPFERROR(60071); return (EINVAL); } if ((n->in_nsrc.na_atype == FRI_LOOKUP) && (n->in_nsrc.na_type != IPLT_DSTLIST)) { IPFERROR(60069); return (EINVAL); } if (n->in_redir == NAT_BIMAP) { n->in_ndstaddr = n->in_osrcaddr; n->in_ndstmsk = n->in_osrcmsk; n->in_odstaddr = n->in_nsrcaddr; n->in_odstmsk = n->in_nsrcmsk; } if (n->in_redir & NAT_REDIRECT) idx = 1; else idx = 0; /* * Initialise all of the address fields. */ error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1, n->in_ifps[idx]); if (error != 0) return (error); error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1, n->in_ifps[idx]); if (error != 0) return (error); error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1, n->in_ifps[idx]); if (error != 0) return (error); error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1, n->in_ifps[idx]); if (error != 0) return (error); if (n->in_redir & NAT_DIVERTUDP) ipf_nat_builddivertmp(softn, n); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_resolvrule */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* n(I) - pointer to NAT rule */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ static int ipf_nat_resolverule(ipf_main_softc_t *softc, ipnat_t *n) { char *base; base = n->in_names; n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], n->in_v[0]); if (n->in_ifnames[1] == -1) { n->in_ifnames[1] = n->in_ifnames[0]; n->in_ifps[1] = n->in_ifps[0]; } else { n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], n->in_v[1]); } if (n->in_plabel != -1) { if (n->in_redir & NAT_REDIRECT) n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, n->in_pr[0], base + n->in_plabel); else n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft, n->in_pr[1], base + n->in_plabel); if (n->in_apr == NULL) return (-1); } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_siocdelnat */ /* Returns: int - 0 == success, != 0 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - pointer to new NAT rule */ /* getlock(I) - flag indicating if lock on is held */ /* Mutex Locks: ipf_nat_io */ /* */ /* Handle SIOCADNAT. Resolve and calculate details inside the NAT rule */ /* from information passed to the kernel, then add it to the appropriate */ /* NAT rule table(s). */ /* ------------------------------------------------------------------------ */ static void ipf_nat_siocdelnat(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n, int getlock) { if (getlock) { WRITE_ENTER(&softc->ipf_nat); } ipf_nat_delrule(softc, softn, n, 1); if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); /* READ/WRITE */ } } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_getsz */ /* Returns: int - 0 == success, != 0 is the error value. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to natget structure with kernel */ /* pointer get the size of. */ /* getlock(I) - flag indicating whether or not the caller */ /* holds a lock on ipf_nat */ /* */ /* Handle SIOCSTGSZ. */ /* Return the size of the nat list entry to be copied back to user space. */ /* The size of the entry is stored in the ng_sz field and the enture natget */ /* structure is copied back to the user. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_getsz(ipf_main_softc_t *softc, caddr_t data, int getlock) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ap_session_t *aps; nat_t *nat, *n; natget_t ng; int error; error = BCOPYIN(data, &ng, sizeof(ng)); if (error != 0) { IPFERROR(60024); return (EFAULT); } if (getlock) { READ_ENTER(&softc->ipf_nat); } nat = ng.ng_ptr; if (!nat) { nat = softn->ipf_nat_instances; ng.ng_sz = 0; /* * Empty list so the size returned is 0. Simple. */ if (nat == NULL) { if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } error = BCOPYOUT(&ng, data, sizeof(ng)); if (error != 0) { IPFERROR(60025); return (EFAULT); } return (0); } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n == nat) break; if (n == NULL) { if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } IPFERROR(60026); return (ESRCH); } } /* * Include any space required for proxy data structures. */ ng.ng_sz = sizeof(nat_save_t); aps = nat->nat_aps; if (aps != NULL) { ng.ng_sz += sizeof(ap_session_t) - 4; if (aps->aps_data != 0) ng.ng_sz += aps->aps_psiz; } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } error = BCOPYOUT(&ng, data, sizeof(ng)); if (error != 0) { IPFERROR(60027); return (EFAULT); } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_getent */ /* Returns: int - 0 == success, != 0 is the error value. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to natget structure with kernel pointer*/ /* to NAT structure to copy out. */ /* getlock(I) - flag indicating whether or not the caller */ /* holds a lock on ipf_nat */ /* */ /* Handle SIOCSTGET. */ /* Copies out NAT entry to user space. Any additional data held for a */ /* proxy is also copied, as to is the NAT rule which was responsible for it */ /* ------------------------------------------------------------------------ */ static int ipf_nat_getent(ipf_main_softc_t *softc, caddr_t data, int getlock) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; int error, outsize; ap_session_t *aps; nat_save_t *ipn, ipns; nat_t *n, *nat; error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE); if (error != 0) return (error); if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) { IPFERROR(60028); return (EINVAL); } KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize); if (ipn == NULL) { IPFERROR(60029); return (ENOMEM); } if (getlock) { READ_ENTER(&softc->ipf_nat); } ipn->ipn_dsize = ipns.ipn_dsize; nat = ipns.ipn_next; if (nat == NULL) { nat = softn->ipf_nat_instances; if (nat == NULL) { if (softn->ipf_nat_instances == NULL) { IPFERROR(60030); error = ENOENT; } goto finished; } } else { /* * Make sure the pointer we're copying from exists in the * current list of entries. Security precaution to prevent * copying of random kernel data. */ for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n == nat) break; if (n == NULL) { IPFERROR(60031); error = ESRCH; goto finished; } } ipn->ipn_next = nat->nat_next; /* * Copy the NAT structure. */ bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat)); /* * If we have a pointer to the NAT rule it belongs to, save that too. */ if (nat->nat_ptr != NULL) bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat, sizeof(nat->nat_ptr)); /* * If we also know the NAT entry has an associated filter rule, * save that too. */ if (nat->nat_fr != NULL) bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr, sizeof(ipn->ipn_fr)); /* * Last but not least, if there is an application proxy session set * up for this NAT entry, then copy that out too, including any * private data saved along side it by the proxy. */ aps = nat->nat_aps; outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data); if (aps != NULL) { char *s; if (outsize < sizeof(*aps)) { IPFERROR(60032); error = ENOBUFS; goto finished; } s = ipn->ipn_data; bcopy((char *)aps, s, sizeof(*aps)); s += sizeof(*aps); outsize -= sizeof(*aps); if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz)) bcopy(aps->aps_data, s, aps->aps_psiz); else { IPFERROR(60033); error = ENOBUFS; } } if (error == 0) { error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize); } finished: if (ipn != NULL) { KFREES(ipn, ipns.ipn_dsize); } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_putent */ /* Returns: int - 0 == success, != 0 is the error value. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to natget structure with NAT */ /* structure information to load into the kernel */ /* getlock(I) - flag indicating whether or not a write lock */ /* on is already held. */ /* */ /* Handle SIOCSTPUT. */ /* Loads a NAT table entry from user space, including a NAT rule, proxy and */ /* firewall rule data structures, if pointers to them indicate so. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_putent(ipf_main_softc_t *softc, caddr_t data, int getlock) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; nat_save_t ipn, *ipnn; ap_session_t *aps; nat_t *n, *nat; frentry_t *fr; fr_info_t fin; ipnat_t *in; int error; error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE); if (error != 0) return (error); /* * Initialise early because of code at junkput label. */ n = NULL; in = NULL; aps = NULL; nat = NULL; ipnn = NULL; fr = NULL; /* * New entry, copy in the rest of the NAT entry if it's size is more * than just the nat_t structure. */ if (ipn.ipn_dsize > sizeof(ipn)) { if (ipn.ipn_dsize > 81920) { IPFERROR(60034); error = ENOMEM; goto junkput; } KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize); if (ipnn == NULL) { IPFERROR(60035); return (ENOMEM); } bzero(ipnn, ipn.ipn_dsize); error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize); if (error != 0) { goto junkput; } } else ipnn = &ipn; KMALLOC(nat, nat_t *); if (nat == NULL) { IPFERROR(60037); error = ENOMEM; goto junkput; } bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat)); switch (nat->nat_v[0]) { case 4: #ifdef USE_INET6 case 6 : #endif break; default : IPFERROR(60061); error = EPROTONOSUPPORT; goto junkput; /*NOTREACHED*/ } /* * Initialize all these so that ipf_nat_delete() doesn't cause a crash. */ bzero((char *)nat, offsetof(struct nat, nat_tqe)); nat->nat_tqe.tqe_pnext = NULL; nat->nat_tqe.tqe_next = NULL; nat->nat_tqe.tqe_ifq = NULL; nat->nat_tqe.tqe_parent = nat; /* * Restore the rule associated with this nat session */ in = ipnn->ipn_nat.nat_ptr; if (in != NULL) { KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size); nat->nat_ptr = in; if (in == NULL) { IPFERROR(60038); error = ENOMEM; goto junkput; } bcopy((char *)&ipnn->ipn_ipnat, (char *)in, ipnn->ipn_ipnat.in_size); in->in_use = 1; in->in_flags |= IPN_DELETE; ATOMIC_INC32(softn->ipf_nat_stats.ns_rules); if (ipf_nat_resolverule(softc, in) != 0) { IPFERROR(60039); error = ESRCH; goto junkput; } } /* * Check that the NAT entry doesn't already exist in the kernel. * * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry. To do * this, we check to see if the inbound combination of addresses and * ports is already known. Similar logic is applied for NAT_INBOUND. * */ bzero((char *)&fin, sizeof(fin)); fin.fin_v = nat->nat_v[0]; fin.fin_p = nat->nat_pr[0]; fin.fin_rev = nat->nat_rev; fin.fin_ifp = nat->nat_ifps[0]; fin.fin_data[0] = ntohs(nat->nat_ndport); fin.fin_data[1] = ntohs(nat->nat_nsport); switch (nat->nat_dir) { case NAT_OUTBOUND : case NAT_DIVERTOUT : if (getlock) { READ_ENTER(&softc->ipf_nat); } fin.fin_v = nat->nat_v[1]; if (nat->nat_v[1] == 4) { n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p, nat->nat_ndstip, nat->nat_nsrcip); #ifdef USE_INET6 } else if (nat->nat_v[1] == 6) { n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p, &nat->nat_ndst6.in6, &nat->nat_nsrc6.in6); #endif } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (n != NULL) { IPFERROR(60040); error = EEXIST; goto junkput; } break; case NAT_INBOUND : case NAT_DIVERTIN : if (getlock) { READ_ENTER(&softc->ipf_nat); } if (fin.fin_v == 4) { n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p, nat->nat_ndstip, nat->nat_nsrcip); #ifdef USE_INET6 } else if (fin.fin_v == 6) { n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p, &nat->nat_ndst6.in6, &nat->nat_nsrc6.in6); #endif } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (n != NULL) { IPFERROR(60041); error = EEXIST; goto junkput; } break; default : IPFERROR(60042); error = EINVAL; goto junkput; } /* * Restore ap_session_t structure. Include the private data allocated * if it was there. */ aps = nat->nat_aps; if (aps != NULL) { KMALLOC(aps, ap_session_t *); nat->nat_aps = aps; if (aps == NULL) { IPFERROR(60043); error = ENOMEM; goto junkput; } bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps)); if (in != NULL) aps->aps_apr = in->in_apr; else aps->aps_apr = NULL; if (aps->aps_psiz != 0) { if (aps->aps_psiz > 81920) { IPFERROR(60044); error = ENOMEM; goto junkput; } KMALLOCS(aps->aps_data, void *, aps->aps_psiz); if (aps->aps_data == NULL) { IPFERROR(60045); error = ENOMEM; goto junkput; } bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data, aps->aps_psiz); } else { aps->aps_psiz = 0; aps->aps_data = NULL; } } /* * If there was a filtering rule associated with this entry then * build up a new one. */ fr = nat->nat_fr; if (fr != NULL) { if ((nat->nat_flags & SI_NEWFR) != 0) { KMALLOC(fr, frentry_t *); nat->nat_fr = fr; if (fr == NULL) { IPFERROR(60046); error = ENOMEM; goto junkput; } ipnn->ipn_nat.nat_fr = fr; fr->fr_ref = 1; (void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE); bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr)); fr->fr_ref = 1; fr->fr_dsize = 0; fr->fr_data = NULL; fr->fr_type = FR_T_NONE; MUTEX_NUKE(&fr->fr_lock); MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock"); } else { if (getlock) { READ_ENTER(&softc->ipf_nat); } for (n = softn->ipf_nat_instances; n; n = n->nat_next) if (n->nat_fr == fr) break; if (n != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (n == NULL) { IPFERROR(60047); error = ESRCH; goto junkput; } } } if (ipnn != &ipn) { KFREES(ipnn, ipn.ipn_dsize); ipnn = NULL; } if (getlock) { WRITE_ENTER(&softc->ipf_nat); } if (fin.fin_v == 4) error = ipf_nat_finalise(&fin, nat); #ifdef USE_INET6 else error = ipf_nat6_finalise(&fin, nat); #endif if (getlock) { RWLOCK_EXIT(&softc->ipf_nat); } if (error == 0) return (0); IPFERROR(60048); error = ENOMEM; junkput: if (fr != NULL) { (void) ipf_derefrule(softc, &fr); } if ((ipnn != NULL) && (ipnn != &ipn)) { KFREES(ipnn, ipn.ipn_dsize); } if (nat != NULL) { if (aps != NULL) { if (aps->aps_data != NULL) { KFREES(aps->aps_data, aps->aps_psiz); } KFREE(aps); } if (in != NULL) { if (in->in_apr) ipf_proxy_deref(in->in_apr); KFREES(in, in->in_size); } KFREE(nat); } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delete */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* nat(I) - pointer to NAT structure to delete */ /* logtype(I) - type of LOG record to create before deleting */ /* Write Lock: ipf_nat */ /* */ /* Delete a nat entry from the various lists and table. If NAT logging is */ /* enabled then generate a NAT log record for this event. */ /* ------------------------------------------------------------------------ */ void ipf_nat_delete(ipf_main_softc_t *softc, struct nat *nat, int logtype) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; int madeorphan = 0, bkt, removed = 0; nat_stat_side_t *nss; struct ipnat *ipn; if (logtype != 0 && softn->ipf_nat_logging != 0) ipf_nat_log(softc, softn, nat, logtype); /* * Take it as a general indication that all the pointers are set if * nat_pnext is set. */ if (nat->nat_pnext != NULL) { removed = 1; bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz; nss = &softn->ipf_nat_stats.ns_side[0]; if (nss->ns_bucketlen[bkt] > 0) nss->ns_bucketlen[bkt]--; if (nss->ns_bucketlen[bkt] == 0) { nss->ns_inuse--; } bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz; nss = &softn->ipf_nat_stats.ns_side[1]; if (nss->ns_bucketlen[bkt] > 0) nss->ns_bucketlen[bkt]--; if (nss->ns_bucketlen[bkt] == 0) { nss->ns_inuse--; } *nat->nat_pnext = nat->nat_next; if (nat->nat_next != NULL) { nat->nat_next->nat_pnext = nat->nat_pnext; nat->nat_next = NULL; } nat->nat_pnext = NULL; *nat->nat_phnext[0] = nat->nat_hnext[0]; if (nat->nat_hnext[0] != NULL) { nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; nat->nat_hnext[0] = NULL; } nat->nat_phnext[0] = NULL; *nat->nat_phnext[1] = nat->nat_hnext[1]; if (nat->nat_hnext[1] != NULL) { nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; nat->nat_hnext[1] = NULL; } nat->nat_phnext[1] = NULL; if ((nat->nat_flags & SI_WILDP) != 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds); } madeorphan = 1; } if (nat->nat_me != NULL) { *nat->nat_me = NULL; nat->nat_me = NULL; nat->nat_ref--; ASSERT(nat->nat_ref >= 0); } if (nat->nat_tqe.tqe_ifq != NULL) { /* * No call to ipf_freetimeoutqueue() is made here, they are * garbage collected in ipf_nat_expire(). */ (void) ipf_deletequeueentry(&nat->nat_tqe); } if (nat->nat_sync) { ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); nat->nat_sync = NULL; } if (logtype == NL_EXPIRE) softn->ipf_nat_stats.ns_expire++; MUTEX_ENTER(&nat->nat_lock); /* * NL_DESTROY should only be passed in when we've got nat_ref >= 2. * This happens when a nat'd packet is blocked and we want to throw * away the NAT session. */ if (logtype == NL_DESTROY) { if (nat->nat_ref > 2) { nat->nat_ref -= 2; MUTEX_EXIT(&nat->nat_lock); if (removed) softn->ipf_nat_stats.ns_orphans++; return; } } else if (nat->nat_ref > 1) { nat->nat_ref--; MUTEX_EXIT(&nat->nat_lock); if (madeorphan == 1) softn->ipf_nat_stats.ns_orphans++; return; } ASSERT(nat->nat_ref >= 0); MUTEX_EXIT(&nat->nat_lock); nat->nat_ref = 0; if (madeorphan == 0) softn->ipf_nat_stats.ns_orphans--; /* * At this point, nat_ref can be either 0 or -1 */ softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--; if (nat->nat_fr != NULL) { (void) ipf_derefrule(softc, &nat->nat_fr); } if (nat->nat_hm != NULL) { ipf_nat_hostmapdel(softc, &nat->nat_hm); } /* * If there is an active reference from the nat entry to its parent * rule, decrement the rule's reference count and free it too if no * longer being used. */ ipn = nat->nat_ptr; nat->nat_ptr = NULL; if (ipn != NULL) { ipn->in_space++; ipf_nat_rule_deref(softc, &ipn); } if (nat->nat_aps != NULL) { ipf_proxy_free(softc, nat->nat_aps); nat->nat_aps = NULL; } MUTEX_DESTROY(&nat->nat_lock); softn->ipf_nat_stats.ns_active--; /* * If there's a fragment table entry too for this nat entry, then * dereference that as well. This is after nat_lock is released * because of Tru64. */ ipf_frag_natforget(softc, (void *)nat); KFREE(nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_flushtable */ /* Returns: int - number of NAT rules deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* Write Lock: ipf_nat */ /* */ /* Deletes all currently active NAT sessions. In deleting each NAT entry a */ /* log record should be emitted in ipf_nat_delete() if NAT logging is */ /* enabled. */ /* ------------------------------------------------------------------------ */ /* * nat_flushtable - clear the NAT table of all mapping entries. */ static int ipf_nat_flushtable(ipf_main_softc_t *softc, ipf_nat_softc_t *softn) { nat_t *nat; int j = 0; /* * ALL NAT mappings deleted, so lets just make the deletions * quicker. */ if (softn->ipf_nat_table[0] != NULL) bzero((char *)softn->ipf_nat_table[0], sizeof(softn->ipf_nat_table[0]) * softn->ipf_nat_table_sz); if (softn->ipf_nat_table[1] != NULL) bzero((char *)softn->ipf_nat_table[1], sizeof(softn->ipf_nat_table[1]) * softn->ipf_nat_table_sz); while ((nat = softn->ipf_nat_instances) != NULL) { ipf_nat_delete(softc, nat, NL_FLUSH); j++; } return (j); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_clearlist */ /* Returns: int - number of NAT/RDR rules deleted */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* */ /* Delete all rules in the current list of rules. There is nothing elegant */ /* about this cleanup: simply free all entries on the list of rules and */ /* clear out the tables used for hashed NAT rule lookups. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_clearlist(ipf_main_softc_t *softc, ipf_nat_softc_t *softn) { ipnat_t *n; int i = 0; if (softn->ipf_nat_map_rules != NULL) { bzero((char *)softn->ipf_nat_map_rules, sizeof(*softn->ipf_nat_map_rules) * softn->ipf_nat_maprules_sz); } if (softn->ipf_nat_rdr_rules != NULL) { bzero((char *)softn->ipf_nat_rdr_rules, sizeof(*softn->ipf_nat_rdr_rules) * softn->ipf_nat_rdrrules_sz); } while ((n = softn->ipf_nat_list) != NULL) { ipf_nat_delrule(softc, softn, n, 0); i++; } #if SOLARIS && !defined(INSTANCES) pfil_delayed_copy = 1; #endif return (i); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_delrule */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* np(I) - pointer to NAT rule to delete */ /* purge(I) - 1 == allow purge, 0 == prevent purge */ /* Locks: WRITE(ipf_nat) */ /* */ /* Preventing "purge" from occuring is allowed because when all of the NAT */ /* rules are being removed, allowing the "purge" to walk through the list */ /* of NAT sessions, possibly multiple times, would be a large performance */ /* hit, on the order of O(N^2). */ /* ------------------------------------------------------------------------ */ static void ipf_nat_delrule(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *np, int purge) { if (np->in_pnext != NULL) { *np->in_pnext = np->in_next; if (np->in_next != NULL) np->in_next->in_pnext = np->in_pnext; if (softn->ipf_nat_list_tail == &np->in_next) softn->ipf_nat_list_tail = np->in_pnext; } if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) { nat_t *next; nat_t *nat; for (next = softn->ipf_nat_instances; (nat = next) != NULL;) { next = nat->nat_next; if (nat->nat_ptr == np) ipf_nat_delete(softc, nat, NL_PURGE); } } if ((np->in_flags & IPN_DELETE) == 0) { if (np->in_redir & NAT_REDIRECT) { switch (np->in_v[0]) { case 4 : ipf_nat_delrdr(softn, np); break; #ifdef USE_INET6 case 6 : ipf_nat6_delrdr(softn, np); break; #endif } } if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) { switch (np->in_v[0]) { case 4 : ipf_nat_delmap(softn, np); break; #ifdef USE_INET6 case 6 : ipf_nat6_delmap(softn, np); break; #endif } } } np->in_flags |= IPN_DELETE; ipf_nat_rule_deref(softc, &np); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_newmap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* */ /* Given an empty NAT structure, populate it with new information about a */ /* new NAT session, as defined by the matching NAT rule. */ /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_newmap(fr_info_t *fin, nat_t *nat, natinfo_t *ni) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short st_port, dport, sport, port, sp, dp; struct in_addr in, inb; hostmap_t *hm; u_32_t flags; u_32_t st_ip; ipnat_t *np; nat_t *natl; int l; /* * If it's an outbound packet which doesn't match any existing * record, then create a new port */ l = 0; hm = NULL; np = ni->nai_np; st_ip = np->in_snip; st_port = np->in_spnext; flags = nat->nat_flags; if (flags & IPN_ICMPQUERY) { sport = fin->fin_data[1]; dport = 0; } else { sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); } /* * Do a loop until we either run out of entries to try or we find * a NAT mapping that isn't currently being used. This is done * because the change to the source is not (usually) being fixed. */ do { port = 0; in.s_addr = htonl(np->in_snip); if (l == 0) { /* * Check to see if there is an existing NAT * setup for this IP address pair. */ hm = ipf_nat_hostmap(softn, np, fin->fin_src, fin->fin_dst, in, 0); if (hm != NULL) in.s_addr = hm->hm_nsrcip.s_addr; } else if ((l == 1) && (hm != NULL)) { ipf_nat_hostmapdel(softc, &hm); } in.s_addr = ntohl(in.s_addr); nat->nat_hm = hm; if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) { if (l > 0) { NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1); DT4(ns_exhausted_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return (-1); } } if (np->in_redir == NAT_BIMAP && np->in_osrcmsk == np->in_nsrcmsk) { /* * map the address block in a 1:1 fashion */ in.s_addr = np->in_nsrcaddr; in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk; in.s_addr = ntohl(in.s_addr); } else if (np->in_redir & NAT_MAPBLK) { if ((l >= np->in_ppip) || ((l > 0) && !(flags & IPN_TCPUDP))) { NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2); DT4(ns_exhausted_2, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return (-1); } /* * map-block - Calculate destination address. */ in.s_addr = ntohl(fin->fin_saddr); in.s_addr &= ntohl(~np->in_osrcmsk); inb.s_addr = in.s_addr; in.s_addr /= np->in_ippip; in.s_addr &= ntohl(~np->in_nsrcmsk); in.s_addr += ntohl(np->in_nsrcaddr); /* * Calculate destination port. */ if ((flags & IPN_TCPUDP) && (np->in_ppip != 0)) { port = ntohs(sport) + l; port %= np->in_ppip; port += np->in_ppip * (inb.s_addr % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { i6addr_t in6; /* * 0/32 - use the interface's IP address. */ if ((l > 0) || ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, &in6, NULL) == -1) { NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1); DT4(ns_new_ifpaddr_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return (-1); } in.s_addr = ntohl(in6.in4.s_addr); } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { /* * 0/0 - use the original source address/port. */ if (l > 0) { NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3); DT4(ns_exhausted_3, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return (-1); } in.s_addr = ntohl(fin->fin_saddr); } else if ((np->in_nsrcmsk != 0xffffffff) && (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) np->in_snip++; natl = NULL; if ((flags & IPN_TCPUDP) && ((np->in_redir & NAT_MAPBLK) == 0) && (np->in_flags & IPN_AUTOPORTMAP)) { /* * "ports auto" (without map-block) */ if ((l > 0) && (l % np->in_ppip == 0)) { if ((l > np->in_ppip) && np->in_nsrcmsk != 0xffffffff) np->in_snip++; } if (np->in_ppip != 0) { port = ntohs(sport); port += (l % np->in_ppip); port %= np->in_ppip; port += np->in_ppip * (ntohl(fin->fin_saddr) % np->in_ippip); port += MAPBLK_MINPORT; port = htons(port); } } else if (((np->in_redir & NAT_MAPBLK) == 0) && (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) { /* * Standard port translation. Select next port. */ if (np->in_flags & IPN_SEQUENTIAL) { port = np->in_spnext; } else { port = ipf_random() % (np->in_spmax - np->in_spmin + 1); port += np->in_spmin; } port = htons(port); np->in_spnext++; if (np->in_spnext > np->in_spmax) { np->in_spnext = np->in_spmin; if (np->in_nsrcmsk != 0xffffffff) np->in_snip++; } } if (np->in_flags & IPN_SIPRANGE) { if (np->in_snip > ntohl(np->in_nsrcmsk)) np->in_snip = ntohl(np->in_nsrcaddr); } else { if ((np->in_nsrcmsk != 0xffffffff) && ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) > ntohl(np->in_nsrcaddr)) np->in_snip = ntohl(np->in_nsrcaddr) + 1; } if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY))) port = sport; /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. */ inb.s_addr = htonl(in.s_addr); sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[0] = fin->fin_data[1]; fin->fin_data[1] = ntohs(port); natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, fin->fin_dst, inb); fin->fin_data[0] = sp; fin->fin_data[1] = dp; /* * Has the search wrapped around and come back to the * start ? */ if ((natl != NULL) && (np->in_spnext != 0) && (st_port == np->in_spnext) && (np->in_snip != 0) && (st_ip == np->in_snip)) { NBUMPSIDED(1, ns_wrap); DT4(ns_wrap, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np); return (-1); } l++; } while (natl != NULL); /* Setup the NAT table */ nat->nat_osrcip = fin->fin_src; nat->nat_nsrcaddr = htonl(in.s_addr); nat->nat_odstip = fin->fin_dst; nat->nat_ndstip = fin->fin_dst; if (nat->nat_hm == NULL) nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, fin->fin_dst, nat->nat_nsrcip, 0); if (flags & IPN_TCPUDP) { nat->nat_osport = sport; nat->nat_nsport = port; /* sport */ nat->nat_odport = dport; nat->nat_ndport = dport; ((tcphdr_t *)fin->fin_dp)->th_sport = port; } else if (flags & IPN_ICMPQUERY) { nat->nat_oicmpid = fin->fin_data[1]; ((icmphdr_t *)fin->fin_dp)->icmp_id = port; nat->nat_nicmpid = port; } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_newrdr */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* */ /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/ /* to the new IP address for the translation. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_newrdr(fr_info_t *fin, nat_t *nat, natinfo_t *ni) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short nport, dport, sport; struct in_addr in, inb; u_short sp, dp; hostmap_t *hm; u_32_t flags; ipnat_t *np; nat_t *natl; int move; move = 1; hm = NULL; in.s_addr = 0; np = ni->nai_np; flags = nat->nat_flags; if (flags & IPN_ICMPQUERY) { dport = fin->fin_data[1]; sport = 0; } else { sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); } /* TRACE sport, dport */ /* * If the matching rule has IPN_STICKY set, then we want to have the * same rule kick in as before. Why would this happen? If you have * a collection of rdr rules with "round-robin sticky", the current * packet might match a different one to the previous connection but * we want the same destination to be used. */ if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) && ((np->in_flags & IPN_STICKY) != 0)) { hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, in, (u_32_t)dport); if (hm != NULL) { in.s_addr = ntohl(hm->hm_ndstip.s_addr); np = hm->hm_ipnat; ni->nai_np = np; move = 0; ipf_nat_hostmapdel(softc, &hm); } } /* * Otherwise, it's an inbound packet. Most likely, we don't * want to rewrite source ports and source addresses. Instead, * we want to rewrite to a fixed internal address and fixed * internal port. */ if (np->in_flags & IPN_SPLIT) { in.s_addr = np->in_dnip; inb.s_addr = htonl(in.s_addr); if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) { hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst, inb, (u_32_t)dport); if (hm != NULL) { in.s_addr = hm->hm_ndstip.s_addr; move = 0; } } if (hm == NULL || hm->hm_ref == 1) { if (np->in_ndstaddr == htonl(in.s_addr)) { np->in_dnip = ntohl(np->in_ndstmsk); move = 0; } else { np->in_dnip = ntohl(np->in_ndstaddr); } } if (hm != NULL) ipf_nat_hostmapdel(softc, &hm); } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { i6addr_t in6; /* * 0/32 - use the interface's IP address. */ if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp, &in6, NULL) == -1) { NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2); DT3(ns_new_ifpaddr_2, fr_info_t *, fin, nat_t *, nat, natinfo_t, ni); return (-1); } in.s_addr = ntohl(in6.in4.s_addr); } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) { /* * 0/0 - use the original destination address/port. */ in.s_addr = ntohl(fin->fin_daddr); } else if (np->in_redir == NAT_BIMAP && np->in_ndstmsk == np->in_odstmsk) { /* * map the address block in a 1:1 fashion */ in.s_addr = np->in_ndstaddr; in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk; in.s_addr = ntohl(in.s_addr); } else { in.s_addr = ntohl(np->in_ndstaddr); } if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0)) nport = dport; else { /* * Whilst not optimized for the case where * pmin == pmax, the gain is not significant. */ if (((np->in_flags & IPN_FIXEDDPORT) == 0) && (np->in_odport != np->in_dtop)) { nport = ntohs(dport) - np->in_odport + np->in_dpmax; nport = htons(nport); } else { nport = htons(np->in_dpnext); np->in_dpnext++; if (np->in_dpnext > np->in_dpmax) np->in_dpnext = np->in_dpmin; } } /* * When the redirect-to address is set to 0.0.0.0, just * assume a blank `forwarding' of the packet. We don't * setup any translation for this either. */ if (in.s_addr == 0) { if (nport == dport) { NBUMPSIDED(0, ns_xlate_null); return (-1); } in.s_addr = ntohl(fin->fin_daddr); } /* * Check to see if this redirect mapping already exists and if * it does, return "failure" (allowing it to be created will just * cause one or both of these "connections" to stop working.) */ inb.s_addr = htonl(in.s_addr); sp = fin->fin_data[0]; dp = fin->fin_data[1]; fin->fin_data[1] = fin->fin_data[0]; fin->fin_data[0] = ntohs(nport); natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)fin->fin_p, inb, fin->fin_src); fin->fin_data[0] = sp; fin->fin_data[1] = dp; if (natl != NULL) { DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl); NBUMPSIDE(0, ns_xlate_exists); return (-1); } inb.s_addr = htonl(in.s_addr); nat->nat_ndstaddr = htonl(in.s_addr); nat->nat_odstip = fin->fin_dst; nat->nat_nsrcip = fin->fin_src; nat->nat_osrcip = fin->fin_src; if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0)) nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src, fin->fin_dst, inb, (u_32_t)dport); if (flags & IPN_TCPUDP) { nat->nat_odport = dport; nat->nat_ndport = nport; nat->nat_osport = sport; nat->nat_nsport = sport; ((tcphdr_t *)fin->fin_dp)->th_dport = nport; } else if (flags & IPN_ICMPQUERY) { nat->nat_oicmpid = fin->fin_data[1]; ((icmphdr_t *)fin->fin_dp)->icmp_id = nport; nat->nat_nicmpid = nport; } return (move); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_add */ /* Returns: nat_t* - NULL == failure to create new NAT structure, */ /* else pointer to new NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* natsave(I) - pointer to where to store NAT struct pointer */ /* flags(I) - flags describing the current packet */ /* direction(I) - direction of packet (in/out) */ /* Write Lock: ipf_nat */ /* */ /* Attempts to create a new NAT entry. Does not actually change the packet */ /* in any way. */ /* */ /* This function is in three main parts: (1) deal with creating a new NAT */ /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with */ /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */ /* and (3) building that structure and putting it into the NAT table(s). */ /* */ /* NOTE: natsave should NOT be used to point back to an ipstate_t struct */ /* as it can result in memory being corrupted. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_add(fr_info_t *fin, ipnat_t *np, nat_t **natsave, u_int flags, int direction) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm = NULL; nat_t *nat, *natl; natstat_t *nsp; u_int nflags; natinfo_t ni; int move; nsp = &softn->ipf_nat_stats; if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) > softn->ipf_nat_table_wm_high) { softn->ipf_nat_doflush = 1; } if (nsp->ns_active >= softn->ipf_nat_table_max) { NBUMPSIDED(fin->fin_out, ns_table_max); DT2(ns_table_max, nat_stat_t *, nsp, ipf_nat_softc_t *, softn); return (NULL); } move = 1; nflags = np->in_flags & flags; nflags &= NAT_FROMRULE; ni.nai_np = np; ni.nai_dport = 0; ni.nai_sport = 0; /* Give me a new nat */ KMALLOC(nat, nat_t *); if (nat == NULL) { DT(ns_memfail); NBUMPSIDED(fin->fin_out, ns_memfail); /* * Try to automatically tune the max # of entries in the * table allowed to be less than what will cause kmem_alloc() * to fail and try to eliminate panics due to out of memory * conditions arising. */ if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) && (nsp->ns_active > 100)) { softn->ipf_nat_table_max = nsp->ns_active - 100; printf("table_max reduced to %d\n", softn->ipf_nat_table_max); } return (NULL); } if (flags & IPN_ICMPQUERY) { /* * In the ICMP query NAT code, we translate the ICMP id fields * to make them unique. This is indepedent of the ICMP type * (e.g. in the unlikely event that a host sends an echo and * an tstamp request with the same id, both packets will have * their ip address/id field changed in the same way). */ /* The icmp_id field is used by the sender to identify the * process making the icmp request. (the receiver justs * copies it back in its response). So, it closely matches * the concept of source port. We overlay sport, so we can * maximally reuse the existing code. */ ni.nai_sport = fin->fin_data[1]; ni.nai_dport = 0; } bzero((char *)nat, sizeof(*nat)); nat->nat_flags = flags; nat->nat_redir = np->in_redir; nat->nat_dir = direction; nat->nat_pr[0] = fin->fin_p; nat->nat_pr[1] = fin->fin_p; /* * Search the current table for a match and create a new mapping * if there is none found. */ if (np->in_redir & NAT_DIVERTUDP) { move = ipf_nat_newdivert(fin, nat, &ni); } else if (np->in_redir & NAT_REWRITE) { move = ipf_nat_newrewrite(fin, nat, &ni); } else if (direction == NAT_OUTBOUND) { /* * We can now arrange to call this for the same connection * because ipf_nat_new doesn't protect the code path into * this function. */ natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } move = ipf_nat_newmap(fin, nat, &ni); } else { /* * NAT_INBOUND is used for redirects rules */ natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); if (natl != NULL) { KFREE(nat); nat = natl; goto done; } move = ipf_nat_newrdr(fin, nat, &ni); } if (move == -1) goto badnat; np = ni.nai_np; nat->nat_mssclamp = np->in_mssclamp; nat->nat_me = natsave; nat->nat_fr = fin->fin_fr; nat->nat_rev = fin->fin_rev; nat->nat_ptr = np; nat->nat_dlocal = np->in_dlocal; if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) { if (ipf_proxy_new(fin, nat) == -1) { NBUMPSIDED(fin->fin_out, ns_appr_fail); DT3(ns_appr_fail, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np); goto badnat; } } nat->nat_ifps[0] = np->in_ifps[0]; if (np->in_ifps[0] != NULL) { COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]); } nat->nat_ifps[1] = np->in_ifps[1]; if (np->in_ifps[1] != NULL) { COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]); } if (ipf_nat_finalise(fin, nat) == -1) { goto badnat; } np->in_use++; if ((move == 1) && (np->in_flags & IPN_ROUNDR)) { if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) { ipf_nat_delrdr(softn, np); ipf_nat_addrdr(softn, np); } else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) { ipf_nat_delmap(softn, np); ipf_nat_addmap(softn, np); } } if (flags & SI_WILDP) nsp->ns_wilds++; nsp->ns_proto[nat->nat_pr[0]]++; goto done; badnat: DT3(ns_badnatnew, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np); NBUMPSIDE(fin->fin_out, ns_badnatnew); if ((hm = nat->nat_hm) != NULL) ipf_nat_hostmapdel(softc, &hm); KFREE(nat); nat = NULL; done: if (nat != NULL && np != NULL) np->in_hits++; if (natsave != NULL) *natsave = nat; return (nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_finalise */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* Write Lock: ipf_nat */ /* */ /* This is the tail end of constructing a new NAT entry and is the same */ /* for both IPv4 and IPv6. */ /* ------------------------------------------------------------------------ */ /*ARGSUSED*/ static int ipf_nat_finalise(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd; frentry_t *fr; u_32_t flags; #if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC) qpktinfo_t *qpi = fin->fin_qpi; #endif flags = nat->nat_flags; switch (nat->nat_pr[0]) { case IPPROTO_ICMP : sum1 = LONG_SUM(ntohs(nat->nat_oicmpid)); sum2 = LONG_SUM(ntohs(nat->nat_nicmpid)); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); break; default : sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \ ntohs(nat->nat_osport)); sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \ ntohs(nat->nat_nsport)); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \ ntohs(nat->nat_odport)); sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \ ntohs(nat->nat_ndport)); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16); break; } /* * Compute the partial checksum, just in case. * This is only ever placed into outbound packets so care needs * to be taken over which pair of addresses are used. */ if (nat->nat_dir == NAT_OUTBOUND) { sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr)); } else { sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); sum1 += LONG_SUM(ntohl(nat->nat_odstaddr)); } sum1 += nat->nat_pr[1]; nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16); sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr)); sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); CALC_SUMD(sum1, sum2, sumd); nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); sum1 = LONG_SUM(ntohl(nat->nat_odstaddr)); sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); CALC_SUMD(sum1, sum2, sumd); nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16); nat->nat_v[0] = 4; nat->nat_v[1] = 4; if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); } if ((nat->nat_flags & SI_CLONE) == 0) nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat); if (ipf_nat_insert(softc, softn, nat) == 0) { if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, nat, NL_NEW); fr = nat->nat_fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } return (0); } NBUMPSIDED(fin->fin_out, ns_unfinalised); DT2(ns_unfinalised, fr_info_t *, fin, nat_t *, nat); /* * nat_insert failed, so cleanup time... */ if (nat->nat_sync != NULL) ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync); return (-1); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_insert */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* Insert a NAT entry into the hash tables for searching and add it to the */ /* list of active NAT entries. Adjust global counters when complete. */ /* ------------------------------------------------------------------------ */ int ipf_nat_insert(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat) { u_int hv0, hv1; u_int sp, dp; ipnat_t *in; int ret; /* * Try and return an error as early as possible, so calculate the hash * entry numbers first and then proceed. */ if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) { if ((nat->nat_flags & IPN_TCPUDP) != 0) { sp = nat->nat_osport; dp = nat->nat_odport; } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { sp = 0; dp = nat->nat_oicmpid; } else { sp = 0; dp = 0; } hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff); hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff); /* * TRACE nat_osrcaddr, nat_osport, nat_odstaddr, * nat_odport, hv0 */ if ((nat->nat_flags & IPN_TCPUDP) != 0) { sp = nat->nat_nsport; dp = nat->nat_ndport; } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { sp = 0; dp = nat->nat_nicmpid; } else { sp = 0; dp = 0; } hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff); hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff); /* * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, * nat_ndport, hv1 */ } else { hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff); hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff); /* TRACE nat_osrcaddr, nat_odstaddr, hv0 */ hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff); hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff); /* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */ } nat->nat_hv[0] = hv0; nat->nat_hv[1] = hv1; MUTEX_INIT(&nat->nat_lock, "nat entry lock"); in = nat->nat_ptr; nat->nat_ref = nat->nat_me ? 2 : 1; nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4); if (nat->nat_ifnames[1][0] != '\0') { nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[1] = ipf_resolvenic(softc, nat->nat_ifnames[1], 4); } else if (in->in_ifnames[1] != -1) { char *name; name = in->in_names + in->in_ifnames[1]; if (name[1] != '\0' && name[0] != '-' && name[0] != '*') { (void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0], LIFNAMSIZ); nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0'; nat->nat_ifps[1] = nat->nat_ifps[0]; } } if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); } ret = ipf_nat_hashtab_add(softc, softn, nat); if (ret == -1) MUTEX_DESTROY(&nat->nat_lock); return (ret); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hashtab_add */ /* Returns: int - 0 == sucess, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* */ /* Handle the insertion of a NAT entry into the table/list. */ /* ------------------------------------------------------------------------ */ int ipf_nat_hashtab_add(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat) { nat_t **natp; u_int hv0; u_int hv1; if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { hv1 = nat->nat_hv[0] % softn->ipf_nat_table_sz; hv0 = nat->nat_hv[1] % softn->ipf_nat_table_sz; } else { hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz; hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz; } if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >= softn->ipf_nat_maxbucket) { DT1(ns_bucket_max_0, int, softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]); NBUMPSIDE(0, ns_bucket_max); return (-1); } if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >= softn->ipf_nat_maxbucket) { DT1(ns_bucket_max_1, int, softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]); NBUMPSIDE(1, ns_bucket_max); return (-1); } /* * The ordering of operations in the list and hash table insertion * is very important. The last operation for each task should be * to update the top of the list, after all the "nexts" have been * done so that walking the list while it is being done does not * find strange pointers. * * Global list of NAT instances */ nat->nat_next = softn->ipf_nat_instances; nat->nat_pnext = &softn->ipf_nat_instances; if (softn->ipf_nat_instances) softn->ipf_nat_instances->nat_pnext = &nat->nat_next; softn->ipf_nat_instances = nat; /* * Inbound hash table. */ natp = &softn->ipf_nat_table[0][hv0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; if (*natp) { (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; } else { NBUMPSIDE(0, ns_inuse); } *natp = nat; NBUMPSIDE(0, ns_bucketlen[hv0]); /* * Outbound hash table. */ natp = &softn->ipf_nat_table[1][hv1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; if (*natp) (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; else { NBUMPSIDE(1, ns_inuse); } *natp = nat; NBUMPSIDE(1, ns_bucketlen[hv1]); ipf_nat_setqueue(softc, softn, nat); if (nat->nat_dir & NAT_OUTBOUND) { NBUMPSIDE(1, ns_added); } else { NBUMPSIDE(0, ns_added); } softn->ipf_nat_stats.ns_active++; return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_icmperrorlookup */ /* Returns: nat_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* dir(I) - direction of packet (in/out) */ /* */ /* Check if the ICMP error message is related to an existing TCP, UDP or */ /* ICMP query nat entry. It is assumed that the packet is already of the */ /* the required length. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_icmperrorlookup(fr_info_t *fin, int dir) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; int flags = 0, type, minlen; icmphdr_t *icmp, *orgicmp; nat_stat_side_t *nside; tcphdr_t *tcp = NULL; u_short data[2]; nat_t *nat; ip_t *oip; u_int p; icmp = fin->fin_dp; type = icmp->icmp_type; nside = &softn->ipf_nat_stats.ns_side[fin->fin_out]; /* * Does it at least have the return (basic) IP header ? * Only a basic IP header (no options) should be with an ICMP error * header. Also, if it's not an error type, then return. */ if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) { ATOMIC_INCL(nside->ns_icmp_basic); return (NULL); } /* * Check packet size */ oip = (ip_t *)((char *)fin->fin_dp + 8); minlen = IP_HL(oip) << 2; if ((minlen < sizeof(ip_t)) || (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) { ATOMIC_INCL(nside->ns_icmp_size); return (NULL); } /* * Is the buffer big enough for all of it ? It's the size of the IP * header claimed in the encapsulated part which is of concern. It * may be too big to be in this buffer but not so big that it's * outside the ICMP packet, leading to TCP deref's causing problems. * This is possible because we don't know how big oip_hl is when we * do the pullup early in ipf_check() and thus can't gaurantee it is * all here now. */ #ifdef ipf_nat_KERNEL { mb_t *m; m = fin->fin_m; # if SOLARIS if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr) { ATOMIC_INCL(nside->ns_icmp_mbuf); return (NULL); } # else if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)fin->fin_ip + M_LEN(m)) { ATOMIC_INCL(nside->ns_icmp_mbuf); return (NULL); } # endif } #endif if (fin->fin_daddr != oip->ip_src.s_addr) { ATOMIC_INCL(nside->ns_icmp_address); return (NULL); } p = oip->ip_p; if (p == IPPROTO_TCP) flags = IPN_TCP; else if (p == IPPROTO_UDP) flags = IPN_UDP; else if (p == IPPROTO_ICMP) { orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); /* see if this is related to an ICMP query */ if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) { data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; fin->fin_data[0] = 0; fin->fin_data[1] = orgicmp->icmp_id; flags = IPN_ICMPERR|IPN_ICMPQUERY; /* * NOTE : dir refers to the direction of the original * ip packet. By definition the icmp error * message flows in the opposite direction. */ if (dir == NAT_INBOUND) nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, oip->ip_src); else nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, oip->ip_src); fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return (nat); } } if (flags & IPN_TCPUDP) { minlen += 8; /* + 64bits of data to get ports */ /* TRACE (fin,minlen) */ if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) { ATOMIC_INCL(nside->ns_icmp_short); return (NULL); } data[0] = fin->fin_data[0]; data[1] = fin->fin_data[1]; tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); fin->fin_data[0] = ntohs(tcp->th_dport); fin->fin_data[1] = ntohs(tcp->th_sport); if (dir == NAT_INBOUND) { nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst, oip->ip_src); } else { nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst, oip->ip_src); } fin->fin_data[0] = data[0]; fin->fin_data[1] = data[1]; return (nat); } if (dir == NAT_INBOUND) nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src); else nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src); return (nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_icmperror */ /* Returns: nat_t* - point to matching NAT structure */ /* Parameters: fin(I) - pointer to packet information */ /* nflags(I) - NAT flags for this packet */ /* dir(I) - direction of packet (in/out) */ /* */ /* Fix up an ICMP packet which is an error message for an existing NAT */ /* session. This will correct both packet header data and checksums. */ /* */ /* This should *ONLY* be used for incoming ICMP error packets to make sure */ /* a NAT'd ICMP packet gets correctly recognised. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_icmperror(fr_info_t *fin, u_int *nflags, int dir) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd, sumd2; struct in_addr a1, a2, a3, a4; int flags, dlen, odst; icmphdr_t *icmp; u_short *csump; tcphdr_t *tcp; nat_t *nat; ip_t *oip; void *dp; if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { NBUMPSIDED(fin->fin_out, ns_icmp_short); return (NULL); } /* * ipf_nat_icmperrorlookup() will return NULL for `defective' packets. */ if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) { NBUMPSIDED(fin->fin_out, ns_icmp_notfound); return (NULL); } tcp = NULL; csump = NULL; flags = 0; sumd2 = 0; *nflags = IPN_ICMPERR; icmp = fin->fin_dp; oip = (ip_t *)&icmp->icmp_ip; dp = (((char *)oip) + (IP_HL(oip) << 2)); if (oip->ip_p == IPPROTO_TCP) { tcp = (tcphdr_t *)dp; csump = (u_short *)&tcp->th_sum; flags = IPN_TCP; } else if (oip->ip_p == IPPROTO_UDP) { udphdr_t *udp; udp = (udphdr_t *)dp; tcp = (tcphdr_t *)dp; csump = (u_short *)&udp->uh_sum; flags = IPN_UDP; } else if (oip->ip_p == IPPROTO_ICMP) flags = IPN_ICMPQUERY; dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip); /* * Need to adjust ICMP header to include the real IP#'s and * port #'s. Only apply a checksum change relative to the * IP address change as it will be modified again in ipf_nat_checkout * for both address and port. Two checksum changes are * necessary for the two header address changes. Be careful * to only modify the checksum once for the port # and twice * for the IP#. */ /* * Step 1 * Fix the IP addresses in the offending IP packet. You also need * to adjust the IP header checksum of that offending IP packet. * * Normally, you would expect that the ICMP checksum of the * ICMP error message needs to be adjusted as well for the * IP address change in oip. * However, this is a NOP, because the ICMP checksum is * calculated over the complete ICMP packet, which includes the * changed oip IP addresses and oip->ip_sum. However, these * two changes cancel each other out (if the delta for * the IP address is x, then the delta for ip_sum is minus x), * so no change in the icmp_cksum is necessary. * * Inbound ICMP * ------------ * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b) * - OIP_SRC(c)=nat_newsrcip, OIP_DST(b)=nat_newdstip *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(b)=nat_olddstip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d) * - OIP_SRC(c)=nat_newsrcip, OIP_DST(d)=nat_newdstip *=> OIP_SRC(c)=nat_oldsrcip, OIP_DST(d)=nat_olddstip * * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * Outbound ICMP * ------------- * MAP rule, SRC=a,DST=b -> SRC=c,DST=b * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_olddstip, OIP_DST(a)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * RDR rule, SRC=a,DST=b -> SRC=a,DST=c * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c) * - OIP_SRC(a)=nat_newsrcip, OIP_DST(c)=nat_newdstip *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip * * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d) * - OIP_SRC(c)=nat_olddstip, OIP_DST(d)=nat_oldsrcip *=> OIP_SRC(b)=nat_newdstip, OIP_DST(a)=nat_newsrcip * * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a) * - OIP_SRC(b)=nat_newsrcip, OIP_DST(a)=nat_newdstip *=> OIP_SRC(a)=nat_oldsrcip, OIP_DST(c)=nat_olddstip */ if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) || ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) { a1.s_addr = ntohl(nat->nat_osrcaddr); a4.s_addr = ntohl(oip->ip_src.s_addr); a3.s_addr = ntohl(nat->nat_odstaddr); a2.s_addr = ntohl(oip->ip_dst.s_addr); oip->ip_src.s_addr = htonl(a1.s_addr); oip->ip_dst.s_addr = htonl(a3.s_addr); odst = 1; } else { a1.s_addr = ntohl(nat->nat_ndstaddr); a2.s_addr = ntohl(oip->ip_dst.s_addr); a3.s_addr = ntohl(nat->nat_nsrcaddr); a4.s_addr = ntohl(oip->ip_src.s_addr); oip->ip_dst.s_addr = htonl(a3.s_addr); oip->ip_src.s_addr = htonl(a1.s_addr); odst = 0; } sum1 = 0; sum2 = 0; sumd = 0; CALC_SUMD(a2.s_addr, a3.s_addr, sum1); CALC_SUMD(a4.s_addr, a1.s_addr, sum2); sumd = sum2 + sum1; if (sumd != 0) ipf_fix_datacksum(&oip->ip_sum, sumd); sumd2 = sumd; sum1 = 0; sum2 = 0; /* * Fix UDP pseudo header checksum to compensate for the * IP address change. */ if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) { u_32_t sum3, sum4, sumt; /* * Step 2 : * For offending TCP/UDP IP packets, translate the ports as * well, based on the NAT specification. Of course such * a change may be reflected in the ICMP checksum as well. * * Since the port fields are part of the TCP/UDP checksum * of the offending IP packet, you need to adjust that checksum * as well... except that the change in the port numbers should * be offset by the checksum change. However, the TCP/UDP * checksum will also need to change if there has been an * IP address change. */ if (odst == 1) { sum1 = ntohs(nat->nat_osport); sum4 = ntohs(tcp->th_sport); sum3 = ntohs(nat->nat_odport); sum2 = ntohs(tcp->th_dport); tcp->th_sport = htons(sum1); tcp->th_dport = htons(sum3); } else { sum1 = ntohs(nat->nat_ndport); sum2 = ntohs(tcp->th_dport); sum3 = ntohs(nat->nat_nsport); sum4 = ntohs(tcp->th_sport); tcp->th_dport = htons(sum3); tcp->th_sport = htons(sum1); } CALC_SUMD(sum4, sum1, sumt); sumd += sumt; CALC_SUMD(sum2, sum3, sumt); sumd += sumt; if (sumd != 0 || sumd2 != 0) { /* * At this point, sumd is the delta to apply to the * TCP/UDP header, given the changes in both the IP * address and the ports and sumd2 is the delta to * apply to the ICMP header, given the IP address * change delta that may need to be applied to the * TCP/UDP checksum instead. * * If we will both the IP and TCP/UDP checksums * then the ICMP checksum changes by the address * delta applied to the TCP/UDP checksum. If we * do not change the TCP/UDP checksum them we * apply the delta in ports to the ICMP checksum. */ if (oip->ip_p == IPPROTO_UDP) { if ((dlen >= 8) && (*csump != 0)) { ipf_fix_datacksum(csump, sumd); } else { CALC_SUMD(sum1, sum4, sumd2); CALC_SUMD(sum3, sum2, sumt); sumd2 += sumt; } } else if (oip->ip_p == IPPROTO_TCP) { if (dlen >= 18) { ipf_fix_datacksum(csump, sumd); } else { CALC_SUMD(sum1, sum4, sumd2); CALC_SUMD(sum3, sum2, sumt); sumd2 += sumt; } } if (sumd2 != 0) { sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16); ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0); } } } else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) { icmphdr_t *orgicmp; /* * XXX - what if this is bogus hl and we go off the end ? * In this case, ipf_nat_icmperrorlookup() will have * returned NULL. */ orgicmp = (icmphdr_t *)dp; if (odst == 1) { if (orgicmp->icmp_id != nat->nat_osport) { /* * Fix ICMP checksum (of the offening ICMP * query packet) to compensate the change * in the ICMP id of the offending ICMP * packet. * * Since you modify orgicmp->icmp_id with * a delta (say x) and you compensate that * in origicmp->icmp_cksum with a delta * minus x, you don't have to adjust the * overall icmp->icmp_cksum */ sum1 = ntohs(orgicmp->icmp_id); sum2 = ntohs(nat->nat_oicmpid); CALC_SUMD(sum1, sum2, sumd); orgicmp->icmp_id = nat->nat_oicmpid; ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd); } } /* nat_dir == NAT_INBOUND is impossible for icmp queries */ } return (nat); } /* * MAP-IN MAP-OUT RDR-IN RDR-OUT * osrc X == src == src X * odst X == dst == dst X * nsrc == dst X X == dst * ndst == src X X == src * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND */ /* * NB: these lookups don't lock access to the list, it assumed that it has * already been done! */ /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_inlookup */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - NAT flags for this packet */ /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* mapdst(I) - destination IP address */ /* */ /* Lookup a nat entry based on the mapped destination ip address/port and */ /* real source address/port. We use this lookup when receiving a packet, */ /* we're looking for a table entry, based on the destination address. */ /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_inlookup(fr_info_t *fin, u_int flags, u_int p, struct in_addr src , struct in_addr mapdst) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; grehdr_t *gre; ipnat_t *ipn; u_int sflags; nat_t *nat; int nflags; u_32_t dst; void *ifp; u_int hv, rhv; ifp = fin->fin_ifp; gre = NULL; dst = mapdst.s_addr; sflags = flags & NAT_TCPUDPICMP; switch (p) { case IPPROTO_TCP : case IPPROTO_UDP : sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMP : sport = 0; dport = fin->fin_data[1]; break; default : sport = 0; dport = 0; break; } if ((flags & SI_WILDP) != 0) goto find_in_wild_ports; rhv = NAT_HASH_FN(dst, dport, 0xffffffff); rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff); hv = rhv % softn->ipf_nat_table_sz; nat = softn->ipf_nat_table[1][hv]; /* TRACE dst, dport, src, sport, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; } if (nat->nat_pr[0] != p) continue; switch (nat->nat_dir) { case NAT_INBOUND : case NAT_DIVERTIN : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_osport != sport) continue; if (nat->nat_odport != dport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; case NAT_DIVERTOUT : if (nat->nat_dlocal) continue; case NAT_OUTBOUND : if (nat->nat_v[1] != 4) continue; if (nat->nat_dlocal) continue; if (nat->nat_dlocal) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_ndport != sport) continue; if (nat->nat_nsport != dport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; } if ((nat->nat_flags & IPN_TCPUDP) != 0) { ipn = nat->nat_ptr; if ((ipn != NULL) && (nat->nat_aps != NULL)) if (ipf_proxy_match(fin, nat) != 0) continue; } if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { nat->nat_ifps[0] = ifp; nat->nat_mtu[0] = GETIFMTU_4(ifp); } return (nat); } /* * So if we didn't find it but there are wildcard members in the hash * table, go back and look for them. We do this search and update here * because it is modifying the NAT table and we want to do this only * for the first packet that matches. The exception, of course, is * for "dummy" (FI_IGNORE) lookups. */ find_in_wild_ports: if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0); return (NULL); } if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0); return (NULL); } RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN(dst, 0, 0xffffffff); hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz); WRITE_ENTER(&softc->ipf_nat); nat = softn->ipf_nat_table[1][hv]; /* TRACE dst, src, hv, nat */ for (; nat; nat = nat->nat_hnext[1]) { if (nat->nat_ifps[0] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[0])) continue; } if (nat->nat_pr[0] != fin->fin_p) continue; switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) { case NAT_INBOUND : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst) continue; break; case NAT_OUTBOUND : if (nat->nat_v[1] != 4) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst) continue; break; } nflags = nat->nat_flags; if (!(nflags & (NAT_TCPUDP|SI_WILDP))) continue; if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags, NAT_INBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nflags & SI_CLONE) != 0) { nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { MUTEX_ENTER(&softn->ipf_nat_new); softn->ipf_nat_stats.ns_wilds--; MUTEX_EXIT(&softn->ipf_nat_new); } if (nat->nat_dir == NAT_INBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = sport; nat->nat_nsport = sport; } if (nat->nat_odport == 0) { nat->nat_odport = dport; nat->nat_ndport = dport; } } else if (nat->nat_dir == NAT_OUTBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = dport; nat->nat_nsport = dport; } if (nat->nat_odport == 0) { nat->nat_odport = sport; nat->nat_ndport = sport; } } if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) { nat->nat_ifps[0] = ifp; nat->nat_mtu[0] = GETIFMTU_4(ifp); } nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); ipf_nat_tabmove(softn, nat); break; } } MUTEX_DOWNGRADE(&softc->ipf_nat); if (nat == NULL) { NBUMPSIDE(0, ns_lookup_miss); } return (nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_tabmove */ /* Returns: Nil */ /* Parameters: softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* Write Lock: ipf_nat */ /* */ /* This function is only called for TCP/UDP NAT table entries where the */ /* original was placed in the table without hashing on the ports and we now */ /* want to include hashing on port numbers. */ /* ------------------------------------------------------------------------ */ static void ipf_nat_tabmove(ipf_nat_softc_t *softn, nat_t *nat) { u_int hv0, hv1, rhv0, rhv1; natstat_t *nsp; nat_t **natp; if (nat->nat_flags & SI_CLONE) return; nsp = &softn->ipf_nat_stats; /* * Remove the NAT entry from the old location */ if (nat->nat_hnext[0]) nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0]; *nat->nat_phnext[0] = nat->nat_hnext[0]; nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] % softn->ipf_nat_table_sz]--; if (nat->nat_hnext[1]) nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1]; *nat->nat_phnext[1] = nat->nat_hnext[1]; nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] % softn->ipf_nat_table_sz]--; /* * Add into the NAT table in the new position */ rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff); rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport, 0xffffffff); rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff); rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport, 0xffffffff); hv0 = rhv0 % softn->ipf_nat_table_sz; hv1 = rhv1 % softn->ipf_nat_table_sz; if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) { u_int swap; swap = hv0; hv0 = hv1; hv1 = swap; } /* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */ /* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */ nat->nat_hv[0] = rhv0; natp = &softn->ipf_nat_table[0][hv0]; if (*natp) (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; nsp->ns_side[0].ns_bucketlen[hv0]++; nat->nat_hv[1] = rhv1; natp = &softn->ipf_nat_table[1][hv1]; if (*natp) (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; nsp->ns_side[1].ns_bucketlen[hv1]++; } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_outlookup */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: fin(I) - pointer to packet information */ /* flags(I) - NAT flags for this packet */ /* p(I) - protocol for this packet */ /* src(I) - source IP address */ /* dst(I) - destination IP address */ /* rw(I) - 1 == write lock on held, 0 == read lock. */ /* */ /* Lookup a nat entry based on the source 'real' ip address/port and */ /* destination address/port. We use this lookup when sending a packet out, */ /* we're looking for a table entry, based on the source address. */ /* */ /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY. */ /* */ /* NOTE: IT IS ASSUMED THAT IS ONLY HELD WITH A READ LOCK WHEN */ /* THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags. */ /* */ /* flags -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if */ /* the packet is of said protocol */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_outlookup(fr_info_t *fin, u_int flags, u_int p, struct in_addr src , struct in_addr dst) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_short sport, dport; u_int sflags; ipnat_t *ipn; nat_t *nat; void *ifp; u_int hv; ifp = fin->fin_ifp; sflags = flags & IPN_TCPUDPICMP; switch (p) { case IPPROTO_TCP : case IPPROTO_UDP : sport = htons(fin->fin_data[0]); dport = htons(fin->fin_data[1]); break; case IPPROTO_ICMP : sport = 0; dport = fin->fin_data[1]; break; default : sport = 0; dport = 0; break; } if ((flags & SI_WILDP) != 0) goto find_out_wild_ports; hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff); hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz); nat = softn->ipf_nat_table[0][hv]; /* TRACE src, sport, dst, dport, hv, nat */ for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; } if (nat->nat_pr[1] != p) continue; switch (nat->nat_dir) { case NAT_INBOUND : case NAT_DIVERTIN : if (nat->nat_v[1] != 4) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst.s_addr) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_ndport != sport) continue; if (nat->nat_nsport != dport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; case NAT_OUTBOUND : case NAT_DIVERTOUT : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst.s_addr) continue; if ((nat->nat_flags & IPN_TCPUDP) != 0) { if (nat->nat_odport != dport) continue; if (nat->nat_osport != sport) continue; } else if (p == IPPROTO_ICMP) { if (nat->nat_osport != dport) { continue; } } break; } ipn = nat->nat_ptr; if ((ipn != NULL) && (nat->nat_aps != NULL)) if (ipf_proxy_match(fin, nat) != 0) continue; if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { nat->nat_ifps[1] = ifp; nat->nat_mtu[1] = GETIFMTU_4(ifp); } return (nat); } /* * So if we didn't find it but there are wildcard members in the hash * table, go back and look for them. We do this search and update here * because it is modifying the NAT table and we want to do this only * for the first packet that matches. The exception, of course, is * for "dummy" (FI_IGNORE) lookups. */ find_out_wild_ports: if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) { NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1); return (NULL); } if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) { NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1); return (NULL); } RWLOCK_EXIT(&softc->ipf_nat); hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff); hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz); WRITE_ENTER(&softc->ipf_nat); nat = softn->ipf_nat_table[0][hv]; for (; nat; nat = nat->nat_hnext[0]) { if (nat->nat_ifps[1] != NULL) { if ((ifp != NULL) && (ifp != nat->nat_ifps[1])) continue; } if (nat->nat_pr[1] != fin->fin_p) continue; switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)) { case NAT_INBOUND : if (nat->nat_v[1] != 4) continue; if (nat->nat_ndstaddr != src.s_addr || nat->nat_nsrcaddr != dst.s_addr) continue; break; case NAT_OUTBOUND : if (nat->nat_v[0] != 4) continue; if (nat->nat_osrcaddr != src.s_addr || nat->nat_odstaddr != dst.s_addr) continue; break; } if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP))) continue; if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags, NAT_OUTBOUND) == 1) { if ((fin->fin_flx & FI_IGNORE) != 0) break; if ((nat->nat_flags & SI_CLONE) != 0) { nat = ipf_nat_clone(fin, nat); if (nat == NULL) break; } else { MUTEX_ENTER(&softn->ipf_nat_new); softn->ipf_nat_stats.ns_wilds--; MUTEX_EXIT(&softn->ipf_nat_new); } if (nat->nat_dir == NAT_OUTBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = sport; nat->nat_nsport = sport; } if (nat->nat_odport == 0) { nat->nat_odport = dport; nat->nat_ndport = dport; } } else if (nat->nat_dir == NAT_INBOUND) { if (nat->nat_osport == 0) { nat->nat_osport = dport; nat->nat_nsport = dport; } if (nat->nat_odport == 0) { nat->nat_odport = sport; nat->nat_ndport = sport; } } if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) { nat->nat_ifps[1] = ifp; nat->nat_mtu[1] = GETIFMTU_4(ifp); } nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT); ipf_nat_tabmove(softn, nat); break; } } MUTEX_DOWNGRADE(&softc->ipf_nat); if (nat == NULL) { NBUMPSIDE(1, ns_lookup_miss); } return (nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_lookupredir */ /* Returns: nat_t* - NULL == no match, */ /* else pointer to matching NAT entry */ /* Parameters: np(I) - pointer to description of packet to find NAT table */ /* entry for. */ /* */ /* Lookup the NAT tables to search for a matching redirect */ /* The contents of natlookup_t should imitate those found in a packet that */ /* would be translated - ie a packet coming in for RDR or going out for MAP.*/ /* We can do the lookup in one of two ways, imitating an inbound or */ /* outbound packet. By default we assume outbound, unless IPN_IN is set. */ /* For IN, the fields are set as follows: */ /* nl_real* = source information */ /* nl_out* = destination information (translated) */ /* For an out packet, the fields are set like this: */ /* nl_in* = source information (untranslated) */ /* nl_out* = destination information (translated) */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_lookupredir(natlookup_t *np) { fr_info_t fi; nat_t *nat; bzero((char *)&fi, sizeof(fi)); if (np->nl_flags & IPN_IN) { fi.fin_data[0] = ntohs(np->nl_realport); fi.fin_data[1] = ntohs(np->nl_outport); } else { fi.fin_data[0] = ntohs(np->nl_inport); fi.fin_data[1] = ntohs(np->nl_outport); } if (np->nl_flags & IPN_TCP) fi.fin_p = IPPROTO_TCP; else if (np->nl_flags & IPN_UDP) fi.fin_p = IPPROTO_UDP; else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY)) fi.fin_p = IPPROTO_ICMP; /* * We can do two sorts of lookups: * - IPN_IN: we have the `real' and `out' address, look for `in'. * - default: we have the `in' and `out' address, look for `real'. */ if (np->nl_flags & IPN_IN) { if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p, np->nl_realip, np->nl_outip))) { np->nl_inip = nat->nat_odstip; np->nl_inport = nat->nat_odport; } } else { /* * If nl_inip is non null, this is a lookup based on the real * ip address. Else, we use the fake. */ if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p, np->nl_inip, np->nl_outip))) { if ((np->nl_flags & IPN_FINDFORWARD) != 0) { fr_info_t fin; bzero((char *)&fin, sizeof(fin)); fin.fin_p = nat->nat_pr[0]; fin.fin_data[0] = ntohs(nat->nat_ndport); fin.fin_data[1] = ntohs(nat->nat_nsport); if (ipf_nat_inlookup(&fin, np->nl_flags, fin.fin_p, nat->nat_ndstip, nat->nat_nsrcip) != NULL) { np->nl_flags &= ~IPN_FINDFORWARD; } } np->nl_realip = nat->nat_odstip; np->nl_realport = nat->nat_odport; } } return (nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_match */ /* Returns: int - 0 == no match, 1 == match */ /* Parameters: fin(I) - pointer to packet information */ /* np(I) - pointer to NAT rule */ /* */ /* Pull the matching of a packet against a NAT rule out of that complex */ /* loop inside ipf_nat_checkin() and lay it out properly in its own function. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_match(fr_info_t *fin, ipnat_t *np) { ipf_main_softc_t *softc = fin->fin_main_soft; frtuc_t *ft; int match; match = 0; switch (np->in_osrcatype) { case FRI_NORMAL : match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr); break; case FRI_LOOKUP : match = (*np->in_osrcfunc)(softc, np->in_osrcptr, 4, &fin->fin_saddr, fin->fin_plen); break; } match ^= ((np->in_flags & IPN_NOTSRC) != 0); if (match) return (0); match = 0; switch (np->in_odstatype) { case FRI_NORMAL : match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr); break; case FRI_LOOKUP : match = (*np->in_odstfunc)(softc, np->in_odstptr, 4, &fin->fin_daddr, fin->fin_plen); break; } match ^= ((np->in_flags & IPN_NOTDST) != 0); if (match) return (0); ft = &np->in_tuc; if (!(fin->fin_flx & FI_TCPUDP) || (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) { if (ft->ftu_scmp || ft->ftu_dcmp) return (0); return (1); } return (ipf_tcpudpchk(&fin->fin_fi, ft)); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_update */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* */ /* Updates the lifetime of a NAT table entry for non-TCP packets. Must be */ /* called with fin_rev updated - i.e. after calling ipf_nat_proto(). */ /* */ /* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to */ /* already be set. */ /* ------------------------------------------------------------------------ */ void ipf_nat_update(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *ifq, *ifq2; ipftqent_t *tqe; ipnat_t *np = nat->nat_ptr; tqe = &nat->nat_tqe; ifq = tqe->tqe_ifq; /* * We allow over-riding of NAT timeouts from NAT rules, even for * TCP, however, if it is TCP and there is no rule timeout set, * then do not update the timeout here. */ if (np != NULL) { np->in_bytes[fin->fin_rev] += fin->fin_plen; ifq2 = np->in_tqehead[fin->fin_rev]; } else { ifq2 = NULL; } if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) { (void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq, 0, 2); } else { if (ifq2 == NULL) { if (nat->nat_pr[0] == IPPROTO_UDP) ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq : &softn->ipf_nat_udptq; else if (nat->nat_pr[0] == IPPROTO_ICMP || nat->nat_pr[0] == IPPROTO_ICMPV6) ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq: &softn->ipf_nat_icmptq; else ifq2 = &softn->ipf_nat_iptq; } ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2); } } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_checkout */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check to see if an outcoming packet should be changed. ICMP packets are */ /* first checked to see if they match an existing entry (if an error), */ /* otherwise a search of the current NAT table is made. If neither results */ /* in a match then a search for a matching NAT rule is made. Create a new */ /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ int ipf_nat_checkout(fr_info_t *fin, u_32_t *passp) { ipnat_t *np = NULL, *npnext; struct ifnet *ifp, *sifp; ipf_main_softc_t *softc; ipf_nat_softc_t *softn; icmphdr_t *icmp = NULL; tcphdr_t *tcp = NULL; int rval, natfailed; u_int nflags = 0; u_32_t ipa, iph; int natadd = 1; frentry_t *fr; nat_t *nat; if (fin->fin_v == 6) { #ifdef USE_INET6 return (ipf_nat6_checkout(fin, passp)); #else return (0); #endif } softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; if (softn->ipf_nat_lock != 0) return (0); if (softn->ipf_nat_stats.ns_rules == 0 && softn->ipf_nat_instances == NULL) return (0); natfailed = 0; fr = fin->fin_fr; sifp = fin->fin_ifp; if (fr != NULL) { ifp = fr->fr_tifs[fin->fin_rev].fd_ptr; if ((ifp != NULL) && (ifp != (void *)-1)) fin->fin_ifp = ifp; } ifp = fin->fin_ifp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; case IPPROTO_ICMP : icmp = fin->fin_dp; /* * This is an incoming packet, so the destination is * the icmp_id and the source port equals 0 */ if ((fin->fin_flx & FI_ICMPQUERY) != 0) nflags = IPN_ICMPQUERY; break; default : break; } if ((nflags & IPN_TCPUDP)) tcp = fin->fin_dp; } ipa = fin->fin_saddr; READ_ENTER(&softc->ipf_nat); if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND))) /*EMPTY*/; else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst))) { nflags = nat->nat_flags; } else if (fin->fin_off == 0) { u_32_t hv, msk, nmsk = 0; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: msk = softn->ipf_nat_map_active_masks[nmsk]; iph = ipa & msk; hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz); retry_roundrobin: for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) { npnext = np->in_mnext; if ((np->in_ifps[1] && (np->in_ifps[1] != ifp))) continue; if (np->in_v[0] != 4) continue; if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { switch (ipf_nat_match(fin, np)) { case 0 : continue; case -1 : rval = -3; goto outmatchfail; case 1 : default : break; } } else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr) continue; if ((fr != NULL) && !ipf_matchtag(&np->in_tag, &fr->fr_nattag)) continue; if (np->in_plabel != -1) { if (((np->in_flags & IPN_FILTER) == 0) && (np->in_odport != fin->fin_data[1])) continue; if (ipf_proxy_ok(fin, tcp, np) == 0) continue; } if (np->in_flags & IPN_NO) { np->in_hits++; break; } MUTEX_ENTER(&softn->ipf_nat_new); /* * If we've matched a round-robin rule but it has * moved in the list since we got it, start over as * this is now no longer correct. */ if (npnext != np->in_mnext) { if ((np->in_flags & IPN_ROUNDR) != 0) { MUTEX_EXIT(&softn->ipf_nat_new); goto retry_roundrobin; } npnext = np->in_mnext; } nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat != NULL) { natfailed = 0; break; } natfailed = -2; } if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) { nmsk++; goto maskloop; } } if (nat != NULL) { rval = ipf_nat_out(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); nat->nat_bytes[1] += fin->fin_plen; nat->nat_pkts[1]++; fin->fin_pktnum = nat->nat_pkts[1]; MUTEX_EXIT(&nat->nat_lock); } } else rval = natfailed; outmatchfail: RWLOCK_EXIT(&softc->ipf_nat); switch (rval) { case -3 : /* ipf_nat_match() failure */ /* FALLTHROUGH */ case -2 : /* retry_roundrobin loop failure */ /* FALLTHROUGH */ case -1 : /* proxy failure detected by ipf_nat_out() */ if (passp != NULL) { DT2(frb_natv4out, fr_info_t *, fin, int, rval); NBUMPSIDED(1, ns_drop); *passp = FR_BLOCK; fin->fin_reason = FRB_NATV4; } fin->fin_flx |= FI_BADNAT; NBUMPSIDED(1, ns_badnat); rval = -1; /* We only return -1 on error. */ break; case 0 : NBUMPSIDE(1, ns_ignored); break; case 1 : NBUMPSIDE(1, ns_translated); break; } fin->fin_ifp = sifp; return (rval); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_out */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ /* */ /* Translate a packet coming "out" on an interface. */ /* ------------------------------------------------------------------------ */ int ipf_nat_out(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; icmphdr_t *icmp; tcphdr_t *tcp; ipnat_t *np; int skip; int i; tcp = NULL; icmp = NULL; np = nat->nat_ptr; if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL)) (void) ipf_frag_natnew(softc, fin, 0, nat); /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. * This is only done for STREAMS based IP implementations where the * checksum has already been calculated by IP. In all other cases, * IPFilter is called before the checksum needs calculating so there * is no call to modify whatever is in the header now. */ if (nflags == IPN_ICMPERR) { u_32_t s1, s2, sumd, msumd; s1 = LONG_SUM(ntohl(fin->fin_saddr)); if (nat->nat_dir == NAT_OUTBOUND) { s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr)); } else { s2 = LONG_SUM(ntohl(nat->nat_odstaddr)); } CALC_SUMD(s1, s2, sumd); msumd = sumd; s1 = LONG_SUM(ntohl(fin->fin_daddr)); if (nat->nat_dir == NAT_OUTBOUND) { s2 = LONG_SUM(ntohl(nat->nat_ndstaddr)); } else { s2 = LONG_SUM(ntohl(nat->nat_osrcaddr)); } CALC_SUMD(s1, s2, sumd); msumd += sumd; ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0); } #if !defined(_KERNEL) || SOLARIS || \ defined(BRIDGE_IPF) || defined(__FreeBSD__) else { /* * We always do this on FreeBSD because this code doesn't * exist in fastforward. */ switch (nat->nat_dir) { case NAT_OUTBOUND : ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART, &fin->fin_ip->ip_sum, nat->nat_ipsumd, 0); break; case NAT_INBOUND : ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART, &fin->fin_ip->ip_sum, nat->nat_ipsumd, 0); break; default : break; } } #endif /* * Address assignment is after the checksum modification because * we are using the address in the packet for determining the * correct checksum offset (the ICMP error could be coming from * anyone...) */ switch (nat->nat_dir) { case NAT_OUTBOUND : fin->fin_ip->ip_src = nat->nat_nsrcip; fin->fin_saddr = nat->nat_nsrcaddr; fin->fin_ip->ip_dst = nat->nat_ndstip; fin->fin_daddr = nat->nat_ndstaddr; break; case NAT_INBOUND : fin->fin_ip->ip_src = nat->nat_odstip; fin->fin_saddr = nat->nat_ndstaddr; fin->fin_ip->ip_dst = nat->nat_osrcip; fin->fin_daddr = nat->nat_nsrcaddr; break; case NAT_DIVERTIN : { mb_t *m; skip = ipf_nat_decap(fin, nat); if (skip <= 0) { NBUMPSIDED(1, ns_decap_fail); return (-1); } m = fin->fin_m; #if SOLARIS && defined(_KERNEL) m->b_rptr += skip; #else m->m_data += skip; m->m_len -= skip; # ifdef M_PKTHDR if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= skip; # endif #endif MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); MUTEX_EXIT(&nat->nat_lock); fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return (1); /* NOTREACHED */ } case NAT_DIVERTOUT : { u_32_t s1, s2, sumd; udphdr_t *uh; ip_t *ip; mb_t *m; m = M_DUP(np->in_divmp); if (m == NULL) { NBUMPSIDED(1, ns_divert_dup); return (-1); } ip = MTOD(m, ip_t *); ip_fillid(ip); s2 = ntohs(ip->ip_id); s1 = ip->ip_len; ip->ip_len = ntohs(ip->ip_len); ip->ip_len += fin->fin_plen; ip->ip_len = htons(ip->ip_len); s2 += ntohs(ip->ip_len); CALC_SUMD(s1, s2, sumd); uh = (udphdr_t *)(ip + 1); uh->uh_ulen += fin->fin_plen; uh->uh_ulen = htons(uh->uh_ulen); #if !defined(_KERNEL) || SOLARIS || \ defined(BRIDGE_IPF) || defined(__FreeBSD__) ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); #endif PREP_MB_T(fin, m); fin->fin_src = ip->ip_src; fin->fin_dst = ip->ip_dst; fin->fin_ip = ip; fin->fin_plen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + IPv4 hdr */ nflags &= ~IPN_TCPUDPICMP; break; } default : break; } if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { u_short *csump; if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; switch (nat->nat_dir) { case NAT_OUTBOUND : tcp->th_sport = nat->nat_nsport; fin->fin_data[0] = ntohs(nat->nat_nsport); tcp->th_dport = nat->nat_ndport; fin->fin_data[1] = ntohs(nat->nat_ndport); break; case NAT_INBOUND : tcp->th_sport = nat->nat_odport; fin->fin_data[0] = ntohs(nat->nat_odport); tcp->th_dport = nat->nat_osport; fin->fin_data[1] = ntohs(nat->nat_osport); break; } } if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) { icmp = fin->fin_dp; icmp->icmp_id = nat->nat_nicmpid; } csump = ipf_nat_proto(fin, nat, nflags); /* * The above comments do not hold for layer 4 (or higher) * checksums... */ if (csump != NULL) { if (nat->nat_dir == NAT_OUTBOUND) ipf_fix_outcksum(fin->fin_cksum, csump, nat->nat_sumd[0], nat->nat_sumd[1] + fin->fin_dlen); else ipf_fix_incksum(fin->fin_cksum, csump, nat->nat_sumd[0], nat->nat_sumd[1] + fin->fin_dlen); } } ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); /* ------------------------------------------------------------- */ /* A few quick notes: */ /* Following are test conditions prior to calling the */ /* ipf_proxy_check routine. */ /* */ /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ /* with a redirect rule, we attempt to match the packet's */ /* source port against in_dport, otherwise we'd compare the */ /* packet's destination. */ /* ------------------------------------------------------------- */ if ((np != NULL) && (np->in_apr != NULL)) { i = ipf_proxy_check(fin, nat); if (i == -1) { NBUMPSIDED(1, ns_ipf_proxy_fail); } } else { i = 1; } fin->fin_flx |= FI_NATED; return (i); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_checkin */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 0 == no packet translation occurred, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* passp(I) - pointer to filtering result flags */ /* */ /* Check to see if an incoming packet should be changed. ICMP packets are */ /* first checked to see if they match an existing entry (if an error), */ /* otherwise a search of the current NAT table is made. If neither results */ /* in a match then a search for a matching NAT rule is made. Create a new */ /* NAT entry if a we matched a NAT rule. Lastly, actually change the */ /* packet header(s) as required. */ /* ------------------------------------------------------------------------ */ int ipf_nat_checkin(fr_info_t *fin, u_32_t *passp) { ipf_main_softc_t *softc; ipf_nat_softc_t *softn; u_int nflags, natadd; ipnat_t *np, *npnext; int rval, natfailed; struct ifnet *ifp; struct in_addr in; icmphdr_t *icmp; tcphdr_t *tcp; u_short dport; nat_t *nat; u_32_t iph; softc = fin->fin_main_soft; softn = softc->ipf_nat_soft; if (softn->ipf_nat_lock != 0) return (0); if (softn->ipf_nat_stats.ns_rules == 0 && softn->ipf_nat_instances == NULL) return (0); tcp = NULL; icmp = NULL; dport = 0; natadd = 1; nflags = 0; natfailed = 0; ifp = fin->fin_ifp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; case IPPROTO_ICMP : icmp = fin->fin_dp; /* * This is an incoming packet, so the destination is * the icmp_id and the source port equals 0 */ if ((fin->fin_flx & FI_ICMPQUERY) != 0) { nflags = IPN_ICMPQUERY; dport = icmp->icmp_id; } break; default : break; } if ((nflags & IPN_TCPUDP)) { tcp = fin->fin_dp; dport = fin->fin_data[1]; } } in = fin->fin_dst; READ_ENTER(&softc->ipf_nat); if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) && (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND))) /*EMPTY*/; else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin))) natadd = 0; else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p, fin->fin_src, in))) { nflags = nat->nat_flags; } else if (fin->fin_off == 0) { u_32_t hv, msk, rmsk = 0; /* * If there is no current entry in the nat table for this IP#, * create one for it (if there is a matching rule). */ maskloop: msk = softn->ipf_nat_rdr_active_masks[rmsk]; iph = in.s_addr & msk; hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz); retry_roundrobin: /* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */ for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) { npnext = np->in_rnext; if (np->in_ifps[0] && (np->in_ifps[0] != ifp)) continue; if (np->in_v[0] != 4) continue; if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p)) continue; if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags)) continue; if (np->in_flags & IPN_FILTER) { switch (ipf_nat_match(fin, np)) { case 0 : continue; case -1 : rval = -3; goto inmatchfail; case 1 : default : break; } } else { if ((in.s_addr & np->in_odstmsk) != np->in_odstaddr) continue; if (np->in_odport && ((np->in_dtop < dport) || (dport < np->in_odport))) continue; } if (np->in_plabel != -1) { if (!ipf_proxy_ok(fin, tcp, np)) { continue; } } if (np->in_flags & IPN_NO) { np->in_hits++; break; } MUTEX_ENTER(&softn->ipf_nat_new); /* * If we've matched a round-robin rule but it has * moved in the list since we got it, start over as * this is now no longer correct. */ if (npnext != np->in_rnext) { if ((np->in_flags & IPN_ROUNDR) != 0) { MUTEX_EXIT(&softn->ipf_nat_new); goto retry_roundrobin; } npnext = np->in_rnext; } nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND); MUTEX_EXIT(&softn->ipf_nat_new); if (nat != NULL) { natfailed = 0; break; } natfailed = -2; } if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) { rmsk++; goto maskloop; } } if (nat != NULL) { rval = ipf_nat_in(fin, nat, natadd, nflags); if (rval == 1) { MUTEX_ENTER(&nat->nat_lock); ipf_nat_update(fin, nat); nat->nat_bytes[0] += fin->fin_plen; nat->nat_pkts[0]++; fin->fin_pktnum = nat->nat_pkts[0]; MUTEX_EXIT(&nat->nat_lock); } } else rval = natfailed; inmatchfail: RWLOCK_EXIT(&softc->ipf_nat); DT2(frb_natv4in, fr_info_t *, fin, int, rval); switch (rval) { case -3 : /* ipf_nat_match() failure */ /* FALLTHROUGH */ case -2 : /* retry_roundrobin loop failure */ /* FALLTHROUGH */ case -1 : /* proxy failure detected by ipf_nat_in() */ if (passp != NULL) { NBUMPSIDED(0, ns_drop); *passp = FR_BLOCK; fin->fin_reason = FRB_NATV4; } fin->fin_flx |= FI_BADNAT; NBUMPSIDED(0, ns_badnat); rval = -1; /* We only return -1 on error. */ break; case 0 : NBUMPSIDE(0, ns_ignored); break; case 1 : NBUMPSIDE(0, ns_translated); break; } return (rval); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_in */ /* Returns: int - -1 == packet failed NAT checks so block it, */ /* 1 == packet was successfully translated. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* natadd(I) - flag indicating if it is safe to add frag cache */ /* nflags(I) - NAT flags set for this packet */ /* Locks Held: ipf_nat(READ) */ /* */ /* Translate a packet coming "in" on an interface. */ /* ------------------------------------------------------------------------ */ int ipf_nat_in(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sumd, ipsumd, sum1, sum2; icmphdr_t *icmp; tcphdr_t *tcp; ipnat_t *np; int skip; int i; tcp = NULL; np = nat->nat_ptr; fin->fin_fr = nat->nat_fr; if (np != NULL) { if ((natadd != 0) && (fin->fin_flx & FI_FRAG)) (void) ipf_frag_natnew(softc, fin, 0, nat); /* ------------------------------------------------------------- */ /* A few quick notes: */ /* Following are test conditions prior to calling the */ /* ipf_proxy_check routine. */ /* */ /* A NULL tcp indicates a non TCP/UDP packet. When dealing */ /* with a map rule, we attempt to match the packet's */ /* source port against in_dport, otherwise we'd compare the */ /* packet's destination. */ /* ------------------------------------------------------------- */ if (np->in_apr != NULL) { i = ipf_proxy_check(fin, nat); if (i == -1) { NBUMPSIDED(0, ns_ipf_proxy_fail); return (-1); } } } ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync); ipsumd = nat->nat_ipsumd; /* * Fix up checksums, not by recalculating them, but * simply computing adjustments. * Why only do this for some platforms on inbound packets ? * Because for those that it is done, IP processing is yet to happen * and so the IPv4 header checksum has not yet been evaluated. * Perhaps it should always be done for the benefit of things like * fast forwarding (so that it doesn't need to be recomputed) but with * header checksum offloading, perhaps it is a moot point. */ switch (nat->nat_dir) { case NAT_INBOUND : if ((fin->fin_flx & FI_ICMPERR) == 0) { fin->fin_ip->ip_src = nat->nat_nsrcip; fin->fin_saddr = nat->nat_nsrcaddr; } else { sum1 = nat->nat_osrcaddr; sum2 = nat->nat_nsrcaddr; CALC_SUMD(sum1, sum2, sumd); ipsumd -= sumd; } fin->fin_ip->ip_dst = nat->nat_ndstip; fin->fin_daddr = nat->nat_ndstaddr; #if !defined(_KERNEL) || SOLARIS ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); #endif break; case NAT_OUTBOUND : if ((fin->fin_flx & FI_ICMPERR) == 0) { fin->fin_ip->ip_src = nat->nat_odstip; fin->fin_saddr = nat->nat_odstaddr; } else { sum1 = nat->nat_odstaddr; sum2 = nat->nat_ndstaddr; CALC_SUMD(sum1, sum2, sumd); ipsumd -= sumd; } fin->fin_ip->ip_dst = nat->nat_osrcip; fin->fin_daddr = nat->nat_osrcaddr; #if !defined(_KERNEL) || SOLARIS ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0); #endif break; case NAT_DIVERTIN : { udphdr_t *uh; ip_t *ip; mb_t *m; m = M_DUP(np->in_divmp); if (m == NULL) { NBUMPSIDED(0, ns_divert_dup); return (-1); } ip = MTOD(m, ip_t *); ip_fillid(ip); sum1 = ntohs(ip->ip_len); ip->ip_len = ntohs(ip->ip_len); ip->ip_len += fin->fin_plen; ip->ip_len = htons(ip->ip_len); uh = (udphdr_t *)(ip + 1); uh->uh_ulen += fin->fin_plen; uh->uh_ulen = htons(uh->uh_ulen); sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len); sum2 += ntohs(ip->ip_off) & IP_DF; CALC_SUMD(sum1, sum2, sumd); #if !defined(_KERNEL) || SOLARIS ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0); #endif PREP_MB_T(fin, m); fin->fin_ip = ip; fin->fin_plen += sizeof(ip_t) + 8; /* UDP + new IPv4 hdr */ fin->fin_dlen += sizeof(ip_t) + 8; /* UDP + old IPv4 hdr */ nflags &= ~IPN_TCPUDPICMP; break; } case NAT_DIVERTOUT : { mb_t *m; skip = ipf_nat_decap(fin, nat); if (skip <= 0) { NBUMPSIDED(0, ns_decap_fail); return (-1); } m = fin->fin_m; #if SOLARIS && defined(_KERNEL) m->b_rptr += skip; #else m->m_data += skip; m->m_len -= skip; # ifdef M_PKTHDR if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= skip; # endif #endif ipf_nat_update(fin, nat); nflags &= ~IPN_TCPUDPICMP; fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return (1); /* NOTREACHED */ } } if (nflags & IPN_TCPUDP) tcp = fin->fin_dp; if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) { u_short *csump; if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) { switch (nat->nat_dir) { case NAT_INBOUND : tcp->th_sport = nat->nat_nsport; fin->fin_data[0] = ntohs(nat->nat_nsport); tcp->th_dport = nat->nat_ndport; fin->fin_data[1] = ntohs(nat->nat_ndport); break; case NAT_OUTBOUND : tcp->th_sport = nat->nat_odport; fin->fin_data[0] = ntohs(nat->nat_odport); tcp->th_dport = nat->nat_osport; fin->fin_data[1] = ntohs(nat->nat_osport); break; } } if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) { icmp = fin->fin_dp; icmp->icmp_id = nat->nat_nicmpid; } csump = ipf_nat_proto(fin, nat, nflags); /* * The above comments do not hold for layer 4 (or higher) * checksums... */ if (csump != NULL) { if (nat->nat_dir == NAT_OUTBOUND) ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0); else ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0); } } fin->fin_flx |= FI_NATED; if (np != NULL && np->in_tag.ipt_num[0] != 0) fin->fin_nattag = &np->in_tag; return (1); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_proto */ /* Returns: u_short* - pointer to transport header checksum to update, */ /* NULL if the transport protocol is not recognised */ /* as needing a checksum update. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT structure */ /* nflags(I) - NAT flags set for this packet */ /* */ /* Return the pointer to the checksum field for each protocol so understood.*/ /* If support for making other changes to a protocol header is required, */ /* that is not strictly 'address' translation, such as clamping the MSS in */ /* TCP down to a specific value, then do it from here. */ /* ------------------------------------------------------------------------ */ u_short * ipf_nat_proto(fr_info_t *fin, nat_t *nat, u_int nflags) { icmphdr_t *icmp; u_short *csump; tcphdr_t *tcp; udphdr_t *udp; csump = NULL; if (fin->fin_out == 0) { fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND); } else { fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0); } switch (fin->fin_p) { case IPPROTO_TCP : tcp = fin->fin_dp; if ((nflags & IPN_TCP) != 0) csump = &tcp->th_sum; /* * Do a MSS CLAMPING on a SYN packet, * only deal IPv4 for now. */ if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0) ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump); break; case IPPROTO_UDP : udp = fin->fin_dp; if ((nflags & IPN_UDP) != 0) { if (udp->uh_sum != 0) csump = &udp->uh_sum; } break; case IPPROTO_ICMP : icmp = fin->fin_dp; if ((nflags & IPN_ICMPQUERY) != 0) { if (icmp->icmp_cksum != 0) csump = &icmp->icmp_cksum; } break; #ifdef USE_INET6 case IPPROTO_ICMPV6 : { struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp; icmp6 = fin->fin_dp; if ((nflags & IPN_ICMPQUERY) != 0) { if (icmp6->icmp6_cksum != 0) csump = &icmp6->icmp6_cksum; } break; } #endif } return (csump); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_expire */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Check all of the timeout queues for entries at the top which need to be */ /* expired. */ /* ------------------------------------------------------------------------ */ void ipf_nat_expire(ipf_main_softc_t *softc) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *ifq, *ifqnext; ipftqent_t *tqe, *tqn; int i; SPL_INT(s); SPL_NET(s); WRITE_ENTER(&softc->ipf_nat); for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); } } for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) { if (tqe->tqe_die > softc->ipf_ticks) break; tqn = tqe->tqe_next; ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE); } } for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) { ifqnext = ifq->ifq_next; if (((ifq->ifq_flags & IFQF_DELETE) != 0) && (ifq->ifq_ref == 0)) { ipf_freetimeoutqueue(softc, ifq); } } if (softn->ipf_nat_doflush != 0) { ipf_nat_extraflush(softc, softn, 2); softn->ipf_nat_doflush = 0; } RWLOCK_EXIT(&softc->ipf_nat); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_sync */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* ifp(I) - pointer to network interface */ /* */ /* Walk through all of the currently active NAT sessions, looking for those */ /* which need to have their translated address updated. */ /* ------------------------------------------------------------------------ */ void ipf_nat_sync(ipf_main_softc_t *softc, void *ifp) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t sum1, sum2, sumd; i6addr_t in; ipnat_t *n; nat_t *nat; void *ifp2; int idx; SPL_INT(s); if (softc->ipf_running <= 0) return; /* * Change IP addresses for NAT sessions for any protocol except TCP * since it will break the TCP connection anyway. The only rules * which will get changed are those which are "map ... -> 0/32", * where the rule specifies the address is taken from the interface. */ SPL_NET(s); WRITE_ENTER(&softc->ipf_nat); if (softc->ipf_running <= 0) { RWLOCK_EXIT(&softc->ipf_nat); return; } for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) { if ((nat->nat_flags & IPN_TCP) != 0) continue; n = nat->nat_ptr; if (n != NULL) { if (n->in_v[1] == 4) { if (n->in_redir & NAT_MAP) { if ((n->in_nsrcaddr != 0) || (n->in_nsrcmsk != 0xffffffff)) continue; } else if (n->in_redir & NAT_REDIRECT) { if ((n->in_ndstaddr != 0) || (n->in_ndstmsk != 0xffffffff)) continue; } } #ifdef USE_INET6 if (n->in_v[1] == 4) { if (n->in_redir & NAT_MAP) { if (!IP6_ISZERO(&n->in_nsrcaddr) || !IP6_ISONES(&n->in_nsrcmsk)) continue; } else if (n->in_redir & NAT_REDIRECT) { if (!IP6_ISZERO(&n->in_ndstaddr) || !IP6_ISONES(&n->in_ndstmsk)) continue; } } #endif } if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) || (ifp == nat->nat_ifps[1]))) { nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], nat->nat_v[0]); if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) { nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]); } if (nat->nat_ifnames[1][0] != '\0') { nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1], nat->nat_v[1]); } else { nat->nat_ifps[1] = nat->nat_ifps[0]; } if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) { nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]); } ifp2 = nat->nat_ifps[0]; if (ifp2 == NULL) continue; /* * Change the map-to address to be the same as the * new one. */ sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2, &in, NULL) != -1) { if (nat->nat_v[0] == 4) nat->nat_nsrcip = in.in4; } sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6); if (sum1 == sum2) continue; /* * Readjust the checksum adjustment to take into * account the new IP#. */ CALC_SUMD(sum1, sum2, sumd); /* XXX - dont change for TCP when solaris does * hardware checksumming. */ sumd += nat->nat_sumd[0]; nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); nat->nat_sumd[1] = nat->nat_sumd[0]; } } for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) { char *base = n->in_names; if ((ifp == NULL) || (n->in_ifps[0] == ifp)) n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0], n->in_v[0]); if ((ifp == NULL) || (n->in_ifps[1] == ifp)) n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1], n->in_v[1]); if (n->in_redir & NAT_REDIRECT) idx = 1; else idx = 0; if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) && (n->in_ifps[idx] != NULL && n->in_ifps[idx] != (void *)-1)) { ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 0, n->in_ifps[idx]); ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 0, n->in_ifps[idx]); ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 0, n->in_ifps[idx]); ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 0, n->in_ifps[idx]); } } RWLOCK_EXIT(&softc->ipf_nat); SPL_X(s); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_icmpquerytype */ /* Returns: int - 1 == success, 0 == failure */ /* Parameters: icmptype(I) - ICMP type number */ /* */ /* Tests to see if the ICMP type number passed is a query/response type or */ /* not. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_icmpquerytype(int icmptype) { /* * For the ICMP query NAT code, it is essential that both the query * and the reply match on the NAT rule. Because the NAT structure * does not keep track of the icmptype, and a single NAT structure * is used for all icmp types with the same src, dest and id, we * simply define the replies as queries as well. The funny thing is, * altough it seems silly to call a reply a query, this is exactly * as it is defined in the IPv4 specification */ switch (icmptype) { case ICMP_ECHOREPLY: case ICMP_ECHO: /* route advertisement/solicitation is currently unsupported: */ /* it would require rewriting the ICMP data section */ case ICMP_TSTAMP: case ICMP_TSTAMPREPLY: case ICMP_IREQ: case ICMP_IREQREPLY: case ICMP_MASKREQ: case ICMP_MASKREPLY: return (1); default: return (0); } } /* ------------------------------------------------------------------------ */ /* Function: nat_log */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to NAT structure */ /* action(I) - action related to NAT structure being performed */ /* */ /* Creates a NAT log entry. */ /* ------------------------------------------------------------------------ */ void ipf_nat_log(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, struct nat *nat, u_int action) { #ifdef IPFILTER_LOG struct ipnat *np; int rulen; struct natlog natl; void *items[1]; size_t sizes[1]; int types[1]; bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip, sizeof(natl.nl_osrcip)); bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip, sizeof(natl.nl_nsrcip)); bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip, sizeof(natl.nl_odstip)); bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip, sizeof(natl.nl_ndstip)); natl.nl_bytes[0] = nat->nat_bytes[0]; natl.nl_bytes[1] = nat->nat_bytes[1]; natl.nl_pkts[0] = nat->nat_pkts[0]; natl.nl_pkts[1] = nat->nat_pkts[1]; natl.nl_odstport = nat->nat_odport; natl.nl_osrcport = nat->nat_osport; natl.nl_nsrcport = nat->nat_nsport; natl.nl_ndstport = nat->nat_ndport; natl.nl_p[0] = nat->nat_pr[0]; natl.nl_p[1] = nat->nat_pr[1]; natl.nl_v[0] = nat->nat_v[0]; natl.nl_v[1] = nat->nat_v[1]; natl.nl_type = nat->nat_redir; natl.nl_action = action; natl.nl_rule = -1; bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0], sizeof(nat->nat_ifnames[0])); bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1], sizeof(nat->nat_ifnames[1])); if (softc->ipf_large_nat && nat->nat_ptr != NULL) { for (rulen = 0, np = softn->ipf_nat_list; np != NULL; np = np->in_next, rulen++) if (np == nat->nat_ptr) { natl.nl_rule = rulen; break; } } items[0] = &natl; sizes[0] = sizeof(natl); types[0] = 0; (void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1); #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rule_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* inp(I) - pointer to pointer to NAT rule */ /* Write Locks: ipf_nat */ /* */ /* Dropping the refernce count for a rule means that whatever held the */ /* pointer to this rule (*inp) is no longer interested in it and when the */ /* reference count drops to zero, any resources allocated for the rule can */ /* be released and the rule itself free'd. */ /* ------------------------------------------------------------------------ */ void ipf_nat_rule_deref(ipf_main_softc_t *softc, ipnat_t **inp) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipnat_t *n; n = *inp; *inp = NULL; n->in_use--; if (n->in_use > 0) return; if (n->in_apr != NULL) ipf_proxy_deref(n->in_apr); ipf_nat_rule_fini(softc, n); if (n->in_redir & NAT_REDIRECT) { if ((n->in_flags & IPN_PROXYRULE) == 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr); } } if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) { if ((n->in_flags & IPN_PROXYRULE) == 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map); } } if (n->in_tqehead[0] != NULL) { if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) { ipf_freetimeoutqueue(softc, n->in_tqehead[0]); } } if (n->in_tqehead[1] != NULL) { if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) { ipf_freetimeoutqueue(softc, n->in_tqehead[1]); } } if ((n->in_flags & IPN_PROXYRULE) == 0) { ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules); } MUTEX_DESTROY(&n->in_lock); KFREES(n, n->in_size); #if SOLARIS && !defined(INSTANCES) if (softn->ipf_nat_stats.ns_rules == 0) pfil_delayed_copy = 1; #endif } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_deref */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* natp(I) - pointer to pointer to NAT table entry */ /* */ /* Decrement the reference counter for this NAT table entry and free it if */ /* there are no more things using it. */ /* */ /* IF nat_ref == 1 when this function is called, then we have an orphan nat */ /* structure *because* it only gets called on paths _after_ nat_ref has been*/ /* incremented. If nat_ref == 1 then we shouldn't decrement it here */ /* because nat_delete() will do that and send nat_ref to -1. */ /* */ /* Holding the lock on nat_lock is required to serialise nat_delete() being */ /* called from a NAT flush ioctl with a deref happening because of a packet.*/ /* ------------------------------------------------------------------------ */ void ipf_nat_deref(ipf_main_softc_t *softc, nat_t **natp) { nat_t *nat; nat = *natp; *natp = NULL; MUTEX_ENTER(&nat->nat_lock); if (nat->nat_ref > 1) { nat->nat_ref--; ASSERT(nat->nat_ref >= 0); MUTEX_EXIT(&nat->nat_lock); return; } MUTEX_EXIT(&nat->nat_lock); WRITE_ENTER(&softc->ipf_nat); ipf_nat_delete(softc, nat, NL_EXPIRE); RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_clone */ /* Returns: ipstate_t* - NULL == cloning failed, */ /* else pointer to new state structure */ /* Parameters: fin(I) - pointer to packet information */ /* is(I) - pointer to master state structure */ /* Write Lock: ipf_nat */ /* */ /* Create a "duplcate" state table entry from the master. */ /* ------------------------------------------------------------------------ */ nat_t * ipf_nat_clone(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; frentry_t *fr; nat_t *clone; ipnat_t *np; KMALLOC(clone, nat_t *); if (clone == NULL) { NBUMPSIDED(fin->fin_out, ns_clone_nomem); return (NULL); } bcopy((char *)nat, (char *)clone, sizeof(*clone)); MUTEX_NUKE(&clone->nat_lock); clone->nat_rev = fin->fin_rev; clone->nat_aps = NULL; /* * Initialize all these so that ipf_nat_delete() doesn't cause a crash. */ clone->nat_tqe.tqe_pnext = NULL; clone->nat_tqe.tqe_next = NULL; clone->nat_tqe.tqe_ifq = NULL; clone->nat_tqe.tqe_parent = clone; clone->nat_flags &= ~SI_CLONE; clone->nat_flags |= SI_CLONED; if (clone->nat_hm) clone->nat_hm->hm_ref++; if (ipf_nat_insert(softc, softn, clone) == -1) { KFREE(clone); NBUMPSIDED(fin->fin_out, ns_insert_fail); return (NULL); } np = clone->nat_ptr; if (np != NULL) { if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, clone, NL_CLONE); np->in_use++; } fr = clone->nat_fr; if (fr != NULL) { MUTEX_ENTER(&fr->fr_lock); fr->fr_ref++; MUTEX_EXIT(&fr->fr_lock); } /* * Because the clone is created outside the normal loop of things and * TCP has special needs in terms of state, initialise the timeout * state of the new NAT from here. */ if (clone->nat_pr[0] == IPPROTO_TCP) { (void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq, clone->nat_flags, 2); } clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone); if (softn->ipf_nat_logging) ipf_nat_log(softc, softn, clone, NL_CLONE); return (clone); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_wildok */ /* Returns: int - 1 == packet's ports match wildcards */ /* 0 == packet's ports don't match wildcards */ /* Parameters: nat(I) - NAT entry */ /* sport(I) - source port */ /* dport(I) - destination port */ /* flags(I) - wildcard flags */ /* dir(I) - packet direction */ /* */ /* Use NAT entry and packet direction to determine which combination of */ /* wildcard flags should be used. */ /* ------------------------------------------------------------------------ */ int ipf_nat_wildok(nat_t *nat, int sport, int dport, int flags, int dir) { /* * When called by dir is set to * nat_inlookup NAT_INBOUND (0) * nat_outlookup NAT_OUTBOUND (1) * * We simply combine the packet's direction in dir with the original * "intended" direction of that NAT entry in nat->nat_dir to decide * which combination of wildcard flags to allow. */ switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))) { case 3: /* outbound packet / outbound entry */ if (((nat->nat_osport == sport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == dport) || (flags & SI_W_DPORT))) return (1); break; case 2: /* outbound packet / inbound entry */ if (((nat->nat_osport == dport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == sport) || (flags & SI_W_DPORT))) return (1); break; case 1: /* inbound packet / outbound entry */ if (((nat->nat_osport == dport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == sport) || (flags & SI_W_DPORT))) return (1); break; case 0: /* inbound packet / inbound entry */ if (((nat->nat_osport == sport) || (flags & SI_W_SPORT)) && ((nat->nat_odport == dport) || (flags & SI_W_DPORT))) return (1); break; default: break; } return (0); } /* ------------------------------------------------------------------------ */ /* Function: nat_mssclamp */ /* Returns: Nil */ /* Parameters: tcp(I) - pointer to TCP header */ /* maxmss(I) - value to clamp the TCP MSS to */ /* fin(I) - pointer to packet information */ /* csump(I) - pointer to TCP checksum */ /* */ /* Check for MSS option and clamp it if necessary. If found and changed, */ /* then the TCP header checksum will be updated to reflect the change in */ /* the MSS. */ /* ------------------------------------------------------------------------ */ static void ipf_nat_mssclamp(tcphdr_t *tcp, u_32_t maxmss, fr_info_t *fin, u_short *csump) { u_char *cp, *ep, opt; int hlen, advance; u_32_t mss, sumd; hlen = TCP_OFF(tcp) << 2; if (hlen > sizeof(*tcp)) { cp = (u_char *)tcp + sizeof(*tcp); ep = (u_char *)tcp + hlen; while (cp < ep) { opt = cp[0]; if (opt == TCPOPT_EOL) break; else if (opt == TCPOPT_NOP) { cp++; continue; } if (cp + 1 >= ep) break; advance = cp[1]; if ((cp + advance > ep) || (advance <= 0)) break; switch (opt) { case TCPOPT_MAXSEG: if (advance != 4) break; mss = cp[2] * 256 + cp[3]; if (mss > maxmss) { cp[2] = maxmss / 256; cp[3] = maxmss & 0xff; CALC_SUMD(mss, maxmss, sumd); ipf_fix_outcksum(0, csump, sumd, 0); } break; default: /* ignore unknown options */ break; } cp += advance; } } } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_setqueue */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I)- pointer to NAT structure */ /* Locks: ipf_nat (read or write) */ /* */ /* Put the NAT entry on its default queue entry, using rev as a helped in */ /* determining which queue it should be placed on. */ /* ------------------------------------------------------------------------ */ void ipf_nat_setqueue(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat) { ipftq_t *oifq, *nifq; int rev = nat->nat_rev; if (nat->nat_ptr != NULL) nifq = nat->nat_ptr->in_tqehead[rev]; else nifq = NULL; if (nifq == NULL) { switch (nat->nat_pr[0]) { case IPPROTO_UDP : nifq = &softn->ipf_nat_udptq; break; case IPPROTO_ICMP : nifq = &softn->ipf_nat_icmptq; break; case IPPROTO_TCP : nifq = softn->ipf_nat_tcptq + nat->nat_tqe.tqe_state[rev]; break; default : nifq = &softn->ipf_nat_iptq; break; } } oifq = nat->nat_tqe.tqe_ifq; /* * If it's currently on a timeout queue, move it from one queue to * another, else put it on the end of the newly determined queue. */ if (oifq != NULL) ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq); else ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat); return; } /* ------------------------------------------------------------------------ */ /* Function: nat_getnext */ /* Returns: int - 0 == ok, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter_t structure */ /* */ /* Fetch the next nat/ipnat structure pointer from the linked list and */ /* copy it out to the storage space pointed to by itp_data. The next item */ /* in the list to look at is put back in the ipftoken struture. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_getnext(ipf_main_softc_t *softc, ipftoken_t *t, ipfgeniter_t *itp, ipfobj_t *objp) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm, *nexthm = NULL, zerohm; ipnat_t *ipn, *nextipnat = NULL, zeroipn; nat_t *nat, *nextnat = NULL, zeronat; int error = 0; void *nnext; if (itp->igi_nitems != 1) { IPFERROR(60075); return (ENOSPC); } READ_ENTER(&softc->ipf_nat); switch (itp->igi_type) { case IPFGENITER_HOSTMAP : hm = t->ipt_data; if (hm == NULL) { nexthm = softn->ipf_hm_maplist; } else { nexthm = hm->hm_next; } if (nexthm != NULL) { ATOMIC_INC32(nexthm->hm_ref); t->ipt_data = nexthm; } else { bzero(&zerohm, sizeof(zerohm)); nexthm = &zerohm; t->ipt_data = NULL; } nnext = nexthm->hm_next; break; case IPFGENITER_IPNAT : ipn = t->ipt_data; if (ipn == NULL) { nextipnat = softn->ipf_nat_list; } else { nextipnat = ipn->in_next; } if (nextipnat != NULL) { ATOMIC_INC32(nextipnat->in_use); t->ipt_data = nextipnat; } else { bzero(&zeroipn, sizeof(zeroipn)); nextipnat = &zeroipn; t->ipt_data = NULL; } nnext = nextipnat->in_next; break; case IPFGENITER_NAT : nat = t->ipt_data; if (nat == NULL) { nextnat = softn->ipf_nat_instances; } else { nextnat = nat->nat_next; } if (nextnat != NULL) { MUTEX_ENTER(&nextnat->nat_lock); nextnat->nat_ref++; MUTEX_EXIT(&nextnat->nat_lock); t->ipt_data = nextnat; } else { bzero(&zeronat, sizeof(zeronat)); nextnat = &zeronat; t->ipt_data = NULL; } nnext = nextnat->nat_next; break; default : RWLOCK_EXIT(&softc->ipf_nat); IPFERROR(60055); return (EINVAL); } RWLOCK_EXIT(&softc->ipf_nat); objp->ipfo_ptr = itp->igi_data; switch (itp->igi_type) { case IPFGENITER_HOSTMAP : error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm)); if (error != 0) { IPFERROR(60049); error = EFAULT; } if (hm != NULL) { WRITE_ENTER(&softc->ipf_nat); ipf_nat_hostmapdel(softc, &hm); RWLOCK_EXIT(&softc->ipf_nat); } break; case IPFGENITER_IPNAT : objp->ipfo_size = nextipnat->in_size; objp->ipfo_type = IPFOBJ_IPNAT; error = ipf_outobjk(softc, objp, nextipnat); if (ipn != NULL) { WRITE_ENTER(&softc->ipf_nat); ipf_nat_rule_deref(softc, &ipn); RWLOCK_EXIT(&softc->ipf_nat); } break; case IPFGENITER_NAT : objp->ipfo_size = sizeof(nat_t); objp->ipfo_type = IPFOBJ_NAT; error = ipf_outobjk(softc, objp, nextnat); if (nat != NULL) ipf_nat_deref(softc, &nat); break; } if (nnext == NULL) ipf_token_mark_complete(t); return (error); } /* ------------------------------------------------------------------------ */ /* Function: nat_extraflush */ /* Returns: int - 0 == success, -1 == failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* which(I) - how to flush the active NAT table */ /* Write Locks: ipf_nat */ /* */ /* Flush nat tables. Three actions currently defined: */ /* which == 0 : flush all nat table entries */ /* which == 1 : flush TCP connections which have started to close but are */ /* stuck for some reason. */ /* which == 2 : flush TCP connections which have been idle for a long time, */ /* starting at > 4 days idle and working back in successive half-*/ /* days to at most 12 hours old. If this fails to free enough */ /* slots then work backwards in half hour slots to 30 minutes. */ /* If that too fails, then work backwards in 30 second intervals */ /* for the last 30 minutes to at worst 30 seconds idle. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_extraflush(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, int which) { nat_t *nat, **natp; ipftqent_t *tqn; ipftq_t *ifq; int removed; SPL_INT(s); removed = 0; SPL_NET(s); switch (which) { case 0 : softn->ipf_nat_stats.ns_flush_all++; /* * Style 0 flush removes everything... */ for (natp = &softn->ipf_nat_instances; ((nat = *natp) != NULL); ) { ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } break; case 1 : softn->ipf_nat_stats.ns_flush_closing++; /* * Since we're only interested in things that are closing, * we can start with the appropriate timeout queue. */ for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; tqn != NULL; ) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; if (nat->nat_pr[0] != IPPROTO_TCP || nat->nat_pr[1] != IPPROTO_TCP) break; ipf_nat_delete(softc, nat, NL_EXPIRE); removed++; } } /* * Also need to look through the user defined queues. */ for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) { for (tqn = ifq->ifq_head; tqn != NULL; ) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; if (nat->nat_pr[0] != IPPROTO_TCP || nat->nat_pr[1] != IPPROTO_TCP) continue; if ((nat->nat_tcpstate[0] > IPF_TCPS_ESTABLISHED) && (nat->nat_tcpstate[1] > IPF_TCPS_ESTABLISHED)) { ipf_nat_delete(softc, nat, NL_EXPIRE); removed++; } } } break; /* * Args 5-11 correspond to flushing those particular states * for TCP connections. */ case IPF_TCPS_CLOSE_WAIT : case IPF_TCPS_FIN_WAIT_1 : case IPF_TCPS_CLOSING : case IPF_TCPS_LAST_ACK : case IPF_TCPS_FIN_WAIT_2 : case IPF_TCPS_TIME_WAIT : case IPF_TCPS_CLOSED : softn->ipf_nat_stats.ns_flush_state++; tqn = softn->ipf_nat_tcptq[which].ifq_head; while (tqn != NULL) { nat = tqn->tqe_parent; tqn = tqn->tqe_next; ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } break; default : if (which < 30) break; softn->ipf_nat_stats.ns_flush_timeout++; /* * Take a large arbitrary number to mean the number of seconds * for which which consider to be the maximum value we'll allow * the expiration to be. */ which = IPF_TTLVAL(which); for (natp = &softn->ipf_nat_instances; ((nat = *natp) != NULL); ) { if (softc->ipf_ticks - nat->nat_touched > which) { ipf_nat_delete(softc, nat, NL_FLUSH); removed++; } else natp = &nat->nat_next; } break; } if (which != 2) { SPL_X(s); return (removed); } softn->ipf_nat_stats.ns_flush_queue++; /* * Asked to remove inactive entries because the table is full, try * again, 3 times, if first attempt failed with a different criteria * each time. The order tried in must be in decreasing age. * Another alternative is to implement random drop and drop N entries * at random until N have been freed up. */ if (softc->ipf_ticks - softn->ipf_nat_last_force_flush > IPF_TTLVAL(5)) { softn->ipf_nat_last_force_flush = softc->ipf_ticks; removed = ipf_queueflush(softc, ipf_nat_flush_entry, softn->ipf_nat_tcptq, softn->ipf_nat_utqe, &softn->ipf_nat_stats.ns_active, softn->ipf_nat_table_sz, softn->ipf_nat_table_wm_low); } SPL_X(s); return (removed); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_flush_entry */ /* Returns: 0 - always succeeds */ /* Parameters: softc(I) - pointer to soft context main structure */ /* entry(I) - pointer to NAT entry */ /* Write Locks: ipf_nat */ /* */ /* This function is a stepping stone between ipf_queueflush() and */ /* nat_dlete(). It is used so we can provide a uniform interface via the */ /* ipf_queueflush() function. Since the nat_delete() function returns void */ /* we translate that to mean it always succeeds in deleting something. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_flush_entry(ipf_main_softc_t *softc, void *entry) { ipf_nat_delete(softc, entry, NL_FLUSH); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_iterator */ /* Returns: int - 0 == ok, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* token(I) - pointer to ipftoken structure */ /* itp(I) - pointer to ipfgeniter_t structure */ /* obj(I) - pointer to data description structure */ /* */ /* This function acts as a handler for the SIOCGENITER ioctls that use a */ /* generic structure to iterate through a list. There are three different */ /* linked lists of NAT related information to go through: NAT rules, active */ /* NAT mappings and the NAT fragment cache. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_iterator(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp, ipfobj_t *obj) { int error; if (itp->igi_data == NULL) { IPFERROR(60052); return (EFAULT); } switch (itp->igi_type) { case IPFGENITER_HOSTMAP : case IPFGENITER_IPNAT : case IPFGENITER_NAT : error = ipf_nat_getnext(softc, token, itp, obj); break; case IPFGENITER_NATFRAG : error = ipf_frag_nat_next(softc, token, itp); break; default : IPFERROR(60053); error = EINVAL; break; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_setpending */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* nat(I) - pointer to NAT structure */ /* Locks: ipf_nat (read or write) */ /* */ /* Put the NAT entry on to the pending queue - this queue has a very short */ /* lifetime where items are put that can't be deleted straight away because */ /* of locking issues but we want to delete them ASAP, anyway. In calling */ /* this function, it is assumed that the owner (if there is one, as shown */ /* by nat_me) is no longer interested in it. */ /* ------------------------------------------------------------------------ */ void ipf_nat_setpending(ipf_main_softc_t *softc, nat_t *nat) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipftq_t *oifq; oifq = nat->nat_tqe.tqe_ifq; if (oifq != NULL) ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, &softn->ipf_nat_pending); else ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, &softn->ipf_nat_pending, nat); if (nat->nat_me != NULL) { *nat->nat_me = NULL; nat->nat_me = NULL; nat->nat_ref--; ASSERT(nat->nat_ref >= 0); } } /* ------------------------------------------------------------------------ */ /* Function: nat_newrewrite */ /* Returns: int - -1 == error, 0 == success (no move), 1 == success and */ /* allow rule to be moved if IPN_ROUNDR is set. */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* This function is responsible for setting up an active NAT session where */ /* we are changing both the source and destination parameters at the same */ /* time. The loop in here works differently to elsewhere - each iteration */ /* is responsible for changing a single parameter that can be incremented. */ /* So one pass may increase the source IP#, next source port, next dest. IP#*/ /* and the last destination port for a total of 4 iterations to try each. */ /* This is done to try and exhaustively use the translation space available.*/ /* ------------------------------------------------------------------------ */ static int ipf_nat_newrewrite(fr_info_t *fin, nat_t *nat, natinfo_t *nai) { int src_search = 1; int dst_search = 1; fr_info_t frnat; u_32_t flags; u_short swap; ipnat_t *np; nat_t *natl; int l = 0; int changed; natl = NULL; changed = -1; np = nai->nai_np; flags = nat->nat_flags; bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); nat->nat_hm = NULL; do { changed = -1; /* TRACE (l, src_search, dst_search, np) */ DT4(ipf_nat_rewrite_1, int, l, int, src_search, int, dst_search, ipnat_t *, np); if ((src_search == 0) && (np->in_spnext == 0) && (dst_search == 0) && (np->in_dpnext == 0)) { if (l > 0) return (-1); } /* * Find a new source address */ if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr, &frnat.fin_saddr) == -1) { return (-1); } if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (np->in_nsrcmsk == 0xffffffff) { src_search = 0; if (np->in_stepnext == 0) np->in_stepnext = 1; } else if (np->in_nsrcmsk != 0xffffffff) { if (np->in_stepnext == 0 && changed == -1) { np->in_snip++; np->in_stepnext++; changed = 0; } } if ((flags & IPN_TCPUDPICMP) != 0) { if (np->in_spnext != 0) frnat.fin_data[0] = np->in_spnext; /* * Standard port translation. Select next port. */ if ((flags & IPN_FIXEDSPORT) != 0) { np->in_stepnext = 2; } else if ((np->in_stepnext == 1) && (changed == -1) && (natl != NULL)) { np->in_spnext++; np->in_stepnext++; changed = 1; if (np->in_spnext > np->in_spmax) np->in_spnext = np->in_spmin; } } else { np->in_stepnext = 2; } np->in_stepnext &= 0x3; /* * Find a new destination address */ /* TRACE (fin, np, l, frnat) */ DT4(ipf_nat_rewrite_2, frinfo_t *, fin, ipnat_t *, np, int, l, frinfo_t *, &frnat); if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr, &frnat.fin_daddr) == -1) return (-1); if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (np->in_ndstmsk == 0xffffffff) { dst_search = 0; if (np->in_stepnext == 2) np->in_stepnext = 3; } else if (np->in_ndstmsk != 0xffffffff) { if ((np->in_stepnext == 2) && (changed == -1) && (natl != NULL)) { changed = 2; np->in_stepnext++; np->in_dnip++; } } if ((flags & IPN_TCPUDPICMP) != 0) { if (np->in_dpnext != 0) frnat.fin_data[1] = np->in_dpnext; /* * Standard port translation. Select next port. */ if ((flags & IPN_FIXEDDPORT) != 0) { np->in_stepnext = 0; } else if (np->in_stepnext == 3 && changed == -1) { np->in_dpnext++; np->in_stepnext++; changed = 3; if (np->in_dpnext > np->in_dpmax) np->in_dpnext = np->in_dpmin; } } else { if (np->in_stepnext == 3) np->in_stepnext = 0; } /* TRACE (frnat) */ DT1(ipf_nat_rewrite_3, frinfo_t *, &frnat); /* * Here we do a lookup of the connection as seen from * the outside. If an IP# pair already exists, try * again. So if you have A->B becomes C->B, you can * also have D->E become C->E but not D->B causing * another C->B. Also take protocol and ports into * account when determining whether a pre-existing * NAT setup will cause an external conflict where * this is appropriate. * * fin_data[] is swapped around because we are doing a * lookup of the packet is if it were moving in the opposite * direction of the one we are working with now. */ if (flags & IPN_TCPUDP) { swap = frnat.fin_data[0]; frnat.fin_data[0] = frnat.fin_data[1]; frnat.fin_data[1] = swap; } if (fin->fin_out == 1) { natl = ipf_nat_inlookup(&frnat, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)frnat.fin_p, frnat.fin_dst, frnat.fin_src); } else { natl = ipf_nat_outlookup(&frnat, flags & ~(SI_WILDP|NAT_SEARCH), (u_int)frnat.fin_p, frnat.fin_dst, frnat.fin_src); } if (flags & IPN_TCPUDP) { swap = frnat.fin_data[0]; frnat.fin_data[0] = frnat.fin_data[1]; frnat.fin_data[1] = swap; } /* TRACE natl, in_stepnext, l */ DT3(ipf_nat_rewrite_2, nat_t *, natl, ipnat_t *, np , int, l); if ((natl != NULL) && (l > 8)) /* XXX 8 is arbitrary */ return (-1); np->in_stepnext &= 0x3; l++; changed = -1; } while (natl != NULL); nat->nat_osrcip = fin->fin_src; nat->nat_odstip = fin->fin_dst; nat->nat_nsrcip = frnat.fin_src; nat->nat_ndstip = frnat.fin_dst; if ((flags & IPN_TCPUDP) != 0) { nat->nat_osport = htons(fin->fin_data[0]); nat->nat_odport = htons(fin->fin_data[1]); nat->nat_nsport = htons(frnat.fin_data[0]); nat->nat_ndport = htons(frnat.fin_data[1]); } else if ((flags & IPN_ICMPQUERY) != 0) { nat->nat_oicmpid = fin->fin_data[1]; nat->nat_nicmpid = frnat.fin_data[1]; } return (0); } /* ------------------------------------------------------------------------ */ /* Function: nat_newdivert */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT entry */ /* ni(I) - pointer to structure with misc. information needed */ /* to create new NAT entry. */ /* Write Lock: ipf_nat */ /* */ /* Create a new NAT divert session as defined by the NAT rule. This is */ /* somewhat different to other NAT session creation routines because we */ /* do not iterate through either port numbers or IP addresses, searching */ /* for a unique mapping, however, a complimentary duplicate check is made. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_newdivert(fr_info_t *fin, nat_t *nat, natinfo_t *nai) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; fr_info_t frnat; ipnat_t *np; nat_t *natl; int p; np = nai->nai_np; bcopy((char *)fin, (char *)&frnat, sizeof(*fin)); nat->nat_pr[0] = 0; nat->nat_osrcaddr = fin->fin_saddr; nat->nat_odstaddr = fin->fin_daddr; frnat.fin_saddr = htonl(np->in_snip); frnat.fin_daddr = htonl(np->in_dnip); if ((nat->nat_flags & IPN_TCPUDP) != 0) { nat->nat_osport = htons(fin->fin_data[0]); nat->nat_odport = htons(fin->fin_data[1]); } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { nat->nat_oicmpid = fin->fin_data[1]; } if (np->in_redir & NAT_DIVERTUDP) { frnat.fin_data[0] = np->in_spnext; frnat.fin_data[1] = np->in_dpnext; frnat.fin_flx |= FI_TCPUDP; p = IPPROTO_UDP; } else { frnat.fin_flx &= ~FI_TCPUDP; p = IPPROTO_IPIP; } if (fin->fin_out == 1) { natl = ipf_nat_inlookup(&frnat, 0, p, frnat.fin_dst, frnat.fin_src); } else { natl = ipf_nat_outlookup(&frnat, 0, p, frnat.fin_dst, frnat.fin_src); } if (natl != NULL) { NBUMPSIDED(fin->fin_out, ns_divert_exist); DT3(ns_divert_exist, fr_info_t *, fin, nat_t *, nat, natinfo_t, nai); return (-1); } nat->nat_nsrcaddr = frnat.fin_saddr; nat->nat_ndstaddr = frnat.fin_daddr; if ((nat->nat_flags & IPN_TCPUDP) != 0) { nat->nat_nsport = htons(frnat.fin_data[0]); nat->nat_ndport = htons(frnat.fin_data[1]); } else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) { nat->nat_nicmpid = frnat.fin_data[1]; } nat->nat_pr[fin->fin_out] = fin->fin_p; nat->nat_pr[1 - fin->fin_out] = p; if (np->in_redir & NAT_REDIRECT) nat->nat_dir = NAT_DIVERTIN; else nat->nat_dir = NAT_DIVERTOUT; return (0); } /* ------------------------------------------------------------------------ */ /* Function: nat_builddivertmp */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: softn(I) - pointer to NAT context structure */ /* np(I) - pointer to a NAT rule */ /* */ /* For divert rules, a skeleton packet representing what will be prepended */ /* to the real packet is created. Even though we don't have the full */ /* packet here, a checksum is calculated that we update later when we */ /* fill in the final details. At present a 0 checksum for UDP is being set */ /* here because it is expected that divert will be used for localhost. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_builddivertmp(ipf_nat_softc_t *softn, ipnat_t *np) { udphdr_t *uh; size_t len; ip_t *ip; if ((np->in_redir & NAT_DIVERTUDP) != 0) len = sizeof(ip_t) + sizeof(udphdr_t); else len = sizeof(ip_t); ALLOC_MB_T(np->in_divmp, len); if (np->in_divmp == NULL) { NBUMPD(ipf_nat_stats, ns_divert_build); return (-1); } /* * First, the header to get the packet diverted to the new destination */ ip = MTOD(np->in_divmp, ip_t *); IP_V_A(ip, 4); IP_HL_A(ip, 5); ip->ip_tos = 0; if ((np->in_redir & NAT_DIVERTUDP) != 0) ip->ip_p = IPPROTO_UDP; else ip->ip_p = IPPROTO_IPIP; ip->ip_ttl = 255; ip->ip_off = 0; ip->ip_sum = 0; ip->ip_len = htons(len); ip->ip_id = 0; ip->ip_src.s_addr = htonl(np->in_snip); ip->ip_dst.s_addr = htonl(np->in_dnip); ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip)); if (np->in_redir & NAT_DIVERTUDP) { uh = (udphdr_t *)(ip + 1); uh->uh_sum = 0; uh->uh_ulen = 8; uh->uh_sport = htons(np->in_spnext); uh->uh_dport = htons(np->in_dpnext); } return (0); } #define MINDECAP (sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t)) /* ------------------------------------------------------------------------ */ /* Function: nat_decap */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* This function is responsible for undoing a packet's encapsulation in the */ /* reverse of an encap/divert rule. After removing the outer encapsulation */ /* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/ /* match the "new" packet as it may still be used by IPFilter elsewhere. */ /* We use "dir" here as the basis for some of the expectations about the */ /* outer header. If we return an error, the goal is to leave the original */ /* packet information undisturbed - this falls short at the end where we'd */ /* need to back a backup copy of "fin" - expensive. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_decap(fr_info_t *fin, nat_t *nat) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; char *hdr; int hlen; int skip; mb_t *m; if ((fin->fin_flx & FI_ICMPERR) != 0) { /* * ICMP packets don't get decapsulated, instead what we need * to do is change the ICMP reply from including (in the data * portion for errors) the encapsulated packet that we sent * out to something that resembles the original packet prior * to encapsulation. This isn't done here - all we're doing * here is changing the outer address to ensure that it gets * targetted back to the correct system. */ if (nat->nat_dir & NAT_OUTBOUND) { u_32_t sum1, sum2, sumd; sum1 = ntohl(fin->fin_daddr); sum2 = ntohl(nat->nat_osrcaddr); CALC_SUMD(sum1, sum2, sumd); fin->fin_ip->ip_dst = nat->nat_osrcip; fin->fin_daddr = nat->nat_osrcaddr; #if !defined(_KERNEL) || SOLARIS ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0); #endif } return (0); } m = fin->fin_m; skip = fin->fin_hlen; switch (nat->nat_dir) { case NAT_DIVERTIN : case NAT_DIVERTOUT : if (fin->fin_plen < MINDECAP) return (-1); skip += sizeof(udphdr_t); break; case NAT_ENCAPIN : case NAT_ENCAPOUT : if (fin->fin_plen < (skip + sizeof(ip_t))) return (-1); break; default : return (-1); /* NOTREACHED */ } /* * The aim here is to keep the original packet details in "fin" for * as long as possible so that returning with an error is for the * original packet and there is little undoing work to do. */ if (M_LEN(m) < skip + sizeof(ip_t)) { if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1) return (-1); } hdr = MTOD(fin->fin_m, char *); fin->fin_ip = (ip_t *)(hdr + skip); hlen = IP_HL(fin->fin_ip) << 2; if (ipf_pr_pullup(fin, skip + hlen) == -1) { NBUMPSIDED(fin->fin_out, ns_decap_pullup); return (-1); } fin->fin_hlen = hlen; fin->fin_dlen -= skip; fin->fin_plen -= skip; fin->fin_ipoff += skip; if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) { NBUMPSIDED(fin->fin_out, ns_decap_bad); return (-1); } return (skip); } /* ------------------------------------------------------------------------ */ /* Function: nat_nextaddr */ /* Returns: int - -1 == bad input (no new address), */ /* 0 == success and dst has new address */ /* Parameters: fin(I) - pointer to packet information */ /* na(I) - how to generate new address */ /* old(I) - original address being replaced */ /* dst(O) - where to put the new address */ /* Write Lock: ipf_nat */ /* */ /* This function uses the contents of the "na" structure, in combination */ /* with "old" to produce a new address to store in "dst". Not all of the */ /* possible uses of "na" will result in a new address. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_nextaddr(fr_info_t *fin, nat_addr_t *na, u_32_t *old, u_32_t *dst) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; u_32_t amin, amax, new; i6addr_t newip; int error; new = 0; amin = na->na_addr[0].in4.s_addr; switch (na->na_atype) { case FRI_RANGE : amax = na->na_addr[1].in4.s_addr; break; case FRI_NETMASKED : case FRI_DYNAMIC : case FRI_NORMAL : /* * Compute the maximum address by adding the inverse of the * netmask to the minimum address. */ amax = ~na->na_addr[1].in4.s_addr; amax |= amin; break; case FRI_LOOKUP : break; case FRI_BROADCAST : case FRI_PEERADDR : case FRI_NETWORK : default : DT4(ns_na_atype, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); return (-1); } error = -1; if (na->na_atype == FRI_LOOKUP) { if (na->na_type == IPLT_DSTLIST) { error = ipf_dstlist_select_node(fin, na->na_ptr, dst, NULL); } else { NBUMPSIDE(fin->fin_out, ns_badnextaddr); DT4(ns_badnextaddr_1, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); } } else if (na->na_atype == IPLT_NONE) { /* * 0/0 as the new address means leave it alone. */ if (na->na_addr[0].in4.s_addr == 0 && na->na_addr[1].in4.s_addr == 0) { new = *old; /* * 0/32 means get the interface's address */ } else if (na->na_addr[0].in4.s_addr == 0 && na->na_addr[1].in4.s_addr == 0xffffffff) { if (ipf_ifpaddr(softc, 4, na->na_atype, fin->fin_ifp, &newip, NULL) == -1) { NBUMPSIDED(fin->fin_out, ns_ifpaddrfail); DT4(ns_ifpaddrfail, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); return (-1); } new = newip.in4.s_addr; } else { new = htonl(na->na_nextip); } *dst = new; error = 0; } else { NBUMPSIDE(fin->fin_out, ns_badnextaddr); DT4(ns_badnextaddr_2, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new); } return (error); } /* ------------------------------------------------------------------------ */ /* Function: nat_nextaddrinit */ /* Returns: int - 0 == success, else error number */ /* Parameters: softc(I) - pointer to soft context main structure */ /* na(I) - NAT address information for generating new addr*/ /* initial(I) - flag indicating if it is the first call for */ /* this "na" structure. */ /* ifp(I) - network interface to derive address */ /* information from. */ /* */ /* This function is expected to be called in two scenarious: when a new NAT */ /* rule is loaded into the kernel and when the list of NAT rules is sync'd */ /* up with the valid network interfaces (possibly due to them changing.) */ /* To distinguish between these, the "initial" parameter is used. If it is */ /* 1 then this indicates the rule has just been reloaded and 0 for when we */ /* are updating information. This difference is important because in */ /* instances where we are not updating address information associated with */ /* a network interface, we don't want to disturb what the "next" address to */ /* come out of ipf_nat_nextaddr() will be. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_nextaddrinit(ipf_main_softc_t *softc, char *base, nat_addr_t *na, int initial, void *ifp) { switch (na->na_atype) { case FRI_LOOKUP : if (na->na_subtype == 0) { na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT, na->na_type, na->na_num, &na->na_func); } else if (na->na_subtype == 1) { na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT, na->na_type, base + na->na_num, &na->na_func); } if (na->na_func == NULL) { IPFERROR(60060); return (ESRCH); } if (na->na_ptr == NULL) { IPFERROR(60056); return (ESRCH); } break; case FRI_DYNAMIC : case FRI_BROADCAST : case FRI_NETWORK : case FRI_NETMASKED : case FRI_PEERADDR : if (ifp != NULL) (void )ipf_ifpaddr(softc, 4, na->na_atype, ifp, &na->na_addr[0], &na->na_addr[1]); break; case FRI_SPLIT : case FRI_RANGE : if (initial) na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); break; case FRI_NONE : na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; return (0); case FRI_NORMAL : na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr; break; default : IPFERROR(60054); return (EINVAL); } if (initial && (na->na_atype == FRI_NORMAL)) { if (na->na_addr[0].in4.s_addr == 0) { if ((na->na_addr[1].in4.s_addr == 0xffffffff) || (na->na_addr[1].in4.s_addr == 0)) { return (0); } } if (na->na_addr[1].in4.s_addr == 0xffffffff) { na->na_nextip = ntohl(na->na_addr[0].in4.s_addr); } else { na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1; } } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_matchflush */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* nat(I) - pointer to current NAT session */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_nat_matchflush(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, caddr_t data) { int *array, flushed, error; nat_t *nat, *natnext; ipfobj_t obj; error = ipf_matcharray_load(softc, data, &obj, &array); if (error != 0) return (error); flushed = 0; for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) { natnext = nat->nat_next; if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) { ipf_nat_delete(softc, nat, NL_FLUSH); flushed++; } } obj.ipfo_retval = flushed; error = BCOPYOUT(&obj, data, sizeof(obj)); KFREES(array, array[0] * sizeof(*array)); return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_matcharray */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_nat_matcharray(nat_t *nat, int *array, u_long ticks) { int i, n, *x, e, p; e = 0; n = array[0]; x = array + 1; for (; n > 0; x += 3 + x[2]) { if (x[0] == IPF_EXP_END) break; e = 0; n -= x[2] + 3; if (n < 0) break; p = x[0] >> 16; if (p != 0 && p != nat->nat_pr[1]) break; switch (x[0]) { case IPF_EXP_IP_PR : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_pr[1] == x[i + 3]); } break; case IPF_EXP_IP_SRCADDR : if (nat->nat_v[0] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_osrcaddr & x[i + 4]) == x[i + 3]); } } if (nat->nat_v[1] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_nsrcaddr & x[i + 4]) == x[i + 3]); } } break; case IPF_EXP_IP_DSTADDR : if (nat->nat_v[0] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_odstaddr & x[i + 4]) == x[i + 3]); } } if (nat->nat_v[1] == 4) { for (i = 0; !e && i < x[2]; i++) { e |= ((nat->nat_ndstaddr & x[i + 4]) == x[i + 3]); } } break; case IPF_EXP_IP_ADDR : for (i = 0; !e && i < x[2]; i++) { if (nat->nat_v[0] == 4) { e |= ((nat->nat_osrcaddr & x[i + 4]) == x[i + 3]); } if (nat->nat_v[1] == 4) { e |= ((nat->nat_nsrcaddr & x[i + 4]) == x[i + 3]); } if (nat->nat_v[0] == 4) { e |= ((nat->nat_odstaddr & x[i + 4]) == x[i + 3]); } if (nat->nat_v[1] == 4) { e |= ((nat->nat_ndstaddr & x[i + 4]) == x[i + 3]); } } break; #ifdef USE_INET6 case IPF_EXP_IP6_SRCADDR : if (nat->nat_v[0] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_osrc6, x + i + 7, x + i + 3); } } if (nat->nat_v[1] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_nsrc6, x + i + 7, x + i + 3); } } break; case IPF_EXP_IP6_DSTADDR : if (nat->nat_v[0] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_odst6, x + i + 7, x + i + 3); } } if (nat->nat_v[1] == 6) { for (i = 0; !e && i < x[3]; i++) { e |= IP6_MASKEQ(&nat->nat_ndst6, x + i + 7, x + i + 3); } } break; case IPF_EXP_IP6_ADDR : for (i = 0; !e && i < x[3]; i++) { if (nat->nat_v[0] == 6) { e |= IP6_MASKEQ(&nat->nat_osrc6, x + i + 7, x + i + 3); } if (nat->nat_v[0] == 6) { e |= IP6_MASKEQ(&nat->nat_odst6, x + i + 7, x + i + 3); } if (nat->nat_v[1] == 6) { e |= IP6_MASKEQ(&nat->nat_nsrc6, x + i + 7, x + i + 3); } if (nat->nat_v[1] == 6) { e |= IP6_MASKEQ(&nat->nat_ndst6, x + i + 7, x + i + 3); } } break; #endif case IPF_EXP_UDP_PORT : case IPF_EXP_TCP_PORT : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_nsport == x[i + 3]) || (nat->nat_ndport == x[i + 3]); } break; case IPF_EXP_UDP_SPORT : case IPF_EXP_TCP_SPORT : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_nsport == x[i + 3]); } break; case IPF_EXP_UDP_DPORT : case IPF_EXP_TCP_DPORT : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_ndport == x[i + 3]); } break; case IPF_EXP_TCP_STATE : for (i = 0; !e && i < x[2]; i++) { e |= (nat->nat_tcpstate[0] == x[i + 3]) || (nat->nat_tcpstate[1] == x[i + 3]); } break; case IPF_EXP_IDLE_GT : e |= (ticks - nat->nat_touched > x[3]); break; } e ^= x[1]; if (!e) break; } return (e); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_gettable */ /* Returns: int - 0 = success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* data(I) - pointer to ioctl data */ /* */ /* This function handles ioctl requests for tables of nat information. */ /* At present the only table it deals with is the hash bucket statistics. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_gettable(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, char *data) { ipftable_t table; int error; error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE); if (error != 0) return (error); switch (table.ita_type) { case IPFTABLE_BUCKETS_NATIN : error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, table.ita_table, softn->ipf_nat_table_sz * sizeof(u_int)); break; case IPFTABLE_BUCKETS_NATOUT : error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, table.ita_table, softn->ipf_nat_table_sz * sizeof(u_int)); break; default : IPFERROR(60058); return (EINVAL); } if (error != 0) { IPFERROR(60059); error = EFAULT; } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_settimeout */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* Apply the timeout change to the NAT timeout queues. */ /* ------------------------------------------------------------------------ */ int ipf_nat_settimeout(struct ipf_main_softc_s *softc, ipftuneable_t *t, ipftuneval_t *p) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; if (!strncmp(t->ipft_name, "tcp_", 4)) return (ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq)); if (!strcmp(t->ipft_name, "udp_timeout")) { ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "udp_ack_timeout")) { ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "icmp_timeout")) { ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) { ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int); } else if (!strcmp(t->ipft_name, "ip_timeout")) { ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int); } else { IPFERROR(60062); return (ESRCH); } return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rehash */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* To change the size of the basic NAT table, we need to first allocate the */ /* new tables (lest it fails and we've got nowhere to store all of the NAT */ /* sessions currently active) and then walk through the entire list and */ /* insert them into the table. There are two tables here: an inbound one */ /* and an outbound one. Each NAT entry goes into each table once. */ /* ------------------------------------------------------------------------ */ int ipf_nat_rehash(ipf_main_softc_t *softc, ipftuneable_t *t, ipftuneval_t *p) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; nat_t **newtab[2], *nat, **natp; u_int *bucketlens[2]; u_int maxbucket; u_int newsize; int error; u_int hv; int i; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == softn->ipf_nat_table_sz) return (0); newtab[0] = NULL; newtab[1] = NULL; bucketlens[0] = NULL; bucketlens[1] = NULL; /* * 4 tables depend on the NAT table size: the inbound looking table, * the outbound lookup table and the hash chain length for each. */ KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *)); if (newtab[0] == NULL) { error = 60063; goto badrehash; } KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *)); if (newtab[1] == NULL) { error = 60064; goto badrehash; } KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int)); if (bucketlens[0] == NULL) { error = 60065; goto badrehash; } KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int)); if (bucketlens[1] == NULL) { error = 60066; goto badrehash; } /* * Recalculate the maximum length based on the new size. */ for (maxbucket = 0, i = newsize; i > 0; i >>= 1) maxbucket++; maxbucket *= 2; bzero((char *)newtab[0], newsize * sizeof(nat_t *)); bzero((char *)newtab[1], newsize * sizeof(nat_t *)); bzero((char *)bucketlens[0], newsize * sizeof(u_int)); bzero((char *)bucketlens[1], newsize * sizeof(u_int)); WRITE_ENTER(&softc->ipf_nat); if (softn->ipf_nat_table[0] != NULL) { KFREES(softn->ipf_nat_table[0], softn->ipf_nat_table_sz * sizeof(*softn->ipf_nat_table[0])); } softn->ipf_nat_table[0] = newtab[0]; if (softn->ipf_nat_table[1] != NULL) { KFREES(softn->ipf_nat_table[1], softn->ipf_nat_table_sz * sizeof(*softn->ipf_nat_table[1])); } softn->ipf_nat_table[1] = newtab[1]; if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0]; if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1]; #ifdef USE_INET6 if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0]; if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) { KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen, softn->ipf_nat_table_sz * sizeof(u_int)); } softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1]; #endif softn->ipf_nat_maxbucket = maxbucket; softn->ipf_nat_table_sz = newsize; /* * Walk through the entire list of NAT table entries and put them * in the new NAT table, somewhere. Because we have a new table, * we need to restart the counter of how many chains are in use. */ softn->ipf_nat_stats.ns_side[0].ns_inuse = 0; softn->ipf_nat_stats.ns_side[1].ns_inuse = 0; #ifdef USE_INET6 softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0; softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0; #endif for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) { nat->nat_hnext[0] = NULL; nat->nat_phnext[0] = NULL; hv = nat->nat_hv[0] % softn->ipf_nat_table_sz; natp = &softn->ipf_nat_table[0][hv]; if (*natp) { (*natp)->nat_phnext[0] = &nat->nat_hnext[0]; } else { NBUMPSIDE(0, ns_inuse); } nat->nat_phnext[0] = natp; nat->nat_hnext[0] = *natp; *natp = nat; NBUMPSIDE(0, ns_bucketlen[hv]); nat->nat_hnext[1] = NULL; nat->nat_phnext[1] = NULL; hv = nat->nat_hv[1] % softn->ipf_nat_table_sz; natp = &softn->ipf_nat_table[1][hv]; if (*natp) { (*natp)->nat_phnext[1] = &nat->nat_hnext[1]; } else { NBUMPSIDE(1, ns_inuse); } nat->nat_phnext[1] = natp; nat->nat_hnext[1] = *natp; *natp = nat; NBUMPSIDE(1, ns_bucketlen[hv]); } RWLOCK_EXIT(&softc->ipf_nat); return (0); badrehash: if (bucketlens[1] != NULL) { KFREES(bucketlens[0], newsize * sizeof(u_int)); } if (bucketlens[0] != NULL) { KFREES(bucketlens[0], newsize * sizeof(u_int)); } if (newtab[0] != NULL) { KFREES(newtab[0], newsize * sizeof(nat_t *)); } if (newtab[1] != NULL) { KFREES(newtab[1], newsize * sizeof(nat_t *)); } IPFERROR(error); return (ENOMEM); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rehash_rules */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* All of the NAT rules hang off of a hash table that is searched with a */ /* hash on address after the netmask is applied. There is a different table*/ /* for both inbound rules (rdr) and outbound (map.) The resizing will only */ /* affect one of these two tables. */ /* ------------------------------------------------------------------------ */ int ipf_nat_rehash_rules(ipf_main_softc_t *softc, ipftuneable_t *t, ipftuneval_t *p) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; ipnat_t **newtab, *np, ***old, **npp; u_int newsize; u_int mask; u_int hv; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == *t->ipft_pint) return (0); /* * All inbound rules have the NAT_REDIRECT bit set in in_redir and * all outbound rules have either NAT_MAP or MAT_MAPBLK set. * This if statement allows for some more generic code to be below, * rather than two huge gobs of code that almost do the same thing. */ if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) { old = &softn->ipf_nat_rdr_rules; mask = NAT_REDIRECT; } else { old = &softn->ipf_nat_map_rules; mask = NAT_MAP|NAT_MAPBLK; } KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *)); if (newtab == NULL) { IPFERROR(60067); return (ENOMEM); } bzero((char *)newtab, newsize * sizeof(ipnat_t *)); WRITE_ENTER(&softc->ipf_nat); if (*old != NULL) { KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **)); } *old = newtab; *t->ipft_pint = newsize; for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) { if ((np->in_redir & mask) == 0) continue; if (np->in_redir & NAT_REDIRECT) { np->in_rnext = NULL; hv = np->in_hv[0] % newsize; for (npp = newtab + hv; *npp != NULL; ) npp = &(*npp)->in_rnext; np->in_prnext = npp; *npp = np; } if (np->in_redir & NAT_MAP) { np->in_mnext = NULL; hv = np->in_hv[1] % newsize; for (npp = newtab + hv; *npp != NULL; ) npp = &(*npp)->in_mnext; np->in_pmnext = npp; *npp = np; } } RWLOCK_EXIT(&softc->ipf_nat); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_hostmap_rehash */ /* Returns: int - 0 = success, else failure */ /* Parameters: softc(I) - pointer to soft context main structure */ /* t(I) - pointer to tunable */ /* p(I) - pointer to new tuning data */ /* */ /* Allocate and populate a new hash table that will contain a reference to */ /* all of the active IP# translations currently in place. */ /* ------------------------------------------------------------------------ */ int ipf_nat_hostmap_rehash(ipf_main_softc_t *softc, ipftuneable_t *t, ipftuneval_t *p) { ipf_nat_softc_t *softn = softc->ipf_nat_soft; hostmap_t *hm, **newtab; u_int newsize; u_int hv; newsize = p->ipftu_int; /* * In case there is nothing to do... */ if (newsize == *t->ipft_pint) return (0); KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *)); if (newtab == NULL) { IPFERROR(60068); return (ENOMEM); } bzero((char *)newtab, newsize * sizeof(hostmap_t *)); WRITE_ENTER(&softc->ipf_nat); if (softn->ipf_hm_maptable != NULL) { KFREES(softn->ipf_hm_maptable, softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *)); } softn->ipf_hm_maptable = newtab; softn->ipf_nat_hostmap_sz = newsize; for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) { hv = hm->hm_hv % softn->ipf_nat_hostmap_sz; hm->hm_hnext = softn->ipf_hm_maptable[hv]; hm->hm_phnext = softn->ipf_hm_maptable + hv; if (softn->ipf_hm_maptable[hv] != NULL) softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext; softn->ipf_hm_maptable[hv] = hm; } RWLOCK_EXIT(&softc->ipf_nat); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_add_tq */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* ------------------------------------------------------------------------ */ ipftq_t * ipf_nat_add_tq(ipf_main_softc_t *softc, int ttl) { ipf_nat_softc_t *softs = softc->ipf_nat_soft; return (ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl)); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_uncreate */ /* Returns: Nil */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* This function is used to remove a NAT entry from the NAT table when we */ /* decide that the create was actually in error. It is thus assumed that */ /* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */ /* with the translated packet (not the original), we have to reverse the */ /* lookup. Although doing the lookup is expensive (relatively speaking), it */ /* is not anticipated that this will be a frequent occurance for normal */ /* traffic patterns. */ /* ------------------------------------------------------------------------ */ void ipf_nat_uncreate(fr_info_t *fin) { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_nat_softc_t *softn = softc->ipf_nat_soft; int nflags; nat_t *nat; switch (fin->fin_p) { case IPPROTO_TCP : nflags = IPN_TCP; break; case IPPROTO_UDP : nflags = IPN_UDP; break; default : nflags = 0; break; } WRITE_ENTER(&softc->ipf_nat); if (fin->fin_out == 0) { nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_dst, fin->fin_src); } else { nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p, fin->fin_src, fin->fin_dst); } if (nat != NULL) { NBUMPSIDE(fin->fin_out, ns_uncreate[0]); ipf_nat_delete(softc, nat, NL_DESTROY); } else { NBUMPSIDE(fin->fin_out, ns_uncreate[1]); } RWLOCK_EXIT(&softc->ipf_nat); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_cmp_rules */ /* Returns: int - 0 == success, else rules do not match. */ /* Parameters: n1(I) - first rule to compare */ /* n2(I) - first rule to compare */ /* */ /* Compare two rules using pointers to each rule. A straight bcmp will not */ /* work as some fields (such as in_dst, in_pkts) actually do change once */ /* the rule has been loaded into the kernel. Whilst this function returns */ /* various non-zero returns, they're strictly to aid in debugging. Use of */ /* this function should simply care if the result is zero or not. */ /* ------------------------------------------------------------------------ */ static int ipf_nat_cmp_rules(ipnat_t *n1, ipnat_t *n2) { if (n1->in_size != n2->in_size) return (1); if (bcmp((char *)&n1->in_v, (char *)&n2->in_v, offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0) return (2); if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc, n1->in_size - offsetof(ipnat_t, in_tuc)) != 0) return (3); if (n1->in_ndst.na_atype != n2->in_ndst.na_atype) return (5); if (n1->in_ndst.na_function != n2->in_ndst.na_function) return (6); if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr, sizeof(n1->in_ndst.na_addr))) return (7); if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype) return (8); if (n1->in_nsrc.na_function != n2->in_nsrc.na_function) return (9); if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr, sizeof(n1->in_nsrc.na_addr))) return (10); if (n1->in_odst.na_atype != n2->in_odst.na_atype) return (11); if (n1->in_odst.na_function != n2->in_odst.na_function) return (12); if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr, sizeof(n1->in_odst.na_addr))) return (13); if (n1->in_osrc.na_atype != n2->in_osrc.na_atype) return (14); if (n1->in_osrc.na_function != n2->in_osrc.na_function) return (15); if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr, sizeof(n1->in_osrc.na_addr))) return (16); return (0); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rule_init */ /* Returns: int - 0 == success, else rules do not match. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* softn(I) - pointer to NAT context structure */ /* n(I) - first rule to compare */ /* */ /* ------------------------------------------------------------------------ */ static int ipf_nat_rule_init(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n) { int error = 0; if ((n->in_flags & IPN_SIPRANGE) != 0) n->in_nsrcatype = FRI_RANGE; if ((n->in_flags & IPN_DIPRANGE) != 0) n->in_ndstatype = FRI_RANGE; if ((n->in_flags & IPN_SPLIT) != 0) n->in_ndstatype = FRI_SPLIT; if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0) n->in_spnext = n->in_spmin; if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) { n->in_dpnext = n->in_dpmin; } else if (n->in_redir == NAT_REDIRECT) { n->in_dpnext = n->in_dpmin; } n->in_stepnext = 0; switch (n->in_v[0]) { case 4 : error = ipf_nat_ruleaddrinit(softc, softn, n); if (error != 0) return (error); break; #ifdef USE_INET6 case 6 : error = ipf_nat6_ruleaddrinit(softc, softn, n); if (error != 0) return (error); break; #endif default : break; } if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) { /* * Prerecord whether or not the destination of the divert * is local or not to the interface the packet is going * to be sent out. */ n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1], n->in_ifps[1], &n->in_ndstip6); } return (error); } /* ------------------------------------------------------------------------ */ /* Function: ipf_nat_rule_fini */ /* Returns: int - 0 == success, else rules do not match. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* n(I) - rule to work on */ /* */ /* This function is used to release any objects that were referenced during */ /* the rule initialisation. This is useful both when free'ing the rule and */ /* when handling ioctls that need to initialise these fields but not */ /* actually use them after the ioctl processing has finished. */ /* ------------------------------------------------------------------------ */ static void ipf_nat_rule_fini(ipf_main_softc_t *softc, ipnat_t *n) { if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL) ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr); if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL) ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr); if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL) ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr); if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL) ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr); if (n->in_divmp != NULL) FREE_MB_T(n->in_divmp); } diff --git a/usr.bin/locate/locate/locate.c b/usr.bin/locate/locate/locate.c index 31c74d721c74..d1597c277d40 100644 --- a/usr.bin/locate/locate/locate.c +++ b/usr.bin/locate/locate/locate.c @@ -1,352 +1,350 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995-2022 Wolfram Schneider * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * James A. Woods. * * 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 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1995-1996 Wolfram Schneider, Berlin.\n\ @(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)locate.c 8.1 (Berkeley) 6/6/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Ref: Usenix ;login:, Vol 8, No 1, February/March, 1983, p. 8. * * Locate scans a file list for the full pathname of a file given only part * of the name. The list has been processed with with "front-compression" * and bigram coding. Front compression reduces space by a factor of 4-5, * bigram coding by a further 20-25%. * * The codes are: * * 0-28 likeliest differential counts + offset to make nonnegative * 30 switch code for out-of-range count to follow in next word * 31 an 8 bit char followed * 128-255 bigram codes (128 most common, as determined by 'updatedb') * 32-127 single character (printable) ascii residue (ie, literal) * * A novel two-tiered string search technique is employed: * * First, a metacharacter-free subpattern and partial pathname is matched * BACKWARDS to avoid full expansion of the pathname list. The time savings * is 40-50% over forward matching, which cannot efficiently handle * overlapped search patterns and compressed path residue. * * Then, the actual shell glob-style regular expression (if in this form) is * matched against the candidate pathnames using the slower routines provided * in the standard 'find'. */ #include #include #include #include #include #include #include #include #include #ifdef MMAP # include # include # include # include #endif #include "locate.h" #include "pathnames.h" int f_mmap; /* use mmap */ int f_icase; /* ignore case */ int f_stdin; /* read database from stdin */ int f_statistic; /* print statistic */ int f_silent; /* suppress output, show only count of matches */ long f_limit; /* limit number of output lines, 0 == infinite */ long counter; /* counter for matches [-c] */ char separator='\n'; /* line separator */ u_char myctype[UCHAR_MAX + 1]; void usage(void); void statistic(FILE *, char *); void fastfind(FILE *, char *, char *); void fastfind_icase(FILE *, char *, char *); void fastfind_mmap(char *, caddr_t, off_t, char *); void fastfind_mmap_icase(char *, caddr_t, off_t, char *); void search_mmap(char *, char **); void search_fopen(char *, char **); unsigned long cputime(void); extern char **colon(char **, char*, char*); extern int getwm(caddr_t); extern int getwf(FILE *); extern u_char *tolower_word(u_char *); extern int check_bigram_char(int); extern char *patprep(char *); extern void rebuild_message(char *db); extern int check_size(char *db); int main(int argc, char **argv) { int ch; char **dbv = NULL; char *path_fcodes; /* locate database */ #ifdef MMAP f_mmap = 1; /* mmap is default */ #endif (void) setlocale(LC_ALL, ""); while ((ch = getopt(argc, argv, "0Scd:il:ms")) != -1) switch(ch) { case '0': /* 'find -print0' style */ separator = '\0'; break; case 'S': /* statistic lines */ f_statistic = 1; break; case 'l': /* limit number of output lines, 0 == infinite */ f_limit = atol(optarg); if (f_limit < 0 ) errx(1, "invalid argument for -l: '%s'", optarg); break; case 'd': /* database */ dbv = colon(dbv, optarg, _PATH_FCODES); break; case 'i': /* ignore case */ f_icase = 1; break; case 'm': /* mmap */ #ifdef MMAP f_mmap = 1; #else warnx("mmap(2) not implemented"); #endif break; case 's': /* stdio lib */ f_mmap = 0; break; case 'c': /* suppress output, show only count of matches */ f_silent = 1; break; default: usage(); } argv += optind; argc -= optind; /* to few arguments */ if (argc < 1 && !(f_statistic)) usage(); /* no (valid) database as argument */ if (dbv == NULL || *dbv == NULL) { /* try to read database from environment */ if ((path_fcodes = getenv("LOCATE_PATH")) == NULL || *path_fcodes == '\0') /* use default database */ dbv = colon(dbv, _PATH_FCODES, _PATH_FCODES); else /* $LOCATE_PATH */ dbv = colon(dbv, path_fcodes, _PATH_FCODES); } if (f_icase && UCHAR_MAX < 4096) /* init tolower lookup table */ for (ch = 0; ch < UCHAR_MAX + 1; ch++) myctype[ch] = tolower(ch); /* foreach database ... */ while((path_fcodes = *dbv) != NULL) { dbv++; if (!strcmp(path_fcodes, "-")) f_stdin = 1; else f_stdin = 0; #ifndef MMAP f_mmap = 0; /* be paranoid */ #endif if (!f_mmap || f_stdin || f_statistic) search_fopen(path_fcodes, argv); else search_mmap(path_fcodes, argv); } if (f_silent) printf("%ld\n", counter); exit(0); } /* * Arguments: * db database * s search strings */ void search_fopen(char *db, char **s) { FILE *fp; /* can only read stdin once */ if (f_stdin) { fp = stdin; if (*(s+1) != NULL) { warnx("read database from stdin, use only `%s' as pattern", *s); *(s+1) = NULL; } } else { if (!check_size(db)) exit(1); if ((fp = fopen(db, "r")) == NULL) { warn("`%s'", db); rebuild_message(db); exit(1); } } /* count only chars or lines */ if (f_statistic) { statistic(fp, db); (void)fclose(fp); return; } /* foreach search string ... */ while(*s != NULL) { if (!f_stdin && fseek(fp, (long)0, SEEK_SET) == -1) err(1, "fseek to begin of ``%s''\n", db); if (f_icase) fastfind_icase(fp, *s, db); else fastfind(fp, *s, db); s++; } (void)fclose(fp); } #ifdef MMAP /* * Arguments: * db database * s search strings */ void search_mmap(char *db, char **s) { struct stat sb; int fd; caddr_t p; off_t len; if (!check_size(db)) exit(1); if (stat(db, &sb) == -1) err(1, "stat"); len = sb.st_size; if ((fd = open(db, O_RDONLY)) == -1) { warn("%s", db); rebuild_message(db); exit(1); } if ((p = mmap((caddr_t)0, (size_t)len, PROT_READ, MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) err(1, "mmap ``%s''", db); /* foreach search string ... */ while (*s != NULL) { if (f_icase) fastfind_mmap_icase(*s, p, len, db); else fastfind_mmap(*s, p, len, db); s++; } if (munmap(p, (size_t)len) == -1) warn("munmap %s\n", db); (void)close(fd); } #endif /* MMAP */ void usage () { (void)fprintf(stderr, "usage: locate [-0Scims] [-l limit] [-d database] pattern ...\n\n"); (void)fprintf(stderr, "default database: `%s' or $LOCATE_PATH\n", _PATH_FCODES); exit(1); } /* load fastfind functions */ /* statistic */ /* fastfind_mmap, fastfind_mmap_icase */ #ifdef MMAP #undef FF_MMAP #undef FF_ICASE #define FF_MMAP #include "fastfind.c" #define FF_ICASE #include "fastfind.c" #endif /* MMAP */ /* fopen */ /* fastfind, fastfind_icase */ #undef FF_MMAP #undef FF_ICASE #include "fastfind.c" #define FF_ICASE #include "fastfind.c" diff --git a/usr.bin/mktemp/mktemp.c b/usr.bin/mktemp/mktemp.c index e16c2a001a01..3be86bf21a02 100644 --- a/usr.bin/mktemp/mktemp.c +++ b/usr.bin/mktemp/mktemp.c @@ -1,208 +1,203 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1994, 1995, 1996, 1998 Peter Wemm * 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. * */ /* * This program was originally written long ago, originally for a non * BSD-like OS without mkstemp(). It's been modified over the years * to use mkstemp() rather than the original O_CREAT|O_EXCL/fstat/lstat * etc style hacks. * A cleanup, misc options and mkdtemp() calls were added to try and work * more like the OpenBSD version - which was first to publish the interface. */ #include #include #include #include #include #include #include #include -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - static void usage(void) __dead2; static const struct option long_opts[] = { {"directory", no_argument, NULL, 'd'}, {"tmpdir", optional_argument, NULL, 'p'}, {"quiet", no_argument, NULL, 'q'}, {"dry-run", no_argument, NULL, 'u'}, {NULL, no_argument, NULL, 0}, }; int main(int argc, char **argv) { int c, fd, ret; const char *prefix, *tmpdir; char *name; int dflag, qflag, tflag, uflag; bool prefer_tmpdir; ret = dflag = qflag = tflag = uflag = 0; prefer_tmpdir = true; prefix = "mktemp"; name = NULL; tmpdir = NULL; while ((c = getopt_long(argc, argv, "dp:qt:u", long_opts, NULL)) != -1) switch (c) { case 'd': dflag++; break; case 'p': tmpdir = optarg; if (tmpdir == NULL || *tmpdir == '\0') tmpdir = getenv("TMPDIR"); /* * We've already done the necessary environment * fallback, skip the later one. */ prefer_tmpdir = false; break; case 'q': qflag++; break; case 't': prefix = optarg; tflag++; break; case 'u': uflag++; break; default: usage(); } argc -= optind; argv += optind; if (!tflag && argc < 1) { tflag = 1; prefix = "tmp"; /* * For this implied -t mode, we actually want to swap the usual * order of precedence: -p, then TMPDIR, then /tmp. */ prefer_tmpdir = false; } if (tflag) { const char *envtmp; size_t len; envtmp = NULL; /* * $TMPDIR preferred over `-p` if specified, for compatibility. */ if (prefer_tmpdir || tmpdir == NULL) envtmp = getenv("TMPDIR"); if (envtmp != NULL) tmpdir = envtmp; if (tmpdir == NULL) tmpdir = _PATH_TMP; len = strlen(tmpdir); if (len > 0 && tmpdir[len - 1] == '/') asprintf(&name, "%s%s.XXXXXXXXXX", tmpdir, prefix); else asprintf(&name, "%s/%s.XXXXXXXXXX", tmpdir, prefix); /* if this fails, the program is in big trouble already */ if (name == NULL) { if (qflag) return (1); else errx(1, "cannot generate template"); } } /* generate all requested files */ while (name != NULL || argc > 0) { if (name == NULL) { if (!tflag && tmpdir != NULL) asprintf(&name, "%s/%s", tmpdir, argv[0]); else name = strdup(argv[0]); if (name == NULL) err(1, "%s", argv[0]); argv++; argc--; } if (dflag) { if (mkdtemp(name) == NULL) { ret = 1; if (!qflag) warn("mkdtemp failed on %s", name); } else { printf("%s\n", name); if (uflag) rmdir(name); } } else { fd = mkstemp(name); if (fd < 0) { ret = 1; if (!qflag) warn("mkstemp failed on %s", name); } else { close(fd); if (uflag) unlink(name); printf("%s\n", name); } } if (name) free(name); name = NULL; } return (ret); } static void usage(void) { fprintf(stderr, "usage: mktemp [-d] [-p tmpdir] [-q] [-t prefix] [-u] template " "...\n"); fprintf(stderr, " mktemp [-d] [-p tmpdir] [-q] [-u] -t prefix \n"); exit (1); } diff --git a/usr.bin/morse/morse.c b/usr.bin/morse/morse.c index 7e018f3b9aee..1e72b0b03931 100644 --- a/usr.bin/morse/morse.c +++ b/usr.bin/morse/morse.c @@ -1,672 +1,670 @@ /*- * 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. * 3. 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. */ /* * Taught to send *real* morse by Lyndon Nerenberg (VE6BBM) * */ static const char copyright[] = "@(#) Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #if 0 static char sccsid[] = "@(#)morse.c 8.1 (Berkeley) 5/31/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ /* Always use the speaker, let the open fail if -p is selected */ #define SPEAKER "/dev/speaker" #endif #define WHITESPACE " \t\n" #define DELIMITERS " \t" #ifdef SPEAKER #include #endif struct morsetab { const char inchar; const char *morse; }; static const struct morsetab mtab[] = { /* letters */ {'a', ".-"}, {'b', "-..."}, {'c', "-.-."}, {'d', "-.."}, {'e', "."}, {'f', "..-."}, {'g', "--."}, {'h', "...."}, {'i', ".."}, {'j', ".---"}, {'k', "-.-"}, {'l', ".-.."}, {'m', "--"}, {'n', "-."}, {'o', "---"}, {'p', ".--."}, {'q', "--.-"}, {'r', ".-."}, {'s', "..."}, {'t', "-"}, {'u', "..-"}, {'v', "...-"}, {'w', ".--"}, {'x', "-..-"}, {'y', "-.--"}, {'z', "--.."}, /* digits */ {'0', "-----"}, {'1', ".----"}, {'2', "..---"}, {'3', "...--"}, {'4', "....-"}, {'5', "....."}, {'6', "-...."}, {'7', "--..."}, {'8', "---.."}, {'9', "----."}, /* punctuation */ {',', "--..--"}, {'.', ".-.-.-"}, {'"', ".-..-."}, {'!', "..--."}, {'?', "..--.."}, {'/', "-..-."}, {'-', "-....-"}, {'=', "-...-"}, /* BT */ {':', "---..."}, {';', "-.-.-."}, {'(', "-.--."}, /* KN */ {')', "-.--.-"}, {'$', "...-..-"}, {'+', ".-.-."}, /* AR */ {'@', ".--.-."}, /* AC */ {'_', "..--.-"}, {'\'', ".----."}, /* prosigns without already assigned values */ {'#', ".-..."}, /* AS */ {'&', "...-.-"}, /* SK */ {'*', "...-."}, /* VE */ {'%', "-...-.-"}, /* BK */ {'\0', ""} }; /* * Code-points for some Latin1 chars in ISO-8859-1 encoding. * UTF-8 encoded chars in the comments. */ static const struct morsetab iso8859_1tab[] = { {'\340', ".--.-"}, /* à */ {'\341', ".--.-"}, /* á */ {'\342', ".--.-"}, /* â */ {'\344', ".-.-"}, /* ä */ {'\347', "-.-.."}, /* ç */ {'\350', "..-.."}, /* è */ {'\351', "..-.."}, /* é */ {'\352', "-..-."}, /* ê */ {'\361', "--.--"}, /* ñ */ {'\366', "---."}, /* ö */ {'\374', "..--"}, /* ü */ {'\0', ""} }; /* * Code-points for some Greek chars in ISO-8859-7 encoding. * UTF-8 encoded chars in the comments. */ static const struct morsetab iso8859_7tab[] = { /* * This table does not implement: * - the special sequences for the seven diphthongs, * - the punctuation differences. * Implementing these features would introduce too many * special-cases in the program's main loop. * The diphthong sequences are: * alpha iota .-.- * alpha upsilon ..-- * epsilon upsilon ---. * eta upsilon ...- * omicron iota ---.. * omicron upsilon ..- * upsilon iota .--- * The different punctuation symbols are: * ; ..-.- * ! --..-- */ {'\341', ".-"}, /* α, alpha */ {'\334', ".-"}, /* ά, alpha with acute */ {'\342', "-..."}, /* β, beta */ {'\343', "--."}, /* γ, gamma */ {'\344', "-.."}, /* δ, delta */ {'\345', "."}, /* ε, epsilon */ {'\335', "."}, /* έ, epsilon with acute */ {'\346', "--.."}, /* ζ, zeta */ {'\347', "...."}, /* η, eta */ {'\336', "...."}, /* ή, eta with acute */ {'\350', "-.-."}, /* θ, theta */ {'\351', ".."}, /* ι, iota */ {'\337', ".."}, /* ί, iota with acute */ {'\372', ".."}, /* ϊ, iota with diaeresis */ {'\300', ".."}, /* ΐ, iota with acute and diaeresis */ {'\352', "-.-"}, /* κ, kappa */ {'\353', ".-.."}, /* λ, lambda */ {'\354', "--"}, /* μ, mu */ {'\355', "-."}, /* ν, nu */ {'\356', "-..-"}, /* ξ, xi */ {'\357', "---"}, /* ο, omicron */ {'\374', "---"}, /* ό, omicron with acute */ {'\360', ".--."}, /* π, pi */ {'\361', ".-."}, /* ρ, rho */ {'\363', "..."}, /* σ, sigma */ {'\362', "..."}, /* ς, final sigma */ {'\364', "-"}, /* τ, tau */ {'\365', "-.--"}, /* υ, upsilon */ {'\375', "-.--"}, /* ύ, upsilon with acute */ {'\373', "-.--"}, /* ϋ, upsilon and diaeresis */ {'\340', "-.--"}, /* ΰ, upsilon with acute and diaeresis */ {'\366', "..-."}, /* φ, phi */ {'\367', "----"}, /* χ, chi */ {'\370', "--.-"}, /* ψ, psi */ {'\371', ".--"}, /* ω, omega */ {'\376', ".--"}, /* ώ, omega with acute */ {'\0', ""} }; /* * Code-points for the Cyrillic alphabet in KOI8-R encoding. * UTF-8 encoded chars in the comments. */ static const struct morsetab koi8rtab[] = { {'\301', ".-"}, /* а, a */ {'\302', "-..."}, /* б, be */ {'\327', ".--"}, /* в, ve */ {'\307', "--."}, /* г, ge */ {'\304', "-.."}, /* д, de */ {'\305', "."}, /* е, ye */ {'\243', "."}, /* ё, yo, the same as ye */ {'\326', "...-"}, /* ж, she */ {'\332', "--.."}, /* з, ze */ {'\311', ".."}, /* и, i */ {'\312', ".---"}, /* й, i kratkoye */ {'\313', "-.-"}, /* к, ka */ {'\314', ".-.."}, /* л, el */ {'\315', "--"}, /* м, em */ {'\316', "-."}, /* н, en */ {'\317', "---"}, /* о, o */ {'\320', ".--."}, /* п, pe */ {'\322', ".-."}, /* р, er */ {'\323', "..."}, /* с, es */ {'\324', "-"}, /* т, te */ {'\325', "..-"}, /* у, u */ {'\306', "..-."}, /* ф, ef */ {'\310', "...."}, /* х, kha */ {'\303', "-.-."}, /* ц, ce */ {'\336', "---."}, /* ч, che */ {'\333', "----"}, /* ш, sha */ {'\335', "--.-"}, /* щ, shcha */ {'\331', "-.--"}, /* ы, yi */ {'\330', "-..-"}, /* ь, myakhkij znak */ {'\334', "..-.."}, /* э, ae */ {'\300', "..--"}, /* ю, yu */ {'\321', ".-.-"}, /* я, ya */ {'\0', ""} }; static void show(const char *), play(const char *), morse(char); static void decode (char *), fdecode(FILE *); static void ttyout(const char *); static void sighandler(int); static int pflag, lflag, rflag, sflag, eflag; static int wpm = 20; /* effective words per minute */ static int cpm; /* effective words per minute between * characters */ #define FREQUENCY 600 static int freq = FREQUENCY; static char *device; /* for tty-controlled generator */ #define DASH_LEN 3 #define CHAR_SPACE 3 #define WORD_SPACE (7 - CHAR_SPACE - 1) static float dot_clock; static float cdot_clock; static int spkr, line; static struct termios otty, ntty; static int olflags; #ifdef SPEAKER static tone_t sound; #define GETOPTOPTS "c:d:ef:lprsw:" #define USAGE \ "usage: morse [-elprs] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n" #else #define GETOPTOPTS "c:d:ef:lrsw:" #define USAGE \ "usage: morse [-elrs] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n" #endif static const struct morsetab *hightab; int main(int argc, char *argv[]) { int ch, lflags; char *p, *codeset; while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1) switch ((char) ch) { case 'c': cpm = atoi(optarg); break; case 'd': device = optarg; break; case 'e': eflag = 1; setvbuf(stdout, 0, _IONBF, 0); break; case 'f': freq = atoi(optarg); break; case 'l': lflag = 1; break; #ifdef SPEAKER case 'p': pflag = 1; break; #endif case 'r': rflag = 1; break; case 's': sflag = 1; break; case 'w': wpm = atoi(optarg); break; case '?': default: errx(1, USAGE); } if ((sflag && lflag) || (sflag && rflag) || (lflag && rflag)) { errx(1, "morse: only one of -l, -s, and -r allowed\n"); } if ((pflag || device) && (sflag || lflag)) { errx(1, "morse: only one of -p, -d and -l, -s allowed\n"); } if (cpm == 0) { cpm = wpm; } if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) { errx(1, "morse: insane speed\n"); } if ((pflag || device) && (freq == 0)) { freq = FREQUENCY; } #ifdef SPEAKER if (pflag) { if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) { err(1, SPEAKER); } } else #endif if (device) { if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) { err(1, "open tty line"); } if (tcgetattr(line, &otty) == -1) { err(1, "tcgetattr() failed"); } ntty = otty; ntty.c_cflag |= CLOCAL; tcsetattr(line, TCSANOW, &ntty); lflags = fcntl(line, F_GETFL); lflags &= ~O_NONBLOCK; fcntl(line, F_SETFL, &lflags); ioctl(line, TIOCMGET, &lflags); lflags &= ~TIOCM_RTS; olflags = lflags; ioctl(line, TIOCMSET, &lflags); (void)signal(SIGHUP, sighandler); (void)signal(SIGINT, sighandler); (void)signal(SIGQUIT, sighandler); (void)signal(SIGTERM, sighandler); } if (pflag || device) { dot_clock = wpm / 2.4; /* dots/sec */ dot_clock = 1 / dot_clock; /* duration of a dot */ dot_clock = dot_clock / 2; /* dot_clock runs at twice */ /* the dot rate */ dot_clock = dot_clock * 100; /* scale for ioctl */ cdot_clock = cpm / 2.4; /* dots/sec */ cdot_clock = 1 / cdot_clock; /* duration of a dot */ cdot_clock = cdot_clock / 2; /* dot_clock runs at twice */ /* the dot rate */ cdot_clock = cdot_clock * 100; /* scale for ioctl */ } argc -= optind; argv += optind; if (setlocale(LC_CTYPE, "") != NULL && *(codeset = nl_langinfo(CODESET)) != '\0') { if (strcmp(codeset, "KOI8-R") == 0) hightab = koi8rtab; else if (strcmp(codeset, "ISO8859-1") == 0 || strcmp(codeset, "ISO8859-15") == 0) hightab = iso8859_1tab; else if (strcmp(codeset, "ISO8859-7") == 0) hightab = iso8859_7tab; } if (lflag) { printf("m"); } if (rflag) { if (*argv) { do { p = strtok(*argv, DELIMITERS); if (p == NULL) { decode(*argv); } else { while (p) { decode(p); p = strtok(NULL, DELIMITERS); } } } while (*++argv); putchar('\n'); } else { fdecode(stdin); } } else if (*argv) { do { for (p = *argv; *p; ++p) { if (eflag) putchar(*p); morse(*p); } if (eflag) putchar(' '); morse(' '); } while (*++argv); } else { while ((ch = getchar()) != EOF) { if (eflag) putchar(ch); morse(ch); } } if (device) tcsetattr(line, TCSANOW, &otty); exit(0); } static void morse(char c) { const struct morsetab *m; if (isalpha((unsigned char)c)) c = tolower((unsigned char)c); if ((c == '\r') || (c == '\n')) c = ' '; if (c == ' ') { if (pflag) play(" "); else if (device) ttyout(" "); else if (lflag) printf("\n"); else show(""); return; } for (m = ((unsigned char)c < 0x80? mtab: hightab); m != NULL && m->inchar != '\0'; m++) { if (m->inchar == c) { if (pflag) { play(m->morse); } else if (device) { ttyout(m->morse); } else show(m->morse); } } } static void show(const char *s) { if (lflag) { printf("%s ", s); } else if (sflag) { printf(" %s\n", s); } else { for (; *s; ++s) printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" : "di" : "dah"); printf("\n"); } } static void play(const char *s) { #ifdef SPEAKER const char *c; for (c = s; *c != '\0'; c++) { switch (*c) { case '.': sound.frequency = freq; sound.duration = dot_clock; break; case '-': sound.frequency = freq; sound.duration = dot_clock * DASH_LEN; break; case ' ': sound.frequency = 0; sound.duration = cdot_clock * WORD_SPACE; break; default: sound.duration = 0; } if (sound.duration) { if (ioctl(spkr, SPKRTONE, &sound) == -1) { err(1, "ioctl play"); } } sound.frequency = 0; sound.duration = dot_clock; if (ioctl(spkr, SPKRTONE, &sound) == -1) { err(1, "ioctl rest"); } } sound.frequency = 0; sound.duration = cdot_clock * CHAR_SPACE; ioctl(spkr, SPKRTONE, &sound); #endif } static void ttyout(const char *s) { const char *c; int duration, on, lflags; for (c = s; *c != '\0'; c++) { switch (*c) { case '.': on = 1; duration = dot_clock; break; case '-': on = 1; duration = dot_clock * DASH_LEN; break; case ' ': on = 0; duration = cdot_clock * WORD_SPACE; break; default: on = 0; duration = 0; } if (on) { ioctl(line, TIOCMGET, &lflags); lflags |= TIOCM_RTS; ioctl(line, TIOCMSET, &lflags); } duration *= 10000; if (duration) usleep(duration); ioctl(line, TIOCMGET, &lflags); lflags &= ~TIOCM_RTS; ioctl(line, TIOCMSET, &lflags); duration = dot_clock * 10000; usleep(duration); } duration = cdot_clock * CHAR_SPACE * 10000; usleep(duration); } void fdecode(FILE *stream) { char *n, *p, *s; char buf[BUFSIZ]; s = buf; while (fgets(s, BUFSIZ - (s - buf), stream)) { p = buf; while (*p && isblank(*p)) { p++; } while (*p && isspace(*p)) { p++; putchar (' '); } while (*p) { n = strpbrk(p, WHITESPACE); if (n == NULL) { /* The token was interrupted at the end * of the buffer. Shift it to the begin * of the buffer. */ for (s = buf; *p; *s++ = *p++) ; } else { *n = '\0'; n++; decode(p); p = n; } } } putchar('\n'); } void decode(char *p) { char c; const struct morsetab *m; c = ' '; for (m = mtab; m != NULL && m->inchar != '\0'; m++) { if (strcmp(m->morse, p) == 0) { c = m->inchar; break; } } if (c == ' ') for (m = hightab; m != NULL && m->inchar != '\0'; m++) { if (strcmp(m->morse, p) == 0) { c = m->inchar; break; } } putchar(c); } static void sighandler(int signo) { ioctl(line, TIOCMSET, &olflags); tcsetattr(line, TCSANOW, &otty); signal(signo, SIG_DFL); (void)kill(getpid(), signo); } diff --git a/usr.bin/nfsstat/nfsstat.c b/usr.bin/nfsstat/nfsstat.c index 9ea242cef890..11f403a9c14c 100644 --- a/usr.bin/nfsstat/nfsstat.c +++ b/usr.bin/nfsstat/nfsstat.c @@ -1,1267 +1,1265 @@ /* * Copyright (c) 1983, 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * 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 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. */ /*- * Copyright (c) 2004, 2008, 2009 Silicon Graphics International Corp. * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)nfsstat.c 8.2 (Berkeley) 3/31/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int widemode = 0; static int zflag = 0; static int printtitle = 1; static struct nfsstatsv1 ext_nfsstats; static int extra_output = 0; static void intpr(int, int); static void printhdr(int, int, int); static void usage(void) __dead2; static char *sperc1(int, int); static char *sperc2(int, int); static void exp_intpr(int, int, int); static void exp_sidewaysintpr(u_int, int, int, int); static void compute_new_stats(struct nfsstatsv1 *cur_stats, struct nfsstatsv1 *prev_stats, int curop, long double etime, long double *mbsec, long double *kb_per_transfer, long double *transfers_per_second, long double *ms_per_transfer, uint64_t *queue_len, long double *busy_pct); #define DELTA(field) (nfsstats.field - lastst.field) #define STAT_TYPE_READ 0 #define STAT_TYPE_WRITE 1 #define STAT_TYPE_COMMIT 2 #define NUM_STAT_TYPES 3 struct stattypes { int stat_type; int nfs_type; }; static struct stattypes statstruct[] = { {STAT_TYPE_READ, NFSV4OP_READ}, {STAT_TYPE_WRITE, NFSV4OP_WRITE}, {STAT_TYPE_COMMIT, NFSV4OP_COMMIT} }; #define STAT_TYPE_TO_NFS(stat_type) statstruct[stat_type].nfs_type #define NFSSTAT_XO_VERSION "1" int main(int argc, char **argv) { u_int interval; int clientOnly = -1; int serverOnly = -1; int newStats = 0; int ch; int mntlen, i; char buf[1024]; struct statfs *mntbuf; struct nfscl_dumpmntopts dumpmntopts; interval = 0; argc = xo_parse_args(argc, argv); if (argc < 0) exit(1); xo_set_version(NFSSTAT_XO_VERSION); while ((ch = getopt(argc, argv, "cdEesWM:mN:w:zq")) != -1) switch(ch) { case 'M': printf("*** -M option deprecated, ignored\n\n"); break; case 'm': /* Display mount options for NFS mount points. */ mntlen = getmntinfo(&mntbuf, MNT_NOWAIT); for (i = 0; i < mntlen; i++) { if (strcmp(mntbuf->f_fstypename, "nfs") == 0) { dumpmntopts.ndmnt_fname = mntbuf->f_mntonname; dumpmntopts.ndmnt_buf = buf; dumpmntopts.ndmnt_blen = sizeof(buf); if (nfssvc(NFSSVC_DUMPMNTOPTS, &dumpmntopts) >= 0) printf("%s on %s\n%s\n", mntbuf->f_mntfromname, mntbuf->f_mntonname, buf); else if (errno == EPERM) errx(1, "Only privileged users" " can use the -m option"); } mntbuf++; } exit(0); case 'N': printf("*** -N option deprecated, ignored\n\n"); break; case 'W': widemode = 1; break; case 'w': interval = atoi(optarg); break; case 'c': clientOnly = 1; if (serverOnly < 0) serverOnly = 0; break; case 'd': newStats = 1; if (interval == 0) interval = 1; break; case 's': serverOnly = 1; if (clientOnly < 0) clientOnly = 0; break; case 'z': zflag = 1; break; case 'E': if (extra_output != 0) xo_err(1, "-e and -E are mutually exclusive"); extra_output = 2; break; case 'e': if (extra_output != 0) xo_err(1, "-e and -E are mutually exclusive"); extra_output = 1; break; case 'q': printtitle = 0; break; case '?': default: usage(); } argc -= optind; argv += optind; #define BACKWARD_COMPATIBILITY #ifdef BACKWARD_COMPATIBILITY if (*argv) interval = atoi(*argv); #endif if (modfind("nfscommon") < 0) xo_err(1, "NFS client/server not loaded"); if (interval) { exp_sidewaysintpr(interval, clientOnly, serverOnly, newStats); } else { xo_open_container("nfsstat"); if (extra_output != 0) exp_intpr(clientOnly, serverOnly, extra_output - 1); else intpr(clientOnly, serverOnly); xo_close_container("nfsstat"); } xo_finish(); exit(0); } /* * Print a description of the nfs stats. */ static void intpr(int clientOnly, int serverOnly) { int nfssvc_flag; nfssvc_flag = NFSSVC_GETSTATS | NFSSVC_NEWSTRUCT; if (zflag != 0) { if (clientOnly != 0) nfssvc_flag |= NFSSVC_ZEROCLTSTATS; if (serverOnly != 0) nfssvc_flag |= NFSSVC_ZEROSRVSTATS; } ext_nfsstats.vers = NFSSTATS_V1; if (nfssvc(nfssvc_flag, &ext_nfsstats) < 0) xo_err(1, "Can't get stats"); if (clientOnly) { xo_open_container("clientstats"); if (printtitle) xo_emit("{T:Client Info:\n"); xo_open_container("operations"); xo_emit("{T:Rpc Counts:}\n"); xo_emit("{T:Getattr/%13.13s}{T:Setattr/%13.13s}" "{T:Lookup/%13.13s}{T:Readlink/%13.13s}" "{T:Read/%13.13s}{T:Write/%13.13s}" "{T:Create/%13.13s}{T:Remove/%13.13s}\n"); xo_emit("{:getattr/%13ju}{:setattr/%13ju}" "{:lookup/%13ju}{:readlink/%13ju}" "{:read/%13ju}{:write/%13ju}" "{:create/%13ju}{:remove/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_GETATTR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SETATTR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LOOKUP], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READLINK], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READ], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_WRITE], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_CREATE], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_REMOVE]); xo_emit("{T:Rename/%13.13s}{T:Link/%13.13s}" "{T:Symlink/%13.13s}{T:Mkdir/%13.13s}" "{T:Rmdir/%13.13s}{T:Readdir/%13.13s}" "{T:RdirPlus/%13.13s}{T:Access/%13.13s}\n"); xo_emit("{:rename/%13ju}{:link/%13ju}" "{:symlink/%13ju}{:mkdir/%13ju}" "{:rmdir/%13ju}{:readdir/%13ju}" "{:rdirplus/%13ju}{:access/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RENAME], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LINK], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SYMLINK], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_MKDIR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RMDIR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READDIR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READDIRPLUS], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_ACCESS]); xo_emit("{T:Mknod/%13.13s}{T:Fsstat/%13.13s}" "{T:Fsinfo/%13.13s}{T:PathConf/%13.13s}" "{T:Commit/%13.13s}\n"); xo_emit("{:mknod/%13ju}{:fsstat/%13ju}" "{:fsinfo/%13ju}{:pathconf/%13ju}" "{:commit/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_MKNOD], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_FSSTAT], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_FSINFO], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_PATHCONF], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_COMMIT]); xo_close_container("operations"); xo_open_container("rpcs"); xo_emit("{T:Rpc Info:}\n"); xo_emit("{T:TimedOut/%13.13s}{T:Invalid/%13.13s}" "{T:X Replies/%13.13s}{T:Retries/%13.13s}" "{T:Requests/%13.13s}\n"); xo_emit("{:timedout/%13ju}{:invalid/%13ju}" "{:xreplies/%13ju}{:retries/%13ju}" "{:requests/%13ju}\n", (uintmax_t)ext_nfsstats.rpctimeouts, (uintmax_t)ext_nfsstats.rpcinvalid, (uintmax_t)ext_nfsstats.rpcunexpected, (uintmax_t)ext_nfsstats.rpcretries, (uintmax_t)ext_nfsstats.rpcrequests); xo_close_container("rpcs"); xo_open_container("cache"); xo_emit("{T:Cache Info:}\n"); xo_emit("{T:Attr Hits/%13.13s}{T:Attr Misses/%13.13s}" "{T:Lkup Hits/%13.13s}{T:Lkup Misses/%13.13s}" "{T:BioR Hits/%13.13s}{T:BioR Misses/%13.13s}" "{T:BioW Hits/%13.13s}{T:BioW Misses/%13.13s}\n"); xo_emit("{:attrhits/%13ju}{:attrmisses/%13ju}" "{:lkuphits/%13ju}{:lkupmisses/%13ju}" "{:biorhits/%13ju}{:biormisses/%13ju}" "{:biowhits/%13ju}{:biowmisses/%13ju}\n", (uintmax_t)ext_nfsstats.attrcache_hits, (uintmax_t)ext_nfsstats.attrcache_misses, (uintmax_t)ext_nfsstats.lookupcache_hits, (uintmax_t)ext_nfsstats.lookupcache_misses, (uintmax_t)(ext_nfsstats.biocache_reads - ext_nfsstats.read_bios), (uintmax_t)ext_nfsstats.read_bios, (uintmax_t)(ext_nfsstats.biocache_writes - ext_nfsstats.write_bios), (uintmax_t)ext_nfsstats.write_bios); xo_emit("{T:BioRL Hits/%13.13s}{T:BioRL Misses/%13.13s}" "{T:BioD Hits/%13.13s}{T:BioD Misses/%13.13s}" "{T:DirE Hits/%13.13s}{T:DirE Misses/%13.13s}" "{T:Accs Hits/%13.13s}{T:Accs Misses/%13.13s}\n"); xo_emit("{:biosrlhits/%13ju}{:biorlmisses/%13ju}" "{:biodhits/%13ju}{:biodmisses/%13ju}" "{:direhits/%13ju}{:diremisses/%13ju}" "{:accshits/%13ju}{:accsmisses/%13ju}\n", (uintmax_t)(ext_nfsstats.biocache_readlinks - ext_nfsstats.readlink_bios), (uintmax_t)ext_nfsstats.readlink_bios, (uintmax_t)(ext_nfsstats.biocache_readdirs - ext_nfsstats.readdir_bios), (uintmax_t)ext_nfsstats.readdir_bios, (uintmax_t)ext_nfsstats.direofcache_hits, (uintmax_t)ext_nfsstats.direofcache_misses, (uintmax_t)ext_nfsstats.accesscache_hits, (uintmax_t)ext_nfsstats.accesscache_misses); xo_close_container("cache"); xo_close_container("clientstats"); } if (serverOnly) { xo_open_container("serverstats"); xo_emit("{T:Server Info:}\n"); xo_open_container("operations"); xo_emit("{T:Getattr/%13.13s}{T:Setattr/%13.13s}" "{T:Lookup/%13.13s}{T:Readlink/%13.13s}" "{T:Read/%13.13s}{T:Write/%13.13s}" "{T:Create/%13.13s}{T:Remove/%13.13s}\n"); xo_emit("{:getattr/%13ju}{:setattr/%13ju}" "{:lookup/%13ju}{:readlink/%13ju}" "{:read/%13ju}{:write/%13ju}" "{:create/%13ju}{:remove/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETATTR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SETATTR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOOKUP], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READLINK], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READ], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_WRITE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_CREATE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_REMOVE]); xo_emit("{T:Rename/%13.13s}{T:Link/%13.13s}" "{T:Symlink/%13.13s}{T:Mkdir/%13.13s}" "{T:Rmdir/%13.13s}{T:Readdir/%13.13s}" "{T:RdirPlus/%13.13s}{T:Access/%13.13s}\n"); xo_emit("{:rename/%13ju}{:link/%13ju}" "{:symlink/%13ju}{:mkdir/%13ju}" "{:rmdir/%13ju}{:readdir/%13ju}" "{:rdirplus/%13ju}{:access/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RENAME], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LINK], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SYMLINK], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_MKDIR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RMDIR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READDIR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READDIRPLUS], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_ACCESS]); xo_emit("{T:Mknod/%13.13s}{T:Fsstat/%13.13s}" "{T:Fsinfo/%13.13s}{T:PathConf/%13.13s}" "{T:Commit/%13.13s}\n"); xo_emit("{:mknod/%13ju}{:fsstat/%13ju}" "{:fsinfo/%13ju}{:pathconf/%13ju}" "{:commit/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_MKNOD], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_FSSTAT], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_FSINFO], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_PATHCONF], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_COMMIT]); xo_close_container("operations"); xo_open_container("server"); xo_emit("{T:Server Write Gathering:/%13.13s}\n"); xo_emit("{T:WriteOps/%13.13s}{T:WriteRPC/%13.13s}" "{T:Opsaved/%13.13s}\n"); xo_emit("{:writeops/%13ju}{:writerpc/%13ju}" "{:opsaved/%13ju}\n", /* * The new client doesn't do write gathering. It was * only useful for NFSv2. */ (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_WRITE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_WRITE], 0); xo_close_container("server"); xo_open_container("cache"); xo_emit("{T:Server Cache Stats:/%13.13s}\n"); xo_emit("{T:Inprog/%13.13s}" "{T:Non-Idem/%13.13s}{T:Misses/%13.13s}\n"); xo_emit("{:inprog/%13ju}{:nonidem/%13ju}{:misses/%13ju}\n", (uintmax_t)ext_nfsstats.srvcache_inproghits, (uintmax_t)ext_nfsstats.srvcache_nonidemdonehits, (uintmax_t)ext_nfsstats.srvcache_misses); xo_close_container("cache"); xo_close_container("serverstats"); } } static void printhdr(int clientOnly, int serverOnly, int newStats) { if (newStats) { printf(" [%s Read %s] [%s Write %s] " "%s[=========== Total ============]\n" " KB/t tps MB/s%s KB/t tps MB/s%s " "%sKB/t tps MB/s ms ql %%b", widemode ? "========" : "=====", widemode ? "========" : "=====", widemode ? "========" : "=====", widemode ? "=======" : "====", widemode ? "[Commit ] " : "", widemode ? " ms" : "", widemode ? " ms" : "", widemode ? "tps ms " : ""); } else { printf("%s%6.6s %6.6s %6.6s %6.6s %6.6s %6.6s %6.6s %6.6s", ((serverOnly && clientOnly) ? " " : " "), "GtAttr", "Lookup", "Rdlink", "Read", "Write", "Rename", "Access", "Rddir"); if (widemode && clientOnly) { printf(" Attr Lkup BioR BioW Accs BioD"); } } printf("\n"); fflush(stdout); } static void usage(void) { (void)fprintf(stderr, "usage: nfsstat [-cdemszW] [-w wait]\n"); exit(1); } static char SPBuf[64][8]; static int SPIndex; static char * sperc1(int hits, int misses) { char *p = SPBuf[SPIndex]; if (hits + misses) { sprintf(p, "%3d%%", (int)(char)((quad_t)hits * 100 / (hits + misses))); } else { sprintf(p, " -"); } SPIndex = (SPIndex + 1) & 63; return(p); } static char * sperc2(int ttl, int misses) { char *p = SPBuf[SPIndex]; if (ttl) { sprintf(p, "%3d%%", (int)(char)((quad_t)(ttl - misses) * 100 / ttl)); } else { sprintf(p, " -"); } SPIndex = (SPIndex + 1) & 63; return(p); } #define DELTA_T(field) \ devstat_compute_etime(&cur_stats->field, \ (prev_stats ? &prev_stats->field : NULL)) /* * XXX KDM mostly copied from ctlstat. We should commonize the code (and * the devstat code) somehow. */ static void compute_new_stats(struct nfsstatsv1 *cur_stats, struct nfsstatsv1 *prev_stats, int curop, long double etime, long double *mbsec, long double *kb_per_transfer, long double *transfers_per_second, long double *ms_per_transfer, uint64_t *queue_len, long double *busy_pct) { uint64_t total_bytes = 0, total_operations = 0; struct bintime total_time_bt; struct timespec total_time_ts; bzero(&total_time_bt, sizeof(total_time_bt)); bzero(&total_time_ts, sizeof(total_time_ts)); total_bytes = cur_stats->srvbytes[curop]; total_operations = cur_stats->srvops[curop]; if (prev_stats != NULL) { total_bytes -= prev_stats->srvbytes[curop]; total_operations -= prev_stats->srvops[curop]; } *mbsec = total_bytes; *mbsec /= 1024 * 1024; if (etime > 0.0) { *busy_pct = DELTA_T(busytime); if (*busy_pct < 0) *busy_pct = 0; *busy_pct /= etime; *busy_pct *= 100; if (*busy_pct < 0) *busy_pct = 0; *mbsec /= etime; } else { *busy_pct = 0; *mbsec = 0; } *kb_per_transfer = total_bytes; *kb_per_transfer /= 1024; if (total_operations > 0) *kb_per_transfer /= total_operations; else *kb_per_transfer = 0; if (etime > 0.0) { *transfers_per_second = total_operations; *transfers_per_second /= etime; } else { *transfers_per_second = 0.0; } if (total_operations > 0) { *ms_per_transfer = DELTA_T(srvduration[curop]); *ms_per_transfer /= total_operations; *ms_per_transfer *= 1000; } else *ms_per_transfer = 0.0; *queue_len = cur_stats->srvstartcnt - cur_stats->srvdonecnt; } /* * Print a description of the nfs stats for the client/server, * including NFSv4.1. */ static void exp_intpr(int clientOnly, int serverOnly, int nfs41) { int nfssvc_flag; xo_open_container("nfsv4"); nfssvc_flag = NFSSVC_GETSTATS | NFSSVC_NEWSTRUCT; if (zflag != 0) { if (clientOnly != 0) nfssvc_flag |= NFSSVC_ZEROCLTSTATS; if (serverOnly != 0) nfssvc_flag |= NFSSVC_ZEROSRVSTATS; } ext_nfsstats.vers = NFSSTATS_V1; if (nfssvc(nfssvc_flag, &ext_nfsstats) < 0) xo_err(1, "Can't get stats"); if (clientOnly != 0) { xo_open_container("clientstats"); xo_open_container("operations"); if (printtitle) { xo_emit("{T:Client Info:}\n"); xo_emit("{T:RPC Counts:}\n"); } xo_emit("{T:Getattr/%13.13s}{T:Setattr/%13.13s}" "{T:Lookup/%13.13s}{T:Readlink/%13.13s}" "{T:Read/%13.13s}{T:Write/%13.13s}\n"); xo_emit("{:getattr/%13ju}{:setattr/%13ju}{:lookup/%13ju}" "{:readlink/%13ju}{:read/%13ju}{:write/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_GETATTR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SETATTR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LOOKUP], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READLINK], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READ], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_WRITE]); xo_emit("{T:Create/%13.13s}{T:Remove/%13.13s}" "{T:Rename/%13.13s}{T:Link/%13.13s}" "{T:Symlink/%13.13s}{T:Mkdir/%13.13s}\n"); xo_emit("{:create/%13ju}{:remove/%13ju}{:rename/%13ju}" "{:link/%13ju}{:symlink/%13ju}{:mkdir/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_CREATE], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_REMOVE], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RENAME], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LINK], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SYMLINK], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_MKDIR]); xo_emit("{T:Rmdir/%13.13s}{T:Readdir/%13.13s}" "{T:RdirPlus/%13.13s}{T:Access/%13.13s}" "{T:Mknod/%13.13s}{T:Fsstat/%13.13s}\n"); xo_emit("{:rmdir/%13ju}{:readdir/%13ju}{:rdirplus/%13ju}" "{:access/%13ju}{:mknod/%13ju}{:fsstat/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RMDIR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READDIR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READDIRPLUS], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_ACCESS], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_MKNOD], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_FSSTAT]); xo_emit("{T:FSinfo/%13.13s}{T:pathConf/%13.13s}" "{T:Commit/%13.13s}{T:SetClId/%13.13s}" "{T:SetClIdCf/%13.13s}{T:Lock/%13.13s}\n"); xo_emit("{:fsinfo/%13ju}{:pathconf/%13ju}{:commit/%13ju}" "{:setclientid/%13ju}{:setclientidcf/%13ju}{:lock/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_FSINFO], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_PATHCONF], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_COMMIT], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SETCLIENTID], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SETCLIENTIDCFRM], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LOCK]); xo_emit("{T:LockT/%13.13s}{T:LockU/%13.13s}" "{T:Open/%13.13s}{T:OpenCfr/%13.13s}\n"); xo_emit("{:lockt/%13ju}{:locku/%13ju}" "{:open/%13ju}{:opencfr/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LOCKT], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LOCKU], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_OPEN], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_OPENCONFIRM]); if (nfs41) { xo_open_container("nfsv41"); xo_emit("{T:OpenDownGr/%13.13s}{T:Close/%13.13s}\n"); xo_emit("{:opendowngr/%13ju}{:close/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_OPENDOWNGRADE], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_CLOSE]); xo_emit("{T:RelLckOwn/%13.13s}{T:FreeStateID/%13.13s}" "{T:PutRootFH/%13.13s}{T:DelegRet/%13.13s}" "{T:GetAcl/%13.13s}{T:SetAcl/%13.13s}\n"); xo_emit("{:rellckown/%13ju}{:freestateid/%13ju}" "{:putrootfh/%13ju}{:delegret/%13ju}" "{:getacl/%13ju}{:setacl/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RELEASELCKOWN], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_FREESTATEID], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_PUTROOTFH], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_DELEGRETURN], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_GETACL], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SETACL]); xo_emit("{T:ExchangeId/%13.13s}{T:CreateSess/%13.13s}" "{T:DestroySess/%13.13s}{T:DestroyClId/%13.13s}" "{T:LayoutGet/%13.13s}{T:GetDevInfo/%13.13s}\n"); xo_emit("{:exchangeid/%13ju}{:createsess/%13ju}" "{:destroysess/%13ju}{:destroyclid/%13ju}" "{:layoutget/%13ju}{:getdevinfo/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_EXCHANGEID], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_CREATESESSION], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_DESTROYSESSION], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_DESTROYCLIENT], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LAYOUTGET], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_GETDEVICEINFO]); xo_emit("{T:LayoutCommit/%13.13s}{T:LayoutReturn/%13.13s}" "{T:ReclaimCompl/%13.13s}{T:ReadDataS/%13.13s}" "{T:WriteDataS/%13.13s}{T:CommitDataS/%13.13s}\n"); xo_emit("{:layoutcomit/%13ju}{:layoutreturn/%13ju}" "{:reclaimcompl/%13ju}{:readdatas/%13ju}" "{:writedatas/%13ju}{:commitdatas/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LAYOUTCOMMIT], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LAYOUTRETURN], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RECLAIMCOMPL], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READDS], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_WRITEDS], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_COMMITDS]); xo_emit("{T:OpenLayout/%13.13s}{T:CreateLayout/%13.13s}" "{T:BindConnSess/%13.13s}{T:LookupOpen/%13.13s}" "{T:AppendWrite/%13.13s}\n"); xo_emit("{:openlayout/%13ju}{:createlayout/%13ju}" "{:bindconnsess/%13ju}{:lookupopen/%13ju}" "{:appendwrite/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_OPENLAYGET], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_CREATELAYGET], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_BINDCONNTOSESS], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LOOKUPOPEN], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_APPENDWRITE]); xo_close_container("nfsv41"); xo_open_container("nfsv42"); xo_emit("{T:IOAdvise/%13.13s}{T:Allocate/%13.13s}" "{T:Copy/%13.13s}{T:Seek/%13.13s}" "{T:SeekDataS/%13.13s}{T:GetExtattr/%13.13s}\n"); xo_emit("{:ioadvise/%13ju}{:allocate/%13ju}" "{:copy/%13ju}{:seek/%13ju}" "{:seekdatas/%13ju}{:getextattr/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_IOADVISE], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_ALLOCATE], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_COPY], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SEEK], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SEEKDS], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_GETEXTATTR]); xo_emit("{T:SetExtattr/%13.13s}{T:RmExtattr/%13.13s}" "{T:ListExtattr/%13.13s}{T:Deallocate/%13.13s}" "{T:LayoutError/%13.13s}\n"); xo_emit("{:setextattr/%13ju}{:rmextattr/%13ju}" "{:listextattr/%13ju}{:deallocate/%13ju}" "{:layouterror/%13ju}\n", (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SETEXTATTR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RMEXTATTR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LISTEXTATTR], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_DEALLOCATE], (uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LAYOUTERROR]); xo_close_container("nfsv42"); } xo_close_container("operations"); if (printtitle) xo_emit("{T:Client:}\n"); xo_open_container("client"); xo_emit("{T:OpenOwner/%13.13s}{T:Opens/%13.13s}" "{T:LockOwner/%13.13s}{T:Locks/%13.13s}" "{T:Delegs/%13.13s}{T:LocalOwn/%13.13s}\n"); xo_emit("{:openowner/%13ju}{:opens/%13ju}" "{:lockowner/%13ju}{:locks/%13ju}" "{:delegs/%13ju}{:localown/%13ju}\n", (uintmax_t)ext_nfsstats.clopenowners, (uintmax_t)ext_nfsstats.clopens, (uintmax_t)ext_nfsstats.cllockowners, (uintmax_t)ext_nfsstats.cllocks, (uintmax_t)ext_nfsstats.cldelegates, (uintmax_t)ext_nfsstats.cllocalopenowners); xo_emit("{T:LocalOpen/%13.13s}{T:LocalLown/%13.13s}" "{T:LocalLock/%13.13s}{T:Layouts/%13.13s}\n"); xo_emit("{:localopen/%13ju}{:locallown/%13ju}" "{:locallock/%13ju}{:layouts/%13ju}\n", (uintmax_t)ext_nfsstats.cllocalopens, (uintmax_t)ext_nfsstats.cllocallockowners, (uintmax_t)ext_nfsstats.cllocallocks, (uintmax_t)ext_nfsstats.cllayouts); xo_close_container("client"); xo_open_container("rpc"); if (printtitle) xo_emit("{T:Rpc Info:}\n"); xo_emit("{T:TimedOut/%13.13s}{T:Invalid/%13.13s}" "{T:X Replies/%13.13s}{T:Retries/%13.13s}" "{T:Requests/%13.13s}\n"); xo_emit("{:timedout/%13ju}{:invalid/%13ju}" "{:xreplies/%13ju}{:retries/%13ju}" "{:requests/%13ju}\n", (uintmax_t)ext_nfsstats.rpctimeouts, (uintmax_t)ext_nfsstats.rpcinvalid, (uintmax_t)ext_nfsstats.rpcunexpected, (uintmax_t)ext_nfsstats.rpcretries, (uintmax_t)ext_nfsstats.rpcrequests); xo_close_container("rpc"); xo_open_container("cache"); if (printtitle) xo_emit("{T:Cache Info:}\n"); xo_emit("{T:Attr Hits/%13.13s}{T:Attr Misses/%13.13s}" "{T:Lkup Hits/%13.13s}{T:Lkup Misses/%13.13s}\n"); xo_emit("{:attrhits/%13ju}{:attrmisses/%13ju}" "{:lkuphits/%13ju}{:lkupmisses/%13ju}\n", (uintmax_t)ext_nfsstats.attrcache_hits, (uintmax_t)ext_nfsstats.attrcache_misses, (uintmax_t)ext_nfsstats.lookupcache_hits, (uintmax_t)ext_nfsstats.lookupcache_misses); xo_emit("{T:BioR Hits/%13.13s}{T:BioR Misses/%13.13s}" "{T:BioW Hits/%13.13s}{T:BioW Misses/%13.13s}\n"); xo_emit("{:biorhits/%13ju}{:biormisses/%13ju}" "{:biowhits/%13ju}{:biowmisses/%13ju}\n", (uintmax_t)(ext_nfsstats.biocache_reads - ext_nfsstats.read_bios), (uintmax_t)ext_nfsstats.read_bios, (uintmax_t)(ext_nfsstats.biocache_writes - ext_nfsstats.write_bios), (uintmax_t)ext_nfsstats.write_bios); xo_emit("{T:BioRL Hits/%13.13s}{T:BioRL Misses/%13.13s}" "{T:BioD Hits/%13.13s}{T:BioD Misses/%13.13s}\n"); xo_emit("{:biorlhits/%13ju}{:biorlmisses/%13ju}" "{:biodhits/%13ju}{:biodmisses/%13ju}\n", (uintmax_t)(ext_nfsstats.biocache_readlinks - ext_nfsstats.readlink_bios), (uintmax_t)ext_nfsstats.readlink_bios, (uintmax_t)(ext_nfsstats.biocache_readdirs - ext_nfsstats.readdir_bios), (uintmax_t)ext_nfsstats.readdir_bios); xo_emit("{T:DirE Hits/%13.13s}{T:DirE Misses/%13.13s}\n"); xo_emit("{:direhits/%13ju}{:diremisses/%13ju}\n", (uintmax_t)ext_nfsstats.direofcache_hits, (uintmax_t)ext_nfsstats.direofcache_misses); xo_open_container("cache"); xo_close_container("clientstats"); } if (serverOnly != 0) { xo_open_container("serverstats"); xo_open_container("operations"); if (printtitle) xo_emit("{T:Server Info:}\n"); xo_emit("{T:Getattr/%13.13s}{T:Setattr/%13.13s}" "{T:Lookup/%13.13s}{T:Readlink/%13.13s}" "{T:Read/%13.13s}{T:Write/%13.13s}\n"); xo_emit("{:getattr/%13ju}{:setattr/%13ju}{:lookup/%13ju}" "{:readlink/%13ju}{:read/%13ju}{:write/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETATTR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SETATTR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOOKUP], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READLINK], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READ], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_WRITE]); xo_emit("{T:Create/%13.13s}{T:Remove/%13.13s}" "{T:Rename/%13.13s}{T:Link/%13.13s}" "{T:Symlink/%13.13s}{T:Mkdir/%13.13s}\n"); xo_emit("{:create/%13ju}{:remove/%13ju}{:rename/%13ju}" "{:link/%13ju}{:symlink/%13ju}{:mkdir/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_V3CREATE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_REMOVE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RENAME], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LINK], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SYMLINK], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_MKDIR]); xo_emit("{T:Rmdir/%13.13s}{T:Readdir/%13.13s}" "{T:RdirPlus/%13.13s}{T:Access/%13.13s}" "{T:Mknod/%13.13s}{T:Fsstat/%13.13s}\n"); xo_emit("{:rmdir/%13ju}{:readdir/%13ju}{:rdirplus/%13ju}" "{:access/%13ju}{:mknod/%13ju}{:fsstat/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RMDIR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READDIR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READDIRPLUS], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_ACCESS], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_MKNOD], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_FSSTAT]); xo_emit("{T:FSinfo/%13.13s}{T:pathConf/%13.13s}" "{T:Commit/%13.13s}{T:LookupP/%13.13s}" "{T:SetClId/%13.13s}{T:SetClIdCf/%13.13s}\n"); xo_emit("{:fsinfo/%13ju}{:pathconf/%13ju}{:commit/%13ju}" "{:lookupp/%13ju}{:setclientid/%13ju}{:setclientidcfrm/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_FSINFO], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_PATHCONF], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_COMMIT], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOOKUPP], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SETCLIENTID], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SETCLIENTIDCFRM]); xo_emit("{T:Open/%13.13s}{T:OpenAttr/%13.13s}" "{T:OpenDwnGr/%13.13s}{T:OpenCfrm/%13.13s}" "{T:DelePurge/%13.13s}{T:DelRet/%13.13s}\n"); xo_emit("{:open/%13ju}{:openattr/%13ju}{:opendwgr/%13ju}" "{:opencfrm/%13ju}{:delepurge/%13ju}{:delreg/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_OPEN], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_OPENATTR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_OPENDOWNGRADE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_OPENCONFIRM], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_DELEGPURGE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_DELEGRETURN]); xo_emit("{T:GetFH/%13.13s}{T:Lock/%13.13s}" "{T:LockT/%13.13s}{T:LockU/%13.13s}" "{T:Close/%13.13s}{T:Verify/%13.13s}\n"); xo_emit("{:getfh/%13ju}{:lock/%13ju}{:lockt/%13ju}" "{:locku/%13ju}{:close/%13ju}{:verify/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETFH], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOCK], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOCKT], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOCKU], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_CLOSE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_VERIFY]); xo_emit("{T:NVerify/%13.13s}{T:PutFH/%13.13s}" "{T:PutPubFH/%13.13s}{T:PutRootFH/%13.13s}" "{T:Renew/%13.13s}{T:RestoreFH/%13.13s}\n"); xo_emit("{:nverify/%13ju}{:putfh/%13ju}{:putpubfh/%13ju}" "{:putrootfh/%13ju}{:renew/%13ju}{:restore/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_NVERIFY], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_PUTFH], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_PUTPUBFH], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_PUTROOTFH], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RENEW], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RESTOREFH]); xo_emit("{T:SaveFH/%13.13s}{T:Secinfo/%13.13s}" "{T:RelLockOwn/%13.13s}{T:V4Create/%13.13s}\n"); xo_emit("{:savefh/%13ju}{:secinfo/%13ju}{:rellockown/%13ju}" "{:v4create/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SAVEFH], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SECINFO], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RELEASELCKOWN], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_CREATE]); if (nfs41) { xo_open_container("nfsv41"); xo_emit("{T:BackChannelCtrl/%13.13s}{T:BindConnToSess/%13.13s}" "{T:ExchangeID/%13.13s}{T:CreateSess/%13.13s}" "{T:DestroySess/%13.13s}{T:FreeStateID/%13.13s}\n"); xo_emit("{:backchannelctrl/%13ju}{:bindconntosess/%13ju}" "{:exchangeid/%13ju}{:createsess/%13ju}" "{:destroysess/%13ju}{:freestateid/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_BACKCHANNELCTL], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_BINDCONNTOSESS], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_EXCHANGEID], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_CREATESESSION], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_DESTROYSESSION], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_FREESTATEID]), xo_emit("{T:GetDirDeleg/%13.13s}{T:GetDevInfo/%13.13s}" "{T:GetDevList/%13.13s}{T:layoutCommit/%13.13s}" "{T:LayoutGet/%13.13s}{T:LayoutReturn/%13.13s}\n"); xo_emit("{:getdirdeleg/%13ju}{:getdevinfo/%13ju}" "{:getdevlist/%13ju}{:layoutcommit/%13ju}" "{:layoutget/%13ju}{:layoutreturn/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETDIRDELEG], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETDEVINFO], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETDEVLIST], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LAYOUTCOMMIT], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LAYOUTGET], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LAYOUTRETURN]); xo_emit("{T:SecInfNoName/%13.13s}{T:Sequence/%13.13s}" "{T:SetSSV/%13.13s}{T:TestStateID/%13.13s}" "{T:WantDeleg/%13.13s}{T:DestroyClId/%13.13s}\n"); xo_emit("{:secinfnoname/%13ju}{:sequence/%13ju}" "{:setssv/%13ju}{:teststateid/%13ju}{:wantdeleg/%13ju}" "{:destroyclid/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SECINFONONAME], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SEQUENCE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SETSSV], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_TESTSTATEID], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_WANTDELEG], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_DESTROYCLIENTID]); xo_emit("{T:ReclaimCompl/%13.13s}\n"); xo_emit("{:reclaimcompl/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RECLAIMCOMPL]); xo_close_container("nfsv41"); xo_open_container("nfsv42"); xo_emit("{T:Allocate/%13.13s}{T:Copy/%13.13s}" "{T:CopyNotify/%13.13s}{T:Deallocate/%13.13s}" "{T:IOAdvise/%13.13s}{T:LayoutError/%13.13s}\n"); xo_emit("{:allocate/%13ju}{:copy/%13ju}" "{:copynotify/%13ju}{:deallocate/%13ju}" "{:ioadvise/%13ju}{:layouterror/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_ALLOCATE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_COPY], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_COPYNOTIFY], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_DEALLOCATE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_IOADVISE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LAYOUTERROR]); xo_emit("{T:LayoutStats/%13.13s}{T:OffloadCncl/%13.13s}" "{T:OffloadStat/%13.13s}{T:ReadPlus/%13.13s}" "{T:Seek/%13.13s}{T:WriteSame/%13.13s}\n"); xo_emit("{:layoutstats/%13ju}{:offloadcncl/%13ju}" "{:offloadstat/%13ju}{:readplus/%13ju}" "{:seek/%13ju}{:writesame/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LAYOUTSTATS], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_OFFLOADCANCEL], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_OFFLOADSTATUS], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READPLUS], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SEEK], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_WRITESAME]); xo_emit("{T:Clone/%13.13s}{T:GetExtattr/%13.13s}" "{T:SetExtattr/%13.13s}{T:ListExtattr/%13.13s}" "{T:RmExtattr/%13.13s}\n"); xo_emit("{:clone/%13ju}{:getextattr/%13ju}" "{:setextattr/%13ju}{:listextattr/%13ju}" "{:rmextattr/%13ju}\n", (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_CLONE], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETXATTR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SETXATTR], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LISTXATTRS], (uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_REMOVEXATTR]); xo_close_container("nfsv42"); } xo_close_container("operations"); if (printtitle) xo_emit("{T:Server:}\n"); xo_open_container("server"); xo_emit("{T:Clients/%13.13s}{T:OpenOwner/%13.13s}" "{T:Opens/%13.13s}{T:LockOwner/%13.13s}{T:Locks/%13.13s}" "{T:Delegs/%13.13s}\n"); xo_emit("{:clients/%13ju}{:openowner/%13ju}{:opens/%13ju}" "{:lockowner/%13ju}{:locks/%13ju}{:delegs/%13ju}\n", (uintmax_t)ext_nfsstats.srvclients, (uintmax_t)ext_nfsstats.srvopenowners, (uintmax_t)ext_nfsstats.srvopens, (uintmax_t)ext_nfsstats.srvlockowners, (uintmax_t)ext_nfsstats.srvlocks, (uintmax_t)ext_nfsstats.srvdelegates); xo_emit("{T:Layouts/%13.13s}\n"); xo_emit("{:layouts/%13ju}\n", (uintmax_t)ext_nfsstats.srvlayouts); xo_close_container("server"); if (printtitle) xo_emit("{T:Server Cache Stats:}\n"); xo_open_container("cache"); xo_emit("{T:Inprog/%13.13s}" "{T:Non-idem/%13.13s}{T:Misses/%13.13s}" "{T:CacheSize/%13.13s}{T:TCPPeak/%13.13s}\n"); xo_emit("{:inprog/%13ju}{:nonidem/%13ju}" "{:misses/%13ju}{:cachesize/%13ju}{:tcppeak/%13ju}\n", (uintmax_t)ext_nfsstats.srvcache_inproghits, (uintmax_t)ext_nfsstats.srvcache_nonidemdonehits, (uintmax_t)ext_nfsstats.srvcache_misses, (uintmax_t)ext_nfsstats.srvcache_size, (uintmax_t)ext_nfsstats.srvcache_tcppeak); xo_close_container("cache"); xo_close_container("serverstats"); } xo_close_container("nfsv4"); } static void compute_totals(struct nfsstatsv1 *total_stats, struct nfsstatsv1 *cur_stats) { int i; bzero(total_stats, sizeof(*total_stats)); for (i = 0; i < (NFSV42_NOPS + NFSV4OP_FAKENOPS); i++) { total_stats->srvbytes[0] += cur_stats->srvbytes[i]; total_stats->srvops[0] += cur_stats->srvops[i]; bintime_add(&total_stats->srvduration[0], &cur_stats->srvduration[i]); total_stats->srvrpccnt[i] = cur_stats->srvrpccnt[i]; } total_stats->srvstartcnt = cur_stats->srvstartcnt; total_stats->srvdonecnt = cur_stats->srvdonecnt; total_stats->busytime = cur_stats->busytime; } /* * Print a running summary of nfs statistics for the experimental client and/or * server. * Repeat display every interval seconds, showing statistics * collected over that interval. Assumes that interval is non-zero. * First line printed at top of screen is always cumulative. */ static void exp_sidewaysintpr(u_int interval, int clientOnly, int serverOnly, int newStats) { struct nfsstatsv1 nfsstats, lastst, *ext_nfsstatsp; struct nfsstatsv1 curtotal, lasttotal; struct timespec ts, lastts; int hdrcnt = 1; ext_nfsstatsp = &lastst; ext_nfsstatsp->vers = NFSSTATS_V1; if (nfssvc(NFSSVC_GETSTATS | NFSSVC_NEWSTRUCT, ext_nfsstatsp) < 0) err(1, "Can't get stats"); clock_gettime(CLOCK_MONOTONIC, &lastts); compute_totals(&lasttotal, ext_nfsstatsp); sleep(interval); for (;;) { ext_nfsstatsp = &nfsstats; ext_nfsstatsp->vers = NFSSTATS_V1; if (nfssvc(NFSSVC_GETSTATS | NFSSVC_NEWSTRUCT, ext_nfsstatsp) < 0) err(1, "Can't get stats"); clock_gettime(CLOCK_MONOTONIC, &ts); if (--hdrcnt == 0) { printhdr(clientOnly, serverOnly, newStats); if (newStats) hdrcnt = 20; else if (clientOnly && serverOnly) hdrcnt = 10; else hdrcnt = 20; } if (clientOnly && newStats == 0) { printf("%s %6ju %6ju %6ju %6ju %6ju %6ju %6ju %6ju", ((clientOnly && serverOnly) ? "Client:" : ""), (uintmax_t)DELTA(rpccnt[NFSPROC_GETATTR]), (uintmax_t)DELTA(rpccnt[NFSPROC_LOOKUP]), (uintmax_t)DELTA(rpccnt[NFSPROC_READLINK]), (uintmax_t)DELTA(rpccnt[NFSPROC_READ]), (uintmax_t)DELTA(rpccnt[NFSPROC_WRITE]), (uintmax_t)DELTA(rpccnt[NFSPROC_RENAME]), (uintmax_t)DELTA(rpccnt[NFSPROC_ACCESS]), (uintmax_t)(DELTA(rpccnt[NFSPROC_READDIR]) + DELTA(rpccnt[NFSPROC_READDIRPLUS])) ); if (widemode) { printf(" %s %s %s %s %s %s", sperc1(DELTA(attrcache_hits), DELTA(attrcache_misses)), sperc1(DELTA(lookupcache_hits), DELTA(lookupcache_misses)), sperc2(DELTA(biocache_reads), DELTA(read_bios)), sperc2(DELTA(biocache_writes), DELTA(write_bios)), sperc1(DELTA(accesscache_hits), DELTA(accesscache_misses)), sperc2(DELTA(biocache_readdirs), DELTA(readdir_bios)) ); } printf("\n"); } if (serverOnly && newStats) { long double cur_secs, last_secs, etime; long double mbsec; long double kb_per_transfer; long double transfers_per_second; long double ms_per_transfer; uint64_t queue_len; long double busy_pct; int i; cur_secs = ts.tv_sec + ((long double)ts.tv_nsec / 1000000000); last_secs = lastts.tv_sec + ((long double)lastts.tv_nsec / 1000000000); etime = cur_secs - last_secs; compute_totals(&curtotal, &nfsstats); for (i = 0; i < NUM_STAT_TYPES; i++) { compute_new_stats(&nfsstats, &lastst, STAT_TYPE_TO_NFS(i), etime, &mbsec, &kb_per_transfer, &transfers_per_second, &ms_per_transfer, &queue_len, &busy_pct); if (i == STAT_TYPE_COMMIT) { if (widemode == 0) continue; printf("%2.0Lf %7.2Lf ", transfers_per_second, ms_per_transfer); } else { printf("%5.2Lf %5.0Lf %7.2Lf ", kb_per_transfer, transfers_per_second, mbsec); if (widemode) printf("%5.2Lf ", ms_per_transfer); } } compute_new_stats(&curtotal, &lasttotal, 0, etime, &mbsec, &kb_per_transfer, &transfers_per_second, &ms_per_transfer, &queue_len, &busy_pct); printf("%5.2Lf %5.0Lf %7.2Lf %5.2Lf %3ju %3.0Lf\n", kb_per_transfer, transfers_per_second, mbsec, ms_per_transfer, queue_len, busy_pct); } else if (serverOnly) { printf("%s %6ju %6ju %6ju %6ju %6ju %6ju %6ju %6ju", ((clientOnly && serverOnly) ? "Server:" : ""), (uintmax_t)DELTA(srvrpccnt[NFSV4OP_GETATTR]), (uintmax_t)DELTA(srvrpccnt[NFSV4OP_LOOKUP]), (uintmax_t)DELTA(srvrpccnt[NFSV4OP_READLINK]), (uintmax_t)DELTA(srvrpccnt[NFSV4OP_READ]), (uintmax_t)DELTA(srvrpccnt[NFSV4OP_WRITE]), (uintmax_t)DELTA(srvrpccnt[NFSV4OP_RENAME]), (uintmax_t)DELTA(srvrpccnt[NFSV4OP_ACCESS]), (uintmax_t)(DELTA(srvrpccnt[NFSV4OP_READDIR]) + DELTA(srvrpccnt[NFSV4OP_READDIRPLUS]))); printf("\n"); } bcopy(&nfsstats, &lastst, sizeof(lastst)); bcopy(&curtotal, &lasttotal, sizeof(lasttotal)); lastts = ts; fflush(stdout); sleep(interval); } /*NOTREACHED*/ } diff --git a/usr.bin/number/number.c b/usr.bin/number/number.c index e8cf181cb57a..da5d7d2bedab 100644 --- a/usr.bin/number/number.c +++ b/usr.bin/number/number.c @@ -1,285 +1,283 @@ /* * Copyright (c) 1988, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1988, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)number.c 8.3 (Berkeley) 5/4/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #define MAXNUM 65 /* Biggest number we handle. */ static const char *name1[] = { "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", }, *name2[] = { "", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety", }, *name3[] = { "hundred", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sextillion", "septillion", "octillion", "nonillion", "decillion", "undecillion", "duodecillion", "tredecillion", "quattuordecillion", "quindecillion", "sexdecillion", "septendecillion", "octodecillion", "novemdecillion", "vigintillion", }; static void convert(char *); static int number(char *, int); static void pfract(int); static int unit(int, char *); static void usage(void); static int lflag; int main(int argc, char *argv[]) { int ch, first; char line[256]; lflag = 0; while ((ch = getopt(argc, argv, "l")) != -1) switch (ch) { case 'l': lflag = 1; break; case '?': default: usage(); } argc -= optind; argv += optind; if (*argv == NULL) for (first = 1; fgets(line, sizeof(line), stdin) != NULL; first = 0) { if (strchr(line, '\n') == NULL) errx(1, "line too long."); if (!first) (void)printf("...\n"); convert(line); } else for (first = 1; *argv != NULL; first = 0, ++argv) { if (!first) (void)printf("...\n"); convert(*argv); } exit(0); } static void convert(char *line) { int flen, len, rval; char *p, *fraction; flen = 0; fraction = NULL; for (p = line; *p != '\0' && *p != '\n'; ++p) { if (isblank(*p)) { if (p == line) { ++line; continue; } goto badnum; } if (isdigit(*p)) continue; switch (*p) { case '.': if (fraction != NULL) goto badnum; fraction = p + 1; *p = '\0'; break; case '-': if (p == line) break; /* FALLTHROUGH */ default: badnum: errx(1, "illegal number: %s", line); break; } } *p = '\0'; if ((len = strlen(line)) > MAXNUM || (fraction != NULL && ((flen = strlen(fraction)) > MAXNUM))) errx(1, "number too large, max %d digits.", MAXNUM); if (*line == '-') { (void)printf("minus%s", lflag ? " " : "\n"); ++line; --len; } rval = len > 0 ? unit(len, line) : 0; if (fraction != NULL && flen != 0) for (p = fraction; *p != '\0'; ++p) if (*p != '0') { if (rval) (void)printf("%sand%s", lflag ? " " : "", lflag ? " " : "\n"); if (unit(flen, fraction)) { if (lflag) (void)printf(" "); pfract(flen); rval = 1; } break; } if (!rval) (void)printf("zero%s", lflag ? "" : ".\n"); if (lflag) (void)printf("\n"); } static int unit(int len, char *p) { int off, rval; rval = 0; if (len > 3) { if (len % 3) { off = len % 3; len -= off; if (number(p, off)) { rval = 1; (void)printf(" %s%s", name3[len / 3], lflag ? " " : ".\n"); } p += off; } for (; len > 3; p += 3) { len -= 3; if (number(p, 3)) { rval = 1; (void)printf(" %s%s", name3[len / 3], lflag ? " " : ".\n"); } } } if (number(p, len)) { if (!lflag) (void)printf(".\n"); rval = 1; } return (rval); } static int number(char *p, int len) { int val, rval; rval = 0; switch (len) { case 3: if (*p != '0') { rval = 1; (void)printf("%s hundred", name1[*p - '0']); } ++p; /* FALLTHROUGH */ case 2: val = (p[1] - '0') + (p[0] - '0') * 10; if (val) { if (rval) (void)printf(" "); if (val < 20) (void)printf("%s", name1[val]); else { (void)printf("%s", name2[val / 10]); if (val % 10) (void)printf("-%s", name1[val % 10]); } rval = 1; } break; case 1: if (*p != '0') { rval = 1; (void)printf("%s", name1[*p - '0']); } } return (rval); } static void pfract(int len) { static char const * const pref[] = { "", "ten-", "hundred-" }; switch(len) { case 1: (void)printf("tenths.\n"); break; case 2: (void)printf("hundredths.\n"); break; default: (void)printf("%s%sths.\n", pref[len % 3], name3[len / 3]); break; } } static void usage(void) { (void)fprintf(stderr, "usage: number [-l] [# ...]\n"); exit(1); } diff --git a/usr.bin/primes/pattern.c b/usr.bin/primes/pattern.c index 2b30678cf3b5..25739b96b575 100644 --- a/usr.bin/primes/pattern.c +++ b/usr.bin/primes/pattern.c @@ -1,444 +1,442 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Landon Curt Noll. * * 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 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)pattern.c 8.1 (Berkeley) 5/31/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * pattern - the Eratosthenes sieve on odd numbers for 3,5,7,11 and 13 * * By: Landon Curt Noll chongo@toad.com * * chongo /\oo/\ * * To avoid excessive sieves for small factors, we use the table below to * setup our sieve blocks. Each element represents an odd number starting * with 1. All non-zero elements are factors of 3, 5, 7, 11 and 13. */ #include #include "primes.h" const char pattern[] = { 1,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0, 0,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1, 0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,0,0,0,1,0,1, 1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 0,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,1, 1,0,0,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0,0, 1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1, 0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,0,0,1, 0,0,0,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 0,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,0, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,0,0,0,0,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 0,0,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0, 1,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,0,0,0, 1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,1, 0,0,0,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1, 0,0,1,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,0,0,0, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,0,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,0,0,0,0,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 0,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,0, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,0,0,0, 1,0,0,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,0,0,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0, 1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,0,0,0,0,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1, 0,0,1,0,0,0,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,0,0,0,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,0,0,1, 1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,0, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 0,0,0,1,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1, 1,0,1,0,0,0,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0, 1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,0, 0,0,0,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,0, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,0,0,0, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,0,0,0,1 }; const size_t pattern_size = (sizeof(pattern)/sizeof(pattern[0])); diff --git a/usr.bin/primes/pr_tbl.c b/usr.bin/primes/pr_tbl.c index 5bb709330b83..7bf25e994e1f 100644 --- a/usr.bin/primes/pr_tbl.c +++ b/usr.bin/primes/pr_tbl.c @@ -1,548 +1,546 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Landon Curt Noll. * * 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 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)pr_tbl.c 8.1 (Berkeley) 5/31/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * prime - prime table * * By: Landon Curt Noll chongo@toad.com, ...!{sun,tolsoft}!hoptoad!chongo * * chongo /\oo/\ * * We are able to sieve 2^32-1 because this table has primes up to 65537 * and 65537^2 > 2^32-1. */ #include #include "primes.h" const ubig prime[] = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103, 107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199, 211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313, 317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433, 439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563, 569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673, 677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811, 821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941, 947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051, 1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163, 1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279, 1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399, 1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489, 1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601, 1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,1697,1699,1709, 1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789,1801,1811,1823,1831, 1847,1861,1867,1871,1873,1877,1879,1889,1901,1907,1913,1931,1933,1949,1951, 1973,1979,1987,1993,1997,1999,2003,2011,2017,2027,2029,2039,2053,2063,2069, 2081,2083,2087,2089,2099,2111,2113,2129,2131,2137,2141,2143,2153,2161,2179, 2203,2207,2213,2221,2237,2239,2243,2251,2267,2269,2273,2281,2287,2293,2297, 2309,2311,2333,2339,2341,2347,2351,2357,2371,2377,2381,2383,2389,2393,2399, 2411,2417,2423,2437,2441,2447,2459,2467,2473,2477,2503,2521,2531,2539,2543, 2549,2551,2557,2579,2591,2593,2609,2617,2621,2633,2647,2657,2659,2663,2671, 2677,2683,2687,2689,2693,2699,2707,2711,2713,2719,2729,2731,2741,2749,2753, 2767,2777,2789,2791,2797,2801,2803,2819,2833,2837,2843,2851,2857,2861,2879, 2887,2897,2903,2909,2917,2927,2939,2953,2957,2963,2969,2971,2999,3001,3011, 3019,3023,3037,3041,3049,3061,3067,3079,3083,3089,3109,3119,3121,3137,3163, 3167,3169,3181,3187,3191,3203,3209,3217,3221,3229,3251,3253,3257,3259,3271, 3299,3301,3307,3313,3319,3323,3329,3331,3343,3347,3359,3361,3371,3373,3389, 3391,3407,3413,3433,3449,3457,3461,3463,3467,3469,3491,3499,3511,3517,3527, 3529,3533,3539,3541,3547,3557,3559,3571,3581,3583,3593,3607,3613,3617,3623, 3631,3637,3643,3659,3671,3673,3677,3691,3697,3701,3709,3719,3727,3733,3739, 3761,3767,3769,3779,3793,3797,3803,3821,3823,3833,3847,3851,3853,3863,3877, 3881,3889,3907,3911,3917,3919,3923,3929,3931,3943,3947,3967,3989,4001,4003, 4007,4013,4019,4021,4027,4049,4051,4057,4073,4079,4091,4093,4099,4111,4127, 4129,4133,4139,4153,4157,4159,4177,4201,4211,4217,4219,4229,4231,4241,4243, 4253,4259,4261,4271,4273,4283,4289,4297,4327,4337,4339,4349,4357,4363,4373, 4391,4397,4409,4421,4423,4441,4447,4451,4457,4463,4481,4483,4493,4507,4513, 4517,4519,4523,4547,4549,4561,4567,4583,4591,4597,4603,4621,4637,4639,4643, 4649,4651,4657,4663,4673,4679,4691,4703,4721,4723,4729,4733,4751,4759,4783, 4787,4789,4793,4799,4801,4813,4817,4831,4861,4871,4877,4889,4903,4909,4919, 4931,4933,4937,4943,4951,4957,4967,4969,4973,4987,4993,4999,5003,5009,5011, 5021,5023,5039,5051,5059,5077,5081,5087,5099,5101,5107,5113,5119,5147,5153, 5167,5171,5179,5189,5197,5209,5227,5231,5233,5237,5261,5273,5279,5281,5297, 5303,5309,5323,5333,5347,5351,5381,5387,5393,5399,5407,5413,5417,5419,5431, 5437,5441,5443,5449,5471,5477,5479,5483,5501,5503,5507,5519,5521,5527,5531, 5557,5563,5569,5573,5581,5591,5623,5639,5641,5647,5651,5653,5657,5659,5669, 5683,5689,5693,5701,5711,5717,5737,5741,5743,5749,5779,5783,5791,5801,5807, 5813,5821,5827,5839,5843,5849,5851,5857,5861,5867,5869,5879,5881,5897,5903, 5923,5927,5939,5953,5981,5987,6007,6011,6029,6037,6043,6047,6053,6067,6073, 6079,6089,6091,6101,6113,6121,6131,6133,6143,6151,6163,6173,6197,6199,6203, 6211,6217,6221,6229,6247,6257,6263,6269,6271,6277,6287,6299,6301,6311,6317, 6323,6329,6337,6343,6353,6359,6361,6367,6373,6379,6389,6397,6421,6427,6449, 6451,6469,6473,6481,6491,6521,6529,6547,6551,6553,6563,6569,6571,6577,6581, 6599,6607,6619,6637,6653,6659,6661,6673,6679,6689,6691,6701,6703,6709,6719, 6733,6737,6761,6763,6779,6781,6791,6793,6803,6823,6827,6829,6833,6841,6857, 6863,6869,6871,6883,6899,6907,6911,6917,6947,6949,6959,6961,6967,6971,6977, 6983,6991,6997,7001,7013,7019,7027,7039,7043,7057,7069,7079,7103,7109,7121, 7127,7129,7151,7159,7177,7187,7193,7207,7211,7213,7219,7229,7237,7243,7247, 7253,7283,7297,7307,7309,7321,7331,7333,7349,7351,7369,7393,7411,7417,7433, 7451,7457,7459,7477,7481,7487,7489,7499,7507,7517,7523,7529,7537,7541,7547, 7549,7559,7561,7573,7577,7583,7589,7591,7603,7607,7621,7639,7643,7649,7669, 7673,7681,7687,7691,7699,7703,7717,7723,7727,7741,7753,7757,7759,7789,7793, 7817,7823,7829,7841,7853,7867,7873,7877,7879,7883,7901,7907,7919,7927,7933, 7937,7949,7951,7963,7993,8009,8011,8017,8039,8053,8059,8069,8081,8087,8089, 8093,8101,8111,8117,8123,8147,8161,8167,8171,8179,8191,8209,8219,8221,8231, 8233,8237,8243,8263,8269,8273,8287,8291,8293,8297,8311,8317,8329,8353,8363, 8369,8377,8387,8389,8419,8423,8429,8431,8443,8447,8461,8467,8501,8513,8521, 8527,8537,8539,8543,8563,8573,8581,8597,8599,8609,8623,8627,8629,8641,8647, 8663,8669,8677,8681,8689,8693,8699,8707,8713,8719,8731,8737,8741,8747,8753, 8761,8779,8783,8803,8807,8819,8821,8831,8837,8839,8849,8861,8863,8867,8887, 8893,8923,8929,8933,8941,8951,8963,8969,8971,8999,9001,9007,9011,9013,9029, 9041,9043,9049,9059,9067,9091,9103,9109,9127,9133,9137,9151,9157,9161,9173, 9181,9187,9199,9203,9209,9221,9227,9239,9241,9257,9277,9281,9283,9293,9311, 9319,9323,9337,9341,9343,9349,9371,9377,9391,9397,9403,9413,9419,9421,9431, 9433,9437,9439,9461,9463,9467,9473,9479,9491,9497,9511,9521,9533,9539,9547, 9551,9587,9601,9613,9619,9623,9629,9631,9643,9649,9661,9677,9679,9689,9697, 9719,9721,9733,9739,9743,9749,9767,9769,9781,9787,9791,9803,9811,9817,9829, 9833,9839,9851,9857,9859,9871,9883,9887,9901,9907,9923,9929,9931,9941,9949, 9967,9973,10007,10009,10037,10039,10061,10067,10069,10079,10091,10093,10099, 10103,10111,10133,10139,10141,10151,10159,10163,10169,10177,10181,10193,10211, 10223,10243,10247,10253,10259,10267,10271,10273,10289,10301,10303,10313,10321, 10331,10333,10337,10343,10357,10369,10391,10399,10427,10429,10433,10453,10457, 10459,10463,10477,10487,10499,10501,10513,10529,10531,10559,10567,10589,10597, 10601,10607,10613,10627,10631,10639,10651,10657,10663,10667,10687,10691,10709, 10711,10723,10729,10733,10739,10753,10771,10781,10789,10799,10831,10837,10847, 10853,10859,10861,10867,10883,10889,10891,10903,10909,10937,10939,10949,10957, 10973,10979,10987,10993,11003,11027,11047,11057,11059,11069,11071,11083,11087, 11093,11113,11117,11119,11131,11149,11159,11161,11171,11173,11177,11197,11213, 11239,11243,11251,11257,11261,11273,11279,11287,11299,11311,11317,11321,11329, 11351,11353,11369,11383,11393,11399,11411,11423,11437,11443,11447,11467,11471, 11483,11489,11491,11497,11503,11519,11527,11549,11551,11579,11587,11593,11597, 11617,11621,11633,11657,11677,11681,11689,11699,11701,11717,11719,11731,11743, 11777,11779,11783,11789,11801,11807,11813,11821,11827,11831,11833,11839,11863, 11867,11887,11897,11903,11909,11923,11927,11933,11939,11941,11953,11959,11969, 11971,11981,11987,12007,12011,12037,12041,12043,12049,12071,12073,12097,12101, 12107,12109,12113,12119,12143,12149,12157,12161,12163,12197,12203,12211,12227, 12239,12241,12251,12253,12263,12269,12277,12281,12289,12301,12323,12329,12343, 12347,12373,12377,12379,12391,12401,12409,12413,12421,12433,12437,12451,12457, 12473,12479,12487,12491,12497,12503,12511,12517,12527,12539,12541,12547,12553, 12569,12577,12583,12589,12601,12611,12613,12619,12637,12641,12647,12653,12659, 12671,12689,12697,12703,12713,12721,12739,12743,12757,12763,12781,12791,12799, 12809,12821,12823,12829,12841,12853,12889,12893,12899,12907,12911,12917,12919, 12923,12941,12953,12959,12967,12973,12979,12983,13001,13003,13007,13009,13033, 13037,13043,13049,13063,13093,13099,13103,13109,13121,13127,13147,13151,13159, 13163,13171,13177,13183,13187,13217,13219,13229,13241,13249,13259,13267,13291, 13297,13309,13313,13327,13331,13337,13339,13367,13381,13397,13399,13411,13417, 13421,13441,13451,13457,13463,13469,13477,13487,13499,13513,13523,13537,13553, 13567,13577,13591,13597,13613,13619,13627,13633,13649,13669,13679,13681,13687, 13691,13693,13697,13709,13711,13721,13723,13729,13751,13757,13759,13763,13781, 13789,13799,13807,13829,13831,13841,13859,13873,13877,13879,13883,13901,13903, 13907,13913,13921,13931,13933,13963,13967,13997,13999,14009,14011,14029,14033, 14051,14057,14071,14081,14083,14087,14107,14143,14149,14153,14159,14173,14177, 14197,14207,14221,14243,14249,14251,14281,14293,14303,14321,14323,14327,14341, 14347,14369,14387,14389,14401,14407,14411,14419,14423,14431,14437,14447,14449, 14461,14479,14489,14503,14519,14533,14537,14543,14549,14551,14557,14561,14563, 14591,14593,14621,14627,14629,14633,14639,14653,14657,14669,14683,14699,14713, 14717,14723,14731,14737,14741,14747,14753,14759,14767,14771,14779,14783,14797, 14813,14821,14827,14831,14843,14851,14867,14869,14879,14887,14891,14897,14923, 14929,14939,14947,14951,14957,14969,14983,15013,15017,15031,15053,15061,15073, 15077,15083,15091,15101,15107,15121,15131,15137,15139,15149,15161,15173,15187, 15193,15199,15217,15227,15233,15241,15259,15263,15269,15271,15277,15287,15289, 15299,15307,15313,15319,15329,15331,15349,15359,15361,15373,15377,15383,15391, 15401,15413,15427,15439,15443,15451,15461,15467,15473,15493,15497,15511,15527, 15541,15551,15559,15569,15581,15583,15601,15607,15619,15629,15641,15643,15647, 15649,15661,15667,15671,15679,15683,15727,15731,15733,15737,15739,15749,15761, 15767,15773,15787,15791,15797,15803,15809,15817,15823,15859,15877,15881,15887, 15889,15901,15907,15913,15919,15923,15937,15959,15971,15973,15991,16001,16007, 16033,16057,16061,16063,16067,16069,16073,16087,16091,16097,16103,16111,16127, 16139,16141,16183,16187,16189,16193,16217,16223,16229,16231,16249,16253,16267, 16273,16301,16319,16333,16339,16349,16361,16363,16369,16381,16411,16417,16421, 16427,16433,16447,16451,16453,16477,16481,16487,16493,16519,16529,16547,16553, 16561,16567,16573,16603,16607,16619,16631,16633,16649,16651,16657,16661,16673, 16691,16693,16699,16703,16729,16741,16747,16759,16763,16787,16811,16823,16829, 16831,16843,16871,16879,16883,16889,16901,16903,16921,16927,16931,16937,16943, 16963,16979,16981,16987,16993,17011,17021,17027,17029,17033,17041,17047,17053, 17077,17093,17099,17107,17117,17123,17137,17159,17167,17183,17189,17191,17203, 17207,17209,17231,17239,17257,17291,17293,17299,17317,17321,17327,17333,17341, 17351,17359,17377,17383,17387,17389,17393,17401,17417,17419,17431,17443,17449, 17467,17471,17477,17483,17489,17491,17497,17509,17519,17539,17551,17569,17573, 17579,17581,17597,17599,17609,17623,17627,17657,17659,17669,17681,17683,17707, 17713,17729,17737,17747,17749,17761,17783,17789,17791,17807,17827,17837,17839, 17851,17863,17881,17891,17903,17909,17911,17921,17923,17929,17939,17957,17959, 17971,17977,17981,17987,17989,18013,18041,18043,18047,18049,18059,18061,18077, 18089,18097,18119,18121,18127,18131,18133,18143,18149,18169,18181,18191,18199, 18211,18217,18223,18229,18233,18251,18253,18257,18269,18287,18289,18301,18307, 18311,18313,18329,18341,18353,18367,18371,18379,18397,18401,18413,18427,18433, 18439,18443,18451,18457,18461,18481,18493,18503,18517,18521,18523,18539,18541, 18553,18583,18587,18593,18617,18637,18661,18671,18679,18691,18701,18713,18719, 18731,18743,18749,18757,18773,18787,18793,18797,18803,18839,18859,18869,18899, 18911,18913,18917,18919,18947,18959,18973,18979,19001,19009,19013,19031,19037, 19051,19069,19073,19079,19081,19087,19121,19139,19141,19157,19163,19181,19183, 19207,19211,19213,19219,19231,19237,19249,19259,19267,19273,19289,19301,19309, 19319,19333,19373,19379,19381,19387,19391,19403,19417,19421,19423,19427,19429, 19433,19441,19447,19457,19463,19469,19471,19477,19483,19489,19501,19507,19531, 19541,19543,19553,19559,19571,19577,19583,19597,19603,19609,19661,19681,19687, 19697,19699,19709,19717,19727,19739,19751,19753,19759,19763,19777,19793,19801, 19813,19819,19841,19843,19853,19861,19867,19889,19891,19913,19919,19927,19937, 19949,19961,19963,19973,19979,19991,19993,19997,20011,20021,20023,20029,20047, 20051,20063,20071,20089,20101,20107,20113,20117,20123,20129,20143,20147,20149, 20161,20173,20177,20183,20201,20219,20231,20233,20249,20261,20269,20287,20297, 20323,20327,20333,20341,20347,20353,20357,20359,20369,20389,20393,20399,20407, 20411,20431,20441,20443,20477,20479,20483,20507,20509,20521,20533,20543,20549, 20551,20563,20593,20599,20611,20627,20639,20641,20663,20681,20693,20707,20717, 20719,20731,20743,20747,20749,20753,20759,20771,20773,20789,20807,20809,20849, 20857,20873,20879,20887,20897,20899,20903,20921,20929,20939,20947,20959,20963, 20981,20983,21001,21011,21013,21017,21019,21023,21031,21059,21061,21067,21089, 21101,21107,21121,21139,21143,21149,21157,21163,21169,21179,21187,21191,21193, 21211,21221,21227,21247,21269,21277,21283,21313,21317,21319,21323,21341,21347, 21377,21379,21383,21391,21397,21401,21407,21419,21433,21467,21481,21487,21491, 21493,21499,21503,21517,21521,21523,21529,21557,21559,21563,21569,21577,21587, 21589,21599,21601,21611,21613,21617,21647,21649,21661,21673,21683,21701,21713, 21727,21737,21739,21751,21757,21767,21773,21787,21799,21803,21817,21821,21839, 21841,21851,21859,21863,21871,21881,21893,21911,21929,21937,21943,21961,21977, 21991,21997,22003,22013,22027,22031,22037,22039,22051,22063,22067,22073,22079, 22091,22093,22109,22111,22123,22129,22133,22147,22153,22157,22159,22171,22189, 22193,22229,22247,22259,22271,22273,22277,22279,22283,22291,22303,22307,22343, 22349,22367,22369,22381,22391,22397,22409,22433,22441,22447,22453,22469,22481, 22483,22501,22511,22531,22541,22543,22549,22567,22571,22573,22613,22619,22621, 22637,22639,22643,22651,22669,22679,22691,22697,22699,22709,22717,22721,22727, 22739,22741,22751,22769,22777,22783,22787,22807,22811,22817,22853,22859,22861, 22871,22877,22901,22907,22921,22937,22943,22961,22963,22973,22993,23003,23011, 23017,23021,23027,23029,23039,23041,23053,23057,23059,23063,23071,23081,23087, 23099,23117,23131,23143,23159,23167,23173,23189,23197,23201,23203,23209,23227, 23251,23269,23279,23291,23293,23297,23311,23321,23327,23333,23339,23357,23369, 23371,23399,23417,23431,23447,23459,23473,23497,23509,23531,23537,23539,23549, 23557,23561,23563,23567,23581,23593,23599,23603,23609,23623,23627,23629,23633, 23663,23669,23671,23677,23687,23689,23719,23741,23743,23747,23753,23761,23767, 23773,23789,23801,23813,23819,23827,23831,23833,23857,23869,23873,23879,23887, 23893,23899,23909,23911,23917,23929,23957,23971,23977,23981,23993,24001,24007, 24019,24023,24029,24043,24049,24061,24071,24077,24083,24091,24097,24103,24107, 24109,24113,24121,24133,24137,24151,24169,24179,24181,24197,24203,24223,24229, 24239,24247,24251,24281,24317,24329,24337,24359,24371,24373,24379,24391,24407, 24413,24419,24421,24439,24443,24469,24473,24481,24499,24509,24517,24527,24533, 24547,24551,24571,24593,24611,24623,24631,24659,24671,24677,24683,24691,24697, 24709,24733,24749,24763,24767,24781,24793,24799,24809,24821,24841,24847,24851, 24859,24877,24889,24907,24917,24919,24923,24943,24953,24967,24971,24977,24979, 24989,25013,25031,25033,25037,25057,25073,25087,25097,25111,25117,25121,25127, 25147,25153,25163,25169,25171,25183,25189,25219,25229,25237,25243,25247,25253, 25261,25301,25303,25307,25309,25321,25339,25343,25349,25357,25367,25373,25391, 25409,25411,25423,25439,25447,25453,25457,25463,25469,25471,25523,25537,25541, 25561,25577,25579,25583,25589,25601,25603,25609,25621,25633,25639,25643,25657, 25667,25673,25679,25693,25703,25717,25733,25741,25747,25759,25763,25771,25793, 25799,25801,25819,25841,25847,25849,25867,25873,25889,25903,25913,25919,25931, 25933,25939,25943,25951,25969,25981,25997,25999,26003,26017,26021,26029,26041, 26053,26083,26099,26107,26111,26113,26119,26141,26153,26161,26171,26177,26183, 26189,26203,26209,26227,26237,26249,26251,26261,26263,26267,26293,26297,26309, 26317,26321,26339,26347,26357,26371,26387,26393,26399,26407,26417,26423,26431, 26437,26449,26459,26479,26489,26497,26501,26513,26539,26557,26561,26573,26591, 26597,26627,26633,26641,26647,26669,26681,26683,26687,26693,26699,26701,26711, 26713,26717,26723,26729,26731,26737,26759,26777,26783,26801,26813,26821,26833, 26839,26849,26861,26863,26879,26881,26891,26893,26903,26921,26927,26947,26951, 26953,26959,26981,26987,26993,27011,27017,27031,27043,27059,27061,27067,27073, 27077,27091,27103,27107,27109,27127,27143,27179,27191,27197,27211,27239,27241, 27253,27259,27271,27277,27281,27283,27299,27329,27337,27361,27367,27397,27407, 27409,27427,27431,27437,27449,27457,27479,27481,27487,27509,27527,27529,27539, 27541,27551,27581,27583,27611,27617,27631,27647,27653,27673,27689,27691,27697, 27701,27733,27737,27739,27743,27749,27751,27763,27767,27773,27779,27791,27793, 27799,27803,27809,27817,27823,27827,27847,27851,27883,27893,27901,27917,27919, 27941,27943,27947,27953,27961,27967,27983,27997,28001,28019,28027,28031,28051, 28057,28069,28081,28087,28097,28099,28109,28111,28123,28151,28163,28181,28183, 28201,28211,28219,28229,28277,28279,28283,28289,28297,28307,28309,28319,28349, 28351,28387,28393,28403,28409,28411,28429,28433,28439,28447,28463,28477,28493, 28499,28513,28517,28537,28541,28547,28549,28559,28571,28573,28579,28591,28597, 28603,28607,28619,28621,28627,28631,28643,28649,28657,28661,28663,28669,28687, 28697,28703,28711,28723,28729,28751,28753,28759,28771,28789,28793,28807,28813, 28817,28837,28843,28859,28867,28871,28879,28901,28909,28921,28927,28933,28949, 28961,28979,29009,29017,29021,29023,29027,29033,29059,29063,29077,29101,29123, 29129,29131,29137,29147,29153,29167,29173,29179,29191,29201,29207,29209,29221, 29231,29243,29251,29269,29287,29297,29303,29311,29327,29333,29339,29347,29363, 29383,29387,29389,29399,29401,29411,29423,29429,29437,29443,29453,29473,29483, 29501,29527,29531,29537,29567,29569,29573,29581,29587,29599,29611,29629,29633, 29641,29663,29669,29671,29683,29717,29723,29741,29753,29759,29761,29789,29803, 29819,29833,29837,29851,29863,29867,29873,29879,29881,29917,29921,29927,29947, 29959,29983,29989,30011,30013,30029,30047,30059,30071,30089,30091,30097,30103, 30109,30113,30119,30133,30137,30139,30161,30169,30181,30187,30197,30203,30211, 30223,30241,30253,30259,30269,30271,30293,30307,30313,30319,30323,30341,30347, 30367,30389,30391,30403,30427,30431,30449,30467,30469,30491,30493,30497,30509, 30517,30529,30539,30553,30557,30559,30577,30593,30631,30637,30643,30649,30661, 30671,30677,30689,30697,30703,30707,30713,30727,30757,30763,30773,30781,30803, 30809,30817,30829,30839,30841,30851,30853,30859,30869,30871,30881,30893,30911, 30931,30937,30941,30949,30971,30977,30983,31013,31019,31033,31039,31051,31063, 31069,31079,31081,31091,31121,31123,31139,31147,31151,31153,31159,31177,31181, 31183,31189,31193,31219,31223,31231,31237,31247,31249,31253,31259,31267,31271, 31277,31307,31319,31321,31327,31333,31337,31357,31379,31387,31391,31393,31397, 31469,31477,31481,31489,31511,31513,31517,31531,31541,31543,31547,31567,31573, 31583,31601,31607,31627,31643,31649,31657,31663,31667,31687,31699,31721,31723, 31727,31729,31741,31751,31769,31771,31793,31799,31817,31847,31849,31859,31873, 31883,31891,31907,31957,31963,31973,31981,31991,32003,32009,32027,32029,32051, 32057,32059,32063,32069,32077,32083,32089,32099,32117,32119,32141,32143,32159, 32173,32183,32189,32191,32203,32213,32233,32237,32251,32257,32261,32297,32299, 32303,32309,32321,32323,32327,32341,32353,32359,32363,32369,32371,32377,32381, 32401,32411,32413,32423,32429,32441,32443,32467,32479,32491,32497,32503,32507, 32531,32533,32537,32561,32563,32569,32573,32579,32587,32603,32609,32611,32621, 32633,32647,32653,32687,32693,32707,32713,32717,32719,32749,32771,32779,32783, 32789,32797,32801,32803,32831,32833,32839,32843,32869,32887,32909,32911,32917, 32933,32939,32941,32957,32969,32971,32983,32987,32993,32999,33013,33023,33029, 33037,33049,33053,33071,33073,33083,33091,33107,33113,33119,33149,33151,33161, 33179,33181,33191,33199,33203,33211,33223,33247,33287,33289,33301,33311,33317, 33329,33331,33343,33347,33349,33353,33359,33377,33391,33403,33409,33413,33427, 33457,33461,33469,33479,33487,33493,33503,33521,33529,33533,33547,33563,33569, 33577,33581,33587,33589,33599,33601,33613,33617,33619,33623,33629,33637,33641, 33647,33679,33703,33713,33721,33739,33749,33751,33757,33767,33769,33773,33791, 33797,33809,33811,33827,33829,33851,33857,33863,33871,33889,33893,33911,33923, 33931,33937,33941,33961,33967,33997,34019,34031,34033,34039,34057,34061,34123, 34127,34129,34141,34147,34157,34159,34171,34183,34211,34213,34217,34231,34253, 34259,34261,34267,34273,34283,34297,34301,34303,34313,34319,34327,34337,34351, 34361,34367,34369,34381,34403,34421,34429,34439,34457,34469,34471,34483,34487, 34499,34501,34511,34513,34519,34537,34543,34549,34583,34589,34591,34603,34607, 34613,34631,34649,34651,34667,34673,34679,34687,34693,34703,34721,34729,34739, 34747,34757,34759,34763,34781,34807,34819,34841,34843,34847,34849,34871,34877, 34883,34897,34913,34919,34939,34949,34961,34963,34981,35023,35027,35051,35053, 35059,35069,35081,35083,35089,35099,35107,35111,35117,35129,35141,35149,35153, 35159,35171,35201,35221,35227,35251,35257,35267,35279,35281,35291,35311,35317, 35323,35327,35339,35353,35363,35381,35393,35401,35407,35419,35423,35437,35447, 35449,35461,35491,35507,35509,35521,35527,35531,35533,35537,35543,35569,35573, 35591,35593,35597,35603,35617,35671,35677,35729,35731,35747,35753,35759,35771, 35797,35801,35803,35809,35831,35837,35839,35851,35863,35869,35879,35897,35899, 35911,35923,35933,35951,35963,35969,35977,35983,35993,35999,36007,36011,36013, 36017,36037,36061,36067,36073,36083,36097,36107,36109,36131,36137,36151,36161, 36187,36191,36209,36217,36229,36241,36251,36263,36269,36277,36293,36299,36307, 36313,36319,36341,36343,36353,36373,36383,36389,36433,36451,36457,36467,36469, 36473,36479,36493,36497,36523,36527,36529,36541,36551,36559,36563,36571,36583, 36587,36599,36607,36629,36637,36643,36653,36671,36677,36683,36691,36697,36709, 36713,36721,36739,36749,36761,36767,36779,36781,36787,36791,36793,36809,36821, 36833,36847,36857,36871,36877,36887,36899,36901,36913,36919,36923,36929,36931, 36943,36947,36973,36979,36997,37003,37013,37019,37021,37039,37049,37057,37061, 37087,37097,37117,37123,37139,37159,37171,37181,37189,37199,37201,37217,37223, 37243,37253,37273,37277,37307,37309,37313,37321,37337,37339,37357,37361,37363, 37369,37379,37397,37409,37423,37441,37447,37463,37483,37489,37493,37501,37507, 37511,37517,37529,37537,37547,37549,37561,37567,37571,37573,37579,37589,37591, 37607,37619,37633,37643,37649,37657,37663,37691,37693,37699,37717,37747,37781, 37783,37799,37811,37813,37831,37847,37853,37861,37871,37879,37889,37897,37907, 37951,37957,37963,37967,37987,37991,37993,37997,38011,38039,38047,38053,38069, 38083,38113,38119,38149,38153,38167,38177,38183,38189,38197,38201,38219,38231, 38237,38239,38261,38273,38281,38287,38299,38303,38317,38321,38327,38329,38333, 38351,38371,38377,38393,38431,38447,38449,38453,38459,38461,38501,38543,38557, 38561,38567,38569,38593,38603,38609,38611,38629,38639,38651,38653,38669,38671, 38677,38693,38699,38707,38711,38713,38723,38729,38737,38747,38749,38767,38783, 38791,38803,38821,38833,38839,38851,38861,38867,38873,38891,38903,38917,38921, 38923,38933,38953,38959,38971,38977,38993,39019,39023,39041,39043,39047,39079, 39089,39097,39103,39107,39113,39119,39133,39139,39157,39161,39163,39181,39191, 39199,39209,39217,39227,39229,39233,39239,39241,39251,39293,39301,39313,39317, 39323,39341,39343,39359,39367,39371,39373,39383,39397,39409,39419,39439,39443, 39451,39461,39499,39503,39509,39511,39521,39541,39551,39563,39569,39581,39607, 39619,39623,39631,39659,39667,39671,39679,39703,39709,39719,39727,39733,39749, 39761,39769,39779,39791,39799,39821,39827,39829,39839,39841,39847,39857,39863, 39869,39877,39883,39887,39901,39929,39937,39953,39971,39979,39983,39989,40009, 40013,40031,40037,40039,40063,40087,40093,40099,40111,40123,40127,40129,40151, 40153,40163,40169,40177,40189,40193,40213,40231,40237,40241,40253,40277,40283, 40289,40343,40351,40357,40361,40387,40423,40427,40429,40433,40459,40471,40483, 40487,40493,40499,40507,40519,40529,40531,40543,40559,40577,40583,40591,40597, 40609,40627,40637,40639,40693,40697,40699,40709,40739,40751,40759,40763,40771, 40787,40801,40813,40819,40823,40829,40841,40847,40849,40853,40867,40879,40883, 40897,40903,40927,40933,40939,40949,40961,40973,40993,41011,41017,41023,41039, 41047,41051,41057,41077,41081,41113,41117,41131,41141,41143,41149,41161,41177, 41179,41183,41189,41201,41203,41213,41221,41227,41231,41233,41243,41257,41263, 41269,41281,41299,41333,41341,41351,41357,41381,41387,41389,41399,41411,41413, 41443,41453,41467,41479,41491,41507,41513,41519,41521,41539,41543,41549,41579, 41593,41597,41603,41609,41611,41617,41621,41627,41641,41647,41651,41659,41669, 41681,41687,41719,41729,41737,41759,41761,41771,41777,41801,41809,41813,41843, 41849,41851,41863,41879,41887,41893,41897,41903,41911,41927,41941,41947,41953, 41957,41959,41969,41981,41983,41999,42013,42017,42019,42023,42043,42061,42071, 42073,42083,42089,42101,42131,42139,42157,42169,42179,42181,42187,42193,42197, 42209,42221,42223,42227,42239,42257,42281,42283,42293,42299,42307,42323,42331, 42337,42349,42359,42373,42379,42391,42397,42403,42407,42409,42433,42437,42443, 42451,42457,42461,42463,42467,42473,42487,42491,42499,42509,42533,42557,42569, 42571,42577,42589,42611,42641,42643,42649,42667,42677,42683,42689,42697,42701, 42703,42709,42719,42727,42737,42743,42751,42767,42773,42787,42793,42797,42821, 42829,42839,42841,42853,42859,42863,42899,42901,42923,42929,42937,42943,42953, 42961,42967,42979,42989,43003,43013,43019,43037,43049,43051,43063,43067,43093, 43103,43117,43133,43151,43159,43177,43189,43201,43207,43223,43237,43261,43271, 43283,43291,43313,43319,43321,43331,43391,43397,43399,43403,43411,43427,43441, 43451,43457,43481,43487,43499,43517,43541,43543,43573,43577,43579,43591,43597, 43607,43609,43613,43627,43633,43649,43651,43661,43669,43691,43711,43717,43721, 43753,43759,43777,43781,43783,43787,43789,43793,43801,43853,43867,43889,43891, 43913,43933,43943,43951,43961,43963,43969,43973,43987,43991,43997,44017,44021, 44027,44029,44041,44053,44059,44071,44087,44089,44101,44111,44119,44123,44129, 44131,44159,44171,44179,44189,44201,44203,44207,44221,44249,44257,44263,44267, 44269,44273,44279,44281,44293,44351,44357,44371,44381,44383,44389,44417,44449, 44453,44483,44491,44497,44501,44507,44519,44531,44533,44537,44543,44549,44563, 44579,44587,44617,44621,44623,44633,44641,44647,44651,44657,44683,44687,44699, 44701,44711,44729,44741,44753,44771,44773,44777,44789,44797,44809,44819,44839, 44843,44851,44867,44879,44887,44893,44909,44917,44927,44939,44953,44959,44963, 44971,44983,44987,45007,45013,45053,45061,45077,45083,45119,45121,45127,45131, 45137,45139,45161,45179,45181,45191,45197,45233,45247,45259,45263,45281,45289, 45293,45307,45317,45319,45329,45337,45341,45343,45361,45377,45389,45403,45413, 45427,45433,45439,45481,45491,45497,45503,45523,45533,45541,45553,45557,45569, 45587,45589,45599,45613,45631,45641,45659,45667,45673,45677,45691,45697,45707, 45737,45751,45757,45763,45767,45779,45817,45821,45823,45827,45833,45841,45853, 45863,45869,45887,45893,45943,45949,45953,45959,45971,45979,45989,46021,46027, 46049,46051,46061,46073,46091,46093,46099,46103,46133,46141,46147,46153,46171, 46181,46183,46187,46199,46219,46229,46237,46261,46271,46273,46279,46301,46307, 46309,46327,46337,46349,46351,46381,46399,46411,46439,46441,46447,46451,46457, 46471,46477,46489,46499,46507,46511,46523,46549,46559,46567,46573,46589,46591, 46601,46619,46633,46639,46643,46649,46663,46679,46681,46687,46691,46703,46723, 46727,46747,46751,46757,46769,46771,46807,46811,46817,46819,46829,46831,46853, 46861,46867,46877,46889,46901,46919,46933,46957,46993,46997,47017,47041,47051, 47057,47059,47087,47093,47111,47119,47123,47129,47137,47143,47147,47149,47161, 47189,47207,47221,47237,47251,47269,47279,47287,47293,47297,47303,47309,47317, 47339,47351,47353,47363,47381,47387,47389,47407,47417,47419,47431,47441,47459, 47491,47497,47501,47507,47513,47521,47527,47533,47543,47563,47569,47581,47591, 47599,47609,47623,47629,47639,47653,47657,47659,47681,47699,47701,47711,47713, 47717,47737,47741,47743,47777,47779,47791,47797,47807,47809,47819,47837,47843, 47857,47869,47881,47903,47911,47917,47933,47939,47947,47951,47963,47969,47977, 47981,48017,48023,48029,48049,48073,48079,48091,48109,48119,48121,48131,48157, 48163,48179,48187,48193,48197,48221,48239,48247,48259,48271,48281,48299,48311, 48313,48337,48341,48353,48371,48383,48397,48407,48409,48413,48437,48449,48463, 48473,48479,48481,48487,48491,48497,48523,48527,48533,48539,48541,48563,48571, 48589,48593,48611,48619,48623,48647,48649,48661,48673,48677,48679,48731,48733, 48751,48757,48761,48767,48779,48781,48787,48799,48809,48817,48821,48823,48847, 48857,48859,48869,48871,48883,48889,48907,48947,48953,48973,48989,48991,49003, 49009,49019,49031,49033,49037,49043,49057,49069,49081,49103,49109,49117,49121, 49123,49139,49157,49169,49171,49177,49193,49199,49201,49207,49211,49223,49253, 49261,49277,49279,49297,49307,49331,49333,49339,49363,49367,49369,49391,49393, 49409,49411,49417,49429,49433,49451,49459,49463,49477,49481,49499,49523,49529, 49531,49537,49547,49549,49559,49597,49603,49613,49627,49633,49639,49663,49667, 49669,49681,49697,49711,49727,49739,49741,49747,49757,49783,49787,49789,49801, 49807,49811,49823,49831,49843,49853,49871,49877,49891,49919,49921,49927,49937, 49939,49943,49957,49991,49993,49999,50021,50023,50033,50047,50051,50053,50069, 50077,50087,50093,50101,50111,50119,50123,50129,50131,50147,50153,50159,50177, 50207,50221,50227,50231,50261,50263,50273,50287,50291,50311,50321,50329,50333, 50341,50359,50363,50377,50383,50387,50411,50417,50423,50441,50459,50461,50497, 50503,50513,50527,50539,50543,50549,50551,50581,50587,50591,50593,50599,50627, 50647,50651,50671,50683,50707,50723,50741,50753,50767,50773,50777,50789,50821, 50833,50839,50849,50857,50867,50873,50891,50893,50909,50923,50929,50951,50957, 50969,50971,50989,50993,51001,51031,51043,51047,51059,51061,51071,51109,51131, 51133,51137,51151,51157,51169,51193,51197,51199,51203,51217,51229,51239,51241, 51257,51263,51283,51287,51307,51329,51341,51343,51347,51349,51361,51383,51407, 51413,51419,51421,51427,51431,51437,51439,51449,51461,51473,51479,51481,51487, 51503,51511,51517,51521,51539,51551,51563,51577,51581,51593,51599,51607,51613, 51631,51637,51647,51659,51673,51679,51683,51691,51713,51719,51721,51749,51767, 51769,51787,51797,51803,51817,51827,51829,51839,51853,51859,51869,51871,51893, 51899,51907,51913,51929,51941,51949,51971,51973,51977,51991,52009,52021,52027, 52051,52057,52067,52069,52081,52103,52121,52127,52147,52153,52163,52177,52181, 52183,52189,52201,52223,52237,52249,52253,52259,52267,52289,52291,52301,52313, 52321,52361,52363,52369,52379,52387,52391,52433,52453,52457,52489,52501,52511, 52517,52529,52541,52543,52553,52561,52567,52571,52579,52583,52609,52627,52631, 52639,52667,52673,52691,52697,52709,52711,52721,52727,52733,52747,52757,52769, 52783,52807,52813,52817,52837,52859,52861,52879,52883,52889,52901,52903,52919, 52937,52951,52957,52963,52967,52973,52981,52999,53003,53017,53047,53051,53069, 53077,53087,53089,53093,53101,53113,53117,53129,53147,53149,53161,53171,53173, 53189,53197,53201,53231,53233,53239,53267,53269,53279,53281,53299,53309,53323, 53327,53353,53359,53377,53381,53401,53407,53411,53419,53437,53441,53453,53479, 53503,53507,53527,53549,53551,53569,53591,53593,53597,53609,53611,53617,53623, 53629,53633,53639,53653,53657,53681,53693,53699,53717,53719,53731,53759,53773, 53777,53783,53791,53813,53819,53831,53849,53857,53861,53881,53887,53891,53897, 53899,53917,53923,53927,53939,53951,53959,53987,53993,54001,54011,54013,54037, 54049,54059,54083,54091,54101,54121,54133,54139,54151,54163,54167,54181,54193, 54217,54251,54269,54277,54287,54293,54311,54319,54323,54331,54347,54361,54367, 54371,54377,54401,54403,54409,54413,54419,54421,54437,54443,54449,54469,54493, 54497,54499,54503,54517,54521,54539,54541,54547,54559,54563,54577,54581,54583, 54601,54617,54623,54629,54631,54647,54667,54673,54679,54709,54713,54721,54727, 54751,54767,54773,54779,54787,54799,54829,54833,54851,54869,54877,54881,54907, 54917,54919,54941,54949,54959,54973,54979,54983,55001,55009,55021,55049,55051, 55057,55061,55073,55079,55103,55109,55117,55127,55147,55163,55171,55201,55207, 55213,55217,55219,55229,55243,55249,55259,55291,55313,55331,55333,55337,55339, 55343,55351,55373,55381,55399,55411,55439,55441,55457,55469,55487,55501,55511, 55529,55541,55547,55579,55589,55603,55609,55619,55621,55631,55633,55639,55661, 55663,55667,55673,55681,55691,55697,55711,55717,55721,55733,55763,55787,55793, 55799,55807,55813,55817,55819,55823,55829,55837,55843,55849,55871,55889,55897, 55901,55903,55921,55927,55931,55933,55949,55967,55987,55997,56003,56009,56039, 56041,56053,56081,56087,56093,56099,56101,56113,56123,56131,56149,56167,56171, 56179,56197,56207,56209,56237,56239,56249,56263,56267,56269,56299,56311,56333, 56359,56369,56377,56383,56393,56401,56417,56431,56437,56443,56453,56467,56473, 56477,56479,56489,56501,56503,56509,56519,56527,56531,56533,56543,56569,56591, 56597,56599,56611,56629,56633,56659,56663,56671,56681,56687,56701,56711,56713, 56731,56737,56747,56767,56773,56779,56783,56807,56809,56813,56821,56827,56843, 56857,56873,56891,56893,56897,56909,56911,56921,56923,56929,56941,56951,56957, 56963,56983,56989,56993,56999,57037,57041,57047,57059,57073,57077,57089,57097, 57107,57119,57131,57139,57143,57149,57163,57173,57179,57191,57193,57203,57221, 57223,57241,57251,57259,57269,57271,57283,57287,57301,57329,57331,57347,57349, 57367,57373,57383,57389,57397,57413,57427,57457,57467,57487,57493,57503,57527, 57529,57557,57559,57571,57587,57593,57601,57637,57641,57649,57653,57667,57679, 57689,57697,57709,57713,57719,57727,57731,57737,57751,57773,57781,57787,57791, 57793,57803,57809,57829,57839,57847,57853,57859,57881,57899,57901,57917,57923, 57943,57947,57973,57977,57991,58013,58027,58031,58043,58049,58057,58061,58067, 58073,58099,58109,58111,58129,58147,58151,58153,58169,58171,58189,58193,58199, 58207,58211,58217,58229,58231,58237,58243,58271,58309,58313,58321,58337,58363, 58367,58369,58379,58391,58393,58403,58411,58417,58427,58439,58441,58451,58453, 58477,58481,58511,58537,58543,58549,58567,58573,58579,58601,58603,58613,58631, 58657,58661,58679,58687,58693,58699,58711,58727,58733,58741,58757,58763,58771, 58787,58789,58831,58889,58897,58901,58907,58909,58913,58921,58937,58943,58963, 58967,58979,58991,58997,59009,59011,59021,59023,59029,59051,59053,59063,59069, 59077,59083,59093,59107,59113,59119,59123,59141,59149,59159,59167,59183,59197, 59207,59209,59219,59221,59233,59239,59243,59263,59273,59281,59333,59341,59351, 59357,59359,59369,59377,59387,59393,59399,59407,59417,59419,59441,59443,59447, 59453,59467,59471,59473,59497,59509,59513,59539,59557,59561,59567,59581,59611, 59617,59621,59627,59629,59651,59659,59663,59669,59671,59693,59699,59707,59723, 59729,59743,59747,59753,59771,59779,59791,59797,59809,59833,59863,59879,59887, 59921,59929,59951,59957,59971,59981,59999,60013,60017,60029,60037,60041,60077, 60083,60089,60091,60101,60103,60107,60127,60133,60139,60149,60161,60167,60169, 60209,60217,60223,60251,60257,60259,60271,60289,60293,60317,60331,60337,60343, 60353,60373,60383,60397,60413,60427,60443,60449,60457,60493,60497,60509,60521, 60527,60539,60589,60601,60607,60611,60617,60623,60631,60637,60647,60649,60659, 60661,60679,60689,60703,60719,60727,60733,60737,60757,60761,60763,60773,60779, 60793,60811,60821,60859,60869,60887,60889,60899,60901,60913,60917,60919,60923, 60937,60943,60953,60961,61001,61007,61027,61031,61043,61051,61057,61091,61099, 61121,61129,61141,61151,61153,61169,61211,61223,61231,61253,61261,61283,61291, 61297,61331,61333,61339,61343,61357,61363,61379,61381,61403,61409,61417,61441, 61463,61469,61471,61483,61487,61493,61507,61511,61519,61543,61547,61553,61559, 61561,61583,61603,61609,61613,61627,61631,61637,61643,61651,61657,61667,61673, 61681,61687,61703,61717,61723,61729,61751,61757,61781,61813,61819,61837,61843, 61861,61871,61879,61909,61927,61933,61949,61961,61967,61979,61981,61987,61991, 62003,62011,62017,62039,62047,62053,62057,62071,62081,62099,62119,62129,62131, 62137,62141,62143,62171,62189,62191,62201,62207,62213,62219,62233,62273,62297, 62299,62303,62311,62323,62327,62347,62351,62383,62401,62417,62423,62459,62467, 62473,62477,62483,62497,62501,62507,62533,62539,62549,62563,62581,62591,62597, 62603,62617,62627,62633,62639,62653,62659,62683,62687,62701,62723,62731,62743, 62753,62761,62773,62791,62801,62819,62827,62851,62861,62869,62873,62897,62903, 62921,62927,62929,62939,62969,62971,62981,62983,62987,62989,63029,63031,63059, 63067,63073,63079,63097,63103,63113,63127,63131,63149,63179,63197,63199,63211, 63241,63247,63277,63281,63299,63311,63313,63317,63331,63337,63347,63353,63361, 63367,63377,63389,63391,63397,63409,63419,63421,63439,63443,63463,63467,63473, 63487,63493,63499,63521,63527,63533,63541,63559,63577,63587,63589,63599,63601, 63607,63611,63617,63629,63647,63649,63659,63667,63671,63689,63691,63697,63703, 63709,63719,63727,63737,63743,63761,63773,63781,63793,63799,63803,63809,63823, 63839,63841,63853,63857,63863,63901,63907,63913,63929,63949,63977,63997,64007, 64013,64019,64033,64037,64063,64067,64081,64091,64109,64123,64151,64153,64157, 64171,64187,64189,64217,64223,64231,64237,64271,64279,64283,64301,64303,64319, 64327,64333,64373,64381,64399,64403,64433,64439,64451,64453,64483,64489,64499, 64513,64553,64567,64577,64579,64591,64601,64609,64613,64621,64627,64633,64661, 64663,64667,64679,64693,64709,64717,64747,64763,64781,64783,64793,64811,64817, 64849,64853,64871,64877,64879,64891,64901,64919,64921,64927,64937,64951,64969, 64997,65003,65011,65027,65029,65033,65053,65063,65071,65089,65099,65101,65111, 65119,65123,65129,65141,65147,65167,65171,65173,65179,65183,65203,65213,65239, 65257,65267,65269,65287,65293,65309,65323,65327,65353,65357,65371,65381,65393, 65407,65413,65419,65423,65437,65447,65449,65479,65497,65519,65521,65537 }; /* pr_limit - largest prime in the prime table */ const ubig *const pr_limit = &prime[(sizeof(prime)/sizeof(prime[0]))-1]; diff --git a/usr.bin/primes/primes.c b/usr.bin/primes/primes.c index aca63f99cc5e..949ded680d5b 100644 --- a/usr.bin/primes/primes.c +++ b/usr.bin/primes/primes.c @@ -1,329 +1,327 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Landon Curt Noll. * * 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 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)primes.c 8.5 (Berkeley) 5/10/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * primes - generate a table of primes between two values * * By: Landon Curt Noll chongo@toad.com, ...!{sun,tolsoft}!hoptoad!chongo * * chongo /\oo/\ * * usage: * primes [-h] [start [stop]] * * Print primes >= start and < stop. If stop is omitted, * the value 18446744073709551615 (2^64-1) is assumed. If * start is omitted, start is read from standard input. * * validation check: there are 664579 primes between 0 and 10^7 */ #include #include #include #include #include #include #include #include #include #include #include #include #include "primes.h" /* * Eratosthenes sieve table * * We only sieve the odd numbers. The base of our sieve windows are always * odd. If the base of table is 1, table[i] represents 2*i-1. After the * sieve, table[i] == 1 if and only if 2*i-1 is prime. * * We make TABSIZE large to reduce the overhead of inner loop setup. */ static char table[TABSIZE]; /* Eratosthenes sieve of odd numbers */ static int hflag; static void primes(ubig, ubig); static ubig read_num_buf(void); static void usage(void); int main(int argc, char *argv[]) { ubig start; /* where to start generating */ ubig stop; /* don't generate at or above this value */ int ch; char *p; caph_cache_catpages(); if (caph_enter() < 0) err(1, "cap_enter"); while ((ch = getopt(argc, argv, "h")) != -1) switch (ch) { case 'h': hflag++; break; case '?': default: usage(); } argc -= optind; argv += optind; start = 0; stop = (uint64_t)(-1); /* * Convert low and high args. Strtoumax(3) sets errno to * ERANGE if the number is too large, but, if there's * a leading minus sign it returns the negation of the * result of the conversion, which we'd rather disallow. */ switch (argc) { case 2: /* Start and stop supplied on the command line. */ if (argv[0][0] == '-' || argv[1][0] == '-') errx(1, "negative numbers aren't permitted."); errno = 0; start = strtoumax(argv[0], &p, 0); if (errno) err(1, "%s", argv[0]); if (*p != '\0') errx(1, "%s: illegal numeric format.", argv[0]); errno = 0; stop = strtoumax(argv[1], &p, 0); if (errno) err(1, "%s", argv[1]); if (*p != '\0') errx(1, "%s: illegal numeric format.", argv[1]); break; case 1: /* Start on the command line. */ if (argv[0][0] == '-') errx(1, "negative numbers aren't permitted."); errno = 0; start = strtoumax(argv[0], &p, 0); if (errno) err(1, "%s", argv[0]); if (*p != '\0') errx(1, "%s: illegal numeric format.", argv[0]); break; case 0: start = read_num_buf(); break; default: usage(); } if (start > stop) errx(1, "start value must be less than stop value."); primes(start, stop); return (0); } /* * read_num_buf -- * This routine returns a number n, where 0 <= n && n <= BIG. */ static ubig read_num_buf(void) { ubig val; char *p, buf[LINE_MAX]; /* > max number of digits. */ for (;;) { if (fgets(buf, sizeof(buf), stdin) == NULL) { if (ferror(stdin)) err(1, "stdin"); exit(0); } for (p = buf; isblank(*p); ++p); if (*p == '\n' || *p == '\0') continue; if (*p == '-') errx(1, "negative numbers aren't permitted."); errno = 0; val = strtoumax(buf, &p, 0); if (errno) err(1, "%s", buf); if (*p != '\n') errx(1, "%s: illegal numeric format.", buf); return (val); } } /* * primes - sieve and print primes from start up to and but not including stop */ static void primes(ubig start, ubig stop) { char *q; /* sieve spot */ ubig factor; /* index and factor */ char *tab_lim; /* the limit to sieve on the table */ const ubig *p; /* prime table pointer */ ubig fact_lim; /* highest prime for current block */ ubig mod; /* temp storage for mod */ /* * A number of systems can not convert double values into unsigned * longs when the values are larger than the largest signed value. * We don't have this problem, so we can go all the way to BIG. */ if (start < 3) { start = (ubig)2; } if (stop < 3) { stop = (ubig)2; } if (stop <= start) { return; } /* * be sure that the values are odd, or 2 */ if (start != 2 && (start&0x1) == 0) { ++start; } if (stop != 2 && (stop&0x1) == 0) { ++stop; } /* * quick list of primes <= pr_limit */ if (start <= *pr_limit) { /* skip primes up to the start value */ for (p = &prime[0], factor = prime[0]; factor < stop && p <= pr_limit; factor = *(++p)) { if (factor >= start) { printf(hflag ? "%" PRIx64 "\n" : "%" PRIu64 "\n", factor); } } /* return early if we are done */ if (p <= pr_limit) { return; } start = *pr_limit+2; } /* * we shall sieve a bytemap window, note primes and move the window * upward until we pass the stop point */ while (start < stop) { /* * factor out 3, 5, 7, 11 and 13 */ /* initial pattern copy */ factor = (start%(2*3*5*7*11*13))/2; /* starting copy spot */ memcpy(table, &pattern[factor], pattern_size-factor); /* main block pattern copies */ for (fact_lim=pattern_size-factor; fact_lim+pattern_size<=TABSIZE; fact_lim+=pattern_size) { memcpy(&table[fact_lim], pattern, pattern_size); } /* final block pattern copy */ memcpy(&table[fact_lim], pattern, TABSIZE-fact_lim); /* * sieve for primes 17 and higher */ /* note highest useful factor and sieve spot */ if (stop-start > TABSIZE+TABSIZE) { tab_lim = &table[TABSIZE]; /* sieve it all */ fact_lim = sqrt(start+1.0+TABSIZE+TABSIZE); } else { tab_lim = &table[(stop-start)/2]; /* partial sieve */ fact_lim = sqrt(stop+1.0); } /* sieve for factors >= 17 */ factor = 17; /* 17 is first prime to use */ p = &prime[7]; /* 19 is next prime, pi(19)=7 */ do { /* determine the factor's initial sieve point */ mod = start%factor; if (mod & 0x1) { q = &table[(factor-mod)/2]; } else { q = &table[mod ? factor-(mod/2) : 0]; } /* sive for our current factor */ for ( ; q < tab_lim; q += factor) { *q = '\0'; /* sieve out a spot */ } factor = *p++; } while (factor <= fact_lim); /* * print generated primes */ for (q = table; q < tab_lim; ++q, start+=2) { if (*q) { if (start > SIEVEMAX) { if (!isprime(start)) continue; } printf(hflag ? "%" PRIx64 "\n" : "%" PRIu64 "\n", start); } } } } static void usage(void) { fprintf(stderr, "usage: primes [-h] [start [stop]]\n"); exit(1); } diff --git a/usr.bin/printf/printf.c b/usr.bin/printf/printf.c index 94667f381a60..9ce413b750b7 100644 --- a/usr.bin/printf/printf.c +++ b/usr.bin/printf/printf.c @@ -1,688 +1,686 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright 2018 Staysail Systems, Inc. * Copyright 2014 Garrett D'Amore * Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1989, 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. * 3. 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. */ /* * Important: This file is used both as a standalone program /usr/bin/printf * and as a builtin for /bin/sh (#define SHELL). */ #ifndef SHELL #ifndef lint static char const copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #endif #ifndef lint #if 0 static char const sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SHELL #define main printfcmd #include "bltin/bltin.h" #include "options.h" #endif #define PF(f, func) do { \ if (havewidth) \ if (haveprec) \ (void)printf(f, fieldwidth, precision, func); \ else \ (void)printf(f, fieldwidth, func); \ else if (haveprec) \ (void)printf(f, precision, func); \ else \ (void)printf(f, func); \ } while (0) static int asciicode(void); static char *printf_doformat(char *, int *); static int escape(char *, int, size_t *); static int getchr(void); static int getfloating(long double *, int); static int getint(int *); static int getnum(intmax_t *, uintmax_t *, int); static const char *getstr(void); static char *mknum(char *, char); static void usage(void); static const char digits[] = "0123456789"; static char end_fmt[1]; static int myargc; static char **myargv; static char **gargv; static char **maxargv; int main(int argc, char *argv[]) { size_t len; int end, rval; char *format, *fmt, *start; #ifndef SHELL int ch; (void) setlocale(LC_ALL, ""); #endif #ifdef SHELL nextopt(""); argc -= argptr - argv; argv = argptr; #else while ((ch = getopt(argc, argv, "")) != -1) switch (ch) { case '?': default: usage(); return (1); } argc -= optind; argv += optind; #endif if (argc < 1) { usage(); return (1); } #ifdef SHELL INTOFF; #endif /* * Basic algorithm is to scan the format string for conversion * specifications -- once one is found, find out if the field * width or precision is a '*'; if it is, gather up value. Note, * format strings are reused as necessary to use up the provided * arguments, arguments of zero/null string are provided to use * up the format string. */ fmt = format = *argv; escape(fmt, 1, &len); /* backslash interpretation */ rval = end = 0; gargv = ++argv; for (;;) { maxargv = gargv; myargv = gargv; for (myargc = 0; gargv[myargc]; myargc++) /* nop */; start = fmt; while (fmt < format + len) { if (fmt[0] == '%') { fwrite(start, 1, fmt - start, stdout); if (fmt[1] == '%') { /* %% prints a % */ putchar('%'); fmt += 2; } else { fmt = printf_doformat(fmt, &rval); if (fmt == NULL || fmt == end_fmt) { #ifdef SHELL INTON; #endif return (fmt == NULL ? 1 : rval); } end = 0; } start = fmt; } else fmt++; if (gargv > maxargv) maxargv = gargv; } gargv = maxargv; if (end == 1) { warnx("missing format character"); #ifdef SHELL INTON; #endif return (1); } fwrite(start, 1, fmt - start, stdout); if (!*gargv) { #ifdef SHELL INTON; #endif return (rval); } /* Restart at the beginning of the format string. */ fmt = format; end = 1; } /* NOTREACHED */ } static char * printf_doformat(char *fmt, int *rval) { static const char skip1[] = "#'-+ 0"; int fieldwidth, haveprec, havewidth, mod_ldbl, precision; char convch, nextch; char start[strlen(fmt) + 1]; char **fargv; char *dptr; int l; dptr = start; *dptr++ = '%'; *dptr = 0; fmt++; /* look for "n$" field index specifier */ l = strspn(fmt, digits); if ((l > 0) && (fmt[l] == '$')) { int idx = atoi(fmt); if (idx <= myargc) { gargv = &myargv[idx - 1]; } else { gargv = &myargv[myargc]; } if (gargv > maxargv) maxargv = gargv; fmt += l + 1; /* save format argument */ fargv = gargv; } else { fargv = NULL; } /* skip to field width */ while (*fmt && strchr(skip1, *fmt) != NULL) { *dptr++ = *fmt++; *dptr = 0; } if (*fmt == '*') { fmt++; l = strspn(fmt, digits); if ((l > 0) && (fmt[l] == '$')) { int idx = atoi(fmt); if (fargv == NULL) { warnx("incomplete use of n$"); return (NULL); } if (idx <= myargc) { gargv = &myargv[idx - 1]; } else { gargv = &myargv[myargc]; } fmt += l + 1; } else if (fargv != NULL) { warnx("incomplete use of n$"); return (NULL); } if (getint(&fieldwidth)) return (NULL); if (gargv > maxargv) maxargv = gargv; havewidth = 1; *dptr++ = '*'; *dptr = 0; } else { havewidth = 0; /* skip to possible '.', get following precision */ while (isdigit(*fmt)) { *dptr++ = *fmt++; *dptr = 0; } } if (*fmt == '.') { /* precision present? */ fmt++; *dptr++ = '.'; if (*fmt == '*') { fmt++; l = strspn(fmt, digits); if ((l > 0) && (fmt[l] == '$')) { int idx = atoi(fmt); if (fargv == NULL) { warnx("incomplete use of n$"); return (NULL); } if (idx <= myargc) { gargv = &myargv[idx - 1]; } else { gargv = &myargv[myargc]; } fmt += l + 1; } else if (fargv != NULL) { warnx("incomplete use of n$"); return (NULL); } if (getint(&precision)) return (NULL); if (gargv > maxargv) maxargv = gargv; haveprec = 1; *dptr++ = '*'; *dptr = 0; } else { haveprec = 0; /* skip to conversion char */ while (isdigit(*fmt)) { *dptr++ = *fmt++; *dptr = 0; } } } else haveprec = 0; if (!*fmt) { warnx("missing format character"); return (NULL); } *dptr++ = *fmt; *dptr = 0; /* * Look for a length modifier. POSIX doesn't have these, so * we only support them for floating-point conversions, which * are extensions. This is useful because the L modifier can * be used to gain extra range and precision, while omitting * it is more likely to produce consistent results on different * architectures. This is not so important for integers * because overflow is the only bad thing that can happen to * them, but consider the command printf %a 1.1 */ if (*fmt == 'L') { mod_ldbl = 1; fmt++; if (!strchr("aAeEfFgG", *fmt)) { warnx("bad modifier L for %%%c", *fmt); return (NULL); } } else { mod_ldbl = 0; } /* save the current arg offset, and set to the format arg */ if (fargv != NULL) { gargv = fargv; } convch = *fmt; nextch = *++fmt; *fmt = '\0'; switch (convch) { case 'b': { size_t len; char *p; int getout; /* Convert "b" to "s" for output. */ start[strlen(start) - 1] = 's'; if ((p = strdup(getstr())) == NULL) { warnx("%s", strerror(ENOMEM)); return (NULL); } getout = escape(p, 0, &len); PF(start, p); /* Restore format for next loop. */ free(p); if (getout) return (end_fmt); break; } case 'c': { char p; p = getchr(); if (p != '\0') PF(start, p); break; } case 's': { const char *p; p = getstr(); PF(start, p); break; } case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { char *f; intmax_t val; uintmax_t uval; int signedconv; signedconv = (convch == 'd' || convch == 'i'); if ((f = mknum(start, convch)) == NULL) return (NULL); if (getnum(&val, &uval, signedconv)) *rval = 1; if (signedconv) PF(f, val); else PF(f, uval); break; } case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': { long double p; if (getfloating(&p, mod_ldbl)) *rval = 1; if (mod_ldbl) PF(start, p); else PF(start, (double)p); break; } default: warnx("illegal format character %c", convch); return (NULL); } *fmt = nextch; /* return the gargv to the next element */ return (fmt); } static char * mknum(char *str, char ch) { static char *copy; static size_t copy_size; char *newcopy; size_t len, newlen; len = strlen(str) + 2; if (len > copy_size) { newlen = ((len + 1023) >> 10) << 10; if ((newcopy = realloc(copy, newlen)) == NULL) { warnx("%s", strerror(ENOMEM)); return (NULL); } copy = newcopy; copy_size = newlen; } memmove(copy, str, len - 3); copy[len - 3] = 'j'; copy[len - 2] = ch; copy[len - 1] = '\0'; return (copy); } static int escape(char *fmt, int percent, size_t *len) { char *save, *store, c; int value; for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) { if (c != '\\') { *store = c; continue; } switch (*++fmt) { case '\0': /* EOS, user error */ *store = '\\'; *++store = '\0'; *len = store - save; return (0); case '\\': /* backslash */ case '\'': /* single quote */ *store = *fmt; break; case 'a': /* bell/alert */ *store = '\a'; break; case 'b': /* backspace */ *store = '\b'; break; case 'c': if (!percent) { *store = '\0'; *len = store - save; return (1); } *store = 'c'; break; case 'f': /* form-feed */ *store = '\f'; break; case 'n': /* newline */ *store = '\n'; break; case 'r': /* carriage-return */ *store = '\r'; break; case 't': /* horizontal tab */ *store = '\t'; break; case 'v': /* vertical tab */ *store = '\v'; break; /* octal constant */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c = (!percent && *fmt == '0') ? 4 : 3; for (value = 0; c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { value <<= 3; value += *fmt - '0'; } --fmt; if (percent && value == '%') { *store++ = '%'; *store = '%'; } else *store = (char)value; break; default: *store = *fmt; break; } } *store = '\0'; *len = store - save; return (0); } static int getchr(void) { if (!*gargv) return ('\0'); return ((int)**gargv++); } static const char * getstr(void) { if (!*gargv) return (""); return (*gargv++); } static int getint(int *ip) { intmax_t val; uintmax_t uval; int rval; if (getnum(&val, &uval, 1)) return (1); rval = 0; if (val < INT_MIN || val > INT_MAX) { warnx("%s: %s", *gargv, strerror(ERANGE)); rval = 1; } *ip = (int)val; return (rval); } static int getnum(intmax_t *ip, uintmax_t *uip, int signedconv) { char *ep; int rval; if (!*gargv) { *ip = *uip = 0; return (0); } if (**gargv == '"' || **gargv == '\'') { if (signedconv) *ip = asciicode(); else *uip = asciicode(); return (0); } rval = 0; errno = 0; if (signedconv) *ip = strtoimax(*gargv, &ep, 0); else *uip = strtoumax(*gargv, &ep, 0); if (ep == *gargv) { warnx("%s: expected numeric value", *gargv); rval = 1; } else if (*ep != '\0') { warnx("%s: not completely converted", *gargv); rval = 1; } if (errno == ERANGE) { warnx("%s: %s", *gargv, strerror(ERANGE)); rval = 1; } ++gargv; return (rval); } static int getfloating(long double *dp, int mod_ldbl) { char *ep; int rval; if (!*gargv) { *dp = 0.0; return (0); } if (**gargv == '"' || **gargv == '\'') { *dp = asciicode(); return (0); } rval = 0; errno = 0; if (mod_ldbl) *dp = strtold(*gargv, &ep); else *dp = strtod(*gargv, &ep); if (ep == *gargv) { warnx("%s: expected numeric value", *gargv); rval = 1; } else if (*ep != '\0') { warnx("%s: not completely converted", *gargv); rval = 1; } if (errno == ERANGE) { warnx("%s: %s", *gargv, strerror(ERANGE)); rval = 1; } ++gargv; return (rval); } static int asciicode(void) { int ch; wchar_t wch; mbstate_t mbs; ch = (unsigned char)**gargv; if (ch == '\'' || ch == '"') { memset(&mbs, 0, sizeof(mbs)); switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) { case (size_t)-2: case (size_t)-1: wch = (unsigned char)gargv[0][1]; break; case 0: wch = 0; break; } ch = wch; } ++gargv; return (ch); } static void usage(void) { (void)fprintf(stderr, "usage: printf format [arguments ...]\n"); } diff --git a/usr.bin/showmount/showmount.c b/usr.bin/showmount/showmount.c index d2b9f46792ba..f87965e107fc 100644 --- a/usr.bin/showmount/showmount.c +++ b/usr.bin/showmount/showmount.c @@ -1,428 +1,426 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * 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 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993, 1995\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)showmount.c 8.3 (Berkeley) 3/29/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Constant defs */ #define ALL 1 #define DIRS 2 #define DODUMP 0x1 #define DOEXPORTS 0x2 #define DOPARSABLEEXPORTS 0x4 struct mountlist { struct mountlist *ml_left; struct mountlist *ml_right; char ml_host[MNTNAMLEN+1]; char ml_dirp[MNTPATHLEN+1]; }; struct grouplist { struct grouplist *gr_next; char gr_name[MNTNAMLEN+1]; }; struct exportslist { struct exportslist *ex_next; struct grouplist *ex_groups; char ex_dirp[MNTPATHLEN+1]; }; static struct mountlist *mntdump; static struct exportslist *exportslist; static int type = 0; void print_dump(struct mountlist *); static void usage(void) __dead2; int xdr_mntdump(XDR *, struct mountlist **); int xdr_exportslist(XDR *, struct exportslist **); int tcp_callrpc(const char *host, int prognum, int versnum, int procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out); static const struct option long_opts[] = { { "all", no_argument, NULL, 'a' }, { "directories", no_argument, NULL, 'd' }, { "exports-script", no_argument, NULL, 'E' }, { "exports", no_argument, NULL, 'e' }, { NULL, 0, NULL, 0 }, }; /* * This command queries the NFS mount daemon for it's mount list and/or * it's exports list and prints them out. * See "NFS: Network File System Protocol Specification, RFC1094, Appendix A" * and the "Network File System Protocol XXX.." * for detailed information on the protocol. */ int main(int argc, char **argv) { char strvised[MNTPATHLEN * 4 + 1]; register struct exportslist *exp; register struct grouplist *grp; register int rpcs = 0, mntvers = 3; const char *host; int ch, estat, nbytes; while ((ch = getopt_long(argc, argv, "+adEe13", long_opts, NULL)) != -1) switch (ch) { case 'a': if (type == 0) { type = ALL; rpcs |= DODUMP; } else usage(); break; case 'd': if (type == 0) { type = DIRS; rpcs |= DODUMP; } else usage(); break; case 'E': rpcs |= DOPARSABLEEXPORTS; break; case 'e': rpcs |= DOEXPORTS; break; case '1': mntvers = 1; break; case '3': mntvers = 3; break; case '?': default: usage(); } argc -= optind; argv += optind; if ((rpcs & DOPARSABLEEXPORTS) != 0) { if ((rpcs & DOEXPORTS) != 0) errx(1, "-E cannot be used with -e"); if ((rpcs & DODUMP) != 0) errx(1, "-E cannot be used with -a or -d"); } if (argc > 0) host = *argv; else host = "localhost"; if (rpcs == 0) rpcs = DODUMP; if (rpcs & DODUMP) if ((estat = tcp_callrpc(host, MOUNTPROG, mntvers, MOUNTPROC_DUMP, (xdrproc_t)xdr_void, (char *)0, (xdrproc_t)xdr_mntdump, (char *)&mntdump)) != 0) { clnt_perrno(estat); errx(1, "can't do mountdump rpc"); } if (rpcs & (DOEXPORTS | DOPARSABLEEXPORTS)) if ((estat = tcp_callrpc(host, MOUNTPROG, mntvers, MOUNTPROC_EXPORT, (xdrproc_t)xdr_void, (char *)0, (xdrproc_t)xdr_exportslist, (char *)&exportslist)) != 0) { clnt_perrno(estat); errx(1, "can't do exports rpc"); } /* Now just print out the results */ if (rpcs & DODUMP) { switch (type) { case ALL: printf("All mount points on %s:\n", host); break; case DIRS: printf("Directories on %s:\n", host); break; default: printf("Hosts on %s:\n", host); break; } print_dump(mntdump); } if (rpcs & DOEXPORTS) { printf("Exports list on %s:\n", host); exp = exportslist; while (exp) { printf("%-34s ", exp->ex_dirp); grp = exp->ex_groups; if (grp == NULL) { printf("Everyone\n"); } else { while (grp) { printf("%s ", grp->gr_name); grp = grp->gr_next; } printf("\n"); } exp = exp->ex_next; } } if (rpcs & DOPARSABLEEXPORTS) { exp = exportslist; while (exp) { nbytes = strsnvis(strvised, sizeof(strvised), exp->ex_dirp, VIS_GLOB | VIS_NL, "\"'$"); if (nbytes == -1) err(1, "strsnvis"); printf("%s\n", strvised); exp = exp->ex_next; } } exit(0); } /* * tcp_callrpc has the same interface as callrpc, but tries to * use tcp as transport method in order to handle large replies. */ int tcp_callrpc(const char *host, int prognum, int versnum, int procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out) { CLIENT *client; struct timeval timeout; int rval; if ((client = clnt_create(host, prognum, versnum, "tcp")) == NULL && (client = clnt_create(host, prognum, versnum, "udp")) == NULL) return ((int) rpc_createerr.cf_stat); timeout.tv_sec = 25; timeout.tv_usec = 0; rval = (int) clnt_call(client, procnum, inproc, in, outproc, out, timeout); clnt_destroy(client); return rval; } /* * Xdr routine for retrieving the mount dump list */ int xdr_mntdump(XDR *xdrsp, struct mountlist **mlp) { register struct mountlist *mp; register struct mountlist *tp; register struct mountlist **otp; int val, val2; int bool; char *strp; *mlp = (struct mountlist *)0; if (!xdr_bool(xdrsp, &bool)) return (0); while (bool) { mp = (struct mountlist *)malloc(sizeof(struct mountlist)); if (mp == NULL) return (0); mp->ml_left = mp->ml_right = (struct mountlist *)0; strp = mp->ml_host; if (!xdr_string(xdrsp, &strp, MNTNAMLEN)) { free(mp); return (0); } strp = mp->ml_dirp; if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) { free(mp); return (0); } /* * Build a binary tree on sorted order of either host or dirp. * Drop any duplications. */ if (*mlp == NULL) { *mlp = mp; } else { tp = *mlp; while (tp) { val = strcmp(mp->ml_host, tp->ml_host); val2 = strcmp(mp->ml_dirp, tp->ml_dirp); switch (type) { case ALL: if (val == 0) { if (val2 == 0) { free((caddr_t)mp); goto next; } val = val2; } break; case DIRS: if (val2 == 0) { free((caddr_t)mp); goto next; } val = val2; break; default: if (val == 0) { free((caddr_t)mp); goto next; } break; } if (val < 0) { otp = &tp->ml_left; tp = tp->ml_left; } else { otp = &tp->ml_right; tp = tp->ml_right; } } *otp = mp; } next: if (!xdr_bool(xdrsp, &bool)) return (0); } return (1); } /* * Xdr routine to retrieve exports list */ int xdr_exportslist(XDR *xdrsp, struct exportslist **exp) { register struct exportslist *ep; register struct grouplist *gp; int bool, grpbool; char *strp; *exp = (struct exportslist *)0; if (!xdr_bool(xdrsp, &bool)) return (0); while (bool) { ep = (struct exportslist *)malloc(sizeof(struct exportslist)); if (ep == NULL) return (0); ep->ex_groups = (struct grouplist *)0; strp = ep->ex_dirp; if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) return (0); if (!xdr_bool(xdrsp, &grpbool)) return (0); while (grpbool) { gp = (struct grouplist *)malloc(sizeof(struct grouplist)); if (gp == NULL) return (0); strp = gp->gr_name; if (!xdr_string(xdrsp, &strp, MNTNAMLEN)) return (0); gp->gr_next = ep->ex_groups; ep->ex_groups = gp; if (!xdr_bool(xdrsp, &grpbool)) return (0); } ep->ex_next = *exp; *exp = ep; if (!xdr_bool(xdrsp, &bool)) return (0); } return (1); } static void usage(void) { fprintf(stderr, "usage: showmount [-a | -d] [-e3] [host]\n"); exit(1); } /* * Print the binary tree in inorder so that output is sorted. */ void print_dump(struct mountlist *mp) { if (mp == NULL) return; if (mp->ml_left) print_dump(mp->ml_left); switch (type) { case ALL: printf("%s:%s\n", mp->ml_host, mp->ml_dirp); break; case DIRS: printf("%s\n", mp->ml_dirp); break; default: printf("%s\n", mp->ml_host); break; } if (mp->ml_right) print_dump(mp->ml_right); } diff --git a/usr.bin/tee/tee.c b/usr.bin/tee/tee.c index 5646930a1c98..c4fe945d6d12 100644 --- a/usr.bin/tee/tee.c +++ b/usr.bin/tee/tee.c @@ -1,160 +1,158 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)tee.c 8.1 (Berkeley) 6/6/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct entry { int fd; const char *name; STAILQ_ENTRY(entry) entries; }; static STAILQ_HEAD(, entry) head = STAILQ_HEAD_INITIALIZER(head); static void add(int, const char *); static void usage(void) __dead2; int main(int argc, char *argv[]) { struct entry *p; int n, fd, rval, wval; char *bp; int append, ch, exitval; char *buf; #define BSIZE (8 * 1024) append = 0; while ((ch = getopt(argc, argv, "ai")) != -1) switch((char)ch) { case 'a': append = 1; break; case 'i': (void)signal(SIGINT, SIG_IGN); break; case '?': default: usage(); } argv += optind; argc -= optind; if ((buf = malloc(BSIZE)) == NULL) err(1, "malloc"); if (caph_limit_stdin() == -1 || caph_limit_stderr() == -1) err(EXIT_FAILURE, "unable to limit stdio"); add(STDOUT_FILENO, "stdout"); for (exitval = 0; *argv; ++argv) if ((fd = open(*argv, append ? O_WRONLY|O_CREAT|O_APPEND : O_WRONLY|O_CREAT|O_TRUNC, DEFFILEMODE)) < 0) { warn("%s", *argv); exitval = 1; } else add(fd, *argv); if (caph_enter() < 0) err(EXIT_FAILURE, "unable to enter capability mode"); while ((rval = read(STDIN_FILENO, buf, BSIZE)) > 0) STAILQ_FOREACH(p, &head, entries) { n = rval; bp = buf; do { if ((wval = write(p->fd, bp, n)) == -1) { warn("%s", p->name); exitval = 1; break; } bp += wval; } while (n -= wval); } if (rval < 0) err(1, "read"); exit(exitval); } static void usage(void) { (void)fprintf(stderr, "usage: tee [-ai] [file ...]\n"); exit(1); } static void add(int fd, const char *name) { struct entry *p; cap_rights_t rights; if (fd == STDOUT_FILENO) { if (caph_limit_stdout() == -1) err(EXIT_FAILURE, "unable to limit stdout"); } else { cap_rights_init(&rights, CAP_WRITE, CAP_FSTAT); if (caph_rights_limit(fd, &rights) < 0) err(EXIT_FAILURE, "unable to limit rights"); } if ((p = malloc(sizeof(struct entry))) == NULL) err(1, "malloc"); p->fd = fd; p->name = name; STAILQ_INSERT_HEAD(&head, p, entries); } diff --git a/usr.bin/time/time.c b/usr.bin/time/time.c index 64063212170d..8e45f1283317 100644 --- a/usr.bin/time/time.c +++ b/usr.bin/time/time.c @@ -1,313 +1,311 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1987, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1987, 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)time.c 8.1 (Berkeley) 6/6/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int getstathz(void); static void humantime(FILE *, long, long); static void showtime(FILE *, struct timespec *, struct timespec *, struct rusage *); static void siginfo(int); static void usage(void) __dead2; static sig_atomic_t siginfo_recvd; static char decimal_point; static struct timespec before_ts; static int hflag, pflag; int main(int argc, char **argv) { int aflag, ch, lflag, status; int exitonsig; pid_t pid; struct rlimit rl; struct rusage ru; struct timespec after; char *ofn = NULL; FILE *out = stderr; (void) setlocale(LC_NUMERIC, ""); decimal_point = localeconv()->decimal_point[0]; aflag = hflag = lflag = pflag = 0; while ((ch = getopt(argc, argv, "ahlo:p")) != -1) switch((char)ch) { case 'a': aflag = 1; break; case 'h': hflag = 1; break; case 'l': lflag = 1; break; case 'o': ofn = optarg; break; case 'p': pflag = 1; break; case '?': default: usage(); } if (!(argc -= optind)) exit(0); argv += optind; if (ofn) { if ((out = fopen(ofn, aflag ? "ae" : "we")) == NULL) err(1, "%s", ofn); setvbuf(out, (char *)NULL, _IONBF, (size_t)0); } if (clock_gettime(CLOCK_MONOTONIC, &before_ts)) err(1, "clock_gettime"); switch(pid = fork()) { case -1: /* error */ err(1, "time"); /* NOTREACHED */ case 0: /* child */ execvp(*argv, argv); err(errno == ENOENT ? 127 : 126, "%s", *argv); /* NOTREACHED */ } /* parent */ (void)signal(SIGINT, SIG_IGN); (void)signal(SIGQUIT, SIG_IGN); siginfo_recvd = 0; (void)signal(SIGINFO, siginfo); (void)siginterrupt(SIGINFO, 1); while (wait4(pid, &status, 0, &ru) != pid) { if (siginfo_recvd) { siginfo_recvd = 0; if (clock_gettime(CLOCK_MONOTONIC, &after)) err(1, "clock_gettime"); getrusage(RUSAGE_CHILDREN, &ru); showtime(stdout, &before_ts, &after, &ru); } } if (clock_gettime(CLOCK_MONOTONIC, &after)) err(1, "clock_gettime"); if ( ! WIFEXITED(status)) warnx("command terminated abnormally"); exitonsig = WIFSIGNALED(status) ? WTERMSIG(status) : 0; showtime(out, &before_ts, &after, &ru); if (lflag) { int hz = getstathz(); u_long ticks; ticks = hz * (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) + hz * (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000000; /* * If our round-off on the tick calculation still puts us at 0, * then always assume at least one tick. */ if (ticks == 0) ticks = 1; fprintf(out, "%10ld %s\n", ru.ru_maxrss, "maximum resident set size"); fprintf(out, "%10ld %s\n", ru.ru_ixrss / ticks, "average shared memory size"); fprintf(out, "%10ld %s\n", ru.ru_idrss / ticks, "average unshared data size"); fprintf(out, "%10ld %s\n", ru.ru_isrss / ticks, "average unshared stack size"); fprintf(out, "%10ld %s\n", ru.ru_minflt, "page reclaims"); fprintf(out, "%10ld %s\n", ru.ru_majflt, "page faults"); fprintf(out, "%10ld %s\n", ru.ru_nswap, "swaps"); fprintf(out, "%10ld %s\n", ru.ru_inblock, "block input operations"); fprintf(out, "%10ld %s\n", ru.ru_oublock, "block output operations"); fprintf(out, "%10ld %s\n", ru.ru_msgsnd, "messages sent"); fprintf(out, "%10ld %s\n", ru.ru_msgrcv, "messages received"); fprintf(out, "%10ld %s\n", ru.ru_nsignals, "signals received"); fprintf(out, "%10ld %s\n", ru.ru_nvcsw, "voluntary context switches"); fprintf(out, "%10ld %s\n", ru.ru_nivcsw, "involuntary context switches"); } /* * If the child has exited on a signal, exit on the same * signal, too, in order to reproduce the child's exit status. * However, avoid actually dumping core from the current process. */ if (exitonsig) { if (signal(exitonsig, SIG_DFL) == SIG_ERR) warn("signal"); else { rl.rlim_max = rl.rlim_cur = 0; if (setrlimit(RLIMIT_CORE, &rl) == -1) warn("setrlimit"); kill(getpid(), exitonsig); } } exit (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE); } static void usage(void) { fprintf(stderr, "usage: time [-al] [-h | -p] [-o file] utility [argument ...]\n"); exit(1); } /* * Return the frequency of the kernel's statistics clock. */ static int getstathz(void) { int mib[2]; size_t size; struct clockinfo clockrate; mib[0] = CTL_KERN; mib[1] = KERN_CLOCKRATE; size = sizeof clockrate; if (sysctl(mib, 2, &clockrate, &size, NULL, 0) == -1) err(1, "sysctl kern.clockrate"); return clockrate.stathz; } static void humantime(FILE *out, long sec, long centisec) { long days, hrs, mins; days = sec / (60L * 60 * 24); sec %= (60L * 60 * 24); hrs = sec / (60L * 60); sec %= (60L * 60); mins = sec / 60; sec %= 60; fprintf(out, "\t"); if (days) fprintf(out, "%ldd", days); if (hrs) fprintf(out, "%ldh", hrs); if (mins) fprintf(out, "%ldm", mins); fprintf(out, "%ld%c%02lds", sec, decimal_point, centisec); } static void showtime(FILE *out, struct timespec *before, struct timespec *after, struct rusage *ru) { after->tv_sec -= before->tv_sec; after->tv_nsec -= before->tv_nsec; if (after->tv_nsec < 0) after->tv_sec--, after->tv_nsec += 1000000000; if (pflag) { /* POSIX wants output that must look like "real %f\nuser %f\nsys %f\n" and requires at least two digits after the radix. */ fprintf(out, "real %jd%c%02ld\n", (intmax_t)after->tv_sec, decimal_point, after->tv_nsec/10000000); fprintf(out, "user %jd%c%02ld\n", (intmax_t)ru->ru_utime.tv_sec, decimal_point, ru->ru_utime.tv_usec/10000); fprintf(out, "sys %jd%c%02ld\n", (intmax_t)ru->ru_stime.tv_sec, decimal_point, ru->ru_stime.tv_usec/10000); } else if (hflag) { humantime(out, after->tv_sec, after->tv_nsec/10000000); fprintf(out, " real\t"); humantime(out, ru->ru_utime.tv_sec, ru->ru_utime.tv_usec/10000); fprintf(out, " user\t"); humantime(out, ru->ru_stime.tv_sec, ru->ru_stime.tv_usec/10000); fprintf(out, " sys\n"); } else { fprintf(out, "%9jd%c%02ld real ", (intmax_t)after->tv_sec, decimal_point, after->tv_nsec/10000000); fprintf(out, "%9jd%c%02ld user ", (intmax_t)ru->ru_utime.tv_sec, decimal_point, ru->ru_utime.tv_usec/10000); fprintf(out, "%9jd%c%02ld sys\n", (intmax_t)ru->ru_stime.tv_sec, decimal_point, ru->ru_stime.tv_usec/10000); } } static void siginfo(int sig __unused) { siginfo_recvd = 1; } diff --git a/usr.bin/truncate/truncate.c b/usr.bin/truncate/truncate.c index ca705f055add..d8484257294a 100644 --- a/usr.bin/truncate/truncate.c +++ b/usr.bin/truncate/truncate.c @@ -1,229 +1,226 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2000 Sheldon Hearn . * All rights reserved. * * Copyright (c) 2021 The FreeBSD Foundation * * Portions of this software were developed by Ka Ho Ng * under sponsorship from the FreeBSD Foundation. * * 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. * */ -static const char rcsid[] = - "$FreeBSD$"; - #include #include #include #include #include #include #include #include #include static void usage(void); int main(int argc, char **argv) { struct stat sb; mode_t omode; off_t oflow, rsize, sz, tsize, round, off, len; uint64_t usz; int ch, error, fd, oflags, r; int do_dealloc; int do_truncate; int no_create; int do_relative; int do_round; int do_refer; int got_size; char *fname, *rname; struct spacectl_range sr; fd = -1; rsize = tsize = sz = off = 0; len = -1; do_dealloc = no_create = do_relative = do_round = do_refer = got_size = 0; do_truncate = 1; error = r = 0; rname = NULL; while ((ch = getopt(argc, argv, "cdr:s:o:l:")) != -1) switch (ch) { case 'c': no_create = 1; break; case 'd': do_dealloc = 1; do_truncate = 0; break; case 'r': do_refer = 1; rname = optarg; break; case 's': if (*optarg == '+' || *optarg == '-') { do_relative = 1; } else if (*optarg == '%' || *optarg == '/') { do_round = 1; } if (expand_number(do_relative || do_round ? optarg + 1 : optarg, &usz) == -1 || (off_t)usz < 0) errx(EXIT_FAILURE, "invalid size argument `%s'", optarg); sz = (*optarg == '-' || *optarg == '/') ? -(off_t)usz : (off_t)usz; got_size = 1; break; case 'o': if (expand_number(optarg, &usz) == -1 || (off_t)usz < 0) errx(EXIT_FAILURE, "invalid offset argument `%s'", optarg); off = usz; break; case 'l': if (expand_number(optarg, &usz) == -1 || (off_t)usz <= 0) errx(EXIT_FAILURE, "invalid length argument `%s'", optarg); len = usz; break; default: usage(); /* NOTREACHED */ } argv += optind; argc -= optind; /* * Exactly one of do_refer, got_size or do_dealloc must be specified. * Since do_relative implies got_size, do_relative, do_refer and * do_dealloc are also mutually exclusive. If do_dealloc is specified, * the length argument must be set. See usage() for allowed * invocations. */ if (argc < 1 || do_refer + got_size + do_dealloc != 1 || (do_dealloc == 1 && len == -1)) usage(); if (do_refer == 1) { if (stat(rname, &sb) == -1) err(EXIT_FAILURE, "%s", rname); tsize = sb.st_size; } else if (do_relative == 1 || do_round == 1) rsize = sz; else if (do_dealloc == 0) tsize = sz; if (no_create) oflags = O_WRONLY; else oflags = O_WRONLY | O_CREAT; omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; while ((fname = *argv++) != NULL) { if (fd != -1) close(fd); if ((fd = open(fname, oflags, omode)) == -1) { if (errno != ENOENT) { warn("%s", fname); error++; } continue; } if (do_relative == 1) { if (fstat(fd, &sb) == -1) { warn("%s", fname); error++; continue; } oflow = sb.st_size + rsize; if (oflow < (sb.st_size + rsize)) { errno = EFBIG; warn("%s", fname); error++; continue; } tsize = oflow; } if (do_round == 1) { if (fstat(fd, &sb) == -1) { warn("%s", fname); error++; continue; } sz = rsize; if (sz < 0) sz = -sz; if (sb.st_size % sz) { round = sb.st_size / sz; if (round != sz && rsize < 0) round--; else if (rsize > 0) round++; tsize = (round < 0 ? 0 : round) * sz; } else tsize = sb.st_size; } if (tsize < 0) tsize = 0; if (do_dealloc == 1) { sr.r_offset = off; sr.r_len = len; r = fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, &sr); } if (do_truncate == 1) r = ftruncate(fd, tsize); if (r == -1) { warn("%s", fname); error++; } } if (fd != -1) close(fd); return (error ? EXIT_FAILURE : EXIT_SUCCESS); } static void usage(void) { fprintf(stderr, "%s\n%s\n%s\n", "usage: truncate [-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ...", " truncate [-c] -r rfile file ...", " truncate [-c] -d [-o offset[K|k|M|m|G|g|T|t]] -l length[K|k|M|m|G|g|T|t] file ..."); exit(EXIT_FAILURE); } diff --git a/usr.bin/ul/ul.c b/usr.bin/ul/ul.c index 07a796ffd55b..5b52db208b68 100644 --- a/usr.bin/ul/ul.c +++ b/usr.bin/ul/ul.c @@ -1,588 +1,586 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)ul.c 8.1 (Berkeley) 6/6/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #define IESC '\033' #define SO '\016' #define SI '\017' #define HFWD '9' #define HREV '8' #define FREV '7' #define MAXBUF 512 #define NORMAL 000 #define ALTSET 001 /* Reverse */ #define SUPERSC 002 /* Dim */ #define SUBSC 004 /* Dim | Ul */ #define UNDERL 010 /* Ul */ #define BOLD 020 /* Bold */ static int must_use_uc, must_overstrike; static const char *CURS_UP, *CURS_RIGHT, *CURS_LEFT, *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE, *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES; struct CHAR { char c_mode; wchar_t c_char; int c_width; /* width or -1 if multi-column char. filler */ } ; static struct CHAR sobuf[MAXBUF]; /* static output buffer */ static struct CHAR *obuf = sobuf; static int buflen = MAXBUF; static int col, maxcol; static int mode; static int halfpos; static int upln; static int iflag; static void usage(void) __dead2; static void setnewmode(int); static void initcap(void); static void reverse(void); static int outchar(int); static void fwd(void); static void initbuf(void); static void iattr(void); static void overstrike(void); static void flushln(void); static void filter(FILE *); static void outc(wint_t, int); #define PRINT(s) if (s == NULL) /* void */; else tputs(s, 1, outchar) int main(int argc, char **argv) { int c; const char *termtype; FILE *f; char termcap[1024]; setlocale(LC_ALL, ""); termtype = getenv("TERM"); if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1))) termtype = "lpr"; while ((c = getopt(argc, argv, "it:T:")) != -1) switch (c) { case 't': case 'T': /* for nroff compatibility */ termtype = optarg; break; case 'i': iflag = 1; break; default: usage(); } switch (tgetent(termcap, termtype)) { case 1: break; default: warnx("trouble reading termcap"); /* FALLTHROUGH */ case 0: /* No such terminal type - assume dumb */ (void)strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:"); break; } initcap(); if ((tgetflag("os") && ENTER_BOLD == NULL ) || (tgetflag("ul") && ENTER_UNDERLINE == NULL && UNDER_CHAR == NULL)) must_overstrike = 1; initbuf(); if (optind == argc) filter(stdin); else for (; optind 0) col--; continue; case '\t': col = (col+8) & ~07; if (col > maxcol) maxcol = col; continue; case '\r': col = 0; continue; case SO: mode |= ALTSET; continue; case SI: mode &= ~ALTSET; continue; case IESC: switch (c = getwc(f)) { case HREV: if (halfpos == 0) { mode |= SUPERSC; halfpos--; } else if (halfpos > 0) { mode &= ~SUBSC; halfpos--; } else { halfpos = 0; reverse(); } continue; case HFWD: if (halfpos == 0) { mode |= SUBSC; halfpos++; } else if (halfpos < 0) { mode &= ~SUPERSC; halfpos++; } else { halfpos = 0; fwd(); } continue; case FREV: reverse(); continue; default: errx(1, "unknown escape sequence in input: %o, %o", IESC, c); } continue; case '_': if (obuf[col].c_char || obuf[col].c_width < 0) { while (col > 0 && obuf[col].c_width < 0) col--; w = obuf[col].c_width; for (i = 0; i < w; i++) obuf[col++].c_mode |= UNDERL | mode; if (col > maxcol) maxcol = col; continue; } obuf[col].c_char = '_'; obuf[col].c_width = 1; /* FALLTHROUGH */ case ' ': col++; if (col > maxcol) maxcol = col; continue; case '\n': flushln(); continue; case '\f': flushln(); putwchar('\f'); continue; default: if ((w = wcwidth(c)) <= 0) /* non printing */ continue; if (obuf[col].c_char == '\0') { obuf[col].c_char = c; for (i = 0; i < w; i++) obuf[col + i].c_mode = mode; obuf[col].c_width = w; for (i = 1; i < w; i++) obuf[col + i].c_width = -1; } else if (obuf[col].c_char == '_') { obuf[col].c_char = c; for (i = 0; i < w; i++) obuf[col + i].c_mode |= UNDERL|mode; obuf[col].c_width = w; for (i = 1; i < w; i++) obuf[col + i].c_width = -1; } else if ((wint_t)obuf[col].c_char == c) { for (i = 0; i < w; i++) obuf[col + i].c_mode |= BOLD|mode; } else { w = obuf[col].c_width; for (i = 0; i < w; i++) obuf[col + i].c_mode = mode; } col += w; if (col > maxcol) maxcol = col; continue; } } if (ferror(f)) err(1, NULL); if (maxcol) flushln(); } static void flushln(void) { int lastmode; int i; int hadmodes = 0; lastmode = NORMAL; for (i = 0; i < maxcol; i++) { if (obuf[i].c_mode != lastmode) { hadmodes++; setnewmode(obuf[i].c_mode); lastmode = obuf[i].c_mode; } if (obuf[i].c_char == '\0') { if (upln) PRINT(CURS_RIGHT); else outc(' ', 1); } else outc(obuf[i].c_char, obuf[i].c_width); if (obuf[i].c_width > 1) i += obuf[i].c_width - 1; } if (lastmode != NORMAL) { setnewmode(0); } if (must_overstrike && hadmodes) overstrike(); putwchar('\n'); if (iflag && hadmodes) iattr(); (void)fflush(stdout); if (upln) upln--; initbuf(); } /* * For terminals that can overstrike, overstrike underlines and bolds. * We don't do anything with halfline ups and downs, or Greek. */ static void overstrike(void) { int i; wchar_t lbuf[256]; wchar_t *cp = lbuf; int hadbold=0; /* Set up overstrike buffer */ for (i=0; i 1) i += obuf[i].c_width - 1; hadbold=1; break; } putwchar('\r'); for (*cp=' '; *cp==' '; cp--) *cp = 0; for (cp=lbuf; *cp; cp++) putwchar(*cp); if (hadbold) { putwchar('\r'); for (cp=lbuf; *cp; cp++) putwchar(*cp=='_' ? ' ' : *cp); putwchar('\r'); for (cp=lbuf; *cp; cp++) putwchar(*cp=='_' ? ' ' : *cp); } } static void iattr(void) { int i; wchar_t lbuf[256]; wchar_t *cp = lbuf; for (i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int Dflag, cflag, dflag, uflag, iflag; static int numchars, numfields, repeats; /* Dflag values */ #define DF_NONE 0 #define DF_NOSEP 1 #define DF_PRESEP 2 #define DF_POSTSEP 3 static const struct option long_opts[] = { {"all-repeated",optional_argument, NULL, 'D'}, {"count", no_argument, NULL, 'c'}, {"repeated", no_argument, NULL, 'd'}, {"skip-fields", required_argument, NULL, 'f'}, {"ignore-case", no_argument, NULL, 'i'}, {"skip-chars", required_argument, NULL, 's'}, {"unique", no_argument, NULL, 'u'}, {NULL, no_argument, NULL, 0} }; static FILE *file(const char *, const char *); static wchar_t *convert(const char *); static int inlcmp(const char *, const char *); static void show(FILE *, const char *); static wchar_t *skip(wchar_t *); static void obsolete(char *[]); static void usage(void); int main (int argc, char *argv[]) { wchar_t *tprev, *tthis; FILE *ifp, *ofp; int ch, comp; size_t prevbuflen, thisbuflen, b1; char *prevline, *thisline, *p; const char *ifn, *errstr;; cap_rights_t rights; (void) setlocale(LC_ALL, ""); obsolete(argv); while ((ch = getopt_long(argc, argv, "+D::cdif:s:u", long_opts, NULL)) != -1) switch (ch) { case 'D': if (optarg == NULL || strcasecmp(optarg, "none") == 0) Dflag = DF_NOSEP; else if (strcasecmp(optarg, "prepend") == 0) Dflag = DF_PRESEP; else if (strcasecmp(optarg, "separate") == 0) Dflag = DF_POSTSEP; else usage(); break; case 'c': cflag = 1; break; case 'd': dflag = 1; break; case 'i': iflag = 1; break; case 'f': numfields = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr) errx(1, "field skip value is %s: %s", errstr, optarg); break; case 's': numchars = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr != NULL) errx(1, "character skip value is %s: %s", errstr, optarg); break; case 'u': uflag = 1; break; case '?': default: usage(); } argc -= optind; argv += optind; if (argc > 2) usage(); ifp = stdin; ifn = "stdin"; ofp = stdout; if (argc > 0 && strcmp(argv[0], "-") != 0) ifp = file(ifn = argv[0], "r"); cap_rights_init(&rights, CAP_FSTAT, CAP_READ); if (caph_rights_limit(fileno(ifp), &rights) < 0) err(1, "unable to limit rights for %s", ifn); cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE); if (argc > 1) ofp = file(argv[1], "w"); else cap_rights_set(&rights, CAP_IOCTL); if (caph_rights_limit(fileno(ofp), &rights) < 0) { err(1, "unable to limit rights for %s", argc > 1 ? argv[1] : "stdout"); } if (cap_rights_is_set(&rights, CAP_IOCTL)) { unsigned long cmd; cmd = TIOCGETA; /* required by isatty(3) in printf(3) */ if (caph_ioctls_limit(fileno(ofp), &cmd, 1) < 0) { err(1, "unable to limit ioctls for %s", argc > 1 ? argv[1] : "stdout"); } } caph_cache_catpages(); if (caph_enter() < 0) err(1, "unable to enter capability mode"); prevbuflen = thisbuflen = 0; prevline = thisline = NULL; if (getline(&prevline, &prevbuflen, ifp) < 0) { if (ferror(ifp)) err(1, "%s", ifn); exit(0); } tprev = convert(prevline); tthis = NULL; while (getline(&thisline, &thisbuflen, ifp) >= 0) { if (tthis != NULL) free(tthis); tthis = convert(thisline); if (tthis == NULL && tprev == NULL) comp = inlcmp(thisline, prevline); else if (tthis == NULL || tprev == NULL) comp = 1; else comp = wcscoll(tthis, tprev); if (comp) { /* If different, print; set previous to new value. */ if (Dflag == DF_POSTSEP && repeats > 0) fputc('\n', ofp); if (!Dflag) show(ofp, prevline); p = prevline; b1 = prevbuflen; prevline = thisline; prevbuflen = thisbuflen; if (tprev != NULL) free(tprev); tprev = tthis; thisline = p; thisbuflen = b1; tthis = NULL; repeats = 0; } else { if (Dflag) { if (repeats == 0) { if (Dflag == DF_PRESEP) fputc('\n', ofp); show(ofp, prevline); } show(ofp, thisline); } ++repeats; } } if (ferror(ifp)) err(1, "%s", ifn); if (!Dflag) show(ofp, prevline); exit(0); } static wchar_t * convert(const char *str) { size_t n; wchar_t *buf, *ret, *p; if ((n = mbstowcs(NULL, str, 0)) == (size_t)-1) return (NULL); if (SIZE_MAX / sizeof(*buf) < n + 1) errx(1, "conversion buffer length overflow"); if ((buf = malloc((n + 1) * sizeof(*buf))) == NULL) err(1, "malloc"); if (mbstowcs(buf, str, n + 1) != n) errx(1, "internal mbstowcs() error"); /* The last line may not end with \n. */ if (n > 0 && buf[n - 1] == L'\n') buf[n - 1] = L'\0'; /* If requested get the chosen fields + character offsets. */ if (numfields || numchars) { if ((ret = wcsdup(skip(buf))) == NULL) err(1, "wcsdup"); free(buf); } else ret = buf; if (iflag) { for (p = ret; *p != L'\0'; p++) *p = towlower(*p); } return (ret); } static int inlcmp(const char *s1, const char *s2) { int c1, c2; while (*s1 == *s2++) if (*s1++ == '\0') return (0); c1 = (unsigned char)*s1; c2 = (unsigned char)*(s2 - 1); /* The last line may not end with \n. */ if (c1 == '\n') c1 = '\0'; if (c2 == '\n') c2 = '\0'; return (c1 - c2); } /* * show -- * Output a line depending on the flags and number of repetitions * of the line. */ static void show(FILE *ofp, const char *str) { if ((!Dflag && dflag && repeats == 0) || (uflag && repeats > 0)) return; if (cflag) (void)fprintf(ofp, "%4d %s", repeats + 1, str); else (void)fprintf(ofp, "%s", str); } static wchar_t * skip(wchar_t *str) { int nchars, nfields; for (nfields = 0; *str != L'\0' && nfields++ != numfields; ) { while (iswblank(*str)) str++; while (*str != L'\0' && !iswblank(*str)) str++; } for (nchars = numchars; nchars-- && *str != L'\0'; ++str) ; return(str); } static FILE * file(const char *name, const char *mode) { FILE *fp; if ((fp = fopen(name, mode)) == NULL) err(1, "%s", name); return(fp); } static void obsolete(char *argv[]) { int len; char *ap, *p, *start; while ((ap = *++argv)) { /* Return if "--" or not an option of any form. */ if (ap[0] != '-') { if (ap[0] != '+') return; } else if (ap[1] == '-') return; if (!isdigit((unsigned char)ap[1])) continue; /* * Digit signifies an old-style option. Malloc space for dash, * new option and argument. */ len = strlen(ap); if ((start = p = malloc(len + 3)) == NULL) err(1, "malloc"); *p++ = '-'; *p++ = ap[0] == '+' ? 's' : 'f'; (void)strcpy(p, ap + 1); *argv = start; } } static void usage(void) { (void)fprintf(stderr, "usage: uniq [-c | -d | -D | -u] [-i] [-f fields] [-s chars] [input [output]]\n"); exit(1); } diff --git a/usr.bin/units/units.c b/usr.bin/units/units.c index 88363e2e6c34..78b7e4020ed3 100644 --- a/usr.bin/units/units.c +++ b/usr.bin/units/units.c @@ -1,892 +1,887 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu) * * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * Disclaimer: This software is provided by the author "as is". The author * shall not be liable for any damages caused in any way by this software. * * I would appreciate (though I do not require) receiving a copy of any * improvements you might make to this program. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #ifndef UNITSFILE #define UNITSFILE "/usr/share/misc/definitions.units" #endif #define MAXUNITS 1000 #define MAXPREFIXES 100 #define MAXSUBUNITS 500 #define PRIMITIVECHAR '!' static const char *powerstring = "^"; static const char *numfmt = "%.8g"; static struct { char *uname; char *uval; } unittable[MAXUNITS]; struct unittype { char *numerator[MAXSUBUNITS]; char *denominator[MAXSUBUNITS]; double factor; double offset; int quantity; }; static struct { char *prefixname; char *prefixval; } prefixtable[MAXPREFIXES]; static char NULLUNIT[] = ""; #define SEPARATOR ":" static int unitcount; static int prefixcount; static bool verbose = false; static bool terse = false; static const char * outputformat; static const char * havestr; static const char * wantstr; static int addsubunit(char *product[], char *toadd); static int addunit(struct unittype *theunit, const char *toadd, int flip, int quantity); static void cancelunit(struct unittype * theunit); static int compare(const void *item1, const void *item2); static int compareproducts(char **one, char **two); static int compareunits(struct unittype * first, struct unittype * second); static int completereduce(struct unittype * unit); static char *dupstr(const char *str); static void initializeunit(struct unittype * theunit); static char *lookupunit(const char *unit); static void readunits(const char *userfile); static int reduceproduct(struct unittype * theunit, int flip); static int reduceunit(struct unittype * theunit); static void showanswer(struct unittype * have, struct unittype * want); static void showunit(struct unittype * theunit); static void sortunit(struct unittype * theunit); static void usage(void); static void zeroerror(void); static const char* promptstr = ""; static const char * prompt(EditLine *e __unused) { return promptstr; } static char * dupstr(const char *str) { char *ret; ret = strdup(str); if (!ret) err(3, "dupstr"); return (ret); } static void readunits(const char *userfile) { FILE *unitfile; char line[512], *lineptr; int len, linenum, i; cap_rights_t unitfilerights; unitcount = 0; linenum = 0; if (userfile) { unitfile = fopen(userfile, "r"); if (!unitfile) errx(1, "unable to open units file '%s'", userfile); } else { unitfile = fopen(UNITSFILE, "r"); if (!unitfile) { char *direc, *env; char filename[1000]; env = getenv("PATH"); if (env) { direc = strtok(env, SEPARATOR); while (direc) { snprintf(filename, sizeof(filename), "%s/%s", direc, UNITSFILE); unitfile = fopen(filename, "rt"); if (unitfile) break; direc = strtok(NULL, SEPARATOR); } } if (!unitfile) errx(1, "can't find units file '%s'", UNITSFILE); } } cap_rights_init(&unitfilerights, CAP_READ, CAP_FSTAT); if (caph_rights_limit(fileno(unitfile), &unitfilerights) < 0) err(1, "cap_rights_limit() failed"); while (!feof(unitfile)) { if (!fgets(line, sizeof(line), unitfile)) break; linenum++; lineptr = line; if (*lineptr == '/' || *lineptr == '#') continue; lineptr += strspn(lineptr, " \n\t"); len = strcspn(lineptr, " \n\t"); lineptr[len] = 0; if (!strlen(lineptr)) continue; if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */ if (prefixcount == MAXPREFIXES) { warnx("memory for prefixes exceeded in line %d", linenum); continue; } lineptr[strlen(lineptr) - 1] = 0; prefixtable[prefixcount].prefixname = dupstr(lineptr); for (i = 0; i < prefixcount; i++) if (!strcmp(prefixtable[i].prefixname, lineptr)) { warnx("redefinition of prefix '%s' on line %d ignored", lineptr, linenum); continue; } lineptr += len + 1; lineptr += strspn(lineptr, " \n\t"); len = strcspn(lineptr, "\n\t"); if (len == 0) { warnx("unexpected end of prefix on line %d", linenum); continue; } lineptr[len] = 0; prefixtable[prefixcount++].prefixval = dupstr(lineptr); } else { /* it's not a prefix */ if (unitcount == MAXUNITS) { warnx("memory for units exceeded in line %d", linenum); continue; } unittable[unitcount].uname = dupstr(lineptr); for (i = 0; i < unitcount; i++) if (!strcmp(unittable[i].uname, lineptr)) { warnx("redefinition of unit '%s' on line %d ignored", lineptr, linenum); continue; } lineptr += len + 1; lineptr += strspn(lineptr, " \n\t"); if (!strlen(lineptr)) { warnx("unexpected end of unit on line %d", linenum); continue; } len = strcspn(lineptr, "\n\t"); lineptr[len] = 0; unittable[unitcount++].uval = dupstr(lineptr); } } fclose(unitfile); } static void initializeunit(struct unittype * theunit) { theunit->numerator[0] = theunit->denominator[0] = NULL; theunit->factor = 1.0; theunit->offset = 0.0; theunit->quantity = 0; } static int addsubunit(char *product[], char *toadd) { char **ptr; for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++); if (ptr >= product + MAXSUBUNITS) { warnx("memory overflow in unit reduction"); return 1; } if (!*ptr) *(ptr + 1) = NULL; *ptr = dupstr(toadd); return 0; } static void showunit(struct unittype * theunit) { char **ptr; int printedslash; int counter = 1; printf(numfmt, theunit->factor); if (theunit->offset) printf("&%.8g", theunit->offset); for (ptr = theunit->numerator; *ptr; ptr++) { if (ptr > theunit->numerator && **ptr && !strcmp(*ptr, *(ptr - 1))) counter++; else { if (counter > 1) printf("%s%d", powerstring, counter); if (**ptr) printf(" %s", *ptr); counter = 1; } } if (counter > 1) printf("%s%d", powerstring, counter); counter = 1; printedslash = 0; for (ptr = theunit->denominator; *ptr; ptr++) { if (ptr > theunit->denominator && **ptr && !strcmp(*ptr, *(ptr - 1))) counter++; else { if (counter > 1) printf("%s%d", powerstring, counter); if (**ptr) { if (!printedslash) printf(" /"); printedslash = 1; printf(" %s", *ptr); } counter = 1; } } if (counter > 1) printf("%s%d", powerstring, counter); printf("\n"); } void zeroerror(void) { warnx("unit reduces to zero"); } /* Adds the specified string to the unit. Flip is 0 for adding normally, 1 for adding reciprocal. Quantity is 1 if this is a quantity to be converted rather than a pure unit. Returns 0 for successful addition, nonzero on error. */ static int addunit(struct unittype * theunit, const char *toadd, int flip, int quantity) { char *scratch, *savescr; char *item; char *divider, *slash, *offset; int doingtop; if (!strlen(toadd)) return 1; savescr = scratch = dupstr(toadd); for (slash = scratch + 1; *slash; slash++) if (*slash == '-' && (tolower(*(slash - 1)) != 'e' || !strchr(".0123456789", *(slash + 1)))) *slash = ' '; slash = strchr(scratch, '/'); if (slash) *slash = 0; doingtop = 1; do { item = strtok(scratch, " *\t\n/"); while (item) { if (strchr("0123456789.", *item)) { /* item is a number */ double num, offsetnum; if (quantity) theunit->quantity = 1; offset = strchr(item, '&'); if (offset) { *offset = 0; offsetnum = atof(offset+1); } else offsetnum = 0.0; divider = strchr(item, '|'); if (divider) { *divider = 0; num = atof(item); if (!num) { zeroerror(); free(savescr); return 1; } if (doingtop ^ flip) { theunit->factor *= num; theunit->offset *= num; } else { theunit->factor /= num; theunit->offset /= num; } num = atof(divider + 1); if (!num) { zeroerror(); free(savescr); return 1; } if (doingtop ^ flip) { theunit->factor /= num; theunit->offset /= num; } else { theunit->factor *= num; theunit->offset *= num; } } else { num = atof(item); if (!num) { zeroerror(); free(savescr); return 1; } if (doingtop ^ flip) { theunit->factor *= num; theunit->offset *= num; } else { theunit->factor /= num; theunit->offset /= num; } } if (doingtop ^ flip) theunit->offset += offsetnum; } else { /* item is not a number */ int repeat = 1; if (strchr("23456789", item[strlen(item) - 1])) { repeat = item[strlen(item) - 1] - '0'; item[strlen(item) - 1] = 0; } for (; repeat; repeat--) { if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item)) { free(savescr); return 1; } } } item = strtok(NULL, " *\t/\n"); } doingtop--; if (slash) { scratch = slash + 1; } else doingtop--; } while (doingtop >= 0); free(savescr); return 0; } static int compare(const void *item1, const void *item2) { return strcmp(*(const char * const *)item1, *(const char * const *)item2); } static void sortunit(struct unittype * theunit) { char **ptr; unsigned int count; for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++); qsort(theunit->numerator, count, sizeof(char *), compare); for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++); qsort(theunit->denominator, count, sizeof(char *), compare); } void cancelunit(struct unittype * theunit) { char **den, **num; int comp; den = theunit->denominator; num = theunit->numerator; while (*num && *den) { comp = strcmp(*den, *num); if (!comp) { /* if (*den!=NULLUNIT) free(*den); if (*num!=NULLUNIT) free(*num);*/ *den++ = NULLUNIT; *num++ = NULLUNIT; } else if (comp < 0) den++; else num++; } } /* Looks up the definition for the specified unit. Returns a pointer to the definition or a null pointer if the specified unit does not appear in the units table. */ static char buffer[100]; /* buffer for lookupunit answers with prefixes */ char * lookupunit(const char *unit) { int i; char *copy; for (i = 0; i < unitcount; i++) { if (!strcmp(unittable[i].uname, unit)) return unittable[i].uval; } if (unit[strlen(unit) - 1] == '^') { copy = dupstr(unit); copy[strlen(copy) - 1] = 0; for (i = 0; i < unitcount; i++) { if (!strcmp(unittable[i].uname, copy)) { strlcpy(buffer, copy, sizeof(buffer)); free(copy); return buffer; } } free(copy); } if (unit[strlen(unit) - 1] == 's') { copy = dupstr(unit); copy[strlen(copy) - 1] = 0; for (i = 0; i < unitcount; i++) { if (!strcmp(unittable[i].uname, copy)) { strlcpy(buffer, copy, sizeof(buffer)); free(copy); return buffer; } } if (copy[strlen(copy) - 1] == 'e') { copy[strlen(copy) - 1] = 0; for (i = 0; i < unitcount; i++) { if (!strcmp(unittable[i].uname, copy)) { strlcpy(buffer, copy, sizeof(buffer)); free(copy); return buffer; } } } free(copy); } for (i = 0; i < prefixcount; i++) { size_t len = strlen(prefixtable[i].prefixname); if (!strncmp(prefixtable[i].prefixname, unit, len)) { if (!strlen(unit + len) || lookupunit(unit + len)) { snprintf(buffer, sizeof(buffer), "%s %s", prefixtable[i].prefixval, unit + len); return buffer; } } } return 0; } /* reduces a product of symbolic units to primitive units. The three low bits are used to return flags: bit 0 (1) set on if reductions were performed without error. bit 1 (2) set on if no reductions are performed. bit 2 (4) set on if an unknown unit is discovered. */ #define ERROR 4 static int reduceproduct(struct unittype * theunit, int flip) { char *toadd; char **product; int didsomething = 2; if (flip) product = theunit->denominator; else product = theunit->numerator; for (; *product; product++) { for (;;) { if (!strlen(*product)) break; toadd = lookupunit(*product); if (!toadd) { printf("unknown unit '%s'\n", *product); return ERROR; } if (strchr(toadd, PRIMITIVECHAR)) break; didsomething = 1; if (*product != NULLUNIT) { free(*product); *product = NULLUNIT; } if (addunit(theunit, toadd, flip, 0)) return ERROR; } } return didsomething; } /* Reduces numerator and denominator of the specified unit. Returns 0 on success, or 1 on unknown unit error. */ static int reduceunit(struct unittype * theunit) { int ret; ret = 1; while (ret & 1) { ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1); if (ret & 4) return 1; } return 0; } static int compareproducts(char **one, char **two) { while (*one || *two) { if (!*one && *two != NULLUNIT) return 1; if (!*two && *one != NULLUNIT) return 1; if (*one == NULLUNIT) one++; else if (*two == NULLUNIT) two++; else if (strcmp(*one, *two)) return 1; else { one++; two++; } } return 0; } /* Return zero if units are compatible, nonzero otherwise */ static int compareunits(struct unittype * first, struct unittype * second) { return compareproducts(first->numerator, second->numerator) || compareproducts(first->denominator, second->denominator); } static int completereduce(struct unittype * unit) { if (reduceunit(unit)) return 1; sortunit(unit); cancelunit(unit); return 0; } static void showanswer(struct unittype * have, struct unittype * want) { double ans; char* oformat; if (compareunits(have, want)) { printf("conformability error\n"); if (verbose) printf("\t%s = ", havestr); else if (!terse) printf("\t"); showunit(have); if (!terse) { if (verbose) printf("\t%s = ", wantstr); else printf("\t"); showunit(want); } } else if (have->offset != want->offset) { if (want->quantity) printf("WARNING: conversion of non-proportional quantities.\n"); if (have->quantity) { asprintf(&oformat, "\t%s\n", outputformat); printf(oformat, (have->factor + have->offset-want->offset)/want->factor); free(oformat); } else { asprintf(&oformat, "\t (-> x*%sg %sg)\n\t (<- y*%sg %sg)\n", outputformat, outputformat, outputformat, outputformat); printf(oformat, have->factor / want->factor, (have->offset-want->offset)/want->factor, want->factor / have->factor, (want->offset - have->offset)/have->factor); } } else { ans = have->factor / want->factor; if (verbose) { printf("\t%s = ", havestr); printf(outputformat, ans); printf(" * %s", wantstr); printf("\n"); } else if (terse) { printf(outputformat, ans); printf("\n"); } else { printf("\t* "); printf(outputformat, ans); printf("\n"); } if (verbose) { printf("\t%s = (1 / ", havestr); printf(outputformat, 1/ans); printf(") * %s\n", wantstr); } else if (!terse) { printf("\t/ "); printf(outputformat, 1/ans); printf("\n"); } } } static void __dead2 usage(void) { fprintf(stderr, "usage: units [-ehqtUVv] [-f unitsfile] [-o format] [from to]\n"); exit(3); } static struct option longopts[] = { {"exponential", no_argument, NULL, 'e'}, {"file", required_argument, NULL, 'f'}, {"history", required_argument, NULL, 'H'}, {"help", no_argument, NULL, 'h'}, {"output-format", required_argument, NULL, 'o'}, {"quiet", no_argument, NULL, 'q'}, {"terse", no_argument, NULL, 't'}, {"unitsfile", no_argument, NULL, 'U'}, {"version", no_argument, NULL, 'V'}, {"verbose", no_argument, NULL, 'v'}, { 0, 0, 0, 0 } }; int main(int argc, char **argv) { struct unittype have, want; int optchar; bool quiet; bool readfile; bool quit; History *inhistory; EditLine *el; HistEvent ev; int inputsz; quiet = false; readfile = false; outputformat = numfmt; quit = false; while ((optchar = getopt_long(argc, argv, "+ehf:o:qtvH:UV", longopts, NULL)) != -1) { switch (optchar) { case 'e': outputformat = "%6e"; break; case 'f': readfile = true; if (strlen(optarg) == 0) readunits(NULL); else readunits(optarg); break; case 'H': /* Ignored, for compatibility with GNU units. */ break; case 'q': quiet = true; break; case 't': terse = true; break; case 'o': outputformat = optarg; break; case 'v': verbose = true; break; case 'V': fprintf(stderr, "FreeBSD units\n"); /* FALLTHROUGH */ case 'U': if (access(UNITSFILE, F_OK) == 0) printf("%s\n", UNITSFILE); else printf("Units data file not found"); exit(0); case 'h': /* FALLTHROUGH */ default: usage(); } } if (!readfile) readunits(NULL); if (optind == argc - 2) { if (caph_enter() < 0) err(1, "unable to enter capability mode"); havestr = argv[optind]; wantstr = argv[optind + 1]; initializeunit(&have); addunit(&have, havestr, 0, 1); completereduce(&have); initializeunit(&want); addunit(&want, wantstr, 0, 1); completereduce(&want); showanswer(&have, &want); } else { inhistory = history_init(); el = el_init(argv[0], stdin, stdout, stderr); el_set(el, EL_PROMPT, &prompt); el_set(el, EL_EDITOR, "emacs"); el_set(el, EL_SIGNAL, 1); el_set(el, EL_HIST, history, inhistory); el_source(el, NULL); history(inhistory, &ev, H_SETSIZE, 800); if (inhistory == 0) err(1, "Could not initialize history"); if (caph_enter() < 0) err(1, "unable to enter capability mode"); if (!quiet) printf("%d units, %d prefixes\n", unitcount, prefixcount); while (!quit) { do { initializeunit(&have); if (!quiet) promptstr = "You have: "; havestr = el_gets(el, &inputsz); if (havestr == NULL) { quit = true; break; } if (inputsz > 0) history(inhistory, &ev, H_ENTER, havestr); } while (addunit(&have, havestr, 0, 1) || completereduce(&have)); if (quit) { break; } do { initializeunit(&want); if (!quiet) promptstr = "You want: "; wantstr = el_gets(el, &inputsz); if (wantstr == NULL) { quit = true; break; } if (inputsz > 0) history(inhistory, &ev, H_ENTER, wantstr); } while (addunit(&want, wantstr, 0, 1) || completereduce(&want)); if (quit) { break; } showanswer(&have, &want); } history_end(inhistory); el_end(el); } return (0); } diff --git a/usr.bin/yes/yes.c b/usr.bin/yes/yes.c index 03111ed53fc1..60820368cea1 100644 --- a/usr.bin/yes/yes.c +++ b/usr.bin/yes/yes.c @@ -1,88 +1,86 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1987, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1987, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)yes.c 8.1 (Berkeley) 6/6/93"; -#else -static const char rcsid[] = "$FreeBSD$"; #endif #endif /* not lint */ #include #include #include #include #include int main(int argc, char **argv) { char buf[8192]; char y[2] = { 'y', '\n' }; char * exp = y; size_t buflen = 0; size_t explen = sizeof(y); size_t more; ssize_t ret; if (caph_limit_stdio() < 0 || caph_enter() < 0) err(1, "capsicum"); if (argc > 1) { exp = argv[1]; explen = strlen(exp) + 1; exp[explen - 1] = '\n'; } if (explen <= sizeof(buf)) { while (buflen < sizeof(buf) - explen) { memcpy(buf + buflen, exp, explen); buflen += explen; } exp = buf; explen = buflen; } more = explen; while ((ret = write(STDOUT_FILENO, exp + (explen - more), more)) > 0) if ((more -= ret) == 0) more = explen; err(1, "stdout"); /*NOTREACHED*/ } diff --git a/usr.sbin/apmd/apmd.c b/usr.sbin/apmd/apmd.c index 54003a33ad3e..0a3fcf6315e0 100644 --- a/usr.sbin/apmd/apmd.c +++ b/usr.sbin/apmd/apmd.c @@ -1,707 +1,702 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * APM (Advanced Power Management) Event Dispatcher * * Copyright (c) 1999 Mitsuru IWASAKI * Copyright (c) 1999 KOIE Hidetaka * 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "apmd.h" int debug_level = 0; int verbose = 0; int soft_power_state_change = 0; const char *apmd_configfile = APMD_CONFIGFILE; const char *apmd_pidfile = APMD_PIDFILE; int apmctl_fd = -1, apmnorm_fd = -1; /* * table of event handlers */ #define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R }, struct event_config events[EVENT_MAX] = { EVENT_CONFIG_INITIALIZER(NOEVENT, 0) EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1) EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1) EVENT_CONFIG_INITIALIZER(NORMRESUME, 0) EVENT_CONFIG_INITIALIZER(CRITRESUME, 0) EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0) EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0) EVENT_CONFIG_INITIALIZER(UPDATETIME, 0) EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1) EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1) EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1) EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0) EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0) }; /* * List of battery events */ struct battery_watch_event *battery_watch_list = NULL; #define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */ /* * default procedure */ struct event_cmd * event_cmd_default_clone(void *this) { struct event_cmd * oldone = this; struct event_cmd * newone = malloc(oldone->len); newone->next = NULL; newone->len = oldone->len; newone->name = oldone->name; newone->op = oldone->op; return newone; } /* * exec command */ int event_cmd_exec_act(void *this) { struct event_cmd_exec * p = this; int status = -1; pid_t pid; switch ((pid = fork())) { case -1: warn("cannot fork"); break; case 0: /* child process */ signal(SIGHUP, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGTERM, SIG_DFL); execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL); _exit(127); default: /* parent process */ do { pid = waitpid(pid, &status, 0); } while (pid == -1 && errno == EINTR); break; } return status; } void event_cmd_exec_dump(void *this, FILE *fp) { fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line); } struct event_cmd * event_cmd_exec_clone(void *this) { struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this); struct event_cmd_exec * oldone = this; newone->evcmd.next = NULL; newone->evcmd.len = oldone->evcmd.len; newone->evcmd.name = oldone->evcmd.name; newone->evcmd.op = oldone->evcmd.op; if ((newone->line = strdup(oldone->line)) == NULL) err(1, "out of memory"); return (struct event_cmd *) newone; } void event_cmd_exec_free(void *this) { free(((struct event_cmd_exec *)this)->line); } struct event_cmd_op event_cmd_exec_ops = { event_cmd_exec_act, event_cmd_exec_dump, event_cmd_exec_clone, event_cmd_exec_free }; /* * reject command */ int event_cmd_reject_act(void *this __unused) { int rc = 0; if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) { syslog(LOG_NOTICE, "fail to reject\n"); rc = -1; } return rc; } struct event_cmd_op event_cmd_reject_ops = { event_cmd_reject_act, NULL, event_cmd_default_clone, NULL }; /* * manipulate event_config */ struct event_cmd * clone_event_cmd_list(struct event_cmd *p) { struct event_cmd dummy; struct event_cmd *q = &dummy; for ( ;p; p = p->next) { assert(p->op->clone); if ((q->next = p->op->clone(p)) == NULL) err(1, "out of memory"); q = q->next; } q->next = NULL; return dummy.next; } void free_event_cmd_list(struct event_cmd *p) { struct event_cmd * q; for ( ; p ; p = q) { q = p->next; if (p->op->free) p->op->free(p); free(p); } } int register_battery_handlers( int level, int direction, struct event_cmd *cmdlist) { /* * level is negative if it's in "minutes", non-negative if * percentage. * * direction =1 means we care about this level when charging, * direction =-1 means we care about it when discharging. */ if (level>100) /* percentage > 100 */ return -1; if (abs(direction) != 1) /* nonsense direction value */ return -1; if (cmdlist) { struct battery_watch_event *we; if ((we = malloc(sizeof(struct battery_watch_event))) == NULL) err(1, "out of memory"); we->next = battery_watch_list; /* starts at NULL */ battery_watch_list = we; we->level = abs(level); we->type = (level<0)?BATTERY_MINUTES:BATTERY_PERCENT; we->direction = (direction<0)?BATTERY_DISCHARGING: BATTERY_CHARGING; we->done = 0; we->cmdlist = clone_event_cmd_list(cmdlist); } return 0; } int register_apm_event_handlers( bitstr_t bit_decl(evlist, EVENT_MAX), struct event_cmd *cmdlist) { if (cmdlist) { bitstr_t bit_decl(tmp, EVENT_MAX); memcpy(&tmp, evlist, bitstr_size(EVENT_MAX)); for (;;) { int n; struct event_cmd *p; struct event_cmd *q; bit_ffs(tmp, EVENT_MAX, &n); if (n < 0) break; p = events[n].cmdlist; if ((q = clone_event_cmd_list(cmdlist)) == NULL) err(1, "out of memory"); if (p) { while (p->next != NULL) p = p->next; p->next = q; } else { events[n].cmdlist = q; } bit_clear(tmp, n); } } return 0; } /* * execute command */ int exec_run_cmd(struct event_cmd *p) { int status = 0; for (; p; p = p->next) { assert(p->op->act); if (verbose) syslog(LOG_INFO, "action: %s", p->name); status = p->op->act(p); if (status) { syslog(LOG_NOTICE, "command finished with %d\n", status); break; } } return status; } /* * execute command -- the event version */ int exec_event_cmd(struct event_config *ev) { int status = 0; status = exec_run_cmd(ev->cmdlist); if (status && ev->rejectable) { syslog(LOG_ERR, "canceled"); event_cmd_reject_act(NULL); } return status; } /* * read config file */ extern FILE * yyin; extern int yydebug; void read_config(void) { int i; if ((yyin = fopen(apmd_configfile, "r")) == NULL) { err(1, "cannot open config file"); } #ifdef DEBUG yydebug = debug_level; #endif if (yyparse() != 0) err(1, "cannot parse config file"); fclose(yyin); /* enable events */ for (i = 0; i < EVENT_MAX; i++) { if (events[i].cmdlist) { u_int event_type = i; if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { err(1, "cannot enable event 0x%x", event_type); } } } } void dump_config(void) { int i; struct battery_watch_event *q; for (i = 0; i < EVENT_MAX; i++) { struct event_cmd * p; if ((p = events[i].cmdlist)) { fprintf(stderr, "apm_event %s {\n", events[i].name); for ( ; p ; p = p->next) { fprintf(stderr, "\t%s", p->name); if (p->op->dump) p->op->dump(p, stderr); fprintf(stderr, ";\n"); } fprintf(stderr, "}\n"); } } for (q = battery_watch_list ; q != NULL ; q = q -> next) { struct event_cmd * p; fprintf(stderr, "apm_battery %d%s %s {\n", q -> level, (q -> type == BATTERY_PERCENT)?"%":"m", (q -> direction == BATTERY_CHARGING)?"charging": "discharging"); for ( p = q -> cmdlist; p ; p = p->next) { fprintf(stderr, "\t%s", p->name); if (p->op->dump) p->op->dump(p, stderr); fprintf(stderr, ";\n"); } fprintf(stderr, "}\n"); } } void destroy_config(void) { int i; struct battery_watch_event *q; /* disable events */ for (i = 0; i < EVENT_MAX; i++) { if (events[i].cmdlist) { u_int event_type = i; if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { err(1, "cannot disable event 0x%x", event_type); } } } for (i = 0; i < EVENT_MAX; i++) { struct event_cmd * p; if ((p = events[i].cmdlist)) free_event_cmd_list(p); events[i].cmdlist = NULL; } for( ; battery_watch_list; battery_watch_list = battery_watch_list -> next) { free_event_cmd_list(battery_watch_list->cmdlist); q = battery_watch_list->next; free(battery_watch_list); battery_watch_list = q; } } void restart(void) { destroy_config(); read_config(); if (verbose) dump_config(); } /* * write pid file */ static void write_pid(void) { FILE *fp = fopen(apmd_pidfile, "w"); if (fp) { fprintf(fp, "%ld\n", (long)getpid()); fclose(fp); } } /* * handle signals */ static int signal_fd[2]; void enque_signal(int sig) { if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig) err(1, "cannot process signal."); } void wait_child(void) { int status; while (waitpid(-1, &status, WNOHANG) > 0) ; } int proc_signal(int fd) { int rc = 0; int sig; while (read(fd, &sig, sizeof sig) == sizeof sig) { syslog(LOG_INFO, "caught signal: %d", sig); switch (sig) { case SIGHUP: syslog(LOG_NOTICE, "restart by SIG"); restart(); break; case SIGTERM: syslog(LOG_NOTICE, "going down on signal %d", sig); rc = -1; return rc; case SIGCHLD: wait_child(); break; default: warn("unexpected signal(%d) received.", sig); break; } } return rc; } void proc_apmevent(int fd) { struct apm_event_info apmevent; while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) { int status; syslog(LOG_NOTICE, "apmevent %04x index %d\n", apmevent.type, apmevent.index); syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name); if (fork() == 0) { status = exec_event_cmd(&events[apmevent.type]); exit(status); } } } #define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\ BATTERY_DISCHARGING) void check_battery(void) { static int first_time=1, last_state; int status; struct apm_info pw_info; struct battery_watch_event *p; /* If we don't care, don't bother */ if (battery_watch_list == NULL) return; if (first_time) { if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0) err(1, "cannot check battery state."); /* * This next statement isn't entirely true. The spec does not tie AC * line state to battery charging or not, but this is a bit lazier to do. */ last_state = AC_POWER_STATE; first_time = 0; return; /* We can't process events, we have no baseline */ } /* * XXX - should we do this a bunch of times and perform some sort * of smoothing or correction? */ if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0) err(1, "cannot check battery state."); /* * If we're not in the state now that we were in last time, * then it's a transition, which means we must clean out * the event-caught state. */ if (last_state != AC_POWER_STATE) { if (soft_power_state_change && fork() == 0) { status = exec_event_cmd(&events[PMEV_POWERSTATECHANGE]); exit(status); } last_state = AC_POWER_STATE; for (p = battery_watch_list ; p!=NULL ; p = p -> next) p->done = 0; } for (p = battery_watch_list ; p != NULL ; p = p -> next) if (p -> direction == AC_POWER_STATE && !(p -> done) && ((p -> type == BATTERY_PERCENT && p -> level == (int)pw_info.ai_batt_life) || (p -> type == BATTERY_MINUTES && p -> level == (pw_info.ai_batt_time / 60)))) { p -> done++; if (verbose) syslog(LOG_NOTICE, "Caught battery event: %s, %d%s", (p -> direction == BATTERY_CHARGING)?"charging":"discharging", p -> level, (p -> type == BATTERY_PERCENT)?"%":" minutes"); if (fork() == 0) { status = exec_run_cmd(p -> cmdlist); exit(status); } } } void event_loop(void) { int fdmax = 0; struct sigaction nsa; fd_set master_rfds; sigset_t sigmask, osigmask; FD_ZERO(&master_rfds); FD_SET(apmctl_fd, &master_rfds); fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax; FD_SET(signal_fd[0], &master_rfds); fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax; memset(&nsa, 0, sizeof nsa); nsa.sa_handler = enque_signal; sigfillset(&nsa.sa_mask); nsa.sa_flags = SA_RESTART; sigaction(SIGHUP, &nsa, NULL); sigaction(SIGCHLD, &nsa, NULL); sigaction(SIGTERM, &nsa, NULL); sigemptyset(&sigmask); sigaddset(&sigmask, SIGHUP); sigaddset(&sigmask, SIGCHLD); sigaddset(&sigmask, SIGTERM); sigprocmask(SIG_SETMASK, &sigmask, &osigmask); while (1) { fd_set rfds; int res; struct timeval to; to.tv_sec = BATT_CHK_INTV; to.tv_usec = 0; memcpy(&rfds, &master_rfds, sizeof rfds); sigprocmask(SIG_SETMASK, &osigmask, NULL); if ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) { if (errno != EINTR) err(1, "select"); } sigprocmask(SIG_SETMASK, &sigmask, NULL); if (res == 0) { /* time to check the battery */ check_battery(); continue; } if (FD_ISSET(signal_fd[0], &rfds)) { if (proc_signal(signal_fd[0]) < 0) return; } if (FD_ISSET(apmctl_fd, &rfds)) proc_apmevent(apmctl_fd); } } int main(int ac, char* av[]) { int ch; int daemonize = 1; char *prog; int logopt = LOG_NDELAY | LOG_PID; while ((ch = getopt(ac, av, "df:sv")) != -1) { switch (ch) { case 'd': daemonize = 0; debug_level++; break; case 'f': apmd_configfile = optarg; break; case 's': soft_power_state_change = 1; break; case 'v': verbose = 1; break; default: err(1, "unknown option `%c'", ch); } } if (daemonize) daemon(0, 0); #ifdef NICE_INCR nice(NICE_INCR); #endif if (!daemonize) logopt |= LOG_PERROR; prog = strrchr(av[0], '/'); openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON); syslog(LOG_NOTICE, "start"); if (pipe(signal_fd) < 0) err(1, "pipe"); if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0) err(1, "fcntl"); if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) { err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE); } if (fcntl(apmnorm_fd, F_SETFD, 1) == -1) { err(1, "cannot set close-on-exec flag for device file '%s'", APM_NORM_DEVICEFILE); } if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) { err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE); } if (fcntl(apmctl_fd, F_SETFD, 1) == -1) { err(1, "cannot set close-on-exec flag for device file '%s'", APM_CTL_DEVICEFILE); } restart(); write_pid(); event_loop(); exit(EXIT_SUCCESS); } diff --git a/usr.sbin/btxld/btxld.c b/usr.sbin/btxld/btxld.c index e7211ad48fa4..86d380926e90 100644 --- a/usr.sbin/btxld/btxld.c +++ b/usr.sbin/btxld/btxld.c @@ -1,576 +1,571 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1998 Robert Nordier * 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include /* XXX make this work as an i386/amd64 cross-tool */ #include #undef __LDPGSZ #define __LDPGSZ 4096 #include #include #include #include #include #include #include #include #include #include #include "btx.h" #include "elfh.h" #define BTX_PATH "/sys/boot/i386/btx" #define I_LDR 0 /* BTX loader */ #define I_BTX 1 /* BTX kernel */ #define I_CLNT 2 /* Client program */ #define F_BIN 0 /* Binary */ #define F_AOUT 1 /* ZMAGIC a.out */ #define F_ELF 2 /* 32-bit ELF */ #define F_CNT 3 /* Number of formats */ #define IMPURE 1 /* Writable text */ #define MAXU32 0xffffffff /* Maximum unsigned 32-bit quantity */ struct hdr { uint32_t fmt; /* Format */ uint32_t flags; /* Bit flags */ uint32_t size; /* Size of file */ uint32_t text; /* Size of text segment */ uint32_t data; /* Size of data segment */ uint32_t bss; /* Size of bss segment */ uint32_t org; /* Program origin */ uint32_t entry; /* Program entry point */ }; static const char *const fmtlist[] = {"bin", "aout", "elf"}; static const char binfo[] = "kernel: ver=%u.%02u size=%x load=%x entry=%x map=%uM " "pgctl=%x:%x\n"; static const char cinfo[] = "client: fmt=%s size=%x text=%x data=%x bss=%x entry=%x\n"; static const char oinfo[] = "output: fmt=%s size=%x text=%x data=%x org=%x entry=%x\n"; static const char *lname = BTX_PATH "/btxldr/btxldr"; /* BTX loader */ static const char *bname = BTX_PATH "/btx/btx"; /* BTX kernel */ static const char *oname = "a.out"; /* Output filename */ static int ppage = -1; /* First page present */ static int wpage = -1; /* First page writable */ static unsigned int format; /* Output format */ static uint32_t centry; /* Client entry address */ static uint32_t lentry; /* Loader entry address */ static int Eflag; /* Client entry option */ static int quiet; /* Inhibit warnings */ static int verbose; /* Display information */ static const char *tname; /* Temporary output file */ static const char *fname; /* Current input file */ static void cleanup(void); static void btxld(const char *); static void getbtx(int, struct btx_hdr *); static void gethdr(int, struct hdr *); static void puthdr(int, struct hdr *); static void copy(int, int, size_t, off_t); static size_t readx(int, void *, size_t, off_t); static void writex(int, const void *, size_t); static void seekx(int, off_t); static unsigned int optfmt(const char *); static uint32_t optaddr(const char *); static int optpage(const char *, int); static void Warn(const char *, const char *, ...); static void usage(void) __dead2; /* * A link editor for BTX clients. */ int main(int argc, char *argv[]) { int c; while ((c = getopt(argc, argv, "qvb:E:e:f:l:o:P:W:")) != -1) switch (c) { case 'q': quiet = 1; break; case 'v': verbose = 1; break; case 'b': bname = optarg; break; case 'E': centry = optaddr(optarg); Eflag = 1; break; case 'e': lentry = optaddr(optarg); break; case 'f': format = optfmt(optarg); break; case 'l': lname = optarg; break; case 'o': oname = optarg; break; case 'P': ppage = optpage(optarg, 1); break; case 'W': wpage = optpage(optarg, BTX_MAXCWR); break; default: usage(); } argc -= optind; argv += optind; if (argc != 1) usage(); atexit(cleanup); btxld(*argv); return 0; } /* * Clean up after errors. */ static void cleanup(void) { if (tname) (void)remove(tname); } /* * Read the input files; write the output file; display information. */ static void btxld(const char *iname) { char name[FILENAME_MAX]; struct btx_hdr btx, btxle; struct hdr ihdr, ohdr; unsigned int ldr_size, cwr; int fdi[3], fdo, i; ldr_size = 0; for (i = I_LDR; i <= I_CLNT; i++) { fname = i == I_LDR ? lname : i == I_BTX ? bname : iname; if ((fdi[i] = open(fname, O_RDONLY)) == -1) err(2, "%s", fname); switch (i) { case I_LDR: gethdr(fdi[i], &ihdr); if (ihdr.fmt != F_BIN) Warn(fname, "Loader format is %s; processing as %s", fmtlist[ihdr.fmt], fmtlist[F_BIN]); ldr_size = ihdr.size; break; case I_BTX: getbtx(fdi[i], &btx); break; case I_CLNT: gethdr(fdi[i], &ihdr); if (ihdr.org && ihdr.org != BTX_PGSIZE) Warn(fname, "Client origin is 0x%x; expecting 0 or 0x%x", ihdr.org, BTX_PGSIZE); } } memset(&ohdr, 0, sizeof(ohdr)); ohdr.fmt = format; ohdr.text = ldr_size; ohdr.data = btx.btx_textsz + ihdr.size; ohdr.org = lentry; ohdr.entry = lentry; cwr = 0; if (wpage > 0 || (wpage == -1 && !(ihdr.flags & IMPURE))) { if (wpage > 0) cwr = wpage; else { cwr = howmany(ihdr.text, BTX_PGSIZE); if (cwr > BTX_MAXCWR) cwr = BTX_MAXCWR; } } if (ppage > 0 || (ppage && wpage && ihdr.org >= BTX_PGSIZE)) { btx.btx_flags |= BTX_MAPONE; if (!cwr) cwr++; } btx.btx_pgctl -= cwr; btx.btx_entry = Eflag ? centry : ihdr.entry; if ((size_t)snprintf(name, sizeof(name), "%s.tmp", oname) >= sizeof(name)) errx(2, "%s: Filename too long", oname); if ((fdo = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666)) == -1) err(2, "%s", name); if (!(tname = strdup(name))) err(2, NULL); puthdr(fdo, &ohdr); for (i = I_LDR; i <= I_CLNT; i++) { fname = i == I_LDR ? lname : i == I_BTX ? bname : iname; switch (i) { case I_LDR: copy(fdi[i], fdo, ldr_size, 0); seekx(fdo, ohdr.size += ohdr.text); break; case I_BTX: btxle = btx; btxle.btx_pgctl = htole16(btxle.btx_pgctl); btxle.btx_textsz = htole16(btxle.btx_textsz); btxle.btx_entry = htole32(btxle.btx_entry); writex(fdo, &btxle, sizeof(btxle)); copy(fdi[i], fdo, btx.btx_textsz - sizeof(btx), sizeof(btx)); break; case I_CLNT: copy(fdi[i], fdo, ihdr.size, 0); if (ftruncate(fdo, ohdr.size += ohdr.data)) err(2, "%s", tname); } if (close(fdi[i])) err(2, "%s", fname); } if (close(fdo)) err(2, "%s", tname); if (rename(tname, oname)) err(2, "%s: Can't rename to %s", tname, oname); free((void*)(intptr_t)tname); tname = NULL; if (verbose) { printf(binfo, btx.btx_majver, btx.btx_minver, btx.btx_textsz, BTX_ORIGIN(btx), BTX_ENTRY(btx), BTX_MAPPED(btx) * BTX_PGSIZE / 0x100000, !!(btx.btx_flags & BTX_MAPONE), BTX_MAPPED(btx) - btx.btx_pgctl - BTX_PGBASE / BTX_PGSIZE - BTX_MAPPED(btx) * 4 / BTX_PGSIZE); printf(cinfo, fmtlist[ihdr.fmt], ihdr.size, ihdr.text, ihdr.data, ihdr.bss, ihdr.entry); printf(oinfo, fmtlist[ohdr.fmt], ohdr.size, ohdr.text, ohdr.data, ohdr.org, ohdr.entry); } } /* * Read BTX file header. */ static void getbtx(int fd, struct btx_hdr * btx) { if (readx(fd, btx, sizeof(*btx), 0) != sizeof(*btx) || btx->btx_magic[0] != BTX_MAG0 || btx->btx_magic[1] != BTX_MAG1 || btx->btx_magic[2] != BTX_MAG2) errx(1, "%s: Not a BTX kernel", fname); btx->btx_pgctl = le16toh(btx->btx_pgctl); btx->btx_textsz = le16toh(btx->btx_textsz); btx->btx_entry = le32toh(btx->btx_entry); } /* * Get file size and read a.out or ELF header. */ static void gethdr(int fd, struct hdr *hdr) { struct stat sb; const struct exec *ex; const Elf32_Ehdr *ee; const Elf32_Phdr *ep; void *p; unsigned int fmt, x, n, i; memset(hdr, 0, sizeof(*hdr)); if (fstat(fd, &sb)) err(2, "%s", fname); if (sb.st_size > MAXU32) errx(1, "%s: Too big", fname); hdr->size = sb.st_size; if (!hdr->size) return; if ((p = mmap(NULL, hdr->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) err(2, "%s", fname); for (fmt = F_CNT - 1; !hdr->fmt && fmt; fmt--) switch (fmt) { case F_AOUT: ex = p; if (hdr->size >= sizeof(struct exec) && !N_BADMAG(*ex)) { hdr->fmt = fmt; x = N_GETMAGIC(*ex); if (x == OMAGIC || x == NMAGIC) { if (x == NMAGIC) Warn(fname, "Treating %s NMAGIC as OMAGIC", fmtlist[fmt]); hdr->flags |= IMPURE; } hdr->text = le32toh(ex->a_text); hdr->data = le32toh(ex->a_data); hdr->bss = le32toh(ex->a_bss); hdr->entry = le32toh(ex->a_entry); if (le32toh(ex->a_entry) >= BTX_PGSIZE) hdr->org = BTX_PGSIZE; } break; case F_ELF: ee = p; if (hdr->size >= sizeof(Elf32_Ehdr) && IS_ELF(*ee)) { hdr->fmt = fmt; for (n = i = 0; i < le16toh(ee->e_phnum); i++) { ep = (void *)((uint8_t *)p + le32toh(ee->e_phoff) + le16toh(ee->e_phentsize) * i); if (le32toh(ep->p_type) == PT_LOAD) switch (n++) { case 0: hdr->text = le32toh(ep->p_filesz); hdr->org = le32toh(ep->p_paddr); if (le32toh(ep->p_flags) & PF_W) hdr->flags |= IMPURE; break; case 1: hdr->data = le32toh(ep->p_filesz); hdr->bss = le32toh(ep->p_memsz) - le32toh(ep->p_filesz); break; case 2: Warn(fname, "Ignoring extra %s PT_LOAD segments", fmtlist[fmt]); } } hdr->entry = le32toh(ee->e_entry); } } if (munmap(p, hdr->size)) err(2, "%s", fname); } /* * Write a.out or ELF header. */ static void puthdr(int fd, struct hdr *hdr) { struct exec ex; struct elfh eh; switch (hdr->fmt) { case F_AOUT: memset(&ex, 0, sizeof(ex)); N_SETMAGIC(ex, ZMAGIC, MID_I386, 0); hdr->text = N_ALIGN(ex, hdr->text); ex.a_text = htole32(hdr->text); hdr->data = N_ALIGN(ex, hdr->data); ex.a_data = htole32(hdr->data); ex.a_entry = htole32(hdr->entry); writex(fd, &ex, sizeof(ex)); hdr->size = N_ALIGN(ex, sizeof(ex)); seekx(fd, hdr->size); break; case F_ELF: eh = elfhdr; eh.e.e_entry = htole32(hdr->entry); eh.p[0].p_vaddr = eh.p[0].p_paddr = htole32(hdr->org); eh.p[0].p_filesz = eh.p[0].p_memsz = htole32(hdr->text); eh.p[1].p_offset = htole32(le32toh(eh.p[0].p_offset) + le32toh(eh.p[0].p_filesz)); eh.p[1].p_vaddr = eh.p[1].p_paddr = htole32(roundup2(le32toh(eh.p[0].p_paddr) + le32toh(eh.p[0].p_memsz), 4096)); eh.p[1].p_filesz = eh.p[1].p_memsz = htole32(hdr->data); eh.sh[2].sh_addr = eh.p[0].p_vaddr; eh.sh[2].sh_offset = eh.p[0].p_offset; eh.sh[2].sh_size = eh.p[0].p_filesz; eh.sh[3].sh_addr = eh.p[1].p_vaddr; eh.sh[3].sh_offset = eh.p[1].p_offset; eh.sh[3].sh_size = eh.p[1].p_filesz; writex(fd, &eh, sizeof(eh)); hdr->size = sizeof(eh); } } /* * Safe copy from input file to output file. */ static void copy(int fdi, int fdo, size_t nbyte, off_t offset) { char buf[8192]; size_t n; while (nbyte) { if ((n = sizeof(buf)) > nbyte) n = nbyte; if (readx(fdi, buf, n, offset) != n) errx(2, "%s: Short read", fname); writex(fdo, buf, n); nbyte -= n; offset = -1; } } /* * Safe read from input file. */ static size_t readx(int fd, void *buf, size_t nbyte, off_t offset) { ssize_t n; if (offset != -1 && lseek(fd, offset, SEEK_SET) != offset) err(2, "%s", fname); if ((n = read(fd, buf, nbyte)) == -1) err(2, "%s", fname); return n; } /* * Safe write to output file. */ static void writex(int fd, const void *buf, size_t nbyte) { ssize_t n; if ((n = write(fd, buf, nbyte)) == -1) err(2, "%s", tname); if ((size_t)n != nbyte) errx(2, "%s: Short write", tname); } /* * Safe seek in output file. */ static void seekx(int fd, off_t offset) { if (lseek(fd, offset, SEEK_SET) != offset) err(2, "%s", tname); } /* * Convert an option argument to a format code. */ static unsigned int optfmt(const char *arg) { unsigned int i; for (i = 0; i < F_CNT && strcmp(arg, fmtlist[i]); i++); if (i == F_CNT) errx(1, "%s: Unknown format", arg); return i; } /* * Convert an option argument to an address. */ static uint32_t optaddr(const char *arg) { char *s; unsigned long x; errno = 0; x = strtoul(arg, &s, 0); if (errno || !*arg || *s || x > MAXU32) errx(1, "%s: Illegal address", arg); return x; } /* * Convert an option argument to a page number. */ static int optpage(const char *arg, int hi) { char *s; long x; errno = 0; x = strtol(arg, &s, 0); if (errno || !*arg || *s || x < 0 || x > hi) errx(1, "%s: Illegal page number", arg); return x; } /* * Display a warning. */ static void Warn(const char *locus, const char *fmt, ...) { va_list ap; char *s; if (!quiet) { asprintf(&s, "%s: Warning: %s", locus, fmt); va_start(ap, fmt); vwarnx(s, ap); va_end(ap); free(s); } } /* * Display usage information. */ static void usage(void) { fprintf(stderr, "%s\n%s\n", "usage: btxld [-qv] [-b file] [-E address] [-e address] [-f format]", " [-l file] [-o filename] [-P page] [-W page] file"); exit(1); } diff --git a/usr.sbin/ckdist/ckdist.c b/usr.sbin/ckdist/ckdist.c index caee507f246b..ccd3a7ddf298 100644 --- a/usr.sbin/ckdist/ckdist.c +++ b/usr.sbin/ckdist/ckdist.c @@ -1,447 +1,442 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1997 Robert Nordier * 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(S) ``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(S) 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include extern int crc(int fd, uint32_t *cval, off_t *clen); #define DISTMD5 1 /* MD5 format */ #define DISTINF 2 /* .inf format */ #define DISTTYPES 2 /* types supported */ #define E_UNKNOWN 1 /* Unknown format */ #define E_BADMD5 2 /* Invalid MD5 format */ #define E_BADINF 3 /* Invalid .inf format */ #define E_NAME 4 /* Can't derive component name */ #define E_LENGTH 5 /* Length mismatch */ #define E_CHKSUM 6 /* Checksum mismatch */ #define E_ERRNO 7 /* sys_errlist[errno] */ #define isfatal(err) ((err) && (err) <= E_NAME) #define NAMESIZE 256 /* filename buffer size */ #define MDSUMLEN 32 /* length of MD5 message digest */ #define isstdin(path) ((path)[0] == '-' && !(path)[1]) static const char *opt_dir; /* where to look for components */ static const char *opt_name; /* name for accessing components */ static int opt_all; /* report on all components */ static int opt_ignore; /* ignore missing components */ static int opt_recurse; /* search directories recursively */ static int opt_silent; /* silent about inaccessible files */ static int opt_type; /* dist type: md5 or inf */ static int opt_exist; /* just verify existence */ static int ckdist(const char *path, int type); static int chkmd5(FILE * fp, const char *path); static int chkinf(FILE * fp, const char *path); static int report(const char *path, const char *name, int error); static const char *distname(const char *path, const char *name, const char *ext); static const char *stripath(const char *path); static int distfile(const char *path); static int disttype(const char *name); static int fail(const char *path, const char *msg); static void usage(void) __dead2; int main(int argc, char *argv[]) { static char *arg[2]; struct stat sb; FTS *ftsp; FTSENT *f; int rval, c, type; while ((c = getopt(argc, argv, "ad:in:rst:x")) != -1) switch (c) { case 'a': opt_all = 1; break; case 'd': opt_dir = optarg; break; case 'i': opt_ignore = 1; break; case 'n': opt_name = optarg; break; case 'r': opt_recurse = 1; break; case 's': opt_silent = 1; break; case 't': if ((opt_type = disttype(optarg)) == 0) { warnx("illegal argument to -t option"); usage(); } break; case 'x': opt_exist = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc < 1) usage(); if (opt_dir) { if (stat(opt_dir, &sb)) err(2, "%s", opt_dir); if (!S_ISDIR(sb.st_mode)) errx(2, "%s: not a directory", opt_dir); } rval = 0; do { if (isstdin(*argv)) rval |= ckdist(*argv, opt_type); else if (stat(*argv, &sb)) rval |= fail(*argv, NULL); else if (S_ISREG(sb.st_mode)) rval |= ckdist(*argv, opt_type); else { arg[0] = *argv; if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL) err(2, "fts_open"); while (errno = 0, (f = fts_read(ftsp)) != NULL) switch (f->fts_info) { case FTS_DC: rval = fail(f->fts_path, "Directory causes a cycle"); break; case FTS_DNR: case FTS_ERR: case FTS_NS: rval = fail(f->fts_path, sys_errlist[f->fts_errno]); break; case FTS_D: if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL && fts_set(ftsp, f, FTS_SKIP)) err(2, "fts_set"); break; case FTS_F: if ((type = distfile(f->fts_name)) != 0 && (!opt_type || type == opt_type)) rval |= ckdist(f->fts_path, type); break; default: ; } if (errno) err(2, "fts_read"); if (fts_close(ftsp)) err(2, "fts_close"); } } while (*++argv); return rval; } static int ckdist(const char *path, int type) { FILE *fp; int rval, c; if (isstdin(path)) { path = "(stdin)"; fp = stdin; } else if ((fp = fopen(path, "r")) == NULL) return fail(path, NULL); if (!type) { if (fp != stdin) type = distfile(path); if (!type) if ((c = fgetc(fp)) != EOF) { type = c == 'M' ? DISTMD5 : c == 'P' ? DISTINF : 0; (void)ungetc(c, fp); } } switch (type) { case DISTMD5: rval = chkmd5(fp, path); break; case DISTINF: rval = chkinf(fp, path); break; default: rval = report(path, NULL, E_UNKNOWN); } if (ferror(fp)) warn("%s", path); if (fp != stdin && fclose(fp)) err(2, "%s", path); return rval; } static int chkmd5(FILE * fp, const char *path) { char buf[298]; /* "MD5 (NAMESIZE = MDSUMLEN" */ char name[NAMESIZE + 1]; char sum[MDSUMLEN + 1], chk[MDSUMLEN + 1]; const char *dname; char *s; int rval, error, c, fd; char ch; rval = 0; while (fgets(buf, sizeof(buf), fp)) { dname = NULL; error = 0; if (((c = sscanf(buf, "MD5 (%256s = %32s%c", name, sum, &ch)) != 3 && (!feof(fp) || c != 2)) || (c == 3 && ch != '\n') || (s = strrchr(name, ')')) == NULL || strlen(sum) != MDSUMLEN) error = E_BADMD5; else { *s = 0; if ((dname = distname(path, name, NULL)) == NULL) error = E_NAME; else if (opt_exist) { if ((fd = open(dname, O_RDONLY)) == -1) error = E_ERRNO; else if (close(fd)) err(2, "%s", dname); } else if (!MD5File(dname, chk)) error = E_ERRNO; else if (strcmp(chk, sum)) error = E_CHKSUM; } if (opt_ignore && error == E_ERRNO && errno == ENOENT) continue; if (error || opt_all) rval |= report(path, dname, error); if (isfatal(error)) break; } return rval; } static int chkinf(FILE * fp, const char *path) { char buf[30]; /* "cksum.2 = 10 6" */ char ext[3]; struct stat sb; const char *dname; off_t len; u_long sum; intmax_t sumlen; uint32_t chk; int rval, error, c, pieces, cnt, fd; char ch; rval = 0; for (cnt = -1; fgets(buf, sizeof(buf), fp); cnt++) { fd = -1; dname = NULL; error = 0; if (cnt == -1) { if ((c = sscanf(buf, "Pieces = %d%c", &pieces, &ch)) != 2 || ch != '\n' || pieces < 1) error = E_BADINF; } else if (((c = sscanf(buf, "cksum.%2s = %lu %jd%c", ext, &sum, &sumlen, &ch)) != 4 && (!feof(fp) || c != 3)) || (c == 4 && ch != '\n') || ext[0] != 'a' + cnt / 26 || ext[1] != 'a' + cnt % 26) error = E_BADINF; else if ((dname = distname(fp == stdin ? NULL : path, NULL, ext)) == NULL) error = E_NAME; else if ((fd = open(dname, O_RDONLY)) == -1) error = E_ERRNO; else if (fstat(fd, &sb)) error = E_ERRNO; else if (sb.st_size != (off_t)sumlen) error = E_LENGTH; else if (!opt_exist) { if (crc(fd, &chk, &len)) error = E_ERRNO; else if (chk != sum) error = E_CHKSUM; } if (fd != -1 && close(fd)) err(2, "%s", dname); if (opt_ignore && error == E_ERRNO && errno == ENOENT) continue; if (error || (opt_all && cnt >= 0)) rval |= report(path, dname, error); if (isfatal(error)) break; } return rval; } static int report(const char *path, const char *name, int error) { if (name) name = stripath(name); switch (error) { case E_UNKNOWN: printf("%s: Unknown format\n", path); break; case E_BADMD5: printf("%s: Invalid MD5 format\n", path); break; case E_BADINF: printf("%s: Invalid .inf format\n", path); break; case E_NAME: printf("%s: Can't derive component name\n", path); break; case E_LENGTH: printf("%s: %s: Size mismatch\n", path, name); break; case E_CHKSUM: printf("%s: %s: Checksum mismatch\n", path, name); break; case E_ERRNO: printf("%s: %s: %s\n", path, name, sys_errlist[errno]); break; default: printf("%s: %s: OK\n", path, name); } return error != 0; } static const char * distname(const char *path, const char *name, const char *ext) { static char buf[NAMESIZE]; size_t plen, nlen; char *s; if (opt_name) name = opt_name; else if (!name) { if (!path) return NULL; name = stripath(path); } nlen = strlen(name); if (ext && nlen > 4 && name[nlen - 4] == '.' && disttype(name + nlen - 3) == DISTINF) nlen -= 4; if (opt_dir) { path = opt_dir; plen = strlen(path); } else plen = path && (s = strrchr(path, '/')) != NULL ? (size_t)(s - path) : 0; if (plen + (plen > 0) + nlen + (ext ? 3 : 0) >= sizeof(buf)) return NULL; s = buf; if (plen) { memcpy(s, path, plen); s += plen; *s++ = '/'; } memcpy(s, name, nlen); s += nlen; if (ext) { *s++ = '.'; memcpy(s, ext, 2); s += 2; } *s = 0; return buf; } static const char * stripath(const char *path) { const char *s; return ((s = strrchr(path, '/')) != NULL && s[1] ? s + 1 : path); } static int distfile(const char *path) { const char *s; int type; if ((type = disttype(path)) == DISTMD5 || ((s = strrchr(path, '.')) != NULL && s > path && (type = disttype(s + 1)) != 0)) return type; return 0; } static int disttype(const char *name) { static const char dname[DISTTYPES][4] = {"md5", "inf"}; int i; for (i = 0; i < DISTTYPES; i++) if (!strcmp(dname[i], name)) return 1 + i; return 0; } static int fail(const char *path, const char *msg) { if (opt_silent) return 0; warnx("%s: %s", path, msg ? msg : sys_errlist[errno]); return 2; } static void usage(void) { fprintf(stderr, "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n"); exit(2); } diff --git a/usr.sbin/config/main.cc b/usr.sbin/config/main.cc index f6c99e32c594..59af07b76c8d 100644 --- a/usr.sbin/config/main.cc +++ b/usr.sbin/config/main.cc @@ -1,828 +1,826 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 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. * 3. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "y.tab.h" #include "config.h" #include "configvers.h" #ifndef TRUE #define TRUE (1) #endif #ifndef FALSE #define FALSE (0) #endif #define CDIR "../compile/" char *machinename; char *machinearch; struct cfgfile_head cfgfiles; struct cputype_head cputype; struct opt_head opt, mkopt, rmopts; struct opt_list_head otab; struct envvar_head envvars; struct hint_head hints; struct includepath_head includepath; char * PREFIX; char destdir[MAXPATHLEN]; char srcdir[MAXPATHLEN]; int debugging; int found_defaults; int incignore; /* * Preserve old behaviour in INCLUDE_CONFIG_FILE handling (files are included * literally). */ int filebased = 0; int versreq; static void configfile(void); static void get_srcdir(void); static void usage(void); static void cleanheaders(char *); static void kernconfdump(const char *); static void badversion(void); static void checkversion(void); struct hdr_list { const char *h_name; struct hdr_list *h_next; } *htab; static std::stringstream line_buf; /* * Config builds a set of files for building a UNIX * system given a description of the desired system. */ int main(int argc, char **argv) { struct stat buf; int ch, len; char *p; char *kernfile; struct includepath* ipath; int printmachine; bool cust_dest = false; printmachine = 0; kernfile = NULL; SLIST_INIT(&includepath); SLIST_INIT(&cputype); SLIST_INIT(&mkopt); SLIST_INIT(&opt); SLIST_INIT(&rmopts); STAILQ_INIT(&cfgfiles); STAILQ_INIT(&dtab); STAILQ_INIT(&fntab); STAILQ_INIT(&ftab); STAILQ_INIT(&hints); STAILQ_INIT(&envvars); while ((ch = getopt(argc, argv, "Cd:gI:mps:Vx:")) != -1) switch (ch) { case 'C': filebased = 1; break; case 'd': if (*destdir == '\0') strlcpy(destdir, optarg, sizeof(destdir)); else errx(EXIT_FAILURE, "directory already set"); cust_dest = true; break; case 'g': debugging++; break; case 'I': ipath = (struct includepath *) \ calloc(1, sizeof (struct includepath)); if (ipath == NULL) err(EXIT_FAILURE, "calloc"); ipath->path = optarg; SLIST_INSERT_HEAD(&includepath, ipath, path_next); break; case 'm': printmachine = 1; break; case 's': if (*srcdir == '\0') strlcpy(srcdir, optarg, sizeof(srcdir)); else errx(EXIT_FAILURE, "src directory already set"); break; case 'V': printf("%d\n", CONFIGVERS); exit(0); case 'x': kernfile = optarg; break; case '?': default: usage(); } argc -= optind; argv += optind; if (kernfile != NULL) { kernconfdump(kernfile); exit(EXIT_SUCCESS); } if (argc != 1) usage(); PREFIX = *argv; if (stat(PREFIX, &buf) != 0 || !S_ISREG(buf.st_mode)) err(2, "%s", PREFIX); if (freopen("DEFAULTS", "r", stdin) != NULL) { found_defaults = 1; yyfile = "DEFAULTS"; } else { if (freopen(PREFIX, "r", stdin) == NULL) err(2, "%s", PREFIX); yyfile = PREFIX; } if (*destdir != '\0') { len = strlen(destdir); while (len > 1 && destdir[len - 1] == '/') destdir[--len] = '\0'; if (*srcdir == '\0') get_srcdir(); } else { strlcpy(destdir, CDIR, sizeof(destdir)); strlcat(destdir, PREFIX, sizeof(destdir)); } if (yyparse()) exit(3); /* * Ensure that required elements (machine, cpu, ident) are present. */ if (machinename == NULL) { printf("Specify machine type, e.g. ``machine i386''\n"); exit(1); } if (ident == NULL) { printf("no ident line specified\n"); exit(1); } if (SLIST_EMPTY(&cputype)) { printf("cpu type must be specified\n"); exit(1); } checkversion(); if (printmachine) { printf("%s\t%s\n",machinename,machinearch); exit(0); } /* * Make CDIR directory, if doing a default destination. Some version * control systems delete empty directories and this seemlessly copes. */ if (!cust_dest && stat(CDIR, &buf)) if (mkdir(CDIR, 0777)) err(2, "%s", CDIR); /* Create the compile directory */ p = path((char *)NULL); if (stat(p, &buf)) { if (mkdir(p, 0777)) err(2, "%s", p); } else if (!S_ISDIR(buf.st_mode)) errx(EXIT_FAILURE, "%s isn't a directory", p); configfile(); /* put config file into kernel*/ options(); /* make options .h files */ makefile(); /* build Makefile */ makeenv(); /* build env.c */ makehints(); /* build hints.c */ headers(); /* make a lot of .h files */ cleanheaders(p); printf("Kernel build directory is %s\n", p); printf("Don't forget to do ``make cleandepend && make depend''\n"); exit(0); } /* * get_srcdir * determine the root of the kernel source tree * and save that in srcdir. */ static void get_srcdir(void) { struct stat lg, phy; char *p, *pwd; int i; if (realpath("../..", srcdir) == NULL) err(EXIT_FAILURE, "Unable to find root of source tree"); if ((pwd = getenv("PWD")) != NULL && *pwd == '/' && (pwd = strdup(pwd)) != NULL) { /* Remove the last two path components. */ for (i = 0; i < 2; i++) { if ((p = strrchr(pwd, '/')) == NULL) { free(pwd); return; } *p = '\0'; } if (stat(pwd, &lg) != -1 && stat(srcdir, &phy) != -1 && lg.st_dev == phy.st_dev && lg.st_ino == phy.st_ino) strlcpy(srcdir, pwd, MAXPATHLEN); free(pwd); } } static void usage(void) { fprintf(stderr, "usage: config [-CgmpV] [-d destdir] [-s srcdir] sysname\n"); fprintf(stderr, " config -x kernel\n"); exit(EX_USAGE); } static void init_line_buf(void) { line_buf.str(""); } static std::string get_line_buf(void) { line_buf.flush(); if (!line_buf.good()) { errx(EXIT_FAILURE, "failed to generate line buffer, " "partial line = %s", line_buf.str().c_str()); } return line_buf.str(); } /* * get_word * returns EOF on end of file * NULL on end of line * pointer to the word otherwise */ configword get_word(FILE *fp) { int ch; int escaped_nl = 0; init_line_buf(); begin: while ((ch = getc(fp)) != EOF) if (ch != ' ' && ch != '\t') break; if (ch == EOF) return (configword().eof(true)); if (ch == '\\'){ escaped_nl = 1; goto begin; } if (ch == '\n') { if (escaped_nl){ escaped_nl = 0; goto begin; } else return (configword().eol(true)); } line_buf << (char)ch; /* Negation operator is a word by itself. */ if (ch == '!') { return (configword(get_line_buf())); } while ((ch = getc(fp)) != EOF) { if (isspace(ch)) break; line_buf << (char)ch; } if (ch == EOF) return (configword().eof(true)); (void) ungetc(ch, fp); return (configword(get_line_buf())); } /* * get_quoted_word * like get_word but will accept something in double or single quotes * (to allow embedded spaces). */ configword get_quoted_word(FILE *fp) { int ch; int escaped_nl = 0; init_line_buf(); begin: while ((ch = getc(fp)) != EOF) if (ch != ' ' && ch != '\t') break; if (ch == EOF) return (configword().eof(true)); if (ch == '\\'){ escaped_nl = 1; goto begin; } if (ch == '\n') { if (escaped_nl){ escaped_nl = 0; goto begin; } else return (configword().eol(true)); } if (ch == '"' || ch == '\'') { int quote = ch; escaped_nl = 0; while ((ch = getc(fp)) != EOF) { if (ch == quote && !escaped_nl) break; if (ch == '\n' && !escaped_nl) { printf("config: missing quote reading `%s'\n", get_line_buf().c_str()); exit(2); } if (ch == '\\' && !escaped_nl) { escaped_nl = 1; continue; } if (ch != quote && escaped_nl) line_buf << "\\"; line_buf << (char)ch; escaped_nl = 0; } } else { line_buf << (char)ch; while ((ch = getc(fp)) != EOF) { if (isspace(ch)) break; line_buf << (char)ch; } if (ch != EOF) (void) ungetc(ch, fp); } if (ch == EOF) return (configword().eof(true)); return (configword(get_line_buf())); } /* * prepend the path to a filename */ char * path(const char *file) { char *cp = NULL; if (file) asprintf(&cp, "%s/%s", destdir, file); else cp = strdup(destdir); if (cp == NULL) err(EXIT_FAILURE, "malloc"); return (cp); } /* * Generate configuration file based on actual settings. With this mode, user * will be able to obtain and build conifguration file with one command. */ static void configfile_dynamic(std::ostringstream &cfg) { struct cputype *cput; struct device *d; struct opt *ol; char *lend; unsigned int i; asprintf(&lend, "\\n\\\n"); assert(lend != NULL); cfg << "options\t" << OPT_AUTOGEN << lend; cfg << "ident\t" << ident << lend; cfg << "machine\t" << machinename << lend; SLIST_FOREACH(cput, &cputype, cpu_next) cfg << "cpu\t" << cput->cpu_name << lend; SLIST_FOREACH(ol, &mkopt, op_next) cfg << "makeoptions\t" << ol->op_name << '=' << ol->op_value << lend; SLIST_FOREACH(ol, &opt, op_next) { if (strncmp(ol->op_name, "DEV_", 4) == 0) continue; cfg << "options\t" << ol->op_name; if (ol->op_value != NULL) { cfg << '='; for (i = 0; i < strlen(ol->op_value); i++) { if (ol->op_value[i] == '"') cfg << '\\' << ol->op_value[i]; else cfg << ol->op_value[i]; } } cfg << lend; } /* * Mark this file as containing everything we need. */ STAILQ_FOREACH(d, &dtab, d_next) cfg << "device\t" << d->d_name << lend; free(lend); } /* * Generate file from the configuration files. */ static void configfile_filebased(std::ostringstream &cfg) { FILE *cff; struct cfgfile *cf; int i; /* * Try to read all configuration files. Since those will be present as * C string in the macro, we have to slash their ends then the line * wraps. */ STAILQ_FOREACH(cf, &cfgfiles, cfg_next) { cff = fopen(cf->cfg_path, "r"); if (cff == NULL) { warn("Couldn't open file %s", cf->cfg_path); continue; } while ((i = getc(cff)) != EOF) { if (i == '\n') cfg << "\\n\\\n"; else if (i == '"' || i == '\'') cfg << '\\' << i; else cfg << i; } fclose(cff); } } static void configfile(void) { FILE *fo; std::ostringstream cfg; char *p; /* Add main configuration file to the list of files to be included */ cfgfile_add(PREFIX); p = path("config.c.new"); fo = fopen(p, "w"); if (!fo) err(2, "%s", p); free(p); if (filebased) { /* Is needed, can be used for backward compatibility. */ configfile_filebased(cfg); } else { configfile_dynamic(cfg); } cfg.flush(); /* * We print first part of the template, replace our tag with * configuration files content and later continue writing our * template. */ p = strstr(kernconfstr, KERNCONFTAG); if (p == NULL) errx(EXIT_FAILURE, "Something went terribly wrong!"); *p = '\0'; fprintf(fo, "%s", kernconfstr); fprintf(fo, "%s", cfg.str().c_str()); p += strlen(KERNCONFTAG); fprintf(fo, "%s", p); fclose(fo); moveifchanged("config.c.new", "config.c"); cfgfile_removeall(); } /* * moveifchanged -- * compare two files; rename if changed. */ void moveifchanged(const char *from_name, const char *to_name) { char *p, *q; char *from_path, *to_path; int changed; size_t tsize; struct stat from_sb, to_sb; int from_fd, to_fd; changed = 0; from_path = path(from_name); to_path = path(to_name); if ((from_fd = open(from_path, O_RDONLY)) < 0) err(EX_OSERR, "moveifchanged open(%s)", from_name); if ((to_fd = open(to_path, O_RDONLY)) < 0) changed++; if (!changed && fstat(from_fd, &from_sb) < 0) err(EX_OSERR, "moveifchanged fstat(%s)", from_path); if (!changed && fstat(to_fd, &to_sb) < 0) err(EX_OSERR, "moveifchanged fstat(%s)", to_path); if (!changed && from_sb.st_size != to_sb.st_size) changed++; if (!changed) { tsize = (size_t)from_sb.st_size; p = (char *)mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0); if (p == MAP_FAILED) err(EX_OSERR, "mmap %s", from_path); q = (char *)mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0); if (q == MAP_FAILED) err(EX_OSERR, "mmap %s", to_path); changed = memcmp(p, q, tsize); munmap(p, tsize); munmap(q, tsize); } if (changed) { if (rename(from_path, to_path) < 0) err(EX_OSERR, "rename(%s, %s)", from_path, to_path); } else { if (unlink(from_path) < 0) err(EX_OSERR, "unlink(%s)", from_path); } close(from_fd); if (to_fd >= 0) close(to_fd); free(from_path); free(to_path); } static void cleanheaders(char *p) { DIR *dirp; struct dirent *dp; struct file_list *fl; struct hdr_list *hl; size_t len; remember("y.tab.h"); remember("setdefs.h"); STAILQ_FOREACH(fl, &ftab, f_next) remember(fl->f_fn); /* * Scan the build directory and clean out stuff that looks like * it might have been a leftover NFOO header, etc. */ if ((dirp = opendir(p)) == NULL) err(EX_OSERR, "opendir %s", p); while ((dp = readdir(dirp)) != NULL) { len = strlen(dp->d_name); /* Skip non-headers */ if (len < 2 || dp->d_name[len - 2] != '.' || dp->d_name[len - 1] != 'h') continue; /* Skip special stuff, eg: bus_if.h, but check opt_*.h */ if (strchr(dp->d_name, '_') && strncmp(dp->d_name, "opt_", 4) != 0) continue; /* Check if it is a target file */ for (hl = htab; hl != NULL; hl = hl->h_next) { if (eq(dp->d_name, hl->h_name)) { break; } } if (hl) continue; printf("Removing stale header: %s\n", dp->d_name); p = path(dp->d_name); if (unlink(p) == -1) warn("unlink %s", dp->d_name); free(p); } (void)closedir(dirp); } void remember(const char *file) { const char *s; struct hdr_list *hl; if ((s = strrchr(file, '/')) != NULL) s = ns(s + 1); else s = ns(file); if (strchr(s, '_') && strncmp(s, "opt_", 4) != 0) { free(__DECONST(char *, s)); return; } for (hl = htab; hl != NULL; hl = hl->h_next) { if (eq(s, hl->h_name)) { free(__DECONST(char *, s)); return; } } hl = (struct hdr_list *)calloc(1, sizeof(*hl)); if (hl == NULL) err(EXIT_FAILURE, "calloc"); hl->h_name = s; hl->h_next = htab; htab = hl; } /* * This one is quick hack. Will be probably moved to elf(3) interface. * It takes kernel configuration file name, passes it as an argument to * elfdump -a, which output is parsed by some UNIX tools... */ static void kernconfdump(const char *file) { struct stat st; FILE *fp, *pp; int error, osz, r; size_t i, off, size, t1, t2, align; char *cmd, *o; r = open(file, O_RDONLY); if (r == -1) err(EXIT_FAILURE, "Couldn't open file '%s'", file); error = fstat(r, &st); if (error == -1) err(EXIT_FAILURE, "fstat() failed"); if (S_ISDIR(st.st_mode)) errx(EXIT_FAILURE, "'%s' is a directory", file); fp = fdopen(r, "r"); if (fp == NULL) err(EXIT_FAILURE, "fdopen() failed"); osz = 1024; o = (char *)calloc(1, osz); if (o == NULL) err(EXIT_FAILURE, "Couldn't allocate memory"); /* ELF note section header. */ asprintf(&cmd, "/usr/bin/elfdump -c %s | grep -A 8 kern_conf" "| tail -5 | cut -d ' ' -f 2 | paste - - - - -", file); if (cmd == NULL) errx(EXIT_FAILURE, "asprintf() failed"); pp = popen(cmd, "r"); if (pp == NULL) errx(EXIT_FAILURE, "popen() failed"); free(cmd); (void)fread(o, osz, 1, pp); pclose(pp); r = sscanf(o, "%zu%zu%zu%zu%zu", &off, &size, &t1, &t2, &align); free(o); if (size > SIZE_MAX - off || off + size > (size_t)st.st_size) errx(EXIT_FAILURE, "%s: incoherent ELF headers", file); if (r != 5) errx(EXIT_FAILURE, "File %s doesn't contain configuration " "file. Either unsupported, or not compiled with " "INCLUDE_CONFIG_FILE", file); r = fseek(fp, off, SEEK_CUR); if (r != 0) err(EXIT_FAILURE, "fseek() failed"); for (i = 0; i < size; i++) { r = fgetc(fp); if (r == EOF) break; if (r == '\0') { assert(i == size - 1 && ("\\0 found in the middle of a file")); break; } fputc(r, stdout); } fclose(fp); } static void badversion(void) { fprintf(stderr, "ERROR: version of config(8) does not match kernel!\n"); fprintf(stderr, "config version = %d, ", CONFIGVERS); fprintf(stderr, "version required = %d\n\n", versreq); fprintf(stderr, "Make sure that /usr/src/usr.sbin/config is in sync\n"); fprintf(stderr, "with your /usr/src/sys and install a new config binary\n"); fprintf(stderr, "before trying this again.\n\n"); fprintf(stderr, "If running the new config fails check your config\n"); fprintf(stderr, "file against the GENERIC or LINT config files for\n"); fprintf(stderr, "changes in config syntax, or option/device naming\n"); fprintf(stderr, "conventions\n\n"); exit(1); } static void checkversion(void) { FILE *ifp; char line[BUFSIZ]; ifp = open_makefile_template(); while (fgets(line, BUFSIZ, ifp) != 0) { if (*line != '%') continue; if (strncmp(line, "%VERSREQ=", 9) != 0) continue; versreq = atoi(line + 9); if (MAJOR_VERS(versreq) == MAJOR_VERS(CONFIGVERS) && versreq <= CONFIGVERS) continue; badversion(); } fclose(ifp); } diff --git a/usr.sbin/config/mkheaders.c b/usr.sbin/config/mkheaders.c index 5dfde12c1db8..f1fc967bf1be 100644 --- a/usr.sbin/config/mkheaders.c +++ b/usr.sbin/config/mkheaders.c @@ -1,68 +1,66 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)mkheaders.c 8.1 (Berkeley) 6/6/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * This used to generate a bunch of headers files related to devices when * device counters were supported. Support for that was removed in 2005. * Since then, all we've done is to report unknown devices in this file. * It's kept its historical name, despite no longer generating headers. */ #include #include #include "config.h" #include "y.tab.h" void headers(void) { struct device *dp; int errors; errors = 0; STAILQ_FOREACH(dp, &dtab, d_next) { if (!(dp->d_done & DEVDONE)) { warnx("Error: device \"%s\" is unknown", dp->d_name); errors++; } } if (errors) errx(1, "%d errors", errors); } diff --git a/usr.sbin/config/mkmakefile.cc b/usr.sbin/config/mkmakefile.cc index 232933711ce5..49990c4a250c 100644 --- a/usr.sbin/config/mkmakefile.cc +++ b/usr.sbin/config/mkmakefile.cc @@ -1,844 +1,842 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1990, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)mkmakefile.c 8.1 (Berkeley) 6/6/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Build the makefile for the system, from * the information in the files files and the * additional files for the machine being compiled to. */ #include #include #include #include #include #include #include #include #include #include #include "y.tab.h" #include "config.h" #include "configvers.h" typedef std::unordered_map env_map; static char *tail(char *); static void do_clean(FILE *); static void do_rules(FILE *); static void do_xxfiles(char *, FILE *); static void do_objs(FILE *); static void do_before_depend(FILE *); static void read_files(void); static void sanitize_envline(char *result, const char *src); static bool preprocess(char *line, char *result); static void process_into_file(char *line, FILE *ofp); static int process_into_map(char *line, env_map &emap); static void dump_map(env_map &emap, FILE *ofp); static void __printflike(1, 2) errout(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); exit(1); } /* * Lookup a file, by name. */ static struct file_list * fl_lookup(char *file) { struct file_list *fp; STAILQ_FOREACH(fp, &ftab, f_next) { if (eq(fp->f_fn, file)) return (fp); } return (0); } /* * Make a new file list entry */ static struct file_list * new_fent(void) { struct file_list *fp; fp = (struct file_list *) calloc(1, sizeof *fp); if (fp == NULL) err(EXIT_FAILURE, "calloc"); STAILQ_INSERT_TAIL(&ftab, fp, f_next); return (fp); } /* * Open the correct Makefile and return it, or error out. */ FILE * open_makefile_template(void) { FILE *ifp; char line[BUFSIZ]; snprintf(line, sizeof(line), "../../conf/Makefile.%s", machinename); ifp = fopen(line, "r"); if (ifp == NULL) { snprintf(line, sizeof(line), "Makefile.%s", machinename); ifp = fopen(line, "r"); } if (ifp == NULL) err(1, "%s", line); return (ifp); } /* * Build the makefile from the skeleton */ void makefile(void) { FILE *ifp, *ofp; char line[BUFSIZ]; struct opt *op, *t; read_files(); ifp = open_makefile_template(); ofp = fopen(path("Makefile.new"), "w"); if (ofp == NULL) err(1, "%s", path("Makefile.new")); fprintf(ofp, "KERN_IDENT=%s\n", ident); fprintf(ofp, "MACHINE=%s\n", machinename); fprintf(ofp, "MACHINE_ARCH=%s\n", machinearch); SLIST_FOREACH_SAFE(op, &mkopt, op_next, t) { fprintf(ofp, "%s=%s", op->op_name, op->op_value); while ((op = SLIST_NEXT(op, op_append)) != NULL) fprintf(ofp, " %s", op->op_value); fprintf(ofp, "\n"); } if (debugging) fprintf(ofp, "DEBUG=-g\n"); if (*srcdir != '\0') fprintf(ofp,"S=%s\n", srcdir); while (fgets(line, BUFSIZ, ifp) != NULL) { if (*line != '%') { fprintf(ofp, "%s", line); continue; } if (eq(line, "%BEFORE_DEPEND\n")) do_before_depend(ofp); else if (eq(line, "%OBJS\n")) do_objs(ofp); else if (strncmp(line, "%FILES.", 7) == 0) do_xxfiles(line, ofp); else if (eq(line, "%RULES\n")) do_rules(ofp); else if (eq(line, "%CLEAN\n")) do_clean(ofp); else if (strncmp(line, "%VERSREQ=", 9) == 0) line[0] = '\0'; /* handled elsewhere */ else fprintf(stderr, "Unknown %% construct in generic makefile: %s", line); } (void) fclose(ifp); (void) fclose(ofp); moveifchanged("Makefile.new", "Makefile"); } static void sanitize_envline(char *result, const char *src) { const char *eq; char c, *dst; bool leading; /* If there is no '=' it's not a well-formed name=value line. */ if ((eq = strchr(src, '=')) == NULL) { *result = '\0'; return; } dst = result; /* Copy chars before the '=', skipping any leading spaces/quotes. */ leading = true; while (src < eq) { c = *src++; if (leading && (isspace(c) || c == '"')) continue; *dst++ = c; leading = false; } /* If it was all leading space, we don't have a well-formed line. */ if (leading) { *result = '\0'; return; } /* Trim spaces/quotes immediately before the '=', then copy the '='. */ while (isspace(dst[-1]) || dst[-1] == '"') --dst; *dst++ = *src++; /* Copy chars after the '=', skipping any leading whitespace. */ leading = true; while ((c = *src++) != '\0') { if (leading && (isspace(c) || c == '"')) continue; *dst++ = c; leading = false; } /* If it was all leading space, it's a valid 'var=' (nil value). */ if (leading) { *dst = '\0'; return; } /* Trim trailing whitespace and quotes. */ while (isspace(dst[-1]) || dst[-1] == '"') --dst; *dst = '\0'; } /* * Returns true if the caller may use the string. */ static bool preprocess(char *line, char *result) { char *s; /* Strip any comments */ if ((s = strchr(line, '#')) != NULL) *s = '\0'; sanitize_envline(result, line); /* Return true if it's non-empty */ return (*result != '\0'); } static void process_into_file(char *line, FILE *ofp) { char result[BUFSIZ]; if (preprocess(line, result)) fprintf(ofp, "\"%s\\0\"\n", result); } static int process_into_map(char *line, env_map &emap) { char result[BUFSIZ], *s; if (preprocess(line, result)) { s = strchr(result, '='); if (s == NULL) return (EINVAL); *s = '\0'; emap[result] = s + 1; } return (0); } static void dump_map(env_map &emap, FILE *ofp) { for (auto iter : emap) { fprintf(ofp, "\"%s=%s\\0\"\n", iter.first.c_str(), iter.second.c_str()); } } /* * Build hints.c from the skeleton */ void makehints(void) { FILE *ifp, *ofp; env_map emap; char line[BUFSIZ]; struct hint *hint; ofp = fopen(path("hints.c.new"), "w"); if (ofp == NULL) err(1, "%s", path("hints.c.new")); fprintf(ofp, "#include \n"); fprintf(ofp, "#include \n"); fprintf(ofp, "\n"); /* * Write out hintmode for older kernels. Remove when config(8) major * version rolls over. */ if (versreq <= CONFIGVERS_ENVMODE_REQ) fprintf(ofp, "int hintmode = %d;\n", !STAILQ_EMPTY(&hints) ? 1 : 0); fprintf(ofp, "char static_hints[] = {\n"); STAILQ_FOREACH(hint, &hints, hint_next) { ifp = fopen(hint->hint_name, "r"); if (ifp == NULL) err(1, "%s", hint->hint_name); while (fgets(line, BUFSIZ, ifp) != NULL) { if (process_into_map(line, emap) != 0) errout("%s: malformed line: %s\n", hint->hint_name, line); } dump_map(emap, ofp); fclose(ifp); } fprintf(ofp, "\"\\0\"\n};\n"); fclose(ofp); moveifchanged("hints.c.new", "hints.c"); } /* * Build env.c from the skeleton */ void makeenv(void) { FILE *ifp, *ofp; env_map emap; char line[BUFSIZ]; struct envvar *envvar; ofp = fopen(path("env.c.new"), "w"); if (ofp == NULL) err(1, "%s", path("env.c.new")); fprintf(ofp, "#include \n"); fprintf(ofp, "#include \n"); fprintf(ofp, "\n"); /* * Write out envmode for older kernels. Remove when config(8) major * version rolls over. */ if (versreq <= CONFIGVERS_ENVMODE_REQ) fprintf(ofp, "int envmode = %d;\n", !STAILQ_EMPTY(&envvars) ? 1 : 0); fprintf(ofp, "char static_env[] = {\n"); STAILQ_FOREACH(envvar, &envvars, envvar_next) { if (envvar->env_is_file) { ifp = fopen(envvar->env_str, "r"); if (ifp == NULL) err(1, "%s", envvar->env_str); while (fgets(line, BUFSIZ, ifp) != NULL) { if (process_into_map(line, emap) != 0) errout("%s: malformed line: %s\n", envvar->env_str, line); } dump_map(emap, ofp); fclose(ifp); } else process_into_file(envvar->env_str, ofp); } fprintf(ofp, "\"\\0\"\n};\n"); fclose(ofp); moveifchanged("env.c.new", "env.c"); } static void read_file(char *fname) { char ifname[MAXPATHLEN]; FILE *fp; struct file_list *tp; struct device *dp; struct opt *op; configword wd; char *rfile, *compilewith, *depends, *clean, *warning; const char *objprefix; int compile, match, nreqs, std, filetype, negate, imp_rule, no_ctfconvert, no_obj, before_depend, nowerror; fp = fopen(fname, "r"); if (fp == NULL) err(1, "%s", fname); next: /* * include "filename" * filename [ standard | optional ] * [ dev* [ | dev* ... ] | [ no-obj ] * [ compile-with "compile rule" [no-implicit-rule] ] * [ dependency "dependency-list"] [ before-depend ] * [ clean "file-list"] [ warning "text warning" ] * [ obj-prefix "file prefix"] * [ nowerror ] [ local ] */ wd = get_word(fp); if (wd.eof()) { (void) fclose(fp); return; } if (wd.eol()) goto next; if (wd[0] == '#') { while (!(wd = get_word(fp)).eof() && !wd.eol()) ; goto next; } if (eq(wd, "include")) { wd = get_quoted_word(fp); if (wd.eof() || wd.eol()) errout("%s: missing include filename.\n", fname); (void) snprintf(ifname, sizeof(ifname), "../../%s", wd->c_str()); read_file(ifname); while (!(wd = get_word(fp)).eof() && !wd.eol()) ; goto next; } rfile = ns(wd); wd = get_word(fp); if (wd.eof()) return; if (wd.eol()) errout("%s: No type for %s.\n", fname, rfile); tp = fl_lookup(rfile); compile = 0; match = 1; nreqs = 0; compilewith = NULL; depends = NULL; clean = NULL; warning = NULL; std = 0; imp_rule = 0; no_ctfconvert = 0; no_obj = 0; before_depend = 0; nowerror = 0; negate = 0; filetype = NORMAL; objprefix = ""; if (eq(wd, "standard")) std = 1; else if (!eq(wd, "optional")) errout("%s: \"%s\" %s must be optional or standard\n", fname, wd->c_str(), rfile); for (wd = get_word(fp); !wd.eol(); wd = get_word(fp)) { if (wd.eof()) return; if (eq(wd, "!")) { negate = 1; continue; } if (eq(wd, "|")) { if (nreqs == 0) errout("%s: syntax error describing %s\n", fname, rfile); compile += match; match = 1; nreqs = 0; continue; } if (eq(wd, "no-ctfconvert")) { no_ctfconvert++; continue; } if (eq(wd, "no-obj")) { no_obj++; continue; } if (eq(wd, "no-implicit-rule")) { if (compilewith == NULL) errout("%s: alternate rule required when " "\"no-implicit-rule\" is specified for" " %s.\n", fname, rfile); imp_rule++; continue; } if (eq(wd, "before-depend")) { before_depend++; continue; } if (eq(wd, "dependency")) { wd = get_quoted_word(fp); if (wd.eof() || wd.eol()) errout("%s: %s missing dependency string.\n", fname, rfile); depends = ns(wd); continue; } if (eq(wd, "clean")) { wd = get_quoted_word(fp); if (wd.eof() || wd.eol()) errout("%s: %s missing clean file list.\n", fname, rfile); clean = ns(wd); continue; } if (eq(wd, "compile-with")) { wd = get_quoted_word(fp); if (wd.eof() || wd.eol()) errout("%s: %s missing compile command string.\n", fname, rfile); compilewith = ns(wd); continue; } if (eq(wd, "warning")) { wd = get_quoted_word(fp); if (wd.eof() || wd.eol()) errout("%s: %s missing warning text string.\n", fname, rfile); warning = ns(wd); continue; } if (eq(wd, "obj-prefix")) { wd = get_quoted_word(fp); if (wd.eof() || wd.eol()) errout("%s: %s missing object prefix string.\n", fname, rfile); objprefix = ns(wd); continue; } if (eq(wd, "nowerror")) { nowerror = 1; continue; } if (eq(wd, "local")) { filetype = LOCAL; continue; } if (eq(wd, "no-depend")) { filetype = NODEPEND; continue; } nreqs++; if (std) errout("standard entry %s has optional inclusion specifier %s!\n", rfile, wd->c_str()); STAILQ_FOREACH(dp, &dtab, d_next) if (eq(dp->d_name, wd)) { if (negate) match = 0; else dp->d_done |= DEVDONE; goto nextparam; } SLIST_FOREACH(op, &opt, op_next) if (op->op_value == 0 && strcasecmp(op->op_name, wd) == 0) { if (negate) match = 0; goto nextparam; } match &= negate; nextparam:; negate = 0; } compile += match; if (compile && tp == NULL) { if (std == 0 && nreqs == 0) errout("%s: what is %s optional on?\n", fname, rfile); tp = new_fent(); tp->f_fn = rfile; tp->f_type = filetype; if (filetype == LOCAL) tp->f_srcprefix = ""; else tp->f_srcprefix = "$S/"; if (imp_rule) tp->f_flags |= NO_IMPLCT_RULE; if (no_ctfconvert) tp->f_flags |= NO_CTFCONVERT; if (no_obj) tp->f_flags |= NO_OBJ | NO_CTFCONVERT; if (before_depend) tp->f_flags |= BEFORE_DEPEND; if (nowerror) tp->f_flags |= NOWERROR; tp->f_compilewith = compilewith; tp->f_depends = depends; tp->f_clean = clean; tp->f_warn = warning; tp->f_objprefix = objprefix; } goto next; } /* * Read in the information about files used in making the system. * Store it in the ftab linked list. */ static void read_files(void) { char fname[MAXPATHLEN]; struct files_name *nl, *tnl; (void) snprintf(fname, sizeof(fname), "../../conf/files"); read_file(fname); (void) snprintf(fname, sizeof(fname), "../../conf/files.%s", machinename); read_file(fname); for (nl = STAILQ_FIRST(&fntab); nl != NULL; nl = tnl) { read_file(nl->f_name); tnl = STAILQ_NEXT(nl, f_next); free(nl->f_name); free(nl); } } static void do_before_depend(FILE *fp) { struct file_list *tp; int lpos, len; fputs("BEFORE_DEPEND=", fp); lpos = 15; STAILQ_FOREACH(tp, &ftab, f_next) if (tp->f_flags & BEFORE_DEPEND) { len = strlen(tp->f_fn) + strlen(tp->f_srcprefix); if (len + lpos > 72) { lpos = 8; fputs("\\\n\t", fp); } if (tp->f_flags & NO_IMPLCT_RULE) lpos += fprintf(fp, "%s ", tp->f_fn); else lpos += fprintf(fp, "%s%s ", tp->f_srcprefix, tp->f_fn); } if (lpos != 8) putc('\n', fp); } static void do_objs(FILE *fp) { struct file_list *tp; int lpos, len; char *cp, och, *sp; fprintf(fp, "OBJS="); lpos = 6; STAILQ_FOREACH(tp, &ftab, f_next) { if (tp->f_flags & NO_OBJ) continue; sp = tail(tp->f_fn); cp = sp + (len = strlen(sp)) - 1; och = *cp; *cp = 'o'; len += strlen(tp->f_objprefix); if (len + lpos > 72) { lpos = 8; fprintf(fp, "\\\n\t"); } fprintf(fp, "%s%s ", tp->f_objprefix, sp); lpos += len + 1; *cp = och; } if (lpos != 8) putc('\n', fp); } static void do_xxfiles(char *tag, FILE *fp) { struct file_list *tp; int lpos, len, slen; char *suff, *SUFF; if (tag[strlen(tag) - 1] == '\n') tag[strlen(tag) - 1] = '\0'; suff = ns(tag + 7); SUFF = ns(suff); raisestr(SUFF); slen = strlen(suff); fprintf(fp, "%sFILES=", SUFF); free(SUFF); lpos = 8; STAILQ_FOREACH(tp, &ftab, f_next) if (tp->f_type != NODEPEND) { len = strlen(tp->f_fn); if (tp->f_fn[len - slen - 1] != '.') continue; if (strcasecmp(&tp->f_fn[len - slen], suff) != 0) continue; if (len + strlen(tp->f_srcprefix) + lpos > 72) { lpos = 8; fputs("\\\n\t", fp); } lpos += fprintf(fp, "%s%s ", tp->f_srcprefix, tp->f_fn); } free(suff); if (lpos != 8) putc('\n', fp); } static char * tail(char *fn) { char *cp; cp = strrchr(fn, '/'); if (cp == NULL) return (fn); return (cp+1); } /* * Create the makerules for each file * which is part of the system. */ static void do_rules(FILE *f) { char *cp, *np, och; struct file_list *ftp; char *compilewith; char cmd[128]; STAILQ_FOREACH(ftp, &ftab, f_next) { if (ftp->f_warn) fprintf(stderr, "WARNING: %s\n", ftp->f_warn); cp = (np = ftp->f_fn) + strlen(ftp->f_fn) - 1; och = *cp; if (ftp->f_flags & NO_IMPLCT_RULE) { if (ftp->f_depends) fprintf(f, "%s%s: %s\n", ftp->f_objprefix, np, ftp->f_depends); else fprintf(f, "%s%s: \n", ftp->f_objprefix, np); } else { *cp = '\0'; if (och == 'o') { fprintf(f, "%s%so:\n\t-cp %s%so .\n\n", ftp->f_objprefix, tail(np), ftp->f_srcprefix, np); continue; } if (ftp->f_depends) { fprintf(f, "%s%so: %s%s%c %s\n", ftp->f_objprefix, tail(np), ftp->f_srcprefix, np, och, ftp->f_depends); } else { fprintf(f, "%s%so: %s%s%c\n", ftp->f_objprefix, tail(np), ftp->f_srcprefix, np, och); } } compilewith = ftp->f_compilewith; if (compilewith == NULL) { const char *ftype = NULL; switch (ftp->f_type) { case NORMAL: ftype = "NORMAL"; break; default: fprintf(stderr, "config: don't know rules for %s\n", np); break; } snprintf(cmd, sizeof(cmd), "${%s_%c%s}", ftype, toupper(och), ftp->f_flags & NOWERROR ? "_NOWERROR" : ""); compilewith = cmd; } *cp = och; if (strlen(ftp->f_objprefix)) fprintf(f, "\t%s %s%s\n", compilewith, ftp->f_srcprefix, np); else fprintf(f, "\t%s\n", compilewith); if (!(ftp->f_flags & NO_CTFCONVERT)) fprintf(f, "\t${NORMAL_CTFCONVERT}\n\n"); else fprintf(f, "\n"); } } static void do_clean(FILE *fp) { struct file_list *tp; int lpos, len; fputs("CLEAN=", fp); lpos = 7; STAILQ_FOREACH(tp, &ftab, f_next) if (tp->f_clean) { len = strlen(tp->f_clean); if (len + lpos > 72) { lpos = 8; fputs("\\\n\t", fp); } fprintf(fp, "%s ", tp->f_clean); lpos += len + 1; } if (lpos != 8) putc('\n', fp); } char * raisestr(char *str) { char *cp = str; while (*str) { if (islower(*str)) *str = toupper(*str); str++; } return (cp); } diff --git a/usr.sbin/config/mkoptions.cc b/usr.sbin/config/mkoptions.cc index 1ffb9722af8b..53c8fa796a26 100644 --- a/usr.sbin/config/mkoptions.cc +++ b/usr.sbin/config/mkoptions.cc @@ -1,451 +1,449 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995 Peter Wemm * Copyright (c) 1980, 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. * 3. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)mkheaders.c 8.1 (Berkeley) 6/6/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Make all the .h files for the optional entries */ #include #include #include #include #include #include "config.h" #include "y.tab.h" static struct users { int u_default; int u_min; int u_max; } users = { 8, 2, 512 }; static char *lower(char *); static void read_options(void); static void do_option(char *); static char *tooption(char *); void options(void) { char buf[40]; struct cputype *cp; struct opt_list *ol; struct opt *op; /* Fake the cpu types as options. */ SLIST_FOREACH(cp, &cputype, cpu_next) { op = (struct opt *)calloc(1, sizeof(*op)); if (op == NULL) err(EXIT_FAILURE, "calloc"); op->op_name = ns(cp->cpu_name); SLIST_INSERT_HEAD(&opt, op, op_next); } if (maxusers == 0) { /* fprintf(stderr, "maxusers not specified; will auto-size\n"); */ } else if (maxusers < users.u_min) { fprintf(stderr, "minimum of %d maxusers assumed\n", users.u_min); maxusers = users.u_min; } else if (maxusers > users.u_max) fprintf(stderr, "warning: maxusers > %d (%d)\n", users.u_max, maxusers); /* Fake MAXUSERS as an option. */ op = (struct opt *)calloc(1, sizeof(*op)); if (op == NULL) err(EXIT_FAILURE, "calloc"); op->op_name = ns("MAXUSERS"); snprintf(buf, sizeof(buf), "%d", maxusers); op->op_value = ns(buf); SLIST_INSERT_HEAD(&opt, op, op_next); read_options(); /* Fake the value of MACHINE_ARCH as an option if necessary */ SLIST_FOREACH(ol, &otab, o_next) { if (strcasecmp(ol->o_name, machinearch) != 0) continue; op = (struct opt *)calloc(1, sizeof(*op)); if (op == NULL) err(EXIT_FAILURE, "calloc"); op->op_name = ns(ol->o_name); SLIST_INSERT_HEAD(&opt, op, op_next); break; } SLIST_FOREACH(op, &opt, op_next) { SLIST_FOREACH(ol, &otab, o_next) { if (eq(op->op_name, ol->o_name) && (ol->o_flags & OL_ALIAS)) { fprintf(stderr, "Mapping option %s to %s.\n", op->op_name, ol->o_file); op->op_name = ol->o_file; break; } } } SLIST_FOREACH(ol, &otab, o_next) do_option(ol->o_name); SLIST_FOREACH(op, &opt, op_next) { if (!op->op_ownfile && strncmp(op->op_name, "DEV_", 4)) { fprintf(stderr, "%s: unknown option \"%s\"\n", PREFIX, op->op_name); exit(1); } } } /* * Generate an .h file */ static void do_option(char *name) { char *file; const char *basefile; struct opt_list *ol; struct opt *op; struct opt_head op_head; FILE *inf, *outf; char *value; char *oldvalue; int seen; int tidy; file = tooption(name); /* * Check to see if the option was specified.. */ value = NULL; SLIST_FOREACH(op, &opt, op_next) { if (eq(name, op->op_name)) { oldvalue = value; value = op->op_value; if (value == NULL) value = ns("1"); if (oldvalue != NULL && !eq(value, oldvalue)) fprintf(stderr, "%s: option \"%s\" redefined from %s to %s\n", PREFIX, op->op_name, oldvalue, value); op->op_ownfile++; } } remember(file); inf = fopen(file, "r"); if (inf == NULL) { outf = fopen(file, "w"); if (outf == NULL) err(1, "%s", file); /* was the option in the config file? */ if (value) { fprintf(outf, "#define %s %s\n", name, value); } /* else empty file */ (void)fclose(outf); return; } basefile = ""; SLIST_FOREACH(ol, &otab, o_next) if (eq(name, ol->o_name)) { basefile = ol->o_file; break; } oldvalue = NULL; SLIST_INIT(&op_head); seen = 0; tidy = 0; for (;;) { configword cp, inw; char *invalue; /* get the #define */ if ((inw = get_word(inf)).eol() || inw.eof()) break; /* get the option name */ if ((inw = get_word(inf)).eol() || inw.eof()) break; /* get the option value */ if ((cp = get_word(inf)).eol() || cp.eof()) break; /* option value */ invalue = ns(cp); /* malloced */ if (eq(inw, name)) { oldvalue = invalue; invalue = value; seen++; } SLIST_FOREACH(ol, &otab, o_next) if (eq(inw, ol->o_name)) break; if (!eq(inw, name) && !ol) { fprintf(stderr, "WARNING: unknown option `%s' removed from %s\n", inw->c_str(), file); tidy++; } else if (ol != NULL && !eq(basefile, ol->o_file)) { fprintf(stderr, "WARNING: option `%s' moved from %s to %s\n", inw->c_str(), basefile, ol->o_file); tidy++; } else { op = (struct opt *) calloc(1, sizeof *op); if (op == NULL) err(EXIT_FAILURE, "calloc"); op->op_name = ns(inw); op->op_value = invalue; SLIST_INSERT_HEAD(&op_head, op, op_next); } /* EOL? */ cp = get_word(inf); if (cp.eof()) break; } (void)fclose(inf); if (!tidy && ((value == NULL && oldvalue == NULL) || (value && oldvalue && eq(value, oldvalue)))) { while (!SLIST_EMPTY(&op_head)) { op = SLIST_FIRST(&op_head); SLIST_REMOVE_HEAD(&op_head, op_next); free(op->op_name); free(op->op_value); free(op); } return; } if (value && !seen) { /* New option appears */ op = (struct opt *) calloc(1, sizeof *op); if (op == NULL) err(EXIT_FAILURE, "calloc"); op->op_name = ns(name); op->op_value = ns(value); SLIST_INSERT_HEAD(&op_head, op, op_next); } outf = fopen(file, "w"); if (outf == NULL) err(1, "%s", file); while (!SLIST_EMPTY(&op_head)) { op = SLIST_FIRST(&op_head); /* was the option in the config file? */ if (op->op_value) { fprintf(outf, "#define %s %s\n", op->op_name, op->op_value); } SLIST_REMOVE_HEAD(&op_head, op_next); free(op->op_name); free(op->op_value); free(op); } (void)fclose(outf); } /* * Find the filename to store the option spec into. */ static char * tooption(char *name) { static char hbuf[MAXPATHLEN]; char nbuf[MAXPATHLEN]; struct opt_list *po; char *fpath; /* "cannot happen"? the otab list should be complete.. */ (void)strlcpy(nbuf, "options.h", sizeof(nbuf)); SLIST_FOREACH(po, &otab, o_next) { if (eq(po->o_name, name)) { strlcpy(nbuf, po->o_file, sizeof(nbuf)); break; } } fpath = path(nbuf); (void)strlcpy(hbuf, fpath, sizeof(hbuf)); free(fpath); return (hbuf); } static void check_duplicate(const char *fname, const char *chkopt) { struct opt_list *po; SLIST_FOREACH(po, &otab, o_next) { if (eq(po->o_name, chkopt)) { fprintf(stderr, "%s: Duplicate option %s.\n", fname, chkopt); exit(1); } } } static void insert_option(const char *fname, char *optname, char *val) { struct opt_list *po; check_duplicate(fname, optname); po = (struct opt_list *) calloc(1, sizeof *po); if (po == NULL) err(EXIT_FAILURE, "calloc"); po->o_name = optname; po->o_file = val; po->o_flags = 0; SLIST_INSERT_HEAD(&otab, po, o_next); } static void update_option(const char *optname, char *val, int flags) { struct opt_list *po; SLIST_FOREACH(po, &otab, o_next) { if (eq(po->o_name, optname)) { free(po->o_file); po->o_file = val; po->o_flags = flags; return; } } /* * Option not found, but that's OK, we just ignore it since it * may be for another arch. */ return; } static int read_option_file(const char *fname, int flags) { FILE *fp; configword wd; char *optname, *val; char genopt[MAXPATHLEN]; fp = fopen(fname, "r"); if (fp == NULL) return (0); while (!(wd = get_word(fp)).eof()) { if (wd.eol()) continue; if (wd[0] == '#') { while (!(wd = get_word(fp)).eof() && !wd.eol()) continue; continue; } optname = ns(wd); wd = get_word(fp); if (wd.eof()) { free(optname); break; } if (wd.eol()) { if (flags) { fprintf(stderr, "%s: compat file requires two" " words per line at %s\n", fname, optname); exit(1); } char *s = ns(optname); (void)snprintf(genopt, sizeof(genopt), "opt_%s.h", lower(s)); val = ns(genopt); free(s); } else { val = ns(wd); } if (flags == 0) { /* * insert_option takes possession of `optname` in the * new option instead of making yet another copy. */ insert_option(fname, optname, val); } else { update_option(optname, val, flags); free(optname); optname = NULL; } } (void)fclose(fp); return (1); } /* * read the options and options. files */ static void read_options(void) { char fname[MAXPATHLEN]; SLIST_INIT(&otab); read_option_file("../../conf/options", 0); (void)snprintf(fname, sizeof fname, "../../conf/options.%s", machinename); if (!read_option_file(fname, 0)) { (void)snprintf(fname, sizeof fname, "options.%s", machinename); read_option_file(fname, 0); } read_option_file("../../conf/options-compat", OL_ALIAS); } static char * lower(char *str) { char *cp = str; while (*str) { if (isupper(*str)) *str = tolower(*str); str++; } return (cp); } diff --git a/usr.sbin/flowctl/flowctl.c b/usr.sbin/flowctl/flowctl.c index 9385ea566c70..e7684a68a35b 100644 --- a/usr.sbin/flowctl/flowctl.c +++ b/usr.sbin/flowctl/flowctl.c @@ -1,428 +1,423 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2004-2005 Gleb Smirnoff * Copyright (c) 2001-2003 Roman V. Palagin * 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. * * $SourceForge: flowctl.c,v 1.15 2004/08/31 20:24:58 glebius Exp $ */ -#ifndef lint -static const char rcs_id[] = - "@(#) $FreeBSD$"; -#endif - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CISCO_SH_FLOW_HEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Pr SrcP DstP Pkts\n" #define CISCO_SH_FLOW "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n" /* human-readable IPv4 header */ #define CISCO_SH_FLOW_HHEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Proto SrcPort DstPort Pkts\n" #define CISCO_SH_FLOW_H "%-13s %-15s %-13s %-15s %5u %8d %8d %8lu\n" #define CISCO_SH_FLOW6_HEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Pr SrcP DstP Pkts\n" #define CISCO_SH_FLOW6 "%-13s %-30s %-13s %-30s %2u %4.4x %4.4x %6lu\n" /* Human-readable IPv6 headers */ #define CISCO_SH_FLOW6_HHEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Proto SrcPort DstPort Pkts\n" #define CISCO_SH_FLOW6_H "%-13s %-36s %-13s %-36s %5u %8d %8d %8lu\n" #define CISCO_SH_VERB_FLOW_HEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ "Port Msk AS Port Msk AS NextHop B/Pk Active\n" #define CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \ "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-15s %9u %8u\n\n" #define CISCO_SH_VERB_FLOW6_HEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ "Port Msk AS Port Msk AS NextHop B/Pk Active\n" #define CISCO_SH_VERB_FLOW6 "%-14s %-30s %-14s %-30s %2u %3x %4x %6lu\n" \ "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-30s %9u %8u\n\n" #ifdef INET static void flow_cache_print(struct ngnf_show_header *resp); static void flow_cache_print_verbose(struct ngnf_show_header *resp); #endif #ifdef INET6 static void flow_cache_print6(struct ngnf_show_header *resp); static void flow_cache_print6_verbose(struct ngnf_show_header *resp); #endif static void ctl_show(int, char **); #if defined(INET) || defined(INET6) static void do_show(int, void (*func)(struct ngnf_show_header *)); #endif static void help(void); static void execute_command(int, char **); struct ip_ctl_cmd { char *cmd_name; void (*cmd_func)(int argc, char **argv); }; struct ip_ctl_cmd cmds[] = { {"show", ctl_show}, {NULL, NULL}, }; int cs, human = 0; char *ng_path; int main(int argc, char **argv) { int c; char sname[NG_NODESIZ]; int rcvbuf = SORCVBUF_SIZE; /* parse options */ while ((c = getopt(argc, argv, "d:")) != -1) { switch (c) { case 'd': /* set libnetgraph debug level. */ NgSetDebug(atoi(optarg)); break; } } argc -= optind; argv += optind; ng_path = argv[0]; if (ng_path == NULL || (strlen(ng_path) > NG_PATHSIZ)) help(); argc--; argv++; /* create control socket. */ snprintf(sname, sizeof(sname), "flowctl%i", getpid()); if (NgMkSockNode(sname, &cs, NULL) == -1) err(1, "NgMkSockNode"); /* set receive buffer size */ if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1) err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)"); /* parse and execute command */ execute_command(argc, argv); close(cs); exit(0); } static void execute_command(int argc, char **argv) { int cindex = -1; int i; if (!argc) help(); for (i = 0; cmds[i].cmd_name != NULL; i++) if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) { if (cindex != -1) errx(1, "ambiguous command: %s", argv[0]); cindex = i; } if (cindex == -1) errx(1, "bad command: %s", argv[0]); argc--; argv++; (*cmds[cindex].cmd_func)(argc, argv); } static void ctl_show(int argc, char **argv) { int ipv4, ipv6, verbose = 0; ipv4 = feature_present("inet"); ipv6 = feature_present("inet6"); if (argc > 0 && !strncmp(argv[0], "ipv4", 4)) { ipv6 = 0; argc--; argv++; } if (argc > 0 && !strncmp(argv[0], "ipv6", 4)) { ipv4 = 0; argc--; argv++; } if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0]))) verbose = 1; if (argc > 0 && !strncmp(argv[0], "human", strlen(argv[0]))) human = 1; #ifdef INET if (ipv4) { if (verbose) do_show(4, &flow_cache_print_verbose); else do_show(4, &flow_cache_print); } #endif #ifdef INET6 if (ipv6) { if (verbose) do_show(6, &flow_cache_print6_verbose); else do_show(6, &flow_cache_print6); } #endif } #if defined(INET) || defined(INET6) static void do_show(int version, void (*func)(struct ngnf_show_header *)) { char buf[SORCVBUF_SIZE]; struct ng_mesg *ng_mesg; struct ngnf_show_header req, *resp; int token, nread; ng_mesg = (struct ng_mesg *)buf; req.version = version; req.hash_id = req.list_id = 0; for (;;) { /* request set of accounting records */ token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE, NGM_NETFLOW_SHOW, (void *)&req, sizeof(req)); if (token == -1) err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); /* read reply */ nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL); if (nread == -1) err(1, "NgRecvMsg() failed"); if (ng_mesg->header.token != token) err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); resp = (struct ngnf_show_header *)ng_mesg->data; if ((ng_mesg->header.arglen < (sizeof(*resp))) || (ng_mesg->header.arglen < (sizeof(*resp) + (resp->nentries * sizeof(struct flow_entry_data))))) err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); (*func)(resp); if (resp->hash_id != 0) req.hash_id = resp->hash_id; else break; req.list_id = resp->list_id; } } #endif #ifdef INET static void flow_cache_print(struct ngnf_show_header *resp) { struct flow_entry_data *fle; char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; int i; if (resp->version != 4) errx(EX_SOFTWARE, "%s: version mismatch: %u", __func__, resp->version); if (resp->nentries > 0) printf(human ? CISCO_SH_FLOW_HHEADER : CISCO_SH_FLOW_HEADER); fle = (struct flow_entry_data *)(resp + 1); for (i = 0; i < resp->nentries; i++, fle++) { inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); printf(human ? CISCO_SH_FLOW_H : CISCO_SH_FLOW, if_indextoname(fle->fle_i_ifx, src_if), src, if_indextoname(fle->fle_o_ifx, dst_if), dst, fle->r.r_ip_p, ntohs(fle->r.r_sport), ntohs(fle->r.r_dport), fle->packets); } } #endif #ifdef INET6 static void flow_cache_print6(struct ngnf_show_header *resp) { struct flow6_entry_data *fle6; char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN]; char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; int i; if (resp->version != 6) errx(EX_SOFTWARE, "%s: version mismatch: %u", __func__, resp->version); if (resp->nentries > 0) printf(human ? CISCO_SH_FLOW6_HHEADER : CISCO_SH_FLOW6_HEADER); fle6 = (struct flow6_entry_data *)(resp + 1); for (i = 0; i < resp->nentries; i++, fle6++) { inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); printf(human ? CISCO_SH_FLOW6_H : CISCO_SH_FLOW6, if_indextoname(fle6->fle_i_ifx, src_if), src6, if_indextoname(fle6->fle_o_ifx, dst_if), dst6, fle6->r.r_ip_p, ntohs(fle6->r.r_sport), ntohs(fle6->r.r_dport), fle6->packets); } } #endif #ifdef INET static void flow_cache_print_verbose(struct ngnf_show_header *resp) { struct flow_entry_data *fle; char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN]; char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; int i; if (resp->version != 4) errx(EX_SOFTWARE, "%s: version mismatch: %u", __func__, resp->version); printf(CISCO_SH_VERB_FLOW_HEADER); fle = (struct flow_entry_data *)(resp + 1); for (i = 0; i < resp->nentries; i++, fle++) { inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next)); printf(CISCO_SH_VERB_FLOW, if_indextoname(fle->fle_i_ifx, src_if), src, if_indextoname(fle->fle_o_ifx, dst_if), dst, fle->r.r_ip_p, fle->r.r_tos, fle->tcp_flags, fle->packets, ntohs(fle->r.r_sport), fle->src_mask, 0, ntohs(fle->r.r_dport), fle->dst_mask, 0, next, (u_int)(fle->bytes / fle->packets), 0); } } #endif #ifdef INET6 static void flow_cache_print6_verbose(struct ngnf_show_header *resp) { struct flow6_entry_data *fle6; char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN], next6[INET6_ADDRSTRLEN]; char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; int i; if (resp->version != 6) errx(EX_SOFTWARE, "%s: version mismatch: %u", __func__, resp->version); printf(CISCO_SH_VERB_FLOW6_HEADER); fle6 = (struct flow6_entry_data *)(resp + 1); for (i = 0; i < resp->nentries; i++, fle6++) { inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); inet_ntop(AF_INET6, &fle6->n.next_hop6, next6, sizeof(next6)); printf(CISCO_SH_VERB_FLOW6, if_indextoname(fle6->fle_i_ifx, src_if), src6, if_indextoname(fle6->fle_o_ifx, dst_if), dst6, fle6->r.r_ip_p, fle6->r.r_tos, fle6->tcp_flags, fle6->packets, ntohs(fle6->r.r_sport), fle6->src_mask, 0, ntohs(fle6->r.r_dport), fle6->dst_mask, 0, next6, (u_int)(fle6->bytes / fle6->packets), 0); } } #endif static void help(void) { extern char *__progname; fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname); exit (0); } diff --git a/usr.sbin/keyserv/crypt_server.c b/usr.sbin/keyserv/crypt_server.c index 25f48e978781..04290326cf3b 100644 --- a/usr.sbin/keyserv/crypt_server.c +++ b/usr.sbin/keyserv/crypt_server.c @@ -1,272 +1,267 @@ /* * Copyright (c) 1996 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. */ #include #include #include #include #include #include #include #include #include #include #include "crypt.h" -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - /* * The U.S. government stupidly believes that a) it can keep strong * crypto code a secret and b) that doing so somehow protects national * interests. It's wrong on both counts, but until it listens to reason * we have to make certain compromises so it doesn't have an excuse to * throw us in federal prison. * * Consequently, the core OS ships without DES support, and keyserv * defaults to using ARCFOUR with only a 40 bit key, just like nutscrape. * This breaks compatibility with Secure RPC on other systems, but it * allows Secure RPC to work between FreeBSD systems that don't have the * DES package installed without throwing security totally out the window. * * In order to avoid having to supply two versions of keyserv (one with * DES and one without), we use dlopen() and friends to load libdes.so * into our address space at runtime. We check for the presence of * /usr/lib/libdes.so.3.0 at startup and load it if we find it. If we * can't find it, or the __des_crypt symbol doesn't exist, we fall back * to the ARCFOUR encryption code. The user can specify another path using * the -p flag. */ /* arcfour.h */ typedef struct arcfour_key { unsigned char state[256]; unsigned char x; unsigned char y; } arcfour_key; static void prepare_key(unsigned char *key_data_ptr,int key_data_len, arcfour_key *key); static void arcfour(unsigned char *buffer_ptr,int buffer_len,arcfour_key * key); static void swap_byte(unsigned char *a, unsigned char *b); static void prepare_key(unsigned char *key_data_ptr, int key_data_len, arcfour_key *key) { unsigned char index1; unsigned char index2; unsigned char* state; short counter; state = &key->state[0]; for(counter = 0; counter < 256; counter++) state[counter] = counter; key->x = 0; key->y = 0; index1 = 0; index2 = 0; for(counter = 0; counter < 256; counter++) { index2 = (key_data_ptr[index1] + state[counter] + index2) % 256; swap_byte(&state[counter], &state[index2]); index1 = (index1 + 1) % key_data_len; } } static void arcfour(unsigned char *buffer_ptr, int buffer_len, arcfour_key *key) { unsigned char x; unsigned char y; unsigned char* state; unsigned char xorIndex; short counter; x = key->x; y = key->y; state = &key->state[0]; for(counter = 0; counter < buffer_len; counter ++) { x = (x + 1) % 256; y = (state[x] + y) % 256; swap_byte(&state[x], &state[y]); xorIndex = (state[x] + state[y]) % 256; buffer_ptr[counter] ^= state[xorIndex]; } key->x = x; key->y = y; } static void swap_byte(unsigned char *a, unsigned char *b) { unsigned char swapByte; swapByte = *a; *a = *b; *b = swapByte; } /* Dummy _des_crypt function that uses ARCFOUR with a 40 bit key */ int _arcfour_crypt(char *buf, int len, struct desparams *desp) { struct arcfour_key arcfourk; /* * U.S. government anti-crypto weasels take * note: although we are supplied with a 64 bit * key, we're only passing 40 bits to the ARCFOUR * encryption code. So there. */ prepare_key(desp->des_key, 5, &arcfourk); arcfour(buf, len, &arcfourk); return(DESERR_NOHWDEVICE); } int (*_my_crypt)(char *, int, struct desparams *) = NULL; static void *dlhandle; #ifndef _PATH_USRLIB #define _PATH_USRLIB "/usr/lib" #endif #ifndef LIBCRYPTO #define LIBCRYPTO "libcrypto.so.2" #endif void load_des(int warn, char *libpath) { char dlpath[MAXPATHLEN]; if (libpath == NULL) snprintf(dlpath, sizeof(dlpath), "%s/%s", _PATH_USRLIB, LIBCRYPTO); else snprintf(dlpath, sizeof(dlpath), "%s", libpath); if ((dlhandle = dlopen(dlpath, 0444)) != NULL) _my_crypt = (int (*)())dlsym(dlhandle, "_des_crypt"); if (_my_crypt == NULL) { if (dlhandle != NULL) dlclose(dlhandle); _my_crypt = &_arcfour_crypt; if (warn) { printf ("DES support disabled -- using ARCFOUR instead.\n"); printf ("Warning: ARCFOUR cipher is not compatible with "); printf ("other Secure RPC implementations.\nInstall "); printf ("the FreeBSD 'des' distribution to enable"); printf (" DES encryption.\n"); } } else { if (warn) { printf ("DES support enabled\n"); printf ("Using %s shared object.\n", dlpath); } } return; } desresp * des_crypt_1_svc(desargs *argp, struct svc_req *rqstp) { static desresp result; struct desparams dparm; if (argp->desbuf.desbuf_len > DES_MAXDATA) { result.stat = DESERR_BADPARAM; return(&result); } bcopy(argp->des_key, dparm.des_key, 8); bcopy(argp->des_ivec, dparm.des_ivec, 8); dparm.des_mode = (argp->des_mode == CBC_DES) ? CBC : ECB; dparm.des_dir = (argp->des_dir == ENCRYPT_DES) ? ENCRYPT : DECRYPT; #ifdef BROKEN_DES dparm.UDES.UDES_buf = argp->desbuf.desbuf_val; #endif /* * XXX This compensates for a bug in the libdes Secure RPC * compat interface. (Actually, there are a couple.) The * des_ecb_encrypt() routine in libdes only encrypts 8 bytes * (64 bits) at a time. However, the Sun Secure RPC ecb_crypt() * routine is supposed to be able to handle buffers up to 8Kbytes. * The rpc_enc module in libdes ignores this fact and just drops * the length parameter on the floor, encrypting only the * first 64 bits of whatever buffer you feed it. We deal with * this here: if we're using DES encryption, and we're using * ECB mode, then we make a pass over the entire buffer * ourselves. Note: the rpc_enc module incorrectly transposes * the mode flags, so when you ask for CBC mode, you're really * getting ECB mode. */ #ifdef BROKEN_DES if (_my_crypt != &_arcfour_crypt && argp->des_mode == CBC) { #else if (_my_crypt != &_arcfour_crypt && argp->des_mode == ECB) { #endif int i; char *dptr; for (i = 0; i < argp->desbuf.desbuf_len / 8; i++) { dptr = argp->desbuf.desbuf_val; dptr += (i * 8); #ifdef BROKEN_DES dparm.UDES.UDES_buf = dptr; #endif result.stat = _my_crypt(dptr, 8, &dparm); } } else { result.stat = _my_crypt(argp->desbuf.desbuf_val, argp->desbuf.desbuf_len, &dparm); } if (result.stat == DESERR_NONE || result.stat == DESERR_NOHWDEVICE) { bcopy(dparm.des_ivec, result.des_ivec, 8); result.desbuf.desbuf_len = argp->desbuf.desbuf_len; result.desbuf.desbuf_val = argp->desbuf.desbuf_val; } return (&result); } diff --git a/usr.sbin/keyserv/keyserv.c b/usr.sbin/keyserv/keyserv.c index 7935a72941ae..bc219e886020 100644 --- a/usr.sbin/keyserv/keyserv.c +++ b/usr.sbin/keyserv/keyserv.c @@ -1,761 +1,759 @@ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #ifndef lint #if 0 static char sccsid[] = "@(#)keyserv.c 1.15 94/04/25 SMI"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. */ /* * Keyserver * Store secret keys per uid. Do public key encryption and decryption * operations. Generate "random" keys. * Do not talk to anything but a local root * process on the local transport only */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "keyserv.h" #ifndef NGROUPS #define NGROUPS 16 #endif #ifndef KEYSERVSOCK #define KEYSERVSOCK "/var/run/keyservsock" #endif static void randomize( des_block * ); static void usage( void ); static int getrootkey( des_block *, int ); static int root_auth( SVCXPRT *, struct svc_req * ); #ifdef DEBUG static int debugging = 1; #else static int debugging = 0; #endif static void keyprogram(struct svc_req *rqstp, SVCXPRT *transp); static des_block masterkey; static char ROOTKEY[] = "/etc/.rootkey"; /* * Hack to allow the keyserver to use AUTH_DES (for authenticated * NIS+ calls, for example). The only functions that get called * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes. * * The approach is to have the keyserver fill in pointers to local * implementations of these functions, and to call those in key_call(). */ extern cryptkeyres *(*__key_encryptsession_pk_LOCAL)(); extern cryptkeyres *(*__key_decryptsession_pk_LOCAL)(); extern des_block *(*__key_gendes_LOCAL)(); extern int (*__des_crypt_LOCAL)(); cryptkeyres *key_encrypt_pk_2_svc_prog( uid_t, cryptkeyarg2 * ); cryptkeyres *key_decrypt_pk_2_svc_prog( uid_t, cryptkeyarg2 * ); des_block *key_gen_1_svc_prog( void *, struct svc_req * ); int main(int argc, char *argv[]) { int nflag = 0; int c; int warn = 0; char *path = NULL; void *localhandle; register SVCXPRT *transp; struct netconfig *nconf = NULL; __key_encryptsession_pk_LOCAL = &key_encrypt_pk_2_svc_prog; __key_decryptsession_pk_LOCAL = &key_decrypt_pk_2_svc_prog; __key_gendes_LOCAL = &key_gen_1_svc_prog; while ((c = getopt(argc, argv, "ndDvp:")) != -1) switch (c) { case 'n': nflag++; break; case 'd': pk_nodefaultkeys(); break; case 'D': debugging = 1; break; case 'v': warn = 1; break; case 'p': path = optarg; break; default: usage(); } load_des(warn, path); __des_crypt_LOCAL = _my_crypt; if (svc_auth_reg(AUTH_DES, _svcauth_des) == -1) errx(1, "failed to register AUTH_DES authenticator"); if (optind != argc) { usage(); } /* * Initialize */ (void) umask(S_IXUSR|S_IXGRP|S_IXOTH); if (geteuid() != 0) errx(1, "keyserv must be run as root"); setmodulus(HEXMODULUS); getrootkey(&masterkey, nflag); rpcb_unset(KEY_PROG, KEY_VERS, NULL); rpcb_unset(KEY_PROG, KEY_VERS2, NULL); if (svc_create(keyprogram, KEY_PROG, KEY_VERS, "netpath") == 0) { (void) fprintf(stderr, "%s: unable to create service\n", argv[0]); exit(1); } if (svc_create(keyprogram, KEY_PROG, KEY_VERS2, "netpath") == 0) { (void) fprintf(stderr, "%s: unable to create service\n", argv[0]); exit(1); } localhandle = setnetconfig(); while ((nconf = getnetconfig(localhandle)) != NULL) { if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) break; } if (nconf == NULL) errx(1, "getnetconfig: %s", nc_sperror()); unlink(KEYSERVSOCK); rpcb_unset(CRYPT_PROG, CRYPT_VERS, nconf); transp = svcunix_create(RPC_ANYSOCK, 0, 0, KEYSERVSOCK); if (transp == NULL) errx(1, "cannot create AF_LOCAL service"); if (!svc_reg(transp, KEY_PROG, KEY_VERS, keyprogram, nconf)) errx(1, "unable to register (KEY_PROG, KEY_VERS, unix)"); if (!svc_reg(transp, KEY_PROG, KEY_VERS2, keyprogram, nconf)) errx(1, "unable to register (KEY_PROG, KEY_VERS2, unix)"); if (!svc_reg(transp, CRYPT_PROG, CRYPT_VERS, crypt_prog_1, nconf)) errx(1, "unable to register (CRYPT_PROG, CRYPT_VERS, unix)"); endnetconfig(localhandle); (void) umask(066); /* paranoia */ if (!debugging) { daemon(0,0); } signal(SIGPIPE, SIG_IGN); svc_run(); abort(); /* NOTREACHED */ } /* * In the event that we don't get a root password, we try to * randomize the master key the best we can */ static void randomize(des_block *master) { master->key.low = arc4random(); master->key.high = arc4random(); } /* * Try to get root's secret key, by prompting if terminal is a tty, else trying * from standard input. * Returns 1 on success. */ static int getrootkey(des_block *master, int prompt) { char *passwd; char name[MAXNETNAMELEN + 1]; char secret[HEXKEYBYTES]; key_netstarg netstore; int fd; if (!prompt) { /* * Read secret key out of ROOTKEY */ fd = open(ROOTKEY, O_RDONLY, 0); if (fd < 0) { randomize(master); return (0); } if (read(fd, secret, HEXKEYBYTES) < HEXKEYBYTES) { warnx("the key read from %s was too short", ROOTKEY); (void) close(fd); return (0); } (void) close(fd); if (!getnetname(name)) { warnx( "failed to generate host's netname when establishing root's key"); return (0); } memcpy(netstore.st_priv_key, secret, HEXKEYBYTES); memset(netstore.st_pub_key, 0, HEXKEYBYTES); netstore.st_netname = name; if (pk_netput(0, &netstore) != KEY_SUCCESS) { warnx("could not set root's key and netname"); return (0); } return (1); } /* * Decrypt yellow pages publickey entry to get secret key */ passwd = getpass("root password:"); passwd2des(passwd, (char *)master); getnetname(name); if (!getsecretkey(name, secret, passwd)) { warnx("can't find %s's secret key", name); return (0); } if (secret[0] == 0) { warnx("password does not decrypt secret key for %s", name); return (0); } (void) pk_setkey(0, secret); /* * Store it for future use in $ROOTKEY, if possible */ fd = open(ROOTKEY, O_WRONLY|O_TRUNC|O_CREAT, 0); if (fd > 0) { char newline = '\n'; write(fd, secret, strlen(secret)); write(fd, &newline, sizeof (newline)); close(fd); } return (1); } /* * Procedures to implement RPC service */ char * strstatus(keystatus status) { switch (status) { case KEY_SUCCESS: return ("KEY_SUCCESS"); case KEY_NOSECRET: return ("KEY_NOSECRET"); case KEY_UNKNOWN: return ("KEY_UNKNOWN"); case KEY_SYSTEMERR: return ("KEY_SYSTEMERR"); default: return ("(bad result code)"); } } keystatus * key_set_1_svc_prog(uid_t uid, keybuf key) { static keystatus status; if (debugging) { (void) fprintf(stderr, "set(%u, %.*s) = ", uid, (int) sizeof (keybuf), key); } status = pk_setkey(uid, key); if (debugging) { (void) fprintf(stderr, "%s\n", strstatus(status)); (void) fflush(stderr); } return (&status); } cryptkeyres * key_encrypt_pk_2_svc_prog(uid_t uid, cryptkeyarg2 *arg) { static cryptkeyres res; if (debugging) { (void) fprintf(stderr, "encrypt(%u, %s, %08x%08x) = ", uid, arg->remotename, arg->deskey.key.high, arg->deskey.key.low); } res.cryptkeyres_u.deskey = arg->deskey; res.status = pk_encrypt(uid, arg->remotename, &(arg->remotekey), &res.cryptkeyres_u.deskey); if (debugging) { if (res.status == KEY_SUCCESS) { (void) fprintf(stderr, "%08x%08x\n", res.cryptkeyres_u.deskey.key.high, res.cryptkeyres_u.deskey.key.low); } else { (void) fprintf(stderr, "%s\n", strstatus(res.status)); } (void) fflush(stderr); } return (&res); } cryptkeyres * key_decrypt_pk_2_svc_prog(uid_t uid, cryptkeyarg2 *arg) { static cryptkeyres res; if (debugging) { (void) fprintf(stderr, "decrypt(%u, %s, %08x%08x) = ", uid, arg->remotename, arg->deskey.key.high, arg->deskey.key.low); } res.cryptkeyres_u.deskey = arg->deskey; res.status = pk_decrypt(uid, arg->remotename, &(arg->remotekey), &res.cryptkeyres_u.deskey); if (debugging) { if (res.status == KEY_SUCCESS) { (void) fprintf(stderr, "%08x%08x\n", res.cryptkeyres_u.deskey.key.high, res.cryptkeyres_u.deskey.key.low); } else { (void) fprintf(stderr, "%s\n", strstatus(res.status)); } (void) fflush(stderr); } return (&res); } keystatus * key_net_put_2_svc_prog(uid_t uid, key_netstarg *arg) { static keystatus status; if (debugging) { (void) fprintf(stderr, "net_put(%s, %.*s, %.*s) = ", arg->st_netname, (int)sizeof (arg->st_pub_key), arg->st_pub_key, (int)sizeof (arg->st_priv_key), arg->st_priv_key); } status = pk_netput(uid, arg); if (debugging) { (void) fprintf(stderr, "%s\n", strstatus(status)); (void) fflush(stderr); } return (&status); } key_netstres * key_net_get_2_svc_prog(uid_t uid, void *arg) { static key_netstres keynetname; if (debugging) (void) fprintf(stderr, "net_get(%u) = ", uid); keynetname.status = pk_netget(uid, &keynetname.key_netstres_u.knet); if (debugging) { if (keynetname.status == KEY_SUCCESS) { fprintf(stderr, "<%s, %.*s, %.*s>\n", keynetname.key_netstres_u.knet.st_netname, (int)sizeof (keynetname.key_netstres_u.knet.st_pub_key), keynetname.key_netstres_u.knet.st_pub_key, (int)sizeof (keynetname.key_netstres_u.knet.st_priv_key), keynetname.key_netstres_u.knet.st_priv_key); } else { (void) fprintf(stderr, "NOT FOUND\n"); } (void) fflush(stderr); } return (&keynetname); } cryptkeyres * key_get_conv_2_svc_prog(uid_t uid, keybuf arg) { static cryptkeyres res; if (debugging) (void) fprintf(stderr, "get_conv(%u, %.*s) = ", uid, (int)sizeof (keybuf), arg); res.status = pk_get_conv_key(uid, arg, &res); if (debugging) { if (res.status == KEY_SUCCESS) { (void) fprintf(stderr, "%08x%08x\n", res.cryptkeyres_u.deskey.key.high, res.cryptkeyres_u.deskey.key.low); } else { (void) fprintf(stderr, "%s\n", strstatus(res.status)); } (void) fflush(stderr); } return (&res); } cryptkeyres * key_encrypt_1_svc_prog(uid_t uid, cryptkeyarg *arg) { static cryptkeyres res; if (debugging) { (void) fprintf(stderr, "encrypt(%u, %s, %08x%08x) = ", uid, arg->remotename, arg->deskey.key.high, arg->deskey.key.low); } res.cryptkeyres_u.deskey = arg->deskey; res.status = pk_encrypt(uid, arg->remotename, NULL, &res.cryptkeyres_u.deskey); if (debugging) { if (res.status == KEY_SUCCESS) { (void) fprintf(stderr, "%08x%08x\n", res.cryptkeyres_u.deskey.key.high, res.cryptkeyres_u.deskey.key.low); } else { (void) fprintf(stderr, "%s\n", strstatus(res.status)); } (void) fflush(stderr); } return (&res); } cryptkeyres * key_decrypt_1_svc_prog(uid_t uid, cryptkeyarg *arg) { static cryptkeyres res; if (debugging) { (void) fprintf(stderr, "decrypt(%u, %s, %08x%08x) = ", uid, arg->remotename, arg->deskey.key.high, arg->deskey.key.low); } res.cryptkeyres_u.deskey = arg->deskey; res.status = pk_decrypt(uid, arg->remotename, NULL, &res.cryptkeyres_u.deskey); if (debugging) { if (res.status == KEY_SUCCESS) { (void) fprintf(stderr, "%08x%08x\n", res.cryptkeyres_u.deskey.key.high, res.cryptkeyres_u.deskey.key.low); } else { (void) fprintf(stderr, "%s\n", strstatus(res.status)); } (void) fflush(stderr); } return (&res); } /* ARGSUSED */ des_block * key_gen_1_svc_prog(void *v, struct svc_req *s) { struct timeval time; static des_block keygen; static des_block key; (void)gettimeofday(&time, NULL); keygen.key.high += (time.tv_sec ^ time.tv_usec); keygen.key.low += (time.tv_sec ^ time.tv_usec); ecb_crypt((char *)&masterkey, (char *)&keygen, sizeof (keygen), DES_ENCRYPT | DES_HW); key = keygen; des_setparity((char *)&key); if (debugging) { (void) fprintf(stderr, "gen() = %08x%08x\n", key.key.high, key.key.low); (void) fflush(stderr); } return (&key); } getcredres * key_getcred_1_svc_prog(uid_t uid, netnamestr *name) { static getcredres res; static u_int gids[NGROUPS]; struct unixcred *cred; cred = &res.getcredres_u.cred; cred->gids.gids_val = gids; if (!netname2user(*name, (uid_t *) &cred->uid, (gid_t *) &cred->gid, (int *)&cred->gids.gids_len, (gid_t *)gids)) { res.status = KEY_UNKNOWN; } else { res.status = KEY_SUCCESS; } if (debugging) { (void) fprintf(stderr, "getcred(%s) = ", *name); if (res.status == KEY_SUCCESS) { (void) fprintf(stderr, "uid=%d, gid=%d, grouplen=%d\n", cred->uid, cred->gid, cred->gids.gids_len); } else { (void) fprintf(stderr, "%s\n", strstatus(res.status)); } (void) fflush(stderr); } return (&res); } /* * RPC boilerplate */ static void keyprogram(struct svc_req *rqstp, SVCXPRT *transp) { union { keybuf key_set_1_arg; cryptkeyarg key_encrypt_1_arg; cryptkeyarg key_decrypt_1_arg; netnamestr key_getcred_1_arg; cryptkeyarg key_encrypt_2_arg; cryptkeyarg key_decrypt_2_arg; netnamestr key_getcred_2_arg; cryptkeyarg2 key_encrypt_pk_2_arg; cryptkeyarg2 key_decrypt_pk_2_arg; key_netstarg key_net_put_2_arg; netobj key_get_conv_2_arg; } argument; char *result; xdrproc_t xdr_argument, xdr_result; typedef void *(svc_cb)(uid_t uid, void *arg); svc_cb *local; uid_t uid = -1; int check_auth; switch (rqstp->rq_proc) { case NULLPROC: svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); return; case KEY_SET: xdr_argument = (xdrproc_t)xdr_keybuf; xdr_result = (xdrproc_t)xdr_int; local = (svc_cb *)key_set_1_svc_prog; check_auth = 1; break; case KEY_ENCRYPT: xdr_argument = (xdrproc_t)xdr_cryptkeyarg; xdr_result = (xdrproc_t)xdr_cryptkeyres; local = (svc_cb *)key_encrypt_1_svc_prog; check_auth = 1; break; case KEY_DECRYPT: xdr_argument = (xdrproc_t)xdr_cryptkeyarg; xdr_result = (xdrproc_t)xdr_cryptkeyres; local = (svc_cb *)key_decrypt_1_svc_prog; check_auth = 1; break; case KEY_GEN: xdr_argument = (xdrproc_t)xdr_void; xdr_result = (xdrproc_t)xdr_des_block; local = (svc_cb *)key_gen_1_svc_prog; check_auth = 0; break; case KEY_GETCRED: xdr_argument = (xdrproc_t)xdr_netnamestr; xdr_result = (xdrproc_t)xdr_getcredres; local = (svc_cb *)key_getcred_1_svc_prog; check_auth = 0; break; case KEY_ENCRYPT_PK: xdr_argument = (xdrproc_t)xdr_cryptkeyarg2; xdr_result = (xdrproc_t)xdr_cryptkeyres; local = (svc_cb *)key_encrypt_pk_2_svc_prog; check_auth = 1; break; case KEY_DECRYPT_PK: xdr_argument = (xdrproc_t)xdr_cryptkeyarg2; xdr_result = (xdrproc_t)xdr_cryptkeyres; local = (svc_cb *)key_decrypt_pk_2_svc_prog; check_auth = 1; break; case KEY_NET_PUT: xdr_argument = (xdrproc_t)xdr_key_netstarg; xdr_result = (xdrproc_t)xdr_keystatus; local = (svc_cb *)key_net_put_2_svc_prog; check_auth = 1; break; case KEY_NET_GET: xdr_argument = (xdrproc_t) xdr_void; xdr_result = (xdrproc_t)xdr_key_netstres; local = (svc_cb *)key_net_get_2_svc_prog; check_auth = 1; break; case KEY_GET_CONV: xdr_argument = (xdrproc_t) xdr_keybuf; xdr_result = (xdrproc_t)xdr_cryptkeyres; local = (svc_cb *)key_get_conv_2_svc_prog; check_auth = 1; break; default: svcerr_noproc(transp); return; } if (check_auth) { if (root_auth(transp, rqstp) == 0) { if (debugging) { (void) fprintf(stderr, "not local privileged process\n"); } svcerr_weakauth(transp); return; } if (rqstp->rq_cred.oa_flavor != AUTH_SYS) { if (debugging) { (void) fprintf(stderr, "not unix authentication\n"); } svcerr_weakauth(transp); return; } uid = ((struct authsys_parms *)rqstp->rq_clntcred)->aup_uid; } memset(&argument, 0, sizeof (argument)); if (!svc_getargs(transp, xdr_argument, &argument)) { svcerr_decode(transp); return; } result = (*local) (uid, &argument); if (!svc_sendreply(transp, xdr_result, result)) { if (debugging) (void) fprintf(stderr, "unable to reply\n"); svcerr_systemerr(transp); } if (!svc_freeargs(transp, xdr_argument, &argument)) { if (debugging) (void) fprintf(stderr, "unable to free arguments\n"); exit(1); } return; } static int root_auth(SVCXPRT *trans, struct svc_req *rqstp) { uid_t uid; struct sockaddr *remote; remote = svc_getrpccaller(trans)->buf; if (remote->sa_family != AF_UNIX) { if (debugging) fprintf(stderr, "client didn't use AF_UNIX\n"); return (0); } if (__rpc_get_local_uid(trans, &uid) < 0) { if (debugging) fprintf(stderr, "__rpc_get_local_uid failed\n"); return (0); } if (debugging) fprintf(stderr, "local_uid %u\n", uid); if (uid == 0) return (1); if (rqstp->rq_cred.oa_flavor == AUTH_SYS) { if (((uid_t) ((struct authunix_parms *) rqstp->rq_clntcred)->aup_uid) == uid) { return (1); } else { if (debugging) fprintf(stderr, "local_uid %u mismatches auth %u\n", uid, ((uid_t) ((struct authunix_parms *)rqstp->rq_clntcred)->aup_uid)); return (0); } } else { if (debugging) fprintf(stderr, "Not auth sys\n"); return (0); } } static void usage(void) { (void) fprintf(stderr, "usage: keyserv [-n] [-D] [-d] [-v] [-p path]\n"); (void) fprintf(stderr, "-d disables the use of default keys\n"); exit(1); } diff --git a/usr.sbin/keyserv/setkey.c b/usr.sbin/keyserv/setkey.c index d829e68937ef..d1496190b559 100644 --- a/usr.sbin/keyserv/setkey.c +++ b/usr.sbin/keyserv/setkey.c @@ -1,505 +1,503 @@ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #ifndef lint #if 0 static char sccsid[] = "@(#)setkey.c 1.11 94/04/25 SMI"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. */ /* * Do the real work of the keyserver. * Store secret keys. Compute common keys, * and use them to decrypt and encrypt DES keys. * Cache the common keys, so the expensive computation is avoided. */ #include #include #include #include #include #include #include #include #include #include #include #include "keyserv.h" static MINT *MODULUS; static char *fetchsecretkey( uid_t ); static void writecache( char *, char *, des_block * ); static int readcache( char *, char *, des_block * ); static void extractdeskey( MINT *, des_block * ); static int storesecretkey( uid_t, keybuf ); static keystatus pk_crypt( uid_t, char *, netobj *, des_block *, int); static int nodefaultkeys = 0; /* * prohibit the nobody key on this machine k (the -d flag) */ void pk_nodefaultkeys(void) { nodefaultkeys = 1; } /* * Set the modulus for all our Diffie-Hellman operations */ void setmodulus(char *modx) { MODULUS = mp_xtom(modx); } /* * Set the secretkey key for this uid */ keystatus pk_setkey(uid_t uid, keybuf skey) { if (!storesecretkey(uid, skey)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } /* * Encrypt the key using the public key associated with remote_name and the * secret key associated with uid. */ keystatus pk_encrypt(uid_t uid, char *remote_name, netobj *remote_key, des_block *key) { return (pk_crypt(uid, remote_name, remote_key, key, DES_ENCRYPT)); } /* * Decrypt the key using the public key associated with remote_name and the * secret key associated with uid. */ keystatus pk_decrypt(uid_t uid, char *remote_name, netobj *remote_key, des_block *key) { return (pk_crypt(uid, remote_name, remote_key, key, DES_DECRYPT)); } static int store_netname( uid_t, key_netstarg * ); static int fetch_netname( uid_t, key_netstarg * ); keystatus pk_netput(uid_t uid, key_netstarg *netstore) { if (!store_netname(uid, netstore)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } keystatus pk_netget(uid_t uid, key_netstarg *netstore) { if (!fetch_netname(uid, netstore)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } /* * Do the work of pk_encrypt && pk_decrypt */ static keystatus pk_crypt(uid_t uid, char *remote_name, netobj *remote_key, des_block *key, int mode) { char *xsecret; char xpublic[1024]; char xsecret_hold[1024]; des_block deskey; int err; MINT *public; MINT *secret; MINT *common; char zero[8]; xsecret = fetchsecretkey(uid); if (xsecret == NULL || xsecret[0] == 0) { memset(zero, 0, sizeof (zero)); xsecret = xsecret_hold; if (nodefaultkeys) return (KEY_NOSECRET); if (!getsecretkey("nobody", xsecret, zero) || xsecret[0] == 0) { return (KEY_NOSECRET); } } if (remote_key) { memcpy(xpublic, remote_key->n_bytes, remote_key->n_len); } else { bzero((char *)&xpublic, sizeof(xpublic)); if (!getpublickey(remote_name, xpublic)) { if (nodefaultkeys || !getpublickey("nobody", xpublic)) return (KEY_UNKNOWN); } } if (!readcache(xpublic, xsecret, &deskey)) { public = mp_xtom(xpublic); secret = mp_xtom(xsecret); /* Sanity Check on public and private keys */ if ((public == NULL) || (secret == NULL)) return (KEY_SYSTEMERR); common = mp_itom(0); mp_pow(public, secret, MODULUS, common); extractdeskey(common, &deskey); writecache(xpublic, xsecret, &deskey); mp_mfree(secret); mp_mfree(public); mp_mfree(common); } err = ecb_crypt((char *)&deskey, (char *)key, sizeof (des_block), DES_HW | mode); if (DES_FAILED(err)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } keystatus pk_get_conv_key(uid_t uid, keybuf xpublic, cryptkeyres *result) { char *xsecret; char xsecret_hold[1024]; MINT *public; MINT *secret; MINT *common; char zero[8]; xsecret = fetchsecretkey(uid); if (xsecret == NULL || xsecret[0] == 0) { memset(zero, 0, sizeof (zero)); xsecret = xsecret_hold; if (nodefaultkeys) return (KEY_NOSECRET); if (!getsecretkey("nobody", xsecret, zero) || xsecret[0] == 0) return (KEY_NOSECRET); } if (!readcache(xpublic, xsecret, &result->cryptkeyres_u.deskey)) { public = mp_xtom(xpublic); secret = mp_xtom(xsecret); /* Sanity Check on public and private keys */ if ((public == NULL) || (secret == NULL)) return (KEY_SYSTEMERR); common = mp_itom(0); mp_pow(public, secret, MODULUS, common); extractdeskey(common, &result->cryptkeyres_u.deskey); writecache(xpublic, xsecret, &result->cryptkeyres_u.deskey); mp_mfree(secret); mp_mfree(public); mp_mfree(common); } return (KEY_SUCCESS); } /* * Choose middle 64 bits of the common key to use as our des key, possibly * overwriting the lower order bits by setting parity. */ static void extractdeskey(MINT *ck, des_block *deskey) { MINT *a; short r; int i; short base = (1 << 8); char *k; a = mp_itom(0); #ifdef SOLARIS_MP _mp_move(ck, a); #else mp_move(ck, a); #endif for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) { mp_sdiv(a, base, a, &r); } k = deskey->c; for (i = 0; i < 8; i++) { mp_sdiv(a, base, a, &r); *k++ = r; } mp_mfree(a); des_setparity((char *)deskey); } /* * Key storage management */ #define KEY_ONLY 0 #define KEY_NAME 1 struct secretkey_netname_list { uid_t uid; key_netstarg keynetdata; u_char sc_flag; struct secretkey_netname_list *next; }; static struct secretkey_netname_list *g_secretkey_netname; /* * Store the keys and netname for this uid */ static int store_netname(uid_t uid, key_netstarg *netstore) { struct secretkey_netname_list *new; struct secretkey_netname_list **l; for (l = &g_secretkey_netname; *l != NULL && (*l)->uid != uid; l = &(*l)->next) { } if (*l == NULL) { new = (struct secretkey_netname_list *)malloc(sizeof (*new)); if (new == NULL) { return (0); } new->uid = uid; new->next = NULL; *l = new; } else { new = *l; if (new->keynetdata.st_netname) (void) free (new->keynetdata.st_netname); } memcpy(new->keynetdata.st_priv_key, netstore->st_priv_key, HEXKEYBYTES); memcpy(new->keynetdata.st_pub_key, netstore->st_pub_key, HEXKEYBYTES); if (netstore->st_netname) new->keynetdata.st_netname = strdup(netstore->st_netname); else new->keynetdata.st_netname = (char *)NULL; new->sc_flag = KEY_NAME; return (1); } /* * Fetch the keys and netname for this uid */ static int fetch_netname(uid_t uid, struct key_netstarg *key_netst) { struct secretkey_netname_list *l; for (l = g_secretkey_netname; l != NULL; l = l->next) { if ((l->uid == uid) && (l->sc_flag == KEY_NAME)){ memcpy(key_netst->st_priv_key, l->keynetdata.st_priv_key, HEXKEYBYTES); memcpy(key_netst->st_pub_key, l->keynetdata.st_pub_key, HEXKEYBYTES); if (l->keynetdata.st_netname) key_netst->st_netname = strdup(l->keynetdata.st_netname); else key_netst->st_netname = NULL; return (1); } } return (0); } static char * fetchsecretkey(uid_t uid) { struct secretkey_netname_list *l; for (l = g_secretkey_netname; l != NULL; l = l->next) { if (l->uid == uid) { return (l->keynetdata.st_priv_key); } } return (NULL); } /* * Store the secretkey for this uid */ static int storesecretkey(uid_t uid, keybuf key) { struct secretkey_netname_list *new; struct secretkey_netname_list **l; for (l = &g_secretkey_netname; *l != NULL && (*l)->uid != uid; l = &(*l)->next) { } if (*l == NULL) { new = (struct secretkey_netname_list *) malloc(sizeof (*new)); if (new == NULL) { return (0); } new->uid = uid; new->sc_flag = KEY_ONLY; memset(new->keynetdata.st_pub_key, 0, HEXKEYBYTES); new->keynetdata.st_netname = NULL; new->next = NULL; *l = new; } else { new = *l; } memcpy(new->keynetdata.st_priv_key, key, HEXKEYBYTES); return (1); } static int hexdigit(int val) { return ("0123456789abcdef"[val]); } void bin2hex(unsigned char *bin, unsigned char *hex, int size) { int i; for (i = 0; i < size; i++) { *hex++ = hexdigit(*bin >> 4); *hex++ = hexdigit(*bin++ & 0xf); } } static int hexval(char dig) { if ('0' <= dig && dig <= '9') { return (dig - '0'); } else if ('a' <= dig && dig <= 'f') { return (dig - 'a' + 10); } else if ('A' <= dig && dig <= 'F') { return (dig - 'A' + 10); } else { return (-1); } } void hex2bin(unsigned char *hex, unsigned char *bin, int size) { int i; for (i = 0; i < size; i++) { *bin = hexval(*hex++) << 4; *bin++ |= hexval(*hex++); } } /* * Exponential caching management */ struct cachekey_list { keybuf secret; keybuf public; des_block deskey; struct cachekey_list *next; }; static struct cachekey_list *g_cachedkeys; /* * cache result of expensive multiple precision exponential operation */ static void writecache(char *pub, char *sec, des_block *deskey) { struct cachekey_list *new; new = (struct cachekey_list *) malloc(sizeof (struct cachekey_list)); if (new == NULL) { return; } memcpy(new->public, pub, sizeof (keybuf)); memcpy(new->secret, sec, sizeof (keybuf)); new->deskey = *deskey; new->next = g_cachedkeys; g_cachedkeys = new; } /* * Try to find the common key in the cache */ static int readcache(char *pub, char *sec, des_block *deskey) { struct cachekey_list *found; register struct cachekey_list **l; #define cachehit(pub, sec, list) \ (memcmp(pub, (list)->public, sizeof (keybuf)) == 0 && \ memcmp(sec, (list)->secret, sizeof (keybuf)) == 0) for (l = &g_cachedkeys; (*l) != NULL && !cachehit(pub, sec, *l); l = &(*l)->next) ; if ((*l) == NULL) { return (0); } found = *l; (*l) = (*l)->next; found->next = g_cachedkeys; g_cachedkeys = found; *deskey = found->deskey; return (1); } diff --git a/usr.sbin/mptable/mptable.c b/usr.sbin/mptable/mptable.c index 79bee8e1edd8..0d1e169ec96f 100644 --- a/usr.sbin/mptable/mptable.c +++ b/usr.sbin/mptable/mptable.c @@ -1,933 +1,928 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1996, by Steve Passe * 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. The name of the developer may NOT be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. */ /* * mptable.c */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - /* * this will cause the raw mp table to be dumped to /tmp/mpdump * #define RAW_DUMP */ #define MP_SIG 0x5f504d5f /* _MP_ */ #define EXTENDED_PROCESSING_READY #define OEM_PROCESSING_READY_NOT #include #include #include #include #include #include #include #include #include #include #include #define SEP_LINE \ "\n-------------------------------------------------------------------------------\n" #define SEP_LINE2 \ "\n===============================================================================\n" /* EBDA is @ 40:0e in real-mode terms */ #define EBDA_POINTER 0x040e /* location of EBDA pointer */ /* CMOS 'top of mem' is @ 40:13 in real-mode terms */ #define TOPOFMEM_POINTER 0x0413 /* BIOS: base memory size */ #define DEFAULT_TOPOFMEM 0xa0000 #define BIOS_BASE 0xf0000 #define BIOS_BASE2 0xe0000 #define BIOS_SIZE 0x10000 #define ONE_KBYTE 1024 #define GROPE_AREA1 0x80000 #define GROPE_AREA2 0x90000 #define GROPE_SIZE 0x10000 #define MAXPNSTR 132 typedef struct BUSTYPENAME { u_char type; char name[ 7 ]; } busTypeName; static const busTypeName busTypeTable[] = { { CBUS, "CBUS" }, { CBUSII, "CBUSII" }, { EISA, "EISA" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" }, { ISA, "ISA" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" }, { PCI, "PCI" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" }, { UNKNOWN_BUSTYPE, "---" } }; static const char *whereStrings[] = { "Extended BIOS Data Area", "BIOS top of memory", "Default top of memory", "BIOS", "Extended BIOS", "GROPE AREA #1", "GROPE AREA #2" }; static void apic_probe( u_int32_t* paddr, int* where ); static void MPConfigDefault( int featureByte ); static void MPFloatingPointer( u_int32_t paddr, int where, mpfps_t* mpfpsp ); static void MPConfigTableHeader( u_int32_t pap ); static void seekEntry( u_int32_t addr ); static void readEntry( void* entry, int size ); static void *mapEntry( u_int32_t addr, int size ); static void processorEntry( proc_entry_ptr entry ); static void busEntry( bus_entry_ptr entry ); static void ioApicEntry( io_apic_entry_ptr entry ); static void intEntry( int_entry_ptr entry ); static void sasEntry( sas_entry_ptr entry ); static void bhdEntry( bhd_entry_ptr entry ); static void cbasmEntry( cbasm_entry_ptr entry ); static void doDmesg( void ); static void pnstr( char* s, int c ); /* global data */ static int pfd; /* physical /dev/mem fd */ static int busses[256]; static int apics[256]; static int ncpu; static int nbus; static int napic; static int nintr; static int dmesg; static int grope; static int verbose; static void usage( void ) { fprintf( stderr, "usage: mptable [-dmesg] [-verbose] [-grope] [-help]\n" ); exit( 0 ); } /* * */ int main( int argc, char *argv[] ) { u_int32_t paddr; int where; mpfps_t mpfps; int defaultConfig; int ch; /* announce ourselves */ puts( SEP_LINE2 ); printf( "MPTable\n" ); while ((ch = getopt(argc, argv, "d:g:h:v:")) != -1) { switch(ch) { case 'd': if ( strcmp( optarg, "mesg") == 0 ) dmesg = 1; else dmesg = 0; break; case 'h': if ( strcmp( optarg, "elp") == 0 ) usage(); break; case 'g': if ( strcmp( optarg, "rope") == 0 ) grope = 1; break; case 'v': if ( strcmp( optarg, "erbose") == 0 ) verbose = 1; break; default: usage(); } argc -= optind; argv += optind; optreset = 1; optind = 0; } /* open physical memory for access to MP structures */ if ( (pfd = open( _PATH_MEM, O_RDONLY )) < 0 ) err( 1, "mem open" ); /* probe for MP structures */ apic_probe( &paddr, &where ); if ( where <= 0 ) { fprintf( stderr, "\n MP FPS NOT found,\n" ); if (!grope) fprintf( stderr, " suggest trying -grope option!!!\n\n" ); return 1; } if ( verbose ) printf( "\n MP FPS found in %s @ physical addr: 0x%08x\n", whereStrings[ where - 1 ], paddr ); puts( SEP_LINE ); /* analyze the MP Floating Pointer Structure */ MPFloatingPointer( paddr, where, &mpfps ); puts( SEP_LINE ); /* check whether an MP config table exists */ if ( (defaultConfig = mpfps->config_type) ) MPConfigDefault( defaultConfig ); else MPConfigTableHeader( mpfps->pap ); /* do a dmesg output */ if ( dmesg ) doDmesg(); puts( SEP_LINE2 ); return 0; } /* * set PHYSICAL address of MP floating pointer structure */ #define NEXT(X) ((X) += 4) static void apic_probe( u_int32_t* paddr, int* where ) { /* * c rewrite of apic_probe() by Jack F. Vogel */ int x; u_short segment; u_int32_t target; u_int buffer[ BIOS_SIZE / sizeof( int ) ]; if ( verbose ) printf( "\n" ); /* search Extended Bios Data Area, if present */ if ( verbose ) printf( " looking for EBDA pointer @ 0x%04x, ", EBDA_POINTER ); seekEntry( (u_int32_t)EBDA_POINTER ); readEntry( &segment, 2 ); if ( segment ) { /* search EBDA */ target = (u_int32_t)segment << 4; if ( verbose ) printf( "found, searching EBDA @ 0x%08x\n", target ); seekEntry( target ); readEntry( buffer, ONE_KBYTE ); for ( x = 0; x < ONE_KBYTE / (int)sizeof ( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 1; *paddr = (x * sizeof( unsigned int )) + target; return; } } } else { if ( verbose ) printf( "NOT found\n" ); } /* read CMOS for real top of mem */ seekEntry( (u_int32_t)TOPOFMEM_POINTER ); readEntry( &segment, 2 ); --segment; /* less ONE_KBYTE */ target = segment * 1024; if ( verbose ) printf( " searching CMOS 'top of mem' @ 0x%08x (%dK)\n", target, segment ); seekEntry( target ); readEntry( buffer, ONE_KBYTE ); for ( x = 0; x < ONE_KBYTE / (int)sizeof ( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 2; *paddr = (x * sizeof( unsigned int )) + target; return; } } /* we don't necessarily believe CMOS, check base of the last 1K of 640K */ if ( target != (DEFAULT_TOPOFMEM - 1024)) { target = (DEFAULT_TOPOFMEM - 1024); if ( verbose ) printf( " searching default 'top of mem' @ 0x%08x (%dK)\n", target, (target / 1024) ); seekEntry( target ); readEntry( buffer, ONE_KBYTE ); for ( x = 0; x < ONE_KBYTE / (int)sizeof ( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 3; *paddr = (x * sizeof( unsigned int )) + target; return; } } } /* search the BIOS */ if ( verbose ) printf( " searching BIOS @ 0x%08x\n", BIOS_BASE ); seekEntry( BIOS_BASE ); readEntry( buffer, BIOS_SIZE ); for ( x = 0; x < BIOS_SIZE / (int)sizeof( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 4; *paddr = (x * sizeof( unsigned int )) + BIOS_BASE; return; } } /* search the extended BIOS */ if ( verbose ) printf( " searching extended BIOS @ 0x%08x\n", BIOS_BASE2 ); seekEntry( BIOS_BASE2 ); readEntry( buffer, BIOS_SIZE ); for ( x = 0; x < BIOS_SIZE / (int)sizeof( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 5; *paddr = (x * sizeof( unsigned int )) + BIOS_BASE2; return; } } if ( grope ) { /* search additional memory */ target = GROPE_AREA1; if ( verbose ) printf( " groping memory @ 0x%08x\n", target ); seekEntry( target ); readEntry( buffer, GROPE_SIZE ); for ( x = 0; x < GROPE_SIZE / (int)sizeof( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 6; *paddr = (x * sizeof( unsigned int )) + GROPE_AREA1; return; } } target = GROPE_AREA2; if ( verbose ) printf( " groping memory @ 0x%08x\n", target ); seekEntry( target ); readEntry( buffer, GROPE_SIZE ); for ( x = 0; x < GROPE_SIZE / (int)sizeof( unsigned int ); NEXT(x) ) { if ( buffer[ x ] == MP_SIG ) { *where = 7; *paddr = (x * sizeof( unsigned int )) + GROPE_AREA2; return; } } } *where = 0; *paddr = (u_int32_t)0; } /* * */ static void MPFloatingPointer( u_int32_t paddr, int where, mpfps_t* mpfpsp ) { mpfps_t mpfps; /* map in mpfps structure*/ *mpfpsp = mpfps = mapEntry( paddr, sizeof( *mpfps ) ); /* show its contents */ printf( "MP Floating Pointer Structure:\n\n" ); printf( " location:\t\t\t" ); switch ( where ) { case 1: printf( "EBDA\n" ); break; case 2: printf( "BIOS base memory\n" ); break; case 3: printf( "DEFAULT base memory (639K)\n" ); break; case 4: printf( "BIOS\n" ); break; case 5: printf( "Extended BIOS\n" ); break; case 0: printf( "NOT found!\n" ); exit( 1 ); default: printf( "BOGUS!\n" ); exit( 1 ); } printf( " physical address:\t\t0x%08x\n", paddr ); printf( " signature:\t\t\t'" ); pnstr( mpfps->signature, 4 ); printf( "'\n" ); printf( " length:\t\t\t%d bytes\n", mpfps->length * 16 ); printf( " version:\t\t\t1.%1d\n", mpfps->spec_rev ); printf( " checksum:\t\t\t0x%02x\n", mpfps->checksum ); /* bits 0:6 are RESERVED */ if ( mpfps->mpfb2 & 0x7f ) { printf( " warning, MP feature byte 2: 0x%02x\n", mpfps->mpfb2 ); } /* bit 7 is IMCRP */ printf( " mode:\t\t\t\t%s\n", (mpfps->mpfb2 & MPFB2_IMCR_PRESENT) ? "PIC" : "Virtual Wire" ); /* MP feature bytes 3-5 are expected to be ZERO */ if ( mpfps->mpfb3 ) printf( " warning, MP feature byte 3 NONZERO!\n" ); if ( mpfps->mpfb4 ) printf( " warning, MP feature byte 4 NONZERO!\n" ); if ( mpfps->mpfb5 ) printf( " warning, MP feature byte 5 NONZERO!\n" ); } /* * */ static void MPConfigDefault( int featureByte ) { printf( " MP default config type: %d\n\n", featureByte ); switch ( featureByte ) { case 1: printf( " bus: ISA, APIC: 82489DX\n" ); break; case 2: printf( " bus: EISA, APIC: 82489DX\n" ); break; case 3: printf( " bus: EISA, APIC: 82489DX\n" ); break; case 4: printf( " bus: MCA, APIC: 82489DX\n" ); break; case 5: printf( " bus: ISA+PCI, APIC: Integrated\n" ); break; case 6: printf( " bus: EISA+PCI, APIC: Integrated\n" ); break; case 7: printf( " bus: MCA+PCI, APIC: Integrated\n" ); break; default: printf( " future type\n" ); break; } switch ( featureByte ) { case 1: case 2: case 3: case 4: nbus = 1; break; case 5: case 6: case 7: nbus = 2; break; default: printf( " future type\n" ); break; } ncpu = 2; napic = 1; nintr = 16; } /* * */ static void MPConfigTableHeader( u_int32_t pap ) { mpcth_t cth; int x; int c; int oldtype, entrytype; u_int8_t *entry; if ( pap == 0 ) { printf( "MP Configuration Table Header MISSING!\n" ); exit( 1 ); } /* map in cth structure */ cth = mapEntry( pap, sizeof( *cth ) ); printf( "MP Config Table Header:\n\n" ); printf( " physical address:\t\t0x%08x\n", pap ); printf( " signature:\t\t\t'" ); pnstr( cth->signature, 4 ); printf( "'\n" ); printf( " base table length:\t\t%d\n", cth->base_table_length ); printf( " version:\t\t\t1.%1d\n", cth->spec_rev ); printf( " checksum:\t\t\t0x%02x\n", cth->checksum ); printf( " OEM ID:\t\t\t'" ); pnstr( cth->oem_id, 8 ); printf( "'\n" ); printf( " Product ID:\t\t\t'" ); pnstr( cth->product_id, 12 ); printf( "'\n" ); printf( " OEM table pointer:\t\t0x%08x\n", cth->oem_table_pointer ); printf( " OEM table size:\t\t%d\n", cth->oem_table_size ); printf( " entry count:\t\t\t%d\n", cth->entry_count ); printf( " local APIC address:\t\t0x%08x\n", cth->apic_address ); printf( " extended table length:\t%d\n", cth->extended_table_length ); printf( " extended table checksum:\t%d\n", cth->extended_table_checksum ); puts( SEP_LINE ); printf( "MP Config Base Table Entries:\n\n" ); /* initialize tables */ for (x = 0; x < (int)nitems(busses); x++) busses[x] = 0xff; for (x = 0; x < (int)nitems(apics); x++) apics[x] = 0xff; ncpu = 0; nbus = 0; napic = 0; nintr = 0; oldtype = -1; entry = mapEntry(pap + sizeof(*cth), cth->base_table_length); for (c = cth->entry_count; c; c--) { entrytype = *entry; if (entrytype != oldtype) printf("--\n"); if (entrytype < oldtype) printf("MPTABLE OUT OF ORDER!\n"); switch (entrytype) { case MPCT_ENTRY_PROCESSOR: if (oldtype != MPCT_ENTRY_PROCESSOR) printf( "Processors:\tAPIC ID\tVersion\tState" "\t\tFamily\tModel\tStep\tFlags\n" ); processorEntry((proc_entry_ptr)entry); entry += sizeof(struct PROCENTRY); break; case MPCT_ENTRY_BUS: if (oldtype != MPCT_ENTRY_BUS) printf( "Bus:\t\tBus ID\tType\n" ); busEntry((bus_entry_ptr)entry); entry += sizeof(struct BUSENTRY); break; case MPCT_ENTRY_IOAPIC: if (oldtype != MPCT_ENTRY_IOAPIC) printf( "I/O APICs:\tAPIC ID\tVersion\tState\t\tAddress\n" ); ioApicEntry((io_apic_entry_ptr)entry); entry += sizeof(struct IOAPICENTRY); break; case MPCT_ENTRY_INT: if (oldtype != MPCT_ENTRY_INT) printf( "I/O Ints:\tType\tPolarity Trigger\tBus ID\t IRQ\tAPIC ID\tPIN#\n" ); intEntry((int_entry_ptr)entry); entry += sizeof(struct INTENTRY); break; case MPCT_ENTRY_LOCAL_INT: if (oldtype != MPCT_ENTRY_LOCAL_INT) printf( "Local Ints:\tType\tPolarity Trigger\tBus ID\t IRQ\tAPIC ID\tPIN#\n" ); intEntry((int_entry_ptr)entry); entry += sizeof(struct INTENTRY); break; default: printf("MPTABLE HOSED! record type = %d\n", entrytype); exit(1); } oldtype = entrytype; } #if defined( EXTENDED_PROCESSING_READY ) /* process any extended data */ if ( cth->extended_table_length ) { ext_entry_ptr ext_entry, end; puts( SEP_LINE ); printf( "MP Config Extended Table Entries:\n\n" ); ext_entry = mapEntry(pap + cth->base_table_length, cth->extended_table_length); end = (ext_entry_ptr)((char *)ext_entry + cth->extended_table_length); while (ext_entry < end) { switch (ext_entry->type) { case MPCT_EXTENTRY_SAS: sasEntry((sas_entry_ptr)ext_entry); break; case MPCT_EXTENTRY_BHD: bhdEntry((bhd_entry_ptr)ext_entry); break; case MPCT_EXTENTRY_CBASM: cbasmEntry((cbasm_entry_ptr)ext_entry); break; default: printf( "Extended Table HOSED!\n" ); exit( 1 ); } ext_entry = (ext_entry_ptr)((char *)ext_entry + ext_entry->length); } } #endif /* EXTENDED_PROCESSING_READY */ /* process any OEM data */ if ( cth->oem_table_pointer && (cth->oem_table_size > 0) ) { #if defined( OEM_PROCESSING_READY ) # error your on your own here! /* map in oem table structure */ oemdata = mapEntry( cth->oem_table_pointer, cth->oem_table_size); /** process it */ #else printf( "\nyou need to modify the source to handle OEM data!\n\n" ); #endif /* OEM_PROCESSING_READY */ } fflush( stdout ); #if defined( RAW_DUMP ) { int ofd; void *dumpbuf; ofd = open( "/tmp/mpdump", O_CREAT | O_RDWR, 0666 ); dumpbuf = mapEntry( paddr, 1024 ); write( ofd, dumpbuf, 1024 ); close( ofd ); } #endif /* RAW_DUMP */ } /* * */ static void seekEntry( u_int32_t addr ) { if ( lseek( pfd, (off_t)addr, SEEK_SET ) < 0 ) err( 1, "%s seek", _PATH_MEM ); } /* * */ static void readEntry( void* entry, int size ) { if ( read( pfd, entry, size ) != size ) err( 1, "readEntry" ); } static void * mapEntry( u_int32_t addr, int size ) { void *p; p = mmap( NULL, size, PROT_READ, MAP_SHARED, pfd, addr ); if (p == MAP_FAILED) err( 1, "mapEntry" ); return (p); } static void processorEntry( proc_entry_ptr entry ) { /* count it */ ++ncpu; printf( "\t\t%2d", entry->apic_id ); printf( "\t 0x%2x", entry->apic_version ); printf( "\t %s, %s", (entry->cpu_flags & PROCENTRY_FLAG_BP) ? "BSP" : "AP", (entry->cpu_flags & PROCENTRY_FLAG_EN) ? "usable" : "unusable" ); printf( "\t %d\t %d\t %d", (entry->cpu_signature >> 8) & 0x0f, (entry->cpu_signature >> 4) & 0x0f, entry->cpu_signature & 0x0f ); printf( "\t 0x%04x\n", entry->feature_flags ); } /* * */ static int lookupBusType( char* name ) { int x; for ( x = 0; x < MAX_BUSTYPE; ++x ) if ( strcmp( busTypeTable[ x ].name, name ) == 0 ) return busTypeTable[ x ].type; return UNKNOWN_BUSTYPE; } static void busEntry( bus_entry_ptr entry ) { int x; char name[ 8 ]; char c; /* count it */ ++nbus; printf( "\t\t%2d", entry->bus_id ); printf( "\t " ); pnstr( entry->bus_type, 6 ); printf( "\n" ); for ( x = 0; x < 6; ++x ) { if ( (c = entry->bus_type[ x ]) == ' ' ) break; name[ x ] = c; } name[ x ] = '\0'; busses[ entry->bus_id ] = lookupBusType( name ); } static void ioApicEntry( io_apic_entry_ptr entry ) { /* count it */ ++napic; printf( "\t\t%2d", entry->apic_id ); printf( "\t 0x%02x", entry->apic_version ); printf( "\t %s", (entry->apic_flags & IOAPICENTRY_FLAG_EN) ? "usable" : "unusable" ); printf( "\t\t 0x%x\n", entry->apic_address ); apics[ entry->apic_id ] = entry->apic_id; } static const char *intTypes[] = { "INT", "NMI", "SMI", "ExtINT" }; static const char *polarityMode[] = { "conforms", "active-hi", "reserved", "active-lo" }; static const char *triggerMode[] = { "conforms", "edge", "reserved", "level" }; static void intEntry( int_entry_ptr entry ) { /* count it */ if ( entry->type == MPCT_ENTRY_INT ) ++nintr; printf( "\t\t%s", intTypes[ entry->int_type ] ); printf( "\t%9s", polarityMode[ entry->int_flags & INTENTRY_FLAGS_POLARITY ] ); printf( "%12s", triggerMode[ (entry->int_flags & INTENTRY_FLAGS_TRIGGER) >> 2 ] ); printf( "\t %5d", entry->src_bus_id ); if ( busses[ entry->src_bus_id ] == PCI ) printf( "\t%2d:%c", (entry->src_bus_irq >> 2) & 0x1f, (entry->src_bus_irq & 0x03) + 'A' ); else printf( "\t %3d", entry->src_bus_irq ); printf( "\t %6d", entry->dst_apic_id ); printf( "\t %3d\n", entry->dst_apic_int ); } static void sasEntry( sas_entry_ptr entry ) { printf( "--\nSystem Address Space\n"); printf( " bus ID: %d", entry->bus_id ); printf( " address type: " ); switch ( entry->address_type ) { case SASENTRY_TYPE_IO: printf( "I/O address\n" ); break; case SASENTRY_TYPE_MEMORY: printf( "memory address\n" ); break; case SASENTRY_TYPE_PREFETCH: printf( "prefetch address\n" ); break; default: printf( "UNKNOWN type\n" ); break; } printf( " address base: 0x%jx\n", (uintmax_t)entry->address_base ); printf( " address range: 0x%jx\n", (uintmax_t)entry->address_length ); } static void bhdEntry( bhd_entry_ptr entry ) { printf( "--\nBus Hierarchy\n" ); printf( " bus ID: %d", entry->bus_id ); printf( " bus info: 0x%02x", entry->bus_info ); printf( " parent bus ID: %d\n", entry->parent_bus ); } static void cbasmEntry( cbasm_entry_ptr entry ) { printf( "--\nCompatibility Bus Address\n" ); printf( " bus ID: %d", entry->bus_id ); printf( " address modifier: %s\n", (entry->address_mod & CBASMENTRY_ADDRESS_MOD_SUBTRACT) ? "subtract" : "add" ); printf( " predefined range: 0x%08x\n", entry->predefined_range ); } /* * do a dmesg output */ static void doDmesg( void ) { puts( SEP_LINE ); printf( "dmesg output:\n\n" ); fflush( stdout ); system( "dmesg" ); } /* * */ static void pnstr( char* s, int c ) { char string[ MAXPNSTR + 1 ]; if ( c > MAXPNSTR ) c = MAXPNSTR; strncpy( string, s, c ); string[ c ] = '\0'; printf( "%s", string ); } diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c index 3a7f58c7b4e5..be26e0be77e8 100644 --- a/usr.sbin/nfsd/nfsd.c +++ b/usr.sbin/nfsd/nfsd.c @@ -1,1363 +1,1361 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * 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 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)nfsd.c 8.9 (Berkeley) 3/29/95"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #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 static int debug = 0; #define NFSD_STABLERESTART "/var/db/nfs-stablerestart" #define NFSD_STABLEBACKUP "/var/db/nfs-stablerestart.bak" #define MAXNFSDCNT 256 #define DEFNFSDCNT 4 #define NFS_VER2 2 #define NFS_VER3 3 #define NFS_VER4 4 static pid_t children[MAXNFSDCNT]; /* PIDs of children */ static pid_t masterpid; /* PID of master/parent */ static int nfsdcnt; /* number of children */ static int nfsdcnt_set; static int minthreads; static int maxthreads; static int nfssvc_nfsd; /* Set to correct NFSSVC_xxx flag */ static int stablefd = -1; /* Fd for the stable restart file */ static int backupfd; /* Fd for the backup stable restart file */ static const char *getopt_shortopts; static const char *getopt_usage; static int nfs_minvers = NFS_VER2; static int minthreads_set; static int maxthreads_set; static struct option longopts[] = { { "debug", no_argument, &debug, 1 }, { "minthreads", required_argument, &minthreads_set, 1 }, { "maxthreads", required_argument, &maxthreads_set, 1 }, { "pnfs", required_argument, NULL, 'p' }, { "mirror", required_argument, NULL, 'm' }, { NULL, 0, NULL, 0} }; static void cleanup(int); static void child_cleanup(int); static void killchildren(void); static void nfsd_exit(int); static void nonfs(int); static void reapchild(int); static int setbindhost(struct addrinfo **ia, const char *bindhost, struct addrinfo hints); static void start_server(int, struct nfsd_nfsd_args *, const char *vhost); static void unregistration(void); static void usage(void); static void open_stable(int *, int *); static void copy_stable(int, int); static void backup_stable(int); static void set_nfsdcnt(int); static void parse_dsserver(const char *, struct nfsd_nfsd_args *); /* * Nfs server daemon mostly just a user context for nfssvc() * * 1 - do file descriptor and signal cleanup * 2 - fork the nfsd(s) * 3 - create server socket(s) * 4 - register socket with rpcbind * * For connectionless protocols, just pass the socket into the kernel via. * nfssvc(). * For connection based sockets, loop doing accepts. When you get a new * socket from accept, pass the msgsock into the kernel via. nfssvc(). * The arguments are: * -r - reregister with rpcbind * -d - unregister with rpcbind * -t - support tcp nfs clients * -u - support udp nfs clients * -e - forces it to run a server that supports nfsv4 * -p - enable a pNFS service * -m - set the mirroring level for a pNFS service * followed by "n" which is the number of nfsds' to fork off */ int main(int argc, char **argv) { struct nfsd_addsock_args addsockargs; struct addrinfo *ai_udp, *ai_tcp, *ai_udp6, *ai_tcp6, hints; struct netconfig *nconf_udp, *nconf_tcp, *nconf_udp6, *nconf_tcp6; struct netbuf nb_udp, nb_tcp, nb_udp6, nb_tcp6; struct sockaddr_storage peer; fd_set ready, sockbits; int ch, connect_type_cnt, i, maxsock, msgsock; socklen_t len; int on = 1, unregister, reregister, sock; int tcp6sock, ip6flag, tcpflag, tcpsock; int udpflag, ecode, error, s; int bindhostc, bindanyflag, rpcbreg, rpcbregcnt; int nfssvc_addsock; int jailed, longindex = 0; size_t jailed_size, nfs_minvers_size; const char *lopt; char **bindhost = NULL; pid_t pid; struct nfsd_nfsd_args nfsdargs; const char *vhostname = NULL; nfsdargs.mirrorcnt = 1; nfsdargs.addr = NULL; nfsdargs.addrlen = 0; nfsdcnt = DEFNFSDCNT; unregister = reregister = tcpflag = maxsock = 0; bindanyflag = udpflag = connect_type_cnt = bindhostc = 0; getopt_shortopts = "ah:n:rdtuep:m:V:"; getopt_usage = "usage:\n" " nfsd [-ardtue] [-h bindip]\n" " [-n numservers] [--minthreads #] [--maxthreads #]\n" " [-p/--pnfs dsserver0:/dsserver0-mounted-on-dir,...," "dsserverN:/dsserverN-mounted-on-dir] [-m mirrorlevel]\n" " [-V virtual_hostname]\n"; while ((ch = getopt_long(argc, argv, getopt_shortopts, longopts, &longindex)) != -1) switch (ch) { case 'V': if (strlen(optarg) <= MAXHOSTNAMELEN) vhostname = optarg; else warnx("Virtual host name (%s) is too long", optarg); break; case 'a': bindanyflag = 1; break; case 'n': set_nfsdcnt(atoi(optarg)); break; case 'h': bindhostc++; bindhost = realloc(bindhost,sizeof(char *)*bindhostc); if (bindhost == NULL) errx(1, "Out of memory"); bindhost[bindhostc-1] = strdup(optarg); if (bindhost[bindhostc-1] == NULL) errx(1, "Out of memory"); break; case 'r': reregister = 1; break; case 'd': unregister = 1; break; case 't': tcpflag = 1; break; case 'u': udpflag = 1; break; case 'e': /* now a no-op, since this is the default */ break; case 'p': /* Parse out the DS server host names and mount pts. */ parse_dsserver(optarg, &nfsdargs); break; case 'm': /* Set the mirror level for a pNFS service. */ i = atoi(optarg); if (i < 2 || i > NFSDEV_MAXMIRRORS) errx(1, "Mirror level out of range 2<-->%d", NFSDEV_MAXMIRRORS); nfsdargs.mirrorcnt = i; break; case 0: lopt = longopts[longindex].name; if (!strcmp(lopt, "minthreads")) { minthreads = atoi(optarg); } else if (!strcmp(lopt, "maxthreads")) { maxthreads = atoi(optarg); } break; default: case '?': usage(); } if (!tcpflag && !udpflag) udpflag = 1; argv += optind; argc -= optind; if (minthreads_set && maxthreads_set && minthreads > maxthreads) errx(EX_USAGE, "error: minthreads(%d) can't be greater than " "maxthreads(%d)", minthreads, maxthreads); /* * XXX * Backward compatibility, trailing number is the count of daemons. */ if (argc > 1) usage(); if (argc == 1) set_nfsdcnt(atoi(argv[0])); /* * Unless the "-o" option was specified, try and run "nfsd". * If "-o" was specified, try and run "nfsserver". */ if (modfind("nfsd") < 0) { /* Not present in kernel, try loading it */ if (kldload("nfsd") < 0 || modfind("nfsd") < 0) errx(1, "NFS server is not available"); } ip6flag = 1; s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (s == -1) { if (errno != EPROTONOSUPPORT && errno != EAFNOSUPPORT) err(1, "socket"); ip6flag = 0; } else if (getnetconfigent("udp6") == NULL || getnetconfigent("tcp6") == NULL) { ip6flag = 0; } if (s != -1) close(s); if (bindhostc == 0 || bindanyflag) { bindhostc++; bindhost = realloc(bindhost,sizeof(char *)*bindhostc); if (bindhost == NULL) errx(1, "Out of memory"); bindhost[bindhostc-1] = strdup("*"); if (bindhost[bindhostc-1] == NULL) errx(1, "Out of memory"); } if (unregister) { /* * Unregister before setting nfs_minvers, in case the * value of vfs.nfsd.server_min_nfsvers has changed * since registering with rpcbind. */ unregistration(); exit (0); } nfs_minvers_size = sizeof(nfs_minvers); error = sysctlbyname("vfs.nfsd.server_min_nfsvers", &nfs_minvers, &nfs_minvers_size, NULL, 0); if (error != 0 || nfs_minvers < NFS_VER2 || nfs_minvers > NFS_VER4) { warnx("sysctlbyname(vfs.nfsd.server_min_nfsvers) failed," " defaulting to NFSv2"); nfs_minvers = NFS_VER2; } if (reregister) { if (udpflag) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp); if (ecode != 0) err(1, "getaddrinfo udp: %s", gai_strerror(ecode)); nconf_udp = getnetconfigent("udp"); if (nconf_udp == NULL) err(1, "getnetconfigent udp failed"); nb_udp.buf = ai_udp->ai_addr; nb_udp.len = nb_udp.maxlen = ai_udp->ai_addrlen; if (nfs_minvers == NFS_VER2) if (!rpcb_set(NFS_PROGRAM, 2, nconf_udp, &nb_udp)) err(1, "rpcb_set udp failed"); if (nfs_minvers <= NFS_VER3) if (!rpcb_set(NFS_PROGRAM, 3, nconf_udp, &nb_udp)) err(1, "rpcb_set udp failed"); freeaddrinfo(ai_udp); } if (udpflag && ip6flag) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp6); if (ecode != 0) err(1, "getaddrinfo udp6: %s", gai_strerror(ecode)); nconf_udp6 = getnetconfigent("udp6"); if (nconf_udp6 == NULL) err(1, "getnetconfigent udp6 failed"); nb_udp6.buf = ai_udp6->ai_addr; nb_udp6.len = nb_udp6.maxlen = ai_udp6->ai_addrlen; if (nfs_minvers == NFS_VER2) if (!rpcb_set(NFS_PROGRAM, 2, nconf_udp6, &nb_udp6)) err(1, "rpcb_set udp6 failed"); if (nfs_minvers <= NFS_VER3) if (!rpcb_set(NFS_PROGRAM, 3, nconf_udp6, &nb_udp6)) err(1, "rpcb_set udp6 failed"); freeaddrinfo(ai_udp6); } if (tcpflag) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp); if (ecode != 0) err(1, "getaddrinfo tcp: %s", gai_strerror(ecode)); nconf_tcp = getnetconfigent("tcp"); if (nconf_tcp == NULL) err(1, "getnetconfigent tcp failed"); nb_tcp.buf = ai_tcp->ai_addr; nb_tcp.len = nb_tcp.maxlen = ai_tcp->ai_addrlen; if (nfs_minvers == NFS_VER2) if (!rpcb_set(NFS_PROGRAM, 2, nconf_tcp, &nb_tcp)) err(1, "rpcb_set tcp failed"); if (nfs_minvers <= NFS_VER3) if (!rpcb_set(NFS_PROGRAM, 3, nconf_tcp, &nb_tcp)) err(1, "rpcb_set tcp failed"); freeaddrinfo(ai_tcp); } if (tcpflag && ip6flag) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp6); if (ecode != 0) err(1, "getaddrinfo tcp6: %s", gai_strerror(ecode)); nconf_tcp6 = getnetconfigent("tcp6"); if (nconf_tcp6 == NULL) err(1, "getnetconfigent tcp6 failed"); nb_tcp6.buf = ai_tcp6->ai_addr; nb_tcp6.len = nb_tcp6.maxlen = ai_tcp6->ai_addrlen; if (nfs_minvers == NFS_VER2) if (!rpcb_set(NFS_PROGRAM, 2, nconf_tcp6, &nb_tcp6)) err(1, "rpcb_set tcp6 failed"); if (nfs_minvers <= NFS_VER3) if (!rpcb_set(NFS_PROGRAM, 3, nconf_tcp6, &nb_tcp6)) err(1, "rpcb_set tcp6 failed"); freeaddrinfo(ai_tcp6); } exit (0); } if (debug == 0) { daemon(0, 0); (void)signal(SIGHUP, SIG_IGN); (void)signal(SIGINT, SIG_IGN); /* * nfsd sits in the kernel most of the time. It needs * to ignore SIGTERM/SIGQUIT in order to stay alive as long * as possible during a shutdown, otherwise loopback * mounts will not be able to unmount. */ (void)signal(SIGTERM, SIG_IGN); (void)signal(SIGQUIT, SIG_IGN); } (void)signal(SIGSYS, nonfs); (void)signal(SIGCHLD, reapchild); (void)signal(SIGUSR2, backup_stable); openlog("nfsd", LOG_PID | (debug ? LOG_PERROR : 0), LOG_DAEMON); /* * For V4, we open the stablerestart file and call nfssvc() * to get it loaded. This is done before the daemons do the * regular nfssvc() call to service NFS requests. * (This way the file remains open until the last nfsd is killed * off.) * It and the backup copy will be created as empty files * the first time this nfsd is started and should never be * deleted/replaced if at all possible. It should live on a * local, non-volatile storage device that does not do hardware * level write-back caching. (See SCSI doc for more information * on how to prevent write-back caching on SCSI disks.) */ open_stable(&stablefd, &backupfd); if (stablefd < 0) { syslog(LOG_ERR, "Can't open %s: %m\n", NFSD_STABLERESTART); exit(1); } /* This system call will fail for old kernels, but that's ok. */ nfssvc(NFSSVC_BACKUPSTABLE, NULL); if (nfssvc(NFSSVC_STABLERESTART, (caddr_t)&stablefd) < 0) { if (errno == EPERM) { jailed = 0; jailed_size = sizeof(jailed); sysctlbyname("security.jail.jailed", &jailed, &jailed_size, NULL, 0); if (jailed != 0) syslog(LOG_ERR, "nfssvc stablerestart failed: " "allow.nfsd might not be configured"); else syslog(LOG_ERR, "nfssvc stablerestart failed"); } else if (errno == ENXIO) syslog(LOG_ERR, "nfssvc stablerestart failed: is nfsd " "already running?"); else syslog(LOG_ERR, "Can't read stable storage file: %m\n"); exit(1); } nfssvc_addsock = NFSSVC_NFSDADDSOCK; nfssvc_nfsd = NFSSVC_NFSDNFSD | NFSSVC_NEWSTRUCT; if (tcpflag) { /* * For TCP mode, we fork once to start the first * kernel nfsd thread. The kernel will add more * threads as needed. */ masterpid = getpid(); pid = fork(); if (pid == -1) { syslog(LOG_ERR, "fork: %m"); nfsd_exit(1); } if (pid) { children[0] = pid; } else { (void)signal(SIGUSR1, child_cleanup); setproctitle("server"); start_server(0, &nfsdargs, vhostname); } } (void)signal(SIGUSR1, cleanup); FD_ZERO(&sockbits); rpcbregcnt = 0; /* Set up the socket for udp and rpcb register it. */ if (udpflag) { rpcbreg = 0; for (i = 0; i < bindhostc; i++) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; if (setbindhost(&ai_udp, bindhost[i], hints) == 0) { rpcbreg = 1; rpcbregcnt++; if ((sock = socket(ai_udp->ai_family, ai_udp->ai_socktype, ai_udp->ai_protocol)) < 0) { syslog(LOG_ERR, "can't create udp socket"); nfsd_exit(1); } if (bind(sock, ai_udp->ai_addr, ai_udp->ai_addrlen) < 0) { syslog(LOG_ERR, "can't bind udp addr %s: %m", bindhost[i]); nfsd_exit(1); } freeaddrinfo(ai_udp); addsockargs.sock = sock; addsockargs.name = NULL; addsockargs.namelen = 0; if (nfssvc(nfssvc_addsock, &addsockargs) < 0) { syslog(LOG_ERR, "can't Add UDP socket"); nfsd_exit(1); } (void)close(sock); } } if (rpcbreg == 1) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp); if (ecode != 0) { syslog(LOG_ERR, "getaddrinfo udp: %s", gai_strerror(ecode)); nfsd_exit(1); } nconf_udp = getnetconfigent("udp"); if (nconf_udp == NULL) err(1, "getnetconfigent udp failed"); nb_udp.buf = ai_udp->ai_addr; nb_udp.len = nb_udp.maxlen = ai_udp->ai_addrlen; if (nfs_minvers == NFS_VER2) if (!rpcb_set(NFS_PROGRAM, 2, nconf_udp, &nb_udp)) err(1, "rpcb_set udp failed"); if (nfs_minvers <= NFS_VER3) if (!rpcb_set(NFS_PROGRAM, 3, nconf_udp, &nb_udp)) err(1, "rpcb_set udp failed"); freeaddrinfo(ai_udp); } } /* Set up the socket for udp6 and rpcb register it. */ if (udpflag && ip6flag) { rpcbreg = 0; for (i = 0; i < bindhostc; i++) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; if (setbindhost(&ai_udp6, bindhost[i], hints) == 0) { rpcbreg = 1; rpcbregcnt++; if ((sock = socket(ai_udp6->ai_family, ai_udp6->ai_socktype, ai_udp6->ai_protocol)) < 0) { syslog(LOG_ERR, "can't create udp6 socket"); nfsd_exit(1); } if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof on) < 0) { syslog(LOG_ERR, "can't set v6-only binding for " "udp6 socket: %m"); nfsd_exit(1); } if (bind(sock, ai_udp6->ai_addr, ai_udp6->ai_addrlen) < 0) { syslog(LOG_ERR, "can't bind udp6 addr %s: %m", bindhost[i]); nfsd_exit(1); } freeaddrinfo(ai_udp6); addsockargs.sock = sock; addsockargs.name = NULL; addsockargs.namelen = 0; if (nfssvc(nfssvc_addsock, &addsockargs) < 0) { syslog(LOG_ERR, "can't add UDP6 socket"); nfsd_exit(1); } (void)close(sock); } } if (rpcbreg == 1) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp6); if (ecode != 0) { syslog(LOG_ERR, "getaddrinfo udp6: %s", gai_strerror(ecode)); nfsd_exit(1); } nconf_udp6 = getnetconfigent("udp6"); if (nconf_udp6 == NULL) err(1, "getnetconfigent udp6 failed"); nb_udp6.buf = ai_udp6->ai_addr; nb_udp6.len = nb_udp6.maxlen = ai_udp6->ai_addrlen; if (nfs_minvers == NFS_VER2) if (!rpcb_set(NFS_PROGRAM, 2, nconf_udp6, &nb_udp6)) err(1, "rpcb_set udp6 failed"); if (nfs_minvers <= NFS_VER3) if (!rpcb_set(NFS_PROGRAM, 3, nconf_udp6, &nb_udp6)) err(1, "rpcb_set udp6 failed"); freeaddrinfo(ai_udp6); } } /* Set up the socket for tcp and rpcb register it. */ if (tcpflag) { rpcbreg = 0; for (i = 0; i < bindhostc; i++) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if (setbindhost(&ai_tcp, bindhost[i], hints) == 0) { rpcbreg = 1; rpcbregcnt++; if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, "can't create tcp socket"); nfsd_exit(1); } if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); if (bind(tcpsock, ai_tcp->ai_addr, ai_tcp->ai_addrlen) < 0) { syslog(LOG_ERR, "can't bind tcp addr %s: %m", bindhost[i]); nfsd_exit(1); } if (listen(tcpsock, -1) < 0) { syslog(LOG_ERR, "listen failed"); nfsd_exit(1); } freeaddrinfo(ai_tcp); FD_SET(tcpsock, &sockbits); maxsock = tcpsock; connect_type_cnt++; } } if (rpcbreg == 1) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp); if (ecode != 0) { syslog(LOG_ERR, "getaddrinfo tcp: %s", gai_strerror(ecode)); nfsd_exit(1); } nconf_tcp = getnetconfigent("tcp"); if (nconf_tcp == NULL) err(1, "getnetconfigent tcp failed"); nb_tcp.buf = ai_tcp->ai_addr; nb_tcp.len = nb_tcp.maxlen = ai_tcp->ai_addrlen; if (nfs_minvers == NFS_VER2) if (!rpcb_set(NFS_PROGRAM, 2, nconf_tcp, &nb_tcp)) err(1, "rpcb_set tcp failed"); if (nfs_minvers <= NFS_VER3) if (!rpcb_set(NFS_PROGRAM, 3, nconf_tcp, &nb_tcp)) err(1, "rpcb_set tcp failed"); freeaddrinfo(ai_tcp); } } /* Set up the socket for tcp6 and rpcb register it. */ if (tcpflag && ip6flag) { rpcbreg = 0; for (i = 0; i < bindhostc; i++) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if (setbindhost(&ai_tcp6, bindhost[i], hints) == 0) { rpcbreg = 1; rpcbregcnt++; if ((tcp6sock = socket(ai_tcp6->ai_family, ai_tcp6->ai_socktype, ai_tcp6->ai_protocol)) < 0) { syslog(LOG_ERR, "can't create tcp6 socket"); nfsd_exit(1); } if (setsockopt(tcp6sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); if (setsockopt(tcp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof on) < 0) { syslog(LOG_ERR, "can't set v6-only binding for tcp6 " "socket: %m"); nfsd_exit(1); } if (bind(tcp6sock, ai_tcp6->ai_addr, ai_tcp6->ai_addrlen) < 0) { syslog(LOG_ERR, "can't bind tcp6 addr %s: %m", bindhost[i]); nfsd_exit(1); } if (listen(tcp6sock, -1) < 0) { syslog(LOG_ERR, "listen failed"); nfsd_exit(1); } freeaddrinfo(ai_tcp6); FD_SET(tcp6sock, &sockbits); if (maxsock < tcp6sock) maxsock = tcp6sock; connect_type_cnt++; } } if (rpcbreg == 1) { memset(&hints, 0, sizeof hints); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp6); if (ecode != 0) { syslog(LOG_ERR, "getaddrinfo tcp6: %s", gai_strerror(ecode)); nfsd_exit(1); } nconf_tcp6 = getnetconfigent("tcp6"); if (nconf_tcp6 == NULL) err(1, "getnetconfigent tcp6 failed"); nb_tcp6.buf = ai_tcp6->ai_addr; nb_tcp6.len = nb_tcp6.maxlen = ai_tcp6->ai_addrlen; if (nfs_minvers == NFS_VER2) if (!rpcb_set(NFS_PROGRAM, 2, nconf_tcp6, &nb_tcp6)) err(1, "rpcb_set tcp6 failed"); if (nfs_minvers <= NFS_VER3) if (!rpcb_set(NFS_PROGRAM, 3, nconf_tcp6, &nb_tcp6)) err(1, "rpcb_set tcp6 failed"); freeaddrinfo(ai_tcp6); } } if (rpcbregcnt == 0) { syslog(LOG_ERR, "rpcb_set() failed, nothing to do: %m"); nfsd_exit(1); } if (tcpflag && connect_type_cnt == 0) { syslog(LOG_ERR, "tcp connects == 0, nothing to do: %m"); nfsd_exit(1); } setproctitle("master"); /* * We always want a master to have a clean way to shut nfsd down * (with unregistration): if the master is killed, it unregisters and * kills all children. If we run for UDP only (and so do not have to * loop waiting for accept), we instead make the parent * a "server" too. start_server will not return. */ if (!tcpflag) start_server(1, &nfsdargs, vhostname); /* * Loop forever accepting connections and passing the sockets * into the kernel for the mounts. */ for (;;) { ready = sockbits; if (connect_type_cnt > 1) { if (select(maxsock + 1, &ready, NULL, NULL, NULL) < 1) { error = errno; if (error == EINTR) continue; syslog(LOG_ERR, "select failed: %m"); nfsd_exit(1); } } for (tcpsock = 0; tcpsock <= maxsock; tcpsock++) { if (FD_ISSET(tcpsock, &ready)) { len = sizeof(peer); if ((msgsock = accept(tcpsock, (struct sockaddr *)&peer, &len)) < 0) { error = errno; syslog(LOG_ERR, "accept failed: %m"); if (error == ECONNABORTED || error == EINTR) continue; nfsd_exit(1); } if (setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "setsockopt SO_KEEPALIVE: %m"); addsockargs.sock = msgsock; addsockargs.name = (caddr_t)&peer; addsockargs.namelen = len; nfssvc(nfssvc_addsock, &addsockargs); (void)close(msgsock); } } } } static int setbindhost(struct addrinfo **ai, const char *bindhost, struct addrinfo hints) { int ecode; u_int32_t host_addr[4]; /* IPv4 or IPv6 */ const char *hostptr; if (bindhost == NULL || strcmp("*", bindhost) == 0) hostptr = NULL; else hostptr = bindhost; if (hostptr != NULL) { switch (hints.ai_family) { case AF_INET: if (inet_pton(AF_INET, hostptr, host_addr) == 1) { hints.ai_flags = AI_NUMERICHOST; } else { if (inet_pton(AF_INET6, hostptr, host_addr) == 1) return (1); } break; case AF_INET6: if (inet_pton(AF_INET6, hostptr, host_addr) == 1) { hints.ai_flags = AI_NUMERICHOST; } else { if (inet_pton(AF_INET, hostptr, host_addr) == 1) return (1); } break; default: break; } } ecode = getaddrinfo(hostptr, "nfs", &hints, ai); if (ecode != 0) { syslog(LOG_ERR, "getaddrinfo %s: %s", bindhost, gai_strerror(ecode)); return (1); } return (0); } static void set_nfsdcnt(int proposed) { if (proposed < 1) { warnx("nfsd count too low %d; reset to %d", proposed, DEFNFSDCNT); nfsdcnt = DEFNFSDCNT; } else if (proposed > MAXNFSDCNT) { warnx("nfsd count too high %d; truncated to %d", proposed, MAXNFSDCNT); nfsdcnt = MAXNFSDCNT; } else nfsdcnt = proposed; nfsdcnt_set = 1; } static void usage(void) { (void)fprintf(stderr, "%s", getopt_usage); exit(1); } static void nonfs(__unused int signo) { syslog(LOG_ERR, "missing system call: NFS not available"); } static void reapchild(__unused int signo) { pid_t pid; int i; while ((pid = wait3(NULL, WNOHANG, NULL)) > 0) { for (i = 0; i < nfsdcnt; i++) if (pid == children[i]) children[i] = -1; } } static void unregistration(void) { if ((nfs_minvers == NFS_VER2 && !rpcb_unset(NFS_PROGRAM, 2, NULL)) || (nfs_minvers <= NFS_VER3 && !rpcb_unset(NFS_PROGRAM, 3, NULL))) syslog(LOG_ERR, "rpcb_unset failed"); } static void killchildren(void) { int i; for (i = 0; i < nfsdcnt; i++) { if (children[i] > 0) kill(children[i], SIGKILL); } } /* * Cleanup master after SIGUSR1. */ static void cleanup(__unused int signo) { nfsd_exit(0); } /* * Cleanup child after SIGUSR1. */ static void child_cleanup(__unused int signo) { exit(0); } static void nfsd_exit(int status) { killchildren(); unregistration(); exit(status); } static int get_tuned_nfsdcount(void) { int ncpu, error, tuned_nfsdcnt; size_t ncpu_size; ncpu_size = sizeof(ncpu); error = sysctlbyname("hw.ncpu", &ncpu, &ncpu_size, NULL, 0); if (error) { warnx("sysctlbyname(hw.ncpu) failed defaulting to %d nfs servers", DEFNFSDCNT); tuned_nfsdcnt = DEFNFSDCNT; } else { tuned_nfsdcnt = ncpu * 8; } return tuned_nfsdcnt; } static void start_server(int master, struct nfsd_nfsd_args *nfsdargp, const char *vhost) { char principal[MAXHOSTNAMELEN + 5]; int status, error; char hostname[MAXHOSTNAMELEN + 1], *cp; struct addrinfo *aip, hints; status = 0; if (vhost == NULL) gethostname(hostname, sizeof (hostname)); else strlcpy(hostname, vhost, sizeof (hostname)); snprintf(principal, sizeof (principal), "nfs@%s", hostname); if ((cp = strchr(hostname, '.')) == NULL || *(cp + 1) == '\0') { /* If not fully qualified, try getaddrinfo() */ memset((void *)&hints, 0, sizeof (hints)); hints.ai_flags = AI_CANONNAME; error = getaddrinfo(hostname, NULL, &hints, &aip); if (error == 0) { if (aip->ai_canonname != NULL && (cp = strchr(aip->ai_canonname, '.')) != NULL && *(cp + 1) != '\0') snprintf(principal, sizeof (principal), "nfs@%s", aip->ai_canonname); freeaddrinfo(aip); } } nfsdargp->principal = principal; if (nfsdcnt_set) nfsdargp->minthreads = nfsdargp->maxthreads = nfsdcnt; else { nfsdargp->minthreads = minthreads_set ? minthreads : get_tuned_nfsdcount(); nfsdargp->maxthreads = maxthreads_set ? maxthreads : nfsdargp->minthreads; if (nfsdargp->maxthreads < nfsdargp->minthreads) nfsdargp->maxthreads = nfsdargp->minthreads; } error = nfssvc(nfssvc_nfsd, nfsdargp); if (error < 0 && errno == EAUTH) { /* * This indicates that it could not register the * rpcsec_gss credentials, usually because the * gssd daemon isn't running. * (only the experimental server with nfsv4) */ syslog(LOG_ERR, "No gssd, using AUTH_SYS only"); principal[0] = '\0'; error = nfssvc(nfssvc_nfsd, nfsdargp); } if (error < 0) { if (errno == ENXIO) { syslog(LOG_ERR, "Bad -p option, cannot run"); if (masterpid != 0 && master == 0) kill(masterpid, SIGUSR1); } else syslog(LOG_ERR, "nfssvc: %m"); status = 1; } if (master) nfsd_exit(status); else exit(status); } /* * Open the stable restart file and return the file descriptor for it. */ static void open_stable(int *stable_fdp, int *backup_fdp) { int stable_fd, backup_fd = -1, ret; struct stat st, backup_st; /* Open and stat the stable restart file. */ stable_fd = open(NFSD_STABLERESTART, O_RDWR, 0); if (stable_fd < 0) stable_fd = open(NFSD_STABLERESTART, O_RDWR | O_CREAT, 0600); if (stable_fd >= 0) { ret = fstat(stable_fd, &st); if (ret < 0) { close(stable_fd); stable_fd = -1; } } /* Open and stat the backup stable restart file. */ if (stable_fd >= 0) { backup_fd = open(NFSD_STABLEBACKUP, O_RDWR, 0); if (backup_fd < 0) backup_fd = open(NFSD_STABLEBACKUP, O_RDWR | O_CREAT, 0600); if (backup_fd >= 0) { ret = fstat(backup_fd, &backup_st); if (ret < 0) { close(backup_fd); backup_fd = -1; } } if (backup_fd < 0) { close(stable_fd); stable_fd = -1; } } *stable_fdp = stable_fd; *backup_fdp = backup_fd; if (stable_fd < 0) return; /* Sync up the 2 files, as required. */ if (st.st_size > 0) copy_stable(stable_fd, backup_fd); else if (backup_st.st_size > 0) copy_stable(backup_fd, stable_fd); } /* * Copy the stable restart file to the backup or vice versa. */ static void copy_stable(int from_fd, int to_fd) { int cnt, ret; static char buf[1024]; ret = lseek(from_fd, (off_t)0, SEEK_SET); if (ret >= 0) ret = lseek(to_fd, (off_t)0, SEEK_SET); if (ret >= 0) ret = ftruncate(to_fd, (off_t)0); if (ret >= 0) do { cnt = read(from_fd, buf, 1024); if (cnt > 0) ret = write(to_fd, buf, cnt); else if (cnt < 0) ret = cnt; } while (cnt > 0 && ret >= 0); if (ret >= 0) ret = fsync(to_fd); if (ret < 0) syslog(LOG_ERR, "stable restart copy failure: %m"); } /* * Back up the stable restart file when indicated by the kernel. */ static void backup_stable(__unused int signo) { if (stablefd >= 0) copy_stable(stablefd, backupfd); } /* * Parse the pNFS string and extract the DS servers and ports numbers. */ static void parse_dsserver(const char *optionarg, struct nfsd_nfsd_args *nfsdargp) { char *cp, *cp2, *dsaddr, *dshost, *dspath, *dsvol, nfsprt[9]; char *mdspath, *mdsp, ip6[INET6_ADDRSTRLEN]; const char *ad; int ecode; u_int adsiz, dsaddrcnt, dshostcnt, dspathcnt, hostsiz, pathsiz; u_int mdspathcnt; size_t dsaddrsiz, dshostsiz, dspathsiz, nfsprtsiz, mdspathsiz; struct addrinfo hints, *ai_tcp, *res; struct sockaddr_in sin; struct sockaddr_in6 sin6; cp = strdup(optionarg); if (cp == NULL) errx(1, "Out of memory"); /* Now, do the host names. */ dspathsiz = 1024; dspathcnt = 0; dspath = malloc(dspathsiz); if (dspath == NULL) errx(1, "Out of memory"); dshostsiz = 1024; dshostcnt = 0; dshost = malloc(dshostsiz); if (dshost == NULL) errx(1, "Out of memory"); dsaddrsiz = 1024; dsaddrcnt = 0; dsaddr = malloc(dsaddrsiz); if (dsaddr == NULL) errx(1, "Out of memory"); mdspathsiz = 1024; mdspathcnt = 0; mdspath = malloc(mdspathsiz); if (mdspath == NULL) errx(1, "Out of memory"); /* Put the NFS port# in "." form. */ snprintf(nfsprt, 9, ".%d.%d", 2049 >> 8, 2049 & 0xff); nfsprtsiz = strlen(nfsprt); ai_tcp = NULL; /* Loop around for each DS server name. */ do { cp2 = strchr(cp, ','); if (cp2 != NULL) { /* Not the last DS in the list. */ *cp2++ = '\0'; if (*cp2 == '\0') usage(); } dsvol = strchr(cp, ':'); if (dsvol == NULL || *(dsvol + 1) == '\0') usage(); *dsvol++ = '\0'; /* Optional path for MDS file system to be stored on DS. */ mdsp = strchr(dsvol, '#'); if (mdsp != NULL) { if (*(mdsp + 1) == '\0' || mdsp <= dsvol) usage(); *mdsp++ = '\0'; } /* Append this pathname to dspath. */ pathsiz = strlen(dsvol); if (dspathcnt + pathsiz + 1 > dspathsiz) { dspathsiz *= 2; dspath = realloc(dspath, dspathsiz); if (dspath == NULL) errx(1, "Out of memory"); } strcpy(&dspath[dspathcnt], dsvol); dspathcnt += pathsiz + 1; /* Append this pathname to mdspath. */ if (mdsp != NULL) pathsiz = strlen(mdsp); else pathsiz = 0; if (mdspathcnt + pathsiz + 1 > mdspathsiz) { mdspathsiz *= 2; mdspath = realloc(mdspath, mdspathsiz); if (mdspath == NULL) errx(1, "Out of memory"); } if (mdsp != NULL) strcpy(&mdspath[mdspathcnt], mdsp); else mdspath[mdspathcnt] = '\0'; mdspathcnt += pathsiz + 1; if (ai_tcp != NULL) freeaddrinfo(ai_tcp); /* Get the fully qualified domain name and IP address. */ memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; ecode = getaddrinfo(cp, NULL, &hints, &ai_tcp); if (ecode != 0) err(1, "getaddrinfo pnfs: %s %s", cp, gai_strerror(ecode)); ad = NULL; for (res = ai_tcp; res != NULL; res = res->ai_next) { if (res->ai_addr->sa_family == AF_INET) { if (res->ai_addrlen < sizeof(sin)) err(1, "getaddrinfo() returned " "undersized IPv4 address"); /* * Mips cares about sockaddr_in alignment, * so copy the address. */ memcpy(&sin, res->ai_addr, sizeof(sin)); ad = inet_ntoa(sin.sin_addr); break; } else if (res->ai_family == AF_INET6) { if (res->ai_addrlen < sizeof(sin6)) err(1, "getaddrinfo() returned " "undersized IPv6 address"); /* * Mips cares about sockaddr_in6 alignment, * so copy the address. */ memcpy(&sin6, res->ai_addr, sizeof(sin6)); ad = inet_ntop(AF_INET6, &sin6.sin6_addr, ip6, sizeof(ip6)); /* * XXX * Since a link local address will only * work if the client and DS are in the * same scope zone, only use it if it is * the only address. */ if (ad != NULL && !IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) break; } } if (ad == NULL) err(1, "No IP address for %s", cp); /* Append this address to dsaddr. */ adsiz = strlen(ad); if (dsaddrcnt + adsiz + nfsprtsiz + 1 > dsaddrsiz) { dsaddrsiz *= 2; dsaddr = realloc(dsaddr, dsaddrsiz); if (dsaddr == NULL) errx(1, "Out of memory"); } strcpy(&dsaddr[dsaddrcnt], ad); strcat(&dsaddr[dsaddrcnt], nfsprt); dsaddrcnt += adsiz + nfsprtsiz + 1; /* Append this hostname to dshost. */ hostsiz = strlen(ai_tcp->ai_canonname); if (dshostcnt + hostsiz + 1 > dshostsiz) { dshostsiz *= 2; dshost = realloc(dshost, dshostsiz); if (dshost == NULL) errx(1, "Out of memory"); } strcpy(&dshost[dshostcnt], ai_tcp->ai_canonname); dshostcnt += hostsiz + 1; cp = cp2; } while (cp != NULL); nfsdargp->addr = dsaddr; nfsdargp->addrlen = dsaddrcnt; nfsdargp->dnshost = dshost; nfsdargp->dnshostlen = dshostcnt; nfsdargp->dspath = dspath; nfsdargp->dspathlen = dspathcnt; nfsdargp->mdspath = mdspath; nfsdargp->mdspathlen = mdspathcnt; freeaddrinfo(ai_tcp); } diff --git a/usr.sbin/pciconf/cap.c b/usr.sbin/pciconf/cap.c index 9cae4bc49e03..8595bff3d3d7 100644 --- a/usr.sbin/pciconf/cap.c +++ b/usr.sbin/pciconf/cap.c @@ -1,1238 +1,1233 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2007 Yahoo!, Inc. * All rights reserved. * Written by: John Baldwin * * 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 author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include "pciconf.h" static void list_ecaps(int fd, struct pci_conf *p); static int cap_level; static void cap_power(int fd, struct pci_conf *p, uint8_t ptr) { uint16_t cap, status; cap = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_CAP, 2); status = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_STATUS, 2); printf("powerspec %d supports D0%s%s D3 current D%d", cap & PCIM_PCAP_SPEC, cap & PCIM_PCAP_D1SUPP ? " D1" : "", cap & PCIM_PCAP_D2SUPP ? " D2" : "", status & PCIM_PSTAT_DMASK); } static void cap_agp(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t status, command; status = read_config(fd, &p->pc_sel, ptr + AGP_STATUS, 4); command = read_config(fd, &p->pc_sel, ptr + AGP_CAPID, 4); printf("AGP "); if (AGP_MODE_GET_MODE_3(status)) { printf("v3 "); if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_8x) printf("8x "); if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_4x) printf("4x "); } else { if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_4x) printf("4x "); if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_2x) printf("2x "); if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_1x) printf("1x "); } if (AGP_MODE_GET_SBA(status)) printf("SBA "); if (AGP_MODE_GET_AGP(command)) { printf("enabled at "); if (AGP_MODE_GET_MODE_3(command)) { printf("v3 "); switch (AGP_MODE_GET_RATE(command)) { case AGP_MODE_V3_RATE_8x: printf("8x "); break; case AGP_MODE_V3_RATE_4x: printf("4x "); break; } } else switch (AGP_MODE_GET_RATE(command)) { case AGP_MODE_V2_RATE_4x: printf("4x "); break; case AGP_MODE_V2_RATE_2x: printf("2x "); break; case AGP_MODE_V2_RATE_1x: printf("1x "); break; } if (AGP_MODE_GET_SBA(command)) printf("SBA "); } else printf("disabled"); } static void cap_vpd(int fd __unused, struct pci_conf *p __unused, uint8_t ptr __unused) { printf("VPD"); } static void cap_msi(int fd, struct pci_conf *p, uint8_t ptr) { uint16_t ctrl; int msgnum; ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSI_CTRL, 2); msgnum = 1 << ((ctrl & PCIM_MSICTRL_MMC_MASK) >> 1); printf("MSI supports %d message%s%s%s ", msgnum, (msgnum == 1) ? "" : "s", (ctrl & PCIM_MSICTRL_64BIT) ? ", 64 bit" : "", (ctrl & PCIM_MSICTRL_VECTOR) ? ", vector masks" : ""); if (ctrl & PCIM_MSICTRL_MSI_ENABLE) { msgnum = 1 << ((ctrl & PCIM_MSICTRL_MME_MASK) >> 4); printf("enabled with %d message%s", msgnum, (msgnum == 1) ? "" : "s"); } } static void cap_pcix(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t status; int comma, max_splits, max_burst_read; status = read_config(fd, &p->pc_sel, ptr + PCIXR_STATUS, 4); printf("PCI-X "); if (status & PCIXM_STATUS_64BIT) printf("64-bit "); if ((p->pc_hdr & PCIM_HDRTYPE) == 1) printf("bridge "); if ((p->pc_hdr & PCIM_HDRTYPE) != 1 || (status & (PCIXM_STATUS_133CAP | PCIXM_STATUS_266CAP | PCIXM_STATUS_533CAP)) != 0) printf("supports"); comma = 0; if (status & PCIXM_STATUS_133CAP) { printf(" 133MHz"); comma = 1; } if (status & PCIXM_STATUS_266CAP) { printf("%s 266MHz", comma ? "," : ""); comma = 1; } if (status & PCIXM_STATUS_533CAP) { printf("%s 533MHz", comma ? "," : ""); comma = 1; } if ((p->pc_hdr & PCIM_HDRTYPE) == 1) return; max_burst_read = 0; switch (status & PCIXM_STATUS_MAX_READ) { case PCIXM_STATUS_MAX_READ_512: max_burst_read = 512; break; case PCIXM_STATUS_MAX_READ_1024: max_burst_read = 1024; break; case PCIXM_STATUS_MAX_READ_2048: max_burst_read = 2048; break; case PCIXM_STATUS_MAX_READ_4096: max_burst_read = 4096; break; } max_splits = 0; switch (status & PCIXM_STATUS_MAX_SPLITS) { case PCIXM_STATUS_MAX_SPLITS_1: max_splits = 1; break; case PCIXM_STATUS_MAX_SPLITS_2: max_splits = 2; break; case PCIXM_STATUS_MAX_SPLITS_3: max_splits = 3; break; case PCIXM_STATUS_MAX_SPLITS_4: max_splits = 4; break; case PCIXM_STATUS_MAX_SPLITS_8: max_splits = 8; break; case PCIXM_STATUS_MAX_SPLITS_12: max_splits = 12; break; case PCIXM_STATUS_MAX_SPLITS_16: max_splits = 16; break; case PCIXM_STATUS_MAX_SPLITS_32: max_splits = 32; break; } printf("%s %d burst read, %d split transaction%s", comma ? "," : "", max_burst_read, max_splits, max_splits == 1 ? "" : "s"); } static void cap_ht(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t reg; uint16_t command; command = read_config(fd, &p->pc_sel, ptr + PCIR_HT_COMMAND, 2); printf("HT "); if ((command & 0xe000) == PCIM_HTCAP_SLAVE) printf("slave"); else if ((command & 0xe000) == PCIM_HTCAP_HOST) printf("host"); else switch (command & PCIM_HTCMD_CAP_MASK) { case PCIM_HTCAP_SWITCH: printf("switch"); break; case PCIM_HTCAP_INTERRUPT: printf("interrupt"); break; case PCIM_HTCAP_REVISION_ID: printf("revision ID"); break; case PCIM_HTCAP_UNITID_CLUMPING: printf("unit ID clumping"); break; case PCIM_HTCAP_EXT_CONFIG_SPACE: printf("extended config space"); break; case PCIM_HTCAP_ADDRESS_MAPPING: printf("address mapping"); break; case PCIM_HTCAP_MSI_MAPPING: printf("MSI %saddress window %s at 0x", command & PCIM_HTCMD_MSI_FIXED ? "fixed " : "", command & PCIM_HTCMD_MSI_ENABLE ? "enabled" : "disabled"); if (command & PCIM_HTCMD_MSI_FIXED) printf("fee00000"); else { reg = read_config(fd, &p->pc_sel, ptr + PCIR_HTMSI_ADDRESS_HI, 4); if (reg != 0) printf("%08x", reg); reg = read_config(fd, &p->pc_sel, ptr + PCIR_HTMSI_ADDRESS_LO, 4); printf("%08x", reg); } break; case PCIM_HTCAP_DIRECT_ROUTE: printf("direct route"); break; case PCIM_HTCAP_VCSET: printf("VC set"); break; case PCIM_HTCAP_RETRY_MODE: printf("retry mode"); break; case PCIM_HTCAP_X86_ENCODING: printf("X86 encoding"); break; case PCIM_HTCAP_GEN3: printf("Gen3"); break; case PCIM_HTCAP_FLE: printf("function-level extension"); break; case PCIM_HTCAP_PM: printf("power management"); break; case PCIM_HTCAP_HIGH_NODE_COUNT: printf("high node count"); break; default: printf("unknown %02x", command); break; } } static void cap_vendor(int fd, struct pci_conf *p, uint8_t ptr) { uint8_t length; length = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_LENGTH, 1); printf("vendor (length %d)", length); if (p->pc_vendor == 0x8086) { /* Intel */ uint8_t version; version = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA, 1); printf(" Intel cap %d version %d", version >> 4, version & 0xf); if (version >> 4 == 1 && length == 12) { /* Feature Detection */ uint32_t fvec; int comma; comma = 0; fvec = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA + 5, 4); printf("\n\t\t features:"); if (fvec & (1 << 0)) { printf(" AMT"); comma = 1; } fvec = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA + 1, 4); if (fvec & (1 << 21)) { printf("%s Quick Resume", comma ? "," : ""); comma = 1; } if (fvec & (1 << 18)) { printf("%s SATA RAID-5", comma ? "," : ""); comma = 1; } if (fvec & (1 << 9)) { printf("%s Mobile", comma ? "," : ""); comma = 1; } if (fvec & (1 << 7)) { printf("%s 6 PCI-e x1 slots", comma ? "," : ""); comma = 1; } else { printf("%s 4 PCI-e x1 slots", comma ? "," : ""); comma = 1; } if (fvec & (1 << 5)) { printf("%s SATA RAID-0/1/10", comma ? "," : ""); comma = 1; } if (fvec & (1 << 3)) printf(", SATA AHCI"); } } } static void cap_debug(int fd, struct pci_conf *p, uint8_t ptr) { uint16_t debug_port; debug_port = read_config(fd, &p->pc_sel, ptr + PCIR_DEBUG_PORT, 2); printf("EHCI Debug Port at offset 0x%x in map 0x%x", debug_port & PCIM_DEBUG_PORT_OFFSET, PCIR_BAR(debug_port >> 13)); } static void cap_subvendor(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t id; uint16_t ssid, ssvid; id = read_config(fd, &p->pc_sel, ptr + PCIR_SUBVENDCAP_ID, 4); ssid = id >> 16; ssvid = id & 0xffff; printf("PCI Bridge subvendor=0x%04x subdevice=0x%04x", ssvid, ssid); } #define MAX_PAYLOAD(field) (128 << (field)) static const char * link_speed_string(uint8_t speed) { switch (speed) { case 1: return ("2.5"); case 2: return ("5.0"); case 3: return ("8.0"); case 4: return ("16.0"); case 5: return ("32.0"); case 6: return ("64.0"); default: return ("undef"); } } static const char * max_read_string(u_int max_read) { switch (max_read) { case 0x0: return ("128"); case 0x1: return ("256"); case 0x2: return ("512"); case 0x3: return ("1024"); case 0x4: return ("2048"); case 0x5: return ("4096"); default: return ("undef"); } } static const char * aspm_string(uint8_t aspm) { switch (aspm) { case 1: return ("L0s"); case 2: return ("L1"); case 3: return ("L0s/L1"); default: return ("disabled"); } } static int slot_power(uint32_t cap) { int mwatts; mwatts = (cap & PCIEM_SLOT_CAP_SPLV) >> 7; switch (cap & PCIEM_SLOT_CAP_SPLS) { case 0x0: mwatts *= 1000; break; case 0x1: mwatts *= 100; break; case 0x2: mwatts *= 10; break; default: break; } return (mwatts); } static void cap_express(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t cap; uint16_t ctl, flags, sta; unsigned int version; flags = read_config(fd, &p->pc_sel, ptr + PCIER_FLAGS, 2); version = flags & PCIEM_FLAGS_VERSION; printf("PCI-Express %u ", version); switch (flags & PCIEM_FLAGS_TYPE) { case PCIEM_TYPE_ENDPOINT: printf("endpoint"); break; case PCIEM_TYPE_LEGACY_ENDPOINT: printf("legacy endpoint"); break; case PCIEM_TYPE_ROOT_PORT: printf("root port"); break; case PCIEM_TYPE_UPSTREAM_PORT: printf("upstream port"); break; case PCIEM_TYPE_DOWNSTREAM_PORT: printf("downstream port"); break; case PCIEM_TYPE_PCI_BRIDGE: printf("PCI bridge"); break; case PCIEM_TYPE_PCIE_BRIDGE: printf("PCI to PCIe bridge"); break; case PCIEM_TYPE_ROOT_INT_EP: printf("root endpoint"); break; case PCIEM_TYPE_ROOT_EC: printf("event collector"); break; default: printf("type %d", (flags & PCIEM_FLAGS_TYPE) >> 4); break; } if (flags & PCIEM_FLAGS_IRQ) printf(" MSI %d", (flags & PCIEM_FLAGS_IRQ) >> 9); cap = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CAP, 4); ctl = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CTL, 2); printf(" max data %d(%d)", MAX_PAYLOAD((ctl & PCIEM_CTL_MAX_PAYLOAD) >> 5), MAX_PAYLOAD(cap & PCIEM_CAP_MAX_PAYLOAD)); if ((cap & PCIEM_CAP_FLR) != 0) printf(" FLR"); if (ctl & PCIEM_CTL_RELAXED_ORD_ENABLE) printf(" RO"); if (ctl & PCIEM_CTL_NOSNOOP_ENABLE) printf(" NS"); if (version >= 2) { cap = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CAP2, 4); if ((cap & PCIEM_CAP2_ARI) != 0) { ctl = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CTL2, 4); printf(" ARI %s", (ctl & PCIEM_CTL2_ARI) ? "enabled" : "disabled"); } } printf("\n max read %s", max_read_string((ctl & PCIEM_CTL_MAX_READ_REQUEST) >> 12)); cap = read_config(fd, &p->pc_sel, ptr + PCIER_LINK_CAP, 4); sta = read_config(fd, &p->pc_sel, ptr + PCIER_LINK_STA, 2); if (cap == 0 && sta == 0) return; printf("\n "); printf(" link x%d(x%d)", (sta & PCIEM_LINK_STA_WIDTH) >> 4, (cap & PCIEM_LINK_CAP_MAX_WIDTH) >> 4); if ((cap & PCIEM_LINK_CAP_MAX_WIDTH) != 0) { printf(" speed %s(%s)", (sta & PCIEM_LINK_STA_WIDTH) == 0 ? "0.0" : link_speed_string(sta & PCIEM_LINK_STA_SPEED), link_speed_string(cap & PCIEM_LINK_CAP_MAX_SPEED)); } if ((cap & PCIEM_LINK_CAP_ASPM) != 0) { ctl = read_config(fd, &p->pc_sel, ptr + PCIER_LINK_CTL, 2); printf(" ASPM %s(%s)", aspm_string(ctl & PCIEM_LINK_CTL_ASPMC), aspm_string((cap & PCIEM_LINK_CAP_ASPM) >> 10)); } if ((cap & PCIEM_LINK_CAP_CLOCK_PM) != 0) { ctl = read_config(fd, &p->pc_sel, ptr + PCIER_LINK_CTL, 2); printf(" ClockPM %s", (ctl & PCIEM_LINK_CTL_ECPM) ? "enabled" : "disabled"); } if (!(flags & PCIEM_FLAGS_SLOT)) return; cap = read_config(fd, &p->pc_sel, ptr + PCIER_SLOT_CAP, 4); sta = read_config(fd, &p->pc_sel, ptr + PCIER_SLOT_STA, 2); ctl = read_config(fd, &p->pc_sel, ptr + PCIER_SLOT_CTL, 2); printf("\n "); printf(" slot %d", (cap & PCIEM_SLOT_CAP_PSN) >> 19); printf(" power limit %d mW", slot_power(cap)); if (cap & PCIEM_SLOT_CAP_HPC) printf(" HotPlug(%s)", sta & PCIEM_SLOT_STA_PDS ? "present" : "empty"); if (cap & PCIEM_SLOT_CAP_HPS) printf(" surprise"); if (cap & PCIEM_SLOT_CAP_APB) printf(" Attn Button"); if (cap & PCIEM_SLOT_CAP_PCP) printf(" PC(%s)", ctl & PCIEM_SLOT_CTL_PCC ? "off" : "on"); if (cap & PCIEM_SLOT_CAP_MRLSP) printf(" MRL(%s)", sta & PCIEM_SLOT_STA_MRLSS ? "open" : "closed"); if (cap & PCIEM_SLOT_CAP_EIP) printf(" EI(%s)", sta & PCIEM_SLOT_STA_EIS ? "engaged" : "disengaged"); } static void cap_msix(int fd, struct pci_conf *p, uint8_t ptr) { uint32_t pba_offset, table_offset, val; int msgnum, pba_bar, table_bar; uint16_t ctrl; ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_CTRL, 2); msgnum = (ctrl & PCIM_MSIXCTRL_TABLE_SIZE) + 1; val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_TABLE, 4); table_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); table_offset = val & ~PCIM_MSIX_BIR_MASK; val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_PBA, 4); pba_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); pba_offset = val & ~PCIM_MSIX_BIR_MASK; printf("MSI-X supports %d message%s%s\n", msgnum, (msgnum == 1) ? "" : "s", (ctrl & PCIM_MSIXCTRL_MSIX_ENABLE) ? ", enabled" : ""); printf(" "); printf("Table in map 0x%x[0x%x], PBA in map 0x%x[0x%x]", table_bar, table_offset, pba_bar, pba_offset); } static void cap_sata(int fd __unused, struct pci_conf *p __unused, uint8_t ptr __unused) { printf("SATA Index-Data Pair"); } static void cap_pciaf(int fd, struct pci_conf *p, uint8_t ptr) { uint8_t cap; cap = read_config(fd, &p->pc_sel, ptr + PCIR_PCIAF_CAP, 1); printf("PCI Advanced Features:%s%s", cap & PCIM_PCIAFCAP_FLR ? " FLR" : "", cap & PCIM_PCIAFCAP_TP ? " TP" : ""); } static const char * ea_bei_to_name(int bei) { static const char *barstr[] = { "BAR0", "BAR1", "BAR2", "BAR3", "BAR4", "BAR5" }; static const char *vfbarstr[] = { "VFBAR0", "VFBAR1", "VFBAR2", "VFBAR3", "VFBAR4", "VFBAR5" }; if ((bei >= PCIM_EA_BEI_BAR_0) && (bei <= PCIM_EA_BEI_BAR_5)) return (barstr[bei - PCIM_EA_BEI_BAR_0]); if ((bei >= PCIM_EA_BEI_VF_BAR_0) && (bei <= PCIM_EA_BEI_VF_BAR_5)) return (vfbarstr[bei - PCIM_EA_BEI_VF_BAR_0]); switch (bei) { case PCIM_EA_BEI_BRIDGE: return "BRIDGE"; case PCIM_EA_BEI_ENI: return "ENI"; case PCIM_EA_BEI_ROM: return "ROM"; case PCIM_EA_BEI_RESERVED: default: return "RSVD"; } } static const char * ea_prop_to_name(uint8_t prop) { switch (prop) { case PCIM_EA_P_MEM: return "Non-Prefetchable Memory"; case PCIM_EA_P_MEM_PREFETCH: return "Prefetchable Memory"; case PCIM_EA_P_IO: return "I/O Space"; case PCIM_EA_P_VF_MEM_PREFETCH: return "VF Prefetchable Memory"; case PCIM_EA_P_VF_MEM: return "VF Non-Prefetchable Memory"; case PCIM_EA_P_BRIDGE_MEM: return "Bridge Non-Prefetchable Memory"; case PCIM_EA_P_BRIDGE_MEM_PREFETCH: return "Bridge Prefetchable Memory"; case PCIM_EA_P_BRIDGE_IO: return "Bridge I/O Space"; case PCIM_EA_P_MEM_RESERVED: return "Reserved Memory"; case PCIM_EA_P_IO_RESERVED: return "Reserved I/O Space"; case PCIM_EA_P_UNAVAILABLE: return "Unavailable"; default: return "Reserved"; } } static void cap_ea(int fd, struct pci_conf *p, uint8_t ptr) { int num_ent; int a, b; uint32_t bei; uint32_t val; int ent_size; uint32_t dw[4]; uint32_t flags, flags_pp, flags_sp; uint64_t base, max_offset; uint8_t fixed_sub_bus_nr, fixed_sec_bus_nr; /* Determine the number of entries */ num_ent = read_config(fd, &p->pc_sel, ptr + PCIR_EA_NUM_ENT, 2); num_ent &= PCIM_EA_NUM_ENT_MASK; printf("PCI Enhanced Allocation (%d entries)", num_ent); /* Find the first entry to care of */ ptr += PCIR_EA_FIRST_ENT; /* Print BUS numbers for bridges */ if ((p->pc_hdr & PCIM_HDRTYPE) == PCIM_HDRTYPE_BRIDGE) { val = read_config(fd, &p->pc_sel, ptr, 4); fixed_sec_bus_nr = PCIM_EA_SEC_NR(val); fixed_sub_bus_nr = PCIM_EA_SUB_NR(val); printf("\n\t\t BRIDGE, sec bus [%d], sub bus [%d]", fixed_sec_bus_nr, fixed_sub_bus_nr); ptr += 4; } for (a = 0; a < num_ent; a++) { /* Read a number of dwords in the entry */ val = read_config(fd, &p->pc_sel, ptr, 4); ptr += 4; ent_size = (val & PCIM_EA_ES); for (b = 0; b < ent_size; b++) { dw[b] = read_config(fd, &p->pc_sel, ptr, 4); ptr += 4; } flags = val; flags_pp = (flags & PCIM_EA_PP) >> PCIM_EA_PP_OFFSET; flags_sp = (flags & PCIM_EA_SP) >> PCIM_EA_SP_OFFSET; bei = (PCIM_EA_BEI & val) >> PCIM_EA_BEI_OFFSET; base = dw[0] & PCIM_EA_FIELD_MASK; max_offset = dw[1] | ~PCIM_EA_FIELD_MASK; b = 2; if (((dw[0] & PCIM_EA_IS_64) != 0) && (b < ent_size)) { base |= (uint64_t)dw[b] << 32UL; b++; } if (((dw[1] & PCIM_EA_IS_64) != 0) && (b < ent_size)) { max_offset |= (uint64_t)dw[b] << 32UL; b++; } printf("\n\t\t [%d] %s, %s, %s, base [0x%jx], size [0x%jx]" "\n\t\t\tPrimary properties [0x%x] (%s)" "\n\t\t\tSecondary properties [0x%x] (%s)", bei, ea_bei_to_name(bei), (flags & PCIM_EA_ENABLE ? "Enabled" : "Disabled"), (flags & PCIM_EA_WRITABLE ? "Writable" : "Read-only"), (uintmax_t)base, (uintmax_t)(max_offset + 1), flags_pp, ea_prop_to_name(flags_pp), flags_sp, ea_prop_to_name(flags_sp)); } } void list_caps(int fd, struct pci_conf *p, int level) { int express; uint16_t sta; uint8_t ptr, cap; /* Are capabilities present for this device? */ sta = read_config(fd, &p->pc_sel, PCIR_STATUS, 2); if (!(sta & PCIM_STATUS_CAPPRESENT)) return; cap_level = level; switch (p->pc_hdr & PCIM_HDRTYPE) { case PCIM_HDRTYPE_NORMAL: case PCIM_HDRTYPE_BRIDGE: ptr = PCIR_CAP_PTR; break; case PCIM_HDRTYPE_CARDBUS: ptr = PCIR_CAP_PTR_2; break; default: errx(1, "list_caps: bad header type"); } /* Walk the capability list. */ express = 0; ptr = read_config(fd, &p->pc_sel, ptr, 1); while (ptr != 0 && ptr != 0xff) { cap = read_config(fd, &p->pc_sel, ptr + PCICAP_ID, 1); printf(" cap %02x[%02x] = ", cap, ptr); switch (cap) { case PCIY_PMG: cap_power(fd, p, ptr); break; case PCIY_AGP: cap_agp(fd, p, ptr); break; case PCIY_VPD: cap_vpd(fd, p, ptr); break; case PCIY_MSI: cap_msi(fd, p, ptr); break; case PCIY_PCIX: cap_pcix(fd, p, ptr); break; case PCIY_HT: cap_ht(fd, p, ptr); break; case PCIY_VENDOR: cap_vendor(fd, p, ptr); break; case PCIY_DEBUG: cap_debug(fd, p, ptr); break; case PCIY_SUBVENDOR: cap_subvendor(fd, p, ptr); break; case PCIY_EXPRESS: express = 1; cap_express(fd, p, ptr); break; case PCIY_MSIX: cap_msix(fd, p, ptr); break; case PCIY_SATA: cap_sata(fd, p, ptr); break; case PCIY_PCIAF: cap_pciaf(fd, p, ptr); break; case PCIY_EA: cap_ea(fd, p, ptr); break; default: printf("unknown"); break; } printf("\n"); ptr = read_config(fd, &p->pc_sel, ptr + PCICAP_NEXTPTR, 1); } if (express) list_ecaps(fd, p); } /* From . */ static __inline uint32_t bitcount32(uint32_t x) { x = (x & 0x55555555) + ((x & 0xaaaaaaaa) >> 1); x = (x & 0x33333333) + ((x & 0xcccccccc) >> 2); x = (x + (x >> 4)) & 0x0f0f0f0f; x = (x + (x >> 8)); x = (x + (x >> 16)) & 0x000000ff; return (x); } static void ecap_aer(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint32_t sta, mask; printf("AER %d", ver); if (ver < 1) { printf("\n"); return; } sta = read_config(fd, &p->pc_sel, ptr + PCIR_AER_UC_STATUS, 4); mask = read_config(fd, &p->pc_sel, ptr + PCIR_AER_UC_SEVERITY, 4); printf(" %d fatal", bitcount32(sta & mask)); printf(" %d non-fatal", bitcount32(sta & ~mask)); sta = read_config(fd, &p->pc_sel, ptr + PCIR_AER_COR_STATUS, 4); printf(" %d corrected\n", bitcount32(sta)); } static void ecap_vc(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint32_t cap1; printf("VC %d", ver); if (ver < 1) { printf("\n"); return; } cap1 = read_config(fd, &p->pc_sel, ptr + PCIR_VC_CAP1, 4); printf(" max VC%d", cap1 & PCIM_VC_CAP1_EXT_COUNT); if ((cap1 & PCIM_VC_CAP1_LOWPRI_EXT_COUNT) != 0) printf(" lowpri VC0-VC%d", (cap1 & PCIM_VC_CAP1_LOWPRI_EXT_COUNT) >> 4); printf("\n"); } static void ecap_sernum(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint32_t high, low; printf("Serial %d", ver); if (ver < 1) { printf("\n"); return; } low = read_config(fd, &p->pc_sel, ptr + PCIR_SERIAL_LOW, 4); high = read_config(fd, &p->pc_sel, ptr + PCIR_SERIAL_HIGH, 4); printf(" %08x%08x\n", high, low); } static void ecap_vendor(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint32_t val, hdr; uint16_t nextptr, len; int i; val = read_config(fd, &p->pc_sel, ptr, 4); nextptr = PCI_EXTCAP_NEXTPTR(val); hdr = read_config(fd, &p->pc_sel, ptr + PCIR_VSEC_HEADER, 4); len = PCIR_VSEC_LENGTH(hdr); if (len == 0) { if (nextptr == 0) nextptr = 0x1000; len = nextptr - ptr; } printf("Vendor [%d] ID %04x Rev %d Length %d\n", ver, PCIR_VSEC_ID(hdr), PCIR_VSEC_REV(hdr), len); if ((ver < 1) || (cap_level <= 1)) return; for (i = 0; i < len; i += 4) { val = read_config(fd, &p->pc_sel, ptr + i, 4); if ((i % 16) == 0) printf(" "); printf("%02x %02x %02x %02x", val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff); if ((((i + 4) % 16) == 0 ) || ((i + 4) >= len)) printf("\n"); else printf(" "); } } static void ecap_sec_pcie(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint32_t val; printf("PCIe Sec %d", ver); if (ver < 1) { printf("\n"); return; } val = read_config(fd, &p->pc_sel, ptr + 8, 4); printf(" lane errors %#x\n", val); } static const char * check_enabled(int value) { return (value ? "enabled" : "disabled"); } static void ecap_sriov(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { const char *comma, *enabled; uint16_t iov_ctl, total_vfs, num_vfs, vf_offset, vf_stride, vf_did; uint32_t page_caps, page_size, page_shift, size; int i; printf("SR-IOV %d ", ver); iov_ctl = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_CTL, 2); printf("IOV %s, Memory Space %s, ARI %s\n", check_enabled(iov_ctl & PCIM_SRIOV_VF_EN), check_enabled(iov_ctl & PCIM_SRIOV_VF_MSE), check_enabled(iov_ctl & PCIM_SRIOV_ARI_EN)); total_vfs = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_TOTAL_VFS, 2); num_vfs = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_NUM_VFS, 2); printf(" "); printf("%d VFs configured out of %d supported\n", num_vfs, total_vfs); vf_offset = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_VF_OFF, 2); vf_stride = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_VF_STRIDE, 2); printf(" "); printf("First VF RID Offset 0x%04x, VF RID Stride 0x%04x\n", vf_offset, vf_stride); vf_did = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_VF_DID, 2); printf(" VF Device ID 0x%04x\n", vf_did); page_caps = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_PAGE_CAP, 4); page_size = read_config(fd, &p->pc_sel, ptr + PCIR_SRIOV_PAGE_SIZE, 4); printf(" "); printf("Page Sizes: "); comma = ""; while (page_caps != 0) { page_shift = ffs(page_caps) - 1; if (page_caps & page_size) enabled = " (enabled)"; else enabled = ""; size = (1 << (page_shift + PCI_SRIOV_BASE_PAGE_SHIFT)); printf("%s%d%s", comma, size, enabled); comma = ", "; page_caps &= ~(1 << page_shift); } printf("\n"); for (i = 0; i <= PCIR_MAX_BAR_0; i++) print_bar(fd, p, "iov bar ", ptr + PCIR_SRIOV_BAR(i)); } static const char * check_avail_and_state(u_int cap, u_int capbit, u_int ctl, u_int ctlbit) { if (cap & capbit) return (ctl & ctlbit ? "enabled" : "disabled"); else return "unavailable"; } static void ecap_acs(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver) { uint16_t acs_cap, acs_ctl; static const char *const acc[] = { "access enabled", "blocking enabled", "redirect enabled", "reserved" }; printf("ACS %d ", ver); if (ver != 1) { printf("\n"); return; } #define CHECK_AVAIL_STATE(bit) \ check_avail_and_state(acs_cap, bit, acs_ctl, bit##_ENABLE) acs_cap = read_config(fd, &p->pc_sel, ptr + PCIR_ACS_CAP, 2); acs_ctl = read_config(fd, &p->pc_sel, ptr + PCIR_ACS_CTL, 2); printf("Source Validation %s, Translation Blocking %s\n", CHECK_AVAIL_STATE(PCIM_ACS_SOURCE_VALIDATION), CHECK_AVAIL_STATE(PCIM_ACS_TRANSLATION_BLOCKING)); printf(" "); printf("P2P Req Redirect %s, P2P Cmpl Redirect %s\n", CHECK_AVAIL_STATE(PCIM_ACS_P2P_REQ_REDIRECT), CHECK_AVAIL_STATE(PCIM_ACS_P2P_CMP_REDIRECT)); printf(" "); printf("P2P Upstream Forwarding %s, P2P Egress Control %s\n", CHECK_AVAIL_STATE(PCIM_ACS_P2P_UPSTREAM_FORWARDING), CHECK_AVAIL_STATE(PCIM_ACS_P2P_EGRESS_CTL)); printf(" "); printf("P2P Direct Translated %s, Enhanced Capability %s\n", CHECK_AVAIL_STATE(PCIM_ACS_P2P_DIRECT_TRANSLATED), acs_ctl & PCIM_ACS_ENHANCED_CAP ? "available" : "unavailable"); #undef CHECK_AVAIL_STATE if (acs_cap & PCIM_ACS_ENHANCED_CAP) { printf(" "); printf("I/O Req Blocking %s, Unclaimed Req Redirect Control %s\n", check_enabled(acs_ctl & PCIM_ACS_IO_REQ_BLOCKING_ENABLE), check_enabled(acs_ctl & PCIM_ACS_UNCLAIMED_REQ_REDIRECT_CTL)); printf(" "); printf("DSP BAR %s, USP BAR %s\n", acc[(acs_cap & PCIM_ACS_DSP_MEM_TGT_ACC_CTL) >> 8], acc[(acs_cap & PCIM_ACS_USP_MEM_TGT_ACC_CTL) >> 10]); } } static struct { uint16_t id; const char *name; } ecap_names[] = { { PCIZ_AER, "AER" }, { PCIZ_VC, "Virtual Channel" }, { PCIZ_SERNUM, "Device Serial Number" }, { PCIZ_PWRBDGT, "Power Budgeting" }, { PCIZ_RCLINK_DCL, "Root Complex Link Declaration" }, { PCIZ_RCLINK_CTL, "Root Complex Internal Link Control" }, { PCIZ_RCEC_ASSOC, "Root Complex Event Collector ASsociation" }, { PCIZ_MFVC, "MFVC" }, { PCIZ_VC2, "Virtual Channel 2" }, { PCIZ_RCRB, "RCRB" }, { PCIZ_CAC, "Configuration Access Correction" }, { PCIZ_ACS, "ACS" }, { PCIZ_ARI, "ARI" }, { PCIZ_ATS, "ATS" }, { PCIZ_SRIOV, "SRIOV" }, { PCIZ_MRIOV, "MRIOV" }, { PCIZ_MULTICAST, "Multicast" }, { PCIZ_PAGE_REQ, "Page Page Request" }, { PCIZ_AMD, "AMD proprietary "}, { PCIZ_RESIZE_BAR, "Resizable BAR" }, { PCIZ_DPA, "DPA" }, { PCIZ_TPH_REQ, "TPH Requester" }, { PCIZ_LTR, "LTR" }, { PCIZ_SEC_PCIE, "Secondary PCI Express" }, { PCIZ_PMUX, "Protocol Multiplexing" }, { PCIZ_PASID, "Process Address Space ID" }, { PCIZ_LN_REQ, "LN Requester" }, { PCIZ_DPC, "Downstream Port Containment" }, { PCIZ_L1PM, "L1 PM Substates" }, { PCIZ_PTM, "Precision Time Measurement" }, { PCIZ_M_PCIE, "PCIe over M-PHY" }, { PCIZ_FRS, "FRS Queuing" }, { PCIZ_RTR, "Readiness Time Reporting" }, { PCIZ_DVSEC, "Designated Vendor-Specific" }, { PCIZ_VF_REBAR, "VF Resizable BAR" }, { PCIZ_DLNK, "Data Link Feature" }, { PCIZ_16GT, "Physical Layer 16.0 GT/s" }, { PCIZ_LMR, "Lane Margining at Receiver" }, { PCIZ_HIER_ID, "Hierarchy ID" }, { PCIZ_NPEM, "Native PCIe Enclosure Management" }, { PCIZ_PL32, "Physical Layer 32.0 GT/s" }, { PCIZ_AP, "Alternate Protocol" }, { PCIZ_SFI, "System Firmware Intermediary" }, { 0, NULL } }; static void list_ecaps(int fd, struct pci_conf *p) { const char *name; uint32_t ecap; uint16_t ptr; int i; ptr = PCIR_EXTCAP; ecap = read_config(fd, &p->pc_sel, ptr, 4); if (ecap == 0xffffffff || ecap == 0) return; for (;;) { printf(" ecap %04x[%03x] = ", PCI_EXTCAP_ID(ecap), ptr); switch (PCI_EXTCAP_ID(ecap)) { case PCIZ_AER: ecap_aer(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_VC: ecap_vc(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_SERNUM: ecap_sernum(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_VENDOR: ecap_vendor(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_SEC_PCIE: ecap_sec_pcie(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_SRIOV: ecap_sriov(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; case PCIZ_ACS: ecap_acs(fd, p, ptr, PCI_EXTCAP_VER(ecap)); break; default: name = "unknown"; for (i = 0; ecap_names[i].name != NULL; i++) if (ecap_names[i].id == PCI_EXTCAP_ID(ecap)) { name = ecap_names[i].name; break; } printf("%s %d\n", name, PCI_EXTCAP_VER(ecap)); break; } ptr = PCI_EXTCAP_NEXTPTR(ecap); if (ptr == 0) break; ecap = read_config(fd, &p->pc_sel, ptr, 4); } } /* Find offset of a specific capability. Returns 0 on failure. */ uint8_t pci_find_cap(int fd, struct pci_conf *p, uint8_t id) { uint16_t sta; uint8_t ptr, cap; /* Are capabilities present for this device? */ sta = read_config(fd, &p->pc_sel, PCIR_STATUS, 2); if (!(sta & PCIM_STATUS_CAPPRESENT)) return (0); switch (p->pc_hdr & PCIM_HDRTYPE) { case PCIM_HDRTYPE_NORMAL: case PCIM_HDRTYPE_BRIDGE: ptr = PCIR_CAP_PTR; break; case PCIM_HDRTYPE_CARDBUS: ptr = PCIR_CAP_PTR_2; break; default: return (0); } ptr = read_config(fd, &p->pc_sel, ptr, 1); while (ptr != 0 && ptr != 0xff) { cap = read_config(fd, &p->pc_sel, ptr + PCICAP_ID, 1); if (cap == id) return (ptr); ptr = read_config(fd, &p->pc_sel, ptr + PCICAP_NEXTPTR, 1); } return (0); } /* Find offset of a specific extended capability. Returns 0 on failure. */ uint16_t pcie_find_cap(int fd, struct pci_conf *p, uint16_t id) { uint32_t ecap; uint16_t ptr; ptr = PCIR_EXTCAP; ecap = read_config(fd, &p->pc_sel, ptr, 4); if (ecap == 0xffffffff || ecap == 0) return (0); for (;;) { if (PCI_EXTCAP_ID(ecap) == id) return (ptr); ptr = PCI_EXTCAP_NEXTPTR(ecap); if (ptr == 0) break; ecap = read_config(fd, &p->pc_sel, ptr, 4); } return (0); } diff --git a/usr.sbin/pciconf/err.c b/usr.sbin/pciconf/err.c index fc5cdbef7f70..dca0ca1cea87 100644 --- a/usr.sbin/pciconf/err.c +++ b/usr.sbin/pciconf/err.c @@ -1,175 +1,170 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012 Hudson River Trading LLC * Written by: John H. Baldwin * 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. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include "pciconf.h" struct bit_table { uint32_t mask; const char *desc; }; /* Error indicators in the PCI status register (PCIR_STATUS). */ static struct bit_table pci_status[] = { { PCIM_STATUS_MDPERR, "Master Data Parity Error" }, { PCIM_STATUS_STABORT, "Sent Target-Abort" }, { PCIM_STATUS_RTABORT, "Received Target-Abort" }, { PCIM_STATUS_RMABORT, "Received Master-Abort" }, { PCIM_STATUS_SERR, "Signalled System Error" }, { PCIM_STATUS_PERR, "Detected Parity Error" }, { 0, NULL }, }; /* Valid error indicator bits in PCIR_STATUS. */ #define PCI_ERRORS (PCIM_STATUS_MDPERR | PCIM_STATUS_STABORT | \ PCIM_STATUS_RTABORT | PCIM_STATUS_RMABORT | \ PCIM_STATUS_SERR | PCIM_STATUS_PERR) /* Error indicators in the PCI-Express device status register. */ static struct bit_table pcie_device_status[] = { { PCIEM_STA_CORRECTABLE_ERROR, "Correctable Error Detected" }, { PCIEM_STA_NON_FATAL_ERROR, "Non-Fatal Error Detected" }, { PCIEM_STA_FATAL_ERROR, "Fatal Error Detected" }, { PCIEM_STA_UNSUPPORTED_REQ, "Unsupported Request Detected" }, { 0, NULL }, }; /* Valid error indicator bits in the PCI-Express device status register. */ #define PCIE_ERRORS (PCIEM_STA_CORRECTABLE_ERROR | \ PCIEM_STA_NON_FATAL_ERROR | \ PCIEM_STA_FATAL_ERROR | \ PCIEM_STA_UNSUPPORTED_REQ) /* AER Uncorrected errors. */ static struct bit_table aer_uc[] = { { PCIM_AER_UC_TRAINING_ERROR, "Link Training Error" }, { PCIM_AER_UC_DL_PROTOCOL_ERROR, "Data Link Protocol Error" }, { PCIM_AER_UC_SURPRISE_LINK_DOWN, "Surprise Link Down Error" }, { PCIM_AER_UC_POISONED_TLP, "Poisoned TLP" }, { PCIM_AER_UC_FC_PROTOCOL_ERROR, "Flow Control Protocol Error" }, { PCIM_AER_UC_COMPLETION_TIMEOUT, "Completion Timeout" }, { PCIM_AER_UC_COMPLETER_ABORT, "Completer Abort" }, { PCIM_AER_UC_UNEXPECTED_COMPLETION, "Unexpected Completion" }, { PCIM_AER_UC_RECEIVER_OVERFLOW, "Receiver Overflow Error" }, { PCIM_AER_UC_MALFORMED_TLP, "Malformed TLP" }, { PCIM_AER_UC_ECRC_ERROR, "ECRC Error" }, { PCIM_AER_UC_UNSUPPORTED_REQUEST, "Unsupported Request" }, { PCIM_AER_UC_ACS_VIOLATION, "ACS Violation" }, { PCIM_AER_UC_INTERNAL_ERROR, "Uncorrectable Internal Error" }, { PCIM_AER_UC_MC_BLOCKED_TLP, "MC Blocked TLP" }, { PCIM_AER_UC_ATOMIC_EGRESS_BLK, "AtomicOp Egress Blocked" }, { PCIM_AER_UC_TLP_PREFIX_BLOCKED, "TLP Prefix Blocked Error" }, { 0, NULL }, }; /* AER Corrected errors. */ static struct bit_table aer_cor[] = { { PCIM_AER_COR_RECEIVER_ERROR, "Receiver Error" }, { PCIM_AER_COR_BAD_TLP, "Bad TLP" }, { PCIM_AER_COR_BAD_DLLP, "Bad DLLP" }, { PCIM_AER_COR_REPLAY_ROLLOVER, "REPLAY_NUM Rollover" }, { PCIM_AER_COR_REPLAY_TIMEOUT, "Replay Timer Timeout" }, { PCIM_AER_COR_ADVISORY_NF_ERROR, "Advisory Non-Fatal Error" }, { PCIM_AER_COR_INTERNAL_ERROR, "Corrected Internal Error" }, { PCIM_AER_COR_HEADER_LOG_OVFLOW, "Header Log Overflow" }, { 0, NULL }, }; static void print_bits(const char *header, struct bit_table *table, uint32_t mask) { int first; first = 1; for (; table->desc != NULL; table++) if (mask & table->mask) { if (first) { printf("%14s = ", header); first = 0; } else printf(" "); printf("%s\n", table->desc); mask &= ~table->mask; } if (mask != 0) { if (first) printf("%14s = ", header); else printf(" "); printf("Unknown: 0x%08x\n", mask); } } void list_errors(int fd, struct pci_conf *p) { uint32_t mask, severity; uint16_t sta, aer; uint8_t pcie; /* First check for standard PCI errors. */ sta = read_config(fd, &p->pc_sel, PCIR_STATUS, 2); print_bits("PCI errors", pci_status, sta & PCI_ERRORS); /* See if this is a PCI-express device. */ pcie = pci_find_cap(fd, p, PCIY_EXPRESS); if (pcie == 0) return; /* Check for PCI-e errors. */ sta = read_config(fd, &p->pc_sel, pcie + PCIER_DEVICE_STA, 2); print_bits("PCI-e errors", pcie_device_status, sta & PCIE_ERRORS); /* See if this device supports AER. */ aer = pcie_find_cap(fd, p, PCIZ_AER); if (aer == 0) return; /* Check for uncorrected errors. */ mask = read_config(fd, &p->pc_sel, aer + PCIR_AER_UC_STATUS, 4); severity = read_config(fd, &p->pc_sel, aer + PCIR_AER_UC_SEVERITY, 4); print_bits("Fatal", aer_uc, mask & severity); print_bits("Non-fatal", aer_uc, mask & ~severity); /* Check for corrected errors. */ mask = read_config(fd, &p->pc_sel, aer + PCIR_AER_COR_STATUS, 4); print_bits("Corrected", aer_cor, mask); } diff --git a/usr.sbin/pciconf/pciconf.c b/usr.sbin/pciconf/pciconf.c index 946adc080720..83ea50efb183 100644 --- a/usr.sbin/pciconf/pciconf.c +++ b/usr.sbin/pciconf/pciconf.c @@ -1,1208 +1,1203 @@ /* * Copyright 1996 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "pciconf.h" struct pci_device_info { TAILQ_ENTRY(pci_device_info) link; int id; char *desc; }; struct pci_vendor_info { TAILQ_ENTRY(pci_vendor_info) link; TAILQ_HEAD(,pci_device_info) devs; int id; char *desc; }; static TAILQ_HEAD(,pci_vendor_info) pci_vendors; static struct pcisel getsel(const char *str); static void list_bridge(int fd, struct pci_conf *p); static void list_bars(int fd, struct pci_conf *p); static void list_devs(const char *name, int verbose, int bars, int bridge, int caps, int errors, int vpd, int listmode); static void list_verbose(struct pci_conf *p); static void list_vpd(int fd, struct pci_conf *p); static const char *guess_class(struct pci_conf *p); static const char *guess_subclass(struct pci_conf *p); static int load_vendors(void); static void readit(const char *, const char *, int); static void writeit(const char *, const char *, const char *, int); static void chkattached(const char *); static void dump_bar(const char *name, const char *reg, const char *bar_start, const char *bar_count, int width, int verbose); static int exitstatus = 0; static void usage(void) { fprintf(stderr, "%s", "usage: pciconf -l [-BbcevV] [device]\n" " pciconf -a device\n" " pciconf -r [-b | -h] device addr[:addr2]\n" " pciconf -w [-b | -h] device addr value\n" " pciconf -D [-b | -h | -x] device bar [start [count]]" "\n"); exit(1); } int main(int argc, char **argv) { int c, width; int listmode, readmode, writemode, attachedmode, dumpbarmode; int bars, bridge, caps, errors, verbose, vpd; listmode = readmode = writemode = attachedmode = dumpbarmode = 0; bars = bridge = caps = errors = verbose = vpd= 0; width = 4; while ((c = getopt(argc, argv, "aBbcDehlrwVv")) != -1) { switch(c) { case 'a': attachedmode = 1; break; case 'B': bridge = 1; break; case 'b': bars = 1; width = 1; break; case 'c': caps++; break; case 'D': dumpbarmode = 1; break; case 'e': errors = 1; break; case 'h': width = 2; break; case 'l': listmode++; break; case 'r': readmode = 1; break; case 'w': writemode = 1; break; case 'v': verbose = 1; break; case 'V': vpd = 1; break; case 'x': width = 8; break; default: usage(); } } if ((listmode && optind >= argc + 1) || (writemode && optind + 3 != argc) || (readmode && optind + 2 != argc) || (attachedmode && optind + 1 != argc) || (dumpbarmode && (optind + 2 > argc || optind + 4 < argc)) || (width == 8 && !dumpbarmode)) usage(); if (listmode) { list_devs(optind + 1 == argc ? argv[optind] : NULL, verbose, bars, bridge, caps, errors, vpd, listmode); } else if (attachedmode) { chkattached(argv[optind]); } else if (readmode) { readit(argv[optind], argv[optind + 1], width); } else if (writemode) { writeit(argv[optind], argv[optind + 1], argv[optind + 2], width); } else if (dumpbarmode) { dump_bar(argv[optind], argv[optind + 1], optind + 2 < argc ? argv[optind + 2] : NULL, optind + 3 < argc ? argv[optind + 3] : NULL, width, verbose); } else { usage(); } return (exitstatus); } static void list_devs(const char *name, int verbose, int bars, int bridge, int caps, int errors, int vpd, int listmode) { int fd; struct pci_conf_io pc; struct pci_conf conf[255], *p; struct pci_match_conf patterns[1]; int none_count = 0; if (verbose) load_vendors(); fd = open(_PATH_DEVPCI, (bridge || caps || errors) ? O_RDWR : O_RDONLY, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); bzero(&pc, sizeof(struct pci_conf_io)); pc.match_buf_len = sizeof(conf); pc.matches = conf; if (name != NULL) { bzero(&patterns, sizeof(patterns)); patterns[0].pc_sel = getsel(name); patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN | PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC; pc.num_patterns = 1; pc.pat_buf_len = sizeof(patterns); pc.patterns = patterns; } do { if (ioctl(fd, PCIOCGETCONF, &pc) == -1) err(1, "ioctl(PCIOCGETCONF)"); /* * 255 entries should be more than enough for most people, * but if someone has more devices, and then changes things * around between ioctls, we'll do the cheesy thing and * just bail. The alternative would be to go back to the * beginning of the list, and print things twice, which may * not be desirable. */ if (pc.status == PCI_GETCONF_LIST_CHANGED) { warnx("PCI device list changed, please try again"); exitstatus = 1; close(fd); return; } else if (pc.status == PCI_GETCONF_ERROR) { warnx("error returned from PCIOCGETCONF ioctl"); exitstatus = 1; close(fd); return; } if (listmode == 2) printf("drv\tselector\tclass rev hdr " "vendor device subven subdev\n"); for (p = conf; p < &conf[pc.num_matches]; p++) { if (listmode == 2) printf("%s%d@pci%d:%d:%d:%d:" "\t%06x %02x %02x " "%04x %04x %04x %04x\n", *p->pd_name ? p->pd_name : "none", *p->pd_name ? (int)p->pd_unit : none_count++, p->pc_sel.pc_domain, p->pc_sel.pc_bus, p->pc_sel.pc_dev, p->pc_sel.pc_func, (p->pc_class << 16) | (p->pc_subclass << 8) | p->pc_progif, p->pc_revid, p->pc_hdr, p->pc_vendor, p->pc_device, p->pc_subvendor, p->pc_subdevice); else printf("%s%d@pci%d:%d:%d:%d:" "\tclass=0x%06x rev=0x%02x hdr=0x%02x " "vendor=0x%04x device=0x%04x " "subvendor=0x%04x subdevice=0x%04x\n", *p->pd_name ? p->pd_name : "none", *p->pd_name ? (int)p->pd_unit : none_count++, p->pc_sel.pc_domain, p->pc_sel.pc_bus, p->pc_sel.pc_dev, p->pc_sel.pc_func, (p->pc_class << 16) | (p->pc_subclass << 8) | p->pc_progif, p->pc_revid, p->pc_hdr, p->pc_vendor, p->pc_device, p->pc_subvendor, p->pc_subdevice); if (verbose) list_verbose(p); if (bars) list_bars(fd, p); if (bridge) list_bridge(fd, p); if (caps) list_caps(fd, p, caps); if (errors) list_errors(fd, p); if (vpd) list_vpd(fd, p); } } while (pc.status == PCI_GETCONF_MORE_DEVS); close(fd); } static void print_bus_range(int fd, struct pci_conf *p, int secreg, int subreg) { uint8_t secbus, subbus; secbus = read_config(fd, &p->pc_sel, secreg, 1); subbus = read_config(fd, &p->pc_sel, subreg, 1); printf(" bus range = %u-%u\n", secbus, subbus); } static void print_window(int reg, const char *type, int range, uint64_t base, uint64_t limit) { printf(" window[%02x] = type %s, range %2d, addr %#jx-%#jx, %s\n", reg, type, range, (uintmax_t)base, (uintmax_t)limit, base < limit ? "enabled" : "disabled"); } static void print_special_decode(bool isa, bool vga, bool subtractive) { bool comma; if (isa || vga || subtractive) { comma = false; printf(" decode = "); if (isa) { printf("ISA"); comma = true; } if (vga) { printf("%sVGA", comma ? ", " : ""); comma = true; } if (subtractive) printf("%ssubtractive", comma ? ", " : ""); printf("\n"); } } static void print_bridge_windows(int fd, struct pci_conf *p) { uint64_t base, limit; uint32_t val; uint16_t bctl; bool subtractive; int range; /* * XXX: This assumes that a window with a base and limit of 0 * is not implemented. In theory a window might be programmed * at the smallest size with a base of 0, but those do not seem * common in practice. */ val = read_config(fd, &p->pc_sel, PCIR_IOBASEL_1, 1); if (val != 0 || read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1) != 0) { if ((val & PCIM_BRIO_MASK) == PCIM_BRIO_32) { base = PCI_PPBIOBASE( read_config(fd, &p->pc_sel, PCIR_IOBASEH_1, 2), val); limit = PCI_PPBIOLIMIT( read_config(fd, &p->pc_sel, PCIR_IOLIMITH_1, 2), read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1)); range = 32; } else { base = PCI_PPBIOBASE(0, val); limit = PCI_PPBIOLIMIT(0, read_config(fd, &p->pc_sel, PCIR_IOLIMITL_1, 1)); range = 16; } print_window(PCIR_IOBASEL_1, "I/O Port", range, base, limit); } base = PCI_PPBMEMBASE(0, read_config(fd, &p->pc_sel, PCIR_MEMBASE_1, 2)); limit = PCI_PPBMEMLIMIT(0, read_config(fd, &p->pc_sel, PCIR_MEMLIMIT_1, 2)); print_window(PCIR_MEMBASE_1, "Memory", 32, base, limit); val = read_config(fd, &p->pc_sel, PCIR_PMBASEL_1, 2); if (val != 0 || read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2) != 0) { if ((val & PCIM_BRPM_MASK) == PCIM_BRPM_64) { base = PCI_PPBMEMBASE( read_config(fd, &p->pc_sel, PCIR_PMBASEH_1, 4), val); limit = PCI_PPBMEMLIMIT( read_config(fd, &p->pc_sel, PCIR_PMLIMITH_1, 4), read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2)); range = 64; } else { base = PCI_PPBMEMBASE(0, val); limit = PCI_PPBMEMLIMIT(0, read_config(fd, &p->pc_sel, PCIR_PMLIMITL_1, 2)); range = 32; } print_window(PCIR_PMBASEL_1, "Prefetchable Memory", range, base, limit); } /* * XXX: This list of bridges that are subtractive but do not set * progif to indicate it is copied from pci_pci.c. */ subtractive = p->pc_progif == PCIP_BRIDGE_PCI_SUBTRACTIVE; switch (p->pc_device << 16 | p->pc_vendor) { case 0xa002177d: /* Cavium ThunderX */ case 0x124b8086: /* Intel 82380FB Mobile */ case 0x060513d7: /* Toshiba ???? */ subtractive = true; } if (p->pc_vendor == 0x8086 && (p->pc_device & 0xff00) == 0x2400) subtractive = true; bctl = read_config(fd, &p->pc_sel, PCIR_BRIDGECTL_1, 2); print_special_decode(bctl & PCIB_BCR_ISA_ENABLE, bctl & PCIB_BCR_VGA_ENABLE, subtractive); } static void print_cardbus_mem_window(int fd, struct pci_conf *p, int basereg, int limitreg, bool prefetch) { print_window(basereg, prefetch ? "Prefetchable Memory" : "Memory", 32, PCI_CBBMEMBASE(read_config(fd, &p->pc_sel, basereg, 4)), PCI_CBBMEMLIMIT(read_config(fd, &p->pc_sel, limitreg, 4))); } static void print_cardbus_io_window(int fd, struct pci_conf *p, int basereg, int limitreg) { uint32_t base, limit; uint32_t val; int range; val = read_config(fd, &p->pc_sel, basereg, 2); if ((val & PCIM_CBBIO_MASK) == PCIM_CBBIO_32) { base = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, basereg, 4)); limit = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, limitreg, 4)); range = 32; } else { base = PCI_CBBIOBASE(val); limit = PCI_CBBIOBASE(read_config(fd, &p->pc_sel, limitreg, 2)); range = 16; } print_window(basereg, "I/O Port", range, base, limit); } static void print_cardbus_windows(int fd, struct pci_conf *p) { uint16_t bctl; bctl = read_config(fd, &p->pc_sel, PCIR_BRIDGECTL_2, 2); print_cardbus_mem_window(fd, p, PCIR_MEMBASE0_2, PCIR_MEMLIMIT0_2, bctl & CBB_BCR_PREFETCH_0_ENABLE); print_cardbus_mem_window(fd, p, PCIR_MEMBASE1_2, PCIR_MEMLIMIT1_2, bctl & CBB_BCR_PREFETCH_1_ENABLE); print_cardbus_io_window(fd, p, PCIR_IOBASE0_2, PCIR_IOLIMIT0_2); print_cardbus_io_window(fd, p, PCIR_IOBASE1_2, PCIR_IOLIMIT1_2); print_special_decode(bctl & CBB_BCR_ISA_ENABLE, bctl & CBB_BCR_VGA_ENABLE, false); } static void list_bridge(int fd, struct pci_conf *p) { switch (p->pc_hdr & PCIM_HDRTYPE) { case PCIM_HDRTYPE_BRIDGE: print_bus_range(fd, p, PCIR_SECBUS_1, PCIR_SUBBUS_1); print_bridge_windows(fd, p); break; case PCIM_HDRTYPE_CARDBUS: print_bus_range(fd, p, PCIR_SECBUS_2, PCIR_SUBBUS_2); print_cardbus_windows(fd, p); break; } } static void list_bars(int fd, struct pci_conf *p) { int i, max; switch (p->pc_hdr & PCIM_HDRTYPE) { case PCIM_HDRTYPE_NORMAL: max = PCIR_MAX_BAR_0; break; case PCIM_HDRTYPE_BRIDGE: max = PCIR_MAX_BAR_1; break; case PCIM_HDRTYPE_CARDBUS: max = PCIR_MAX_BAR_2; break; default: return; } for (i = 0; i <= max; i++) print_bar(fd, p, "bar ", PCIR_BAR(i)); } void print_bar(int fd, struct pci_conf *p, const char *label, uint16_t bar_offset) { uint64_t base; const char *type; struct pci_bar_io bar; int range; bar.pbi_sel = p->pc_sel; bar.pbi_reg = bar_offset; if (ioctl(fd, PCIOCGETBAR, &bar) < 0) return; if (PCI_BAR_IO(bar.pbi_base)) { type = "I/O Port"; range = 32; base = bar.pbi_base & PCIM_BAR_IO_BASE; } else { if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) type = "Prefetchable Memory"; else type = "Memory"; switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) { case PCIM_BAR_MEM_32: range = 32; break; case PCIM_BAR_MEM_1MB: range = 20; break; case PCIM_BAR_MEM_64: range = 64; break; default: range = -1; } base = bar.pbi_base & ~((uint64_t)0xf); } printf(" %s[%02x] = type %s, range %2d, base %#jx, ", label, bar_offset, type, range, (uintmax_t)base); printf("size %ju, %s\n", (uintmax_t)bar.pbi_length, bar.pbi_enabled ? "enabled" : "disabled"); } static void list_verbose(struct pci_conf *p) { struct pci_vendor_info *vi; struct pci_device_info *di; const char *dp; TAILQ_FOREACH(vi, &pci_vendors, link) { if (vi->id == p->pc_vendor) { printf(" vendor = '%s'\n", vi->desc); break; } } if (vi == NULL) { di = NULL; } else { TAILQ_FOREACH(di, &vi->devs, link) { if (di->id == p->pc_device) { printf(" device = '%s'\n", di->desc); break; } } } if ((dp = guess_class(p)) != NULL) printf(" class = %s\n", dp); if ((dp = guess_subclass(p)) != NULL) printf(" subclass = %s\n", dp); } static void list_vpd(int fd, struct pci_conf *p) { struct pci_list_vpd_io list; struct pci_vpd_element *vpd, *end; list.plvi_sel = p->pc_sel; list.plvi_len = 0; list.plvi_data = NULL; if (ioctl(fd, PCIOCLISTVPD, &list) < 0 || list.plvi_len == 0) return; list.plvi_data = malloc(list.plvi_len); if (ioctl(fd, PCIOCLISTVPD, &list) < 0) { free(list.plvi_data); return; } vpd = list.plvi_data; end = (struct pci_vpd_element *)((char *)vpd + list.plvi_len); for (; vpd < end; vpd = PVE_NEXT(vpd)) { if (vpd->pve_flags == PVE_FLAG_IDENT) { printf(" VPD ident = '%.*s'\n", (int)vpd->pve_datalen, vpd->pve_data); continue; } /* Ignore the checksum keyword. */ if (!(vpd->pve_flags & PVE_FLAG_RW) && memcmp(vpd->pve_keyword, "RV", 2) == 0) continue; /* Ignore remaining read-write space. */ if (vpd->pve_flags & PVE_FLAG_RW && memcmp(vpd->pve_keyword, "RW", 2) == 0) continue; /* Handle extended capability keyword. */ if (!(vpd->pve_flags & PVE_FLAG_RW) && memcmp(vpd->pve_keyword, "CP", 2) == 0) { printf(" VPD ro CP = ID %02x in map 0x%x[0x%x]\n", (unsigned int)vpd->pve_data[0], PCIR_BAR((unsigned int)vpd->pve_data[1]), (unsigned int)vpd->pve_data[3] << 8 | (unsigned int)vpd->pve_data[2]); continue; } /* Remaining keywords should all have ASCII values. */ printf(" VPD %s %c%c = '%.*s'\n", vpd->pve_flags & PVE_FLAG_RW ? "rw" : "ro", vpd->pve_keyword[0], vpd->pve_keyword[1], (int)vpd->pve_datalen, vpd->pve_data); } free(list.plvi_data); } /* * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c. */ static struct { int class; int subclass; const char *desc; } pci_nomatch_tab[] = { {PCIC_OLD, -1, "old"}, {PCIC_OLD, PCIS_OLD_NONVGA, "non-VGA display device"}, {PCIC_OLD, PCIS_OLD_VGA, "VGA-compatible display device"}, {PCIC_STORAGE, -1, "mass storage"}, {PCIC_STORAGE, PCIS_STORAGE_SCSI, "SCSI"}, {PCIC_STORAGE, PCIS_STORAGE_IDE, "ATA"}, {PCIC_STORAGE, PCIS_STORAGE_FLOPPY, "floppy disk"}, {PCIC_STORAGE, PCIS_STORAGE_IPI, "IPI"}, {PCIC_STORAGE, PCIS_STORAGE_RAID, "RAID"}, {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, "ATA (ADMA)"}, {PCIC_STORAGE, PCIS_STORAGE_SATA, "SATA"}, {PCIC_STORAGE, PCIS_STORAGE_SAS, "SAS"}, {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM"}, {PCIC_STORAGE, PCIS_STORAGE_UFS, "UFS"}, {PCIC_NETWORK, -1, "network"}, {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"}, {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"}, {PCIC_NETWORK, PCIS_NETWORK_FDDI, "fddi"}, {PCIC_NETWORK, PCIS_NETWORK_ATM, "ATM"}, {PCIC_NETWORK, PCIS_NETWORK_ISDN, "ISDN"}, {PCIC_NETWORK, PCIS_NETWORK_WORLDFIP, "WorldFip"}, {PCIC_NETWORK, PCIS_NETWORK_PICMG, "PICMG"}, {PCIC_NETWORK, PCIS_NETWORK_INFINIBAND, "InfiniBand"}, {PCIC_NETWORK, PCIS_NETWORK_HFC, "host fabric"}, {PCIC_DISPLAY, -1, "display"}, {PCIC_DISPLAY, PCIS_DISPLAY_VGA, "VGA"}, {PCIC_DISPLAY, PCIS_DISPLAY_XGA, "XGA"}, {PCIC_DISPLAY, PCIS_DISPLAY_3D, "3D"}, {PCIC_MULTIMEDIA, -1, "multimedia"}, {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, "video"}, {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, "audio"}, {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_TELE, "telephony"}, {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_HDA, "HDA"}, {PCIC_MEMORY, -1, "memory"}, {PCIC_MEMORY, PCIS_MEMORY_RAM, "RAM"}, {PCIC_MEMORY, PCIS_MEMORY_FLASH, "flash"}, {PCIC_BRIDGE, -1, "bridge"}, {PCIC_BRIDGE, PCIS_BRIDGE_HOST, "HOST-PCI"}, {PCIC_BRIDGE, PCIS_BRIDGE_ISA, "PCI-ISA"}, {PCIC_BRIDGE, PCIS_BRIDGE_EISA, "PCI-EISA"}, {PCIC_BRIDGE, PCIS_BRIDGE_MCA, "PCI-MCA"}, {PCIC_BRIDGE, PCIS_BRIDGE_PCI, "PCI-PCI"}, {PCIC_BRIDGE, PCIS_BRIDGE_PCMCIA, "PCI-PCMCIA"}, {PCIC_BRIDGE, PCIS_BRIDGE_NUBUS, "PCI-NuBus"}, {PCIC_BRIDGE, PCIS_BRIDGE_CARDBUS, "PCI-CardBus"}, {PCIC_BRIDGE, PCIS_BRIDGE_RACEWAY, "PCI-RACEway"}, {PCIC_BRIDGE, PCIS_BRIDGE_PCI_TRANSPARENT, "Semi-transparent PCI-to-PCI"}, {PCIC_BRIDGE, PCIS_BRIDGE_INFINIBAND, "InfiniBand-PCI"}, {PCIC_BRIDGE, PCIS_BRIDGE_AS_PCI, "AdvancedSwitching-PCI"}, {PCIC_SIMPLECOMM, -1, "simple comms"}, {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, "UART"}, /* could detect 16550 */ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, "parallel port"}, {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MULSER, "multiport serial"}, {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MODEM, "generic modem"}, {PCIC_BASEPERIPH, -1, "base peripheral"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PIC, "interrupt controller"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_DMA, "DMA controller"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_TIMER, "timer"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RTC, "realtime clock"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PCIHOT, "PCI hot-plug controller"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_SDHC, "SD host controller"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_IOMMU, "IOMMU"}, {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RCEC, "Root Complex Event Collector"}, {PCIC_INPUTDEV, -1, "input device"}, {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, "keyboard"}, {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,"digitizer"}, {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, "mouse"}, {PCIC_INPUTDEV, PCIS_INPUTDEV_SCANNER, "scanner"}, {PCIC_INPUTDEV, PCIS_INPUTDEV_GAMEPORT, "gameport"}, {PCIC_DOCKING, -1, "docking station"}, {PCIC_PROCESSOR, -1, "processor"}, {PCIC_SERIALBUS, -1, "serial bus"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_FW, "FireWire"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_ACCESS, "AccessBus"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_SSA, "SSA"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_USB, "USB"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_FC, "Fibre Channel"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_SMBUS, "SMBus"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_INFINIBAND, "InfiniBand"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_IPMI, "IPMI"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_SERCOS, "SERCOS"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_CANBUS, "CANbus"}, {PCIC_SERIALBUS, PCIS_SERIALBUS_MIPI_I3C, "MIPI I3C"}, {PCIC_WIRELESS, -1, "wireless controller"}, {PCIC_WIRELESS, PCIS_WIRELESS_IRDA, "iRDA"}, {PCIC_WIRELESS, PCIS_WIRELESS_IR, "IR"}, {PCIC_WIRELESS, PCIS_WIRELESS_RF, "RF"}, {PCIC_WIRELESS, PCIS_WIRELESS_BLUETOOTH, "bluetooth"}, {PCIC_WIRELESS, PCIS_WIRELESS_BROADBAND, "broadband"}, {PCIC_WIRELESS, PCIS_WIRELESS_80211A, "ethernet 802.11a"}, {PCIC_WIRELESS, PCIS_WIRELESS_80211B, "ethernet 802.11b"}, {PCIC_WIRELESS, PCIS_WIRELESS_CELL, "cellular controller/modem"}, {PCIC_WIRELESS, PCIS_WIRELESS_CELL_E, "cellular controller/modem plus ethernet"}, {PCIC_INTELLIIO, -1, "intelligent I/O controller"}, {PCIC_INTELLIIO, PCIS_INTELLIIO_I2O, "I2O"}, {PCIC_SATCOM, -1, "satellite communication"}, {PCIC_SATCOM, PCIS_SATCOM_TV, "sat TV"}, {PCIC_SATCOM, PCIS_SATCOM_AUDIO, "sat audio"}, {PCIC_SATCOM, PCIS_SATCOM_VOICE, "sat voice"}, {PCIC_SATCOM, PCIS_SATCOM_DATA, "sat data"}, {PCIC_CRYPTO, -1, "encrypt/decrypt"}, {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "network/computer crypto"}, {PCIC_CRYPTO, PCIS_CRYPTO_ENTERTAIN, "entertainment crypto"}, {PCIC_DASP, -1, "dasp"}, {PCIC_DASP, PCIS_DASP_DPIO, "DPIO module"}, {PCIC_DASP, PCIS_DASP_PERFCNTRS, "performance counters"}, {PCIC_DASP, PCIS_DASP_COMM_SYNC, "communication synchronizer"}, {PCIC_DASP, PCIS_DASP_MGMT_CARD, "signal processing management"}, {PCIC_ACCEL, -1, "processing accelerators"}, {PCIC_ACCEL, PCIS_ACCEL_PROCESSING, "processing accelerators"}, {PCIC_INSTRUMENT, -1, "non-essential instrumentation"}, {0, 0, NULL} }; static const char * guess_class(struct pci_conf *p) { int i; for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) { if (pci_nomatch_tab[i].class == p->pc_class) return(pci_nomatch_tab[i].desc); } return(NULL); } static const char * guess_subclass(struct pci_conf *p) { int i; for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) { if ((pci_nomatch_tab[i].class == p->pc_class) && (pci_nomatch_tab[i].subclass == p->pc_subclass)) return(pci_nomatch_tab[i].desc); } return(NULL); } static int load_vendors(void) { const char *dbf; FILE *db; struct pci_vendor_info *cv; struct pci_device_info *cd; char buf[1024], str[1024]; char *ch; int id, error; /* * Locate the database and initialise. */ TAILQ_INIT(&pci_vendors); if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL) dbf = _PATH_LPCIVDB; if ((db = fopen(dbf, "r")) == NULL) { dbf = _PATH_PCIVDB; if ((db = fopen(dbf, "r")) == NULL) return(1); } cv = NULL; cd = NULL; error = 0; /* * Scan input lines from the database */ for (;;) { if (fgets(buf, sizeof(buf), db) == NULL) break; if ((ch = strchr(buf, '#')) != NULL) *ch = '\0'; ch = strchr(buf, '\0') - 1; while (ch > buf && isspace(*ch)) *ch-- = '\0'; if (ch <= buf) continue; /* Can't handle subvendor / subdevice entries yet */ if (buf[0] == '\t' && buf[1] == '\t') continue; /* Check for vendor entry */ if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) { if ((id == 0) || (strlen(str) < 1)) continue; if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) { warn("allocating vendor entry"); error = 1; break; } if ((cv->desc = strdup(str)) == NULL) { free(cv); warn("allocating vendor description"); error = 1; break; } cv->id = id; TAILQ_INIT(&cv->devs); TAILQ_INSERT_TAIL(&pci_vendors, cv, link); continue; } /* Check for device entry */ if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) { if ((id == 0) || (strlen(str) < 1)) continue; if (cv == NULL) { warnx("device entry with no vendor!"); continue; } if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) { warn("allocating device entry"); error = 1; break; } if ((cd->desc = strdup(str)) == NULL) { free(cd); warn("allocating device description"); error = 1; break; } cd->id = id; TAILQ_INSERT_TAIL(&cv->devs, cd, link); continue; } /* It's a comment or junk, ignore it */ } if (ferror(db)) error = 1; fclose(db); return(error); } uint32_t read_config(int fd, struct pcisel *sel, long reg, int width) { struct pci_io pi; pi.pi_sel = *sel; pi.pi_reg = reg; pi.pi_width = width; if (ioctl(fd, PCIOCREAD, &pi) < 0) err(1, "ioctl(PCIOCREAD)"); return (pi.pi_data); } static struct pcisel getdevice(const char *name) { struct pci_conf_io pc; struct pci_conf conf[1]; struct pci_match_conf patterns[1]; char *cp; int fd; fd = open(_PATH_DEVPCI, O_RDONLY, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); bzero(&pc, sizeof(struct pci_conf_io)); pc.match_buf_len = sizeof(conf); pc.matches = conf; bzero(&patterns, sizeof(patterns)); /* * The pattern structure requires the unit to be split out from * the driver name. Walk backwards from the end of the name to * find the start of the unit. */ if (name[0] == '\0') errx(1, "Empty device name"); cp = strchr(name, '\0'); assert(cp != NULL && cp != name); cp--; while (cp != name && isdigit(cp[-1])) cp--; if (cp == name || !isdigit(*cp)) errx(1, "Invalid device name"); if ((size_t)(cp - name) + 1 > sizeof(patterns[0].pd_name)) errx(1, "Device name is too long"); memcpy(patterns[0].pd_name, name, cp - name); patterns[0].pd_unit = strtol(cp, &cp, 10); if (*cp != '\0') errx(1, "Invalid device name"); patterns[0].flags = PCI_GETCONF_MATCH_NAME | PCI_GETCONF_MATCH_UNIT; pc.num_patterns = 1; pc.pat_buf_len = sizeof(patterns); pc.patterns = patterns; if (ioctl(fd, PCIOCGETCONF, &pc) == -1) err(1, "ioctl(PCIOCGETCONF)"); if (pc.status != PCI_GETCONF_LAST_DEVICE && pc.status != PCI_GETCONF_MORE_DEVS) errx(1, "error returned from PCIOCGETCONF ioctl"); close(fd); if (pc.num_matches == 0) errx(1, "Device not found"); return (conf[0].pc_sel); } static struct pcisel parsesel(const char *str) { const char *ep; char *eppos; struct pcisel sel; unsigned long selarr[4]; int i; ep = strchr(str, '@'); if (ep != NULL) ep++; else ep = str; if (strncmp(ep, "pci", 3) == 0) { ep += 3; i = 0; while (isdigit(*ep) && i < 4) { selarr[i++] = strtoul(ep, &eppos, 10); ep = eppos; if (*ep == ':') ep++; } if (i > 0 && *ep == '\0') { sel.pc_func = (i > 2) ? selarr[--i] : 0; sel.pc_dev = (i > 0) ? selarr[--i] : 0; sel.pc_bus = (i > 0) ? selarr[--i] : 0; sel.pc_domain = (i > 0) ? selarr[--i] : 0; return (sel); } } errx(1, "cannot parse selector %s", str); } static struct pcisel getsel(const char *str) { /* * No device names contain colons and selectors always contain * at least one colon. */ if (strchr(str, ':') == NULL) return (getdevice(str)); else return (parsesel(str)); } static void readone(int fd, struct pcisel *sel, long reg, int width) { printf("%0*x", width*2, read_config(fd, sel, reg, width)); } static void readit(const char *name, const char *reg, int width) { long rstart; long rend; long r; char *end; int i; int fd; struct pcisel sel; fd = open(_PATH_DEVPCI, O_RDWR, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); rend = rstart = strtol(reg, &end, 0); if (end && *end == ':') { end++; rend = strtol(end, (char **) 0, 0); } sel = getsel(name); for (i = 1, r = rstart; r <= rend; i++, r += width) { readone(fd, &sel, r, width); if (i && !(i % 8)) putchar(' '); putchar(i % (16/width) ? ' ' : '\n'); } if (i % (16/width) != 1) putchar('\n'); close(fd); } static void writeit(const char *name, const char *reg, const char *data, int width) { int fd; struct pci_io pi; pi.pi_sel = getsel(name); pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */ pi.pi_width = width; pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */ fd = open(_PATH_DEVPCI, O_RDWR, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); if (ioctl(fd, PCIOCWRITE, &pi) < 0) err(1, "ioctl(PCIOCWRITE)"); close(fd); } static void chkattached(const char *name) { int fd; struct pci_io pi; pi.pi_sel = getsel(name); fd = open(_PATH_DEVPCI, O_RDWR, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); if (ioctl(fd, PCIOCATTACHED, &pi) < 0) err(1, "ioctl(PCIOCATTACHED)"); exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */ printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached"); close(fd); } static void dump_bar(const char *name, const char *reg, const char *bar_start, const char *bar_count, int width, int verbose) { struct pci_bar_mmap pbm; uint32_t *dd; uint16_t *dh; uint8_t *db; uint64_t *dx, a, start, count; char *el; size_t res; int fd; start = 0; if (bar_start != NULL) { start = strtoul(bar_start, &el, 0); if (*el != '\0') errx(1, "Invalid bar start specification %s", bar_start); } count = 0; if (bar_count != NULL) { count = strtoul(bar_count, &el, 0); if (*el != '\0') errx(1, "Invalid count specification %s", bar_count); } pbm.pbm_sel = getsel(name); pbm.pbm_reg = strtoul(reg, &el, 0); if (*reg == '\0' || *el != '\0') errx(1, "Invalid bar specification %s", reg); pbm.pbm_flags = 0; pbm.pbm_memattr = VM_MEMATTR_DEVICE; fd = open(_PATH_DEVPCI, O_RDWR, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); if (ioctl(fd, PCIOCBARMMAP, &pbm) < 0) err(1, "ioctl(PCIOCBARMMAP)"); if (count == 0) count = pbm.pbm_bar_length / width; if (start + count < start || (start + count) * width < (uint64_t)width) errx(1, "(start + count) x width overflow"); if ((start + count) * width > pbm.pbm_bar_length) { if (start * width > pbm.pbm_bar_length) count = 0; else count = (pbm.pbm_bar_length - start * width) / width; } if (verbose) { fprintf(stderr, "Dumping pci%d:%d:%d:%d BAR %x mapped base %p " "off %#x length %#jx from %#jx count %#jx in %d-bytes\n", pbm.pbm_sel.pc_domain, pbm.pbm_sel.pc_bus, pbm.pbm_sel.pc_dev, pbm.pbm_sel.pc_func, pbm.pbm_reg, pbm.pbm_map_base, pbm.pbm_bar_off, pbm.pbm_bar_length, start, count, width); } switch (width) { case 1: db = (uint8_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + pbm.pbm_bar_off + start * width); for (a = 0; a < count; a += width, db++) { res = fwrite(db, width, 1, stdout); if (res != 1) { errx(1, "error writing to stdout"); break; } } break; case 2: dh = (uint16_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + pbm.pbm_bar_off + start * width); for (a = 0; a < count; a += width, dh++) { res = fwrite(dh, width, 1, stdout); if (res != 1) { errx(1, "error writing to stdout"); break; } } break; case 4: dd = (uint32_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + pbm.pbm_bar_off + start * width); for (a = 0; a < count; a += width, dd++) { res = fwrite(dd, width, 1, stdout); if (res != 1) { errx(1, "error writing to stdout"); break; } } break; case 8: dx = (uint64_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base + pbm.pbm_bar_off + start * width); for (a = 0; a < count; a += width, dx++) { res = fwrite(dx, width, 1, stdout); if (res != 1) { errx(1, "error writing to stdout"); break; } } break; default: errx(1, "invalid access width"); } munmap((void *)pbm.pbm_map_base, pbm.pbm_map_length); close(fd); } diff --git a/usr.sbin/pw/bitmap.c b/usr.sbin/pw/bitmap.c index b7767f025529..f176fe571a36 100644 --- a/usr.sbin/pw/bitmap.c +++ b/usr.sbin/pw/bitmap.c @@ -1,133 +1,128 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include "bitmap.h" struct bitmap bm_alloc(int size) { struct bitmap bm; int szmap = (size / 8) + !!(size % 8); bm.size = size; bm.map = malloc(szmap); if (bm.map) memset(bm.map, 0, szmap); return bm; } void bm_dealloc(struct bitmap * bm) { free(bm->map); } static void bm_getmask(int *pos, unsigned char *bmask) { *bmask = (unsigned char) (1 << (*pos % 8)); *pos /= 8; } void bm_setbit(struct bitmap * bm, int pos) { unsigned char bmask; bm_getmask(&pos, &bmask); bm->map[pos] |= bmask; } void bm_clrbit(struct bitmap * bm, int pos) { unsigned char bmask; bm_getmask(&pos, &bmask); bm->map[pos] &= ~bmask; } int bm_isset(struct bitmap * bm, int pos) { unsigned char bmask; bm_getmask(&pos, &bmask); return !!(bm->map[pos] & bmask); } int bm_firstunset(struct bitmap * bm) { int szmap = (bm->size / 8) + !!(bm->size % 8); int at = 0; int pos = 0; while (pos < szmap) { unsigned char bmv = bm->map[pos++]; unsigned char bmask = 1; while (bmask & 0xff) { if ((bmv & bmask) == 0) return at; bmask <<= 1; ++at; } } return at; } int bm_lastset(struct bitmap * bm) { int szmap = (bm->size / 8) + !!(bm->size % 8); int at = 0; int pos = 0; int ofs = 0; while (pos < szmap) { unsigned char bmv = bm->map[pos++]; unsigned char bmask = 1; while (bmask & 0xff) { if ((bmv & bmask) != 0) ofs = at; bmask <<= 1; ++at; } } return ofs; } diff --git a/usr.sbin/pw/cpdir.c b/usr.sbin/pw/cpdir.c index 8ffca63bdb70..504933ab88af 100644 --- a/usr.sbin/pw/cpdir.c +++ b/usr.sbin/pw/cpdir.c @@ -1,125 +1,120 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include "pw.h" void copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid, gid_t gid, int flags) { char *p, lnk[MAXPATHLEN], copybuf[4096]; int len, homefd, srcfd, destfd; ssize_t sz; struct stat st; struct dirent *e; DIR *d; if (*dir == '/') dir++; if (mkdirat(rootfd, dir, mode) != 0 && errno != EEXIST) { warn("mkdir(%s)", dir); return; } fchownat(rootfd, dir, uid, gid, AT_SYMLINK_NOFOLLOW); if (flags > 0) chflagsat(rootfd, dir, flags, AT_SYMLINK_NOFOLLOW); if (skelfd == -1) return; homefd = openat(rootfd, dir, O_DIRECTORY); if ((d = fdopendir(skelfd)) == NULL) { close(skelfd); close(homefd); return; } while ((e = readdir(d)) != NULL) { if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; p = e->d_name; if (fstatat(skelfd, p, &st, AT_SYMLINK_NOFOLLOW) == -1) continue; if (strncmp(p, "dot.", 4) == 0) /* Conversion */ p += 3; if (S_ISDIR(st.st_mode)) { copymkdir(homefd, p, openat(skelfd, e->d_name, O_DIRECTORY), st.st_mode & _DEF_DIRMODE, uid, gid, st.st_flags); continue; } if (S_ISLNK(st.st_mode) && (len = readlinkat(skelfd, e->d_name, lnk, sizeof(lnk) -1)) != -1) { lnk[len] = '\0'; symlinkat(lnk, homefd, p); fchownat(homefd, p, uid, gid, AT_SYMLINK_NOFOLLOW); continue; } if (!S_ISREG(st.st_mode)) continue; if ((srcfd = openat(skelfd, e->d_name, O_RDONLY)) == -1) continue; destfd = openat(homefd, p, O_RDWR | O_CREAT | O_EXCL, st.st_mode); if (destfd == -1) { close(srcfd); continue; } while ((sz = read(srcfd, copybuf, sizeof(copybuf))) > 0) write(destfd, copybuf, sz); close(srcfd); /* * Propagate special filesystem flags */ fchown(destfd, uid, gid); fchflags(destfd, st.st_flags); close(destfd); } closedir(d); } diff --git a/usr.sbin/pw/grupd.c b/usr.sbin/pw/grupd.c index f29ea882ca83..392e32b3ee7a 100644 --- a/usr.sbin/pw/grupd.c +++ b/usr.sbin/pw/grupd.c @@ -1,111 +1,106 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include "pwupd.h" char * getgrpath(const char * file) { static char pathbuf[MAXPATHLEN]; snprintf(pathbuf, sizeof pathbuf, "%s/%s", conf.etcpath, file); return (pathbuf); } static int gr_update(struct group * grp, char const * group) { int pfd, tfd; struct group *gr = NULL; struct group *old_gr = NULL; if (grp != NULL) gr = gr_dup(grp); if (group != NULL) old_gr = GETGRNAM(group); if (gr_init(conf.etcpath, NULL)) err(1, "gr_init()"); if ((pfd = gr_lock()) == -1) { gr_fini(); err(1, "gr_lock()"); } if ((tfd = gr_tmp(-1)) == -1) { gr_fini(); err(1, "gr_tmp()"); } if (gr_copy(pfd, tfd, gr, old_gr) == -1) { gr_fini(); close(tfd); err(1, "gr_copy()"); } fsync(tfd); close(tfd); if (gr_mkdb() == -1) { gr_fini(); err(1, "gr_mkdb()"); } free(gr); gr_fini(); return 0; } int addgrent(struct group * grp) { return gr_update(grp, NULL); } int chggrent(char const * login, struct group * grp) { return gr_update(grp, login); } int delgrent(struct group * grp) { return (gr_update(NULL, grp->gr_name)); } diff --git a/usr.sbin/pw/psdate.c b/usr.sbin/pw/psdate.c index 5e91ca59784f..7d37e6b4aa1b 100644 --- a/usr.sbin/pw/psdate.c +++ b/usr.sbin/pw/psdate.c @@ -1,259 +1,254 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include "psdate.h" int numerics(char const * str) { return (str[strspn(str, "0123456789x")] == '\0'); } static int aindex(char const * arr[], char const ** str, int len) { int l, i; char mystr[32]; mystr[len] = '\0'; l = strlen(strncpy(mystr, *str, len)); for (i = 0; i < l; i++) mystr[i] = (char) tolower((unsigned char)mystr[i]); for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++); if (arr[i] == NULL) i = -1; else { /* Skip past it */ while (**str && isalpha((unsigned char)**str)) ++(*str); /* And any following whitespace */ while (**str && (**str == ',' || isspace((unsigned char)**str))) ++(*str); } /* Return index */ return i; } static int weekday(char const ** str) { static char const *days[] = {"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL}; return aindex(days, str, 3); } static void parse_datesub(char const * str, struct tm *t) { struct tm tm; locale_t l; int i; char *ret; const char *valid_formats[] = { "%d-%b-%y", "%d-%b-%Y", "%d-%m-%y", "%d-%m-%Y", "%H:%M %d-%b-%y", "%H:%M %d-%b-%Y", "%H:%M %d-%m-%y", "%H:%M %d-%m-%Y", "%H:%M:%S %d-%b-%y", "%H:%M:%S %d-%b-%Y", "%H:%M:%S %d-%m-%y", "%H:%M:%S %d-%m-%Y", "%d-%b-%y %H:%M", "%d-%b-%Y %H:%M", "%d-%m-%y %H:%M", "%d-%m-%Y %H:%M", "%d-%b-%y %H:%M:%S", "%d-%b-%Y %H:%M:%S", "%d-%m-%y %H:%M:%S", "%d-%m-%Y %H:%M:%S", "%H:%M\t%d-%b-%y", "%H:%M\t%d-%b-%Y", "%H:%M\t%d-%m-%y", "%H:%M\t%d-%m-%Y", "%H:%M\t%S %d-%b-%y", "%H:%M\t%S %d-%b-%Y", "%H:%M\t%S %d-%m-%y", "%H:%M\t%S %d-%m-%Y", "%d-%b-%y\t%H:%M", "%d-%b-%Y\t%H:%M", "%d-%m-%y\t%H:%M", "%d-%m-%Y\t%H:%M", "%d-%b-%y\t%H:%M:%S", "%d-%b-%Y\t%H:%M:%S", "%d-%m-%y\t%H:%M:%S", "%d-%m-%Y\t%H:%M:%S", NULL, }; l = newlocale(LC_ALL_MASK, "C", NULL); for (i=0; valid_formats[i] != NULL; i++) { memset(&tm, 0, sizeof(tm)); ret = strptime_l(str, valid_formats[i], &tm, l); if (ret && *ret == '\0') { t->tm_mday = tm.tm_mday; t->tm_mon = tm.tm_mon; t->tm_year = tm.tm_year; t->tm_hour = tm.tm_hour; t->tm_min = tm.tm_min; t->tm_sec = tm.tm_sec; freelocale(l); return; } } freelocale(l); errx(EXIT_FAILURE, "Invalid date"); } /*- * Parse time must be flexible, it handles the following formats: * nnnnnnnnnnn UNIX timestamp (all numeric), 0 = now * 0xnnnnnnnn UNIX timestamp in hexadecimal * 0nnnnnnnnn UNIX timestamp in octal * 0 Given time * +nnnn[smhdwoy] Given time + nnnn hours, mins, days, weeks, months or years * -nnnn[smhdwoy] Given time - nnnn hours, mins, days, weeks, months or years * dd[ ./-]mmm[ ./-]yy Date } * hh:mm:ss Time } May be combined */ time_t parse_date(time_t dt, char const * str) { char *p; int i; long val; struct tm *T; if (dt == 0) dt = time(NULL); while (*str && isspace((unsigned char)*str)) ++str; if (numerics(str)) { dt = strtol(str, &p, 0); } else if (*str == '+' || *str == '-') { val = strtol(str, &p, 0); switch (*p) { case 'h': case 'H': /* hours */ dt += (val * 3600L); break; case '\0': case 'm': case 'M': /* minutes */ dt += (val * 60L); break; case 's': case 'S': /* seconds */ dt += val; break; case 'd': case 'D': /* days */ dt += (val * 86400L); break; case 'w': case 'W': /* weeks */ dt += (val * 604800L); break; case 'o': case 'O': /* months */ T = localtime(&dt); T->tm_mon += (int) val; i = T->tm_mday; goto fixday; case 'y': case 'Y': /* years */ T = localtime(&dt); T->tm_year += (int) val; i = T->tm_mday; fixday: dt = mktime(T); T = localtime(&dt); if (T->tm_mday != i) { T->tm_mday = 1; dt = mktime(T); dt -= (time_t) 86400L; } default: /* unknown */ break; /* leave untouched */ } } else { char *q, tmp[64]; /* * Skip past any weekday prefix */ weekday(&str); strlcpy(tmp, str, sizeof(tmp)); str = tmp; T = localtime(&dt); /* * See if we can break off any timezone */ while ((q = strrchr(tmp, ' ')) != NULL) { if (strchr("(+-", q[1]) != NULL) *q = '\0'; else { int j = 1; while (q[j] && isupper((unsigned char)q[j])) ++j; if (q[j] == '\0') *q = '\0'; else break; } } parse_datesub(tmp, T); dt = mktime(T); } return dt; } diff --git a/usr.sbin/pw/pw.c b/usr.sbin/pw/pw.c index 5b30cdf221af..063553dd084f 100644 --- a/usr.sbin/pw/pw.c +++ b/usr.sbin/pw/pw.c @@ -1,384 +1,379 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include "pw.h" const char *Modes[] = { "add", "del", "mod", "show", "next", NULL}; const char *Which[] = {"user", "group", NULL}; static const char *Combo1[] = { "useradd", "userdel", "usermod", "usershow", "usernext", "lock", "unlock", "groupadd", "groupdel", "groupmod", "groupshow", "groupnext", NULL}; static const char *Combo2[] = { "adduser", "deluser", "moduser", "showuser", "nextuser", "lock", "unlock", "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup", NULL}; struct pwf PWF = { PWF_REGULAR, setpwent, endpwent, getpwent, getpwuid, getpwnam, setgrent, endgrent, getgrent, getgrgid, getgrnam, }; struct pwf VPWF = { PWF_ALT, vsetpwent, vendpwent, vgetpwent, vgetpwuid, vgetpwnam, vsetgrent, vendgrent, vgetgrent, vgetgrgid, vgetgrnam, }; static int (*cmdfunc[W_NUM][M_NUM])(int argc, char **argv, char *_name) = { { /* user */ pw_user_add, pw_user_del, pw_user_mod, pw_user_show, pw_user_next, pw_user_lock, pw_user_unlock, }, { /* group */ pw_group_add, pw_group_del, pw_group_mod, pw_group_show, pw_group_next, } }; struct pwconf conf; static int getindex(const char *words[], const char *word); static void cmdhelp(int mode, int which); int main(int argc, char *argv[]) { int mode = -1, which = -1, tmp; struct stat st; char arg, *arg1; bool relocated, nis; arg1 = NULL; relocated = nis = false; memset(&conf, 0, sizeof(conf)); strlcpy(conf.rootdir, "/", sizeof(conf.rootdir)); strlcpy(conf.etcpath, _PATH_PWD, sizeof(conf.etcpath)); conf.fd = -1; conf.checkduplicate = true; setlocale(LC_ALL, ""); /* * Break off the first couple of words to determine what exactly * we're being asked to do */ while (argc > 1) { if (*argv[1] == '-') { /* * Special case, allow pw -V [args] for scripts etc. */ arg = argv[1][1]; if (arg == 'V' || arg == 'R') { if (relocated) errx(EXIT_FAILURE, "Both '-R' and '-V' " "specified, only one accepted"); relocated = true; optarg = &argv[1][2]; if (*optarg == '\0') { if (stat(argv[2], &st) != 0) errx(EX_OSFILE, \ "no such directory `%s'", argv[2]); if (!S_ISDIR(st.st_mode)) errx(EX_OSFILE, "`%s' not a " "directory", argv[2]); optarg = argv[2]; ++argv; --argc; } memcpy(&PWF, &VPWF, sizeof PWF); if (arg == 'R') { strlcpy(conf.rootdir, optarg, sizeof(conf.rootdir)); PWF._altdir = PWF_ROOTDIR; } snprintf(conf.etcpath, sizeof(conf.etcpath), "%s%s", optarg, arg == 'R' ? _PATH_PWD : ""); } else break; } else if (mode == -1 && (tmp = getindex(Modes, argv[1])) != -1) mode = tmp; else if (which == -1 && (tmp = getindex(Which, argv[1])) != -1) which = tmp; else if ((mode == -1 && which == -1) && ((tmp = getindex(Combo1, argv[1])) != -1 || (tmp = getindex(Combo2, argv[1])) != -1)) { which = tmp / M_NUM; mode = tmp % M_NUM; } else if (strcmp(argv[1], "help") == 0 && argv[2] == NULL) cmdhelp(mode, which); else if (which != -1 && mode != -1) arg1 = argv[1]; else errx(EX_USAGE, "unknown keyword `%s'", argv[1]); ++argv; --argc; } /* * Bail out unless the user is specific! */ if (mode == -1 || which == -1) cmdhelp(mode, which); conf.rootfd = open(conf.rootdir, O_DIRECTORY|O_CLOEXEC); if (conf.rootfd == -1) errx(EXIT_FAILURE, "Unable to open '%s'", conf.rootdir); return (cmdfunc[which][mode](argc, argv, arg1)); } static int getindex(const char *words[], const char *word) { int i = 0; while (words[i]) { if (strcmp(words[i], word) == 0) return (i); i++; } return (-1); } /* * This is probably an overkill for a cmdline help system, but it reflects * the complexity of the command line. */ static void cmdhelp(int mode, int which) { if (which == -1) fprintf(stderr, "usage:\n pw [user|group|lock|unlock] [add|del|mod|show|next] [help|switches/values]\n"); else if (mode == -1) fprintf(stderr, "usage:\n pw %s [add|del|mod|show|next] [help|switches/values]\n", Which[which]); else { /* * We need to give mode specific help */ static const char *help[W_NUM][M_NUM] = { { "usage: pw useradd [name] [switches]\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n" " Adding users:\n" "\t-n name login name\n" "\t-u uid user id\n" "\t-c comment user name/comment\n" "\t-d directory home directory\n" "\t-e date account expiry date\n" "\t-p date password expiry date\n" "\t-g grp initial group\n" "\t-G grp1,grp2 additional groups\n" "\t-m [ -k dir ] create and set up home\n" "\t-M mode home directory permissions\n" "\t-s shell name of login shell\n" "\t-o duplicate uid ok\n" "\t-L class user class\n" "\t-h fd read password on fd\n" "\t-H fd read encrypted password on fd\n" "\t-Y update NIS maps\n" "\t-N no update\n" " Setting defaults:\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-D set user defaults\n" "\t-b dir default home root dir\n" "\t-e period default expiry period\n" "\t-p period default password change period\n" "\t-g group default group\n" "\t-G grp1,grp2 additional groups\n" "\t-L class default user class\n" "\t-k dir default home skeleton\n" "\t-M mode home directory permissions\n" "\t-u min,max set min,max uids\n" "\t-i min,max set min,max gids\n" "\t-w method set default password method\n" "\t-s shell default shell\n" "\t-y path set NIS passwd file path\n", "usage: pw userdel [uid|name] [switches]\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-n name login name\n" "\t-u uid user id\n" "\t-Y update NIS maps\n" "\t-y path set NIS passwd file path\n" "\t-r remove home & contents\n", "usage: pw usermod [uid|name] [switches]\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n" "\t-F force add if no user\n" "\t-n name login name\n" "\t-u uid user id\n" "\t-c comment user name/comment\n" "\t-d directory home directory\n" "\t-e date account expiry date\n" "\t-p date password expiry date\n" "\t-g grp initial group\n" "\t-G grp1,grp2 additional groups\n" "\t-l name new login name\n" "\t-L class user class\n" "\t-m [ -k dir ] create and set up home\n" "\t-M mode home directory permissions\n" "\t-s shell name of login shell\n" "\t-w method set new password using method\n" "\t-h fd read password on fd\n" "\t-H fd read encrypted password on fd\n" "\t-Y update NIS maps\n" "\t-y path set NIS passwd file path\n" "\t-N no update\n", "usage: pw usershow [uid|name] [switches]\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-n name login name\n" "\t-u uid user id\n" "\t-F force print\n" "\t-P prettier format\n" "\t-a print all users\n" "\t-7 print in v7 format\n", "usage: pw usernext [switches]\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n", "usage pw: lock [switches]\n" "\t-V etcdir alternate /etc locations\n" "\t-C config configuration file\n" "\t-q quiet operation\n", "usage pw: unlock [switches]\n" "\t-V etcdir alternate /etc locations\n" "\t-C config configuration file\n" "\t-q quiet operation\n" }, { "usage: pw groupadd [group|gid] [switches]\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n" "\t-n group group name\n" "\t-g gid group id\n" "\t-M usr1,usr2 add users as group members\n" "\t-o duplicate gid ok\n" "\t-Y update NIS maps\n" "\t-N no update\n", "usage: pw groupdel [group|gid] [switches]\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-n name group name\n" "\t-g gid group id\n" "\t-Y update NIS maps\n", "usage: pw groupmod [group|gid] [switches]\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n" "\t-F force add if not exists\n" "\t-n name group name\n" "\t-g gid group id\n" "\t-M usr1,usr2 replaces users as group members\n" "\t-m usr1,usr2 add users as group members\n" "\t-d usr1,usr2 delete users as group members\n" "\t-l name new group name\n" "\t-Y update NIS maps\n" "\t-N no update\n", "usage: pw groupshow [group|gid] [switches]\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-n name group name\n" "\t-g gid group id\n" "\t-F force print\n" "\t-P prettier format\n" "\t-a print all accounting groups\n", "usage: pw groupnext [switches]\n" "\t-V etcdir alternate /etc location\n" "\t-R rootdir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n" } }; fprintf(stderr, "%s", help[which][mode]); } exit(EXIT_FAILURE); } diff --git a/usr.sbin/pw/pw_conf.c b/usr.sbin/pw/pw_conf.c index 0ec88f60eda1..e9042b15b321 100644 --- a/usr.sbin/pw/pw_conf.c +++ b/usr.sbin/pw/pw_conf.c @@ -1,553 +1,548 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include "pw.h" #define debugging 0 enum { _UC_NONE, _UC_DEFAULTPWD, _UC_REUSEUID, _UC_REUSEGID, _UC_NISPASSWD, _UC_DOTDIR, _UC_NEWMAIL, _UC_LOGFILE, _UC_HOMEROOT, _UC_HOMEMODE, _UC_SHELLPATH, _UC_SHELLS, _UC_DEFAULTSHELL, _UC_DEFAULTGROUP, _UC_EXTRAGROUPS, _UC_DEFAULTCLASS, _UC_MINUID, _UC_MAXUID, _UC_MINGID, _UC_MAXGID, _UC_EXPIRE, _UC_PASSWORD, _UC_FIELDS }; static char bourne_shell[] = "sh"; static char *system_shells[_UC_MAXSHELLS] = { bourne_shell, "csh", "tcsh" }; static char const *booltrue[] = { "yes", "true", "1", "on", NULL }; static char const *boolfalse[] = { "no", "false", "0", "off", NULL }; static struct userconf config = { 0, /* Default password for new users? (nologin) */ 0, /* Reuse uids? */ 0, /* Reuse gids? */ NULL, /* NIS version of the passwd file */ "/usr/share/skel", /* Where to obtain skeleton files */ NULL, /* Mail to send to new accounts */ "/var/log/userlog", /* Where to log changes */ "/home", /* Where to create home directory */ _DEF_DIRMODE, /* Home directory perms, modified by umask */ "/bin", /* Where shells are located */ system_shells, /* List of shells (first is default) */ bourne_shell, /* Default shell */ NULL, /* Default group name */ NULL, /* Default (additional) groups */ NULL, /* Default login class */ 1000, 32000, /* Allowed range of uids */ 1000, 32000, /* Allowed range of gids */ 0, /* Days until account expires */ 0 /* Days until password expires */ }; static char const *comments[_UC_FIELDS] = { "#\n# pw.conf - user/group configuration defaults\n#\n", "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n", "\n# Reuse gaps in uid sequence? (yes or no)\n", "\n# Reuse gaps in gid sequence? (yes or no)\n", "\n# Path to the NIS passwd file (blank or 'no' for none)\n", "\n# Obtain default dotfiles from this directory\n", "\n# Mail this file to new user (/etc/newuser.msg or no)\n", "\n# Log add/change/remove information in this file\n", "\n# Root directory in which $HOME directory is created\n", "\n# Mode for the new $HOME directory, will be modified by umask\n", "\n# Colon separated list of directories containing valid shells\n", "\n# Comma separated list of available shells (without paths)\n", "\n# Default shell (without path)\n", "\n# Default group (leave blank for new group per user)\n", "\n# Extra groups for new users\n", "\n# Default login class for new users\n", "\n# Range of valid default user ids\n", NULL, "\n# Range of valid default group ids\n", NULL, "\n# Days after which account expires (0=disabled)\n", "\n# Days after which password expires (0=disabled)\n" }; static char const *kwds[] = { "", "defaultpasswd", "reuseuids", "reusegids", "nispasswd", "skeleton", "newmail", "logfile", "home", "homemode", "shellpath", "shells", "defaultshell", "defaultgroup", "extragroups", "defaultclass", "minuid", "maxuid", "mingid", "maxgid", "expire_days", "password_days", NULL }; static char * unquote(char const * str) { if (str && (*str == '"' || *str == '\'')) { char *p = strchr(str + 1, *str); if (p != NULL) *p = '\0'; return (char *) (*++str ? str : NULL); } return (char *) str; } int boolean_val(char const * str, int dflt) { if ((str = unquote(str)) != NULL) { int i; for (i = 0; booltrue[i]; i++) if (strcmp(str, booltrue[i]) == 0) return 1; for (i = 0; boolfalse[i]; i++) if (strcmp(str, boolfalse[i]) == 0) return 0; } return dflt; } int passwd_val(char const * str, int dflt) { if ((str = unquote(str)) != NULL) { int i; for (i = 0; booltrue[i]; i++) if (strcmp(str, booltrue[i]) == 0) return P_YES; for (i = 0; boolfalse[i]; i++) if (strcmp(str, boolfalse[i]) == 0) return P_NO; /* * Special cases for defaultpassword */ if (strcmp(str, "random") == 0) return P_RANDOM; if (strcmp(str, "none") == 0) return P_NONE; errx(1, "Invalid value for default password"); } return dflt; } char const * boolean_str(int val) { if (val == P_NO) return (boolfalse[0]); else if (val == P_RANDOM) return ("random"); else if (val == P_NONE) return ("none"); else return (booltrue[0]); } char * newstr(char const * p) { char *q; if ((p = unquote(p)) == NULL) return (NULL); if ((q = strdup(p)) == NULL) err(1, "strdup()"); return (q); } struct userconf * read_userconfig(char const * file) { FILE *fp; char *buf, *p; const char *errstr; size_t linecap; ssize_t linelen; buf = NULL; linecap = 0; if ((fp = fopen(file, "r")) == NULL) return (&config); while ((linelen = getline(&buf, &linecap, fp)) > 0) { if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { static char const toks[] = " \t\r\n,="; char *q = strtok(NULL, toks); int i = 0; mode_t *modeset; while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) ++i; #if debugging if (i == _UC_FIELDS) printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); else printf("Got kwd[%s]=%s\n", p, q); #endif switch (i) { case _UC_DEFAULTPWD: config.default_password = passwd_val(q, 1); break; case _UC_REUSEUID: config.reuse_uids = boolean_val(q, 0); break; case _UC_REUSEGID: config.reuse_gids = boolean_val(q, 0); break; case _UC_NISPASSWD: config.nispasswd = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_DOTDIR: config.dotdir = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_NEWMAIL: config.newmail = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_LOGFILE: config.logfile = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_HOMEROOT: config.home = (q == NULL || !boolean_val(q, 1)) ? "/home" : newstr(q); break; case _UC_HOMEMODE: modeset = setmode(q); config.homemode = (q == NULL || !boolean_val(q, 1)) ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE); free(modeset); break; case _UC_SHELLPATH: config.shelldir = (q == NULL || !boolean_val(q, 1)) ? "/bin" : newstr(q); break; case _UC_SHELLS: for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) system_shells[i] = newstr(q); if (i > 0) while (i < _UC_MAXSHELLS) system_shells[i++] = NULL; break; case _UC_DEFAULTSHELL: config.shell_default = (q == NULL || !boolean_val(q, 1)) ? (char *) bourne_shell : newstr(q); break; case _UC_DEFAULTGROUP: q = unquote(q); config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) ? NULL : newstr(q); break; case _UC_EXTRAGROUPS: while ((q = strtok(NULL, toks)) != NULL) { if (config.groups == NULL) config.groups = sl_init(); sl_add(config.groups, newstr(q)); } break; case _UC_DEFAULTCLASS: config.default_class = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_MINUID: if ((q = unquote(q)) != NULL) { config.min_uid = strtounum(q, 0, UID_MAX, &errstr); if (errstr) warnx("Invalid min_uid: '%s';" " ignoring", q); } break; case _UC_MAXUID: if ((q = unquote(q)) != NULL) { config.max_uid = strtounum(q, 0, UID_MAX, &errstr); if (errstr) warnx("Invalid max_uid: '%s';" " ignoring", q); } break; case _UC_MINGID: if ((q = unquote(q)) != NULL) { config.min_gid = strtounum(q, 0, GID_MAX, &errstr); if (errstr) warnx("Invalid min_gid: '%s';" " ignoring", q); } break; case _UC_MAXGID: if ((q = unquote(q)) != NULL) { config.max_gid = strtounum(q, 0, GID_MAX, &errstr); if (errstr) warnx("Invalid max_gid: '%s';" " ignoring", q); } break; case _UC_EXPIRE: if ((q = unquote(q)) != NULL) { config.expire_days = strtonum(q, 0, INT_MAX, &errstr); if (errstr) warnx("Invalid expire days:" " '%s'; ignoring", q); } break; case _UC_PASSWORD: if ((q = unquote(q)) != NULL) { config.password_days = strtonum(q, 0, INT_MAX, &errstr); if (errstr) warnx("Invalid password days:" " '%s'; ignoring", q); } break; case _UC_FIELDS: case _UC_NONE: break; } } } free(buf); fclose(fp); return (&config); } int write_userconfig(struct userconf *cnf, const char *file) { int fd; int i, j; FILE *buffp; FILE *fp; char cfgfile[MAXPATHLEN]; char *buf; size_t sz; if (file == NULL) { snprintf(cfgfile, sizeof(cfgfile), "%s/" _PW_CONF, conf.etcpath); file = cfgfile; } if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1) return (0); if ((fp = fdopen(fd, "w")) == NULL) { close(fd); return (0); } sz = 0; buf = NULL; buffp = open_memstream(&buf, &sz); if (buffp == NULL) err(EXIT_FAILURE, "open_memstream()"); for (i = _UC_NONE; i < _UC_FIELDS; i++) { int quote = 1; if (buf != NULL) memset(buf, 0, sz); rewind(buffp); switch (i) { case _UC_DEFAULTPWD: fputs(boolean_str(cnf->default_password), buffp); break; case _UC_REUSEUID: fputs(boolean_str(cnf->reuse_uids), buffp); break; case _UC_REUSEGID: fputs(boolean_str(cnf->reuse_gids), buffp); break; case _UC_NISPASSWD: fputs(cnf->nispasswd ? cnf->nispasswd : "", buffp); quote = 0; break; case _UC_DOTDIR: fputs(cnf->dotdir ? cnf->dotdir : boolean_str(0), buffp); break; case _UC_NEWMAIL: fputs(cnf->newmail ? cnf->newmail : boolean_str(0), buffp); break; case _UC_LOGFILE: fputs(cnf->logfile ? cnf->logfile : boolean_str(0), buffp); break; case _UC_HOMEROOT: fputs(cnf->home, buffp); break; case _UC_HOMEMODE: fprintf(buffp, "%04o", cnf->homemode); quote = 0; break; case _UC_SHELLPATH: fputs(cnf->shelldir, buffp); break; case _UC_SHELLS: for (j = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) fprintf(buffp, "%s\"%s\"", j ? "," : "", system_shells[j]); quote = 0; break; case _UC_DEFAULTSHELL: fputs(cnf->shell_default ? cnf->shell_default : bourne_shell, buffp); break; case _UC_DEFAULTGROUP: fputs(cnf->default_group ? cnf->default_group : "", buffp); break; case _UC_EXTRAGROUPS: for (j = 0; cnf->groups != NULL && j < (int)cnf->groups->sl_cur; j++) fprintf(buffp, "%s\"%s\"", j ? "," : "", cnf->groups->sl_str[j]); quote = 0; break; case _UC_DEFAULTCLASS: fputs(cnf->default_class ? cnf->default_class : "", buffp); break; case _UC_MINUID: fprintf(buffp, "%ju", (uintmax_t)cnf->min_uid); quote = 0; break; case _UC_MAXUID: fprintf(buffp, "%ju", (uintmax_t)cnf->max_uid); quote = 0; break; case _UC_MINGID: fprintf(buffp, "%ju", (uintmax_t)cnf->min_gid); quote = 0; break; case _UC_MAXGID: fprintf(buffp, "%ju", (uintmax_t)cnf->max_gid); quote = 0; break; case _UC_EXPIRE: fprintf(buffp, "%jd", (intmax_t)cnf->expire_days); quote = 0; break; case _UC_PASSWORD: fprintf(buffp, "%jd", (intmax_t)cnf->password_days); quote = 0; break; case _UC_NONE: break; } fflush(buffp); if (comments[i]) fputs(comments[i], fp); if (*kwds[i]) { if (quote) fprintf(fp, "%s = \"%s\"\n", kwds[i], buf); else fprintf(fp, "%s = %s\n", kwds[i], buf); #if debugging printf("WROTE: %s = %s\n", kwds[i], buf); #endif } } fclose(buffp); free(buf); return (fclose(fp) != EOF); } diff --git a/usr.sbin/pw/pw_group.c b/usr.sbin/pw/pw_group.c index 48f999d3e1d3..32dec769fb1a 100644 --- a/usr.sbin/pw/pw_group.c +++ b/usr.sbin/pw/pw_group.c @@ -1,712 +1,707 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include "pw.h" #include "bitmap.h" static struct passwd *lookup_pwent(const char *user); static void delete_members(struct group *grp, char *list); static int print_group(struct group * grp, bool pretty); static gid_t gr_gidpolicy(struct userconf * cnf, intmax_t id); static void grp_set_passwd(struct group *grp, bool update, int fd, bool precrypted) { int b; int istty; struct termios t, n; static char line[256]; char *p; if (fd == -1) return; if (fd == '-') { grp->gr_passwd = "*"; /* No access */ return; } if ((istty = isatty(fd))) { if (tcgetattr(fd, &t) == -1) istty = 0; else { n = t; /* Disable echo */ n.c_lflag &= ~(ECHO); tcsetattr(fd, TCSANOW, &n); printf("%sassword for group %s:", update ? "New p" : "P", grp->gr_name); fflush(stdout); } } b = read(fd, line, sizeof(line) - 1); if (istty) { /* Restore state */ tcsetattr(fd, TCSANOW, &t); fputc('\n', stdout); fflush(stdout); } if (b < 0) err(EX_OSERR, "-h file descriptor"); line[b] = '\0'; if ((p = strpbrk(line, " \t\r\n")) != NULL) *p = '\0'; if (!*line) errx(EX_DATAERR, "empty password read on file descriptor %d", conf.fd); if (precrypted) { if (strchr(line, ':') != 0) errx(EX_DATAERR, "wrong encrypted passwrd"); grp->gr_passwd = line; } else grp->gr_passwd = pw_pwcrypt(line); } int pw_groupnext(struct userconf *cnf, bool quiet) { gid_t next = gr_gidpolicy(cnf, -1); if (quiet) return (next); printf("%ju\n", (uintmax_t)next); return (EXIT_SUCCESS); } static struct group * getgroup(char *name, intmax_t id, bool fatal) { struct group *grp; if (id < 0 && name == NULL) errx(EX_DATAERR, "groupname or id required"); grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); if (grp == NULL) { if (!fatal) return (NULL); if (name == NULL) errx(EX_DATAERR, "unknown gid `%ju'", id); errx(EX_DATAERR, "unknown group `%s'", name); } return (grp); } /* * Lookup a passwd entry using a name or UID. */ static struct passwd * lookup_pwent(const char *user) { struct passwd *pwd; if ((pwd = GETPWNAM(user)) == NULL && (!isdigit((unsigned char)*user) || (pwd = getpwuid((uid_t) atoi(user))) == NULL)) errx(EX_NOUSER, "user `%s' does not exist", user); return (pwd); } /* * Delete requested members from a group. */ static void delete_members(struct group *grp, char *list) { char *p; int k; if (grp->gr_mem == NULL) return; for (p = strtok(list, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { for (k = 0; grp->gr_mem[k] != NULL; k++) { if (strcmp(grp->gr_mem[k], p) == 0) break; } if (grp->gr_mem[k] == NULL) /* No match */ continue; for (; grp->gr_mem[k] != NULL; k++) grp->gr_mem[k] = grp->gr_mem[k+1]; } } static gid_t gr_gidpolicy(struct userconf * cnf, intmax_t id) { struct group *grp; struct bitmap bm; gid_t gid = (gid_t) - 1; /* * Check the given gid, if any */ if (id > 0) { gid = (gid_t) id; if ((grp = GETGRGID(gid)) != NULL && conf.checkduplicate) errx(EX_DATAERR, "gid `%ju' has already been allocated", (uintmax_t)grp->gr_gid); return (gid); } /* * We need to allocate the next available gid under one of * two policies a) Grab the first unused gid b) Grab the * highest possible unused gid */ if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */ cnf->min_gid = 1000; cnf->max_gid = 32000; } bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1); /* * Now, let's fill the bitmap from the password file */ SETGRENT(); while ((grp = GETGRENT()) != NULL) if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid && (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid) bm_setbit(&bm, grp->gr_gid - cnf->min_gid); ENDGRENT(); /* * Then apply the policy, with fallback to reuse if necessary */ if (cnf->reuse_gids) gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); else { gid = (gid_t) (bm_lastset(&bm) + 1); if (!bm_isset(&bm, gid)) gid += cnf->min_gid; else gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); } /* * Another sanity check */ if (gid < cnf->min_gid || gid > cnf->max_gid) errx(EX_SOFTWARE, "unable to allocate a new gid - range fully " "used"); bm_dealloc(&bm); return (gid); } static int print_group(struct group * grp, bool pretty) { char *buf = NULL; int i; if (pretty) { printf("Group Name: %-15s #%lu\n" " Members: ", grp->gr_name, (long) grp->gr_gid); if (grp->gr_mem != NULL) { for (i = 0; grp->gr_mem[i]; i++) printf("%s%s", i ? "," : "", grp->gr_mem[i]); } fputs("\n\n", stdout); return (EXIT_SUCCESS); } buf = gr_make(grp); printf("%s\n", buf); free(buf); return (EXIT_SUCCESS); } int pw_group_next(int argc, char **argv, char *arg1 __unused) { struct userconf *cnf; const char *cfg = NULL; int ch; bool quiet = false; while ((ch = getopt(argc, argv, "C:q")) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; default: exit(EX_USAGE); } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); cnf = get_userconfig(cfg); return (pw_groupnext(cnf, quiet)); } int pw_group_show(int argc, char **argv, char *arg1) { struct group *grp = NULL; char *name = NULL; intmax_t id = -1; int ch; bool all, force, quiet, pretty; all = force = quiet = pretty = false; struct group fakegroup = { "nogroup", "*", -1, NULL }; if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, GID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, "C:qn:g:FPa")) != -1) { switch (ch) { case 'C': /* ignore compatibility */ break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'g': id = pw_checkid(optarg, GID_MAX); break; case 'F': force = true; break; case 'P': pretty = true; break; case 'a': all = true; break; default: exit(EX_USAGE); } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); if (all) { SETGRENT(); while ((grp = GETGRENT()) != NULL) print_group(grp, pretty); ENDGRENT(); return (EXIT_SUCCESS); } grp = getgroup(name, id, !force); if (grp == NULL) grp = &fakegroup; return (print_group(grp, pretty)); } int pw_group_del(int argc, char **argv, char *arg1) { struct userconf *cnf = NULL; struct group *grp = NULL; char *name; const char *cfg = NULL; intmax_t id = -1; int ch, rc; bool quiet = false; bool nis = false; if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, GID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, "C:qn:g:Y")) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'g': id = pw_checkid(optarg, GID_MAX); break; case 'Y': nis = true; break; default: exit(EX_USAGE); } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); grp = getgroup(name, id, true); cnf = get_userconfig(cfg); rc = delgrent(grp); if (rc == -1) err(EX_IOERR, "group '%s' not available (NIS?)", name); else if (rc != 0) err(EX_IOERR, "group update"); pw_log(cnf, M_DELETE, W_GROUP, "%s(%ju) removed", name, (uintmax_t)id); if (nis && nis_update() == 0) pw_log(cnf, M_DELETE, W_GROUP, "NIS maps updated"); return (EXIT_SUCCESS); } bool grp_has_member(struct group *grp, const char *name) { int j; for (j = 0; grp->gr_mem != NULL && grp->gr_mem[j] != NULL; j++) if (strcmp(grp->gr_mem[j], name) == 0) return (true); return (false); } static void grp_add_members(struct group **grp, char *members) { struct passwd *pwd; char *p; char tok[] = ", \t"; if (members == NULL) return; for (p = strtok(members, tok); p != NULL; p = strtok(NULL, tok)) { pwd = lookup_pwent(p); if (grp_has_member(*grp, pwd->pw_name)) continue; *grp = gr_add(*grp, pwd->pw_name); } } int groupadd(struct userconf *cnf, char *name, gid_t id, char *members, int fd, bool dryrun, bool pretty, bool precrypted) { struct group *grp; int rc; struct group fakegroup = { "nogroup", "*", -1, NULL }; grp = &fakegroup; grp->gr_name = pw_checkname(name, 0); grp->gr_passwd = "*"; grp->gr_gid = gr_gidpolicy(cnf, id); grp->gr_mem = NULL; /* * This allows us to set a group password Group passwords is an * antique idea, rarely used and insecure (no secure database) Should * be discouraged, but it is apparently still supported by some * software. */ grp_set_passwd(grp, false, fd, precrypted); grp_add_members(&grp, members); if (dryrun) return (print_group(grp, pretty)); if ((rc = addgrent(grp)) != 0) { if (rc == -1) errx(EX_IOERR, "group '%s' already exists", grp->gr_name); else err(EX_IOERR, "group update"); } pw_log(cnf, M_ADD, W_GROUP, "%s(%ju)", grp->gr_name, (uintmax_t)grp->gr_gid); return (EXIT_SUCCESS); } int pw_group_add(int argc, char **argv, char *arg1) { struct userconf *cnf = NULL; char *name = NULL; char *members = NULL; const char *cfg = NULL; intmax_t id = -1; int ch, rc, fd = -1; bool quiet, precrypted, dryrun, pretty, nis; quiet = precrypted = dryrun = pretty = nis = false; if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, GID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, "C:qn:g:h:H:M:oNPY")) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'g': id = pw_checkid(optarg, GID_MAX); break; case 'H': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); precrypted = true; if (fd == '-') errx(EX_USAGE, "-H expects a file descriptor"); break; case 'h': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); break; case 'M': members = optarg; break; case 'o': conf.checkduplicate = false; break; case 'N': dryrun = true; break; case 'P': pretty = true; break; case 'Y': nis = true; break; default: exit(EX_USAGE); } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); if (name == NULL) errx(EX_DATAERR, "group name required"); if (GETGRNAM(name) != NULL) errx(EX_DATAERR, "group name `%s' already exists", name); cnf = get_userconfig(cfg); rc = groupadd(cnf, name, gr_gidpolicy(cnf, id), members, fd, dryrun, pretty, precrypted); if (nis && rc == EXIT_SUCCESS && nis_update() == 0) pw_log(cnf, M_ADD, W_GROUP, "NIS maps updated"); return (rc); } int pw_group_mod(int argc, char **argv, char *arg1) { struct userconf *cnf; struct group *grp = NULL; const char *cfg = NULL; char *oldmembers = NULL; char *members = NULL; char *newmembers = NULL; char *newname = NULL; char *name = NULL; intmax_t id = -1; int ch, rc, fd = -1; bool quiet, pretty, dryrun, nis, precrypted; quiet = pretty = dryrun = nis = precrypted = false; if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, GID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, "C:qn:d:g:l:h:H:M:m:NPY")) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'g': id = pw_checkid(optarg, GID_MAX); break; case 'd': oldmembers = optarg; break; case 'l': newname = optarg; break; case 'H': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); precrypted = true; if (fd == '-') errx(EX_USAGE, "-H expects a file descriptor"); break; case 'h': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); break; case 'M': members = optarg; break; case 'm': newmembers = optarg; break; case 'N': dryrun = true; break; case 'P': pretty = true; break; case 'Y': nis = true; break; default: exit(EX_USAGE); } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); cnf = get_userconfig(cfg); grp = getgroup(name, id, true); if (name == NULL) name = grp->gr_name; if (id > 0) grp->gr_gid = id; if (newname != NULL) grp->gr_name = pw_checkname(newname, 0); grp_set_passwd(grp, true, fd, precrypted); /* * Keep the same logic as old code for now: * if -M is passed, -d and -m are ignored * then id -d, -m is ignored * last is -m */ if (members) { grp->gr_mem = NULL; grp_add_members(&grp, members); } else if (oldmembers) { delete_members(grp, oldmembers); } else if (newmembers) { grp_add_members(&grp, newmembers); } if (dryrun) { print_group(grp, pretty); return (EXIT_SUCCESS); } if ((rc = chggrent(name, grp)) != 0) { if (rc == -1) errx(EX_IOERR, "group '%s' not available (NIS?)", grp->gr_name); else err(EX_IOERR, "group update"); } if (newname) name = newname; /* grp may have been invalidated */ if ((grp = GETGRNAM(name)) == NULL) errx(EX_SOFTWARE, "group disappeared during update"); pw_log(cnf, M_UPDATE, W_GROUP, "%s(%ju)", grp->gr_name, (uintmax_t)grp->gr_gid); if (nis && nis_update() == 0) pw_log(cnf, M_UPDATE, W_GROUP, "NIS maps updated"); return (EXIT_SUCCESS); } diff --git a/usr.sbin/pw/pw_log.c b/usr.sbin/pw/pw_log.c index 1dbffc8b5d9d..43dd4207ba29 100644 --- a/usr.sbin/pw/pw_log.c +++ b/usr.sbin/pw/pw_log.c @@ -1,120 +1,115 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include "pw.h" static FILE *logfile = NULL; void pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...) { va_list argp; time_t now; const char *cp, *name; struct tm *t; int fd, i, rlen; char nfmt[256], sname[32]; if (cnf->logfile == NULL || cnf->logfile[0] == '\0') { return; } if (logfile == NULL) { /* With umask==0 we need to control file access modes on create */ fd = open(cnf->logfile, O_WRONLY | O_CREAT | O_APPEND, 0600); if (fd == -1) { return; } logfile = fdopen(fd, "a"); if (logfile == NULL) { return; } } if ((name = getenv("LOGNAME")) == NULL && (name = getenv("USER")) == NULL) { strcpy(sname, "unknown"); } else { /* * Since "name" will be embedded in a printf-like format, * we must sanitize it: * * Limit its length so other information in the message * is not truncated * * Squeeze out embedded whitespace for the benefit of * log file parsers * * Escape embedded % characters with another % */ for (i = 0, cp = name; *cp != '\0' && i < (int)sizeof(sname) - 1; cp++) { if (*cp == '%') { if (i < (int)sizeof(sname) - 2) { sname[i++] = '%'; sname[i++] = '%'; } else { break; } } else if (!isspace(*cp)) { sname[i++] = *cp; } /* else do nothing */ } if (i == 0) { strcpy(sname, "unknown"); } else { sname[i] = '\0'; } } now = time(NULL); t = localtime(&now); /* ISO 8601 International Standard Date format */ strftime(nfmt, sizeof nfmt, "%Y-%m-%d %T ", t); rlen = sizeof(nfmt) - strlen(nfmt); if (rlen <= 0 || snprintf(nfmt + strlen(nfmt), rlen, "[%s:%s%s] %s\n", sname, Which[which], Modes[mode], fmt) >= rlen) { warnx("log format overflow, user name=%s", sname); } else { va_start(argp, fmt); vfprintf(logfile, nfmt, argp); va_end(argp); fflush(logfile); } } diff --git a/usr.sbin/pw/pw_nis.c b/usr.sbin/pw/pw_nis.c index 0126990d61cb..23e82239379f 100644 --- a/usr.sbin/pw/pw_nis.c +++ b/usr.sbin/pw/pw_nis.c @@ -1,101 +1,96 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include "pw.h" static int pw_nisupdate(const char * path, struct passwd * pwd, char const * user) { int pfd, tfd; struct passwd *pw = NULL; struct passwd *old_pw = NULL; printf("===> %s\n", path); if (pwd != NULL) pw = pw_dup(pwd); if (user != NULL) old_pw = GETPWNAM(user); if (pw_init(NULL, path)) err(1,"pw_init()"); if ((pfd = pw_lock()) == -1) { pw_fini(); err(1, "pw_lock()"); } if ((tfd = pw_tmp(-1)) == -1) { pw_fini(); err(1, "pw_tmp()"); } if (pw_copy(pfd, tfd, pw, old_pw) == -1) { pw_fini(); close(tfd); err(1, "pw_copy()"); } fsync(tfd); close(tfd); if (chmod(pw_tempname(), 0644) == -1) err(1, "chmod()"); if (rename(pw_tempname(), path) == -1) err(1, "rename()"); free(pw); pw_fini(); return (0); } int addnispwent(const char *path, struct passwd * pwd) { return pw_nisupdate(path, pwd, NULL); } int chgnispwent(const char *path, char const * login, struct passwd * pwd) { return pw_nisupdate(path, pwd, login); } int delnispwent(const char *path, const char *login) { return pw_nisupdate(path, NULL, login); } diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c index 3e5a9841c5f3..6875d931a1d2 100644 --- a/usr.sbin/pw/pw_user.c +++ b/usr.sbin/pw/pw_user.c @@ -1,1819 +1,1814 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pw.h" #include "bitmap.h" #include "psdate.h" #define LOGNAMESIZE (MAXLOGNAME-1) extern char **environ; static char locked_str[] = "*LOCKED*"; static struct passwd fakeuser = { "nouser", "*", -1, -1, 0, "", "User &", "/nonexistent", "/bin/sh", 0, 0 }; static int print_user(struct passwd *pwd, bool pretty, bool v7); static uid_t pw_uidpolicy(struct userconf *cnf, intmax_t id); static uid_t pw_gidpolicy(struct userconf *cnf, char *grname, char *nam, gid_t prefer, bool dryrun); static char *pw_homepolicy(struct userconf * cnf, char *homedir, const char *user); static char *pw_shellpolicy(struct userconf * cnf); static char *pw_password(struct userconf * cnf, char const * user); static char *shell_path(char const * path, char *shells[], char *sh); static void rmat(uid_t uid); static void mkdir_home_parents(int dfd, const char *dir) { struct stat st; char *dirs, *tmp; if (*dir != '/') errx(EX_DATAERR, "invalid base directory for home '%s'", dir); dir++; if (fstatat(dfd, dir, &st, 0) != -1) { if (S_ISDIR(st.st_mode)) return; errx(EX_OSFILE, "root home `/%s' is not a directory", dir); } dirs = strdup(dir); if (dirs == NULL) errx(EX_UNAVAILABLE, "out of memory"); tmp = strrchr(dirs, '/'); if (tmp == NULL) { free(dirs); return; } tmp[0] = '\0'; tmp = dirs; if (fstatat(dfd, dirs, &st, 0) == -1) { while ((tmp = strchr(tmp + 1, '/')) != NULL) { *tmp = '\0'; if (fstatat(dfd, dirs, &st, 0) == -1) { if (mkdirat(dfd, dirs, _DEF_DIRMODE) == -1) err(EX_OSFILE, "'%s' (home parent) is not a directory", dirs); } *tmp = '/'; } } if (fstatat(dfd, dirs, &st, 0) == -1) { if (mkdirat(dfd, dirs, _DEF_DIRMODE) == -1) err(EX_OSFILE, "'%s' (home parent) is not a directory", dirs); fchownat(dfd, dirs, 0, 0, 0); } free(dirs); } static void create_and_populate_homedir(struct userconf *cnf, struct passwd *pwd, const char *skeldir, mode_t homemode, bool update) { int skelfd = -1; /* Create home parents directories */ mkdir_home_parents(conf.rootfd, pwd->pw_dir); if (skeldir != NULL && *skeldir != '\0') { if (*skeldir == '/') skeldir++; skelfd = openat(conf.rootfd, skeldir, O_DIRECTORY|O_CLOEXEC); } copymkdir(conf.rootfd, pwd->pw_dir, skelfd, homemode, pwd->pw_uid, pwd->pw_gid, 0); pw_log(cnf, update ? M_UPDATE : M_ADD, W_USER, "%s(%ju) home %s made", pwd->pw_name, (uintmax_t)pwd->pw_uid, pwd->pw_dir); } static int pw_set_passwd(struct passwd *pwd, int fd, bool precrypted, bool update) { int b, istty; struct termios t, n; login_cap_t *lc; char line[_PASSWORD_LEN+1]; char *p; if (fd == '-') { if (!pwd->pw_passwd || *pwd->pw_passwd != '*') { pwd->pw_passwd = "*"; /* No access */ return (1); } return (0); } if ((istty = isatty(fd))) { if (tcgetattr(fd, &t) == -1) istty = 0; else { n = t; n.c_lflag &= ~(ECHO); tcsetattr(fd, TCSANOW, &n); printf("%s%spassword for user %s:", update ? "new " : "", precrypted ? "encrypted " : "", pwd->pw_name); fflush(stdout); } } b = read(fd, line, sizeof(line) - 1); if (istty) { /* Restore state */ tcsetattr(fd, TCSANOW, &t); fputc('\n', stdout); fflush(stdout); } if (b < 0) err(EX_IOERR, "-%c file descriptor", precrypted ? 'H' : 'h'); line[b] = '\0'; if ((p = strpbrk(line, "\r\n")) != NULL) *p = '\0'; if (!*line) errx(EX_DATAERR, "empty password read on file descriptor %d", fd); if (precrypted) { if (strchr(line, ':') != NULL) errx(EX_DATAERR, "bad encrypted password"); pwd->pw_passwd = strdup(line); } else { lc = login_getpwclass(pwd); if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) warn("setting crypt(3) format"); login_close(lc); pwd->pw_passwd = pw_pwcrypt(line); } return (1); } static void perform_chgpwent(const char *name, struct passwd *pwd, char *nispasswd) { int rc; struct passwd *nispwd; /* duplicate for nis so that chgpwent is not modifying before NIS */ if (nispasswd && *nispasswd == '/') nispwd = pw_dup(pwd); rc = chgpwent(name, pwd); if (rc == -1) errx(EX_IOERR, "user '%s' does not exist (NIS?)", pwd->pw_name); else if (rc != 0) err(EX_IOERR, "passwd file update"); if (nispasswd && *nispasswd == '/') { rc = chgnispwent(nispasswd, name, nispwd); if (rc == -1) warn("User '%s' not found in NIS passwd", pwd->pw_name); else if (rc != 0) warn("NIS passwd update"); /* NOTE: NIS-only update errors are not fatal */ } } /* * The M_LOCK and M_UNLOCK functions simply add or remove * a "*LOCKED*" prefix from in front of the password to * prevent it decoding correctly, and therefore prevents * access. Of course, this only prevents access via * password authentication (not ssh, kerberos or any * other method that does not use the UNIX password) but * that is a known limitation. */ static int pw_userlock(char *arg1, int mode) { struct passwd *pwd = NULL; char *passtmp = NULL; char *name; bool locked = false; uid_t id = (uid_t)-1; if (geteuid() != 0) errx(EX_NOPERM, "you must be root"); if (arg1 == NULL) errx(EX_DATAERR, "username or id required"); name = arg1; if (arg1[strspn(name, "0123456789")] == '\0') id = pw_checkid(name, UID_MAX); pwd = GETPWNAM(pw_checkname(name, 0)); if (pwd == NULL && id != (uid_t)-1) { pwd = GETPWUID(id); if (pwd != NULL) name = pwd->pw_name; } if (pwd == NULL) { if (id == (uid_t)-1) errx(EX_NOUSER, "no such name or uid `%ju'", (uintmax_t) id); errx(EX_NOUSER, "no such user `%s'", name); } if (name == NULL) name = pwd->pw_name; if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str) -1) == 0) locked = true; if (mode == M_LOCK && locked) errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name); if (mode == M_UNLOCK && !locked) errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name); if (mode == M_LOCK) { asprintf(&passtmp, "%s%s", locked_str, pwd->pw_passwd); if (passtmp == NULL) /* disaster */ errx(EX_UNAVAILABLE, "out of memory"); pwd->pw_passwd = passtmp; } else { pwd->pw_passwd += sizeof(locked_str)-1; } perform_chgpwent(name, pwd, NULL); free(passtmp); return (EXIT_SUCCESS); } static uid_t pw_uidpolicy(struct userconf * cnf, intmax_t id) { struct passwd *pwd; struct bitmap bm; uid_t uid = (uid_t) - 1; /* * Check the given uid, if any */ if (id >= 0) { uid = (uid_t) id; if ((pwd = GETPWUID(uid)) != NULL && conf.checkduplicate) errx(EX_DATAERR, "uid `%ju' has already been allocated", (uintmax_t)pwd->pw_uid); return (uid); } /* * We need to allocate the next available uid under one of * two policies a) Grab the first unused uid b) Grab the * highest possible unused uid */ if (cnf->min_uid >= cnf->max_uid) { /* Sanity * claus^H^H^H^Hheck */ cnf->min_uid = 1000; cnf->max_uid = 32000; } bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1); /* * Now, let's fill the bitmap from the password file */ SETPWENT(); while ((pwd = GETPWENT()) != NULL) if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid) bm_setbit(&bm, pwd->pw_uid - cnf->min_uid); ENDPWENT(); /* * Then apply the policy, with fallback to reuse if necessary */ if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid) uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid); /* * Another sanity check */ if (uid < cnf->min_uid || uid > cnf->max_uid) errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used"); bm_dealloc(&bm); return (uid); } static uid_t pw_gidpolicy(struct userconf *cnf, char *grname, char *nam, gid_t prefer, bool dryrun) { struct group *grp; gid_t gid = (uid_t) - 1; /* * Check the given gid, if any */ SETGRENT(); if (grname) { if ((grp = GETGRNAM(grname)) == NULL) { gid = pw_checkid(grname, GID_MAX); grp = GETGRGID(gid); } gid = grp->gr_gid; } else if ((grp = GETGRNAM(nam)) != NULL) { gid = grp->gr_gid; /* Already created? Use it anyway... */ } else { intmax_t grid = -1; /* * We need to auto-create a group with the user's name. We * can send all the appropriate output to our sister routine * bit first see if we can create a group with gid==uid so we * can keep the user and group ids in sync. We purposely do * NOT check the gid range if we can force the sync. If the * user's name dups an existing group, then the group add * function will happily handle that case for us and exit. */ if (GETGRGID(prefer) == NULL) grid = prefer; if (dryrun) { gid = pw_groupnext(cnf, true); } else { if (grid == -1) grid = pw_groupnext(cnf, true); groupadd(cnf, nam, grid, NULL, -1, false, false, false); if ((grp = GETGRNAM(nam)) != NULL) gid = grp->gr_gid; } } ENDGRENT(); return (gid); } static char * pw_homepolicy(struct userconf * cnf, char *homedir, const char *user) { static char home[128]; if (homedir) return (homedir); if (cnf->home == NULL || *cnf->home == '\0') errx(EX_CONFIG, "no base home directory set"); snprintf(home, sizeof(home), "%s/%s", cnf->home, user); return (home); } static char * shell_path(char const * path, char *shells[], char *sh) { if (sh != NULL && (*sh == '/' || *sh == '\0')) return sh; /* specified full path or forced none */ else { char *p; char paths[_UC_MAXLINE]; /* * We need to search paths */ strlcpy(paths, path, sizeof(paths)); for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) { int i; static char shellpath[256]; if (sh != NULL) { snprintf(shellpath, sizeof(shellpath), "%s/%s", p, sh); if (access(shellpath, X_OK) == 0) return shellpath; } else for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) { snprintf(shellpath, sizeof(shellpath), "%s/%s", p, shells[i]); if (access(shellpath, X_OK) == 0) return shellpath; } } if (sh == NULL) errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh); errx(EX_CONFIG, "no default shell available or defined"); return NULL; } } static char * pw_shellpolicy(struct userconf * cnf) { return shell_path(cnf->shelldir, cnf->shells, cnf->shell_default); } #define SALTSIZE 32 static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./"; char * pw_pwcrypt(char *password) { int i; char salt[SALTSIZE + 1]; char *cryptpw; static char buf[256]; size_t pwlen; /* * Calculate a salt value */ for (i = 0; i < SALTSIZE; i++) salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)]; salt[SALTSIZE] = '\0'; cryptpw = crypt(password, salt); if (cryptpw == NULL) errx(EX_CONFIG, "crypt(3) failure"); pwlen = strlcpy(buf, cryptpw, sizeof(buf)); assert(pwlen < sizeof(buf)); return (buf); } static char * pw_password(struct userconf * cnf, char const * user) { int i, l; char pwbuf[32]; switch (cnf->default_password) { case P_NONE: /* No password at all! */ return ""; case P_RANDOM: /* Random password */ l = (arc4random() % 8 + 8); /* 8 - 16 chars */ for (i = 0; i < l; i++) pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)]; pwbuf[i] = '\0'; /* * We give this information back to the user */ if (conf.fd == -1) { if (isatty(STDOUT_FILENO)) printf("Password for '%s' is: ", user); printf("%s\n", pwbuf); fflush(stdout); } break; case P_YES: /* user's name */ strlcpy(pwbuf, user, sizeof(pwbuf)); break; case P_NO: /* No login - default */ /* FALLTHROUGH */ default: return "*"; } return pw_pwcrypt(pwbuf); } static int print_user(struct passwd * pwd, bool pretty, bool v7) { int j; char *p; struct group *grp = GETGRGID(pwd->pw_gid); char uname[60] = "User &", office[60] = "[None]", wphone[60] = "[None]", hphone[60] = "[None]"; char acexpire[32] = "[None]", pwexpire[32] = "[None]"; struct tm * tptr; if (!pretty) { p = v7 ? pw_make_v7(pwd) : pw_make(pwd); printf("%s\n", p); free(p); return (EXIT_SUCCESS); } if ((p = strtok(pwd->pw_gecos, ",")) != NULL) { strlcpy(uname, p, sizeof(uname)); if ((p = strtok(NULL, ",")) != NULL) { strlcpy(office, p, sizeof(office)); if ((p = strtok(NULL, ",")) != NULL) { strlcpy(wphone, p, sizeof(wphone)); if ((p = strtok(NULL, "")) != NULL) { strlcpy(hphone, p, sizeof(hphone)); } } } } /* * Handle '&' in gecos field */ if ((p = strchr(uname, '&')) != NULL) { int l = strlen(pwd->pw_name); int m = strlen(p); memmove(p + l, p + 1, m); memmove(p, pwd->pw_name, l); *p = (char) toupper((unsigned char)*p); } if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL) strftime(acexpire, sizeof acexpire, "%c", tptr); if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL) strftime(pwexpire, sizeof pwexpire, "%c", tptr); printf("Login Name: %-15s #%-12ju Group: %-15s #%ju\n" " Full Name: %s\n" " Home: %-26.26s Class: %s\n" " Shell: %-26.26s Office: %s\n" "Work Phone: %-26.26s Home Phone: %s\n" "Acc Expire: %-26.26s Pwd Expire: %s\n", pwd->pw_name, (uintmax_t)pwd->pw_uid, grp ? grp->gr_name : "(invalid)", (uintmax_t)pwd->pw_gid, uname, pwd->pw_dir, pwd->pw_class, pwd->pw_shell, office, wphone, hphone, acexpire, pwexpire); SETGRENT(); j = 0; while ((grp=GETGRENT()) != NULL) { int i = 0; if (grp->gr_mem != NULL) { while (grp->gr_mem[i] != NULL) { if (strcmp(grp->gr_mem[i], pwd->pw_name)==0) { printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name); break; } ++i; } } } ENDGRENT(); printf("%s", j ? "\n" : ""); return (EXIT_SUCCESS); } char * pw_checkname(char *name, int gecos) { char showch[8]; const char *badchars, *ch, *showtype; int reject; ch = name; reject = 0; if (gecos) { /* See if the name is valid as a gecos (comment) field. */ badchars = ":"; showtype = "gecos field"; } else { /* See if the name is valid as a userid or group. */ badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\";"; showtype = "userid/group name"; /* Userids and groups can not have a leading '-'. */ if (*ch == '-') reject = 1; } if (!reject) { while (*ch) { if (strchr(badchars, *ch) != NULL || (!gecos && *ch < ' ') || *ch == 127) { reject = 1; break; } /* 8-bit characters are only allowed in GECOS fields */ if (!gecos && (*ch & 0x80)) { reject = 1; break; } ch++; } } /* * A `$' is allowed as the final character for userids and groups, * mainly for the benefit of samba. */ if (reject && !gecos) { if (*ch == '$' && *(ch + 1) == '\0') { reject = 0; ch++; } } if (reject) { snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127) ? "`%c'" : "0x%02x", *ch); errx(EX_DATAERR, "invalid character %s at position %td in %s", showch, (ch - name), showtype); } if (!gecos && (ch - name) > LOGNAMESIZE) errx(EX_USAGE, "name too long `%s' (max is %d)", name, LOGNAMESIZE); return (name); } static void rmat(uid_t uid) { DIR *d = opendir("/var/at/jobs"); if (d != NULL) { struct dirent *e; while ((e = readdir(d)) != NULL) { struct stat st; if (strncmp(e->d_name, ".lock", 5) != 0 && stat(e->d_name, &st) == 0 && !S_ISDIR(st.st_mode) && st.st_uid == uid) { const char *argv[] = { "/usr/sbin/atrm", e->d_name, NULL }; if (posix_spawn(NULL, argv[0], NULL, NULL, (char *const *) argv, environ)) { warn("Failed to execute '%s %s'", argv[0], argv[1]); } } } closedir(d); } } int pw_user_next(int argc, char **argv, char *name __unused) { struct userconf *cnf = NULL; const char *cfg = NULL; int ch; bool quiet = false; uid_t next; while ((ch = getopt(argc, argv, "C:q")) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; default: exit(EX_USAGE); } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); cnf = get_userconfig(cfg); next = pw_uidpolicy(cnf, -1); printf("%ju:", (uintmax_t)next); pw_groupnext(cnf, quiet); return (EXIT_SUCCESS); } int pw_user_show(int argc, char **argv, char *arg1) { struct passwd *pwd = NULL; char *name = NULL; intmax_t id = -1; int ch; bool all = false; bool pretty = false; bool force = false; bool v7 = false; bool quiet = false; if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, UID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, "C:qn:u:FPa7")) != -1) { switch (ch) { case 'C': /* ignore compatibility */ break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'u': id = pw_checkid(optarg, UID_MAX); break; case 'F': force = true; break; case 'P': pretty = true; break; case 'a': all = true; break; case '7': v7 = true; break; default: exit(EX_USAGE); } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); if (all) { SETPWENT(); while ((pwd = GETPWENT()) != NULL) print_user(pwd, pretty, v7); ENDPWENT(); return (EXIT_SUCCESS); } if (id < 0 && name == NULL) errx(EX_DATAERR, "username or id required"); pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id); if (pwd == NULL) { if (force) { pwd = &fakeuser; } else { if (name == NULL) errx(EX_NOUSER, "no such uid `%ju'", (uintmax_t) id); errx(EX_NOUSER, "no such user `%s'", name); } } return (print_user(pwd, pretty, v7)); } int pw_user_del(int argc, char **argv, char *arg1) { struct userconf *cnf = NULL; struct passwd *pwd = NULL; struct group *gr, *grp; char *name = NULL; char grname[MAXLOGNAME]; char *nispasswd = NULL; char file[MAXPATHLEN]; char home[MAXPATHLEN]; const char *cfg = NULL; struct stat st; intmax_t id = -1; int ch, rc; bool nis = false; bool deletehome = false; bool quiet = false; if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, UID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, "C:qn:u:rYy:")) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'u': id = pw_checkid(optarg, UID_MAX); break; case 'r': deletehome = true; break; case 'y': nispasswd = optarg; break; case 'Y': nis = true; break; default: exit(EX_USAGE); } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); if (id < 0 && name == NULL) errx(EX_DATAERR, "username or id required"); cnf = get_userconfig(cfg); if (nispasswd == NULL) nispasswd = cnf->nispasswd; pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id); if (pwd == NULL) { if (name == NULL) errx(EX_NOUSER, "no such uid `%ju'", (uintmax_t) id); errx(EX_NOUSER, "no such user `%s'", name); } if (PWF._altdir == PWF_REGULAR && ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) { if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) { if (!nis && nispasswd && *nispasswd != '/') errx(EX_NOUSER, "Cannot remove NIS user `%s'", name); } else { errx(EX_NOUSER, "Cannot remove non local user `%s'", name); } } id = pwd->pw_uid; if (name == NULL) name = pwd->pw_name; if (strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "cannot remove user 'root'"); if (!PWALTDIR()) { /* Remove crontabs */ snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name); if (access(file, F_OK) == 0) { const char *argv[] = { "crontab", "-u", pwd->pw_name, "-r", NULL }; if (posix_spawnp(NULL, argv[0], NULL, NULL, (char *const *) argv, environ)) { warn("Failed to execute '%s %s'", argv[0], argv[1]); } } } /* * Save these for later, since contents of pwd may be * invalidated by deletion */ snprintf(file, sizeof(file), "%s/%s", _PATH_MAILDIR, pwd->pw_name); strlcpy(home, pwd->pw_dir, sizeof(home)); gr = GETGRGID(pwd->pw_gid); if (gr != NULL) strlcpy(grname, gr->gr_name, LOGNAMESIZE); else grname[0] = '\0'; rc = delpwent(pwd); if (rc == -1) err(EX_IOERR, "user '%s' does not exist", pwd->pw_name); else if (rc != 0) err(EX_IOERR, "passwd update"); if (nis && nispasswd && *nispasswd=='/') { rc = delnispwent(nispasswd, name); if (rc == -1) warnx("WARNING: user '%s' does not exist in NIS passwd", pwd->pw_name); else if (rc != 0) warn("WARNING: NIS passwd update"); } grp = GETGRNAM(name); if (grp != NULL && (grp->gr_mem == NULL || *grp->gr_mem == NULL) && strcmp(name, grname) == 0) delgrent(GETGRNAM(name)); SETGRENT(); while ((grp = GETGRENT()) != NULL) { int i, j; char group[MAXLOGNAME]; if (grp->gr_mem == NULL) continue; for (i = 0; grp->gr_mem[i] != NULL; i++) { if (strcmp(grp->gr_mem[i], name) != 0) continue; for (j = i; grp->gr_mem[j] != NULL; j++) grp->gr_mem[j] = grp->gr_mem[j+1]; strlcpy(group, grp->gr_name, MAXLOGNAME); chggrent(group, grp); } } ENDGRENT(); pw_log(cnf, M_DELETE, W_USER, "%s(%ju) account removed", name, (uintmax_t)id); /* Remove mail file */ if (PWALTDIR() != PWF_ALT) unlinkat(conf.rootfd, file + 1, 0); /* Remove at jobs */ if (!PWALTDIR() && getpwuid(id) == NULL) rmat(id); /* Remove home directory and contents */ if (PWALTDIR() != PWF_ALT && deletehome && *home == '/' && GETPWUID(id) == NULL && fstatat(conf.rootfd, home + 1, &st, 0) != -1) { rm_r(conf.rootfd, home, id); pw_log(cnf, M_DELETE, W_USER, "%s(%ju) home '%s' %s" "removed", name, (uintmax_t)id, home, fstatat(conf.rootfd, home + 1, &st, 0) == -1 ? "" : "not " "completely "); } return (EXIT_SUCCESS); } int pw_user_lock(int argc, char **argv, char *arg1) { int ch; while ((ch = getopt(argc, argv, "Cq")) != -1) { switch (ch) { case 'C': case 'q': /* compatibility */ break; default: exit(EX_USAGE); } } return (pw_userlock(arg1, M_LOCK)); } int pw_user_unlock(int argc, char **argv, char *arg1) { int ch; while ((ch = getopt(argc, argv, "Cq")) != -1) { switch (ch) { case 'C': case 'q': /* compatibility */ break; default: exit(EX_USAGE); } } return (pw_userlock(arg1, M_UNLOCK)); } static struct group * group_from_name_or_id(char *name) { const char *errstr = NULL; struct group *grp; uintmax_t id; if ((grp = GETGRNAM(name)) == NULL) { id = strtounum(name, 0, GID_MAX, &errstr); if (errstr) errx(EX_NOUSER, "group `%s' does not exist", name); grp = GETGRGID(id); if (grp == NULL) errx(EX_NOUSER, "group `%s' does not exist", name); } return (grp); } static void split_groups(StringList **groups, char *groupsstr) { struct group *grp; char *p; char tok[] = ", \t"; if (*groups == NULL) *groups = sl_init(); for (p = strtok(groupsstr, tok); p != NULL; p = strtok(NULL, tok)) { grp = group_from_name_or_id(p); sl_add(*groups, newstr(grp->gr_name)); } } static void validate_grname(struct userconf *cnf, char *group) { struct group *grp; if (group == NULL || *group == '\0') { cnf->default_group = ""; return; } grp = group_from_name_or_id(group); cnf->default_group = newstr(grp->gr_name); } static mode_t validate_mode(char *mode) { mode_t m; void *set; if ((set = setmode(mode)) == NULL) errx(EX_DATAERR, "invalid directory creation mode '%s'", mode); m = getmode(set, _DEF_DIRMODE); free(set); return (m); } static long validate_expire(char *str, int opt) { if (!numerics(str)) errx(EX_DATAERR, "-%c argument must be numeric " "when setting defaults: %s", (char)opt, str); return strtol(str, NULL, 0); } static void mix_config(struct userconf *cmdcnf, struct userconf *cfg) { if (cmdcnf->default_password < 0) cmdcnf->default_password = cfg->default_password; if (cmdcnf->reuse_uids == 0) cmdcnf->reuse_uids = cfg->reuse_uids; if (cmdcnf->reuse_gids == 0) cmdcnf->reuse_gids = cfg->reuse_gids; if (cmdcnf->nispasswd == NULL) cmdcnf->nispasswd = cfg->nispasswd; if (cmdcnf->dotdir == NULL) cmdcnf->dotdir = cfg->dotdir; if (cmdcnf->newmail == NULL) cmdcnf->newmail = cfg->newmail; if (cmdcnf->logfile == NULL) cmdcnf->logfile = cfg->logfile; if (cmdcnf->home == NULL) cmdcnf->home = cfg->home; if (cmdcnf->homemode == 0) cmdcnf->homemode = cfg->homemode; if (cmdcnf->shelldir == NULL) cmdcnf->shelldir = cfg->shelldir; if (cmdcnf->shells == NULL) cmdcnf->shells = cfg->shells; if (cmdcnf->shell_default == NULL) cmdcnf->shell_default = cfg->shell_default; if (cmdcnf->default_group == NULL) cmdcnf->default_group = cfg->default_group; if (cmdcnf->groups == NULL) cmdcnf->groups = cfg->groups; if (cmdcnf->default_class == NULL) cmdcnf->default_class = cfg->default_class; if (cmdcnf->min_uid == 0) cmdcnf->min_uid = cfg->min_uid; if (cmdcnf->max_uid == 0) cmdcnf->max_uid = cfg->max_uid; if (cmdcnf->min_gid == 0) cmdcnf->min_gid = cfg->min_gid; if (cmdcnf->max_gid == 0) cmdcnf->max_gid = cfg->max_gid; if (cmdcnf->expire_days < 0) cmdcnf->expire_days = cfg->expire_days; if (cmdcnf->password_days < 0) cmdcnf->password_days = cfg->password_days; } int pw_user_add(int argc, char **argv, char *arg1) { struct userconf *cnf, *cmdcnf; struct passwd *pwd; struct group *grp; struct stat st; char args[] = "C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y"; char line[_PASSWORD_LEN+1], path[MAXPATHLEN]; char *gecos, *homedir, *skel, *walk, *userid, *groupid, *grname; char *default_passwd, *name, *p; const char *cfg = NULL; login_cap_t *lc; FILE *pfp, *fp; intmax_t id = -1; time_t now; int rc, ch, fd = -1; size_t i; bool dryrun, nis, pretty, quiet, createhome, precrypted, genconf; dryrun = nis = pretty = quiet = createhome = precrypted = false; genconf = false; gecos = homedir = skel = userid = groupid = default_passwd = NULL; grname = name = NULL; if ((cmdcnf = calloc(1, sizeof(struct userconf))) == NULL) err(EXIT_FAILURE, "calloc()"); cmdcnf->default_password = cmdcnf->expire_days = cmdcnf->password_days = -1; now = time(NULL); if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, UID_MAX); else name = pw_checkname(arg1, 0); } while ((ch = getopt(argc, argv, args)) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; case 'n': name = pw_checkname(optarg, 0); break; case 'u': userid = optarg; break; case 'c': gecos = pw_checkname(optarg, 1); break; case 'd': homedir = optarg; break; case 'e': if (genconf) cmdcnf->expire_days = validate_expire(optarg, ch); else cmdcnf->expire_days = parse_date(now, optarg); break; case 'p': if (genconf) cmdcnf->password_days = validate_expire(optarg, ch); else cmdcnf->password_days = parse_date(now, optarg); break; case 'g': validate_grname(cmdcnf, optarg); grname = optarg; break; case 'G': split_groups(&cmdcnf->groups, optarg); break; case 'm': createhome = true; break; case 'M': cmdcnf->homemode = validate_mode(optarg); break; case 'k': walk = skel = optarg; if (*walk == '/') walk++; if (fstatat(conf.rootfd, walk, &st, 0) == -1) errx(EX_OSFILE, "skeleton `%s' does not " "exists", skel); if (!S_ISDIR(st.st_mode)) errx(EX_OSFILE, "skeleton `%s' is not a " "directory", skel); cmdcnf->dotdir = skel; break; case 's': cmdcnf->shell_default = optarg; break; case 'o': conf.checkduplicate = false; break; case 'L': cmdcnf->default_class = pw_checkname(optarg, 0); break; case 'i': groupid = optarg; break; case 'w': default_passwd = optarg; break; case 'H': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); precrypted = true; if (fd == '-') errx(EX_USAGE, "-H expects a file descriptor"); break; case 'h': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); break; case 'D': genconf = true; break; case 'b': cmdcnf->home = optarg; break; case 'N': dryrun = true; break; case 'P': pretty = true; break; case 'y': cmdcnf->nispasswd = optarg; break; case 'Y': nis = true; break; default: exit(EX_USAGE); } } if (geteuid() != 0 && ! dryrun) errx(EX_NOPERM, "you must be root"); if (quiet) freopen(_PATH_DEVNULL, "w", stderr); cnf = get_userconfig(cfg); mix_config(cmdcnf, cnf); if (default_passwd) cmdcnf->default_password = passwd_val(default_passwd, cnf->default_password); if (genconf) { if (name != NULL) errx(EX_DATAERR, "can't combine `-D' with `-n name'"); if (userid != NULL) { if ((p = strtok(userid, ", \t")) != NULL) cmdcnf->min_uid = pw_checkid(p, UID_MAX); if (cmdcnf->min_uid == 0) cmdcnf->min_uid = 1000; if ((p = strtok(NULL, " ,\t")) != NULL) cmdcnf->max_uid = pw_checkid(p, UID_MAX); if (cmdcnf->max_uid == 0) cmdcnf->max_uid = 32000; } if (groupid != NULL) { if ((p = strtok(groupid, ", \t")) != NULL) cmdcnf->min_gid = pw_checkid(p, GID_MAX); if (cmdcnf->min_gid == 0) cmdcnf->min_gid = 1000; if ((p = strtok(NULL, " ,\t")) != NULL) cmdcnf->max_gid = pw_checkid(p, GID_MAX); if (cmdcnf->max_gid == 0) cmdcnf->max_gid = 32000; } if (write_userconfig(cmdcnf, cfg)) return (EXIT_SUCCESS); err(EX_IOERR, "config update"); } if (userid) id = pw_checkid(userid, UID_MAX); if (id < 0 && name == NULL) errx(EX_DATAERR, "user name or id required"); if (name == NULL) errx(EX_DATAERR, "login name required"); if (GETPWNAM(name) != NULL) errx(EX_DATAERR, "login name `%s' already exists", name); if (!grname) grname = cmdcnf->default_group; pwd = &fakeuser; pwd->pw_name = name; pwd->pw_class = cmdcnf->default_class ? cmdcnf->default_class : ""; pwd->pw_uid = pw_uidpolicy(cmdcnf, id); pwd->pw_gid = pw_gidpolicy(cnf, grname, pwd->pw_name, (gid_t) pwd->pw_uid, dryrun); /* cmdcnf->password_days and cmdcnf->expire_days hold unixtime here */ if (cmdcnf->password_days > 0) pwd->pw_change = cmdcnf->password_days; if (cmdcnf->expire_days > 0) pwd->pw_expire = cmdcnf->expire_days; pwd->pw_dir = pw_homepolicy(cmdcnf, homedir, pwd->pw_name); pwd->pw_shell = pw_shellpolicy(cmdcnf); lc = login_getpwclass(pwd); if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) warn("setting crypt(3) format"); login_close(lc); pwd->pw_passwd = pw_password(cmdcnf, pwd->pw_name); if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) warnx("WARNING: new account `%s' has a uid of 0 " "(superuser access!)", pwd->pw_name); if (gecos) pwd->pw_gecos = gecos; if (fd != -1) pw_set_passwd(pwd, fd, precrypted, false); if (dryrun) return (print_user(pwd, pretty, false)); if ((rc = addpwent(pwd)) != 0) { if (rc == -1) errx(EX_IOERR, "user '%s' already exists", pwd->pw_name); else if (rc != 0) err(EX_IOERR, "passwd file update"); } if (nis && cmdcnf->nispasswd && *cmdcnf->nispasswd == '/') { printf("%s\n", cmdcnf->nispasswd); rc = addnispwent(cmdcnf->nispasswd, pwd); if (rc == -1) warnx("User '%s' already exists in NIS passwd", pwd->pw_name); else if (rc != 0) warn("NIS passwd update"); /* NOTE: we treat NIS-only update errors as non-fatal */ } if (cmdcnf->groups != NULL) { for (i = 0; i < cmdcnf->groups->sl_cur; i++) { grp = GETGRNAM(cmdcnf->groups->sl_str[i]); /* gr_add doesn't check if new member is already in group */ if (grp_has_member(grp, pwd->pw_name)) continue; grp = gr_add(grp, pwd->pw_name); /* * grp can only be NULL in 2 cases: * - the new member is already a member * - a problem with memory occurs * in both cases we want to skip now. */ if (grp == NULL) continue; chggrent(grp->gr_name, grp); free(grp); } } pwd = GETPWNAM(name); if (pwd == NULL) errx(EX_NOUSER, "user '%s' disappeared during update", name); grp = GETGRGID(pwd->pw_gid); pw_log(cnf, M_ADD, W_USER, "%s(%ju):%s(%ju):%s:%s:%s", pwd->pw_name, (uintmax_t)pwd->pw_uid, grp ? grp->gr_name : "unknown", (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1), pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); /* * let's touch and chown the user's mail file. This is not * strictly necessary under BSD with a 0755 maildir but it also * doesn't hurt anything to create the empty mailfile */ if (PWALTDIR() != PWF_ALT) { snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, pwd->pw_name); /* Preserve contents & mtime */ close(openat(conf.rootfd, path +1, O_RDWR | O_CREAT, 0600)); fchownat(conf.rootfd, path + 1, pwd->pw_uid, pwd->pw_gid, AT_SYMLINK_NOFOLLOW); } /* * Let's create and populate the user's home directory. Note * that this also `works' for editing users if -m is used, but * existing files will *not* be overwritten. */ if (PWALTDIR() != PWF_ALT && createhome && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) create_and_populate_homedir(cmdcnf, pwd, cmdcnf->dotdir, cmdcnf->homemode, false); if (!PWALTDIR() && cmdcnf->newmail && *cmdcnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) { if ((pfp = popen(_PATH_SENDMAIL " -t", "w")) == NULL) warn("sendmail"); else { fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name); while (fgets(line, sizeof(line), fp) != NULL) { /* Do substitutions? */ fputs(line, pfp); } pclose(pfp); pw_log(cnf, M_ADD, W_USER, "%s(%ju) new user mail sent", pwd->pw_name, (uintmax_t)pwd->pw_uid); } fclose(fp); } if (nis && nis_update() == 0) pw_log(cnf, M_ADD, W_USER, "NIS maps updated"); return (EXIT_SUCCESS); } int pw_user_mod(int argc, char **argv, char *arg1) { struct userconf *cnf; struct passwd *pwd; struct group *grp; StringList *groups = NULL; char args[] = "C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:NPYy:"; const char *cfg = NULL; char *gecos, *homedir, *grname, *name, *newname, *walk, *skel, *shell; char *passwd, *class, *nispasswd; login_cap_t *lc; struct stat st; intmax_t id = -1; int ch, fd = -1; size_t i, j; bool quiet, createhome, pretty, dryrun, nis, edited; bool precrypted; mode_t homemode = 0; time_t expire_time, password_time, now; expire_time = password_time = -1; gecos = homedir = grname = name = newname = skel = shell =NULL; passwd = NULL; class = nispasswd = NULL; quiet = createhome = pretty = dryrun = nis = precrypted = false; edited = false; now = time(NULL); if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, UID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, args)) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'u': id = pw_checkid(optarg, UID_MAX); break; case 'c': gecos = pw_checkname(optarg, 1); break; case 'd': homedir = optarg; break; case 'e': expire_time = parse_date(now, optarg); break; case 'p': password_time = parse_date(now, optarg); break; case 'g': group_from_name_or_id(optarg); grname = optarg; break; case 'G': split_groups(&groups, optarg); break; case 'm': createhome = true; break; case 'M': homemode = validate_mode(optarg); break; case 'l': newname = optarg; break; case 'k': walk = skel = optarg; if (*walk == '/') walk++; if (fstatat(conf.rootfd, walk, &st, 0) == -1) errx(EX_OSFILE, "skeleton `%s' does not " "exists", skel); if (!S_ISDIR(st.st_mode)) errx(EX_OSFILE, "skeleton `%s' is not a " "directory", skel); break; case 's': shell = optarg; break; case 'w': passwd = optarg; break; case 'L': class = pw_checkname(optarg, 0); break; case 'H': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); precrypted = true; if (fd == '-') errx(EX_USAGE, "-H expects a file descriptor"); break; case 'h': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); break; case 'N': dryrun = true; break; case 'P': pretty = true; break; case 'y': nispasswd = optarg; break; case 'Y': nis = true; break; default: exit(EX_USAGE); } } if (geteuid() != 0 && ! dryrun) errx(EX_NOPERM, "you must be root"); if (quiet) freopen(_PATH_DEVNULL, "w", stderr); cnf = get_userconfig(cfg); if (id < 0 && name == NULL) errx(EX_DATAERR, "username or id required"); pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id); if (pwd == NULL) { if (name == NULL) errx(EX_NOUSER, "no such uid `%ju'", (uintmax_t) id); errx(EX_NOUSER, "no such user `%s'", name); } if (name == NULL) name = pwd->pw_name; if (nis && nispasswd == NULL) nispasswd = cnf->nispasswd; if (PWF._altdir == PWF_REGULAR && ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) { if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) { if (!nis && nispasswd && *nispasswd != '/') errx(EX_NOUSER, "Cannot modify NIS user `%s'", name); } else { errx(EX_NOUSER, "Cannot modify non local user `%s'", name); } } if (newname) { if (strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "can't rename `root' account"); if (strcmp(pwd->pw_name, newname) != 0) { pwd->pw_name = pw_checkname(newname, 0); edited = true; } } if (id >= 0 && pwd->pw_uid != id) { pwd->pw_uid = id; edited = true; if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "can't change uid of `root' account"); if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) warnx("WARNING: account `%s' will have a uid of 0 " "(superuser access!)", pwd->pw_name); } if (grname && pwd->pw_uid != 0) { grp = GETGRNAM(grname); if (grp == NULL) grp = GETGRGID(pw_checkid(grname, GID_MAX)); if (grp->gr_gid != pwd->pw_gid) { pwd->pw_gid = grp->gr_gid; edited = true; } } if (password_time >= 0 && pwd->pw_change != password_time) { pwd->pw_change = password_time; edited = true; } if (expire_time >= 0 && pwd->pw_expire != expire_time) { pwd->pw_expire = expire_time; edited = true; } if (shell) { shell = shell_path(cnf->shelldir, cnf->shells, shell); if (shell == NULL) shell = ""; if (strcmp(shell, pwd->pw_shell) != 0) { pwd->pw_shell = shell; edited = true; } } if (class && strcmp(pwd->pw_class, class) != 0) { pwd->pw_class = class; edited = true; } if (homedir && strcmp(pwd->pw_dir, homedir) != 0) { pwd->pw_dir = homedir; edited = true; if (fstatat(conf.rootfd, pwd->pw_dir, &st, 0) == -1) { if (!createhome) warnx("WARNING: home `%s' does not exist", pwd->pw_dir); } else if (!S_ISDIR(st.st_mode)) { warnx("WARNING: home `%s' is not a directory", pwd->pw_dir); } } if (passwd && conf.fd == -1) { lc = login_getpwclass(pwd); if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) warn("setting crypt(3) format"); login_close(lc); cnf->default_password = passwd_val(passwd, cnf->default_password); pwd->pw_passwd = pw_password(cnf, pwd->pw_name); edited = true; } if (gecos && strcmp(pwd->pw_gecos, gecos) != 0) { pwd->pw_gecos = gecos; edited = true; } if (fd != -1) edited = pw_set_passwd(pwd, fd, precrypted, true); if (dryrun) return (print_user(pwd, pretty, false)); if (edited) /* Only updated this if required */ perform_chgpwent(name, pwd, nis ? nispasswd : NULL); /* Now perform the needed changes concern groups */ if (groups != NULL) { /* Delete User from groups using old name */ SETGRENT(); while ((grp = GETGRENT()) != NULL) { if (grp->gr_mem == NULL) continue; for (i = 0; grp->gr_mem[i] != NULL; i++) { if (strcmp(grp->gr_mem[i] , name) != 0) continue; for (j = i; grp->gr_mem[j] != NULL ; j++) grp->gr_mem[j] = grp->gr_mem[j+1]; chggrent(grp->gr_name, grp); break; } } ENDGRENT(); /* Add the user to the needed groups */ for (i = 0; i < groups->sl_cur; i++) { grp = GETGRNAM(groups->sl_str[i]); grp = gr_add(grp, pwd->pw_name); if (grp == NULL) continue; chggrent(grp->gr_name, grp); free(grp); } } /* In case of rename we need to walk over the different groups */ if (newname) { SETGRENT(); while ((grp = GETGRENT()) != NULL) { if (grp->gr_mem == NULL) continue; for (i = 0; grp->gr_mem[i] != NULL; i++) { if (strcmp(grp->gr_mem[i], name) != 0) continue; grp->gr_mem[i] = newname; chggrent(grp->gr_name, grp); break; } } } /* go get a current version of pwd */ if (newname) name = newname; pwd = GETPWNAM(name); if (pwd == NULL) errx(EX_NOUSER, "user '%s' disappeared during update", name); grp = GETGRGID(pwd->pw_gid); pw_log(cnf, M_UPDATE, W_USER, "%s(%ju):%s(%ju):%s:%s:%s", pwd->pw_name, (uintmax_t)pwd->pw_uid, grp ? grp->gr_name : "unknown", (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1), pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); /* * Let's create and populate the user's home directory. Note * that this also `works' for editing users if -m is used, but * existing files will *not* be overwritten. */ if (PWALTDIR() != PWF_ALT && createhome && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) { if (!skel) skel = cnf->dotdir; if (homemode == 0) homemode = cnf->homemode; create_and_populate_homedir(cnf, pwd, skel, homemode, true); } if (nis && nis_update() == 0) pw_log(cnf, M_UPDATE, W_USER, "NIS maps updated"); return (EXIT_SUCCESS); } diff --git a/usr.sbin/pw/pw_vpw.c b/usr.sbin/pw/pw_vpw.c index 3ee1e794c4a5..4814ec2076d6 100644 --- a/usr.sbin/pw/pw_vpw.c +++ b/usr.sbin/pw/pw_vpw.c @@ -1,216 +1,211 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include "pwupd.h" static FILE * pwd_fp = NULL; static int pwd_scanflag; static const char *pwd_filename; void vendpwent(void) { if (pwd_fp != NULL) { fclose(pwd_fp); pwd_fp = NULL; } } void vsetpwent(void) { vendpwent(); } static struct passwd * vnextpwent(char const *nam, uid_t uid, int doclose) { struct passwd *pw; char *line; size_t linecap; ssize_t linelen; pw = NULL; line = NULL; linecap = 0; if (pwd_fp == NULL) { if (geteuid() == 0) { pwd_filename = _MASTERPASSWD; pwd_scanflag = PWSCAN_MASTER; } else { pwd_filename = _PASSWD; pwd_scanflag = 0; } pwd_fp = fopen(getpwpath(pwd_filename), "r"); } if (pwd_fp != NULL) { while ((linelen = getline(&line, &linecap, pwd_fp)) > 0) { /* Skip comments and empty lines */ if (*line == '\n' || *line == '#') continue; /* trim latest \n */ if (line[linelen - 1 ] == '\n') line[linelen - 1] = '\0'; pw = pw_scan(line, pwd_scanflag); if (pw == NULL) errx(EXIT_FAILURE, "Invalid user entry in '%s':" " '%s'", getpwpath(pwd_filename), line); if (uid != (uid_t)-1) { if (uid == pw->pw_uid) break; } else if (nam != NULL) { if (strcmp(nam, pw->pw_name) == 0) break; } else break; free(pw); pw = NULL; } if (doclose) vendpwent(); } free(line); return (pw); } struct passwd * vgetpwent(void) { return vnextpwent(NULL, -1, 0); } struct passwd * vgetpwuid(uid_t uid) { return vnextpwent(NULL, uid, 1); } struct passwd * vgetpwnam(const char * nam) { return vnextpwent(nam, -1, 1); } static FILE * grp_fp = NULL; void vendgrent(void) { if (grp_fp != NULL) { fclose(grp_fp); grp_fp = NULL; } } void vsetgrent(void) { vendgrent(); } static struct group * vnextgrent(char const *nam, gid_t gid, int doclose) { struct group *gr; char *line; size_t linecap; ssize_t linelen; gr = NULL; line = NULL; linecap = 0; if (grp_fp != NULL || (grp_fp = fopen(getgrpath(_GROUP), "r")) != NULL) { while ((linelen = getline(&line, &linecap, grp_fp)) > 0) { /* Skip comments and empty lines */ if (*line == '\n' || *line == '#') continue; /* trim latest \n */ if (line[linelen - 1 ] == '\n') line[linelen - 1] = '\0'; gr = gr_scan(line); if (gr == NULL) errx(EXIT_FAILURE, "Invalid group entry in '%s':" " '%s'", getgrpath(_GROUP), line); if (gid != (gid_t)-1) { if (gid == gr->gr_gid) break; } else if (nam != NULL) { if (strcmp(nam, gr->gr_name) == 0) break; } else break; free(gr); gr = NULL; } if (doclose) vendgrent(); } free(line); return (gr); } struct group * vgetgrent(void) { return vnextgrent(NULL, -1, 0); } struct group * vgetgrgid(gid_t gid) { return vnextgrent(NULL, gid, 1); } struct group * vgetgrnam(const char * nam) { return vnextgrent(nam, -1, 1); } diff --git a/usr.sbin/pw/pwupd.c b/usr.sbin/pw/pwupd.c index 068b830726da..89c1553c8c92 100644 --- a/usr.sbin/pw/pwupd.c +++ b/usr.sbin/pw/pwupd.c @@ -1,154 +1,149 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include "pwupd.h" char * getpwpath(char const * file) { static char pathbuf[MAXPATHLEN]; snprintf(pathbuf, sizeof pathbuf, "%s/%s", conf.etcpath, file); return (pathbuf); } static int pwdb_check(void) { int i = 0; pid_t pid; char *args[10]; args[i++] = _PATH_PWD_MKDB; args[i++] = "-C"; if (strcmp(conf.etcpath, _PATH_PWD) != 0) { args[i++] = "-d"; args[i++] = conf.etcpath; } args[i++] = getpwpath(_MASTERPASSWD); args[i] = NULL; if ((pid = fork()) == -1) /* Error (errno set) */ i = errno; else if (pid == 0) { /* Child */ execv(args[0], args); _exit(1); } else { /* Parent */ waitpid(pid, &i, 0); if (WEXITSTATUS(i)) i = EIO; } return (i); } static int pw_update(struct passwd * pwd, char const * user) { struct passwd *pw = NULL; struct passwd *old_pw = NULL; int rc, pfd, tfd; if ((rc = pwdb_check()) != 0) return (rc); if (pwd != NULL) pw = pw_dup(pwd); if (user != NULL) old_pw = GETPWNAM(user); if (pw_init(conf.etcpath, NULL)) err(1, "pw_init()"); if ((pfd = pw_lock()) == -1) { pw_fini(); err(1, "pw_lock()"); } if ((tfd = pw_tmp(-1)) == -1) { pw_fini(); err(1, "pw_tmp()"); } if (pw_copy(pfd, tfd, pw, old_pw) == -1) { pw_fini(); close(tfd); err(1, "pw_copy()"); } fsync(tfd); close(tfd); /* * in case of deletion of a user, the whole database * needs to be regenerated */ if (pw_mkdb(pw != NULL ? pw->pw_name : NULL) == -1) { pw_fini(); err(1, "pw_mkdb()"); } free(pw); pw_fini(); return (0); } int addpwent(struct passwd * pwd) { return (pw_update(pwd, NULL)); } int chgpwent(char const * login, struct passwd * pwd) { return (pw_update(pwd, login)); } int delpwent(struct passwd * pwd) { return (pw_update(NULL, pwd->pw_name)); } diff --git a/usr.sbin/pw/rm_r.c b/usr.sbin/pw/rm_r.c index 6032a4769cd9..14218d68215b 100644 --- a/usr.sbin/pw/rm_r.c +++ b/usr.sbin/pw/rm_r.c @@ -1,82 +1,77 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1996 * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include "pwupd.h" void rm_r(int rootfd, const char *path, uid_t uid) { int dirfd; DIR *d; struct dirent *e; struct stat st; if (*path == '/') path++; dirfd = openat(rootfd, path, O_DIRECTORY); if (dirfd == -1) { return; } d = fdopendir(dirfd); if (d == NULL) { (void)close(dirfd); return; } while ((e = readdir(d)) != NULL) { if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; if (fstatat(dirfd, e->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) continue; if (S_ISDIR(st.st_mode)) rm_r(dirfd, e->d_name, uid); else if (S_ISLNK(st.st_mode) || st.st_uid == uid) unlinkat(dirfd, e->d_name, 0); } closedir(d); if (fstatat(rootfd, path, &st, AT_SYMLINK_NOFOLLOW) != 0) return; if (S_ISLNK(st.st_mode)) unlinkat(rootfd, path, 0); else if (st.st_uid == uid) unlinkat(rootfd, path, AT_REMOVEDIR); } diff --git a/usr.sbin/rpc.lockd/test.c b/usr.sbin/rpc.lockd/test.c index a751e5c6f4e4..87449e3219e5 100644 --- a/usr.sbin/rpc.lockd/test.c +++ b/usr.sbin/rpc.lockd/test.c @@ -1,365 +1,364 @@ /* $NetBSD: test.c,v 1.2 1997/10/18 04:01:21 lukem Exp $ */ #include #include #include #ifndef lint #if 0 static char sccsid[] = "from: @(#)nlm_prot.x 1.8 87/09/21 Copyr 1987 Sun Micro"; static char sccsid[] = "from: * @(#)nlm_prot.x 2.1 88/08/01 4.0 RPCSRC"; #else __RCSID("$NetBSD: test.c,v 1.2 1997/10/18 04:01:21 lukem Exp $"); -static const char rcsid[] = "$FreeBSD$"; #endif #endif /* not lint */ /* Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 0, 0 }; nlm_testres * nlm_test_1(argp, clnt) struct nlm_testargs *argp; CLIENT *clnt; { static nlm_testres res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_TEST, xdr_nlm_testargs, argp, xdr_nlm_testres, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } nlm_res * nlm_lock_1(argp, clnt) struct nlm_lockargs *argp; CLIENT *clnt; { enum clnt_stat st; static nlm_res res; bzero((char *)&res, sizeof(res)); if (st = clnt_call(clnt, NLM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) { printf("clnt_call returns %d\n", st); clnt_perror(clnt, "humbug"); return (NULL); } return (&res); } nlm_res * nlm_cancel_1(argp, clnt) struct nlm_cancargs *argp; CLIENT *clnt; { static nlm_res res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_CANCEL, xdr_nlm_cancargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } nlm_res * nlm_unlock_1(argp, clnt) struct nlm_unlockargs *argp; CLIENT *clnt; { static nlm_res res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_UNLOCK, xdr_nlm_unlockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } nlm_res * nlm_granted_1(argp, clnt) struct nlm_testargs *argp; CLIENT *clnt; { static nlm_res res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_GRANTED, xdr_nlm_testargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } void * nlm_test_msg_1(argp, clnt) struct nlm_testargs *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_TEST_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } void * nlm_lock_msg_1(argp, clnt) struct nlm_lockargs *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_LOCK_MSG, xdr_nlm_lockargs, argp, xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) { clnt_perror(clnt, "nlm_lock_msg_1"); return (NULL); } return ((void *)&res); } void * nlm_cancel_msg_1(argp, clnt) struct nlm_cancargs *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_CANCEL_MSG, xdr_nlm_cancargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } void * nlm_unlock_msg_1(argp, clnt) struct nlm_unlockargs *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_UNLOCK_MSG, xdr_nlm_unlockargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } void * nlm_granted_msg_1(argp, clnt) struct nlm_testargs *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_GRANTED_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } void * nlm_test_res_1(argp, clnt) nlm_testres *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_TEST_RES, xdr_nlm_testres, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } void * nlm_lock_res_1(argp, clnt) nlm_res *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_LOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } void * nlm_cancel_res_1(argp, clnt) nlm_res *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_CANCEL_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } void * nlm_unlock_res_1(argp, clnt) nlm_res *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_UNLOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } void * nlm_granted_res_1(argp, clnt) nlm_res *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_GRANTED_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } nlm_shareres * nlm_share_3(argp, clnt) nlm_shareargs *argp; CLIENT *clnt; { static nlm_shareres res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_SHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } nlm_shareres * nlm_unshare_3(argp, clnt) nlm_shareargs *argp; CLIENT *clnt; { static nlm_shareres res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_UNSHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } nlm_res * nlm_nm_lock_3(argp, clnt) nlm_lockargs *argp; CLIENT *clnt; { static nlm_res res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_NM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } void * nlm_free_all_3(argp, clnt) nlm_notify *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, NLM_FREE_ALL, xdr_nlm_notify, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } int main(int argc, char **argv) { CLIENT *cli; nlm_res res_block; nlm_res *out; nlm_lockargs arg; struct timeval tim; printf("Creating client for host %s\n", argv[1]); cli = clnt_create(argv[1], NLM_PROG, NLM_VERS, "udp"); if (!cli) { errx(1, "Failed to create client\n"); /* NOTREACHED */ } clnt_control(cli, CLGET_TIMEOUT, &tim); printf("Default timeout was %d.%d\n", tim.tv_sec, tim.tv_usec); tim.tv_usec = -1; tim.tv_sec = -1; clnt_control(cli, CLSET_TIMEOUT, &tim); clnt_control(cli, CLGET_TIMEOUT, &tim); printf("timeout now %d.%d\n", tim.tv_sec, tim.tv_usec); arg.cookie.n_len = 4; arg.cookie.n_bytes = "hello"; arg.block = 0; arg.exclusive = 0; arg.reclaim = 0; arg.state = 0x1234; arg.alock.caller_name = "localhost"; arg.alock.fh.n_len = 32; arg.alock.fh.n_bytes = "\x04\x04\x02\x00\x01\x00\x00\x00\x0c\x00\x00\x00\xff\xff\xff\xd0\x16\x00\x00\x5b\x7c\xff\xff\xff\xec\x2f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x54\xef\xbf\xd7\x94"; arg.alock.oh.n_len = 8; arg.alock.oh.n_bytes = "\x00\x00\x02\xff\xff\xff\xd3"; arg.alock.svid = 0x5678; arg.alock.l_offset = 0; arg.alock.l_len = 100; res_block.stat.stat = nlm_granted; res_block.cookie.n_bytes = "hello"; res_block.cookie.n_len = 5; #if 0 if (nlm_lock_res_1(&res_block, cli)) printf("Success!\n"); else printf("Fail\n"); #else if (out = nlm_lock_msg_1(&arg, cli)) { printf("Success!\n"); printf("out->stat = %d", out->stat); } else { printf("Fail\n"); } #endif return 0; } diff --git a/usr.sbin/rpc.statd/test.c b/usr.sbin/rpc.statd/test.c index c3b342441781..2f18a159ba2d 100644 --- a/usr.sbin/rpc.statd/test.c +++ b/usr.sbin/rpc.statd/test.c @@ -1,144 +1,139 @@ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include /* Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 25, 0 }; struct sm_stat_res * sm_stat_1(argp, clnt) struct sm_name *argp; CLIENT *clnt; { static struct sm_stat_res res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, SM_STAT, (xdrproc_t)xdr_sm_name, argp, (xdrproc_t)xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } struct sm_stat_res * sm_mon_1(argp, clnt) struct mon *argp; CLIENT *clnt; { static struct sm_stat_res res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, SM_MON, (xdrproc_t)xdr_mon, argp, (xdrproc_t)xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } struct sm_stat * sm_unmon_1(argp, clnt) struct mon_id *argp; CLIENT *clnt; { static struct sm_stat res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, SM_UNMON, (xdrproc_t)xdr_mon_id, argp, (xdrproc_t)xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } struct sm_stat * sm_unmon_all_1(argp, clnt) struct my_id *argp; CLIENT *clnt; { static struct sm_stat res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, SM_UNMON_ALL, (xdrproc_t)xdr_my_id, argp, (xdrproc_t)xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&res); } void * sm_simu_crash_1(argp, clnt) void *argp; CLIENT *clnt; { static char res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, SM_SIMU_CRASH, (xdrproc_t)xdr_void, argp, (xdrproc_t)xdr_void, &res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&res); } int main(int argc, char **argv) { CLIENT *cli; char dummy; void *out; struct mon mon; if (argc < 2) { fprintf(stderr, "usage: test | crash\n"); fprintf(stderr, "always talks to statd at localhost\n"); exit(1); } printf("Creating client for localhost\n" ); cli = clnt_create("localhost", SM_PROG, SM_VERS, "udp"); if (!cli) { printf("Failed to create client\n"); exit(1); } mon.mon_id.mon_name = argv[1]; mon.mon_id.my_id.my_name = argv[1]; mon.mon_id.my_id.my_prog = SM_PROG; mon.mon_id.my_id.my_vers = SM_VERS; mon.mon_id.my_id.my_proc = 1; /* have it call sm_stat() !!! */ if (strcmp(argv[1], "crash")) { /* Hostname given */ struct sm_stat_res *res; res = sm_mon_1(&mon, cli); if (res) printf("Success!\n"); else printf("Fail\n"); } else { out = sm_simu_crash_1(&dummy, cli); if (out) printf("Success!\n"); else printf("Fail\n"); } return 0; } diff --git a/usr.sbin/rpc.umntall/rpc.umntall.c b/usr.sbin/rpc.umntall/rpc.umntall.c index 620c5cb26f15..b5f0413c0857 100644 --- a/usr.sbin/rpc.umntall/rpc.umntall.c +++ b/usr.sbin/rpc.umntall/rpc.umntall.c @@ -1,269 +1,264 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1999 Martin Blapp * 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. * */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include "mounttab.h" int verbose; static int do_umount (char *, char *); static int do_umntall (char *); static int is_mounted (char *, char *); static void usage (void); int xdr_dir (XDR *, char *); int main(int argc, char **argv) { int ch, keep, success, pathlen; time_t expire, now; char *host, *path; struct mtablist *mtab; expire = 0; host = path = NULL; success = keep = verbose = 0; while ((ch = getopt(argc, argv, "h:kp:ve:")) != -1) switch (ch) { case 'h': host = optarg; break; case 'e': expire = atoi(optarg); break; case 'k': keep = 1; break; case 'p': path = optarg; break; case 'v': verbose = 1; break; case '?': usage(); default: break; } argc -= optind; argv += optind; /* Default expiretime is one day */ if (expire == 0) expire = 86400; time(&now); /* Read PATH_MOUNTTAB. */ if (!read_mtab()) { if (verbose) warnx("no mounttab entries (%s does not exist)", PATH_MOUNTTAB); mtabhead = NULL; } if (host == NULL && path == NULL) { /* Check each entry and do any necessary unmount RPCs. */ for (mtab = mtabhead; mtab != NULL; mtab = mtab->mtab_next) { if (*mtab->mtab_host == '\0') continue; if (mtab->mtab_time + expire < now) { /* Clear expired entry. */ if (verbose) warnx("remove expired entry %s:%s", mtab->mtab_host, mtab->mtab_dirp); bzero(mtab->mtab_host, sizeof(mtab->mtab_host)); continue; } if (keep && is_mounted(mtab->mtab_host, mtab->mtab_dirp)) { if (verbose) warnx("skip entry %s:%s", mtab->mtab_host, mtab->mtab_dirp); continue; } if (do_umount(mtab->mtab_host, mtab->mtab_dirp)) { if (verbose) warnx("umount RPC for %s:%s succeeded", mtab->mtab_host, mtab->mtab_dirp); /* Remove all entries for this host + path. */ clean_mtab(mtab->mtab_host, mtab->mtab_dirp, verbose); } } success = 1; } else { if (host == NULL && path != NULL) /* Missing hostname. */ usage(); if (path == NULL) { /* Do a RPC UMNTALL for this specific host */ success = do_umntall(host); if (verbose && success) warnx("umntall RPC for %s succeeded", host); } else { /* Do a RPC UMNTALL for this specific mount */ for (pathlen = strlen(path); pathlen > 1 && path[pathlen - 1] == '/'; pathlen--) path[pathlen - 1] = '\0'; success = do_umount(host, path); if (verbose && success) warnx("umount RPC for %s:%s succeeded", host, path); } /* If successful, remove any corresponding mounttab entries. */ if (success) clean_mtab(host, path, verbose); } /* Write and unlink PATH_MOUNTTAB if necessary */ if (success) success = write_mtab(verbose); free_mtab(); exit (success ? 0 : 1); } /* * Send a RPC_MNT UMNTALL request to hostname. * XXX This works for all mountd implementations, * but produces a RPC IOERR on non FreeBSD systems. */ int do_umntall(char *hostname) { enum clnt_stat clnt_stat; struct timeval try; CLIENT *clp; try.tv_sec = 3; try.tv_usec = 0; clp = clnt_create_timed(hostname, MOUNTPROG, MOUNTVERS, "udp", &try); if (clp == NULL) { warnx("%s: %s", hostname, clnt_spcreateerror("MOUNTPROG")); return (0); } clp->cl_auth = authunix_create_default(); clnt_stat = clnt_call(clp, MOUNTPROC_UMNTALL, (xdrproc_t)xdr_void, (caddr_t)0, (xdrproc_t)xdr_void, (caddr_t)0, try); if (clnt_stat != RPC_SUCCESS) warnx("%s: %s", hostname, clnt_sperror(clp, "MOUNTPROC_UMNTALL")); auth_destroy(clp->cl_auth); clnt_destroy(clp); return (clnt_stat == RPC_SUCCESS); } /* * Send a RPC_MNT UMOUNT request for dirp to hostname. */ int do_umount(char *hostname, char *dirp) { enum clnt_stat clnt_stat; struct timeval try; CLIENT *clp; try.tv_sec = 3; try.tv_usec = 0; clp = clnt_create_timed(hostname, MOUNTPROG, MOUNTVERS, "udp", &try); if (clp == NULL) { warnx("%s: %s", hostname, clnt_spcreateerror("MOUNTPROG")); return (0); } clp->cl_auth = authsys_create_default(); clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir, dirp, (xdrproc_t)xdr_void, (caddr_t)0, try); if (clnt_stat != RPC_SUCCESS) warnx("%s: %s", hostname, clnt_sperror(clp, "MOUNTPROC_UMNT")); auth_destroy(clp->cl_auth); clnt_destroy(clp); return (clnt_stat == RPC_SUCCESS); } /* * Check if the entry is still/already mounted. */ int is_mounted(char *hostname, char *dirp) { struct statfs *mntbuf; char name[MNAMELEN + 1]; size_t bufsize; int mntsize, i; if (strlen(hostname) + strlen(dirp) >= MNAMELEN) return (0); snprintf(name, sizeof(name), "%s:%s", hostname, dirp); mntsize = getfsstat(NULL, 0, MNT_NOWAIT); if (mntsize <= 0) return (0); bufsize = (mntsize + 1) * sizeof(struct statfs); if ((mntbuf = malloc(bufsize)) == NULL) err(1, "malloc"); mntsize = getfsstat(mntbuf, (long)bufsize, MNT_NOWAIT); for (i = mntsize - 1; i >= 0; i--) { if (strcmp(mntbuf[i].f_mntfromname, name) == 0) { free(mntbuf); return (1); } } free(mntbuf); return (0); } /* * xdr routines for mount rpc's */ int xdr_dir(XDR *xdrsp, char *dirp) { return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); } static void usage(void) { (void)fprintf(stderr, "%s\n", "usage: rpc.umntall [-kv] [-e expire] [-h host] [-p path]"); exit(1); } diff --git a/usr.sbin/rpc.ypupdated/update.c b/usr.sbin/rpc.ypupdated/update.c index f3d54b833d2f..635b097356e9 100644 --- a/usr.sbin/rpc.ypupdated/update.c +++ b/usr.sbin/rpc.ypupdated/update.c @@ -1,336 +1,334 @@ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user or with the express written consent of * Sun Microsystems, Inc. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #ifndef lint #if 0 static char sccsid[] = "@(#)update.c 1.2 91/03/11 Copyr 1986 Sun Micro"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * Copyright (C) 1986, 1989, Sun Microsystems, Inc. */ /* * Administrative tool to add a new user to the publickey database */ #include #include #include #include #include #ifdef YP #include #include #include #include #endif /* YP */ #include #include #include #include "ypupdated_extern.h" #ifdef YP #define MAXMAPNAMELEN 256 #else #define YPOP_CHANGE 1 /* change, do not add */ #define YPOP_INSERT 2 /* add, do not change */ #define YPOP_DELETE 3 /* delete this entry */ #define YPOP_STORE 4 /* add, or change */ #endif #ifdef YP static char SHELL[] = "/bin/sh"; static char YPDBPATH[]="/var/yp"; /* This is defined but not used! */ static char PKMAP[] = "publickey.byname"; static char UPDATEFILE[] = "updaters"; static char PKFILE[] = "/etc/publickey"; #endif /* YP */ #ifdef YP static int _openchild(char *, FILE **, FILE **); /* * Determine if requester is allowed to update the given map, * and update it if so. Returns the yp status, which is zero * if there is no access violation. */ int mapupdate(char *requester, char *mapname, u_int op, u_int keylen, char *key, u_int datalen, char *data) { char updater[MAXMAPNAMELEN + 40]; FILE *childargs; FILE *childrslt; #ifdef WEXITSTATUS int status; #else union wait status; #endif pid_t pid; u_int yperrno; #ifdef DEBUG printf("%s %s\n", key, data); #endif (void)sprintf(updater, "make -s -f %s/%s %s", YPDBPATH, /* !!! */ UPDATEFILE, mapname); pid = _openchild(updater, &childargs, &childrslt); if (pid < 0) { return (YPERR_YPERR); } /* * Write to child */ (void)fprintf(childargs, "%s\n", requester); (void)fprintf(childargs, "%u\n", op); (void)fprintf(childargs, "%u\n", keylen); (void)fwrite(key, (int)keylen, 1, childargs); (void)fprintf(childargs, "\n"); (void)fprintf(childargs, "%u\n", datalen); (void)fwrite(data, (int)datalen, 1, childargs); (void)fprintf(childargs, "\n"); (void)fclose(childargs); /* * Read from child */ (void)fscanf(childrslt, "%d", &yperrno); (void)fclose(childrslt); (void)wait(&status); #ifdef WEXITSTATUS if (WEXITSTATUS(status) != 0) #else if (status.w_retcode != 0) #endif return (YPERR_YPERR); return (yperrno); } /* * returns pid, or -1 for failure */ static int _openchild(char *command, FILE **fto, FILE **ffrom) { int i; pid_t pid; int pdto[2]; int pdfrom[2]; char *com; struct rlimit rl; if (pipe(pdto) < 0) { goto error1; } if (pipe(pdfrom) < 0) { goto error2; } switch (pid = fork()) { case -1: goto error3; case 0: /* * child: read from pdto[0], write into pdfrom[1] */ (void)close(0); (void)dup(pdto[0]); (void)close(1); (void)dup(pdfrom[1]); getrlimit(RLIMIT_NOFILE, &rl); for (i = rl.rlim_max - 1; i >= 3; i--) { (void) close(i); } com = malloc((unsigned) strlen(command) + 6); if (com == NULL) { _exit(~0); } (void)sprintf(com, "exec %s", command); execl(SHELL, basename(SHELL), "-c", com, (char *)NULL); _exit(~0); default: /* * parent: write into pdto[1], read from pdfrom[0] */ *fto = fdopen(pdto[1], "w"); (void)close(pdto[0]); *ffrom = fdopen(pdfrom[0], "r"); (void)close(pdfrom[1]); break; } return (pid); /* * error cleanup and return */ error3: (void)close(pdfrom[0]); (void)close(pdfrom[1]); error2: (void)close(pdto[0]); (void)close(pdto[1]); error1: return (-1); } static char * basename(char *path) { char *p; p = strrchr(path, '/'); if (p == NULL) { return (path); } else { return (p + 1); } } #else /* YP */ static int match(char *, char *); /* * Determine if requester is allowed to update the given map, * and update it if so. Returns the status, which is zero * if there is no access violation. This function updates * the local file and then shuts up. */ int localupdate(char *name, char *filename, u_int op, u_int keylen __unused, char *key, u_int datalen __unused, char *data) { char line[256]; FILE *rf; FILE *wf; char *tmpname; int err; /* * Check permission */ if (strcmp(name, key) != 0) { return (ERR_ACCESS); } if (strcmp(name, "nobody") == 0) { /* * Can't change "nobody"s key. */ return (ERR_ACCESS); } /* * Open files */ tmpname = malloc(strlen(filename) + 4); if (tmpname == NULL) { return (ERR_MALLOC); } sprintf(tmpname, "%s.tmp", filename); rf = fopen(filename, "r"); if (rf == NULL) { err = ERR_READ; goto cleanup; } wf = fopen(tmpname, "w"); if (wf == NULL) { fclose(rf); err = ERR_WRITE; goto cleanup; } err = -1; while (fgets(line, sizeof (line), rf)) { if (err < 0 && match(line, name)) { switch (op) { case YPOP_INSERT: err = ERR_KEY; break; case YPOP_STORE: case YPOP_CHANGE: fprintf(wf, "%s %s\n", key, data); err = 0; break; case YPOP_DELETE: /* do nothing */ err = 0; break; } } else { fputs(line, wf); } } if (err < 0) { switch (op) { case YPOP_CHANGE: case YPOP_DELETE: err = ERR_KEY; break; case YPOP_INSERT: case YPOP_STORE: err = 0; fprintf(wf, "%s %s\n", key, data); break; } } fclose(wf); fclose(rf); if (err == 0) { if (rename(tmpname, filename) < 0) { err = ERR_DBASE; goto cleanup; } } else { if (unlink(tmpname) < 0) { err = ERR_DBASE; goto cleanup; } } cleanup: free(tmpname); return (err); } static int match(char *line, char *name) { int len; len = strlen(name); return (strncmp(line, name, len) == 0 && (line[len] == ' ' || line[len] == '\t')); } #endif /* !YP */ diff --git a/usr.sbin/spray/spray.c b/usr.sbin/spray/spray.c index 18caf3ff3bd9..436b28d32fba 100644 --- a/usr.sbin/spray/spray.c +++ b/usr.sbin/spray/spray.c @@ -1,222 +1,217 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1993 Winning Strategies, Inc. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Winning Strategies, Inc. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #ifndef SPRAYOVERHEAD #define SPRAYOVERHEAD 86 #endif static void usage(void) __dead2; static void print_xferstats(unsigned int, int, double); /* spray buffer */ static char spray_buffer[SPRAYMAX]; /* RPC timeouts */ static struct timeval NO_DEFAULT = { -1, -1 }; static struct timeval ONE_WAY = { 0, 0 }; static struct timeval TIMEOUT = { 25, 0 }; int main(int argc, char *argv[]) { spraycumul host_stats; sprayarr host_array; CLIENT *cl; int c; u_int i; u_int count = 0; int delay = 0; int length = 0; double xmit_time; /* time to receive data */ while ((c = getopt(argc, argv, "c:d:l:")) != -1) { switch (c) { case 'c': count = atoi(optarg); break; case 'd': delay = atoi(optarg); break; case 'l': length = atoi(optarg); break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 1) { usage(); /* NOTREACHED */ } /* Correct packet length. */ if (length > SPRAYMAX) { length = SPRAYMAX; } else if (length < SPRAYOVERHEAD) { length = SPRAYOVERHEAD; } else { /* The RPC portion of the packet is a multiple of 32 bits. */ length -= SPRAYOVERHEAD - 3; length &= ~3; length += SPRAYOVERHEAD; } /* * The default value of count is the number of packets required * to make the total stream size 100000 bytes. */ if (!count) { count = 100000 / length; } /* Initialize spray argument */ host_array.sprayarr_len = length - SPRAYOVERHEAD; host_array.sprayarr_val = spray_buffer; /* create connection with server */ cl = clnt_create(*argv, SPRAYPROG, SPRAYVERS, "udp"); if (cl == NULL) errx(1, "%s", clnt_spcreateerror("")); /* * For some strange reason, RPC 4.0 sets the default timeout, * thus timeouts specified in clnt_call() are always ignored. * * The following (undocumented) hack resets the internal state * of the client handle. */ clnt_control(cl, CLSET_TIMEOUT, &NO_DEFAULT); /* Clear server statistics */ if (clnt_call(cl, SPRAYPROC_CLEAR, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) errx(1, "%s", clnt_sperror(cl, "")); /* Spray server with packets */ printf ("sending %u packets of length %d to %s ...", count, length, *argv); fflush (stdout); for (i = 0; i < count; i++) { clnt_call(cl, SPRAYPROC_SPRAY, (xdrproc_t)xdr_sprayarr, &host_array, (xdrproc_t)xdr_void, NULL, ONE_WAY); if (delay) { usleep(delay); } } /* Collect statistics from server */ if (clnt_call(cl, SPRAYPROC_GET, (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_spraycumul, &host_stats, TIMEOUT) != RPC_SUCCESS) errx(1, "%s", clnt_sperror(cl, "")); xmit_time = host_stats.clock.sec + (host_stats.clock.usec / 1000000.0); printf ("\n\tin %.2f seconds elapsed time\n", xmit_time); /* report dropped packets */ if (host_stats.counter != count) { int packets_dropped = count - host_stats.counter; printf("\t%d packets (%.2f%%) dropped\n", packets_dropped, 100.0 * packets_dropped / count ); } else { printf("\tno packets dropped\n"); } printf("Sent:"); print_xferstats(count, length, xmit_time); printf("Rcvd:"); print_xferstats(host_stats.counter, length, xmit_time); exit (0); } static void print_xferstats(u_int packets, int packetlen, double xfertime) { int datalen; double pps; /* packets per second */ double bps; /* bytes per second */ datalen = packets * packetlen; pps = packets / xfertime; bps = datalen / xfertime; printf("\t%.0f packets/sec, ", pps); if (bps >= 1024) printf ("%.1fK ", bps / 1024); else printf ("%.0f ", bps); printf("bytes/sec\n"); } static void usage(void) { fprintf(stderr, "usage: spray [-c count] [-l length] [-d delay] host\n"); exit(1); } diff --git a/usr.sbin/traceroute6/traceroute6.c b/usr.sbin/traceroute6/traceroute6.c index d887acf5ae53..cf1c12c5962c 100644 --- a/usr.sbin/traceroute6/traceroute6.c +++ b/usr.sbin/traceroute6/traceroute6.c @@ -1,1814 +1,1812 @@ /* $KAME: traceroute6.c,v 1.68 2004/01/25 11:16:12 suz Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Van Jacobson. * * 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 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1990, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)traceroute.c 8.1 (Berkeley) 6/6/93"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ /* * traceroute host - trace the route ip packets follow going to "host". * * Attempt to trace the route an ip packet would follow to some * internet host. We find out intermediate hops by launching probe * packets with a small ttl (time to live) then listening for an * icmp "time exceeded" reply from a gateway. We start our probes * with a ttl of one and increase by one until we get an icmp "port * unreachable" (which means we got to "host") or hit a max (which * defaults to 30 hops & can be changed with the -m flag). Three * probes (change with -q flag) are sent at each ttl setting and a * line is printed showing the ttl, address of the gateway and * round trip time of each probe. If the probe answers come from * different gateways, the address of each responding system will * be printed. If there is no response within a 5 sec. timeout * interval (changed with the -w flag), a "*" is printed for that * probe. * * Probe packets are UDP format. We don't want the destination * host to process them so the destination port is set to an * unlikely value (if some clod on the destination is using that * value, it can be changed with the -p flag). * * A sample use might be: * * [yak 71]% traceroute nis.nsf.net. * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms * * Note that lines 2 & 3 are the same. This is due to a buggy * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards * packets with a zero ttl. * * A more interesting example is: * * [yak 72]% traceroute allspice.lcs.mit.edu. * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms * 12 * * * * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms * 14 * * * * 15 * * * * 16 * * * * 17 * * * * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms * * (I start to see why I'm having so much trouble with mail to * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away * either don't send ICMP "time exceeded" messages or send them * with a ttl too small to reach us. 14 - 17 are running the * MIT C Gateway code that doesn't send "time exceeded"s. God * only knows what's going on with 12. * * The silent gateway 12 in the above may be the result of a bug in * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) * sends an unreachable message using whatever ttl remains in the * original datagram. Since, for gateways, the remaining ttl is * zero, the icmp "time exceeded" is guaranteed to not make it back * to us. The behavior of this bug is slightly more interesting * when it appears on the destination system: * * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms * 7 * * * * 8 * * * * 9 * * * * 10 * * * * 11 * * * * 12 * * * * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! * * Notice that there are 12 "gateways" (13 is the final * destination) and exactly the last half of them are "missing". * What's really happening is that rip (a Sun-3 running Sun OS3.5) * is using the ttl from our arriving datagram as the ttl in its * icmp reply. So, the reply will time out on the return path * (with no notice sent to anyone since icmp's aren't sent for * icmp's) until we probe with a ttl that's at least twice the path * length. I.e., rip is really only 7 hops away. A reply that * returns with a ttl of 1 is a clue this problem exists. * Traceroute prints a "!" after the time if the ttl is <= 1. * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or * non-standard (HPUX) software, expect to see this problem * frequently and/or take care picking the target host of your * probes. * * Other possible annotations after the time are !H, !N, !P (got a host, * network or protocol unreachable, respectively), !S or !F (source * route failed or fragmentation needed -- neither of these should * ever occur and the associated gateway is busted if you see one). If * almost all the probes result in some kind of unreachable, traceroute * will give up and exit. * * Notes * ----- * This program must be run by root or be setuid. (I suggest that * you *don't* make it setuid -- casual use could result in a lot * of unnecessary traffic on our poor, congested nets.) * * This program requires a kernel mod that does not appear in any * system available from Berkeley: A raw ip socket using proto * IPPROTO_RAW must interpret the data sent as an ip datagram (as * opposed to data to be wrapped in an ip datagram). See the README * file that came with the source to this program for a description * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE * MODIFIED TO RUN THIS PROGRAM. * * The udp port usage may appear bizarre (well, ok, it is bizarre). * The problem is that an icmp message only contains 8 bytes of * data from the original datagram. 8 bytes is the size of a udp * header so, if we want to associate replies with the original * datagram, the necessary information must be encoded into the * udp header (the ip id could be used but there's no way to * interlock with the kernel's assignment of ip id's and, anyway, * it would have taken a lot more kernel hacking to allow this * code to set the ip id). So, to allow two or more users to * use traceroute simultaneously, we use this task's pid as the * source port (the high bit is set to move the port number out * of the "likely" range). To keep track of which probe is being * replied to (so times and/or hop counts don't get confused by a * reply that was delayed in transit), we increment the destination * port number before each probe. * * Don't use this as a coding example. I was trying to find a * routing problem and this code sort-of popped out after 48 hours * without sleep. I was amazed it ever compiled, much less ran. * * I stole the idea for this program from Steve Deering. Since * the first release, I've learned that had I attended the right * IETF working group meetings, I also could have stolen it from Guy * Almes or Matt Mathis. I don't know (or care) who came up with * the idea first. I envy the originators' perspicacity and I'm * glad they didn't keep the idea a secret. * * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or * enhancements to the original distribution. * * I've hacked up a round-trip-route version of this that works by * sending a loose-source-routed udp datagram through the destination * back to yourself. Unfortunately, SO many gateways botch source * routing, the thing is almost worthless. Maybe one day... * * -- Van Jacobson (van@helios.ee.lbl.gov) * Tue Dec 20 03:50:13 PST 1988 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_POLL #include #endif #include #include #include #include #include #include #include #include #include #include #ifdef IPSEC #include #include #endif #include "as.h" #define DUMMY_PORT 10010 #define MAXPACKET 65535 /* max ip packet size */ static u_char packet[512]; /* last inbound (icmp) packet */ static char *outpacket; /* last output packet */ int main(int, char *[]); int wait_for_reply(int, struct msghdr *); #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) int setpolicy(int so, char *policy); #endif void send_probe(int, u_long); void *get_uphdr(struct ip6_hdr *, u_char *); void capdns_open(void); int get_hoplim(struct msghdr *); double deltaT(struct timeval *, struct timeval *); const char *pr_type(int); int packet_ok(struct msghdr *, int, int, u_char *, u_char *); void print(struct msghdr *, int); const char *inetname(struct sockaddr *); u_int32_t sctp_crc32c(void *, u_int32_t); u_int16_t in_cksum(u_int16_t *addr, int); u_int16_t udp_cksum(struct sockaddr_in6 *, struct sockaddr_in6 *, void *, u_int32_t); u_int16_t tcp_chksum(struct sockaddr_in6 *, struct sockaddr_in6 *, void *, u_int32_t); void usage(void); static int rcvsock; /* receive (icmp) socket file descriptor */ static int sndsock; /* send (raw/udp) socket file descriptor */ static struct msghdr rcvmhdr; static struct iovec rcviov[2]; static int rcvhlim; static struct in6_pktinfo *rcvpktinfo; static struct sockaddr_in6 Src, Dst, Rcv; static u_long datalen = 20; /* How much data */ #define ICMP6ECHOLEN 8 /* XXX: 2064 = 127(max hops in type 0 rthdr) * sizeof(ip6_hdr) + 16(margin) */ static char rtbuf[2064]; static struct ip6_rthdr *rth; static struct cmsghdr *cmsg; static char *source = NULL; static char *hostname; static cap_channel_t *capdns; static u_long nprobes = 3; static u_long first_hop = 1; static u_long max_hops = 30; static u_int16_t srcport; static u_int16_t port = 32768+666; /* start udp dest port # for probe packets */ static u_int16_t ident; static int tclass = -1; static int options; /* socket options */ static int verbose; static int waittime = 5; /* time to wait for response (in seconds) */ static int nflag; /* print addresses numerically */ static int useproto = IPPROTO_UDP; /* protocol to use to send packet */ static int lflag; /* print both numerical address & hostname */ static int as_path; /* print as numbers for each hop */ static char *as_server = NULL; static void *asn; int main(int argc, char *argv[]) { int mib[4] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_DEFHLIM }; char hbuf[NI_MAXHOST], src0[NI_MAXHOST], *ep; int ch, i, on = 1, seq, rcvcmsglen, error; struct addrinfo hints, *res; static u_char *rcvcmsgbuf; u_long probe, hops, lport, ltclass; struct hostent *hp; size_t size, minlen; uid_t uid; u_char type, code; #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) char ipsec_inpolicy[] = "in bypass"; char ipsec_outpolicy[] = "out bypass"; #endif cap_rights_t rights; capdns_open(); /* * Receive ICMP */ if ((rcvsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { perror("socket(ICMPv6)"); exit(5); } size = sizeof(i); (void) sysctl(mib, sizeof(mib)/sizeof(mib[0]), &i, &size, NULL, 0); max_hops = i; /* specify to tell receiving interface */ #ifdef IPV6_RECVPKTINFO if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) err(1, "setsockopt(IPV6_RECVPKTINFO)"); #else /* old adv. API */ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)) < 0) err(1, "setsockopt(IPV6_PKTINFO)"); #endif /* specify to tell value of hoplimit field of received IP6 hdr */ #ifdef IPV6_RECVHOPLIMIT if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) < 0) err(1, "setsockopt(IPV6_RECVHOPLIMIT)"); #else /* old adv. API */ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) < 0) err(1, "setsockopt(IPV6_HOPLIMIT)"); #endif seq = 0; ident = htons(getpid() & 0xffff); /* same as ping6 */ while ((ch = getopt(argc, argv, "aA:df:g:Ilm:nNp:q:rs:St:TUvw:")) != -1) switch (ch) { case 'a': as_path = 1; break; case 'A': as_path = 1; as_server = optarg; break; case 'd': options |= SO_DEBUG; break; case 'f': ep = NULL; errno = 0; first_hop = strtoul(optarg, &ep, 0); if (errno || !*optarg || *ep || first_hop > 255) { fprintf(stderr, "traceroute6: invalid min hoplimit.\n"); exit(1); } break; case 'g': /* XXX use after capability mode is entered */ hp = getipnodebyname(optarg, AF_INET6, 0, &h_errno); if (hp == NULL) { fprintf(stderr, "traceroute6: unknown host %s\n", optarg); exit(1); } if (rth == NULL) { /* * XXX: We can't detect the number of * intermediate nodes yet. */ if ((rth = inet6_rth_init((void *)rtbuf, sizeof(rtbuf), IPV6_RTHDR_TYPE_0, 0)) == NULL) { fprintf(stderr, "inet6_rth_init failed.\n"); exit(1); } } if (inet6_rth_add((void *)rth, (struct in6_addr *)hp->h_addr)) { fprintf(stderr, "inet6_rth_add failed for %s\n", optarg); exit(1); } freehostent(hp); break; case 'I': useproto = IPPROTO_ICMPV6; break; case 'l': lflag++; break; case 'm': ep = NULL; errno = 0; max_hops = strtoul(optarg, &ep, 0); if (errno || !*optarg || *ep || max_hops > 255) { fprintf(stderr, "traceroute6: invalid max hoplimit.\n"); exit(1); } break; case 'n': nflag++; break; case 'N': useproto = IPPROTO_NONE; break; case 'p': ep = NULL; errno = 0; lport = strtoul(optarg, &ep, 0); if (errno || !*optarg || *ep) { fprintf(stderr, "traceroute6: invalid port.\n"); exit(1); } if (lport == 0 || lport != (lport & 0xffff)) { fprintf(stderr, "traceroute6: port out of range.\n"); exit(1); } port = lport & 0xffff; break; case 'q': ep = NULL; errno = 0; nprobes = strtoul(optarg, &ep, 0); if (errno || !*optarg || *ep) { fprintf(stderr, "traceroute6: invalid nprobes.\n"); exit(1); } if (nprobes < 1) { fprintf(stderr, "traceroute6: nprobes must be >0.\n"); exit(1); } break; case 'r': options |= SO_DONTROUTE; break; case 's': /* * set the ip source address of the outbound * probe (e.g., on a multi-homed host). */ source = optarg; break; case 'S': useproto = IPPROTO_SCTP; break; case 't': ep = NULL; errno = 0; ltclass = strtoul(optarg, &ep, 0); if (errno || !*optarg || *ep || ltclass > 255) { fprintf(stderr, "traceroute6: invalid traffic class.\n"); exit(1); } tclass = (int)ltclass; break; case 'T': useproto = IPPROTO_TCP; break; case 'U': useproto = IPPROTO_UDP; break; case 'v': verbose++; break; case 'w': ep = NULL; errno = 0; waittime = strtoul(optarg, &ep, 0); if (errno || !*optarg || *ep) { fprintf(stderr, "traceroute6: invalid wait time.\n"); exit(1); } if (waittime < 1) { fprintf(stderr, "traceroute6: wait must be >= 1 sec.\n"); exit(1); } break; default: usage(); } argc -= optind; argv += optind; /* * Open socket to send probe packets. */ switch (useproto) { case IPPROTO_ICMPV6: case IPPROTO_NONE: case IPPROTO_SCTP: case IPPROTO_TCP: case IPPROTO_UDP: if ((sndsock = socket(AF_INET6, SOCK_RAW, useproto)) < 0) { perror("socket(SOCK_RAW)"); exit(5); } break; default: fprintf(stderr, "traceroute6: unknown probe protocol %d\n", useproto); exit(5); } if (max_hops < first_hop) { fprintf(stderr, "traceroute6: max hoplimit must be larger than first hoplimit.\n"); exit(1); } /* revoke privs */ uid = getuid(); if (setresuid(uid, uid, uid) == -1) { perror("setresuid"); exit(1); } if (tclass != -1) { if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(int)) == -1) { perror("setsockopt(IPV6_TCLASS)"); exit(7); } } if (argc < 1 || argc > 2) usage(); #if 1 setvbuf(stdout, NULL, _IOLBF, BUFSIZ); #else setlinebuf(stdout); #endif memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_RAW; hints.ai_protocol = IPPROTO_ICMPV6; hints.ai_flags = AI_CANONNAME; error = cap_getaddrinfo(capdns, *argv, NULL, &hints, &res); if (error) { fprintf(stderr, "traceroute6: %s\n", gai_strerror(error)); exit(1); } if (res->ai_addrlen != sizeof(Dst)) { fprintf(stderr, "traceroute6: size of sockaddr mismatch\n"); exit(1); } memcpy(&Dst, res->ai_addr, res->ai_addrlen); hostname = res->ai_canonname ? strdup(res->ai_canonname) : *argv; if (!hostname) { fprintf(stderr, "traceroute6: not enough core\n"); exit(1); } if (res->ai_next) { if (cap_getnameinfo(capdns, res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(hbuf, "?", sizeof(hbuf)); fprintf(stderr, "traceroute6: Warning: %s has multiple " "addresses; using %s\n", hostname, hbuf); } freeaddrinfo(res); if (*++argv) { ep = NULL; errno = 0; datalen = strtoul(*argv, &ep, 0); if (errno || *ep) { fprintf(stderr, "traceroute6: invalid packet length.\n"); exit(1); } } switch (useproto) { case IPPROTO_ICMPV6: minlen = ICMP6ECHOLEN; break; case IPPROTO_UDP: minlen = sizeof(struct udphdr); break; case IPPROTO_NONE: minlen = 0; datalen = 0; break; case IPPROTO_SCTP: minlen = sizeof(struct sctphdr); break; case IPPROTO_TCP: minlen = sizeof(struct tcphdr); break; default: fprintf(stderr, "traceroute6: unknown probe protocol %d.\n", useproto); exit(1); } if (datalen < minlen) datalen = minlen; else if (datalen >= MAXPACKET) { fprintf(stderr, "traceroute6: packet size must be %zu <= s < %d.\n", minlen, MAXPACKET); exit(1); } if ((useproto == IPPROTO_SCTP) && (datalen & 3)) { fprintf(stderr, "traceroute6: packet size must be a multiple of 4.\n"); exit(1); } outpacket = malloc(datalen); if (!outpacket) { perror("malloc"); exit(1); } (void) bzero((char *)outpacket, datalen); /* initialize msghdr for receiving packets */ rcviov[0].iov_base = (caddr_t)packet; rcviov[0].iov_len = sizeof(packet); rcvmhdr.msg_name = (caddr_t)&Rcv; rcvmhdr.msg_namelen = sizeof(Rcv); rcvmhdr.msg_iov = rcviov; rcvmhdr.msg_iovlen = 1; rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)); if ((rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { fprintf(stderr, "traceroute6: malloc failed\n"); exit(1); } rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; rcvmhdr.msg_controllen = rcvcmsglen; if (options & SO_DEBUG) (void) setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)); if (options & SO_DONTROUTE) (void) setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, sizeof(on)); #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) /* * do not raise error even if setsockopt fails, kernel may have ipsec * turned off. */ if (setpolicy(rcvsock, ipsec_inpolicy) < 0) errx(1, "%s", ipsec_strerror()); if (setpolicy(rcvsock, ipsec_outpolicy) < 0) errx(1, "%s", ipsec_strerror()); #else { int level = IPSEC_LEVEL_NONE; (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level, sizeof(level)); (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level, sizeof(level)); #ifdef IP_AUTH_TRANS_LEVEL (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level, sizeof(level)); #else (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level, sizeof(level)); #endif #ifdef IP_AUTH_NETWORK_LEVEL (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level, sizeof(level)); #endif } #endif /* !(IPSEC && IPSEC_POLICY_IPSEC) */ #ifdef SO_SNDBUF i = datalen; if (i == 0) i = 1; if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&i, sizeof(i)) < 0) { perror("setsockopt(SO_SNDBUF)"); exit(6); } #endif /* SO_SNDBUF */ if (options & SO_DEBUG) (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)); if (options & SO_DONTROUTE) (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, sizeof(on)); if (rth) {/* XXX: there is no library to finalize the header... */ rth->ip6r_len = rth->ip6r_segleft * 2; if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_RTHDR, (void *)rth, (rth->ip6r_len + 1) << 3)) { fprintf(stderr, "setsockopt(IPV6_RTHDR): %s\n", strerror(errno)); exit(1); } } #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) /* * do not raise error even if setsockopt fails, kernel may have ipsec * turned off. */ if (setpolicy(sndsock, ipsec_inpolicy) < 0) errx(1, "%s", ipsec_strerror()); if (setpolicy(sndsock, ipsec_outpolicy) < 0) errx(1, "%s", ipsec_strerror()); #else { int level = IPSEC_LEVEL_BYPASS; (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level, sizeof(level)); (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level, sizeof(level)); #ifdef IP_AUTH_TRANS_LEVEL (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level, sizeof(level)); #else (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level, sizeof(level)); #endif #ifdef IP_AUTH_NETWORK_LEVEL (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level, sizeof(level)); #endif } #endif /* !(IPSEC && IPSEC_POLICY_IPSEC) */ /* * Source selection */ bzero(&Src, sizeof(Src)); if (source) { memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_flags = AI_NUMERICHOST; error = cap_getaddrinfo(capdns, source, "0", &hints, &res); if (error) { printf("traceroute6: %s: %s\n", source, gai_strerror(error)); exit(1); } if (res->ai_addrlen > sizeof(Src)) { printf("traceroute6: %s: %s\n", source, gai_strerror(error)); exit(1); } memcpy(&Src, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); } else { struct sockaddr_in6 Nxt; int dummy; socklen_t len; Nxt = Dst; Nxt.sin6_port = htons(DUMMY_PORT); if (cmsg != NULL) bcopy(inet6_rthdr_getaddr(cmsg, 1), &Nxt.sin6_addr, sizeof(Nxt.sin6_addr)); if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(1); } if (connect(dummy, (struct sockaddr *)&Nxt, Nxt.sin6_len) < 0) { perror("connect"); exit(1); } len = sizeof(Src); if (getsockname(dummy, (struct sockaddr *)&Src, &len) < 0) { perror("getsockname"); exit(1); } if (cap_getnameinfo(capdns, (struct sockaddr *)&Src, Src.sin6_len, src0, sizeof(src0), NULL, 0, NI_NUMERICHOST)) { fprintf(stderr, "getnameinfo failed for source\n"); exit(1); } source = src0; close(dummy); } Src.sin6_port = htons(0); if (bind(sndsock, (struct sockaddr *)&Src, Src.sin6_len) < 0) { perror("bind"); exit(1); } { socklen_t len; len = sizeof(Src); if (getsockname(sndsock, (struct sockaddr *)&Src, &len) < 0) { perror("getsockname"); exit(1); } srcport = ntohs(Src.sin6_port); } if (as_path) { asn = as_setup(as_server); if (asn == NULL) { fprintf(stderr, "traceroute6: as_setup failed, AS# lookups" " disabled\n"); (void)fflush(stderr); as_path = 0; } } /* * Message to users */ if (cap_getnameinfo(capdns, (struct sockaddr *)&Dst, Dst.sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) strlcpy(hbuf, "(invalid)", sizeof(hbuf)); fprintf(stderr, "traceroute6"); fprintf(stderr, " to %s (%s)", hostname, hbuf); if (source) fprintf(stderr, " from %s", source); fprintf(stderr, ", %lu hops max, %lu byte packets\n", max_hops, datalen + ((useproto == IPPROTO_UDP) ? sizeof(struct udphdr) : 0)); (void) fflush(stderr); if (first_hop > 1) printf("Skipping %lu intermediate hops\n", first_hop - 1); if (connect(sndsock, (struct sockaddr *)&Dst, sizeof(Dst)) != 0) { fprintf(stderr, "connect: %s\n", strerror(errno)); exit(1); } /* * Here we enter capability mode. Further down access to global * namespaces (e.g filesystem) is restricted (see capsicum(4)). * We must connect(2) our socket before this point. */ if (caph_enter_casper() < 0) { fprintf(stderr, "caph_enter_casper: %s\n", strerror(errno)); exit(1); } cap_rights_init(&rights, CAP_SEND, CAP_SETSOCKOPT); if (caph_rights_limit(sndsock, &rights) < 0) { fprintf(stderr, "caph_rights_limit sndsock: %s\n", strerror(errno)); exit(1); } cap_rights_init(&rights, CAP_RECV, CAP_EVENT); if (caph_rights_limit(rcvsock, &rights) < 0) { fprintf(stderr, "caph_rights_limit rcvsock: %s\n", strerror(errno)); exit(1); } /* * Main loop */ for (hops = first_hop; hops <= max_hops; ++hops) { struct in6_addr lastaddr; int got_there = 0; unsigned unreachable = 0; printf("%2lu ", hops); bzero(&lastaddr, sizeof(lastaddr)); for (probe = 0; probe < nprobes; ++probe) { int cc; struct timeval t1, t2; (void) gettimeofday(&t1, NULL); send_probe(++seq, hops); while ((cc = wait_for_reply(rcvsock, &rcvmhdr))) { (void) gettimeofday(&t2, NULL); if (packet_ok(&rcvmhdr, cc, seq, &type, &code)) { if (!IN6_ARE_ADDR_EQUAL(&Rcv.sin6_addr, &lastaddr)) { if (probe > 0) fputs("\n ", stdout); print(&rcvmhdr, cc); lastaddr = Rcv.sin6_addr; } printf(" %.3f ms", deltaT(&t1, &t2)); if (type == ICMP6_DST_UNREACH) { switch (code) { case ICMP6_DST_UNREACH_NOROUTE: ++unreachable; printf(" !N"); break; case ICMP6_DST_UNREACH_ADMIN: ++unreachable; printf(" !P"); break; case ICMP6_DST_UNREACH_NOTNEIGHBOR: ++unreachable; printf(" !S"); break; case ICMP6_DST_UNREACH_ADDR: ++unreachable; printf(" !A"); break; case ICMP6_DST_UNREACH_NOPORT: if (rcvhlim >= 0 && rcvhlim <= 1) printf(" !"); ++got_there; break; } } else if (type == ICMP6_PARAM_PROB && code == ICMP6_PARAMPROB_NEXTHEADER) { printf(" !H"); ++got_there; } else if (type == ICMP6_ECHO_REPLY) { if (rcvhlim >= 0 && rcvhlim <= 1) printf(" !"); ++got_there; } break; } else if (deltaT(&t1, &t2) > waittime * 1000) { cc = 0; break; } } if (cc == 0) printf(" *"); (void) fflush(stdout); } putchar('\n'); if (got_there || (unreachable > 0 && unreachable >= ((nprobes + 1) / 2))) { exit(0); } } if (as_path) as_shutdown(asn); exit(0); } int wait_for_reply(int sock, struct msghdr *mhdr) { #ifdef HAVE_POLL struct pollfd pfd[1]; int cc = 0; pfd[0].fd = sock; pfd[0].events = POLLIN; pfd[0].revents = 0; if (poll(pfd, 1, waittime * 1000) > 0 && pfd[0].revents & POLLIN) cc = recvmsg(rcvsock, mhdr, 0); return (cc); #else fd_set *fdsp; struct timeval wait; int cc = 0, fdsn; fdsn = howmany(sock + 1, NFDBITS) * sizeof(fd_mask); if ((fdsp = (fd_set *)malloc(fdsn)) == NULL) err(1, "malloc"); memset(fdsp, 0, fdsn); FD_SET(sock, fdsp); wait.tv_sec = waittime; wait.tv_usec = 0; if (select(sock+1, fdsp, (fd_set *)0, (fd_set *)0, &wait) > 0) cc = recvmsg(rcvsock, mhdr, 0); free(fdsp); return (cc); #endif } #if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) int setpolicy(int so, char *policy) { char *buf; buf = ipsec_set_policy(policy, strlen(policy)); if (buf == NULL) { warnx("%s", ipsec_strerror()); return -1; } (void)setsockopt(so, IPPROTO_IPV6, IPV6_IPSEC_POLICY, buf, ipsec_get_policylen(buf)); free(buf); return 0; } #endif void send_probe(int seq, u_long hops) { struct icmp6_hdr *icp; struct sctphdr *sctp; struct udphdr *outudp; struct sctp_chunkhdr *chk; struct sctp_init_chunk *init; struct sctp_paramhdr *param; struct tcphdr *tcp; int i; i = hops; if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&i, sizeof(i)) < 0) { perror("setsockopt IPV6_UNICAST_HOPS"); } Dst.sin6_port = htons(port + seq); switch (useproto) { case IPPROTO_ICMPV6: icp = (struct icmp6_hdr *)outpacket; icp->icmp6_type = ICMP6_ECHO_REQUEST; icp->icmp6_code = 0; icp->icmp6_cksum = 0; icp->icmp6_id = ident; icp->icmp6_seq = htons(seq); break; case IPPROTO_UDP: outudp = (struct udphdr *) outpacket; outudp->uh_sport = htons(ident); outudp->uh_dport = htons(port+seq); outudp->uh_ulen = htons(datalen); outudp->uh_sum = 0; outudp->uh_sum = udp_cksum(&Src, &Dst, outpacket, datalen); break; case IPPROTO_NONE: /* No space for anything. No harm as seq/tv32 are decorative. */ break; case IPPROTO_SCTP: sctp = (struct sctphdr *)outpacket; sctp->src_port = htons(ident); sctp->dest_port = htons(port + seq); if (datalen >= (u_long)(sizeof(struct sctphdr) + sizeof(struct sctp_init_chunk))) { sctp->v_tag = 0; } else { sctp->v_tag = (sctp->src_port << 16) | sctp->dest_port; } sctp->checksum = htonl(0); if (datalen >= (u_long)(sizeof(struct sctphdr) + sizeof(struct sctp_init_chunk))) { /* * Send a packet containing an INIT chunk. This works * better in case of firewalls on the path, but * results in a probe packet containing at least * 32 bytes of payload. For shorter payloads, use * SHUTDOWN-ACK chunks. */ init = (struct sctp_init_chunk *)(sctp + 1); init->ch.chunk_type = SCTP_INITIATION; init->ch.chunk_flags = 0; init->ch.chunk_length = htons((u_int16_t)(datalen - sizeof(struct sctphdr))); init->init.initiate_tag = (sctp->src_port << 16) | sctp->dest_port; init->init.a_rwnd = htonl(1500); init->init.num_outbound_streams = htons(1); init->init.num_inbound_streams = htons(1); init->init.initial_tsn = htonl(0); if (datalen >= (u_long)(sizeof(struct sctphdr) + sizeof(struct sctp_init_chunk) + sizeof(struct sctp_paramhdr))) { param = (struct sctp_paramhdr *)(init + 1); param->param_type = htons(SCTP_PAD); param->param_length = htons((u_int16_t)(datalen - sizeof(struct sctphdr) - sizeof(struct sctp_init_chunk))); } } else { /* * Send a packet containing a SHUTDOWN-ACK chunk, * possibly followed by a PAD chunk. */ if (datalen >= (u_long)(sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr))) { chk = (struct sctp_chunkhdr *)(sctp + 1); chk->chunk_type = SCTP_SHUTDOWN_ACK; chk->chunk_flags = 0; chk->chunk_length = htons(4); } if (datalen >= (u_long)(sizeof(struct sctphdr) + 2 * sizeof(struct sctp_chunkhdr))) { chk = chk + 1; chk->chunk_type = SCTP_PAD_CHUNK; chk->chunk_flags = 0; chk->chunk_length = htons((u_int16_t)(datalen - sizeof(struct sctphdr) - sizeof(struct sctp_chunkhdr))); } } sctp->checksum = sctp_crc32c(outpacket, datalen); break; case IPPROTO_TCP: tcp = (struct tcphdr *)outpacket; tcp->th_sport = htons(ident); tcp->th_dport = htons(port + seq); tcp->th_seq = (tcp->th_sport << 16) | tcp->th_dport; tcp->th_ack = 0; tcp->th_off = 5; tcp->th_flags = TH_SYN; tcp->th_sum = 0; tcp->th_sum = tcp_chksum(&Src, &Dst, outpacket, datalen); break; default: fprintf(stderr, "Unknown probe protocol %d.\n", useproto); exit(1); } i = send(sndsock, (char *)outpacket, datalen, 0); if (i < 0 || (u_long)i != datalen) { if (i < 0) perror("send"); printf("traceroute6: wrote %s %lu chars, ret=%d\n", hostname, datalen, i); (void) fflush(stdout); } } int get_hoplim(struct msghdr *mhdr) { struct cmsghdr *cm; for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT && cm->cmsg_len == CMSG_LEN(sizeof(int))) return (*(int *)CMSG_DATA(cm)); } return (-1); } double deltaT(struct timeval *t1p, struct timeval *t2p) { double dt; dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 + (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0; return (dt); } /* * Convert an ICMP "type" field to a printable string. */ const char * pr_type(int t0) { u_char t = t0 & 0xff; const char *cp; switch (t) { case ICMP6_DST_UNREACH: cp = "Destination Unreachable"; break; case ICMP6_PACKET_TOO_BIG: cp = "Packet Too Big"; break; case ICMP6_TIME_EXCEEDED: cp = "Time Exceeded"; break; case ICMP6_PARAM_PROB: cp = "Parameter Problem"; break; case ICMP6_ECHO_REQUEST: cp = "Echo Request"; break; case ICMP6_ECHO_REPLY: cp = "Echo Reply"; break; case ICMP6_MEMBERSHIP_QUERY: cp = "Group Membership Query"; break; case ICMP6_MEMBERSHIP_REPORT: cp = "Group Membership Report"; break; case ICMP6_MEMBERSHIP_REDUCTION: cp = "Group Membership Reduction"; break; case ND_ROUTER_SOLICIT: cp = "Router Solicitation"; break; case ND_ROUTER_ADVERT: cp = "Router Advertisement"; break; case ND_NEIGHBOR_SOLICIT: cp = "Neighbor Solicitation"; break; case ND_NEIGHBOR_ADVERT: cp = "Neighbor Advertisement"; break; case ND_REDIRECT: cp = "Redirect"; break; default: cp = "Unknown"; break; } return cp; } int packet_ok(struct msghdr *mhdr, int cc, int seq, u_char *type, u_char *code) { struct icmp6_hdr *icp; struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; char *buf = (char *)mhdr->msg_iov[0].iov_base; struct cmsghdr *cm; int *hlimp; char hbuf[NI_MAXHOST]; #ifdef OLDRAWSOCKET int hlen; struct ip6_hdr *ip; #endif #ifdef OLDRAWSOCKET ip = (struct ip6_hdr *) buf; hlen = sizeof(struct ip6_hdr); if (cc < hlen + sizeof(struct icmp6_hdr)) { if (verbose) { if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(hbuf, "invalid", sizeof(hbuf)); printf("packet too short (%d bytes) from %s\n", cc, hbuf); } return (0); } cc -= hlen; icp = (struct icmp6_hdr *)(buf + hlen); #else if (cc < (int)sizeof(struct icmp6_hdr)) { if (verbose) { if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(hbuf, "invalid", sizeof(hbuf)); printf("data too short (%d bytes) from %s\n", cc, hbuf); } return (0); } icp = (struct icmp6_hdr *)buf; #endif /* get optional information via advanced API */ rcvpktinfo = NULL; hlimp = NULL; for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO && cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) rcvpktinfo = (struct in6_pktinfo *)(CMSG_DATA(cm)); if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT && cm->cmsg_len == CMSG_LEN(sizeof(int))) hlimp = (int *)CMSG_DATA(cm); } if (rcvpktinfo == NULL || hlimp == NULL) { warnx("failed to get received hop limit or packet info"); #if 0 return (0); #else rcvhlim = 0; /*XXX*/ #endif } else rcvhlim = *hlimp; *type = icp->icmp6_type; *code = icp->icmp6_code; if ((*type == ICMP6_TIME_EXCEEDED && *code == ICMP6_TIME_EXCEED_TRANSIT) || (*type == ICMP6_DST_UNREACH) || (*type == ICMP6_PARAM_PROB && *code == ICMP6_PARAMPROB_NEXTHEADER)) { struct ip6_hdr *hip; struct icmp6_hdr *icmp; struct sctp_init_chunk *init; struct sctphdr *sctp; struct tcphdr *tcp; struct udphdr *udp; void *up; hip = (struct ip6_hdr *)(icp + 1); if ((up = get_uphdr(hip, (u_char *)(buf + cc))) == NULL) { if (verbose) warnx("failed to get upper layer header"); return (0); } switch (useproto) { case IPPROTO_ICMPV6: icmp = (struct icmp6_hdr *)up; if (icmp->icmp6_id == ident && icmp->icmp6_seq == htons(seq)) return (1); break; case IPPROTO_UDP: udp = (struct udphdr *)up; if (udp->uh_sport == htons(ident) && udp->uh_dport == htons(port + seq)) return (1); break; case IPPROTO_SCTP: sctp = (struct sctphdr *)up; if (sctp->src_port != htons(ident) || sctp->dest_port != htons(port + seq)) { break; } if (datalen >= (u_long)(sizeof(struct sctphdr) + sizeof(struct sctp_init_chunk))) { if (sctp->v_tag != 0) { break; } init = (struct sctp_init_chunk *)(sctp + 1); /* Check the initiate tag, if available. */ if ((char *)&init->init.a_rwnd > buf + cc) { return (1); } if (init->init.initiate_tag == (u_int32_t) ((sctp->src_port << 16) | sctp->dest_port)) { return (1); } } else { if (sctp->v_tag == (u_int32_t)((sctp->src_port << 16) | sctp->dest_port)) { return (1); } } break; case IPPROTO_TCP: tcp = (struct tcphdr *)up; if (tcp->th_sport == htons(ident) && tcp->th_dport == htons(port + seq) && tcp->th_seq == (tcp_seq)((tcp->th_sport << 16) | tcp->th_dport)) return (1); break; case IPPROTO_NONE: return (1); default: fprintf(stderr, "Unknown probe proto %d.\n", useproto); break; } } else if (useproto == IPPROTO_ICMPV6 && *type == ICMP6_ECHO_REPLY) { if (icp->icmp6_id == ident && icp->icmp6_seq == htons(seq)) return (1); } if (verbose) { char sbuf[NI_MAXHOST+1], dbuf[INET6_ADDRSTRLEN]; u_int8_t *p; int i; if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, sbuf, sizeof(sbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(sbuf, "invalid", sizeof(sbuf)); printf("\n%d bytes from %s to %s", cc, sbuf, rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, dbuf, sizeof(dbuf)) : "?"); printf(": icmp type %d (%s) code %d\n", *type, pr_type(*type), *code); p = (u_int8_t *)(icp + 1); #define WIDTH 16 for (i = 0; i < cc; i++) { if (i % WIDTH == 0) printf("%04x:", i); if (i % 4 == 0) printf(" "); printf("%02x", p[i]); if (i % WIDTH == WIDTH - 1) printf("\n"); } if (cc % WIDTH != 0) printf("\n"); } return (0); } /* * Increment pointer until find the UDP or ICMP header. */ void * get_uphdr(struct ip6_hdr *ip6, u_char *lim) { u_char *cp = (u_char *)ip6, nh; int hlen; static u_char none_hdr[1]; /* Fake pointer for IPPROTO_NONE. */ if (cp + sizeof(*ip6) > lim) return (NULL); nh = ip6->ip6_nxt; cp += sizeof(struct ip6_hdr); while (lim - cp >= (nh == IPPROTO_NONE ? 0 : 8)) { switch (nh) { case IPPROTO_ESP: return (NULL); case IPPROTO_ICMPV6: return (useproto == nh ? cp : NULL); case IPPROTO_SCTP: case IPPROTO_TCP: case IPPROTO_UDP: return (useproto == nh ? cp : NULL); case IPPROTO_NONE: return (useproto == nh ? none_hdr : NULL); case IPPROTO_FRAGMENT: hlen = sizeof(struct ip6_frag); nh = ((struct ip6_frag *)cp)->ip6f_nxt; break; case IPPROTO_AH: hlen = (((struct ip6_ext *)cp)->ip6e_len + 2) << 2; nh = ((struct ip6_ext *)cp)->ip6e_nxt; break; default: hlen = (((struct ip6_ext *)cp)->ip6e_len + 1) << 3; nh = ((struct ip6_ext *)cp)->ip6e_nxt; break; } cp += hlen; } return (NULL); } void capdns_open(void) { #ifdef WITH_CASPER const char *types[] = { "NAME", "ADDR" }; int families[1]; cap_channel_t *casper; casper = cap_init(); if (casper == NULL) errx(1, "unable to create casper process"); capdns = cap_service_open(casper, "system.dns"); if (capdns == NULL) errx(1, "unable to open system.dns service"); if (cap_dns_type_limit(capdns, types, nitems(types)) < 0) errx(1, "unable to limit access to system.dns service"); families[0] = AF_INET6; if (cap_dns_family_limit(capdns, families, nitems(families)) < 0) errx(1, "unable to limit access to system.dns service"); cap_close(casper); #endif /* WITH_CASPER */ } void print(struct msghdr *mhdr, int cc) { struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; char hbuf[NI_MAXHOST]; if (cap_getnameinfo(capdns, (struct sockaddr *)from, from->sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(hbuf, "invalid", sizeof(hbuf)); if (as_path) printf(" [AS%u]", as_lookup(asn, hbuf, AF_INET6)); if (nflag) printf(" %s", hbuf); else if (lflag) printf(" %s (%s)", inetname((struct sockaddr *)from), hbuf); else printf(" %s", inetname((struct sockaddr *)from)); if (verbose) { #ifdef OLDRAWSOCKET printf(" %d bytes to %s", cc, rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, hbuf, sizeof(hbuf)) : "?"); #else printf(" %d bytes of data to %s", cc, rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, hbuf, sizeof(hbuf)) : "?"); #endif } } /* * Construct an Internet address representation. * If the nflag has been supplied, give * numeric value, otherwise try for symbolic name. */ const char * inetname(struct sockaddr *sa) { static char line[NI_MAXHOST], domain[MAXHOSTNAMELEN + 1]; static int first = 1; char *cp; if (first && !nflag) { first = 0; if (gethostname(domain, sizeof(domain)) == 0 && (cp = strchr(domain, '.'))) (void) strlcpy(domain, cp + 1, sizeof(domain)); else domain[0] = 0; } cp = NULL; if (!nflag) { if (cap_getnameinfo(capdns, sa, sa->sa_len, line, sizeof(line), NULL, 0, NI_NAMEREQD) == 0) { if ((cp = strchr(line, '.')) && !strcmp(cp + 1, domain)) *cp = 0; cp = line; } } if (cp) return cp; if (cap_getnameinfo(capdns, sa, sa->sa_len, line, sizeof(line), NULL, 0, NI_NUMERICHOST) != 0) strlcpy(line, "invalid", sizeof(line)); return line; } /* * CRC32C routine for the Stream Control Transmission Protocol */ #define CRC32C(c, d) (c = (c>>8) ^ crc_c[(c^(d))&0xFF]) static u_int32_t crc_c[256] = { 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 }; u_int32_t sctp_crc32c(void *pack, u_int32_t len) { u_int32_t i, crc32c; u_int8_t byte0, byte1, byte2, byte3; u_int8_t *buf = (u_int8_t *)pack; crc32c = ~0; for (i = 0; i < len; i++) CRC32C(crc32c, buf[i]); crc32c = ~crc32c; byte0 = crc32c & 0xff; byte1 = (crc32c>>8) & 0xff; byte2 = (crc32c>>16) & 0xff; byte3 = (crc32c>>24) & 0xff; crc32c = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); return htonl(crc32c); } u_int16_t in_cksum(u_int16_t *addr, int len) { int nleft = len; u_int16_t *w = addr; u_int16_t answer; int sum = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), * we add sequential 16 bit words to it, and at the end, fold * back all the carry bits from the top 16 bits into the lower * 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) sum += *(u_char *)w; /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return (answer); } u_int16_t udp_cksum(struct sockaddr_in6 *src, struct sockaddr_in6 *dst, void *payload, u_int32_t len) { struct { struct in6_addr src; struct in6_addr dst; u_int32_t len; u_int8_t zero[3]; u_int8_t next; } pseudo_hdr; u_int16_t sum[2]; pseudo_hdr.src = src->sin6_addr; pseudo_hdr.dst = dst->sin6_addr; pseudo_hdr.len = htonl(len); pseudo_hdr.zero[0] = 0; pseudo_hdr.zero[1] = 0; pseudo_hdr.zero[2] = 0; pseudo_hdr.next = IPPROTO_UDP; sum[1] = in_cksum((u_int16_t *)&pseudo_hdr, sizeof(pseudo_hdr)); sum[0] = in_cksum(payload, len); return (~in_cksum(sum, sizeof(sum))); } u_int16_t tcp_chksum(struct sockaddr_in6 *src, struct sockaddr_in6 *dst, void *payload, u_int32_t len) { struct { struct in6_addr src; struct in6_addr dst; u_int32_t len; u_int8_t zero[3]; u_int8_t next; } pseudo_hdr; u_int16_t sum[2]; pseudo_hdr.src = src->sin6_addr; pseudo_hdr.dst = dst->sin6_addr; pseudo_hdr.len = htonl(len); pseudo_hdr.zero[0] = 0; pseudo_hdr.zero[1] = 0; pseudo_hdr.zero[2] = 0; pseudo_hdr.next = IPPROTO_TCP; sum[1] = in_cksum((u_int16_t *)&pseudo_hdr, sizeof(pseudo_hdr)); sum[0] = in_cksum(payload, len); return (~in_cksum(sum, sizeof(sum))); } void usage(void) { fprintf(stderr, "usage: traceroute6 [-adIlnNrSTUv] [-A as_server] [-f firsthop] [-g gateway]\n" " [-m hoplimit] [-p port] [-q probes] [-s src] [-w waittime] target\n" " [datalen]\n"); exit(1); } diff --git a/usr.sbin/vidcontrol/decode.c b/usr.sbin/vidcontrol/decode.c index 160a6ca92e98..9db7c537a19f 100644 --- a/usr.sbin/vidcontrol/decode.c +++ b/usr.sbin/vidcontrol/decode.c @@ -1,101 +1,96 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1994 Søren Schmidt * 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, * in this position and unchanged. * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include "decode.h" int decode(FILE *fd, char *buffer, int len) { int n, pos = 0, tpos; char *bp, *p; char tbuffer[3]; char temp[128]; #define DEC(c) (((c) - ' ') & 0x3f) do { if (!fgets(temp, sizeof(temp), fd)) return(0); } while (strncmp(temp, "begin ", 6)); sscanf(temp, "begin %o %s", (unsigned *)&n, temp); bp = buffer; for (;;) { if (!fgets(p = temp, sizeof(temp), fd)) return(0); if ((n = DEC(*p)) <= 0) break; for (++p; n > 0; p += 4, n -= 3) { tpos = 0; if (n >= 3) { tbuffer[tpos++] = DEC(p[0])<<2 | DEC(p[1])>>4; tbuffer[tpos++] = DEC(p[1])<<4 | DEC(p[2])>>2; tbuffer[tpos++] = DEC(p[2])<<6 | DEC(p[3]); } else { if (n >= 1) { tbuffer[tpos++] = DEC(p[0])<<2 | DEC(p[1])>>4; } if (n >= 2) { tbuffer[tpos++] = DEC(p[1])<<4 | DEC(p[2])>>2; } if (n >= 3) { tbuffer[tpos++] = DEC(p[2])<<6 | DEC(p[3]); } } if (tpos == 0) continue; if (tpos + pos > len) { tpos = len - pos; /* * Arrange return value > len to indicate * overflow. */ pos++; } bcopy(tbuffer, bp, tpos); pos += tpos; bp += tpos; if (pos > len) return(pos); } } if (!fgets(temp, sizeof(temp), fd) || strcmp(temp, "end\n")) return(0); return(pos); } diff --git a/usr.sbin/vidcontrol/vidcontrol.c b/usr.sbin/vidcontrol/vidcontrol.c index 3e0ab102ae8c..e4cf1e8efc53 100644 --- a/usr.sbin/vidcontrol/vidcontrol.c +++ b/usr.sbin/vidcontrol/vidcontrol.c @@ -1,1572 +1,1567 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1994-1996 Søren Schmidt * All rights reserved. * * Portions of this software are based in part on the work of * Sascha Wildner contributed to The DragonFly Project * * 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, * in this position and unchanged. * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. * * $DragonFly: src/usr.sbin/vidcontrol/vidcontrol.c,v 1.10 2005/03/02 06:08:29 joerg Exp $ */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "path.h" #include "decode.h" #define DATASIZE(x) ((x).w * (x).h * 256 / 8) /* Screen dump modes */ #define DUMP_FMT_RAW 1 #define DUMP_FMT_TXT 2 /* Screen dump options */ #define DUMP_FBF 0 #define DUMP_ALL 1 /* Screen dump file format revision */ #define DUMP_FMT_REV 1 static const char *legal_colors[16] = { "black", "blue", "green", "cyan", "red", "magenta", "brown", "white", "grey", "lightblue", "lightgreen", "lightcyan", "lightred", "lightmagenta", "yellow", "lightwhite" }; static struct { int active_vty; vid_info_t console_info; unsigned char screen_map[256]; int video_mode_number; struct video_info video_mode_info; } cur_info; static int hex = 0; static int vesa_cols; static int vesa_rows; static int font_height; static int vt4_mode = 0; static int video_mode_changed; static struct video_info new_mode_info; /* * Initialize revert data. * * NOTE: the following parameters are not yet saved/restored: * * screen saver timeout * cursor type * mouse character and mouse show/hide state * vty switching on/off state * history buffer size * history contents * font maps */ static void init(void) { if (ioctl(0, VT_GETACTIVE, &cur_info.active_vty) == -1) err(1, "getting active vty"); cur_info.console_info.size = sizeof(cur_info.console_info); if (ioctl(0, CONS_GETINFO, &cur_info.console_info) == -1) err(1, "getting console information"); /* vt(4) use unicode, so no screen mapping required. */ if (vt4_mode == 0 && ioctl(0, GIO_SCRNMAP, &cur_info.screen_map) == -1) err(1, "getting screen map"); if (ioctl(0, CONS_GET, &cur_info.video_mode_number) == -1) err(1, "getting video mode number"); cur_info.video_mode_info.vi_mode = cur_info.video_mode_number; if (ioctl(0, CONS_MODEINFO, &cur_info.video_mode_info) == -1) err(1, "getting video mode parameters"); } /* * If something goes wrong along the way we call revert() to go back to the * console state we came from (which is assumed to be working). * * NOTE: please also read the comments of init(). */ static void revert(void) { int save_errno, size[3]; save_errno = errno; ioctl(0, VT_ACTIVATE, cur_info.active_vty); ioctl(0, KDSBORDER, cur_info.console_info.mv_ovscan); fprintf(stderr, "\033[=%dH", cur_info.console_info.mv_rev.fore); fprintf(stderr, "\033[=%dI", cur_info.console_info.mv_rev.back); if (vt4_mode == 0) ioctl(0, PIO_SCRNMAP, &cur_info.screen_map); if (video_mode_changed) { if (cur_info.video_mode_number >= M_VESA_BASE) ioctl(0, _IO('V', cur_info.video_mode_number - M_VESA_BASE), NULL); else ioctl(0, _IO('S', cur_info.video_mode_number), NULL); if (cur_info.video_mode_info.vi_flags & V_INFO_GRAPHICS) { size[0] = cur_info.console_info.mv_csz; size[1] = cur_info.console_info.mv_rsz; size[2] = cur_info.console_info.font_size; ioctl(0, KDRASTER, size); } } /* Restore some colors last since mode setting forgets some. */ fprintf(stderr, "\033[=%dF", cur_info.console_info.mv_norm.fore); fprintf(stderr, "\033[=%dG", cur_info.console_info.mv_norm.back); errno = save_errno; } /* * Print a short usage string describing all options, then exit. */ static void usage(void) { if (vt4_mode) fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n", "usage: vidcontrol [-Cx] [-b color] [-c appearance] [-f [[size] file]]", " [-g geometry] [-h size] [-i active | adapter | mode]", " [-M char] [-m on | off]", " [-r foreground background] [-S on | off] [-s number]", " [-T xterm | cons25] [-t N | off] [mode]", " [foreground [background]] [show]"); else fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n", "usage: vidcontrol [-CdHLPpx] [-b color] [-c appearance] [-E emulator]", " [-f [[size] file]] [-g geometry] [-h size]", " [-i active | adapter | mode] [-l screen_map] [-M char]", " [-m on | off] [-r foreground background] [-S on | off]", " [-s number] [-T xterm | cons25] [-t N | off] [mode]", " [foreground [background]] [show]"); exit(1); } /* Detect presence of vt(4). */ static int is_vt4(void) { char vty_name[4] = ""; size_t len = sizeof(vty_name); if (sysctlbyname("kern.vty", vty_name, &len, NULL, 0) != 0) return (0); return (strcmp(vty_name, "vt") == 0); } /* * Retrieve the next argument from the command line (for options that require * more than one argument). */ static char * nextarg(int ac, char **av, int *indp, int oc, int strict) { if (*indp < ac) return(av[(*indp)++]); if (strict != 0) { revert(); errx(1, "option requires two arguments -- %c", oc); } return(NULL); } /* * Guess which file to open. Try to open each combination of a specified set * of file name components. */ static FILE * openguess(const char *a[], const char *b[], const char *c[], const char *d[], char **name) { FILE *f; int i, j, k, l; for (i = 0; a[i] != NULL; i++) { for (j = 0; b[j] != NULL; j++) { for (k = 0; c[k] != NULL; k++) { for (l = 0; d[l] != NULL; l++) { asprintf(name, "%s%s%s%s", a[i], b[j], c[k], d[l]); f = fopen(*name, "r"); if (f != NULL) return (f); free(*name); } } } } return (NULL); } /* * Load a screenmap from a file and set it. */ static void load_scrnmap(const char *filename) { FILE *fd; int size; char *name; scrmap_t scrnmap; const char *a[] = {"", SCRNMAP_PATH, NULL}; const char *b[] = {filename, NULL}; const char *c[] = {"", ".scm", NULL}; const char *d[] = {"", NULL}; fd = openguess(a, b, c, d, &name); if (fd == NULL) { revert(); errx(1, "screenmap file not found"); } size = sizeof(scrnmap); if (decode(fd, (char *)&scrnmap, size) != size) { rewind(fd); if (fread(&scrnmap, 1, size, fd) != (size_t)size) { fclose(fd); revert(); errx(1, "bad screenmap file"); } } if (ioctl(0, PIO_SCRNMAP, &scrnmap) == -1) { revert(); err(1, "loading screenmap"); } fclose(fd); } /* * Set the default screenmap. */ static void load_default_scrnmap(void) { scrmap_t scrnmap; int i; for (i=0; i<256; i++) *((char*)&scrnmap + i) = i; if (ioctl(0, PIO_SCRNMAP, &scrnmap) == -1) { revert(); err(1, "loading default screenmap"); } } /* * Print the current screenmap to stdout. */ static void print_scrnmap(void) { unsigned char map[256]; size_t i; if (ioctl(0, GIO_SCRNMAP, &map) == -1) { revert(); err(1, "getting screenmap"); } for (i=0; ishape[0]; while ((word = strsep(¶m, ",")) != NULL) { if (strcmp(word, "normal") == 0) type = 0; else if (strcmp(word, "destructive") == 0) type = CONS_BLINK_CURSOR | CONS_CHAR_CURSOR; else if (strcmp(word, "blink") == 0) type |= CONS_BLINK_CURSOR; else if (strcmp(word, "noblink") == 0) type &= ~CONS_BLINK_CURSOR; else if (strcmp(word, "block") == 0) type &= ~CONS_CHAR_CURSOR; else if (strcmp(word, "noblock") == 0) type |= CONS_CHAR_CURSOR; else if (strcmp(word, "hidden") == 0) type |= CONS_HIDDEN_CURSOR; else if (strcmp(word, "nohidden") == 0) type &= ~CONS_HIDDEN_CURSOR; else if (strncmp(word, "base=", 5) == 0) shape->shape[1] = strtol(word + 5, NULL, 0); else if (strncmp(word, "height=", 7) == 0) shape->shape[2] = strtol(word + 7, NULL, 0); else if (strcmp(word, "charcolors") == 0) type |= CONS_CHARCURSOR_COLORS; else if (strcmp(word, "mousecolors") == 0) type |= CONS_MOUSECURSOR_COLORS; else if (strcmp(word, "default") == 0) type |= CONS_DEFAULT_CURSOR; else if (strcmp(word, "shapeonly") == 0) type |= CONS_SHAPEONLY_CURSOR; else if (strcmp(word, "local") == 0) type |= CONS_LOCAL_CURSOR; else if (strcmp(word, "reset") == 0) type |= CONS_RESET_CURSOR; else if (strcmp(word, "show") == 0) printf("flags %#x, base %d, height %d\n", type, shape->shape[1], shape->shape[2]); else { revert(); errx(1, "invalid parameters for -c starting at '%s%s%s'", word, param != NULL ? "," : "", param != NULL ? param : ""); } } free(dupparam); shape->shape[0] = type; } /* * Set the cursor's shape/type. */ static void set_cursor_type(char *param) { struct cshape shape; /* Dry run to determine color, default and local flags. */ shape.shape[0] = 0; shape.shape[1] = -1; shape.shape[2] = -1; parse_cursor_params(param, &shape); /* Get the relevant old setting. */ if (ioctl(0, CONS_GETCURSORSHAPE, &shape) != 0) { revert(); err(1, "ioctl(CONS_GETCURSORSHAPE)"); } parse_cursor_params(param, &shape); if (ioctl(0, CONS_SETCURSORSHAPE, &shape) != 0) { revert(); err(1, "ioctl(CONS_SETCURSORSHAPE)"); } } /* * Set the video mode. */ static void video_mode(int argc, char **argv, int *mode_index) { static struct { const char *name; unsigned long mode; unsigned long mode_num; } modes[] = { { "80x25", SW_TEXT_80x25, M_TEXT_80x25 }, { "80x30", SW_TEXT_80x30, M_TEXT_80x30 }, { "80x43", SW_TEXT_80x43, M_TEXT_80x43 }, { "80x50", SW_TEXT_80x50, M_TEXT_80x50 }, { "80x60", SW_TEXT_80x60, M_TEXT_80x60 }, { "132x25", SW_TEXT_132x25, M_TEXT_132x25 }, { "132x30", SW_TEXT_132x30, M_TEXT_132x30 }, { "132x43", SW_TEXT_132x43, M_TEXT_132x43 }, { "132x50", SW_TEXT_132x50, M_TEXT_132x50 }, { "132x60", SW_TEXT_132x60, M_TEXT_132x60 }, { "VGA_40x25", SW_VGA_C40x25, M_VGA_C40x25 }, { "VGA_80x25", SW_VGA_C80x25, M_VGA_C80x25 }, { "VGA_80x30", SW_VGA_C80x30, M_VGA_C80x30 }, { "VGA_80x50", SW_VGA_C80x50, M_VGA_C80x50 }, { "VGA_80x60", SW_VGA_C80x60, M_VGA_C80x60 }, #ifdef SW_VGA_C90x25 { "VGA_90x25", SW_VGA_C90x25, M_VGA_C90x25 }, { "VGA_90x30", SW_VGA_C90x30, M_VGA_C90x30 }, { "VGA_90x43", SW_VGA_C90x43, M_VGA_C90x43 }, { "VGA_90x50", SW_VGA_C90x50, M_VGA_C90x50 }, { "VGA_90x60", SW_VGA_C90x60, M_VGA_C90x60 }, #endif { "VGA_320x200", SW_VGA_CG320, M_CG320 }, { "EGA_80x25", SW_ENH_C80x25, M_ENH_C80x25 }, { "EGA_80x43", SW_ENH_C80x43, M_ENH_C80x43 }, { "VESA_132x25", SW_VESA_C132x25,M_VESA_C132x25 }, { "VESA_132x43", SW_VESA_C132x43,M_VESA_C132x43 }, { "VESA_132x50", SW_VESA_C132x50,M_VESA_C132x50 }, { "VESA_132x60", SW_VESA_C132x60,M_VESA_C132x60 }, { "VESA_800x600", SW_VESA_800x600,M_VESA_800x600 }, { NULL, 0, 0 }, }; int new_mode_num = 0; unsigned long mode = 0; int cur_mode; int save_errno; int size[3]; int i; if (ioctl(0, CONS_GET, &cur_mode) < 0) err(1, "cannot get the current video mode"); /* * Parse the video mode argument... */ if (*mode_index < argc) { if (!strncmp(argv[*mode_index], "MODE_", 5)) { if (!isdigit(argv[*mode_index][5])) errx(1, "invalid video mode number"); new_mode_num = atoi(&argv[*mode_index][5]); } else { for (i = 0; modes[i].name != NULL; ++i) { if (!strcmp(argv[*mode_index], modes[i].name)) { mode = modes[i].mode; new_mode_num = modes[i].mode_num; break; } } if (modes[i].name == NULL) return; if (ioctl(0, mode, NULL) < 0) { revert(); err(1, "cannot set videomode"); } video_mode_changed = 1; } /* * Collect enough information about the new video mode... */ new_mode_info.vi_mode = new_mode_num; if (ioctl(0, CONS_MODEINFO, &new_mode_info) == -1) { revert(); err(1, "obtaining new video mode parameters"); } if (mode == 0) { if (new_mode_num >= M_VESA_BASE) mode = _IO('V', new_mode_num - M_VESA_BASE); else mode = _IO('S', new_mode_num); } /* * Try setting the new mode. */ if (ioctl(0, mode, NULL) == -1) { revert(); err(1, "setting video mode"); } video_mode_changed = 1; /* * For raster modes it's not enough to just set the mode. * We also need to explicitly set the raster mode. */ if (new_mode_info.vi_flags & V_INFO_GRAPHICS) { /* font size */ if (font_height == 0) font_height = cur_info.console_info.font_size; size[2] = font_height; /* adjust columns */ if ((vesa_cols * 8 > new_mode_info.vi_width) || (vesa_cols <= 0)) { size[0] = new_mode_info.vi_width / 8; } else { size[0] = vesa_cols; } /* adjust rows */ if ((vesa_rows * font_height > new_mode_info.vi_height) || (vesa_rows <= 0)) { size[1] = new_mode_info.vi_height / font_height; } else { size[1] = vesa_rows; } /* set raster mode */ if (ioctl(0, KDRASTER, size)) { save_errno = errno; if (cur_mode >= M_VESA_BASE) ioctl(0, _IO('V', cur_mode - M_VESA_BASE), NULL); else ioctl(0, _IO('S', cur_mode), NULL); revert(); errno = save_errno; err(1, "cannot activate raster display"); } } /* Recover from mode setting forgetting colors. */ fprintf(stderr, "\033[=%dF", cur_info.console_info.mv_norm.fore); fprintf(stderr, "\033[=%dG", cur_info.console_info.mv_norm.back); (*mode_index)++; } } /* * Return the number for a specified color name. */ static int get_color_number(char *color) { int i; for (i=0; i<16; i++) { if (!strcmp(color, legal_colors[i])) return i; } return -1; } /* * Set normal text and background colors. */ static void set_normal_colors(int argc, char **argv, int *_index) { int color; if (*_index < argc && (color = get_color_number(argv[*_index])) != -1) { (*_index)++; fprintf(stderr, "\033[=%dF", color); if (*_index < argc && (color = get_color_number(argv[*_index])) != -1) { (*_index)++; fprintf(stderr, "\033[=%dG", color); } } } /* * Set reverse text and background colors. */ static void set_reverse_colors(int argc, char **argv, int *_index) { int color; if ((color = get_color_number(argv[*(_index)-1])) != -1) { fprintf(stderr, "\033[=%dH", color); if (*_index < argc && (color = get_color_number(argv[*_index])) != -1) { (*_index)++; fprintf(stderr, "\033[=%dI", color); } } } /* * Switch to virtual terminal #arg. */ static void set_console(char *arg) { int n; if(!arg || strspn(arg,"0123456789") != strlen(arg)) { revert(); errx(1, "bad console number"); } n = atoi(arg); if (n < 1 || n > 16) { revert(); errx(1, "console number out of range"); } else if (ioctl(0, VT_ACTIVATE, n) == -1) { revert(); err(1, "switching vty"); } } /* * Sets the border color. */ static void set_border_color(char *arg) { int color; color = get_color_number(arg); if (color == -1) { revert(); errx(1, "invalid color '%s'", arg); } if (ioctl(0, KDSBORDER, color) != 0) { revert(); err(1, "ioctl(KD_SBORDER)"); } } static void set_mouse_char(char *arg) { struct mouse_info mouse; long l; l = strtol(arg, NULL, 0); if ((l < 0) || (l > UCHAR_MAX - 3)) { revert(); warnx("argument to -M must be 0 through %d", UCHAR_MAX - 3); return; } mouse.operation = MOUSE_MOUSECHAR; mouse.u.mouse_char = (int)l; if (ioctl(0, CONS_MOUSECTL, &mouse) == -1) { revert(); err(1, "setting mouse character"); } } /* * Show/hide the mouse. */ static void set_mouse(char *arg) { struct mouse_info mouse; if (!strcmp(arg, "on")) { mouse.operation = MOUSE_SHOW; } else if (!strcmp(arg, "off")) { mouse.operation = MOUSE_HIDE; } else { revert(); errx(1, "argument to -m must be either on or off"); } if (ioctl(0, CONS_MOUSECTL, &mouse) == -1) { revert(); err(1, "%sing the mouse", mouse.operation == MOUSE_SHOW ? "show" : "hid"); } } static void set_lockswitch(char *arg) { int data; if (!strcmp(arg, "off")) { data = 0x01; } else if (!strcmp(arg, "on")) { data = 0x02; } else { revert(); errx(1, "argument to -S must be either on or off"); } if (ioctl(0, VT_LOCKSWITCH, &data) == -1) { revert(); err(1, "turning %s vty switching", data == 0x01 ? "off" : "on"); } } /* * Return the adapter name for a specified type. */ static const char *adapter_name(int type) { static struct { int type; const char *name; } names[] = { { KD_MONO, "MDA" }, { KD_HERCULES, "Hercules" }, { KD_CGA, "CGA" }, { KD_EGA, "EGA" }, { KD_VGA, "VGA" }, { KD_TGA, "TGA" }, { -1, "Unknown" }, }; int i; for (i = 0; names[i].type != -1; ++i) if (names[i].type == type) break; return names[i].name; } /* * Show active VTY, ie current console number. */ static void show_active_info(void) { printf("%d\n", cur_info.active_vty); } /* * Show graphics adapter information. */ static void show_adapter_info(void) { struct video_adapter_info ad; ad.va_index = 0; if (ioctl(0, CONS_ADPINFO, &ad) == -1) { revert(); err(1, "obtaining adapter information"); } printf("fb%d:\n", ad.va_index); printf(" %.*s%d, type:%s%s (%d), flags:0x%x\n", (int)sizeof(ad.va_name), ad.va_name, ad.va_unit, (ad.va_flags & V_ADP_VESA) ? "VESA " : "", adapter_name(ad.va_type), ad.va_type, ad.va_flags); printf(" initial mode:%d, current mode:%d, BIOS mode:%d\n", ad.va_initial_mode, ad.va_mode, ad.va_initial_bios_mode); printf(" frame buffer window:0x%zx, buffer size:0x%zx\n", ad.va_window, ad.va_buffer_size); printf(" window size:0x%zx, origin:0x%x\n", ad.va_window_size, ad.va_window_orig); printf(" display start address (%d, %d), scan line width:%d\n", ad.va_disp_start.x, ad.va_disp_start.y, ad.va_line_width); printf(" reserved:0x%zx\n", ad.va_unused0); } /* * Show video mode information. */ static void show_mode_info(void) { char buf[80]; struct video_info info; int c; int mm; int mode; printf(" mode# flags type size " "font window linear buffer\n"); printf("---------------------------------------" "---------------------------------------\n"); memset(&info, 0, sizeof(info)); for (mode = 0; mode <= M_VESA_MODE_MAX; ++mode) { info.vi_mode = mode; if (ioctl(0, CONS_MODEINFO, &info)) continue; if (info.vi_mode != mode) continue; if (info.vi_width == 0 && info.vi_height == 0 && info.vi_cwidth == 0 && info.vi_cheight == 0) continue; printf("%3d (0x%03x)", mode, mode); printf(" 0x%08x", info.vi_flags); if (info.vi_flags & V_INFO_GRAPHICS) { c = 'G'; if (info.vi_mem_model == V_INFO_MM_PLANAR) snprintf(buf, sizeof(buf), "%dx%dx%d %d", info.vi_width, info.vi_height, info.vi_depth, info.vi_planes); else { switch (info.vi_mem_model) { case V_INFO_MM_PACKED: mm = 'P'; break; case V_INFO_MM_DIRECT: mm = 'D'; break; case V_INFO_MM_CGA: mm = 'C'; break; case V_INFO_MM_HGC: mm = 'H'; break; case V_INFO_MM_VGAX: mm = 'V'; break; default: mm = ' '; break; } snprintf(buf, sizeof(buf), "%dx%dx%d %c", info.vi_width, info.vi_height, info.vi_depth, mm); } } else { c = 'T'; snprintf(buf, sizeof(buf), "%dx%d", info.vi_width, info.vi_height); } printf(" %c %-15s", c, buf); snprintf(buf, sizeof(buf), "%dx%d", info.vi_cwidth, info.vi_cheight); printf(" %-5s", buf); printf(" 0x%05zx %2dk %2dk", info.vi_window, (int)info.vi_window_size/1024, (int)info.vi_window_gran/1024); printf(" 0x%08zx %dk\n", info.vi_buffer, (int)info.vi_buffer_size/1024); } } static void show_info(char *arg) { if (!strcmp(arg, "active")) { show_active_info(); } else if (!strcmp(arg, "adapter")) { show_adapter_info(); } else if (!strcmp(arg, "mode")) { show_mode_info(); } else { revert(); errx(1, "argument to -i must be active, adapter, or mode"); } } static void test_frame(void) { vid_info_t info; const char *bg, *sep; int i, fore; info.size = sizeof(info); if (ioctl(0, CONS_GETINFO, &info) == -1) err(1, "getting console information"); fore = 15; if (info.mv_csz < 80) { bg = "BG"; sep = " "; } else { bg = "BACKGROUND"; sep = " "; } fprintf(stdout, "\033[=0G\n\n"); for (i=0; i<8; i++) { fprintf(stdout, "\033[=%dF\033[=0G%2d \033[=%dF%-7s%s" "\033[=%dF\033[=0G%2d \033[=%dF%-12s%s" "\033[=%dF%2d \033[=%dG%s\033[=0G%s" "\033[=%dF%2d \033[=%dG%s\033[=0G\n", fore, i, i, legal_colors[i], sep, fore, i + 8, i + 8, legal_colors[i + 8], sep, fore, i, i, bg, sep, fore, i + 8, i + 8, bg); } fprintf(stdout, "\033[=%dF\033[=%dG\033[=%dH\033[=%dI\n", info.mv_norm.fore, info.mv_norm.back, info.mv_rev.fore, info.mv_rev.back); } /* * Snapshot the video memory of that terminal, using the CONS_SCRSHOT * ioctl, and writes the results to stdout either in the special * binary format (see manual page for details), or in the plain * text format. */ static void dump_screen(int mode, int opt) { scrshot_t shot; vid_info_t info; info.size = sizeof(info); if (ioctl(0, CONS_GETINFO, &info) == -1) { revert(); err(1, "getting console information"); } shot.x = shot.y = 0; shot.xsize = info.mv_csz; shot.ysize = info.mv_rsz; if (opt == DUMP_ALL) shot.ysize += info.mv_hsz; shot.buf = alloca(shot.xsize * shot.ysize * sizeof(u_int16_t)); if (shot.buf == NULL) { revert(); errx(1, "failed to allocate memory for dump"); } if (ioctl(0, CONS_SCRSHOT, &shot) == -1) { revert(); err(1, "dumping screen"); } if (mode == DUMP_FMT_RAW) { printf("SCRSHOT_%c%c%c%c", DUMP_FMT_REV, 2, shot.xsize, shot.ysize); fflush(stdout); write(STDOUT_FILENO, shot.buf, shot.xsize * shot.ysize * sizeof(u_int16_t)); } else { char *line; int x, y; u_int16_t ch; line = alloca(shot.xsize + 1); if (line == NULL) { revert(); errx(1, "failed to allocate memory for line buffer"); } for (y = 0; y < shot.ysize; y++) { for (x = 0; x < shot.xsize; x++) { ch = shot.buf[x + (y * shot.xsize)]; ch &= 0xff; if (isprint(ch) == 0) ch = ' '; line[x] = (char)ch; } /* Trim trailing spaces */ do { line[x--] = '\0'; } while (line[x] == ' ' && x != 0); puts(line); } fflush(stdout); } } /* * Set the console history buffer size. */ static void set_history(char *opt) { int size; size = atoi(opt); if ((*opt == '\0') || size < 0) { revert(); errx(1, "argument must not be less than zero"); } if (ioctl(0, CONS_HISTORY, &size) == -1) { revert(); err(1, "setting history buffer size"); } } /* * Clear the console history buffer. */ static void clear_history(void) { if (ioctl(0, CONS_CLRHIST) == -1) { revert(); err(1, "clearing history buffer"); } } static int get_terminal_emulator(int i, struct term_info *tip) { tip->ti_index = i; if (ioctl(0, CONS_GETTERM, tip) == 0) return (1); strlcpy((char *)tip->ti_name, "unknown", sizeof(tip->ti_name)); strlcpy((char *)tip->ti_desc, "unknown", sizeof(tip->ti_desc)); return (0); } static void get_terminal_emulators(void) { struct term_info ti; int i; for (i = 0; i < 10; i++) { if (get_terminal_emulator(i, &ti) == 0) break; printf("%d: %s (%s)%s\n", i, ti.ti_name, ti.ti_desc, i == 0 ? " (active)" : ""); } } static void set_terminal_emulator(const char *name) { struct term_info old_ti, ti; get_terminal_emulator(0, &old_ti); strlcpy((char *)ti.ti_name, name, sizeof(ti.ti_name)); if (ioctl(0, CONS_SETTERM, &ti) != 0) warn("SETTERM '%s'", name); get_terminal_emulator(0, &ti); printf("%s (%s) -> %s (%s)\n", old_ti.ti_name, old_ti.ti_desc, ti.ti_name, ti.ti_desc); } static void set_terminal_mode(char *arg) { if (strcmp(arg, "xterm") == 0) fprintf(stderr, "\033[=T"); else if (strcmp(arg, "cons25") == 0) fprintf(stderr, "\033[=1T"); } int main(int argc, char **argv) { char *font, *type, *termmode; const char *opts; int dumpmod, dumpopt, opt; vt4_mode = is_vt4(); init(); dumpmod = 0; dumpopt = DUMP_FBF; termmode = NULL; if (vt4_mode) opts = "b:Cc:fg:h:i:M:m:r:S:s:T:t:x"; else opts = "b:Cc:deE:fg:h:Hi:l:LM:m:pPr:S:s:T:t:x"; while ((opt = getopt(argc, argv, opts)) != -1) switch(opt) { case 'b': set_border_color(optarg); break; case 'C': clear_history(); break; case 'c': set_cursor_type(optarg); break; case 'd': if (vt4_mode) break; print_scrnmap(); break; case 'E': if (vt4_mode) break; set_terminal_emulator(optarg); break; case 'e': if (vt4_mode) break; get_terminal_emulators(); break; case 'f': optarg = nextarg(argc, argv, &optind, 'f', 0); if (optarg != NULL) { font = nextarg(argc, argv, &optind, 'f', 0); if (font == NULL) { type = NULL; font = optarg; } else type = optarg; load_font(type, font); } else { if (!vt4_mode) usage(); /* Switch syscons to ROM? */ load_default_vt4font(); } break; case 'g': if (sscanf(optarg, "%dx%d", &vesa_cols, &vesa_rows) != 2) { revert(); warnx("incorrect geometry: %s", optarg); usage(); } break; case 'h': set_history(optarg); break; case 'H': dumpopt = DUMP_ALL; break; case 'i': show_info(optarg); break; case 'l': if (vt4_mode) break; load_scrnmap(optarg); break; case 'L': if (vt4_mode) break; load_default_scrnmap(); break; case 'M': set_mouse_char(optarg); break; case 'm': set_mouse(optarg); break; case 'p': dumpmod = DUMP_FMT_RAW; break; case 'P': dumpmod = DUMP_FMT_TXT; break; case 'r': set_reverse_colors(argc, argv, &optind); break; case 'S': set_lockswitch(optarg); break; case 's': set_console(optarg); break; case 'T': if (strcmp(optarg, "xterm") != 0 && strcmp(optarg, "cons25") != 0) usage(); termmode = optarg; break; case 't': set_screensaver_timeout(optarg); break; case 'x': hex = 1; break; default: usage(); } if (dumpmod != 0) dump_screen(dumpmod, dumpopt); video_mode(argc, argv, &optind); set_normal_colors(argc, argv, &optind); if (optind < argc && !strcmp(argv[optind], "show")) { test_frame(); optind++; } if (termmode != NULL) set_terminal_mode(termmode); if ((optind != argc) || (argc == 1)) usage(); return (0); }