Index: stable/12/lib/libc/gen/getgrent.c =================================================================== --- stable/12/lib/libc/gen/getgrent.c (revision 360413) +++ stable/12/lib/libc/gen/getgrent.c (revision 360414) @@ -1,1568 +1,1568 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2003 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by * Jacques A. Vidrine, Safeport Network Services, 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. * * 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. * */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #ifdef YP #include #include #include #endif #include #include #include #ifdef HESIOD #include #endif #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" #include "nss_tls.h" #ifdef NS_CACHING #include "nscache.h" #endif enum constants { GRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ GRP_STORAGE_MAX = 1 << 20, /* 1 MByte */ SETGRENT = 1, ENDGRENT = 2, HESIOD_NAME_MAX = 256, }; static const ns_src defaultsrc[] = { { NSSRC_COMPAT, NS_SUCCESS }, { NULL, 0 } }; int __getgroupmembership(const char *, gid_t, gid_t *, int, int *); int __gr_match_entry(const char *, size_t, enum nss_lookup_type, const char *, gid_t); int __gr_parse_entry(char *, size_t, struct group *, char *, size_t, int *); static int is_comment_line(const char *, size_t); union key { const char *name; gid_t gid; }; static struct group *getgr(int (*)(union key, struct group *, char *, size_t, struct group **), union key); static int wrap_getgrnam_r(union key, struct group *, char *, size_t, struct group **); static int wrap_getgrgid_r(union key, struct group *, char *, size_t, struct group **); static int wrap_getgrent_r(union key, struct group *, char *, size_t, struct group **); struct files_state { FILE *fp; int stayopen; }; static void files_endstate(void *); NSS_TLS_HANDLING(files); static int files_setgrent(void *, void *, va_list); static int files_group(void *, void *, va_list); #ifdef HESIOD struct dns_state { long counter; }; static void dns_endstate(void *); NSS_TLS_HANDLING(dns); static int dns_setgrent(void *, void *, va_list); static int dns_group(void *, void *, va_list); #endif #ifdef YP struct nis_state { char domain[MAXHOSTNAMELEN]; int done; char *key; int keylen; }; static void nis_endstate(void *); NSS_TLS_HANDLING(nis); static int nis_setgrent(void *, void *, va_list); static int nis_group(void *, void *, va_list); #endif struct compat_state { FILE *fp; int stayopen; char *name; enum _compat { COMPAT_MODE_OFF = 0, COMPAT_MODE_ALL, COMPAT_MODE_NAME } compat; }; static void compat_endstate(void *); NSS_TLS_HANDLING(compat); static int compat_setgrent(void *, void *, va_list); static int compat_group(void *, void *, va_list); static int gr_addgid(gid_t, gid_t *, int, int *); static int getgroupmembership_fallback(void *, void *, va_list); #ifdef NS_CACHING static int grp_id_func(char *, size_t *, va_list, void *); static int grp_marshal_func(char *, size_t *, void *, va_list, void *); static int grp_unmarshal_func(char *, size_t, void *, va_list, void *); static int grp_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { char *name; gid_t gid; size_t desired_size, size; int res = NS_UNAVAIL; enum nss_lookup_type lookup_type; - lookup_type = (enum nss_lookup_type)cache_mdata; + lookup_type = (enum nss_lookup_type)(uintptr_t)cache_mdata; switch (lookup_type) { case nss_lt_name: name = va_arg(ap, char *); size = strlen(name); desired_size = sizeof(enum nss_lookup_type) + size + 1; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); res = NS_SUCCESS; break; case nss_lt_id: gid = va_arg(ap, gid_t); desired_size = sizeof(enum nss_lookup_type) + sizeof(gid_t); if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), &gid, sizeof(gid_t)); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } static int grp_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; gid_t gid; struct group *grp; char *orig_buf; size_t orig_buf_size; struct group new_grp; size_t desired_size, size, mem_size; char *p, **mem; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: gid = va_arg(ap, gid_t); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } grp = va_arg(ap, struct group *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); desired_size = _ALIGNBYTES + sizeof(struct group) + sizeof(char *); if (grp->gr_name != NULL) desired_size += strlen(grp->gr_name) + 1; if (grp->gr_passwd != NULL) desired_size += strlen(grp->gr_passwd) + 1; if (grp->gr_mem != NULL) { mem_size = 0; for (mem = grp->gr_mem; *mem; ++mem) { desired_size += strlen(*mem) + 1; ++mem_size; } desired_size += _ALIGNBYTES + (mem_size + 1) * sizeof(char *); } if (desired_size > *buffer_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } memcpy(&new_grp, grp, sizeof(struct group)); memset(buffer, 0, desired_size); *buffer_size = desired_size; p = buffer + sizeof(struct group) + sizeof(char *); memcpy(buffer + sizeof(struct group), &p, sizeof(char *)); p = (char *)_ALIGN(p); if (new_grp.gr_name != NULL) { size = strlen(new_grp.gr_name); memcpy(p, new_grp.gr_name, size); new_grp.gr_name = p; p += size + 1; } if (new_grp.gr_passwd != NULL) { size = strlen(new_grp.gr_passwd); memcpy(p, new_grp.gr_passwd, size); new_grp.gr_passwd = p; p += size + 1; } if (new_grp.gr_mem != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size); new_grp.gr_mem = (char **)p; p += sizeof(char *) * (mem_size + 1); for (mem = new_grp.gr_mem; *mem; ++mem) { size = strlen(*mem); memcpy(p, *mem, size); *mem = p; p += size + 1; } } memcpy(buffer, &new_grp, sizeof(struct group)); return (NS_SUCCESS); } static int grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; gid_t gid; struct group *grp; char *orig_buf; size_t orig_buf_size; int *ret_errno; char *p; char **mem; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: gid = va_arg(ap, gid_t); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } grp = va_arg(ap, struct group *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); ret_errno = va_arg(ap, int *); if (orig_buf_size < buffer_size - sizeof(struct group) - sizeof(char *)) { *ret_errno = ERANGE; return (NS_RETURN); } memcpy(grp, buffer, sizeof(struct group)); memcpy(&p, buffer + sizeof(struct group), sizeof(char *)); orig_buf = (char *)_ALIGN(orig_buf); memcpy(orig_buf, buffer + sizeof(struct group) + sizeof(char *) + _ALIGN(p) - (size_t)p, buffer_size - sizeof(struct group) - sizeof(char *) - _ALIGN(p) + (size_t)p); p = (char *)_ALIGN(p); NS_APPLY_OFFSET(grp->gr_name, orig_buf, p, char *); NS_APPLY_OFFSET(grp->gr_passwd, orig_buf, p, char *); if (grp->gr_mem != NULL) { NS_APPLY_OFFSET(grp->gr_mem, orig_buf, p, char **); for (mem = grp->gr_mem; *mem; ++mem) NS_APPLY_OFFSET(*mem, orig_buf, p, char *); } if (retval != NULL) *((struct group **)retval) = grp; return (NS_SUCCESS); } NSS_MP_CACHE_HANDLING(group); #endif /* NS_CACHING */ #ifdef NS_CACHING static const nss_cache_info setgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER( group, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab setgrent_dtab[] = { { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, #ifdef HESIOD { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, #endif #ifdef YP { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, #endif { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, #ifdef NS_CACHING NS_CACHE_CB(&setgrent_cache_info) #endif { NULL, NULL, NULL } }; #ifdef NS_CACHING static const nss_cache_info endgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER( group, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab endgrent_dtab[] = { { NSSRC_FILES, files_setgrent, (void *)ENDGRENT }, #ifdef HESIOD { NSSRC_DNS, dns_setgrent, (void *)ENDGRENT }, #endif #ifdef YP { NSSRC_NIS, nis_setgrent, (void *)ENDGRENT }, #endif { NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT }, #ifdef NS_CACHING NS_CACHE_CB(&endgrent_cache_info) #endif { NULL, NULL, NULL } }; #ifdef NS_CACHING static const nss_cache_info getgrent_r_cache_info = NS_MP_CACHE_INFO_INITIALIZER( group, (void *)nss_lt_all, grp_marshal_func, grp_unmarshal_func); #endif static const ns_dtab getgrent_r_dtab[] = { { NSSRC_FILES, files_group, (void *)nss_lt_all }, #ifdef HESIOD { NSSRC_DNS, dns_group, (void *)nss_lt_all }, #endif #ifdef YP { NSSRC_NIS, nis_group, (void *)nss_lt_all }, #endif { NSSRC_COMPAT, compat_group, (void *)nss_lt_all }, #ifdef NS_CACHING NS_CACHE_CB(&getgrent_r_cache_info) #endif { NULL, NULL, NULL } }; static int gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *grpcnt) { int ret, dupc; for (dupc = 1; dupc < MIN(maxgrp, *grpcnt); dupc++) { if (groups[dupc] == gid) return 1; } ret = 1; if (*grpcnt < maxgrp) groups[*grpcnt] = gid; else ret = 0; (*grpcnt)++; return ret; } static int getgroupmembership_fallback(void *retval, void *mdata, va_list ap) { const ns_src src[] = { { mdata, NS_SUCCESS }, { NULL, 0} }; struct group grp; struct group *grp_p; char *buf; size_t bufsize; const char *uname; gid_t *groups; gid_t agroup; int maxgrp, *grpcnt; int i, rv, ret_errno; /* * As this is a fallback method, only provided src * list will be respected during methods search. */ assert(src[0].name != NULL); uname = va_arg(ap, const char *); agroup = va_arg(ap, gid_t); groups = va_arg(ap, gid_t *); maxgrp = va_arg(ap, int); grpcnt = va_arg(ap, int *); rv = NS_UNAVAIL; buf = malloc(GRP_STORAGE_INITIAL); if (buf == NULL) goto out; bufsize = GRP_STORAGE_INITIAL; gr_addgid(agroup, groups, maxgrp, grpcnt); _nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", src, 0); for (;;) { do { ret_errno = 0; grp_p = NULL; rv = _nsdispatch(&grp_p, getgrent_r_dtab, NSDB_GROUP, "getgrent_r", src, &grp, buf, bufsize, &ret_errno); if (grp_p == NULL && ret_errno == ERANGE) { free(buf); if ((bufsize << 1) > GRP_STORAGE_MAX) { buf = NULL; errno = ERANGE; goto out; } bufsize <<= 1; buf = malloc(bufsize); if (buf == NULL) { goto out; } } } while (grp_p == NULL && ret_errno == ERANGE); if (ret_errno != 0) { errno = ret_errno; goto out; } if (grp_p == NULL) break; for (i = 0; grp.gr_mem[i]; i++) { if (strcmp(grp.gr_mem[i], uname) == 0) gr_addgid(grp.gr_gid, groups, maxgrp, grpcnt); } } _nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", src); out: free(buf); return (rv); } void setgrent(void) { (void)_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc, 0); } int setgroupent(int stayopen) { (void)_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc, stayopen); return (1); } void endgrent(void) { (void)_nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", defaultsrc); } int getgrent_r(struct group *grp, char *buffer, size_t bufsize, struct group **result) { int rv, ret_errno; ret_errno = 0; *result = NULL; rv = _nsdispatch(result, getgrent_r_dtab, NSDB_GROUP, "getgrent_r", defaultsrc, grp, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, struct group **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( group, (void *)nss_lt_name, grp_id_func, grp_marshal_func, grp_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_group, (void *)nss_lt_name }, #ifdef HESIOD { NSSRC_DNS, dns_group, (void *)nss_lt_name }, #endif #ifdef YP { NSSRC_NIS, nis_group, (void *)nss_lt_name }, #endif { NSSRC_COMPAT, compat_group, (void *)nss_lt_name }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrnam_r", defaultsrc, name, grp, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( group, (void *)nss_lt_id, grp_id_func, grp_marshal_func, grp_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_group, (void *)nss_lt_id }, #ifdef HESIOD { NSSRC_DNS, dns_group, (void *)nss_lt_id }, #endif #ifdef YP { NSSRC_NIS, nis_group, (void *)nss_lt_id }, #endif { NSSRC_COMPAT, compat_group, (void *)nss_lt_id }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrgid_r", defaultsrc, gid, grp, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int __getgroupmembership(const char *uname, gid_t agroup, gid_t *groups, int maxgrp, int *grpcnt) { static const ns_dtab dtab[] = { NS_FALLBACK_CB(getgroupmembership_fallback) { NULL, NULL, NULL } }; assert(uname != NULL); /* groups may be NULL if just sizing when invoked with maxgrp = 0 */ assert(grpcnt != NULL); *grpcnt = 0; (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership", defaultsrc, uname, agroup, groups, maxgrp, grpcnt); /* too many groups found? */ return (*grpcnt > maxgrp ? -1 : 0); } static struct group grp; static char *grp_storage; static size_t grp_storage_size; static struct group * getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **), union key key) { int rv; struct group *res; if (grp_storage == NULL) { grp_storage = malloc(GRP_STORAGE_INITIAL); if (grp_storage == NULL) return (NULL); grp_storage_size = GRP_STORAGE_INITIAL; } do { rv = fn(key, &grp, grp_storage, grp_storage_size, &res); if (res == NULL && rv == ERANGE) { free(grp_storage); if ((grp_storage_size << 1) > GRP_STORAGE_MAX) { grp_storage = NULL; errno = ERANGE; return (NULL); } grp_storage_size <<= 1; grp_storage = malloc(grp_storage_size); if (grp_storage == NULL) return (NULL); } } while (res == NULL && rv == ERANGE); if (rv != 0) errno = rv; return (res); } static int wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize, struct group **res) { return (getgrnam_r(key.name, grp, buffer, bufsize, res)); } static int wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize, struct group **res) { return (getgrgid_r(key.gid, grp, buffer, bufsize, res)); } static int wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer, size_t bufsize, struct group **res) { return (getgrent_r(grp, buffer, bufsize, res)); } struct group * getgrnam(const char *name) { union key key; key.name = name; return (getgr(wrap_getgrnam_r, key)); } struct group * getgrgid(gid_t gid) { union key key; key.gid = gid; return (getgr(wrap_getgrgid_r, key)); } struct group * getgrent(void) { union key key; key.gid = 0; /* not used */ return (getgr(wrap_getgrent_r, key)); } static int is_comment_line(const char *s, size_t n) { const char *eom; eom = &s[n]; for (; s < eom; s++) if (*s == '#' || !isspace((unsigned char)*s)) break; return (*s == '#' || s == eom); } /* * files backend */ static void files_endstate(void *p) { if (p == NULL) return; if (((struct files_state *)p)->fp != NULL) fclose(((struct files_state *)p)->fp); free(p); } static int files_setgrent(void *retval, void *mdata, va_list ap) { struct files_state *st; int rv, stayopen; rv = files_getstate(&st); if (rv != 0) return (NS_UNAVAIL); - switch ((enum constants)mdata) { + switch ((enum constants)(uintptr_t)mdata) { case SETGRENT: stayopen = va_arg(ap, int); if (st->fp != NULL) rewind(st->fp); else if (stayopen) st->fp = fopen(_PATH_GROUP, "re"); break; case ENDGRENT: if (st->fp != NULL) { fclose(st->fp); st->fp = NULL; } break; default: break; } return (NS_UNAVAIL); } static int files_group(void *retval, void *mdata, va_list ap) { struct files_state *st; enum nss_lookup_type how; const char *name, *line; struct group *grp; gid_t gid; char *buffer; size_t bufsize, linesize; off_t pos; int fresh, rv, stayopen, *errnop; fresh = 0; name = NULL; gid = (gid_t)-1; - how = (enum nss_lookup_type)mdata; + how = (enum nss_lookup_type)(uintptr_t)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: gid = va_arg(ap, gid_t); break; case nss_lt_all: break; default: return (NS_NOTFOUND); } grp = va_arg(ap, struct group *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = files_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->fp == NULL) { st->fp = fopen(_PATH_GROUP, "re"); if (st->fp == NULL) { *errnop = errno; return (NS_UNAVAIL); } fresh = 1; } if (how == nss_lt_all) stayopen = 1; else { if (!fresh) rewind(st->fp); stayopen = st->stayopen; } rv = NS_NOTFOUND; if (stayopen) pos = ftello(st->fp); while ((line = fgetln(st->fp, &linesize)) != NULL) { if (line[linesize-1] == '\n') linesize--; rv = __gr_match_entry(line, linesize, how, name, gid); if (rv != NS_SUCCESS) continue; /* We need room at least for the line, a string NUL * terminator, alignment padding, and one (char *) * pointer for the member list terminator. */ if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, line, linesize); buffer[linesize] = '\0'; rv = __gr_parse_entry(buffer, linesize, grp, &buffer[linesize + 1], bufsize - linesize - 1, errnop); if (rv & NS_TERMINATE) break; if (stayopen) pos = ftello(st->fp); } if (st->fp != NULL && !stayopen) { fclose(st->fp); st->fp = NULL; } if (rv == NS_SUCCESS && retval != NULL) *(struct group **)retval = grp; else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL) fseeko(st->fp, pos, SEEK_SET); return (rv); } #ifdef HESIOD /* * dns backend */ static void dns_endstate(void *p) { free(p); } static int dns_setgrent(void *retval, void *cb_data, va_list ap) { struct dns_state *st; int rv; rv = dns_getstate(&st); if (rv != 0) return (NS_UNAVAIL); st->counter = 0; return (NS_UNAVAIL); } static int dns_group(void *retval, void *mdata, va_list ap) { char buf[HESIOD_NAME_MAX]; struct dns_state *st; struct group *grp; const char *name, *label; void *ctx; char *buffer, **hes; size_t bufsize, adjsize, linesize; gid_t gid; enum nss_lookup_type how; int rv, *errnop; ctx = NULL; hes = NULL; name = NULL; gid = (gid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: gid = va_arg(ap, gid_t); break; case nss_lt_all: break; } grp = va_arg(ap, struct group *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = dns_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (hesiod_init(&ctx) != 0) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } do { rv = NS_NOTFOUND; switch (how) { case nss_lt_name: label = name; break; case nss_lt_id: if (snprintf(buf, sizeof(buf), "%lu", (unsigned long)gid) >= sizeof(buf)) goto fin; label = buf; break; case nss_lt_all: if (st->counter < 0) goto fin; if (snprintf(buf, sizeof(buf), "group-%ld", st->counter++) >= sizeof(buf)) goto fin; label = buf; break; } hes = hesiod_resolve(ctx, label, how == nss_lt_id ? "gid" : "group"); if ((how == nss_lt_id && hes == NULL && (hes = hesiod_resolve(ctx, buf, "group")) == NULL) || hes == NULL) { if (how == nss_lt_all) st->counter = -1; if (errno != ENOENT) *errnop = errno; goto fin; } rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid); if (rv != NS_SUCCESS) { hesiod_free_list(ctx, hes); hes = NULL; continue; } /* We need room at least for the line, a string NUL * terminator, alignment padding, and one (char *) * pointer for the member list terminator. */ adjsize = bufsize - _ALIGNBYTES - sizeof(char *); linesize = strlcpy(buffer, hes[0], adjsize); if (linesize >= adjsize) { *errnop = ERANGE; rv = NS_RETURN; goto fin; } hesiod_free_list(ctx, hes); hes = NULL; rv = __gr_parse_entry(buffer, linesize, grp, &buffer[linesize + 1], bufsize - linesize - 1, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (hes != NULL) hesiod_free_list(ctx, hes); if (ctx != NULL) hesiod_end(ctx); if (rv == NS_SUCCESS && retval != NULL) *(struct group **)retval = grp; return (rv); } #endif /* HESIOD */ #ifdef YP /* * nis backend */ static void nis_endstate(void *p) { if (p == NULL) return; free(((struct nis_state *)p)->key); free(p); } static int nis_setgrent(void *retval, void *cb_data, va_list ap) { struct nis_state *st; int rv; rv = nis_getstate(&st); if (rv != 0) return (NS_UNAVAIL); st->done = 0; free(st->key); st->key = NULL; return (NS_UNAVAIL); } static int nis_group(void *retval, void *mdata, va_list ap) { char *map; struct nis_state *st; struct group *grp; const char *name; char *buffer, *key, *result; size_t bufsize; gid_t gid; enum nss_lookup_type how; int *errnop, keylen, resultlen, rv; name = NULL; gid = (gid_t)-1; - how = (enum nss_lookup_type)mdata; + how = (enum nss_lookup_type)(uintptr_t)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); map = "group.byname"; break; case nss_lt_id: gid = va_arg(ap, gid_t); map = "group.bygid"; break; case nss_lt_all: map = "group.byname"; break; } grp = va_arg(ap, struct group *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = nis_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->domain[0] == '\0') { if (getdomainname(st->domain, sizeof(st->domain)) != 0) { *errnop = errno; return (NS_UNAVAIL); } } result = NULL; do { rv = NS_NOTFOUND; switch (how) { case nss_lt_name: if (strlcpy(buffer, name, bufsize) >= bufsize) goto erange; break; case nss_lt_id: if (snprintf(buffer, bufsize, "%lu", (unsigned long)gid) >= bufsize) goto erange; break; case nss_lt_all: if (st->done) goto fin; break; } result = NULL; if (how == nss_lt_all) { if (st->key == NULL) rv = yp_first(st->domain, map, &st->key, &st->keylen, &result, &resultlen); else { key = st->key; keylen = st->keylen; st->key = NULL; rv = yp_next(st->domain, map, key, keylen, &st->key, &st->keylen, &result, &resultlen); free(key); } if (rv != 0) { free(result); free(st->key); st->key = NULL; if (rv == YPERR_NOMORE) { st->done = 1; rv = NS_NOTFOUND; } else rv = NS_UNAVAIL; goto fin; } } else { rv = yp_match(st->domain, map, buffer, strlen(buffer), &result, &resultlen); if (rv == YPERR_KEY) { rv = NS_NOTFOUND; continue; } else if (rv != 0) { free(result); rv = NS_UNAVAIL; continue; } } /* We need room at least for the line, a string NUL * terminator, alignment padding, and one (char *) * pointer for the member list terminator. */ if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *)) { free(result); goto erange; } memcpy(buffer, result, resultlen); buffer[resultlen] = '\0'; free(result); rv = __gr_match_entry(buffer, resultlen, how, name, gid); if (rv == NS_SUCCESS) rv = __gr_parse_entry(buffer, resultlen, grp, &buffer[resultlen+1], bufsize - resultlen - 1, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (rv == NS_SUCCESS && retval != NULL) *(struct group **)retval = grp; return (rv); erange: *errnop = ERANGE; return (NS_RETURN); } #endif /* YP */ /* * compat backend */ static void compat_endstate(void *p) { struct compat_state *st; if (p == NULL) return; st = (struct compat_state *)p; free(st->name); if (st->fp != NULL) fclose(st->fp); free(p); } static int compat_setgrent(void *retval, void *mdata, va_list ap) { static const ns_src compatsrc[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab dtab[] = { #ifdef HESIOD { NSSRC_DNS, dns_setgrent, NULL }, #endif #ifdef YP { NSSRC_NIS, nis_setgrent, NULL }, #endif { NULL, NULL, NULL } }; struct compat_state *st; int rv, stayopen; #define set_setent(x, y) do { \ int i; \ for (i = 0; i < (int)(nitems(x) - 1); i++) \ x[i].mdata = (void *)y; \ } while (0) rv = compat_getstate(&st); if (rv != 0) return (NS_UNAVAIL); - switch ((enum constants)mdata) { + switch ((enum constants)(uintptr_t)mdata) { case SETGRENT: stayopen = va_arg(ap, int); if (st->fp != NULL) rewind(st->fp); else if (stayopen) st->fp = fopen(_PATH_GROUP, "re"); set_setent(dtab, mdata); (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent", compatsrc, 0); break; case ENDGRENT: if (st->fp != NULL) { fclose(st->fp); st->fp = NULL; } set_setent(dtab, mdata); (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent", compatsrc, 0); break; default: break; } st->compat = COMPAT_MODE_OFF; free(st->name); st->name = NULL; return (NS_UNAVAIL); #undef set_setent } static int compat_group(void *retval, void *mdata, va_list ap) { static const ns_src compatsrc[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab dtab[] = { #ifdef YP { NSSRC_NIS, nis_group, NULL }, #endif #ifdef HESIOD { NSSRC_DNS, dns_group, NULL }, #endif { NULL, NULL, NULL } }; struct compat_state *st; enum nss_lookup_type how; const char *name, *line; struct group *grp; gid_t gid; char *buffer, *p; void *discard; size_t bufsize, linesize; off_t pos; int fresh, rv, stayopen, *errnop; #define set_lookup_type(x, y) do { \ int i; \ for (i = 0; i < (int)(nitems(x) - 1); i++) \ x[i].mdata = (void *)y; \ } while (0) fresh = 0; name = NULL; gid = (gid_t)-1; - how = (enum nss_lookup_type)mdata; + how = (enum nss_lookup_type)(uintptr_t)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: gid = va_arg(ap, gid_t); break; case nss_lt_all: break; default: return (NS_NOTFOUND); } grp = va_arg(ap, struct group *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = compat_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->fp == NULL) { st->fp = fopen(_PATH_GROUP, "re"); if (st->fp == NULL) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } fresh = 1; } if (how == nss_lt_all) stayopen = 1; else { if (!fresh) rewind(st->fp); stayopen = st->stayopen; } docompat: switch (st->compat) { case COMPAT_MODE_ALL: set_lookup_type(dtab, how); switch (how) { case nss_lt_all: rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, "getgrent_r", compatsrc, grp, buffer, bufsize, errnop); break; case nss_lt_id: rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, "getgrgid_r", compatsrc, gid, grp, buffer, bufsize, errnop); break; case nss_lt_name: rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, "getgrnam_r", compatsrc, name, grp, buffer, bufsize, errnop); break; } if (rv & NS_TERMINATE) goto fin; st->compat = COMPAT_MODE_OFF; break; case COMPAT_MODE_NAME: set_lookup_type(dtab, nss_lt_name); rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize, errnop); switch (rv) { case NS_SUCCESS: switch (how) { case nss_lt_name: if (strcmp(name, grp->gr_name) != 0) rv = NS_NOTFOUND; break; case nss_lt_id: if (gid != grp->gr_gid) rv = NS_NOTFOUND; break; default: break; } break; case NS_RETURN: goto fin; default: break; } free(st->name); st->name = NULL; st->compat = COMPAT_MODE_OFF; if (rv == NS_SUCCESS) goto fin; break; default: break; } rv = NS_NOTFOUND; if (stayopen) pos = ftello(st->fp); while ((line = fgetln(st->fp, &linesize)) != NULL) { if (line[linesize-1] == '\n') linesize--; if (linesize > 2 && line[0] == '+') { p = memchr(&line[1], ':', linesize); if (p == NULL || p == &line[1]) st->compat = COMPAT_MODE_ALL; else { st->name = malloc(p - line); if (st->name == NULL) { syslog(LOG_ERR, "getgrent memory allocation failure"); *errnop = ENOMEM; rv = NS_UNAVAIL; break; } memcpy(st->name, &line[1], p - line - 1); st->name[p - line - 1] = '\0'; st->compat = COMPAT_MODE_NAME; } goto docompat; } rv = __gr_match_entry(line, linesize, how, name, gid); if (rv != NS_SUCCESS) continue; /* We need room at least for the line, a string NUL * terminator, alignment padding, and one (char *) * pointer for the member list terminator. */ if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, line, linesize); buffer[linesize] = '\0'; rv = __gr_parse_entry(buffer, linesize, grp, &buffer[linesize + 1], bufsize - linesize - 1, errnop); if (rv & NS_TERMINATE) break; if (stayopen) pos = ftello(st->fp); } fin: if (st->fp != NULL && !stayopen) { fclose(st->fp); st->fp = NULL; } if (rv == NS_SUCCESS && retval != NULL) *(struct group **)retval = grp; else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL) fseeko(st->fp, pos, SEEK_SET); return (rv); #undef set_lookup_type } /* * common group line matching and parsing */ int __gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how, const char *name, gid_t gid) { size_t namesize; const char *p, *eol; char *q; unsigned long n; int i, needed; if (linesize == 0 || is_comment_line(line, linesize)) return (NS_NOTFOUND); switch (how) { case nss_lt_name: needed = 1; break; case nss_lt_id: needed = 2; break; default: needed = 2; break; } eol = &line[linesize]; for (p = line, i = 0; i < needed && p < eol; p++) if (*p == ':') i++; if (i < needed) return (NS_NOTFOUND); switch (how) { case nss_lt_name: namesize = strlen(name); if (namesize + 1 == (size_t)(p - line) && memcmp(line, name, namesize) == 0) return (NS_SUCCESS); break; case nss_lt_id: n = strtoul(p, &q, 10); if (q < eol && *q == ':' && gid == (gid_t)n) return (NS_SUCCESS); break; case nss_lt_all: return (NS_SUCCESS); default: break; } return (NS_NOTFOUND); } int __gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf, size_t membufsize, int *errnop) { char *s_gid, *s_mem, *p, **members; unsigned long n; int maxmembers; memset(grp, 0, sizeof(*grp)); members = (char **)_ALIGN(membuf); membufsize -= (char *)members - membuf; maxmembers = membufsize / sizeof(*members); if (maxmembers <= 0 || (grp->gr_name = strsep(&line, ":")) == NULL || grp->gr_name[0] == '\0' || (grp->gr_passwd = strsep(&line, ":")) == NULL || (s_gid = strsep(&line, ":")) == NULL || s_gid[0] == '\0') return (NS_NOTFOUND); s_mem = line; n = strtoul(s_gid, &s_gid, 10); if (s_gid[0] != '\0') return (NS_NOTFOUND); grp->gr_gid = (gid_t)n; grp->gr_mem = members; while (maxmembers > 1 && s_mem != NULL) { p = strsep(&s_mem, ","); if (p != NULL && *p != '\0') { *members++ = p; maxmembers--; } } *members = NULL; if (s_mem == NULL) return (NS_SUCCESS); else { *errnop = ERANGE; return (NS_RETURN); } } Index: stable/12/lib/libc/gen/getpwent.c =================================================================== --- stable/12/lib/libc/gen/getpwent.c (revision 360413) +++ stable/12/lib/libc/gen/getpwent.c (revision 360414) @@ -1,1993 +1,1993 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2003 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by * Jacques A. Vidrine, Safeport Network Services, 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. * * 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. * */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #ifdef YP #include #include #include #endif #include #include #include #ifdef HESIOD #include #endif #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include #include "libc_private.h" #include "pw_scan.h" #include "nss_tls.h" #ifdef NS_CACHING #include "nscache.h" #endif #ifndef CTASSERT #define CTASSERT(x) _CTASSERT(x, __LINE__) #define _CTASSERT(x, y) __CTASSERT(x, y) #define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1] #endif /* Counter as stored in /etc/pwd.db */ typedef int pwkeynum; CTASSERT(MAXLOGNAME > sizeof(uid_t)); CTASSERT(MAXLOGNAME > sizeof(pwkeynum)); enum constants { PWD_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ PWD_STORAGE_MAX = 1 << 20, /* 1 MByte */ SETPWENT = 1, ENDPWENT = 2, HESIOD_NAME_MAX = 256 }; static const ns_src defaultsrc[] = { { NSSRC_COMPAT, NS_SUCCESS }, { NULL, 0 } }; int __pw_match_entry(const char *, size_t, enum nss_lookup_type, const char *, uid_t); int __pw_parse_entry(char *, size_t, struct passwd *, int, int *errnop); union key { const char *name; uid_t uid; }; static struct passwd *getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **), union key); static int wrap_getpwnam_r(union key, struct passwd *, char *, size_t, struct passwd **); static int wrap_getpwuid_r(union key, struct passwd *, char *, size_t, struct passwd **); static int wrap_getpwent_r(union key, struct passwd *, char *, size_t, struct passwd **); static int pwdb_match_entry_v3(char *, size_t, enum nss_lookup_type, const char *, uid_t); static int pwdb_parse_entry_v3(char *, size_t, struct passwd *, int *); static int pwdb_match_entry_v4(char *, size_t, enum nss_lookup_type, const char *, uid_t); static int pwdb_parse_entry_v4(char *, size_t, struct passwd *, int *); struct { int (*match)(char *, size_t, enum nss_lookup_type, const char *, uid_t); int (*parse)(char *, size_t, struct passwd *, int *); } pwdb_versions[] = { { NULL, NULL }, /* version 0 */ { NULL, NULL }, /* version 1 */ { NULL, NULL }, /* version 2 */ { pwdb_match_entry_v3, pwdb_parse_entry_v3 }, /* version 3 */ { pwdb_match_entry_v4, pwdb_parse_entry_v4 }, /* version 4 */ }; struct files_state { DB *db; pwkeynum keynum; int stayopen; int version; }; static void files_endstate(void *); NSS_TLS_HANDLING(files); static DB *pwdbopen(int *); static void files_endstate(void *); static int files_setpwent(void *, void *, va_list); static int files_passwd(void *, void *, va_list); #ifdef HESIOD struct dns_state { long counter; }; static void dns_endstate(void *); NSS_TLS_HANDLING(dns); static int dns_setpwent(void *, void *, va_list); static int dns_passwd(void *, void *, va_list); #endif #ifdef YP struct nis_state { char domain[MAXHOSTNAMELEN]; int done; char *key; int keylen; }; static void nis_endstate(void *); NSS_TLS_HANDLING(nis); static int nis_setpwent(void *, void *, va_list); static int nis_passwd(void *, void *, va_list); static int nis_map(char *, enum nss_lookup_type, char *, size_t, int *); static int nis_adjunct(char *, const char *, char *, size_t); #endif struct compat_state { DB *db; pwkeynum keynum; int stayopen; int version; DB *exclude; struct passwd template; char *name; enum _compat { COMPAT_MODE_OFF = 0, COMPAT_MODE_ALL, COMPAT_MODE_NAME, COMPAT_MODE_NETGROUP } compat; }; static void compat_endstate(void *); NSS_TLS_HANDLING(compat); static int compat_setpwent(void *, void *, va_list); static int compat_passwd(void *, void *, va_list); static void compat_clear_template(struct passwd *); static int compat_set_template(struct passwd *, struct passwd *); static int compat_use_template(struct passwd *, struct passwd *, char *, size_t); static int compat_redispatch(struct compat_state *, enum nss_lookup_type, enum nss_lookup_type, const char *, const char *, uid_t, struct passwd *, char *, size_t, int *); #ifdef NS_CACHING static int pwd_id_func(char *, size_t *, va_list ap, void *); static int pwd_marshal_func(char *, size_t *, void *, va_list, void *); static int pwd_unmarshal_func(char *, size_t, void *, va_list, void *); static int pwd_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { char *name; uid_t uid; size_t size, desired_size; int res = NS_UNAVAIL; enum nss_lookup_type lookup_type; - lookup_type = (enum nss_lookup_type)cache_mdata; + lookup_type = (enum nss_lookup_type)(uintptr_t)cache_mdata; switch (lookup_type) { case nss_lt_name: name = va_arg(ap, char *); size = strlen(name); desired_size = sizeof(enum nss_lookup_type) + size + 1; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); res = NS_SUCCESS; break; case nss_lt_id: uid = va_arg(ap, uid_t); desired_size = sizeof(enum nss_lookup_type) + sizeof(uid_t); if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), &uid, sizeof(uid_t)); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } static int pwd_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; uid_t uid; struct passwd *pwd; char *orig_buf; size_t orig_buf_size; struct passwd new_pwd; size_t desired_size, size; char *p; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } pwd = va_arg(ap, struct passwd *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); desired_size = sizeof(struct passwd) + sizeof(char *) + strlen(pwd->pw_name) + 1; if (pwd->pw_passwd != NULL) desired_size += strlen(pwd->pw_passwd) + 1; if (pwd->pw_class != NULL) desired_size += strlen(pwd->pw_class) + 1; if (pwd->pw_gecos != NULL) desired_size += strlen(pwd->pw_gecos) + 1; if (pwd->pw_dir != NULL) desired_size += strlen(pwd->pw_dir) + 1; if (pwd->pw_shell != NULL) desired_size += strlen(pwd->pw_shell) + 1; if (*buffer_size < desired_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } memcpy(&new_pwd, pwd, sizeof(struct passwd)); memset(buffer, 0, desired_size); *buffer_size = desired_size; p = buffer + sizeof(struct passwd) + sizeof(char *); memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *)); if (new_pwd.pw_name != NULL) { size = strlen(new_pwd.pw_name); memcpy(p, new_pwd.pw_name, size); new_pwd.pw_name = p; p += size + 1; } if (new_pwd.pw_passwd != NULL) { size = strlen(new_pwd.pw_passwd); memcpy(p, new_pwd.pw_passwd, size); new_pwd.pw_passwd = p; p += size + 1; } if (new_pwd.pw_class != NULL) { size = strlen(new_pwd.pw_class); memcpy(p, new_pwd.pw_class, size); new_pwd.pw_class = p; p += size + 1; } if (new_pwd.pw_gecos != NULL) { size = strlen(new_pwd.pw_gecos); memcpy(p, new_pwd.pw_gecos, size); new_pwd.pw_gecos = p; p += size + 1; } if (new_pwd.pw_dir != NULL) { size = strlen(new_pwd.pw_dir); memcpy(p, new_pwd.pw_dir, size); new_pwd.pw_dir = p; p += size + 1; } if (new_pwd.pw_shell != NULL) { size = strlen(new_pwd.pw_shell); memcpy(p, new_pwd.pw_shell, size); new_pwd.pw_shell = p; p += size + 1; } memcpy(buffer, &new_pwd, sizeof(struct passwd)); return (NS_SUCCESS); } static int pwd_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; uid_t uid; struct passwd *pwd; char *orig_buf; size_t orig_buf_size; int *ret_errno; char *p; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } pwd = va_arg(ap, struct passwd *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); ret_errno = va_arg(ap, int *); if (orig_buf_size < buffer_size - sizeof(struct passwd) - sizeof(char *)) { *ret_errno = ERANGE; return (NS_RETURN); } memcpy(pwd, buffer, sizeof(struct passwd)); memcpy(&p, buffer + sizeof(struct passwd), sizeof(char *)); memcpy(orig_buf, buffer + sizeof(struct passwd) + sizeof(char *), buffer_size - sizeof(struct passwd) - sizeof(char *)); NS_APPLY_OFFSET(pwd->pw_name, orig_buf, p, char *); NS_APPLY_OFFSET(pwd->pw_passwd, orig_buf, p, char *); NS_APPLY_OFFSET(pwd->pw_class, orig_buf, p, char *); NS_APPLY_OFFSET(pwd->pw_gecos, orig_buf, p, char *); NS_APPLY_OFFSET(pwd->pw_dir, orig_buf, p, char *); NS_APPLY_OFFSET(pwd->pw_shell, orig_buf, p, char *); if (retval != NULL) *((struct passwd **)retval) = pwd; return (NS_SUCCESS); } NSS_MP_CACHE_HANDLING(passwd); #endif /* NS_CACHING */ void setpwent(void) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setpwent, (void *)SETPWENT }, #ifdef HESIOD { NSSRC_DNS, dns_setpwent, (void *)SETPWENT }, #endif #ifdef YP { NSSRC_NIS, nis_setpwent, (void *)SETPWENT }, #endif { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 0); } int setpassent(int stayopen) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setpwent, (void *)SETPWENT }, #ifdef HESIOD { NSSRC_DNS, dns_setpwent, (void *)SETPWENT }, #endif #ifdef YP { NSSRC_NIS, nis_setpwent, (void *)SETPWENT }, #endif { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, stayopen); return (1); } void endpwent(void) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setpwent, (void *)ENDPWENT }, #ifdef HESIOD { NSSRC_DNS, dns_setpwent, (void *)ENDPWENT }, #endif #ifdef YP { NSSRC_NIS, nis_setpwent, (void *)ENDPWENT }, #endif { NSSRC_COMPAT, compat_setpwent, (void *)ENDPWENT }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent", defaultsrc); } int getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_all, pwd_marshal_func, pwd_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_passwd, (void *)nss_lt_all }, #ifdef HESIOD { NSSRC_DNS, dns_passwd, (void *)nss_lt_all }, #endif #ifdef YP { NSSRC_NIS, nis_passwd, (void *)nss_lt_all }, #endif { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_all }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; __pw_initpwd(pwd); ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwent_r", defaultsrc, pwd, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_name, pwd_id_func, pwd_marshal_func, pwd_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_passwd, (void *)nss_lt_name }, #ifdef HESIOD { NSSRC_DNS, dns_passwd, (void *)nss_lt_name }, #endif #ifdef YP { NSSRC_NIS, nis_passwd, (void *)nss_lt_name }, #endif { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_name }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; __pw_initpwd(pwd); ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwnam_r", defaultsrc, name, pwd, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_id, pwd_id_func, pwd_marshal_func, pwd_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_passwd, (void *)nss_lt_id }, #ifdef HESIOD { NSSRC_DNS, dns_passwd, (void *)nss_lt_id }, #endif #ifdef YP { NSSRC_NIS, nis_passwd, (void *)nss_lt_id }, #endif { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_id }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; __pw_initpwd(pwd); ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwuid_r", defaultsrc, uid, pwd, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } static struct passwd pwd; static char *pwd_storage; static size_t pwd_storage_size; static struct passwd * getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **), union key key) { int rv; struct passwd *res; if (pwd_storage == NULL) { pwd_storage = malloc(PWD_STORAGE_INITIAL); if (pwd_storage == NULL) return (NULL); pwd_storage_size = PWD_STORAGE_INITIAL; } do { rv = fn(key, &pwd, pwd_storage, pwd_storage_size, &res); if (res == NULL && rv == ERANGE) { free(pwd_storage); if ((pwd_storage_size << 1) > PWD_STORAGE_MAX) { pwd_storage = NULL; errno = ERANGE; return (NULL); } pwd_storage_size <<= 1; pwd_storage = malloc(pwd_storage_size); if (pwd_storage == NULL) return (NULL); } } while (res == NULL && rv == ERANGE); if (rv != 0) errno = rv; return (res); } static int wrap_getpwnam_r(union key key, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **res) { return (getpwnam_r(key.name, pwd, buffer, bufsize, res)); } static int wrap_getpwuid_r(union key key, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **res) { return (getpwuid_r(key.uid, pwd, buffer, bufsize, res)); } static int wrap_getpwent_r(union key key __unused, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **res) { return (getpwent_r(pwd, buffer, bufsize, res)); } struct passwd * getpwnam(const char *name) { union key key; key.name = name; return (getpw(wrap_getpwnam_r, key)); } struct passwd * getpwuid(uid_t uid) { union key key; key.uid = uid; return (getpw(wrap_getpwuid_r, key)); } struct passwd * getpwent(void) { union key key; key.uid = 0; /* not used */ return (getpw(wrap_getpwent_r, key)); } /* * files backend */ static DB * pwdbopen(int *version) { DB *res; DBT key, entry; int rv; if (geteuid() != 0 || (res = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL)) == NULL) res = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL); if (res == NULL) return (NULL); key.data = _PWD_VERSION_KEY; key.size = strlen(_PWD_VERSION_KEY); rv = res->get(res, &key, &entry, 0); if (rv == 0) *version = *(unsigned char *)entry.data; else *version = 3; if (*version < 3 || *version >= nitems(pwdb_versions)) { syslog(LOG_CRIT, "Unsupported password database version %d", *version); res->close(res); res = NULL; } return (res); } static void files_endstate(void *p) { DB *db; if (p == NULL) return; db = ((struct files_state *)p)->db; if (db != NULL) db->close(db); free(p); } static int files_setpwent(void *retval, void *mdata, va_list ap) { struct files_state *st; int rv, stayopen; rv = files_getstate(&st); if (rv != 0) return (NS_UNAVAIL); - switch ((enum constants)mdata) { + switch ((enum constants)(uintptr_t)mdata) { case SETPWENT: stayopen = va_arg(ap, int); st->keynum = 0; if (stayopen) st->db = pwdbopen(&st->version); st->stayopen = stayopen; break; case ENDPWENT: if (st->db != NULL) { (void)st->db->close(st->db); st->db = NULL; } break; default: break; } return (NS_UNAVAIL); } static int files_passwd(void *retval, void *mdata, va_list ap) { char keybuf[MAXLOGNAME + 1]; DBT key, entry; struct files_state *st; enum nss_lookup_type how; const char *name; struct passwd *pwd; char *buffer; size_t bufsize, namesize; uid_t uid; uint32_t store; int rv, stayopen = 0, *errnop; name = NULL; uid = (uid_t)-1; - how = (enum nss_lookup_type)mdata; + how = (enum nss_lookup_type)(uintptr_t)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); keybuf[0] = _PW_KEYBYNAME; break; case nss_lt_id: uid = va_arg(ap, uid_t); keybuf[0] = _PW_KEYBYUID; break; case nss_lt_all: keybuf[0] = _PW_KEYBYNUM; break; default: rv = NS_NOTFOUND; goto fin; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = files_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (how == nss_lt_all && st->keynum < 0) { rv = NS_NOTFOUND; goto fin; } if (st->db == NULL && (st->db = pwdbopen(&st->version)) == NULL) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } if (how == nss_lt_all) stayopen = 1; else stayopen = st->stayopen; key.data = keybuf; do { switch (how) { case nss_lt_name: /* MAXLOGNAME includes NUL byte, but we do not * include the NUL byte in the key. */ namesize = strlcpy(&keybuf[1], name, sizeof(keybuf)-1); if (namesize >= sizeof(keybuf)-1) { *errnop = EINVAL; rv = NS_NOTFOUND; goto fin; } key.size = namesize + 1; break; case nss_lt_id: if (st->version < _PWD_CURRENT_VERSION) { memcpy(&keybuf[1], &uid, sizeof(uid)); key.size = sizeof(uid) + 1; } else { store = htonl(uid); memcpy(&keybuf[1], &store, sizeof(store)); key.size = sizeof(store) + 1; } break; case nss_lt_all: st->keynum++; if (st->version < _PWD_CURRENT_VERSION) { memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum)); key.size = sizeof(st->keynum) + 1; } else { store = htonl(st->keynum); memcpy(&keybuf[1], &store, sizeof(store)); key.size = sizeof(store) + 1; } break; } keybuf[0] = _PW_VERSIONED(keybuf[0], st->version); rv = st->db->get(st->db, &key, &entry, 0); if (rv < 0 || rv > 1) { /* should never return > 1 */ *errnop = errno; rv = NS_UNAVAIL; goto fin; } else if (rv == 1) { if (how == nss_lt_all) st->keynum = -1; rv = NS_NOTFOUND; goto fin; } rv = pwdb_versions[st->version].match(entry.data, entry.size, how, name, uid); if (rv != NS_SUCCESS) continue; if (entry.size > bufsize) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, entry.data, entry.size); rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (st->db != NULL && !stayopen) { (void)st->db->close(st->db); st->db = NULL; } if (rv == NS_SUCCESS) { pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_FILES; if (retval != NULL) *(struct passwd **)retval = pwd; } return (rv); } static int pwdb_match_entry_v3(char *entry, size_t entrysize, enum nss_lookup_type how, const char *name, uid_t uid) { const char *p, *eom; uid_t uid2; eom = &entry[entrysize]; for (p = entry; p < eom; p++) if (*p == '\0') break; if (*p != '\0') return (NS_NOTFOUND); if (how == nss_lt_all) return (NS_SUCCESS); if (how == nss_lt_name) return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND); for (p++; p < eom; p++) if (*p == '\0') break; if (*p != '\0' || (++p) + sizeof(uid) >= eom) return (NS_NOTFOUND); memcpy(&uid2, p, sizeof(uid2)); return (uid == uid2 ? NS_SUCCESS : NS_NOTFOUND); } static int pwdb_parse_entry_v3(char *buffer, size_t bufsize, struct passwd *pwd, int *errnop) { char *p, *eom; int32_t pw_change, pw_expire; /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */ p = buffer; eom = &buffer[bufsize]; #define STRING(field) do { \ (field) = p; \ while (p < eom && *p != '\0') \ p++; \ if (p >= eom) \ return (NS_NOTFOUND); \ p++; \ } while (0) #define SCALAR(field) do { \ if (p + sizeof(field) > eom) \ return (NS_NOTFOUND); \ memcpy(&(field), p, sizeof(field)); \ p += sizeof(field); \ } while (0) STRING(pwd->pw_name); STRING(pwd->pw_passwd); SCALAR(pwd->pw_uid); SCALAR(pwd->pw_gid); SCALAR(pw_change); STRING(pwd->pw_class); STRING(pwd->pw_gecos); STRING(pwd->pw_dir); STRING(pwd->pw_shell); SCALAR(pw_expire); SCALAR(pwd->pw_fields); #undef STRING #undef SCALAR pwd->pw_change = pw_change; pwd->pw_expire = pw_expire; return (NS_SUCCESS); } static int pwdb_match_entry_v4(char *entry, size_t entrysize, enum nss_lookup_type how, const char *name, uid_t uid) { const char *p, *eom; uint32_t uid2; eom = &entry[entrysize]; for (p = entry; p < eom; p++) if (*p == '\0') break; if (*p != '\0') return (NS_NOTFOUND); if (how == nss_lt_all) return (NS_SUCCESS); if (how == nss_lt_name) return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND); for (p++; p < eom; p++) if (*p == '\0') break; if (*p != '\0' || (++p) + sizeof(uid) >= eom) return (NS_NOTFOUND); memcpy(&uid2, p, sizeof(uid2)); uid2 = ntohl(uid2); return (uid == (uid_t)uid2 ? NS_SUCCESS : NS_NOTFOUND); } static int pwdb_parse_entry_v4(char *buffer, size_t bufsize, struct passwd *pwd, int *errnop) { char *p, *eom; uint32_t n; /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */ p = buffer; eom = &buffer[bufsize]; #define STRING(field) do { \ (field) = p; \ while (p < eom && *p != '\0') \ p++; \ if (p >= eom) \ return (NS_NOTFOUND); \ p++; \ } while (0) #define SCALAR(field) do { \ if (p + sizeof(n) > eom) \ return (NS_NOTFOUND); \ memcpy(&n, p, sizeof(n)); \ (field) = ntohl(n); \ p += sizeof(n); \ } while (0) STRING(pwd->pw_name); STRING(pwd->pw_passwd); SCALAR(pwd->pw_uid); SCALAR(pwd->pw_gid); SCALAR(pwd->pw_change); STRING(pwd->pw_class); STRING(pwd->pw_gecos); STRING(pwd->pw_dir); STRING(pwd->pw_shell); SCALAR(pwd->pw_expire); SCALAR(pwd->pw_fields); #undef STRING #undef SCALAR return (NS_SUCCESS); } #ifdef HESIOD /* * dns backend */ static void dns_endstate(void *p) { free(p); } static int dns_setpwent(void *retval, void *mdata, va_list ap) { struct dns_state *st; int rv; rv = dns_getstate(&st); if (rv != 0) return (NS_UNAVAIL); st->counter = 0; return (NS_UNAVAIL); } static int dns_passwd(void *retval, void *mdata, va_list ap) { char buf[HESIOD_NAME_MAX]; struct dns_state *st; struct passwd *pwd; const char *name, *label; void *ctx; char *buffer, **hes; size_t bufsize, linesize; uid_t uid; enum nss_lookup_type how; int rv, *errnop; ctx = NULL; hes = NULL; name = NULL; uid = (uid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = dns_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (hesiod_init(&ctx) != 0) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } do { rv = NS_NOTFOUND; switch (how) { case nss_lt_name: label = name; break; case nss_lt_id: if (snprintf(buf, sizeof(buf), "%lu", (unsigned long)uid) >= sizeof(buf)) goto fin; label = buf; break; case nss_lt_all: if (st->counter < 0) goto fin; if (snprintf(buf, sizeof(buf), "passwd-%ld", st->counter++) >= sizeof(buf)) goto fin; label = buf; break; } hes = hesiod_resolve(ctx, label, how == nss_lt_id ? "uid" : "passwd"); if (hes == NULL) { if (how == nss_lt_all) st->counter = -1; if (errno != ENOENT) *errnop = errno; goto fin; } rv = __pw_match_entry(hes[0], strlen(hes[0]), how, name, uid); if (rv != NS_SUCCESS) { hesiod_free_list(ctx, hes); hes = NULL; continue; } linesize = strlcpy(buffer, hes[0], bufsize); if (linesize >= bufsize) { *errnop = ERANGE; rv = NS_RETURN; continue; } hesiod_free_list(ctx, hes); hes = NULL; rv = __pw_parse_entry(buffer, bufsize, pwd, 0, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (hes != NULL) hesiod_free_list(ctx, hes); if (ctx != NULL) hesiod_end(ctx); if (rv == NS_SUCCESS) { pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_HESIOD; if (retval != NULL) *(struct passwd **)retval = pwd; } return (rv); } #endif /* HESIOD */ #ifdef YP /* * nis backend */ static void nis_endstate(void *p) { free(((struct nis_state *)p)->key); free(p); } /* * Test for the presence of special FreeBSD-specific master.passwd.by* * maps. We do this using yp_order(). If it fails, then either the server * doesn't have the map, or the YPPROC_ORDER procedure isn't supported by * the server (Sun NIS+ servers in YP compat mode behave this way). If * the master.passwd.by* maps don't exist, then let the lookup routine try * the regular passwd.by* maps instead. If the lookup routine fails, it * can return an error as needed. */ static int nis_map(char *domain, enum nss_lookup_type how, char *buffer, size_t bufsize, int *master) { int rv, order; *master = 0; if (geteuid() == 0) { if (snprintf(buffer, bufsize, "master.passwd.by%s", (how == nss_lt_id) ? "uid" : "name") >= bufsize) return (NS_UNAVAIL); rv = yp_order(domain, buffer, &order); if (rv == 0) { *master = 1; return (NS_SUCCESS); } } if (snprintf(buffer, bufsize, "passwd.by%s", (how == nss_lt_id) ? "uid" : "name") >= bufsize) return (NS_UNAVAIL); return (NS_SUCCESS); } static int nis_adjunct(char *domain, const char *name, char *buffer, size_t bufsize) { int rv; char *result, *p, *q, *eor; int resultlen; result = NULL; rv = yp_match(domain, "passwd.adjunct.byname", name, strlen(name), &result, &resultlen); if (rv != 0) rv = 1; else { eor = &result[resultlen]; p = memchr(result, ':', eor - result); if (p != NULL && ++p < eor && (q = memchr(p, ':', eor - p)) != NULL) { if (q - p >= bufsize) rv = -1; else { memcpy(buffer, p, q - p); buffer[q - p] ='\0'; } } else rv = 1; } free(result); return (rv); } static int nis_setpwent(void *retval, void *mdata, va_list ap) { struct nis_state *st; int rv; rv = nis_getstate(&st); if (rv != 0) return (NS_UNAVAIL); st->done = 0; free(st->key); st->key = NULL; return (NS_UNAVAIL); } static int nis_passwd(void *retval, void *mdata, va_list ap) { char map[YPMAXMAP]; struct nis_state *st; struct passwd *pwd; const char *name; char *buffer, *key, *result; size_t bufsize; uid_t uid; enum nss_lookup_type how; int *errnop, keylen, resultlen, rv, master; name = NULL; uid = (uid_t)-1; - how = (enum nss_lookup_type)mdata; + how = (enum nss_lookup_type)(uintptr_t)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = nis_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->domain[0] == '\0') { if (getdomainname(st->domain, sizeof(st->domain)) != 0) { *errnop = errno; return (NS_UNAVAIL); } } rv = nis_map(st->domain, how, map, sizeof(map), &master); if (rv != NS_SUCCESS) return (rv); result = NULL; do { rv = NS_NOTFOUND; switch (how) { case nss_lt_name: if (strlcpy(buffer, name, bufsize) >= bufsize) goto erange; break; case nss_lt_id: if (snprintf(buffer, bufsize, "%lu", (unsigned long)uid) >= bufsize) goto erange; break; case nss_lt_all: if (st->done) goto fin; break; } result = NULL; if (how == nss_lt_all) { if (st->key == NULL) rv = yp_first(st->domain, map, &st->key, &st->keylen, &result, &resultlen); else { key = st->key; keylen = st->keylen; st->key = NULL; rv = yp_next(st->domain, map, key, keylen, &st->key, &st->keylen, &result, &resultlen); free(key); } if (rv != 0) { free(result); free(st->key); st->key = NULL; if (rv == YPERR_NOMORE) st->done = 1; else rv = NS_UNAVAIL; goto fin; } } else { rv = yp_match(st->domain, map, buffer, strlen(buffer), &result, &resultlen); if (rv == YPERR_KEY) { rv = NS_NOTFOUND; continue; } else if (rv != 0) { free(result); rv = NS_UNAVAIL; continue; } } if (resultlen >= bufsize) { free(result); goto erange; } memcpy(buffer, result, resultlen); buffer[resultlen] = '\0'; free(result); rv = __pw_match_entry(buffer, resultlen, how, name, uid); if (rv == NS_SUCCESS) rv = __pw_parse_entry(buffer, resultlen, pwd, master, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (rv == NS_SUCCESS) { if (strstr(pwd->pw_passwd, "##") != NULL) { rv = nis_adjunct(st->domain, pwd->pw_name, &buffer[resultlen+1], bufsize-resultlen-1); if (rv < 0) goto erange; else if (rv == 0) pwd->pw_passwd = &buffer[resultlen+1]; } pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_NIS; if (retval != NULL) *(struct passwd **)retval = pwd; rv = NS_SUCCESS; } return (rv); erange: *errnop = ERANGE; return (NS_RETURN); } #endif /* YP */ /* * compat backend */ static void compat_clear_template(struct passwd *template) { free(template->pw_passwd); free(template->pw_gecos); free(template->pw_dir); free(template->pw_shell); memset(template, 0, sizeof(*template)); } static int compat_set_template(struct passwd *src, struct passwd *template) { compat_clear_template(template); #ifdef PW_OVERRIDE_PASSWD if ((src->pw_fields & _PWF_PASSWD) && (template->pw_passwd = strdup(src->pw_passwd)) == NULL) goto enomem; #endif if (src->pw_fields & _PWF_UID) template->pw_uid = src->pw_uid; if (src->pw_fields & _PWF_GID) template->pw_gid = src->pw_gid; if ((src->pw_fields & _PWF_GECOS) && (template->pw_gecos = strdup(src->pw_gecos)) == NULL) goto enomem; if ((src->pw_fields & _PWF_DIR) && (template->pw_dir = strdup(src->pw_dir)) == NULL) goto enomem; if ((src->pw_fields & _PWF_SHELL) && (template->pw_shell = strdup(src->pw_shell)) == NULL) goto enomem; template->pw_fields = src->pw_fields; return (0); enomem: syslog(LOG_ERR, "getpwent memory allocation failure"); return (-1); } static int compat_use_template(struct passwd *pwd, struct passwd *template, char *buffer, size_t bufsize) { struct passwd hold; char *copy, *p, *q, *eob; size_t n; /* We cannot know the layout of the password fields in `buffer', * so we have to copy everything. */ if (template->pw_fields == 0) /* nothing to fill-in */ return (0); n = 0; n += pwd->pw_name != NULL ? strlen(pwd->pw_name) + 1 : 0; n += pwd->pw_passwd != NULL ? strlen(pwd->pw_passwd) + 1 : 0; n += pwd->pw_class != NULL ? strlen(pwd->pw_class) + 1 : 0; n += pwd->pw_gecos != NULL ? strlen(pwd->pw_gecos) + 1 : 0; n += pwd->pw_dir != NULL ? strlen(pwd->pw_dir) + 1 : 0; n += pwd->pw_shell != NULL ? strlen(pwd->pw_shell) + 1 : 0; copy = malloc(n); if (copy == NULL) { syslog(LOG_ERR, "getpwent memory allocation failure"); return (ENOMEM); } p = copy; eob = ©[n]; #define COPY(field) do { \ if (pwd->field == NULL) \ hold.field = NULL; \ else { \ hold.field = p; \ p += strlcpy(p, pwd->field, eob-p) + 1; \ } \ } while (0) COPY(pw_name); COPY(pw_passwd); COPY(pw_class); COPY(pw_gecos); COPY(pw_dir); COPY(pw_shell); #undef COPY p = buffer; eob = &buffer[bufsize]; #define COPY(field, flag) do { \ q = (template->pw_fields & flag) ? template->field : hold.field; \ if (q == NULL) \ pwd->field = NULL; \ else { \ pwd->field = p; \ if ((n = strlcpy(p, q, eob-p)) >= eob-p) { \ free(copy); \ return (ERANGE); \ } \ p += n + 1; \ } \ } while (0) COPY(pw_name, 0); #ifdef PW_OVERRIDE_PASSWD COPY(pw_passwd, _PWF_PASSWD); #else COPY(pw_passwd, 0); #endif COPY(pw_class, 0); COPY(pw_gecos, _PWF_GECOS); COPY(pw_dir, _PWF_DIR); COPY(pw_shell, _PWF_SHELL); #undef COPY #define COPY(field, flag) do { \ if (template->pw_fields & flag) \ pwd->field = template->field; \ } while (0) COPY(pw_uid, _PWF_UID); COPY(pw_gid, _PWF_GID); #undef COPY free(copy); return (0); } static int compat_exclude(const char *name, DB **db) { DBT key, data; if (*db == NULL && (*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL) return (errno); key.size = strlen(name); key.data = (char *)name; data.size = 0; data.data = NULL; if ((*db)->put(*db, &key, &data, 0) == -1) return (errno); return (0); } static int compat_is_excluded(const char *name, DB *db) { DBT key, data; if (db == NULL) return (0); key.size = strlen(name); key.data = (char *)name; return (db->get(db, &key, &data, 0) == 0); } static int compat_redispatch(struct compat_state *st, enum nss_lookup_type how, enum nss_lookup_type lookup_how, const char *name, const char *lookup_name, uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop) { static const ns_src compatsrc[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab dtab[] = { #ifdef YP { NSSRC_NIS, nis_passwd, NULL }, #endif #ifdef HESIOD { NSSRC_DNS, dns_passwd, NULL }, #endif { NULL, NULL, NULL } }; void *discard; int e, i, rv; for (i = 0; i < (int)(nitems(dtab) - 1); i++) dtab[i].mdata = (void *)lookup_how; more: __pw_initpwd(pwd); switch (lookup_how) { case nss_lt_all: rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, "getpwent_r", compatsrc, pwd, buffer, bufsize, errnop); break; case nss_lt_id: rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, "getpwuid_r", compatsrc, uid, pwd, buffer, bufsize, errnop); break; case nss_lt_name: rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, "getpwnam_r", compatsrc, lookup_name, pwd, buffer, bufsize, errnop); break; default: return (NS_UNAVAIL); } if (rv != NS_SUCCESS) return (rv); if (compat_is_excluded(pwd->pw_name, st->exclude)) { if (how == nss_lt_all) goto more; return (NS_NOTFOUND); } e = compat_use_template(pwd, &st->template, buffer, bufsize); if (e != 0) { *errnop = e; if (e == ERANGE) return (NS_RETURN); else return (NS_UNAVAIL); } switch (how) { case nss_lt_name: if (strcmp(name, pwd->pw_name) != 0) return (NS_NOTFOUND); break; case nss_lt_id: if (uid != pwd->pw_uid) return (NS_NOTFOUND); break; default: break; } return (NS_SUCCESS); } static void compat_endstate(void *p) { struct compat_state *st; if (p == NULL) return; st = (struct compat_state *)p; if (st->db != NULL) st->db->close(st->db); if (st->exclude != NULL) st->exclude->close(st->exclude); compat_clear_template(&st->template); free(p); } static int compat_setpwent(void *retval, void *mdata, va_list ap) { static const ns_src compatsrc[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab dtab[] = { #ifdef YP { NSSRC_NIS, nis_setpwent, NULL }, #endif #ifdef HESIOD { NSSRC_DNS, dns_setpwent, NULL }, #endif { NULL, NULL, NULL } }; struct compat_state *st; int rv, stayopen; #define set_setent(x, y) do { \ int i; \ for (i = 0; i < (int)(nitems(x) - 1); i++) \ x[i].mdata = (void *)y; \ } while (0) rv = compat_getstate(&st); if (rv != 0) return (NS_UNAVAIL); - switch ((enum constants)mdata) { + switch ((enum constants)(uintptr_t)mdata) { case SETPWENT: stayopen = va_arg(ap, int); st->keynum = 0; if (stayopen) st->db = pwdbopen(&st->version); st->stayopen = stayopen; set_setent(dtab, mdata); (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpwent", compatsrc, 0); break; case ENDPWENT: if (st->db != NULL) { (void)st->db->close(st->db); st->db = NULL; } set_setent(dtab, mdata); (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent", compatsrc, 0); break; default: break; } return (NS_UNAVAIL); #undef set_setent } static int compat_passwd(void *retval, void *mdata, va_list ap) { char keybuf[MAXLOGNAME + 1]; DBT key, entry; struct compat_state *st; enum nss_lookup_type how; const char *name; struct passwd *pwd; char *buffer, *pw_name; char *host, *user, *domain; size_t bufsize; uid_t uid; uint32_t store; int rv, from_compat, stayopen, *errnop; from_compat = 0; name = NULL; uid = (uid_t)-1; - how = (enum nss_lookup_type)mdata; + how = (enum nss_lookup_type)(uintptr_t)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; default: rv = NS_NOTFOUND; goto fin; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = compat_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (how == nss_lt_all && st->keynum < 0) { rv = NS_NOTFOUND; goto fin; } if (st->db == NULL && (st->db = pwdbopen(&st->version)) == NULL) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } if (how == nss_lt_all) { if (st->keynum < 0) { rv = NS_NOTFOUND; goto fin; } stayopen = 1; } else { st->keynum = 0; stayopen = st->stayopen; } docompat: rv = NS_NOTFOUND; switch (st->compat) { case COMPAT_MODE_ALL: rv = compat_redispatch(st, how, how, name, name, uid, pwd, buffer, bufsize, errnop); if (rv != NS_SUCCESS) st->compat = COMPAT_MODE_OFF; break; case COMPAT_MODE_NETGROUP: /* XXX getnetgrent is not thread-safe. */ do { rv = getnetgrent(&host, &user, &domain); if (rv == 0) { endnetgrent(); st->compat = COMPAT_MODE_OFF; rv = NS_NOTFOUND; continue; } else if (user == NULL || user[0] == '\0') continue; rv = compat_redispatch(st, how, nss_lt_name, name, user, uid, pwd, buffer, bufsize, errnop); } while (st->compat == COMPAT_MODE_NETGROUP && !(rv & NS_TERMINATE)); break; case COMPAT_MODE_NAME: rv = compat_redispatch(st, how, nss_lt_name, name, st->name, uid, pwd, buffer, bufsize, errnop); free(st->name); st->name = NULL; st->compat = COMPAT_MODE_OFF; break; default: break; } if (rv & NS_TERMINATE) { from_compat = 1; goto fin; } key.data = keybuf; rv = NS_NOTFOUND; while (st->keynum >= 0) { st->keynum++; if (st->version < _PWD_CURRENT_VERSION) { memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum)); key.size = sizeof(st->keynum) + 1; } else { store = htonl(st->keynum); memcpy(&keybuf[1], &store, sizeof(store)); key.size = sizeof(store) + 1; } keybuf[0] = _PW_VERSIONED(_PW_KEYBYNUM, st->version); rv = st->db->get(st->db, &key, &entry, 0); if (rv < 0 || rv > 1) { /* should never return > 1 */ *errnop = errno; rv = NS_UNAVAIL; goto fin; } else if (rv == 1) { st->keynum = -1; rv = NS_NOTFOUND; goto fin; } pw_name = (char *)entry.data; switch (pw_name[0]) { case '+': switch (pw_name[1]) { case '\0': st->compat = COMPAT_MODE_ALL; break; case '@': setnetgrent(&pw_name[2]); st->compat = COMPAT_MODE_NETGROUP; break; default: st->name = strdup(&pw_name[1]); if (st->name == NULL) { syslog(LOG_ERR, "getpwent memory allocation failure"); *errnop = ENOMEM; rv = NS_UNAVAIL; break; } st->compat = COMPAT_MODE_NAME; } if (entry.size > bufsize) { *errnop = ERANGE; rv = NS_RETURN; goto fin; } memcpy(buffer, entry.data, entry.size); rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, errnop); if (rv != NS_SUCCESS) ; else if (compat_set_template(pwd, &st->template) < 0) { *errnop = ENOMEM; rv = NS_UNAVAIL; goto fin; } goto docompat; case '-': switch (pw_name[1]) { case '\0': /* XXX Maybe syslog warning */ continue; case '@': setnetgrent(&pw_name[2]); while (getnetgrent(&host, &user, &domain) != 0) { if (user != NULL && user[0] != '\0') compat_exclude(user, &st->exclude); } endnetgrent(); continue; default: compat_exclude(&pw_name[1], &st->exclude); continue; } break; default: break; } if (compat_is_excluded((char *)entry.data, st->exclude)) continue; rv = pwdb_versions[st->version].match(entry.data, entry.size, how, name, uid); if (rv == NS_RETURN) break; else if (rv != NS_SUCCESS) continue; if (entry.size > bufsize) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, entry.data, entry.size); rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, errnop); if (rv & NS_TERMINATE) break; } fin: if (st->db != NULL && !stayopen) { (void)st->db->close(st->db); st->db = NULL; } if (rv == NS_SUCCESS) { if (!from_compat) { pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_FILES; } if (retval != NULL) *(struct passwd **)retval = pwd; } return (rv); } /* * common passwd line matching and parsing */ int __pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how, const char *name, uid_t uid) { const char *p, *eom; char *q; size_t len; unsigned long m; eom = entry + entrysize; for (p = entry; p < eom; p++) if (*p == ':') break; if (*p != ':') return (NS_NOTFOUND); if (how == nss_lt_all) return (NS_SUCCESS); if (how == nss_lt_name) { len = strlen(name); if (len == (p - entry) && memcmp(name, entry, len) == 0) return (NS_SUCCESS); else return (NS_NOTFOUND); } for (p++; p < eom; p++) if (*p == ':') break; if (*p != ':') return (NS_NOTFOUND); m = strtoul(++p, &q, 10); if (q[0] != ':' || (uid_t)m != uid) return (NS_NOTFOUND); else return (NS_SUCCESS); } /* XXX buffer must be NUL-terminated. errnop is not set correctly. */ int __pw_parse_entry(char *buffer, size_t bufsize __unused, struct passwd *pwd, int master, int *errnop __unused) { if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0) return (NS_NOTFOUND); else return (NS_SUCCESS); } Index: stable/12/lib/libc/net/gethostnamadr.c =================================================================== --- stable/12/lib/libc/net/gethostnamadr.c (revision 360413) +++ stable/12/lib/libc/net/gethostnamadr.c (revision 360414) @@ -1,729 +1,729 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1994, Garrett Wollman * * 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 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX hack for _res */ #include /* XXX hack for _res */ #include "un-namespace.h" #include "netdb_private.h" #ifdef NS_CACHING #include "nscache.h" #endif static int gethostbyname_internal(const char *, int, struct hostent *, char *, size_t, struct hostent **, int *, res_state); /* Host lookup order if nsswitch.conf is broken or nonexistent */ static const ns_src default_src[] = { { NSSRC_FILES, NS_SUCCESS }, { NSSRC_DNS, NS_SUCCESS }, { 0 } }; #ifdef NS_CACHING static int host_id_func(char *, size_t *, va_list, void *); static int host_marshal_func(char *, size_t *, void *, va_list, void *); static int host_unmarshal_func(char *, size_t, void *, va_list, void *); #endif NETDB_THREAD_ALLOC(hostent) NETDB_THREAD_ALLOC(hostent_data) NETDB_THREAD_ALLOC(hostdata) static void hostent_free(void *ptr) { free(ptr); } static void hostent_data_free(void *ptr) { struct hostent_data *hed = ptr; if (hed == NULL) return; hed->stayopen = 0; _endhosthtent(hed); free(hed); } static void hostdata_free(void *ptr) { free(ptr); } int __copy_hostent(struct hostent *he, struct hostent *hptr, char *buf, size_t buflen) { char *cp; char **ptr; int i, n; int nptr, len; /* Find out the amount of space required to store the answer. */ nptr = 2; /* NULL ptrs */ len = (char *)ALIGN(buf) - buf; for (i = 0; he->h_addr_list[i]; i++, nptr++) { len += he->h_length; } for (i = 0; he->h_aliases[i]; i++, nptr++) { len += strlen(he->h_aliases[i]) + 1; } len += strlen(he->h_name) + 1; len += nptr * sizeof(char*); if (len > buflen) { errno = ERANGE; return (-1); } /* copy address size and type */ hptr->h_addrtype = he->h_addrtype; n = hptr->h_length = he->h_length; ptr = (char **)ALIGN(buf); cp = (char *)ALIGN(buf) + nptr * sizeof(char *); /* copy address list */ hptr->h_addr_list = ptr; for (i = 0; he->h_addr_list[i]; i++ , ptr++) { memcpy(cp, he->h_addr_list[i], n); hptr->h_addr_list[i] = cp; cp += n; } hptr->h_addr_list[i] = NULL; ptr++; /* copy official name */ n = strlen(he->h_name) + 1; strcpy(cp, he->h_name); hptr->h_name = cp; cp += n; /* copy aliases */ hptr->h_aliases = ptr; for (i = 0 ; he->h_aliases[i]; i++) { n = strlen(he->h_aliases[i]) + 1; strcpy(cp, he->h_aliases[i]); hptr->h_aliases[i] = cp; cp += n; } hptr->h_aliases[i] = NULL; return (0); } #ifdef NS_CACHING static int host_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { res_state statp; u_long res_options; const int op_id = 1; char *str; void *addr; socklen_t len; int type; size_t desired_size, size; enum nss_lookup_type lookup_type; char *p; int res = NS_UNAVAIL; statp = __res_state(); res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); - lookup_type = (enum nss_lookup_type)cache_mdata; + lookup_type = (enum nss_lookup_type)(uintptr_t)cache_mdata; switch (lookup_type) { case nss_lt_name: str = va_arg(ap, char *); type = va_arg(ap, int); size = strlen(str); desired_size = sizeof(res_options) + sizeof(int) + sizeof(enum nss_lookup_type) + sizeof(int) + size + 1; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } p = buffer; memcpy(p, &res_options, sizeof(res_options)); p += sizeof(res_options); memcpy(p, &op_id, sizeof(int)); p += sizeof(int); memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); p += sizeof(int); memcpy(p, &type, sizeof(int)); p += sizeof(int); memcpy(p, str, size + 1); res = NS_SUCCESS; break; case nss_lt_id: addr = va_arg(ap, void *); len = va_arg(ap, socklen_t); type = va_arg(ap, int); desired_size = sizeof(res_options) + sizeof(int) + sizeof(enum nss_lookup_type) + sizeof(int) + sizeof(socklen_t) + len; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } p = buffer; memcpy(p, &res_options, sizeof(res_options)); p += sizeof(res_options); memcpy(p, &op_id, sizeof(int)); p += sizeof(int); memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); p += sizeof(int); memcpy(p, &type, sizeof(int)); p += sizeof(int); memcpy(p, &len, sizeof(socklen_t)); p += sizeof(socklen_t); memcpy(p, addr, len); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } static int host_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *str; void *addr; socklen_t len; int type; struct hostent *ht; struct hostent new_ht; size_t desired_size, aliases_size, addr_size, size; char *p, **iter; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: str = va_arg(ap, char *); type = va_arg(ap, int); break; case nss_lt_id: addr = va_arg(ap, void *); len = va_arg(ap, socklen_t); type = va_arg(ap, int); break; default: /* should be unreachable */ return (NS_UNAVAIL); } ht = va_arg(ap, struct hostent *); desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *); if (ht->h_name != NULL) desired_size += strlen(ht->h_name) + 1; if (ht->h_aliases != NULL) { aliases_size = 0; for (iter = ht->h_aliases; *iter; ++iter) { desired_size += strlen(*iter) + 1; ++aliases_size; } desired_size += _ALIGNBYTES + (aliases_size + 1) * sizeof(char *); } if (ht->h_addr_list != NULL) { addr_size = 0; for (iter = ht->h_addr_list; *iter; ++iter) ++addr_size; desired_size += addr_size * _ALIGN(ht->h_length); desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *); } if (desired_size > *buffer_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } memcpy(&new_ht, ht, sizeof(struct hostent)); memset(buffer, 0, desired_size); *buffer_size = desired_size; p = buffer + sizeof(struct hostent) + sizeof(char *); memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *)); p = (char *)_ALIGN(p); if (new_ht.h_name != NULL) { size = strlen(new_ht.h_name); memcpy(p, new_ht.h_name, size); new_ht.h_name = p; p += size + 1; } if (new_ht.h_aliases != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size); new_ht.h_aliases = (char **)p; p += sizeof(char *) * (aliases_size + 1); for (iter = new_ht.h_aliases; *iter; ++iter) { size = strlen(*iter); memcpy(p, *iter, size); *iter = p; p += size + 1; } } if (new_ht.h_addr_list != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size); new_ht.h_addr_list = (char **)p; p += sizeof(char *) * (addr_size + 1); size = _ALIGN(new_ht.h_length); for (iter = new_ht.h_addr_list; *iter; ++iter) { memcpy(p, *iter, size); *iter = p; p += size + 1; } } memcpy(buffer, &new_ht, sizeof(struct hostent)); return (NS_SUCCESS); } static int host_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *str; void *addr; socklen_t len; int type; struct hostent *ht; char *p; char **iter; char *orig_buf; size_t orig_buf_size; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: str = va_arg(ap, char *); type = va_arg(ap, int); break; case nss_lt_id: addr = va_arg(ap, void *); len = va_arg(ap, socklen_t); type = va_arg(ap, int); break; default: /* should be unreachable */ return (NS_UNAVAIL); } ht = va_arg(ap, struct hostent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); if (orig_buf_size < buffer_size - sizeof(struct hostent) - sizeof(char *)) { errno = ERANGE; return (NS_RETURN); } memcpy(ht, buffer, sizeof(struct hostent)); memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *)); orig_buf = (char *)_ALIGN(orig_buf); memcpy(orig_buf, buffer + sizeof(struct hostent) + sizeof(char *) + _ALIGN(p) - (size_t)p, buffer_size - sizeof(struct hostent) - sizeof(char *) - _ALIGN(p) + (size_t)p); p = (char *)_ALIGN(p); NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *); if (ht->h_aliases != NULL) { NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **); for (iter = ht->h_aliases; *iter; ++iter) NS_APPLY_OFFSET(*iter, orig_buf, p, char *); } if (ht->h_addr_list != NULL) { NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **); for (iter = ht->h_addr_list; *iter; ++iter) NS_APPLY_OFFSET(*iter, orig_buf, p, char *); } *((struct hostent **)retval) = ht; return (NS_SUCCESS); } #endif /* NS_CACHING */ static int fakeaddr(const char *name, int af, struct hostent *hp, char *buf, size_t buflen, res_state statp) { struct hostent_data *hed; struct hostent he; if ((hed = __hostent_data_init()) == NULL) { errno = ENOMEM; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } if ((af != AF_INET || inet_aton(name, (struct in_addr *)hed->host_addr) != 1) && inet_pton(af, name, hed->host_addr) != 1) { RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); return (-1); } strncpy(hed->hostbuf, name, MAXDNAME); hed->hostbuf[MAXDNAME] = '\0'; if (af == AF_INET && (statp->options & RES_USE_INET6) != 0U) { _map_v4v6_address((char *)hed->host_addr, (char *)hed->host_addr); af = AF_INET6; } he.h_addrtype = af; switch(af) { case AF_INET: he.h_length = NS_INADDRSZ; break; case AF_INET6: he.h_length = NS_IN6ADDRSZ; break; default: errno = EAFNOSUPPORT; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } he.h_name = hed->hostbuf; he.h_aliases = hed->host_aliases; hed->host_aliases[0] = NULL; hed->h_addr_ptrs[0] = (char *)hed->host_addr; hed->h_addr_ptrs[1] = NULL; he.h_addr_list = hed->h_addr_ptrs; if (__copy_hostent(&he, hp, buf, buflen) != 0) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } RES_SET_H_ERRNO(statp, NETDB_SUCCESS); return (0); } int gethostbyname_r(const char *name, struct hostent *he, char *buffer, size_t buflen, struct hostent **result, int *h_errnop) { res_state statp; statp = __res_state(); if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } if (statp->options & RES_USE_INET6) { if (fakeaddr(name, AF_INET, he, buffer, buflen, statp) == 0) { *result = he; return (0); } if (gethostbyname_internal(name, AF_INET6, he, buffer, buflen, result, h_errnop, statp) == 0) return (0); } return (gethostbyname_internal(name, AF_INET, he, buffer, buflen, result, h_errnop, statp)); } int gethostbyname2_r(const char *name, int af, struct hostent *he, char *buffer, size_t buflen, struct hostent **result, int *h_errnop) { res_state statp; statp = __res_state(); if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); return (-1); } return (gethostbyname_internal(name, af, he, buffer, buflen, result, h_errnop, statp)); } int gethostbyname_internal(const char *name, int af, struct hostent *hp, char *buf, size_t buflen, struct hostent **result, int *h_errnop, res_state statp) { const char *cp; int rval, ret_errno = 0; char abuf[MAXDNAME]; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( hosts, (void *)nss_lt_name, host_id_func, host_marshal_func, host_unmarshal_func); #endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_gethostbyname, NULL) { NSSRC_DNS, _dns_gethostbyname, NULL }, NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */ #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { 0 } }; switch (af) { case AF_INET: case AF_INET6: break; default: RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; errno = EAFNOSUPPORT; return (-1); } /* * if there aren't any dots, it could be a user-level alias. * this is also done in res_query() since we are not the only * function that looks up host names. */ if (!strchr(name, '.') && (cp = res_hostalias(statp, name, abuf, sizeof abuf))) name = cp; if (fakeaddr(name, af, hp, buf, buflen, statp) == 0) { *result = hp; return (0); } rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS, "gethostbyname2_r", default_src, name, af, hp, buf, buflen, &ret_errno, h_errnop); if (rval != NS_SUCCESS) { errno = ret_errno; return ((ret_errno != 0) ? ret_errno : -1); } return (0); } int gethostbyaddr_r(const void *addr, socklen_t len, int af, struct hostent *hp, char *buf, size_t buflen, struct hostent **result, int *h_errnop) { const u_char *uaddr = (const u_char *)addr; const struct in6_addr *addr6; socklen_t size; int rval, ret_errno = 0; res_state statp; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( hosts, (void *)nss_lt_id, host_id_func, host_marshal_func, host_unmarshal_func); #endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_gethostbyaddr, NULL) { NSSRC_DNS, _dns_gethostbyaddr, NULL }, NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */ #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { 0 } }; statp = __res_state(); if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (-1); } if (af == AF_INET6 && len == NS_IN6ADDRSZ) { addr6 = (const struct in6_addr *)addr; if (IN6_IS_ADDR_LINKLOCAL(addr6)) { RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); *h_errnop = statp->res_h_errno; return (-1); } if (IN6_IS_ADDR_V4MAPPED(addr6) || IN6_IS_ADDR_V4COMPAT(addr6)) { /* Unmap. */ uaddr += NS_IN6ADDRSZ - NS_INADDRSZ; af = AF_INET; len = NS_INADDRSZ; } } switch (af) { case AF_INET: size = NS_INADDRSZ; break; case AF_INET6: size = NS_IN6ADDRSZ; break; default: errno = EAFNOSUPPORT; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (-1); } if (size != len) { errno = EINVAL; RES_SET_H_ERRNO(statp, NETDB_INTERNAL); *h_errnop = statp->res_h_errno; return (-1); } rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS, "gethostbyaddr_r", default_src, uaddr, len, af, hp, buf, buflen, &ret_errno, h_errnop); if (rval != NS_SUCCESS) { errno = ret_errno; return ((ret_errno != 0) ? ret_errno : -1); } return (0); } struct hostent * gethostbyname(const char *name) { struct hostdata *hd; struct hostent *rval; int ret_h_errno; if ((hd = __hostdata_init()) == NULL) return (NULL); if (gethostbyname_r(name, &hd->host, hd->data, sizeof(hd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } struct hostent * gethostbyname2(const char *name, int af) { struct hostdata *hd; struct hostent *rval; int ret_h_errno; if ((hd = __hostdata_init()) == NULL) return (NULL); if (gethostbyname2_r(name, af, &hd->host, hd->data, sizeof(hd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } struct hostent * gethostbyaddr(const void *addr, socklen_t len, int af) { struct hostdata *hd; struct hostent *rval; int ret_h_errno; if ((hd = __hostdata_init()) == NULL) return (NULL); if (gethostbyaddr_r(addr, len, af, &hd->host, hd->data, sizeof(hd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } void sethostent(int stayopen) { struct hostent_data *hed; if ((hed = __hostent_data_init()) == NULL) return; _sethosthtent(stayopen, hed); _sethostdnsent(stayopen); } void endhostent(void) { struct hostent_data *hed; if ((hed = __hostent_data_init()) == NULL) return; _endhosthtent(hed); _endhostdnsent(); } Index: stable/12/lib/libc/net/getnetnamadr.c =================================================================== --- stable/12/lib/libc/net/getnetnamadr.c (revision 360413) +++ stable/12/lib/libc/net/getnetnamadr.c (revision 360414) @@ -1,453 +1,453 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1994, Garrett Wollman * * 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 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. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "netdb_private.h" #ifdef NS_CACHING #include "nscache.h" #endif /* Network lookup order if nsswitch.conf is broken or nonexistent */ static const ns_src default_src[] = { { NSSRC_FILES, NS_SUCCESS }, { NSSRC_DNS, NS_SUCCESS }, { 0 } }; NETDB_THREAD_ALLOC(netent_data) NETDB_THREAD_ALLOC(netdata) #ifdef NS_CACHING static int net_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { char *name; uint32_t net; int type; size_t desired_size, size; enum nss_lookup_type lookup_type; int res = NS_UNAVAIL; - lookup_type = (enum nss_lookup_type)cache_mdata; + lookup_type = (enum nss_lookup_type)(uintptr_t)cache_mdata; switch (lookup_type) { case nss_lt_name: name = va_arg(ap, char *); size = strlen(name); desired_size = sizeof(enum nss_lookup_type) + size + 1; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); res = NS_SUCCESS; break; case nss_lt_id: net = va_arg(ap, uint32_t); type = va_arg(ap, int); desired_size = sizeof(enum nss_lookup_type) + sizeof(uint32_t) + sizeof(int); if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), &net, sizeof(uint32_t)); memcpy(buffer + sizeof(enum nss_lookup_type) + sizeof(uint32_t), &type, sizeof(int)); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } static int net_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; uint32_t net; int type; struct netent *ne; char *orig_buf; size_t orig_buf_size; struct netent new_ne; size_t desired_size, size, aliases_size; char *p; char **alias; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: net = va_arg(ap, uint32_t); type = va_arg(ap, int); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } ne = va_arg(ap, struct netent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); desired_size = _ALIGNBYTES + sizeof(struct netent) + sizeof(char *); if (ne->n_name != NULL) desired_size += strlen(ne->n_name) + 1; if (ne->n_aliases != NULL) { aliases_size = 0; for (alias = ne->n_aliases; *alias; ++alias) { desired_size += strlen(*alias) + 1; ++aliases_size; } desired_size += _ALIGNBYTES + (aliases_size + 1) * sizeof(char *); } if (*buffer_size < desired_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } memcpy(&new_ne, ne, sizeof(struct netent)); *buffer_size = desired_size; memset(buffer, 0, desired_size); p = buffer + sizeof(struct netent) + sizeof(char *); memcpy(buffer + sizeof(struct netent), &p, sizeof(char *)); p = (char *)_ALIGN(p); if (new_ne.n_name != NULL) { size = strlen(new_ne.n_name); memcpy(p, new_ne.n_name, size); new_ne.n_name = p; p += size + 1; } if (new_ne.n_aliases != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_ne.n_aliases, sizeof(char *) * aliases_size); new_ne.n_aliases = (char **)p; p += sizeof(char *) * (aliases_size + 1); for (alias = new_ne.n_aliases; *alias; ++alias) { size = strlen(*alias); memcpy(p, *alias, size); *alias = p; p += size + 1; } } memcpy(buffer, &new_ne, sizeof(struct netent)); return (NS_SUCCESS); } static int net_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; uint32_t net; int type; struct netent *ne; char *orig_buf; size_t orig_buf_size; int *ret_errno; char *p; char **alias; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: net = va_arg(ap, uint32_t); type = va_arg(ap, int); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } ne = va_arg(ap, struct netent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); ret_errno = va_arg(ap, int *); if (orig_buf_size < buffer_size - sizeof(struct netent) - sizeof(char *)) { *ret_errno = ERANGE; return (NS_RETURN); } memcpy(ne, buffer, sizeof(struct netent)); memcpy(&p, buffer + sizeof(struct netent), sizeof(char *)); orig_buf = (char *)_ALIGN(orig_buf); memcpy(orig_buf, buffer + sizeof(struct netent) + sizeof(char *) + _ALIGN(p) - (size_t)p, buffer_size - sizeof(struct netent) - sizeof(char *) - _ALIGN(p) + (size_t)p); p = (char *)_ALIGN(p); NS_APPLY_OFFSET(ne->n_name, orig_buf, p, char *); if (ne->n_aliases != NULL) { NS_APPLY_OFFSET(ne->n_aliases, orig_buf, p, char **); for (alias = ne->n_aliases; *alias; ++alias) NS_APPLY_OFFSET(*alias, orig_buf, p, char *); } if (retval != NULL) *((struct netent **)retval) = ne; return (NS_SUCCESS); } #endif /* NS_CACHING */ static void netent_data_free(void *ptr) { struct netent_data *ned = ptr; if (ned == NULL) return; ned->stayopen = 0; _endnethtent(ned); free(ned); } static void netdata_free(void *ptr) { free(ptr); } int __copy_netent(struct netent *ne, struct netent *nptr, char *buf, size_t buflen) { char *cp; int i, n; int numptr, len; /* Find out the amount of space required to store the answer. */ numptr = 1; /* NULL ptr */ len = (char *)ALIGN(buf) - buf; for (i = 0; ne->n_aliases[i]; i++, numptr++) { len += strlen(ne->n_aliases[i]) + 1; } len += strlen(ne->n_name) + 1; len += numptr * sizeof(char*); if (len > (int)buflen) { errno = ERANGE; return (-1); } /* copy net value and type */ nptr->n_addrtype = ne->n_addrtype; nptr->n_net = ne->n_net; cp = (char *)ALIGN(buf) + numptr * sizeof(char *); /* copy official name */ n = strlen(ne->n_name) + 1; strcpy(cp, ne->n_name); nptr->n_name = cp; cp += n; /* copy aliases */ nptr->n_aliases = (char **)ALIGN(buf); for (i = 0 ; ne->n_aliases[i]; i++) { n = strlen(ne->n_aliases[i]) + 1; strcpy(cp, ne->n_aliases[i]); nptr->n_aliases[i] = cp; cp += n; } nptr->n_aliases[i] = NULL; return (0); } int getnetbyname_r(const char *name, struct netent *ne, char *buffer, size_t buflen, struct netent **result, int *h_errorp) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( networks, (void *)nss_lt_name, net_id_func, net_marshal_func, net_unmarshal_func); #endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_getnetbyname, NULL) { NSSRC_DNS, _dns_getnetbyname, NULL }, NS_NIS_CB(_nis_getnetbyname, NULL) /* force -DHESIOD */ #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { 0 } }; int rval, ret_errno = 0; rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS, "getnetbyname_r", default_src, name, ne, buffer, buflen, &ret_errno, h_errorp); if (rval != NS_SUCCESS) { errno = ret_errno; return ((ret_errno != 0) ? ret_errno : -1); } return (0); } int getnetbyaddr_r(uint32_t addr, int af, struct netent *ne, char *buffer, size_t buflen, struct netent **result, int *h_errorp) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( networks, (void *)nss_lt_id, net_id_func, net_marshal_func, net_unmarshal_func); #endif static const ns_dtab dtab[] = { NS_FILES_CB(_ht_getnetbyaddr, NULL) { NSSRC_DNS, _dns_getnetbyaddr, NULL }, NS_NIS_CB(_nis_getnetbyaddr, NULL) /* force -DHESIOD */ #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { 0 } }; int rval, ret_errno = 0; rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS, "getnetbyaddr_r", default_src, addr, af, ne, buffer, buflen, &ret_errno, h_errorp); if (rval != NS_SUCCESS) { errno = ret_errno; return ((ret_errno != 0) ? ret_errno : -1); } return (0); } struct netent * getnetbyname(const char *name) { struct netdata *nd; struct netent *rval; int ret_h_errno; if ((nd = __netdata_init()) == NULL) return (NULL); if (getnetbyname_r(name, &nd->net, nd->data, sizeof(nd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } struct netent * getnetbyaddr(uint32_t addr, int af) { struct netdata *nd; struct netent *rval; int ret_h_errno; if ((nd = __netdata_init()) == NULL) return (NULL); if (getnetbyaddr_r(addr, af, &nd->net, nd->data, sizeof(nd->data), &rval, &ret_h_errno) != 0) return (NULL); return (rval); } void setnetent(int stayopen) { struct netent_data *ned; if ((ned = __netent_data_init()) == NULL) return; _setnethtent(stayopen, ned); _setnetdnsent(stayopen); } void endnetent(void) { struct netent_data *ned; if ((ned = __netent_data_init()) == NULL) return; _endnethtent(ned); _endnetdnsent(); } Index: stable/12/lib/libc/net/getprotoent.c =================================================================== --- stable/12/lib/libc/net/getprotoent.c (revision 360413) +++ stable/12/lib/libc/net/getprotoent.c (revision 360414) @@ -1,555 +1,555 @@ /*- * 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getprotoent.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "namespace.h" #include "reentrant.h" #include "un-namespace.h" #include "netdb_private.h" #ifdef NS_CACHING #include "nscache.h" #endif #include "nss_tls.h" static const ns_src defaultsrc[] = { { NSSRC_FILES, NS_SUCCESS }, { NULL, 0 } }; NETDB_THREAD_ALLOC(protoent_data) NETDB_THREAD_ALLOC(protodata) static void protoent_data_clear(struct protoent_data *ped) { if (ped->fp) { fclose(ped->fp); ped->fp = NULL; } } static void protoent_data_free(void *ptr) { struct protoent_data *ped = ptr; protoent_data_clear(ped); free(ped); } static void protodata_free(void *ptr) { free(ptr); } #ifdef NS_CACHING int __proto_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { char *name; int proto; size_t desired_size, size; enum nss_lookup_type lookup_type; int res = NS_UNAVAIL; - lookup_type = (enum nss_lookup_type)cache_mdata; + lookup_type = (enum nss_lookup_type)(uintptr_t)cache_mdata; switch (lookup_type) { case nss_lt_name: name = va_arg(ap, char *); size = strlen(name); desired_size = sizeof(enum nss_lookup_type) + size + 1; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); res = NS_SUCCESS; break; case nss_lt_id: proto = va_arg(ap, int); desired_size = sizeof(enum nss_lookup_type) + sizeof(int); if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), &proto, sizeof(int)); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } int __proto_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; int num; struct protoent *proto; char *orig_buf; size_t orig_buf_size; struct protoent new_proto; size_t desired_size, size, aliases_size; char *p; char **alias; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: num = va_arg(ap, int); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } proto = va_arg(ap, struct protoent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); desired_size = _ALIGNBYTES + sizeof(struct protoent) + sizeof(char *); if (proto->p_name != NULL) desired_size += strlen(proto->p_name) + 1; if (proto->p_aliases != NULL) { aliases_size = 0; for (alias = proto->p_aliases; *alias; ++alias) { desired_size += strlen(*alias) + 1; ++aliases_size; } desired_size += _ALIGNBYTES + (aliases_size + 1) * sizeof(char *); } if (*buffer_size < desired_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } memcpy(&new_proto, proto, sizeof(struct protoent)); *buffer_size = desired_size; memset(buffer, 0, desired_size); p = buffer + sizeof(struct protoent) + sizeof(char *); memcpy(buffer + sizeof(struct protoent), &p, sizeof(char *)); p = (char *)_ALIGN(p); if (new_proto.p_name != NULL) { size = strlen(new_proto.p_name); memcpy(p, new_proto.p_name, size); new_proto.p_name = p; p += size + 1; } if (new_proto.p_aliases != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_proto.p_aliases, sizeof(char *) * aliases_size); new_proto.p_aliases = (char **)p; p += sizeof(char *) * (aliases_size + 1); for (alias = new_proto.p_aliases; *alias; ++alias) { size = strlen(*alias); memcpy(p, *alias, size); *alias = p; p += size + 1; } } memcpy(buffer, &new_proto, sizeof(struct protoent)); return (NS_SUCCESS); } int __proto_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; int num; struct protoent *proto; char *orig_buf; size_t orig_buf_size; int *ret_errno; char *p; char **alias; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: num = va_arg(ap, int); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } proto = va_arg(ap, struct protoent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); ret_errno = va_arg(ap, int *); if (orig_buf_size < buffer_size - sizeof(struct protoent) - sizeof(char *)) { *ret_errno = ERANGE; return (NS_RETURN); } memcpy(proto, buffer, sizeof(struct protoent)); memcpy(&p, buffer + sizeof(struct protoent), sizeof(char *)); orig_buf = (char *)_ALIGN(orig_buf); memcpy(orig_buf, buffer + sizeof(struct protoent) + sizeof(char *) + _ALIGN(p) - (size_t)p, buffer_size - sizeof(struct protoent) - sizeof(char *) - _ALIGN(p) + (size_t)p); p = (char *)_ALIGN(p); NS_APPLY_OFFSET(proto->p_name, orig_buf, p, char *); if (proto->p_aliases != NULL) { NS_APPLY_OFFSET(proto->p_aliases, orig_buf, p, char **); for (alias = proto->p_aliases; *alias; ++alias) NS_APPLY_OFFSET(*alias, orig_buf, p, char *); } if (retval != NULL) *((struct protoent **)retval) = proto; return (NS_SUCCESS); } NSS_MP_CACHE_HANDLING(protocols); #endif /* NS_CACHING */ int __copy_protoent(struct protoent *pe, struct protoent *pptr, char *buf, size_t buflen) { char *cp; int i, n; int numptr, len; /* Find out the amount of space required to store the answer. */ numptr = 1; /* NULL ptr */ len = (char *)ALIGN(buf) - buf; for (i = 0; pe->p_aliases[i]; i++, numptr++) { len += strlen(pe->p_aliases[i]) + 1; } len += strlen(pe->p_name) + 1; len += numptr * sizeof(char*); if (len > (int)buflen) { errno = ERANGE; return (-1); } /* copy protocol value*/ pptr->p_proto = pe->p_proto; cp = (char *)ALIGN(buf) + numptr * sizeof(char *); /* copy official name */ n = strlen(pe->p_name) + 1; strcpy(cp, pe->p_name); pptr->p_name = cp; cp += n; /* copy aliases */ pptr->p_aliases = (char **)ALIGN(buf); for (i = 0 ; pe->p_aliases[i]; i++) { n = strlen(pe->p_aliases[i]) + 1; strcpy(cp, pe->p_aliases[i]); pptr->p_aliases[i] = cp; cp += n; } pptr->p_aliases[i] = NULL; return (0); } void __setprotoent_p(int f, struct protoent_data *ped) { if (ped->fp == NULL) ped->fp = fopen(_PATH_PROTOCOLS, "re"); else rewind(ped->fp); ped->stayopen |= f; } void __endprotoent_p(struct protoent_data *ped) { if (ped->fp) { fclose(ped->fp); ped->fp = NULL; } ped->stayopen = 0; } int __getprotoent_p(struct protoent *pe, struct protoent_data *ped) { char *p; char *cp, **q, *endp; long l; if (ped->fp == NULL && (ped->fp = fopen(_PATH_PROTOCOLS, "re")) == NULL) return (-1); again: if ((p = fgets(ped->line, sizeof ped->line, ped->fp)) == NULL) return (-1); if (*p == '#') goto again; cp = strpbrk(p, "#\n"); if (cp != NULL) *cp = '\0'; pe->p_name = p; cp = strpbrk(p, " \t"); if (cp == NULL) goto again; *cp++ = '\0'; while (*cp == ' ' || *cp == '\t') cp++; p = strpbrk(cp, " \t"); if (p != NULL) *p++ = '\0'; l = strtol(cp, &endp, 10); if (endp == cp || *endp != '\0' || l < 0 || l > USHRT_MAX) goto again; pe->p_proto = l; q = pe->p_aliases = ped->aliases; if (p != NULL) { cp = p; while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (q < &ped->aliases[_MAXALIASES - 1]) *q++ = cp; cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; } } *q = NULL; return (0); } static int files_getprotoent_r(void *retval, void *mdata, va_list ap) { struct protoent pe; struct protoent_data *ped; struct protoent *pptr; char *buffer; size_t buflen; int *errnop; pptr = va_arg(ap, struct protoent *); buffer = va_arg(ap, char *); buflen = va_arg(ap, size_t); errnop = va_arg(ap, int *); if ((ped = __protoent_data_init()) == NULL) { *errnop = errno; return (NS_NOTFOUND); } if (__getprotoent_p(&pe, ped) != 0) { *errnop = errno; return (NS_NOTFOUND); } if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { *errnop = errno; return (NS_RETURN); } *((struct protoent **)retval) = pptr; return (NS_SUCCESS); } static int files_setprotoent(void *retval, void *mdata, va_list ap) { struct protoent_data *ped; int f; f = va_arg(ap, int); if ((ped = __protoent_data_init()) == NULL) return (NS_UNAVAIL); __setprotoent_p(f, ped); return (NS_UNAVAIL); } static int files_endprotoent(void *retval, void *mdata, va_list ap) { struct protoent_data *ped; if ((ped = __protoent_data_init()) == NULL) return (NS_UNAVAIL); __endprotoent_p(ped); return (NS_UNAVAIL); } int getprotoent_r(struct protoent *pptr, char *buffer, size_t buflen, struct protoent **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( protocols, (void *)nss_lt_all, __proto_marshal_func, __proto_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_getprotoent_r, (void *)nss_lt_all }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotoent_r", defaultsrc, pptr, buffer, buflen, &ret_errno); if (rv != NS_SUCCESS) { errno = ret_errno; return (ret_errno); } return (0); } void setprotoent(int stayopen) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( protocols, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setprotoent, NULL }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "setprotoent", defaultsrc, stayopen); } void endprotoent(void) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( protocols, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_endprotoent, NULL }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "endprotoent", defaultsrc); } struct protoent * getprotoent(void) { struct protodata *pd; struct protoent *rval; if ((pd = __protodata_init()) == NULL) return (NULL); if (getprotoent_r(&pd->proto, pd->data, sizeof(pd->data), &rval) != 0) return (NULL); return (rval); } Index: stable/12/lib/libc/net/getservent.c =================================================================== --- stable/12/lib/libc/net/getservent.c (revision 360413) +++ stable/12/lib/libc/net/getservent.c (revision 360414) @@ -1,1381 +1,1381 @@ /*- * 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getservent.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef YP #include #include #include #endif #include "namespace.h" #include "reentrant.h" #include "un-namespace.h" #include "netdb_private.h" #ifdef NS_CACHING #include "nscache.h" #endif #include "nss_tls.h" enum constants { SETSERVENT = 1, ENDSERVENT = 2, SERVENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ SERVENT_STORAGE_MAX = 1 << 20, /* 1 MByte */ }; struct servent_mdata { enum nss_lookup_type how; int compat_mode; }; static const ns_src defaultsrc[] = { { NSSRC_COMPAT, NS_SUCCESS }, { NULL, 0 } }; static int servent_unpack(char *, struct servent *, char **, size_t, int *); /* files backend declarations */ struct files_state { FILE *fp; int stayopen; int compat_mode_active; }; static void files_endstate(void *); NSS_TLS_HANDLING(files); static int files_servent(void *, void *, va_list); static int files_setservent(void *, void *, va_list); /* db backend declarations */ struct db_state { DB *db; int stayopen; int keynum; }; static void db_endstate(void *); NSS_TLS_HANDLING(db); static int db_servent(void *, void *, va_list); static int db_setservent(void *, void *, va_list); #ifdef YP /* nis backend declarations */ static int nis_servent(void *, void *, va_list); static int nis_setservent(void *, void *, va_list); struct nis_state { int yp_stepping; char yp_domain[MAXHOSTNAMELEN]; char *yp_key; int yp_keylen; }; static void nis_endstate(void *); NSS_TLS_HANDLING(nis); static int nis_servent(void *, void *, va_list); static int nis_setservent(void *, void *, va_list); #endif /* compat backend declarations */ static int compat_setservent(void *, void *, va_list); /* get** wrappers for get**_r functions declarations */ struct servent_state { struct servent serv; char *buffer; size_t bufsize; }; static void servent_endstate(void *); NSS_TLS_HANDLING(servent); struct key { const char *proto; union { const char *name; int port; }; }; static int wrap_getservbyname_r(struct key, struct servent *, char *, size_t, struct servent **); static int wrap_getservbyport_r(struct key, struct servent *, char *, size_t, struct servent **); static int wrap_getservent_r(struct key, struct servent *, char *, size_t, struct servent **); static struct servent *getserv(int (*fn)(struct key, struct servent *, char *, size_t, struct servent **), struct key); #ifdef NS_CACHING static int serv_id_func(char *, size_t *, va_list, void *); static int serv_marshal_func(char *, size_t *, void *, va_list, void *); static int serv_unmarshal_func(char *, size_t, void *, va_list, void *); #endif static int servent_unpack(char *p, struct servent *serv, char **aliases, size_t aliases_size, int *errnop) { char *cp, **q, *endp; long l; if (*p == '#') return -1; memset(serv, 0, sizeof(struct servent)); cp = strpbrk(p, "#\n"); if (cp != NULL) *cp = '\0'; serv->s_name = p; p = strpbrk(p, " \t"); if (p == NULL) return -1; *p++ = '\0'; while (*p == ' ' || *p == '\t') p++; cp = strpbrk(p, ",/"); if (cp == NULL) return -1; *cp++ = '\0'; l = strtol(p, &endp, 10); if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX) return -1; serv->s_port = htons((in_port_t)l); serv->s_proto = cp; q = serv->s_aliases = aliases; cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (q < &aliases[aliases_size - 1]) { *q++ = cp; } else { *q = NULL; *errnop = ERANGE; return -1; } cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; } *q = NULL; return 0; } static int parse_result(struct servent *serv, char *buffer, size_t bufsize, char *resultbuf, size_t resultbuflen, int *errnop) { char **aliases; int aliases_size; if (bufsize <= resultbuflen + _ALIGNBYTES + sizeof(char *)) { *errnop = ERANGE; return (NS_RETURN); } aliases = (char **)_ALIGN(&buffer[resultbuflen + 1]); aliases_size = (buffer + bufsize - (char *)aliases) / sizeof(char *); if (aliases_size < 1) { *errnop = ERANGE; return (NS_RETURN); } memcpy(buffer, resultbuf, resultbuflen); buffer[resultbuflen] = '\0'; if (servent_unpack(buffer, serv, aliases, aliases_size, errnop) != 0) return ((*errnop == 0) ? NS_NOTFOUND : NS_RETURN); return (NS_SUCCESS); } /* files backend implementation */ static void files_endstate(void *p) { FILE * f; if (p == NULL) return; f = ((struct files_state *)p)->fp; if (f != NULL) fclose(f); free(p); } /* * compat structures. compat and files sources functionalities are almost * equal, so they all are managed by files_servent function */ static int files_servent(void *retval, void *mdata, va_list ap) { static const ns_src compat_src[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab compat_dtab[] = { { NSSRC_DB, db_servent, (void *)((struct servent_mdata *)mdata)->how }, #ifdef YP { NSSRC_NIS, nis_servent, (void *)((struct servent_mdata *)mdata)->how }, #endif { NULL, NULL, NULL } }; struct files_state *st; int rv; int stayopen; struct servent_mdata *serv_mdata; char *name; char *proto; int port; struct servent *serv; char *buffer; size_t bufsize; int *errnop; size_t linesize; char *line; char **cp; name = NULL; proto = NULL; serv_mdata = (struct servent_mdata *)mdata; switch (serv_mdata->how) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); break; case nss_lt_all: break; default: return NS_NOTFOUND; } serv = va_arg(ap, struct servent *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap,int *); *errnop = files_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->fp == NULL) st->compat_mode_active = 0; if (st->fp == NULL && (st->fp = fopen(_PATH_SERVICES, "re")) == NULL) { *errnop = errno; return (NS_UNAVAIL); } if (serv_mdata->how == nss_lt_all) stayopen = 1; else { rewind(st->fp); stayopen = st->stayopen; } rv = NS_NOTFOUND; do { if (!st->compat_mode_active) { if ((line = fgetln(st->fp, &linesize)) == NULL) { *errnop = errno; rv = NS_RETURN; break; } if (*line=='+' && serv_mdata->compat_mode != 0) st->compat_mode_active = 1; } if (st->compat_mode_active != 0) { switch (serv_mdata->how) { case nss_lt_name: rv = nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, "getservbyname_r", compat_src, name, proto, serv, buffer, bufsize, errnop); break; case nss_lt_id: rv = nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, "getservbyport_r", compat_src, port, proto, serv, buffer, bufsize, errnop); break; case nss_lt_all: rv = nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, "getservent_r", compat_src, serv, buffer, bufsize, errnop); break; } if (!(rv & NS_TERMINATE) || serv_mdata->how != nss_lt_all) st->compat_mode_active = 0; continue; } rv = parse_result(serv, buffer, bufsize, line, linesize, errnop); if (rv == NS_NOTFOUND) continue; if (rv == NS_RETURN) break; rv = NS_NOTFOUND; switch (serv_mdata->how) { case nss_lt_name: if (strcmp(name, serv->s_name) == 0) goto gotname; for (cp = serv->s_aliases; *cp; cp++) if (strcmp(name, *cp) == 0) goto gotname; continue; gotname: if (proto == NULL || strcmp(serv->s_proto, proto) == 0) rv = NS_SUCCESS; break; case nss_lt_id: if (port != serv->s_port) continue; if (proto == NULL || strcmp(serv->s_proto, proto) == 0) rv = NS_SUCCESS; break; case nss_lt_all: rv = NS_SUCCESS; break; } } while (!(rv & NS_TERMINATE)); if (!stayopen && st->fp != NULL) { fclose(st->fp); st->fp = NULL; } if ((rv == NS_SUCCESS) && (retval != NULL)) *(struct servent **)retval=serv; return (rv); } static int files_setservent(void *retval, void *mdata, va_list ap) { struct files_state *st; int rv; int f; rv = files_getstate(&st); if (rv != 0) return (NS_UNAVAIL); - switch ((enum constants)mdata) { + switch ((enum constants)(uintptr_t)mdata) { case SETSERVENT: f = va_arg(ap,int); if (st->fp == NULL) st->fp = fopen(_PATH_SERVICES, "re"); else rewind(st->fp); st->stayopen |= f; break; case ENDSERVENT: if (st->fp != NULL) { fclose(st->fp); st->fp = NULL; } st->stayopen = 0; break; default: break; } st->compat_mode_active = 0; return (NS_UNAVAIL); } /* db backend implementation */ static void db_endstate(void *p) { DB *db; if (p == NULL) return; db = ((struct db_state *)p)->db; if (db != NULL) db->close(db); free(p); } static int db_servent(void *retval, void *mdata, va_list ap) { char buf[BUFSIZ]; DBT key, data, *result; DB *db; struct db_state *st; int rv; int stayopen; enum nss_lookup_type how; char *name; char *proto; int port; struct servent *serv; char *buffer; size_t bufsize; int *errnop; name = NULL; proto = NULL; - how = (enum nss_lookup_type)mdata; + how = (enum nss_lookup_type)(uintptr_t)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); break; case nss_lt_all: break; default: return NS_NOTFOUND; } serv = va_arg(ap, struct servent *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap,int *); *errnop = db_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (how == nss_lt_all && st->keynum < 0) return (NS_NOTFOUND); if (st->db == NULL) { st->db = dbopen(_PATH_SERVICES_DB, O_RDONLY, 0, DB_HASH, NULL); if (st->db == NULL) { *errnop = errno; return (NS_UNAVAIL); } } stayopen = (how == nss_lt_all) ? 1 : st->stayopen; db = st->db; do { switch (how) { case nss_lt_name: key.data = buf; if (proto == NULL) key.size = snprintf(buf, sizeof(buf), "\376%s", name); else key.size = snprintf(buf, sizeof(buf), "\376%s/%s", name, proto); key.size++; if (db->get(db, &key, &data, 0) != 0 || db->get(db, &data, &key, 0) != 0) { rv = NS_NOTFOUND; goto db_fin; } result = &key; break; case nss_lt_id: key.data = buf; port = htons(port); if (proto == NULL) key.size = snprintf(buf, sizeof(buf), "\377%d", port); else key.size = snprintf(buf, sizeof(buf), "\377%d/%s", port, proto); key.size++; if (db->get(db, &key, &data, 0) != 0 || db->get(db, &data, &key, 0) != 0) { rv = NS_NOTFOUND; goto db_fin; } result = &key; break; case nss_lt_all: key.data = buf; key.size = snprintf(buf, sizeof(buf), "%d", st->keynum++); key.size++; if (db->get(db, &key, &data, 0) != 0) { st->keynum = -1; rv = NS_NOTFOUND; goto db_fin; } result = &data; break; } rv = parse_result(serv, buffer, bufsize, result->data, result->size - 1, errnop); } while (!(rv & NS_TERMINATE) && how == nss_lt_all); db_fin: if (!stayopen && st->db != NULL) { db->close(db); st->db = NULL; } if (rv == NS_SUCCESS && retval != NULL) *(struct servent **)retval = serv; return (rv); } static int db_setservent(void *retval, void *mdata, va_list ap) { DB *db; struct db_state *st; int rv; int f; rv = db_getstate(&st); if (rv != 0) return (NS_UNAVAIL); - switch ((enum constants)mdata) { + switch ((enum constants)(uintptr_t)mdata) { case SETSERVENT: f = va_arg(ap, int); st->stayopen |= f; st->keynum = 0; break; case ENDSERVENT: db = st->db; if (db != NULL) { db->close(db); st->db = NULL; } st->stayopen = 0; break; default: break; } return (NS_UNAVAIL); } /* nis backend implementation */ #ifdef YP static void nis_endstate(void *p) { if (p == NULL) return; free(((struct nis_state *)p)->yp_key); free(p); } static int nis_servent(void *retval, void *mdata, va_list ap) { char *resultbuf, *lastkey; int resultbuflen; char *buf; struct nis_state *st; int rv; enum nss_lookup_type how; char *name; char *proto; int port; struct servent *serv; char *buffer; size_t bufsize; int *errnop; name = NULL; proto = NULL; buf = NULL; - how = (enum nss_lookup_type)mdata; + how = (enum nss_lookup_type)(uintptr_t)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); break; case nss_lt_all: break; default: return NS_NOTFOUND; } serv = va_arg(ap, struct servent *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = nis_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->yp_domain[0] == '\0') { if (getdomainname(st->yp_domain, sizeof st->yp_domain)) { *errnop = errno; return (NS_UNAVAIL); } } do { switch (how) { case nss_lt_name: free(buf); asprintf(&buf, "%s/%s", name, proto); if (buf == NULL) return (NS_TRYAGAIN); if (yp_match(st->yp_domain, "services.byname", buf, strlen(buf), &resultbuf, &resultbuflen)) { rv = NS_NOTFOUND; goto fin; } break; case nss_lt_id: free(buf); asprintf(&buf, "%d/%s", ntohs(port), proto); if (buf == NULL) return (NS_TRYAGAIN); /* * We have to be a little flexible * here. Ideally you're supposed to have both * a services.byname and a services.byport * map, but some systems have only * services.byname. FreeBSD cheats a little by * putting the services.byport information in * the same map as services.byname so that * either case will work. We allow for both * possibilities here: if there is no * services.byport map, we try services.byname * instead. */ rv = yp_match(st->yp_domain, "services.byport", buf, strlen(buf), &resultbuf, &resultbuflen); if (rv) { if (rv == YPERR_MAP) { if (yp_match(st->yp_domain, "services.byname", buf, strlen(buf), &resultbuf, &resultbuflen)) { rv = NS_NOTFOUND; goto fin; } } else { rv = NS_NOTFOUND; goto fin; } } break; case nss_lt_all: if (!st->yp_stepping) { free(st->yp_key); rv = yp_first(st->yp_domain, "services.byname", &st->yp_key, &st->yp_keylen, &resultbuf, &resultbuflen); if (rv) { rv = NS_NOTFOUND; goto fin; } st->yp_stepping = 1; } else { lastkey = st->yp_key; rv = yp_next(st->yp_domain, "services.byname", st->yp_key, st->yp_keylen, &st->yp_key, &st->yp_keylen, &resultbuf, &resultbuflen); free(lastkey); if (rv) { st->yp_stepping = 0; rv = NS_NOTFOUND; goto fin; } } break; } rv = parse_result(serv, buffer, bufsize, resultbuf, resultbuflen, errnop); free(resultbuf); } while (!(rv & NS_TERMINATE) && how == nss_lt_all); fin: free(buf); if (rv == NS_SUCCESS && retval != NULL) *(struct servent **)retval = serv; return (rv); } static int nis_setservent(void *result, void *mdata, va_list ap) { struct nis_state *st; int rv; rv = nis_getstate(&st); if (rv != 0) return (NS_UNAVAIL); - switch ((enum constants)mdata) { + switch ((enum constants)(uintptr_t)mdata) { case SETSERVENT: case ENDSERVENT: free(st->yp_key); st->yp_key = NULL; st->yp_stepping = 0; break; default: break; } return (NS_UNAVAIL); } #endif /* compat backend implementation */ static int compat_setservent(void *retval, void *mdata, va_list ap) { static const ns_src compat_src[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab compat_dtab[] = { { NSSRC_DB, db_setservent, mdata }, #ifdef YP { NSSRC_NIS, nis_setservent, mdata }, #endif { NULL, NULL, NULL } }; int f; (void)files_setservent(retval, mdata, ap); - switch ((enum constants)mdata) { + switch ((enum constants)(uintptr_t)mdata) { case SETSERVENT: f = va_arg(ap,int); (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, "setservent", compat_src, f); break; case ENDSERVENT: (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, "endservent", compat_src); break; default: break; } return (NS_UNAVAIL); } #ifdef NS_CACHING static int serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { char *name; char *proto; int port; size_t desired_size, size, size2; enum nss_lookup_type lookup_type; int res = NS_UNAVAIL; - lookup_type = (enum nss_lookup_type)cache_mdata; + lookup_type = (enum nss_lookup_type)(uintptr_t)cache_mdata; switch (lookup_type) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); size = strlen(name); desired_size = sizeof(enum nss_lookup_type) + size + 1; if (proto != NULL) { size2 = strlen(proto); desired_size += size2 + 1; } else size2 = 0; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); if (proto != NULL) memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1, proto, size2 + 1); res = NS_SUCCESS; break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); desired_size = sizeof(enum nss_lookup_type) + sizeof(int); if (proto != NULL) { size = strlen(proto); desired_size += size + 1; } else size = 0; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), &port, sizeof(int)); if (proto != NULL) memcpy(buffer + sizeof(enum nss_lookup_type) + sizeof(int), proto, size + 1); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } int serv_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; char *proto; int port; struct servent *serv; char *orig_buf; size_t orig_buf_size; struct servent new_serv; size_t desired_size; char **alias; char *p; size_t size; size_t aliases_size; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } serv = va_arg(ap, struct servent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *); if (serv->s_name != NULL) desired_size += strlen(serv->s_name) + 1; if (serv->s_proto != NULL) desired_size += strlen(serv->s_proto) + 1; aliases_size = 0; if (serv->s_aliases != NULL) { for (alias = serv->s_aliases; *alias; ++alias) { desired_size += strlen(*alias) + 1; ++aliases_size; } desired_size += _ALIGNBYTES + sizeof(char *) * (aliases_size + 1); } if (*buffer_size < desired_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } memcpy(&new_serv, serv, sizeof(struct servent)); memset(buffer, 0, desired_size); *buffer_size = desired_size; p = buffer + sizeof(struct servent) + sizeof(char *); memcpy(buffer + sizeof(struct servent), &p, sizeof(char *)); p = (char *)_ALIGN(p); if (new_serv.s_name != NULL) { size = strlen(new_serv.s_name); memcpy(p, new_serv.s_name, size); new_serv.s_name = p; p += size + 1; } if (new_serv.s_proto != NULL) { size = strlen(new_serv.s_proto); memcpy(p, new_serv.s_proto, size); new_serv.s_proto = p; p += size + 1; } if (new_serv.s_aliases != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size); new_serv.s_aliases = (char **)p; p += sizeof(char *) * (aliases_size + 1); for (alias = new_serv.s_aliases; *alias; ++alias) { size = strlen(*alias); memcpy(p, *alias, size); *alias = p; p += size + 1; } } memcpy(buffer, &new_serv, sizeof(struct servent)); return (NS_SUCCESS); } int serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; char *proto; int port; struct servent *serv; char *orig_buf; char *p; char **alias; size_t orig_buf_size; int *ret_errno; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); proto = va_arg(ap, char *); break; case nss_lt_id: port = va_arg(ap, int); proto = va_arg(ap, char *); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } serv = va_arg(ap, struct servent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); ret_errno = va_arg(ap, int *); if (orig_buf_size < buffer_size - sizeof(struct servent) - sizeof(char *)) { *ret_errno = ERANGE; return (NS_RETURN); } memcpy(serv, buffer, sizeof(struct servent)); memcpy(&p, buffer + sizeof(struct servent), sizeof(char *)); orig_buf = (char *)_ALIGN(orig_buf); memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) + (_ALIGN(p) - (size_t)p), buffer_size - sizeof(struct servent) - sizeof(char *) - (_ALIGN(p) - (size_t)p)); p = (char *)_ALIGN(p); NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *); NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *); if (serv->s_aliases != NULL) { NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **); for (alias = serv->s_aliases; *alias; ++alias) NS_APPLY_OFFSET(*alias, orig_buf, p, char *); } if (retval != NULL) *((struct servent **)retval) = serv; return (NS_SUCCESS); } NSS_MP_CACHE_HANDLING(services); #endif /* NS_CACHING */ /* get**_r functions implementation */ int getservbyname_r(const char *name, const char *proto, struct servent *serv, char *buffer, size_t bufsize, struct servent **result) { static const struct servent_mdata mdata = { nss_lt_name, 0 }; static const struct servent_mdata compat_mdata = { nss_lt_name, 1 }; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( services, (void *)nss_lt_name, serv_id_func, serv_marshal_func, serv_unmarshal_func); #endif /* NS_CACHING */ static const ns_dtab dtab[] = { { NSSRC_FILES, files_servent, (void *)&mdata }, { NSSRC_DB, db_servent, (void *)nss_lt_name }, #ifdef YP { NSSRC_NIS, nis_servent, (void *)nss_lt_name }, #endif { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r", defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getservbyport_r(int port, const char *proto, struct servent *serv, char *buffer, size_t bufsize, struct servent **result) { static const struct servent_mdata mdata = { nss_lt_id, 0 }; static const struct servent_mdata compat_mdata = { nss_lt_id, 1 }; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( services, (void *)nss_lt_id, serv_id_func, serv_marshal_func, serv_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_servent, (void *)&mdata }, { NSSRC_DB, db_servent, (void *)nss_lt_id }, #ifdef YP { NSSRC_NIS, nis_servent, (void *)nss_lt_id }, #endif { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r", defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getservent_r(struct servent *serv, char *buffer, size_t bufsize, struct servent **result) { static const struct servent_mdata mdata = { nss_lt_all, 0 }; static const struct servent_mdata compat_mdata = { nss_lt_all, 1 }; #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( services, (void *)nss_lt_all, serv_marshal_func, serv_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_servent, (void *)&mdata }, { NSSRC_DB, db_servent, (void *)nss_lt_all }, #ifdef YP { NSSRC_NIS, nis_servent, (void *)nss_lt_all }, #endif { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r", defaultsrc, serv, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } void setservent(int stayopen) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( services, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setservent, (void *)SETSERVENT }, { NSSRC_DB, db_setservent, (void *)SETSERVENT }, #ifdef YP { NSSRC_NIS, nis_setservent, (void *)SETSERVENT }, #endif { NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc, stayopen); } void endservent(void) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( services, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setservent, (void *)ENDSERVENT }, { NSSRC_DB, db_setservent, (void *)ENDSERVENT }, #ifdef YP { NSSRC_NIS, nis_setservent, (void *)ENDSERVENT }, #endif { NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc); } /* get** wrappers for get**_r functions implementation */ static void servent_endstate(void *p) { if (p == NULL) return; free(((struct servent_state *)p)->buffer); free(p); } static int wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer, size_t bufsize, struct servent **res) { return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize, res)); } static int wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer, size_t bufsize, struct servent **res) { return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize, res)); } static int wrap_getservent_r(struct key key, struct servent *serv, char *buffer, size_t bufsize, struct servent **res) { return (getservent_r(serv, buffer, bufsize, res)); } static struct servent * getserv(int (*fn)(struct key, struct servent *, char *, size_t, struct servent **), struct key key) { int rv; struct servent *res; struct servent_state * st; rv = servent_getstate(&st); if (rv != 0) { errno = rv; return NULL; } if (st->buffer == NULL) { st->buffer = malloc(SERVENT_STORAGE_INITIAL); if (st->buffer == NULL) return (NULL); st->bufsize = SERVENT_STORAGE_INITIAL; } do { rv = fn(key, &st->serv, st->buffer, st->bufsize, &res); if (res == NULL && rv == ERANGE) { free(st->buffer); if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) { st->buffer = NULL; errno = ERANGE; return (NULL); } st->bufsize <<= 1; st->buffer = malloc(st->bufsize); if (st->buffer == NULL) return (NULL); } } while (res == NULL && rv == ERANGE); if (rv != 0) errno = rv; return (res); } struct servent * getservbyname(const char *name, const char *proto) { struct key key; key.name = name; key.proto = proto; return (getserv(wrap_getservbyname_r, key)); } struct servent * getservbyport(int port, const char *proto) { struct key key; key.port = port; key.proto = proto; return (getserv(wrap_getservbyport_r, key)); } struct servent * getservent(void) { struct key key; key.proto = NULL; key.port = 0; return (getserv(wrap_getservent_r, key)); } Index: stable/12/lib/libc/rpc/getrpcent.c =================================================================== --- stable/12/lib/libc/rpc/getrpcent.c (revision 360413) +++ stable/12/lib/libc/rpc/getrpcent.c (revision 360414) @@ -1,1057 +1,1057 @@ /* $NetBSD: getrpcent.c,v 1.17 2000/01/22 22:19:17 mycroft Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009, Sun Microsystems, 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: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - 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. * - Neither the name of Sun Microsystems, Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 = "@(#)getrpcent.c 1.14 91/03/11 Copyr 1984 Sun Micro"; #endif #include __FBSDID("$FreeBSD$"); /* * Copyright (c) 1984 by Sun Microsystems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef YP #include #include #endif #include #include "namespace.h" #include "reentrant.h" #include "un-namespace.h" #include "libc_private.h" #include "nss_tls.h" #ifdef NS_CACHING #include "nscache.h" #endif #define RPCDB "/etc/rpc" /* nsswitch declarations */ enum constants { SETRPCENT = 1, ENDRPCENT = 2, RPCENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ RPCENT_STORAGE_MAX = 1 << 20, /* 1 MByte */ }; static const ns_src defaultsrc[] = { { NSSRC_FILES, NS_SUCCESS }, #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; /* files backend declarations */ struct files_state { FILE *fp; int stayopen; }; static int files_rpcent(void *, void *, va_list); static int files_setrpcent(void *, void *, va_list); static void files_endstate(void *); NSS_TLS_HANDLING(files); /* nis backend declarations */ #ifdef YP struct nis_state { char domain[MAXHOSTNAMELEN]; char *current; int currentlen; int stepping; int no_name_map; }; static int nis_rpcent(void *, void *, va_list); static int nis_setrpcent(void *, void *, va_list); static void nis_endstate(void *); NSS_TLS_HANDLING(nis); #endif /* get** wrappers for get**_r functions declarations */ struct rpcent_state { struct rpcent rpc; char *buffer; size_t bufsize; }; static void rpcent_endstate(void *); NSS_TLS_HANDLING(rpcent); union key { const char *name; int number; }; static int wrap_getrpcbyname_r(union key, struct rpcent *, char *, size_t, struct rpcent **); static int wrap_getrpcbynumber_r(union key, struct rpcent *, char *, size_t, struct rpcent **); static int wrap_getrpcent_r(union key, struct rpcent *, char *, size_t, struct rpcent **); static struct rpcent *getrpc(int (*fn)(union key, struct rpcent *, char *, size_t, struct rpcent **), union key); #ifdef NS_CACHING static int rpc_id_func(char *, size_t *, va_list, void *); static int rpc_marshal_func(char *, size_t *, void *, va_list, void *); static int rpc_unmarshal_func(char *, size_t, void *, va_list, void *); #endif static int rpcent_unpack(char *p, struct rpcent *rpc, char **r_aliases, size_t aliases_size, int *errnop) { char *cp, **q; assert(p != NULL); if (*p == '#') return (-1); cp = strpbrk(p, "#\n"); if (cp == NULL) return (-1); *cp = '\0'; cp = strpbrk(p, " \t"); if (cp == NULL) return (-1); *cp++ = '\0'; /* THIS STUFF IS INTERNET SPECIFIC */ rpc->r_name = p; while (*cp == ' ' || *cp == '\t') cp++; rpc->r_number = atoi(cp); q = rpc->r_aliases = r_aliases; cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (q < &(r_aliases[aliases_size - 1])) *q++ = cp; else { *errnop = ERANGE; return -1; } cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; } *q = NULL; return 0; } /* files backend implementation */ static void files_endstate(void *p) { FILE * f; if (p == NULL) return; f = ((struct files_state *)p)->fp; if (f != NULL) fclose(f); free(p); } static int files_rpcent(void *retval, void *mdata, va_list ap) { char *name; int number; struct rpcent *rpc; char *buffer; size_t bufsize; int *errnop; char *line; size_t linesize; char **aliases; int aliases_size; char **rp; struct files_state *st; int rv; int stayopen; enum nss_lookup_type how; - how = (enum nss_lookup_type)mdata; + how = (enum nss_lookup_type)(uintptr_t)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: number = va_arg(ap, int); break; case nss_lt_all: break; default: return (NS_NOTFOUND); } rpc = va_arg(ap, struct rpcent *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = files_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->fp == NULL && (st->fp = fopen(RPCDB, "r")) == NULL) { *errnop = errno; return (NS_UNAVAIL); } if (how == nss_lt_all) stayopen = 1; else { rewind(st->fp); stayopen = st->stayopen; } do { if ((line = fgetln(st->fp, &linesize)) == NULL) { *errnop = errno; rv = NS_RETURN; break; } if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { *errnop = ERANGE; rv = NS_RETURN; break; } aliases = (char **)_ALIGN(&buffer[linesize+1]); aliases_size = (buffer + bufsize - (char *)aliases)/sizeof(char *); if (aliases_size < 1) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, line, linesize); buffer[linesize] = '\0'; rv = rpcent_unpack(buffer, rpc, aliases, aliases_size, errnop); if (rv != 0) { if (*errnop == 0) { rv = NS_NOTFOUND; continue; } else { rv = NS_RETURN; break; } } switch (how) { case nss_lt_name: if (strcmp(rpc->r_name, name) == 0) goto done; for (rp = rpc->r_aliases; *rp != NULL; rp++) { if (strcmp(*rp, name) == 0) goto done; } rv = NS_NOTFOUND; continue; done: rv = NS_SUCCESS; break; case nss_lt_id: rv = (rpc->r_number == number) ? NS_SUCCESS : NS_NOTFOUND; break; case nss_lt_all: rv = NS_SUCCESS; break; } } while (!(rv & NS_TERMINATE)); if (!stayopen && st->fp!=NULL) { fclose(st->fp); st->fp = NULL; } if ((rv == NS_SUCCESS) && (retval != NULL)) *((struct rpcent **)retval) = rpc; return (rv); } static int files_setrpcent(void *retval, void *mdata, va_list ap) { struct files_state *st; int rv; int f; rv = files_getstate(&st); if (rv != 0) return (NS_UNAVAIL); - switch ((enum constants)mdata) + switch ((enum constants)(uintptr_t)mdata) { case SETRPCENT: f = va_arg(ap,int); if (st->fp == NULL) st->fp = fopen(RPCDB, "r"); else rewind(st->fp); st->stayopen |= f; break; case ENDRPCENT: if (st->fp != NULL) { fclose(st->fp); st->fp = NULL; } st->stayopen = 0; break; default: break; } return (NS_UNAVAIL); } /* nis backend implementation */ #ifdef YP static void nis_endstate(void *p) { if (p == NULL) return; free(((struct nis_state *)p)->current); free(p); } static int nis_rpcent(void *retval, void *mdata, va_list ap) { char *name; int number; struct rpcent *rpc; char *buffer; size_t bufsize; int *errnop; char **rp; char **aliases; int aliases_size; char *lastkey; char *resultbuf; int resultbuflen; char *buf; struct nis_state *st; int rv; enum nss_lookup_type how; int no_name_active; - how = (enum nss_lookup_type)mdata; + how = (enum nss_lookup_type)(uintptr_t)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: number = va_arg(ap, int); break; case nss_lt_all: break; default: return (NS_NOTFOUND); } buf = NULL; rpc = va_arg(ap, struct rpcent *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = nis_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->domain[0] == '\0') { if (getdomainname(st->domain, sizeof(st->domain)) != 0) { *errnop = errno; return (NS_UNAVAIL); } } no_name_active = 0; do { switch (how) { case nss_lt_name: if (!st->no_name_map) { free(buf); asprintf(&buf, "%s", name); if (buf == NULL) return (NS_TRYAGAIN); rv = yp_match(st->domain, "rpc.byname", buf, strlen(buf), &resultbuf, &resultbuflen); switch (rv) { case 0: break; case YPERR_MAP: st->stepping = 0; no_name_active = 1; how = nss_lt_all; rv = NS_NOTFOUND; continue; default: rv = NS_NOTFOUND; goto fin; } } else { st->stepping = 0; no_name_active = 1; how = nss_lt_all; rv = NS_NOTFOUND; continue; } break; case nss_lt_id: free(buf); asprintf(&buf, "%d", number); if (buf == NULL) return (NS_TRYAGAIN); if (yp_match(st->domain, "rpc.bynumber", buf, strlen(buf), &resultbuf, &resultbuflen)) { rv = NS_NOTFOUND; goto fin; } break; case nss_lt_all: if (!st->stepping) { rv = yp_first(st->domain, "rpc.bynumber", &st->current, &st->currentlen, &resultbuf, &resultbuflen); if (rv) { rv = NS_NOTFOUND; goto fin; } st->stepping = 1; } else { lastkey = st->current; rv = yp_next(st->domain, "rpc.bynumber", st->current, st->currentlen, &st->current, &st->currentlen, &resultbuf, &resultbuflen); free(lastkey); if (rv) { st->stepping = 0; rv = NS_NOTFOUND; goto fin; } } break; } /* we need a room for additional \n symbol */ if (bufsize <= resultbuflen + 1 + _ALIGNBYTES + sizeof(char *)) { *errnop = ERANGE; rv = NS_RETURN; free(resultbuf); break; } aliases=(char **)_ALIGN(&buffer[resultbuflen+2]); aliases_size = (buffer + bufsize - (char *)aliases) / sizeof(char *); if (aliases_size < 1) { *errnop = ERANGE; rv = NS_RETURN; free(resultbuf); break; } /* * rpcent_unpack expects lines terminated with \n -- make it happy */ memcpy(buffer, resultbuf, resultbuflen); buffer[resultbuflen] = '\n'; buffer[resultbuflen+1] = '\0'; free(resultbuf); if (rpcent_unpack(buffer, rpc, aliases, aliases_size, errnop) != 0) { if (*errnop == 0) rv = NS_NOTFOUND; else rv = NS_RETURN; } else { if ((how == nss_lt_all) && (no_name_active != 0)) { if (strcmp(rpc->r_name, name) == 0) goto done; for (rp = rpc->r_aliases; *rp != NULL; rp++) { if (strcmp(*rp, name) == 0) goto done; } rv = NS_NOTFOUND; continue; done: rv = NS_SUCCESS; } else rv = NS_SUCCESS; } } while (!(rv & NS_TERMINATE) && (how == nss_lt_all)); fin: free(buf); if ((rv == NS_SUCCESS) && (retval != NULL)) *((struct rpcent **)retval) = rpc; return (rv); } static int nis_setrpcent(void *retval, void *mdata, va_list ap) { struct nis_state *st; int rv; rv = nis_getstate(&st); if (rv != 0) return (NS_UNAVAIL); - switch ((enum constants)mdata) + switch ((enum constants)(uintptr_t)mdata) { case SETRPCENT: case ENDRPCENT: free(st->current); st->current = NULL; st->stepping = 0; break; default: break; } return (NS_UNAVAIL); } #endif #ifdef NS_CACHING static int rpc_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { char *name; int rpc; size_t desired_size, size; enum nss_lookup_type lookup_type; int res = NS_UNAVAIL; - lookup_type = (enum nss_lookup_type)cache_mdata; + lookup_type = (enum nss_lookup_type)(uintptr_t)cache_mdata; switch (lookup_type) { case nss_lt_name: name = va_arg(ap, char *); size = strlen(name); desired_size = sizeof(enum nss_lookup_type) + size + 1; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); res = NS_SUCCESS; break; case nss_lt_id: rpc = va_arg(ap, int); desired_size = sizeof(enum nss_lookup_type) + sizeof(int); if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), &rpc, sizeof(int)); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } static int rpc_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; int num; struct rpcent *rpc; char *orig_buf; size_t orig_buf_size; struct rpcent new_rpc; size_t desired_size, size, aliases_size; char *p; char **alias; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: num = va_arg(ap, int); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } rpc = va_arg(ap, struct rpcent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); desired_size = _ALIGNBYTES + sizeof(struct rpcent) + sizeof(char *); if (rpc->r_name != NULL) desired_size += strlen(rpc->r_name) + 1; if (rpc->r_aliases != NULL) { aliases_size = 0; for (alias = rpc->r_aliases; *alias; ++alias) { desired_size += strlen(*alias) + 1; ++aliases_size; } desired_size += _ALIGNBYTES + (aliases_size + 1) * sizeof(char *); } if (*buffer_size < desired_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } new_rpc = *rpc; *buffer_size = desired_size; memset(buffer, 0, desired_size); p = buffer + sizeof(struct rpcent) + sizeof(char *); memcpy(buffer + sizeof(struct rpcent), &p, sizeof(char *)); p = (char *)_ALIGN(p); if (new_rpc.r_name != NULL) { size = strlen(new_rpc.r_name); memcpy(p, new_rpc.r_name, size); new_rpc.r_name = p; p += size + 1; } if (new_rpc.r_aliases != NULL) { p = (char *)_ALIGN(p); memcpy(p, new_rpc.r_aliases, sizeof(char *) * aliases_size); new_rpc.r_aliases = (char **)p; p += sizeof(char *) * (aliases_size + 1); for (alias = new_rpc.r_aliases; *alias; ++alias) { size = strlen(*alias); memcpy(p, *alias, size); *alias = p; p += size + 1; } } memcpy(buffer, &new_rpc, sizeof(struct rpcent)); return (NS_SUCCESS); } static int rpc_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; int num; struct rpcent *rpc; char *orig_buf; size_t orig_buf_size; int *ret_errno; char *p; char **alias; - switch ((enum nss_lookup_type)cache_mdata) { + switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: num = va_arg(ap, int); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } rpc = va_arg(ap, struct rpcent *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); ret_errno = va_arg(ap, int *); if (orig_buf_size < buffer_size - sizeof(struct rpcent) - sizeof(char *)) { *ret_errno = ERANGE; return (NS_RETURN); } memcpy(rpc, buffer, sizeof(struct rpcent)); memcpy(&p, buffer + sizeof(struct rpcent), sizeof(char *)); orig_buf = (char *)_ALIGN(orig_buf); memcpy(orig_buf, buffer + sizeof(struct rpcent) + sizeof(char *) + _ALIGN(p) - (size_t)p, buffer_size - sizeof(struct rpcent) - sizeof(char *) - _ALIGN(p) + (size_t)p); p = (char *)_ALIGN(p); NS_APPLY_OFFSET(rpc->r_name, orig_buf, p, char *); if (rpc->r_aliases != NULL) { NS_APPLY_OFFSET(rpc->r_aliases, orig_buf, p, char **); for (alias = rpc->r_aliases ; *alias; ++alias) NS_APPLY_OFFSET(*alias, orig_buf, p, char *); } if (retval != NULL) *((struct rpcent **)retval) = rpc; return (NS_SUCCESS); } NSS_MP_CACHE_HANDLING(rpc); #endif /* NS_CACHING */ /* get**_r functions implementation */ static int getrpcbyname_r(const char *name, struct rpcent *rpc, char *buffer, size_t bufsize, struct rpcent **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( rpc, (void *)nss_lt_name, rpc_id_func, rpc_marshal_func, rpc_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_rpcent, (void *)nss_lt_name }, #ifdef YP { NSSRC_NIS, nis_rpcent, (void *)nss_lt_name }, #endif #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_RPC, "getrpcbyname_r", defaultsrc, name, rpc, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } static int getrpcbynumber_r(int number, struct rpcent *rpc, char *buffer, size_t bufsize, struct rpcent **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( rpc, (void *)nss_lt_id, rpc_id_func, rpc_marshal_func, rpc_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_rpcent, (void *)nss_lt_id }, #ifdef YP { NSSRC_NIS, nis_rpcent, (void *)nss_lt_id }, #endif #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_RPC, "getrpcbynumber_r", defaultsrc, number, rpc, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } static int getrpcent_r(struct rpcent *rpc, char *buffer, size_t bufsize, struct rpcent **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( rpc, (void *)nss_lt_all, rpc_marshal_func, rpc_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_rpcent, (void *)nss_lt_all }, #ifdef YP { NSSRC_NIS, nis_rpcent, (void *)nss_lt_all }, #endif #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; ret_errno = 0; *result = NULL; rv = nsdispatch(result, dtab, NSDB_RPC, "getrpcent_r", defaultsrc, rpc, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } /* get** wrappers for get**_r functions implementation */ static void rpcent_endstate(void *p) { if (p == NULL) return; free(((struct rpcent_state *)p)->buffer); free(p); } static int wrap_getrpcbyname_r(union key key, struct rpcent *rpc, char *buffer, size_t bufsize, struct rpcent **res) { return (getrpcbyname_r(key.name, rpc, buffer, bufsize, res)); } static int wrap_getrpcbynumber_r(union key key, struct rpcent *rpc, char *buffer, size_t bufsize, struct rpcent **res) { return (getrpcbynumber_r(key.number, rpc, buffer, bufsize, res)); } static int wrap_getrpcent_r(union key key __unused, struct rpcent *rpc, char *buffer, size_t bufsize, struct rpcent **res) { return (getrpcent_r(rpc, buffer, bufsize, res)); } static struct rpcent * getrpc(int (*fn)(union key, struct rpcent *, char *, size_t, struct rpcent **), union key key) { int rv; struct rpcent *res; struct rpcent_state * st; rv=rpcent_getstate(&st); if (rv != 0) { errno = rv; return NULL; } if (st->buffer == NULL) { st->buffer = malloc(RPCENT_STORAGE_INITIAL); if (st->buffer == NULL) return (NULL); st->bufsize = RPCENT_STORAGE_INITIAL; } do { rv = fn(key, &st->rpc, st->buffer, st->bufsize, &res); if (res == NULL && rv == ERANGE) { free(st->buffer); if ((st->bufsize << 1) > RPCENT_STORAGE_MAX) { st->buffer = NULL; errno = ERANGE; return (NULL); } st->bufsize <<= 1; st->buffer = malloc(st->bufsize); if (st->buffer == NULL) return (NULL); } } while (res == NULL && rv == ERANGE); if (rv != 0) errno = rv; return (res); } struct rpcent * getrpcbyname(const char *name) { union key key; key.name = name; return (getrpc(wrap_getrpcbyname_r, key)); } struct rpcent * getrpcbynumber(int number) { union key key; key.number = number; return (getrpc(wrap_getrpcbynumber_r, key)); } struct rpcent * getrpcent(void) { union key key; key.number = 0; /* not used */ return (getrpc(wrap_getrpcent_r, key)); } void setrpcent(int stayopen) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( rpc, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setrpcent, (void *)SETRPCENT }, #ifdef YP { NSSRC_NIS, nis_setrpcent, (void *)SETRPCENT }, #endif #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)nsdispatch(NULL, dtab, NSDB_RPC, "setrpcent", defaultsrc, stayopen); } void endrpcent(void) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( rpc, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setrpcent, (void *)ENDRPCENT }, #ifdef YP { NSSRC_NIS, nis_setrpcent, (void *)ENDRPCENT }, #endif #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)nsdispatch(NULL, dtab, NSDB_RPC, "endrpcent", defaultsrc); } Index: stable/12/sys/dev/ocs_fc/ocs_device.c =================================================================== --- stable/12/sys/dev/ocs_fc/ocs_device.c (revision 360413) +++ stable/12/sys/dev/ocs_fc/ocs_device.c (revision 360414) @@ -1,1929 +1,1929 @@ /*- * Copyright (c) 2017 Broadcom. All rights reserved. * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries. * * 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /** * @file * Implement remote device state machine for target and initiator. */ /*! @defgroup device_sm Node State Machine: Remote Device States */ #include "ocs.h" #include "ocs_device.h" #include "ocs_fabric.h" #include "ocs_els.h" static void *__ocs_d_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg); static void *__ocs_d_wait_del_node(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg); static void *__ocs_d_wait_del_ini_tgt(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg); static int32_t ocs_process_abts(ocs_io_t *io, fc_header_t *hdr); /** * @ingroup device_sm * @brief Send response to PRLI. * *

Description

* For device nodes, this function sends a PRLI response. * * @param io Pointer to a SCSI IO object. * @param ox_id OX_ID of PRLI * * @return Returns None. */ void ocs_d_send_prli_rsp(ocs_io_t *io, uint16_t ox_id) { ocs_t *ocs = io->ocs; ocs_node_t *node = io->node; /* If the back-end doesn't support the fc-type, we send an LS_RJT */ if (ocs->fc_type != node->fc_type) { node_printf(node, "PRLI rejected by target-server, fc-type not supported\n"); ocs_send_ls_rjt(io, ox_id, FC_REASON_UNABLE_TO_PERFORM, FC_EXPL_REQUEST_NOT_SUPPORTED, 0, NULL, NULL); node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT; ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL); } /* If the back-end doesn't want to talk to this initiator, we send an LS_RJT */ if (node->sport->enable_tgt && (ocs_scsi_validate_initiator(node) == 0)) { node_printf(node, "PRLI rejected by target-server\n"); ocs_send_ls_rjt(io, ox_id, FC_REASON_UNABLE_TO_PERFORM, FC_EXPL_NO_ADDITIONAL, 0, NULL, NULL); node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT; ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL); } else { /*sm: process PRLI payload, send PRLI acc */ ocs_send_prli_acc(io, ox_id, ocs->fc_type, NULL, NULL); /* Immediately go to ready state to avoid window where we're * waiting for the PRLI LS_ACC to complete while holding FCP_CMNDs */ ocs_node_transition(node, __ocs_d_device_ready, NULL); } } /** * @ingroup device_sm * @brief Device node state machine: Initiate node shutdown * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_initiate_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: { int32_t rc = OCS_SCSI_CALL_COMPLETE; /* assume no wait needed */ ocs_scsi_io_alloc_disable(node); /* make necessary delete upcall(s) */ if (node->init && !node->targ) { ocs_log_debug(node->ocs, "[%s] delete (initiator) WWPN %s WWNN %s\n", node->display_name, node->wwpn, node->wwnn); ocs_node_transition(node, __ocs_d_wait_del_node, NULL); if (node->sport->enable_tgt) { rc = ocs_scsi_del_initiator(node, OCS_SCSI_INITIATOR_DELETED); } if (rc == OCS_SCSI_CALL_COMPLETE) { ocs_node_post_event(node, OCS_EVT_NODE_DEL_INI_COMPLETE, NULL); } } else if (node->targ && !node->init) { ocs_log_debug(node->ocs, "[%s] delete (target) WWPN %s WWNN %s\n", node->display_name, node->wwpn, node->wwnn); ocs_node_transition(node, __ocs_d_wait_del_node, NULL); if (node->sport->enable_ini) { rc = ocs_scsi_del_target(node, OCS_SCSI_TARGET_DELETED); } if (rc == OCS_SCSI_CALL_COMPLETE) { ocs_node_post_event(node, OCS_EVT_NODE_DEL_TGT_COMPLETE, NULL); } } else if (node->init && node->targ) { ocs_log_debug(node->ocs, "[%s] delete (initiator+target) WWPN %s WWNN %s\n", node->display_name, node->wwpn, node->wwnn); ocs_node_transition(node, __ocs_d_wait_del_ini_tgt, NULL); if (node->sport->enable_tgt) { rc = ocs_scsi_del_initiator(node, OCS_SCSI_INITIATOR_DELETED); } if (rc == OCS_SCSI_CALL_COMPLETE) { ocs_node_post_event(node, OCS_EVT_NODE_DEL_INI_COMPLETE, NULL); } rc = OCS_SCSI_CALL_COMPLETE; /* assume no wait needed */ if (node->sport->enable_ini) { rc = ocs_scsi_del_target(node, OCS_SCSI_TARGET_DELETED); } if (rc == OCS_SCSI_CALL_COMPLETE) { ocs_node_post_event(node, OCS_EVT_NODE_DEL_TGT_COMPLETE, NULL); } } /* we've initiated the upcalls as needed, now kick off the node * detach to precipitate the aborting of outstanding exchanges * associated with said node * * Beware: if we've made upcall(s), we've already transitioned * to a new state by the time we execute this. * TODO: consider doing this before the upcalls... */ if (node->attached) { /* issue hw node free; don't care if succeeds right away * or sometime later, will check node->attached later in * shutdown process */ rc = ocs_hw_node_detach(&ocs->hw, &node->rnode); if (node->rnode.free_group) { ocs_remote_node_group_free(node->node_group); node->node_group = NULL; node->rnode.free_group = FALSE; } if (rc != OCS_HW_RTN_SUCCESS && rc != OCS_HW_RTN_SUCCESS_SYNC) { node_printf(node, "Failed freeing HW node, rc=%d\n", rc); } } /* if neither initiator nor target, proceed to cleanup */ if (!node->init && !node->targ){ /* * node has either been detached or is in the process * of being detached, call common node's initiate * cleanup function. */ ocs_node_initiate_cleanup(node); } break; } case OCS_EVT_ALL_CHILD_NODES_FREE: /* Ignore, this can happen if an ELS is aborted, * while in a delay/retry state */ break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Common device event handler. * *

Description

* For device nodes, this event handler manages default and common events. * * @param funcname Function name text. * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ static void * __ocs_d_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { ocs_node_t *node = NULL; ocs_t *ocs = NULL; ocs_assert(ctx, NULL); node = ctx->app; ocs_assert(node, NULL); ocs = node->ocs; ocs_assert(ocs, NULL); switch(evt) { /* Handle shutdown events */ case OCS_EVT_SHUTDOWN: ocs_log_debug(ocs, "[%s] %-20s %-20s\n", node->display_name, funcname, ocs_sm_event_name(evt)); node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT; ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL); break; case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO: ocs_log_debug(ocs, "[%s] %-20s %-20s\n", node->display_name, funcname, ocs_sm_event_name(evt)); node->shutdown_reason = OCS_NODE_SHUTDOWN_EXPLICIT_LOGO; ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL); break; case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO: ocs_log_debug(ocs, "[%s] %-20s %-20s\n", node->display_name, funcname, ocs_sm_event_name(evt)); node->shutdown_reason = OCS_NODE_SHUTDOWN_IMPLICIT_LOGO; ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL); break; default: /* call default event handler common to all nodes */ __ocs_node_common(funcname, ctx, evt, arg); break; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Wait for a domain-attach completion in loop topology. * *

Description

* State waits for a domain-attached completion while in loop topology. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_loop(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: ocs_node_hold_frames(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_DOMAIN_ATTACH_OK: { /* send PLOGI automatically if initiator */ ocs_node_init_device(node, TRUE); break; } default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief state: wait for node resume event * * State is entered when a node is in I+T mode and sends a delete initiator/target * call to the target-server/initiator-client and needs to wait for that work to complete. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg per event optional argument * * @return returns NULL */ void * __ocs_d_wait_del_ini_tgt(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: ocs_node_hold_frames(node); /* Fall through */ case OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY: case OCS_EVT_ALL_CHILD_NODES_FREE: /* These are expected events. */ break; case OCS_EVT_NODE_DEL_INI_COMPLETE: case OCS_EVT_NODE_DEL_TGT_COMPLETE: ocs_node_transition(node, __ocs_d_wait_del_node, NULL); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_SRRS_ELS_REQ_FAIL: /* Can happen as ELS IO IO's complete */ ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; break; /* ignore shutdown events as we're already in shutdown path */ case OCS_EVT_SHUTDOWN: /* have default shutdown event take precedence */ node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT; /* fall through */ case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO: case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO: node_printf(node, "%s received\n", ocs_sm_event_name(evt)); break; case OCS_EVT_DOMAIN_ATTACH_OK: /* don't care about domain_attach_ok */ break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief state: Wait for node resume event. * * State is entered when a node sends a delete initiator/target call to the * target-server/initiator-client and needs to wait for that work to complete. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_del_node(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: ocs_node_hold_frames(node); /* Fall through */ case OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY: case OCS_EVT_ALL_CHILD_NODES_FREE: /* These are expected events. */ break; case OCS_EVT_NODE_DEL_INI_COMPLETE: case OCS_EVT_NODE_DEL_TGT_COMPLETE: /* * node has either been detached or is in the process of being detached, * call common node's initiate cleanup function */ ocs_node_initiate_cleanup(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_SRRS_ELS_REQ_FAIL: /* Can happen as ELS IO IO's complete */ ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; break; /* ignore shutdown events as we're already in shutdown path */ case OCS_EVT_SHUTDOWN: /* have default shutdown event take precedence */ node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT; /* fall through */ case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO: case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO: node_printf(node, "%s received\n", ocs_sm_event_name(evt)); break; case OCS_EVT_DOMAIN_ATTACH_OK: /* don't care about domain_attach_ok */ break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @brief Save the OX_ID for sending LS_ACC sometime later. * *

Description

* When deferring the response to an ELS request, the OX_ID of the request * is saved using this function. * * @param io Pointer to a SCSI IO object. * @param hdr Pointer to the FC header. * @param ls Defines the type of ELS to send: LS_ACC, LS_ACC for PLOGI; * or LSS_ACC for PRLI. * * @return None. */ void ocs_send_ls_acc_after_attach(ocs_io_t *io, fc_header_t *hdr, ocs_node_send_ls_acc_e ls) { ocs_node_t *node = io->node; uint16_t ox_id = ocs_be16toh(hdr->ox_id); ocs_assert(node->send_ls_acc == OCS_NODE_SEND_LS_ACC_NONE); node->ls_acc_oxid = ox_id; node->send_ls_acc = ls; node->ls_acc_io = io; node->ls_acc_did = fc_be24toh(hdr->d_id); } /** * @brief Process the PRLI payload. * *

Description

* The PRLI payload is processed; the initiator/target capabilities of the * remote node are extracted and saved in the node object. * * @param node Pointer to the node object. * @param prli Pointer to the PRLI payload. * * @return None. */ void ocs_process_prli_payload(ocs_node_t *node, fc_prli_payload_t *prli) { node->init = (ocs_be16toh(prli->service_params) & FC_PRLI_INITIATOR_FUNCTION) != 0; node->targ = (ocs_be16toh(prli->service_params) & FC_PRLI_TARGET_FUNCTION) != 0; node->fcp2device = (ocs_be16toh(prli->service_params) & FC_PRLI_RETRY) != 0; node->fc_type = prli->type; } /** * @brief Process the ABTS. * *

Description

* Common code to process a received ABTS. If an active IO can be found * that matches the OX_ID of the ABTS request, a call is made to the * backend. Otherwise, a BA_ACC is returned to the initiator. * * @param io Pointer to a SCSI IO object. * @param hdr Pointer to the FC header. * * @return Returns 0 on success, or a negative error value on failure. */ static int32_t ocs_process_abts(ocs_io_t *io, fc_header_t *hdr) { ocs_node_t *node = io->node; ocs_t *ocs = node->ocs; uint16_t ox_id = ocs_be16toh(hdr->ox_id); uint16_t rx_id = ocs_be16toh(hdr->rx_id); ocs_io_t *abortio; abortio = ocs_io_find_tgt_io(ocs, node, ox_id, rx_id); /* If an IO was found, attempt to take a reference on it */ if (abortio != NULL && (ocs_ref_get_unless_zero(&abortio->ref) != 0)) { /* Got a reference on the IO. Hold it until backend is notified below */ node_printf(node, "Abort request: ox_id [%04x] rx_id [%04x]\n", ox_id, rx_id); /* * Save the ox_id for the ABTS as the init_task_tag in our manufactured * TMF IO object */ io->display_name = "abts"; io->init_task_tag = ox_id; /* don't set tgt_task_tag, don't want to confuse with XRI */ /* * Save the rx_id from the ABTS as it is needed for the BLS response, * regardless of the IO context's rx_id */ io->abort_rx_id = rx_id; /* Call target server command abort */ io->tmf_cmd = OCS_SCSI_TMF_ABORT_TASK; ocs_scsi_recv_tmf(io, abortio->tgt_io.lun, OCS_SCSI_TMF_ABORT_TASK, abortio, 0); /* * Backend will have taken an additional reference on the IO if needed; * done with current reference. */ ocs_ref_put(&abortio->ref); /* ocs_ref_get(): same function */ } else { /* * Either IO was not found or it has been freed between finding it * and attempting to get the reference, */ node_printf(node, "Abort request: ox_id [%04x], IO not found (exists=%d)\n", ox_id, (abortio != NULL)); /* Send a BA_ACC */ ocs_bls_send_acc_hdr(io, hdr); } return 0; } /** * @ingroup device_sm * @brief Device node state machine: Wait for the PLOGI accept to complete. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_plogi_acc_cmpl(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: ocs_node_hold_frames(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_SRRS_ELS_CMPL_FAIL: ocs_assert(node->els_cmpl_cnt, NULL); node->els_cmpl_cnt--; node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT; ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL); break; case OCS_EVT_SRRS_ELS_CMPL_OK: /* PLOGI ACC completions */ ocs_assert(node->els_cmpl_cnt, NULL); node->els_cmpl_cnt--; ocs_node_transition(node, __ocs_d_port_logged_in, NULL); break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Wait for the LOGO response. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_logo_rsp(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: /* TODO: may want to remove this; * if we'll want to know about PLOGI */ ocs_node_hold_frames(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_SRRS_ELS_REQ_OK: case OCS_EVT_SRRS_ELS_REQ_RJT: case OCS_EVT_SRRS_ELS_REQ_FAIL: /* LOGO response received, sent shutdown */ if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_LOGO, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; node_printf(node, "LOGO sent (evt=%s), shutdown node\n", ocs_sm_event_name(evt)); /* sm: post explicit logout */ ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); break; /* TODO: PLOGI: abort LOGO and process PLOGI? (SHUTDOWN_EXPLICIT/IMPLICIT_LOGO?) */ default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Wait for the PRLO response. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_prlo_rsp(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: ocs_node_hold_frames(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_SRRS_ELS_REQ_OK: case OCS_EVT_SRRS_ELS_REQ_RJT: case OCS_EVT_SRRS_ELS_REQ_FAIL: if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PRLO, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; node_printf(node, "PRLO sent (evt=%s)\n", ocs_sm_event_name(evt)); ocs_node_transition(node, __ocs_d_port_logged_in, NULL); break; default: __ocs_node_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @brief Initialize device node. * * Initialize device node. If a node is an initiator, then send a PLOGI and transition * to __ocs_d_wait_plogi_rsp, otherwise transition to __ocs_d_init. * * @param node Pointer to the node object. * @param send_plogi Boolean indicating to send PLOGI command or not. * * @return none */ void ocs_node_init_device(ocs_node_t *node, int send_plogi) { node->send_plogi = send_plogi; if ((node->ocs->nodedb_mask & OCS_NODEDB_PAUSE_NEW_NODES) && !FC_ADDR_IS_DOMAIN_CTRL(node->rnode.fc_id)) { node->nodedb_state = __ocs_d_init; ocs_node_transition(node, __ocs_node_paused, NULL); } else { ocs_node_transition(node, __ocs_d_init, NULL); } } /** * @ingroup device_sm * @brief Device node state machine: Initial node state for an initiator or a target. * *

Description

* This state is entered when a node is instantiated, either having been * discovered from a name services query, or having received a PLOGI/FLOGI. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * - OCS_EVT_ENTER: (uint8_t *) - 1 to send a PLOGI on * entry (initiator-only); 0 indicates a PLOGI is * not sent on entry (initiator-only). Not applicable for a target. * * @return Returns NULL. */ void * __ocs_d_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { int32_t rc; ocs_node_cb_t *cbdata = arg; std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: /* check if we need to send PLOGI */ if (node->send_plogi) { /* only send if we have initiator capability, and domain is attached */ if (node->sport->enable_ini && node->sport->domain->attached) { ocs_send_plogi(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL); ocs_node_transition(node, __ocs_d_wait_plogi_rsp, NULL); } else { node_printf(node, "not sending plogi sport.ini=%d, domain attached=%d\n", node->sport->enable_ini, node->sport->domain->attached); } } break; case OCS_EVT_PLOGI_RCVD: { /* T, or I+T */ fc_header_t *hdr = cbdata->header->dma.virt; uint32_t d_id = fc_be24toh(hdr->d_id); ocs_node_save_sparms(node, cbdata->payload->dma.virt); ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PLOGI); /* domain already attached */ if (node->sport->domain->attached) { rc = ocs_node_attach(node); ocs_node_transition(node, __ocs_d_wait_node_attach, NULL); if (rc == OCS_HW_RTN_SUCCESS_SYNC) { ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL); } break; } /* domain not attached; several possibilities: */ switch (node->sport->topology) { case OCS_SPORT_TOPOLOGY_P2P: /* we're not attached and sport is p2p, need to attach */ ocs_domain_attach(node->sport->domain, d_id); ocs_node_transition(node, __ocs_d_wait_domain_attach, NULL); break; case OCS_SPORT_TOPOLOGY_FABRIC: /* we're not attached and sport is fabric, domain attach should have * already been requested as part of the fabric state machine, wait for it */ ocs_node_transition(node, __ocs_d_wait_domain_attach, NULL); break; case OCS_SPORT_TOPOLOGY_UNKNOWN: /* Two possibilities: * 1. received a PLOGI before our FLOGI has completed (possible since * completion comes in on another CQ), thus we don't know what we're * connected to yet; transition to a state to wait for the fabric * node to tell us; * 2. PLOGI received before link went down and we haven't performed * domain attach yet. * Note: we cannot distinguish between 1. and 2. so have to assume PLOGI * was received after link back up. */ node_printf(node, "received PLOGI, with unknown topology did=0x%x\n", d_id); ocs_node_transition(node, __ocs_d_wait_topology_notify, NULL); break; default: node_printf(node, "received PLOGI, with unexpectd topology %d\n", node->sport->topology); ocs_assert(FALSE, NULL); break; } break; } case OCS_EVT_FDISC_RCVD: { __ocs_d_common(__func__, ctx, evt, arg); break; } case OCS_EVT_FLOGI_RCVD: { fc_header_t *hdr = cbdata->header->dma.virt; /* this better be coming from an NPort */ ocs_assert(ocs_rnode_is_nport(cbdata->payload->dma.virt), NULL); /* sm: save sparams, send FLOGI acc */ ocs_domain_save_sparms(node->sport->domain, cbdata->payload->dma.virt); /* send FC LS_ACC response, override s_id */ ocs_fabric_set_topology(node, OCS_SPORT_TOPOLOGY_P2P); ocs_send_flogi_p2p_acc(cbdata->io, ocs_be16toh(hdr->ox_id), fc_be24toh(hdr->d_id), NULL, NULL); if (ocs_p2p_setup(node->sport)) { node_printf(node, "p2p setup failed, shutting down node\n"); ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL); } else { ocs_node_transition(node, __ocs_p2p_wait_flogi_acc_cmpl, NULL); } break; } case OCS_EVT_LOGO_RCVD: { fc_header_t *hdr = cbdata->header->dma.virt; if (!node->sport->domain->attached) { /* most likely a frame left over from before a link down; drop and * shut node down w/ "explicit logout" so pending frames are processed */ node_printf(node, "%s domain not attached, dropping\n", ocs_sm_event_name(evt)); ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); break; } ocs_send_logo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL); ocs_node_transition(node, __ocs_d_wait_logo_acc_cmpl, NULL); break; } case OCS_EVT_PRLI_RCVD: case OCS_EVT_PRLO_RCVD: case OCS_EVT_PDISC_RCVD: case OCS_EVT_ADISC_RCVD: case OCS_EVT_RSCN_RCVD: { fc_header_t *hdr = cbdata->header->dma.virt; if (!node->sport->domain->attached) { /* most likely a frame left over from before a link down; drop and * shut node down w/ "explicit logout" so pending frames are processed */ node_printf(node, "%s domain not attached, dropping\n", ocs_sm_event_name(evt)); ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); break; } node_printf(node, "%s received, sending reject\n", ocs_sm_event_name(evt)); ocs_send_ls_rjt(cbdata->io, ocs_be16toh(hdr->ox_id), FC_REASON_UNABLE_TO_PERFORM, FC_EXPL_NPORT_LOGIN_REQUIRED, 0, NULL, NULL); break; } case OCS_EVT_FCP_CMD_RCVD: { /* note: problem, we're now expecting an ELS REQ completion * from both the LOGO and PLOGI */ if (!node->sport->domain->attached) { /* most likely a frame left over from before a link down; drop and * shut node down w/ "explicit logout" so pending frames are processed */ node_printf(node, "%s domain not attached, dropping\n", ocs_sm_event_name(evt)); ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); break; } /* Send LOGO */ node_printf(node, "FCP_CMND received, send LOGO\n"); if (ocs_send_logo(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, 0, NULL, NULL) == NULL) { /* failed to send LOGO, go ahead and cleanup node anyways */ node_printf(node, "Failed to send LOGO\n"); ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); } else { /* sent LOGO, wait for response */ ocs_node_transition(node, __ocs_d_wait_logo_rsp, NULL); } break; } case OCS_EVT_DOMAIN_ATTACH_OK: /* don't care about domain_attach_ok */ break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Wait on a response for a sent PLOGI. * *

Description

* State is entered when an initiator-capable node has sent * a PLOGI and is waiting for a response. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_plogi_rsp(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { int32_t rc; ocs_node_cb_t *cbdata = arg; std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_PLOGI_RCVD: { /* T, or I+T */ /* received PLOGI with svc parms, go ahead and attach node * when PLOGI that was sent ultimately completes, it'll be a no-op */ /* TODO: there is an outstanding PLOGI sent, can we set a flag * to indicate that we don't want to retry it if it times out? */ ocs_node_save_sparms(node, cbdata->payload->dma.virt); ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PLOGI); /* sm: domain->attached / ocs_node_attach */ rc = ocs_node_attach(node); ocs_node_transition(node, __ocs_d_wait_node_attach, NULL); if (rc == OCS_HW_RTN_SUCCESS_SYNC) { ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL); } break; } case OCS_EVT_PRLI_RCVD: /* I, or I+T */ /* sent PLOGI and before completion was seen, received the * PRLI from the remote node (WCQEs and RCQEs come in on * different queues and order of processing cannot be assumed) * Save OXID so PRLI can be sent after the attach and continue * to wait for PLOGI response */ ocs_process_prli_payload(node, cbdata->payload->dma.virt); if (ocs->fc_type == node->fc_type) { ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PRLI); ocs_node_transition(node, __ocs_d_wait_plogi_rsp_recvd_prli, NULL); } else { /* TODO this need to be looked at. What do we do here ? */ } break; /* TODO this need to be looked at. we could very well be logged in */ case OCS_EVT_LOGO_RCVD: /* why don't we do a shutdown here?? */ case OCS_EVT_PRLO_RCVD: case OCS_EVT_PDISC_RCVD: case OCS_EVT_FDISC_RCVD: case OCS_EVT_ADISC_RCVD: case OCS_EVT_RSCN_RCVD: case OCS_EVT_SCR_RCVD: { fc_header_t *hdr = cbdata->header->dma.virt; node_printf(node, "%s received, sending reject\n", ocs_sm_event_name(evt)); ocs_send_ls_rjt(cbdata->io, ocs_be16toh(hdr->ox_id), FC_REASON_UNABLE_TO_PERFORM, FC_EXPL_NPORT_LOGIN_REQUIRED, 0, NULL, NULL); break; } case OCS_EVT_SRRS_ELS_REQ_OK: /* PLOGI response received */ /* Completion from PLOGI sent */ if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; /* sm: save sparams, ocs_node_attach */ ocs_node_save_sparms(node, cbdata->els->els_rsp.virt); ocs_display_sparams(node->display_name, "plogi rcvd resp", 0, NULL, ((uint8_t*)cbdata->els->els_rsp.virt) + 4); rc = ocs_node_attach(node); ocs_node_transition(node, __ocs_d_wait_node_attach, NULL); if (rc == OCS_HW_RTN_SUCCESS_SYNC) { ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL); } break; case OCS_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */ /* PLOGI failed, shutdown the node */ if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL); break; case OCS_EVT_SRRS_ELS_REQ_RJT: /* Our PLOGI was rejected, this is ok in some cases */ if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; break; case OCS_EVT_FCP_CMD_RCVD: { /* not logged in yet and outstanding PLOGI so don't send LOGO, * just drop */ node_printf(node, "FCP_CMND received, drop\n"); break; } default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Waiting on a response for a * sent PLOGI. * *

Description

* State is entered when an initiator-capable node has sent * a PLOGI and is waiting for a response. Before receiving the * response, a PRLI was received, implying that the PLOGI was * successful. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_plogi_rsp_recvd_prli(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { int32_t rc; ocs_node_cb_t *cbdata = arg; std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: /* * Since we've received a PRLI, we have a port login and will * just need to wait for the PLOGI response to do the node * attach and then we can send the LS_ACC for the PRLI. If, * during this time, we receive FCP_CMNDs (which is possible * since we've already sent a PRLI and our peer may have accepted). * At this time, we are not waiting on any other unsolicited * frames to continue with the login process. Thus, it will not * hurt to hold frames here. */ ocs_node_hold_frames(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_SRRS_ELS_REQ_OK: /* PLOGI response received */ /* Completion from PLOGI sent */ if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; /* sm: save sparams, ocs_node_attach */ ocs_node_save_sparms(node, cbdata->els->els_rsp.virt); ocs_display_sparams(node->display_name, "plogi rcvd resp", 0, NULL, ((uint8_t*)cbdata->els->els_rsp.virt) + 4); rc = ocs_node_attach(node); ocs_node_transition(node, __ocs_d_wait_node_attach, NULL); if (rc == OCS_HW_RTN_SUCCESS_SYNC) { ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL); } break; case OCS_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */ case OCS_EVT_SRRS_ELS_REQ_RJT: /* PLOGI failed, shutdown the node */ if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PLOGI, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL); break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Wait for a domain attach. * *

Description

* Waits for a domain-attach complete ok event. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_domain_attach(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { int32_t rc; std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: ocs_node_hold_frames(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_DOMAIN_ATTACH_OK: ocs_assert(node->sport->domain->attached, NULL); /* sm: ocs_node_attach */ rc = ocs_node_attach(node); ocs_node_transition(node, __ocs_d_wait_node_attach, NULL); if (rc == OCS_HW_RTN_SUCCESS_SYNC) { ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL); } break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Wait for topology * notification * *

Description

* Waits for topology notification from fabric node, then * attaches domain and node. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_topology_notify(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { int32_t rc; std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: ocs_node_hold_frames(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_SPORT_TOPOLOGY_NOTIFY: { - ocs_sport_topology_e topology = (ocs_sport_topology_e)arg; + ocs_sport_topology_e topology = (ocs_sport_topology_e)(uintptr_t)arg; ocs_assert(!node->sport->domain->attached, NULL); ocs_assert(node->send_ls_acc == OCS_NODE_SEND_LS_ACC_PLOGI, NULL); node_printf(node, "topology notification, topology=%d\n", topology); /* At the time the PLOGI was received, the topology was unknown, * so we didn't know which node would perform the domain attach: * 1. The node from which the PLOGI was sent (p2p) or * 2. The node to which the FLOGI was sent (fabric). */ if (topology == OCS_SPORT_TOPOLOGY_P2P) { /* if this is p2p, need to attach to the domain using the * d_id from the PLOGI received */ ocs_domain_attach(node->sport->domain, node->ls_acc_did); } /* else, if this is fabric, the domain attach should be performed * by the fabric node (node sending FLOGI); just wait for attach * to complete */ ocs_node_transition(node, __ocs_d_wait_domain_attach, NULL); break; } case OCS_EVT_DOMAIN_ATTACH_OK: ocs_assert(node->sport->domain->attached, NULL); node_printf(node, "domain attach ok\n"); /*sm: ocs_node_attach */ rc = ocs_node_attach(node); ocs_node_transition(node, __ocs_d_wait_node_attach, NULL); if (rc == OCS_HW_RTN_SUCCESS_SYNC) { ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL); } break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Wait for a node attach when found by a remote node. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_node_attach(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: ocs_node_hold_frames(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_NODE_ATTACH_OK: node->attached = TRUE; switch (node->send_ls_acc) { case OCS_NODE_SEND_LS_ACC_PLOGI: { /* sm: send_plogi_acc is set / send PLOGI acc */ /* Normal case for T, or I+T */ ocs_send_plogi_acc(node->ls_acc_io, node->ls_acc_oxid, NULL, NULL); ocs_node_transition(node, __ocs_d_wait_plogi_acc_cmpl, NULL); node->send_ls_acc = OCS_NODE_SEND_LS_ACC_NONE; node->ls_acc_io = NULL; break; } case OCS_NODE_SEND_LS_ACC_PRLI: { ocs_d_send_prli_rsp(node->ls_acc_io, node->ls_acc_oxid); node->send_ls_acc = OCS_NODE_SEND_LS_ACC_NONE; node->ls_acc_io = NULL; break; } case OCS_NODE_SEND_LS_ACC_NONE: default: /* Normal case for I */ /* sm: send_plogi_acc is not set / send PLOGI acc */ ocs_node_transition(node, __ocs_d_port_logged_in, NULL); break; } break; case OCS_EVT_NODE_ATTACH_FAIL: /* node attach failed, shutdown the node */ node->attached = FALSE; node_printf(node, "node attach failed\n"); node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT; ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL); break; /* Handle shutdown events */ case OCS_EVT_SHUTDOWN: node_printf(node, "%s received\n", ocs_sm_event_name(evt)); node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT; ocs_node_transition(node, __ocs_d_wait_attach_evt_shutdown, NULL); break; case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO: node_printf(node, "%s received\n", ocs_sm_event_name(evt)); node->shutdown_reason = OCS_NODE_SHUTDOWN_EXPLICIT_LOGO; ocs_node_transition(node, __ocs_d_wait_attach_evt_shutdown, NULL); break; case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO: node_printf(node, "%s received\n", ocs_sm_event_name(evt)); node->shutdown_reason = OCS_NODE_SHUTDOWN_IMPLICIT_LOGO; ocs_node_transition(node, __ocs_d_wait_attach_evt_shutdown, NULL); break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Wait for a node/domain * attach then shutdown node. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_attach_evt_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: ocs_node_hold_frames(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; /* wait for any of these attach events and then shutdown */ case OCS_EVT_NODE_ATTACH_OK: node->attached = TRUE; node_printf(node, "Attach evt=%s, proceed to shutdown\n", ocs_sm_event_name(evt)); ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL); break; case OCS_EVT_NODE_ATTACH_FAIL: /* node attach failed, shutdown the node */ node->attached = FALSE; node_printf(node, "Attach evt=%s, proceed to shutdown\n", ocs_sm_event_name(evt)); ocs_node_transition(node, __ocs_d_initiate_shutdown, NULL); break; /* ignore shutdown events as we're already in shutdown path */ case OCS_EVT_SHUTDOWN: /* have default shutdown event take precedence */ node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT; /* fall through */ case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO: case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO: node_printf(node, "%s received\n", ocs_sm_event_name(evt)); break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Port is logged in. * *

Description

* This state is entered when a remote port has completed port login (PLOGI). * * @param ctx Remote node state machine context. * @param evt Event to process * @param arg Per event optional argument * * @return Returns NULL. */ void * __ocs_d_port_logged_in(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { ocs_node_cb_t *cbdata = arg; std_node_state_decl(); node_sm_trace(); /* TODO: I+T: what if PLOGI response not yet received ? */ switch(evt) { case OCS_EVT_ENTER: /* Normal case for I or I+T */ if (node->sport->enable_ini && !FC_ADDR_IS_DOMAIN_CTRL(node->rnode.fc_id) && !node->sent_prli) { /* sm: if enable_ini / send PRLI */ ocs_send_prli(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL); node->sent_prli = TRUE; /* can now expect ELS_REQ_OK/FAIL/RJT */ } break; case OCS_EVT_FCP_CMD_RCVD: { /* For target functionality send PRLO and drop the CMD frame. */ if (node->sport->enable_tgt) { if (ocs_send_prlo(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL)) { ocs_node_transition(node, __ocs_d_wait_prlo_rsp, NULL); } } break; } case OCS_EVT_PRLI_RCVD: { fc_header_t *hdr = cbdata->header->dma.virt; /* Normal for T or I+T */ ocs_process_prli_payload(node, cbdata->payload->dma.virt); ocs_d_send_prli_rsp(cbdata->io, ocs_be16toh(hdr->ox_id)); break; } case OCS_EVT_SRRS_ELS_REQ_OK: { /* PRLI response */ /* Normal case for I or I+T */ if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PRLI, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; /* sm: process PRLI payload */ ocs_process_prli_payload(node, cbdata->els->els_rsp.virt); ocs_node_transition(node, __ocs_d_device_ready, NULL); break; } case OCS_EVT_SRRS_ELS_REQ_FAIL: { /* PRLI response failed */ /* I, I+T, assume some link failure, shutdown node */ if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PRLI, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL); break; } case OCS_EVT_SRRS_ELS_REQ_RJT: {/* PRLI rejected by remote */ /* Normal for I, I+T (connected to an I) */ /* Node doesn't want to be a target, stay here and wait for a PRLI from the remote node * if it really wants to connect to us as target */ if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_PRLI, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; break; } case OCS_EVT_SRRS_ELS_CMPL_OK: { /* Normal T, I+T, target-server rejected the process login */ /* This would be received only in the case where we sent LS_RJT for the PRLI, so * do nothing. (note: as T only we could shutdown the node) */ ocs_assert(node->els_cmpl_cnt, NULL); node->els_cmpl_cnt--; break; } case OCS_EVT_PLOGI_RCVD: { /* sm: save sparams, set send_plogi_acc, post implicit logout * Save plogi parameters */ ocs_node_save_sparms(node, cbdata->payload->dma.virt); ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PLOGI); /* Restart node attach with new service parameters, and send ACC */ ocs_node_post_event(node, OCS_EVT_SHUTDOWN_IMPLICIT_LOGO, NULL); break; } case OCS_EVT_LOGO_RCVD: { /* I, T, I+T */ fc_header_t *hdr = cbdata->header->dma.virt; node_printf(node, "%s received attached=%d\n", ocs_sm_event_name(evt), node->attached); ocs_send_logo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL); ocs_node_transition(node, __ocs_d_wait_logo_acc_cmpl, NULL); break; } default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Wait for a LOGO accept. * *

Description

* Waits for a LOGO accept completion. * * @param ctx Remote node state machine context. * @param evt Event to process * @param arg Per event optional argument * * @return Returns NULL. */ void * __ocs_d_wait_logo_acc_cmpl(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: ocs_node_hold_frames(node); break; case OCS_EVT_EXIT: ocs_node_accept_frames(node); break; case OCS_EVT_SRRS_ELS_CMPL_OK: case OCS_EVT_SRRS_ELS_CMPL_FAIL: /* sm: / post explicit logout */ ocs_assert(node->els_cmpl_cnt, NULL); node->els_cmpl_cnt--; ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Device is ready. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_device_ready(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { ocs_node_cb_t *cbdata = arg; std_node_state_decl(); if (evt != OCS_EVT_FCP_CMD_RCVD) { node_sm_trace(); } switch(evt) { case OCS_EVT_ENTER: node->fcp_enabled = TRUE; if (node->init) { device_printf(ocs->dev, "[%s] found (initiator) WWPN %s WWNN %s\n", node->display_name, node->wwpn, node->wwnn); if (node->sport->enable_tgt) ocs_scsi_new_initiator(node); } if (node->targ) { device_printf(ocs->dev, "[%s] found (target) WWPN %s WWNN %s\n", node->display_name, node->wwpn, node->wwnn); if (node->sport->enable_ini) ocs_scsi_new_target(node); } break; case OCS_EVT_EXIT: node->fcp_enabled = FALSE; break; case OCS_EVT_PLOGI_RCVD: { /* sm: save sparams, set send_plogi_acc, post implicit logout * Save plogi parameters */ ocs_node_save_sparms(node, cbdata->payload->dma.virt); ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PLOGI); /* Restart node attach with new service parameters, and send ACC */ ocs_node_post_event(node, OCS_EVT_SHUTDOWN_IMPLICIT_LOGO, NULL); break; } case OCS_EVT_PDISC_RCVD: { fc_header_t *hdr = cbdata->header->dma.virt; ocs_send_plogi_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL); break; } case OCS_EVT_PRLI_RCVD: { /* T, I+T: remote initiator is slow to get started */ fc_header_t *hdr = cbdata->header->dma.virt; ocs_process_prli_payload(node, cbdata->payload->dma.virt); /* sm: send PRLI acc/reject */ if (ocs->fc_type == node->fc_type) ocs_send_prli_acc(cbdata->io, ocs_be16toh(hdr->ox_id), ocs->fc_type, NULL, NULL); else ocs_send_ls_rjt(cbdata->io, ocs_be16toh(hdr->ox_id), FC_REASON_UNABLE_TO_PERFORM, FC_EXPL_REQUEST_NOT_SUPPORTED, 0, NULL, NULL); break; } case OCS_EVT_PRLO_RCVD: { fc_header_t *hdr = cbdata->header->dma.virt; fc_prlo_payload_t *prlo = cbdata->payload->dma.virt; /* sm: send PRLO acc/reject */ if (ocs->fc_type == prlo->type) ocs_send_prlo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), ocs->fc_type, NULL, NULL); else ocs_send_ls_rjt(cbdata->io, ocs_be16toh(hdr->ox_id), FC_REASON_UNABLE_TO_PERFORM, FC_EXPL_REQUEST_NOT_SUPPORTED, 0, NULL, NULL); /*TODO: need implicit logout */ break; } case OCS_EVT_LOGO_RCVD: { fc_header_t *hdr = cbdata->header->dma.virt; node_printf(node, "%s received attached=%d\n", ocs_sm_event_name(evt), node->attached); ocs_send_logo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL); ocs_node_transition(node, __ocs_d_wait_logo_acc_cmpl, NULL); break; } case OCS_EVT_ADISC_RCVD: { fc_header_t *hdr = cbdata->header->dma.virt; ocs_send_adisc_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL); break; } case OCS_EVT_RRQ_RCVD: { fc_header_t *hdr = cbdata->header->dma.virt; /* Send LS_ACC */ ocs_send_ls_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL); break; } case OCS_EVT_ABTS_RCVD: ocs_process_abts(cbdata->io, cbdata->header->dma.virt); break; case OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY: break; case OCS_EVT_NODE_REFOUND: break; case OCS_EVT_NODE_MISSING: if (node->sport->enable_rscn) { ocs_node_transition(node, __ocs_d_device_gone, NULL); } break; case OCS_EVT_SRRS_ELS_CMPL_OK: /* T, or I+T, PRLI accept completed ok */ ocs_assert(node->els_cmpl_cnt, NULL); node->els_cmpl_cnt--; break; case OCS_EVT_SRRS_ELS_CMPL_FAIL: /* T, or I+T, PRLI accept failed to complete */ ocs_assert(node->els_cmpl_cnt, NULL); node->els_cmpl_cnt--; node_printf(node, "Failed to send PRLI LS_ACC\n"); break; default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Node is gone (absent from GID_PT). * *

Description

* State entered when a node is detected as being gone (absent from GID_PT). * * @param ctx Remote node state machine context. * @param evt Event to process * @param arg Per event optional argument * * @return Returns NULL. */ void * __ocs_d_device_gone(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { int32_t rc = OCS_SCSI_CALL_COMPLETE; int32_t rc_2 = OCS_SCSI_CALL_COMPLETE; ocs_node_cb_t *cbdata = arg; std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_ENTER: { const char *labels[] = {"none", "initiator", "target", "initiator+target"}; device_printf(ocs->dev, "[%s] missing (%s) WWPN %s WWNN %s\n", node->display_name, labels[(node->targ << 1) | (node->init)], node->wwpn, node->wwnn); switch(ocs_node_get_enable(node)) { case OCS_NODE_ENABLE_T_TO_T: case OCS_NODE_ENABLE_I_TO_T: case OCS_NODE_ENABLE_IT_TO_T: rc = ocs_scsi_del_target(node, OCS_SCSI_TARGET_MISSING); break; case OCS_NODE_ENABLE_T_TO_I: case OCS_NODE_ENABLE_I_TO_I: case OCS_NODE_ENABLE_IT_TO_I: rc = ocs_scsi_del_initiator(node, OCS_SCSI_INITIATOR_MISSING); break; case OCS_NODE_ENABLE_T_TO_IT: rc = ocs_scsi_del_initiator(node, OCS_SCSI_INITIATOR_MISSING); break; case OCS_NODE_ENABLE_I_TO_IT: rc = ocs_scsi_del_target(node, OCS_SCSI_TARGET_MISSING); break; case OCS_NODE_ENABLE_IT_TO_IT: rc = ocs_scsi_del_initiator(node, OCS_SCSI_INITIATOR_MISSING); rc_2 = ocs_scsi_del_target(node, OCS_SCSI_TARGET_MISSING); break; default: rc = OCS_SCSI_CALL_COMPLETE; break; } if ((rc == OCS_SCSI_CALL_COMPLETE) && (rc_2 == OCS_SCSI_CALL_COMPLETE)) { ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL); } break; } case OCS_EVT_NODE_REFOUND: /* two approaches, reauthenticate with PLOGI/PRLI, or ADISC */ /* reauthenticate with PLOGI/PRLI */ /* ocs_node_transition(node, __ocs_d_discovered, NULL); */ /* reauthenticate with ADISC * sm: send ADISC */ ocs_send_adisc(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, OCS_FC_ELS_DEFAULT_RETRIES, NULL, NULL); ocs_node_transition(node, __ocs_d_wait_adisc_rsp, NULL); break; case OCS_EVT_PLOGI_RCVD: { /* sm: save sparams, set send_plogi_acc, post implicit logout * Save plogi parameters */ ocs_node_save_sparms(node, cbdata->payload->dma.virt); ocs_send_ls_acc_after_attach(cbdata->io, cbdata->header->dma.virt, OCS_NODE_SEND_LS_ACC_PLOGI); /* Restart node attach with new service parameters, and send ACC */ ocs_node_post_event(node, OCS_EVT_SHUTDOWN_IMPLICIT_LOGO, NULL); break; } case OCS_EVT_FCP_CMD_RCVD: { /* most likely a stale frame (received prior to link down), if attempt * to send LOGO, will probably timeout and eat up 20s; thus, drop FCP_CMND */ node_printf(node, "FCP_CMND received, drop\n"); break; } case OCS_EVT_LOGO_RCVD: { /* I, T, I+T */ fc_header_t *hdr = cbdata->header->dma.virt; node_printf(node, "%s received attached=%d\n", ocs_sm_event_name(evt), node->attached); /* sm: send LOGO acc */ ocs_send_logo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL); ocs_node_transition(node, __ocs_d_wait_logo_acc_cmpl, NULL); break; } default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } /** * @ingroup device_sm * @brief Device node state machine: Wait for the ADISC response. * *

Description

* Waits for the ADISC response from the remote node. * * @param ctx Remote node state machine context. * @param evt Event to process. * @param arg Per event optional argument. * * @return Returns NULL. */ void * __ocs_d_wait_adisc_rsp(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) { ocs_node_cb_t *cbdata = arg; std_node_state_decl(); node_sm_trace(); switch(evt) { case OCS_EVT_SRRS_ELS_REQ_OK: if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_ADISC, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; ocs_node_transition(node, __ocs_d_device_ready, NULL); break; case OCS_EVT_SRRS_ELS_REQ_RJT: /* received an LS_RJT, in this case, send shutdown (explicit logo) * event which will unregister the node, and start over with PLOGI */ if (node_check_els_req(ctx, evt, arg, FC_ELS_CMD_ADISC, __ocs_d_common, __func__)) { return NULL; } ocs_assert(node->els_req_cnt, NULL); node->els_req_cnt--; /*sm: post explicit logout */ ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); break; case OCS_EVT_LOGO_RCVD: { /* In this case, we have the equivalent of an LS_RJT for the ADISC, * so we need to abort the ADISC, and re-login with PLOGI */ /*sm: request abort, send LOGO acc */ fc_header_t *hdr = cbdata->header->dma.virt; node_printf(node, "%s received attached=%d\n", ocs_sm_event_name(evt), node->attached); ocs_send_logo_acc(cbdata->io, ocs_be16toh(hdr->ox_id), NULL, NULL); ocs_node_transition(node, __ocs_d_wait_logo_acc_cmpl, NULL); break; } default: __ocs_d_common(__func__, ctx, evt, arg); return NULL; } return NULL; } Index: stable/12/sys/net80211/ieee80211_output.c =================================================================== --- stable/12/sys/net80211/ieee80211_output.c (revision 360413) +++ stable/12/sys/net80211/ieee80211_output.c (revision 360414) @@ -1,4012 +1,4012 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2009 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #ifdef IEEE80211_SUPPORT_TDMA #include #endif #include #include #include #if defined(INET) || defined(INET6) #include #endif #ifdef INET #include #include #include #endif #ifdef INET6 #include #endif #include #define ETHER_HEADER_COPY(dst, src) \ memcpy(dst, src, sizeof(struct ether_header)) static int ieee80211_fragment(struct ieee80211vap *, struct mbuf *, u_int hdrsize, u_int ciphdrsize, u_int mtu); static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int); #ifdef IEEE80211_DEBUG /* * Decide if an outbound management frame should be * printed when debugging is enabled. This filters some * of the less interesting frames that come frequently * (e.g. beacons). */ static __inline int doprint(struct ieee80211vap *vap, int subtype) { switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: return (vap->iv_opmode == IEEE80211_M_IBSS); } return 1; } #endif /* * Transmit a frame to the given destination on the given VAP. * * It's up to the caller to figure out the details of who this * is going to and resolving the node. * * This routine takes care of queuing it for power save, * A-MPDU state stuff, fast-frames state stuff, encapsulation * if required, then passing it up to the driver layer. * * This routine (for now) consumes the mbuf and frees the node * reference; it ideally will return a TX status which reflects * whether the mbuf was consumed or not, so the caller can * free the mbuf (if appropriate) and the node reference (again, * if appropriate.) */ int ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = vap->iv_ifp; int mcast; if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && (m->m_flags & M_PWR_SAV) == 0) { /* * Station in power save mode; pass the frame * to the 802.11 layer and continue. We'll get * the frame back when the time is right. * XXX lose WDS vap linkage? */ if (ieee80211_pwrsave(ni, m) != 0) if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); /* * We queued it fine, so tell the upper layer * that we consumed it. */ return (0); } /* calculate priority so drivers can find the tx queue */ if (ieee80211_classify(ni, m)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, ni->ni_macaddr, NULL, "%s", "classification failure"); vap->iv_stats.is_tx_classify++; if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); ieee80211_free_node(ni); /* XXX better status? */ return (0); } /* * Stash the node pointer. Note that we do this after * any call to ieee80211_dwds_mcast because that code * uses any existing value for rcvif to identify the * interface it (might have been) received on. */ m->m_pkthdr.rcvif = (void *)ni; mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1: 0; BPF_MTAP(ifp, m); /* 802.3 tx */ /* * Check if A-MPDU tx aggregation is setup or if we * should try to enable it. The sta must be associated * with HT and A-MPDU enabled for use. When the policy * routine decides we should enable A-MPDU we issue an * ADDBA request and wait for a reply. The frame being * encapsulated will go out w/o using A-MPDU, or possibly * it might be collected by the driver and held/retransmit. * The default ic_ampdu_enable routine handles staggering * ADDBA requests in case the receiver NAK's us or we are * otherwise unable to establish a BA stream. * * Don't treat group-addressed frames as candidates for aggregation; * net80211 doesn't support 802.11aa-2012 and so group addressed * frames will always have sequence numbers allocated from the NON_QOS * TID. */ if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX)) { if ((m->m_flags & M_EAPOL) == 0 && (! mcast)) { int tid = WME_AC_TO_TID(M_WME_GETAC(m)); struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid]; ieee80211_txampdu_count_packet(tap); if (IEEE80211_AMPDU_RUNNING(tap)) { /* * Operational, mark frame for aggregation. * * XXX do tx aggregation here */ m->m_flags |= M_AMPDU_MPDU; } else if (!IEEE80211_AMPDU_REQUESTED(tap) && ic->ic_ampdu_enable(ni, tap)) { /* * Not negotiated yet, request service. */ ieee80211_ampdu_request(ni, tap); /* XXX hold frame for reply? */ } } } #ifdef IEEE80211_SUPPORT_SUPERG /* * Check for AMSDU/FF; queue for aggregation * * Note: we don't bother trying to do fast frames or * A-MSDU encapsulation for 802.3 drivers. Now, we * likely could do it for FF (because it's a magic * atheros tunnel LLC type) but I don't think we're going * to really need to. For A-MSDU we'd have to set the * A-MSDU QoS bit in the wifi header, so we just plain * can't do it. * * Strictly speaking, we could actually /do/ A-MSDU / FF * with A-MPDU together which for certain circumstances * is beneficial (eg A-MSDU of TCK ACKs.) However, * I'll ignore that for now so existing behaviour is maintained. * Later on it would be good to make "amsdu + ampdu" configurable. */ else if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { if ((! mcast) && ieee80211_amsdu_tx_ok(ni)) { m = ieee80211_amsdu_check(ni, m); if (m == NULL) { /* NB: any ni ref held on stageq */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: amsdu_check queued frame\n", __func__); return (0); } } else if ((! mcast) && IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) { m = ieee80211_ff_check(ni, m); if (m == NULL) { /* NB: any ni ref held on stageq */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: ff_check queued frame\n", __func__); return (0); } } } #endif /* IEEE80211_SUPPORT_SUPERG */ /* * Grab the TX lock - serialise the TX process from this * point (where TX state is being checked/modified) * through to driver queue. */ IEEE80211_TX_LOCK(ic); /* * XXX make the encap and transmit code a separate function * so things like the FF (and later A-MSDU) path can just call * it for flushed frames. */ if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { /* * Encapsulate the packet in prep for transmission. */ m = ieee80211_encap(vap, ni, m); if (m == NULL) { /* NB: stat+msg handled in ieee80211_encap */ IEEE80211_TX_UNLOCK(ic); ieee80211_free_node(ni); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (ENOBUFS); } } (void) ieee80211_parent_xmitpkt(ic, m); /* * Unlock at this point - no need to hold it across * ieee80211_free_node() (ie, the comlock) */ IEEE80211_TX_UNLOCK(ic); ic->ic_lastdata = ticks; return (0); } /* * Send the given mbuf through the given vap. * * This consumes the mbuf regardless of whether the transmit * was successful or not. * * This does none of the initial checks that ieee80211_start() * does (eg CAC timeout, interface wakeup) - the caller must * do this first. */ static int ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) { #define IS_DWDS(vap) \ (vap->iv_opmode == IEEE80211_M_WDS && \ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = vap->iv_ifp; struct ieee80211_node *ni; struct ether_header *eh; /* * Cancel any background scan. */ if (ic->ic_flags & IEEE80211_F_SCAN) ieee80211_cancel_anyscan(vap); /* * Find the node for the destination so we can do * things like power save and fast frames aggregation. * * NB: past this point various code assumes the first * mbuf has the 802.3 header present (and contiguous). */ ni = NULL; if (m->m_len < sizeof(struct ether_header) && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "discard frame, %s\n", "m_pullup failed"); vap->iv_stats.is_tx_nobuf++; /* XXX */ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (ENOBUFS); } eh = mtod(m, struct ether_header *); if (ETHER_IS_MULTICAST(eh->ether_dhost)) { if (IS_DWDS(vap)) { /* * Only unicast frames from the above go out * DWDS vaps; multicast frames are handled by * dispatching the frame as it comes through * the AP vap (see below). */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS, eh->ether_dhost, "mcast", "%s", "on DWDS"); vap->iv_stats.is_dwds_mcast++; m_freem(m); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); /* XXX better status? */ return (ENOBUFS); } if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* * Spam DWDS vap's w/ multicast traffic. */ /* XXX only if dwds in use? */ ieee80211_dwds_mcast(vap, m); } } #ifdef IEEE80211_SUPPORT_MESH if (vap->iv_opmode != IEEE80211_M_MBSS) { #endif ni = ieee80211_find_txnode(vap, eh->ether_dhost); if (ni == NULL) { /* NB: ieee80211_find_txnode does stat+msg */ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); /* XXX better status? */ return (ENOBUFS); } if (ni->ni_associd == 0 && (ni->ni_flags & IEEE80211_NODE_ASSOCID)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, eh->ether_dhost, NULL, "sta not associated (type 0x%04x)", htons(eh->ether_type)); vap->iv_stats.is_tx_notassoc++; if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); ieee80211_free_node(ni); /* XXX better status? */ return (ENOBUFS); } #ifdef IEEE80211_SUPPORT_MESH } else { if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) { /* * Proxy station only if configured. */ if (!ieee80211_mesh_isproxyena(vap)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_MESH, eh->ether_dhost, NULL, "%s", "proxy not enabled"); vap->iv_stats.is_mesh_notproxy++; if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); /* XXX better status? */ return (ENOBUFS); } IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "forward frame from DS SA(%6D), DA(%6D)\n", eh->ether_shost, ":", eh->ether_dhost, ":"); ieee80211_mesh_proxy_check(vap, eh->ether_shost); } ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m); if (ni == NULL) { /* * NB: ieee80211_mesh_discover holds/disposes * frame (e.g. queueing on path discovery). */ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); /* XXX better status? */ return (ENOBUFS); } } #endif /* * We've resolved the sender, so attempt to transmit it. */ if (vap->iv_state == IEEE80211_S_SLEEP) { /* * In power save; queue frame and then wakeup device * for transmit. */ ic->ic_lastdata = ticks; if (ieee80211_pwrsave(ni, m) != 0) if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); ieee80211_new_state(vap, IEEE80211_S_RUN, 0); return (0); } if (ieee80211_vap_pkt_send_dest(vap, m, ni) != 0) return (ENOBUFS); return (0); #undef IS_DWDS } /* * Start method for vap's. All packets from the stack come * through here. We handle common processing of the packets * before dispatching them to the underlying device. * * if_transmit() requires that the mbuf be consumed by this call * regardless of the return condition. */ int ieee80211_vap_transmit(struct ifnet *ifp, struct mbuf *m) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; /* * No data frames go out unless we're running. * Note in particular this covers CAC and CSA * states (though maybe we should check muting * for CSA). */ if (vap->iv_state != IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_SLEEP) { IEEE80211_LOCK(ic); /* re-check under the com lock to avoid races */ if (vap->iv_state != IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_SLEEP) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "%s: ignore queue, in %s state\n", __func__, ieee80211_state_name[vap->iv_state]); vap->iv_stats.is_tx_badstate++; IEEE80211_UNLOCK(ic); ifp->if_drv_flags |= IFF_DRV_OACTIVE; m_freem(m); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (ENETDOWN); } IEEE80211_UNLOCK(ic); } /* * Sanitize mbuf flags for net80211 use. We cannot * clear M_PWR_SAV or M_MORE_DATA because these may * be set for frames that are re-submitted from the * power save queue. * * NB: This must be done before ieee80211_classify as * it marks EAPOL in frames with M_EAPOL. */ m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); /* * Bump to the packet transmission path. * The mbuf will be consumed here. */ return (ieee80211_start_pkt(vap, m)); } void ieee80211_vap_qflush(struct ifnet *ifp) { /* Empty for now */ } /* * 802.11 raw output routine. * * XXX TODO: this (and other send routines) should correctly * XXX keep the pwr mgmt bit set if it decides to call into the * XXX driver to send a frame whilst the state is SLEEP. * * Otherwise the peer may decide that we're awake and flood us * with traffic we are still too asleep to receive! */ int ieee80211_raw_output(struct ieee80211vap *vap, struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = vap->iv_ic; int error; /* * Set node - the caller has taken a reference, so ensure * that the mbuf has the same node value that * it would if it were going via the normal path. */ m->m_pkthdr.rcvif = (void *)ni; /* * Attempt to add bpf transmit parameters. * * For now it's ok to fail; the raw_xmit api still takes * them as an option. * * Later on when ic_raw_xmit() has params removed, * they'll have to be added - so fail the transmit if * they can't be. */ if (params) (void) ieee80211_add_xmit_params(m, params); error = ic->ic_raw_xmit(ni, m, params); if (error) { if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); } return (error); } static int ieee80211_validate_frame(struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211_frame *wh; int type; if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) return (EINVAL); wh = mtod(m, struct ieee80211_frame *); if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) return (EINVAL); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; if (type != IEEE80211_FC0_TYPE_DATA) { if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS) return (EINVAL); if (type != IEEE80211_FC0_TYPE_MGT && (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) != 0) return (EINVAL); /* XXX skip other field checks? */ } if ((params && (params->ibp_flags & IEEE80211_BPF_CRYPTO) != 0) || (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) != 0) { int subtype; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; /* * See IEEE Std 802.11-2012, * 8.2.4.1.9 'Protected Frame field' */ /* XXX no support for robust management frames yet. */ if (!(type == IEEE80211_FC0_TYPE_DATA || (type == IEEE80211_FC0_TYPE_MGT && subtype == IEEE80211_FC0_SUBTYPE_AUTH))) return (EINVAL); wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; } if (m->m_pkthdr.len < ieee80211_anyhdrsize(wh)) return (EINVAL); return (0); } static int ieee80211_validate_rate(struct ieee80211_node *ni, uint8_t rate) { struct ieee80211com *ic = ni->ni_ic; if (IEEE80211_IS_HT_RATE(rate)) { if ((ic->ic_htcaps & IEEE80211_HTC_HT) == 0) return (EINVAL); rate = IEEE80211_RV(rate); if (rate <= 31) { if (rate > ic->ic_txstream * 8 - 1) return (EINVAL); return (0); } if (rate == 32) { if ((ic->ic_htcaps & IEEE80211_HTC_TXMCS32) == 0) return (EINVAL); return (0); } if ((ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) == 0) return (EINVAL); switch (ic->ic_txstream) { case 0: case 1: return (EINVAL); case 2: if (rate > 38) return (EINVAL); return (0); case 3: if (rate > 52) return (EINVAL); return (0); case 4: default: if (rate > 76) return (EINVAL); return (0); } } if (!ieee80211_isratevalid(ic->ic_rt, rate)) return (EINVAL); return (0); } static int ieee80211_sanitize_rates(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { int error; if (!params) return (0); /* nothing to do */ /* NB: most drivers assume that ibp_rate0 is set (!= 0). */ if (params->ibp_rate0 != 0) { error = ieee80211_validate_rate(ni, params->ibp_rate0); if (error != 0) return (error); } else { /* XXX pre-setup some default (e.g., mgmt / mcast) rate */ /* XXX __DECONST? */ (void) m; } if (params->ibp_rate1 != 0 && (error = ieee80211_validate_rate(ni, params->ibp_rate1)) != 0) return (error); if (params->ibp_rate2 != 0 && (error = ieee80211_validate_rate(ni, params->ibp_rate2)) != 0) return (error); if (params->ibp_rate3 != 0 && (error = ieee80211_validate_rate(ni, params->ibp_rate3)) != 0) return (error); return (0); } /* * 802.11 output routine. This is (currently) used only to * connect bpf write calls to the 802.11 layer for injecting * raw 802.11 frames. */ int ieee80211_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro) { #define senderr(e) do { error = (e); goto bad;} while (0) const struct ieee80211_bpf_params *params = NULL; struct ieee80211_node *ni = NULL; struct ieee80211vap *vap; struct ieee80211_frame *wh; struct ieee80211com *ic = NULL; int error; int ret; if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { /* * Short-circuit requests if the vap is marked OACTIVE * as this can happen because a packet came down through * ieee80211_start before the vap entered RUN state in * which case it's ok to just drop the frame. This * should not be necessary but callers of if_output don't * check OACTIVE. */ senderr(ENETDOWN); } vap = ifp->if_softc; ic = vap->iv_ic; /* * Hand to the 802.3 code if not tagged as * a raw 802.11 frame. */ if (dst->sa_family != AF_IEEE80211) return vap->iv_output(ifp, m, dst, ro); #ifdef MAC error = mac_ifnet_check_transmit(ifp, m); if (error) senderr(error); #endif if (ifp->if_flags & IFF_MONITOR) senderr(ENETDOWN); if (!IFNET_IS_UP_RUNNING(ifp)) senderr(ENETDOWN); if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, "block %s frame in CAC state\n", "raw data"); vap->iv_stats.is_tx_badstate++; senderr(EIO); /* XXX */ } else if (vap->iv_state == IEEE80211_S_SCAN) senderr(EIO); /* XXX bypass bridge, pfil, carp, etc. */ /* * NB: DLT_IEEE802_11_RADIO identifies the parameters are * present by setting the sa_len field of the sockaddr (yes, * this is a hack). * NB: we assume sa_data is suitably aligned to cast. */ if (dst->sa_len != 0) params = (const struct ieee80211_bpf_params *)dst->sa_data; error = ieee80211_validate_frame(m, params); if (error != 0) senderr(error); wh = mtod(m, struct ieee80211_frame *); /* locate destination node */ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { case IEEE80211_FC1_DIR_NODS: case IEEE80211_FC1_DIR_FROMDS: ni = ieee80211_find_txnode(vap, wh->i_addr1); break; case IEEE80211_FC1_DIR_TODS: case IEEE80211_FC1_DIR_DSTODS: ni = ieee80211_find_txnode(vap, wh->i_addr3); break; default: senderr(EDOOFUS); } if (ni == NULL) { /* * Permit packets w/ bpf params through regardless * (see below about sa_len). */ if (dst->sa_len == 0) senderr(EHOSTUNREACH); ni = ieee80211_ref_node(vap->iv_bss); } /* * Sanitize mbuf for net80211 flags leaked from above. * * NB: This must be done before ieee80211_classify as * it marks EAPOL in frames with M_EAPOL. */ m->m_flags &= ~M_80211_TX; m->m_flags |= M_ENCAP; /* mark encapsulated */ if (IEEE80211_IS_DATA(wh)) { /* calculate priority so drivers can find the tx queue */ if (ieee80211_classify(ni, m)) senderr(EIO); /* XXX */ /* NB: ieee80211_encap does not include 802.11 header */ IEEE80211_NODE_STAT_ADD(ni, tx_bytes, m->m_pkthdr.len - ieee80211_hdrsize(wh)); } else M_WME_SETAC(m, WME_AC_BE); error = ieee80211_sanitize_rates(ni, m, params); if (error != 0) senderr(error); IEEE80211_NODE_STAT(ni, tx_data); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { IEEE80211_NODE_STAT(ni, tx_mcast); m->m_flags |= M_MCAST; } else IEEE80211_NODE_STAT(ni, tx_ucast); IEEE80211_TX_LOCK(ic); ret = ieee80211_raw_output(vap, ni, m, params); IEEE80211_TX_UNLOCK(ic); return (ret); bad: if (m != NULL) m_freem(m); if (ni != NULL) ieee80211_free_node(ni); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return error; #undef senderr } /* * Set the direction field and address fields of an outgoing * frame. Note this should be called early on in constructing * a frame as it sets i_fc[1]; other bits can then be or'd in. */ void ieee80211_send_setup( struct ieee80211_node *ni, struct mbuf *m, int type, int tid, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN], const uint8_t bssid[IEEE80211_ADDR_LEN]) { #define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_tx_ampdu *tap; struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); ieee80211_seq seqno; IEEE80211_TX_LOCK_ASSERT(ni->ni_ic); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { switch (vap->iv_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; IEEE80211_ADDR_COPY(wh->i_addr1, bssid); IEEE80211_ADDR_COPY(wh->i_addr2, sa); IEEE80211_ADDR_COPY(wh->i_addr3, da); break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, da); IEEE80211_ADDR_COPY(wh->i_addr2, sa); IEEE80211_ADDR_COPY(wh->i_addr3, bssid); break; case IEEE80211_M_HOSTAP: wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; IEEE80211_ADDR_COPY(wh->i_addr1, da); IEEE80211_ADDR_COPY(wh->i_addr2, bssid); IEEE80211_ADDR_COPY(wh->i_addr3, sa); break; case IEEE80211_M_WDS: wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; IEEE80211_ADDR_COPY(wh->i_addr1, da); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, da); IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); break; case IEEE80211_M_MBSS: #ifdef IEEE80211_SUPPORT_MESH if (IEEE80211_IS_MULTICAST(da)) { wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; /* XXX next hop */ IEEE80211_ADDR_COPY(wh->i_addr1, da); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); } else { wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; IEEE80211_ADDR_COPY(wh->i_addr1, da); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, da); IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa); } #endif break; case IEEE80211_M_MONITOR: /* NB: to quiet compiler */ break; } } else { wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, da); IEEE80211_ADDR_COPY(wh->i_addr2, sa); #ifdef IEEE80211_SUPPORT_MESH if (vap->iv_opmode == IEEE80211_M_MBSS) IEEE80211_ADDR_COPY(wh->i_addr3, sa); else #endif IEEE80211_ADDR_COPY(wh->i_addr3, bssid); } *(uint16_t *)&wh->i_dur[0] = 0; /* * XXX TODO: this is what the TX lock is for. * Here we're incrementing sequence numbers, and they * need to be in lock-step with what the driver is doing * both in TX ordering and crypto encap (IV increment.) * * If the driver does seqno itself, then we can skip * assigning sequence numbers here, and we can avoid * requiring the TX lock. */ tap = &ni->ni_tx_ampdu[tid]; if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) { m->m_flags |= M_AMPDU_MPDU; /* NB: zero out i_seq field (for s/w encryption etc) */ *(uint16_t *)&wh->i_seq[0] = 0; } else { if (IEEE80211_HAS_SEQ(type & IEEE80211_FC0_TYPE_MASK, type & IEEE80211_FC0_SUBTYPE_MASK)) /* * 802.11-2012 9.3.2.10 - QoS multicast frames * come out of a different seqno space. */ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; } else { seqno = ni->ni_txseqs[tid]++; } else seqno = 0; *(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); } if (IEEE80211_IS_MULTICAST(wh->i_addr1)) m->m_flags |= M_MCAST; #undef WH4 } /* * Send a management frame to the specified node. The node pointer * must have a reference as the pointer will be passed to the driver * and potentially held for a long time. If the frame is successfully * dispatched to the driver, then it is responsible for freeing the * reference (and potentially free'ing up any associated storage); * otherwise deal with reclaiming any reference (on error). */ int ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type, struct ieee80211_bpf_params *params) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; int ret; KASSERT(ni != NULL, ("null node")); if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, ni, "block %s frame in CAC state", ieee80211_mgt_subtype_name(type)); vap->iv_stats.is_tx_badstate++; ieee80211_free_node(ni); m_freem(m); return EIO; /* XXX */ } M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); if (m == NULL) { ieee80211_free_node(ni); return ENOMEM; } IEEE80211_TX_LOCK(ic); wh = mtod(m, struct ieee80211_frame *); ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | type, IEEE80211_NONQOS_TID, vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); if (params->ibp_flags & IEEE80211_BPF_CRYPTO) { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1, "encrypting frame (%s)", __func__); wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; } m->m_flags |= M_ENCAP; /* mark encapsulated */ KASSERT(type != IEEE80211_FC0_SUBTYPE_PROBE_RESP, ("probe response?")); M_WME_SETAC(m, params->ibp_pri); #ifdef IEEE80211_DEBUG /* avoid printing too many frames */ if ((ieee80211_msg_debug(vap) && doprint(vap, type)) || ieee80211_msg_dumppkts(vap)) { printf("[%s] send %s on channel %u\n", ether_sprintf(wh->i_addr1), ieee80211_mgt_subtype_name(type), ieee80211_chan2ieee(ic, ic->ic_curchan)); } #endif IEEE80211_NODE_STAT(ni, tx_mgmt); ret = ieee80211_raw_output(vap, ni, m, params); IEEE80211_TX_UNLOCK(ic); return (ret); } static void ieee80211_nulldata_transmitted(struct ieee80211_node *ni, void *arg, int status) { struct ieee80211vap *vap = ni->ni_vap; wakeup(vap); } /* * Send a null data frame to the specified node. If the station * is setup for QoS then a QoS Null Data frame is constructed. * If this is a WDS station then a 4-address frame is constructed. * * NB: the caller is assumed to have setup a node reference * for use; this is necessary to deal with a race condition * when probing for inactive stations. Like ieee80211_mgmt_output * we must cleanup any node reference on error; however we * can safely just unref it as we know it will never be the * last reference to the node. */ int ieee80211_send_nulldata(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct mbuf *m; struct ieee80211_frame *wh; int hdrlen; uint8_t *frm; int ret; if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, ni, "block %s frame in CAC state", "null data"); ieee80211_unref_node(&ni); vap->iv_stats.is_tx_badstate++; return EIO; /* XXX */ } if (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) hdrlen = sizeof(struct ieee80211_qosframe); else hdrlen = sizeof(struct ieee80211_frame); /* NB: only WDS vap's get 4-address frames */ if (vap->iv_opmode == IEEE80211_M_WDS) hdrlen += IEEE80211_ADDR_LEN; if (ic->ic_flags & IEEE80211_F_DATAPAD) hdrlen = roundup(hdrlen, sizeof(uint32_t)); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + hdrlen, 0); if (m == NULL) { /* XXX debug msg */ ieee80211_unref_node(&ni); vap->iv_stats.is_tx_nobuf++; return ENOMEM; } KASSERT(M_LEADINGSPACE(m) >= hdrlen, ("leading space %zd", M_LEADINGSPACE(m))); M_PREPEND(m, hdrlen, M_NOWAIT); if (m == NULL) { /* NB: cannot happen */ ieee80211_free_node(ni); return ENOMEM; } IEEE80211_TX_LOCK(ic); wh = mtod(m, struct ieee80211_frame *); /* NB: a little lie */ if (ni->ni_flags & IEEE80211_NODE_QOS) { const int tid = WME_AC_TO_TID(WME_AC_BE); uint8_t *qos; ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS_NULL, tid, vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); if (vap->iv_opmode == IEEE80211_M_WDS) qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; else qos = ((struct ieee80211_qosframe *) wh)->i_qos; qos[0] = tid & IEEE80211_QOS_TID; if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[WME_AC_BE].wmep_noackPolicy) qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; qos[1] = 0; } else { ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, IEEE80211_NONQOS_TID, vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); } if (vap->iv_opmode != IEEE80211_M_WDS) { /* NB: power management bit is never sent by an AP */ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && vap->iv_opmode != IEEE80211_M_HOSTAP) wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT; } if ((ic->ic_flags & IEEE80211_F_SCAN) && (ni->ni_flags & IEEE80211_NODE_PWR_MGT)) { ieee80211_add_callback(m, ieee80211_nulldata_transmitted, NULL); } m->m_len = m->m_pkthdr.len = hdrlen; m->m_flags |= M_ENCAP; /* mark encapsulated */ M_WME_SETAC(m, WME_AC_BE); IEEE80211_NODE_STAT(ni, tx_data); IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, ni, "send %snull data frame on channel %u, pwr mgt %s", ni->ni_flags & IEEE80211_NODE_QOS ? "QoS " : "", ieee80211_chan2ieee(ic, ic->ic_curchan), wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); ret = ieee80211_raw_output(vap, ni, m, NULL); IEEE80211_TX_UNLOCK(ic); return (ret); } /* * Assign priority to a frame based on any vlan tag assigned * to the station and/or any Diffserv setting in an IP header. * Finally, if an ACM policy is setup (in station mode) it's * applied. */ int ieee80211_classify(struct ieee80211_node *ni, struct mbuf *m) { const struct ether_header *eh = NULL; uint16_t ether_type; int v_wme_ac, d_wme_ac, ac; if (__predict_false(m->m_flags & M_ENCAP)) { struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); struct llc *llc; int hdrlen, subtype; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; if (subtype & IEEE80211_FC0_SUBTYPE_NODATA) { ac = WME_AC_BE; goto done; } hdrlen = ieee80211_hdrsize(wh); if (m->m_pkthdr.len < hdrlen + sizeof(*llc)) return 1; llc = (struct llc *)mtodo(m, hdrlen); if (llc->llc_dsap != LLC_SNAP_LSAP || llc->llc_ssap != LLC_SNAP_LSAP || llc->llc_control != LLC_UI || llc->llc_snap.org_code[0] != 0 || llc->llc_snap.org_code[1] != 0 || llc->llc_snap.org_code[2] != 0) return 1; ether_type = llc->llc_snap.ether_type; } else { eh = mtod(m, struct ether_header *); ether_type = eh->ether_type; } /* * Always promote PAE/EAPOL frames to high priority. */ if (ether_type == htons(ETHERTYPE_PAE)) { /* NB: mark so others don't need to check header */ m->m_flags |= M_EAPOL; ac = WME_AC_VO; goto done; } /* * Non-qos traffic goes to BE. */ if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) { ac = WME_AC_BE; goto done; } /* * If node has a vlan tag then all traffic * to it must have a matching tag. */ v_wme_ac = 0; if (ni->ni_vlan != 0) { if ((m->m_flags & M_VLANTAG) == 0) { IEEE80211_NODE_STAT(ni, tx_novlantag); return 1; } if (EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) != EVL_VLANOFTAG(ni->ni_vlan)) { IEEE80211_NODE_STAT(ni, tx_vlanmismatch); return 1; } /* map vlan priority to AC */ v_wme_ac = TID_TO_WME_AC(EVL_PRIOFTAG(ni->ni_vlan)); } /* XXX m_copydata may be too slow for fast path */ #ifdef INET if (eh && eh->ether_type == htons(ETHERTYPE_IP)) { uint8_t tos; /* * IP frame, map the DSCP bits from the TOS field. */ /* NB: ip header may not be in first mbuf */ m_copydata(m, sizeof(struct ether_header) + offsetof(struct ip, ip_tos), sizeof(tos), &tos); tos >>= 5; /* NB: ECN + low 3 bits of DSCP */ d_wme_ac = TID_TO_WME_AC(tos); } else { #endif /* INET */ #ifdef INET6 if (eh && eh->ether_type == htons(ETHERTYPE_IPV6)) { uint32_t flow; uint8_t tos; /* * IPv6 frame, map the DSCP bits from the traffic class field. */ m_copydata(m, sizeof(struct ether_header) + offsetof(struct ip6_hdr, ip6_flow), sizeof(flow), (caddr_t) &flow); tos = (uint8_t)(ntohl(flow) >> 20); tos >>= 5; /* NB: ECN + low 3 bits of DSCP */ d_wme_ac = TID_TO_WME_AC(tos); } else { #endif /* INET6 */ d_wme_ac = WME_AC_BE; #ifdef INET6 } #endif #ifdef INET } #endif /* * Use highest priority AC. */ if (v_wme_ac > d_wme_ac) ac = v_wme_ac; else ac = d_wme_ac; /* * Apply ACM policy. */ if (ni->ni_vap->iv_opmode == IEEE80211_M_STA) { static const int acmap[4] = { WME_AC_BK, /* WME_AC_BE */ WME_AC_BK, /* WME_AC_BK */ WME_AC_BE, /* WME_AC_VI */ WME_AC_VI, /* WME_AC_VO */ }; struct ieee80211com *ic = ni->ni_ic; while (ac != WME_AC_BK && ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm) ac = acmap[ac]; } done: M_WME_SETAC(m, ac); return 0; } /* * Insure there is sufficient contiguous space to encapsulate the * 802.11 data frame. If room isn't already there, arrange for it. * Drivers and cipher modules assume we have done the necessary work * and fail rudely if they don't find the space they need. */ struct mbuf * ieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize, struct ieee80211_key *key, struct mbuf *m) { #define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) int needed_space = vap->iv_ic->ic_headroom + hdrsize; if (key != NULL) { /* XXX belongs in crypto code? */ needed_space += key->wk_cipher->ic_header; /* XXX frags */ /* * When crypto is being done in the host we must insure * the data are writable for the cipher routines; clone * a writable mbuf chain. * XXX handle SWMIC specially */ if (key->wk_flags & (IEEE80211_KEY_SWENCRYPT|IEEE80211_KEY_SWENMIC)) { m = m_unshare(m, M_NOWAIT); if (m == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "%s: cannot get writable mbuf\n", __func__); vap->iv_stats.is_tx_nobuf++; /* XXX new stat */ return NULL; } } } /* * We know we are called just before stripping an Ethernet * header and prepending an LLC header. This means we know * there will be * sizeof(struct ether_header) - sizeof(struct llc) * bytes recovered to which we need additional space for the * 802.11 header and any crypto header. */ /* XXX check trailing space and copy instead? */ if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) { struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type); if (n == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "%s: cannot expand storage\n", __func__); vap->iv_stats.is_tx_nobuf++; m_freem(m); return NULL; } KASSERT(needed_space <= MHLEN, ("not enough room, need %u got %d\n", needed_space, MHLEN)); /* * Setup new mbuf to have leading space to prepend the * 802.11 header and any crypto header bits that are * required (the latter are added when the driver calls * back to ieee80211_crypto_encap to do crypto encapsulation). */ /* NB: must be first 'cuz it clobbers m_data */ m_move_pkthdr(n, m); n->m_len = 0; /* NB: m_gethdr does not set */ n->m_data += needed_space; /* * Pull up Ethernet header to create the expected layout. * We could use m_pullup but that's overkill (i.e. we don't * need the actual data) and it cannot fail so do it inline * for speed. */ /* NB: struct ether_header is known to be contiguous */ n->m_len += sizeof(struct ether_header); m->m_len -= sizeof(struct ether_header); m->m_data += sizeof(struct ether_header); /* * Replace the head of the chain. */ n->m_next = m; m = n; } return m; #undef TO_BE_RECLAIMED } /* * Return the transmit key to use in sending a unicast frame. * If a unicast key is set we use that. When no unicast key is set * we fall back to the default transmit key. */ static __inline struct ieee80211_key * ieee80211_crypto_getucastkey(struct ieee80211vap *vap, struct ieee80211_node *ni) { if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) { if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) return NULL; return &vap->iv_nw_keys[vap->iv_def_txkey]; } else { return &ni->ni_ucastkey; } } /* * Return the transmit key to use in sending a multicast frame. * Multicast traffic always uses the group key which is installed as * the default tx key. */ static __inline struct ieee80211_key * ieee80211_crypto_getmcastkey(struct ieee80211vap *vap, struct ieee80211_node *ni) { if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE || IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey])) return NULL; return &vap->iv_nw_keys[vap->iv_def_txkey]; } /* * Encapsulate an outbound data frame. The mbuf chain is updated. * If an error is encountered NULL is returned. The caller is required * to provide a node reference and pullup the ethernet header in the * first mbuf. * * NB: Packet is assumed to be processed by ieee80211_classify which * marked EAPOL frames w/ M_EAPOL. */ struct mbuf * ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, struct mbuf *m) { #define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh)) #define MC01(mc) ((struct ieee80211_meshcntl_ae01 *)mc) struct ieee80211com *ic = ni->ni_ic; #ifdef IEEE80211_SUPPORT_MESH struct ieee80211_mesh_state *ms = vap->iv_mesh; struct ieee80211_meshcntl_ae10 *mc; struct ieee80211_mesh_route *rt = NULL; int dir = -1; #endif struct ether_header eh; struct ieee80211_frame *wh; struct ieee80211_key *key; struct llc *llc; int hdrsize, hdrspace, datalen, addqos, txfrag, is4addr, is_mcast; ieee80211_seq seqno; int meshhdrsize, meshae; uint8_t *qos; int is_amsdu = 0; IEEE80211_TX_LOCK_ASSERT(ic); is_mcast = !! (m->m_flags & (M_MCAST | M_BCAST)); /* * Copy existing Ethernet header to a safe place. The * rest of the code assumes it's ok to strip it when * reorganizing state for the final encapsulation. */ KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!")); ETHER_HEADER_COPY(&eh, mtod(m, caddr_t)); /* * Insure space for additional headers. First identify * transmit key to use in calculating any buffer adjustments * required. This is also used below to do privacy * encapsulation work. Then calculate the 802.11 header * size and any padding required by the driver. * * Note key may be NULL if we fall back to the default * transmit key and that is not set. In that case the * buffer may not be expanded as needed by the cipher * routines, but they will/should discard it. */ if (vap->iv_flags & IEEE80211_F_PRIVACY) { if (vap->iv_opmode == IEEE80211_M_STA || !IEEE80211_IS_MULTICAST(eh.ether_dhost) || (vap->iv_opmode == IEEE80211_M_WDS && (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))) key = ieee80211_crypto_getucastkey(vap, ni); else key = ieee80211_crypto_getmcastkey(vap, ni); if (key == NULL && (m->m_flags & M_EAPOL) == 0) { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, eh.ether_dhost, "no default transmit key (%s) deftxkey %u", __func__, vap->iv_def_txkey); vap->iv_stats.is_tx_nodefkey++; goto bad; } } else key = NULL; /* * XXX Some ap's don't handle QoS-encapsulated EAPOL * frames so suppress use. This may be an issue if other * ap's require all data frames to be QoS-encapsulated * once negotiated in which case we'll need to make this * configurable. * * Don't send multicast QoS frames. * Technically multicast frames can be QoS if all stations in the * BSS are also QoS. * * NB: mesh data frames are QoS, including multicast frames. */ addqos = (((is_mcast == 0) && (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT))) || (vap->iv_opmode == IEEE80211_M_MBSS)) && (m->m_flags & M_EAPOL) == 0; if (addqos) hdrsize = sizeof(struct ieee80211_qosframe); else hdrsize = sizeof(struct ieee80211_frame); #ifdef IEEE80211_SUPPORT_MESH if (vap->iv_opmode == IEEE80211_M_MBSS) { /* * Mesh data frames are encapsulated according to the * rules of Section 11B.8.5 (p.139 of D3.0 spec). * o Group Addressed data (aka multicast) originating * at the local sta are sent w/ 3-address format and * address extension mode 00 * o Individually Addressed data (aka unicast) originating * at the local sta are sent w/ 4-address format and * address extension mode 00 * o Group Addressed data forwarded from a non-mesh sta are * sent w/ 3-address format and address extension mode 01 * o Individually Address data from another sta are sent * w/ 4-address format and address extension mode 10 */ is4addr = 0; /* NB: don't use, disable */ if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) { rt = ieee80211_mesh_rt_find(vap, eh.ether_dhost); KASSERT(rt != NULL, ("route is NULL")); dir = IEEE80211_FC1_DIR_DSTODS; hdrsize += IEEE80211_ADDR_LEN; if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) { if (IEEE80211_ADDR_EQ(rt->rt_mesh_gate, vap->iv_myaddr)) { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, eh.ether_dhost, "%s", "trying to send to ourself"); goto bad; } meshae = IEEE80211_MESH_AE_10; meshhdrsize = sizeof(struct ieee80211_meshcntl_ae10); } else { meshae = IEEE80211_MESH_AE_00; meshhdrsize = sizeof(struct ieee80211_meshcntl); } } else { dir = IEEE80211_FC1_DIR_FROMDS; if (!IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) { /* proxy group */ meshae = IEEE80211_MESH_AE_01; meshhdrsize = sizeof(struct ieee80211_meshcntl_ae01); } else { /* group */ meshae = IEEE80211_MESH_AE_00; meshhdrsize = sizeof(struct ieee80211_meshcntl); } } } else { #endif /* * 4-address frames need to be generated for: * o packets sent through a WDS vap (IEEE80211_M_WDS) * o packets sent through a vap marked for relaying * (e.g. a station operating with dynamic WDS) */ is4addr = vap->iv_opmode == IEEE80211_M_WDS || ((vap->iv_flags_ext & IEEE80211_FEXT_4ADDR) && !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)); if (is4addr) hdrsize += IEEE80211_ADDR_LEN; meshhdrsize = meshae = 0; #ifdef IEEE80211_SUPPORT_MESH } #endif /* * Honor driver DATAPAD requirement. */ if (ic->ic_flags & IEEE80211_F_DATAPAD) hdrspace = roundup(hdrsize, sizeof(uint32_t)); else hdrspace = hdrsize; if (__predict_true((m->m_flags & M_FF) == 0)) { /* * Normal frame. */ m = ieee80211_mbuf_adjust(vap, hdrspace + meshhdrsize, key, m); if (m == NULL) { /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ goto bad; } /* NB: this could be optimized 'cuz of ieee80211_mbuf_adjust */ m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); llc = mtod(m, struct llc *); llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; llc->llc_control = LLC_UI; llc->llc_snap.org_code[0] = 0; llc->llc_snap.org_code[1] = 0; llc->llc_snap.org_code[2] = 0; llc->llc_snap.ether_type = eh.ether_type; } else { #ifdef IEEE80211_SUPPORT_SUPERG /* * Aggregated frame. Check if it's for AMSDU or FF. * * XXX TODO: IEEE80211_NODE_AMSDU* isn't implemented * anywhere for some reason. But, since 11n requires * AMSDU RX, we can just assume "11n" == "AMSDU". */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: called; M_FF\n", __func__); if (ieee80211_amsdu_tx_ok(ni)) { m = ieee80211_amsdu_encap(vap, m, hdrspace + meshhdrsize, key); is_amsdu = 1; } else { m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key); } if (m == NULL) #endif goto bad; } datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */ M_PREPEND(m, hdrspace + meshhdrsize, M_NOWAIT); if (m == NULL) { vap->iv_stats.is_tx_nobuf++; goto bad; } wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; *(uint16_t *)wh->i_dur = 0; qos = NULL; /* NB: quiet compiler */ if (is4addr) { wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); } else switch (vap->iv_opmode) { case IEEE80211_M_STA: wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); /* * NB: always use the bssid from iv_bss as the * neighbor's may be stale after an ibss merge */ IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_bssid); break; case IEEE80211_M_HOSTAP: wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); break; #ifdef IEEE80211_SUPPORT_MESH case IEEE80211_M_MBSS: /* NB: offset by hdrspace to deal with DATAPAD */ mc = (struct ieee80211_meshcntl_ae10 *) (mtod(m, uint8_t *) + hdrspace); wh->i_fc[1] = dir; switch (meshae) { case IEEE80211_MESH_AE_00: /* no proxy */ mc->mc_flags = 0; if (dir == IEEE80211_FC1_DIR_DSTODS) { /* ucast */ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); qos =((struct ieee80211_qosframe_addr4 *) wh)->i_qos; } else if (dir == IEEE80211_FC1_DIR_FROMDS) { /* mcast */ IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); qos = ((struct ieee80211_qosframe *) wh)->i_qos; } break; case IEEE80211_MESH_AE_01: /* mcast, proxy */ wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_myaddr); mc->mc_flags = 1; IEEE80211_ADDR_COPY(MC01(mc)->mc_addr4, eh.ether_shost); qos = ((struct ieee80211_qosframe *) wh)->i_qos; break; case IEEE80211_MESH_AE_10: /* ucast, proxy */ KASSERT(rt != NULL, ("route is NULL")); IEEE80211_ADDR_COPY(wh->i_addr1, rt->rt_nexthop); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, rt->rt_mesh_gate); IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, vap->iv_myaddr); mc->mc_flags = IEEE80211_MESH_AE_10; IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_dhost); IEEE80211_ADDR_COPY(mc->mc_addr6, eh.ether_shost); qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; break; default: KASSERT(0, ("meshae %d", meshae)); break; } mc->mc_ttl = ms->ms_ttl; ms->ms_seq++; le32enc(mc->mc_seq, ms->ms_seq); break; #endif case IEEE80211_M_WDS: /* NB: is4addr should always be true */ default: goto bad; } if (m->m_flags & M_MORE_DATA) wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; if (addqos) { int ac, tid; if (is4addr) { qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; /* NB: mesh case handled earlier */ } else if (vap->iv_opmode != IEEE80211_M_MBSS) qos = ((struct ieee80211_qosframe *) wh)->i_qos; ac = M_WME_GETAC(m); /* map from access class/queue to 11e header priorty value */ tid = WME_AC_TO_TID(ac); qos[0] = tid & IEEE80211_QOS_TID; if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK; #ifdef IEEE80211_SUPPORT_MESH if (vap->iv_opmode == IEEE80211_M_MBSS) qos[1] = IEEE80211_QOS_MC; else #endif qos[1] = 0; wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; /* * If this is an A-MSDU then ensure we set the * relevant field. */ if (is_amsdu) qos[0] |= IEEE80211_QOS_AMSDU; /* * XXX TODO TX lock is needed for atomic updates of sequence * numbers. If the driver does it, then don't do it here; * and we don't need the TX lock held. */ if ((m->m_flags & M_AMPDU_MPDU) == 0) { /* * 802.11-2012 9.3.2.10 - * * If this is a multicast frame then we need * to ensure that the sequence number comes from * a separate seqno space and not the TID space. * * Otherwise multicast frames may actually cause * holes in the TX blockack window space and * upset various things. */ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; else seqno = ni->ni_txseqs[tid]++; /* * NB: don't assign a sequence # to potential * aggregates; we expect this happens at the * point the frame comes off any aggregation q * as otherwise we may introduce holes in the * BA sequence space and/or make window accouting * more difficult. * * XXX may want to control this with a driver * capability; this may also change when we pull * aggregation up into net80211 */ *(uint16_t *)wh->i_seq = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); } else { /* NB: zero out i_seq field (for s/w encryption etc) */ *(uint16_t *)wh->i_seq = 0; } } else { /* * XXX TODO TX lock is needed for atomic updates of sequence * numbers. If the driver does it, then don't do it here; * and we don't need the TX lock held. */ seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; *(uint16_t *)wh->i_seq = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); /* * XXX TODO: we shouldn't allow EAPOL, etc that would * be forced to be non-QoS traffic to be A-MSDU encapsulated. */ if (is_amsdu) printf("%s: XXX ERROR: is_amsdu set; not QoS!\n", __func__); } /* * Check if xmit fragmentation is required. * * If the hardware does fragmentation offload, then don't bother * doing it here. */ if (IEEE80211_CONF_FRAG_OFFLOAD(ic)) txfrag = 0; else txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold && !IEEE80211_IS_MULTICAST(wh->i_addr1) && (vap->iv_caps & IEEE80211_C_TXFRAG) && (m->m_flags & (M_FF | M_AMPDU_MPDU)) == 0); if (key != NULL) { /* * IEEE 802.1X: send EAPOL frames always in the clear. * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set. */ if ((m->m_flags & M_EAPOL) == 0 || ((vap->iv_flags & IEEE80211_F_WPA) && (vap->iv_opmode == IEEE80211_M_STA ? !IEEE80211_KEY_UNDEFINED(key) : !IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) { wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, eh.ether_dhost, "%s", "enmic failed, discard frame"); vap->iv_stats.is_crypto_enmicfail++; goto bad; } } } if (txfrag && !ieee80211_fragment(vap, m, hdrsize, key != NULL ? key->wk_cipher->ic_header : 0, vap->iv_fragthreshold)) goto bad; m->m_flags |= M_ENCAP; /* mark encapsulated */ IEEE80211_NODE_STAT(ni, tx_data); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { IEEE80211_NODE_STAT(ni, tx_mcast); m->m_flags |= M_MCAST; } else IEEE80211_NODE_STAT(ni, tx_ucast); IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen); return m; bad: if (m != NULL) m_freem(m); return NULL; #undef WH4 #undef MC01 } void ieee80211_free_mbuf(struct mbuf *m) { struct mbuf *next; if (m == NULL) return; do { next = m->m_nextpkt; m->m_nextpkt = NULL; m_freem(m); } while ((m = next) != NULL); } /* * Fragment the frame according to the specified mtu. * The size of the 802.11 header (w/o padding) is provided * so we don't need to recalculate it. We create a new * mbuf for each fragment and chain it through m_nextpkt; * we might be able to optimize this by reusing the original * packet's mbufs but that is significantly more complicated. */ static int ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0, u_int hdrsize, u_int ciphdrsize, u_int mtu) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_frame *wh, *whf; struct mbuf *m, *prev; u_int totalhdrsize, fragno, fragsize, off, remainder, payload; u_int hdrspace; KASSERT(m0->m_nextpkt == NULL, ("mbuf already chained?")); KASSERT(m0->m_pkthdr.len > mtu, ("pktlen %u mtu %u", m0->m_pkthdr.len, mtu)); /* * Honor driver DATAPAD requirement. */ if (ic->ic_flags & IEEE80211_F_DATAPAD) hdrspace = roundup(hdrsize, sizeof(uint32_t)); else hdrspace = hdrsize; wh = mtod(m0, struct ieee80211_frame *); /* NB: mark the first frag; it will be propagated below */ wh->i_fc[1] |= IEEE80211_FC1_MORE_FRAG; totalhdrsize = hdrspace + ciphdrsize; fragno = 1; off = mtu - ciphdrsize; remainder = m0->m_pkthdr.len - off; prev = m0; do { fragsize = MIN(totalhdrsize + remainder, mtu); m = m_get2(fragsize, M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) goto bad; /* leave room to prepend any cipher header */ m_align(m, fragsize - ciphdrsize); /* * Form the header in the fragment. Note that since * we mark the first fragment with the MORE_FRAG bit * it automatically is propagated to each fragment; we * need only clear it on the last fragment (done below). * NB: frag 1+ dont have Mesh Control field present. */ whf = mtod(m, struct ieee80211_frame *); memcpy(whf, wh, hdrsize); #ifdef IEEE80211_SUPPORT_MESH if (vap->iv_opmode == IEEE80211_M_MBSS) ieee80211_getqos(wh)[1] &= ~IEEE80211_QOS_MC; #endif *(uint16_t *)&whf->i_seq[0] |= htole16( (fragno & IEEE80211_SEQ_FRAG_MASK) << IEEE80211_SEQ_FRAG_SHIFT); fragno++; payload = fragsize - totalhdrsize; /* NB: destination is known to be contiguous */ m_copydata(m0, off, payload, mtod(m, uint8_t *) + hdrspace); m->m_len = hdrspace + payload; m->m_pkthdr.len = hdrspace + payload; m->m_flags |= M_FRAG; /* chain up the fragment */ prev->m_nextpkt = m; prev = m; /* deduct fragment just formed */ remainder -= payload; off += payload; } while (remainder != 0); /* set the last fragment */ m->m_flags |= M_LASTFRAG; whf->i_fc[1] &= ~IEEE80211_FC1_MORE_FRAG; /* strip first mbuf now that everything has been copied */ m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize))); m0->m_flags |= M_FIRSTFRAG | M_FRAG; vap->iv_stats.is_tx_fragframes++; vap->iv_stats.is_tx_frags += fragno-1; return 1; bad: /* reclaim fragments but leave original frame for caller to free */ ieee80211_free_mbuf(m0->m_nextpkt); m0->m_nextpkt = NULL; return 0; } /* * Add a supported rates element id to a frame. */ uint8_t * ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs) { int nrates; *frm++ = IEEE80211_ELEMID_RATES; nrates = rs->rs_nrates; if (nrates > IEEE80211_RATE_SIZE) nrates = IEEE80211_RATE_SIZE; *frm++ = nrates; memcpy(frm, rs->rs_rates, nrates); return frm + nrates; } /* * Add an extended supported rates element id to a frame. */ uint8_t * ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) { /* * Add an extended supported rates element if operating in 11g mode. */ if (rs->rs_nrates > IEEE80211_RATE_SIZE) { int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; *frm++ = IEEE80211_ELEMID_XRATES; *frm++ = nrates; memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); frm += nrates; } return frm; } /* * Add an ssid element to a frame. */ uint8_t * ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) { *frm++ = IEEE80211_ELEMID_SSID; *frm++ = len; memcpy(frm, ssid, len); return frm + len; } /* * Add an erp element to a frame. */ static uint8_t * ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic) { uint8_t erp; *frm++ = IEEE80211_ELEMID_ERP; *frm++ = 1; erp = 0; if (ic->ic_nonerpsta != 0) erp |= IEEE80211_ERP_NON_ERP_PRESENT; if (ic->ic_flags & IEEE80211_F_USEPROT) erp |= IEEE80211_ERP_USE_PROTECTION; if (ic->ic_flags & IEEE80211_F_USEBARKER) erp |= IEEE80211_ERP_LONG_PREAMBLE; *frm++ = erp; return frm; } /* * Add a CFParams element to a frame. */ static uint8_t * ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic) { #define ADDSHORT(frm, v) do { \ le16enc(frm, v); \ frm += 2; \ } while (0) *frm++ = IEEE80211_ELEMID_CFPARMS; *frm++ = 6; *frm++ = 0; /* CFP count */ *frm++ = 2; /* CFP period */ ADDSHORT(frm, 0); /* CFP MaxDuration (TU) */ ADDSHORT(frm, 0); /* CFP CurRemaining (TU) */ return frm; #undef ADDSHORT } static __inline uint8_t * add_appie(uint8_t *frm, const struct ieee80211_appie *ie) { memcpy(frm, ie->ie_data, ie->ie_len); return frm + ie->ie_len; } static __inline uint8_t * add_ie(uint8_t *frm, const uint8_t *ie) { memcpy(frm, ie, 2 + ie[1]); return frm + 2 + ie[1]; } #define WME_OUI_BYTES 0x00, 0x50, 0xf2 /* * Add a WME information element to a frame. */ uint8_t * ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme) { static const struct ieee80211_wme_info info = { .wme_id = IEEE80211_ELEMID_VENDOR, .wme_len = sizeof(struct ieee80211_wme_info) - 2, .wme_oui = { WME_OUI_BYTES }, .wme_type = WME_OUI_TYPE, .wme_subtype = WME_INFO_OUI_SUBTYPE, .wme_version = WME_VERSION, .wme_info = 0, }; memcpy(frm, &info, sizeof(info)); return frm + sizeof(info); } /* * Add a WME parameters element to a frame. */ static uint8_t * ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme) { #define SM(_v, _f) (((_v) << _f##_S) & _f) #define ADDSHORT(frm, v) do { \ le16enc(frm, v); \ frm += 2; \ } while (0) /* NB: this works 'cuz a param has an info at the front */ static const struct ieee80211_wme_info param = { .wme_id = IEEE80211_ELEMID_VENDOR, .wme_len = sizeof(struct ieee80211_wme_param) - 2, .wme_oui = { WME_OUI_BYTES }, .wme_type = WME_OUI_TYPE, .wme_subtype = WME_PARAM_OUI_SUBTYPE, .wme_version = WME_VERSION, }; int i; memcpy(frm, ¶m, sizeof(param)); frm += __offsetof(struct ieee80211_wme_info, wme_info); *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */ *frm++ = 0; /* reserved field */ for (i = 0; i < WME_NUM_AC; i++) { const struct wmeParams *ac = &wme->wme_bssChanParams.cap_wmeParams[i]; *frm++ = SM(i, WME_PARAM_ACI) | SM(ac->wmep_acm, WME_PARAM_ACM) | SM(ac->wmep_aifsn, WME_PARAM_AIFSN) ; *frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX) | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN) ; ADDSHORT(frm, ac->wmep_txopLimit); } return frm; #undef SM #undef ADDSHORT } #undef WME_OUI_BYTES /* * Add an 11h Power Constraint element to a frame. */ static uint8_t * ieee80211_add_powerconstraint(uint8_t *frm, struct ieee80211vap *vap) { const struct ieee80211_channel *c = vap->iv_bss->ni_chan; /* XXX per-vap tx power limit? */ int8_t limit = vap->iv_ic->ic_txpowlimit / 2; frm[0] = IEEE80211_ELEMID_PWRCNSTR; frm[1] = 1; frm[2] = c->ic_maxregpower > limit ? c->ic_maxregpower - limit : 0; return frm + 3; } /* * Add an 11h Power Capability element to a frame. */ static uint8_t * ieee80211_add_powercapability(uint8_t *frm, const struct ieee80211_channel *c) { frm[0] = IEEE80211_ELEMID_PWRCAP; frm[1] = 2; frm[2] = c->ic_minpower; frm[3] = c->ic_maxpower; return frm + 4; } /* * Add an 11h Supported Channels element to a frame. */ static uint8_t * ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic) { static const int ielen = 26; frm[0] = IEEE80211_ELEMID_SUPPCHAN; frm[1] = ielen; /* XXX not correct */ memcpy(frm+2, ic->ic_chan_avail, ielen); return frm + 2 + ielen; } /* * Add an 11h Quiet time element to a frame. */ static uint8_t * ieee80211_add_quiet(uint8_t *frm, struct ieee80211vap *vap, int update) { struct ieee80211_quiet_ie *quiet = (struct ieee80211_quiet_ie *) frm; quiet->quiet_ie = IEEE80211_ELEMID_QUIET; quiet->len = 6; /* * Only update every beacon interval - otherwise probe responses * would update the quiet count value. */ if (update) { if (vap->iv_quiet_count_value == 1) vap->iv_quiet_count_value = vap->iv_quiet_count; else if (vap->iv_quiet_count_value > 1) vap->iv_quiet_count_value--; } if (vap->iv_quiet_count_value == 0) { /* value 0 is reserved as per 802.11h standerd */ vap->iv_quiet_count_value = 1; } quiet->tbttcount = vap->iv_quiet_count_value; quiet->period = vap->iv_quiet_period; quiet->duration = htole16(vap->iv_quiet_duration); quiet->offset = htole16(vap->iv_quiet_offset); return frm + sizeof(*quiet); } /* * Add an 11h Channel Switch Announcement element to a frame. * Note that we use the per-vap CSA count to adjust the global * counter so we can use this routine to form probe response * frames and get the current count. */ static uint8_t * ieee80211_add_csa(uint8_t *frm, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_csa_ie *csa = (struct ieee80211_csa_ie *) frm; csa->csa_ie = IEEE80211_ELEMID_CSA; csa->csa_len = 3; csa->csa_mode = 1; /* XXX force quiet on channel */ csa->csa_newchan = ieee80211_chan2ieee(ic, ic->ic_csa_newchan); csa->csa_count = ic->ic_csa_count - vap->iv_csa_count; return frm + sizeof(*csa); } /* * Add an 11h country information element to a frame. */ static uint8_t * ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic) { if (ic->ic_countryie == NULL || ic->ic_countryie_chan != ic->ic_bsschan) { /* * Handle lazy construction of ie. This is done on * first use and after a channel change that requires * re-calculation. */ if (ic->ic_countryie != NULL) IEEE80211_FREE(ic->ic_countryie, M_80211_NODE_IE); ic->ic_countryie = ieee80211_alloc_countryie(ic); if (ic->ic_countryie == NULL) return frm; ic->ic_countryie_chan = ic->ic_bsschan; } return add_appie(frm, ic->ic_countryie); } uint8_t * ieee80211_add_wpa(uint8_t *frm, const struct ieee80211vap *vap) { if (vap->iv_flags & IEEE80211_F_WPA1 && vap->iv_wpa_ie != NULL) return (add_ie(frm, vap->iv_wpa_ie)); else { /* XXX else complain? */ return (frm); } } uint8_t * ieee80211_add_rsn(uint8_t *frm, const struct ieee80211vap *vap) { if (vap->iv_flags & IEEE80211_F_WPA2 && vap->iv_rsn_ie != NULL) return (add_ie(frm, vap->iv_rsn_ie)); else { /* XXX else complain? */ return (frm); } } uint8_t * ieee80211_add_qos(uint8_t *frm, const struct ieee80211_node *ni) { if (ni->ni_flags & IEEE80211_NODE_QOS) { *frm++ = IEEE80211_ELEMID_QOS; *frm++ = 1; *frm++ = 0; } return (frm); } /* * Send a probe request frame with the specified ssid * and any optional information element data. */ int ieee80211_send_probereq(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN], const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t *ssid, size_t ssidlen) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_node *bss; const struct ieee80211_txparam *tp; struct ieee80211_bpf_params params; const struct ieee80211_rateset *rs; struct mbuf *m; uint8_t *frm; int ret; bss = ieee80211_ref_node(vap->iv_bss); if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, "block %s frame in CAC state", "probe request"); vap->iv_stats.is_tx_badstate++; ieee80211_free_node(bss); return EIO; /* XXX */ } /* * Hold a reference on the node so it doesn't go away until after * the xmit is complete all the way in the driver. On error we * will remove our reference. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); ieee80211_ref_node(ni); /* * prreq frame format * [tlv] ssid * [tlv] supported rates * [tlv] RSN (optional) * [tlv] extended supported rates * [tlv] HT cap (optional) * [tlv] VHT cap (optional) * [tlv] WPA (optional) * [tlv] user-specified ie's */ m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + sizeof(struct ieee80211_ie_htcap) + sizeof(struct ieee80211_ie_vhtcap) + sizeof(struct ieee80211_ie_htinfo) /* XXX not needed? */ + sizeof(struct ieee80211_ie_wpa) + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + sizeof(struct ieee80211_ie_wpa) + (vap->iv_appie_probereq != NULL ? vap->iv_appie_probereq->ie_len : 0) ); if (m == NULL) { vap->iv_stats.is_tx_nobuf++; ieee80211_free_node(ni); ieee80211_free_node(bss); return ENOMEM; } frm = ieee80211_add_ssid(frm, ssid, ssidlen); rs = ieee80211_get_suprates(ic, ic->ic_curchan); frm = ieee80211_add_rates(frm, rs); frm = ieee80211_add_rsn(frm, vap); frm = ieee80211_add_xrates(frm, rs); /* * Note: we can't use bss; we don't have one yet. * * So, we should announce our capabilities * in this channel mode (2g/5g), not the * channel details itself. */ if ((vap->iv_opmode == IEEE80211_M_IBSS) && (vap->iv_flags_ht & IEEE80211_FHT_HT)) { struct ieee80211_channel *c; /* * Get the HT channel that we should try upgrading to. * If we can do 40MHz then this'll upgrade it appropriately. */ c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, vap->iv_flags_ht); frm = ieee80211_add_htcap_ch(frm, vap, c); } /* * XXX TODO: need to figure out what/how to update the * VHT channel. */ #if 0 (vap->iv_flags_vht & IEEE80211_FVHT_VHT) { struct ieee80211_channel *c; c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, vap->iv_flags_ht); c = ieee80211_vht_adjust_channel(ic, c, vap->iv_flags_vht); frm = ieee80211_add_vhtcap_ch(frm, vap, c); } #endif frm = ieee80211_add_wpa(frm, vap); if (vap->iv_appie_probereq != NULL) frm = add_appie(frm, vap->iv_appie_probereq); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); KASSERT(M_LEADINGSPACE(m) >= sizeof(struct ieee80211_frame), ("leading space %zd", M_LEADINGSPACE(m))); M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); if (m == NULL) { /* NB: cannot happen */ ieee80211_free_node(ni); ieee80211_free_node(bss); return ENOMEM; } IEEE80211_TX_LOCK(ic); ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, IEEE80211_NONQOS_TID, sa, da, bssid); /* XXX power management? */ m->m_flags |= M_ENCAP; /* mark encapsulated */ M_WME_SETAC(m, WME_AC_BE); IEEE80211_NODE_STAT(ni, tx_probereq); IEEE80211_NODE_STAT(ni, tx_mgmt); IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, "send probe req on channel %u bssid %s sa %6D da %6D ssid \"%.*s\"\n", ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid), sa, ":", da, ":", ssidlen, ssid); memset(¶ms, 0, sizeof(params)); params.ibp_pri = M_WME_GETAC(m); tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; params.ibp_rate0 = tp->mgmtrate; if (IEEE80211_IS_MULTICAST(da)) { params.ibp_flags |= IEEE80211_BPF_NOACK; params.ibp_try0 = 1; } else params.ibp_try0 = tp->maxretry; params.ibp_power = ni->ni_txpower; ret = ieee80211_raw_output(vap, ni, m, ¶ms); IEEE80211_TX_UNLOCK(ic); ieee80211_free_node(bss); return (ret); } /* * Calculate capability information for mgt frames. */ uint16_t ieee80211_getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan) { struct ieee80211com *ic = vap->iv_ic; uint16_t capinfo; KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode")); if (vap->iv_opmode == IEEE80211_M_HOSTAP) capinfo = IEEE80211_CAPINFO_ESS; else if (vap->iv_opmode == IEEE80211_M_IBSS) capinfo = IEEE80211_CAPINFO_IBSS; else capinfo = 0; if (vap->iv_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && IEEE80211_IS_CHAN_2GHZ(chan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; if (ic->ic_flags & IEEE80211_F_SHSLOT) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; if (IEEE80211_IS_CHAN_5GHZ(chan) && (vap->iv_flags & IEEE80211_F_DOTH)) capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; return capinfo; } /* * Send a management frame. The node is for the destination (or ic_bss * when in station mode). Nodes other than ic_bss have their reference * count bumped to reflect our use for an indeterminant time. */ int ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) { #define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT) #define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_node *bss = vap->iv_bss; struct ieee80211_bpf_params params; struct mbuf *m; uint8_t *frm; uint16_t capinfo; int has_challenge, is_shared_key, ret, status; KASSERT(ni != NULL, ("null node")); /* * Hold a reference on the node so it doesn't go away until after * the xmit is complete all the way in the driver. On error we * will remove our reference. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); ieee80211_ref_node(ni); memset(¶ms, 0, sizeof(params)); switch (type) { case IEEE80211_FC0_SUBTYPE_AUTH: status = arg >> 16; arg &= 0xffff; has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || arg == IEEE80211_AUTH_SHARED_RESPONSE) && ni->ni_challenge != NULL); /* * Deduce whether we're doing open authentication or * shared key authentication. We do the latter if * we're in the middle of a shared key authentication * handshake or if we're initiating an authentication * request and configured to use shared key. */ is_shared_key = has_challenge || arg >= IEEE80211_AUTH_SHARED_RESPONSE || (arg == IEEE80211_AUTH_SHARED_REQUEST && bss->ni_authmode == IEEE80211_AUTH_SHARED); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), 3 * sizeof(uint16_t) + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); ((uint16_t *)frm)[0] = (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) : htole16(IEEE80211_AUTH_ALG_OPEN); ((uint16_t *)frm)[1] = htole16(arg); /* sequence number */ ((uint16_t *)frm)[2] = htole16(status);/* status */ if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { ((uint16_t *)frm)[3] = htole16((IEEE80211_CHALLENGE_LEN << 8) | IEEE80211_ELEMID_CHALLENGE); memcpy(&((uint16_t *)frm)[4], ni->ni_challenge, IEEE80211_CHALLENGE_LEN); m->m_pkthdr.len = m->m_len = 4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN; if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, "request encrypt frame (%s)", __func__); /* mark frame for encryption */ params.ibp_flags |= IEEE80211_BPF_CRYPTO; } } else m->m_pkthdr.len = m->m_len = 3 * sizeof(uint16_t); /* XXX not right for shared key */ if (status == IEEE80211_STATUS_SUCCESS) IEEE80211_NODE_STAT(ni, tx_auth); else IEEE80211_NODE_STAT(ni, tx_auth_fail); if (vap->iv_opmode == IEEE80211_M_STA) ieee80211_add_callback(m, ieee80211_tx_mgt_cb, (void *) vap->iv_state); break; case IEEE80211_FC0_SUBTYPE_DEAUTH: IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni, "send station deauthenticate (reason: %d (%s))", arg, ieee80211_reason_to_string(arg)); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t)); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); *(uint16_t *)frm = htole16(arg); /* reason */ m->m_pkthdr.len = m->m_len = sizeof(uint16_t); IEEE80211_NODE_STAT(ni, tx_deauth); IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg); ieee80211_node_unauthorize(ni); /* port closed */ break; case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: /* * asreq frame format * [2] capability information * [2] listen interval * [6*] current AP address (reassoc only) * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates * [4] power capability (optional) * [28] supported channels (optional) * [tlv] HT capabilities * [tlv] VHT capabilities * [tlv] WME (optional) * [tlv] Vendor OUI HT capabilities (optional) * [tlv] Atheros capabilities (if negotiated) * [tlv] AppIE's (optional) */ m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t) + sizeof(uint16_t) + IEEE80211_ADDR_LEN + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + 4 + 2 + 26 + sizeof(struct ieee80211_wme_info) + sizeof(struct ieee80211_ie_htcap) + sizeof(struct ieee80211_ie_vhtcap) + 4 + sizeof(struct ieee80211_ie_htcap) #ifdef IEEE80211_SUPPORT_SUPERG + sizeof(struct ieee80211_ath_ie) #endif + (vap->iv_appie_wpa != NULL ? vap->iv_appie_wpa->ie_len : 0) + (vap->iv_appie_assocreq != NULL ? vap->iv_appie_assocreq->ie_len : 0) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode %u", vap->iv_opmode)); capinfo = IEEE80211_CAPINFO_ESS; if (vap->iv_flags & IEEE80211_F_PRIVACY) capinfo |= IEEE80211_CAPINFO_PRIVACY; /* * NB: Some 11a AP's reject the request when * short preamble is set. */ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && (ic->ic_caps & IEEE80211_C_SHSLOT)) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) && (vap->iv_flags & IEEE80211_F_DOTH)) capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; *(uint16_t *)frm = htole16(capinfo); frm += 2; KASSERT(bss->ni_intval != 0, ("beacon interval is zero!")); *(uint16_t *)frm = htole16(howmany(ic->ic_lintval, bss->ni_intval)); frm += 2; if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { IEEE80211_ADDR_COPY(frm, bss->ni_bssid); frm += IEEE80211_ADDR_LEN; } frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); frm = ieee80211_add_rates(frm, &ni->ni_rates); frm = ieee80211_add_rsn(frm, vap); frm = ieee80211_add_xrates(frm, &ni->ni_rates); if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) { frm = ieee80211_add_powercapability(frm, ic->ic_curchan); frm = ieee80211_add_supportedchannels(frm, ic); } /* * Check the channel - we may be using an 11n NIC with an * 11n capable station, but we're configured to be an 11b * channel. */ if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_ies.htcap_ie != NULL && ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) { frm = ieee80211_add_htcap(frm, ni); } if ((vap->iv_flags_vht & IEEE80211_FVHT_VHT) && IEEE80211_IS_CHAN_VHT(ni->ni_chan) && ni->ni_ies.vhtcap_ie != NULL && ni->ni_ies.vhtcap_ie[0] == IEEE80211_ELEMID_VHT_CAP) { frm = ieee80211_add_vhtcap(frm, ni); } frm = ieee80211_add_wpa(frm, vap); if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) frm = ieee80211_add_wme_info(frm, &ic->ic_wme); /* * Same deal - only send HT info if we're on an 11n * capable channel. */ if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_ies.htcap_ie != NULL && ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR) { frm = ieee80211_add_htcap_vendor(frm, ni); } #ifdef IEEE80211_SUPPORT_SUPERG if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) { frm = ieee80211_add_ath(frm, IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), ((vap->iv_flags & IEEE80211_F_WPA) == 0 && ni->ni_authmode != IEEE80211_AUTH_8021X) ? vap->iv_def_txkey : IEEE80211_KEYIX_NONE); } #endif /* IEEE80211_SUPPORT_SUPERG */ if (vap->iv_appie_assocreq != NULL) frm = add_appie(frm, vap->iv_appie_assocreq); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); ieee80211_add_callback(m, ieee80211_tx_mgt_cb, (void *) vap->iv_state); break; case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: /* * asresp frame format * [2] capability information * [2] status * [2] association ID * [tlv] supported rates * [tlv] extended supported rates * [tlv] HT capabilities (standard, if STA enabled) * [tlv] HT information (standard, if STA enabled) * [tlv] VHT capabilities (standard, if STA enabled) * [tlv] VHT information (standard, if STA enabled) * [tlv] WME (if configured and STA enabled) * [tlv] HT capabilities (vendor OUI, if STA enabled) * [tlv] HT information (vendor OUI, if STA enabled) * [tlv] Atheros capabilities (if STA enabled) * [tlv] AppIE's (optional) */ m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + 2 + IEEE80211_RATE_SIZE + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + sizeof(struct ieee80211_ie_htcap) + 4 + sizeof(struct ieee80211_ie_htinfo) + 4 + sizeof(struct ieee80211_ie_vhtcap) + sizeof(struct ieee80211_ie_vht_operation) + sizeof(struct ieee80211_wme_param) #ifdef IEEE80211_SUPPORT_SUPERG + sizeof(struct ieee80211_ath_ie) #endif + (vap->iv_appie_assocresp != NULL ? vap->iv_appie_assocresp->ie_len : 0) ); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); capinfo = ieee80211_getcapinfo(vap, bss->ni_chan); *(uint16_t *)frm = htole16(capinfo); frm += 2; *(uint16_t *)frm = htole16(arg); /* status */ frm += 2; if (arg == IEEE80211_STATUS_SUCCESS) { *(uint16_t *)frm = htole16(ni->ni_associd); IEEE80211_NODE_STAT(ni, tx_assoc); } else IEEE80211_NODE_STAT(ni, tx_assoc_fail); frm += 2; frm = ieee80211_add_rates(frm, &ni->ni_rates); frm = ieee80211_add_xrates(frm, &ni->ni_rates); /* NB: respond according to what we received */ if ((ni->ni_flags & HTFLAGS) == IEEE80211_NODE_HT) { frm = ieee80211_add_htcap(frm, ni); frm = ieee80211_add_htinfo(frm, ni); } if ((vap->iv_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) frm = ieee80211_add_wme_param(frm, &ic->ic_wme); if ((ni->ni_flags & HTFLAGS) == HTFLAGS) { frm = ieee80211_add_htcap_vendor(frm, ni); frm = ieee80211_add_htinfo_vendor(frm, ni); } if (ni->ni_flags & IEEE80211_NODE_VHT) { frm = ieee80211_add_vhtcap(frm, ni); frm = ieee80211_add_vhtinfo(frm, ni); } #ifdef IEEE80211_SUPPORT_SUPERG if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) frm = ieee80211_add_ath(frm, IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS), ((vap->iv_flags & IEEE80211_F_WPA) == 0 && ni->ni_authmode != IEEE80211_AUTH_8021X) ? vap->iv_def_txkey : IEEE80211_KEYIX_NONE); #endif /* IEEE80211_SUPPORT_SUPERG */ if (vap->iv_appie_assocresp != NULL) frm = add_appie(frm, vap->iv_appie_assocresp); m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); break; case IEEE80211_FC0_SUBTYPE_DISASSOC: IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, "send station disassociate (reason: %d (%s))", arg, ieee80211_reason_to_string(arg)); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t)); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); *(uint16_t *)frm = htole16(arg); /* reason */ m->m_pkthdr.len = m->m_len = sizeof(uint16_t); IEEE80211_NODE_STAT(ni, tx_disassoc); IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg); break; default: IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, "invalid mgmt frame type %u", type); senderr(EINVAL, is_tx_unknownmgt); /* NOTREACHED */ } /* NB: force non-ProbeResp frames to the highest queue */ params.ibp_pri = WME_AC_VO; params.ibp_rate0 = bss->ni_txparms->mgmtrate; /* NB: we know all frames are unicast */ params.ibp_try0 = bss->ni_txparms->maxretry; params.ibp_power = bss->ni_txpower; return ieee80211_mgmt_output(ni, m, type, ¶ms); bad: ieee80211_free_node(ni); return ret; #undef senderr #undef HTFLAGS } /* * Return an mbuf with a probe response frame in it. * Space is left to prepend and 802.11 header at the * front but it's left to the caller to fill in. */ struct mbuf * ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) { struct ieee80211vap *vap = bss->ni_vap; struct ieee80211com *ic = bss->ni_ic; const struct ieee80211_rateset *rs; struct mbuf *m; uint16_t capinfo; uint8_t *frm; /* * probe response frame format * [8] time stamp * [2] beacon interval * [2] cabability information * [tlv] ssid * [tlv] supported rates * [tlv] parameter set (FH/DS) * [tlv] parameter set (IBSS) * [tlv] country (optional) * [3] power control (optional) * [5] channel switch announcement (CSA) (optional) * [tlv] extended rate phy (ERP) * [tlv] extended supported rates * [tlv] RSN (optional) * [tlv] HT capabilities * [tlv] HT information * [tlv] VHT capabilities * [tlv] VHT information * [tlv] WPA (optional) * [tlv] WME (optional) * [tlv] Vendor OUI HT capabilities (optional) * [tlv] Vendor OUI HT information (optional) * [tlv] Atheros capabilities * [tlv] AppIE's (optional) * [tlv] Mesh ID (MBSS) * [tlv] Mesh Conf (MBSS) */ m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), 8 + sizeof(uint16_t) + sizeof(uint16_t) + 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + 7 /* max(7,3) */ + IEEE80211_COUNTRY_MAX_SIZE + 3 + sizeof(struct ieee80211_csa_ie) + sizeof(struct ieee80211_quiet_ie) + 3 + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + sizeof(struct ieee80211_ie_wpa) + sizeof(struct ieee80211_ie_htcap) + sizeof(struct ieee80211_ie_htinfo) + sizeof(struct ieee80211_ie_wpa) + sizeof(struct ieee80211_wme_param) + 4 + sizeof(struct ieee80211_ie_htcap) + 4 + sizeof(struct ieee80211_ie_htinfo) + sizeof(struct ieee80211_ie_vhtcap) + sizeof(struct ieee80211_ie_vht_operation) #ifdef IEEE80211_SUPPORT_SUPERG + sizeof(struct ieee80211_ath_ie) #endif #ifdef IEEE80211_SUPPORT_MESH + 2 + IEEE80211_MESHID_LEN + sizeof(struct ieee80211_meshconf_ie) #endif + (vap->iv_appie_proberesp != NULL ? vap->iv_appie_proberesp->ie_len : 0) ); if (m == NULL) { vap->iv_stats.is_tx_nobuf++; return NULL; } memset(frm, 0, 8); /* timestamp should be filled later */ frm += 8; *(uint16_t *)frm = htole16(bss->ni_intval); frm += 2; capinfo = ieee80211_getcapinfo(vap, bss->ni_chan); *(uint16_t *)frm = htole16(capinfo); frm += 2; frm = ieee80211_add_ssid(frm, bss->ni_essid, bss->ni_esslen); rs = ieee80211_get_suprates(ic, bss->ni_chan); frm = ieee80211_add_rates(frm, rs); if (IEEE80211_IS_CHAN_FHSS(bss->ni_chan)) { *frm++ = IEEE80211_ELEMID_FHPARMS; *frm++ = 5; *frm++ = bss->ni_fhdwell & 0x00ff; *frm++ = (bss->ni_fhdwell >> 8) & 0x00ff; *frm++ = IEEE80211_FH_CHANSET( ieee80211_chan2ieee(ic, bss->ni_chan)); *frm++ = IEEE80211_FH_CHANPAT( ieee80211_chan2ieee(ic, bss->ni_chan)); *frm++ = bss->ni_fhindex; } else { *frm++ = IEEE80211_ELEMID_DSPARMS; *frm++ = 1; *frm++ = ieee80211_chan2ieee(ic, bss->ni_chan); } if (vap->iv_opmode == IEEE80211_M_IBSS) { *frm++ = IEEE80211_ELEMID_IBSSPARMS; *frm++ = 2; *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ } if ((vap->iv_flags & IEEE80211_F_DOTH) || (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) frm = ieee80211_add_countryie(frm, ic); if (vap->iv_flags & IEEE80211_F_DOTH) { if (IEEE80211_IS_CHAN_5GHZ(bss->ni_chan)) frm = ieee80211_add_powerconstraint(frm, vap); if (ic->ic_flags & IEEE80211_F_CSAPENDING) frm = ieee80211_add_csa(frm, vap); } if (vap->iv_flags & IEEE80211_F_DOTH) { if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) { if (vap->iv_quiet) frm = ieee80211_add_quiet(frm, vap, 0); } } if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan)) frm = ieee80211_add_erp(frm, ic); frm = ieee80211_add_xrates(frm, rs); frm = ieee80211_add_rsn(frm, vap); /* * NB: legacy 11b clients do not get certain ie's. * The caller identifies such clients by passing * a token in legacy to us. Could expand this to be * any legacy client for stuff like HT ie's. */ if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && legacy != IEEE80211_SEND_LEGACY_11B) { frm = ieee80211_add_htcap(frm, bss); frm = ieee80211_add_htinfo(frm, bss); } if (IEEE80211_IS_CHAN_VHT(bss->ni_chan) && legacy != IEEE80211_SEND_LEGACY_11B) { frm = ieee80211_add_vhtcap(frm, bss); frm = ieee80211_add_vhtinfo(frm, bss); } frm = ieee80211_add_wpa(frm, vap); if (vap->iv_flags & IEEE80211_F_WME) frm = ieee80211_add_wme_param(frm, &ic->ic_wme); if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) && legacy != IEEE80211_SEND_LEGACY_11B) { frm = ieee80211_add_htcap_vendor(frm, bss); frm = ieee80211_add_htinfo_vendor(frm, bss); } #ifdef IEEE80211_SUPPORT_SUPERG if ((vap->iv_flags & IEEE80211_F_ATHEROS) && legacy != IEEE80211_SEND_LEGACY_11B) frm = ieee80211_add_athcaps(frm, bss); #endif if (vap->iv_appie_proberesp != NULL) frm = add_appie(frm, vap->iv_appie_proberesp); #ifdef IEEE80211_SUPPORT_MESH if (vap->iv_opmode == IEEE80211_M_MBSS) { frm = ieee80211_add_meshid(frm, vap); frm = ieee80211_add_meshconf(frm, vap); } #endif m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); return m; } /* * Send a probe response frame to the specified mac address. * This does not go through the normal mgt frame api so we * can specify the destination address and re-use the bss node * for the sta reference. */ int ieee80211_send_proberesp(struct ieee80211vap *vap, const uint8_t da[IEEE80211_ADDR_LEN], int legacy) { struct ieee80211_node *bss = vap->iv_bss; struct ieee80211com *ic = vap->iv_ic; struct mbuf *m; int ret; if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss, "block %s frame in CAC state", "probe response"); vap->iv_stats.is_tx_badstate++; return EIO; /* XXX */ } /* * Hold a reference on the node so it doesn't go away until after * the xmit is complete all the way in the driver. On error we * will remove our reference. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, bss, ether_sprintf(bss->ni_macaddr), ieee80211_node_refcnt(bss)+1); ieee80211_ref_node(bss); m = ieee80211_alloc_proberesp(bss, legacy); if (m == NULL) { ieee80211_free_node(bss); return ENOMEM; } M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); KASSERT(m != NULL, ("no room for header")); IEEE80211_TX_LOCK(ic); ieee80211_send_setup(bss, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP, IEEE80211_NONQOS_TID, vap->iv_myaddr, da, bss->ni_bssid); /* XXX power management? */ m->m_flags |= M_ENCAP; /* mark encapsulated */ M_WME_SETAC(m, WME_AC_BE); IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, "send probe resp on channel %u to %s%s\n", ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(da), legacy ? " " : ""); IEEE80211_NODE_STAT(bss, tx_mgmt); ret = ieee80211_raw_output(vap, bss, m, NULL); IEEE80211_TX_UNLOCK(ic); return (ret); } /* * Allocate and build a RTS (Request To Send) control frame. */ struct mbuf * ieee80211_alloc_rts(struct ieee80211com *ic, const uint8_t ra[IEEE80211_ADDR_LEN], const uint8_t ta[IEEE80211_ADDR_LEN], uint16_t dur) { struct ieee80211_frame_rts *rts; struct mbuf *m; /* XXX honor ic_headroom */ m = m_gethdr(M_NOWAIT, MT_DATA); if (m != NULL) { rts = mtod(m, struct ieee80211_frame_rts *); rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_RTS; rts->i_fc[1] = IEEE80211_FC1_DIR_NODS; *(u_int16_t *)rts->i_dur = htole16(dur); IEEE80211_ADDR_COPY(rts->i_ra, ra); IEEE80211_ADDR_COPY(rts->i_ta, ta); m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts); } return m; } /* * Allocate and build a CTS (Clear To Send) control frame. */ struct mbuf * ieee80211_alloc_cts(struct ieee80211com *ic, const uint8_t ra[IEEE80211_ADDR_LEN], uint16_t dur) { struct ieee80211_frame_cts *cts; struct mbuf *m; /* XXX honor ic_headroom */ m = m_gethdr(M_NOWAIT, MT_DATA); if (m != NULL) { cts = mtod(m, struct ieee80211_frame_cts *); cts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_CTS; cts->i_fc[1] = IEEE80211_FC1_DIR_NODS; *(u_int16_t *)cts->i_dur = htole16(dur); IEEE80211_ADDR_COPY(cts->i_ra, ra); m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts); } return m; } /* * Wrapper for CTS/RTS frame allocation. */ struct mbuf * ieee80211_alloc_prot(struct ieee80211_node *ni, const struct mbuf *m, uint8_t rate, int prot) { struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_frame *wh; struct mbuf *mprot; uint16_t dur; int pktlen, isshort; KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, ("wrong protection type %d", prot)); wh = mtod(m, const struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort) + ieee80211_ack_duration(ic->ic_rt, rate, isshort); if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort); mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); return (mprot); } static void ieee80211_tx_mgt_timeout(void *arg) { struct ieee80211vap *vap = arg; IEEE80211_LOCK(vap->iv_ic); if (vap->iv_state != IEEE80211_S_INIT && (vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0) { /* * NB: it's safe to specify a timeout as the reason here; * it'll only be used in the right state. */ ieee80211_new_state_locked(vap, IEEE80211_S_SCAN, IEEE80211_SCAN_FAIL_TIMEOUT); } IEEE80211_UNLOCK(vap->iv_ic); } /* * This is the callback set on net80211-sourced transmitted * authentication request frames. * * This does a couple of things: * * + If the frame transmitted was a success, it schedules a future * event which will transition the interface to scan. * If a state transition _then_ occurs before that event occurs, * said state transition will cancel this callout. * * + If the frame transmit was a failure, it immediately schedules * the transition back to scan. */ static void ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) { struct ieee80211vap *vap = ni->ni_vap; - enum ieee80211_state ostate = (enum ieee80211_state) arg; + enum ieee80211_state ostate = (enum ieee80211_state)(uintptr_t)arg; /* * Frame transmit completed; arrange timer callback. If * transmit was successfully we wait for response. Otherwise * we arrange an immediate callback instead of doing the * callback directly since we don't know what state the driver * is in (e.g. what locks it is holding). This work should * not be too time-critical and not happen too often so the * added overhead is acceptable. * * XXX what happens if !acked but response shows up before callback? */ if (vap->iv_state == ostate) { callout_reset(&vap->iv_mgtsend, status == 0 ? IEEE80211_TRANS_WAIT*hz : 0, ieee80211_tx_mgt_timeout, vap); } } static void ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_rateset *rs = &ni->ni_rates; uint16_t capinfo; /* * beacon frame format * * TODO: update to 802.11-2012; a lot of stuff has changed; * vendor extensions should be at the end, etc. * * [8] time stamp * [2] beacon interval * [2] cabability information * [tlv] ssid * [tlv] supported rates * [3] parameter set (DS) * [8] CF parameter set (optional) * [tlv] parameter set (IBSS/TIM) * [tlv] country (optional) * [3] power control (optional) * [5] channel switch announcement (CSA) (optional) * XXX TODO: Quiet * XXX TODO: IBSS DFS * XXX TODO: TPC report * [tlv] extended rate phy (ERP) * [tlv] extended supported rates * [tlv] RSN parameters * XXX TODO: BSSLOAD * (XXX EDCA parameter set, QoS capability?) * XXX TODO: AP channel report * * [tlv] HT capabilities * [tlv] HT information * XXX TODO: 20/40 BSS coexistence * Mesh: * XXX TODO: Meshid * XXX TODO: mesh config * XXX TODO: mesh awake window * XXX TODO: beacon timing (mesh, etc) * XXX TODO: MCCAOP Advertisement Overview * XXX TODO: MCCAOP Advertisement * XXX TODO: Mesh channel switch parameters * VHT: * XXX TODO: VHT capabilities * XXX TODO: VHT operation * XXX TODO: VHT transmit power envelope * XXX TODO: channel switch wrapper element * XXX TODO: extended BSS load element * * XXX Vendor-specific OIDs (e.g. Atheros) * [tlv] WPA parameters * [tlv] WME parameters * [tlv] Vendor OUI HT capabilities (optional) * [tlv] Vendor OUI HT information (optional) * [tlv] Atheros capabilities (optional) * [tlv] TDMA parameters (optional) * [tlv] Mesh ID (MBSS) * [tlv] Mesh Conf (MBSS) * [tlv] application data (optional) */ memset(bo, 0, sizeof(*bo)); memset(frm, 0, 8); /* XXX timestamp is set by hardware/driver */ frm += 8; *(uint16_t *)frm = htole16(ni->ni_intval); frm += 2; capinfo = ieee80211_getcapinfo(vap, ni->ni_chan); bo->bo_caps = (uint16_t *)frm; *(uint16_t *)frm = htole16(capinfo); frm += 2; *frm++ = IEEE80211_ELEMID_SSID; if ((vap->iv_flags & IEEE80211_F_HIDESSID) == 0) { *frm++ = ni->ni_esslen; memcpy(frm, ni->ni_essid, ni->ni_esslen); frm += ni->ni_esslen; } else *frm++ = 0; frm = ieee80211_add_rates(frm, rs); if (!IEEE80211_IS_CHAN_FHSS(ni->ni_chan)) { *frm++ = IEEE80211_ELEMID_DSPARMS; *frm++ = 1; *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); } if (ic->ic_flags & IEEE80211_F_PCF) { bo->bo_cfp = frm; frm = ieee80211_add_cfparms(frm, ic); } bo->bo_tim = frm; if (vap->iv_opmode == IEEE80211_M_IBSS) { *frm++ = IEEE80211_ELEMID_IBSSPARMS; *frm++ = 2; *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ bo->bo_tim_len = 0; } else if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_MBSS) { /* TIM IE is the same for Mesh and Hostap */ struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm; tie->tim_ie = IEEE80211_ELEMID_TIM; tie->tim_len = 4; /* length */ tie->tim_count = 0; /* DTIM count */ tie->tim_period = vap->iv_dtim_period; /* DTIM period */ tie->tim_bitctl = 0; /* bitmap control */ tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */ frm += sizeof(struct ieee80211_tim_ie); bo->bo_tim_len = 1; } bo->bo_tim_trailer = frm; if ((vap->iv_flags & IEEE80211_F_DOTH) || (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) frm = ieee80211_add_countryie(frm, ic); if (vap->iv_flags & IEEE80211_F_DOTH) { if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) frm = ieee80211_add_powerconstraint(frm, vap); bo->bo_csa = frm; if (ic->ic_flags & IEEE80211_F_CSAPENDING) frm = ieee80211_add_csa(frm, vap); } else bo->bo_csa = frm; bo->bo_quiet = NULL; if (vap->iv_flags & IEEE80211_F_DOTH) { if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && (vap->iv_flags_ext & IEEE80211_FEXT_DFS) && (vap->iv_quiet == 1)) { /* * We only insert the quiet IE offset if * the quiet IE is enabled. Otherwise don't * put it here or we'll just overwrite * some other beacon contents. */ if (vap->iv_quiet) { bo->bo_quiet = frm; frm = ieee80211_add_quiet(frm,vap, 0); } } } if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) { bo->bo_erp = frm; frm = ieee80211_add_erp(frm, ic); } frm = ieee80211_add_xrates(frm, rs); frm = ieee80211_add_rsn(frm, vap); if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { frm = ieee80211_add_htcap(frm, ni); bo->bo_htinfo = frm; frm = ieee80211_add_htinfo(frm, ni); } if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) { frm = ieee80211_add_vhtcap(frm, ni); bo->bo_vhtinfo = frm; frm = ieee80211_add_vhtinfo(frm, ni); /* Transmit power envelope */ /* Channel switch wrapper element */ /* Extended bss load element */ } frm = ieee80211_add_wpa(frm, vap); if (vap->iv_flags & IEEE80211_F_WME) { bo->bo_wme = frm; frm = ieee80211_add_wme_param(frm, &ic->ic_wme); } if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT)) { frm = ieee80211_add_htcap_vendor(frm, ni); frm = ieee80211_add_htinfo_vendor(frm, ni); } #ifdef IEEE80211_SUPPORT_SUPERG if (vap->iv_flags & IEEE80211_F_ATHEROS) { bo->bo_ath = frm; frm = ieee80211_add_athcaps(frm, ni); } #endif #ifdef IEEE80211_SUPPORT_TDMA if (vap->iv_caps & IEEE80211_C_TDMA) { bo->bo_tdma = frm; frm = ieee80211_add_tdma(frm, vap); } #endif if (vap->iv_appie_beacon != NULL) { bo->bo_appie = frm; bo->bo_appie_len = vap->iv_appie_beacon->ie_len; frm = add_appie(frm, vap->iv_appie_beacon); } /* XXX TODO: move meshid/meshconf up to before vendor extensions? */ #ifdef IEEE80211_SUPPORT_MESH if (vap->iv_opmode == IEEE80211_M_MBSS) { frm = ieee80211_add_meshid(frm, vap); bo->bo_meshconf = frm; frm = ieee80211_add_meshconf(frm, vap); } #endif bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer; bo->bo_csa_trailer_len = frm - bo->bo_csa; m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); } /* * Allocate a beacon frame and fillin the appropriate bits. */ struct mbuf * ieee80211_beacon_alloc(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = vap->iv_ifp; struct ieee80211_frame *wh; struct mbuf *m; int pktlen; uint8_t *frm; /* * Update the "We're putting the quiet IE in the beacon" state. */ if (vap->iv_quiet == 1) vap->iv_flags_ext |= IEEE80211_FEXT_QUIET_IE; else if (vap->iv_quiet == 0) vap->iv_flags_ext &= ~IEEE80211_FEXT_QUIET_IE; /* * beacon frame format * * Note: This needs updating for 802.11-2012. * * [8] time stamp * [2] beacon interval * [2] cabability information * [tlv] ssid * [tlv] supported rates * [3] parameter set (DS) * [8] CF parameter set (optional) * [tlv] parameter set (IBSS/TIM) * [tlv] country (optional) * [3] power control (optional) * [5] channel switch announcement (CSA) (optional) * [tlv] extended rate phy (ERP) * [tlv] extended supported rates * [tlv] RSN parameters * [tlv] HT capabilities * [tlv] HT information * [tlv] VHT capabilities * [tlv] VHT operation * [tlv] Vendor OUI HT capabilities (optional) * [tlv] Vendor OUI HT information (optional) * XXX Vendor-specific OIDs (e.g. Atheros) * [tlv] WPA parameters * [tlv] WME parameters * [tlv] TDMA parameters (optional) * [tlv] Mesh ID (MBSS) * [tlv] Mesh Conf (MBSS) * [tlv] application data (optional) * NB: we allocate the max space required for the TIM bitmap. * XXX how big is this? */ pktlen = 8 /* time stamp */ + sizeof(uint16_t) /* beacon interval */ + sizeof(uint16_t) /* capabilities */ + 2 + ni->ni_esslen /* ssid */ + 2 + IEEE80211_RATE_SIZE /* supported rates */ + 2 + 1 /* DS parameters */ + 2 + 6 /* CF parameters */ + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */ + IEEE80211_COUNTRY_MAX_SIZE /* country */ + 2 + 1 /* power control */ + sizeof(struct ieee80211_csa_ie) /* CSA */ + sizeof(struct ieee80211_quiet_ie) /* Quiet */ + 2 + 1 /* ERP */ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */ 2*sizeof(struct ieee80211_ie_wpa) : 0) /* XXX conditional? */ + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ + sizeof(struct ieee80211_ie_vhtcap)/* VHT caps */ + sizeof(struct ieee80211_ie_vht_operation)/* VHT info */ + (vap->iv_caps & IEEE80211_C_WME ? /* WME */ sizeof(struct ieee80211_wme_param) : 0) #ifdef IEEE80211_SUPPORT_SUPERG + sizeof(struct ieee80211_ath_ie) /* ATH */ #endif #ifdef IEEE80211_SUPPORT_TDMA + (vap->iv_caps & IEEE80211_C_TDMA ? /* TDMA */ sizeof(struct ieee80211_tdma_param) : 0) #endif #ifdef IEEE80211_SUPPORT_MESH + 2 + ni->ni_meshidlen + sizeof(struct ieee80211_meshconf_ie) #endif + IEEE80211_MAX_APPIE ; m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen); if (m == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, "%s: cannot get buf; size %u\n", __func__, pktlen); vap->iv_stats.is_tx_nobuf++; return NULL; } ieee80211_beacon_construct(m, frm, ni); M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); KASSERT(m != NULL, ("no space for 802.11 header?")); wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; *(uint16_t *)wh->i_dur = 0; IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); *(uint16_t *)wh->i_seq = 0; return m; } /* * Update the dynamic parts of a beacon frame based on the current state. */ int ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; struct ieee80211com *ic = ni->ni_ic; int len_changed = 0; uint16_t capinfo; struct ieee80211_frame *wh; ieee80211_seq seqno; IEEE80211_LOCK(ic); /* * Handle 11h channel change when we've reached the count. * We must recalculate the beacon frame contents to account * for the new channel. Note we do this only for the first * vap that reaches this point; subsequent vaps just update * their beacon state to reflect the recalculated channel. */ if (isset(bo->bo_flags, IEEE80211_BEACON_CSA) && vap->iv_csa_count == ic->ic_csa_count) { vap->iv_csa_count = 0; /* * Effect channel change before reconstructing the beacon * frame contents as many places reference ni_chan. */ if (ic->ic_csa_newchan != NULL) ieee80211_csa_completeswitch(ic); /* * NB: ieee80211_beacon_construct clears all pending * updates in bo_flags so we don't need to explicitly * clear IEEE80211_BEACON_CSA. */ ieee80211_beacon_construct(m, mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), ni); /* XXX do WME aggressive mode processing? */ IEEE80211_UNLOCK(ic); return 1; /* just assume length changed */ } /* * Handle the quiet time element being added and removed. * Again, for now we just cheat and reconstruct the whole * beacon - that way the gap is provided as appropriate. * * So, track whether we have already added the IE versus * whether we want to be adding the IE. */ if ((vap->iv_flags_ext & IEEE80211_FEXT_QUIET_IE) && (vap->iv_quiet == 0)) { /* * Quiet time beacon IE enabled, but it's disabled; * recalc */ vap->iv_flags_ext &= ~IEEE80211_FEXT_QUIET_IE; ieee80211_beacon_construct(m, mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), ni); /* XXX do WME aggressive mode processing? */ IEEE80211_UNLOCK(ic); return 1; /* just assume length changed */ } if (((vap->iv_flags_ext & IEEE80211_FEXT_QUIET_IE) == 0) && (vap->iv_quiet == 1)) { /* * Quiet time beacon IE disabled, but it's now enabled; * recalc */ vap->iv_flags_ext |= IEEE80211_FEXT_QUIET_IE; ieee80211_beacon_construct(m, mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), ni); /* XXX do WME aggressive mode processing? */ IEEE80211_UNLOCK(ic); return 1; /* just assume length changed */ } wh = mtod(m, struct ieee80211_frame *); /* * XXX TODO Strictly speaking this should be incremented with the TX * lock held so as to serialise access to the non-qos TID sequence * number space. * * If the driver identifies it does its own TX seqno management then * we can skip this (and still not do the TX seqno.) */ seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; *(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); /* XXX faster to recalculate entirely or just changes? */ capinfo = ieee80211_getcapinfo(vap, ni->ni_chan); *bo->bo_caps = htole16(capinfo); if (vap->iv_flags & IEEE80211_F_WME) { struct ieee80211_wme_state *wme = &ic->ic_wme; /* * Check for aggressive mode change. When there is * significant high priority traffic in the BSS * throttle back BE traffic by using conservative * parameters. Otherwise BE uses aggressive params * to optimize performance of legacy/non-QoS traffic. */ if (wme->wme_flags & WME_F_AGGRMODE) { if (wme->wme_hipri_traffic > wme->wme_hipri_switch_thresh) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: traffic %u, disable aggressive mode\n", __func__, wme->wme_hipri_traffic); wme->wme_flags &= ~WME_F_AGGRMODE; ieee80211_wme_updateparams_locked(vap); wme->wme_hipri_traffic = wme->wme_hipri_switch_hysteresis; } else wme->wme_hipri_traffic = 0; } else { if (wme->wme_hipri_traffic <= wme->wme_hipri_switch_thresh) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, "%s: traffic %u, enable aggressive mode\n", __func__, wme->wme_hipri_traffic); wme->wme_flags |= WME_F_AGGRMODE; ieee80211_wme_updateparams_locked(vap); wme->wme_hipri_traffic = 0; } else wme->wme_hipri_traffic = wme->wme_hipri_switch_hysteresis; } if (isset(bo->bo_flags, IEEE80211_BEACON_WME)) { (void) ieee80211_add_wme_param(bo->bo_wme, wme); clrbit(bo->bo_flags, IEEE80211_BEACON_WME); } } if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) { ieee80211_ht_update_beacon(vap, bo); clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO); } #ifdef IEEE80211_SUPPORT_TDMA if (vap->iv_caps & IEEE80211_C_TDMA) { /* * NB: the beacon is potentially updated every TBTT. */ ieee80211_tdma_update_beacon(vap, bo); } #endif #ifdef IEEE80211_SUPPORT_MESH if (vap->iv_opmode == IEEE80211_M_MBSS) ieee80211_mesh_update_beacon(vap, bo); #endif if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_MBSS) { /* NB: no IBSS support*/ struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) bo->bo_tim; if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) { u_int timlen, timoff, i; /* * ATIM/DTIM needs updating. If it fits in the * current space allocated then just copy in the * new bits. Otherwise we need to move any trailing * data to make room. Note that we know there is * contiguous space because ieee80211_beacon_allocate * insures there is space in the mbuf to write a * maximal-size virtual bitmap (based on iv_max_aid). */ /* * Calculate the bitmap size and offset, copy any * trailer out of the way, and then copy in the * new bitmap and update the information element. * Note that the tim bitmap must contain at least * one byte and any offset must be even. */ if (vap->iv_ps_pending != 0) { timoff = 128; /* impossibly large */ for (i = 0; i < vap->iv_tim_len; i++) if (vap->iv_tim_bitmap[i]) { timoff = i &~ 1; break; } KASSERT(timoff != 128, ("tim bitmap empty!")); for (i = vap->iv_tim_len-1; i >= timoff; i--) if (vap->iv_tim_bitmap[i]) break; timlen = 1 + (i - timoff); } else { timoff = 0; timlen = 1; } /* * TODO: validate this! */ if (timlen != bo->bo_tim_len) { /* copy up/down trailer */ int adjust = tie->tim_bitmap+timlen - bo->bo_tim_trailer; ovbcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust, bo->bo_tim_trailer_len); bo->bo_tim_trailer += adjust; bo->bo_erp += adjust; bo->bo_htinfo += adjust; bo->bo_vhtinfo += adjust; #ifdef IEEE80211_SUPPORT_SUPERG bo->bo_ath += adjust; #endif #ifdef IEEE80211_SUPPORT_TDMA bo->bo_tdma += adjust; #endif #ifdef IEEE80211_SUPPORT_MESH bo->bo_meshconf += adjust; #endif bo->bo_appie += adjust; bo->bo_wme += adjust; bo->bo_csa += adjust; bo->bo_quiet += adjust; bo->bo_tim_len = timlen; /* update information element */ tie->tim_len = 3 + timlen; tie->tim_bitctl = timoff; len_changed = 1; } memcpy(tie->tim_bitmap, vap->iv_tim_bitmap + timoff, bo->bo_tim_len); clrbit(bo->bo_flags, IEEE80211_BEACON_TIM); IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s: TIM updated, pending %u, off %u, len %u\n", __func__, vap->iv_ps_pending, timoff, timlen); } /* count down DTIM period */ if (tie->tim_count == 0) tie->tim_count = tie->tim_period - 1; else tie->tim_count--; /* update state for buffered multicast frames on DTIM */ if (mcast && tie->tim_count == 0) tie->tim_bitctl |= 1; else tie->tim_bitctl &= ~1; if (isset(bo->bo_flags, IEEE80211_BEACON_CSA)) { struct ieee80211_csa_ie *csa = (struct ieee80211_csa_ie *) bo->bo_csa; /* * Insert or update CSA ie. If we're just starting * to count down to the channel switch then we need * to insert the CSA ie. Otherwise we just need to * drop the count. The actual change happens above * when the vap's count reaches the target count. */ if (vap->iv_csa_count == 0) { memmove(&csa[1], csa, bo->bo_csa_trailer_len); bo->bo_erp += sizeof(*csa); bo->bo_htinfo += sizeof(*csa); bo->bo_vhtinfo += sizeof(*csa); bo->bo_wme += sizeof(*csa); #ifdef IEEE80211_SUPPORT_SUPERG bo->bo_ath += sizeof(*csa); #endif #ifdef IEEE80211_SUPPORT_TDMA bo->bo_tdma += sizeof(*csa); #endif #ifdef IEEE80211_SUPPORT_MESH bo->bo_meshconf += sizeof(*csa); #endif bo->bo_appie += sizeof(*csa); bo->bo_csa_trailer_len += sizeof(*csa); bo->bo_quiet += sizeof(*csa); bo->bo_tim_trailer_len += sizeof(*csa); m->m_len += sizeof(*csa); m->m_pkthdr.len += sizeof(*csa); ieee80211_add_csa(bo->bo_csa, vap); } else csa->csa_count--; vap->iv_csa_count++; /* NB: don't clear IEEE80211_BEACON_CSA */ } /* * Only add the quiet time IE if we've enabled it * as appropriate. */ if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) { if (vap->iv_quiet && (vap->iv_flags_ext & IEEE80211_FEXT_QUIET_IE)) { ieee80211_add_quiet(bo->bo_quiet, vap, 1); } } if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) { /* * ERP element needs updating. */ (void) ieee80211_add_erp(bo->bo_erp, ic); clrbit(bo->bo_flags, IEEE80211_BEACON_ERP); } #ifdef IEEE80211_SUPPORT_SUPERG if (isset(bo->bo_flags, IEEE80211_BEACON_ATH)) { ieee80211_add_athcaps(bo->bo_ath, ni); clrbit(bo->bo_flags, IEEE80211_BEACON_ATH); } #endif } if (isset(bo->bo_flags, IEEE80211_BEACON_APPIE)) { const struct ieee80211_appie *aie = vap->iv_appie_beacon; int aielen; uint8_t *frm; aielen = 0; if (aie != NULL) aielen += aie->ie_len; if (aielen != bo->bo_appie_len) { /* copy up/down trailer */ int adjust = aielen - bo->bo_appie_len; ovbcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust, bo->bo_tim_trailer_len); bo->bo_tim_trailer += adjust; bo->bo_appie += adjust; bo->bo_appie_len = aielen; len_changed = 1; } frm = bo->bo_appie; if (aie != NULL) frm = add_appie(frm, aie); clrbit(bo->bo_flags, IEEE80211_BEACON_APPIE); } IEEE80211_UNLOCK(ic); return len_changed; } /* * Do Ethernet-LLC encapsulation for each payload in a fast frame * tunnel encapsulation. The frame is assumed to have an Ethernet * header at the front that must be stripped before prepending the * LLC followed by the Ethernet header passed in (with an Ethernet * type that specifies the payload size). */ struct mbuf * ieee80211_ff_encap1(struct ieee80211vap *vap, struct mbuf *m, const struct ether_header *eh) { struct llc *llc; uint16_t payload; /* XXX optimize by combining m_adj+M_PREPEND */ m_adj(m, sizeof(struct ether_header) - sizeof(struct llc)); llc = mtod(m, struct llc *); llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; llc->llc_control = LLC_UI; llc->llc_snap.org_code[0] = 0; llc->llc_snap.org_code[1] = 0; llc->llc_snap.org_code[2] = 0; llc->llc_snap.ether_type = eh->ether_type; payload = m->m_pkthdr.len; /* NB: w/o Ethernet header */ M_PREPEND(m, sizeof(struct ether_header), M_NOWAIT); if (m == NULL) { /* XXX cannot happen */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: no space for ether_header\n", __func__); vap->iv_stats.is_tx_nobuf++; return NULL; } ETHER_HEADER_COPY(mtod(m, void *), eh); mtod(m, struct ether_header *)->ether_type = htons(payload); return m; } /* * Complete an mbuf transmission. * * For now, this simply processes a completed frame after the * driver has completed it's transmission and/or retransmission. * It assumes the frame is an 802.11 encapsulated frame. * * Later on it will grow to become the exit path for a given frame * from the driver and, depending upon how it's been encapsulated * and already transmitted, it may end up doing A-MPDU retransmission, * power save requeuing, etc. * * In order for the above to work, the driver entry point to this * must not hold any driver locks. Thus, the driver needs to delay * any actual mbuf completion until it can release said locks. * * This frees the mbuf and if the mbuf has a node reference, * the node reference will be freed. */ void ieee80211_tx_complete(struct ieee80211_node *ni, struct mbuf *m, int status) { if (ni != NULL) { struct ifnet *ifp = ni->ni_vap->iv_ifp; if (status == 0) { if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if (m->m_flags & M_MCAST) if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); } else if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); if (m->m_flags & M_TXCB) ieee80211_process_callback(ni, m, status); ieee80211_free_node(ni); } m_freem(m); } Index: stable/12 =================================================================== --- stable/12 (revision 360413) +++ stable/12 (revision 360414) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r359978