diff --git a/Makefile b/Makefile index a86df3b6723..130fdbf6720 100644 --- a/Makefile +++ b/Makefile @@ -243,14 +243,8 @@ cleanworld: # Handle the user-driven targets, using the source relative mk files. # -.if !(!empty(.MAKEFLAGS:M-n) && ${.MAKEFLAGS:M-n} == "-n") -# skip this for -n to avoid changing previous behavior of -# 'make -n buildworld' etc. Using -n -n will run it. -${TGTS}: .MAKE tinderbox toolchains kernel-toolchains: .MAKE -.endif - -${TGTS}: +${TGTS}: .PHONY .MAKE ${_+_}@cd ${.CURDIR}; ${_MAKE} ${.TARGET} # The historic default "all" target creates files which may cause stale @@ -259,9 +253,9 @@ ${TGTS}: # if they want the historic behavior. .MAIN: _guard -_guard: +_guard: .PHONY @echo - @echo "Explicit target required (use \"all\" for historic behavior)" + @echo "Explicit target required. Likely \"${SUBDIR_OVERRIDE:Dall:Ubuildworld}\" is wanted. See build(7)." @echo @false diff --git a/Makefile.inc1 b/Makefile.inc1 index 534564261d5..2c79d96e9f2 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -50,16 +50,23 @@ .include .include -# We must do lib/ and libexec/ before bin/, because if installworld -# installs a new /bin/sh, the 'make' command will *immediately* -# use that new version. And the new (dynamically-linked) /bin/sh -# will expect to find appropriate libraries in /lib and /libexec. -# +# We must do lib/ and libexec/ before bin/ in case of a mid-install error to +# keep the users system reasonably usable. For static->dynamic root upgrades, +# we don't want to install a dynamic binary without rtld and the needed +# libraries. More commonly, for dynamic root, we don't want to install a +# binary that requires a newer library version that hasn't been installed yet. +# This ordering is not a guarantee though. The only guarantee of a working +# system here would require fine-grained ordering of all components based +# on their dependencies. SRCDIR?= ${.CURDIR} .if defined(SUBDIR_OVERRIDE) SUBDIR= ${SUBDIR_OVERRIDE} .else SUBDIR= lib libexec +.if make(install*) +# Ensure libraries are installed before progressing. +SUBDIR+=.WAIT +.endif SUBDIR+=bin .if ${MK_CDDL} != "no" SUBDIR+=cddl @@ -85,10 +92,6 @@ SUBDIR+= tests .if ${MK_OFED} != "no" SUBDIR+=contrib/ofed .endif -# -# We must do etc/ last for install/distribute to work. -# -SUBDIR+=etc # Local directories are last, since it is nice to at least get the base # system rebuilt before you do them. @@ -112,6 +115,15 @@ SUBDIR+= ${_DIR} .endfor .endif +# We must do etc/ last as it hooks into building the man whatis file +# by calling 'makedb' in share/man. This is only relevant for +# install/distribute so they build the whatis file after every manpage is +# installed. +.if make(install*) +SUBDIR+=.WAIT +.endif +SUBDIR+=etc + .if defined(NOCLEAN) NO_CLEAN= ${NOCLEAN} .endif @@ -136,14 +148,17 @@ OSRELDATE!= awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ .else OSRELDATE= 0 .endif +.export OSRELDATE .endif +# Set VERSION for CTFMERGE to use via the default CTFFLAGS=-L VERSION. .if !defined(VERSION) REVISION!= ${MAKE} -C ${SRCDIR}/release -V REVISION BRANCH!= ${MAKE} -C ${SRCDIR}/release -V BRANCH SRCRELDATE!= awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ ${SRCDIR}/sys/sys/param.h VERSION= FreeBSD ${REVISION}-${BRANCH:C/-p[0-9]+$//} ${TARGET_ARCH} ${SRCRELDATE} +.export VERSION .endif KNOWN_ARCHES?= aarch64/arm64 amd64 arm armeb/arm armv6/arm armv6hf/arm i386 i386/pc98 mips mipsel/mips mips64el/mips mips64/mips mipsn32el/mips mipsn32/mips powerpc powerpc64/powerpc sparc64 @@ -252,7 +267,6 @@ CROSSENV+= ${TARGET_CFLAGS} BMAKEENV= INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${BPATH}:${PATH} \ WORLDTMP=${WORLDTMP} \ - VERSION="${VERSION}" \ MAKEFLAGS="-m ${.CURDIR}/tools/build/mk ${.MAKEFLAGS}" # need to keep this in sync with targets/pseudo/bootstrap-tools/Makefile BSARGS= DESTDIR= \ @@ -289,8 +303,7 @@ XMAKE= TOOLS_PREFIX=${WORLDTMP} ${BMAKE} \ # kernel-tools stage KTMAKEENV= INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${BPATH}:${PATH} \ - WORLDTMP=${WORLDTMP} \ - VERSION="${VERSION}" + WORLDTMP=${WORLDTMP} KTMAKE= TOOLS_PREFIX=${WORLDTMP} MAKEOBJDIRPREFIX=${WORLDTMP} \ ${KTMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ DESTDIR= \ @@ -303,7 +316,6 @@ KTMAKE= TOOLS_PREFIX=${WORLDTMP} MAKEOBJDIRPREFIX=${WORLDTMP} \ # world stage WMAKEENV= ${CROSSENV} \ _LDSCRIPTROOT= \ - VERSION="${VERSION}" \ INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${TMPPATH} @@ -313,10 +325,6 @@ HMAKE= PATH=${TMPPATH} ${MAKE} LOCAL_MTREE=${LOCAL_MTREE:Q} HMAKE+= PATH=${TMPPATH} METALOG=${METALOG} -DNO_ROOT .endif -.if ${MK_CDDL} == "no" -WMAKEENV+= MK_CTF=no -.endif - .if defined(CROSS_TOOLCHAIN) LOCALBASE?= /usr/local .include "${LOCALBASE}/share/toolchains/${CROSS_TOOLCHAIN}.mk" @@ -446,7 +454,6 @@ LIB32FLAGS+= --sysroot=${WORLDTMP} # Yes, the flags are redundant. LIB32WMAKEENV+= MAKEOBJDIRPREFIX=${LIB32_OBJTREE} \ _LDSCRIPTROOT=${LIB32TMP} \ - VERSION="${VERSION}" \ INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${TMPPATH} \ LIBDIR=/usr/lib32 \ @@ -557,6 +564,11 @@ _worldtmp: mkdir -p ${WORLDTMP}${TESTSBASE} mtree -deU -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${WORLDTMP}${TESTSBASE} >/dev/null +.if ${MK_DEBUG_FILES} != "no" + mkdir -p ${WORLDTMP}/usr/lib/debug/${TESTSBASE} + mtree -deU -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ + -p ${WORLDTMP}/usr/lib/debug/${TESTSBASE} >/dev/null +.endif .endif .for _mtree in ${LOCAL_MTREE} mtree -deU -f ${.CURDIR}/${_mtree} -p ${WORLDTMP} > /dev/null @@ -579,9 +591,9 @@ _cleanobj: @echo "--------------------------------------------------------------" @echo ">>> stage 2.1: cleaning up the object tree" @echo "--------------------------------------------------------------" - ${_+_}cd ${.CURDIR}; ${WMAKE} ${CLEANDIR:S/^/par-/} + ${_+_}cd ${.CURDIR}; ${WMAKE} ${CLEANDIR} .if defined(LIB32TMP) - ${_+_}cd ${.CURDIR}; ${LIB32WMAKE} -f Makefile.inc1 ${CLEANDIR:S/^/par-/} + ${_+_}cd ${.CURDIR}; ${LIB32WMAKE} -f Makefile.inc1 ${CLEANDIR} .endif .endif _obj: @@ -589,7 +601,7 @@ _obj: @echo "--------------------------------------------------------------" @echo ">>> stage 2.2: rebuilding the object tree" @echo "--------------------------------------------------------------" - ${_+_}cd ${.CURDIR}; ${WMAKE} par-obj + ${_+_}cd ${.CURDIR}; ${WMAKE} obj _build-tools: @echo @echo "--------------------------------------------------------------" @@ -608,7 +620,8 @@ _includes: @echo "--------------------------------------------------------------" @echo ">>> stage 4.1: building includes" @echo "--------------------------------------------------------------" - ${_+_}cd ${.CURDIR}; ${WMAKE} SHARED=symlinks par-includes + ${_+_}cd ${.CURDIR}; ${WMAKE} SHARED=symlinks buildincludes + ${_+_}cd ${.CURDIR}; ${WMAKE} SHARED=symlinks installincludes _libraries: @echo @echo "--------------------------------------------------------------" @@ -622,7 +635,7 @@ _depend: @echo "--------------------------------------------------------------" @echo ">>> stage 4.3: make dependencies" @echo "--------------------------------------------------------------" - ${_+_}cd ${.CURDIR}; ${WMAKE} par-depend + ${_+_}cd ${.CURDIR}; ${WMAKE} depend everything: @echo @echo "--------------------------------------------------------------" @@ -731,7 +744,7 @@ buildworld_epilogue: # to quote multiword values. # buildenvvars: - @echo ${WMAKEENV:Q} + @echo ${WMAKEENV:Q} ${.MAKE.EXPORTED:@v@$v=\"${$v}\"@} .if ${.TARGETS:Mbuildenv} .if ${.MAKEFLAGS:M-j} @@ -815,7 +828,7 @@ _zoneinfo= zic tzsetup ITOOLS= [ awk cap_mkdb cat chflags chmod chown cmp cp \ date echo egrep find grep id install ${_install-info} \ - ln lockf make mkdir mtree mv pwd_mkdb \ + ln make mkdir mtree mv pwd_mkdb \ rm sed services_mkdb sh strip sysctl test true uname wc ${_zoneinfo} \ ${LOCAL_ITOOLS} @@ -892,6 +905,10 @@ distributeworld installworld: _installcheck_world -mkdir -p ${DESTDIR}/${DISTDIR}/${dist}${TESTSBASE} mtree -deU -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}${TESTSBASE} >/dev/null +.if ${MK_DEBUG_FILES} != "no" + mtree -deU -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ + -p ${DESTDIR}/${DISTDIR}/${dist}/usr/lib/debug/${TESTSBASE} >/dev/null +.endif .endif .if defined(NO_ROOT) ${IMAKEENV} mtree -C -f ${.CURDIR}/etc/mtree/BSD.root.dist | \ @@ -1054,7 +1071,7 @@ INSTALLKERNEL= ${_kernel} .endif .endfor -buildkernel ${WMAKE_TGTS} ${.ALLTARGETS:M_*}: .MAKE +buildkernel ${WMAKE_TGTS:N_worldtmp:Nbuild32} ${.ALLTARGETS:M_*:N_worldtmp}: .MAKE .PHONY # # buildkernel @@ -1485,7 +1502,7 @@ build-tools: .MAKE # # kernel-tools: Build kernel-building tools # -kernel-tools: .MAKE +kernel-tools: mkdir -p ${MAKEOBJDIRPREFIX}/usr mtree -deU -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${MAKEOBJDIRPREFIX}/usr >/dev/null @@ -1566,7 +1583,6 @@ cross-tools: .MAKE NXBENV= MAKEOBJDIRPREFIX=${OBJTREE}/nxb \ INSTALL="sh ${.CURDIR}/tools/install.sh" \ - VERSION="${VERSION}" \ PATH=${PATH}:${OBJTREE}/gperf_for_gcc/usr/bin NXBMAKE= ${NXBENV} ${MAKE} \ TBLGEN=${OBJTREE}/nxb-bin/usr/bin/tblgen \ @@ -1585,7 +1601,7 @@ NXBMAKE= ${NXBENV} ${MAKE} \ # For non-clang enabled targets that are still using the in tree gcc # we must build a gperf binary for one instance of its Makefiles. On # clang-enabled systems, the gperf binary is obsolete. -native-xtools: .MAKE +native-xtools: .if ${MK_GCC_BOOTSTRAP} != "no" mkdir -p ${OBJTREE}/gperf_for_gcc/usr/bin ${_+_}@${ECHODIR} "===> ${_gperf} (obj,depend,all,install)"; \ @@ -1931,22 +1947,18 @@ _startup_libs: ${_startup_libs:S/$/__L/} _prebuild_libs: ${_prebuild_libs:S/$/__L/} _generic_libs: ${_generic_libs:S/$/__L/} -.for __target in all clean cleandepend cleandir depend includes obj -.for entry in ${SUBDIR} -${entry}.${__target}__D: .PHONY .MAKE - ${_+_}@set -e; if test -d ${.CURDIR}/${entry}.${MACHINE_ARCH}; then \ - ${ECHODIR} "===> ${DIRPRFX}${entry}.${MACHINE_ARCH} (${__target})"; \ - edir=${entry}.${MACHINE_ARCH}; \ - cd ${.CURDIR}/$${edir}; \ - else \ - ${ECHODIR} "===> ${DIRPRFX}${entry} (${__target})"; \ - edir=${entry}; \ - cd ${.CURDIR}/$${edir}; \ - fi; \ - ${MAKE} ${__target} DIRPRFX=${DIRPRFX}$${edir}/ -.endfor -par-${__target}: ${SUBDIR:S/$/.${__target}__D/} -.endfor +# Enable SUBDIR_PARALLEL when not calling 'make all', unless called as +# 'par-all'. This is because it is unlikely that running 'make all' from +# the top-level, especially with a SUBDIR_OVERRIDE or LOCAL_DIRS set, +# will have a reliable build if SUBDIRs are built in parallel. This is +# safe for the world stage of buildworld though since it has already +# built libraries in a proper order and installed includes into WORLDTMP. +# Special handling is done for SUBDIR ordering for 'install*' to avoid +# trashing a system if it crashes mid-install. +par-all: all .PHONY +.if !make(all) +SUBDIR_PARALLEL= +.endif .include @@ -1988,6 +2000,13 @@ delete-old-files: chflags noschg "${DESTDIR}/$${file}" 2>/dev/null || true; \ rm ${RM_I} "${DESTDIR}/$${file}" <&3; \ fi; \ + for ext in debug symbols; do \ + if ! [ -e "${DESTDIR}/$${file}" ] && [ -f \ + "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ + rm ${RM_I} "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" \ + <&3; \ + fi; \ + done; \ done # Remove catpages without corresponding manpages. @exec 3<&0; \ @@ -2010,6 +2029,11 @@ check-old-files: if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ echo "${DESTDIR}/$${file}"; \ fi; \ + for ext in debug symbols; do \ + if [ -f "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ + echo "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}"; \ + fi; \ + done; \ done # Check for catpages without corresponding manpages. @find ${DESTDIR}/usr/share/man/cat* ! -type d | \ @@ -2242,7 +2266,9 @@ _xi-cross-tools: .endfor _xi-includes: - ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 par-includes \ + ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 buildincludes \ + DESTDIR=${XDDESTDIR} + ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 installincludes \ DESTDIR=${XDDESTDIR} _xi-libraries: diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc index 8bcc9b43fd9..c04479edddd 100644 --- a/ObsoleteFiles.inc +++ b/ObsoleteFiles.inc @@ -97,6 +97,410 @@ OLD_FILES+=usr/bin/colldef OLD_FILES+=usr/share/man/man1/colldef.1.gz OLD_FILES+=usr/bin/mklocale OLD_FILES+=usr/share/man/man1/mklocale.1.gz +# 20151015: test symbols moved to /usr/lib/debug +OLD_DIRS+=usr/tests/lib/atf/libatf-c++/.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/.debug/atf_c++_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/.debug/build_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/.debug/check_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/.debug/config_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/.debug/macros_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/.debug/tests_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/.debug/utils_test.debug +OLD_DIRS+=usr/tests/lib/atf/libatf-c++/detail/.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/.debug/application_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/.debug/env_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/.debug/exceptions_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/.debug/fs_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/.debug/process_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/.debug/sanity_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/.debug/text_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/.debug/version_helper.debug +OLD_DIRS+=usr/tests/lib/atf/libatf-c/.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/.debug/atf_c_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/.debug/build_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/.debug/check_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/.debug/config_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/.debug/error_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/.debug/macros_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/.debug/tc_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/.debug/tp_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/.debug/utils_test.debug +OLD_DIRS+=usr/tests/lib/atf/libatf-c/detail/.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/dynstr_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/env_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/fs_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/list_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/map_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/process_helpers.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/process_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/sanity_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/text_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/user_test.debug +OLD_FILES+=usr/tests/lib/atf/libatf-c/detail/.debug/version_helper.debug +OLD_DIRS+=usr/tests/lib/atf/test-programs/.debug +OLD_FILES+=usr/tests/lib/atf/test-programs/.debug/c_helpers.debug +OLD_FILES+=usr/tests/lib/atf/test-programs/.debug/cpp_helpers.debug +OLD_DIRS+=usr/tests/lib/libc/c063/.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/faccessat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/fchmodat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/fchownat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/fexecve.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/fstatat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/linkat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/mkdirat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/mkfifoat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/mknodat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/openat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/readlinkat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/renameat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/symlinkat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/unlinkat.debug +OLD_FILES+=usr/tests/lib/libc/c063/.debug/utimensat.debug +OLD_DIRS+=usr/tests/lib/libc/db/.debug +OLD_FILES+=usr/tests/lib/libc/db/.debug/h_db.debug +OLD_DIRS+=usr/tests/lib/libc/gen/.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/alarm_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/arc4random_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/assert_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/basedirname_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/dir_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/floatunditf_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/fnmatch_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/fpclassify2_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/fpclassify_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/fpsetmask_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/fpsetround_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/ftok_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/getcwd_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/getgrent_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/glob_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/humanize_number_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/isnan_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/nice_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/pause_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/raise_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/realpath_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/setdomainname_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/sethostname_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/sleep_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/syslog_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/time_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/ttyname_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/.debug/vis_test.debug +OLD_DIRS+=usr/tests/lib/libc/gen/execve/.debug +OLD_FILES+=usr/tests/lib/libc/gen/execve/.debug/execve_test.debug +OLD_DIRS+=usr/tests/lib/libc/gen/posix_spawn/.debug +OLD_FILES+=usr/tests/lib/libc/gen/posix_spawn/.debug/fileactions_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/posix_spawn/.debug/h_fileactions.debug +OLD_FILES+=usr/tests/lib/libc/gen/posix_spawn/.debug/h_spawn.debug +OLD_FILES+=usr/tests/lib/libc/gen/posix_spawn/.debug/h_spawnattr.debug +OLD_FILES+=usr/tests/lib/libc/gen/posix_spawn/.debug/spawn_test.debug +OLD_FILES+=usr/tests/lib/libc/gen/posix_spawn/.debug/spawnattr_test.debug +OLD_DIRS+=usr/tests/lib/libc/hash/.debug +OLD_FILES+=usr/tests/lib/libc/hash/.debug/h_hash.debug +OLD_FILES+=usr/tests/lib/libc/hash/.debug/sha2_test.debug +OLD_DIRS+=usr/tests/lib/libc/inet/.debug +OLD_FILES+=usr/tests/lib/libc/inet/.debug/inet_network_test.debug +OLD_DIRS+=usr/tests/lib/libc/locale/.debug +OLD_FILES+=usr/tests/lib/libc/locale/.debug/io_test.debug +OLD_FILES+=usr/tests/lib/libc/locale/.debug/mbrtowc_test.debug +OLD_FILES+=usr/tests/lib/libc/locale/.debug/mbsnrtowcs_test.debug +OLD_FILES+=usr/tests/lib/libc/locale/.debug/mbstowcs_test.debug +OLD_FILES+=usr/tests/lib/libc/locale/.debug/mbtowc_test.debug +OLD_FILES+=usr/tests/lib/libc/locale/.debug/wcscspn_test.debug +OLD_FILES+=usr/tests/lib/libc/locale/.debug/wcspbrk_test.debug +OLD_FILES+=usr/tests/lib/libc/locale/.debug/wcsspn_test.debug +OLD_FILES+=usr/tests/lib/libc/locale/.debug/wcstod_test.debug +OLD_FILES+=usr/tests/lib/libc/locale/.debug/wctomb_test.debug +OLD_DIRS+=usr/tests/lib/libc/net/.debug +OLD_FILES+=usr/tests/lib/libc/net/.debug/ether_aton_test.debug +OLD_FILES+=usr/tests/lib/libc/net/.debug/getprotoent_test.debug +OLD_FILES+=usr/tests/lib/libc/net/.debug/h_dns_server.debug +OLD_FILES+=usr/tests/lib/libc/net/.debug/h_nsd_recurse.debug +OLD_FILES+=usr/tests/lib/libc/net/.debug/h_protoent.debug +OLD_FILES+=usr/tests/lib/libc/net/.debug/h_servent.debug +OLD_DIRS+=usr/tests/lib/libc/regex/.debug +OLD_FILES+=usr/tests/lib/libc/regex/.debug/exhaust_test.debug +OLD_FILES+=usr/tests/lib/libc/regex/.debug/h_regex.debug +OLD_FILES+=usr/tests/lib/libc/regex/.debug/regex_att_test.debug +OLD_DIRS+=usr/tests/lib/libc/ssp/.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_fgets.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_getcwd.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_gets.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_memcpy.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_memmove.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_memset.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_raw.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_read.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_readlink.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_snprintf.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_sprintf.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_stpcpy.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_stpncpy.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_strcat.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_strcpy.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_strncat.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_strncpy.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_vsnprintf.debug +OLD_FILES+=usr/tests/lib/libc/ssp/.debug/h_vsprintf.debug +OLD_DIRS+=usr/tests/lib/libc/stdio/.debug +OLD_FILES+=usr/tests/lib/libc/stdio/.debug/clearerr_test.debug +OLD_FILES+=usr/tests/lib/libc/stdio/.debug/fflush_test.debug +OLD_FILES+=usr/tests/lib/libc/stdio/.debug/fmemopen2_test.debug +OLD_FILES+=usr/tests/lib/libc/stdio/.debug/fmemopen_test.debug +OLD_FILES+=usr/tests/lib/libc/stdio/.debug/fopen_test.debug +OLD_FILES+=usr/tests/lib/libc/stdio/.debug/fputc_test.debug +OLD_FILES+=usr/tests/lib/libc/stdio/.debug/mktemp_test.debug +OLD_FILES+=usr/tests/lib/libc/stdio/.debug/popen_test.debug +OLD_FILES+=usr/tests/lib/libc/stdio/.debug/printf_test.debug +OLD_FILES+=usr/tests/lib/libc/stdio/.debug/scanf_test.debug +OLD_DIRS+=usr/tests/lib/libc/stdlib/.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/abs_test.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/atoi_test.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/div_test.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/exit_test.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/getenv_test.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/h_getopt.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/h_getopt_long.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/hsearch_test.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/posix_memalign_test.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/random_test.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/strtod_test.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/strtol_test.debug +OLD_FILES+=usr/tests/lib/libc/stdlib/.debug/system_test.debug +OLD_DIRS+=usr/tests/lib/libc/string/.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/memchr.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/memcpy.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/memmem.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/memset.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/strcat.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/strchr.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/strcmp.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/strcpy.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/strcspn.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/strerror.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/strlen.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/strpbrk.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/strrchr.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/strspn.debug +OLD_FILES+=usr/tests/lib/libc/string/.debug/swab.debug +OLD_DIRS+=usr/tests/lib/libc/sys/.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/access_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/chroot_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/clock_gettime_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/connect_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/dup_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/fsync_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/getcontext_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/getgroups_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/getitimer_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/getlogin_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/getpid_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/getrusage_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/getsid_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/gettimeofday_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/issetugid_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/kevent_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/kill_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/link_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/listen_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/mincore_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/mkdir_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/mkfifo_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/mknod_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/mlock_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/mmap_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/mprotect_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/msgctl_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/msgget_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/msgrcv_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/msgsnd_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/msync_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/nanosleep_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/pipe2_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/pipe_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/poll_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/revoke_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/select_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/setrlimit_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/setuid_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/sigaction_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/sigqueue_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/sigtimedwait_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/socketpair_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/stat_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/timer_create_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/truncate_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/ucontext_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/umask_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/unlink_test.debug +OLD_FILES+=usr/tests/lib/libc/sys/.debug/write_test.debug +OLD_DIRS+=usr/tests/lib/libc/termios/.debug +OLD_FILES+=usr/tests/lib/libc/termios/.debug/tcsetpgrp_test.debug +OLD_DIRS+=usr/tests/lib/libc/tls/.debug +OLD_FILES+=usr/tests/lib/libc/tls/.debug/h_tls_dlopen.so.debug +OLD_FILES+=usr/tests/lib/libc/tls/.debug/libh_tls_dynamic.so.1.debug +OLD_FILES+=usr/tests/lib/libc/tls/.debug/tls_dlopen_test.debug +OLD_FILES+=usr/tests/lib/libc/tls/.debug/tls_dynamic_test.debug +OLD_DIRS+=usr/tests/lib/libc/ttyio/.debug +OLD_FILES+=usr/tests/lib/libc/ttyio/.debug/ttyio_test.debug +OLD_DIRS+=usr/tests/lib/libcrypt/.debug +OLD_FILES+=usr/tests/lib/libcrypt/.debug/crypt_tests.debug +OLD_DIRS+=usr/tests/lib/libmp/.debug +OLD_FILES+=usr/tests/lib/libmp/.debug/legacy_test.debug +OLD_DIRS+=usr/tests/lib/libnv/.debug +OLD_FILES+=usr/tests/lib/libnv/.debug/dnv_tests.debug +OLD_FILES+=usr/tests/lib/libnv/.debug/nv_array_tests.debug +OLD_FILES+=usr/tests/lib/libnv/.debug/nv_tests.debug +OLD_FILES+=usr/tests/lib/libnv/.debug/nvlist_add_test.debug +OLD_FILES+=usr/tests/lib/libnv/.debug/nvlist_exists_test.debug +OLD_FILES+=usr/tests/lib/libnv/.debug/nvlist_free_test.debug +OLD_FILES+=usr/tests/lib/libnv/.debug/nvlist_get_test.debug +OLD_FILES+=usr/tests/lib/libnv/.debug/nvlist_move_test.debug +OLD_FILES+=usr/tests/lib/libnv/.debug/nvlist_send_recv_test.debug +OLD_DIRS+=usr/tests/lib/libpam/.debug +OLD_FILES+=usr/tests/lib/libpam/.debug/t_openpam_ctype.debug +OLD_FILES+=usr/tests/lib/libpam/.debug/t_openpam_readlinev.debug +OLD_FILES+=usr/tests/lib/libpam/.debug/t_openpam_readword.debug +OLD_DIRS+=usr/tests/lib/libproc/.debug +OLD_FILES+=usr/tests/lib/libproc/.debug/proc_test.debug +OLD_FILES+=usr/tests/lib/libproc/.debug/target_prog.debug +OLD_DIRS+=usr/tests/lib/librt/.debug +OLD_FILES+=usr/tests/lib/librt/.debug/sched_test.debug +OLD_FILES+=usr/tests/lib/librt/.debug/sem_test.debug +OLD_DIRS+=usr/tests/lib/libthr/.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/barrier_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/cond_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/condwait_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/detach_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/equal_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/fork_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/fpu_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/h_atexit.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/h_cancel.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/h_exit.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/h_resolv.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/join_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/kill_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/mutex_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/once_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/preempt_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/rwlock_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/sem_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/siglongjmp_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/sigmask_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/sigsuspend_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/sleep_test.debug +OLD_FILES+=usr/tests/lib/libthr/.debug/swapcontext_test.debug +OLD_DIRS+=usr/tests/lib/libthr/dlopen/.debug +OLD_FILES+=usr/tests/lib/libthr/dlopen/.debug/dlopen_test.debug +OLD_FILES+=usr/tests/lib/libthr/dlopen/.debug/h_pthread_dlopen.so.1.debug +OLD_FILES+=usr/tests/lib/libthr/dlopen/.debug/main_pthread_create_test.debug +OLD_DIRS+=usr/tests/lib/libutil/.debug +OLD_FILES+=usr/tests/lib/libutil/.debug/flopen_test.debug +OLD_FILES+=usr/tests/lib/libutil/.debug/grp_test.debug +OLD_FILES+=usr/tests/lib/libutil/.debug/humanize_number_test.debug +OLD_FILES+=usr/tests/lib/libutil/.debug/pidfile_test.debug +OLD_FILES+=usr/tests/lib/libutil/.debug/trimdomain-nodomain_test.debug +OLD_FILES+=usr/tests/lib/libutil/.debug/trimdomain_test.debug +OLD_DIRS+=usr/tests/lib/libxo/.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/libenc_test.so.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_01.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_02.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_03.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_04.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_05.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_06.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_07.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_08.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_09.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_10.debug +OLD_FILES+=usr/tests/lib/libxo/.debug/test_11.debug +OLD_DIRS+=usr/tests/lib/msun/.debug +OLD_FILES+=usr/tests/lib/msun/.debug/acos_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/asin_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/atan_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/cbrt_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/ceil_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/cos_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/cosh_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/erf_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/exp_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/fmod_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/infinity_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/ldexp_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/log_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/pow_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/precision_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/round_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/scalbn_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/sin_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/sinh_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/sqrt_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/tan_test.debug +OLD_FILES+=usr/tests/lib/msun/.debug/tanh_test.debug +OLD_DIRS+=usr/tests/libexec/rtld-elf/.debug +OLD_FILES+=usr/tests/libexec/rtld-elf/.debug/ld_library_pathfds.debug +OLD_FILES+=usr/tests/libexec/rtld-elf/.debug/libpythagoras.so.0.debug +OLD_FILES+=usr/tests/libexec/rtld-elf/.debug/target.debug +OLD_DIRS+=usr/tests/sbin/devd/.debug +OLD_FILES+=usr/tests/sbin/devd/.debug/client_test.debug +OLD_DIRS+=usr/tests/sbin/dhclient/.debug +OLD_FILES+=usr/tests/sbin/dhclient/.debug/option-domain-search_test.debug +OLD_DIRS+=usr/tests/share/examples/tests/atf/.debug +OLD_FILES+=usr/tests/share/examples/tests/atf/.debug/printf_test.debug +OLD_DIRS+=usr/tests/share/examples/tests/plain/.debug +OLD_FILES+=usr/tests/share/examples/tests/plain/.debug/printf_test.debug +OLD_DIRS+=usr/tests/sys/aio/.debug +OLD_FILES+=usr/tests/sys/aio/.debug/aio_kqueue_test.debug +OLD_FILES+=usr/tests/sys/aio/.debug/aio_test.debug +OLD_FILES+=usr/tests/sys/aio/.debug/lio_kqueue_test.debug +OLD_DIRS+=usr/tests/sys/fifo/.debug +OLD_FILES+=usr/tests/sys/fifo/.debug/fifo_create.debug +OLD_FILES+=usr/tests/sys/fifo/.debug/fifo_io.debug +OLD_FILES+=usr/tests/sys/fifo/.debug/fifo_misc.debug +OLD_FILES+=usr/tests/sys/fifo/.debug/fifo_open.debug +OLD_DIRS+=usr/tests/sys/file/.debug +OLD_FILES+=usr/tests/sys/file/.debug/closefrom_test.debug +OLD_FILES+=usr/tests/sys/file/.debug/dup_test.debug +OLD_FILES+=usr/tests/sys/file/.debug/fcntlflags_test.debug +OLD_FILES+=usr/tests/sys/file/.debug/flock_helper.debug +OLD_FILES+=usr/tests/sys/file/.debug/ftruncate_test.debug +OLD_FILES+=usr/tests/sys/file/.debug/newfileops_on_fork_test.debug +OLD_DIRS+=usr/tests/sys/kern/.debug +OLD_FILES+=usr/tests/sys/kern/.debug/kern_descrip_test.debug +OLD_FILES+=usr/tests/sys/kern/.debug/ptrace_test.debug +OLD_FILES+=usr/tests/sys/kern/.debug/unix_seqpacket_test.debug +OLD_DIRS+=usr/tests/sys/kern/execve/.debug +OLD_FILES+=usr/tests/sys/kern/execve/.debug/execve_helper.debug +OLD_FILES+=usr/tests/sys/kern/execve/.debug/good_aout.debug +OLD_DIRS+=usr/tests/sys/kqueue/.debug +OLD_FILES+=usr/tests/sys/kqueue/.debug/kqtest.debug +OLD_DIRS+=usr/tests/sys/mqueue/.debug +OLD_FILES+=usr/tests/sys/mqueue/.debug/mqtest1.debug +OLD_FILES+=usr/tests/sys/mqueue/.debug/mqtest2.debug +OLD_FILES+=usr/tests/sys/mqueue/.debug/mqtest3.debug +OLD_FILES+=usr/tests/sys/mqueue/.debug/mqtest4.debug +OLD_FILES+=usr/tests/sys/mqueue/.debug/mqtest5.debug +OLD_DIRS+=usr/tests/sys/netinet/.debug +OLD_FILES+=usr/tests/sys/netinet/.debug/udp_dontroute.debug +OLD_DIRS+=usr/tests/sys/pjdfstest/.debug +OLD_FILES+=usr/tests/sys/pjdfstest/.debug/pjdfstest.debug +OLD_DIRS+=usr/tests/sys/vm/.debug +OLD_FILES+=usr/tests/sys/vm/.debug/mmap_test.debug +# 20151015: Rename files due to file-installed-as-dir bug +OLD_FILES+=usr/share/doc/legal/realtek +OLD_FILES+=usr/share/doc/legal/realtek/LICENSE +OLD_DIRS+=usr/share/doc/legal/realtek +OLD_DIRS+=usr/share/doc/legal/intel_ipw +OLD_FILES+=usr/share/doc/legal/intel_ipw/LICENSE +OLD_FILES+=usr/share/doc/legal/intel_iwn +OLD_FILES+=usr/share/doc/legal/intel_iwn/LICENSE +OLD_DIRS+=usr/share/doc/legal/intel_iwn +OLD_DIRS+=usr/share/doc/legal/intel_iwi +OLD_FILES+=usr/share/doc/legal/intel_iwi/LICENSE +OLD_DIRS+=usr/share/doc/legal/intel_wpi +OLD_FILES+=usr/share/doc/legal/intel_wpi/LICENSE # 20151006: new libc++ import OLD_FILES+=usr/include/c++/__tuple_03 # 20151006: new clang import which bumps version from 3.6.1 to 3.7.0. diff --git a/UPDATING b/UPDATING index baff1f13605..1095ba6ea5d 100644 --- a/UPDATING +++ b/UPDATING @@ -31,6 +31,12 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 11.x IS SLOW: disable the most expensive debugging functionality run "ln -s 'abort:false,junk:false' /etc/malloc.conf".) +20151017: + The build previously allowed using 'make -n' to not recurse into + sub-directories while showing what commands would be executed, and + 'make -n -n' to recursively show commands. Now 'make -n' will recurse + and 'make -N' will not. + 20151012: If you specify SENDMAIL_MC or SENDMAIL_CF in make.conf, mergemaster and etcupdate will now use this file. A custom sendmail.cf is now diff --git a/bin/dd/Makefile b/bin/dd/Makefile index eb8ec8527b1..5f07dbc248a 100644 --- a/bin/dd/Makefile +++ b/bin/dd/Makefile @@ -1,6 +1,8 @@ # @(#)Makefile 8.1 (Berkeley) 5/31/93 # $FreeBSD$ +.include + PROG= dd SRCS= args.c conv.c conv_tab.c dd.c misc.c position.c @@ -24,4 +26,8 @@ test: ${PROG} gen .endfor @rm -f gen +.if ${MK_TESTS} != "no" +SUBDIR+= tests +.endif + .include diff --git a/bin/dd/tests/Makefile b/bin/dd/tests/Makefile new file mode 100644 index 00000000000..dd04af91588 --- /dev/null +++ b/bin/dd/tests/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +NETBSD_ATF_TESTS_SH= dd_test + +.include + +.include diff --git a/bin/ps/Makefile b/bin/ps/Makefile index 5177832131a..79e9fc6cf0e 100644 --- a/bin/ps/Makefile +++ b/bin/ps/Makefile @@ -11,6 +11,6 @@ SRCS= fmt.c keyword.c nlist.c print.c ps.c # on large systems. # CFLAGS+=-DLAZY_PS -LIBADD= m kvm jail xo util +LIBADD= m kvm jail xo .include diff --git a/bin/sh/tests/Makefile b/bin/sh/tests/Makefile index d93e19aea7e..a9abc80311d 100644 --- a/bin/sh/tests/Makefile +++ b/bin/sh/tests/Makefile @@ -10,4 +10,6 @@ TESTS_SUBDIRS+= parameters TESTS_SUBDIRS+= parser TESTS_SUBDIRS+= set-e +SUBDIR_PARALLEL= + .include diff --git a/bin/test/tests/legacy_test.sh b/bin/test/tests/legacy_test.sh index 922955129d9..8dae88f0d7a 100644 --- a/bin/test/tests/legacy_test.sh +++ b/bin/test/tests/legacy_test.sh @@ -2,7 +2,7 @@ #- # Copyright (c) June 1996 Wolfram Schneider . Berlin. -# All rights reserved. +# All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ # $FreeBSD$ # force a specified test program, e.g. `env test=/bin/test sh regress.sh' -: ${test=test} +: ${test=test} t () { @@ -54,13 +54,13 @@ t () count=0 echo "1..130" -t 0 'b = b' -t 0 'b == b' -t 1 'b != b' -t 0 '\( b = b \)' -t 0 '\( b == b \)' -t 1 '! \( b = b \)' -t 1 '! \( b == b \)' +t 0 'b = b' +t 0 'b == b' +t 1 'b != b' +t 0 '\( b = b \)' +t 0 '\( b == b \)' +t 1 '! \( b = b \)' +t 1 '! \( b == b \)' t 1 '! -f /etc/passwd' t 0 '-h = -h' diff --git a/cddl/Makefile b/cddl/Makefile index 44b778dbfc6..26b502a1208 100644 --- a/cddl/Makefile +++ b/cddl/Makefile @@ -2,10 +2,13 @@ .include -SUBDIR= lib sbin usr.bin usr.sbin +SUBDIR= lib .WAIT \ + sbin usr.bin usr.sbin .if ${MK_TESTS} != "no" SUBDIR+=tests .endif +SUBDIR_PARALLEL= + .include diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs.8 b/cddl/contrib/opensolaris/cmd/zfs/zfs.8 index e534ae9a02a..5eca00fe008 100644 --- a/cddl/contrib/opensolaris/cmd/zfs/zfs.8 +++ b/cddl/contrib/opensolaris/cmd/zfs/zfs.8 @@ -18,7 +18,7 @@ .\" information: Portions Copyright [yyyy] [name of copyright owner] .\" .\" Copyright (c) 2010, Sun Microsystems, Inc. All Rights Reserved. -.\" Copyright (c) 2014 by Delphix. All rights reserved. +.\" Copyright (c) 2011, 2014 by Delphix. All rights reserved. .\" Copyright (c) 2011, Pawel Jakub Dawidek .\" Copyright (c) 2012, Glen Barber .\" Copyright (c) 2012, Bryan Drewery @@ -117,7 +117,7 @@ .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot .Nm .Cm set -.Ar property Ns = Ns Ar value +.Ar property Ns = Ns Ar value Oc ... .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ... .Nm .Cm get @@ -189,17 +189,25 @@ .Op Fl i Ar snapshot Ns | Ns bookmark .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot .Nm +.Cm send +.Op Fl Penv +.Fl t Ar receive_resume_token +.Nm .Cm receive Ns | Ns Cm recv -.Op Fl vnFu +.Op Fl vnsFu .Op Fl o Sy origin Ns = Ns Ar snapshot .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot .Nm .Cm receive Ns | Ns Cm recv -.Op Fl vnFu +.Op Fl vnsFu .Op Fl d | e .Op Fl o Sy origin Ns = Ns Ar snapshot .Ar filesystem .Nm +.Cm receive Ns | Ns Cm recv +.Fl A +.Ar filesystem Ns | Ns Ar volume +.Nm .Cm allow .Ar filesystem Ns | Ns Ar volume .Nm @@ -597,6 +605,13 @@ For cloned file systems or volumes, the snapshot from which the clone was created. See also the .Sy clones property. +.It Sy receive_resume_token +For filesystems or volumes which have saved partially-completed state from +.Sy zfs receive -s , +this opaque token can be provided to +.Sy zfs send -t +to resume and complete the +.Sy zfs receive . .It Sy referenced The amount of data that is accessible by this dataset, which may or may not be shared with other datasets in the pool. When a snapshot or clone is created, it @@ -2106,14 +2121,14 @@ option, but sorts by property in descending order. .It Xo .Nm .Cm set -.Ar property Ns = Ns Ar value +.Ar property Ns = Ns Ar value Oc ... .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot .Xc .Pp -Sets the property to the given value for each dataset. Only some properties can -be edited. See the "Properties" section for more information on what properties -can be set and acceptable values. Numeric values can be specified as exact -values, or in a human-readable form with a suffix of +Sets the property or list of properties to the given value(s) for each dataset. +Only some properties can be edited. See the "Properties" section for more +information on what properties can be set and acceptable values. Numeric values +can be specified as exact values, or in a human-readable form with a suffix of .Sy B , K , M , G , T , P , E , Z (for bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, exabytes, or zettabytes, respectively). User properties can be set on snapshots. For more @@ -2630,6 +2645,9 @@ useful in conjunction with the or .Fl P flags to determine what data will be sent. +In this case, the verbose output will be written to +standard output (contrast with a non-dry-run, where the stream is written +to standard output and the verbose output goes to standard error). .It Fl P Print machine-parsable verbose information about the stream package generated. .It Fl v @@ -2711,15 +2729,28 @@ feature. .El .It Xo .Nm +.Cm send +.Op Fl Penv +.Fl t +.Ar receive_resume_token +.Xc +Creates a send stream which resumes an interrupted receive. The +.Ar receive_resume_token +is the value of this property on the filesystem +or volume that was being received into. See the documentation for +.Sy zfs receive -s +for more details. +.It Xo +.Nm .Cm receive Ns | Ns Cm recv -.Op Fl vnFu +.Op Fl vnsFu .Op Fl o Sy origin Ns = Ns Ar snapshot .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot .Xc .It Xo .Nm .Cm receive Ns | Ns Cm recv -.Op Fl vnFu +.Op Fl vnsFu .Op Fl d | e .Op Fl o Sy origin Ns = Ns Ar snapshot .Ar filesystem @@ -2816,9 +2847,42 @@ performing the receive operation. If receiving an incremental replication stream (for example, one generated by .Qq Nm Cm send Fl R Bro Fl i | Fl I Brc ) , destroy snapshots and file systems that do not exist on the sending side. +.It Fl s +If the receive is interrupted, save the partially received state, rather +than deleting it. Interruption may be due to premature termination of +the stream +.Po e.g. due to network failure or failure of the remote system +if the stream is being read over a network connection +.Pc , +a checksum error in the stream, termination of the +.Nm zfs Cm receive +process, or unclean shutdown of the system. +.Pp +The receive can be resumed with a stream generated by +.Nm zfs Cm send Fl t Ar token , +where the +.Ar token +is the value of the +.Sy receive_resume_token +property of the filesystem or volume which is received into. +.Pp +To use this flag, the storage pool must have the +.Sy extensible_dataset +feature enabled. See +.Xr zpool-features 5 +for details on ZFS feature flags. .El .It Xo .Nm +.Cm receive Ns | Ns Cm recv +.Fl A +.Ar filesystem Ns | Ns Ar volume +.Xc +Abort an interrupted +.Nm zfs Cm receive Fl s , +deleting its saved partially received state. +.It Xo +.Nm .Cm allow .Ar filesystem Ns | Ns Ar volume .Xc diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c b/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c index 9ea178e9c3d..b8466fe24e1 100644 --- a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c +++ b/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2015 by Delphix. All rights reserved. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright (c) 2011-2012 Pawel Jakub Dawidek . @@ -263,10 +263,11 @@ get_usage(zfs_help_t idx) case HELP_PROMOTE: return (gettext("\tpromote \n")); case HELP_RECEIVE: - return (gettext("\treceive|recv [-vnFu] \n" - "\treceive|recv [-vnFu] [-o origin=] [-d | -e] " - "\n")); + "\treceive|recv [-vnsFu] [-o origin=] [-d | -e] " + "\n" + "\treceive|recv -A \n")); case HELP_RENAME: return (gettext("\trename [-f] " "\n" @@ -279,9 +280,10 @@ get_usage(zfs_help_t idx) return (gettext("\tsend [-DnPpRvLe] [-[iI] snapshot] " "\n" "\tsend [-Le] [-i snapshot|bookmark] " - "\n")); + "\n" + "\tsend [-nvPe] -t \n")); case HELP_SET: - return (gettext("\tset " + return (gettext("\tset ... " " ...\n")); case HELP_SHARE: return (gettext("\tshare <-a | filesystem>\n")); @@ -496,6 +498,10 @@ usage(boolean_t requested) exit(requested ? 0 : 2); } +/* + * Take a property=value argument string and add it to the given nvlist. + * Modifies the argument inplace. + */ static int parseprop(nvlist_t *props, char *propname) { @@ -503,7 +509,7 @@ parseprop(nvlist_t *props, char *propname) if ((propval = strchr(propname, '=')) == NULL) { (void) fprintf(stderr, gettext("missing " - "'=' for -o option\n")); + "'=' for property=value argument\n")); return (-1); } *propval = '\0'; @@ -630,7 +636,7 @@ zfs_do_clone(int argc, char **argv) while ((c = getopt(argc, argv, "o:p")) != -1) { switch (c) { case 'o': - if (parseprop(props, optarg)) + if (parseprop(props, optarg) != 0) return (1); break; case 'p': @@ -839,10 +845,12 @@ zfs_do_create(int argc, char **argv) if (type == ZFS_TYPE_VOLUME && !noreserve) { zpool_handle_t *zpool_handle; + nvlist_t *real_props; uint64_t spa_version; char *p; zfs_prop_t resv_prop; char *strval; + char msg[1024]; if (p = strchr(argv[0], '/')) *p = '\0'; @@ -853,12 +861,22 @@ zfs_do_create(int argc, char **argv) goto error; spa_version = zpool_get_prop_int(zpool_handle, ZPOOL_PROP_VERSION, NULL); - zpool_close(zpool_handle); if (spa_version >= SPA_VERSION_REFRESERVATION) resv_prop = ZFS_PROP_REFRESERVATION; else resv_prop = ZFS_PROP_RESERVATION; - volsize = zvol_volsize_to_reservation(volsize, props); + + (void) snprintf(msg, sizeof (msg), + gettext("cannot create '%s'"), argv[0]); + if (props && (real_props = zfs_valid_proplist(g_zfs, type, + props, 0, NULL, zpool_handle, msg)) == NULL) { + zpool_close(zpool_handle); + goto error; + } + zpool_close(zpool_handle); + + volsize = zvol_volsize_to_reservation(volsize, real_props); + nvlist_free(real_props); if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop), &strval) != 0) { @@ -3530,21 +3548,17 @@ out: } /* - * zfs set property=value { fs | snap | vol } ... + * zfs set property=value ... { fs | snap | vol } ... * - * Sets the given property for all datasets specified on the command line. + * Sets the given properties for all datasets specified on the command line. */ -typedef struct set_cbdata { - char *cb_propname; - char *cb_value; -} set_cbdata_t; static int set_callback(zfs_handle_t *zhp, void *data) { - set_cbdata_t *cbp = data; + nvlist_t *props = data; - if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) { + if (zfs_prop_set_list(zhp, props) != 0) { switch (libzfs_errno(g_zfs)) { case EZFS_MOUNTFAILED: (void) fprintf(stderr, gettext("property may be set " @@ -3563,7 +3577,8 @@ set_callback(zfs_handle_t *zhp, void *data) static int zfs_do_set(int argc, char **argv) { - set_cbdata_t cb; + nvlist_t *props = NULL; + int ds_start = -1; /* argv idx of first dataset arg */ int ret = 0; /* check for options */ @@ -3575,36 +3590,51 @@ zfs_do_set(int argc, char **argv) /* check number of arguments */ if (argc < 2) { - (void) fprintf(stderr, gettext("missing property=value " - "argument\n")); + (void) fprintf(stderr, gettext("missing arguments\n")); usage(B_FALSE); } if (argc < 3) { - (void) fprintf(stderr, gettext("missing dataset name\n")); + if (strchr(argv[1], '=') == NULL) { + (void) fprintf(stderr, gettext("missing property=value " + "argument(s)\n")); + } else { + (void) fprintf(stderr, gettext("missing dataset " + "name(s)\n")); + } usage(B_FALSE); } - /* validate property=value argument */ - cb.cb_propname = argv[1]; - if (((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) || - (cb.cb_value[1] == '\0')) { - (void) fprintf(stderr, gettext("missing value in " - "property=value argument\n")); + /* validate argument order: prop=val args followed by dataset args */ + for (int i = 1; i < argc; i++) { + if (strchr(argv[i], '=') != NULL) { + if (ds_start > 0) { + /* out-of-order prop=val argument */ + (void) fprintf(stderr, gettext("invalid " + "argument order\n"), i); + usage(B_FALSE); + } + } else if (ds_start < 0) { + ds_start = i; + } + } + if (ds_start < 0) { + (void) fprintf(stderr, gettext("missing dataset name(s)\n")); usage(B_FALSE); } - *cb.cb_value = '\0'; - cb.cb_value++; - - if (*cb.cb_propname == '\0') { - (void) fprintf(stderr, - gettext("missing property in property=value argument\n")); - usage(B_FALSE); + /* Populate a list of property settings */ + if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) + nomem(); + for (int i = 1; i < ds_start; i++) { + if ((ret = parseprop(props, argv[i])) != 0) + goto error; } - ret = zfs_for_each(argc - 2, argv + 2, 0, - ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb); + ret = zfs_for_each(argc - ds_start, argv + ds_start, 0, + ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props); +error: + nvlist_free(props); return (ret); } @@ -3728,6 +3758,7 @@ zfs_do_send(int argc, char **argv) { char *fromname = NULL; char *toname = NULL; + char *resume_token = NULL; char *cp; zfs_handle_t *zhp; sendflags_t flags = { 0 }; @@ -3736,7 +3767,7 @@ zfs_do_send(int argc, char **argv) boolean_t extraverbose = B_FALSE; /* check options */ - while ((c = getopt(argc, argv, ":i:I:RDpvnPLe")) != -1) { + while ((c = getopt(argc, argv, ":i:I:RDpvnPLet:")) != -1) { switch (c) { case 'i': if (fromname) @@ -3777,6 +3808,9 @@ zfs_do_send(int argc, char **argv) case 'e': flags.embed_data = B_TRUE; break; + case 't': + resume_token = optarg; + break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); @@ -3792,14 +3826,28 @@ zfs_do_send(int argc, char **argv) argc -= optind; argv += optind; - /* check number of arguments */ - if (argc < 1) { - (void) fprintf(stderr, gettext("missing snapshot argument\n")); - usage(B_FALSE); - } - if (argc > 1) { - (void) fprintf(stderr, gettext("too many arguments\n")); - usage(B_FALSE); + if (resume_token != NULL) { + if (fromname != NULL || flags.replicate || flags.props || + flags.dedup) { + (void) fprintf(stderr, + gettext("invalid flags combined with -t\n")); + usage(B_FALSE); + } + if (argc != 0) { + (void) fprintf(stderr, gettext("no additional " + "arguments are permitted with -t\n")); + usage(B_FALSE); + } + } else { + if (argc < 1) { + (void) fprintf(stderr, + gettext("missing snapshot argument\n")); + usage(B_FALSE); + } + if (argc > 1) { + (void) fprintf(stderr, gettext("too many arguments\n")); + usage(B_FALSE); + } } if (!flags.dryrun && isatty(STDOUT_FILENO)) { @@ -3809,6 +3857,11 @@ zfs_do_send(int argc, char **argv) return (1); } + if (resume_token != NULL) { + return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO, + resume_token)); + } + /* * Special case sending a filesystem, or from a bookmark. */ @@ -3914,8 +3967,6 @@ zfs_do_send(int argc, char **argv) } /* - * zfs receive [-vnFu] [-d | -e] - * * Restore a backup stream from stdin. */ static int @@ -3923,6 +3974,8 @@ zfs_do_receive(int argc, char **argv) { int c, err; recvflags_t flags = { 0 }; + boolean_t abort_resumable = B_FALSE; + nvlist_t *props; nvpair_t *nvp = NULL; @@ -3930,7 +3983,7 @@ zfs_do_receive(int argc, char **argv) nomem(); /* check options */ - while ((c = getopt(argc, argv, ":o:denuvF")) != -1) { + while ((c = getopt(argc, argv, ":o:denuvFsA")) != -1) { switch (c) { case 'o': if (parseprop(props, optarg) != 0) @@ -3952,9 +4005,15 @@ zfs_do_receive(int argc, char **argv) case 'v': flags.verbose = B_TRUE; break; + case 's': + flags.resumable = B_TRUE; + break; case 'F': flags.force = B_TRUE; break; + case 'A': + abort_resumable = B_TRUE; + break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); @@ -3987,6 +4046,44 @@ zfs_do_receive(int argc, char **argv) } } + if (abort_resumable) { + if (flags.isprefix || flags.istail || flags.dryrun || + flags.resumable || flags.nomount) { + (void) fprintf(stderr, gettext("invalid option")); + usage(B_FALSE); + } + + char namebuf[ZFS_MAXNAMELEN]; + (void) snprintf(namebuf, sizeof (namebuf), + "%s/%%recv", argv[0]); + + if (zfs_dataset_exists(g_zfs, namebuf, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) { + zfs_handle_t *zhp = zfs_open(g_zfs, + namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); + if (zhp == NULL) + return (1); + err = zfs_destroy(zhp, B_FALSE); + } else { + zfs_handle_t *zhp = zfs_open(g_zfs, + argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); + if (zhp == NULL) + usage(B_FALSE); + if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) || + zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, + NULL, 0, NULL, NULL, 0, B_TRUE) == -1) { + (void) fprintf(stderr, + gettext("'%s' does not have any " + "resumable receive state to abort\n"), + argv[0]); + return (1); + } + err = zfs_destroy(zhp, B_FALSE); + } + + return (err != 0); + } + if (isatty(STDIN_FILENO)) { (void) fprintf(stderr, gettext("Error: Backup stream can not be read " @@ -3994,7 +4091,6 @@ zfs_do_receive(int argc, char **argv) "You must redirect standard input.\n")); return (1); } - err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL); return (err != 0); @@ -5815,6 +5911,24 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol, return (0); } + /* + * If this filesystem is inconsistent and has a receive resume + * token, we can not mount it. + */ + if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) && + zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, + NULL, 0, NULL, NULL, 0, B_TRUE) == 0) { + if (!explicit) + return (0); + + (void) fprintf(stderr, gettext("cannot %s '%s': " + "Contains partially-completed state from " + "\"zfs receive -r\", which can be resumed with " + "\"zfs send -t\"\n"), + cmdname, zfs_get_name(zhp)); + return (1); + } + /* * At this point, we have verified that the mountpoint and/or * shareopts are appropriate for auto management. If the diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c index a3eabd1e0eb..6e1670e8a39 100644 --- a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c +++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c @@ -199,7 +199,8 @@ static boolean_t log_history = B_TRUE; static uint_t timestamp_fmt = NODATE; static const char * -get_usage(zpool_help_t idx) { +get_usage(zpool_help_t idx) +{ switch (idx) { case HELP_ADD: return (gettext("\tadd [-fn] ...\n")); @@ -2940,6 +2941,9 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv, uint_t c, children; char *vname; boolean_t scripted = cb->cb_scripted; + uint64_t islog = B_FALSE; + boolean_t haslog = B_FALSE; + char *dashes = "%-*s - - - - - -\n"; verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &c) == 0); @@ -2990,24 +2994,47 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv, ZPOOL_CONFIG_IS_HOLE, &ishole) == 0 && ishole) continue; + if (nvlist_lookup_uint64(child[c], + ZPOOL_CONFIG_IS_LOG, &islog) == 0 && islog) { + haslog = B_TRUE; + continue; + } + vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); print_list_stats(zhp, vname, child[c], cb, depth + 2); free(vname); } - /* - * Include level 2 ARC devices in iostat output - */ - if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, - &child, &children) != 0) - return; - - if (children > 0) { - (void) printf("%-*s - - - - - " - "-\n", cb->cb_namewidth, "cache"); + if (haslog == B_TRUE) { + /* LINTED E_SEC_PRINTF_VAR_FMT */ + (void) printf(dashes, cb->cb_namewidth, "log"); for (c = 0; c < children; c++) { - vname = zpool_vdev_name(g_zfs, zhp, child[c], - B_FALSE); + if (nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, + &islog) != 0 || !islog) + continue; + vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); + print_list_stats(zhp, vname, child[c], cb, depth + 2); + free(vname); + } + } + + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, + &child, &children) == 0 && children > 0) { + /* LINTED E_SEC_PRINTF_VAR_FMT */ + (void) printf(dashes, cb->cb_namewidth, "cache"); + for (c = 0; c < children; c++) { + vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); + print_list_stats(zhp, vname, child[c], cb, depth + 2); + free(vname); + } + } + + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child, + &children) == 0 && children > 0) { + /* LINTED E_SEC_PRINTF_VAR_FMT */ + (void) printf(dashes, cb->cb_namewidth, "spare"); + for (c = 0; c < children; c++) { + vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); print_list_stats(zhp, vname, child[c], cb, depth + 2); free(vname); } diff --git a/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c b/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c index f6dedc2fa38..83a5b54b001 100644 --- a/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c +++ b/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c @@ -125,7 +125,7 @@ read_hdr(dmu_replay_record_t *drr, zio_cksum_t *cksum) saved_cksum.zc_word[1], saved_cksum.zc_word[2], saved_cksum.zc_word[3]); - exit(1); + return (0); } return (sizeof (*drr)); } @@ -346,8 +346,7 @@ main(int argc, char *argv[]) if (verbose) (void) printf("\n"); - if ((DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == - DMU_COMPOUNDSTREAM) && drr->drr_payloadlen != 0) { + if (drr->drr_payloadlen != 0) { nvlist_t *nv; int sz = drr->drr_payloadlen; diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h index 968732ede98..8ab15461c8e 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h @@ -23,7 +23,7 @@ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011 Pawel Jakub Dawidek . * All rights reserved. - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2011, 2015 by Delphix. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright (c) 2012 Martin Matuska . All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. @@ -426,10 +426,11 @@ extern const char *zfs_prop_column_name(zfs_prop_t); extern boolean_t zfs_prop_align_right(zfs_prop_t); extern nvlist_t *zfs_valid_proplist(libzfs_handle_t *, zfs_type_t, - nvlist_t *, uint64_t, zfs_handle_t *, const char *); + nvlist_t *, uint64_t, zfs_handle_t *, zpool_handle_t *, const char *); extern const char *zfs_prop_to_name(zfs_prop_t); extern int zfs_prop_set(zfs_handle_t *, const char *, const char *); +extern int zfs_prop_set_list(zfs_handle_t *, nvlist_t *); extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t, zprop_source_t *, char *, size_t, boolean_t); extern int zfs_prop_get_recvd(zfs_handle_t *, const char *, char *, size_t, @@ -621,6 +622,10 @@ typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *); extern int zfs_send(zfs_handle_t *, const char *, const char *, sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **); extern int zfs_send_one(zfs_handle_t *, const char *, int, enum lzc_send_flags); +extern int zfs_send_resume(libzfs_handle_t *, sendflags_t *, int outfd, + const char *); +extern nvlist_t *zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, + const char *token); extern int zfs_promote(zfs_handle_t *); extern int zfs_hold(zfs_handle_t *, const char *, const char *, @@ -661,6 +666,12 @@ typedef struct recvflags { /* set "canmount=off" on all modified filesystems */ boolean_t canmountoff; + /* + * Mark the file systems as "resumable" and do not destroy them if the + * receive is interrupted + */ + boolean_t resumable; + /* byteswap flag is used internally; callers need not specify */ boolean_t byteswap; diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c index a3f6129876c..833e1b67574 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c @@ -74,6 +74,9 @@ zcmd_ioctl(int fd, int request, zfs_cmd_t *zc) if (zfs_ioctl_version >= ZFS_IOCVER_DEADMAN) { switch (zfs_ioctl_version) { + case ZFS_IOCVER_EDBP: + cflag = ZFS_CMD_COMPAT_EDBP; + break; case ZFS_IOCVER_ZCMD: cflag = ZFS_CMD_COMPAT_ZCMD; break; diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c index 6d9b68ffde4..234d2cd5bf9 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c @@ -22,7 +22,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2015 by Delphix. All rights reserved. * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved. * Copyright (c) 2011-2012 Pawel Jakub Dawidek . * All rights reserved. @@ -890,7 +890,8 @@ zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) */ nvlist_t * zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, - uint64_t zoned, zfs_handle_t *zhp, const char *errbuf) + uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl, + const char *errbuf) { nvpair_t *elem; uint64_t intval; @@ -1084,8 +1085,8 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, case ZFS_PROP_RECORDSIZE: { int maxbs = SPA_MAXBLOCKSIZE; - if (zhp != NULL) { - maxbs = zpool_get_prop_int(zhp->zpool_hdl, + if (zpool_hdl != NULL) { + maxbs = zpool_get_prop_int(zpool_hdl, ZPOOL_PROP_MAXBLOCKSIZE, NULL); } /* @@ -1403,6 +1404,7 @@ zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl) uint64_t old_reservation; uint64_t new_reservation; zfs_prop_t resv_prop; + nvlist_t *props; /* * If this is an existing volume, and someone is setting the volsize, @@ -1412,16 +1414,25 @@ zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl) if (zfs_which_resv_prop(zhp, &resv_prop) < 0) return (-1); old_reservation = zfs_prop_get_int(zhp, resv_prop); - if ((zvol_volsize_to_reservation(old_volsize, zhp->zfs_props) != - old_reservation) || nvlist_lookup_uint64(nvl, - zfs_prop_to_name(resv_prop), &new_reservation) != ENOENT) { + + props = fnvlist_alloc(); + fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), + zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE)); + + if ((zvol_volsize_to_reservation(old_volsize, props) != + old_reservation) || nvlist_exists(nvl, + zfs_prop_to_name(resv_prop))) { + fnvlist_free(props); return (0); } if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE), - &new_volsize) != 0) + &new_volsize) != 0) { + fnvlist_free(props); return (-1); - new_reservation = zvol_volsize_to_reservation(new_volsize, - zhp->zfs_props); + } + new_reservation = zvol_volsize_to_reservation(new_volsize, props); + fnvlist_free(props); + if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop), new_reservation) != 0) { (void) no_memory(zhp->zfs_hdl); @@ -1493,6 +1504,12 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, "property setting is not allowed on " "bootable datasets")); (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); + } else if (prop == ZFS_PROP_CHECKSUM || + prop == ZFS_PROP_DEDUP) { + (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "property setting is not allowed on " + "root pools")); + (void) zfs_error(hdl, EZFS_NOTSUP, errbuf); } else { (void) zfs_standard_error(hdl, err, errbuf); } @@ -1528,15 +1545,10 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, int zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) { - zfs_cmd_t zc = { 0 }; int ret = -1; - prop_changelist_t *cl = NULL; char errbuf[1024]; libzfs_handle_t *hdl = zhp->zfs_hdl; - nvlist_t *nvl = NULL, *realprops; - zfs_prop_t prop; - boolean_t do_prefix = B_TRUE; - int added_resv; + nvlist_t *nvl = NULL; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), @@ -1548,79 +1560,149 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) goto error; } - if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl, - zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL) - goto error; + ret = zfs_prop_set_list(zhp, nvl); +error: nvlist_free(nvl); - nvl = realprops; + return (ret); +} - prop = zfs_name_to_prop(propname); - /* We don't support those properties on FreeBSD. */ - switch (prop) { - case ZFS_PROP_DEVICES: - case ZFS_PROP_ISCSIOPTIONS: - case ZFS_PROP_XATTR: - case ZFS_PROP_VSCAN: - case ZFS_PROP_NBMAND: - case ZFS_PROP_MLSLABEL: - (void) snprintf(errbuf, sizeof (errbuf), - "property '%s' not supported on FreeBSD", propname); - ret = zfs_error(hdl, EZFS_PERM, errbuf); + +/* + * Given an nvlist of property names and values, set the properties for the + * given dataset. + */ +int +zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props) +{ + zfs_cmd_t zc = { 0 }; + int ret = -1; + prop_changelist_t **cls = NULL; + int cl_idx; + char errbuf[1024]; + libzfs_handle_t *hdl = zhp->zfs_hdl; + nvlist_t *nvl; + int nvl_len; + int added_resv; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), + zhp->zfs_name); + + if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props, + zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl, + errbuf)) == NULL) goto error; - } - - if (prop == ZFS_PROP_VOLSIZE) { - if ((added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) - goto error; - } - - if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL) - goto error; - - if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "child dataset with inherited mountpoint is used " - "in a non-global zone")); - ret = zfs_error(hdl, EZFS_ZONED, errbuf); - goto error; - } /* - * We don't want to unmount & remount the dataset when changing - * its canmount property to 'on' or 'noauto'. We only use - * the changelist logic to unmount when setting canmount=off. + * We have to check for any extra properties which need to be added + * before computing the length of the nvlist. */ - if (prop == ZFS_PROP_CANMOUNT) { - uint64_t idx; - int err = zprop_string_to_index(prop, propval, &idx, - ZFS_TYPE_DATASET); - if (err == 0 && idx != ZFS_CANMOUNT_OFF) - do_prefix = B_FALSE; + for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); + elem != NULL; + elem = nvlist_next_nvpair(nvl, elem)) { + if (zfs_name_to_prop(nvpair_name(elem)) == ZFS_PROP_VOLSIZE && + (added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) { + goto error; + } } - - if (do_prefix && (ret = changelist_prefix(cl)) != 0) + /* + * Check how many properties we're setting and allocate an array to + * store changelist pointers for postfix(). + */ + nvl_len = 0; + for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); + elem != NULL; + elem = nvlist_next_nvpair(nvl, elem)) + nvl_len++; + if ((cls = calloc(nvl_len, sizeof (prop_changelist_t *))) == NULL) goto error; + cl_idx = 0; + for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); + elem != NULL; + elem = nvlist_next_nvpair(nvl, elem)) { + + zfs_prop_t prop = zfs_name_to_prop(nvpair_name(elem)); + + assert(cl_idx < nvl_len); + /* + * We don't want to unmount & remount the dataset when changing + * its canmount property to 'on' or 'noauto'. We only use + * the changelist logic to unmount when setting canmount=off. + */ + if (!(prop == ZFS_PROP_CANMOUNT && + fnvpair_value_uint64(elem) != ZFS_CANMOUNT_OFF)) { + cls[cl_idx] = changelist_gather(zhp, prop, 0, 0); + if (cls[cl_idx] == NULL) + goto error; + } + + if (prop == ZFS_PROP_MOUNTPOINT && + changelist_haszonedchild(cls[cl_idx])) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "child dataset with inherited mountpoint is used " + "in a non-global zone")); + ret = zfs_error(hdl, EZFS_ZONED, errbuf); + goto error; + } + + /* We don't support those properties on FreeBSD. */ + switch (prop) { + case ZFS_PROP_DEVICES: + case ZFS_PROP_ISCSIOPTIONS: + case ZFS_PROP_XATTR: + case ZFS_PROP_VSCAN: + case ZFS_PROP_NBMAND: + case ZFS_PROP_MLSLABEL: + (void) snprintf(errbuf, sizeof (errbuf), + "property '%s' not supported on FreeBSD", + nvpair_name(elem)); + ret = zfs_error(hdl, EZFS_PERM, errbuf); + goto error; + } + + if (cls[cl_idx] != NULL && + (ret = changelist_prefix(cls[cl_idx])) != 0) + goto error; + + cl_idx++; + } + assert(cl_idx == nvl_len); + /* - * Execute the corresponding ioctl() to set this property. + * Execute the corresponding ioctl() to set this list of properties. */ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) + if ((ret = zcmd_write_src_nvlist(hdl, &zc, nvl)) != 0 || + (ret = zcmd_alloc_dst_nvlist(hdl, &zc, 0)) != 0) goto error; ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); if (ret != 0) { - zfs_setprop_error(hdl, prop, errno, errbuf); + /* Get the list of unset properties back and report them. */ + nvlist_t *errorprops = NULL; + if (zcmd_read_dst_nvlist(hdl, &zc, &errorprops) != 0) + goto error; + for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL); + elem != NULL; + elem = nvlist_next_nvpair(nvl, elem)) { + zfs_prop_t prop = zfs_name_to_prop(nvpair_name(elem)); + zfs_setprop_error(hdl, prop, errno, errbuf); + } + nvlist_free(errorprops); + if (added_resv && errno == ENOSPC) { /* clean up the volsize property we tried to set */ uint64_t old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); nvlist_free(nvl); + nvl = NULL; zcmd_free_nvlists(&zc); + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) goto error; if (nvlist_add_uint64(nvl, @@ -1632,8 +1714,13 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc); } } else { - if (do_prefix) - ret = changelist_postfix(cl); + for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) { + if (cls[cl_idx] != NULL) { + int clp_err = changelist_postfix(cls[cl_idx]); + if (clp_err != 0) + ret = clp_err; + } + } /* * Refresh the statistics so the new property value @@ -1646,8 +1733,13 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval) error: nvlist_free(nvl); zcmd_free_nvlists(&zc); - if (cl) - changelist_free(cl); + if (cls != NULL) { + for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) { + if (cls[cl_idx] != NULL) + changelist_free(cls[cl_idx]); + } + free(cls); + } return (ret); } @@ -1778,22 +1870,21 @@ getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source) return (value); } -static char * +static const char * getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source) { nvlist_t *nv; - char *value; + const char *value; *source = NULL; if (nvlist_lookup_nvlist(zhp->zfs_props, zfs_prop_to_name(prop), &nv) == 0) { - verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); + value = fnvlist_lookup_string(nv, ZPROP_VALUE); (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source); } else { verify(!zhp->zfs_props_table || zhp->zfs_props_table[prop] == B_TRUE); - if ((value = (char *)zfs_prop_default_string(prop)) == NULL) - value = ""; + value = zfs_prop_default_string(prop); *source = ""; } @@ -2195,7 +2286,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, { char *source = NULL; uint64_t val; - char *str; + const char *str; const char *strval; boolean_t received = zfs_is_recvd_props_mode(zhp); @@ -2300,14 +2391,10 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, break; case ZFS_PROP_ORIGIN: - (void) strlcpy(propbuf, getprop_string(zhp, prop, &source), - proplen); - /* - * If there is no parent at all, return failure to indicate that - * it doesn't apply to this dataset. - */ - if (propbuf[0] == '\0') + str = getprop_string(zhp, prop, &source); + if (str == NULL) return (-1); + (void) strlcpy(propbuf, str, proplen); break; case ZFS_PROP_CLONES: @@ -2488,8 +2575,10 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, break; case PROP_TYPE_STRING: - (void) strlcpy(propbuf, - getprop_string(zhp, prop, &source), proplen); + str = getprop_string(zhp, prop, &source); + if (str == NULL) + return (-1); + (void) strlcpy(propbuf, str, proplen); break; case PROP_TYPE_INDEX: @@ -3167,9 +3256,23 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, else ost = DMU_OST_ZFS; + /* open zpool handle for prop validation */ + char pool_path[MAXNAMELEN]; + (void) strlcpy(pool_path, path, sizeof (pool_path)); + + /* truncate pool_path at first slash */ + char *p = strchr(pool_path, '/'); + if (p != NULL) + *p = '\0'; + + zpool_handle_t *zpool_handle = zpool_open(hdl, pool_path); + if (props && (props = zfs_valid_proplist(hdl, type, props, - zoned, NULL, errbuf)) == 0) + zoned, NULL, zpool_handle, errbuf)) == 0) { + zpool_close(zpool_handle); return (-1); + } + zpool_close(zpool_handle); if (type == ZFS_TYPE_VOLUME) { /* @@ -3237,13 +3340,6 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, "parent '%s' is not a filesystem"), parent); return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); - case EDOM: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "volume block size must be power of 2 from " - "512B to 128KB")); - - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); - case ENOTSUP: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be upgraded to set this " @@ -3438,7 +3534,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) type = ZFS_TYPE_FILESYSTEM; } if ((props = zfs_valid_proplist(hdl, type, props, zoned, - zhp, errbuf)) == NULL) + zhp, zhp->zpool_hdl, errbuf)) == NULL) return (-1); } @@ -3582,11 +3678,23 @@ zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props) } } + /* + * get pool handle for prop validation. assumes all snaps are in the + * same pool, as does lzc_snapshot (below). + */ + char pool[MAXNAMELEN]; + elem = nvlist_next_nvpair(snaps, NULL); + (void) strlcpy(pool, nvpair_name(elem), sizeof (pool)); + pool[strcspn(pool, "/@")] = '\0'; + zpool_handle_t *zpool_hdl = zpool_open(hdl, pool); + if (props != NULL && (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, - props, B_FALSE, NULL, errbuf)) == NULL) { + props, B_FALSE, NULL, zpool_hdl, errbuf)) == NULL) { + zpool_close(zpool_hdl); return (-1); } + zpool_close(zpool_hdl); ret = lzc_snapshot(snaps, props, &errors); @@ -4200,7 +4308,7 @@ zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path, if (cmd == ZFS_SMB_ACL_RENAME) { if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { (void) no_memory(hdl); - return (NULL); + return (0); } } diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c index dde329a8226..bb49eba0f83 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -624,9 +625,12 @@ get_snapshot_names(differ_info_t *di, const char *fromsnap, zhp = zfs_open(hdl, di->ds, ZFS_TYPE_FILESYSTEM); while (zhp != NULL) { - (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN, - origin, sizeof (origin), &src, NULL, 0, B_FALSE); - + if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, + sizeof (origin), &src, NULL, 0, B_FALSE) != 0) { + (void) zfs_close(zhp); + zhp = NULL; + break; + } if (strncmp(origin, fromsnap, fsnlen) == 0) break; diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c index aadc1f96dea..b6f6e733e8e 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, 2015 by Delphix. All rights reserved. * Copyright (c) 2012 Pawel Jakub Dawidek . * All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. @@ -315,7 +315,8 @@ typedef struct { } snapspec_arg_t; static int -snapspec_cb(zfs_handle_t *zhp, void *arg) { +snapspec_cb(zfs_handle_t *zhp, void *arg) +{ snapspec_arg_t *ssa = arg; char *shortsnapname; int err = 0; diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c index 6c115153bf0..f03da991ed6 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c @@ -1072,6 +1072,17 @@ mount_cb(zfs_handle_t *zhp, void *data) return (0); } + /* + * If this filesystem is inconsistent and has a receive resume + * token, we can not mount it. + */ + if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) && + zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, + NULL, 0, NULL, NULL, 0, B_TRUE) == 0) { + zfs_close(zhp); + return (0); + } + libzfs_add_handle(cbp, zhp); if (zfs_iter_filesystems(zhp, mount_cb, cbp) != 0) { zfs_close(zhp); diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c index 5c78e813b69..7cdcfc90fec 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c @@ -22,7 +22,7 @@ /* * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2015 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ @@ -402,29 +402,6 @@ bootfs_name_valid(const char *pool, char *bootfs) return (B_FALSE); } -/* - * Inspect the configuration to determine if any of the devices contain - * an EFI label. - */ -static boolean_t -pool_uses_efi(nvlist_t *config) -{ -#ifdef illumos - nvlist_t **child; - uint_t c, children; - - if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN, - &child, &children) != 0) - return (read_efi_label(config, NULL) >= 0); - - for (c = 0; c < children; c++) { - if (pool_uses_efi(child[c])) - return (B_TRUE); - } -#endif /* illumos */ - return (B_FALSE); -} - boolean_t zpool_is_bootable(zpool_handle_t *zhp) { @@ -453,7 +430,6 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname, char *slash, *check; struct stat64 statbuf; zpool_handle_t *zhp; - nvlist_t *nvroot; if (nvlist_alloc(&retprops, NV_UNIQUE_NAME, 0) != 0) { (void) no_memory(hdl); @@ -572,23 +548,6 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname, (void) zfs_error(hdl, EZFS_OPENFAILED, errbuf); goto error; } - verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL), - ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); - -#ifdef illumos - /* - * bootfs property cannot be set on a disk which has - * been EFI labeled. - */ - if (pool_uses_efi(nvroot)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property '%s' not supported on " - "EFI labeled devices"), propname); - (void) zfs_error(hdl, EZFS_POOL_NOTSUP, errbuf); - zpool_close(zhp); - goto error; - } -#endif /* illumos */ zpool_close(zhp); break; @@ -1164,8 +1123,8 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, zfs_prop_to_name(ZFS_PROP_ZONED), &zonestr) == 0) && strcmp(zonestr, "on") == 0); - if ((zc_fsprops = zfs_valid_proplist(hdl, - ZFS_TYPE_FILESYSTEM, fsprops, zoned, NULL, msg)) == NULL) { + if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM, + fsprops, zoned, NULL, NULL, msg)) == NULL) { goto create_failed; } if (!zc_props && @@ -1201,6 +1160,21 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, "one or more vdevs refer to the same device")); return (zfs_error(hdl, EZFS_BADDEV, msg)); + case ERANGE: + /* + * This happens if the record size is smaller or larger + * than the allowed size range, or not a power of 2. + * + * NOTE: although zfs_valid_proplist is called earlier, + * this case may have slipped through since the + * pool does not exist yet and it is therefore + * impossible to read properties e.g. max blocksize + * from the pool. + */ + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "record size invalid")); + return (zfs_error(hdl, EZFS_BADPROP, msg)); + case EOVERFLOW: /* * This occurs when one of the devices is below @@ -1311,25 +1285,6 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot) return (zfs_error(hdl, EZFS_BADVERSION, msg)); } - if (zpool_is_bootable(zhp) && nvlist_lookup_nvlist_array(nvroot, - ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0) { - uint64_t s; - - for (s = 0; s < nspares; s++) { - char *path; - - if (nvlist_lookup_string(spares[s], ZPOOL_CONFIG_PATH, - &path) == 0 && pool_uses_efi(spares[s])) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "device '%s' contains an EFI label and " - "cannot be used on root pools."), - zpool_vdev_name(hdl, NULL, spares[s], - B_FALSE)); - return (zfs_error(hdl, EZFS_POOL_NOTSUP, msg)); - } - } - } - if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) < SPA_VERSION_L2CACHE && nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, @@ -1930,7 +1885,8 @@ zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func) * and the like. */ static int -ctd_check_path(char *str) { +ctd_check_path(char *str) +{ /* * If it starts with a slash, check the last component. */ @@ -2350,11 +2306,9 @@ zpool_get_config_physpath(nvlist_t *config, char *physpath, size_t phypath_size) return (EZFS_INVALCONFIG); /* - * root pool can not have EFI labeled disks and can only have - * a single top-level vdev. + * root pool can only have a single top-level vdev. */ - if (strcmp(type, VDEV_TYPE_ROOT) != 0 || count != 1 || - pool_uses_efi(vdev_root)) + if (strcmp(type, VDEV_TYPE_ROOT) != 0 || count != 1) return (EZFS_POOL_INVALARG); (void) vdev_get_physpaths(child[0], physpath, phypath_size, &rsz, @@ -2658,16 +2612,6 @@ zpool_vdev_attach(zpool_handle_t *zhp, (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot attach %s to %s"), new_disk, old_disk); - /* - * If this is a root pool, make sure that we're not attaching an - * EFI labeled device. - */ - if (rootpool && pool_uses_efi(nvroot)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "EFI labeled devices are not supported on root pools.")); - return (zfs_error(hdl, EZFS_POOL_NOTSUP, msg)); - } - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare, &l2cache, &islog)) == 0) @@ -3786,17 +3730,18 @@ zpool_history_unpack(char *buf, uint64_t bytes_read, uint64_t *leftover, int zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp) { - char *buf = NULL; - uint64_t bufsize = HIS_BUF_LEN_DEF; + char *buf; + uint64_t buflen = HIS_BUF_LEN_DEF; uint64_t off = 0; nvlist_t **records = NULL; uint_t numrecords = 0; int err, i; - if ((buf = malloc(bufsize)) == NULL) + buf = malloc(buflen); + if (buf == NULL) return (ENOMEM); do { - uint64_t bytes_read = bufsize; + uint64_t bytes_read = buflen; uint64_t leftover; if ((err = get_history(zhp, buf, &off, &bytes_read)) != 0) @@ -3810,18 +3755,16 @@ zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp) &leftover, &records, &numrecords)) != 0) break; off -= leftover; - - /* - * If the history block is too big, double the buffer - * size and try again. - */ if (leftover == bytes_read) { + /* + * no progress made, because buffer is not big enough + * to hold this record; resize and retry. + */ + buflen *= 2; free(buf); buf = NULL; - - bufsize <<= 1; - if ((bufsize >= HIS_BUF_LEN_MAX) || - ((buf = malloc(bufsize)) == NULL)) { + if ((buflen >= HIS_BUF_LEN_MAX) || + ((buf = malloc(buflen)) == NULL)) { err = ENOMEM; break; } @@ -3829,6 +3772,7 @@ zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp) /* CONSTCOND */ } while (1); + free(buf); if (!err) { @@ -3981,13 +3925,6 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name) if (zhp) { nvlist_t *nvroot; - if (zpool_is_bootable(zhp)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "EFI labeled devices are not supported on root " - "pools.")); - return (zfs_error(hdl, EZFS_POOL_NOTSUP, errbuf)); - } - verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c index f6efa97569c..3ba069e011c 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2015 by Delphix. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright (c) 2012 Pawel Jakub Dawidek . * All rights reserved. @@ -51,6 +51,7 @@ #include "zfs_prop.h" #include "zfs_fletcher.h" #include "libzfs_impl.h" +#include #include #include #include @@ -67,6 +68,8 @@ extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *); static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *, recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int, uint64_t *); +static int guid_to_name(libzfs_handle_t *, const char *, + uint64_t, boolean_t, char *); static const zio_cksum_t zero_cksum = { 0 }; @@ -284,8 +287,7 @@ cksummer(void *arg) DMU_BACKUP_FEATURE_DEDUPPROPS); DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags); - if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == - DMU_COMPOUNDSTREAM && drr->drr_payloadlen != 0) { + if (drr->drr_payloadlen != 0) { sz = drr->drr_payloadlen; if (sz > SPA_MAXBLOCKSIZE) { @@ -818,7 +820,8 @@ typedef struct send_dump_data { char prevsnap[ZFS_MAXNAMELEN]; uint64_t prevsnap_obj; boolean_t seenfrom, seento, replicate, doall, fromorigin; - boolean_t verbose, dryrun, parsable, progress, embed_data, large_block; + boolean_t verbose, dryrun, parsable, progress, embed_data, std_out; + boolean_t large_block; int outfd; boolean_t err; nvlist_t *fss; @@ -994,17 +997,14 @@ static void * send_progress_thread(void *arg) { progress_arg_t *pa = arg; - zfs_cmd_t zc = { 0 }; zfs_handle_t *zhp = pa->pa_zhp; libzfs_handle_t *hdl = zhp->zfs_hdl; unsigned long long bytes; char buf[16]; - time_t t; struct tm *tm; - assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT); (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); if (!pa->pa_parsable) @@ -1037,6 +1037,51 @@ send_progress_thread(void *arg) } } +static void +send_print_verbose(FILE *fout, const char *tosnap, const char *fromsnap, + uint64_t size, boolean_t parsable) +{ + if (parsable) { + if (fromsnap != NULL) { + (void) fprintf(fout, "incremental\t%s\t%s", + fromsnap, tosnap); + } else { + (void) fprintf(fout, "full\t%s", + tosnap); + } + } else { + if (fromsnap != NULL) { + if (strchr(fromsnap, '@') == NULL && + strchr(fromsnap, '#') == NULL) { + (void) fprintf(fout, dgettext(TEXT_DOMAIN, + "send from @%s to %s"), + fromsnap, tosnap); + } else { + (void) fprintf(fout, dgettext(TEXT_DOMAIN, + "send from %s to %s"), + fromsnap, tosnap); + } + } else { + (void) fprintf(fout, dgettext(TEXT_DOMAIN, + "full send of %s"), + tosnap); + } + } + + if (size != 0) { + if (parsable) { + (void) fprintf(fout, "\t%llu", + (longlong_t)size); + } else { + char buf[16]; + zfs_nicenum(size, buf, sizeof (buf)); + (void) fprintf(fout, dgettext(TEXT_DOMAIN, + " estimated size is %s"), buf); + } + } + (void) fprintf(fout, "\n"); +} + static int dump_snapshot(zfs_handle_t *zhp, void *arg) { @@ -1047,6 +1092,7 @@ dump_snapshot(zfs_handle_t *zhp, void *arg) int err; boolean_t isfromsnap, istosnap, fromorigin; boolean_t exclude = B_FALSE; + FILE *fout = sdd->std_out ? stdout : stderr; err = 0; thissnap = strchr(zhp->zfs_name, '@') + 1; @@ -1115,37 +1161,14 @@ dump_snapshot(zfs_handle_t *zhp, void *arg) (sdd->fromorigin || sdd->replicate); if (sdd->verbose) { - uint64_t size; - err = estimate_ioctl(zhp, sdd->prevsnap_obj, + uint64_t size = 0; + (void) estimate_ioctl(zhp, sdd->prevsnap_obj, fromorigin, &size); - if (sdd->parsable) { - if (sdd->prevsnap[0] != '\0') { - (void) fprintf(stderr, "incremental\t%s\t%s", - sdd->prevsnap, zhp->zfs_name); - } else { - (void) fprintf(stderr, "full\t%s", - zhp->zfs_name); - } - } else { - (void) fprintf(stderr, dgettext(TEXT_DOMAIN, - "send from @%s to %s"), - sdd->prevsnap, zhp->zfs_name); - } - if (err == 0) { - if (sdd->parsable) { - (void) fprintf(stderr, "\t%llu\n", - (longlong_t)size); - } else { - char buf[16]; - zfs_nicenum(size, buf, sizeof (buf)); - (void) fprintf(stderr, dgettext(TEXT_DOMAIN, - " estimated size is %s\n"), buf); - } - sdd->size += size; - } else { - (void) fprintf(stderr, "\n"); - } + send_print_verbose(fout, zhp->zfs_name, + sdd->prevsnap[0] ? sdd->prevsnap : NULL, + size, sdd->parsable); + sdd->size += size; } if (!sdd->dryrun) { @@ -1356,6 +1379,233 @@ again: return (0); } +nvlist_t * +zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token) +{ + unsigned int version; + int nread; + unsigned long long checksum, packed_len; + + /* + * Decode token header, which is: + * -- + * Note that the only supported token version is 1. + */ + nread = sscanf(token, "%u-%llx-%llx-", + &version, &checksum, &packed_len); + if (nread != 3) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "resume token is corrupt (invalid format)")); + return (NULL); + } + + if (version != ZFS_SEND_RESUME_TOKEN_VERSION) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "resume token is corrupt (invalid version %u)"), + version); + return (NULL); + } + + /* convert hexadecimal representation to binary */ + token = strrchr(token, '-') + 1; + int len = strlen(token) / 2; + unsigned char *compressed = zfs_alloc(hdl, len); + for (int i = 0; i < len; i++) { + nread = sscanf(token + i * 2, "%2hhx", compressed + i); + if (nread != 1) { + free(compressed); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "resume token is corrupt " + "(payload is not hex-encoded)")); + return (NULL); + } + } + + /* verify checksum */ + zio_cksum_t cksum; + fletcher_4_native(compressed, len, NULL, &cksum); + if (cksum.zc_word[0] != checksum) { + free(compressed); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "resume token is corrupt (incorrect checksum)")); + return (NULL); + } + + /* uncompress */ + void *packed = zfs_alloc(hdl, packed_len); + uLongf packed_len_long = packed_len; + if (uncompress(packed, &packed_len_long, compressed, len) != Z_OK || + packed_len_long != packed_len) { + free(packed); + free(compressed); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "resume token is corrupt (decompression failed)")); + return (NULL); + } + + /* unpack nvlist */ + nvlist_t *nv; + int error = nvlist_unpack(packed, packed_len, &nv, KM_SLEEP); + free(packed); + free(compressed); + if (error != 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "resume token is corrupt (nvlist_unpack failed)")); + return (NULL); + } + return (nv); +} + +int +zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd, + const char *resume_token) +{ + char errbuf[1024]; + char *toname; + char *fromname = NULL; + uint64_t resumeobj, resumeoff, toguid, fromguid, bytes; + zfs_handle_t *zhp; + int error = 0; + char name[ZFS_MAXNAMELEN]; + enum lzc_send_flags lzc_flags = 0; + + (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, + "cannot resume send")); + + nvlist_t *resume_nvl = + zfs_send_resume_token_to_nvlist(hdl, resume_token); + if (resume_nvl == NULL) { + /* + * zfs_error_aux has already been set by + * zfs_send_resume_token_to_nvlist + */ + return (zfs_error(hdl, EZFS_FAULT, errbuf)); + } + if (flags->verbose) { + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + "resume token contents:\n")); + nvlist_print(stderr, resume_nvl); + } + + if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 || + nvlist_lookup_uint64(resume_nvl, "object", &resumeobj) != 0 || + nvlist_lookup_uint64(resume_nvl, "offset", &resumeoff) != 0 || + nvlist_lookup_uint64(resume_nvl, "bytes", &bytes) != 0 || + nvlist_lookup_uint64(resume_nvl, "toguid", &toguid) != 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "resume token is corrupt")); + return (zfs_error(hdl, EZFS_FAULT, errbuf)); + } + fromguid = 0; + (void) nvlist_lookup_uint64(resume_nvl, "fromguid", &fromguid); + + if (flags->embed_data || nvlist_exists(resume_nvl, "embedok")) + lzc_flags |= LZC_SEND_FLAG_EMBED_DATA; + + if (guid_to_name(hdl, toname, toguid, B_FALSE, name) != 0) { + if (zfs_dataset_exists(hdl, toname, ZFS_TYPE_DATASET)) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' is no longer the same snapshot used in " + "the initial send"), toname); + } else { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' used in the initial send no longer exists"), + toname); + } + return (zfs_error(hdl, EZFS_BADPATH, errbuf)); + } + zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET); + if (zhp == NULL) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "unable to access '%s'"), name); + return (zfs_error(hdl, EZFS_BADPATH, errbuf)); + } + + if (fromguid != 0) { + if (guid_to_name(hdl, toname, fromguid, B_TRUE, name) != 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "incremental source %#llx no longer exists"), + (longlong_t)fromguid); + return (zfs_error(hdl, EZFS_BADPATH, errbuf)); + } + fromname = name; + } + + if (flags->verbose) { + uint64_t size = 0; + error = lzc_send_space(zhp->zfs_name, fromname, &size); + if (error == 0) + size = MAX(0, (int64_t)(size - bytes)); + send_print_verbose(stderr, zhp->zfs_name, fromname, + size, flags->parsable); + } + + if (!flags->dryrun) { + progress_arg_t pa = { 0 }; + pthread_t tid; + /* + * If progress reporting is requested, spawn a new thread to + * poll ZFS_IOC_SEND_PROGRESS at a regular interval. + */ + if (flags->progress) { + pa.pa_zhp = zhp; + pa.pa_fd = outfd; + pa.pa_parsable = flags->parsable; + + error = pthread_create(&tid, NULL, + send_progress_thread, &pa); + if (error != 0) { + zfs_close(zhp); + return (error); + } + } + + error = lzc_send_resume(zhp->zfs_name, fromname, outfd, + lzc_flags, resumeobj, resumeoff); + + if (flags->progress) { + (void) pthread_cancel(tid); + (void) pthread_join(tid, NULL); + } + + char errbuf[1024]; + (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, + "warning: cannot send '%s'"), zhp->zfs_name); + + zfs_close(zhp); + + switch (error) { + case 0: + return (0); + case EXDEV: + case ENOENT: + case EDQUOT: + case EFBIG: + case EIO: + case ENOLINK: + case ENOSPC: +#ifdef illumos + case ENOSTR: +#endif + case ENXIO: + case EPIPE: + case ERANGE: + case EFAULT: + case EROFS: + zfs_error_aux(hdl, strerror(errno)); + return (zfs_error(hdl, EZFS_BADBACKUP, errbuf)); + + default: + return (zfs_standard_error(hdl, errno, errbuf)); + } + } + + + zfs_close(zhp); + + return (error); +} + /* * Generate a send stream for the dataset identified by the argument zhp. * @@ -1388,6 +1638,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, int pipefd[2]; dedup_arg_t dda = { 0 }; int featureflags = 0; + FILE *fout; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot send '%s'"), zhp->zfs_name); @@ -1515,6 +1766,9 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, sdd.filter_cb_arg = cb_arg; if (debugnvp) sdd.debugnv = *debugnvp; + if (sdd.verbose && sdd.dryrun) + sdd.std_out = B_TRUE; + fout = sdd.std_out ? stdout : stderr; /* * Some flags require that we place user holds on the datasets that are @@ -1554,12 +1808,12 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, if (flags->verbose) { if (flags->parsable) { - (void) fprintf(stderr, "size\t%llu\n", + (void) fprintf(fout, "size\t%llu\n", (longlong_t)sdd.size); } else { char buf[16]; zfs_nicenum(sdd.size, buf, sizeof (buf)); - (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + (void) fprintf(fout, dgettext(TEXT_DOMAIN, "total estimated size is %s\n"), buf); } } @@ -1891,6 +2145,7 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen, typedef struct guid_to_name_data { uint64_t guid; + boolean_t bookmark_ok; char *name; char *skip; } guid_to_name_data_t; @@ -1899,20 +2154,25 @@ static int guid_to_name_cb(zfs_handle_t *zhp, void *arg) { guid_to_name_data_t *gtnd = arg; + const char *slash; int err; if (gtnd->skip != NULL && - strcmp(zhp->zfs_name, gtnd->skip) == 0) { + (slash = strrchr(zhp->zfs_name, '/')) != NULL && + strcmp(slash + 1, gtnd->skip) == 0) { + zfs_close(zhp); return (0); } - if (zhp->zfs_dmustats.dds_guid == gtnd->guid) { + if (zfs_prop_get_int(zhp, ZFS_PROP_GUID) == gtnd->guid) { (void) strcpy(gtnd->name, zhp->zfs_name); zfs_close(zhp); return (EEXIST); } err = zfs_iter_children(zhp, guid_to_name_cb, gtnd); + if (err != EEXIST && gtnd->bookmark_ok) + err = zfs_iter_bookmarks(zhp, guid_to_name_cb, gtnd); zfs_close(zhp); return (err); } @@ -1926,45 +2186,48 @@ guid_to_name_cb(zfs_handle_t *zhp, void *arg) */ static int guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid, - char *name) + boolean_t bookmark_ok, char *name) { - /* exhaustive search all local snapshots */ char pname[ZFS_MAXNAMELEN]; guid_to_name_data_t gtnd; - int err = 0; - zfs_handle_t *zhp; - char *cp; gtnd.guid = guid; + gtnd.bookmark_ok = bookmark_ok; gtnd.name = name; gtnd.skip = NULL; - (void) strlcpy(pname, parent, sizeof (pname)); - /* - * Search progressively larger portions of the hierarchy. This will + * Search progressively larger portions of the hierarchy, starting + * with the filesystem specified by 'parent'. This will * select the "most local" version of the origin snapshot in the case * that there are multiple matching snapshots in the system. */ - while ((cp = strrchr(pname, '/')) != NULL) { - + (void) strlcpy(pname, parent, sizeof (pname)); + char *cp = strrchr(pname, '@'); + if (cp == NULL) + cp = strchr(pname, '\0'); + for (; cp != NULL; cp = strrchr(pname, '/')) { /* Chop off the last component and open the parent */ *cp = '\0'; - zhp = make_dataset_handle(hdl, pname); + zfs_handle_t *zhp = make_dataset_handle(hdl, pname); if (zhp == NULL) continue; - - err = zfs_iter_children(zhp, guid_to_name_cb, >nd); + int err = guid_to_name_cb(zfs_handle_dup(zhp), >nd); + if (err != EEXIST) + err = zfs_iter_children(zhp, guid_to_name_cb, >nd); + if (err != EEXIST && bookmark_ok) + err = zfs_iter_bookmarks(zhp, guid_to_name_cb, >nd); zfs_close(zhp); if (err == EEXIST) return (0); /* - * Remember the dataset that we already searched, so we - * skip it next time through. + * Remember the last portion of the dataset so we skip it next + * time through (as we've already searched that portion of the + * hierarchy). */ - gtnd.skip = pname; + gtnd.skip = strrchr(pname, '/') + 1; } return (ENOENT); @@ -2562,11 +2825,9 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap) switch (drr->drr_type) { case DRR_BEGIN: - /* NB: not to be used on v2 stream packages */ if (drr->drr_payloadlen != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "invalid substream header")); - return (zfs_error(hdl, EZFS_BADSTREAM, errbuf)); + (void) recv_read(hdl, fd, buf, + drr->drr_payloadlen, B_FALSE, NULL); } break; @@ -2627,6 +2888,40 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap) return (-1); } +static void +recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap, + boolean_t resumable) +{ + char target_fs[ZFS_MAXNAMELEN]; + + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "checksum mismatch or incomplete stream")); + + if (!resumable) + return; + (void) strlcpy(target_fs, target_snap, sizeof (target_fs)); + *strchr(target_fs, '@') = '\0'; + zfs_handle_t *zhp = zfs_open(hdl, target_fs, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); + if (zhp == NULL) + return; + + char token_buf[ZFS_MAXPROPLEN]; + int error = zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, + token_buf, sizeof (token_buf), + NULL, NULL, 0, B_TRUE); + if (error == 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "checksum mismatch or incomplete stream.\n" + "Partially received snapshot is saved.\n" + "A resuming stream can be generated on the sending " + "system by running:\n" + " zfs send -t %s"), + token_buf); + } + zfs_close(zhp); +} + /* * Restores a backup of tosnap from the file descriptor specified by infd. */ @@ -2790,7 +3085,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, */ if (drrb->drr_flags & DRR_FLAG_CLONE) { if (guid_to_name(hdl, zc.zc_value, - drrb->drr_fromguid, zc.zc_string) != 0) { + drrb->drr_fromguid, B_FALSE, zc.zc_string) != 0) { zcmd_free_nvlists(&zc); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "local origin for clone %s does not exist"), @@ -2806,8 +3101,10 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, zc.zc_string); } + boolean_t resuming = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) & + DMU_BACKUP_FEATURE_RESUMING; stream_wantsnewfs = (drrb->drr_fromguid == 0 || - (drrb->drr_flags & DRR_FLAG_CLONE) || originsnap); + (drrb->drr_flags & DRR_FLAG_CLONE) || originsnap) && !resuming; if (stream_wantsnewfs) { /* @@ -2826,7 +3123,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, char suffix[ZFS_MAXNAMELEN]; (void) strcpy(suffix, strrchr(zc.zc_value, '/')); if (guid_to_name(hdl, zc.zc_name, parent_snapguid, - zc.zc_value) == 0) { + B_FALSE, zc.zc_value) == 0) { *strchr(zc.zc_value, '@') = '\0'; (void) strcat(zc.zc_value, suffix); } @@ -2853,7 +3150,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, char snap[ZFS_MAXNAMELEN]; (void) strcpy(snap, strchr(zc.zc_value, '@')); if (guid_to_name(hdl, zc.zc_name, drrb->drr_fromguid, - zc.zc_value) == 0) { + B_FALSE, zc.zc_value) == 0) { *strchr(zc.zc_value, '@') = '\0'; (void) strcat(zc.zc_value, snap); } @@ -2867,11 +3164,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, zfs_handle_t *zhp; /* - * Destination fs exists. Therefore this should either - * be an incremental, or the stream specifies a new fs - * (full stream or clone) and they want us to blow it - * away (and have therefore specified -F and removed any - * snapshots). + * Destination fs exists. It must be one of these cases: + * - an incremental send stream + * - the stream specifies a new fs (full stream or clone) + * and they want us to blow away the existing fs (and + * have therefore specified -F and removed any snapshots) + * - we are resuming a failed receive. */ if (stream_wantsnewfs) { if (!flags->force) { @@ -2926,6 +3224,18 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, return (-1); } } + + /* + * If we are resuming a newfs, set newfs here so that we will + * mount it if the recv succeeds this time. We can tell + * that it was a newfs on the first recv because the fs + * itself will be inconsistent (if the fs existed when we + * did the first recv, we would have received it into + * .../%recv). + */ + if (resuming && zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT)) + newfs = B_TRUE; + zfs_close(zhp); } else { /* @@ -2958,9 +3268,10 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, newfs = B_TRUE; } - zc.zc_begin_record = drr_noswap->drr_u.drr_begin; + zc.zc_begin_record = *drr_noswap; zc.zc_cookie = infd; zc.zc_guid = flags->force; + zc.zc_resumable = flags->resumable; if (flags->verbose) { (void) printf("%s %s stream of %s into %s\n", flags->dryrun ? "would receive" : "receiving", @@ -3097,8 +3408,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); break; case ECKSUM: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "invalid stream (checksum mismatch)")); + recv_ecksum_set_aux(hdl, zc.zc_value, flags->resumable); (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); break; case ENOTSUP: @@ -3300,7 +3610,8 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, * Restores a backup of tosnap from the file descriptor specified by infd. * Return 0 on total success, -2 if some things couldn't be * destroyed/renamed/promoted, -1 if some things couldn't be received. - * (-1 will override -2). + * (-1 will override -2, if -1 and the resumable flag was specified the + * transfer can be resumed if the sending side supports it). */ int zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props, diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c index ab8a9bffefb..1316aec7c68 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c @@ -22,7 +22,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2011, 2015 by Delphix. All rights reserved. */ /* @@ -782,8 +782,9 @@ zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len) if (len == 0) len = 16 * 1024; zc->zc_nvlist_dst_size = len; - if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t) - zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == 0) + zc->zc_nvlist_dst = + (uint64_t)(uintptr_t)zfs_alloc(hdl, zc->zc_nvlist_dst_size); + if (zc->zc_nvlist_dst == 0) return (-1); return (0); @@ -798,9 +799,9 @@ int zcmd_expand_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc) { free((void *)(uintptr_t)zc->zc_nvlist_dst); - if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t) - zfs_alloc(hdl, zc->zc_nvlist_dst_size)) - == 0) + zc->zc_nvlist_dst = + (uint64_t)(uintptr_t)zfs_alloc(hdl, zc->zc_nvlist_dst_size); + if (zc->zc_nvlist_dst == 0) return (-1); return (0); @@ -815,6 +816,9 @@ zcmd_free_nvlists(zfs_cmd_t *zc) free((void *)(uintptr_t)zc->zc_nvlist_conf); free((void *)(uintptr_t)zc->zc_nvlist_src); free((void *)(uintptr_t)zc->zc_nvlist_dst); + zc->zc_nvlist_conf = NULL; + zc->zc_nvlist_src = NULL; + zc->zc_nvlist_dst = NULL; } static int diff --git a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c index 5ba660de6ba..69d332a5f8e 100644 --- a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c +++ b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c @@ -514,6 +514,13 @@ lzc_get_holds(const char *snapname, nvlist_t **holdsp) int lzc_send(const char *snapname, const char *from, int fd, enum lzc_send_flags flags) +{ + return (lzc_send_resume(snapname, from, fd, flags, 0, 0)); +} + +int +lzc_send_resume(const char *snapname, const char *from, int fd, + enum lzc_send_flags flags, uint64_t resumeobj, uint64_t resumeoff) { nvlist_t *args; int err; @@ -526,6 +533,10 @@ lzc_send(const char *snapname, const char *from, int fd, fnvlist_add_boolean(args, "largeblockok"); if (flags & LZC_SEND_FLAG_EMBED_DATA) fnvlist_add_boolean(args, "embedok"); + if (resumeobj != 0 || resumeoff != 0) { + fnvlist_add_uint64(args, "resume_object", resumeobj); + fnvlist_add_uint64(args, "resume_offset", resumeoff); + } err = lzc_ioctl(ZFS_IOC_SEND_NEW, snapname, args, NULL); nvlist_free(args); return (err); @@ -583,22 +594,9 @@ recv_read(int fd, void *buf, int ilen) return (0); } -/* - * The simplest receive case: receive from the specified fd, creating the - * specified snapshot. Apply the specified properties a "received" properties - * (which can be overridden by locally-set properties). If the stream is a - * clone, its origin snapshot must be specified by 'origin'. The 'force' - * flag will cause the target filesystem to be rolled back or destroyed if - * necessary to receive. - * - * Return 0 on success or an errno on failure. - * - * Note: this interface does not work on dedup'd streams - * (those with DMU_BACKUP_FEATURE_DEDUP). - */ -int -lzc_receive(const char *snapname, nvlist_t *props, const char *origin, - boolean_t force, int fd) +static int +lzc_receive_impl(const char *snapname, nvlist_t *props, const char *origin, + boolean_t force, boolean_t resumable, int fd) { /* * The receive ioctl is still legacy, so we need to construct our own @@ -608,7 +606,6 @@ lzc_receive(const char *snapname, nvlist_t *props, const char *origin, char *atp; char *packed = NULL; size_t size; - dmu_replay_record_t drr; int error; ASSERT3S(g_refcount, >, 0); @@ -644,10 +641,9 @@ lzc_receive(const char *snapname, nvlist_t *props, const char *origin, (void) strlcpy(zc.zc_string, origin, sizeof (zc.zc_string)); /* zc_begin_record is non-byteswapped BEGIN record */ - error = recv_read(fd, &drr, sizeof (drr)); + error = recv_read(fd, &zc.zc_begin_record, sizeof (zc.zc_begin_record)); if (error != 0) goto out; - zc.zc_begin_record = drr.drr_u.drr_begin; /* zc_cookie is fd to read from */ zc.zc_cookie = fd; @@ -655,6 +651,8 @@ lzc_receive(const char *snapname, nvlist_t *props, const char *origin, /* zc guid is force flag */ zc.zc_guid = force; + zc.zc_resumable = resumable; + /* zc_cleanup_fd is unused */ zc.zc_cleanup_fd = -1; @@ -669,6 +667,39 @@ out: return (error); } +/* + * The simplest receive case: receive from the specified fd, creating the + * specified snapshot. Apply the specified properties as "received" properties + * (which can be overridden by locally-set properties). If the stream is a + * clone, its origin snapshot must be specified by 'origin'. The 'force' + * flag will cause the target filesystem to be rolled back or destroyed if + * necessary to receive. + * + * Return 0 on success or an errno on failure. + * + * Note: this interface does not work on dedup'd streams + * (those with DMU_BACKUP_FEATURE_DEDUP). + */ +int +lzc_receive(const char *snapname, nvlist_t *props, const char *origin, + boolean_t force, int fd) +{ + return (lzc_receive_impl(snapname, props, origin, force, B_FALSE, fd)); +} + +/* + * Like lzc_receive, but if the receive fails due to premature stream + * termination, the intermediate state will be preserved on disk. In this + * case, ECKSUM will be returned. The receive may subsequently be resumed + * with a resuming send stream generated by lzc_send_resume(). + */ +int +lzc_receive_resumable(const char *snapname, nvlist_t *props, const char *origin, + boolean_t force, int fd) +{ + return (lzc_receive_impl(snapname, props, origin, force, B_TRUE, fd)); +} + /* * Roll back this filesystem or volume to its most recent snapshot. * If snapnamebuf is not NULL, it will be filled in with the name diff --git a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h index b6a4c12f250..b9235d1eb10 100644 --- a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h +++ b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2012, 2014 by Delphix. All rights reserved. * Copyright (c) 2013 by Martin Matuska . All rights reserved. */ @@ -59,7 +59,11 @@ enum lzc_send_flags { }; int lzc_send(const char *, const char *, int, enum lzc_send_flags); +int lzc_send_resume(const char *, const char *, int, + enum lzc_send_flags, uint64_t, uint64_t); int lzc_receive(const char *, nvlist_t *, const char *, boolean_t, int); +int lzc_receive_resumable(const char *, nvlist_t *, const char *, + boolean_t, int); int lzc_send_space(const char *, const char *, uint64_t *); boolean_t lzc_exists(const char *); diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c b/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c index f2515d5ab4a..126a7c1347a 100644 --- a/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c +++ b/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2014 by Delphix. All rights reserved. + * Copyright (c) 2012, 2015 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ @@ -523,7 +523,7 @@ vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, /*ARGSUSED*/ int vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset, - int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp) + int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp) { ssize_t iolen, split; diff --git a/cddl/lib/libzfs/Makefile b/cddl/lib/libzfs/Makefile index 19a3f2e1b1b..b960c9e97d9 100644 --- a/cddl/lib/libzfs/Makefile +++ b/cddl/lib/libzfs/Makefile @@ -8,10 +8,10 @@ LIB= zfs DPADD= ${LIBMD} ${LIBPTHREAD} ${LIBUMEM} ${LIBUTIL} ${LIBM} ${LIBNVPAIR} \ ${LIBAVL} ${LIBZFS_CORE} ${LIBUUTIL} ${LIBBSDXML} ${LIBGEOM} \ - ${LIBNVPAIR} + ${LIBNVPAIR} ${LIBZ} LDADD= -lmd -lpthread -lumem -lutil -luutil -lm -lnvpair -lavl \ - -lbsdxml -lgeom -lnvpair -lzfs_core + -lbsdxml -lgeom -lnvpair -lz -lzfs_core SRCS= deviceid.c \ fsshare.c \ diff --git a/contrib/llvm/patches/patch-09-clang-r250657-openmp.diff b/contrib/llvm/patches/patch-09-clang-r250657-openmp.diff new file mode 100644 index 00000000000..e784673d026 --- /dev/null +++ b/contrib/llvm/patches/patch-09-clang-r250657-openmp.diff @@ -0,0 +1,182 @@ +Pull in r248379 from upstream clang trunk (by Jörg Sonnenberger): + + Refactor library decision for -fopenmp support from Darwin into a + function for sharing with other platforms. + +Pull in r248424 from upstream clang trunk (by Jörg Sonnenberger): + + Push OpenMP linker flags after linker input on Darwin. Don't add any + libraries if -nostdlib is specified. Test. + +Pull in r248426 from upstream clang trunk (by Jörg Sonnenberger): + + Support linking against OpenMP runtime on NetBSD. + +Pull in r250657 from upstream clang trunk (by Dimitry Andric): + + Support linking against OpenMP runtime on FreeBSD. + +Introduced here: http://svnweb.freebsd.org/changeset/base/289523 + +Index: tools/clang/lib/Driver/Tools.cpp +=================================================================== +--- tools/clang/lib/Driver/Tools.cpp ++++ tools/clang/lib/Driver/Tools.cpp +@@ -2460,6 +2460,28 @@ static OpenMPRuntimeKind getOpenMPRuntime(const To + return RT; + } + ++static void addOpenMPRuntime(ArgStringList &CmdArgs, const ToolChain &TC, ++ const ArgList &Args) { ++ if (!Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, ++ options::OPT_fno_openmp, false)) ++ return; ++ ++ switch (getOpenMPRuntime(TC, Args)) { ++ case OMPRT_OMP: ++ CmdArgs.push_back("-lomp"); ++ break; ++ case OMPRT_GOMP: ++ CmdArgs.push_back("-lgomp"); ++ break; ++ case OMPRT_IOMP5: ++ CmdArgs.push_back("-liomp5"); ++ break; ++ case OMPRT_Unknown: ++ // Already diagnosed. ++ break; ++ } ++} ++ + static void addSanitizerRuntime(const ToolChain &TC, const ArgList &Args, + ArgStringList &CmdArgs, StringRef Sanitizer, + bool IsShared) { +@@ -6527,24 +6549,6 @@ void darwin::Linker::ConstructJob(Compilation &C, + + Args.AddAllArgs(CmdArgs, options::OPT_L); + +- if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, +- options::OPT_fno_openmp, false)) { +- switch (getOpenMPRuntime(getToolChain(), Args)) { +- case OMPRT_OMP: +- CmdArgs.push_back("-lomp"); +- break; +- case OMPRT_GOMP: +- CmdArgs.push_back("-lgomp"); +- break; +- case OMPRT_IOMP5: +- CmdArgs.push_back("-liomp5"); +- break; +- case OMPRT_Unknown: +- // Already diagnosed. +- break; +- } +- } +- + AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); + // Build the input file for -filelist (list of linker input files) in case we + // need it later +@@ -6563,6 +6567,10 @@ void darwin::Linker::ConstructJob(Compilation &C, + InputFileList.push_back(II.getFilename()); + } + ++ if (!Args.hasArg(options::OPT_nostdlib) && ++ !Args.hasArg(options::OPT_nodefaultlibs)) ++ addOpenMPRuntime(CmdArgs, getToolChain(), Args); ++ + if (isObjCRuntimeLinked(Args) && !Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) { + // We use arclite library for both ARC and subscripting support. +@@ -7358,6 +7366,7 @@ void freebsd::Linker::ConstructJob(Compilation &C, + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) { ++ addOpenMPRuntime(CmdArgs, ToolChain, Args); + if (D.CCCIsCXX()) { + ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); + if (Args.hasArg(options::OPT_pg)) +@@ -7673,6 +7682,7 @@ void netbsd::Linker::ConstructJob(Compilation &C, + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) { ++ addOpenMPRuntime(CmdArgs, getToolChain(), Args); + if (D.CCCIsCXX()) { + getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs); + CmdArgs.push_back("-lm"); +Index: tools/clang/test/Driver/fopenmp.c +=================================================================== +--- tools/clang/test/Driver/fopenmp.c ++++ tools/clang/test/Driver/fopenmp.c +@@ -1,6 +1,15 @@ + // RUN: %clang -target x86_64-linux-gnu -fopenmp=libomp -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-OPENMP + // RUN: %clang -target x86_64-linux-gnu -fopenmp=libgomp -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-NO-OPENMP + // RUN: %clang -target x86_64-linux-gnu -fopenmp=libiomp5 -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-OPENMP ++// RUN: %clang -target x86_64-apple-darwin -fopenmp=libomp -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-OPENMP ++// RUN: %clang -target x86_64-apple-darwin -fopenmp=libgomp -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-NO-OPENMP ++// RUN: %clang -target x86_64-apple-darwin -fopenmp=libiomp5 -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-OPENMP ++// RUN: %clang -target x86_64-freebsd -fopenmp=libomp -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-OPENMP ++// RUN: %clang -target x86_64-freebsd -fopenmp=libgomp -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-NO-OPENMP ++// RUN: %clang -target x86_64-freebsd -fopenmp=libiomp5 -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-OPENMP ++// RUN: %clang -target x86_64-netbsd -fopenmp=libomp -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-OPENMP ++// RUN: %clang -target x86_64-netbsd -fopenmp=libgomp -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-NO-OPENMP ++// RUN: %clang -target x86_64-netbsd -fopenmp=libiomp5 -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CC1-OPENMP + // + // CHECK-CC1-OPENMP: "-cc1" + // CHECK-CC1-OPENMP: "-fopenmp" +@@ -12,6 +21,30 @@ + // RUN: %clang -target x86_64-linux-gnu -fopenmp=libgomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-GOMP + // RUN: %clang -target x86_64-linux-gnu -fopenmp=libiomp5 %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-IOMP5 + // ++// RUN: %clang -nostdlib -target x86_64-linux-gnu -fopenmp=libomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-OMP ++// RUN: %clang -nostdlib -target x86_64-linux-gnu -fopenmp=libgomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-GOMP ++// RUN: %clang -nostdlib -target x86_64-linux-gnu -fopenmp=libiomp5 %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-IOMP5 ++// ++// RUN: %clang -target x86_64-darwin -fopenmp=libomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-OMP ++// RUN: %clang -target x86_64-darwin -fopenmp=libgomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-GOMP ++// RUN: %clang -target x86_64-darwin -fopenmp=libiomp5 %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-IOMP5 ++// ++// RUN: %clang -nostdlib -target x86_64-darwin -fopenmp=libomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-OMP ++// RUN: %clang -nostdlib -target x86_64-darwin -fopenmp=libgomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-GOMP ++// RUN: %clang -nostdlib -target x86_64-darwin -fopenmp=libiomp5 %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-IOMP5 ++// ++// RUN: %clang -target x86_64-netbsd -fopenmp=libomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-OMP ++// RUN: %clang -target x86_64-netbsd -fopenmp=libgomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-GOMP ++// RUN: %clang -target x86_64-netbsd -fopenmp=libiomp5 %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-IOMP5 ++// ++// RUN: %clang -nostdlib -target x86_64-freebsd -fopenmp=libomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-OMP ++// RUN: %clang -nostdlib -target x86_64-freebsd -fopenmp=libgomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-GOMP ++// RUN: %clang -nostdlib -target x86_64-freebsd -fopenmp=libiomp5 %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-IOMP5 ++// ++// RUN: %clang -nostdlib -target x86_64-netbsd -fopenmp=libomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-OMP ++// RUN: %clang -nostdlib -target x86_64-netbsd -fopenmp=libgomp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-GOMP ++// RUN: %clang -nostdlib -target x86_64-netbsd -fopenmp=libiomp5 %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-IOMP5 ++// + // CHECK-LD-OMP: "{{.*}}ld{{(.exe)?}}" + // CHECK-LD-OMP: "-lomp" + // +@@ -21,6 +54,15 @@ + // CHECK-LD-IOMP5: "{{.*}}ld{{(.exe)?}}" + // CHECK-LD-IOMP5: "-liomp5" + // ++// CHECK-NO-OMP: "{{.*}}ld{{(.exe)?}}" ++// CHECK-NO-OMP-NOT: "-lomp" ++// ++// CHECK-NO-GOMP: "{{.*}}ld{{(.exe)?}}" ++// CHECK-NO-GOMP-NOT: "-lgomp" ++// ++// CHECK-NO-IOMP5: "{{.*}}ld{{(.exe)?}}" ++// CHECK-NO-IOMP5-NOT: "-liomp5" ++// + // We'd like to check that the default is sane, but until we have the ability + // to *always* semantically analyze OpenMP without always generating runtime + // calls (in the event of an unsupported runtime), we don't have a good way to +@@ -28,6 +70,9 @@ + // OpenMP runtime. + // + // RUN: %clang -target x86_64-linux-gnu -fopenmp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-ANY ++// RUN: %clang -target x86_64-darwin -fopenmp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-ANY ++// RUN: %clang -target x86_64-freebsd -fopenmp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-ANY ++// RUN: %clang -target x86_64-netbsd -fopenmp %s -o %t -### 2>&1 | FileCheck %s --check-prefix=CHECK-LD-ANY + // + // CHECK-LD-ANY: "{{.*}}ld{{(.exe)?}}" + // CHECK-LD-ANY: "-l{{(omp|gomp|iomp5)}}" diff --git a/contrib/llvm/tools/clang/lib/Driver/Tools.cpp b/contrib/llvm/tools/clang/lib/Driver/Tools.cpp index 3c26fc5c86d..7352537cf84 100644 --- a/contrib/llvm/tools/clang/lib/Driver/Tools.cpp +++ b/contrib/llvm/tools/clang/lib/Driver/Tools.cpp @@ -2460,6 +2460,28 @@ static OpenMPRuntimeKind getOpenMPRuntime(const ToolChain &TC, return RT; } +static void addOpenMPRuntime(ArgStringList &CmdArgs, const ToolChain &TC, + const ArgList &Args) { + if (!Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, + options::OPT_fno_openmp, false)) + return; + + switch (getOpenMPRuntime(TC, Args)) { + case OMPRT_OMP: + CmdArgs.push_back("-lomp"); + break; + case OMPRT_GOMP: + CmdArgs.push_back("-lgomp"); + break; + case OMPRT_IOMP5: + CmdArgs.push_back("-liomp5"); + break; + case OMPRT_Unknown: + // Already diagnosed. + break; + } +} + static void addSanitizerRuntime(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, StringRef Sanitizer, bool IsShared) { @@ -6527,24 +6549,6 @@ void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, Args.AddAllArgs(CmdArgs, options::OPT_L); - if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, - options::OPT_fno_openmp, false)) { - switch (getOpenMPRuntime(getToolChain(), Args)) { - case OMPRT_OMP: - CmdArgs.push_back("-lomp"); - break; - case OMPRT_GOMP: - CmdArgs.push_back("-lgomp"); - break; - case OMPRT_IOMP5: - CmdArgs.push_back("-liomp5"); - break; - case OMPRT_Unknown: - // Already diagnosed. - break; - } - } - AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); // Build the input file for -filelist (list of linker input files) in case we // need it later @@ -6563,6 +6567,10 @@ void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, InputFileList.push_back(II.getFilename()); } + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) + addOpenMPRuntime(CmdArgs, getToolChain(), Args); + if (isObjCRuntimeLinked(Args) && !Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { // We use arclite library for both ARC and subscripting support. @@ -7358,6 +7366,7 @@ void freebsd::Linker::ConstructJob(Compilation &C, const JobAction &JA, if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { + addOpenMPRuntime(CmdArgs, ToolChain, Args); if (D.CCCIsCXX()) { ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); if (Args.hasArg(options::OPT_pg)) @@ -7673,6 +7682,7 @@ void netbsd::Linker::ConstructJob(Compilation &C, const JobAction &JA, if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { + addOpenMPRuntime(CmdArgs, getToolChain(), Args); if (D.CCCIsCXX()) { getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs); CmdArgs.push_back("-lm"); diff --git a/contrib/netbsd-tests/bin/dd/t_dd.sh b/contrib/netbsd-tests/bin/dd/t_dd.sh index d713ad9cb24..62379c24148 100755 --- a/contrib/netbsd-tests/bin/dd/t_dd.sh +++ b/contrib/netbsd-tests/bin/dd/t_dd.sh @@ -44,6 +44,12 @@ length_head() { "the one expected to fail. (NetBSD PR bin/8521)" } length_body() { + # Begin FreeBSD + if ! df /dev/fd | grep -q '^fdescfs'; then + atf_skip "fdescfs is not mounted on /dev/fd" + fi + # End FreeBSD + test_dd_length 512 \ "dd if=/dev/zero of=/dev/fd/5 count=1 5>&1 >/dev/null 2>/dev/null" test_dd_length 512 \ diff --git a/contrib/unbound/doc/unbound.conf.5.in b/contrib/unbound/doc/unbound.conf.5.in index c497eeebf33..42653a51ccf 100644 --- a/contrib/unbound/doc/unbound.conf.5.in +++ b/contrib/unbound/doc/unbound.conf.5.in @@ -481,7 +481,7 @@ kill \-HUP `cat @UNBOUND_PIDFILE@` .fi triggers a reload, .nf -kill \-QUIT `cat @UNBOUND_PIDFILE@` +kill \-TERM `cat @UNBOUND_PIDFILE@` .fi gracefully terminates. .TP diff --git a/contrib/wpa/hostapd/ChangeLog b/contrib/wpa/hostapd/ChangeLog index e6f8c6a03e0..af54e1e5b4e 100644 --- a/contrib/wpa/hostapd/ChangeLog +++ b/contrib/wpa/hostapd/ChangeLog @@ -1,5 +1,41 @@ ChangeLog for hostapd +2015-09-27 - v2.5 + * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding + [http://w1.fi/security/2015-2/] (CVE-2015-4141) + * fixed WMM Action frame parser + [http://w1.fi/security/2015-3/] (CVE-2015-4142) + * fixed EAP-pwd server missing payload length validation + [http://w1.fi/security/2015-4/] + (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145) + * fixed validation of WPS and P2P NFC NDEF record payload length + [http://w1.fi/security/2015-5/] + * nl80211: + - fixed vendor command handling to check OUI properly + * fixed hlr_auc_gw build with OpenSSL + * hlr_auc_gw: allow Milenage RES length to be reduced + * disable HT for a station that does not support WMM/QoS + * added support for hashed password (NtHash) in EAP-pwd server + * fixed and extended dynamic VLAN cases + * added EAP-EKE server support for deriving Session-Id + * set Acct-Session-Id to a random value to make it more likely to be + unique even if the device does not have a proper clock + * added more 2.4 GHz channels for 20/40 MHz HT co-ex scan + * modified SAE routines to be more robust and PWE generation to be + stronger against timing attacks + * added support for Brainpool Elliptic Curves with SAE + * increases maximum value accepted for cwmin/cwmax + * added support for CCMP-256 and GCMP-256 as group ciphers with FT + * added Fast Session Transfer (FST) module + * removed optional fields from RSNE when using FT with PMF + (workaround for interoperability issues with iOS 8.4) + * added EAP server support for TLS session resumption + * fixed key derivation for Suite B 192-bit AKM (this breaks + compatibility with the earlier version) + * added mechanism to track unconnected stations and do minimal band + steering + * number of small fixes + 2015-03-15 - v2.4 * allow OpenSSL cipher configuration to be set for internal EAP server (openssl_ciphers parameter) diff --git a/contrib/wpa/hostapd/config_file.c b/contrib/wpa/hostapd/config_file.c index 53143f76cfe..82ac61d7729 100644 --- a/contrib/wpa/hostapd/config_file.c +++ b/contrib/wpa/hostapd/config_file.c @@ -222,9 +222,15 @@ static int hostapd_config_read_eap_user(const char *fname, return 0; if (os_strncmp(fname, "sqlite:", 7) == 0) { +#ifdef CONFIG_SQLITE os_free(conf->eap_user_sqlite); conf->eap_user_sqlite = os_strdup(fname + 7); return 0; +#else /* CONFIG_SQLITE */ + wpa_printf(MSG_ERROR, + "EAP user file in SQLite DB, but CONFIG_SQLITE was not enabled in the build."); + return -1; +#endif /* CONFIG_SQLITE */ } f = fopen(fname, "r"); @@ -775,6 +781,24 @@ static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, } +static int hostapd_parse_chanlist(struct hostapd_config *conf, char *val) +{ + char *pos; + + /* for backwards compatibility, translate ' ' in conf str to ',' */ + pos = val; + while (pos) { + pos = os_strchr(pos, ' '); + if (pos) + *pos++ = ','; + } + if (freq_range_list_parse(&conf->acs_ch_list, val)) + return -1; + + return 0; +} + + static int hostapd_parse_intlist(int **int_list, char *val) { int *list; @@ -875,7 +899,9 @@ static int hostapd_config_read_int10(const char *value) static int valid_cw(int cw) { return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 || - cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023); + cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 || + cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 || + cw == 32767); } @@ -886,11 +912,11 @@ enum { IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */ }; -static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, - char *val) +static int hostapd_config_tx_queue(struct hostapd_config *conf, + const char *name, const char *val) { int num; - char *pos; + const char *pos; struct hostapd_tx_queue_params *queue; /* skip 'tx_queue_' prefix */ @@ -1134,13 +1160,23 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf, if (os_strstr(capab, "[BF-ANTENNA-2]") && (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET); + if (os_strstr(capab, "[BF-ANTENNA-3]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + conf->vht_capab |= (2 << VHT_CAP_BEAMFORMEE_STS_OFFSET); + if (os_strstr(capab, "[BF-ANTENNA-4]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET); if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") && (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); + if (os_strstr(capab, "[SOUNDING-DIMENSION-3]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + conf->vht_capab |= (2 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); + if (os_strstr(capab, "[SOUNDING-DIMENSION-4]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + conf->vht_capab |= (3 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); if (os_strstr(capab, "[MU-BEAMFORMER]")) conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE; - if (os_strstr(capab, "[MU-BEAMFORMEE]")) - conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE; if (os_strstr(capab, "[VHT-TXOP-PS]")) conf->vht_capab |= VHT_CAP_VHT_TXOP_PS; if (os_strstr(capab, "[HTC-VHT]")) @@ -1699,7 +1735,7 @@ static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss, char *str; str = wpa_config_parse_string(pos, &slen); - if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) { + if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) { wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos); os_free(str); return -1; @@ -1900,7 +1936,7 @@ fail: static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_bss_config *bss, - char *buf, char *pos, int line) + const char *buf, char *pos, int line) { if (os_strcmp(buf, "interface") == 0) { os_strlcpy(conf->bss[0]->iface, pos, @@ -1946,7 +1982,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, line); } else if (os_strcmp(buf, "ssid") == 0) { bss->ssid.ssid_len = os_strlen(pos); - if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN || + if (bss->ssid.ssid_len > SSID_MAX_LEN || bss->ssid.ssid_len < 1) { wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", line, pos); @@ -1957,7 +1993,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "ssid2") == 0) { size_t slen; char *str = wpa_config_parse_string(pos, &slen); - if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) { + if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) { wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", line, pos); os_free(str); @@ -2043,6 +2079,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->private_key_passwd = os_strdup(pos); } else if (os_strcmp(buf, "check_crl") == 0) { bss->check_crl = atoi(pos); + } else if (os_strcmp(buf, "tls_session_lifetime") == 0) { + bss->tls_session_lifetime = atoi(pos); } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { os_free(bss->ocsp_stapling_response); bss->ocsp_stapling_response = os_strdup(pos); @@ -2515,13 +2553,17 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->hw_mode = HOSTAPD_MODE_IEEE80211G; else if (os_strcmp(pos, "ad") == 0) conf->hw_mode = HOSTAPD_MODE_IEEE80211AD; + else if (os_strcmp(pos, "any") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY; else { wpa_printf(MSG_ERROR, "Line %d: unknown hw_mode '%s'", line, pos); return 1; } } else if (os_strcmp(buf, "wps_rf_bands") == 0) { - if (os_strcmp(pos, "a") == 0) + if (os_strcmp(pos, "ad") == 0) + bss->wps_rf_bands = WPS_RF_60GHZ; + else if (os_strcmp(pos, "a") == 0) bss->wps_rf_bands = WPS_RF_50GHZ; else if (os_strcmp(pos, "g") == 0 || os_strcmp(pos, "b") == 0) @@ -2542,12 +2584,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, line); return 1; #else /* CONFIG_ACS */ + conf->acs = 1; conf->channel = 0; #endif /* CONFIG_ACS */ - } else + } else { conf->channel = atoi(pos); + conf->acs = conf->channel == 0; + } } else if (os_strcmp(buf, "chanlist") == 0) { - if (hostapd_parse_intlist(&conf->chanlist, pos)) { + if (hostapd_parse_chanlist(conf, pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid channel list", line); return 1; @@ -2810,7 +2855,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, os_free(bss->wps_pin_requests); bss->wps_pin_requests = os_strdup(pos); } else if (os_strcmp(buf, "device_name") == 0) { - if (os_strlen(pos) > 32) { + if (os_strlen(pos) > WPS_DEV_NAME_MAX_LEN) { wpa_printf(MSG_ERROR, "Line %d: Too long " "device_name", line); return 1; @@ -3111,6 +3156,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->disable_dgaf = atoi(pos); } else if (os_strcmp(buf, "proxy_arp") == 0) { bss->proxy_arp = atoi(pos); + } else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) { + bss->na_mcast_to_ucast = atoi(pos); } else if (os_strcmp(buf, "osen") == 0) { bss->osen = atoi(pos); } else if (os_strcmp(buf, "anqp_domain_id") == 0) { @@ -3223,6 +3270,24 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->bss_load_test_set = 1; } else if (os_strcmp(buf, "radio_measurements") == 0) { bss->radio_measurements = atoi(pos); + } else if (os_strcmp(buf, "own_ie_override") == 0) { + struct wpabuf *tmp; + size_t len = os_strlen(pos) / 2; + + tmp = wpabuf_alloc(len); + if (!tmp) + return 1; + + if (hexstr2bin(pos, wpabuf_put(tmp, len), len)) { + wpabuf_free(tmp); + wpa_printf(MSG_ERROR, + "Line %d: Invalid own_ie_override '%s'", + line, pos); + return 1; + } + + wpabuf_free(bss->own_ie_override); + bss->own_ie_override = tmp; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strcmp(buf, "vendor_elements") == 0) { struct wpabuf *elems; @@ -3276,6 +3341,74 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "wowlan_triggers") == 0) { os_free(bss->wowlan_triggers); bss->wowlan_triggers = os_strdup(pos); +#ifdef CONFIG_FST + } else if (os_strcmp(buf, "fst_group_id") == 0) { + size_t len = os_strlen(pos); + + if (!len || len >= sizeof(conf->fst_cfg.group_id)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fst_group_id value '%s'", + line, pos); + return 1; + } + + if (conf->fst_cfg.group_id[0]) { + wpa_printf(MSG_ERROR, + "Line %d: Duplicate fst_group value '%s'", + line, pos); + return 1; + } + + os_strlcpy(conf->fst_cfg.group_id, pos, + sizeof(conf->fst_cfg.group_id)); + } else if (os_strcmp(buf, "fst_priority") == 0) { + char *endp; + long int val; + + if (!*pos) { + wpa_printf(MSG_ERROR, + "Line %d: fst_priority value not supplied (expected 1..%u)", + line, FST_MAX_PRIO_VALUE); + return -1; + } + + val = strtol(pos, &endp, 0); + if (*endp || val < 1 || val > FST_MAX_PRIO_VALUE) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fst_priority %ld (%s) (expected 1..%u)", + line, val, pos, FST_MAX_PRIO_VALUE); + return 1; + } + conf->fst_cfg.priority = (u8) val; + } else if (os_strcmp(buf, "fst_llt") == 0) { + char *endp; + long int val; + + if (!*pos) { + wpa_printf(MSG_ERROR, + "Line %d: fst_llt value not supplied (expected 1..%u)", + line, FST_MAX_LLT_MS); + return -1; + } + val = strtol(pos, &endp, 0); + if (*endp || val < 1 || val > FST_MAX_LLT_MS) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)", + line, val, pos, FST_MAX_LLT_MS); + return 1; + } + conf->fst_cfg.llt = (u32) val; +#endif /* CONFIG_FST */ + } else if (os_strcmp(buf, "track_sta_max_num") == 0) { + conf->track_sta_max_num = atoi(pos); + } else if (os_strcmp(buf, "track_sta_max_age") == 0) { + conf->track_sta_max_age = atoi(pos); + } else if (os_strcmp(buf, "no_probe_resp_if_seen_on") == 0) { + os_free(bss->no_probe_resp_if_seen_on); + bss->no_probe_resp_if_seen_on = os_strdup(pos); + } else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) { + os_free(bss->no_auth_if_seen_on); + bss->no_auth_if_seen_on = os_strdup(pos); } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration item '%s'", @@ -3378,7 +3511,8 @@ struct hostapd_config * hostapd_config_read(const char *fname) int hostapd_set_iface(struct hostapd_config *conf, - struct hostapd_bss_config *bss, char *field, char *value) + struct hostapd_bss_config *bss, const char *field, + char *value) { int errors; size_t i; diff --git a/contrib/wpa/hostapd/config_file.h b/contrib/wpa/hostapd/config_file.h index fba57b87ad7..c98bdb683ba 100644 --- a/contrib/wpa/hostapd/config_file.h +++ b/contrib/wpa/hostapd/config_file.h @@ -11,7 +11,7 @@ struct hostapd_config * hostapd_config_read(const char *fname); int hostapd_set_iface(struct hostapd_config *conf, - struct hostapd_bss_config *bss, char *field, + struct hostapd_bss_config *bss, const char *field, char *value); #endif /* CONFIG_FILE_H */ diff --git a/contrib/wpa/hostapd/ctrl_iface.c b/contrib/wpa/hostapd/ctrl_iface.c index 86f1aa6dfdb..cb6fb175770 100644 --- a/contrib/wpa/hostapd/ctrl_iface.c +++ b/contrib/wpa/hostapd/ctrl_iface.c @@ -25,6 +25,7 @@ #include "common/ieee802_11_defs.h" #include "crypto/tls.h" #include "drivers/driver.h" +#include "eapol_auth/eapol_auth_sm.h" #include "radius/radius_client.h" #include "radius/radius_server.h" #include "l2_packet/l2_packet.h" @@ -43,10 +44,13 @@ #include "ap/beacon.h" #include "wps/wps_defs.h" #include "wps/wps.h" +#include "fst/fst_ctrl_iface.h" #include "config_file.h" #include "ctrl_iface.h" +#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256 + struct wpa_ctrl_dst { struct wpa_ctrl_dst *next; struct sockaddr_un addr; @@ -57,6 +61,7 @@ struct wpa_ctrl_dst { static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + enum wpa_msg_type type, const char *buf, size_t len); @@ -1055,6 +1060,97 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, #endif /* CONFIG_WNM */ +static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + int ret = 0; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + WPA_ASSERT(hapd->conf->wpa_key_mgmt); + + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + ret = os_snprintf(pos, end - pos, "WPA-PSK "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "WPA-EAP "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#ifdef CONFIG_IEEE80211R + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, "FT-PSK "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "FT-EAP "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#ifdef CONFIG_SAE + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { + ret = os_snprintf(pos, end - pos, "FT-SAE "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SAE */ +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { + ret = os_snprintf(pos, end - pos, "SAE "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SAE */ + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & + WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + ret = os_snprintf(pos, end - pos, + "WPA-EAP-SUITE-B-192 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if (pos > buf && *(pos - 1) == ' ') { + *(pos - 1) = '\0'; + pos--; + } + + return pos - buf; +} + + static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, char *buf, size_t buflen) { @@ -1104,82 +1200,20 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, } #endif /* CONFIG_WPS */ + if (hapd->conf->wpa) { + ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) { ret = os_snprintf(pos, end - pos, "key_mgmt="); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { - ret = os_snprintf(pos, end - pos, "WPA-PSK "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { - ret = os_snprintf(pos, end - pos, "WPA-EAP "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#ifdef CONFIG_IEEE80211R - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { - ret = os_snprintf(pos, end - pos, "FT-PSK "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { - ret = os_snprintf(pos, end - pos, "FT-EAP "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#ifdef CONFIG_SAE - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { - ret = os_snprintf(pos, end - pos, "FT-SAE "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#endif /* CONFIG_SAE */ -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211W - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { - ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { - ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_SAE - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { - ret = os_snprintf(pos, end - pos, "SAE "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#endif /* CONFIG_SAE */ - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { - ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_key_mgmt & - WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { - ret = os_snprintf(pos, end - pos, - "WPA-EAP-SUITE-B-192 "); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } + pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos); ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) @@ -1528,7 +1562,7 @@ void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, { struct hostapd_data *hapd = ctx; const struct ether_header *eth; - const struct iphdr *ip; + struct iphdr ip; const u8 *pos; unsigned int i; @@ -1536,14 +1570,14 @@ void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, return; eth = (const struct ether_header *) buf; - ip = (const struct iphdr *) (eth + 1); - pos = (const u8 *) (ip + 1); + os_memcpy(&ip, eth + 1, sizeof(ip)); + pos = &buf[sizeof(*eth) + sizeof(ip)]; - if (ip->ihl != 5 || ip->version != 4 || - ntohs(ip->tot_len) != HWSIM_IP_LEN) + if (ip.ihl != 5 || ip.version != 4 || + ntohs(ip.tot_len) != HWSIM_IP_LEN) return; - for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) { + for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) { if (*pos != (u8) i) return; pos++; @@ -1599,7 +1633,7 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) int used; long int val; u8 tos; - u8 buf[HWSIM_PACKETLEN]; + u8 buf[2 + HWSIM_PACKETLEN]; struct ether_header *eth; struct iphdr *ip; u8 *dpos; @@ -1627,7 +1661,7 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) return -1; tos = val; - eth = (struct ether_header *) buf; + eth = (struct ether_header *) &buf[2]; os_memcpy(eth->ether_dhost, dst, ETH_ALEN); os_memcpy(eth->ether_shost, src, ETH_ALEN); eth->ether_type = htons(ETHERTYPE_IP); @@ -1639,14 +1673,14 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) ip->tos = tos; ip->tot_len = htons(HWSIM_IP_LEN); ip->protocol = 1; - ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1); - ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2); + ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1); + ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2); ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); dpos = (u8 *) (ip + 1); for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) *dpos++ = i; - if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, buf, + if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2], HWSIM_PACKETLEN) < 0) return -1; @@ -1746,6 +1780,45 @@ static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd, #endif /* WPA_TRACE_BFD */ } + +static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd) +{ +#ifdef WPA_TRACE_BFD + extern char wpa_trace_test_fail_func[256]; + extern unsigned int wpa_trace_test_fail_after; + char *pos; + + wpa_trace_test_fail_after = atoi(cmd); + pos = os_strchr(cmd, ':'); + if (pos) { + pos++; + os_strlcpy(wpa_trace_test_fail_func, pos, + sizeof(wpa_trace_test_fail_func)); + } else { + wpa_trace_test_fail_after = 0; + } + + return 0; +#else /* WPA_TRACE_BFD */ + return -1; +#endif /* WPA_TRACE_BFD */ +} + + +static int hostapd_ctrl_get_fail(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ +#ifdef WPA_TRACE_BFD + extern char wpa_trace_test_fail_func[256]; + extern unsigned int wpa_trace_test_fail_after; + + return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after, + wpa_trace_test_fail_func); +#else /* WPA_TRACE_BFD */ + return -1; +#endif /* WPA_TRACE_BFD */ +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -1847,41 +1920,134 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd, } -static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, - void *sock_ctx) +static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd, + const char *cmd) { - struct hostapd_data *hapd = eloop_ctx; - char buf[4096]; - int res; - struct sockaddr_un from; - socklen_t fromlen = sizeof(from); - char *reply; - const int reply_size = 4096; - int reply_len; - int level = MSG_DEBUG; + u8 addr[ETH_ALEN]; + struct sta_info *sta; - res = recvfrom(sock, buf, sizeof(buf) - 1, 0, - (struct sockaddr *) &from, &fromlen); - if (res < 0) { - wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", - strerror(errno)); - return; + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->eapol_sm) + return -1; + + eapol_auth_reauthenticate(sta->eapol_sm); + return 0; +} + + +static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + char *pos = cmd, *param; + + if (hwaddr_aton(pos, addr) || pos[17] != ' ') + return -1; + pos += 18; + param = pos; + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + *pos++ = '\0'; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->eapol_sm) + return -1; + + return eapol_auth_set_conf(sta->eapol_sm, param, pos); +} + + +static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + char *pos, *end, *stamp; + int ret; + + /* cmd: "LOG_LEVEL []" */ + if (*cmd == '\0') { + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, "Current level: %s\n" + "Timestamp: %d\n", + debug_level_str(wpa_debug_level), + wpa_debug_timestamp); + if (os_snprintf_error(end - pos, ret)) + ret = 0; + + return ret; } - buf[res] = '\0'; - if (os_strcmp(buf, "PING") == 0) - level = MSG_EXCESSIVE; - wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res); - reply = os_malloc(reply_size); - if (reply == NULL) { - if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, - fromlen) < 0) { - wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", - strerror(errno)); + while (*cmd == ' ') + cmd++; + + stamp = os_strchr(cmd, ' '); + if (stamp) { + *stamp++ = '\0'; + while (*stamp == ' ') { + stamp++; } - return; } + if (os_strlen(cmd)) { + int level = str_to_debug_level(cmd); + if (level < 0) + return -1; + wpa_debug_level = level; + } + + if (stamp && os_strlen(stamp)) + wpa_debug_timestamp = atoi(stamp); + + os_memcpy(buf, "OK\n", 3); + return 3; +} + + +#ifdef NEED_AP_MLME +static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + struct hostapd_iface *iface = hapd->iface; + char *pos, *end; + struct hostapd_sta_info *info; + struct os_reltime now; + + sta_track_expire(iface, 0); + + pos = buf; + end = buf + buflen; + + os_get_reltime(&now); + dl_list_for_each_reverse(info, &iface->sta_seen, + struct hostapd_sta_info, list) { + struct os_reltime age; + int ret; + + os_reltime_sub(&now, &info->last_seen, &age); + ret = os_snprintf(pos, end - pos, MACSTR " %u\n", + MAC2STR(info->addr), (unsigned int) age.sec); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + } + + return pos - buf; +} +#endif /* NEED_AP_MLME */ + + +static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, + char *buf, char *reply, + int reply_size, + struct sockaddr_un *from, + socklen_t fromlen) +{ + int reply_len, res; + os_memcpy(reply, "OK\n", 3); reply_len = 3; @@ -1938,13 +2104,13 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, reply_size); } else if (os_strcmp(buf, "ATTACH") == 0) { - if (hostapd_ctrl_iface_attach(hapd, &from, fromlen)) + if (hostapd_ctrl_iface_attach(hapd, from, fromlen)) reply_len = -1; } else if (os_strcmp(buf, "DETACH") == 0) { - if (hostapd_ctrl_iface_detach(hapd, &from, fromlen)) + if (hostapd_ctrl_iface_detach(hapd, from, fromlen)) reply_len = -1; } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { - if (hostapd_ctrl_iface_level(hapd, &from, fromlen, + if (hostapd_ctrl_iface_level(hapd, from, fromlen, buf + 6)) reply_len = -1; } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) { @@ -2079,6 +2245,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) { reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply, reply_size); + } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) { + if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "GET_FAIL") == 0) { + reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size); #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) @@ -2091,6 +2262,20 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, #ifdef RADIUS_SERVER radius_server_erp_flush(hapd->radius_srv); #endif /* RADIUS_SERVER */ + } else if (os_strncmp(buf, "EAPOL_REAUTH ", 13) == 0) { + if (hostapd_ctrl_iface_eapol_reauth(hapd, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) { + if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10)) + reply_len = -1; + } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) { + reply_len = hostapd_ctrl_iface_log_level( + hapd, buf + 9, reply, reply_size); +#ifdef NEED_AP_MLME + } else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) { + reply_len = hostapd_ctrl_iface_track_sta_list( + hapd, reply, reply_size); +#endif /* NEED_AP_MLME */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -2100,6 +2285,50 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, os_memcpy(reply, "FAIL\n", 5); reply_len = 5; } + + return reply_len; +} + + +static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + char buf[4096]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + char *reply; + const int reply_size = 4096; + int reply_len; + int level = MSG_DEBUG; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); + return; + } + buf[res] = '\0'; + if (os_strcmp(buf, "PING") == 0) + level = MSG_EXCESSIVE; + wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res); + + reply = os_malloc(reply_size); + if (reply == NULL) { + if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } + return; + } + + reply_len = hostapd_ctrl_iface_receive_process(hapd, buf, + reply, reply_size, + &from, fromlen); + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen) < 0) { wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", @@ -2130,13 +2359,14 @@ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) } -static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, int global, +static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, + enum wpa_msg_type type, const char *txt, size_t len) { struct hostapd_data *hapd = ctx; if (hapd == NULL) return; - hostapd_ctrl_iface_send(hapd, level, txt, len); + hostapd_ctrl_iface_send(hapd, level, type, txt, len); } @@ -2359,6 +2589,58 @@ static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces, } +static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces, + struct sockaddr_un *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); + dst->addrlen = fromlen; + dst->debug_level = MSG_INFO; + dst->next = interfaces->global_ctrl_dst; + interfaces->global_ctrl_dst = dst; + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached (global)", + from->sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)); + return 0; +} + + +static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces, + struct sockaddr_un *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst, *prev = NULL; + + dst = interfaces->global_ctrl_dst; + while (dst) { + if (fromlen == dst->addrlen && + os_memcmp(from->sun_path, dst->addr.sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)) + == 0) { + wpa_hexdump(MSG_DEBUG, + "CTRL_IFACE monitor detached (global)", + from->sun_path, + fromlen - + offsetof(struct sockaddr_un, sun_path)); + if (prev == NULL) + interfaces->global_ctrl_dst = dst->next; + else + prev->next = dst->next; + os_free(dst); + return 0; + } + prev = dst; + dst = dst->next; + } + return -1; +} + + static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) { #ifdef CONFIG_WPS_TESTING @@ -2369,6 +2651,214 @@ static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) } +#ifdef CONFIG_FST + +static int +hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces, + const char *cmd) +{ + char ifname[IFNAMSIZ + 1]; + struct fst_iface_cfg cfg; + struct hostapd_data *hapd; + struct fst_wpa_obj iface_obj; + + if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) { + hapd = hostapd_get_iface(interfaces, ifname); + if (hapd) { + if (hapd->iface->fst) { + wpa_printf(MSG_INFO, "FST: Already attached"); + return -1; + } + fst_hostapd_fill_iface_obj(hapd, &iface_obj); + hapd->iface->fst = fst_attach(ifname, hapd->own_addr, + &iface_obj, &cfg); + if (hapd->iface->fst) + return 0; + } + } + + return -EINVAL; +} + + +static int +hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces, + const char *cmd) +{ + char ifname[IFNAMSIZ + 1]; + struct hostapd_data * hapd; + + if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) { + hapd = hostapd_get_iface(interfaces, ifname); + if (hapd) { + if (!fst_iface_detach(ifname)) { + hapd->iface->fst = NULL; + hapd->iface->fst_ies = NULL; + return 0; + } + } + } + + return -EINVAL; +} + +#endif /* CONFIG_FST */ + + +static struct hostapd_data * +hostapd_interfaces_get_hapd(struct hapd_interfaces *interfaces, + const char *ifname) +{ + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_iface *iface = interfaces->iface[i]; + + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd; + + hapd = iface->bss[j]; + if (os_strcmp(ifname, hapd->conf->iface) == 0) + return hapd; + } + } + + return NULL; +} + + +static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd, + struct hostapd_data *dst_hapd, + const char *param) +{ + int res; + char *value; + + value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN); + if (!value) { + wpa_printf(MSG_ERROR, + "DUP: cannot allocate buffer to stringify %s", + param); + goto error_return; + } + + if (os_strcmp(param, "wpa") == 0) { + os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d", + src_hapd->conf->wpa); + } else if (os_strcmp(param, "wpa_key_mgmt") == 0 && + src_hapd->conf->wpa_key_mgmt) { + res = hostapd_ctrl_iface_get_key_mgmt( + src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN); + if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res)) + goto error_stringify; + } else if (os_strcmp(param, "wpa_pairwise") == 0 && + src_hapd->conf->wpa_pairwise) { + res = wpa_write_ciphers(value, + value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN, + src_hapd->conf->wpa_pairwise, " "); + if (res < 0) + goto error_stringify; + } else if (os_strcmp(param, "rsn_pairwise") == 0 && + src_hapd->conf->rsn_pairwise) { + res = wpa_write_ciphers(value, + value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN, + src_hapd->conf->rsn_pairwise, " "); + if (res < 0) + goto error_stringify; + } else if (os_strcmp(param, "wpa_passphrase") == 0 && + src_hapd->conf->ssid.wpa_passphrase) { + os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s", + src_hapd->conf->ssid.wpa_passphrase); + } else if (os_strcmp(param, "wpa_psk") == 0 && + src_hapd->conf->ssid.wpa_psk_set) { + wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, + src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN); + } else { + wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param); + goto error_return; + } + + res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value); + os_free(value); + return res; + +error_stringify: + wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param); +error_return: + os_free(value); + return -1; +} + + +static int +hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces, + char *cmd) +{ + char *p_start = cmd, *p_end; + struct hostapd_data *src_hapd, *dst_hapd; + + /* cmd: " */ + + p_end = os_strchr(p_start, ' '); + if (!p_end) { + wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'", + cmd); + return -1; + } + + *p_end = '\0'; + src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start); + if (!src_hapd) { + wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'", + p_start); + return -1; + } + + p_start = p_end + 1; + p_end = os_strchr(p_start, ' '); + if (!p_end) { + wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'", + cmd); + return -1; + } + + *p_end = '\0'; + dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start); + if (!dst_hapd) { + wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'", + p_start); + return -1; + } + + p_start = p_end + 1; + return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start); +} + + +static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces, + const char *ifname, + char *buf, char *reply, + int reply_size, + struct sockaddr_un *from, + socklen_t fromlen) +{ + struct hostapd_data *hapd; + + hapd = hostapd_interfaces_get_hapd(interfaces, ifname); + if (hapd == NULL) { + int res; + + res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n"); + if (os_snprintf_error(reply_size, res)) + return -1; + return res; + } + + return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size, + from, fromlen); +} + + static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { @@ -2377,8 +2867,9 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); - char reply[24]; + char *reply; int reply_len; + const int reply_size = 4096; res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); @@ -2390,9 +2881,31 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, buf[res] = '\0'; wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf); + reply = os_malloc(reply_size); + if (reply == NULL) { + if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } + return; + } + os_memcpy(reply, "OK\n", 3); reply_len = 3; + if (os_strncmp(buf, "IFNAME=", 7) == 0) { + char *pos = os_strchr(buf + 7, ' '); + + if (pos) { + *pos++ = '\0'; + reply_len = hostapd_global_ctrl_iface_ifname( + interfaces, buf + 7, pos, reply, reply_size, + &from, fromlen); + goto send_reply; + } + } + if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG\n", 5); reply_len = 5; @@ -2407,18 +2920,47 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (os_strncmp(buf, "REMOVE ", 7) == 0) { if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0) reply_len = -1; + } else if (os_strcmp(buf, "ATTACH") == 0) { + if (hostapd_global_ctrl_iface_attach(interfaces, &from, + fromlen)) + reply_len = -1; + } else if (os_strcmp(buf, "DETACH") == 0) { + if (hostapd_global_ctrl_iface_detach(interfaces, &from, + fromlen)) + reply_len = -1; #ifdef CONFIG_MODULE_TESTS } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { int hapd_module_tests(void); if (hapd_module_tests() < 0) reply_len = -1; #endif /* CONFIG_MODULE_TESTS */ +#ifdef CONFIG_FST + } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) { + if (!hostapd_global_ctrl_iface_fst_attach(interfaces, buf + 11)) + reply_len = os_snprintf(reply, reply_size, "OK\n"); + else + reply_len = -1; + } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) { + if (!hostapd_global_ctrl_iface_fst_detach(interfaces, buf + 11)) + reply_len = os_snprintf(reply, reply_size, "OK\n"); + else + reply_len = -1; + } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) { + reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size); +#endif /* CONFIG_FST */ + } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { + if (!hostapd_global_ctrl_iface_dup_network(interfaces, + buf + 12)) + reply_len = os_snprintf(reply, reply_size, "OK\n"); + else + reply_len = -1; } else { wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " "ignored"); reply_len = -1; } +send_reply: if (reply_len < 0) { os_memcpy(reply, "FAIL\n", 5); reply_len = 5; @@ -2429,6 +2971,7 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", strerror(errno)); } + os_free(reply); } @@ -2566,6 +3109,7 @@ fail: void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) { char *fname = NULL; + struct wpa_ctrl_dst *dst, *prev; if (interfaces->global_ctrl_sock > -1) { eloop_unregister_read_sock(interfaces->global_ctrl_sock); @@ -2590,13 +3134,23 @@ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) strerror(errno)); } } - os_free(interfaces->global_iface_path); - interfaces->global_iface_path = NULL; + } + + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = NULL; + + dst = interfaces->global_ctrl_dst; + interfaces->global_ctrl_dst = NULL; + while (dst) { + prev = dst; + dst = dst->next; + os_free(prev); } } static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + enum wpa_msg_type type, const char *buf, size_t len) { struct wpa_ctrl_dst *dst, *next; @@ -2604,9 +3158,17 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, int idx; struct iovec io[2]; char levelstr[10]; + int s; - dst = hapd->ctrl_dst; - if (hapd->ctrl_sock < 0 || dst == NULL) + if (type != WPA_MSG_ONLY_GLOBAL) { + s = hapd->ctrl_sock; + dst = hapd->ctrl_dst; + } else { + s = hapd->iface->interfaces->global_ctrl_sock; + dst = hapd->iface->interfaces->global_ctrl_dst; + } + + if (s < 0 || dst == NULL) return; os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); @@ -2627,16 +3189,22 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, offsetof(struct sockaddr_un, sun_path)); msg.msg_name = &dst->addr; msg.msg_namelen = dst->addrlen; - if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) { + if (sendmsg(s, &msg, 0) < 0) { int _errno = errno; wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " "%d - %s", idx, errno, strerror(errno)); dst->errors++; if (dst->errors > 10 || _errno == ENOENT) { - hostapd_ctrl_iface_detach( - hapd, &dst->addr, - dst->addrlen); + if (type != WPA_MSG_ONLY_GLOBAL) + hostapd_ctrl_iface_detach( + hapd, &dst->addr, + dst->addrlen); + else + hostapd_global_ctrl_iface_detach( + hapd->iface->interfaces, + &dst->addr, + dst->addrlen); } } else dst->errors = 0; diff --git a/contrib/wpa/hostapd/defconfig b/contrib/wpa/hostapd/defconfig index 4cde2b56348..430f7584b16 100644 --- a/contrib/wpa/hostapd/defconfig +++ b/contrib/wpa/hostapd/defconfig @@ -240,6 +240,12 @@ CONFIG_IPV6=y # requirements described above. #CONFIG_NO_RANDOM_POOL=y +# Should we use poll instead of select? Select is used by default. +#CONFIG_ELOOP_POLL=y + +# Should we use epoll instead of select? Select is used by default. +#CONFIG_ELOOP_EPOLL=y + # Select TLS implementation # openssl = OpenSSL (default) # gnutls = GnuTLS @@ -283,6 +289,12 @@ CONFIG_IPV6=y # Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file #CONFIG_SQLITE=y +# Enable Fast Session Transfer (FST) +#CONFIG_FST=y + +# Enable CLI commands for FST testing +#CONFIG_FST_TEST=y + # Testing options # This can be used to enable some testing options (see also the example # configuration file) that are really useful only for testing clients that diff --git a/contrib/wpa/hostapd/hlr_auc_gw.c b/contrib/wpa/hostapd/hlr_auc_gw.c index 42d59dba7ed..84d0308262e 100644 --- a/contrib/wpa/hostapd/hlr_auc_gw.c +++ b/contrib/wpa/hostapd/hlr_auc_gw.c @@ -87,6 +87,7 @@ struct milenage_parameters { u8 amf[2]; u8 sqn[6]; int set; + size_t res_len; }; static struct milenage_parameters *milenage_db = NULL; @@ -96,6 +97,7 @@ static struct milenage_parameters *milenage_db = NULL; #define EAP_AKA_RAND_LEN 16 #define EAP_AKA_AUTN_LEN 16 #define EAP_AKA_AUTS_LEN 14 +#define EAP_AKA_RES_MIN_LEN 4 #define EAP_AKA_RES_MAX_LEN 16 #define EAP_AKA_IK_LEN 16 #define EAP_AKA_CK_LEN 16 @@ -124,7 +126,8 @@ static int db_table_create_milenage(sqlite3 *db) " ki CHAR(32) NOT NULL," " opc CHAR(32) NOT NULL," " amf CHAR(4) NOT NULL," - " sqn CHAR(12) NOT NULL" + " sqn CHAR(12) NOT NULL," + " res_len INTEGER" ");"; printf("Adding database table for milenage information\n"); @@ -190,6 +193,10 @@ static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[]) printf("Invalid sqn value in database\n"); return -1; } + + if (os_strcmp(col[i], "res_len") == 0 && argv[i]) { + m->res_len = atoi(argv[i]); + } } return 0; @@ -206,8 +213,7 @@ static struct milenage_parameters * db_get_milenage(const char *imsi_txt) os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi), "%llu", imsi); os_snprintf(cmd, sizeof(cmd), - "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;", - imsi); + "SELECT * FROM milenage WHERE imsi=%llu;", imsi); if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage, NULL) != SQLITE_OK) return NULL; @@ -424,7 +430,7 @@ static int read_milenage(const char *fname) while (fgets(buf, sizeof(buf), f)) { line++; - /* Parse IMSI Ki OPc AMF SQN */ + /* Parse IMSI Ki OPc AMF SQN [RES_len] */ buf[sizeof(buf) - 1] = '\0'; if (buf[0] == '#') continue; @@ -515,7 +521,19 @@ static int read_milenage(const char *fname) ret = -1; break; } - pos = pos2 + 1; + + if (pos2) { + pos = pos2 + 1; + m->res_len = atoi(pos); + if (m->res_len && + (m->res_len < EAP_AKA_RES_MIN_LEN || + m->res_len > EAP_AKA_RES_MAX_LEN)) { + printf("%s:%d - Invalid RES_len (%s)\n", + fname, line, pos); + ret = -1; + break; + } + } m->next = milenage_db; milenage_db = m; @@ -532,7 +550,7 @@ static int read_milenage(const char *fname) static void update_milenage_file(const char *fname) { FILE *f, *f2; - char buf[500], *pos; + char name[500], buf[500], *pos; char *end = buf + sizeof(buf); struct milenage_parameters *m; size_t imsi_len; @@ -543,10 +561,10 @@ static void update_milenage_file(const char *fname) return; } - snprintf(buf, sizeof(buf), "%s.new", fname); - f2 = fopen(buf, "w"); + snprintf(name, sizeof(name), "%s.new", fname); + f2 = fopen(name, "w"); if (f2 == NULL) { - printf("Could not write Milenage data file '%s'\n", buf); + printf("Could not write Milenage data file '%s'\n", name); fclose(f); return; } @@ -588,14 +606,14 @@ static void update_milenage_file(const char *fname) fclose(f2); fclose(f); - snprintf(buf, sizeof(buf), "%s.bak", fname); - if (rename(fname, buf) < 0) { + snprintf(name, sizeof(name), "%s.bak", fname); + if (rename(fname, name) < 0) { perror("rename"); return; } - snprintf(buf, sizeof(buf), "%s.new", fname); - if (rename(buf, fname) < 0) { + snprintf(name, sizeof(name), "%s.new", fname); + if (rename(name, fname) < 0) { perror("rename"); return; } @@ -798,6 +816,10 @@ static int aka_req_auth(char *imsi, char *resp, size_t resp_len) } milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand, autn, ik, ck, res, &res_len); + if (m->res_len >= EAP_AKA_RES_MIN_LEN && + m->res_len <= EAP_AKA_RES_MAX_LEN && + m->res_len < res_len) + res_len = m->res_len; } else { printf("Unknown IMSI: %s\n", imsi); #ifdef AKA_USE_FIXED_TEST_VALUES diff --git a/contrib/wpa/hostapd/hlr_auc_gw.milenage_db b/contrib/wpa/hostapd/hlr_auc_gw.milenage_db index ecd06d72096..c156a29aeda 100644 --- a/contrib/wpa/hostapd/hlr_auc_gw.milenage_db +++ b/contrib/wpa/hostapd/hlr_auc_gw.milenage_db @@ -5,8 +5,10 @@ # authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but # dummy values will need to be included in this file. -# IMSI Ki OPc AMF SQN +# IMSI Ki OPc AMF SQN [RES_len] 232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 +# Example using truncated 32-bit RES instead of 64-bit default +232010000000001 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 4 # These values are from Test Set 19 which has the AMF separation bit set to 1 # and as such, is suitable for EAP-AKA' test. diff --git a/contrib/wpa/hostapd/hostapd.conf b/contrib/wpa/hostapd/hostapd.conf index 9e81e9e9868..a0071f7d82c 100644 --- a/contrib/wpa/hostapd/hostapd.conf +++ b/contrib/wpa/hostapd/hostapd.conf @@ -127,7 +127,9 @@ ssid=test # Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g, # ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to -# specify band) +# specify band). When using ACS (see channel parameter), a special value "any" +# can be used to indicate that any support band can be used. This special case +# is currently supported only with drivers with which offloaded ACS is used. # Default: IEEE 802.11b hw_mode=g @@ -170,8 +172,11 @@ channel=1 # Channel list restriction. This option allows hostapd to select one of the # provided channels when a channel should be automatically selected. -# Default: not set (allow any enabled channel to be selected) +# Channel list can be provided as range using hyphen ('-') or individual +# channels can be specified by space (' ') seperated values +# Default: all channels allowed in selected hw_mode #chanlist=100 104 108 112 116 +#chanlist=1 6 11-13 # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) beacon_int=100 @@ -275,8 +280,9 @@ ignore_broadcast_ssid=0 # (data0 is the highest priority queue) # parameters: # aifs: AIFS (default 2) -# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023) -# cwmax: cwMax (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023); cwMax >= cwMin +# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, +# 16383, 32767) +# cwmax: cwMax (same values as cwMin, cwMax >= cwMin) # burst: maximum length (in milliseconds with precision of up to 0.1 ms) for # bursting # @@ -337,8 +343,9 @@ ignore_broadcast_ssid=0 # note - txop_limit is in units of 32microseconds # note - acm is admission control mandatory flag. 0 = admission control not # required, 1 = mandatory -# note - here cwMin and cmMax are in exponent form. the actual cw value used -# will be (2^n)-1 where n is the value given here +# note - Here cwMin and cmMax are in exponent form. The actual cw value used +# will be (2^n)-1 where n is the value given here. The allowed range for these +# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin. # wmm_enabled=1 # @@ -575,14 +582,16 @@ wmm_ac_vo_acm=0 # 0 = Not supported (default) # 1 = Supported # -# Compressed Steering Number of Beamformer Antennas Supported: [BF-ANTENNA-2] +# Compressed Steering Number of Beamformer Antennas Supported: +# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4] # Beamformee's capability indicating the maximum number of beamformer # antennas the beamformee can support when sending compressed beamforming # feedback # If SU beamformer capable, set to maximum value minus 1 # else reserved (default) # -# Number of Sounding Dimensions: [SOUNDING-DIMENSION-2] +# Number of Sounding Dimensions: +# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4] # Beamformer's capability indicating the maximum value of the NUM_STS parameter # in the TXVECTOR of a VHT NDP # If SU beamformer capable, set to maximum value minus 1 @@ -593,11 +602,6 @@ wmm_ac_vo_acm=0 # 0 = Not supported or sent by Non-AP STA (default) # 1 = Supported # -# MU Beamformee Capable: [MU-BEAMFORMEE] -# Indicates support for operation as an MU beamformee -# 0 = Not supported or sent by AP (default) -# 1 = Supported -# # VHT TXOP PS: [VHT-TXOP-PS] # Indicates whether or not the AP supports VHT TXOP Power Save Mode # or whether or not the STA is in VHT TXOP Power Save mode @@ -764,6 +768,12 @@ eap_server=0 # 2 = check all CRLs in the certificate path #check_crl=1 +# TLS Session Lifetime in seconds +# This can be used to allow TLS sessions to be cached and resumed with an +# abbreviated handshake when using EAP-TLS/TTLS/PEAP. +# (default: 0 = session caching and resumption disabled) +#tls_session_lifetime=3600 + # Cached OCSP stapling response (DER encoded) # If set, this file is sent as a certificate status response by the EAP server # if the EAP peer requests certificate status in the ClientHello message. @@ -787,7 +797,7 @@ eap_server=0 # is in DSA parameters format, it will be automatically converted into DH # params. This parameter is required if anonymous EAP-FAST is used. # You can generate DH parameters file with OpenSSL, e.g., -# "openssl dhparam -out /etc/hostapd.dh.pem 1024" +# "openssl dhparam -out /etc/hostapd.dh.pem 2048" #dh_file=/etc/hostapd.dh.pem # OpenSSL cipher string @@ -1247,6 +1257,11 @@ own_ip_addr=127.0.0.1 # 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived #pmk_r1_push=1 +# Whether to enable FT-over-DS +# 0 = FT-over-DS disabled +# 1 = FT-over-DS enabled (default) +#ft_over_ds=1 + ##### Neighbor table ########################################################## # Maximum number of entries kept in AP table (either for neigbor table or for # detecting Overlapping Legacy BSS Condition). The oldest entry will be @@ -1264,6 +1279,43 @@ own_ip_addr=127.0.0.1 # default: 60 #ap_table_expiration_time=3600 +# Maximum number of stations to track on the operating channel +# This can be used to detect dualband capable stations before they have +# associated, e.g., to provide guidance on which colocated BSS to use. +# Default: 0 (disabled) +#track_sta_max_num=100 + +# Maximum age of a station tracking entry in seconds +# Default: 180 +#track_sta_max_age=180 + +# Do not reply to group-addressed Probe Request from a station that was seen on +# another radio. +# Default: Disabled +# +# This can be used with enabled track_sta_max_num configuration on another +# interface controlled by the same hostapd process to restrict Probe Request +# frame handling from replying to group-addressed Probe Request frames from a +# station that has been detected to be capable of operating on another band, +# e.g., to try to reduce likelihood of the station selecting a 2.4 GHz BSS when +# the AP operates both a 2.4 GHz and 5 GHz BSS concurrently. +# +# Note: Enabling this can cause connectivity issues and increase latency for +# discovering the AP. +#no_probe_resp_if_seen_on=wlan1 + +# Reject authentication from a station that was seen on another radio. +# Default: Disabled +# +# This can be used with enabled track_sta_max_num configuration on another +# interface controlled by the same hostapd process to reject authentication +# attempts from a station that has been detected to be capable of operating on +# another band, e.g., to try to reduce likelihood of the station selecting a +# 2.4 GHz BSS when the AP operates both a 2.4 GHz and 5 GHz BSS concurrently. +# +# Note: Enabling this can cause connectivity issues and increase latency for +# connecting with the AP. +#no_auth_if_seen_on=wlan1 ##### Wi-Fi Protected Setup (WPS) ############################################# @@ -1430,7 +1482,7 @@ own_ip_addr=127.0.0.1 # 12-digit, all-numeric code that identifies the consumer package. #upc=123456789012 -# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band) +# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band, ad = 60 GHz) # This value should be set according to RF band(s) supported by the AP if # hw_mode is not set. For dual band dual concurrent devices, this needs to be # set to ag to allow both RF bands to be advertized. @@ -1490,6 +1542,13 @@ own_ip_addr=127.0.0.1 # 1 = enabled #proxy_arp=1 +# IPv6 Neighbor Advertisement multicast-to-unicast conversion +# This can be used with Proxy ARP to allow multicast NAs to be forwarded to +# associated STAs using link layer unicast delivery. +# 0 = disabled (default) +# 1 = enabled +#na_mcast_to_ucast=0 + ##### IEEE 802.11u-2011 ####################################################### # Enable Interworking service @@ -1738,6 +1797,32 @@ own_ip_addr=127.0.0.1 # #osu_server_uri=... +##### Fast Session Transfer (FST) support ##################################### +# +# The options in this section are only available when the build configuration +# option CONFIG_FST is set while compiling hostapd. They allow this interface +# to be a part of FST setup. +# +# FST is the transfer of a session from a channel to another channel, in the +# same or different frequency bands. +# +# For detals, see IEEE Std 802.11ad-2012. + +# Identifier of an FST Group the interface belongs to. +#fst_group_id=bond0 + +# Interface priority within the FST Group. +# Announcing a higher priority for an interface means declaring it more +# preferable for FST switch. +# fst_priority is in 1..255 range with 1 being the lowest priority. +#fst_priority=100 + +# Default LLT value for this interface in milliseconds. The value used in case +# no value provided during session setup. Default is 50 ms. +# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2 +# Transitioning between states). +#fst_llt=100 + ##### TESTING OPTIONS ######################################################### # # The options in this section are only available when the build configuration diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c index 3f00cbbf0cc..46c2f37e460 100644 --- a/contrib/wpa/hostapd/hostapd_cli.c +++ b/contrib/wpa/hostapd/hostapd_cli.c @@ -10,22 +10,23 @@ #include #include "common/wpa_ctrl.h" +#include "common/ieee802_11_defs.h" #include "utils/common.h" #include "utils/eloop.h" #include "utils/edit.h" #include "common/version.h" -static const char *hostapd_cli_version = +static const char *const hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" "Copyright (c) 2004-2015, Jouni Malinen and contributors"; -static const char *hostapd_cli_license = +static const char *const hostapd_cli_license = "This software may be distributed under the terms of the BSD license.\n" "See README for more details.\n"; -static const char *hostapd_cli_full_license = +static const char *const hostapd_cli_full_license = "This software may be distributed under the terms of the BSD license.\n" "\n" "Redistribution and use in source and binary forms, with or without\n" @@ -56,7 +57,7 @@ static const char *hostapd_cli_full_license = "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" "\n"; -static const char *commands_help = +static const char *const commands_help = "Commands:\n" " mib get MIB variables (dot1x, dot11, radius)\n" " sta get MIB variables for one station\n" @@ -96,6 +97,7 @@ static int hostapd_cli_attached = 0; #define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd" #endif /* CONFIG_CTRL_IFACE_DIR */ static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; +static const char *client_socket_dir = NULL; static char *ctrl_ifname = NULL; static const char *pid_file = NULL; @@ -111,13 +113,15 @@ static void usage(void) "\n" "usage: hostapd_cli [-p] [-i] [-hvB] " "[-a] \\\n" - " [-G] [command..]\n" + " [-P] [-G] [command..]\n" "\n" "Options:\n" " -h help (show this usage text)\n" " -v shown version information\n" " -p path to find control sockets (default: " "/var/run/hostapd)\n" + " -s dir path to open client sockets (default: " + CONFIG_CTRL_IFACE_DIR ")\n" " -a run in daemon mode executing the action file " "based on events\n" " from hostapd\n" @@ -144,7 +148,14 @@ static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) return NULL; snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); - ctrl_conn = wpa_ctrl_open(cfile); + if (client_socket_dir && client_socket_dir[0] && + access(client_socket_dir, F_OK) < 0) { + perror(client_socket_dir); + free(cfile); + return NULL; + } + + ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir); free(cfile); return ctrl_conn; } @@ -541,7 +552,7 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char buf[256]; - char ssid_hex[2 * 32 + 1]; + char ssid_hex[2 * SSID_MAX_LEN + 1]; char key_hex[2 * 64 + 1]; int i; @@ -552,7 +563,7 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, } ssid_hex[0] = '\0'; - for (i = 0; i < 32; i++) { + for (i = 0; i < SSID_MAX_LEN; i++) { if (argv[0][i] == '\0') break; os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]); @@ -921,6 +932,35 @@ static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +#ifdef CONFIG_FST +static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + int i; + int total; + + if (argc <= 0) { + printf("FST command: parameters are required.\n"); + return -1; + } + + total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER"); + + for (i = 0; i < argc; i++) { + res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s", + argv[i]); + if (os_snprintf_error(sizeof(cmd) - total, res)) { + printf("Too long fst command.\n"); + return -1; + } + total += res; + } + return wpa_ctrl_command(ctrl, cmd); +} +#endif /* CONFIG_FST */ + + static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1009,12 +1049,31 @@ static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s", + argc >= 1 ? " " : "", + argc >= 1 ? argv[0] : "", + argc == 2 ? " " : "", + argc == 2 ? argv[1] : ""); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long option\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); }; -static struct hostapd_cli_cmd hostapd_cli_commands[] = { +static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "ping", hostapd_cli_cmd_ping }, { "mib", hostapd_cli_cmd_mib }, { "relog", hostapd_cli_cmd_relog }, @@ -1048,6 +1107,9 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "get_config", hostapd_cli_cmd_get_config }, { "help", hostapd_cli_cmd_help }, { "interface", hostapd_cli_cmd_interface }, +#ifdef CONFIG_FST + { "fst", hostapd_cli_cmd_fst }, +#endif /* CONFIG_FST */ { "level", hostapd_cli_cmd_level }, { "license", hostapd_cli_cmd_license }, { "quit", hostapd_cli_cmd_quit }, @@ -1063,13 +1125,14 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "reload", hostapd_cli_cmd_reload }, { "disable", hostapd_cli_cmd_disable }, { "erp_flush", hostapd_cli_cmd_erp_flush }, + { "log_level", hostapd_cli_cmd_log_level }, { NULL, NULL } }; static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - struct hostapd_cli_cmd *cmd, *match = NULL; + const struct hostapd_cli_cmd *cmd, *match = NULL; int count; count = 0; @@ -1284,7 +1347,7 @@ int main(int argc, char *argv[]) return -1; for (;;) { - c = getopt(argc, argv, "a:BhG:i:p:v"); + c = getopt(argc, argv, "a:BhG:i:p:P:s:v"); if (c < 0) break; switch (c) { @@ -1310,6 +1373,12 @@ int main(int argc, char *argv[]) case 'p': ctrl_iface_dir = optarg; break; + case 'P': + pid_file = optarg; + break; + case 's': + client_socket_dir = optarg; + break; default: usage(); return -1; diff --git a/contrib/wpa/hostapd/main.c b/contrib/wpa/hostapd/main.c index dd389a8d66a..6c7406af447 100644 --- a/contrib/wpa/hostapd/main.c +++ b/contrib/wpa/hostapd/main.c @@ -24,6 +24,7 @@ #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" +#include "fst/fst.h" #include "config_file.h" #include "eap_register.h" #include "ctrl_iface.h" @@ -533,6 +534,28 @@ static int gen_uuid(const char *txt_addr) #endif /* CONFIG_WPS */ +#ifndef HOSTAPD_CLEANUP_INTERVAL +#define HOSTAPD_CLEANUP_INTERVAL 10 +#endif /* HOSTAPD_CLEANUP_INTERVAL */ + +static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx) +{ + hostapd_periodic_iface(iface); + return 0; +} + + +/* Periodic cleanup tasks */ +static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx) +{ + struct hapd_interfaces *interfaces = eloop_ctx; + + eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0, + hostapd_periodic, interfaces, NULL); + hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL); +} + + int main(int argc, char *argv[]) { struct hapd_interfaces interfaces; @@ -561,6 +584,7 @@ int main(int argc, char *argv[]) interfaces.global_iface_path = NULL; interfaces.global_iface_name = NULL; interfaces.global_ctrl_sock = -1; + interfaces.global_ctrl_dst = NULL; for (;;) { c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:"); @@ -661,10 +685,24 @@ int main(int argc, char *argv[]) } if (hostapd_global_init(&interfaces, entropy_file)) { - wpa_printf(MSG_ERROR, "Failed to initilize global context"); + wpa_printf(MSG_ERROR, "Failed to initialize global context"); return -1; } + eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0, + hostapd_periodic, &interfaces, NULL); + + if (fst_global_init()) { + wpa_printf(MSG_ERROR, + "Failed to initialize global FST context"); + goto out; + } + +#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE) + if (!fst_global_add_ctrl(fst_ctrl_cli)) + wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl"); +#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */ + /* Allocate and parse configuration for full interface files */ for (i = 0; i < interfaces.count; i++) { interfaces.iface[i] = hostapd_interface_init(&interfaces, @@ -749,6 +787,7 @@ int main(int argc, char *argv[]) } os_free(interfaces.iface); + eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); hostapd_global_deinit(pid_file); os_free(pid_file); @@ -758,6 +797,8 @@ int main(int argc, char *argv[]) os_free(bss_config); + fst_global_deinit(); + os_program_deinit(); return ret; diff --git a/contrib/wpa/hs20/client/Makefile b/contrib/wpa/hs20/client/Makefile index ca67b54da2e..94cd5f14df1 100644 --- a/contrib/wpa/hs20/client/Makefile +++ b/contrib/wpa/hs20/client/Makefile @@ -67,7 +67,13 @@ OBJS += ../../src/crypto/sha256-internal.o CFLAGS += $(shell xml2-config --cflags) LIBS += $(shell xml2-config --libs) + +# Allow static/custom linking of libcurl. +ifdef CUST_CURL_LINKAGE +LIBS += ${CUST_CURL_LINKAGE} +else LIBS += -lcurl +endif CFLAGS += -DEAP_TLS_OPENSSL LIBS += -lssl -lcrypto diff --git a/contrib/wpa/hs20/client/osu_client.c b/contrib/wpa/hs20/client/osu_client.c index de7f351da24..0315f7b75ad 100644 --- a/contrib/wpa/hs20/client/osu_client.c +++ b/contrib/wpa/hs20/client/osu_client.c @@ -25,6 +25,8 @@ #include "crypto/sha256.h" #include "osu_client.h" +const char *spp_xsd_fname = "spp.xsd"; + void write_result(struct hs20_osu_client *ctx, const char *fmt, ...) { @@ -540,6 +542,7 @@ int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, uri); write_result(ctx, "Unsupported location for addMO to " "add PPS MO (extra directory): '%s'", uri); + free(fqdn); return -1; } *pos = '\0'; /* remove trailing slash and PPS node name */ @@ -547,8 +550,9 @@ int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn); if (!server_dnsname_suffix_match(ctx, fqdn)) { - wpa_printf(MSG_INFO, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values", - fqdn); + wpa_printf(MSG_INFO, + "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d", + fqdn, (int) ctx->server_dnsname_count); write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values", fqdn); free(fqdn); @@ -2094,10 +2098,14 @@ static int osu_connect(struct hs20_osu_client *ctx, const char *bssid, } ctx->no_reconnect = 1; - if (methods & 0x02) + if (methods & 0x02) { + wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect"); res = cmd_prov(ctx, url); - else if (methods & 0x01) + } else if (methods & 0x01) { + wpa_printf(MSG_DEBUG, + "Calling cmd_oma_dm_prov from osu_connect"); res = cmd_oma_dm_prov(ctx, url); + } wpa_printf(MSG_INFO, "Remove OSU network connection"); write_summary(ctx, "Remove OSU network connection"); @@ -2139,7 +2147,7 @@ static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir, snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir); osu = parse_osu_providers(fname, &osu_count); if (osu == NULL) { - wpa_printf(MSG_INFO, "Could not any OSU providers from %s", + wpa_printf(MSG_INFO, "Could not find any OSU providers from %s", fname); write_result(ctx, "No OSU providers available"); return -1; @@ -2290,12 +2298,19 @@ selected: } if (connect == 2) { - if (last->methods & 0x02) + if (last->methods & 0x02) { + wpa_printf(MSG_DEBUG, + "Calling cmd_prov from cmd_osu_select"); ret = cmd_prov(ctx, last->url); - else if (last->methods & 0x01) + } else if (last->methods & 0x01) { + wpa_printf(MSG_DEBUG, + "Calling cmd_oma_dm_prov from cmd_osu_select"); ret = cmd_oma_dm_prov(ctx, last->url); - else + } else { + wpa_printf(MSG_DEBUG, + "No supported OSU provisioning method"); ret = -1; + } } else if (connect) ret = osu_connect(ctx, last->bssid, last->osu_ssid, last->url, last->methods, @@ -2690,7 +2705,7 @@ static char * get_hostname(const char *url) end = os_strchr(pos, '/'); end2 = os_strchr(pos, ':'); - if (end && end2 && end2 < end) + if ((end && end2 && end2 < end) || (!end && end2)) end = end2; if (end) end--; @@ -2720,8 +2735,8 @@ static int osu_cert_cb(void *_ctx, struct http_cert *cert) int found; char *host = NULL; - wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d)", - !ctx->no_osu_cert_validation); + wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s)", + !ctx->no_osu_cert_validation, ctx->server_url); host = get_hostname(ctx->server_url); @@ -2810,17 +2825,21 @@ static int osu_cert_cb(void *_ctx, struct http_cert *cert) char *name = ctx->icon_filename[j]; size_t name_len = os_strlen(name); - wpa_printf(MSG_INFO, "Looking for icon file name '%s' match", - name); + wpa_printf(MSG_INFO, + "[%i] Looking for icon file name '%s' match", + j, name); for (i = 0; i < cert->num_logo; i++) { struct http_logo *logo = &cert->logo[i]; size_t uri_len = os_strlen(logo->uri); char *pos; - wpa_printf(MSG_INFO, "Comparing to '%s' uri_len=%d name_len=%d", - logo->uri, (int) uri_len, (int) name_len); - if (uri_len < 1 + name_len) + wpa_printf(MSG_INFO, + "[%i] Comparing to '%s' uri_len=%d name_len=%d", + i, logo->uri, (int) uri_len, (int) name_len); + if (uri_len < 1 + name_len) { + wpa_printf(MSG_INFO, "URI Length is too short"); continue; + } pos = &logo->uri[uri_len - name_len - 1]; if (*pos != '/') continue; @@ -2847,17 +2866,30 @@ static int osu_cert_cb(void *_ctx, struct http_cert *cert) for (i = 0; i < cert->num_logo; i++) { struct http_logo *logo = &cert->logo[i]; - if (logo->hash_len != 32) + if (logo->hash_len != 32) { + wpa_printf(MSG_INFO, + "[%i][%i] Icon hash length invalid (should be 32): %d", + j, i, (int) logo->hash_len); continue; + } if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) { found = 1; break; } + + wpa_printf(MSG_DEBUG, + "[%u][%u] Icon hash did not match", j, i); + wpa_hexdump_ascii(MSG_DEBUG, "logo->hash", + logo->hash, 32); + wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]", + ctx->icon_hash[j], 32); } if (!found) { - wpa_printf(MSG_INFO, "No icon hash match found"); - write_result(ctx, "No icon hash match found"); + wpa_printf(MSG_INFO, + "No icon hash match (by hash) found"); + write_result(ctx, + "No icon hash match (by hash) found"); return -1; } } @@ -2955,6 +2987,7 @@ static void usage(void) " [-w] " "[-r] [-f] \\\n" " [-s] \\\n" + " [-x] \\\n" " [arguments..]\n" "commands:\n" "- to_tnds [URN]\n" @@ -2996,7 +3029,7 @@ int main(int argc, char *argv[]) return -1; for (;;) { - c = getopt(argc, argv, "df:hi:KNO:qr:s:S:tw:"); + c = getopt(argc, argv, "df:hKNO:qr:s:S:tw:x:"); if (c < 0) break; switch (c) { @@ -3034,6 +3067,9 @@ int main(int argc, char *argv[]) case 'w': wpas_ctrl_path = optarg; break; + case 'x': + spp_xsd_fname = optarg; + break; case 'h': default: usage(); @@ -3108,6 +3144,7 @@ int main(int argc, char *argv[]) exit(0); } ctx.ca_fname = argv[optind + 2]; + wpa_printf(MSG_DEBUG, "Calling cmd_prov from main"); cmd_prov(&ctx, argv[optind + 1]); } else if (strcmp(argv[optind], "sim_prov") == 0) { if (argc - optind < 2) { diff --git a/contrib/wpa/hs20/client/spp_client.c b/contrib/wpa/hs20/client/spp_client.c index 302a05040df..c619541ae28 100644 --- a/contrib/wpa/hs20/client/spp_client.c +++ b/contrib/wpa/hs20/client/spp_client.c @@ -21,6 +21,8 @@ #include "osu_client.h" +extern const char *spp_xsd_fname; + static int hs20_spp_update_response(struct hs20_osu_client *ctx, const char *session_id, const char *spp_status, @@ -59,7 +61,7 @@ static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node, return -1; } - ret = xml_validate(xctx, node, "spp.xsd", &err); + ret = xml_validate(xctx, node, spp_xsd_fname, &err); if (ret < 0) { wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err); write_summary(ctx, "SPP XML schema validation failed"); @@ -77,9 +79,14 @@ static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns, xml_node_t *fnode, *tnds; char *str; + errno = 0; fnode = node_from_file(ctx, fname); - if (!fnode) + if (!fnode) { + wpa_printf(MSG_ERROR, + "Failed to create XML node from file: %s, possible error: %s", + fname, strerror(errno)); return; + } tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2"); xml_node_free(ctx, fnode); if (!tnds) @@ -952,7 +959,9 @@ int cmd_prov(struct hs20_osu_client *ctx, const char *url) return -1; } - wpa_printf(MSG_INFO, "Credential provisioning requested"); + wpa_printf(MSG_INFO, + "Credential provisioning requested - URL: %s ca_fname: %s", + url, ctx->ca_fname ? ctx->ca_fname : "N/A"); os_free(ctx->server_url); ctx->server_url = os_strdup(url); diff --git a/contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch b/contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch new file mode 100644 index 00000000000..3a8f90e40ce --- /dev/null +++ b/contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch @@ -0,0 +1,398 @@ +This patch adds support for TLS SessionTicket extension (RFC 5077) for +the parts used by EAP-FAST (RFC 4851). + +This is based on the patch from Alexey Kobozev +(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). + +OpenSSL 0.9.8zf does not enable TLS extension support by default, so it +will need to be enabled by adding enable-tlsext to config script +command line. + + +diff -upr openssl-0.9.8zf.orig/ssl/s3_clnt.c openssl-0.9.8zf/ssl/s3_clnt.c +--- openssl-0.9.8zf.orig/ssl/s3_clnt.c 2015-03-19 15:46:46.000000000 +0200 ++++ openssl-0.9.8zf/ssl/s3_clnt.c 2015-03-24 16:19:14.043911769 +0200 +@@ -760,6 +760,23 @@ int ssl3_get_server_hello(SSL *s) + goto f_err; + } + ++#ifndef OPENSSL_NO_TLSEXT ++ /* check if we want to resume the session based on external pre-shared secret */ ++ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) { ++ SSL_CIPHER *pref_cipher = NULL; ++ ++ s->session->master_key_length = sizeof(s->session->master_key); ++ if (s->tls_session_secret_cb(s, s->session->master_key, ++ &s->session->master_key_length, ++ NULL, &pref_cipher, ++ s->tls_session_secret_cb_arg)) { ++ s->session->cipher = pref_cipher ? ++ pref_cipher : ssl_get_cipher_by_char(s, p + j); ++ s->s3->flags |= SSL3_FLAGS_CCS_OK; ++ } ++ } ++#endif /* OPENSSL_NO_TLSEXT */ ++ + if (j != 0 && j == s->session->session_id_length + && memcmp(p, s->session->session_id, j) == 0) { + if (s->sid_ctx_length != s->session->sid_ctx_length +@@ -2684,12 +2701,8 @@ int ssl3_check_finished(SSL *s) + { + int ok; + long n; +- /* +- * If we have no ticket or session ID is non-zero length (a match of a +- * non-zero session length would never reach here) it cannot be a resumed +- * session. +- */ +- if (!s->session->tlsext_tick || s->session->session_id_length) ++ /* If we have no ticket it cannot be a resumed session. */ ++ if (!s->session->tlsext_tick) + return 1; + /* + * this function is called when we really expect a Certificate message, +diff -upr openssl-0.9.8zf.orig/ssl/s3_srvr.c openssl-0.9.8zf/ssl/s3_srvr.c +--- openssl-0.9.8zf.orig/ssl/s3_srvr.c 2015-03-19 15:46:46.000000000 +0200 ++++ openssl-0.9.8zf/ssl/s3_srvr.c 2015-03-24 16:23:34.567909681 +0200 +@@ -999,6 +999,59 @@ int ssl3_get_client_hello(SSL *s) + SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT); + goto err; + } ++ ++ /* Check if we want to use external pre-shared secret for this ++ * handshake for not reused session only. We need to generate ++ * server_random before calling tls_session_secret_cb in order to allow ++ * SessionTicket processing to use it in key derivation. */ ++ { ++ unsigned long Time; ++ unsigned char *pos; ++ Time = (unsigned long)time(NULL); /* Time */ ++ pos = s->s3->server_random; ++ l2n(Time, pos); ++ if (RAND_pseudo_bytes(pos, SSL3_RANDOM_SIZE - 4) <= 0) { ++ al = SSL_AD_INTERNAL_ERROR; ++ goto f_err; ++ } ++ } ++ ++ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) { ++ SSL_CIPHER *pref_cipher = NULL; ++ ++ s->session->master_key_length = sizeof(s->session->master_key); ++ if (s->tls_session_secret_cb(s, s->session->master_key, ++ &s->session->master_key_length, ++ ciphers, &pref_cipher, ++ s->tls_session_secret_cb_arg)) { ++ s->hit = 1; ++ s->session->ciphers = ciphers; ++ s->session->verify_result = X509_V_OK; ++ ++ ciphers = NULL; ++ ++ /* check if some cipher was preferred by call back */ ++ pref_cipher = pref_cipher ? pref_cipher : ++ ssl3_choose_cipher(s, s->session->ciphers, ++ SSL_get_ciphers(s)); ++ if (pref_cipher == NULL) { ++ al = SSL_AD_HANDSHAKE_FAILURE; ++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER); ++ goto f_err; ++ } ++ ++ s->session->cipher = pref_cipher; ++ ++ if (s->cipher_list) ++ sk_SSL_CIPHER_free(s->cipher_list); ++ ++ if (s->cipher_list_by_id) ++ sk_SSL_CIPHER_free(s->cipher_list_by_id); ++ ++ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers); ++ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers); ++ } ++ } + #endif + /* + * Worst case, we will use the NULL compression, but if we have other +@@ -1143,15 +1196,21 @@ int ssl3_send_server_hello(SSL *s) + unsigned char *buf; + unsigned char *p, *d; + int i, sl; +- unsigned long l, Time; ++ unsigned long l; ++#ifdef OPENSSL_NO_TLSEXT ++ unsigned long Time; ++#endif + + if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { + buf = (unsigned char *)s->init_buf->data; ++#ifdef OPENSSL_NO_TLSEXT + p = s->s3->server_random; ++ /* Generate server_random if it was not needed previously */ + Time = (unsigned long)time(NULL); /* Time */ + l2n(Time, p); + if (RAND_pseudo_bytes(p, SSL3_RANDOM_SIZE - 4) <= 0) + return -1; ++#endif + /* Do the message type and length last */ + d = p = &(buf[4]); + +diff -upr openssl-0.9.8zf.orig/ssl/ssl_err.c openssl-0.9.8zf/ssl/ssl_err.c +--- openssl-0.9.8zf.orig/ssl/ssl_err.c 2015-03-19 15:46:46.000000000 +0200 ++++ openssl-0.9.8zf/ssl/ssl_err.c 2015-03-24 16:35:58.627903717 +0200 +@@ -316,6 +316,7 @@ static ERR_STRING_DATA SSL_str_functs[] + {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"}, + {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"}, + {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"}, ++ {ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, + {0, NULL} + }; + +diff -upr openssl-0.9.8zf.orig/ssl/ssl.h openssl-0.9.8zf/ssl/ssl.h +--- openssl-0.9.8zf.orig/ssl/ssl.h 2015-03-19 15:46:46.000000000 +0200 ++++ openssl-0.9.8zf/ssl/ssl.h 2015-03-24 16:25:44.339908641 +0200 +@@ -349,6 +349,7 @@ extern "C" { + * function parameters used to prototype callbacks in SSL_CTX. + */ + typedef struct ssl_st *ssl_crock_st; ++typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT; + + /* used to hold info on the particular ciphers used */ + typedef struct ssl_cipher_st { +@@ -366,6 +367,12 @@ typedef struct ssl_cipher_st { + + DECLARE_STACK_OF(SSL_CIPHER) + ++typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, ++ int len, void *arg); ++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, ++ STACK_OF(SSL_CIPHER) *peer_ciphers, ++ SSL_CIPHER **cipher, void *arg); ++ + /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ + typedef struct ssl_method_st { + int version; +@@ -1116,6 +1123,18 @@ struct ssl_st { + int tlsext_ocsp_resplen; + /* RFC4507 session ticket expected to be received or sent */ + int tlsext_ticket_expected; ++ ++ /* TLS Session Ticket extension override */ ++ TLS_SESSION_TICKET_EXT *tlsext_session_ticket; ++ ++ /* TLS Session Ticket extension callback */ ++ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb; ++ void *tls_session_ticket_ext_cb_arg; ++ ++ /* TLS pre-shared secret session resumption */ ++ tls_session_secret_cb_fn tls_session_secret_cb; ++ void *tls_session_secret_cb_arg; ++ + SSL_CTX *initial_ctx; /* initial ctx, used to store sessions */ + # define session_ctx initial_ctx + # else +@@ -1772,6 +1791,17 @@ void *SSL_COMP_get_compression_methods(v + int SSL_COMP_add_compression_method(int id, void *cm); + # endif + ++/* TLS extensions functions */ ++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len); ++ ++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, ++ void *arg); ++ ++/* Pre-shared secret session resumption functions */ ++int SSL_set_session_secret_cb(SSL *s, ++ tls_session_secret_cb_fn tls_session_secret_cb, ++ void *arg); ++ + /* BEGIN ERROR CODES */ + /* + * The following lines are auto generated by the script mkerr.pl. Any changes +@@ -1977,6 +2007,7 @@ void ERR_load_SSL_strings(void); + # define SSL_F_TLS1_ENC 210 + # define SSL_F_TLS1_SETUP_KEY_BLOCK 211 + # define SSL_F_WRITE_PENDING 212 ++#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213 + + /* Reason codes. */ + # define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -upr openssl-0.9.8zf.orig/ssl/ssl_sess.c openssl-0.9.8zf/ssl/ssl_sess.c +--- openssl-0.9.8zf.orig/ssl/ssl_sess.c 2015-03-19 15:46:46.000000000 +0200 ++++ openssl-0.9.8zf/ssl/ssl_sess.c 2015-03-24 16:28:04.819907515 +0200 +@@ -716,6 +716,61 @@ long SSL_CTX_get_timeout(const SSL_CTX * + return (s->session_timeout); + } + ++#ifndef OPENSSL_NO_TLSEXT ++int SSL_set_session_secret_cb( ++ SSL *s, ++ int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, ++ STACK_OF(SSL_CIPHER) *peer_ciphers, ++ SSL_CIPHER **cipher, void *arg), void *arg) ++{ ++ if (s == NULL) ++ return 0; ++ s->tls_session_secret_cb = tls_session_secret_cb; ++ s->tls_session_secret_cb_arg = arg; ++ return 1; ++} ++ ++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb, ++ void *arg) ++{ ++ if (s == NULL) ++ return 0; ++ s->tls_session_ticket_ext_cb = cb; ++ s->tls_session_ticket_ext_cb_arg = arg; ++ return 1; ++} ++ ++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len) ++{ ++ if (s->version >= TLS1_VERSION) { ++ if (s->tlsext_session_ticket) { ++ OPENSSL_free(s->tlsext_session_ticket); ++ s->tlsext_session_ticket = NULL; ++ } ++ ++ s->tlsext_session_ticket = OPENSSL_malloc( ++ sizeof(TLS_SESSION_TICKET_EXT) + ext_len); ++ if (!s->tlsext_session_ticket) { ++ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE); ++ return 0; ++ } ++ ++ if (ext_data) { ++ s->tlsext_session_ticket->length = ext_len; ++ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1; ++ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len); ++ } else { ++ s->tlsext_session_ticket->length = 0; ++ s->tlsext_session_ticket->data = NULL; ++ } ++ ++ return 1; ++ } ++ ++ return 0; ++} ++#endif /* OPENSSL_NO_TLSEXT */ ++ + typedef struct timeout_param_st { + SSL_CTX *ctx; + long time; +diff -upr openssl-0.9.8zf.orig/ssl/t1_lib.c openssl-0.9.8zf/ssl/t1_lib.c +--- openssl-0.9.8zf.orig/ssl/t1_lib.c 2015-03-19 15:46:46.000000000 +0200 ++++ openssl-0.9.8zf/ssl/t1_lib.c 2015-03-24 16:32:46.923905254 +0200 +@@ -108,6 +108,11 @@ int tls1_new(SSL *s) + + void tls1_free(SSL *s) + { ++#ifndef OPENSSL_NO_TLSEXT ++ if (s->tlsext_session_ticket) { ++ OPENSSL_free(s->tlsext_session_ticket); ++ } ++#endif + ssl3_free(s); + } + +@@ -206,8 +211,20 @@ unsigned char *ssl_add_clienthello_tlsex + int ticklen; + if (!s->new_session && s->session && s->session->tlsext_tick) + ticklen = s->session->tlsext_ticklen; +- else ++ else if (s->session && s->tlsext_session_ticket && ++ s->tlsext_session_ticket->data) { ++ ticklen = s->tlsext_session_ticket->length; ++ s->session->tlsext_tick = OPENSSL_malloc(ticklen); ++ if (!s->session->tlsext_tick) ++ return NULL; ++ memcpy(s->session->tlsext_tick, s->tlsext_session_ticket->data, ++ ticklen); ++ s->session->tlsext_ticklen = ticklen; ++ } else + ticklen = 0; ++ if (ticklen == 0 && s->tlsext_session_ticket && ++ s->tlsext_session_ticket->data == NULL) ++ goto skip_ext; + /* + * Check for enough room 2 for extension type, 2 for len rest for + * ticket +@@ -221,6 +238,7 @@ unsigned char *ssl_add_clienthello_tlsex + ret += ticklen; + } + } ++skip_ext: + + if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp && + s->version != DTLS1_VERSION) { +@@ -560,6 +578,14 @@ int ssl_parse_clienthello_tlsext(SSL *s, + if (!ssl_parse_clienthello_renegotiate_ext(s, data, size, al)) + return 0; + renegotiate_seen = 1; ++ } else if (type == TLSEXT_TYPE_session_ticket) { ++ if (s->tls_session_ticket_ext_cb && ++ !s->tls_session_ticket_ext_cb(s, data, size, ++ s->tls_session_ticket_ext_cb_arg)) ++ { ++ *al = TLS1_AD_INTERNAL_ERROR; ++ return 0; ++ } + } else if (type == TLSEXT_TYPE_status_request && + s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) { + +@@ -710,6 +736,13 @@ int ssl_parse_serverhello_tlsext(SSL *s, + } + tlsext_servername = 1; + } else if (type == TLSEXT_TYPE_session_ticket) { ++ if (s->tls_session_ticket_ext_cb && ++ !s->tls_session_ticket_ext_cb( ++ s, data, size, ++ s->tls_session_ticket_ext_cb_arg)) { ++ *al = TLS1_AD_INTERNAL_ERROR; ++ return 0; ++ } + if ((SSL_get_options(s) & SSL_OP_NO_TICKET) + || (size > 0)) { + *al = TLS1_AD_UNSUPPORTED_EXTENSION; +@@ -993,6 +1026,14 @@ int tls1_process_ticket(SSL *s, unsigned + s->tlsext_ticket_expected = 1; + return 0; /* Cache miss */ + } ++ if (s->tls_session_secret_cb) { ++ /* Indicate cache miss here and instead of ++ * generating the session from ticket now, ++ * trigger abbreviated handshake based on ++ * external mechanism to calculate the master ++ * secret later. */ ++ return 0; ++ } + return tls_decrypt_ticket(s, p, size, session_id, len, ret); + } + p += size; +diff -upr openssl-0.9.8zf.orig/ssl/tls1.h openssl-0.9.8zf/ssl/tls1.h +--- openssl-0.9.8zf.orig/ssl/tls1.h 2015-03-19 15:46:46.000000000 +0200 ++++ openssl-0.9.8zf/ssl/tls1.h 2015-03-24 16:33:31.855904894 +0200 +@@ -460,6 +460,12 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T + # define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" + # endif + ++/* TLS extension struct */ ++struct tls_session_ticket_ext_st { ++ unsigned short length; ++ void *data; ++}; ++ + #ifdef __cplusplus + } + #endif +diff -upr openssl-0.9.8zf.orig/util/ssleay.num openssl-0.9.8zf/util/ssleay.num +--- openssl-0.9.8zf.orig/util/ssleay.num 2015-03-19 15:47:15.000000000 +0200 ++++ openssl-0.9.8zf/util/ssleay.num 2015-03-24 16:33:51.127904739 +0200 +@@ -242,3 +242,5 @@ SSL_set_SSL_CTX + SSL_get_servername 291 EXIST::FUNCTION:TLSEXT + SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT + SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE ++SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT ++SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT diff --git a/contrib/wpa/src/ap/accounting.c b/contrib/wpa/src/ap/accounting.c index 7c55146b2c5..a096de4d3e5 100644 --- a/contrib/wpa/src/ap/accounting.c +++ b/contrib/wpa/src/ap/accounting.c @@ -459,10 +459,14 @@ int accounting_init(struct hostapd_data *hapd) { struct os_time now; - /* Acct-Session-Id should be unique over reboots. If reliable clock is - * not available, this could be replaced with reboot counter, etc. */ + /* Acct-Session-Id should be unique over reboots. Using a random number + * is preferred. If that is not available, take the current time. Mix + * in microseconds to make this more likely to be unique. */ os_get_time(&now); - hapd->acct_session_id_hi = now.sec; + if (os_get_random((u8 *) &hapd->acct_session_id_hi, + sizeof(hapd->acct_session_id_hi)) < 0) + hapd->acct_session_id_hi = now.sec; + hapd->acct_session_id_hi ^= now.usec; if (radius_client_register(hapd->radius, RADIUS_ACCT, accounting_receive, hapd)) @@ -475,7 +479,7 @@ int accounting_init(struct hostapd_data *hapd) /** - * accounting_deinit: Deinitilize accounting + * accounting_deinit: Deinitialize accounting * @hapd: hostapd BSS data */ void accounting_deinit(struct hostapd_data *hapd) diff --git a/contrib/wpa/src/ap/acs.c b/contrib/wpa/src/ap/acs.c index ae7f6c30928..03d797fe883 100644 --- a/contrib/wpa/src/ap/acs.c +++ b/contrib/wpa/src/ap/acs.c @@ -479,16 +479,10 @@ static int acs_usable_chan(struct hostapd_channel_data *chan) static int is_in_chanlist(struct hostapd_iface *iface, struct hostapd_channel_data *chan) { - int *entry; - - if (!iface->conf->chanlist) + if (!iface->conf->acs_ch_list.num) return 1; - for (entry = iface->conf->chanlist; *entry != -1; entry++) { - if (*entry == chan->chan) - return 1; - } - return 0; + return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); } @@ -900,6 +894,9 @@ static int acs_request_scan(struct hostapd_iface *iface) if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; + if (!is_in_chanlist(iface, chan)) + continue; + *freq++ = chan->freq; } *freq = 0; diff --git a/contrib/wpa/src/ap/ap_config.c b/contrib/wpa/src/ap/ap_config.c index 76011dc07fd..9a96e50b738 100644 --- a/contrib/wpa/src/ap/ap_config.c +++ b/contrib/wpa/src/ap/ap_config.c @@ -172,6 +172,7 @@ struct hostapd_config * hostapd_config_defaults(void) conf->ap_table_max_size = 255; conf->ap_table_expiration_time = 60; + conf->track_sta_max_age = 180; #ifdef CONFIG_TESTING_OPTIONS conf->ignore_probe_probability = 0.0; @@ -181,6 +182,8 @@ struct hostapd_config * hostapd_config_defaults(void) conf->corrupt_gtk_rekey_mic_probability = 0.0; #endif /* CONFIG_TESTING_OPTIONS */ + conf->acs = 0; + conf->acs_ch_list.num = 0; #ifdef CONFIG_ACS conf->acs_num_scans = 5; #endif /* CONFIG_ACS */ @@ -559,6 +562,13 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->server_id); +#ifdef CONFIG_TESTING_OPTIONS + wpabuf_free(conf->own_ie_override); +#endif /* CONFIG_TESTING_OPTIONS */ + + os_free(conf->no_probe_resp_if_seen_on); + os_free(conf->no_auth_if_seen_on); + os_free(conf); } @@ -579,7 +589,7 @@ void hostapd_config_free(struct hostapd_config *conf) os_free(conf->bss); os_free(conf->supported_rates); os_free(conf->basic_rates); - os_free(conf->chanlist); + os_free(conf->acs_ch_list.range); os_free(conf->driver_params); #ifdef CONFIG_ACS os_free(conf->acs_chan_bias); @@ -817,9 +827,9 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, if (full_config && bss->wps_state && bss->wpa && (!(bss->wpa & 2) || - !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) { + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) { wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " - "WPA2/CCMP forced WPS to be disabled"); + "WPA2/CCMP/GCMP forced WPS to be disabled"); bss->wps_state = 0; } #endif /* CONFIG_WPS */ @@ -841,6 +851,29 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } +static int hostapd_config_check_cw(struct hostapd_config *conf, int queue) +{ + int tx_cwmin = conf->tx_queue[queue].cwmin; + int tx_cwmax = conf->tx_queue[queue].cwmax; + int ac_cwmin = conf->wmm_ac_params[queue].cwmin; + int ac_cwmax = conf->wmm_ac_params[queue].cwmax; + + if (tx_cwmin > tx_cwmax) { + wpa_printf(MSG_ERROR, + "Invalid TX queue cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)", + tx_cwmin, tx_cwmax); + return -1; + } + if (ac_cwmin > ac_cwmax) { + wpa_printf(MSG_ERROR, + "Invalid WMM AC cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)", + ac_cwmin, ac_cwmax); + return -1; + } + return 0; +} + + int hostapd_config_check(struct hostapd_config *conf, int full_config) { size_t i; @@ -870,6 +903,11 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config) return -1; } + for (i = 0; i < NUM_TX_QUEUES; i++) { + if (hostapd_config_check_cw(conf, i)) + return -1; + } + for (i = 0; i < conf->num_bss; i++) { if (hostapd_config_check_bss(conf->bss[i], conf, full_config)) return -1; @@ -937,10 +975,11 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, bss->rsn_pairwise = WPA_CIPHER_CCMP; } else { bss->ssid.security_policy = SECURITY_PLAINTEXT; - bss->wpa_group = WPA_CIPHER_NONE; - bss->wpa_pairwise = WPA_CIPHER_NONE; - bss->rsn_pairwise = WPA_CIPHER_NONE; - if (full_config) + if (full_config) { + bss->wpa_group = WPA_CIPHER_NONE; + bss->wpa_pairwise = WPA_CIPHER_NONE; + bss->rsn_pairwise = WPA_CIPHER_NONE; bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE; + } } } diff --git a/contrib/wpa/src/ap/ap_config.h b/contrib/wpa/src/ap/ap_config.h index 961d2dd389f..de470a969b5 100644 --- a/contrib/wpa/src/ap/ap_config.h +++ b/contrib/wpa/src/ap/ap_config.h @@ -1,6 +1,6 @@ /* * hostapd / Configuration definitions and helpers functions - * Copyright (c) 2003-2012, Jouni Malinen + * Copyright (c) 2003-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,8 +12,10 @@ #include "common/defs.h" #include "ip_addr.h" #include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wps/wps.h" +#include "fst/fst.h" /** * mesh_conf - local MBSS state and settings @@ -31,8 +33,8 @@ struct mesh_conf { u8 mesh_sp_id; /* Authentication Protocol Identifier */ u8 mesh_auth_id; - u8 *ies; - int ie_len; + u8 *rsn_ie; + int rsn_ie_len; #define MESH_CONF_SEC_NONE BIT(0) #define MESH_CONF_SEC_AUTH BIT(1) #define MESH_CONF_SEC_AMPE BIT(2) @@ -57,8 +59,6 @@ struct hostapd_radius_servers; struct ft_remote_r0kh; struct ft_remote_r1kh; -#define HOSTAPD_MAX_SSID_LEN 32 - #define NUM_WEP_KEYS 4 struct hostapd_wep_keys { u8 idx; @@ -78,7 +78,7 @@ typedef enum hostap_security_policy { } secpolicy; struct hostapd_ssid { - u8 ssid[HOSTAPD_MAX_SSID_LEN]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; unsigned int ssid_set:1; unsigned int utf8_ssid:1; @@ -114,12 +114,10 @@ struct hostapd_vlan { struct hostapd_vlan *next; int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ char ifname[IFNAMSIZ + 1]; + int configured; int dynamic_vlan; #ifdef CONFIG_FULL_DYNAMIC_VLAN -#define DVLAN_CLEAN_BR 0x1 -#define DVLAN_CLEAN_VLAN 0x2 -#define DVLAN_CLEAN_VLAN_PORT 0x4 #define DVLAN_CLEAN_WLAN_PORT 0x8 int clean; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -332,6 +330,7 @@ struct hostapd_bss_config { char *private_key; char *private_key_passwd; int check_crl; + unsigned int tls_session_lifetime; char *ocsp_stapling_response; char *dh_file; char *openssl_ciphers; @@ -490,6 +489,7 @@ struct hostapd_bss_config { int osen; int proxy_arp; + int na_mcast_to_ucast; #ifdef CONFIG_HS20 int hs20; int disable_dgaf; @@ -510,7 +510,7 @@ struct hostapd_bss_config { char file[256]; } *hs20_icons; size_t hs20_icons_count; - u8 osu_ssid[HOSTAPD_MAX_SSID_LEN]; + u8 osu_ssid[SSID_MAX_LEN]; size_t osu_ssid_len; struct hs20_osu_provider { unsigned int friendly_name_count; @@ -545,6 +545,7 @@ struct hostapd_bss_config { #ifdef CONFIG_TESTING_OPTIONS u8 bss_load_test[5]; u8 bss_load_test_set; + struct wpabuf *own_ie_override; #endif /* CONFIG_TESTING_OPTIONS */ #define MESH_ENABLED BIT(0) @@ -553,6 +554,9 @@ struct hostapd_bss_config { int radio_measurements; int vendor_vht; + + char *no_probe_resp_if_seen_on; + char *no_auth_if_seen_on; }; @@ -568,7 +572,8 @@ struct hostapd_config { int fragm_threshold; u8 send_probe_response; u8 channel; - int *chanlist; + u8 acs; + struct wpa_freq_range_list acs_ch_list; enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ enum { LONG_PREAMBLE = 0, @@ -584,6 +589,9 @@ struct hostapd_config { int ap_table_max_size; int ap_table_expiration_time; + unsigned int track_sta_max_num; + unsigned int track_sta_max_age; + char country[3]; /* first two octets: country code as described in * ISO/IEC 3166-1. Third octet: * ' ' (ascii 32): all environments @@ -620,6 +628,7 @@ struct hostapd_config { u16 ht_capab; int ieee80211n; int secondary_channel; + int no_pri_sec_switch; int require_ht; int obss_interval; u32 vht_capab; @@ -629,6 +638,10 @@ struct hostapd_config { u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; +#ifdef CONFIG_FST + struct fst_iface_cfg fst_cfg; +#endif /* CONFIG_FST */ + #ifdef CONFIG_P2P u8 p2p_go_ctwindow; #endif /* CONFIG_P2P */ diff --git a/contrib/wpa/src/ap/ap_drv_ops.c b/contrib/wpa/src/ap/ap_drv_ops.c index e16306c4e10..6cafcb74935 100644 --- a/contrib/wpa/src/ap/ap_drv_ops.c +++ b/contrib/wpa/src/ap/ap_drv_ops.c @@ -81,6 +81,22 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, wpabuf_put_data(proberesp, buf, pos - buf); } +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + size_t add = wpabuf_len(hapd->iface->fst_ies); + + if (wpabuf_resize(&beacon, add) < 0) + goto fail; + wpabuf_put_buf(beacon, hapd->iface->fst_ies); + if (wpabuf_resize(&proberesp, add) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->iface->fst_ies); + if (wpabuf_resize(&assocresp, add) < 0) + goto fail; + wpabuf_put_buf(assocresp, hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + if (hapd->wps_beacon_ie) { if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < 0) @@ -217,6 +233,15 @@ void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, } +int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) + return 0; + + return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL); +} + + int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) { struct wpabuf *beacon, *proberesp, *assocresp; @@ -281,8 +306,14 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, params.wpa = hapd->conf->wpa; params.ieee802_1x = hapd->conf->ieee802_1x; params.wpa_group = hapd->conf->wpa_group; - params.wpa_pairwise = hapd->conf->wpa_pairwise | - hapd->conf->rsn_pairwise; + if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == + (WPA_PROTO_WPA | WPA_PROTO_RSN)) + params.wpa_pairwise = hapd->conf->wpa_pairwise | + hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_RSN) + params.wpa_pairwise = hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_WPA) + params.wpa_pairwise = hapd->conf->wpa_pairwise; params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt; params.rsn_preauth = hapd->conf->rsn_preauth; #ifdef CONFIG_IEEE80211W @@ -618,7 +649,7 @@ int hostapd_drv_send_mlme(struct hostapd_data *hapd, { if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) return 0; - return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack); + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0); } @@ -712,16 +743,100 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, } +static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode, + int acs_ch_list_all, + int **freq_list) +{ + int i; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + + if ((acs_ch_list_all || + freq_range_list_includes(&hapd->iface->conf->acs_ch_list, + chan->chan)) && + !(chan->flag & HOSTAPD_CHAN_DISABLED)) + int_array_add_unique(freq_list, chan->freq); + } +} + + int hostapd_drv_do_acs(struct hostapd_data *hapd) { struct drv_acs_params params; + int ret, i, acs_ch_list_all = 0; + u8 *channels = NULL; + unsigned int num_channels = 0; + struct hostapd_hw_modes *mode; + int *freq_list = NULL; if (hapd->driver == NULL || hapd->driver->do_acs == NULL) return 0; + os_memset(¶ms, 0, sizeof(params)); params.hw_mode = hapd->iface->conf->hw_mode; + + /* + * If no chanlist config parameter is provided, include all enabled + * channels of the selected hw_mode. + */ + if (!hapd->iface->conf->acs_ch_list.num) + acs_ch_list_all = 1; + + mode = hapd->iface->current_mode; + if (mode) { + channels = os_malloc(mode->num_channels); + if (channels == NULL) + return -1; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (!acs_ch_list_all && + !freq_range_list_includes( + &hapd->iface->conf->acs_ch_list, + chan->chan)) + continue; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) { + channels[num_channels++] = chan->chan; + int_array_add_unique(&freq_list, chan->freq); + } + } + } else { + for (i = 0; i < hapd->iface->num_hw_features; i++) { + mode = &hapd->iface->hw_features[i]; + hostapd_get_hw_mode_any_channels(hapd, mode, + acs_ch_list_all, + &freq_list); + } + } + + params.ch_list = channels; + params.ch_list_len = num_channels; + params.freq_list = freq_list; + params.ht_enabled = !!(hapd->iface->conf->ieee80211n); params.ht40_enabled = !!(hapd->iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); - return hapd->driver->do_acs(hapd->drv_priv, ¶ms); + params.vht_enabled = !!(hapd->iface->conf->ieee80211ac); + params.ch_width = 20; + if (hapd->iface->conf->ieee80211n && params.ht40_enabled) + params.ch_width = 40; + + /* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth + */ + if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) { + if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) + params.ch_width = 80; + else if (hapd->iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_160MHZ || + hapd->iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_80P80MHZ) + params.ch_width = 160; + } + + ret = hapd->driver->do_acs(hapd->drv_priv, ¶ms); + os_free(channels); + + return ret; } diff --git a/contrib/wpa/src/ap/ap_drv_ops.h b/contrib/wpa/src/ap/ap_drv_ops.h index 5d07e71f1bf..82eaf3f08bb 100644 --- a/contrib/wpa/src/ap/ap_drv_ops.h +++ b/contrib/wpa/src/ap/ap_drv_ops.h @@ -24,6 +24,7 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon, struct wpabuf *proberesp, struct wpabuf *assocresp); +int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd); int hostapd_set_ap_wps_ie(struct hostapd_data *hapd); int hostapd_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); diff --git a/contrib/wpa/src/ap/ap_list.c b/contrib/wpa/src/ap/ap_list.c index 04a56a95efd..8bf6ddec8d3 100644 --- a/contrib/wpa/src/ap/ap_list.c +++ b/contrib/wpa/src/ap/ap_list.c @@ -193,14 +193,14 @@ void ap_list_process_beacon(struct hostapd_iface *iface, elems->supp_rates, elems->supp_rates_len, elems->ext_supp_rates, elems->ext_supp_rates_len); - if (elems->erp_info && elems->erp_info_len == 1) + if (elems->erp_info) ap->erp = elems->erp_info[0]; else ap->erp = -1; - if (elems->ds_params && elems->ds_params_len == 1) + if (elems->ds_params) ap->channel = elems->ds_params[0]; - else if (elems->ht_operation && elems->ht_operation_len >= 1) + else if (elems->ht_operation) ap->channel = elems->ht_operation[0]; else if (fi) ap->channel = fi->channel; @@ -248,15 +248,12 @@ void ap_list_process_beacon(struct hostapd_iface *iface, } -static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) +void ap_list_timer(struct hostapd_iface *iface) { - struct hostapd_iface *iface = eloop_ctx; struct os_reltime now; struct ap_info *ap; int set_beacon = 0; - eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); - if (!iface->ap_list) return; @@ -305,13 +302,11 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) int ap_list_init(struct hostapd_iface *iface) { - eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); return 0; } void ap_list_deinit(struct hostapd_iface *iface) { - eloop_cancel_timeout(ap_list_timer, iface, NULL); hostapd_free_aps(iface); } diff --git a/contrib/wpa/src/ap/ap_list.h b/contrib/wpa/src/ap/ap_list.h index 93dc0eda88d..9e0353cfec9 100644 --- a/contrib/wpa/src/ap/ap_list.h +++ b/contrib/wpa/src/ap/ap_list.h @@ -39,6 +39,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, #ifdef NEED_AP_MLME int ap_list_init(struct hostapd_iface *iface); void ap_list_deinit(struct hostapd_iface *iface); +void ap_list_timer(struct hostapd_iface *iface); #else /* NEED_AP_MLME */ static inline int ap_list_init(struct hostapd_iface *iface) { @@ -48,6 +49,10 @@ static inline int ap_list_init(struct hostapd_iface *iface) static inline void ap_list_deinit(struct hostapd_iface *iface) { } + +static inline void ap_list_timer(struct hostapd_iface *iface) +{ +} #endif /* NEED_AP_MLME */ #endif /* AP_LIST_H */ diff --git a/contrib/wpa/src/ap/authsrv.c b/contrib/wpa/src/ap/authsrv.c index bd1778e4186..934dcfc8d63 100644 --- a/contrib/wpa/src/ap/authsrv.c +++ b/contrib/wpa/src/ap/authsrv.c @@ -55,10 +55,11 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, { const struct hostapd_eap_user *eap_user; int i; + int rv = -1; eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2); if (eap_user == NULL) - return -1; + goto out; if (user == NULL) return 0; @@ -72,7 +73,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, if (eap_user->password) { user->password = os_malloc(eap_user->password_len); if (user->password == NULL) - return -1; + goto out; os_memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; @@ -83,8 +84,13 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; user->accept_attr = eap_user->accept_attr; + rv = 0; - return 0; +out: + if (rv) + wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__); + + return rv; } @@ -126,6 +132,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) #endif /* CONFIG_HS20 */ srv.erp = conf->eap_server_erp; srv.erp_domain = conf->erp_domain; + srv.tls_session_lifetime = conf->tls_session_lifetime; hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { @@ -145,9 +152,12 @@ int authsrv_init(struct hostapd_data *hapd) if (hapd->conf->eap_server && (hapd->conf->ca_cert || hapd->conf->server_cert || hapd->conf->private_key || hapd->conf->dh_file)) { + struct tls_config conf; struct tls_connection_params params; - hapd->ssl_ctx = tls_init(NULL); + os_memset(&conf, 0, sizeof(conf)); + conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + hapd->ssl_ctx = tls_init(&conf); if (hapd->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize TLS"); authsrv_deinit(hapd); diff --git a/contrib/wpa/src/ap/beacon.c b/contrib/wpa/src/ap/beacon.c index e575b65cbf3..5fe8fd5660b 100644 --- a/contrib/wpa/src/ap/beacon.c +++ b/contrib/wpa/src/ap/beacon.c @@ -360,7 +360,6 @@ static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, - struct sta_info *sta, const struct ieee80211_mgmt *req, int is_p2p, size_t *resp_len) { @@ -378,6 +377,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, if (hapd->p2p_probe_resp_ie) buflen += wpabuf_len(hapd->p2p_probe_resp_ie); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) + buflen += wpabuf_len(hapd->iface->fst_ies); +#endif /* CONFIG_FST */ if (hapd->conf->vendor_elements) buflen += wpabuf_len(hapd->conf->vendor_elements); if (hapd->conf->vendor_vht) { @@ -402,7 +405,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* hardware or low-level driver will setup seq_ctrl and timestamp */ resp->u.probe_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); + host_to_le16(hostapd_own_capab_info(hapd)); pos = resp->u.probe_resp.variable; *pos++ = WLAN_EID_SSID; @@ -450,6 +453,15 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, &hapd->cs_c_off_proberesp); + +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies), + wpabuf_len(hapd->iface->fst_ies)); + pos += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { pos = hostapd_eid_vht_capabilities(hapd, pos); @@ -540,6 +552,102 @@ static enum ssid_match_result ssid_match(struct hostapd_data *hapd, } +void sta_track_expire(struct hostapd_iface *iface, int force) +{ + struct os_reltime now; + struct hostapd_sta_info *info; + + if (!iface->num_sta_seen) + return; + + os_get_reltime(&now); + while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info, + list))) { + if (!force && + !os_reltime_expired(&now, &info->last_seen, + iface->conf->track_sta_max_age)) + break; + force = 0; + + wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for " + MACSTR, iface->bss[0]->conf->iface, + MAC2STR(info->addr)); + dl_list_del(&info->list); + iface->num_sta_seen--; + os_free(info); + } +} + + +static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface, + const u8 *addr) +{ + struct hostapd_sta_info *info; + + dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list) + if (os_memcmp(addr, info->addr, ETH_ALEN) == 0) + return info; + + return NULL; +} + + +void sta_track_add(struct hostapd_iface *iface, const u8 *addr) +{ + struct hostapd_sta_info *info; + + info = sta_track_get(iface, addr); + if (info) { + /* Move the most recent entry to the end of the list */ + dl_list_del(&info->list); + dl_list_add_tail(&iface->sta_seen, &info->list); + os_get_reltime(&info->last_seen); + return; + } + + /* Add a new entry */ + info = os_zalloc(sizeof(*info)); + os_memcpy(info->addr, addr, ETH_ALEN); + os_get_reltime(&info->last_seen); + + if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { + /* Expire oldest entry to make room for a new one */ + sta_track_expire(iface, 1); + } + + wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for " + MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr)); + dl_list_add_tail(&iface->sta_seen, &info->list); + iface->num_sta_seen++; +} + + +struct hostapd_data * +sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, + const char *ifname) +{ + struct hapd_interfaces *interfaces = iface->interfaces; + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_data *hapd = NULL; + + iface = interfaces->iface[i]; + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (os_strcmp(ifname, hapd->conf->iface) == 0) + break; + hapd = NULL; + } + + if (hapd && sta_track_get(iface, addr)) + return hapd; + } + + return NULL; +} + + void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal) @@ -548,7 +656,6 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee802_11_elems elems; const u8 *ie; size_t ie_len; - struct sta_info *sta = NULL; size_t i, resp_len; int noack; enum ssid_match_result res; @@ -556,6 +663,8 @@ void handle_probe_req(struct hostapd_data *hapd, ie = mgmt->u.probe_req.variable; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) return; + if (hapd->iconf->track_sta_max_num) + sta_track_add(hapd->iface, mgmt->sa); ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) @@ -590,7 +699,7 @@ void handle_probe_req(struct hostapd_data *hapd, * is less likely to see them (Probe Request frame sent on a * neighboring, but partially overlapping, channel). */ - if (elems.ds_params && elems.ds_params_len == 1 && + if (elems.ds_params && hapd->iface->current_mode && (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G || hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) && @@ -635,8 +744,6 @@ void handle_probe_req(struct hostapd_data *hapd, return; } - sta = ap_get_sta(hapd, mgmt->sa); - #ifdef CONFIG_P2P if ((hapd->conf->p2p & P2P_GROUP_OWNER) && elems.ssid_len == P2P_WILDCARD_SSID_LEN && @@ -649,10 +756,7 @@ void handle_probe_req(struct hostapd_data *hapd, res = ssid_match(hapd, elems.ssid, elems.ssid_len, elems.ssid_list, elems.ssid_list_len); - if (res != NO_SSID_MATCH) { - if (sta) - sta->ssid_probe = &hapd->conf->ssid; - } else { + if (res == NO_SSID_MATCH) { if (!(mgmt->da[0] & 0x01)) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for foreign SSID '%s' (DA " MACSTR ")%s", @@ -709,6 +813,18 @@ void handle_probe_req(struct hostapd_data *hapd, /* TODO: verify that supp_rates contains at least one matching rate * with AP configuration */ + if (hapd->conf->no_probe_resp_if_seen_on && + is_multicast_ether_addr(mgmt->da) && + is_multicast_ether_addr(mgmt->bssid) && + sta_track_seen_on(hapd->iface, mgmt->sa, + hapd->conf->no_probe_resp_if_seen_on)) { + wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR + " since STA has been seen on %s", + hapd->conf->iface, MAC2STR(mgmt->sa), + hapd->conf->no_probe_resp_if_seen_on); + return; + } + #ifdef CONFIG_TESTING_OPTIONS if (hapd->iconf->ignore_probe_probability > 0.0 && drand48() < hapd->iconf->ignore_probe_probability) { @@ -719,7 +835,7 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_TESTING_OPTIONS */ - resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, + resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL, &resp_len); if (resp == NULL) return; @@ -774,7 +890,7 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, "this"); /* Generate a Probe Response template for the non-P2P case */ - return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len); + return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len); } #endif /* NEED_AP_MLME */ @@ -804,6 +920,10 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, if (hapd->p2p_beacon_ie) tail_len += wpabuf_len(hapd->p2p_beacon_ie); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) + tail_len += wpabuf_len(hapd->iface->fst_ies); +#endif /* CONFIG_FST */ if (hapd->conf->vendor_elements) tail_len += wpabuf_len(hapd->conf->vendor_elements); @@ -833,7 +953,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, host_to_le16(hapd->iconf->beacon_int); /* hardware or low-level driver will setup seq_ctrl and timestamp */ - capab_info = hostapd_own_capab_info(hapd, NULL, 0); + capab_info = hostapd_own_capab_info(hapd); head->u.beacon.capab_info = host_to_le16(capab_info); pos = &head->u.beacon.variable[0]; @@ -902,6 +1022,15 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, &hapd->cs_c_off_beacon); + +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies), + wpabuf_len(hapd->iface->fst_ies)); + tailpos += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); @@ -963,8 +1092,14 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, params->basic_rates = hapd->iface->basic_rates; params->ssid = hapd->conf->ssid.ssid; params->ssid_len = hapd->conf->ssid.ssid_len; - params->pairwise_ciphers = hapd->conf->wpa_pairwise | - hapd->conf->rsn_pairwise; + if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == + (WPA_PROTO_WPA | WPA_PROTO_RSN)) + params->pairwise_ciphers = hapd->conf->wpa_pairwise | + hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_RSN) + params->pairwise_ciphers = hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_WPA) + params->pairwise_ciphers = hapd->conf->wpa_pairwise; params->group_cipher = hapd->conf->wpa_group; params->key_mgmt_suites = hapd->conf->wpa_key_mgmt; params->auth_algs = hapd->conf->auth_algs; diff --git a/contrib/wpa/src/ap/beacon.h b/contrib/wpa/src/ap/beacon.h index 722159a7500..d98f42e8157 100644 --- a/contrib/wpa/src/ap/beacon.h +++ b/contrib/wpa/src/ap/beacon.h @@ -21,5 +21,10 @@ int ieee802_11_update_beacons(struct hostapd_iface *iface); int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params); void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params); +void sta_track_add(struct hostapd_iface *iface, const u8 *addr); +void sta_track_expire(struct hostapd_iface *iface, int force); +struct hostapd_data * +sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, + const char *ifname); #endif /* BEACON_H */ diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.c b/contrib/wpa/src/ap/ctrl_iface_ap.c index 41ab988277b..c98978f33d0 100644 --- a/contrib/wpa/src/ap/ctrl_iface_ap.c +++ b/contrib/wpa/src/ap/ctrl_iface_ap.c @@ -12,6 +12,7 @@ #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "eapol_auth/eapol_auth_sm.h" +#include "fst/fst_ctrl_iface.h" #include "hostapd.h" #include "ieee802_1x.h" #include "wpa_auth.h" @@ -153,6 +154,13 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, } #endif /* CONFIG_SAE */ + if (sta->vlan_id > 0) { + res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n", + sta->vlan_id); + if (!os_snprintf_error(buflen - len, res)) + len += res; + } + return len; } @@ -199,7 +207,10 @@ int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, return -1; } - return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); + ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); + ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret); + + return ret; } diff --git a/contrib/wpa/src/ap/dfs.c b/contrib/wpa/src/ap/dfs.c index da6fd464671..715f19b6ac7 100644 --- a/contrib/wpa/src/ap/dfs.c +++ b/contrib/wpa/src/ap/dfs.c @@ -122,6 +122,20 @@ static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) } +static struct hostapd_channel_data * +dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx) +{ + int i; + + for (i = first_chan_idx; i < mode->num_channels; i++) { + if (mode->channels[i].freq == freq) + return &mode->channels[i]; + } + + return NULL; +} + + static int dfs_chan_range_available(struct hostapd_hw_modes *mode, int first_chan_idx, int num_chans, int skip_radar) @@ -129,15 +143,15 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode, struct hostapd_channel_data *first_chan, *chan; int i; - if (first_chan_idx + num_chans >= mode->num_channels) + if (first_chan_idx + num_chans > mode->num_channels) return 0; first_chan = &mode->channels[first_chan_idx]; for (i = 0; i < num_chans; i++) { - chan = &mode->channels[first_chan_idx + i]; - - if (first_chan->freq + i * 20 != chan->freq) + chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, + first_chan_idx); + if (!chan) return 0; if (!dfs_channel_available(chan, skip_radar)) @@ -151,16 +165,10 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode, static int is_in_chanlist(struct hostapd_iface *iface, struct hostapd_channel_data *chan) { - int *entry; - - if (!iface->conf->chanlist) + if (!iface->conf->acs_ch_list.num) return 1; - for (entry = iface->conf->chanlist; *entry != -1; entry++) { - if (*entry == chan->chan) - return 1; - } - return 0; + return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); } diff --git a/contrib/wpa/src/ap/drv_callbacks.c b/contrib/wpa/src/ap/drv_callbacks.c index a0adc67db43..ca8b75c8390 100644 --- a/contrib/wpa/src/ap/drv_callbacks.c +++ b/contrib/wpa/src/ap/drv_callbacks.c @@ -18,6 +18,7 @@ #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" +#include "fst/fst.h" #include "wnm_ap.h" #include "hostapd.h" #include "ieee802_11.h" @@ -42,10 +43,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, struct ieee802_11_elems elems; const u8 *ie; size_t ielen; -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; u8 *p = buf; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ u16 reason = WLAN_REASON_UNSPECIFIED; u16 status = WLAN_STATUS_SUCCESS; const u8 *p2p_dev_addr = NULL; @@ -58,8 +59,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, * running, so better make sure we stop processing such an * event here. */ - wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with " - "no address"); + wpa_printf(MSG_DEBUG, + "hostapd_notif_assoc: Skip event with no address"); return -1; } random_add_randomness(addr, ETH_ALEN); @@ -89,8 +90,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } else { ie = NULL; ielen = 0; - wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in " - "(Re)AssocReq"); + wpa_printf(MSG_DEBUG, + "STA did not include WPS/RSN/WPA IE in (Re)AssocReq"); } sta = ap_get_sta(hapd, addr); @@ -126,8 +127,6 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, #ifdef CONFIG_IEEE80211N #ifdef NEED_AP_MLME if (elems.ht_capabilities && - elems.ht_capabilities_len >= - sizeof(struct ieee80211_ht_capabilities) && (hapd->iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { struct ieee80211_ht_capabilities *ht_cap = @@ -157,13 +156,20 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->hs20_ie = NULL; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + wpabuf_free(sta->mb_ies); + if (hapd->iface->fst) + sta->mb_ies = mb_ies_by_info(&elems.mb_ies); + else + sta->mb_ies = NULL; +#endif /* CONFIG_FST */ + if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { #ifdef CONFIG_WPS if (hapd->conf->wps_state) { - wpa_printf(MSG_DEBUG, "STA did not include " - "WPA/RSN IE in (Re)Association " - "Request - possible WPS use"); + wpa_printf(MSG_DEBUG, + "STA did not include WPA/RSN IE in (Re)Association Request - possible WPS use"); sta->flags |= WLAN_STA_MAYBE_WPS; goto skip_wpa_check; } @@ -176,13 +182,14 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { struct wpabuf *wps; + sta->flags |= WLAN_STA_WPS; wps = ieee802_11_vendor_ie_concat(ie, ielen, WPS_IE_VENDOR_TYPE); if (wps) { if (wps_is_20(wps)) { - wpa_printf(MSG_DEBUG, "WPS: STA " - "supports WPS 2.0"); + wpa_printf(MSG_DEBUG, + "WPS: STA supports WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } wpabuf_free(wps); @@ -196,16 +203,17 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->addr, p2p_dev_addr); if (sta->wpa_sm == NULL) { - wpa_printf(MSG_ERROR, "Failed to initialize WPA state " - "machine"); + wpa_printf(MSG_ERROR, + "Failed to initialize WPA state machine"); return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, ie, ielen, elems.mdie, elems.mdie_len); if (res != WPA_IE_OK) { - wpa_printf(MSG_DEBUG, "WPA/RSN information element " - "rejected? (res %u)", res); + wpa_printf(MSG_DEBUG, + "WPA/RSN information element rejected? (res %u)", + res); wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); if (res == WPA_INVALID_GROUP) { reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; @@ -248,14 +256,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (sta->sa_query_count == 0) ap_sta_start_sa_query(hapd, sta); -#ifdef CONFIG_IEEE80211R status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; p = hostapd_eid_assoc_comeback_time(hapd, sta, p); hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); -#endif /* CONFIG_IEEE80211R */ return 0; } @@ -283,6 +289,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; + if (req_ies) wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, WPS_IE_VENDOR_TYPE); @@ -299,8 +306,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (wps) { sta->flags |= WLAN_STA_WPS; if (wps_is_20(wps)) { - wpa_printf(MSG_DEBUG, "WPS: STA supports " - "WPS 2.0"); + wpa_printf(MSG_DEBUG, + "WPS: STA supports WPS 2.0"); sta->flags |= WLAN_STA_WPS2; } } else @@ -322,8 +329,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { - wpa_printf(MSG_WARNING, "Failed to initialize WPA " - "state machine"); + wpa_printf(MSG_WARNING, + "Failed to initialize WPA state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm, @@ -395,8 +402,8 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) * was running, so better make sure we stop processing such an * event here. */ - wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event " - "with no address"); + wpa_printf(MSG_DEBUG, + "hostapd_notif_disassoc: Skip event with no address"); return; } @@ -405,8 +412,9 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) sta = ap_get_sta(hapd, addr); if (sta == NULL) { - wpa_printf(MSG_DEBUG, "Disassociation notification for " - "unknown STA " MACSTR, MAC2STR(addr)); + wpa_printf(MSG_DEBUG, + "Disassociation notification for unknown STA " + MACSTR, MAC2STR(addr)); return; } @@ -427,8 +435,8 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) return; hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "disconnected due to excessive " - "missing ACKs"); + HOSTAPD_LEVEL_INFO, + "disconnected due to excessive missing ACKs"); hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK); if (sta) ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); @@ -452,8 +460,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, channel = hostapd_hw_get_channel(hapd, freq); if (!channel) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, "driver switched to " - "bad channel!"); + HOSTAPD_LEVEL_WARNING, + "driver switched to bad channel!"); return; } @@ -532,10 +540,9 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, #ifdef CONFIG_ACS static void hostapd_acs_channel_selected(struct hostapd_data *hapd, - u8 pri_channel, u8 sec_channel) + struct acs_selected_channels *acs_res) { - int channel; - int ret; + int ret, i; if (hapd->iconf->channel) { wpa_printf(MSG_INFO, "ACS: Channel was already set to %d", @@ -543,29 +550,73 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, return; } - hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel); + if (!hapd->iface->current_mode) { + for (i = 0; i < hapd->iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode = + &hapd->iface->hw_features[i]; - channel = pri_channel; - if (!channel) { + if (mode->mode == acs_res->hw_mode) { + hapd->iface->current_mode = mode; + break; + } + } + if (!hapd->iface->current_mode) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "driver selected to bad hw_mode"); + return; + } + } + + hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel); + + if (!acs_res->pri_channel) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "driver switched to bad channel"); return; } - hapd->iconf->channel = channel; + hapd->iconf->channel = acs_res->pri_channel; + hapd->iconf->acs = 1; - if (sec_channel == 0) + if (acs_res->sec_channel == 0) hapd->iconf->secondary_channel = 0; - else if (sec_channel < pri_channel) + else if (acs_res->sec_channel < acs_res->pri_channel) hapd->iconf->secondary_channel = -1; - else if (sec_channel > pri_channel) + else if (acs_res->sec_channel > acs_res->pri_channel) hapd->iconf->secondary_channel = 1; else { wpa_printf(MSG_ERROR, "Invalid secondary channel!"); return; } + if (hapd->iface->conf->ieee80211ac) { + /* set defaults for backwards compatibility */ + hapd->iconf->vht_oper_centr_freq_seg1_idx = 0; + hapd->iconf->vht_oper_centr_freq_seg0_idx = 0; + hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + if (acs_res->ch_width == 80) { + hapd->iconf->vht_oper_centr_freq_seg0_idx = + acs_res->vht_seg0_center_ch; + hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + } else if (acs_res->ch_width == 160) { + if (acs_res->vht_seg1_center_ch == 0) { + hapd->iconf->vht_oper_centr_freq_seg0_idx = + acs_res->vht_seg0_center_ch; + hapd->iconf->vht_oper_chwidth = + VHT_CHANWIDTH_160MHZ; + } else { + hapd->iconf->vht_oper_centr_freq_seg0_idx = + acs_res->vht_seg0_center_ch; + hapd->iconf->vht_oper_centr_freq_seg1_idx = + acs_res->vht_seg1_center_ch; + hapd->iconf->vht_oper_chwidth = + VHT_CHANWIDTH_80P80MHZ; + } + } + } + ret = hostapd_acs_completed(hapd->iface, 0); if (ret) { wpa_printf(MSG_ERROR, @@ -647,8 +698,8 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { - wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " - "state machine"); + wpa_printf(MSG_DEBUG, + "FT: Failed to initialize WPA state machine"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -683,7 +734,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) return; /* handled by the driver */ - wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", + wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", mgmt->u.action.category, (int) plen); sta = ap_get_sta(hapd, mgmt->sa); @@ -694,6 +745,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211R if (mgmt->u.action.category == WLAN_ACTION_FT) { const u8 *payload = drv_mgmt->frame + 24 + 1; + wpa_ft_action_rx(sta->wpa_sm, payload, plen); } #endif /* CONFIG_IEEE80211R */ @@ -710,6 +762,13 @@ static void hostapd_action_rx(struct hostapd_data *hapd, ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); } #endif /* CONFIG_WNM */ +#ifdef CONFIG_FST + if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) { + fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len); + return; + } +#endif /* CONFIG_FST */ + } @@ -761,6 +820,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) if (hapd->ext_mgmt_frame_handling) { size_t hex_len = 2 * rx_mgmt->frame_len + 1; char *hex = os_malloc(hex_len); + if (hex) { wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame, rx_mgmt->frame_len); @@ -778,8 +838,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) hapd = get_hapd_bssid(iface, bssid); if (hapd == NULL) { - u16 fc; - fc = le_to_host16(hdr->frame_control); + u16 fc = le_to_host16(hdr->frame_control); /* * Drop frames to unknown BSSIDs except for Beacon frames which @@ -798,6 +857,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) if (hapd == HAPD_BROADCAST) { size_t i; + ret = 0; for (i = 0; i < iface->num_bss; i++) { /* if bss is set, driver will call this function for @@ -824,6 +884,7 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok) { struct ieee80211_hdr *hdr; + hdr = (struct ieee80211_hdr *) buf; hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); if (hapd == NULL || hapd == HAPD_BROADCAST) @@ -837,6 +898,7 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta) return 0; @@ -863,11 +925,10 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, size_t j; for (j = 0; j < iface->num_bss; j++) { - if ((sta = ap_get_sta(iface->bss[j], src))) { - if (sta->flags & WLAN_STA_ASSOC) { - hapd = iface->bss[j]; - break; - } + sta = ap_get_sta(iface->bss[j], src); + if (sta && sta->flags & WLAN_STA_ASSOC) { + hapd = iface->bss[j]; + break; } } @@ -927,7 +988,8 @@ static void hostapd_single_channel_get_survey(struct hostapd_iface *iface, if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED) return; - wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)", + wpa_printf(MSG_DEBUG, + "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)", survey->freq, (unsigned long int) survey->channel_time, (unsigned long int) survey->channel_time_busy); @@ -1061,6 +1123,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_mgmt.frame_len >= 24) { const struct ieee80211_hdr *hdr; u16 fc; + hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; fc = le_to_host16(hdr->frame_control); if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && @@ -1248,9 +1311,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #ifdef CONFIG_ACS case EVENT_ACS_CHANNEL_SELECTED: - hostapd_acs_channel_selected( - hapd, data->acs_selected_channels.pri_channel, - data->acs_selected_channels.sec_channel); + hostapd_acs_channel_selected(hapd, + &data->acs_selected_channels); break; #endif /* CONFIG_ACS */ default: diff --git a/contrib/wpa/src/ap/eap_user_db.c b/contrib/wpa/src/ap/eap_user_db.c index 559d77f9ef2..082d0f53175 100644 --- a/contrib/wpa/src/ap/eap_user_db.c +++ b/contrib/wpa/src/ap/eap_user_db.c @@ -138,8 +138,12 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, char id_str[256], cmd[300]; size_t i; - if (identity_len >= sizeof(id_str)) + if (identity_len >= sizeof(id_str)) { + wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d", + __func__, (int) identity_len, + (int) (sizeof(id_str))); return NULL; + } os_memcpy(id_str, identity, identity_len); id_str[identity_len] = '\0'; for (i = 0; i < identity_len; i++) { @@ -182,7 +186,9 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { - wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation"); + wpa_printf(MSG_DEBUG, + "DB: Failed to complete SQL operation: %s db: %s", + sqlite3_errmsg(db), hapd->conf->eap_user_sqlite); } else if (hapd->tmp_eap_user.next) user = &hapd->tmp_eap_user; @@ -192,8 +198,10 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { - wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL " - "operation"); + wpa_printf(MSG_DEBUG, + "DB: Failed to complete SQL operation: %s db: %s", + sqlite3_errmsg(db), + hapd->conf->eap_user_sqlite); } else if (hapd->tmp_eap_user.next) { user = &hapd->tmp_eap_user; os_free(user->identity); diff --git a/contrib/wpa/src/ap/hostapd.c b/contrib/wpa/src/ap/hostapd.c index 3e4e16b4f39..c09c17a4469 100644 --- a/contrib/wpa/src/ap/hostapd.c +++ b/contrib/wpa/src/ap/hostapd.c @@ -17,6 +17,7 @@ #include "eap_server/tncs.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" +#include "fst/fst.h" #include "hostapd.h" #include "authsrv.h" #include "sta_info.h" @@ -179,6 +180,7 @@ int hostapd_reload_config(struct hostapd_iface *iface) hapd = iface->bss[j]; hapd->iconf = newconf; hapd->iconf->channel = oldconf->channel; + hapd->iconf->acs = oldconf->acs; hapd->iconf->secondary_channel = oldconf->secondary_channel; hapd->iconf->ieee80211n = oldconf->ieee80211n; hapd->iconf->ieee80211ac = oldconf->ieee80211ac; @@ -259,6 +261,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) { os_free(hapd->probereq_cb); hapd->probereq_cb = NULL; + hapd->num_probereq_cb = 0; #ifdef CONFIG_P2P wpabuf_free(hapd->p2p_beacon_ie); @@ -353,6 +356,22 @@ static void hostapd_cleanup(struct hostapd_data *hapd) } +static void sta_track_deinit(struct hostapd_iface *iface) +{ + struct hostapd_sta_info *info; + + if (!iface->num_sta_seen) + return; + + while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info, + list))) { + dl_list_del(&info->list); + iface->num_sta_seen--; + os_free(info); + } +} + + static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); @@ -368,6 +387,7 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) os_free(iface->basic_rates); iface->basic_rates = NULL; ap_list_deinit(iface); + sta_track_deinit(iface); } @@ -861,7 +881,7 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) static int hostapd_setup_bss(struct hostapd_data *hapd, int first) { struct hostapd_bss_config *conf = hapd->conf; - u8 ssid[HOSTAPD_MAX_SSID_LEN + 1]; + u8 ssid[SSID_MAX_LEN + 1]; int ssid_len, set_ssid; char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; @@ -1363,6 +1383,132 @@ fail: } +#ifdef CONFIG_FST + +static const u8 * fst_hostapd_get_bssid_cb(void *ctx) +{ + struct hostapd_data *hapd = ctx; + + return hapd->own_addr; +} + + +static void fst_hostapd_get_channel_info_cb(void *ctx, + enum hostapd_hw_mode *hw_mode, + u8 *channel) +{ + struct hostapd_data *hapd = ctx; + + *hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel); +} + + +static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies) +{ + struct hostapd_data *hapd = ctx; + + if (hapd->iface->fst_ies != fst_ies) { + hapd->iface->fst_ies = fst_ies; + if (ieee802_11_set_beacon(hapd)) + wpa_printf(MSG_WARNING, "FST: Cannot set beacon"); + } +} + + +static int fst_hostapd_send_action_cb(void *ctx, const u8 *da, + struct wpabuf *buf) +{ + struct hostapd_data *hapd = ctx; + + return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da, + wpabuf_head(buf), wpabuf_len(buf)); +} + + +static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + + return sta ? sta->mb_ies : NULL; +} + + +static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr, + const u8 *buf, size_t size) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + + if (sta) { + struct mb_ies_info info; + + if (!mb_ies_info_by_ies(&info, buf, size)) { + wpabuf_free(sta->mb_ies); + sta->mb_ies = mb_ies_by_info(&info); + } + } +} + + +static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + struct sta_info *s = (struct sta_info *) *get_ctx; + + if (mb_only) { + for (; s && !s->mb_ies; s = s->next) + ; + } + + if (s) { + *get_ctx = (struct fst_get_peer_ctx *) s->next; + + return s->addr; + } + + *get_ctx = NULL; + return NULL; +} + + +static const u8 * fst_hostapd_get_peer_first(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + struct hostapd_data *hapd = ctx; + + *get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list; + + return fst_hostapd_get_sta(get_ctx, mb_only); +} + + +static const u8 * fst_hostapd_get_peer_next(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + return fst_hostapd_get_sta(get_ctx, mb_only); +} + + +void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, + struct fst_wpa_obj *iface_obj) +{ + iface_obj->ctx = hapd; + iface_obj->get_bssid = fst_hostapd_get_bssid_cb; + iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb; + iface_obj->set_ies = fst_hostapd_set_ies_cb; + iface_obj->send_action = fst_hostapd_send_action_cb; + iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb; + iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb; + iface_obj->get_peer_first = fst_hostapd_get_peer_first; + iface_obj->get_peer_next = fst_hostapd_get_peer_next; +} + +#endif /* CONFIG_FST */ + + /** * hostapd_setup_interface_complete - Complete interface setup * @@ -1495,6 +1641,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) hostapd_tx_queue_params(iface); ap_list_init(iface); + dl_list_init(&iface->sta_seen); hostapd_set_acl(hapd); @@ -1528,6 +1675,22 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) #ifdef NEED_AP_MLME dfs_offload: #endif /* NEED_AP_MLME */ + +#ifdef CONFIG_FST + if (hapd->iconf->fst_cfg.group_id[0]) { + struct fst_wpa_obj iface_obj; + + fst_hostapd_fill_iface_obj(hapd, &iface_obj); + iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr, + &iface_obj, &hapd->iconf->fst_cfg); + if (!iface->fst) { + wpa_printf(MSG_ERROR, "Could not attach to FST %s", + hapd->iconf->fst_cfg.group_id); + goto fail; + } + } +#endif /* CONFIG_FST */ + hostapd_set_state(iface, HAPD_IFACE_ENABLED); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); if (hapd->setup_complete_cb) @@ -1544,6 +1707,12 @@ fail: wpa_printf(MSG_ERROR, "Interface initialization failed"); hostapd_set_state(iface, HAPD_IFACE_DISABLED); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); +#ifdef CONFIG_FST + if (iface->fst) { + fst_detach(iface->fst); + iface->fst = NULL; + } +#endif /* CONFIG_FST */ if (iface->interfaces && iface->interfaces->terminate_on_error) eloop_terminate(); return -1; @@ -1643,6 +1812,13 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); iface->wait_channel_update = 0; +#ifdef CONFIG_FST + if (iface->fst) { + fst_detach(iface->fst); + iface->fst = NULL; + } +#endif /* CONFIG_FST */ + for (j = iface->num_bss - 1; j >= 0; j--) hostapd_bss_deinit(iface->bss[j]); } @@ -2029,7 +2205,7 @@ hostapd_iface_alloc(struct hapd_interfaces *interfaces) static struct hostapd_config * hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, - const char *ctrl_iface) + const char *ctrl_iface, const char *driver) { struct hostapd_bss_config *bss; struct hostapd_config *conf; @@ -2042,6 +2218,21 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, return NULL; } + if (driver) { + int j; + + for (j = 0; wpa_drivers[j]; j++) { + if (os_strcmp(driver, wpa_drivers[j]->name) == 0) { + conf->driver = wpa_drivers[j]; + goto skip; + } + } + + wpa_printf(MSG_ERROR, + "Invalid/unknown driver '%s' - registering the default driver", + driver); + } + conf->driver = wpa_drivers[0]; if (conf->driver == NULL) { wpa_printf(MSG_ERROR, "No driver wrappers registered!"); @@ -2049,6 +2240,7 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, return NULL; } +skip: bss = conf->last_bss = conf->bss[0]; os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); @@ -2209,8 +2401,14 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) if (conf && conf->bss) os_strlcpy(conf->bss[0]->iface, buf, sizeof(conf->bss[0]->iface)); - } else - conf = hostapd_config_alloc(interfaces, buf, ptr); + } else { + char *driver = os_strchr(ptr, ' '); + + if (driver) + *driver++ = '\0'; + conf = hostapd_config_alloc(interfaces, buf, ptr, driver); + } + if (conf == NULL || conf->bss == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " "for configuration", __func__); @@ -2722,4 +2920,43 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, hostapd_enable_iface(iface); } + +struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, + const char *ifname) +{ + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_iface *iface = interfaces->iface[i]; + + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + + if (os_strcmp(ifname, hapd->conf->iface) == 0) + return hapd; + } + } + + return NULL; +} + #endif /* NEED_AP_MLME */ + + +void hostapd_periodic_iface(struct hostapd_iface *iface) +{ + size_t i; + + ap_list_timer(iface); + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *hapd = iface->bss[i]; + + if (!hapd->started) + continue; + +#ifndef CONFIG_NO_RADIUS + hostapd_acl_expire(hapd); +#endif /* CONFIG_NO_RADIUS */ + } +} diff --git a/contrib/wpa/src/ap/hostapd.h b/contrib/wpa/src/ap/hostapd.h index 75cc24edb66..dcf51f00f78 100644 --- a/contrib/wpa/src/ap/hostapd.h +++ b/contrib/wpa/src/ap/hostapd.h @@ -41,6 +41,7 @@ struct hapd_interfaces { size_t count; int global_ctrl_sock; + struct wpa_ctrl_dst *global_ctrl_dst; char *global_iface_path; char *global_iface_name; #ifndef CONFIG_NATIVE_WINDOWS @@ -49,6 +50,9 @@ struct hapd_interfaces { struct hostapd_iface **iface; size_t terminate_on_error; +#ifndef CONFIG_NO_VLAN + struct dynamic_iface *vlan_priv; +#endif /* CONFIG_NO_VLAN */ }; enum hostapd_chan_status { @@ -265,6 +269,7 @@ struct hostapd_data { /** Key used for generating SAE anti-clogging tokens */ u8 sae_token_key[8]; struct os_reltime last_sae_token_key_update; + int dot11RSNASAERetransPeriod; /* msec */ #endif /* CONFIG_SAE */ #ifdef CONFIG_TESTING_OPTIONS @@ -276,6 +281,12 @@ struct hostapd_data { }; +struct hostapd_sta_info { + struct dl_list list; + u8 addr[ETH_ALEN]; + struct os_reltime last_seen; +}; + /** * struct hostapd_iface - hostapd per-interface data structure */ @@ -305,6 +316,10 @@ struct hostapd_iface { unsigned int wait_channel_update:1; unsigned int cac_started:1; +#ifdef CONFIG_FST + struct fst_iface *fst; + const struct wpabuf *fst_ies; +#endif /* CONFIG_FST */ /* * When set, indicates that the driver will handle the AP @@ -400,6 +415,9 @@ struct hostapd_iface { void (*scan_cb)(struct hostapd_iface *iface); int num_ht40_scan_tries; + + struct dl_list sta_seen; /* struct hostapd_sta_info */ + unsigned int num_sta_seen; }; /* hostapd.c */ @@ -437,6 +455,7 @@ void hostapd_switch_channel_fallback(struct hostapd_iface *iface, const struct hostapd_freq_params *freq_params); void hostapd_cleanup_cs_params(struct hostapd_data *hapd); +void hostapd_periodic_iface(struct hostapd_iface *iface); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, @@ -464,4 +483,12 @@ const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, size_t identity_len, int phase2); +struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, + const char *ifname); + +#ifdef CONFIG_FST +void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, + struct fst_wpa_obj *iface_obj); +#endif /* CONFIG_FST */ + #endif /* HOSTAPD_H */ diff --git a/contrib/wpa/src/ap/hw_features.c b/contrib/wpa/src/ap/hw_features.c index 05431d32c20..fc8786dc311 100644 --- a/contrib/wpa/src/ap/hw_features.c +++ b/contrib/wpa/src/ap/hw_features.c @@ -260,8 +260,14 @@ static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan); - if (res == 2) - ieee80211n_switch_pri_sec(iface); + if (res == 2) { + if (iface->conf->no_pri_sec_switch) { + wpa_printf(MSG_DEBUG, + "Cannot switch PRI/SEC channels due to local constraint"); + } else { + ieee80211n_switch_pri_sec(iface); + } + } return !!res; } @@ -347,8 +353,13 @@ static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, sec_freq = pri_freq + 20; else sec_freq = pri_freq - 20; - affected_start = (pri_freq + sec_freq) / 2 - 25; - affected_end = (pri_freq + sec_freq) / 2 + 25; + /* + * Note: Need to find the PRI channel also in cases where the affected + * channel is the SEC channel of a 40 MHz BSS, so need to include the + * scanning coverage here to be 40 MHz from the center frequency. + */ + affected_start = (pri_freq + sec_freq) / 2 - 40; + affected_end = (pri_freq + sec_freq) / 2 + 40; wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", affected_start, affected_end); @@ -510,7 +521,11 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) return 0; } - if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + /* + * Driver ACS chosen channel may not be HT40 due to internal driver + * restrictions. + */ + if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { wpa_printf(MSG_ERROR, "Driver does not support configured " "HT capability [HT40*]"); @@ -717,6 +732,15 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) int ret; if (!iface->conf->ieee80211n) return 0; + + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B && + iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G && + (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) { + wpa_printf(MSG_DEBUG, + "Disable HT capability [DSSS_CCK-40] on 5 GHz band"); + iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ; + } + if (!ieee80211n_supported_ht_capab(iface)) return -1; #ifdef CONFIG_IEEE80211AC @@ -740,6 +764,9 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, int i; struct hostapd_channel_data *chan; + if (!iface->current_mode) + return 0; + for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; if (chan->chan != channel) @@ -801,6 +828,12 @@ hostapd_check_chans(struct hostapd_iface *iface) static void hostapd_notify_bad_chans(struct hostapd_iface *iface) { + if (!iface->current_mode) { + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured mode"); + return; + } hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, @@ -891,14 +924,18 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) } if (iface->current_mode == NULL) { - wpa_printf(MSG_ERROR, "Hardware does not support configured " - "mode"); - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Hardware does not support configured mode " - "(%d) (hw_mode in hostapd.conf)", - (int) iface->conf->hw_mode); - return -2; + if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) || + !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) + { + wpa_printf(MSG_ERROR, + "Hardware does not support configured mode"); + hostapd_logger(iface->bss[0], NULL, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)", + (int) iface->conf->hw_mode); + return -2; + } } switch (hostapd_check_chans(iface)) { diff --git a/contrib/wpa/src/ap/hw_features.h b/contrib/wpa/src/ap/hw_features.h index 0f67ab8e5f3..ca7f22ba205 100644 --- a/contrib/wpa/src/ap/hw_features.h +++ b/contrib/wpa/src/ap/hw_features.h @@ -36,6 +36,11 @@ static inline int hostapd_get_hw_features(struct hostapd_iface *iface) return -1; } +static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err) +{ + return -1; +} + static inline int hostapd_select_hw_mode(struct hostapd_iface *iface) { return -100; diff --git a/contrib/wpa/src/ap/ieee802_11.c b/contrib/wpa/src/ap/ieee802_11.c index 89911b1fdd4..7bb18c01d1a 100644 --- a/contrib/wpa/src/ap/ieee802_11.c +++ b/contrib/wpa/src/ap/ieee802_11.c @@ -23,6 +23,7 @@ #include "radius/radius_client.h" #include "p2p/p2p.h" #include "wps/wps.h" +#include "fst/fst.h" #include "hostapd.h" #include "beacon.h" #include "ieee802_11_auth.h" @@ -38,6 +39,7 @@ #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "wnm_ap.h" +#include "hw_features.h" #include "ieee802_11.h" #include "dfs.h" @@ -132,8 +134,7 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) } -u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, - int probe) +u16 hostapd_own_capab_info(struct hostapd_data *hapd) { int capab = WLAN_CAPABILITY_ESS; int privacy; @@ -166,20 +167,6 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, privacy = 1; #endif /* CONFIG_HS20 */ - if (sta) { - int policy, def_klen; - if (probe && sta->ssid_probe) { - policy = sta->ssid_probe->security_policy; - def_klen = sta->ssid_probe->wep.default_len; - } else { - policy = sta->ssid->security_policy; - def_klen = sta->ssid->wep.default_len; - } - privacy = policy != SECURITY_PLAINTEXT; - if (policy == SECURITY_IEEE_802_1X && def_klen == 0) - privacy = 0; - } - if (privacy) capab |= WLAN_CAPABILITY_PRIVACY; @@ -206,6 +193,7 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, } +#ifndef CONFIG_NO_RC4 static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u16 auth_transaction, const u8 *challenge, int iswep) @@ -259,6 +247,7 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, return 0; } +#endif /* CONFIG_NO_RC4 */ static void send_auth_reply(struct hostapd_data *hapd, @@ -328,7 +317,6 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, #ifdef CONFIG_SAE -#define dot11RSNASAERetransPeriod 40 /* msec */ #define dot11RSNASAESync 5 /* attempts */ @@ -511,12 +499,14 @@ static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) switch (sta->sae->state) { case SAE_COMMITTED: ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0); - eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + eloop_register_timeout(0, + hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); break; case SAE_CONFIRMED: ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr); - eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + eloop_register_timeout(0, + hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); break; default: @@ -542,7 +532,7 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd, return; eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta); - eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000, auth_sae_retransmit_timer, hapd, sta); } @@ -624,7 +614,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; sta->sae->sync++; - ret = auth_sae_send_commit(hapd, sta, bssid, 1); + ret = auth_sae_send_commit(hapd, sta, bssid, 0); if (ret) return ret; @@ -784,6 +774,12 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, &token_len, hapd->conf->sae_groups); + if (resp == SAE_SILENTLY_DISCARD) { + wpa_printf(MSG_DEBUG, + "SAE: Drop commit message from " MACSTR " due to reflection attack", + MAC2STR(sta->addr)); + return; + } if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " @@ -934,6 +930,16 @@ static void handle_auth(struct hostapd_data *hapd, challenge ? " challenge" : "", seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); +#ifdef CONFIG_NO_RC4 + if (auth_alg == WLAN_AUTH_SHARED_KEY) { + wpa_printf(MSG_INFO, + "Unsupported authentication algorithm (%d)", + auth_alg); + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } +#endif /* CONFIG_NO_RC4 */ + if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; goto fail; @@ -972,6 +978,61 @@ static void handle_auth(struct hostapd_data *hapd, goto fail; } + if (hapd->conf->no_auth_if_seen_on) { + struct hostapd_data *other; + + other = sta_track_seen_on(hapd->iface, mgmt->sa, + hapd->conf->no_auth_if_seen_on); + if (other) { + u8 *pos; + u32 info; + u8 op_class, channel, phytype; + + wpa_printf(MSG_DEBUG, "%s: Reject authentication from " + MACSTR " since STA has been seen on %s", + hapd->conf->iface, MAC2STR(mgmt->sa), + hapd->conf->no_auth_if_seen_on); + + resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION; + pos = &resp_ies[0]; + *pos++ = WLAN_EID_NEIGHBOR_REPORT; + *pos++ = 13; + os_memcpy(pos, other->own_addr, ETH_ALEN); + pos += ETH_ALEN; + info = 0; /* TODO: BSSID Information */ + WPA_PUT_LE32(pos, info); + pos += 4; + if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) + phytype = 8; /* dmg */ + else if (other->iconf->ieee80211ac) + phytype = 9; /* vht */ + else if (other->iconf->ieee80211n) + phytype = 7; /* ht */ + else if (other->iconf->hw_mode == + HOSTAPD_MODE_IEEE80211A) + phytype = 4; /* ofdm */ + else if (other->iconf->hw_mode == + HOSTAPD_MODE_IEEE80211G) + phytype = 6; /* erp */ + else + phytype = 5; /* hrdsss */ + if (ieee80211_freq_to_channel_ext( + hostapd_hw_get_freq(other, + other->iconf->channel), + other->iconf->secondary_channel, + other->iconf->ieee80211ac, + &op_class, &channel) == NUM_HOSTAPD_MODES) { + op_class = 0; + channel = other->iconf->channel; + } + *pos++ = op_class; + *pos++ = channel; + *pos++ = phytype; + resp_ies_len = pos - &resp_ies[0]; + goto fail; + } + } + res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, &session_timeout, &acct_interim_interval, &vlan_id, @@ -1081,6 +1142,7 @@ static void handle_auth(struct hostapd_data *hapd, sta->auth_alg = WLAN_AUTH_OPEN; mlme_authenticate_indication(hapd, sta); break; +#ifndef CONFIG_NO_RC4 case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, fc & WLAN_FC_ISWEP); @@ -1094,6 +1156,7 @@ static void handle_auth(struct hostapd_data *hapd, resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; } break; +#endif /* CONFIG_NO_RC4 */ #ifdef CONFIG_IEEE80211R case WLAN_AUTH_FT: sta->auth_alg = WLAN_AUTH_FT; @@ -1297,8 +1360,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211N - resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities, - elems.ht_capabilities_len); + resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) return resp; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && @@ -1311,14 +1373,15 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC - resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities, - elems.vht_capabilities_len); - if (resp != WLAN_STATUS_SUCCESS) - return resp; + if (hapd->iconf->ieee80211ac) { + resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities); + if (resp != WLAN_STATUS_SUCCESS) + return resp; - resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); - if (resp != WLAN_STATUS_SUCCESS) - return resp; + resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && !(sta->flags & WLAN_STA_VHT)) { @@ -1546,6 +1609,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, sta->hs20_ie = NULL; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + wpabuf_free(sta->mb_ies); + if (hapd->iface->fst) + sta->mb_ies = mb_ies_by_info(&elems.mb_ies); + else + sta->mb_ies = NULL; +#endif /* CONFIG_FST */ + return WLAN_STATUS_SUCCESS; } @@ -1594,7 +1665,7 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, send_len = IEEE80211_HDRLEN; send_len += sizeof(reply->u.assoc_resp); reply->u.assoc_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd, sta, 0)); + host_to_le16(hostapd_own_capab_info(hapd)); reply->u.assoc_resp.status_code = host_to_le16(status_code); reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15)); /* Supported rates */ @@ -1634,6 +1705,14 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, if (sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + os_memcpy(p, wpabuf_head(hapd->iface->fst_ies), + wpabuf_len(hapd->iface->fst_ies)); + p += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + #ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) p = hostapd_eid_vendor_vht(hapd, p); @@ -2112,10 +2191,20 @@ static int handle_action(struct hostapd_data *hapd, ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); return 1; #endif /* CONFIG_WNM */ +#ifdef CONFIG_FST + case WLAN_ACTION_FST: + if (hapd->iface->fst) + fst_rx_action(hapd->iface->fst, mgmt, len); + else + wpa_printf(MSG_DEBUG, + "FST: Ignore FST Action frame - no FST attached"); + return 1; +#endif /* CONFIG_FST */ case WLAN_ACTION_PUBLIC: case WLAN_ACTION_PROTECTED_DUAL: #ifdef CONFIG_IEEE80211N - if (mgmt->u.action.u.public_action.action == + if (len >= IEEE80211_HDRLEN + 2 && + mgmt->u.action.u.public_action.action == WLAN_PA_20_40_BSS_COEX) { wpa_printf(MSG_DEBUG, "HT20/40 coex mgmt frame received from STA " @@ -2248,6 +2337,9 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, return 0; } + if (hapd->iconf->track_sta_max_num) + sta_track_add(hapd->iface, mgmt->sa); + switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); @@ -2335,7 +2427,7 @@ static void hostapd_set_wds_encryption(struct hostapd_data *hapd, char *ifname_wds) { int i; - struct hostapd_ssid *ssid = sta->ssid; + struct hostapd_ssid *ssid = &hapd->conf->ssid; if (hapd->conf->ieee802_1x || hapd->conf->wpa) return; @@ -2473,11 +2565,11 @@ static void handle_assoc_cb(struct hostapd_data *hapd, * so bind it to the selected VLAN interface now, since the * interface selection is not going to change anymore. */ - if (ap_sta_bind_vlan(hapd, sta, 0) < 0) + if (ap_sta_bind_vlan(hapd, sta) < 0) return; } else if (sta->vlan_id) { /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ - if (ap_sta_bind_vlan(hapd, sta, 0) < 0) + if (ap_sta_bind_vlan(hapd, sta) < 0) return; } diff --git a/contrib/wpa/src/ap/ieee802_11.h b/contrib/wpa/src/ap/ieee802_11.h index 41c27d906e7..44c1bff364a 100644 --- a/contrib/wpa/src/ap/ieee802_11.h +++ b/contrib/wpa/src/ap/ieee802_11.h @@ -14,6 +14,7 @@ struct hostapd_data; struct sta_info; struct hostapd_frame_info; struct ieee80211_ht_capabilities; +struct ieee80211_vht_capabilities; struct ieee80211_mgmt; int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, @@ -40,8 +41,7 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, return 0; } #endif /* NEED_AP_MLME */ -u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, - int probe); +u16 hostapd_own_capab_info(struct hostapd_data *hapd); void ap_ht2040_timeout(void *eloop_data, void *user_data); u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid); @@ -62,7 +62,7 @@ void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *ht_capab, size_t ht_capab_len); + const u8 *ht_capab); u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ie, size_t len); @@ -70,7 +70,7 @@ void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta); void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta); u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *vht_capab, size_t vht_capab_len); + const u8 *vht_capab); u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_opmode); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, diff --git a/contrib/wpa/src/ap/ieee802_11_auth.c b/contrib/wpa/src/ap/ieee802_11_auth.c index 56c3ce0313d..531a67da412 100644 --- a/contrib/wpa/src/ap/ieee802_11_auth.c +++ b/contrib/wpa/src/ap/ieee802_11_auth.c @@ -399,19 +399,15 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, /** * hostapd_acl_expire - ACL cache expiration callback - * @eloop_ctx: struct hostapd_data * - * @timeout_ctx: Not used + * @hapd: struct hostapd_data * */ -static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) +void hostapd_acl_expire(struct hostapd_data *hapd) { - struct hostapd_data *hapd = eloop_ctx; struct os_reltime now; os_get_reltime(&now); hostapd_acl_expire_cache(hapd, &now); hostapd_acl_expire_queries(hapd, &now); - - eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); } @@ -561,6 +557,19 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && !cache->psk) cache->accepted = HOSTAPD_ACL_REJECT; + + if (cache->vlan_id && + !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) { + hostapd_logger(hapd, query->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN ID %d received from RADIUS server", + cache->vlan_id); + cache->vlan_id = 0; + } + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && + !cache->vlan_id) + cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; cache->next = hapd->acl_cache; @@ -602,8 +611,6 @@ int hostapd_acl_init(struct hostapd_data *hapd) if (radius_client_register(hapd->radius, RADIUS_AUTH, hostapd_acl_recv_radius, hapd)) return -1; - - eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); #endif /* CONFIG_NO_RADIUS */ return 0; @@ -619,8 +626,6 @@ void hostapd_acl_deinit(struct hostapd_data *hapd) struct hostapd_acl_query_data *query, *prev; #ifndef CONFIG_NO_RADIUS - eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL); - hostapd_acl_cache_free(hapd->acl_cache); #endif /* CONFIG_NO_RADIUS */ diff --git a/contrib/wpa/src/ap/ieee802_11_auth.h b/contrib/wpa/src/ap/ieee802_11_auth.h index 2bc1065a226..b66f244b3eb 100644 --- a/contrib/wpa/src/ap/ieee802_11_auth.h +++ b/contrib/wpa/src/ap/ieee802_11_auth.h @@ -24,5 +24,6 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, int hostapd_acl_init(struct hostapd_data *hapd); void hostapd_acl_deinit(struct hostapd_data *hapd); void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); +void hostapd_acl_expire(struct hostapd_data *hapd); #endif /* IEEE802_11_AUTH_H */ diff --git a/contrib/wpa/src/ap/ieee802_11_ht.c b/contrib/wpa/src/ap/ieee802_11_ht.c index 4b0653de95d..11fde2a2639 100644 --- a/contrib/wpa/src/ap/ieee802_11_ht.c +++ b/contrib/wpa/src/ap/ieee802_11_ht.c @@ -209,7 +209,7 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, struct hostapd_iface *iface = hapd->iface; struct ieee80211_2040_bss_coex_ie *bc_ie; struct ieee80211_2040_intol_chan_report *ic_report; - int is_ht_allowed = 1; + int is_ht40_allowed = 1; int i; const u8 *start = (const u8 *) mgmt; const u8 *data = start + IEEE80211_HDRLEN + 2; @@ -242,7 +242,7 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "20 MHz BSS width request bit is set in BSS coexistence information field"); - is_ht_allowed = 0; + is_ht40_allowed = 0; } if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { @@ -250,7 +250,7 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "40 MHz intolerant bit is set in BSS coexistence information field"); - is_ht_allowed = 0; + is_ht40_allowed = 0; } if (start + len - data >= 3 && @@ -276,13 +276,13 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "20_40_INTOLERANT channel %d reported", chan); - is_ht_allowed = 0; + is_ht40_allowed = 0; } } - wpa_printf(MSG_DEBUG, "is_ht_allowed=%d num_sta_ht40_intolerant=%d", - is_ht_allowed, iface->num_sta_ht40_intolerant); + wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", + is_ht40_allowed, iface->num_sta_ht40_intolerant); - if (!is_ht_allowed && + if (!is_ht40_allowed && (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { if (iface->conf->secondary_channel) { hostapd_logger(hapd, mgmt->sa, @@ -310,12 +310,15 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *ht_capab, size_t ht_capab_len) + const u8 *ht_capab) { - /* Disable HT caps for STAs associated to no-HT BSSes. */ + /* + * Disable HT caps for STAs associated to no-HT BSSes, or for stations + * that did not specify a valid WMM IE in the (Re)Association Request + * frame. + */ if (!ht_capab || - ht_capab_len < sizeof(struct ieee80211_ht_capabilities) || - hapd->conf->disable_11n) { + !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) { sta->flags &= ~WLAN_STA_HT; os_free(sta->ht_capabilities); sta->ht_capabilities = NULL; diff --git a/contrib/wpa/src/ap/ieee802_11_vht.c b/contrib/wpa/src/ap/ieee802_11_vht.c index 171538ad74a..5bf1b5d7200 100644 --- a/contrib/wpa/src/ap/ieee802_11_vht.c +++ b/contrib/wpa/src/ap/ieee802_11_vht.c @@ -132,11 +132,10 @@ static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *vht_capab, size_t vht_capab_len) + const u8 *vht_capab) { /* Disable VHT caps for STAs associated to no-VHT BSSes. */ if (!vht_capab || - vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || hapd->conf->disable_11ac || !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) { sta->flags &= ~WLAN_STA_VHT; diff --git a/contrib/wpa/src/ap/ieee802_1x.c b/contrib/wpa/src/ap/ieee802_1x.c index 79dc0f95748..0f2d428cf75 100644 --- a/contrib/wpa/src/ap/ieee802_1x.c +++ b/contrib/wpa/src/ap/ieee802_1x.c @@ -125,6 +125,9 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, } +#ifndef CONFIG_FIPS +#ifndef CONFIG_NO_RC4 + static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, struct sta_info *sta, int idx, int broadcast, @@ -204,7 +207,7 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, } -void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) +static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_authenticator *eapol = hapd->eapol_auth; struct eapol_state_machine *sm = sta->eapol_sm; @@ -259,6 +262,9 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) } } +#endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_FIPS */ + const char *radius_mode_txt(struct hostapd_data *hapd) { @@ -346,7 +352,8 @@ static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd, return -1; } - suite = wpa_cipher_to_suite((hapd->conf->wpa & 0x2) ? + suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) || + hapd->conf->osen) ? WPA_PROTO_RSN : WPA_PROTO_WPA, hapd->conf->wpa_group); if (!hostapd_config_get_radius_attr(req_attr, @@ -453,7 +460,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211R */ - if (hapd->conf->wpa && sta->wpa_sm && + if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm && add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) return -1; @@ -599,7 +606,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } - if (eap && !radius_msg_add_eap(msg, eap, len)) { + if (!radius_msg_add_eap(msg, eap, len)) { wpa_printf(MSG_INFO, "Could not add EAP-Message"); goto fail; } @@ -1108,8 +1115,6 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { - int old_vlanid; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); @@ -1123,11 +1128,8 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->authFail = FALSE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); - old_vlanid = sta->vlan_id; pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); - if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) - sta->vlan_id = 0; - ap_sta_bind_vlan(hapd, sta, old_vlanid); + ap_sta_bind_vlan(hapd, sta); } else { if (reassoc) { /* @@ -1290,7 +1292,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, struct sta_info *sta, struct radius_msg *msg) { - u8 *class; + u8 *attr_class; size_t class_len; struct eapol_state_machine *sm = sta->eapol_sm; int count, i; @@ -1312,12 +1314,12 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, nclass_count = 0; - class = NULL; + attr_class = NULL; for (i = 0; i < count; i++) { do { if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, - &class, &class_len, - class) < 0) { + &attr_class, &class_len, + attr_class) < 0) { i = count; break; } @@ -1327,7 +1329,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, if (nclass[nclass_count].data == NULL) break; - os_memcpy(nclass[nclass_count].data, class, class_len); + os_memcpy(nclass[nclass_count].data, attr_class, class_len); nclass[nclass_count].len = class_len; nclass_count++; } @@ -1590,7 +1592,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct hostapd_data *hapd = data; struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; - int session_timeout_set, old_vlanid = 0; + int session_timeout_set, vlan_id = 0; struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); @@ -1657,20 +1659,27 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: - if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) - sta->vlan_id = 0; + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) + vlan_id = 0; #ifndef CONFIG_NO_VLAN - else { - old_vlanid = sta->vlan_id; - sta->vlan_id = radius_msg_get_vlanid(msg); - } - if (sta->vlan_id > 0 && - hostapd_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) { + else + vlan_id = radius_msg_get_vlanid(msg); + if (vlan_id > 0 && + hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "VLAN ID %d", sta->vlan_id); - } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) { + "VLAN ID %d", vlan_id); + } else if (vlan_id > 0) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN ID %d received from RADIUS server", + vlan_id); + break; + } else if (hapd->conf->ssid.dynamic_vlan == + DYNAMIC_VLAN_REQUIRED) { sta->eapol_sm->authFail = TRUE; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -1681,7 +1690,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, } #endif /* CONFIG_NO_VLAN */ - if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0) + sta->vlan_id = vlan_id; + if ((sta->flags & WLAN_STA_ASSOC) && + ap_sta_bind_vlan(hapd, sta) < 0) break; sta->session_timeout_set = !!session_timeout_set; @@ -1926,10 +1937,11 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, struct hostapd_data *hapd = ctx; const struct hostapd_eap_user *eap_user; int i; + int rv = -1; eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2); if (eap_user == NULL) - return -1; + goto out; os_memset(user, 0, sizeof(*user)); user->phase2 = phase2; @@ -1941,7 +1953,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, if (eap_user->password) { user->password = os_malloc(eap_user->password_len); if (user->password == NULL) - return -1; + goto out; os_memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; @@ -1951,8 +1963,13 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, user->macacl = eap_user->macacl; user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; + rv = 0; - return 0; +out: + if (rv) + wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__); + + return rv; } @@ -2012,9 +2029,13 @@ static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx) static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx) { +#ifndef CONFIG_FIPS +#ifndef CONFIG_NO_RC4 struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; ieee802_1x_tx_key(hapd, sta); +#endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_FIPS */ } @@ -2085,6 +2106,7 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start; conf.erp_domain = hapd->conf->erp_domain; conf.erp = hapd->conf->eap_server_erp; + conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; @@ -2332,9 +2354,9 @@ void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth) } -static const char * bool_txt(Boolean bool) +static const char * bool_txt(Boolean val) { - return bool ? "TRUE" : "FALSE"; + return val ? "TRUE" : "FALSE"; } diff --git a/contrib/wpa/src/ap/ieee802_1x.h b/contrib/wpa/src/ap/ieee802_1x.h index de6e0e75fac..14d69556993 100644 --- a/contrib/wpa/src/ap/ieee802_1x.h +++ b/contrib/wpa/src/ap/ieee802_1x.h @@ -23,7 +23,6 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_free_station(struct sta_info *sta); -void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); diff --git a/contrib/wpa/src/ap/ndisc_snoop.c b/contrib/wpa/src/ap/ndisc_snoop.c index b0d42dcd82c..4a87721e2ec 100644 --- a/contrib/wpa/src/ap/ndisc_snoop.c +++ b/contrib/wpa/src/ap/ndisc_snoop.c @@ -81,12 +81,24 @@ static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr) } +static void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + continue; + x_snoop_mcast_to_ucast_convert_send(hapd, sta, (u8 *) buf, len); + } +} + + static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct hostapd_data *hapd = ctx; struct icmpv6_ndmsg *msg; - struct in6_addr *saddr; + struct in6_addr saddr; struct sta_info *sta; int res; char addrtxt[INET6_ADDRSTRLEN + 1]; @@ -101,25 +113,30 @@ static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, if (msg->opt_type != SOURCE_LL_ADDR) return; - saddr = &msg->ipv6h.ip6_src; - if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 && - saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) { + /* + * IPv6 header may not be 32-bit aligned in the buffer, so use + * a local copy to avoid unaligned reads. + */ + os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr)); + if (!(saddr.s6_addr32[0] == 0 && saddr.s6_addr32[1] == 0 && + saddr.s6_addr32[2] == 0 && saddr.s6_addr32[3] == 0)) { if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN) return; sta = ap_get_sta(hapd, msg->opt_lladdr); if (!sta) return; - if (sta_has_ip6addr(sta, saddr)) + if (sta_has_ip6addr(sta, &saddr)) return; - if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt)) - == NULL) + if (inet_ntop(AF_INET6, &saddr, addrtxt, + sizeof(addrtxt)) == NULL) addrtxt[0] = '\0'; wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for " MACSTR, addrtxt, MAC2STR(sta->addr)); - hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr); - res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr, + hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &saddr); + res = hostapd_drv_br_add_ip_neigh(hapd, 6, + (u8 *) &saddr, 128, sta->addr); if (res) { wpa_printf(MSG_ERROR, @@ -128,21 +145,17 @@ static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, return; } - if (sta_ip6addr_add(sta, saddr)) + if (sta_ip6addr_add(sta, &saddr)) return; } break; case ROUTER_ADVERTISEMENT: - if (!hapd->conf->disable_dgaf) - return; - /* fall through */ + if (hapd->conf->disable_dgaf) + ucast_to_stas(hapd, buf, len); + break; case NEIGHBOR_ADVERTISEMENT: - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (!(sta->flags & WLAN_STA_AUTHORIZED)) - continue; - x_snoop_mcast_to_ucast_convert_send(hapd, sta, - (u8 *) buf, len); - } + if (hapd->conf->na_mcast_to_ucast) + ucast_to_stas(hapd, buf, len); break; default: break; diff --git a/contrib/wpa/src/ap/sta_info.c b/contrib/wpa/src/ap/sta_info.c index 7e75e1a61e1..d64307ccfd0 100644 --- a/contrib/wpa/src/ap/sta_info.c +++ b/contrib/wpa/src/ap/sta_info.c @@ -16,6 +16,7 @@ #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" +#include "fst/fst.h" #include "hostapd.h" #include "accounting.h" #include "ieee802_1x.h" @@ -171,6 +172,19 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) !(sta->flags & WLAN_STA_PREAUTH)) hostapd_drv_sta_remove(hapd, sta->addr); +#ifndef CONFIG_NO_VLAN + if (sta->vlan_id_bound) { + /* + * Need to remove the STA entry before potentially removing the + * VLAN. + */ + if (hapd->iface->driver_ap_teardown && + !(sta->flags & WLAN_STA_PREAUTH)) + hostapd_drv_sta_remove(hapd, sta->addr); + vlan_remove_dynamic(hapd, sta->vlan_id_bound); + } +#endif /* CONFIG_NO_VLAN */ + ap_sta_hash_del(hapd, sta); ap_sta_list_del(hapd, sta); @@ -283,6 +297,9 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) wpabuf_free(sta->wps_ie); wpabuf_free(sta->p2p_ie); wpabuf_free(sta->hs20_ie); +#ifdef CONFIG_FST + wpabuf_free(sta->mb_ies); +#endif /* CONFIG_FST */ os_free(sta->ht_capabilities); os_free(sta->vht_capabilities); @@ -619,7 +636,6 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) hapd->sta_list = sta; hapd->num_sta++; ap_sta_hash_add(hapd, sta); - sta->ssid = &hapd->conf->ssid; ap_sta_remove_in_other_bss(hapd, sta); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; dl_list_init(&sta->ip6addr); @@ -768,26 +784,19 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd, #endif /* CONFIG_WPS */ -int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, - int old_vlanid) +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) { #ifndef CONFIG_NO_VLAN const char *iface; struct hostapd_vlan *vlan = NULL; int ret; - - /* - * Do not proceed furthur if the vlan id remains same. We do not want - * duplicate dynamic vlan entries. - */ - if (sta->vlan_id == old_vlanid) - return 0; + int old_vlanid = sta->vlan_id_bound; iface = hapd->conf->iface; - if (sta->ssid->vlan[0]) - iface = sta->ssid->vlan; + if (hapd->conf->ssid.vlan[0]) + iface = hapd->conf->ssid.vlan; - if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) sta->vlan_id = 0; else if (sta->vlan_id > 0) { struct hostapd_vlan *wildcard_vlan = NULL; @@ -805,6 +814,14 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, iface = vlan->ifname; } + /* + * Do not increment ref counters if the VLAN ID remains same, but do + * not skip hostapd_drv_set_sta_vlan() as hostapd_drv_sta_remove() might + * have been called before. + */ + if (sta->vlan_id == old_vlanid) + goto skip_counting; + if (sta->vlan_id > 0 && vlan == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " @@ -825,7 +842,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, } iface = vlan->ifname; - if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + if (vlan_setup_encryption_dyn(hapd, iface) != 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not " @@ -838,7 +855,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " "interface '%s'", iface); } else if (vlan && vlan->vlan_id == sta->vlan_id) { - if (sta->vlan_id > 0) { + if (vlan->dynamic_vlan > 0) { vlan->dynamic_vlan++; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -852,7 +869,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, * configuration for the case where hostapd did not yet know * which keys are to be used when the interface was added. */ - if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + if (vlan_setup_encryption_dyn(hapd, iface) != 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not " @@ -862,6 +879,10 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, } } + /* ref counters have been increased, so mark the station */ + sta->vlan_id_bound = sta->vlan_id; + +skip_counting: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "binding station to interface " "'%s'", iface); @@ -876,10 +897,10 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, "entry to vlan_id=%d", sta->vlan_id); } -done: /* During 1x reauth, if the vlan id changes, then remove the old id. */ - if (old_vlanid > 0) + if (old_vlanid > 0 && old_vlanid != sta->vlan_id) vlan_remove_dynamic(hapd, old_vlanid); +done: return ret; #else /* CONFIG_NO_VLAN */ @@ -1043,6 +1064,16 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); } + +#ifdef CONFIG_FST + if (hapd->iface->fst) { + if (authorized) + fst_notify_peer_connected(hapd->iface->fst, sta->addr); + else + fst_notify_peer_disconnected(hapd->iface->fst, + sta->addr); + } +#endif /* CONFIG_FST */ } diff --git a/contrib/wpa/src/ap/sta_info.h b/contrib/wpa/src/ap/sta_info.h index 57551ab17d5..420d64e5793 100644 --- a/contrib/wpa/src/ap/sta_info.h +++ b/contrib/wpa/src/ap/sta_info.h @@ -117,10 +117,8 @@ struct sta_info { struct wpa_state_machine *wpa_sm; struct rsn_preauth_interface *preauth_iface; - struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */ - struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ - - int vlan_id; + int vlan_id; /* 0: none, >0: VID */ + int vlan_id_bound; /* updated by ap_sta_bind_vlan() */ /* PSKs from RADIUS authentication server */ struct hostapd_sta_wpa_psk_short *psk; @@ -155,6 +153,9 @@ struct sta_info { struct wpabuf *hs20_deauth_req; char *hs20_session_info_url; int hs20_disassoc_timer; +#ifdef CONFIG_FST + struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */ +#endif /* CONFIG_FST */ struct os_reltime connected_time; @@ -218,8 +219,7 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, int ap_sta_wps_cancel(struct hostapd_data *hapd, struct sta_info *sta, void *ctx); #endif /* CONFIG_WPS */ -int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, - int old_vlanid); +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); diff --git a/contrib/wpa/src/ap/utils.c b/contrib/wpa/src/ap/utils.c index 931968c84b0..fcb371bec28 100644 --- a/contrib/wpa/src/ap/utils.c +++ b/contrib/wpa/src/ap/utils.c @@ -10,6 +10,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "fst/fst.h" #include "sta_info.h" #include "hostapd.h" @@ -55,10 +56,20 @@ static int prune_associations(struct hostapd_iface *iface, void *ctx) ohapd = iface->bss[j]; if (ohapd == data->hapd) continue; +#ifdef CONFIG_FST + /* Don't prune STAs belong to same FST */ + if (ohapd->iface->fst && + data->hapd->iface->fst && + fst_are_ifaces_aggregated(ohapd->iface->fst, + data->hapd->iface->fst)) + continue; +#endif /* CONFIG_FST */ osta = ap_get_sta(ohapd, data->addr); if (!osta) continue; + wpa_printf(MSG_INFO, "%s: Prune association for " MACSTR, + ohapd->conf->iface, MAC2STR(osta->addr)); ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED); } diff --git a/contrib/wpa/src/ap/vlan_init.c b/contrib/wpa/src/ap/vlan_init.c index dc6501997db..fd1c8ddacee 100644 --- a/contrib/wpa/src/ap/vlan_init.c +++ b/contrib/wpa/src/ap/vlan_init.c @@ -9,6 +9,13 @@ */ #include "utils/includes.h" +#ifdef CONFIG_FULL_DYNAMIC_VLAN +#include +#include +#include +#include +#include +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ #include "utils/common.h" #include "hostapd.h" @@ -20,12 +27,6 @@ #ifdef CONFIG_FULL_DYNAMIC_VLAN -#include -#include -#include -#include -#include - #include "drivers/priv_netlink.h" #include "utils/eloop.h" @@ -34,6 +35,90 @@ struct full_dynamic_vlan { int s; /* socket on which to listen for new/removed interfaces. */ }; +#define DVLAN_CLEAN_BR 0x1 +#define DVLAN_CLEAN_VLAN 0x2 +#define DVLAN_CLEAN_VLAN_PORT 0x4 + +struct dynamic_iface { + char ifname[IFNAMSIZ + 1]; + int usage; + int clean; + struct dynamic_iface *next; +}; + + +/* Increment ref counter for ifname and add clean flag. + * If not in list, add it only if some flags are given. + */ +static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, + int clean) +{ + struct dynamic_iface *next, **dynamic_ifaces; + struct hapd_interfaces *interfaces; + + interfaces = hapd->iface->interfaces; + dynamic_ifaces = &interfaces->vlan_priv; + + for (next = *dynamic_ifaces; next; next = next->next) { + if (os_strcmp(ifname, next->ifname) == 0) + break; + } + + if (next) { + next->usage++; + next->clean |= clean; + return; + } + + if (!clean) + return; + + next = os_zalloc(sizeof(*next)); + if (!next) + return; + os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); + next->usage = 1; + next->clean = clean; + next->next = *dynamic_ifaces; + *dynamic_ifaces = next; +} + + +/* Decrement reference counter for given ifname. + * Return clean flag iff reference counter was decreased to zero, else zero + */ +static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) +{ + struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; + struct hapd_interfaces *interfaces; + int clean; + + interfaces = hapd->iface->interfaces; + dynamic_ifaces = &interfaces->vlan_priv; + + for (next = *dynamic_ifaces; next; next = next->next) { + if (os_strcmp(ifname, next->ifname) == 0) + break; + prev = next; + } + + if (!next) + return 0; + + next->usage--; + if (next->usage) + return 0; + + if (prev) + prev->next = next->next; + else + *dynamic_ifaces = next->next; + clean = next->clean; + os_free(next); + + return clean; +} + static int ifconfig_helper(const char *if_name, int up) { @@ -481,11 +566,13 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) struct hostapd_vlan *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; int vlan_naming = hapd->conf->ssid.vlan_naming; + int clean; wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); while (vlan) { - if (os_strcmp(ifname, vlan->ifname) == 0) { + if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) { + vlan->configured = 1; if (hapd->conf->vlan_bridge[0]) { os_snprintf(br_name, sizeof(br_name), "%s%d", @@ -500,8 +587,8 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) "brvlan%d", vlan->vlan_id); } - if (!br_addbr(br_name)) - vlan->clean |= DVLAN_CLEAN_BR; + dyn_iface_get(hapd, br_name, + br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); ifconfig_up(br_name); @@ -517,13 +604,16 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) sizeof(vlan_ifname), "vlan%d", vlan->vlan_id); + clean = 0; ifconfig_up(tagged_interface); if (!vlan_add(tagged_interface, vlan->vlan_id, vlan_ifname)) - vlan->clean |= DVLAN_CLEAN_VLAN; + clean |= DVLAN_CLEAN_VLAN; if (!br_addif(br_name, vlan_ifname)) - vlan->clean |= DVLAN_CLEAN_VLAN_PORT; + clean |= DVLAN_CLEAN_VLAN_PORT; + + dyn_iface_get(hapd, vlan_ifname, clean); ifconfig_up(vlan_ifname); } @@ -547,13 +637,15 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; int vlan_naming = hapd->conf->ssid.vlan_naming; + int clean; wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); first = prev = vlan; while (vlan) { - if (os_strcmp(ifname, vlan->ifname) == 0) { + if (os_strcmp(ifname, vlan->ifname) == 0 && + vlan->configured) { if (hapd->conf->vlan_bridge[0]) { os_snprintf(br_name, sizeof(br_name), "%s%d", hapd->conf->vlan_bridge, @@ -581,20 +673,27 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vlan->vlan_id); - if (vlan->clean & DVLAN_CLEAN_VLAN_PORT) - br_delif(br_name, vlan_ifname); - ifconfig_down(vlan_ifname); - if (vlan->clean & DVLAN_CLEAN_VLAN) + clean = dyn_iface_put(hapd, vlan_ifname); + + if (clean & DVLAN_CLEAN_VLAN_PORT) + br_delif(br_name, vlan_ifname); + + if (clean & DVLAN_CLEAN_VLAN) { + ifconfig_down(vlan_ifname); vlan_rem(vlan_ifname); + } } - if ((vlan->clean & DVLAN_CLEAN_BR) && + clean = dyn_iface_put(hapd, br_name); + if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) { ifconfig_down(br_name); br_delbr(br_name); } + } + if (os_strcmp(ifname, vlan->ifname) == 0) { if (vlan == first) { hapd->conf->vlan = vlan->next; } else { @@ -651,6 +750,11 @@ vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, if (!ifname[0]) return; + if (del && if_nametoindex(ifname)) { + /* interface still exists, race condition -> + * iface has just been recreated */ + return; + } wpa_printf(MSG_DEBUG, "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", @@ -778,8 +882,7 @@ static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) #endif /* CONFIG_FULL_DYNAMIC_VLAN */ -int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, const char *dyn_vlan) +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan) { int i; @@ -789,10 +892,11 @@ int vlan_setup_encryption_dyn(struct hostapd_data *hapd, /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own * functions for setting up dynamic broadcast keys. */ for (i = 0; i < 4; i++) { - if (mssid->wep.key[i] && + if (hapd->conf->ssid.wep.key[i] && hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, - i == mssid->wep.idx, NULL, 0, - mssid->wep.key[i], mssid->wep.len[i])) + i == hapd->conf->ssid.wep.idx, NULL, 0, + hapd->conf->ssid.wep.key[i], + hapd->conf->ssid.wep.len[i])) { wpa_printf(MSG_ERROR, "VLAN: Could not set WEP " "encryption for dynamic VLAN"); @@ -953,7 +1057,8 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) return 1; - wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id); + wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)", + __func__, hapd->conf->iface, vlan_id); vlan = hapd->conf->vlan; while (vlan) { @@ -967,8 +1072,12 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) if (vlan == NULL) return 1; - if (vlan->dynamic_vlan == 0) + if (vlan->dynamic_vlan == 0) { hostapd_vlan_if_remove(hapd, vlan->ifname); +#ifdef CONFIG_FULL_DYNAMIC_VLAN + vlan_dellink(vlan->ifname, hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + } return 0; } diff --git a/contrib/wpa/src/ap/vlan_init.h b/contrib/wpa/src/ap/vlan_init.h index 781eaac441b..fc39443e5d3 100644 --- a/contrib/wpa/src/ap/vlan_init.h +++ b/contrib/wpa/src/ap/vlan_init.h @@ -18,7 +18,6 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, int vlan_id); int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, const char *dyn_vlan); #else /* CONFIG_NO_VLAN */ static inline int vlan_init(struct hostapd_data *hapd) @@ -43,7 +42,6 @@ static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) } static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, const char *dyn_vlan) { return -1; diff --git a/contrib/wpa/src/ap/vlan_util.c b/contrib/wpa/src/ap/vlan_util.c index cc54051b1ec..d4e0efb9b02 100644 --- a/contrib/wpa/src/ap/vlan_util.c +++ b/contrib/wpa/src/ap/vlan_util.c @@ -31,7 +31,7 @@ */ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { - int ret = -1; + int err, ret = -1; struct nl_sock *handle = NULL; struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; @@ -58,14 +58,18 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) goto vlan_add_error; } - if (nl_connect(handle, NETLINK_ROUTE) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + err = nl_connect(handle, NETLINK_ROUTE); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s", + nl_geterror(err)); goto vlan_add_error; } - if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + if (err < 0) { cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", + nl_geterror(err)); goto vlan_add_error; } @@ -92,23 +96,29 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) goto vlan_add_error; } - if (rtnl_link_set_type(rlink, "vlan") < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to set link type"); + err = rtnl_link_set_type(rlink, "vlan"); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link type: %s", + nl_geterror(err)); goto vlan_add_error; } rtnl_link_set_link(rlink, if_idx); rtnl_link_set_name(rlink, vlan_if_name); - if (rtnl_link_vlan_set_id(rlink, vid) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id"); + err = rtnl_link_vlan_set_id(rlink, vid); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id: %s", + nl_geterror(err)); goto vlan_add_error; } - if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) { + err = rtnl_link_add(handle, rlink, NLM_F_CREATE); + if (err < 0) { wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for " - "vlan %d on %s (%d)", - vlan_if_name, vid, if_name, if_idx); + "vlan %d on %s (%d): %s", + vlan_if_name, vid, if_name, if_idx, + nl_geterror(err)); goto vlan_add_error; } @@ -127,7 +137,7 @@ vlan_add_error: int vlan_rem(const char *if_name) { - int ret = -1; + int err, ret = -1; struct nl_sock *handle = NULL; struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; @@ -140,14 +150,18 @@ int vlan_rem(const char *if_name) goto vlan_rem_error; } - if (nl_connect(handle, NETLINK_ROUTE) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + err = nl_connect(handle, NETLINK_ROUTE); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s", + nl_geterror(err)); goto vlan_rem_error; } - if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + if (err < 0) { cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", + nl_geterror(err)); goto vlan_rem_error; } @@ -158,9 +172,10 @@ int vlan_rem(const char *if_name) goto vlan_rem_error; } - if (rtnl_link_delete(handle, rlink) < 0) { - wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s", - if_name); + err = rtnl_link_delete(handle, rlink); + if (err < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s: %s", + if_name, nl_geterror(err)); goto vlan_rem_error; } diff --git a/contrib/wpa/src/ap/wmm.c b/contrib/wpa/src/ap/wmm.c index 6d4177c2a84..314e244bc95 100644 --- a/contrib/wpa/src/ap/wmm.c +++ b/contrib/wpa/src/ap/wmm.c @@ -274,6 +274,9 @@ void hostapd_wmm_action(struct hostapd_data *hapd, return; } + if (left < 0) + return; /* not a valid WMM Action frame */ + /* extract the tspec info element */ if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, diff --git a/contrib/wpa/src/ap/wpa_auth.c b/contrib/wpa/src/ap/wpa_auth.c index 9c5f6094acb..2760a3f3a00 100644 --- a/contrib/wpa/src/ap/wpa_auth.c +++ b/contrib/wpa/src/ap/wpa_auth.c @@ -45,6 +45,12 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, struct wpa_ptk *ptk); +static void wpa_group_free(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static void wpa_group_get(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static void wpa_group_put(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; @@ -67,6 +73,14 @@ static inline int wpa_auth_mic_failure_report( } +static inline void wpa_auth_psk_failure_report( + struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + if (wpa_auth->cb.psk_failure_report) + wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr); +} + + static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var, int value) @@ -254,15 +268,22 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; - struct wpa_group *group; + struct wpa_group *group, *next; wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); - for (group = wpa_auth->group; group; group = group->next) { + group = wpa_auth->group; + while (group) { + wpa_group_get(wpa_auth, group); + group->GTKReKey = TRUE; do { group->changed = FALSE; wpa_group_sm_step(wpa_auth, group); } while (group->changed); + + next = group->next; + wpa_group_put(wpa_auth, group); + group = next; } if (wpa_auth->conf.wpa_group_rekey) { @@ -565,6 +586,7 @@ wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, sm->wpa_auth = wpa_auth; sm->group = wpa_auth->group; + wpa_group_get(sm->wpa_auth, sm->group); return sm; } @@ -643,6 +665,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) #endif /* CONFIG_IEEE80211R */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); + wpa_group_put(sm->wpa_auth, sm->group); os_free(sm); } @@ -1517,6 +1540,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, else WPA_PUT_BE16(key->key_data_length, key_data_len); +#ifndef CONFIG_NO_RC4 } else if (sm->PTK.kek_len == 16) { u8 ek[32]; os_memcpy(key->key_iv, @@ -1532,6 +1556,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, else WPA_PUT_BE16(key->key_data_length, key_data_len); +#endif /* CONFIG_NO_RC4 */ } else { os_free(hdr); os_free(buf); @@ -1646,7 +1671,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) } -int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) +int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) { int remove_ptk = 1; @@ -1734,6 +1759,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) wpa_remove_ptk(sm); } + if (sm->in_step_loop) { + /* + * wpa_sm_step() is already running - avoid recursive call to + * it by making the existing loop process the new update. + */ + sm->changed = TRUE; + return 0; + } return wpa_sm_step(sm); } @@ -1818,9 +1851,13 @@ static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, group->reject_4way_hs_for_entropy = FALSE; } - wpa_group_init_gmk_and_counter(wpa_auth, group); - wpa_gtk_update(wpa_auth, group); - wpa_group_config_group_keys(wpa_auth, group); + if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 || + wpa_gtk_update(wpa_auth, group) < 0 || + wpa_group_config_group_keys(wpa_auth, group) < 0) { + wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed"); + group->first_sta_seen = FALSE; + group->reject_4way_hs_for_entropy = TRUE; + } } @@ -1985,7 +2022,7 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { struct wpa_ptk PTK; - int ok = 0; + int ok = 0, psk_found = 0; const u8 *pmk = NULL; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); @@ -2001,6 +2038,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) sm->p2p_dev_addr, pmk); if (pmk == NULL) break; + psk_found = 1; } else pmk = sm->PMK; @@ -2020,6 +2058,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (!ok) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "invalid MIC in msg 2/4 of 4-Way Handshake"); + if (psk_found) + wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr); return; } @@ -2983,9 +3023,9 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) } -static const char * wpa_bool_txt(int bool) +static const char * wpa_bool_txt(int val) { - return bool ? "TRUE" : "FALSE"; + return val ? "TRUE" : "FALSE"; } @@ -3270,6 +3310,63 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, } +/* + * Remove and free the group from wpa_authenticator. This is triggered by a + * callback to make sure nobody is currently iterating the group list while it + * gets modified. + */ +static void wpa_group_free(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + struct wpa_group *prev = wpa_auth->group; + + wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d", + group->vlan_id); + + while (prev) { + if (prev->next == group) { + /* This never frees the special first group as needed */ + prev->next = group->next; + os_free(group); + break; + } + prev = prev->next; + } + +} + + +/* Increase the reference counter for group */ +static void wpa_group_get(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + /* Skip the special first group */ + if (wpa_auth->group == group) + return; + + group->references++; +} + + +/* Decrease the reference counter and maybe free the group */ +static void wpa_group_put(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + /* Skip the special first group */ + if (wpa_auth->group == group) + return; + + group->references--; + if (group->references) + return; + wpa_group_free(wpa_auth, group); +} + + +/* + * Add a group that has its references counter set to zero. Caller needs to + * call wpa_group_get() on the return value to mark the entry in use. + */ static struct wpa_group * wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) { @@ -3320,7 +3417,10 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); + wpa_group_get(sm->wpa_auth, group); + wpa_group_put(sm->wpa_auth, sm->group); sm->group = group; + return 0; } diff --git a/contrib/wpa/src/ap/wpa_auth.h b/contrib/wpa/src/ap/wpa_auth.h index 2788e657435..fd04f169433 100644 --- a/contrib/wpa/src/ap/wpa_auth.h +++ b/contrib/wpa/src/ap/wpa_auth.h @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,6 +12,9 @@ #include "common/defs.h" #include "common/eapol_common.h" #include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" + +#define MAX_OWN_IE_OVERRIDE 256 #ifdef _MSC_VER #pragma pack(push, 1) @@ -146,8 +149,7 @@ struct wpa_auth_config { int group_mgmt_cipher; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R -#define SSID_LEN 32 - u8 ssid[SSID_LEN]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; @@ -164,6 +166,8 @@ struct wpa_auth_config { int ap_mlme; #ifdef CONFIG_TESTING_OPTIONS double corrupt_gtk_rekey_mic_probability; + u8 own_ie_override[MAX_OWN_IE_OVERRIDE]; + size_t own_ie_override_len; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P u8 ip_addr_go[4]; @@ -189,6 +193,7 @@ struct wpa_auth_callbacks { const char *txt); void (*disconnect)(void *ctx, const u8 *addr, u16 reason); int (*mic_failure_report)(void *ctx, const u8 *addr); + void (*psk_failure_report)(void *ctx, const u8 *addr); void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); @@ -251,12 +256,12 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm); void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len); -typedef enum { +enum wpa_event { WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, WPA_REAUTH_EAPOL, WPA_ASSOC_FT -} wpa_event; +}; void wpa_remove_ptk(struct wpa_state_machine *sm); -int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); +int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event); void wpa_auth_sm_notify(struct wpa_state_machine *sm); void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); diff --git a/contrib/wpa/src/ap/wpa_auth_ft.c b/contrib/wpa/src/ap/wpa_auth_ft.c index ef3249a3eb9..eeaffbf6351 100644 --- a/contrib/wpa/src/ap/wpa_auth_ft.c +++ b/contrib/wpa/src/ap/wpa_auth_ft.c @@ -534,10 +534,8 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, return pos; } -#ifdef NEED_AP_MLME - if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { + if (parse.wmm_tspec) { struct wmm_tspec_element *tspec; - int res; if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) { wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE " @@ -555,7 +553,13 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, } tspec = (struct wmm_tspec_element *) pos; os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); - res = wmm_process_tspec(tspec); + } + +#ifdef NEED_AP_MLME + if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { + int res; + + res = wmm_process_tspec((struct wmm_tspec_element *) pos); wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res); if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS) rdie->status_code = @@ -566,20 +570,17 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, else { /* TSPEC accepted; include updated TSPEC in response */ rdie->descr_count = 1; - pos += sizeof(*tspec); + pos += sizeof(struct wmm_tspec_element); } return pos; } #endif /* NEED_AP_MLME */ if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { - struct wmm_tspec_element *tspec; int res; - tspec = (struct wmm_tspec_element *) pos; - os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, - sizeof(*tspec)); + sizeof(struct wmm_tspec_element)); if (res >= 0) { if (res) rdie->status_code = host_to_le16(res); @@ -587,7 +588,7 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, /* TSPEC accepted; include updated TSPEC in * response */ rdie->descr_count = 1; - pos += sizeof(*tspec); + pos += sizeof(struct wmm_tspec_element); } return pos; } diff --git a/contrib/wpa/src/ap/wpa_auth_glue.c b/contrib/wpa/src/ap/wpa_auth_glue.c index 7f8320708c3..f98cc50599e 100644 --- a/contrib/wpa/src/ap/wpa_auth_glue.c +++ b/contrib/wpa/src/ap/wpa_auth_glue.c @@ -11,6 +11,7 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/sae.h" +#include "common/wpa_ctrl.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" @@ -53,8 +54,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R wconf->ssid_len = conf->ssid.ssid_len; - if (wconf->ssid_len > SSID_LEN) - wconf->ssid_len = SSID_LEN; + if (wconf->ssid_len > SSID_MAX_LEN) + wconf->ssid_len = SSID_MAX_LEN; os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); os_memcpy(wconf->mobility_domain, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); @@ -91,6 +92,13 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #ifdef CONFIG_TESTING_OPTIONS wconf->corrupt_gtk_rekey_mic_probability = iconf->corrupt_gtk_rekey_mic_probability; + if (conf->own_ie_override && + wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) { + wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override); + os_memcpy(wconf->own_ie_override, + wpabuf_head(conf->own_ie_override), + wconf->own_ie_override_len); + } #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4); @@ -144,6 +152,14 @@ static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) } +static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR, + MAC2STR(addr)); +} + + static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, wpa_eapol_variable var, int value) { @@ -579,6 +595,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) cb.logger = hostapd_wpa_auth_logger; cb.disconnect = hostapd_wpa_auth_disconnect; cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; + cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report; cb.set_eapol = hostapd_wpa_auth_set_eapol; cb.get_eapol = hostapd_wpa_auth_get_eapol; cb.get_psk = hostapd_wpa_auth_get_psk; @@ -620,7 +637,8 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) } #ifdef CONFIG_IEEE80211R - if (!hostapd_drv_none(hapd)) { + if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds && + wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? hapd->conf->bridge : hapd->conf->iface, NULL, ETH_P_RRB, diff --git a/contrib/wpa/src/ap/wpa_auth_i.h b/contrib/wpa/src/ap/wpa_auth_i.h index 7b2cd3ea8ed..57b098f2ed7 100644 --- a/contrib/wpa/src/ap/wpa_auth_i.h +++ b/contrib/wpa/src/ap/wpa_auth_i.h @@ -169,6 +169,8 @@ struct wpa_group { u8 IGTK[2][WPA_IGTK_MAX_LEN]; int GN_igtk, GM_igtk; #endif /* CONFIG_IEEE80211W */ + /* Number of references except those in struct wpa_group->next */ + unsigned int references; }; diff --git a/contrib/wpa/src/ap/wpa_auth_ie.c b/contrib/wpa/src/ap/wpa_auth_ie.c index f2872970aff..eafb828b8d6 100644 --- a/contrib/wpa/src/ap/wpa_auth_ie.c +++ b/contrib/wpa/src/ap/wpa_auth_ie.c @@ -261,7 +261,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } #ifdef CONFIG_IEEE80211W - if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION && + conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) { if (pos + 2 + 4 > buf + len) return -1; if (pmkid == NULL) { @@ -377,6 +378,23 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) u8 *pos, buf[128]; int res; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_auth->conf.own_ie_override_len) { + wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing", + wpa_auth->conf.own_ie_override, + wpa_auth->conf.own_ie_override_len); + os_free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = + os_malloc(wpa_auth->conf.own_ie_override_len); + if (wpa_auth->wpa_ie == NULL) + return -1; + os_memcpy(wpa_auth->wpa_ie, wpa_auth->conf.own_ie_override, + wpa_auth->conf.own_ie_override_len); + wpa_auth->wpa_ie_len = wpa_auth->conf.own_ie_override_len; + return 0; + } +#endif /* CONFIG_TESTING_OPTIONS */ + pos = buf; if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) { diff --git a/contrib/wpa/src/ap/wps_hostapd.c b/contrib/wpa/src/ap/wps_hostapd.c index b0e8b0bfcac..cde31e60e03 100644 --- a/contrib/wpa/src/ap/wps_hostapd.c +++ b/contrib/wpa/src/ap/wps_hostapd.c @@ -324,7 +324,7 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration"); bss->wps_state = 2; - if (cred->ssid_len <= HOSTAPD_MAX_SSID_LEN) { + if (cred->ssid_len <= SSID_MAX_LEN) { os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len); bss->ssid.ssid_len = cred->ssid_len; bss->ssid.ssid_set = 1; @@ -347,8 +347,12 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = 0; - if (cred->encr_type & WPS_ENCR_AES) - bss->wpa_pairwise |= WPA_CIPHER_CCMP; + if (cred->encr_type & WPS_ENCR_AES) { + if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) + bss->wpa_pairwise |= WPA_CIPHER_GCMP; + else + bss->wpa_pairwise |= WPA_CIPHER_CCMP; + } if (cred->encr_type & WPS_ENCR_TKIP) bss->wpa_pairwise |= WPA_CIPHER_TKIP; bss->rsn_pairwise = bss->wpa_pairwise; @@ -448,6 +452,11 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) os_free(hapd->wps->network_key); hapd->wps->network_key = NULL; hapd->wps->network_key_len = 0; + } else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) && + (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) { + wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2", + (unsigned long) cred->key_len); + return -1; } else { if (hapd->wps->network_key == NULL || hapd->wps->network_key_len < cred->key_len) { @@ -530,7 +539,11 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) fprintf(nconf, "wpa_pairwise="); prefix = ""; if (cred->encr_type & WPS_ENCR_AES) { - fprintf(nconf, "CCMP"); + if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD) + fprintf(nconf, "GCMP"); + else + fprintf(nconf, "CCMP"); + prefix = " "; } if (cred->encr_type & WPS_ENCR_TKIP) { @@ -844,7 +857,9 @@ static int hostapd_wps_rf_band_cb(void *ctx) struct hostapd_data *hapd = ctx; return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? - WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + WPS_RF_50GHZ : + hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ? + WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ } @@ -856,8 +871,10 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only) wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = NULL; - if (deinit_only) + if (deinit_only) { + hostapd_reset_ap_wps_ie(hapd); return; + } hostapd_set_ap_wps_ie(hapd); } @@ -1039,7 +1056,9 @@ int hostapd_init_wps(struct hostapd_data *hapd, } else { wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? - WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + WPS_RF_50GHZ : + hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ? + WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ } if (conf->wpa & WPA_PROTO_RSN) { @@ -1285,30 +1304,53 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, } +struct wps_button_pushed_ctx { + const u8 *p2p_dev_addr; + unsigned int count; +}; + static int wps_button_pushed(struct hostapd_data *hapd, void *ctx) { - const u8 *p2p_dev_addr = ctx; - if (hapd->wps == NULL) - return -1; - return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr); + struct wps_button_pushed_ctx *data = ctx; + + if (hapd->wps) { + data->count++; + return wps_registrar_button_pushed(hapd->wps->registrar, + data->p2p_dev_addr); + } + + return 0; } int hostapd_wps_button_pushed(struct hostapd_data *hapd, const u8 *p2p_dev_addr) { - return hostapd_wps_for_each(hapd, wps_button_pushed, - (void *) p2p_dev_addr); + struct wps_button_pushed_ctx ctx; + int ret; + + os_memset(&ctx, 0, sizeof(ctx)); + ctx.p2p_dev_addr = p2p_dev_addr; + ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx); + if (ret == 0 && !ctx.count) + ret = -1; + return ret; } +struct wps_cancel_ctx { + unsigned int count; +}; + static int wps_cancel(struct hostapd_data *hapd, void *ctx) { - if (hapd->wps == NULL) - return -1; + struct wps_cancel_ctx *data = ctx; - wps_registrar_wps_cancel(hapd->wps->registrar); - ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); + if (hapd->wps) { + data->count++; + wps_registrar_wps_cancel(hapd->wps->registrar); + ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); + } return 0; } @@ -1316,7 +1358,14 @@ static int wps_cancel(struct hostapd_data *hapd, void *ctx) int hostapd_wps_cancel(struct hostapd_data *hapd) { - return hostapd_wps_for_each(hapd, wps_cancel, NULL); + struct wps_cancel_ctx ctx; + int ret; + + os_memset(&ctx, 0, sizeof(ctx)); + ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx); + if (ret == 0 && !ctx.count) + ret = -1; + return ret; } @@ -1546,6 +1595,10 @@ struct wps_ap_pin_data { static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx) { struct wps_ap_pin_data *data = ctx; + + if (!hapd->wps) + return 0; + os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(data->pin_txt); #ifdef CONFIG_WPS_UPNP diff --git a/contrib/wpa/src/ap/x_snoop.c b/contrib/wpa/src/ap/x_snoop.c index 8f77015ef57..aef9a53c46c 100644 --- a/contrib/wpa/src/ap/x_snoop.c +++ b/contrib/wpa/src/ap/x_snoop.c @@ -51,6 +51,14 @@ int x_snoop_init(struct hostapd_data *hapd) return -1; } +#ifdef CONFIG_IPV6 + if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to enable multicast snooping on the bridge"); + return -1; + } +#endif /* CONFIG_IPV6 */ + return 0; } diff --git a/contrib/wpa/src/common/common_module_tests.c b/contrib/wpa/src/common/common_module_tests.c index 56b11220c9e..d69448bd380 100644 --- a/contrib/wpa/src/common/common_module_tests.c +++ b/contrib/wpa/src/common/common_module_tests.c @@ -1,6 +1,6 @@ /* * common module tests - * Copyright (c) 2014, Jouni Malinen + * Copyright (c) 2014-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,8 @@ #include "utils/common.h" #include "ieee802_11_common.h" +#include "ieee802_11_defs.h" +#include "gas.h" #include "wpa_common.h" @@ -46,6 +48,10 @@ static const struct ieee802_11_parse_test_data parse_tests[] = { { (u8 *) "\x6e\x00", 2, ParseOK, 1 }, { (u8 *) "\xc7\x00", 2, ParseOK, 1 }, { (u8 *) "\xc7\x01\x00", 3, ParseOK, 1 }, + { (u8 *) "\x03\x00\x2a\x00\x36\x00\x37\x00\x38\x00\x2d\x00\x3d\x00\xbf\x00\xc0\x00", + 18, ParseOK, 9 }, + { (u8 *) "\x8b\x00", 2, ParseOK, 1 }, + { (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 }, { NULL, 0, ParseOK, 0 } }; @@ -158,6 +164,34 @@ static int rsn_ie_parse_tests(void) } +static int gas_tests(void) +{ + struct wpabuf *buf; + + wpa_printf(MSG_INFO, "gas tests"); + gas_anqp_set_len(NULL); + + buf = wpabuf_alloc(1); + if (buf == NULL) + return -1; + gas_anqp_set_len(buf); + wpabuf_free(buf); + + buf = wpabuf_alloc(20); + if (buf == NULL) + return -1; + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ); + wpabuf_put_u8(buf, 0); + wpabuf_put_be32(buf, 0); + wpabuf_put_u8(buf, 0); + gas_anqp_set_len(buf); + wpabuf_free(buf); + + return 0; +} + + int common_module_tests(void) { int ret = 0; @@ -165,6 +199,7 @@ int common_module_tests(void) wpa_printf(MSG_INFO, "common module tests"); if (ieee802_11_parse_tests() < 0 || + gas_tests() < 0 || rsn_ie_parse_tests() < 0) ret = -1; diff --git a/contrib/wpa/src/common/defs.h b/contrib/wpa/src/common/defs.h index b5f4f801eda..6aea3751a2b 100644 --- a/contrib/wpa/src/common/defs.h +++ b/contrib/wpa/src/common/defs.h @@ -174,7 +174,7 @@ enum wpa_states { /** * WPA_INTERFACE_DISABLED - Interface disabled * - * This stat eis entered if the network interface is disabled, e.g., + * This state is entered if the network interface is disabled, e.g., * due to rfkill. wpa_supplicant refuses any new operations that would * use the radio until the interface has been enabled. */ @@ -295,6 +295,7 @@ enum hostapd_hw_mode { HOSTAPD_MODE_IEEE80211G, HOSTAPD_MODE_IEEE80211A, HOSTAPD_MODE_IEEE80211AD, + HOSTAPD_MODE_IEEE80211ANY, NUM_HOSTAPD_MODES }; @@ -310,6 +311,7 @@ enum wpa_ctrl_req_type { WPA_CTRL_REQ_EAP_OTP, WPA_CTRL_REQ_EAP_PASSPHRASE, WPA_CTRL_REQ_SIM, + WPA_CTRL_REQ_PSK_PASSPHRASE, NUM_WPA_CTRL_REQS }; @@ -326,4 +328,10 @@ enum mesh_plink_state { PLINK_BLOCKED, }; +enum set_band { + WPA_SETBAND_AUTO, + WPA_SETBAND_5G, + WPA_SETBAND_2G +}; + #endif /* DEFS_H */ diff --git a/contrib/wpa/src/common/hw_features_common.c b/contrib/wpa/src/common/hw_features_common.c index e8babb52a9c..9c37ea63ca8 100644 --- a/contrib/wpa/src/common/hw_features_common.c +++ b/contrib/wpa/src/common/hw_features_common.c @@ -88,8 +88,8 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, int sec_chan) { int ok, j, first; - int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, - 184, 192 }; + int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, + 149, 157, 184, 192 }; size_t k; if (pri_chan == sec_chan || !sec_chan) @@ -152,8 +152,7 @@ void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan) *pri_chan = *sec_chan = 0; ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); - if (elems.ht_operation && - elems.ht_operation_len >= sizeof(*oper)) { + if (elems.ht_operation) { oper = (struct ieee80211_ht_operation *) elems.ht_operation; *pri_chan = oper->primary_chan; if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) { @@ -177,10 +176,8 @@ int check_40mhz_5g(struct hostapd_hw_modes *mode, size_t i; int match; - if (!mode || !scan_res || !pri_chan || !sec_chan) - return 0; - - if (pri_chan == sec_chan) + if (!mode || !scan_res || !pri_chan || !sec_chan || + pri_chan == sec_chan) return 0; pri_freq = hw_get_freq(mode, pri_chan); @@ -238,7 +235,8 @@ int check_40mhz_5g(struct hostapd_hw_modes *mode, } -int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end) +static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, + int end) { struct ieee802_11_elems elems; struct ieee80211_ht_operation *oper; @@ -253,8 +251,7 @@ int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end) return 1; } - if (elems.ht_operation && - elems.ht_operation_len >= sizeof(*oper)) { + if (elems.ht_operation) { oper = (struct ieee80211_ht_operation *) elems.ht_operation; if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK) return 0; @@ -275,10 +272,8 @@ int check_40mhz_2g4(struct hostapd_hw_modes *mode, int affected_start, affected_end; size_t i; - if (!mode || !scan_res || !pri_chan || !sec_chan) - return 0; - - if (pri_chan == sec_chan) + if (!mode || !scan_res || !pri_chan || !sec_chan || + pri_chan == sec_chan) return 0; pri_freq = hw_get_freq(mode, pri_chan); @@ -335,9 +330,7 @@ int check_40mhz_2g4(struct hostapd_hw_modes *mode, ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); - if (elems.ht_capabilities && - elems.ht_capabilities_len >= - sizeof(struct ieee80211_ht_capabilities)) { + if (elems.ht_capabilities) { struct ieee80211_ht_capabilities *ht_cap = (struct ieee80211_ht_capabilities *) elems.ht_capabilities; @@ -363,8 +356,6 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, int vht_oper_chwidth, int center_segment0, int center_segment1, u32 vht_caps) { - int tmp; - os_memset(data, 0, sizeof(*data)); data->mode = mode; data->freq = freq; @@ -378,11 +369,10 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, if (data->vht_enabled) switch (vht_oper_chwidth) { case VHT_CHANWIDTH_USE_HT: - if (center_segment1) - return -1; - if (center_segment0 != 0 && - 5000 + center_segment0 * 5 != data->center_freq1 && - 2407 + center_segment0 * 5 != data->center_freq1) + if (center_segment1 || + (center_segment0 != 0 && + 5000 + center_segment0 * 5 != data->center_freq1 && + 2407 + center_segment0 * 5 != data->center_freq1)) return -1; break; case VHT_CHANWIDTH_80P80MHZ: @@ -398,19 +388,38 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, /* fall through */ case VHT_CHANWIDTH_80MHZ: data->bandwidth = 80; - if (vht_oper_chwidth == 1 && center_segment1) + if ((vht_oper_chwidth == 1 && center_segment1) || + (vht_oper_chwidth == 3 && !center_segment1) || + !sec_channel_offset) return -1; - if (vht_oper_chwidth == 3 && !center_segment1) - return -1; - if (!sec_channel_offset) - return -1; - /* primary 40 part must match the HT configuration */ - tmp = (30 + freq - 5000 - center_segment0 * 5) / 20; - tmp /= 2; - if (data->center_freq1 != 5000 + - center_segment0 * 5 - 20 + 40 * tmp) - return -1; - data->center_freq1 = 5000 + center_segment0 * 5; + if (!center_segment0) { + if (channel <= 48) + center_segment0 = 42; + else if (channel <= 64) + center_segment0 = 58; + else if (channel <= 112) + center_segment0 = 106; + else if (channel <= 128) + center_segment0 = 122; + else if (channel <= 144) + center_segment0 = 138; + else if (channel <= 161) + center_segment0 = 155; + data->center_freq1 = 5000 + center_segment0 * 5; + } else { + /* + * Note: HT/VHT config and params are coupled. Check if + * HT40 channel band is in VHT80 Pri channel band + * configuration. + */ + if (center_segment0 == channel + 6 || + center_segment0 == channel + 2 || + center_segment0 == channel - 2 || + center_segment0 == channel - 6) + data->center_freq1 = 5000 + center_segment0 * 5; + else + return -1; + } break; case VHT_CHANWIDTH_160MHZ: data->bandwidth = 160; @@ -424,13 +433,21 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, return -1; if (!sec_channel_offset) return -1; - /* primary 40 part must match the HT configuration */ - tmp = (70 + freq - 5000 - center_segment0 * 5) / 20; - tmp /= 2; - if (data->center_freq1 != 5000 + - center_segment0 * 5 - 60 + 40 * tmp) + /* + * Note: HT/VHT config and params are coupled. Check if + * HT40 channel band is in VHT160 channel band configuration. + */ + if (center_segment0 == channel + 14 || + center_segment0 == channel + 10 || + center_segment0 == channel + 6 || + center_segment0 == channel + 2 || + center_segment0 == channel - 2 || + center_segment0 == channel - 6 || + center_segment0 == channel - 10 || + center_segment0 == channel - 14) + data->center_freq1 = 5000 + center_segment0 * 5; + else return -1; - data->center_freq1 = 5000 + center_segment0 * 5; break; } diff --git a/contrib/wpa/src/common/hw_features_common.h b/contrib/wpa/src/common/hw_features_common.h index 7f43d00c5b2..7360b4e3efe 100644 --- a/contrib/wpa/src/common/hw_features_common.h +++ b/contrib/wpa/src/common/hw_features_common.h @@ -26,7 +26,6 @@ void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan); int check_40mhz_5g(struct hostapd_hw_modes *mode, struct wpa_scan_results *scan_res, int pri_chan, int sec_chan); -int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end); int check_40mhz_2g4(struct hostapd_hw_modes *mode, struct wpa_scan_results *scan_res, int pri_chan, int sec_chan); diff --git a/contrib/wpa/src/common/ieee802_11_common.c b/contrib/wpa/src/common/ieee802_11_common.c index aca0b73223b..d07a316a792 100644 --- a/contrib/wpa/src/common/ieee802_11_common.c +++ b/contrib/wpa/src/common/ieee802_11_common.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,8 @@ #include "common.h" #include "defs.h" +#include "wpa_common.h" +#include "qca-vendor.h" #include "ieee802_11_defs.h" #include "ieee802_11_common.h" @@ -146,6 +148,20 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, } break; + case OUI_QCA: + switch (pos[3]) { + case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: + elems->pref_freq_list = pos; + elems->pref_freq_list_len = elen; + break; + default: + wpa_printf(MSG_EXCESSIVE, + "Unknown QCA information element ignored (type=%d len=%lu)", + pos[3], (unsigned long) elen); + return -1; + } + break; + default: wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " "information element ignored (vendor OUI " @@ -196,6 +212,12 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, switch (id) { case WLAN_EID_SSID: + if (elen > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "Ignored too long SSID element (elen=%u)", + elen); + break; + } elems->ssid = pos; elems->ssid_len = elen; break; @@ -204,8 +226,9 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->supp_rates_len = elen; break; case WLAN_EID_DS_PARAMS: + if (elen < 1) + break; elems->ds_params = pos; - elems->ds_params_len = elen; break; case WLAN_EID_CF_PARAMS: case WLAN_EID_TIM: @@ -215,8 +238,9 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->challenge_len = elen; break; case WLAN_EID_ERP_INFO: + if (elen < 1) + break; elems->erp_info = pos; - elems->erp_info_len = elen; break; case WLAN_EID_EXT_SUPP_RATES: elems->ext_supp_rates = pos; @@ -239,24 +263,31 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->supp_channels_len = elen; break; case WLAN_EID_MOBILITY_DOMAIN: + if (elen < sizeof(struct rsn_mdie)) + break; elems->mdie = pos; elems->mdie_len = elen; break; case WLAN_EID_FAST_BSS_TRANSITION: + if (elen < sizeof(struct rsn_ftie)) + break; elems->ftie = pos; elems->ftie_len = elen; break; case WLAN_EID_TIMEOUT_INTERVAL: + if (elen != 5) + break; elems->timeout_int = pos; - elems->timeout_int_len = elen; break; case WLAN_EID_HT_CAP: + if (elen < sizeof(struct ieee80211_ht_capabilities)) + break; elems->ht_capabilities = pos; - elems->ht_capabilities_len = elen; break; case WLAN_EID_HT_OPERATION: + if (elen < sizeof(struct ieee80211_ht_operation)) + break; elems->ht_operation = pos; - elems->ht_operation_len = elen; break; case WLAN_EID_MESH_CONFIG: elems->mesh_config = pos; @@ -271,12 +302,14 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->peer_mgmt_len = elen; break; case WLAN_EID_VHT_CAP: + if (elen < sizeof(struct ieee80211_vht_capabilities)) + break; elems->vht_capabilities = pos; - elems->vht_capabilities_len = elen; break; case WLAN_EID_VHT_OPERATION: + if (elen < sizeof(struct ieee80211_vht_operation)) + break; elems->vht_operation = pos; - elems->vht_operation_len = elen; break; case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION: if (elen != 1) @@ -321,6 +354,18 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, /* after mic everything is encrypted, so stop. */ left = elen; break; + case WLAN_EID_MULTI_BAND: + if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { + wpa_printf(MSG_MSGDUMP, + "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)", + id, elen); + break; + } + + elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos; + elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen; + elems->mb_ies.nof_ies++; + break; default: unknown++; if (!show_errors) @@ -486,14 +531,14 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], ac->aifs = v; } else if (os_strcmp(pos, "cwmin") == 0) { v = atoi(val); - if (v < 0 || v > 12) { + if (v < 0 || v > 15) { wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); return -1; } ac->cwmin = v; } else if (os_strcmp(pos, "cwmax") == 0) { v = atoi(val); - if (v < 0 || v > 12) { + if (v < 0 || v > 15) { wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); return -1; } @@ -523,50 +568,163 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) { - enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES; + u8 op_class; - if (freq >= 2412 && freq <= 2472) { - mode = HOSTAPD_MODE_IEEE80211G; - *channel = (freq - 2407) / 5; - } else if (freq == 2484) { - mode = HOSTAPD_MODE_IEEE80211B; - *channel = 14; - } else if (freq >= 4900 && freq < 5000) { - mode = HOSTAPD_MODE_IEEE80211A; - *channel = (freq - 4000) / 5; - } else if (freq >= 5000 && freq < 5900) { - mode = HOSTAPD_MODE_IEEE80211A; - *channel = (freq - 5000) / 5; - } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { - mode = HOSTAPD_MODE_IEEE80211AD; - *channel = (freq - 56160) / 2160; - } - - return mode; + return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel); } -static const char *us_op_class_cc[] = { +/** + * ieee80211_freq_to_channel_ext - Convert frequency into channel info + * for HT40 and VHT. DFS channels are not covered. + * @freq: Frequency (MHz) to convert + * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below + * @vht: 0 - non-VHT, 1 - 80 MHz + * @op_class: Buffer for returning operating class + * @channel: Buffer for returning channel number + * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure + */ +enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, + int sec_channel, int vht, + u8 *op_class, u8 *channel) +{ + /* TODO: more operating classes */ + + if (sec_channel > 1 || sec_channel < -1) + return NUM_HOSTAPD_MODES; + + if (freq >= 2412 && freq <= 2472) { + if ((freq - 2407) % 5) + return NUM_HOSTAPD_MODES; + + if (vht) + return NUM_HOSTAPD_MODES; + + /* 2.407 GHz, channels 1..13 */ + if (sec_channel == 1) + *op_class = 83; + else if (sec_channel == -1) + *op_class = 84; + else + *op_class = 81; + + *channel = (freq - 2407) / 5; + + return HOSTAPD_MODE_IEEE80211G; + } + + if (freq == 2484) { + if (sec_channel || vht) + return NUM_HOSTAPD_MODES; + + *op_class = 82; /* channel 14 */ + *channel = 14; + + return HOSTAPD_MODE_IEEE80211B; + } + + if (freq >= 4900 && freq < 5000) { + if ((freq - 4000) % 5) + return NUM_HOSTAPD_MODES; + *channel = (freq - 4000) / 5; + *op_class = 0; /* TODO */ + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 36..48 */ + if (freq >= 5180 && freq <= 5240) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + if (sec_channel == 1) + *op_class = 116; + else if (sec_channel == -1) + *op_class = 117; + else if (vht) + *op_class = 128; + else + *op_class = 115; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 149..161 */ + if (freq >= 5745 && freq <= 5805) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + if (sec_channel == 1) + *op_class = 126; + else if (sec_channel == -1) + *op_class = 127; + else if (vht) + *op_class = 128; + else + *op_class = 124; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 149..169 */ + if (freq >= 5745 && freq <= 5845) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + *op_class = 125; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + if (freq >= 5000 && freq < 5900) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + *channel = (freq - 5000) / 5; + *op_class = 0; /* TODO */ + return HOSTAPD_MODE_IEEE80211A; + } + + /* 56.16 GHz, channel 1..4 */ + if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { + if (sec_channel || vht) + return NUM_HOSTAPD_MODES; + + *channel = (freq - 56160) / 2160; + *op_class = 180; + + return HOSTAPD_MODE_IEEE80211AD; + } + + return NUM_HOSTAPD_MODES; +} + + +static const char *const us_op_class_cc[] = { "US", "CA", NULL }; -static const char *eu_op_class_cc[] = { +static const char *const eu_op_class_cc[] = { "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT", "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL }; -static const char *jp_op_class_cc[] = { +static const char *const jp_op_class_cc[] = { "JP", NULL }; -static const char *cn_op_class_cc[] = { - "CN", "CA", NULL +static const char *const cn_op_class_cc[] = { + "CN", NULL }; -static int country_match(const char *cc[], const char *country) +static int country_match(const char *const cc[], const char *const country) { int i; @@ -612,6 +770,10 @@ static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan) if (chan < 149 || chan > 161) return -1; return 5000 + 5 * chan; + case 5: /* channels 149,153,157,161,165 */ + if (chan < 149 || chan > 165) + return -1; + return 5000 + 5 * chan; case 34: /* 60 GHz band, channels 1..3 */ if (chan < 1 || chan > 3) return -1; @@ -764,12 +926,15 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) return -1; return 5000 + 5 * chan; case 124: /* channels 149,153,157,161 */ - case 125: /* channels 149,153,157,161,165,169 */ case 126: /* channels 149,157; 40 MHz */ case 127: /* channels 153,161; 40 MHz */ if (chan < 149 || chan > 161) return -1; return 5000 + 5 * chan; + case 125: /* channels 149,153,157,161,165,169 */ + if (chan < 149 || chan > 169) + return -1; + return 5000 + 5 * chan; case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ if (chan < 36 || chan > 161) @@ -921,3 +1086,62 @@ const char * fc2str(u16 fc) return "WLAN_FC_TYPE_UNKNOWN"; #undef C2S } + + +int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, + size_t ies_len) +{ + os_memset(info, 0, sizeof(*info)); + + while (ies_buf && ies_len >= 2 && + info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) { + size_t len = 2 + ies_buf[1]; + + if (len > ies_len) { + wpa_hexdump(MSG_DEBUG, "Truncated IEs", + ies_buf, ies_len); + return -1; + } + + if (ies_buf[0] == WLAN_EID_MULTI_BAND) { + wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len); + info->ies[info->nof_ies].ie = ies_buf + 2; + info->ies[info->nof_ies].ie_len = ies_buf[1]; + info->nof_ies++; + } + + ies_len -= len; + ies_buf += len; + } + + return 0; +} + + +struct wpabuf * mb_ies_by_info(struct mb_ies_info *info) +{ + struct wpabuf *mb_ies = NULL; + + WPA_ASSERT(info != NULL); + + if (info->nof_ies) { + u8 i; + size_t mb_ies_size = 0; + + for (i = 0; i < info->nof_ies; i++) + mb_ies_size += 2 + info->ies[i].ie_len; + + mb_ies = wpabuf_alloc(mb_ies_size); + if (mb_ies) { + for (i = 0; i < info->nof_ies; i++) { + wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND); + wpabuf_put_u8(mb_ies, info->ies[i].ie_len); + wpabuf_put_data(mb_ies, + info->ies[i].ie, + info->ies[i].ie_len); + } + } + } + + return mb_ies; +} diff --git a/contrib/wpa/src/common/ieee802_11_common.h b/contrib/wpa/src/common/ieee802_11_common.h index 7f0b296d2b0..55ce0223d92 100644 --- a/contrib/wpa/src/common/ieee802_11_common.h +++ b/contrib/wpa/src/common/ieee802_11_common.h @@ -9,6 +9,16 @@ #ifndef IEEE802_11_COMMON_H #define IEEE802_11_COMMON_H +#define MAX_NOF_MB_IES_SUPPORTED 5 + +struct mb_ies_info { + struct { + const u8 *ie; + u8 ie_len; + } ies[MAX_NOF_MB_IES_SUPPORTED]; + u8 nof_ies; +}; + /* Parsed Information Elements */ struct ieee802_11_elems { const u8 *ssid; @@ -48,12 +58,11 @@ struct ieee802_11_elems { const u8 *osen; const u8 *ampe; const u8 *mic; + const u8 *pref_freq_list; u8 ssid_len; u8 supp_rates_len; - u8 ds_params_len; u8 challenge_len; - u8 erp_info_len; u8 ext_supp_rates_len; u8 wpa_ie_len; u8 rsn_ie_len; @@ -63,14 +72,9 @@ struct ieee802_11_elems { u8 supp_channels_len; u8 mdie_len; u8 ftie_len; - u8 timeout_int_len; - u8 ht_capabilities_len; - u8 ht_operation_len; u8 mesh_config_len; u8 mesh_id_len; u8 peer_mgmt_len; - u8 vht_capabilities_len; - u8 vht_operation_len; u8 vendor_ht_cap_len; u8 vendor_vht_len; u8 p2p_len; @@ -83,6 +87,8 @@ struct ieee802_11_elems { u8 osen_len; u8 ampe_len; u8 mic_len; + u8 pref_freq_list_len; + struct mb_ies_info mb_ies; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -108,9 +114,15 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], const char *name, const char *val); enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel); int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan); +enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, + int sec_channel, int vht, + u8 *op_class, u8 *channel); int ieee80211_is_dfs(int freq); int supp_rates_11b_only(struct ieee802_11_elems *elems); +int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, + size_t ies_len); +struct wpabuf * mb_ies_by_info(struct mb_ies_info *info); const char * fc2str(u16 fc); #endif /* IEEE802_11_COMMON_H */ diff --git a/contrib/wpa/src/common/ieee802_11_defs.h b/contrib/wpa/src/common/ieee802_11_defs.h index 2e51935b8e3..44530ce3cee 100644 --- a/contrib/wpa/src/common/ieee802_11_defs.h +++ b/contrib/wpa/src/common/ieee802_11_defs.h @@ -10,6 +10,8 @@ #ifndef IEEE802_11_DEFS_H #define IEEE802_11_DEFS_H +#include + /* IEEE 802.11 defines */ #define WLAN_FC_PVER 0x0003 @@ -163,7 +165,10 @@ #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 #define WLAN_STATUS_TRANSMISSION_FAILURE 79 +#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82 +#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86 #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95 +#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ @@ -269,6 +274,8 @@ #define WLAN_EID_AMPE 139 #define WLAN_EID_MIC 140 #define WLAN_EID_CCKM 156 +#define WLAN_EID_MULTI_BAND 158 +#define WLAN_EID_SESSION_TRANSITION 164 #define WLAN_EID_VHT_CAP 191 #define WLAN_EID_VHT_OPERATION 192 #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 @@ -297,6 +304,7 @@ #define WLAN_ACTION_TDLS 12 #define WLAN_ACTION_SELF_PROTECTED 15 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ +#define WLAN_ACTION_FST 18 #define WLAN_ACTION_VENDOR_SPECIFIC 127 /* Public action codes */ @@ -470,35 +478,35 @@ struct ieee80211_mgmt { le16 auth_transaction; le16 status_code; /* possibly followed by Challenge text */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED auth; struct { le16 reason_code; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED deauth; struct { le16 capab_info; le16 listen_interval; /* followed by SSID and Supported rates */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED assoc_req; struct { le16 capab_info; le16 status_code; le16 aid; /* followed by Supported rates */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED assoc_resp, reassoc_resp; struct { le16 capab_info; le16 listen_interval; u8 current_ap[6]; /* followed by SSID and Supported rates */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED reassoc_req; struct { le16 reason_code; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED disassoc; struct { u8 timestamp[8]; @@ -506,7 +514,7 @@ struct ieee80211_mgmt { le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params, TIM */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED beacon; struct { /* only variable items: SSID, Supported rates */ @@ -518,7 +526,7 @@ struct ieee80211_mgmt { le16 capab_info; /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED probe_resp; struct { u8 category; @@ -527,7 +535,7 @@ struct ieee80211_mgmt { u8 action_code; u8 dialog_token; u8 status_code; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED wmm_action; struct{ u8 action_code; @@ -541,14 +549,14 @@ struct ieee80211_mgmt { u8 action; u8 sta_addr[ETH_ALEN]; u8 target_ap_addr[ETH_ALEN]; - u8 variable[0]; /* FT Request */ + u8 variable[]; /* FT Request */ } STRUCT_PACKED ft_action_req; struct { u8 action; u8 sta_addr[ETH_ALEN]; u8 target_ap_addr[ETH_ALEN]; le16 status_code; - u8 variable[0]; /* FT Request */ + u8 variable[]; /* FT Request */ } STRUCT_PACKED ft_action_resp; struct { u8 action; @@ -561,23 +569,23 @@ struct ieee80211_mgmt { struct { u8 action; u8 dialogtoken; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED wnm_sleep_req; struct { u8 action; u8 dialogtoken; le16 keydata_len; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED wnm_sleep_resp; struct { u8 action; - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED public_action; struct { u8 action; /* 9 */ u8 oui[3]; /* Vendor-specific content */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED vs_public_action; struct { u8 action; /* 7 */ @@ -589,7 +597,7 @@ struct ieee80211_mgmt { * Session Information URL (optional), * BSS Transition Candidate List * Entries */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED bss_tm_req; struct { u8 action; /* 8 */ @@ -599,7 +607,7 @@ struct ieee80211_mgmt { /* Target BSSID (optional), * BSS Transition Candidate List * Entries (optional) */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED bss_tm_resp; struct { u8 action; /* 6 */ @@ -607,12 +615,16 @@ struct ieee80211_mgmt { u8 query_reason; /* BSS Transition Candidate List * Entries (optional) */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED bss_tm_query; struct { u8 action; /* 15 */ - u8 variable[0]; + u8 variable[]; } STRUCT_PACKED slf_prot_action; + struct { + u8 action; + u8 variable[]; + } STRUCT_PACKED fst_action; } u; } STRUCT_PACKED action; } u; @@ -1065,6 +1077,15 @@ enum p2p_attr_id { #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) #define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7) +/* P2PS Coordination Protocol Transport Bitmap */ +#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0) +#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1) + +struct p2ps_feature_capab { + u8 cpt; + u8 reserved; +} STRUCT_PACKED; + /* Invitation Flags */ #define P2P_INVITATION_FLAGS_TYPE BIT(0) @@ -1354,4 +1375,62 @@ struct rrm_link_measurement_report { u8 variable[0]; } STRUCT_PACKED; +#define SSID_MAX_LEN 32 + +/* IEEE Std 802.11ad-2012 - Multi-band element */ +struct multi_band_ie { + u8 eid; /* WLAN_EID_MULTI_BAND */ + u8 len; + u8 mb_ctrl; + u8 band_id; + u8 op_class; + u8 chan; + u8 bssid[ETH_ALEN]; + le16 beacon_int; + u8 tsf_offs[8]; + u8 mb_connection_capability; + u8 fst_session_tmout; + /* Optional: + * STA MAC Address + * Pairwise Cipher Suite Count + * Pairwise Cipher Suite List + */ + u8 variable[0]; +} STRUCT_PACKED; + +enum mb_ctrl_sta_role { + MB_STA_ROLE_AP = 0, + MB_STA_ROLE_TDLS_STA = 1, + MB_STA_ROLE_IBSS_STA = 2, + MB_STA_ROLE_PCP = 3, + MB_STA_ROLE_NON_PCP_NON_AP = 4 +}; + +#define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK)) +#define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3))) +#define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4))) + +enum mb_band_id { + MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */ + MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */ + MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */ +}; + +#define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0))) +#define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1))) +#define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2))) +#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3))) +#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4))) + +/* IEEE Std 802.11ad-2014 - FST Action field */ +enum fst_action { + FST_ACTION_SETUP_REQUEST = 0, + FST_ACTION_SETUP_RESPONSE = 1, + FST_ACTION_TEAR_DOWN = 2, + FST_ACTION_ACK_REQUEST = 3, + FST_ACTION_ACK_RESPONSE = 4, + FST_ACTION_ON_CHANNEL_TUNNEL = 5, +}; + #endif /* IEEE802_11_DEFS_H */ diff --git a/contrib/wpa/src/common/privsep_commands.h b/contrib/wpa/src/common/privsep_commands.h index 4dc34c4ad32..8dff30382b6 100644 --- a/contrib/wpa/src/common/privsep_commands.h +++ b/contrib/wpa/src/common/privsep_commands.h @@ -9,6 +9,8 @@ #ifndef PRIVSEP_COMMANDS_H #define PRIVSEP_COMMANDS_H +#include "common/ieee802_11_defs.h" + enum privsep_cmd { PRIVSEP_CMD_REGISTER, PRIVSEP_CMD_UNREGISTER, @@ -24,12 +26,31 @@ enum privsep_cmd { PRIVSEP_CMD_L2_NOTIFY_AUTH_START, PRIVSEP_CMD_L2_SEND, PRIVSEP_CMD_SET_COUNTRY, + PRIVSEP_CMD_AUTHENTICATE, +}; + +struct privsep_cmd_authenticate +{ + int freq; + u8 bssid[ETH_ALEN]; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + int auth_alg; + size_t ie_len; + u8 wep_key[4][16]; + size_t wep_key_len[4]; + int wep_tx_keyidx; + int local_state_change; + int p2p; + size_t sae_data_len; + /* followed by ie_len bytes of ie */ + /* followed by sae_data_len bytes of sae_data */ }; struct privsep_cmd_associate { u8 bssid[ETH_ALEN]; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int hwmode; int freq; @@ -66,6 +87,18 @@ enum privsep_event { PRIVSEP_EVENT_STKSTART, PRIVSEP_EVENT_FT_RESPONSE, PRIVSEP_EVENT_RX_EAPOL, + PRIVSEP_EVENT_SCAN_STARTED, + PRIVSEP_EVENT_AUTH, +}; + +struct privsep_event_auth { + u8 peer[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u16 auth_type; + u16 auth_transaction; + u16 status_code; + size_t ies_len; + /* followed by ies_len bytes of ies */ }; #endif /* PRIVSEP_COMMANDS_H */ diff --git a/contrib/wpa/src/common/qca-vendor.h b/contrib/wpa/src/common/qca-vendor.h index 2117ee7028c..28985f5194e 100644 --- a/contrib/wpa/src/common/qca-vendor.h +++ b/contrib/wpa/src/common/qca-vendor.h @@ -132,7 +132,7 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50, QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51, QCA_NL80211_VENDOR_SUBCMD_APFIND = 52, - /* 53 - reserved for QCA */ + /* 53 - reserved - was used by QCA, but not in use anymore */ QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54, QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED = 56, @@ -142,6 +142,20 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60, /* 61-90 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91, + QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92, + QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93, + QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT = 94, + QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT = 95, + QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER = 96, + QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS = 97, + QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS = 98, + QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99, + QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100, + QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101, + QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG = 102, + QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103, + QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104, + QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105, }; @@ -162,6 +176,15 @@ enum qca_wlan_vendor_attr { /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7, QCA_WLAN_VENDOR_ATTR_TEST = 8, + /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ + /* Unsigned 32-bit value. */ + QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA = 9, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11, + /* Unsigned 32-bit value from enum qca_set_band. */ + QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, @@ -195,6 +218,12 @@ enum qca_wlan_vendor_attr_acs_offload { QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED, + QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH, + QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST, /* keep last */ QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ACS_MAX = @@ -206,6 +235,7 @@ enum qca_wlan_vendor_acs_hw_mode { QCA_ACS_MODE_IEEE80211G, QCA_ACS_MODE_IEEE80211A, QCA_ACS_MODE_IEEE80211AD, + QCA_ACS_MODE_IEEE80211ANY, }; /** @@ -215,10 +245,13 @@ enum qca_wlan_vendor_acs_hw_mode { * management offload, a mechanism where the station's firmware * does the exchange with the AP to establish the temporal keys * after roaming, rather than having the user space wpa_supplicant do it. + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic + * band selection based on channel selection results. * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -243,4 +276,82 @@ enum qca_wlan_vendor_attr_data_offload_ind { QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX = QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1 }; + +enum qca_vendor_attr_get_preferred_freq_list { + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID, + /* A 32-unsigned value; the interface type/mode for which the preferred + * frequency list is requested (see enum qca_iface_type for possible + * values); used in GET_PREFERRED_FREQ_LIST command from user-space to + * kernel and in the kernel response back to user-space. + */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE, + /* An array of 32-unsigned values; values are frequency (MHz); sent + * from kernel space to user space. + */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX = + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST - 1 +}; + +enum qca_vendor_attr_probable_oper_channel { + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_INVALID, + /* 32-bit unsigned value; indicates the connection/iface type likely to + * come on this channel (see enum qca_iface_type). + */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE, + /* 32-bit unsigned value; the frequency (MHz) of the probable channel */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX = + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST - 1 +}; + +enum qca_iface_type { + QCA_IFACE_TYPE_STA, + QCA_IFACE_TYPE_AP, + QCA_IFACE_TYPE_P2P_CLIENT, + QCA_IFACE_TYPE_P2P_GO, + QCA_IFACE_TYPE_IBSS, + QCA_IFACE_TYPE_TDLS, +}; + +enum qca_set_band { + QCA_SETBAND_AUTO, + QCA_SETBAND_5G, + QCA_SETBAND_2G, +}; + +/* IEEE 802.11 Vendor Specific elements */ + +/** + * enum qca_vendor_element_id - QCA Vendor Specific element types + * + * These values are used to identify QCA Vendor Specific elements. The + * payload of the element starts with the three octet OUI (OUI_QCA) and + * is followed by a single octet type which is defined by this enum. + * + * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list. + * This element can be used to specify preference order for supported + * channels. The channels in this list are in preference order (the first + * one has the highest preference) and are described as a pair of + * (global) Operating Class and Channel Number (each one octet) fields. + * + * This extends the standard P2P functionality by providing option to have + * more than one preferred operating channel. When this element is present, + * it replaces the preference indicated in the Operating Channel attribute. + * For supporting other implementations, the Operating Channel attribute is + * expected to be used with the highest preference channel. Similarly, all + * the channels included in this Preferred channel list element are + * expected to be included in the Channel List attribute. + * + * This vendor element may be included in GO Negotiation Request, P2P + * Invitation Request, and Provision Discovery Request frames. + */ +enum qca_vendor_element_id { + QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0, +}; + #endif /* QCA_VENDOR_H */ diff --git a/contrib/wpa/src/common/sae.c b/contrib/wpa/src/common/sae.c index 588895808fd..503fa1d7b9a 100644 --- a/contrib/wpa/src/common/sae.c +++ b/contrib/wpa/src/common/sae.c @@ -1,6 +1,6 @@ /* * Simultaneous authentication of equals - * Copyright (c) 2012-2013, Jouni Malinen + * Copyright (c) 2012-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -124,9 +124,7 @@ static struct crypto_bignum * sae_get_rand(struct sae_data *sae) return NULL; for (;;) { - if (iter++ > 100) - return NULL; - if (random_get_bytes(val, order_len) < 0) + if (iter++ > 100 || random_get_bytes(val, order_len) < 0) return NULL; if (order_len_bits % 8) buf_shift_right(val, order_len, 8 - order_len_bits % 8); @@ -171,17 +169,107 @@ static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) } -static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, - struct crypto_ec_point *pwe) +static struct crypto_bignum * +get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, + int *r_odd) { - u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN]; - struct crypto_bignum *x; - int y_bit; + for (;;) { + struct crypto_bignum *r; + u8 tmp[SAE_MAX_ECC_PRIME_LEN]; + + if (random_get_bytes(tmp, prime_len) < 0) + break; + if (prime_bits % 8) + buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); + if (os_memcmp(tmp, prime, prime_len) >= 0) + continue; + r = crypto_bignum_init_set(tmp, prime_len); + if (!r) + break; + if (crypto_bignum_is_zero(r)) { + crypto_bignum_deinit(r, 0); + continue; + } + + *r_odd = tmp[prime_len - 1] & 0x01; + return r; + } + + return NULL; +} + + +static int is_quadratic_residue_blind(struct sae_data *sae, + const u8 *prime, size_t bits, + const struct crypto_bignum *qr, + const struct crypto_bignum *qnr, + const struct crypto_bignum *y_sqr) +{ + struct crypto_bignum *r, *num; + int r_odd, check, res = -1; + + /* + * Use the blinding technique to mask y_sqr while determining + * whether it is a quadratic residue modulo p to avoid leaking + * timing information while determining the Legendre symbol. + * + * v = y_sqr + * r = a random number between 1 and p-1, inclusive + * num = (v * r * r) modulo p + */ + r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd); + if (!r) + return -1; + + num = crypto_bignum_init(); + if (!num || + crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 || + crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0) + goto fail; + + if (r_odd) { + /* + * num = (num * qr) module p + * LGR(num, p) = 1 ==> quadratic residue + */ + if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0) + goto fail; + check = 1; + } else { + /* + * num = (num * qnr) module p + * LGR(num, p) = -1 ==> quadratic residue + */ + if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0) + goto fail; + check = -1; + } + + res = crypto_bignum_legendre(num, sae->tmp->prime); + if (res == -2) { + res = -1; + goto fail; + } + res = res == check; +fail: + crypto_bignum_deinit(num, 1); + crypto_bignum_deinit(r, 1); + return res; +} + + +static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, + const u8 *prime, + const struct crypto_bignum *qr, + const struct crypto_bignum *qnr, + struct crypto_bignum **ret_x_cand) +{ + u8 pwd_value[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *y_sqr, *x_cand; + int res; size_t bits; - if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), - sae->tmp->prime_len) < 0) - return -1; + *ret_x_cand = NULL; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); @@ -197,20 +285,23 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) return 0; - y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; - - x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); - if (x == NULL) + x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + if (!x_cand) + return -1; + y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); + if (!y_sqr) { + crypto_bignum_deinit(x_cand, 1); return -1; - if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) { - crypto_bignum_deinit(x, 0); - wpa_printf(MSG_DEBUG, "SAE: No solution found"); - return 0; } - crypto_bignum_deinit(x, 0); - wpa_printf(MSG_DEBUG, "SAE: PWE found"); + res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); + crypto_bignum_deinit(y_sqr, 1); + if (res <= 0) { + crypto_bignum_deinit(x_cand, 1); + return res; + } + *ret_x_cand = x_cand; return 1; } @@ -288,24 +379,77 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, } +static int get_random_qr_qnr(const u8 *prime, size_t prime_len, + const struct crypto_bignum *prime_bn, + size_t prime_bits, struct crypto_bignum **qr, + struct crypto_bignum **qnr) +{ + *qr = NULL; + *qnr = NULL; + + while (!(*qr) || !(*qnr)) { + u8 tmp[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *q; + int res; + + if (random_get_bytes(tmp, prime_len) < 0) + break; + if (prime_bits % 8) + buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); + if (os_memcmp(tmp, prime, prime_len) >= 0) + continue; + q = crypto_bignum_init_set(tmp, prime_len); + if (!q) + break; + res = crypto_bignum_legendre(q, prime_bn); + + if (res == 1 && !(*qr)) + *qr = q; + else if (res == -1 && !(*qnr)) + *qnr = q; + else + crypto_bignum_deinit(q, 0); + } + + return (*qr && *qnr) ? 0 : -1; +} + + static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len) { - u8 counter, k = 4; + u8 counter, k = 40; u8 addrs[2 * ETH_ALEN]; const u8 *addr[2]; size_t len[2]; - int found = 0; - struct crypto_ec_point *pwe_tmp; + u8 dummy_password[32]; + size_t dummy_password_len; + int pwd_seed_odd = 0; + u8 prime[SAE_MAX_ECC_PRIME_LEN]; + size_t prime_len; + struct crypto_bignum *x = NULL, *qr, *qnr; + size_t bits; + int res; - if (sae->tmp->pwe_ecc == NULL) { - sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); - if (sae->tmp->pwe_ecc == NULL) - return -1; - } - pwe_tmp = crypto_ec_point_init(sae->tmp->ec); - if (pwe_tmp == NULL) + dummy_password_len = password_len; + if (dummy_password_len > sizeof(dummy_password)) + dummy_password_len = sizeof(dummy_password); + if (random_get_bytes(dummy_password, dummy_password_len) < 0) + return -1; + + prime_len = sae->tmp->prime_len; + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + prime_len) < 0) + return -1; + bits = crypto_ec_prime_len_bits(sae->tmp->ec); + + /* + * Create a random quadratic residue (qr) and quadratic non-residue + * (qnr) modulo p for blinding purposes during the loop. + */ + if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, + &qr, &qnr) < 0) return -1; wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", @@ -313,8 +457,9 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * base = password * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), - * password || counter) + * base || counter) */ sae_pwd_seed_key(addr1, addr2, addrs); @@ -328,9 +473,9 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, * attacks that attempt to determine the number of iterations required * in the loop. */ - for (counter = 1; counter < k || !found; counter++) { + for (counter = 1; counter <= k || !x; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; - int res; + struct crypto_bignum *x_cand; if (counter > 200) { /* This should not happen in practice */ @@ -342,25 +487,58 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, pwd_seed) < 0) break; + res = sae_test_pwd_seed_ecc(sae, pwd_seed, - found ? pwe_tmp : - sae->tmp->pwe_ecc); + prime, qr, qnr, &x_cand); if (res < 0) - break; - if (res == 0) - continue; - if (found) { - wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was " - "already selected)"); - } else { - wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); - found = 1; + goto fail; + if (res > 0 && !x) { + wpa_printf(MSG_DEBUG, + "SAE: Selected pwd-seed with counter %u", + counter); + x = x_cand; + pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; + os_memset(pwd_seed, 0, sizeof(pwd_seed)); + + /* + * Use a dummy password for the following rounds, if + * any. + */ + addr[0] = dummy_password; + len[0] = dummy_password_len; + } else if (res > 0) { + crypto_bignum_deinit(x_cand, 1); } } - crypto_ec_point_deinit(pwe_tmp, 1); + if (!x) { + wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); + res = -1; + goto fail; + } - return found ? 0 : -1; + if (!sae->tmp->pwe_ecc) + sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); + if (!sae->tmp->pwe_ecc) + res = -1; + else + res = crypto_ec_point_solve_y_coord(sae->tmp->ec, + sae->tmp->pwe_ecc, x, + pwd_seed_odd); + crypto_bignum_deinit(x, 1); + if (res < 0) { + /* + * This should not happen since we already checked that there + * is a result. + */ + wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); + } + +fail: + crypto_bignum_deinit(qr, 0); + crypto_bignum_deinit(qnr, 0); + + return res; } @@ -472,27 +650,41 @@ static int sae_derive_commit(struct sae_data *sae) { struct crypto_bignum *mask; int ret = -1; + unsigned int counter = 0; - mask = sae_get_rand_and_mask(sae); - if (mask == NULL) { - wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); - return -1; - } + do { + counter++; + if (counter > 100) { + /* + * This cannot really happen in practice if the random + * number generator is working. Anyway, to avoid even a + * theoretical infinite loop, break out after 100 + * attemps. + */ + return -1; + } - /* commit-scalar = (rand + mask) modulo r */ - if (!sae->tmp->own_commit_scalar) { - sae->tmp->own_commit_scalar = crypto_bignum_init(); - if (!sae->tmp->own_commit_scalar) - goto fail; - } - crypto_bignum_add(sae->tmp->sae_rand, mask, - sae->tmp->own_commit_scalar); - crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, - sae->tmp->own_commit_scalar); + mask = sae_get_rand_and_mask(sae); + if (mask == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); + return -1; + } - if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) - goto fail; - if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0) + /* commit-scalar = (rand + mask) modulo r */ + if (!sae->tmp->own_commit_scalar) { + sae->tmp->own_commit_scalar = crypto_bignum_init(); + if (!sae->tmp->own_commit_scalar) + goto fail; + } + crypto_bignum_add(sae->tmp->sae_rand, mask, + sae->tmp->own_commit_scalar); + crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, + sae->tmp->own_commit_scalar); + } while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) || + crypto_bignum_is_one(sae->tmp->own_commit_scalar)); + + if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) || + (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)) goto fail; ret = 0; @@ -506,15 +698,12 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, struct sae_data *sae) { - if (sae->tmp == NULL) - return -1; - if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, - password_len) < 0) - return -1; - if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, - password_len) < 0) - return -1; - if (sae_derive_commit(sae) < 0) + if (sae->tmp == NULL || + (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, + password_len) < 0) || + (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, + password_len) < 0) || + sae_derive_commit(sae) < 0) return -1; return 0; } @@ -780,8 +969,9 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, return WLAN_STATUS_UNSPECIFIED_FAILURE; } - /* 0 < scalar < r */ + /* 1 < scalar < r */ if (crypto_bignum_is_zero(peer_scalar) || + crypto_bignum_is_one(peer_scalar) || crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); crypto_bignum_deinit(peer_scalar, 0); @@ -847,7 +1037,8 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, const u8 *end) { - struct crypto_bignum *res; + struct crypto_bignum *res, *one; + const u8 one_bin[1] = { 0x01 }; if (pos + sae->tmp->prime_len > end) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " @@ -862,18 +1053,23 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, crypto_bignum_init_set(pos, sae->tmp->prime_len); if (sae->tmp->peer_commit_element_ffc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || + /* 1 < element < p - 1 */ + res = crypto_bignum_init(); + one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); + if (!res || !one || + crypto_bignum_sub(sae->tmp->prime, one, res) || + crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || - crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, - sae->tmp->prime) >= 0) { + crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { + crypto_bignum_deinit(res, 0); + crypto_bignum_deinit(one, 0); wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } + crypto_bignum_deinit(one, 0); /* scalar-op(r, ELEMENT) = 1 modulo p */ - res = crypto_bignum_init(); - if (res == NULL || - crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, + if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, sae->tmp->order, sae->tmp->prime, res) < 0 || !crypto_bignum_is_one(res)) { wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); @@ -918,7 +1114,34 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, return res; /* commit-element */ - return sae_parse_commit_element(sae, pos, end); + res = sae_parse_commit_element(sae, pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* + * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as + * the values we sent which would be evidence of a reflection attack. + */ + if (!sae->tmp->own_commit_scalar || + crypto_bignum_cmp(sae->tmp->own_commit_scalar, + sae->peer_commit_scalar) != 0 || + (sae->tmp->dh && + (!sae->tmp->own_commit_element_ffc || + crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, + sae->tmp->peer_commit_element_ffc) != 0)) || + (sae->tmp->ec && + (!sae->tmp->own_commit_element_ecc || + crypto_ec_point_cmp(sae->tmp->ec, + sae->tmp->own_commit_element_ecc, + sae->tmp->peer_commit_element_ecc) != 0))) + return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ + + /* + * This is a reflection attack - return special value to trigger caller + * to silently discard the frame instead of replying with a specific + * status code. + */ + return SAE_SILENTLY_DISCARD; } diff --git a/contrib/wpa/src/common/sae.h b/contrib/wpa/src/common/sae.h index 3ebf40cf4a4..c07026cd497 100644 --- a/contrib/wpa/src/common/sae.h +++ b/contrib/wpa/src/common/sae.h @@ -18,6 +18,9 @@ #define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN) #define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN) +/* Special value returned by sae_parse_commit() */ +#define SAE_SILENTLY_DISCARD 65535 + struct sae_temporary_data { u8 kck[SAE_KCK_LEN]; struct crypto_bignum *own_commit_scalar; diff --git a/contrib/wpa/src/common/version.h b/contrib/wpa/src/common/version.h index e39a8dbf92e..a5cc5b7b5bc 100644 --- a/contrib/wpa/src/common/version.h +++ b/contrib/wpa/src/common/version.h @@ -5,6 +5,6 @@ #define VERSION_STR_POSTFIX "" #endif /* VERSION_STR_POSTFIX */ -#define VERSION_STR "2.4" VERSION_STR_POSTFIX +#define VERSION_STR "2.5" VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/contrib/wpa/src/common/wpa_common.c b/contrib/wpa/src/common/wpa_common.c index de81d53694c..e9d4248d72d 100644 --- a/contrib/wpa/src/common/wpa_common.c +++ b/contrib/wpa/src/common/wpa_common.c @@ -170,6 +170,12 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, ptk->tk_len = wpa_cipher_key_len(cipher); ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; +#ifdef CONFIG_SUITEB192 + if (wpa_key_mgmt_sha384(akmp)) + sha384_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len); + else +#endif /* CONFIG_SUITEB192 */ #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(akmp)) sha256_prf(pmk, pmk_len, label, data, sizeof(data), @@ -207,8 +213,10 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, const u8 *rsnie, size_t rsnie_len, const u8 *ric, size_t ric_len, u8 *mic) { - u8 *buf, *pos; - size_t buf_len; + const u8 *addr[9]; + size_t len[9]; + size_t i, num_elem = 0; + u8 zero_mic[16]; if (kck_len != 16) { wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u", @@ -216,48 +224,58 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, return -1; } - buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len; - buf = os_malloc(buf_len); - if (buf == NULL) - return -1; + addr[num_elem] = sta_addr; + len[num_elem] = ETH_ALEN; + num_elem++; + + addr[num_elem] = ap_addr; + len[num_elem] = ETH_ALEN; + num_elem++; + + addr[num_elem] = &transaction_seqnum; + len[num_elem] = 1; + num_elem++; - pos = buf; - os_memcpy(pos, sta_addr, ETH_ALEN); - pos += ETH_ALEN; - os_memcpy(pos, ap_addr, ETH_ALEN); - pos += ETH_ALEN; - *pos++ = transaction_seqnum; if (rsnie) { - os_memcpy(pos, rsnie, rsnie_len); - pos += rsnie_len; + addr[num_elem] = rsnie; + len[num_elem] = rsnie_len; + num_elem++; } if (mdie) { - os_memcpy(pos, mdie, mdie_len); - pos += mdie_len; + addr[num_elem] = mdie; + len[num_elem] = mdie_len; + num_elem++; } if (ftie) { - struct rsn_ftie *_ftie; - os_memcpy(pos, ftie, ftie_len); - if (ftie_len < 2 + sizeof(*_ftie)) { - os_free(buf); + if (ftie_len < 2 + sizeof(struct rsn_ftie)) return -1; - } - _ftie = (struct rsn_ftie *) (pos + 2); - os_memset(_ftie->mic, 0, sizeof(_ftie->mic)); - pos += ftie_len; + + /* IE hdr and mic_control */ + addr[num_elem] = ftie; + len[num_elem] = 2 + 2; + num_elem++; + + /* MIC field with all zeros */ + os_memset(zero_mic, 0, sizeof(zero_mic)); + addr[num_elem] = zero_mic; + len[num_elem] = sizeof(zero_mic); + num_elem++; + + /* Rest of FTIE */ + addr[num_elem] = ftie + 2 + 2 + 16; + len[num_elem] = ftie_len - (2 + 2 + 16); + num_elem++; } if (ric) { - os_memcpy(pos, ric, ric_len); - pos += ric_len; + addr[num_elem] = ric; + len[num_elem] = ric_len; + num_elem++; } - wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf); - if (omac1_aes_128(kck, buf, pos - buf, mic)) { - os_free(buf); + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]); + if (omac1_aes_128_vector(kck, num_elem, addr, len, mic)) return -1; - } - - os_free(buf); return 0; } @@ -344,6 +362,8 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, parse->rsn_pmkid = data.pmkid; break; case WLAN_EID_MOBILITY_DOMAIN: + if (pos[1] < sizeof(struct rsn_mdie)) + return -1; parse->mdie = pos + 2; parse->mdie_len = pos[1]; break; @@ -356,6 +376,8 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, return -1; break; case WLAN_EID_TIMEOUT_INTERVAL: + if (pos[1] != 5) + break; parse->tie = pos + 2; parse->tie_len = pos[1]; break; @@ -416,14 +438,10 @@ static int rsn_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40) - return WPA_CIPHER_WEP40; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP) return WPA_CIPHER_TKIP; if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP) return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104) - return WPA_CIPHER_WEP104; #ifdef CONFIG_IEEE80211W if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) return WPA_CIPHER_AES_128_CMAC; @@ -474,15 +492,15 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) return WPA_KEY_MGMT_IEEE8021X_SUITE_B; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192) return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN) + return WPA_KEY_MGMT_OSEN; return 0; } -static int wpa_cipher_valid_group(int cipher) +int wpa_cipher_valid_group(int cipher) { return wpa_cipher_valid_pairwise(cipher) || - cipher == WPA_CIPHER_WEP104 || - cipher == WPA_CIPHER_WEP40 || cipher == WPA_CIPHER_GTK_NOT_USED; } @@ -508,7 +526,6 @@ int wpa_cipher_valid_mgmt_group(int cipher) int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data) { - const struct rsn_ie_hdr *hdr; const u8 *pos; int left; int i, count; @@ -538,19 +555,30 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, return -1; } - hdr = (const struct rsn_ie_hdr *) rsn_ie; + if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 && + rsn_ie[1] == rsn_ie_len - 2 && + WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) { + pos = rsn_ie + 6; + left = rsn_ie_len - 6; - if (hdr->elem_id != WLAN_EID_RSN || - hdr->len != rsn_ie_len - 2 || - WPA_GET_LE16(hdr->version) != RSN_VERSION) { - wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", - __func__); - return -2; + data->proto = WPA_PROTO_OSEN; + } else { + const struct rsn_ie_hdr *hdr; + + hdr = (const struct rsn_ie_hdr *) rsn_ie; + + if (hdr->elem_id != WLAN_EID_RSN || + hdr->len != rsn_ie_len - 2 || + WPA_GET_LE16(hdr->version) != RSN_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = rsn_ie_len - sizeof(*hdr); } - pos = (const u8 *) (hdr + 1); - left = rsn_ie_len - sizeof(*hdr); - if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); if (!wpa_cipher_valid_group(data->group_cipher)) { @@ -667,14 +695,10 @@ static int wpa_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) - return WPA_CIPHER_WEP40; if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) return WPA_CIPHER_TKIP; if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) - return WPA_CIPHER_WEP104; return 0; } @@ -709,11 +733,6 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, data->num_pmkid = 0; data->mgmt_group_cipher = 0; - if (wpa_ie_len == 0) { - /* No WPA IE - fail silently */ - return -1; - } - if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", __func__, (unsigned long) wpa_ie_len); @@ -814,7 +833,7 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name) { - u8 buf[1 + WPA_MAX_SSID_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + + u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + FT_R0KH_ID_MAX_LEN + ETH_ALEN]; u8 *pos, r0_key_data[48], hash[32]; const u8 *addr[2]; @@ -828,7 +847,7 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, * PMK-R0 = L(R0-Key-Data, 0, 256) * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128) */ - if (ssid_len > WPA_MAX_SSID_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) + if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) return; pos = buf; *pos++ = ssid_len; @@ -1279,6 +1298,9 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) os_memmove(rpos + 2, rpos, end - rpos); *rpos++ = 0; *rpos++ = 0; + added += 2; + start[1] += 2; + rend = rpos; } else { /* Skip RSN Capabilities */ rpos += 2; @@ -1291,7 +1313,7 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) if (rpos == rend) { /* No PMKID-Count field included; add it */ - os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos); + os_memmove(rpos + 2 + PMKID_LEN, rpos, end + added - rpos); WPA_PUT_LE16(rpos, 1); rpos += 2; os_memcpy(rpos, pmkid, PMKID_LEN); @@ -1306,7 +1328,7 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) } WPA_PUT_LE16(rpos, 1); rpos += 2; - os_memmove(rpos + PMKID_LEN, rpos, end - rpos); + os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos); os_memcpy(rpos, pmkid, PMKID_LEN); added += PMKID_LEN; start[1] += PMKID_LEN; @@ -1335,10 +1357,6 @@ int wpa_cipher_key_len(int cipher) return 16; case WPA_CIPHER_TKIP: return 32; - case WPA_CIPHER_WEP104: - return 13; - case WPA_CIPHER_WEP40: - return 5; } return 0; @@ -1354,9 +1372,6 @@ int wpa_cipher_rsc_len(int cipher) case WPA_CIPHER_GCMP: case WPA_CIPHER_TKIP: return 6; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return 0; } return 0; @@ -1376,9 +1391,6 @@ int wpa_cipher_to_alg(int cipher) return WPA_ALG_GCMP; case WPA_CIPHER_TKIP: return WPA_ALG_TKIP; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return WPA_ALG_WEP; case WPA_CIPHER_AES_128_CMAC: return WPA_ALG_IGTK; case WPA_CIPHER_BIP_GMAC_128: @@ -1416,12 +1428,6 @@ u32 wpa_cipher_to_suite(int proto, int cipher) if (cipher & WPA_CIPHER_TKIP) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); - if (cipher & WPA_CIPHER_WEP104) - return (proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); - if (cipher & WPA_CIPHER_WEP40) - return (proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); if (cipher & WPA_CIPHER_NONE) return (proto == WPA_PROTO_RSN ? RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); @@ -1525,10 +1531,6 @@ int wpa_pick_group_cipher(int ciphers) return WPA_CIPHER_GTK_NOT_USED; if (ciphers & WPA_CIPHER_TKIP) return WPA_CIPHER_TKIP; - if (ciphers & WPA_CIPHER_WEP104) - return WPA_CIPHER_WEP104; - if (ciphers & WPA_CIPHER_WEP40) - return WPA_CIPHER_WEP40; return -1; } @@ -1626,20 +1628,6 @@ int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) return -1; pos += ret; } - if (ciphers & WPA_CIPHER_WEP104) { - ret = os_snprintf(pos, end - pos, "%sWEP104", - pos == start ? "" : delim); - if (os_snprintf_error(end - pos, ret)) - return -1; - pos += ret; - } - if (ciphers & WPA_CIPHER_WEP40) { - ret = os_snprintf(pos, end - pos, "%sWEP40", - pos == start ? "" : delim); - if (os_snprintf_error(end - pos, ret)) - return -1; - pos += ret; - } if (ciphers & WPA_CIPHER_NONE) { ret = os_snprintf(pos, end - pos, "%sNONE", pos == start ? "" : delim); diff --git a/contrib/wpa/src/common/wpa_common.h b/contrib/wpa/src/common/wpa_common.h index 091e317fdd6..c08f6514ab5 100644 --- a/contrib/wpa/src/common/wpa_common.h +++ b/contrib/wpa/src/common/wpa_common.h @@ -9,8 +9,6 @@ #ifndef WPA_COMMON_H #define WPA_COMMON_H -#define WPA_MAX_SSID_LEN 32 - /* IEEE 802.11i */ #define PMKID_LEN 16 #define PMK_LEN 32 @@ -24,8 +22,8 @@ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) #define WPA_ALLOWED_GROUP_CIPHERS \ -(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \ -WPA_CIPHER_WEP40 | WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ +(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \ +WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ WPA_CIPHER_GTK_NOT_USED) #define WPA_SELECTOR_LEN 4 @@ -42,13 +40,8 @@ WPA_CIPHER_GTK_NOT_USED) #define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) #define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0) #define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) -#define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) -#if 0 -#define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3) -#endif #define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4) -#define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5) #define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) @@ -70,13 +63,11 @@ RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) -#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2) #if 0 #define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #endif #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4) -#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) @@ -308,7 +299,6 @@ struct wpa_igtk_kde { } STRUCT_PACKED; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R struct rsn_mdie { u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 ft_capab; @@ -336,7 +326,6 @@ struct rsn_rdie { le16 status_code; } STRUCT_PACKED; -#endif /* CONFIG_IEEE80211R */ #ifdef _MSC_VER #pragma pack(pop) @@ -446,6 +435,7 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); int wpa_cipher_key_len(int cipher); int wpa_cipher_rsc_len(int cipher); int wpa_cipher_to_alg(int cipher); +int wpa_cipher_valid_group(int cipher); int wpa_cipher_valid_pairwise(int cipher); int wpa_cipher_valid_mgmt_group(int cipher); u32 wpa_cipher_to_suite(int proto, int cipher); diff --git a/contrib/wpa/src/common/wpa_ctrl.c b/contrib/wpa/src/common/wpa_ctrl.c index ccaaf1b056b..5733aa605d1 100644 --- a/contrib/wpa/src/common/wpa_ctrl.c +++ b/contrib/wpa/src/common/wpa_ctrl.c @@ -21,6 +21,7 @@ #ifdef ANDROID #include +#include #include #include "private/android_filesystem_config.h" #endif /* ANDROID */ @@ -83,6 +84,13 @@ struct wpa_ctrl { struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + return wpa_ctrl_open2(ctrl_path, NULL); +} + + +struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, + const char *cli_path) { struct wpa_ctrl *ctrl; static int counter = 0; @@ -107,10 +115,18 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) ctrl->local.sun_family = AF_UNIX; counter++; try_again: - ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), - CONFIG_CTRL_IFACE_CLIENT_DIR "/" - CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", - (int) getpid(), counter); + if (cli_path && cli_path[0] == '/') { + ret = os_snprintf(ctrl->local.sun_path, + sizeof(ctrl->local.sun_path), + "%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + cli_path, (int) getpid(), counter); + } else { + ret = os_snprintf(ctrl->local.sun_path, + sizeof(ctrl->local.sun_path), + CONFIG_CTRL_IFACE_CLIENT_DIR "/" + CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + (int) getpid(), counter); + } if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) { close(ctrl->s); os_free(ctrl); @@ -136,6 +152,8 @@ try_again: #ifdef ANDROID chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + /* Set group even if we do not have privileges to change owner */ + chown(ctrl->local.sun_path, -1, AID_WIFI); chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); if (os_strncmp(ctrl_path, "@android:", 9) == 0) { diff --git a/contrib/wpa/src/common/wpa_ctrl.h b/contrib/wpa/src/common/wpa_ctrl.h index 1d19fc550df..3de46823588 100644 --- a/contrib/wpa/src/common/wpa_ctrl.h +++ b/contrib/wpa/src/common/wpa_ctrl.h @@ -28,6 +28,8 @@ extern "C" { #define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " /** Association rejected during connection attempt */ #define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT " +/** Authentication rejected during connection attempt */ +#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT " /** wpa_supplicant is exiting */ #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " /** Password change was completed successfully */ @@ -68,6 +70,8 @@ extern "C" { #define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED " /** A BSS entry was removed (followed by BSS entry id and BSSID) */ #define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED " +/** No suitable network was found */ +#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND " /** Change in the signal level was reported by the driver */ #define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE " /** Regulatory domain channel */ @@ -227,6 +231,7 @@ extern "C" { #define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED " #define AP_STA_CONNECTED "AP-STA-CONNECTED " #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " +#define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH " #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " @@ -276,6 +281,7 @@ extern "C" { #define WPA_BSS_MASK_MESH_SCAN BIT(18) #define WPA_BSS_MASK_SNR BIT(19) #define WPA_BSS_MASK_EST_THROUGHPUT BIT(20) +#define WPA_BSS_MASK_FST BIT(21) /* VENDOR_ELEM_* frame id values */ @@ -312,6 +318,20 @@ enum wpa_vendor_elem_frame { */ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); +/** + * wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket + * is used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd + * when the socket path for client need to be specified explicitly. Default + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client + * socket path is /tmp. + */ +struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path); + /** * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h index f2d5662ff01..534c4bd7865 100644 --- a/contrib/wpa/src/crypto/crypto.h +++ b/contrib/wpa/src/crypto/crypto.h @@ -613,6 +613,15 @@ int crypto_bignum_is_zero(const struct crypto_bignum *a); */ int crypto_bignum_is_one(const struct crypto_bignum *a); +/** + * crypto_bignum_legendre - Compute the Legendre symbol (a/p) + * @a: Bignum + * @p: Bignum + * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure + */ +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p); + /** * struct crypto_ec - Elliptic curve context * @@ -757,6 +766,16 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, struct crypto_ec_point *p, const struct crypto_bignum *x, int y_bit); +/** + * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b + * @e: EC context from crypto_ec_init() + * @x: x coordinate + * Returns: y^2 on success, %NULL failure + */ +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x); + /** * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element * @e: EC context from crypto_ec_init() @@ -776,4 +795,15 @@ int crypto_ec_point_is_at_infinity(struct crypto_ec *e, int crypto_ec_point_is_on_curve(struct crypto_ec *e, const struct crypto_ec_point *p); +/** + * crypto_ec_point_cmp - Compare two EC points + * @e: EC context from crypto_ec_init() + * @a: EC point + * @b: EC point + * Returns: 0 on equal, non-zero otherwise + */ +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b); + #endif /* CRYPTO_H */ diff --git a/contrib/wpa/src/crypto/crypto_cryptoapi.c b/contrib/wpa/src/crypto/crypto_cryptoapi.c deleted file mode 100644 index 55a069b0d03..00000000000 --- a/contrib/wpa/src/crypto/crypto_cryptoapi.c +++ /dev/null @@ -1,783 +0,0 @@ -/* - * Crypto wrapper for Microsoft CryptoAPI - * Copyright (c) 2005-2009, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include -#include - -#include "common.h" -#include "crypto.h" - -#ifndef MS_ENH_RSA_AES_PROV -#ifdef UNICODE -#define MS_ENH_RSA_AES_PROV \ -L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" -#else -#define MS_ENH_RSA_AES_PROV \ -"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" -#endif -#endif /* MS_ENH_RSA_AES_PROV */ - -#ifndef CALG_HMAC -#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) -#endif - -#ifdef __MINGW32_VERSION -/* - * MinGW does not yet include all the needed definitions for CryptoAPI, so - * define here whatever extra is needed. - */ - -static BOOL WINAPI -(*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, - PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey) -= NULL; /* to be loaded from crypt32.dll */ - - -static int mingw_load_crypto_func(void) -{ - HINSTANCE dll; - - /* MinGW does not yet have full CryptoAPI support, so load the needed - * function here. */ - - if (CryptImportPublicKeyInfo) - return 0; - - dll = LoadLibrary("crypt32"); - if (dll == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " - "library"); - return -1; - } - - CryptImportPublicKeyInfo = GetProcAddress( - dll, "CryptImportPublicKeyInfo"); - if (CryptImportPublicKeyInfo == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " - "CryptImportPublicKeyInfo() address from " - "crypt32 library"); - return -1; - } - - return 0; -} - -#else /* __MINGW32_VERSION */ - -static int mingw_load_crypto_func(void) -{ - return 0; -} - -#endif /* __MINGW32_VERSION */ - - -static void cryptoapi_report_error(const char *msg) -{ - char *s, *pos; - DWORD err = GetLastError(); - - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) { - wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d", msg, (int) err); - } - - pos = s; - while (*pos) { - if (*pos == '\n' || *pos == '\r') { - *pos = '\0'; - break; - } - pos++; - } - - wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d: (%s)", msg, (int) err, s); - LocalFree(s); -} - - -int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) -{ - HCRYPTPROV prov; - HCRYPTHASH hash; - size_t i; - DWORD hlen; - int ret = 0; - - if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) { - cryptoapi_report_error("CryptAcquireContext"); - return -1; - } - - if (!CryptCreateHash(prov, alg, 0, 0, &hash)) { - cryptoapi_report_error("CryptCreateHash"); - CryptReleaseContext(prov, 0); - return -1; - } - - for (i = 0; i < num_elem; i++) { - if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) { - cryptoapi_report_error("CryptHashData"); - CryptDestroyHash(hash); - CryptReleaseContext(prov, 0); - } - } - - hlen = hash_len; - if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) { - cryptoapi_report_error("CryptGetHashParam"); - ret = -1; - } - - CryptDestroyHash(hash); - CryptReleaseContext(prov, 0); - - return ret; -} - - -int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); -} - - -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) -{ - u8 next, tmp; - int i; - HCRYPTPROV prov; - HCRYPTKEY ckey; - DWORD dlen; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[8]; - } key_blob; - DWORD mode = CRYPT_MODE_ECB; - - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - key_blob.hdr.aiKeyAlg = CALG_DES; - key_blob.len = 8; - - /* Add parity bits to the key */ - next = 0; - for (i = 0; i < 7; i++) { - tmp = key[i]; - key_blob.key[i] = (tmp >> i) | next | 1; - next = tmp << (7 - i); - } - key_blob.key[i] = next | 1; - - if (!CryptAcquireContext(&prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " - "%d", (int) GetLastError()); - return; - } - - if (!CryptImportKey(prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, - &ckey)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", - (int) GetLastError()); - CryptReleaseContext(prov, 0); - return; - } - - if (!CryptSetKeyParam(ckey, KP_MODE, (BYTE *) &mode, 0)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " - "failed: %d", (int) GetLastError()); - CryptDestroyKey(ckey); - CryptReleaseContext(prov, 0); - return; - } - - os_memcpy(cypher, clear, 8); - dlen = 8; - if (!CryptEncrypt(ckey, 0, FALSE, 0, cypher, &dlen, 8)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", - (int) GetLastError()); - os_memset(cypher, 0, 8); - } - - CryptDestroyKey(ckey); - CryptReleaseContext(prov, 0); -} - - -int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); -} - - -int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); -} - - -struct aes_context { - HCRYPTPROV prov; - HCRYPTKEY ckey; -}; - - -void * aes_encrypt_init(const u8 *key, size_t len) -{ - struct aes_context *akey; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[16]; - } key_blob; - DWORD mode = CRYPT_MODE_ECB; - - if (len != 16) - return NULL; - - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - key_blob.hdr.aiKeyAlg = CALG_AES_128; - key_blob.len = len; - os_memcpy(key_blob.key, key, len); - - akey = os_zalloc(sizeof(*akey)); - if (akey == NULL) - return NULL; - - if (!CryptAcquireContext(&akey->prov, NULL, - MS_ENH_RSA_AES_PROV, PROV_RSA_AES, - CRYPT_VERIFYCONTEXT)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " - "%d", (int) GetLastError()); - os_free(akey); - return NULL; - } - - if (!CryptImportKey(akey->prov, (BYTE *) &key_blob, sizeof(key_blob), - 0, 0, &akey->ckey)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", - (int) GetLastError()); - CryptReleaseContext(akey->prov, 0); - os_free(akey); - return NULL; - } - - if (!CryptSetKeyParam(akey->ckey, KP_MODE, (BYTE *) &mode, 0)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " - "failed: %d", (int) GetLastError()); - CryptDestroyKey(akey->ckey); - CryptReleaseContext(akey->prov, 0); - os_free(akey); - return NULL; - } - - return akey; -} - - -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) -{ - struct aes_context *akey = ctx; - DWORD dlen; - - os_memcpy(crypt, plain, 16); - dlen = 16; - if (!CryptEncrypt(akey->ckey, 0, FALSE, 0, crypt, &dlen, 16)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", - (int) GetLastError()); - os_memset(crypt, 0, 16); - } -} - - -void aes_encrypt_deinit(void *ctx) -{ - struct aes_context *akey = ctx; - if (akey) { - CryptDestroyKey(akey->ckey); - CryptReleaseContext(akey->prov, 0); - os_free(akey); - } -} - - -void * aes_decrypt_init(const u8 *key, size_t len) -{ - return aes_encrypt_init(key, len); -} - - -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) -{ - struct aes_context *akey = ctx; - DWORD dlen; - - os_memcpy(plain, crypt, 16); - dlen = 16; - - if (!CryptDecrypt(akey->ckey, 0, FALSE, 0, plain, &dlen)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: CryptDecrypt failed: %d", - (int) GetLastError()); - } -} - - -void aes_decrypt_deinit(void *ctx) -{ - aes_encrypt_deinit(ctx); -} - - -struct crypto_hash { - enum crypto_hash_alg alg; - int error; - HCRYPTPROV prov; - HCRYPTHASH hash; - HCRYPTKEY key; -}; - -struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, - size_t key_len) -{ - struct crypto_hash *ctx; - ALG_ID calg; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[32]; - } key_blob; - - os_memset(&key_blob, 0, sizeof(key_blob)); - switch (alg) { - case CRYPTO_HASH_ALG_MD5: - calg = CALG_MD5; - break; - case CRYPTO_HASH_ALG_SHA1: - calg = CALG_SHA; - break; - case CRYPTO_HASH_ALG_HMAC_MD5: - case CRYPTO_HASH_ALG_HMAC_SHA1: - calg = CALG_HMAC; - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - /* - * Note: RC2 is not really used, but that can be used to - * import HMAC keys of up to 16 byte long. - * CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to - * be able to import longer keys (HMAC-SHA1 uses 20-byte key). - */ - key_blob.hdr.aiKeyAlg = CALG_RC2; - key_blob.len = key_len; - if (key_len > sizeof(key_blob.key)) - return NULL; - os_memcpy(key_blob.key, key, key_len); - break; - default: - return NULL; - } - - ctx = os_zalloc(sizeof(*ctx)); - if (ctx == NULL) - return NULL; - - ctx->alg = alg; - - if (!CryptAcquireContext(&ctx->prov, NULL, NULL, PROV_RSA_FULL, 0)) { - cryptoapi_report_error("CryptAcquireContext"); - os_free(ctx); - return NULL; - } - - if (calg == CALG_HMAC) { -#ifndef CRYPT_IPSEC_HMAC_KEY -#define CRYPT_IPSEC_HMAC_KEY 0x00000100 -#endif - if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, - sizeof(key_blob), 0, CRYPT_IPSEC_HMAC_KEY, - &ctx->key)) { - cryptoapi_report_error("CryptImportKey"); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); - return NULL; - } - } - - if (!CryptCreateHash(ctx->prov, calg, ctx->key, 0, &ctx->hash)) { - cryptoapi_report_error("CryptCreateHash"); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); - return NULL; - } - - if (calg == CALG_HMAC) { - HMAC_INFO info; - os_memset(&info, 0, sizeof(info)); - switch (alg) { - case CRYPTO_HASH_ALG_HMAC_MD5: - info.HashAlgid = CALG_MD5; - break; - case CRYPTO_HASH_ALG_HMAC_SHA1: - info.HashAlgid = CALG_SHA; - break; - default: - /* unreachable */ - break; - } - - if (!CryptSetHashParam(ctx->hash, HP_HMAC_INFO, (BYTE *) &info, - 0)) { - cryptoapi_report_error("CryptSetHashParam"); - CryptDestroyHash(ctx->hash); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); - return NULL; - } - } - - return ctx; -} - - -void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) -{ - if (ctx == NULL || ctx->error) - return; - - if (!CryptHashData(ctx->hash, (BYTE *) data, len, 0)) { - cryptoapi_report_error("CryptHashData"); - ctx->error = 1; - } -} - - -int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) -{ - int ret = 0; - DWORD hlen; - - if (ctx == NULL) - return -2; - - if (mac == NULL || len == NULL) - goto done; - - if (ctx->error) { - ret = -2; - goto done; - } - - hlen = *len; - if (!CryptGetHashParam(ctx->hash, HP_HASHVAL, mac, &hlen, 0)) { - cryptoapi_report_error("CryptGetHashParam"); - ret = -2; - } - *len = hlen; - -done: - if (ctx->alg == CRYPTO_HASH_ALG_HMAC_SHA1 || - ctx->alg == CRYPTO_HASH_ALG_HMAC_MD5) - CryptDestroyKey(ctx->key); - - os_free(ctx); - - return ret; -} - - -struct crypto_cipher { - HCRYPTPROV prov; - HCRYPTKEY key; -}; - - -struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, - const u8 *iv, const u8 *key, - size_t key_len) -{ - struct crypto_cipher *ctx; - struct { - BLOBHEADER hdr; - DWORD len; - BYTE key[32]; - } key_blob; - DWORD mode = CRYPT_MODE_CBC; - - key_blob.hdr.bType = PLAINTEXTKEYBLOB; - key_blob.hdr.bVersion = CUR_BLOB_VERSION; - key_blob.hdr.reserved = 0; - key_blob.len = key_len; - if (key_len > sizeof(key_blob.key)) - return NULL; - os_memcpy(key_blob.key, key, key_len); - - switch (alg) { - case CRYPTO_CIPHER_ALG_AES: - if (key_len == 32) - key_blob.hdr.aiKeyAlg = CALG_AES_256; - else if (key_len == 24) - key_blob.hdr.aiKeyAlg = CALG_AES_192; - else - key_blob.hdr.aiKeyAlg = CALG_AES_128; - break; - case CRYPTO_CIPHER_ALG_3DES: - key_blob.hdr.aiKeyAlg = CALG_3DES; - break; - case CRYPTO_CIPHER_ALG_DES: - key_blob.hdr.aiKeyAlg = CALG_DES; - break; - case CRYPTO_CIPHER_ALG_RC2: - key_blob.hdr.aiKeyAlg = CALG_RC2; - break; - case CRYPTO_CIPHER_ALG_RC4: - key_blob.hdr.aiKeyAlg = CALG_RC4; - break; - default: - return NULL; - } - - ctx = os_zalloc(sizeof(*ctx)); - if (ctx == NULL) - return NULL; - - if (!CryptAcquireContext(&ctx->prov, NULL, MS_ENH_RSA_AES_PROV, - PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { - cryptoapi_report_error("CryptAcquireContext"); - goto fail1; - } - - if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, - sizeof(key_blob), 0, 0, &ctx->key)) { - cryptoapi_report_error("CryptImportKey"); - goto fail2; - } - - if (!CryptSetKeyParam(ctx->key, KP_MODE, (BYTE *) &mode, 0)) { - cryptoapi_report_error("CryptSetKeyParam(KP_MODE)"); - goto fail3; - } - - if (iv && !CryptSetKeyParam(ctx->key, KP_IV, (BYTE *) iv, 0)) { - cryptoapi_report_error("CryptSetKeyParam(KP_IV)"); - goto fail3; - } - - return ctx; - -fail3: - CryptDestroyKey(ctx->key); -fail2: - CryptReleaseContext(ctx->prov, 0); -fail1: - os_free(ctx); - return NULL; -} - - -int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, - u8 *crypt, size_t len) -{ - DWORD dlen; - - os_memcpy(crypt, plain, len); - dlen = len; - if (!CryptEncrypt(ctx->key, 0, FALSE, 0, crypt, &dlen, len)) { - cryptoapi_report_error("CryptEncrypt"); - os_memset(crypt, 0, len); - return -1; - } - - return 0; -} - - -int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, - u8 *plain, size_t len) -{ - DWORD dlen; - - os_memcpy(plain, crypt, len); - dlen = len; - if (!CryptDecrypt(ctx->key, 0, FALSE, 0, plain, &dlen)) { - cryptoapi_report_error("CryptDecrypt"); - return -1; - } - - return 0; -} - - -void crypto_cipher_deinit(struct crypto_cipher *ctx) -{ - CryptDestroyKey(ctx->key); - CryptReleaseContext(ctx->prov, 0); - os_free(ctx); -} - - -struct crypto_public_key { - HCRYPTPROV prov; - HCRYPTKEY rsa; -}; - -struct crypto_private_key { - HCRYPTPROV prov; - HCRYPTKEY rsa; -}; - - -struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) -{ - /* Use crypto_public_key_from_cert() instead. */ - return NULL; -} - - -struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len, - const char *passwd) -{ - /* TODO */ - return NULL; -} - - -struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, - size_t len) -{ - struct crypto_public_key *pk; - PCCERT_CONTEXT cc; - - pk = os_zalloc(sizeof(*pk)); - if (pk == NULL) - return NULL; - - cc = CertCreateCertificateContext(X509_ASN_ENCODING | - PKCS_7_ASN_ENCODING, buf, len); - if (!cc) { - cryptoapi_report_error("CryptCreateCertificateContext"); - os_free(pk); - return NULL; - } - - if (!CryptAcquireContext(&pk->prov, NULL, MS_DEF_PROV, PROV_RSA_FULL, - 0)) { - cryptoapi_report_error("CryptAcquireContext"); - os_free(pk); - CertFreeCertificateContext(cc); - return NULL; - } - - if (!CryptImportPublicKeyInfo(pk->prov, X509_ASN_ENCODING | - PKCS_7_ASN_ENCODING, - &cc->pCertInfo->SubjectPublicKeyInfo, - &pk->rsa)) { - cryptoapi_report_error("CryptImportPublicKeyInfo"); - CryptReleaseContext(pk->prov, 0); - os_free(pk); - CertFreeCertificateContext(cc); - return NULL; - } - - CertFreeCertificateContext(cc); - - return pk; -} - - -int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - DWORD clen; - u8 *tmp; - size_t i; - - if (*outlen < inlen) - return -1; - tmp = malloc(*outlen); - if (tmp == NULL) - return -1; - - os_memcpy(tmp, in, inlen); - clen = inlen; - if (!CryptEncrypt(key->rsa, 0, TRUE, 0, tmp, &clen, *outlen)) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Failed to encrypt using " - "public key: %d", (int) GetLastError()); - os_free(tmp); - return -1; - } - - *outlen = clen; - - /* Reverse the output */ - for (i = 0; i < *outlen; i++) - out[i] = tmp[*outlen - 1 - i]; - - os_free(tmp); - - return 0; -} - - -int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - /* TODO */ - return -1; -} - - -void crypto_public_key_free(struct crypto_public_key *key) -{ - if (key) { - CryptDestroyKey(key->rsa); - CryptReleaseContext(key->prov, 0); - os_free(key); - } -} - - -void crypto_private_key_free(struct crypto_private_key *key) -{ - if (key) { - CryptDestroyKey(key->rsa); - CryptReleaseContext(key->prov, 0); - os_free(key); - } -} - - -int crypto_global_init(void) -{ - return mingw_load_crypto_func(); -} - - -void crypto_global_deinit(void) -{ -} - - -int crypto_mod_exp(const u8 *base, size_t base_len, - const u8 *power, size_t power_len, - const u8 *modulus, size_t modulus_len, - u8 *result, size_t *result_len) -{ - /* TODO */ - return -1; -} diff --git a/contrib/wpa/src/crypto/crypto_module_tests.c b/contrib/wpa/src/crypto/crypto_module_tests.c index 7137c27d0e8..581005df3e3 100644 --- a/contrib/wpa/src/crypto/crypto_module_tests.c +++ b/contrib/wpa/src/crypto/crypto_module_tests.c @@ -161,7 +161,7 @@ struct omac1_test_vector { u8 tag[16]; }; -static struct omac1_test_vector omac1_test_vectors[] = +static const struct omac1_test_vector omac1_test_vectors[] = { { { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, @@ -210,7 +210,8 @@ static struct omac1_test_vector omac1_test_vectors[] = }; -static int test_omac1_vector(struct omac1_test_vector *tv, unsigned int i) +static int test_omac1_vector(const struct omac1_test_vector *tv, + unsigned int i) { u8 key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, @@ -515,6 +516,7 @@ static int test_key_wrap(void) 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5 }; +#ifndef CONFIG_BORINGSSL /* RFC 3394 - Test vector 4.2 */ u8 kek42[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -530,6 +532,7 @@ static int test_key_wrap(void) 0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2, 0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D }; +#endif /* CONFIG_BORINGSSL */ /* RFC 3394 - Test vector 4.3 */ u8 kek43[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -546,6 +549,7 @@ static int test_key_wrap(void) 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7, }; +#ifndef CONFIG_BORINGSSL /* RFC 3394 - Test vector 4.4 */ u8 kek44[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -563,6 +567,7 @@ static int test_key_wrap(void) 0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93, 0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2 }; +#endif /* CONFIG_BORINGSSL */ /* RFC 3394 - Test vector 4.5 */ u8 kek45[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -623,6 +628,7 @@ static int test_key_wrap(void) ret++; } +#ifndef CONFIG_BORINGSSL wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2"); if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42, result)) { @@ -642,6 +648,7 @@ static int test_key_wrap(void) wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed"); ret++; } +#endif /* CONFIG_BORINGSSL */ wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3"); if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43, @@ -663,6 +670,7 @@ static int test_key_wrap(void) ret++; } +#ifndef CONFIG_BORINGSSL wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4"); if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44, result)) { @@ -682,6 +690,7 @@ static int test_key_wrap(void) wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed"); ret++; } +#endif /* CONFIG_BORINGSSL */ wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5"); if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45, @@ -732,6 +741,7 @@ static int test_key_wrap(void) static int test_md5(void) { +#ifndef CONFIG_FIPS struct { char *data; char *hash; @@ -810,6 +820,10 @@ static int test_md5(void) wpa_printf(MSG_INFO, "MD5 test cases passed"); return errors; +#else /* CONFIG_FIPS */ + wpa_printf(MSG_INFO, "MD5 test cases skipped due to CONFIG_FIPS"); + return 0; +#endif /* CONFIG_FIPS */ } @@ -841,6 +855,7 @@ static int test_eap_fast(void) 0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27, 0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2 }; +#ifndef CONFIG_FIPS const u8 key_block[] = { 0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74, 0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35, @@ -857,6 +872,7 @@ static int test_eap_fast(void) 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 }; +#endif /* CONFIG_FIPS */ const u8 sks[] = { 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, @@ -931,6 +947,7 @@ static int test_eap_fast(void) errors++; } +#ifndef CONFIG_FIPS wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block"); if (tls_prf_sha1_md5(master_secret, sizeof(master_secret), "key expansion", seed, sizeof(seed), @@ -939,6 +956,7 @@ static int test_eap_fast(void) wpa_printf(MSG_INFO, "PRF test - FAILED!"); errors++; } +#endif /* CONFIG_FIPS */ wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK"); if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys", @@ -983,14 +1001,14 @@ static int test_eap_fast(void) } -static u8 key0[] = +static const u8 key0[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }; -static u8 data0[] = "Hi There"; -static u8 prf0[] = +static const u8 data0[] = "Hi There"; +static const u8 prf0[] = { 0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84, 0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54, @@ -1002,9 +1020,9 @@ static u8 prf0[] = 0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a }; -static u8 key1[] = "Jefe"; -static u8 data1[] = "what do ya want for nothing?"; -static u8 prf1[] = +static const u8 key1[] = "Jefe"; +static const u8 data1[] = "what do ya want for nothing?"; +static const u8 prf1[] = { 0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad, 0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4, @@ -1017,13 +1035,13 @@ static u8 prf1[] = }; -static u8 key2[] = +static const u8 key2[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; -static u8 data2[] = +static const u8 data2[] = { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, @@ -1033,7 +1051,7 @@ static u8 data2[] = 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd }; -static u8 prf2[] = +static const u8 prf2[] = { 0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f, 0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1, @@ -1052,7 +1070,7 @@ struct passphrase_test { char psk[32]; }; -static struct passphrase_test passphrase_tests[] = +static const struct passphrase_test passphrase_tests[] = { { "password", @@ -1097,7 +1115,7 @@ struct rfc6070_test { size_t dk_len; }; -static struct rfc6070_test rfc6070_tests[] = +static const struct rfc6070_test rfc6070_tests[] = { { "password", @@ -1214,7 +1232,7 @@ static int test_sha1(void) wpa_printf(MSG_INFO, "PBKDF2-SHA1 Passphrase test cases:"); for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) { u8 psk[32]; - struct passphrase_test *test = &passphrase_tests[i]; + const struct passphrase_test *test = &passphrase_tests[i]; if (pbkdf2_sha1(test->passphrase, (const u8 *) test->ssid, strlen(test->ssid), @@ -1230,7 +1248,7 @@ static int test_sha1(void) wpa_printf(MSG_INFO, "PBKDF2-SHA1 test cases (RFC 6070):"); for (i = 0; i < NUM_RFC6070_TESTS; i++) { u8 dk[25]; - struct rfc6070_test *test = &rfc6070_tests[i]; + const struct rfc6070_test *test = &rfc6070_tests[i]; if (pbkdf2_sha1(test->p, (const u8 *) test->s, strlen(test->s), test->c, dk, test->dk_len) == 0 && @@ -1248,7 +1266,7 @@ static int test_sha1(void) } -struct { +const struct { char *data; u8 hash[32]; } tests[] = { @@ -1272,7 +1290,7 @@ struct { } }; -struct hmac_test { +const struct hmac_test { u8 key[80]; size_t key_len; u8 data[128]; @@ -1513,7 +1531,7 @@ static int test_sha256(void) } for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) { - struct hmac_test *t = &hmac_tests[i]; + const struct hmac_test *t = &hmac_tests[i]; wpa_printf(MSG_INFO, "HMAC-SHA256 test case %d:", i + 1); @@ -1563,6 +1581,7 @@ static int test_sha256(void) static int test_ms_funcs(void) { +#ifndef CONFIG_FIPS /* Test vector from RFC2759 example */ char *username = "User"; char *password = "clientPass"; @@ -1655,6 +1674,10 @@ static int test_ms_funcs(void) wpa_printf(MSG_INFO, "ms_funcs test cases passed"); return errors; +#else /* CONFIG_FIPS */ + wpa_printf(MSG_INFO, "ms_funcs test cases skipped due to CONFIG_FIPS"); + return 0; +#endif /* CONFIG_FIPS */ } diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c index f158ef43a64..6cff75c64ae 100644 --- a/contrib/wpa/src/crypto/crypto_openssl.c +++ b/contrib/wpa/src/crypto/crypto_openssl.c @@ -93,10 +93,12 @@ static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, } +#ifndef CONFIG_FIPS int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac); } +#endif /* CONFIG_FIPS */ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) @@ -120,6 +122,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } +#ifndef CONFIG_NO_RC4 int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) { @@ -155,12 +158,15 @@ out: return res; #endif /* OPENSSL_NO_RC4 */ } +#endif /* CONFIG_NO_RC4 */ +#ifndef CONFIG_FIPS int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); } +#endif /* CONFIG_FIPS */ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) @@ -297,6 +303,9 @@ void aes_decrypt_deinit(void *ctx) } +#ifndef CONFIG_FIPS +#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP + int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) { AES_KEY actx; @@ -323,6 +332,59 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, return res <= 0 ? -1 : 0; } +#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */ +#endif /* CONFIG_FIPS */ + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + EVP_CIPHER_CTX ctx; + int clen, len; + u8 buf[16]; + + EVP_CIPHER_CTX_init(&ctx); + if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + return -1; + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + clen = data_len; + if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 || + clen != (int) data_len) + return -1; + + len = sizeof(buf); + if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) + return -1; + EVP_CIPHER_CTX_cleanup(&ctx); + + return 0; +} + + +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + EVP_CIPHER_CTX ctx; + int plen, len; + u8 buf[16]; + + EVP_CIPHER_CTX_init(&ctx); + if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + return -1; + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + plen = data_len; + if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 || + plen != (int) data_len) + return -1; + + len = sizeof(buf); + if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) + return -1; + EVP_CIPHER_CTX_cleanup(&ctx); + + return 0; +} + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, @@ -380,11 +442,13 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, return NULL; switch (alg) { +#ifndef CONFIG_NO_RC4 #ifndef OPENSSL_NO_RC4 case CRYPTO_CIPHER_ALG_RC4: cipher = EVP_rc4(); break; #endif /* OPENSSL_NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ #ifndef OPENSSL_NO_AES case CRYPTO_CIPHER_ALG_AES: switch (key_len) { @@ -1057,6 +1121,42 @@ int crypto_bignum_is_one(const struct crypto_bignum *a) } +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p) +{ + BN_CTX *bnctx; + BIGNUM *exp = NULL, *tmp = NULL; + int res = -2; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -2; + + exp = BN_new(); + tmp = BN_new(); + if (!exp || !tmp || + /* exp = (p-1) / 2 */ + !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) || + !BN_rshift1(exp, exp) || + !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p, + bnctx)) + goto fail; + + if (BN_is_word(tmp, 1)) + res = 1; + else if (BN_is_zero(tmp)) + res = 0; + else + res = -1; + +fail: + BN_clear_free(tmp); + BN_clear_free(exp); + BN_CTX_free(bnctx); + return res; +} + + #ifdef CONFIG_ECC struct crypto_ec { @@ -1064,6 +1164,8 @@ struct crypto_ec { BN_CTX *bnctx; BIGNUM *prime; BIGNUM *order; + BIGNUM *a; + BIGNUM *b; }; struct crypto_ec * crypto_ec_init(int group) @@ -1088,6 +1190,26 @@ struct crypto_ec * crypto_ec_init(int group) case 26: nid = NID_secp224r1; break; +#ifdef NID_brainpoolP224r1 + case 27: + nid = NID_brainpoolP224r1; + break; +#endif /* NID_brainpoolP224r1 */ +#ifdef NID_brainpoolP256r1 + case 28: + nid = NID_brainpoolP256r1; + break; +#endif /* NID_brainpoolP256r1 */ +#ifdef NID_brainpoolP384r1 + case 29: + nid = NID_brainpoolP384r1; + break; +#endif /* NID_brainpoolP384r1 */ +#ifdef NID_brainpoolP512r1 + case 30: + nid = NID_brainpoolP512r1; + break; +#endif /* NID_brainpoolP512r1 */ default: return NULL; } @@ -1100,9 +1222,11 @@ struct crypto_ec * crypto_ec_init(int group) e->group = EC_GROUP_new_by_curve_name(nid); e->prime = BN_new(); e->order = BN_new(); + e->a = BN_new(); + e->b = BN_new(); if (e->group == NULL || e->bnctx == NULL || e->prime == NULL || - e->order == NULL || - !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, e->bnctx) || + e->order == NULL || e->a == NULL || e->b == NULL || + !EC_GROUP_get_curve_GFp(e->group, e->prime, e->a, e->b, e->bnctx) || !EC_GROUP_get_order(e->group, e->order, e->bnctx)) { crypto_ec_deinit(e); e = NULL; @@ -1116,6 +1240,8 @@ void crypto_ec_deinit(struct crypto_ec *e) { if (e == NULL) return; + BN_clear_free(e->b); + BN_clear_free(e->a); BN_clear_free(e->order); BN_clear_free(e->prime); EC_GROUP_free(e->group); @@ -1263,6 +1389,33 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, } +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x) +{ + BIGNUM *tmp, *tmp2, *y_sqr = NULL; + + tmp = BN_new(); + tmp2 = BN_new(); + + /* y^2 = x^3 + ax + b */ + if (tmp && tmp2 && + BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_mul(tmp2, e->a, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_add_quick(tmp2, tmp2, tmp, e->prime) && + BN_mod_add_quick(tmp2, tmp2, e->b, e->prime)) { + y_sqr = tmp2; + tmp2 = NULL; + } + + BN_clear_free(tmp); + BN_clear_free(tmp2); + + return (struct crypto_bignum *) y_sqr; +} + + int crypto_ec_point_is_at_infinity(struct crypto_ec *e, const struct crypto_ec_point *p) { @@ -1273,7 +1426,17 @@ int crypto_ec_point_is_at_infinity(struct crypto_ec *e, int crypto_ec_point_is_on_curve(struct crypto_ec *e, const struct crypto_ec_point *p) { - return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, e->bnctx); + return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, + e->bnctx) == 1; +} + + +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b) +{ + return EC_POINT_cmp(e->group, (const EC_POINT *) a, + (const EC_POINT *) b, e->bnctx); } #endif /* CONFIG_ECC */ diff --git a/contrib/wpa/src/crypto/dh_groups.c b/contrib/wpa/src/crypto/dh_groups.c index d3b263196e2..3aeb2bbc60a 100644 --- a/contrib/wpa/src/crypto/dh_groups.c +++ b/contrib/wpa/src/crypto/dh_groups.c @@ -1153,7 +1153,7 @@ dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \ dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe } -static struct dh_group dh_groups[] = { +static const struct dh_group dh_groups[] = { DH_GROUP(5, 1), #ifdef ALL_DH_GROUPS DH_GROUP(1, 1), diff --git a/contrib/wpa/src/crypto/fips_prf_openssl.c b/contrib/wpa/src/crypto/fips_prf_openssl.c index d69eceabff1..fb03efcd4ff 100644 --- a/contrib/wpa/src/crypto/fips_prf_openssl.c +++ b/contrib/wpa/src/crypto/fips_prf_openssl.c @@ -13,13 +13,21 @@ #include "crypto.h" -static void sha1_transform(u8 *state, const u8 data[64]) +static void sha1_transform(u32 *state, const u8 data[64]) { SHA_CTX context; os_memset(&context, 0, sizeof(context)); - os_memcpy(&context.h0, state, 5 * 4); + context.h0 = state[0]; + context.h1 = state[1]; + context.h2 = state[2]; + context.h3 = state[3]; + context.h4 = state[4]; SHA1_Transform(&context, data); - os_memcpy(state, &context.h0, 5 * 4); + state[0] = context.h0; + state[1] = context.h1; + state[2] = context.h2; + state[3] = context.h3; + state[4] = context.h4; } @@ -53,7 +61,7 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) /* w_i = G(t, XVAL) */ os_memcpy(_t, t, 20); - sha1_transform((u8 *) _t, xkey); + sha1_transform(_t, xkey); _t[0] = host_to_be32(_t[0]); _t[1] = host_to_be32(_t[1]); _t[2] = host_to_be32(_t[2]); diff --git a/contrib/wpa/src/crypto/ms_funcs.c b/contrib/wpa/src/crypto/ms_funcs.c index 49a5c1c245d..053d203cb65 100644 --- a/contrib/wpa/src/crypto/ms_funcs.c +++ b/contrib/wpa/src/crypto/ms_funcs.c @@ -78,9 +78,8 @@ static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, * @challenge: 8-octet Challenge (OUT) * Returns: 0 on success, -1 on failure */ -static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, - const u8 *username, size_t username_len, - u8 *challenge) +int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, u8 *challenge) { u8 hash[SHA1_MAC_LEN]; const unsigned char *addr[3]; @@ -175,9 +174,8 @@ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, u8 password_hash[16]; if (challenge_hash(peer_challenge, auth_challenge, username, - username_len, challenge)) - return -1; - if (nt_password_hash(password, password_len, password_hash)) + username_len, challenge) || + nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); return 0; @@ -257,12 +255,9 @@ int generate_authenticator_response_pwhash( addr2[1] = challenge; addr2[2] = magic2; - if (hash_nt_password_hash(password_hash, password_hash_hash)) - return -1; - if (sha1_vector(3, addr1, len1, response)) - return -1; - - if (challenge_hash(peer_challenge, auth_challenge, username, + if (hash_nt_password_hash(password_hash, password_hash_hash) || + sha1_vector(3, addr1, len1, response) || + challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge)) return -1; return sha1_vector(3, addr2, len2, response); @@ -417,6 +412,8 @@ int get_asymetric_start_key(const u8 *master_key, u8 *session_key, } +#ifndef CONFIG_NO_RC4 + #define PWBLOCK_LEN 516 /** @@ -436,10 +433,8 @@ int encrypt_pw_block_with_password_hash( os_memset(pw_block, 0, PWBLOCK_LEN); - if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0) - return -1; - - if (ucs2_len > 256) + if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0 + || ucs2_len > 256) return -1; offset = (256 - ucs2_len) * 2; @@ -484,6 +479,8 @@ int new_password_encrypted_with_old_nt_password_hash( return 0; } +#endif /* CONFIG_NO_RC4 */ + /** * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 diff --git a/contrib/wpa/src/crypto/ms_funcs.h b/contrib/wpa/src/crypto/ms_funcs.h index bd9bfee95b7..b5b5918e1a5 100644 --- a/contrib/wpa/src/crypto/ms_funcs.h +++ b/contrib/wpa/src/crypto/ms_funcs.h @@ -33,6 +33,8 @@ int nt_challenge_response(const u8 *challenge, const u8 *password, void challenge_response(const u8 *challenge, const u8 *password_hash, u8 *response); +int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, u8 *challenge); int nt_password_hash(const u8 *password, size_t password_len, u8 *password_hash); int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); diff --git a/contrib/wpa/src/crypto/random.c b/contrib/wpa/src/crypto/random.c index bc758aa5723..3a86a93a46a 100644 --- a/contrib/wpa/src/crypto/random.c +++ b/contrib/wpa/src/crypto/random.c @@ -181,6 +181,7 @@ int random_get_bytes(void *buf, size_t len) #ifdef CONFIG_FIPS /* Mix in additional entropy from the crypto module */ + bytes = buf; left = len; while (left) { size_t siz, i; diff --git a/contrib/wpa/src/crypto/sha1-tlsprf.c b/contrib/wpa/src/crypto/sha1-tlsprf.c index 0effd9b76dd..f9bc0ebf6e3 100644 --- a/contrib/wpa/src/crypto/sha1-tlsprf.c +++ b/contrib/wpa/src/crypto/sha1-tlsprf.c @@ -95,5 +95,10 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, SHA1_pos++; } + os_memset(A_MD5, 0, MD5_MAC_LEN); + os_memset(P_MD5, 0, MD5_MAC_LEN); + os_memset(A_SHA1, 0, SHA1_MAC_LEN); + os_memset(P_SHA1, 0, SHA1_MAC_LEN); + return 0; } diff --git a/contrib/wpa/src/crypto/sha1-tprf.c b/contrib/wpa/src/crypto/sha1-tprf.c index a52949462f7..562510f8937 100644 --- a/contrib/wpa/src/crypto/sha1-tprf.c +++ b/contrib/wpa/src/crypto/sha1-tprf.c @@ -66,5 +66,7 @@ int sha1_t_prf(const u8 *key, size_t key_len, const char *label, len[0] = SHA1_MAC_LEN; } + os_memset(hash, 0, SHA1_MAC_LEN); + return 0; } diff --git a/contrib/wpa/src/crypto/sha256-kdf.c b/contrib/wpa/src/crypto/sha256-kdf.c index d8a1beb32e9..e7509ce41ab 100644 --- a/contrib/wpa/src/crypto/sha256-kdf.c +++ b/contrib/wpa/src/crypto/sha256-kdf.c @@ -61,6 +61,7 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, if (iter == 255) { os_memset(out, 0, outlen); + os_memset(T, 0, SHA256_MAC_LEN); return -1; } iter++; @@ -68,9 +69,11 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0) { os_memset(out, 0, outlen); + os_memset(T, 0, SHA256_MAC_LEN); return -1; } } + os_memset(T, 0, SHA256_MAC_LEN); return 0; } diff --git a/contrib/wpa/src/crypto/sha384-prf.c b/contrib/wpa/src/crypto/sha384-prf.c new file mode 100644 index 00000000000..653920ba283 --- /dev/null +++ b/contrib/wpa/src/crypto/sha384-prf.c @@ -0,0 +1,100 @@ +/* + * SHA384-based KDF (IEEE 802.11ac) + * Copyright (c) 2003-2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384.h" +#include "crypto.h" + + +/** + * sha384_prf - SHA384-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2) + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +void sha384_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); +} + + +/** + * sha384_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bits of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. If the requested buf_len is not divisible by eight, the least + * significant 1-7 bits of the last octet in the output are not part of the + * requested output. + */ +void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) +{ + u16 counter = 1; + size_t pos, plen; + u8 hash[SHA384_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; + size_t buf_len = (buf_len_bits + 7) / 8; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (u8 *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len_bits); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA384_MAC_LEN) { + hmac_sha384_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA384_MAC_LEN; + } else { + hmac_sha384_vector(key, key_len, 4, addr, len, hash); + os_memcpy(&buf[pos], hash, plen); + pos += plen; + break; + } + counter++; + } + + /* + * Mask out unused bits in the last octet if it does not use all the + * bits. + */ + if (buf_len_bits % 8) { + u8 mask = 0xff << (8 - buf_len_bits % 8); + buf[pos - 1] &= mask; + } + + os_memset(hash, 0, sizeof(hash)); +} diff --git a/contrib/wpa/src/crypto/sha384.h b/contrib/wpa/src/crypto/sha384.h index e6a1fe41e1a..3deafa59ec2 100644 --- a/contrib/wpa/src/crypto/sha384.h +++ b/contrib/wpa/src/crypto/sha384.h @@ -15,5 +15,10 @@ int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); +void sha384_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); #endif /* SHA384_H */ diff --git a/contrib/wpa/src/crypto/tls.h b/contrib/wpa/src/crypto/tls.h index 9ae95a66c9e..2e562339cc5 100644 --- a/contrib/wpa/src/crypto/tls.h +++ b/contrib/wpa/src/crypto/tls.h @@ -11,9 +11,7 @@ struct tls_connection; -struct tls_keys { - const u8 *master_key; /* TLS master secret */ - size_t master_key_len; +struct tls_random { const u8 *client_random; size_t client_random_len; const u8 *server_random; @@ -81,6 +79,7 @@ struct tls_config { int fips_mode; int cert_in_cb; const char *openssl_ciphers; + unsigned int tls_session_lifetime; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -95,6 +94,7 @@ struct tls_config { #define TLS_CONN_DISABLE_TLSv1_1 BIT(5) #define TLS_CONN_DISABLE_TLSv1_2 BIT(6) #define TLS_CONN_EAP_FAST BIT(7) +#define TLS_CONN_DISABLE_TLSv1_0 BIT(8) /** * struct tls_connection_params - Parameters for TLS connection @@ -255,6 +255,7 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn); int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); enum { + TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4, TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 }; @@ -265,10 +266,12 @@ enum { * @conn: Connection context data from tls_connection_init() * @params: Connection parameters * Returns: 0 on success, -1 on failure, - * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing - * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine + * failure, or * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the - * PKCS#11 engine private key. + * PKCS#11 engine private key, or + * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine + * failure. */ int __must_check tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, @@ -279,10 +282,12 @@ tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, * @tls_ctx: TLS context data from tls_init() * @params: Global TLS parameters * Returns: 0 on success, -1 on failure, - * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing - * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine + * failure, or * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the - * PKCS#11 engine private key. + * PKCS#11 engine private key, or + * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine + * failure. */ int __must_check tls_global_set_params( void *tls_ctx, const struct tls_connection_params *params); @@ -301,22 +306,28 @@ int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @verify_peer: 1 = verify peer certificate + * @flags: Connection flags (TLS_CONN_*) + * @session_ctx: Session caching context or %NULL to use default + * @session_ctx_len: Length of @session_ctx in bytes. * Returns: 0 on success, -1 on failure */ int __must_check tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer); + int verify_peer, + unsigned int flags, + const u8 *session_ctx, + size_t session_ctx_len); /** - * tls_connection_get_keys - Get master key and random data from TLS connection + * tls_connection_get_random - Get random data from TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * @keys: Structure of key/random data (filled on success) + * @data: Structure of client/server random data (filled on success) * Returns: 0 on success, -1 on failure */ -int __must_check tls_connection_get_keys(void *tls_ctx, +int __must_check tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys); + struct tls_random *data); /** * tls_connection_prf - Use TLS-PRF to derive keying material @@ -325,23 +336,22 @@ int __must_check tls_connection_get_keys(void *tls_ctx, * @label: Label (e.g., description of the key) for PRF * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random + * @skip_keyblock: Skip TLS key block from the beginning of PRF output * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure * - * This function is optional to implement if tls_connection_get_keys() provides - * access to master secret and server/client random values. If these values are - * not exported from the TLS library, tls_connection_prf() is required so that - * further keying material can be derived from the master secret. If not - * implemented, the function will still need to be defined, but it can just - * return -1. Example implementation of this function is in tls_prf_sha1_md5() - * when it is called with seed set to client_random|server_random (or - * server_random|client_random). + * tls_connection_prf() is required so that further keying material can be + * derived from the master secret. Example implementation of this function is in + * tls_prf_sha1_md5() when it is called with seed set to + * client_random|server_random (or server_random|client_random). For TLSv1.2 and + * newer, a different PRF is needed, though. */ int __must_check tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, + int skip_keyblock, u8 *out, size_t out_len); /** @@ -460,6 +470,19 @@ int __must_check tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers); +/** + * tls_get_version - Get the current TLS version number + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for returning the TLS version number + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the currently used TLS version number. + */ +int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen); + /** * tls_get_cipher - Get current cipher name * @tls_ctx: TLS context data from tls_init() @@ -527,23 +550,6 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn); -/** - * tls_connection_get_keyblock_size - Get TLS key_block size - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * Returns: Size of the key_block for the negotiated cipher suite or -1 on - * failure - */ -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn); - -/** - * tls_capabilities - Get supported TLS capabilities - * @tls_ctx: TLS context data from tls_init() - * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*) - */ -unsigned int tls_capabilities(void *tls_ctx); - typedef int (*tls_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret); @@ -569,4 +575,14 @@ void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags); int tls_get_library_version(char *buf, size_t buf_len); +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data); + +void tls_connection_set_success_data_resumed(struct tls_connection *conn); + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn); + +void tls_connection_remove_session(struct tls_connection *conn); + #endif /* TLS_H */ diff --git a/contrib/wpa/src/crypto/tls_gnutls.c b/contrib/wpa/src/crypto/tls_gnutls.c index 65db6fcc256..f994379b16b 100644 --- a/contrib/wpa/src/crypto/tls_gnutls.c +++ b/contrib/wpa/src/crypto/tls_gnutls.c @@ -708,7 +708,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { if (conn == NULL || conn->session == NULL) return -1; @@ -722,8 +723,8 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, } -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) { #if GNUTLS_VERSION_NUMBER >= 0x030012 gnutls_datum_t client, server; @@ -747,9 +748,9 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, - u8 *out, size_t out_len) + int skip_keyblock, u8 *out, size_t out_len) { - if (conn == NULL || conn->session == NULL) + if (conn == NULL || conn->session == NULL || skip_keyblock) return -1; return gnutls_prf(conn->session, os_strlen(label), label, @@ -1426,6 +1427,14 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, } +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + /* TODO */ + return -1; +} + + int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -1476,20 +1485,6 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ - /* TODO */ - return -1; -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, void *ctx) @@ -1503,3 +1498,26 @@ int tls_get_library_version(char *buf, size_t buf_len) return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s", GNUTLS_VERSION, gnutls_check_version(NULL)); } + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ +} diff --git a/contrib/wpa/src/crypto/tls_internal.c b/contrib/wpa/src/crypto/tls_internal.c index 0c955da29f1..704751d308f 100644 --- a/contrib/wpa/src/crypto/tls_internal.c +++ b/contrib/wpa/src/crypto/tls_internal.c @@ -192,26 +192,31 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (params->subject_match) { wpa_printf(MSG_INFO, "TLS: subject_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->altsubject_match) { wpa_printf(MSG_INFO, "TLS: altsubject_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->suffix_match) { wpa_printf(MSG_INFO, "TLS: suffix_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->domain_match) { wpa_printf(MSG_INFO, "TLS: domain_match not supported"); + tlsv1_cred_free(cred); return -1; } if (params->openssl_ciphers) { - wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); + wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported"); + tlsv1_cred_free(cred); return -1; } @@ -323,7 +328,8 @@ int tls_global_set_verify(void *tls_ctx, int check_crl) int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) @@ -333,16 +339,30 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, + struct tls_random *data) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) - return tlsv1_client_get_keys(conn->client, keys); + return tlsv1_client_get_random(conn->client, data); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) - return tlsv1_server_get_keys(conn->server, keys); + return tlsv1_server_get_random(conn->server, data); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +static int tls_get_keyblock_size(struct tls_connection *conn) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_keyblock_size(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_keyblock_size(conn->server); #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } @@ -350,23 +370,41 @@ int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, - u8 *out, size_t out_len) + int skip_keyblock, u8 *out, size_t out_len) { + int ret = -1, skip = 0; + u8 *tmp_out = NULL; + u8 *_out = out; + + if (skip_keyblock) { + skip = tls_get_keyblock_size(conn); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + } + #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - return tlsv1_client_prf(conn->client, label, - server_random_first, - out, out_len); + ret = tlsv1_client_prf(conn->client, label, + server_random_first, + _out, out_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - return tlsv1_server_prf(conn->server, label, - server_random_first, - out, out_len); + ret = tlsv1_server_prf(conn->server, label, + server_random_first, + _out, out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ - return -1; + if (ret == 0 && skip_keyblock) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip); + + return ret; } @@ -580,6 +618,14 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, } +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + /* TODO */ + return -1; +} + + int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -637,27 +683,6 @@ int tls_connection_get_write_alerts(void *tls_ctx, } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ -#ifdef CONFIG_TLS_INTERNAL_CLIENT - if (conn->client) - return tlsv1_client_get_keyblock_size(conn->client); -#endif /* CONFIG_TLS_INTERNAL_CLIENT */ -#ifdef CONFIG_TLS_INTERNAL_SERVER - if (conn->server) - return tlsv1_server_get_keyblock_size(conn->server); -#endif /* CONFIG_TLS_INTERNAL_SERVER */ - return -1; -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, @@ -683,3 +708,26 @@ int tls_get_library_version(char *buf, size_t buf_len) { return os_snprintf(buf, buf_len, "internal"); } + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ +} diff --git a/contrib/wpa/src/crypto/tls_none.c b/contrib/wpa/src/crypto/tls_none.c index a6d210afcf0..ae392ad8aa0 100644 --- a/contrib/wpa/src/crypto/tls_none.c +++ b/contrib/wpa/src/crypto/tls_none.c @@ -72,14 +72,15 @@ int tls_global_set_verify(void *tls_ctx, int check_crl) int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { return -1; } -int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, + struct tls_random *data) { return -1; } @@ -87,7 +88,7 @@ int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, - u8 *out, size_t out_len) + int skip_keyblock, u8 *out, size_t out_len) { return -1; } @@ -140,6 +141,13 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, } +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + return -1; +} + + int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -181,20 +189,30 @@ int tls_connection_get_write_alerts(void *tls_ctx, } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - int tls_get_library_version(char *buf, size_t buf_len) { return os_snprintf(buf, buf_len, "none"); } + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ +} diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c index 52db8fc076a..8b7b47bc256 100644 --- a/contrib/wpa/src/crypto/tls_openssl.c +++ b/contrib/wpa/src/crypto/tls_openssl.c @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for OpenSSL - * Copyright (c) 2004-2013, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,15 +23,19 @@ #ifndef OPENSSL_NO_ENGINE #include #endif /* OPENSSL_NO_ENGINE */ +#ifndef OPENSSL_NO_DSA +#include +#endif +#ifndef OPENSSL_NO_DH +#include +#endif #include "common.h" #include "crypto.h" +#include "sha1.h" +#include "sha256.h" #include "tls.h" -#if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data) -#define OPENSSL_SUPPORTS_CTX_APP_DATA -#endif - #if OPENSSL_VERSION_NUMBER < 0x10000000L /* ERR_remove_thread_state replaces ERR_remove_state and the latter is * deprecated. However, OpenSSL 0.9.8 doesn't include @@ -70,6 +74,7 @@ static BIO * BIO_from_keystore(const char *key) #endif /* ANDROID */ static int tls_openssl_ref_count = 0; +static int tls_ex_idx_session = -1; struct tls_context { void (*event_cb)(void *ctx, enum tls_event ev, @@ -82,6 +87,11 @@ struct tls_context { static struct tls_context *tls_global = NULL; +struct tls_data { + SSL_CTX *ssl; + unsigned int tls_session_lifetime; +}; + struct tls_connection { struct tls_context *context; SSL_CTX *ssl_ctx; @@ -105,6 +115,7 @@ struct tls_connection { unsigned int cert_probe:1; unsigned int server_cert_only:1; unsigned int invalid_hb_used:1; + unsigned int success_data:1; u8 srv_cert_hash[32]; @@ -113,6 +124,11 @@ struct tls_connection { X509 *peer_cert; X509 *peer_issuer; X509 *peer_issuer_issuer; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; +#endif }; @@ -735,8 +751,27 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) #endif /* OPENSSL_NO_ENGINE */ +static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess) +{ + struct wpabuf *buf; + + if (tls_ex_idx_session < 0) + return; + buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (!buf) + return; + wpa_printf(MSG_DEBUG, + "OpenSSL: Free application session data %p (sess %p)", + buf, sess); + wpabuf_free(buf); + + SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL); +} + + void * tls_init(const struct tls_config *conf) { + struct tls_data *data; SSL_CTX *ssl; struct tls_context *context; const char *ciphers; @@ -748,7 +783,9 @@ void * tls_init(const struct tls_config *conf) #ifdef CONFIG_FIPS #ifdef OPENSSL_FIPS if (conf && conf->fips_mode) { - if (!FIPS_mode_set(1)) { + static int fips_enabled = 0; + + if (!fips_enabled && !FIPS_mode_set(1)) { wpa_printf(MSG_ERROR, "Failed to enable FIPS " "mode"); ERR_load_crypto_strings(); @@ -756,8 +793,10 @@ void * tls_init(const struct tls_config *conf) os_free(tls_global); tls_global = NULL; return NULL; - } else + } else { wpa_printf(MSG_INFO, "Running in FIPS mode"); + fips_enabled = 1; + } } #else /* OPENSSL_FIPS */ if (conf && conf->fips_mode) { @@ -791,38 +830,58 @@ void * tls_init(const struct tls_config *conf) PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ } else { -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA - /* Newer OpenSSL can store app-data per-SSL */ context = tls_context_new(conf); if (context == NULL) return NULL; -#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */ - context = tls_global; -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ } tls_openssl_ref_count++; - ssl = SSL_CTX_new(SSLv23_method()); + data = os_zalloc(sizeof(*data)); + if (data) + ssl = SSL_CTX_new(SSLv23_method()); + else + ssl = NULL; if (ssl == NULL) { tls_openssl_ref_count--; -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA if (context != tls_global) os_free(context); -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ if (tls_openssl_ref_count == 0) { os_free(tls_global); tls_global = NULL; } return NULL; } + data->ssl = ssl; + if (conf) + data->tls_session_lifetime = conf->tls_session_lifetime; SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); SSL_CTX_set_info_callback(ssl, ssl_info_cb); -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA SSL_CTX_set_app_data(ssl, context); -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + if (data->tls_session_lifetime > 0) { + SSL_CTX_set_quiet_shutdown(ssl, 1); + /* + * Set default context here. In practice, this will be replaced + * by the per-EAP method context in tls_connection_set_verify(). + */ + SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7); + SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER); + SSL_CTX_set_timeout(ssl, data->tls_session_lifetime); + SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb); + } else { + SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF); + } + + if (tls_ex_idx_session < 0) { + tls_ex_idx_session = SSL_SESSION_get_ex_new_index( + 0, NULL, NULL, NULL, NULL); + if (tls_ex_idx_session < 0) { + tls_deinit(data); + return NULL; + } + } #ifndef OPENSSL_NO_ENGINE wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); @@ -835,7 +894,7 @@ void * tls_init(const struct tls_config *conf) if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, conf->pkcs11_module_path)) { - tls_deinit(ssl); + tls_deinit(data); return NULL; } } @@ -849,22 +908,23 @@ void * tls_init(const struct tls_config *conf) wpa_printf(MSG_ERROR, "OpenSSL: Failed to set cipher string '%s'", ciphers); - tls_deinit(ssl); + tls_deinit(data); return NULL; } - return ssl; + return data; } void tls_deinit(void *ssl_ctx) { - SSL_CTX *ssl = ssl_ctx; -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + struct tls_data *data = ssl_ctx; + SSL_CTX *ssl = data->ssl; struct tls_context *context = SSL_CTX_get_app_data(ssl); if (context != tls_global) os_free(context); -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + if (data->tls_session_lifetime > 0) + SSL_CTX_flush_sessions(ssl, 0); SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -881,9 +941,32 @@ void tls_deinit(void *ssl_ctx) os_free(tls_global); tls_global = NULL; } + + os_free(data); } +#ifndef OPENSSL_NO_ENGINE + +/* Cryptoki return values */ +#define CKR_PIN_INCORRECT 0x000000a0 +#define CKR_PIN_INVALID 0x000000a1 +#define CKR_PIN_LEN_RANGE 0x000000a2 + +/* libp11 */ +#define ERR_LIB_PKCS11 ERR_LIB_USER + +static int tls_is_pin_error(unsigned int err) +{ + return ERR_GET_LIB(err) == ERR_LIB_PKCS11 && + (ERR_GET_REASON(err) == CKR_PIN_INCORRECT || + ERR_GET_REASON(err) == CKR_PIN_INVALID || + ERR_GET_REASON(err) == CKR_PIN_LEN_RANGE); +} + +#endif /* OPENSSL_NO_ENGINE */ + + static int tls_engine_init(struct tls_connection *conn, const char *engine_id, const char *pin, const char *key_id, const char *cert_id, const char *ca_cert_id) @@ -935,11 +1018,16 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, key_id, NULL, &key_cb); if (!conn->private_key) { + unsigned long err = ERR_get_error(); + wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id '%s' [%s]", key_id, - ERR_error_string(ERR_get_error(), NULL)); - ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + ERR_error_string(err, NULL)); + if (tls_is_pin_error(err)) + ret = TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN; + else + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; goto err; } } @@ -1030,19 +1118,16 @@ static void tls_msg_cb(int write_p, int version, int content_type, struct tls_connection * tls_connection_init(void *ssl_ctx) { - SSL_CTX *ssl = ssl_ctx; + struct tls_data *data = ssl_ctx; + SSL_CTX *ssl = data->ssl; struct tls_connection *conn; long options; -#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA struct tls_context *context = SSL_CTX_get_app_data(ssl); -#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */ - struct tls_context *context = tls_global; -#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; - conn->ssl_ctx = ssl_ctx; + conn->ssl_ctx = ssl; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { tls_show_errors(MSG_INFO, __func__, @@ -1091,6 +1176,14 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; + if (conn->success_data) { + /* + * Make sure ssl_clear_bad_session() does not remove this + * session. + */ + SSL_set_quiet_shutdown(conn->ssl, 1); + SSL_shutdown(conn->ssl); + } SSL_free(conn->ssl); tls_engine_deinit(conn); os_free(conn->subject_match); @@ -1118,7 +1211,7 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) * and "close notify" shutdown alert would confuse AS. */ SSL_set_quiet_shutdown(conn->ssl, 1); SSL_shutdown(conn->ssl); - return 0; + return SSL_clear(conn->ssl) == 1 ? 0 : -1; } @@ -1617,9 +1710,9 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) #ifndef OPENSSL_NO_STDIO -static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) +static int tls_load_ca_der(struct tls_data *data, const char *ca_cert) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; X509_LOOKUP *lookup; int ret = 0; @@ -1649,11 +1742,12 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) #endif /* OPENSSL_NO_STDIO */ -static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, +static int tls_connection_ca_cert(struct tls_data *data, + struct tls_connection *conn, const char *ca_cert, const u8 *ca_cert_blob, size_t ca_cert_blob_len, const char *ca_path) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; X509_STORE *store; /* @@ -1788,7 +1882,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, tls_show_errors(MSG_WARNING, __func__, "Failed to load root certificates"); if (ca_cert && - tls_load_ca_der(ssl_ctx, ca_cert) == 0) { + tls_load_ca_der(data, ca_cert) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " "DER format CA certificate", __func__); @@ -1797,7 +1891,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } else { wpa_printf(MSG_DEBUG, "TLS: Trusted root " "certificate(s) loaded"); - tls_get_errors(ssl_ctx); + tls_get_errors(data); } #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", @@ -1814,8 +1908,10 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } -static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert) +static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) { + SSL_CTX *ssl_ctx = data->ssl; + if (ca_cert) { if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) { @@ -1843,7 +1939,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) int flags; if (check_crl) { - X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); + struct tls_data *data = ssl_ctx; + X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl); if (cs == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to get " "certificate store when enabling " @@ -1901,10 +1998,44 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, } +static void tls_set_conn_flags(SSL *ssl, unsigned int flags) +{ +#ifdef SSL_OP_NO_TICKET + if (flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_set_options(ssl, SSL_OP_NO_TICKET); +#ifdef SSL_clear_options + else + SSL_clear_options(ssl, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ +#endif /* SSL_OP_NO_TICKET */ + +#ifdef SSL_OP_NO_TLSv1 + if (flags & TLS_CONN_DISABLE_TLSv1_0) + SSL_set_options(ssl, SSL_OP_NO_TLSv1); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1); +#endif /* SSL_OP_NO_TLSv1 */ +#ifdef SSL_OP_NO_TLSv1_1 + if (flags & TLS_CONN_DISABLE_TLSv1_1) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1); +#endif /* SSL_OP_NO_TLSv1_1 */ +#ifdef SSL_OP_NO_TLSv1_2 + if (flags & TLS_CONN_DISABLE_TLSv1_2) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_2); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2); +#endif /* SSL_OP_NO_TLSv1_2 */ +} + + int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { static int counter = 0; + struct tls_data *data = ssl_ctx; if (conn == NULL) return -1; @@ -1919,20 +2050,25 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); } + tls_set_conn_flags(conn->ssl, flags); + conn->flags = flags; + SSL_set_accept_state(conn->ssl); - /* - * Set session id context in order to avoid fatal errors when client - * tries to resume a session. However, set the context to a unique - * value in order to effectively disable session resumption for now - * since not all areas of the server code are ready for it (e.g., - * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS - * handshake). - */ - counter++; - SSL_set_session_id_context(conn->ssl, - (const unsigned char *) &counter, - sizeof(counter)); + if (data->tls_session_lifetime == 0) { + /* + * Set session id context to a unique value to make sure + * session resumption cannot be used either through session + * caching or TLS ticket extension. + */ + counter++; + SSL_set_session_id_context(conn->ssl, + (const unsigned char *) &counter, + sizeof(counter)); + } else if (session_ctx) { + SSL_set_session_id_context(conn->ssl, session_ctx, + session_ctx_len); + } return 0; } @@ -2004,9 +2140,12 @@ static int tls_connection_client_cert(struct tls_connection *conn, } -static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert) +static int tls_global_client_cert(struct tls_data *data, + const char *client_cert) { #ifndef OPENSSL_NO_STDIO + SSL_CTX *ssl_ctx = data->ssl; + if (client_cert == NULL) return 0; @@ -2040,7 +2179,7 @@ static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) #ifdef PKCS12_FUNCS -static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, +static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, const char *passwd) { EVP_PKEY *pkey; @@ -2052,6 +2191,8 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, pkey = NULL; cert = NULL; certs = NULL; + if (!passwd) + passwd = ""; if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { tls_show_errors(MSG_DEBUG, __func__, "Failed to parse PKCS12 file"); @@ -2069,7 +2210,7 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, if (SSL_use_certificate(ssl, cert) != 1) res = -1; } else { - if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1) + if (SSL_CTX_use_certificate(data->ssl, cert) != 1) res = -1; } X509_free(cert); @@ -2081,13 +2222,52 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, if (SSL_use_PrivateKey(ssl, pkey) != 1) res = -1; } else { - if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) + if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1) res = -1; } EVP_PKEY_free(pkey); } if (certs) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_clear_chain_certs(ssl); + while ((cert = sk_X509_pop(certs)) != NULL) { + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: additional certificate" + " from PKCS12: subject='%s'", buf); + if (SSL_add1_chain_cert(ssl, cert) != 1) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to add additional certificate"); + res = -1; + break; + } + } + if (!res) { + /* Try to continue anyway */ + } + sk_X509_free(certs); +#ifndef OPENSSL_IS_BORINGSSL + res = SSL_build_cert_chain(ssl, + SSL_BUILD_CHAIN_FLAG_CHECK | + SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); + if (!res) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to build certificate chain"); + } else if (res == 2) { + wpa_printf(MSG_DEBUG, + "TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates"); + } +#endif /* OPENSSL_IS_BORINGSSL */ + /* + * Try to continue regardless of result since it is possible for + * the extra certificates not to be required. + */ + res = 0; +#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + SSL_CTX_clear_extra_chain_certs(data->ssl); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */ while ((cert = sk_X509_pop(certs)) != NULL) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); @@ -2097,26 +2277,28 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, * There is no SSL equivalent for the chain cert - so * always add it to the context... */ - if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) { + if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1) + { res = -1; break; } } sk_X509_free(certs); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ } PKCS12_free(p12); if (res < 0) - tls_get_errors(ssl_ctx); + tls_get_errors(data); return res; } #endif /* PKCS12_FUNCS */ -static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, - const char *passwd) +static int tls_read_pkcs12(struct tls_data *data, SSL *ssl, + const char *private_key, const char *passwd) { #ifdef PKCS12_FUNCS FILE *f; @@ -2135,7 +2317,7 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, return -1; } - return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + return tls_parse_pkcs12(data, ssl, p12, passwd); #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " @@ -2145,7 +2327,7 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, } -static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, +static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl, const u8 *blob, size_t len, const char *passwd) { #ifdef PKCS12_FUNCS @@ -2158,7 +2340,7 @@ static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, return -1; } - return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + return tls_parse_pkcs12(data, ssl, p12, passwd); #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " @@ -2183,9 +2365,13 @@ static int tls_engine_get_cert(struct tls_connection *conn, if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL", 0, ¶ms, NULL, 1)) { + unsigned long err = ERR_get_error(); + wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id" " '%s' [%s]", cert_id, - ERR_error_string(ERR_get_error(), NULL)); + ERR_error_string(err, NULL)); + if (tls_is_pin_error(err)) + return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN; return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; } if (!params.cert) { @@ -2225,13 +2411,13 @@ static int tls_connection_engine_client_cert(struct tls_connection *conn, } -static int tls_connection_engine_ca_cert(void *_ssl_ctx, +static int tls_connection_engine_ca_cert(struct tls_data *data, struct tls_connection *conn, const char *ca_cert_id) { #ifndef OPENSSL_NO_ENGINE X509 *cert; - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; X509_STORE *store; if (tls_engine_get_cert(conn, ca_cert_id, &cert)) @@ -2297,14 +2483,14 @@ static int tls_connection_engine_private_key(struct tls_connection *conn) } -static int tls_connection_private_key(void *_ssl_ctx, +static int tls_connection_private_key(struct tls_data *data, struct tls_connection *conn, const char *private_key, const char *private_key_passwd, const u8 *private_key_blob, size_t private_key_blob_len) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; char *passwd; int ok; @@ -2350,7 +2536,7 @@ static int tls_connection_private_key(void *_ssl_ctx, break; } - if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, + if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob, private_key_blob_len, passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " "OK"); @@ -2383,7 +2569,7 @@ static int tls_connection_private_key(void *_ssl_ctx, __func__); #endif /* OPENSSL_NO_STDIO */ - if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd) + if (tls_read_pkcs12(data, conn->ssl, private_key, passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " "--> OK"); @@ -2422,9 +2608,11 @@ static int tls_connection_private_key(void *_ssl_ctx, } -static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, +static int tls_global_private_key(struct tls_data *data, + const char *private_key, const char *private_key_passwd) { + SSL_CTX *ssl_ctx = data->ssl; char *passwd; if (private_key == NULL) @@ -2446,7 +2634,7 @@ static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_PEM) != 1 && #endif /* OPENSSL_NO_STDIO */ - tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { + tls_read_pkcs12(data, NULL, private_key, passwd)) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); os_free(passwd); @@ -2541,7 +2729,7 @@ static int tls_connection_dh(struct tls_connection *conn, const char *dh_file) } -static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) +static int tls_global_dh(struct tls_data *data, const char *dh_file) { #ifdef OPENSSL_NO_DH if (dh_file == NULL) @@ -2550,6 +2738,7 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) "dh_file specified"); return -1; #else /* OPENSSL_NO_DH */ + SSL_CTX *ssl_ctx = data->ssl; DH *dh; BIO *bio; @@ -2615,45 +2804,275 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) } -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) +{ + SSL *ssl; + + if (conn == NULL || keys == NULL) + return -1; + ssl = conn->ssl; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) + return -1; + + os_memset(keys, 0, sizeof(*keys)); + keys->client_random = ssl->s3->client_random; + keys->client_random_len = SSL3_RANDOM_SIZE; + keys->server_random = ssl->s3->server_random; + keys->server_random_len = SSL3_RANDOM_SIZE; +#else + if (ssl == NULL) + return -1; + + os_memset(keys, 0, sizeof(*keys)); + keys->client_random = conn->client_random; + keys->client_random_len = SSL_get_client_random( + ssl, conn->client_random, sizeof(conn->client_random)); + keys->server_random = conn->server_random; + keys->server_random_len = SSL_get_server_random( + ssl, conn->server_random, sizeof(conn->server_random)); +#endif + + return 0; +} + + +#ifndef CONFIG_FIPS +static int openssl_get_keyblock_size(SSL *ssl) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + const EVP_CIPHER *c; + const EVP_MD *h; + int md_size; + + if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL || + ssl->read_hash == NULL) + return -1; + + c = ssl->enc_read_ctx->cipher; +#if OPENSSL_VERSION_NUMBER >= 0x00909000L + h = EVP_MD_CTX_md(ssl->read_hash); +#else + h = ssl->read_hash; +#endif + if (h) + md_size = EVP_MD_size(h); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + else if (ssl->s3) + md_size = ssl->s3->tmp.new_mac_secret_size; +#endif + else + return -1; + + wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " + "IV_len=%d", EVP_CIPHER_key_length(c), md_size, + EVP_CIPHER_iv_length(c)); + return 2 * (EVP_CIPHER_key_length(c) + + md_size + + EVP_CIPHER_iv_length(c)); +#else + const SSL_CIPHER *ssl_cipher; + int cipher, digest; + const EVP_CIPHER *c; + const EVP_MD *h; + + ssl_cipher = SSL_get_current_cipher(ssl); + if (!ssl_cipher) + return -1; + cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher); + digest = SSL_CIPHER_get_digest_nid(ssl_cipher); + wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d", + cipher, digest); + if (cipher < 0 || digest < 0) + return -1; + c = EVP_get_cipherbynid(cipher); + h = EVP_get_digestbynid(digest); + if (!c || !h) + return -1; + + wpa_printf(MSG_DEBUG, + "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d", + EVP_CIPHER_key_length(c), EVP_MD_size(h), + EVP_CIPHER_iv_length(c)); + return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) + + EVP_CIPHER_iv_length(c)); +#endif +} +#endif /* CONFIG_FIPS */ + + +static int openssl_tls_prf(struct tls_connection *conn, + const char *label, int server_random_first, + int skip_keyblock, u8 *out, size_t out_len) { #ifdef CONFIG_FIPS wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " "mode"); return -1; #else /* CONFIG_FIPS */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L SSL *ssl; + u8 *rnd; + int ret = -1; + int skip = 0; + u8 *tmp_out = NULL; + u8 *_out = out; + const char *ver; - if (conn == NULL || keys == NULL) + /* + * TLS library did not support key generation, so get the needed TLS + * session parameters and use an internal implementation of TLS PRF to + * derive the key. + */ + + if (conn == NULL) return -1; ssl = conn->ssl; - if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) + if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL || + ssl->session->master_key_length <= 0) + return -1; + ver = SSL_get_version(ssl); + + if (skip_keyblock) { + skip = openssl_get_keyblock_size(ssl); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + } + + rnd = os_malloc(2 * SSL3_RANDOM_SIZE); + if (!rnd) { + os_free(tmp_out); + return -1; + } + + if (server_random_first) { + os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random, + SSL3_RANDOM_SIZE); + } else { + os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random, + SSL3_RANDOM_SIZE); + } + + if (os_strcmp(ver, "TLSv1.2") == 0) { + tls_prf_sha256(ssl->session->master_key, + ssl->session->master_key_length, + label, rnd, 2 * SSL3_RANDOM_SIZE, + _out, skip + out_len); + ret = 0; + } else if (tls_prf_sha1_md5(ssl->session->master_key, + ssl->session->master_key_length, + label, rnd, 2 * SSL3_RANDOM_SIZE, + _out, skip + out_len) == 0) { + ret = 0; + } + os_free(rnd); + if (ret == 0 && skip_keyblock) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip); + + return ret; +#else + SSL *ssl; + SSL_SESSION *sess; + u8 *rnd; + int ret = -1; + int skip = 0; + u8 *tmp_out = NULL; + u8 *_out = out; + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; + unsigned char master_key[64]; + size_t master_key_len; + const char *ver; + + /* + * TLS library did not support key generation, so get the needed TLS + * session parameters and use an internal implementation of TLS PRF to + * derive the key. + */ + + if (conn == NULL) + return -1; + ssl = conn->ssl; + if (ssl == NULL) + return -1; + ver = SSL_get_version(ssl); + sess = SSL_get_session(ssl); + if (!ver || !sess) return -1; - os_memset(keys, 0, sizeof(*keys)); - keys->master_key = ssl->session->master_key; - keys->master_key_len = ssl->session->master_key_length; - keys->client_random = ssl->s3->client_random; - keys->client_random_len = SSL3_RANDOM_SIZE; - keys->server_random = ssl->s3->server_random; - keys->server_random_len = SSL3_RANDOM_SIZE; + if (skip_keyblock) { + skip = openssl_get_keyblock_size(ssl); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + } - return 0; + rnd = os_malloc(2 * SSL3_RANDOM_SIZE); + if (!rnd) { + os_free(tmp_out); + return -1; + } + + SSL_get_client_random(ssl, client_random, sizeof(client_random)); + SSL_get_server_random(ssl, server_random, sizeof(server_random)); + master_key_len = SSL_SESSION_get_master_key(sess, master_key, + sizeof(master_key)); + + if (server_random_first) { + os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, + SSL3_RANDOM_SIZE); + } else { + os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random, + SSL3_RANDOM_SIZE); + } + + if (os_strcmp(ver, "TLSv1.2") == 0) { + tls_prf_sha256(master_key, master_key_len, + label, rnd, 2 * SSL3_RANDOM_SIZE, + _out, skip + out_len); + ret = 0; + } else if (tls_prf_sha1_md5(master_key, master_key_len, + label, rnd, 2 * SSL3_RANDOM_SIZE, + _out, skip + out_len) == 0) { + ret = 0; + } + os_memset(master_key, 0, sizeof(master_key)); + os_free(rnd); + if (ret == 0 && skip_keyblock) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip); + + return ret; +#endif #endif /* CONFIG_FIPS */ } int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, - u8 *out, size_t out_len) + int skip_keyblock, u8 *out, size_t out_len) { #if OPENSSL_VERSION_NUMBER >= 0x10001000L SSL *ssl; if (conn == NULL) return -1; - if (server_random_first) - return -1; + if (server_random_first || skip_keyblock) + return openssl_tls_prf(conn, label, + server_random_first, skip_keyblock, + out, out_len); ssl = conn->ssl; if (SSL_export_keying_material(ssl, out, out_len, label, os_strlen(label), NULL, 0, 0) == 1) { @@ -2661,7 +3080,8 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, return 0; } #endif - return -1; + return openssl_tls_prf(conn, label, server_random_first, + skip_keyblock, out, out_len); } @@ -2676,7 +3096,7 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, * Give TLS handshake data from the server (if available) to OpenSSL * for processing. */ - if (in_data && + if (in_data && wpabuf_len(in_data) > 0 && BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data)) < 0) { tls_show_errors(MSG_INFO, __func__, @@ -2788,8 +3208,14 @@ openssl_connection_handshake(struct tls_connection *conn, return NULL; } - if (SSL_is_init_finished(conn->ssl) && appl_data && in_data) - *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data)); + if (SSL_is_init_finished(conn->ssl)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Handshake finished - resumed=%d", + tls_connection_resumed(conn->ssl_ctx, conn)); + if (appl_data && in_data) + *appl_data = openssl_get_appl_data(conn, + wpabuf_len(in_data)); + } if (conn->invalid_hb_used) { wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response"); @@ -2968,6 +3394,21 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + if (os_strstr(buf, ":ADH-")) { + /* + * Need to drop to security level 0 to allow anonymous + * cipher suites for EAP-FAST. + */ + SSL_set_security_level(conn->ssl, 0); + } else if (SSL_get_security_level(conn->ssl) == 0) { + /* Force at least security level 1 */ + SSL_set_security_level(conn->ssl, 1); + } +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif + if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) { tls_show_errors(MSG_INFO, __func__, "Cipher suite configuration failed"); @@ -2978,6 +3419,22 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, } +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + const char *name; + if (conn == NULL || conn->ssl == NULL) + return -1; + + name = SSL_get_version(conn->ssl); + if (name == NULL) + return -1; + + os_strlcpy(buf, name, buflen); + return 0; +} + + int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -3300,6 +3757,7 @@ static int ocsp_status_cb(SSL *s, void *arg) int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { + struct tls_data *data = tls_ctx; int ret; unsigned long err; int can_pkcs11 = 0; @@ -3341,6 +3799,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (can_pkcs11 == 2 && !engine_id) engine_id = "pkcs11"; +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#if OPENSSL_VERSION_NUMBER < 0x10100000L if (params->flags & TLS_CONN_EAP_FAST) { wpa_printf(MSG_DEBUG, "OpenSSL: Use TLSv1_method() for EAP-FAST"); @@ -3350,6 +3810,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } } +#endif +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", @@ -3371,10 +3833,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; if (engine_id && ca_cert_id) { - if (tls_connection_engine_ca_cert(tls_ctx, conn, - ca_cert_id)) + if (tls_connection_engine_ca_cert(data, conn, ca_cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; - } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + } else if (tls_connection_ca_cert(data, conn, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) @@ -3392,7 +3853,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; - } else if (tls_connection_private_key(tls_ctx, conn, + } else if (tls_connection_private_key(data, conn, params->private_key, params->private_key_passwd, params->private_key_blob, @@ -3416,40 +3877,30 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } -#ifdef SSL_OP_NO_TICKET - if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) - SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); -#ifdef SSL_clear_options - else - SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET); -#endif /* SSL_clear_options */ -#endif /* SSL_OP_NO_TICKET */ - -#ifdef SSL_OP_NO_TLSv1_1 - if (params->flags & TLS_CONN_DISABLE_TLSv1_1) - SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_1); - else - SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_1); -#endif /* SSL_OP_NO_TLSv1_1 */ -#ifdef SSL_OP_NO_TLSv1_2 - if (params->flags & TLS_CONN_DISABLE_TLSv1_2) - SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_2); - else - SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_2); -#endif /* SSL_OP_NO_TLSv1_2 */ + tls_set_conn_flags(conn->ssl, params->flags); #ifdef HAVE_OCSP if (params->flags & TLS_CONN_REQUEST_OCSP) { - SSL_CTX *ssl_ctx = tls_ctx; + SSL_CTX *ssl_ctx = data->ssl; SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp); SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn); } +#else /* HAVE_OCSP */ + if (params->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "OpenSSL: No OCSP support included - reject configuration"); + return -1; + } + if (params->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "OpenSSL: No OCSP support included - allow optional OCSP case to continue"); + } #endif /* HAVE_OCSP */ conn->flags = params->flags; - tls_get_errors(tls_ctx); + tls_get_errors(data); return 0; } @@ -3458,7 +3909,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { - SSL_CTX *ssl_ctx = tls_ctx; + struct tls_data *data = tls_ctx; + SSL_CTX *ssl_ctx = data->ssl; unsigned long err; while ((err = ERR_get_error())) { @@ -3466,19 +3918,12 @@ int tls_global_set_params(void *tls_ctx, __func__, ERR_error_string(err, NULL)); } - if (tls_global_ca_cert(ssl_ctx, params->ca_cert)) - return -1; - - if (tls_global_client_cert(ssl_ctx, params->client_cert)) - return -1; - - if (tls_global_private_key(ssl_ctx, params->private_key, - params->private_key_passwd)) - return -1; - - if (tls_global_dh(ssl_ctx, params->dh_file)) { - wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", - params->dh_file); + if (tls_global_ca_cert(data, params->ca_cert) || + tls_global_client_cert(data, params->client_cert) || + tls_global_private_key(data, params->private_key, + params->private_key_passwd) || + tls_global_dh(data, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to set global parameters"); return -1; } @@ -3514,49 +3959,6 @@ int tls_global_set_params(void *tls_ctx, } -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ - const EVP_CIPHER *c; - const EVP_MD *h; - int md_size; - - if (conn == NULL || conn->ssl == NULL || - conn->ssl->enc_read_ctx == NULL || - conn->ssl->enc_read_ctx->cipher == NULL || - conn->ssl->read_hash == NULL) - return -1; - - c = conn->ssl->enc_read_ctx->cipher; -#if OPENSSL_VERSION_NUMBER >= 0x00909000L - h = EVP_MD_CTX_md(conn->ssl->read_hash); -#else - h = conn->ssl->read_hash; -#endif - if (h) - md_size = EVP_MD_size(h); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - else if (conn->ssl->s3) - md_size = conn->ssl->s3->tmp.new_mac_secret_size; -#endif - else - return -1; - - wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " - "IV_len=%d", EVP_CIPHER_key_length(c), md_size, - EVP_CIPHER_iv_length(c)); - return 2 * (EVP_CIPHER_key_length(c) + - md_size + - EVP_CIPHER_iv_length(c)); -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) /* Pre-shared secred requires a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to @@ -3575,6 +3977,7 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, struct tls_connection *conn = arg; int ret; +#if OPENSSL_VERSION_NUMBER < 0x10100000L if (conn == NULL || conn->session_ticket_cb == NULL) return 0; @@ -3583,6 +3986,23 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, conn->session_ticket_len, s->s3->client_random, s->s3->server_random, secret); +#else + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + SSL_get_client_random(s, client_random, sizeof(client_random)); + SSL_get_server_random(s, server_random, sizeof(server_random)); + + ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, + conn->session_ticket, + conn->session_ticket_len, + client_random, + server_random, secret); +#endif + os_free(conn->session_ticket); conn->session_ticket = NULL; @@ -3656,3 +4076,70 @@ int tls_get_library_version(char *buf, size_t buf_len) OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); } + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ + SSL_SESSION *sess; + struct wpabuf *old; + + if (tls_ex_idx_session < 0) + goto fail; + sess = SSL_get_session(conn->ssl); + if (!sess) + goto fail; + old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (old) { + wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p", + old); + wpabuf_free(old); + } + if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1) + goto fail; + + wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data); + conn->success_data = 1; + return; + +fail: + wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data"); + wpabuf_free(data); +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ + wpa_printf(MSG_DEBUG, + "OpenSSL: Success data accepted for resumed session"); + conn->success_data = 1; +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + SSL_SESSION *sess; + + if (tls_ex_idx_session < 0 || + !(sess = SSL_get_session(conn->ssl))) + return NULL; + return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ + SSL_SESSION *sess; + + sess = SSL_get_session(conn->ssl); + if (!sess) + return; + + if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1) + wpa_printf(MSG_DEBUG, + "OpenSSL: Session was not cached"); + else + wpa_printf(MSG_DEBUG, + "OpenSSL: Removed cached session to disable session resumption"); +} diff --git a/contrib/wpa/src/crypto/tls_schannel.c b/contrib/wpa/src/crypto/tls_schannel.c deleted file mode 100644 index 31a2c946d04..00000000000 --- a/contrib/wpa/src/crypto/tls_schannel.c +++ /dev/null @@ -1,763 +0,0 @@ -/* - * SSL/TLS interface functions for Microsoft Schannel - * Copyright (c) 2005-2009, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -/* - * FIX: Go through all SSPI functions and verify what needs to be freed - * FIX: session resumption - * TODO: add support for server cert chain validation - * TODO: add support for CA cert validation - * TODO: add support for EAP-TLS (client cert/key conf) - */ - -#include "includes.h" -#include -#include -#include -#define SECURITY_WIN32 -#include -#include - -#include "common.h" -#include "tls.h" - - -struct tls_global { - HMODULE hsecurity; - PSecurityFunctionTable sspi; - HCERTSTORE my_cert_store; -}; - -struct tls_connection { - int established, start; - int failed, read_alerts, write_alerts; - - SCHANNEL_CRED schannel_cred; - CredHandle creds; - CtxtHandle context; - - u8 eap_tls_prf[128]; - int eap_tls_prf_set; -}; - - -static int schannel_load_lib(struct tls_global *global) -{ - INIT_SECURITY_INTERFACE pInitSecurityInterface; - - global->hsecurity = LoadLibrary(TEXT("Secur32.dll")); - if (global->hsecurity == NULL) { - wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x", - __func__, (unsigned int) GetLastError()); - return -1; - } - - pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress( - global->hsecurity, "InitSecurityInterfaceA"); - if (pInitSecurityInterface == NULL) { - wpa_printf(MSG_ERROR, "%s: Could not find " - "InitSecurityInterfaceA from Secur32.dll", - __func__); - FreeLibrary(global->hsecurity); - global->hsecurity = NULL; - return -1; - } - - global->sspi = pInitSecurityInterface(); - if (global->sspi == NULL) { - wpa_printf(MSG_ERROR, "%s: Could not read security " - "interface - 0x%x", - __func__, (unsigned int) GetLastError()); - FreeLibrary(global->hsecurity); - global->hsecurity = NULL; - return -1; - } - - return 0; -} - - -void * tls_init(const struct tls_config *conf) -{ - struct tls_global *global; - - global = os_zalloc(sizeof(*global)); - if (global == NULL) - return NULL; - if (schannel_load_lib(global)) { - os_free(global); - return NULL; - } - return global; -} - - -void tls_deinit(void *ssl_ctx) -{ - struct tls_global *global = ssl_ctx; - - if (global->my_cert_store) - CertCloseStore(global->my_cert_store, 0); - FreeLibrary(global->hsecurity); - os_free(global); -} - - -int tls_get_errors(void *ssl_ctx) -{ - return 0; -} - - -struct tls_connection * tls_connection_init(void *ssl_ctx) -{ - struct tls_connection *conn; - - conn = os_zalloc(sizeof(*conn)); - if (conn == NULL) - return NULL; - conn->start = 1; - - return conn; -} - - -void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return; - - os_free(conn); -} - - -int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) -{ - return conn ? conn->established : 0; -} - - -int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) -{ - struct tls_global *global = ssl_ctx; - if (conn == NULL) - return -1; - - conn->eap_tls_prf_set = 0; - conn->established = conn->failed = 0; - conn->read_alerts = conn->write_alerts = 0; - global->sspi->DeleteSecurityContext(&conn->context); - /* FIX: what else needs to be reseted? */ - - return 0; -} - - -int tls_global_set_params(void *tls_ctx, - const struct tls_connection_params *params) -{ - return -1; -} - - -int tls_global_set_verify(void *ssl_ctx, int check_crl) -{ - return -1; -} - - -int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) -{ - return -1; -} - - -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) -{ - /* Schannel does not export master secret or client/server random. */ - return -1; -} - - -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - u8 *out, size_t out_len) -{ - /* - * Cannot get master_key from Schannel, but EapKeyBlock can be used to - * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and - * EAP-TTLS cannot use this, though, since they are using different - * labels. The only option could be to implement TLSv1 completely here - * and just use Schannel or CryptoAPI for low-level crypto - * functionality.. - */ - - if (conn == NULL || !conn->eap_tls_prf_set || server_random_first || - os_strcmp(label, "client EAP encryption") != 0 || - out_len > sizeof(conn->eap_tls_prf)) - return -1; - - os_memcpy(out, conn->eap_tls_prf, out_len); - - return 0; -} - - -static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global, - struct tls_connection *conn) -{ - DWORD sspi_flags, sspi_flags_out; - SecBufferDesc outbuf; - SecBuffer outbufs[1]; - SECURITY_STATUS status; - TimeStamp ts_expiry; - - sspi_flags = ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | - ISC_RET_EXTENDED_ERROR | - ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_MANUAL_CRED_VALIDATION; - - wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__); - - outbufs[0].pvBuffer = NULL; - outbufs[0].BufferType = SECBUFFER_TOKEN; - outbufs[0].cbBuffer = 0; - - outbuf.cBuffers = 1; - outbuf.pBuffers = outbufs; - outbuf.ulVersion = SECBUFFER_VERSION; - -#ifdef UNICODE - status = global->sspi->InitializeSecurityContextW( - &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, - SECURITY_NATIVE_DREP, NULL, 0, &conn->context, - &outbuf, &sspi_flags_out, &ts_expiry); -#else /* UNICODE */ - status = global->sspi->InitializeSecurityContextA( - &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, - SECURITY_NATIVE_DREP, NULL, 0, &conn->context, - &outbuf, &sspi_flags_out, &ts_expiry); -#endif /* UNICODE */ - if (status != SEC_I_CONTINUE_NEEDED) { - wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA " - "failed - 0x%x", - __func__, (unsigned int) status); - return NULL; - } - - if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { - struct wpabuf *buf; - wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", - outbufs[0].pvBuffer, outbufs[0].cbBuffer); - conn->start = 0; - buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, - outbufs[0].cbBuffer); - if (buf == NULL) - return NULL; - global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); - return buf; - } - - wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello"); - - return NULL; -} - - -#ifndef SECPKG_ATTR_EAP_KEY_BLOCK -#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b - -typedef struct _SecPkgContext_EapKeyBlock { - BYTE rgbKeys[128]; - BYTE rgbIVs[64]; -} SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock; -#endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */ - -static int tls_get_eap(struct tls_global *global, struct tls_connection *conn) -{ - SECURITY_STATUS status; - SecPkgContext_EapKeyBlock kb; - - /* Note: Windows NT and Windows Me/98/95 do not support getting - * EapKeyBlock */ - - status = global->sspi->QueryContextAttributes( - &conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb); - if (status != SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes(" - "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)", - __func__, (int) status); - return -1; - } - - wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys", - kb.rgbKeys, sizeof(kb.rgbKeys)); - wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs", - kb.rgbIVs, sizeof(kb.rgbIVs)); - - os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys)); - conn->eap_tls_prf_set = 1; - return 0; -} - - -struct wpabuf * tls_connection_handshake(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data, - struct wpabuf **appl_data) -{ - struct tls_global *global = tls_ctx; - DWORD sspi_flags, sspi_flags_out; - SecBufferDesc inbuf, outbuf; - SecBuffer inbufs[2], outbufs[1]; - SECURITY_STATUS status; - TimeStamp ts_expiry; - struct wpabuf *out_buf = NULL; - - if (appl_data) - *appl_data = NULL; - - if (conn->start) - return tls_conn_hs_clienthello(global, conn); - - wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process", - (int) wpabuf_len(in_data)); - - sspi_flags = ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | - ISC_RET_EXTENDED_ERROR | - ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_MANUAL_CRED_VALIDATION; - - /* Input buffer for Schannel */ - inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data); - inbufs[0].cbBuffer = wpabuf_len(in_data); - inbufs[0].BufferType = SECBUFFER_TOKEN; - - /* Place for leftover data from Schannel */ - inbufs[1].pvBuffer = NULL; - inbufs[1].cbBuffer = 0; - inbufs[1].BufferType = SECBUFFER_EMPTY; - - inbuf.cBuffers = 2; - inbuf.pBuffers = inbufs; - inbuf.ulVersion = SECBUFFER_VERSION; - - /* Output buffer for Schannel */ - outbufs[0].pvBuffer = NULL; - outbufs[0].cbBuffer = 0; - outbufs[0].BufferType = SECBUFFER_TOKEN; - - outbuf.cBuffers = 1; - outbuf.pBuffers = outbufs; - outbuf.ulVersion = SECBUFFER_VERSION; - -#ifdef UNICODE - status = global->sspi->InitializeSecurityContextW( - &conn->creds, &conn->context, NULL, sspi_flags, 0, - SECURITY_NATIVE_DREP, &inbuf, 0, NULL, - &outbuf, &sspi_flags_out, &ts_expiry); -#else /* UNICODE */ - status = global->sspi->InitializeSecurityContextA( - &conn->creds, &conn->context, NULL, sspi_flags, 0, - SECURITY_NATIVE_DREP, &inbuf, 0, NULL, - &outbuf, &sspi_flags_out, &ts_expiry); -#endif /* UNICODE */ - - wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> " - "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d " - "intype[1]=%d outlen[0]=%d", - (int) status, (int) inbufs[0].cbBuffer, - (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer, - (int) inbufs[1].BufferType, - (int) outbufs[0].cbBuffer); - if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED || - (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) { - if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { - wpa_hexdump(MSG_MSGDUMP, "SChannel - output", - outbufs[0].pvBuffer, outbufs[0].cbBuffer); - out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, - outbufs[0].cbBuffer); - global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); - outbufs[0].pvBuffer = NULL; - if (out_buf == NULL) - return NULL; - } - } - - switch (status) { - case SEC_E_INCOMPLETE_MESSAGE: - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE"); - break; - case SEC_I_CONTINUE_NEEDED: - wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED"); - break; - case SEC_E_OK: - /* TODO: verify server certificate chain */ - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake " - "completed successfully"); - conn->established = 1; - tls_get_eap(global, conn); - - /* Need to return something to get final TLS ACK. */ - if (out_buf == NULL) - out_buf = wpabuf_alloc(0); - - if (inbufs[1].BufferType == SECBUFFER_EXTRA) { - wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted " - "application data", - inbufs[1].pvBuffer, inbufs[1].cbBuffer); - if (appl_data) { - *appl_data = wpabuf_alloc_copy( - outbufs[1].pvBuffer, - outbufs[1].cbBuffer); - } - global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); - inbufs[1].pvBuffer = NULL; - } - break; - case SEC_I_INCOMPLETE_CREDENTIALS: - wpa_printf(MSG_DEBUG, - "Schannel: SEC_I_INCOMPLETE_CREDENTIALS"); - break; - case SEC_E_WRONG_PRINCIPAL: - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL"); - break; - case SEC_E_INTERNAL_ERROR: - wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR"); - break; - } - - if (FAILED(status)) { - wpa_printf(MSG_DEBUG, "Schannel: Handshake failed " - "(out_buf=%p)", out_buf); - conn->failed++; - global->sspi->DeleteSecurityContext(&conn->context); - return out_buf; - } - - if (inbufs[1].BufferType == SECBUFFER_EXTRA) { - /* TODO: Can this happen? What to do with this data? */ - wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data", - inbufs[1].pvBuffer, inbufs[1].cbBuffer); - global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); - inbufs[1].pvBuffer = NULL; - } - - return out_buf; -} - - -struct wpabuf * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data, - struct wpabuf **appl_data) -{ - return NULL; -} - - -struct wpabuf * tls_connection_encrypt(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data) -{ - struct tls_global *global = tls_ctx; - SECURITY_STATUS status; - SecBufferDesc buf; - SecBuffer bufs[4]; - SecPkgContext_StreamSizes sizes; - int i; - struct wpabuf *out; - - status = global->sspi->QueryContextAttributes(&conn->context, - SECPKG_ATTR_STREAM_SIZES, - &sizes); - if (status != SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed", - __func__); - return NULL; - } - wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u", - __func__, - (unsigned int) sizes.cbHeader, - (unsigned int) sizes.cbTrailer); - - out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) + - sizes.cbTrailer); - - os_memset(&bufs, 0, sizeof(bufs)); - bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader); - bufs[0].cbBuffer = sizes.cbHeader; - bufs[0].BufferType = SECBUFFER_STREAM_HEADER; - - bufs[1].pvBuffer = wpabuf_put(out, 0); - wpabuf_put_buf(out, in_data); - bufs[1].cbBuffer = wpabuf_len(in_data); - bufs[1].BufferType = SECBUFFER_DATA; - - bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer); - bufs[2].cbBuffer = sizes.cbTrailer; - bufs[2].BufferType = SECBUFFER_STREAM_TRAILER; - - buf.ulVersion = SECBUFFER_VERSION; - buf.cBuffers = 3; - buf.pBuffers = bufs; - - status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0); - - wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> " - "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " - "len[2]=%d type[2]=%d", - (int) status, - (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, - (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, - (int) bufs[2].cbBuffer, (int) bufs[2].BufferType); - wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: " - "out_data=%p bufs %p %p %p", - wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer, - bufs[2].pvBuffer); - - for (i = 0; i < 3; i++) { - if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY) - { - wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs", - bufs[i].pvBuffer, bufs[i].cbBuffer); - } - } - - if (status == SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); - wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data " - "from EncryptMessage", out); - return out; - } - - wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", - __func__, (int) status); - wpabuf_free(out); - return NULL; -} - - -struct wpabuf * tls_connection_decrypt(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data) -{ - struct tls_global *global = tls_ctx; - SECURITY_STATUS status; - SecBufferDesc buf; - SecBuffer bufs[4]; - int i; - struct wpabuf *out, *tmp; - - wpa_hexdump_buf(MSG_MSGDUMP, - "Schannel: Encrypted data to DecryptMessage", in_data); - os_memset(&bufs, 0, sizeof(bufs)); - tmp = wpabuf_dup(in_data); - if (tmp == NULL) - return NULL; - bufs[0].pvBuffer = wpabuf_mhead(tmp); - bufs[0].cbBuffer = wpabuf_len(in_data); - bufs[0].BufferType = SECBUFFER_DATA; - - bufs[1].BufferType = SECBUFFER_EMPTY; - bufs[2].BufferType = SECBUFFER_EMPTY; - bufs[3].BufferType = SECBUFFER_EMPTY; - - buf.ulVersion = SECBUFFER_VERSION; - buf.cBuffers = 4; - buf.pBuffers = bufs; - - status = global->sspi->DecryptMessage(&conn->context, &buf, 0, - NULL); - wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> " - "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " - "len[2]=%d type[2]=%d len[3]=%d type[3]=%d", - (int) status, - (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, - (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, - (int) bufs[2].cbBuffer, (int) bufs[2].BufferType, - (int) bufs[3].cbBuffer, (int) bufs[3].BufferType); - wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: " - "out_data=%p bufs %p %p %p %p", - wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer, - bufs[2].pvBuffer, bufs[3].pvBuffer); - - switch (status) { - case SEC_E_INCOMPLETE_MESSAGE: - wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE", - __func__); - break; - case SEC_E_OK: - wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); - for (i = 0; i < 4; i++) { - if (bufs[i].BufferType == SECBUFFER_DATA) - break; - } - if (i == 4) { - wpa_printf(MSG_DEBUG, "%s: No output data from " - "DecryptMessage", __func__); - wpabuf_free(tmp); - return NULL; - } - wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from " - "DecryptMessage", - bufs[i].pvBuffer, bufs[i].cbBuffer); - out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer); - wpabuf_free(tmp); - return out; - } - - wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", - __func__, (int) status); - wpabuf_free(tmp); - return NULL; -} - - -int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) -{ - return 0; -} - - -int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, - u8 *ciphers) -{ - return -1; -} - - -int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, - char *buf, size_t buflen) -{ - return -1; -} - - -int tls_connection_enable_workaround(void *ssl_ctx, - struct tls_connection *conn) -{ - return 0; -} - - -int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, - int ext_type, const u8 *data, - size_t data_len) -{ - return -1; -} - - -int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->failed; -} - - -int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->read_alerts; -} - - -int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->write_alerts; -} - - -int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, - const struct tls_connection_params *params) -{ - struct tls_global *global = tls_ctx; - ALG_ID algs[1]; - SECURITY_STATUS status; - TimeStamp ts_expiry; - - if (conn == NULL) - return -1; - - if (params->subject_match) { - wpa_printf(MSG_INFO, "TLS: subject_match not supported"); - return -1; - } - - if (params->altsubject_match) { - wpa_printf(MSG_INFO, "TLS: altsubject_match not supported"); - return -1; - } - - if (params->suffix_match) { - wpa_printf(MSG_INFO, "TLS: suffix_match not supported"); - return -1; - } - - if (params->domain_match) { - wpa_printf(MSG_INFO, "TLS: domain_match not supported"); - return -1; - } - - if (params->openssl_ciphers) { - wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); - return -1; - } - - if (global->my_cert_store == NULL && - (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) == - NULL) { - wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x", - __func__, (unsigned int) GetLastError()); - return -1; - } - - os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred)); - conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; - conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1; - algs[0] = CALG_RSA_KEYX; - conn->schannel_cred.cSupportedAlgs = 1; - conn->schannel_cred.palgSupportedAlgs = algs; - conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; -#ifdef UNICODE - status = global->sspi->AcquireCredentialsHandleW( - NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, - &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); -#else /* UNICODE */ - status = global->sspi->AcquireCredentialsHandleA( - NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, - &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); -#endif /* UNICODE */ - if (status != SEC_E_OK) { - wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - " - "0x%x", __func__, (unsigned int) status); - return -1; - } - - return 0; -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - -int tls_get_library_version(char *buf, size_t buf_len) -{ - return os_snprintf(buf, buf_len, "schannel"); -} diff --git a/contrib/wpa/src/drivers/driver.h b/contrib/wpa/src/drivers/driver.h index 03bd1a79a14..3cdab5a7a87 100644 --- a/contrib/wpa/src/drivers/driver.h +++ b/contrib/wpa/src/drivers/driver.h @@ -20,6 +20,7 @@ #define WPA_SUPPLICANT_DRIVER_VERSION 4 #include "common/defs.h" +#include "common/ieee802_11_defs.h" #include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 @@ -341,7 +342,7 @@ struct wpa_driver_scan_params { * is not needed anymore. */ struct wpa_driver_scan_filter { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; } *filter_ssids; @@ -1211,6 +1212,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_HT_IBSS 0x0000001000000000ULL /** Driver supports IBSS with VHT datarates */ #define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL +/** Driver supports automatic band selection */ +#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL u64 flags; #define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001 @@ -1294,6 +1297,13 @@ struct wpa_driver_capa { */ #define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008 u32 rrm_flags; + + /* Driver concurrency capabilities */ + unsigned int conc_capab; + /* Maximum number of concurrent channels on 2.4 GHz */ + unsigned int max_conc_chan_2_4; + /* Maximum number of concurrent channels on 5 GHz */ + unsigned int max_conc_chan_5_0; }; @@ -1394,6 +1404,16 @@ enum wpa_driver_if_type { * WPA_IF_MESH - Mesh interface */ WPA_IF_MESH, + + /* + * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only) + */ + WPA_IF_TDLS, + + /* + * WPA_IF_IBSS - IBSS interface (used for pref freq only) + */ + WPA_IF_IBSS, }; struct wpa_init_params { @@ -1477,6 +1497,7 @@ struct wpa_signal_info { int above_threshold; int current_signal; int avg_signal; + int avg_beacon_signal; int current_noise; int current_txrate; enum chan_width chanwidth; @@ -1576,6 +1597,7 @@ enum drv_br_port_attr { enum drv_br_net_param { DRV_BR_NET_PARAM_GARP_ACCEPT, + DRV_BR_MULTICAST_SNOOPING, }; struct drv_acs_params { @@ -1587,6 +1609,17 @@ struct drv_acs_params { /* Indicates whether HT40 is enabled */ int ht40_enabled; + + /* Indicates whether VHT is enabled */ + int vht_enabled; + + /* Configured ACS channel width */ + u16 ch_width; + + /* ACS channel list info */ + unsigned int ch_list_len; + const u8 *ch_list; + const int *freq_list; }; @@ -1925,10 +1958,12 @@ struct wpa_driver_ops { * @data: IEEE 802.11 management frame with IEEE 802.11 header * @data_len: Size of the management frame * @noack: Do not wait for this frame to be acked (disable retries) + * @freq: Frequency (in MHz) to send the frame on, or 0 to let the + * driver decide * Returns: 0 on success, -1 on failure */ int (*send_mlme)(void *priv, const u8 *data, size_t data_len, - int noack); + int noack, unsigned int freq); /** * update_ft_ies - Update FT (IEEE 802.11r) IEs @@ -2332,7 +2367,8 @@ struct wpa_driver_ops { * Returns: 0 on success, -1 on failure */ int (*sta_set_flags)(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and); + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and); /** * set_tx_queue_params - Set TX queue parameters @@ -2655,18 +2691,6 @@ struct wpa_driver_ops { int (*send_frame)(void *priv, const u8 *data, size_t data_len, int encrypt); - /** - * shared_freq - Get operating frequency of shared interface(s) - * @priv: Private driver interface data - * Returns: Operating frequency in MHz, 0 if no shared operation in - * use, or -1 on failure - * - * This command can be used to request the current operating frequency - * of any virtual interface that shares the same radio to provide - * information for channel selection for other virtual interfaces. - */ - int (*shared_freq)(void *priv); - /** * get_noa - Get current Notice of Absence attribute payload * @priv: Private driver interface data @@ -3381,6 +3405,40 @@ struct wpa_driver_ops { * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD). */ int (*do_acs)(void *priv, struct drv_acs_params *params); + + /** + * set_band - Notify driver of band selection + * @priv: Private driver interface data + * @band: The selected band(s) + * Returns 0 on success, -1 on failure + */ + int (*set_band)(void *priv, enum set_band band); + + /** + * get_pref_freq_list - Get preferred frequency list for an interface + * @priv: Private driver interface data + * @if_type: Interface type + * @num: Number of channels + * @freq_list: Preferred channel frequency list encoded in MHz values + * Returns 0 on success, -1 on failure + * + * This command can be used to query the preferred frequency list from + * the driver specific to a particular interface type. + */ + int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type, + unsigned int *num, unsigned int *freq_list); + + /** + * set_prob_oper_freq - Indicate probable P2P operating channel + * @priv: Private driver interface data + * @freq: Channel frequency in MHz + * Returns 0 on success, -1 on failure + * + * This command can be used to inform the driver of the operating + * frequency that an ongoing P2P group formation is likely to come up + * on. Local device is assuming P2P Client role. + */ + int (*set_prob_oper_freq)(void *priv, unsigned int freq); }; @@ -4557,10 +4615,20 @@ union wpa_event_data { * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED * @pri_channel: Selected primary channel * @sec_channel: Selected secondary channel + * @vht_seg0_center_ch: VHT mode Segment0 center channel + * @vht_seg1_center_ch: VHT mode Segment1 center channel + * @ch_width: Selected Channel width by driver. Driver may choose to + * change hostapd configured ACS channel width due driver internal + * channel restrictions. + * hw_mode: Selected band (used with hw_mode=any) */ struct acs_selected_channels { u8 pri_channel; u8 sec_channel; + u8 vht_seg0_center_ch; + u8 vht_seg1_center_ch; + u16 ch_width; + enum hostapd_hw_mode hw_mode; } acs_selected_channels; }; @@ -4631,6 +4699,6 @@ wpa_get_wowlan_triggers(const char *wowlan_triggers, const struct wpa_driver_capa *capa); /* NULL terminated array of linked in driver wrappers */ -extern struct wpa_driver_ops *wpa_drivers[]; +extern const struct wpa_driver_ops *const wpa_drivers[]; #endif /* DRIVER_H */ diff --git a/contrib/wpa/src/drivers/driver_bsd.c b/contrib/wpa/src/drivers/driver_bsd.c index 63b0e19e78c..b57dd03c9ca 100644 --- a/contrib/wpa/src/drivers/driver_bsd.c +++ b/contrib/wpa/src/drivers/driver_bsd.c @@ -860,8 +860,7 @@ bad: if (drv->sock >= 0) close(drv->sock); os_free(drv->event_buf); - if (drv != NULL) - os_free(drv); + os_free(drv); return NULL; } @@ -894,7 +893,8 @@ bsd_commit(void *priv) static int bsd_set_sta_authorized(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and) + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and) { int authorized = -1; diff --git a/contrib/wpa/src/drivers/driver_ndis.c b/contrib/wpa/src/drivers/driver_ndis.c index bf19a5842d9..2f5ade68194 100644 --- a/contrib/wpa/src/drivers/driver_ndis.c +++ b/contrib/wpa/src/drivers/driver_ndis.c @@ -710,11 +710,11 @@ static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv) /* Disconnect by setting SSID to random (i.e., likely not used). */ static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv) { - char ssid[32]; + char ssid[SSID_MAX_LEN]; int i; - for (i = 0; i < 32; i++) + for (i = 0; i < SSID_MAX_LEN; i++) ssid[i] = rand() & 0xff; - return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32); + return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN); } @@ -807,7 +807,7 @@ static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid( if (wpa_scan_get_ie(r, WLAN_EID_SSID)) return r; /* SSID IE already present */ - if (ssid->SsidLength == 0 || ssid->SsidLength > 32) + if (ssid->SsidLength == 0 || ssid->SsidLength > SSID_MAX_LEN) return r; /* No valid SSID inside scan data */ nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength); diff --git a/contrib/wpa/src/drivers/driver_nl80211.h b/contrib/wpa/src/drivers/driver_nl80211.h index 802589aa758..5c21e0faf55 100644 --- a/contrib/wpa/src/drivers/driver_nl80211.h +++ b/contrib/wpa/src/drivers/driver_nl80211.h @@ -110,7 +110,7 @@ struct wpa_driver_nl80211_data { u8 bssid[ETH_ALEN]; u8 prev_bssid[ETH_ALEN]; int associated; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; enum nl80211_iftype nlmode; enum nl80211_iftype ap_scan_as_station; @@ -145,6 +145,9 @@ struct wpa_driver_nl80211_data { unsigned int get_features_vendor_cmd_avail:1; unsigned int set_rekey_offload:1; unsigned int p2p_go_ctwindow_supported:1; + unsigned int setband_vendor_cmd_avail:1; + unsigned int get_pref_freq_list:1; + unsigned int set_prob_oper_freq:1; u64 remain_on_chan_cookie; u64 send_action_cookie; @@ -169,7 +172,7 @@ struct wpa_driver_nl80211_data { /* From failed authentication command */ int auth_freq; u8 auth_bssid_[ETH_ALEN]; - u8 auth_ssid[32]; + u8 auth_ssid[SSID_MAX_LEN]; size_t auth_ssid_len; int auth_alg; u8 *auth_ie; @@ -232,7 +235,6 @@ int process_bss_event(struct nl_msg *msg, void *arg); #ifdef ANDROID int android_nl_socket_set_nonblocking(struct nl_handle *handle); -int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name); int android_pno_start(struct i802_bss *bss, struct wpa_driver_scan_params *params); int android_pno_stop(struct i802_bss *bss); @@ -270,5 +272,6 @@ int wpa_driver_nl80211_sched_scan(void *priv, int wpa_driver_nl80211_stop_sched_scan(void *priv); struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv); void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv); +const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie); #endif /* DRIVER_NL80211_H */ diff --git a/contrib/wpa/src/drivers/driver_nl80211_android.c b/contrib/wpa/src/drivers/driver_nl80211_android.c index 3cc9a65867f..ba47888843b 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_android.c +++ b/contrib/wpa/src/drivers/driver_nl80211_android.c @@ -151,7 +151,7 @@ int android_pno_stop(struct i802_bss *bss) #ifdef ANDROID_P2P -#ifdef ANDROID_P2P_STUB +#ifdef ANDROID_LIB_STUB int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) { @@ -178,7 +178,7 @@ int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, return 0; } -#endif /* ANDROID_P2P_STUB */ +#endif /* ANDROID_LIB_STUB */ #endif /* ANDROID_P2P */ @@ -188,33 +188,3 @@ int android_nl_socket_set_nonblocking(struct nl_handle *handle) } -int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name) -{ - /* - * Android ICS has very minimal genl_ctrl_resolve() implementation, so - * need to work around that. - */ - struct nl_cache *cache = NULL; - struct genl_family *nl80211 = NULL; - int id = -1; - - if (genl_ctrl_alloc_cache(handle, &cache) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache"); - goto fail; - } - - nl80211 = genl_ctrl_search_by_name(cache, name); - if (nl80211 == NULL) - goto fail; - - id = genl_family_get_id(nl80211); - -fail: - if (nl80211) - genl_family_put(nl80211); - if (cache) - nl_cache_free(cache); - - return id; -} diff --git a/contrib/wpa/src/drivers/driver_nl80211_capa.c b/contrib/wpa/src/drivers/driver_nl80211_capa.c index e0d1d233e2a..4cf31238aeb 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_capa.c +++ b/contrib/wpa/src/drivers/driver_nl80211_capa.c @@ -335,6 +335,33 @@ static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls, } +static int ext_feature_isset(const u8 *ext_features, int ext_features_len, + enum nl80211_ext_feature_index ftidx) +{ + u8 ft_byte; + + if ((int) ftidx / 8 >= ext_features_len) + return 0; + + ft_byte = ext_features[ftidx / 8]; + return (ft_byte & BIT(ftidx % 8)) != 0; +} + + +static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct wpa_driver_capa *capa = info->capa; + + if (tb == NULL) + return; + + if (ext_feature_isset(nla_data(tb), nla_len(tb), + NL80211_EXT_FEATURE_VHT_IBSS)) + capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS; +} + + static void wiphy_info_feature_flags(struct wiphy_info_data *info, struct nlattr *tb) { @@ -509,6 +536,7 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) info->device_ap_sme = 1; wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]); + wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]); wiphy_info_probe_resp_offload(capa, tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); @@ -547,22 +575,34 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) continue; } vinfo = nla_data(nl); - switch (vinfo->subcmd) { - case QCA_NL80211_VENDOR_SUBCMD_TEST: - drv->vendor_cmd_test_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_ROAMING: - drv->roaming_vendor_cmd_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: - drv->dfs_vendor_cmd_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: - drv->get_features_vendor_cmd_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: - drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD; - break; + if (vinfo->vendor_id == OUI_QCA) { + switch (vinfo->subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_TEST: + drv->vendor_cmd_test_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_ROAMING: + drv->roaming_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: + drv->dfs_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: + drv->get_features_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST: + drv->get_pref_freq_list = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL: + drv->set_prob_oper_freq = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: + drv->capa.flags |= + WPA_DRIVER_FLAGS_ACS_OFFLOAD; + break; + case QCA_NL80211_VENDOR_SUBCMD_SETBAND: + drv->setband_vendor_cmd_avail = 1; + break; + } } wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", @@ -717,6 +757,7 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) struct features_info { u8 *flags; size_t flags_len; + struct wpa_driver_capa *capa; }; @@ -742,6 +783,19 @@ static int features_info_handler(struct nl_msg *msg, void *arg) info->flags = nla_data(attr); info->flags_len = nla_len(attr); } + attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA]; + if (attr) + info->capa->conc_capab = nla_get_u32(attr); + + attr = tb_vendor[ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND]; + if (attr) + info->capa->max_conc_chan_2_4 = nla_get_u32(attr); + + attr = tb_vendor[ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND]; + if (attr) + info->capa->max_conc_chan_5_0 = nla_get_u32(attr); } return NL_SKIP; @@ -776,12 +830,16 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) } os_memset(&info, 0, sizeof(info)); + info.capa = &drv->capa; ret = send_and_recv_msgs(drv, msg, features_info_handler, &info); if (ret || !info.flags) return; if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info)) drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY; } diff --git a/contrib/wpa/src/drivers/driver_nl80211_event.c b/contrib/wpa/src/drivers/driver_nl80211_event.c index 87e412dc596..7b0f721e658 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_event.c +++ b/contrib/wpa/src/drivers/driver_nl80211_event.c @@ -271,6 +271,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, struct nlattr *ptk_kek) { union wpa_event_data event; + const u8 *ssid; u16 status_code; if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { @@ -331,6 +332,16 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, if (req_ie) { event.assoc_info.req_ies = nla_data(req_ie); event.assoc_info.req_ies_len = nla_len(req_ie); + + if (cmd == NL80211_CMD_ROAM) { + ssid = nl80211_get_ie(event.assoc_info.req_ies, + event.assoc_info.req_ies_len, + WLAN_EID_SSID); + if (ssid && ssid[1] > 0 && ssid[1] <= 32) { + drv->ssid_len = ssid[1]; + os_memcpy(drv->ssid, ssid + 2, ssid[1]); + } + } } if (resp_ie) { event.assoc_info.resp_ies = nla_data(resp_ie); @@ -1480,6 +1491,25 @@ static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, } +static enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode) +{ + switch (hw_mode) { + case QCA_ACS_MODE_IEEE80211B: + return HOSTAPD_MODE_IEEE80211B; + case QCA_ACS_MODE_IEEE80211G: + return HOSTAPD_MODE_IEEE80211G; + case QCA_ACS_MODE_IEEE80211A: + return HOSTAPD_MODE_IEEE80211A; + case QCA_ACS_MODE_IEEE80211AD: + return HOSTAPD_MODE_IEEE80211AD; + case QCA_ACS_MODE_IEEE80211ANY: + return HOSTAPD_MODE_IEEE80211ANY; + default: + return NUM_HOSTAPD_MODES; + } +} + + static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { @@ -1500,6 +1530,39 @@ static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]); event.acs_selected_channels.sec_channel = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) + event.acs_selected_channels.vht_seg0_center_ch = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) + event.acs_selected_channels.vht_seg1_center_ch = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]) + event.acs_selected_channels.ch_width = + nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]); + if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) { + u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]); + + event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode); + if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES || + event.acs_selected_channels.hw_mode == + HOSTAPD_MODE_IEEE80211ANY) { + wpa_printf(MSG_DEBUG, + "nl80211: Invalid hw_mode %d in ACS selection event", + hw_mode); + return; + } + } + + wpa_printf(MSG_INFO, + "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d", + event.acs_selected_channels.pri_channel, + event.acs_selected_channels.sec_channel, + event.acs_selected_channels.ch_width, + event.acs_selected_channels.vht_seg0_center_ch, + event.acs_selected_channels.vht_seg1_center_ch, + event.acs_selected_channels.hw_mode); + + /* Ignore ACS channel list check for backwards compatibility */ wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event); } diff --git a/contrib/wpa/src/drivers/driver_nl80211_scan.c b/contrib/wpa/src/drivers/driver_nl80211_scan.c index 3911f485f72..4b762eafbe8 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_scan.c +++ b/contrib/wpa/src/drivers/driver_nl80211_scan.c @@ -221,6 +221,9 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); drv->scan_for_auth = 0; + if (TEST_FAIL()) + return -1; + msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params); if (!msg) return -1; @@ -433,7 +436,7 @@ int wpa_driver_nl80211_stop_sched_scan(void *priv) } -static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) +const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) { const u8 *end, *pos; @@ -583,6 +586,11 @@ int bss_info_handler(struct nl_msg *msg, void *arg) r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; if (bss[NL80211_BSS_TSF]) r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); + if (bss[NL80211_BSS_BEACON_TSF]) { + u64 tsf = nla_get_u64(bss[NL80211_BSS_BEACON_TSF]); + if (tsf > r->tsf) + r->tsf = tsf; + } if (bss[NL80211_BSS_SEEN_MS_AGO]) r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); r->ie_len = ie_len; diff --git a/contrib/wpa/src/drivers/driver_privsep.c b/contrib/wpa/src/drivers/driver_privsep.c index de23fbd2b9f..1f1676a20ac 100644 --- a/contrib/wpa/src/drivers/driver_privsep.c +++ b/contrib/wpa/src/drivers/driver_privsep.c @@ -220,6 +220,56 @@ static int wpa_driver_privsep_set_key(const char *ifname, void *priv, } +static int wpa_driver_privsep_authenticate( + void *priv, struct wpa_driver_auth_params *params) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_authenticate *data; + int i, res; + size_t buflen; + u8 *pos; + + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d bssid=" MACSTR + " auth_alg=%d local_state_change=%d p2p=%d", + __func__, priv, params->freq, MAC2STR(params->bssid), + params->auth_alg, params->local_state_change, params->p2p); + + buflen = sizeof(*data) + params->ie_len + params->sae_data_len; + data = os_zalloc(buflen); + if (data == NULL) + return -1; + + data->freq = params->freq; + os_memcpy(data->bssid, params->bssid, ETH_ALEN); + os_memcpy(data->ssid, params->ssid, params->ssid_len); + data->ssid_len = params->ssid_len; + data->auth_alg = params->auth_alg; + data->ie_len = params->ie_len; + for (i = 0; i < 4; i++) { + if (params->wep_key[i]) + os_memcpy(data->wep_key[i], params->wep_key[i], + params->wep_key_len[i]); + data->wep_key_len[i] = params->wep_key_len[i]; + } + data->wep_tx_keyidx = params->wep_tx_keyidx; + data->local_state_change = params->local_state_change; + data->p2p = params->p2p; + pos = (u8 *) (data + 1); + if (params->ie_len) { + os_memcpy(pos, params->ie, params->ie_len); + pos += params->ie_len; + } + if (params->sae_data_len) + os_memcpy(pos, params->sae_data, params->sae_data_len); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen, + NULL, NULL); + os_free(data); + + return res; +} + + static int wpa_driver_privsep_associate( void *priv, struct wpa_driver_associate_params *params) { @@ -281,14 +331,15 @@ static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid) { struct wpa_driver_privsep_data *drv = priv; int res, ssid_len; - u8 reply[sizeof(int) + 32]; + u8 reply[sizeof(int) + SSID_MAX_LEN]; size_t len = sizeof(reply); res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len); if (res < 0 || len < sizeof(int)) return -1; os_memcpy(&ssid_len, reply, sizeof(int)); - if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) { + if (ssid_len < 0 || ssid_len > SSID_MAX_LEN || + sizeof(int) + ssid_len > len) { wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply"); return -1; } @@ -308,6 +359,32 @@ static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, } +static void wpa_driver_privsep_event_auth(void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + struct privsep_event_auth *auth; + + os_memset(&data, 0, sizeof(data)); + if (len < sizeof(*auth)) + return; + auth = (struct privsep_event_auth *) buf; + if (len < sizeof(*auth) + auth->ies_len) + return; + + os_memcpy(data.auth.peer, auth->peer, ETH_ALEN); + os_memcpy(data.auth.bssid, auth->bssid, ETH_ALEN); + data.auth.auth_type = auth->auth_type; + data.auth.auth_transaction = auth->auth_transaction; + data.auth.status_code = auth->status_code; + if (auth->ies_len) { + data.auth.ies = (u8 *) (auth + 1); + data.auth.ies_len = auth->ies_len; + } + + wpa_supplicant_event(ctx, EVENT_AUTH, &data); +} + + static void wpa_driver_privsep_event_assoc(void *ctx, enum wpa_event_type event, u8 *buf, size_t len) @@ -467,6 +544,9 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, case PRIVSEP_EVENT_SCAN_RESULTS: wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); break; + case PRIVSEP_EVENT_SCAN_STARTED: + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); + break; case PRIVSEP_EVENT_ASSOC: wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC, event_buf, event_len); @@ -502,6 +582,9 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, event_len); break; + case PRIVSEP_EVENT_AUTH: + wpa_driver_privsep_event_auth(drv->ctx, event_buf, event_len); + break; } os_free(buf); @@ -702,6 +785,10 @@ static int wpa_driver_privsep_get_capa(void *priv, res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len); if (res < 0 || len != sizeof(*capa)) return -1; + /* For now, no support for passing extended_capa pointers */ + capa->extended_capa = NULL; + capa->extended_capa_mask = NULL; + capa->extended_capa_len = 0; return 0; } @@ -734,6 +821,7 @@ struct wpa_driver_ops wpa_driver_privsep_ops = { .set_param = wpa_driver_privsep_set_param, .scan2 = wpa_driver_privsep_scan, .deauthenticate = wpa_driver_privsep_deauthenticate, + .authenticate = wpa_driver_privsep_authenticate, .associate = wpa_driver_privsep_associate, .get_capa = wpa_driver_privsep_get_capa, .get_mac_addr = wpa_driver_privsep_get_mac_addr, @@ -742,7 +830,7 @@ struct wpa_driver_ops wpa_driver_privsep_ops = { }; -struct wpa_driver_ops *wpa_drivers[] = +const struct wpa_driver_ops *const wpa_drivers[] = { &wpa_driver_privsep_ops, NULL diff --git a/contrib/wpa/src/drivers/drivers.c b/contrib/wpa/src/drivers/drivers.c index f0c3bb3c63b..a98af9ac7d7 100644 --- a/contrib/wpa/src/drivers/drivers.c +++ b/contrib/wpa/src/drivers/drivers.c @@ -47,7 +47,7 @@ extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ #endif /* CONFIG_DRIVER_NONE */ -struct wpa_driver_ops *wpa_drivers[] = +const struct wpa_driver_ops *const wpa_drivers[] = { #ifdef CONFIG_DRIVER_NL80211 &wpa_driver_nl80211_ops, diff --git a/contrib/wpa/src/eap_common/eap_common.c b/contrib/wpa/src/eap_common/eap_common.c index 1de13281c51..51a15d75bc9 100644 --- a/contrib/wpa/src/eap_common/eap_common.c +++ b/contrib/wpa/src/eap_common/eap_common.c @@ -192,7 +192,7 @@ u8 eap_get_id(const struct wpabuf *msg) /** - * eap_get_id - Get EAP Type from wpabuf + * eap_get_type - Get EAP Type from wpabuf * @msg: Buffer starting with an EAP header * Returns: The EAP Type after the EAP header */ diff --git a/contrib/wpa/src/eap_common/eap_fast_common.c b/contrib/wpa/src/eap_common/eap_fast_common.c index fceb1b0adc1..151cc7859c5 100644 --- a/contrib/wpa/src/eap_common/eap_fast_common.c +++ b/contrib/wpa/src/eap_common/eap_fast_common.c @@ -96,49 +96,18 @@ void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, const char *label, size_t len) { - struct tls_keys keys; - u8 *rnd = NULL, *out; - int block_size; + u8 *out; - block_size = tls_connection_get_keyblock_size(ssl_ctx, conn); - if (block_size < 0) - return NULL; - - out = os_malloc(block_size + len); + out = os_malloc(len); if (out == NULL) return NULL; - if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len) - == 0) { - os_memmove(out, out + block_size, len); - return out; + if (tls_connection_prf(ssl_ctx, conn, label, 1, 1, out, len)) { + os_free(out); + return NULL; } - if (tls_connection_get_keys(ssl_ctx, conn, &keys)) - goto fail; - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - if (rnd == NULL) - goto fail; - - os_memcpy(rnd, keys.server_random, keys.server_random_len); - os_memcpy(rnd + keys.server_random_len, keys.client_random, - keys.client_random_len); - - wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " - "expansion", keys.master_key, keys.master_key_len); - if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, block_size + len)) - goto fail; - os_free(rnd); - os_memmove(out, out + block_size, len); return out; - -fail: - os_free(rnd); - os_free(out); - return NULL; } diff --git a/contrib/wpa/src/eap_common/eap_pwd_common.c b/contrib/wpa/src/eap_common/eap_pwd_common.c index 631c363fb7c..4d27623f87b 100644 --- a/contrib/wpa/src/eap_common/eap_pwd_common.c +++ b/contrib/wpa/src/eap_common/eap_pwd_common.c @@ -86,9 +86,10 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, * on the password and identities. */ int compute_password_element(EAP_PWD_group *grp, u16 num, - u8 *password, int password_len, - u8 *id_server, int id_server_len, - u8 *id_peer, int id_peer_len, u8 *token) + const u8 *password, size_t password_len, + const u8 *id_server, size_t id_server_len, + const u8 *id_peer, size_t id_peer_len, + const u8 *token) { BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; struct crypto_hash *hash; @@ -283,10 +284,10 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, } -int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, - BIGNUM *peer_scalar, BIGNUM *server_scalar, - u8 *confirm_peer, u8 *confirm_server, - u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id) +int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, + const BIGNUM *peer_scalar, const BIGNUM *server_scalar, + const u8 *confirm_peer, const u8 *confirm_server, + const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id) { struct crypto_hash *hash; u8 mk[SHA256_MAC_LEN], *cruft; @@ -306,7 +307,7 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, os_free(cruft); return -1; } - eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32)); + eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32)); offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(peer_scalar, cruft + offset); diff --git a/contrib/wpa/src/eap_common/eap_pwd_common.h b/contrib/wpa/src/eap_common/eap_pwd_common.h index c54c4414f11..a0d717edfe7 100644 --- a/contrib/wpa/src/eap_common/eap_pwd_common.h +++ b/contrib/wpa/src/eap_common/eap_pwd_common.h @@ -56,10 +56,15 @@ struct eap_pwd_id { } STRUCT_PACKED; /* common routines */ -int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *, - int, u8 *); -int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *, - u8 *, u8 *, u32 *, u8 *, u8 *, u8 *); +int compute_password_element(EAP_PWD_group *grp, u16 num, + const u8 *password, size_t password_len, + const u8 *id_server, size_t id_server_len, + const u8 *id_peer, size_t id_peer_len, + const u8 *token); +int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, + const BIGNUM *peer_scalar, const BIGNUM *server_scalar, + const u8 *confirm_peer, const u8 *confirm_server, + const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id); struct crypto_hash * eap_pwd_h_init(void); void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len); void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest); diff --git a/contrib/wpa/src/eap_common/eap_sake_common.c b/contrib/wpa/src/eap_common/eap_sake_common.c index a76253d00f6..c22e43ed84b 100644 --- a/contrib/wpa/src/eap_common/eap_sake_common.c +++ b/contrib/wpa/src/eap_common/eap_sake_common.c @@ -16,99 +16,99 @@ static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, - const u8 *pos) + u8 attr_id, u8 len, const u8 *data) { size_t i; - switch (pos[0]) { + switch (attr_id) { case EAP_SAKE_AT_RAND_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S"); - if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { + if (len != EAP_SAKE_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->rand_s = pos + 2; + attr->rand_s = data; break; case EAP_SAKE_AT_RAND_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P"); - if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { + if (len != EAP_SAKE_RAND_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->rand_p = pos + 2; + attr->rand_p = data; break; case EAP_SAKE_AT_MIC_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S"); - if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { + if (len != EAP_SAKE_MIC_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->mic_s = pos + 2; + attr->mic_s = data; break; case EAP_SAKE_AT_MIC_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P"); - if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { + if (len != EAP_SAKE_MIC_LEN) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with " - "invalid length %d", pos[1]); + "invalid payload length %d", len); return -1; } - attr->mic_p = pos + 2; + attr->mic_p = data; break; case EAP_SAKE_AT_SERVERID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID"); - attr->serverid = pos + 2; - attr->serverid_len = pos[1] - 2; + attr->serverid = data; + attr->serverid_len = len; break; case EAP_SAKE_AT_PEERID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID"); - attr->peerid = pos + 2; - attr->peerid_len = pos[1] - 2; + attr->peerid = data; + attr->peerid_len = len; break; case EAP_SAKE_AT_SPI_S: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S"); - attr->spi_s = pos + 2; - attr->spi_s_len = pos[1] - 2; + attr->spi_s = data; + attr->spi_s_len = len; break; case EAP_SAKE_AT_SPI_P: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P"); - attr->spi_p = pos + 2; - attr->spi_p_len = pos[1] - 2; + attr->spi_p = data; + attr->spi_p_len = len; break; case EAP_SAKE_AT_ANY_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ"); - if (pos[1] != 4) { + if (len != 2) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ" - " length %d", pos[1]); + " payload length %d", len); return -1; } - attr->any_id_req = pos + 2; + attr->any_id_req = data; break; case EAP_SAKE_AT_PERM_ID_REQ: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ"); - if (pos[1] != 4) { + if (len != 2) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " - "AT_PERM_ID_REQ length %d", pos[1]); + "AT_PERM_ID_REQ payload length %d", len); return -1; } - attr->perm_id_req = pos + 2; + attr->perm_id_req = data; break; case EAP_SAKE_AT_ENCR_DATA: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA"); - attr->encr_data = pos + 2; - attr->encr_data_len = pos[1] - 2; + attr->encr_data = data; + attr->encr_data_len = len; break; case EAP_SAKE_AT_IV: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); - attr->iv = pos + 2; - attr->iv_len = pos[1] - 2; + attr->iv = data; + attr->iv_len = len; break; case EAP_SAKE_AT_PADDING: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING"); - for (i = 2; i < pos[1]; i++) { - if (pos[i]) { + for (i = 0; i < len; i++) { + if (data[i]) { wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING " "with non-zero pad byte"); return -1; @@ -117,26 +117,26 @@ static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, break; case EAP_SAKE_AT_NEXT_TMPID: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID"); - attr->next_tmpid = pos + 2; - attr->next_tmpid_len = pos[1] - 2; + attr->next_tmpid = data; + attr->next_tmpid_len = len; break; case EAP_SAKE_AT_MSK_LIFE: wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); - if (pos[1] != 6) { + if (len != 4) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " - "AT_MSK_LIFE length %d", pos[1]); + "AT_MSK_LIFE payload length %d", len); return -1; } - attr->msk_life = pos + 2; + attr->msk_life = data; break; default: - if (pos[0] < 128) { + if (attr_id < 128) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable" - " attribute %d", pos[0]); + " attribute %d", attr_id); return -1; } wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable " - "attribute %d", pos[0]); + "attribute %d", attr_id); break; } @@ -180,7 +180,7 @@ int eap_sake_parse_attributes(const u8 *buf, size_t len, return -1; } - if (eap_sake_parse_add_attr(attr, pos)) + if (eap_sake_parse_add_attr(attr, pos[0], pos[1] - 2, pos + 2)) return -1; pos += pos[1]; diff --git a/contrib/wpa/src/eap_common/ikev2_common.c b/contrib/wpa/src/eap_common/ikev2_common.c index 4f9e64eced0..d60358c733f 100644 --- a/contrib/wpa/src/eap_common/ikev2_common.c +++ b/contrib/wpa/src/eap_common/ikev2_common.c @@ -16,7 +16,7 @@ #include "ikev2_common.h" -static struct ikev2_integ_alg ikev2_integ_algs[] = { +static const struct ikev2_integ_alg ikev2_integ_algs[] = { { AUTH_HMAC_SHA1_96, 20, 12 }, { AUTH_HMAC_MD5_96, 16, 12 } }; @@ -24,7 +24,7 @@ static struct ikev2_integ_alg ikev2_integ_algs[] = { #define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs) -static struct ikev2_prf_alg ikev2_prf_algs[] = { +static const struct ikev2_prf_alg ikev2_prf_algs[] = { { PRF_HMAC_SHA1, 20, 20 }, { PRF_HMAC_MD5, 16, 16 } }; @@ -32,7 +32,7 @@ static struct ikev2_prf_alg ikev2_prf_algs[] = { #define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs) -static struct ikev2_encr_alg ikev2_encr_algs[] = { +static const struct ikev2_encr_alg ikev2_encr_algs[] = { { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */ { ENCR_3DES, 24, 8 } }; diff --git a/contrib/wpa/src/eap_peer/eap.c b/contrib/wpa/src/eap_peer/eap.c index 35433f3bd8e..56c24b55032 100644 --- a/contrib/wpa/src/eap_peer/eap.c +++ b/contrib/wpa/src/eap_peer/eap.c @@ -584,7 +584,7 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)", erp->keyname_nai, erp->next_seq); - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, + msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, 1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16, EAP_CODE_INITIATE, hdr->identifier); if (msg == NULL) @@ -708,7 +708,7 @@ SM_STATE(EAP, SEND_RESPONSE) wpabuf_free(sm->lastRespData); if (sm->eapRespData) { if (sm->workaround) - os_memcpy(sm->last_md5, sm->req_md5, 16); + os_memcpy(sm->last_sha1, sm->req_sha1, 20); sm->lastId = sm->reqId; sm->lastRespData = wpabuf_dup(sm->eapRespData); eapol_set_bool(sm, EAPOL_eapResp, TRUE); @@ -914,12 +914,12 @@ static int eap_peer_req_is_duplicate(struct eap_sm *sm) duplicate = (sm->reqId == sm->lastId) && sm->rxReq; if (sm->workaround && duplicate && - os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) { + os_memcmp(sm->req_sha1, sm->last_sha1, 20) != 0) { /* * RFC 4137 uses (reqId == lastId) as the only verification for * duplicate EAP requests. However, this misses cases where the * AS is incorrectly using the same id again; and - * unfortunately, such implementations exist. Use MD5 hash as + * unfortunately, such implementations exist. Use SHA1 hash as * an extra verification for the packets being duplicate to * workaround these issues. */ @@ -1765,7 +1765,7 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) if (sm->workaround) { const u8 *addr[1]; addr[0] = wpabuf_head(req); - md5_vector(1, addr, &plen, sm->req_md5); + sha1_vector(1, addr, &plen, sm->req_sha1); } switch (hdr->code) { @@ -1911,7 +1911,7 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, * structure remains alive while the EAP state machine is active. */ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, void *msg_ctx, struct eap_config *conf) { struct eap_sm *sm; @@ -2400,7 +2400,7 @@ static int eap_allowed_phase2_type(int vendor, int type) u32 eap_get_phase2_type(const char *name, int *vendor) { int v; - u8 type = eap_peer_get_type(name, &v); + u32 type = eap_peer_get_type(name, &v); if (eap_allowed_phase2_type(v, type)) { *vendor = v; return type; diff --git a/contrib/wpa/src/eap_peer/eap.h b/contrib/wpa/src/eap_peer/eap.h index 702463b9d51..1a645af8b20 100644 --- a/contrib/wpa/src/eap_peer/eap.h +++ b/contrib/wpa/src/eap_peer/eap.h @@ -307,7 +307,7 @@ struct eap_config { }; struct eap_sm * eap_peer_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, void *msg_ctx, struct eap_config *conf); void eap_peer_sm_deinit(struct eap_sm *sm); int eap_peer_sm_step(struct eap_sm *sm); diff --git a/contrib/wpa/src/eap_peer/eap_aka.c b/contrib/wpa/src/eap_peer/eap_aka.c index 0662ae73836..dc9e8cc34d4 100644 --- a/contrib/wpa/src/eap_peer/eap_aka.c +++ b/contrib/wpa/src/eap_peer/eap_aka.c @@ -1296,7 +1296,7 @@ static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, &len); - if (pos == NULL || len < 1) { + if (pos == NULL || len < 3) { ret->ignore = TRUE; return NULL; } diff --git a/contrib/wpa/src/eap_peer/eap_eke.c b/contrib/wpa/src/eap_peer/eap_eke.c index 9fec66c0686..dfbda5644f6 100644 --- a/contrib/wpa/src/eap_peer/eap_eke.c +++ b/contrib/wpa/src/eap_peer/eap_eke.c @@ -195,15 +195,14 @@ static int eap_eke_supp_mac(u8 mac) static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, - u32 failure_code) + u8 id, u32 failure_code) { struct wpabuf *resp; wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x", failure_code); - resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE); + resp = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE); if (resp) wpabuf_put_be32(resp, failure_code); @@ -230,9 +229,10 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, const u8 *pos, *end; const u8 *prop = NULL; u8 idtype; + u8 id = eap_get_id(reqData); if (data->state != IDENTITY) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -240,7 +240,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (payload_len < 2 + 4) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -253,7 +253,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (pos + num_prop * 4 > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)", num_prop); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -293,7 +293,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (prop == NULL) { wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN); } @@ -301,7 +301,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, if (pos == end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -312,7 +312,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, os_free(data->serverid); data->serverid = os_malloc(end - pos); if (data->serverid == NULL) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } os_memcpy(data->serverid, pos, end - pos); @@ -320,11 +320,11 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response"); - resp = eap_eke_build_msg(data, eap_get_id(reqData), + resp = eap_eke_build_msg(data, id, 2 + 4 + 1 + data->peerid_len, EAP_EKE_ID); if (resp == NULL) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -339,7 +339,7 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp)); if (data->msgs == NULL) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put_buf(data->msgs, reqData); @@ -366,10 +366,11 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, u8 pub[EAP_EKE_MAX_DH_LEN]; const u8 *password; size_t password_len; + u8 id = eap_get_id(reqData); if (data->state != COMMIT) { wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -378,7 +379,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, password = eap_get_config_password(sm, &password_len); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-EKE: No password configured!"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PASSWD_NOT_FOUND); } @@ -387,7 +388,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (pos + data->sess.dhcomp_len > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -405,7 +406,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, data->serverid, data->serverid_len, data->peerid, data->peerid_len, key) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -416,7 +417,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -424,7 +425,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -433,18 +434,18 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, data->peerid, data->peerid_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response"); - resp = eap_eke_build_msg(data, eap_get_id(reqData), + resp = eap_eke_build_msg(data, id, data->sess.dhcomp_len + data->sess.pnonce_len, EAP_EKE_COMMIT); if (resp == NULL) { os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -453,7 +454,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P"); os_memset(key, 0, sizeof(key)); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } os_memset(key, 0, sizeof(key)); @@ -463,7 +464,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", @@ -472,7 +473,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len, wpabuf_put(resp, 0), &prot_len) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", @@ -484,7 +485,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp)) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put_buf(data->msgs, reqData); @@ -509,11 +510,12 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, u8 auth_s[EAP_EKE_MAX_HASH_LEN]; size_t decrypt_len; u8 *auth; + u8 id = eap_get_id(reqData); if (data->state != CONFIRM) { wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)", data->state); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -524,7 +526,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) { wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PROTO_ERROR); } @@ -532,19 +534,19 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len, nonces, &decrypt_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } if (decrypt_len != (size_t) 2 * data->sess.nonce_len) { wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S", nonces, 2 * data->sess.nonce_len); if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match transmitted Nonce_P"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } @@ -556,30 +558,30 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len, data->peerid, data->peerid_len, data->nonce_p, data->nonce_s) < 0) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len); if (os_memcmp_const(auth_s, pos + data->sess.pnonce_ps_len, data->sess.prf_len) != 0) { wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match"); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_AUTHENTICATION_FAIL); } wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response"); - resp = eap_eke_build_msg(data, eap_get_id(reqData), + resp = eap_eke_build_msg(data, id, data->sess.pnonce_len + data->sess.prf_len, EAP_EKE_CONFIRM); if (resp == NULL) { - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -587,7 +589,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len, wpabuf_put(resp, 0), &prot_len) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpabuf_put(resp, prot_len); @@ -595,7 +597,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, auth = wpabuf_put(resp, data->sess.prf_len); if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) { wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len); @@ -606,7 +608,7 @@ static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, data->msk, data->emsk) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); wpabuf_free(resp); - return eap_eke_build_fail(data, ret, reqData, + return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -638,7 +640,8 @@ static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data, wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code); } - return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR); + return eap_eke_build_fail(data, ret, eap_get_id(reqData), + EAP_EKE_FAIL_NO_ERROR); } @@ -741,6 +744,29 @@ static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *sid; + size_t sid_len; + + if (data->state != SUCCESS) + return NULL; + + sid_len = 1 + 2 * data->sess.nonce_len; + sid = os_malloc(sid_len); + if (sid == NULL) + return NULL; + sid[0] = EAP_TYPE_EKE; + os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len); + os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s, + data->sess.nonce_len); + *len = sid_len; + + return sid; +} + + int eap_peer_eke_register(void) { struct eap_method *eap; @@ -757,6 +783,7 @@ int eap_peer_eke_register(void) eap->isKeyAvailable = eap_eke_isKeyAvailable; eap->getKey = eap_eke_getKey; eap->get_emsk = eap_eke_get_emsk; + eap->getSessionId = eap_eke_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/contrib/wpa/src/eap_peer/eap_fast.c b/contrib/wpa/src/eap_peer/eap_fast.c index 68d7fba8892..4cbe3bacb0a 100644 --- a/contrib/wpa/src/eap_peer/eap_fast.c +++ b/contrib/wpa/src/eap_peer/eap_fast.c @@ -267,8 +267,8 @@ static int eap_fast_derive_msk(struct eap_fast_data *data) } -static void eap_fast_derive_key_auth(struct eap_sm *sm, - struct eap_fast_data *data) +static int eap_fast_derive_key_auth(struct eap_sm *sm, + struct eap_fast_data *data) { u8 *sks; @@ -281,7 +281,7 @@ static void eap_fast_derive_key_auth(struct eap_sm *sm, if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " "session_key_seed"); - return; + return -1; } /* @@ -294,11 +294,12 @@ static void eap_fast_derive_key_auth(struct eap_sm *sm, data->simck_idx = 0; os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN); os_free(sks); + return 0; } -static void eap_fast_derive_key_provisioning(struct eap_sm *sm, - struct eap_fast_data *data) +static int eap_fast_derive_key_provisioning(struct eap_sm *sm, + struct eap_fast_data *data) { os_free(data->key_block_p); data->key_block_p = (struct eap_fast_key_block_provisioning *) @@ -307,7 +308,7 @@ static void eap_fast_derive_key_provisioning(struct eap_sm *sm, sizeof(*data->key_block_p)); if (data->key_block_p == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); - return; + return -1; } /* * RFC 4851, Section 5.2: @@ -326,15 +327,19 @@ static void eap_fast_derive_key_provisioning(struct eap_sm *sm, wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", data->key_block_p->client_challenge, sizeof(data->key_block_p->client_challenge)); + return 0; } -static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) +static int eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) { + int res; + if (data->anon_provisioning) - eap_fast_derive_key_provisioning(sm, data); + res = eap_fast_derive_key_provisioning(sm, data); else - eap_fast_derive_key_auth(sm, data); + res = eap_fast_derive_key_auth(sm, data); + return res; } @@ -1172,7 +1177,7 @@ static struct wpabuf * eap_fast_pac_request(void) static int eap_fast_process_decrypted(struct eap_sm *sm, struct eap_fast_data *data, struct eap_method_ret *ret, - const struct eap_hdr *req, + u8 identifier, struct wpabuf *decrypted, struct wpabuf **out_data) { @@ -1184,18 +1189,18 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, return 0; if (resp) return eap_fast_encrypt_response(sm, data, resp, - req->identifier, out_data); + identifier, out_data); if (tlv.result == EAP_TLV_RESULT_FAILURE) { resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); return eap_fast_encrypt_response(sm, data, resp, - req->identifier, out_data); + identifier, out_data); } if (tlv.iresult == EAP_TLV_RESULT_FAILURE) { resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1); return eap_fast_encrypt_response(sm, data, resp, - req->identifier, out_data); + identifier, out_data); } if (tlv.crypto_binding) { @@ -1277,14 +1282,13 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, resp = wpabuf_alloc(1); } - return eap_fast_encrypt_response(sm, data, resp, req->identifier, + return eap_fast_encrypt_response(sm, data, resp, identifier, out_data); } static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, - struct eap_method_ret *ret, - const struct eap_hdr *req, + struct eap_method_ret *ret, u8 identifier, const struct wpabuf *in_data, struct wpabuf **out_data) { @@ -1309,7 +1313,7 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, /* Received TLS ACK - requesting more fragments */ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST, data->fast_version, - req->identifier, NULL, out_data); + identifier, NULL, out_data); } res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); @@ -1328,7 +1332,7 @@ continue_req: return -1; } - res = eap_fast_process_decrypted(sm, data, ret, req, + res = eap_fast_process_decrypted(sm, data, ret, identifier, in_decrypted, out_data); wpabuf_free(in_decrypted); @@ -1340,7 +1344,7 @@ continue_req: static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len) { const u8 *a_id; - struct pac_tlv_hdr *hdr; + const struct pac_tlv_hdr *hdr; /* * Parse authority identity (A-ID) from the EAP-FAST/Start. This @@ -1350,13 +1354,13 @@ static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len) *id_len = len; if (len > sizeof(*hdr)) { int tlen; - hdr = (struct pac_tlv_hdr *) buf; + hdr = (const struct pac_tlv_hdr *) buf; tlen = be_to_host16(hdr->len); if (be_to_host16(hdr->type) == PAC_TYPE_A_ID && sizeof(*hdr) + tlen <= len) { wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV " "(Start)"); - a_id = (u8 *) (hdr + 1); + a_id = (const u8 *) (hdr + 1); *id_len = tlen; } } @@ -1529,6 +1533,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; struct eap_fast_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret, reqData, &left, &flags); @@ -1545,13 +1550,13 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, left = 0; /* A-ID is not used in further packet processing */ } + wpabuf_set(&msg, pos, left); + resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { /* Process tunneled (encrypted) phase 2 data. */ - struct wpabuf msg; - wpabuf_set(&msg, pos, left); - res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp); + res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp); if (res < 0) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; @@ -1565,8 +1570,15 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, /* Continue processing TLS handshake (phase 1). */ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_FAST, - data->fast_version, id, pos, - left, &resp); + data->fast_version, id, &msg, + &resp); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return resp; + } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char cipher[80]; @@ -1586,20 +1598,24 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, } else data->anon_provisioning = 0; data->resuming = 0; - eap_fast_derive_keys(sm, data); + if (eap_fast_derive_keys(sm, data) < 0) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: Could not derive keys"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + wpabuf_free(resp); + return NULL; + } } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; - wpabuf_set(&msg, pos, left); - res = eap_fast_decrypt(sm, data, ret, req, &msg, - &resp); + res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp); } } diff --git a/contrib/wpa/src/eap_peer/eap_gpsk.c b/contrib/wpa/src/eap_peer/eap_gpsk.c index c54bf116477..902b4ba26d6 100644 --- a/contrib/wpa/src/eap_peer/eap_gpsk.c +++ b/contrib/wpa/src/eap_peer/eap_gpsk.c @@ -274,7 +274,7 @@ static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, struct eap_gpsk_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 identifier, const u8 *payload, size_t payload_len) { @@ -301,7 +301,7 @@ static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, return NULL; } - resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData), + resp = eap_gpsk_send_gpsk_2(data, identifier, csuite_list, csuite_list_len); if (resp == NULL) return NULL; @@ -583,7 +583,7 @@ static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data, static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, struct eap_gpsk_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 identifier, const u8 *payload, size_t payload_len) { @@ -615,7 +615,7 @@ static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, (unsigned long) (end - pos)); } - resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData)); + resp = eap_gpsk_send_gpsk_4(data, identifier); if (resp == NULL) return NULL; @@ -670,6 +670,7 @@ static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; size_t len; + u8 opcode, id; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len); if (pos == NULL || len < 1) { @@ -677,25 +678,27 @@ static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, return NULL; } - wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos); + id = eap_get_id(reqData); + opcode = *pos++; + len--; + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", opcode); ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; - switch (*pos) { + switch (opcode) { case EAP_GPSK_OPCODE_GPSK_1: - resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData, - pos + 1, len - 1); + resp = eap_gpsk_process_gpsk_1(sm, data, ret, id, pos, len); break; case EAP_GPSK_OPCODE_GPSK_3: - resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData, - pos + 1, len - 1); + resp = eap_gpsk_process_gpsk_3(sm, data, ret, id, pos, len); break; default: - wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with " - "unknown opcode %d", *pos); + wpa_printf(MSG_DEBUG, + "EAP-GPSK: Ignoring message with unknown opcode %d", + opcode); ret->ignore = TRUE; return NULL; } diff --git a/contrib/wpa/src/eap_peer/eap_i.h b/contrib/wpa/src/eap_peer/eap_i.h index 2d7fdea2277..99b44dae4e3 100644 --- a/contrib/wpa/src/eap_peer/eap_i.h +++ b/contrib/wpa/src/eap_peer/eap_i.h @@ -328,7 +328,7 @@ struct eap_sm { /* not defined in RFC 4137 */ Boolean changed; void *eapol_ctx; - struct eapol_callbacks *eapol_cb; + const struct eapol_callbacks *eapol_cb; void *eap_method_priv; int init_phase2; int fast_reauth; @@ -338,9 +338,9 @@ struct eap_sm { Boolean rxResp /* LEAP only */; Boolean leap_done; Boolean peap_done; - u8 req_md5[16]; /* MD5() of the current EAP packet */ - u8 last_md5[16]; /* MD5() of the previously received EAP packet; used - * in duplicate request detection. */ + u8 req_sha1[20]; /* SHA1() of the current EAP packet */ + u8 last_sha1[20]; /* SHA1() of the previously received EAP packet; used + * in duplicate request detection. */ void *msg_ctx; void *scard_ctx; diff --git a/contrib/wpa/src/eap_peer/eap_mschapv2.c b/contrib/wpa/src/eap_peer/eap_mschapv2.c index 9e486e7d18f..6acf1e8ad39 100644 --- a/contrib/wpa/src/eap_peer/eap_mschapv2.c +++ b/contrib/wpa/src/eap_peer/eap_mschapv2.c @@ -511,6 +511,11 @@ static struct wpabuf * eap_mschapv2_change_password( struct eap_sm *sm, struct eap_mschapv2_data *data, struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id) { +#ifdef CONFIG_NO_RC4 + wpa_printf(MSG_ERROR, + "EAP-MSCHAPV2: RC4 not support in the build - cannot change password"); + return NULL; +#else /* CONFIG_NO_RC4 */ struct wpabuf *resp; int ms_len; const u8 *username, *password, *new_password; @@ -628,6 +633,7 @@ static struct wpabuf * eap_mschapv2_change_password( fail: wpabuf_free(resp); return NULL; +#endif /* CONFIG_NO_RC4 */ } diff --git a/contrib/wpa/src/eap_peer/eap_pax.c b/contrib/wpa/src/eap_peer/eap_pax.c index 6d1ff208ac7..c920bcd3182 100644 --- a/contrib/wpa/src/eap_peer/eap_pax.c +++ b/contrib/wpa/src/eap_peer/eap_pax.c @@ -333,7 +333,7 @@ static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv, u16 flen, mlen; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len); - if (pos == NULL || len < EAP_PAX_ICV_LEN) { + if (pos == NULL || len < sizeof(*req) + EAP_PAX_ICV_LEN) { ret->ignore = TRUE; return NULL; } diff --git a/contrib/wpa/src/eap_peer/eap_peap.c b/contrib/wpa/src/eap_peer/eap_peap.c index 86a18bb866d..98a48a6cf5d 100644 --- a/contrib/wpa/src/eap_peer/eap_peap.c +++ b/contrib/wpa/src/eap_peer/eap_peap.c @@ -968,6 +968,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; struct eap_peap_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret, reqData, &left, &flags); @@ -998,18 +999,25 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, * should always be, anyway */ } + wpabuf_set(&msg, pos, left); + resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { - struct wpabuf msg; - wpabuf_set(&msg, pos, left); res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); } else { res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP, - data->peap_version, id, pos, - left, &resp); + data->peap_version, id, &msg, + &resp); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return resp; + } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char *label; wpa_printf(MSG_DEBUG, @@ -1077,14 +1085,12 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; - wpabuf_set(&msg, pos, left); res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); } diff --git a/contrib/wpa/src/eap_peer/eap_pwd.c b/contrib/wpa/src/eap_peer/eap_pwd.c index 059bbeecb72..1f785443ee5 100644 --- a/contrib/wpa/src/eap_peer/eap_pwd.c +++ b/contrib/wpa/src/eap_peer/eap_pwd.c @@ -10,6 +10,7 @@ #include "common.h" #include "crypto/sha256.h" +#include "crypto/ms_funcs.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -25,6 +26,7 @@ struct eap_pwd_data { size_t id_server_len; u8 *password; size_t password_len; + int password_hash; u16 group_num; EAP_PWD_group *grp; @@ -86,8 +88,9 @@ static void * eap_pwd_init(struct eap_sm *sm) const u8 *identity, *password; size_t identity_len, password_len; int fragment_size; + int pwhash; - password = eap_get_config_password(sm, &password_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); if (password == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: No password configured!"); return NULL; @@ -129,6 +132,7 @@ static void * eap_pwd_init(struct eap_sm *sm) } os_memcpy(data->password, password, password_len); data->password_len = password_len; + data->password_hash = pwhash; data->out_frag_pos = data->in_frag_pos = 0; data->inbuf = data->outbuf = NULL; @@ -216,6 +220,10 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { struct eap_pwd_id *id; + const u8 *password; + size_t password_len; + u8 pwhashhash[16]; + int res; if (data->state != PWD_ID_Req) { ret->ignore = TRUE; @@ -231,6 +239,9 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, id = (struct eap_pwd_id *) payload; data->group_num = be_to_host16(id->group_num); + wpa_printf(MSG_DEBUG, + "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u", + data->group_num, id->random_function, id->prf, id->prep); if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || (id->prf != EAP_PWD_DEFAULT_PRF)) { ret->ignore = TRUE; @@ -238,6 +249,22 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, return; } + if (id->prep != EAP_PWD_PREP_NONE && + id->prep != EAP_PWD_PREP_MS) { + wpa_printf(MSG_DEBUG, + "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)", + id->prep); + eap_pwd_state(data, FAILURE); + return; + } + + if (id->prep == EAP_PWD_PREP_NONE && data->password_hash) { + wpa_printf(MSG_DEBUG, + "EAP-PWD: Unhashed password not available"); + eap_pwd_state(data, FAILURE); + return; + } + wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", data->group_num); @@ -260,12 +287,46 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, return; } + if (id->prep == EAP_PWD_PREP_MS) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, + "EAP-PWD (peer): MS password hash not supported in FIPS mode"); + eap_pwd_state(data, FAILURE); + return; +#else /* CONFIG_FIPS */ + if (data->password_hash) { + res = hash_nt_password_hash(data->password, pwhashhash); + } else { + u8 pwhash[16]; + + res = nt_password_hash(data->password, + data->password_len, pwhash); + if (res == 0) + res = hash_nt_password_hash(pwhash, pwhashhash); + os_memset(pwhash, 0, sizeof(pwhash)); + } + + if (res) { + eap_pwd_state(data, FAILURE); + return; + } + + password = pwhashhash; + password_len = sizeof(pwhashhash); +#endif /* CONFIG_FIPS */ + } else { + password = data->password; + password_len = data->password_len; + } + /* compute PWE */ - if (compute_password_element(data->grp, data->group_num, - data->password, data->password_len, - data->id_server, data->id_server_len, - data->id_peer, data->id_peer_len, - id->token)) { + res = compute_password_element(data->grp, data->group_num, + password, password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + id->token); + os_memset(pwhashhash, 0, sizeof(pwhashhash)); + if (res) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); eap_pwd_state(data, FAILURE); return; @@ -301,6 +362,23 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL; u16 offset; u8 *ptr, *scalar = NULL, *element = NULL; + size_t prime_len, order_len; + + if (data->state != PWD_Commit_Req) { + ret->ignore = TRUE; + goto fin; + } + + prime_len = BN_num_bytes(data->grp->prime); + order_len = BN_num_bytes(data->grp->order); + + if (payload_len != 2 * prime_len + order_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) (2 * prime_len + order_len)); + goto fin; + } if (((data->private_value = BN_new()) == NULL) || ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || @@ -500,6 +578,18 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; + if (data->state != PWD_Confirm_Req) { + ret->ignore = TRUE; + goto fin; + } + + if (payload_len != SHA256_MAC_LEN) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Confirm payload length %u (expected %u)", + (unsigned int) payload_len, SHA256_MAC_LEN); + goto fin; + } + /* * first build up the ciphersuite which is group | random_function | * prf @@ -783,17 +873,30 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, * if it's the first fragment there'll be a length field */ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + if (len < 2) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Frame too short to contain Total-Length field"); + ret->ignore = TRUE; + return NULL; + } tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " "total length = %d", tot_len); if (tot_len > 15000) return NULL; + if (data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Unexpected new fragment start when previous fragment is still in use"); + ret->ignore = TRUE; + return NULL; + } data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "Out of memory to buffer " "fragments!"); return NULL; } + data->in_frag_pos = 0; pos += sizeof(u16); len -= sizeof(u16); } @@ -873,6 +976,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, /* * we have output! Do we need to fragment it? */ + lm_exch = EAP_PWD_GET_EXCHANGE(lm_exch); len = wpabuf_len(data->outbuf); if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, diff --git a/contrib/wpa/src/eap_peer/eap_sake.c b/contrib/wpa/src/eap_peer/eap_sake.c index 7d14907433e..c4f9843febb 100644 --- a/contrib/wpa/src/eap_peer/eap_sake.c +++ b/contrib/wpa/src/eap_peer/eap_sake.c @@ -141,7 +141,7 @@ static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 id, const u8 *payload, size_t payload_len) { @@ -166,8 +166,7 @@ static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity"); - resp = eap_sake_build_msg(data, eap_get_id(reqData), - 2 + data->peerid_len, + resp = eap_sake_build_msg(data, id, 2 + data->peerid_len, EAP_SAKE_SUBTYPE_IDENTITY); if (resp == NULL) return NULL; @@ -185,7 +184,7 @@ static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, - const struct wpabuf *reqData, + u8 id, const u8 *payload, size_t payload_len) { @@ -247,8 +246,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN; if (data->peerid) rlen += 2 + data->peerid_len; - resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen, - EAP_SAKE_SUBTYPE_CHALLENGE); + resp = eap_sake_build_msg(data, id, rlen, EAP_SAKE_SUBTYPE_CHALLENGE); if (resp == NULL) return NULL; @@ -285,6 +283,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, struct eap_sake_data *data, struct eap_method_ret *ret, + u8 id, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) @@ -323,14 +322,13 @@ static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, ret->allowNotifications = FALSE; wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending " "Response/Auth-Reject"); - return eap_sake_build_msg(data, eap_get_id(reqData), 0, + return eap_sake_build_msg(data, id, 0, EAP_SAKE_SUBTYPE_AUTH_REJECT); } wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm"); - resp = eap_sake_build_msg(data, eap_get_id(reqData), - 2 + EAP_SAKE_MIC_LEN, + resp = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN, EAP_SAKE_SUBTYPE_CONFIRM); if (resp == NULL) return NULL; @@ -367,7 +365,7 @@ static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos, *end; size_t len; - u8 subtype, session_id; + u8 subtype, session_id, id; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len); if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { @@ -377,6 +375,7 @@ static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, req = (const struct eap_sake_hdr *) pos; end = pos + len; + id = eap_get_id(reqData); subtype = req->subtype; session_id = req->session_id; pos = (const u8 *) (req + 1); @@ -402,15 +401,15 @@ static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, switch (subtype) { case EAP_SAKE_SUBTYPE_IDENTITY: - resp = eap_sake_process_identity(sm, data, ret, reqData, + resp = eap_sake_process_identity(sm, data, ret, id, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CHALLENGE: - resp = eap_sake_process_challenge(sm, data, ret, reqData, + resp = eap_sake_process_challenge(sm, data, ret, id, pos, end - pos); break; case EAP_SAKE_SUBTYPE_CONFIRM: - resp = eap_sake_process_confirm(sm, data, ret, reqData, + resp = eap_sake_process_confirm(sm, data, ret, id, reqData, pos, end - pos); break; default: diff --git a/contrib/wpa/src/eap_peer/eap_sim.c b/contrib/wpa/src/eap_peer/eap_sim.c index bd06df78db4..99a2816ce61 100644 --- a/contrib/wpa/src/eap_peer/eap_sim.c +++ b/contrib/wpa/src/eap_peer/eap_sim.c @@ -1042,7 +1042,7 @@ static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv, } pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len); - if (pos == NULL || len < 1) { + if (pos == NULL || len < 3) { ret->ignore = TRUE; return NULL; } diff --git a/contrib/wpa/src/eap_peer/eap_tls.c b/contrib/wpa/src/eap_peer/eap_tls.c index 5aa3fd59125..66a027a626e 100644 --- a/contrib/wpa/src/eap_peer/eap_tls.c +++ b/contrib/wpa/src/eap_peer/eap_tls.c @@ -156,20 +156,6 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm, ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - if (res == -1) { - struct eap_peer_config *config = eap_get_config(sm); - if (config) { - /* - * The TLS handshake failed. So better forget the old - * PIN. It may be wrong, we cannot be sure but trying - * the wrong one again might block it on the card--so - * better ask the user again. - */ - os_free(config->pin); - config->pin = NULL; - } - } - if (resp) { /* * This is likely an alert message, so send it instead of just @@ -228,6 +214,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, u8 flags, id; const u8 *pos; struct eap_tls_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, reqData, &left, &flags); @@ -242,8 +229,9 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, } resp = NULL; + wpabuf_set(&msg, pos, left); res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0, - id, pos, left, &resp); + id, &msg, &resp); if (res < 0) { return eap_tls_failure(sm, data, ret, res, resp, id); diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.c b/contrib/wpa/src/eap_peer/eap_tls_common.c index 8710781618e..af2b7541d70 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.c +++ b/contrib/wpa/src/eap_peer/eap_tls_common.c @@ -68,6 +68,10 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; if (os_strstr(txt, "tls_disable_session_ticket=0")) params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET; + if (os_strstr(txt, "tls_disable_tlsv1_0=1")) + params->flags |= TLS_CONN_DISABLE_TLSv1_0; + if (os_strstr(txt, "tls_disable_tlsv1_0=0")) + params->flags &= ~TLS_CONN_DISABLE_TLSv1_0; if (os_strstr(txt, "tls_disable_tlsv1_1=1")) params->flags |= TLS_CONN_DISABLE_TLSv1_1; if (os_strstr(txt, "tls_disable_tlsv1_1=0")) @@ -196,28 +200,25 @@ static int eap_tls_init_connection(struct eap_sm *sm, } res = tls_connection_set_params(data->ssl_ctx, data->conn, params); - if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { + if (res == TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN) { /* - * At this point with the pkcs11 engine the PIN might be wrong. - * We reset the PIN in the configuration to be sure to not use - * it again and the calling function must request a new one. - */ - os_free(config->pin); - config->pin = NULL; - } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) { - wpa_printf(MSG_INFO, "TLS: Failed to load private key"); - /* - * We do not know exactly but maybe the PIN was wrong, - * so ask for a new one. + * At this point with the pkcs11 engine the PIN is wrong. We + * reset the PIN in the configuration to be sure to not use it + * again and the calling function must request a new one. */ + wpa_printf(MSG_INFO, + "TLS: Bad PIN provided, requesting a new one"); os_free(config->pin); config->pin = NULL; eap_sm_request_pin(sm); sm->ignore = TRUE; - tls_connection_deinit(data->ssl_ctx, data->conn); - data->conn = NULL; - return -1; - } else if (res) { + } else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { + wpa_printf(MSG_INFO, "TLS: Failed to initialize engine"); + } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key"); + sm->ignore = TRUE; + } + if (res) { wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " "parameters"); tls_connection_deinit(data->ssl_ctx, data->conn); @@ -313,53 +314,19 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len) { -#ifndef CONFIG_FIPS - struct tls_keys keys; -#endif /* CONFIG_FIPS */ - u8 *rnd = NULL, *out; + u8 *out; out = os_malloc(len); if (out == NULL) return NULL; - /* First, try to use TLS library function for PRF, if available. */ - if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len) - == 0) - return out; + if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0, + out, len)) { + os_free(out); + return NULL; + } -#ifndef CONFIG_FIPS - /* - * TLS library did not support key generation, so get the needed TLS - * session parameters and use an internal implementation of TLS PRF to - * derive the key. - */ - if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys)) - goto fail; - - if (keys.client_random == NULL || keys.server_random == NULL || - keys.master_key == NULL) - goto fail; - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - if (rnd == NULL) - goto fail; - os_memcpy(rnd, keys.client_random, keys.client_random_len); - os_memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, len)) - goto fail; - - os_free(rnd); return out; - -fail: -#endif /* CONFIG_FIPS */ - os_free(out); - os_free(rnd); - return NULL; } @@ -380,10 +347,10 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len) { - struct tls_keys keys; + struct tls_random keys; u8 *out; - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) return NULL; if (keys.client_random == NULL || keys.server_random == NULL) @@ -514,22 +481,19 @@ static const struct wpabuf * eap_peer_tls_data_reassemble( * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @in_data: Message received from the server - * @in_len: Length of in_data * @out_data: Buffer for returning a pointer to application data (if available) * Returns: 0 on success, 1 if more input data is needed, 2 if application data * is available, -1 on failure */ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, - const u8 *in_data, size_t in_len, + const struct wpabuf *in_data, struct wpabuf **out_data) { const struct wpabuf *msg; int need_more_input; struct wpabuf *appl_data; - struct wpabuf buf; - wpabuf_set(&buf, in_data, in_len); - msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input); + msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; @@ -649,7 +613,6 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, * @peap_version: Version number for EAP-PEAP/TTLS * @id: EAP identifier for the response * @in_data: Message received from the server - * @in_len: Length of in_data * @out_data: Buffer for returning a pointer to the response message * Returns: 0 on success, 1 if more input data is needed, 2 if application data * is available, or -1 on failure @@ -672,14 +635,15 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, */ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, - u8 id, const u8 *in_data, size_t in_len, + u8 id, const struct wpabuf *in_data, struct wpabuf **out_data) { int ret = 0; *out_data = NULL; - if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) { + if (data->tls_out && wpabuf_len(data->tls_out) > 0 && + wpabuf_len(in_data) > 0) { wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output " "fragments are waiting to be sent out"); return -1; @@ -690,8 +654,7 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, * No more data to send out - expect to receive more data from * the AS. */ - int res = eap_tls_process_input(sm, data, in_data, in_len, - out_data); + int res = eap_tls_process_input(sm, data, in_data, out_data); if (res) { /* * Input processing failed (res = -1) or more data is @@ -719,12 +682,18 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, if (tls_connection_get_failed(data->ssl_ctx, data->conn)) { /* TLS processing has failed - return error */ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " - "report error"); + "report error (len=%u)", + (unsigned int) wpabuf_len(data->tls_out)); ret = -1; /* TODO: clean pin if engine used? */ + if (wpabuf_len(data->tls_out) == 0) { + wpabuf_free(data->tls_out); + data->tls_out = NULL; + return -1; + } } - if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { + if (wpabuf_len(data->tls_out) == 0) { /* * TLS negotiation should now be complete since all other cases * needing more data should have been caught above based on @@ -790,20 +759,24 @@ int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, size_t buflen, int verbose) { - char name[128]; + char version[20], name[128]; int len = 0, ret; - if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0) - { - ret = os_snprintf(buf + len, buflen - len, - "EAP TLS cipher=%s\n" - "tls_session_reused=%d\n", - name, tls_connection_resumed(data->ssl_ctx, - data->conn)); - if (os_snprintf_error(buflen - len, ret)) - return len; - len += ret; - } + if (tls_get_version(data->ssl_ctx, data->conn, version, + sizeof(version)) < 0) + version[0] = '\0'; + if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0) + name[0] = '\0'; + + ret = os_snprintf(buf + len, buflen - len, + "eap_tls_version=%s\n" + "EAP TLS cipher=%s\n" + "tls_session_reused=%d\n", + version, name, + tls_connection_resumed(data->ssl_ctx, data->conn)); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; return len; } @@ -1032,7 +1005,7 @@ int eap_peer_select_phase2_methods(struct eap_peer_config *config, { char *start, *pos, *buf; struct eap_method_type *methods = NULL, *_methods; - u8 method; + u32 method; size_t num_methods = 0, prefix_len; if (config == NULL || config->phase2 == NULL) diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.h b/contrib/wpa/src/eap_peer/eap_tls_common.h index 390c2165927..acd2b783617 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.h +++ b/contrib/wpa/src/eap_peer/eap_tls_common.h @@ -100,7 +100,7 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, size_t *len); int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, - u8 id, const u8 *in_data, size_t in_len, + u8 id, const struct wpabuf *in_data, struct wpabuf **out_data); struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, int peap_version); diff --git a/contrib/wpa/src/eap_peer/eap_ttls.c b/contrib/wpa/src/eap_peer/eap_ttls.c index b5c028b5276..b186c9156a7 100644 --- a/contrib/wpa/src/eap_peer/eap_ttls.c +++ b/contrib/wpa/src/eap_peer/eap_ttls.c @@ -175,7 +175,8 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, } avp->avp_code = host_to_be32(avp_code); - avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len)); + avp->avp_length = host_to_be32(((u32) flags << 24) | + (u32) (hdrlen + len)); return avphdr + hdrlen; } @@ -253,11 +254,13 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, } +#ifndef CONFIG_FIPS static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); } +#endif /* CONFIG_FIPS */ static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data, @@ -428,6 +431,10 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ #ifdef EAP_MSCHAPv2 struct wpabuf *msg; u8 *buf, *pos, *challenge, *peer_challenge; @@ -510,6 +517,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); return -1; #endif /* EAP_MSCHAPv2 */ +#endif /* CONFIG_FIPS */ } @@ -518,6 +526,10 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ struct wpabuf *msg; u8 *buf, *pos, *challenge; const u8 *identity, *password; @@ -592,6 +604,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, ret->decision = DECISION_COND_SUCC; return 0; +#endif /* CONFIG_FIPS */ } @@ -654,6 +667,10 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ struct wpabuf *msg; u8 *buf, *pos, *challenge; const u8 *identity, *password; @@ -722,6 +739,7 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, ret->decision = DECISION_COND_SUCC; return 0; +#endif /* CONFIG_FIPS */ } @@ -1385,14 +1403,20 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 identifier, - const u8 *in_data, size_t in_len, + const struct wpabuf *in_data, struct wpabuf **out_data) { int res; res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, data->ttls_version, identifier, - in_data, in_len, out_data); + in_data, out_data); + if (res < 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " @@ -1419,15 +1443,13 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, } if (res == 2) { - struct wpabuf msg; /* * Application data included in the handshake message. */ wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = *out_data; *out_data = NULL; - wpabuf_set(&msg, in_data, in_len); - res = eap_ttls_decrypt(sm, data, ret, identifier, &msg, + res = eap_ttls_decrypt(sm, data, ret, identifier, in_data, out_data); } @@ -1477,6 +1499,7 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, struct wpabuf *resp; const u8 *pos; struct eap_ttls_data *data = priv; + struct wpabuf msg; pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, reqData, &left, &flags); @@ -1497,15 +1520,15 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, left = 0; } + wpabuf_set(&msg, pos, left); + resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { - struct wpabuf msg; - wpabuf_set(&msg, pos, left); res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); } else { res = eap_ttls_process_handshake(sm, data, ret, id, - pos, left, &resp); + &msg, &resp); } eap_ttls_check_auth_status(sm, data, ret); diff --git a/contrib/wpa/src/eap_peer/eap_wsc.c b/contrib/wpa/src/eap_peer/eap_wsc.c index 7ce0a53d0b2..7ac99c7ce72 100644 --- a/contrib/wpa/src/eap_peer/eap_wsc.c +++ b/contrib/wpa/src/eap_peer/eap_wsc.c @@ -557,6 +557,9 @@ send_msg: if (data->out_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive " "message from WPS"); + eap_wsc_state(data, FAIL); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; return NULL; } data->out_used = 0; diff --git a/contrib/wpa/src/eap_server/eap.h b/contrib/wpa/src/eap_server/eap.h index 9de6cb62f51..69eaab8de94 100644 --- a/contrib/wpa/src/eap_server/eap.h +++ b/contrib/wpa/src/eap_server/eap.h @@ -131,6 +131,7 @@ struct eap_config { const u8 *server_id; size_t server_id_len; int erp; + unsigned int tls_session_lifetime; #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; @@ -139,7 +140,7 @@ struct eap_config { struct eap_sm * eap_server_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, struct eap_config *eap_conf); void eap_server_sm_deinit(struct eap_sm *sm); int eap_server_sm_step(struct eap_sm *sm); @@ -149,5 +150,8 @@ int eap_sm_method_pending(struct eap_sm *sm); const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); void eap_server_clear_identity(struct eap_sm *sm); +void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, + const u8 *username, size_t username_len, + const u8 *challenge, const u8 *response); #endif /* EAP_H */ diff --git a/contrib/wpa/src/eap_server/eap_i.h b/contrib/wpa/src/eap_server/eap_i.h index 7d723091ffb..c90443d19cb 100644 --- a/contrib/wpa/src/eap_server/eap_i.h +++ b/contrib/wpa/src/eap_server/eap_i.h @@ -155,7 +155,7 @@ struct eap_sm { /* not defined in RFC 4137 */ Boolean changed; void *eapol_ctx, *msg_ctx; - struct eapol_callbacks *eapol_cb; + const struct eapol_callbacks *eapol_cb; void *eap_method_priv; u8 *identity; size_t identity_len; @@ -210,6 +210,7 @@ struct eap_sm { Boolean initiate_reauth_start_sent; Boolean try_initiate_reauth; int erp; + unsigned int tls_session_lifetime; #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; diff --git a/contrib/wpa/src/eap_server/eap_server.c b/contrib/wpa/src/eap_server/eap_server.c index bd919e570c8..84ecafc7ca3 100644 --- a/contrib/wpa/src/eap_server/eap_server.c +++ b/contrib/wpa/src/eap_server/eap_server.c @@ -96,7 +96,8 @@ static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm, plen += 2 + domain_len; } - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen, + msg = eap_msg_alloc(EAP_VENDOR_IETF, + (EapType) EAP_ERP_TYPE_REAUTH_START, plen, EAP_CODE_INITIATE, id); if (msg == NULL) return NULL; @@ -714,8 +715,8 @@ static void erp_send_finish_reauth(struct eap_sm *sm, plen = 1 + 2 + 2 + os_strlen(nai); if (hash_len) plen += 1 + hash_len; - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, plen, - EAP_CODE_FINISH, id); + msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, + plen, EAP_CODE_FINISH, id); if (msg == NULL) return; wpabuf_put_u8(msg, flags); @@ -745,7 +746,7 @@ static void erp_send_finish_reauth(struct eap_sm *sm, wpabuf_free(sm->lastReqData); sm->lastReqData = NULL; - if (flags & 0x80) { + if ((flags & 0x80) || !erp) { sm->eap_if.eapFail = TRUE; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE MACSTR, MAC2STR(sm->peer_addr)); @@ -799,7 +800,7 @@ SM_STATE(EAP, INITIATE_RECEIVED) sm->rxInitiate = FALSE; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, + pos = eap_hdr_validate(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, sm->eap_if.eapRespData, &len); if (pos == NULL) { wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame"); @@ -1246,6 +1247,17 @@ SM_STEP(EAP) break; } SM_ENTER(EAP, SEND_REQUEST); + if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) { + /* + * This transition is not mentioned in RFC 4137, but it + * is needed to handle cleanly a case where EAP method + * buildReq fails. + */ + wpa_printf(MSG_DEBUG, + "EAP: Method did not return a request"); + SM_ENTER(EAP, FAILURE); + break; + } break; case EAP_METHOD_RESPONSE: /* @@ -1802,7 +1814,7 @@ static void eap_user_free(struct eap_user *user) * This function allocates and initializes an EAP state machine. */ struct eap_sm * eap_server_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, struct eap_config *conf) { struct eap_sm *sm; @@ -1853,6 +1865,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->server_id = conf->server_id; sm->server_id_len = conf->server_id_len; sm->erp = conf->erp; + sm->tls_session_lifetime = conf->tls_session_lifetime; #ifdef CONFIG_TESTING_OPTIONS sm->tls_test_flags = conf->tls_test_flags; @@ -1979,3 +1992,25 @@ void eap_server_clear_identity(struct eap_sm *sm) os_free(sm->identity); sm->identity = NULL; } + + +#ifdef CONFIG_TESTING_OPTIONS +void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, + const u8 *username, size_t username_len, + const u8 *challenge, const u8 *response) +{ + char hex_challenge[30], hex_response[90], user[100]; + + /* Print out Challenge and Response in format supported by asleap. */ + if (username) + printf_encode(user, sizeof(user), username, username_len); + else + user[0] = '\0'; + wpa_snprintf_hex_sep(hex_challenge, sizeof(hex_challenge), + challenge, sizeof(challenge), ':'); + wpa_snprintf_hex_sep(hex_response, sizeof(hex_response), response, 24, + ':'); + wpa_printf(MSG_DEBUG, "[%s/user=%s] asleap -C %s -R %s", + source, user, hex_challenge, hex_response); +} +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/contrib/wpa/src/eap_server/eap_server_eke.c b/contrib/wpa/src/eap_server/eap_server_eke.c index 966f511dddd..ba82be9c3f3 100644 --- a/contrib/wpa/src/eap_server/eap_server_eke.c +++ b/contrib/wpa/src/eap_server/eap_server_eke.c @@ -766,6 +766,29 @@ static Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *sid; + size_t sid_len; + + if (data->state != SUCCESS) + return NULL; + + sid_len = 1 + 2 * data->sess.nonce_len; + sid = os_malloc(sid_len); + if (sid == NULL) + return NULL; + sid[0] = EAP_TYPE_EKE; + os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len); + os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s, + data->sess.nonce_len); + *len = sid_len; + + return sid; +} + + int eap_server_eke_register(void) { struct eap_method *eap; @@ -785,6 +808,7 @@ int eap_server_eke_register(void) eap->getKey = eap_eke_getKey; eap->isSuccess = eap_eke_isSuccess; eap->get_emsk = eap_eke_get_emsk; + eap->getSessionId = eap_eke_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/contrib/wpa/src/eap_server/eap_server_fast.c b/contrib/wpa/src/eap_server/eap_server_fast.c index 6745100d338..bd9018e78b5 100644 --- a/contrib/wpa/src/eap_server/eap_server_fast.c +++ b/contrib/wpa/src/eap_server/eap_server_fast.c @@ -428,7 +428,7 @@ static void * eap_fast_init(struct eap_sm *sm) } data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_FAST)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); eap_fast_reset(sm, data); return NULL; diff --git a/contrib/wpa/src/eap_server/eap_server_mschapv2.c b/contrib/wpa/src/eap_server/eap_server_mschapv2.c index 05848d2eaac..98d74e0d717 100644 --- a/contrib/wpa/src/eap_server/eap_server_mschapv2.c +++ b/contrib/wpa/src/eap_server/eap_server_mschapv2.c @@ -360,6 +360,19 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, } } +#ifdef CONFIG_TESTING_OPTIONS + { + u8 challenge[8]; + + if (challenge_hash(peer_challenge, data->auth_challenge, + username, username_len, challenge) == 0) { + eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2", + username, username_len, + challenge, nt_response); + } + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (username_len != user_len || os_memcmp(username, user, username_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); diff --git a/contrib/wpa/src/eap_server/eap_server_peap.c b/contrib/wpa/src/eap_server/eap_server_peap.c index faa0fd2f238..51062b0987e 100644 --- a/contrib/wpa/src/eap_server/eap_server_peap.c +++ b/contrib/wpa/src/eap_server/eap_server_peap.c @@ -95,6 +95,37 @@ static void eap_peap_state(struct eap_peap_data *data, int state) eap_peap_state_txt(data->state), eap_peap_state_txt(state)); data->state = state; + if (state == FAILURE || state == FAILURE_REQ) + tls_connection_remove_session(data->ssl.conn); +} + + +static void eap_peap_valid_session(struct eap_sm *sm, + struct eap_peap_data *data) +{ + struct wpabuf *buf; + + if (!sm->tls_session_lifetime || + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = wpabuf_alloc(1 + 1 + sm->identity_len); + if (!buf) + return; + wpabuf_put_u8(buf, EAP_TYPE_PEAP); + if (sm->identity) { + u8 id_len; + + if (sm->identity_len <= 255) + id_len = sm->identity_len; + else + id_len = 255; + wpabuf_put_u8(buf, id_len); + wpabuf_put_data(buf, sm->identity, id_len); + } else { + wpabuf_put_u8(buf, 0); + } + tls_connection_set_success_data(data->ssl.conn, buf); } @@ -151,7 +182,7 @@ static void * eap_peap_init(struct eap_sm *sm) data->state = START; data->crypto_binding = OPTIONAL_BINDING; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_PEAP)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); eap_peap_reset(sm, data); return NULL; @@ -539,15 +570,14 @@ static Boolean eap_peap_check(struct eap_sm *sm, void *priv, static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, - EapType eap_type) + int vendor, EapType eap_type) { if (data->phase2_priv && data->phase2_method) { data->phase2_method->reset(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; } - data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, - eap_type); + data->phase2_method = eap_server_get_eap_method(vendor, eap_type); if (!data->phase2_method) return -1; @@ -709,10 +739,12 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, if (status == EAP_TLV_RESULT_SUCCESS) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success " "- requested %s", requested); - if (data->tlv_request == TLV_REQ_SUCCESS) + if (data->tlv_request == TLV_REQ_SUCCESS) { eap_peap_state(data, SUCCESS); - else + eap_peap_valid_session(sm, data); + } else { eap_peap_state(data, FAILURE); + } } else if (status == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " @@ -737,7 +769,7 @@ static void eap_peap_process_phase2_soh(struct eap_sm *sm, const u8 *soh_tlv = NULL; size_t soh_tlv_len = 0; int tlv_type, mandatory, tlv_len, vtlv_len; - u8 next_type; + u32 next_type; u32 vendor_id; pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left); @@ -852,8 +884,9 @@ auth_method: eap_peap_state(data, PHASE2_METHOD); next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); - eap_peap_phase2_init(sm, data, next_type); + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type %d", + sm->user->methods[0].vendor, next_type); + eap_peap_phase2_init(sm, data, sm->user->methods[0].vendor, next_type); } #endif /* EAP_SERVER_TNC */ @@ -862,7 +895,8 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *in_data) { - u8 next_type = EAP_TYPE_NONE; + int next_vendor = EAP_VENDOR_IETF; + u32 next_type = EAP_TYPE_NONE; const struct eap_hdr *hdr; const u8 *pos; size_t left; @@ -894,17 +928,23 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, "allowed types", pos + 1, left - 1); eap_sm_process_nak(sm, pos + 1, left - 1); if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index].method != - EAP_TYPE_NONE) { + (sm->user->methods[sm->user_eap_method_index].vendor != + EAP_VENDOR_IETF || + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE)) { + next_vendor = sm->user->methods[ + sm->user_eap_method_index].vendor; next_type = sm->user->methods[ sm->user_eap_method_index++].method; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", - next_type); + wpa_printf(MSG_DEBUG, + "EAP-PEAP: try EAP vendor %d type 0x%x", + next_vendor, next_type); } else { eap_peap_req_failure(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; } - eap_peap_phase2_init(sm, data, next_type); + eap_peap_phase2_init(sm, data, next_vendor, next_type); return; } @@ -929,8 +969,9 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed"); eap_peap_req_failure(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; - eap_peap_phase2_init(sm, data, next_type); + eap_peap_phase2_init(sm, data, next_vendor, next_type); return; } @@ -942,7 +983,8 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey " "failed"); eap_peap_req_failure(sm, data); - eap_peap_phase2_init(sm, data, EAP_TYPE_NONE); + eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF, + EAP_TYPE_NONE); return; } } @@ -957,6 +999,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, "database", sm->identity, sm->identity_len); eap_peap_req_failure(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; break; } @@ -967,18 +1010,22 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, eap_peap_state(data, PHASE2_SOH); wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize " "TNC (NAP SOH)"); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; break; } #endif /* EAP_SERVER_TNC */ eap_peap_state(data, PHASE2_METHOD); + next_vendor = sm->user->methods[0].vendor; next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type 0x%x", + next_vendor, next_type); break; case PHASE2_METHOD: eap_peap_req_success(sm, data); + next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; break; case FAILURE: @@ -989,7 +1036,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, break; } - eap_peap_phase2_init(sm, data, next_type); + eap_peap_phase2_init(sm, data, next_vendor, next_type); } @@ -1080,6 +1127,7 @@ static void eap_peap_process_phase2(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); if (data->state == SUCCESS_REQ) { eap_peap_state(data, SUCCESS); + eap_peap_valid_session(sm, data); } break; case EAP_CODE_FAILURE: @@ -1133,7 +1181,8 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv, break; case PHASE2_START: eap_peap_state(data, PHASE2_ID); - eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY); + eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF, + EAP_TYPE_IDENTITY); break; case PHASE1_ID2: case PHASE2_ID: @@ -1144,6 +1193,7 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv, break; case SUCCESS_REQ: eap_peap_state(data, SUCCESS); + eap_peap_valid_session(sm, data); break; case FAILURE_REQ: eap_peap_state(data, FAILURE); @@ -1160,10 +1210,65 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_peap_data *data = priv; + const struct wpabuf *buf; + const u8 *pos; + u8 id_len; + if (eap_server_tls_process(sm, &data->ssl, respData, data, EAP_TYPE_PEAP, eap_peap_process_version, - eap_peap_process_msg) < 0) + eap_peap_process_msg) < 0) { eap_peap_state(data, FAILURE); + return; + } + + if (data->state == SUCCESS || + !tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = tls_connection_get_success_data(data->ssl.conn); + if (!buf || wpabuf_len(buf) < 2) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: No success data in resumed session - reject attempt"); + eap_peap_state(data, FAILURE); + return; + } + + pos = wpabuf_head(buf); + if (*pos != EAP_TYPE_PEAP) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: Resumed session for another EAP type (%u) - reject attempt", + *pos); + eap_peap_state(data, FAILURE); + return; + } + + pos++; + id_len = *pos++; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Identity from cached session", + pos, id_len); + os_free(sm->identity); + sm->identity = os_malloc(id_len ? id_len : 1); + if (!sm->identity) { + sm->identity_len = 0; + eap_peap_state(data, FAILURE); + return; + } + + os_memcpy(sm->identity, pos, id_len); + sm->identity_len = id_len; + + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Phase2 Identity not found in the user database", + sm->identity, sm->identity_len); + eap_peap_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, + "EAP-PEAP: Resuming previous session - skip Phase2"); + eap_peap_state(data, SUCCESS_REQ); + tls_connection_set_success_data_resumed(data->ssl.conn); } diff --git a/contrib/wpa/src/eap_server/eap_server_pwd.c b/contrib/wpa/src/eap_server/eap_server_pwd.c index 943af0d1507..cb83ff7305b 100644 --- a/contrib/wpa/src/eap_server/eap_server_pwd.c +++ b/contrib/wpa/src/eap_server/eap_server_pwd.c @@ -10,6 +10,7 @@ #include "common.h" #include "crypto/sha256.h" +#include "crypto/ms_funcs.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -24,6 +25,7 @@ struct eap_pwd_data { size_t id_server_len; u8 *password; size_t password_len; + int password_hash; u32 token; u16 group_num; EAP_PWD_group *grp; @@ -112,6 +114,7 @@ static void * eap_pwd_init(struct eap_sm *sm) } data->password_len = sm->user->password_len; os_memcpy(data->password, sm->user->password, data->password_len); + data->password_hash = sm->user->password_hash; data->bnctx = BN_CTX_new(); if (data->bnctx == NULL) { @@ -181,7 +184,8 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token)); - wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); + wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS : + EAP_PWD_PREP_NONE); wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); } @@ -579,6 +583,10 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, const u8 *payload, size_t payload_len) { struct eap_pwd_id *id; + const u8 *password; + size_t password_len; + u8 pwhashhash[16]; + int res; if (payload_len < sizeof(struct eap_pwd_id)) { wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response"); @@ -610,11 +618,25 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, "group"); return; } - if (compute_password_element(data->grp, data->group_num, - data->password, data->password_len, - data->id_server, data->id_server_len, - data->id_peer, data->id_peer_len, - (u8 *) &data->token)) { + + if (data->password_hash) { + res = hash_nt_password_hash(data->password, pwhashhash); + if (res) + return; + password = pwhashhash; + password_len = sizeof(pwhashhash); + } else { + password = data->password; + password_len = data->password_len; + } + + res = compute_password_element(data->grp, data->group_num, + password, password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + (u8 *) &data->token); + os_memset(pwhashhash, 0, sizeof(pwhashhash)); + if (res) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute " "PWE"); return; @@ -634,9 +656,21 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; EC_POINT *K = NULL, *point = NULL; int res = 0; + size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response"); + prime_len = BN_num_bytes(data->grp->prime); + order_len = BN_num_bytes(data->grp->order); + + if (payload_len != 2 * prime_len + order_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) (2 * prime_len + order_len)); + goto fin; + } + if (((data->peer_scalar = BN_new()) == NULL) || ((data->k = BN_new()) == NULL) || ((cofactor = BN_new()) == NULL) || @@ -752,6 +786,13 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; + if (payload_len != SHA256_MAC_LEN) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Confirm payload length %u (expected %u)", + (unsigned int) payload_len, SHA256_MAC_LEN); + goto fin; + } + /* build up the ciphersuite: group | random_function | prf */ grp = htons(data->group_num); ptr = (u8 *) &cs; @@ -901,17 +942,28 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * the first fragment has a total length */ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + if (len < 2) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Frame too short to contain Total-Length field"); + return; + } tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total " "length = %d", tot_len); if (tot_len > 15000) return; + if (data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: Unexpected new fragment start when previous fragment is still in use"); + return; + } data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to " "buffer fragments!"); return; } + data->in_frag_pos = 0; pos += sizeof(u16); len -= sizeof(u16); } diff --git a/contrib/wpa/src/eap_server/eap_server_tls.c b/contrib/wpa/src/eap_server/eap_server_tls.c index 58cfe8ac64a..bd18a4ba654 100644 --- a/contrib/wpa/src/eap_server/eap_server_tls.c +++ b/contrib/wpa/src/eap_server/eap_server_tls.c @@ -48,6 +48,23 @@ static void eap_tls_state(struct eap_tls_data *data, int state) eap_tls_state_txt(data->state), eap_tls_state_txt(state)); data->state = state; + if (state == FAILURE) + tls_connection_remove_session(data->ssl.conn); +} + + +static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data) +{ + struct wpabuf *buf; + + if (!sm->tls_session_lifetime) + return; + + buf = wpabuf_alloc(1); + if (!buf) + return; + wpabuf_put_u8(buf, data->eap_type); + tls_connection_set_success_data(data->ssl.conn, buf); } @@ -60,7 +77,7 @@ static void * eap_tls_init(struct eap_sm *sm) return NULL; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 1, EAP_TYPE_TLS)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; @@ -82,7 +99,7 @@ static void * eap_unauth_tls_init(struct eap_sm *sm) return NULL; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_UNAUTH_TLS_TYPE)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; @@ -104,7 +121,8 @@ static void * eap_wfa_unauth_tls_init(struct eap_sm *sm) return NULL; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, + EAP_WFA_UNAUTH_TLS_TYPE)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_reset(sm, data); return NULL; @@ -183,6 +201,7 @@ check_established: * fragments waiting to be sent out. */ wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); eap_tls_state(data, SUCCESS); + eap_tls_valid_session(sm, data); } return res; @@ -234,10 +253,41 @@ static void eap_tls_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_tls_data *data = priv; + const struct wpabuf *buf; + const u8 *pos; + if (eap_server_tls_process(sm, &data->ssl, respData, data, data->eap_type, NULL, eap_tls_process_msg) < - 0) + 0) { eap_tls_state(data, FAILURE); + return; + } + + if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = tls_connection_get_success_data(data->ssl.conn); + if (!buf || wpabuf_len(buf) < 1) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: No success data in resumed session - reject attempt"); + eap_tls_state(data, FAILURE); + return; + } + + pos = wpabuf_head(buf); + if (*pos != data->eap_type) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt", + *pos); + eap_tls_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, + "EAP-TLS: Resuming previous session"); + eap_tls_state(data, SUCCESS); + tls_connection_set_success_data_resumed(data->ssl.conn); } diff --git a/contrib/wpa/src/eap_server/eap_server_tls_common.c b/contrib/wpa/src/eap_server/eap_server_tls_common.c index 56916c45ac6..05677b70e88 100644 --- a/contrib/wpa/src/eap_server/eap_server_tls_common.c +++ b/contrib/wpa/src/eap_server/eap_server_tls_common.c @@ -44,8 +44,11 @@ static void eap_server_tls_log_cb(void *ctx, const char *msg) int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - int verify_peer) + int verify_peer, int eap_type) { + u8 session_ctx[8]; + unsigned int flags = 0; + if (sm->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method"); return -1; @@ -68,7 +71,13 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_TLS_INTERNAL */ - if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { + if (eap_type != EAP_TYPE_FAST) + flags |= TLS_CONN_DISABLE_SESSION_TICKET; + os_memcpy(session_ctx, "hostapd", 7); + session_ctx[7] = (u8) eap_type; + if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer, + flags, session_ctx, + sizeof(session_ctx))) { wpa_printf(MSG_INFO, "SSL: Failed to configure verification " "of TLS peer certificate"); tls_connection_deinit(sm->ssl_ctx, data->conn); @@ -100,43 +109,19 @@ void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len) { - struct tls_keys keys; - u8 *rnd = NULL, *out; + u8 *out; out = os_malloc(len); if (out == NULL) return NULL; - if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == - 0) - return out; + if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, 0, + out, len)) { + os_free(out); + return NULL; + } - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) - goto fail; - - if (keys.client_random == NULL || keys.server_random == NULL || - keys.master_key == NULL) - goto fail; - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - if (rnd == NULL) - goto fail; - os_memcpy(rnd, keys.client_random, keys.client_random_len); - os_memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, len)) - goto fail; - - os_free(rnd); return out; - -fail: - os_free(out); - os_free(rnd); - return NULL; } @@ -157,10 +142,10 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len) { - struct tls_keys keys; + struct tls_random keys; u8 *out; - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) return NULL; if (keys.client_random == NULL || keys.server_random == NULL) diff --git a/contrib/wpa/src/eap_server/eap_server_ttls.c b/contrib/wpa/src/eap_server/eap_server_ttls.c index 12a31b07a63..53ffa1ec678 100644 --- a/contrib/wpa/src/eap_server/eap_server_ttls.c +++ b/contrib/wpa/src/eap_server/eap_server_ttls.c @@ -71,6 +71,36 @@ static void eap_ttls_state(struct eap_ttls_data *data, int state) eap_ttls_state_txt(data->state), eap_ttls_state_txt(state)); data->state = state; + if (state == FAILURE) + tls_connection_remove_session(data->ssl.conn); +} + + +static void eap_ttls_valid_session(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + struct wpabuf *buf; + + if (!sm->tls_session_lifetime) + return; + + buf = wpabuf_alloc(1 + 1 + sm->identity_len); + if (!buf) + return; + wpabuf_put_u8(buf, EAP_TYPE_TTLS); + if (sm->identity) { + u8 id_len; + + if (sm->identity_len <= 255) + id_len = sm->identity_len; + else + id_len = 255; + wpabuf_put_u8(buf, id_len); + wpabuf_put_data(buf, sm->identity, id_len); + } else { + wpabuf_put_u8(buf, 0); + } + tls_connection_set_success_data(data->ssl.conn, buf); } @@ -317,7 +347,7 @@ static void * eap_ttls_init(struct eap_sm *sm) data->ttls_version = EAP_TTLS_VERSION; data->state = START; - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TTLS)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_reset(sm, data); return NULL; @@ -518,6 +548,7 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } @@ -576,6 +607,7 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); eap_ttls_state(data, FAILURE); @@ -618,6 +650,12 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, return; } +#ifdef CONFIG_TESTING_OPTIONS + eap_server_mschap_rx_callback(sm, "TTLS-MSCHAP", + sm->identity, sm->identity_len, + challenge, response + 2 + 24); +#endif /* CONFIG_TESTING_OPTIONS */ + if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 || response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) { @@ -637,6 +675,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", @@ -740,6 +779,18 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, } rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; +#ifdef CONFIG_TESTING_OPTIONS + { + u8 challenge2[8]; + + if (challenge_hash(peer_challenge, auth_challenge, + username, username_len, challenge2) == 0) { + eap_server_mschap_rx_callback(sm, "TTLS-MSCHAPV2", + username, username_len, + challenge2, rx_resp); + } + } +#endif /* CONFIG_TESTING_OPTIONS */ if (os_memcmp_const(nt_response, rx_resp, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " "NT-Response"); @@ -888,6 +939,7 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, break; case PHASE2_METHOD: eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); break; case FAILURE: break; @@ -1111,6 +1163,7 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged response"); eap_ttls_state(data, SUCCESS); + eap_ttls_valid_session(sm, data); } else if (!data->mschapv2_resp_ok) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged error"); @@ -1137,10 +1190,64 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, struct wpabuf *respData) { struct eap_ttls_data *data = priv; + const struct wpabuf *buf; + const u8 *pos; + u8 id_len; + if (eap_server_tls_process(sm, &data->ssl, respData, data, EAP_TYPE_TTLS, eap_ttls_process_version, - eap_ttls_process_msg) < 0) + eap_ttls_process_msg) < 0) { eap_ttls_state(data, FAILURE); + return; + } + + if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) + return; + + buf = tls_connection_get_success_data(data->ssl.conn); + if (!buf || wpabuf_len(buf) < 1) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: No success data in resumed session - reject attempt"); + eap_ttls_state(data, FAILURE); + return; + } + + pos = wpabuf_head(buf); + if (*pos != EAP_TYPE_TTLS) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Resumed session for another EAP type (%u) - reject attempt", + *pos); + eap_ttls_state(data, FAILURE); + return; + } + + pos++; + id_len = *pos++; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Identity from cached session", + pos, id_len); + os_free(sm->identity); + sm->identity = os_malloc(id_len ? id_len : 1); + if (!sm->identity) { + sm->identity_len = 0; + eap_ttls_state(data, FAILURE); + return; + } + + os_memcpy(sm->identity, pos, id_len); + sm->identity_len = id_len; + + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not found in the user database", + sm->identity, sm->identity_len); + eap_ttls_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Resuming previous session - skip Phase2"); + eap_ttls_state(data, SUCCESS); + tls_connection_set_success_data_resumed(data->ssl.conn); } diff --git a/contrib/wpa/src/eap_server/eap_tls_common.h b/contrib/wpa/src/eap_server/eap_tls_common.h index ddf90b859ee..dc943eb207d 100644 --- a/contrib/wpa/src/eap_server/eap_tls_common.h +++ b/contrib/wpa/src/eap_server/eap_tls_common.h @@ -70,7 +70,7 @@ struct eap_ssl_data { struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, u8 code, u8 identifier); int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - int verify_peer); + int verify_peer, int eap_type); void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len); diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c index 0df6eb56416..ff33d286223 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,7 +22,7 @@ #define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" #define STATE_MACHINE_ADDR sm->addr -static struct eapol_callbacks eapol_cb; +static const struct eapol_callbacks eapol_cb; /* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ @@ -198,6 +198,18 @@ SM_STATE(AUTH_PAE, INITIALIZE) { SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); sm->portMode = Auto; + + /* + * Clearing keyRun here is not specified in IEEE Std 802.1X-2004, but + * it looks like this would be logical thing to do here since the + * EAPOL-Key exchange is not possible in this state. It is possible to + * get here on disconnection event without advancing to the + * AUTHENTICATING state to clear keyRun before the IEEE 802.11 RSN + * authenticator state machine runs and that may advance from + * AUTHENTICATION2 to INITPMK if keyRun = TRUE has been left from the + * last association. This can be avoided by clearing keyRun here. + */ + sm->keyRun = FALSE; } @@ -835,6 +847,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.server_id = eapol->conf.server_id; eap_conf.server_id_len = eapol->conf.server_id_len; eap_conf.erp = eapol->conf.erp; + eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -1056,7 +1069,7 @@ static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp) } -static struct eapol_callbacks eapol_cb = +static const struct eapol_callbacks eapol_cb = { eapol_sm_get_eap_user, eapol_sm_get_eap_req_id_text, @@ -1080,6 +1093,87 @@ int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) } +void eapol_auth_reauthenticate(struct eapol_state_machine *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: External reauthentication trigger for " + MACSTR, MAC2STR(sm->addr)); + sm->reAuthenticate = TRUE; + eapol_auth_step(sm); +} + + +int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param, + const char *value) +{ + wpa_printf(MSG_DEBUG, "EAPOL: External configuration operation for " + MACSTR " - param=%s value=%s", + MAC2STR(sm->addr), param, value); + + if (os_strcasecmp(param, "AdminControlledDirections") == 0) { + if (os_strcmp(value, "Both") == 0) + sm->adminControlledDirections = Both; + else if (os_strcmp(value, "In") == 0) + sm->adminControlledDirections = In; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "AdminControlledPortControl") == 0) { + if (os_strcmp(value, "ForceAuthorized") == 0) + sm->portControl = ForceAuthorized; + else if (os_strcmp(value, "ForceUnauthorized") == 0) + sm->portControl = ForceUnauthorized; + else if (os_strcmp(value, "Auto") == 0) + sm->portControl = Auto; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "quietPeriod") == 0) { + sm->quietPeriod = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "serverTimeout") == 0) { + sm->serverTimeout = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "reAuthPeriod") == 0) { + sm->reAuthPeriod = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "reAuthEnabled") == 0) { + if (os_strcmp(value, "TRUE") == 0) + sm->reAuthEnabled = TRUE; + else if (os_strcmp(value, "FALSE") == 0) + sm->reAuthEnabled = FALSE; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "KeyTransmissionEnabled") == 0) { + if (os_strcmp(value, "TRUE") == 0) + sm->keyTxEnabled = TRUE; + else if (os_strcmp(value, "FALSE") == 0) + sm->keyTxEnabled = FALSE; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + return -1; +} + + static int eapol_auth_conf_clone(struct eapol_auth_config *dst, struct eapol_auth_config *src) { @@ -1148,6 +1242,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, } dst->erp_send_reauth_start = src->erp_send_reauth_start; dst->erp = src->erp; + dst->tls_session_lifetime = src->tls_session_lifetime; return 0; diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h index ebed19adefc..e1974e4354d 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,7 @@ struct eapol_auth_config { int erp_send_reauth_start; char *erp_domain; /* a copy of this will be allocated */ int erp; /* Whether ERP is enabled on authentication server */ + unsigned int tls_session_lifetime; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -94,5 +95,8 @@ void eapol_auth_step(struct eapol_state_machine *sm); int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, size_t buflen); int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); +void eapol_auth_reauthenticate(struct eapol_state_machine *sm); +int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param, + const char *value); #endif /* EAPOL_AUTH_SM_H */ diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c index 9cc234a82b0..09cf4f6b922 100644 --- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c +++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c @@ -244,7 +244,8 @@ SM_STATE(SUPP_PAE, DISCONNECTED) SM_STATE(SUPP_PAE, CONNECTING) { - int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; + int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING || + sm->SUPP_PAE_state == SUPP_PAE_HELD; SM_ENTRY(SUPP_PAE, CONNECTING); if (sm->eapTriggerStart) @@ -653,7 +654,9 @@ static void eapol_sm_processKey(struct eapol_sm *sm) struct ieee802_1x_eapol_key *key; struct eap_key_data keydata; u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; +#ifndef CONFIG_NO_RC4 u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; +#endif /* CONFIG_NO_RC4 */ int key_len, res, sign_key_len, encr_key_len; u16 rx_key_length; size_t plen; @@ -747,6 +750,13 @@ static void eapol_sm_processKey(struct eapol_sm *sm) return; } if (key_len == rx_key_length) { +#ifdef CONFIG_NO_RC4 + if (encr_key_len) { + /* otherwise unused */ + } + wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build"); + return; +#else /* CONFIG_NO_RC4 */ os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN); os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, encr_key_len); @@ -755,6 +765,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) datakey, key_len); wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", datakey, key_len); +#endif /* CONFIG_NO_RC4 */ } else if (key_len == 0) { /* * IEEE 802.1X-2004 specifies that least significant Key Length @@ -1997,7 +2008,7 @@ static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len) } -static struct eapol_callbacks eapol_cb = +static const struct eapol_callbacks eapol_cb = { eapol_sm_get_config, eapol_sm_get_bool, diff --git a/contrib/wpa/src/fst/Makefile b/contrib/wpa/src/fst/Makefile new file mode 100644 index 00000000000..9c41962fd7e --- /dev/null +++ b/contrib/wpa/src/fst/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/wpa/src/fst/fst.c b/contrib/wpa/src/fst/fst.c new file mode 100644 index 00000000000..2880870213e --- /dev/null +++ b/contrib/wpa/src/fst/fst.c @@ -0,0 +1,225 @@ +/* + * FST module implementation + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "fst/fst.h" +#include "fst/fst_internal.h" +#include "fst/fst_defs.h" +#include "fst/fst_ctrl_iface.h" + +struct dl_list fst_global_ctrls_list; + + +static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface, + Boolean connected, + const u8 *peer_addr) +{ + union fst_event_extra extra; + + extra.peer_state.connected = connected; + os_strlcpy(extra.peer_state.ifname, fst_iface_get_name(iface), + sizeof(extra.peer_state.ifname)); + os_memcpy(extra.peer_state.addr, peer_addr, ETH_ALEN); + + foreach_fst_ctrl_call(on_event, EVENT_PEER_STATE_CHANGED, + iface, NULL, &extra); +} + + +struct fst_iface * fst_attach(const char *ifname, const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg) +{ + struct fst_group *g; + struct fst_group *group = NULL; + struct fst_iface *iface = NULL; + Boolean new_group = FALSE; + + WPA_ASSERT(ifname != NULL); + WPA_ASSERT(iface_obj != NULL); + WPA_ASSERT(cfg != NULL); + + foreach_fst_group(g) { + if (os_strcmp(cfg->group_id, fst_group_get_id(g)) == 0) { + group = g; + break; + } + } + + if (!group) { + group = fst_group_create(cfg->group_id); + if (!group) { + fst_printf(MSG_ERROR, "%s: FST group cannot be created", + cfg->group_id); + return NULL; + } + new_group = TRUE; + } + + iface = fst_iface_create(group, ifname, own_addr, iface_obj, cfg); + if (!iface) { + fst_printf_group(group, MSG_ERROR, "cannot create iface for %s", + ifname); + if (new_group) + fst_group_delete(group); + return NULL; + } + + fst_group_attach_iface(group, iface); + fst_group_update_ie(group); + + foreach_fst_ctrl_call(on_iface_added, iface); + + fst_printf_iface(iface, MSG_DEBUG, + "iface attached to group %s (prio=%d, llt=%d)", + cfg->group_id, cfg->priority, cfg->llt); + + return iface; +} + + +void fst_detach(struct fst_iface *iface) +{ + struct fst_group *group = fst_iface_get_group(iface); + + fst_printf_iface(iface, MSG_DEBUG, "iface detached from group %s", + fst_group_get_id(group)); + fst_session_global_on_iface_detached(iface); + foreach_fst_ctrl_call(on_iface_removed, iface); + fst_group_detach_iface(group, iface); + fst_iface_delete(iface); + fst_group_update_ie(group); + fst_group_delete_if_empty(group); +} + + +int fst_global_init(void) +{ + dl_list_init(&fst_global_groups_list); + dl_list_init(&fst_global_ctrls_list); + fst_session_global_init(); + return 0; +} + + +void fst_global_deinit(void) +{ + struct fst_group *group; + struct fst_ctrl_handle *h; + + fst_session_global_deinit(); + while ((group = fst_first_group()) != NULL) + fst_group_delete(group); + while ((h = dl_list_first(&fst_global_ctrls_list, + struct fst_ctrl_handle, + global_ctrls_lentry))) + fst_global_del_ctrl(h); +} + + +struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl) +{ + struct fst_ctrl_handle *h; + + if (!ctrl) + return NULL; + + h = os_zalloc(sizeof(*h)); + if (!h) + return NULL; + + if (ctrl->init && ctrl->init()) { + os_free(h); + return NULL; + } + + h->ctrl = *ctrl; + dl_list_add_tail(&fst_global_ctrls_list, &h->global_ctrls_lentry); + + return h; +} + + +void fst_global_del_ctrl(struct fst_ctrl_handle *h) +{ + dl_list_del(&h->global_ctrls_lentry); + if (h->ctrl.deinit) + h->ctrl.deinit(); + os_free(h); +} + + +void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, + size_t len) +{ + if (fst_iface_is_connected(iface, mgmt->sa)) + fst_session_on_action_rx(iface, mgmt, len); + else + wpa_printf(MSG_DEBUG, + "FST: Ignore FST Action frame - no FST connection with " + MACSTR, MAC2STR(mgmt->sa)); +} + + +void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr) +{ + if (is_zero_ether_addr(addr)) + return; + +#ifndef HOSTAPD + fst_group_update_ie(fst_iface_get_group(iface)); +#endif /* HOSTAPD */ + + fst_printf_iface(iface, MSG_DEBUG, MACSTR " became connected", + MAC2STR(addr)); + + fst_ctrl_iface_notify_peer_state_change(iface, TRUE, addr); +} + + +void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr) +{ + if (is_zero_ether_addr(addr)) + return; + +#ifndef HOSTAPD + fst_group_update_ie(fst_iface_get_group(iface)); +#endif /* HOSTAPD */ + + fst_printf_iface(iface, MSG_DEBUG, MACSTR " became disconnected", + MAC2STR(addr)); + + fst_ctrl_iface_notify_peer_state_change(iface, FALSE, addr); +} + + +Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1, + struct fst_iface *iface2) +{ + return fst_iface_get_group(iface1) == fst_iface_get_group(iface2); +} + + +enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode) +{ + switch (mode) { + case HOSTAPD_MODE_IEEE80211B: + case HOSTAPD_MODE_IEEE80211G: + return MB_BAND_ID_WIFI_2_4GHZ; + case HOSTAPD_MODE_IEEE80211A: + return MB_BAND_ID_WIFI_5GHZ; + case HOSTAPD_MODE_IEEE80211AD: + return MB_BAND_ID_WIFI_60GHZ; + default: + WPA_ASSERT(0); + return MB_BAND_ID_WIFI_2_4GHZ; + } +} diff --git a/contrib/wpa/src/fst/fst.h b/contrib/wpa/src/fst/fst.h new file mode 100644 index 00000000000..0c0e435b974 --- /dev/null +++ b/contrib/wpa/src/fst/fst.h @@ -0,0 +1,296 @@ +/* + * FST module - interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_H +#define FST_H + +#ifdef CONFIG_FST + +#include "common/defs.h" +#include "fst/fst_ctrl_iface.h" + +/* FST module hostap integration API */ + +#define US_IN_MS 1000 +#define LLT_UNIT_US 32 /* See 10.32.2.2 Transitioning between states */ + +#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US) +#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS) + +#define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1) +#define FST_MAX_PRIO_VALUE ((u8) -1) +#define FST_MAX_GROUP_ID_LEN IFNAMSIZ + +#define FST_DEFAULT_LLT_CFG_VALUE 50 + +struct hostapd_hw_modes; +struct ieee80211_mgmt; +struct fst_iface; +struct fst_group; +struct fst_session; +struct fst_get_peer_ctx; +struct fst_ctrl_handle; + +struct fst_wpa_obj { + void *ctx; + + /** + * get_bssid - Get BSSID of the interface + * @ctx: User context %ctx + * Returns: BSSID for success, %NULL for failure. + * + * NOTE: For AP it returns the own BSSID, while for STA - the BSSID of + * the associated AP. + */ + const u8 * (*get_bssid)(void *ctx); + + /** + * get_channel_info - Get current channel info + * @ctx: User context %ctx + * @hw_mode: OUT, current HW mode + * @channel: OUT, current channel + */ + void (*get_channel_info)(void *ctx, enum hostapd_hw_mode *hw_mode, + u8 *channel); + + /** + * get_hw_modes - Get hardware modes + * @ctx: User context %ctx + * @modes: OUT, pointer on array of hw modes + * + * Returns: Number of hw modes available. + */ + int (*get_hw_modes)(void *ctx, struct hostapd_hw_modes **modes); + + /** + * set_ies - Set interface's MB IE + * @ctx: User context %ctx + * @fst_ies: MB IE buffer (owned by FST module) + */ + void (*set_ies)(void *ctx, const struct wpabuf *fst_ies); + + /** + * send_action - Send FST Action frame via the interface + * @ctx: User context %ctx + * @addr: Address of the destination STA + * @data: Action frame buffer + * Returns: 0 for success, negative error code for failure. + */ + int (*send_action)(void *ctx, const u8 *addr, struct wpabuf *data); + + /** + * get_mb_ie - Get last MB IE received from STA + * @ctx: User context %ctx + * @addr: Address of the STA + * Returns: MB IE buffer, %NULL if no MB IE received from the STA + */ + const struct wpabuf * (*get_mb_ie)(void *ctx, const u8 *addr); + + /** + * update_mb_ie - Update last MB IE received from STA + * @ctx: User context %ctx + * @addr: Address of the STA + * @buf: Buffer that contains the MB IEs data + * @size: Size of data in %buf + */ + void (*update_mb_ie)(void *ctx, const u8 *addr, + const u8 *buf, size_t size); + + /** + * get_peer_first - Get MAC address of the 1st connected STA + * @ctx: User context %ctx + * @get_ctx: Context to be used for %get_peer_next call + * @mb_only: %TRUE if only multi-band capable peer should be reported + * Returns: Address of the 1st connected STA, %NULL if no STAs connected + */ + const u8 * (*get_peer_first)(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only); + /** + * get_peer_next - Get MAC address of the next connected STA + * @ctx: User context %ctx + * @get_ctx: Context received from %get_peer_first or previous + * %get_peer_next call + * @mb_only: %TRUE if only multi-band capable peer should be reported + * Returns: Address of the next connected STA, %NULL if no more STAs + * connected + */ + const u8 * (*get_peer_next)(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only); +}; + +/** + * fst_global_init - Global FST module initiator + * Returns: 0 for success, negative error code for failure. + * Note: The purpose of this function is to allocate and initiate global + * FST module data structures (linked lists, static data etc.) + * This function should be called prior to the 1st %fst_attach call. + */ +int fst_global_init(void); + +/** + * fst_global_deinit - Global FST module de-initiator + * Note: The purpose of this function is to deallocate and de-initiate global + * FST module data structures (linked lists, static data etc.) + */ +void fst_global_deinit(void); + +/** + * struct fst_ctrl - Notification interface for FST module + */ +struct fst_ctrl { + /** + * init - Initialize the notification interface + * Returns: 0 for success, negative error code for failure. + */ + int (*init)(void); + + /** + * deinit - Deinitialize the notification interface + */ + void (*deinit)(void); + + /** + * on_group_created - Notify about FST group creation + * Returns: 0 for success, negative error code for failure. + */ + int (*on_group_created)(struct fst_group *g); + + /** + * on_group_deleted - Notify about FST group deletion + */ + void (*on_group_deleted)(struct fst_group *g); + + /** + * on_iface_added - Notify about interface addition + * Returns: 0 for success, negative error code for failure. + */ + int (*on_iface_added)(struct fst_iface *i); + + /** + * on_iface_removed - Notify about interface removal + */ + void (*on_iface_removed)(struct fst_iface *i); + + /** + * on_session_added - Notify about FST session addition + * Returns: 0 for success, negative error code for failure. + */ + int (*on_session_added)(struct fst_session *s); + + /** + * on_session_removed - Notify about FST session removal + */ + void (*on_session_removed)(struct fst_session *s); + + /** + * on_event - Notify about FST event + * @event_type: Event type + * @i: Interface object that relates to the event or NULL + * @g: Group object that relates to the event or NULL + * @extra - Event specific data (see fst_ctrl_iface.h for more info) + */ + void (*on_event)(enum fst_event_type event_type, struct fst_iface *i, + struct fst_session *s, + const union fst_event_extra *extra); +}; + +struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl); +void fst_global_del_ctrl(struct fst_ctrl_handle *h); + +/** + * NOTE: These values have to be read from configuration file + */ +struct fst_iface_cfg { + char group_id[FST_MAX_GROUP_ID_LEN + 1]; + u8 priority; + u32 llt; +}; + +/** + * fst_attach - Attach interface to an FST group according to configuration read + * @ifname: Interface name + * @own_addr: Own interface MAC address + * @iface_obj: Callbacks to be used by FST module to communicate with + * hostapd/wpa_supplicant + * @cfg: FST-related interface configuration read from the configuration file + * Returns: FST interface object for success, %NULL for failure. + */ +struct fst_iface * fst_attach(const char *ifname, + const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg); + +/** + * fst_detach - Detach an interface + * @iface: FST interface object + */ +void fst_detach(struct fst_iface *iface); + +/* FST module inputs */ +/** + * fst_rx_action - FST Action frames handler + * @iface: FST interface object + * @mgmt: Action frame arrived + * @len: Action frame length + */ +void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, + size_t len); + +/** + * fst_notify_peer_connected - FST STA connect handler + * @iface: FST interface object + * @addr: Address of the connected STA + */ +void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr); + +/** + * fst_notify_peer_disconnected - FST STA disconnect handler + * @iface: FST interface object + * @addr: Address of the disconnected STA + */ +void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr); + +/* FST module auxiliary routines */ + +/** + * fst_are_ifaces_aggregated - Determines whether 2 interfaces belong to the + * same FST group + * @iface1: 1st FST interface object + * @iface1: 2nd FST interface object + * + * Returns: %TRUE if the interfaces belong to the same FST group, + * %FALSE otherwise + */ +Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1, + struct fst_iface *iface2); + +#else /* CONFIG_FST */ + +static inline int fst_global_init(void) +{ + return 0; +} + +static inline int fst_global_start(void) +{ + return 0; +} + +static inline void fst_global_stop(void) +{ +} + +static inline void fst_global_deinit(void) +{ +} + +#endif /* CONFIG_FST */ + +#endif /* FST_H */ diff --git a/contrib/wpa/src/fst/fst_ctrl_aux.c b/contrib/wpa/src/fst/fst_ctrl_aux.c new file mode 100644 index 00000000000..dc7b2a7d720 --- /dev/null +++ b/contrib/wpa/src/fst/fst_ctrl_aux.c @@ -0,0 +1,69 @@ +/* + * FST module implementation + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include "utils/common.h" +#include "common/defs.h" +#include "fst_ctrl_defs.h" +#include "fst_ctrl_aux.h" + + +static const char *session_event_names[] = { + [EVENT_FST_ESTABLISHED] FST_PVAL_EVT_TYPE_ESTABLISHED, + [EVENT_FST_SETUP] FST_PVAL_EVT_TYPE_SETUP, + [EVENT_FST_SESSION_STATE_CHANGED] FST_PVAL_EVT_TYPE_SESSION_STATE, +}; + +static const char *reason_names[] = { + [REASON_TEARDOWN] FST_CS_PVAL_REASON_TEARDOWN, + [REASON_SETUP] FST_CS_PVAL_REASON_SETUP, + [REASON_SWITCH] FST_CS_PVAL_REASON_SWITCH, + [REASON_STT] FST_CS_PVAL_REASON_STT, + [REASON_REJECT] FST_CS_PVAL_REASON_REJECT, + [REASON_ERROR_PARAMS] FST_CS_PVAL_REASON_ERROR_PARAMS, + [REASON_RESET] FST_CS_PVAL_REASON_RESET, + [REASON_DETACH_IFACE] FST_CS_PVAL_REASON_DETACH_IFACE, +}; + +static const char *session_state_names[] = { + [FST_SESSION_STATE_INITIAL] FST_CS_PVAL_STATE_INITIAL, + [FST_SESSION_STATE_SETUP_COMPLETION] FST_CS_PVAL_STATE_SETUP_COMPLETION, + [FST_SESSION_STATE_TRANSITION_DONE] FST_CS_PVAL_STATE_TRANSITION_DONE, + [FST_SESSION_STATE_TRANSITION_CONFIRMED] + FST_CS_PVAL_STATE_TRANSITION_CONFIRMED, +}; + + +/* helpers */ +const char * fst_get_str_name(unsigned index, const char *names[], + size_t names_size) +{ + if (index >= names_size || !names[index]) + return FST_NAME_UNKNOWN; + return names[index]; +} + + +const char * fst_session_event_type_name(enum fst_event_type event) +{ + return fst_get_str_name(event, session_event_names, + ARRAY_SIZE(session_event_names)); +} + + +const char * fst_reason_name(enum fst_reason reason) +{ + return fst_get_str_name(reason, reason_names, ARRAY_SIZE(reason_names)); +} + + +const char * fst_session_state_name(enum fst_session_state state) +{ + return fst_get_str_name(state, session_state_names, + ARRAY_SIZE(session_state_names)); +} diff --git a/contrib/wpa/src/fst/fst_ctrl_aux.h b/contrib/wpa/src/fst/fst_ctrl_aux.h new file mode 100644 index 00000000000..e2133f5062b --- /dev/null +++ b/contrib/wpa/src/fst/fst_ctrl_aux.h @@ -0,0 +1,91 @@ +/* + * FST module - miscellaneous definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_AUX_H +#define FST_CTRL_AUX_H + +#include "common/defs.h" + +/* FST module control interface API */ +#define FST_INVALID_SESSION_ID ((u32) -1) +#define FST_MAX_GROUP_ID_SIZE 32 +#define FST_MAX_INTERFACE_SIZE 32 + +enum fst_session_state { + FST_SESSION_STATE_INITIAL, + FST_SESSION_STATE_SETUP_COMPLETION, + FST_SESSION_STATE_TRANSITION_DONE, + FST_SESSION_STATE_TRANSITION_CONFIRMED, + FST_SESSION_STATE_LAST +}; + +enum fst_event_type { + EVENT_FST_IFACE_STATE_CHANGED, /* An interface has been either attached + * to or detached from an FST group */ + EVENT_FST_ESTABLISHED, /* FST Session has been established */ + EVENT_FST_SETUP, /* FST Session request received */ + EVENT_FST_SESSION_STATE_CHANGED,/* FST Session state has been changed */ + EVENT_PEER_STATE_CHANGED /* FST related generic event occurred, + * see struct fst_hostap_event_data for + * more info */ +}; + +enum fst_initiator { + FST_INITIATOR_UNDEFINED, + FST_INITIATOR_LOCAL, + FST_INITIATOR_REMOTE, +}; + +union fst_event_extra { + struct fst_event_extra_iface_state { + Boolean attached; + char ifname[FST_MAX_INTERFACE_SIZE]; + char group_id[FST_MAX_GROUP_ID_SIZE]; + } iface_state; /* for EVENT_FST_IFACE_STATE_CHANGED */ + struct fst_event_extra_peer_state { + Boolean connected; + char ifname[FST_MAX_INTERFACE_SIZE]; + u8 addr[ETH_ALEN]; + } peer_state; /* for EVENT_PEER_STATE_CHANGED */ + struct fst_event_extra_session_state { + enum fst_session_state old_state; + enum fst_session_state new_state; + union fst_session_state_switch_extra { + struct { + enum fst_reason { + REASON_TEARDOWN, + REASON_SETUP, + REASON_SWITCH, + REASON_STT, + REASON_REJECT, + REASON_ERROR_PARAMS, + REASON_RESET, + REASON_DETACH_IFACE, + } reason; + u8 reject_code; /* REASON_REJECT */ + /* REASON_SWITCH, + * REASON_TEARDOWN, + * REASON_REJECT + */ + enum fst_initiator initiator; + } to_initial; + } extra; + } session_state; /* for EVENT_FST_SESSION_STATE_CHANGED */ +}; + +/* helpers - prints enum in string form */ +#define FST_NAME_UNKNOWN "UNKNOWN" + +const char * fst_get_str_name(unsigned index, const char *names[], + size_t names_size); + +const char * fst_session_event_type_name(enum fst_event_type); +const char * fst_reason_name(enum fst_reason reason); +const char * fst_session_state_name(enum fst_session_state state); + +#endif /* FST_CTRL_AUX_H */ diff --git a/contrib/wpa/src/fst/fst_ctrl_defs.h b/contrib/wpa/src/fst/fst_ctrl_defs.h new file mode 100644 index 00000000000..67353890ffc --- /dev/null +++ b/contrib/wpa/src/fst/fst_ctrl_defs.h @@ -0,0 +1,109 @@ +/* + * FST module - shared Control interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_DEFS_H +#define FST_CTRL_DEFS_H + +/* Undefined value */ +#define FST_CTRL_PVAL_NONE "NONE" + +/* FST-ATTACH parameters */ +#define FST_ATTACH_CMD_PNAME_LLT "llt" /* pval = desired LLT */ +#define FST_ATTACH_CMD_PNAME_PRIORITY "priority" /* pval = desired priority */ + +/* FST-MANAGER parameters */ +/* FST Session states */ +#define FST_CS_PVAL_STATE_INITIAL "INITIAL" +#define FST_CS_PVAL_STATE_SETUP_COMPLETION "SETUP_COMPLETION" +#define FST_CS_PVAL_STATE_TRANSITION_DONE "TRANSITION_DONE" +#define FST_CS_PVAL_STATE_TRANSITION_CONFIRMED "TRANSITION_CONFIRMED" + +/* FST Session reset reasons */ +#define FST_CS_PVAL_REASON_TEARDOWN "REASON_TEARDOWN" +#define FST_CS_PVAL_REASON_SETUP "REASON_SETUP" +#define FST_CS_PVAL_REASON_SWITCH "REASON_SWITCH" +#define FST_CS_PVAL_REASON_STT "REASON_STT" +#define FST_CS_PVAL_REASON_REJECT "REASON_REJECT" +#define FST_CS_PVAL_REASON_ERROR_PARAMS "REASON_ERROR_PARAMS" +#define FST_CS_PVAL_REASON_RESET "REASON_RESET" +#define FST_CS_PVAL_REASON_DETACH_IFACE "REASON_DETACH_IFACE" + +/* FST Session responses */ +#define FST_CS_PVAL_RESPONSE_ACCEPT "ACCEPT" +#define FST_CS_PVAL_RESPONSE_REJECT "REJECT" + +/* FST Session action initiator */ +#define FST_CS_PVAL_INITIATOR_LOCAL "LOCAL" +#define FST_CS_PVAL_INITIATOR_REMOTE "REMOTE" + +/* FST-CLI subcommands and parameter names */ +#define FST_CMD_LIST_GROUPS "list_groups" +#define FST_CMD_LIST_IFACES "list_ifaces" +#define FST_CMD_IFACE_PEERS "iface_peers" +#define FST_CMD_GET_PEER_MBIES "get_peer_mbies" +#define FST_CMD_LIST_SESSIONS "list_sessions" +#define FST_CMD_SESSION_ADD "session_add" +#define FST_CMD_SESSION_REMOVE "session_remove" +#define FST_CMD_SESSION_GET "session_get" +#define FST_CSG_PNAME_OLD_PEER_ADDR "old_peer_addr" /* pval = address string */ +#define FST_CSG_PNAME_NEW_PEER_ADDR "new_peer_addr" /* pval = address string */ +#define FST_CSG_PNAME_OLD_IFNAME "old_ifname" /* pval = ifname */ +#define FST_CSG_PNAME_NEW_IFNAME "new_ifname" /* pval = ifname */ +#define FST_CSG_PNAME_LLT "llt" /* pval = numeric llt value */ +#define FST_CSG_PNAME_STATE "state" /* pval = FST_CS_PVAL_STATE_... */ +#define FST_CMD_SESSION_SET "session_set" +#define FST_CSS_PNAME_OLD_PEER_ADDR FST_CSG_PNAME_OLD_PEER_ADDR +#define FST_CSS_PNAME_NEW_PEER_ADDR FST_CSG_PNAME_NEW_PEER_ADDR +#define FST_CSS_PNAME_OLD_IFNAME FST_CSG_PNAME_OLD_IFNAME +#define FST_CSS_PNAME_NEW_IFNAME FST_CSG_PNAME_NEW_IFNAME +#define FST_CSS_PNAME_LLT FST_CSG_PNAME_LLT +#define FST_CMD_SESSION_INITIATE "session_initiate" +#define FST_CMD_SESSION_RESPOND "session_respond" +#define FST_CMD_SESSION_TRANSFER "session_transfer" +#define FST_CMD_SESSION_TEARDOWN "session_teardown" + +#ifdef CONFIG_FST_TEST +#define FST_CTR_PVAL_BAD_NEW_BAND "bad_new_band" + +#define FST_CMD_TEST_REQUEST "test_request" +#define FST_CTR_IS_SUPPORTED "is_supported" +#define FST_CTR_SEND_SETUP_REQUEST "send_setup_request" +#define FST_CTR_SEND_SETUP_RESPONSE "send_setup_response" +#define FST_CTR_SEND_ACK_REQUEST "send_ack_request" +#define FST_CTR_SEND_ACK_RESPONSE "send_ack_response" +#define FST_CTR_SEND_TEAR_DOWN "send_tear_down" +#define FST_CTR_GET_FSTS_ID "get_fsts_id" +#define FST_CTR_GET_LOCAL_MBIES "get_local_mbies" +#endif /* CONFIG_FST_TEST */ + +/* Events */ +#define FST_CTRL_EVENT_IFACE "FST-EVENT-IFACE" +#define FST_CEI_PNAME_IFNAME "ifname" +#define FST_CEI_PNAME_GROUP "group" +#define FST_CEI_PNAME_ATTACHED "attached" +#define FST_CEI_PNAME_DETACHED "detached" +#define FST_CTRL_EVENT_PEER "FST-EVENT-PEER" +#define FST_CEP_PNAME_IFNAME "ifname" +#define FST_CEP_PNAME_ADDR "peer_addr" +#define FST_CEP_PNAME_CONNECTED "connected" +#define FST_CEP_PNAME_DISCONNECTED "disconnected" +#define FST_CTRL_EVENT_SESSION "FST-EVENT-SESSION" +#define FST_CES_PNAME_SESSION_ID "session_id" +#define FST_CES_PNAME_EVT_TYPE "event_type" +#define FST_PVAL_EVT_TYPE_SESSION_STATE "EVENT_FST_SESSION_STATE" +/* old_state/new_state: pval = FST_CS_PVAL_STATE_... */ +#define FST_CES_PNAME_OLD_STATE "old_state" +#define FST_CES_PNAME_NEW_STATE "new_state" +#define FST_CES_PNAME_REASON "reason" /* pval = FST_CS_PVAL_REASON_... */ +#define FST_CES_PNAME_REJECT_CODE "reject_code" /* pval = u8 code */ +/* pval = FST_CS_PVAL_INITIATOR_... */ +#define FST_CES_PNAME_INITIATOR "initiator" +#define FST_PVAL_EVT_TYPE_ESTABLISHED "EVENT_FST_ESTABLISHED" +#define FST_PVAL_EVT_TYPE_SETUP "EVENT_FST_SETUP" + +#endif /* FST_CTRL_DEFS_H */ diff --git a/contrib/wpa/src/fst/fst_ctrl_iface.c b/contrib/wpa/src/fst/fst_ctrl_iface.c new file mode 100644 index 00000000000..d0907188a38 --- /dev/null +++ b/contrib/wpa/src/fst/fst_ctrl_iface.c @@ -0,0 +1,948 @@ +/* + * FST module - Control Interface implementation + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include "utils/common.h" +#include "common/defs.h" +#include "list.h" +#include "fst/fst.h" +#include "fst/fst_internal.h" +#include "fst_ctrl_defs.h" +#include "fst_ctrl_iface.h" + + +static struct fst_group * get_fst_group_by_id(const char *id) +{ + struct fst_group *g; + + foreach_fst_group(g) { + const char *group_id = fst_group_get_id(g); + + if (os_strncmp(group_id, id, os_strlen(group_id)) == 0) + return g; + } + + return NULL; +} + + +/* notifications */ +static Boolean format_session_state_extra(const union fst_event_extra *extra, + char *buffer, size_t size) +{ + int len; + char reject_str[32] = FST_CTRL_PVAL_NONE; + const char *initiator = FST_CTRL_PVAL_NONE; + const struct fst_event_extra_session_state *ss; + + ss = &extra->session_state; + if (ss->new_state != FST_SESSION_STATE_INITIAL) + return TRUE; + + switch (ss->extra.to_initial.reason) { + case REASON_REJECT: + if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS) + os_snprintf(reject_str, sizeof(reject_str), "%u", + ss->extra.to_initial.reject_code); + /* no break */ + case REASON_TEARDOWN: + case REASON_SWITCH: + switch (ss->extra.to_initial.initiator) { + case FST_INITIATOR_LOCAL: + initiator = FST_CS_PVAL_INITIATOR_LOCAL; + break; + case FST_INITIATOR_REMOTE: + initiator = FST_CS_PVAL_INITIATOR_REMOTE; + break; + default: + break; + } + break; + default: + break; + } + + len = os_snprintf(buffer, size, + FST_CES_PNAME_REASON "=%s " + FST_CES_PNAME_REJECT_CODE "=%s " + FST_CES_PNAME_INITIATOR "=%s", + fst_reason_name(ss->extra.to_initial.reason), + reject_str, initiator); + + return !os_snprintf_error(size, len); +} + + +static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id, + enum fst_event_type event_type, + const union fst_event_extra *extra) +{ + struct fst_group *g; + char extra_str[128] = ""; + const struct fst_event_extra_session_state *ss; + const struct fst_event_extra_iface_state *is; + const struct fst_event_extra_peer_state *ps; + + /* + * FST can use any of interface objects as it only sends messages + * on global Control Interface, so we just pick the 1st one. + */ + + if (!f) { + foreach_fst_group(g) { + f = fst_group_first_iface(g); + if (f) + break; + } + if (!f) + return; + } + + WPA_ASSERT(f->iface_obj.ctx); + + switch (event_type) { + case EVENT_FST_IFACE_STATE_CHANGED: + if (!extra) + return; + is = &extra->iface_state; + wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO, + FST_CTRL_EVENT_IFACE " %s " + FST_CEI_PNAME_IFNAME "=%s " + FST_CEI_PNAME_GROUP "=%s", + is->attached ? FST_CEI_PNAME_ATTACHED : + FST_CEI_PNAME_DETACHED, + is->ifname, is->group_id); + break; + case EVENT_PEER_STATE_CHANGED: + if (!extra) + return; + ps = &extra->peer_state; + wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO, + FST_CTRL_EVENT_PEER " %s " + FST_CEP_PNAME_IFNAME "=%s " + FST_CEP_PNAME_ADDR "=" MACSTR, + ps->connected ? FST_CEP_PNAME_CONNECTED : + FST_CEP_PNAME_DISCONNECTED, + ps->ifname, MAC2STR(ps->addr)); + break; + case EVENT_FST_SESSION_STATE_CHANGED: + if (!extra) + return; + if (!format_session_state_extra(extra, extra_str, + sizeof(extra_str))) { + fst_printf(MSG_ERROR, + "CTRL: Cannot format STATE_CHANGE extra"); + extra_str[0] = 0; + } + ss = &extra->session_state; + wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO, + FST_CTRL_EVENT_SESSION " " + FST_CES_PNAME_SESSION_ID "=%u " + FST_CES_PNAME_EVT_TYPE "=%s " + FST_CES_PNAME_OLD_STATE "=%s " + FST_CES_PNAME_NEW_STATE "=%s %s", + session_id, + fst_session_event_type_name(event_type), + fst_session_state_name(ss->old_state), + fst_session_state_name(ss->new_state), + extra_str); + break; + case EVENT_FST_ESTABLISHED: + case EVENT_FST_SETUP: + wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO, + FST_CTRL_EVENT_SESSION " " + FST_CES_PNAME_SESSION_ID "=%u " + FST_CES_PNAME_EVT_TYPE "=%s", + session_id, + fst_session_event_type_name(event_type)); + break; + } +} + + +/* command processors */ + +/* fst session_get */ +static int session_get(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + struct fst_iface *new_iface, *old_iface; + const u8 *old_peer_addr, *new_peer_addr; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + old_peer_addr = fst_session_get_peer_addr(s, TRUE); + new_peer_addr = fst_session_get_peer_addr(s, FALSE); + new_iface = fst_session_get_iface(s, FALSE); + old_iface = fst_session_get_iface(s, TRUE); + + return os_snprintf(buf, buflen, + FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n" + FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n" + FST_CSG_PNAME_NEW_IFNAME "=%s\n" + FST_CSG_PNAME_OLD_IFNAME "=%s\n" + FST_CSG_PNAME_LLT "=%u\n" + FST_CSG_PNAME_STATE "=%s\n", + MAC2STR(old_peer_addr), + MAC2STR(new_peer_addr), + new_iface ? fst_iface_get_name(new_iface) : + FST_CTRL_PVAL_NONE, + old_iface ? fst_iface_get_name(old_iface) : + FST_CTRL_PVAL_NONE, + fst_session_get_llt(s), + fst_session_state_name(fst_session_get_state(s))); +} + + +/* fst session_set */ +static int session_set(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + char *p, *q; + u32 id; + int ret; + + id = strtoul(session_id, &p, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (*p != ' ' || !(q = os_strchr(p + 1, '='))) + return os_snprintf(buf, buflen, "FAIL\n"); + p++; + + if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) { + ret = fst_session_set_str_ifname(s, q + 1, TRUE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) { + ret = fst_session_set_str_ifname(s, q + 1, FALSE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) { + ret = fst_session_set_str_peer_addr(s, q + 1, TRUE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) { + ret = fst_session_set_str_peer_addr(s, q + 1, FALSE); + } else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) { + ret = fst_session_set_str_llt(s, q + 1); + } else { + fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK"); +} + + +/* fst session_add/remove */ +static int session_add(const char *group_id, char *buf, size_t buflen) +{ + struct fst_group *g; + struct fst_session *s; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + s = fst_session_create(g); + if (!s) { + fst_printf(MSG_ERROR, + "CTRL: Cannot create session for group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s)); +} + + +static int session_remove(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + struct fst_group *g; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + g = fst_session_get_group(s); + fst_session_reset(s); + fst_session_delete(s); + fst_group_delete_if_empty(g); + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_initiate */ +static int session_initiate(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_initiate_setup(s)) { + fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_respond */ +static int session_respond(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + char *p; + u32 id; + u8 status_code; + + id = strtoul(session_id, &p, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (*p != ' ') + return os_snprintf(buf, buflen, "FAIL\n"); + p++; + + if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) { + status_code = WLAN_STATUS_SUCCESS; + } else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) { + status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION; + } else { + fst_printf(MSG_WARNING, + "CTRL: session %u: unknown response status: %s", + id, p); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_respond(s, status_code)) { + fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u", + id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + fst_printf(MSG_INFO, "CTRL: session %u responded", id); + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_transfer */ +static int session_transfer(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_initiate_switch(s)) { + fst_printf(MSG_WARNING, + "CTRL: Cannot initiate ST for session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "OK\n"); +} + + +/* fst session_teardown */ +static int session_teardown(const char *session_id, char *buf, size_t buflen) +{ + struct fst_session *s; + u32 id; + + id = strtoul(session_id, NULL, 0); + + s = fst_session_get_by_id(id); + if (!s) { + fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + if (fst_session_tear_down_setup(s)) { + fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u", + id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "OK\n"); +} + + +#ifdef CONFIG_FST_TEST +/* fst test_request */ +static int test_request(const char *request, char *buf, size_t buflen) +{ + const char *p = request; + int ret; + + if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST, + os_strlen(FST_CTR_SEND_SETUP_REQUEST))) { + ret = fst_test_req_send_fst_request( + p + os_strlen(FST_CTR_SEND_SETUP_REQUEST)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE, + os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) { + ret = fst_test_req_send_fst_response( + p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST, + os_strlen(FST_CTR_SEND_ACK_REQUEST))) { + ret = fst_test_req_send_ack_request( + p + os_strlen(FST_CTR_SEND_ACK_REQUEST)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE, + os_strlen(FST_CTR_SEND_ACK_RESPONSE))) { + ret = fst_test_req_send_ack_response( + p + os_strlen(FST_CTR_SEND_ACK_RESPONSE)); + } else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN, + os_strlen(FST_CTR_SEND_TEAR_DOWN))) { + ret = fst_test_req_send_tear_down( + p + os_strlen(FST_CTR_SEND_TEAR_DOWN)); + } else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID, + os_strlen(FST_CTR_GET_FSTS_ID))) { + u32 fsts_id = fst_test_req_get_fsts_id( + p + os_strlen(FST_CTR_GET_FSTS_ID)); + if (fsts_id != FST_FSTS_ID_NOT_FOUND) + return os_snprintf(buf, buflen, "%u\n", fsts_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES, + os_strlen(FST_CTR_GET_LOCAL_MBIES))) { + return fst_test_req_get_local_mbies( + p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen); + } else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED, + os_strlen(FST_CTR_IS_SUPPORTED))) { + ret = 0; + } else { + fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK"); +} +#endif /* CONFIG_FST_TEST */ + + +/* fst list_sessions */ +struct list_sessions_cb_ctx { + char *buf; + size_t buflen; + size_t reply_len; +}; + + +static void list_session_enum_cb(struct fst_group *g, struct fst_session *s, + void *ctx) +{ + struct list_sessions_cb_ctx *c = ctx; + int ret; + + ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s)); + + c->buf += ret; + c->buflen -= ret; + c->reply_len += ret; +} + + +static int list_sessions(const char *group_id, char *buf, size_t buflen) +{ + struct list_sessions_cb_ctx ctx; + struct fst_group *g; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + ctx.buf = buf; + ctx.buflen = buflen; + ctx.reply_len = 0; + + fst_session_enum(g, list_session_enum_cb, &ctx); + + ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n"); + + return ctx.reply_len; +} + + +/* fst iface_peers */ +static int iface_peers(const char *group_id, char *buf, size_t buflen) +{ + const char *ifname; + struct fst_group *g; + struct fst_iface *f; + struct fst_get_peer_ctx *ctx; + const u8 *addr; + unsigned found = 0; + int ret = 0; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + ifname = os_strchr(group_id, ' '); + if (!ifname) + return os_snprintf(buf, buflen, "FAIL\n"); + ifname++; + + foreach_fst_group_iface(g, f) { + const char *in = fst_iface_get_name(f); + + if (os_strncmp(ifname, in, os_strlen(in)) == 0) { + found = 1; + break; + } + } + + if (!found) + return os_snprintf(buf, buflen, "FAIL\n"); + + addr = fst_iface_get_peer_first(f, &ctx, FALSE); + for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) { + int res; + + res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n", + MAC2STR(addr)); + if (os_snprintf_error(buflen - ret, res)) + break; + ret += res; + } + + return ret; +} + + +static int get_peer_mbies(const char *params, char *buf, size_t buflen) +{ + char *endp; + char ifname[FST_MAX_INTERFACE_SIZE]; + u8 peer_addr[ETH_ALEN]; + struct fst_group *g; + struct fst_iface *iface = NULL; + const struct wpabuf *mbies; + + if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) || + !*ifname) + goto problem; + + while (isspace(*endp)) + endp++; + if (fst_read_peer_addr(endp, peer_addr)) + goto problem; + + foreach_fst_group(g) { + iface = fst_group_get_iface_by_name(g, ifname); + if (iface) + break; + } + if (!iface) + goto problem; + + mbies = fst_iface_get_peer_mb_ie(iface, peer_addr); + if (!mbies) + goto problem; + + return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies), + wpabuf_len(mbies)); + +problem: + return os_snprintf(buf, buflen, "FAIL\n"); +} + + +/* fst list_ifaces */ +static int list_ifaces(const char *group_id, char *buf, size_t buflen) +{ + struct fst_group *g; + struct fst_iface *f; + int ret = 0; + + g = get_fst_group_by_id(group_id); + if (!g) { + fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'", + group_id); + return os_snprintf(buf, buflen, "FAIL\n"); + } + + foreach_fst_group_iface(g, f) { + int res; + const u8 *iface_addr = fst_iface_get_addr(f); + + res = os_snprintf(buf + ret, buflen - ret, + "%s|" MACSTR "|%u|%u\n", + fst_iface_get_name(f), + MAC2STR(iface_addr), + fst_iface_get_priority(f), + fst_iface_get_llt(f)); + if (os_snprintf_error(buflen - ret, res)) + break; + ret += res; + } + + return ret; +} + + +/* fst list_groups */ +static int list_groups(const char *cmd, char *buf, size_t buflen) +{ + struct fst_group *g; + int ret = 0; + + foreach_fst_group(g) { + int res; + + res = os_snprintf(buf + ret, buflen - ret, "%s\n", + fst_group_get_id(g)); + if (os_snprintf_error(buflen - ret, res)) + break; + ret += res; + } + + return ret; +} + + +static const char * band_freq(enum mb_band_id band) +{ + static const char *band_names[] = { + [MB_BAND_ID_WIFI_2_4GHZ] "2.4GHZ", + [MB_BAND_ID_WIFI_5GHZ] "5GHZ", + [MB_BAND_ID_WIFI_60GHZ] "60GHZ", + }; + + return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names)); +} + + +static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr, + char *buf, size_t buflen) +{ + const struct wpabuf *wpabuf; + enum hostapd_hw_mode hw_mode; + u8 channel; + int ret = 0; + + fst_iface_get_channel_info(iface, &hw_mode, &channel); + + ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n", + num, band_freq(fst_hw_mode_to_band(hw_mode))); + ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n", + num, fst_iface_get_name(iface)); + wpabuf = fst_iface_get_peer_mb_ie(iface, addr); + if (wpabuf) { + ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=", + num); + ret += wpa_snprintf_hex(buf + ret, buflen - ret, + wpabuf_head(wpabuf), + wpabuf_len(wpabuf)); + ret += os_snprintf(buf + ret, buflen - ret, "\n"); + } + ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n", + num, fst_iface_get_group_id(iface)); + ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n", + num, fst_iface_get_priority(iface)); + ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n", + num, fst_iface_get_llt(iface)); + + return ret; +} + + +static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i, + Boolean attached) +{ + union fst_event_extra extra; + + os_memset(&extra, 0, sizeof(extra)); + extra.iface_state.attached = attached; + os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i), + sizeof(extra.iface_state.ifname)); + os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i), + sizeof(extra.iface_state.group_id)); + + fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID, + EVENT_FST_IFACE_STATE_CHANGED, &extra); +} + + +static int fst_ctrl_iface_on_iface_added(struct fst_iface *i) +{ + fst_ctrl_iface_on_iface_state_changed(i, TRUE); + return 0; +} + + +static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i) +{ + fst_ctrl_iface_on_iface_state_changed(i, FALSE); +} + + +static void fst_ctrl_iface_on_event(enum fst_event_type event_type, + struct fst_iface *i, struct fst_session *s, + const union fst_event_extra *extra) +{ + u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID; + + fst_ctrl_iface_notify(i, session_id, event_type, extra); +} + + +static const struct fst_ctrl ctrl_cli = { + .on_iface_added = fst_ctrl_iface_on_iface_added, + .on_iface_removed = fst_ctrl_iface_on_iface_removed, + .on_event = fst_ctrl_iface_on_event, +}; + +const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli; + + +int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen) +{ + struct fst_group *g; + struct fst_iface *f; + unsigned num = 0; + int ret = 0; + + foreach_fst_group(g) { + foreach_fst_group_iface(g, f) { + if (fst_iface_is_connected(f, addr)) { + ret += print_band(num++, f, addr, + buf + ret, buflen - ret); + } + } + } + + return ret; +} + + +/* fst ctrl processor */ +int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size) +{ + static const struct fst_command { + const char *name; + unsigned has_param; + int (*process)(const char *group_id, char *buf, size_t buflen); + } commands[] = { + { FST_CMD_LIST_GROUPS, 0, list_groups}, + { FST_CMD_LIST_IFACES, 1, list_ifaces}, + { FST_CMD_IFACE_PEERS, 1, iface_peers}, + { FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies}, + { FST_CMD_LIST_SESSIONS, 1, list_sessions}, + { FST_CMD_SESSION_ADD, 1, session_add}, + { FST_CMD_SESSION_REMOVE, 1, session_remove}, + { FST_CMD_SESSION_GET, 1, session_get}, + { FST_CMD_SESSION_SET, 1, session_set}, + { FST_CMD_SESSION_INITIATE, 1, session_initiate}, + { FST_CMD_SESSION_RESPOND, 1, session_respond}, + { FST_CMD_SESSION_TRANSFER, 1, session_transfer}, + { FST_CMD_SESSION_TEARDOWN, 1, session_teardown}, +#ifdef CONFIG_FST_TEST + { FST_CMD_TEST_REQUEST, 1, test_request }, +#endif /* CONFIG_FST_TEST */ + { NULL, 0, NULL } + }; + const struct fst_command *c; + const char *p; + const char *temp; + Boolean non_spaces_found; + + for (c = commands; c->name; c++) { + if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0) + continue; + p = cmd + os_strlen(c->name); + if (c->has_param) { + if (!isspace(p[0])) + return os_snprintf(reply, reply_size, "FAIL\n"); + p++; + temp = p; + non_spaces_found = FALSE; + while (*temp) { + if (!isspace(*temp)) { + non_spaces_found = TRUE; + break; + } + temp++; + } + if (!non_spaces_found) + return os_snprintf(reply, reply_size, "FAIL\n"); + } + return c->process(p, reply, reply_size); + } + + return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n"); +} + + +int fst_read_next_int_param(const char *params, Boolean *valid, char **endp) +{ + int ret = -1; + const char *curp; + + *valid = FALSE; + *endp = (char *) params; + curp = params; + if (*curp) { + ret = (int) strtol(curp, endp, 0); + if (!**endp || isspace(**endp)) + *valid = TRUE; + } + + return ret; +} + + +int fst_read_next_text_param(const char *params, char *buf, size_t buflen, + char **endp) +{ + size_t max_chars_to_copy; + char *cur_dest; + + *endp = (char *) params; + while (isspace(**endp)) + (*endp)++; + if (!**endp || buflen <= 1) + return -EINVAL; + + max_chars_to_copy = buflen - 1; + /* We need 1 byte for the terminating zero */ + cur_dest = buf; + while (**endp && !isspace(**endp) && max_chars_to_copy > 0) { + *cur_dest = **endp; + (*endp)++; + cur_dest++; + max_chars_to_copy--; + } + *cur_dest = 0; + + return 0; +} + + +int fst_read_peer_addr(const char *mac, u8 *peer_addr) +{ + if (hwaddr_aton(mac, peer_addr)) { + fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string", + mac); + return -1; + } + + if (is_zero_ether_addr(peer_addr) || + is_multicast_ether_addr(peer_addr)) { + fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr", + mac); + return -1; + } + + return 0; +} + + +int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size, + struct fst_iface_cfg *cfg) +{ + char *pos; + char *endp; + Boolean is_valid; + int val; + + if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) || + fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id), + &endp)) + return -EINVAL; + + cfg->llt = FST_DEFAULT_LLT_CFG_VALUE; + cfg->priority = 0; + pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT); + if (pos) { + pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT); + if (*pos == '=') { + val = fst_read_next_int_param(pos + 1, &is_valid, + &endp); + if (is_valid) + cfg->llt = val; + } + } + pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY); + if (pos) { + pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY); + if (*pos == '=') { + val = fst_read_next_int_param(pos + 1, &is_valid, + &endp); + if (is_valid) + cfg->priority = (u8) val; + } + } + + return 0; +} + + +int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size) +{ + char *endp; + + return fst_read_next_text_param(cmd, ifname, ifname_size, &endp); +} + + +int fst_iface_detach(const char *ifname) +{ + struct fst_group *g; + + foreach_fst_group(g) { + struct fst_iface *f; + + f = fst_group_get_iface_by_name(g, ifname); + if (f) { + fst_detach(f); + return 0; + } + } + + return -EINVAL; +} diff --git a/contrib/wpa/src/fst/fst_ctrl_iface.h b/contrib/wpa/src/fst/fst_ctrl_iface.h new file mode 100644 index 00000000000..4d0cd9fce4e --- /dev/null +++ b/contrib/wpa/src/fst/fst_ctrl_iface.h @@ -0,0 +1,45 @@ +/* + * FST module - internal Control interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_IFACE_H +#define FST_CTRL_IFACE_H + +#include "fst/fst_ctrl_aux.h" + +#ifdef CONFIG_FST + +/* receiver */ +int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen); + +int fst_ctrl_iface_receive(const char *txtaddr, char *buf, size_t buflen); + +extern const struct fst_ctrl *fst_ctrl_cli; + +#else /* CONFIG_FST */ + +static inline int +fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen) +{ + return 0; +} + +#endif /* CONFIG_FST */ + +int fst_read_next_int_param(const char *params, Boolean *valid, char **endp); +int fst_read_next_text_param(const char *params, char *buf, size_t buflen, + char **endp); +int fst_read_peer_addr(const char *mac, u8 *peer_addr); + +struct fst_iface_cfg; + +int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size, + struct fst_iface_cfg *cfg); +int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size); +int fst_iface_detach(const char *ifname); + +#endif /* CTRL_IFACE_FST_H */ diff --git a/contrib/wpa/src/fst/fst_defs.h b/contrib/wpa/src/fst/fst_defs.h new file mode 100644 index 00000000000..8ddcc61376b --- /dev/null +++ b/contrib/wpa/src/fst/fst_defs.h @@ -0,0 +1,87 @@ +/* + * FST module - FST related definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE_80211_FST_DEFS_H +#define IEEE_80211_FST_DEFS_H + +/* IEEE Std 802.11ad */ + +#define MB_STA_CHANNEL_ALL 0 + +enum session_type { + SESSION_TYPE_BSS = 0, /* Infrastructure BSS */ + SESSION_TYPE_IBSS = 1, + SESSION_TYPE_DLS = 2, + SESSION_TYPE_TDLS = 3, + SESSION_TYPE_PBSS = 4 +}; + +#define SESSION_CONTROL(session_type, switch_intent) \ + (((u8) ((session_type) & 0x7)) | ((switch_intent) ? 0x10 : 0x00)) + +#define GET_SESSION_CONTROL_TYPE(session_control) \ + ((u8) ((session_control) & 0x7)) + +#define GET_SESSION_CONTROL_SWITCH_INTENT(session_control) \ + (((session_control) & 0x10) >> 4) + +/* 8.4.2.147 Session Transition element */ +struct session_transition_ie { + u8 element_id; + u8 length; + u32 fsts_id; + u8 session_control; + u8 new_band_id; + u8 new_band_setup; + u8 new_band_op; + u8 old_band_id; + u8 old_band_setup; + u8 old_band_op; +} STRUCT_PACKED; + +struct fst_setup_req { + u8 action; + u8 dialog_token; + u32 llt; + struct session_transition_ie stie; + /* Multi-band (optional) */ + /* Wakeup Schedule (optional) */ + /* Awake Window (optional) */ + /* Switching Stream (optional) */ +} STRUCT_PACKED; + +struct fst_setup_res { + u8 action; + u8 dialog_token; + u8 status_code; + struct session_transition_ie stie; + /* Multi-band (optional) */ + /* Wakeup Schedule (optional) */ + /* Awake Window (optional) */ + /* Switching Stream (optional) */ + /* Timeout Interval (optional) */ +} STRUCT_PACKED; + +struct fst_ack_req { + u8 action; + u8 dialog_token; + u32 fsts_id; +} STRUCT_PACKED; + +struct fst_ack_res { + u8 action; + u8 dialog_token; + u32 fsts_id; +} STRUCT_PACKED; + +struct fst_tear_down { + u8 action; + u32 fsts_id; +} STRUCT_PACKED; + +#endif /* IEEE_80211_FST_DEFS_H */ diff --git a/contrib/wpa/src/fst/fst_group.c b/contrib/wpa/src/fst/fst_group.c new file mode 100644 index 00000000000..f6c7be9435f --- /dev/null +++ b/contrib/wpa/src/fst/fst_group.c @@ -0,0 +1,462 @@ +/* + * FST module - FST group object implementation + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include "utils/common.h" +#include "common/defs.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "drivers/driver.h" +#include "fst/fst_internal.h" +#include "fst/fst_defs.h" + + +struct dl_list fst_global_groups_list; + +#ifndef HOSTAPD +static Boolean fst_has_fst_peer(struct fst_iface *iface, Boolean *has_peer) +{ + const u8 *bssid; + + bssid = fst_iface_get_bssid(iface); + if (!bssid) { + *has_peer = FALSE; + return FALSE; + } + + *has_peer = TRUE; + return fst_iface_get_peer_mb_ie(iface, bssid) != NULL; +} +#endif /* HOSTAPD */ + + +static void fst_dump_mb_ies(const char *group_id, const char *ifname, + struct wpabuf *mbies) +{ + const u8 *p = wpabuf_head(mbies); + size_t s = wpabuf_len(mbies); + + while (s >= 2) { + const struct multi_band_ie *mbie = + (const struct multi_band_ie *) p; + WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND); + WPA_ASSERT(2 + mbie->len >= sizeof(*mbie)); + + fst_printf(MSG_WARNING, + "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid=" + MACSTR + " beacon_int=%u tsf_offs=[%u %u %u %u %u %u %u %u] mb_cc=0x%02x tmout=%u", + group_id, ifname, + mbie->mb_ctrl, mbie->band_id, mbie->op_class, + mbie->chan, MAC2STR(mbie->bssid), mbie->beacon_int, + mbie->tsf_offs[0], mbie->tsf_offs[1], + mbie->tsf_offs[2], mbie->tsf_offs[3], + mbie->tsf_offs[4], mbie->tsf_offs[5], + mbie->tsf_offs[6], mbie->tsf_offs[7], + mbie->mb_connection_capability, + mbie->fst_session_tmout); + + p += 2 + mbie->len; + s -= 2 + mbie->len; + } +} + + +static void fst_fill_mb_ie(struct wpabuf *buf, const u8 *bssid, + const u8 *own_addr, enum mb_band_id band, u8 channel) +{ + struct multi_band_ie *mbie; + size_t len = sizeof(*mbie); + + if (own_addr) + len += ETH_ALEN; + + mbie = wpabuf_put(buf, len); + + os_memset(mbie, 0, len); + + mbie->eid = WLAN_EID_MULTI_BAND; + mbie->len = len - 2; +#ifdef HOSTAPD + mbie->mb_ctrl = MB_STA_ROLE_AP; + mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP; +#else /* HOSTAPD */ + mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP; + mbie->mb_connection_capability = 0; +#endif /* HOSTAPD */ + if (bssid) + os_memcpy(mbie->bssid, bssid, ETH_ALEN); + mbie->band_id = band; + mbie->op_class = 0; /* means all */ + mbie->chan = channel; + mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU; + + if (own_addr) { + mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT; + os_memcpy(&mbie[1], own_addr, ETH_ALEN); + } +} + + +static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf) +{ + const u8 *bssid; + + bssid = fst_iface_get_bssid(f); + if (bssid) { + enum hostapd_hw_mode hw_mode; + u8 channel; + + if (buf) { + fst_iface_get_channel_info(f, &hw_mode, &channel); + fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f), + fst_hw_mode_to_band(hw_mode), channel); + } + return 1; + } else { + unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {}; + struct hostapd_hw_modes *modes; + enum mb_band_id b; + int num_modes = fst_iface_get_hw_modes(f, &modes); + int ret = 0; + + while (num_modes--) { + b = fst_hw_mode_to_band(modes->mode); + modes++; + if (b >= ARRAY_SIZE(bands) || bands[b]++) + continue; + ret++; + if (buf) + fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f), + b, MB_STA_CHANNEL_ALL); + } + return ret; + } +} + + +static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g, + struct fst_iface *i) +{ + struct wpabuf *buf; + struct fst_iface *f; + unsigned int nof_mbies = 0; + unsigned int nof_ifaces_added = 0; +#ifndef HOSTAPD + Boolean has_peer; + Boolean has_fst_peer; + + foreach_fst_group_iface(g, f) { + has_fst_peer = fst_has_fst_peer(f, &has_peer); + if (has_peer && !has_fst_peer) + return NULL; + } +#endif /* HOSTAPD */ + + foreach_fst_group_iface(g, f) { + if (f == i) + continue; + nof_mbies += fst_fill_iface_mb_ies(f, NULL); + } + + buf = wpabuf_alloc(nof_mbies * + (sizeof(struct multi_band_ie) + ETH_ALEN)); + if (!buf) { + fst_printf_iface(i, MSG_ERROR, + "cannot allocate mem for %u MB IEs", + nof_mbies); + return NULL; + } + + /* The list is sorted in descending order by priorities, so MB IEs will + * be arranged in the same order, as required by spec (see corresponding + * comment in.fst_attach(). + */ + foreach_fst_group_iface(g, f) { + if (f == i) + continue; + + fst_fill_iface_mb_ies(f, buf); + ++nof_ifaces_added; + + fst_printf_iface(i, MSG_DEBUG, "added to MB IE"); + } + + if (!nof_ifaces_added) { + wpabuf_free(buf); + buf = NULL; + fst_printf_iface(i, MSG_INFO, + "cannot add MB IE: no backup ifaces"); + } else { + fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i), + buf); + } + + return buf; +} + + +static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie) +{ + const u8 *peer_addr = NULL; + + switch (MB_CTRL_ROLE(mbie->mb_ctrl)) { + case MB_STA_ROLE_AP: + peer_addr = mbie->bssid; + break; + case MB_STA_ROLE_NON_PCP_NON_AP: + if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT && + (size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN) + peer_addr = (const u8 *) &mbie[1]; + break; + default: + break; + } + + return peer_addr; +} + + +static struct fst_iface * +fst_group_get_new_iface_by_mbie_and_band_id(struct fst_group *g, + const u8 *mb_ies_buff, + size_t mb_ies_size, + u8 band_id, + u8 *iface_peer_addr) +{ + while (mb_ies_size >= 2) { + const struct multi_band_ie *mbie = + (const struct multi_band_ie *) mb_ies_buff; + + if (mbie->eid != WLAN_EID_MULTI_BAND || + (size_t) 2 + mbie->len < sizeof(*mbie)) + break; + + if (mbie->band_id == band_id) { + struct fst_iface *iface; + + foreach_fst_group_iface(g, iface) { + const u8 *peer_addr = + fst_mbie_get_peer_addr(mbie); + + if (peer_addr && + fst_iface_is_connected(iface, peer_addr) && + band_id == fst_iface_get_band_id(iface)) { + os_memcpy(iface_peer_addr, peer_addr, + ETH_ALEN); + return iface; + } + } + break; + } + + mb_ies_buff += 2 + mbie->len; + mb_ies_size -= 2 + mbie->len; + } + + return NULL; +} + + +struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g, + const char *ifname) +{ + struct fst_iface *f; + + foreach_fst_group_iface(g, f) { + const char *in = fst_iface_get_name(f); + + if (os_strncmp(in, ifname, os_strlen(in)) == 0) + return f; + } + + return NULL; +} + + +u8 fst_group_assign_dialog_token(struct fst_group *g) +{ + g->dialog_token++; + if (g->dialog_token == 0) + g->dialog_token++; + return g->dialog_token; +} + + +u32 fst_group_assign_fsts_id(struct fst_group *g) +{ + g->fsts_id++; + return g->fsts_id; +} + + +static Boolean +fst_group_does_iface_appear_in_other_mbies(struct fst_group *g, + struct fst_iface *iface, + struct fst_iface *other, + u8 *peer_addr) +{ + struct fst_get_peer_ctx *ctx; + const u8 *addr; + const u8 *iface_addr; + enum mb_band_id iface_band_id; + + WPA_ASSERT(g == fst_iface_get_group(iface)); + WPA_ASSERT(g == fst_iface_get_group(other)); + + iface_addr = fst_iface_get_addr(iface); + iface_band_id = fst_iface_get_band_id(iface); + + addr = fst_iface_get_peer_first(other, &ctx, TRUE); + for (; addr; addr = fst_iface_get_peer_next(other, &ctx, TRUE)) { + const struct wpabuf *mbies; + u8 other_iface_peer_addr[ETH_ALEN]; + struct fst_iface *other_new_iface; + + mbies = fst_iface_get_peer_mb_ie(other, addr); + if (!mbies) + continue; + + other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id( + g, wpabuf_head(mbies), wpabuf_len(mbies), + iface_band_id, other_iface_peer_addr); + if (other_new_iface == iface && + os_memcmp(iface_addr, other_iface_peer_addr, + ETH_ALEN) != 0) { + os_memcpy(peer_addr, addr, ETH_ALEN); + return TRUE; + } + } + + return FALSE; +} + + +struct fst_iface * +fst_group_find_new_iface_by_stie(struct fst_group *g, + struct fst_iface *iface, + const u8 *peer_addr, + const struct session_transition_ie *stie, + u8 *iface_peer_addr) +{ + struct fst_iface *i; + + foreach_fst_group_iface(g, i) { + if (i == iface || + stie->new_band_id != fst_iface_get_band_id(i)) + continue; + if (fst_group_does_iface_appear_in_other_mbies(g, iface, i, + iface_peer_addr)) + return i; + break; + } + return NULL; +} + + +struct fst_iface * +fst_group_get_new_iface_by_stie_and_mbie( + struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size, + const struct session_transition_ie *stie, u8 *iface_peer_addr) +{ + return fst_group_get_new_iface_by_mbie_and_band_id( + g, mb_ies_buff, mb_ies_size, stie->new_band_id, + iface_peer_addr); +} + + +struct fst_group * fst_group_create(const char *group_id) +{ + struct fst_group *g; + + g = os_zalloc(sizeof(*g)); + if (g == NULL) { + fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id); + return NULL; + } + + dl_list_init(&g->ifaces); + os_strlcpy(g->group_id, group_id, sizeof(g->group_id)); + + dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry); + fst_printf_group(g, MSG_DEBUG, "instance created"); + + foreach_fst_ctrl_call(on_group_created, g); + + return g; +} + + +void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i) +{ + struct dl_list *list = &g->ifaces; + struct fst_iface *f; + + /* + * Add new interface to the list. + * The list is sorted in descending order by priority to allow + * multiple MB IEs creation according to the spec (see 10.32 Multi-band + * operation, 10.32.1 General), as they should be ordered according to + * priorities. + */ + foreach_fst_group_iface(g, f) { + if (fst_iface_get_priority(f) < fst_iface_get_priority(i)) + break; + list = &f->group_lentry; + } + dl_list_add(list, &i->group_lentry); +} + + +void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i) +{ + dl_list_del(&i->group_lentry); +} + + +void fst_group_delete(struct fst_group *group) +{ + struct fst_session *s; + + dl_list_del(&group->global_groups_lentry); + WPA_ASSERT(dl_list_empty(&group->ifaces)); + foreach_fst_ctrl_call(on_group_deleted, group); + fst_printf_group(group, MSG_DEBUG, "instance deleted"); + while ((s = fst_session_global_get_first_by_group(group)) != NULL) + fst_session_delete(s); + os_free(group); +} + + +Boolean fst_group_delete_if_empty(struct fst_group *group) +{ + Boolean is_empty = !fst_group_has_ifaces(group) && + !fst_session_global_get_first_by_group(group); + + if (is_empty) + fst_group_delete(group); + + return is_empty; +} + + +void fst_group_update_ie(struct fst_group *g) +{ + struct fst_iface *i; + + foreach_fst_group_iface(g, i) { + struct wpabuf *mbie = fst_group_create_mb_ie(g, i); + + if (!mbie) + fst_printf_iface(i, MSG_WARNING, "cannot create MB IE"); + + fst_iface_attach_mbie(i, mbie); + fst_iface_set_ies(i, mbie); + fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie); + } +} diff --git a/contrib/wpa/src/fst/fst_group.h b/contrib/wpa/src/fst/fst_group.h new file mode 100644 index 00000000000..3a87c0bc91c --- /dev/null +++ b/contrib/wpa/src/fst/fst_group.h @@ -0,0 +1,75 @@ +/* + * FST module - FST group object definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_GROUP_H +#define FST_GROUP_H + +struct fst_group { + char group_id[IFNAMSIZ + 1]; + struct dl_list ifaces; + u8 dialog_token; + u32 fsts_id; + struct dl_list global_groups_lentry; +}; + +struct session_transition_ie; + +#define foreach_fst_group_iface(g, i) \ + dl_list_for_each((i), &(g)->ifaces, struct fst_iface, group_lentry) + +struct fst_group * fst_group_create(const char *group_id); +void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i); +void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i); +void fst_group_delete(struct fst_group *g); + +void fst_group_update_ie(struct fst_group *g); + +static inline Boolean fst_group_has_ifaces(struct fst_group *g) +{ + return !dl_list_empty(&g->ifaces); +} + +static inline struct fst_iface * fst_group_first_iface(struct fst_group *g) +{ + return dl_list_first(&g->ifaces, struct fst_iface, group_lentry); +} + +static inline const char * fst_group_get_id(struct fst_group *g) +{ + return g->group_id; +} + +Boolean fst_group_delete_if_empty(struct fst_group *group); +struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g, + const char *ifname); +struct fst_iface * +fst_group_find_new_iface_by_stie(struct fst_group *g, + struct fst_iface *iface, + const u8 *peer_addr, + const struct session_transition_ie *stie, + u8 *iface_peer_addr); +struct fst_iface * +fst_group_get_new_iface_by_stie_and_mbie( + struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size, + const struct session_transition_ie *stie, u8 *iface_peer_addr); +u8 fst_group_assign_dialog_token(struct fst_group *g); +u32 fst_group_assign_fsts_id(struct fst_group *g); + +extern struct dl_list fst_global_groups_list; + +#define foreach_fst_group(g) \ + dl_list_for_each((g), &fst_global_groups_list, \ + struct fst_group, global_groups_lentry) + +static inline struct fst_group * fst_first_group(void) +{ + return dl_list_first(&fst_global_groups_list, struct fst_group, + global_groups_lentry); +} + +#endif /* FST_GROUP_H */ diff --git a/contrib/wpa/src/fst/fst_iface.c b/contrib/wpa/src/fst/fst_iface.c new file mode 100644 index 00000000000..5a92d2c33e4 --- /dev/null +++ b/contrib/wpa/src/fst/fst_iface.c @@ -0,0 +1,79 @@ +/* + * FST module - FST interface object implementation + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include "utils/common.h" +#include "fst/fst_internal.h" +#include "fst/fst_defs.h" + + +struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname, + const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg) +{ + struct fst_iface *i; + + i = os_zalloc(sizeof(*i)); + if (!i) { + fst_printf_group(g, MSG_ERROR, "cannot allocate iface for %s", + ifname); + return NULL; + } + + i->cfg = *cfg; + i->iface_obj = *iface_obj; + i->group = g; + os_strlcpy(i->ifname, ifname, sizeof(i->ifname)); + os_memcpy(i->own_addr, own_addr, sizeof(i->own_addr)); + + if (!i->cfg.llt) { + fst_printf_iface(i, MSG_WARNING, "Zero llt adjusted"); + i->cfg.llt = FST_DEFAULT_LLT_CFG_VALUE; + } + + return i; +} + + +void fst_iface_delete(struct fst_iface *i) +{ + fst_iface_set_ies(i, NULL); + wpabuf_free(i->mb_ie); + os_free(i); +} + + +Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr) +{ + struct fst_get_peer_ctx *ctx; + const u8 *a = fst_iface_get_peer_first(iface, &ctx, TRUE); + + for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, TRUE)) + if (os_memcmp(addr, a, ETH_ALEN) == 0) + return TRUE; + + return FALSE; +} + + +void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie) +{ + wpabuf_free(i->mb_ie); + i->mb_ie = mbie; +} + + +enum mb_band_id fst_iface_get_band_id(struct fst_iface *i) +{ + enum hostapd_hw_mode hw_mode; + u8 channel; + + fst_iface_get_channel_info(i, &hw_mode, &channel); + return fst_hw_mode_to_band(hw_mode); +} diff --git a/contrib/wpa/src/fst/fst_iface.h b/contrib/wpa/src/fst/fst_iface.h new file mode 100644 index 00000000000..4670d894f7c --- /dev/null +++ b/contrib/wpa/src/fst/fst_iface.h @@ -0,0 +1,135 @@ +/* + * FST module - FST interface object definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + + +#ifndef FST_IFACE_H +#define FST_IFACE_H + +#include "utils/includes.h" +#include "utils/common.h" +#include "list.h" +#include "fst.h" + +struct fst_iface { + struct fst_group *group; + struct fst_wpa_obj iface_obj; + u8 own_addr[ETH_ALEN]; + struct wpabuf *mb_ie; + char ifname[IFNAMSIZ + 1]; + struct fst_iface_cfg cfg; + struct dl_list group_lentry; +}; + +struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname, + const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg); +void fst_iface_delete(struct fst_iface *i); + +static inline struct fst_group * fst_iface_get_group(struct fst_iface *i) +{ + return i->group; +} + +static inline const char * fst_iface_get_name(struct fst_iface *i) +{ + return i->ifname; +} + +static inline const u8 * fst_iface_get_addr(struct fst_iface *i) +{ + return i->own_addr; +} + +static inline const char * fst_iface_get_group_id(struct fst_iface *i) +{ + return i->cfg.group_id; +} + +static inline u8 fst_iface_get_priority(struct fst_iface *i) +{ + return i->cfg.priority; +} + +static inline u32 fst_iface_get_llt(struct fst_iface *i) +{ + return i->cfg.llt; +} + +static inline const struct wpabuf * fst_iface_get_mbie(struct fst_iface *i) +{ + return i->mb_ie; +} + +static inline const u8 * fst_iface_get_bssid(struct fst_iface *i) +{ + return i->iface_obj.get_bssid(i->iface_obj.ctx); +} + +static inline void fst_iface_get_channel_info(struct fst_iface *i, + enum hostapd_hw_mode *hw_mode, + u8 *channel) +{ + i->iface_obj.get_channel_info(i->iface_obj.ctx, hw_mode, channel); +} + +static inline int fst_iface_get_hw_modes(struct fst_iface *i, + struct hostapd_hw_modes **modes) +{ + return i->iface_obj.get_hw_modes(i->iface_obj.ctx, modes); +} + +static inline void fst_iface_set_ies(struct fst_iface *i, + const struct wpabuf *fst_ies) +{ + i->iface_obj.set_ies(i->iface_obj.ctx, fst_ies); +} + +static inline int fst_iface_send_action(struct fst_iface *i, + const u8 *addr, struct wpabuf *data) +{ + return i->iface_obj.send_action(i->iface_obj.ctx, addr, data); +} + +static inline const struct wpabuf * +fst_iface_get_peer_mb_ie(struct fst_iface *i, const u8 *addr) +{ + return i->iface_obj.get_mb_ie(i->iface_obj.ctx, addr); +} + +static inline void fst_iface_update_mb_ie(struct fst_iface *i, + const u8 *addr, + const u8 *buf, size_t size) +{ + return i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size); +} + +static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i, + struct fst_get_peer_ctx **ctx, + Boolean mb_only) +{ + return i->iface_obj.get_peer_first(i->iface_obj.ctx, ctx, mb_only); +} + +static inline const u8 * fst_iface_get_peer_next(struct fst_iface *i, + struct fst_get_peer_ctx **ctx, + Boolean mb_only) +{ + return i->iface_obj.get_peer_next(i->iface_obj.ctx, ctx, mb_only); +} + +Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr); +void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie); +enum mb_band_id fst_iface_get_band_id(struct fst_iface *i); + +static inline void * fst_iface_get_wpa_obj_ctx(struct fst_iface *i) +{ + return i->iface_obj.ctx; +} + +#endif /* FST_IFACE_H */ diff --git a/contrib/wpa/src/fst/fst_internal.h b/contrib/wpa/src/fst/fst_internal.h new file mode 100644 index 00000000000..9fe32b85409 --- /dev/null +++ b/contrib/wpa/src/fst/fst_internal.h @@ -0,0 +1,49 @@ +/* + * FST module - auxiliary definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_INTERNAL_H +#define FST_INTERNAL_H + +#include "utils/includes.h" +#include "utils/common.h" +#include "common/defs.h" +#include "common/ieee802_11_defs.h" +#include "fst/fst_iface.h" +#include "fst/fst_group.h" +#include "fst/fst_session.h" + +#define fst_printf(level, format, ...) \ + wpa_printf((level), "FST: " format, ##__VA_ARGS__) + +#define fst_printf_group(group, level, format, ...) \ + wpa_printf((level), "FST: %s: " format, \ + fst_group_get_id(group), ##__VA_ARGS__) + +#define fst_printf_iface(iface, level, format, ...) \ + fst_printf_group(fst_iface_get_group(iface), (level), "%s: " format, \ + fst_iface_get_name(iface), ##__VA_ARGS__) + +enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode); + +struct fst_ctrl_handle { + struct fst_ctrl ctrl; + struct dl_list global_ctrls_lentry; +}; + +extern struct dl_list fst_global_ctrls_list; + +#define foreach_fst_ctrl_call(clb, ...) \ + do { \ + struct fst_ctrl_handle *__fst_ctrl_h; \ + dl_list_for_each(__fst_ctrl_h, &fst_global_ctrls_list, \ + struct fst_ctrl_handle, global_ctrls_lentry) \ + if (__fst_ctrl_h->ctrl.clb) \ + __fst_ctrl_h->ctrl.clb(__VA_ARGS__);\ + } while (0) + +#endif /* FST_INTERNAL_H */ diff --git a/contrib/wpa/src/fst/fst_session.c b/contrib/wpa/src/fst/fst_session.c new file mode 100644 index 00000000000..55fa69495e9 --- /dev/null +++ b/contrib/wpa/src/fst/fst_session.c @@ -0,0 +1,1620 @@ +/* + * FST module - FST Session implementation + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/defs.h" +#include "fst/fst_internal.h" +#include "fst/fst_defs.h" +#include "fst/fst_ctrl_iface.h" +#ifdef CONFIG_FST_TEST +#include "fst/fst_ctrl_defs.h" +#endif /* CONFIG_FST_TEST */ + +#define US_80211_TU 1024 + +#define US_TO_TU(m) ((m) * / US_80211_TU) +#define TU_TO_US(m) ((m) * US_80211_TU) + +#define FST_LLT_SWITCH_IMMEDIATELY 0 + +#define fst_printf_session(s, level, format, ...) \ + fst_printf((level), "%u (0x%08x): [" MACSTR "," MACSTR "] :" format, \ + (s)->id, (s)->data.fsts_id, \ + MAC2STR((s)->data.old_peer_addr), \ + MAC2STR((s)->data.new_peer_addr), \ + ##__VA_ARGS__) + +#define fst_printf_siface(s, iface, level, format, ...) \ + fst_printf_session((s), (level), "%s: " format, \ + fst_iface_get_name(iface), ##__VA_ARGS__) + +#define fst_printf_sframe(s, is_old, level, format, ...) \ + fst_printf_siface((s), \ + (is_old) ? (s)->data.old_iface : (s)->data.new_iface, \ + (level), format, ##__VA_ARGS__) + +#define FST_LLT_MS_DEFAULT 50 +#define FST_ACTION_MAX_SUPPORTED FST_ACTION_ON_CHANNEL_TUNNEL + +const char * const fst_action_names[] = { + [FST_ACTION_SETUP_REQUEST] = "Setup Request", + [FST_ACTION_SETUP_RESPONSE] = "Setup Response", + [FST_ACTION_TEAR_DOWN] = "Tear Down", + [FST_ACTION_ACK_REQUEST] = "Ack Request", + [FST_ACTION_ACK_RESPONSE] = "Ack Response", + [FST_ACTION_ON_CHANNEL_TUNNEL] = "On Channel Tunnel", +}; + +struct fst_session { + struct { + /* Session configuration that can be zeroed on reset */ + u8 old_peer_addr[ETH_ALEN]; + u8 new_peer_addr[ETH_ALEN]; + struct fst_iface *new_iface; + struct fst_iface *old_iface; + u32 llt_ms; + u8 pending_setup_req_dlgt; + u32 fsts_id; /* FSTS ID, see spec, 8.4.2.147 + * Session Transition element */ + } data; + /* Session object internal fields which won't be zeroed on reset */ + struct dl_list global_sessions_lentry; + u32 id; /* Session object ID used to identify + * specific session object */ + struct fst_group *group; + enum fst_session_state state; + Boolean stt_armed; +}; + +static struct dl_list global_sessions_list; +static u32 global_session_id = 0; + +#define foreach_fst_session(s) \ + dl_list_for_each((s), &global_sessions_list, \ + struct fst_session, global_sessions_lentry) + +#define foreach_fst_session_safe(s, temp) \ + dl_list_for_each_safe((s), (temp), &global_sessions_list, \ + struct fst_session, global_sessions_lentry) + + +static void fst_session_global_inc_id(void) +{ + global_session_id++; + if (global_session_id == FST_INVALID_SESSION_ID) + global_session_id++; +} + + +int fst_session_global_init(void) +{ + dl_list_init(&global_sessions_list); + return 0; +} + + +void fst_session_global_deinit(void) +{ + WPA_ASSERT(dl_list_empty(&global_sessions_list)); +} + + +static inline void fst_session_notify_ctrl(struct fst_session *s, + enum fst_event_type event_type, + union fst_event_extra *extra) +{ + foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra); +} + + +static void fst_session_set_state(struct fst_session *s, + enum fst_session_state state, + union fst_session_state_switch_extra *extra) +{ + if (s->state != state) { + union fst_event_extra evext = { + .session_state = { + .old_state = s->state, + .new_state = state, + }, + }; + + if (extra) + evext.session_state.extra = *extra; + fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED, + &evext); + fst_printf_session(s, MSG_INFO, "State: %s => %s", + fst_session_state_name(s->state), + fst_session_state_name(state)); + s->state = state; + } +} + + +static u32 fst_find_free_session_id(void) +{ + u32 i, id = FST_INVALID_SESSION_ID; + struct fst_session *s; + + for (i = 0; i < (u32) -1; i++) { + Boolean in_use = FALSE; + + foreach_fst_session(s) { + if (s->id == global_session_id) { + fst_session_global_inc_id(); + in_use = TRUE; + break; + } + } + if (!in_use) { + id = global_session_id; + fst_session_global_inc_id(); + break; + } + } + + return id; +} + + +static void fst_session_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct fst_session *s = user_ctx; + union fst_session_state_switch_extra extra = { + .to_initial = { + .reason = REASON_STT, + }, + }; + + fst_printf_session(s, MSG_WARNING, "Session State Timeout"); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra); +} + + +static void fst_session_stt_arm(struct fst_session *s) +{ + eloop_register_timeout(0, TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU), + fst_session_timeout_handler, NULL, s); + s->stt_armed = TRUE; +} + + +static void fst_session_stt_disarm(struct fst_session *s) +{ + if (s->stt_armed) { + eloop_cancel_timeout(fst_session_timeout_handler, NULL, s); + s->stt_armed = FALSE; + } +} + + +static Boolean fst_session_is_in_transition(struct fst_session *s) +{ + /* See spec, 10.32.2.2 Transitioning between states */ + return s->stt_armed; +} + + +static int fst_session_is_in_progress(struct fst_session *s) +{ + return s->state != FST_SESSION_STATE_INITIAL; +} + + +static int fst_session_is_ready_pending(struct fst_session *s) +{ + return s->state == FST_SESSION_STATE_SETUP_COMPLETION && + fst_session_is_in_transition(s); +} + + +static int fst_session_is_ready(struct fst_session *s) +{ + return s->state == FST_SESSION_STATE_SETUP_COMPLETION && + !fst_session_is_in_transition(s); +} + + +static int fst_session_is_switch_requested(struct fst_session *s) +{ + return s->state == FST_SESSION_STATE_TRANSITION_DONE && + fst_session_is_in_transition(s); +} + + +static struct fst_session * +fst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (s->group == g && + (os_memcmp(s->data.old_peer_addr, peer_addr, + ETH_ALEN) == 0 || + os_memcmp(s->data.new_peer_addr, peer_addr, + ETH_ALEN) == 0) && + fst_session_is_in_progress(s)) + return s; + } + + return NULL; +} + + +static void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason) +{ + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = reason, + }, + }; + + if (s->state == FST_SESSION_STATE_SETUP_COMPLETION || + s->state == FST_SESSION_STATE_TRANSITION_DONE) + fst_session_tear_down_setup(s); + fst_session_stt_disarm(s); + os_memset(&s->data, 0, sizeof(s->data)); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); +} + + +static int fst_session_send_action(struct fst_session *s, Boolean old_iface, + const void *payload, size_t size, + const struct wpabuf *extra_buf) +{ + size_t len; + int res; + struct wpabuf *buf; + u8 action; + struct fst_iface *iface = + old_iface ? s->data.old_iface : s->data.new_iface; + + WPA_ASSERT(payload != NULL); + WPA_ASSERT(size != 0); + + action = *(const u8 *) payload; + + WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED); + + if (!iface) { + fst_printf_session(s, MSG_ERROR, + "no %s interface for FST Action '%s' sending", + old_iface ? "old" : "new", + fst_action_names[action]); + return -1; + } + + len = sizeof(u8) /* category */ + size; + if (extra_buf) + len += wpabuf_size(extra_buf); + + buf = wpabuf_alloc(len); + if (!buf) { + fst_printf_session(s, MSG_ERROR, + "cannot allocate buffer of %zu bytes for FST Action '%s' sending", + len, fst_action_names[action]); + return -1; + } + + wpabuf_put_u8(buf, WLAN_ACTION_FST); + wpabuf_put_data(buf, payload, size); + if (extra_buf) + wpabuf_put_buf(buf, extra_buf); + + res = fst_iface_send_action(iface, + old_iface ? s->data.old_peer_addr : + s->data.new_peer_addr, buf); + if (res < 0) + fst_printf_siface(s, iface, MSG_ERROR, + "failed to send FST Action '%s'", + fst_action_names[action]); + else + fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent", + fst_action_names[action]); + wpabuf_free(buf); + + return res; +} + + +static int fst_session_send_tear_down(struct fst_session *s) +{ + struct fst_tear_down td; + int res; + + if (!fst_session_is_in_progress(s)) { + fst_printf_session(s, MSG_ERROR, "No FST setup to tear down"); + return -1; + } + + WPA_ASSERT(s->data.old_iface != NULL); + WPA_ASSERT(s->data.new_iface != NULL); + + os_memset(&td, 0, sizeof(td)); + + td.action = FST_ACTION_TEAR_DOWN; + td.fsts_id = host_to_le32(s->data.fsts_id); + + res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL); + if (!res) + fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent"); + else + fst_printf_sframe(s, TRUE, MSG_ERROR, + "failed to send FST TearDown"); + + return res; +} + + +static void fst_session_handle_setup_request(struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + struct fst_session *s; + const struct fst_setup_req *req; + struct fst_iface *new_iface = NULL; + struct fst_group *g; + u8 new_iface_peer_addr[ETH_ALEN]; + const struct wpabuf *peer_mbies; + size_t plen; + + if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req)) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: too short (%zu < %zu)", + frame_len, + IEEE80211_HDRLEN + 1 + sizeof(*req)); + return; + } + plen = frame_len - IEEE80211_HDRLEN - 1; + req = (const struct fst_setup_req *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION || + req->stie.length < 11) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: invalid STIE"); + return; + } + + if (req->stie.new_band_id == req->stie.old_band_id) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: new and old band IDs are the same"); + return; + } + + g = fst_iface_get_group(iface); + + if (plen > sizeof(*req)) { + fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1), + plen - sizeof(*req)); + fst_printf_iface(iface, MSG_INFO, + "FST Request: MB IEs updated for " MACSTR, + MAC2STR(mgmt->sa)); + } + + peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa); + if (peer_mbies) { + new_iface = fst_group_get_new_iface_by_stie_and_mbie( + g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies), + &req->stie, new_iface_peer_addr); + if (new_iface) + fst_printf_iface(iface, MSG_INFO, + "FST Request: new iface (%s:" MACSTR + ") found by MB IEs", + fst_iface_get_name(new_iface), + MAC2STR(new_iface_peer_addr)); + } + + if (!new_iface) { + new_iface = fst_group_find_new_iface_by_stie( + g, iface, mgmt->sa, &req->stie, + new_iface_peer_addr); + if (new_iface) + fst_printf_iface(iface, MSG_INFO, + "FST Request: new iface (%s:" MACSTR + ") found by others", + fst_iface_get_name(new_iface), + MAC2STR(new_iface_peer_addr)); + } + + if (!new_iface) { + fst_printf_iface(iface, MSG_WARNING, + "FST Request dropped: new iface not found"); + return; + } + + s = fst_find_session_in_progress(mgmt->sa, g); + if (s) { + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_SETUP, + }, + }; + + /* + * 10.32.2.2 Transitioning between states: + * Upon receipt of an FST Setup Request frame, the responder + * shall respond with an FST Setup Response frame unless it has + * a pending FST Setup Request frame addressed to the initiator + * and the responder has a numerically larger MAC address than + * the initiator’s MAC address, in which case, the responder + * shall delete the received FST Setup Request. + */ + if (os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) { + fst_printf_session(s, MSG_WARNING, + "FST Request dropped due to MAC comparison (our MAC is " + MACSTR ")", + MAC2STR(mgmt->da)); + return; + } + + if (!fst_session_is_ready_pending(s)) { + fst_printf_session(s, MSG_WARNING, + "FST Request from " MACSTR + " dropped due to inappropriate state %s", + MAC2STR(mgmt->da), + fst_session_state_name(s->state)); + return; + } + + + /* + * If FST Setup Request arrived with the same FSTS ID as one we + * initialized before, it means the other side either didn't + * receive our FST Request or skipped it for some reason (for + * example, due to numerical MAC comparison). + * + * In this case, there's no need to tear down the session. + * Moreover, as FSTS ID is the same, the other side will + * associate this tear down with the session it initiated that + * will break the sync. + */ + if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id) + fst_session_send_tear_down(s); + else + fst_printf_session(s, MSG_WARNING, + "Skipping TearDown as the FST request has the same FSTS ID as initiated"); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + fst_session_stt_disarm(s); + fst_printf_session(s, MSG_WARNING, "reset due to FST request"); + } + + s = fst_session_create(g); + if (!s) { + fst_printf(MSG_WARNING, + "FST Request dropped: cannot create session for %s and %s", + fst_iface_get_name(iface), + fst_iface_get_name(new_iface)); + return; + } + + fst_session_set_iface(s, iface, TRUE); + fst_session_set_peer_addr(s, mgmt->sa, TRUE); + fst_session_set_iface(s, new_iface, FALSE); + fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE); + fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt))); + s->data.pending_setup_req_dlgt = req->dialog_token; + s->data.fsts_id = le_to_host32(req->stie.fsts_id); + + fst_session_stt_arm(s); + + fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL); + + fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL); +} + + +static void fst_session_handle_setup_response(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_setup_res *res; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + enum hostapd_hw_mode hw_mode; + u8 channel; + union fst_session_state_switch_extra evext = { + .to_initial = {0}, + }; + + if (iface != s->data.old_iface) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped: %s is not the old iface", + fst_iface_get_name(iface)); + return; + } + + if (!fst_session_is_ready_pending(s)) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped due to wrong state: %s", + fst_session_state_name(s->state)); + return; + } + + if (plen < sizeof(*res)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Response dropped"); + return; + } + res = (const struct fst_setup_res *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION || + res->stie.length < 11) { + fst_printf_iface(iface, MSG_WARNING, + "FST Response dropped: invalid STIE"); + return; + } + + if (res->dialog_token != s->data.pending_setup_req_dlgt) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped due to wrong dialog token (%u != %u)", + s->data.pending_setup_req_dlgt, + res->dialog_token); + return; + } + + if (res->status_code == WLAN_STATUS_SUCCESS && + le_to_host32(res->stie.fsts_id) != s->data.fsts_id) { + fst_printf_session(s, MSG_WARNING, + "FST Response dropped due to wrong FST Session ID (%u)", + le_to_host32(res->stie.fsts_id)); + return; + } + + fst_session_stt_disarm(s); + + if (res->status_code != WLAN_STATUS_SUCCESS) { + /* + * 10.32.2.2 Transitioning between states + * The initiator shall set the STT to the value of the + * FSTSessionTimeOut field at ... and at each ACK frame sent in + * response to a received FST Setup Response with the Status + * Code field equal to PENDING_ADMITTING_FST_SESSION or + * PENDING_GAP_IN_BA_WINDOW. + */ + evext.to_initial.reason = REASON_REJECT; + evext.to_initial.reject_code = res->status_code; + evext.to_initial.initiator = FST_INITIATOR_REMOTE; + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + fst_printf_session(s, MSG_WARNING, + "FST Setup rejected by remote side with status %u", + res->status_code); + return; + } + + fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel); + + if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) { + evext.to_initial.reason = REASON_ERROR_PARAMS; + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + fst_printf_session(s, MSG_WARNING, + "invalid FST Setup parameters"); + fst_session_tear_down_setup(s); + return; + } + + fst_printf_session(s, MSG_INFO, + "%s: FST Setup established for %s (llt=%u)", + fst_iface_get_name(s->data.old_iface), + fst_iface_get_name(s->data.new_iface), + s->data.llt_ms); + + fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL); + + if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY) + fst_session_initiate_switch(s); +} + + +static void fst_session_handle_tear_down(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_tear_down *td; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_TEARDOWN, + .initiator = FST_INITIATOR_REMOTE, + }, + }; + + if (plen < sizeof(*td)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Tear Down dropped"); + return; + } + td = (const struct fst_tear_down *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + + if (le_to_host32(td->fsts_id) != s->data.fsts_id) { + fst_printf_siface(s, iface, MSG_WARNING, + "tear down for wrong FST Setup ID (%u)", + le_to_host32(td->fsts_id)); + return; + } + + fst_session_stt_disarm(s); + + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); +} + + +static void fst_session_handle_ack_request(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_ack_req *req; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + struct fst_ack_res res; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_SWITCH, + .initiator = FST_INITIATOR_REMOTE, + }, + }; + + if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) { + fst_printf_siface(s, iface, MSG_ERROR, + "cannot initiate switch due to wrong session state (%s)", + fst_session_state_name(s->state)); + return; + } + + WPA_ASSERT(s->data.new_iface != NULL); + + if (iface != s->data.new_iface) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack received on wrong interface"); + return; + } + + if (plen < sizeof(*req)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Ack Request dropped"); + return; + } + req = (const struct fst_ack_req *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + + if (le_to_host32(req->fsts_id) != s->data.fsts_id) { + fst_printf_siface(s, iface, MSG_WARNING, + "Ack for wrong FST Setup ID (%u)", + le_to_host32(req->fsts_id)); + return; + } + + os_memset(&res, 0, sizeof(res)); + + res.action = FST_ACTION_ACK_RESPONSE; + res.dialog_token = req->dialog_token; + res.fsts_id = req->fsts_id; + + if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) { + fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent"); + fst_session_stt_disarm(s); + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE, + NULL); + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, + NULL); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + } +} + + +static void +fst_session_handle_ack_response(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + const struct fst_ack_res *res; + size_t plen = frame_len - IEEE80211_HDRLEN - 1; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_SWITCH, + .initiator = FST_INITIATOR_LOCAL, + }, + }; + + if (!fst_session_is_switch_requested(s)) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack Response in inappropriate session state (%s)", + fst_session_state_name(s->state)); + return; + } + + WPA_ASSERT(s->data.new_iface != NULL); + + if (iface != s->data.new_iface) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack response received on wrong interface"); + return; + } + + if (plen < sizeof(*res)) { + fst_printf_session(s, MSG_WARNING, + "Too short FST Ack Response dropped"); + return; + } + res = (const struct fst_ack_res *) + (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1); + + if (le_to_host32(res->fsts_id) != s->data.fsts_id) { + fst_printf_siface(s, iface, MSG_ERROR, + "Ack response for wrong FST Setup ID (%u)", + le_to_host32(res->fsts_id)); + return; + } + + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL); + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + + fst_session_stt_disarm(s); +} + + +struct fst_session * fst_session_create(struct fst_group *g) +{ + struct fst_session *s; + u32 id; + + WPA_ASSERT(!is_zero_ether_addr(own_addr)); + + id = fst_find_free_session_id(); + if (id == FST_INVALID_SESSION_ID) { + fst_printf(MSG_ERROR, "Cannot assign new session ID"); + return NULL; + } + + s = os_zalloc(sizeof(*s)); + if (!s) { + fst_printf(MSG_ERROR, "Cannot allocate new session object"); + return NULL; + } + + s->id = id; + s->group = g; + s->state = FST_SESSION_STATE_INITIAL; + + s->data.llt_ms = FST_LLT_MS_DEFAULT; + + fst_printf(MSG_INFO, "Session %u created", s->id); + + dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry); + + foreach_fst_ctrl_call(on_session_added, s); + + return s; +} + + +void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface, + Boolean is_old) +{ + if (is_old) + s->data.old_iface = iface; + else + s->data.new_iface = iface; + +} + + +void fst_session_set_llt(struct fst_session *s, u32 llt) +{ + s->data.llt_ms = llt; +} + + +void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr, + Boolean is_old) +{ + u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr; + + os_memcpy(a, addr, ETH_ALEN); +} + + +int fst_session_initiate_setup(struct fst_session *s) +{ + struct fst_setup_req req; + int res; + u32 fsts_id; + u8 dialog_token; + struct fst_session *_s; + + if (fst_session_is_in_progress(s)) { + fst_printf_session(s, MSG_ERROR, "Session in progress"); + return -EINVAL; + } + + if (is_zero_ether_addr(s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, "No old peer MAC address"); + return -EINVAL; + } + + if (is_zero_ether_addr(s->data.new_peer_addr)) { + fst_printf_session(s, MSG_ERROR, "No new peer MAC address"); + return -EINVAL; + } + + if (!s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, "No old interface defined"); + return -EINVAL; + } + + if (!s->data.new_iface) { + fst_printf_session(s, MSG_ERROR, "No new interface defined"); + return -EINVAL; + } + + if (s->data.new_iface == s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, + "Same interface set as old and new"); + return -EINVAL; + } + + if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, + "The preset old peer address is not connected"); + return -EINVAL; + } + + if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr)) { + fst_printf_session(s, MSG_ERROR, + "The preset new peer address is not connected"); + return -EINVAL; + } + + _s = fst_find_session_in_progress(s->data.old_peer_addr, s->group); + if (_s) { + fst_printf_session(s, MSG_ERROR, + "There is another session in progress (old): %u", + _s->id); + return -EINVAL; + } + + _s = fst_find_session_in_progress(s->data.new_peer_addr, s->group); + if (_s) { + fst_printf_session(s, MSG_ERROR, + "There is another session in progress (new): %u", + _s->id); + return -EINVAL; + } + + dialog_token = fst_group_assign_dialog_token(s->group); + fsts_id = fst_group_assign_fsts_id(s->group); + + os_memset(&req, 0, sizeof(req)); + + fst_printf_siface(s, s->data.old_iface, MSG_INFO, + "initiating FST setup for %s (llt=%u ms)", + fst_iface_get_name(s->data.new_iface), s->data.llt_ms); + + req.action = FST_ACTION_SETUP_REQUEST; + req.dialog_token = dialog_token; + req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms)); + /* 8.4.2.147 Session Transition element */ + req.stie.element_id = WLAN_EID_SESSION_TRANSITION; + req.stie.length = sizeof(req.stie) - 2; + req.stie.fsts_id = host_to_le32(fsts_id); + req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface); + req.stie.new_band_op = 1; + req.stie.new_band_setup = 0; + + req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface); + req.stie.old_band_op = 1; + req.stie.old_band_setup = 0; + + res = fst_session_send_action(s, TRUE, &req, sizeof(req), + fst_iface_get_mbie(s->data.old_iface)); + if (!res) { + s->data.fsts_id = fsts_id; + s->data.pending_setup_req_dlgt = dialog_token; + fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent"); + fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, + NULL); + + fst_session_stt_arm(s); + } + + return res; +} + + +int fst_session_respond(struct fst_session *s, u8 status_code) +{ + struct fst_setup_res res; + enum hostapd_hw_mode hw_mode; + u8 channel; + + if (!fst_session_is_ready_pending(s)) { + fst_printf_session(s, MSG_ERROR, "incorrect state: %s", + fst_session_state_name(s->state)); + return -EINVAL; + } + + if (is_zero_ether_addr(s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, "No peer MAC address"); + return -EINVAL; + } + + if (!s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, "No old interface defined"); + return -EINVAL; + } + + if (!s->data.new_iface) { + fst_printf_session(s, MSG_ERROR, "No new interface defined"); + return -EINVAL; + } + + if (s->data.new_iface == s->data.old_iface) { + fst_printf_session(s, MSG_ERROR, + "Same interface set as old and new"); + return -EINVAL; + } + + if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) { + fst_printf_session(s, MSG_ERROR, + "The preset peer address is not in the peer list"); + return -EINVAL; + } + + fst_session_stt_disarm(s); + + os_memset(&res, 0, sizeof(res)); + + res.action = FST_ACTION_SETUP_RESPONSE; + res.dialog_token = s->data.pending_setup_req_dlgt; + res.status_code = status_code; + + res.stie.element_id = WLAN_EID_SESSION_TRANSITION; + res.stie.length = sizeof(res.stie) - 2; + + if (status_code == WLAN_STATUS_SUCCESS) { + res.stie.fsts_id = s->data.fsts_id; + res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + fst_iface_get_channel_info(s->data.new_iface, &hw_mode, + &channel); + res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.new_band_op = 1; + res.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s->data.old_iface, &hw_mode, + &channel); + res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.old_band_op = 1; + res.stie.old_band_setup = 0; + + fst_printf_session(s, MSG_INFO, + "%s: FST Setup Request accepted for %s (llt=%u)", + fst_iface_get_name(s->data.old_iface), + fst_iface_get_name(s->data.new_iface), + s->data.llt_ms); + } else { + fst_printf_session(s, MSG_WARNING, + "%s: FST Setup Request rejected with code %d", + fst_iface_get_name(s->data.old_iface), + status_code); + } + + if (fst_session_send_action(s, TRUE, &res, sizeof(res), + fst_iface_get_mbie(s->data.old_iface))) { + fst_printf_sframe(s, TRUE, MSG_ERROR, + "cannot send FST Setup Response with code %d", + status_code); + return -EINVAL; + } + + fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent"); + + if (status_code != WLAN_STATUS_SUCCESS) { + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_REJECT, + .reject_code = status_code, + .initiator = FST_INITIATOR_LOCAL, + }, + }; + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + } + + return 0; +} + + +int fst_session_initiate_switch(struct fst_session *s) +{ + struct fst_ack_req req; + int res; + u8 dialog_token; + + if (!fst_session_is_ready(s)) { + fst_printf_session(s, MSG_ERROR, + "cannot initiate switch due to wrong setup state (%d)", + s->state); + return -1; + } + + dialog_token = fst_group_assign_dialog_token(s->group); + + WPA_ASSERT(s->data.new_iface != NULL); + WPA_ASSERT(s->data.old_iface != NULL); + + fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s", + fst_iface_get_name(s->data.old_iface), + fst_iface_get_name(s->data.new_iface)); + + os_memset(&req, 0, sizeof(req)); + + req.action = FST_ACTION_ACK_REQUEST; + req.dialog_token = dialog_token; + req.fsts_id = host_to_le32(s->data.fsts_id); + + res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL); + if (!res) { + fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent"); + fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE, + NULL); + fst_session_stt_arm(s); + } else { + fst_printf_sframe(s, FALSE, MSG_ERROR, + "Cannot send FST Ack Request"); + } + + return res; +} + + +void fst_session_handle_action(struct fst_session *s, + struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len) +{ + switch (mgmt->u.action.u.fst_action.action) { + case FST_ACTION_SETUP_REQUEST: + WPA_ASSERT(0); + break; + case FST_ACTION_SETUP_RESPONSE: + fst_session_handle_setup_response(s, iface, mgmt, frame_len); + break; + case FST_ACTION_TEAR_DOWN: + fst_session_handle_tear_down(s, iface, mgmt, frame_len); + break; + case FST_ACTION_ACK_REQUEST: + fst_session_handle_ack_request(s, iface, mgmt, frame_len); + break; + case FST_ACTION_ACK_RESPONSE: + fst_session_handle_ack_response(s, iface, mgmt, frame_len); + break; + case FST_ACTION_ON_CHANNEL_TUNNEL: + default: + fst_printf_sframe(s, FALSE, MSG_ERROR, + "Unsupported FST Action frame"); + break; + } +} + + +int fst_session_tear_down_setup(struct fst_session *s) +{ + int res; + union fst_session_state_switch_extra evext = { + .to_initial = { + .reason = REASON_TEARDOWN, + .initiator = FST_INITIATOR_LOCAL, + }, + }; + + res = fst_session_send_tear_down(s); + + fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); + + return res; +} + + +void fst_session_reset(struct fst_session *s) +{ + fst_session_reset_ex(s, REASON_RESET); +} + + +void fst_session_delete(struct fst_session *s) +{ + fst_printf(MSG_INFO, "Session %u deleted", s->id); + dl_list_del(&s->global_sessions_lentry); + foreach_fst_ctrl_call(on_session_removed, s); + os_free(s); +} + + +struct fst_group * fst_session_get_group(struct fst_session *s) +{ + return s->group; +} + + +struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old) +{ + return is_old ? s->data.old_iface : s->data.new_iface; +} + + +u32 fst_session_get_id(struct fst_session *s) +{ + return s->id; +} + + +const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old) +{ + return is_old ? s->data.old_peer_addr : s->data.new_peer_addr; +} + + +u32 fst_session_get_llt(struct fst_session *s) +{ + return s->data.llt_ms; +} + + +enum fst_session_state fst_session_get_state(struct fst_session *s) +{ + return s->state; +} + + +struct fst_session * fst_session_get_by_id(u32 id) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (id == s->id) + return s; + } + + return NULL; +} + + +void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (!g || s->group == g) + clb(s->group, s, ctx); + } +} + + +void fst_session_on_action_rx(struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct fst_session *s; + + if (len < IEEE80211_HDRLEN + 2 || + mgmt->u.action.category != WLAN_ACTION_FST) { + fst_printf_iface(iface, MSG_ERROR, + "invalid Action frame received"); + return; + } + + if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) { + fst_printf_iface(iface, MSG_DEBUG, + "FST Action '%s' received!", + fst_action_names[mgmt->u.action.u.fst_action.action]); + } else { + fst_printf_iface(iface, MSG_WARNING, + "unknown FST Action (%u) received!", + mgmt->u.action.u.fst_action.action); + return; + } + + if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) { + fst_session_handle_setup_request(iface, mgmt, len); + return; + } + + s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface)); + if (s) { + fst_session_handle_action(s, iface, mgmt, len); + } else { + fst_printf_iface(iface, MSG_WARNING, + "FST Action '%s' dropped: no session in progress found", + fst_action_names[mgmt->u.action.u.fst_action.action]); + } +} + + +int fst_session_set_str_ifname(struct fst_session *s, const char *ifname, + Boolean is_old) +{ + struct fst_group *g = fst_session_get_group(s); + struct fst_iface *i; + + i = fst_group_get_iface_by_name(g, ifname); + if (!i) { + fst_printf_session(s, MSG_WARNING, + "Cannot set iface %s: no such iface within group '%s'", + ifname, fst_group_get_id(g)); + return -1; + } + + fst_session_set_iface(s, i, is_old); + + return 0; +} + + +int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac, + Boolean is_old) +{ + u8 peer_addr[ETH_ALEN]; + int res = fst_read_peer_addr(mac, peer_addr); + + if (res) + return res; + + fst_session_set_peer_addr(s, peer_addr, is_old); + + return 0; +} + + +int fst_session_set_str_llt(struct fst_session *s, const char *llt_str) +{ + char *endp; + long int llt = strtol(llt_str, &endp, 0); + + if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) { + fst_printf_session(s, MSG_WARNING, + "Cannot set llt %s: Invalid llt value (1..%u expected)", + llt_str, FST_MAX_LLT_MS); + return -1; + } + fst_session_set_llt(s, (u32) llt); + + return 0; +} + + +void fst_session_global_on_iface_detached(struct fst_iface *iface) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (fst_session_is_in_progress(s) && + (s->data.new_iface == iface || + s->data.old_iface == iface)) + fst_session_reset_ex(s, REASON_DETACH_IFACE); + } +} + + +struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g) +{ + struct fst_session *s; + + foreach_fst_session(s) { + if (s->group == g) + return s; + } + + return NULL; +} + + +#ifdef CONFIG_FST_TEST + +static int get_group_fill_session(struct fst_group **g, struct fst_session *s) +{ + const u8 *old_addr, *new_addr; + struct fst_get_peer_ctx *ctx; + + os_memset(s, 0, sizeof(*s)); + foreach_fst_group(*g) { + s->data.new_iface = fst_group_first_iface(*g); + if (s->data.new_iface) + break; + } + if (!s->data.new_iface) + return -EINVAL; + + s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next, + struct fst_iface, group_lentry); + if (!s->data.old_iface) + return -EINVAL; + + old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE); + if (!old_addr) + return -EINVAL; + + new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE); + if (!new_addr) + return -EINVAL; + + os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN); + os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN); + + return 0; +} + + +#define FST_MAX_COMMAND_WORD_NAME_LENGTH 16 + +int fst_test_req_send_fst_request(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_setup_req req; + struct fst_session s; + struct fst_group *g; + enum hostapd_hw_mode hw_mode; + u8 channel; + char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + req.action = FST_ACTION_SETUP_REQUEST; + req.dialog_token = g->dialog_token; + req.llt = host_to_le32(FST_LLT_MS_DEFAULT); + /* 8.4.2.147 Session Transition element */ + req.stie.element_id = WLAN_EID_SESSION_TRANSITION; + req.stie.length = sizeof(req.stie) - 2; + req.stie.fsts_id = host_to_le32(fsts_id); + req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel); + req.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + req.stie.new_band_op = 1; + req.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel); + req.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + req.stie.old_band_op = 1; + req.stie.old_band_setup = 0; + + if (!fst_read_next_text_param(endp, additional_param, + sizeof(additional_param), &endp)) { + if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND)) + req.stie.new_band_id = req.stie.old_band_id; + } + + return fst_session_send_action(&s, TRUE, &req, sizeof(req), + s.data.old_iface->mb_ie); +} + + +int fst_test_req_send_fst_response(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_setup_res res; + struct fst_session s; + struct fst_group *g; + enum hostapd_hw_mode hw_mode; + u8 status_code; + u8 channel; + char response[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + struct fst_session *_s; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + status_code = WLAN_STATUS_SUCCESS; + if (!fst_read_next_text_param(endp, response, sizeof(response), + &endp)) { + if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT)) + status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION; + } + + os_memset(&res, 0, sizeof(res)); + + res.action = FST_ACTION_SETUP_RESPONSE; + /* + * If some session has just received an FST Setup Request, then + * use the correct dialog token copied from this request. + */ + _s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE), + g); + res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ? + _s->data.pending_setup_req_dlgt : g->dialog_token; + res.status_code = status_code; + + res.stie.element_id = WLAN_EID_SESSION_TRANSITION; + res.stie.length = sizeof(res.stie) - 2; + + if (res.status_code == WLAN_STATUS_SUCCESS) { + res.stie.fsts_id = fsts_id; + res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); + + fst_iface_get_channel_info(s.data.new_iface, &hw_mode, + &channel); + res.stie.new_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.new_band_op = 1; + res.stie.new_band_setup = 0; + + fst_iface_get_channel_info(s.data.old_iface, &hw_mode, + &channel); + res.stie.old_band_id = fst_hw_mode_to_band(hw_mode); + res.stie.old_band_op = 1; + res.stie.old_band_setup = 0; + } + + if (!fst_read_next_text_param(endp, response, sizeof(response), + &endp)) { + if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND)) + res.stie.new_band_id = res.stie.old_band_id; + } + + return fst_session_send_action(&s, TRUE, &res, sizeof(res), + s.data.old_iface->mb_ie); +} + + +int fst_test_req_send_ack_request(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_ack_req req; + struct fst_session s; + struct fst_group *g; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + os_memset(&req, 0, sizeof(req)); + req.action = FST_ACTION_ACK_REQUEST; + req.dialog_token = g->dialog_token; + req.fsts_id = fsts_id; + + return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL); +} + + +int fst_test_req_send_ack_response(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_ack_res res; + struct fst_session s; + struct fst_group *g; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + os_memset(&res, 0, sizeof(res)); + res.action = FST_ACTION_ACK_RESPONSE; + res.dialog_token = g->dialog_token; + res.fsts_id = fsts_id; + + return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL); +} + + +int fst_test_req_send_tear_down(const char *params) +{ + int fsts_id; + Boolean is_valid; + char *endp; + struct fst_tear_down td; + struct fst_session s; + struct fst_group *g; + + if (params[0] != ' ') + return -EINVAL; + params++; + fsts_id = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return -EINVAL; + + if (get_group_fill_session(&g, &s)) + return -EINVAL; + + os_memset(&td, 0, sizeof(td)); + td.action = FST_ACTION_TEAR_DOWN; + td.fsts_id = fsts_id; + + return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL); +} + + +u32 fst_test_req_get_fsts_id(const char *params) +{ + int sid; + Boolean is_valid; + char *endp; + struct fst_session *s; + + if (params[0] != ' ') + return FST_FSTS_ID_NOT_FOUND; + params++; + sid = fst_read_next_int_param(params, &is_valid, &endp); + if (!is_valid) + return FST_FSTS_ID_NOT_FOUND; + + s = fst_session_get_by_id(sid); + if (!s) + return FST_FSTS_ID_NOT_FOUND; + + return s->data.fsts_id; +} + + +int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen) +{ + char *endp; + char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH]; + struct fst_group *g; + struct fst_iface *iface; + + if (request[0] != ' ') + return -EINVAL; + request++; + if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) || + !*ifname) + goto problem; + g = dl_list_first(&fst_global_groups_list, struct fst_group, + global_groups_lentry); + if (!g) + goto problem; + iface = fst_group_get_iface_by_name(g, ifname); + if (!iface || !iface->mb_ie) + goto problem; + return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie), + wpabuf_len(iface->mb_ie)); + +problem: + return os_snprintf(buf, buflen, "FAIL\n"); +} + +#endif /* CONFIG_FST_TEST */ diff --git a/contrib/wpa/src/fst/fst_session.h b/contrib/wpa/src/fst/fst_session.h new file mode 100644 index 00000000000..1162de4b367 --- /dev/null +++ b/contrib/wpa/src/fst/fst_session.h @@ -0,0 +1,80 @@ +/* + * FST module - FST Session related definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_SESSION_H +#define FST_SESSION_H + +#define FST_DEFAULT_SESSION_TIMEOUT_TU 255 /* u8 */ + +struct fst_iface; +struct fst_group; +struct fst_session; +enum fst_session_state; + +int fst_session_global_init(void); +void fst_session_global_deinit(void); +void fst_session_global_on_iface_detached(struct fst_iface *iface); +struct fst_session * +fst_session_global_get_first_by_group(struct fst_group *g); + +struct fst_session * fst_session_create(struct fst_group *g); +void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface, + Boolean is_old); +void fst_session_set_llt(struct fst_session *s, u32 llt); +void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr, + Boolean is_old); +int fst_session_initiate_setup(struct fst_session *s); +int fst_session_respond(struct fst_session *s, u8 status_code); +int fst_session_initiate_switch(struct fst_session *s); +void fst_session_handle_action(struct fst_session *s, struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, + size_t frame_len); +int fst_session_tear_down_setup(struct fst_session *s); +void fst_session_reset(struct fst_session *s); +void fst_session_delete(struct fst_session *s); + +struct fst_group * fst_session_get_group(struct fst_session *s); +struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old); +const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old); +u32 fst_session_get_id(struct fst_session *s); +u32 fst_session_get_llt(struct fst_session *s); +enum fst_session_state fst_session_get_state(struct fst_session *s); + +struct fst_session *fst_session_get_by_id(u32 id); + +typedef void (*fst_session_enum_clb)(struct fst_group *g, struct fst_session *s, + void *ctx); + +void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx); + +void fst_session_on_action_rx(struct fst_iface *iface, + const struct ieee80211_mgmt *mgmt, size_t len); + + +int fst_session_set_str_ifname(struct fst_session *s, const char *ifname, + Boolean is_old); +int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac, + Boolean is_old); +int fst_session_set_str_llt(struct fst_session *s, const char *llt_str); + +#ifdef CONFIG_FST_TEST + +#define FST_FSTS_ID_NOT_FOUND ((u32) -1) + +int fst_test_req_send_fst_request(const char *params); +int fst_test_req_send_fst_response(const char *params); +int fst_test_req_send_ack_request(const char *params); +int fst_test_req_send_ack_response(const char *params); +int fst_test_req_send_tear_down(const char *params); +u32 fst_test_req_get_fsts_id(const char *params); +int fst_test_req_get_local_mbies(const char *request, char *buf, + size_t buflen); + +#endif /* CONFIG_FST_TEST */ + +#endif /* FST_SESSION_H */ diff --git a/contrib/wpa/src/p2p/p2p.c b/contrib/wpa/src/p2p/p2p.c index 6adb3dc2049..767706c01d6 100644 --- a/contrib/wpa/src/p2p/p2p.c +++ b/contrib/wpa/src/p2p/p2p.c @@ -48,9 +48,8 @@ static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); #define P2P_PEER_EXPIRATION_AGE 60 #endif /* P2P_PEER_EXPIRATION_AGE */ -#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) -static void p2p_expire_peers(struct p2p_data *p2p) +void p2p_expire_peers(struct p2p_data *p2p) { struct p2p_device *dev, *n; struct os_reltime now; @@ -103,15 +102,6 @@ static void p2p_expire_peers(struct p2p_data *p2p) } -static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct p2p_data *p2p = eloop_ctx; - p2p_expire_peers(p2p); - eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, - p2p_expiration_timeout, p2p, NULL); -} - - static const char * p2p_state_txt(int state) { switch (state) { @@ -297,7 +287,7 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) return; } - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return; @@ -346,7 +336,7 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) return 0; } - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return -1; @@ -468,7 +458,8 @@ static void p2p_copy_client_info(struct p2p_device *dev, static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, const u8 *go_interface_addr, int freq, - const u8 *gi, size_t gi_len) + const u8 *gi, size_t gi_len, + struct os_reltime *rx_time) { struct p2p_group_info info; size_t c; @@ -536,10 +527,11 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, os_memcpy(dev->interface_addr, cli->p2p_interface_addr, ETH_ALEN); - os_get_reltime(&dev->last_seen); + os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); os_memcpy(dev->member_in_go_iface, go_interface_addr, ETH_ALEN); + dev->flags |= P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT; } return 0; @@ -758,26 +750,35 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, /* * Update the device entry only if the new peer - * entry is newer than the one previously stored. + * entry is newer than the one previously stored, or if + * the device was previously seen as a P2P Client in a group + * and the new entry isn't older than a threshold. */ if (dev->last_seen.sec > 0 && - os_reltime_before(rx_time, &dev->last_seen)) { - p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)", + os_reltime_before(rx_time, &dev->last_seen) && + (!(dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT) || + os_reltime_expired(&dev->last_seen, rx_time, + P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD))) { + p2p_dbg(p2p, + "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u flags=0x%x)", (unsigned int) rx_time->sec, (unsigned int) rx_time->usec, (unsigned int) dev->last_seen.sec, - (unsigned int) dev->last_seen.usec); + (unsigned int) dev->last_seen.usec, + dev->flags); p2p_parse_free(&msg); return -1; } os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); - dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY | + P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT); if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0) os_memcpy(dev->interface_addr, addr, ETH_ALEN); if (msg.ssid && + msg.ssid[1] <= sizeof(dev->oper_ssid) && (msg.ssid[1] != P2P_WILDCARD_SSID_LEN || os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0)) { @@ -843,7 +844,8 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, if (scan_res) { p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, - msg.group_info, msg.group_info_len); + msg.group_info, msg.group_info_len, + rx_time); } p2p_parse_free(&msg); @@ -1127,8 +1129,10 @@ static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash) adv_array = (u8 *) str_buf; adv_len = os_strlen(str); + if (adv_len >= sizeof(str_buf)) + return 0; - for (i = 0; str[i] && i < adv_len; i++) { + for (i = 0; i < adv_len; i++) { if (str[i] >= 'A' && str[i] <= 'Z') str_buf[i] = str[i] - 'A' + 'a'; else @@ -1182,27 +1186,25 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, * An empty seek string means no hash values, but still an ASP * search. */ + p2p_dbg(p2p, "ASP search"); p2p->p2ps_seek_count = 0; p2p->p2ps_seek = 1; } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) { u8 buf[P2PS_HASH_LEN]; - int i; + int i, count = 0; - p2p->p2ps_seek_count = seek_count; for (i = 0; i < seek_count; i++) { if (!p2ps_gen_hash(p2p, seek[i], buf)) continue; - /* If asking for wildcard, don't do others */ - if (os_memcmp(buf, p2p->wild_card_hash, - P2PS_HASH_LEN) == 0) { - p2p->p2ps_seek_count = 0; - break; - } - - os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf, - P2PS_HASH_LEN); + p2p_dbg(p2p, "Seek service %s hash " MACSTR, + seek[i], MAC2STR(buf)); + os_memcpy(&p2p->p2ps_seek_hash[count * P2PS_HASH_LEN], + buf, P2PS_HASH_LEN); + count++; } + + p2p->p2ps_seek_count = count; p2p->p2ps_seek = 1; } else { p2p->p2ps_seek_count = 0; @@ -1212,7 +1214,8 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, /* Special case to perform wildcard search */ if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) { p2p->p2ps_seek_count = 1; - os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN); + os_memcpy(&p2p->p2ps_seek_hash, p2p->wild_card_hash, + P2PS_HASH_LEN); } p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; @@ -1380,7 +1383,7 @@ static int p2p_prepare_channel_pref(struct p2p_data *p2p, static void p2p_prepare_channel_best(struct p2p_data *p2p) { u8 op_class, op_channel; - const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_5ghz[] = { 124, 125, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; const int op_classes_vht[] = { 128, 0 }; @@ -2147,7 +2150,9 @@ int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) } -struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, + const u8 *query_hash, + u8 query_count) { struct wpabuf *buf; u8 *len; @@ -2162,7 +2167,7 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]); - if (p2p->query_count) + if (query_count) extra += MAX_SVC_ADV_IE_LEN; buf = wpabuf_alloc(1000 + extra); @@ -2199,9 +2204,8 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) p2p_buf_add_device_info(buf, p2p, NULL); p2p_buf_update_ie_hdr(buf, len); - if (p2p->query_count) { - p2p_buf_add_service_instance(buf, p2p, p2p->query_count, - p2p->query_hash, + if (query_count) { + p2p_buf_add_service_instance(buf, p2p, query_count, query_hash, p2p->p2ps_adv_list); } @@ -2212,18 +2216,21 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) { struct p2ps_advertisement *adv_data; + int any_wfa; p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list); - /* Wildcard always matches if we have actual services */ - if (os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0) - return p2p->p2ps_adv_list != NULL; + /* Wildcard org.wi-fi.wfds matches any WFA spec defined service */ + any_wfa = os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0; adv_data = p2p->p2ps_adv_list; while (adv_data) { - p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]); if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0) - return 1; + return 1; /* exact hash match */ + if (any_wfa && + os_strncmp(adv_data->svc_name, P2PS_WILD_HASH_STR, + os_strlen(P2PS_WILD_HASH_STR)) == 0) + return 1; /* WFA service match */ adv_data = adv_data->next; } @@ -2233,13 +2240,15 @@ static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) static enum p2p_probe_req_status p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len) + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq) { struct ieee802_11_elems elems; struct wpabuf *buf; struct ieee80211_mgmt *resp; struct p2p_message msg; struct wpabuf *ies; + u8 channel, op_class; if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { @@ -2291,53 +2300,42 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_P2P; } - p2p->p2ps_svc_found = 0; - if (msg.service_hash && msg.service_hash_count) { const u8 *hash = msg.service_hash; - u8 *dest = p2p->query_hash; u8 i; + int p2ps_svc_found = 0; + + p2p_dbg(p2p, "in_listen=%d drv_in_listen=%d when received P2PS Probe Request at %u MHz; own Listen channel %u, pending listen freq %u MHz", + p2p->in_listen, p2p->drv_in_listen, rx_freq, + p2p->cfg->channel, p2p->pending_listen_freq); + + if (!p2p->in_listen && !p2p->drv_in_listen && + p2p->pending_listen_freq && rx_freq && + rx_freq != p2p->pending_listen_freq) { + p2p_dbg(p2p, "Do not reply to Probe Request frame that was received on %u MHz while waiting to start Listen state on %u MHz", + rx_freq, p2p->pending_listen_freq); + p2p_parse_free(&msg); + return P2P_PREQ_NOT_LISTEN; + } - p2p->query_count = 0; for (i = 0; i < msg.service_hash_count; i++) { if (p2p_service_find_asp(p2p, hash)) { - p2p->p2ps_svc_found = 1; - - if (!os_memcmp(hash, p2p->wild_card_hash, - P2PS_HASH_LEN)) { - /* We found match(es) but wildcard - * will return all */ - p2p->query_count = 1; - os_memcpy(p2p->query_hash, hash, - P2PS_HASH_LEN); - break; - } - - /* Save each matching hash */ - if (p2p->query_count < P2P_MAX_QUERY_HASH) { - os_memcpy(dest, hash, P2PS_HASH_LEN); - dest += P2PS_HASH_LEN; - p2p->query_count++; - } else { - /* We found match(es) but too many to - * return all */ - p2p->query_count = 0; - break; - } + p2p_dbg(p2p, "Service Hash match found: " + MACSTR, MAC2STR(hash)); + p2ps_svc_found = 1; + break; } hash += P2PS_HASH_LEN; } - p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found); - /* Probed hash unknown */ - if (!p2p->p2ps_svc_found) { + if (!p2ps_svc_found) { + p2p_dbg(p2p, "No Service Hash match found"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } } else { /* This is not a P2PS Probe Request */ - p2p->query_count = 0; p2p_dbg(p2p, "No P2PS Hash in Probe Request"); if (!p2p->in_listen || !p2p->drv_in_listen) { @@ -2366,11 +2364,11 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } - p2p_parse_free(&msg); if (!p2p->cfg->send_probe_resp) { /* Response generated elsewhere */ p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response"); + p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -2382,7 +2380,9 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, * really only used for discovery purposes, not to learn exact BSS * parameters. */ - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, msg.service_hash, + msg.service_hash_count); + p2p_parse_free(&msg); if (ies == NULL) return P2P_PREQ_NOT_PROCESSED; @@ -2392,8 +2392,8 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_PROCESSED; } - resp = NULL; - resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp); + resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, + u.probe_resp.variable)); resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_PROBE_RESP << 4)); @@ -2422,32 +2422,50 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, wpabuf_put_u8(buf, 480 / 5); wpabuf_put_u8(buf, 540 / 5); + if (!rx_freq) { + channel = p2p->cfg->channel; + } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) { + wpabuf_free(ies); + wpabuf_free(buf); + return P2P_PREQ_NOT_PROCESSED; + } + wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); wpabuf_put_u8(buf, 1); - wpabuf_put_u8(buf, p2p->cfg->channel); + wpabuf_put_u8(buf, channel); wpabuf_put_buf(buf, ies); wpabuf_free(ies); - p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf); + p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq); wpabuf_free(buf); - return P2P_PREQ_NOT_PROCESSED; + return P2P_PREQ_PROCESSED; } enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len) + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq) { enum p2p_probe_req_status res; p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); - res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); - p2p->query_count = 0; + res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq); + if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED) + return res; + /* + * Activate a pending GO Negotiation/Invite flow if a received Probe + * Request frame is from an expected peer. Some devices may share the + * same address for P2P and non-P2P STA running simultaneously. The + * P2P_PREQ_PROCESSED and P2P_PREQ_NOT_PROCESSED p2p_reply_probe() + * return values verified above ensure we are handling a Probe Request + * frame from a P2P peer. + */ if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && p2p->go_neg_peer && os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) @@ -2457,7 +2475,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout"); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); - return P2P_PREQ_PROCESSED; + return res; } if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && @@ -2469,7 +2487,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout"); eloop_cancel_timeout(p2p_invite_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); - return P2P_PREQ_PROCESSED; + return res; } return res; @@ -2484,10 +2502,21 @@ static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, size_t tmplen; int res; u8 group_capab; + struct p2p_message msg; if (p2p_ie == NULL) return 0; /* WLAN AP is not a P2P manager */ + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg) < 0) + return 0; + + p2p_dbg(p2p, "BSS P2P manageability %s", + msg.manageability ? "enabled" : "disabled"); + + if (!msg.manageability) + return 0; + /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) @@ -2650,13 +2679,14 @@ int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id) int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, u16 config_methods, - const char *svc_info) + const char *svc_info, const u8 *cpt_priority) { struct p2ps_advertisement *adv_data, *tmp, **prev; u8 buf[P2PS_HASH_LEN]; size_t adv_data_len, adv_len, info_len = 0; + int i; - if (!p2p || !adv_str || !adv_str[0]) + if (!p2p || !adv_str || !adv_str[0] || !cpt_priority) return -1; if (!(config_methods & p2p->cfg->config_methods)) { @@ -2685,6 +2715,11 @@ int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, adv_data->auto_accept = (u8) auto_accept; os_memcpy(adv_data->svc_name, adv_str, adv_len); + for (i = 0; cpt_priority[i] && i < P2PS_FEATURE_CAPAB_CPT_MAX; i++) { + adv_data->cpt_priority[i] = cpt_priority[i]; + adv_data->cpt_mask |= cpt_priority[i]; + } + if (svc_info && info_len) { adv_data->svc_info = &adv_data->svc_name[adv_len + 1]; os_memcpy(adv_data->svc_info, svc_info, info_len); @@ -2723,13 +2758,33 @@ int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, inserted: p2p_dbg(p2p, - "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'", - adv_id, adv_data->config_methods, svc_state, adv_str); + "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s' cpt_mask=0x%x", + adv_id, adv_data->config_methods, svc_state, adv_str, + adv_data->cpt_mask); return 0; } +void p2p_service_flush_asp(struct p2p_data *p2p) +{ + struct p2ps_advertisement *adv, *prev; + + if (!p2p) + return; + + adv = p2p->p2ps_adv_list; + while (adv) { + prev = adv; + adv = adv->next; + os_free(prev); + } + + p2p->p2ps_adv_list = NULL; + p2p_dbg(p2p, "All ASP advertisements flushed"); +} + + int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) { struct p2p_message msg; @@ -2861,9 +2916,6 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) dl_list_init(&p2p->devices); - eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, - p2p_expiration_timeout, p2p, NULL); - p2p->go_timeout = 100; p2p->client_timeout = 20; p2p->num_p2p_sd_queries = 0; @@ -2878,8 +2930,6 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) void p2p_deinit(struct p2p_data *p2p) { - struct p2ps_advertisement *adv, *prev; - #ifdef CONFIG_WIFI_DISPLAY wpabuf_free(p2p->wfd_ie_beacon); wpabuf_free(p2p->wfd_ie_probe_req); @@ -2894,7 +2944,6 @@ void p2p_deinit(struct p2p_data *p2p) wpabuf_free(p2p->wfd_coupled_sink_info); #endif /* CONFIG_WIFI_DISPLAY */ - eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); @@ -2908,18 +2957,12 @@ void p2p_deinit(struct p2p_data *p2p) os_free(p2p->cfg->serial_number); os_free(p2p->cfg->pref_chan); os_free(p2p->groups); - os_free(p2p->p2ps_prov); + p2ps_prov_free(p2p); wpabuf_free(p2p->sd_resp); os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); os_free(p2p->no_go_freq.range); - - adv = p2p->p2ps_adv_list; - while (adv) { - prev = adv; - adv = adv->next; - os_free(prev); - } + p2p_service_flush_asp(p2p); os_free(p2p); } @@ -2937,6 +2980,7 @@ void p2p_flush(struct p2p_data *p2p) p2p_free_sd_queries(p2p); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; + p2p->ssid_set = 0; } @@ -4120,7 +4164,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "country=%c%c\n" "oper_freq=%d\n" "req_config_methods=0x%x\n" - "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" + "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "status=%d\n" "invitation_reqs=%u\n", (int) (now.sec - dev->last_seen.sec), @@ -4164,6 +4208,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "[FORCE_FREQ]" : "", dev->flags & P2P_DEV_PD_FOR_JOIN ? "[PD_FOR_JOIN]" : "", + dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT ? + "[LAST_SEEN_AS_GROUP_CLIENT]" : "", dev->status, dev->invitation_reqs); if (os_snprintf_error(end - pos, res)) @@ -5215,6 +5261,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, if (!msg.oob_go_neg_channel) { p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included"); + p2p_parse_free(&msg); return -1; } @@ -5226,6 +5273,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, msg.oob_go_neg_channel[4]); if (freq < 0) { p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); + p2p_parse_free(&msg); return -1; } role = msg.oob_go_neg_channel[5]; @@ -5246,6 +5294,7 @@ int p2p_process_nfc_connection_handover(struct p2p_data *p2p, p2p->cfg->channel); if (freq < 0) { p2p_dbg(p2p, "Own listen channel not known"); + p2p_parse_free(&msg); return -1; } p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq); @@ -5334,3 +5383,20 @@ void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx) "Timeout on waiting peer to become ready for GO Negotiation"); p2p_go_neg_failed(p2p, -1); } + + +void p2p_set_own_pref_freq_list(struct p2p_data *p2p, + const unsigned int *pref_freq_list, + unsigned int size) +{ + unsigned int i; + + if (size > P2P_MAX_PREF_CHANNELS) + size = P2P_MAX_PREF_CHANNELS; + p2p->num_pref_freq = size; + for (i = 0; i < size; i++) { + p2p->pref_freq_list[i] = pref_freq_list[i]; + p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz", + i, p2p->pref_freq_list[i]); + } +} diff --git a/contrib/wpa/src/p2p/p2p.h b/contrib/wpa/src/p2p/p2p.h index 2402db6a7eb..b4060be477b 100644 --- a/contrib/wpa/src/p2p/p2p.h +++ b/contrib/wpa/src/p2p/p2p.h @@ -9,7 +9,8 @@ #ifndef P2P_H #define P2P_H -#include "wps/wps_defs.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps.h" /* P2P ASP Setup Capability */ #define P2PS_SETUP_NONE 0 @@ -20,6 +21,12 @@ #define P2PS_WILD_HASH_STR "org.wi-fi.wfds" #define P2PS_HASH_LEN 6 #define P2P_MAX_QUERY_HASH 6 +#define P2PS_FEATURE_CAPAB_CPT_MAX 2 + +/** + * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels + */ +#define P2P_MAX_PREF_CHANNELS 100 /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes @@ -95,7 +102,7 @@ struct p2p_go_neg_results { /** * ssid - SSID of the group */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of SSID in octets @@ -154,6 +161,11 @@ struct p2p_go_neg_results { }; struct p2ps_provision { + /** + * pd_seeker - P2PS provision discovery seeker role + */ + unsigned int pd_seeker:1; + /** * status - Remote returned provisioning status code */ @@ -194,6 +206,23 @@ struct p2ps_provision { */ u8 adv_mac[ETH_ALEN]; + /** + * cpt_mask - Supported Coordination Protocol Transport mask + * + * A bitwise mask of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_priority. + */ + u8 cpt_mask; + + /** + * cpt_priority - Coordination Protocol Transport priority list + * + * Priorities of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_mask. + * The CPT priority list is 0 terminated. + */ + u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + /** * info - Vendor defined extra Provisioning information */ @@ -233,6 +262,23 @@ struct p2ps_advertisement { */ u8 hash[P2PS_HASH_LEN]; + /** + * cpt_mask - supported Coordination Protocol Transport mask + * + * A bitwise mask of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_priority. + */ + u8 cpt_mask; + + /** + * cpt_priority - Coordination Protocol Transport priority list + * + * Priorities of supported ASP Coordinatin Protocol Transports. + * This property is set together and corresponds with cpt_mask. + * The CPT priority list is 0 terminated. + */ + u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + /** * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage */ @@ -268,27 +314,27 @@ struct p2p_peer_info { /** * device_name - Device Name (0..32 octets encoded in UTF-8) */ - char device_name[33]; + char device_name[WPS_DEV_NAME_MAX_LEN + 1]; /** * manufacturer - Manufacturer (0..64 octets encoded in UTF-8) */ - char manufacturer[65]; + char manufacturer[WPS_MANUFACTURER_MAX_LEN + 1]; /** * model_name - Model Name (0..32 octets encoded in UTF-8) */ - char model_name[33]; + char model_name[WPS_MODEL_NAME_MAX_LEN + 1]; /** * model_number - Model Number (0..32 octets encoded in UTF-8) */ - char model_number[33]; + char model_number[WPS_MODEL_NUMBER_MAX_LEN + 1]; /** * serial_number - Serial Number (0..32 octets encoded in UTF-8) */ - char serial_number[33]; + char serial_number[WPS_SERIAL_NUMBER_MAX_LEN + 1]; /** * level - Signal level @@ -316,7 +362,7 @@ struct p2p_peer_info { * This list includes from 0 to 16 Secondary Device Types as indicated * by wps_sec_dev_type_list_len (8 * number of types). */ - u8 wps_sec_dev_type_list[128]; + u8 wps_sec_dev_type_list[WPS_SEC_DEV_TYPE_MAX_LEN]; /** * wps_sec_dev_type_list_len - Length of secondary device type list @@ -495,7 +541,7 @@ struct p2p_config { * This data will be added to the end of the SSID after the * DIRECT- prefix. */ - u8 ssid_postfix[32 - 9]; + u8 ssid_postfix[SSID_MAX_LEN - 9]; /** * ssid_postfix_len - Length of the ssid_postfix data @@ -569,12 +615,14 @@ struct p2p_config { * send_probe_resp - Transmit a Probe Response frame * @ctx: Callback context from cb_ctx * @buf: Probe Response frame (including the header and body) + * @freq: Forced frequency (in MHz) to use or 0. * Returns: 0 on success, -1 on failure * * This function is used to reply to Probe Request frames that were * indicated with a call to p2p_probe_req_rx(). The response is to be - * sent on the same channel or to be dropped if the driver is not - * anymore listening to Probe Request frames. + * sent on the same channel, unless otherwise specified, or to be + * dropped if the driver is not listening to Probe Request frames + * anymore. * * Alternatively, the responsibility for building the Probe Response * frames in Listen state may be in another system component in which @@ -585,7 +633,8 @@ struct p2p_config { * Request frames must be indicated by calling p2p_probe_req_rx() even * if this send_probe_resp() is not used. */ - int (*send_probe_resp)(void *ctx, const struct wpabuf *buf); + int (*send_probe_resp)(void *ctx, const struct wpabuf *buf, + unsigned int freq); /** * send_action - Transmit an Action frame @@ -704,6 +753,7 @@ struct p2p_config { * @ctx: Callback context from cb_ctx * @src: Source address of the message triggering this notification * @dev_passwd_id: WPS Device Password ID + * @go_intent: Peer's GO Intent * * This callback is used to notify that a P2P Device is requesting * group owner negotiation with us, but we do not have all the @@ -712,7 +762,8 @@ struct p2p_config { * PIN or PBC button press. This information can be provided with a * call to p2p_connect(). */ - void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id); + void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id, + u8 go_intent); /** * go_neg_completed - Notification of GO Negotiation results @@ -949,18 +1000,21 @@ struct p2p_config { /** * Determine if we have a persistent group we share with remote peer + * and allocate interface for this group if needed * @ctx: Callback context from cb_ctx * @addr: Peer device address to search for * @ssid: Persistent group SSID or %NULL if any * @ssid_len: Length of @ssid - * @go_dev_addr: Buffer for returning intended GO P2P Device Address + * @go_dev_addr: Buffer for returning GO P2P Device Address * @ret_ssid: Buffer for returning group SSID * @ret_ssid_len: Buffer for returning length of @ssid + * @intended_iface_addr: Buffer for returning intended iface address * Returns: 1 if a matching persistent group was found, 0 otherwise */ int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid, size_t ssid_len, u8 *go_dev_addr, - u8 *ret_ssid, size_t *ret_ssid_len); + u8 *ret_ssid, size_t *ret_ssid_len, + u8 *intended_iface_addr); /** * Get information about a possible local GO role @@ -1001,7 +1055,8 @@ struct p2p_config { u8 conncap, int passwd_id, const u8 *persist_ssid, size_t persist_ssid_size, int response_done, - int prov_start, const char *session_info); + int prov_start, const char *session_info, + const u8 *feat_cap, size_t feat_cap_len); /** * prov_disc_resp_cb - Callback for indicating completion of PD Response @@ -1023,6 +1078,20 @@ struct p2p_config { * P2PS_SETUP_* bitmap is used as the parameters and return value. */ u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role); + + /** + * get_pref_freq_list - Get preferred frequency list for an interface + * @ctx: Callback context from cb_ctx + * @go: Whether the use if for GO role + * @len: Length of freq_list in entries (both IN and OUT) + * @freq_list: Buffer for returning the preferred frequencies (MHz) + * Returns: 0 on success, -1 on failure + * + * This function can be used to query the preferred frequency list from + * the driver specific to a particular interface type. + */ + int (*get_pref_freq_list)(void *ctx, int go, + unsigned int *len, unsigned int *freq_list); }; @@ -1460,11 +1529,13 @@ enum p2p_probe_req_status { * @bssid: BSSID if available or %NULL * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets + * @rx_freq: Probe Request frame RX frequency * Returns: value indicating the type and status of the probe request */ enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len); + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq); /** * p2p_rx_action - Report received Action frame @@ -1607,7 +1678,7 @@ struct p2p_group_config { /** * ssid - Group SSID */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of SSID @@ -2214,7 +2285,7 @@ struct p2p_nfc_params { size_t oob_dev_pw_len; int go_freq; u8 go_dev_addr[ETH_ALEN]; - u8 go_ssid[32]; + u8 go_ssid[SSID_MAX_LEN]; size_t go_ssid_len; }; @@ -2240,8 +2311,33 @@ struct p2ps_advertisement * p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id); int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, - u16 config_methods, const char *svc_info); + u16 config_methods, const char *svc_info, + const u8 *cpt_priority); int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id); +void p2p_service_flush_asp(struct p2p_data *p2p); struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p); +/** + * p2p_expire_peers - Periodic cleanup function to expire peers + * @p2p: P2P module context from p2p_init() + * + * This is a cleanup function that the entity calling p2p_init() is + * expected to call periodically to clean up expired peer entries. + */ +void p2p_expire_peers(struct p2p_data *p2p); + +void p2p_set_own_pref_freq_list(struct p2p_data *p2p, + const unsigned int *pref_freq_list, + unsigned int size); + +/** + * p2p_group_get_common_freqs - Get the group common frequencies + * @group: P2P group context from p2p_group_init() + * @common_freqs: On return will hold the group common frequencies + * @num: On return will hold the number of group common frequencies + * Returns: 0 on success, -1 otherwise + */ +int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, + unsigned int *num); + #endif /* P2P_H */ diff --git a/contrib/wpa/src/p2p/p2p_build.c b/contrib/wpa/src/p2p/p2p_build.c index 92c920662ed..793d28ba7bd 100644 --- a/contrib/wpa/src/p2p/p2p_build.c +++ b/contrib/wpa/src/p2p/p2p_build.c @@ -10,6 +10,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/qca-vendor.h" #include "wps/wps_i.h" #include "p2p_i.h" @@ -109,6 +110,44 @@ void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, } +void p2p_buf_add_pref_channel_list(struct wpabuf *buf, + const u32 *preferred_freq_list, + unsigned int size) +{ + unsigned int i, count = 0; + u8 op_class, op_channel; + + if (!size) + return; + + /* + * First, determine the number of P2P supported channels in the + * pref_freq_list returned from driver. This is needed for calculations + * of the vendor IE size. + */ + for (i = 0; i < size; i++) { + if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, + &op_channel) == 0) + count++; + } + + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 4 + count * sizeof(u16)); + wpabuf_put_be24(buf, OUI_QCA); + wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST); + for (i = 0; i < size; i++) { + if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, + &op_channel) < 0) { + wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz", + preferred_freq_list[i]); + continue; + } + wpabuf_put_u8(buf, op_class); + wpabuf_put_u8(buf, op_channel); + } +} + + void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, struct p2p_channels *chan) { @@ -353,10 +392,10 @@ void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p) /* Service Hash */ wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH); wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN); - wpabuf_put_data(buf, p2p->query_hash, + wpabuf_put_data(buf, p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash", - p2p->query_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); + p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); } @@ -404,152 +443,221 @@ void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac) } +static int p2ps_wildcard_hash(struct p2p_data *p2p, + const u8 *hash, u8 hash_count) +{ + u8 i; + const u8 *test = hash; + + for (i = 0; i < hash_count; i++) { + if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0) + return 1; + test += P2PS_HASH_LEN; + } + + return 0; +} + + +static int p2p_wfa_service_adv(struct p2p_data *p2p) +{ + struct p2ps_advertisement *adv; + + for (adv = p2p->p2ps_adv_list; adv; adv = adv->next) { + if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR, + os_strlen(P2PS_WILD_HASH_STR)) == 0) + return 1; + } + + return 0; +} + + +static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p, + u32 adv_id, u16 config_methods, + const char *svc_name, u8 **ie_len, u8 **pos, + size_t *total_len, u8 *attr_len) +{ + size_t svc_len; + size_t remaining; + size_t info_len; + + p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id); + svc_len = os_strlen(svc_name); + info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) + + svc_len; + + if (info_len + *total_len > MAX_SVC_ADV_LEN) { + p2p_dbg(p2p, + "Unsufficient buffer, failed to add advertised service info"); + return -1; + } + + if (svc_len > 255) { + p2p_dbg(p2p, + "Invalid service name length (%u bytes), failed to add advertised service info", + (unsigned int) svc_len); + return -1; + } + + if (*ie_len) { + int ie_data_len = (*pos - *ie_len) - 1; + + if (ie_data_len < 0 || ie_data_len > 255) { + p2p_dbg(p2p, + "Invalid IE length, failed to add advertised service info"); + return -1; + } + remaining = 255 - ie_data_len; + } else { + /* + * Adding new P2P IE header takes 6 extra bytes: + * - 2 byte IE header (1 byte IE id and 1 byte length) + * - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below + */ + *ie_len = p2p_buf_add_ie_hdr(buf); + remaining = 255 - 4; + } + + if (remaining < sizeof(u32) + sizeof(u16) + sizeof(u8)) { + /* + * Split adv_id, config_methods, and svc_name_len between two + * IEs. + */ + size_t front = remaining; + size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front; + u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)]; + + WPA_PUT_LE32(holder, adv_id); + WPA_PUT_BE16(&holder[sizeof(u32)], config_methods); + holder[sizeof(u32) + sizeof(u16)] = svc_len; + + if (front) + wpabuf_put_data(buf, holder, front); + + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); + + wpabuf_put_data(buf, &holder[front], back); + remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) - + back; + } else { + wpabuf_put_le32(buf, adv_id); + wpabuf_put_be16(buf, config_methods); + wpabuf_put_u8(buf, svc_len); + remaining -= sizeof(adv_id) + sizeof(config_methods) + + sizeof(u8); + } + + if (remaining < svc_len) { + /* split svc_name between two or three IEs */ + size_t front = remaining; + size_t back = svc_len - front; + + if (front) + wpabuf_put_data(buf, svc_name, front); + + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); + + /* In rare cases, we must split across 3 attributes */ + if (back > 255 - 4) { + wpabuf_put_data(buf, &svc_name[front], 255 - 4); + back -= 255 - 4; + front += 255 - 4; + p2p_buf_update_ie_hdr(buf, *ie_len); + *ie_len = p2p_buf_add_ie_hdr(buf); + } + + wpabuf_put_data(buf, &svc_name[front], back); + remaining = 255 - 4 - back; + } else { + wpabuf_put_data(buf, svc_name, svc_len); + remaining -= svc_len; + } + + p2p_buf_update_ie_hdr(buf, *ie_len); + + /* set *ie_len to NULL if a new IE has to be added on the next call */ + if (!remaining) + *ie_len = NULL; + + /* set *pos to point to the next byte to update */ + *pos = wpabuf_put(buf, 0); + + *total_len += info_len; + WPA_PUT_LE16(attr_len, (u16) *total_len); + return 0; +} + + void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, u8 hash_count, const u8 *hash, struct p2ps_advertisement *adv_list) { struct p2ps_advertisement *adv; - struct wpabuf *tmp_buf; - u8 *tag_len = NULL, *ie_len = NULL; - size_t svc_len = 0, remaining = 0, total_len = 0; + int p2ps_wildcard; + size_t total_len; + struct wpabuf *tmp_buf = NULL; + u8 *pos, *attr_len, *ie_len = NULL; - if (!adv_list || !hash) + if (!adv_list || !hash || !hash_count) return; + wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values", + hash, hash_count * P2PS_HASH_LEN); + p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) && + p2p_wfa_service_adv(p2p); + /* Allocate temp buffer, allowing for overflow of 1 instance */ tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN); if (!tmp_buf) return; + /* + * Attribute data can be split into a number of IEs. Start with the + * first IE and the attribute headers here. + */ + ie_len = p2p_buf_add_ie_hdr(tmp_buf); + + total_len = 0; + + wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE); + attr_len = wpabuf_put(tmp_buf, sizeof(u16)); + WPA_PUT_LE16(attr_len, (u16) total_len); + p2p_buf_update_ie_hdr(tmp_buf, ie_len); + pos = wpabuf_put(tmp_buf, 0); + + if (p2ps_wildcard) { + /* org.wi-fi.wfds match found */ + p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR, + &ie_len, &pos, &total_len, attr_len); + } + + /* add advertised service info of matching services */ for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN; adv = adv->next) { - u8 count = hash_count; const u8 *test = hash; + u8 i; - while (count--) { - /* Check for wildcard */ - if (os_memcmp(test, p2p->wild_card_hash, - P2PS_HASH_LEN) == 0) { - total_len = MAX_SVC_ADV_LEN + 1; - goto wild_hash; - } - - if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0) - goto hash_match; + for (i = 0; i < hash_count; i++) { + /* exact name hash match */ + if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 && + p2p_buf_add_service_info(tmp_buf, p2p, + adv->id, + adv->config_methods, + adv->svc_name, + &ie_len, &pos, + &total_len, + attr_len)) + break; test += P2PS_HASH_LEN; } - - /* No matches found - Skip this Adv Instance */ - continue; - -hash_match: - if (!tag_len) { - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - remaining = 255 - 4; - if (!ie_len) { - wpabuf_put_u8(tmp_buf, - P2P_ATTR_ADVERTISED_SERVICE); - ie_len = wpabuf_put(tmp_buf, sizeof(u16)); - remaining -= (sizeof(u8) + sizeof(u16)); - } - } - - svc_len = os_strlen(adv->svc_name); - - if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) { - /* Can't fit... return wildcard */ - total_len = MAX_SVC_ADV_LEN + 1; - break; - } - - if (remaining <= (sizeof(adv->id) + - sizeof(adv->config_methods))) { - size_t front = remaining; - size_t back = (sizeof(adv->id) + - sizeof(adv->config_methods)) - front; - u8 holder[sizeof(adv->id) + - sizeof(adv->config_methods)]; - - /* This works even if front or back == 0 */ - WPA_PUT_LE32(holder, adv->id); - WPA_PUT_BE16(&holder[sizeof(adv->id)], - adv->config_methods); - wpabuf_put_data(tmp_buf, holder, front); - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - wpabuf_put_data(tmp_buf, &holder[front], back); - remaining = 255 - (sizeof(adv->id) + - sizeof(adv->config_methods)) - back; - } else { - wpabuf_put_le32(tmp_buf, adv->id); - wpabuf_put_be16(tmp_buf, adv->config_methods); - remaining -= (sizeof(adv->id) + - sizeof(adv->config_methods)); - } - - /* We are guaranteed at least one byte for svc_len */ - wpabuf_put_u8(tmp_buf, svc_len); - remaining -= sizeof(u8); - - if (remaining < svc_len) { - size_t front = remaining; - size_t back = svc_len - front; - - wpabuf_put_data(tmp_buf, adv->svc_name, front); - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - - /* In rare cases, we must split across 3 attributes */ - if (back > 255 - 4) { - wpabuf_put_data(tmp_buf, - &adv->svc_name[front], 255 - 4); - back -= 255 - 4; - front += 255 - 4; - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - tag_len = p2p_buf_add_ie_hdr(tmp_buf); - } - - wpabuf_put_data(tmp_buf, &adv->svc_name[front], back); - remaining = 255 - 4 - back; - } else { - wpabuf_put_data(tmp_buf, adv->svc_name, svc_len); - remaining -= svc_len; - } - - /* adv_id config_methods svc_string */ - total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len; } - if (tag_len) - p2p_buf_update_ie_hdr(tmp_buf, tag_len); - - if (ie_len) - WPA_PUT_LE16(ie_len, (u16) total_len); - -wild_hash: - /* If all fit, return matching instances, otherwise the wildcard */ - if (total_len <= MAX_SVC_ADV_LEN) { + if (total_len) wpabuf_put_buf(buf, tmp_buf); - } else { - char *wild_card = P2PS_WILD_HASH_STR; - u8 wild_len; - - /* Insert wildcard instance */ - tag_len = p2p_buf_add_ie_hdr(buf); - wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE); - ie_len = wpabuf_put(buf, sizeof(u16)); - - wild_len = (u8) os_strlen(wild_card); - wpabuf_put_le32(buf, 0); - wpabuf_put_be16(buf, 0); - wpabuf_put_u8(buf, wild_len); - wpabuf_put_data(buf, wild_card, wild_len); - - WPA_PUT_LE16(ie_len, 4 + 2 + 1 + wild_len); - p2p_buf_update_ie_hdr(buf, tag_len); - } - wpabuf_free(tmp_buf); } diff --git a/contrib/wpa/src/p2p/p2p_dev_disc.c b/contrib/wpa/src/p2p/p2p_dev_disc.c index 86bae1a2c0d..98805fee240 100644 --- a/contrib/wpa/src/p2p/p2p_dev_disc.c +++ b/contrib/wpa/src/p2p/p2p_dev_disc.c @@ -314,7 +314,7 @@ void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU"); - ies = p2p_build_probe_resp_ies(p2p); + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); if (ies == NULL) return; diff --git a/contrib/wpa/src/p2p/p2p_go_neg.c b/contrib/wpa/src/p2p/p2p_go_neg.c index 98abf9d2293..83b43563d94 100644 --- a/contrib/wpa/src/p2p/p2p_go_neg.c +++ b/contrib/wpa/src/p2p/p2p_go_neg.c @@ -185,6 +185,9 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p->op_reg_class, p2p->op_channel); p2p_buf_update_ie_hdr(buf, len); + p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, + p2p->num_pref_freq); + /* WPS IE with Device Password ID attribute */ pw_id = p2p_wps_method_pw_id(peer->wps_method); if (peer->oob_pw_id) @@ -312,7 +315,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); - if (peer && peer->go_state == REMOTE_GO) { + if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) { p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { p2p_buf_add_operating_channel(buf, p2p->cfg->country, @@ -379,7 +382,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, int freq; u8 op_reg_class, op_channel; unsigned int i; - const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_5ghz[] = { 124, 125, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; const int op_classes_vht[] = { 128, 0 }; @@ -542,6 +545,195 @@ int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, } +static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, + struct p2p_device *dev, + struct p2p_message *msg, + unsigned freq_list[], unsigned int size) +{ + u8 op_class, op_channel; + unsigned int oper_freq = 0, i, j; + int found = 0; + + p2p_dbg(p2p, + "Peer didn't provide a preferred frequency list, see if any of our preferred channels are supported by peer device"); + + /* + * Search for a common channel in our preferred frequency list which is + * also supported by the peer device. + */ + for (i = 0; i < size && !found; i++) { + /* + * Make sure that the common frequency is: + * 1. Supported by peer + * 2. Allowed for P2P use. + */ + oper_freq = freq_list[i]; + if (p2p_freq_to_channel(oper_freq, &op_class, + &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq); + continue; + } + if (!p2p_channels_includes(&p2p->cfg->channels, + op_class, op_channel) && + (go || !p2p_channels_includes(&p2p->cfg->cli_channels, + op_class, op_channel))) { + p2p_dbg(p2p, + "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", + oper_freq, op_class, op_channel); + break; + } + for (j = 0; j < msg->channel_list_len; j++) { + + if (op_channel != msg->channel_list[j]) + continue; + + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + found = 1; + break; + } + } + + if (found) { + p2p_dbg(p2p, + "Freq %d MHz is a preferred channel and is also supported by peer, use it as the operating channel", + oper_freq); + } else { + p2p_dbg(p2p, + "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel", + dev->oper_freq); + } +} + + +static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, + struct p2p_device *dev, + struct p2p_message *msg, + unsigned freq_list[], unsigned int size) +{ + u8 op_class, op_channel; + unsigned int oper_freq = 0, i, j; + int found = 0; + + /* + * Peer device supports a Preferred Frequency List. + * Search for a common channel in the preferred frequency lists + * of both peer and local devices. + */ + for (i = 0; i < size && !found; i++) { + for (j = 2; j < (msg->pref_freq_list_len / 2); j++) { + oper_freq = p2p_channel_to_freq( + msg->pref_freq_list[2 * j], + msg->pref_freq_list[2 * j + 1]); + if (freq_list[i] != oper_freq) + continue; + + /* + * Make sure that the found frequency is: + * 1. Supported + * 2. Allowed for P2P use. + */ + if (p2p_freq_to_channel(oper_freq, &op_class, + &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", + oper_freq); + continue; + } + + if (!p2p_channels_includes(&p2p->cfg->channels, + op_class, op_channel) && + (go || + !p2p_channels_includes(&p2p->cfg->cli_channels, + op_class, op_channel))) { + p2p_dbg(p2p, + "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", + oper_freq, op_class, op_channel); + break; + } + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + found = 1; + break; + } + } + + if (found) { + p2p_dbg(p2p, + "Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel", + oper_freq); + } else { + p2p_dbg(p2p, + "No common preferred channels found! Use: %d MHz for oper_channel", + dev->oper_freq); + } +} + + +void p2p_check_pref_chan(struct p2p_data *p2p, int go, + struct p2p_device *dev, struct p2p_message *msg) +{ + unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size; + unsigned int i; + u8 op_class, op_channel; + + /* + * Use the preferred channel list from the driver only if there is no + * forced_freq, e.g., P2P_CONNECT freq=..., and no preferred operating + * channel hardcoded in the configuration file. + */ + if (!p2p->cfg->get_pref_freq_list || p2p->cfg->num_pref_chan || + (dev->flags & P2P_DEV_FORCE_FREQ) || p2p->cfg->cfg_op_channel) + return; + + /* Obtain our preferred frequency list from driver based on P2P role. */ + size = P2P_MAX_PREF_CHANNELS; + if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size, + freq_list)) + return; + + /* + * Check if peer's preference of operating channel is in + * our preferred channel list. + */ + for (i = 0; i < size; i++) { + if (freq_list[i] == (unsigned int) dev->oper_freq) + break; + } + if (i != size) { + /* Peer operating channel preference matches our preference */ + if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) < + 0) { + p2p_dbg(p2p, + "Peer operating channel preference is unsupported frequency %u MHz", + freq_list[i]); + } else { + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + return; + } + } + + p2p_dbg(p2p, + "Peer operating channel preference: %d MHz is not in our preferred channel list", + dev->oper_freq); + + /* + Check if peer's preferred channel list is + * _not_ included in the GO Negotiation Request or Invitation Request. + */ + if (msg->pref_freq_list_len == 0) + p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size); + else + p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size); +} + + void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -668,7 +860,9 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, - msg.dev_password_id); + msg.dev_password_id, + msg.go_intent ? (*msg.go_intent >> 1) : + 0); } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { p2p_dbg(p2p, "Already in Group Formation with another peer"); status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; @@ -797,6 +991,12 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Peer operating channel preference: %d MHz", dev->oper_freq); + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, go, dev, &msg); + if (msg.config_timeout) { dev->go_timeout = msg.config_timeout[0]; dev->client_timeout = msg.config_timeout[1]; @@ -1148,6 +1348,13 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, if (go && p2p_go_select_channel(p2p, dev, &status) < 0) goto fail; + /* + * Use the driver preferred frequency list extension if local device is + * GO. + */ + if (go) + p2p_check_pref_chan(p2p, go, dev, &msg); + p2p_set_state(p2p, P2P_GO_NEG); p2p_clear_timeout(p2p); diff --git a/contrib/wpa/src/p2p/p2p_group.c b/contrib/wpa/src/p2p/p2p_group.c index 41ca99faaf4..0d669934656 100644 --- a/contrib/wpa/src/p2p/p2p_group.c +++ b/contrib/wpa/src/p2p/p2p_group.c @@ -1071,3 +1071,43 @@ void p2p_loop_on_all_groups(struct p2p_data *p2p, break; } } + + +int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, + unsigned int *num) + +{ + struct p2p_channels intersect, res; + struct p2p_group_member *m; + + if (!group || !common_freqs || !num) + return -1; + + os_memset(&intersect, 0, sizeof(intersect)); + os_memset(&res, 0, sizeof(res)); + + p2p_channels_union(&intersect, &group->p2p->cfg->channels, + &intersect); + + p2p_channels_dump(group->p2p, + "Group common freqs before iterating members", + &intersect); + + for (m = group->members; m; m = m->next) { + struct p2p_device *dev; + + dev = p2p_get_device(group->p2p, m->dev_addr); + if (!dev) + continue; + + p2p_channels_intersect(&intersect, &dev->channels, &res); + intersect = res; + } + + p2p_channels_dump(group->p2p, "Group common channels", &intersect); + + os_memset(common_freqs, 0, *num * sizeof(int)); + *num = p2p_channels_to_freqs(&intersect, common_freqs, *num); + + return 0; +} diff --git a/contrib/wpa/src/p2p/p2p_i.h b/contrib/wpa/src/p2p/p2p_i.h index 6af19ceda45..0ce4058fe3e 100644 --- a/contrib/wpa/src/p2p/p2p_i.h +++ b/contrib/wpa/src/p2p/p2p_i.h @@ -14,6 +14,12 @@ #define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1 +/* + * A threshold (in seconds) to prefer a direct Probe Response frame from a P2P + * Device over the P2P Client Info received from a GO. + */ +#define P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD 1 + enum p2p_role_indication; /* @@ -47,6 +53,9 @@ struct p2p_device { * from Beacon/Probe Response), the interface address is stored here. * p2p_device_addr must still be set in such a case to the unique * identifier for the P2P Device. + * + * This field is also used during P2PS PD to store the intended GO + * address of the peer. */ u8 interface_addr[ETH_ALEN]; @@ -71,7 +80,7 @@ struct p2p_device { char country[3]; struct p2p_channels channels; int oper_freq; - u8 oper_ssid[32]; + u8 oper_ssid[SSID_MAX_LEN]; size_t oper_ssid_len; /** @@ -107,6 +116,8 @@ struct p2p_device { #define P2P_DEV_WAIT_INV_REQ_ACK BIT(19) #define P2P_DEV_P2PS_REPORTED BIT(20) #define P2P_DEV_PD_PEER_P2PS BIT(21) +#define P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT BIT(22) + unsigned int flags; int status; /* enum p2p_status_code */ @@ -322,7 +333,7 @@ struct p2p_data { /** * ssid - Selected SSID for GO Negotiation (if local end will be GO) */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - ssid length in octets @@ -403,7 +414,7 @@ struct p2p_data { enum p2p_invite_role inv_role; u8 inv_bssid[ETH_ALEN]; int inv_bssid_set; - u8 inv_ssid[32]; + u8 inv_ssid[SSID_MAX_LEN]; size_t inv_ssid_len; u8 inv_sa[ETH_ALEN]; u8 inv_group_bssid[ETH_ALEN]; @@ -506,11 +517,9 @@ struct p2p_data { struct p2ps_advertisement *p2ps_adv_list; struct p2ps_provision *p2ps_prov; u8 wild_card_hash[P2PS_HASH_LEN]; - u8 query_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; - u8 query_count; u8 p2ps_seek; + u8 p2ps_seek_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; u8 p2ps_seek_count; - u8 p2ps_svc_found; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie_beacon; @@ -529,6 +538,9 @@ struct p2p_data { u16 authorized_oob_dev_pw_id; struct wpabuf **vendor_elem; + + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; + unsigned int num_pref_freq; }; /** @@ -578,7 +590,7 @@ struct p2p_message { const u8 *p2p_device_addr; const u8 *pri_dev_type; u8 num_sec_dev_types; - char device_name[33]; + char device_name[WPS_DEV_NAME_MAX_LEN + 1]; u16 config_methods; /* WPS IE */ @@ -631,6 +643,9 @@ struct p2p_message { const u8 *persistent_dev; const u8 *persistent_ssid; size_t persistent_ssid_len; + + const u8 *pref_freq_list; + size_t pref_freq_list_len; }; @@ -757,6 +772,8 @@ void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, const u8 *ssid, size_t ssid_len); int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr); +void p2p_buf_add_pref_channel_list(struct wpabuf *buf, + const u32 *preferred_freq_list, u32 size); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, @@ -786,6 +803,8 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); void p2p_reselect_channel(struct p2p_data *p2p, struct p2p_channels *intersection); +void p2p_check_pref_chan(struct p2p_data *p2p, int go, + struct p2p_device *dev, struct p2p_message *msg); /* p2p_pd.c */ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, @@ -795,6 +814,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join, int force_freq); void p2p_reset_pending_pd(struct p2p_data *p2p); +void p2ps_prov_free(struct p2p_data *p2p); /* p2p_invitation.c */ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, @@ -840,7 +860,9 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], size_t num_req_dev_type); -struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p); +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, + const u8 *query_hash, + u8 query_count); void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, diff --git a/contrib/wpa/src/p2p/p2p_invitation.c b/contrib/wpa/src/p2p/p2p_invitation.c index 558c6dd0c58..108e5b7f93e 100644 --- a/contrib/wpa/src/p2p/p2p_invitation.c +++ b/contrib/wpa/src/p2p/p2p_invitation.c @@ -85,6 +85,9 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); + p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, + p2p->num_pref_freq); + #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); @@ -134,6 +137,9 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -158,6 +164,9 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]); + return buf; } @@ -337,6 +346,12 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, p2p_reselect_channel(p2p, &intersection); } + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, go, dev, &msg); + op_freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (op_freq < 0) { @@ -387,7 +402,7 @@ fail: } else p2p->inv_group_bssid_ptr = NULL; if (msg.group_id) { - if (msg.group_id_len - ETH_ALEN <= 32) { + if (msg.group_id_len - ETH_ALEN <= SSID_MAX_LEN) { os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN); p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; @@ -528,6 +543,12 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, peer_oper_freq = 0; } + /* + * Use the driver preferred frequency list extension if + * supported. + */ + p2p_check_pref_chan(p2p, 0, dev, &msg); + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, msg.group_bssid, channels, sa, freq, peer_oper_freq); diff --git a/contrib/wpa/src/p2p/p2p_parse.c b/contrib/wpa/src/p2p/p2p_parse.c index fd6a4610d83..bd1e68bd424 100644 --- a/contrib/wpa/src/p2p/p2p_parse.c +++ b/contrib/wpa/src/p2p/p2p_parse.c @@ -149,7 +149,8 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, pos += 2; nlen = WPA_GET_BE16(pos); pos += 2; - if (data + len - pos < (int) nlen || nlen > 32) { + if (data + len - pos < (int) nlen || + nlen > WPS_DEV_NAME_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " "length %d (buf len %d)", (int) nlen, (int) (data + len - pos)); @@ -160,8 +161,7 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, for (i = 0; i < nlen; i++) { if (msg->device_name[i] == '\0') break; - if (msg->device_name[i] > 0 && - msg->device_name[i] < 32) + if (is_ctrl_char(msg->device_name[i])) msg->device_name[i] = '_'; } wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR @@ -203,7 +203,7 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, MAC2STR(msg->group_bssid)); break; case P2P_ATTR_GROUP_ID: - if (len < ETH_ALEN || len > ETH_ALEN + 32) { + if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID " "attribute length %d", len); return -1; @@ -371,9 +371,9 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, break; case P2P_ATTR_PERSISTENT_GROUP: { - if (len < ETH_ALEN) { + if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, - "P2P: Too short Persistent Group Info (length %u)", + "P2P: Invalid Persistent Group Info (length %u)", len); return -1; } @@ -516,7 +516,7 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) struct ieee802_11_elems elems; ieee802_11_parse_elems(data, len, &elems, 0); - if (elems.ds_params && elems.ds_params_len >= 1) + if (elems.ds_params) msg->ds_params = elems.ds_params; if (elems.ssid) msg->ssid = elems.ssid - 2; @@ -548,6 +548,9 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) } #endif /* CONFIG_WIFI_DISPLAY */ + msg->pref_freq_list = elems.pref_freq_list; + msg->pref_freq_list_len = elems.pref_freq_list_len; + return 0; } @@ -674,8 +677,8 @@ int p2p_group_info_parse(const u8 *gi, size_t gi_len, t += 2; if (count > cend - t) return -1; /* invalid Device Name TLV */ - if (count >= 32) - count = 32; + if (count >= WPS_DEV_NAME_MAX_LEN) + count = WPS_DEV_NAME_MAX_LEN; cli->dev_name = (const char *) t; cli->dev_name_len = count; @@ -703,7 +706,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, for (i = 0; i < info.num_clients; i++) { struct p2p_client_info *cli; - char name[33]; + char name[WPS_DEV_NAME_MAX_LEN + 1]; char devtype[WPS_DEV_TYPE_BUFSIZE]; u8 s; int count; @@ -742,7 +745,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, name[cli->dev_name_len] = '\0'; count = (int) cli->dev_name_len - 1; while (count >= 0) { - if (name[count] > 0 && name[count] < 32) + if (is_ctrl_char(name[count])) name[count] = '_'; count--; } diff --git a/contrib/wpa/src/p2p/p2p_pd.c b/contrib/wpa/src/p2p/p2p_pd.c index 328b1e029ce..89009455182 100644 --- a/contrib/wpa/src/p2p/p2p_pd.c +++ b/contrib/wpa/src/p2p/p2p_pd.c @@ -44,7 +44,7 @@ static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) { int found; u8 intended_addr[ETH_ALEN]; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int group_iface; @@ -57,7 +57,11 @@ static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) if (found) { p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, ssid, ssid_len); - p2p_buf_add_intended_addr(buf, intended_addr); + + if (group_iface) + p2p_buf_add_intended_addr(buf, p2p->intended_addr); + else + p2p_buf_add_intended_addr(buf, intended_addr); } else { if (!p2p->ssid_set) { p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); @@ -82,11 +86,12 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, struct wpabuf *buf, u16 config_methods) { struct p2ps_provision *prov = p2p->p2ps_prov; - u8 feat_cap_mask[] = { 1, 0 }; + struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 }; int shared_group = 0; - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; + u8 intended_addr[ETH_ALEN]; /* If we might be explicite group owner, add GO details */ if (prov->conncap & (P2PS_SETUP_GROUP_OWNER | @@ -101,7 +106,7 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, if (p2p->cfg->get_persistent_group) { shared_group = p2p->cfg->get_persistent_group( p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0, - go_dev_addr, ssid, &ssid_len); + go_dev_addr, ssid, &ssid_len, intended_addr); } /* Add Operating Channel if conncap includes GO */ @@ -146,12 +151,17 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); - p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), - feat_cap_mask); + p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap); - if (shared_group) + if (shared_group) { p2p_buf_add_persistent_group_info(buf, go_dev_addr, ssid, ssid_len); + /* Add intended interface address if it is not added yet */ + if ((prov->conncap == P2PS_SETUP_NONE || + prov->conncap == P2PS_SETUP_CLIENT) && + !is_zero_ether_addr(intended_addr)) + p2p_buf_add_intended_addr(buf, intended_addr); + } } @@ -232,7 +242,9 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, const u8 *group_id, size_t group_id_len, const u8 *persist_ssid, - size_t persist_ssid_len) + size_t persist_ssid_len, + const u8 *fcap, + u16 fcap_len) { struct wpabuf *buf; size_t extra = 0; @@ -270,7 +282,6 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, /* Add P2P IE for P2PS */ if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) { - u8 feat_cap_mask[] = { 1, 0 }; u8 *len = p2p_buf_add_ie_hdr(buf); struct p2ps_provision *prov = p2p->p2ps_prov; u8 group_capab; @@ -293,18 +304,23 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, if (persist_ssid && p2p->cfg->get_persistent_group && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED)) { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; + u8 intended_addr[ETH_ALEN]; persist = p2p->cfg->get_persistent_group( p2p->cfg->cb_ctx, dev->info.p2p_device_addr, persist_ssid, persist_ssid_len, go_dev_addr, - ssid, &ssid_len); - if (persist) + ssid, &ssid_len, intended_addr); + if (persist) { p2p_buf_add_persistent_group_info( buf, go_dev_addr, ssid, ssid_len); + if (!is_zero_ether_addr(intended_addr)) + p2p_buf_add_intended_addr( + buf, intended_addr); + } } if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) @@ -344,8 +360,7 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); - p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), - feat_cap_mask); + p2p_buf_add_feature_capability(buf, fcap_len, fcap); p2p_buf_update_ie_hdr(buf, len); } else if (status != P2P_SC_SUCCESS || adv_id) { u8 *len = p2p_buf_add_ie_hdr(buf); @@ -400,6 +415,18 @@ static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id, } +static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask) +{ + int i; + + for (i = 0; cpt_priority[i]; i++) + if (req_cpt_mask & cpt_priority[i]) + return cpt_priority[i]; + + return 0; +} + + void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -415,9 +442,12 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, u32 session_id = 0; u8 session_mac[ETH_ALEN]; u8 adv_mac[ETH_ALEN]; - u8 group_mac[ETH_ALEN]; + const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; u16 config_methods; + u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + struct p2ps_feature_capab resp_fcap = { 0, 0 }; + struct p2ps_feature_capab *req_fcap; if (p2p_parse(data, len, &msg)) return; @@ -425,6 +455,7 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR " with config methods 0x%x (freq=%d)", MAC2STR(sa), msg.wps_config_methods, rx_freq); + group_mac = msg.intended_addr; dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { @@ -441,9 +472,12 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } - if (!(msg.wps_config_methods & - (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | - WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) { + if (msg.adv_id) + allowed_config_methods |= WPS_CONFIG_P2PS; + else + allowed_config_methods |= WPS_CONFIG_PUSHBUTTON; + + if (!(msg.wps_config_methods & allowed_config_methods)) { p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); goto out; } @@ -501,14 +535,23 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, os_memset(session_mac, 0, ETH_ALEN); os_memset(adv_mac, 0, ETH_ALEN); - os_memset(group_mac, 0, ETH_ALEN); + /* Note 1: A feature capability attribute structure can be changed + * in the future. The assumption is that such modifications are + * backwards compatible, therefore we allow processing of + * msg.feature_cap exceeding the size of the p2ps_feature_capab + * structure. + * Note 2: Vverification of msg.feature_cap_len below has to be changed + * to allow 2 byte feature capability processing if struct + * p2ps_feature_capab is extended to include additional fields and it + * affects the structure size. + */ if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac && + msg.feature_cap && msg.feature_cap_len >= sizeof(*req_fcap) && (msg.status || msg.conn_cap)) { u8 remote_conncap; - if (msg.intended_addr) - os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); + req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; os_memcpy(session_mac, msg.session_mac, ETH_ALEN); os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); @@ -533,9 +576,22 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", auto_accept, remote_conncap, conncap); - if (p2ps_adv->config_methods && - !(msg.wps_config_methods & - p2ps_adv->config_methods)) { + resp_fcap.cpt = + p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, + req_fcap->cpt); + + p2p_dbg(p2p, + "cpt: service:0x%x remote:0x%x result:0x%x", + p2ps_adv->cpt_mask, req_fcap->cpt, + resp_fcap.cpt); + + if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (p2ps_adv->config_methods && + !(msg.wps_config_methods & + p2ps_adv->config_methods)) { p2p_dbg(p2p, "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", p2ps_adv->config_methods, @@ -624,6 +680,15 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, p2p->p2ps_prov->conncap, remote_conncap, conncap); + resp_fcap.cpt = p2ps_own_preferred_cpt( + p2p->p2ps_prov->cpt_priority, + req_fcap->cpt); + + p2p_dbg(p2p, + "cpt: local:0x%x remote:0x%x result:0x%x", + p2p->p2ps_prov->cpt_mask, + req_fcap->cpt, resp_fcap.cpt); + /* * Ensure that if we asked for PIN originally, * our method is consistent with original @@ -634,14 +699,22 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, else if (method & WPS_CONFIG_KEYPAD) method = WPS_CONFIG_DISPLAY; - /* Reject this "Deferred Accept* if incompatible - * conncap or method */ if (!conncap || - !(msg.wps_config_methods & method)) + !(msg.wps_config_methods & method)) { + /* + * Reject this "Deferred Accept* + * if incompatible conncap or method + */ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - else + } else if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = + P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else { reject = P2P_SC_SUCCESS; + } p2p->p2ps_prov->status = reject; p2p->p2ps_prov->conncap = conncap; @@ -659,7 +732,9 @@ out: config_methods, adv_id, msg.group_id, msg.group_id_len, msg.persistent_ssid, - msg.persistent_ssid_len); + msg.persistent_ssid_len, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); if (resp == NULL) { p2p_parse_free(&msg); return; @@ -696,7 +771,7 @@ out: NULL, adv_id, session_id, 0, 0, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, NULL, 0); } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { p2p->p2ps_prov->status = reject; @@ -709,7 +784,7 @@ out: session_id, conncap, 0, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL); + 0, NULL, NULL, 0); else p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, @@ -719,7 +794,9 @@ out: passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL); + 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (msg.status && p2p->p2ps_prov) { p2p->p2ps_prov->status = P2P_SC_SUCCESS; p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa, @@ -728,7 +805,9 @@ out: passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (msg.status) { } else if (auto_accept && reject == P2P_SC_SUCCESS) { p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, @@ -737,7 +816,9 @@ out: conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL); + 0, 0, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && (!msg.session_info || !msg.session_info_len)) { p2p->p2ps_prov->method = msg.wps_config_methods; @@ -748,7 +829,9 @@ out: conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 1, NULL); + 0, 1, NULL, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { size_t buf_len = msg.session_info_len; char *buf = os_malloc(2 * buf_len + 1); @@ -764,14 +847,45 @@ out: adv_mac, session_mac, group_mac, adv_id, session_id, conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 1, buf); + 0, 1, buf, + (const u8 *) &resp_fcap, sizeof(resp_fcap)); os_free(buf); } } - if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) { + /* + * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN, + * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. + * Call it either on legacy P2P PD or on P2PS PD only if we need to + * enter/show PIN. + * + * The callback is called in the following cases: + * 1. Legacy P2P PD request, response status SUCCESS + * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE, + * response status: SUCCESS + * 3. P2PS advertiser, method DISPLAY, autoaccept: FALSE, + * response status: INFO_CURRENTLY_UNAVAILABLE + * 4. P2PS advertiser, method: KEYPAD, autoaccept==any, + * response status: INFO_CURRENTLY_UNAVAILABLE + * 5. P2PS follow-on with SUCCESS_DEFERRED, + * advertiser role: DISPLAY, autoaccept: FALSE, + * seeker: KEYPAD, response status: SUCCESS + */ + if (p2p->cfg->prov_disc_req && + ((reject == P2P_SC_SUCCESS && !msg.adv_id) || + (!msg.status && + (reject == P2P_SC_SUCCESS || + reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) && + passwd_id == DEV_PW_USER_SPECIFIED) || + (!msg.status && + reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || + (reject == P2P_SC_SUCCESS && + msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) { const u8 *dev_addr = sa; + if (msg.p2p_device_addr) dev_addr = msg.p2p_device_addr; p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, @@ -783,10 +897,133 @@ out: 0, msg.group_id, msg.group_id_len); } + + if (dev && reject == P2P_SC_SUCCESS) { + switch (config_methods) { + case WPS_CONFIG_DISPLAY: + dev->wps_prov_info = WPS_CONFIG_KEYPAD; + break; + case WPS_CONFIG_KEYPAD: + dev->wps_prov_info = WPS_CONFIG_DISPLAY; + break; + case WPS_CONFIG_PUSHBUTTON: + dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON; + break; + case WPS_CONFIG_P2PS: + dev->wps_prov_info = WPS_CONFIG_P2PS; + break; + default: + dev->wps_prov_info = 0; + break; + } + + if (msg.intended_addr) + os_memcpy(dev->interface_addr, msg.intended_addr, + ETH_ALEN); + } p2p_parse_free(&msg); } +static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p, + struct p2p_message *msg) +{ + u8 conn_cap_go = 0; + u8 conn_cap_cli = 0; + u32 session_id; + u32 adv_id; + +#define P2PS_PD_RESP_CHECK(_val, _attr) \ + do { \ + if ((_val) && !msg->_attr) { \ + p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \ + return -1; \ + } \ + } while (0) + + P2PS_PD_RESP_CHECK(1, status); + P2PS_PD_RESP_CHECK(1, adv_id); + P2PS_PD_RESP_CHECK(1, adv_mac); + P2PS_PD_RESP_CHECK(1, capability); + P2PS_PD_RESP_CHECK(1, p2p_device_info); + P2PS_PD_RESP_CHECK(1, session_id); + P2PS_PD_RESP_CHECK(1, session_mac); + P2PS_PD_RESP_CHECK(1, feature_cap); + + session_id = WPA_GET_LE32(msg->session_id); + adv_id = WPA_GET_LE32(msg->adv_id); + + if (p2p->p2ps_prov->session_id != session_id) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Session ID"); + return -1; + } + + if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac, + ETH_ALEN)) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Session MAC"); + return -1; + } + + if (p2p->p2ps_prov->adv_id != adv_id) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Advertisement ID"); + return -1; + } + + if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) { + p2p_dbg(p2p, + "Ignore PD Response with unexpected Advertisement MAC"); + return -1; + } + + if (msg->listen_channel) { + p2p_dbg(p2p, + "Ignore malformed PD Response - unexpected Listen Channel"); + return -1; + } + + if (*msg->status == P2P_SC_SUCCESS && + !(!!msg->conn_cap ^ !!msg->persistent_dev)) { + p2p_dbg(p2p, + "Ignore malformed PD Response - either conn_cap or persistent group should be present"); + return -1; + } + + if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, + "Ignore malformed PD Response - persistent group is present, but the status isn't success"); + return -1; + } + + if (msg->conn_cap) { + conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER; + conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT; + } + + P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, + channel_list); + P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, + config_timeout); + + P2PS_PD_RESP_CHECK(conn_cap_go, group_id); + P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr); + P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel); + /* + * TODO: Also validate that operating channel is present if the device + * is a GO in a persistent group. We can't do it here since we don't + * know what is the role of the peer. It should be probably done in + * p2ps_prov_complete callback, but currently operating channel isn't + * passed to it. + */ + +#undef P2PS_PD_RESP_CHECK + + return 0; +} + + void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len) { @@ -794,24 +1031,26 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, struct p2p_device *dev; u16 report_config_methods = 0, req_config_methods; u8 status = P2P_SC_SUCCESS; - int success = 0; u32 adv_id = 0; u8 conncap = P2PS_SETUP_NEW; u8 adv_mac[ETH_ALEN]; - u8 group_mac[ETH_ALEN]; + const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; + int p2ps_seeker; if (p2p_parse(data, len, &msg)) return; + if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) { + p2p_parse_free(&msg); + return; + } + /* Parse the P2PS members present */ if (msg.status) status = *msg.status; - if (msg.intended_addr) - os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); - else - os_memset(group_mac, 0, ETH_ALEN); + group_mac = msg.intended_addr; if (msg.adv_mac) os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); @@ -859,6 +1098,8 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->pending_action_state = P2P_NO_PENDING_ACTION; } + p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker; + /* * Use a local copy of the requested config methods since * p2p_reset_pending_pd() can clear this in the peer entry. @@ -881,8 +1122,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, P2P_PROV_DISC_REJECTED, adv_id, adv_mac, NULL); p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } @@ -909,7 +1149,6 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, } if ((msg.conn_cap || msg.persistent_dev) && - msg.adv_id && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && p2p->p2ps_prov) { if (p2p->cfg->p2ps_prov_complete) { @@ -918,23 +1157,20 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, conncap, passwd_id, msg.persistent_ssid, - msg.persistent_ssid_len, 1, 0, NULL); + msg.persistent_ssid_len, 1, 0, NULL, + msg.feature_cap, msg.feature_cap_len); } - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; - } - - if (status != P2P_SC_SUCCESS && - status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && - status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { + p2ps_prov_free(p2p); + } else if (status != P2P_SC_SUCCESS && + status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { if (p2p->cfg->p2ps_prov_complete) p2p->cfg->p2ps_prov_complete( p2p->cfg->cb_ctx, status, sa, adv_mac, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, - 0, 0, NULL, 0, 1, 0, NULL); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + 0, 0, NULL, 0, 1, 0, NULL, NULL, 0); + p2ps_prov_free(p2p); } if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { @@ -950,8 +1186,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, if (!deferred_sess_resp) { p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } utf8_escape((char *) msg.session_info, info_len, @@ -970,24 +1205,23 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->cfg->cb_ctx, sa, P2P_PROV_DISC_INFO_UNAVAILABLE, adv_id, adv_mac, NULL); - } else if (msg.wps_config_methods != dev->req_config_methods || - status != P2P_SC_SUCCESS) { + } else if (status != P2P_SC_SUCCESS) { p2p_dbg(p2p, "Peer rejected our Provision Discovery Request"); if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, - P2P_PROV_DISC_REJECTED, 0, - NULL, NULL); + P2P_PROV_DISC_REJECTED, + adv_id, adv_mac, NULL); p2p_parse_free(&msg); - os_free(p2p->p2ps_prov); - p2p->p2ps_prov = NULL; + p2ps_prov_free(p2p); goto out; } /* Store the provisioning info */ dev->wps_prov_info = msg.wps_config_methods; + if (msg.intended_addr) + os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN); p2p_parse_free(&msg); - success = 1; out: dev->req_config_methods = 0; @@ -999,7 +1233,28 @@ out: p2p_connect_send(p2p, dev); return; } - if (success && p2p->cfg->prov_disc_resp) + + /* + * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN, + * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. + * Call it only for a legacy P2P PD or for P2PS PD scenarios where + * show/enter PIN events are needed. + * + * The callback is called in the following cases: + * 1. Legacy P2P PD response with a status SUCCESS + * 2. P2PS, advertiser method: DISPLAY, autoaccept: true, + * response status: SUCCESS, local method KEYPAD + * 3. P2PS, advertiser method: KEYPAD,Seeker side, + * response status: INFO_CURRENTLY_UNAVAILABLE, + * local method: DISPLAY + */ + if (p2p->cfg->prov_disc_resp && + ((status == P2P_SC_SUCCESS && !adv_id) || + (p2ps_seeker && status == P2P_SC_SUCCESS && + passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || + (p2ps_seeker && + status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + passwd_id == DEV_PW_USER_SPECIFIED))) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); @@ -1120,7 +1375,7 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, /* Reset provisioning info */ dev->wps_prov_info = 0; - os_free(p2p->p2ps_prov); + p2ps_prov_free(p2p); p2p->p2ps_prov = p2ps_prov; dev->req_config_methods = config_methods; @@ -1176,3 +1431,10 @@ void p2p_reset_pending_pd(struct p2p_data *p2p) p2p->pd_retries = 0; p2p->pd_force_freq = 0; } + + +void p2ps_prov_free(struct p2p_data *p2p) +{ + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; +} diff --git a/contrib/wpa/src/p2p/p2p_utils.c b/contrib/wpa/src/p2p/p2p_utils.c index f32751d79ca..2e2aa8ad06f 100644 --- a/contrib/wpa/src/p2p/p2p_utils.c +++ b/contrib/wpa/src/p2p/p2p_utils.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "common/defs.h" #include "common/ieee802_11_common.h" #include "p2p_i.h" @@ -67,50 +68,11 @@ int p2p_channel_to_freq(int op_class, int channel) */ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) { - /* TODO: more operating classes */ - if (freq >= 2412 && freq <= 2472) { - if ((freq - 2407) % 5) - return -1; + if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) == + NUM_HOSTAPD_MODES) + return -1; - *op_class = 81; /* 2.407 GHz, channels 1..13 */ - *channel = (freq - 2407) / 5; - return 0; - } - - if (freq == 2484) { - *op_class = 82; /* channel 14 */ - *channel = 14; - return 0; - } - - if (freq >= 5180 && freq <= 5240) { - if ((freq - 5000) % 5) - return -1; - - *op_class = 115; /* 5 GHz, channels 36..48 */ - *channel = (freq - 5000) / 5; - return 0; - } - - if (freq >= 5745 && freq <= 5805) { - if ((freq - 5000) % 5) - return -1; - - *op_class = 124; /* 5 GHz, channels 149..161 */ - *channel = (freq - 5000) / 5; - return 0; - } - - if (freq >= 58320 && freq <= 64800) { - if ((freq - 58320) % 2160) - return -1; - - *op_class = 180; /* 60 GHz, channels 1..4 */ - *channel = (freq - 56160) / 2160; - return 0; - } - - return -1; + return 0; } @@ -497,12 +459,22 @@ int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list, break; for (j = 0; j < c->channels; j++) { int freq; + unsigned int k; + if (idx + 1 == max_len) break; freq = p2p_channel_to_freq(c->reg_class, c->channel[j]); if (freq < 0) continue; + + for (k = 0; k < idx; k++) { + if (freq_list[k] == freq) + break; + } + + if (k < idx) + continue; freq_list[idx++] = freq; } } diff --git a/contrib/wpa/src/radius/radius.c b/contrib/wpa/src/radius/radius.c index 8d878a4bd07..1ebfd11f3b9 100644 --- a/contrib/wpa/src/radius/radius.c +++ b/contrib/wpa/src/radius/radius.c @@ -167,7 +167,7 @@ struct radius_attr_type { } data_type; }; -static struct radius_attr_type radius_attrs[] = +static const struct radius_attr_type radius_attrs[] = { { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, @@ -259,7 +259,7 @@ static struct radius_attr_type radius_attrs[] = #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) -static struct radius_attr_type *radius_get_attr_type(u8 type) +static const struct radius_attr_type *radius_get_attr_type(u8 type) { size_t i; @@ -274,7 +274,7 @@ static struct radius_attr_type *radius_get_attr_type(u8 type) static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) { - struct radius_attr_type *attr; + const struct radius_attr_type *attr; int len; unsigned char *pos; char buf[1000]; @@ -1225,7 +1225,7 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, } /* MS-MPPE-Recv-Key */ - buf = os_malloc(hlen + send_key_len + 16); + buf = os_malloc(hlen + recv_key_len + 16); if (buf == NULL) { return 0; } @@ -1425,7 +1425,7 @@ struct radius_tunnel_attrs { /** * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information * @msg: RADIUS message - * Returns: VLAN ID for the first tunnel configuration of -1 if none is found + * Returns: VLAN ID for the first tunnel configuration or 0 if none is found */ int radius_msg_get_vlanid(struct radius_msg *msg) { @@ -1488,7 +1488,7 @@ int radius_msg_get_vlanid(struct radius_msg *msg) return tun->vlanid; } - return -1; + return 0; } diff --git a/contrib/wpa/src/radius/radius_das.c b/contrib/wpa/src/radius/radius_das.c index 39ceea879ca..b7d991bbd09 100644 --- a/contrib/wpa/src/radius/radius_das.c +++ b/contrib/wpa/src/radius/radius_das.c @@ -245,7 +245,7 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) (u8 *) &val, 4); if (res == 4) { u32 timestamp = ntohl(val); - if ((unsigned int) abs(now.sec - timestamp) > + if ((unsigned int) abs((int) (now.sec - timestamp)) > das->time_window) { wpa_printf(MSG_DEBUG, "DAS: Unacceptable " "Event-Timestamp (%u; local time %u) in " diff --git a/contrib/wpa/src/radius/radius_server.c b/contrib/wpa/src/radius/radius_server.c index 85a485e91d9..744283c7dc9 100644 --- a/contrib/wpa/src/radius/radius_server.c +++ b/contrib/wpa/src/radius/radius_server.c @@ -35,7 +35,7 @@ */ #define RADIUS_MAX_MSG_LEN 3000 -static struct eapol_callbacks radius_server_eapol_cb; +static const struct eapol_callbacks radius_server_eapol_cb; struct radius_client; struct radius_server_data; @@ -265,6 +265,8 @@ struct radius_server_data { struct dl_list erp_keys; /* struct eap_server_erp_key */ + unsigned int tls_session_lifetime; + /** * wps - Wi-Fi Protected Setup context * @@ -688,6 +690,7 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.server_id = (const u8 *) data->server_id; eap_conf.server_id_len = os_strlen(data->server_id); eap_conf.erp = data->erp; + eap_conf.tls_session_lifetime = data->tls_session_lifetime; radius_server_testing_options(sess, &eap_conf); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); @@ -1711,8 +1714,10 @@ radius_server_init(struct radius_server_conf *conf) data->ipv6 = conf->ipv6; if (conf->pac_opaque_encr_key) { data->pac_opaque_encr_key = os_malloc(16); - os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key, - 16); + if (data->pac_opaque_encr_key) { + os_memcpy(data->pac_opaque_encr_key, + conf->pac_opaque_encr_key, 16); + } } if (conf->eap_fast_a_id) { data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); @@ -1743,6 +1748,7 @@ radius_server_init(struct radius_server_conf *conf) } data->erp = conf->erp; data->erp_domain = conf->erp_domain; + data->tls_session_lifetime = conf->tls_session_lifetime; if (conf->subscr_remediation_url) { data->subscr_remediation_url = @@ -2035,6 +2041,12 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, sess->remediation = user->remediation; sess->macacl = user->macacl; } + + if (ret) { + RADIUS_DEBUG("%s: User-Name not found from user database", + __func__); + } + return ret; } @@ -2095,7 +2107,7 @@ static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp) #endif /* CONFIG_ERP */ -static struct eapol_callbacks radius_server_eapol_cb = +static const struct eapol_callbacks radius_server_eapol_cb = { .get_eap_user = radius_server_get_eap_user, .get_eap_req_id_text = radius_server_get_eap_req_id_text, diff --git a/contrib/wpa/src/radius/radius_server.h b/contrib/wpa/src/radius/radius_server.h index ca4e38c12e9..7a25802c815 100644 --- a/contrib/wpa/src/radius/radius_server.h +++ b/contrib/wpa/src/radius/radius_server.h @@ -170,6 +170,8 @@ struct radius_server_conf { const char *erp_domain; + unsigned int tls_session_lifetime; + /** * wps - Wi-Fi Protected Setup context * diff --git a/contrib/wpa/src/rsn_supp/tdls.c b/contrib/wpa/src/rsn_supp/tdls.c index c1d7749191d..722c20a706f 100644 --- a/contrib/wpa/src/rsn_supp/tdls.c +++ b/contrib/wpa/src/rsn_supp/tdls.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "utils/os.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "crypto/aes_wrap.h" @@ -1577,9 +1578,7 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { - if (!kde->ht_capabilities || - kde->ht_capabilities_len < - sizeof(struct ieee80211_ht_capabilities) ) { + if (!kde->ht_capabilities) { wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities " "received"); return 0; @@ -1605,9 +1604,7 @@ static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde, struct wpa_tdls_peer *peer) { - if (!kde->vht_capabilities || - kde->vht_capabilities_len < - sizeof(struct ieee80211_vht_capabilities) ) { + if (!kde->vht_capabilities) { wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities " "received"); return 0; @@ -2863,14 +2860,14 @@ void wpa_tdls_disassoc(struct wpa_sm *sm) } -static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems) +static int wpa_tdls_prohibited(struct ieee802_11_elems *elems) { /* bit 38 - TDLS Prohibited */ return !!(elems->ext_capab[2 + 4] & 0x40); } -static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems) +static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems) { /* bit 39 - TDLS Channel Switch Prohibited */ return !!(elems->ext_capab[2 + 4] & 0x80); @@ -2879,12 +2876,13 @@ static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems) void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - struct wpa_eapol_ie_parse elems; + struct ieee802_11_elems elems; sm->tdls_prohibited = 0; sm->tdls_chan_switch_prohibited = 0; - if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + if (ies == NULL || + ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) return; @@ -2900,9 +2898,10 @@ void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - struct wpa_eapol_ie_parse elems; + struct ieee802_11_elems elems; - if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + if (ies == NULL || + ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) return; diff --git a/contrib/wpa/src/rsn_supp/wpa.c b/contrib/wpa/src/rsn_supp/wpa.c index 127e246ee7a..d397ff1605c 100644 --- a/contrib/wpa/src/rsn_supp/wpa.c +++ b/contrib/wpa/src/rsn_supp/wpa.c @@ -249,6 +249,17 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, "RSN: the new PMK matches with the " "PMKID"); abort_cached = 0; + } else if (sa && !sm->cur_pmksa && pmkid) { + /* + * It looks like the authentication server + * derived mismatching MSK. This should not + * really happen, but bugs happen.. There is not + * much we can do here without knowing what + * exactly caused the server to misbehave. + */ + wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); + return -1; } if (!sm->cur_pmksa) @@ -1281,8 +1292,8 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, &gd->key_rsc_len, &gd->alg)) return -1; - wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake", - ie.gtk, ie.gtk_len); + wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake", + ie.gtk, ie.gtk_len); gd->keyidx = ie.gtk[0] & 0x3; gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(ie.gtk[0] & BIT(2))); @@ -1333,6 +1344,11 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> WPA_KEY_INFO_KEY_INDEX_SHIFT; if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { +#ifdef CONFIG_NO_RC4 + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 not supported in the build"); + return -1; +#else /* CONFIG_NO_RC4 */ u8 ek[32]; if (key_data_len > sizeof(gd->gtk)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1350,6 +1366,7 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, return -1; } os_memset(ek, 0, sizeof(ek)); +#endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1564,6 +1581,11 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, /* Decrypt key data here so that this operation does not need * to be implemented separately for each message type. */ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { +#ifdef CONFIG_NO_RC4 + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 not supported in the build"); + return -1; +#else /* CONFIG_NO_RC4 */ u8 ek[32]; os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); @@ -1574,6 +1596,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, return -1; } os_memset(ek, 0, sizeof(ek)); +#endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || sm->key_mgmt == WPA_KEY_MGMT_OSEN || diff --git a/contrib/wpa/src/rsn_supp/wpa_ft.c b/contrib/wpa/src/rsn_supp/wpa_ft.c index 06dea0550f1..205793e7f43 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ft.c +++ b/contrib/wpa/src/rsn_supp/wpa_ft.c @@ -168,9 +168,7 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos = (u8 *) (rsnie + 1); /* Group Suite Selector */ - if (sm->group_cipher != WPA_CIPHER_CCMP && - sm->group_cipher != WPA_CIPHER_GCMP && - sm->group_cipher != WPA_CIPHER_TKIP) { + if (!wpa_cipher_valid_group(sm->group_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", sm->group_cipher); os_free(buf); diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.c b/contrib/wpa/src/rsn_supp/wpa_ie.c index cb334df675b..0c37b35c1ee 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.c +++ b/contrib/wpa/src/rsn_supp/wpa_ie.c @@ -30,6 +30,9 @@ int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, { if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC && + wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE) + return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); else return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); } @@ -508,12 +511,14 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, ie->rsn_ie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", ie->rsn_ie, ie->rsn_ie_len); - } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN && + pos[1] >= sizeof(struct rsn_mdie)) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key", ie->mdie, ie->mdie_len); - } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { + } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION && + pos[1] >= sizeof(struct rsn_ftie)) { ie->ftie = pos; ie->ftie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", @@ -548,15 +553,16 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { ie->ext_supp_rates = pos; ie->ext_supp_rates_len = pos[1] + 2; - } else if (*pos == WLAN_EID_HT_CAP) { + } else if (*pos == WLAN_EID_HT_CAP && + pos[1] >= sizeof(struct ieee80211_ht_capabilities)) { ie->ht_capabilities = pos + 2; - ie->ht_capabilities_len = pos[1]; } else if (*pos == WLAN_EID_VHT_AID) { if (pos[1] >= 2) ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff; - } else if (*pos == WLAN_EID_VHT_CAP) { + } else if (*pos == WLAN_EID_VHT_CAP && + pos[1] >= sizeof(struct ieee80211_vht_capabilities)) + { ie->vht_capabilities = pos + 2; - ie->vht_capabilities_len = pos[1]; } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { ie->qosinfo = pos[2]; } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.h b/contrib/wpa/src/rsn_supp/wpa_ie.h index 0fc42cc492a..fe95af0abc5 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.h +++ b/contrib/wpa/src/rsn_supp/wpa_ie.h @@ -50,9 +50,7 @@ struct wpa_eapol_ie_parse { const u8 *ext_supp_rates; size_t ext_supp_rates_len; const u8 *ht_capabilities; - size_t ht_capabilities_len; const u8 *vht_capabilities; - size_t vht_capabilities_len; const u8 *supp_channels; size_t supp_channels_len; const u8 *supp_oper_classes; diff --git a/contrib/wpa/src/tls/libtommath.c b/contrib/wpa/src/tls/libtommath.c index 3fb8fbed25e..8bc824f20dc 100644 --- a/contrib/wpa/src/tls/libtommath.c +++ b/contrib/wpa/src/tls/libtommath.c @@ -1472,8 +1472,7 @@ static int mp_init_multi(mp_int *mp, ...) cur_arg = va_arg(clean_args, mp_int*); } va_end(clean_args); - res = MP_MEM; - break; + return MP_MEM; } n++; cur_arg = va_arg(args, mp_int*); @@ -1631,7 +1630,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) } /* init our temps */ - if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { return res; } diff --git a/contrib/wpa/src/tls/tlsv1_client.c b/contrib/wpa/src/tls/tlsv1_client.c index facdd659173..a6f0587e34c 100644 --- a/contrib/wpa/src/tls/tlsv1_client.c +++ b/contrib/wpa/src/tls/tlsv1_client.c @@ -714,12 +714,12 @@ int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, /** - * tlsv1_client_get_keys - Get master key and random data from TLS connection + * tlsv1_client_get_random - Get random data from TLS connection * @conn: TLSv1 client connection data from tlsv1_client_init() - * @keys: Structure of key/random data (filled on success) + * @keys: Structure of random data (filled on success) * Returns: 0 on success, -1 on failure */ -int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) +int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *keys) { os_memset(keys, 0, sizeof(*keys)); if (conn->state == CLIENT_HELLO) @@ -731,8 +731,6 @@ int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) if (conn->state != SERVER_HELLO) { keys->server_random = conn->server_random; keys->server_random_len = TLS_RANDOM_LEN; - keys->master_key = conn->master_secret; - keys->master_key_len = TLS_MASTER_SECRET_LEN; } return 0; diff --git a/contrib/wpa/src/tls/tlsv1_client.h b/contrib/wpa/src/tls/tlsv1_client.h index 8ec85f1a919..a4e25e96993 100644 --- a/contrib/wpa/src/tls/tlsv1_client.h +++ b/contrib/wpa/src/tls/tlsv1_client.h @@ -36,7 +36,7 @@ int tlsv1_client_shutdown(struct tlsv1_client *conn); int tlsv1_client_resumed(struct tlsv1_client *conn); int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, const u8 *data, size_t data_len); -int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys); +int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *data); int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); int tlsv1_client_set_cred(struct tlsv1_client *conn, diff --git a/contrib/wpa/src/tls/tlsv1_server.c b/contrib/wpa/src/tls/tlsv1_server.c index 93ae4888d89..ba47337bcbb 100644 --- a/contrib/wpa/src/tls/tlsv1_server.c +++ b/contrib/wpa/src/tls/tlsv1_server.c @@ -610,12 +610,12 @@ int tlsv1_server_resumed(struct tlsv1_server *conn) /** - * tlsv1_server_get_keys - Get master key and random data from TLS connection + * tlsv1_server_get_random - Get random data from TLS connection * @conn: TLSv1 server connection data from tlsv1_server_init() - * @keys: Structure of key/random data (filled on success) + * @keys: Structure of random data (filled on success) * Returns: 0 on success, -1 on failure */ -int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys) +int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *keys) { os_memset(keys, 0, sizeof(*keys)); if (conn->state == CLIENT_HELLO) @@ -627,8 +627,6 @@ int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys) if (conn->state != SERVER_HELLO) { keys->server_random = conn->server_random; keys->server_random_len = TLS_RANDOM_LEN; - keys->master_key = conn->master_secret; - keys->master_key_len = TLS_MASTER_SECRET_LEN; } return 0; diff --git a/contrib/wpa/src/tls/tlsv1_server.h b/contrib/wpa/src/tls/tlsv1_server.h index b2b28d1e121..10e7699312b 100644 --- a/contrib/wpa/src/tls/tlsv1_server.h +++ b/contrib/wpa/src/tls/tlsv1_server.h @@ -32,7 +32,7 @@ int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, size_t buflen); int tlsv1_server_shutdown(struct tlsv1_server *conn); int tlsv1_server_resumed(struct tlsv1_server *conn); -int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys); +int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *data); int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn); int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers); int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer); diff --git a/contrib/wpa/src/tls/x509v3.c b/contrib/wpa/src/tls/x509v3.c index 742af328cf8..b51dfcd4473 100644 --- a/contrib/wpa/src/tls/x509v3.c +++ b/contrib/wpa/src/tls/x509v3.c @@ -1511,7 +1511,7 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) if (pos + hdr.length < end) { wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " "encoded certificate", - pos + hdr.length, end - pos + hdr.length); + pos + hdr.length, end - (pos + hdr.length)); end = pos + hdr.length; } diff --git a/contrib/wpa/src/utils/browser-wpadebug.c b/contrib/wpa/src/utils/browser-wpadebug.c index 5fc40fac610..59ba4d1e02d 100644 --- a/contrib/wpa/src/utils/browser-wpadebug.c +++ b/contrib/wpa/src/utils/browser-wpadebug.c @@ -96,7 +96,7 @@ int hs20_web_browser(const char *url) if (pid == 0) { /* run the external command in the child process */ - char *argv[12]; + char *argv[14]; argv[0] = "browser-wpadebug"; argv[1] = "start"; @@ -109,7 +109,9 @@ int hs20_web_browser(const char *url) argv[8] = "-e"; argv[9] = "w1.fi.wpadebug.URL"; argv[10] = (void *) url; - argv[11] = NULL; + argv[11] = "--user"; + argv[12] = "-3"; /* USER_CURRENT_OR_SELF */ + argv[13] = NULL; execv("/system/bin/am", argv); wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); diff --git a/contrib/wpa/src/utils/common.c b/contrib/wpa/src/utils/common.c index 5fd795f3f30..660e9fc985d 100644 --- a/contrib/wpa/src/utils/common.c +++ b/contrib/wpa/src/utils/common.c @@ -8,6 +8,7 @@ #include "includes.h" +#include "common/ieee802_11_defs.h" #include "common.h" @@ -277,6 +278,31 @@ int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...) return ret; } + +int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len, + char sep) +{ + size_t i; + char *pos = buf, *end = buf + buf_size; + int ret; + + if (buf_size == 0) + return 0; + + for (i = 0; i < len; i++) { + ret = os_snprintf(pos, end - pos, "%02x%c", + data[i], sep); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return pos - buf; + } + pos += ret; + } + pos[-1] = '\0'; + return pos - buf; +} + + static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len, int uppercase) { @@ -584,7 +610,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str) */ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) { - static char ssid_txt[32 * 4 + 1]; + static char ssid_txt[SSID_MAX_LEN * 4 + 1]; if (ssid == NULL) { ssid_txt[0] = '\0'; @@ -946,6 +972,48 @@ int random_mac_addr_keep_oui(u8 *addr) } +/** + * cstr_token - Get next token from const char string + * @str: a constant string to tokenize + * @delim: a string of delimiters + * @last: a pointer to a character following the returned token + * It has to be set to NULL for the first call and passed for any + * futher call. + * Returns: a pointer to token position in str or NULL + * + * This function is similar to str_token, but it can be used with both + * char and const char strings. Differences: + * - The str buffer remains unmodified + * - The returned token is not a NULL terminated string, but a token + * position in str buffer. If a return value is not NULL a size + * of the returned token could be calculated as (last - token). + */ +const char * cstr_token(const char *str, const char *delim, const char **last) +{ + const char *end, *token = str; + + if (!str || !delim || !last) + return NULL; + + if (*last) + token = *last; + + while (*token && os_strchr(delim, *token)) + token++; + + if (!*token) + return NULL; + + end = token + 1; + + while (*end && !os_strchr(delim, *end)) + end++; + + *last = end; + return token; +} + + /** * str_token - Get next token from a string * @buf: String to tokenize. Note that the string might be modified. @@ -956,25 +1024,12 @@ int random_mac_addr_keep_oui(u8 *addr) */ char * str_token(char *str, const char *delim, char **context) { - char *end, *pos = str; + char *token = (char *) cstr_token(str, delim, (const char **) context); - if (*context) - pos = *context; + if (token && **context) + *(*context)++ = '\0'; - while (*pos && os_strchr(delim, *pos)) - pos++; - if (!*pos) - return NULL; - - end = pos + 1; - while (*end && !os_strchr(delim, *end)) - end++; - - if (*end) - *end++ = '\0'; - - *context = end; - return pos; + return token; } @@ -1062,3 +1117,9 @@ size_t utf8_escape(const char *inp, size_t in_size, return res_size; } + + +int is_ctrl_char(char c) +{ + return c > 0 && c < 32; +} diff --git a/contrib/wpa/src/utils/common.h b/contrib/wpa/src/utils/common.h index 576e8e7e2d4..0b9cc3d8820 100644 --- a/contrib/wpa/src/utils/common.h +++ b/contrib/wpa/src/utils/common.h @@ -53,16 +53,6 @@ static inline unsigned int bswap_32(unsigned int v) } #endif /* __APPLE__ */ -#ifdef CONFIG_TI_COMPILER -#define __BIG_ENDIAN 4321 -#define __LITTLE_ENDIAN 1234 -#ifdef __big_endian__ -#define __BYTE_ORDER __BIG_ENDIAN -#else -#define __BYTE_ORDER __LITTLE_ENDIAN -#endif -#endif /* CONFIG_TI_COMPILER */ - #ifdef CONFIG_NATIVE_WINDOWS #include @@ -110,22 +100,6 @@ typedef INT8 s8; #define WPA_TYPES_DEFINED #endif /* __vxworks */ -#ifdef CONFIG_TI_COMPILER -#ifdef _LLONG_AVAILABLE -typedef unsigned long long u64; -#else -/* - * TODO: 64-bit variable not available. Using long as a workaround to test the - * build, but this will likely not work for all operations. - */ -typedef unsigned long u64; -#endif -typedef unsigned int u32; -typedef unsigned short u16; -typedef unsigned char u8; -#define WPA_TYPES_DEFINED -#endif /* CONFIG_TI_COMPILER */ - #ifndef WPA_TYPES_DEFINED #ifdef CONFIG_USE_INTTYPES_H #include @@ -262,7 +236,7 @@ static inline void WPA_PUT_BE24(u8 *a, u32 val) static inline u32 WPA_GET_BE32(const u8 *a) { - return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; + return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; } static inline void WPA_PUT_BE32(u8 *a, u32 val) @@ -275,7 +249,7 @@ static inline void WPA_PUT_BE32(u8 *a, u32 val) static inline u32 WPA_GET_LE32(const u8 *a) { - return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; + return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; } static inline void WPA_PUT_LE32(u8 *a, u32 val) @@ -433,7 +407,7 @@ void perror(const char *s); #endif #ifndef BIT -#define BIT(x) (1 << (x)) +#define BIT(x) (1U << (x)) #endif /* @@ -480,6 +454,8 @@ int hexstr2bin(const char *hex, u8 *buf, size_t len); void inc_byte_array(u8 *counter, size_t len); void wpa_get_ntp_timestamp(u8 *buf); int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...); +int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len, + char sep); int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, size_t len); @@ -516,6 +492,11 @@ static inline int is_broadcast_ether_addr(const u8 *a) return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff; } +static inline int is_multicast_ether_addr(const u8 *a) +{ + return a[0] & 0x01; +} + #define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff" #include "wpa_debug.h" @@ -547,11 +528,13 @@ void bin_clear_free(void *bin, size_t len); int random_mac_addr(u8 *addr); int random_mac_addr_keep_oui(u8 *addr); +const char * cstr_token(const char *str, const char *delim, const char **last); char * str_token(char *str, const char *delim, char **context); size_t utf8_escape(const char *inp, size_t in_size, char *outp, size_t out_size); size_t utf8_unescape(const char *inp, size_t in_size, char *outp, size_t out_size); +int is_ctrl_char(char c); /* diff --git a/contrib/wpa/src/utils/eloop.c b/contrib/wpa/src/utils/eloop.c index 4a565ebdbd0..8647229b8eb 100644 --- a/contrib/wpa/src/utils/eloop.c +++ b/contrib/wpa/src/utils/eloop.c @@ -61,11 +61,8 @@ struct eloop_signal { struct eloop_sock_table { int count; struct eloop_sock *table; -#ifdef CONFIG_ELOOP_EPOLL eloop_event_type type; -#else /* CONFIG_ELOOP_EPOLL */ int changed; -#endif /* CONFIG_ELOOP_EPOLL */ }; struct eloop_data { @@ -256,9 +253,7 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, table->table = tmp; eloop.max_sock = new_max_sock; eloop.count++; -#ifndef CONFIG_ELOOP_EPOLL table->changed = 1; -#endif /* CONFIG_ELOOP_EPOLL */ eloop_trace_sock_add_ref(table); #ifdef CONFIG_ELOOP_EPOLL @@ -314,9 +309,7 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, } table->count--; eloop.count--; -#ifndef CONFIG_ELOOP_EPOLL table->changed = 1; -#endif /* CONFIG_ELOOP_EPOLL */ eloop_trace_sock_add_ref(table); #ifdef CONFIG_ELOOP_EPOLL if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) { @@ -523,6 +516,10 @@ static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds) continue; table->handler(table->sock, table->eloop_data, table->user_data); + if (eloop.readers.changed || + eloop.writers.changed || + eloop.exceptions.changed) + break; } } #endif /* CONFIG_ELOOP_EPOLL */ @@ -923,6 +920,20 @@ void eloop_run(void) (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || eloop.writers.count > 0 || eloop.exceptions.count > 0)) { struct eloop_timeout *timeout; + + if (eloop.pending_terminate) { + /* + * This may happen in some corner cases where a signal + * is received during a blocking operation. We need to + * process the pending signals and exit if requested to + * avoid hitting the SIGALRM limit if the blocking + * operation took more than two seconds. + */ + eloop_process_pending_signals(); + if (eloop.terminate) + break; + } + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); if (timeout) { @@ -977,6 +988,11 @@ void eloop_run(void) , strerror(errno)); goto out; } + + eloop.readers.changed = 0; + eloop.writers.changed = 0; + eloop.exceptions.changed = 0; + eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ @@ -998,6 +1014,19 @@ void eloop_run(void) if (res <= 0) continue; + if (eloop.readers.changed || + eloop.writers.changed || + eloop.exceptions.changed) { + /* + * Sockets may have been closed and reopened with the + * same FD in the signal or timeout handlers, so we + * must skip the previous results and check again + * whether any of the currently registered sockets have + * events. + */ + continue; + } + #ifdef CONFIG_ELOOP_POLL eloop_sock_table_dispatch(&eloop.readers, &eloop.writers, &eloop.exceptions, eloop.pollfds_map, @@ -1073,7 +1102,7 @@ void eloop_destroy(void) int eloop_terminated(void) { - return eloop.terminate; + return eloop.terminate || eloop.pending_terminate; } diff --git a/contrib/wpa/src/utils/http_curl.c b/contrib/wpa/src/utils/http_curl.c index b38cf796ca2..653eb541ab4 100644 --- a/contrib/wpa/src/utils/http_curl.c +++ b/contrib/wpa/src/utils/http_curl.c @@ -855,8 +855,10 @@ static int validate_server_cert(struct http_ctx *ctx, X509 *cert) struct http_cert hcert; int ret; - if (ctx->cert_cb == NULL) + if (ctx->cert_cb == NULL) { + wpa_printf(MSG_DEBUG, "%s: no cert_cb configured", __func__); return 0; + } if (0) { BIO *out; @@ -950,7 +952,8 @@ static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) ssl_ctx = ssl->ctx; ctx = SSL_CTX_get_app_data(ssl_ctx); - wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify"); + wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d", + preverify_ok); err = X509_STORE_CTX_get_error(x509_ctx); err_str = X509_verify_cert_error_string(err); @@ -1249,9 +1252,14 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address, const char *client_key) { CURL *curl; +#ifdef EAP_TLS_OPENSSL + const char *extra = " tls=openssl"; +#else /* EAP_TLS_OPENSSL */ + const char *extra = ""; +#endif /* EAP_TLS_OPENSSL */ wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s " - "username=%s", address, ca_fname, username); + "username=%s%s", address, ca_fname, username, extra); curl = curl_easy_init(); if (curl == NULL) diff --git a/contrib/wpa/src/utils/includes.h b/contrib/wpa/src/utils/includes.h index 6c6ec87d0ea..75513fc8c1e 100644 --- a/contrib/wpa/src/utils/includes.h +++ b/contrib/wpa/src/utils/includes.h @@ -17,26 +17,22 @@ #include "build_config.h" #include +#include #include #include #include #ifndef _WIN32_WCE -#ifndef CONFIG_TI_COMPILER #include #include -#endif /* CONFIG_TI_COMPILER */ #include #endif /* _WIN32_WCE */ #include -#ifndef CONFIG_TI_COMPILER #ifndef _MSC_VER #include #endif /* _MSC_VER */ -#endif /* CONFIG_TI_COMPILER */ #ifndef CONFIG_NATIVE_WINDOWS -#ifndef CONFIG_TI_COMPILER #include #include #include @@ -44,7 +40,6 @@ #include #include #endif /* __vxworks */ -#endif /* CONFIG_TI_COMPILER */ #endif /* CONFIG_NATIVE_WINDOWS */ #endif /* INCLUDES_H */ diff --git a/contrib/wpa/src/utils/os.h b/contrib/wpa/src/utils/os.h index 77250d6371c..9e496fb6597 100644 --- a/contrib/wpa/src/utils/os.h +++ b/contrib/wpa/src/utils/os.h @@ -246,6 +246,13 @@ char * os_readfile(const char *name, size_t *len); */ int os_file_exists(const char *fname); +/** + * os_fdatasync - Sync a file's (for a given stream) state with storage device + * @stream: the stream to be flushed + * Returns: 0 if the operation succeeded or -1 on failure + */ +int os_fdatasync(FILE *stream); + /** * os_zalloc - Allocate and zero memory * @size: Number of bytes to allocate @@ -646,4 +653,12 @@ int os_exec(const char *program, const char *arg, int wait_completion); #define strcpy OS_DO_NOT_USE_strcpy #endif /* OS_REJECT_C_LIB_FUNCTIONS */ + +#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) +#define TEST_FAIL() testing_test_fail() +int testing_test_fail(void); +#else +#define TEST_FAIL() 0 +#endif + #endif /* OS_H */ diff --git a/contrib/wpa/src/utils/os_internal.c b/contrib/wpa/src/utils/os_internal.c index 77733ad916c..ed6eb3c6b67 100644 --- a/contrib/wpa/src/utils/os_internal.c +++ b/contrib/wpa/src/utils/os_internal.c @@ -243,6 +243,12 @@ char * os_readfile(const char *name, size_t *len) } +int os_fdatasync(FILE *stream) +{ + return 0; +} + + void * os_zalloc(size_t size) { void *n = os_malloc(size); diff --git a/contrib/wpa/src/utils/os_none.c b/contrib/wpa/src/utils/os_none.c index 83fe025167b..0c3214d32ce 100644 --- a/contrib/wpa/src/utils/os_none.c +++ b/contrib/wpa/src/utils/os_none.c @@ -102,6 +102,12 @@ char * os_readfile(const char *name, size_t *len) } +int os_fdatasync(FILE *stream) +{ + return 0; +} + + void * os_zalloc(size_t size) { return NULL; diff --git a/contrib/wpa/src/utils/os_unix.c b/contrib/wpa/src/utils/os_unix.c index 34cb87af6ad..488995c5fa4 100644 --- a/contrib/wpa/src/utils/os_unix.c +++ b/contrib/wpa/src/utils/os_unix.c @@ -17,6 +17,12 @@ #include #endif /* ANDROID */ +#ifdef __MACH__ +#include +#include +#include +#endif /* __MACH__ */ + #include "os.h" #include "common.h" @@ -36,7 +42,7 @@ struct os_alloc_trace { struct dl_list list; size_t len; WPA_TRACE_INFO -}; +} __attribute__((aligned(16))); #endif /* WPA_TRACE */ @@ -63,6 +69,7 @@ int os_get_time(struct os_time *t) int os_get_reltime(struct os_reltime *t) { +#ifndef __MACH__ #if defined(CLOCK_BOOTTIME) static clockid_t clock_id = CLOCK_BOOTTIME; #elif defined(CLOCK_MONOTONIC) @@ -95,6 +102,23 @@ int os_get_reltime(struct os_reltime *t) return -1; } } +#else /* __MACH__ */ + uint64_t abstime, nano; + static mach_timebase_info_data_t info = { 0, 0 }; + + if (!info.denom) { + if (mach_timebase_info(&info) != KERN_SUCCESS) + return -1; + } + + abstime = mach_absolute_time(); + nano = (abstime * info.numer) / info.denom; + + t->sec = nano / NSEC_PER_SEC; + t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC; + + return 0; +#endif /* __MACH__ */ } @@ -253,6 +277,9 @@ int os_get_random(unsigned char *buf, size_t len) FILE *f; size_t rc; + if (TEST_FAIL()) + return -1; + f = fopen("/dev/urandom", "rb"); if (f == NULL) { printf("Could not open /dev/urandom.\n"); @@ -442,6 +469,25 @@ int os_file_exists(const char *fname) } +int os_fdatasync(FILE *stream) +{ + if (!fflush(stream)) { +#ifdef __linux__ + return fdatasync(fileno(stream)); +#else /* !__linux__ */ +#ifdef F_FULLFSYNC + /* OS X does not implement fdatasync(). */ + return fcntl(fileno(stream), F_FULLFSYNC); +#else /* F_FULLFSYNC */ + return fsync(fileno(stream)); +#endif /* F_FULLFSYNC */ +#endif /* __linux__ */ + } + + return -1; +} + + #ifndef WPA_TRACE void * os_zalloc(size_t size) { @@ -575,6 +621,78 @@ static int testing_fail_alloc(void) return 0; } + +char wpa_trace_test_fail_func[256] = { 0 }; +unsigned int wpa_trace_test_fail_after; + +int testing_test_fail(void) +{ + const char *func[WPA_TRACE_LEN]; + size_t i, res, len; + char *pos, *next; + int match; + + if (!wpa_trace_test_fail_after) + return 0; + + res = wpa_trace_calling_func(func, WPA_TRACE_LEN); + i = 0; + if (i < res && os_strcmp(func[i], __func__) == 0) + i++; + + pos = wpa_trace_test_fail_func; + + match = 0; + while (i < res) { + int allow_skip = 1; + int maybe = 0; + + if (*pos == '=') { + allow_skip = 0; + pos++; + } else if (*pos == '?') { + maybe = 1; + pos++; + } + next = os_strchr(pos, ';'); + if (next) + len = next - pos; + else + len = os_strlen(pos); + if (os_memcmp(pos, func[i], len) != 0) { + if (maybe && next) { + pos = next + 1; + continue; + } + if (allow_skip) { + i++; + continue; + } + return 0; + } + if (!next) { + match = 1; + break; + } + pos = next + 1; + i++; + } + if (!match) + return 0; + + wpa_trace_test_fail_after--; + if (wpa_trace_test_fail_after == 0) { + wpa_printf(MSG_INFO, "TESTING: fail at %s", + wpa_trace_test_fail_func); + for (i = 0; i < res; i++) + wpa_printf(MSG_INFO, "backtrace[%d] = %s", + (int) i, func[i]); + return 1; + } + + return 0; +} + #else static inline int testing_fail_alloc(void) diff --git a/contrib/wpa/src/utils/os_win32.c b/contrib/wpa/src/utils/os_win32.c index 296ea13f153..dea27b9f2ad 100644 --- a/contrib/wpa/src/utils/os_win32.c +++ b/contrib/wpa/src/utils/os_win32.c @@ -216,6 +216,24 @@ char * os_readfile(const char *name, size_t *len) } +int os_fdatasync(FILE *stream) +{ + HANDLE h; + + if (stream == NULL) + return -1; + + h = (HANDLE) _get_osfhandle(_fileno(stream)); + if (h == INVALID_HANDLE_VALUE) + return -1; + + if (!FlushFileBuffers(h)) + return -1; + + return 0; +} + + void * os_zalloc(size_t size) { return calloc(1, size); diff --git a/contrib/wpa/src/utils/radiotap.c b/contrib/wpa/src/utils/radiotap.c index f8f815a86be..c9a50233559 100644 --- a/contrib/wpa/src/utils/radiotap.c +++ b/contrib/wpa/src/utils/radiotap.c @@ -123,13 +123,13 @@ int ieee80211_radiotap_iterator_init( /* find payload start allowing for extended bitmap(s) */ - if (iterator->_bitmap_shifter & (1<_bitmap_shifter & BIT(IEEE80211_RADIOTAP_EXT)) { if ((unsigned long)iterator->_arg - (unsigned long)iterator->_rtheader + sizeof(uint32_t) > (unsigned long)iterator->_max_length) return -EINVAL; while (get_unaligned_le32(iterator->_arg) & - (1 << IEEE80211_RADIOTAP_EXT)) { + BIT(IEEE80211_RADIOTAP_EXT)) { iterator->_arg += sizeof(uint32_t); /* diff --git a/contrib/wpa/src/utils/utils_module_tests.c b/contrib/wpa/src/utils/utils_module_tests.c index 4b97dadd786..41511b9999a 100644 --- a/contrib/wpa/src/utils/utils_module_tests.c +++ b/contrib/wpa/src/utils/utils_module_tests.c @@ -9,10 +9,13 @@ #include "utils/includes.h" #include "utils/common.h" +#include "common/ieee802_11_defs.h" #include "utils/bitfield.h" #include "utils/ext_password.h" #include "utils/trace.h" #include "utils/base64.h" +#include "utils/ip_addr.h" +#include "utils/eloop.h" struct printf_test_data { @@ -43,6 +46,7 @@ static int printf_encode_decode_tests(void) char buf[100]; u8 bin[100]; int errors = 0; + int array[10]; wpa_printf(MSG_INFO, "printf encode/decode tests"); @@ -91,9 +95,24 @@ static int printf_encode_decode_tests(void) if (printf_decode(bin, 3, "\\xa") != 1 || bin[0] != 10) errors++; + if (printf_decode(bin, 3, "\\xq") != 1 || bin[0] != 'q') + errors++; + if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a') errors++; + array[0] = 10; + array[1] = 10; + array[2] = 5; + array[3] = 10; + array[4] = 5; + array[5] = 0; + if (int_array_len(array) != 5) + errors++; + int_array_sort_unique(array); + if (int_array_len(array) != 2) + errors++; + if (errors) { wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors); return -1; @@ -335,11 +354,14 @@ static int base64_tests(void) static int common_tests(void) { - char buf[3]; + char buf[3], longbuf[100]; u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 }; u8 bin[3]; int errors = 0; struct wpa_freq_range_list ranges; + size_t len; + const char *txt; + u8 ssid[255]; wpa_printf(MSG_INFO, "common tests"); @@ -395,6 +417,21 @@ static int common_tests(void) if (utf8_escape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a') errors++; + os_memset(ssid, 0, sizeof(ssid)); + txt = wpa_ssid_txt(ssid, sizeof(ssid)); + len = os_strlen(txt); + /* Verify that SSID_MAX_LEN * 4 buffer limit is enforced. */ + if (len != SSID_MAX_LEN * 4) { + wpa_printf(MSG_ERROR, + "Unexpected wpa_ssid_txt() result with too long SSID"); + errors++; + } + + if (wpa_snprintf_hex_sep(longbuf, 0, addr, ETH_ALEN, '-') != 0 || + wpa_snprintf_hex_sep(longbuf, 5, addr, ETH_ALEN, '-') != 3 || + os_strcmp(longbuf, "01-0") != 0) + errors++; + if (errors) { wpa_printf(MSG_ERROR, "%d common test(s) failed", errors); return -1; @@ -404,6 +441,403 @@ static int common_tests(void) } +static int os_tests(void) +{ + int errors = 0; + void *ptr; + os_time_t t; + + wpa_printf(MSG_INFO, "os tests"); + + ptr = os_calloc((size_t) -1, (size_t) -1); + if (ptr) { + errors++; + os_free(ptr); + } + ptr = os_calloc((size_t) 2, (size_t) -1); + if (ptr) { + errors++; + os_free(ptr); + } + ptr = os_calloc((size_t) -1, (size_t) 2); + if (ptr) { + errors++; + os_free(ptr); + } + + ptr = os_realloc_array(NULL, (size_t) -1, (size_t) -1); + if (ptr) { + errors++; + os_free(ptr); + } + + os_sleep(1, 1); + + if (os_mktime(1969, 1, 1, 1, 1, 1, &t) == 0 || + os_mktime(1971, 0, 1, 1, 1, 1, &t) == 0 || + os_mktime(1971, 13, 1, 1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 0, 1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 32, 1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 1, -1, 1, 1, &t) == 0 || + os_mktime(1971, 1, 1, 24, 1, 1, &t) == 0 || + os_mktime(1971, 1, 1, 1, -1, 1, &t) == 0 || + os_mktime(1971, 1, 1, 1, 60, 1, &t) == 0 || + os_mktime(1971, 1, 1, 1, 1, -1, &t) == 0 || + os_mktime(1971, 1, 1, 1, 1, 61, &t) == 0 || + os_mktime(1971, 1, 1, 1, 1, 1, &t) != 0 || + os_mktime(2020, 1, 2, 3, 4, 5, &t) != 0 || + os_mktime(2015, 12, 31, 23, 59, 59, &t) != 0) + errors++; + + if (os_setenv("hwsim_test_env", "test value", 0) != 0 || + os_setenv("hwsim_test_env", "test value 2", 1) != 0 || + os_unsetenv("hwsim_test_env") != 0) + errors++; + + if (os_file_exists("/this-file-does-not-exists-hwsim") != 0) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d os test(s) failed", errors); + return -1; + } + + return 0; +} + + +static int wpabuf_tests(void) +{ + int errors = 0; + void *ptr; + struct wpabuf *buf; + + wpa_printf(MSG_INFO, "wpabuf tests"); + + ptr = os_malloc(100); + if (ptr) { + buf = wpabuf_alloc_ext_data(ptr, 100); + if (buf) { + if (wpabuf_resize(&buf, 100) < 0) + errors++; + else + wpabuf_put(buf, 100); + wpabuf_free(buf); + } else { + errors++; + os_free(ptr); + } + } else { + errors++; + } + + buf = wpabuf_alloc(100); + if (buf) { + struct wpabuf *buf2; + + wpabuf_put(buf, 100); + if (wpabuf_resize(&buf, 100) < 0) + errors++; + else + wpabuf_put(buf, 100); + buf2 = wpabuf_concat(buf, NULL); + if (buf2 != buf) + errors++; + wpabuf_free(buf2); + } else { + errors++; + } + + buf = NULL; + buf = wpabuf_zeropad(buf, 10); + if (buf != NULL) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d wpabuf test(s) failed", errors); + return -1; + } + + return 0; +} + + +static int ip_addr_tests(void) +{ + int errors = 0; + struct hostapd_ip_addr addr; + char buf[100]; + + wpa_printf(MSG_INFO, "ip_addr tests"); + + if (hostapd_parse_ip_addr("1.2.3.4", &addr) != 0 || + addr.af != AF_INET || + hostapd_ip_txt(NULL, buf, sizeof(buf)) != NULL || + hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' || + hostapd_ip_txt(&addr, buf, 0) != NULL || + hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf) + errors++; + + if (hostapd_parse_ip_addr("::", &addr) != 0 || + addr.af != AF_INET6 || + hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' || + hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d ip_addr test(s) failed", errors); + return -1; + } + + return 0; +} + + +struct test_eloop { + unsigned int magic; + int close_in_timeout; + int pipefd1[2]; + int pipefd2[2]; +}; + + +static void eloop_tests_start(int close_in_timeout); + + +static void eloop_test_read_2(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_eloop *t = eloop_ctx; + ssize_t res; + char buf[10]; + + wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd2[0] != sock) { + wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d", + __func__, sock, t->pipefd2[0]); + } + + res = read(sock, buf, sizeof(buf)); + wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d", + __func__, sock, (int) res); +} + + +static void eloop_test_read_2_wrong(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_eloop *t = eloop_ctx; + + wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd2[0] != sock) { + wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d", + __func__, sock, t->pipefd2[0]); + } + + /* + * This is expected to block due to the original socket with data having + * been closed and no new data having been written to the new socket + * with the same fd. To avoid blocking the process during test, skip the + * read here. + */ + wpa_printf(MSG_ERROR, "%s: FAIL - should not have called this function", + __func__); +} + + +static void reopen_pipefd2(struct test_eloop *t) +{ + if (t->pipefd2[0] < 0) { + wpa_printf(MSG_INFO, "pipefd2 had been closed"); + } else { + int res; + + wpa_printf(MSG_INFO, "close pipefd2"); + eloop_unregister_read_sock(t->pipefd2[0]); + close(t->pipefd2[0]); + t->pipefd2[0] = -1; + close(t->pipefd2[1]); + t->pipefd2[1] = -1; + + res = pipe(t->pipefd2); + if (res < 0) { + wpa_printf(MSG_INFO, "pipe: %s", strerror(errno)); + t->pipefd2[0] = -1; + t->pipefd2[1] = -1; + return; + } + + wpa_printf(MSG_INFO, + "re-register pipefd2 with new sockets %d,%d", + t->pipefd2[0], t->pipefd2[1]); + eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2_wrong, + t, NULL); + } +} + + +static void eloop_test_read_1(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_eloop *t = eloop_ctx; + ssize_t res; + char buf[10]; + + wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd1[0] != sock) { + wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d", + __func__, sock, t->pipefd1[0]); + } + + res = read(sock, buf, sizeof(buf)); + wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d", + __func__, sock, (int) res); + + if (!t->close_in_timeout) + reopen_pipefd2(t); +} + + +static void eloop_test_cb(void *eloop_data, void *user_ctx) +{ + struct test_eloop *t = eloop_data; + + wpa_printf(MSG_INFO, "%s", __func__); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->close_in_timeout) + reopen_pipefd2(t); +} + + +static void eloop_test_timeout(void *eloop_data, void *user_ctx) +{ + struct test_eloop *t = eloop_data; + int next_run = 0; + + wpa_printf(MSG_INFO, "%s", __func__); + + if (t->magic != 0x12345678) { + wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x", + __func__, t->magic); + } + + if (t->pipefd1[0] >= 0) { + wpa_printf(MSG_INFO, "pipefd1 had not been closed"); + eloop_unregister_read_sock(t->pipefd1[0]); + close(t->pipefd1[0]); + t->pipefd1[0] = -1; + close(t->pipefd1[1]); + t->pipefd1[1] = -1; + } + + if (t->pipefd2[0] >= 0) { + wpa_printf(MSG_INFO, "pipefd2 had not been closed"); + eloop_unregister_read_sock(t->pipefd2[0]); + close(t->pipefd2[0]); + t->pipefd2[0] = -1; + close(t->pipefd2[1]); + t->pipefd2[1] = -1; + } + + next_run = t->close_in_timeout; + t->magic = 0; + wpa_printf(MSG_INFO, "%s - free(%p)", __func__, t); + os_free(t); + + if (next_run) + eloop_tests_start(0); +} + + +static void eloop_tests_start(int close_in_timeout) +{ + struct test_eloop *t; + int res; + + t = os_zalloc(sizeof(*t)); + if (!t) + return; + t->magic = 0x12345678; + t->close_in_timeout = close_in_timeout; + + wpa_printf(MSG_INFO, "starting eloop tests (%p) (close_in_timeout=%d)", + t, close_in_timeout); + + res = pipe(t->pipefd1); + if (res < 0) { + wpa_printf(MSG_INFO, "pipe: %s", strerror(errno)); + os_free(t); + return; + } + + res = pipe(t->pipefd2); + if (res < 0) { + wpa_printf(MSG_INFO, "pipe: %s", strerror(errno)); + close(t->pipefd1[0]); + close(t->pipefd1[1]); + os_free(t); + return; + } + + wpa_printf(MSG_INFO, "pipe fds: %d,%d %d,%d", + t->pipefd1[0], t->pipefd1[1], + t->pipefd2[0], t->pipefd2[1]); + + eloop_register_read_sock(t->pipefd1[0], eloop_test_read_1, t, NULL); + eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2, t, NULL); + eloop_register_timeout(0, 0, eloop_test_cb, t, NULL); + eloop_register_timeout(0, 200000, eloop_test_timeout, t, NULL); + + if (write(t->pipefd1[1], "HELLO", 5) < 0) + wpa_printf(MSG_INFO, "write: %s", strerror(errno)); + if (write(t->pipefd2[1], "TEST", 4) < 0) + wpa_printf(MSG_INFO, "write: %s", strerror(errno)); + os_sleep(0, 50000); + wpa_printf(MSG_INFO, "waiting for eloop callbacks"); +} + + +static void eloop_tests_run(void *eloop_data, void *user_ctx) +{ + eloop_tests_start(1); +} + + +static int eloop_tests(void) +{ + wpa_printf(MSG_INFO, "schedule eloop tests to be run"); + + /* + * Cannot return error from these without a significant design change, + * so for now, run the tests from a scheduled timeout and require + * separate verification of the results from the debug log. + */ + eloop_register_timeout(0, 0, eloop_tests_run, NULL, NULL); + + return 0; +} + + int utils_module_tests(void) { int ret = 0; @@ -416,6 +850,10 @@ int utils_module_tests(void) bitfield_tests() < 0 || base64_tests() < 0 || common_tests() < 0 || + os_tests() < 0 || + wpabuf_tests() < 0 || + ip_addr_tests() < 0 || + eloop_tests() < 0 || int_array_tests() < 0) ret = -1; diff --git a/contrib/wpa/src/utils/wpa_debug.c b/contrib/wpa/src/utils/wpa_debug.c index 0d119051853..61c0d5ce68c 100644 --- a/contrib/wpa/src/utils/wpa_debug.c +++ b/contrib/wpa/src/utils/wpa_debug.c @@ -307,7 +307,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, "%s - hexdump(len=%lu):%s%s", title, (long unsigned int) len, display, len > slen ? " ..." : ""); - os_free(strbuf); + bin_clear_free(strbuf, 1 + 3 * slen); return; } #else /* CONFIG_ANDROID_LOG */ @@ -339,7 +339,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s", title, (unsigned long) len, display); - os_free(strbuf); + bin_clear_free(strbuf, 1 + 3 * len); return; } #endif /* CONFIG_DEBUG_SYSLOG */ @@ -635,8 +635,8 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) va_end(ap); wpa_printf(level, "%s%s", prefix, buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, 0, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len); + bin_clear_free(buf, buflen); } @@ -663,8 +663,8 @@ void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_msg_cb(ctx, level, 0, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len); + bin_clear_free(buf, buflen); } @@ -690,8 +690,8 @@ void wpa_msg_global(void *ctx, int level, const char *fmt, ...) va_end(ap); wpa_printf(level, "%s", buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, 1, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len); + bin_clear_free(buf, buflen); } @@ -718,8 +718,8 @@ void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...) va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_msg_cb(ctx, level, 1, buf, len); - os_free(buf); + wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len); + bin_clear_free(buf, buflen); } @@ -745,7 +745,34 @@ void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) va_end(ap); wpa_printf(level, "%s", buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, 2, buf, len); + wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len); + bin_clear_free(buf, buflen); +} + + +void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + int len; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate message buffer", + __func__); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + if (wpa_msg_cb) + wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len); os_free(buf); } @@ -789,6 +816,45 @@ void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, MAC2STR(addr), buf); else wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf); - os_free(buf); + bin_clear_free(buf, buflen); } #endif /* CONFIG_NO_HOSTAPD_LOGGER */ + + +const char * debug_level_str(int level) +{ + switch (level) { + case MSG_EXCESSIVE: + return "EXCESSIVE"; + case MSG_MSGDUMP: + return "MSGDUMP"; + case MSG_DEBUG: + return "DEBUG"; + case MSG_INFO: + return "INFO"; + case MSG_WARNING: + return "WARNING"; + case MSG_ERROR: + return "ERROR"; + default: + return "?"; + } +} + + +int str_to_debug_level(const char *s) +{ + if (os_strcasecmp(s, "EXCESSIVE") == 0) + return MSG_EXCESSIVE; + if (os_strcasecmp(s, "MSGDUMP") == 0) + return MSG_MSGDUMP; + if (os_strcasecmp(s, "DEBUG") == 0) + return MSG_DEBUG; + if (os_strcasecmp(s, "INFO") == 0) + return MSG_INFO; + if (os_strcasecmp(s, "WARNING") == 0) + return MSG_WARNING; + if (os_strcasecmp(s, "ERROR") == 0) + return MSG_ERROR; + return -1; +} diff --git a/contrib/wpa/src/utils/wpa_debug.h b/contrib/wpa/src/utils/wpa_debug.h index 400bea9e599..17d8f963802 100644 --- a/contrib/wpa/src/utils/wpa_debug.h +++ b/contrib/wpa/src/utils/wpa_debug.h @@ -164,6 +164,7 @@ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, #define wpa_msg_global(args...) do { } while (0) #define wpa_msg_global_ctrl(args...) do { } while (0) #define wpa_msg_no_global(args...) do { } while (0) +#define wpa_msg_global_only(args...) do { } while (0) #define wpa_msg_register_cb(f) do { } while (0) #define wpa_msg_register_ifname_cb(f) do { } while (0) #else /* CONFIG_NO_WPA_MSG */ @@ -243,7 +244,28 @@ PRINTF_FORMAT(3, 4); void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); -typedef void (*wpa_msg_cb_func)(void *ctx, int level, int global, +/** + * wpa_msg_global_only - Conditional printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg_global(), but it sends the output only as a + * global event. + */ +void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +enum wpa_msg_type { + WPA_MSG_PER_INTERFACE, + WPA_MSG_GLOBAL, + WPA_MSG_NO_GLOBAL, + WPA_MSG_ONLY_GLOBAL, +}; + +typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type, const char *txt, size_t len); /** @@ -342,4 +364,7 @@ static inline void wpa_debug_close_linux_tracing(void) #define WPA_ASSERT(a) do { } while (0) #endif +const char * debug_level_str(int level); +int str_to_debug_level(const char *s); + #endif /* WPA_DEBUG_H */ diff --git a/contrib/wpa/src/utils/wpabuf.c b/contrib/wpa/src/utils/wpabuf.c index 7aafa0a5169..11e7323619d 100644 --- a/contrib/wpa/src/utils/wpabuf.c +++ b/contrib/wpa/src/utils/wpabuf.c @@ -17,7 +17,7 @@ struct wpabuf_trace { unsigned int magic; -}; +} __attribute__((aligned(8))); static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf) { diff --git a/contrib/wpa/src/wps/http_client.c b/contrib/wpa/src/wps/http_client.c index 029001306cb..cdf3a5128ed 100644 --- a/contrib/wpa/src/wps/http_client.c +++ b/contrib/wpa/src/wps/http_client.c @@ -85,15 +85,16 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) { struct http_client *c = eloop_ctx; int res; + size_t send_len; + send_len = wpabuf_len(c->req) - c->req_pos; wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu " "bytes remaining)", inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port), (unsigned long) wpabuf_len(c->req), - (unsigned long) wpabuf_len(c->req) - c->req_pos); + (unsigned long) send_len); - res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, - wpabuf_len(c->req) - c->req_pos, 0); + res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, send_len, 0); if (res < 0) { wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s", strerror(errno)); @@ -102,12 +103,11 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) return; } - if ((size_t) res < wpabuf_len(c->req) - c->req_pos) { + if ((size_t) res < send_len) { wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes " "remaining", res, (unsigned long) wpabuf_len(c->req), - (unsigned long) wpabuf_len(c->req) - c->req_pos - - res); + (unsigned long) send_len - res); c->req_pos += res; return; } @@ -146,24 +146,20 @@ struct http_client * http_client_addr(struct sockaddr_in *dst, c->cb_ctx = cb_ctx; c->sd = socket(AF_INET, SOCK_STREAM, 0); - if (c->sd < 0) { - http_client_free(c); - return NULL; - } + if (c->sd < 0) + goto fail; if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) { wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s", strerror(errno)); - http_client_free(c); - return NULL; + goto fail; } if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) { if (errno != EINPROGRESS) { wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s", strerror(errno)); - http_client_free(c); - return NULL; + goto fail; } /* @@ -173,20 +169,18 @@ struct http_client * http_client_addr(struct sockaddr_in *dst, } if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready, - c, NULL)) { - http_client_free(c); - return NULL; - } - - if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, - http_client_timeout, c, NULL)) { - http_client_free(c); - return NULL; - } + c, NULL) || + eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, + http_client_timeout, c, NULL)) + goto fail; c->req = req; return c; + +fail: + http_client_free(c); + return NULL; } diff --git a/contrib/wpa/src/wps/http_server.c b/contrib/wpa/src/wps/http_server.c index ac088c429d6..507abe87078 100644 --- a/contrib/wpa/src/wps/http_server.c +++ b/contrib/wpa/src/wps/http_server.c @@ -277,11 +277,9 @@ struct http_server * http_server_init(struct in_addr *addr, int port, "%s", srv->port, strerror(errno)); goto fail; } - if (listen(srv->fd, 10 /* max backlog */) < 0) - goto fail; - if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) - goto fail; - if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb, + if (listen(srv->fd, 10 /* max backlog */) < 0 || + fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0 || + eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb, srv, NULL)) goto fail; diff --git a/contrib/wpa/src/wps/httpread.c b/contrib/wpa/src/wps/httpread.c index 2f08f37275c..7a2ba50a9f3 100644 --- a/contrib/wpa/src/wps/httpread.c +++ b/contrib/wpa/src/wps/httpread.c @@ -44,16 +44,6 @@ #define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */ #define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */ -#if 0 -/* httpread_debug -- set this global variable > 0 e.g. from debugger - * to enable debugs (larger numbers for more debugs) - * Make this a #define of 0 to eliminate the debugging code. - */ -int httpread_debug = 99; -#else -#define httpread_debug 0 /* eliminates even the debugging code */ -#endif - /* control instance -- actual definition (opaque to application) */ @@ -136,8 +126,7 @@ static void httpread_timeout_handler(void *eloop_data, void *user_ctx); */ void httpread_destroy(struct httpread *h) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h); + wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h); if (!h) return; @@ -177,6 +166,12 @@ static int httpread_hdr_option_analyze( if (!isdigit(*hbp)) return -1; h->content_length = atol(hbp); + if (h->content_length < 0 || h->content_length > h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: Unacceptable Content-Length %d", + h->content_length); + return -1; + } h->got_content_length = 1; return 0; } @@ -283,8 +278,6 @@ static int httpread_hdr_analyze(struct httpread *h) } } *uri = 0; /* null terminate */ - while (isgraph(*hbp)) - hbp++; while (*hbp == ' ' || *hbp == '\t') hbp++; /* get version */ @@ -380,15 +373,16 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) char *bbp; /* pointer into body buffer */ char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */ - if (httpread_debug >= 20) - wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h); - /* read some at a time, then search for the interal * boundaries between header and data and etc. */ + wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h); nread = read(h->sd, readbuf, sizeof(readbuf)); - if (nread < 0) + if (nread < 0) { + wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno)); goto bad; + } + wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread); if (nread == 0) { /* end of transmission... this may be normal * or may be an error... in some cases we can't @@ -411,8 +405,7 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) * although dropped connections can cause false * end */ - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); + wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); h->got_body = 1; goto got_file; } @@ -432,6 +425,8 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) if (nread == 0) goto get_more; if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) { + wpa_printf(MSG_DEBUG, + "httpread: Too long header"); goto bad; } *hbp++ = *rbp++; @@ -453,16 +448,13 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) goto bad; } if (h->max_bytes == 0) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread no body hdr end(%p)", h); + wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)", + h); goto got_file; } if (h->got_content_length && h->content_length == 0) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread zero content length(%p)", - h); + wpa_printf(MSG_DEBUG, + "httpread zero content length(%p)", h); goto got_file; } } @@ -475,9 +467,7 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) !os_strncasecmp(h->hdr, "HEAD", 4) || !os_strncasecmp(h->hdr, "GET", 3)) { if (!h->got_body) { - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread NO BODY for sp. type"); + wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type"); } h->got_body = 1; goto got_file; @@ -498,8 +488,12 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) char *new_body; int new_alloc_nbytes; - if (h->body_nbytes >= h->max_bytes) + if (h->body_nbytes >= h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: body_nbytes=%d >= max_bytes=%d", + h->body_nbytes, h->max_bytes); goto bad; + } new_alloc_nbytes = h->body_alloc_nbytes + HTTPREAD_BODYBUF_DELTA; /* For content-length case, the first time @@ -509,9 +503,23 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) if (h->got_content_length && new_alloc_nbytes < (h->content_length + 1)) new_alloc_nbytes = h->content_length + 1; - if ((new_body = os_realloc(h->body, new_alloc_nbytes)) - == NULL) + if (new_alloc_nbytes < h->body_alloc_nbytes || + new_alloc_nbytes > h->max_bytes + + HTTPREAD_BODYBUF_DELTA) { + wpa_printf(MSG_DEBUG, + "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)", + new_alloc_nbytes, + h->body_alloc_nbytes, + h->max_bytes); goto bad; + } + if ((new_body = os_realloc(h->body, new_alloc_nbytes)) + == NULL) { + wpa_printf(MSG_DEBUG, + "httpread: Failed to reallocate buffer (len=%d)", + new_alloc_nbytes); + goto bad; + } h->body = new_body; h->body_alloc_nbytes = new_alloc_nbytes; @@ -530,9 +538,19 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) /* hdr line consists solely * of a hex numeral and CFLF */ - if (!isxdigit(*cbp)) + if (!isxdigit(*cbp)) { + wpa_printf(MSG_DEBUG, + "httpread: Unexpected chunk header value (not a hex digit)"); goto bad; + } h->chunk_size = strtoul(cbp, NULL, 16); + if (h->chunk_size < 0 || + h->chunk_size > h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: Invalid chunk size %d", + h->chunk_size); + goto bad; + } /* throw away chunk header * so we have only real data */ @@ -542,10 +560,9 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) /* end of chunking */ /* trailer follows */ h->in_trailer = 1; - if (httpread_debug >= 20) - wpa_printf( - MSG_DEBUG, - "httpread end chunks(%p)", h); + wpa_printf(MSG_DEBUG, + "httpread end chunks(%p)", + h); break; } h->in_chunk_data = 1; @@ -563,8 +580,11 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) */ if (bbp[-1] == '\n' && bbp[-2] == '\r') { - } else + } else { + wpa_printf(MSG_DEBUG, + "httpread: Invalid chunk end"); goto bad; + } h->body_nbytes -= 2; bbp -= 2; h->chunk_start = h->body_nbytes; @@ -574,10 +594,8 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) } else if (h->got_content_length && h->body_nbytes >= h->content_length) { h->got_body = 1; - if (httpread_debug >= 10) - wpa_printf( - MSG_DEBUG, - "httpread got content(%p)", h); + wpa_printf(MSG_DEBUG, + "httpread got content(%p)", h); goto got_file; } if (nread <= 0) @@ -601,6 +619,11 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) ncopy = nread; } /* Note: should never be 0 */ + if (ncopy < 0) { + wpa_printf(MSG_DEBUG, + "httpread: Invalid ncopy=%d", ncopy); + goto bad; + } if (ncopy > nread) ncopy = nread; os_memcpy(bbp, rbp, ncopy); @@ -635,10 +658,9 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) if (c == '\n') { h->trailer_state = trailer_line_begin; h->in_trailer = 0; - if (httpread_debug >= 10) - wpa_printf( - MSG_DEBUG, - "httpread got content(%p)", h); + wpa_printf(MSG_DEBUG, + "httpread got content(%p)", + h); h->got_body = 1; goto got_file; } @@ -666,13 +688,14 @@ bad: return; get_more: + wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h); return; got_file: - if (httpread_debug >= 10) - wpa_printf(MSG_DEBUG, - "httpread got file %d bytes type %d", - h->body_nbytes, h->hdr_type); + wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d", + h->body_nbytes, h->hdr_type); + wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body", + h->body, h->body_nbytes); /* Null terminate for convenience of some applications */ if (h->body) h->body[h->body_nbytes] = 0; /* null terminate */ diff --git a/contrib/wpa/src/wps/ndef.c b/contrib/wpa/src/wps/ndef.c index d45dfc8efee..bb3c055486c 100644 --- a/contrib/wpa/src/wps/ndef.c +++ b/contrib/wpa/src/wps/ndef.c @@ -29,8 +29,8 @@ struct ndef_record { u32 total_length; }; -static char wifi_handover_type[] = "application/vnd.wfa.wsc"; -static char p2p_handover_type[] = "application/vnd.wfa.p2p"; +static const char wifi_handover_type[] = "application/vnd.wfa.wsc"; +static const char p2p_handover_type[] = "application/vnd.wfa.p2p"; static int ndef_parse_record(const u8 *data, u32 size, struct ndef_record *record) @@ -45,9 +45,14 @@ static int ndef_parse_record(const u8 *data, u32 size, return -1; record->payload_length = *pos++; } else { + u32 len; + if (size < 6) return -1; - record->payload_length = ntohl(*(u32 *)pos); + len = WPA_GET_BE32(pos); + if (len > size - 6 || len > 20000) + return -1; + record->payload_length = len; pos += sizeof(u32); } @@ -68,7 +73,8 @@ static int ndef_parse_record(const u8 *data, u32 size, pos += record->payload_length; record->total_length = pos - data; - if (record->total_length > size) + if (record->total_length > size || + record->total_length < record->payload_length) return -1; return 0; } @@ -97,7 +103,7 @@ static struct wpabuf * ndef_parse_records(const struct wpabuf *buf, } -static struct wpabuf * ndef_build_record(u8 flags, void *type, +static struct wpabuf * ndef_build_record(u8 flags, const void *type, u8 type_length, void *id, u8 id_length, const struct wpabuf *payload) diff --git a/contrib/wpa/src/wps/wps.c b/contrib/wpa/src/wps/wps.c index 2c68be8c62e..fbaf85aabab 100644 --- a/contrib/wpa/src/wps/wps.c +++ b/contrib/wpa/src/wps/wps.c @@ -355,16 +355,16 @@ int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, int wps_ap_priority_compar(const struct wpabuf *wps_a, const struct wpabuf *wps_b) { - struct wps_parse_attr attr_a, attr_b; + struct wps_parse_attr attr; int sel_a, sel_b; - if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0) + if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0) return 1; - if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0) - return -1; + sel_a = attr.selected_registrar && *attr.selected_registrar != 0; - sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0; - sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0; + if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0) + return -1; + sel_b = attr.selected_registrar && *attr.selected_registrar != 0; if (sel_a && !sel_b) return -1; @@ -618,7 +618,8 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) if (str == NULL) return pos - buf; for (i = 0; i < attr.dev_name_len; i++) { - if (attr.dev_name[i] < 32) + if (attr.dev_name[i] == 0 || + is_ctrl_char(attr.dev_name[i])) str[i] = '_'; else str[i] = attr.dev_name[i]; diff --git a/contrib/wpa/src/wps/wps.h b/contrib/wpa/src/wps/wps.h index 0a7f65dfd6c..2c91d1678c1 100644 --- a/contrib/wpa/src/wps/wps.h +++ b/contrib/wpa/src/wps/wps.h @@ -9,6 +9,7 @@ #ifndef WPS_H #define WPS_H +#include "common/ieee802_11_defs.h" #include "wps_defs.h" /** @@ -44,7 +45,7 @@ struct wps_parse_attr; * @cred_attr_len: Length of cred_attr in octets */ struct wps_credential { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u16 auth_type; u16 encr_type; @@ -78,7 +79,7 @@ struct wps_credential { * @sec_dev_type: Array of secondary device types * @num_sec_dev_type: Number of secondary device types * @os_version: OS Version - * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags) + * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ, WPS_RF_60GHZ flags) * @p2p: Whether the device is a P2P device */ struct wps_device_data { @@ -623,7 +624,7 @@ struct wps_context { * Credentials. In addition, AP uses it when acting as an Enrollee to * notify Registrar of the current configuration. */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** * ssid_len - Length of ssid in octets diff --git a/contrib/wpa/src/wps/wps_attr_parse.c b/contrib/wpa/src/wps/wps_attr_parse.c index 40bc1ad2d2c..11a967ba0ef 100644 --- a/contrib/wpa/src/wps/wps_attr_parse.c +++ b/contrib/wpa/src/wps/wps_attr_parse.c @@ -447,25 +447,55 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, break; case ATTR_MANUFACTURER: attr->manufacturer = pos; - attr->manufacturer_len = len; + if (len > WPS_MANUFACTURER_MAX_LEN) + attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN; + else + attr->manufacturer_len = len; break; case ATTR_MODEL_NAME: attr->model_name = pos; - attr->model_name_len = len; + if (len > WPS_MODEL_NAME_MAX_LEN) + attr->model_name_len = WPS_MODEL_NAME_MAX_LEN; + else + attr->model_name_len = len; break; case ATTR_MODEL_NUMBER: attr->model_number = pos; - attr->model_number_len = len; + if (len > WPS_MODEL_NUMBER_MAX_LEN) + attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN; + else + attr->model_number_len = len; break; case ATTR_SERIAL_NUMBER: attr->serial_number = pos; - attr->serial_number_len = len; + if (len > WPS_SERIAL_NUMBER_MAX_LEN) + attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN; + else + attr->serial_number_len = len; break; case ATTR_DEV_NAME: + if (len > WPS_DEV_NAME_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore too long Device Name (len=%u)", + len); + break; + } attr->dev_name = pos; attr->dev_name_len = len; break; case ATTR_PUBLIC_KEY: + /* + * The Public Key attribute is supposed to be exactly 192 bytes + * in length. Allow couple of bytes shorter one to try to + * interoperate with implementations that do not use proper + * zero-padding. + */ + if (len < 190 || len > 192) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore Public Key with unexpected length %u", + len); + break; + } attr->public_key = pos; attr->public_key_len = len; break; @@ -485,6 +515,11 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, attr->num_cred++; break; case ATTR_SSID: + if (len > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore too long SSID (len=%u)", len); + break; + } attr->ssid = pos; attr->ssid_len = len; break; diff --git a/contrib/wpa/src/wps/wps_attr_parse.h b/contrib/wpa/src/wps/wps_attr_parse.h index 82c4739f61f..8188fe9173d 100644 --- a/contrib/wpa/src/wps/wps_attr_parse.h +++ b/contrib/wpa/src/wps/wps_attr_parse.h @@ -59,43 +59,44 @@ struct wps_parse_attr { /* variable length fields */ const u8 *manufacturer; - size_t manufacturer_len; const u8 *model_name; - size_t model_name_len; const u8 *model_number; - size_t model_number_len; const u8 *serial_number; - size_t serial_number_len; const u8 *dev_name; - size_t dev_name_len; const u8 *public_key; - size_t public_key_len; const u8 *encr_settings; - size_t encr_settings_len; const u8 *ssid; /* <= 32 octets */ - size_t ssid_len; const u8 *network_key; /* <= 64 octets */ - size_t network_key_len; const u8 *authorized_macs; /* <= 30 octets */ - size_t authorized_macs_len; const u8 *sec_dev_type_list; /* <= 128 octets */ - size_t sec_dev_type_list_len; const u8 *oob_dev_password; /* 38..54 octets */ - size_t oob_dev_password_len; + u16 manufacturer_len; + u16 model_name_len; + u16 model_number_len; + u16 serial_number_len; + u16 dev_name_len; + u16 public_key_len; + u16 encr_settings_len; + u16 ssid_len; + u16 network_key_len; + u16 authorized_macs_len; + u16 sec_dev_type_list_len; + u16 oob_dev_password_len; /* attributes that can occur multiple times */ #define MAX_CRED_COUNT 10 - const u8 *cred[MAX_CRED_COUNT]; - size_t cred_len[MAX_CRED_COUNT]; - size_t num_cred; - #define MAX_REQ_DEV_TYPE_COUNT 10 - const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; - size_t num_req_dev_type; + unsigned int num_cred; + unsigned int num_req_dev_type; + unsigned int num_vendor_ext; + + u16 cred_len[MAX_CRED_COUNT]; + u16 vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; + + const u8 *cred[MAX_CRED_COUNT]; + const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; - size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; - size_t num_vendor_ext; }; int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); diff --git a/contrib/wpa/src/wps/wps_common.c b/contrib/wpa/src/wps/wps_common.c index c1ede6a9ea8..88f85fe83f0 100644 --- a/contrib/wpa/src/wps/wps_common.c +++ b/contrib/wpa/src/wps/wps_common.c @@ -528,7 +528,7 @@ u16 wps_config_methods_str2bin(const char *str) { u16 methods = 0; - if (str == NULL) { + if (str == NULL || str[0] == '\0') { /* Default to enabling methods based on build configuration */ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; methods |= WPS_CONFIG_VIRT_DISPLAY; @@ -764,6 +764,8 @@ static int wps_build_ap_freq(struct wpabuf *msg, int freq) rf_band = WPS_RF_24GHZ; else if (mode == HOSTAPD_MODE_IEEE80211A) rf_band = WPS_RF_50GHZ; + else if (mode == HOSTAPD_MODE_IEEE80211AD) + rf_band = WPS_RF_60GHZ; else return 0; /* Unknown band */ ap_channel = channel; diff --git a/contrib/wpa/src/wps/wps_defs.h b/contrib/wpa/src/wps/wps_defs.h index 25cd14a0b32..a23b979d2e3 100644 --- a/contrib/wpa/src/wps/wps_defs.h +++ b/contrib/wpa/src/wps/wps_defs.h @@ -41,6 +41,11 @@ extern int wps_corrupt_pkhash; #define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16 #define WPS_OOB_DEVICE_PASSWORD_LEN 32 #define WPS_OOB_PUBKEY_HASH_LEN 20 +#define WPS_DEV_NAME_MAX_LEN 32 +#define WPS_MANUFACTURER_MAX_LEN 64 +#define WPS_MODEL_NAME_MAX_LEN 32 +#define WPS_MODEL_NUMBER_MAX_LEN 32 +#define WPS_SERIAL_NUMBER_MAX_LEN 32 /* Attribute Types */ enum wps_attribute { @@ -232,6 +237,7 @@ enum wps_error_indication { /* RF Bands */ #define WPS_RF_24GHZ 0x01 #define WPS_RF_50GHZ 0x02 +#define WPS_RF_60GHZ 0x04 /* Config Methods */ #define WPS_CONFIG_USBA 0x0001 diff --git a/contrib/wpa/src/wps/wps_enrollee.c b/contrib/wpa/src/wps/wps_enrollee.c index 89957b1a818..9321b721abd 100644 --- a/contrib/wpa/src/wps/wps_enrollee.c +++ b/contrib/wpa/src/wps/wps_enrollee.c @@ -759,7 +759,7 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, static int wps_process_creds(struct wps_data *wps, const u8 *cred[], - size_t cred_len[], size_t num_cred, int wps2) + u16 cred_len[], unsigned int num_cred, int wps2) { size_t i; int ok = 0; @@ -799,6 +799,7 @@ static int wps_process_ap_settings_e(struct wps_data *wps, struct wpabuf *attrs, int wps2) { struct wps_credential cred; + int ret = 0; if (!wps->wps->ap) return 0; @@ -877,10 +878,10 @@ static int wps_process_ap_settings_e(struct wps_data *wps, if (wps->wps->cred_cb) { cred.cred_attr = wpabuf_head(attrs); cred.cred_attr_len = wpabuf_len(attrs); - wps->wps->cred_cb(wps->wps->cb_ctx, &cred); + ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred); } - return 0; + return ret; } diff --git a/contrib/wpa/src/wps/wps_er.c b/contrib/wpa/src/wps/wps_er.c index 078ff72781a..b840acd924a 100644 --- a/contrib/wpa/src/wps/wps_er.c +++ b/contrib/wpa/src/wps/wps_er.c @@ -1649,11 +1649,15 @@ static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK"); reply = http_client_get_body(c); - if (reply == NULL) - break; - msg = os_zalloc(wpabuf_len(reply) + 1); - if (msg == NULL) + if (reply) + msg = os_zalloc(wpabuf_len(reply) + 1); + if (msg == NULL) { + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } break; + } os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply)); break; case HTTP_CLIENT_FAILED: @@ -1709,21 +1713,30 @@ static void wps_er_ap_put_message(struct wps_er_ap *ap, url = http_client_url_parse(ap->control_url, &dst, &path); if (url == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); - return; + goto fail; } buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst, &len_ptr, &body_ptr); os_free(url); if (buf == NULL) - return; + goto fail; wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr); ap->http = http_client_addr(&dst, buf, 10000, wps_er_http_put_message_cb, ap); - if (ap->http == NULL) + if (ap->http == NULL) { wpabuf_free(buf); + goto fail; + } + return; + +fail: + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } } diff --git a/contrib/wpa/src/wps/wps_er_ssdp.c b/contrib/wpa/src/wps/wps_er_ssdp.c index e381fecbdc8..280b2b3bde1 100644 --- a/contrib/wpa/src/wps/wps_er_ssdp.c +++ b/contrib/wpa/src/wps/wps_er_ssdp.c @@ -78,9 +78,7 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx) if (os_strstr(start, "ssdp:byebye")) byebye = 1; } else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) { - start += 9; - while (*start == ' ') - start++; + start += 14; pos2 = os_strstr(start, "max-age="); if (pos2 == NULL) continue; diff --git a/contrib/wpa/src/wps/wps_module_tests.c b/contrib/wpa/src/wps/wps_module_tests.c index 6800e86db91..350630768be 100644 --- a/contrib/wpa/src/wps/wps_module_tests.c +++ b/contrib/wpa/src/wps/wps_module_tests.c @@ -17,7 +17,7 @@ struct wps_attr_parse_test { int extra; }; -struct wps_attr_parse_test wps_attr_parse_test_cases[] = { +const struct wps_attr_parse_test wps_attr_parse_test_cases[] = { /* Empty message */ { "", 0, 0 }, /* Truncated attribute header */ @@ -271,7 +271,7 @@ static int wps_attr_parse_tests(void) for (i = 0; i < ARRAY_SIZE(wps_attr_parse_test_cases); i++) { struct wpabuf *buf; size_t len; - struct wps_attr_parse_test *test = + const struct wps_attr_parse_test *test = &wps_attr_parse_test_cases[i]; len = os_strlen(test->data) / 2; diff --git a/contrib/wpa/src/wps/wps_registrar.c b/contrib/wpa/src/wps/wps_registrar.c index 48b7e1288af..4ca3a42d4c7 100644 --- a/contrib/wpa/src/wps/wps_registrar.c +++ b/contrib/wpa/src/wps/wps_registrar.c @@ -2605,13 +2605,16 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, token = wps_get_nfc_pw_token( &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id); if (token && token->peer_pk_hash_known) { + size_t len; + wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " "Password Token"); dl_list_del(&token->list); wps->nfc_pw_token = token; addr[0] = attr->public_key; - sha256_vector(1, addr, &attr->public_key_len, hash); + len = attr->public_key_len; + sha256_vector(1, addr, &len, hash); if (os_memcmp_const(hash, wps->nfc_pw_token->pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN) != 0) { @@ -3226,8 +3229,13 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, os_memset(&cred, 0, sizeof(cred)); os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len); cred.ssid_len = wps->wps->ssid_len; - cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK; - cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES; + if (wps->wps->rf_band_cb(wps->wps->cb_ctx) == WPS_RF_60GHZ) { + cred.auth_type = WPS_AUTH_WPA2PSK; + cred.encr_type = WPS_ENCR_AES; + } else { + cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK; + cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES; + } os_memcpy(cred.key, wps->new_psk, wps->new_psk_len); cred.key_len = wps->new_psk_len; diff --git a/contrib/wpa/src/wps/wps_upnp.c b/contrib/wpa/src/wps/wps_upnp.c index 933d7340ee8..44318e09425 100644 --- a/contrib/wpa/src/wps/wps_upnp.c +++ b/contrib/wpa/src/wps/wps_upnp.c @@ -695,6 +695,7 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, struct subscription *s; time_t now = time(NULL); time_t expire = now + UPNP_SUBSCRIBE_SEC; + char str[80]; /* Get rid of expired subscriptions so we have room */ subscription_list_age(sm, now); @@ -743,8 +744,10 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, subscription_destroy(s); return NULL; } - wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s", - s, callback_urls); + uuid_bin2str(s->uuid, str, sizeof(str)); + wpa_printf(MSG_DEBUG, + "WPS UPnP: Subscription %p (SID %s) started with %s", + s, str, callback_urls); /* Schedule sending this */ event_send_all_later(sm); return s; diff --git a/contrib/wpa/src/wps/wps_upnp_ap.c b/contrib/wpa/src/wps/wps_upnp_ap.c index 2949f141220..cca390530a1 100644 --- a/contrib/wpa/src/wps/wps_upnp_ap.c +++ b/contrib/wpa/src/wps/wps_upnp_ap.c @@ -34,10 +34,8 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", msg); - if (wps_validate_upnp_set_selected_registrar(msg) < 0) - return -1; - - if (wps_parse_msg(msg, &attr) < 0) + if (wps_validate_upnp_set_selected_registrar(msg) < 0 || + wps_parse_msg(msg, &attr) < 0) return -1; s->reg = reg; diff --git a/contrib/wpa/src/wps/wps_upnp_event.c b/contrib/wpa/src/wps/wps_upnp_event.c index 2c8ed4f1399..94aae7542b2 100644 --- a/contrib/wpa/src/wps/wps_upnp_event.c +++ b/contrib/wpa/src/wps/wps_upnp_event.c @@ -276,11 +276,9 @@ static int event_send_start(struct subscription *s) * Assume we are called ONLY with no current event and ONLY with * nonempty event queue and ONLY with at least one address to send to. */ - if (dl_list_empty(&s->addr_list)) - return -1; - if (s->current_event) - return -1; - if (dl_list_empty(&s->event_queue)) + if (dl_list_empty(&s->addr_list) || + s->current_event || + dl_list_empty(&s->event_queue)) return -1; s->current_event = e = event_dequeue(s); diff --git a/contrib/wpa/src/wps/wps_upnp_ssdp.c b/contrib/wpa/src/wps/wps_upnp_ssdp.c index 26a740d2522..968fc03f92e 100644 --- a/contrib/wpa/src/wps/wps_upnp_ssdp.c +++ b/contrib/wpa/src/wps/wps_upnp_ssdp.c @@ -139,7 +139,7 @@ next_advertisement(struct upnp_wps_device_sm *sm, uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); msg = wpabuf_alloc(800); /* more than big enough */ if (msg == NULL) - goto fail; + return NULL; switch (a->type) { case ADVERTISE_UP: case ADVERTISE_DOWN: @@ -213,10 +213,6 @@ next_advertisement(struct upnp_wps_device_sm *sm, *islast = 1; return msg; - -fail: - wpabuf_free(msg); - return NULL; } @@ -744,11 +740,9 @@ int ssdp_listener_open(void) int sd; sd = socket(AF_INET, SOCK_DGRAM, 0); - if (sd < 0) - goto fail; - if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) - goto fail; - if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) + if (sd < 0 || + fcntl(sd, F_SETFL, O_NONBLOCK) != 0 || + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) goto fail; os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; @@ -760,9 +754,8 @@ int ssdp_listener_open(void) mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY); mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS); if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, - (char *) &mcast_addr, sizeof(mcast_addr))) - goto fail; - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, + (char *) &mcast_addr, sizeof(mcast_addr)) || + setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) goto fail; diff --git a/contrib/wpa/src/wps/wps_upnp_web.c b/contrib/wpa/src/wps/wps_upnp_web.c index b1cf571d8ac..d5b0b5b26e9 100644 --- a/contrib/wpa/src/wps/wps_upnp_web.c +++ b/contrib/wpa/src/wps/wps_upnp_web.c @@ -1003,6 +1003,8 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, ret = HTTP_INTERNAL_SERVER_ERROR; goto error; } + if (len > 0 && callback_urls[len - 1] == '\r') + callback_urls[len - 1] = '\0'; continue; } /* SID is only for renewal */ @@ -1214,18 +1216,25 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm, } if (got_uuid) { + char str[80]; + + uuid_bin2str(uuid, str, sizeof(str)); + s = subscription_find(sm, uuid); if (s) { struct subscr_addr *sa; sa = dl_list_first(&s->addr_list, struct subscr_addr, list); - wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s", - s, (sa && sa->domain_and_port) ? + wpa_printf(MSG_DEBUG, + "WPS UPnP: Unsubscribing %p (SID %s) %s", + s, str, (sa && sa->domain_and_port) ? sa->domain_and_port : "-null-"); dl_list_del(&s->list); subscription_destroy(s); } else { - wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe"); + wpa_printf(MSG_INFO, + "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)", + str); ret = HTTP_PRECONDITION_FAILED; goto send_msg; } diff --git a/contrib/wpa/src/wps/wps_validate.c b/contrib/wpa/src/wps/wps_validate.c index 1c6a14bce4b..267b565e478 100644 --- a/contrib/wpa/src/wps/wps_validate.c +++ b/contrib/wpa/src/wps/wps_validate.c @@ -224,6 +224,8 @@ static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory) return 0; } if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ && + *rf_bands != WPS_RF_60GHZ && + *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ | WPS_RF_60GHZ) && *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) { wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands " "attribute value 0x%x", *rf_bands); diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog index 1ac79b4ae5f..facd90eea30 100644 --- a/contrib/wpa/wpa_supplicant/ChangeLog +++ b/contrib/wpa/wpa_supplicant/ChangeLog @@ -1,5 +1,68 @@ ChangeLog for wpa_supplicant +2015-09-27 - v2.5 + * fixed P2P validation of SSID element length before copying it + [http://w1.fi/security/2015-1/] (CVE-2015-1863) + * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding + [http://w1.fi/security/2015-2/] (CVE-2015-4141) + * fixed WMM Action frame parser (AP mode) + [http://w1.fi/security/2015-3/] (CVE-2015-4142) + * fixed EAP-pwd peer missing payload length validation + [http://w1.fi/security/2015-4/] + (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145, CVE-2015-4146) + * fixed validation of WPS and P2P NFC NDEF record payload length + [http://w1.fi/security/2015-5/] + * nl80211: + - added VHT configuration for IBSS + - fixed vendor command handling to check OUI properly + - allow driver-based roaming to change ESS + * added AVG_BEACON_RSSI to SIGNAL_POLL output + * wpa_cli: added tab completion for number of commands + * removed unmaintained and not yet completed SChannel/CryptoAPI support + * modified Extended Capabilities element use in Probe Request frames to + include all cases if any of the values are non-zero + * added support for dynamically creating/removing a virtual interface + with interface_add/interface_remove + * added support for hashed password (NtHash) in EAP-pwd peer + * added support for memory-only PSK/passphrase (mem_only_psk=1 and + CTRL-REQ/RSP-PSK_PASSPHRASE) + * P2P + - optimize scan frequencies list when re-joining a persistent group + - fixed number of sequences with nl80211 P2P Device interface + - added operating class 125 for P2P use cases (this allows 5 GHz + channels 161 and 169 to be used if they are enabled in the current + regulatory domain) + - number of fixes to P2PS functionality + - do not allow 40 MHz co-ex PRI/SEC switch to force MCC + - extended support for preferred channel listing + * D-Bus: + - fixed WPS property of fi.w1.wpa_supplicant1.BSS interface + - fixed PresenceRequest to use group interface + - added new signals: FindStopped, WPS pbc-overlap, + GroupFormationFailure, WPS timeout, InvitationReceived + - added new methods: WPS Cancel, P2P Cancel, Reconnect, RemoveClient + - added manufacturer info + * added EAP-EKE peer support for deriving Session-Id + * added wps_priority configuration parameter to set the default priority + for all network profiles added by WPS + * added support to request a scan with specific SSIDs with the SCAN + command (optional "ssid " arguments) + * removed support for WEP40/WEP104 as a group cipher with WPA/WPA2 + * fixed SAE group selection in an error case + * modified SAE routines to be more robust and PWE generation to be + stronger against timing attacks + * added support for Brainpool Elliptic Curves with SAE + * added support for CCMP-256 and GCMP-256 as group ciphers with FT + * fixed BSS selection based on estimated throughput + * added option to disable TLSv1.0 with OpenSSL + (phase1="tls_disable_tlsv1_0=1") + * added Fast Session Transfer (FST) module + * fixed OpenSSL PKCS#12 extra certificate handling + * fixed key derivation for Suite B 192-bit AKM (this breaks + compatibility with the earlier version) + * added RSN IE to Mesh Peering Open/Confirm frames + * number of small fixes + 2015-03-15 - v2.4 * allow OpenSSL cipher configuration to be set for internal EAP server (openssl_ciphers parameter) diff --git a/contrib/wpa/wpa_supplicant/ap.c b/contrib/wpa/wpa_supplicant/ap.c index 7ecf7a85c30..7a4f4cf4fbe 100644 --- a/contrib/wpa/wpa_supplicant/ap.c +++ b/contrib/wpa/wpa_supplicant/ap.c @@ -142,6 +142,29 @@ void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, } } } + + if (conf->secondary_channel) { + struct wpa_supplicant *iface; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) + { + if (iface == wpa_s || + iface->wpa_state < WPA_AUTHENTICATING || + (int) iface->assoc_freq != ssid->frequency) + continue; + + /* + * Do not allow 40 MHz co-ex PRI/SEC switch to force us + * to change our PRI channel since we have an existing, + * concurrent connection on that channel and doing + * multi-channel concurrency is likely to cause more + * harm than using different PRI/SEC selection in + * environment with multiple BSSes on these two channels + * with mixed 20 MHz or PRI channel selection. + */ + conf->no_pri_sec_switch = 1; + } + } #endif /* CONFIG_IEEE80211N */ } @@ -485,8 +508,13 @@ static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da, int ssi_signal) { struct wpa_supplicant *wpa_s = ctx; + unsigned int freq = 0; + + if (wpa_s->ap_iface) + freq = wpa_s->ap_iface->freq; + return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len, - ssi_signal); + freq, ssi_signal); } @@ -1156,6 +1184,7 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_CTRL_IFACE int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) { struct csa_settings settings; @@ -1166,6 +1195,7 @@ int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) return ap_switch_channel(wpa_s, &settings); } +#endif /* CONFIG_CTRL_IFACE */ void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, @@ -1175,7 +1205,10 @@ void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, return; wpa_s->assoc_freq = freq; - hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1); + if (wpa_s->current_ssid) + wpa_s->current_ssid->frequency = freq; + hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, + offset, width, cf1, cf2); } @@ -1265,6 +1298,7 @@ int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, #endif /* CONFIG_WPS_NFC */ +#ifdef CONFIG_CTRL_IFACE int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s) { struct hostapd_data *hapd; @@ -1274,6 +1308,7 @@ int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s) hapd = wpa_s->ap_iface->bss[0]; return hostapd_ctrl_iface_stop_ap(hapd); } +#endif /* CONFIG_CTRL_IFACE */ #ifdef NEED_AP_MLME @@ -1337,3 +1372,10 @@ void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s, radar->chan_width, radar->cf1, radar->cf2); } #endif /* NEED_AP_MLME */ + + +void ap_periodic(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->ap_iface) + hostapd_periodic_iface(wpa_s->ap_iface); +} diff --git a/contrib/wpa/wpa_supplicant/ap.h b/contrib/wpa/wpa_supplicant/ap.h index 3f4151d8cb9..594168cf1e0 100644 --- a/contrib/wpa/wpa_supplicant/ap.h +++ b/contrib/wpa/wpa_supplicant/ap.h @@ -93,4 +93,6 @@ void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s, struct dfs_event *radar); +void ap_periodic(struct wpa_supplicant *wpa_s); + #endif /* AP_H */ diff --git a/contrib/wpa/wpa_supplicant/bss.c b/contrib/wpa/wpa_supplicant/bss.c index b4c47e21051..1051ee3a4c5 100644 --- a/contrib/wpa/wpa_supplicant/bss.c +++ b/contrib/wpa/wpa_supplicant/bss.c @@ -19,11 +19,6 @@ #include "bss.h" -/** - * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds - */ -#define WPA_BSS_EXPIRATION_PERIOD 10 - #define WPA_BSS_FREQ_CHANGED_FLAG BIT(0) #define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1) #define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2) @@ -311,10 +306,18 @@ static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { - return bss == wpa_s->current_bss || - (!is_zero_ether_addr(bss->bssid) && - (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || - os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0)); + if (bss == wpa_s->current_bss) + return 1; + + if (wpa_s->current_bss && + (bss->ssid_len != wpa_s->current_bss->ssid_len || + os_memcmp(bss->ssid, wpa_s->current_bss->ssid, + bss->ssid_len) != 0)) + return 0; /* SSID has changed */ + + return !is_zero_ether_addr(bss->bssid) && + (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || + os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0); } @@ -390,15 +393,16 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, dl_list_add_tail(&wpa_s->bss_id, &bss->list_id); wpa_s->num_bss++; wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR - " SSID '%s'", - bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); + " SSID '%s' freq %d", + bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len), + bss->freq); wpas_notify_bss_added(wpa_s, bss->bssid, bss->id); return bss; } static int are_ies_equal(const struct wpa_bss *old, - const struct wpa_scan_res *new, u32 ie) + const struct wpa_scan_res *new_res, u32 ie) { const u8 *old_ie, *new_ie; struct wpabuf *old_ie_buff = NULL; @@ -408,19 +412,19 @@ static int are_ies_equal(const struct wpa_bss *old, switch (ie) { case WPA_IE_VENDOR_TYPE: old_ie = wpa_bss_get_vendor_ie(old, ie); - new_ie = wpa_scan_get_vendor_ie(new, ie); + new_ie = wpa_scan_get_vendor_ie(new_res, ie); is_multi = 0; break; case WPS_IE_VENDOR_TYPE: old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie); - new_ie_buff = wpa_scan_get_vendor_ie_multi(new, ie); + new_ie_buff = wpa_scan_get_vendor_ie_multi(new_res, ie); is_multi = 1; break; case WLAN_EID_RSN: case WLAN_EID_SUPP_RATES: case WLAN_EID_EXT_SUPP_RATES: old_ie = wpa_bss_get_ie(old, ie); - new_ie = wpa_scan_get_ie(new, ie); + new_ie = wpa_scan_get_ie(new_res, ie); is_multi = 0; break; default: @@ -454,15 +458,15 @@ static int are_ies_equal(const struct wpa_bss *old, static u32 wpa_bss_compare_res(const struct wpa_bss *old, - const struct wpa_scan_res *new) + const struct wpa_scan_res *new_res) { u32 changes = 0; - int caps_diff = old->caps ^ new->caps; + int caps_diff = old->caps ^ new_res->caps; - if (old->freq != new->freq) + if (old->freq != new_res->freq) changes |= WPA_BSS_FREQ_CHANGED_FLAG; - if (old->level != new->level) + if (old->level != new_res->level) changes |= WPA_BSS_SIGNAL_CHANGED_FLAG; if (caps_diff & IEEE80211_CAP_PRIVACY) @@ -471,22 +475,22 @@ static u32 wpa_bss_compare_res(const struct wpa_bss *old, if (caps_diff & IEEE80211_CAP_IBSS) changes |= WPA_BSS_MODE_CHANGED_FLAG; - if (old->ie_len == new->ie_len && - os_memcmp(old + 1, new + 1, old->ie_len) == 0) + if (old->ie_len == new_res->ie_len && + os_memcmp(old + 1, new_res + 1, old->ie_len) == 0) return changes; changes |= WPA_BSS_IES_CHANGED_FLAG; - if (!are_ies_equal(old, new, WPA_IE_VENDOR_TYPE)) + if (!are_ies_equal(old, new_res, WPA_IE_VENDOR_TYPE)) changes |= WPA_BSS_WPAIE_CHANGED_FLAG; - if (!are_ies_equal(old, new, WLAN_EID_RSN)) + if (!are_ies_equal(old, new_res, WLAN_EID_RSN)) changes |= WPA_BSS_RSNIE_CHANGED_FLAG; - if (!are_ies_equal(old, new, WPS_IE_VENDOR_TYPE)) + if (!are_ies_equal(old, new_res, WPS_IE_VENDOR_TYPE)) changes |= WPA_BSS_WPS_CHANGED_FLAG; - if (!are_ies_equal(old, new, WLAN_EID_SUPP_RATES) || - !are_ies_equal(old, new, WLAN_EID_EXT_SUPP_RATES)) + if (!are_ies_equal(old, new_res, WLAN_EID_SUPP_RATES) || + !are_ies_equal(old, new_res, WLAN_EID_EXT_SUPP_RATES)) changes |= WPA_BSS_RATES_CHANGED_FLAG; return changes; @@ -534,6 +538,9 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, u32 changes; changes = wpa_bss_compare_res(bss, res); + if (changes & WPA_BSS_FREQ_CHANGED_FLAG) + wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d", + MAC2STR(bss->bssid), bss->freq, res->freq); bss->scan_miss_count = 0; bss->last_update_idx = wpa_s->bss_update_idx; wpa_bss_copy_res(bss, res, fetch_time); @@ -652,7 +659,7 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, MACSTR, MAC2STR(res->bssid)); return; } - if (ssid[1] > 32) { + if (ssid[1] > SSID_MAX_LEN) { wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for " MACSTR, MAC2STR(res->bssid)); return; @@ -679,7 +686,7 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, * (to save memory) */ mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID); - if (mesh && mesh[1] <= 32) + if (mesh && mesh[1] <= SSID_MAX_LEN) ssid = mesh; bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); @@ -828,16 +835,6 @@ void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age) } -static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_supplicant *wpa_s = eloop_ctx; - - wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age); - eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, - wpa_bss_timeout, wpa_s, NULL); -} - - /** * wpa_bss_init - Initialize BSS table * @wpa_s: Pointer to wpa_supplicant data @@ -850,8 +847,6 @@ int wpa_bss_init(struct wpa_supplicant *wpa_s) { dl_list_init(&wpa_s->bss); dl_list_init(&wpa_s->bss_id); - eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, - wpa_bss_timeout, wpa_s, NULL); return 0; } @@ -883,7 +878,6 @@ void wpa_bss_flush(struct wpa_supplicant *wpa_s) */ void wpa_bss_deinit(struct wpa_supplicant *wpa_s) { - eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL); wpa_bss_flush(wpa_s); } diff --git a/contrib/wpa/wpa_supplicant/bss.h b/contrib/wpa/wpa_supplicant/bss.h index 634aa3cc06d..b215380eeb1 100644 --- a/contrib/wpa/wpa_supplicant/bss.h +++ b/contrib/wpa/wpa_supplicant/bss.h @@ -69,7 +69,7 @@ struct wpa_bss { /** HESSID */ u8 hessid[ETH_ALEN]; /** SSID */ - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; /** Length of SSID */ size_t ssid_len; /** Frequency of the channel in MHz (e.g., 2412 = channel 1) */ diff --git a/contrib/wpa/wpa_supplicant/config.c b/contrib/wpa/wpa_supplicant/config.c index 8e6cd2006b0..b1adab77bbe 100644 --- a/contrib/wpa/wpa_supplicant/config.c +++ b/contrib/wpa/wpa_supplicant/config.c @@ -15,6 +15,7 @@ #include "rsn_supp/wpa.h" #include "eap_peer/eap.h" #include "p2p/p2p.h" +#include "fst/fst.h" #include "config.h" @@ -967,6 +968,13 @@ static int wpa_config_parse_group(const struct parse_data *data, val = wpa_config_parse_cipher(line, value); if (val == -1) return -1; + + /* + * Backwards compatibility - filter out WEP ciphers that were previously + * allowed. + */ + val &= ~(WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40); + if (val & ~WPA_ALLOWED_GROUP_CIPHERS) { wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher " "(0x%x).", line, val); @@ -1296,6 +1304,7 @@ static int wpa_config_parse_eap(const struct parse_data *data, } +#ifndef NO_CONFIG_WRITE static char * wpa_config_write_eap(const struct parse_data *data, struct wpa_ssid *ssid) { @@ -1329,6 +1338,7 @@ static char * wpa_config_write_eap(const struct parse_data *data, return buf; } +#endif /* NO_CONFIG_WRITE */ static int wpa_config_parse_password(const struct parse_data *data, @@ -1411,6 +1421,7 @@ static int wpa_config_parse_password(const struct parse_data *data, } +#ifndef NO_CONFIG_WRITE static char * wpa_config_write_password(const struct parse_data *data, struct wpa_ssid *ssid) { @@ -1444,6 +1455,7 @@ static char * wpa_config_write_password(const struct parse_data *data, return buf; } +#endif /* NO_CONFIG_WRITE */ #endif /* IEEE8021X_EAPOL */ @@ -1810,12 +1822,13 @@ static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data, * functions. */ static const struct parse_data ssid_fields[] = { - { STR_RANGE(ssid, 0, MAX_SSID_LEN) }, + { STR_RANGE(ssid, 0, SSID_MAX_LEN) }, { INT_RANGE(scan_ssid, 0, 1) }, { FUNC(bssid) }, { FUNC(bssid_blacklist) }, { FUNC(bssid_whitelist) }, { FUNC_KEY(psk) }, + { INT(mem_only_psk) }, { FUNC(proto) }, { FUNC(key_mgmt) }, { INT(bg_scan_period) }, @@ -2257,6 +2270,7 @@ void wpa_config_free(struct wpa_config *config) os_free(config->osu_dir); os_free(config->bgscan); os_free(config->wowlan_triggers); + os_free(config->fst_group_id); os_free(config); } @@ -2516,6 +2530,9 @@ int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var, */ char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys) { +#ifdef NO_CONFIG_WRITE + return NULL; +#else /* NO_CONFIG_WRITE */ const struct parse_data *field; char *key, *value; size_t i; @@ -2561,6 +2578,7 @@ err: os_free(value++); os_free(props); return NULL; +#endif /* NO_CONFIG_WRITE */ } @@ -2947,7 +2965,7 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, if (os_strcmp(var, "excluded_ssid") == 0) { struct excluded_ssid *e; - if (len > MAX_SSID_LEN) { + if (len > SSID_MAX_LEN) { wpa_printf(MSG_ERROR, "Line %d: invalid " "excluded_ssid length %d", line, (int) len); os_free(val); @@ -3499,9 +3517,12 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, config->user_mpm = DEFAULT_USER_MPM; config->max_peer_links = DEFAULT_MAX_PEER_LINKS; config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY; + config->dot11RSNASAERetransPeriod = + DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD; config->fast_reauth = DEFAULT_FAST_REAUTH; config->p2p_go_intent = DEFAULT_P2P_GO_INTENT; config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS; + config->p2p_go_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE; config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY; config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN; config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW; @@ -4052,6 +4073,31 @@ static int wpa_config_get_str(const char *name, struct wpa_config *config, } +#ifdef CONFIG_P2P +static int wpa_config_get_ipv4(const char *name, struct wpa_config *config, + long offset, char *buf, size_t buflen, + int pretty_print) +{ + void *val = ((u8 *) config) + (long) offset; + int res; + char addr[INET_ADDRSTRLEN]; + + if (!val || !inet_ntop(AF_INET, val, addr, sizeof(addr))) + return -1; + + if (pretty_print) + res = os_snprintf(buf, buflen, "%s=%s\n", name, addr); + else + res = os_snprintf(buf, buflen, "%s", addr); + + if (os_snprintf_error(buflen, res)) + res = -1; + + return res; +} +#endif /* CONFIG_P2P */ + + #ifdef OFFSET #undef OFFSET #endif /* OFFSET */ @@ -4067,7 +4113,8 @@ static int wpa_config_get_str(const char *name, struct wpa_config *config, #define STR(f) _STR(f), NULL, NULL #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max #define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL -#define IPV4(f) #f, wpa_global_config_parse_ipv4, NULL, OFFSET(f), NULL, NULL +#define IPV4(f) #f, wpa_global_config_parse_ipv4, wpa_config_get_ipv4, \ + OFFSET(f), NULL, NULL static const struct global_parse_data global_fields[] = { #ifdef CONFIG_CTRL_IFACE @@ -4086,6 +4133,7 @@ static const struct global_parse_data global_fields[] = { { INT(user_mpm), 0 }, { INT_RANGE(max_peer_links, 0, 255), 0 }, { INT(mesh_max_inactivity), 0 }, + { INT(dot11RSNASAERetransPeriod), 0 }, #endif /* CONFIG_MESH */ { INT(disable_scan_offload), 0 }, { INT(fast_reauth), 0 }, @@ -4106,7 +4154,8 @@ static const struct global_parse_data global_fields[] = { { FUNC_NO_VAR(load_dynamic_eap), 0 }, #ifdef CONFIG_WPS { FUNC(uuid), CFG_CHANGED_UUID }, - { STR_RANGE(device_name, 0, 32), CFG_CHANGED_DEVICE_NAME }, + { STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN), + CFG_CHANGED_DEVICE_NAME }, { STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING }, { STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING }, { STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING }, @@ -4119,8 +4168,8 @@ static const struct global_parse_data global_fields[] = { #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE }, - { INT(p2p_listen_reg_class), 0 }, - { INT(p2p_listen_channel), 0 }, + { INT(p2p_listen_reg_class), CFG_CHANGED_P2P_LISTEN_CHANNEL }, + { INT(p2p_listen_channel), CFG_CHANGED_P2P_LISTEN_CHANNEL }, { INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL }, { INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL }, { INT_RANGE(p2p_go_intent, 0, 15), 0 }, @@ -4128,6 +4177,7 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(persistent_reconnect, 0, 1), 0 }, { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS }, { INT(p2p_group_idle), 0 }, + { INT_RANGE(p2p_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 0 }, { INT_RANGE(p2p_passphrase_len, 8, 63), CFG_CHANGED_P2P_PASSPHRASE_LEN }, { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN }, @@ -4144,6 +4194,7 @@ static const struct global_parse_data global_fields[] = { { IPV4(ip_addr_mask), 0 }, { IPV4(ip_addr_start), 0 }, { IPV4(ip_addr_end), 0 }, + { INT_RANGE(p2p_cli_probe, 0, 1), 0 }, #endif /* CONFIG_P2P */ { FUNC(country), CFG_CHANGED_COUNTRY }, { INT(bss_max_count), 0 }, @@ -4189,6 +4240,12 @@ static const struct global_parse_data global_fields[] = { { INT(key_mgmt_offload), 0}, { INT(passive_scan), 0 }, { INT(reassoc_same_bss_optim), 0 }, + { INT(wps_priority), 0}, +#ifdef CONFIG_FST + { STR_RANGE(fst_group_id, 1, FST_MAX_GROUP_ID_LEN), 0 }, + { INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 }, + { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 }, +#endif /* CONFIG_FST */ }; #undef FUNC diff --git a/contrib/wpa/wpa_supplicant/config.h b/contrib/wpa/wpa_supplicant/config.h index 34b754e09c5..627f38b6e00 100644 --- a/contrib/wpa/wpa_supplicant/config.h +++ b/contrib/wpa/wpa_supplicant/config.h @@ -18,6 +18,11 @@ #define DEFAULT_USER_MPM 1 #define DEFAULT_MAX_PEER_LINKS 99 #define DEFAULT_MESH_MAX_INACTIVITY 300 +/* + * The default dot11RSNASAERetransPeriod is defined as 40 ms in the standard, + * but use 1000 ms in practice to avoid issues on low power CPUs. + */ +#define DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD 1000 #define DEFAULT_FAST_REAUTH 1 #define DEFAULT_P2P_GO_INTENT 7 #define DEFAULT_P2P_INTRA_BSS 1 @@ -37,6 +42,7 @@ #include "config_ssid.h" #include "wps/wps.h" +#include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -241,7 +247,7 @@ struct wpa_cred { char *phase2; struct excluded_ssid { - u8 ssid[MAX_SSID_LEN]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; } *excluded_ssid; size_t num_excluded_ssid; @@ -400,6 +406,11 @@ struct wpa_config { * one by one until the driver reports successful association; each * network block should have explicit security policy (i.e., only one * option in the lists) for key_mgmt, pairwise, group, proto variables. + * + * Note: ap_scan=2 should not be used with the nl80211 driver interface + * (the current Linux interface). ap_scan=1 is optimized work working + * with nl80211. For finding networks using hidden SSID, scan_ssid=1 in + * the network block can be used with nl80211. */ int ap_scan; @@ -732,6 +743,34 @@ struct wpa_config { */ int p2p_group_idle; + /** + * p2p_go_freq_change_policy - The GO frequency change policy + * + * This controls the behavior of the GO when there is a change in the + * map of the currently used frequencies in case more than one channel + * is supported. + * + * @P2P_GO_FREQ_MOVE_SCM: Prefer working in a single channel mode if + * possible. In case the GO is the only interface using its frequency + * and there are other station interfaces on other frequencies, the GO + * will migrate to one of these frequencies. + * + * @P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS: Same as P2P_GO_FREQ_MOVE_SCM, + * but a transition is possible only in case one of the other used + * frequencies is one of the frequencies in the intersection of the + * frequency list of the local device and the peer device. + * + * @P2P_GO_FREQ_MOVE_STAY: Prefer to stay on the current frequency. + */ + enum { + P2P_GO_FREQ_MOVE_SCM = 0, + P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS = 1, + P2P_GO_FREQ_MOVE_STAY = 2, + P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_STAY, + } p2p_go_freq_change_policy; + +#define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY + /** * p2p_passphrase_len - Passphrase length (8..63) for P2P GO * @@ -966,6 +1005,18 @@ struct wpa_config { */ int p2p_no_group_iface; + /** + * p2p_cli_probe - Enable/disable P2P CLI probe request handling + * + * If this parameter is set to 1, a connected P2P Client will receive + * and handle Probe Request frames. Setting this parameter to 0 + * disables this option. Default value: 0. + * + * Note: Setting this property at run time takes effect on the following + * interface state transition to/from the WPA_COMPLETED state. + */ + int p2p_cli_probe; + /** * okc - Whether to enable opportunistic key caching by default * @@ -1147,6 +1198,15 @@ struct wpa_config { */ int mesh_max_inactivity; + /** + * dot11RSNASAERetransPeriod - Timeout to retransmit SAE Auth frame + * + * This timeout value is used in mesh STA to retransmit + * SAE Authentication frame. + * By default: 1000 milliseconds. + */ + int dot11RSNASAERetransPeriod; + /** * passive_scan - Whether to force passive scan for network connection * @@ -1163,6 +1223,30 @@ struct wpa_config { * reassoc_same_bss_optim - Whether to optimize reassoc-to-same-BSS */ int reassoc_same_bss_optim; + + /** + * wps_priority - Priority for the networks added through WPS + * + * This priority value will be set to each network profile that is added + * by executing the WPS protocol. + */ + int wps_priority; + + /** + * fst_group_id - FST group ID + */ + char *fst_group_id; + + /** + * fst_priority - priority of the interface within the FST group + */ + int fst_priority; + + /** + * fst_llt - default FST LLT (Link-Lost Timeout) to be used for the + * interface. + */ + int fst_llt; }; diff --git a/contrib/wpa/wpa_supplicant/config_file.c b/contrib/wpa/wpa_supplicant/config_file.c index 3d3a6e404fd..fb438ea43e1 100644 --- a/contrib/wpa/wpa_supplicant/config_file.c +++ b/contrib/wpa/wpa_supplicant/config_file.c @@ -501,7 +501,12 @@ static void write_bssid(FILE *f, struct wpa_ssid *ssid) static void write_psk(FILE *f, struct wpa_ssid *ssid) { - char *value = wpa_config_get(ssid, "psk"); + char *value; + + if (ssid->mem_only_psk) + return; + + value = wpa_config_get(ssid, "psk"); if (value == NULL) return; fprintf(f, "\tpsk=%s\n", value); @@ -673,6 +678,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) write_str(f, "bssid_blacklist", ssid); write_str(f, "bssid_whitelist", ssid); write_psk(f, ssid); + INT(mem_only_psk); write_proto(f, ssid); write_key_mgmt(f, ssid); INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD); @@ -1010,13 +1016,13 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->driver_param) fprintf(f, "driver_param=%s\n", config->driver_param); if (config->dot11RSNAConfigPMKLifetime) - fprintf(f, "dot11RSNAConfigPMKLifetime=%d\n", + fprintf(f, "dot11RSNAConfigPMKLifetime=%u\n", config->dot11RSNAConfigPMKLifetime); if (config->dot11RSNAConfigPMKReauthThreshold) - fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%d\n", + fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%u\n", config->dot11RSNAConfigPMKReauthThreshold); if (config->dot11RSNAConfigSATimeout) - fprintf(f, "dot11RSNAConfigSATimeout=%d\n", + fprintf(f, "dot11RSNAConfigSATimeout=%u\n", config->dot11RSNAConfigSATimeout); if (config->update_config) fprintf(f, "update_config=%d\n", config->update_config); @@ -1064,27 +1070,27 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if (config->p2p_listen_reg_class) - fprintf(f, "p2p_listen_reg_class=%u\n", + fprintf(f, "p2p_listen_reg_class=%d\n", config->p2p_listen_reg_class); if (config->p2p_listen_channel) - fprintf(f, "p2p_listen_channel=%u\n", + fprintf(f, "p2p_listen_channel=%d\n", config->p2p_listen_channel); if (config->p2p_oper_reg_class) - fprintf(f, "p2p_oper_reg_class=%u\n", + fprintf(f, "p2p_oper_reg_class=%d\n", config->p2p_oper_reg_class); if (config->p2p_oper_channel) - fprintf(f, "p2p_oper_channel=%u\n", config->p2p_oper_channel); + fprintf(f, "p2p_oper_channel=%d\n", config->p2p_oper_channel); if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT) - fprintf(f, "p2p_go_intent=%u\n", config->p2p_go_intent); + fprintf(f, "p2p_go_intent=%d\n", config->p2p_go_intent); if (config->p2p_ssid_postfix) fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix); if (config->persistent_reconnect) - fprintf(f, "persistent_reconnect=%u\n", + fprintf(f, "persistent_reconnect=%d\n", config->persistent_reconnect); if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS) - fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss); + fprintf(f, "p2p_intra_bss=%d\n", config->p2p_intra_bss); if (config->p2p_group_idle) - fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle); + fprintf(f, "p2p_group_idle=%d\n", config->p2p_group_idle); if (config->p2p_passphrase_len) fprintf(f, "p2p_passphrase_len=%u\n", config->p2p_passphrase_len); @@ -1112,19 +1118,24 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "p2p_optimize_listen_chan=%d\n", config->p2p_optimize_listen_chan); if (config->p2p_go_ht40) - fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40); + fprintf(f, "p2p_go_ht40=%d\n", config->p2p_go_ht40); if (config->p2p_go_vht) - fprintf(f, "p2p_go_vht=%u\n", config->p2p_go_vht); + fprintf(f, "p2p_go_vht=%d\n", config->p2p_go_vht); if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW) - fprintf(f, "p2p_go_ctwindow=%u\n", config->p2p_go_ctwindow); + fprintf(f, "p2p_go_ctwindow=%d\n", config->p2p_go_ctwindow); if (config->p2p_disabled) - fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled); + fprintf(f, "p2p_disabled=%d\n", config->p2p_disabled); if (config->p2p_no_group_iface) - fprintf(f, "p2p_no_group_iface=%u\n", + fprintf(f, "p2p_no_group_iface=%d\n", config->p2p_no_group_iface); if (config->p2p_ignore_shared_freq) - fprintf(f, "p2p_ignore_shared_freq=%u\n", + fprintf(f, "p2p_ignore_shared_freq=%d\n", config->p2p_ignore_shared_freq); + if (config->p2p_cli_probe) + fprintf(f, "p2p_cli_probe=%d\n", config->p2p_cli_probe); + if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE) + fprintf(f, "p2p_go_freq_change_policy=%u\n", + config->p2p_go_freq_change_policy); #endif /* CONFIG_P2P */ if (config->country[0] && config->country[1]) { fprintf(f, "country=%c%c\n", @@ -1144,14 +1155,14 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->max_num_sta != DEFAULT_MAX_NUM_STA) fprintf(f, "max_num_sta=%u\n", config->max_num_sta); if (config->disassoc_low_ack) - fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack); + fprintf(f, "disassoc_low_ack=%d\n", config->disassoc_low_ack); #ifdef CONFIG_HS20 if (config->hs20) fprintf(f, "hs20=1\n"); #endif /* CONFIG_HS20 */ #ifdef CONFIG_INTERWORKING if (config->interworking) - fprintf(f, "interworking=%u\n", config->interworking); + fprintf(f, "interworking=%d\n", config->interworking); if (!is_zero_ether_addr(config->hessid)) fprintf(f, "hessid=" MACSTR "\n", MAC2STR(config->hessid)); if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE) @@ -1159,7 +1170,7 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) config->access_network_type); #endif /* CONFIG_INTERWORKING */ if (config->pbc_in_m1) - fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1); + fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1); if (config->wps_nfc_pw_from_config) { if (config->wps_nfc_dev_pw_id) fprintf(f, "wps_nfc_dev_pw_id=%d\n", @@ -1218,7 +1229,7 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) int i; fprintf(f, "freq_list="); for (i = 0; config->freq_list[i]; i++) { - fprintf(f, "%s%u", i > 0 ? " " : "", + fprintf(f, "%s%d", i > 0 ? " " : "", config->freq_list[i]); } fprintf(f, "\n"); @@ -1259,7 +1270,7 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr); if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD) - fprintf(f, "key_mgmt_offload=%u\n", config->key_mgmt_offload); + fprintf(f, "key_mgmt_offload=%d\n", config->key_mgmt_offload); if (config->user_mpm != DEFAULT_USER_MPM) fprintf(f, "user_mpm=%d\n", config->user_mpm); @@ -1274,12 +1285,20 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "mesh_max_inactivity=%d\n", config->mesh_max_inactivity); + if (config->dot11RSNASAERetransPeriod != + DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD) + fprintf(f, "dot11RSNASAERetransPeriod=%d\n", + config->dot11RSNASAERetransPeriod); + if (config->passive_scan) fprintf(f, "passive_scan=%d\n", config->passive_scan); if (config->reassoc_same_bss_optim) fprintf(f, "reassoc_same_bss_optim=%d\n", config->reassoc_same_bss_optim); + + if (config->wps_priority) + fprintf(f, "wps_priority=%d\n", config->wps_priority); } #endif /* CONFIG_NO_CONFIG_WRITE */ @@ -1342,6 +1361,8 @@ int wpa_config_write(const char *name, struct wpa_config *config) } #endif /* CONFIG_NO_CONFIG_BLOBS */ + os_fdatasync(f); + fclose(f); if (tmp_name) { diff --git a/contrib/wpa/wpa_supplicant/config_ssid.h b/contrib/wpa/wpa_supplicant/config_ssid.h index 7c826cfd983..7ef326cfbed 100644 --- a/contrib/wpa/wpa_supplicant/config_ssid.h +++ b/contrib/wpa/wpa_supplicant/config_ssid.h @@ -13,8 +13,6 @@ #include "utils/list.h" #include "eap_peer/eap_config.h" -#define MAX_SSID_LEN 32 - #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1) #define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \ @@ -22,8 +20,7 @@ #define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN) #define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X) #define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP) -#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | \ - WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) +#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP) #define DEFAULT_FRAGMENT_SIZE 1398 #define DEFAULT_BG_SCAN_PERIOD -1 @@ -180,6 +177,14 @@ struct wpa_ssid { */ char *ext_psk; + /** + * mem_only_psk - Whether to keep PSK/passphrase only in memory + * + * 0 = allow psk/passphrase to be stored to the configuration file + * 1 = do not store psk/passphrase to the configuration file + */ + int mem_only_psk; + /** * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_* */ @@ -220,7 +225,9 @@ struct wpa_ssid { * * scan_ssid can be used to scan for APs using hidden SSIDs. * Note: Many drivers do not support this. ap_mode=2 can be used with - * such drivers to use hidden SSIDs. + * such drivers to use hidden SSIDs. Note2: Most nl80211-based drivers + * do support scan_ssid=1 and that should be used with them instead of + * ap_scan=2. */ int scan_ssid; diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.c b/contrib/wpa/wpa_supplicant/ctrl_iface.c index b4aefb65ec2..3b97806d871 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface.c @@ -28,6 +28,8 @@ #include "rsn_supp/pmksa_cache.h" #include "l2_packet/l2_packet.h" #include "wps/wps.h" +#include "fst/fst.h" +#include "fst/fst_ctrl_iface.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" @@ -153,7 +155,8 @@ static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val) } ssid = ns; - if ((end - pos) & 0x01 || end - pos > 2 * 32 || + if ((end - pos) & 0x01 || + end - pos > 2 * SSID_MAX_LEN || hexstr2bin(pos, ssid[ssid_count].ssid, (end - pos) / 2) < 0) { os_free(ssid); @@ -283,6 +286,30 @@ static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd) } +static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band) +{ + union wpa_event_data event; + + if (os_strcmp(band, "AUTO") == 0) + wpa_s->setband = WPA_SETBAND_AUTO; + else if (os_strcmp(band, "5G") == 0) + wpa_s->setband = WPA_SETBAND_5G; + else if (os_strcmp(band, "2G") == 0) + wpa_s->setband = WPA_SETBAND_2G; + else + return -1; + + if (wpa_drv_setband(wpa_s, wpa_s->setband) == 0) { + os_memset(&event, 0, sizeof(event)); + event.channel_list_changed.initiator = REGDOM_SET_BY_USER; + event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN; + wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event); + } + + return 0; +} + + static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { @@ -446,14 +473,7 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, ret = wpas_ctrl_set_blob(wpa_s, value); #endif /* CONFIG_NO_CONFIG_BLOBS */ } else if (os_strcasecmp(cmd, "setband") == 0) { - if (os_strcmp(value, "AUTO") == 0) - wpa_s->setband = WPA_SETBAND_AUTO; - else if (os_strcmp(value, "5G") == 0) - wpa_s->setband = WPA_SETBAND_5G; - else if (os_strcmp(value, "2G") == 0) - wpa_s->setband = WPA_SETBAND_2G; - else - ret = -1; + ret = wpas_ctrl_set_band(wpa_s, value); } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -759,6 +779,33 @@ static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch( return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer); } + +static int wpa_supplicant_ctrl_iface_tdls_link_status( + struct wpa_supplicant *wpa_s, const char *addr, + char *buf, size_t buflen) +{ + u8 peer[ETH_ALEN]; + const char *tdls_status; + int ret; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'", + addr); + return -1; + } + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR, + MAC2STR(peer)); + + tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer); + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status); + ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status); + if (os_snprintf_error(buflen, ret)) + return -1; + + return ret; +} + #endif /* CONFIG_TDLS */ @@ -1728,7 +1775,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, if (ssid) { u8 *_ssid = ssid->ssid; size_t ssid_len = ssid->ssid_len; - u8 ssid_buf[MAX_SSID_LEN]; + u8 ssid_buf[SSID_MAX_LEN]; if (ssid_len == 0) { int _res = wpa_drv_get_ssid(wpa_s, ssid_buf); if (_res < 0) @@ -2089,45 +2136,6 @@ static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s, } -static const char * debug_level_str(int level) -{ - switch (level) { - case MSG_EXCESSIVE: - return "EXCESSIVE"; - case MSG_MSGDUMP: - return "MSGDUMP"; - case MSG_DEBUG: - return "DEBUG"; - case MSG_INFO: - return "INFO"; - case MSG_WARNING: - return "WARNING"; - case MSG_ERROR: - return "ERROR"; - default: - return "?"; - } -} - - -static int str_to_debug_level(const char *s) -{ - if (os_strcasecmp(s, "EXCESSIVE") == 0) - return MSG_EXCESSIVE; - if (os_strcasecmp(s, "MSGDUMP") == 0) - return MSG_MSGDUMP; - if (os_strcasecmp(s, "DEBUG") == 0) - return MSG_DEBUG; - if (os_strcasecmp(s, "INFO") == 0) - return MSG_INFO; - if (os_strcasecmp(s, "WARNING") == 0) - return MSG_WARNING; - if (os_strcasecmp(s, "ERROR") == 0) - return MSG_ERROR; - return -1; -} - - static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) @@ -2160,7 +2168,7 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, } } - if (cmd && os_strlen(cmd)) { + if (os_strlen(cmd)) { int level = str_to_debug_level(cmd); if (level < 0) return -1; @@ -2366,6 +2374,14 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, } #endif /* CONFIG_SUITEB192 */ + if (data.key_mgmt & WPA_KEY_MGMT_OSEN) { + ret = os_snprintf(pos, end - pos, "%sOSEN", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } + pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher); if (data.capabilities & WPA_CAPABILITY_PREAUTH) { @@ -2433,7 +2449,7 @@ static int wpa_supplicant_ctrl_iface_scan_result( { char *pos, *end; int ret; - const u8 *ie, *ie2, *p2p, *mesh; + const u8 *ie, *ie2, *osen_ie, *p2p, *mesh; mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID); p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); @@ -2460,8 +2476,12 @@ static int wpa_supplicant_ctrl_iface_scan_result( pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2", ie2, 2 + ie2[1]); } + osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); + if (osen_ie) + pos = wpa_supplicant_ie_txt(pos, end, "OSEN", + osen_ie, 2 + osen_ie[1]); pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); - if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { + if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (os_snprintf_error(end - pos, ret)) return -1; @@ -2525,6 +2545,14 @@ static int wpa_supplicant_ctrl_iface_scan_result( pos += ret; } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) { + ret = os_snprintf(pos, end - pos, "[FST]"); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } +#endif /* CONFIG_FST */ ret = os_snprintf(pos, end - pos, "\t%s", wpa_ssid_txt(bss->ssid, bss->ssid_len)); @@ -2716,6 +2744,8 @@ static int wpa_supplicant_ctrl_iface_select_network( } } + wpa_s->scan_min_time.sec = 0; + wpa_s->scan_min_time.usec = 0; wpa_supplicant_select_network(wpa_s, ssid); return 0; @@ -2753,6 +2783,8 @@ static int wpa_supplicant_ctrl_iface_enable_network( return 0; } } + wpa_s->scan_min_time.sec = 0; + wpa_s->scan_min_time.usec = 0; wpa_supplicant_enable_network(wpa_s, ssid); return 0; @@ -2836,7 +2868,8 @@ static int wpa_supplicant_ctrl_iface_remove_network( #endif /* CONFIG_SME */ wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - wpa_s->own_disconnect_req = 1; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } @@ -2883,7 +2916,8 @@ static int wpa_supplicant_ctrl_iface_remove_network( wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - wpa_s->own_disconnect_req = 1; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } @@ -3005,19 +3039,19 @@ static int wpa_supplicant_ctrl_iface_get_network( *name++ = '\0'; id = atoi(cmd); - wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'", + wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'", id, name); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " + wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } value = wpa_config_get_no_key(ssid, name); if (value == NULL) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network " + wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network " "variable '%s'", name); return -1; } @@ -3035,7 +3069,8 @@ static int wpa_supplicant_ctrl_iface_get_network( static int wpa_supplicant_ctrl_iface_dup_network( - struct wpa_supplicant *wpa_s, char *cmd) + struct wpa_supplicant *wpa_s, char *cmd, + struct wpa_supplicant *dst_wpa_s) { struct wpa_ssid *ssid_s, *ssid_d; char *name, *id, *value; @@ -3054,8 +3089,10 @@ static int wpa_supplicant_ctrl_iface_dup_network( id_s = atoi(cmd); id_d = atoi(id); - wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'", - id_s, id_d, name); + + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'", + wpa_s->ifname, dst_wpa_s->ifname, id_s, id_d, name); ssid_s = wpa_config_get_network(wpa_s->conf, id_s); if (ssid_s == NULL) { @@ -3064,7 +3101,7 @@ static int wpa_supplicant_ctrl_iface_dup_network( return -1; } - ssid_d = wpa_config_get_network(wpa_s->conf, id_d); + ssid_d = wpa_config_get_network(dst_wpa_s->conf, id_d); if (ssid_d == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " "network id=%d", id_d); @@ -3078,7 +3115,7 @@ static int wpa_supplicant_ctrl_iface_dup_network( return -1; } - ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name, + ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name, value); os_free(value); @@ -3889,6 +3926,15 @@ static int wpa_supplicant_ctrl_iface_get_capability( } #endif /* CONFIG_EPR */ +#ifdef CONFIG_FIPS + if (os_strcmp(field, "fips") == 0) { + res = os_snprintf(buf, buflen, "FIPS"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_FIPS */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -3937,7 +3983,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, size_t i; int ret; char *pos, *end; - const u8 *ie, *ie2; + const u8 *ie, *ie2, *osen_ie; pos = buf; end = buf + buflen; @@ -4054,8 +4100,13 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (ie2) pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); + osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); + if (osen_ie) + pos = wpa_supplicant_ie_txt(pos, end, "OSEN", + osen_ie, 2 + osen_ie[1]); pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); - if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { + if (!ie && !ie2 && !osen_ie && + (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (os_snprintf_error(end - pos, ret)) return 0; @@ -4133,9 +4184,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_WPS_SCAN) { ie = (const u8 *) (bss + 1); ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end); - if (ret < 0 || ret >= end - pos) + if (ret >= end - pos) return 0; - pos += ret; + if (ret > 0) + pos += ret; } #endif /* CONFIG_WPS */ @@ -4236,6 +4288,15 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += ret; } +#ifdef CONFIG_FST + if (mask & WPA_BSS_MASK_FST) { + ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } +#endif /* CONFIG_FST */ + if (mask & WPA_BSS_MASK_DELIM) { ret = os_snprintf(pos, end - pos, "====\n"); if (os_snprintf_error(end - pos, ret)) @@ -4548,24 +4609,6 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) } else search_delay = wpas_p2p_search_delay(wpa_s); - /* Must be searched for last, because it adds nul termination */ - pos = os_strstr(cmd, " seek="); - while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) { - char *term; - - term = os_strchr(pos + 1, ' '); - _seek[seek_count++] = pos + 6; - seek = _seek; - pos = os_strstr(pos + 6, " seek="); - - if (term) - *term = '\0'; - } - if (seek_count > P2P_MAX_QUERY_HASH) { - seek[0] = NULL; - seek_count = 1; - } - pos = os_strstr(cmd, "freq="); if (pos) { pos += 5; @@ -4574,11 +4617,75 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) return -1; } + /* Must be searched for last, because it adds nul termination */ + pos = os_strstr(cmd, " seek="); + if (pos) + pos += 6; + while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) { + char *term; + + _seek[seek_count++] = pos; + seek = _seek; + term = os_strchr(pos, ' '); + if (!term) + break; + *term = '\0'; + pos = os_strstr(term + 1, "seek="); + if (pos) + pos += 5; + } + if (seek_count > P2P_MAX_QUERY_HASH) { + seek[0] = NULL; + seek_count = 1; + } + return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type, _dev_id, search_delay, seek_count, seek, freq); } +static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt) +{ + const char *last = NULL; + const char *token; + long int token_len; + unsigned int i; + + /* Expected predefined CPT names delimited by ':' */ + for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) { + if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) { + wpa_printf(MSG_ERROR, + "P2PS: CPT name list is too long, expected up to %d names", + P2PS_FEATURE_CAPAB_CPT_MAX); + cpt[0] = 0; + return -1; + } + + token_len = last - token; + + if (token_len == 3 && + os_memcmp(token, "UDP", token_len) == 0) { + cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; + } else if (token_len == 3 && + os_memcmp(token, "MAC", token_len) == 0) { + cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT; + } else { + wpa_printf(MSG_ERROR, + "P2PS: Unsupported CPT name '%s'", token); + cpt[0] = 0; + return -1; + } + + if (isblank(*last)) { + i++; + break; + } + } + cpt[i] = 0; + return 0; +} + + static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) { struct p2ps_provision *p2ps_prov; @@ -4587,6 +4694,7 @@ static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) char *info = NULL; u8 role = P2PS_SETUP_NONE; long long unsigned val; + int i; pos = os_strstr(cmd, "info="); if (pos) { @@ -4645,6 +4753,18 @@ static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac)) goto invalid_args; + pos = os_strstr(cmd, "cpt="); + if (pos) { + if (p2ps_ctrl_parse_cpt_priority(pos + 4, + p2ps_prov->cpt_priority)) + goto invalid_args; + } else { + p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; + } + + for (i = 0; p2ps_prov->cpt_priority[i]; i++) + p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i]; + /* force conncap with tstCap (no sanity checks) */ pos = os_strstr(cmd, "tstCap="); if (pos) { @@ -4721,6 +4841,8 @@ static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd) if (!p2ps_prov) return -1; + p2ps_prov->pd_seeker = 1; + return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, p2ps_prov); } @@ -5140,6 +5262,8 @@ static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s, char *adv_str; u32 auto_accept, adv_id, svc_state, config_methods; char *svc_info = NULL; + char *cpt_prio_str; + u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; pos = os_strchr(cmd, ' '); if (pos == NULL) @@ -5212,6 +5336,19 @@ static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s, if (pos != NULL) *pos++ = '\0'; + cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL; + if (cpt_prio_str) { + pos = os_strchr(pos, ' '); + if (pos != NULL) + *pos++ = '\0'; + + if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio)) + return -1; + } else { + cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT; + cpt_prio[1] = 0; + } + /* Service and Response Information are optional */ if (pos && pos[0]) { size_t len; @@ -5229,7 +5366,7 @@ static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s, return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str, (u8) svc_state, (u16) config_methods, - svc_info); + svc_info, cpt_prio); } @@ -5299,6 +5436,11 @@ static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd) { u32 adv_id; + if (os_strcmp(cmd, "all") == 0) { + wpas_p2p_service_flush_asp(wpa_s); + return 0; + } + if (sscanf(cmd, "%x", &adv_id) != 1) return -1; @@ -5449,13 +5591,10 @@ static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, - char *cmd, int freq, int ht40, - int vht) + int id, int freq, int ht40, int vht) { - int id; struct wpa_ssid *ssid; - id = atoi(cmd); ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL || ssid->disabled != 2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " @@ -5465,37 +5604,41 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, } return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht, - NULL, 0); + NULL, 0, 0); } static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) { - int freq = 0, ht40, vht; - char *pos; + int freq = 0, persistent = 0, group_id = -1; + int vht = wpa_s->conf->p2p_go_vht; + int ht40 = wpa_s->conf->p2p_go_ht40 || vht; + char *token, *context = NULL; - pos = os_strstr(cmd, "freq="); - if (pos) - freq = atoi(pos + 5); + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "freq=%d", &freq) == 1 || + sscanf(token, "persistent=%d", &group_id) == 1) { + continue; + } else if (os_strcmp(token, "ht40") == 0) { + ht40 = 1; + } else if (os_strcmp(token, "vht") == 0) { + vht = 1; + ht40 = 1; + } else if (os_strcmp(token, "persistent") == 0) { + persistent = 1; + } else { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'", + token); + return -1; + } + } - vht = (os_strstr(cmd, "vht") != NULL) || wpa_s->conf->p2p_go_vht; - ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || - vht; + if (group_id >= 0) + return p2p_ctrl_group_add_persistent(wpa_s, group_id, + freq, ht40, vht); - if (os_strncmp(cmd, "persistent=", 11) == 0) - return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq, - ht40, vht); - if (os_strcmp(cmd, "persistent") == 0 || - os_strncmp(cmd, "persistent ", 11) == 0) - return wpas_p2p_group_add(wpa_s, 1, freq, ht40, vht); - if (os_strncmp(cmd, "freq=", 5) == 0) - return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht); - if (ht40) - return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht); - - wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'", - cmd); - return -1; + return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht); } @@ -5625,7 +5768,7 @@ static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s, freq->min, freq->max); } - wpas_p2p_update_channel_list(wpa_s); + wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW); return 0; } @@ -5842,6 +5985,7 @@ static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s) os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; wpas_p2p_stop_find(wpa_s); + wpa_s->parent->p2ps_method_config_any = 0; if (wpa_s->global->p2p) p2p_flush(wpa_s->global->p2p); } @@ -6476,6 +6620,61 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, pos += ret; } + if (si.avg_beacon_signal) { + ret = os_snprintf(pos, end - pos, + "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + + return pos - buf; +} + + +static int wpas_ctrl_iface_get_pref_freq_list( + struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) +{ + unsigned int freq_list[100], num = 100, i; + int ret; + enum wpa_driver_if_type iface_type; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + /* buf: "" */ + if (os_strcmp(cmd, "STATION") == 0) + iface_type = WPA_IF_STATION; + else if (os_strcmp(cmd, "AP") == 0) + iface_type = WPA_IF_AP_BSS; + else if (os_strcmp(cmd, "P2P_GO") == 0) + iface_type = WPA_IF_P2P_GO; + else if (os_strcmp(cmd, "P2P_CLIENT") == 0) + iface_type = WPA_IF_P2P_CLIENT; + else if (os_strcmp(cmd, "IBSS") == 0) + iface_type = WPA_IF_IBSS; + else if (os_strcmp(cmd, "TDLS") == 0) + iface_type = WPA_IF_TDLS; + else + return -1; + + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)", + iface_type, buf); + + ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list); + if (ret) + return -1; + + for (i = 0; i < num; i++) { + ret = os_snprintf(pos, end - pos, "%s%u", + i > 0 ? "," : "", freq_list[i]); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + return pos - buf; } @@ -6602,6 +6801,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) p2p_wpa_s->p2p_disable_ip_addr_req = 0; os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range); p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL; + p2p_wpa_s->global->p2p_go_avoid_freq.num = 0; p2p_wpa_s->global->pending_p2ps_group = 0; #endif /* CONFIG_P2P */ @@ -6689,6 +6889,8 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid)); } + + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); } @@ -6922,6 +7124,8 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); int *manual_scan_freqs = NULL; + struct wpa_ssid_value *ssid = NULL, *ns; + unsigned int ssid_count = 0; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { *reply_len = -1; @@ -6935,6 +7139,15 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, return; } +#ifdef CONFIG_INTERWORKING + if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) { + wpa_printf(MSG_DEBUG, + "Interworking select in progress - reject new scan"); + *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); + return; + } +#endif /* CONFIG_INTERWORKING */ + if (params) { if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0) scan_only = 1; @@ -6967,6 +7180,60 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, *reply_len = -1; goto done; } + + pos = params; + while (pos && *pos != '\0') { + if (os_strncmp(pos, "ssid ", 5) == 0) { + char *end; + + pos += 5; + end = pos; + while (*end) { + if (*end == '\0' || *end == ' ') + break; + end++; + } + + ns = os_realloc_array( + ssid, ssid_count + 1, + sizeof(struct wpa_ssid_value)); + if (ns == NULL) { + *reply_len = -1; + goto done; + } + ssid = ns; + + if ((end - pos) & 0x01 || + end - pos > 2 * SSID_MAX_LEN || + hexstr2bin(pos, ssid[ssid_count].ssid, + (end - pos) / 2) < 0) { + wpa_printf(MSG_DEBUG, + "Invalid SSID value '%s'", + pos); + *reply_len = -1; + goto done; + } + ssid[ssid_count].ssid_len = (end - pos) / 2; + wpa_hexdump_ascii(MSG_DEBUG, "scan SSID", + ssid[ssid_count].ssid, + ssid[ssid_count].ssid_len); + ssid_count++; + pos = end; + } + + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + } + + wpa_s->num_ssids_from_scan_req = ssid_count; + os_free(wpa_s->ssids_from_scan_req); + if (ssid_count) { + wpa_s->ssids_from_scan_req = ssid; + ssid = NULL; + } else { + wpa_s->ssids_from_scan_req = NULL; } if (scan_only) @@ -7030,6 +7297,7 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, done: os_free(manual_scan_freqs); + os_free(ssid); } @@ -7231,7 +7499,7 @@ void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct wpa_supplicant *wpa_s = ctx; const struct ether_header *eth; - const struct iphdr *ip; + struct iphdr ip; const u8 *pos; unsigned int i; @@ -7239,14 +7507,14 @@ void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) return; eth = (const struct ether_header *) buf; - ip = (const struct iphdr *) (eth + 1); - pos = (const u8 *) (ip + 1); + os_memcpy(&ip, eth + 1, sizeof(ip)); + pos = &buf[sizeof(*eth) + sizeof(ip)]; - if (ip->ihl != 5 || ip->version != 4 || - ntohs(ip->tot_len) != HWSIM_IP_LEN) + if (ip.ihl != 5 || ip.version != 4 || + ntohs(ip.tot_len) != HWSIM_IP_LEN) return; - for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) { + for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) { if (*pos != (u8) i) return; pos++; @@ -7293,7 +7561,7 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) int used; long int val; u8 tos; - u8 buf[HWSIM_PACKETLEN]; + u8 buf[2 + HWSIM_PACKETLEN]; struct ether_header *eth; struct iphdr *ip; u8 *dpos; @@ -7321,7 +7589,7 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) return -1; tos = val; - eth = (struct ether_header *) buf; + eth = (struct ether_header *) &buf[2]; os_memcpy(eth->ether_dhost, dst, ETH_ALEN); os_memcpy(eth->ether_shost, src, ETH_ALEN); eth->ether_type = htons(ETHERTYPE_IP); @@ -7333,14 +7601,14 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) ip->tos = tos; ip->tot_len = htons(HWSIM_IP_LEN); ip->protocol = 1; - ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1); - ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2); + ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1); + ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2); ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); dpos = (u8 *) (ip + 1); for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) *dpos++ = i; - if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, buf, + if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2], HWSIM_PACKETLEN) < 0) return -1; @@ -7429,6 +7697,44 @@ static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s, #endif /* WPA_TRACE_BFD */ } + +static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd) +{ +#ifdef WPA_TRACE_BFD + extern char wpa_trace_test_fail_func[256]; + extern unsigned int wpa_trace_test_fail_after; + char *pos; + + wpa_trace_test_fail_after = atoi(cmd); + pos = os_strchr(cmd, ':'); + if (pos) { + pos++; + os_strlcpy(wpa_trace_test_fail_func, pos, + sizeof(wpa_trace_test_fail_func)); + } else { + wpa_trace_test_fail_after = 0; + } + return 0; +#else /* WPA_TRACE_BFD */ + return -1; +#endif /* WPA_TRACE_BFD */ +} + + +static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ +#ifdef WPA_TRACE_BFD + extern char wpa_trace_test_fail_func[256]; + extern unsigned int wpa_trace_test_fail_after; + + return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after, + wpa_trace_test_fail_func); +#else /* WPA_TRACE_BFD */ + return -1; +#endif /* WPA_TRACE_BFD */ +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -7665,7 +7971,7 @@ static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s, if (os_strncmp(cmd, " ssid=", 6) == 0) { ssid.ssid_len = os_strlen(cmd + 6); - if (ssid.ssid_len > 32) + if (ssid.ssid_len > SSID_MAX_LEN) return -1; ssid.ssid = (u8 *) (cmd + 6); ssid_p = &ssid; @@ -7806,6 +8112,19 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, } +static int wpas_ctrl_cmd_debug_level(const char *cmd) +{ + if (os_strcmp(cmd, "PING") == 0 || + os_strncmp(cmd, "BSS ", 4) == 0 || + os_strncmp(cmd, "GET_NETWORK ", 12) == 0 || + os_strncmp(cmd, "STATUS", 6) == 0 || + os_strncmp(cmd, "STA ", 4) == 0 || + os_strncmp(cmd, "STA-", 4) == 0) + return MSG_EXCESSIVE; + return MSG_DEBUG; +} + + char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { @@ -7829,9 +8148,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", (const u8 *) buf, os_strlen(buf)); } else { - int level = MSG_DEBUG; - if (os_strcmp(buf, "PING") == 0) - level = MSG_EXCESSIVE; + int level = wpas_ctrl_cmd_debug_level(buf); wpa_dbg(wpa_s, level, "Control interface command '%s'", buf); } @@ -8066,7 +8383,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpas_p2p_group_remove(wpa_s, buf + 17)) reply_len = -1; } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) { - if (wpas_p2p_group_add(wpa_s, 0, 0, 0, 0)) + if (p2p_ctrl_group_add(wpa_s, "")) reply_len = -1; } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) { if (p2p_ctrl_group_add(wpa_s, buf + 14)) @@ -8240,6 +8557,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); } else if (os_strcmp(buf, "SCAN") == 0) { wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len); } else if (os_strncmp(buf, "SCAN ", 5) == 0) { @@ -8269,7 +8587,8 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = wpa_supplicant_ctrl_iface_get_network( wpa_s, buf + 12, reply, reply_size); } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { - if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12)) + if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12, + wpa_s)) reply_len = -1; } else if (os_strcmp(buf, "LIST_CREDS") == 0) { reply_len = wpa_supplicant_ctrl_iface_list_creds( @@ -8372,6 +8691,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s, buf + 24)) reply_len = -1; + } else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) { + reply_len = wpa_supplicant_ctrl_iface_tdls_link_status( + wpa_s, buf + 17, reply, reply_size); #endif /* CONFIG_TDLS */ } else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) { reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size); @@ -8442,6 +8764,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) { reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) { + if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "GET_FAIL") == 0) { + reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size); #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) @@ -8460,6 +8787,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) { if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14)) reply_len = -1; + } else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) { + reply_len = wpas_ctrl_iface_get_pref_freq_list( + wpa_s, buf + 19, reply, reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -8479,11 +8809,14 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, char *cmd) { struct wpa_interface iface; - char *pos; + char *pos, *extra; + struct wpa_supplicant *wpa_s; + unsigned int create_iface = 0; + u8 mac_addr[ETH_ALEN]; /* * TABTABTABTAB - * TAB + * TAB[TAB] */ wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); @@ -8543,12 +8876,54 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, iface.bridge_ifname = NULL; if (pos == NULL) break; + + extra = pos; + pos = os_strchr(pos, '\t'); + if (pos) + *pos++ = '\0'; + if (!extra[0]) + break; + + if (os_strcmp(extra, "create") == 0) + create_iface = 1; + else { + wpa_printf(MSG_DEBUG, + "INTERFACE_ADD unsupported extra parameter: '%s'", + extra); + return -1; + } } while (0); - if (wpa_supplicant_get_iface(global, iface.ifname)) - return -1; + if (create_iface) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE creating interface '%s'", + iface.ifname); + if (!global->ifaces) + return -1; + if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname, + NULL, NULL, NULL, mac_addr, NULL) < 0) { + wpa_printf(MSG_ERROR, + "CTRL_IFACE interface creation failed"); + return -1; + } - return wpa_supplicant_add_iface(global, &iface, NULL) ? 0 : -1; + wpa_printf(MSG_DEBUG, + "CTRL_IFACE interface '%s' created with MAC addr: " + MACSTR, iface.ifname, MAC2STR(mac_addr)); + } + + if (wpa_supplicant_get_iface(global, iface.ifname)) + goto fail; + + wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); + if (!wpa_s) + goto fail; + wpa_s->added_vif = create_iface; + return 0; + +fail: + if (create_iface) + wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname); + return -1; } @@ -8556,13 +8931,22 @@ static int wpa_supplicant_global_iface_remove(struct wpa_global *global, char *cmd) { struct wpa_supplicant *wpa_s; + int ret; + unsigned int delete_iface; wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd); wpa_s = wpa_supplicant_get_iface(global, cmd); if (wpa_s == NULL) return -1; - return wpa_supplicant_remove_iface(global, wpa_s, 0); + delete_iface = wpa_s->added_vif; + ret = wpa_supplicant_remove_iface(global, wpa_s, 0); + if (!ret && delete_iface) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'", + cmd); + ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd); + } + return ret; } @@ -8589,7 +8973,7 @@ static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *pos, *end; for (i = 0; wpa_drivers[i]; i++) { - struct wpa_driver_ops *drv = wpa_drivers[i]; + const struct wpa_driver_ops *drv = wpa_drivers[i]; if (drv->get_interfaces == NULL) continue; tmp = drv->get_interfaces(global->drv_priv[i]); @@ -8807,6 +9191,41 @@ static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd) } +static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global, + char *cmd) +{ + struct wpa_supplicant *wpa_s[2]; /* src, dst */ + char *p; + unsigned int i; + + /* cmd: " + * */ + + for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) { + p = os_strchr(cmd, ' '); + if (p == NULL) + return -1; + *p = '\0'; + + wpa_s[i] = global->ifaces; + for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) { + if (os_strcmp(cmd, wpa_s[i]->ifname) == 0) + break; + } + + if (!wpa_s[i]) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: Could not find iface=%s", cmd); + return -1; + } + + cmd = p + 1; + } + + return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]); +} + + #ifndef CONFIG_NO_CONFIG_WRITE static int wpas_global_ctrl_iface_save_config(struct wpa_global *global) { @@ -8888,6 +9307,59 @@ static int wpas_global_ctrl_iface_status(struct wpa_global *global, } +#ifdef CONFIG_FST + +static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global, + char *cmd, char *buf, + size_t reply_size) +{ + char ifname[IFNAMSIZ + 1]; + struct fst_iface_cfg cfg; + struct wpa_supplicant *wpa_s; + struct fst_wpa_obj iface_obj; + + if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) { + wpa_s = wpa_supplicant_get_iface(global, ifname); + if (wpa_s) { + if (wpa_s->fst) { + wpa_printf(MSG_INFO, "FST: Already attached"); + return -1; + } + fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj); + wpa_s->fst = fst_attach(ifname, wpa_s->own_addr, + &iface_obj, &cfg); + if (wpa_s->fst) + return os_snprintf(buf, reply_size, "OK\n"); + } + } + + return -1; +} + + +static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global, + char *cmd, char *buf, + size_t reply_size) +{ + char ifname[IFNAMSIZ + 1]; + struct wpa_supplicant *wpa_s; + + if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) { + wpa_s = wpa_supplicant_get_iface(global, ifname); + if (wpa_s) { + if (!fst_iface_detach(ifname)) { + wpa_s->fst = NULL; + return os_snprintf(buf, reply_size, "OK\n"); + } + } + } + + return -1; +} + +#endif /* CONFIG_FST */ + + char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, char *buf, size_t *resp_len) { @@ -8939,6 +9411,18 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, } else if (os_strcmp(buf, "INTERFACES") == 0) { reply_len = wpa_supplicant_global_iface_interfaces( global, reply, reply_size); +#ifdef CONFIG_FST + } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) { + reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11, + reply, + reply_size); + } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) { + reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11, + reply, + reply_size); + } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) { + reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size); +#endif /* CONFIG_FST */ } else if (os_strcmp(buf, "TERMINATE") == 0) { wpa_supplicant_terminate_proc(global); } else if (os_strcmp(buf, "SUSPEND") == 0) { @@ -8959,6 +9443,9 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, #endif /* CONFIG_P2P */ reply_len = -1; } + } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { + if (wpas_global_ctrl_iface_dup_network(global, buf + 12)) + reply_len = -1; #ifndef CONFIG_NO_CONFIG_WRITE } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { if (wpas_global_ctrl_iface_save_config(global)) diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c b/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c index dc02db213a4..54e0e2fac58 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c @@ -423,7 +423,8 @@ static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params) } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + enum wpa_msg_type type, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c index bf6a3df6c3c..76f69f2b57b 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c @@ -322,7 +322,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + enum wpa_msg_type type, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c index b1ac7666889..11f28147313 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c @@ -13,6 +13,10 @@ #include #include #include +#ifdef __linux__ +#include +#include +#endif /* __linux__ */ #ifdef ANDROID #include #endif /* ANDROID */ @@ -72,6 +76,32 @@ static int wpas_ctrl_iface_global_reinit(struct wpa_global *global, struct ctrl_iface_global_priv *priv); +static void wpas_ctrl_sock_debug(const char *title, int sock, const char *buf, + size_t len) +{ +#ifdef __linux__ + socklen_t optlen; + int sndbuf, outq; + int level = MSG_MSGDUMP; + + if (len >= 5 && os_strncmp(buf, "PONG\n", 5) == 0) + level = MSG_EXCESSIVE; + + optlen = sizeof(sndbuf); + sndbuf = 0; + if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0) + sndbuf = -1; + + if (ioctl(sock, SIOCOUTQ, &outq) < 0) + outq = -1; + + wpa_printf(level, + "CTRL-DEBUG: %s: sock=%d sndbuf=%d outq=%d send_len=%d", + title, sock, sndbuf, outq, (int) len); +#endif /* __linux__ */ +} + + static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_un *from, socklen_t fromlen, int global) @@ -197,6 +227,13 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf, &reply_len); reply = reply_buf; + + /* + * There could be some password/key material in the command, so + * clear the buffer explicitly now that it is not needed + * anymore. + */ + os_memset(buf, 0, res); } if (!reply && reply_len == 1) { @@ -208,6 +245,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, } if (reply) { + wpas_ctrl_sock_debug("ctrl_sock-sendto", sock, reply, + reply_len); if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen) < 0) { int _errno = errno; @@ -295,7 +334,8 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + enum wpa_msg_type type, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; @@ -303,19 +343,19 @@ static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, if (wpa_s == NULL) return; - if (global != 2 && wpa_s->global->ctrl_iface) { + if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) { struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface; if (!dl_list_empty(&priv->ctrl_dst)) { - wpa_supplicant_ctrl_iface_send(wpa_s, global ? NULL : - wpa_s->ifname, - priv->sock, - &priv->ctrl_dst, - level, txt, len, NULL, - priv); + wpa_supplicant_ctrl_iface_send( + wpa_s, + type != WPA_MSG_PER_INTERFACE ? + NULL : wpa_s->ifname, + priv->sock, &priv->ctrl_dst, level, txt, len, + NULL, priv); } } - if (wpa_s->ctrl_iface == NULL) + if (type == WPA_MSG_ONLY_GLOBAL || wpa_s->ctrl_iface == NULL) return; wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock, &wpa_s->ctrl_iface->ctrl_dst, @@ -544,6 +584,53 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) if (wpa_s->conf->ctrl_interface == NULL) return priv; +#ifdef ANDROID + if (wpa_s->global->params.ctrl_interface) { + int same = 0; + + if (wpa_s->global->params.ctrl_interface[0] == '/') { + if (os_strcmp(wpa_s->global->params.ctrl_interface, + wpa_s->conf->ctrl_interface) == 0) + same = 1; + } else if (os_strncmp(wpa_s->global->params.ctrl_interface, + "@android:", 9) == 0 || + os_strncmp(wpa_s->global->params.ctrl_interface, + "@abstract:", 10) == 0) { + char *pos; + + /* + * Currently, Android uses @android:wpa_* as the naming + * convention for the global ctrl interface. This logic + * needs to be revisited if the above naming convention + * is modified. + */ + pos = os_strchr(wpa_s->global->params.ctrl_interface, + '_'); + if (pos && + os_strcmp(pos + 1, + wpa_s->conf->ctrl_interface) == 0) + same = 1; + } + + if (same) { + /* + * The invalid configuration combination might be + * possible to hit in an Android OTA upgrade case, so + * instead of refusing to start the wpa_supplicant + * process, do not open the per-interface ctrl_iface + * and continue with the global control interface that + * was set from the command line since the Wi-Fi + * framework will use it for operations. + */ + wpa_printf(MSG_ERROR, + "global ctrl interface %s matches ctrl interface %s - do not open per-interface ctrl interface", + wpa_s->global->params.ctrl_interface, + wpa_s->conf->ctrl_interface); + return priv; + } + } +#endif /* ANDROID */ + if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) { os_free(priv); return NULL; @@ -708,8 +795,10 @@ static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, offsetof(struct sockaddr_un, sun_path)); msg.msg_name = (void *) &dst->addr; msg.msg_namelen = dst->addrlen; + wpas_ctrl_sock_debug("ctrl_sock-sendmsg", sock, buf, len); if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor sent successfully to %s", + wpa_printf(MSG_MSGDUMP, + "CTRL_IFACE monitor sent successfully to %s", addr_txt); dst->errors = 0; continue; @@ -846,6 +935,13 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, reply_buf = wpa_supplicant_global_ctrl_iface_process( global, buf, &reply_len); reply = reply_buf; + + /* + * There could be some password/key material in the command, so + * clear the buffer explicitly now that it is not needed + * anymore. + */ + os_memset(buf, 0, res); } if (!reply && reply_len == 1) { @@ -857,6 +953,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, } if (reply) { + wpas_ctrl_sock_debug("global_ctrl_sock-sendto", + sock, reply, reply_len); if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen) < 0) { wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s", diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c index 30ef03a7453..67d0e2877a4 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c @@ -137,7 +137,7 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(WPAS_DBUS_NEW_PATH, @@ -200,7 +200,7 @@ void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success) iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -239,7 +239,7 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -307,7 +307,7 @@ static void wpas_dbus_signal_blob(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -374,7 +374,7 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, @@ -467,7 +467,7 @@ void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; field = wpa_supplicant_ctrl_req_to_string(rtype, default_txt, &txt); @@ -511,6 +511,8 @@ void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s, char path[WPAS_DBUS_OBJECT_PATH_MAX]; + if (!wpa_s->dbus_new_path) + return; os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d", wpa_s->dbus_new_path, ssid->id); @@ -522,6 +524,44 @@ void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s, #ifdef CONFIG_WPS +/** + * wpas_dbus_signal_wps_event_pbc_overlap - Signals PBC overlap WPS event + * @wpa_s: %wpa_supplicant network interface data + * + * Sends Event dbus signal with name "pbc-overlap" and empty dict as arguments + */ +void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s) +{ + + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char *key = "pbc-overlap"; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_WPS, "Event"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) || + !wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + + /** * wpas_dbus_signal_wps_event_success - Signals Success WPS event * @wpa_s: %wpa_supplicant network interface data @@ -539,7 +579,7 @@ void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s) iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -563,6 +603,7 @@ void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s) /** * wpas_dbus_signal_wps_event_fail - Signals Fail WPS event * @wpa_s: %wpa_supplicant network interface data + * @fail: WPS failure information * * Sends Event dbus signal with name "fail" and dictionary containing * "msg field with fail message number (int32) as arguments @@ -579,7 +620,7 @@ void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -592,6 +633,10 @@ void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s, if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) || !wpa_dbus_dict_open_write(&iter, &dict_iter) || !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) || + !wpa_dbus_dict_append_int32(&dict_iter, "config_error", + fail->config_error) || + !wpa_dbus_dict_append_int32(&dict_iter, "error_indication", + fail->error_indication) || !wpa_dbus_dict_close_write(&iter, &dict_iter)) wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); else @@ -604,6 +649,7 @@ void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s, /** * wpas_dbus_signal_wps_event_m2d - Signals M2D WPS event * @wpa_s: %wpa_supplicant network interface data + * @m2d: M2D event data information * * Sends Event dbus signal with name "m2d" and dictionary containing * fields of wps_event_m2d structure. @@ -620,7 +666,7 @@ void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -669,6 +715,7 @@ void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s, /** * wpas_dbus_signal_wps_cred - Signals new credentials * @wpa_s: %wpa_supplicant network interface data + * @cred: WPS Credential information * * Sends signal with credentials in directory argument */ @@ -686,7 +733,7 @@ void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -760,7 +807,7 @@ void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -801,7 +848,7 @@ void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -844,7 +891,7 @@ static void wpas_dbus_signal_sta(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, @@ -916,7 +963,8 @@ void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s, if (parent->p2p_mgmt) parent = parent->parent; - if (!wpa_s->dbus_groupobj_path) + if (!wpa_s->dbus_groupobj_path || !wpa_s->dbus_new_path || + !parent->dbus_new_path) return; msg = dbus_message_new_signal(parent->dbus_new_path, @@ -984,6 +1032,8 @@ void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; if (request || !status) { if (config_methods & WPS_CONFIG_DISPLAY) @@ -1057,8 +1107,19 @@ error: } +/** + * wpas_dbus_signal_p2p_go_neg_req - Signal P2P GO Negotiation Request RX + * @wpa_s: %wpa_supplicant network interface data + * @src: Source address of the message triggering this notification + * @dev_passwd_id: WPS Device Password Id + * @go_intent: Peer's GO Intent value + * + * Sends signal to notify that a peer P2P Device is requesting group owner + * negotiation with us. + */ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, - const u8 *src, u16 dev_passwd_id) + const u8 *src, u16 dev_passwd_id, + u8 go_intent) { DBusMessage *msg; DBusMessageIter iter; @@ -1073,6 +1134,8 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, @@ -1090,7 +1153,9 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path) || !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16, - &dev_passwd_id)) + &dev_passwd_id) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE, + &go_intent)) wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); else dbus_connection_send(iface->con, msg, NULL); @@ -1105,7 +1170,8 @@ static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s, { char group_name[3]; - if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)) + if (!wpa_s->dbus_new_path || + os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)) return -1; os_memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2); @@ -1209,7 +1275,7 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, iface = parent->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !parent->dbus_new_path || !wpa_s->dbus_new_path) return; if (wpa_s->dbus_groupobj_path == NULL) @@ -1248,10 +1314,9 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, /** - * - * Method to emit GONegotiation Success or Failure signals based - * on status. - * @status: Status of the GO neg request. 0 for success, other for errors. + * wpas_dbus_signal_p2p_go_neg_resp - Emit GONegotiation Success/Failure signal + * @wpa_s: %wpa_supplicant network interface data + * @res: Result of the GO Neg Request */ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *res) @@ -1272,7 +1337,7 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, os_memset(freqs, 0, sizeof(freqs)); /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, @@ -1363,12 +1428,10 @@ err: /** - * - * Method to emit Invitation Result signal based on status and - * bssid - * @status: Status of the Invite request. 0 for success, other - * for errors - * @bssid : Basic Service Set Identifier + * wpas_dbus_signal_p2p_invitation_result - Emit InvitationResult signal + * @wpa_s: %wpa_supplicant network interface data + * @status: Status of invitation process + * @bssid: Basic Service Set Identifier */ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, int status, const u8 *bssid) @@ -1386,6 +1449,8 @@ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, @@ -1439,6 +1504,8 @@ void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, parent = wpa_s->parent; if (parent->p2p_mgmt) parent = parent->parent; + if (!parent->dbus_new_path) + return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" @@ -1494,6 +1561,8 @@ void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, parent = wpa_s->parent; if (parent->p2p_mgmt) parent = parent->parent; + if (!parent->dbus_new_path) + return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" @@ -1551,6 +1620,8 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; /* Check if this is a known peer */ if (!p2p_peer_known(wpa_s->global->p2p, sa)) @@ -1617,6 +1688,8 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; /* Check if this is a known peer */ if (!p2p_peer_known(wpa_s->global->p2p, sa)) @@ -1678,6 +1751,8 @@ static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u", @@ -1740,6 +1815,7 @@ static void wpas_dbus_signal_persistent_group_removed( /** * wpas_dbus_signal_p2p_wps_failed - Signals WpsFailed event * @wpa_s: %wpa_supplicant network interface data + * @fail: WPS failure information * * Sends Event dbus signal with name "fail" and dictionary containing * "msg" field with fail message number (int32) as arguments @@ -1762,6 +1838,8 @@ void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, "WpsFailed"); @@ -1783,6 +1861,98 @@ void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, dbus_message_unref(msg); } + +/** + * wpas_dbus_signal_p2p_group_formation_failure - Signals GroupFormationFailure event + * @wpa_s: %wpa_supplicant network interface data + * @reason: indicates the reason code for group formation failure + * + * Sends Event dbus signal and string reason code when available. + */ +void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, + const char *reason) +{ + DBusMessage *msg; + struct wpas_dbus_priv *iface; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "GroupFormationFailure"); + if (msg == NULL) + return; + + if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID)) + dbus_connection_send(iface->con, msg, NULL); + else + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_p2p_invitation_received - Emit InvitationReceived signal + * @wpa_s: %wpa_supplicant network interface data + * @sa: Source address of the Invitation Request + * @dev_add: GO Device Address + * @bssid: P2P Group BSSID or %NULL if not received + * @id: Persistent group id or %0 if not persistent group + * @op_freq: Operating frequency for the group + */ + +void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *dev_addr, + const u8 *bssid, int id, + int op_freq) +{ + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "InvitationReceived"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + (sa && + !wpa_dbus_dict_append_byte_array(&dict_iter, "sa", + (const char *) sa, ETH_ALEN)) || + (dev_addr && + !wpa_dbus_dict_append_byte_array(&dict_iter, "go_dev_addr", + (const char *) dev_addr, + ETH_ALEN)) || + (bssid && + !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid", + (const char *) bssid, + ETH_ALEN)) || + (id && + !wpa_dbus_dict_append_int32(&dict_iter, "persistent_id", id)) || + !wpa_dbus_dict_append_int32(&dict_iter, "op_freq", op_freq) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) { + dbus_message_unref(msg); + return; + } + + dbus_connection_send(iface->con, msg, NULL); +} + + #endif /* CONFIG_P2P */ @@ -1862,6 +2032,9 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, char path[WPAS_DBUS_OBJECT_PATH_MAX]; char *prop; + if (!wpa_s->dbus_new_path) + return; + switch (property) { case WPAS_DBUS_BSS_PROP_SIGNAL: prop = "Signal"; @@ -2177,7 +2350,7 @@ int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, #endif /* CONFIG_P2P */ /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) + if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path) return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) @@ -2351,7 +2524,7 @@ int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s, char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) + if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path) return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) @@ -2394,7 +2567,7 @@ int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, struct bss_handler_args *arg; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) + if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path) return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) @@ -2486,6 +2659,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "Reconnect", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_reconnect, + { + END_ARGS + } + }, { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, (WPADBusMethodHandler) wpas_dbus_handler_remove_network, { @@ -2558,6 +2737,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "Cancel", WPAS_DBUS_NEW_IFACE_WPS, + (WPADBusMethodHandler) wpas_dbus_handler_wps_cancel, + { + END_ARGS + } + }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE, @@ -2617,6 +2802,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "Cancel", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_cancel, + { + END_ARGS + } + }, { "Invite", WPAS_DBUS_NEW_IFACE_P2PDEVICE, (WPADBusMethodHandler) wpas_dbus_handler_p2p_invite, { @@ -2637,6 +2828,13 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "RemoveClient", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_remove_client, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, { "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE, (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush, { @@ -3014,6 +3212,11 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "FindStopped", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + END_ARGS + } + }, { "ProvisionDiscoveryRequestDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { { "peer_object", "o", ARG_OUT }, @@ -3065,6 +3268,12 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "GroupFormationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "reason", "s", ARG_OUT }, + END_ARGS + } + }, { "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { { "properties", "a{sv}", ARG_OUT }, @@ -3080,7 +3289,8 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { { "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { { "path", "o", ARG_OUT }, - { "dev_passwd_id", "i", ARG_OUT }, + { "dev_passwd_id", "q", ARG_OUT }, + { "device_go_intent", "y", ARG_OUT }, END_ARGS } }, @@ -3128,6 +3338,12 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "InvitationReceived", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, #endif /* CONFIG_P2P */ #ifdef CONFIG_AP { "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, @@ -3174,6 +3390,11 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { }; +/** + * wpas_dbus_register_interface - Register an interface with D-Bus + * @wpa_s: wpa_supplicant interface structure + * Returns: 0 on success, -1 on failure + */ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) { @@ -3224,6 +3445,11 @@ err: } +/** + * wpas_dbus_unregister_interface - Unregister the interface from D-Bus + * @wpa_s: wpa_supplicant interface structure + * Returns: 0 on success, -1 on failure + */ int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) { struct wpas_dbus_priv *ctrl_iface; @@ -3265,6 +3491,22 @@ static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = { wpas_dbus_getter_p2p_peer_device_name, NULL }, + { "Manufacturer", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", + wpas_dbus_getter_p2p_peer_manufacturer, + NULL + }, + { "ModelName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", + wpas_dbus_getter_p2p_peer_modelname, + NULL + }, + { "ModelNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", + wpas_dbus_getter_p2p_peer_modelnumber, + NULL + }, + { "SerialNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", + wpas_dbus_getter_p2p_peer_serialnumber, + NULL + }, { "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", wpas_dbus_getter_p2p_peer_primary_device_type, NULL @@ -3345,7 +3587,7 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_new_path) return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, @@ -3372,7 +3614,7 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, /** * wpas_dbus_signal_peer_found - Send a peer found signal * @wpa_s: %wpa_supplicant network interface data - * @dev: peer device object + * @dev_addr: Peer P2P Device Address * * Notify listeners about find a p2p peer device found */ @@ -3387,7 +3629,7 @@ void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, /** * wpas_dbus_signal_peer_lost - Send a peer lost signal * @wpa_s: %wpa_supplicant network interface data - * @dev: peer device object + * @dev_addr: Peer P2P Device Address * * Notify listeners about lost a p2p peer device */ @@ -3402,7 +3644,7 @@ void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s, /** * wpas_dbus_register_peer - Register a discovered peer object with dbus * @wpa_s: wpa_supplicant interface structure - * @ssid: network configuration data + * @dev_addr: P2P Device Address of the peer * Returns: 0 on success, -1 on failure * * Registers network representing object with dbus @@ -3422,8 +3664,9 @@ int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr) if (ctrl_iface == NULL) return 0; - if (wpa_s->p2p_mgmt) - wpa_s = wpa_s->parent; + wpa_s = wpa_s->parent->parent; + if (!wpa_s->dbus_new_path) + return 0; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, @@ -3481,12 +3724,12 @@ int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, int ret; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL || - wpa_s->dbus_new_path == NULL) + if (wpa_s == NULL || wpa_s->global == NULL) return 0; - if (wpa_s->p2p_mgmt) - wpa_s = wpa_s->parent; + wpa_s = wpa_s->parent->parent; + if (!wpa_s->dbus_new_path) + return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) @@ -3504,6 +3747,42 @@ int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, } +/** + * wpas_dbus_signal_p2p_find_stopped - Send P2P Find stopped signal + * @wpa_s: %wpa_supplicant network interface data + * + * Notify listeners about P2P Find stopped + */ +void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "FindStopped"); + if (msg == NULL) + return; + + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_peer_groups_changed - Send peer group change property signal + * @wpa_s: %wpa_supplicant network interface data + * @dev_addr: P2P Device Address + * + * Notify listeners about peer Groups property changes. + */ void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s, const u8 *dev_addr) { @@ -3512,6 +3791,8 @@ void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(dev_addr)); @@ -3713,6 +3994,9 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s, /* Do nothing if the control interface is not turned on */ if (wpa_s == NULL || wpa_s->global == NULL) return 0; + wpa_s = wpa_s->parent->parent; + if (!wpa_s->dbus_new_path) + return 0; /* Make sure ssid is a persistent group */ if (ssid->disabled != 2 && !ssid->p2p_persistent_group) @@ -3797,15 +4081,13 @@ int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s, int ret; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL || - wpa_s->dbus_new_path == NULL) + if (wpa_s == NULL || wpa_s->global == NULL) return 0; - if (wpa_s->p2p_mgmt) - wpa_s = wpa_s->parent; + wpa_s = wpa_s->parent->parent; ctrl_iface = wpa_s->global->dbus; - if (ctrl_iface == NULL) + if (ctrl_iface == NULL || !wpa_s->dbus_new_path) return 0; os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h index d162d2b663d..6d240fffce7 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h @@ -152,6 +152,7 @@ void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s, void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail); void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s); +void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s); int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid); @@ -168,6 +169,7 @@ void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global); void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global); int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr); +void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s); void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, const u8 *dev_addr); int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, @@ -184,10 +186,13 @@ void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, u16 config_methods, unsigned int generated_pin); void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, - const u8 *src, u16 dev_passwd_id); + const u8 *src, u16 dev_passwd_id, + u8 go_intent); void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, int client, int network_id); +void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, + const char *reason); void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, @@ -228,6 +233,10 @@ void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *sta); void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, const u8 *sta); +void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *dev_addr, + const u8 *bssid, int id, + int op_freq); #else /* CONFIG_CTRL_IFACE_DBUS_NEW */ @@ -295,6 +304,11 @@ static inline void wpas_dbus_signal_wps_event_success( { } +static inline void wpas_dbus_signal_wps_event_pbc_overlap( + struct wpa_supplicant *wpa_s) +{ +} + static inline int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -377,10 +391,10 @@ wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, { } -static inline void wpas_dbus_signal_p2p_go_neg_req( - struct wpa_supplicant *wpa_s, - const u8 *src, - u16 dev_passwd_id) +static inline void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, + const u8 *src, + u16 dev_passwd_id, + u8 go_intent) { } @@ -391,6 +405,12 @@ wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, { } +static inline void +wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, + const char *reason) +{ +} + static inline void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) @@ -459,6 +479,11 @@ wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, { } +static inline void +wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s) +{ +} + static inline void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, const u8 *dev_addr) @@ -519,6 +544,14 @@ void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, { } +static inline +void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *dev_addr, + const u8 *bssid, int id, + int op_freq) +{ +} + #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #endif /* CTRL_IFACE_DBUS_H_NEW */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c index f2e62ca9638..67562a54717 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c @@ -157,7 +157,8 @@ static struct wpa_supplicant * get_iface_by_dbus_path( struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { - if (os_strcmp(wpa_s->dbus_new_path, path) == 0) + if (wpa_s->dbus_new_path && + os_strcmp(wpa_s->dbus_new_path, path) == 0) return wpa_s; } return NULL; @@ -213,7 +214,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, } else if (entry.type == DBUS_TYPE_STRING) { if (should_quote_opt(entry.key)) { size = os_strlen(entry.str_value); - if (size <= 0) + if (size == 0) goto error; size += 3; @@ -600,7 +601,7 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, iface.bridge_ifname = bridge_ifname; /* Otherwise, have wpa_supplicant attach to it. */ wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); - if (wpa_s) { + if (wpa_s && wpa_s->dbus_new_path) { const char *path = wpa_s->dbus_new_path; reply = dbus_message_new_method_return(message); @@ -684,7 +685,7 @@ DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, DBUS_TYPE_INVALID); wpa_s = wpa_supplicant_get_iface(global, ifname); - if (wpa_s == NULL) + if (wpa_s == NULL || wpa_s->dbus_new_path == NULL) return wpas_dbus_error_iface_unknown(message); path = wpa_s->dbus_new_path; @@ -876,8 +877,10 @@ dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, unsigned int i = 0, num = 0; dbus_bool_t success; - for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) - num++; + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s->dbus_new_path) + num++; + } paths = os_calloc(num, sizeof(char *)); if (!paths) { @@ -885,8 +888,10 @@ dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, return FALSE; } - for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) - paths[i++] = wpa_s->dbus_new_path; + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s->dbus_new_path) + paths[i++] = wpa_s->dbus_new_path; + } success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_OBJECT_PATH, @@ -1034,10 +1039,10 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len); - if (len > MAX_SSID_LEN) { + if (len > SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, "%s[dbus]: SSID too long (len=%d max_len=%d)", - __func__, len, MAX_SSID_LEN); + __func__, len, SSID_MAX_LEN); *reply = wpas_dbus_error_invalid_args( message, "Invalid SSID: too long"); return -1; @@ -1327,14 +1332,26 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, message, "You can specify only Channels in passive scan"); goto out; - } else if (params.freqs && params.freqs[0]) { - if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { - reply = wpas_dbus_error_scan_error( - message, "Scan request rejected"); - } } else { - wpa_s->scan_req = MANUAL_SCAN_REQ; - wpa_supplicant_req_scan(wpa_s, 0, 0); + if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, + "%s[dbus]: Stop ongoing sched_scan to allow requested scan to proceed", + __func__); + wpa_supplicant_cancel_sched_scan(wpa_s); + } + + if (params.freqs && params.freqs[0]) { + wpa_s->last_scan_req = MANUAL_SCAN_REQ; + if (wpa_supplicant_trigger_scan(wpa_s, + ¶ms)) { + reply = wpas_dbus_error_scan_error( + message, + "Scan request rejected"); + } + } else { + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } } } else if (os_strcmp(type, "active") == 0) { if (!params.num_ssids) { @@ -1344,6 +1361,14 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, #ifdef CONFIG_AUTOSCAN autoscan_deinit(wpa_s); #endif /* CONFIG_AUTOSCAN */ + if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, + "%s[dbus]: Stop ongoing sched_scan to allow requested scan to proceed", + __func__); + wpa_supplicant_cancel_sched_scan(wpa_s); + } + + wpa_s->last_scan_req = MANUAL_SCAN_REQ; if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { reply = wpas_dbus_error_scan_error( message, "Scan request rejected"); @@ -1478,7 +1503,8 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, dbus_message_iter_init(message, &iter); - ssid = wpa_config_add_network(wpa_s->conf); + if (wpa_s->dbus_new_path) + ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { wpa_printf(MSG_ERROR, "%s[dbus]: can't add new interface.", __func__); @@ -1576,6 +1602,30 @@ DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message, } +/** + * wpas_dbus_handler_reconnect - Reconnect if disconnected + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: InterfaceDisabled DBus error message if disabled + * or NULL otherwise. + * + * Handler function for "Reconnect" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_reconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_IFACE_DISABLED, + "This interface is disabled"); + } + + if (wpa_s->disconnected) + wpas_request_connection(wpa_s); + return NULL; +} + + /** * wpas_dbus_handler_remove_network - Remove a configured network * @message: Pointer to incoming dbus message @@ -1602,7 +1652,7 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, iface = wpas_dbus_new_decompose_object_path(op, WPAS_DBUS_NEW_NETWORKS_PART, &net_id); - if (iface == NULL || net_id == NULL || + if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; @@ -1715,7 +1765,7 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, iface = wpas_dbus_new_decompose_object_path(op, WPAS_DBUS_NEW_NETWORKS_PART, &net_id); - if (iface == NULL || net_id == NULL || + if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; @@ -1773,7 +1823,7 @@ DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message, iface = wpas_dbus_new_decompose_object_path(op, WPAS_DBUS_NEW_NETWORKS_PART, &net_id); - if (iface == NULL || net_id == NULL || + if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; @@ -2266,12 +2316,14 @@ DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path( message, DBUS_ERROR_FAILED, "Reinit of the EAPOL state machine with the new PKCS #11 engine and module path failed."); - wpa_dbus_mark_property_changed( - wpa_s->global->dbus, wpa_s->dbus_new_path, - WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath"); - wpa_dbus_mark_property_changed( - wpa_s->global->dbus, wpa_s->dbus_new_path, - WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath"); + if (wpa_s->dbus_new_path) { + wpa_dbus_mark_property_changed( + wpa_s->global->dbus, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath"); + wpa_dbus_mark_property_changed( + wpa_s->global->dbus, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath"); + } return NULL; } @@ -3024,7 +3076,7 @@ dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf; - if (wpa_s->current_bss) + if (wpa_s->current_bss && wpa_s->dbus_new_path) os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", wpa_s->dbus_new_path, wpa_s->current_bss->id); @@ -3052,7 +3104,7 @@ dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf; - if (wpa_s->current_ssid) + if (wpa_s->current_ssid && wpa_s->dbus_new_path) os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", wpa_s->dbus_new_path, wpa_s->current_ssid->id); @@ -3140,6 +3192,12 @@ dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error, unsigned int i = 0; dbus_bool_t success = FALSE; + if (!wpa_s->dbus_new_path) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: no D-Bus interface", __func__); + return FALSE; + } + paths = os_calloc(wpa_s->num_bss, sizeof(char *)); if (!paths) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); @@ -3191,6 +3249,12 @@ dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, unsigned int i = 0, num = 0; dbus_bool_t success = FALSE; + if (!wpa_s->dbus_new_path) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: no D-Bus interface", __func__); + return FALSE; + } + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) if (!network_is_persistent_group(ssid)) num++; @@ -3791,6 +3855,7 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, struct wpabuf *wps_ie; #endif /* CONFIG_WPS */ DBusMessageIter iter_dict, variant_iter; + int wps_support = 0; const char *type = ""; res = get_bss_helper(args, error, __func__); @@ -3805,6 +3870,7 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, #ifdef CONFIG_WPS wps_ie = wpa_bss_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE); if (wps_ie) { + wps_support = 1; if (wps_is_selected_pbc_registrar(wps_ie)) type = "pbc"; else if (wps_is_selected_pin_registrar(wps_ie)) @@ -3814,7 +3880,7 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, } #endif /* CONFIG_WPS */ - if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type) || + if ((wps_support && !wpa_dbus_dict_append_string(&iter_dict, "Type", type)) || !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) || !dbus_message_iter_close_container(iter, &variant_iter)) goto nomem; @@ -4102,7 +4168,7 @@ void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, struct wpas_dbus_priv *priv = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (priv == NULL) + if (priv == NULL || !wpa_s->dbus_new_path) return; if (wpa_s->preq_notify_peer == NULL) diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h index 6113db50039..50f72ec507b 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h @@ -107,6 +107,9 @@ DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_reconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -291,6 +294,9 @@ dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter, DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message, + struct wpa_supplicant *wpa_s); + dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, DBusError *error, void *user_data); diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 0eff76386fa..67c079e7506 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -127,8 +127,7 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); } - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types, NULL, 0, 0, NULL, 0); @@ -147,10 +146,7 @@ error: DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message, struct wpa_supplicant *wpa_s) { - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; - - wpas_p2p_stop_find(wpa_s); + wpas_p2p_stop_find(wpa_s->global->p2p_init_wpa_s); return NULL; } @@ -168,8 +164,7 @@ DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message, if (parse_peer_object_path(peer_object_path, peer_addr) < 0) return wpas_dbus_error_invalid_args(message, NULL); - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (wpas_p2p_reject(wpa_s, peer_addr) < 0) return wpas_dbus_error_unknown_error(message, @@ -188,8 +183,7 @@ DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message, DBUS_TYPE_INVALID)) return wpas_dbus_error_no_memory(message); - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (wpas_p2p_listen(wpa_s, (unsigned int) timeout)) { return dbus_message_new_error(message, @@ -230,8 +224,7 @@ DBusMessage * wpas_dbus_handler_p2p_extendedlisten( wpa_dbus_dict_entry_clear(&entry); } - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (wpas_p2p_ext_listen(wpa_s, period, interval)) return wpas_dbus_error_unknown_error( @@ -282,9 +275,6 @@ DBusMessage * wpas_dbus_handler_p2p_presence_request( wpa_dbus_dict_entry_clear(&entry); } - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; - if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0) return wpas_dbus_error_unknown_error(message, "Failed to invoke presence request."); @@ -339,8 +329,7 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); } - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (pg_object_path != NULL) { char *net_id_str; @@ -354,7 +343,8 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, pg_object_path, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, &net_id_str); if (iface == NULL || net_id_str == NULL || - os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + !wpa_s->parent->dbus_new_path || + os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, pg_object_path); @@ -374,7 +364,7 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, goto inv_args; if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0, - NULL, 0)) { + NULL, 0, 0)) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); @@ -426,6 +416,64 @@ static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s, } +DBusMessage * wpas_dbus_handler_p2p_remove_client(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + char *peer_object_path = NULL; + char *interface_addr = NULL; + u8 peer_addr[ETH_ALEN]; + + if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) + return reply; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto err; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto err; + + if (os_strcmp(entry.key, "peer") == 0 && + entry.type == DBUS_TYPE_OBJECT_PATH) { + os_free(peer_object_path); + peer_object_path = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "iface") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(interface_addr); + interface_addr = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + } else { + wpa_dbus_dict_entry_clear(&entry); + goto err; + } + } + + if ((!peer_object_path && !interface_addr) || + (peer_object_path && + (parse_peer_object_path(peer_object_path, peer_addr) < 0 || + !p2p_peer_known(wpa_s->global->p2p, peer_addr))) || + (interface_addr && hwaddr_aton(interface_addr, peer_addr) < 0)) + goto err; + + wpas_p2p_remove_client(wpa_s, peer_addr, interface_addr != NULL); + reply = NULL; +out: + os_free(peer_object_path); + os_free(interface_addr); + return reply; +err: + reply = wpas_dbus_error_invalid_args(message, "Invalid address format"); + goto out; +} + + DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message, struct wpa_supplicant *wpa_s) { @@ -434,8 +482,7 @@ DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message, if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) return reply; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; @@ -531,8 +578,7 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, if ((!pin || !pin[0]) && wps_method == WPS_PIN_KEYPAD) goto inv_args; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, 0, join, authorize_only, @@ -587,6 +633,26 @@ inv_args: } +/** + * wpas_dbus_handler_p2p_cancel - Cancel P2P group formation + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: NULL on success or DBus error on failure + * + * Handler for "Cancel" method call. Returns NULL if P2P cancel succeeds or DBus + * error on P2P cancel failure + */ +DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpas_p2p_cancel(wpa_s)) + return wpas_dbus_error_unknown_error(message, + "P2P cancel failed"); + + return NULL; +} + + DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, struct wpa_supplicant *wpa_s) { @@ -634,8 +700,7 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, !p2p_peer_known(wpa_s->global->p2p, peer_addr)) goto err; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (persistent) { char *net_id_str; @@ -649,7 +714,8 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, &net_id_str); if (iface == NULL || net_id_str == NULL || - os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + !wpa_s->parent->dbus_new_path || + os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, pg_object_path); goto out; @@ -726,8 +792,7 @@ DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message, os_strcmp(config_method, "pushbutton")) return wpas_dbus_error_invalid_args(message, NULL); - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0) @@ -758,8 +823,7 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) return FALSE; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}", &variant_iter) || @@ -864,8 +928,7 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) return FALSE; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; dbus_message_iter_recurse(iter, &variant_iter); if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error)) @@ -1043,7 +1106,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error, char **peer_obj_paths = NULL; - if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) + if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error) || + !wpa_s->parent->parent->dbus_new_path) return FALSE; dl_list_init(&peer_objpath_list); @@ -1064,7 +1128,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error, os_snprintf(node->path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->dbus_new_path, MAC2STR(addr)); + wpa_s->parent->parent->dbus_new_path, + MAC2STR(addr)); dl_list_add_tail(&peer_objpath_list, &node->list); num++; @@ -1184,13 +1249,17 @@ dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + if (!wpa_s->parent->parent->dbus_new_path) + return FALSE; + if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_CLIENT) os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/"); else os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->dbus_new_path, MAC2STR(wpa_s->go_dev_addr)); + wpa_s->parent->parent->dbus_new_path, + MAC2STR(wpa_s->go_dev_addr)); path = go_peer_obj_path; return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH, @@ -1240,6 +1309,154 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, } +dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + char *tmp; + + if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) + return FALSE; + + /* get the peer info */ + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); + return FALSE; + } + + tmp = os_strdup(info->manufacturer); + if (!tmp) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, + error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + os_free(tmp); + return FALSE; + } + + os_free(tmp); + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + char *tmp; + + if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) + return FALSE; + + /* get the peer info */ + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); + return FALSE; + } + + tmp = os_strdup(info->model_name); + if (!tmp) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, + error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + os_free(tmp); + return FALSE; + } + + os_free(tmp); + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + char *tmp; + + if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) + return FALSE; + + /* get the peer info */ + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); + return FALSE; + } + + tmp = os_strdup(info->model_number); + if (!tmp) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, + error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + os_free(tmp); + return FALSE; + } + + os_free(tmp); + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + char *tmp; + + if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) + return FALSE; + + /* get the peer info */ + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); + return FALSE; + } + + tmp = os_strdup(info->serial_number); + if (!tmp) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, + error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + os_free(tmp); + return FALSE; + } + + os_free(tmp); + return TRUE; +} + + dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( DBusMessageIter *iter, DBusError *error, void *user_data) { @@ -1578,8 +1795,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter, os_memset(&data, 0, sizeof(data)); wpa_s = peer_args->wpa_s; - if (wpa_s->p2p_dev) - wpa_s = wpa_s->p2p_dev; + wpa_s = wpa_s->global->p2p_init_wpa_s; wpa_s_go = wpas_get_p2p_client_iface(wpa_s, info->p2p_device_addr); if (wpa_s_go) { @@ -1636,6 +1852,10 @@ dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, unsigned int i = 0, num = 0; dbus_bool_t success = FALSE; + wpa_s = wpa_s->global->p2p_init_wpa_s; + if (!wpa_s->parent->dbus_new_path) + return FALSE; + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) if (network_is_persistent_group(ssid)) num++; @@ -1659,7 +1879,7 @@ dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, /* Construct the object path for this network. */ os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d", - wpa_s->dbus_new_path, ssid->id); + wpa_s->parent->dbus_new_path, ssid->id); } success = wpas_dbus_simple_array_property_getter(iter, @@ -1698,7 +1918,7 @@ dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter, /** - * wpas_dbus_setter_persistent_group_properties - Get options for a persistent + * wpas_dbus_setter_persistent_group_properties - Set options for a persistent * group * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure @@ -1746,7 +1966,9 @@ DBusMessage * wpas_dbus_handler_add_persistent_group( dbus_message_iter_init(message, &iter); - ssid = wpa_config_add_network(wpa_s->conf); + wpa_s = wpa_s->global->p2p_init_wpa_s; + if (wpa_s->parent->dbus_new_path) + ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { wpa_printf(MSG_ERROR, "dbus: %s: Cannot add new persistent group", @@ -1779,7 +2001,7 @@ DBusMessage * wpas_dbus_handler_add_persistent_group( /* Construct the object path for this network. */ os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d", - wpa_s->dbus_new_path, ssid->id); + wpa_s->parent->dbus_new_path, ssid->id); reply = dbus_message_new_method_return(message); if (reply == NULL) { @@ -1826,6 +2048,8 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, DBUS_TYPE_INVALID); + wpa_s = wpa_s->global->p2p_init_wpa_s; + /* * Extract the network ID and ensure the network is actually a child of * this interface. @@ -1834,7 +2058,8 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( op, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, &persistent_group_id); if (iface == NULL || persistent_group_id == NULL || - os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + !wpa_s->parent->dbus_new_path || + os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } @@ -1899,6 +2124,8 @@ DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( struct wpa_ssid *ssid, *next; struct wpa_config *config; + wpa_s = wpa_s->global->p2p_init_wpa_s; + config = wpa_s->conf; ssid = config->ssid; while (ssid) { @@ -1928,6 +2155,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, const u8 *addr; dbus_bool_t success = FALSE; + if (!wpa_s->parent->parent->dbus_new_path) + return FALSE; + /* Verify correct role for this property */ if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_GO) { return wpas_dbus_simple_array_property_getter( @@ -1955,7 +2185,8 @@ dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->parent->dbus_new_path, MAC2STR(addr)); + wpa_s->parent->parent->dbus_new_path, + MAC2STR(addr)); i++; } diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h index fdaccbafb14..2aecbbe4650 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -46,6 +46,9 @@ DBusMessage *wpas_dbus_handler_p2p_connect( DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage *wpas_dbus_handler_p2p_invite( DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -53,6 +56,9 @@ DBusMessage *wpas_dbus_handler_p2p_invite( DBusMessage *wpas_dbus_handler_p2p_disconnect( DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_p2p_remove_client( + DBusMessage *message, struct wpa_supplicant *wpa_s); + DBusMessage *wpas_dbus_handler_p2p_flush( DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -112,6 +118,22 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter, + DBusError *error, + void *user_data); + dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( DBusMessageIter *iter, DBusError *error, void *user_data); diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c index a94a0e51fc2..b2251baa3fe 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -53,7 +53,7 @@ static int wpas_dbus_handler_wps_role(DBusMessage *message, else if (os_strcmp(val, "registrar") == 0) params->role = 2; else { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Uknown role %s", val); + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Unknown role %s", val); *reply = wpas_dbus_error_invalid_args(message, val); return -1; } @@ -113,7 +113,7 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message, dbus_message_iter_recurse(&variant_iter, &array_iter); dbus_message_iter_get_fixed_array(&array_iter, ¶ms->bssid, &len); if (len != ETH_ALEN) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length %d", + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid length %d", len); *reply = wpas_dbus_error_invalid_args(message, "Bssid is wrong length"); @@ -319,6 +319,26 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, } +/** + * wpas_dbus_handler_wps_cancel - Cancel ongoing WPS configuration + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: NULL on success or DBus error on failure + * + * Handler for "Cancel" method call. Returns NULL if WPS cancel successfull + * or DBus error on WPS cancel failure + */ +DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpas_wps_cancel(wpa_s)) + return wpas_dbus_error_unknown_error(message, + "WPS cancel failed"); + + return NULL; +} + + /** * wpas_dbus_getter_process_credentials - Check if credentials are processed * @message: Pointer to incoming dbus message @@ -358,6 +378,8 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; dbus_bool_t process_credentials, old_pc; + if (!wpa_s->dbus_new_path) + return FALSE; if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN, &process_credentials)) return FALSE; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c index 15b090141c9..45623f34646 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c @@ -46,8 +46,13 @@ static dbus_bool_t fill_dict_with_properties( goto error; /* An error getting a property fails the request entirely */ - if (!dsc->getter(&entry_iter, error, user_data)) + if (!dsc->getter(&entry_iter, error, user_data)) { + wpa_printf(MSG_INFO, + "dbus: %s dbus_interface=%s dbus_property=%s getter failed", + __func__, dsc->dbus_interface, + dsc->dbus_property); return FALSE; + } if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) goto error; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c index 6209c67856b..fba57e6361a 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c @@ -257,7 +257,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *message, DBusMessage *reply; struct wpabuf *xml; - xml = wpabuf_alloc(10000); + xml = wpabuf_alloc(15000); if (xml == NULL) return NULL; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old.c b/contrib/wpa/wpa_supplicant/dbus/dbus_old.c index 45bb4022702..88227af7c03 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_old.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old.c @@ -383,7 +383,7 @@ void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) DBusMessage *_signal; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_path) return; _signal = dbus_message_new_signal(wpa_s->dbus_path, @@ -474,7 +474,7 @@ void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; /* Do nothing if the control interface is not turned on */ - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_path) return; _signal = dbus_message_new_signal(wpa_s->dbus_path, @@ -509,7 +509,7 @@ void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, if (wpa_s->global == NULL) return; iface = wpa_s->global->dbus; - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_path) return; _signal = dbus_message_new_signal(wpa_s->dbus_path, @@ -559,7 +559,7 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, if (wpa_s->global == NULL) return; iface = wpa_s->global->dbus; - if (iface == NULL) + if (iface == NULL || !wpa_s->dbus_path) return; _signal = dbus_message_new_signal(wpa_s->dbus_path, @@ -738,7 +738,7 @@ struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { - if (strcmp(wpa_s->dbus_path, path) == 0) + if (wpa_s->dbus_path && strcmp(wpa_s->dbus_path, path) == 0) return wpa_s; } return NULL; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c index 773ee8b49a2..e8f62ef6bdc 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c @@ -166,7 +166,7 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, iface.bridge_ifname = bridge_ifname; /* Otherwise, have wpa_supplicant attach to it. */ wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); - if (wpa_s) { + if (wpa_s && wpa_s->dbus_path) { const char *path = wpa_s->dbus_path; reply = dbus_message_new_method_return(message); @@ -262,7 +262,7 @@ DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, } wpa_s = wpa_supplicant_get_iface(global, ifname); - if (wpa_s == NULL) { + if (wpa_s == NULL || !wpa_s->dbus_path) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; } @@ -354,6 +354,11 @@ DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, DBusMessageIter sub_iter; struct wpa_bss *bss; + if (!wpa_s->dbus_path) + return dbus_message_new_error(message, + WPAS_ERROR_INTERNAL_ERROR, + "no D-Bus interface available"); + /* Create and initialize the return message */ reply = dbus_message_new_method_return(message); dbus_message_iter_init_append(reply, &iter); @@ -495,7 +500,7 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, /* EAP methods */ eap_methods = eap_get_names_as_string_array(&num_items); if (eap_methods) { - dbus_bool_t success = FALSE; + dbus_bool_t success; size_t i = 0; success = wpa_dbus_dict_append_string_array( @@ -708,10 +713,11 @@ DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; - struct wpa_ssid *ssid; + struct wpa_ssid *ssid = NULL; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; - ssid = wpa_config_add_network(wpa_s->conf); + if (wpa_s->dbus_path) + ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { reply = dbus_message_new_error( message, WPAS_ERROR_ADD_NETWORK_ERROR, @@ -769,7 +775,7 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, } /* Ensure the network is actually a child of this interface */ - if (os_strcmp(iface, wpa_s->dbus_path) != 0) { + if (!wpa_s->dbus_path || os_strcmp(iface, wpa_s->dbus_path) != 0) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } @@ -803,10 +809,10 @@ out: } -static const char const *dont_quote[] = { +static const char * const dont_quote[] = { "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", - "bssid", NULL + "bssid", "scan_freq", "freq_list", NULL }; @@ -878,7 +884,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, if (should_quote_opt(entry.key)) { size = os_strlen(entry.str_value); /* Zero-length option check */ - if (size <= 0) + if (size == 0) goto error; size += 3; /* For quotes and terminator */ value = os_zalloc(size); @@ -1020,7 +1026,7 @@ DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, goto out; } /* Ensure the object path really points to this interface */ - if (network == NULL || + if (network == NULL || !wpa_s->dbus_path || os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) { reply = wpas_dbus_new_invalid_network_error(message); goto out; diff --git a/contrib/wpa/wpa_supplicant/defconfig b/contrib/wpa/wpa_supplicant/defconfig index 7f627fdd614..01a8c2ccb00 100644 --- a/contrib/wpa/wpa_supplicant/defconfig +++ b/contrib/wpa/wpa_supplicant/defconfig @@ -495,3 +495,12 @@ CONFIG_PEERKEY=y # # External password backend for testing purposes (developer use) #CONFIG_EXT_PASSWORD_TEST=y + +# Enable Fast Session Transfer (FST) +#CONFIG_FST=y + +# Enable CLI commands for FST testing +#CONFIG_FST_TEST=y + +# OS X builds. This is only for building eapol_test. +#CONFIG_OSX=y diff --git a/contrib/wpa/wpa_supplicant/driver_i.h b/contrib/wpa/wpa_supplicant/driver_i.h index 65b430d4a6c..73768c756f0 100644 --- a/contrib/wpa/wpa_supplicant/driver_i.h +++ b/contrib/wpa/wpa_supplicant/driver_i.h @@ -286,11 +286,13 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s, - const u8 *data, size_t data_len, int noack) + const u8 *data, size_t data_len, int noack, + unsigned int freq) { if (wpa_s->driver->send_mlme) return wpa_s->driver->send_mlme(wpa_s->drv_priv, - data, data_len, noack); + data, data_len, noack, + freq); return -1; } @@ -503,13 +505,6 @@ static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s, proberesp, assocresp); } -static inline int wpa_drv_shared_freq(struct wpa_supplicant *wpa_s) -{ - if (!wpa_s->driver->shared_freq) - return -1; - return wpa_s->driver->shared_freq(wpa_s->drv_priv); -} - static inline int wpa_drv_get_noa(struct wpa_supplicant *wpa_s, u8 *buf, size_t buf_len) { @@ -890,4 +885,31 @@ static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_MACSEC */ +static inline int wpa_drv_setband(struct wpa_supplicant *wpa_s, + enum set_band band) +{ + if (!wpa_s->driver->set_band) + return -1; + return wpa_s->driver->set_band(wpa_s->drv_priv, band); +} + +static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type if_type, + unsigned int *num, + unsigned int *freq_list) +{ + if (!wpa_s->driver->get_pref_freq_list) + return -1; + return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type, + num, freq_list); +} + +static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + if (!wpa_s->driver->set_prob_oper_freq) + return 0; + return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq); +} + #endif /* DRIVER_I_H */ diff --git a/contrib/wpa/wpa_supplicant/eapol_test.c b/contrib/wpa/wpa_supplicant/eapol_test.c index 9b7af30550b..dce7d1fadd4 100644 --- a/contrib/wpa/wpa_supplicant/eapol_test.c +++ b/contrib/wpa/wpa_supplicant/eapol_test.c @@ -30,7 +30,7 @@ #include "wpas_glue.h" -struct wpa_driver_ops *wpa_drivers[] = { NULL }; +const struct wpa_driver_ops *const wpa_drivers[] = { NULL }; struct extra_radius_attr { @@ -76,6 +76,9 @@ struct eapol_test_data { const char *pcsc_reader; const char *pcsc_pin; + + unsigned int ctrl_iface:1; + unsigned int id_req_sent:1; }; static struct eapol_test_data eapol_test; @@ -329,7 +332,11 @@ eapol_test_get_config_blob(void *ctx, const char *name) static void eapol_test_eapol_done_cb(void *ctx) { + struct eapol_test_data *e = ctx; + printf("WPA: EAPOL processing complete\n"); + wpa_supplicant_cancel_auth_timeout(e->wpa_s); + wpa_supplicant_set_state(e->wpa_s, WPA_COMPLETED); } @@ -407,6 +414,9 @@ static void eapol_sm_cb(struct eapol_sm *eapol, enum eapol_supp_result result, { struct eapol_test_data *e = ctx; printf("eapol_sm_cb: result=%d\n", result); + e->id_req_sent = 0; + if (e->ctrl_iface) + return; e->eapol_test_num_reauths--; if (e->eapol_test_num_reauths < 0) eloop_terminate(); @@ -552,11 +562,21 @@ static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len) } +static enum wpa_states eapol_test_get_state(void *ctx) +{ + struct eapol_test_data *e = ctx; + struct wpa_supplicant *wpa_s = e->wpa_s; + + return wpa_s->wpa_state; +} + + static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct eapol_config eapol_conf; struct eapol_ctx *ctx; + struct wpa_sm_ctx *wctx; ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) { @@ -590,6 +610,25 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, return -1; } + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA; + wctx = os_zalloc(sizeof(*wctx)); + if (wctx == NULL) { + os_free(ctx); + return -1; + } + wctx->ctx = e; + wctx->msg_ctx = wpa_s; + wctx->get_state = eapol_test_get_state; + wpa_s->wpa = wpa_sm_init(wctx); + if (!wpa_s->wpa) { + os_free(ctx); + os_free(wctx); + return -1; + } + + if (!ssid) + return 0; + wpa_s->current_ssid = ssid; os_memset(&eapol_conf, 0, sizeof(eapol_conf)); eapol_conf.accept_802_1x_keys = 1; @@ -614,6 +653,8 @@ static void test_eapol_clean(struct eapol_test_data *e, { struct extra_radius_attr *p, *prev; + wpa_sm_deinit(wpa_s->wpa); + wpa_s->wpa = NULL; radius_client_deinit(e->radius); wpabuf_free(e->last_eap_radius); radius_msg_free(e->last_recv_radius); @@ -757,6 +798,8 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) break; case EAP_CODE_FAILURE: os_strlcpy(buf, "EAP Failure", sizeof(buf)); + if (e->ctrl_iface) + break; eloop_terminate(); break; default: @@ -901,25 +944,66 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT && e->eapol_test_num_reauths < 0) || hdr->code == RADIUS_CODE_ACCESS_REJECT) { - eloop_terminate(); + if (!e->ctrl_iface) + eloop_terminate(); } return RADIUS_RX_QUEUED; } +static int driver_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int driver_get_bssid(void *priv, u8 *bssid) +{ + struct eapol_test_data *e = priv; + + if (e->ctrl_iface && !e->id_req_sent) { + eloop_register_timeout(0, 0, send_eap_request_identity, + e->wpa_s, NULL); + e->id_req_sent = 1; + } + + os_memset(bssid, 0, ETH_ALEN); + bssid[5] = 1; + return 0; +} + + +static int driver_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_WIRED; + return 0; +} + + +struct wpa_driver_ops eapol_test_drv_ops = { + .name = "test", + .get_ssid = driver_get_ssid, + .get_bssid = driver_get_bssid, + .get_capa = driver_get_capa, +}; + static void wpa_init_conf(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, const char *authsrv, int port, const char *secret, - const char *cli_addr) + const char *cli_addr, const char *ifname) { struct hostapd_radius_server *as; int res; + wpa_s->driver = &eapol_test_drv_ops; + wpa_s->drv_priv = e; wpa_s->bssid[5] = 1; os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN); e->own_ip_addr.s_addr = htonl((127 << 24) | 1); - os_strlcpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname)); + os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers)); assert(e->radius_conf != NULL); @@ -938,13 +1022,12 @@ static void wpa_init_conf(struct eapol_test_data *e, *pos++ = a[3]; } #else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ - if (inet_aton(authsrv, &as->addr.u.v4) < 0) { + if (hostapd_parse_ip_addr(authsrv, &as->addr) < 0) { wpa_printf(MSG_ERROR, "Invalid IP address '%s'", authsrv); assert(0); } #endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ - as->addr.af = AF_INET; as->port = port; as->shared_secret = (u8 *) os_strdup(secret); as->shared_secret_len = os_strlen(secret); @@ -1162,7 +1245,7 @@ static void usage(void) " [-M] [-o] [-R] " "[-P] \\\n" - " [-A]\n" + " [-A] [-i] [-T]\n" "eapol_test scard\n" "eapol_test sim [debug]\n" "\n"); @@ -1217,6 +1300,8 @@ int main(int argc, char *argv[]) int timeout = 30; char *pos; struct extra_radius_attr *p = NULL, *p1; + const char *ifname = "test"; + const char *ctrl_iface = NULL; if (os_program_init()) return -1; @@ -1232,7 +1317,7 @@ int main(int argc, char *argv[]) wpa_debug_show_keys = 1; for (;;) { - c = getopt(argc, argv, "a:A:c:C:eM:nN:o:p:P:r:R:s:St:W"); + c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:W"); if (c < 0) break; switch (c) { @@ -1251,6 +1336,9 @@ int main(int argc, char *argv[]) case 'e': eapol_test.req_eap_key_name = 1; break; + case 'i': + ifname = optarg; + break; case 'M': if (hwaddr_aton(optarg, eapol_test.own_addr)) { usage(); @@ -1291,6 +1379,10 @@ int main(int argc, char *argv[]) case 't': timeout = atoi(optarg); break; + case 'T': + ctrl_iface = optarg; + eapol_test.ctrl_iface = 1; + break; case 'W': wait_for_monitor++; break; @@ -1337,7 +1429,7 @@ int main(int argc, char *argv[]) &argv[optind + 1]); } - if (conf == NULL) { + if (conf == NULL && !ctrl_iface) { usage(); printf("Configuration file is required.\n"); return -1; @@ -1359,12 +1451,15 @@ int main(int argc, char *argv[]) eapol_test.wpa_s = &wpa_s; dl_list_init(&wpa_s.bss); dl_list_init(&wpa_s.bss_id); - wpa_s.conf = wpa_config_read(conf, NULL); + if (conf) + wpa_s.conf = wpa_config_read(conf, NULL); + else + wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL); if (wpa_s.conf == NULL) { printf("Failed to parse configuration file '%s'.\n", conf); return -1; } - if (wpa_s.conf->ssid == NULL) { + if (!ctrl_iface && wpa_s.conf->ssid == NULL) { printf("No networks defined.\n"); return -1; } @@ -1375,7 +1470,7 @@ int main(int argc, char *argv[]) } wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret, - cli_addr); + cli_addr, ifname); wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s); if (wpa_s.ctrl_iface == NULL) { printf("Failed to initialize control interface '%s'.\n" @@ -1388,7 +1483,8 @@ int main(int argc, char *argv[]) wpa_s.conf->ctrl_interface); return -1; } - if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid)) + if (wpa_s.conf->ssid && + wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid)) return -1; if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid)) @@ -1400,9 +1496,12 @@ int main(int argc, char *argv[]) if (wait_for_monitor) wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface); - eloop_register_timeout(timeout, 0, eapol_test_timeout, &eapol_test, - NULL); - eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL); + if (!ctrl_iface) { + eloop_register_timeout(timeout, 0, eapol_test_timeout, + &eapol_test, NULL); + eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, + NULL); + } eloop_register_signal_terminate(eapol_test_terminate, &wpa_s); eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s); eloop_run(); diff --git a/contrib/wpa/wpa_supplicant/eapol_test.py b/contrib/wpa/wpa_supplicant/eapol_test.py new file mode 100755 index 00000000000..80e7dfcf531 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/eapol_test.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python2 +# +# eapol_test controller +# Copyright (c) 2015, Jouni Malinen +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import argparse +import logging +import os +import Queue +import sys +import threading + +logger = logging.getLogger() +dir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__)) +sys.path.append(os.path.join(dir, '..', 'wpaspy')) +import wpaspy +wpas_ctrl = '/tmp/eapol_test' + +class eapol_test: + def __init__(self, ifname): + self.ifname = ifname + self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname)) + if "PONG" not in self.ctrl.request("PING"): + raise Exception("Failed to connect to eapol_test (%s)" % ifname) + self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname)) + self.mon.attach() + + def add_network(self): + id = self.request("ADD_NETWORK") + if "FAIL" in id: + raise Exception("ADD_NETWORK failed") + return int(id) + + def remove_network(self, id): + id = self.request("REMOVE_NETWORK " + str(id)) + if "FAIL" in id: + raise Exception("REMOVE_NETWORK failed") + return None + + def set_network(self, id, field, value): + res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value) + if "FAIL" in res: + raise Exception("SET_NETWORK failed") + return None + + def set_network_quoted(self, id, field, value): + res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"') + if "FAIL" in res: + raise Exception("SET_NETWORK failed") + return None + + def request(self, cmd, timeout=10): + return self.ctrl.request(cmd, timeout=timeout) + + def wait_event(self, events, timeout=10): + start = os.times()[4] + while True: + while self.mon.pending(): + ev = self.mon.recv() + logger.debug(self.ifname + ": " + ev) + for event in events: + if event in ev: + return ev + now = os.times()[4] + remaining = start + timeout - now + if remaining <= 0: + break + if not self.mon.pending(timeout=remaining): + break + return None + +def run(ifname, count, no_fast_reauth, res): + et = eapol_test(ifname) + + et.request("AP_SCAN 0") + if no_fast_reauth: + et.request("SET fast_reauth 0") + else: + et.request("SET fast_reauth 1") + id = et.add_network() + et.set_network(id, "key_mgmt", "IEEE8021X") + et.set_network(id, "eapol_flags", "0") + et.set_network(id, "eap", "TLS") + et.set_network_quoted(id, "identity", "user") + et.set_network_quoted(id, "ca_cert", 'ca.pem') + et.set_network_quoted(id, "client_cert", 'client.pem') + et.set_network_quoted(id, "private_key", 'client.key') + et.set_network_quoted(id, "private_key_passwd", 'whatever') + et.set_network(id, "disabled", "0") + + fail = False + for i in range(count): + et.request("REASSOCIATE") + ev = et.wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-EAP-FAILURE"]) + if ev is None or "CTRL-EVENT-CONNECTED" not in ev: + fail = True + break + + et.remove_network(id) + + if fail: + res.put("FAIL (%d OK)" % i) + else: + res.put("PASS %d" % (i + 1)) + +def main(): + parser = argparse.ArgumentParser(description='eapol_test controller') + parser.add_argument('--ctrl', help='control interface directory') + parser.add_argument('--num', help='number of processes') + parser.add_argument('--iter', help='number of iterations') + parser.add_argument('--no-fast-reauth', action='store_true', + dest='no_fast_reauth', + help='disable TLS session resumption') + args = parser.parse_args() + + num = int(args.num) + iter = int(args.iter) + if args.ctrl: + global wpas_ctrl + wpas_ctrl = args.ctrl + + t = {} + res = {} + for i in range(num): + res[i] = Queue.Queue() + t[i] = threading.Thread(target=run, args=(str(i), iter, + args.no_fast_reauth, res[i])) + for i in range(num): + t[i].start() + for i in range(num): + t[i].join() + try: + results = res[i].get(False) + except: + results = "N/A" + print "%d: %s" % (i, results) + +if __name__ == "__main__": + main() diff --git a/contrib/wpa/wpa_supplicant/events.c b/contrib/wpa/wpa_supplicant/events.c index d275ca424e6..3af1c7d89c6 100644 --- a/contrib/wpa/wpa_supplicant/events.c +++ b/contrib/wpa/wpa_supplicant/events.c @@ -23,6 +23,7 @@ #include "eap_peer/eap.h" #include "ap/hostapd.h" #include "p2p/p2p.h" +#include "fst/fst.h" #include "wnm_sta.h" #include "notify.h" #include "common/ieee802_11_defs.h" @@ -71,6 +72,59 @@ static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, } +/** + * wpas_reenabled_network_time - Time until first network is re-enabled + * @wpa_s: Pointer to wpa_supplicant data + * Returns: If all enabled networks are temporarily disabled, returns the time + * (in sec) until the first network is re-enabled. Otherwise returns 0. + * + * This function is used in case all enabled networks are temporarily disabled, + * in which case it returns the time (in sec) that the first network will be + * re-enabled. The function assumes that at least one network is enabled. + */ +static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + int disabled_for, res = 0; + +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking && + wpa_s->conf->cred) + return 0; +#endif /* CONFIG_INTERWORKING */ + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid->disabled) + continue; + + disabled_for = wpas_temp_disabled(wpa_s, ssid); + if (!disabled_for) + return 0; + + if (!res || disabled_for < res) + res = disabled_for; + } + + return res; +} + + +void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->disconnected || wpa_s->wpa_state != WPA_SCANNING) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, + "Try to associate due to network getting re-enabled"); + if (wpa_supplicant_fast_associate(wpa_s) != 1) { + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } +} + + static struct wpa_bss * wpa_supplicant_get_new_bss( struct wpa_supplicant *wpa_s, const u8 *bssid) { @@ -105,11 +159,32 @@ static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s) static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *old_ssid; + u8 drv_ssid[SSID_MAX_LEN]; + size_t drv_ssid_len; int res; if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) { wpa_supplicant_update_current_bss(wpa_s); - return 0; + + if (wpa_s->current_ssid->ssid_len == 0) + return 0; /* current profile still in use */ + res = wpa_drv_get_ssid(wpa_s, drv_ssid); + if (res < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Failed to read SSID from driver"); + return 0; /* try to use current profile */ + } + drv_ssid_len = res; + + if (drv_ssid_len == wpa_s->current_ssid->ssid_len && + os_memcmp(drv_ssid, wpa_s->current_ssid->ssid, + drv_ssid_len) == 0) + return 0; /* current profile still in use */ + + wpa_msg(wpa_s, MSG_DEBUG, + "Driver-initiated BSS selection changed the SSID to %s", + wpa_ssid_txt(drv_ssid, drv_ssid_len)); + /* continue selecting a new network profile */ } wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association " @@ -212,9 +287,6 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); sme_clear_on_disassoc(wpa_s); -#ifdef CONFIG_P2P - os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); -#endif /* CONFIG_P2P */ wpa_s->current_bss = NULL; wpa_s->assoc_freq = 0; @@ -756,9 +828,9 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, osen = ie != NULL; wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' " - "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%s", + "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s", i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), - wpa_ie_len, rsn_ie_len, bss->caps, bss->level, + wpa_ie_len, rsn_ie_len, bss->caps, bss->level, bss->freq, wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "", (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ? @@ -964,6 +1036,19 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, */ #endif /* CONFIG_P2P */ + if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time)) + { + struct os_reltime diff; + + os_reltime_sub(&wpa_s->scan_min_time, + &bss->last_update, &diff); + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - scan result not recent enough (%u.%06u seconds too old)", + (unsigned int) diff.sec, + (unsigned int) diff.usec); + continue; + } + /* Matching configuration found */ return ssid; } @@ -1011,14 +1096,13 @@ struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, struct wpa_bss *selected = NULL; int prio; struct wpa_ssid *next_ssid = NULL; + struct wpa_ssid *ssid; if (wpa_s->last_scan_res == NULL || wpa_s->last_scan_res_used == 0) return NULL; /* no scan results from last update */ if (wpa_s->next_ssid) { - struct wpa_ssid *ssid; - /* check that next_ssid is still valid */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (ssid == wpa_s->next_ssid) @@ -1054,6 +1138,27 @@ struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, break; } + ssid = *selected_ssid; + if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set && + !ssid->passphrase && !ssid->ext_psk) { + const char *field_name, *txt = NULL; + + wpa_dbg(wpa_s, MSG_DEBUG, + "PSK/passphrase not yet available for the selected network"); + + wpas_notify_network_request(wpa_s, ssid, + WPA_CTRL_REQ_PSK_PASSPHRASE, NULL); + + field_name = wpa_supplicant_ctrl_req_to_string( + WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt); + if (field_name == NULL) + return NULL; + + wpas_send_ctrl_req(wpa_s, ssid, field_name, txt); + + selected = NULL; + } + return selected; } @@ -1085,6 +1190,7 @@ int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP "PBC session overlap"); + wpas_notify_wps_event_pbc_overlap(wpa_s); #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT || wpa_s->p2p_in_provisioning) { @@ -1095,6 +1201,7 @@ int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS + wpas_wps_pbc_overlap(wpa_s); wpas_wps_cancel(wpa_s); #endif /* CONFIG_WPS */ return -1; @@ -1192,7 +1299,9 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct wpa_bss *current_bss = NULL; +#ifndef CONFIG_NO_ROAMING int min_diff; +#endif /* CONFIG_NO_ROAMING */ if (wpa_s->reassociate) return 1; /* explicit request to reassociate */ @@ -1421,6 +1530,17 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, { struct wpa_bss *selected; struct wpa_ssid *ssid = NULL; + int time_to_reenable = wpas_reenabled_network_time(wpa_s); + + if (time_to_reenable > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Postpone network selection by %d seconds since all networks are disabled", + time_to_reenable); + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + eloop_register_timeout(time_to_reenable, 0, + wpas_network_reenabled, wpa_s, NULL); + return 0; + } if (wpa_s->p2p_mgmt) return 0; /* no normal connection on p2p_mgmt interface */ @@ -1520,6 +1640,9 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, if (wpa_supplicant_req_sched_scan(wpa_s)) wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); + + wpa_msg_ctrl(wpa_s, MSG_INFO, + WPA_EVENT_NETWORK_NOT_FOUND); } } return 0; @@ -1577,7 +1700,7 @@ int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s) #else /* CONFIG_NO_SCAN_PROCESSING */ struct os_reltime now; - if (wpa_s->last_scan_res_used <= 0) + if (wpa_s->last_scan_res_used == 0) return -1; os_get_reltime(&now); @@ -1889,6 +2012,19 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, if (wpa_found || rsn_found) wpa_s->ap_ies_from_associnfo = 1; +#ifdef CONFIG_FST + wpabuf_free(wpa_s->received_mb_ies); + wpa_s->received_mb_ies = NULL; + if (wpa_s->fst) { + struct mb_ies_info mb_ies; + + wpa_printf(MSG_DEBUG, "Looking for MB IE"); + if (!mb_ies_info_by_ies(&mb_ies, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len)) + wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies); + } +#endif /* CONFIG_FST */ + if (wpa_s->assoc_freq && data->assoc_info.freq && wpa_s->assoc_freq != data->assoc_info.freq) { wpa_printf(MSG_DEBUG, "Operating frequency changed from " @@ -1932,6 +2068,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, { u8 bssid[ETH_ALEN]; int ft_completed; + int new_bss = 0; #ifdef CONFIG_AP if (wpa_s->ap_iface) { @@ -1946,6 +2083,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_AP */ + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + ft_completed = wpa_ft_is_completed(wpa_s->wpa); if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) return; @@ -1961,6 +2100,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" MACSTR, MAC2STR(bssid)); + new_bss = 1; random_add_randomness(bssid, ETH_ALEN); os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); @@ -1974,13 +2114,13 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } + } - if (wpa_s->conf->ap_scan == 1 && - wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) { - if (wpa_supplicant_assoc_update_ie(wpa_s) < 0) - wpa_msg(wpa_s, MSG_WARNING, - "WPA/RSN IEs not updated"); - } + if (wpa_s->conf->ap_scan == 1 && + wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) { + if (wpa_supplicant_assoc_update_ie(wpa_s) < 0 && new_bss) + wpa_msg(wpa_s, MSG_WARNING, + "WPA/RSN IEs not updated"); } #ifdef CONFIG_SME @@ -2253,7 +2393,8 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, "try to re-connect"); wpa_s->reassociate = 0; wpa_s->disconnected = 1; - wpa_supplicant_cancel_sched_scan(wpa_s); + if (!wpa_s->pno) + wpa_supplicant_cancel_sched_scan(wpa_s); } bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) @@ -2443,6 +2584,21 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the " "driver after interface was added"); } + +#ifdef CONFIG_P2P + if (!wpa_s->global->p2p && + !wpa_s->global->p2p_disabled && + !wpa_s->conf->p2p_disabled && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + wpas_p2p_add_p2pdev_interface( + wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) { + wpa_printf(MSG_INFO, + "P2P: Failed to enable P2P Device interface"); + /* Try to continue without. P2P will be disabled. */ + } +#endif /* CONFIG_P2P */ + break; case EVENT_INTERFACE_REMOVED: wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed"); @@ -2451,6 +2607,21 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); l2_packet_deinit(wpa_s->l2); wpa_s->l2 = NULL; + +#ifdef CONFIG_P2P + if (wpa_s->global->p2p && + wpa_s->global->p2p_init_wpa_s->parent == wpa_s && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Removing P2P Device interface"); + wpa_supplicant_remove_iface( + wpa_s->global, wpa_s->global->p2p_init_wpa_s, + 0); + wpa_s->global->p2p_init_wpa_s = NULL; + } +#endif /* CONFIG_P2P */ + #ifdef CONFIG_TERMINATE_ONLASTIF /* check if last interface */ if (!any_interfaces(wpa_s->global->ifaces)) @@ -2844,26 +3015,24 @@ static void wpa_supplicant_update_channel_list( if (wpa_s->drv_priv == NULL) return; /* Ignore event during drv initialization */ - free_hw_features(wpa_s); - wpa_s->hw.modes = wpa_drv_get_hw_feature_data( - wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags); - - wpas_p2p_update_channel_list(wpa_s); - - /* - * Check other interfaces to see if they share the same radio. If - * so, they get updated with this same hw mode info. - */ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { - if (ifs != wpa_s) { - wpa_printf(MSG_DEBUG, "%s: Updating hw mode", - ifs->ifname); - free_hw_features(ifs); - ifs->hw.modes = wpa_drv_get_hw_feature_data( - ifs, &ifs->hw.num_modes, &ifs->hw.flags); - } + wpa_printf(MSG_DEBUG, "%s: Updating hw mode", + ifs->ifname); + free_hw_features(ifs); + ifs->hw.modes = wpa_drv_get_hw_feature_data( + ifs, &ifs->hw.num_modes, &ifs->hw.flags); } + + /* Restart sched_scan with updated channel list */ + if (wpa_s->sched_scanning) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Channel list changed restart sched scan."); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + + wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER); } @@ -2964,6 +3133,13 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, return; } +#ifdef CONFIG_FST + if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) { + fst_rx_action(wpa_s->fst, mgmt, len); + return; + } +#endif /* CONFIG_FST */ + wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, category, payload, plen, freq); if (wpa_s->ifmsh) @@ -2974,9 +3150,6 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s, union wpa_event_data *event) { -#ifdef CONFIG_P2P - struct wpa_supplicant *ifs; -#endif /* CONFIG_P2P */ struct wpa_freq_range_list *list; char *str = NULL; @@ -2993,29 +3166,13 @@ static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s, __func__); } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event"); - wpas_p2p_update_channel_list(wpa_s); - } - for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { - int freq; - if (!ifs->current_ssid || - !ifs->current_ssid->p2p_group || - (ifs->current_ssid->mode != WPAS_MODE_P2P_GO && - ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)) - continue; - - freq = ifs->current_ssid->frequency; - if (!freq_range_list_includes(list, freq)) { - wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range", - freq); - continue; - } - - wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz", - freq); - /* TODO: Consider using CSA or removing the group within - * wpa_supplicant */ - wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP); + /* + * The update channel flow will also take care of moving a GO + * from the unsafe frequency if needed. + */ + wpas_p2p_update_channel_list(wpa_s, + WPAS_P2P_CHANNEL_UPDATE_AVOID); } #endif /* CONFIG_P2P */ @@ -3047,6 +3204,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_supplicant *wpa_s = ctx; + int resched; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && event != EVENT_INTERFACE_ENABLED && @@ -3372,6 +3530,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpas_p2p_probe_req_rx( wpa_s, src, mgmt->da, mgmt->bssid, ie, ie_len, + data->rx_mgmt.freq, data->rx_mgmt.ssi_signal); break; } @@ -3443,6 +3602,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_probe_req.bssid, data->rx_probe_req.ie, data->rx_probe_req.ie_len, + 0, data->rx_probe_req.ssi_signal); break; case EVENT_REMAIN_ON_CHANNEL: @@ -3613,6 +3773,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_SCHED_SCAN_STOPPED: wpa_s->pno = 0; wpa_s->sched_scanning = 0; + resched = wpa_s->scanning; wpa_supplicant_notify_scanning(wpa_s, 0); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) @@ -3627,6 +3788,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } else if (wpa_s->pno_sched_pending) { wpa_s->pno_sched_pending = 0; wpas_start_pno(wpa_s); + } else if (resched) { + wpa_supplicant_req_scan(wpa_s, 0, 0); } break; diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.c b/contrib/wpa/wpa_supplicant/hs20_supplicant.c index b9cd68193b2..a1afc85ff9b 100644 --- a/contrib/wpa/wpa_supplicant/hs20_supplicant.c +++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.c @@ -46,7 +46,7 @@ struct osu_icon { struct osu_provider { u8 bssid[ETH_ALEN]; - u8 osu_ssid[32]; + u8 osu_ssid[SSID_MAX_LEN]; u8 osu_ssid_len; char server_uri[256]; u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */ @@ -188,14 +188,16 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, struct wpa_bss *bss; int res; - freq = wpa_s->assoc_freq; bss = wpa_bss_get_bssid(wpa_s, dst); - if (bss) { - wpa_bss_anqp_unshare_alloc(bss); - freq = bss->freq; - } - if (freq <= 0) + if (!bss) { + wpa_printf(MSG_WARNING, + "ANQP: Cannot send query to unknown BSS " + MACSTR, MAC2STR(dst)); return -1; + } + + wpa_bss_anqp_unshare_alloc(bss); + freq = bss->freq; wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for " "subtypes 0x%x", MAC2STR(dst), stypes); @@ -822,7 +824,7 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) continue; } osu_ssid_len = *pos++; - if (osu_ssid_len > 32) { + if (osu_ssid_len > SSID_MAX_LEN) { wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID " "Length %u", osu_ssid_len); continue; diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.c b/contrib/wpa/wpa_supplicant/ibss_rsn.c index d0ae135bdf7..d9d0ae7f10d 100644 --- a/contrib/wpa/wpa_supplicant/ibss_rsn.c +++ b/contrib/wpa/wpa_supplicant/ibss_rsn.c @@ -571,6 +571,9 @@ int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) struct ibss_rsn_peer *peer; int res; + if (!ibss_rsn) + return -1; + /* if the peer already exists, exit immediately */ peer = ibss_rsn_get_peer(ibss_rsn, addr); if (peer) @@ -694,7 +697,8 @@ void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn) ibss_rsn_free(prev); } - wpa_deinit(ibss_rsn->auth_group); + if (ibss_rsn->auth_group) + wpa_deinit(ibss_rsn->auth_group); os_free(ibss_rsn); } diff --git a/contrib/wpa/wpa_supplicant/interworking.c b/contrib/wpa/wpa_supplicant/interworking.c index 4a396654487..fd47c179ea4 100644 --- a/contrib/wpa/wpa_supplicant/interworking.c +++ b/contrib/wpa/wpa_supplicant/interworking.c @@ -2058,7 +2058,7 @@ static struct wpa_cred * interworking_credentials_available_helper( int *excluded) { struct wpa_cred *cred, *cred2; - int excluded1, excluded2; + int excluded1, excluded2 = 0; if (disallowed_bssid(wpa_s, bss->bssid) || disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { @@ -2673,14 +2673,16 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, struct wpa_bss *bss; int res; - freq = wpa_s->assoc_freq; bss = wpa_bss_get_bssid(wpa_s, dst); - if (bss) { - wpa_bss_anqp_unshare_alloc(bss); - freq = bss->freq; - } - if (freq <= 0) + if (!bss) { + wpa_printf(MSG_WARNING, + "ANQP: Cannot send query to unknown BSS " + MACSTR, MAC2STR(dst)); return -1; + } + + wpa_bss_anqp_unshare_alloc(bss); + freq = bss->freq; wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)", diff --git a/contrib/wpa/wpa_supplicant/main.c b/contrib/wpa/wpa_supplicant/main.c index 22827479c64..d5d47e1d77b 100644 --- a/contrib/wpa/wpa_supplicant/main.c +++ b/contrib/wpa/wpa_supplicant/main.c @@ -12,6 +12,7 @@ #endif /* __linux__ */ #include "common.h" +#include "fst/fst.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "p2p_supplicant.h" @@ -237,7 +238,7 @@ int main(int argc, char *argv[]) goto out; #ifdef CONFIG_P2P case 'm': - iface->conf_p2p_dev = optarg; + params.conf_p2p_dev = optarg; break; #endif /* CONFIG_P2P */ case 'o': @@ -288,7 +289,7 @@ int main(int argc, char *argv[]) if (iface == NULL) goto out; ifaces = iface; - iface = &ifaces[iface_count - 1]; + iface = &ifaces[iface_count - 1]; os_memset(iface, 0, sizeof(*iface)); break; default: @@ -309,6 +310,17 @@ int main(int argc, char *argv[]) "wpa_supplicant"); } + if (fst_global_init()) { + wpa_printf(MSG_ERROR, "Failed to initialize FST"); + exitcode = -1; + goto out; + } + +#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE) + if (!fst_global_add_ctrl(fst_ctrl_cli)) + wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl"); +#endif + for (i = 0; exitcode == 0 && i < iface_count; i++) { struct wpa_supplicant *wpa_s; @@ -334,6 +346,8 @@ int main(int argc, char *argv[]) wpa_supplicant_deinit(global); + fst_global_deinit(); + out: wpa_supplicant_fd_workaround(0); os_free(ifaces); diff --git a/contrib/wpa/wpa_supplicant/mesh.c b/contrib/wpa/wpa_supplicant/mesh.c index 33b4af38faa..77f708b42da 100644 --- a/contrib/wpa/wpa_supplicant/mesh.c +++ b/contrib/wpa/wpa_supplicant/mesh.c @@ -47,8 +47,8 @@ void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, if (ifmsh->mconf) { mesh_mpm_deinit(wpa_s, ifmsh); - if (ifmsh->mconf->ies) { - ifmsh->mconf->ies = NULL; + if (ifmsh->mconf->rsn_ie) { + ifmsh->mconf->rsn_ie = NULL; /* We cannot free this struct * because wpa_authenticator on * hostapd side is also using it @@ -171,6 +171,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, ifmsh->conf = conf; ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links; + ifmsh->bss[0]->dot11RSNASAERetransPeriod = + wpa_s->conf->dot11RSNASAERetransPeriod; os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface)); mconf = mesh_config_create(ssid); @@ -350,8 +352,8 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, } if (wpa_s->ifmsh) { - params.ies = wpa_s->ifmsh->mconf->ies; - params.ie_len = wpa_s->ifmsh->mconf->ie_len; + params.ies = wpa_s->ifmsh->mconf->rsn_ie; + params.ie_len = wpa_s->ifmsh->mconf->rsn_ie_len; params.basic_rates = wpa_s->ifmsh->basic_rates; } @@ -453,22 +455,23 @@ static int mesh_attr_text(const u8 *ies, size_t ies_len, char *buf, char *end) ret = os_snprintf(pos, end - pos, "bss_basic_rate_set=%d", bss_basic_rate_set[0]); if (os_snprintf_error(end - pos, ret)) - return pos - buf; + goto fail; pos += ret; for (i = 1; i < bss_basic_rate_set_len; i++) { ret = os_snprintf(pos, end - pos, " %d", bss_basic_rate_set[i]); if (os_snprintf_error(end - pos, ret)) - return pos - buf; + goto fail; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) - return pos - buf; + goto fail; pos += ret; } +fail: os_free(bss_basic_rate_set); return pos - buf; diff --git a/contrib/wpa/wpa_supplicant/mesh_mpm.c b/contrib/wpa/wpa_supplicant/mesh_mpm.c index 1d6f2be2b50..f81b88c8940 100644 --- a/contrib/wpa/wpa_supplicant/mesh_mpm.c +++ b/contrib/wpa/wpa_supplicant/mesh_mpm.c @@ -239,6 +239,9 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, 2 + 22; /* HT operation */ } #endif /* CONFIG_IEEE80211N */ + if (type != PLINK_CLOSE) + buf_len += conf->rsn_ie_len; /* RSN IE */ + buf = wpabuf_alloc(buf_len); if (!buf) return; @@ -262,6 +265,9 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, pos = hostapd_eid_ext_supp_rates(bss, pos); wpabuf_put_data(buf, supp_rates, pos - supp_rates); + /* IE: RSN IE */ + wpabuf_put_data(buf, conf->rsn_ie, conf->rsn_ie_len); + /* IE: Mesh ID */ wpabuf_put_u8(buf, WLAN_EID_MESH_ID); wpabuf_put_u8(buf, conf->meshid_len); @@ -551,8 +557,7 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, mesh_mpm_init_link(wpa_s, sta); #ifdef CONFIG_IEEE80211N - copy_sta_ht_capab(data, sta, elems->ht_capabilities, - elems->ht_capabilities_len); + copy_sta_ht_capab(data, sta, elems->ht_capabilities); update_ht_state(data, sta); #endif /* CONFIG_IEEE80211N */ diff --git a/contrib/wpa/wpa_supplicant/mesh_rsn.c b/contrib/wpa/wpa_supplicant/mesh_rsn.c index 936002d954e..747f1ae6968 100644 --- a/contrib/wpa/wpa_supplicant/mesh_rsn.c +++ b/contrib/wpa/wpa_supplicant/mesh_rsn.c @@ -190,7 +190,8 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) static void mesh_rsn_deinit(struct mesh_rsn *rsn) { os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk)); - wpa_deinit(rsn->auth); + if (rsn->auth) + wpa_deinit(rsn->auth); } @@ -209,14 +210,15 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) { mesh_rsn_deinit(mesh_rsn); + os_free(mesh_rsn); return NULL; } bss->wpa_auth = mesh_rsn->auth; ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len); - conf->ies = (u8 *) ie; - conf->ie_len = ie_len; + conf->rsn_ie = (u8 *) ie; + conf->rsn_ie_len = ie_len; wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); diff --git a/contrib/wpa/wpa_supplicant/notify.c b/contrib/wpa/wpa_supplicant/notify.c index ea7dbdb15bf..45d06bf3574 100644 --- a/contrib/wpa/wpa_supplicant/notify.c +++ b/contrib/wpa/wpa_supplicant/notify.c @@ -17,6 +17,7 @@ #include "dbus/dbus_old.h" #include "dbus/dbus_new.h" #include "rsn_supp/wpa.h" +#include "fst/fst.h" #include "driver_i.h" #include "scan.h" #include "p2p_supplicant.h" @@ -88,6 +89,16 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, /* notify the new DBus API */ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE); +#ifdef CONFIG_FST + if (wpa_s->fst && !is_zero_ether_addr(wpa_s->bssid)) { + if (new_state == WPA_COMPLETED) + fst_notify_peer_connected(wpa_s->fst, wpa_s->bssid); + else if (old_state >= WPA_ASSOCIATED && + new_state < WPA_ASSOCIATED) + fst_notify_peer_disconnected(wpa_s->fst, wpa_s->bssid); + } +#endif /* CONFIG_FST */ + if (new_state == WPA_COMPLETED) wpas_p2p_notif_connected(wpa_s); else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED) @@ -268,6 +279,16 @@ void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s) #endif /* CONFIG_WPS */ } +void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + +#ifdef CONFIG_WPS + wpas_dbus_signal_wps_event_pbc_overlap(wpa_s); +#endif /* CONFIG_WPS */ +} + void wpas_notify_network_added(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) @@ -307,14 +328,12 @@ void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s, void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { - if (wpa_s->p2p_mgmt) - return; - if (wpa_s->next_ssid == ssid) wpa_s->next_ssid = NULL; if (wpa_s->wpa) wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); - if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) + if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s && + !wpa_s->p2p_mgmt) wpas_dbus_unregister_network(wpa_s, ssid->id); if (network_is_persistent_group(ssid)) wpas_notify_persistent_group_removed(wpa_s, ssid); @@ -522,6 +541,13 @@ void wpas_notify_resume(struct wpa_global *global) #ifdef CONFIG_P2P +void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s) +{ + /* Notify P2P find has stopped */ + wpas_dbus_signal_p2p_find_stopped(wpa_s); +} + + void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s, const u8 *dev_addr, int new_device) { @@ -556,9 +582,9 @@ void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s, void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s, - const u8 *src, u16 dev_passwd_id) + const u8 *src, u16 dev_passwd_id, u8 go_intent) { - wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id); + wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent); } @@ -631,12 +657,30 @@ void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s, } +void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, + const char *reason) +{ + /* Notify a group formation failed */ + wpas_dbus_signal_p2p_group_formation_failure(wpa_s, reason); +} + + void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) { wpas_dbus_signal_p2p_wps_failed(wpa_s, fail); } + +void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *go_dev_addr, + const u8 *bssid, int id, int op_freq) +{ + /* Notify a P2P Invitation Request */ + wpas_dbus_signal_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid, + id, op_freq); +} + #endif /* CONFIG_P2P */ @@ -775,10 +819,12 @@ void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, ssid->disabled = 0; wpas_dbus_unregister_network(wpa_s, ssid->id); ssid->disabled = 2; + ssid->p2p_persistent_group = 1; wpas_dbus_register_persistent_group(wpa_s, ssid); } else { /* Changed from persistent group to normal network profile */ wpas_dbus_unregister_persistent_group(wpa_s, ssid->id); + ssid->p2p_persistent_group = 0; wpas_dbus_register_network(wpa_s, ssid); } #endif /* CONFIG_P2P */ diff --git a/contrib/wpa/wpa_supplicant/notify.h b/contrib/wpa/wpa_supplicant/notify.h index b268332ffc3..d9f0f5a9673 100644 --- a/contrib/wpa/wpa_supplicant/notify.h +++ b/contrib/wpa/wpa_supplicant/notify.h @@ -45,6 +45,7 @@ void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s, void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail); void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s); +void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s); void wpas_notify_network_added(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, @@ -84,6 +85,7 @@ void wpas_notify_resume(struct wpa_global *global); void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *mac_addr, int authorized, const u8 *p2p_dev_addr); +void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s); void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s, const u8 *dev_addr, int new_device); void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s, @@ -92,7 +94,7 @@ void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, const char *role); void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s, - const u8 *src, u16 dev_passwd_id); + const u8 *src, u16 dev_passwd_id, u8 go_intent); void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *res); void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s, @@ -112,6 +114,8 @@ void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s, void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int network_id, int client); +void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, + const char *reason); void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s, @@ -133,5 +137,8 @@ void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *go_dev_addr, + const u8 *bssid, int id, int op_freq); #endif /* NOTIFY_H */ diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.c b/contrib/wpa/wpa_supplicant/p2p_supplicant.c index b200ca01026..78bdd0837e8 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.c +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.c @@ -47,6 +47,12 @@ #define P2P_AUTO_PD_SCAN_ATTEMPTS 5 +/** + * Defines time interval in seconds when a GO needs to evacuate a frequency that + * it is currently using, but is no longer valid for P2P use cases. + */ +#define P2P_GO_FREQ_CHANGE_TIME 5 + #ifndef P2P_MAX_CLIENT_IDLE /* * How many seconds to try to reconnect to the GO when connection in P2P client @@ -85,6 +91,12 @@ #define P2P_MGMT_DEVICE_PREFIX "p2p-dev-" +/* + * How many seconds to wait to re-attempt to move GOs, in case previous attempt + * was not possible. + */ +#define P2P_RECONSIDER_GO_MOVE_DELAY 30 + enum p2p_group_removal_reason { P2P_GROUP_REMOVAL_UNKNOWN, P2P_GROUP_REMOVAL_SILENT, @@ -94,7 +106,8 @@ enum p2p_group_removal_reason { P2P_GROUP_REMOVAL_UNAVAILABLE, P2P_GROUP_REMOVAL_GO_ENDING_SESSION, P2P_GROUP_REMOVAL_PSK_FAILURE, - P2P_GROUP_REMOVAL_FREQ_CONFLICT + P2P_GROUP_REMOVAL_FREQ_CONFLICT, + P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL }; @@ -126,6 +139,18 @@ static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx); static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type type); +static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s, + int already_deleted); +static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, + unsigned int num); +static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx); +static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq); +static void +wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, unsigned int num, + enum wpas_p2p_channel_update_trig trig); +static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx); /* @@ -191,7 +216,11 @@ static void wpas_p2p_set_own_freq_preference(struct wpa_supplicant *wpa_s, { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; - if (wpa_s->parent->conf->p2p_ignore_shared_freq && + + /* Use the wpa_s used to control the P2P Device operation */ + wpa_s = wpa_s->global->p2p_init_wpa_s; + + if (wpa_s->conf->p2p_ignore_shared_freq && freq > 0 && wpa_s->num_multichan_concurrent > 1 && wpas_p2p_num_unused_channels(wpa_s) > 0) { wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration", @@ -600,7 +629,7 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) */ go_wpa_s = wpas_p2p_get_go_group(wpa_s); persistent_go = wpas_p2p_get_persistent_go(wpa_s); - p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface; + p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s); wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p", go_wpa_s, persistent_go); @@ -866,9 +895,12 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation " "timeout"); wpa_s->p2p_in_provisioning = 0; + wpas_p2p_group_formation_failed(wpa_s, 1); } wpa_s->p2p_in_invitation = 0; + eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); + eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL); /* * Make sure wait for the first client does not remain active after the @@ -940,6 +972,8 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, else wpa_drv_deinit_p2p_cli(wpa_s); + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); + return 0; } @@ -1109,13 +1143,14 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, u8 *n; size_t i; int found = 0; + struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; ssid = wpa_s->current_ssid; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) return; - for (s = wpa_s->parent->conf->ssid; s; s = s->next) { + for (s = p2p_wpa_s->conf->ssid; s; s = s->next) { if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO) continue; @@ -1174,8 +1209,8 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, 0xff, ETH_ALEN); } - if (wpa_s->parent->conf->update_config && - wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + if (p2p_wpa_s->conf->update_config && + wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); } @@ -1226,7 +1261,7 @@ static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s, static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, - int success) + int success, int already_deleted) { struct wpa_ssid *ssid; int client; @@ -1251,6 +1286,9 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, if (!success) { wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); + wpas_notify_p2p_group_formation_failure(wpa_s, ""); + if (already_deleted) + return; wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FORMATION_FAILED); return; @@ -1677,14 +1715,22 @@ static void p2p_go_configured(void *ctx, void *data) params->persistent_group, ""); wpa_s->group_formation_reported = 1; - if (wpa_s->parent->p2ps_join_addr_valid) { - wpa_dbg(wpa_s, MSG_DEBUG, - "P2PS: Setting default PIN for " MACSTR, - MAC2STR(wpa_s->parent->p2ps_join_addr)); - wpa_supplicant_ap_wps_pin(wpa_s, - wpa_s->parent->p2ps_join_addr, - "12345670", NULL, 0, 0); - wpa_s->parent->p2ps_join_addr_valid = 0; + if (wpa_s->parent->p2ps_method_config_any) { + if (is_zero_ether_addr(wpa_s->parent->p2ps_join_addr)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2PS: Setting default PIN for ANY"); + wpa_supplicant_ap_wps_pin(wpa_s, NULL, + "12345670", NULL, 0, + 0); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2PS: Setting default PIN for " MACSTR, + MAC2STR(wpa_s->parent->p2ps_join_addr)); + wpa_supplicant_ap_wps_pin( + wpa_s, wpa_s->parent->p2ps_join_addr, + "12345670", NULL, 0, 0); + } + wpa_s->parent->p2ps_method_config_any = 0; } os_get_reltime(&wpa_s->global->p2p_go_wait_client); @@ -1771,6 +1817,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, wpa_s->show_group_started = 0; wpa_s->p2p_go_group_formation_completed = 0; wpa_s->group_formation_reported = 0; + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); wpa_config_set_network_defaults(ssid); ssid->temporary = 1; @@ -1853,6 +1900,7 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst, d->num_sec_device_types = s->num_sec_device_types; d->p2p_group_idle = s->p2p_group_idle; + d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy; d->p2p_intra_bss = s->p2p_intra_bss; d->persistent_reconnect = s->persistent_reconnect; d->max_num_sta = s->max_num_sta; @@ -1869,6 +1917,7 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst, d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey); d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey); } + d->p2p_cli_probe = s->p2p_cli_probe; } @@ -2014,17 +2063,18 @@ static void wpas_p2p_group_formation_timeout(void *eloop_ctx, { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); - wpas_p2p_group_formation_failed(wpa_s); + wpas_p2p_group_formation_failed(wpa_s, 0); } -void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s) +static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s, + int already_deleted) { eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, NULL); if (wpa_s->global->p2p) p2p_group_formation_failed(wpa_s->global->p2p); - wpas_group_formation_completed(wpa_s, 0); + wpas_group_formation_completed(wpa_s, 0, already_deleted); } @@ -2070,6 +2120,11 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) return; } + if (!res->role_go) { + /* Inform driver of the operating channel of GO. */ + wpa_drv_set_prob_oper_freq(wpa_s, res->freq); + } + if (wpa_s->p2p_go_ht40) res->ht40 = 1; if (wpa_s->p2p_go_vht) @@ -2105,7 +2160,7 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpas_p2p_remove_pending_group_interface(wpa_s); eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); - wpas_p2p_group_formation_failed(wpa_s); + wpas_p2p_group_formation_failed(wpa_s, 1); return; } if (group_wpa_s != wpa_s) { @@ -2117,18 +2172,22 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpa_s->pending_interface_name[0] = '\0'; group_wpa_s->p2p_in_provisioning = 1; - if (res->role_go) + if (res->role_go) { wpas_start_wps_go(group_wpa_s, res, 1); - else + } else { + os_get_reltime(&group_wpa_s->scan_min_time); wpas_start_wps_enrollee(group_wpa_s, res); + } } else { wpa_s->p2p_in_provisioning = 1; wpa_s->global->p2p_group_formation = wpa_s; - if (res->role_go) + if (res->role_go) { wpas_start_wps_go(wpa_s, res, 1); - else + } else { + os_get_reltime(&wpa_s->scan_min_time); wpas_start_wps_enrollee(ctx, res); + } } wpa_s->p2p_long_listen = 0; @@ -2141,13 +2200,15 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) } -static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) +static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id, + u8 go_intent) { struct wpa_supplicant *wpa_s = ctx; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR - " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id); + " dev_passwd_id=%u go_intent=%u", MAC2STR(src), + dev_passwd_id, go_intent); - wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id); + wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent); } @@ -2248,6 +2309,7 @@ static void wpas_find_stopped(void *ctx) { struct wpa_supplicant *wpa_s = ctx; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED); + wpas_notify_p2p_find_stopped(wpa_s); } @@ -2376,1236 +2438,24 @@ static void wpas_stop_listen(void *ctx) wpa_s->roc_waiting_drv_freq = 0; } wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL); - wpa_drv_probe_req_report(wpa_s, 0); + + /* + * Don't cancel Probe Request RX reporting for a connected P2P Client + * handling Probe Request frames. + */ + if (!wpa_s->p2p_cli_probe) + wpa_drv_probe_req_report(wpa_s, 0); + wpas_p2p_listen_work_done(wpa_s); } -static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf) +static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf, + unsigned int freq) { struct wpa_supplicant *wpa_s = ctx; - return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1); -} - - -/* - * DNS Header section is used only to calculate compression pointers, so the - * contents of this data does not matter, but the length needs to be reserved - * in the virtual packet. - */ -#define DNS_HEADER_LEN 12 - -/* - * 27-octet in-memory packet from P2P specification containing two implied - * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN - */ -#define P2P_SD_IN_MEMORY_LEN 27 - -static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start, - u8 **spos, const u8 *end) -{ - while (*spos < end) { - u8 val = ((*spos)[0] & 0xc0) >> 6; - int len; - - if (val == 1 || val == 2) { - /* These are reserved values in RFC 1035 */ - wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " - "sequence starting with 0x%x", val); - return -1; - } - - if (val == 3) { - u16 offset; - u8 *spos_tmp; - - /* Offset */ - if (*spos + 2 > end) { - wpa_printf(MSG_DEBUG, "P2P: No room for full " - "DNS offset field"); - return -1; - } - - offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1]; - if (offset >= *spos - start) { - wpa_printf(MSG_DEBUG, "P2P: Invalid DNS " - "pointer offset %u", offset); - return -1; - } - - (*spos) += 2; - spos_tmp = start + offset; - return p2p_sd_dns_uncompress_label(upos, uend, start, - &spos_tmp, - *spos - 2); - } - - /* Label */ - len = (*spos)[0] & 0x3f; - if (len == 0) - return 0; - - (*spos)++; - if (*spos + len > end) { - wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " - "sequence - no room for label with length " - "%u", len); - return -1; - } - - if (*upos + len + 2 > uend) - return -2; - - os_memcpy(*upos, *spos, len); - *spos += len; - *upos += len; - (*upos)[0] = '.'; - (*upos)++; - (*upos)[0] = '\0'; - } - - return 0; -} - - -/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet. - * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is - * not large enough */ -static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg, - size_t msg_len, size_t offset) -{ - /* 27-octet in-memory packet from P2P specification */ - const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01" - "\x04_udp\xC0\x11\x00\x0C\x00\x01"; - u8 *tmp, *end, *spos; - char *upos, *uend; - int ret = 0; - - if (buf_len < 2) - return -1; - if (offset > msg_len) - return -1; - - tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len); - if (tmp == NULL) - return -1; - spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN; - end = spos + msg_len; - spos += offset; - - os_memset(tmp, 0, DNS_HEADER_LEN); - os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN); - os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len); - - upos = buf; - uend = buf + buf_len; - - ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end); - if (ret) { - os_free(tmp); - return ret; - } - - if (upos == buf) { - upos[0] = '.'; - upos[1] = '\0'; - } else if (upos[-1] == '.') - upos[-1] = '\0'; - - os_free(tmp); - return 0; -} - - -static struct p2p_srv_bonjour * -wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s, - const struct wpabuf *query) -{ - struct p2p_srv_bonjour *bsrv; - size_t len; - - len = wpabuf_len(query); - dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, - struct p2p_srv_bonjour, list) { - if (len == wpabuf_len(bsrv->query) && - os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query), - len) == 0) - return bsrv; - } - return NULL; -} - - -static struct p2p_srv_upnp * -wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version, - const char *service) -{ - struct p2p_srv_upnp *usrv; - - dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, - struct p2p_srv_upnp, list) { - if (version == usrv->version && - os_strcmp(service, usrv->service) == 0) - return usrv; - } - return NULL; -} - - -static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto, - u8 srv_trans_id, u8 status) -{ - u8 *len_pos; - - if (wpabuf_tailroom(resp) < 5) - return; - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, srv_proto); - wpabuf_put_u8(resp, srv_trans_id); - /* Status Code */ - wpabuf_put_u8(resp, status); - /* Response Data: empty */ - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); -} - - -static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, - u8 srv_trans_id) -{ - wpas_sd_add_empty(resp, srv_proto, srv_trans_id, - P2P_SD_PROTO_NOT_AVAILABLE); -} - - -static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto, - u8 srv_trans_id) -{ - wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST); -} - - -static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto, - u8 srv_trans_id) -{ - wpas_sd_add_empty(resp, srv_proto, srv_trans_id, - P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); -} - - -static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id) -{ - struct p2p_srv_bonjour *bsrv; - u8 *len_pos; - - wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services"); - - if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { - wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); - return; - } - - dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, - struct p2p_srv_bonjour, list) { - if (wpabuf_tailroom(resp) < - 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) - return; - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_BONJOUR); - wpabuf_put_u8(resp, srv_trans_id); - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", - wpabuf_head(bsrv->resp), - wpabuf_len(bsrv->resp)); - /* Response Data */ - wpabuf_put_buf(resp, bsrv->query); /* Key */ - wpabuf_put_buf(resp, bsrv->resp); /* Value */ - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - - 2); - } -} - - -static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query, - size_t query_len) -{ - char str_rx[256], str_srv[256]; - - if (query_len < 3 || wpabuf_len(bsrv->query) < 3) - return 0; /* Too short to include DNS Type and Version */ - if (os_memcmp(query + query_len - 3, - wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3, - 3) != 0) - return 0; /* Mismatch in DNS Type or Version */ - if (query_len == wpabuf_len(bsrv->query) && - os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0) - return 1; /* Binary match */ - - if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3, - 0)) - return 0; /* Failed to uncompress query */ - if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv), - wpabuf_head(bsrv->query), - wpabuf_len(bsrv->query) - 3, 0)) - return 0; /* Failed to uncompress service */ - - return os_strcmp(str_rx, str_srv) == 0; -} - - -static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id, - const u8 *query, size_t query_len) -{ - struct p2p_srv_bonjour *bsrv; - u8 *len_pos; - int matches = 0; - - wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour", - query, query_len); - if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { - wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); - wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR, - srv_trans_id); - return; - } - - if (query_len == 0) { - wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); - return; - } - - dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, - struct p2p_srv_bonjour, list) { - if (!match_bonjour_query(bsrv, query, query_len)) - continue; - - if (wpabuf_tailroom(resp) < - 5 + query_len + wpabuf_len(bsrv->resp)) - return; - - matches++; - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_BONJOUR); - wpabuf_put_u8(resp, srv_trans_id); - - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", - wpabuf_head(bsrv->resp), - wpabuf_len(bsrv->resp)); - - /* Response Data */ - wpabuf_put_data(resp, query, query_len); /* Key */ - wpabuf_put_buf(resp, bsrv->resp); /* Value */ - - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); - } - - if (matches == 0) { - wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not " - "available"); - if (wpabuf_tailroom(resp) < 5) - return; - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_BONJOUR); - wpabuf_put_u8(resp, srv_trans_id); - - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); - /* Response Data: empty */ - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - - 2); - } -} - - -static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id) -{ - struct p2p_srv_upnp *usrv; - u8 *len_pos; - - wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services"); - - if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { - wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); - return; - } - - dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, - struct p2p_srv_upnp, list) { - if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service)) - return; - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_UPNP); - wpabuf_put_u8(resp, srv_trans_id); - - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - /* Response Data */ - wpabuf_put_u8(resp, usrv->version); - wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", - usrv->service); - wpabuf_put_str(resp, usrv->service); - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - - 2); - } -} - - -static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id, - const u8 *query, size_t query_len) -{ - struct p2p_srv_upnp *usrv; - u8 *len_pos; - u8 version; - char *str; - int count = 0; - - wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP", - query, query_len); - - if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { - wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); - wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP, - srv_trans_id); - return; - } - - if (query_len == 0) { - wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); - return; - } - - if (wpabuf_tailroom(resp) < 5) - return; - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_UPNP); - wpabuf_put_u8(resp, srv_trans_id); - - version = query[0]; - str = os_malloc(query_len); - if (str == NULL) - return; - os_memcpy(str, query + 1, query_len - 1); - str[query_len - 1] = '\0'; - - dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, - struct p2p_srv_upnp, list) { - if (version != usrv->version) - continue; - - if (os_strcmp(str, "ssdp:all") != 0 && - os_strstr(usrv->service, str) == NULL) - continue; - - if (wpabuf_tailroom(resp) < 2) - break; - if (count == 0) { - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - /* Response Data */ - wpabuf_put_u8(resp, version); - } else - wpabuf_put_u8(resp, ','); - - count++; - - wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", - usrv->service); - if (wpabuf_tailroom(resp) < os_strlen(usrv->service)) - break; - wpabuf_put_str(resp, usrv->service); - } - os_free(str); - - if (count == 0) { - wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not " - "available"); - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); - /* Response Data: empty */ - } - - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); -} - - -#ifdef CONFIG_WIFI_DISPLAY -static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id, - const u8 *query, size_t query_len) -{ - const u8 *pos; - u8 role; - u8 *len_pos; - - wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len); - - if (!wpa_s->global->wifi_display) { - wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available"); - wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY, - srv_trans_id); - return; - } - - if (query_len < 1) { - wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device " - "Role"); - return; - } - - if (wpabuf_tailroom(resp) < 5) - return; - - pos = query; - role = *pos++; - wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role); - - /* TODO: role specific handling */ - - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY); - wpabuf_put_u8(resp, srv_trans_id); - wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */ - - while (pos < query + query_len) { - if (*pos < MAX_WFD_SUBELEMS && - wpa_s->global->wfd_subelem[*pos] && - wpabuf_tailroom(resp) >= - wpabuf_len(wpa_s->global->wfd_subelem[*pos])) { - wpa_printf(MSG_DEBUG, "P2P: Add WSD response " - "subelement %u", *pos); - wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]); - } - pos++; - } - - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); -} -#endif /* CONFIG_WIFI_DISPLAY */ - - -static int find_p2ps_substr(struct p2ps_advertisement *adv_data, - const u8 *needle, size_t needle_len) -{ - const u8 *haystack = (const u8 *) adv_data->svc_info; - size_t haystack_len, i; - - /* Allow search term to be empty */ - if (!needle || !needle_len) - return 1; - - if (!haystack) - return 0; - - haystack_len = os_strlen(adv_data->svc_info); - for (i = 0; i < haystack_len; i++) { - if (haystack_len - i < needle_len) - break; - if (os_memcmp(haystack + i, needle, needle_len) == 0) - return 1; - } - - return 0; -} - - -static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s, - struct wpabuf *resp, u8 srv_trans_id, - const u8 *query, size_t query_len) -{ - struct p2ps_advertisement *adv_data; - const u8 *svc = &query[1]; - const u8 *info = NULL; - size_t svc_len = query[0]; - size_t info_len = 0; - int prefix = 0; - u8 *count_pos = NULL; - u8 *len_pos = NULL; - - wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len); - - if (!wpa_s->global->p2p) { - wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available"); - wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id); - return; - } - - /* Info block is optional */ - if (svc_len + 1 < query_len) { - info = &svc[svc_len]; - info_len = *info++; - } - - /* Range check length of svc string and info block */ - if (svc_len + (info_len ? info_len + 2 : 1) > query_len) { - wpa_printf(MSG_DEBUG, "P2P: ASP bad request"); - wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id); - return; - } - - /* Detect and correct for prefix search */ - if (svc_len && svc[svc_len - 1] == '*') { - prefix = 1; - svc_len--; - } - - for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p); - adv_data; adv_data = adv_data->next) { - /* If not a prefix match, reject length mismatches */ - if (!prefix && svc_len != os_strlen(adv_data->svc_name)) - continue; - - /* Search each service for request */ - if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 && - find_p2ps_substr(adv_data, info, info_len)) { - size_t len = os_strlen(adv_data->svc_name); - size_t svc_info_len = 0; - - if (adv_data->svc_info) - svc_info_len = os_strlen(adv_data->svc_info); - - if (len > 0xff || svc_info_len > 0xffff) - return; - - /* Length & Count to be filled as we go */ - if (!len_pos && !count_pos) { - if (wpabuf_tailroom(resp) < - len + svc_info_len + 16) - return; - - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_P2PS); - wpabuf_put_u8(resp, srv_trans_id); - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - count_pos = wpabuf_put(resp, 1); - *count_pos = 0; - } else if (wpabuf_tailroom(resp) < - len + svc_info_len + 10) - return; - - if (svc_info_len) { - wpa_printf(MSG_DEBUG, - "P2P: Add Svc: %s info: %s", - adv_data->svc_name, - adv_data->svc_info); - } else { - wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s", - adv_data->svc_name); - } - - /* Advertisement ID */ - wpabuf_put_le32(resp, adv_data->id); - - /* Config Methods */ - wpabuf_put_be16(resp, adv_data->config_methods); - - /* Service Name */ - wpabuf_put_u8(resp, (u8) len); - wpabuf_put_data(resp, adv_data->svc_name, len); - - /* Service State */ - wpabuf_put_u8(resp, adv_data->state); - - /* Service Information */ - wpabuf_put_le16(resp, (u16) svc_info_len); - wpabuf_put_data(resp, adv_data->svc_info, svc_info_len); - - /* Update length and count */ - (*count_pos)++; - WPA_PUT_LE16(len_pos, - (u8 *) wpabuf_put(resp, 0) - len_pos - 2); - } - } - - /* Return error if no matching svc found */ - if (count_pos == NULL) { - wpa_printf(MSG_DEBUG, "P2P: ASP service not found"); - wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id); - } -} - - -static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, - u16 update_indic, const u8 *tlvs, size_t tlvs_len) -{ - struct wpa_supplicant *wpa_s = ctx; - const u8 *pos = tlvs; - const u8 *end = tlvs + tlvs_len; - const u8 *tlv_end; - u16 slen; - struct wpabuf *resp; - u8 srv_proto, srv_trans_id; - size_t buf_len; - char *buf; - - wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs", - tlvs, tlvs_len); - buf_len = 2 * tlvs_len + 1; - buf = os_malloc(buf_len); - if (buf) { - wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); - wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d " - MACSTR " %u %u %s", - freq, MAC2STR(sa), dialog_token, update_indic, - buf); - os_free(buf); - } - - if (wpa_s->p2p_sd_over_ctrl_iface) { - wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, - update_indic, tlvs, tlvs_len); - return; /* to be processed by an external program */ - } - - resp = wpabuf_alloc(10000); - if (resp == NULL) - return; - - while (pos + 1 < end) { - wpa_printf(MSG_DEBUG, "P2P: Service Request TLV"); - slen = WPA_GET_LE16(pos); - pos += 2; - if (pos + slen > end || slen < 2) { - wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data " - "length"); - wpabuf_free(resp); - return; - } - tlv_end = pos + slen; - - srv_proto = *pos++; - wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", - srv_proto); - srv_trans_id = *pos++; - wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", - srv_trans_id); - - wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data", - pos, tlv_end - pos); - - - if (wpa_s->force_long_sd) { - wpa_printf(MSG_DEBUG, "P2P: SD test - force long " - "response"); - wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); - wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); - goto done; - } - - switch (srv_proto) { - case P2P_SERV_ALL_SERVICES: - wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request " - "for all services"); - if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) && - dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { - wpa_printf(MSG_DEBUG, "P2P: No service " - "discovery protocols available"); - wpas_sd_add_proto_not_avail( - resp, P2P_SERV_ALL_SERVICES, - srv_trans_id); - break; - } - wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); - wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); - break; - case P2P_SERV_BONJOUR: - wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id, - pos, tlv_end - pos); - break; - case P2P_SERV_UPNP: - wpas_sd_req_upnp(wpa_s, resp, srv_trans_id, - pos, tlv_end - pos); - break; -#ifdef CONFIG_WIFI_DISPLAY - case P2P_SERV_WIFI_DISPLAY: - wpas_sd_req_wfd(wpa_s, resp, srv_trans_id, - pos, tlv_end - pos); - break; -#endif /* CONFIG_WIFI_DISPLAY */ - case P2P_SERV_P2PS: - wpas_sd_req_asp(wpa_s, resp, srv_trans_id, - pos, tlv_end - pos); - break; - default: - wpa_printf(MSG_DEBUG, "P2P: Unavailable service " - "protocol %u", srv_proto); - wpas_sd_add_proto_not_avail(resp, srv_proto, - srv_trans_id); - break; - } - - pos = tlv_end; - } - -done: - wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, - update_indic, tlvs, tlvs_len); - - wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp); - - wpabuf_free(resp); -} - - -static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s, - const u8 *sa, u8 srv_trans_id, - const u8 *pos, const u8 *tlv_end) -{ - u8 left = *pos++; - u32 adv_id; - u8 svc_status; - u16 config_methods; - char svc_str[256]; - - while (left-- && pos < tlv_end) { - char *buf = NULL; - size_t buf_len; - u8 svc_len; - - /* Sanity check fixed length+svc_str */ - if (pos + 6 >= tlv_end) - break; - svc_len = pos[6]; - if (pos + svc_len + 10 > tlv_end) - break; - - /* Advertisement ID */ - adv_id = WPA_GET_LE32(pos); - pos += sizeof(u32); - - /* Config Methods */ - config_methods = WPA_GET_BE16(pos); - pos += sizeof(u16); - - /* Service Name */ - pos++; /* svc_len */ - os_memcpy(svc_str, pos, svc_len); - svc_str[svc_len] = '\0'; - pos += svc_len; - - /* Service Status */ - svc_status = *pos++; - - /* Service Information Length */ - buf_len = WPA_GET_LE16(pos); - pos += sizeof(u16); - - /* Sanity check buffer length */ - if (buf_len > (unsigned int) (tlv_end - pos)) - break; - - if (buf_len) { - buf = os_zalloc(2 * buf_len + 1); - if (buf) { - utf8_escape((const char *) pos, buf_len, buf, - 2 * buf_len + 1); - } - } - - pos += buf_len; - - if (buf) { - wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP - MACSTR " %x %x %x %x %s '%s'", - MAC2STR(sa), srv_trans_id, adv_id, - svc_status, config_methods, svc_str, - buf); - os_free(buf); - } else { - wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP - MACSTR " %x %x %x %x %s", - MAC2STR(sa), srv_trans_id, adv_id, - svc_status, config_methods, svc_str); - } - } -} - - -static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, - const u8 *tlvs, size_t tlvs_len) -{ - struct wpa_supplicant *wpa_s = ctx; - const u8 *pos = tlvs; - const u8 *end = tlvs + tlvs_len; - const u8 *tlv_end; - u16 slen; - size_t buf_len; - char *buf; - - wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs", - tlvs, tlvs_len); - if (tlvs_len > 1500) { - /* TODO: better way for handling this */ - wpa_msg_ctrl(wpa_s, MSG_INFO, - P2P_EVENT_SERV_DISC_RESP MACSTR - " %u ", - MAC2STR(sa), update_indic, - (unsigned int) tlvs_len); - } else { - buf_len = 2 * tlvs_len + 1; - buf = os_malloc(buf_len); - if (buf) { - wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); - wpa_msg_ctrl(wpa_s, MSG_INFO, - P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s", - MAC2STR(sa), update_indic, buf); - os_free(buf); - } - } - - while (pos < end) { - u8 srv_proto, srv_trans_id, status; - - wpa_printf(MSG_DEBUG, "P2P: Service Response TLV"); - slen = WPA_GET_LE16(pos); - pos += 2; - if (pos + slen > end || slen < 3) { - wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data " - "length"); - return; - } - tlv_end = pos + slen; - - srv_proto = *pos++; - wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", - srv_proto); - srv_trans_id = *pos++; - wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", - srv_trans_id); - status = *pos++; - wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u", - status); - - wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data", - pos, tlv_end - pos); - - if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) { - wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id, - pos, tlv_end); - } - - pos = tlv_end; - } - - wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len); -} - - -u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, - const struct wpabuf *tlvs) -{ - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) - return 0; - return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs); -} - - -u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, - u8 version, const char *query) -{ - struct wpabuf *tlvs; - u64 ret; - - tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query)); - if (tlvs == NULL) - return 0; - wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query)); - wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */ - wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */ - wpabuf_put_u8(tlvs, version); - wpabuf_put_str(tlvs, query); - ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); - wpabuf_free(tlvs); - return ret; -} - - -u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, - const char *svc_str, const char *info_substr) -{ - struct wpabuf *tlvs; - size_t plen, svc_len, substr_len = 0; - u64 ret; - - svc_len = os_strlen(svc_str); - if (info_substr) - substr_len = os_strlen(info_substr); - - if (svc_len > 0xff || substr_len > 0xff) - return 0; - - plen = 1 + 1 + 1 + svc_len + 1 + substr_len; - tlvs = wpabuf_alloc(2 + plen); - if (tlvs == NULL) - return 0; - - wpabuf_put_le16(tlvs, plen); - wpabuf_put_u8(tlvs, P2P_SERV_P2PS); - wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ - wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */ - wpabuf_put_data(tlvs, svc_str, svc_len); - wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */ - wpabuf_put_data(tlvs, info_substr, substr_len); - ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); - wpabuf_free(tlvs); - - return ret; -} - - -#ifdef CONFIG_WIFI_DISPLAY - -static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst, - const struct wpabuf *tlvs) -{ - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) - return 0; - return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs); -} - - -#define MAX_WFD_SD_SUBELEMS 20 - -static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role, - const char *subelems) -{ - u8 *len; - const char *pos; - int val; - int count = 0; - - len = wpabuf_put(tlvs, 2); - wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */ - wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ - - wpabuf_put_u8(tlvs, role); - - pos = subelems; - while (*pos) { - val = atoi(pos); - if (val >= 0 && val < 256) { - wpabuf_put_u8(tlvs, val); - count++; - if (count == MAX_WFD_SD_SUBELEMS) - break; - } - pos = os_strchr(pos + 1, ','); - if (pos == NULL) - break; - pos++; - } - - WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2); -} - - -u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, - const u8 *dst, const char *role) -{ - struct wpabuf *tlvs; - u64 ret; - const char *subelems; - u8 id = 1; - - subelems = os_strchr(role, ' '); - if (subelems == NULL) - return 0; - subelems++; - - tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS)); - if (tlvs == NULL) - return 0; - - if (os_strstr(role, "[source]")) - wfd_add_sd_req_role(tlvs, id++, 0x00, subelems); - if (os_strstr(role, "[pri-sink]")) - wfd_add_sd_req_role(tlvs, id++, 0x01, subelems); - if (os_strstr(role, "[sec-sink]")) - wfd_add_sd_req_role(tlvs, id++, 0x02, subelems); - if (os_strstr(role, "[source+sink]")) - wfd_add_sd_req_role(tlvs, id++, 0x03, subelems); - - ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs); - wpabuf_free(tlvs); - return ret; -} - -#endif /* CONFIG_WIFI_DISPLAY */ - - -int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req) -{ - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) - return -1; - return p2p_sd_cancel_request(wpa_s->global->p2p, - (void *) (uintptr_t) req); -} - - -void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, - const u8 *dst, u8 dialog_token, - const struct wpabuf *resp_tlvs) -{ - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) - return; - p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token, - resp_tlvs); -} - - -void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s) -{ - if (wpa_s->global->p2p) - p2p_sd_service_update(wpa_s->global->p2p); -} - - -static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv) -{ - dl_list_del(&bsrv->list); - wpabuf_free(bsrv->query); - wpabuf_free(bsrv->resp); - os_free(bsrv); -} - - -static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv) -{ - dl_list_del(&usrv->list); - os_free(usrv->service); - os_free(usrv); -} - - -void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s) -{ - struct p2p_srv_bonjour *bsrv, *bn; - struct p2p_srv_upnp *usrv, *un; - - dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour, - struct p2p_srv_bonjour, list) - wpas_p2p_srv_bonjour_free(bsrv); - - dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp, - struct p2p_srv_upnp, list) - wpas_p2p_srv_upnp_free(usrv); - - wpas_p2p_sd_service_update(wpa_s); -} - - -int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id) -{ - if (adv_id == 0) - return 1; - - if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id)) - return 1; - - return 0; -} - - -int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id) -{ - return p2p_service_del_asp(wpa_s->global->p2p, adv_id); -} - - -int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, - int auto_accept, u32 adv_id, - const char *adv_str, u8 svc_state, - u16 config_methods, const char *svc_info) -{ - return p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id, - adv_str, svc_state, config_methods, - svc_info); -} - - -int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, - struct wpabuf *query, struct wpabuf *resp) -{ - struct p2p_srv_bonjour *bsrv; - - bsrv = os_zalloc(sizeof(*bsrv)); - if (bsrv == NULL) - return -1; - bsrv->query = query; - bsrv->resp = resp; - dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list); - - wpas_p2p_sd_service_update(wpa_s); - return 0; -} - - -int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s, - const struct wpabuf *query) -{ - struct p2p_srv_bonjour *bsrv; - - bsrv = wpas_p2p_service_get_bonjour(wpa_s, query); - if (bsrv == NULL) - return -1; - wpas_p2p_srv_bonjour_free(bsrv); - wpas_p2p_sd_service_update(wpa_s); - return 0; -} - - -int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, - const char *service) -{ - struct p2p_srv_upnp *usrv; - - if (wpas_p2p_service_get_upnp(wpa_s, version, service)) - return 0; /* Already listed */ - usrv = os_zalloc(sizeof(*usrv)); - if (usrv == NULL) - return -1; - usrv->version = version; - usrv->service = os_strdup(service); - if (usrv->service == NULL) { - os_free(usrv); - return -1; - } - dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list); - - wpas_p2p_sd_service_update(wpa_s); - return 0; -} - - -int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, - const char *service) -{ - struct p2p_srv_upnp *usrv; - - usrv = wpas_p2p_service_get_upnp(wpa_s, version, service); - if (usrv == NULL) - return -1; - wpas_p2p_srv_upnp_free(usrv); - wpas_p2p_sd_service_update(wpa_s); - return 0; + return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, + freq); } @@ -3776,12 +2626,62 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer, } -static int freq_included(const struct p2p_channels *channels, unsigned int freq) +static int freq_included(struct wpa_supplicant *wpa_s, + const struct p2p_channels *channels, + unsigned int freq) { - if (channels == NULL) - return 1; /* Assume no restrictions */ - return p2p_channels_includes_freq(channels, freq); + if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) && + wpas_p2p_go_is_peer_freq(wpa_s, freq)) + return 1; + return 0; +} + +static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s) +{ + unsigned int num = P2P_MAX_CHANNELS; + int *common_freqs; + int ret; + + p2p_go_dump_common_freqs(wpa_s); + common_freqs = os_calloc(num, sizeof(int)); + if (!common_freqs) + return; + + ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num); + if (ret < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Failed to get group common freqs"); + os_free(common_freqs); + return; + } + + os_free(wpa_s->p2p_group_common_freqs); + wpa_s->p2p_group_common_freqs = common_freqs; + wpa_s->p2p_group_common_freqs_num = num; + p2p_go_dump_common_freqs(wpa_s); +} + + +/* + * Check if the given frequency is one of the possible operating frequencies + * set after the completion of the GO Negotiation. + */ +static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq) +{ + unsigned int i; + + p2p_go_dump_common_freqs(wpa_s); + + /* assume no restrictions */ + if (!wpa_s->p2p_group_common_freqs_num) + return 1; + + for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { + if (wpa_s->p2p_group_common_freqs[i] == freq) + return 1; + } + return 0; } @@ -3957,7 +2857,7 @@ accept_inv: "running a GO but we are capable of MCC, " "figure out the best channel to use"); *force_freq = 0; - } else if (!freq_included(channels, *force_freq)) { + } else if (!freq_included(wpa_s, channels, *force_freq)) { /* We are the GO, and *force_freq is not in the * intersection */ wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not " @@ -3995,7 +2895,8 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, int go = s->mode == WPAS_MODE_P2P_GO; wpas_p2p_group_add_persistent( wpa_s, s, go, 0, op_freq, 0, 0, NULL, - go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0); + go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, + 1); } else if (bssid) { wpa_s->user_initiated_pd = 0; wpas_p2p_join(wpa_s, bssid, go_dev_addr, @@ -4026,6 +2927,8 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, " unknown-network", MAC2STR(sa), MAC2STR(go_dev_addr)); } + wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, + bssid, 0, op_freq); return; } @@ -4038,6 +2941,8 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, "sa=" MACSTR " persistent=%d", MAC2STR(sa), s->id); } + wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid, + s->id, op_freq); } @@ -4046,6 +2951,7 @@ static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s, const u8 *peer, int inv) { size_t i; + struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; if (ssid == NULL) return; @@ -4075,8 +2981,8 @@ static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s, ssid->p2p_client_list + (i + 1) * 2 * ETH_ALEN, (ssid->num_p2p_clients - i - 1) * 2 * ETH_ALEN); ssid->num_p2p_clients--; - if (wpa_s->parent->conf->update_config && - wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + if (p2p_wpa_s->conf->update_config && + wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); } @@ -4163,10 +3069,10 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, os_sleep(0, 50000); if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO && - freq_included(channels, neg_freq)) + freq_included(wpa_s, channels, neg_freq)) freq = neg_freq; else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO && - freq_included(channels, peer_oper_freq)) + freq_included(wpa_s, channels, peer_oper_freq)) freq = peer_oper_freq; else freq = 0; @@ -4181,7 +3087,7 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, channels, ssid->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : - 0); + 0, 1); } @@ -4320,7 +3226,7 @@ struct p2p_oper_class_map { enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw; }; -static struct p2p_oper_class_map op_class[] = { +static const struct p2p_oper_class_map op_class[] = { { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 }, #if 0 /* Do not enable HT40 on 2 GHz for now */ { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS }, @@ -4328,6 +3234,7 @@ static struct p2p_oper_class_map op_class[] = { #endif { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 }, { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20 }, { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS }, { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS }, { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS }, @@ -4453,7 +3360,7 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, cla = cli_cla = 0; for (op = 0; op_class[op].op_class; op++) { - struct p2p_oper_class_map *o = &op_class[op]; + const struct p2p_oper_class_map *o = &op_class[op]; u8 ch; struct p2p_reg_class *reg = NULL, *cli_reg = NULL; @@ -4512,12 +3419,13 @@ int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, enum chan_allowed ret; for (op = 0; op_class[op].op_class; op++) { - struct p2p_oper_class_map *o = &op_class[op]; + const struct p2p_oper_class_map *o = &op_class[op]; u8 ch; for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { if (o->mode != HOSTAPD_MODE_IEEE80211A || - o->bw == BW20 || ch != channel) + (o->bw != BW40PLUS && o->bw != BW40MINUS) || + ch != channel) continue; ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); if (ret == ALLOWED) @@ -4581,12 +3489,7 @@ struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s, { for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { struct wpa_ssid *ssid = wpa_s->current_ssid; - if (ssid == NULL) - continue; - if (ssid->mode != WPAS_MODE_INFRA) - continue; - if (wpa_s->wpa_state != WPA_COMPLETED && - wpa_s->wpa_state != WPA_GROUP_HANDSHAKE) + if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group)) continue; if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0) return wpa_s; @@ -4667,14 +3570,12 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, iface.confname = wpa_s->confname; iface.ctrl_interface = wpa_s->conf->ctrl_interface; } - iface.conf_p2p_dev = NULL; p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s); if (!p2pdev_wpa_s) { wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface"); return -1; } - wpa_s->p2p_dev = p2pdev_wpa_s; wpa_s->pending_interface_name[0] = '\0'; return 0; @@ -4705,7 +3606,8 @@ static void wpas_presence_resp(void *ctx, const u8 *src, u8 status, static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid, size_t ssid_len, u8 *go_dev_addr, - u8 *ret_ssid, size_t *ret_ssid_len) + u8 *ret_ssid, size_t *ret_ssid_len, + u8 *intended_iface_addr) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; @@ -4715,6 +3617,19 @@ static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid, os_memcpy(ret_ssid, s->ssid, s->ssid_len); *ret_ssid_len = s->ssid_len; os_memcpy(go_dev_addr, s->bssid, ETH_ALEN); + + if (s->mode != WPAS_MODE_P2P_GO) { + os_memset(intended_iface_addr, 0, ETH_ALEN); + } else if (wpas_p2p_create_iface(wpa_s)) { + if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO)) + return 0; + + os_memcpy(intended_iface_addr, + wpa_s->pending_interface_addr, ETH_ALEN); + } else { + os_memcpy(intended_iface_addr, wpa_s->own_addr, + ETH_ALEN); + } return 1; } @@ -4729,17 +3644,24 @@ static int wpas_get_go_info(void *ctx, u8 *intended_addr, struct wpa_ssid *s; u8 bssid[ETH_ALEN]; + /* + * group_iface will be set to 1 only if a dedicated interface for P2P + * role is required. First, we try to reuse an active GO. However, + * if it is not present, we will try to reactivate an existing + * persistent group and set group_iface to 1, so the caller will know + * that the pending interface should be used. + */ + *group_iface = 0; s = wpas_p2p_group_go_ssid(wpa_s, bssid); if (!s) { s = wpas_p2p_get_persistent_go(wpa_s); + *group_iface = wpas_p2p_create_iface(wpa_s); if (s) os_memcpy(bssid, s->bssid, ETH_ALEN); + else + return 0; } - *group_iface = wpas_p2p_create_iface(wpa_s); - if (!s) - return 0; - os_memcpy(intended_addr, bssid, ETH_ALEN); os_memcpy(ssid, s->ssid, s->ssid_len); *ssid_len = s->ssid_len; @@ -4793,19 +3715,49 @@ static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go, } +static void wpas_p2ps_get_feat_cap_str(char *buf, size_t buf_len, + const u8 *feat_cap, size_t feat_cap_len) +{ + static const char pref[] = " feature_cap="; + int ret; + + buf[0] = '\0'; + + /* + * We expect a feature capability to contain at least one byte to be + * reported. The string buffer provided by the caller function is + * expected to be big enough to contain all bytes of the attribute for + * known specifications. This function truncates the reported bytes if + * the feature capability data exceeds the string buffer size. + */ + if (!feat_cap || !feat_cap_len || buf_len < sizeof(pref) + 2) + return; + + os_memcpy(buf, pref, sizeof(pref)); + ret = wpa_snprintf_hex(&buf[sizeof(pref) - 1], + buf_len - sizeof(pref) + 1, + feat_cap, feat_cap_len); + + if (ret != (2 * (int) feat_cap_len)) + wpa_printf(MSG_WARNING, "P2PS feature_cap bytes truncated"); +} + + static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, const u8 *adv_mac, const u8 *ses_mac, const u8 *grp_mac, u32 adv_id, u32 ses_id, u8 conncap, int passwd_id, const u8 *persist_ssid, size_t persist_ssid_size, int response_done, - int prov_start, const char *session_info) + int prov_start, const char *session_info, + const u8 *feat_cap, size_t feat_cap_len) { struct wpa_supplicant *wpa_s = ctx; u8 mac[ETH_ALEN]; struct wpa_ssid *persistent_go, *stale, *s; int save_config = 0; struct wpa_supplicant *go_wpa_s; + char feat_cap_str[256]; if (!dev) return; @@ -4818,6 +3770,9 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, if (!grp_mac) grp_mac = mac; + wpas_p2ps_get_feat_cap_str(feat_cap_str, sizeof(feat_cap_str), + feat_cap, feat_cap_len); + if (prov_start) { if (session_info == NULL) { wpa_msg_global(wpa_s, MSG_INFO, @@ -4825,22 +3780,22 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, " adv_id=%x conncap=%x" " adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d", + " dev_passwd_id=%d%s", MAC2STR(dev), adv_id, conncap, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id); + passwd_id, feat_cap_str); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_START MACSTR " adv_id=%x conncap=%x" " adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d info='%s'", + " dev_passwd_id=%d info='%s'%s", MAC2STR(dev), adv_id, conncap, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id, session_info); + passwd_id, session_info, feat_cap_str); } return; } @@ -4862,16 +3817,24 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, P2P_EVENT_P2PS_PROVISION_DONE MACSTR " status=%d" " adv_id=%x adv_mac=" MACSTR - " session=%x mac=" MACSTR, + " session=%x mac=" MACSTR "%s", MAC2STR(dev), status, adv_id, MAC2STR(adv_mac), - ses_id, MAC2STR(ses_mac)); + ses_id, MAC2STR(ses_mac), feat_cap_str); return; } /* Clean up stale persistent groups with this device */ s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid, persist_ssid_size); + + if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO && + is_zero_ether_addr(grp_mac)) { + wpa_dbg(wpa_s, MSG_ERROR, + "P2P: Peer device is a GO in a persistent group, but it did not provide the intended MAC address"); + return; + } + for (;;) { stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0); if (!stale) @@ -4927,29 +3890,39 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, " status=%d" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR - " persist=%d", + " persist=%d%s", MAC2STR(dev), status, adv_id, MAC2STR(adv_mac), - ses_id, MAC2STR(ses_mac), s->id); + ses_id, MAC2STR(ses_mac), s->id, feat_cap_str); return; } if (conncap == P2PS_SETUP_GROUP_OWNER) { - const char *go_ifname = NULL; + /* + * We need to copy the interface name. Simply saving a + * pointer isn't enough, since if we use pending_interface_name + * it will be overwritten when the group is added. + */ + char go_ifname[100]; + + go_ifname[0] = '\0'; if (!go_wpa_s) { wpa_s->global->pending_p2ps_group = 1; - if (wpa_s->conf->p2p_no_group_iface) - go_ifname = wpa_s->ifname; + if (!wpas_p2p_create_iface(wpa_s)) + os_memcpy(go_ifname, wpa_s->ifname, + sizeof(go_ifname)); else if (wpa_s->pending_interface_name[0]) - go_ifname = wpa_s->pending_interface_name; + os_memcpy(go_ifname, + wpa_s->pending_interface_name, + sizeof(go_ifname)); - if (!go_ifname) { + if (!go_ifname[0]) { wpas_p2ps_prov_complete( wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP, dev, adv_mac, ses_mac, - NULL, adv_id, ses_id, 0, 0, - NULL, 0, 0, 0, NULL); + grp_mac, adv_id, ses_id, 0, 0, + NULL, 0, 0, 0, NULL, NULL, 0); return; } @@ -4961,30 +3934,37 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : - 0); + 0, 0); } else if (response_done) { wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); } if (passwd_id == DEV_PW_P2PS_DEFAULT) { - os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN); - wpa_s->p2ps_join_addr_valid = 1; - wpa_dbg(wpa_s, MSG_DEBUG, - "P2PS: Saving PIN for " MACSTR, - MAC2STR(dev)); + os_memcpy(wpa_s->p2ps_join_addr, grp_mac, + ETH_ALEN); + wpa_s->p2ps_method_config_any = 1; } } else if (passwd_id == DEV_PW_P2PS_DEFAULT) { - go_ifname = go_wpa_s->ifname; + os_memcpy(go_ifname, go_wpa_s->ifname, + sizeof(go_ifname)); - wpa_dbg(go_wpa_s, MSG_DEBUG, - "P2P: Setting PIN-1 For " MACSTR, MAC2STR(dev)); - wpa_supplicant_ap_wps_pin(go_wpa_s, dev, "12345670", - NULL, 0, 0); + if (is_zero_ether_addr(grp_mac)) { + wpa_dbg(go_wpa_s, MSG_DEBUG, + "P2P: Setting PIN-1 for ANY"); + wpa_supplicant_ap_wps_pin(go_wpa_s, NULL, + "12345670", NULL, 0, + 0); + } else { + wpa_dbg(go_wpa_s, MSG_DEBUG, + "P2P: Setting PIN-1 for " MACSTR, + MAC2STR(grp_mac)); + wpa_supplicant_ap_wps_pin(go_wpa_s, grp_mac, + "12345670", NULL, 0, + 0); + } - os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN); - wpa_s->p2ps_join_addr_valid = 1; - wpa_dbg(wpa_s, MSG_DEBUG, - "P2PS: Saving PIN for " MACSTR, MAC2STR(dev)); + os_memcpy(wpa_s->p2ps_join_addr, grp_mac, ETH_ALEN); + wpa_s->p2ps_method_config_any = 1; } wpa_msg_global(wpa_s, MSG_INFO, @@ -4992,11 +3972,11 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, " status=%d conncap=%x" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d go=%s", + " dev_passwd_id=%d go=%s%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id, go_ifname); + passwd_id, go_ifname, feat_cap_str); return; } @@ -5014,22 +3994,22 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, " status=%d conncap=%x" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d join=" MACSTR, + " dev_passwd_id=%d join=" MACSTR "%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id, MAC2STR(grp_mac)); + passwd_id, MAC2STR(grp_mac), feat_cap_str); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_DONE MACSTR " status=%d conncap=%x" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d", + " dev_passwd_id=%d%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id); + passwd_id, feat_cap_str); } } @@ -5059,7 +4039,7 @@ static int wpas_prov_disc_resp_cb(void *ctx) wpas_p2p_group_add_persistent( wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? - P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0); + P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0); } else { wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); } @@ -5068,6 +4048,17 @@ static int wpas_prov_disc_resp_cb(void *ctx) } +static int wpas_p2p_get_pref_freq_list(void *ctx, int go, + unsigned int *len, + unsigned int *freq_list) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO : + WPA_IF_P2P_CLIENT, len, freq_list); +} + + /** * wpas_p2p_init - Initialize P2P module for %wpa_supplicant * @global: Pointer to global data from wpa_supplicant_init() @@ -5121,6 +4112,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.p2ps_prov_complete = wpas_p2ps_prov_complete; p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb; p2p.p2ps_group_capability = p2ps_group_capability; + p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list; os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN); os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN); @@ -5152,9 +4144,9 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) */ if (p2p_config_get_random_social(&p2p, &p2p.reg_class, &p2p.channel) != 0) { - wpa_printf(MSG_ERROR, - "P2P: Failed to select random social channel as listen channel"); - return -1; + wpa_printf(MSG_INFO, + "P2P: No social channels supported by the driver - do not enable P2P"); + return 0; } p2p.channel_forced = 0; } @@ -5425,6 +4417,7 @@ static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s) } wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); + wpas_notify_p2p_group_formation_failure(wpa_s, ""); } } @@ -5623,10 +4616,25 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_s->pending_join_iface_addr); } if (bss) { + u8 dev_addr[ETH_ALEN]; + freq = bss->freq; wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency " "from BSS table: %d MHz (SSID %s)", freq, wpa_ssid_txt(bss->ssid, bss->ssid_len)); + if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len, + dev_addr) == 0 && + os_memcmp(wpa_s->pending_join_dev_addr, + wpa_s->pending_join_iface_addr, ETH_ALEN) == 0 && + os_memcmp(dev_addr, wpa_s->pending_join_dev_addr, + ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "P2P: Update target GO device address based on BSS entry: " MACSTR " (was " MACSTR ")", + MAC2STR(dev_addr), + MAC2STR(wpa_s->pending_join_dev_addr)); + os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, + ETH_ALEN); + } } if (freq > 0) { u16 method; @@ -5635,6 +4643,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE "reason=FREQ_CONFLICT"); + wpas_notify_p2p_group_formation_failure( + wpa_s, "FREQ_CONFLICT"); return; } @@ -5654,6 +4664,9 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, case WPS_PBC: method = WPS_CONFIG_PUSHBUTTON; break; + case WPS_P2PS: + method = WPS_CONFIG_P2PS; + break; default: method = 0; break; @@ -5896,11 +4909,16 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, - int *force_freq, int *pref_freq, int go) + int *force_freq, int *pref_freq, int go, + unsigned int *pref_freq_list, + unsigned int *num_pref_freq) { struct wpa_used_freq_data *freqs; int res, best_freq, num_unused; - unsigned int freq_in_use = 0, num, i; + unsigned int freq_in_use = 0, num, i, max_pref_freq; + + max_pref_freq = *num_pref_freq; + *num_pref_freq = 0; freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); @@ -5965,6 +4983,47 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); + if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) { + enum wpa_driver_if_type iface_type; + + if (go) + iface_type = WPA_IF_P2P_GO; + else + iface_type = WPA_IF_P2P_CLIENT; + + wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d", + best_freq, go); + + res = wpa_drv_get_pref_freq_list(wpa_s, iface_type, + &max_pref_freq, + pref_freq_list); + if (!res && max_pref_freq > 0) { + *num_pref_freq = max_pref_freq; + i = 0; + while (wpas_p2p_disallowed_freq(wpa_s->global, + pref_freq_list[i]) && + i < *num_pref_freq) { + wpa_printf(MSG_DEBUG, + "P2P: preferred_freq_list[%d]=%d is disallowed", + i, pref_freq_list[i]); + i++; + } + if (i != *num_pref_freq) { + best_freq = pref_freq_list[i]; + wpa_printf(MSG_DEBUG, + "P2P: Using preferred_freq_list[%d]=%d", + i, best_freq); + } else { + wpa_printf(MSG_DEBUG, + "P2P: All driver preferred frequencies are disallowed for P2P use"); + *num_pref_freq = 0; + } + } else { + wpa_printf(MSG_DEBUG, + "P2P: No preferred frequency list available"); + } + } + /* We have a candidate frequency to use */ if (best_freq > 0) { if (*pref_freq == 0 && num_unused > 0) { @@ -6029,6 +5088,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, enum wpa_driver_if_type iftype; const u8 *if_addr; struct wpa_ssid *ssid = NULL; + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -6045,6 +5105,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->global->p2p_fail_on_wps_complete = 0; wpa_s->global->pending_p2ps_group = 0; + wpa_s->p2ps_method_config_any = 0; if (go_intent < 0) go_intent = wpa_s->conf->p2p_go_intent; @@ -6105,13 +5166,16 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, return ret; } + size = P2P_MAX_PREF_CHANNELS; res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, - go_intent == 15); + go_intent == 15, pref_freq_list, &size); if (res) return res; wpas_p2p_set_own_freq_preference(wpa_s, force_freq ? force_freq : pref_freq); + p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size); + wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); if (wpa_s->create_p2p_iface) { @@ -6126,8 +5190,10 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } if_addr = wpa_s->pending_interface_addr; - } else + } else { if_addr = wpa_s->own_addr; + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); + } if (auth) { if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, @@ -6272,6 +5338,38 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) { unsigned int r; + if (!wpa_s->conf->num_p2p_pref_chan && !freq) { + unsigned int i, size = P2P_MAX_PREF_CHANNELS; + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; + int res; + + res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO, + &size, pref_freq_list); + if (!res && size > 0) { + i = 0; + while (wpas_p2p_disallowed_freq(wpa_s->global, + pref_freq_list[i]) && + i < size) { + wpa_printf(MSG_DEBUG, + "P2P: preferred_freq_list[%d]=%d is disallowed", + i, pref_freq_list[i]); + i++; + } + if (i != size) { + freq = pref_freq_list[i]; + wpa_printf(MSG_DEBUG, + "P2P: Using preferred_freq_list[%d]=%d", + i, freq); + } else { + wpa_printf(MSG_DEBUG, + "P2P: All driver preferred frequencies are disallowed for P2P use"); + } + } else { + wpa_printf(MSG_DEBUG, + "P2P: No preferred frequency list available"); + } + } + if (freq == 2) { wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz " "band"); @@ -6335,30 +5433,45 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) } -static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, - struct p2p_go_neg_results *params, - const struct p2p_channels *channels) +static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s, + const struct p2p_channels *channels, + int freq) +{ + if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) && + p2p_supported_freq_go(wpa_s->global->p2p, freq) && + freq_included(wpa_s, channels, freq)) + return 1; + return 0; +} + + +static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params, + const struct p2p_channels *channels) { unsigned int i, r; /* first try some random selection of the social channels */ if (os_get_random((u8 *) &r, sizeof(r)) < 0) - return -1; + return; for (i = 0; i < 3; i++) { params->freq = 2412 + ((r + i) % 3) * 25; - if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && - p2p_supported_freq(wpa_s->global->p2p, params->freq)) + if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) goto out; } - /* try all channels in reg. class 81 */ + /* try all other channels in operating class 81 */ for (i = 0; i < 11; i++) { params->freq = 2412 + i * 5; - if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && - p2p_supported_freq(wpa_s->global->p2p, params->freq)) + + /* skip social channels; covered in the previous loop */ + if (params->freq == 2412 || + params->freq == 2437 || + params->freq == 2462) + continue; + + if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) goto out; } @@ -6366,7 +5479,7 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, for (i = 0; i < 4; i++) { params->freq = 5180 + i * 20; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && + freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; } @@ -6375,7 +5488,7 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, for (i = 0; i < 4; i++) { params->freq = 5745 + i * 20; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && + freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; } @@ -6383,7 +5496,7 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, /* try social channel class 180 channel 2 */ params->freq = 58320 + 1 * 2160; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && + freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; @@ -6391,17 +5504,17 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, for (i = 0; i < 4; i++) { params->freq = 58320 + i * 2160; if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && - freq_included(channels, params->freq) && + freq_included(wpa_s, channels, params->freq) && p2p_supported_freq(wpa_s->global->p2p, params->freq)) goto out; } + params->freq = 0; wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed"); - return -1; + return; out: wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)", params->freq); - return 0; } @@ -6411,75 +5524,17 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, const struct p2p_channels *channels) { struct wpa_used_freq_data *freqs; - unsigned int pref_freq, cand_freq; + unsigned int cand; unsigned int num, i; os_memset(params, 0, sizeof(*params)); params->role_go = 1; params->ht40 = ht40; params->vht = vht; - if (freq) { - if (!freq_included(channels, freq)) { - wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not " - "accepted", freq); - return -1; - } - wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced " - "frequency %d MHz", freq); - params->freq = freq; - } else if (wpa_s->conf->p2p_oper_reg_class == 81 && - wpa_s->conf->p2p_oper_channel >= 1 && - wpa_s->conf->p2p_oper_channel <= 11 && - freq_included(channels, - 2407 + 5 * wpa_s->conf->p2p_oper_channel)) { - params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel; - wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " - "frequency %d MHz", params->freq); - } else if ((wpa_s->conf->p2p_oper_reg_class == 115 || - wpa_s->conf->p2p_oper_reg_class == 116 || - wpa_s->conf->p2p_oper_reg_class == 117 || - wpa_s->conf->p2p_oper_reg_class == 124 || - wpa_s->conf->p2p_oper_reg_class == 126 || - wpa_s->conf->p2p_oper_reg_class == 127) && - freq_included(channels, - 5000 + 5 * wpa_s->conf->p2p_oper_channel)) { - params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel; - wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " - "frequency %d MHz", params->freq); - } else if (wpa_s->conf->p2p_oper_channel == 0 && - wpa_s->best_overall_freq > 0 && - p2p_supported_freq_go(wpa_s->global->p2p, - wpa_s->best_overall_freq) && - freq_included(channels, wpa_s->best_overall_freq)) { - params->freq = wpa_s->best_overall_freq; - wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall " - "channel %d MHz", params->freq); - } else if (wpa_s->conf->p2p_oper_channel == 0 && - wpa_s->best_24_freq > 0 && - p2p_supported_freq_go(wpa_s->global->p2p, - wpa_s->best_24_freq) && - freq_included(channels, wpa_s->best_24_freq)) { - params->freq = wpa_s->best_24_freq; - wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz " - "channel %d MHz", params->freq); - } else if (wpa_s->conf->p2p_oper_channel == 0 && - wpa_s->best_5_freq > 0 && - p2p_supported_freq_go(wpa_s->global->p2p, - wpa_s->best_5_freq) && - freq_included(channels, wpa_s->best_5_freq)) { - params->freq = wpa_s->best_5_freq; - wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz " - "channel %d MHz", params->freq); - } else if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p, - channels))) { - params->freq = pref_freq; - wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred " - "channels", params->freq); - } else { - /* no preference, select some channel */ - if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0) - return -1; - } + + if (wpa_s->p2p_group_common_freqs_num) + wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO", + __func__); freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); @@ -6489,51 +5544,166 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, wpa_s->num_multichan_concurrent); - cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); + /* try using the forced freq */ + if (freq) { + if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) { + wpa_printf(MSG_DEBUG, + "P2P: Forced GO freq %d MHz not accepted", + freq); + goto fail; + } - /* First try the best used frequency if possible */ - if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) { - params->freq = cand_freq; - } else if (!freq) { - /* Try any of the used frequencies */ for (i = 0; i < num; i++) { - if (freq_included(channels, freqs[i].freq)) { - wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)", - freqs[i].freq); + if (freqs[i].freq == freq) { + wpa_printf(MSG_DEBUG, + "P2P: forced freq (%d MHz) is also shared", + freq); + params->freq = freq; + goto success; + } + } + + if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + wpa_printf(MSG_DEBUG, + "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use", + freq); + goto fail; + } + + wpa_printf(MSG_DEBUG, + "P2P: force GO freq (%d MHz) on a free channel", + freq); + params->freq = freq; + goto success; + } + + /* consider using one of the shared frequencies */ + if (num) { + cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); + if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { + wpa_printf(MSG_DEBUG, + "P2P: Use shared freq (%d MHz) for GO", + freq); + params->freq = cand; + goto success; + } + + /* try using one of the shared freqs */ + for (i = 0; i < num; i++) { + if (wpas_p2p_supported_freq_go(wpa_s, channels, + freqs[i].freq)) { + wpa_printf(MSG_DEBUG, + "P2P: Use shared freq (%d MHz) for GO", + freq); params->freq = freqs[i].freq; - break; - } - } - - if (i == num) { - if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { - wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using"); - os_free(freqs); - return -1; - } else { - wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels"); - } - } - } else { - for (i = 0; i < num; i++) { - if (freqs[i].freq == freq) - break; - } - - if (i == num) { - if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { - if (freq) - wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq); - os_free(freqs); - return -1; - } else { - wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels"); + goto success; } } } + if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + wpa_printf(MSG_DEBUG, + "P2P: Cannot force GO on any of the channels we are already using"); + goto fail; + } + + /* try using the setting from the configuration file */ + if (wpa_s->conf->p2p_oper_reg_class == 81 && + wpa_s->conf->p2p_oper_channel >= 1 && + wpa_s->conf->p2p_oper_channel <= 11 && + wpas_p2p_supported_freq_go( + wpa_s, channels, + 2407 + 5 * wpa_s->conf->p2p_oper_channel)) { + params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " + "frequency %d MHz", params->freq); + goto success; + } + + if ((wpa_s->conf->p2p_oper_reg_class == 115 || + wpa_s->conf->p2p_oper_reg_class == 116 || + wpa_s->conf->p2p_oper_reg_class == 117 || + wpa_s->conf->p2p_oper_reg_class == 124 || + wpa_s->conf->p2p_oper_reg_class == 125 || + wpa_s->conf->p2p_oper_reg_class == 126 || + wpa_s->conf->p2p_oper_reg_class == 127) && + wpas_p2p_supported_freq_go(wpa_s, channels, + 5000 + + 5 * wpa_s->conf->p2p_oper_channel)) { + params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " + "frequency %d MHz", params->freq); + goto success; + } + + /* Try using best channels */ + if (wpa_s->conf->p2p_oper_channel == 0 && + wpa_s->best_overall_freq > 0 && + wpas_p2p_supported_freq_go(wpa_s, channels, + wpa_s->best_overall_freq)) { + params->freq = wpa_s->best_overall_freq; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall " + "channel %d MHz", params->freq); + goto success; + } + + if (wpa_s->conf->p2p_oper_channel == 0 && + wpa_s->best_24_freq > 0 && + wpas_p2p_supported_freq_go(wpa_s, channels, + wpa_s->best_24_freq)) { + params->freq = wpa_s->best_24_freq; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz " + "channel %d MHz", params->freq); + goto success; + } + + if (wpa_s->conf->p2p_oper_channel == 0 && + wpa_s->best_5_freq > 0 && + wpas_p2p_supported_freq_go(wpa_s, channels, + wpa_s->best_5_freq)) { + params->freq = wpa_s->best_5_freq; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz " + "channel %d MHz", params->freq); + goto success; + } + + /* try using preferred channels */ + cand = p2p_get_pref_freq(wpa_s->global->p2p, channels); + if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { + params->freq = cand; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred " + "channels", params->freq); + goto success; + } + + /* Try using one of the group common freqs */ + if (wpa_s->p2p_group_common_freqs) { + for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { + cand = wpa_s->p2p_group_common_freqs[i]; + if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { + params->freq = cand; + wpa_printf(MSG_DEBUG, + "P2P: Use freq %d MHz common with the peer", + params->freq); + goto success; + } + } + } + + /* no preference, select some channel */ + wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels); + + if (params->freq == 0) { + wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use"); + goto fail; + } + +success: os_free(freqs); return 0; +fail: + os_free(freqs); + return -1; } @@ -6636,13 +5806,15 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, struct wpa_ssid *params, int addr_allocated, - int freq) + int freq, int force_scan) { struct wpa_ssid *ssid; wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0); if (wpa_s == NULL) return -1; + if (force_scan) + os_get_reltime(&wpa_s->scan_min_time); wpa_s->p2p_last_4way_hs_fail = NULL; wpa_supplicant_ap_deinit(wpa_s); @@ -6650,6 +5822,7 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) return -1; + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); wpa_config_set_network_defaults(ssid); ssid->temporary = 1; ssid->proto = WPA_PROTO_RSN; @@ -6691,7 +5864,7 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, int force_freq, int neg_freq, int ht40, int vht, const struct p2p_channels *channels, - int connection_timeout) + int connection_timeout, int force_scan) { struct p2p_go_neg_results params; int go = 0, freq; @@ -6703,6 +5876,23 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, go == (ssid->mode == WPAS_MODE_P2P_GO)) { wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is " "already running"); + if (go == 0 && + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL)) { + /* + * This can happen if Invitation Response frame was lost + * and the peer (GO of a persistent group) tries to + * invite us again. Reschedule the timeout to avoid + * terminating the wait for the connection too early + * since we now know that the peer is still trying to + * invite us instead of having already started the GO. + */ + wpa_printf(MSG_DEBUG, + "P2P: Reschedule group formation timeout since peer is still trying to invite us"); + eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, + wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + } return 0; } @@ -6722,21 +5912,30 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, } else { freq = wpas_p2p_select_go_freq(wpa_s, neg_freq); if (freq < 0 || - (freq > 0 && !freq_included(channels, freq))) + (freq > 0 && !freq_included(wpa_s, channels, freq))) freq = 0; } - } else { + } else if (ssid->mode == WPAS_MODE_INFRA) { freq = neg_freq; - if (freq < 0 || - (freq > 0 && !freq_included(channels, freq))) - freq = 0; - } + if (freq <= 0 || !freq_included(wpa_s, channels, freq)) { + struct os_reltime now; + struct wpa_bss *bss = + wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid); - if (ssid->mode == WPAS_MODE_INFRA) - return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq); + os_get_reltime(&now); + if (bss && + !os_reltime_expired(&now, &bss->last_update, 5) && + freq_included(wpa_s, channels, bss->freq)) + freq = bss->freq; + else + freq = 0; + } - if (ssid->mode != WPAS_MODE_P2P_GO) + return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq, + force_scan); + } else { return -1; + } if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, channels)) return -1; @@ -6909,7 +6108,7 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } if (wpa_s->global->p2p) p2p_wps_success_cb(wpa_s->global->p2p, peer_addr); - wpas_group_formation_completed(wpa_s, 1); + wpas_group_formation_completed(wpa_s, 1, 0); } @@ -7163,7 +6362,11 @@ int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (wpa_s->global->p2p_disabled) return -1; - if (wpa_s->conf->p2p_disabled) + /* + * Advertize mandatory cross connection capability even on + * p2p_disabled=1 interface when associating with a P2P Manager WLAN AP. + */ + if (wpa_s->conf->p2p_disabled && p2p_group) return -1; if (wpa_s->global->p2p == NULL) return -1; @@ -7181,7 +6384,8 @@ int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, - const u8 *ie, size_t ie_len, int ssi_signal) + const u8 *ie, size_t ie_len, + unsigned int rx_freq, int ssi_signal) { if (wpa_s->global->p2p_disabled) return 0; @@ -7189,7 +6393,7 @@ int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, return 0; switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid, - ie, ie_len)) { + ie, ie_len, rx_freq)) { case P2P_PREQ_NOT_P2P: wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal); @@ -7263,6 +6467,7 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int force_freq = 0; int res; int no_pref_freq_given = pref_freq == 0; + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; wpa_s->global->p2p_invite_group = NULL; if (peer_addr) @@ -7296,10 +6501,13 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } wpa_s->pending_invite_ssid_id = ssid->id; + size = P2P_MAX_PREF_CHANNELS; res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, - role == P2P_INVITE_ROLE_GO); + role == P2P_INVITE_ROLE_GO, + pref_freq_list, &size); if (res) return res; + p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -7336,6 +6544,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, int persistent; int freq = 0, force_freq = 0, pref_freq = 0; int res; + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; wpa_s->p2p_persistent_go_freq = 0; wpa_s->p2p_go_ht40 = 0; @@ -7387,8 +6596,10 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + size = P2P_MAX_PREF_CHANNELS; res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, - role == P2P_INVITE_ROLE_ACTIVE_GO); + role == P2P_INVITE_ROLE_ACTIVE_GO, + pref_freq_list, &size); if (res) return res; wpas_p2p_set_own_freq_preference(wpa_s, force_freq); @@ -7921,7 +7132,7 @@ int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) "session overlap"); if (wpa_s != wpa_s->parent) wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP); - wpas_p2p_group_formation_failed(wpa_s); + wpas_p2p_group_formation_failed(wpa_s, 0); return 1; } @@ -7933,14 +7144,22 @@ void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx) } -void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) +void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, + enum wpas_p2p_channel_update_trig trig) { struct p2p_channels chan, cli_chan; - struct wpa_supplicant *ifs; + struct wpa_used_freq_data *freqs = NULL; + unsigned int num = wpa_s->num_multichan_concurrent; if (wpa_s->global == NULL || wpa_s->global->p2p == NULL) return; + freqs = os_calloc(num, sizeof(struct wpa_used_freq_data)); + if (!freqs) + return; + + num = get_shared_radio_freqs_data(wpa_s, freqs, num); + os_memset(&chan, 0, sizeof(chan)); os_memset(&cli_chan, 0, sizeof(cli_chan)); if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) { @@ -7951,27 +7170,17 @@ void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan); - for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { - int freq; - if (!ifs->current_ssid || - !ifs->current_ssid->p2p_group || - (ifs->current_ssid->mode != WPAS_MODE_P2P_GO && - ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)) - continue; - freq = ifs->current_ssid->frequency; - if (freq_included(&chan, freq)) { - wpa_dbg(ifs, MSG_DEBUG, - "P2P GO operating frequency %d MHz in valid range", - freq); - continue; - } + wpas_p2p_optimize_listen_channel(wpa_s, freqs, num); - wpa_dbg(ifs, MSG_DEBUG, - "P2P GO operating in invalid frequency %d MHz", freq); - /* TODO: Consider using CSA or removing the group within - * wpa_supplicant */ - wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP); - } + /* + * The used frequencies map changed, so it is possible that a GO is + * using a channel that is no longer valid for P2P use. It is also + * possible that due to policy consideration, it would be preferable to + * move it to a frequency already used by other station interfaces. + */ + wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig); + + os_free(freqs); } @@ -8031,7 +7240,7 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, NULL); if (wpa_s->p2p_in_provisioning) { - wpas_group_formation_completed(wpa_s, 0); + wpas_group_formation_completed(wpa_s, 0, 0); break; } wpas_p2p_group_delete(wpa_s, @@ -8041,7 +7250,7 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling", wpa_s->ifname); found = 1; - wpas_p2p_group_formation_failed(wpa_s); + wpas_p2p_group_formation_failed(wpa_s, 0); } } @@ -8237,7 +7446,7 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, */ if (wpa_s->global->p2p) p2p_wps_success_cb(wpa_s->global->p2p, addr); - wpas_group_formation_completed(wpa_s, 1); + wpas_group_formation_completed(wpa_s, 1, 0); } } if (!wpa_s->p2p_go_group_formation_completed) { @@ -8262,7 +7471,7 @@ static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, if (wpa_s->global->p2p_group_formation) group = wpa_s->global->p2p_group_formation; - wpa_s = wpa_s->parent; + wpa_s = wpa_s->global->p2p_init_wpa_s; offchannel_send_action_done(wpa_s); if (group_added) ret = wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT); @@ -8517,16 +7726,17 @@ void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer, { struct wpa_ssid *s; struct wpa_supplicant *w; + struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer)); /* Remove from any persistent group */ - for (s = wpa_s->parent->conf->ssid; s; s = s->next) { + for (s = p2p_wpa_s->conf->ssid; s; s = s->next) { if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO) continue; if (!iface_addr) - wpas_remove_persistent_peer(wpa_s, s, peer, 0); - wpas_p2p_remove_psk(wpa_s->parent, s, peer, iface_addr); + wpas_remove_persistent_peer(p2p_wpa_s, s, peer, 0); + wpas_p2p_remove_psk(p2p_wpa_s, s, peer, iface_addr); } /* Remove from any operating group */ @@ -9230,6 +8440,16 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, u8 curr_chan, cand, chan; unsigned int i; + /* + * If possible, optimize the Listen channel to be a channel that is + * already used by one of the other interfaces. + */ + if (!wpa_s->conf->p2p_optimize_listen_chan) + return; + + if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED) + return; + curr_chan = p2p_get_listen_channel(wpa_s->global->p2p); for (i = 0, cand = 0; i < num; i++) { ieee80211_freq_to_chan(freqs[i].freq, &chan); @@ -9251,23 +8471,86 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, } -void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) +static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s) { - struct wpa_used_freq_data *freqs; - unsigned int num = wpa_s->num_multichan_concurrent; + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) { + wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled"); + return -1; + } - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + /* TODO: Add CSA support */ + wpa_dbg(wpa_s, MSG_DEBUG, "Moving GO with CSA is not implemented"); + return -1; +} + + +static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s) +{ + struct p2p_go_neg_results params; + struct wpa_ssid *current_ssid = wpa_s->current_ssid; + + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP); + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz", + current_ssid->frequency); + + /* Stop the AP functionality */ + /* TODO: Should do this in a way that does not indicated to possible + * P2P Clients in the group that the group is terminated. */ + wpa_supplicant_ap_deinit(wpa_s); + + /* Reselect the GO frequency */ + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, NULL)) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq"); + wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL); return; + } + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New freq selected for the GO (%u MHz)", + params.freq); + + if (params.freq && + !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) { + wpa_printf(MSG_DEBUG, + "P2P: Selected freq (%u MHz) is not valid for P2P", + params.freq); + wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL); + return; + } + + /* Update the frequency */ + current_ssid->frequency = params.freq; + wpa_s->connect_without_scan = current_ssid; + wpa_s->reassociate = 1; + wpa_s->disconnected = 0; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->ap_iface || !wpa_s->current_ssid) + return; + + wpas_p2p_go_update_common_freqs(wpa_s); /* - * If possible, optimize the Listen channel to be a channel that is - * already used by one of the other interfaces. + * First, try a channel switch flow. If it is not supported or fails, + * take down the GO and bring it up again. */ - if (!wpa_s->conf->p2p_optimize_listen_chan) - return; + if (wpas_p2p_move_go_csa(wpa_s) < 0) + wpas_p2p_move_go_no_csa(wpa_s); +} - if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED) - return; + +static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_used_freq_data *freqs = NULL; + unsigned int num = wpa_s->num_multichan_concurrent; freqs = os_calloc(num, sizeof(struct wpa_used_freq_data)); if (!freqs) @@ -9275,11 +8558,158 @@ void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) num = get_shared_radio_freqs_data(wpa_s, freqs, num); - wpas_p2p_optimize_listen_channel(wpa_s, freqs, num); + /* Previous attempt to move a GO was not possible -- try again. */ + wpas_p2p_consider_moving_gos(wpa_s, freqs, num, + WPAS_P2P_CHANNEL_UPDATE_ANY); + os_free(freqs); } +/* + * Consider moving a GO from its currently used frequency: + * 1. It is possible that due to regulatory consideration the frequency + * can no longer be used and there is a need to evacuate the GO. + * 2. It is possible that due to MCC considerations, it would be preferable + * to move the GO to a channel that is currently used by some other + * station interface. + * + * In case a frequency that became invalid is once again valid, cancel a + * previously initiated GO frequency change. + */ +static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, + unsigned int num) +{ + unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0; + unsigned int timeout; + int freq; + + wpas_p2p_go_update_common_freqs(wpa_s); + + freq = wpa_s->current_ssid->frequency; + for (i = 0, invalid_freq = 0; i < num; i++) { + if (freqs[i].freq == freq) { + flags = freqs[i].flags; + + /* The channel is invalid, must change it */ + if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Freq=%d MHz no longer valid for GO", + freq); + invalid_freq = 1; + } + } else if (freqs[i].flags == 0) { + /* Freq is not used by any other station interface */ + continue; + } else if (!p2p_supported_freq(wpa_s->global->p2p, + freqs[i].freq)) { + /* Freq is not valid for P2P use cases */ + continue; + } else if (wpa_s->conf->p2p_go_freq_change_policy == + P2P_GO_FREQ_MOVE_SCM) { + policy_move = 1; + } else if (wpa_s->conf->p2p_go_freq_change_policy == + P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS && + wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) { + policy_move = 1; + } + } + + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: GO move: invalid_freq=%u, policy_move=%u, flags=0x%X", + invalid_freq, policy_move, flags); + + /* + * The channel is valid, or we are going to have a policy move, so + * cancel timeout. + */ + if (!invalid_freq || policy_move) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Cancel a GO move from freq=%d MHz", freq); + eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); + + if (wpas_p2p_in_progress(wpa_s)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move"); + eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, + wpa_s, NULL); + eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0, + wpas_p2p_reconsider_moving_go, + wpa_s, NULL); + return; + } + } + + if (!invalid_freq && (!policy_move || flags != 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Not initiating a GO frequency change"); + return; + } + + if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq)) + timeout = P2P_GO_FREQ_CHANGE_TIME; + else + timeout = 0; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz in %d secs", + freq, timeout); + eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); + eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL); +} + + +static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, + unsigned int num, + enum wpas_p2p_channel_update_trig trig) +{ + struct wpa_supplicant *ifs; + + eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX, + NULL); + + /* + * Travers all the radio interfaces, and for each GO interface, check + * if there is a need to move the GO from the frequency it is using, + * or in case the frequency is valid again, cancel the evacuation flow. + */ + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (ifs->current_ssid == NULL || + ifs->current_ssid->mode != WPAS_MODE_P2P_GO) + continue; + + /* + * The GO was just started or completed channel switch, no need + * to move it. + */ + if (wpa_s == ifs && + (trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE || + trig == WPAS_P2P_CHANNEL_UPDATE_CS)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: GO move - schedule re-consideration"); + eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0, + wpas_p2p_reconsider_moving_go, + wpa_s, NULL); + continue; + } + + wpas_p2p_consider_moving_one_go(ifs, freqs, num); + } +} + + +void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + + wpas_p2p_update_channel_list(wpa_s, + WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE); +} + + void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s) { if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) { diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.h b/contrib/wpa/wpa_supplicant/p2p_supplicant.h index b7861786ca2..56e683498d6 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.h +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.h @@ -17,6 +17,15 @@ struct p2p_channels; struct wps_event_fail; struct p2ps_provision; +enum wpas_p2p_channel_update_trig { + WPAS_P2P_CHANNEL_UPDATE_ANY, + WPAS_P2P_CHANNEL_UPDATE_DRIVER, + WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE, + WPAS_P2P_CHANNEL_UPDATE_AVOID, + WPAS_P2P_CHANNEL_UPDATE_DISALLOW, + WPAS_P2P_CHANNEL_UPDATE_CS, +}; + int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, const char *conf_p2p_dev); struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s, @@ -36,7 +45,7 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, int force_freq, int neg_freq, int ht40, int vht, const struct p2p_channels *channels, - int connection_timeout); + int connection_timeout, int force_scan); struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); enum wpas_p2p_prov_disc_use { @@ -66,7 +75,6 @@ int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout); int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, u8 *buf, size_t len, int p2p_group); void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies); -void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s); u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, const struct wpabuf *tlvs); u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, @@ -91,9 +99,15 @@ int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, const char *service); int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept, u32 adv_id, const char *adv_str, u8 svc_state, - u16 config_methods, const char *svc_info); + u16 config_methods, const char *svc_info, + const u8 *cpt_priority); int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id); +void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s); int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id); +void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len); +void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, @@ -153,10 +167,13 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s); int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, - int ssi_signal); + unsigned int rx_freq, int ssi_signal); void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int registrar); -void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s); + +void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, + enum wpas_p2p_channel_update_trig trig); + void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s, int freq_24, int freq_5, int freq_overall); void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, @@ -207,7 +224,7 @@ static inline int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, - int ssi_signal) + unsigned int rx_freq, int ssi_signal) { return 0; } @@ -217,7 +234,9 @@ static inline void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, { } -static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) +static inline void +wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, + enum wpas_p2p_channel_update_trig trig) { } diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c b/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c new file mode 100644 index 00000000000..fc07b07462f --- /dev/null +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c @@ -0,0 +1,1273 @@ +/* + * wpa_supplicant - P2P service discovery + * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2010-2014, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "p2p/p2p.h" +#include "wpa_supplicant_i.h" +#include "notify.h" +#include "p2p_supplicant.h" + + +/* + * DNS Header section is used only to calculate compression pointers, so the + * contents of this data does not matter, but the length needs to be reserved + * in the virtual packet. + */ +#define DNS_HEADER_LEN 12 + +/* + * 27-octet in-memory packet from P2P specification containing two implied + * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN + */ +#define P2P_SD_IN_MEMORY_LEN 27 + +static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start, + u8 **spos, const u8 *end) +{ + while (*spos < end) { + u8 val = ((*spos)[0] & 0xc0) >> 6; + int len; + + if (val == 1 || val == 2) { + /* These are reserved values in RFC 1035 */ + wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " + "sequence starting with 0x%x", val); + return -1; + } + + if (val == 3) { + u16 offset; + u8 *spos_tmp; + + /* Offset */ + if (*spos + 2 > end) { + wpa_printf(MSG_DEBUG, "P2P: No room for full " + "DNS offset field"); + return -1; + } + + offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1]; + if (offset >= *spos - start) { + wpa_printf(MSG_DEBUG, "P2P: Invalid DNS " + "pointer offset %u", offset); + return -1; + } + + (*spos) += 2; + spos_tmp = start + offset; + return p2p_sd_dns_uncompress_label(upos, uend, start, + &spos_tmp, + *spos - 2); + } + + /* Label */ + len = (*spos)[0] & 0x3f; + if (len == 0) + return 0; + + (*spos)++; + if (*spos + len > end) { + wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " + "sequence - no room for label with length " + "%u", len); + return -1; + } + + if (*upos + len + 2 > uend) + return -2; + + os_memcpy(*upos, *spos, len); + *spos += len; + *upos += len; + (*upos)[0] = '.'; + (*upos)++; + (*upos)[0] = '\0'; + } + + return 0; +} + + +/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet. + * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is + * not large enough */ +static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg, + size_t msg_len, size_t offset) +{ + /* 27-octet in-memory packet from P2P specification */ + const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01" + "\x04_udp\xC0\x11\x00\x0C\x00\x01"; + u8 *tmp, *end, *spos; + char *upos, *uend; + int ret = 0; + + if (buf_len < 2) + return -1; + if (offset > msg_len) + return -1; + + tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len); + if (tmp == NULL) + return -1; + spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN; + end = spos + msg_len; + spos += offset; + + os_memset(tmp, 0, DNS_HEADER_LEN); + os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN); + os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len); + + upos = buf; + uend = buf + buf_len; + + ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end); + if (ret) { + os_free(tmp); + return ret; + } + + if (upos == buf) { + upos[0] = '.'; + upos[1] = '\0'; + } else if (upos[-1] == '.') + upos[-1] = '\0'; + + os_free(tmp); + return 0; +} + + +static struct p2p_srv_bonjour * +wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query) +{ + struct p2p_srv_bonjour *bsrv; + size_t len; + + len = wpabuf_len(query); + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (len == wpabuf_len(bsrv->query) && + os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query), + len) == 0) + return bsrv; + } + return NULL; +} + + +static struct p2p_srv_upnp * +wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (version == usrv->version && + os_strcmp(service, usrv->service) == 0) + return usrv; + } + return NULL; +} + + +static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id, u8 status) +{ + u8 *len_pos; + + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, srv_proto); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, status); + /* Response Data: empty */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, + P2P_SD_PROTO_NOT_AVAILABLE); +} + + +static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST); +} + + +static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, + P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); +} + + +static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + struct p2p_srv_bonjour *bsrv; + u8 *len_pos; + + wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services"); + + if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); + return; + } + + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (wpabuf_tailroom(resp) < + 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) + return; + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", + wpabuf_head(bsrv->resp), + wpabuf_len(bsrv->resp)); + /* Response Data */ + wpabuf_put_buf(resp, bsrv->query); /* Key */ + wpabuf_put_buf(resp, bsrv->resp); /* Value */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query, + size_t query_len) +{ + char str_rx[256], str_srv[256]; + + if (query_len < 3 || wpabuf_len(bsrv->query) < 3) + return 0; /* Too short to include DNS Type and Version */ + if (os_memcmp(query + query_len - 3, + wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3, + 3) != 0) + return 0; /* Mismatch in DNS Type or Version */ + if (query_len == wpabuf_len(bsrv->query) && + os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0) + return 1; /* Binary match */ + + if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3, + 0)) + return 0; /* Failed to uncompress query */ + if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv), + wpabuf_head(bsrv->query), + wpabuf_len(bsrv->query) - 3, 0)) + return 0; /* Failed to uncompress service */ + + return os_strcmp(str_rx, str_srv) == 0; +} + + +static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2p_srv_bonjour *bsrv; + u8 *len_pos; + int matches = 0; + + wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour", + query, query_len); + if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR, + srv_trans_id); + return; + } + + if (query_len == 0) { + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + return; + } + + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (!match_bonjour_query(bsrv, query, query_len)) + continue; + + if (wpabuf_tailroom(resp) < + 5 + query_len + wpabuf_len(bsrv->resp)) + return; + + matches++; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", + wpabuf_head(bsrv->resp), + wpabuf_len(bsrv->resp)); + + /* Response Data */ + wpabuf_put_data(resp, query, query_len); /* Key */ + wpabuf_put_buf(resp, bsrv->resp); /* Value */ + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); + } + + if (matches == 0) { + wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not " + "available"); + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); + /* Response Data: empty */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + struct p2p_srv_upnp *usrv; + u8 *len_pos; + + wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services"); + + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { + wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); + return; + } + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service)) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_UPNP); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + /* Response Data */ + wpabuf_put_u8(resp, usrv->version); + wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", + usrv->service); + wpabuf_put_str(resp, usrv->service); + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2p_srv_upnp *usrv; + u8 *len_pos; + u8 version; + char *str; + int count = 0; + + wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP", + query, query_len); + + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { + wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP, + srv_trans_id); + return; + } + + if (query_len == 0) { + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_UPNP); + wpabuf_put_u8(resp, srv_trans_id); + + version = query[0]; + str = os_malloc(query_len); + if (str == NULL) + return; + os_memcpy(str, query + 1, query_len - 1); + str[query_len - 1] = '\0'; + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (version != usrv->version) + continue; + + if (os_strcmp(str, "ssdp:all") != 0 && + os_strstr(usrv->service, str) == NULL) + continue; + + if (wpabuf_tailroom(resp) < 2) + break; + if (count == 0) { + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + /* Response Data */ + wpabuf_put_u8(resp, version); + } else + wpabuf_put_u8(resp, ','); + + count++; + + wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", + usrv->service); + if (wpabuf_tailroom(resp) < os_strlen(usrv->service)) + break; + wpabuf_put_str(resp, usrv->service); + } + os_free(str); + + if (count == 0) { + wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not " + "available"); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); + /* Response Data: empty */ + } + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +#ifdef CONFIG_WIFI_DISPLAY +static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + const u8 *pos; + u8 role; + u8 *len_pos; + + wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len); + + if (!wpa_s->global->wifi_display) { + wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY, + srv_trans_id); + return; + } + + if (query_len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device " + "Role"); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + + pos = query; + role = *pos++; + wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role); + + /* TODO: role specific handling */ + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY); + wpabuf_put_u8(resp, srv_trans_id); + wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */ + + while (pos < query + query_len) { + if (*pos < MAX_WFD_SUBELEMS && + wpa_s->global->wfd_subelem[*pos] && + wpabuf_tailroom(resp) >= + wpabuf_len(wpa_s->global->wfd_subelem[*pos])) { + wpa_printf(MSG_DEBUG, "P2P: Add WSD response " + "subelement %u", *pos); + wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]); + } + pos++; + } + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} +#endif /* CONFIG_WIFI_DISPLAY */ + + +static int find_p2ps_substr(struct p2ps_advertisement *adv_data, + const u8 *needle, size_t needle_len) +{ + const u8 *haystack = (const u8 *) adv_data->svc_info; + size_t haystack_len, i; + + /* Allow search term to be empty */ + if (!needle || !needle_len) + return 1; + + if (!haystack) + return 0; + + haystack_len = os_strlen(adv_data->svc_info); + for (i = 0; i < haystack_len; i++) { + if (haystack_len - i < needle_len) + break; + if (os_memcmp(haystack + i, needle, needle_len) == 0) + return 1; + } + + return 0; +} + + +static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2ps_advertisement *adv_data; + const u8 *svc = &query[1]; + const u8 *info = NULL; + size_t svc_len = query[0]; + size_t info_len = 0; + int prefix = 0; + u8 *count_pos = NULL; + u8 *len_pos = NULL; + + wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len); + + if (!wpa_s->global->p2p) { + wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id); + return; + } + + /* Info block is optional */ + if (svc_len + 1 < query_len) { + info = &svc[svc_len]; + info_len = *info++; + } + + /* Range check length of svc string and info block */ + if (svc_len + (info_len ? info_len + 2 : 1) > query_len) { + wpa_printf(MSG_DEBUG, "P2P: ASP bad request"); + wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id); + return; + } + + /* Detect and correct for prefix search */ + if (svc_len && svc[svc_len - 1] == '*') { + prefix = 1; + svc_len--; + } + + for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p); + adv_data; adv_data = adv_data->next) { + /* If not a prefix match, reject length mismatches */ + if (!prefix && svc_len != os_strlen(adv_data->svc_name)) + continue; + + /* Search each service for request */ + if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 && + find_p2ps_substr(adv_data, info, info_len)) { + size_t len = os_strlen(adv_data->svc_name); + size_t svc_info_len = 0; + + if (adv_data->svc_info) + svc_info_len = os_strlen(adv_data->svc_info); + + if (len > 0xff || svc_info_len > 0xffff) + return; + + /* Length & Count to be filled as we go */ + if (!len_pos && !count_pos) { + if (wpabuf_tailroom(resp) < + len + svc_info_len + 16) + return; + + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_P2PS); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + count_pos = wpabuf_put(resp, 1); + *count_pos = 0; + } else if (wpabuf_tailroom(resp) < + len + svc_info_len + 10) + return; + + if (svc_info_len) { + wpa_printf(MSG_DEBUG, + "P2P: Add Svc: %s info: %s", + adv_data->svc_name, + adv_data->svc_info); + } else { + wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s", + adv_data->svc_name); + } + + /* Advertisement ID */ + wpabuf_put_le32(resp, adv_data->id); + + /* Config Methods */ + wpabuf_put_be16(resp, adv_data->config_methods); + + /* Service Name */ + wpabuf_put_u8(resp, (u8) len); + wpabuf_put_data(resp, adv_data->svc_name, len); + + /* Service State */ + wpabuf_put_u8(resp, adv_data->state); + + /* Service Information */ + wpabuf_put_le16(resp, (u16) svc_info_len); + wpabuf_put_data(resp, adv_data->svc_info, svc_info_len); + + /* Update length and count */ + (*count_pos)++; + WPA_PUT_LE16(len_pos, + (u8 *) wpabuf_put(resp, 0) - len_pos - 2); + } + } + + /* Return error if no matching svc found */ + if (count_pos == NULL) { + wpa_printf(MSG_DEBUG, "P2P: ASP service not found"); + wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id); + } +} + + +static void wpas_sd_all_asp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + /* Query data to add all P2PS advertisements: + * - Service name length: 1 + * - Service name: '*' + * - Service Information Request Length: 0 + */ + const u8 q[] = { 1, (const u8) '*', 0 }; + + if (p2p_get_p2ps_adv_list(wpa_s->global->p2p)) + wpas_sd_req_asp(wpa_s, resp, srv_trans_id, q, sizeof(q)); +} + + +void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 *pos = tlvs; + const u8 *end = tlvs + tlvs_len; + const u8 *tlv_end; + u16 slen; + struct wpabuf *resp; + u8 srv_proto, srv_trans_id; + size_t buf_len; + char *buf; + + wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs", + tlvs, tlvs_len); + buf_len = 2 * tlvs_len + 1; + buf = os_malloc(buf_len); + if (buf) { + wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); + wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d " + MACSTR " %u %u %s", + freq, MAC2STR(sa), dialog_token, update_indic, + buf); + os_free(buf); + } + + if (wpa_s->p2p_sd_over_ctrl_iface) { + wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, + update_indic, tlvs, tlvs_len); + return; /* to be processed by an external program */ + } + + resp = wpabuf_alloc(10000); + if (resp == NULL) + return; + + while (pos + 1 < end) { + wpa_printf(MSG_DEBUG, "P2P: Service Request TLV"); + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 2) { + wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data " + "length"); + wpabuf_free(resp); + return; + } + tlv_end = pos + slen; + + srv_proto = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", + srv_proto); + srv_trans_id = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", + srv_trans_id); + + wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data", + pos, tlv_end - pos); + + + if (wpa_s->force_long_sd) { + wpa_printf(MSG_DEBUG, "P2P: SD test - force long " + "response"); + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + wpas_sd_all_asp(wpa_s, resp, srv_trans_id); + goto done; + } + + switch (srv_proto) { + case P2P_SERV_ALL_SERVICES: + wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request " + "for all services"); + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) && + dl_list_empty(&wpa_s->global->p2p_srv_bonjour) && + !p2p_get_p2ps_adv_list(wpa_s->global->p2p)) { + wpa_printf(MSG_DEBUG, "P2P: No service " + "discovery protocols available"); + wpas_sd_add_proto_not_avail( + resp, P2P_SERV_ALL_SERVICES, + srv_trans_id); + break; + } + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + wpas_sd_all_asp(wpa_s, resp, srv_trans_id); + break; + case P2P_SERV_BONJOUR: + wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; + case P2P_SERV_UPNP: + wpas_sd_req_upnp(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; +#ifdef CONFIG_WIFI_DISPLAY + case P2P_SERV_WIFI_DISPLAY: + wpas_sd_req_wfd(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; +#endif /* CONFIG_WIFI_DISPLAY */ + case P2P_SERV_P2PS: + wpas_sd_req_asp(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; + default: + wpa_printf(MSG_DEBUG, "P2P: Unavailable service " + "protocol %u", srv_proto); + wpas_sd_add_proto_not_avail(resp, srv_proto, + srv_trans_id); + break; + } + + pos = tlv_end; + } + +done: + wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, + update_indic, tlvs, tlvs_len); + + wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp); + + wpabuf_free(resp); +} + + +static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u8 srv_trans_id, + const u8 *pos, const u8 *tlv_end) +{ + u8 left = *pos++; + u32 adv_id; + u8 svc_status; + u16 config_methods; + char svc_str[256]; + + while (left-- && pos < tlv_end) { + char *buf = NULL; + size_t buf_len; + u8 svc_len; + + /* Sanity check fixed length+svc_str */ + if (pos + 6 >= tlv_end) + break; + svc_len = pos[6]; + if (pos + svc_len + 10 > tlv_end) + break; + + /* Advertisement ID */ + adv_id = WPA_GET_LE32(pos); + pos += sizeof(u32); + + /* Config Methods */ + config_methods = WPA_GET_BE16(pos); + pos += sizeof(u16); + + /* Service Name */ + pos++; /* svc_len */ + os_memcpy(svc_str, pos, svc_len); + svc_str[svc_len] = '\0'; + pos += svc_len; + + /* Service Status */ + svc_status = *pos++; + + /* Service Information Length */ + buf_len = WPA_GET_LE16(pos); + pos += sizeof(u16); + + /* Sanity check buffer length */ + if (buf_len > (unsigned int) (tlv_end - pos)) + break; + + if (buf_len) { + buf = os_zalloc(2 * buf_len + 1); + if (buf) { + utf8_escape((const char *) pos, buf_len, buf, + 2 * buf_len + 1); + } + } + + pos += buf_len; + + if (buf) { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP + MACSTR " %x %x %x %x %s '%s'", + MAC2STR(sa), srv_trans_id, adv_id, + svc_status, config_methods, svc_str, + buf); + os_free(buf); + } else { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP + MACSTR " %x %x %x %x %s", + MAC2STR(sa), srv_trans_id, adv_id, + svc_status, config_methods, svc_str); + } + } +} + + +void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 *pos = tlvs; + const u8 *end = tlvs + tlvs_len; + const u8 *tlv_end; + u16 slen; + size_t buf_len; + char *buf; + + wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs", + tlvs, tlvs_len); + if (tlvs_len > 1500) { + /* TODO: better way for handling this */ + wpa_msg_ctrl(wpa_s, MSG_INFO, + P2P_EVENT_SERV_DISC_RESP MACSTR + " %u ", + MAC2STR(sa), update_indic, + (unsigned int) tlvs_len); + } else { + buf_len = 2 * tlvs_len + 1; + buf = os_malloc(buf_len); + if (buf) { + wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); + wpa_msg_ctrl(wpa_s, MSG_INFO, + P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s", + MAC2STR(sa), update_indic, buf); + os_free(buf); + } + } + + while (pos < end) { + u8 srv_proto, srv_trans_id, status; + + wpa_printf(MSG_DEBUG, "P2P: Service Response TLV"); + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 3) { + wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data " + "length"); + return; + } + tlv_end = pos + slen; + + srv_proto = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", + srv_proto); + srv_trans_id = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", + srv_trans_id); + status = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u", + status); + + wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data", + pos, tlv_end - pos); + + if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) { + wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id, + pos, tlv_end); + } + + pos = tlv_end; + } + + wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len); +} + + +u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return 0; + return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs); +} + + +u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, + u8 version, const char *query) +{ + struct wpabuf *tlvs; + u64 ret; + + tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query)); + if (tlvs == NULL) + return 0; + wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query)); + wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */ + wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */ + wpabuf_put_u8(tlvs, version); + wpabuf_put_str(tlvs, query); + ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + return ret; +} + + +u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, + const char *svc_str, const char *info_substr) +{ + struct wpabuf *tlvs; + size_t plen, svc_len, substr_len = 0; + u64 ret; + + svc_len = os_strlen(svc_str); + if (info_substr) + substr_len = os_strlen(info_substr); + + if (svc_len > 0xff || substr_len > 0xff) + return 0; + + plen = 1 + 1 + 1 + svc_len + 1 + substr_len; + tlvs = wpabuf_alloc(2 + plen); + if (tlvs == NULL) + return 0; + + wpabuf_put_le16(tlvs, plen); + wpabuf_put_u8(tlvs, P2P_SERV_P2PS); + wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ + wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */ + wpabuf_put_data(tlvs, svc_str, svc_len); + wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */ + wpabuf_put_data(tlvs, info_substr, substr_len); + ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + + return ret; +} + + +#ifdef CONFIG_WIFI_DISPLAY + +static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return 0; + return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs); +} + + +#define MAX_WFD_SD_SUBELEMS 20 + +static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role, + const char *subelems) +{ + u8 *len; + const char *pos; + int val; + int count = 0; + + len = wpabuf_put(tlvs, 2); + wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */ + wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ + + wpabuf_put_u8(tlvs, role); + + pos = subelems; + while (*pos) { + val = atoi(pos); + if (val >= 0 && val < 256) { + wpabuf_put_u8(tlvs, val); + count++; + if (count == MAX_WFD_SD_SUBELEMS) + break; + } + pos = os_strchr(pos + 1, ','); + if (pos == NULL) + break; + pos++; + } + + WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2); +} + + +u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, + const u8 *dst, const char *role) +{ + struct wpabuf *tlvs; + u64 ret; + const char *subelems; + u8 id = 1; + + subelems = os_strchr(role, ' '); + if (subelems == NULL) + return 0; + subelems++; + + tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS)); + if (tlvs == NULL) + return 0; + + if (os_strstr(role, "[source]")) + wfd_add_sd_req_role(tlvs, id++, 0x00, subelems); + if (os_strstr(role, "[pri-sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x01, subelems); + if (os_strstr(role, "[sec-sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x02, subelems); + if (os_strstr(role, "[source+sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x03, subelems); + + ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + return ret; +} + +#endif /* CONFIG_WIFI_DISPLAY */ + + +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + return p2p_sd_cancel_request(wpa_s->global->p2p, + (void *) (uintptr_t) req); +} + + +void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, + const u8 *dst, u8 dialog_token, + const struct wpabuf *resp_tlvs) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token, + resp_tlvs); +} + + +void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->global->p2p) + p2p_sd_service_update(wpa_s->global->p2p); +} + + +static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv) +{ + dl_list_del(&bsrv->list); + wpabuf_free(bsrv->query); + wpabuf_free(bsrv->resp); + os_free(bsrv); +} + + +static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv) +{ + dl_list_del(&usrv->list); + os_free(usrv->service); + os_free(usrv); +} + + +void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s) +{ + struct p2p_srv_bonjour *bsrv, *bn; + struct p2p_srv_upnp *usrv, *un; + + dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) + wpas_p2p_srv_bonjour_free(bsrv); + + dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) + wpas_p2p_srv_upnp_free(usrv); + + wpas_p2p_service_flush_asp(wpa_s); + wpas_p2p_sd_service_update(wpa_s); +} + + +int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id) +{ + if (adv_id == 0) + return 1; + + if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id)) + return 1; + + return 0; +} + + +int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id) +{ + int ret; + + ret = p2p_service_del_asp(wpa_s->global->p2p, adv_id); + if (ret == 0) + wpas_p2p_sd_service_update(wpa_s); + return ret; +} + + +int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, + int auto_accept, u32 adv_id, + const char *adv_str, u8 svc_state, + u16 config_methods, const char *svc_info, + const u8 *cpt_priority) +{ + int ret; + + ret = p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id, + adv_str, svc_state, config_methods, + svc_info, cpt_priority); + if (ret == 0) + wpas_p2p_sd_service_update(wpa_s); + return ret; +} + + +void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s) +{ + p2p_service_flush_asp(wpa_s->global->p2p); +} + + +int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *query, struct wpabuf *resp) +{ + struct p2p_srv_bonjour *bsrv; + + bsrv = os_zalloc(sizeof(*bsrv)); + if (bsrv == NULL) + return -1; + bsrv->query = query; + bsrv->resp = resp; + dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list); + + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query) +{ + struct p2p_srv_bonjour *bsrv; + + bsrv = wpas_p2p_service_get_bonjour(wpa_s, query); + if (bsrv == NULL) + return -1; + wpas_p2p_srv_bonjour_free(bsrv); + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + if (wpas_p2p_service_get_upnp(wpa_s, version, service)) + return 0; /* Already listed */ + usrv = os_zalloc(sizeof(*usrv)); + if (usrv == NULL) + return -1; + usrv->version = version; + usrv->service = os_strdup(service); + if (usrv->service == NULL) { + os_free(usrv); + return -1; + } + dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list); + + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + usrv = wpas_p2p_service_get_upnp(wpa_s, version, service); + if (usrv == NULL) + return -1; + wpas_p2p_srv_upnp_free(usrv); + wpas_p2p_sd_service_update(wpa_s); + return 0; +} diff --git a/contrib/wpa/wpa_supplicant/preauth_test.c b/contrib/wpa/wpa_supplicant/preauth_test.c index ed5708585be..f4bba98e2a8 100644 --- a/contrib/wpa/wpa_supplicant/preauth_test.c +++ b/contrib/wpa/wpa_supplicant/preauth_test.c @@ -27,7 +27,7 @@ #include "drivers/driver.h" -struct wpa_driver_ops *wpa_drivers[] = { NULL }; +const struct wpa_driver_ops *const wpa_drivers[] = { NULL }; struct preauth_test_data { diff --git a/contrib/wpa/wpa_supplicant/scan.c b/contrib/wpa/wpa_supplicant/scan.c index 805891a8800..d7049a1a816 100644 --- a/contrib/wpa/wpa_supplicant/scan.c +++ b/contrib/wpa/wpa_supplicant/scan.c @@ -418,22 +418,6 @@ static void wpa_supplicant_optimize_freqs( static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, struct wpabuf *buf) { - if (wpa_s->conf->interworking == 0) - return; - - wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB); - wpabuf_put_u8(buf, 6); - wpabuf_put_u8(buf, 0x00); - wpabuf_put_u8(buf, 0x00); - wpabuf_put_u8(buf, 0x00); - wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */ - wpabuf_put_u8(buf, 0x00); -#ifdef CONFIG_HS20 - wpabuf_put_u8(buf, 0x40); /* Bit 46 - WNM-Notification */ -#else /* CONFIG_HS20 */ - wpabuf_put_u8(buf, 0x00); -#endif /* CONFIG_HS20 */ - wpabuf_put_u8(buf, WLAN_EID_INTERWORKING); wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 : 1 + ETH_ALEN); @@ -448,11 +432,19 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) { struct wpabuf *extra_ie = NULL; + u8 ext_capab[18]; + int ext_capab_len; #ifdef CONFIG_WPS int wps = 0; enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; #endif /* CONFIG_WPS */ + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, + sizeof(ext_capab)); + if (ext_capab_len > 0 && + wpabuf_resize(&extra_ie, ext_capab_len) == 0) + wpabuf_put_data(extra_ie, ext_capab, ext_capab_len); + #ifdef CONFIG_INTERWORKING if (wpa_s->conf->interworking && wpabuf_resize(&extra_ie, 100) == 0) @@ -493,6 +485,12 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) wpas_hs20_add_indication(extra_ie, -1); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + if (wpa_s->fst_ies && + wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0) + wpabuf_put_buf(extra_ie, wpa_s->fst_ies); +#endif /* CONFIG_FST */ + return extra_ie; } @@ -622,6 +620,37 @@ static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s, } +static int wpa_set_ssids_from_scan_req(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + size_t max_ssids) +{ + unsigned int i; + + if (wpa_s->ssids_from_scan_req == NULL || + wpa_s->num_ssids_from_scan_req == 0) + return 0; + + if (wpa_s->num_ssids_from_scan_req > max_ssids) { + wpa_s->num_ssids_from_scan_req = max_ssids; + wpa_printf(MSG_DEBUG, "Over max scan SSIDs from scan req: %u", + (unsigned int) max_ssids); + } + + for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) { + params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid; + params->ssids[i].ssid_len = + wpa_s->ssids_from_scan_req[i].ssid_len; + wpa_hexdump_ascii(MSG_DEBUG, "specific SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + } + + params->num_ssids = wpa_s->num_ssids_from_scan_req; + wpa_s->num_ssids_from_scan_req = 0; + return 1; +} + + static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -734,6 +763,12 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) goto scan; } + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_set_ssids_from_scan_req(wpa_s, ¶ms, max_ssids)) { + wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command"); + goto ssid_list_set; + } + #ifdef CONFIG_P2P if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) && wpa_s->go_params && !wpa_s->conf->passive_scan) { @@ -773,6 +808,9 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } if (wpa_s->last_scan_req != MANUAL_SCAN_REQ && +#ifdef CONFIG_AP + !wpa_s->ap_iface && +#endif /* CONFIG_AP */ wpa_s->conf->ap_scan == 2) { wpa_s->connect_without_scan = NULL; wpa_s->prev_scan_wildcard = 0; @@ -899,10 +937,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard " "SSID"); } -#ifdef CONFIG_P2P -ssid_list_set: -#endif /* CONFIG_P2P */ +ssid_list_set: wpa_supplicant_optimize_freqs(wpa_s, ¶ms); extra_ie = wpa_supplicant_extra_ies(wpa_s); @@ -1652,7 +1688,7 @@ static int wpa_scan_result_compar(const void *a, const void *b) snr_a_full = wa->snr; snr_a = MIN(wa->snr, GREAT_SNR); snr_b_full = wb->snr; - snr_b = MIN(wa->snr, GREAT_SNR); + snr_b = MIN(wb->snr, GREAT_SNR); } else { /* Level is not in dBm, so we can't calculate * SNR. Just use raw level (units unknown). */ diff --git a/contrib/wpa/wpa_supplicant/sme.c b/contrib/wpa/wpa_supplicant/sme.c index 17881137140..f2e5a43b978 100644 --- a/contrib/wpa/wpa_supplicant/sme.c +++ b/contrib/wpa/wpa_supplicant/sme.c @@ -67,7 +67,7 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s) for (;;) { int group = groups[wpa_s->sme.sae_group_index]; - if (group < 0) + if (group <= 0) break; if (sae_set_group(&wpa_s->sme.sae, group) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d", @@ -438,6 +438,21 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FST + if (wpa_s->fst_ies) { + int fst_ies_len = wpabuf_len(wpa_s->fst_ies); + + if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <= + sizeof(wpa_s->sme.assoc_req_ie)) { + os_memcpy(wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(wpa_s->fst_ies), + fst_ies_len); + wpa_s->sme.assoc_req_ie_len += fst_ies_len; + } + } +#endif /* CONFIG_FST */ + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); if (ext_capab_len > 0) { @@ -583,7 +598,8 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit) wpa_s->connect_work = work; if (cwork->bss_removed || - !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) { + !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid) || + wpas_network_disabled(wpa_s, cwork->ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt"); wpas_connect_work_done(wpa_s); return; @@ -698,6 +714,8 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return -1; if (auth_transaction == 1) { + u16 res; + groups = wpa_s->conf->sae_groups; wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit"); @@ -708,8 +726,14 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return -1; if (groups && groups[0] <= 0) groups = NULL; - if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL, - groups) != WLAN_STATUS_SUCCESS) + res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL, + groups); + if (res == SAE_SILENTLY_DISCARD) { + wpa_printf(MSG_DEBUG, + "SAE: Drop commit message due to reflection attack"); + return 0; + } + if (res != WLAN_STATUS_SUCCESS) return -1; if (sae_process_commit(&wpa_s->sme.sae) < 0) { @@ -793,8 +817,22 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) #endif /* CONFIG_SAE */ if (data->auth.status_code != WLAN_STATUS_SUCCESS) { - wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status " - "code %d)", data->auth.status_code); + char *ie_txt = NULL; + + if (data->auth.ies && data->auth.ies_len) { + size_t buflen = 2 * data->auth.ies_len + 1; + ie_txt = os_malloc(buflen); + if (ie_txt) { + wpa_snprintf_hex(ie_txt, buflen, data->auth.ies, + data->auth.ies_len); + } + } + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR + " auth_type=%u auth_transaction=%u status_code=%u ie=%s", + MAC2STR(data->auth.peer), data->auth.auth_type, + data->auth.auth_transaction, data->auth.status_code, + ie_txt); + os_free(ie_txt); if (data->auth.status_code != WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG || @@ -831,12 +869,20 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) #ifdef CONFIG_IEEE80211R if (data->auth.auth_type == WLAN_AUTH_FT) { - union wpa_event_data edata; - os_memset(&edata, 0, sizeof(edata)); - edata.ft_ies.ies = data->auth.ies; - edata.ft_ies.ies_len = data->auth.ies_len; - os_memcpy(edata.ft_ies.target_ap, data->auth.peer, ETH_ALEN); - wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &edata); + if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies, + data->auth.ies_len, 0, + data->auth.peer, NULL, 0) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: FT Authentication response processing failed"); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" + MACSTR + " reason=%d locally_generated=1", + MAC2STR(wpa_s->pending_bssid), + WLAN_REASON_DEAUTH_LEAVING); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_mark_disassoc(wpa_s); + return; + } } #endif /* CONFIG_IEEE80211R */ diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c index 5a0af0dc9bb..7ddae3d3b6b 100644 --- a/contrib/wpa/wpa_supplicant/wpa_cli.c +++ b/contrib/wpa/wpa_supplicant/wpa_cli.c @@ -26,16 +26,16 @@ #endif /* ANDROID */ -static const char *wpa_cli_version = +static const char *const wpa_cli_version = "wpa_cli v" VERSION_STR "\n" "Copyright (c) 2004-2015, Jouni Malinen and contributors"; -static const char *wpa_cli_license = +static const char *const wpa_cli_license = "This software may be distributed under the terms of the BSD license.\n" "See README for more details.\n"; -static const char *wpa_cli_full_license = +static const char *const wpa_cli_full_license = "This software may be distributed under the terms of the BSD license.\n" "\n" "Redistribution and use in source and binary forms, with or without\n" @@ -76,6 +76,7 @@ static int wpa_cli_last_id = 0; #define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant" #endif /* CONFIG_CTRL_IFACE_DIR */ static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; +static const char *client_socket_dir = NULL; static char *ctrl_ifname = NULL; static const char *pid_file = NULL; static const char *action_file = NULL; @@ -92,6 +93,7 @@ static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */ static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */ static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */ static DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */ +static DEFINE_DL_LIST(networks); /* struct cli_txt_entry */ static void print_help(const char *cmd); @@ -99,13 +101,16 @@ static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx); static void wpa_cli_close_connection(void); static char * wpa_cli_get_default_ifname(void); static char ** wpa_list_cmd_list(void); +static void update_networks(struct wpa_ctrl *ctrl); static void usage(void) { printf("wpa_cli [-p] [-i] [-hvB] " "[-a] \\\n" - " [-P] [-g] [-G] " + " [-P] [-g] [-G] " + "\\\n" + " [-s] " "[command..]\n" " -h = help (show this usage text)\n" " -v = shown version information\n" @@ -168,11 +173,12 @@ static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt) #ifdef CONFIG_P2P -static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt) +static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, + int separator) { const char *end; char *buf; - end = os_strchr(txt, ' '); + end = os_strchr(txt, separator); if (end == NULL) end = txt + os_strlen(txt); buf = dup_binstr(txt, end - txt); @@ -213,14 +219,16 @@ static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt) os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); return cli_txt_list_add(txt_list, buf); } +#endif /* CONFIG_P2P */ -static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt) +static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, + int separator) { const char *end; char *buf; int ret; - end = os_strchr(txt, ' '); + end = os_strchr(txt, separator); if (end == NULL) end = txt + os_strlen(txt); buf = dup_binstr(txt, end - txt); @@ -230,7 +238,6 @@ static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt) os_free(buf); return ret; } -#endif /* CONFIG_P2P */ static char ** cli_txt_list_array(struct dl_list *txt_list) @@ -326,6 +333,13 @@ static int wpa_cli_open_connection(const char *ifname, int attach) } #endif /* ANDROID */ + if (client_socket_dir && client_socket_dir[0] && + access(client_socket_dir, F_OK) < 0) { + perror(client_socket_dir); + os_free(cfile); + return -1; + } + if (cfile == NULL) { flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2; cfile = os_malloc(flen); @@ -339,14 +353,14 @@ static int wpa_cli_open_connection(const char *ifname, int attach) } } - ctrl_conn = wpa_ctrl_open(cfile); + ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir); if (ctrl_conn == NULL) { os_free(cfile); return -1; } if (attach && interactive) - mon_conn = wpa_ctrl_open(cfile); + mon_conn = wpa_ctrl_open2(cfile, client_socket_dir); else mon_conn = NULL; os_free(cfile); @@ -498,6 +512,10 @@ static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) return wpa_ctrl_command(ctrl, "STATUS-WPS"); if (argc > 0 && os_strcmp(argv[0], "driver") == 0) return wpa_ctrl_command(ctrl, "STATUS-DRIVER"); +#ifdef ANDROID + if (argc > 0 && os_strcmp(argv[0], "no_events") == 0) + return wpa_ctrl_command(ctrl, "STATUS-NO_EVENTS"); +#endif /* ANDROID */ return wpa_ctrl_command(ctrl, "STATUS"); } @@ -608,35 +626,58 @@ static char ** wpa_cli_complete_set(const char *str, int pos) "uapsd", "ps", "wifi_display", "bssid_filter", "disallow_aps", "no_keep_alive", /* global configuration parameters */ - "eapol_version", "ap_scan", "disable_scan_offload", - "fast_reauth", "opensc_engine_path", "pkcs11_engine_path", - "pkcs11_module_path", "openssl_ciphers", - "pcsc_reader", "pcsc_pin", - "driver_param", "dot11RSNAConfigPMKLifetime", +#ifdef CONFIG_CTRL_IFACE + "ctrl_interface", "no_ctrl_interface", "ctrl_interface_group", +#endif /* CONFIG_CTRL_IFACE */ + "eapol_version", "ap_scan", "bgscan", +#ifdef CONFIG_MESH + "user_mpm", "max_peer_links", "mesh_max_inactivity", + "dot11RSNASAERetransPeriod", +#endif /* CONFIG_MESH */ + "disable_scan_offload", "fast_reauth", "opensc_engine_path", + "pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers", + "pcsc_reader", "pcsc_pin", "external_sim", "driver_param", + "dot11RSNAConfigPMKLifetime", "dot11RSNAConfigPMKReauthThreshold", "dot11RSNAConfigSATimeout", - "update_config", "load_dynamic_eap", "uuid", "device_name", - "manufacturer", "model_name", "model_number", "serial_number", - "device_type", "os_version", "config_methods", - "wps_cred_processing", "wps_vendor_ext_m1", "sec_device_type", +#ifndef CONFIG_NO_CONFIG_WRITE + "update_config", +#endif /* CONFIG_NO_CONFIG_WRITE */ + "load_dynamic_eap", +#ifdef CONFIG_WPS + "uuid", "device_name", "manufacturer", "model_name", + "model_number", "serial_number", "device_type", "os_version", + "config_methods", "wps_cred_processing", "wps_vendor_ext_m1", +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + "sec_device_type", "p2p_listen_reg_class", "p2p_listen_channel", - "p2p_oper_reg_class", "p2p_oper_channel", - "p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect", - "p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan", - "p2p_no_go_freq", - "p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface", - "p2p_go_vht", - "p2p_ignore_shared_freq", "country", "bss_max_count", - "bss_expiration_age", "bss_expiration_scan_count", - "filter_ssids", "filter_rssi", "max_num_sta", - "disassoc_low_ack", "hs20", "interworking", "hessid", - "access_network_type", "pbc_in_m1", "autoscan", - "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey", "wps_nfc_dh_privkey", - "wps_nfc_dev_pw", "ext_password_backend", + "p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent", + "p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss", + "p2p_group_idle", "p2p_passphrase_len", "p2p_pref_chan", + "p2p_no_go_freq", "p2p_add_cli_chan", + "p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht", + "p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface", + "p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask", + "ip_addr_start", "ip_addr_end", +#endif /* CONFIG_P2P */ + "country", "bss_max_count", "bss_expiration_age", + "bss_expiration_scan_count", "filter_ssids", "filter_rssi", + "max_num_sta", "disassoc_low_ack", +#ifdef CONFIG_HS20 + "hs20", +#endif /* CONFIG_HS20 */ + "interworking", "hessid", "access_network_type", "pbc_in_m1", + "autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey", + "wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend", "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf", - "sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements", - "ignore_old_scan_res", "freq_list", "external_sim", - "tdls_external_control", "p2p_search_delay" + "sae_groups", "dtim_period", "beacon_int", + "ap_vendor_elements", "ignore_old_scan_res", "freq_list", + "scan_cur_freq", "sched_scan_interval", + "tdls_external_control", "osu_dir", "wowlan_triggers", + "p2p_search_delay", "mac_addr", "rand_addr_lifetime", + "preassoc_mac_addr", "key_mgmt_offload", "passive_scan", + "reassoc_same_bss_optim", "wps_priority" }; int i, num_fields = ARRAY_SIZE(fields); @@ -670,6 +711,74 @@ static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static char ** wpa_cli_complete_get(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { +#ifdef CONFIG_CTRL_IFACE + "ctrl_interface", "ctrl_interface_group", +#endif /* CONFIG_CTRL_IFACE */ + "eapol_version", "ap_scan", +#ifdef CONFIG_MESH + "user_mpm", "max_peer_links", "mesh_max_inactivity", +#endif /* CONFIG_MESH */ + "disable_scan_offload", "fast_reauth", "opensc_engine_path", + "pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers", + "pcsc_reader", "pcsc_pin", "external_sim", "driver_param", + "dot11RSNAConfigPMKLifetime", + "dot11RSNAConfigPMKReauthThreshold", + "dot11RSNAConfigSATimeout", +#ifndef CONFIG_NO_CONFIG_WRITE + "update_config", +#endif /* CONFIG_NO_CONFIG_WRITE */ +#ifdef CONFIG_WPS + "device_name", "manufacturer", "model_name", "model_number", + "serial_number", "config_methods", "wps_cred_processing", +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + "p2p_listen_reg_class", "p2p_listen_channel", + "p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent", + "p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss", + "p2p_group_idle", "p2p_passphrase_len", "p2p_add_cli_chan", + "p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht", + "p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface", + "p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask", + "ip_addr_start", "ip_addr_end", +#endif /* CONFIG_P2P */ + "bss_max_count", "bss_expiration_age", + "bss_expiration_scan_count", "filter_ssids", "filter_rssi", + "max_num_sta", "disassoc_low_ack", +#ifdef CONFIG_HS20 + "hs20", +#endif /* CONFIG_HS20 */ + "interworking", "access_network_type", "pbc_in_m1", "autoscan", + "wps_nfc_dev_pw_id", "ext_password_backend", + "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf", + "dtim_period", "beacon_int", "ignore_old_scan_res", + "scan_cur_freq", "sched_scan_interval", + "tdls_external_control", "osu_dir", "wowlan_triggers", + "p2p_search_delay", "mac_addr", "rand_addr_lifetime", + "preassoc_mac_addr", "key_mgmt_offload", "passive_scan", + "reassoc_same_bss_optim" + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (res[i] == NULL) + return res; + } + return res; + } + + return NULL; +} + + static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "LOGOFF"); @@ -873,12 +982,12 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s", argv[0], argv[1]); else if (argc == 5 || argc == 6) { - char ssid_hex[2 * 32 + 1]; + char ssid_hex[2 * SSID_MAX_LEN + 1]; char key_hex[2 * 64 + 1]; int i; ssid_hex[0] = '\0'; - for (i = 0; i < 32; i++) { + for (i = 0; i < SSID_MAX_LEN; i++) { if (argv[2][i] == '\0') break; os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]); @@ -1002,12 +1111,12 @@ static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc, int res; if (argc == 5 || argc == 6) { - char ssid_hex[2 * 32 + 1]; + char ssid_hex[2 * SSID_MAX_LEN + 1]; char key_hex[2 * 64 + 1]; int i; ssid_hex[0] = '\0'; - for (i = 0; i < 32; i++) { + for (i = 0; i < SSID_MAX_LEN; i++) { if (argv[2][i] == '\0') break; os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]); @@ -1361,14 +1470,20 @@ static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return wpa_ctrl_command(ctrl, "ADD_NETWORK"); + int res = wpa_ctrl_command(ctrl, "ADD_NETWORK"); + if (interactive) + update_networks(ctrl); + return res; } static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv); + int res = wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv); + if (interactive) + update_networks(ctrl); + return res; } @@ -1429,6 +1544,105 @@ static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, } +static const char *network_fields[] = { + "ssid", "scan_ssid", "bssid", "bssid_blacklist", + "bssid_whitelist", "psk", "proto", "key_mgmt", + "bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq", + "freq_list", +#ifdef IEEE8021X_EAPOL + "eap", "identity", "anonymous_identity", "password", "ca_cert", + "ca_path", "client_cert", "private_key", "private_key_passwd", + "dh_file", "subject_match", "altsubject_match", + "domain_suffix_match", "domain_match", "ca_cert2", "ca_path2", + "client_cert2", "private_key2", "private_key2_passwd", + "dh_file2", "subject_match2", "altsubject_match2", + "domain_suffix_match2", "domain_match2", "phase1", "phase2", + "pcsc", "pin", "engine_id", "key_id", "cert_id", "ca_cert_id", + "pin2", "engine2_id", "key2_id", "cert2_id", "ca_cert2_id", + "engine", "engine2", "eapol_flags", "sim_num", + "openssl_ciphers", "erp", +#endif /* IEEE8021X_EAPOL */ + "wep_key0", "wep_key1", "wep_key2", "wep_key3", + "wep_tx_keyidx", "priority", +#ifdef IEEE8021X_EAPOL + "eap_workaround", "pac_file", "fragment_size", "ocsp", +#endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_MESH + "mode", "no_auto_peer", +#else /* CONFIG_MESH */ + "mode", +#endif /* CONFIG_MESH */ + "proactive_key_caching", "disabled", "id_str", +#ifdef CONFIG_IEEE80211W + "ieee80211w", +#endif /* CONFIG_IEEE80211W */ + "peerkey", "mixed_cell", "frequency", "fixed_freq", +#ifdef CONFIG_MESH + "mesh_basic_rates", "dot11MeshMaxRetries", + "dot11MeshRetryTimeout", "dot11MeshConfirmTimeout", + "dot11MeshHoldingTimeout", +#endif /* CONFIG_MESH */ + "wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid", +#ifdef CONFIG_P2P + "go_p2p_dev_addr", "p2p_client_list", "psk_list", +#endif /* CONFIG_P2P */ +#ifdef CONFIG_HT_OVERRIDES + "disable_ht", "disable_ht40", "disable_sgi", "disable_ldpc", + "ht40_intolerant", "disable_max_amsdu", "ampdu_factor", + "ampdu_density", "ht_mcs", +#endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + "disable_vht", "vht_capa", "vht_capa_mask", "vht_rx_mcs_nss_1", + "vht_rx_mcs_nss_2", "vht_rx_mcs_nss_3", "vht_rx_mcs_nss_4", + "vht_rx_mcs_nss_5", "vht_rx_mcs_nss_6", "vht_rx_mcs_nss_7", + "vht_rx_mcs_nss_8", "vht_tx_mcs_nss_1", "vht_tx_mcs_nss_2", + "vht_tx_mcs_nss_3", "vht_tx_mcs_nss_4", "vht_tx_mcs_nss_5", + "vht_tx_mcs_nss_6", "vht_tx_mcs_nss_7", "vht_tx_mcs_nss_8", +#endif /* CONFIG_VHT_OVERRIDES */ + "ap_max_inactivity", "dtim_period", "beacon_int", +#ifdef CONFIG_MACSEC + "macsec_policy", +#endif /* CONFIG_MACSEC */ +#ifdef CONFIG_HS20 + "update_identifier", +#endif /* CONFIG_HS20 */ + "mac_addr" +}; + + +static char ** wpa_cli_complete_network(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + int i, num_fields = ARRAY_SIZE(network_fields); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&networks); + break; + case 2: + res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(network_fields[i]); + if (res[i] == NULL) + break; + } + } + return res; +} + + +static char ** wpa_cli_complete_network_id(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + if (arg == 1) + return cli_txt_list_array(&networks); + return NULL; +} + + static int wpa_cli_cmd_dup_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1447,6 +1661,31 @@ static int wpa_cli_cmd_dup_network(struct wpa_ctrl *ctrl, int argc, } +static char ** wpa_cli_complete_dup_network(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + int i, num_fields = ARRAY_SIZE(network_fields); + char **res = NULL; + + switch (arg) { + case 1: + case 2: + res = cli_txt_list_array(&networks); + break; + case 3: + res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(network_fields[i]); + if (res[i] == NULL) + break; + } + } + return res; +} + + static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1621,20 +1860,20 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, printf("Invalid INTERFACE_ADD command: needs at least one " "argument (interface name)\n" "All arguments: ifname confname driver ctrl_interface " - "driver_param bridge_name\n"); + "driver_param bridge_name [create]\n"); return -1; } /* * INTERFACE_ADD TABTABTABTAB - * TAB + * TAB[TAB] */ res = os_snprintf(cmd, sizeof(cmd), - "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s", + "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s", argv[0], argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "", argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "", - argc > 5 ? argv[5] : ""); + argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : ""); if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; @@ -2431,6 +2670,13 @@ static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_tdls_link_status(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_LINK_STATUS", 1, argc, argv); +} + + static int wpa_cli_cmd_wmm_ac_addts(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2570,6 +2816,13 @@ static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -2583,7 +2836,7 @@ struct wpa_cli_cmd { const char *usage; }; -static struct wpa_cli_cmd wpa_cli_commands[] = { +static const struct wpa_cli_cmd wpa_cli_commands[] = { { "status", wpa_cli_cmd_status, NULL, cli_cmd_flag_none, "[verbose] = get current WPA/EAPOL/EAP status" }, @@ -2624,7 +2877,7 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "dump", wpa_cli_cmd_dump, NULL, cli_cmd_flag_none, "= dump config variables" }, - { "get", wpa_cli_cmd_get, NULL, + { "get", wpa_cli_cmd_get, wpa_cli_complete_get, cli_cmd_flag_none, " = get information" }, { "logon", wpa_cli_cmd_logon, NULL, @@ -2686,29 +2939,33 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "list_networks", wpa_cli_cmd_list_networks, NULL, cli_cmd_flag_none, "= list configured networks" }, - { "select_network", wpa_cli_cmd_select_network, NULL, + { "select_network", wpa_cli_cmd_select_network, + wpa_cli_complete_network_id, cli_cmd_flag_none, " = select a network (disable others)" }, - { "enable_network", wpa_cli_cmd_enable_network, NULL, + { "enable_network", wpa_cli_cmd_enable_network, + wpa_cli_complete_network_id, cli_cmd_flag_none, " = enable a network" }, - { "disable_network", wpa_cli_cmd_disable_network, NULL, + { "disable_network", wpa_cli_cmd_disable_network, + wpa_cli_complete_network_id, cli_cmd_flag_none, " = disable a network" }, { "add_network", wpa_cli_cmd_add_network, NULL, cli_cmd_flag_none, "= add a network" }, - { "remove_network", wpa_cli_cmd_remove_network, NULL, + { "remove_network", wpa_cli_cmd_remove_network, + wpa_cli_complete_network_id, cli_cmd_flag_none, " = remove a network" }, - { "set_network", wpa_cli_cmd_set_network, NULL, + { "set_network", wpa_cli_cmd_set_network, wpa_cli_complete_network, cli_cmd_flag_sensitive, " = set network variables (shows\n" " list of variables when run without arguments)" }, - { "get_network", wpa_cli_cmd_get_network, NULL, + { "get_network", wpa_cli_cmd_get_network, wpa_cli_complete_network, cli_cmd_flag_none, " = get network variables" }, - { "dup_network", wpa_cli_cmd_dup_network, NULL, + { "dup_network", wpa_cli_cmd_dup_network, wpa_cli_complete_dup_network, cli_cmd_flag_none, " = duplicate network variables" }, @@ -2750,7 +3007,7 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "get_capability", wpa_cli_cmd_get_capability, NULL, cli_cmd_flag_none, " " - "= get capabilies" }, + "= get capabilities" }, { "reconfigure", wpa_cli_cmd_reconfigure, NULL, cli_cmd_flag_none, "= force wpa_supplicant to re-read its configuration file" }, @@ -3054,6 +3311,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL, cli_cmd_flag_none, " = tear down TDLS with " }, + { "tdls_link_status", wpa_cli_cmd_tdls_link_status, NULL, + cli_cmd_flag_none, + " = TDLS link status with " }, { "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL, cli_cmd_flag_none, " [nominal_msdu_size=#] " @@ -3117,6 +3377,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { " enable=<0/1> [addr=mac-address " "mask=mac-address-mask] = scan MAC randomization" }, + { "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL, + cli_cmd_flag_none, + " = retrieve preferred freq list for the specified interface type" }, { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -3124,7 +3387,7 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { /* * Prints command usage, lines are padded with the specified string. */ -static void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad) +static void print_cmd_help(const struct wpa_cli_cmd *cmd, const char *pad) { char c; size_t n; @@ -3262,7 +3525,7 @@ static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos) static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - struct wpa_cli_cmd *cmd, *match = NULL; + const struct wpa_cli_cmd *cmd, *match = NULL; int count; int ret = 0; @@ -3347,6 +3610,9 @@ static void wpa_cli_action_process(const char *msg) const char *ifname = ctrl_ifname; char ifname_buf[100]; + if (eloop_terminated()) + return; + pos = msg; if (os_strncmp(pos, "IFNAME=", 7) == 0) { const char *end; @@ -3524,7 +3790,7 @@ static void cli_event(const char *str) s = os_strchr(start, ' '); if (s == NULL) return; - cli_txt_list_add_word(&p2p_groups, s + 1); + cli_txt_list_add_word(&p2p_groups, s + 1, ' '); return; } @@ -3532,7 +3798,7 @@ static void cli_event(const char *str) s = os_strchr(start, ' '); if (s == NULL) return; - cli_txt_list_del_word(&p2p_groups, s + 1); + cli_txt_list_del_word(&p2p_groups, s + 1, ' '); return; } #endif /* CONFIG_P2P */ @@ -3691,7 +3957,11 @@ static void start_edit(void) ps = wpa_ctrl_get_remote_ifname(ctrl_conn); #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +#ifdef CONFIG_WPA_CLI_HISTORY_DIR + home = CONFIG_WPA_CLI_HISTORY_DIR; +#else /* CONFIG_WPA_CLI_HISTORY_DIR */ home = getenv("HOME"); +#endif /* CONFIG_WPA_CLI_HISTORY_DIR */ if (home) { const char *fname = ".wpa_cli_history"; int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; @@ -3774,6 +4044,38 @@ static void update_ifnames(struct wpa_ctrl *ctrl) } +static void update_networks(struct wpa_ctrl *ctrl) +{ + char buf[4096]; + size_t len = sizeof(buf); + int ret; + char *cmd = "LIST_NETWORKS"; + char *pos, *end; + int header = 1; + + cli_txt_list_flush(&networks); + + if (ctrl == NULL) + return; + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL); + if (ret < 0) + return; + buf[len] = '\0'; + + pos = buf; + while (pos) { + end = os_strchr(pos, '\n'); + if (end == NULL) + break; + *end = '\0'; + if (!header) + cli_txt_list_add_word(&networks, pos, '\t'); + header = 0; + pos = end + 1; + } +} + + static void try_connection(void *eloop_ctx, void *timeout_ctx) { if (ctrl_conn) @@ -3794,6 +4096,7 @@ static void try_connection(void *eloop_ctx, void *timeout_ctx) } update_bssid_list(ctrl_conn); + update_networks(ctrl_conn); if (warning_displayed) printf("Connection established.\n"); @@ -3815,6 +4118,7 @@ static void wpa_cli_interactive(void) cli_txt_list_flush(&p2p_groups); cli_txt_list_flush(&bsses); cli_txt_list_flush(&ifnames); + cli_txt_list_flush(&networks); if (edit_started) edit_deinit(hfile, wpa_cli_edit_filter_history_cb); os_free(hfile); @@ -3823,45 +4127,49 @@ static void wpa_cli_interactive(void) } +static void wpa_cli_action_ping(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_ctrl *ctrl = eloop_ctx; + char buf[256]; + size_t len; + + /* verify that connection is still working */ + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len, + wpa_cli_action_cb) < 0 || + len < 4 || os_memcmp(buf, "PONG", 4) != 0) { + printf("wpa_supplicant did not reply to PING command - exiting\n"); + eloop_terminate(); + return; + } + eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping, + ctrl, NULL); +} + + +static void wpa_cli_action_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_ctrl *ctrl = eloop_ctx; + + wpa_cli_recv_pending(ctrl, 1); +} + + static void wpa_cli_action(struct wpa_ctrl *ctrl) { #ifdef CONFIG_ANSI_C_EXTRA /* TODO: ANSI C version(?) */ printf("Action processing not supported in ANSI C build.\n"); #else /* CONFIG_ANSI_C_EXTRA */ - fd_set rfds; - int fd, res; - struct timeval tv; - char buf[256]; /* note: large enough to fit in unsolicited messages */ - size_t len; + int fd; fd = wpa_ctrl_get_fd(ctrl); - - while (!wpa_cli_quit) { - FD_ZERO(&rfds); - FD_SET(fd, &rfds); - tv.tv_sec = ping_interval; - tv.tv_usec = 0; - res = select(fd + 1, &rfds, NULL, NULL, &tv); - if (res < 0 && errno != EINTR) { - perror("select"); - break; - } - - if (FD_ISSET(fd, &rfds)) - wpa_cli_recv_pending(ctrl, 1); - else { - /* verify that connection is still working */ - len = sizeof(buf) - 1; - if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len, - wpa_cli_action_cb) < 0 || - len < 4 || os_memcmp(buf, "PONG", 4) != 0) { - printf("wpa_supplicant did not reply to PING " - "command - exiting\n"); - break; - } - } - } + eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping, + ctrl, NULL); + eloop_register_read_sock(fd, wpa_cli_action_receive, ctrl, NULL); + eloop_run(); + eloop_cancel_timeout(wpa_cli_action_ping, ctrl, NULL); + eloop_unregister_read_sock(fd); #endif /* CONFIG_ANSI_C_EXTRA */ } @@ -3886,18 +4194,17 @@ static char * wpa_cli_get_default_ifname(void) { char *ifname = NULL; +#ifdef ANDROID + char ifprop[PROPERTY_VALUE_MAX]; + if (property_get("wifi.interface", ifprop, NULL) != 0) { + ifname = os_strdup(ifprop); + printf("Using interface '%s'\n", ifname ? ifname : "N/A"); + } +#else /* ANDROID */ #ifdef CONFIG_CTRL_IFACE_UNIX struct dirent *dent; DIR *dir = opendir(ctrl_iface_dir); if (!dir) { -#ifdef ANDROID - char ifprop[PROPERTY_VALUE_MAX]; - if (property_get("wifi.interface", ifprop, NULL) != 0) { - ifname = os_strdup(ifprop); - printf("Using interface '%s'\n", ifname); - return ifname; - } -#endif /* ANDROID */ return NULL; } while ((dent = readdir(dir))) { @@ -3941,6 +4248,7 @@ static char * wpa_cli_get_default_ifname(void) } wpa_ctrl_close(ctrl); #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +#endif /* ANDROID */ return ifname; } @@ -3957,7 +4265,7 @@ int main(int argc, char *argv[]) return -1; for (;;) { - c = getopt(argc, argv, "a:Bg:G:hi:p:P:v"); + c = getopt(argc, argv, "a:Bg:G:hi:p:P:s:v"); if (c < 0) break; switch (c) { @@ -3989,6 +4297,9 @@ int main(int argc, char *argv[]) case 'P': pid_file = optarg; break; + case 's': + client_socket_dir = optarg; + break; default: usage(); return -1; diff --git a/contrib/wpa/wpa_supplicant/wpa_priv.c b/contrib/wpa/wpa_supplicant/wpa_priv.c index ac38d69d5d1..850ec405b42 100644 --- a/contrib/wpa/wpa_supplicant/wpa_priv.c +++ b/contrib/wpa/wpa_supplicant/wpa_priv.c @@ -29,8 +29,9 @@ struct wpa_priv_interface { char *sock_name; int fd; - struct wpa_driver_ops *driver; + const struct wpa_driver_ops *driver; void *drv_priv; + void *drv_global_priv; struct sockaddr_un drv_addr; int wpas_registered; @@ -48,6 +49,10 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, if (iface->driver->deinit) iface->driver->deinit(iface->drv_priv); iface->drv_priv = NULL; + if (iface->drv_global_priv) { + iface->driver->global_deinit(iface->drv_global_priv); + iface->drv_global_priv = NULL; + } iface->wpas_registered = 0; } @@ -58,10 +63,24 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, iface->l2 = NULL; } - if (iface->driver->init == NULL) + if (iface->driver->init2) { + if (iface->driver->global_init) { + iface->drv_global_priv = iface->driver->global_init(); + if (!iface->drv_global_priv) { + wpa_printf(MSG_INFO, + "Failed to initialize driver global context"); + return; + } + } else { + iface->drv_global_priv = NULL; + } + iface->drv_priv = iface->driver->init2(iface, iface->ifname, + iface->drv_global_priv); + } else if (iface->driver->init) { + iface->drv_priv = iface->driver->init(iface, iface->ifname); + } else { return; - - iface->drv_priv = iface->driver->init(iface, iface->ifname); + } if (iface->drv_priv == NULL) { wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper"); return; @@ -87,6 +106,10 @@ static void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface, if (iface->driver->deinit) iface->driver->deinit(iface->drv_priv); iface->drv_priv = NULL; + if (iface->drv_global_priv) { + iface->driver->global_deinit(iface->drv_global_priv); + iface->drv_global_priv = NULL; + } iface->wpas_registered = 0; } } @@ -172,6 +195,58 @@ static void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface, } +static void wpa_priv_cmd_authenticate(struct wpa_priv_interface *iface, + void *buf, size_t len) +{ + struct wpa_driver_auth_params params; + struct privsep_cmd_authenticate *auth; + int res, i; + + if (iface->drv_priv == NULL || iface->driver->authenticate == NULL) + return; + + if (len < sizeof(*auth)) { + wpa_printf(MSG_DEBUG, "Invalid authentication request"); + return; + } + + auth = buf; + if (sizeof(*auth) + auth->ie_len + auth->sae_data_len > len) { + wpa_printf(MSG_DEBUG, "Authentication request overflow"); + return; + } + + os_memset(¶ms, 0, sizeof(params)); + params.freq = auth->freq; + params.bssid = auth->bssid; + params.ssid = auth->ssid; + if (auth->ssid_len > SSID_MAX_LEN) + return; + params.ssid_len = auth->ssid_len; + params.auth_alg = auth->auth_alg; + for (i = 0; i < 4; i++) { + if (auth->wep_key_len[i]) { + params.wep_key[i] = auth->wep_key[i]; + params.wep_key_len[i] = auth->wep_key_len[i]; + } + } + params.wep_tx_keyidx = auth->wep_tx_keyidx; + params.local_state_change = auth->local_state_change; + params.p2p = auth->p2p; + if (auth->ie_len) { + params.ie = (u8 *) (auth + 1); + params.ie_len = auth->ie_len; + } + if (auth->sae_data_len) { + params.sae_data = ((u8 *) (auth + 1)) + auth->ie_len; + params.sae_data_len = auth->sae_data_len; + } + + res = iface->driver->authenticate(iface->drv_priv, ¶ms); + wpa_printf(MSG_DEBUG, "drv->authenticate: res=%d", res); +} + + static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface, void *buf, size_t len) { @@ -199,7 +274,7 @@ static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface, if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5]) params.bssid = bssid; params.ssid = assoc->ssid; - if (assoc->ssid_len > 32) + if (assoc->ssid_len > SSID_MAX_LEN) return; params.ssid_len = assoc->ssid_len; params.freq.mode = assoc->hwmode; @@ -244,7 +319,7 @@ fail: static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface, struct sockaddr_un *from) { - u8 ssid[sizeof(int) + 32]; + u8 ssid[sizeof(int) + SSID_MAX_LEN]; int res; if (iface->drv_priv == NULL) @@ -254,7 +329,7 @@ static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface, goto fail; res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]); - if (res < 0 || res > 32) + if (res < 0 || res > SSID_MAX_LEN) goto fail; os_memcpy(ssid, &res, sizeof(int)); @@ -307,6 +382,10 @@ static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface, iface->driver->get_capa(iface->drv_priv, &capa) < 0) goto fail; + /* For now, no support for passing extended_capa pointers */ + capa.extended_capa = NULL; + capa.extended_capa_mask = NULL; + capa.extended_capa_len = 0; sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from, sizeof(*from)); return; @@ -356,7 +435,8 @@ static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface, } proto = reg_cmd[0]; - if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) { + if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH && + proto != ETH_P_80211_ENCAP) { wpa_printf(MSG_DEBUG, "Refused l2_packet connection for " "ethertype 0x%x", proto); return; @@ -529,6 +609,9 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx) pos[cmd_len] = '\0'; wpa_priv_cmd_set_country(iface, pos); break; + case PRIVSEP_CMD_AUTHENTICATE: + wpa_priv_cmd_authenticate(iface, cmd_buf, cmd_len); + break; } } @@ -698,6 +781,36 @@ static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event, } +static void wpa_priv_send_auth(struct wpa_priv_interface *iface, + union wpa_event_data *data) +{ + size_t buflen = sizeof(struct privsep_event_auth) + data->auth.ies_len; + struct privsep_event_auth *auth; + u8 *buf, *pos; + + buf = os_malloc(buflen); + if (buf == NULL) + return; + + auth = (struct privsep_event_auth *) buf; + pos = (u8 *) (auth + 1); + + os_memcpy(auth->peer, data->auth.peer, ETH_ALEN); + os_memcpy(auth->bssid, data->auth.bssid, ETH_ALEN); + auth->auth_type = data->auth.auth_type; + auth->auth_transaction = data->auth.auth_transaction; + auth->status_code = data->auth.status_code; + if (data->auth.ies) { + os_memcpy(pos, data->auth.ies, data->auth.ies_len); + auth->ies_len = data->auth.ies_len; + } + + wpa_priv_send_event(iface, PRIVSEP_EVENT_AUTH, buf, buflen); + + os_free(buf); +} + + static void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event, union wpa_event_data *data) { @@ -851,6 +964,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, &data->michael_mic_failure.unicast, sizeof(int)); break; + case EVENT_SCAN_STARTED: + wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_STARTED, NULL, + 0); + break; case EVENT_SCAN_RESULTS: wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL, 0); @@ -874,9 +991,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_FT_RESPONSE: wpa_priv_send_ft_response(iface, data); break; + case EVENT_AUTH: + wpa_priv_send_auth(iface, data); + break; default: - wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO", - event); + wpa_printf(MSG_DEBUG, "Unsupported driver event %d (%s) - TODO", + event, event_to_string(event)); break; } } @@ -944,8 +1064,9 @@ static void usage(void) "contributors\n" "\n" "usage:\n" - " wpa_priv [-Bdd] [-P] " - "[driver:ifname ...]\n"); + " wpa_priv [-Bdd] [-c] [-P] " + " \\\n" + " [driver:ifname ...]\n"); } @@ -982,20 +1103,20 @@ int main(int argc, char *argv[]) break; default: usage(); - goto out; + goto out2; } } if (optind >= argc) { usage(); - goto out; + goto out2; } wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir); if (eloop_init()) { wpa_printf(MSG_ERROR, "Failed to initialize event loop"); - goto out; + goto out2; } for (i = optind; i < argc; i++) { @@ -1025,7 +1146,9 @@ out: eloop_destroy(); - os_daemonize_terminate(pid_file); +out2: + if (daemonize) + os_daemonize_terminate(pid_file); os_free(pid_file); os_program_deinit(); diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c index 19fb8900bd7..ef55fdcf79c 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c @@ -35,6 +35,7 @@ #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" #include "p2p/p2p.h" +#include "fst/fst.h" #include "blacklist.h" #include "wpas_glue.h" #include "wps_supplicant.h" @@ -55,11 +56,11 @@ #include "wpas_kay.h" #include "mesh.h" -const char *wpa_supplicant_version = +const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" "Copyright (c) 2003-2015, Jouni Malinen and contributors"; -const char *wpa_supplicant_license = +const char *const wpa_supplicant_license = "This software may be distributed under the terms of the BSD license.\n" "See README for more details.\n" #ifdef EAP_TLS_OPENSSL @@ -70,16 +71,16 @@ const char *wpa_supplicant_license = #ifndef CONFIG_NO_STDOUT_DEBUG /* Long text divided into parts in order to fit in C89 strings size limits. */ -const char *wpa_supplicant_full_license1 = +const char *const wpa_supplicant_full_license1 = ""; -const char *wpa_supplicant_full_license2 = +const char *const wpa_supplicant_full_license2 = "This software may be distributed under the terms of the BSD license.\n" "\n" "Redistribution and use in source and binary forms, with or without\n" "modification, are permitted provided that the following conditions are\n" "met:\n" "\n"; -const char *wpa_supplicant_full_license3 = +const char *const wpa_supplicant_full_license3 = "1. Redistributions of source code must retain the above copyright\n" " notice, this list of conditions and the following disclaimer.\n" "\n" @@ -87,7 +88,7 @@ const char *wpa_supplicant_full_license3 = " notice, this list of conditions and the following disclaimer in the\n" " documentation and/or other materials provided with the distribution.\n" "\n"; -const char *wpa_supplicant_full_license4 = +const char *const wpa_supplicant_full_license4 = "3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" " names of its contributors may be used to endorse or promote products\n" " derived from this software without specific prior written permission.\n" @@ -96,7 +97,7 @@ const char *wpa_supplicant_full_license4 = "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"; -const char *wpa_supplicant_full_license5 = +const char *const wpa_supplicant_full_license5 = "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" @@ -456,6 +457,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s, NULL); #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + wpas_wps_deinit(wpa_s); wpabuf_free(wpa_s->pending_eapol_rx); @@ -491,6 +494,16 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL); + /* + * Need to remove any pending gas-query radio work before the + * gas_query_deinit() call because gas_query::work has not yet been set + * for works that have not been started. gas_query_free() will be unable + * to cancel such pending radio works and once the pending gas-query + * radio work eventually gets removed, the deinit notification call to + * gas_query_start_cb() would result in dereferencing freed memory. + */ + if (wpa_s->radio) + radio_remove_works(wpa_s, "gas-query", 0); gas_query_deinit(wpa_s->gas); wpa_s->gas = NULL; @@ -716,6 +729,30 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_s->normal_scans = 0; } +#ifdef CONFIG_P2P + /* + * P2PS client has to reply to Probe Request frames received on the + * group operating channel. Enable Probe Request frame reporting for + * P2P connected client in case p2p_cli_probe configuration property is + * set to 1. + */ + if (wpa_s->conf->p2p_cli_probe && wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_INFRA && + wpa_s->current_ssid->p2p_group) { + if (state == WPA_COMPLETED && !wpa_s->p2p_cli_probe) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Enable CLI Probe Request RX reporting"); + wpa_s->p2p_cli_probe = + wpa_drv_probe_req_report(wpa_s, 1) >= 0; + } else if (state != WPA_COMPLETED && wpa_s->p2p_cli_probe) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Disable CLI Probe Request RX reporting"); + wpa_s->p2p_cli_probe = 0; + wpa_drv_probe_req_report(wpa_s, 0); + } + } +#endif /* CONFIG_P2P */ + if (state != WPA_SCANNING) wpa_supplicant_notify_scanning(wpa_s, 0); @@ -729,6 +766,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, ssid && ssid->id_str ? ssid->id_str : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ wpas_clear_temp_disabled(wpa_s, ssid, 1); + wpa_blacklist_clear(wpa_s); wpa_s->extra_blacklist_count = 0; wpa_s->new_connection = 0; wpa_drv_set_operstate(wpa_s, 1); @@ -870,7 +908,8 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) eapol_sm_invalidate_cached_session(wpa_s->eapol); if (wpa_s->current_ssid) { - wpa_s->own_disconnect_req = 1; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } @@ -1237,7 +1276,12 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { - wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL); + int psk_set = 0; + + if (ssid->psk_set) { + wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL); + psk_set = 1; + } #ifndef CONFIG_NO_PBKDF2 if (bss && ssid->bssid_set && ssid->ssid_len == 0 && ssid->passphrase) { @@ -1247,6 +1291,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + psk_set = 1; os_memset(psk, 0, sizeof(psk)); } #endif /* CONFIG_NO_PBKDF2 */ @@ -1284,6 +1329,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, "external passphrase)", psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + psk_set = 1; os_memset(psk, 0, sizeof(psk)); } else #endif /* CONFIG_NO_PBKDF2 */ @@ -1296,6 +1342,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, return -1; } wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + psk_set = 1; os_memset(psk, 0, sizeof(psk)); } else { wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable " @@ -1309,6 +1356,12 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ext_password_free(pw); } #endif /* CONFIG_EXT_PASSWORD */ + + if (!psk_set) { + wpa_msg(wpa_s, MSG_INFO, + "No PSK available for association"); + return -1; + } } else wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); @@ -1914,7 +1967,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_s->connect_work = work; - if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid)) { + if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) || + wpas_network_disabled(wpa_s, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt"); wpas_connect_work_done(wpa_s); return; @@ -1963,7 +2017,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); } - wpa_supplicant_cancel_sched_scan(wpa_s); + if (!wpa_s->pno) + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_cancel_scan(wpa_s); /* Starting new association, so clear the possibly used WPA IE from the @@ -2136,6 +2192,18 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } } +#ifdef CONFIG_FST + if (wpa_s->fst_ies) { + int fst_ies_len = wpabuf_len(wpa_s->fst_ies); + + if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) { + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(wpa_s->fst_ies), fst_ies_len); + wpa_ie_len += fst_ies_len; + } + } +#endif /* CONFIG_FST */ + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; cipher_pairwise = wpa_s->pairwise_cipher; @@ -2383,7 +2451,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; - wpa_s->current_bss = bss; + if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) + wpa_s->current_bss = bss; wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); wpa_supplicant_initiate_eapol(wpa_s); if (old_ssid != wpa_s->current_ssid) @@ -2505,15 +2574,20 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, } else wpa_supplicant_enable_one_network(wpa_s, ssid); - if (wpa_s->reassociate && !wpa_s->disconnected) { + if (wpa_s->reassociate && !wpa_s->disconnected && + (!wpa_s->current_ssid || + wpa_s->wpa_state == WPA_DISCONNECTED || + wpa_s->wpa_state == WPA_SCANNING)) { if (wpa_s->sched_scanning) { wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add " "new network to scan filters"); wpa_supplicant_cancel_sched_scan(wpa_s); } - if (wpa_supplicant_fast_associate(wpa_s) != 1) + if (wpa_supplicant_fast_associate(wpa_s) != 1) { + wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); + } } } @@ -2586,7 +2660,8 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, int disconnected = 0; if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) { - wpa_s->own_disconnect_req = 1; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); disconnected = 1; @@ -2625,6 +2700,13 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->connect_without_scan = (ssid->mode == WPAS_MODE_MESH) ? ssid : NULL; + + /* + * Don't optimize next scan freqs since a new ESS has been + * selected. + */ + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; } else { wpa_s->connect_without_scan = NULL; } @@ -2633,8 +2715,10 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, wpa_s->reassociate = 1; if (wpa_s->connect_without_scan || - wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_fast_associate(wpa_s) != 1) { + wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); + } if (ssid) wpas_notify_network_selected(wpa_s, ssid); @@ -2709,6 +2793,11 @@ int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan) if (ap_scan < 0 || ap_scan > 2) return -1; + if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) { + wpa_printf(MSG_INFO, + "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures"); + } + #ifdef ANDROID if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan && wpa_s->wpa_state >= WPA_ASSOCIATING && @@ -2848,7 +2937,7 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) { struct wpa_ssid *entry; - u8 ssid[MAX_SSID_LEN]; + u8 ssid[SSID_MAX_LEN]; int res; size_t ssid_len; u8 bssid[ETH_ALEN]; @@ -3053,12 +3142,36 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, (wpa_s->current_ssid == NULL || wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) { /* Timeout for completing IEEE 802.1X and WPA authentication */ - wpa_supplicant_req_auth_timeout( - wpa_s, - (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || - wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA || - wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) ? - 70 : 10, 0); + int timeout = 10; + + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { + /* Use longer timeout for IEEE 802.1X/EAP */ + timeout = 70; + } + +#ifdef CONFIG_WPS + if (wpa_s->current_ssid && wpa_s->current_bss && + (wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) && + eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) { + /* + * Use shorter timeout if going through WPS AP iteration + * for PIN config method with an AP that does not + * advertise Selected Registrar. + */ + struct wpabuf *wps_ie; + + wps_ie = wpa_bss_get_vendor_ie_multi( + wpa_s->current_bss, WPS_IE_VENDOR_TYPE); + if (wps_ie && + !wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) + timeout = 10; + wpabuf_free(wps_ie); + } +#endif /* CONFIG_WPS */ + + wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0); } wpa_s->eapol_received++; @@ -3190,6 +3303,12 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) } } + if (wpa_s->conf->ap_scan == 2 && + os_strcmp(wpa_s->driver->name, "nl80211") == 0) { + wpa_printf(MSG_INFO, + "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures"); + } + wpa_clear_keys(wpa_s, NULL); /* Make sure that TKIP countermeasures are not left enabled (could @@ -3615,6 +3734,124 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_FST + +static const u8 * wpas_fst_get_bssid_cb(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + + return (is_zero_ether_addr(wpa_s->bssid) || + wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid; +} + + +static void wpas_fst_get_channel_info_cb(void *ctx, + enum hostapd_hw_mode *hw_mode, + u8 *channel) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->current_bss) { + *hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq, + channel); + } else if (wpa_s->hw.num_modes) { + *hw_mode = wpa_s->hw.modes[0].mode; + } else { + WPA_ASSERT(0); + *hw_mode = 0; + } +} + + +static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes) +{ + struct wpa_supplicant *wpa_s = ctx; + + *modes = wpa_s->hw.modes; + return wpa_s->hw.num_modes; +} + + +static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies); + wpa_s->fst_ies = fst_ies; +} + + +static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data) +{ + struct wpa_supplicant *wpa_s = ctx; + + WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0); + return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(data), wpabuf_len(data), + 0); +} + + +static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0); + return wpa_s->received_mb_ies; +} + + +static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr, + const u8 *buf, size_t size) +{ + struct wpa_supplicant *wpa_s = ctx; + struct mb_ies_info info; + + WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0); + + if (!mb_ies_info_by_ies(&info, buf, size)) { + wpabuf_free(wpa_s->received_mb_ies); + wpa_s->received_mb_ies = mb_ies_by_info(&info); + } +} + + +const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + struct wpa_supplicant *wpa_s = ctx; + + *get_ctx = NULL; + if (!is_zero_ether_addr(wpa_s->bssid)) + return (wpa_s->received_mb_ies || !mb_only) ? + wpa_s->bssid : NULL; + return NULL; +} + + +const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) +{ + return NULL; +} + +void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s, + struct fst_wpa_obj *iface_obj) +{ + iface_obj->ctx = wpa_s; + iface_obj->get_bssid = wpas_fst_get_bssid_cb; + iface_obj->get_channel_info = wpas_fst_get_channel_info_cb; + iface_obj->get_hw_modes = wpas_fst_get_hw_modes; + iface_obj->set_ies = wpas_fst_set_ies_cb; + iface_obj->send_action = wpas_fst_send_action_cb; + iface_obj->get_mb_ie = wpas_fst_get_mb_ie_cb; + iface_obj->update_mb_ie = wpas_fst_update_mb_ie_cb; + iface_obj->get_peer_first = wpas_fst_get_peer_first; + iface_obj->get_peer_next = wpas_fst_get_peer_next; +} +#endif /* CONFIG_FST */ + static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s, const struct wpa_driver_capa *capa) { @@ -4153,6 +4390,28 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; } +#ifdef CONFIG_FST + if (wpa_s->conf->fst_group_id) { + struct fst_iface_cfg cfg; + struct fst_wpa_obj iface_obj; + + fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj); + os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id, + sizeof(cfg.group_id)); + cfg.priority = wpa_s->conf->fst_priority; + cfg.llt = wpa_s->conf->fst_llt; + + wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr, + &iface_obj, &cfg); + if (!wpa_s->fst) { + wpa_msg(wpa_s, MSG_ERROR, + "FST: Cannot attach iface %s to group %s", + wpa_s->ifname, cfg.group_id); + return -1; + } + } +#endif /* CONFIG_FST */ + if (wpas_wps_init(wpa_s)) return -1; @@ -4261,6 +4520,17 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, wpas_ctrl_radio_work_flush(wpa_s); radio_remove_interface(wpa_s); +#ifdef CONFIG_FST + if (wpa_s->fst) { + fst_detach(wpa_s->fst); + wpa_s->fst = NULL; + } + if (wpa_s->received_mb_ies) { + wpabuf_free(wpa_s->received_mb_ies); + wpa_s->received_mb_ies = NULL; + } +#endif /* CONFIG_FST */ + if (wpa_s->drv_priv) wpa_drv_deinit(wpa_s); @@ -4287,6 +4557,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, wpa_s->conf = NULL; } + os_free(wpa_s->ssids_from_scan_req); + os_free(wpa_s); } @@ -4362,8 +4634,10 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, #ifdef CONFIG_P2P if (wpa_s->global->p2p == NULL && + !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && - wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) < 0) { + wpas_p2p_add_p2pdev_interface( + wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) { wpa_printf(MSG_INFO, "P2P: Failed to enable P2P Device interface"); /* Try to continue without. P2P will be disabled. */ @@ -4489,6 +4763,33 @@ static const char * wpa_supplicant_msg_ifname_cb(void *ctx) #endif /* CONFIG_NO_WPA_MSG */ +#ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL +#define WPA_SUPPLICANT_CLEANUP_INTERVAL 10 +#endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */ + +/* Periodic cleanup tasks */ +static void wpas_periodic(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_global *global = eloop_ctx; + struct wpa_supplicant *wpa_s; + + eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0, + wpas_periodic, global, NULL); + +#ifdef CONFIG_P2P + if (global->p2p) + p2p_expire_peers(global->p2p); +#endif /* CONFIG_P2P */ + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age); +#ifdef CONFIG_AP + ap_periodic(wpa_s); +#endif /* CONFIG_AP */ + } +} + + /** * wpa_supplicant_init - Initialize %wpa_supplicant * @params: Parameters for %wpa_supplicant @@ -4563,6 +4864,11 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) if (params->override_ctrl_interface) global->params.override_ctrl_interface = os_strdup(params->override_ctrl_interface); +#ifdef CONFIG_P2P + if (params->conf_p2p_dev) + global->params.conf_p2p_dev = + os_strdup(params->conf_p2p_dev); +#endif /* CONFIG_P2P */ wpa_debug_level = global->params.wpa_debug_level = params->wpa_debug_level; wpa_debug_show_keys = global->params.wpa_debug_show_keys = @@ -4612,6 +4918,9 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) } #endif /* CONFIG_WIFI_DISPLAY */ + eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0, + wpas_periodic, global, NULL); + return global; } @@ -4663,6 +4972,8 @@ void wpa_supplicant_deinit(struct wpa_global *global) if (global == NULL) return; + eloop_cancel_timeout(wpas_periodic, global, NULL); + #ifdef CONFIG_WIFI_DISPLAY wifi_display_deinit(global); #endif /* CONFIG_WIFI_DISPLAY */ @@ -4699,6 +5010,9 @@ void wpa_supplicant_deinit(struct wpa_global *global) os_free(global->params.ctrl_interface_group); os_free(global->params.override_driver); os_free(global->params.override_ctrl_interface); +#ifdef CONFIG_P2P + os_free(global->params.conf_p2p_dev); +#endif /* CONFIG_P2P */ os_free(global->p2p_disallow_freq.range); os_free(global->p2p_go_avoid_freq.range); @@ -4958,6 +5272,15 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, str_clear_free(eap->external_sim_resp); eap->external_sim_resp = os_strdup(value); break; + case WPA_CTRL_REQ_PSK_PASSPHRASE: + if (wpa_config_set(ssid, "psk", value, 0) < 0) + return -1; + ssid->mem_only_psk = 1; + if (ssid->passphrase) + wpa_config_update_psk(ssid); + if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning) + wpa_supplicant_req_scan(wpa_s, 0, 0); + break; default: wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); return -1; @@ -5005,7 +5328,8 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) } if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && - (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk) + (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk && + !ssid->mem_only_psk) return 1; return 0; @@ -5227,7 +5551,8 @@ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, continue; if (ifs->current_ssid->mode == WPAS_MODE_AP || - ifs->current_ssid->mode == WPAS_MODE_P2P_GO) + ifs->current_ssid->mode == WPAS_MODE_P2P_GO || + ifs->current_ssid->mode == WPAS_MODE_MESH) freq = ifs->current_ssid->frequency; else if (wpa_drv_get_bssid(ifs, bssid) == 0) freq = ifs->assoc_freq; @@ -5243,7 +5568,7 @@ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, freqs_data[idx++].freq = freq; if (ifs->current_ssid->mode == WPAS_MODE_INFRA) { - freqs_data[i].flags = ifs->current_ssid->p2p_group ? + freqs_data[i].flags |= ifs->current_ssid->p2p_group ? WPA_FREQ_USED_BY_P2P_CLIENT : WPA_FREQ_USED_BY_INFRA_STATION; } diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf index 9980dbcdd91..68c04768752 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf @@ -99,6 +99,7 @@ eapol_version=1 # key_mgmt, pairwise, group, proto variables # # For use in FreeBSD with the wlan module ap_scan must be set to 1. +# # When using IBSS or AP mode, ap_scan=2 mode can force the new network to be # created immediately regardless of scan results. ap_scan=1 mode will first try # to scan for existing networks and only if no matches with the enabled @@ -259,6 +260,11 @@ fast_reauth=1 #wps_nfc_dh_privkey: Hexdump of DH Private Key #wps_nfc_dev_pw: Hexdump of Device Password +# Priority for the networks added through WPS +# This priority value will be set to each network profile that is added +# by executing the WPS protocol. +#wps_priority=0 + # Maximum number of BSS entries to keep in memory # Default: 200 # This can be used to limit memory use on the BSS entries (cached scan @@ -288,6 +294,10 @@ fast_reauth=1 # format: [:] #ext_password_backend=test:pw1=password|pw2=testing + +# Disable P2P functionality +# p2p_disabled=1 + # Timeout in seconds to detect STA inactivity (default: 300 seconds) # # This timeout value is used in P2P GO mode to clean up @@ -731,6 +741,11 @@ fast_reauth=1 # startup and reconfiguration time can be optimized by generating the PSK only # only when the passphrase or SSID has actually changed. # +# mem_only_psk: Whether to keep PSK/passphrase only in memory +# 0 = allow psk/passphrase to be stored to the configuration file +# 1 = do not store psk/passphrase to the configuration file +#mem_only_psk=0 +# # eapol_flags: IEEE 802.1X/EAPOL options (bit field) # Dynamic WEP key required for non-WPA mode # bit0 (1): require dynamically generated unicast WEP key @@ -960,9 +975,10 @@ fast_reauth=1 # tls_disable_session_ticket=0 - allow TLS Session Ticket extension to be used # Note: If not set, this is automatically set to 1 for EAP-TLS/PEAP/TTLS # as a workaround for broken authentication server implementations unless -# EAP workarounds are disabled with eap_workarounds=0. +# EAP workarounds are disabled with eap_workaround=0. # For EAP-FAST, this must be set to 0 (or left unconfigured for the # default value to be used automatically). +# tls_disable_tlsv1_0=1 - disable use of TLSv1.0 # tls_disable_tlsv1_1=1 - disable use of TLSv1.1 (a workaround for AAA servers # that have issues interoperating with updated TLS version) # tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers @@ -1113,6 +1129,32 @@ fast_reauth=1 # 2: MCS 0-9 # 3: not supported +##### Fast Session Transfer (FST) support ##################################### +# +# The options in this section are only available when the build configuration +# option CONFIG_FST is set while compiling hostapd. They allow this interface +# to be a part of FST setup. +# +# FST is the transfer of a session from a channel to another channel, in the +# same or different frequency bands. +# +# For detals, see IEEE Std 802.11ad-2012. + +# Identifier of an FST Group the interface belongs to. +#fst_group_id=bond0 + +# Interface priority within the FST Group. +# Announcing a higher priority for an interface means declaring it more +# preferable for FST switch. +# fst_priority is in 1..255 range with 1 being the lowest priority. +#fst_priority=100 + +# Default LLT value for this interface in milliseconds. The value used in case +# no value provided during session setup. Default is 50 msec. +# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2 +# Transitioning between states). +#fst_llt=100 + # Example blocks: # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h index 26ff216b02d..58df48c548e 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h @@ -17,14 +17,14 @@ #include "config_ssid.h" #include "wmm_ac.h" -extern const char *wpa_supplicant_version; -extern const char *wpa_supplicant_license; +extern const char *const wpa_supplicant_version; +extern const char *const wpa_supplicant_license; #ifndef CONFIG_NO_STDOUT_DEBUG -extern const char *wpa_supplicant_full_license1; -extern const char *wpa_supplicant_full_license2; -extern const char *wpa_supplicant_full_license3; -extern const char *wpa_supplicant_full_license4; -extern const char *wpa_supplicant_full_license5; +extern const char *const wpa_supplicant_full_license1; +extern const char *const wpa_supplicant_full_license2; +extern const char *const wpa_supplicant_full_license3; +extern const char *const wpa_supplicant_full_license4; +extern const char *const wpa_supplicant_full_license5; #endif /* CONFIG_NO_STDOUT_DEBUG */ struct wpa_sm; @@ -66,17 +66,6 @@ struct wpa_interface { */ const char *confanother; -#ifdef CONFIG_P2P - /** - * conf_p2p_dev - Configuration file used to hold the - * P2P Device configuration parameters. - * - * This can also be %NULL. In such a case, if a P2P Device dedicated - * interfaces is created, the main configuration file will be used. - */ - const char *conf_p2p_dev; -#endif /* CONFIG_P2P */ - /** * ctrl_interface - Control interface parameter * @@ -227,6 +216,18 @@ struct wpa_params { * its internal entropy store over restarts. */ char *entropy_file; + +#ifdef CONFIG_P2P + /** + * conf_p2p_dev - Configuration file used to hold the + * P2P Device configuration parameters. + * + * This can also be %NULL. In such a case, if a P2P Device dedicated + * interfaces is created, the main configuration file will be used. + */ + char *conf_p2p_dev; +#endif /* CONFIG_P2P */ + }; struct p2p_srv_bonjour { @@ -366,10 +367,12 @@ struct wps_ap_info { } type; unsigned int tries; struct os_reltime last_attempt; + unsigned int pbc_active; + u8 uuid[WPS_UUID_LEN]; }; struct wpa_ssid_value { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; }; @@ -479,7 +482,7 @@ struct wpa_supplicant { struct wpa_ssid_value *disallow_aps_ssid; size_t disallow_aps_ssid_count; - enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband; + enum set_band setband; /* Preferred network for the next connection attempt */ struct wpa_ssid *next_ssid; @@ -518,7 +521,7 @@ struct wpa_supplicant { unsigned int last_scan_res_size; struct os_reltime last_scan; - struct wpa_driver_ops *driver; + const struct wpa_driver_ops *driver; int interface_removed; /* whether the network interface has been * removed */ struct wpa_sm *wpa; @@ -590,6 +593,8 @@ struct wpa_supplicant { } scan_req, last_scan_req; enum wpa_states scan_prev_wpa_state; struct os_reltime scan_trigger_time, scan_start_time; + /* Minimum freshness requirement for connection purposes */ + struct os_reltime scan_min_time; int scan_runs; /* number of scan runs since WPS was started */ int *next_scan_freqs; int *manual_scan_freqs; @@ -609,6 +614,9 @@ struct wpa_supplicant { int scan_id[MAX_SCAN_ID]; unsigned int scan_id_count; + struct wpa_ssid_value *ssids_from_scan_req; + unsigned int num_ssids_from_scan_req; + u64 drv_flags; unsigned int drv_enc; unsigned int drv_smps_modes; @@ -639,6 +647,7 @@ struct wpa_supplicant { int wps_success; /* WPS success event received */ struct wps_er *wps_er; unsigned int wps_run; + struct os_reltime wps_pin_start_time; int blacklist_cleared; struct wpabuf *pending_eapol_rx; @@ -648,6 +657,7 @@ struct wpa_supplicant { unsigned int eap_expected_failure:1; unsigned int reattach:1; /* reassociation to the same BSS requested */ unsigned int mac_addr_changed:1; + unsigned int added_vif:1; struct os_reltime last_mac_addr_change; int last_mac_addr_style; @@ -661,7 +671,7 @@ struct wpa_supplicant { #ifdef CONFIG_SME struct { - u8 ssid[32]; + u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int freq; u8 assoc_req_ie[200]; @@ -736,7 +746,6 @@ struct wpa_supplicant { int p2p_mgmt; #ifdef CONFIG_P2P - struct wpa_supplicant *p2p_dev; struct p2p_go_neg_results *go_params; int create_p2p_iface; u8 pending_interface_addr[ETH_ALEN]; @@ -767,7 +776,7 @@ struct wpa_supplicant { u8 pending_join_iface_addr[ETH_ALEN]; u8 pending_join_dev_addr[ETH_ALEN]; int pending_join_wps_method; - u8 p2p_join_ssid[32]; + u8 p2p_join_ssid[SSID_MAX_LEN]; size_t p2p_join_ssid_len; int p2p_join_scan_count; int auto_pd_scan_retry; @@ -813,7 +822,8 @@ struct wpa_supplicant { unsigned int p2p_nfc_tag_enabled:1; unsigned int p2p_peer_oob_pk_hash_known:1; unsigned int p2p_disable_ip_addr_req:1; - unsigned int p2ps_join_addr_valid:1; + unsigned int p2ps_method_config_any:1; + unsigned int p2p_cli_probe:1; int p2p_persistent_go_freq; int p2p_persistent_id; int p2p_go_intent; @@ -969,6 +979,12 @@ struct wpa_supplicant { u8 last_tspecs_count; struct rrm_data rrm; + +#ifdef CONFIG_FST + struct fst_iface *fst; + const struct wpabuf *fst_ies; + struct wpabuf *received_mb_ies; +#endif /* CONFIG_FST */ }; @@ -1116,13 +1132,13 @@ struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, int eap_register_methods(void); /** - * Utility method to tell if a given network is a persistent group + * Utility method to tell if a given network is for persistent group storage * @ssid: Network object * Returns: 1 if network is a persistent group, 0 otherwise */ static inline int network_is_persistent_group(struct wpa_ssid *ssid) { - return ((ssid->disabled == 2) || ssid->p2p_persistent_group); + return ssid->disabled == 2 && ssid->p2p_persistent_group; } int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); @@ -1140,4 +1156,15 @@ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, int *freq_array, unsigned int len); +void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx); + +#ifdef CONFIG_FST + +struct fst_wpa_obj; + +void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s, + struct fst_wpa_obj *iface_obj); + +#endif /* CONFIG_FST */ + #endif /* WPA_SUPPLICANT_I_H */ diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.c b/contrib/wpa/wpa_supplicant/wpas_glue.c index 1bb82ba7169..29c22ba2c96 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.c +++ b/contrib/wpa/wpa_supplicant/wpas_glue.c @@ -737,6 +737,8 @@ enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field) return WPA_CTRL_REQ_EAP_PASSPHRASE; else if (os_strcmp(field, "SIM") == 0) return WPA_CTRL_REQ_SIM; + else if (os_strcmp(field, "PSK_PASSPHRASE") == 0) + return WPA_CTRL_REQ_PSK_PASSPHRASE; return WPA_CTRL_REQ_UNKNOWN; } @@ -776,6 +778,10 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, case WPA_CTRL_REQ_SIM: ret = "SIM"; break; + case WPA_CTRL_REQ_PSK_PASSPHRASE: + *txt = "PSK or passphrase"; + ret = "PSK_PASSPHRASE"; + break; default: break; } @@ -789,6 +795,35 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, return ret; } + +void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + const char *field_name, const char *txt) +{ + char *buf; + size_t buflen; + int len; + + buflen = 100 + os_strlen(txt) + ssid->ssid_len; + buf = os_malloc(buflen); + if (buf == NULL) + return; + len = os_snprintf(buf, buflen, "%s-%d:%s needed for SSID ", + field_name, ssid->id, txt); + if (os_snprintf_error(buflen, len)) { + os_free(buf); + return; + } + if (ssid->ssid && buflen > len + ssid->ssid_len) { + os_memcpy(buf + len, ssid->ssid, ssid->ssid_len); + len += ssid->ssid_len; + buf[len] = '\0'; + } + buf[buflen - 1] = '\0'; + wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s", buf); + os_free(buf); +} + + #ifdef IEEE8021X_EAPOL #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) static void wpa_supplicant_eap_param_needed(void *ctx, @@ -798,9 +833,6 @@ static void wpa_supplicant_eap_param_needed(void *ctx, struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *ssid = wpa_s->current_ssid; const char *field_name, *txt = NULL; - char *buf; - size_t buflen; - int len; if (ssid == NULL) return; @@ -817,25 +849,7 @@ static void wpa_supplicant_eap_param_needed(void *ctx, wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name); - buflen = 100 + os_strlen(txt) + ssid->ssid_len; - buf = os_malloc(buflen); - if (buf == NULL) - return; - len = os_snprintf(buf, buflen, - WPA_CTRL_REQ "%s-%d:%s needed for SSID ", - field_name, ssid->id, txt); - if (os_snprintf_error(buflen, len)) { - os_free(buf); - return; - } - if (ssid->ssid && buflen > len + ssid->ssid_len) { - os_memcpy(buf + len, ssid->ssid, ssid->ssid_len); - len += ssid->ssid_len; - buf[len] = '\0'; - } - buf[buflen - 1] = '\0'; - wpa_msg(wpa_s, MSG_INFO, "%s", buf); - os_free(buf); + wpas_send_ctrl_req(wpa_s, ssid, field_name, txt); } #else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ #define wpa_supplicant_eap_param_needed NULL @@ -1007,7 +1021,8 @@ static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk, { struct wpa_supplicant *wpa_s = ctx; - if (wpa_s->conf->key_mgmt_offload) + if (wpa_s->conf->key_mgmt_offload && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk, pmk_len); else diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.h b/contrib/wpa/wpa_supplicant/wpas_glue.h index 9808c2265f9..5585e5615a6 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.h +++ b/contrib/wpa/wpa_supplicant/wpas_glue.h @@ -22,4 +22,7 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field); +void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + const char *field_name, const char *txt); + #endif /* WPAS_GLUE_H */ diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.c b/contrib/wpa/wpa_supplicant/wps_supplicant.c index eabe986541c..60f761c81b8 100644 --- a/contrib/wpa/wpa_supplicant/wps_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wps_supplicant.c @@ -39,6 +39,14 @@ #define WPS_PIN_SCAN_IGNORE_SEL_REG 3 #endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */ +/* + * The minimum time in seconds before trying to associate to a WPS PIN AP that + * does not have Selected Registrar TRUE. + */ +#ifndef WPS_PIN_TIME_IGNORE_SEL_REG +#define WPS_PIN_TIME_IGNORE_SEL_REG 5 +#endif /* WPS_PIN_TIME_IGNORE_SEL_REG */ + static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); static void wpas_clear_wps(struct wpa_supplicant *wpa_s); @@ -539,6 +547,7 @@ static int wpa_supplicant_wps_cred(void *ctx, return -1; } } + ssid->priority = wpa_s->conf->wps_priority; wpas_wps_security_workaround(wpa_s, ssid, cred); @@ -552,6 +561,9 @@ static int wpa_supplicant_wps_cred(void *ctx, } #endif /* CONFIG_NO_CONFIG_WRITE */ + if (ssid->priority) + wpa_config_update_prio_list(wpa_s->conf); + /* * Optimize the post-WPS scan based on the channel used during * the provisioning in case EAP-Failure is not received. @@ -880,7 +892,8 @@ static int wpa_supplicant_wps_rf_band(void *ctx) if (!wpa_s->current_ssid || !wpa_s->assoc_freq) return 0; - return (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ; + return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ : + (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ; } @@ -942,8 +955,20 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; + union wps_event_data data; + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed " "out"); + os_memset(&data, 0, sizeof(data)); + data.fail.config_error = WPS_CFG_MSG_TIMEOUT; + data.fail.error_indication = WPS_EI_NO_ERROR; + /* + * Call wpas_notify_wps_event_fail() directly instead of through + * wpa_supplicant_wps_event() which would end up registering unnecessary + * timeouts (those are only for the case where the failure happens + * during an EAP-WSC exchange). + */ + wpas_notify_wps_event_fail(wpa_s, &data.fail); wpas_clear_wps(wpa_s); } @@ -1178,6 +1203,7 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, } #ifdef CONFIG_P2P if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) { + os_free(ssid->ssid); ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1); if (ssid->ssid) { ssid->ssid_len = wpa_s->go_params->ssid_len; @@ -1216,11 +1242,28 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id) { + os_get_reltime(&wpa_s->wps_pin_start_time); return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group, dev_pw_id, NULL, NULL, 0, 0); } +void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s) +{ + union wps_event_data data; + + os_memset(&data, 0, sizeof(data)); + data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + data.fail.error_indication = WPS_EI_NO_ERROR; + /* + * Call wpas_notify_wps_event_fail() directly instead of through + * wpa_supplicant_wps_event() which would end up registering unnecessary + * timeouts (those are only for the case where the failure happens + * during an EAP-WSC exchange). + */ + wpas_notify_wps_event_fail(wpa_s, &data.fail); +} + /* Cancel the wps pbc/pin requests */ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) { @@ -1487,6 +1530,8 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) wps->dev.rf_bands |= WPS_RF_24GHZ; else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A) wps->dev.rf_bands |= WPS_RF_50GHZ; + else if (modes[m].mode == HOSTAPD_MODE_IEEE80211AD) + wps->dev.rf_bands |= WPS_RF_60GHZ; } } if (wps->dev.rf_bands == 0) { @@ -1609,9 +1654,15 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, * external Registrar. */ if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) { - if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) { - wpa_printf(MSG_DEBUG, " skip - WPS AP " - "without active PIN Registrar"); + struct os_reltime age; + + os_reltime_age(&wpa_s->wps_pin_start_time, &age); + + if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG || + age.sec < WPS_PIN_TIME_IGNORE_SEL_REG) { + wpa_printf(MSG_DEBUG, + " skip - WPS AP without active PIN Registrar (scan_runs=%d age=%d)", + wpa_s->scan_runs, (int) age.sec); wpabuf_free(wps_ie); return 0; } @@ -1694,10 +1745,10 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid) { - const u8 *sel_uuid, *uuid; + const u8 *sel_uuid; struct wpabuf *wps_ie; int ret = 0; - struct wpa_bss *bss; + size_t i; if (!eap_is_wps_pbc_enrollee(&ssid->eap)) return 0; @@ -1718,40 +1769,28 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, sel_uuid = NULL; } - dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - struct wpabuf *ie; - if (bss == selected) + for (i = 0; i < wpa_s->num_wps_ap; i++) { + struct wps_ap_info *ap = &wpa_s->wps_ap[i]; + + if (!ap->pbc_active || + os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0) continue; - ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); - if (!ie) - continue; - if (!wps_is_selected_pbc_registrar(ie)) { - wpabuf_free(ie); - continue; - } + wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: " - MACSTR, MAC2STR(bss->bssid)); - uuid = wps_get_uuid_e(ie); + MACSTR, MAC2STR(ap->bssid)); wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS", - uuid, UUID_LEN); - if (os_memcmp(selected->bssid, bss->bssid, ETH_ALEN) == 0) { - wpabuf_free(ie); - continue; - } - if (sel_uuid == NULL || uuid == NULL || - os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) { + ap->uuid, UUID_LEN); + if (sel_uuid == NULL || + os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) { ret = 1; /* PBC overlap */ wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: " MACSTR " and " MACSTR, MAC2STR(selected->bssid), - MAC2STR(bss->bssid)); - wpabuf_free(ie); + MAC2STR(ap->bssid)); break; } /* TODO: verify that this is reasonable dual-band situation */ - - wpabuf_free(ie); } wpabuf_free(wps_ie); @@ -1910,7 +1949,7 @@ static int wpas_wps_network_to_cred(struct wpa_ssid *ssid, struct wps_credential *cred) { os_memset(cred, 0, sizeof(*cred)); - if (ssid->ssid_len > 32) + if (ssid->ssid_len > SSID_MAX_LEN) return -1; os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len); cred->ssid_len = ssid->ssid_len; @@ -2582,6 +2621,10 @@ static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_50GHZ)) freq = 5000 + 5 * chan; + else if (chan >= 1 && chan <= 4 && + (attr.rf_bands == NULL || + *attr.rf_bands & WPS_RF_60GHZ)) + freq = 56160 + 2160 * chan; if (freq) { wpa_printf(MSG_DEBUG, @@ -2771,7 +2814,8 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, struct wpabuf *wps; enum wps_ap_info_type type; struct wps_ap_info *ap; - int r; + int r, pbc_active; + const u8 *uuid; if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL) return; @@ -2788,7 +2832,8 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, else type = WPS_AP_NOT_SEL_REG; - wpabuf_free(wps); + uuid = wps_get_uuid_e(wps); + pbc_active = wps_is_selected_pbc_registrar(wps); ap = wpas_wps_get_ap_info(wpa_s, res->bssid); if (ap) { @@ -2800,13 +2845,16 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, if (type != WPS_AP_NOT_SEL_REG) wpa_blacklist_del(wpa_s, ap->bssid); } - return; + ap->pbc_active = pbc_active; + if (uuid) + os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); + goto out; } ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1, sizeof(struct wps_ap_info)); if (ap == NULL) - return; + goto out; wpa_s->wps_ap = ap; ap = &wpa_s->wps_ap[wpa_s->num_wps_ap]; @@ -2815,8 +2863,14 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, os_memset(ap, 0, sizeof(*ap)); os_memcpy(ap->bssid, res->bssid, ETH_ALEN); ap->type = type; + ap->pbc_active = pbc_active; + if (uuid) + os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added", MAC2STR(ap->bssid), ap->type); + +out: + wpabuf_free(wps); } diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.h b/contrib/wpa/wpa_supplicant/wps_supplicant.h index 683bd50e4ba..3c25ca86dc6 100644 --- a/contrib/wpa/wpa_supplicant/wps_supplicant.h +++ b/contrib/wpa/wpa_supplicant/wps_supplicant.h @@ -33,6 +33,7 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, int p2p_group); int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id); +void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s); int wpas_wps_cancel(struct wpa_supplicant *wpa_s); int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, struct wps_new_ap_settings *settings); diff --git a/etc/Makefile b/etc/Makefile index 0112c617bc6..24c83d1eb2a 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -240,6 +240,9 @@ distribution: ${_+_}cd ${.CURDIR}/defaults; ${MAKE} install ${_+_}cd ${.CURDIR}/devd; ${MAKE} install ${_+_}cd ${.CURDIR}/gss; ${MAKE} install +.if ${MK_NTP} != "no" + ${_+_}cd ${.CURDIR}/ntp; ${MAKE} install +.endif ${_+_}cd ${.CURDIR}/periodic; ${MAKE} install .if ${MK_PKGBOOTSTRAP} != "no" ${_+_}cd ${.CURDIR}/pkg; ${MAKE} install @@ -343,6 +346,7 @@ MTREES+= mtree/BSD.groff.dist /usr .endif .if ${MK_TESTS} != "no" MTREES+= mtree/BSD.tests.dist ${TESTSBASE} +MTREES+= mtree/BSD.tests.dist /usr/lib/debug/${TESTSBASE} .endif .if ${MK_SENDMAIL} != "no" MTREES+= mtree/BSD.sendmail.dist / diff --git a/etc/mtree/BSD.debug.dist b/etc/mtree/BSD.debug.dist index fe5bc48cd1d..8df0a725e5f 100644 --- a/etc/mtree/BSD.debug.dist +++ b/etc/mtree/BSD.debug.dist @@ -17,6 +17,8 @@ .. .. libexec + casper + .. .. sbin .. @@ -59,6 +61,8 @@ .. sbin .. + tests + .. .. .. .. diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 1fb33654ac5..8d293230f26 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -12,6 +12,8 @@ .. date .. + dd + .. expr .. ls @@ -360,6 +362,8 @@ .. .. sys + acl + .. aio .. fifo @@ -410,6 +414,10 @@ unlink .. .. + posixshm + .. + vfs + .. vm .. .. diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist index 98e11cd2b3a..12ee777518d 100644 --- a/etc/mtree/BSD.usr.dist +++ b/etc/mtree/BSD.usr.dist @@ -190,12 +190,6 @@ atm .. legal - intel_ipw - .. - intel_iwi - .. - intel_wpi - .. .. llvm clang diff --git a/etc/mtree/BSD.var.dist b/etc/mtree/BSD.var.dist index 7d4af0be5ab..3a5852905f3 100644 --- a/etc/mtree/BSD.var.dist +++ b/etc/mtree/BSD.var.dist @@ -46,6 +46,8 @@ .. ipf mode=0700 .. + ntp mode=0700 + .. pkg .. ports diff --git a/etc/ntp.conf b/etc/ntp.conf index ea398776605..c4ad0aa1bde 100644 --- a/etc/ntp.conf +++ b/etc/ntp.conf @@ -77,3 +77,8 @@ restrict 127.127.1.0 # #server 127.127.1.0 #fudge 127.127.1.0 stratum 10 + +# See http://support.ntp.org/bin/view/Support/ConfiguringNTP#Section_6.14. +# for documentation regarding leapfile. Updates to the file can be obtained +# from ftp://time.nist.gov/pub/ or ftp://tycho.usno.navy.mil/pub/ntp/. +leapfile "/etc/ntp/leap-seconds" diff --git a/etc/ntp/Makefile b/etc/ntp/Makefile new file mode 100644 index 00000000000..f1aff4f9006 --- /dev/null +++ b/etc/ntp/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +NO_OBJ= + +FILES= leap-seconds + +FILESDIR= /etc/ntp +FILESMODE= 644 + +.include diff --git a/etc/ntp/leap-seconds b/etc/ntp/leap-seconds new file mode 100644 index 00000000000..b8b41f2d117 --- /dev/null +++ b/etc/ntp/leap-seconds @@ -0,0 +1,119 @@ +# +# $FreeBSD$ +# +# ATOMIC TIME. +# The Coordinated Universal Time (UTC) is the reference time scale derived +# from The "Temps Atomique International" (TAI) calculated by the Bureau +# International des Poids et Mesures (BIPM) using a worldwide network of atomic +# clocks. UTC differs from TAI by an integer number of seconds; it is the basis +# of all activities in the world. +# +# +# ASTRONOMICAL TIME (UT1) is the time scale based on the rate of rotation of the earth. +# It is now mainly derived from Very Long Baseline Interferometry (VLBI). The various +# irregular fluctuations progressively detected in the rotation rate of the Earth lead +# in 1972 to the replacement of UT1 by UTC as the reference time scale. +# +# +# LEAP SECOND +# Atomic clocks are more stable than the rate of the earth rotatiob since the later +# undergoes a full range of geophysical perturbations at various time scales (lunisolar +# and core-mantle torques,atmospheric and oceanic effetcs, ...) +# Leap seconds are needed to keep the two time scales in agreement, i.e. UT1-UTC smaller +# than 0.9 second. So, when necessary a "leap second" is introduced in UTC. +# Since the adoption of this system in 1972 it has been necessary to add 26 seconds to UTC, +# firstly due to the initial choice of the value of the second (1/86400 mean solar day of +# the year 1820) and secondly to the general slowing down of the Earth's rotation. It is +# theorically possible to have a negative leap second (a second removed from UTC), but so far, +# all leap seconds have been positive (a second has been added to UTC). Based on what we know about the earth's rotation, +# it is unlikely that we will ever have a negative leap second. +# +# +# HISTORY +# The first leap second was added on June 30, 1972. Until 2000, it was necessary in average to add a leap second at a rate +# of 1 to 2 years. Since 2000, due to the fact that the earth rate of rotation is accelerating, leap seconds are introduced +# with an average frequency of 3 to 4 years. +# +# +# RESPONSABILITY OF THE DECISION TO INTRODUCE A LEAP SECOND IN UTC +# The decision to introduce a leap second in UTC is the responsibility of the Earth Orientation Center of +# the International Earth Rotation and reference System Service (IERS). This center is located at Paris +# Observatory. According to international agreements, leap second date have to occur at fixed date : +# first preference is given to the end of December and June, and second preference at the end of March +# and September. Since the system was introduced in 1972, only dates in June and December were used. +# +# Questions or comments to: +# Daniel Gambis, daniel.gambis@obspm.fr +# Christian Bizouard: christian.bizouard@obspm.fr +# Earth orientation Center of the IERS +# Paris Observatory, France +# +# +# +# VALIDITY OF THE FILE +# It is important to express the validity of the file. These next two dates are +# given in units of seconds since 1900.0. +# +# 1) Last update of the file. +# +# Updated through IERS Bulletin C (ftp://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat) +# +# The following line shows the last update of this file in NTP timestamp: +# +#$ 3645216000 +# +# 2) Expiration date of the file given on a semi-annual basis: last June or last December +# +# File expires on 28 December 2015 +# +# Expire date in NTP timestamp: +# +#@ 3660249600 +# +# +# LIST OF LEAP SECONDS +# NTP timestamp (X parameter) is the number of seconds since 1900.0 +# +# MJD: The Modified Julian Day number. MJD = X/86400 + 15020 +# +# DTAI: The difference DTAI= TAI-UTC in units of seconds +# It is the quantity to add to UTC to get the time in TAI +# +# Day Month Year : epoch in clear +# +#NTP Time DTAI Day Month Year +# +2272060800 10 # 1 Jan 1972 +2287785600 11 # 1 Jul 1972 +2303683200 12 # 1 Jan 1973 +2335219200 13 # 1 Jan 1974 +2366755200 14 # 1 Jan 1975 +2398291200 15 # 1 Jan 1976 +2429913600 16 # 1 Jan 1977 +2461449600 17 # 1 Jan 1978 +2492985600 18 # 1 Jan 1979 +2524521600 19 # 1 Jan 1980 +2571782400 20 # 1 Jul 1981 +2603318400 21 # 1 Jul 1982 +2634854400 22 # 1 Jul 1983 +2698012800 23 # 1 Jul 1985 +2776982400 24 # 1 Jan 1988 +2840140800 25 # 1 Jan 1990 +2871676800 26 # 1 Jan 1991 +2918937600 27 # 1 Jul 1992 +2950473600 28 # 1 Jul 1993 +2982009600 29 # 1 Jul 1994 +3029443200 30 # 1 Jan 1996 +3076704000 31 # 1 Jul 1997 +3124137600 32 # 1 Jan 1999 +3345062400 33 # 1 Jan 2006 +3439756800 34 # 1 Jan 2009 +3550089600 35 # 1 Jul 2012 +3644697600 36 # 1 Jul 2015 +# +# In order to verify the integrity of this file, a hash code +# has been generated. For more information how to use +# this hash code, please consult the README file under the +# 'sha' repertory. +# +#h 620ba8af 37900668 95ac09ba d77640f9 6fd75493 diff --git a/etc/periodic/Makefile b/etc/periodic/Makefile index 8fb56dff051..a2d99020fd1 100644 --- a/etc/periodic/Makefile +++ b/etc/periodic/Makefile @@ -1,5 +1,6 @@ # $FreeBSD$ SUBDIR= daily security weekly monthly +SUBDIR_PARALLEL= .include diff --git a/gnu/Makefile b/gnu/Makefile index 25480c7b951..bd84bc42698 100644 --- a/gnu/Makefile +++ b/gnu/Makefile @@ -3,10 +3,13 @@ .include -SUBDIR= lib ${_tests} usr.bin +SUBDIR= lib .WAIT \ + ${_tests} usr.bin .if ${MK_TESTS} != "no" _tests= tests .endif +SUBDIR_PARALLEL= + .include diff --git a/gnu/lib/Makefile b/gnu/lib/Makefile index 4fff36a4872..b77f9e3e74d 100644 --- a/gnu/lib/Makefile +++ b/gnu/lib/Makefile @@ -24,6 +24,9 @@ SUBDIR+= libreadline # have taken care of that already. .if ${MK_GNUCXX} != "no" SUBDIR+= libstdc++ libsupc++ +SUBDIR_DEPENDS_libsupc++:= libstdc++ .endif +SUBDIR_PARALLEL= + .include diff --git a/gnu/usr.bin/binutils/ld/genscripts.sh b/gnu/usr.bin/binutils/ld/genscripts.sh index 5090cd3f877..238263eb75a 100755 --- a/gnu/usr.bin/binutils/ld/genscripts.sh +++ b/gnu/usr.bin/binutils/ld/genscripts.sh @@ -50,8 +50,7 @@ fi if test -d ldscripts; then true else - rm -f ldscripts - mkdir ldscripts + mkdir -p ldscripts fi # Set some flags for the emultempl scripts. USE_LIBPATH will diff --git a/gnu/usr.bin/cc/Makefile b/gnu/usr.bin/cc/Makefile index 41349cfa1b9..378ababfcde 100644 --- a/gnu/usr.bin/cc/Makefile +++ b/gnu/usr.bin/cc/Makefile @@ -5,7 +5,8 @@ # The order of some of these are rather important. Some depend on previous # subdirs. -SUBDIR= cc_tools libiberty libcpp libdecnumber cc_int cc cc1 include +SUBDIR= cc_tools .WAIT \ + libiberty libcpp libdecnumber cc_int cc cc1 include .if ${MK_CPP} != "no" SUBDIR+= cpp @@ -19,4 +20,13 @@ SUBDIR+= cc1plus c++ SUBDIR+= gcov .endif +SUBDIR_DEPEND_c++:= libcpp libiberty +SUBDIR_DEPEND_cc= libcpp libiberty +SUBDIR_DEPEND_cpp= libcpp libiberty +SUBDIR_DEPEND_cc1plus= cc_int libcpp libdecnumber libiberty +SUBDIR_DEPEND_cc1= cc_int libcpp libdecnumber libiberty +SUBDIR_DEPEND_gcov= libiberty + +SUBDIR_PARALLEL= + .include diff --git a/gnu/usr.bin/cc/collect2/Makefile b/gnu/usr.bin/cc/collect2/Makefile deleted file mode 100644 index 0b07b857a44..00000000000 --- a/gnu/usr.bin/cc/collect2/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# $FreeBSD$ - -.include "../Makefile.inc" - -.PATH: ${GCCDIR} - -PROG= collect2 -SRCS= collect2.c tlink.c version.c -MAN= - -.include diff --git a/gnu/usr.bin/cc/protoize/Makefile b/gnu/usr.bin/cc/protoize/Makefile deleted file mode 100644 index b851bed18b2..00000000000 --- a/gnu/usr.bin/cc/protoize/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# $FreeBSD$ - -.include "../Makefile.inc" - -.PATH: ${.CURDIR}/../cc_tools ${GCCDIR} - -PROG= protoize -MAN= - -# things are rather hard-coded, we work around that here -CFLAGS+= -DDEFAULT_TARGET_VERSION=\"\" -CFLAGS+= -DDEFAULT_TARGET_MACHINE=\"../libdata/gcc\" - -DPADD= ${LIBCC_INT} -LDADD= ${LIBCC_INT} - -CLEANFILES= config.h -config.h: auto-host.h freebsd-native.h - cat ${.ALLSRC} >${.TARGET} - -.include diff --git a/gnu/usr.bin/groff/Makefile b/gnu/usr.bin/groff/Makefile index cb83deaf10d..ba760258ee6 100644 --- a/gnu/usr.bin/groff/Makefile +++ b/gnu/usr.bin/groff/Makefile @@ -6,4 +6,6 @@ SUBDIR= contrib font man src tmac SUBDIR_DEPEND_${subdir}= src .endfor +SUBDIR_PARALLEL= + .include diff --git a/gnu/usr.bin/rcs/Makefile b/gnu/usr.bin/rcs/Makefile index d6a960b729f..6b2bd1c3a8f 100644 --- a/gnu/usr.bin/rcs/Makefile +++ b/gnu/usr.bin/rcs/Makefile @@ -1,5 +1,7 @@ # $FreeBSD$ -SUBDIR= lib ci co merge rcs rcsclean rcsdiff rcsmerge rlog rcsfreeze +SUBDIR= lib .WAIT \ + ci co merge rcs rcsclean rcsdiff rcsmerge rlog rcsfreeze +SUBDIR_PARALLEL= .include diff --git a/include/Makefile b/include/Makefile index 1cecff751b0..eed1c926d84 100644 --- a/include/Makefile +++ b/include/Makefile @@ -7,6 +7,7 @@ CLEANFILES= osreldate.h version vers.c SUBDIR= arpa protocols rpcsvc rpc xlocale +SUBDIR_PARALLEL= INCS= a.out.h ar.h assert.h bitstring.h complex.h cpio.h _ctype.h ctype.h \ db.h \ dirent.h dlfcn.h elf.h elf-hints.h err.h fmtmsg.h fnmatch.h fstab.h \ diff --git a/include/resolv.h b/include/resolv.h index e3d4fd12a09..78da3748a20 100644 --- a/include/resolv.h +++ b/include/resolv.h @@ -176,7 +176,8 @@ struct __res_state { int res_h_errno; /*%< last one set for this context */ int _vcsock; /*%< PRIVATE: for res_send VC i/o */ u_int _flags; /*%< PRIVATE: see below */ - u_int _pad; /*%< make _u 64 bit aligned */ + u_short reload_period; /*%< seconds between stat(resolv.conf)*/ + u_short _pad; /*%< make _u 64 bit aligned */ union { /* On an 32-bit arch this means 512b total. */ char pad[72 - 4*sizeof (int) - 3*sizeof (void *)]; @@ -188,6 +189,8 @@ struct __res_state { } _ext; } _u; u_char *_rnd; /*%< PRIVATE: random state */ + struct timespec conf_mtim; /*%< mod time of loaded resolv.conf */ + time_t conf_stat; /*%< time of last stat(resolv.conf) */ }; typedef struct __res_state *res_state; diff --git a/lib/Makefile b/lib/Makefile index 031c1b46bde..a3321af9aed 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -129,7 +129,7 @@ SUBDIR_DEPEND_libarchive= libz libbz2 libexpat liblzma libmd SUBDIR_DEPEND_libatm= libmd SUBDIR_DEPEND_libauditdm= libbsm SUBDIR_DEPEND_libbsnmp= ${_libnetgraph} -SUBDIR_DEPEND_libc++= libcxxrt +SUBDIR_DEPEND_libc++:= libcxxrt SUBDIR_DEPEND_libc= libcompiler_rt SUBDIR_DEPEND_libcam= libsbuf SUBDIR_DEPEND_libcapsicum= libnv @@ -137,7 +137,6 @@ SUBDIR_DEPEND_libcasper= libcapsicum libnv libpjdlog SUBDIR_DEPEND_libdevstat= libkvm SUBDIR_DEPEND_libdpv= libfigpar ncurses libutil SUBDIR_DEPEND_libedit= ncurses -SUBDIR_DEPEND_libg++= msun SUBDIR_DEPEND_libgeom= libexpat libsbuf SUBDIR_DEPEND_liblibrpcsec_gss= libgssapi SUBDIR_DEPEND_libmagic= libz @@ -148,7 +147,6 @@ SUBDIR_DEPEND_libpjdlog= libutil SUBDIR_DEPEND_libprocstat= libkvm libutil SUBDIR_DEPEND_libradius= libmd SUBDIR_DEPEND_libsmb= libkiconv -SUBDIR_DEPEND_libstdc++= msun SUBDIR_DEPEND_libtacplus= libmd SUBDIR_DEPEND_libulog= libmd SUBDIR_DEPEND_libunbound= ${_libldns} diff --git a/lib/clang/clang.build.mk b/lib/clang/clang.build.mk index acb09f51c54..0c564a5fadf 100644 --- a/lib/clang/clang.build.mk +++ b/lib/clang/clang.build.mk @@ -39,6 +39,7 @@ CXXFLAGS.clang+= -stdlib=libc++ .PATH: ${LLVM_SRCS}/${SRCDIR} +.if ${MK_META_MODE} == "yes" .if empty(TOOLSDIR) || !exists(${TOOLSDIR}/usr/bin/clang-tblgen) .if ${MACHINE} == "host" && defined(BOOTSTRAPPING_TOOLS) .if !empty(LEGACY_TOOLS) && exists(${LEGACY_TOOLS}/usr/bin/tblgen) @@ -57,6 +58,7 @@ TOOLSDIR?= TBLGEN= ${TOOLSDIR}/usr/bin/tblgen CLANG_TBLGEN= ${TOOLSDIR}/usr/bin/clang-tblgen .endif +.endif # ${MK_META_MODE} == "yes" TBLGEN?= tblgen CLANG_TBLGEN?= clang-tblgen diff --git a/lib/clang/include/clang/Config/config.h b/lib/clang/include/clang/Config/config.h index 7ab95a0a190..fecc361e119 100644 --- a/lib/clang/include/clang/Config/config.h +++ b/lib/clang/include/clang/Config/config.h @@ -11,7 +11,7 @@ #define BUG_REPORT_URL "https://bugs.freebsd.org/submit/" /* Default OpenMP runtime used by -fopenmp. */ -#define CLANG_DEFAULT_OPENMP_RUNTIME "libgomp" +#define CLANG_DEFAULT_OPENMP_RUNTIME "libomp" /* Multilib suffix for libdir. */ #define CLANG_LIBDIR_SUFFIX "" diff --git a/lib/clang/include/llvm/Config/config.h b/lib/clang/include/llvm/Config/config.h index bae066c9690..ef7295cb380 100644 --- a/lib/clang/include/llvm/Config/config.h +++ b/lib/clang/include/llvm/Config/config.h @@ -15,7 +15,7 @@ #define BUG_REPORT_URL "https://bugs.freebsd.org/submit/" /* Default OpenMP runtime used by -fopenmp. */ -#define CLANG_DEFAULT_OPENMP_RUNTIME "libgomp" +#define CLANG_DEFAULT_OPENMP_RUNTIME "libomp" /* Define if we have libxml2 */ /* #undef CLANG_HAVE_LIBXML */ diff --git a/lib/libc/resolv/res_init.c b/lib/libc/resolv/res_init.c index 4dbd6d1caca..087e6a86256 100644 --- a/lib/libc/resolv/res_init.c +++ b/lib/libc/resolv/res_init.c @@ -78,6 +78,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -227,6 +228,7 @@ __res_vinit(res_state statp, int preinit) { statp->pfcode = 0; statp->_vcsock = -1; statp->_flags = 0; + statp->reload_period = 2; statp->qhook = NULL; statp->rhook = NULL; statp->_u._ext.nscount = 0; @@ -321,6 +323,22 @@ __res_vinit(res_state statp, int preinit) { nserv = 0; if ((fp = fopen(_PATH_RESCONF, "re")) != NULL) { + struct stat sb; + struct timespec now; + + if (_fstat(fileno(fp), &sb) == 0) { + statp->conf_mtim = sb.st_mtim; + if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == 0) { + statp->conf_stat = now.tv_sec; + } else { + statp->conf_stat = 0; + } + } else { + statp->conf_mtim.tv_sec = 0; + statp->conf_mtim.tv_nsec = 0; + statp->conf_stat = 0; + } + /* read the config file */ while (fgets(buf, sizeof(buf), fp) != NULL) { /* skip comments */ @@ -666,6 +684,10 @@ res_setoptions(res_state statp, const char *options, const char *source) } else if (!strncmp(cp, "no-check-names", sizeof("no-check-names") - 1)) { statp->options |= RES_NOCHECKNAME; + } else if (!strncmp(cp, "reload-period:", + sizeof("reload-period:") - 1)) { + statp->reload_period = (u_short) + atoi(cp + sizeof("reload-period:") - 1); } #ifdef RES_USE_EDNS0 else if (!strncmp(cp, "edns0", sizeof("edns0") - 1)) { diff --git a/lib/libc/resolv/res_state.c b/lib/libc/resolv/res_state.c index 59c94306075..87643422ebb 100644 --- a/lib/libc/resolv/res_state.c +++ b/lib/libc/resolv/res_state.c @@ -26,6 +26,8 @@ */ #include +#include +#include #include #include #include @@ -59,13 +61,38 @@ res_keycreate(void) res_thr_keycreated = thr_keycreate(&res_key, free_res) == 0; } +static res_state +res_check_reload(res_state statp) +{ + struct timespec now; + struct stat sb; + + if ((statp->options & RES_INIT) == 0 || statp->reload_period == 0) { + return (statp); + } + + if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) != 0 || + (now.tv_sec - statp->conf_stat) < statp->reload_period) { + return (statp); + } + + statp->conf_stat = now.tv_sec; + if (stat(_PATH_RESCONF, &sb) == 0 && + (sb.st_mtim.tv_sec != statp->conf_mtim.tv_sec || + sb.st_mtim.tv_nsec != statp->conf_mtim.tv_nsec)) { + statp->options &= ~RES_INIT; + } + + return (statp); +} + res_state __res_state(void) { res_state statp; if (thr_main() != 0) - return (&_res); + return res_check_reload(&_res); if (thr_once(&res_init_once, res_keycreate) != 0 || !res_thr_keycreated) @@ -73,7 +100,7 @@ __res_state(void) statp = thr_getspecific(res_key); if (statp != NULL) - return (statp); + return res_check_reload(statp); statp = calloc(1, sizeof(*statp)); if (statp == NULL) return (&_res); diff --git a/lib/libc/string/ffs.3 b/lib/libc/string/ffs.3 index 192771ffaee..8d2601d6efc 100644 --- a/lib/libc/string/ffs.3 +++ b/lib/libc/string/ffs.3 @@ -30,7 +30,7 @@ .\" @(#)ffs.3 8.2 (Berkeley) 4/19/94 .\" $FreeBSD$ .\" -.Dd September 29, 2012 +.Dd October 17, 2015 .Dt FFS 3 .Os .Sh NAME @@ -81,7 +81,8 @@ Bits are numbered starting at 1, the least significant bit. A return value of zero from any of these functions means that the argument was zero. .Sh SEE ALSO -.Xr bitstring 3 +.Xr bitstring 3 , +.Xr bitset 9 .Sh HISTORY The .Fn ffs diff --git a/lib/libcam/camlib.c b/lib/libcam/camlib.c index f3202470a0e..b7024a6d609 100644 --- a/lib/libcam/camlib.c +++ b/lib/libcam/camlib.c @@ -676,8 +676,10 @@ cam_close_spec_device(struct cam_device *dev) if (dev == NULL) return; - if (dev->fd >= 0) + if (dev->fd >= 0) { close(dev->fd); + dev->fd = -1; + } } char * diff --git a/lib/libfetch/file.c b/lib/libfetch/file.c index 8c1d4042dcc..7b6462f5b69 100644 --- a/lib/libfetch/file.c +++ b/lib/libfetch/file.c @@ -48,7 +48,7 @@ fetchXGetFile(struct url *u, struct url_stat *us, const char *flags) if (us && fetchStatFile(u, us, flags) == -1) return (NULL); - f = fopen(u->doc, "r"); + f = fopen(u->doc, "re"); if (f == NULL) { fetch_syserr(); @@ -61,7 +61,6 @@ fetchXGetFile(struct url *u, struct url_stat *us, const char *flags) return (NULL); } - fcntl(fileno(f), F_SETFD, FD_CLOEXEC); return (f); } @@ -77,9 +76,9 @@ fetchPutFile(struct url *u, const char *flags) FILE *f; if (CHECK_FLAG('a')) - f = fopen(u->doc, "a"); + f = fopen(u->doc, "ae"); else - f = fopen(u->doc, "w+"); + f = fopen(u->doc, "w+e"); if (f == NULL) { fetch_syserr(); @@ -92,7 +91,6 @@ fetchPutFile(struct url *u, const char *flags) return (NULL); } - fcntl(fileno(f), F_SETFD, FD_CLOEXEC); return (f); } diff --git a/lib/libfetch/http.c b/lib/libfetch/http.c index 91ca6a39973..f6a74540c8f 100644 --- a/lib/libfetch/http.c +++ b/lib/libfetch/http.c @@ -1376,8 +1376,12 @@ http_connect(struct url *URL, struct url *purl, const char *flags) { struct url *curl; conn_t *conn; + hdr_t h; + http_headerbuf_t headerbuf; + const char *p; int verbose; int af, val; + int serrno; #ifdef INET6 af = AF_UNSPEC; @@ -1398,6 +1402,7 @@ http_connect(struct url *URL, struct url *purl, const char *flags) if ((conn = fetch_connect(curl->host, curl->port, af, verbose)) == NULL) /* fetch_connect() has already set an error code */ return (NULL); + init_http_headerbuf(&headerbuf); if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 && purl) { http_cmd(conn, "CONNECT %s:%d HTTP/1.1", URL->host, URL->port); @@ -1405,10 +1410,26 @@ http_connect(struct url *URL, struct url *purl, const char *flags) URL->host, URL->port); http_cmd(conn, ""); if (http_get_reply(conn) != HTTP_OK) { - fetch_close(conn); - return (NULL); + http_seterr(conn->err); + goto ouch; } - http_get_reply(conn); + /* Read and discard the rest of the proxy response */ + if (fetch_getln(conn) < 0) { + fetch_syserr(); + goto ouch; + } + do { + switch ((h = http_next_header(conn, &headerbuf, &p))) { + case hdr_syserror: + fetch_syserr(); + goto ouch; + case hdr_error: + http_seterr(HTTP_PROTOCOL_ERROR); + goto ouch; + default: + /* ignore */ ; + } + } while (h < hdr_end); } if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 && fetch_ssl(conn, URL, verbose) == -1) { @@ -1416,13 +1437,20 @@ http_connect(struct url *URL, struct url *purl, const char *flags) /* grrr */ errno = EAUTH; fetch_syserr(); - return (NULL); + goto ouch; } val = 1; setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val)); + clean_http_headerbuf(&headerbuf); return (conn); +ouch: + serrno = errno; + clean_http_headerbuf(&headerbuf); + fetch_close(conn); + errno = serrno; + return (NULL); } static struct url * diff --git a/lib/libiconv_modules/Makefile b/lib/libiconv_modules/Makefile index 1a7213612b8..3af91c746c2 100644 --- a/lib/libiconv_modules/Makefile +++ b/lib/libiconv_modules/Makefile @@ -5,5 +5,6 @@ SUBDIR= BIG5 DECHanyu EUC EUCTW GBK2K HZ ISO2022 JOHAB MSKanji UES UTF1632 \ UTF7 UTF8 VIQR ZW iconv_none iconv_std mapper_646 mapper_none \ mapper_parallel mapper_serial mapper_std mapper_zone +SUBDIR_PARALLEL= .include diff --git a/lib/libpmc/libpmc.c b/lib/libpmc/libpmc.c index 93056183dad..129c064b5c2 100644 --- a/lib/libpmc/libpmc.c +++ b/lib/libpmc/libpmc.c @@ -388,7 +388,7 @@ PMC_CLASS_TABLE_DESC(tsc, TSC, tsc, tsc); #if defined(__XSCALE__) PMC_CLASS_TABLE_DESC(xscale, XSCALE, xscale, xscale); #endif -PMC_CLASS_TABLE_DESC(cortex_a8, ARMV7, cortex_a9, armv7); +PMC_CLASS_TABLE_DESC(cortex_a8, ARMV7, cortex_a8, armv7); PMC_CLASS_TABLE_DESC(cortex_a9, ARMV7, cortex_a9, armv7); #endif #if defined(__aarch64__) diff --git a/lib/libxo/Makefile b/lib/libxo/Makefile index d854a4c1d3c..f8453f40528 100644 --- a/lib/libxo/Makefile +++ b/lib/libxo/Makefile @@ -19,6 +19,8 @@ CFLAGS+=-DXO_ENCODERDIR=\"/usr/lib/libxo/encoder\" INCS= xo.h xo_encoder.h INCSDIR=${INCLUDEDIR}/libxo +LIBADD= util + WARNS?= 5 MAN+= libxo.3 diff --git a/lib/libxo/tests/Makefile b/lib/libxo/tests/Makefile index 2a4a99cac1d..1a0f7c69cfe 100644 --- a/lib/libxo/tests/Makefile +++ b/lib/libxo/tests/Makefile @@ -242,8 +242,7 @@ PROGS+= test_11 CFLAGS+= -I${LIBXOSRC}/libxo -DPADD= ${LIBXO} ${LIBUTIL} -LDADD= -lxo -lutil +LIBADD= xo SUBDIR+= encoder diff --git a/lib/libxo/tests/encoder/Makefile b/lib/libxo/tests/encoder/Makefile index 2cd86b91785..2fe42457f2e 100644 --- a/lib/libxo/tests/encoder/Makefile +++ b/lib/libxo/tests/encoder/Makefile @@ -14,7 +14,6 @@ SRCS= enc_test.c CFLAGS+= -I${LIBXOSRC}/libxo -DPADD+= ${LIBXO} -LDADD+= -lxo +LIBADD= xo .include diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index 2e17fbf06b7..6012015bcc2 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -88,6 +88,8 @@ map_object(int fd, const char *path, const struct stat *sb) size_t relro_size; Elf_Addr note_start; Elf_Addr note_end; + char *note_map; + size_t note_map_len; hdr = get_elf_header(fd, path); if (hdr == NULL) @@ -108,6 +110,7 @@ map_object(int fd, const char *path, const struct stat *sb) relro_size = 0; note_start = 0; note_end = 0; + note_map = NULL; segs = alloca(sizeof(segs[0]) * hdr->e_phnum); stack_flags = RTLD_DEFAULT_STACK_PF_EXEC | PF_R | PF_W; while (phdr < phlimit) { @@ -150,9 +153,20 @@ map_object(int fd, const char *path, const struct stat *sb) case PT_NOTE: if (phdr->p_offset > PAGE_SIZE || - phdr->p_offset + phdr->p_filesz > PAGE_SIZE) - break; - note_start = (Elf_Addr)(char *)hdr + phdr->p_offset; + phdr->p_offset + phdr->p_filesz > PAGE_SIZE) { + note_map_len = round_page(phdr->p_offset + + phdr->p_filesz) - trunc_page(phdr->p_offset); + note_map = mmap(NULL, note_map_len, PROT_READ, + MAP_PRIVATE, fd, trunc_page(phdr->p_offset)); + if (note_map == MAP_FAILED) { + _rtld_error("%s: error mapping PT_NOTE (%d)", path, errno); + goto error; + } + note_start = (Elf_Addr)(note_map + phdr->p_offset - + trunc_page(phdr->p_offset)); + } else { + note_start = (Elf_Addr)(char *)hdr + phdr->p_offset; + } note_end = note_start + phdr->p_filesz; break; } @@ -295,12 +309,16 @@ map_object(int fd, const char *path, const struct stat *sb) obj->relro_size = round_page(relro_size); if (note_start < note_end) digest_notes(obj, note_start, note_end); + if (note_map != NULL) + munmap(note_map, note_map_len); munmap(hdr, PAGE_SIZE); return (obj); error1: munmap(mapbase, mapsize); error: + if (note_map != NULL && note_map != MAP_FAILED) + munmap(note_map, note_map_len); munmap(hdr, PAGE_SIZE); return (NULL); } diff --git a/release/Makefile b/release/Makefile index 1dbb5a77609..d9dbb4f80c2 100644 --- a/release/Makefile +++ b/release/Makefile @@ -300,7 +300,7 @@ release-install: ${XZ_CMD} -k ${DESTDIR}/${OSRELEASE}-${I} . endif .endfor + cd ${DESTDIR} && sha512 ${OSRELEASE}* > ${DESTDIR}/CHECKSUM.SHA512 cd ${DESTDIR} && sha256 ${OSRELEASE}* > ${DESTDIR}/CHECKSUM.SHA256 - cd ${DESTDIR} && md5 ${OSRELEASE}* > ${DESTDIR}/CHECKSUM.MD5 .include "${.CURDIR}/Makefile.vm" diff --git a/release/Makefile.mirrors b/release/Makefile.mirrors index d5e8949c383..92e5edabff1 100644 --- a/release/Makefile.mirrors +++ b/release/Makefile.mirrors @@ -76,7 +76,7 @@ VM_DIR= ${TLD}/VM-IMAGES/${REVISION}-${BRANCH}/${TARGET_ARCH} .endif CLEANFILES+= ${STAGE_TARGETS} -CHECKSUM_FILES?= SHA256 MD5 +CHECKSUM_FILES?= SHA512 SHA256 SNAP_SUFFIX!= echo ${_SNAP_SUFFIX:S,^-,,1} | tr -d ' ' ISO_DIR= ${TLD}/${TARGET}/${TARGET_ARCH}/ISO-IMAGES/${REVISION} FTP_DIR= ${TLD}/${TARGET}/${TARGET_ARCH}/${REVISION}-${BRANCH} diff --git a/release/Makefile.vm b/release/Makefile.vm index b39d0552076..902442fdfdd 100644 --- a/release/Makefile.vm +++ b/release/Makefile.vm @@ -68,10 +68,10 @@ cw${_CW:tl}-install: mkdir -p ${DESTDIR}/${_CW:tl} cp -p ${${_CW}IMAGE} \ ${DESTDIR}/${_CW:tl}/${${_CW}_DISK} + cd ${DESTDIR}/${_CW:tl} && sha512 ${${_CW}_DISK}* > \ + ${DESTDIR}/${_CW:tl}/CHECKSUM.SHA512 cd ${DESTDIR}/${_CW:tl} && sha256 ${${_CW}_DISK}* > \ ${DESTDIR}/${_CW:tl}/CHECKSUM.SHA256 - cd ${DESTDIR}/${_CW:tl} && md5 ${${_CW}_DISK}* > \ - ${DESTDIR}/${_CW:tl}/CHECKSUM.MD5 cw${_CW:tl}-package: @# Special target to handle packaging cloud images in the formats @@ -140,10 +140,10 @@ vm-install: ${XZ_CMD} ${DESTDIR}/vmimages/${OSRELEASE}.${FORMAT} . endfor . endif + cd ${DESTDIR}/vmimages && sha512 ${OSRELEASE}* > \ + ${DESTDIR}/vmimages/CHECKSUM.SHA512 cd ${DESTDIR}/vmimages && sha256 ${OSRELEASE}* > \ ${DESTDIR}/vmimages/CHECKSUM.SHA256 - cd ${DESTDIR}/vmimages && md5 ${OSRELEASE}* > \ - ${DESTDIR}/vmimages/CHECKSUM.MD5 .endif vm-release: diff --git a/release/release.sh b/release/release.sh index e1684220408..313a16c8bb5 100755 --- a/release/release.sh +++ b/release/release.sh @@ -362,10 +362,10 @@ chroot_arm_armv6_build_release() { chroot ${CHROOTDIR} cp -p ${OBJDIR}/${OSRELEASE}-${KERNEL}.img \ /R/${OSRELEASE}-${KERNEL}.img chroot ${CHROOTDIR} xz -T ${XZ_THREADS} /R/${OSRELEASE}-${KERNEL}.img + cd ${CHROOTDIR}/R && sha512 ${OSRELEASE}* \ + > CHECKSUM.SHA512 cd ${CHROOTDIR}/R && sha256 ${OSRELEASE}* \ > CHECKSUM.SHA256 - cd ${CHROOTDIR}/R && md5 ${OSRELEASE}* \ - > CHECKSUM.MD5 return 0 } # chroot_arm_armv6_build_release() diff --git a/rescue/rescue/Makefile b/rescue/rescue/Makefile index 8e682bd1f2e..45a6cc21165 100644 --- a/rescue/rescue/Makefile +++ b/rescue/rescue/Makefile @@ -221,5 +221,10 @@ CRUNCH_ALIAS_chown= chgrp ################################################################## CRUNCH_LIBS+= -lm +.if ${MK_ISCSI} != "no" +CRUNCH_PROGS_usr.bin+= iscsictl +CRUNCH_PROGS_usr.sbin+= iscsid +.endif + .include .include diff --git a/sbin/mount/mount.conf.8 b/sbin/mount/mount.conf.8 index 45b8257ebae..48af514dff6 100644 --- a/sbin/mount/mount.conf.8 +++ b/sbin/mount/mount.conf.8 @@ -26,7 +26,7 @@ .\" $FreeBSD$ .\" .\" -.Dd July 7, 2013 +.Dd October 17, 2013 .Dt MOUNT.CONF 8 .Os .Sh NAME @@ -154,7 +154,7 @@ will direct the kernel to try mounting the root file system first as an ISO CD9660 file system on .Pa /dev/cd0 , then if that does not work, as an ISO CD9660 file system on -.Pa /dev/acd0 , +.Pa /dev/cd1 , and then if that does not work, as a UFS file system on .Pa /dev/ada0s1a . If that does not work, a @@ -167,7 +167,7 @@ Finally if that does not work, the kernel will panic. .Li .timeout 3 cd9660:/dev/cd0 ro .Li .timeout 0 -cd9660:/dev/acd0 ro +cd9660:/dev/cd1 ro .Li .timeout 3 ufs:/dev/ada0s1a .Li .ask diff --git a/sbin/newfs_msdos/Makefile b/sbin/newfs_msdos/Makefile index 0a9ffb4340a..ec5e84878ae 100644 --- a/sbin/newfs_msdos/Makefile +++ b/sbin/newfs_msdos/Makefile @@ -2,6 +2,7 @@ PROG= newfs_msdos MAN= newfs_msdos.8 +SRCS= newfs_msdos.c mkfs_msdos.c # XXX - this is verboten .if ${MACHINE_CPUARCH} == "arm" diff --git a/sbin/newfs_msdos/mkfs_msdos.c b/sbin/newfs_msdos/mkfs_msdos.c new file mode 100644 index 00000000000..28280359974 --- /dev/null +++ b/sbin/newfs_msdos/mkfs_msdos.c @@ -0,0 +1,854 @@ +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mkfs_msdos.h" + +#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */ +#define BPN 4 /* bits per nibble */ +#define NPB 2 /* nibbles per byte */ + +#define DOSMAGIC 0xaa55 /* DOS magic number */ +#define MINBPS 512 /* minimum bytes per sector */ +#define MAXSPC 128 /* maximum sectors per cluster */ +#define MAXNFT 16 /* maximum number of FATs */ +#define DEFBLK 4096 /* default block size */ +#define DEFBLK16 2048 /* default block size FAT16 */ +#define DEFRDE 512 /* default root directory entries */ +#define RESFTE 2 /* reserved FAT entries */ +#define MINCLS12 1U /* minimum FAT12 clusters */ +#define MINCLS16 0xff5U /* minimum FAT16 clusters */ +#define MINCLS32 0xfff5U /* minimum FAT32 clusters */ +#define MAXCLS12 0xff4U /* maximum FAT12 clusters */ +#define MAXCLS16 0xfff4U /* maximum FAT16 clusters */ +#define MAXCLS32 0xffffff4U /* maximum FAT32 clusters */ + +#define mincls(fat) ((fat) == 12 ? MINCLS12 : \ + (fat) == 16 ? MINCLS16 : \ + MINCLS32) + +#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \ + (fat) == 16 ? MAXCLS16 : \ + MAXCLS32) + +#define mk1(p, x) \ + (p) = (u_int8_t)(x) + +#define mk2(p, x) \ + (p)[0] = (u_int8_t)(x), \ + (p)[1] = (u_int8_t)((x) >> 010) + +#define mk4(p, x) \ + (p)[0] = (u_int8_t)(x), \ + (p)[1] = (u_int8_t)((x) >> 010), \ + (p)[2] = (u_int8_t)((x) >> 020), \ + (p)[3] = (u_int8_t)((x) >> 030) + +struct bs { + u_int8_t bsJump[3]; /* bootstrap entry point */ + u_int8_t bsOemName[8]; /* OEM name and version */ +} __packed; + +struct bsbpb { + u_int8_t bpbBytesPerSec[2]; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int8_t bpbResSectors[2]; /* reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int8_t bpbRootDirEnts[2]; /* root directory entries */ + u_int8_t bpbSectors[2]; /* total sectors */ + u_int8_t bpbMedia; /* media descriptor */ + u_int8_t bpbFATsecs[2]; /* sectors per FAT */ + u_int8_t bpbSecPerTrack[2]; /* sectors per track */ + u_int8_t bpbHeads[2]; /* drive heads */ + u_int8_t bpbHiddenSecs[4]; /* hidden sectors */ + u_int8_t bpbHugeSectors[4]; /* big total sectors */ +} __packed; + +struct bsxbpb { + u_int8_t bpbBigFATsecs[4]; /* big sectors per FAT */ + u_int8_t bpbExtFlags[2]; /* FAT control flags */ + u_int8_t bpbFSVers[2]; /* file system version */ + u_int8_t bpbRootClust[4]; /* root directory start cluster */ + u_int8_t bpbFSInfo[2]; /* file system info sector */ + u_int8_t bpbBackup[2]; /* backup boot sector */ + u_int8_t bpbReserved[12]; /* reserved */ +} __packed; + +struct bsx { + u_int8_t exDriveNumber; /* drive number */ + u_int8_t exReserved1; /* reserved */ + u_int8_t exBootSignature; /* extended boot signature */ + u_int8_t exVolumeID[4]; /* volume ID number */ + u_int8_t exVolumeLabel[11]; /* volume label */ + u_int8_t exFileSysType[8]; /* file system type */ +} __packed; + +struct de { + u_int8_t deName[11]; /* name and extension */ + u_int8_t deAttributes; /* attributes */ + u_int8_t rsvd[10]; /* reserved */ + u_int8_t deMTime[2]; /* last-modified time */ + u_int8_t deMDate[2]; /* last-modified date */ + u_int8_t deStartCluster[2]; /* starting cluster */ + u_int8_t deFileSize[4]; /* size */ +} __packed; + +struct bpb { + u_int bpbBytesPerSec; /* bytes per sector */ + u_int bpbSecPerClust; /* sectors per cluster */ + u_int bpbResSectors; /* reserved sectors */ + u_int bpbFATs; /* number of FATs */ + u_int bpbRootDirEnts; /* root directory entries */ + u_int bpbSectors; /* total sectors */ + u_int bpbMedia; /* media descriptor */ + u_int bpbFATsecs; /* sectors per FAT */ + u_int bpbSecPerTrack; /* sectors per track */ + u_int bpbHeads; /* drive heads */ + u_int bpbHiddenSecs; /* hidden sectors */ + u_int bpbHugeSectors; /* big total sectors */ + u_int bpbBigFATsecs; /* big sectors per FAT */ + u_int bpbRootClust; /* root directory start cluster */ + u_int bpbFSInfo; /* file system info sector */ + u_int bpbBackup; /* backup boot sector */ +}; + +#define BPBGAP 0, 0, 0, 0, 0, 0 + +static struct { + const char *name; + struct bpb bpb; +} const stdfmt[] = { + {"160", {512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1, BPBGAP}}, + {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}}, + {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}}, + {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}}, + {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}}, + {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}}, + {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}}, + {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}}, + {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}}, + {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}} +}; + +static const u_int8_t bootcode[] = { + 0xfa, /* cli */ + 0x31, 0xc0, /* xor ax,ax */ + 0x8e, 0xd0, /* mov ss,ax */ + 0xbc, 0x00, 0x7c, /* mov sp,7c00h */ + 0xfb, /* sti */ + 0x8e, 0xd8, /* mov ds,ax */ + 0xe8, 0x00, 0x00, /* call $ + 3 */ + 0x5e, /* pop si */ + 0x83, 0xc6, 0x19, /* add si,+19h */ + 0xbb, 0x07, 0x00, /* mov bx,0007h */ + 0xfc, /* cld */ + 0xac, /* lodsb */ + 0x84, 0xc0, /* test al,al */ + 0x74, 0x06, /* jz $ + 8 */ + 0xb4, 0x0e, /* mov ah,0eh */ + 0xcd, 0x10, /* int 10h */ + 0xeb, 0xf5, /* jmp $ - 9 */ + 0x30, 0xe4, /* xor ah,ah */ + 0xcd, 0x16, /* int 16h */ + 0xcd, 0x19, /* int 19h */ + 0x0d, 0x0a, + 'N', 'o', 'n', '-', 's', 'y', 's', 't', + 'e', 'm', ' ', 'd', 'i', 's', 'k', + 0x0d, 0x0a, + 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', + 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o', + ' ', 'r', 'e', 'b', 'o', 'o', 't', + 0x0d, 0x0a, + 0 +}; + +static volatile sig_atomic_t got_siginfo; +static void infohandler(int); + +static void check_mounted(const char *, mode_t); +static void getstdfmt(const char *, struct bpb *); +static void getdiskinfo(int, const char *, const char *, int, + struct bpb *); +static void print_bpb(struct bpb *); +static u_int ckgeom(const char *, u_int, const char *); +static void mklabel(u_int8_t *, const char *); +static int oklabel(const char *); +static void setstr(u_int8_t *, const char *, size_t); + +int mkfs_msdos(const char *fname, const char *dtype, + const struct msdos_options *op) +{ + char buf[MAXPATHLEN]; + struct sigaction si_sa; + struct stat sb; + struct timeval tv; + struct bpb bpb; + struct tm *tm; + struct bs *bs; + struct bsbpb *bsbpb; + struct bsxbpb *bsxbpb; + struct bsx *bsx; + struct de *de; + u_int8_t *img; + const char *bname; + ssize_t n; + time_t now; + u_int fat, bss, rds, cls, dir, lsn, x, x1, x2; + int fd, fd1; + struct msdos_options o = *op; + + if (o.OEM_string && strlen(o.OEM_string) > 8) { + errx(1, "%s: bad OEM string", o.OEM_string); + } + if (o.create_size) { + if (o.no_create) + errx(1, "create (-C) is incompatible with -N"); + fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644); + if (fd == -1) + errx(1, "failed to create %s", fname); + if (ftruncate(fd, o.create_size)) + errx(1, "failed to initialize %jd bytes", (intmax_t)o.create_size); + } else if ((fd = open(fname, o.no_create ? O_RDONLY : O_RDWR)) == -1) + err(1, "%s", fname); + if (fstat(fd, &sb)) + err(1, "%s", fname); + if (o.create_size) { + if (!S_ISREG(sb.st_mode)) + warnx("warning, %s is not a regular file", fname); + } else { + if (!S_ISCHR(sb.st_mode)) + warnx("warning, %s is not a character device", fname); + } + if (!o.no_create) + check_mounted(fname, sb.st_mode); + if (o.offset && o.offset != lseek(fd, o.offset, SEEK_SET)) + errx(1, "cannot seek to %jd", (intmax_t)o.offset); + memset(&bpb, 0, sizeof(bpb)); + if (o.floppy) { + getstdfmt(o.floppy, &bpb); + bpb.bpbHugeSectors = bpb.bpbSectors; + bpb.bpbSectors = 0; + bpb.bpbBigFATsecs = bpb.bpbFATsecs; + bpb.bpbFATsecs = 0; + } + if (o.drive_heads) + bpb.bpbHeads = o.drive_heads; + if (o.sectors_per_track) + bpb.bpbSecPerTrack = o.sectors_per_track; + if (o.bytes_per_sector) + bpb.bpbBytesPerSec = o.bytes_per_sector; + if (o.size) + bpb.bpbHugeSectors = o.size; + if (o.hidden_sectors_set) + bpb.bpbHiddenSecs = o.hidden_sectors; + if (!(o.floppy || (o.drive_heads && o.sectors_per_track && o.bytes_per_sector && o.size && o.hidden_sectors_set))) { + off_t delta; + getdiskinfo(fd, fname, dtype, o.hidden_sectors_set, &bpb); + bpb.bpbHugeSectors -= (o.offset / bpb.bpbBytesPerSec); + delta = bpb.bpbHugeSectors % bpb.bpbSecPerTrack; + if (delta != 0) { + warnx("trim %d sectors to adjust to a multiple of %d", + (int)delta, bpb.bpbSecPerTrack); + bpb.bpbHugeSectors -= delta; + } + if (bpb.bpbSecPerClust == 0) { /* set defaults */ + if (bpb.bpbHugeSectors <= 6000) /* about 3MB -> 512 bytes */ + bpb.bpbSecPerClust = 1; + else if (bpb.bpbHugeSectors <= (1<<17)) /* 64M -> 4k */ + bpb.bpbSecPerClust = 8; + else if (bpb.bpbHugeSectors <= (1<<19)) /* 256M -> 8k */ + bpb.bpbSecPerClust = 16; + else if (bpb.bpbHugeSectors <= (1<<21)) /* 1G -> 16k */ + bpb.bpbSecPerClust = 32; + else + bpb.bpbSecPerClust = 64; /* otherwise 32k */ + } + } + if (!powerof2(bpb.bpbBytesPerSec)) + errx(1, "bytes/sector (%u) is not a power of 2", bpb.bpbBytesPerSec); + if (bpb.bpbBytesPerSec < MINBPS) + errx(1, "bytes/sector (%u) is too small; minimum is %u", + bpb.bpbBytesPerSec, MINBPS); + if (o.volume_label && !oklabel(o.volume_label)) + errx(1, "%s: bad volume label", o.volume_label); + if (!(fat = o.fat_type)) { + if (o.floppy) + fat = 12; + else if (!o.directory_entries && (o.info_sector || o.backup_sector)) + fat = 32; + } + if ((fat == 32 && o.directory_entries) || (fat != 32 && (o.info_sector || o.backup_sector))) + errx(1, "-%c is not a legal FAT%s option", + fat == 32 ? 'e' : o.info_sector ? 'i' : 'k', + fat == 32 ? "32" : "12/16"); + if (o.floppy && fat == 32) + bpb.bpbRootDirEnts = 0; + if (fat != 0 && fat != 12 && fat != 16 && fat != 32) { + errx(1, "%d: bad FAT type", fat); + } + + if (o.block_size) { + if (!powerof2(o.block_size)) + errx(1, "block size (%u) is not a power of 2", o.block_size); + if (o.block_size < bpb.bpbBytesPerSec) + errx(1, "block size (%u) is too small; minimum is %u", + o.block_size, bpb.bpbBytesPerSec); + if (o.block_size > bpb.bpbBytesPerSec * MAXSPC) + errx(1, "block size (%u) is too large; maximum is %u", + o.block_size, bpb.bpbBytesPerSec * MAXSPC); + bpb.bpbSecPerClust = o.block_size / bpb.bpbBytesPerSec; + } + if (o.sectors_per_cluster) { + if (!powerof2(o.sectors_per_cluster)) + errx(1, "sectors/cluster (%u) is not a power of 2", o.sectors_per_cluster); + bpb.bpbSecPerClust = o.sectors_per_cluster; + } + if (o.reserved_sectors) + bpb.bpbResSectors = o.reserved_sectors; + if (o.num_FAT) { + if (o.num_FAT > MAXNFT) + errx(1, "number of FATs (%u) is too large; maximum is %u", + o.num_FAT, MAXNFT); + bpb.bpbFATs = o.num_FAT; + } + if (o.directory_entries) + bpb.bpbRootDirEnts = o.directory_entries; + if (o.media_descriptor_set) { + if (o.media_descriptor < 0xf0) + errx(1, "illegal media descriptor (%#x)", o.media_descriptor); + bpb.bpbMedia = o.media_descriptor; + } + if (o.sectors_per_fat) + bpb.bpbBigFATsecs = o.sectors_per_fat; + if (o.info_sector) + bpb.bpbFSInfo = o.info_sector; + if (o.backup_sector) + bpb.bpbBackup = o.backup_sector; + bss = 1; + bname = NULL; + fd1 = -1; + if (o.bootstrap) { + bname = o.bootstrap; + if (!strchr(bname, '/')) { + snprintf(buf, sizeof(buf), "/boot/%s", bname); + if (!(bname = strdup(buf))) + err(1, NULL); + } + if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) + err(1, "%s", bname); + if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bpbBytesPerSec || + sb.st_size < bpb.bpbBytesPerSec || + sb.st_size > bpb.bpbBytesPerSec * MAXU16) + errx(1, "%s: inappropriate file type or format", bname); + bss = sb.st_size / bpb.bpbBytesPerSec; + } + if (!bpb.bpbFATs) + bpb.bpbFATs = 2; + if (!fat) { + if (bpb.bpbHugeSectors < (bpb.bpbResSectors ? bpb.bpbResSectors : bss) + + howmany((RESFTE + (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1)) * + (bpb.bpbSecPerClust ? 16 : 12) / BPN, + bpb.bpbBytesPerSec * NPB) * + bpb.bpbFATs + + howmany(bpb.bpbRootDirEnts ? bpb.bpbRootDirEnts : DEFRDE, + bpb.bpbBytesPerSec / sizeof(struct de)) + + (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1) * + (bpb.bpbSecPerClust ? bpb.bpbSecPerClust : + howmany(DEFBLK, bpb.bpbBytesPerSec))) + fat = 12; + else if (bpb.bpbRootDirEnts || bpb.bpbHugeSectors < + (bpb.bpbResSectors ? bpb.bpbResSectors : bss) + + howmany((RESFTE + MAXCLS16) * 2, bpb.bpbBytesPerSec) * + bpb.bpbFATs + + howmany(DEFRDE, bpb.bpbBytesPerSec / sizeof(struct de)) + + (MAXCLS16 + 1) * + (bpb.bpbSecPerClust ? bpb.bpbSecPerClust : + howmany(8192, bpb.bpbBytesPerSec))) + fat = 16; + else + fat = 32; + } + x = bss; + if (fat == 32) { + if (!bpb.bpbFSInfo) { + if (x == MAXU16 || x == bpb.bpbBackup) + errx(1, "no room for info sector"); + bpb.bpbFSInfo = x; + } + if (bpb.bpbFSInfo != MAXU16 && x <= bpb.bpbFSInfo) + x = bpb.bpbFSInfo + 1; + if (!bpb.bpbBackup) { + if (x == MAXU16) + errx(1, "no room for backup sector"); + bpb.bpbBackup = x; + } else if (bpb.bpbBackup != MAXU16 && bpb.bpbBackup == bpb.bpbFSInfo) + errx(1, "backup sector would overwrite info sector"); + if (bpb.bpbBackup != MAXU16 && x <= bpb.bpbBackup) + x = bpb.bpbBackup + 1; + } + if (!bpb.bpbResSectors) + bpb.bpbResSectors = fat == 32 ? + MAX(x, MAX(16384 / bpb.bpbBytesPerSec, 4)) : x; + else if (bpb.bpbResSectors < x) + errx(1, "too few reserved sectors (need %d have %d)", x, + bpb.bpbResSectors); + if (fat != 32 && !bpb.bpbRootDirEnts) + bpb.bpbRootDirEnts = DEFRDE; + rds = howmany(bpb.bpbRootDirEnts, bpb.bpbBytesPerSec / sizeof(struct de)); + if (!bpb.bpbSecPerClust) + for (bpb.bpbSecPerClust = howmany(fat == 16 ? DEFBLK16 : + DEFBLK, bpb.bpbBytesPerSec); + bpb.bpbSecPerClust < MAXSPC && + bpb.bpbResSectors + + howmany((RESFTE + maxcls(fat)) * (fat / BPN), + bpb.bpbBytesPerSec * NPB) * + bpb.bpbFATs + + rds + + (u_int64_t) (maxcls(fat) + 1) * + bpb.bpbSecPerClust <= bpb.bpbHugeSectors; + bpb.bpbSecPerClust <<= 1) + continue; + if (fat != 32 && bpb.bpbBigFATsecs > MAXU16) + errx(1, "too many sectors/FAT for FAT12/16"); + x1 = bpb.bpbResSectors + rds; + x = bpb.bpbBigFATsecs ? bpb.bpbBigFATsecs : 1; + if (x1 + (u_int64_t)x * bpb.bpbFATs > bpb.bpbHugeSectors) + errx(1, "meta data exceeds file system size"); + x1 += x * bpb.bpbFATs; + x = (u_int64_t)(bpb.bpbHugeSectors - x1) * bpb.bpbBytesPerSec * NPB / + (bpb.bpbSecPerClust * bpb.bpbBytesPerSec * NPB + fat / + BPN * bpb.bpbFATs); + x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), + bpb.bpbBytesPerSec * NPB); + if (!bpb.bpbBigFATsecs) { + bpb.bpbBigFATsecs = x2; + x1 += (bpb.bpbBigFATsecs - 1) * bpb.bpbFATs; + } + cls = (bpb.bpbHugeSectors - x1) / bpb.bpbSecPerClust; + x = (u_int64_t)bpb.bpbBigFATsecs * bpb.bpbBytesPerSec * NPB / (fat / BPN) - + RESFTE; + if (cls > x) + cls = x; + if (bpb.bpbBigFATsecs < x2) + warnx("warning: sectors/FAT limits file system to %u clusters", + cls); + if (cls < mincls(fat)) + errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, + mincls(fat)); + if (cls > maxcls(fat)) { + cls = maxcls(fat); + bpb.bpbHugeSectors = x1 + (cls + 1) * bpb.bpbSecPerClust - 1; + warnx("warning: FAT type limits file system to %u sectors", + bpb.bpbHugeSectors); + } + printf("%s: %u sector%s in %u FAT%u cluster%s " + "(%u bytes/cluster)\n", fname, cls * bpb.bpbSecPerClust, + cls * bpb.bpbSecPerClust == 1 ? "" : "s", cls, fat, + cls == 1 ? "" : "s", bpb.bpbBytesPerSec * bpb.bpbSecPerClust); + if (!bpb.bpbMedia) + bpb.bpbMedia = !bpb.bpbHiddenSecs ? 0xf0 : 0xf8; + if (fat == 32) + bpb.bpbRootClust = RESFTE; + if (bpb.bpbHiddenSecs + bpb.bpbHugeSectors <= MAXU16) { + bpb.bpbSectors = bpb.bpbHugeSectors; + bpb.bpbHugeSectors = 0; + } + if (fat != 32) { + bpb.bpbFATsecs = bpb.bpbBigFATsecs; + bpb.bpbBigFATsecs = 0; + } + print_bpb(&bpb); + if (!o.no_create) { + gettimeofday(&tv, NULL); + now = tv.tv_sec; + tm = localtime(&now); + if (!(img = malloc(bpb.bpbBytesPerSec))) + err(1, NULL); + dir = bpb.bpbResSectors + (bpb.bpbFATsecs ? bpb.bpbFATsecs : + bpb.bpbBigFATsecs) * bpb.bpbFATs; + memset(&si_sa, 0, sizeof(si_sa)); + si_sa.sa_handler = infohandler; + if (sigaction(SIGINFO, &si_sa, NULL) == -1) + err(1, "sigaction SIGINFO"); + for (lsn = 0; lsn < dir + (fat == 32 ? bpb.bpbSecPerClust : rds); lsn++) { + if (got_siginfo) { + fprintf(stderr,"%s: writing sector %u of %u (%u%%)\n", + fname, lsn, + (dir + (fat == 32 ? bpb.bpbSecPerClust: rds)), + (lsn * 100) / (dir + + (fat == 32 ? bpb.bpbSecPerClust: rds))); + got_siginfo = 0; + } + x = lsn; + if (o.bootstrap && + fat == 32 && bpb.bpbBackup != MAXU16 && + bss <= bpb.bpbBackup && x >= bpb.bpbBackup) { + x -= bpb.bpbBackup; + if (!x && lseek(fd1, o.offset, SEEK_SET)) + err(1, "%s", bname); + } + if (o.bootstrap && x < bss) { + if ((n = read(fd1, img, bpb.bpbBytesPerSec)) == -1) + err(1, "%s", bname); + if ((unsigned)n != bpb.bpbBytesPerSec) + errx(1, "%s: can't read sector %u", bname, x); + } else + memset(img, 0, bpb.bpbBytesPerSec); + if (!lsn || + (fat == 32 && bpb.bpbBackup != MAXU16 && + lsn == bpb.bpbBackup)) { + x1 = sizeof(struct bs); + bsbpb = (struct bsbpb *)(img + x1); + mk2(bsbpb->bpbBytesPerSec, bpb.bpbBytesPerSec); + mk1(bsbpb->bpbSecPerClust, bpb.bpbSecPerClust); + mk2(bsbpb->bpbResSectors, bpb.bpbResSectors); + mk1(bsbpb->bpbFATs, bpb.bpbFATs); + mk2(bsbpb->bpbRootDirEnts, bpb.bpbRootDirEnts); + mk2(bsbpb->bpbSectors, bpb.bpbSectors); + mk1(bsbpb->bpbMedia, bpb.bpbMedia); + mk2(bsbpb->bpbFATsecs, bpb.bpbFATsecs); + mk2(bsbpb->bpbSecPerTrack, bpb.bpbSecPerTrack); + mk2(bsbpb->bpbHeads, bpb.bpbHeads); + mk4(bsbpb->bpbHiddenSecs, bpb.bpbHiddenSecs); + mk4(bsbpb->bpbHugeSectors, bpb.bpbHugeSectors); + x1 += sizeof(struct bsbpb); + if (fat == 32) { + bsxbpb = (struct bsxbpb *)(img + x1); + mk4(bsxbpb->bpbBigFATsecs, bpb.bpbBigFATsecs); + mk2(bsxbpb->bpbExtFlags, 0); + mk2(bsxbpb->bpbFSVers, 0); + mk4(bsxbpb->bpbRootClust, bpb.bpbRootClust); + mk2(bsxbpb->bpbFSInfo, bpb.bpbFSInfo); + mk2(bsxbpb->bpbBackup, bpb.bpbBackup); + x1 += sizeof(struct bsxbpb); + } + bsx = (struct bsx *)(img + x1); + mk1(bsx->exBootSignature, 0x29); + if (o.volume_id_set) + x = o.volume_id; + else + x = (((u_int)(1 + tm->tm_mon) << 8 | + (u_int)tm->tm_mday) + + ((u_int)tm->tm_sec << 8 | + (u_int)(tv.tv_usec / 10))) << 16 | + ((u_int)(1900 + tm->tm_year) + + ((u_int)tm->tm_hour << 8 | + (u_int)tm->tm_min)); + mk4(bsx->exVolumeID, x); + mklabel(bsx->exVolumeLabel, o.volume_label ? o.volume_label : "NO NAME"); + snprintf(buf, sizeof(buf), "FAT%u", fat); + setstr(bsx->exFileSysType, buf, sizeof(bsx->exFileSysType)); + if (!o.bootstrap) { + x1 += sizeof(struct bsx); + bs = (struct bs *)img; + mk1(bs->bsJump[0], 0xeb); + mk1(bs->bsJump[1], x1 - 2); + mk1(bs->bsJump[2], 0x90); + setstr(bs->bsOemName, o.OEM_string ? o.OEM_string : "BSD4.4 ", + sizeof(bs->bsOemName)); + memcpy(img + x1, bootcode, sizeof(bootcode)); + mk2(img + MINBPS - 2, DOSMAGIC); + } + } else if (fat == 32 && bpb.bpbFSInfo != MAXU16 && + (lsn == bpb.bpbFSInfo || + (bpb.bpbBackup != MAXU16 && + lsn == bpb.bpbBackup + bpb.bpbFSInfo))) { + mk4(img, 0x41615252); + mk4(img + MINBPS - 28, 0x61417272); + mk4(img + MINBPS - 24, 0xffffffff); + mk4(img + MINBPS - 20, bpb.bpbRootClust); + mk2(img + MINBPS - 2, DOSMAGIC); + } else if (lsn >= bpb.bpbResSectors && lsn < dir && + !((lsn - bpb.bpbResSectors) % + (bpb.bpbFATsecs ? bpb.bpbFATsecs : + bpb.bpbBigFATsecs))) { + mk1(img[0], bpb.bpbMedia); + for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++) + mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff); + } else if (lsn == dir && o.volume_label) { + de = (struct de *)img; + mklabel(de->deName, o.volume_label); + mk1(de->deAttributes, 050); + x = (u_int)tm->tm_hour << 11 | + (u_int)tm->tm_min << 5 | + (u_int)tm->tm_sec >> 1; + mk2(de->deMTime, x); + x = (u_int)(tm->tm_year - 80) << 9 | + (u_int)(tm->tm_mon + 1) << 5 | + (u_int)tm->tm_mday; + mk2(de->deMDate, x); + } + if ((n = write(fd, img, bpb.bpbBytesPerSec)) == -1) + err(1, "%s", fname); + if ((unsigned)n != bpb.bpbBytesPerSec) + errx(1, "%s: can't write sector %u", fname, lsn); + } + } + return 0; +} + +/* + * Exit with error if file system is mounted. + */ +static void +check_mounted(const char *fname, mode_t mode) +{ + struct statfs *mp; + const char *s1, *s2; + size_t len; + int n, r; + + if (!(n = getmntinfo(&mp, MNT_NOWAIT))) + err(1, "getmntinfo"); + len = strlen(_PATH_DEV); + s1 = fname; + if (!strncmp(s1, _PATH_DEV, len)) + s1 += len; + r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; + for (; n--; mp++) { + s2 = mp->f_mntfromname; + if (!strncmp(s2, _PATH_DEV, len)) + s2 += len; + if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || + !strcmp(s1, s2)) + errx(1, "%s is mounted on %s", fname, mp->f_mntonname); + } +} + +/* + * Get a standard format. + */ +static void +getstdfmt(const char *fmt, struct bpb *bpb) +{ + u_int x, i; + + x = sizeof(stdfmt) / sizeof(stdfmt[0]); + for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++); + if (i == x) + errx(1, "%s: unknown standard format", fmt); + *bpb = stdfmt[i].bpb; +} + +/* + * Get disk slice, partition, and geometry information. + */ +static void +getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag, + struct bpb *bpb) +{ + struct disklabel *lp, dlp; + struct fd_type type; + off_t ms, hs = 0; + + lp = NULL; + + /* If the user specified a disk type, try to use that */ + if (dtype != NULL) { + lp = getdiskbyname(dtype); + } + + /* Maybe it's a floppy drive */ + if (lp == NULL) { + if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) { + struct stat st; + + if (fstat(fd, &st)) + err(1, "cannot get disk size"); + /* create a fake geometry for a file image */ + ms = st.st_size; + dlp.d_secsize = 512; + dlp.d_nsectors = 63; + dlp.d_ntracks = 255; + dlp.d_secperunit = ms / dlp.d_secsize; + lp = &dlp; + } else if (ioctl(fd, FD_GTYPE, &type) != -1) { + dlp.d_secsize = 128 << type.secsize; + dlp.d_nsectors = type.sectrac; + dlp.d_ntracks = type.heads; + dlp.d_secperunit = ms / dlp.d_secsize; + lp = &dlp; + } + } + + /* Maybe it's a fixed drive */ + if (lp == NULL) { + if (bpb->bpbBytesPerSec) + dlp.d_secsize = bpb->bpbBytesPerSec; + if (bpb->bpbBytesPerSec == 0 && ioctl(fd, DIOCGSECTORSIZE, + &dlp.d_secsize) == -1) + err(1, "cannot get sector size"); + + dlp.d_secperunit = ms / dlp.d_secsize; + + if (bpb->bpbSecPerTrack == 0 && ioctl(fd, DIOCGFWSECTORS, + &dlp.d_nsectors) == -1) { + warn("cannot get number of sectors per track"); + dlp.d_nsectors = 63; + } + if (bpb->bpbHeads == 0 && + ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) { + warn("cannot get number of heads"); + if (dlp.d_secperunit <= 63*1*1024) + dlp.d_ntracks = 1; + else if (dlp.d_secperunit <= 63*16*1024) + dlp.d_ntracks = 16; + else + dlp.d_ntracks = 255; + } + + hs = (ms / dlp.d_secsize) - dlp.d_secperunit; + lp = &dlp; + } + + if (bpb->bpbBytesPerSec == 0) + bpb->bpbBytesPerSec = ckgeom(fname, lp->d_secsize, "bytes/sector"); + if (bpb->bpbSecPerTrack == 0) + bpb->bpbSecPerTrack = ckgeom(fname, lp->d_nsectors, "sectors/track"); + if (bpb->bpbHeads == 0) + bpb->bpbHeads = ckgeom(fname, lp->d_ntracks, "drive heads"); + if (bpb->bpbHugeSectors == 0) + bpb->bpbHugeSectors = lp->d_secperunit; + if (bpb->bpbHiddenSecs == 0) + bpb->bpbHiddenSecs = hs; +} + +/* + * Print out BPB values. + */ +static void +print_bpb(struct bpb *bpb) +{ + printf("BytesPerSec=%u SecPerClust=%u ResSectors=%u FATs=%u", + bpb->bpbBytesPerSec, bpb->bpbSecPerClust, bpb->bpbResSectors, + bpb->bpbFATs); + if (bpb->bpbRootDirEnts) + printf(" RootDirEnts=%u", bpb->bpbRootDirEnts); + if (bpb->bpbSectors) + printf(" Sectors=%u", bpb->bpbSectors); + printf(" Media=%#x", bpb->bpbMedia); + if (bpb->bpbFATsecs) + printf(" FATsecs=%u", bpb->bpbFATsecs); + printf(" SecPerTrack=%u Heads=%u HiddenSecs=%u", bpb->bpbSecPerTrack, + bpb->bpbHeads, bpb->bpbHiddenSecs); + if (bpb->bpbHugeSectors) + printf(" HugeSectors=%u", bpb->bpbHugeSectors); + if (!bpb->bpbFATsecs) { + printf(" FATsecs=%u RootCluster=%u", bpb->bpbBigFATsecs, + bpb->bpbRootClust); + printf(" FSInfo="); + printf(bpb->bpbFSInfo == MAXU16 ? "%#x" : "%u", bpb->bpbFSInfo); + printf(" Backup="); + printf(bpb->bpbBackup == MAXU16 ? "%#x" : "%u", bpb->bpbBackup); + } + printf("\n"); +} + +/* + * Check a disk geometry value. + */ +static u_int +ckgeom(const char *fname, u_int val, const char *msg) +{ + if (!val) + errx(1, "%s: no default %s", fname, msg); + if (val > MAXU16) + errx(1, "%s: illegal %s %d", fname, msg, val); + return val; +} + +/* + * Check a volume label. + */ +static int +oklabel(const char *src) +{ + int c, i; + + for (i = 0; i <= 11; i++) { + c = (u_char)*src++; + if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c)) + break; + } + return i && !c; +} + +/* + * Make a volume label. + */ +static void +mklabel(u_int8_t *dest, const char *src) +{ + int c, i; + + for (i = 0; i < 11; i++) { + c = *src ? toupper(*src++) : ' '; + *dest++ = !i && c == '\xe5' ? 5 : c; + } +} + +/* + * Copy string, padding with spaces. + */ +static void +setstr(u_int8_t *dest, const char *src, size_t len) +{ + while (len--) + *dest++ = *src ? *src++ : ' '; +} + +static void +infohandler(int sig __unused) +{ + + got_siginfo = 1; +} diff --git a/sbin/newfs_msdos/mkfs_msdos.h b/sbin/newfs_msdos/mkfs_msdos.h new file mode 100644 index 00000000000..e9529cee8e1 --- /dev/null +++ b/sbin/newfs_msdos/mkfs_msdos.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2015 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Ed Maste under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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$ + */ + +struct msdos_options { + const char *bootstrap; + const char *volume_label; + const char *OEM_string; + const char *floppy; + u_int fat_type; + u_int volume_id; + u_int bytes_per_sector; + u_int sectors_per_fat; + u_int block_size; + u_int sectors_per_cluster; + u_int directory_entries; + u_int drive_heads; + u_int info_sector; + u_int backup_sector; + u_int media_descriptor; + u_int num_FAT; + u_int hidden_sectors; + u_int reserved_sectors; + u_int size; + u_int sectors_per_track; + int no_create; + off_t create_size; + off_t offset; + int volume_id_set; + int media_descriptor_set; + int hidden_sectors_set; +}; + +int mkfs_msdos(const char *, const char *, const struct msdos_options *); diff --git a/sbin/newfs_msdos/newfs_msdos.c b/sbin/newfs_msdos/newfs_msdos.c index 46857828767..79529f28420 100644 --- a/sbin/newfs_msdos/newfs_msdos.c +++ b/sbin/newfs_msdos/newfs_msdos.c @@ -31,203 +31,24 @@ static const char rcsid[] = #endif /* not lint */ #include -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include #include -#include #include #include #include -#include #include -#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */ -#define BPN 4 /* bits per nibble */ -#define NPB 2 /* nibbles per byte */ - -#define DOSMAGIC 0xaa55 /* DOS magic number */ -#define MINBPS 512 /* minimum bytes per sector */ -#define MAXSPC 128 /* maximum sectors per cluster */ -#define MAXNFT 16 /* maximum number of FATs */ -#define DEFBLK 4096 /* default block size */ -#define DEFBLK16 2048 /* default block size FAT16 */ -#define DEFRDE 512 /* default root directory entries */ -#define RESFTE 2 /* reserved FAT entries */ -#define MINCLS12 1U /* minimum FAT12 clusters */ -#define MINCLS16 0xff5U /* minimum FAT16 clusters */ -#define MINCLS32 0xfff5U /* minimum FAT32 clusters */ -#define MAXCLS12 0xff4U /* maximum FAT12 clusters */ -#define MAXCLS16 0xfff4U /* maximum FAT16 clusters */ -#define MAXCLS32 0xffffff4U /* maximum FAT32 clusters */ - -#define mincls(fat) ((fat) == 12 ? MINCLS12 : \ - (fat) == 16 ? MINCLS16 : \ - MINCLS32) - -#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \ - (fat) == 16 ? MAXCLS16 : \ - MAXCLS32) - -#define mk1(p, x) \ - (p) = (u_int8_t)(x) - -#define mk2(p, x) \ - (p)[0] = (u_int8_t)(x), \ - (p)[1] = (u_int8_t)((x) >> 010) - -#define mk4(p, x) \ - (p)[0] = (u_int8_t)(x), \ - (p)[1] = (u_int8_t)((x) >> 010), \ - (p)[2] = (u_int8_t)((x) >> 020), \ - (p)[3] = (u_int8_t)((x) >> 030) +#include "mkfs_msdos.h" #define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg) #define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg) #define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg) #define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg) -struct bs { - u_int8_t bsJump[3]; /* bootstrap entry point */ - u_int8_t bsOemName[8]; /* OEM name and version */ -} __packed; - -struct bsbpb { - u_int8_t bpbBytesPerSec[2]; /* bytes per sector */ - u_int8_t bpbSecPerClust; /* sectors per cluster */ - u_int8_t bpbResSectors[2]; /* reserved sectors */ - u_int8_t bpbFATs; /* number of FATs */ - u_int8_t bpbRootDirEnts[2]; /* root directory entries */ - u_int8_t bpbSectors[2]; /* total sectors */ - u_int8_t bpbMedia; /* media descriptor */ - u_int8_t bpbFATsecs[2]; /* sectors per FAT */ - u_int8_t bpbSecPerTrack[2]; /* sectors per track */ - u_int8_t bpbHeads[2]; /* drive heads */ - u_int8_t bpbHiddenSecs[4]; /* hidden sectors */ - u_int8_t bpbHugeSectors[4]; /* big total sectors */ -} __packed; - -struct bsxbpb { - u_int8_t bpbBigFATsecs[4]; /* big sectors per FAT */ - u_int8_t bpbExtFlags[2]; /* FAT control flags */ - u_int8_t bpbFSVers[2]; /* file system version */ - u_int8_t bpbRootClust[4]; /* root directory start cluster */ - u_int8_t bpbFSInfo[2]; /* file system info sector */ - u_int8_t bpbBackup[2]; /* backup boot sector */ - u_int8_t bpbReserved[12]; /* reserved */ -} __packed; - -struct bsx { - u_int8_t exDriveNumber; /* drive number */ - u_int8_t exReserved1; /* reserved */ - u_int8_t exBootSignature; /* extended boot signature */ - u_int8_t exVolumeID[4]; /* volume ID number */ - u_int8_t exVolumeLabel[11]; /* volume label */ - u_int8_t exFileSysType[8]; /* file system type */ -} __packed; - -struct de { - u_int8_t deName[11]; /* name and extension */ - u_int8_t deAttributes; /* attributes */ - u_int8_t rsvd[10]; /* reserved */ - u_int8_t deMTime[2]; /* last-modified time */ - u_int8_t deMDate[2]; /* last-modified date */ - u_int8_t deStartCluster[2]; /* starting cluster */ - u_int8_t deFileSize[4]; /* size */ -} __packed; - -struct bpb { - u_int bpbBytesPerSec; /* bytes per sector */ - u_int bpbSecPerClust; /* sectors per cluster */ - u_int bpbResSectors; /* reserved sectors */ - u_int bpbFATs; /* number of FATs */ - u_int bpbRootDirEnts; /* root directory entries */ - u_int bpbSectors; /* total sectors */ - u_int bpbMedia; /* media descriptor */ - u_int bpbFATsecs; /* sectors per FAT */ - u_int bpbSecPerTrack; /* sectors per track */ - u_int bpbHeads; /* drive heads */ - u_int bpbHiddenSecs; /* hidden sectors */ - u_int bpbHugeSectors; /* big total sectors */ - u_int bpbBigFATsecs; /* big sectors per FAT */ - u_int bpbRootClust; /* root directory start cluster */ - u_int bpbFSInfo; /* file system info sector */ - u_int bpbBackup; /* backup boot sector */ -}; - -#define BPBGAP 0, 0, 0, 0, 0, 0 - -static struct { - const char *name; - struct bpb bpb; -} const stdfmt[] = { - {"160", {512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1, BPBGAP}}, - {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}}, - {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}}, - {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}}, - {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}}, - {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}}, - {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}}, - {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}}, - {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}}, - {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}} -}; - -static const u_int8_t bootcode[] = { - 0xfa, /* cli */ - 0x31, 0xc0, /* xor ax,ax */ - 0x8e, 0xd0, /* mov ss,ax */ - 0xbc, 0x00, 0x7c, /* mov sp,7c00h */ - 0xfb, /* sti */ - 0x8e, 0xd8, /* mov ds,ax */ - 0xe8, 0x00, 0x00, /* call $ + 3 */ - 0x5e, /* pop si */ - 0x83, 0xc6, 0x19, /* add si,+19h */ - 0xbb, 0x07, 0x00, /* mov bx,0007h */ - 0xfc, /* cld */ - 0xac, /* lodsb */ - 0x84, 0xc0, /* test al,al */ - 0x74, 0x06, /* jz $ + 8 */ - 0xb4, 0x0e, /* mov ah,0eh */ - 0xcd, 0x10, /* int 10h */ - 0xeb, 0xf5, /* jmp $ - 9 */ - 0x30, 0xe4, /* xor ah,ah */ - 0xcd, 0x16, /* int 16h */ - 0xcd, 0x19, /* int 19h */ - 0x0d, 0x0a, - 'N', 'o', 'n', '-', 's', 'y', 's', 't', - 'e', 'm', ' ', 'd', 'i', 's', 'k', - 0x0d, 0x0a, - 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', - 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o', - ' ', 'r', 'e', 'b', 'o', 'o', 't', - 0x0d, 0x0a, - 0 -}; - -static volatile sig_atomic_t got_siginfo; -static void infohandler(int); - -static void check_mounted(const char *, mode_t); -static void getstdfmt(const char *, struct bpb *); -static void getdiskinfo(int, const char *, const char *, int, - struct bpb *); -static void print_bpb(struct bpb *); -static u_int ckgeom(const char *, u_int, const char *); static u_int argtou(const char *, u_int, u_int, const char *); static off_t argtooff(const char *, const char *); -static int oklabel(const char *); -static void mklabel(u_int8_t *, const char *); -static void setstr(u_int8_t *, const char *, size_t); static void usage(void); /* @@ -237,114 +58,92 @@ int main(int argc, char *argv[]) { static const char opts[] = "@:NB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:"; - const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL; - u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0; - u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0; - u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0; - int opt_N = 0; - int Iflag = 0, mflag = 0, oflag = 0; + struct msdos_options o; + const char *fname, *dtype; char buf[MAXPATHLEN]; - struct sigaction si_sa; - struct stat sb; - struct timeval tv; - struct bpb bpb; - struct tm *tm; - struct bs *bs; - struct bsbpb *bsbpb; - struct bsxbpb *bsxbpb; - struct bsx *bsx; - struct de *de; - u_int8_t *img; - const char *fname, *dtype, *bname; - ssize_t n; - time_t now; - u_int fat, bss, rds, cls, dir, lsn, x, x1, x2; - int ch, fd, fd1; - off_t opt_create = 0, opt_ofs = 0; + int ch; + + memset(&o, 0, sizeof(o)); while ((ch = getopt(argc, argv, opts)) != -1) switch (ch) { case '@': - opt_ofs = argtooff(optarg, "offset"); + o.offset = argtooff(optarg, "offset"); break; case 'N': - opt_N = 1; + o.no_create = 1; break; case 'B': - opt_B = optarg; + o.bootstrap = optarg; break; case 'C': - opt_create = argtooff(optarg, "create size"); + o.create_size = argtooff(optarg, "create size"); break; case 'F': if (strcmp(optarg, "12") && strcmp(optarg, "16") && strcmp(optarg, "32")) errx(1, "%s: bad FAT type", optarg); - opt_F = atoi(optarg); + o.fat_type = atoi(optarg); break; case 'I': - opt_I = argto4(optarg, 0, "volume ID"); - Iflag = 1; + o.volume_id = argto4(optarg, 0, "volume ID"); + o.volume_id_set = 1; break; case 'L': - if (!oklabel(optarg)) - errx(1, "%s: bad volume label", optarg); - opt_L = optarg; + o.volume_label = optarg; break; case 'O': - if (strlen(optarg) > 8) - errx(1, "%s: bad OEM string", optarg); - opt_O = optarg; + o.OEM_string = optarg; break; case 'S': - opt_S = argto2(optarg, 1, "bytes/sector"); + o.bytes_per_sector = argto2(optarg, 1, "bytes/sector"); break; case 'a': - opt_a = argto4(optarg, 1, "sectors/FAT"); + o.sectors_per_fat = argto4(optarg, 1, "sectors/FAT"); break; case 'b': - opt_b = argtox(optarg, 1, "block size"); - opt_c = 0; + o.block_size = argtox(optarg, 1, "block size"); + o.sectors_per_cluster = 0; break; case 'c': - opt_c = argto1(optarg, 1, "sectors/cluster"); - opt_b = 0; + o.sectors_per_cluster = argto1(optarg, 1, "sectors/cluster"); + o.block_size = 0; break; case 'e': - opt_e = argto2(optarg, 1, "directory entries"); + o.directory_entries = argto2(optarg, 1, "directory entries"); break; case 'f': - opt_f = optarg; + o.floppy = optarg; break; case 'h': - opt_h = argto2(optarg, 1, "drive heads"); + o.drive_heads = argto2(optarg, 1, "drive heads"); break; case 'i': - opt_i = argto2(optarg, 1, "info sector"); + o.info_sector = argto2(optarg, 1, "info sector"); break; case 'k': - opt_k = argto2(optarg, 1, "backup sector"); + o.backup_sector = argto2(optarg, 1, "backup sector"); break; case 'm': - opt_m = argto1(optarg, 0, "media descriptor"); - mflag = 1; + o.media_descriptor = argto1(optarg, 0, "media descriptor"); + o.media_descriptor_set = 1; break; case 'n': - opt_n = argto1(optarg, 1, "number of FATs"); + o.num_FAT = argto1(optarg, 1, "number of FATs"); break; case 'o': - opt_o = argto4(optarg, 0, "hidden sectors"); - oflag = 1; + o.hidden_sectors = argto4(optarg, 0, "hidden sectors"); + o.hidden_sectors_set = 1; break; case 'r': - opt_r = argto2(optarg, 1, "reserved sectors"); + o.reserved_sectors = argto2(optarg, 1, "reserved sectors"); break; case 's': - opt_s = argto4(optarg, 1, "file system size"); + o.size = argto4(optarg, 1, "file system size"); break; case 'u': - opt_u = argto2(optarg, 1, "sectors/track"); + o.sectors_per_track = argto2(optarg, 1, "sectors/track"); break; default: usage(); @@ -354,561 +153,13 @@ main(int argc, char *argv[]) if (argc < 1 || argc > 2) usage(); fname = *argv++; - if (!opt_create && !strchr(fname, '/')) { + if (!o.create_size && !strchr(fname, '/')) { snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname); if (!(fname = strdup(buf))) err(1, NULL); } dtype = *argv; - if (opt_create) { - if (opt_N) - errx(1, "create (-C) is incompatible with -N"); - fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644); - if (fd == -1) - errx(1, "failed to create %s", fname); - if (ftruncate(fd, opt_create)) - errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create); - } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1) - err(1, "%s", fname); - if (fstat(fd, &sb)) - err(1, "%s", fname); - if (opt_create) { - if (!S_ISREG(sb.st_mode)) - warnx("warning, %s is not a regular file", fname); - } else { - if (!S_ISCHR(sb.st_mode)) - warnx("warning, %s is not a character device", fname); - } - if (!opt_N) - check_mounted(fname, sb.st_mode); - if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET)) - errx(1, "cannot seek to %jd", (intmax_t)opt_ofs); - memset(&bpb, 0, sizeof(bpb)); - if (opt_f) { - getstdfmt(opt_f, &bpb); - bpb.bpbHugeSectors = bpb.bpbSectors; - bpb.bpbSectors = 0; - bpb.bpbBigFATsecs = bpb.bpbFATsecs; - bpb.bpbFATsecs = 0; - } - if (opt_h) - bpb.bpbHeads = opt_h; - if (opt_u) - bpb.bpbSecPerTrack = opt_u; - if (opt_S) - bpb.bpbBytesPerSec = opt_S; - if (opt_s) - bpb.bpbHugeSectors = opt_s; - if (oflag) - bpb.bpbHiddenSecs = opt_o; - if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) { - off_t delta; - getdiskinfo(fd, fname, dtype, oflag, &bpb); - bpb.bpbHugeSectors -= (opt_ofs / bpb.bpbBytesPerSec); - delta = bpb.bpbHugeSectors % bpb.bpbSecPerTrack; - if (delta != 0) { - warnx("trim %d sectors to adjust to a multiple of %d", - (int)delta, bpb.bpbSecPerTrack); - bpb.bpbHugeSectors -= delta; - } - if (bpb.bpbSecPerClust == 0) { /* set defaults */ - if (bpb.bpbHugeSectors <= 6000) /* about 3MB -> 512 bytes */ - bpb.bpbSecPerClust = 1; - else if (bpb.bpbHugeSectors <= (1<<17)) /* 64M -> 4k */ - bpb.bpbSecPerClust = 8; - else if (bpb.bpbHugeSectors <= (1<<19)) /* 256M -> 8k */ - bpb.bpbSecPerClust = 16; - else if (bpb.bpbHugeSectors <= (1<<21)) /* 1G -> 16k */ - bpb.bpbSecPerClust = 32; - else - bpb.bpbSecPerClust = 64; /* otherwise 32k */ - } - } - if (!powerof2(bpb.bpbBytesPerSec)) - errx(1, "bytes/sector (%u) is not a power of 2", bpb.bpbBytesPerSec); - if (bpb.bpbBytesPerSec < MINBPS) - errx(1, "bytes/sector (%u) is too small; minimum is %u", - bpb.bpbBytesPerSec, MINBPS); - if (!(fat = opt_F)) { - if (opt_f) - fat = 12; - else if (!opt_e && (opt_i || opt_k)) - fat = 32; - } - if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k))) - errx(1, "-%c is not a legal FAT%s option", - fat == 32 ? 'e' : opt_i ? 'i' : 'k', - fat == 32 ? "32" : "12/16"); - if (opt_f && fat == 32) - bpb.bpbRootDirEnts = 0; - if (opt_b) { - if (!powerof2(opt_b)) - errx(1, "block size (%u) is not a power of 2", opt_b); - if (opt_b < bpb.bpbBytesPerSec) - errx(1, "block size (%u) is too small; minimum is %u", - opt_b, bpb.bpbBytesPerSec); - if (opt_b > bpb.bpbBytesPerSec * MAXSPC) - errx(1, "block size (%u) is too large; maximum is %u", - opt_b, bpb.bpbBytesPerSec * MAXSPC); - bpb.bpbSecPerClust = opt_b / bpb.bpbBytesPerSec; - } - if (opt_c) { - if (!powerof2(opt_c)) - errx(1, "sectors/cluster (%u) is not a power of 2", opt_c); - bpb.bpbSecPerClust = opt_c; - } - if (opt_r) - bpb.bpbResSectors = opt_r; - if (opt_n) { - if (opt_n > MAXNFT) - errx(1, "number of FATs (%u) is too large; maximum is %u", - opt_n, MAXNFT); - bpb.bpbFATs = opt_n; - } - if (opt_e) - bpb.bpbRootDirEnts = opt_e; - if (mflag) { - if (opt_m < 0xf0) - errx(1, "illegal media descriptor (%#x)", opt_m); - bpb.bpbMedia = opt_m; - } - if (opt_a) - bpb.bpbBigFATsecs = opt_a; - if (opt_i) - bpb.bpbFSInfo = opt_i; - if (opt_k) - bpb.bpbBackup = opt_k; - bss = 1; - bname = NULL; - fd1 = -1; - if (opt_B) { - bname = opt_B; - if (!strchr(bname, '/')) { - snprintf(buf, sizeof(buf), "/boot/%s", bname); - if (!(bname = strdup(buf))) - err(1, NULL); - } - if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) - err(1, "%s", bname); - if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bpbBytesPerSec || - sb.st_size < bpb.bpbBytesPerSec || - sb.st_size > bpb.bpbBytesPerSec * MAXU16) - errx(1, "%s: inappropriate file type or format", bname); - bss = sb.st_size / bpb.bpbBytesPerSec; - } - if (!bpb.bpbFATs) - bpb.bpbFATs = 2; - if (!fat) { - if (bpb.bpbHugeSectors < (bpb.bpbResSectors ? bpb.bpbResSectors : bss) + - howmany((RESFTE + (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1)) * - (bpb.bpbSecPerClust ? 16 : 12) / BPN, - bpb.bpbBytesPerSec * NPB) * - bpb.bpbFATs + - howmany(bpb.bpbRootDirEnts ? bpb.bpbRootDirEnts : DEFRDE, - bpb.bpbBytesPerSec / sizeof(struct de)) + - (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1) * - (bpb.bpbSecPerClust ? bpb.bpbSecPerClust : - howmany(DEFBLK, bpb.bpbBytesPerSec))) - fat = 12; - else if (bpb.bpbRootDirEnts || bpb.bpbHugeSectors < - (bpb.bpbResSectors ? bpb.bpbResSectors : bss) + - howmany((RESFTE + MAXCLS16) * 2, bpb.bpbBytesPerSec) * - bpb.bpbFATs + - howmany(DEFRDE, bpb.bpbBytesPerSec / sizeof(struct de)) + - (MAXCLS16 + 1) * - (bpb.bpbSecPerClust ? bpb.bpbSecPerClust : - howmany(8192, bpb.bpbBytesPerSec))) - fat = 16; - else - fat = 32; - } - x = bss; - if (fat == 32) { - if (!bpb.bpbFSInfo) { - if (x == MAXU16 || x == bpb.bpbBackup) - errx(1, "no room for info sector"); - bpb.bpbFSInfo = x; - } - if (bpb.bpbFSInfo != MAXU16 && x <= bpb.bpbFSInfo) - x = bpb.bpbFSInfo + 1; - if (!bpb.bpbBackup) { - if (x == MAXU16) - errx(1, "no room for backup sector"); - bpb.bpbBackup = x; - } else if (bpb.bpbBackup != MAXU16 && bpb.bpbBackup == bpb.bpbFSInfo) - errx(1, "backup sector would overwrite info sector"); - if (bpb.bpbBackup != MAXU16 && x <= bpb.bpbBackup) - x = bpb.bpbBackup + 1; - } - if (!bpb.bpbResSectors) - bpb.bpbResSectors = fat == 32 ? - MAX(x, MAX(16384 / bpb.bpbBytesPerSec, 4)) : x; - else if (bpb.bpbResSectors < x) - errx(1, "too few reserved sectors (need %d have %d)", x, - bpb.bpbResSectors); - if (fat != 32 && !bpb.bpbRootDirEnts) - bpb.bpbRootDirEnts = DEFRDE; - rds = howmany(bpb.bpbRootDirEnts, bpb.bpbBytesPerSec / sizeof(struct de)); - if (!bpb.bpbSecPerClust) - for (bpb.bpbSecPerClust = howmany(fat == 16 ? DEFBLK16 : - DEFBLK, bpb.bpbBytesPerSec); - bpb.bpbSecPerClust < MAXSPC && - bpb.bpbResSectors + - howmany((RESFTE + maxcls(fat)) * (fat / BPN), - bpb.bpbBytesPerSec * NPB) * - bpb.bpbFATs + - rds + - (u_int64_t) (maxcls(fat) + 1) * - bpb.bpbSecPerClust <= bpb.bpbHugeSectors; - bpb.bpbSecPerClust <<= 1) - continue; - if (fat != 32 && bpb.bpbBigFATsecs > MAXU16) - errx(1, "too many sectors/FAT for FAT12/16"); - x1 = bpb.bpbResSectors + rds; - x = bpb.bpbBigFATsecs ? bpb.bpbBigFATsecs : 1; - if (x1 + (u_int64_t)x * bpb.bpbFATs > bpb.bpbHugeSectors) - errx(1, "meta data exceeds file system size"); - x1 += x * bpb.bpbFATs; - x = (u_int64_t)(bpb.bpbHugeSectors - x1) * bpb.bpbBytesPerSec * NPB / - (bpb.bpbSecPerClust * bpb.bpbBytesPerSec * NPB + fat / - BPN * bpb.bpbFATs); - x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), - bpb.bpbBytesPerSec * NPB); - if (!bpb.bpbBigFATsecs) { - bpb.bpbBigFATsecs = x2; - x1 += (bpb.bpbBigFATsecs - 1) * bpb.bpbFATs; - } - cls = (bpb.bpbHugeSectors - x1) / bpb.bpbSecPerClust; - x = (u_int64_t)bpb.bpbBigFATsecs * bpb.bpbBytesPerSec * NPB / (fat / BPN) - - RESFTE; - if (cls > x) - cls = x; - if (bpb.bpbBigFATsecs < x2) - warnx("warning: sectors/FAT limits file system to %u clusters", - cls); - if (cls < mincls(fat)) - errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, - mincls(fat)); - if (cls > maxcls(fat)) { - cls = maxcls(fat); - bpb.bpbHugeSectors = x1 + (cls + 1) * bpb.bpbSecPerClust - 1; - warnx("warning: FAT type limits file system to %u sectors", - bpb.bpbHugeSectors); - } - printf("%s: %u sector%s in %u FAT%u cluster%s " - "(%u bytes/cluster)\n", fname, cls * bpb.bpbSecPerClust, - cls * bpb.bpbSecPerClust == 1 ? "" : "s", cls, fat, - cls == 1 ? "" : "s", bpb.bpbBytesPerSec * bpb.bpbSecPerClust); - if (!bpb.bpbMedia) - bpb.bpbMedia = !bpb.bpbHiddenSecs ? 0xf0 : 0xf8; - if (fat == 32) - bpb.bpbRootClust = RESFTE; - if (bpb.bpbHiddenSecs + bpb.bpbHugeSectors <= MAXU16) { - bpb.bpbSectors = bpb.bpbHugeSectors; - bpb.bpbHugeSectors = 0; - } - if (fat != 32) { - bpb.bpbFATsecs = bpb.bpbBigFATsecs; - bpb.bpbBigFATsecs = 0; - } - print_bpb(&bpb); - if (!opt_N) { - gettimeofday(&tv, NULL); - now = tv.tv_sec; - tm = localtime(&now); - if (!(img = malloc(bpb.bpbBytesPerSec))) - err(1, NULL); - dir = bpb.bpbResSectors + (bpb.bpbFATsecs ? bpb.bpbFATsecs : - bpb.bpbBigFATsecs) * bpb.bpbFATs; - memset(&si_sa, 0, sizeof(si_sa)); - si_sa.sa_handler = infohandler; - if (sigaction(SIGINFO, &si_sa, NULL) == -1) - err(1, "sigaction SIGINFO"); - for (lsn = 0; lsn < dir + (fat == 32 ? bpb.bpbSecPerClust : rds); lsn++) { - if (got_siginfo) { - fprintf(stderr,"%s: writing sector %u of %u (%u%%)\n", - fname, lsn, - (dir + (fat == 32 ? bpb.bpbSecPerClust: rds)), - (lsn * 100) / (dir + - (fat == 32 ? bpb.bpbSecPerClust: rds))); - got_siginfo = 0; - } - x = lsn; - if (opt_B && - fat == 32 && bpb.bpbBackup != MAXU16 && - bss <= bpb.bpbBackup && x >= bpb.bpbBackup) { - x -= bpb.bpbBackup; - if (!x && lseek(fd1, opt_ofs, SEEK_SET)) - err(1, "%s", bname); - } - if (opt_B && x < bss) { - if ((n = read(fd1, img, bpb.bpbBytesPerSec)) == -1) - err(1, "%s", bname); - if ((unsigned)n != bpb.bpbBytesPerSec) - errx(1, "%s: can't read sector %u", bname, x); - } else - memset(img, 0, bpb.bpbBytesPerSec); - if (!lsn || - (fat == 32 && bpb.bpbBackup != MAXU16 && - lsn == bpb.bpbBackup)) { - x1 = sizeof(struct bs); - bsbpb = (struct bsbpb *)(img + x1); - mk2(bsbpb->bpbBytesPerSec, bpb.bpbBytesPerSec); - mk1(bsbpb->bpbSecPerClust, bpb.bpbSecPerClust); - mk2(bsbpb->bpbResSectors, bpb.bpbResSectors); - mk1(bsbpb->bpbFATs, bpb.bpbFATs); - mk2(bsbpb->bpbRootDirEnts, bpb.bpbRootDirEnts); - mk2(bsbpb->bpbSectors, bpb.bpbSectors); - mk1(bsbpb->bpbMedia, bpb.bpbMedia); - mk2(bsbpb->bpbFATsecs, bpb.bpbFATsecs); - mk2(bsbpb->bpbSecPerTrack, bpb.bpbSecPerTrack); - mk2(bsbpb->bpbHeads, bpb.bpbHeads); - mk4(bsbpb->bpbHiddenSecs, bpb.bpbHiddenSecs); - mk4(bsbpb->bpbHugeSectors, bpb.bpbHugeSectors); - x1 += sizeof(struct bsbpb); - if (fat == 32) { - bsxbpb = (struct bsxbpb *)(img + x1); - mk4(bsxbpb->bpbBigFATsecs, bpb.bpbBigFATsecs); - mk2(bsxbpb->bpbExtFlags, 0); - mk2(bsxbpb->bpbFSVers, 0); - mk4(bsxbpb->bpbRootClust, bpb.bpbRootClust); - mk2(bsxbpb->bpbFSInfo, bpb.bpbFSInfo); - mk2(bsxbpb->bpbBackup, bpb.bpbBackup); - x1 += sizeof(struct bsxbpb); - } - bsx = (struct bsx *)(img + x1); - mk1(bsx->exBootSignature, 0x29); - if (Iflag) - x = opt_I; - else - x = (((u_int)(1 + tm->tm_mon) << 8 | - (u_int)tm->tm_mday) + - ((u_int)tm->tm_sec << 8 | - (u_int)(tv.tv_usec / 10))) << 16 | - ((u_int)(1900 + tm->tm_year) + - ((u_int)tm->tm_hour << 8 | - (u_int)tm->tm_min)); - mk4(bsx->exVolumeID, x); - mklabel(bsx->exVolumeLabel, opt_L ? opt_L : "NO NAME"); - sprintf(buf, "FAT%u", fat); - setstr(bsx->exFileSysType, buf, sizeof(bsx->exFileSysType)); - if (!opt_B) { - x1 += sizeof(struct bsx); - bs = (struct bs *)img; - mk1(bs->bsJump[0], 0xeb); - mk1(bs->bsJump[1], x1 - 2); - mk1(bs->bsJump[2], 0x90); - setstr(bs->bsOemName, opt_O ? opt_O : "BSD4.4 ", - sizeof(bs->bsOemName)); - memcpy(img + x1, bootcode, sizeof(bootcode)); - mk2(img + MINBPS - 2, DOSMAGIC); - } - } else if (fat == 32 && bpb.bpbFSInfo != MAXU16 && - (lsn == bpb.bpbFSInfo || - (bpb.bpbBackup != MAXU16 && - lsn == bpb.bpbBackup + bpb.bpbFSInfo))) { - mk4(img, 0x41615252); - mk4(img + MINBPS - 28, 0x61417272); - mk4(img + MINBPS - 24, 0xffffffff); - mk4(img + MINBPS - 20, bpb.bpbRootClust); - mk2(img + MINBPS - 2, DOSMAGIC); - } else if (lsn >= bpb.bpbResSectors && lsn < dir && - !((lsn - bpb.bpbResSectors) % - (bpb.bpbFATsecs ? bpb.bpbFATsecs : - bpb.bpbBigFATsecs))) { - mk1(img[0], bpb.bpbMedia); - for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++) - mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff); - } else if (lsn == dir && opt_L) { - de = (struct de *)img; - mklabel(de->deName, opt_L); - mk1(de->deAttributes, 050); - x = (u_int)tm->tm_hour << 11 | - (u_int)tm->tm_min << 5 | - (u_int)tm->tm_sec >> 1; - mk2(de->deMTime, x); - x = (u_int)(tm->tm_year - 80) << 9 | - (u_int)(tm->tm_mon + 1) << 5 | - (u_int)tm->tm_mday; - mk2(de->deMDate, x); - } - if ((n = write(fd, img, bpb.bpbBytesPerSec)) == -1) - err(1, "%s", fname); - if ((unsigned)n != bpb.bpbBytesPerSec) - errx(1, "%s: can't write sector %u", fname, lsn); - } - } - return 0; -} - -/* - * Exit with error if file system is mounted. - */ -static void -check_mounted(const char *fname, mode_t mode) -{ - struct statfs *mp; - const char *s1, *s2; - size_t len; - int n, r; - - if (!(n = getmntinfo(&mp, MNT_NOWAIT))) - err(1, "getmntinfo"); - len = strlen(_PATH_DEV); - s1 = fname; - if (!strncmp(s1, _PATH_DEV, len)) - s1 += len; - r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; - for (; n--; mp++) { - s2 = mp->f_mntfromname; - if (!strncmp(s2, _PATH_DEV, len)) - s2 += len; - if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || - !strcmp(s1, s2)) - errx(1, "%s is mounted on %s", fname, mp->f_mntonname); - } -} - -/* - * Get a standard format. - */ -static void -getstdfmt(const char *fmt, struct bpb *bpb) -{ - u_int x, i; - - x = sizeof(stdfmt) / sizeof(stdfmt[0]); - for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++); - if (i == x) - errx(1, "%s: unknown standard format", fmt); - *bpb = stdfmt[i].bpb; -} - -/* - * Get disk slice, partition, and geometry information. - */ -static void -getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag, - struct bpb *bpb) -{ - struct disklabel *lp, dlp; - struct fd_type type; - off_t ms, hs = 0; - - lp = NULL; - - /* If the user specified a disk type, try to use that */ - if (dtype != NULL) { - lp = getdiskbyname(dtype); - } - - /* Maybe it's a floppy drive */ - if (lp == NULL) { - if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) { - struct stat st; - - if (fstat(fd, &st)) - err(1, "cannot get disk size"); - /* create a fake geometry for a file image */ - ms = st.st_size; - dlp.d_secsize = 512; - dlp.d_nsectors = 63; - dlp.d_ntracks = 255; - dlp.d_secperunit = ms / dlp.d_secsize; - lp = &dlp; - } else if (ioctl(fd, FD_GTYPE, &type) != -1) { - dlp.d_secsize = 128 << type.secsize; - dlp.d_nsectors = type.sectrac; - dlp.d_ntracks = type.heads; - dlp.d_secperunit = ms / dlp.d_secsize; - lp = &dlp; - } - } - - /* Maybe it's a fixed drive */ - if (lp == NULL) { - if (bpb->bpbBytesPerSec) - dlp.d_secsize = bpb->bpbBytesPerSec; - if (bpb->bpbBytesPerSec == 0 && ioctl(fd, DIOCGSECTORSIZE, - &dlp.d_secsize) == -1) - err(1, "cannot get sector size"); - - dlp.d_secperunit = ms / dlp.d_secsize; - - if (bpb->bpbSecPerTrack == 0 && ioctl(fd, DIOCGFWSECTORS, - &dlp.d_nsectors) == -1) { - warn("cannot get number of sectors per track"); - dlp.d_nsectors = 63; - } - if (bpb->bpbHeads == 0 && - ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) { - warn("cannot get number of heads"); - if (dlp.d_secperunit <= 63*1*1024) - dlp.d_ntracks = 1; - else if (dlp.d_secperunit <= 63*16*1024) - dlp.d_ntracks = 16; - else - dlp.d_ntracks = 255; - } - - hs = (ms / dlp.d_secsize) - dlp.d_secperunit; - lp = &dlp; - } - - if (bpb->bpbBytesPerSec == 0) - bpb->bpbBytesPerSec = ckgeom(fname, lp->d_secsize, "bytes/sector"); - if (bpb->bpbSecPerTrack == 0) - bpb->bpbSecPerTrack = ckgeom(fname, lp->d_nsectors, "sectors/track"); - if (bpb->bpbHeads == 0) - bpb->bpbHeads = ckgeom(fname, lp->d_ntracks, "drive heads"); - if (bpb->bpbHugeSectors == 0) - bpb->bpbHugeSectors = lp->d_secperunit; - if (bpb->bpbHiddenSecs == 0) - bpb->bpbHiddenSecs = hs; -} - -/* - * Print out BPB values. - */ -static void -print_bpb(struct bpb *bpb) -{ - printf("BytesPerSec=%u SecPerClust=%u ResSectors=%u FATs=%u", - bpb->bpbBytesPerSec, bpb->bpbSecPerClust, bpb->bpbResSectors, - bpb->bpbFATs); - if (bpb->bpbRootDirEnts) - printf(" RootDirEnts=%u", bpb->bpbRootDirEnts); - if (bpb->bpbSectors) - printf(" Sectors=%u", bpb->bpbSectors); - printf(" Media=%#x", bpb->bpbMedia); - if (bpb->bpbFATsecs) - printf(" FATsecs=%u", bpb->bpbFATsecs); - printf(" SecPerTrack=%u Heads=%u HiddenSecs=%u", bpb->bpbSecPerTrack, - bpb->bpbHeads, bpb->bpbHiddenSecs); - if (bpb->bpbHugeSectors) - printf(" HugeSectors=%u", bpb->bpbHugeSectors); - if (!bpb->bpbFATsecs) { - printf(" FATsecs=%u RootCluster=%u", bpb->bpbBigFATsecs, - bpb->bpbRootClust); - printf(" FSInfo="); - printf(bpb->bpbFSInfo == MAXU16 ? "%#x" : "%u", bpb->bpbFSInfo); - printf(" Backup="); - printf(bpb->bpbBackup == MAXU16 ? "%#x" : "%u", bpb->bpbBackup); - } - printf("\n"); -} - -/* - * Check a disk geometry value. - */ -static u_int -ckgeom(const char *fname, u_int val, const char *msg) -{ - if (!val) - errx(1, "%s: no default %s", fname, msg); - if (val > MAXU16) - errx(1, "%s: illegal %s %d", fname, msg, val); - return val; + return mkfs_msdos(fname, dtype, &o); } /* @@ -946,7 +197,7 @@ argtooff(const char *arg, const char *msg) default: errx(1, "%s: bad %s", arg, msg); /* notreached */ - + case 's': /* sector */ case 'S': x <<= 9; /* times 512 */ @@ -978,46 +229,6 @@ argtooff(const char *arg, const char *msg) return x; } -/* - * Check a volume label. - */ -static int -oklabel(const char *src) -{ - int c, i; - - for (i = 0; i <= 11; i++) { - c = (u_char)*src++; - if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c)) - break; - } - return i && !c; -} - -/* - * Make a volume label. - */ -static void -mklabel(u_int8_t *dest, const char *src) -{ - int c, i; - - for (i = 0; i < 11; i++) { - c = *src ? toupper(*src++) : ' '; - *dest++ = !i && c == '\xe5' ? 5 : c; - } -} - -/* - * Copy string, padding with spaces. - */ -static void -setstr(u_int8_t *dest, const char *src, size_t len) -{ - while (len--) - *dest++ = *src ? *src++ : ' '; -} - /* * Print usage message. */ @@ -1052,10 +263,3 @@ usage(void) "\t-u sectors/track\n"); exit(1); } - -static void -infohandler(int sig __unused) -{ - - got_siginfo = 1; -} diff --git a/sbin/savecore/Makefile b/sbin/savecore/Makefile index e46496f49c9..5134e4c546a 100644 --- a/sbin/savecore/Makefile +++ b/sbin/savecore/Makefile @@ -1,7 +1,7 @@ # $FreeBSD$ PROG= savecore -LIBADD= z xo util +LIBADD= z xo MAN= savecore.8 .include diff --git a/secure/Makefile b/secure/Makefile index 4e2cc4f7252..0b8ec0106ec 100644 --- a/secure/Makefile +++ b/secure/Makefile @@ -2,7 +2,9 @@ .include -SUBDIR= lib libexec ${_tests} usr.bin usr.sbin +SUBDIR= lib .WAIT \ + libexec ${_tests} usr.bin usr.sbin +SUBDIR_PARALLEL= .if ${MK_TESTS} != "no" _tests= tests @@ -17,7 +19,7 @@ SPROGS+=usr.sbin/sendmail .endif # This target is used to rebuild these programs with crypto. -secure: +secure: .MAKE .PHONY .for entry in ${SPROGS} cd ${.CURDIR}/../${entry}; \ ${MAKE} cleandir; \ @@ -28,7 +30,7 @@ secure: .endfor # This target is used to rebuild these programs without crypto. -insecure: +insecure: .MAKE .PHONY .for entry in ${SPROGS} cd ${.CURDIR}/../${entry}; \ ${MAKE} MK_CRYPT=no cleandir; \ diff --git a/secure/libexec/Makefile b/secure/libexec/Makefile index 07aa3dd4bdc..2e2eaf96da1 100644 --- a/secure/libexec/Makefile +++ b/secure/libexec/Makefile @@ -11,4 +11,6 @@ SUBDIR+=sftp-server ssh-keysign ssh-pkcs11-helper SUBDIR+=tests .endif +SUBDIR_PARALLEL= + .include diff --git a/secure/usr.bin/Makefile b/secure/usr.bin/Makefile index e14ebe3000e..f85fced7501 100644 --- a/secure/usr.bin/Makefile +++ b/secure/usr.bin/Makefile @@ -14,4 +14,6 @@ SUBDIR+=scp sftp ssh ssh-add ssh-agent ssh-keygen ssh-keyscan SUBDIR+=tests .endif +SUBDIR_PARALLEL= + .include diff --git a/secure/usr.sbin/Makefile b/secure/usr.sbin/Makefile index e42f456b12d..9b2f910f9c4 100644 --- a/secure/usr.sbin/Makefile +++ b/secure/usr.sbin/Makefile @@ -11,4 +11,6 @@ SUBDIR+=sshd SUBDIR+=tests .endif +SUBDIR_PARALLEL= + .include diff --git a/share/Makefile b/share/Makefile index fe67bd768dc..56cebef2e85 100644 --- a/share/Makefile +++ b/share/Makefile @@ -100,4 +100,6 @@ _vt= vt _zoneinfo= zoneinfo .endif +SUBDIR_PARALLEL= + .include diff --git a/share/doc/Makefile b/share/doc/Makefile index c752c51b33e..7a02b295259 100644 --- a/share/doc/Makefile +++ b/share/doc/Makefile @@ -28,6 +28,8 @@ _IPv6= IPv6 _roffdocs= papers psd smm usd .endif +SUBDIR_PARALLEL= + # Default output format for troff documents is ascii. # To generate postscript versions of troff documents, use: # make PRINTERDEVICE=ps diff --git a/share/doc/legal/Makefile b/share/doc/legal/Makefile index 9590800a67d..345eafc5b4a 100644 --- a/share/doc/legal/Makefile +++ b/share/doc/legal/Makefile @@ -6,4 +6,6 @@ SUBDIR= intel_ipw \ intel_wpi \ realtek +SUBDIR_PARALLEL= + .include diff --git a/share/doc/legal/intel_ipw/Makefile b/share/doc/legal/intel_ipw/Makefile index 18a2c127219..e52d8bbd0f3 100644 --- a/share/doc/legal/intel_ipw/Makefile +++ b/share/doc/legal/intel_ipw/Makefile @@ -1,6 +1,7 @@ # $FreeBSD$ FILES= ${.CURDIR}/../../../../sys/contrib/dev/ipw/LICENSE -FILESDIR= ${SHAREDIR}/doc/legal/intel_ipw +FILESDIR= ${SHAREDIR}/doc/legal +FILESNAME= intel_ipw.LICENSE .include diff --git a/share/doc/legal/intel_iwi/Makefile b/share/doc/legal/intel_iwi/Makefile index 6f0e6be1561..012ce7a4a22 100644 --- a/share/doc/legal/intel_iwi/Makefile +++ b/share/doc/legal/intel_iwi/Makefile @@ -1,6 +1,7 @@ # $FreeBSD$ FILES= ${.CURDIR}/../../../../sys/contrib/dev/iwi/LICENSE -FILESDIR= ${SHAREDIR}/doc/legal/intel_iwi +FILESDIR= ${SHAREDIR}/doc/legal +FILESNAME= intel_iwi.LICENSE .include diff --git a/share/doc/legal/intel_iwn/Makefile b/share/doc/legal/intel_iwn/Makefile index d2e04c9ba6f..dd5772bb288 100644 --- a/share/doc/legal/intel_iwn/Makefile +++ b/share/doc/legal/intel_iwn/Makefile @@ -1,6 +1,7 @@ # $FreeBSD$ FILES= ${.CURDIR}/../../../../sys/contrib/dev/iwn/LICENSE -FILESDIR= ${SHAREDIR}/doc/legal/intel_iwn +FILESDIR= ${SHAREDIR}/doc/legal +FILESNAME= intel_iwn.LICENSE .include diff --git a/share/doc/legal/intel_wpi/Makefile b/share/doc/legal/intel_wpi/Makefile index 06b486354ee..61f6d5dea93 100644 --- a/share/doc/legal/intel_wpi/Makefile +++ b/share/doc/legal/intel_wpi/Makefile @@ -1,7 +1,8 @@ # $FreeBSD$ FILES= ${.CURDIR}/../../../../sys/contrib/dev/wpi/LICENSE -FILESDIR= ${SHAREDIR}/doc/legal/intel_wpi +FILESDIR= ${SHAREDIR}/doc/legal +FILESNAME= intel_wpi.LICENSE .include diff --git a/share/doc/legal/realtek/Makefile b/share/doc/legal/realtek/Makefile index 2d96c422957..e168a18290d 100644 --- a/share/doc/legal/realtek/Makefile +++ b/share/doc/legal/realtek/Makefile @@ -1,6 +1,7 @@ # $FreeBSD$ FILES= ${.CURDIR}/../../../../sys/contrib/dev/urtwn/LICENSE -FILESDIR= ${SHAREDIR}/doc/legal/realtek +FILESDIR= ${SHAREDIR}/doc/legal +FILESNAME= realtek.LICENSE .include diff --git a/share/doc/papers/Makefile b/share/doc/papers/Makefile index eaf097f56d3..ddf1323725d 100644 --- a/share/doc/papers/Makefile +++ b/share/doc/papers/Makefile @@ -15,4 +15,6 @@ SUBDIR= beyond4.3 \ sysperf \ timecounter +SUBDIR_PARALLEL= + .include diff --git a/share/doc/psd/Makefile b/share/doc/psd/Makefile index 243ba9992e2..6b6d9cd727b 100644 --- a/share/doc/psd/Makefile +++ b/share/doc/psd/Makefile @@ -37,4 +37,6 @@ SUBDIR+=22.rpcgen \ 26.rpcrfc \ 27.nfsrpc +SUBDIR_PARALLEL= + .include diff --git a/share/doc/smm/Makefile b/share/doc/smm/Makefile index 1d99b052a35..c6c9c51dc5e 100644 --- a/share/doc/smm/Makefile +++ b/share/doc/smm/Makefile @@ -32,4 +32,6 @@ _08.sendmailop= 08.sendmailop _07.lpd= 07.lpd .endif +SUBDIR_PARALLEL= + .include diff --git a/share/doc/usd/Makefile b/share/doc/usd/Makefile index 5fcb6b30501..86381580363 100644 --- a/share/doc/usd/Makefile +++ b/share/doc/usd/Makefile @@ -19,5 +19,6 @@ SUBDIR= title \ 20.meref \ 21.troff \ 22.trofftut +SUBDIR_PARALLEL= .include diff --git a/share/examples/Makefile b/share/examples/Makefile index f20ea7f2b5b..63ad5b38176 100644 --- a/share/examples/Makefile +++ b/share/examples/Makefile @@ -261,4 +261,6 @@ SUBDIR+=pf SUBDIR+=tests .endif +SUBDIR_PARALLEL= + .include diff --git a/share/i18n/Makefile b/share/i18n/Makefile index 1cadd547c0b..b166d946480 100644 --- a/share/i18n/Makefile +++ b/share/i18n/Makefile @@ -4,5 +4,6 @@ .include SUBDIR= csmapper esdb +SUBDIR_PARALLEL= .include diff --git a/share/i18n/csmapper/Makefile b/share/i18n/csmapper/Makefile index 7bca3ab3649..18ee8fcef6e 100644 --- a/share/i18n/csmapper/Makefile +++ b/share/i18n/csmapper/Makefile @@ -5,6 +5,7 @@ FILESDIR= ${CSMAPPERDIR} SUBDIR= APPLE AST BIG5 CNS CP EBCDIC GB GEORGIAN ISO646 ISO-8859 JIS \ KAZAKH KOI KS MISC TCVN +SUBDIR_PARALLEL= mapper.dir: ${SUBDIR} newfile=$$(for i in ${SUBDIR}; do \ diff --git a/share/i18n/esdb/Makefile b/share/i18n/esdb/Makefile index 2b16d90288a..69d44454a9b 100644 --- a/share/i18n/esdb/Makefile +++ b/share/i18n/esdb/Makefile @@ -5,6 +5,7 @@ FILESDIR= ${ESDBDIR} SUBDIR= APPLE AST BIG5 CP DEC EUC EBCDIC GB GEORGIAN ISO-2022 ISO-8859 \ ISO646 KAZAKH KOI MISC TCVN UTF +SUBDIR_PARALLEL= FILES+= esdb.dir esdb.dir.db esdb.alias esdb.alias.db CLEANFILES= ${FILES} diff --git a/share/man/Makefile b/share/man/Makefile index c3bd5f22aca..e1dc9cf06c8 100644 --- a/share/man/Makefile +++ b/share/man/Makefile @@ -5,6 +5,7 @@ # XXX MISSING: man3f SUBDIR= man1 man3 man4 man5 man6 man7 man8 man9 +SUBDIR_PARALLEL= MAKEWHATIS?= makewhatis diff --git a/share/man/man3/bitstring.3 b/share/man/man3/bitstring.3 index e7e94260349..89a9524bae0 100644 --- a/share/man/man3/bitstring.3 +++ b/share/man/man3/bitstring.3 @@ -30,7 +30,7 @@ .\" @(#)bitstring.3 8.1 (Berkeley) 7/19/93 .\" $FreeBSD$ .\" -.Dd July 19, 1993 +.Dd October 17, 2015 .Dt BITSTRING 3 .Os .Sh NAME @@ -178,7 +178,8 @@ make_lpr_available() } .Ed .Sh SEE ALSO -.Xr malloc 3 +.Xr malloc 3 , +.Xr bitset 9 .Sh HISTORY The .Nm bitstring diff --git a/share/man/man4/ipw.4 b/share/man/man4/ipw.4 index 9dfbd6dce52..71ce5752836 100644 --- a/share/man/man4/ipw.4 +++ b/share/man/man4/ipw.4 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 13, 2008 +.Dd October 15, 2015 .Dt IPW 4 .Os .Sh NAME @@ -77,14 +77,14 @@ This driver requires the firmware built with the .Nm ipwfw module to work. For the loaded firmware to be enabled for use the license at -.Pa /usr/share/doc/legal/intel_ipw/LICENSE +.Pa /usr/share/doc/legal/intel_ipw.LICENSE must be agreed by adding the following line to .Xr loader.conf 5 : .Pp .Dl "legal.intel_ipw.license_ack=1" .Sh FILES -.Bl -tag -width ".Pa /usr/share/doc/legal/intel_ipw/LICENSE" -compact -.It Pa /usr/share/doc/legal/intel_ipw/LICENSE +.Bl -tag -width ".Pa /usr/share/doc/legal/intel_ipw.LICENSE" -compact +.It Pa /usr/share/doc/legal/intel_ipw.LICENSE .Nm firmware license .El diff --git a/share/man/man4/ipwfw.4 b/share/man/man4/ipwfw.4 index b901de8c0ee..810fe2a3238 100644 --- a/share/man/man4/ipwfw.4 +++ b/share/man/man4/ipwfw.4 @@ -22,7 +22,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 14, 2010 +.Dd October 15, 2015 .Dt IPWFW 4 .Os .Sh NAME @@ -59,14 +59,14 @@ Intel PRO/Wireless 2100 series of IEEE 802.11 adapters. It may be statically linked into the kernel, or loaded as a module. .Pp For the loaded firmware to be enabled for use the license at -.Pa /usr/share/doc/legal/intel_ipw/LICENSE +.Pa /usr/share/doc/legal/intel_ipw.LICENSE must be agreed to by adding the following line to .Xr loader.conf 5 : .Pp .Dl "legal.intel_ipw.license_ack=1" .Sh FILES -.Bl -tag -width ".Pa /usr/share/doc/legal/intel_ipw/LICENSE" -compact -.It Pa /usr/share/doc/legal/intel_ipw/LICENSE +.Bl -tag -width ".Pa /usr/share/doc/legal/intel_ipw.LICENSE" -compact +.It Pa /usr/share/doc/legal/intel_ipw.LICENSE .Nm firmware license .El diff --git a/share/man/man4/iwi.4 b/share/man/man4/iwi.4 index 911e54ce525..5d6b8f54247 100644 --- a/share/man/man4/iwi.4 +++ b/share/man/man4/iwi.4 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 13, 2008 +.Dd October 15, 2015 .Dt IWI 4 .Os .Sh NAME @@ -77,14 +77,14 @@ This driver requires the firmware built with the .Nm iwifw module to work. For the loaded firmware to be enabled for use the license at -.Pa /usr/share/doc/legal/intel_iwi/LICENSE +.Pa /usr/share/doc/legal/intel_iwi.LICENSE must be agreed by adding the following line to .Xr loader.conf 5 : .Pp .Dl "legal.intel_iwi.license_ack=1" .Sh FILES -.Bl -tag -width ".Pa /usr/share/doc/legal/intel_iwi/LICENSE" -compact -.It Pa /usr/share/doc/legal/intel_iwi/LICENSE +.Bl -tag -width ".Pa /usr/share/doc/legal/intel_iwi.LICENSE" -compact +.It Pa /usr/share/doc/legal/intel_iwi.LICENSE .Nm firmware license .El diff --git a/share/man/man4/iwifw.4 b/share/man/man4/iwifw.4 index 7effd3db717..52f90e3ea6f 100644 --- a/share/man/man4/iwifw.4 +++ b/share/man/man4/iwifw.4 @@ -22,7 +22,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 14, 2010 +.Dd October 15, 2015 .Dt IWIFW 4 .Os .Sh NAME @@ -59,14 +59,14 @@ Intel PRO/Wireless 2200BG/2225BG/2915ABG series of IEEE 802.11 adapters. It may be statically linked into the kernel, or loaded as a module. .Pp For the loaded firmware to be enabled for use the license at -.Pa /usr/share/doc/legal/intel_iwi/LICENSE +.Pa /usr/share/doc/legal/intel_iwi.LICENSE must be agreed to by adding the following line to .Xr loader.conf 5 : .Pp .Dl "legal.intel_iwi.license_ack=1" .Sh FILES -.Bl -tag -width ".Pa /usr/share/doc/legal/intel_iwi/LICENSE" -compact -.It Pa /usr/share/doc/legal/intel_iwi/LICENSE +.Bl -tag -width ".Pa /usr/share/doc/legal/intel_iwi.LICENSE" -compact +.It Pa /usr/share/doc/legal/intel_iwi.LICENSE .Nm firmware license .El diff --git a/share/man/man4/rsu.4 b/share/man/man4/rsu.4 index 2bd2f947c2a..a9fe2a2f641 100644 --- a/share/man/man4/rsu.4 +++ b/share/man/man4/rsu.4 @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd November 19, 2014 +.Dd October 15, 2015 .Dt RSU 4 .Os .Sh NAME @@ -41,7 +41,7 @@ if_rsu_load="YES" .Ed .Pp After you have read the license in -.Pa /usr/share/doc/legal/realtek +.Pa /usr/share/doc/legal/realtek.LICENSE you will want to add the following lines to .Xr loader.conf 5 : .Bd -literal -offset indent @@ -175,7 +175,10 @@ driver was written by .An Damien Bergamini Aq Mt damien@openbsd.org and ported by .An Rui Paulo Aq Mt rpaulo@freebsd.org . +The 802.11n support was added by +.An Adrian Chadd Aq Mt adrian@freebsd.org . .Sh CAVEATS The .Nm -driver only supports 1T1R 802.11n operation. +driver currently does not support 802.11n transmit aggregation, +either A-MSDU or A-MPDU. diff --git a/share/man/man4/urtwn.4 b/share/man/man4/urtwn.4 index 5a58200a81f..a4c1b9f10a4 100644 --- a/share/man/man4/urtwn.4 +++ b/share/man/man4/urtwn.4 @@ -14,7 +14,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 24, 2014 +.Dd October 15, 2015 .Dt URTWN 4 .Os .Sh NAME @@ -68,14 +68,14 @@ This driver requires the firmware built with the .Nm urtwnfw module to work. For the loaded firmware to be enabled for use the license at -.Pa /usr/share/doc/legal/realtek +.Pa /usr/share/doc/legal/realtek.LICENSE must be agreed by adding the following line to .Xr loader.conf 5 : .Pp .Dl "legal.realtek.license_ack=1" .Sh FILES -.Bl -tag -width ".Pa /usr/share/doc/legal/realtek" -compact -.It Pa /usr/share/doc/legal/realtek +.Bl -tag -width ".Pa /usr/share/doc/legal/realtek.LICENSE" -compact +.It Pa /usr/share/doc/legal/realtek.LICENSE .Nm firmware license .El diff --git a/share/man/man4/urtwnfw.4 b/share/man/man4/urtwnfw.4 index c25f944f1b5..70e6e67f20e 100644 --- a/share/man/man4/urtwnfw.4 +++ b/share/man/man4/urtwnfw.4 @@ -22,7 +22,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 25, 2014 +.Dd October 15, 2015 .Dt URTWNFW 4 .Os .Sh NAME @@ -61,14 +61,14 @@ It may be statically linked into the kernel, or loaded as a module. .Pp For the loaded firmware to be enabled for use the license at -.Pa /usr/share/doc/legal/realtek +.Pa /usr/share/doc/legal/realtek.LICENSE must be agreed to by adding the following line to .Xr loader.conf 5 : .Pp .Dl "legal.realtek.license_ack=1" .Sh FILES -.Bl -tag -width ".Pa /usr/share/doc/legal/realtek" -compact -.It Pa /usr/share/doc/legal/realtek +.Bl -tag -width ".Pa /usr/share/doc/legal/realtek.LICENSE" -compact +.It Pa /usr/share/doc/legal/realtek.LICENSE .Nm firmware license .El diff --git a/share/man/man4/wpi.4 b/share/man/man4/wpi.4 index 4cf33c410e7..04539f7328a 100644 --- a/share/man/man4/wpi.4 +++ b/share/man/man4/wpi.4 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 5, 2008 +.Dd October 15, 2015 .Dt WPI 4 .Os .Sh NAME @@ -72,8 +72,8 @@ This driver requires the firmware built with the .Nm wpifw module to work. .Sh FILES -.Bl -tag -width ".Pa /usr/share/doc/legal/intel_wpi/LICENSE" -compact -.It Pa /usr/share/doc/legal/intel_wpi/LICENSE +.Bl -tag -width ".Pa /usr/share/doc/legal/intel_wpi.LICENSE" -compact +.It Pa /usr/share/doc/legal/intel_wpi.LICENSE .Nm firmware license .El diff --git a/share/man/man5/resolver.5 b/share/man/man5/resolver.5 index 4116f23c9cd..a3c5e05cf7b 100644 --- a/share/man/man5/resolver.5 +++ b/share/man/man5/resolver.5 @@ -28,7 +28,7 @@ .\" @(#)resolver.5 8.1 (Berkeley) 6/5/93 .\" $FreeBSD$ .\" -.Dd December 25, 2013 +.Dd October 12, 2015 .Dt RESOLVER 5 .Os .Sh NAME @@ -175,6 +175,19 @@ the resolver from obeying the standard and .Sy search rules with the given name. +.It Sy reload-period: Ns Ar n +The resolver checks the modification time of +.Pa /etc/resolv.conf +every +.Ar n +seconds. +If +.Pa /etc/resolv.conf +has changed, it is automatically reloaded. +The default for +.Ar n +is two seconds. +Setting it to zero disables the file check. .El .Pp Options may also be specified as a space or tab separated list using the @@ -191,8 +204,7 @@ If more than one instance of these keywords is present, the last instance will override. .Pp The keyword and value must appear on a single line, and the keyword -(e.g.\& -.Sy nameserver ) +.Pq for example, Sy nameserver must start the line. The value follows the keyword, separated by white space. .Sh FILES diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index f26123df134..a5293664e3b 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -11,6 +11,7 @@ MAN= accept_filter.9 \ altq.9 \ atomic.9 \ bios.9 \ + bitset.9 \ boot.9 \ bpf.9 \ buf.9 \ @@ -429,6 +430,32 @@ MLINKS+=atomic.9 atomic_add.9 \ atomic.9 atomic_subtract.9 \ atomic.9 atomic_swap.9 \ atomic.9 atomic_testandset.9 +MLINKS+=bitset.9 BITSET_DEFINE.9 \ + bitset.9 BITSET_T_INITIALIZER.9 \ + bitset.9 BITSET_FSET.9 \ + bitset.9 BIT_CLR.9 \ + bitset.9 BIT_COPY.9 \ + bitset.9 BIT_ISSET.9 \ + bitset.9 BIT_SET.9 \ + bitset.9 BIT_ZERO.9 \ + bitset.9 BIT_FILL.9 \ + bitset.9 BIT_SETOF.9 \ + bitset.9 BIT_EMPTY.9 \ + bitset.9 BIT_ISFULLSET.9 \ + bitset.9 BIT_FFS.9 \ + bitset.9 BIT_COUNT.9 \ + bitset.9 BIT_SUBSET.9 \ + bitset.9 BIT_OVERLAP.9 \ + bitset.9 BIT_CMP.9 \ + bitset.9 BIT_OR.9 \ + bitset.9 BIT_AND.9 \ + bitset.9 BIT_NAND.9 \ + bitset.9 BIT_CLR_ATOMIC.9 \ + bitset.9 BIT_SET_ATOMIC.9 \ + bitset.9 BIT_SET_ATOMIC_ACQ.9 \ + bitset.9 BIT_AND_ATOMIC.9 \ + bitset.9 BIT_OR_ATOMIC.9 \ + bitset.9 BIT_COPY_STORE_REL.9 MLINKS+=bpf.9 bpfattach.9 \ bpf.9 bpfattach2.9 \ bpf.9 bpfdetach.9 \ diff --git a/share/man/man9/bitset.9 b/share/man/man9/bitset.9 new file mode 100644 index 00000000000..361b0e541c5 --- /dev/null +++ b/share/man/man9/bitset.9 @@ -0,0 +1,400 @@ +.\" Copyright (c) 2015 Conrad Meyer +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' +.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd October 17, 2015 +.Dt BITSET 9 +.Os +.Sh NAME +.Nm bitset(9) +\(em +.Nm BITSET_DEFINE , +.Nm BITSET_T_INITIALIZER , +.Nm BITSET_FSET , +.Nm BIT_CLR , +.Nm BIT_COPY , +.Nm BIT_ISSET , +.Nm BIT_SET , +.Nm BIT_ZERO , +.Nm BIT_FILL , +.Nm BIT_SETOF , +.Nm BIT_EMPTY , +.Nm BIT_ISFULLSET , +.Nm BIT_FFS , +.Nm BIT_COUNT , +.Nm BIT_SUBSET , +.Nm BIT_OVERLAP , +.Nm BIT_CMP , +.Nm BIT_OR , +.Nm BIT_AND , +.Nm BIT_NAND , +.Nm BIT_CLR_ATOMIC , +.Nm BIT_SET_ATOMIC , +.Nm BIT_SET_ATOMIC_ACQ , +.Nm BIT_AND_ATOMIC , +.Nm BIT_OR_ATOMIC , +.Nm BIT_COPY_STORE_REL +.Nd bitset manipulation macros +.Sh SYNOPSIS +.In sys/_bitset.h +.In sys/bitset.h +.\" +.Fn BITSET_DEFINE "STRUCTNAME" "const SETSIZE" +.Fn BITSET_T_INITIALIZER "ARRAY_CONTENTS" +.Fn BITSET_FSET "N_WORDS" +.\" +.Fn BIT_CLR "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" +.Fn BIT_COPY "const SETSIZE" "struct STRUCTNAME *from" "struct STRUCTNAME *to" +.Ft bool +.Fn BIT_ISSET "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" +.Fn BIT_SET "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" +.Fn BIT_ZERO "const SETSIZE" "struct STRUCTNAME *bitset" +.Fn BIT_FILL "const SETSIZE" "struct STRUCTNAME *bitset" +.Fn BIT_SETOF "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" +.Ft bool +.Fn BIT_EMPTY "const SETSIZE" "struct STRUCTNAME *bitset" +.Ft bool +.Fn BIT_ISFULLSET "const SETSIZE" "struct STRUCTNAME *bitset" +.Ft size_t +.Fn BIT_FFS "const SETSIZE" "struct STRUCTNAME *bitset" +.Ft size_t +.Fn BIT_COUNT "const SETSIZE" "struct STRUCTNAME *bitset" +.\" +.Ft bool +.Fo BIT_SUBSET +.Fa "const SETSIZE" "struct STRUCTNAME *haystack" "struct STRUCTNAME *needle" +.Fc +.Ft bool +.Fo BIT_OVERLAP +.Fa "const SETSIZE" "struct STRUCTNAME *bitset1" "struct STRUCTNAME *bitset2" +.Fc +.Ft bool +.Fo BIT_CMP +.Fa "const SETSIZE" "struct STRUCTNAME *bitset1" "struct STRUCTNAME *bitset2" +.Fc +.Fn BIT_OR "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" +.Fn BIT_AND "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" +.Fn BIT_NAND "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" +.\" +.Fn BIT_CLR_ATOMIC "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" +.Fn BIT_SET_ATOMIC "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" +.Fn BIT_SET_ATOMIC_ACQ "const SETSIZE" "size_t bit" "struct STRUCTNAME *bitset" +.\" +.Fo BIT_AND_ATOMIC +.Fa "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" +.Fc +.Fo BIT_OR_ATOMIC +.Fa "const SETSIZE" "struct STRUCTNAME *dst" "struct STRUCTNAME *src" +.Fc +.Fo BIT_COPY_STORE_REL +.Fa "const SETSIZE" "struct STRUCTNAME *from" "struct STRUCTNAME *to" +.Fc +.Sh DESCRIPTION +The +.Nm +family of macros provide a flexible and efficient bitset implementation if the +maximum size of the set is known at compilation. +Throughout this manual page, the name +.Fa SETSIZE +refers to the size of the bitset in bits. +Individual bits in bitsets are referenced with indices zero through +.Fa SETSIZE - 1 . +One example use of +.In sys/bitset.h +is +.In sys/cpuset.h . +.Pp +The +.Fn BITSET_DEFINE +macro defines a bitset struct +.Fa STRUCTNAME +with room to represent +.Fa SETSIZE +bits. +.Pp +The +.Fn BITSET_T_INITIALIZER +macro allows one to initialize a bitset struct with a compile time literal +value. +.Pp +The +.Fn BITSET_FSET +macro generates a compile time literal, usable by +.Fn BITSET_T_INITIALIZER , +representing a full bitset (all bits set). +For examples of +.Fn BITSET_T_INITIALIZER +and +.Fn BITSET_FSET +usage, see the +.Sx BITSET_T_INITIALIZER EXAMPLE +section. +The +.Fa N_WORDS +parameter to +.Fn BITSET_FSET +should be: +.Bd -literal -offset indent +__bitset_words(SETSIZE) +.Ed +.Pp +The +.Fn BIT_CLR +macro clears bit +.Fa bit +in the bitset pointed to by +.Fa bitset . +The +.Fn BIT_CLR_ATOMIC +macro is identical, but the bit is cleared atomically. +.Pp +The +.Fn BIT_COPY +macro copies the contents of the bitset +.Fa from +to the bitset +.Fa to . +.Fn BIT_COPY_STORE_REL +is similar, but copies component machine words from +.Fa from +and writes them to +.Fa to +with atomic store with release semantics. +(That is, if +.Fa to +is composed of multiple machine words, +.Fn BIT_COPY_STORE_REL +performs multiple individually atomic operations.) +.Pp +The +.Fn BIT_SET +macro sets bit +.Fa bit +in the bitset pointed to by +.Fa bitset . +The +.Fn BIT_SET_ATOMIC +macro is identical, but the bit is set atomically. +The +.Fn BIT_SET_ATOMIC_ACQ +macro sets the bit with acquire semantics. +.Pp +The +.Fn BIT_ZERO +macro clears all bits in +.Fa bitset . +.Pp +The +.Fn BIT_FILL +macro sets all bits in +.Fa bitset . +.Pp +The +.Fn BIT_SETOF +macro clears all bits in +.Fa bitset +before setting only bit +.Fa bit . +.Pp +The +.Fn BIT_EMPTY +macro returns +.Dv true +if +.Fa bitset +is empty. +.Pp +The +.Fn BIT_ISFULLSET +macro returns +.Dv true +if +.Fa bitset +is full (all bits set). +.Pp +The +.Fn BIT_FFS +macro returns the 1-index of the first (lowest) set bit in +.Fa bitset , +or zero if +.Fa bitset +is empty. +Like with +.Xr ffs 3 , +to use the non-zero result of +.Fn BIT_FFS +as a +.Fa bit +index parameter to any other +.Nm +macro, you must subtract one from the result. +.Pp +The +.Fn BIT_COUNT +macro returns the total number of set bits in +.Fa bitset . +.Pp +The +.Fn BIT_SUBSET +macro returns +.Dv true +if +.Fa needle +is a subset of +.Fa haystack . +.Pp +The +.Fn BIT_OVERLAP +macro returns +.Dv true +if +.Fa bitset1 +and +.Fa bitset2 +have any common bits. +(That is, if +.Fa bitset1 +AND +.Fa bitset2 +is not the empty set.) +.Pp +The +.Fn BIT_CMP +macro returns +.Dv true +if +.Fa bitset1 +is NOT equal to +.Fa bitset2 . +.Pp +The +.Fn BIT_OR +macro sets bits present in +.Fa src +in +.Fa dst . +(It is the +.Nm +equivalent of the scalar: +.Fa dst +|= +.Fa src . ) +.Fn BIT_OR_ATOMIC +is similar, but sets bits in the component machine words in +.Fa dst +atomically. +(That is, if +.Fa dst +is composed of multiple machine words, +.Fn BIT_OR_ATOMIC +performs multiple individually atomic operations.) +.Pp +The +.Fn BIT_AND +macro clears bits absent from +.Fa src +from +.Fa dst . +(It is the +.Nm +equivalent of the scalar: +.Fa dst +&= +.Fa src . ) +.Fn BIT_AND_ATOMIC +is similar, with the same atomic semantics as +.Fn BIT_OR_ATOMIC . +.Pp +The +.Fn BIT_NAND +macro clears bits set in +.Fa src +from +.Fa dst . +(It is the +.Nm +equivalent of the scalar: +.Fa dst +&= +.Fa ~ src . ) +.Sh BITSET_T_INITIALIZER EXAMPLE +.Bd -literal +BITSET_DEFINE(_myset, MYSETSIZE); + +struct _myset myset; + +/* Initialize myset to filled (all bits set) */ +myset = BITSET_T_INITIALIZER(BITSET_FSET(__bitset_words(MYSETSIZE))); + +/* Initialize myset to only the lowest bit set */ +myset = BITSET_T_INITIALIZER(0x1); +.Ed +.Sh SEE ALSO +The older +.Xr bitstring 3 . +.Sh HISTORY +.In sys/cpuset.h +first appeared in +.Fx 7.1 , +released in January 2009, and in +.Fx 8.0 , +released in November 2009 . +.Pp +The +.Nm +macros first appeared in +.Fx 10.0 +in January 2014. +They were MFCed to +.Fx 9.3 , +released in July 2014. +.Pp +This manual page first appeared in +.Fx 11.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +macros were written for +.In sys/cpuset.h +by +.An Jeff Roberson Aq Mt jeff@FreeBSD.org ; +they were generalized and pulled out as +.In sys/_bitset.h +and +.In sys/bitset.h +by +.An Attilio Rao Aq Mt attilio@FreeBSD.org . +This manual page was written by +.An Conrad Meyer Aq Mt cem@FreeBSD.org . +.Sh CAVEATS +The +.Fa SETSIZE +argument to all of these macros must match the value given to +.Fn BITSET_DEFINE . +.Pp +Unlike every other reference to individual set members, which are zero-indexed, +.Fn BIT_FFS +returns a one-indexed result (or zero if the set is empty). diff --git a/share/mk/bsd.confs.mk b/share/mk/bsd.confs.mk index 4e219e828bc..36298d7da02 100644 --- a/share/mk/bsd.confs.mk +++ b/share/mk/bsd.confs.mk @@ -69,7 +69,7 @@ _${group}INS: ${_${group}CONFS} ${.ALLSRC} ${DESTDIR}${${group}DIR}/${${group}NAME} .else ${INSTALL} -C -o ${${group}OWN} -g ${${group}GRP} -m ${${group}MODE} \ - ${.ALLSRC} ${DESTDIR}${${group}DIR} + ${.ALLSRC} ${DESTDIR}${${group}DIR}/ .endif .endif @@ -84,4 +84,4 @@ STAGE_TARGETS+= stage_config .endif .endif -.endif # ${MK_TOOLCHAIN} != "no" +.endif # ${MK_INCLUDES} != "no" diff --git a/share/mk/bsd.doc.mk b/share/mk/bsd.doc.mk index 9c6b0057912..c89c09d1c4a 100644 --- a/share/mk/bsd.doc.mk +++ b/share/mk/bsd.doc.mk @@ -133,11 +133,11 @@ CLEANFILES+= ${DOC}.ascii ${DOC}.ascii${DCOMPRESS_EXT} \ ${DOC}.html ${DOC}-*.html realinstall: -.for _dev in ${PRINTERDEVICE:Mhtml} +.if ${PRINTERDEVICE:Mhtml} cd ${SRCDIR}; \ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${DOC}*.html ${DESTDIR}${BINDIR}/${VOLUME} -.endfor +.endif .for _dev in ${PRINTERDEVICE:Nhtml} ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${DFILE.${_dev}} ${DESTDIR}${BINDIR}/${VOLUME} @@ -184,7 +184,6 @@ ${DFILE.html}: ${SRCS} .else # unroff(1) requires a macro package as an argument cd ${SRCDIR}; ${UNROFF} -ms ${UNROFFFLAGS} \ document=${DOC} ${SRCS} -.else .endif .endif .endfor diff --git a/share/mk/bsd.files.mk b/share/mk/bsd.files.mk index b9379d92b59..6a6c66a682f 100644 --- a/share/mk/bsd.files.mk +++ b/share/mk/bsd.files.mk @@ -75,7 +75,7 @@ _${group}INS: ${_${group}FILES} ${DESTDIR}${${group}DIR}/${${group}NAME} .else ${INSTALL} -o ${${group}OWN} -g ${${group}GRP} \ - -m ${${group}MODE} ${.ALLSRC} ${DESTDIR}${${group}DIR} + -m ${${group}MODE} ${.ALLSRC} ${DESTDIR}${${group}DIR}/ .endif .endif diff --git a/share/mk/bsd.incs.mk b/share/mk/bsd.incs.mk index 3f9680a6ef2..8ea97ba14fc 100644 --- a/share/mk/bsd.incs.mk +++ b/share/mk/bsd.incs.mk @@ -70,7 +70,7 @@ _${group}INS: ${_${group}INCS} ${.ALLSRC} ${DESTDIR}${${group}DIR}/${${group}NAME} .else ${INSTALL} -C -o ${${group}OWN} -g ${${group}GRP} -m ${${group}MODE} \ - ${.ALLSRC} ${DESTDIR}${${group}DIR} + ${.ALLSRC} ${DESTDIR}${${group}DIR}/ .endif .endif @@ -99,4 +99,4 @@ STAGE_SYMLINKS.INCS= ${INCSLINKS} .endif .endif -.endif # ${MK_TOOLCHAIN} != "no" +.endif # ${MK_INCLUDES} != "no" diff --git a/share/mk/bsd.info.mk b/share/mk/bsd.info.mk index 8f913158541..7720d286d3b 100644 --- a/share/mk/bsd.info.mk +++ b/share/mk/bsd.info.mk @@ -177,11 +177,11 @@ CLEANFILES+= ${INFO:S/$/.info.*.html/} ${INFO:S/$/.info/} install: ${INSTALLINFODIRS} .if !empty(IFILES:N*.html) ${INSTALL} -o ${INFOOWN} -g ${INFOGRP} -m ${INFOMODE} \ - ${IFILES:N*.html} ${DESTDIR}${INFODIR} + ${IFILES:N*.html} ${DESTDIR}${INFODIR}/ .endif .if !empty(FORMATS:Mhtml) ${INSTALL} -o ${INFOOWN} -g ${INFOGRP} -m ${INFOMODE} \ - ${INFO:S/$/.info.*.html/} ${DESTDIR}${INFODIR} + ${INFO:S/$/.info.*.html/} ${DESTDIR}${INFODIR}/ .endif .else # The indirection in the following is to avoid the null install rule diff --git a/share/mk/bsd.lib.mk b/share/mk/bsd.lib.mk index 0ed83454ebf..e906ab7e83a 100644 --- a/share/mk/bsd.lib.mk +++ b/share/mk/bsd.lib.mk @@ -140,7 +140,7 @@ SHLIB_NAME_FULL=${SHLIB_NAME}.full # Use ${DEBUGDIR} for base system debug files, else .debug subdirectory .if ${_SHLIBDIR} == "/boot" ||\ ${SHLIBDIR:C%/lib(/.*)?$%/lib%} == "/lib" ||\ - ${SHLIBDIR:C%/usr/lib(32)?(/.*)?%/usr/lib%} == "/usr/lib" + ${SHLIBDIR:C%/usr/(tests/)?lib(32|exec)?(/.*)?%/usr/lib%} == "/usr/lib" DEBUGFILEDIR=${DEBUGDIR}${_SHLIBDIR} .else DEBUGFILEDIR=${_SHLIBDIR}/.debug @@ -339,23 +339,23 @@ realinstall: _libinstall _libinstall: .if defined(LIB) && !empty(LIB) && ${MK_INSTALLLIB} != "no" ${INSTALL} -C -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ - ${_INSTALLFLAGS} lib${LIB_PRIVATE}${LIB}.a ${DESTDIR}${_LIBDIR} + ${_INSTALLFLAGS} lib${LIB_PRIVATE}${LIB}.a ${DESTDIR}${_LIBDIR}/ .endif .if ${MK_PROFILE} != "no" && defined(LIB) && !empty(LIB) ${INSTALL} -C -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ - ${_INSTALLFLAGS} lib${LIB_PRIVATE}${LIB}_p.a ${DESTDIR}${_LIBDIR} + ${_INSTALLFLAGS} lib${LIB_PRIVATE}${LIB}_p.a ${DESTDIR}${_LIBDIR}/ .endif .if defined(SHLIB_NAME) ${INSTALL} ${STRIP} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ ${_INSTALLFLAGS} ${_SHLINSTALLFLAGS} \ - ${SHLIB_NAME} ${DESTDIR}${_SHLIBDIR} + ${SHLIB_NAME} ${DESTDIR}${_SHLIBDIR}/ .if ${MK_DEBUG_FILES} != "no" .if defined(DEBUGMKDIR) - ${INSTALL} -T debug -d ${DESTDIR}${DEBUGFILEDIR} + ${INSTALL} -T debug -d ${DESTDIR}${DEBUGFILEDIR}/ .endif ${INSTALL} -T debug -o ${LIBOWN} -g ${LIBGRP} -m ${DEBUGMODE} \ ${_INSTALLFLAGS} \ - ${SHLIB_NAME}.debug ${DESTDIR}${DEBUGFILEDIR} + ${SHLIB_NAME}.debug ${DESTDIR}${DEBUGFILEDIR}/ .endif .if defined(SHLIB_LINK) .if commands(${SHLIB_LINK:R}.ld) @@ -378,11 +378,11 @@ _libinstall: .endif # SHIB_NAME .if defined(INSTALL_PIC_ARCHIVE) && defined(LIB) && !empty(LIB) && ${MK_TOOLCHAIN} != "no" ${INSTALL} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ - ${_INSTALLFLAGS} lib${LIB}_pic.a ${DESTDIR}${_LIBDIR} + ${_INSTALLFLAGS} lib${LIB}_pic.a ${DESTDIR}${_LIBDIR}/ .endif .if defined(WANT_LINT) && !defined(NO_LINT) && defined(LIB) && !empty(LIB) ${INSTALL} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ - ${_INSTALLFLAGS} ${LINTLIB} ${DESTDIR}${LINTLIBDIR} + ${_INSTALLFLAGS} ${LINTLIB} ${DESTDIR}${LINTLIBDIR}/ .endif .endif # !defined(INTERNALLIB) diff --git a/share/mk/bsd.man.mk b/share/mk/bsd.man.mk index bbb47b9df93..72aedfb5423 100644 --- a/share/mk/bsd.man.mk +++ b/share/mk/bsd.man.mk @@ -187,7 +187,7 @@ _maninstall: ${MAN} .endfor .else .for _page _sect in ${.ALLSRC:C/\.([^.]*)$/.\1 \1/} - @d=${DESTDIR}${MANDIR}${_sect}${MANSUBDIR}; \ + @d=${DESTDIR}${MANDIR}${_sect}${MANSUBDIR}/; \ ${ECHO} ${MINSTALL} ${_page} $${d}; \ ${MINSTALL} $${page} $${d}; .endfor @@ -201,7 +201,7 @@ _maninstall: ${MAN} .else .for __page in ${MAN} ${MINSTALL} ${__page:T:S/$/${MCOMPRESS_EXT}/g} \ - ${DESTDIR}${MANDIR}${__page:E}${MANSUBDIR} + ${DESTDIR}${MANDIR}${__page:E}${MANSUBDIR}/ .if defined(MANBUILDCAT) && !empty(MANBUILDCAT) ${MINSTALL} ${__page:T:S/$/${CATEXT}${MCOMPRESS_EXT}/g} \ ${DESTDIR}${CATDIR}${__page:E}${MANSUBDIR}/${__page:T:S/$/${MCOMPRESS_EXT}/} diff --git a/share/mk/bsd.prog.mk b/share/mk/bsd.prog.mk index 966c17e8cff..fbb6922f7f6 100644 --- a/share/mk/bsd.prog.mk +++ b/share/mk/bsd.prog.mk @@ -56,9 +56,9 @@ PROG_FULL=${PROG}.full # Use ${DEBUGDIR} for base system debug files, else .debug subdirectory .if defined(BINDIR) && (\ ${BINDIR} == "/bin" ||\ - ${BINDIR} == "/libexec" ||\ + ${BINDIR:C%/libexec(/.*)?%/libexec%} == "/libexec" ||\ ${BINDIR} == "/sbin" ||\ - ${BINDIR:C%/usr/(bin|bsdinstall|libexec|lpr|sendmail|sm.bin|sbin)(/.*)?%/usr/bin%} == "/usr/bin"\ + ${BINDIR:C%/usr/(bin|bsdinstall|libexec|lpr|sendmail|sm.bin|sbin|tests)(/.*)?%/usr/bin%} == "/usr/bin"\ ) DEBUGFILEDIR= ${DEBUGDIR}${BINDIR} .else @@ -205,7 +205,7 @@ _proginstall: ${_INSTALLFLAGS} ${PROG} ${DESTDIR}${BINDIR}/${PROGNAME} .if ${MK_DEBUG_FILES} != "no" .if defined(DEBUGMKDIR) - ${INSTALL} -T debug -d ${DESTDIR}${DEBUGFILEDIR} + ${INSTALL} -T debug -d ${DESTDIR}${DEBUGFILEDIR}/ .endif ${INSTALL} -T debug -o ${BINOWN} -g ${BINGRP} -m ${DEBUGMODE} \ ${PROGNAME}.debug ${DESTDIR}${DEBUGFILEDIR}/${PROGNAME}.debug diff --git a/share/mk/bsd.progs.mk b/share/mk/bsd.progs.mk index ab527089c80..2da30c70ebb 100644 --- a/share/mk/bsd.progs.mk +++ b/share/mk/bsd.progs.mk @@ -31,6 +31,9 @@ UPDATE_DEPENDFILE_PROG = ${PROGS:[1]} # They may have asked us to build just one .for t in ${PROGS} .if make($t) +.if ${PROGS_CXX:M${t}} +PROG_CXX ?= $t +.endif PROG ?= $t .endif .endfor @@ -61,7 +64,7 @@ UPDATE_DEPENDFILE ?= yes UPDATE_DEPENDFILE ?= NO # prog.mk will do the rest -.else +.else # !defined(PROG) all: ${PROGS} # We cannot capture dependencies for meta mode here @@ -80,10 +83,29 @@ $v = # handle being called [bsd.]progs.mk .include -.if !empty(PROGS) && !defined(_RECURSING_PROGS) +.if !empty(PROGS) && !defined(_RECURSING_PROGS) && !defined(PROG) # tell progs.mk we might want to install things PROGS_TARGETS+= checkdpadd clean cleandepend cleandir depend install +# Find common sources among the PROGS and depend on them before building +# anything. This allows parallelization without them each fighting over +# the same objects. +_PROGS_COMMON_SRCS= +_PROGS_ALL_SRCS= +.for p in ${PROGS} +.for s in ${SRCS.${p}} +.if ${_PROGS_ALL_SRCS:M${s}} && !${_PROGS_COMMON_SRCS:M${s}} +_PROGS_COMMON_SRCS+= ${s} +.else +_PROGS_ALL_SRCS+= ${s} +.endif +.endfor +.endfor +.if !empty(_PROGS_COMMON_SRCS) +_PROGS_COMMON_OBJS= ${_PROGS_COMMON_SRCS:N*.h:R:S/$/.o/g} +${PROGS}: ${_PROGS_COMMON_OBJS} +.endif + .for p in ${PROGS} .if defined(PROGS_CXX) && !empty(PROGS_CXX:M$p) # bsd.prog.mk may need to know this @@ -111,4 +133,4 @@ $p.$t: .PHONY .MAKE .for t in ${PROGS_TARGETS:O:u} $t: ${PROGS:%=%.$t} .endfor -.endif +.endif # !empty(PROGS) && !defined(_RECURSING_PROGS) && !defined(PROG) diff --git a/share/mk/bsd.subdir.mk b/share/mk/bsd.subdir.mk index 293b017e225..037a7fec37a 100644 --- a/share/mk/bsd.subdir.mk +++ b/share/mk/bsd.subdir.mk @@ -32,9 +32,11 @@ .if !target(____) ____: -ALL_SUBDIR_TARGETS= all all-man checkdpadd clean cleandepend cleandir \ - cleanilinks cleanobj depend distribute lint maninstall manlint obj \ - objlink realinstall regress tags ${SUBDIR_TARGETS} +ALL_SUBDIR_TARGETS= all all-man buildconfig checkdpadd clean cleandepend \ + cleandir cleanilinks cleanobj depend distribute \ + installconfig lint maninstall manlint obj objlink \ + realinstall regress tags \ + ${SUBDIR_TARGETS} .include @@ -123,7 +125,10 @@ _sub.${__target}: _SUBDIR .endif .endfor -.for __target in files includes config +# This is to support 'make includes' calling 'make buildincludes' and +# 'make installincludes' in the proper order, and to support these +# targets as SUBDIR_TARGETS. +.for __target in files includes .for __stage in build install ${__stage}${__target}: .if make(${__stage}${__target}) diff --git a/share/mk/bsd.sys.mk b/share/mk/bsd.sys.mk index 044b668ebad..cad2b2f7964 100644 --- a/share/mk/bsd.sys.mk +++ b/share/mk/bsd.sys.mk @@ -148,8 +148,14 @@ CXXFLAGS.clang+= -Wno-c++11-extensions .if ${MK_SSP} != "no" && \ ${MACHINE_CPUARCH} != "arm" && ${MACHINE_CPUARCH} != "mips" +.if (${COMPILER_TYPE} == "clang" && ${COMPILER_VERSION} >= 30500) || \ + (${COMPILER_TYPE} == "gcc" && \ + (${COMPILER_VERSION} == 40201 || ${COMPILER_VERSION} >= 40900)) # Don't use -Wstack-protector as it breaks world with -Werror. SSP_CFLAGS?= -fstack-protector-strong +.else +SSP_CFLAGS?= -fstack-protector +.endif CFLAGS+= ${SSP_CFLAGS} .endif # SSP && !ARM && !MIPS @@ -165,13 +171,13 @@ CXXFLAGS+= ${CXXFLAGS.${COMPILER_TYPE}} # Tell bmake not to mistake standard targets for things to be searched for # or expect to ever be up-to-date. PHONY_NOTMAIN = afterdepend afterinstall all beforedepend beforeinstall \ - beforelinking build build-tools buildfiles buildincludes \ - checkdpadd clean cleandepend cleandir cleanobj configure \ - depend dependall distclean distribute exe \ - html includes install installfiles installincludes lint \ - obj objlink objs objwarn realall realdepend \ - realinstall regress subdir-all subdir-depend subdir-install \ - tags whereobj + beforelinking build build-tools buildconfig buildfiles \ + buildincludes checkdpadd clean cleandepend cleandir cleanobj \ + configure depend dependall distclean distribute exe \ + files html includes install installconfig installfiles \ + installincludes lint obj objlink objs objwarn realall \ + realdepend realinstall regress subdir-all subdir-depend \ + subdir-install tags whereobj # we don't want ${PROG} to be PHONY .PHONY: ${PHONY_NOTMAIN:N${PROG:U}} diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk index fc287e33361..61f9d79ad4f 100644 --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -240,6 +240,7 @@ _DP_vmmapi= util _DP_ctf= z _DP_proc= rtld_db util _DP_dtrace= rtld_db pthread +_DP_xo= util # Define spacial cases LDADD_supcplusplus= -lsupc++ diff --git a/share/mk/sys.mk b/share/mk/sys.mk index e8f4892cd99..b6c75b6f96a 100644 --- a/share/mk/sys.mk +++ b/share/mk/sys.mk @@ -145,13 +145,12 @@ ECHODIR ?= true .endif .endif -.if defined(.PARSEDIR) -# _+_ appears to be a workaround for the special src .MAKE not working. -# setting it to + interferes with -N -_+_ ?= -.elif !empty(.MAKEFLAGS:M-n) && ${.MAKEFLAGS:M-n} == "-n" -# the check above matches only a single -n, so -n -n will result -# in _+_ = + +.if ${.MAKEFLAGS:M-N} +# bmake -N is supposed to skip executing anything but it does not skip +# exeucting '+' commands. The '+' feature is used where .MAKE +# is not safe for the entire target. -N is intended to skip building sub-makes +# so it executing '+' commands is not right. Work around the bug by not +# setting '+' when -N is used. _+_ ?= .else _+_ ?= + diff --git a/share/syscons/Makefile b/share/syscons/Makefile index 9cbf100f13b..c5993a8eaf1 100644 --- a/share/syscons/Makefile +++ b/share/syscons/Makefile @@ -1,5 +1,6 @@ # $FreeBSD$ SUBDIR= fonts keymaps scrnmaps +SUBDIR_PARALLEL= .include diff --git a/share/zoneinfo/Makefile b/share/zoneinfo/Makefile index ebb26c7c7ad..b3d968dc4b4 100644 --- a/share/zoneinfo/Makefile +++ b/share/zoneinfo/Makefile @@ -79,7 +79,7 @@ zoneinfo: yearistype ${TDATA} beforeinstall: cd ${TZBUILDDIR} && \ - find * -type f -print -exec ${INSTALL} \ + find -s * -type f -print -exec ${INSTALL} \ -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \ \{} ${DESTDIR}/usr/share/zoneinfo/\{} \; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \ diff --git a/sys/arm/allwinner/a20/a20_mp.c b/sys/arm/allwinner/a20/a20_mp.c index 9429aaf2b6d..4beac42481f 100644 --- a/sys/arm/allwinner/a20/a20_mp.c +++ b/sys/arm/allwinner/a20/a20_mp.c @@ -61,7 +61,7 @@ void platform_mp_init_secondary(void) { - arm_init_secondary_ic(); + arm_pic_init_secondary(); } void diff --git a/sys/arm/altera/socfpga/socfpga_mp.c b/sys/arm/altera/socfpga/socfpga_mp.c index 24784e98881..461d3f4958b 100644 --- a/sys/arm/altera/socfpga/socfpga_mp.c +++ b/sys/arm/altera/socfpga/socfpga_mp.c @@ -87,7 +87,7 @@ void platform_mp_init_secondary(void) { - arm_init_secondary_ic(); + arm_pic_init_secondary(); } void diff --git a/sys/arm/amlogic/aml8726/aml8726_mp.c b/sys/arm/amlogic/aml8726/aml8726_mp.c index 4f0bce06766..1d7ef032d5d 100644 --- a/sys/arm/amlogic/aml8726/aml8726_mp.c +++ b/sys/arm/amlogic/aml8726/aml8726_mp.c @@ -351,7 +351,7 @@ platform_mp_init_secondary(void) * each AP. */ - arm_init_secondary_ic(); + arm_pic_init_secondary(); } diff --git a/sys/arm/annapurna/alpine/alpine_machdep_mp.c b/sys/arm/annapurna/alpine/alpine_machdep_mp.c index 2c909175cb0..a4c45b8d07f 100644 --- a/sys/arm/annapurna/alpine/alpine_machdep_mp.c +++ b/sys/arm/annapurna/alpine/alpine_machdep_mp.c @@ -122,7 +122,7 @@ void platform_mp_init_secondary(void) { - arm_init_secondary_ic(); + arm_pic_init_secondary(); } void diff --git a/sys/arm/annapurna/alpine/common.c b/sys/arm/annapurna/alpine/common.c index a0fade09128..774bf00acec 100644 --- a/sys/arm/annapurna/alpine/common.c +++ b/sys/arm/annapurna/alpine/common.c @@ -29,6 +29,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include diff --git a/sys/arm/arm/bcopyinout.S b/sys/arm/arm/bcopyinout.S index df0484e685c..1c7e0fd6341 100644 --- a/sys/arm/arm/bcopyinout.S +++ b/sys/arm/arm/bcopyinout.S @@ -94,6 +94,15 @@ ENTRY(copyin) moveq r0, #0 RETeq + adds r3, r0, r2 + movcs r0, #EFAULT + RETc(cs) + + ldr r12, =(VM_MAXUSER_ADDRESS + 1) + cmp r3, r12 + movcs r0, #EFAULT + RETc(cs) + ldr r3, .L_arm_memcpy ldr r3, [r3] cmp r3, #0 @@ -332,6 +341,15 @@ ENTRY(copyout) moveq r0, #0 RETeq + adds r3, r1, r2 + movcs r0, #EFAULT + RETc(cs) + + ldr r12, =(VM_MAXUSER_ADDRESS + 1) + cmp r3, r12 + movcs r0, #EFAULT + RETc(cs) + ldr r3, .L_arm_memcpy ldr r3, [r3] cmp r3, #0 diff --git a/sys/arm/arm/bcopyinout_xscale.S b/sys/arm/arm/bcopyinout_xscale.S index 7a5abb588d6..79a5027356c 100644 --- a/sys/arm/arm/bcopyinout_xscale.S +++ b/sys/arm/arm/bcopyinout_xscale.S @@ -67,6 +67,15 @@ ENTRY(copyin) movle r0, #0x00 movle pc, lr /* Bail early if length is <= 0 */ + adds r3, r0, r2 + movcs r0, #EFAULT + RETc(cs) + + ldr r12, =(VM_MAXUSER_ADDRESS + 1) + cmp r3, r12 + movcs r0, #EFAULT + RETc(cs) + ldr r3, .L_arm_memcpy ldr r3, [r3] cmp r3, #0 @@ -509,6 +518,15 @@ ENTRY(copyout) movle r0, #0x00 movle pc, lr /* Bail early if length is <= 0 */ + adds r3, r1, r2 + movcs r0, #EFAULT + RETc(cs) + + ldr r12, =(VM_MAXUSER_ADDRESS + 1) + cmp r3, r12 + movcs r0, #EFAULT + RETc(cs) + ldr r3, .L_arm_memcpy ldr r3, [r3] cmp r3, #0 diff --git a/sys/arm/arm/copystr.S b/sys/arm/arm/copystr.S index 48c296b0929..4cb8469903d 100644 --- a/sys/arm/arm/copystr.S +++ b/sys/arm/arm/copystr.S @@ -113,6 +113,8 @@ ENTRY(copyinstr) moveq r0, #ENAMETOOLONG beq 2f + ldr r12, =VM_MAXUSER_ADDRESS + GET_PCB(r4) ldr r4, [r4] @@ -124,7 +126,10 @@ ENTRY(copyinstr) adr r5, .Lcopystrfault str r5, [r4, #PCB_ONFAULT] -1: ldrbt r5, [r0], #0x0001 +1: + cmp r0, r12 + bcs .Lcopystrfault + ldrbt r5, [r0], #0x0001 add r6, r6, #0x00000001 teq r5, #0x00000000 strb r5, [r1], #0x0001 @@ -161,6 +166,8 @@ ENTRY(copyoutstr) moveq r0, #ENAMETOOLONG beq 2f + ldr r12, =VM_MAXUSER_ADDRESS + GET_PCB(r4) ldr r4, [r4] @@ -172,7 +179,10 @@ ENTRY(copyoutstr) adr r5, .Lcopystrfault str r5, [r4, #PCB_ONFAULT] -1: ldrb r5, [r0], #0x0001 +1: + cmp r0, r12 + bcs .Lcopystrfault + ldrb r5, [r0], #0x0001 add r6, r6, #0x00000001 teq r5, #0x00000000 strbt r5, [r1], #0x0001 @@ -195,9 +205,9 @@ END(copyoutstr) /* A fault occurred during the copy */ .Lcopystrfault: - mov r0, #EFAULT mov r1, #0x00000000 str r1, [r4, #PCB_ONFAULT] + mov r0, #EFAULT RESTORE_REGS RET diff --git a/sys/arm/arm/fusu.S b/sys/arm/arm/fusu.S index 06853c45694..dee2455a0d3 100644 --- a/sys/arm/arm/fusu.S +++ b/sys/arm/arm/fusu.S @@ -53,49 +53,51 @@ __FBSDID("$FreeBSD$"); #endif /* - * fuword(caddr_t uaddr); - * Fetch an int from the user's address space. + * casueword32(volatile uint32_t *base, uint32_t oldval, uint32_t *oldvalp, + * uint32_t newval); */ -ENTRY(casuword) -EENTRY_NP(casuword32) - GET_PCB(r3) - ldr r3, [r3] +ENTRY(casueword) +EENTRY_NP(casueword32) + stmfd sp!, {r4, r5, r6} + + ldr r4, =(VM_MAXUSER_ADDRESS-3) + cmp r0, r4 + mvncs r0, #0 + bcs 2f + + GET_PCB(r6) + ldr r6, [r6] #ifdef DIAGNOSTIC - teq r3, #0x00000000 + teq r6, #0x00000000 + ldmfdeq sp!, {r4, r5, r6} beq .Lfusupcbfault #endif - stmfd sp!, {r4, r5} + adr r4, .Lcasuwordfault - str r4, [r3, #PCB_ONFAULT] + str r4, [r6, #PCB_ONFAULT] + #if __ARM_ARCH >= 6 1: - cmp r0, #KERNBASE - mvnhs r0, #0 - bhs 2f - - ldrex r5, [r0] - cmp r5, r1 - movne r0, r5 - bne 2f - strex r5, r2, [r0] - cmp r5, #0 - bne 1b + ldrex r4, [r0] + cmp r4, r1 + strexeq r5, r3, [r0] + cmpeq r5, #1 + beq 1b #else - ldrt r5, [r0] - cmp r5, r1 - movne r0, r5 - strteq r2, [r0] + ldrt r4, [r0] + cmp r4, r1 + strteq r3, [r0] #endif - moveq r0, r1 + str r4, [r2] + mov r0, #0 + str r0, [r6, #PCB_ONFAULT] 2: - ldmfd sp!, {r4, r5} - mov r1, #0x00000000 - str r1, [r3, #PCB_ONFAULT] + ldmfd sp!, {r4, r5, r6} RET -EEND(casuword32) -END(casuword) +EEND(casueword32) +END(casueword) /* * Handle faults from casuword. Clean up and return -1. @@ -103,18 +105,23 @@ END(casuword) .Lcasuwordfault: mov r0, #0x00000000 - str r0, [r3, #PCB_ONFAULT] - mvn r0, #0x00000000 - ldmfd sp!, {r4, r5} + str r0, [r6, #PCB_ONFAULT] + mvn r0, #0 + ldmfd sp!, {r4, r5, r6} RET /* - * fuword(caddr_t uaddr); + * fueword(caddr_t uaddr, long *val); * Fetch an int from the user's address space. */ -ENTRY(fuword) -EENTRY_NP(fuword32) +ENTRY(fueword) +EENTRY_NP(fueword32) + ldr r3, =(VM_MAXUSER_ADDRESS-3) + cmp r0, r3 + mvncs r0, #0 + RETc(cs) + GET_PCB(r2) ldr r2, [r2] @@ -123,14 +130,14 @@ EENTRY_NP(fuword32) beq .Lfusupcbfault #endif - adr r1, .Lfusufault - str r1, [r2, #PCB_ONFAULT] + adr r3, .Lfusufault + str r3, [r2, #PCB_ONFAULT] ldrt r3, [r0] + str r3, [r1] - mov r1, #0x00000000 - str r1, [r2, #PCB_ONFAULT] - mov r0, r3 + mov r0, #0x00000000 + str r0, [r2, #PCB_ONFAULT] RET EEND(fuword32) END(fuword) @@ -141,6 +148,11 @@ END(fuword) */ ENTRY(fusword) + ldr r3, =(VM_MAXUSER_ADDRESS-1) + cmp r0, r3 + mvncs r0, #0 + RETc(cs) + GET_PCB(r2) ldr r2, [r2] @@ -171,6 +183,11 @@ END(fusword) */ ENTRY(fuswintr) + ldr r3, =(VM_MAXUSER_ADDRESS-1) + cmp r0, r3 + mvncs r0, #0 + RETc(cs) + ldr r2, Lblock_userspace_access ldr r2, [r2] teq r2, #0 @@ -217,6 +234,11 @@ _C_LABEL(block_userspace_access): */ ENTRY(fubyte) + ldr r3, =VM_MAXUSER_ADDRESS + cmp r0, r3 + mvncs r0, #0 + RETc(cs) + GET_PCB(r2) ldr r2, [r2] @@ -282,6 +304,11 @@ fusupcbfaulttext: ENTRY(suword) EENTRY_NP(suword32) + ldr r3, =(VM_MAXUSER_ADDRESS-3) + cmp r0, r3 + mvncs r0, #0 + RETc(cs) + GET_PCB(r2) ldr r2, [r2] @@ -308,6 +335,11 @@ END(suword) */ ENTRY(suswintr) + ldr r3, =(VM_MAXUSER_ADDRESS-1) + cmp r0, r3 + mvncs r0, #0 + RETc(cs) + ldr r2, Lblock_userspace_access ldr r2, [r2] teq r2, #0 @@ -345,6 +377,11 @@ END(suswintr) */ ENTRY(susword) + ldr r3, =(VM_MAXUSER_ADDRESS-1) + cmp r0, r3 + mvncs r0, #0 + RETc(cs) + GET_PCB(r2) ldr r2, [r2] @@ -376,6 +413,11 @@ END(susword) */ ENTRY(subyte) + ldr r3, =VM_MAXUSER_ADDRESS + cmp r0, r3 + mvncs r0, #0 + RETc(cs) + GET_PCB(r2) ldr r2, [r2] diff --git a/sys/arm/arm/genassym.c b/sys/arm/arm/genassym.c index 900fc418394..4e2b7e91f23 100644 --- a/sys/arm/arm/genassym.c +++ b/sys/arm/arm/genassym.c @@ -161,6 +161,7 @@ ASSYM(P_VMSPACE, offsetof(struct proc, p_vmspace)); ASSYM(VM_PMAP, offsetof(struct vmspace, vm_pmap)); ASSYM(PM_ACTIVE, offsetof(struct pmap, pm_active)); ASSYM(PC_CPUID, offsetof(struct pcpu, pc_cpuid)); +ASSYM(VM_MAXUSER_ADDRESS, VM_MAXUSER_ADDRESS); ASSYM(DCACHE_LINE_SIZE, offsetof(struct cpuinfo, dcache_line_size)); ASSYM(DCACHE_LINE_MASK, offsetof(struct cpuinfo, dcache_line_mask)); diff --git a/sys/arm/arm/gic.c b/sys/arm/arm/gic.c index 6fcb7e08221..a1ce111f9f1 100644 --- a/sys/arm/arm/gic.c +++ b/sys/arm/arm/gic.c @@ -34,18 +34,27 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + +#include "opt_platform.h" + #include #include #include #include #include #include +#include #include #include #include #include #include #include +#include +#ifdef ARM_INTRNG +#include +#endif #include #include #include @@ -55,6 +64,10 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef ARM_INTRNG +#include "pic_if.h" +#endif + /* We are using GICv2 register naming */ /* Distributor Registers */ @@ -83,8 +96,8 @@ __FBSDID("$FreeBSD$"); #define GICC_ABPR 0x001C /* v1 ICCABPR */ #define GICC_IIDR 0x00FC /* v1 ICCIIDR*/ -#define GIC_FIRST_IPI 0 /* Irqs 0-15 are SGIs/IPIs. */ -#define GIC_LAST_IPI 15 +#define GIC_FIRST_SGI 0 /* Irqs 0-15 are SGIs/IPIs. */ +#define GIC_LAST_SGI 15 #define GIC_FIRST_PPI 16 /* Irqs 16-31 are private (per */ #define GIC_LAST_PPI 31 /* core) peripheral interrupts. */ #define GIC_FIRST_SPI 32 /* Irqs 32+ are shared peripherals. */ @@ -102,8 +115,18 @@ __FBSDID("$FreeBSD$"); #define GIC_DEFAULT_ICFGR_INIT 0x00000000 #endif +#ifdef ARM_INTRNG +static u_int gic_irq_cpu; +static int arm_gic_intr(void *); +static int arm_gic_bind(device_t dev, struct arm_irqsrc *isrc); +#endif + struct arm_gic_softc { device_t gic_dev; +#ifdef ARM_INTRNG + void * gic_intrhand; + struct arm_irqsrc ** gic_irqs; +#endif struct resource * gic_res[3]; bus_space_tag_t gic_c_bst; bus_space_tag_t gic_d_bst; @@ -117,10 +140,13 @@ struct arm_gic_softc { static struct resource_spec arm_gic_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */ +#ifdef ARM_INTRNG + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, /* Parent interrupt */ +#endif { -1, 0 } }; -static struct arm_gic_softc *arm_gic_sc = NULL; +static struct arm_gic_softc *gic_sc = NULL; #define gic_c_read_4(_sc, _reg) \ bus_space_read_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg)) @@ -128,12 +154,16 @@ static struct arm_gic_softc *arm_gic_sc = NULL; bus_space_write_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg), (_val)) #define gic_d_read_4(_sc, _reg) \ bus_space_read_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg)) +#define gic_d_write_1(_sc, _reg, _val) \ + bus_space_write_1((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) #define gic_d_write_4(_sc, _reg, _val) \ bus_space_write_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) +#ifndef ARM_INTRNG static int gic_config_irq(int irq, enum intr_trigger trig, enum intr_polarity pol); static void gic_post_filter(void *); +#endif static struct ofw_compat_data compat_data[] = { {"arm,gic", true}, /* Non-standard, used in FreeBSD dts. */ @@ -159,6 +189,72 @@ arm_gic_probe(device_t dev) return (BUS_PROBE_DEFAULT); } +#ifdef ARM_INTRNG +static inline void +gic_irq_unmask(struct arm_gic_softc *sc, u_int irq) +{ + + gic_d_write_4(sc, GICD_ISENABLER(irq >> 5), (1UL << (irq & 0x1F))); +} + +static inline void +gic_irq_mask(struct arm_gic_softc *sc, u_int irq) +{ + + gic_d_write_4(sc, GICD_ICENABLER(irq >> 5), (1UL << (irq & 0x1F))); +} +#endif + +#ifdef SMP +#ifdef ARM_INTRNG +static void +arm_gic_init_secondary(device_t dev) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + struct arm_irqsrc *isrc; + u_int irq; + + for (irq = 0; irq < sc->nirqs; irq += 4) + gic_d_write_4(sc, GICD_IPRIORITYR(irq >> 2), 0); + + /* Set all the interrupts to be in Group 0 (secure) */ + for (irq = 0; irq < sc->nirqs; irq += 32) { + gic_d_write_4(sc, GICD_IGROUPR(irq >> 5), 0); + } + + /* Enable CPU interface */ + gic_c_write_4(sc, GICC_CTLR, 1); + + /* Set priority mask register. */ + gic_c_write_4(sc, GICC_PMR, 0xff); + + /* Enable interrupt distribution */ + gic_d_write_4(sc, GICD_CTLR, 0x01); + + /* Unmask attached SGI interrupts. */ + for (irq = GIC_FIRST_SGI; irq <= GIC_LAST_SGI; irq++) { + isrc = sc->gic_irqs[irq]; + if (isrc != NULL && isrc->isrc_handlers != 0) { + CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); + gic_irq_unmask(sc, irq); + } + } + + /* Unmask attached PPI interrupts. */ + for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) { + isrc = sc->gic_irqs[irq]; + if (isrc == NULL || isrc->isrc_handlers == 0) + continue; + if (isrc->isrc_flags & ARM_ISRCF_BOUND) { + if (CPU_ISSET(PCPU_GET(cpuid), &isrc->isrc_cpu)) + gic_irq_unmask(sc, irq); + } else { + CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); + gic_irq_unmask(sc, irq); + } + } +} +#else static void arm_gic_init_secondary(device_t dev) { @@ -189,12 +285,28 @@ arm_gic_init_secondary(device_t dev) gic_d_write_4(sc, GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F))); gic_d_write_4(sc, GICD_ISENABLER(30 >> 5), (1UL << (30 & 0x1F))); } - +#endif /* ARM_INTRNG */ +#endif /* SMP */ + +#ifndef ARM_INTRNG int -gic_decode_fdt(uint32_t iparent, uint32_t *intr, int *interrupt, +gic_decode_fdt(phandle_t iparent, pcell_t *intr, int *interrupt, int *trig, int *pol) { static u_int num_intr_cells; + static phandle_t self; + struct ofw_compat_data *ocd; + + if (self == 0) { + for (ocd = compat_data; ocd->ocd_str != NULL; ocd++) { + if (fdt_is_compatible(iparent, ocd->ocd_str)) { + self = iparent; + break; + } + } + } + if (self != iparent) + return (ENXIO); if (num_intr_cells == 0) { if (OF_searchencprop(OF_node_from_xref(iparent), @@ -234,6 +346,19 @@ gic_decode_fdt(uint32_t iparent, uint32_t *intr, int *interrupt, } return (0); } +#endif + +#ifdef ARM_INTRNG +static inline intptr_t +gic_xref(device_t dev) +{ +#ifdef FDT + return (OF_xref_from_node(ofw_bus_get_node(dev))); +#else + return (0); +#endif +} +#endif static int arm_gic_attach(device_t dev) @@ -241,8 +366,11 @@ arm_gic_attach(device_t dev) struct arm_gic_softc *sc; int i; uint32_t icciidr; +#ifdef ARM_INTRNG + intptr_t xref = gic_xref(dev); +#endif - if (arm_gic_sc) + if (gic_sc) return (ENXIO); sc = device_get_softc(dev); @@ -253,7 +381,7 @@ arm_gic_attach(device_t dev) } sc->gic_dev = dev; - arm_gic_sc = sc; + gic_sc = sc; /* Initialize mutex */ mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN); @@ -273,9 +401,14 @@ arm_gic_attach(device_t dev) sc->nirqs = gic_d_read_4(sc, GICD_TYPER); sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1); +#ifdef ARM_INTRNG + sc->gic_irqs = malloc(sc->nirqs * sizeof (*sc->gic_irqs), M_DEVBUF, + M_WAITOK | M_ZERO); +#else /* Set up function pointers */ arm_post_filter = gic_post_filter; arm_config_irq = gic_config_irq; +#endif icciidr = gic_c_read_4(sc, GICC_IIDR); device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x irqs %u\n", @@ -311,10 +444,455 @@ arm_gic_attach(device_t dev) /* Enable interrupt distribution */ gic_d_write_4(sc, GICD_CTLR, 0x01); +#ifndef ARM_INTRNG + return (0); +#else + /* + * Now, when everything is initialized, it's right time to + * register interrupt controller to interrupt framefork. + */ + if (arm_pic_register(dev, xref) != 0) { + device_printf(dev, "could not register PIC\n"); + goto cleanup; + } + if (sc->gic_res[2] == NULL) { + if (arm_pic_claim_root(dev, xref, arm_gic_intr, sc, + GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) { + device_printf(dev, "could not set PIC as a root\n"); + arm_pic_unregister(dev, xref); + goto cleanup; + } + } else { + if (bus_setup_intr(dev, sc->gic_res[2], INTR_TYPE_CLK, + arm_gic_intr, NULL, sc, &sc->gic_intrhand)) { + device_printf(dev, "could not setup irq handler\n"); + arm_pic_unregister(dev, xref); + goto cleanup; + } + } + + return (0); + +cleanup: + /* + * XXX - not implemented arm_gic_detach() should be called ! + */ + if (sc->gic_irqs != NULL) + free(sc->gic_irqs, M_DEVBUF); + bus_release_resources(dev, arm_gic_spec, sc->gic_res); + return(ENXIO); +#endif +} + +#ifdef ARM_INTRNG +static int +arm_gic_intr(void *arg) +{ + struct arm_gic_softc *sc = arg; + struct arm_irqsrc *isrc; + uint32_t irq_active_reg, irq; + struct trapframe *tf; + + irq_active_reg = gic_c_read_4(sc, GICC_IAR); + irq = irq_active_reg & 0x3FF; + + /* + * 1. We do EOI here because recent read value from active interrupt + * register must be used for it. Another approach is to save this + * value into associated interrupt source. + * 2. EOI must be done on same CPU where interrupt has fired. Thus + * we must ensure that interrupted thread does not migrate to + * another CPU. + * 3. EOI cannot be delayed by any preemption which could happen on + * critical_exit() used in MI intr code, when interrupt thread is + * scheduled. See next point. + * 4. IPI_RENDEZVOUS assumes that no preemption is permitted during + * an action and any use of critical_exit() could break this + * assumption. See comments within smp_rendezvous_action(). + * 5. We always return FILTER_HANDLED as this is an interrupt + * controller dispatch function. Otherwise, in cascaded interrupt + * case, the whole interrupt subtree would be masked. + */ + + if (irq >= sc->nirqs) { + device_printf(sc->gic_dev, "Spurious interrupt detected\n"); + gic_c_write_4(sc, GICC_EOIR, irq_active_reg); + return (FILTER_HANDLED); + } + + tf = curthread->td_intr_frame; +dispatch_irq: + isrc = sc->gic_irqs[irq]; + if (isrc == NULL) { + device_printf(sc->gic_dev, "Stray interrupt %u detected\n", irq); + gic_irq_mask(sc, irq); + gic_c_write_4(sc, GICC_EOIR, irq_active_reg); + goto next_irq; + } + + /* + * Note that GIC_FIRST_SGI is zero and is not used in 'if' statement + * as compiler complains that comparing u_int >= 0 is always true. + */ + if (irq <= GIC_LAST_SGI) { +#ifdef SMP + /* Call EOI for all IPI before dispatch. */ + gic_c_write_4(sc, GICC_EOIR, irq_active_reg); + arm_ipi_dispatch(isrc, tf); + goto next_irq; +#else + printf("SGI %u on UP system detected\n", irq - GIC_FIRST_SGI); + gic_c_write_4(sc, GICC_EOIR, irq_active_reg); + goto next_irq; +#endif + } + + if (isrc->isrc_trig == INTR_TRIGGER_EDGE) + gic_c_write_4(sc, GICC_EOIR, irq_active_reg); + + arm_irq_dispatch(isrc, tf); + +next_irq: +// arm_irq_memory_barrier(irq); /* XXX */ +// irq_active_reg = gic_c_read_4(sc, GICC_IAR); +// irq = irq_active_reg & 0x3FF; + if (0 && irq < sc->nirqs) + goto dispatch_irq; + + return (FILTER_HANDLED); +} + +static int +gic_attach_isrc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int irq) +{ + const char *name; + + /* + * 1. The link between ISRC and controller must be set atomically. + * 2. Just do things only once in rare case when consumers + * of shared interrupt came here at the same moment. + */ + mtx_lock_spin(&sc->mutex); + if (sc->gic_irqs[irq] != NULL) { + mtx_unlock_spin(&sc->mutex); + return (sc->gic_irqs[irq] == isrc ? 0 : EEXIST); + } + sc->gic_irqs[irq] = isrc; + isrc->isrc_data = irq; + mtx_unlock_spin(&sc->mutex); + + name = device_get_nameunit(sc->gic_dev); + if (irq <= GIC_LAST_SGI) + arm_irq_set_name(isrc, "%s,i%u", name, irq - GIC_FIRST_SGI); + else if (irq <= GIC_LAST_PPI) + arm_irq_set_name(isrc, "%s,p%u", name, irq - GIC_FIRST_PPI); + else + arm_irq_set_name(isrc, "%s,s%u", name, irq - GIC_FIRST_SPI); return (0); } +static int +gic_detach_isrc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int irq) +{ + + mtx_lock_spin(&sc->mutex); + if (sc->gic_irqs[irq] != isrc) { + mtx_unlock_spin(&sc->mutex); + return (sc->gic_irqs[irq] == NULL ? 0 : EINVAL); + } + sc->gic_irqs[irq] = NULL; + isrc->isrc_data = 0; + mtx_unlock_spin(&sc->mutex); + + arm_irq_set_name(isrc, ""); + return (0); +} + +static void +gic_config(struct arm_gic_softc *sc, u_int irq, enum intr_trigger trig, + enum intr_polarity pol) +{ + uint32_t reg; + uint32_t mask; + + if (irq < GIC_FIRST_SPI) + return; + + mtx_lock_spin(&sc->mutex); + + reg = gic_d_read_4(sc, GICD_ICFGR(irq >> 4)); + mask = (reg >> 2*(irq % 16)) & 0x3; + + if (pol == INTR_POLARITY_LOW) { + mask &= ~GICD_ICFGR_POL_MASK; + mask |= GICD_ICFGR_POL_LOW; + } else if (pol == INTR_POLARITY_HIGH) { + mask &= ~GICD_ICFGR_POL_MASK; + mask |= GICD_ICFGR_POL_HIGH; + } + + if (trig == INTR_TRIGGER_LEVEL) { + mask &= ~GICD_ICFGR_TRIG_MASK; + mask |= GICD_ICFGR_TRIG_LVL; + } else if (trig == INTR_TRIGGER_EDGE) { + mask &= ~GICD_ICFGR_TRIG_MASK; + mask |= GICD_ICFGR_TRIG_EDGE; + } + + /* Set mask */ + reg = reg & ~(0x3 << 2*(irq % 16)); + reg = reg | (mask << 2*(irq % 16)); + gic_d_write_4(sc, GICD_ICFGR(irq >> 4), reg); + + mtx_unlock_spin(&sc->mutex); +} + +static int +gic_bind(struct arm_gic_softc *sc, u_int irq, cpuset_t *cpus) +{ + uint32_t cpu, end, mask; + + end = min(mp_ncpus, 8); + for (cpu = end; cpu < MAXCPU; cpu++) + if (CPU_ISSET(cpu, cpus)) + return (EINVAL); + + for (mask = 0, cpu = 0; cpu < end; cpu++) + if (CPU_ISSET(cpu, cpus)) + mask |= 1 << cpu; + + gic_d_write_1(sc, GICD_ITARGETSR(0) + irq, mask); + return (0); +} + +static int +gic_irq_from_nspc(struct arm_gic_softc *sc, u_int type, u_int num, u_int *irqp) +{ + + switch (type) { + case ARM_IRQ_NSPC_PLAIN: + *irqp = num; + return (*irqp < sc->nirqs ? 0 : EINVAL); + + case ARM_IRQ_NSPC_IRQ: + *irqp = num + GIC_FIRST_PPI; + return (*irqp < sc->nirqs ? 0 : EINVAL); + + case ARM_IRQ_NSPC_IPI: + *irqp = num + GIC_FIRST_SGI; + return (*irqp < GIC_LAST_SGI ? 0 : EINVAL); + + default: + return (EINVAL); + } +} + +static int +gic_map_nspc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int *irqp) +{ + int error; + + error = gic_irq_from_nspc(sc, isrc->isrc_nspc_type, isrc->isrc_nspc_num, + irqp); + if (error != 0) + return (error); + return (gic_attach_isrc(sc, isrc, *irqp)); +} + +#ifdef FDT +static int +gic_map_fdt(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int *irqp) +{ + u_int irq, tripol; + enum intr_trigger trig; + enum intr_polarity pol; + int error; + + if (isrc->isrc_ncells == 1) { + irq = isrc->isrc_cells[0]; + pol = INTR_POLARITY_CONFORM; + trig = INTR_TRIGGER_CONFORM; + } else if (isrc->isrc_ncells == 3) { + if (isrc->isrc_cells[0] == 0) + irq = isrc->isrc_cells[1] + GIC_FIRST_SPI; + else + irq = isrc->isrc_cells[1] + GIC_FIRST_PPI; + + /* + * In intr[2], bits[3:0] are trigger type and level flags. + * 1 = low-to-high edge triggered + * 2 = high-to-low edge triggered + * 4 = active high level-sensitive + * 8 = active low level-sensitive + * The hardware only supports active-high-level or rising-edge. + */ + tripol = isrc->isrc_cells[2]; + if (tripol & 0x0a) { + printf("unsupported trigger/polarity configuration " + "0x%2x\n", tripol & 0x0f); + return (ENOTSUP); + } + pol = INTR_POLARITY_CONFORM; + if (tripol & 0x01) + trig = INTR_TRIGGER_EDGE; + else + trig = INTR_TRIGGER_LEVEL; + } else + return (EINVAL); + + if (irq >= sc->nirqs) + return (EINVAL); + + error = gic_attach_isrc(sc, isrc, irq); + if (error != 0) + return (error); + + isrc->isrc_nspc_type = ARM_IRQ_NSPC_PLAIN; + isrc->isrc_nspc_num = irq; + isrc->isrc_trig = trig; + isrc->isrc_pol = pol; + + *irqp = irq; + return (0); +} +#endif + +static int +arm_gic_register(device_t dev, struct arm_irqsrc *isrc, boolean_t *is_percpu) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + u_int irq; + int error; + + if (isrc->isrc_type == ARM_ISRCT_NAMESPACE) + error = gic_map_nspc(sc, isrc, &irq); +#ifdef FDT + else if (isrc->isrc_type == ARM_ISRCT_FDT) + error = gic_map_fdt(sc, isrc, &irq); +#endif + else + return (EINVAL); + + if (error == 0) + *is_percpu = irq < GIC_FIRST_SPI ? TRUE : FALSE; + return (error); +} + +static void +arm_gic_enable_intr(device_t dev, struct arm_irqsrc *isrc) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + u_int irq = isrc->isrc_data; + + if (isrc->isrc_trig == INTR_TRIGGER_CONFORM) + isrc->isrc_trig = INTR_TRIGGER_LEVEL; + + /* + * XXX - In case that per CPU interrupt is going to be enabled in time + * when SMP is already started, we need some IPI call which + * enables it on others CPUs. Further, it's more complicated as + * pic_enable_source() and pic_disable_source() should act on + * per CPU basis only. Thus, it should be solved here somehow. + */ + if (isrc->isrc_flags & ARM_ISRCF_PERCPU) + CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); + + gic_config(sc, irq, isrc->isrc_trig, isrc->isrc_pol); + arm_gic_bind(dev, isrc); +} + +static void +arm_gic_enable_source(device_t dev, struct arm_irqsrc *isrc) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + u_int irq = isrc->isrc_data; + + arm_irq_memory_barrier(irq); + gic_irq_unmask(sc, irq); +} + +static void +arm_gic_disable_source(device_t dev, struct arm_irqsrc *isrc) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + u_int irq = isrc->isrc_data; + + gic_irq_mask(sc, irq); +} + +static int +arm_gic_unregister(device_t dev, struct arm_irqsrc *isrc) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + u_int irq = isrc->isrc_data; + + return (gic_detach_isrc(sc, isrc, irq)); +} + +static void +arm_gic_pre_ithread(device_t dev, struct arm_irqsrc *isrc) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + + arm_gic_disable_source(dev, isrc); + gic_c_write_4(sc, GICC_EOIR, isrc->isrc_data); +} + +static void +arm_gic_post_ithread(device_t dev, struct arm_irqsrc *isrc) +{ + + arm_irq_memory_barrier(0); + arm_gic_enable_source(dev, isrc); +} + +static void +arm_gic_post_filter(device_t dev, struct arm_irqsrc *isrc) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + + /* EOI for edge-triggered done earlier. */ + if (isrc->isrc_trig == INTR_TRIGGER_EDGE) + return; + + arm_irq_memory_barrier(0); + gic_c_write_4(sc, GICC_EOIR, isrc->isrc_data); +} + +#ifdef SMP +static int +arm_gic_bind(device_t dev, struct arm_irqsrc *isrc) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + uint32_t irq = isrc->isrc_data; + + if (irq < GIC_FIRST_SPI) + return (EINVAL); + + if (CPU_EMPTY(&isrc->isrc_cpu)) { + gic_irq_cpu = arm_irq_next_cpu(gic_irq_cpu, &all_cpus); + CPU_SETOF(gic_irq_cpu, &isrc->isrc_cpu); + } + return (gic_bind(sc, irq, &isrc->isrc_cpu)); +} + +static void +arm_gic_ipi_send(device_t dev, struct arm_irqsrc *isrc, cpuset_t cpus) +{ + struct arm_gic_softc *sc = device_get_softc(dev); + uint32_t irq, val = 0, i; + + irq = isrc->isrc_data; + + for (i = 0; i < MAXCPU; i++) + if (CPU_ISSET(i, &cpus)) + val |= 1 << (16 + i); + + gic_d_write_4(sc, GICD_SGIR(0), val | irq); +} +#endif +#else static int arm_gic_next_irq(struct arm_gic_softc *sc, int last_irq) { @@ -327,7 +905,7 @@ arm_gic_next_irq(struct arm_gic_softc *sc, int last_irq) * bits (ie CPU number), not just the IRQ number, and we do not * have this information later. */ - if ((active_irq & 0x3ff) <= GIC_LAST_IPI) + if ((active_irq & 0x3ff) <= GIC_LAST_SGI) gic_c_write_4(sc, GICC_EOIR, active_irq); active_irq &= 0x3FF; @@ -400,7 +978,7 @@ arm_gic_mask(device_t dev, int irq) struct arm_gic_softc *sc = device_get_softc(dev); gic_d_write_4(sc, GICD_ICENABLER(irq >> 5), (1UL << (irq & 0x1F))); - gic_c_write_4(sc, GICC_EOIR, irq); + gic_c_write_4(sc, GICC_EOIR, irq); /* XXX - not allowed */ } static void @@ -408,7 +986,7 @@ arm_gic_unmask(device_t dev, int irq) { struct arm_gic_softc *sc = device_get_softc(dev); - if (irq > GIC_LAST_IPI) + if (irq > GIC_LAST_SGI) arm_irq_memory_barrier(irq); gic_d_write_4(sc, GICD_ISENABLER(irq >> 5), (1UL << (irq & 0x1F))); @@ -455,10 +1033,10 @@ arm_gic_ipi_clear(device_t dev, int ipi) static void gic_post_filter(void *arg) { - struct arm_gic_softc *sc = arm_gic_sc; + struct arm_gic_softc *sc = gic_sc; uintptr_t irq = (uintptr_t) arg; - if (irq > GIC_LAST_IPI) + if (irq > GIC_LAST_SGI) arm_irq_memory_barrier(irq); gic_c_write_4(sc, GICC_EOIR, irq); } @@ -467,64 +1045,81 @@ static int gic_config_irq(int irq, enum intr_trigger trig, enum intr_polarity pol) { - return (arm_gic_config(arm_gic_sc->gic_dev, irq, trig, pol)); + return (arm_gic_config(gic_sc->gic_dev, irq, trig, pol)); } void arm_mask_irq(uintptr_t nb) { - arm_gic_mask(arm_gic_sc->gic_dev, nb); + arm_gic_mask(gic_sc->gic_dev, nb); } void arm_unmask_irq(uintptr_t nb) { - arm_gic_unmask(arm_gic_sc->gic_dev, nb); + arm_gic_unmask(gic_sc->gic_dev, nb); } int arm_get_next_irq(int last_irq) { - return (arm_gic_next_irq(arm_gic_sc, last_irq)); -} - -void -arm_init_secondary_ic(void) -{ - - arm_gic_init_secondary(arm_gic_sc->gic_dev); + return (arm_gic_next_irq(gic_sc, last_irq)); } #ifdef SMP +void +arm_pic_init_secondary(void) +{ + + arm_gic_init_secondary(gic_sc->gic_dev); +} + void pic_ipi_send(cpuset_t cpus, u_int ipi) { - arm_gic_ipi_send(arm_gic_sc->gic_dev, cpus, ipi); + arm_gic_ipi_send(gic_sc->gic_dev, cpus, ipi); } int pic_ipi_read(int i) { - return (arm_gic_ipi_read(arm_gic_sc->gic_dev, i)); + return (arm_gic_ipi_read(gic_sc->gic_dev, i)); } void pic_ipi_clear(int ipi) { - arm_gic_ipi_clear(arm_gic_sc->gic_dev, ipi); + arm_gic_ipi_clear(gic_sc->gic_dev, ipi); } #endif +#endif /* ARM_INTRNG */ static device_method_t arm_gic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, arm_gic_probe), DEVMETHOD(device_attach, arm_gic_attach), +#ifdef ARM_INTRNG + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_source, arm_gic_disable_source), + DEVMETHOD(pic_enable_intr, arm_gic_enable_intr), + DEVMETHOD(pic_enable_source, arm_gic_enable_source), + DEVMETHOD(pic_post_filter, arm_gic_post_filter), + DEVMETHOD(pic_post_ithread, arm_gic_post_ithread), + DEVMETHOD(pic_pre_ithread, arm_gic_pre_ithread), + DEVMETHOD(pic_register, arm_gic_register), + DEVMETHOD(pic_unregister, arm_gic_unregister), +#ifdef SMP + DEVMETHOD(pic_bind, arm_gic_bind), + DEVMETHOD(pic_init_secondary, arm_gic_init_secondary), + DEVMETHOD(pic_ipi_send, arm_gic_ipi_send), +#endif +#endif { 0, 0 } }; diff --git a/sys/arm/arm/intrng.c b/sys/arm/arm/intrng.c new file mode 100644 index 00000000000..c30ec684bb3 --- /dev/null +++ b/sys/arm/arm/intrng.c @@ -0,0 +1,1458 @@ +/*- + * Copyright (c) 2012-2014 Jakub Wojciech Klama . + * Copyright (c) 2015 Svatopluk Kraus + * Copyright (c) 2015 Michal Meloun + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * ARM Interrupt Framework + * + * TODO: - to support IPI (PPI) enabling on other CPUs if already started + * - to complete things for removable PICs + */ + +#include "opt_ddb.h" +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef DDB +#include +#endif + +#include "pic_if.h" + +#define INTRNAME_LEN (2*MAXCOMLEN + 1) + +#ifdef DEBUG +#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ + printf(fmt,##args); } while (0) +#else +#define debugf(fmt, args...) +#endif + +MALLOC_DECLARE(M_INTRNG); +MALLOC_DEFINE(M_INTRNG, "intrng", "ARM interrupt handling"); + +/* Main ARM interrupt handler called from assembler -> 'hidden' for C code. */ +void arm_irq_handler(struct trapframe *tf); + +/* Root interrupt controller stuff. */ +static struct arm_irqsrc *irq_root_isrc; +static device_t irq_root_dev; +static arm_irq_filter_t *irq_root_filter; +static void *irq_root_arg; +static u_int irq_root_ipicount; + +/* Interrupt controller definition. */ +struct arm_pic { + SLIST_ENTRY(arm_pic) pic_next; + intptr_t pic_xref; /* hardware identification */ + device_t pic_dev; +}; + +static struct mtx pic_list_lock; +static SLIST_HEAD(, arm_pic) pic_list; + +static struct arm_pic *pic_lookup(device_t dev, intptr_t xref); + +/* Interrupt source definition. */ +static struct mtx isrc_table_lock; +static struct arm_irqsrc *irq_sources[NIRQ]; +u_int irq_next_free; + +#define IRQ_INVALID nitems(irq_sources) + +#ifdef SMP +static boolean_t irq_assign_cpu = FALSE; + +static struct arm_irqsrc ipi_sources[ARM_IPI_COUNT]; +static u_int ipi_next_num; +#endif + +/* + * - 2 counters for each I/O interrupt. + * - MAXCPU counters for each IPI counters for SMP. + */ +#ifdef SMP +#define INTRCNT_COUNT (NIRQ * 2 + ARM_IPI_COUNT * MAXCPU) +#else +#define INTRCNT_COUNT (NIRQ * 2) +#endif + +/* Data for MI statistics reporting. */ +u_long intrcnt[INTRCNT_COUNT]; +char intrnames[INTRCNT_COUNT * INTRNAME_LEN]; +size_t sintrcnt = sizeof(intrcnt); +size_t sintrnames = sizeof(intrnames); +static u_int intrcnt_index; + +/* + * Interrupt framework initialization routine. + */ +static void +arm_irq_init(void *dummy __unused) +{ + + SLIST_INIT(&pic_list); + mtx_init(&pic_list_lock, "arm pic list", NULL, MTX_DEF); + mtx_init(&isrc_table_lock, "arm isrc table", NULL, MTX_DEF); +} +SYSINIT(arm_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, arm_irq_init, NULL); + +static void +intrcnt_setname(const char *name, int index) +{ + + snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s", + INTRNAME_LEN - 1, name); +} + +/* + * Update name for interrupt source with interrupt event. + */ +static void +intrcnt_updatename(struct arm_irqsrc *isrc) +{ + + /* QQQ: What about stray counter name? */ + mtx_assert(&isrc_table_lock, MA_OWNED); + intrcnt_setname(isrc->isrc_event->ie_fullname, isrc->isrc_index); +} + +/* + * Virtualization for interrupt source interrupt counter increment. + */ +static inline void +isrc_increment_count(struct arm_irqsrc *isrc) +{ + + /* + * XXX - It should be atomic for PPI interrupts. It was proven that + * the lost is measurable easily for timer PPI interrupts. + */ + isrc->isrc_count[0]++; + /*atomic_add_long(&isrc->isrc_count[0], 1);*/ +} + +/* + * Virtualization for interrupt source interrupt stray counter increment. + */ +static inline void +isrc_increment_straycount(struct arm_irqsrc *isrc) +{ + + isrc->isrc_count[1]++; +} + +/* + * Virtualization for interrupt source interrupt name update. + */ +static void +isrc_update_name(struct arm_irqsrc *isrc, const char *name) +{ + char str[INTRNAME_LEN]; + + mtx_assert(&isrc_table_lock, MA_OWNED); + + if (name != NULL) { + snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name); + intrcnt_setname(str, isrc->isrc_index); + snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name, + name); + intrcnt_setname(str, isrc->isrc_index + 1); + } else { + snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name); + intrcnt_setname(str, isrc->isrc_index); + snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name); + intrcnt_setname(str, isrc->isrc_index + 1); + } +} + +/* + * Virtualization for interrupt source interrupt counters setup. + */ +static void +isrc_setup_counters(struct arm_irqsrc *isrc) +{ + u_int index; + + /* + * XXX - it does not work well with removable controllers and + * interrupt sources !!! + */ + index = atomic_fetchadd_int(&intrcnt_index, 2); + isrc->isrc_index = index; + isrc->isrc_count = &intrcnt[index]; + isrc_update_name(isrc, NULL); +} + +#ifdef SMP +/* + * Virtualization for interrupt source IPI counter increment. + */ +static inline void +isrc_increment_ipi_count(struct arm_irqsrc *isrc, u_int cpu) +{ + + isrc->isrc_count[cpu]++; +} + +/* + * Virtualization for interrupt source IPI counters setup. + */ +static void +isrc_setup_ipi_counters(struct arm_irqsrc *isrc, const char *name) +{ + u_int index, i; + char str[INTRNAME_LEN]; + + index = atomic_fetchadd_int(&intrcnt_index, MAXCPU); + isrc->isrc_index = index; + isrc->isrc_count = &intrcnt[index]; + + for (i = 0; i < MAXCPU; i++) { + /* + * We do not expect any race in IPI case here, + * so locking is not needed. + */ + snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name); + intrcnt_setname(str, index + i); + } +} +#endif + +/* + * Main ARM interrupt dispatch handler. It's called straight + * from the assembler, where CPU interrupt is served. + */ +void +arm_irq_handler(struct trapframe *tf) +{ + struct trapframe * oldframe; + struct thread * td; + + KASSERT(irq_root_filter != NULL, ("%s: no filter", __func__)); + + PCPU_INC(cnt.v_intr); + critical_enter(); + td = curthread; + oldframe = td->td_intr_frame; + td->td_intr_frame = tf; + irq_root_filter(irq_root_arg); + td->td_intr_frame = oldframe; + critical_exit(); +} + +/* + * ARM interrupt controller dispatch function for interrupts. It should + * be called straight from the interrupt controller, when associated interrupt + * source is learned. + */ +void +arm_irq_dispatch(struct arm_irqsrc *isrc, struct trapframe *tf) +{ + + KASSERT(isrc != NULL, ("%s: no source", __func__)); + + isrc_increment_count(isrc); + +#ifdef INTR_SOLO + if (isrc->isrc_filter != NULL) { + int error; + error = isrc->isrc_filter(isrc->isrc_arg, tf); + PIC_POST_FILTER(isrc->isrc_dev, isrc); + if (error == FILTER_HANDLED) + return; + } else +#endif + if (isrc->isrc_event != NULL) { + if (intr_event_handle(isrc->isrc_event, tf) == 0) + return; + } + + isrc_increment_straycount(isrc); + PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc); + + device_printf(isrc->isrc_dev, "stray irq <%s> disabled", + isrc->isrc_name); +} + +/* + * Allocate interrupt source. + */ +static struct arm_irqsrc * +isrc_alloc(u_int type, u_int extsize) +{ + struct arm_irqsrc *isrc; + + isrc = malloc(sizeof(*isrc) + extsize, M_INTRNG, M_WAITOK | M_ZERO); + isrc->isrc_irq = IRQ_INVALID; /* just to be safe */ + isrc->isrc_type = type; + isrc->isrc_nspc_type = ARM_IRQ_NSPC_NONE; + isrc->isrc_trig = INTR_TRIGGER_CONFORM; + isrc->isrc_pol = INTR_POLARITY_CONFORM; + CPU_ZERO(&isrc->isrc_cpu); + return (isrc); +} + +/* + * Free interrupt source. + */ +static void +isrc_free(struct arm_irqsrc *isrc) +{ + + free(isrc, M_INTRNG); +} + +void +arm_irq_set_name(struct arm_irqsrc *isrc, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(isrc->isrc_name, ARM_ISRC_NAMELEN, fmt, ap); + va_end(ap); +} + +/* + * Alloc unique interrupt number (resource handle) for interrupt source. + * + * There could be various strategies how to allocate free interrupt number + * (resource handle) for new interrupt source. + * + * 1. Handles are always allocated forward, so handles are not recycled + * immediately. However, if only one free handle left which is reused + * constantly... + */ +static int +isrc_alloc_irq_locked(struct arm_irqsrc *isrc) +{ + u_int maxirqs, irq; + + mtx_assert(&isrc_table_lock, MA_OWNED); + + maxirqs = nitems(irq_sources); + if (irq_next_free >= maxirqs) + return (ENOSPC); + + for (irq = irq_next_free; irq < maxirqs; irq++) { + if (irq_sources[irq] == NULL) + goto found; + } + for (irq = 0; irq < irq_next_free; irq++) { + if (irq_sources[irq] == NULL) + goto found; + } + + irq_next_free = maxirqs; + return (ENOSPC); + +found: + isrc->isrc_irq = irq; + irq_sources[irq] = isrc; + + arm_irq_set_name(isrc, "irq%u", irq); + isrc_setup_counters(isrc); + + irq_next_free = irq + 1; + if (irq_next_free >= maxirqs) + irq_next_free = 0; + return (0); +} +#ifdef notyet +/* + * Free unique interrupt number (resource handle) from interrupt source. + */ +static int +isrc_free_irq(struct arm_irqsrc *isrc) +{ + u_int maxirqs; + + mtx_assert(&isrc_table_lock, MA_NOTOWNED); + + maxirqs = nitems(irq_sources); + if (isrc->isrc_irq >= maxirqs) + return (EINVAL); + + mtx_lock(&isrc_table_lock); + if (irq_sources[isrc->isrc_irq] != isrc) { + mtx_unlock(&isrc_table_lock); + return (EINVAL); + } + + irq_sources[isrc->isrc_irq] = NULL; + isrc->isrc_irq = IRQ_INVALID; /* just to be safe */ + mtx_unlock(&isrc_table_lock); + + return (0); +} +#endif +/* + * Lookup interrupt source by interrupt number (resource handle). + */ +static struct arm_irqsrc * +isrc_lookup(u_int irq) +{ + + if (irq < nitems(irq_sources)) + return (irq_sources[irq]); + return (NULL); +} + +/* + * Lookup interrupt source by namespace description. + */ +static struct arm_irqsrc * +isrc_namespace_lookup(device_t dev, uint16_t type, uint16_t num) +{ + u_int irq; + struct arm_irqsrc *isrc; + + mtx_assert(&isrc_table_lock, MA_OWNED); + + for (irq = 0; irq < nitems(irq_sources); irq++) { + isrc = irq_sources[irq]; + if (isrc != NULL && isrc->isrc_dev == dev && + isrc->isrc_nspc_type == type && isrc->isrc_nspc_num == num) + return (isrc); + } + return (NULL); +} + +/* + * Map interrupt source according to namespace into framework. If such mapping + * does not exist, create it. Return unique interrupt number (resource handle) + * associated with mapped interrupt source. + */ +u_int +arm_namespace_map_irq(device_t dev, uint16_t type, uint16_t num) +{ + struct arm_irqsrc *isrc, *new_isrc; + int error; + + new_isrc = isrc_alloc(ARM_ISRCT_NAMESPACE, 0); + + mtx_lock(&isrc_table_lock); + isrc = isrc_namespace_lookup(dev, type, num); + if (isrc != NULL) { + mtx_unlock(&isrc_table_lock); + isrc_free(new_isrc); + return (isrc->isrc_irq); /* already mapped */ + } + + error = isrc_alloc_irq_locked(new_isrc); + if (error != 0) { + mtx_unlock(&isrc_table_lock); + isrc_free(new_isrc); + return (IRQ_INVALID); /* no space left */ + } + + new_isrc->isrc_dev = dev; + new_isrc->isrc_nspc_type = type; + new_isrc->isrc_nspc_num = num; + mtx_unlock(&isrc_table_lock); + + return (new_isrc->isrc_irq); +} + +#ifdef FDT +/* + * Lookup interrupt source by FDT description. + */ +static struct arm_irqsrc * +isrc_fdt_lookup(intptr_t xref, pcell_t *cells, u_int ncells) +{ + u_int irq, cellsize; + struct arm_irqsrc *isrc; + + mtx_assert(&isrc_table_lock, MA_OWNED); + + cellsize = ncells * sizeof(*cells); + for (irq = 0; irq < nitems(irq_sources); irq++) { + isrc = irq_sources[irq]; + if (isrc != NULL && isrc->isrc_type == ARM_ISRCT_FDT && + isrc->isrc_xref == xref && isrc->isrc_ncells == ncells && + memcmp(isrc->isrc_cells, cells, cellsize) == 0) + return (isrc); + } + return (NULL); +} + +/* + * Map interrupt source according to FDT data into framework. If such mapping + * does not exist, create it. Return unique interrupt number (resource handle) + * associated with mapped interrupt source. + */ +u_int +arm_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells) +{ + struct arm_irqsrc *isrc, *new_isrc; + u_int cellsize; + intptr_t xref; + int error; + + xref = (intptr_t)node; /* It's so simple for now. */ + + cellsize = ncells * sizeof(*cells); + new_isrc = isrc_alloc(ARM_ISRCT_FDT, cellsize); + + mtx_lock(&isrc_table_lock); + isrc = isrc_fdt_lookup(xref, cells, ncells); + if (isrc != NULL) { + mtx_unlock(&isrc_table_lock); + isrc_free(new_isrc); + return (isrc->isrc_irq); /* already mapped */ + } + + error = isrc_alloc_irq_locked(new_isrc); + if (error != 0) { + mtx_unlock(&isrc_table_lock); + isrc_free(new_isrc); + return (IRQ_INVALID); /* no space left */ + } + + new_isrc->isrc_xref = xref; + new_isrc->isrc_ncells = ncells; + memcpy(new_isrc->isrc_cells, cells, cellsize); + mtx_unlock(&isrc_table_lock); + + return (new_isrc->isrc_irq); +} +#endif + +/* + * Register interrupt source into interrupt controller. + */ +static int +isrc_register(struct arm_irqsrc *isrc) +{ + struct arm_pic *pic; + boolean_t is_percpu; + int error; + + if (isrc->isrc_flags & ARM_ISRCF_REGISTERED) + return (0); + + if (isrc->isrc_dev == NULL) { + pic = pic_lookup(NULL, isrc->isrc_xref); + if (pic == NULL || pic->pic_dev == NULL) + return (ESRCH); + isrc->isrc_dev = pic->pic_dev; + } + + error = PIC_REGISTER(isrc->isrc_dev, isrc, &is_percpu); + if (error != 0) + return (error); + + mtx_lock(&isrc_table_lock); + isrc->isrc_flags |= ARM_ISRCF_REGISTERED; + if (is_percpu) + isrc->isrc_flags |= ARM_ISRCF_PERCPU; + isrc_update_name(isrc, NULL); + mtx_unlock(&isrc_table_lock); + return (0); +} + +#ifdef INTR_SOLO +/* + * Setup filter into interrupt source. + */ +static int +iscr_setup_filter(struct arm_irqsrc *isrc, const char *name, + arm_irq_filter_t *filter, void *arg, void **cookiep) +{ + + if (filter == NULL) + return (EINVAL); + + mtx_lock(&isrc_table_lock); + /* + * Make sure that we do not mix the two ways + * how we handle interrupt sources. + */ + if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { + mtx_unlock(&isrc_table_lock); + return (EBUSY); + } + isrc->isrc_filter = filter; + isrc->isrc_arg = arg; + isrc_update_name(isrc, name); + mtx_unlock(&isrc_table_lock); + + *cookiep = isrc; + return (0); +} +#endif + +/* + * Interrupt source pre_ithread method for MI interrupt framework. + */ +static void +arm_isrc_pre_ithread(void *arg) +{ + struct arm_irqsrc *isrc = arg; + + PIC_PRE_ITHREAD(isrc->isrc_dev, isrc); +} + +/* + * Interrupt source post_ithread method for MI interrupt framework. + */ +static void +arm_isrc_post_ithread(void *arg) +{ + struct arm_irqsrc *isrc = arg; + + PIC_POST_ITHREAD(isrc->isrc_dev, isrc); +} + +/* + * Interrupt source post_filter method for MI interrupt framework. + */ +static void +arm_isrc_post_filter(void *arg) +{ + struct arm_irqsrc *isrc = arg; + + PIC_POST_FILTER(isrc->isrc_dev, isrc); +} + +/* + * Interrupt source assign_cpu method for MI interrupt framework. + */ +static int +arm_isrc_assign_cpu(void *arg, int cpu) +{ +#ifdef SMP + struct arm_irqsrc *isrc = arg; + int error; + + if (isrc->isrc_dev != irq_root_dev) + return (EINVAL); + + mtx_lock(&isrc_table_lock); + if (cpu == NOCPU) { + CPU_ZERO(&isrc->isrc_cpu); + isrc->isrc_flags &= ~ARM_ISRCF_BOUND; + } else { + CPU_SETOF(cpu, &isrc->isrc_cpu); + isrc->isrc_flags |= ARM_ISRCF_BOUND; + } + + /* + * In NOCPU case, it's up to PIC to either leave ISRC on same CPU or + * re-balance it to another CPU or enable it on more CPUs. However, + * PIC is expected to change isrc_cpu appropriately to keep us well + * informed if the call is successfull. + */ + if (irq_assign_cpu) { + error = PIC_BIND(isrc->isrc_dev, isrc); + if (error) { + CPU_ZERO(&isrc->isrc_cpu); + mtx_unlock(&isrc_table_lock); + return (error); + } + } + mtx_unlock(&isrc_table_lock); + return (0); +#else + return (EOPNOTSUPP); +#endif +} + +/* + * Create interrupt event for interrupt source. + */ +static int +isrc_event_create(struct arm_irqsrc *isrc) +{ + struct intr_event *ie; + int error; + + error = intr_event_create(&ie, isrc, 0, isrc->isrc_irq, + arm_isrc_pre_ithread, arm_isrc_post_ithread, arm_isrc_post_filter, + arm_isrc_assign_cpu, "%s:", isrc->isrc_name); + if (error) + return (error); + + mtx_lock(&isrc_table_lock); + /* + * Make sure that we do not mix the two ways + * how we handle interrupt sources. Let contested event wins. + */ + if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) { + mtx_unlock(&isrc_table_lock); + intr_event_destroy(ie); + return (isrc->isrc_event != NULL ? EBUSY : 0); + } + isrc->isrc_event = ie; + mtx_unlock(&isrc_table_lock); + + return (0); +} +#ifdef notyet +/* + * Destroy interrupt event for interrupt source. + */ +static void +isrc_event_destroy(struct arm_irqsrc *isrc) +{ + struct intr_event *ie; + + mtx_lock(&isrc_table_lock); + ie = isrc->isrc_event; + isrc->isrc_event = NULL; + mtx_unlock(&isrc_table_lock); + + if (ie != NULL) + intr_event_destroy(ie); +} +#endif +/* + * Add handler to interrupt source. + */ +static int +isrc_add_handler(struct arm_irqsrc *isrc, const char *name, + driver_filter_t filter, driver_intr_t handler, void *arg, + enum intr_type flags, void **cookiep) +{ + int error; + + if (isrc->isrc_event == NULL) { + error = isrc_event_create(isrc); + if (error) + return (error); + } + + error = intr_event_add_handler(isrc->isrc_event, name, filter, handler, + arg, intr_priority(flags), flags, cookiep); + if (error == 0) { + mtx_lock(&isrc_table_lock); + intrcnt_updatename(isrc); + mtx_unlock(&isrc_table_lock); + } + + return (error); +} + +/* + * Lookup interrupt controller locked. + */ +static struct arm_pic * +pic_lookup_locked(device_t dev, intptr_t xref) +{ + struct arm_pic *pic; + + mtx_assert(&pic_list_lock, MA_OWNED); + + SLIST_FOREACH(pic, &pic_list, pic_next) { + if (pic->pic_xref != xref) + continue; + if (pic->pic_xref != 0 || pic->pic_dev == dev) + return (pic); + } + return (NULL); +} + +/* + * Lookup interrupt controller. + */ +static struct arm_pic * +pic_lookup(device_t dev, intptr_t xref) +{ + struct arm_pic *pic; + + mtx_lock(&pic_list_lock); + pic = pic_lookup_locked(dev, xref); + mtx_unlock(&pic_list_lock); + + return (pic); +} + +/* + * Create interrupt controller. + */ +static struct arm_pic * +pic_create(device_t dev, intptr_t xref) +{ + struct arm_pic *pic; + + mtx_lock(&pic_list_lock); + pic = pic_lookup_locked(dev, xref); + if (pic != NULL) { + mtx_unlock(&pic_list_lock); + return (pic); + } + pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO); + pic->pic_xref = xref; + pic->pic_dev = dev; + SLIST_INSERT_HEAD(&pic_list, pic, pic_next); + mtx_unlock(&pic_list_lock); + + return (pic); +} +#ifdef notyet +/* + * Destroy interrupt controller. + */ +static void +pic_destroy(device_t dev, intptr_t xref) +{ + struct arm_pic *pic; + + mtx_lock(&pic_list_lock); + pic = pic_lookup_locked(dev, xref); + if (pic == NULL) { + mtx_unlock(&pic_list_lock); + return; + } + SLIST_REMOVE(&pic_list, pic, arm_pic, pic_next); + mtx_unlock(&pic_list_lock); + + free(pic, M_INTRNG); +} +#endif +/* + * Register interrupt controller. + */ +int +arm_pic_register(device_t dev, intptr_t xref) +{ + struct arm_pic *pic; + + pic = pic_create(dev, xref); + if (pic == NULL) + return (ENOMEM); + if (pic->pic_dev != dev) + return (EINVAL); /* XXX it could be many things. */ + + debugf("PIC %p registered for %s \n", pic, + device_get_nameunit(dev), xref); + return (0); +} + +/* + * Unregister interrupt controller. + */ +int +arm_pic_unregister(device_t dev, intptr_t xref) +{ + + panic("%s: not implemented", __func__); +} + +/* + * Mark interrupt controller (itself) as a root one. + * + * Note that only an interrupt controller can really know its position + * in interrupt controller's tree. So root PIC must claim itself as a root. + * + * In FDT case, according to ePAPR approved version 1.1 from 08 April 2011, + * page 30: + * "The root of the interrupt tree is determined when traversal + * of the interrupt tree reaches an interrupt controller node without + * an interrupts property and thus no explicit interrupt parent." + */ +int +arm_pic_claim_root(device_t dev, intptr_t xref, arm_irq_filter_t *filter, + void *arg, u_int ipicount) +{ + int error; + u_int rootirq; + + if (pic_lookup(dev, xref) == NULL) { + device_printf(dev, "not registered\n"); + return (EINVAL); + } + if (filter == NULL) { + device_printf(dev, "filter missing\n"); + return (EINVAL); + } + + /* + * Only one interrupt controllers could be on the root for now. + * Note that we further suppose that there is not threaded interrupt + * routine (handler) on the root. See arm_irq_handler(). + */ + if (irq_root_dev != NULL) { + device_printf(dev, "another root already set\n"); + return (EBUSY); + } + + rootirq = arm_namespace_map_irq(device_get_parent(dev), 0, 0); + if (rootirq == IRQ_INVALID) { + device_printf(dev, "failed to map an irq for the root pic\n"); + return (ENOMEM); + } + + /* Create the isrc. */ + irq_root_isrc = isrc_lookup(rootirq); + + /* XXX "register" with the PIC. We are the "pic" here, so fake it. */ + irq_root_isrc->isrc_flags |= ARM_ISRCF_REGISTERED; + + error = arm_irq_add_handler(device_get_parent(dev), + (void*)filter, NULL, arg, rootirq, INTR_TYPE_CLK, NULL); + if (error != 0) { + device_printf(dev, "failed to install root pic handler\n"); + return (error); + } + irq_root_dev = dev; + irq_root_filter = filter; + irq_root_arg = arg; + irq_root_ipicount = ipicount; + + debugf("irq root set to %s\n", device_get_nameunit(dev)); + return (0); +} + +int +arm_irq_add_handler(device_t dev, driver_filter_t filt, driver_intr_t hand, + void *arg, u_int irq, int flags, void **cookiep) +{ + const char *name; + struct arm_irqsrc *isrc; + int error; + + name = device_get_nameunit(dev); + +#ifdef INTR_SOLO + /* + * Standard handling is done thru MI interrupt framework. However, + * some interrupts could request solely own special handling. This + * non standard handling can be used for interrupt controllers without + * handler (filter only), so in case that interrupt controllers are + * chained, MI interrupt framework is called only in leaf controller. + * + * Note that root interrupt controller routine is served as well, + * however in arm_irq_handler(), i.e. main system dispatch routine. + */ + if (flags & INTR_SOLO && hand != NULL) { + debugf("irq %u cannot solo on %s\n", irq, name); + return (EINVAL); + } +#endif + + isrc = isrc_lookup(irq); + if (isrc == NULL) { + debugf("irq %u without source on %s\n", irq, name); + return (EINVAL); + } + + error = isrc_register(isrc); + if (error != 0) { + debugf("irq %u map error %d on %s\n", irq, error, name); + return (error); + } + +#ifdef INTR_SOLO + if (flags & INTR_SOLO) { + error = iscr_setup_filter(isrc, name, (arm_irq_filter_t *)filt, + arg, cookiep); + debugf("irq %u setup filter error %d on %s\n", irq, error, + name); + } else +#endif + { + error = isrc_add_handler(isrc, name, filt, hand, arg, flags, + cookiep); + debugf("irq %u add handler error %d on %s\n", irq, error, name); + } + if (error != 0) + return (error); + + mtx_lock(&isrc_table_lock); + isrc->isrc_handlers++; + if (isrc->isrc_handlers == 1) { + PIC_ENABLE_INTR(isrc->isrc_dev, isrc); + PIC_ENABLE_SOURCE(isrc->isrc_dev, isrc); + } + mtx_unlock(&isrc_table_lock); + return (0); +} + +int +arm_irq_remove_handler(device_t dev, u_int irq, void *cookie) +{ + struct arm_irqsrc *isrc; + int error; + + isrc = isrc_lookup(irq); + if (isrc == NULL || isrc->isrc_handlers == 0) + return (EINVAL); + + if (isrc->isrc_filter != NULL) { + if (isrc != cookie) + return (EINVAL); + + mtx_lock(&isrc_table_lock); + isrc->isrc_filter = NULL; + isrc->isrc_arg = NULL; + isrc->isrc_handlers = 0; + PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc); + PIC_DISABLE_INTR(isrc->isrc_dev, isrc); + isrc_update_name(isrc, NULL); + mtx_unlock(&isrc_table_lock); + return (0); + } + + if (isrc != intr_handler_source(cookie)) + return (EINVAL); + + error = intr_event_remove_handler(cookie); + if (error == 0) { + mtx_lock(&isrc_table_lock); + isrc->isrc_handlers--; + if (isrc->isrc_handlers == 0) { + PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc); + PIC_DISABLE_INTR(isrc->isrc_dev, isrc); + } + intrcnt_updatename(isrc); + mtx_unlock(&isrc_table_lock); + } + return (error); +} + +int +arm_irq_config(u_int irq, enum intr_trigger trig, enum intr_polarity pol) +{ + struct arm_irqsrc *isrc; + + isrc = isrc_lookup(irq); + if (isrc == NULL) + return (EINVAL); + + if (isrc->isrc_handlers != 0) + return (EBUSY); /* interrrupt is enabled (active) */ + + /* + * Once an interrupt is enabled, we do not change its configuration. + * A controller PIC_ENABLE_INTR() method is called when an interrupt + * is going to be enabled. In this method, a controller should setup + * the interrupt according to saved configuration parameters. + */ + isrc->isrc_trig = trig; + isrc->isrc_pol = pol; + + return (0); +} + +int +arm_irq_describe(u_int irq, void *cookie, const char *descr) +{ + struct arm_irqsrc *isrc; + int error; + + isrc = isrc_lookup(irq); + if (isrc == NULL || isrc->isrc_handlers == 0) + return (EINVAL); + + if (isrc->isrc_filter != NULL) { + if (isrc != cookie) + return (EINVAL); + + mtx_lock(&isrc_table_lock); + isrc_update_name(isrc, descr); + mtx_unlock(&isrc_table_lock); + return (0); + } + + error = intr_event_describe_handler(isrc->isrc_event, cookie, descr); + if (error == 0) { + mtx_lock(&isrc_table_lock); + intrcnt_updatename(isrc); + mtx_unlock(&isrc_table_lock); + } + return (error); +} + +#ifdef SMP +int +arm_irq_bind(u_int irq, int cpu) +{ + struct arm_irqsrc *isrc; + + isrc = isrc_lookup(irq); + if (isrc == NULL || isrc->isrc_handlers == 0) + return (EINVAL); + + if (isrc->isrc_filter != NULL) + return (arm_isrc_assign_cpu(isrc, cpu)); + + return (intr_event_bind(isrc->isrc_event, cpu)); +} + +/* + * Return the CPU that the next interrupt source should use. + * For now just returns the next CPU according to round-robin. + */ +u_int +arm_irq_next_cpu(u_int last_cpu, cpuset_t *cpumask) +{ + + if (!irq_assign_cpu || mp_ncpus == 1) + return (PCPU_GET(cpuid)); + + do { + last_cpu++; + if (last_cpu > mp_maxid) + last_cpu = 0; + } while (!CPU_ISSET(last_cpu, cpumask)); + return (last_cpu); +} + +/* + * Distribute all the interrupt sources among the available + * CPUs once the AP's have been launched. + */ +static void +arm_irq_shuffle(void *arg __unused) +{ + struct arm_irqsrc *isrc; + u_int i; + + if (mp_ncpus == 1) + return; + + mtx_lock(&isrc_table_lock); + irq_assign_cpu = TRUE; + for (i = 0; i < NIRQ; i++) { + isrc = irq_sources[i]; + if (isrc == NULL || isrc->isrc_handlers == 0 || + isrc->isrc_flags & ARM_ISRCF_PERCPU) + continue; + + if (isrc->isrc_event != NULL && + isrc->isrc_flags & ARM_ISRCF_BOUND && + isrc->isrc_event->ie_cpu != CPU_FFS(&isrc->isrc_cpu) - 1) + panic("%s: CPU inconsistency", __func__); + + if ((isrc->isrc_flags & ARM_ISRCF_BOUND) == 0) + CPU_ZERO(&isrc->isrc_cpu); /* start again */ + + /* + * We are in wicked position here if the following call fails + * for bound ISRC. The best thing we can do is to clear + * isrc_cpu so inconsistency with ie_cpu will be detectable. + */ + if (PIC_BIND(isrc->isrc_dev, isrc) != 0) + CPU_ZERO(&isrc->isrc_cpu); + } + mtx_unlock(&isrc_table_lock); +} +SYSINIT(arm_irq_shuffle, SI_SUB_SMP, SI_ORDER_SECOND, arm_irq_shuffle, NULL); + +#else +u_int +arm_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask) +{ + + return (PCPU_GET(cpuid)); +} +#endif + +void dosoftints(void); +void +dosoftints(void) +{ +} + +/* + * arm_irq_memory_barrier() + * + * Ensure all writes to device memory have reached devices before proceeding. + * + * This is intended to be called from the post-filter and post-thread routines + * of an interrupt controller implementation. A peripheral device driver should + * use bus_space_barrier() if it needs to ensure a write has reached the + * hardware for some reason other than clearing interrupt conditions. + * + * The need for this function arises from the ARM weak memory ordering model. + * Writes to locations mapped with the Device attribute bypass any caches, but + * are buffered. Multiple writes to the same device will be observed by that + * device in the order issued by the cpu. Writes to different devices may + * appear at those devices in a different order than issued by the cpu. That + * is, if the cpu writes to device A then device B, the write to device B could + * complete before the write to device A. + * + * Consider a typical device interrupt handler which services the interrupt and + * writes to a device status-acknowledge register to clear the interrupt before + * returning. That write is posted to the L2 controller which "immediately" + * places it in a store buffer and automatically drains that buffer. This can + * be less immediate than you'd think... There may be no free slots in the store + * buffers, so an existing buffer has to be drained first to make room. The + * target bus may be busy with other traffic (such as DMA for various devices), + * delaying the drain of the store buffer for some indeterminate time. While + * all this delay is happening, execution proceeds on the CPU, unwinding its way + * out of the interrupt call stack to the point where the interrupt driver code + * is ready to EOI and unmask the interrupt. The interrupt controller may be + * accessed via a faster bus than the hardware whose handler just ran; the write + * to unmask and EOI the interrupt may complete quickly while the device write + * to ack and clear the interrupt source is still lingering in a store buffer + * waiting for access to a slower bus. With the interrupt unmasked at the + * interrupt controller but still active at the device, as soon as interrupts + * are enabled on the core the device re-interrupts immediately: now you've got + * a spurious interrupt on your hands. + * + * The right way to fix this problem is for every device driver to use the + * proper bus_space_barrier() calls in its interrupt handler. For ARM a single + * barrier call at the end of the handler would work. This would have to be + * done to every driver in the system, not just arm-specific drivers. + * + * Another potential fix is to map all device memory as Strongly-Ordered rather + * than Device memory, which takes the store buffers out of the picture. This + * has a pretty big impact on overall system performance, because each strongly + * ordered memory access causes all L2 store buffers to be drained. + * + * A compromise solution is to have the interrupt controller implementation call + * this function to establish a barrier between writes to the interrupt-source + * device and writes to the interrupt controller device. + * + * This takes the interrupt number as an argument, and currently doesn't use it. + * The plan is that maybe some day there is a way to flag certain interrupts as + * "memory barrier safe" and we can avoid this overhead with them. + */ +void +arm_irq_memory_barrier(uintptr_t irq) +{ + + dsb(); + cpu_l2cache_drain_writebuf(); +} + +#ifdef SMP +/* + * Lookup IPI source. + */ +static struct arm_irqsrc * +arm_ipi_lookup(u_int ipi) +{ + + if (ipi >= ARM_IPI_COUNT) + panic("%s: no such IPI %u", __func__, ipi); + + return (&ipi_sources[ipi]); +} + +/* + * ARM interrupt controller dispatch function for IPIs. It should + * be called straight from the interrupt controller, when associated + * interrupt source is learned. Or from anybody who has an interrupt + * source mapped. + */ +void +arm_ipi_dispatch(struct arm_irqsrc *isrc, struct trapframe *tf) +{ + void *arg; + + KASSERT(isrc != NULL, ("%s: no source", __func__)); + + isrc_increment_ipi_count(isrc, PCPU_GET(cpuid)); + + /* + * Supply ipi filter with trapframe argument + * if none is registered. + */ + arg = isrc->isrc_arg != NULL ? isrc->isrc_arg : tf; + isrc->isrc_ipifilter(arg); +} + +/* + * Map IPI into interrupt controller. + * + * Not SMP coherent. + */ +static int +ipi_map(struct arm_irqsrc *isrc, u_int ipi) +{ + boolean_t is_percpu; + int error; + + if (ipi >= ARM_IPI_COUNT) + panic("%s: no such IPI %u", __func__, ipi); + + KASSERT(irq_root_dev != NULL, ("%s: no root attached", __func__)); + + isrc->isrc_type = ARM_ISRCT_NAMESPACE; + isrc->isrc_nspc_type = ARM_IRQ_NSPC_IPI; + isrc->isrc_nspc_num = ipi_next_num; + + error = PIC_REGISTER(irq_root_dev, isrc, &is_percpu); + + debugf("ipi %u mapped to %u on %s - error %d\n", ipi, ipi_next_num, + device_get_nameunit(irq_root_dev), error); + + if (error == 0) { + isrc->isrc_dev = irq_root_dev; + ipi_next_num++; + } + return (error); +} + +/* + * Setup IPI handler to interrupt source. + * + * Note that there could be more ways how to send and receive IPIs + * on a platform like fast interrupts for example. In that case, + * one can call this function with ASIF_NOALLOC flag set and then + * call arm_ipi_dispatch() when appropriate. + * + * Not SMP coherent. + */ +int +arm_ipi_set_handler(u_int ipi, const char *name, arm_ipi_filter_t *filter, + void *arg, u_int flags) +{ + struct arm_irqsrc *isrc; + int error; + + if (filter == NULL) + return(EINVAL); + + isrc = arm_ipi_lookup(ipi); + if (isrc->isrc_ipifilter != NULL) + return (EEXIST); + + if ((flags & AISHF_NOALLOC) == 0) { + error = ipi_map(isrc, ipi); + if (error != 0) + return (error); + } + + isrc->isrc_ipifilter = filter; + isrc->isrc_arg = arg; + isrc->isrc_handlers = 1; + isrc_setup_ipi_counters(isrc, name); + + if (isrc->isrc_dev != NULL) { + mtx_lock(&isrc_table_lock); + PIC_ENABLE_INTR(isrc->isrc_dev, isrc); + PIC_ENABLE_SOURCE(isrc->isrc_dev, isrc); + mtx_unlock(&isrc_table_lock); + } + return (0); +} + +/* + * Send IPI thru interrupt controller. + */ +void +pic_ipi_send(cpuset_t cpus, u_int ipi) +{ + struct arm_irqsrc *isrc; + + isrc = arm_ipi_lookup(ipi); + + KASSERT(irq_root_dev != NULL, ("%s: no root attached", __func__)); + PIC_IPI_SEND(irq_root_dev, isrc, cpus); +} + +/* + * Init interrupt controller on another CPU. + */ +void +arm_pic_init_secondary(void) +{ + + /* + * QQQ: Only root PIC is aware of other CPUs ??? + */ + KASSERT(irq_root_dev != NULL, ("%s: no root attached", __func__)); + + //mtx_lock(&isrc_table_lock); + PIC_INIT_SECONDARY(irq_root_dev); + //mtx_unlock(&isrc_table_lock); +} +#endif + +#ifdef DDB +DB_SHOW_COMMAND(irqs, db_show_irqs) +{ + u_int i, irqsum; + struct arm_irqsrc *isrc; + +#ifdef SMP + for (i = 0; i <= mp_maxid; i++) { + struct pcpu *pc; + u_int ipi, ipisum; + + pc = pcpu_find(i); + if (pc != NULL) { + for (ipisum = 0, ipi = 0; ipi < ARM_IPI_COUNT; ipi++) { + isrc = arm_ipi_lookup(ipi); + if (isrc->isrc_count != NULL) + ipisum += isrc->isrc_count[i]; + } + printf ("cpu%u: total %u ipis %u\n", i, + pc->pc_cnt.v_intr, ipisum); + } + } + db_printf("\n"); +#endif + + for (irqsum = 0, i = 0; i < NIRQ; i++) { + isrc = irq_sources[i]; + if (isrc == NULL) + continue; + + db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i, + isrc->isrc_name, isrc->isrc_cpu.__bits[0], + isrc->isrc_flags & ARM_ISRCF_BOUND ? " (bound)" : "", + isrc->isrc_count[0]); + irqsum += isrc->isrc_count[0]; + } + db_printf("irq total %u\n", irqsum); +} +#endif diff --git a/sys/arm/arm/mp_machdep.c b/sys/arm/arm/mp_machdep.c index 083e62efb8f..5d1043fde6e 100644 --- a/sys/arm/arm/mp_machdep.c +++ b/sys/arm/arm/mp_machdep.c @@ -74,7 +74,9 @@ volatile int mp_naps; /* Set to 1 once we're ready to let the APs out of the pen. */ volatile int aps_ready = 0; +#ifndef ARM_INTRNG static int ipi_handler(void *arg); +#endif void set_stackptrs(int cpu); /* Temporary variables for init_secondary() */ @@ -134,7 +136,6 @@ cpu_mp_start(void) else for (i = 1; i < mp_ncpus; i++) CPU_SET(i, &all_cpus); - } /* Introduce rest of cores to the world */ @@ -150,7 +151,9 @@ init_secondary(int cpu) { struct pcpu *pc; uint32_t loop_counter; +#ifndef ARM_INTRNG int start = 0, end = 0; +#endif #ifdef ARM_NEW_PMAP pmap_set_tex(); @@ -211,11 +214,12 @@ init_secondary(int cpu) mtx_unlock_spin(&ap_boot_mtx); +#ifndef ARM_INTRNG /* Enable ipi */ #ifdef IPI_IRQ_START start = IPI_IRQ_START; #ifdef IPI_IRQ_END - end = IPI_IRQ_END; + end = IPI_IRQ_END; #else end = IPI_IRQ_START; #endif @@ -223,6 +227,7 @@ init_secondary(int cpu) for (int i = start; i <= end; i++) arm_unmask_irq(i); +#endif /* INTRNG */ enable_interrupts(PSR_I); loop_counter = 0; @@ -245,6 +250,108 @@ init_secondary(int cpu) /* NOTREACHED */ } +#ifdef ARM_INTRNG +static void +ipi_rendezvous(void *dummy __unused) +{ + + CTR0(KTR_SMP, "IPI_RENDEZVOUS"); + smp_rendezvous_action(); +} + +static void +ipi_ast(void *dummy __unused) +{ + + CTR0(KTR_SMP, "IPI_AST"); +} + +static void +ipi_stop(void *dummy __unused) +{ + u_int cpu; + + /* + * IPI_STOP_HARD is mapped to IPI_STOP. + */ + CTR0(KTR_SMP, "IPI_STOP or IPI_STOP_HARD"); + + cpu = PCPU_GET(cpuid); + savectx(&stoppcbs[cpu]); + + /* + * CPUs are stopped when entering the debugger and at + * system shutdown, both events which can precede a + * panic dump. For the dump to be correct, all caches + * must be flushed and invalidated, but on ARM there's + * no way to broadcast a wbinv_all to other cores. + * Instead, we have each core do the local wbinv_all as + * part of stopping the core. The core requesting the + * stop will do the l2 cache flush after all other cores + * have done their l1 flushes and stopped. + */ + cpu_idcache_wbinv_all(); + + /* Indicate we are stopped */ + CPU_SET_ATOMIC(cpu, &stopped_cpus); + + /* Wait for restart */ + while (!CPU_ISSET(cpu, &started_cpus)) + cpu_spinwait(); + + CPU_CLR_ATOMIC(cpu, &started_cpus); + CPU_CLR_ATOMIC(cpu, &stopped_cpus); + CTR0(KTR_SMP, "IPI_STOP (restart)"); +} + +static void +ipi_preempt(void *arg) +{ + struct trapframe *oldframe; + struct thread *td; + + critical_enter(); + td = curthread; + td->td_intr_nesting_level++; + oldframe = td->td_intr_frame; + td->td_intr_frame = (struct trapframe *)arg; + + CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__); + sched_preempt(td); + + td->td_intr_frame = oldframe; + td->td_intr_nesting_level--; + critical_exit(); +} + +static void +ipi_hardclock(void *arg) +{ + struct trapframe *oldframe; + struct thread *td; + + critical_enter(); + td = curthread; + td->td_intr_nesting_level++; + oldframe = td->td_intr_frame; + td->td_intr_frame = (struct trapframe *)arg; + + CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__); + hardclockintr(); + + td->td_intr_frame = oldframe; + td->td_intr_nesting_level--; + critical_exit(); +} + +static void +ipi_tlb(void *dummy __unused) +{ + + CTR1(KTR_SMP, "%s: IPI_TLB", __func__); + cpufuncs.cf_tlb_flushID(); +} +#else static int ipi_handler(void *arg) { @@ -320,15 +427,28 @@ ipi_handler(void *arg) return (FILTER_HANDLED); } +#endif static void release_aps(void *dummy __unused) { uint32_t loop_counter; +#ifndef ARM_INTRNG int start = 0, end = 0; +#endif if (mp_ncpus == 1) return; + +#ifdef ARM_INTRNG + arm_ipi_set_handler(IPI_RENDEZVOUS, "rendezvous", ipi_rendezvous, NULL, 0); + arm_ipi_set_handler(IPI_AST, "ast", ipi_ast, NULL, 0); + arm_ipi_set_handler(IPI_STOP, "stop", ipi_stop, NULL, 0); + arm_ipi_set_handler(IPI_PREEMPT, "preempt", ipi_preempt, NULL, 0); + arm_ipi_set_handler(IPI_HARDCLOCK, "hardclock", ipi_hardclock, NULL, 0); + arm_ipi_set_handler(IPI_TLB, "tlb", ipi_tlb, NULL, 0); + +#else #ifdef IPI_IRQ_START start = IPI_IRQ_START; #ifdef IPI_IRQ_END @@ -353,6 +473,7 @@ release_aps(void *dummy __unused) /* Enable ipi */ arm_unmask_irq(i); } +#endif atomic_store_rel_int(&aps_ready, 1); /* Wake the other threads up */ #if __ARM_ARCH >= 7 diff --git a/sys/arm/arm/nexus.c b/sys/arm/arm/nexus.c index def6c1ad3cc..e2deeb0e29f 100644 --- a/sys/arm/arm/nexus.c +++ b/sys/arm/arm/nexus.c @@ -85,8 +85,17 @@ static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); static int nexus_activate_resource(device_t, device_t, int, int, struct resource *); +#ifdef ARM_INTRNG +#ifdef SMP +static int nexus_bind_intr(device_t, device_t, struct resource *, int); +#endif +#endif static int nexus_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol); +#ifdef ARM_INTRNG +static int nexus_describe_intr(device_t dev, device_t child, + struct resource *irq, void *cookie, const char *descr); +#endif static int nexus_deactivate_resource(device_t, device_t, int, int, struct resource *); static int nexus_release_resource(device_t, device_t, int, int, @@ -115,6 +124,12 @@ static device_method_t nexus_methods[] = { DEVMETHOD(bus_release_resource, nexus_release_resource), DEVMETHOD(bus_setup_intr, nexus_setup_intr), DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), +#ifdef ARM_INTRNG + DEVMETHOD(bus_describe_intr, nexus_describe_intr), +#ifdef SMP + DEVMETHOD(bus_bind_intr, nexus_bind_intr), +#endif +#endif #ifdef FDT DEVMETHOD(ofw_bus_map_intr, nexus_ofw_map_intr), #endif @@ -251,9 +266,12 @@ nexus_config_intr(device_t dev, int irq, enum intr_trigger trig, { int ret = ENODEV; +#ifdef ARM_INTRNG + ret = arm_irq_config(irq, trig, pol); +#else if (arm_config_irq) ret = (*arm_config_irq)(irq, trig, pol); - +#endif return (ret); } @@ -267,9 +285,14 @@ nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, flags |= INTR_EXCL; for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) { +#ifdef ARM_INTRNG + arm_irq_add_handler(child, filt, intr, arg, irq, flags, + cookiep); +#else arm_setup_irqhandler(device_get_nameunit(child), filt, intr, arg, irq, flags, cookiep); arm_unmask_irq(irq); +#endif } return (0); } @@ -278,9 +301,31 @@ static int nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) { +#ifdef ARM_INTRNG + return (arm_irq_remove_handler(child, rman_get_start(r), ih)); +#else return (arm_remove_irqhandler(rman_get_start(r), ih)); +#endif } +#ifdef ARM_INTRNG +static int +nexus_describe_intr(device_t dev, device_t child, struct resource *irq, + void *cookie, const char *descr) +{ + + return (arm_irq_describe(rman_get_start(irq), cookie, descr)); +} + +#ifdef SMP +static int +nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu) +{ + + return (arm_irq_bind(rman_get_start(irq), cpu)); +} +#endif +#endif static int nexus_activate_resource(device_t bus, device_t child, int type, int rid, diff --git a/sys/arm/arm/pic_if.m b/sys/arm/arm/pic_if.m new file mode 100644 index 00000000000..d3002ea4956 --- /dev/null +++ b/sys/arm/arm/pic_if.m @@ -0,0 +1,124 @@ +#- +# Copyright (c) 2012 Jakub Wojciech Klama +# Copyright (c) 2015 Svatopluk Kraus +# Copyright (c) 2015 Michal Meloun +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include +#include +#include +#include + +INTERFACE pic; + +CODE { + static int null_pic_bind(device_t dev, struct arm_irqsrc *isrc) + { + return (EOPNOTSUPP); + } + + static void null_pic_disable_intr(device_t dev, struct arm_irqsrc *isrc) + { + return; + } + + static void null_pic_enable_intr(device_t dev, struct arm_irqsrc *isrc) + { + return; + } + + static void null_pic_init_secondary(device_t dev) + { + return; + } + + static void null_pic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi) + { + return; + } +}; + +METHOD int register { + device_t dev; + struct arm_irqsrc *isrc; + boolean_t *is_percpu; +}; + +METHOD int unregister { + device_t dev; + struct arm_irqsrc *isrc; +}; + +METHOD void disable_intr { + device_t dev; + struct arm_irqsrc *isrc; +} DEFAULT null_pic_disable_intr; + +METHOD void disable_source { + device_t dev; + struct arm_irqsrc *isrc; +}; + +METHOD void enable_source { + device_t dev; + struct arm_irqsrc *isrc; +}; + +METHOD void enable_intr { + device_t dev; + struct arm_irqsrc *isrc; +} DEFAULT null_pic_enable_intr; + +METHOD void pre_ithread { + device_t dev; + struct arm_irqsrc *isrc; +}; + +METHOD void post_ithread { + device_t dev; + struct arm_irqsrc *isrc; +}; + +METHOD void post_filter { + device_t dev; + struct arm_irqsrc *isrc; +}; + +METHOD int bind { + device_t dev; + struct arm_irqsrc *isrc; +} DEFAULT null_pic_bind; + +METHOD void init_secondary { + device_t dev; +} DEFAULT null_pic_init_secondary; + +METHOD void ipi_send { + device_t dev; + struct arm_irqsrc *isrc; + cpuset_t cpus; +} DEFAULT null_pic_ipi_send; diff --git a/sys/arm/conf/BEAGLEBONE b/sys/arm/conf/BEAGLEBONE index 48d79340631..59c72792bde 100644 --- a/sys/arm/conf/BEAGLEBONE +++ b/sys/arm/conf/BEAGLEBONE @@ -85,6 +85,7 @@ device random # Entropy device # GPIO device gpio device gpioled +device gpiobacklight # ADC support device ti_adc diff --git a/sys/arm/conf/IMX6 b/sys/arm/conf/IMX6 index fb293a9d6d6..cf2f34cdf37 100644 --- a/sys/arm/conf/IMX6 +++ b/sys/arm/conf/IMX6 @@ -22,6 +22,8 @@ ident IMX6 include "std.armv6" include "../freescale/imx/std.imx6" +options ARM_INTRNG + options SOC_IMX6 options HZ=500 # Scheduling quantum is 2 milliseconds. diff --git a/sys/arm/conf/PANDABOARD b/sys/arm/conf/PANDABOARD index d04b81a637f..fe62e899365 100644 --- a/sys/arm/conf/PANDABOARD +++ b/sys/arm/conf/PANDABOARD @@ -30,6 +30,8 @@ hints "PANDABOARD.hints" include "std.armv6" include "../ti/omap4/pandaboard/std.pandaboard" +options ARM_INTRNG # new interrupt framework + options HZ=100 options SCHED_ULE # ULE scheduler options PLATFORM diff --git a/sys/arm/freescale/imx/imx6_machdep.c b/sys/arm/freescale/imx/imx6_machdep.c index 2322cffab0d..ce936b69579 100644 --- a/sys/arm/freescale/imx/imx6_machdep.c +++ b/sys/arm/freescale/imx/imx6_machdep.c @@ -58,6 +58,7 @@ struct fdt_fixup_entry fdt_fixup_table[] = { static uint32_t gpio1_node; +#ifndef ARM_INTRNG /* * Work around the linux workaround for imx6 erratum 006687, in which some * ethernet interrupts don't go to the GPC and thus won't wake the system from @@ -91,6 +92,7 @@ fdt_pic_decode_t fdt_pic_table[] = { &imx6_decode_fdt, NULL }; +#endif static vm_offset_t imx6_lastaddr(platform_t plat) diff --git a/sys/arm/freescale/imx/imx6_mp.c b/sys/arm/freescale/imx/imx6_mp.c index b02588cfa22..8957c547ad5 100644 --- a/sys/arm/freescale/imx/imx6_mp.c +++ b/sys/arm/freescale/imx/imx6_mp.c @@ -69,7 +69,7 @@ void platform_mp_init_secondary(void) { - arm_init_secondary_ic(); + arm_pic_init_secondary(); } void diff --git a/sys/arm/freescale/imx/imx_gpio.c b/sys/arm/freescale/imx/imx_gpio.c index e70b710a6c1..3c10efa1c89 100644 --- a/sys/arm/freescale/imx/imx_gpio.c +++ b/sys/arm/freescale/imx/imx_gpio.c @@ -34,6 +34,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include @@ -44,8 +46,10 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include #include #include @@ -56,13 +60,9 @@ __FBSDID("$FreeBSD$"); #include "gpio_if.h" -#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define GPIO_LOCK_INIT(_sc) mtx_init(&_sc->sc_mtx, \ - device_get_nameunit(_sc->sc_dev), "imx_gpio", MTX_DEF) -#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); -#define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); -#define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); +#ifdef ARM_INTRNG +#include "pic_if.h" +#endif #define WRITE4(_sc, _r, _v) \ bus_space_write_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r), (_v)) @@ -95,13 +95,13 @@ struct imx51_gpio_softc { device_t dev; device_t sc_busdev; struct mtx sc_mtx; - struct resource *sc_res[11]; /* 1 x mem, 2 x IRQ, 8 x IRQ */ - void *gpio_ih[11]; /* 1 ptr is not a big waste */ - int sc_l_irq; /* Last irq resource */ + struct resource *sc_res[3]; /* 1 x mem, 2 x IRQ */ + void *gpio_ih[2]; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; int gpio_npins; struct gpio_pin gpio_pins[NGPIO]; + struct arm_irqsrc *gpio_pic_irqsrc[NGPIO]; }; static struct ofw_compat_data compat_data[] = { @@ -118,18 +118,6 @@ static struct resource_spec imx_gpio_spec[] = { { -1, 0 } }; -static struct resource_spec imx_gpio0irq_spec[] = { - { SYS_RES_IRQ, 2, RF_ACTIVE }, - { SYS_RES_IRQ, 3, RF_ACTIVE }, - { SYS_RES_IRQ, 4, RF_ACTIVE }, - { SYS_RES_IRQ, 5, RF_ACTIVE }, - { SYS_RES_IRQ, 6, RF_ACTIVE }, - { SYS_RES_IRQ, 7, RF_ACTIVE }, - { SYS_RES_IRQ, 8, RF_ACTIVE }, - { SYS_RES_IRQ, 9, RF_ACTIVE }, - { -1, 0 } -}; - /* * Helpers */ @@ -142,7 +130,6 @@ static void imx51_gpio_pin_configure(struct imx51_gpio_softc *, static int imx51_gpio_probe(device_t); static int imx51_gpio_attach(device_t); static int imx51_gpio_detach(device_t); -static int imx51_gpio_intr(void *); /* * GPIO interface @@ -157,12 +144,258 @@ static int imx51_gpio_pin_set(device_t, uint32_t, unsigned int); static int imx51_gpio_pin_get(device_t, uint32_t, unsigned int *); static int imx51_gpio_pin_toggle(device_t, uint32_t pin); +#ifdef ARM_INTRNG +/* + * this is teardown_intr + */ +static void +gpio_pic_disable_intr(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + u_int irq; + + sc = device_get_softc(dev); + irq = isrc->isrc_data; + + // XXX Not sure this is necessary + mtx_lock_spin(&sc->sc_mtx); + CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq)); + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); + mtx_unlock_spin(&sc->sc_mtx); +} + +/* + * this is mask_intr + */ +static void +gpio_pic_disable_source(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + + sc = device_get_softc(dev); + + mtx_lock_spin(&sc->sc_mtx); + CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << isrc->isrc_data)); + mtx_unlock_spin(&sc->sc_mtx); +} + +/* + * this is setup_intr + */ +static void +gpio_pic_enable_intr(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + int icfg; + u_int irq, reg, shift, wrk; + + sc = device_get_softc(dev); + + if (isrc->isrc_trig == INTR_TRIGGER_LEVEL) { + if (isrc->isrc_pol == INTR_POLARITY_LOW) + icfg = GPIO_ICR_COND_LOW; + else + icfg = GPIO_ICR_COND_HIGH; + } else { + if (isrc->isrc_pol == INTR_POLARITY_HIGH) + icfg = GPIO_ICR_COND_FALL; + else + icfg = GPIO_ICR_COND_RISE; + } + + irq = isrc->isrc_data; + if (irq < 16) { + reg = IMX_GPIO_ICR1_REG; + shift = 2 * irq; + } else { + reg = IMX_GPIO_ICR2_REG; + shift = 2 * (irq - 16); + } + + mtx_lock_spin(&sc->sc_mtx); + CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq)); + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); + wrk = READ4(sc, reg); + wrk &= ~(0x03 << shift); + wrk |= icfg << shift; + WRITE4(sc, reg, wrk); + mtx_unlock_spin(&sc->sc_mtx); +} + +/* + * this is unmask_intr + */ +static void +gpio_pic_enable_source(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + + sc = device_get_softc(dev); + + mtx_lock_spin(&sc->sc_mtx); + SET4(sc, IMX_GPIO_IMR_REG, (1U << isrc->isrc_data)); + mtx_unlock_spin(&sc->sc_mtx); +} + +static void +gpio_pic_post_filter(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + + sc = device_get_softc(dev); + + arm_irq_memory_barrier(0); + /* EOI. W1C reg so no r-m-w, no locking needed. */ + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << isrc->isrc_data)); +} + +static void +gpio_pic_post_ithread(device_t dev, struct arm_irqsrc *isrc) +{ + + arm_irq_memory_barrier(0); + gpio_pic_enable_source(dev, isrc); +} + +static void +gpio_pic_pre_ithread(device_t dev, struct arm_irqsrc *isrc) +{ + + gpio_pic_disable_source(dev, isrc); +} + +/* + * intrng calls this to make a new isrc known to us. + */ +static int +gpio_pic_register(device_t dev, struct arm_irqsrc *isrc, boolean_t *is_percpu) +{ + struct imx51_gpio_softc *sc; + u_int irq, tripol; + + sc = device_get_softc(dev); + + /* + * From devicetree/bindings/gpio/fsl-imx-gpio.txt: + * #interrupt-cells: 2. The first cell is the GPIO number. The second + * cell bits[3:0] is used to specify trigger type and level flags: + * 1 = low-to-high edge triggered. + * 2 = high-to-low edge triggered. + * 4 = active high level-sensitive. + * 8 = active low level-sensitive. + * We can do any single one of these modes, but nothing in combo. + */ + + if (isrc->isrc_ncells != 2) { + device_printf(sc->dev, "Invalid #interrupt-cells"); + return (EINVAL); + } + + irq = isrc->isrc_cells[0]; + tripol = isrc->isrc_cells[1]; + if (irq >= sc->gpio_npins) { + device_printf(sc->dev, "Invalid interrupt number %d", irq); + return (EINVAL); + } + switch (tripol) + { + case 1: + isrc->isrc_trig = INTR_TRIGGER_EDGE; + isrc->isrc_pol = INTR_POLARITY_HIGH; + break; + case 2: + isrc->isrc_trig = INTR_TRIGGER_EDGE; + isrc->isrc_pol = INTR_POLARITY_LOW; + break; + case 4: + isrc->isrc_trig = INTR_TRIGGER_LEVEL; + isrc->isrc_pol = INTR_POLARITY_HIGH; + break; + case 8: + isrc->isrc_trig = INTR_TRIGGER_LEVEL; + isrc->isrc_pol = INTR_POLARITY_LOW; + break; + default: + device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n", + tripol); + return (ENOTSUP); + } + isrc->isrc_nspc_type = ARM_IRQ_NSPC_PLAIN; + isrc->isrc_nspc_num = irq; + + /* + * 1. The link between ISRC and controller must be set atomically. + * 2. Just do things only once in rare case when consumers + * of shared interrupt came here at the same moment. + */ + mtx_lock_spin(&sc->sc_mtx); + if (sc->gpio_pic_irqsrc[irq] != NULL) { + mtx_unlock_spin(&sc->sc_mtx); + return (sc->gpio_pic_irqsrc[irq] == isrc ? 0 : EEXIST); + } + sc->gpio_pic_irqsrc[irq] = isrc; + isrc->isrc_data = irq; + mtx_unlock_spin(&sc->sc_mtx); + + arm_irq_set_name(isrc, "%s,%u", device_get_nameunit(sc->dev), irq); + return (0); +} + +static int +gpio_pic_unregister(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + u_int irq; + + sc = device_get_softc(dev); + + mtx_lock_spin(&sc->sc_mtx); + irq = isrc->isrc_data; + if (sc->gpio_pic_irqsrc[irq] != isrc) { + mtx_unlock_spin(&sc->sc_mtx); + return (sc->gpio_pic_irqsrc[irq] == NULL ? 0 : EINVAL); + } + sc->gpio_pic_irqsrc[irq] = NULL; + isrc->isrc_data = 0; + mtx_unlock_spin(&sc->sc_mtx); + + arm_irq_set_name(isrc, ""); + return (0); +} + +static int +gpio_pic_filter(void *arg) +{ + struct imx51_gpio_softc *sc; + uint32_t i, interrupts; + + sc = arg; + mtx_lock_spin(&sc->sc_mtx); + interrupts = READ4(sc, IMX_GPIO_ISR_REG) & READ4(sc, IMX_GPIO_IMR_REG); + mtx_unlock_spin(&sc->sc_mtx); + + for (i = 0; interrupts != 0; i++, interrupts >>= 1) { + if ((interrupts & 0x1) == 0) + continue; + if (sc->gpio_pic_irqsrc[i]) + arm_irq_dispatch(sc->gpio_pic_irqsrc[i], curthread->td_intr_frame); + else + device_printf(sc->dev, "spurious interrupt %d\n", i); + } + + return (FILTER_HANDLED); +} +#endif + +/* + * + */ static void imx51_gpio_pin_configure(struct imx51_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); /* * Manage input/output @@ -171,15 +404,15 @@ imx51_gpio_pin_configure(struct imx51_gpio_softc *sc, struct gpio_pin *pin, pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; - SET4(sc, IMX_GPIO_OE_REG, (1 << pin->gp_pin)); + SET4(sc, IMX_GPIO_OE_REG, (1U << pin->gp_pin)); } else { pin->gp_flags |= GPIO_PIN_INPUT; - CLEAR4(sc, IMX_GPIO_OE_REG, (1 << pin->gp_pin)); + CLEAR4(sc, IMX_GPIO_OE_REG, (1U << pin->gp_pin)); } } - GPIO_UNLOCK(sc); + mtx_unlock_spin(&sc->sc_mtx); } static device_t @@ -195,8 +428,11 @@ imx51_gpio_get_bus(device_t dev) static int imx51_gpio_pin_max(device_t dev, int *maxpin) { + struct imx51_gpio_softc *sc; + + sc = device_get_softc(dev); + *maxpin = sc->gpio_npins - 1; - *maxpin = NGPIO - 1; return (0); } @@ -215,9 +451,9 @@ imx51_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); *caps = sc->gpio_pins[i].gp_caps; - GPIO_UNLOCK(sc); + mtx_unlock_spin(&sc->sc_mtx); return (0); } @@ -237,9 +473,9 @@ imx51_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); *flags = sc->gpio_pins[i].gp_flags; - GPIO_UNLOCK(sc); + mtx_unlock_spin(&sc->sc_mtx); return (0); } @@ -259,9 +495,9 @@ imx51_gpio_pin_getname(device_t dev, uint32_t pin, char *name) if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); - GPIO_UNLOCK(sc); + mtx_unlock_spin(&sc->sc_mtx); return (0); } @@ -301,12 +537,12 @@ imx51_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); if (value) - SET4(sc, IMX_GPIO_DR_REG, (1 << i)); + SET4(sc, IMX_GPIO_DR_REG, (1U << i)); else - CLEAR4(sc, IMX_GPIO_DR_REG, (1 << i)); - GPIO_UNLOCK(sc); + CLEAR4(sc, IMX_GPIO_DR_REG, (1U << i)); + mtx_unlock_spin(&sc->sc_mtx); return (0); } @@ -326,9 +562,9 @@ imx51_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); *val = (READ4(sc, IMX_GPIO_DR_REG) >> i) & 1; - GPIO_UNLOCK(sc); + mtx_unlock_spin(&sc->sc_mtx); return (0); } @@ -348,34 +584,14 @@ imx51_gpio_pin_toggle(device_t dev, uint32_t pin) if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); WRITE4(sc, IMX_GPIO_DR_REG, - (READ4(sc, IMX_GPIO_DR_REG) ^ (1 << i))); - GPIO_UNLOCK(sc); + (READ4(sc, IMX_GPIO_DR_REG) ^ (1U << i))); + mtx_unlock_spin(&sc->sc_mtx); return (0); } -static int -imx51_gpio_intr(void *arg) -{ - struct imx51_gpio_softc *sc; - uint32_t input, value; - - sc = arg; - input = READ4(sc, IMX_GPIO_ISR_REG); - value = input & READ4(sc, IMX_GPIO_IMR_REG); - WRITE4(sc, IMX_GPIO_ISR_REG, input); - - if (!value) - goto intr_done; - - /* TODO: interrupt handling */ - -intr_done: - return (FILTER_HANDLED); -} - static int imx51_gpio_probe(device_t dev) { @@ -395,10 +611,13 @@ static int imx51_gpio_attach(device_t dev) { struct imx51_gpio_softc *sc; - int i, irq; + int i, irq, unit; sc = device_get_softc(dev); - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + sc->dev = dev; + sc->gpio_npins = NGPIO; + + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->dev), NULL, MTX_SPIN); if (bus_alloc_resources(dev, imx_gpio_spec, sc->sc_res)) { device_printf(dev, "could not allocate resources\n"); @@ -407,40 +626,40 @@ imx51_gpio_attach(device_t dev) return (ENXIO); } - sc->dev = dev; - sc->gpio_npins = NGPIO; - sc->sc_l_irq = 2; sc->sc_iot = rman_get_bustag(sc->sc_res[0]); sc->sc_ioh = rman_get_bushandle(sc->sc_res[0]); - - if (bus_alloc_resources(dev, imx_gpio0irq_spec, &sc->sc_res[3]) == 0) { - /* - * First GPIO unit able to serve +8 interrupts for 8 first - * pins. - */ - sc->sc_l_irq = 10; - } - - for (irq = 1; irq <= sc->sc_l_irq; irq ++) { - if ((bus_setup_intr(dev, sc->sc_res[irq], INTR_TYPE_MISC, - imx51_gpio_intr, NULL, sc, &sc->gpio_ih[irq]))) { + /* + * Mask off all interrupts in hardware, then set up interrupt handling. + */ + WRITE4(sc, IMX_GPIO_IMR_REG, 0); + for (irq = 0; irq < 2; irq++) { +#ifdef ARM_INTRNG + if ((bus_setup_intr(dev, sc->sc_res[1 + irq], INTR_TYPE_CLK, + gpio_pic_filter, NULL, sc, &sc->gpio_ih[irq]))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); imx51_gpio_detach(dev); return (ENXIO); } +#endif } + unit = device_get_unit(dev); for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; sc->gpio_pins[i].gp_flags = - (READ4(sc, IMX_GPIO_OE_REG) & (1 << i)) ? GPIO_PIN_OUTPUT: + (READ4(sc, IMX_GPIO_OE_REG) & (1U << i)) ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, - "imx_gpio%d.%d", device_get_unit(dev), i); + "imx_gpio%d.%d", unit, i); } + +#ifdef ARM_INTRNG + arm_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev))); +#endif sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) { imx51_gpio_detach(dev); return (ENXIO); @@ -457,14 +676,11 @@ imx51_gpio_detach(device_t dev) sc = device_get_softc(dev); - KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); - gpiobus_detach_bus(dev); - for (irq = 1; irq <= sc->sc_l_irq; irq ++) { + for (irq = 1; irq <= 2; irq++) { if (sc->gpio_ih[irq]) bus_teardown_intr(dev, sc->sc_res[irq], sc->gpio_ih[irq]); } - bus_release_resources(dev, imx_gpio0irq_spec, &sc->sc_res[3]); bus_release_resources(dev, imx_gpio_spec, sc->sc_res); mtx_destroy(&sc->sc_mtx); @@ -476,6 +692,19 @@ static device_method_t imx51_gpio_methods[] = { DEVMETHOD(device_attach, imx51_gpio_attach), DEVMETHOD(device_detach, imx51_gpio_detach), +#ifdef ARM_INTRNG + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, gpio_pic_disable_intr), + DEVMETHOD(pic_disable_source, gpio_pic_disable_source), + DEVMETHOD(pic_enable_intr, gpio_pic_enable_intr), + DEVMETHOD(pic_enable_source, gpio_pic_enable_source), + DEVMETHOD(pic_post_filter, gpio_pic_post_filter), + DEVMETHOD(pic_post_ithread, gpio_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, gpio_pic_pre_ithread), + DEVMETHOD(pic_register, gpio_pic_register), + DEVMETHOD(pic_unregister, gpio_pic_unregister), +#endif + /* GPIO protocol */ DEVMETHOD(gpio_get_bus, imx51_gpio_get_bus), DEVMETHOD(gpio_pin_max, imx51_gpio_pin_max), diff --git a/sys/arm/include/fdt.h b/sys/arm/include/fdt.h index c1b785e8c1f..e8302d6a7b3 100644 --- a/sys/arm/include/fdt.h +++ b/sys/arm/include/fdt.h @@ -34,12 +34,16 @@ #include +#ifndef INTRNG + /* Max interrupt number */ #define FDT_INTR_MAX NIRQ /* Map phandle/intpin pair to global IRQ number */ #define FDT_MAP_IRQ(node, pin) (pin) +#endif + /* * Bus space tag. XXX endianess info needs to be derived from the blob. */ diff --git a/sys/arm/include/intr.h b/sys/arm/include/intr.h index 7ab69d9405a..2bb479acb7f 100644 --- a/sys/arm/include/intr.h +++ b/sys/arm/include/intr.h @@ -43,6 +43,102 @@ #include #endif +#ifdef ARM_INTRNG + +#ifndef NIRQ +#define NIRQ 1024 /* XXX - It should be an option. */ +#endif + +#ifdef notyet +#define INTR_SOLO INTR_MD1 +typedef int arm_irq_filter_t(void *arg, struct trapframe *tf); +#else +typedef int arm_irq_filter_t(void *arg); +#endif + +#define ARM_ISRC_NAMELEN (MAXCOMLEN + 1) + +typedef void arm_ipi_filter_t(void *arg); + +enum arm_isrc_type { + ARM_ISRCT_NAMESPACE, + ARM_ISRCT_FDT +}; + +#define ARM_ISRCF_REGISTERED 0x01 /* registered in a controller */ +#define ARM_ISRCF_PERCPU 0x02 /* per CPU interrupt */ +#define ARM_ISRCF_BOUND 0x04 /* bound to a CPU */ + +/* Interrupt source definition. */ +struct arm_irqsrc { + device_t isrc_dev; /* where isrc is mapped */ + intptr_t isrc_xref; /* device reference key */ + uintptr_t isrc_data; /* device data for isrc */ + u_int isrc_irq; /* unique identificator */ + enum arm_isrc_type isrc_type; /* how is isrc decribed */ + u_int isrc_flags; + char isrc_name[ARM_ISRC_NAMELEN]; + uint16_t isrc_nspc_type; + uint16_t isrc_nspc_num; + enum intr_trigger isrc_trig; + enum intr_polarity isrc_pol; + cpuset_t isrc_cpu; /* on which CPUs is enabled */ + u_int isrc_index; + u_long * isrc_count; + u_int isrc_handlers; + struct intr_event * isrc_event; + arm_irq_filter_t * isrc_filter; + arm_ipi_filter_t * isrc_ipifilter; + void * isrc_arg; +#ifdef FDT + u_int isrc_ncells; + pcell_t isrc_cells[]; /* leave it last */ +#endif +}; + +void arm_irq_set_name(struct arm_irqsrc *isrc, const char *fmt, ...) + __printflike(2, 3); + +void arm_irq_dispatch(struct arm_irqsrc *isrc, struct trapframe *tf); + +#define ARM_IRQ_NSPC_NONE 0 +#define ARM_IRQ_NSPC_PLAIN 1 +#define ARM_IRQ_NSPC_IRQ 2 +#define ARM_IRQ_NSPC_IPI 3 + +u_int arm_namespace_map_irq(device_t dev, uint16_t type, uint16_t num); +#ifdef FDT +u_int arm_fdt_map_irq(phandle_t, pcell_t *, u_int); +#endif + +int arm_pic_register(device_t dev, intptr_t xref); +int arm_pic_unregister(device_t dev, intptr_t xref); +int arm_pic_claim_root(device_t dev, intptr_t xref, arm_irq_filter_t *filter, + void *arg, u_int ipicount); + +int arm_irq_add_handler(device_t dev, driver_filter_t, driver_intr_t, void *, + u_int, int, void **); +int arm_irq_remove_handler(device_t dev, u_int, void *); +int arm_irq_config(u_int, enum intr_trigger, enum intr_polarity); +int arm_irq_describe(u_int, void *, const char *); + +u_int arm_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask); + +#ifdef SMP +int arm_irq_bind(u_int, int); + +void arm_ipi_dispatch(struct arm_irqsrc *isrc, struct trapframe *tf); + +#define AISHF_NOALLOC 0x0001 + +int arm_ipi_set_handler(u_int ipi, const char *name, arm_ipi_filter_t *filter, + void *arg, u_int flags); + +void arm_pic_init_secondary(void); +#endif + +#else /* ARM_INTRNG */ + /* XXX move to std.* files? */ #ifdef CPU_XSCALE_81342 #define NIRQ 128 @@ -71,7 +167,6 @@ #define NIRQ 32 #endif - int arm_get_next_irq(int); void arm_mask_irq(uintptr_t); void arm_unmask_irq(uintptr_t); @@ -83,14 +178,15 @@ extern void (*arm_post_filter)(void *); extern int (*arm_config_irq)(int irq, enum intr_trigger trig, enum intr_polarity pol); -void arm_irq_memory_barrier(uintptr_t); - -void arm_init_secondary_ic(void); -int gic_decode_fdt(uint32_t iparentnode, uint32_t *intrcells, int *interrupt, - int *trig, int *pol); +void arm_pic_init_secondary(void); #ifdef FDT +int gic_decode_fdt(phandle_t, pcell_t *, int *, int *, int *); int arm_fdt_map_irq(phandle_t, pcell_t *, int); #endif +#endif /* ARM_INTRNG */ + +void arm_irq_memory_barrier(uintptr_t); + #endif /* _MACHINE_INTR_H */ diff --git a/sys/arm/include/param.h b/sys/arm/include/param.h index d3aa01b7ce1..bbe9bcb0832 100644 --- a/sys/arm/include/param.h +++ b/sys/arm/include/param.h @@ -149,8 +149,4 @@ #define pgtok(x) ((x) * (PAGE_SIZE / 1024)) -#ifdef _KERNEL -#define NO_FUEWORD 1 -#endif - #endif /* !_ARM_INCLUDE_PARAM_H_ */ diff --git a/sys/arm/include/smp.h b/sys/arm/include/smp.h index bce8b4f342c..1c90431a853 100644 --- a/sys/arm/include/smp.h +++ b/sys/arm/include/smp.h @@ -6,6 +6,19 @@ #include #include +#ifdef ARM_INTRNG +enum { + IPI_AST, + IPI_PREEMPT, + IPI_RENDEZVOUS, + IPI_STOP, + IPI_STOP_HARD = IPI_STOP, /* These are synonyms on arm. */ + IPI_HARDCLOCK, + IPI_TLB, + IPI_CACHE, + ARM_IPI_COUNT +}; +#else #define IPI_AST 0 #define IPI_PREEMPT 2 #define IPI_RENDEZVOUS 3 @@ -14,6 +27,7 @@ #define IPI_HARDCLOCK 6 #define IPI_TLB 7 #define IPI_CACHE 8 +#endif /* INTRNG */ void init_secondary(int cpu); void mpentry(void); @@ -24,8 +38,10 @@ void ipi_selected(cpuset_t cpus, u_int ipi); /* PIC interface */ void pic_ipi_send(cpuset_t cpus, u_int ipi); +#ifndef ARM_INTRNG void pic_ipi_clear(int ipi); int pic_ipi_read(int arg); +#endif /* Platform interface */ void platform_mp_setmaxid(void); diff --git a/sys/arm/mv/std-pj4b.mv b/sys/arm/mv/std-pj4b.mv index ed9fd00bab6..8a64fab7bc1 100644 --- a/sys/arm/mv/std-pj4b.mv +++ b/sys/arm/mv/std-pj4b.mv @@ -5,4 +5,8 @@ cpu CPU_MV_PJ4B machine arm armv6 makeoptions CONF_CFLAGS="-march=armv7a" -options VM_MAXUSER_ADDRESS="(KERNBASE-(1024*1024*1024))" +# This was originally defined as "(KERNBASE-(1024*1024*1024))" but that +# (in opt_global.h) clashed with the value emitted by genassym which +# reduces the original macro text to its numeric value. The only way +# to avoid that is to define it here as the numeric value genassym emits. +options VM_MAXUSER_ADDRESS="0x80000000" diff --git a/sys/arm/qemu/virt_common.c b/sys/arm/qemu/virt_common.c index fc022473e6e..0f407243cff 100644 --- a/sys/arm/qemu/virt_common.c +++ b/sys/arm/qemu/virt_common.c @@ -28,6 +28,8 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include diff --git a/sys/arm/qemu/virt_mp.c b/sys/arm/qemu/virt_mp.c index 4942745e109..2fe1592af0f 100644 --- a/sys/arm/qemu/virt_mp.c +++ b/sys/arm/qemu/virt_mp.c @@ -108,7 +108,7 @@ void platform_mp_init_secondary(void) { - arm_init_secondary_ic(); + arm_pic_init_secondary(); } void diff --git a/sys/arm/rockchip/rk30xx_mp.c b/sys/arm/rockchip/rk30xx_mp.c index 74c8b554c85..f07605cfe18 100644 --- a/sys/arm/rockchip/rk30xx_mp.c +++ b/sys/arm/rockchip/rk30xx_mp.c @@ -82,7 +82,7 @@ void platform_mp_init_secondary(void) { - arm_init_secondary_ic(); + arm_pic_init_secondary(); } void diff --git a/sys/arm/samsung/exynos/exynos5_mp.c b/sys/arm/samsung/exynos/exynos5_mp.c index 1b3afbbf0d6..0f4a792b79d 100644 --- a/sys/arm/samsung/exynos/exynos5_mp.c +++ b/sys/arm/samsung/exynos/exynos5_mp.c @@ -73,7 +73,7 @@ void platform_mp_init_secondary(void) { - arm_init_secondary_ic(); + arm_pic_init_secondary(); } void diff --git a/sys/arm/ti/omap4/omap4_mp.c b/sys/arm/ti/omap4/omap4_mp.c index 6423e568f24..f53748e90a9 100644 --- a/sys/arm/ti/omap4/omap4_mp.c +++ b/sys/arm/ti/omap4/omap4_mp.c @@ -44,7 +44,7 @@ __FBSDID("$FreeBSD$"); void platform_mp_init_secondary(void) { - arm_init_secondary_ic(); + arm_pic_init_secondary(); } void diff --git a/sys/arm/ti/omap4/std.omap4 b/sys/arm/ti/omap4/std.omap4 index 27ec5ff4bbe..02fc8a2d28a 100644 --- a/sys/arm/ti/omap4/std.omap4 +++ b/sys/arm/ti/omap4/std.omap4 @@ -17,7 +17,3 @@ makeoptions KERNVIRTADDR=0xc0200000 options SOC_OMAP4 options ARM_L2_PIPT - -options IPI_IRQ_START=0 -options IPI_IRQ_END=15 - diff --git a/sys/arm/ti/ti_common.c b/sys/arm/ti/ti_common.c index 50072f2acfa..31a6a32eed7 100644 --- a/sys/arm/ti/ti_common.c +++ b/sys/arm/ti/ti_common.c @@ -70,7 +70,7 @@ fdt_aintc_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, #endif fdt_pic_decode_t fdt_pic_table[] = { -#ifdef SOC_OMAP4 +#if defined(SOC_OMAP4) && !defined(ARM_INTRNG) &gic_decode_fdt, #endif #ifdef SOC_TI_AM335X diff --git a/sys/arm/xilinx/zy7_mp.c b/sys/arm/xilinx/zy7_mp.c index 305290ed203..24e8595a5af 100644 --- a/sys/arm/xilinx/zy7_mp.c +++ b/sys/arm/xilinx/zy7_mp.c @@ -49,7 +49,7 @@ void platform_mp_init_secondary(void) { - arm_init_secondary_ic(); + arm_pic_init_secondary(); } void diff --git a/sys/arm64/arm64/copyinout.S b/sys/arm64/arm64/copyinout.S index 0982c70fc7a..1ba81068e43 100644 --- a/sys/arm64/arm64/copyinout.S +++ b/sys/arm64/arm64/copyinout.S @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); */ ENTRY(copyio_fault) SET_FAULT_HANDLER(xzr, x1) /* Clear the handler */ +copyio_fault_nopcb: mov x0, #EFAULT ret END(copyio_fault) @@ -51,6 +52,10 @@ END(copyio_fault) */ ENTRY(copyout) cbz x2, 2f /* If len == 0 then skip loop */ + add x3, x1, x2 + ldr x4, =VM_MAXUSER_ADDRESS + cmp x3, x4 + b.hi copyio_fault_nopcb adr x6, copyio_fault /* Get the handler address */ SET_FAULT_HANDLER(x6, x7) /* Set the handler */ @@ -73,6 +78,10 @@ END(copyout) */ ENTRY(copyin) cbz x2, 2f /* If len == 0 then skip loop */ + add x3, x0, x2 + ldr x4, =VM_MAXUSER_ADDRESS + cmp x3, x4 + b.hi copyio_fault_nopcb adr x6, copyio_fault /* Get the handler address */ SET_FAULT_HANDLER(x6, x7) /* Set the handler */ @@ -97,11 +106,14 @@ ENTRY(copyinstr) mov x5, xzr /* count = 0 */ mov w4, #1 /* If zero return faulure */ cbz x2, 3f /* If len == 0 then skip loop */ + ldr x7, =VM_MAXUSER_ADDRESS adr x6, copyio_fault /* Get the handler address */ SET_FAULT_HANDLER(x6, x7) /* Set the handler */ -1: ldrb w4, [x0], #1 /* Load from uaddr */ +1: cmp x0, x7 + b.cs copyio_fault + ldrb w4, [x0], #1 /* Load from uaddr */ strb w4, [x1], #1 /* Store in kaddr */ add x5, x5, #1 /* count++ */ cbz w4, 2f /* Break when NUL-terminated */ diff --git a/sys/arm64/arm64/genassym.c b/sys/arm64/arm64/genassym.c index 33b20421666..f8dfedab83a 100644 --- a/sys/arm64/arm64/genassym.c +++ b/sys/arm64/arm64/genassym.c @@ -38,6 +38,8 @@ __FBSDID("$FreeBSD$"); #include ASSYM(KERNBASE, KERNBASE); +ASSYM(VM_MAXUSER_ADDRESS, VM_MAXUSER_ADDRESS); + ASSYM(TDF_ASTPENDING, TDF_ASTPENDING); ASSYM(TDF_NEEDRESCHED, TDF_NEEDRESCHED); diff --git a/sys/arm64/arm64/locore.S b/sys/arm64/arm64/locore.S index c5da907118d..204306d063c 100644 --- a/sys/arm64/arm64/locore.S +++ b/sys/arm64/arm64/locore.S @@ -357,7 +357,7 @@ create_pagetables: mov x7, #NORMAL_MEM mov x8, #(KERNBASE & L2_BLOCK_MASK) mov x9, x28 - bl build_block_pagetable + bl build_l2_block_pagetable /* Move to the l1 table */ add x26, x26, #PAGE_SIZE @@ -379,7 +379,8 @@ create_pagetables: mov x7, #DEVICE_MEM mov x8, #(SOCDEV_VA) /* VA start */ mov x9, #(SOCDEV_PA) /* PA start */ - bl build_section_pagetable + mov x10, #1 + bl build_l1_block_pagetable #endif /* Create the VA = PA map */ @@ -387,47 +388,18 @@ create_pagetables: mov x7, #NORMAL_UNCACHED /* Uncached as it's only needed early on */ mov x9, x27 mov x8, x9 /* VA start (== PA start) */ - bl build_section_pagetable + mov x10, #1 + bl build_l1_block_pagetable /* Restore the Link register */ mov x30, x5 ret -/* - * Builds a 1 GiB page table entry - * x6 = L1 table - * x7 = Type (0 = Device, 1 = Normal) - * x8 = VA start - * x9 = PA start (trashed) - * x11, x12 and x13 are trashed - */ -build_section_pagetable: - /* - * Build the L1 table entry. - */ - /* Find the table index */ - lsr x11, x8, #L1_SHIFT - and x11, x11, #Ln_ADDR_MASK - - /* Build the L1 block entry */ - lsl x12, x7, #2 - orr x12, x12, #L1_BLOCK - orr x12, x12, #(ATTR_AF) - - /* Only use the output address bits */ - lsr x9, x9, #L1_SHIFT - orr x12, x12, x9, lsl #L1_SHIFT - - /* Store the entry */ - str x12, [x6, x11, lsl #3] - - ret - /* * Builds an L1 -> L2 table descriptor * * This is a link for a 1GiB block of memory with up to 2MiB regions mapped - * within it by build_block_pagetable. + * within it by build_l2_block_pagetable. * * x6 = L1 table * x8 = Virtual Address @@ -454,6 +426,50 @@ link_l1_pagetable: ret +/* + * Builds count 1 GiB page table entry + * x6 = L1 table + * x7 = Type (0 = Device, 1 = Normal) + * x8 = VA start + * x9 = PA start (trashed) + * x10 = Entry count (TODO) + * x11, x12 and x13 are trashed + */ +build_l1_block_pagetable: + /* + * Build the L1 table entry. + */ + /* Find the table index */ + lsr x11, x8, #L1_SHIFT + and x11, x11, #Ln_ADDR_MASK + + /* Build the L1 block entry */ + lsl x12, x7, #2 + orr x12, x12, #L1_BLOCK + orr x12, x12, #(ATTR_AF) +#ifdef SMP + orr x12, x12, ATTR_SH(ATTR_SH_IS) +#endif + + /* Only use the output address bits */ + lsr x9, x9, #L1_SHIFT + + /* Set the physical address for this virtual address */ +1: orr x12, x12, x9, lsl #L1_SHIFT + + /* Store the entry */ + str x12, [x6, x11, lsl #3] + + /* Clear the address bits */ + and x12, x12, #ATTR_MASK_L + + sub x10, x10, #1 + add x11, x11, #1 + add x9, x9, #1 + cbnz x10, 1b + +2: ret + /* * Builds count 2 MiB page table entry * x6 = L2 table @@ -463,7 +479,7 @@ link_l1_pagetable: * x10 = Entry count (TODO) * x11, x12 and x13 are trashed */ -build_block_pagetable: +build_l2_block_pagetable: /* * Build the L2 table entry. */ diff --git a/sys/arm64/arm64/machdep.c b/sys/arm64/arm64/machdep.c index 4bb3f674b9f..e92a99ff305 100644 --- a/sys/arm64/arm64/machdep.c +++ b/sys/arm64/arm64/machdep.c @@ -251,7 +251,13 @@ exec_setregs(struct thread *td, struct image_params *imgp, u_long stack) memset(tf, 0, sizeof(struct trapframe)); - tf->tf_sp = stack; + /* + * We need to set x0 for init as it doesn't call + * cpu_set_syscall_retval to copy the value. We also + * need to set td_retval for the cases where we do. + */ + tf->tf_x[0] = td->td_retval[0] = stack; + tf->tf_sp = STACKALIGN(stack); tf->tf_lr = imgp->entry_addr; tf->tf_elr = imgp->entry_addr; } diff --git a/sys/arm64/arm64/support.S b/sys/arm64/arm64/support.S index 73ef6a4ac58..749fc6e7026 100644 --- a/sys/arm64/arm64/support.S +++ b/sys/arm64/arm64/support.S @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); */ ENTRY(fsu_fault) SET_FAULT_HANDLER(xzr, x1) /* Reset the handler function */ +fsu_fault_nopcb: mov x0, #-1 ret END(fsu_fault) @@ -49,6 +50,9 @@ END(fsu_fault) * int casueword32(volatile uint32_t *, uint32_t, uint32_t *, uint32_t) */ ENTRY(casueword32) + ldr x4, =(VM_MAXUSER_ADDRESS-3) + cmp x0, x4 + b.cs fsu_fault_nopcb adr x6, fsu_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x4) /* And set it */ 1: ldxr w4, [x0] /* Load-exclusive the data */ @@ -59,6 +63,7 @@ ENTRY(casueword32) ldrb w0, [x0] /* Try loading the data */ 2: SET_FAULT_HANDLER(xzr, x5) /* Reset the fault handler */ str w4, [x2] /* Store the read data */ + mov x0, #0 /* Success */ ret /* Return */ END(casueword32) @@ -66,6 +71,9 @@ END(casueword32) * int casueword(volatile u_long *, u_long, u_long *, u_long) */ ENTRY(casueword) + ldr x4, =(VM_MAXUSER_ADDRESS-7) + cmp x0, x4 + b.cs fsu_fault_nopcb adr x6, fsu_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x4) /* And set it */ 1: ldxr x4, [x0] /* Load-exclusive the data */ @@ -76,6 +84,7 @@ ENTRY(casueword) ldrb w0, [x0] /* Try loading the data */ 2: SET_FAULT_HANDLER(xzr, x5) /* Reset the fault handler */ str x4, [x2] /* Store the read data */ + mov x0, #0 /* Success */ ret /* Return */ END(casueword) @@ -83,6 +92,9 @@ END(casueword) * int fubyte(volatile const void *) */ ENTRY(fubyte) + ldr x1, =VM_MAXUSER_ADDRESS + cmp x0, x1 + b.cs fsu_fault_nopcb adr x6, fsu_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x1) /* And set it */ ldrb w0, [x0] /* Try loading the data */ @@ -94,6 +106,9 @@ END(fubyte) * int fuword(volatile const void *) */ ENTRY(fuword16) + ldr x1, =(VM_MAXUSER_ADDRESS-1) + cmp x0, x1 + b.cs fsu_fault_nopcb adr x6, fsu_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x1) /* And set it */ ldrh w0, [x0] /* Try loading the data */ @@ -105,6 +120,9 @@ END(fuword16) * int32_t fueword32(volatile const void *, int32_t *) */ ENTRY(fueword32) + ldr x2, =(VM_MAXUSER_ADDRESS-3) + cmp x0, x2 + b.cs fsu_fault_nopcb adr x6, fsu_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x2) /* And set it */ ldr w0, [x0] /* Try loading the data */ @@ -120,6 +138,9 @@ END(fueword32) */ ENTRY(fueword) EENTRY(fueword64) + ldr x2, =(VM_MAXUSER_ADDRESS-7) + cmp x0, x2 + b.cs fsu_fault_nopcb adr x6, fsu_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x2) /* And set it */ ldr x0, [x0] /* Try loading the data */ @@ -134,6 +155,9 @@ END(fueword) * int subyte(volatile void *, int) */ ENTRY(subyte) + ldr x2, =VM_MAXUSER_ADDRESS + cmp x0, x2 + b.cs fsu_fault_nopcb adr x6, fsu_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x2) /* And set it */ strb w1, [x0] /* Try storing the data */ @@ -146,6 +170,9 @@ END(subyte) * int suword16(volatile void *, int) */ ENTRY(suword16) + ldr x2, =(VM_MAXUSER_ADDRESS-1) + cmp x0, x2 + b.cs fsu_fault_nopcb adr x6, fsu_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x2) /* And set it */ strh w1, [x0] /* Try storing the data */ @@ -158,6 +185,9 @@ END(suword16) * int suword32(volatile void *, int) */ ENTRY(suword32) + ldr x2, =(VM_MAXUSER_ADDRESS-3) + cmp x0, x2 + b.cs fsu_fault_nopcb adr x6, fsu_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x2) /* And set it */ str w1, [x0] /* Try storing the data */ @@ -171,6 +201,9 @@ END(suword32) */ ENTRY(suword) EENTRY(suword64) + ldr x2, =(VM_MAXUSER_ADDRESS-7) + cmp x0, x2 + b.cs fsu_fault_nopcb adr x6, fsu_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x2) /* And set it */ str x1, [x0] /* Try storing the data */ @@ -199,6 +232,9 @@ END(fsu_fault) * int fuswintr(void *) */ ENTRY(fuswintr) + ldr x1, =(VM_MAXUSER_ADDRESS-3) + cmp x0, x1 + b.cs fsu_fault_nopcb adr x6, fsu_intr_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x1) /* And set it */ ldr w0, [x0] /* Try loading the data */ @@ -210,6 +246,9 @@ END(fuswintr) * int suswintr(void *base, int word) */ ENTRY(suswintr) + ldr x2, =(VM_MAXUSER_ADDRESS-3) + cmp x0, x2 + b.cs fsu_fault_nopcb adr x6, fsu_intr_fault /* Load the fault handler */ SET_FAULT_HANDLER(x6, x2) /* And set it */ str w1, [x0] /* Try storing the data */ diff --git a/sys/arm64/conf/GENERIC b/sys/arm64/conf/GENERIC index 1486f079368..d671fa870c2 100644 --- a/sys/arm64/conf/GENERIC +++ b/sys/arm64/conf/GENERIC @@ -94,9 +94,11 @@ device virtio_blk device vtnet # Bus drivers +options PCI_IOV device pci # Ethernet NICs +device vnic # Cavium ThunderX NIC device em # Intel PRO/1000 Gigabit Ethernet Family device mii device miibus # MII bus support diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.c b/sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.c index d59fbf0dea5..6eade2a3a4a 100644 --- a/sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.c +++ b/sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.c @@ -117,7 +117,8 @@ zfeature_lookup_name(const char *name, spa_feature_t *res) } boolean_t -zfeature_depends_on(spa_feature_t fid, spa_feature_t check) { +zfeature_depends_on(spa_feature_t fid, spa_feature_t check) +{ zfeature_info_t *feature = &spa_feature_table[fid]; for (int i = 0; feature->fi_depends[i] != SPA_FEATURE_NONE; i++) { @@ -230,4 +231,19 @@ zpool_feature_init(void) "org.open-zfs:large_blocks", "large_blocks", "Support for blocks larger than 128KB.", ZFEATURE_FLAG_PER_DATASET, large_blocks_deps); + +#ifdef illumos + zfeature_register(SPA_FEATURE_SHA512, + "org.illumos:sha512", "sha512", + "SHA-512/256 hash algorithm.", + ZFEATURE_FLAG_PER_DATASET, NULL); + zfeature_register(SPA_FEATURE_SKEIN, + "org.illumos:skein", "skein", + "Skein hash algorithm.", + ZFEATURE_FLAG_PER_DATASET, NULL); + zfeature_register(SPA_FEATURE_EDONR, + "org.illumos:edonr", "edonr", + "Edon-R hash algorithm.", + ZFEATURE_FLAG_PER_DATASET, NULL); +#endif } diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.h b/sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.h index 0e88a9ae6fa..56f3da7d43f 100644 --- a/sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.h +++ b/sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.h @@ -51,6 +51,11 @@ typedef enum spa_feature { SPA_FEATURE_BOOKMARKS, SPA_FEATURE_FS_SS_LIMIT, SPA_FEATURE_LARGE_BLOCKS, +#ifdef illumos + SPA_FEATURE_SHA512, + SPA_FEATURE_SKEIN, + SPA_FEATURE_EDONR, +#endif SPA_FEATURES } spa_feature_t; diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_fletcher.c b/sys/cddl/contrib/opensolaris/common/zfs/zfs_fletcher.c index fa43ce6bdb5..a58fa14b7c0 100644 --- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_fletcher.c +++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_fletcher.c @@ -22,6 +22,9 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2013 Saso Kiselkov. All rights reserved. + */ /* * Fletcher Checksums @@ -131,8 +134,10 @@ #include #include +/*ARGSUSED*/ void -fletcher_2_native(const void *buf, uint64_t size, zio_cksum_t *zcp) +fletcher_2_native(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) { const uint64_t *ip = buf; const uint64_t *ipend = ip + (size / sizeof (uint64_t)); @@ -148,8 +153,10 @@ fletcher_2_native(const void *buf, uint64_t size, zio_cksum_t *zcp) ZIO_SET_CHECKSUM(zcp, a0, a1, b0, b1); } +/*ARGSUSED*/ void -fletcher_2_byteswap(const void *buf, uint64_t size, zio_cksum_t *zcp) +fletcher_2_byteswap(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) { const uint64_t *ip = buf; const uint64_t *ipend = ip + (size / sizeof (uint64_t)); @@ -165,8 +172,10 @@ fletcher_2_byteswap(const void *buf, uint64_t size, zio_cksum_t *zcp) ZIO_SET_CHECKSUM(zcp, a0, a1, b0, b1); } +/*ARGSUSED*/ void -fletcher_4_native(const void *buf, uint64_t size, zio_cksum_t *zcp) +fletcher_4_native(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) { const uint32_t *ip = buf; const uint32_t *ipend = ip + (size / sizeof (uint32_t)); @@ -182,8 +191,10 @@ fletcher_4_native(const void *buf, uint64_t size, zio_cksum_t *zcp) ZIO_SET_CHECKSUM(zcp, a, b, c, d); } +/*ARGSUSED*/ void -fletcher_4_byteswap(const void *buf, uint64_t size, zio_cksum_t *zcp) +fletcher_4_byteswap(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) { const uint32_t *ip = buf; const uint32_t *ipend = ip + (size / sizeof (uint32_t)); diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_fletcher.h b/sys/cddl/contrib/opensolaris/common/zfs/zfs_fletcher.h index b49df0cf4f0..a920cc816d4 100644 --- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_fletcher.h +++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_fletcher.h @@ -22,6 +22,9 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2013 Saso Kiselkov. All rights reserved. + */ #ifndef _ZFS_FLETCHER_H #define _ZFS_FLETCHER_H @@ -37,14 +40,12 @@ extern "C" { * fletcher checksum functions */ -void fletcher_2_native(const void *, uint64_t, zio_cksum_t *); -void fletcher_2_byteswap(const void *, uint64_t, zio_cksum_t *); -void fletcher_4_native(const void *, uint64_t, zio_cksum_t *); -void fletcher_4_byteswap(const void *, uint64_t, zio_cksum_t *); -void fletcher_4_incremental_native(const void *, uint64_t, - zio_cksum_t *); -void fletcher_4_incremental_byteswap(const void *, uint64_t, - zio_cksum_t *); +void fletcher_2_native(const void *, uint64_t, const void *, zio_cksum_t *); +void fletcher_2_byteswap(const void *, uint64_t, const void *, zio_cksum_t *); +void fletcher_4_native(const void *, uint64_t, const void *, zio_cksum_t *); +void fletcher_4_byteswap(const void *, uint64_t, const void *, zio_cksum_t *); +void fletcher_4_incremental_native(const void *, uint64_t, zio_cksum_t *); +void fletcher_4_incremental_byteswap(const void *, uint64_t, zio_cksum_t *); #ifdef __cplusplus } diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c b/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c index 62fed7b6ef2..39ad7ce5f10 100644 --- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c +++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c @@ -53,8 +53,52 @@ zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag) zfs_cmd_v28_t *zc28_c; zfs_cmd_deadman_t *zcdm_c; zfs_cmd_zcmd_t *zcmd_c; + zfs_cmd_edbp_t *edbp_c; switch (cflag) { + case ZFS_CMD_COMPAT_EDBP: + edbp_c = (void *)addr; + /* zc */ + strlcpy(zc->zc_name, edbp_c->zc_name, MAXPATHLEN); + strlcpy(zc->zc_value, edbp_c->zc_value, MAXPATHLEN * 2); + strlcpy(zc->zc_string, edbp_c->zc_string, MAXPATHLEN); + +#define ZCMD_COPY(field) zc->field = edbp_c->field + ZCMD_COPY(zc_nvlist_src); + ZCMD_COPY(zc_nvlist_src_size); + ZCMD_COPY(zc_nvlist_dst); + ZCMD_COPY(zc_nvlist_dst_size); + ZCMD_COPY(zc_nvlist_dst_filled); + ZCMD_COPY(zc_pad2); + ZCMD_COPY(zc_history); + ZCMD_COPY(zc_guid); + ZCMD_COPY(zc_nvlist_conf); + ZCMD_COPY(zc_nvlist_conf_size); + ZCMD_COPY(zc_cookie); + ZCMD_COPY(zc_objset_type); + ZCMD_COPY(zc_perm_action); + ZCMD_COPY(zc_history_len); + ZCMD_COPY(zc_history_offset); + ZCMD_COPY(zc_obj); + ZCMD_COPY(zc_iflags); + ZCMD_COPY(zc_share); + ZCMD_COPY(zc_jailid); + ZCMD_COPY(zc_objset_stats); + zc->zc_begin_record.drr_u.drr_begin = edbp_c->zc_begin_record; + ZCMD_COPY(zc_inject_record); + ZCMD_COPY(zc_defer_destroy); + ZCMD_COPY(zc_flags); + ZCMD_COPY(zc_action_handle); + ZCMD_COPY(zc_cleanup_fd); + ZCMD_COPY(zc_simple); + zc->zc_resumable = B_FALSE; + ZCMD_COPY(zc_sendobj); + ZCMD_COPY(zc_fromobj); + ZCMD_COPY(zc_createtxg); + ZCMD_COPY(zc_stat); +#undef ZCMD_COPY + break; + case ZFS_CMD_COMPAT_ZCMD: zcmd_c = (void *)addr; /* zc */ @@ -83,14 +127,7 @@ zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag) ZCMD_COPY(zc_share); ZCMD_COPY(zc_jailid); ZCMD_COPY(zc_objset_stats); - - /* - * zc_begin_record, zc_inject_record didn't change in embedeed-data - * block pointers - * - * TODO: CTASSERT? - */ - ZCMD_COPY(zc_begin_record); + zc->zc_begin_record.drr_u.drr_begin = zcmd_c->zc_begin_record; ZCMD_COPY(zc_inject_record); /* boolean_t -> uint32_t */ @@ -100,7 +137,7 @@ zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag) ZCMD_COPY(zc_action_handle); ZCMD_COPY(zc_cleanup_fd); ZCMD_COPY(zc_simple); - bcopy(zcmd_c->zc_pad, zc->zc_pad, sizeof(zc->zc_pad)); + zc->zc_resumable = B_FALSE; ZCMD_COPY(zc_sendobj); ZCMD_COPY(zc_fromobj); ZCMD_COPY(zc_createtxg); @@ -133,13 +170,13 @@ zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag) zc->zc_share = zcdm_c->zc_share; zc->zc_jailid = zcdm_c->zc_jailid; zc->zc_objset_stats = zcdm_c->zc_objset_stats; - zc->zc_begin_record = zcdm_c->zc_begin_record; + zc->zc_begin_record.drr_u.drr_begin = zcdm_c->zc_begin_record; zc->zc_defer_destroy = zcdm_c->zc_defer_destroy; (void)zcdm_c->zc_temphold; zc->zc_action_handle = zcdm_c->zc_action_handle; zc->zc_cleanup_fd = zcdm_c->zc_cleanup_fd; zc->zc_simple = zcdm_c->zc_simple; - bcopy(zcdm_c->zc_pad, zc->zc_pad, sizeof(zc->zc_pad)); + zc->zc_resumable = B_FALSE; zc->zc_sendobj = zcdm_c->zc_sendobj; zc->zc_fromobj = zcdm_c->zc_fromobj; zc->zc_createtxg = zcdm_c->zc_createtxg; @@ -177,13 +214,13 @@ zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag) zc->zc_share = zc28_c->zc_share; zc->zc_jailid = zc28_c->zc_jailid; zc->zc_objset_stats = zc28_c->zc_objset_stats; - zc->zc_begin_record = zc28_c->zc_begin_record; + zc->zc_begin_record.drr_u.drr_begin = zc28_c->zc_begin_record; zc->zc_defer_destroy = zc28_c->zc_defer_destroy; (void)zc28_c->zc_temphold; zc->zc_action_handle = zc28_c->zc_action_handle; zc->zc_cleanup_fd = zc28_c->zc_cleanup_fd; zc->zc_simple = zc28_c->zc_simple; - bcopy(zc28_c->zc_pad, zc->zc_pad, sizeof(zc->zc_pad)); + zc->zc_resumable = B_FALSE; zc->zc_sendobj = zc28_c->zc_sendobj; zc->zc_fromobj = zc28_c->zc_fromobj; zc->zc_createtxg = zc28_c->zc_createtxg; @@ -246,7 +283,7 @@ zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag) zc->zc_share = zc_c->zc_share; zc->zc_jailid = zc_c->zc_jailid; zc->zc_objset_stats = zc_c->zc_objset_stats; - zc->zc_begin_record = zc_c->zc_begin_record; + zc->zc_begin_record.drr_u.drr_begin = zc_c->zc_begin_record; /* zc->zc_inject_record */ zc->zc_inject_record.zi_objset = @@ -281,8 +318,50 @@ zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int request, zfs_cmd_v28_t *zc28_c; zfs_cmd_deadman_t *zcdm_c; zfs_cmd_zcmd_t *zcmd_c; + zfs_cmd_edbp_t *edbp_c; switch (cflag) { + case ZFS_CMD_COMPAT_EDBP: + edbp_c = (void *)addr; + strlcpy(edbp_c->zc_name, zc->zc_name, MAXPATHLEN); + strlcpy(edbp_c->zc_value, zc->zc_value, MAXPATHLEN * 2); + strlcpy(edbp_c->zc_string, zc->zc_string, MAXPATHLEN); + +#define ZCMD_COPY(field) edbp_c->field = zc->field + ZCMD_COPY(zc_nvlist_src); + ZCMD_COPY(zc_nvlist_src_size); + ZCMD_COPY(zc_nvlist_dst); + ZCMD_COPY(zc_nvlist_dst_size); + ZCMD_COPY(zc_nvlist_dst_filled); + ZCMD_COPY(zc_pad2); + ZCMD_COPY(zc_history); + ZCMD_COPY(zc_guid); + ZCMD_COPY(zc_nvlist_conf); + ZCMD_COPY(zc_nvlist_conf_size); + ZCMD_COPY(zc_cookie); + ZCMD_COPY(zc_objset_type); + ZCMD_COPY(zc_perm_action); + ZCMD_COPY(zc_history_len); + ZCMD_COPY(zc_history_offset); + ZCMD_COPY(zc_obj); + ZCMD_COPY(zc_iflags); + ZCMD_COPY(zc_share); + ZCMD_COPY(zc_jailid); + ZCMD_COPY(zc_objset_stats); + edbp_c->zc_begin_record = zc->zc_begin_record.drr_u.drr_begin; + ZCMD_COPY(zc_inject_record); + ZCMD_COPY(zc_defer_destroy); + ZCMD_COPY(zc_flags); + ZCMD_COPY(zc_action_handle); + ZCMD_COPY(zc_cleanup_fd); + ZCMD_COPY(zc_simple); + ZCMD_COPY(zc_sendobj); + ZCMD_COPY(zc_fromobj); + ZCMD_COPY(zc_createtxg); + ZCMD_COPY(zc_stat); +#undef ZCMD_COPY + break; + case ZFS_CMD_COMPAT_ZCMD: zcmd_c = (void *)addr; /* zc */ @@ -311,14 +390,7 @@ zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int request, ZCMD_COPY(zc_share); ZCMD_COPY(zc_jailid); ZCMD_COPY(zc_objset_stats); - - /* - * zc_begin_record, zc_inject_record didn't change in embedeed-data - * block pointers - * - * TODO: CTASSERT? - */ - ZCMD_COPY(zc_begin_record); + zcmd_c->zc_begin_record = zc->zc_begin_record.drr_u.drr_begin; ZCMD_COPY(zc_inject_record); /* boolean_t -> uint32_t */ @@ -328,7 +400,6 @@ zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int request, ZCMD_COPY(zc_action_handle); ZCMD_COPY(zc_cleanup_fd); ZCMD_COPY(zc_simple); - bcopy(zc->zc_pad, zcmd_c->zc_pad, sizeof(zcmd_c->zc_pad)); ZCMD_COPY(zc_sendobj); ZCMD_COPY(zc_fromobj); ZCMD_COPY(zc_createtxg); @@ -361,13 +432,12 @@ zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int request, zcdm_c->zc_share = zc->zc_share; zcdm_c->zc_jailid = zc->zc_jailid; zcdm_c->zc_objset_stats = zc->zc_objset_stats; - zcdm_c->zc_begin_record = zc->zc_begin_record; + zcdm_c->zc_begin_record = zc->zc_begin_record.drr_u.drr_begin; zcdm_c->zc_defer_destroy = zc->zc_defer_destroy; zcdm_c->zc_temphold = 0; zcdm_c->zc_action_handle = zc->zc_action_handle; zcdm_c->zc_cleanup_fd = zc->zc_cleanup_fd; zcdm_c->zc_simple = zc->zc_simple; - bcopy(zc->zc_pad, zcdm_c->zc_pad, sizeof(zcdm_c->zc_pad)); zcdm_c->zc_sendobj = zc->zc_sendobj; zcdm_c->zc_fromobj = zc->zc_fromobj; zcdm_c->zc_createtxg = zc->zc_createtxg; @@ -407,13 +477,12 @@ zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int request, zc28_c->zc_share = zc->zc_share; zc28_c->zc_jailid = zc->zc_jailid; zc28_c->zc_objset_stats = zc->zc_objset_stats; - zc28_c->zc_begin_record = zc->zc_begin_record; + zc28_c->zc_begin_record = zc->zc_begin_record.drr_u.drr_begin; zc28_c->zc_defer_destroy = zc->zc_defer_destroy; zc28_c->zc_temphold = 0; zc28_c->zc_action_handle = zc->zc_action_handle; zc28_c->zc_cleanup_fd = zc->zc_cleanup_fd; zc28_c->zc_simple = zc->zc_simple; - bcopy(zc->zc_pad, zc28_c->zc_pad, sizeof(zc28_c->zc_pad)); zc28_c->zc_sendobj = zc->zc_sendobj; zc28_c->zc_fromobj = zc->zc_fromobj; zc28_c->zc_createtxg = zc->zc_createtxg; @@ -479,7 +548,7 @@ zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int request, zc_c->zc_share = zc->zc_share; zc_c->zc_jailid = zc->zc_jailid; zc_c->zc_objset_stats = zc->zc_objset_stats; - zc_c->zc_begin_record = zc->zc_begin_record; + zc_c->zc_begin_record = zc->zc_begin_record.drr_u.drr_begin; /* zc_inject_record */ zc_c->zc_inject_record.zi_objset = @@ -697,6 +766,12 @@ zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag) zp.zfs_cmd_size = sizeof(zfs_cmd_t); zp.zfs_ioctl_version = ZFS_IOCVER_CURRENT; return (ioctl(fd, ncmd, &zp)); + case ZFS_CMD_COMPAT_EDBP: + ncmd = _IOWR('Z', request, struct zfs_iocparm); + zp.zfs_cmd = (uint64_t)zc; + zp.zfs_cmd_size = sizeof(zfs_cmd_edbp_t); + zp.zfs_ioctl_version = ZFS_IOCVER_EDBP; + return (ioctl(fd, ncmd, &zp)); case ZFS_CMD_COMPAT_ZCMD: ncmd = _IOWR('Z', request, struct zfs_iocparm); zp.zfs_cmd = (uint64_t)zc; @@ -801,7 +876,7 @@ zfs_ioctl_compat_innvl(zfs_cmd_t *zc, nvlist_t * innvl, const int vec, int err; if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC || - cflag == ZFS_CMD_COMPAT_ZCMD) + cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP) goto out; switch (vec) { @@ -953,7 +1028,7 @@ zfs_ioctl_compat_outnvl(zfs_cmd_t *zc, nvlist_t * outnvl, const int vec, nvlist_t *tmpnvl; if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC || - cflag == ZFS_CMD_COMPAT_ZCMD) + cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP) return (outnvl); switch (vec) { diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h b/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h index bdcac6f9c3b..8361aa32b45 100644 --- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h +++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h @@ -52,7 +52,8 @@ extern "C" { #define ZFS_IOCVER_LZC 2 #define ZFS_IOCVER_ZCMD 3 #define ZFS_IOCVER_EDBP 4 -#define ZFS_IOCVER_CURRENT ZFS_IOCVER_EDBP +#define ZFS_IOCVER_RESUME 5 +#define ZFS_IOCVER_CURRENT ZFS_IOCVER_RESUME /* compatibility conversion flag */ #define ZFS_CMD_COMPAT_NONE 0 @@ -61,6 +62,7 @@ extern "C" { #define ZFS_CMD_COMPAT_DEADMAN 3 #define ZFS_CMD_COMPAT_LZC 4 #define ZFS_CMD_COMPAT_ZCMD 5 +#define ZFS_CMD_COMPAT_EDBP 6 #define ZFS_IOC_COMPAT_PASS 254 #define ZFS_IOC_COMPAT_FAIL 255 @@ -246,6 +248,49 @@ typedef struct zfs_cmd_zcmd { zfs_stat_t zc_stat; } zfs_cmd_zcmd_t; +typedef struct zfs_cmd_edbp { + char zc_name[MAXPATHLEN]; /* name of pool or dataset */ + uint64_t zc_nvlist_src; /* really (char *) */ + uint64_t zc_nvlist_src_size; + uint64_t zc_nvlist_dst; /* really (char *) */ + uint64_t zc_nvlist_dst_size; + boolean_t zc_nvlist_dst_filled; /* put an nvlist in dst? */ + int zc_pad2; + + /* + * The following members are for legacy ioctls which haven't been + * converted to the new method. + */ + uint64_t zc_history; /* really (char *) */ + char zc_value[MAXPATHLEN * 2]; + char zc_string[MAXNAMELEN]; + uint64_t zc_guid; + uint64_t zc_nvlist_conf; /* really (char *) */ + uint64_t zc_nvlist_conf_size; + uint64_t zc_cookie; + uint64_t zc_objset_type; + uint64_t zc_perm_action; + uint64_t zc_history_len; + uint64_t zc_history_offset; + uint64_t zc_obj; + uint64_t zc_iflags; /* internal to zfs(7fs) */ + zfs_share_t zc_share; + uint64_t zc_jailid; + dmu_objset_stats_t zc_objset_stats; + struct drr_begin zc_begin_record; + zinject_record_t zc_inject_record; + uint32_t zc_defer_destroy; + uint32_t zc_flags; + uint64_t zc_action_handle; + int zc_cleanup_fd; + uint8_t zc_simple; + uint8_t zc_pad[3]; /* alignment */ + uint64_t zc_sendobj; + uint64_t zc_fromobj; + uint64_t zc_createtxg; + zfs_stat_t zc_stat; +} zfs_cmd_edbp_t; + #ifdef _KERNEL unsigned static long zfs_ioctl_v15_to_v28[] = { 0, /* 0 ZFS_IOC_POOL_CREATE */ diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c b/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c index dda72de8737..20b54d84dbc 100644 --- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c +++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c @@ -71,6 +71,11 @@ zfs_prop_init(void) { "fletcher4", ZIO_CHECKSUM_FLETCHER_4 }, { "sha256", ZIO_CHECKSUM_SHA256 }, { "noparity", ZIO_CHECKSUM_NOPARITY }, +#ifdef illumos + { "sha512", ZIO_CHECKSUM_SHA512 }, + { "skein", ZIO_CHECKSUM_SKEIN }, + { "edonr", ZIO_CHECKSUM_EDONR }, +#endif { NULL } }; @@ -81,6 +86,16 @@ zfs_prop_init(void) { "sha256", ZIO_CHECKSUM_SHA256 }, { "sha256,verify", ZIO_CHECKSUM_SHA256 | ZIO_CHECKSUM_VERIFY }, +#ifdef illumos + { "sha512", ZIO_CHECKSUM_SHA512 }, + { "sha512,verify", + ZIO_CHECKSUM_SHA512 | ZIO_CHECKSUM_VERIFY }, + { "skein", ZIO_CHECKSUM_SKEIN }, + { "skein,verify", + ZIO_CHECKSUM_SKEIN | ZIO_CHECKSUM_VERIFY }, + { "edonr,verify", + ZIO_CHECKSUM_EDONR | ZIO_CHECKSUM_VERIFY }, +#endif { NULL } }; @@ -225,12 +240,12 @@ zfs_prop_init(void) zprop_register_index(ZFS_PROP_CHECKSUM, "checksum", ZIO_CHECKSUM_DEFAULT, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, - "on | off | fletcher2 | fletcher4 | sha256", "CHECKSUM", - checksum_table); + "on | off | fletcher2 | fletcher4 | sha256 | sha512 | " + "skein | edonr", "CHECKSUM", checksum_table); zprop_register_index(ZFS_PROP_DEDUP, "dedup", ZIO_CHECKSUM_OFF, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, - "on | off | verify | sha256[,verify]", "DEDUP", - dedup_table); + "on | off | verify | sha256[,verify], sha512[,verify], " + "skein[,verify], edonr,verify", "DEDUP", dedup_table); zprop_register_index(ZFS_PROP_COMPRESSION, "compression", ZIO_COMPRESS_DEFAULT, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, @@ -343,6 +358,10 @@ zfs_prop_init(void) zprop_register_string(ZFS_PROP_MLSLABEL, "mlslabel", ZFS_MLSLABEL_DEFAULT, PROP_INHERIT, ZFS_TYPE_DATASET, "", "MLSLABEL"); + zprop_register_string(ZFS_PROP_RECEIVE_RESUME_TOKEN, + "receive_resume_token", + NULL, PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, + "", "RESUMETOK"); /* readonly number properties */ zprop_register_number(ZFS_PROP_USED, "used", 0, PROP_READONLY, diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c index 42c001a7cda..ba16dedcecd 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c @@ -1552,7 +1552,7 @@ arc_cksum_verify(arc_buf_t *buf) mutex_exit(&buf->b_hdr->b_l1hdr.b_freeze_lock); return; } - fletcher_2_native(buf->b_data, buf->b_hdr->b_size, &zc); + fletcher_2_native(buf->b_data, buf->b_hdr->b_size, NULL, &zc); if (!ZIO_CHECKSUM_EQUAL(*buf->b_hdr->b_freeze_cksum, zc)) panic("buffer modified while frozen!"); mutex_exit(&buf->b_hdr->b_l1hdr.b_freeze_lock); @@ -1565,7 +1565,7 @@ arc_cksum_equal(arc_buf_t *buf) int equal; mutex_enter(&buf->b_hdr->b_l1hdr.b_freeze_lock); - fletcher_2_native(buf->b_data, buf->b_hdr->b_size, &zc); + fletcher_2_native(buf->b_data, buf->b_hdr->b_size, NULL, &zc); equal = ZIO_CHECKSUM_EQUAL(*buf->b_hdr->b_freeze_cksum, zc); mutex_exit(&buf->b_hdr->b_l1hdr.b_freeze_lock); @@ -1585,7 +1585,7 @@ arc_cksum_compute(arc_buf_t *buf, boolean_t force) } buf->b_hdr->b_freeze_cksum = kmem_alloc(sizeof (zio_cksum_t), KM_SLEEP); fletcher_2_native(buf->b_data, buf->b_hdr->b_size, - buf->b_hdr->b_freeze_cksum); + NULL, buf->b_hdr->b_freeze_cksum); mutex_exit(&buf->b_hdr->b_l1hdr.b_freeze_lock); #ifdef illumos arc_buf_watch(buf); @@ -5295,6 +5295,16 @@ arc_init(void) arc_c_max = arc_c_min; arc_c_max = MAX(arc_c * 5, arc_c_max); + /* + * In userland, there's only the memory pressure that we artificially + * create (see arc_available_memory()). Don't let arc_c get too + * small, because it can cause transactions to be larger than + * arc_c, causing arc_tempreserve_space() to fail. + */ +#ifndef _KERNEL + arc_c_min = arc_c_max / 2; +#endif + #ifdef _KERNEL /* * Allow the tunables to override our calculations if they are @@ -6243,7 +6253,8 @@ l2arc_write_buffers(spa_t *spa, l2arc_dev_t *dev, uint64_t target_sz, boolean_t *headroom_boost) { arc_buf_hdr_t *hdr, *hdr_prev, *head; - uint64_t write_asize, write_sz, headroom, buf_compress_minsz; + uint64_t write_asize, write_sz, headroom, + buf_compress_minsz; void *buf_data; boolean_t full; l2arc_write_callback_t *cb; @@ -6405,6 +6416,7 @@ l2arc_write_buffers(spa_t *spa, l2arc_dev_t *dev, uint64_t target_sz, * using it to denote the header's state change. */ hdr->b_l2hdr.b_daddr = L2ARC_ADDR_UNSET; + hdr->b_flags |= ARC_FLAG_HAS_L2HDR; mutex_enter(&dev->l2ad_mtx); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c index 4ca13568dba..ac7ef3dc742 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c @@ -272,7 +272,7 @@ dbuf_verify_user(dmu_buf_impl_t *db, dbvu_verify_type_t verify_type) */ ASSERT3U(holds, >=, db->db_dirtycnt); } else { - if (db->db_immediate_evict == TRUE) + if (db->db_user_immediate_evict == TRUE) ASSERT3U(holds, >=, db->db_dirtycnt); else ASSERT3U(holds, >, 0); @@ -1115,6 +1115,32 @@ dbuf_release_bp(dmu_buf_impl_t *db) (void) arc_release(db->db_buf, db); } +/* + * We already have a dirty record for this TXG, and we are being + * dirtied again. + */ +static void +dbuf_redirty(dbuf_dirty_record_t *dr) +{ + dmu_buf_impl_t *db = dr->dr_dbuf; + + ASSERT(MUTEX_HELD(&db->db_mtx)); + + if (db->db_level == 0 && db->db_blkid != DMU_BONUS_BLKID) { + /* + * If this buffer has already been written out, + * we now need to reset its state. + */ + dbuf_unoverride(dr); + if (db->db.db_object != DMU_META_DNODE_OBJECT && + db->db_state != DB_NOFILL) { + /* Already released on initial dirty, so just thaw. */ + ASSERT(arc_released(db->db_buf)); + arc_buf_thaw(db->db_buf); + } + } +} + dbuf_dirty_record_t * dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx) { @@ -1187,16 +1213,7 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx) if (dr && dr->dr_txg == tx->tx_txg) { DB_DNODE_EXIT(db); - if (db->db_level == 0 && db->db_blkid != DMU_BONUS_BLKID) { - /* - * If this buffer has already been written out, - * we now need to reset its state. - */ - dbuf_unoverride(dr); - if (db->db.db_object != DMU_META_DNODE_OBJECT && - db->db_state != DB_NOFILL) - arc_buf_thaw(db->db_buf); - } + dbuf_redirty(dr); mutex_exit(&db->db_mtx); return (dr); } @@ -1500,6 +1517,30 @@ dmu_buf_will_dirty(dmu_buf_t *db_fake, dmu_tx_t *tx) ASSERT(tx->tx_txg != 0); ASSERT(!refcount_is_zero(&db->db_holds)); + /* + * Quick check for dirtyness. For already dirty blocks, this + * reduces runtime of this function by >90%, and overall performance + * by 50% for some workloads (e.g. file deletion with indirect blocks + * cached). + */ + mutex_enter(&db->db_mtx); + dbuf_dirty_record_t *dr; + for (dr = db->db_last_dirty; + dr != NULL && dr->dr_txg >= tx->tx_txg; dr = dr->dr_next) { + /* + * It's possible that it is already dirty but not cached, + * because there are some calls to dbuf_dirty() that don't + * go through dmu_buf_will_dirty(). + */ + if (dr->dr_txg == tx->tx_txg && db->db_state == DB_CACHED) { + /* This dbuf is already dirty and cached. */ + dbuf_redirty(dr); + mutex_exit(&db->db_mtx); + return; + } + } + mutex_exit(&db->db_mtx); + DB_DNODE_ENTER(db); if (RW_WRITE_HELD(&DB_DNODE(db)->dn_struct_rwlock)) rf |= DB_RF_HAVESTRUCT; @@ -1834,8 +1875,9 @@ dbuf_create(dnode_t *dn, uint8_t level, uint64_t blkid, db->db_blkptr = blkptr; db->db_user = NULL; - db->db_immediate_evict = 0; - db->db_freed_in_flight = 0; + db->db_user_immediate_evict = FALSE; + db->db_freed_in_flight = FALSE; + db->db_pending_evict = FALSE; if (blkid == DMU_BONUS_BLKID) { ASSERT3P(parent, ==, dn->dn_dbuf); @@ -2391,12 +2433,13 @@ dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag) arc_buf_freeze(db->db_buf); if (holds == db->db_dirtycnt && - db->db_level == 0 && db->db_immediate_evict) + db->db_level == 0 && db->db_user_immediate_evict) dbuf_evict_user(db); if (holds == 0) { if (db->db_blkid == DMU_BONUS_BLKID) { dnode_t *dn; + boolean_t evict_dbuf = db->db_pending_evict; /* * If the dnode moves here, we cannot cross this @@ -2411,7 +2454,7 @@ dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag) * Decrementing the dbuf count means that the bonus * buffer's dnode hold is no longer discounted in * dnode_move(). The dnode cannot move until after - * the dnode_rele_and_unlock() below. + * the dnode_rele() below. */ DB_DNODE_EXIT(db); @@ -2421,35 +2464,10 @@ dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag) */ mutex_exit(&db->db_mtx); - /* - * If the dnode has been freed, evict the bonus - * buffer immediately. The data in the bonus - * buffer is no longer relevant and this prevents - * a stale bonus buffer from being associated - * with this dnode_t should the dnode_t be reused - * prior to being destroyed. - */ - mutex_enter(&dn->dn_mtx); - if (dn->dn_type == DMU_OT_NONE || - dn->dn_free_txg != 0) { - /* - * Drop dn_mtx. It is a leaf lock and - * cannot be held when dnode_evict_bonus() - * acquires other locks in order to - * perform the eviction. - * - * Freed dnodes cannot be reused until the - * last hold is released. Since this bonus - * buffer has a hold, the dnode will remain - * in the free state, even without dn_mtx - * held, until the dnode_rele_and_unlock() - * below. - */ - mutex_exit(&dn->dn_mtx); + if (evict_dbuf) dnode_evict_bonus(dn); - mutex_enter(&dn->dn_mtx); - } - dnode_rele_and_unlock(dn, db); + + dnode_rele(dn, db); } else if (db->db_buf == NULL) { /* * This is a special case: we never associated this @@ -2496,7 +2514,7 @@ dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag) } else { dbuf_clear(db); } - } else if (db->db_objset->os_evicting || + } else if (db->db_pending_evict || arc_buf_eviction_needed(db->db_buf)) { dbuf_clear(db); } else { @@ -2544,7 +2562,7 @@ dmu_buf_set_user_ie(dmu_buf_t *db_fake, dmu_buf_user_t *user) { dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake; - db->db_immediate_evict = TRUE; + db->db_user_immediate_evict = TRUE; return (dmu_buf_set_user(db_fake, user)); } diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/ddt.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/ddt.c index df5b77e14e8..5f07f9baa6c 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/ddt.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/ddt.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2014 by Delphix. All rights reserved. + * Copyright (c) 2012, 2015 by Delphix. All rights reserved. */ #include @@ -64,7 +64,8 @@ ddt_object_create(ddt_t *ddt, enum ddt_type type, enum ddt_class class, spa_t *spa = ddt->ddt_spa; objset_t *os = ddt->ddt_os; uint64_t *objectp = &ddt->ddt_object[type][class]; - boolean_t prehash = zio_checksum_table[ddt->ddt_checksum].ci_dedup; + boolean_t prehash = zio_checksum_table[ddt->ddt_checksum].ci_flags & + ZCHECKSUM_FLAG_DEDUP; char name[DDT_NAMELEN]; ddt_object_name(ddt, type, class, name); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c index 4ed3f52028c..ed3f19cac85 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c @@ -1493,7 +1493,8 @@ dmu_sync_done(zio_t *zio, arc_buf_t *buf, void *varg) ASSERT(BP_EQUAL(bp, bp_orig)); ASSERT(zio->io_prop.zp_compress != ZIO_COMPRESS_OFF); - ASSERT(zio_checksum_table[chksum].ci_dedup); + ASSERT(zio_checksum_table[chksum].ci_flags & + ZCHECKSUM_FLAG_NOPWRITE); } dr->dt.dl.dr_overridden_by = *zio->io_bp; dr->dt.dl.dr_override_state = DR_OVERRIDDEN; @@ -1738,7 +1739,7 @@ dmu_sync(zio_t *pio, uint64_t txg, dmu_sync_cb_t *done, zgd_t *zgd) int dmu_object_set_blocksize(objset_t *os, uint64_t object, uint64_t size, int ibs, - dmu_tx_t *tx) + dmu_tx_t *tx) { dnode_t *dn; int err; @@ -1753,7 +1754,7 @@ dmu_object_set_blocksize(objset_t *os, uint64_t object, uint64_t size, int ibs, void dmu_object_set_checksum(objset_t *os, uint64_t object, uint8_t checksum, - dmu_tx_t *tx) + dmu_tx_t *tx) { dnode_t *dn; @@ -1773,7 +1774,7 @@ dmu_object_set_checksum(objset_t *os, uint64_t object, uint8_t checksum, void dmu_object_set_compress(objset_t *os, uint64_t object, uint8_t compress, - dmu_tx_t *tx) + dmu_tx_t *tx) { dnode_t *dn; @@ -1840,8 +1841,10 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, zio_prop_t *zp) * as well. Otherwise, the metadata checksum defaults * to fletcher4. */ - if (zio_checksum_table[checksum].ci_correctable < 1 || - zio_checksum_table[checksum].ci_eck) + if (!(zio_checksum_table[checksum].ci_flags & + ZCHECKSUM_FLAG_METADATA) || + (zio_checksum_table[checksum].ci_flags & + ZCHECKSUM_FLAG_EMBEDDED)) checksum = ZIO_CHECKSUM_FLETCHER_4; if (os->os_redundant_metadata == ZFS_REDUNDANT_METADATA_ALL || @@ -1880,17 +1883,20 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, zio_prop_t *zp) */ if (dedup_checksum != ZIO_CHECKSUM_OFF) { dedup = (wp & WP_DMU_SYNC) ? B_FALSE : B_TRUE; - if (!zio_checksum_table[checksum].ci_dedup) + if (!(zio_checksum_table[checksum].ci_flags & + ZCHECKSUM_FLAG_DEDUP)) dedup_verify = B_TRUE; } /* - * Enable nopwrite if we have a cryptographically secure - * checksum that has no known collisions (i.e. SHA-256) - * and compression is enabled. We don't enable nopwrite if - * dedup is enabled as the two features are mutually exclusive. + * Enable nopwrite if we have secure enough checksum + * algorithm (see comment in zio_nop_write) and + * compression is enabled. We don't enable nopwrite if + * dedup is enabled as the two features are mutually + * exclusive. */ - nopwrite = (!dedup && zio_checksum_table[checksum].ci_dedup && + nopwrite = (!dedup && (zio_checksum_table[checksum].ci_flags & + ZCHECKSUM_FLAG_NOPWRITE) && compress != ZIO_COMPRESS_OFF && zfs_nopwrite_enabled); } @@ -1938,7 +1944,8 @@ dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole, uint64_t *off) * ID and wait for that to be synced. */ int -dmu_object_wait_synced(objset_t *os, uint64_t object) { +dmu_object_wait_synced(objset_t *os, uint64_t object) +{ dnode_t *dn; int error, i; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c index f84ff378c94..79de1d127d4 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c @@ -362,6 +362,17 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp, * checksum/compression/copies. */ if (ds != NULL) { + boolean_t needlock = B_FALSE; + + /* + * Note: it's valid to open the objset if the dataset is + * long-held, in which case the pool_config lock will not + * be held. + */ + if (!dsl_pool_config_held(dmu_objset_pool(os))) { + needlock = B_TRUE; + dsl_pool_config_enter(dmu_objset_pool(os), FTAG); + } err = dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_PRIMARYCACHE), primary_cache_changed_cb, os); @@ -413,6 +424,8 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp, recordsize_changed_cb, os); } } + if (needlock) + dsl_pool_config_exit(dmu_objset_pool(os), FTAG); if (err != 0) { VERIFY(arc_buf_remove_ref(os->os_phys_buf, &os->os_phys_buf)); @@ -469,6 +482,13 @@ dmu_objset_from_ds(dsl_dataset_t *ds, objset_t **osp) { int err = 0; + /* + * We shouldn't be doing anything with dsl_dataset_t's unless the + * pool_config lock is held, or the dataset is long-held. + */ + ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool) || + dsl_dataset_long_held(ds)); + mutex_enter(&ds->ds_opening_lock); if (ds->ds_objset == NULL) { objset_t *os; @@ -686,7 +706,6 @@ dmu_objset_evict(objset_t *os) if (os->os_sa) sa_tear_down(os); - os->os_evicting = B_TRUE; dmu_objset_evict_dbufs(os); mutex_enter(&os->os_lock); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c index ef139616d0e..ede1555b709 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c @@ -66,12 +66,14 @@ int zfs_send_queue_length = 16 * 1024 * 1024; int zfs_recv_queue_length = 16 * 1024 * 1024; static char *dmu_recv_tag = "dmu_recv_tag"; -static const char *recv_clone_name = "%recv"; +const char *recv_clone_name = "%recv"; #define BP_SPAN(datablkszsec, indblkshift, level) \ (((uint64_t)datablkszsec) << (SPA_MINBLOCKSHIFT + \ (level) * (indblkshift - SPA_BLKPTRSHIFT))) +static void byteswap_record(dmu_replay_record_t *drr); + struct send_thread_arg { bqueue_t q; dsl_dataset_t *ds; /* Dataset to traverse */ @@ -79,6 +81,7 @@ struct send_thread_arg { int flags; /* flags to pass to traverse_dataset */ int error_code; boolean_t cancel; + zbookmark_phys_t resume; }; struct send_block_record { @@ -93,7 +96,7 @@ struct send_block_record { static int dump_bytes(dmu_sendarg_t *dsp, void *buf, int len) { - dsl_dataset_t *ds = dsp->dsa_os->os_dsl_dataset; + dsl_dataset_t *ds = dmu_objset_ds(dsp->dsa_os); struct uio auio; struct iovec aiov; ASSERT0(len % 8); @@ -166,7 +169,7 @@ dump_free(dmu_sendarg_t *dsp, uint64_t object, uint64_t offset, * that the receiving system doesn't have any dbufs in the range * being freed. This is always true because there is a one-record * constraint: we only send one WRITE record for any given - * object+offset. We know that the one-record constraint is + * object,offset. We know that the one-record constraint is * true because we always send data in increasing order by * object,offset. * @@ -289,7 +292,8 @@ dump_write(dmu_sendarg_t *dsp, dmu_object_type_t type, drrw->drr_checksumtype = ZIO_CHECKSUM_OFF; } else { drrw->drr_checksumtype = BP_GET_CHECKSUM(bp); - if (zio_checksum_table[drrw->drr_checksumtype].ci_dedup) + if (zio_checksum_table[drrw->drr_checksumtype].ci_flags & + ZCHECKSUM_FLAG_DEDUP) drrw->drr_checksumflags |= DRR_CHECKSUM_DEDUP; DDK_SET_LSIZE(&drrw->drr_key, BP_GET_LSIZE(bp)); DDK_SET_PSIZE(&drrw->drr_key, BP_GET_PSIZE(bp)); @@ -414,6 +418,19 @@ dump_dnode(dmu_sendarg_t *dsp, uint64_t object, dnode_phys_t *dnp) { struct drr_object *drro = &(dsp->dsa_drr->drr_u.drr_object); + if (object < dsp->dsa_resume_object) { + /* + * Note: when resuming, we will visit all the dnodes in + * the block of dnodes that we are resuming from. In + * this case it's unnecessary to send the dnodes prior to + * the one we are resuming from. We should be at most one + * block's worth of dnodes behind the resume point. + */ + ASSERT3U(dsp->dsa_resume_object - object, <, + 1 << (DNODE_BLOCK_SHIFT - DNODE_SHIFT)); + return (0); + } + if (dnp == NULL || dnp->dn_type == DMU_OT_NONE) return (dump_freeobjects(dsp, object, 1)); @@ -494,6 +511,9 @@ send_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, uint64_t record_size; int err = 0; + ASSERT(zb->zb_object == DMU_META_DNODE_OBJECT || + zb->zb_object >= sta->resume.zb_object); + if (sta->cancel) return (SET_ERROR(EINTR)); @@ -530,8 +550,10 @@ send_traverse_thread(void *arg) struct send_block_record *data; if (st_arg->ds != NULL) { - err = traverse_dataset(st_arg->ds, st_arg->fromtxg, - st_arg->flags, send_cb, arg); + err = traverse_dataset_resume(st_arg->ds, + st_arg->fromtxg, &st_arg->resume, + st_arg->flags, send_cb, st_arg); + if (err != EINTR) st_arg->error_code = err; } @@ -560,6 +582,9 @@ do_dump(dmu_sendarg_t *dsa, struct send_block_record *data) ASSERT3U(zb->zb_level, >=, 0); + ASSERT(zb->zb_object == DMU_META_DNODE_OBJECT || + zb->zb_object >= dsa->dsa_resume_object); + if (zb->zb_object != DMU_META_DNODE_OBJECT && DMU_OBJECT_IS_SPECIAL(zb->zb_object)) { return (0); @@ -620,6 +645,10 @@ do_dump(dmu_sendarg_t *dsa, struct send_block_record *data) uint64_t offset; ASSERT0(zb->zb_level); + ASSERT(zb->zb_object > dsa->dsa_resume_object || + (zb->zb_object == dsa->dsa_resume_object && + zb->zb_blkid * blksz >= dsa->dsa_resume_offset)); + if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &aflags, zb) != 0) { @@ -680,11 +709,13 @@ get_next_record(bqueue_t *bq, struct send_block_record *data) */ static int dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds, - zfs_bookmark_phys_t *ancestor_zb, boolean_t is_clone, boolean_t embedok, + zfs_bookmark_phys_t *ancestor_zb, + boolean_t is_clone, boolean_t embedok, boolean_t large_block_ok, int outfd, + uint64_t resumeobj, uint64_t resumeoff, #ifdef illumos - boolean_t large_block_ok, int outfd, vnode_t *vp, offset_t *off) + vnode_t *vp, offset_t *off) #else - boolean_t large_block_ok, int outfd, struct file *fp, offset_t *off) + struct file *fp, offset_t *off) #endif { objset_t *os; @@ -693,7 +724,7 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds, int err; uint64_t fromtxg = 0; uint64_t featureflags = 0; - struct send_thread_arg to_arg; + struct send_thread_arg to_arg = { 0 }; err = dmu_objset_from_ds(to_ds, &os); if (err != 0) { @@ -730,6 +761,10 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds, featureflags |= DMU_BACKUP_FEATURE_EMBED_DATA_LZ4; } + if (resumeobj != 0 || resumeoff != 0) { + featureflags |= DMU_BACKUP_FEATURE_RESUMING; + } + DMU_SET_FEATUREFLAGS(drr->drr_u.drr_begin.drr_versioninfo, featureflags); @@ -766,6 +801,8 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds, dsp->dsa_pending_op = PENDING_NONE; dsp->dsa_incremental = (ancestor_zb != NULL); dsp->dsa_featureflags = featureflags; + dsp->dsa_resume_object = resumeobj; + dsp->dsa_resume_offset = resumeoff; mutex_enter(&to_ds->ds_sendstream_lock); list_insert_head(&to_ds->ds_sendstreams, dsp); @@ -774,7 +811,27 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds, dsl_dataset_long_hold(to_ds, FTAG); dsl_pool_rele(dp, tag); - if (dump_record(dsp, NULL, 0) != 0) { + void *payload = NULL; + size_t payload_len = 0; + if (resumeobj != 0 || resumeoff != 0) { + dmu_object_info_t to_doi; + err = dmu_object_info(os, resumeobj, &to_doi); + if (err != 0) + goto out; + SET_BOOKMARK(&to_arg.resume, to_ds->ds_object, resumeobj, 0, + resumeoff / to_doi.doi_data_block_size); + + nvlist_t *nvl = fnvlist_alloc(); + fnvlist_add_uint64(nvl, "resume_object", resumeobj); + fnvlist_add_uint64(nvl, "resume_offset", resumeoff); + payload = fnvlist_pack(nvl, &payload_len); + drr->drr_payloadlen = payload_len; + fnvlist_free(nvl); + } + + err = dump_record(dsp, payload, payload_len); + fnvlist_pack_free(payload, payload_len); + if (err != 0) { err = dsp->dsa_err; goto out; } @@ -889,22 +946,22 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap, is_clone = (fromds->ds_dir != ds->ds_dir); dsl_dataset_rele(fromds, FTAG); err = dmu_send_impl(FTAG, dp, ds, &zb, is_clone, - embedok, large_block_ok, outfd, fp, off); + embedok, large_block_ok, outfd, 0, 0, fp, off); } else { err = dmu_send_impl(FTAG, dp, ds, NULL, B_FALSE, - embedok, large_block_ok, outfd, fp, off); + embedok, large_block_ok, outfd, 0, 0, fp, off); } dsl_dataset_rele(ds, FTAG); return (err); } int -dmu_send(const char *tosnap, const char *fromsnap, - boolean_t embedok, boolean_t large_block_ok, +dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, + boolean_t large_block_ok, int outfd, uint64_t resumeobj, uint64_t resumeoff, #ifdef illumos - int outfd, vnode_t *vp, offset_t *off) + vnode_t *vp, offset_t *off) #else - int outfd, struct file *fp, offset_t *off) + struct file *fp, offset_t *off) #endif { dsl_pool_t *dp; @@ -972,10 +1029,12 @@ dmu_send(const char *tosnap, const char *fromsnap, return (err); } err = dmu_send_impl(FTAG, dp, ds, &zb, is_clone, - embedok, large_block_ok, outfd, fp, off); + embedok, large_block_ok, + outfd, resumeobj, resumeoff, fp, off); } else { err = dmu_send_impl(FTAG, dp, ds, NULL, B_FALSE, - embedok, large_block_ok, outfd, fp, off); + embedok, large_block_ok, + outfd, resumeobj, resumeoff, fp, off); } if (owned) dsl_dataset_disown(ds, FTAG); @@ -1218,6 +1277,7 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) /* already checked */ ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC); + ASSERT(!(featureflags & DMU_BACKUP_FEATURE_RESUMING)); if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == DMU_COMPOUNDSTREAM || @@ -1230,6 +1290,10 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) spa_version(dp->dp_spa) < SPA_VERSION_SA) return (SET_ERROR(ENOTSUP)); + if (drba->drba_cookie->drc_resumable && + !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_EXTENSIBLE_DATASET)) + return (SET_ERROR(ENOTSUP)); + /* * The receiving code doesn't know how to translate a WRITE_EMBEDDED * record to a plan WRITE record, so the pool must have the @@ -1333,15 +1397,16 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) { dmu_recv_begin_arg_t *drba = arg; dsl_pool_t *dp = dmu_tx_pool(tx); + objset_t *mos = dp->dp_meta_objset; struct drr_begin *drrb = drba->drba_cookie->drc_drrb; const char *tofs = drba->drba_cookie->drc_tofs; dsl_dataset_t *ds, *newds; uint64_t dsobj; int error; - uint64_t crflags; + uint64_t crflags = 0; - crflags = (drrb->drr_flags & DRR_FLAG_CI_DATA) ? - DS_FLAG_CI_DATASET : 0; + if (drrb->drr_flags & DRR_FLAG_CI_DATA) + crflags |= DS_FLAG_CI_DATASET; error = dsl_dataset_hold(dp, tofs, FTAG, &ds); if (error == 0) { @@ -1379,6 +1444,31 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) } VERIFY0(dsl_dataset_own_obj(dp, dsobj, dmu_recv_tag, &newds)); + if (drba->drba_cookie->drc_resumable) { + dsl_dataset_zapify(newds, tx); + if (drrb->drr_fromguid != 0) { + VERIFY0(zap_add(mos, dsobj, DS_FIELD_RESUME_FROMGUID, + 8, 1, &drrb->drr_fromguid, tx)); + } + VERIFY0(zap_add(mos, dsobj, DS_FIELD_RESUME_TOGUID, + 8, 1, &drrb->drr_toguid, tx)); + VERIFY0(zap_add(mos, dsobj, DS_FIELD_RESUME_TONAME, + 1, strlen(drrb->drr_toname) + 1, drrb->drr_toname, tx)); + uint64_t one = 1; + uint64_t zero = 0; + VERIFY0(zap_add(mos, dsobj, DS_FIELD_RESUME_OBJECT, + 8, 1, &one, tx)); + VERIFY0(zap_add(mos, dsobj, DS_FIELD_RESUME_OFFSET, + 8, 1, &zero, tx)); + VERIFY0(zap_add(mos, dsobj, DS_FIELD_RESUME_BYTES, + 8, 1, &zero, tx)); + if (DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) & + DMU_BACKUP_FEATURE_EMBED_DATA) { + VERIFY0(zap_add(mos, dsobj, DS_FIELD_RESUME_EMBEDOK, + 8, 1, &one, tx)); + } + } + dmu_buf_will_dirty(newds->ds_dbuf, tx); dsl_dataset_phys(newds)->ds_flags |= DS_FLAG_INCONSISTENT; @@ -1396,56 +1486,192 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) spa_history_log_internal_ds(newds, "receive", tx, ""); } +static int +dmu_recv_resume_begin_check(void *arg, dmu_tx_t *tx) +{ + dmu_recv_begin_arg_t *drba = arg; + dsl_pool_t *dp = dmu_tx_pool(tx); + struct drr_begin *drrb = drba->drba_cookie->drc_drrb; + int error; + uint64_t featureflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo); + dsl_dataset_t *ds; + const char *tofs = drba->drba_cookie->drc_tofs; + + /* already checked */ + ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC); + ASSERT(featureflags & DMU_BACKUP_FEATURE_RESUMING); + + if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == + DMU_COMPOUNDSTREAM || + drrb->drr_type >= DMU_OST_NUMTYPES) + return (SET_ERROR(EINVAL)); + + /* Verify pool version supports SA if SA_SPILL feature set */ + if ((featureflags & DMU_BACKUP_FEATURE_SA_SPILL) && + spa_version(dp->dp_spa) < SPA_VERSION_SA) + return (SET_ERROR(ENOTSUP)); + + /* + * The receiving code doesn't know how to translate a WRITE_EMBEDDED + * record to a plain WRITE record, so the pool must have the + * EMBEDDED_DATA feature enabled if the stream has WRITE_EMBEDDED + * records. Same with WRITE_EMBEDDED records that use LZ4 compression. + */ + if ((featureflags & DMU_BACKUP_FEATURE_EMBED_DATA) && + !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_EMBEDDED_DATA)) + return (SET_ERROR(ENOTSUP)); + if ((featureflags & DMU_BACKUP_FEATURE_EMBED_DATA_LZ4) && + !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LZ4_COMPRESS)) + return (SET_ERROR(ENOTSUP)); + + char recvname[ZFS_MAXNAMELEN]; + + (void) snprintf(recvname, sizeof (recvname), "%s/%s", + tofs, recv_clone_name); + + if (dsl_dataset_hold(dp, recvname, FTAG, &ds) != 0) { + /* %recv does not exist; continue in tofs */ + error = dsl_dataset_hold(dp, tofs, FTAG, &ds); + if (error != 0) + return (error); + } + + /* check that ds is marked inconsistent */ + if (!DS_IS_INCONSISTENT(ds)) { + dsl_dataset_rele(ds, FTAG); + return (SET_ERROR(EINVAL)); + } + + /* check that there is resuming data, and that the toguid matches */ + if (!dsl_dataset_is_zapified(ds)) { + dsl_dataset_rele(ds, FTAG); + return (SET_ERROR(EINVAL)); + } + uint64_t val; + error = zap_lookup(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_TOGUID, sizeof (val), 1, &val); + if (error != 0 || drrb->drr_toguid != val) { + dsl_dataset_rele(ds, FTAG); + return (SET_ERROR(EINVAL)); + } + + /* + * Check if the receive is still running. If so, it will be owned. + * Note that nothing else can own the dataset (e.g. after the receive + * fails) because it will be marked inconsistent. + */ + if (dsl_dataset_has_owner(ds)) { + dsl_dataset_rele(ds, FTAG); + return (SET_ERROR(EBUSY)); + } + + /* There should not be any snapshots of this fs yet. */ + if (ds->ds_prev != NULL && ds->ds_prev->ds_dir == ds->ds_dir) { + dsl_dataset_rele(ds, FTAG); + return (SET_ERROR(EINVAL)); + } + + /* + * Note: resume point will be checked when we process the first WRITE + * record. + */ + + /* check that the origin matches */ + val = 0; + (void) zap_lookup(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_FROMGUID, sizeof (val), 1, &val); + if (drrb->drr_fromguid != val) { + dsl_dataset_rele(ds, FTAG); + return (SET_ERROR(EINVAL)); + } + + dsl_dataset_rele(ds, FTAG); + return (0); +} + +static void +dmu_recv_resume_begin_sync(void *arg, dmu_tx_t *tx) +{ + dmu_recv_begin_arg_t *drba = arg; + dsl_pool_t *dp = dmu_tx_pool(tx); + const char *tofs = drba->drba_cookie->drc_tofs; + dsl_dataset_t *ds; + uint64_t dsobj; + char recvname[ZFS_MAXNAMELEN]; + + (void) snprintf(recvname, sizeof (recvname), "%s/%s", + tofs, recv_clone_name); + + if (dsl_dataset_hold(dp, recvname, FTAG, &ds) != 0) { + /* %recv does not exist; continue in tofs */ + VERIFY0(dsl_dataset_hold(dp, tofs, FTAG, &ds)); + drba->drba_cookie->drc_newfs = B_TRUE; + } + + /* clear the inconsistent flag so that we can own it */ + ASSERT(DS_IS_INCONSISTENT(ds)); + dmu_buf_will_dirty(ds->ds_dbuf, tx); + dsl_dataset_phys(ds)->ds_flags &= ~DS_FLAG_INCONSISTENT; + dsobj = ds->ds_object; + dsl_dataset_rele(ds, FTAG); + + VERIFY0(dsl_dataset_own_obj(dp, dsobj, dmu_recv_tag, &ds)); + + dmu_buf_will_dirty(ds->ds_dbuf, tx); + dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_INCONSISTENT; + + ASSERT(!BP_IS_HOLE(dsl_dataset_get_blkptr(ds))); + + drba->drba_cookie->drc_ds = ds; + + spa_history_log_internal_ds(ds, "resume receive", tx, ""); +} + /* * NB: callers *MUST* call dmu_recv_stream() if dmu_recv_begin() * succeeds; otherwise we will leak the holds on the datasets. */ int -dmu_recv_begin(char *tofs, char *tosnap, struct drr_begin *drrb, - boolean_t force, char *origin, dmu_recv_cookie_t *drc) +dmu_recv_begin(char *tofs, char *tosnap, dmu_replay_record_t *drr_begin, + boolean_t force, boolean_t resumable, char *origin, dmu_recv_cookie_t *drc) { dmu_recv_begin_arg_t drba = { 0 }; - dmu_replay_record_t *drr; bzero(drc, sizeof (dmu_recv_cookie_t)); - drc->drc_drrb = drrb; + drc->drc_drr_begin = drr_begin; + drc->drc_drrb = &drr_begin->drr_u.drr_begin; drc->drc_tosnap = tosnap; drc->drc_tofs = tofs; drc->drc_force = force; + drc->drc_resumable = resumable; drc->drc_cred = CRED(); - if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) + if (drc->drc_drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) { drc->drc_byteswap = B_TRUE; - else if (drrb->drr_magic != DMU_BACKUP_MAGIC) - return (SET_ERROR(EINVAL)); - - drr = kmem_zalloc(sizeof (dmu_replay_record_t), KM_SLEEP); - drr->drr_type = DRR_BEGIN; - drr->drr_u.drr_begin = *drc->drc_drrb; - if (drc->drc_byteswap) { - fletcher_4_incremental_byteswap(drr, + fletcher_4_incremental_byteswap(drr_begin, + sizeof (dmu_replay_record_t), &drc->drc_cksum); + byteswap_record(drr_begin); + } else if (drc->drc_drrb->drr_magic == DMU_BACKUP_MAGIC) { + fletcher_4_incremental_native(drr_begin, sizeof (dmu_replay_record_t), &drc->drc_cksum); } else { - fletcher_4_incremental_native(drr, - sizeof (dmu_replay_record_t), &drc->drc_cksum); - } - kmem_free(drr, sizeof (dmu_replay_record_t)); - - if (drc->drc_byteswap) { - drrb->drr_magic = BSWAP_64(drrb->drr_magic); - drrb->drr_versioninfo = BSWAP_64(drrb->drr_versioninfo); - drrb->drr_creation_time = BSWAP_64(drrb->drr_creation_time); - drrb->drr_type = BSWAP_32(drrb->drr_type); - drrb->drr_toguid = BSWAP_64(drrb->drr_toguid); - drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid); + return (SET_ERROR(EINVAL)); } drba.drba_origin = origin; drba.drba_cookie = drc; drba.drba_cred = CRED(); - return (dsl_sync_task(tofs, dmu_recv_begin_check, dmu_recv_begin_sync, - &drba, 5, ZFS_SPACE_CHECK_NORMAL)); + if (DMU_GET_FEATUREFLAGS(drc->drc_drrb->drr_versioninfo) & + DMU_BACKUP_FEATURE_RESUMING) { + return (dsl_sync_task(tofs, + dmu_recv_resume_begin_check, dmu_recv_resume_begin_sync, + &drba, 5, ZFS_SPACE_CHECK_NORMAL)); + } else { + return (dsl_sync_task(tofs, + dmu_recv_begin_check, dmu_recv_begin_sync, + &drba, 5, ZFS_SPACE_CHECK_NORMAL)); + } } struct receive_record_arg { @@ -1457,6 +1683,7 @@ struct receive_record_arg { */ arc_buf_t *write_buf; int payload_size; + uint64_t bytes_read; /* bytes read from stream when record created */ boolean_t eos_marker; /* Marks the end of the stream */ bqueue_node_t node; }; @@ -1465,6 +1692,7 @@ struct receive_writer_arg { objset_t *os; boolean_t byteswap; bqueue_t q; + /* * These three args are used to signal to the main thread that we're * done. @@ -1472,9 +1700,13 @@ struct receive_writer_arg { kmutex_t mutex; kcondvar_t cv; boolean_t done; + int err; /* A map from guid to dataset to help handle dedup'd streams. */ avl_tree_t *guid_to_ds_map; + boolean_t resumable; + uint64_t last_object, last_offset; + uint64_t bytes_read; /* bytes read when current record created */ }; struct receive_arg { @@ -1482,6 +1714,7 @@ struct receive_arg { kthread_t *td; struct file *fp; uint64_t voff; /* The current offset in the stream */ + uint64_t bytes_read; /* * A record that has had its payload read in, but hasn't yet been handed * off to the worker thread. @@ -1577,14 +1810,21 @@ receive_read(struct receive_arg *ra, int len, void *buf) ra->err = restore_bytes(ra, buf + done, len - done, ra->voff, &resid); - if (resid == len - done) - ra->err = SET_ERROR(EINVAL); + if (resid == len - done) { + /* + * Note: ECKSUM indicates that the receive + * was interrupted and can potentially be resumed. + */ + ra->err = SET_ERROR(ECKSUM); + } ra->voff += len - done - resid; done = len - resid; if (ra->err != 0) return (ra->err); } + ra->bytes_read += len; + ASSERT3U(done, ==, len); return (0); } @@ -1685,6 +1925,43 @@ deduce_nblkptr(dmu_object_type_t bonus_type, uint64_t bonus_size) } } +static void +save_resume_state(struct receive_writer_arg *rwa, + uint64_t object, uint64_t offset, dmu_tx_t *tx) +{ + int txgoff = dmu_tx_get_txg(tx) & TXG_MASK; + + if (!rwa->resumable) + return; + + /* + * We use ds_resume_bytes[] != 0 to indicate that we need to + * update this on disk, so it must not be 0. + */ + ASSERT(rwa->bytes_read != 0); + + /* + * We only resume from write records, which have a valid + * (non-meta-dnode) object number. + */ + ASSERT(object != 0); + + /* + * For resuming to work correctly, we must receive records in order, + * sorted by object,offset. This is checked by the callers, but + * assert it here for good measure. + */ + ASSERT3U(object, >=, rwa->os->os_dsl_dataset->ds_resume_object[txgoff]); + ASSERT(object != rwa->os->os_dsl_dataset->ds_resume_object[txgoff] || + offset >= rwa->os->os_dsl_dataset->ds_resume_offset[txgoff]); + ASSERT3U(rwa->bytes_read, >=, + rwa->os->os_dsl_dataset->ds_resume_bytes[txgoff]); + + rwa->os->os_dsl_dataset->ds_resume_object[txgoff] = object; + rwa->os->os_dsl_dataset->ds_resume_offset[txgoff] = offset; + rwa->os->os_dsl_dataset->ds_resume_bytes[txgoff] = rwa->bytes_read; +} + static int receive_object(struct receive_writer_arg *rwa, struct drr_object *drro, void *data) @@ -1781,6 +2058,7 @@ receive_object(struct receive_writer_arg *rwa, struct drr_object *drro, dmu_buf_rele(db, FTAG); } dmu_tx_commit(tx); + return (0); } @@ -1806,6 +2084,7 @@ receive_freeobjects(struct receive_writer_arg *rwa, if (err != 0) return (err); } + return (0); } @@ -1820,6 +2099,18 @@ receive_write(struct receive_writer_arg *rwa, struct drr_write *drrw, !DMU_OT_IS_VALID(drrw->drr_type)) return (SET_ERROR(EINVAL)); + /* + * For resuming to work, records must be in increasing order + * by (object, offset). + */ + if (drrw->drr_object < rwa->last_object || + (drrw->drr_object == rwa->last_object && + drrw->drr_offset < rwa->last_offset)) { + return (SET_ERROR(EINVAL)); + } + rwa->last_object = drrw->drr_object; + rwa->last_offset = drrw->drr_offset; + if (dmu_object_info(rwa->os, drrw->drr_object, NULL) != 0) return (SET_ERROR(EINVAL)); @@ -1843,8 +2134,17 @@ receive_write(struct receive_writer_arg *rwa, struct drr_write *drrw, if (dmu_bonus_hold(rwa->os, drrw->drr_object, FTAG, &bonus) != 0) return (SET_ERROR(EINVAL)); dmu_assign_arcbuf(bonus, drrw->drr_offset, abuf, tx); + + /* + * Note: If the receive fails, we want the resume stream to start + * with the same record that we last successfully received (as opposed + * to the next record), so that we can verify that we are + * resuming from the correct location. + */ + save_resume_state(rwa, drrw->drr_object, drrw->drr_offset, tx); dmu_tx_commit(tx); dmu_buf_rele(bonus, FTAG); + return (0); } @@ -1903,43 +2203,48 @@ receive_write_byref(struct receive_writer_arg *rwa, dmu_write(rwa->os, drrwbr->drr_object, drrwbr->drr_offset, drrwbr->drr_length, dbp->db_data, tx); dmu_buf_rele(dbp, FTAG); + + /* See comment in restore_write. */ + save_resume_state(rwa, drrwbr->drr_object, drrwbr->drr_offset, tx); dmu_tx_commit(tx); return (0); } static int receive_write_embedded(struct receive_writer_arg *rwa, - struct drr_write_embedded *drrwnp, void *data) + struct drr_write_embedded *drrwe, void *data) { dmu_tx_t *tx; int err; - if (drrwnp->drr_offset + drrwnp->drr_length < drrwnp->drr_offset) + if (drrwe->drr_offset + drrwe->drr_length < drrwe->drr_offset) return (EINVAL); - if (drrwnp->drr_psize > BPE_PAYLOAD_SIZE) + if (drrwe->drr_psize > BPE_PAYLOAD_SIZE) return (EINVAL); - if (drrwnp->drr_etype >= NUM_BP_EMBEDDED_TYPES) + if (drrwe->drr_etype >= NUM_BP_EMBEDDED_TYPES) return (EINVAL); - if (drrwnp->drr_compression >= ZIO_COMPRESS_FUNCTIONS) + if (drrwe->drr_compression >= ZIO_COMPRESS_FUNCTIONS) return (EINVAL); tx = dmu_tx_create(rwa->os); - dmu_tx_hold_write(tx, drrwnp->drr_object, - drrwnp->drr_offset, drrwnp->drr_length); + dmu_tx_hold_write(tx, drrwe->drr_object, + drrwe->drr_offset, drrwe->drr_length); err = dmu_tx_assign(tx, TXG_WAIT); if (err != 0) { dmu_tx_abort(tx); return (err); } - dmu_write_embedded(rwa->os, drrwnp->drr_object, - drrwnp->drr_offset, data, drrwnp->drr_etype, - drrwnp->drr_compression, drrwnp->drr_lsize, drrwnp->drr_psize, + dmu_write_embedded(rwa->os, drrwe->drr_object, + drrwe->drr_offset, data, drrwe->drr_etype, + drrwe->drr_compression, drrwe->drr_lsize, drrwe->drr_psize, rwa->byteswap ^ ZFS_HOST_BYTEORDER, tx); + /* See comment in restore_write. */ + save_resume_state(rwa, drrwe->drr_object, drrwe->drr_offset, tx); dmu_tx_commit(tx); return (0); } @@ -2013,10 +2318,16 @@ receive_free(struct receive_writer_arg *rwa, struct drr_free *drrf) static void dmu_recv_cleanup_ds(dmu_recv_cookie_t *drc) { - char name[MAXNAMELEN]; - dsl_dataset_name(drc->drc_ds, name); - dsl_dataset_disown(drc->drc_ds, dmu_recv_tag); - (void) dsl_destroy_head(name); + if (drc->drc_resumable) { + /* wait for our resume state to be written to disk */ + txg_wait_synced(drc->drc_ds->ds_dir->dd_pool, 0); + dsl_dataset_disown(drc->drc_ds, dmu_recv_tag); + } else { + char name[MAXNAMELEN]; + dsl_dataset_name(drc->drc_ds, name); + dsl_dataset_disown(drc->drc_ds, dmu_recv_tag); + (void) dsl_destroy_head(name); + } } static void @@ -2043,12 +2354,17 @@ receive_read_payload_and_next_header(struct receive_arg *ra, int len, void *buf) if (len != 0) { ASSERT3U(len, <=, SPA_MAXBLOCKSIZE); - ra->rrd->payload = buf; - ra->rrd->payload_size = len; - err = receive_read(ra, len, ra->rrd->payload); + err = receive_read(ra, len, buf); if (err != 0) return (err); - receive_cksum(ra, len, ra->rrd->payload); + receive_cksum(ra, len, buf); + + /* note: rrd is NULL when reading the begin record's payload */ + if (ra->rrd != NULL) { + ra->rrd->payload = buf; + ra->rrd->payload_size = len; + ra->rrd->bytes_read = ra->bytes_read; + } } ra->prev_cksum = ra->cksum; @@ -2056,6 +2372,7 @@ receive_read_payload_and_next_header(struct receive_arg *ra, int len, void *buf) ra->next_rrd = kmem_zalloc(sizeof (*ra->next_rrd), KM_SLEEP); err = receive_read(ra, sizeof (ra->next_rrd->header), &ra->next_rrd->header); + ra->next_rrd->bytes_read = ra->bytes_read; if (err != 0) { kmem_free(ra->next_rrd, sizeof (*ra->next_rrd)); ra->next_rrd = NULL; @@ -2235,7 +2552,7 @@ receive_read_record(struct receive_arg *ra) { struct drr_end *drre = &ra->rrd->header.drr_u.drr_end; if (!ZIO_CHECKSUM_EQUAL(ra->prev_cksum, drre->drr_checksum)) - return (SET_ERROR(EINVAL)); + return (SET_ERROR(ECKSUM)); return (0); } case DRR_SPILL: @@ -2262,6 +2579,10 @@ receive_process_record(struct receive_writer_arg *rwa, { int err; + /* Processing in order, therefore bytes_read should be increasing. */ + ASSERT3U(rrd->bytes_read, >=, rwa->bytes_read); + rwa->bytes_read = rrd->bytes_read; + switch (rrd->header.drr_type) { case DRR_OBJECT: { @@ -2357,6 +2678,33 @@ receive_writer_thread(void *arg) thread_exit(); } +static int +resume_check(struct receive_arg *ra, nvlist_t *begin_nvl) +{ + uint64_t val; + objset_t *mos = dmu_objset_pool(ra->os)->dp_meta_objset; + uint64_t dsobj = dmu_objset_id(ra->os); + uint64_t resume_obj, resume_off; + + if (nvlist_lookup_uint64(begin_nvl, + "resume_object", &resume_obj) != 0 || + nvlist_lookup_uint64(begin_nvl, + "resume_offset", &resume_off) != 0) { + return (SET_ERROR(EINVAL)); + } + VERIFY0(zap_lookup(mos, dsobj, + DS_FIELD_RESUME_OBJECT, sizeof (val), 1, &val)); + if (resume_obj != val) + return (SET_ERROR(EINVAL)); + VERIFY0(zap_lookup(mos, dsobj, + DS_FIELD_RESUME_OFFSET, sizeof (val), 1, &val)); + if (resume_off != val) + return (SET_ERROR(EINVAL)); + + return (0); +} + + /* * Read in the stream's records, one by one, and apply them to the pool. There * are two threads involved; the thread that calls this function will spin up a @@ -2377,12 +2725,20 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, struct file *fp, offset_t *voffp, struct receive_arg ra = { 0 }; struct receive_writer_arg rwa = { 0 }; int featureflags; + nvlist_t *begin_nvl = NULL; ra.byteswap = drc->drc_byteswap; ra.cksum = drc->drc_cksum; ra.td = curthread; ra.fp = fp; ra.voff = *voffp; + + if (dsl_dataset_is_zapified(drc->drc_ds)) { + (void) zap_lookup(drc->drc_ds->ds_dir->dd_pool->dp_meta_objset, + drc->drc_ds->ds_object, DS_FIELD_RESUME_BYTES, + sizeof (ra.bytes_read), 1, &ra.bytes_read); + } + list_create(&ra.ignore_obj_list, sizeof (struct receive_ign_obj_node), offsetof(struct receive_ign_obj_node, node)); @@ -2435,9 +2791,29 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, struct file *fp, offset_t *voffp, drc->drc_guid_to_ds_map = rwa.guid_to_ds_map; } - err = receive_read_payload_and_next_header(&ra, 0, NULL); - if (err) + uint32_t payloadlen = drc->drc_drr_begin->drr_payloadlen; + void *payload = NULL; + if (payloadlen != 0) + payload = kmem_alloc(payloadlen, KM_SLEEP); + + err = receive_read_payload_and_next_header(&ra, payloadlen, payload); + if (err != 0) { + if (payloadlen != 0) + kmem_free(payload, payloadlen); goto out; + } + if (payloadlen != 0) { + err = nvlist_unpack(payload, payloadlen, &begin_nvl, KM_SLEEP); + kmem_free(payload, payloadlen); + if (err != 0) + goto out; + } + + if (featureflags & DMU_BACKUP_FEATURE_RESUMING) { + err = resume_check(&ra, begin_nvl); + if (err != 0) + goto out; + } (void) bqueue_init(&rwa.q, zfs_recv_queue_length, offsetof(struct receive_record_arg, node)); @@ -2445,6 +2821,7 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, struct file *fp, offset_t *voffp, mutex_init(&rwa.mutex, NULL, MUTEX_DEFAULT, NULL); rwa.os = ra.os; rwa.byteswap = drc->drc_byteswap; + rwa.resumable = drc->drc_resumable; (void) thread_create(NULL, 0, receive_writer_thread, &rwa, 0, &p0, TS_RUN, minclsyspri); @@ -2503,13 +2880,15 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, struct file *fp, offset_t *voffp, err = rwa.err; out: + nvlist_free(begin_nvl); if ((featureflags & DMU_BACKUP_FEATURE_DEDUP) && (cleanup_fd != -1)) zfs_onexit_fd_rele(cleanup_fd); if (err != 0) { /* - * destroy what we created, so we don't leave it in the - * inconsistent restoring state. + * Clean up references. If receive is not resumable, + * destroy what we created, so we don't leave it in + * the inconsistent state. */ dmu_recv_cleanup_ds(drc); } @@ -2669,6 +3048,20 @@ dmu_recv_end_sync(void *arg, dmu_tx_t *tx) dmu_buf_will_dirty(ds->ds_dbuf, tx); dsl_dataset_phys(ds)->ds_flags &= ~DS_FLAG_INCONSISTENT; + if (dsl_dataset_has_resume_receive_state(ds)) { + (void) zap_remove(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_FROMGUID, tx); + (void) zap_remove(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_OBJECT, tx); + (void) zap_remove(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_OFFSET, tx); + (void) zap_remove(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_BYTES, tx); + (void) zap_remove(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_TOGUID, tx); + (void) zap_remove(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_TONAME, tx); + } } drc->drc_newsnapobj = dsl_dataset_phys(drc->drc_ds)->ds_prev_snap_obj; /* diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_traverse.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_traverse.c index 151d04c0fb9..2c718df0489 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_traverse.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_traverse.c @@ -48,6 +48,7 @@ typedef struct prefetch_data { int pd_flags; boolean_t pd_cancel; boolean_t pd_exited; + zbookmark_phys_t pd_resume; } prefetch_data_t; typedef struct traverse_data { @@ -307,59 +308,52 @@ traverse_visitbp(traverse_data_t *td, const dnode_phys_t *dnp, arc_flags_t flags = ARC_FLAG_WAIT; int i; int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT; - dnode_phys_t *cdnp; err = arc_read(NULL, td->td_spa, bp, arc_getbuf_func, &buf, ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb); if (err != 0) goto post; - cdnp = buf->b_data; + dnode_phys_t *child_dnp = buf->b_data; for (i = 0; i < epb; i++) { - prefetch_dnode_metadata(td, &cdnp[i], zb->zb_objset, - zb->zb_blkid * epb + i); + prefetch_dnode_metadata(td, &child_dnp[i], + zb->zb_objset, zb->zb_blkid * epb + i); } /* recursively visitbp() blocks below this */ for (i = 0; i < epb; i++) { - err = traverse_dnode(td, &cdnp[i], zb->zb_objset, - zb->zb_blkid * epb + i); + err = traverse_dnode(td, &child_dnp[i], + zb->zb_objset, zb->zb_blkid * epb + i); if (err != 0) break; } } else if (BP_GET_TYPE(bp) == DMU_OT_OBJSET) { arc_flags_t flags = ARC_FLAG_WAIT; - objset_phys_t *osp; - dnode_phys_t *mdnp, *gdnp, *udnp; err = arc_read(NULL, td->td_spa, bp, arc_getbuf_func, &buf, ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb); if (err != 0) goto post; - osp = buf->b_data; - mdnp = &osp->os_meta_dnode; - gdnp = &osp->os_groupused_dnode; - udnp = &osp->os_userused_dnode; - - prefetch_dnode_metadata(td, mdnp, zb->zb_objset, + objset_phys_t *osp = buf->b_data; + prefetch_dnode_metadata(td, &osp->os_meta_dnode, zb->zb_objset, DMU_META_DNODE_OBJECT); if (arc_buf_size(buf) >= sizeof (objset_phys_t)) { - prefetch_dnode_metadata(td, gdnp, zb->zb_objset, - DMU_GROUPUSED_OBJECT); - prefetch_dnode_metadata(td, udnp, zb->zb_objset, - DMU_USERUSED_OBJECT); + prefetch_dnode_metadata(td, &osp->os_groupused_dnode, + zb->zb_objset, DMU_GROUPUSED_OBJECT); + prefetch_dnode_metadata(td, &osp->os_userused_dnode, + zb->zb_objset, DMU_USERUSED_OBJECT); } - err = traverse_dnode(td, mdnp, zb->zb_objset, + err = traverse_dnode(td, &osp->os_meta_dnode, zb->zb_objset, DMU_META_DNODE_OBJECT); if (err == 0 && arc_buf_size(buf) >= sizeof (objset_phys_t)) { - err = traverse_dnode(td, gdnp, zb->zb_objset, - DMU_GROUPUSED_OBJECT); + err = traverse_dnode(td, &osp->os_groupused_dnode, + zb->zb_objset, DMU_GROUPUSED_OBJECT); } if (err == 0 && arc_buf_size(buf) >= sizeof (objset_phys_t)) { - err = traverse_dnode(td, udnp, zb->zb_objset, - DMU_USERUSED_OBJECT); + err = traverse_dnode(td, &osp->os_userused_dnode, + zb->zb_objset, DMU_USERUSED_OBJECT); } } @@ -391,9 +385,15 @@ post: * Set the bookmark to the first level-0 block that we need * to visit. This way, the resuming code does not need to * deal with resuming from indirect blocks. + * + * Note, if zb_level <= 0, dnp may be NULL, so we don't want + * to dereference it. */ - td->td_resume->zb_blkid = zb->zb_blkid << - (zb->zb_level * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT)); + td->td_resume->zb_blkid = zb->zb_blkid; + if (zb->zb_level > 0) { + td->td_resume->zb_blkid <<= zb->zb_level * + (dnp->dn_indblkshift - SPA_BLKPTRSHIFT); + } td->td_paused = B_TRUE; } @@ -425,6 +425,10 @@ traverse_dnode(traverse_data_t *td, const dnode_phys_t *dnp, int j, err = 0; zbookmark_phys_t czb; + if (object != DMU_META_DNODE_OBJECT && td->td_resume != NULL && + object < td->td_resume->zb_object) + return (0); + if (td->td_flags & TRAVERSE_PRE) { SET_BOOKMARK(&czb, objset, object, ZB_DNODE_LEVEL, ZB_DNODE_BLKID); @@ -501,6 +505,7 @@ traverse_prefetch_thread(void *arg) td.td_func = traverse_prefetcher; td.td_arg = td_main->td_pfd; td.td_pfd = NULL; + td.td_resume = &td_main->td_pfd->pd_resume; SET_BOOKMARK(&czb, td.td_objset, ZB_ROOT_OBJECT, ZB_ROOT_LEVEL, ZB_ROOT_BLKID); @@ -529,12 +534,6 @@ traverse_impl(spa_t *spa, dsl_dataset_t *ds, uint64_t objset, blkptr_t *rootbp, ASSERT(ds == NULL || objset == ds->ds_object); ASSERT(!(flags & TRAVERSE_PRE) || !(flags & TRAVERSE_POST)); - /* - * The data prefetching mechanism (the prefetch thread) is incompatible - * with resuming from a bookmark. - */ - ASSERT(resume == NULL || !(flags & TRAVERSE_PREFETCH_DATA)); - td.td_spa = spa; td.td_objset = objset; td.td_rootbp = rootbp; @@ -554,6 +553,8 @@ traverse_impl(spa_t *spa, dsl_dataset_t *ds, uint64_t objset, blkptr_t *rootbp, } pd.pd_flags = flags; + if (resume != NULL) + pd.pd_resume = *resume; mutex_init(&pd.pd_mtx, NULL, MUTEX_DEFAULT, NULL); cv_init(&pd.pd_cv, NULL, CV_DEFAULT, NULL); @@ -601,11 +602,19 @@ traverse_impl(spa_t *spa, dsl_dataset_t *ds, uint64_t objset, blkptr_t *rootbp, * in syncing context). */ int -traverse_dataset(dsl_dataset_t *ds, uint64_t txg_start, int flags, - blkptr_cb_t func, void *arg) +traverse_dataset_resume(dsl_dataset_t *ds, uint64_t txg_start, + zbookmark_phys_t *resume, + int flags, blkptr_cb_t func, void *arg) { return (traverse_impl(ds->ds_dir->dd_pool->dp_spa, ds, ds->ds_object, - &dsl_dataset_phys(ds)->ds_bp, txg_start, NULL, flags, func, arg)); + &dsl_dataset_phys(ds)->ds_bp, txg_start, resume, flags, func, arg)); +} + +int +traverse_dataset(dsl_dataset_t *ds, uint64_t txg_start, + int flags, blkptr_cb_t func, void *arg) +{ + return (traverse_dataset_resume(ds, txg_start, NULL, flags, func, arg)); } int @@ -625,7 +634,6 @@ traverse_pool(spa_t *spa, uint64_t txg_start, int flags, blkptr_cb_t func, void *arg) { int err; - uint64_t obj; dsl_pool_t *dp = spa_get_dsl(spa); objset_t *mos = dp->dp_meta_objset; boolean_t hard = (flags & TRAVERSE_HARD); @@ -637,8 +645,8 @@ traverse_pool(spa_t *spa, uint64_t txg_start, int flags, return (err); /* visit each dataset */ - for (obj = 1; err == 0; - err = dmu_object_next(mos, &obj, FALSE, txg_start)) { + for (uint64_t obj = 1; err == 0; + err = dmu_object_next(mos, &obj, B_FALSE, txg_start)) { dmu_object_info_t doi; err = dmu_object_info(mos, obj, &doi); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode_sync.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode_sync.c index 0787885229d..9aee5138187 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode_sync.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode_sync.c @@ -424,6 +424,7 @@ dnode_evict_dbufs(dnode_t *dn) db_next = AVL_NEXT(&dn->dn_dbufs, &db_marker); avl_remove(&dn->dn_dbufs, &db_marker); } else { + db->db_pending_evict = TRUE; mutex_exit(&db->db_mtx); db_next = AVL_NEXT(&dn->dn_dbufs, db); } @@ -437,10 +438,14 @@ void dnode_evict_bonus(dnode_t *dn) { rw_enter(&dn->dn_struct_rwlock, RW_WRITER); - if (dn->dn_bonus && refcount_is_zero(&dn->dn_bonus->db_holds)) { - mutex_enter(&dn->dn_bonus->db_mtx); - dbuf_evict(dn->dn_bonus); - dn->dn_bonus = NULL; + if (dn->dn_bonus != NULL) { + if (refcount_is_zero(&dn->dn_bonus->db_holds)) { + mutex_enter(&dn->dn_bonus->db_mtx); + dbuf_evict(dn->dn_bonus); + dn->dn_bonus = NULL; + } else { + dn->dn_bonus->db_pending_evict = TRUE; + } } rw_exit(&dn->dn_struct_rwlock); } @@ -492,7 +497,6 @@ dnode_sync_free(dnode_t *dn, dmu_tx_t *tx) dnode_undirty_dbufs(&dn->dn_dirty_records[txgoff]); dnode_evict_dbufs(dn); - ASSERT(avl_is_empty(&dn->dn_dbufs)); /* * XXX - It would be nice to assert this, but we may still diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c index 4fbbe7c4e80..95e53923896 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,10 @@ #include #include #include +#include +#include +#include +#include SYSCTL_DECL(_vfs_zfs); @@ -130,10 +135,16 @@ dsl_dataset_block_born(dsl_dataset_t *ds, const blkptr_t *bp, dmu_tx_t *tx) dsl_dataset_phys(ds)->ds_compressed_bytes += compressed; dsl_dataset_phys(ds)->ds_uncompressed_bytes += uncompressed; dsl_dataset_phys(ds)->ds_unique_bytes += used; + if (BP_GET_LSIZE(bp) > SPA_OLD_MAXBLOCKSIZE) { ds->ds_feature_activation_needed[SPA_FEATURE_LARGE_BLOCKS] = B_TRUE; } + + spa_feature_t f = zio_checksum_to_feature(BP_GET_CHECKSUM(bp)); + if (f != SPA_FEATURE_NONE) + ds->ds_feature_activation_needed[f] = B_TRUE; + mutex_exit(&ds->ds_lock); dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD, delta, compressed, uncompressed, tx); @@ -701,6 +712,7 @@ dsl_dataset_tryown(dsl_dataset_t *ds, void *tag) { boolean_t gotit = FALSE; + ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); mutex_enter(&ds->ds_lock); if (ds->ds_owner == NULL && !DS_IS_INCONSISTENT(ds)) { ds->ds_owner = tag; @@ -711,6 +723,16 @@ dsl_dataset_tryown(dsl_dataset_t *ds, void *tag) return (gotit); } +boolean_t +dsl_dataset_has_owner(dsl_dataset_t *ds) +{ + boolean_t rv; + mutex_enter(&ds->ds_lock); + rv = (ds->ds_owner != NULL); + mutex_exit(&ds->ds_lock); + return (rv); +} + static void dsl_dataset_activate_feature(uint64_t dsobj, spa_feature_t f, dmu_tx_t *tx) { @@ -1657,6 +1679,21 @@ dsl_dataset_sync(dsl_dataset_t *ds, zio_t *zio, dmu_tx_t *tx) dmu_buf_will_dirty(ds->ds_dbuf, tx); dsl_dataset_phys(ds)->ds_fsid_guid = ds->ds_fsid_guid; + if (ds->ds_resume_bytes[tx->tx_txg & TXG_MASK] != 0) { + VERIFY0(zap_update(tx->tx_pool->dp_meta_objset, + ds->ds_object, DS_FIELD_RESUME_OBJECT, 8, 1, + &ds->ds_resume_object[tx->tx_txg & TXG_MASK], tx)); + VERIFY0(zap_update(tx->tx_pool->dp_meta_objset, + ds->ds_object, DS_FIELD_RESUME_OFFSET, 8, 1, + &ds->ds_resume_offset[tx->tx_txg & TXG_MASK], tx)); + VERIFY0(zap_update(tx->tx_pool->dp_meta_objset, + ds->ds_object, DS_FIELD_RESUME_BYTES, 8, 1, + &ds->ds_resume_bytes[tx->tx_txg & TXG_MASK], tx)); + ds->ds_resume_object[tx->tx_txg & TXG_MASK] = 0; + ds->ds_resume_offset[tx->tx_txg & TXG_MASK] = 0; + ds->ds_resume_bytes[tx->tx_txg & TXG_MASK] = 0; + } + dmu_objset_sync(ds->ds_objset, zio, tx); for (spa_feature_t f = 0; f < SPA_FEATURES; f++) { @@ -1712,6 +1749,76 @@ fail: nvlist_free(propval); } +static void +get_receive_resume_stats(dsl_dataset_t *ds, nvlist_t *nv) +{ + dsl_pool_t *dp = ds->ds_dir->dd_pool; + + if (dsl_dataset_has_resume_receive_state(ds)) { + char *str; + void *packed; + uint8_t *compressed; + uint64_t val; + nvlist_t *token_nv = fnvlist_alloc(); + size_t packed_size, compressed_size; + + if (zap_lookup(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_FROMGUID, sizeof (val), 1, &val) == 0) { + fnvlist_add_uint64(token_nv, "fromguid", val); + } + if (zap_lookup(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_OBJECT, sizeof (val), 1, &val) == 0) { + fnvlist_add_uint64(token_nv, "object", val); + } + if (zap_lookup(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_OFFSET, sizeof (val), 1, &val) == 0) { + fnvlist_add_uint64(token_nv, "offset", val); + } + if (zap_lookup(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_BYTES, sizeof (val), 1, &val) == 0) { + fnvlist_add_uint64(token_nv, "bytes", val); + } + if (zap_lookup(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_TOGUID, sizeof (val), 1, &val) == 0) { + fnvlist_add_uint64(token_nv, "toguid", val); + } + char buf[256]; + if (zap_lookup(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_TONAME, 1, sizeof (buf), buf) == 0) { + fnvlist_add_string(token_nv, "toname", buf); + } + if (zap_contains(dp->dp_meta_objset, ds->ds_object, + DS_FIELD_RESUME_EMBEDOK) == 0) { + fnvlist_add_boolean(token_nv, "embedok"); + } + packed = fnvlist_pack(token_nv, &packed_size); + fnvlist_free(token_nv); + compressed = kmem_alloc(packed_size, KM_SLEEP); + + compressed_size = gzip_compress(packed, compressed, + packed_size, packed_size, 6); + + zio_cksum_t cksum; + fletcher_4_native(compressed, compressed_size, NULL, &cksum); + + str = kmem_alloc(compressed_size * 2 + 1, KM_SLEEP); + for (int i = 0; i < compressed_size; i++) { + (void) sprintf(str + i * 2, "%02x", compressed[i]); + } + str[compressed_size * 2] = '\0'; + char *propval = kmem_asprintf("%u-%llx-%llx-%s", + ZFS_SEND_RESUME_TOKEN_VERSION, + (longlong_t)cksum.zc_word[0], + (longlong_t)packed_size, str); + dsl_prop_nvlist_add_string(nv, + ZFS_PROP_RECEIVE_RESUME_TOKEN, propval); + kmem_free(packed, packed_size); + kmem_free(str, compressed_size * 2 + 1); + kmem_free(compressed, packed_size); + strfree(propval); + } +} + void dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv) { @@ -1783,6 +1890,29 @@ dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv) } } } + + if (!dsl_dataset_is_snapshot(ds)) { + /* + * A failed "newfs" (e.g. full) resumable receive leaves + * the stats set on this dataset. Check here for the prop. + */ + get_receive_resume_stats(ds, nv); + + /* + * A failed incremental resumable receive leaves the + * stats set on our child named "%recv". Check the child + * for the prop. + */ + char recvname[ZFS_MAXNAMELEN]; + dsl_dataset_t *recv_ds; + dsl_dataset_name(ds, recvname); + (void) strcat(recvname, "/"); + (void) strcat(recvname, recv_clone_name); + if (dsl_dataset_hold(dp, recvname, FTAG, &recv_ds) == 0) { + get_receive_resume_stats(recv_ds, nv); + dsl_dataset_rele(recv_ds, FTAG); + } + } } void @@ -3428,7 +3558,7 @@ dsl_dataset_space_wouldfree(dsl_dataset_t *firstsnap, */ boolean_t dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier, - uint64_t earlier_txg) + uint64_t earlier_txg) { dsl_pool_t *dp = later->ds_dir->dd_pool; int error; @@ -3467,3 +3597,20 @@ dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx) objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; dmu_object_zapify(mos, ds->ds_object, DMU_OT_DSL_DATASET, tx); } + +boolean_t +dsl_dataset_is_zapified(dsl_dataset_t *ds) +{ + dmu_object_info_t doi; + + dmu_object_info_from_db(ds->ds_dbuf, &doi); + return (doi.doi_type == DMU_OTN_ZAP_METADATA); +} + +boolean_t +dsl_dataset_has_resume_receive_state(dsl_dataset_t *ds) +{ + return (dsl_dataset_is_zapified(ds) && + zap_contains(ds->ds_dir->dd_pool->dp_meta_objset, + ds->ds_object, DS_FIELD_RESUME_TOGUID) == 0); +} diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c index c7a623c8291..7de98450797 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c @@ -968,9 +968,17 @@ dsl_destroy_inconsistent(const char *dsname, void *arg) objset_t *os; if (dmu_objset_hold(dsname, FTAG, &os) == 0) { - boolean_t inconsistent = DS_IS_INCONSISTENT(dmu_objset_ds(os)); + boolean_t need_destroy = DS_IS_INCONSISTENT(dmu_objset_ds(os)); + + /* + * If the dataset is inconsistent because a resumable receive + * has failed, then do not destroy it. + */ + if (dsl_dataset_has_resume_receive_state(dmu_objset_ds(os))) + need_destroy = B_FALSE; + dmu_objset_rele(os, FTAG); - if (inconsistent) + if (need_destroy) (void) dsl_destroy_head(dsname); } return (0); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/edonr_zfs.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/edonr_zfs.c new file mode 100644 index 00000000000..93f1221fd53 --- /dev/null +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/edonr_zfs.c @@ -0,0 +1,102 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2013 Saso Kiselkov. All rights reserved. + * Use is subject to license terms. + */ +#include +#include +#include + +#define EDONR_MODE 512 +#define EDONR_BLOCK_SIZE EdonR512_BLOCK_SIZE + +/* + * Native zio_checksum interface for the Edon-R hash function. + */ +/*ARGSUSED*/ +void +zio_checksum_edonr_native(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) +{ + uint8_t digest[EDONR_MODE / 8]; + EdonRState ctx; + + ASSERT(ctx_template != NULL); + bcopy(ctx_template, &ctx, sizeof (ctx)); + EdonRUpdate(&ctx, buf, size * 8); + EdonRFinal(&ctx, digest); + bcopy(digest, zcp->zc_word, sizeof (zcp->zc_word)); +} + +/* + * Byteswapped zio_checksum interface for the Edon-R hash function. + */ +void +zio_checksum_edonr_byteswap(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) +{ + zio_cksum_t tmp; + + zio_checksum_edonr_native(buf, size, ctx_template, &tmp); + zcp->zc_word[0] = BSWAP_64(zcp->zc_word[0]); + zcp->zc_word[1] = BSWAP_64(zcp->zc_word[1]); + zcp->zc_word[2] = BSWAP_64(zcp->zc_word[2]); + zcp->zc_word[3] = BSWAP_64(zcp->zc_word[3]); +} + +void * +zio_checksum_edonr_tmpl_init(const zio_cksum_salt_t *salt) +{ + EdonRState *ctx; + uint8_t salt_block[EDONR_BLOCK_SIZE]; + + /* + * Edon-R needs all but the last hash invocation to be on full-size + * blocks, but the salt is too small. Rather than simply padding it + * with zeros, we expand the salt into a new salt block of proper + * size by double-hashing it (the new salt block will be composed of + * H(salt) || H(H(salt))). + */ + CTASSERT(EDONR_BLOCK_SIZE == 2 * (EDONR_MODE / 8)); + EdonRHash(EDONR_MODE, salt->zcs_bytes, sizeof (salt->zcs_bytes) * 8, + salt_block); + EdonRHash(EDONR_MODE, salt_block, EDONR_MODE, salt_block + + EDONR_MODE / 8); + + /* + * Feed the new salt block into the hash function - this will serve + * as our MAC key. + */ + ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP); + EdonRInit(ctx, EDONR_MODE); + EdonRUpdate(ctx, salt_block, sizeof (salt_block) * 8); + return (ctx); +} + +void +zio_checksum_edonr_tmpl_free(void *ctx_template) +{ + EdonRState *ctx = ctx_template; + + bzero(ctx, sizeof (*ctx)); + kmem_free(ctx, sizeof (*ctx)); +} diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c index c565e8b8fbd..6a20d25667d 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2015 by Delphix. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. */ @@ -1794,10 +1794,11 @@ metaslab_condense(metaslab_t *msp, uint64_t txg, dmu_tx_t *tx) ASSERT(msp->ms_loaded); - spa_dbgmsg(spa, "condensing: txg %llu, msp[%llu] %p, " - "smp size %llu, segments %lu, forcing condense=%s", txg, - msp->ms_id, msp, space_map_length(msp->ms_sm), - avl_numnodes(&msp->ms_tree->rt_root), + spa_dbgmsg(spa, "condensing: txg %llu, msp[%llu] %p, vdev id %llu, " + "spa %s, smp size %llu, segments %lu, forcing condense=%s", txg, + msp->ms_id, msp, msp->ms_group->mg_vd->vdev_id, + msp->ms_group->mg_vd->vdev_spa->spa_name, + space_map_length(msp->ms_sm), avl_numnodes(&msp->ms_tree->rt_root), msp->ms_condense_wanted ? "TRUE" : "FALSE"); msp->ms_condense_wanted = B_FALSE; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sha256.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sha256.c index 816c09aa037..a64d6ef33ee 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sha256.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sha256.c @@ -22,6 +22,9 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2013 Saso Kiselkov. All rights reserved. + */ #include #include #ifdef _KERNEL @@ -30,8 +33,10 @@ #include #endif +/*ARGSUSED*/ void -zio_checksum_SHA256(const void *buf, uint64_t size, zio_cksum_t *zcp) +zio_checksum_SHA256(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) { SHA256_CTX ctx; zio_cksum_t tmp; @@ -52,3 +57,31 @@ zio_checksum_SHA256(const void *buf, uint64_t size, zio_cksum_t *zcp) zcp->zc_word[2] = BE_64(tmp.zc_word[2]); zcp->zc_word[3] = BE_64(tmp.zc_word[3]); } + +#ifdef illumos +/*ARGSUSED*/ +void +zio_checksum_SHA512_native(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) +{ + SHA2_CTX ctx; + + SHA2Init(SHA512_256, &ctx); + SHA2Update(&ctx, buf, size); + SHA2Final(zcp, &ctx); +} + +/*ARGSUSED*/ +void +zio_checksum_SHA512_byteswap(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) +{ + zio_cksum_t tmp; + + zio_checksum_SHA512_native(buf, size, ctx_template, &tmp); + zcp->zc_word[0] = BSWAP_64(tmp.zc_word[0]); + zcp->zc_word[1] = BSWAP_64(tmp.zc_word[1]); + zcp->zc_word[2] = BSWAP_64(tmp.zc_word[2]); + zcp->zc_word[3] = BSWAP_64(tmp.zc_word[3]); +} +#endif diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/skein_zfs.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/skein_zfs.c new file mode 100644 index 00000000000..65923403968 --- /dev/null +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/skein_zfs.c @@ -0,0 +1,91 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2013 Saso Kiselkov. All rights reserved. + */ +#include +#include +#include + +/* + * Computes a native 256-bit skein MAC checksum. Please note that this + * function requires the presence of a ctx_template that should be allocated + * using zio_checksum_skein_tmpl_init. + */ +/*ARGSUSED*/ +void +zio_checksum_skein_native(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) +{ + Skein_512_Ctxt_t ctx; + + ASSERT(ctx_template != NULL); + bcopy(ctx_template, &ctx, sizeof (ctx)); + (void) Skein_512_Update(&ctx, buf, size); + (void) Skein_512_Final(&ctx, (uint8_t *)zcp); + bzero(&ctx, sizeof (ctx)); +} + +/* + * Byteswapped version of zio_checksum_skein_native. This just invokes + * the native checksum function and byteswaps the resulting checksum (since + * skein is internally endian-insensitive). + */ +void +zio_checksum_skein_byteswap(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) +{ + zio_cksum_t tmp; + + zio_checksum_skein_native(buf, size, ctx_template, &tmp); + zcp->zc_word[0] = BSWAP_64(tmp.zc_word[0]); + zcp->zc_word[1] = BSWAP_64(tmp.zc_word[1]); + zcp->zc_word[2] = BSWAP_64(tmp.zc_word[2]); + zcp->zc_word[3] = BSWAP_64(tmp.zc_word[3]); +} + +/* + * Allocates a skein MAC template suitable for using in skein MAC checksum + * computations and returns a pointer to it. + */ +void * +zio_checksum_skein_tmpl_init(const zio_cksum_salt_t *salt) +{ + Skein_512_Ctxt_t *ctx; + + ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP); + (void) Skein_512_InitExt(ctx, sizeof (zio_cksum_t) * 8, 0, + salt->zcs_bytes, sizeof (salt->zcs_bytes)); + return (ctx); +} + +/* + * Frees a skein context template previously allocated using + * zio_checksum_skein_tmpl_init. + */ +void +zio_checksum_skein_tmpl_free(void *ctx_template) +{ + Skein_512_Ctxt_t *ctx = ctx_template; + + bzero(ctx, sizeof (*ctx)); + kmem_free(ctx, sizeof (*ctx)); +} diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c index 61c83c1c927..cf9112b72aa 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c @@ -25,6 +25,7 @@ * Copyright (c) 2015, Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2013 Martin Matuska . All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. + * Copyright 2013 Saso Kiselkov. All rights reserved. */ /* @@ -2582,6 +2583,19 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config, return (spa_load(spa, state, SPA_IMPORT_EXISTING, B_TRUE)); } + /* Grab the secret checksum salt from the MOS. */ + error = zap_lookup(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT, + DMU_POOL_CHECKSUM_SALT, 1, + sizeof (spa->spa_cksum_salt.zcs_bytes), + spa->spa_cksum_salt.zcs_bytes); + if (error == ENOENT) { + /* Generate a new salt for subsequent use */ + (void) random_get_pseudo_bytes(spa->spa_cksum_salt.zcs_bytes, + sizeof (spa->spa_cksum_salt.zcs_bytes)); + } else if (error != 0) { + return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO)); + } + if (spa_dir_prop(spa, DMU_POOL_SYNC_BPOBJ, &obj) != 0) return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO)); error = bpobj_open(&spa->spa_deferred_bpobj, spa->spa_meta_objset, obj); @@ -3749,6 +3763,12 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, if (version >= SPA_VERSION_ZPOOL_HISTORY) spa_history_create_obj(spa, tx); + /* + * Generate some random noise for salted checksums to operate on. + */ + (void) random_get_pseudo_bytes(spa->spa_cksum_salt.zcs_bytes, + sizeof (spa->spa_cksum_salt.zcs_bytes)); + /* * Set pool properties. */ @@ -6551,6 +6571,20 @@ spa_sync_upgrades(spa_t *spa, dmu_tx_t *tx) if (lz4_en && !lz4_ac) spa_feature_incr(spa, SPA_FEATURE_LZ4_COMPRESS, tx); } + + /* + * If we haven't written the salt, do so now. Note that the + * feature may not be activated yet, but that's fine since + * the presence of this ZAP entry is backwards compatible. + */ + if (zap_contains(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT, + DMU_POOL_CHECKSUM_SALT) == ENOENT) { + VERIFY0(zap_add(spa->spa_meta_objset, + DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_CHECKSUM_SALT, 1, + sizeof (spa->spa_cksum_salt.zcs_bytes), + spa->spa_cksum_salt.zcs_bytes, tx)); + } + rrw_exit(&dp->dp_config_rwlock, FTAG); } diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c index 950964185ef..30c71a63ddc 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c @@ -24,6 +24,7 @@ * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2013 Martin Matuska . All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. + * Copyright 2013 Saso Kiselkov. All rights reserved. */ #include @@ -51,7 +52,7 @@ #include #include #include "zfs_prop.h" -#include "zfeature_common.h" +#include /* * SPA locking @@ -631,6 +632,7 @@ spa_add(const char *name, nvlist_t *config, const char *altroot) mutex_init(&spa->spa_history_lock, NULL, MUTEX_DEFAULT, NULL); mutex_init(&spa->spa_proc_lock, NULL, MUTEX_DEFAULT, NULL); mutex_init(&spa->spa_props_lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&spa->spa_cksum_tmpls_lock, NULL, MUTEX_DEFAULT, NULL); mutex_init(&spa->spa_scrub_lock, NULL, MUTEX_DEFAULT, NULL); mutex_init(&spa->spa_suspend_lock, NULL, MUTEX_DEFAULT, NULL); mutex_init(&spa->spa_vdev_top_lock, NULL, MUTEX_DEFAULT, NULL); @@ -793,6 +795,8 @@ spa_remove(spa_t *spa) for (int t = 0; t < TXG_SIZE; t++) bplist_destroy(&spa->spa_free_bplist[t]); + zio_checksum_templates_free(spa); + cv_destroy(&spa->spa_async_cv); cv_destroy(&spa->spa_evicting_os_cv); cv_destroy(&spa->spa_proc_cv); @@ -806,6 +810,7 @@ spa_remove(spa_t *spa) mutex_destroy(&spa->spa_history_lock); mutex_destroy(&spa->spa_proc_lock); mutex_destroy(&spa->spa_props_lock); + mutex_destroy(&spa->spa_cksum_tmpls_lock); mutex_destroy(&spa->spa_scrub_lock); mutex_destroy(&spa->spa_suspend_lock); mutex_destroy(&spa->spa_vdev_top_lock); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/space_reftree.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/space_reftree.c index a508092c530..3d990596f76 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/space_reftree.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/space_reftree.c @@ -23,7 +23,7 @@ * Use is subject to license terms. */ /* - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, 2015 by Delphix. All rights reserved. */ #include @@ -103,7 +103,7 @@ space_reftree_add_node(avl_tree_t *t, uint64_t offset, int64_t refcnt) void space_reftree_add_seg(avl_tree_t *t, uint64_t start, uint64_t end, - int64_t refcnt) + int64_t refcnt) { space_reftree_add_node(t, start, refcnt); space_reftree_add_node(t, end, -refcnt); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dbuf.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dbuf.h index 482ccb01dac..233d541342a 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dbuf.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dbuf.h @@ -230,9 +230,25 @@ typedef struct dmu_buf_impl { /* User callback information. */ dmu_buf_user_t *db_user; - uint8_t db_immediate_evict; + /* + * Evict user data as soon as the dirty and reference + * counts are equal. + */ + uint8_t db_user_immediate_evict; + + /* + * This block was freed while a read or write was + * active. + */ uint8_t db_freed_in_flight; + /* + * dnode_evict_dbufs() or dnode_evict_bonus() tried to + * evict this dbuf, but couldn't due to outstanding + * references. Evict once the refcount drops to 0. + */ + uint8_t db_pending_evict; + uint8_t db_dirtycnt; } dmu_buf_impl_t; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h index 56f98ff10b6..226e8f79da9 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h @@ -27,6 +27,7 @@ * Copyright 2013 DEY Storage Systems, Inc. * Copyright 2014 HybridCluster. All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. + * Copyright 2013 Saso Kiselkov. All rights reserved. */ /* Portions Copyright 2010 Robert Milkowski */ @@ -320,6 +321,7 @@ typedef struct dmu_buf { #define DMU_POOL_FREE_BPOBJ "free_bpobj" #define DMU_POOL_BPTREE_OBJ "bptree_obj" #define DMU_POOL_EMPTY_BPOBJ "empty_bpobj" +#define DMU_POOL_CHECKSUM_SALT "org.illumos:checksum_salt" /* * Allocate an object from this objset. The range of object numbers diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h index 6f67b5a0b9b..e8d6294b981 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h @@ -25,7 +25,7 @@ /* * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright (c) 2012, Martin Matuska . All rights reserved. - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, 2014 by Delphix. All rights reserved. */ #ifndef _SYS_DMU_IMPL_H @@ -300,6 +300,8 @@ typedef struct dmu_sendarg { uint64_t dsa_featureflags; uint64_t dsa_last_data_object; uint64_t dsa_last_data_offset; + uint64_t dsa_resume_object; + uint64_t dsa_resume_offset; } dmu_sendarg_t; void dmu_object_zapify(objset_t *, uint64_t, dmu_object_type_t, dmu_tx_t *); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h index 9e98350f001..8a263a39e6b 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h @@ -93,7 +93,6 @@ struct objset { uint8_t os_copies; enum zio_checksum os_dedup_checksum; boolean_t os_dedup_verify; - boolean_t os_evicting; zfs_logbias_op_t os_logbias; zfs_cache_type_t os_primary_cache; zfs_cache_type_t os_secondary_cache; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_send.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_send.h index 143d43f037f..2865e82913a 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_send.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_send.h @@ -35,13 +35,16 @@ struct vnode; struct dsl_dataset; struct drr_begin; struct avl_tree; +struct dmu_replay_record; -int dmu_send(const char *tosnap, const char *fromsnap, - boolean_t embedok, boolean_t large_block_ok, +extern const char *recv_clone_name; + +int dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, + boolean_t large_block_ok, int outfd, uint64_t resumeobj, uint64_t resumeoff, #ifdef illumos - int outfd, struct vnode *vp, offset_t *off); + struct vnode *vp, offset_t *off); #else - int outfd, struct file *fp, offset_t *off); + struct file *fp, offset_t *off); #endif int dmu_send_estimate(struct dsl_dataset *ds, struct dsl_dataset *fromds, uint64_t *sizep); @@ -57,12 +60,14 @@ int dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap, typedef struct dmu_recv_cookie { struct dsl_dataset *drc_ds; + struct dmu_replay_record *drc_drr_begin; struct drr_begin *drc_drrb; const char *drc_tofs; const char *drc_tosnap; boolean_t drc_newfs; boolean_t drc_byteswap; boolean_t drc_force; + boolean_t drc_resumable; struct avl_tree *drc_guid_to_ds_map; zio_cksum_t drc_cksum; uint64_t drc_newsnapobj; @@ -70,8 +75,9 @@ typedef struct dmu_recv_cookie { cred_t *drc_cred; } dmu_recv_cookie_t; -int dmu_recv_begin(char *tofs, char *tosnap, struct drr_begin *drrb, - boolean_t force, char *origin, dmu_recv_cookie_t *drc); +int dmu_recv_begin(char *tofs, char *tosnap, + struct dmu_replay_record *drr_begin, + boolean_t force, boolean_t resumable, char *origin, dmu_recv_cookie_t *drc); #ifdef illumos int dmu_recv_stream(dmu_recv_cookie_t *drc, struct vnode *vp, offset_t *voffp, #else diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_traverse.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_traverse.h index 544b721e461..c010edd440d 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_traverse.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_traverse.h @@ -54,6 +54,8 @@ typedef int (blkptr_cb_t)(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, int traverse_dataset(struct dsl_dataset *ds, uint64_t txg_start, int flags, blkptr_cb_t func, void *arg); +int traverse_dataset_resume(struct dsl_dataset *ds, uint64_t txg_start, + zbookmark_phys_t *resume, int flags, blkptr_cb_t func, void *arg); int traverse_dataset_destroyed(spa_t *spa, blkptr_t *blkptr, uint64_t txg_start, zbookmark_phys_t *resume, int flags, blkptr_cb_t func, void *arg); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h index c9cd5890aa7..d7df05b309b 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h @@ -91,6 +91,18 @@ struct dsl_pool; */ #define DS_FIELD_LARGE_BLOCKS "org.open-zfs:large_blocks" +/* + * These fields are set on datasets that are in the middle of a resumable + * receive, and allow the sender to resume the send if it is interrupted. + */ +#define DS_FIELD_RESUME_FROMGUID "com.delphix:resume_fromguid" +#define DS_FIELD_RESUME_TONAME "com.delphix:resume_toname" +#define DS_FIELD_RESUME_TOGUID "com.delphix:resume_toguid" +#define DS_FIELD_RESUME_OBJECT "com.delphix:resume_object" +#define DS_FIELD_RESUME_OFFSET "com.delphix:resume_offset" +#define DS_FIELD_RESUME_BYTES "com.delphix:resume_bytes" +#define DS_FIELD_RESUME_EMBEDOK "com.delphix:resume_embedok" + /* * DS_FLAG_CI_DATASET is set if the dataset contains a file system whose * name lookups should be performed case-insensitively. @@ -184,6 +196,14 @@ typedef struct dsl_dataset { kmutex_t ds_sendstream_lock; list_t ds_sendstreams; + /* + * When in the middle of a resumable receive, tracks how much + * progress we have made. + */ + uint64_t ds_resume_object[TXG_SIZE]; + uint64_t ds_resume_offset[TXG_SIZE]; + uint64_t ds_resume_bytes[TXG_SIZE]; + /* Protected by our dsl_dir's dd_lock */ list_t ds_prop_cbs; @@ -235,6 +255,7 @@ int dsl_dataset_own_obj(struct dsl_pool *dp, uint64_t dsobj, void dsl_dataset_disown(dsl_dataset_t *ds, void *tag); void dsl_dataset_name(dsl_dataset_t *ds, char *name); boolean_t dsl_dataset_tryown(dsl_dataset_t *ds, void *tag); +boolean_t dsl_dataset_has_owner(dsl_dataset_t *ds); uint64_t dsl_dataset_create_sync(dsl_dir_t *pds, const char *lastname, dsl_dataset_t *origin, uint64_t flags, cred_t *, dmu_tx_t *); uint64_t dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, @@ -315,6 +336,8 @@ int dsl_dataset_snap_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx, void dsl_dataset_set_refreservation_sync_impl(dsl_dataset_t *ds, zprop_source_t source, uint64_t value, dmu_tx_t *tx); void dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx); +boolean_t dsl_dataset_is_zapified(dsl_dataset_t *ds); +boolean_t dsl_dataset_has_resume_receive_state(dsl_dataset_t *ds); int dsl_dataset_rollback(const char *fsname, void *owner, nvlist_t *result); void dsl_dataset_deactivate_feature(uint64_t dsobj, diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h index 801f80ed544..6dc2b118bae 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h @@ -23,6 +23,7 @@ * Copyright (c) 2011, 2014 by Delphix. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. + * Copyright 2013 Saso Kiselkov. All rights reserved. */ #ifndef _SYS_SPA_H @@ -162,6 +163,14 @@ typedef struct zio_cksum { uint64_t zc_word[4]; } zio_cksum_t; +/* + * Some checksums/hashes need a 256-bit initialization salt. This salt is kept + * secret and is suitable for use in MAC algorithms as the key. + */ +typedef struct zio_cksum_salt { + uint8_t zcs_bytes[32]; +} zio_cksum_salt_t; + /* * Each block is described by its DVAs, time of birth, checksum, etc. * The word-by-word, bit-by-bit layout of the blkptr is as follows: diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h index bd088ca46b5..a93ba832cde 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h @@ -24,6 +24,7 @@ * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2013 Martin Matuska . All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. + * Copyright 2013 Saso Kiselkov. All rights reserved. */ #ifndef _SYS_SPA_IMPL_H @@ -166,6 +167,10 @@ struct spa { uint64_t spa_syncing_txg; /* txg currently syncing */ bpobj_t spa_deferred_bpobj; /* deferred-free bplist */ bplist_t spa_free_bplist[TXG_SIZE]; /* bplist of stuff to free */ + zio_cksum_salt_t spa_cksum_salt; /* secret salt for cksum */ + /* checksum context templates */ + kmutex_t spa_cksum_tmpls_lock; + void *spa_cksum_tmpls[ZIO_CHECKSUM_FUNCTIONS]; uberblock_t spa_ubsync; /* last synced uberblock */ uberblock_t spa_uberblock; /* current uberblock */ boolean_t spa_extreme_rewind; /* rewind past deferred frees */ diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h index f9eca27075b..20bf5455332 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h @@ -79,14 +79,15 @@ typedef enum drr_headertype { * Feature flags for zfs send streams (flags in drr_versioninfo) */ -#define DMU_BACKUP_FEATURE_DEDUP (1<<0) -#define DMU_BACKUP_FEATURE_DEDUPPROPS (1<<1) -#define DMU_BACKUP_FEATURE_SA_SPILL (1<<2) +#define DMU_BACKUP_FEATURE_DEDUP (1 << 0) +#define DMU_BACKUP_FEATURE_DEDUPPROPS (1 << 1) +#define DMU_BACKUP_FEATURE_SA_SPILL (1 << 2) /* flags #3 - #15 are reserved for incompatible closed-source implementations */ -#define DMU_BACKUP_FEATURE_EMBED_DATA (1<<16) -#define DMU_BACKUP_FEATURE_EMBED_DATA_LZ4 (1<<17) +#define DMU_BACKUP_FEATURE_EMBED_DATA (1 << 16) +#define DMU_BACKUP_FEATURE_EMBED_DATA_LZ4 (1 << 17) /* flag #18 is reserved for a Delphix feature */ -#define DMU_BACKUP_FEATURE_LARGE_BLOCKS (1<<19) +#define DMU_BACKUP_FEATURE_LARGE_BLOCKS (1 << 19) +#define DMU_BACKUP_FEATURE_RESUMING (1 << 20) /* * Mask of all supported backup features @@ -94,11 +95,16 @@ typedef enum drr_headertype { #define DMU_BACKUP_FEATURE_MASK (DMU_BACKUP_FEATURE_DEDUP | \ DMU_BACKUP_FEATURE_DEDUPPROPS | DMU_BACKUP_FEATURE_SA_SPILL | \ DMU_BACKUP_FEATURE_EMBED_DATA | DMU_BACKUP_FEATURE_EMBED_DATA_LZ4 | \ + DMU_BACKUP_FEATURE_RESUMING | \ DMU_BACKUP_FEATURE_LARGE_BLOCKS) /* Are all features in the given flag word currently supported? */ #define DMU_STREAM_SUPPORTED(x) (!((x) & ~DMU_BACKUP_FEATURE_MASK)) +typedef enum dmu_send_resume_token_version { + ZFS_SEND_RESUME_TOKEN_VERSION = 1 +} dmu_send_resume_token_version_t; + /* * The drr_versioninfo field of the dmu_replay_record has the * following layout: @@ -358,14 +364,14 @@ typedef struct zfs_cmd { zfs_share_t zc_share; uint64_t zc_jailid; dmu_objset_stats_t zc_objset_stats; - struct drr_begin zc_begin_record; + dmu_replay_record_t zc_begin_record; zinject_record_t zc_inject_record; uint32_t zc_defer_destroy; uint32_t zc_flags; uint64_t zc_action_handle; int zc_cleanup_fd; uint8_t zc_simple; - uint8_t zc_pad[3]; /* alignment */ + boolean_t zc_resumable; uint64_t zc_sendobj; uint64_t zc_fromobj; uint64_t zc_createtxg; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h index 342c9cd8f84..eac4b905c70 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h @@ -82,6 +82,11 @@ enum zio_checksum { ZIO_CHECKSUM_SHA256, ZIO_CHECKSUM_ZILOG2, ZIO_CHECKSUM_NOPARITY, +#ifdef illumos + ZIO_CHECKSUM_SHA512, + ZIO_CHECKSUM_SKEIN, + ZIO_CHECKSUM_EDONR, +#endif ZIO_CHECKSUM_FUNCTIONS }; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_checksum.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_checksum.h index 0c293ab20e2..0a9d772591d 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_checksum.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_checksum.h @@ -20,13 +20,15 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2014 by Delphix. All rights reserved. + * Copyright (c) 2014, 2015 by Delphix. All rights reserved. + * Copyright Saso Kiselkov 2013, All rights reserved. */ #ifndef _SYS_ZIO_CHECKSUM_H #define _SYS_ZIO_CHECKSUM_H #include +#include #ifdef __cplusplus extern "C" { @@ -35,17 +37,34 @@ extern "C" { /* * Signature for checksum functions. */ -typedef void zio_checksum_func_t(const void *, uint64_t, zio_cksum_t *); +typedef void zio_checksum_t(const void *data, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp); +typedef void *zio_checksum_tmpl_init_t(const zio_cksum_salt_t *salt); +typedef void zio_checksum_tmpl_free_t(void *ctx_template); + +typedef enum zio_checksum_flags { + /* Strong enough for metadata? */ + ZCHECKSUM_FLAG_METADATA = (1 << 1), + /* ZIO embedded checksum */ + ZCHECKSUM_FLAG_EMBEDDED = (1 << 2), + /* Strong enough for dedup (without verification)? */ + ZCHECKSUM_FLAG_DEDUP = (1 << 3), + /* Uses salt value */ + ZCHECKSUM_FLAG_SALTED = (1 << 4), + /* Strong enough for nopwrite? */ + ZCHECKSUM_FLAG_NOPWRITE = (1 << 5) +} zio_checksum_flags_t; /* * Information about each checksum function. */ typedef struct zio_checksum_info { - zio_checksum_func_t *ci_func[2]; /* checksum function per byteorder */ - int ci_correctable; /* number of correctable bits */ - int ci_eck; /* uses zio embedded checksum? */ - boolean_t ci_dedup; /* strong enough for dedup? */ - char *ci_name; /* descriptive name */ + /* checksum function for each byteorder */ + zio_checksum_t *ci_func[2]; + zio_checksum_tmpl_init_t *ci_tmpl_init; + zio_checksum_tmpl_free_t *ci_tmpl_free; + zio_checksum_flags_t ci_flags; + char *ci_name; /* descriptive name */ } zio_checksum_info_t; typedef struct zio_bad_cksum { @@ -62,12 +81,30 @@ extern zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS]; /* * Checksum routines. */ -extern zio_checksum_func_t zio_checksum_SHA256; +extern zio_checksum_t zio_checksum_SHA256; +#ifdef illumos +extern zio_checksum_t zio_checksum_SHA512_native; +extern zio_checksum_t zio_checksum_SHA512_byteswap; + +/* Skein */ +extern zio_checksum_t zio_checksum_skein_native; +extern zio_checksum_t zio_checksum_skein_byteswap; +extern zio_checksum_tmpl_init_t zio_checksum_skein_tmpl_init; +extern zio_checksum_tmpl_free_t zio_checksum_skein_tmpl_free; + +/* Edon-R */ +extern zio_checksum_t zio_checksum_edonr_native; +extern zio_checksum_t zio_checksum_edonr_byteswap; +extern zio_checksum_tmpl_init_t zio_checksum_edonr_tmpl_init; +extern zio_checksum_tmpl_free_t zio_checksum_edonr_tmpl_free; +#endif extern void zio_checksum_compute(zio_t *zio, enum zio_checksum checksum, void *data, uint64_t size); extern int zio_checksum_error(zio_t *zio, zio_bad_cksum_t *out); extern enum zio_checksum spa_dedup_checksum(spa_t *spa); +extern void zio_checksum_templates_free(spa_t *spa); +extern spa_feature_t zio_checksum_to_feature(enum zio_checksum cksum); #ifdef __cplusplus } diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zrlock.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zrlock.h index dcd63f7b5b9..b6eba1a18ff 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zrlock.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zrlock.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015 by Delphix. All rights reserved. */ #ifndef _SYS_ZRLOCK_H @@ -44,12 +45,8 @@ typedef struct zrlock { extern void zrl_init(zrlock_t *); extern void zrl_destroy(zrlock_t *); -#ifdef ZFS_DEBUG -#define zrl_add(_z) zrl_add_debug((_z), __func__) -extern void zrl_add_debug(zrlock_t *, const char *); -#else -extern void zrl_add(zrlock_t *); -#endif +#define zrl_add(_z) zrl_add_impl((_z), __func__) +extern void zrl_add_impl(zrlock_t *, const char *); extern void zrl_remove(zrlock_t *); extern int zrl_tryenter(zrlock_t *); extern void zrl_exit(zrlock_t *); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c index a8f1c5f5ca7..429d31e1024 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c @@ -3412,8 +3412,6 @@ vdev_is_bootable(vdev_t *vd) strcmp(vdev_type, VDEV_TYPE_MISSING) == 0) { return (B_FALSE); } - } else if (vd->vdev_wholedisk == 1) { - return (B_FALSE); } for (int c = 0; c < vd->vdev_children; c++) { diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c index 966f2fa364b..9befa75a5ee 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2012, 2015 by Delphix. All rights reserved. */ /* @@ -185,7 +185,7 @@ vdev_label_number(uint64_t psize, uint64_t offset) static void vdev_label_read(zio_t *zio, vdev_t *vd, int l, void *buf, uint64_t offset, - uint64_t size, zio_done_func_t *done, void *private, int flags) + uint64_t size, zio_done_func_t *done, void *private, int flags) { ASSERT(spa_config_held(zio->io_spa, SCL_STATE_ALL, RW_WRITER) == SCL_STATE_ALL); @@ -199,7 +199,7 @@ vdev_label_read(zio_t *zio, vdev_t *vd, int l, void *buf, uint64_t offset, static void vdev_label_write(zio_t *zio, vdev_t *vd, int l, void *buf, uint64_t offset, - uint64_t size, zio_done_func_t *done, void *private, int flags) + uint64_t size, zio_done_func_t *done, void *private, int flags) { ASSERT(spa_config_held(zio->io_spa, SCL_ALL, RW_WRITER) == SCL_ALL || (spa_config_held(zio->io_spa, SCL_CONFIG | SCL_STATE, RW_READER) == diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_leaf.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_leaf.c index 96358f7bd80..c8c36607564 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_leaf.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_leaf.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, 2014 by Delphix. All rights reserved. + * Copyright (c) 2013, 2015 by Delphix. All rights reserved. */ /* @@ -535,7 +535,7 @@ zap_entry_read_name(zap_t *zap, const zap_entry_handle_t *zeh, uint16_t buflen, int zap_entry_update(zap_entry_handle_t *zeh, - uint8_t integer_size, uint64_t num_integers, const void *buf) + uint8_t integer_size, uint64_t num_integers, const void *buf) { int delta_chunks; zap_leaf_t *l = zeh->zeh_leaf; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfeature.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfeature.c index 80a3f0be300..78b2912df1d 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfeature.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfeature.c @@ -269,7 +269,8 @@ feature_get_refcount_from_disk(spa_t *spa, zfeature_info_t *feature, static int -feature_get_enabled_txg(spa_t *spa, zfeature_info_t *feature, uint64_t *res) { +feature_get_enabled_txg(spa_t *spa, zfeature_info_t *feature, uint64_t *res) +{ uint64_t enabled_txg_obj = spa->spa_feat_enabled_txg_obj; ASSERT(zfeature_depends_on(feature->fi_feature, @@ -493,7 +494,8 @@ spa_feature_is_active(spa_t *spa, spa_feature_t fid) * Returns B_FALSE otherwise (i.e. if the feature is not enabled). */ boolean_t -spa_feature_enabled_txg(spa_t *spa, spa_feature_t fid, uint64_t *txg) { +spa_feature_enabled_txg(spa_t *spa, spa_feature_t fid, uint64_t *txg) +{ int err; ASSERT(VALID_FEATURE_FID(fid)); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c index f7c2b6a7e67..e2a3bff6457 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, 2014 by Delphix. All rights reserved. + * Copyright (c) 2013, 2015 by Delphix. All rights reserved. */ #include @@ -806,7 +806,7 @@ zfs_dropname(zfs_dirlock_t *dl, znode_t *zp, znode_t *dzp, dmu_tx_t *tx, */ int zfs_link_destroy(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag, - boolean_t *unlinkedp) + boolean_t *unlinkedp) { znode_t *dzp = dl->dl_dzp; zfsvfs_t *zfsvfs = dzp->z_zfsvfs; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c index 2e5191672fd..3e43bda846b 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c @@ -27,7 +27,7 @@ * Copyright 2014 Xin Li . All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2014, Joyent, Inc. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2015 by Delphix. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. */ @@ -188,6 +188,7 @@ #include #include #include +#include #include "zfs_namecheck.h" #include "zfs_prop.h" @@ -3903,11 +3904,6 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) return (SET_ERROR(ENOTSUP)); break; - case ZFS_PROP_DEDUP: - if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP)) - return (SET_ERROR(ENOTSUP)); - break; - case ZFS_PROP_RECORDSIZE: /* Record sizes above 128k need the feature to be enabled */ if (nvpair_value_uint64(pair, &intval) == 0 && @@ -3921,7 +3917,7 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) */ if (zfs_is_bootfs(dsname) && intval > SPA_OLD_MAXBLOCKSIZE) { - return (SET_ERROR(EDOM)); + return (SET_ERROR(ERANGE)); } /* @@ -3930,7 +3926,7 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) */ if (intval > zfs_max_recordsize || intval > SPA_MAXBLOCKSIZE) - return (SET_ERROR(EDOM)); + return (SET_ERROR(ERANGE)); if ((err = spa_open(dsname, &spa, FTAG)) != 0) return (err); @@ -3958,6 +3954,45 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) return (SET_ERROR(ENOTSUP)); } break; + + case ZFS_PROP_CHECKSUM: + case ZFS_PROP_DEDUP: + { + spa_feature_t feature; + spa_t *spa; + + /* dedup feature version checks */ + if (prop == ZFS_PROP_DEDUP && + zfs_earlier_version(dsname, SPA_VERSION_DEDUP)) + return (SET_ERROR(ENOTSUP)); + + if (nvpair_value_uint64(pair, &intval) != 0) + return (SET_ERROR(EINVAL)); + + /* check prop value is enabled in features */ + feature = zio_checksum_to_feature(intval); + if (feature == SPA_FEATURE_NONE) + break; + + if ((err = spa_open(dsname, &spa, FTAG)) != 0) + return (err); + /* + * Salted checksums are not supported on root pools. + */ + if (spa_bootfs(spa) != 0 && + intval < ZIO_CHECKSUM_FUNCTIONS && + (zio_checksum_table[intval].ci_flags & + ZCHECKSUM_FLAG_SALTED)) { + spa_close(spa, FTAG); + return (SET_ERROR(ERANGE)); + } + if (!spa_feature_is_enabled(spa, feature)) { + spa_close(spa, FTAG); + return (SET_ERROR(ENOTSUP)); + } + spa_close(spa, FTAG); + break; + } } return (zfs_secpolicy_setprop(dsname, prop, pair, CRED())); @@ -4156,6 +4191,7 @@ static boolean_t zfs_ioc_recv_inject_err; * zc_guid force flag * zc_cleanup_fd cleanup-on-exit file descriptor * zc_action_handle handle for this guid/ds mapping (or zero on first call) + * zc_resumable if data is incomplete assume sender will resume * * outputs: * zc_cookie number of bytes read @@ -4207,13 +4243,13 @@ zfs_ioc_recv(zfs_cmd_t *zc) return (SET_ERROR(EBADF)); } - VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); + errors = fnvlist_alloc(); if (zc->zc_string[0]) origin = zc->zc_string; error = dmu_recv_begin(tofs, tosnap, - &zc->zc_begin_record, force, origin, &drc); + &zc->zc_begin_record, force, zc->zc_resumable, origin, &drc); if (error != 0) goto out; @@ -5432,6 +5468,8 @@ zfs_ioc_unjail(zfs_cmd_t *zc) * indicates that blocks > 128KB are permitted * (optional) "embedok" -> (value ignored) * presence indicates DRR_WRITE_EMBEDDED records are permitted + * (optional) "resume_object" and "resume_offset" -> (uint64) + * if present, resume send stream from specified object and offset. * } * * outnvl is unused @@ -5448,6 +5486,8 @@ zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) int fd; boolean_t largeblockok; boolean_t embedok; + uint64_t resumeobj = 0; + uint64_t resumeoff = 0; error = nvlist_lookup_int32(innvl, "fd", &fd); if (error != 0) @@ -5458,6 +5498,9 @@ zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) largeblockok = nvlist_exists(innvl, "largeblockok"); embedok = nvlist_exists(innvl, "embedok"); + (void) nvlist_lookup_uint64(innvl, "resume_object", &resumeobj); + (void) nvlist_lookup_uint64(innvl, "resume_offset", &resumeoff); + #ifdef illumos file_t *fp = getf(fd); #else @@ -5467,11 +5510,11 @@ zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) return (SET_ERROR(EBADF)); off = fp->f_offset; - error = dmu_send(snapname, fromname, embedok, largeblockok, + error = dmu_send(snapname, fromname, embedok, largeblockok, fd, #ifdef illumos - fd, fp->f_vnode, &off); + resumeobj, resumeoff, fp->f_vnode, &off); #else - fd, fp, &off); + resumeobj, resumeoff, fp, &off); #endif #ifdef illumos @@ -5665,7 +5708,7 @@ zfs_ioctl_register_dataset_read(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) static void zfs_ioctl_register_dataset_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, - zfs_secpolicy_func_t *secpolicy) + zfs_secpolicy_func_t *secpolicy) { zfs_ioctl_register_legacy(ioc, func, secpolicy, DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); @@ -6100,6 +6143,14 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag, goto out; } break; + case ZFS_IOCVER_EDBP: + if (zc_iocparm->zfs_cmd_size != sizeof(zfs_cmd_edbp_t)) { + error = SET_ERROR(EFAULT); + goto out; + } + compat = B_TRUE; + cflag = ZFS_CMD_COMPAT_EDBP; + break; case ZFS_IOCVER_ZCMD: if (zc_iocparm->zfs_cmd_size > sizeof(zfs_cmd_t) || zc_iocparm->zfs_cmd_size < sizeof(zfs_cmd_zcmd_t)) { diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_log.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_log.c index 7432290d218..30b3b5270ab 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_log.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_log.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015 by Delphix. All rights reserved. */ #include @@ -348,7 +349,7 @@ zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, */ void zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, - znode_t *dzp, char *name, uint64_t foid) + znode_t *dzp, char *name, uint64_t foid) { itx_t *itx; lr_remove_t *lr; @@ -372,7 +373,7 @@ zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, */ void zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, - znode_t *dzp, znode_t *zp, char *name) + znode_t *dzp, znode_t *zp, char *name) { itx_t *itx; lr_link_t *lr; @@ -427,7 +428,7 @@ zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, */ void zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, - znode_t *sdzp, char *sname, znode_t *tdzp, char *dname, znode_t *szp) + znode_t *sdzp, char *sname, znode_t *tdzp, char *dname, znode_t *szp) { itx_t *itx; lr_rename_t *lr; @@ -455,7 +456,7 @@ ssize_t zfs_immediate_write_sz = 32768; void zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype, - znode_t *zp, offset_t off, ssize_t resid, int ioflag) + znode_t *zp, offset_t off, ssize_t resid, int ioflag) { itx_wr_state_t write_state; boolean_t slogging; @@ -532,7 +533,7 @@ zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype, */ void zfs_log_truncate(zilog_t *zilog, dmu_tx_t *tx, int txtype, - znode_t *zp, uint64_t off, uint64_t len) + znode_t *zp, uint64_t off, uint64_t len) { itx_t *itx; lr_truncate_t *lr; @@ -555,7 +556,7 @@ zfs_log_truncate(zilog_t *zilog, dmu_tx_t *tx, int txtype, */ void zfs_log_setattr(zilog_t *zilog, dmu_tx_t *tx, int txtype, - znode_t *zp, vattr_t *vap, uint_t mask_applied, zfs_fuid_info_t *fuidp) + znode_t *zp, vattr_t *vap, uint_t mask_applied, zfs_fuid_info_t *fuidp) { itx_t *itx; lr_setattr_t *lr; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_replay.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_replay.c index 96f777a95f3..6a76513fa1a 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_replay.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_replay.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, 2015 by Delphix. All rights reserved. */ #include @@ -54,7 +54,7 @@ static void zfs_init_vattr(vattr_t *vap, uint64_t mask, uint64_t mode, - uint64_t uid, uint64_t gid, uint64_t rdev, uint64_t nodeid) + uint64_t uid, uint64_t gid, uint64_t rdev, uint64_t nodeid) { VATTR_NULL(vap); vap->va_mask = (uint_t)mask; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c index d5874ea3e94..d1ed9dac23f 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2014 by Delphix. All rights reserved. + * Copyright (c) 2012, 2015 by Delphix. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ @@ -4520,7 +4520,7 @@ top: /* ARGSUSED */ static int zfs_null_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, - size_t *lenp, int flags, cred_t *cr) + size_t *lenp, int flags, cred_t *cr) { pvn_write_done(pp, B_INVAL|B_FORCE|B_ERROR); return (0); @@ -4546,7 +4546,7 @@ zfs_null_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, /* ARGSUSED */ static int zfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, - size_t *lenp, int flags, cred_t *cr) + size_t *lenp, int flags, cred_t *cr) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c index 867b7987560..896c56e0432 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c @@ -328,7 +328,7 @@ zio_data_buf_free(void *buf, size_t size) */ static void zio_push_transform(zio_t *zio, void *data, uint64_t size, uint64_t bufsize, - zio_transform_func_t *transform) + zio_transform_func_t *transform) { zio_transform_t *zt = kmem_alloc(sizeof (zio_transform_t), KM_SLEEP); @@ -997,7 +997,7 @@ zio_write_phys(zio_t *pio, vdev_t *vd, uint64_t offset, uint64_t size, zio->io_prop.zp_checksum = checksum; - if (zio_checksum_table[checksum].ci_eck) { + if (zio_checksum_table[checksum].ci_flags & ZCHECKSUM_FLAG_EMBEDDED) { /* * zec checksums are necessarily destructive -- they modify * the end of the write buffer to hold the verifier/checksum. @@ -1066,8 +1066,8 @@ zio_vdev_child_io(zio_t *pio, blkptr_t *bp, vdev_t *vd, uint64_t offset, zio_t * zio_vdev_delegated_io(vdev_t *vd, uint64_t offset, void *data, uint64_t size, - int type, zio_priority_t priority, enum zio_flag flags, - zio_done_func_t *done, void *private) + int type, zio_priority_t priority, enum zio_flag flags, + zio_done_func_t *done, void *private) { zio_t *zio; @@ -1209,8 +1209,8 @@ zio_write_bp_init(zio_t *zio) if (BP_IS_HOLE(bp) || !zp->zp_dedup) return (ZIO_PIPELINE_CONTINUE); - ASSERT(zio_checksum_table[zp->zp_checksum].ci_dedup || - zp->zp_dedup_verify); + ASSERT((zio_checksum_table[zp->zp_checksum].ci_flags & + ZCHECKSUM_FLAG_DEDUP) || zp->zp_dedup_verify); if (BP_GET_CHECKSUM(bp) == zp->zp_checksum) { BP_SET_DEDUP(bp, 1); @@ -2072,12 +2072,22 @@ zio_write_gang_block(zio_t *pio) } /* - * The zio_nop_write stage in the pipeline determines if allocating - * a new bp is necessary. By leveraging a cryptographically secure checksum, - * such as SHA256, we can compare the checksums of the new data and the old - * to determine if allocating a new block is required. The nopwrite - * feature can handle writes in either syncing or open context (i.e. zil - * writes) and as a result is mutually exclusive with dedup. + * The zio_nop_write stage in the pipeline determines if allocating a + * new bp is necessary. The nopwrite feature can handle writes in + * either syncing or open context (i.e. zil writes) and as a result is + * mutually exclusive with dedup. + * + * By leveraging a cryptographically secure checksum, such as SHA256, we + * can compare the checksums of the new data and the old to determine if + * allocating a new block is required. Note that our requirements for + * cryptographic strength are fairly weak: there can't be any accidental + * hash collisions, but we don't need to be secure against intentional + * (malicious) collisions. To trigger a nopwrite, you have to be able + * to write the file to begin with, and triggering an incorrect (hash + * collision) nopwrite is no worse than simply writing to the file. + * That said, there are no known attacks against the checksum algorithms + * used for nopwrite, assuming that the salt and the checksums + * themselves remain secret. */ static int zio_nop_write(zio_t *zio) @@ -2100,7 +2110,8 @@ zio_nop_write(zio_t *zio) * allocate a new bp. */ if (BP_IS_HOLE(bp_orig) || - !zio_checksum_table[BP_GET_CHECKSUM(bp)].ci_dedup || + !(zio_checksum_table[BP_GET_CHECKSUM(bp)].ci_flags & + ZCHECKSUM_FLAG_NOPWRITE) || BP_GET_CHECKSUM(bp) != BP_GET_CHECKSUM(bp_orig) || BP_GET_COMPRESS(bp) != BP_GET_COMPRESS(bp_orig) || BP_GET_DEDUP(bp) != BP_GET_DEDUP(bp_orig) || @@ -2112,7 +2123,8 @@ zio_nop_write(zio_t *zio) * avoid allocating a new bp and issuing any I/O. */ if (ZIO_CHECKSUM_EQUAL(bp->blk_cksum, bp_orig->blk_cksum)) { - ASSERT(zio_checksum_table[zp->zp_checksum].ci_dedup); + ASSERT(zio_checksum_table[zp->zp_checksum].ci_flags & + ZCHECKSUM_FLAG_NOPWRITE); ASSERT3U(BP_GET_PSIZE(bp), ==, BP_GET_PSIZE(bp_orig)); ASSERT3U(BP_GET_LSIZE(bp), ==, BP_GET_LSIZE(bp_orig)); ASSERT(zp->zp_compress != ZIO_COMPRESS_OFF); @@ -2393,7 +2405,8 @@ zio_ddt_write(zio_t *zio) * we can't resolve it, so just convert to an ordinary write. * (And automatically e-mail a paper to Nature?) */ - if (!zio_checksum_table[zp->zp_checksum].ci_dedup) { + if (!(zio_checksum_table[zp->zp_checksum].ci_flags & + ZCHECKSUM_FLAG_DEDUP)) { zp->zp_checksum = spa_dedup_checksum(spa); zio_pop_transforms(zio); zio->io_stage = ZIO_STAGE_OPEN; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_checksum.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_checksum.c index d1c60c3ffab..6ba64e085b4 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_checksum.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_checksum.c @@ -20,12 +20,14 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, 2015 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright 2013 Saso Kiselkov. All rights reserved. */ #include #include +#include #include #include #include @@ -59,29 +61,99 @@ * checksum function of the appropriate strength. When reading a block, * we compare the expected checksum against the actual checksum, which we * compute via the checksum function specified by BP_GET_CHECKSUM(bp). + * + * SALTED CHECKSUMS + * + * To enable the use of less secure hash algorithms with dedup, we + * introduce the notion of salted checksums (MACs, really). A salted + * checksum is fed both a random 256-bit value (the salt) and the data + * to be checksummed. This salt is kept secret (stored on the pool, but + * never shown to the user). Thus even if an attacker knew of collision + * weaknesses in the hash algorithm, they won't be able to mount a known + * plaintext attack on the DDT, since the actual hash value cannot be + * known ahead of time. How the salt is used is algorithm-specific + * (some might simply prefix it to the data block, others might need to + * utilize a full-blown HMAC). On disk the salt is stored in a ZAP + * object in the MOS (DMU_POOL_CHECKSUM_SALT). + * + * CONTEXT TEMPLATES + * + * Some hashing algorithms need to perform a substantial amount of + * initialization work (e.g. salted checksums above may need to pre-hash + * the salt) before being able to process data. Performing this + * redundant work for each block would be wasteful, so we instead allow + * a checksum algorithm to do the work once (the first time it's used) + * and then keep this pre-initialized context as a template inside the + * spa_t (spa_cksum_tmpls). If the zio_checksum_info_t contains + * non-NULL ci_tmpl_init and ci_tmpl_free callbacks, they are used to + * construct and destruct the pre-initialized checksum context. The + * pre-initialized context is then reused during each checksum + * invocation and passed to the checksum function. */ /*ARGSUSED*/ static void -zio_checksum_off(const void *buf, uint64_t size, zio_cksum_t *zcp) +zio_checksum_off(const void *buf, uint64_t size, + const void *ctx_template, zio_cksum_t *zcp) { ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0); } zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = { - {{NULL, NULL}, 0, 0, 0, "inherit"}, - {{NULL, NULL}, 0, 0, 0, "on"}, - {{zio_checksum_off, zio_checksum_off}, 0, 0, 0, "off"}, - {{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 1, 0, "label"}, - {{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 1, 0, "gang_header"}, - {{fletcher_2_native, fletcher_2_byteswap}, 0, 1, 0, "zilog"}, - {{fletcher_2_native, fletcher_2_byteswap}, 0, 0, 0, "fletcher2"}, - {{fletcher_4_native, fletcher_4_byteswap}, 1, 0, 0, "fletcher4"}, - {{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 0, 1, "sha256"}, - {{fletcher_4_native, fletcher_4_byteswap}, 0, 1, 0, "zilog2"}, - {{zio_checksum_off, zio_checksum_off}, 0, 0, 0, "noparity"}, + {{NULL, NULL}, NULL, NULL, 0, "inherit"}, + {{NULL, NULL}, NULL, NULL, 0, "on"}, + {{zio_checksum_off, zio_checksum_off}, + NULL, NULL, 0, "off"}, + {{zio_checksum_SHA256, zio_checksum_SHA256}, + NULL, NULL, ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_EMBEDDED, + "label"}, + {{zio_checksum_SHA256, zio_checksum_SHA256}, + NULL, NULL, ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_EMBEDDED, + "gang_header"}, + {{fletcher_2_native, fletcher_2_byteswap}, + NULL, NULL, ZCHECKSUM_FLAG_EMBEDDED, "zilog"}, + {{fletcher_2_native, fletcher_2_byteswap}, + NULL, NULL, 0, "fletcher2"}, + {{fletcher_4_native, fletcher_4_byteswap}, + NULL, NULL, ZCHECKSUM_FLAG_METADATA, "fletcher4"}, + {{zio_checksum_SHA256, zio_checksum_SHA256}, + NULL, NULL, ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_DEDUP | + ZCHECKSUM_FLAG_NOPWRITE, "sha256"}, + {{fletcher_4_native, fletcher_4_byteswap}, + NULL, NULL, ZCHECKSUM_FLAG_EMBEDDED, "zilog2"}, + {{zio_checksum_off, zio_checksum_off}, + NULL, NULL, 0, "noparity"}, +#ifdef illumos + {{zio_checksum_SHA512_native, zio_checksum_SHA512_byteswap}, + NULL, NULL, ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_DEDUP | + ZCHECKSUM_FLAG_NOPWRITE, "sha512"}, + {{zio_checksum_skein_native, zio_checksum_skein_byteswap}, + zio_checksum_skein_tmpl_init, zio_checksum_skein_tmpl_free, + ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_DEDUP | + ZCHECKSUM_FLAG_SALTED | ZCHECKSUM_FLAG_NOPWRITE, "skein"}, + {{zio_checksum_edonr_native, zio_checksum_edonr_byteswap}, + zio_checksum_edonr_tmpl_init, zio_checksum_edonr_tmpl_free, + ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_SALTED | + ZCHECKSUM_FLAG_NOPWRITE, "edonr"}, +#endif }; +spa_feature_t +zio_checksum_to_feature(enum zio_checksum cksum) +{ +#ifdef illumos + switch (cksum) { + case ZIO_CHECKSUM_SHA512: + return (SPA_FEATURE_SHA512); + case ZIO_CHECKSUM_SKEIN: + return (SPA_FEATURE_SKEIN); + case ZIO_CHECKSUM_EDONR: + return (SPA_FEATURE_EDONR); + } +#endif + return (SPA_FEATURE_NONE); +} + enum zio_checksum zio_checksum_select(enum zio_checksum child, enum zio_checksum parent) { @@ -115,7 +187,8 @@ zio_checksum_dedup_select(spa_t *spa, enum zio_checksum child, if (child == (ZIO_CHECKSUM_ON | ZIO_CHECKSUM_VERIFY)) return (spa_dedup_checksum(spa) | ZIO_CHECKSUM_VERIFY); - ASSERT(zio_checksum_table[child & ZIO_CHECKSUM_MASK].ci_dedup || + ASSERT((zio_checksum_table[child & ZIO_CHECKSUM_MASK].ci_flags & + ZCHECKSUM_FLAG_DEDUP) || (child & ZIO_CHECKSUM_VERIFY) || child == ZIO_CHECKSUM_OFF); return (child); @@ -147,22 +220,49 @@ zio_checksum_label_verifier(zio_cksum_t *zcp, uint64_t offset) ZIO_SET_CHECKSUM(zcp, offset, 0, 0, 0); } +/* + * Calls the template init function of a checksum which supports context + * templates and installs the template into the spa_t. + */ +static void +zio_checksum_template_init(enum zio_checksum checksum, spa_t *spa) +{ + zio_checksum_info_t *ci = &zio_checksum_table[checksum]; + + if (ci->ci_tmpl_init == NULL) + return; + if (spa->spa_cksum_tmpls[checksum] != NULL) + return; + + VERIFY(ci->ci_tmpl_free != NULL); + mutex_enter(&spa->spa_cksum_tmpls_lock); + if (spa->spa_cksum_tmpls[checksum] == NULL) { + spa->spa_cksum_tmpls[checksum] = + ci->ci_tmpl_init(&spa->spa_cksum_salt); + VERIFY(spa->spa_cksum_tmpls[checksum] != NULL); + } + mutex_exit(&spa->spa_cksum_tmpls_lock); +} + /* * Generate the checksum. */ void zio_checksum_compute(zio_t *zio, enum zio_checksum checksum, - void *data, uint64_t size) + void *data, uint64_t size) { blkptr_t *bp = zio->io_bp; uint64_t offset = zio->io_offset; zio_checksum_info_t *ci = &zio_checksum_table[checksum]; zio_cksum_t cksum; + spa_t *spa = zio->io_spa; ASSERT((uint_t)checksum < ZIO_CHECKSUM_FUNCTIONS); ASSERT(ci->ci_func[0] != NULL); - if (ci->ci_eck) { + zio_checksum_template_init(checksum, spa); + + if (ci->ci_flags & ZCHECKSUM_FLAG_EMBEDDED) { zio_eck_t *eck; if (checksum == ZIO_CHECKSUM_ZILOG2) { @@ -181,10 +281,12 @@ zio_checksum_compute(zio_t *zio, enum zio_checksum checksum, else bp->blk_cksum = eck->zec_cksum; eck->zec_magic = ZEC_MAGIC; - ci->ci_func[0](data, size, &cksum); + ci->ci_func[0](data, size, spa->spa_cksum_tmpls[checksum], + &cksum); eck->zec_cksum = cksum; } else { - ci->ci_func[0](data, size, &bp->blk_cksum); + ci->ci_func[0](data, size, spa->spa_cksum_tmpls[checksum], + &bp->blk_cksum); } } @@ -202,11 +304,14 @@ zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info) void *data = zio->io_data; zio_checksum_info_t *ci = &zio_checksum_table[checksum]; zio_cksum_t actual_cksum, expected_cksum, verifier; + spa_t *spa = zio->io_spa; if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func[0] == NULL) return (SET_ERROR(EINVAL)); - if (ci->ci_eck) { + zio_checksum_template_init(checksum, spa); + + if (ci->ci_flags & ZCHECKSUM_FLAG_EMBEDDED) { zio_eck_t *eck; if (checksum == ZIO_CHECKSUM_ZILOG2) { @@ -243,7 +348,8 @@ zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info) expected_cksum = eck->zec_cksum; eck->zec_cksum = verifier; - ci->ci_func[byteswap](data, size, &actual_cksum); + ci->ci_func[byteswap](data, size, + spa->spa_cksum_tmpls[checksum], &actual_cksum); eck->zec_cksum = expected_cksum; if (byteswap) @@ -253,7 +359,8 @@ zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info) ASSERT(!BP_IS_GANG(bp)); byteswap = BP_SHOULD_BYTESWAP(bp); expected_cksum = bp->blk_cksum; - ci->ci_func[byteswap](data, size, &actual_cksum); + ci->ci_func[byteswap](data, size, + spa->spa_cksum_tmpls[checksum], &actual_cksum); } info->zbc_expected = expected_cksum; @@ -275,3 +382,23 @@ zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info) return (0); } + +/* + * Called by a spa_t that's about to be deallocated. This steps through + * all of the checksum context templates and deallocates any that were + * initialized using the algorithm-specific template init function. + */ +void +zio_checksum_templates_free(spa_t *spa) +{ + for (enum zio_checksum checksum = 0; + checksum < ZIO_CHECKSUM_FUNCTIONS; checksum++) { + if (spa->spa_cksum_tmpls[checksum] != NULL) { + zio_checksum_info_t *ci = &zio_checksum_table[checksum]; + + VERIFY(ci->ci_tmpl_free != NULL); + ci->ci_tmpl_free(spa->spa_cksum_tmpls[checksum]); + spa->spa_cksum_tmpls[checksum] = NULL; + } + } +} diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zrlock.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zrlock.c index 22151843e03..7f6beeed614 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zrlock.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zrlock.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2014 by Delphix. All rights reserved. + * Copyright (c) 2014, 2015 by Delphix. All rights reserved. */ /* @@ -69,11 +69,7 @@ zrl_destroy(zrlock_t *zrl) } void -#ifdef ZFS_DEBUG -zrl_add_debug(zrlock_t *zrl, const char *zc) -#else -zrl_add(zrlock_t *zrl) -#endif +zrl_add_impl(zrlock_t *zrl, const char *zc) { uint32_t n = (uint32_t)zrl->zr_refcount; diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/debug.h b/sys/cddl/contrib/opensolaris/uts/common/sys/debug.h index 4ccc2ecba28..796454f9750 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/debug.h +++ b/sys/cddl/contrib/opensolaris/uts/common/sys/debug.h @@ -27,6 +27,7 @@ /* * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright 2013 Saso Kiselkov. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -123,6 +124,16 @@ _NOTE(CONSTCOND) } while (0) #define ASSERT0(x) ((void)0) #endif +/* + * Compile-time assertion. The condition 'x' must be constant. + */ +#ifndef CTASSERT +#define CTASSERT(x) _CTASSERT(x, __LINE__) +#define _CTASSERT(x, y) __CTASSERT(x, y) +#define __CTASSERT(x, y) \ + typedef char __compile_time_assertion__ ## y [(x) ? 1 : -1] +#endif + #ifdef _KERNEL extern void abort_sequence_enter(char *); diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h index 16d528e025d..809ce9bc5ff 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h +++ b/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h @@ -152,6 +152,7 @@ typedef enum { ZFS_PROP_SNAPSHOT_COUNT, ZFS_PROP_REDUNDANT_METADATA, ZFS_PROP_PREV_SNAP, + ZFS_PROP_RECEIVE_RESUME_TOKEN, ZFS_NUM_PROPS } zfs_prop_t; diff --git a/sys/conf/NOTES b/sys/conf/NOTES index c6478f9bb78..c1210e24019 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -960,6 +960,9 @@ device lagg # for sockets with the SO_DEBUG option set, which can then be examined # using the trpt(8) utility. # +# TCPPCAP enables code which keeps the last n packets sent and received +# on a TCP socket. +# # RADIX_MPATH provides support for equal-cost multi-path routing. # options MROUTING # Multicast routing @@ -976,6 +979,7 @@ options IPFILTER_DEFAULT_BLOCK #block all packets by default options IPSTEALTH #support for stealth forwarding options PF_DEFAULT_TO_DROP #drop everything by default options TCPDEBUG +options TCPPCAP options RADIX_MPATH # The MBUF_STRESS_TEST option enables options which create diff --git a/sys/conf/dtb.mk b/sys/conf/dtb.mk index 41bc9bb20d8..df6af54eae7 100644 --- a/sys/conf/dtb.mk +++ b/sys/conf/dtb.mk @@ -69,7 +69,7 @@ _dtbinstall: test -d ${DESTDIR}${DTBDIR} || ${INSTALL} -d -o ${DTBOWN} -g ${DTBGRP} ${DESTDIR}${DTBDIR} .for _dtb in ${DTB} ${INSTALL} -o ${DTBOWN} -g ${DTBGRP} -m ${DTBMODE} \ - ${_INSTALLFLAGS} ${_dtb} ${DESTDIR}${DTBDIR} + ${_INSTALLFLAGS} ${_dtb} ${DESTDIR}${DTBDIR}/ .endfor .endif # !target(realinstall) .endif # !target(install) diff --git a/sys/conf/files b/sys/conf/files index 3ddf41287a0..f32f60c0174 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -3682,6 +3682,7 @@ netinet/tcp_input.c optional inet | inet6 netinet/tcp_lro.c optional inet | inet6 netinet/tcp_output.c optional inet | inet6 netinet/tcp_offload.c optional tcp_offload inet | tcp_offload inet6 +netinet/tcp_pcap.c optional inet tcppcap | inet6 tcppcap netinet/tcp_reass.c optional inet | inet6 netinet/tcp_sack.c optional inet | inet6 netinet/tcp_subr.c optional inet | inet6 diff --git a/sys/conf/files.arm b/sys/conf/files.arm index 85830bb6717..33838ae146a 100644 --- a/sys/conf/files.arm +++ b/sys/conf/files.arm @@ -41,7 +41,8 @@ arm/arm/gic.c optional gic arm/arm/identcpu.c standard arm/arm/in_cksum.c optional inet | inet6 arm/arm/in_cksum_arm.S optional inet | inet6 -arm/arm/intr.c standard +arm/arm/intr.c optional !arm_intrng +arm/arm/intrng.c optional arm_intrng arm/arm/locore.S standard no-obj arm/arm/machdep.c standard arm/arm/mem.c optional mem @@ -49,6 +50,7 @@ arm/arm/minidump_machdep.c optional mem arm/arm/mp_machdep.c optional smp arm/arm/nexus.c standard arm/arm/physmem.c standard +arm/arm/pic_if.m optional arm_intrng arm/arm/pl190.c optional pl190 arm/arm/pl310.c optional pl310 arm/arm/platform.c optional platform @@ -56,7 +58,7 @@ arm/arm/platform_if.m optional platform arm/arm/pmap.c optional !armv6 arm/arm/pmap-v6.c optional armv6 !arm_new_pmap arm/arm/pmap-v6-new.c optional armv6 arm_new_pmap -arm/arm/pmu.c optional pmu +arm/arm/pmu.c optional pmu | fdt hwpmc arm/arm/sc_machdep.c optional sc arm/arm/setcpsr.S standard arm/arm/setstack.s standard diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index 399a35ec066..a49a1108993 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -68,6 +68,14 @@ dev/psci/psci_arm64.S optional psci dev/uart/uart_cpu_fdt.c optional uart fdt dev/uart/uart_dev_pl011.c optional uart pl011 dev/usb/controller/dwc_otg_hisi.c optional dwcotg soc_hisi_hi6220 +dev/vnic/nic_main.c optional vnic pci +dev/vnic/nicvf_main.c optional vnic pci pci_iov +dev/vnic/nicvf_queues.c optional vnic pci pci_iov +dev/vnic/thunder_bgx_fdt.c optional vnic fdt +dev/vnic/thunder_bgx.c optional vnic pci +dev/vnic/thunder_mdio_fdt.c optional vnic fdt +dev/vnic/thunder_mdio.c optional vnic +dev/vnic/lmac_if.m optional vnic kern/kern_clocksource.c standard kern/subr_dummy_vdso_tc.c standard libkern/bcmp.c standard diff --git a/sys/conf/kern.post.mk b/sys/conf/kern.post.mk index 55b4fd7a21e..ddf828ed317 100644 --- a/sys/conf/kern.post.mk +++ b/sys/conf/kern.post.mk @@ -270,22 +270,22 @@ kernel-install: fi .endif mkdir -p ${DESTDIR}${KODIR} - ${INSTALL} -p -m 555 -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_KO} ${DESTDIR}${KODIR} + ${INSTALL} -p -m 555 -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_KO} ${DESTDIR}${KODIR}/ .if defined(DEBUG) && !defined(INSTALL_NODEBUG) && ${MK_KERNEL_SYMBOLS} != "no" mkdir -p ${DESTDIR}${KERN_DEBUGDIR}${KODIR} - ${INSTALL} -p -m 555 -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_KO}.debug ${DESTDIR}${KERN_DEBUGDIR}${KODIR} + ${INSTALL} -p -m 555 -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_KO}.debug ${DESTDIR}${KERN_DEBUGDIR}${KODIR}/ .endif .if defined(KERNEL_EXTRA_INSTALL) - ${INSTALL} -p -m 555 -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_EXTRA_INSTALL} ${DESTDIR}${KODIR} + ${INSTALL} -p -m 555 -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_EXTRA_INSTALL} ${DESTDIR}${KODIR}/ .endif kernel-reinstall: @-chflags -R noschg ${DESTDIR}${KODIR} - ${INSTALL} -p -m 555 -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_KO} ${DESTDIR}${KODIR} + ${INSTALL} -p -m 555 -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_KO} ${DESTDIR}${KODIR}/ .if defined(DEBUG) && !defined(INSTALL_NODEBUG) && ${MK_KERNEL_SYMBOLS} != "no" - ${INSTALL} -p -m 555 -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_KO}.debug ${DESTDIR}${KERN_DEBUGDIR}${KODIR} + ${INSTALL} -p -m 555 -o ${KMODOWN} -g ${KMODGRP} ${KERNEL_KO}.debug ${DESTDIR}${KERN_DEBUGDIR}${KODIR}/ .endif config.o env.o hints.o vers.o vnode_if.o: diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk index 06e4cfcdc1f..8bbcfcb767d 100644 --- a/sys/conf/kmod.mk +++ b/sys/conf/kmod.mk @@ -296,10 +296,10 @@ realinstall: _kmodinstall .ORDER: beforeinstall _kmodinstall _kmodinstall: ${INSTALL} -o ${KMODOWN} -g ${KMODGRP} -m ${KMODMODE} \ - ${_INSTALLFLAGS} ${PROG} ${DESTDIR}${KMODDIR} + ${_INSTALLFLAGS} ${PROG} ${DESTDIR}${KMODDIR}/ .if defined(DEBUG_FLAGS) && !defined(INSTALL_NODEBUG) && ${MK_KERNEL_SYMBOLS} != "no" ${INSTALL} -o ${KMODOWN} -g ${KMODGRP} -m ${KMODMODE} \ - ${_INSTALLFLAGS} ${PROG}.debug ${DESTDIR}${KERN_DEBUGDIR}${KMODDIR} + ${_INSTALLFLAGS} ${PROG}.debug ${DESTDIR}${KERN_DEBUGDIR}${KMODDIR}/ .endif .include diff --git a/sys/conf/options b/sys/conf/options index ccbbd800c3c..dcea435a3c9 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -436,6 +436,7 @@ ROUTETABLES opt_route.h RSS opt_rss.h SLIP_IFF_OPTS opt_slip.h TCPDEBUG +TCPPCAP opt_global.h SIFTR TCP_OFFLOAD opt_inet.h # Enable code to dispatch TCP offloading TCP_SIGNATURE opt_inet.h diff --git a/sys/conf/options.arm b/sys/conf/options.arm index 57cef6a18dc..aee546a0386 100644 --- a/sys/conf/options.arm +++ b/sys/conf/options.arm @@ -1,6 +1,7 @@ #$FreeBSD$ ARMV6 opt_global.h ARM_CACHE_LOCK_ENABLE opt_global.h +ARM_INTRNG opt_global.h ARM_KERN_DIRECTMAP opt_vm.h ARM_L2_PIPT opt_global.h ARM_MANY_BOARD opt_global.h diff --git a/sys/contrib/ipfilter/netinet/ip_state.c b/sys/contrib/ipfilter/netinet/ip_state.c index a3930ea69fc..2ceec3262ec 100644 --- a/sys/contrib/ipfilter/netinet/ip_state.c +++ b/sys/contrib/ipfilter/netinet/ip_state.c @@ -3650,7 +3650,6 @@ ipf_state_del(softc, is, why) if (is->is_ref > 0) { int refs; - is->is_ref--; refs = is->is_ref; MUTEX_EXIT(&is->is_lock); if (!orphan) @@ -3667,7 +3666,7 @@ ipf_state_del(softc, is, why) } } - is->is_ref = 0; + ASSERT(is->is_ref == 0); MUTEX_EXIT(&is->is_lock); if (is->is_tqehead[0] != NULL) { diff --git a/sys/dev/cxgbe/iw_cxgbe/cm.c b/sys/dev/cxgbe/iw_cxgbe/cm.c index 18d572dc548..b5dede78bef 100644 --- a/sys/dev/cxgbe/iw_cxgbe/cm.c +++ b/sys/dev/cxgbe/iw_cxgbe/cm.c @@ -809,7 +809,7 @@ SYSCTL_INT(_hw_iw_cxgbe, OID_AUTO, ep_timeout_secs, CTLFLAG_RWTUN, &ep_timeout_s static int mpa_rev = 1; SYSCTL_INT(_hw_iw_cxgbe, OID_AUTO, mpa_rev, CTLFLAG_RWTUN, &mpa_rev, 0, - "MPA Revision, 0 supports amso1100, 1 is RFC0544 spec compliant, 2 is IETF MPA Peer Connect Draft compliant (default = 1)"); + "MPA Revision, 0 supports amso1100, 1 is RFC5044 spec compliant, 2 is IETF MPA Peer Connect Draft compliant (default = 1)"); static int markers_enabled; SYSCTL_INT(_hw_iw_cxgbe, OID_AUTO, markers_enabled, CTLFLAG_RWTUN, &markers_enabled, 0, diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c index 53fcfbb44d9..a5c7608310b 100644 --- a/sys/dev/cxgbe/t4_main.c +++ b/sys/dev/cxgbe/t4_main.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_rss.h" #include #include @@ -55,6 +56,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#ifdef RSS +#include +#endif #if defined(__i386__) || defined(__amd64__) #include #include @@ -3464,6 +3468,71 @@ adapter_full_uninit(struct adapter *sc) return (0); } +#ifdef RSS +#define SUPPORTED_RSS_HASHTYPES (RSS_HASHTYPE_RSS_IPV4 | \ + RSS_HASHTYPE_RSS_TCP_IPV4 | RSS_HASHTYPE_RSS_IPV6 | \ + RSS_HASHTYPE_RSS_TCP_IPV6 | RSS_HASHTYPE_RSS_UDP_IPV4 | \ + RSS_HASHTYPE_RSS_UDP_IPV6) + +/* Translates kernel hash types to hardware. */ +static int +hashconfig_to_hashen(int hashconfig) +{ + int hashen = 0; + + if (hashconfig & RSS_HASHTYPE_RSS_IPV4) + hashen |= F_FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN; + if (hashconfig & RSS_HASHTYPE_RSS_IPV6) + hashen |= F_FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN; + if (hashconfig & RSS_HASHTYPE_RSS_UDP_IPV4) { + hashen |= F_FW_RSS_VI_CONFIG_CMD_UDPEN | + F_FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN; + } + if (hashconfig & RSS_HASHTYPE_RSS_UDP_IPV6) { + hashen |= F_FW_RSS_VI_CONFIG_CMD_UDPEN | + F_FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN; + } + if (hashconfig & RSS_HASHTYPE_RSS_TCP_IPV4) + hashen |= F_FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN; + if (hashconfig & RSS_HASHTYPE_RSS_TCP_IPV6) + hashen |= F_FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN; + + return (hashen); +} + +/* Translates hardware hash types to kernel. */ +static int +hashen_to_hashconfig(int hashen) +{ + int hashconfig = 0; + + if (hashen & F_FW_RSS_VI_CONFIG_CMD_UDPEN) { + /* + * If UDP hashing was enabled it must have been enabled for + * either IPv4 or IPv6 (inclusive or). Enabling UDP without + * enabling any 4-tuple hash is nonsense configuration. + */ + MPASS(hashen & (F_FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN | + F_FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN)); + + if (hashen & F_FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN) + hashconfig |= RSS_HASHTYPE_RSS_UDP_IPV4; + if (hashen & F_FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN) + hashconfig |= RSS_HASHTYPE_RSS_UDP_IPV6; + } + if (hashen & F_FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN) + hashconfig |= RSS_HASHTYPE_RSS_TCP_IPV4; + if (hashen & F_FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN) + hashconfig |= RSS_HASHTYPE_RSS_TCP_IPV6; + if (hashen & F_FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN) + hashconfig |= RSS_HASHTYPE_RSS_IPV4; + if (hashen & F_FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN) + hashconfig |= RSS_HASHTYPE_RSS_IPV6; + + return (hashconfig); +} +#endif + int port_full_init(struct port_info *pi) { @@ -3471,7 +3540,14 @@ port_full_init(struct port_info *pi) struct ifnet *ifp = pi->ifp; uint16_t *rss; struct sge_rxq *rxq; - int rc, i, j; + int rc, i, j, hashen; +#ifdef RSS + int nbuckets = rss_getnumbuckets(); + int hashconfig = rss_gethashconfig(); + int extra; + uint32_t raw_rss_key[RSS_KEYSIZE / sizeof(uint32_t)]; + uint32_t rss_key[RSS_KEYSIZE / sizeof(uint32_t)]; +#endif ASSERT_SYNCHRONIZED_OP(sc); KASSERT((pi->flags & PORT_INIT_DONE) == 0, @@ -3490,13 +3566,42 @@ port_full_init(struct port_info *pi) /* * Setup RSS for this port. Save a copy of the RSS table for later use. */ + if (pi->nrxq > pi->rss_size) { + if_printf(ifp, "nrxq (%d) > hw RSS table size (%d); " + "some queues will never receive traffic.\n", pi->nrxq, + pi->rss_size); + } else if (pi->rss_size % pi->nrxq) { + if_printf(ifp, "nrxq (%d), hw RSS table size (%d); " + "expect uneven traffic distribution.\n", pi->nrxq, + pi->rss_size); + } +#ifdef RSS + MPASS(RSS_KEYSIZE == 40); + if (pi->nrxq != nbuckets) { + if_printf(ifp, "nrxq (%d) != kernel RSS buckets (%d);" + "performance will be impacted.\n", pi->nrxq, nbuckets); + } + + rss_getkey((void *)&raw_rss_key[0]); + for (i = 0; i < nitems(rss_key); i++) { + rss_key[i] = htobe32(raw_rss_key[nitems(rss_key) - 1 - i]); + } + t4_write_rss_key(sc, (void *)&rss_key[0], -1); +#endif rss = malloc(pi->rss_size * sizeof (*rss), M_CXGBE, M_ZERO | M_WAITOK); for (i = 0; i < pi->rss_size;) { +#ifdef RSS + j = rss_get_indirection_to_bucket(i); + j %= pi->nrxq; + rxq = &sc->sge.rxq[pi->first_rxq + j]; + rss[i++] = rxq->iq.abs_id; +#else for_each_rxq(pi, j, rxq) { rss[i++] = rxq->iq.abs_id; if (i == pi->rss_size) break; } +#endif } rc = -t4_config_rss_range(sc, sc->mbox, pi->viid, 0, pi->rss_size, rss, @@ -3506,6 +3611,54 @@ port_full_init(struct port_info *pi) goto done; } +#ifdef RSS + hashen = hashconfig_to_hashen(hashconfig); + + /* + * We may have had to enable some hashes even though the global config + * wants them disabled. This is a potential problem that must be + * reported to the user. + */ + extra = hashen_to_hashconfig(hashen) ^ hashconfig; + + /* + * If we consider only the supported hash types, then the enabled hashes + * are a superset of the requested hashes. In other words, there cannot + * be any supported hash that was requested but not enabled, but there + * can be hashes that were not requested but had to be enabled. + */ + extra &= SUPPORTED_RSS_HASHTYPES; + MPASS((extra & hashconfig) == 0); + + if (extra) { + if_printf(ifp, + "global RSS config (0x%x) cannot be accomodated.\n", + hashconfig); + } + if (extra & RSS_HASHTYPE_RSS_IPV4) + if_printf(ifp, "IPv4 2-tuple hashing forced on.\n"); + if (extra & RSS_HASHTYPE_RSS_TCP_IPV4) + if_printf(ifp, "TCP/IPv4 4-tuple hashing forced on.\n"); + if (extra & RSS_HASHTYPE_RSS_IPV6) + if_printf(ifp, "IPv6 2-tuple hashing forced on.\n"); + if (extra & RSS_HASHTYPE_RSS_TCP_IPV6) + if_printf(ifp, "TCP/IPv6 4-tuple hashing forced on.\n"); + if (extra & RSS_HASHTYPE_RSS_UDP_IPV4) + if_printf(ifp, "UDP/IPv4 4-tuple hashing forced on.\n"); + if (extra & RSS_HASHTYPE_RSS_UDP_IPV6) + if_printf(ifp, "UDP/IPv6 4-tuple hashing forced on.\n"); +#else + hashen = F_FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN | + F_FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN | + F_FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN | + F_FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN | F_FW_RSS_VI_CONFIG_CMD_UDPEN; +#endif + rc = -t4_config_vi_rss(sc, sc->mbox, pi->viid, hashen, rss[0]); + if (rc != 0) { + if_printf(ifp, "rss hash/defaultq config failed: %d\n", rc); + goto done; + } + pi->rss = rss; pi->flags |= PORT_INIT_DONE; done: @@ -8380,17 +8533,39 @@ tweak_tunables(void) { int nc = mp_ncpus; /* our snapshot of the number of CPUs */ - if (t4_ntxq10g < 1) + if (t4_ntxq10g < 1) { +#ifdef RSS + t4_ntxq10g = rss_getnumbuckets(); +#else t4_ntxq10g = min(nc, NTXQ_10G); +#endif + } - if (t4_ntxq1g < 1) + if (t4_ntxq1g < 1) { +#ifdef RSS + /* XXX: way too many for 1GbE? */ + t4_ntxq1g = rss_getnumbuckets(); +#else t4_ntxq1g = min(nc, NTXQ_1G); +#endif + } - if (t4_nrxq10g < 1) + if (t4_nrxq10g < 1) { +#ifdef RSS + t4_nrxq10g = rss_getnumbuckets(); +#else t4_nrxq10g = min(nc, NRXQ_10G); +#endif + } - if (t4_nrxq1g < 1) + if (t4_nrxq1g < 1) { +#ifdef RSS + /* XXX: way too many for 1GbE? */ + t4_nrxq1g = rss_getnumbuckets(); +#else t4_nrxq1g = min(nc, NRXQ_1G); +#endif + } #ifdef TCP_OFFLOAD if (t4_nofldtxq10g < 1) diff --git a/sys/dev/drm2/i915/intel_dp.c b/sys/dev/drm2/i915/intel_dp.c index 5791caf319c..eba5fe3206e 100644 --- a/sys/dev/drm2/i915/intel_dp.c +++ b/sys/dev/drm2/i915/intel_dp.c @@ -555,12 +555,12 @@ intel_dp_aux_native_read(struct intel_dp *intel_dp, } static int -intel_dp_i2c_aux_ch(device_t idev, int mode, uint8_t write_byte, - uint8_t *read_byte) +intel_dp_i2c_aux_ch(device_t adapter, int mode, + uint8_t write_byte, uint8_t *read_byte) { - struct iic_dp_aux_data *data; - struct intel_dp *intel_dp; - uint16_t address; + struct iic_dp_aux_data *data = device_get_softc(adapter); + struct intel_dp *intel_dp = data->priv; + uint16_t address = data->address; uint8_t msg[5]; uint8_t reply[2]; unsigned retry; @@ -568,10 +568,6 @@ intel_dp_i2c_aux_ch(device_t idev, int mode, uint8_t write_byte, int reply_bytes; int ret; - data = device_get_softc(idev); - intel_dp = data->priv; - address = data->address; - intel_dp_check_edp(intel_dp); /* Set up the command byte */ if (mode & MODE_I2C_READ) @@ -609,7 +605,7 @@ intel_dp_i2c_aux_ch(device_t idev, int mode, uint8_t write_byte, reply, reply_bytes); if (ret < 0) { DRM_DEBUG_KMS("aux_ch failed %d\n", ret); - return (ret); + return ret; } switch (reply[0] & AUX_NATIVE_REPLY_MASK) { @@ -620,14 +616,14 @@ intel_dp_i2c_aux_ch(device_t idev, int mode, uint8_t write_byte, break; case AUX_NATIVE_REPLY_NACK: DRM_DEBUG_KMS("aux_ch native nack\n"); - return (-EREMOTEIO); + return -EREMOTEIO; case AUX_NATIVE_REPLY_DEFER: DELAY(100); continue; default: DRM_ERROR("aux_ch invalid native reply 0x%02x\n", reply[0]); - return (-EREMOTEIO); + return -EREMOTEIO; } switch (reply[0] & AUX_I2C_REPLY_MASK) { @@ -638,19 +634,19 @@ intel_dp_i2c_aux_ch(device_t idev, int mode, uint8_t write_byte, return (0/*reply_bytes - 1*/); case AUX_I2C_REPLY_NACK: DRM_DEBUG_KMS("aux_i2c nack\n"); - return (-EREMOTEIO); + return -EREMOTEIO; case AUX_I2C_REPLY_DEFER: DRM_DEBUG_KMS("aux_i2c defer\n"); DELAY(100); break; default: DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]); - return (-EREMOTEIO); + return -EREMOTEIO; } } DRM_ERROR("too many retries, giving up\n"); - return (-EREMOTEIO); + return -EREMOTEIO; } static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp); @@ -660,7 +656,7 @@ static int intel_dp_i2c_init(struct intel_dp *intel_dp, struct intel_connector *intel_connector, const char *name) { - int ret; + int ret; DRM_DEBUG_KMS("i2c_init %s\n", name); @@ -669,7 +665,7 @@ intel_dp_i2c_init(struct intel_dp *intel_dp, intel_dp_i2c_aux_ch, intel_dp, &intel_dp->dp_iic_bus, &intel_dp->adapter); ironlake_edp_panel_vdd_off(intel_dp, false); - return (ret); + return ret; } static bool @@ -956,8 +952,7 @@ static void ironlake_wait_panel_status(struct intel_dp *intel_dp, I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); - if (_intel_wait_for(dev, - (I915_READ(PCH_PP_STATUS) & mask) == value, 5000, 10, "915iwp")) { + if (_intel_wait_for(dev, (I915_READ(PCH_PP_STATUS) & mask) == value, 5000, 10, "915iwp")) { DRM_ERROR("Panel status timeout: status %08x control %08x\n", I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); diff --git a/sys/dev/drm2/i915/intel_iic.c b/sys/dev/drm2/i915/intel_iic.c index 36a5b9e5661..c04b27bf242 100644 --- a/sys/dev/drm2/i915/intel_iic.c +++ b/sys/dev/drm2/i915/intel_iic.c @@ -96,51 +96,10 @@ struct intel_iic_softc { uint32_t reg0; }; -static void -intel_iic_quirk_set(struct drm_i915_private *dev_priv, bool enable) -{ - u32 val; - - /* When using bit bashing for I2C, this bit needs to be set to 1 */ - if (!IS_PINEVIEW(dev_priv->dev)) - return; - - val = I915_READ(DSPCLK_GATE_D); - if (enable) - val |= DPCUNIT_CLOCK_GATE_DISABLE; - else - val &= ~DPCUNIT_CLOCK_GATE_DISABLE; - I915_WRITE(DSPCLK_GATE_D, val); -} - -static u32 -intel_iic_get_reserved(device_t idev) -{ - struct intel_iic_softc *sc; - struct drm_device *dev; - struct drm_i915_private *dev_priv; - u32 reserved; - - sc = device_get_softc(idev); - dev = sc->drm_dev; - dev_priv = dev->dev_private; - - if (!IS_I830(dev) && !IS_845G(dev)) { - reserved = I915_READ_NOTRACE(sc->reg) & - (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE); - } else { - reserved = 0; - } - - return (reserved); -} - void intel_iic_reset(struct drm_device *dev) { - struct drm_i915_private *dev_priv; - - dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0); } @@ -157,86 +116,119 @@ intel_iicbus_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr) return (0); } -static void -intel_iicbb_setsda(device_t idev, int val) +static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable) { - struct intel_iic_softc *sc; - struct drm_i915_private *dev_priv; - u32 reserved; - u32 data_bits; + u32 val; - sc = device_get_softc(idev); - dev_priv = sc->drm_dev->dev_private; + /* When using bit bashing for I2C, this bit needs to be set to 1 */ + if (!IS_PINEVIEW(dev_priv->dev)) + return; - reserved = intel_iic_get_reserved(idev); - if (val) - data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK; + val = I915_READ(DSPCLK_GATE_D); + if (enable) + val |= DPCUNIT_CLOCK_GATE_DISABLE; else - data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK | - GPIO_DATA_VAL_MASK; - - I915_WRITE_NOTRACE(sc->reg, reserved | data_bits); - POSTING_READ(sc->reg); + val &= ~DPCUNIT_CLOCK_GATE_DISABLE; + I915_WRITE(DSPCLK_GATE_D, val); } -static void -intel_iicbb_setscl(device_t idev, int val) +static u32 get_reserved(device_t idev) { - struct intel_iic_softc *sc; - struct drm_i915_private *dev_priv; - u32 clock_bits, reserved; + struct intel_iic_softc *sc = device_get_softc(idev); + struct drm_device *dev = sc->drm_dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 reserved = 0; - sc = device_get_softc(idev); - dev_priv = sc->drm_dev->dev_private; + if (!IS_I830(dev) && !IS_845G(dev)) + reserved = I915_READ_NOTRACE(sc->reg) & + (GPIO_DATA_PULLUP_DISABLE | + GPIO_CLOCK_PULLUP_DISABLE); - reserved = intel_iic_get_reserved(idev); - if (val) - clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK; - else - clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | - GPIO_CLOCK_VAL_MASK; - - I915_WRITE_NOTRACE(sc->reg, reserved | clock_bits); - POSTING_READ(sc->reg); + return reserved; } -static int -intel_iicbb_getsda(device_t idev) +static int get_clock(device_t adapter) { - struct intel_iic_softc *sc; - struct drm_i915_private *dev_priv; - u32 reserved; - - sc = device_get_softc(idev); - dev_priv = sc->drm_dev->dev_private; - - reserved = intel_iic_get_reserved(idev); - - I915_WRITE_NOTRACE(sc->reg, reserved | GPIO_DATA_DIR_MASK); - I915_WRITE_NOTRACE(sc->reg, reserved); - return ((I915_READ_NOTRACE(sc->reg) & GPIO_DATA_VAL_IN) != 0); -} - -static int -intel_iicbb_getscl(device_t idev) -{ - struct intel_iic_softc *sc; - struct drm_i915_private *dev_priv; - u32 reserved; - - sc = device_get_softc(idev); - dev_priv = sc->drm_dev->dev_private; - - reserved = intel_iic_get_reserved(idev); - + struct intel_iic_softc *sc = device_get_softc(adapter); + struct drm_i915_private *dev_priv = sc->drm_dev->dev_private; + u32 reserved = get_reserved(adapter); I915_WRITE_NOTRACE(sc->reg, reserved | GPIO_CLOCK_DIR_MASK); I915_WRITE_NOTRACE(sc->reg, reserved); return ((I915_READ_NOTRACE(sc->reg) & GPIO_CLOCK_VAL_IN) != 0); } +static int get_data(device_t adapter) +{ + struct intel_iic_softc *sc = device_get_softc(adapter); + struct drm_i915_private *dev_priv = sc->drm_dev->dev_private; + u32 reserved = get_reserved(adapter); + I915_WRITE_NOTRACE(sc->reg, reserved | GPIO_DATA_DIR_MASK); + I915_WRITE_NOTRACE(sc->reg, reserved); + return ((I915_READ_NOTRACE(sc->reg) & GPIO_DATA_VAL_IN) != 0); +} + +static void set_clock(device_t adapter, int state_high) +{ + struct intel_iic_softc *sc = device_get_softc(adapter); + struct drm_i915_private *dev_priv = sc->drm_dev->dev_private; + u32 reserved = get_reserved(adapter); + u32 clock_bits; + + if (state_high) + clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK; + else + clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | + GPIO_CLOCK_VAL_MASK; + + I915_WRITE_NOTRACE(sc->reg, reserved | clock_bits); + POSTING_READ(sc->reg); +} + +static void set_data(device_t adapter, int state_high) +{ + struct intel_iic_softc *sc = device_get_softc(adapter); + struct drm_i915_private *dev_priv = sc->drm_dev->dev_private; + u32 reserved = get_reserved(adapter); + u32 data_bits; + + if (state_high) + data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK; + else + data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK | + GPIO_DATA_VAL_MASK; + + I915_WRITE_NOTRACE(sc->reg, reserved | data_bits); + POSTING_READ(sc->reg); +} + +static int +intel_gpio_pre_xfer(device_t adapter) +{ + struct intel_iic_softc *sc = device_get_softc(adapter); + struct drm_i915_private *dev_priv = sc->drm_dev->dev_private; + + intel_iic_reset(sc->drm_dev); + intel_i2c_quirk_set(dev_priv, true); + IICBB_SETSDA(adapter, 1); + IICBB_SETSCL(adapter, 1); + DELAY(I2C_RISEFALL_TIME); + return 0; +} + +static void +intel_gpio_post_xfer(device_t adapter) +{ + struct intel_iic_softc *sc = device_get_softc(adapter); + struct drm_i915_private *dev_priv = sc->drm_dev->dev_private; + + IICBB_SETSDA(adapter, 1); + IICBB_SETSCL(adapter, 1); + intel_i2c_quirk_set(dev_priv, false); +} + static int gmbus_xfer_read(struct drm_i915_private *dev_priv, struct iic_msg *msg, - u32 gmbus1_index) + u32 gmbus1_index) { int reg_offset = dev_priv->gpio_mmio_base; u16 len = msg->len; @@ -254,19 +246,19 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct iic_msg *msg, u32 gmbus2; ret = _intel_wait_for(sc->drm_dev, - ((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_RDY)), - 50, 1, "915gbr"); + ((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & + (GMBUS_SATOER | GMBUS_HW_RDY)), + 50, 1, "915gbr"); if (ret) - return (-ETIMEDOUT); + return -ETIMEDOUT; if (gmbus2 & GMBUS_SATOER) - return (-ENXIO); + return -ENXIO; val = I915_READ(GMBUS3 + reg_offset); do { *buf++ = val & 0xff; val >>= 8; - } while (--len != 0 && ++loop < 4); + } while (--len && ++loop < 4); } return 0; @@ -299,18 +291,18 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct iic_msg *msg) val = loop = 0; do { val |= *buf++ << (8 * loop); - } while (--len != 0 && ++loop < 4); + } while (--len && ++loop < 4); I915_WRITE(GMBUS3 + reg_offset, val); ret = _intel_wait_for(sc->drm_dev, - ((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_RDY)), - 50, 1, "915gbw"); + ((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & + (GMBUS_SATOER | GMBUS_HW_RDY)), + 50, 1, "915gbw"); if (ret) - return (-ETIMEDOUT); + return -ETIMEDOUT; if (gmbus2 & GMBUS_SATOER) - return (-ENXIO); + return -ENXIO; } return 0; } @@ -356,20 +348,20 @@ gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct iic_msg *msgs) } static int -intel_gmbus_transfer(device_t idev, struct iic_msg *msgs, uint32_t nmsgs) +gmbus_xfer(device_t adapter, + struct iic_msg *msgs, + uint32_t num) { - struct intel_iic_softc *sc; - struct drm_i915_private *dev_priv; + struct intel_iic_softc *sc = device_get_softc(adapter); + struct drm_i915_private *dev_priv = sc->drm_dev->dev_private; int error, i, ret, reg_offset, unit; error = 0; - sc = device_get_softc(idev); - dev_priv = sc->drm_dev->dev_private; - unit = device_get_unit(idev); + unit = device_get_unit(adapter); sx_xlock(&dev_priv->gmbus_sx); if (sc->force_bit_dev) { - error = -IICBUS_TRANSFER(dev_priv->bbbus[unit], msgs, nmsgs); + error = -IICBUS_TRANSFER(dev_priv->bbbus[unit], msgs, num); goto out; } @@ -377,10 +369,10 @@ intel_gmbus_transfer(device_t idev, struct iic_msg *msgs, uint32_t nmsgs) I915_WRITE(GMBUS0 + reg_offset, sc->reg0); - for (i = 0; i < nmsgs; i++) { + for (i = 0; i < num; i++) { u32 gmbus2; - if (gmbus_is_index_read(msgs, i, nmsgs)) { + if (gmbus_is_index_read(msgs, i, num)) { error = gmbus_xfer_index_read(dev_priv, &msgs[i]); i += 1; /* set i to the index of the read xfer */ } else if (msgs[i].flags & IIC_M_RD) { @@ -413,12 +405,12 @@ intel_gmbus_transfer(device_t idev, struct iic_msg *msgs, uint32_t nmsgs) /* Mark the GMBUS interface as disabled after waiting for idle. * We will re-enable it at the start of the next xfer, * till then let it sleep. - */ + */ if (_intel_wait_for(dev, (I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, 10, 1, "915gbu")) { DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n", - sc->name); + sc->name); error = -ETIMEDOUT; } I915_WRITE(GMBUS0 + reg_offset, 0); @@ -459,81 +451,16 @@ clear_err: timeout: DRM_INFO("GMBUS [%s] timed out, falling back to bit banging on pin %d\n", - sc->name, sc->reg0 & 0xff); + sc->name, sc->reg0 & 0xff); I915_WRITE(GMBUS0 + reg_offset, 0); - /* - * Hardware may not support GMBUS over these pins? - * Try GPIO bitbanging instead. - */ + /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */ sc->force_bit_dev = true; - error = -IICBUS_TRANSFER(idev, msgs, nmsgs); - goto out; + error = -IICBUS_TRANSFER(adapter, msgs, num); out: sx_xunlock(&dev_priv->gmbus_sx); - return (-error); -} - -device_t -intel_gmbus_get_adapter(struct drm_i915_private *dev_priv, - unsigned port) -{ - - if (!intel_gmbus_is_port_valid(port)) - DRM_ERROR("GMBUS get adapter %d: invalid port\n", port); - return (intel_gmbus_is_port_valid(port) ? dev_priv->gmbus[port - 1] : - NULL); -} - -void -intel_gmbus_set_speed(device_t idev, int speed) -{ - struct intel_iic_softc *sc; - - sc = device_get_softc(device_get_parent(idev)); - - sc->reg0 = (sc->reg0 & ~(0x3 << 8)) | speed; -} - -void -intel_gmbus_force_bit(device_t idev, bool force_bit) -{ - struct intel_iic_softc *sc; - - sc = device_get_softc(device_get_parent(idev)); - sc->force_bit_dev = force_bit; -} - -static int -intel_iicbb_pre_xfer(device_t idev) -{ - struct intel_iic_softc *sc; - struct drm_i915_private *dev_priv; - - sc = device_get_softc(idev); - dev_priv = sc->drm_dev->dev_private; - - intel_iic_reset(sc->drm_dev); - intel_iic_quirk_set(dev_priv, true); - IICBB_SETSDA(idev, 1); - IICBB_SETSCL(idev, 1); - DELAY(I2C_RISEFALL_TIME); - return (0); -} - -static void -intel_iicbb_post_xfer(device_t idev) -{ - struct intel_iic_softc *sc; - struct drm_i915_private *dev_priv; - - sc = device_get_softc(idev); - dev_priv = sc->drm_dev->dev_private; - - IICBB_SETSDA(idev, 1); - IICBB_SETSCL(idev, 1); - intel_iic_quirk_set(dev_priv, false); + return -error; } static int @@ -663,7 +590,7 @@ static device_method_t intel_gmbus_methods[] = { DEVMETHOD(device_attach, intel_gmbus_attach), DEVMETHOD(device_detach, intel_gmbus_detach), DEVMETHOD(iicbus_reset, intel_iicbus_reset), - DEVMETHOD(iicbus_transfer, intel_gmbus_transfer), + DEVMETHOD(iicbus_transfer, gmbus_xfer), DEVMETHOD_END }; static driver_t intel_gmbus_driver = { @@ -686,12 +613,12 @@ static device_method_t intel_iicbb_methods[] = { DEVMETHOD(iicbb_callback, iicbus_null_callback), DEVMETHOD(iicbb_reset, intel_iicbus_reset), - DEVMETHOD(iicbb_setsda, intel_iicbb_setsda), - DEVMETHOD(iicbb_setscl, intel_iicbb_setscl), - DEVMETHOD(iicbb_getsda, intel_iicbb_getsda), - DEVMETHOD(iicbb_getscl, intel_iicbb_getscl), - DEVMETHOD(iicbb_pre_xfer, intel_iicbb_pre_xfer), - DEVMETHOD(iicbb_post_xfer, intel_iicbb_post_xfer), + DEVMETHOD(iicbb_setsda, set_data), + DEVMETHOD(iicbb_setscl, set_clock), + DEVMETHOD(iicbb_getsda, get_data), + DEVMETHOD(iicbb_getscl, get_clock), + DEVMETHOD(iicbb_pre_xfer, intel_gpio_pre_xfer), + DEVMETHOD(iicbb_post_xfer, intel_gpio_post_xfer), DEVMETHOD_END }; static driver_t intel_iicbb_driver = { @@ -704,20 +631,19 @@ DRIVER_MODULE_ORDERED(intel_iicbb, drmn, intel_iicbb_driver, intel_iicbb_devclass, 0, 0, SI_ORDER_FIRST); DRIVER_MODULE(iicbb, intel_iicbb, iicbb_driver, iicbb_devclass, 0, 0); -int -intel_setup_gmbus(struct drm_device *dev) +int intel_setup_gmbus(struct drm_device *dev) { - struct drm_i915_private *dev_priv; + struct drm_i915_private *dev_priv = dev->dev_private; device_t iic_dev; - int i, ret; + int ret, i; - dev_priv = dev->dev_private; - sx_init(&dev_priv->gmbus_sx, "gmbus"); if (HAS_PCH_SPLIT(dev)) dev_priv->gpio_mmio_base = PCH_GPIOA - GPIOA; else dev_priv->gpio_mmio_base = 0; + sx_init(&dev_priv->gmbus_sx, "gmbus"); + /* * The Giant there is recursed, most likely. Normally, the * intel_setup_gmbus() is called from the attach method of the @@ -786,14 +712,41 @@ intel_setup_gmbus(struct drm_device *dev) intel_iic_reset(dev); } - mtx_unlock(&Giant); - return (0); + + return 0; err: intel_teardown_gmbus_m(dev, i); mtx_unlock(&Giant); - return (ret); + return ret; +} + +device_t intel_gmbus_get_adapter(struct drm_i915_private *dev_priv, + unsigned port) +{ + + if (!intel_gmbus_is_port_valid(port)) + DRM_ERROR("GMBUS get adapter %d: invalid port\n", port); + return (intel_gmbus_is_port_valid(port) ? dev_priv->gmbus[port - 1] : + NULL); +} + +void intel_gmbus_set_speed(device_t adapter, int speed) +{ + struct intel_iic_softc *sc; + + sc = device_get_softc(device_get_parent(adapter)); + + sc->reg0 = (sc->reg0 & ~(0x3 << 8)) | speed; +} + +void intel_gmbus_force_bit(device_t adapter, bool force_bit) +{ + struct intel_iic_softc *sc; + + sc = device_get_softc(device_get_parent(adapter)); + sc->force_bit_dev = force_bit; } static void @@ -806,8 +759,7 @@ intel_teardown_gmbus_m(struct drm_device *dev, int m) sx_destroy(&dev_priv->gmbus_sx); } -void -intel_teardown_gmbus(struct drm_device *dev) +void intel_teardown_gmbus(struct drm_device *dev) { mtx_lock(&Giant); diff --git a/sys/dev/hwpmc/pmc_events.h b/sys/dev/hwpmc/pmc_events.h index 96dc9003f78..696eea88e6c 100644 --- a/sys/dev/hwpmc/pmc_events.h +++ b/sys/dev/hwpmc/pmc_events.h @@ -5027,7 +5027,7 @@ __PMC_EV_ALIAS("IMPC_C0H_TRK_REQUEST.ALL", UCP_EVENT_84H_01H) #define PMC_EV_ARMV7_FIRST PMC_EV_ARMV7_EVENT_00H #define PMC_EV_ARMV7_LAST PMC_EV_ARMV7_EVENT_FFH -#define __PMC_EV_ALIAS_ARMV7_COMMON() \ +#define __PMC_EV_ALIAS_ARMV7_COMMON_A8() \ __PMC_EV_ALIAS("PMNC_SW_INCR", ARMV7_EVENT_00H) \ __PMC_EV_ALIAS("L1_ICACHE_REFILL", ARMV7_EVENT_01H) \ __PMC_EV_ALIAS("ITLB_REFILL", ARMV7_EVENT_02H) \ @@ -5046,7 +5046,10 @@ __PMC_EV_ALIAS("IMPC_C0H_TRK_REQUEST.ALL", UCP_EVENT_84H_01H) __PMC_EV_ALIAS("MEM_UNALIGNED_ACCESS", ARMV7_EVENT_0FH) \ __PMC_EV_ALIAS("PC_BRANCH_MIS_PRED", ARMV7_EVENT_10H) \ __PMC_EV_ALIAS("CLOCK_CYCLES", ARMV7_EVENT_11H) \ - __PMC_EV_ALIAS("PC_BRANCH_PRED", ARMV7_EVENT_12H) \ + __PMC_EV_ALIAS("PC_BRANCH_PRED", ARMV7_EVENT_12H) + +#define __PMC_EV_ALIAS_ARMV7_COMMON() \ + __PMC_EV_ALIAS_ARMV7_COMMON_A8() \ __PMC_EV_ALIAS("MEM_ACCESS", ARMV7_EVENT_13H) \ __PMC_EV_ALIAS("L1_ICACHE_ACCESS", ARMV7_EVENT_14H) \ __PMC_EV_ALIAS("L1_DCACHE_WB", ARMV7_EVENT_15H) \ @@ -5060,8 +5063,40 @@ __PMC_EV_ALIAS("IMPC_C0H_TRK_REQUEST.ALL", UCP_EVENT_84H_01H) __PMC_EV_ALIAS("BUS_CYCLES", ARMV7_EVENT_1DH) \ __PMC_EV_ALIAS("CPU_CYCLES", ARMV7_EVENT_FFH) -#define __PMC_EV_ALIAS_ARMV7_CORTEX_A8() \ - __PMC_EV_ALIAS_ARMV7_COMMON() +#define __PMC_EV_ALIAS_ARMV7_CORTEX_A8() \ + __PMC_EV_ALIAS_ARMV7_COMMON_A8() \ + __PMC_EV_ALIAS("WRITE_BUF_FULL", ARMV7_EVENT_40H) \ + __PMC_EV_ALIAS("L2_STORE_MERGED", ARMV7_EVENT_41H) \ + __PMC_EV_ALIAS("L2_STORE_BUFFERABLE", ARMV7_EVENT_42H) \ + __PMC_EV_ALIAS("L2_ACCESS", ARMV7_EVENT_43H) \ + __PMC_EV_ALIAS("L2_CACHE_MISS", ARMV7_EVENT_44H) \ + __PMC_EV_ALIAS("AXI_READ", ARMV7_EVENT_45H) \ + __PMC_EV_ALIAS("AXI_WRITE", ARMV7_EVENT_46H) \ + __PMC_EV_ALIAS("MEM_REPLAY_EVT", ARMV7_EVENT_47H) \ + __PMC_EV_ALIAS("MEM_UNALIGNED_ACCESS_REPLAY", ARMV7_EVENT_48H) \ + __PMC_EV_ALIAS("L1_DCACHE_HASH_MISS", ARMV7_EVENT_49H) \ + __PMC_EV_ALIAS("L1_ICACHE_HASH_MISS", ARMV7_EVENT_4AH) \ + __PMC_EV_ALIAS("L1_CACHE_PAGECOL_ALIAS", ARMV7_EVENT_4BH) \ + __PMC_EV_ALIAS("L1_DCACHE_NEON_ACCESS", ARMV7_EVENT_4CH) \ + __PMC_EV_ALIAS("L1_DCACHE_NEON_CACHEABLE", ARMV7_EVENT_4DH) \ + __PMC_EV_ALIAS("L2_CACHE_NEON_MEM_ACCESS", ARMV7_EVENT_4EH) \ + __PMC_EV_ALIAS("L2_CACHE_NEON_HIT", ARMV7_EVENT_4FH) \ + __PMC_EV_ALIAS("L1_CACHE_ACCESS_NOCP15", ARMV7_EVENT_50H) \ + __PMC_EV_ALIAS("RET_STACK_MISPREDICT", ARMV7_EVENT_51H) \ + __PMC_EV_ALIAS("BRANCH_DIR_MISPREDICT", ARMV7_EVENT_52H) \ + __PMC_EV_ALIAS("PRED_BRANCH_PRED_TAKEN", ARMV7_EVENT_53H) \ + __PMC_EV_ALIAS("PRED_BRANCH_EXEC_TAKEN", ARMV7_EVENT_54H) \ + __PMC_EV_ALIAS("OPS_ISSUED", ARMV7_EVENT_55H) \ + __PMC_EV_ALIAS("CYCLES_NO_INSTRUCTION", ARMV7_EVENT_56H) \ + __PMC_EV_ALIAS("INSTRUCTIONS_ISSUED_CYCLE", ARMV7_EVENT_57H) \ + __PMC_EV_ALIAS("CYCLES_STALLED_NEON_MRC", ARMV7_EVENT_58H) \ + __PMC_EV_ALIAS("CYCLES_STALLED_NEON_FULLQ", ARMV7_EVENT_59H) \ + __PMC_EV_ALIAS("CYCLES_NONIDLE_NEON_INT", ARMV7_EVENT_5AH) \ + __PMC_EV_ALIAS("PMUEXTIN0_EVT", ARMV7_EVENT_70H) \ + __PMC_EV_ALIAS("PMUEXTIN1_EVT", ARMV7_EVENT_71H) \ + __PMC_EV_ALIAS("PMUEXTIN_EVT", ARMV7_EVENT_72H) +#define PMC_EV_ARMV7_CORTEX_A8_FIRST PMC_EV_ARMV7_PMNC_SW_INCR +#define PMC_EV_ARMV7_CORTEX_A8_LAST PMC_EV_ARMV7_PMUEXTIN_EVT #define __PMC_EV_ALIAS_ARMV7_CORTEX_A9() \ __PMC_EV_ALIAS_ARMV7_COMMON() \ diff --git a/sys/dev/mpr/mpr.c b/sys/dev/mpr/mpr.c index 07e73c54e8b..ea8249b7988 100644 --- a/sys/dev/mpr/mpr.c +++ b/sys/dev/mpr/mpr.c @@ -1471,11 +1471,9 @@ mpr_setup_sysctl(struct mpr_softc *sc) OID_AUTO, "enable_ssu", CTLFLAG_RW, &sc->enable_ssu, 0, "enable SSU to SATA SSD/HDD at shutdown"); -#if __FreeBSD_version >= 900030 SYSCTL_ADD_UQUAD(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "chain_alloc_fail", CTLFLAG_RD, &sc->chain_alloc_fail, "chain allocation failures"); -#endif //FreeBSD_version >= 900030 SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "spinup_wait_time", CTLFLAG_RD, diff --git a/sys/dev/mpr/mprvar.h b/sys/dev/mpr/mprvar.h index acac44c5bae..306324b674a 100644 --- a/sys/dev/mpr/mprvar.h +++ b/sys/dev/mpr/mprvar.h @@ -265,9 +265,7 @@ struct mpr_softc { int chain_free_lowwater; u_int enable_ssu; int spinup_wait_time; -#if __FreeBSD_version >= 900030 uint64_t chain_alloc_fail; -#endif struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; char fw_version[16]; @@ -451,11 +449,8 @@ mpr_alloc_chain(struct mpr_softc *sc) sc->chain_free--; if (sc->chain_free < sc->chain_free_lowwater) sc->chain_free_lowwater = sc->chain_free; - } -#if __FreeBSD_version >= 900030 - else + } else sc->chain_alloc_fail++; -#endif return (chain); } diff --git a/sys/dev/mps/mps.c b/sys/dev/mps/mps.c index 7f73162830a..4080fc678fc 100644 --- a/sys/dev/mps/mps.c +++ b/sys/dev/mps/mps.c @@ -1468,11 +1468,9 @@ mps_setup_sysctl(struct mps_softc *sc) OID_AUTO, "enable_ssu", CTLFLAG_RW, &sc->enable_ssu, 0, "enable SSU to SATA SSD/HDD at shutdown"); -#if __FreeBSD_version >= 900030 SYSCTL_ADD_UQUAD(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "chain_alloc_fail", CTLFLAG_RD, &sc->chain_alloc_fail, "chain allocation failures"); -#endif //FreeBSD_version >= 900030 SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "spinup_wait_time", CTLFLAG_RD, diff --git a/sys/dev/mps/mpsvar.h b/sys/dev/mps/mpsvar.h index 7a43e62aa12..96bb4e1cfa6 100644 --- a/sys/dev/mps/mpsvar.h +++ b/sys/dev/mps/mpsvar.h @@ -283,9 +283,7 @@ struct mps_softc { int chain_free_lowwater; u_int enable_ssu; int spinup_wait_time; -#if __FreeBSD_version >= 900030 uint64_t chain_alloc_fail; -#endif struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; char fw_version[16]; @@ -483,11 +481,8 @@ mps_alloc_chain(struct mps_softc *sc) sc->chain_free--; if (sc->chain_free < sc->chain_free_lowwater) sc->chain_free_lowwater = sc->chain_free; - } -#if __FreeBSD_version >= 900030 - else + } else sc->chain_alloc_fail++; -#endif return (chain); } diff --git a/sys/dev/ntb/if_ntb/if_ntb.c b/sys/dev/ntb/if_ntb/if_ntb.c index 81ebe73a420..6a1fd92e5c8 100644 --- a/sys/dev/ntb/if_ntb/if_ntb.c +++ b/sys/dev/ntb/if_ntb/if_ntb.c @@ -1,5 +1,6 @@ /*- * Copyright (C) 2013 Intel Corporation + * Copyright (C) 2015 EMC Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,8 +31,10 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include +#include #include #include #include @@ -41,14 +44,17 @@ __FBSDID("$FreeBSD$"); #include #include #include + #include #include #include #include #include #include + #include #include + #include #include #include @@ -66,24 +72,36 @@ __FBSDID("$FreeBSD$"); * be picked up and redistributed in Linux with a dual GPL/BSD license. */ -/* TODO: These functions should really be part of the kernel */ -#define test_bit(pos, bitmap_addr) (*(bitmap_addr) & 1UL << (pos)) -#define set_bit(pos, bitmap_addr) *(bitmap_addr) |= 1UL << (pos) -#define clear_bit(pos, bitmap_addr) *(bitmap_addr) &= ~(1UL << (pos)) +#define QP_SETSIZE 64 +BITSET_DEFINE(_qpset, QP_SETSIZE); +#define test_bit(pos, addr) BIT_ISSET(QP_SETSIZE, (pos), (addr)) +#define set_bit(pos, addr) BIT_SET(QP_SETSIZE, (pos), (addr)) +#define clear_bit(pos, addr) BIT_CLR(QP_SETSIZE, (pos), (addr)) +#define ffs_bit(addr) BIT_FFS(QP_SETSIZE, (addr)) #define KTR_NTB KTR_SPARE3 -#define NTB_TRANSPORT_VERSION 3 +#define NTB_TRANSPORT_VERSION 4 #define NTB_RX_MAX_PKTS 64 #define NTB_RXQ_SIZE 300 +enum ntb_link_event { + NTB_LINK_DOWN = 0, + NTB_LINK_UP, +}; + static unsigned int transport_mtu = 0x4000 + ETHER_HDR_LEN + ETHER_CRC_LEN; -/* - * This is an oversimplification to work around Xeon Errata. The second client - * may be usable for unidirectional traffic. - */ -static unsigned int max_num_clients = 1; +static uint64_t max_mw_size; +SYSCTL_UQUAD(_hw_ntb, OID_AUTO, max_mw_size, CTLFLAG_RDTUN, &max_mw_size, 0, + "If enabled (non-zero), limit the size of large memory windows. " + "Both sides of the NTB MUST set the same value here."); + +static unsigned int max_num_clients; +SYSCTL_UINT(_hw_ntb, OID_AUTO, max_num_clients, CTLFLAG_RDTUN, + &max_num_clients, 0, "Maximum number of NTB transport clients. " + "0 (default) - use all available NTB memory windows; " + "positive integer N - Limit to N memory windows."); STAILQ_HEAD(ntb_queue_list, ntb_queue_entry); @@ -91,11 +109,15 @@ struct ntb_queue_entry { /* ntb_queue list reference */ STAILQ_ENTRY(ntb_queue_entry) entry; - /* info on data to be transfered */ + /* info on data to be transferred */ void *cb_data; void *buf; - uint64_t len; - uint64_t flags; + unsigned len; + unsigned flags; + + struct ntb_transport_qp *qp; + struct ntb_payload_header *x_hdr; + unsigned index; }; struct ntb_rx_info { @@ -103,40 +125,42 @@ struct ntb_rx_info { }; struct ntb_transport_qp { - struct ntb_netdev *transport; + struct ntb_transport_ctx *transport; struct ntb_softc *ntb; void *cb_data; bool client_ready; - bool qp_link; + bool link_is_up; uint8_t qp_num; /* Only 64 QPs are allowed. 0-63 */ struct ntb_rx_info *rx_info; struct ntb_rx_info *remote_rx_info; - void (*tx_handler) (struct ntb_transport_qp *qp, void *qp_data, + void (*tx_handler)(struct ntb_transport_qp *qp, void *qp_data, void *data, int len); struct ntb_queue_list tx_free_q; struct mtx ntb_tx_free_q_lock; void *tx_mw; + bus_addr_t tx_mw_phys; uint64_t tx_index; uint64_t tx_max_entry; uint64_t tx_max_frame; - void (*rx_handler) (struct ntb_transport_qp *qp, void *qp_data, + void (*rx_handler)(struct ntb_transport_qp *qp, void *qp_data, void *data, int len); struct ntb_queue_list rx_pend_q; struct ntb_queue_list rx_free_q; struct mtx ntb_rx_pend_q_lock; struct mtx ntb_rx_free_q_lock; struct task rx_completion_task; + struct task rxc_db_work; void *rx_buff; uint64_t rx_index; uint64_t rx_max_entry; uint64_t rx_max_frame; - void (*event_handler) (void *data, int status); + void (*event_handler)(void *data, enum ntb_link_event status); struct callout link_work; struct callout queue_full; struct callout rx_full; @@ -156,37 +180,48 @@ struct ntb_transport_qp { }; struct ntb_queue_handlers { - void (*rx_handler) (struct ntb_transport_qp *qp, void *qp_data, + void (*rx_handler)(struct ntb_transport_qp *qp, void *qp_data, void *data, int len); - void (*tx_handler) (struct ntb_transport_qp *qp, void *qp_data, + void (*tx_handler)(struct ntb_transport_qp *qp, void *qp_data, void *data, int len); - void (*event_handler) (void *data, int status); + void (*event_handler)(void *data, enum ntb_link_event status); }; - struct ntb_transport_mw { - size_t size; + vm_paddr_t phys_addr; + size_t phys_size; + size_t xlat_align; + size_t xlat_align_size; + /* Tx buff is off vbase / phys_addr */ + void *vbase; + size_t xlat_size; + size_t buff_size; + /* Rx buff is off virt_addr / dma_addr */ void *virt_addr; - vm_paddr_t dma_addr; + bus_addr_t dma_addr; }; -struct ntb_netdev { +struct ntb_transport_ctx { struct ntb_softc *ntb; struct ifnet *ifp; - struct ntb_transport_mw mw[NTB_NUM_MW]; - struct ntb_transport_qp *qps; - uint64_t max_qps; - uint64_t qp_bitmap; - bool transport_link; + struct ntb_transport_mw mw_vec[NTB_MAX_NUM_MW]; + struct ntb_transport_qp *qp_vec; + struct _qpset qp_bitmap; + struct _qpset qp_bitmap_free; + unsigned mw_count; + unsigned qp_count; + enum ntb_link_event link_is_up; struct callout link_work; - struct ntb_transport_qp *qp; uint64_t bufsize; u_char eaddr[ETHER_ADDR_LEN]; struct mtx tx_lock; struct mtx rx_lock; + + /* The hardcoded single queuepair in ntb_setup_interface() */ + struct ntb_transport_qp *qp; }; -static struct ntb_netdev net_softc; +static struct ntb_transport_ctx net_softc; enum { IF_NTB_DESC_DONE_FLAG = 1 << 0, @@ -219,7 +254,7 @@ enum { IF_NTB_MAX_SPAD, }; -#define QP_TO_MW(qp) ((qp) % NTB_NUM_MW) +#define QP_TO_MW(nt, qp) ((qp) % nt->mw_count) #define NTB_QP_DEF_NUM_ENTRIES 100 #define NTB_LINK_DOWN_TIMEOUT 10 @@ -233,37 +268,37 @@ static void ntb_net_tx_handler(struct ntb_transport_qp *qp, void *qp_data, void *data, int len); static void ntb_net_rx_handler(struct ntb_transport_qp *qp, void *qp_data, void *data, int len); -static void ntb_net_event_handler(void *data, int status); +static void ntb_net_event_handler(void *data, enum ntb_link_event status); static int ntb_transport_init(struct ntb_softc *ntb); -static void ntb_transport_free(void *transport); -static void ntb_transport_init_queue(struct ntb_netdev *nt, +static void ntb_transport_free(struct ntb_transport_ctx *); +static void ntb_transport_init_queue(struct ntb_transport_ctx *nt, unsigned int qp_num); static void ntb_transport_free_queue(struct ntb_transport_qp *qp); -static struct ntb_transport_qp * ntb_transport_create_queue(void *data, +static struct ntb_transport_qp *ntb_transport_create_queue(void *data, struct ntb_softc *pdev, const struct ntb_queue_handlers *handlers); static void ntb_transport_link_up(struct ntb_transport_qp *qp); static int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data, unsigned int len); static int ntb_process_tx(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry); -static void ntb_tx_copy_task(struct ntb_transport_qp *qp, +static void ntb_memcpy_tx(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry, void *offset); static void ntb_qp_full(void *arg); -static void ntb_transport_rxc_db(void *data, int db_num); +static void ntb_transport_rxc_db(void *arg, int pending); static void ntb_rx_pendq_full(void *arg); -static void ntb_transport_rx(struct ntb_transport_qp *qp); static int ntb_process_rxc(struct ntb_transport_qp *qp); static void ntb_rx_copy_task(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry, void *offset); -static void ntb_rx_completion_task(void *arg, int pending); -static void ntb_transport_event_callback(void *data, enum ntb_hw_event event); +static void ntb_complete_rxc(void *arg, int pending); +static void ntb_transport_doorbell_callback(void *data, int vector); +static void ntb_transport_event_callback(void *data); static void ntb_transport_link_work(void *arg); -static int ntb_set_mw(struct ntb_netdev *nt, int num_mw, unsigned int size); -static void ntb_free_mw(struct ntb_netdev *nt, int num_mw); -static void ntb_transport_setup_qp_mw(struct ntb_netdev *nt, +static int ntb_set_mw(struct ntb_transport_ctx *, int num_mw, unsigned size); +static void ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw); +static int ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt, unsigned int qp_num); static void ntb_qp_link_work(void *arg); -static void ntb_transport_link_cleanup(struct ntb_netdev *nt); +static void ntb_transport_link_cleanup(struct ntb_transport_ctx *nt); static void ntb_qp_link_down(struct ntb_transport_qp *qp); static void ntb_qp_link_cleanup(struct ntb_transport_qp *qp); static void ntb_transport_link_down(struct ntb_transport_qp *qp); @@ -275,6 +310,11 @@ static struct ntb_queue_entry *ntb_list_rm(struct mtx *lock, static void create_random_local_eui48(u_char *eaddr); static unsigned int ntb_transport_max_size(struct ntb_transport_qp *qp); +static const struct ntb_ctx_ops ntb_transport_ops = { + .link_event = ntb_transport_event_callback, + .db_event = ntb_transport_doorbell_callback, +}; + MALLOC_DEFINE(M_NTB_IF, "if_ntb", "ntb network driver"); /* Module setup and teardown */ @@ -312,6 +352,7 @@ ntb_setup_interface(void) struct ifnet *ifp; struct ntb_queue_handlers handlers = { ntb_net_rx_handler, ntb_net_tx_handler, ntb_net_event_handler }; + int rc; net_softc.ntb = devclass_get_softc(devclass_find("ntb_hw"), 0); if (net_softc.ntb == NULL) { @@ -319,11 +360,16 @@ ntb_setup_interface(void) return (ENXIO); } - ntb_transport_init(net_softc.ntb); + rc = ntb_transport_init(net_softc.ntb); + if (rc != 0) { + printf("ntb: Cannot init transport: %d\n", rc); + return (rc); + } ifp = net_softc.ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { - printf("ntb: cannot allocate ifnet structure\n"); + ntb_transport_free(&net_softc); + printf("ntb: Cannot allocate ifnet structure\n"); return (ENOMEM); } @@ -374,7 +420,7 @@ ntb_teardown_interface(void) static void ntb_net_init(void *arg) { - struct ntb_netdev *ntb_softc = arg; + struct ntb_transport_ctx *ntb_softc = arg; struct ifnet *ifp = ntb_softc->ifp; ifp->if_drv_flags |= IFF_DRV_RUNNING; @@ -386,7 +432,7 @@ ntb_net_init(void *arg) static int ntb_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { - struct ntb_netdev *nt = ifp->if_softc; + struct ntb_transport_ctx *nt = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int error = 0; @@ -415,7 +461,7 @@ static void ntb_start(struct ifnet *ifp) { struct mbuf *m_head; - struct ntb_netdev *nt = ifp->if_softc; + struct ntb_transport_ctx *nt = ifp->if_softc; int rc; mtx_lock(&nt->tx_lock); @@ -465,9 +511,23 @@ ntb_net_rx_handler(struct ntb_transport_qp *qp, void *qp_data, void *data, } static void -ntb_net_event_handler(void *data, int status) +ntb_net_event_handler(void *data, enum ntb_link_event status) { + struct ifnet *ifp; + ifp = data; + (void)ifp; + + /* XXX The Linux driver munges with the carrier status here. */ + + switch (status) { + case NTB_LINK_DOWN: + break; + case NTB_LINK_UP: + break; + default: + panic("Bogus ntb_link_event %u\n", status); + } } /* Transport Init and teardown */ @@ -475,95 +535,138 @@ ntb_net_event_handler(void *data, int status) static int ntb_transport_init(struct ntb_softc *ntb) { - struct ntb_netdev *nt = &net_softc; - int rc, i; + struct ntb_transport_ctx *nt = &net_softc; + struct ntb_transport_mw *mw; + uint64_t qp_bitmap; + int rc; + unsigned i; + + nt->mw_count = ntb_mw_count(ntb); + for (i = 0; i < nt->mw_count; i++) { + mw = &nt->mw_vec[i]; + + rc = ntb_mw_get_range(ntb, i, &mw->phys_addr, &mw->vbase, + &mw->phys_size, &mw->xlat_align, &mw->xlat_align_size); + if (rc != 0) + goto err; + + mw->buff_size = 0; + mw->xlat_size = 0; + mw->virt_addr = 0; + mw->dma_addr = 0; + } + + qp_bitmap = ntb_db_valid_mask(ntb); + nt->qp_count = flsll(qp_bitmap); + KASSERT(nt->qp_count != 0, ("bogus db bitmap")); + nt->qp_count -= 1; + + if (max_num_clients != 0 && max_num_clients < nt->qp_count) + nt->qp_count = max_num_clients; + else if (nt->mw_count < nt->qp_count) + nt->qp_count = nt->mw_count; + KASSERT(nt->qp_count <= QP_SETSIZE, ("invalid qp_count")); - nt->max_qps = max_num_clients; - ntb_register_transport(ntb, nt); mtx_init(&nt->tx_lock, "ntb transport tx", NULL, MTX_DEF); mtx_init(&nt->rx_lock, "ntb transport rx", NULL, MTX_DEF); - nt->qps = malloc(nt->max_qps * sizeof(struct ntb_transport_qp), - M_NTB_IF, M_WAITOK|M_ZERO); + nt->qp_vec = malloc(nt->qp_count * sizeof(*nt->qp_vec), M_NTB_IF, + M_WAITOK | M_ZERO); - nt->qp_bitmap = ((uint64_t) 1 << nt->max_qps) - 1; - - for (i = 0; i < nt->max_qps; i++) + for (i = 0; i < nt->qp_count; i++) { + set_bit(i, &nt->qp_bitmap); + set_bit(i, &nt->qp_bitmap_free); ntb_transport_init_queue(nt, i); + } callout_init(&nt->link_work, 0); - rc = ntb_register_event_callback(ntb, - ntb_transport_event_callback); + rc = ntb_set_ctx(ntb, nt, &ntb_transport_ops); if (rc != 0) goto err; - if (ntb_query_link_status(ntb)) { - if (bootverbose) - device_printf(ntb_get_device(ntb), "link up\n"); - callout_reset(&nt->link_work, 0, ntb_transport_link_work, nt); - } - + nt->link_is_up = false; + ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); + ntb_link_event(ntb); return (0); err: - free(nt->qps, M_NTB_IF); - ntb_unregister_transport(ntb); + free(nt->qp_vec, M_NTB_IF); + nt->qp_vec = NULL; return (rc); } static void -ntb_transport_free(void *transport) +ntb_transport_free(struct ntb_transport_ctx *nt) { - struct ntb_netdev *nt = transport; struct ntb_softc *ntb = nt->ntb; - int i; + struct _qpset qp_bitmap_alloc; + uint8_t i; - nt->transport_link = NTB_LINK_DOWN; + ntb_transport_link_cleanup(nt); callout_drain(&nt->link_work); - /* verify that all the qps are freed */ - for (i = 0; i < nt->max_qps; i++) - if (!test_bit(i, &nt->qp_bitmap)) - ntb_transport_free_queue(&nt->qps[i]); + BIT_COPY(QP_SETSIZE, &nt->qp_bitmap, &qp_bitmap_alloc); + BIT_NAND(QP_SETSIZE, &qp_bitmap_alloc, &nt->qp_bitmap_free); - ntb_unregister_event_callback(ntb); + /* Verify that all the QPs are freed */ + for (i = 0; i < nt->qp_count; i++) + if (test_bit(i, &qp_bitmap_alloc)) + ntb_transport_free_queue(&nt->qp_vec[i]); - for (i = 0; i < NTB_NUM_MW; i++) + ntb_link_disable(ntb); + ntb_clear_ctx(ntb); + + for (i = 0; i < nt->mw_count; i++) ntb_free_mw(nt, i); - free(nt->qps, M_NTB_IF); - ntb_unregister_transport(ntb); + free(nt->qp_vec, M_NTB_IF); } static void -ntb_transport_init_queue(struct ntb_netdev *nt, unsigned int qp_num) +ntb_transport_init_queue(struct ntb_transport_ctx *nt, unsigned int qp_num) { + struct ntb_transport_mw *mw; struct ntb_transport_qp *qp; - unsigned int num_qps_mw, tx_size; - uint8_t mw_num = QP_TO_MW(qp_num); + vm_paddr_t mw_base; + uint64_t mw_size, qp_offset; + size_t tx_size; + unsigned num_qps_mw, mw_num, mw_count; - qp = &nt->qps[qp_num]; + mw_count = nt->mw_count; + mw_num = QP_TO_MW(nt, qp_num); + mw = &nt->mw_vec[mw_num]; + + qp = &nt->qp_vec[qp_num]; qp->qp_num = qp_num; qp->transport = nt; qp->ntb = nt->ntb; - qp->qp_link = NTB_LINK_DOWN; - qp->client_ready = NTB_LINK_DOWN; + qp->link_is_up = false; + qp->client_ready = false; qp->event_handler = NULL; - if (nt->max_qps % NTB_NUM_MW && mw_num < nt->max_qps % NTB_NUM_MW) - num_qps_mw = nt->max_qps / NTB_NUM_MW + 1; + if (nt->qp_count % mw_count && mw_num + 1 < nt->qp_count / mw_count) + num_qps_mw = nt->qp_count / mw_count + 1; else - num_qps_mw = nt->max_qps / NTB_NUM_MW; + num_qps_mw = nt->qp_count / mw_count; + + mw_base = mw->phys_addr; + mw_size = mw->phys_size; + + tx_size = mw_size / num_qps_mw; + qp_offset = tx_size * qp_num / mw_count; + + qp->tx_mw = (char *)mw->vbase + qp_offset; + KASSERT(qp->tx_mw != NULL, ("uh oh?")); + + /* XXX Assumes that a vm_paddr_t is equivalent to bus_addr_t */ + qp->tx_mw_phys = mw_base + qp_offset; + KASSERT(qp->tx_mw_phys != 0, ("uh oh?")); - tx_size = (unsigned int) ntb_get_mw_size(qp->ntb, mw_num) / num_qps_mw; - qp->rx_info = (struct ntb_rx_info *) - ((char *)ntb_get_mw_vbase(qp->ntb, mw_num) + - (qp_num / NTB_NUM_MW * tx_size)); tx_size -= sizeof(struct ntb_rx_info); + qp->rx_info = (void *)((char *)qp->tx_mw + tx_size); - qp->tx_mw = qp->rx_info + 1; /* Due to house-keeping, there must be at least 2 buffs */ qp->tx_max_frame = min(transport_mtu + sizeof(struct ntb_payload_header), tx_size / 2); @@ -576,7 +679,8 @@ ntb_transport_init_queue(struct ntb_netdev *nt, unsigned int qp_num) mtx_init(&qp->ntb_rx_pend_q_lock, "ntb rx pend q", NULL, MTX_SPIN); mtx_init(&qp->ntb_rx_free_q_lock, "ntb rx free q", NULL, MTX_SPIN); mtx_init(&qp->ntb_tx_free_q_lock, "ntb tx free q", NULL, MTX_SPIN); - TASK_INIT(&qp->rx_completion_task, 0, ntb_rx_completion_task, qp); + TASK_INIT(&qp->rx_completion_task, 0, ntb_complete_rxc, qp); + TASK_INIT(&qp->rxc_db_work, 0, ntb_transport_rxc_db, qp); STAILQ_INIT(&qp->rx_pend_q); STAILQ_INIT(&qp->rx_free_q); @@ -593,7 +697,14 @@ ntb_transport_free_queue(struct ntb_transport_qp *qp) callout_drain(&qp->link_work); - ntb_unregister_db_callback(qp->ntb, qp->qp_num); + ntb_db_set_mask(qp->ntb, 1ull << qp->qp_num); + taskqueue_drain(taskqueue_swi, &qp->rxc_db_work); + taskqueue_drain(taskqueue_swi, &qp->rx_completion_task); + + qp->cb_data = NULL; + qp->rx_handler = NULL; + qp->tx_handler = NULL; + qp->event_handler = NULL; while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q))) free(entry, M_NTB_IF); @@ -604,7 +715,7 @@ ntb_transport_free_queue(struct ntb_transport_qp *qp) while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q))) free(entry, M_NTB_IF); - set_bit(qp->qp_num, &qp->transport->qp_bitmap); + set_bit(qp->qp_num, &qp->transport->qp_bitmap_free); } /** @@ -622,37 +733,34 @@ ntb_transport_free_queue(struct ntb_transport_qp *qp) * RETURNS: pointer to newly created ntb_queue, NULL on error. */ static struct ntb_transport_qp * -ntb_transport_create_queue(void *data, struct ntb_softc *pdev, +ntb_transport_create_queue(void *data, struct ntb_softc *ntb, const struct ntb_queue_handlers *handlers) { struct ntb_queue_entry *entry; struct ntb_transport_qp *qp; - struct ntb_netdev *nt; + struct ntb_transport_ctx *nt; unsigned int free_queue; - int rc, i; + int i; - nt = ntb_find_transport(pdev); - if (nt == NULL) - goto err; + nt = ntb_get_ctx(ntb, NULL); + KASSERT(nt != NULL, ("bogus")); - free_queue = ffs(nt->qp_bitmap); + free_queue = ffs_bit(&nt->qp_bitmap); if (free_queue == 0) - goto err; + return (NULL); /* decrement free_queue to make it zero based */ free_queue--; - clear_bit(free_queue, &nt->qp_bitmap); - - qp = &nt->qps[free_queue]; + qp = &nt->qp_vec[free_queue]; + clear_bit(1ull << qp->qp_num, &nt->qp_bitmap_free); qp->cb_data = data; qp->rx_handler = handlers->rx_handler; qp->tx_handler = handlers->tx_handler; qp->event_handler = handlers->event_handler; for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) { - entry = malloc(sizeof(struct ntb_queue_entry), M_NTB_IF, - M_WAITOK|M_ZERO); + entry = malloc(sizeof(*entry), M_NTB_IF, M_WAITOK | M_ZERO); entry->cb_data = nt->ifp; entry->buf = NULL; entry->len = transport_mtu; @@ -660,26 +768,13 @@ ntb_transport_create_queue(void *data, struct ntb_softc *pdev, } for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) { - entry = malloc(sizeof(struct ntb_queue_entry), M_NTB_IF, - M_WAITOK|M_ZERO); + entry = malloc(sizeof(*entry), M_NTB_IF, M_WAITOK | M_ZERO); ntb_list_add(&qp->ntb_tx_free_q_lock, entry, &qp->tx_free_q); } - rc = ntb_register_db_callback(qp->ntb, free_queue, qp, - ntb_transport_rxc_db); - if (rc != 0) - goto err1; - + ntb_db_clear(ntb, 1ull << qp->qp_num); + ntb_db_clear_mask(ntb, 1ull << qp->qp_num); return (qp); - -err1: - while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q))) - free(entry, M_NTB_IF); - while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q))) - free(entry, M_NTB_IF); - set_bit(free_queue, &nt->qp_bitmap); -err: - return (NULL); } /** @@ -695,11 +790,11 @@ ntb_transport_link_up(struct ntb_transport_qp *qp) if (qp == NULL) return; - qp->client_ready = NTB_LINK_UP; + qp->client_ready = true; if (bootverbose) device_printf(ntb_get_device(qp->ntb), "qp client ready\n"); - if (qp->transport->transport_link == NTB_LINK_UP) + if (qp->transport->link_is_up) callout_reset(&qp->link_work, 0, ntb_qp_link_work, qp); } @@ -727,7 +822,7 @@ ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data, struct ntb_queue_entry *entry; int rc; - if (qp == NULL || qp->qp_link != NTB_LINK_UP || len == 0) { + if (qp == NULL || !qp->link_is_up || len == 0) { CTR0(KTR_NTB, "TX: link not up"); return (EINVAL); } @@ -781,7 +876,7 @@ ntb_process_tx(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry) return (0); } CTR2(KTR_NTB, "TX: copying entry %p to offset %p", entry, offset); - ntb_tx_copy_task(qp, entry, offset); + ntb_memcpy_tx(qp, entry, offset); qp->tx_index++; qp->tx_index %= qp->tx_max_entry; @@ -792,26 +887,37 @@ ntb_process_tx(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry) } static void -ntb_tx_copy_task(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry, +ntb_memcpy_tx(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry, void *offset) { struct ntb_payload_header *hdr; - CTR2(KTR_NTB, "TX: copying %d bytes to offset %p", entry->len, offset); - if (entry->buf != NULL) - m_copydata((struct mbuf *)entry->buf, 0, entry->len, offset); - + /* This piece is from Linux' ntb_async_tx() */ hdr = (struct ntb_payload_header *)((char *)offset + qp->tx_max_frame - sizeof(struct ntb_payload_header)); + entry->x_hdr = hdr; hdr->len = entry->len; /* TODO: replace with bus_space_write */ hdr->ver = qp->tx_pkts; /* TODO: replace with bus_space_write */ - wmb(); + + /* This piece is ntb_memcpy_tx() */ + CTR2(KTR_NTB, "TX: copying %d bytes to offset %p", entry->len, offset); + if (entry->buf != NULL) { + m_copydata((struct mbuf *)entry->buf, 0, entry->len, offset); + + /* + * Ensure that the data is fully copied before setting the + * flags + */ + wmb(); + } + + /* The rest is ntb_tx_copy_callback() */ /* TODO: replace with bus_space_write */ hdr->flags = entry->flags | IF_NTB_DESC_DONE_FLAG; - ntb_ring_doorbell(qp->ntb, qp->qp_num); + ntb_peer_db_set(qp->ntb, 1ull << qp->qp_num); - /* + /* * The entry length can only be zero if the packet is intended to be a * "link down" or similar. Since no payload is being sent in these * cases, there is nothing to add to the completion queue. @@ -839,34 +945,27 @@ ntb_qp_full(void *arg) } /* Transport Rx */ -static void -ntb_transport_rxc_db(void *data, int db_num) -{ - struct ntb_transport_qp *qp = data; - - ntb_transport_rx(qp); -} - static void ntb_rx_pendq_full(void *arg) { CTR0(KTR_NTB, "RX: ntb_rx_pendq_full callout"); - ntb_transport_rx(arg); + ntb_transport_rxc_db(arg, 0); } static void -ntb_transport_rx(struct ntb_transport_qp *qp) +ntb_transport_rxc_db(void *arg, int pending __unused) { + struct ntb_transport_qp *qp = arg; uint64_t i; int rc; - /* + /* * Limit the number of packets processed in a single interrupt to * provide fairness to others */ - mtx_lock(&qp->transport->rx_lock); CTR0(KTR_NTB, "RX: transport_rx"); + mtx_lock(&qp->transport->rx_lock); for (i = 0; i < qp->rx_max_entry; i++) { rc = ntb_process_rxc(qp); if (rc != 0) { @@ -875,6 +974,21 @@ ntb_transport_rx(struct ntb_transport_qp *qp) } } mtx_unlock(&qp->transport->rx_lock); + + if (i == qp->rx_max_entry) + taskqueue_enqueue(taskqueue_swi, &qp->rxc_db_work); + else if ((ntb_db_read(qp->ntb) & (1ull << qp->qp_num)) != 0) { + /* If db is set, clear it and read it back to commit clear. */ + ntb_db_clear(qp->ntb, 1ull << qp->qp_num); + (void)ntb_db_read(qp->ntb); + + /* + * An interrupt may have arrived between finishing + * ntb_process_rxc and clearing the doorbell bit: there might + * be some more work to do. + */ + taskqueue_enqueue(taskqueue_swi, &qp->rxc_db_work); + } } static int @@ -891,66 +1005,61 @@ ntb_process_rxc(struct ntb_transport_qp *qp) sizeof(struct ntb_payload_header)); CTR1(KTR_NTB, "RX: process_rxc rx_index = %u", qp->rx_index); - entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q); - if (entry == NULL) { - qp->rx_err_no_buf++; - CTR0(KTR_NTB, "RX: No entries in rx_pend_q"); - return (ENOMEM); - } - callout_stop(&qp->rx_full); - CTR1(KTR_NTB, "RX: rx entry %p from rx_pend_q", entry); - if ((hdr->flags & IF_NTB_DESC_DONE_FLAG) == 0) { - CTR1(KTR_NTB, - "RX: hdr not done. Returning entry %p to rx_pend_q", entry); - ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q); + CTR0(KTR_NTB, "RX: hdr not done"); qp->rx_ring_empty++; return (EAGAIN); } - if (hdr->ver != (uint32_t) qp->rx_pkts) { - CTR3(KTR_NTB,"RX: ver != rx_pkts (%x != %lx). " - "Returning entry %p to rx_pend_q", hdr->ver, qp->rx_pkts, - entry); - ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q); + if ((hdr->flags & IF_NTB_LINK_DOWN_FLAG) != 0) { + CTR0(KTR_NTB, "RX: link down"); + ntb_qp_link_down(qp); + hdr->flags = 0; + return (EAGAIN); + } + + if (hdr->ver != (uint32_t)qp->rx_pkts) { + CTR2(KTR_NTB,"RX: ver != rx_pkts (%x != %lx). " + "Returning entry %p to rx_pend_q", hdr->ver, qp->rx_pkts); qp->rx_err_ver++; return (EIO); } - if ((hdr->flags & IF_NTB_LINK_DOWN_FLAG) != 0) { - ntb_qp_link_down(qp); - CTR1(KTR_NTB, - "RX: link down. adding entry %p back to rx_pend_q", entry); - ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q); - goto out; + entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q); + if (entry == NULL) { + qp->rx_err_no_buf++; + CTR0(KTR_NTB, "RX: No entries in rx_pend_q"); + return (EAGAIN); } + callout_stop(&qp->rx_full); + CTR1(KTR_NTB, "RX: rx entry %p from rx_pend_q", entry); - if (hdr->len <= entry->len) { - entry->len = hdr->len; - ntb_rx_copy_task(qp, entry, offset); - } else { - CTR1(KTR_NTB, - "RX: len too long. Returning entry %p to rx_pend_q", entry); - ntb_list_add(&qp->ntb_rx_pend_q_lock, entry, &qp->rx_pend_q); + entry->x_hdr = hdr; + entry->index = qp->rx_index; + if (hdr->len > entry->len) { + CTR2(KTR_NTB, "RX: len too long. Wanted %ju got %ju", + (uintmax_t)hdr->len, (uintmax_t)entry->len); qp->rx_err_oflow++; + + entry->len = -EIO; + entry->flags |= IF_NTB_DESC_DONE_FLAG; + + ntb_list_add(&qp->ntb_rx_free_q_lock, entry, &qp->rx_free_q); + taskqueue_enqueue(taskqueue_swi, &qp->rx_completion_task); + } else { + qp->rx_bytes += hdr->len; + qp->rx_pkts++; + + CTR1(KTR_NTB, "RX: received %ld rx_pkts", qp->rx_pkts); + + entry->len = hdr->len; + + ntb_rx_copy_task(qp, entry, offset); } - qp->rx_bytes += hdr->len; - qp->rx_pkts++; - CTR1(KTR_NTB, "RX: received %ld rx_pkts", qp->rx_pkts); - - -out: - /* Ensure that the data is globally visible before clearing the flag */ - wmb(); - hdr->flags = 0; - /* TODO: replace with bus_space_write */ - qp->rx_info->entry = qp->rx_index; - qp->rx_index++; qp->rx_index %= qp->rx_max_entry; - return (0); } @@ -968,6 +1077,12 @@ ntb_rx_copy_task(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry, entry->buf = (void *)m; + /* Ensure that the data is globally visible before clearing the flag */ + wmb(); + entry->x_hdr->flags = 0; + /* TODO: replace with bus_space_write */ + qp->rx_info->entry = qp->rx_index; + CTR2(KTR_NTB, "RX: copied entry %p to mbuf %p. Adding entry to rx_free_q", entry, m); @@ -977,7 +1092,7 @@ ntb_rx_copy_task(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry, } static void -ntb_rx_completion_task(void *arg, int pending) +ntb_complete_rxc(void *arg, int pending) { struct ntb_transport_qp *qp = arg; struct mbuf *m; @@ -988,7 +1103,7 @@ ntb_rx_completion_task(void *arg, int pending) while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q))) { m = entry->buf; CTR2(KTR_NTB, "RX: completing entry %p, mbuf %p", entry, m); - if (qp->rx_handler && qp->client_ready == NTB_LINK_UP) + if (qp->rx_handler && qp->client_ready) qp->rx_handler(qp, qp->cb_data, m, entry->len); entry->buf = NULL; @@ -1006,25 +1121,52 @@ ntb_rx_completion_task(void *arg, int pending) } } +static void +ntb_transport_doorbell_callback(void *data, int vector) +{ + struct ntb_transport_ctx *nt = data; + struct ntb_transport_qp *qp; + struct _qpset db_bits; + uint64_t vec_mask; + unsigned qp_num; + + BIT_COPY(QP_SETSIZE, &nt->qp_bitmap, &db_bits); + BIT_NAND(QP_SETSIZE, &db_bits, &nt->qp_bitmap_free); + + vec_mask = ntb_db_vector_mask(nt->ntb, vector); + while (vec_mask != 0) { + qp_num = ffsl(vec_mask); + /* i386 doesn't have ffsll(), fake it */ + if (qp_num == 0) { + qp_num = ffsl(vec_mask >> 32); + KASSERT(qp_num != 0, ("ffs")); + qp_num += 32; + } + qp_num--; + + if (test_bit(qp_num, &db_bits)) { + qp = &nt->qp_vec[qp_num]; + taskqueue_enqueue(taskqueue_swi, &qp->rxc_db_work); + } + + vec_mask &= ~(1ull << qp_num); + } +} + /* Link Event handler */ static void -ntb_transport_event_callback(void *data, enum ntb_hw_event event) +ntb_transport_event_callback(void *data) { - struct ntb_netdev *nt = data; + struct ntb_transport_ctx *nt = data; - switch (event) { - case NTB_EVENT_HW_LINK_UP: + if (ntb_link_is_up(nt->ntb, NULL, NULL)) { if (bootverbose) device_printf(ntb_get_device(nt->ntb), "HW link up\n"); callout_reset(&nt->link_work, 0, ntb_transport_link_work, nt); - break; - case NTB_EVENT_HW_LINK_DOWN: + } else { if (bootverbose) device_printf(ntb_get_device(nt->ntb), "HW link down\n"); ntb_transport_link_cleanup(nt); - break; - default: - panic("ntb: Unknown NTB event"); } } @@ -1032,78 +1174,51 @@ ntb_transport_event_callback(void *data, enum ntb_hw_event event) static void ntb_transport_link_work(void *arg) { - struct ntb_netdev *nt = arg; + struct ntb_transport_ctx *nt = arg; struct ntb_softc *ntb = nt->ntb; struct ntb_transport_qp *qp; - uint64_t val64; - uint32_t val, i, num_mw; + uint64_t val64, size; + uint32_t val; + unsigned i; int rc; - if (ntb_has_feature(ntb, NTB_REGS_THRU_MW)) - num_mw = NTB_NUM_MW - 1; - else - num_mw = NTB_NUM_MW; - /* send the local info, in the opposite order of the way we read it */ - for (i = 0; i < num_mw; i++) { - rc = ntb_write_remote_spad(ntb, IF_NTB_MW0_SZ_HIGH + (i * 2), - (uint64_t)ntb_get_mw_size(ntb, i) >> 32); - if (rc != 0) - goto out; + for (i = 0; i < nt->mw_count; i++) { + size = nt->mw_vec[i].phys_size; - rc = ntb_write_remote_spad(ntb, IF_NTB_MW0_SZ_LOW + (i * 2), - (uint32_t)ntb_get_mw_size(ntb, i)); - if (rc != 0) - goto out; + if (max_mw_size != 0 && size > max_mw_size) + size = max_mw_size; + + ntb_peer_spad_write(ntb, IF_NTB_MW0_SZ_HIGH + (i * 2), + size >> 32); + ntb_peer_spad_write(ntb, IF_NTB_MW0_SZ_LOW + (i * 2), size); } - rc = ntb_write_remote_spad(ntb, IF_NTB_NUM_MWS, num_mw); - if (rc != 0) - goto out; + ntb_peer_spad_write(ntb, IF_NTB_NUM_MWS, nt->mw_count); - rc = ntb_write_remote_spad(ntb, IF_NTB_NUM_QPS, nt->max_qps); - if (rc != 0) - goto out; + ntb_peer_spad_write(ntb, IF_NTB_NUM_QPS, nt->qp_count); - rc = ntb_write_remote_spad(ntb, IF_NTB_VERSION, NTB_TRANSPORT_VERSION); - if (rc != 0) - goto out; + ntb_peer_spad_write(ntb, IF_NTB_VERSION, NTB_TRANSPORT_VERSION); /* Query the remote side for its info */ - rc = ntb_read_local_spad(ntb, IF_NTB_VERSION, &val); - if (rc != 0) - goto out; - + val = 0; + ntb_spad_read(ntb, IF_NTB_VERSION, &val); if (val != NTB_TRANSPORT_VERSION) goto out; - rc = ntb_read_local_spad(ntb, IF_NTB_NUM_QPS, &val); - if (rc != 0) + ntb_spad_read(ntb, IF_NTB_NUM_QPS, &val); + if (val != nt->qp_count) goto out; - if (val != nt->max_qps) + ntb_spad_read(ntb, IF_NTB_NUM_MWS, &val); + if (val != nt->mw_count) goto out; - rc = ntb_read_local_spad(ntb, IF_NTB_NUM_MWS, &val); - if (rc != 0) - goto out; - - if (val != num_mw) - goto out; - - for (i = 0; i < num_mw; i++) { - rc = ntb_read_local_spad(ntb, IF_NTB_MW0_SZ_HIGH + (i * 2), - &val); - if (rc != 0) - goto free_mws; - + for (i = 0; i < nt->mw_count; i++) { + ntb_spad_read(ntb, IF_NTB_MW0_SZ_HIGH + (i * 2), &val); val64 = (uint64_t)val << 32; - rc = ntb_read_local_spad(ntb, IF_NTB_MW0_SZ_LOW + (i * 2), - &val); - if (rc != 0) - goto free_mws; - + ntb_spad_read(ntb, IF_NTB_MW0_SZ_LOW + (i * 2), &val); val64 |= val; rc = ntb_set_mw(nt, i, val64); @@ -1111,94 +1226,131 @@ ntb_transport_link_work(void *arg) goto free_mws; } - nt->transport_link = NTB_LINK_UP; + nt->link_is_up = true; if (bootverbose) device_printf(ntb_get_device(ntb), "transport link up\n"); - for (i = 0; i < nt->max_qps; i++) { - qp = &nt->qps[i]; + for (i = 0; i < nt->qp_count; i++) { + qp = &nt->qp_vec[i]; ntb_transport_setup_qp_mw(nt, i); - if (qp->client_ready == NTB_LINK_UP) + if (qp->client_ready) callout_reset(&qp->link_work, 0, ntb_qp_link_work, qp); } return; free_mws: - for (i = 0; i < NTB_NUM_MW; i++) + for (i = 0; i < nt->mw_count; i++) ntb_free_mw(nt, i); out: - if (ntb_query_link_status(ntb)) + if (ntb_link_is_up(ntb, NULL, NULL)) callout_reset(&nt->link_work, NTB_LINK_DOWN_TIMEOUT * hz / 1000, ntb_transport_link_work, nt); } static int -ntb_set_mw(struct ntb_netdev *nt, int num_mw, unsigned int size) +ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, unsigned size) { - struct ntb_transport_mw *mw = &nt->mw[num_mw]; + struct ntb_transport_mw *mw = &nt->mw_vec[num_mw]; + unsigned xlat_size, buff_size; + int rc; + + xlat_size = roundup(size, mw->xlat_align_size); + buff_size = roundup(size, mw->xlat_align); /* No need to re-setup */ - if (mw->size == size) + if (mw->xlat_size == xlat_size) return (0); - if (mw->size != 0) + if (mw->buff_size != 0) ntb_free_mw(nt, num_mw); - /* Alloc memory for receiving data. Must be 4k aligned */ - mw->size = size; + /* Alloc memory for receiving data. Must be aligned */ + mw->xlat_size = xlat_size; + mw->buff_size = buff_size; - mw->virt_addr = contigmalloc(mw->size, M_NTB_IF, M_ZERO, 0, - BUS_SPACE_MAXADDR, mw->size, 0); + mw->virt_addr = contigmalloc(mw->buff_size, M_NTB_IF, M_ZERO, 0, + BUS_SPACE_MAXADDR, mw->xlat_align, 0); if (mw->virt_addr == NULL) { - mw->size = 0; - printf("ntb: Unable to allocate MW buffer of size %d\n", - (int)mw->size); + mw->xlat_size = 0; + mw->buff_size = 0; + printf("ntb: Unable to allocate MW buffer of size %zu\n", + mw->xlat_size); return (ENOMEM); } /* TODO: replace with bus_space_* functions */ mw->dma_addr = vtophys(mw->virt_addr); + /* + * Ensure that the allocation from contigmalloc is aligned as + * requested. XXX: This may not be needed -- brought in for parity + * with the Linux driver. + */ + if (mw->dma_addr % mw->xlat_align != 0) { + device_printf(ntb_get_device(nt->ntb), + "DMA memory 0x%jx not aligned to BAR size 0x%x\n", + (uintmax_t)mw->dma_addr, size); + ntb_free_mw(nt, num_mw); + return (ENOMEM); + } + /* Notify HW the memory location of the receive buffer */ - ntb_set_mw_addr(nt->ntb, num_mw, mw->dma_addr); + rc = ntb_mw_set_trans(nt->ntb, num_mw, mw->dma_addr, mw->xlat_size); + if (rc) { + device_printf(ntb_get_device(nt->ntb), + "Unable to set mw%d translation", num_mw); + ntb_free_mw(nt, num_mw); + return (rc); + } return (0); } static void -ntb_free_mw(struct ntb_netdev *nt, int num_mw) +ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw) { - struct ntb_transport_mw *mw = &nt->mw[num_mw]; + struct ntb_transport_mw *mw = &nt->mw_vec[num_mw]; if (mw->virt_addr == NULL) return; - contigfree(mw->virt_addr, mw->size, M_NTB_IF); + ntb_mw_clear_trans(nt->ntb, num_mw); + contigfree(mw->virt_addr, mw->xlat_size, M_NTB_IF); + mw->xlat_size = 0; + mw->buff_size = 0; mw->virt_addr = NULL; } -static void -ntb_transport_setup_qp_mw(struct ntb_netdev *nt, unsigned int qp_num) +static int +ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt, unsigned int qp_num) { - struct ntb_transport_qp *qp = &nt->qps[qp_num]; + struct ntb_transport_qp *qp = &nt->qp_vec[qp_num]; + struct ntb_transport_mw *mw; void *offset; - unsigned int rx_size, num_qps_mw; - uint8_t mw_num = QP_TO_MW(qp_num); - unsigned int i; + uint64_t i; + size_t rx_size; + unsigned num_qps_mw, mw_num, mw_count; - if (nt->max_qps % NTB_NUM_MW && mw_num < nt->max_qps % NTB_NUM_MW) - num_qps_mw = nt->max_qps / NTB_NUM_MW + 1; + mw_count = nt->mw_count; + mw_num = QP_TO_MW(nt, qp_num); + mw = &nt->mw_vec[mw_num]; + + if (mw->virt_addr == NULL) + return (ENOMEM); + + if (nt->qp_count % mw_count && mw_num + 1 < nt->qp_count / mw_count) + num_qps_mw = nt->qp_count / mw_count + 1; else - num_qps_mw = nt->max_qps / NTB_NUM_MW; + num_qps_mw = nt->qp_count / mw_count; - rx_size = (unsigned int) nt->mw[mw_num].size / num_qps_mw; - qp->remote_rx_info = (void *)((uint8_t *)nt->mw[mw_num].virt_addr + - (qp_num / NTB_NUM_MW * rx_size)); + rx_size = mw->xlat_size / num_qps_mw; + qp->rx_buff = (char *)mw->virt_addr + rx_size * qp_num / mw_count; rx_size -= sizeof(struct ntb_rx_info); - qp->rx_buff = qp->remote_rx_info + 1; + qp->remote_rx_info = (void*)((char *)qp->rx_buff + rx_size); + /* Due to house-keeping, there must be at least 2 buffs */ qp->rx_max_frame = min(transport_mtu + sizeof(struct ntb_payload_header), rx_size / 2); @@ -1207,7 +1359,7 @@ ntb_transport_setup_qp_mw(struct ntb_netdev *nt, unsigned int qp_num) qp->remote_rx_info->entry = qp->rx_max_entry - 1; - /* setup the hdr offsets with 0's */ + /* Set up the hdr offsets with 0s */ for (i = 0; i < qp->rx_max_entry; i++) { offset = (void *)((uint8_t *)qp->rx_buff + qp->rx_max_frame * (i + 1) - @@ -1218,6 +1370,8 @@ ntb_transport_setup_qp_mw(struct ntb_netdev *nt, unsigned int qp_num) qp->rx_pkts = 0; qp->tx_pkts = 0; qp->tx_index = 0; + + return (0); } static void @@ -1225,55 +1379,60 @@ ntb_qp_link_work(void *arg) { struct ntb_transport_qp *qp = arg; struct ntb_softc *ntb = qp->ntb; - struct ntb_netdev *nt = qp->transport; - int rc, val; + struct ntb_transport_ctx *nt = qp->transport; + int val; + ntb_spad_read(ntb, IF_NTB_QP_LINKS, &val); - rc = ntb_read_remote_spad(ntb, IF_NTB_QP_LINKS, &val); - if (rc != 0) - return; - - rc = ntb_write_remote_spad(ntb, IF_NTB_QP_LINKS, val | 1 << qp->qp_num); + ntb_peer_spad_write(ntb, IF_NTB_QP_LINKS, val | (1ull << qp->qp_num)); /* query remote spad for qp ready bits */ - rc = ntb_read_local_spad(ntb, IF_NTB_QP_LINKS, &val); + ntb_peer_spad_read(ntb, IF_NTB_QP_LINKS, &val); /* See if the remote side is up */ - if ((1 << qp->qp_num & val) != 0) { - qp->qp_link = NTB_LINK_UP; - if (qp->event_handler != NULL) - qp->event_handler(qp->cb_data, NTB_LINK_UP); + if ((val & (1ull << qp->qp_num)) != 0) { if (bootverbose) device_printf(ntb_get_device(ntb), "qp link up\n"); - } else if (nt->transport_link == NTB_LINK_UP) { + qp->link_is_up = true; + + if (qp->event_handler != NULL) + qp->event_handler(qp->cb_data, NTB_LINK_UP); + + taskqueue_enqueue(taskqueue_swi, &qp->rxc_db_work); + } else if (nt->link_is_up) callout_reset(&qp->link_work, NTB_LINK_DOWN_TIMEOUT * hz / 1000, ntb_qp_link_work, qp); - } } /* Link down event*/ static void -ntb_transport_link_cleanup(struct ntb_netdev *nt) +ntb_transport_link_cleanup(struct ntb_transport_ctx *nt) { - int i; + struct ntb_transport_qp *qp; + struct _qpset qp_bitmap_alloc; + unsigned i; - if (nt->transport_link == NTB_LINK_DOWN) - callout_drain(&nt->link_work); - else - nt->transport_link = NTB_LINK_DOWN; + BIT_COPY(QP_SETSIZE, &nt->qp_bitmap, &qp_bitmap_alloc); + BIT_NAND(QP_SETSIZE, &qp_bitmap_alloc, &nt->qp_bitmap_free); /* Pass along the info to any clients */ - for (i = 0; i < nt->max_qps; i++) - if (!test_bit(i, &nt->qp_bitmap)) - ntb_qp_link_down(&nt->qps[i]); + for (i = 0; i < nt->qp_count; i++) + if (test_bit(i, &qp_bitmap_alloc)) { + qp = &nt->qp_vec[i]; + ntb_qp_link_cleanup(qp); + callout_drain(&qp->link_work); + } - /* + if (!nt->link_is_up) + callout_drain(&nt->link_work); + + /* * The scratchpad registers keep the values if the remote side * goes down, blast them now to give them a sane value the next * time they are accessed */ for (i = 0; i < IF_NTB_MAX_SPAD; i++) - ntb_write_local_spad(nt->ntb, i, 0); + ntb_spad_write(nt->ntb, i, 0); } @@ -1287,19 +1446,19 @@ ntb_qp_link_down(struct ntb_transport_qp *qp) static void ntb_qp_link_cleanup(struct ntb_transport_qp *qp) { - struct ntb_netdev *nt = qp->transport; + struct ntb_transport_ctx *nt = qp->transport; - if (qp->qp_link == NTB_LINK_DOWN) { + if (!qp->link_is_up) { callout_drain(&qp->link_work); return; } + qp->link_is_up = false; + if (qp->event_handler != NULL) qp->event_handler(qp->cb_data, NTB_LINK_DOWN); - qp->qp_link = NTB_LINK_DOWN; - - if (nt->transport_link == NTB_LINK_UP) + if (nt->link_is_up) callout_reset(&qp->link_work, NTB_LINK_DOWN_TIMEOUT * hz / 1000, ntb_qp_link_work, qp); } @@ -1316,25 +1475,22 @@ ntb_qp_link_cleanup(struct ntb_transport_qp *qp) static void ntb_transport_link_down(struct ntb_transport_qp *qp) { - int rc, val; + uint32_t val; if (qp == NULL) return; - qp->client_ready = NTB_LINK_DOWN; + qp->client_ready = false; - rc = ntb_read_remote_spad(qp->ntb, IF_NTB_QP_LINKS, &val); - if (rc != 0) - return; + ntb_spad_read(qp->ntb, IF_NTB_QP_LINKS, &val); - rc = ntb_write_remote_spad(qp->ntb, IF_NTB_QP_LINKS, + ntb_peer_spad_write(qp->ntb, IF_NTB_QP_LINKS, val & ~(1 << qp->qp_num)); - if (qp->qp_link == NTB_LINK_UP) + if (qp->link_is_up) ntb_send_link_down(qp); else callout_drain(&qp->link_work); - } static void @@ -1343,10 +1499,10 @@ ntb_send_link_down(struct ntb_transport_qp *qp) struct ntb_queue_entry *entry; int i, rc; - if (qp->qp_link == NTB_LINK_DOWN) + if (!qp->link_is_up) return; - qp->qp_link = NTB_LINK_DOWN; + qp->link_is_up = false; for (i = 0; i < NTB_LINK_DOWN_TIMEOUT; i++) { entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q); diff --git a/sys/dev/ntb/ntb_hw/ntb_hw.c b/sys/dev/ntb/ntb_hw/ntb_hw.c index 66d0d744309..9a3e189b974 100644 --- a/sys/dev/ntb/ntb_hw/ntb_hw.c +++ b/sys/dev/ntb/ntb_hw/ntb_hw.c @@ -1,5 +1,6 @@ /*- * Copyright (C) 2013 Intel Corporation + * Copyright (C) 2015 EMC Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -59,16 +60,10 @@ __FBSDID("$FreeBSD$"); * be picked up and redistributed in Linux with a dual GPL/BSD license. */ -#define NTB_CONFIG_BAR 0 -#define NTB_B2B_BAR_1 1 -#define NTB_B2B_BAR_2 2 -#define NTB_MAX_BARS 3 -#define NTB_MW_TO_BAR(mw) ((mw) + 1) +#define MAX_MSIX_INTERRUPTS MAX(XEON_DB_COUNT, SOC_DB_COUNT) -#define MAX_MSIX_INTERRUPTS MAX(XEON_MAX_DB_BITS, SOC_MAX_DB_BITS) - -#define NTB_HB_TIMEOUT 1 /* second */ -#define SOC_LINK_RECOVERY_TIME 500 +#define NTB_HB_TIMEOUT 1 /* second */ +#define SOC_LINK_RECOVERY_TIME 500 /* ms */ #define DEVICE2SOFTC(dev) ((struct ntb_softc *) device_get_softc(dev)) @@ -77,6 +72,14 @@ enum ntb_device_type { NTB_SOC }; +enum ntb_bar { + NTB_CONFIG_BAR = 0, + NTB_B2B_BAR_1, + NTB_B2B_BAR_2, + NTB_B2B_BAR_3, + NTB_MAX_BARS +}; + /* Device features and workarounds */ #define HAS_FEATURE(feature) \ ((ntb->features & (feature)) != 0) @@ -85,7 +88,7 @@ struct ntb_hw_info { uint32_t device_id; const char *desc; enum ntb_device_type type; - uint64_t features; + uint32_t features; }; struct ntb_pci_bar_info { @@ -96,6 +99,11 @@ struct ntb_pci_bar_info { vm_paddr_t pbase; void *vbase; u_long size; + + /* Configuration register offsets */ + uint32_t psz_off; + uint32_t ssz_off; + uint32_t pbarxlat_off; }; struct ntb_int_info { @@ -104,11 +112,45 @@ struct ntb_int_info { void *tag; }; -struct ntb_db_cb { - ntb_db_callback callback; - unsigned int db_num; - void *data; +struct ntb_vec { struct ntb_softc *ntb; + uint32_t num; +}; + +struct ntb_reg { + uint32_t ntb_ctl; + uint32_t lnk_sta; + uint8_t db_size; + unsigned mw_bar[NTB_MAX_BARS]; +}; + +struct ntb_alt_reg { + uint32_t db_bell; + uint32_t db_mask; + uint32_t spad; +}; + +struct ntb_xlat_reg { + uint32_t bar0_base; + uint32_t bar2_base; + uint32_t bar4_base; + uint32_t bar5_base; + + uint32_t bar2_xlat; + uint32_t bar4_xlat; + uint32_t bar5_xlat; + + uint32_t bar2_limit; + uint32_t bar4_limit; + uint32_t bar5_limit; +}; + +struct ntb_b2b_addr { + uint64_t bar0_addr; + uint64_t bar2_addr64; + uint64_t bar4_addr64; + uint64_t bar4_addr32; + uint64_t bar5_addr32; }; struct ntb_softc { @@ -123,33 +165,59 @@ struct ntb_softc { struct callout heartbeat_timer; struct callout lr_timer; - void *ntb_transport; - ntb_event_callback event_cb; - struct ntb_db_cb *db_cb; + void *ntb_ctx; + const struct ntb_ctx_ops *ctx_ops; + struct ntb_vec *msix_vec; +#define CTX_LOCK(sc) mtx_lock_spin(&(sc)->ctx_lock) +#define CTX_UNLOCK(sc) mtx_unlock_spin(&(sc)->ctx_lock) +#define CTX_ASSERT(sc,f) mtx_assert(&(sc)->ctx_lock, (f)) + struct mtx ctx_lock; - struct { - uint8_t max_spads; - uint8_t max_db_bits; - uint8_t msix_cnt; - } limits; struct { uint32_t ldb; uint32_t ldb_mask; - uint32_t rdb; - uint32_t bar2_xlat; uint32_t bar4_xlat; - uint32_t spad_remote; + uint32_t bar5_xlat; uint32_t spad_local; - uint32_t lnk_cntl; - uint32_t lnk_stat; uint32_t spci_cmd; } reg_ofs; + uint32_t ppd; uint8_t conn_type; uint8_t dev_type; - uint8_t bits_per_vector; - uint8_t link_status; uint8_t link_width; uint8_t link_speed; + + /* Offset of peer bar0 in B2B BAR */ + uint64_t b2b_off; + /* Memory window used to access peer bar0 */ +#define B2B_MW_DISABLED UINT8_MAX + uint8_t b2b_mw_idx; + + uint8_t mw_count; + uint8_t spad_count; + uint8_t db_count; + uint8_t db_vec_count; + uint8_t db_vec_shift; + + /* Protects local db_mask. */ +#define DB_MASK_LOCK(sc) mtx_lock_spin(&(sc)->db_mask_lock) +#define DB_MASK_UNLOCK(sc) mtx_unlock_spin(&(sc)->db_mask_lock) +#define DB_MASK_ASSERT(sc,f) mtx_assert(&(sc)->db_mask_lock, (f)) + struct mtx db_mask_lock; + + uint32_t ntb_ctl; + uint32_t lnk_sta; + + uint64_t db_valid_mask; + uint64_t db_link_mask; + uint64_t db_mask; + + int last_ts; /* ticks @ last irq */ + + const struct ntb_reg *reg; + const struct ntb_alt_reg *self_reg; + const struct ntb_alt_reg *peer_reg; + const struct ntb_xlat_reg *xlat_reg; }; #ifdef __i386__ @@ -181,41 +249,62 @@ bus_space_write_8(bus_space_tag_t tag, bus_space_handle_t handle, #define ntb_reg_read(SIZE, offset) ntb_bar_read(SIZE, NTB_CONFIG_BAR, offset) #define ntb_reg_write(SIZE, offset, val) \ ntb_bar_write(SIZE, NTB_CONFIG_BAR, offset, val) -#define ntb_mw_read(SIZE, offset) ntb_bar_read(SIZE, NTB_B2B_BAR_2, offset) +#define ntb_mw_read(SIZE, offset) \ + ntb_bar_read(SIZE, ntb_mw_to_bar(ntb, ntb->b2b_mw_idx), offset) #define ntb_mw_write(SIZE, offset, val) \ - ntb_bar_write(SIZE, NTB_B2B_BAR_2, offset, val) - -typedef int (*bar_map_strategy)(struct ntb_softc *ntb, - struct ntb_pci_bar_info *bar); + ntb_bar_write(SIZE, ntb_mw_to_bar(ntb, ntb->b2b_mw_idx), \ + offset, val) static int ntb_probe(device_t device); static int ntb_attach(device_t device); static int ntb_detach(device_t device); +static inline enum ntb_bar ntb_mw_to_bar(struct ntb_softc *, unsigned mw); +static inline bool bar_is_64bit(struct ntb_softc *, enum ntb_bar); +static inline void bar_get_xlat_params(struct ntb_softc *, enum ntb_bar, + uint32_t *base, uint32_t *xlat, uint32_t *lmt); static int ntb_map_pci_bars(struct ntb_softc *ntb); -static int map_pci_bar(struct ntb_softc *ntb, bar_map_strategy strategy, - struct ntb_pci_bar_info *bar); +static void print_map_success(struct ntb_softc *, struct ntb_pci_bar_info *); static int map_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar); static int map_memory_window_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar); static void ntb_unmap_pci_bar(struct ntb_softc *ntb); -static int ntb_setup_interrupts(struct ntb_softc *ntb); +static int ntb_remap_msix(device_t, uint32_t desired, uint32_t avail); +static int ntb_init_isr(struct ntb_softc *ntb); +static int ntb_setup_legacy_interrupt(struct ntb_softc *ntb); +static int ntb_setup_msix(struct ntb_softc *ntb, uint32_t num_vectors); static void ntb_teardown_interrupts(struct ntb_softc *ntb); -static void handle_soc_irq(void *arg); -static void handle_xeon_irq(void *arg); -static void handle_xeon_event_irq(void *arg); -static void ntb_handle_legacy_interrupt(void *arg); -static int ntb_create_callbacks(struct ntb_softc *ntb, int num_vectors); -static void ntb_free_callbacks(struct ntb_softc *ntb); +static inline uint64_t ntb_vec_mask(struct ntb_softc *, uint64_t db_vector); +static void ntb_interrupt(struct ntb_softc *, uint32_t vec); +static void ndev_vec_isr(void *arg); +static void ndev_irq_isr(void *arg); +static inline uint64_t db_ioread(struct ntb_softc *, uint64_t regoff); +static inline void db_iowrite(struct ntb_softc *, uint64_t regoff, uint64_t val); +static int ntb_create_msix_vec(struct ntb_softc *ntb, uint32_t num_vectors); +static void ntb_free_msix_vec(struct ntb_softc *ntb); static struct ntb_hw_info *ntb_get_device_info(uint32_t device_id); -static int ntb_initialize_hw(struct ntb_softc *ntb); -static int ntb_setup_xeon(struct ntb_softc *ntb); -static int ntb_setup_soc(struct ntb_softc *ntb); +static void ntb_detect_max_mw(struct ntb_softc *ntb); +static int ntb_detect_xeon(struct ntb_softc *ntb); +static int ntb_detect_soc(struct ntb_softc *ntb); +static int ntb_xeon_init_dev(struct ntb_softc *ntb); +static int ntb_soc_init_dev(struct ntb_softc *ntb); +static void ntb_teardown_xeon(struct ntb_softc *ntb); static void configure_soc_secondary_side_bars(struct ntb_softc *ntb); -static void configure_xeon_secondary_side_bars(struct ntb_softc *ntb); -static void ntb_handle_heartbeat(void *arg); -static void ntb_handle_link_event(struct ntb_softc *ntb, int link_state); +static void xeon_reset_sbar_size(struct ntb_softc *, enum ntb_bar idx, + enum ntb_bar regbar); +static void xeon_set_sbar_base_and_limit(struct ntb_softc *, + uint64_t base_addr, enum ntb_bar idx, enum ntb_bar regbar); +static void xeon_set_pbar_xlat(struct ntb_softc *, uint64_t base_addr, + enum ntb_bar idx); +static int xeon_setup_b2b_mw(struct ntb_softc *, + const struct ntb_b2b_addr *addr, const struct ntb_b2b_addr *peer_addr); +static inline bool link_is_up(struct ntb_softc *ntb); +static inline bool soc_link_is_err(struct ntb_softc *ntb); +static inline enum ntb_speed ntb_link_sta_speed(struct ntb_softc *); +static inline enum ntb_width ntb_link_sta_width(struct ntb_softc *); +static void soc_link_hb(void *arg); +static void ntb_db_event(struct ntb_softc *ntb, uint32_t vec); static void recover_soc_link(void *arg); -static int ntb_check_link_status(struct ntb_softc *ntb); +static bool ntb_poll_link(struct ntb_softc *ntb); static void save_bar_parameters(struct ntb_pci_bar_info *bar); static struct ntb_hw_info pci_ids[] = { @@ -223,22 +312,92 @@ static struct ntb_hw_info pci_ids[] = { /* XXX: PS/SS IDs left out until they are supported. */ { 0x37258086, "JSF Xeon C35xx/C55xx Non-Transparent Bridge B2B", - NTB_XEON, NTB_REGS_THRU_MW | NTB_B2BDOORBELL_BIT14 }, + NTB_XEON, NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 }, { 0x3C0D8086, "SNB Xeon E5/Core i7 Non-Transparent Bridge B2B", - NTB_XEON, NTB_REGS_THRU_MW | NTB_B2BDOORBELL_BIT14 }, + NTB_XEON, NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 }, { 0x0E0D8086, "IVT Xeon E5 V2 Non-Transparent Bridge B2B", NTB_XEON, - NTB_REGS_THRU_MW | NTB_B2BDOORBELL_BIT14 | NTB_SB01BASE_LOCKUP - | NTB_BAR_SIZE_4K }, + NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 | + NTB_SB01BASE_LOCKUP | NTB_BAR_SIZE_4K }, { 0x2F0D8086, "HSX Xeon E5 V3 Non-Transparent Bridge B2B", NTB_XEON, - NTB_REGS_THRU_MW | NTB_B2BDOORBELL_BIT14 | NTB_SB01BASE_LOCKUP - }, + NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 | + NTB_SB01BASE_LOCKUP }, { 0x6F0D8086, "BDX Xeon E5 V4 Non-Transparent Bridge B2B", NTB_XEON, - NTB_REGS_THRU_MW | NTB_B2BDOORBELL_BIT14 | NTB_SB01BASE_LOCKUP - }, + NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 | + NTB_SB01BASE_LOCKUP }, { 0x00000000, NULL, NTB_SOC, 0 } }; +static const struct ntb_reg soc_reg = { + .ntb_ctl = SOC_NTBCNTL_OFFSET, + .lnk_sta = SOC_LINK_STATUS_OFFSET, + .db_size = sizeof(uint64_t), + .mw_bar = { NTB_B2B_BAR_1, NTB_B2B_BAR_2 }, +}; + +static const struct ntb_alt_reg soc_b2b_reg = { + .db_bell = SOC_B2B_DOORBELL_OFFSET, + .spad = SOC_B2B_SPAD_OFFSET, +}; + +static const struct ntb_xlat_reg soc_sec_xlat = { +#if 0 + /* "FIXME" says the Linux driver. */ + .bar0_base = SOC_SBAR0BASE_OFFSET, + .bar2_base = SOC_SBAR2BASE_OFFSET, + .bar4_base = SOC_SBAR4BASE_OFFSET, + + .bar2_limit = SOC_SBAR2LMT_OFFSET, + .bar4_limit = SOC_SBAR4LMT_OFFSET, +#endif + + .bar2_xlat = SOC_SBAR2XLAT_OFFSET, + .bar4_xlat = SOC_SBAR4XLAT_OFFSET, +}; + +static const struct ntb_reg xeon_reg = { + .ntb_ctl = XEON_NTBCNTL_OFFSET, + .lnk_sta = XEON_LINK_STATUS_OFFSET, + .db_size = sizeof(uint16_t), + .mw_bar = { NTB_B2B_BAR_1, NTB_B2B_BAR_2, NTB_B2B_BAR_3 }, +}; + +static const struct ntb_alt_reg xeon_b2b_reg = { + .db_bell = XEON_B2B_DOORBELL_OFFSET, + .spad = XEON_B2B_SPAD_OFFSET, +}; + +static const struct ntb_xlat_reg xeon_sec_xlat = { + .bar0_base = XEON_SBAR0BASE_OFFSET, + .bar2_base = XEON_SBAR2BASE_OFFSET, + .bar4_base = XEON_SBAR4BASE_OFFSET, + .bar5_base = XEON_SBAR5BASE_OFFSET, + + .bar2_limit = XEON_SBAR2LMT_OFFSET, + .bar4_limit = XEON_SBAR4LMT_OFFSET, + .bar5_limit = XEON_SBAR5LMT_OFFSET, + + .bar2_xlat = XEON_SBAR2XLAT_OFFSET, + .bar4_xlat = XEON_SBAR4XLAT_OFFSET, + .bar5_xlat = XEON_SBAR5XLAT_OFFSET, +}; + +static const struct ntb_b2b_addr xeon_b2b_usd_addr = { + .bar0_addr = XEON_B2B_BAR0_USD_ADDR, + .bar2_addr64 = XEON_B2B_BAR2_USD_ADDR64, + .bar4_addr64 = XEON_B2B_BAR4_USD_ADDR64, + .bar4_addr32 = XEON_B2B_BAR4_USD_ADDR32, + .bar5_addr32 = XEON_B2B_BAR5_USD_ADDR32, +}; + +static const struct ntb_b2b_addr xeon_b2b_dsd_addr = { + .bar0_addr = XEON_B2B_BAR0_DSD_ADDR, + .bar2_addr64 = XEON_B2B_BAR2_DSD_ADDR64, + .bar4_addr64 = XEON_B2B_BAR4_DSD_ADDR64, + .bar4_addr32 = XEON_B2B_BAR4_DSD_ADDR32, + .bar5_addr32 = XEON_B2B_BAR5_DSD_ADDR32, +}; + /* * OS <-> Driver interface structures */ @@ -293,18 +452,33 @@ ntb_attach(device_t device) ntb->device = device; ntb->type = p->type; ntb->features = p->features; + ntb->b2b_mw_idx = B2B_MW_DISABLED; /* Heartbeat timer for NTB_SOC since there is no link interrupt */ callout_init(&ntb->heartbeat_timer, 1); callout_init(&ntb->lr_timer, 1); + mtx_init(&ntb->db_mask_lock, "ntb hw bits", NULL, MTX_SPIN); + mtx_init(&ntb->ctx_lock, "ntb ctx", NULL, MTX_SPIN); + + if (ntb->type == NTB_SOC) + error = ntb_detect_soc(ntb); + else + error = ntb_detect_xeon(ntb); + if (error) + goto out; + + ntb_detect_max_mw(ntb); error = ntb_map_pci_bars(ntb); if (error) goto out; - error = ntb_initialize_hw(ntb); + if (ntb->type == NTB_SOC) + error = ntb_soc_init_dev(ntb); + else + error = ntb_xeon_init_dev(ntb); if (error) goto out; - error = ntb_setup_interrupts(ntb); + error = ntb_init_isr(ntb); if (error) goto out; @@ -322,57 +496,142 @@ ntb_detach(device_t device) struct ntb_softc *ntb; ntb = DEVICE2SOFTC(device); + + ntb_db_set_mask(ntb, ntb->db_valid_mask); callout_drain(&ntb->heartbeat_timer); callout_drain(&ntb->lr_timer); + if (ntb->type == NTB_XEON) + ntb_teardown_xeon(ntb); ntb_teardown_interrupts(ntb); + + mtx_destroy(&ntb->db_mask_lock); + mtx_destroy(&ntb->ctx_lock); + + /* + * Redetect total MWs so we unmap properly -- in case we lowered the + * maximum to work around Xeon errata. + */ + ntb_detect_max_mw(ntb); ntb_unmap_pci_bar(ntb); return (0); } +/* + * Driver internal routines + */ +static inline enum ntb_bar +ntb_mw_to_bar(struct ntb_softc *ntb, unsigned mw) +{ + + KASSERT(mw < ntb->mw_count || + (mw != B2B_MW_DISABLED && mw == ntb->b2b_mw_idx), + ("%s: mw:%u > count:%u", __func__, mw, (unsigned)ntb->mw_count)); + KASSERT(ntb->reg->mw_bar[mw] != 0, ("invalid mw")); + + return (ntb->reg->mw_bar[mw]); +} + +static inline bool +bar_is_64bit(struct ntb_softc *ntb, enum ntb_bar bar) +{ + /* XXX This assertion could be stronger. */ + KASSERT(bar < NTB_MAX_BARS, ("bogus bar")); + return (bar < NTB_B2B_BAR_2 || !HAS_FEATURE(NTB_SPLIT_BAR)); +} + +static inline void +bar_get_xlat_params(struct ntb_softc *ntb, enum ntb_bar bar, uint32_t *base, + uint32_t *xlat, uint32_t *lmt) +{ + uint32_t basev, lmtv, xlatv; + + switch (bar) { + case NTB_B2B_BAR_1: + basev = ntb->xlat_reg->bar2_base; + lmtv = ntb->xlat_reg->bar2_limit; + xlatv = ntb->xlat_reg->bar2_xlat; + break; + case NTB_B2B_BAR_2: + basev = ntb->xlat_reg->bar4_base; + lmtv = ntb->xlat_reg->bar4_limit; + xlatv = ntb->xlat_reg->bar4_xlat; + break; + case NTB_B2B_BAR_3: + basev = ntb->xlat_reg->bar5_base; + lmtv = ntb->xlat_reg->bar5_limit; + xlatv = ntb->xlat_reg->bar5_xlat; + break; + default: + KASSERT(bar >= NTB_B2B_BAR_1 && bar < NTB_MAX_BARS, + ("bad bar")); + basev = lmtv = xlatv = 0; + break; + } + + if (base != NULL) + *base = basev; + if (xlat != NULL) + *xlat = xlatv; + if (lmt != NULL) + *lmt = lmtv; +} + static int ntb_map_pci_bars(struct ntb_softc *ntb) { int rc; ntb->bar_info[NTB_CONFIG_BAR].pci_resource_id = PCIR_BAR(0); - rc = map_pci_bar(ntb, map_mmr_bar, &ntb->bar_info[NTB_CONFIG_BAR]); + rc = map_mmr_bar(ntb, &ntb->bar_info[NTB_CONFIG_BAR]); if (rc != 0) - return (rc); + goto out; ntb->bar_info[NTB_B2B_BAR_1].pci_resource_id = PCIR_BAR(2); - rc = map_pci_bar(ntb, map_memory_window_bar, - &ntb->bar_info[NTB_B2B_BAR_1]); + rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_1]); if (rc != 0) - return (rc); + goto out; + ntb->bar_info[NTB_B2B_BAR_1].psz_off = XEON_PBAR23SZ_OFFSET; + ntb->bar_info[NTB_B2B_BAR_1].ssz_off = XEON_SBAR23SZ_OFFSET; + ntb->bar_info[NTB_B2B_BAR_1].pbarxlat_off = XEON_PBAR2XLAT_OFFSET; ntb->bar_info[NTB_B2B_BAR_2].pci_resource_id = PCIR_BAR(4); - if (HAS_FEATURE(NTB_REGS_THRU_MW)) - rc = map_pci_bar(ntb, map_mmr_bar, - &ntb->bar_info[NTB_B2B_BAR_2]); + /* XXX Are shared MW B2Bs write-combining? */ + if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP) && !HAS_FEATURE(NTB_SPLIT_BAR)) + rc = map_mmr_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_2]); else - rc = map_pci_bar(ntb, map_memory_window_bar, - &ntb->bar_info[NTB_B2B_BAR_2]); - return (rc); -} + rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_2]); + ntb->bar_info[NTB_B2B_BAR_2].psz_off = XEON_PBAR4SZ_OFFSET; + ntb->bar_info[NTB_B2B_BAR_2].ssz_off = XEON_SBAR4SZ_OFFSET; + ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off = XEON_PBAR4XLAT_OFFSET; -static int -map_pci_bar(struct ntb_softc *ntb, bar_map_strategy strategy, - struct ntb_pci_bar_info *bar) -{ - int rc; + if (!HAS_FEATURE(NTB_SPLIT_BAR)) + goto out; - rc = strategy(ntb, bar); + ntb->bar_info[NTB_B2B_BAR_3].pci_resource_id = PCIR_BAR(5); + if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP)) + rc = map_mmr_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_3]); + else + rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_3]); + ntb->bar_info[NTB_B2B_BAR_3].psz_off = XEON_PBAR5SZ_OFFSET; + ntb->bar_info[NTB_B2B_BAR_3].ssz_off = XEON_SBAR5SZ_OFFSET; + ntb->bar_info[NTB_B2B_BAR_3].pbarxlat_off = XEON_PBAR5XLAT_OFFSET; + +out: if (rc != 0) device_printf(ntb->device, "unable to allocate pci resource\n"); - else - device_printf(ntb->device, - "Bar size = %lx, v %p, p %p\n", - bar->size, bar->vbase, (void *)(bar->pbase)); return (rc); } +static void +print_map_success(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar) +{ + + device_printf(ntb->device, "Bar size = %lx, v %p, p %p\n", + bar->size, bar->vbase, (void *)(bar->pbase)); +} + static int map_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar) { @@ -383,6 +642,7 @@ map_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar) return (ENXIO); save_bar_parameters(bar); + print_map_success(ntb, bar); return (0); } @@ -443,6 +703,7 @@ map_memory_window_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar) "unable to mark bar as WRITE_COMBINING\n"); return (rc); } + print_map_success(ntb, bar); return (0); } @@ -452,7 +713,7 @@ ntb_unmap_pci_bar(struct ntb_softc *ntb) struct ntb_pci_bar_info *current_bar; int i; - for (i = 0; i< NTB_MAX_BARS; i++) { + for (i = 0; i < NTB_MAX_BARS; i++) { current_bar = &ntb->bar_info[i]; if (current_bar->pci_resource != NULL) bus_release_resource(ntb->device, SYS_RES_MEMORY, @@ -462,91 +723,154 @@ ntb_unmap_pci_bar(struct ntb_softc *ntb) } static int -ntb_setup_interrupts(struct ntb_softc *ntb) +ntb_setup_msix(struct ntb_softc *ntb, uint32_t num_vectors) { - void (*interrupt_handler)(void *); - void *int_arg; - bool use_msix = false; - uint32_t num_vectors; - int i; + uint32_t i; + int rc; - ntb->allocated_interrupts = 0; - /* - * On SOC, disable all interrupts. On XEON, disable all but Link - * Interrupt. The rest will be unmasked as callbacks are registered. - */ - if (ntb->type == NTB_SOC) - ntb_reg_write(8, ntb->reg_ofs.ldb_mask, ~0); - else - ntb_reg_write(2, ntb->reg_ofs.ldb_mask, - ~(1 << ntb->limits.max_db_bits)); - - num_vectors = MIN(pci_msix_count(ntb->device), - ntb->limits.max_db_bits); - if (num_vectors >= 1) { - pci_alloc_msix(ntb->device, &num_vectors); - if (num_vectors >= 4) - use_msix = true; - } - - ntb_create_callbacks(ntb, num_vectors); - if (use_msix == true) { - for (i = 0; i < num_vectors; i++) { - ntb->int_info[i].rid = i + 1; - ntb->int_info[i].res = bus_alloc_resource_any( - ntb->device, SYS_RES_IRQ, &ntb->int_info[i].rid, - RF_ACTIVE); - if (ntb->int_info[i].res == NULL) { - device_printf(ntb->device, - "bus_alloc_resource failed\n"); - return (ENOMEM); - } - ntb->int_info[i].tag = NULL; - ntb->allocated_interrupts++; - if (ntb->type == NTB_SOC) { - interrupt_handler = handle_soc_irq; - int_arg = &ntb->db_cb[i]; - } else { - if (i == num_vectors - 1) { - interrupt_handler = - handle_xeon_event_irq; - int_arg = ntb; - } else { - interrupt_handler = - handle_xeon_irq; - int_arg = &ntb->db_cb[i]; - } - } - if (bus_setup_intr(ntb->device, ntb->int_info[i].res, - INTR_MPSAFE | INTR_TYPE_MISC, NULL, - interrupt_handler, int_arg, - &ntb->int_info[i].tag) != 0) { - device_printf(ntb->device, - "bus_setup_intr failed\n"); - return (ENXIO); - } - } - } else { - ntb->int_info[0].rid = 0; - ntb->int_info[0].res = bus_alloc_resource_any(ntb->device, - SYS_RES_IRQ, &ntb->int_info[0].rid, RF_SHAREABLE|RF_ACTIVE); - interrupt_handler = ntb_handle_legacy_interrupt; - if (ntb->int_info[0].res == NULL) { + for (i = 0; i < num_vectors; i++) { + ntb->int_info[i].rid = i + 1; + ntb->int_info[i].res = bus_alloc_resource_any(ntb->device, + SYS_RES_IRQ, &ntb->int_info[i].rid, RF_ACTIVE); + if (ntb->int_info[i].res == NULL) { device_printf(ntb->device, "bus_alloc_resource failed\n"); return (ENOMEM); } - ntb->int_info[0].tag = NULL; - ntb->allocated_interrupts = 1; - - if (bus_setup_intr(ntb->device, ntb->int_info[0].res, - INTR_MPSAFE | INTR_TYPE_MISC, NULL, - interrupt_handler, ntb, &ntb->int_info[0].tag) != 0) { - + ntb->int_info[i].tag = NULL; + ntb->allocated_interrupts++; + rc = bus_setup_intr(ntb->device, ntb->int_info[i].res, + INTR_MPSAFE | INTR_TYPE_MISC, NULL, ndev_vec_isr, + &ntb->msix_vec[i], &ntb->int_info[i].tag); + if (rc != 0) { device_printf(ntb->device, "bus_setup_intr failed\n"); return (ENXIO); } } + return (0); +} + +/* + * The Linux NTB driver drops from MSI-X to legacy INTx if a unique vector + * cannot be allocated for each MSI-X message. JHB seems to think remapping + * should be okay. This tunable should enable us to test that hypothesis + * when someone gets their hands on some Xeon hardware. + */ +static int ntb_force_remap_mode; +SYSCTL_INT(_hw_ntb, OID_AUTO, force_remap_mode, CTLFLAG_RDTUN, + &ntb_force_remap_mode, 0, "If enabled, force MSI-X messages to be remapped" + " to a smaller number of ithreads, even if the desired number are " + "available"); + +/* + * In case it is NOT ok, give consumers an abort button. + */ +static int ntb_prefer_intx; +SYSCTL_INT(_hw_ntb, OID_AUTO, prefer_intx_to_remap, CTLFLAG_RDTUN, + &ntb_prefer_intx, 0, "If enabled, prefer to use legacy INTx mode rather " + "than remapping MSI-X messages over available slots (match Linux driver " + "behavior)"); + +/* + * Remap the desired number of MSI-X messages to available ithreads in a simple + * round-robin fashion. + */ +static int +ntb_remap_msix(device_t dev, uint32_t desired, uint32_t avail) +{ + u_int *vectors; + uint32_t i; + int rc; + + if (ntb_prefer_intx != 0) + return (ENXIO); + + vectors = malloc(desired * sizeof(*vectors), M_NTB, M_ZERO | M_WAITOK); + + for (i = 0; i < desired; i++) + vectors[i] = (i % avail) + 1; + + rc = pci_remap_msix(dev, desired, vectors); + free(vectors, M_NTB); + return (rc); +} + +static int +ntb_init_isr(struct ntb_softc *ntb) +{ + uint32_t desired_vectors, num_vectors; + int rc; + + ntb->allocated_interrupts = 0; + ntb->last_ts = ticks; + + /* + * Mask all doorbell interrupts. + */ + ntb_db_set_mask(ntb, ntb->db_valid_mask); + + num_vectors = desired_vectors = MIN(pci_msix_count(ntb->device), + ntb->db_count); + if (desired_vectors >= 1) { + rc = pci_alloc_msix(ntb->device, &num_vectors); + + if (ntb_force_remap_mode != 0 && rc == 0 && + num_vectors == desired_vectors) + num_vectors--; + + if (rc == 0 && num_vectors < desired_vectors) { + rc = ntb_remap_msix(ntb->device, desired_vectors, + num_vectors); + if (rc == 0) + num_vectors = desired_vectors; + else + pci_release_msi(ntb->device); + } + if (rc != 0) + num_vectors = 1; + } else + num_vectors = 1; + + if (ntb->type == NTB_XEON && num_vectors < ntb->db_vec_count) { + ntb->db_vec_count = 1; + ntb->db_vec_shift = ntb->db_count; + rc = ntb_setup_legacy_interrupt(ntb); + } else { + ntb_create_msix_vec(ntb, num_vectors); + rc = ntb_setup_msix(ntb, num_vectors); + } + if (rc != 0) { + device_printf(ntb->device, + "Error allocating interrupts: %d\n", rc); + ntb_free_msix_vec(ntb); + } + + return (rc); +} + +static int +ntb_setup_legacy_interrupt(struct ntb_softc *ntb) +{ + int rc; + + ntb->int_info[0].rid = 0; + ntb->int_info[0].res = bus_alloc_resource_any(ntb->device, SYS_RES_IRQ, + &ntb->int_info[0].rid, RF_SHAREABLE|RF_ACTIVE); + if (ntb->int_info[0].res == NULL) { + device_printf(ntb->device, "bus_alloc_resource failed\n"); + return (ENOMEM); + } + + ntb->int_info[0].tag = NULL; + ntb->allocated_interrupts = 1; + + rc = bus_setup_intr(ntb->device, ntb->int_info[0].res, + INTR_MPSAFE | INTR_TYPE_MISC, NULL, ndev_irq_isr, + ntb, &ntb->int_info[0].tag); + if (rc != 0) { + device_printf(ntb->device, "bus_setup_intr failed\n"); + return (ENXIO); + } return (0); } @@ -568,114 +892,157 @@ ntb_teardown_interrupts(struct ntb_softc *ntb) rman_get_rid(current_int->res), current_int->res); } - ntb_free_callbacks(ntb); + ntb_free_msix_vec(ntb); pci_release_msi(ntb->device); } -static void -handle_soc_irq(void *arg) +/* + * Doorbell register and mask are 64-bit on SoC, 16-bit on Xeon. Abstract it + * out to make code clearer. + */ +static inline uint64_t +db_ioread(struct ntb_softc *ntb, uint64_t regoff) { - struct ntb_db_cb *db_cb = arg; - struct ntb_softc *ntb = db_cb->ntb; - ntb_reg_write(8, ntb->reg_ofs.ldb, (uint64_t) 1 << db_cb->db_num); + if (ntb->type == NTB_SOC) + return (ntb_reg_read(8, regoff)); - if (db_cb->callback != NULL) - db_cb->callback(db_cb->data, db_cb->db_num); + KASSERT(ntb->type == NTB_XEON, ("bad ntb type")); + + return (ntb_reg_read(2, regoff)); } -static void -handle_xeon_irq(void *arg) +static inline void +db_iowrite(struct ntb_softc *ntb, uint64_t regoff, uint64_t val) { - struct ntb_db_cb *db_cb = arg; - struct ntb_softc *ntb = db_cb->ntb; - /* - * On Xeon, there are 16 bits in the interrupt register - * but only 4 vectors. So, 5 bits are assigned to the first 3 - * vectors, with the 4th having a single bit for link - * interrupts. - */ - ntb_reg_write(2, ntb->reg_ofs.ldb, - ((1 << ntb->bits_per_vector) - 1) << - (db_cb->db_num * ntb->bits_per_vector)); + KASSERT((val & ~ntb->db_valid_mask) == 0, + ("%s: Invalid bits 0x%jx (valid: 0x%jx)", __func__, + (uintmax_t)(val & ~ntb->db_valid_mask), + (uintmax_t)ntb->db_valid_mask)); - if (db_cb->callback != NULL) - db_cb->callback(db_cb->data, db_cb->db_num); -} - -/* Since we do not have a HW doorbell in SOC, this is only used in JF/JT */ -static void -handle_xeon_event_irq(void *arg) -{ - struct ntb_softc *ntb = arg; - int rc; - - rc = ntb_check_link_status(ntb); - if (rc != 0) - device_printf(ntb->device, "Error determining link status\n"); - - /* bit 15 is always the link bit */ - ntb_reg_write(2, ntb->reg_ofs.ldb, 1 << ntb->limits.max_db_bits); -} - -static void -ntb_handle_legacy_interrupt(void *arg) -{ - struct ntb_softc *ntb = arg; - unsigned int i = 0; - uint64_t ldb64; - uint16_t ldb16; + if (regoff == ntb->reg_ofs.ldb_mask) + DB_MASK_ASSERT(ntb, MA_OWNED); if (ntb->type == NTB_SOC) { - ldb64 = ntb_reg_read(8, ntb->reg_ofs.ldb); - - while (ldb64) { - i = ffs(ldb64); - ldb64 &= ldb64 - 1; - handle_soc_irq(&ntb->db_cb[i]); - } - } else { - ldb16 = ntb_reg_read(2, ntb->reg_ofs.ldb); - - if ((ldb16 & XEON_DB_HW_LINK) != 0) { - handle_xeon_event_irq(ntb); - ldb16 &= ~XEON_DB_HW_LINK; - } - - while (ldb16 != 0) { - i = ffs(ldb16); - ldb16 &= ldb16 - 1; - handle_xeon_irq(&ntb->db_cb[i]); - } + ntb_reg_write(8, regoff, val); + return; } + KASSERT(ntb->type == NTB_XEON, ("bad ntb type")); + ntb_reg_write(2, regoff, (uint16_t)val); +} + +void +ntb_db_set_mask(struct ntb_softc *ntb, uint64_t bits) +{ + + DB_MASK_LOCK(ntb); + ntb->db_mask |= bits; + db_iowrite(ntb, ntb->reg_ofs.ldb_mask, ntb->db_mask); + DB_MASK_UNLOCK(ntb); +} + +void +ntb_db_clear_mask(struct ntb_softc *ntb, uint64_t bits) +{ + + KASSERT((bits & ~ntb->db_valid_mask) == 0, + ("%s: Invalid bits 0x%jx (valid: 0x%jx)", __func__, + (uintmax_t)(bits & ~ntb->db_valid_mask), + (uintmax_t)ntb->db_valid_mask)); + + DB_MASK_LOCK(ntb); + ntb->db_mask &= ~bits; + db_iowrite(ntb, ntb->reg_ofs.ldb_mask, ntb->db_mask); + DB_MASK_UNLOCK(ntb); +} + +uint64_t +ntb_db_read(struct ntb_softc *ntb) +{ + + return (db_ioread(ntb, ntb->reg_ofs.ldb)); +} + +void +ntb_db_clear(struct ntb_softc *ntb, uint64_t bits) +{ + + KASSERT((bits & ~ntb->db_valid_mask) == 0, + ("%s: Invalid bits 0x%jx (valid: 0x%jx)", __func__, + (uintmax_t)(bits & ~ntb->db_valid_mask), + (uintmax_t)ntb->db_valid_mask)); + + db_iowrite(ntb, ntb->reg_ofs.ldb, bits); +} + +static inline uint64_t +ntb_vec_mask(struct ntb_softc *ntb, uint64_t db_vector) +{ + uint64_t shift, mask; + + shift = ntb->db_vec_shift; + mask = (1ull << shift) - 1; + return (mask << (shift * db_vector)); +} + +static void +ntb_interrupt(struct ntb_softc *ntb, uint32_t vec) +{ + uint64_t vec_mask; + + ntb->last_ts = ticks; + vec_mask = ntb_vec_mask(ntb, vec); + + if ((vec_mask & ntb->db_link_mask) != 0) { + if (ntb_poll_link(ntb)) + ntb_link_event(ntb); + } + + if ((vec_mask & ntb->db_valid_mask) != 0) + ntb_db_event(ntb, vec); +} + +static void +ndev_vec_isr(void *arg) +{ + struct ntb_vec *nvec = arg; + + ntb_interrupt(nvec->ntb, nvec->num); +} + +static void +ndev_irq_isr(void *arg) +{ + /* If we couldn't set up MSI-X, we only have the one vector. */ + ntb_interrupt(arg, 0); } static int -ntb_create_callbacks(struct ntb_softc *ntb, int num_vectors) +ntb_create_msix_vec(struct ntb_softc *ntb, uint32_t num_vectors) { - int i; + uint32_t i; - ntb->db_cb = malloc(num_vectors * sizeof(*ntb->db_cb), M_NTB, + ntb->msix_vec = malloc(num_vectors * sizeof(*ntb->msix_vec), M_NTB, M_ZERO | M_WAITOK); for (i = 0; i < num_vectors; i++) { - ntb->db_cb[i].db_num = i; - ntb->db_cb[i].ntb = ntb; + ntb->msix_vec[i].num = i; + ntb->msix_vec[i].ntb = ntb; } return (0); } static void -ntb_free_callbacks(struct ntb_softc *ntb) +ntb_free_msix_vec(struct ntb_softc *ntb) { - int i; - for (i = 0; i < ntb->limits.max_db_bits; i++) - ntb_unregister_db_callback(ntb, i); + if (ntb->msix_vec == NULL) + return; - free(ntb->db_cb, M_NTB); + free(ntb->msix_vec, M_NTB); + ntb->msix_vec = NULL; } static struct ntb_hw_info * @@ -691,69 +1058,117 @@ ntb_get_device_info(uint32_t device_id) return (NULL); } -static int -ntb_initialize_hw(struct ntb_softc *ntb) +static void +ntb_teardown_xeon(struct ntb_softc *ntb) { - if (ntb->type == NTB_SOC) - return (ntb_setup_soc(ntb)); + ntb_link_disable(ntb); +} + +static void +ntb_detect_max_mw(struct ntb_softc *ntb) +{ + + if (ntb->type == NTB_SOC) { + ntb->mw_count = SOC_MW_COUNT; + return; + } + + if (HAS_FEATURE(NTB_SPLIT_BAR)) + ntb->mw_count = XEON_HSX_SPLIT_MW_COUNT; else - return (ntb_setup_xeon(ntb)); + ntb->mw_count = XEON_SNB_MW_COUNT; } static int -ntb_setup_xeon(struct ntb_softc *ntb) +ntb_detect_xeon(struct ntb_softc *ntb) { - uint8_t val, connection_type; + uint8_t ppd, conn_type; - val = pci_read_config(ntb->device, NTB_PPD_OFFSET, 1); + ppd = pci_read_config(ntb->device, NTB_PPD_OFFSET, 1); + ntb->ppd = ppd; - connection_type = val & XEON_PPD_CONN_TYPE; - - if ((val & XEON_PPD_DEV_TYPE) != 0) + if ((ppd & XEON_PPD_DEV_TYPE) != 0) ntb->dev_type = NTB_DEV_USD; else ntb->dev_type = NTB_DEV_DSD; + if ((ppd & XEON_PPD_SPLIT_BAR) != 0) + ntb->features |= NTB_SPLIT_BAR; + + /* SB01BASE_LOCKUP errata is a superset of SDOORBELL errata */ + if (HAS_FEATURE(NTB_SB01BASE_LOCKUP)) + ntb->features |= NTB_SDOORBELL_LOCKUP; + + conn_type = ppd & XEON_PPD_CONN_TYPE; + switch (conn_type) { + case NTB_CONN_B2B: + ntb->conn_type = conn_type; + break; + case NTB_CONN_RP: + case NTB_CONN_TRANSPARENT: + default: + device_printf(ntb->device, "Unsupported connection type: %u\n", + (unsigned)conn_type); + return (ENXIO); + } + return (0); +} + +static int +ntb_detect_soc(struct ntb_softc *ntb) +{ + uint32_t ppd, conn_type; + + ppd = pci_read_config(ntb->device, NTB_PPD_OFFSET, 4); + ntb->ppd = ppd; + + if ((ppd & SOC_PPD_DEV_TYPE) != 0) + ntb->dev_type = NTB_DEV_DSD; + else + ntb->dev_type = NTB_DEV_USD; + + conn_type = (ppd & SOC_PPD_CONN_TYPE) >> 8; + switch (conn_type) { + case NTB_CONN_B2B: + ntb->conn_type = conn_type; + break; + default: + device_printf(ntb->device, "Unsupported NTB configuration\n"); + return (ENXIO); + } + return (0); +} + +static int +ntb_xeon_init_dev(struct ntb_softc *ntb) +{ + int rc; + ntb->reg_ofs.ldb = XEON_PDOORBELL_OFFSET; ntb->reg_ofs.ldb_mask = XEON_PDBMSK_OFFSET; ntb->reg_ofs.spad_local = XEON_SPAD_OFFSET; - ntb->reg_ofs.bar2_xlat = XEON_SBAR2XLAT_OFFSET; ntb->reg_ofs.bar4_xlat = XEON_SBAR4XLAT_OFFSET; + if (HAS_FEATURE(NTB_SPLIT_BAR)) + ntb->reg_ofs.bar5_xlat = XEON_SBAR5XLAT_OFFSET; + ntb->reg_ofs.spci_cmd = XEON_PCICMD_OFFSET; - switch (connection_type) { - case NTB_CONN_B2B: - ntb->conn_type = NTB_CONN_B2B; + ntb->spad_count = XEON_SPAD_COUNT; + ntb->db_count = XEON_DB_COUNT; + ntb->db_link_mask = XEON_DB_LINK_BIT; + ntb->db_vec_count = XEON_DB_MSIX_VECTOR_COUNT; + ntb->db_vec_shift = XEON_DB_MSIX_VECTOR_SHIFT; - /* - * reg_ofs.rdb and reg_ofs.spad_remote are effectively ignored - * with the NTB_REGS_THRU_MW errata mode enabled. (See - * ntb_ring_doorbell() and ntb_read/write_remote_spad().) - */ - ntb->reg_ofs.rdb = XEON_B2B_DOORBELL_OFFSET; - ntb->reg_ofs.spad_remote = XEON_B2B_SPAD_OFFSET; - - ntb->limits.max_spads = XEON_MAX_SPADS; - break; - - case NTB_CONN_RP: - /* - * Every Xeon today needs NTB_REGS_THRU_MW, so punt on RP for - * now. - */ - KASSERT(HAS_FEATURE(NTB_REGS_THRU_MW), - ("Xeon without MW errata unimplemented")); - device_printf(ntb->device, - "NTB-RP disabled to due hardware errata.\n"); - return (ENXIO); - - case NTB_CONN_TRANSPARENT: - default: + if (ntb->conn_type != NTB_CONN_B2B) { device_printf(ntb->device, "Connection type %d not supported\n", - connection_type); + ntb->conn_type); return (ENXIO); } + ntb->reg = &xeon_reg; + ntb->peer_reg = &xeon_b2b_reg; + ntb->xlat_reg = &xeon_sec_xlat; + /* * There is a Xeon hardware errata related to writes to SDOORBELL or * B2BDOORBELL in conjunction with inbound access to NTB MMIO space, @@ -761,85 +1176,63 @@ ntb_setup_xeon(struct ntb_softc *ntb) * window to access the interrupt and scratch pad registers on the * remote system. */ - if (HAS_FEATURE(NTB_REGS_THRU_MW)) + if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP)) + /* Use the last MW for mapping remote spad */ + ntb->b2b_mw_idx = ntb->mw_count - 1; + else if (HAS_FEATURE(NTB_B2BDOORBELL_BIT14)) /* - * Set the Limit register to 4k, the minimum size, to prevent - * an illegal access. + * HW Errata on bit 14 of b2bdoorbell register. Writes will not be + * mirrored to the remote system. Shrink the number of bits by one, + * since bit 14 is the last bit. + * + * On REGS_THRU_MW errata mode, we don't use the b2bdoorbell register + * anyway. Nor for non-B2B connection types. */ - ntb_reg_write(8, XEON_PBAR4LMT_OFFSET, - ntb_get_mw_size(ntb, 1) + 0x1000); + ntb->db_count = XEON_DB_COUNT - 1; + + ntb->db_valid_mask = (1ull << ntb->db_count) - 1; + + if (ntb->dev_type == NTB_DEV_USD) + rc = xeon_setup_b2b_mw(ntb, &xeon_b2b_dsd_addr, + &xeon_b2b_usd_addr); else - /* - * Disable the limit register, just in case it is set to - * something silly. - */ - ntb_reg_write(8, XEON_PBAR4LMT_OFFSET, 0); - - - ntb->reg_ofs.lnk_cntl = XEON_NTBCNTL_OFFSET; - ntb->reg_ofs.lnk_stat = XEON_LINK_STATUS_OFFSET; - ntb->reg_ofs.spci_cmd = XEON_PCICMD_OFFSET; - - ntb->limits.max_db_bits = XEON_MAX_DB_BITS; - ntb->limits.msix_cnt = XEON_MSIX_CNT; - ntb->bits_per_vector = XEON_DB_BITS_PER_VEC; - - configure_xeon_secondary_side_bars(ntb); + rc = xeon_setup_b2b_mw(ntb, &xeon_b2b_usd_addr, + &xeon_b2b_dsd_addr); + if (rc != 0) + return (rc); /* Enable Bus Master and Memory Space on the secondary side */ - if (ntb->conn_type == NTB_CONN_B2B) - ntb_reg_write(2, ntb->reg_ofs.spci_cmd, - PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); + ntb_reg_write(2, ntb->reg_ofs.spci_cmd, + PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); /* Enable link training */ - ntb_reg_write(4, ntb->reg_ofs.lnk_cntl, - NTB_CNTL_BAR23_SNOOP | NTB_CNTL_BAR45_SNOOP); + ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); return (0); } static int -ntb_setup_soc(struct ntb_softc *ntb) +ntb_soc_init_dev(struct ntb_softc *ntb) { - uint32_t val, connection_type; - val = pci_read_config(ntb->device, NTB_PPD_OFFSET, 4); - - connection_type = (val & SOC_PPD_CONN_TYPE) >> 8; - switch (connection_type) { - case NTB_CONN_B2B: - ntb->conn_type = NTB_CONN_B2B; - break; - default: - device_printf(ntb->device, - "Unsupported NTB configuration (%d)\n", connection_type); - return (ENXIO); - } - - if ((val & SOC_PPD_DEV_TYPE) != 0) - ntb->dev_type = NTB_DEV_DSD; - else - ntb->dev_type = NTB_DEV_USD; - - /* Initiate PCI-E link training */ - pci_write_config(ntb->device, NTB_PPD_OFFSET, val | SOC_PPD_INIT_LINK, - 4); + KASSERT(ntb->conn_type == NTB_CONN_B2B, + ("Unsupported NTB configuration (%d)\n", ntb->conn_type)); ntb->reg_ofs.ldb = SOC_PDOORBELL_OFFSET; ntb->reg_ofs.ldb_mask = SOC_PDBMSK_OFFSET; - ntb->reg_ofs.rdb = SOC_B2B_DOORBELL_OFFSET; - ntb->reg_ofs.bar2_xlat = SOC_SBAR2XLAT_OFFSET; ntb->reg_ofs.bar4_xlat = SOC_SBAR4XLAT_OFFSET; - ntb->reg_ofs.lnk_cntl = SOC_NTBCNTL_OFFSET; - ntb->reg_ofs.lnk_stat = SOC_LINK_STATUS_OFFSET; ntb->reg_ofs.spad_local = SOC_SPAD_OFFSET; - ntb->reg_ofs.spad_remote = SOC_B2B_SPAD_OFFSET; ntb->reg_ofs.spci_cmd = SOC_PCICMD_OFFSET; - ntb->limits.max_spads = SOC_MAX_SPADS; - ntb->limits.max_db_bits = SOC_MAX_DB_BITS; - ntb->limits.msix_cnt = SOC_MSIX_CNT; - ntb->bits_per_vector = SOC_DB_BITS_PER_VEC; + ntb->spad_count = SOC_SPAD_COUNT; + ntb->db_count = SOC_DB_COUNT; + ntb->db_vec_count = SOC_DB_MSIX_VECTOR_COUNT; + ntb->db_vec_shift = SOC_DB_MSIX_VECTOR_SHIFT; + ntb->db_valid_mask = (1ull << ntb->db_count) - 1; + + ntb->reg = &soc_reg; + ntb->peer_reg = &soc_b2b_reg; + ntb->xlat_reg = &soc_sec_xlat; /* * FIXME - MSI-X bug on early SOC HW, remove once internal issue is @@ -853,100 +1246,291 @@ ntb_setup_soc(struct ntb_softc *ntb) ntb_reg_write(2, ntb->reg_ofs.spci_cmd, PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); - callout_reset(&ntb->heartbeat_timer, 0, ntb_handle_heartbeat, ntb); + /* Initiate PCI-E link training */ + ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); + + callout_reset(&ntb->heartbeat_timer, 0, soc_link_hb, ntb); return (0); } +/* XXX: Linux driver doesn't seem to do any of this for SoC. */ static void configure_soc_secondary_side_bars(struct ntb_softc *ntb) { if (ntb->dev_type == NTB_DEV_USD) { - ntb_reg_write(8, SOC_PBAR2XLAT_OFFSET, PBAR2XLAT_USD_ADDR); - ntb_reg_write(8, SOC_PBAR4XLAT_OFFSET, PBAR4XLAT_USD_ADDR); - ntb_reg_write(8, SOC_MBAR23_OFFSET, MBAR23_USD_ADDR); - ntb_reg_write(8, SOC_MBAR45_OFFSET, MBAR45_USD_ADDR); + ntb_reg_write(8, SOC_PBAR2XLAT_OFFSET, + XEON_B2B_BAR2_DSD_ADDR64); + ntb_reg_write(8, SOC_PBAR4XLAT_OFFSET, + XEON_B2B_BAR4_DSD_ADDR64); + ntb_reg_write(8, SOC_MBAR23_OFFSET, XEON_B2B_BAR2_USD_ADDR64); + ntb_reg_write(8, SOC_MBAR45_OFFSET, XEON_B2B_BAR4_USD_ADDR64); } else { - ntb_reg_write(8, SOC_PBAR2XLAT_OFFSET, PBAR2XLAT_DSD_ADDR); - ntb_reg_write(8, SOC_PBAR4XLAT_OFFSET, PBAR4XLAT_DSD_ADDR); - ntb_reg_write(8, SOC_MBAR23_OFFSET, MBAR23_DSD_ADDR); - ntb_reg_write(8, SOC_MBAR45_OFFSET, MBAR45_DSD_ADDR); + ntb_reg_write(8, SOC_PBAR2XLAT_OFFSET, + XEON_B2B_BAR2_USD_ADDR64); + ntb_reg_write(8, SOC_PBAR4XLAT_OFFSET, + XEON_B2B_BAR4_USD_ADDR64); + ntb_reg_write(8, SOC_MBAR23_OFFSET, XEON_B2B_BAR2_DSD_ADDR64); + ntb_reg_write(8, SOC_MBAR45_OFFSET, XEON_B2B_BAR4_DSD_ADDR64); + } +} + + +/* + * When working around Xeon SDOORBELL errata by remapping remote registers in a + * MW, limit the B2B MW to half a MW. By sharing a MW, half the shared MW + * remains for use by a higher layer. + * + * Will only be used if working around SDOORBELL errata and the BIOS-configured + * MW size is sufficiently large. + */ +static unsigned int ntb_b2b_mw_share; +SYSCTL_UINT(_hw_ntb, OID_AUTO, b2b_mw_share, CTLFLAG_RDTUN, &ntb_b2b_mw_share, + 0, "If enabled (non-zero), prefer to share half of the B2B peer register " + "MW with higher level consumers. Both sides of the NTB MUST set the same " + "value here."); + +static void +xeon_reset_sbar_size(struct ntb_softc *ntb, enum ntb_bar idx, + enum ntb_bar regbar) +{ + struct ntb_pci_bar_info *bar; + uint8_t bar_sz; + + if (!HAS_FEATURE(NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_3) + return; + + bar = &ntb->bar_info[idx]; + bar_sz = pci_read_config(ntb->device, bar->psz_off, 1); + if (idx == regbar) { + if (ntb->b2b_off != 0) + bar_sz--; + else + bar_sz = 0; + } + pci_write_config(ntb->device, bar->ssz_off, bar_sz, 1); + bar_sz = pci_read_config(ntb->device, bar->ssz_off, 1); + (void)bar_sz; +} + +static void +xeon_set_sbar_base_and_limit(struct ntb_softc *ntb, uint64_t bar_addr, + enum ntb_bar idx, enum ntb_bar regbar) +{ + uint64_t reg_val; + uint32_t base_reg, lmt_reg; + + bar_get_xlat_params(ntb, idx, &base_reg, NULL, &lmt_reg); + if (idx == regbar) + bar_addr += ntb->b2b_off; + + if (!bar_is_64bit(ntb, idx)) { + ntb_reg_write(4, base_reg, bar_addr); + reg_val = ntb_reg_read(4, base_reg); + (void)reg_val; + + ntb_reg_write(4, lmt_reg, bar_addr); + reg_val = ntb_reg_read(4, lmt_reg); + (void)reg_val; + } else { + ntb_reg_write(8, base_reg, bar_addr); + reg_val = ntb_reg_read(8, base_reg); + (void)reg_val; + + ntb_reg_write(8, lmt_reg, bar_addr); + reg_val = ntb_reg_read(8, lmt_reg); + (void)reg_val; } } static void -configure_xeon_secondary_side_bars(struct ntb_softc *ntb) +xeon_set_pbar_xlat(struct ntb_softc *ntb, uint64_t base_addr, enum ntb_bar idx) +{ + struct ntb_pci_bar_info *bar; + + bar = &ntb->bar_info[idx]; + if (HAS_FEATURE(NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_2) { + ntb_reg_write(4, bar->pbarxlat_off, base_addr); + base_addr = ntb_reg_read(4, bar->pbarxlat_off); + } else { + ntb_reg_write(8, bar->pbarxlat_off, base_addr); + base_addr = ntb_reg_read(8, bar->pbarxlat_off); + } + (void)base_addr; +} + +static int +xeon_setup_b2b_mw(struct ntb_softc *ntb, const struct ntb_b2b_addr *addr, + const struct ntb_b2b_addr *peer_addr) +{ + struct ntb_pci_bar_info *b2b_bar; + vm_size_t bar_size; + uint64_t bar_addr; + enum ntb_bar b2b_bar_num, i; + + if (ntb->b2b_mw_idx == B2B_MW_DISABLED) { + b2b_bar = NULL; + b2b_bar_num = NTB_CONFIG_BAR; + ntb->b2b_off = 0; + } else { + b2b_bar_num = ntb_mw_to_bar(ntb, ntb->b2b_mw_idx); + KASSERT(b2b_bar_num > 0 && b2b_bar_num < NTB_MAX_BARS, + ("invalid b2b mw bar")); + + b2b_bar = &ntb->bar_info[b2b_bar_num]; + bar_size = b2b_bar->size; + + if (ntb_b2b_mw_share != 0 && + (bar_size >> 1) >= XEON_B2B_MIN_SIZE) + ntb->b2b_off = bar_size >> 1; + else if (bar_size >= XEON_B2B_MIN_SIZE) { + ntb->b2b_off = 0; + ntb->mw_count--; + } else { + device_printf(ntb->device, + "B2B bar size is too small!\n"); + return (EIO); + } + } + + /* + * Reset the secondary bar sizes to match the primary bar sizes. + * (Except, disable or halve the size of the B2B secondary bar.) + */ + for (i = NTB_B2B_BAR_1; i < NTB_MAX_BARS; i++) + xeon_reset_sbar_size(ntb, i, b2b_bar_num); + + bar_addr = 0; + if (b2b_bar_num == NTB_CONFIG_BAR) + bar_addr = addr->bar0_addr; + else if (b2b_bar_num == NTB_B2B_BAR_1) + bar_addr = addr->bar2_addr64; + else if (b2b_bar_num == NTB_B2B_BAR_2 && !HAS_FEATURE(NTB_SPLIT_BAR)) + bar_addr = addr->bar4_addr64; + else if (b2b_bar_num == NTB_B2B_BAR_2) + bar_addr = addr->bar4_addr32; + else if (b2b_bar_num == NTB_B2B_BAR_3) + bar_addr = addr->bar5_addr32; + else + KASSERT(false, ("invalid bar")); + + ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, bar_addr); + + /* + * Other SBARs are normally hit by the PBAR xlat, except for the b2b + * register BAR. The B2B BAR is either disabled above or configured + * half-size. It starts at PBAR xlat + offset. + * + * Also set up incoming BAR limits == base (zero length window). + */ + xeon_set_sbar_base_and_limit(ntb, addr->bar2_addr64, NTB_B2B_BAR_1, + b2b_bar_num); + if (HAS_FEATURE(NTB_SPLIT_BAR)) { + xeon_set_sbar_base_and_limit(ntb, addr->bar4_addr32, + NTB_B2B_BAR_2, b2b_bar_num); + xeon_set_sbar_base_and_limit(ntb, addr->bar5_addr32, + NTB_B2B_BAR_3, b2b_bar_num); + } else + xeon_set_sbar_base_and_limit(ntb, addr->bar4_addr64, + NTB_B2B_BAR_2, b2b_bar_num); + + /* Zero incoming translation addrs */ + ntb_reg_write(8, XEON_SBAR2XLAT_OFFSET, 0); + ntb_reg_write(8, XEON_SBAR4XLAT_OFFSET, 0); + + /* Zero outgoing translation limits (whole bar size windows) */ + ntb_reg_write(8, XEON_PBAR2LMT_OFFSET, 0); + ntb_reg_write(8, XEON_PBAR4LMT_OFFSET, 0); + + /* Set outgoing translation offsets */ + xeon_set_pbar_xlat(ntb, peer_addr->bar2_addr64, NTB_B2B_BAR_1); + if (HAS_FEATURE(NTB_SPLIT_BAR)) { + xeon_set_pbar_xlat(ntb, peer_addr->bar4_addr32, NTB_B2B_BAR_2); + xeon_set_pbar_xlat(ntb, peer_addr->bar5_addr32, NTB_B2B_BAR_3); + } else + xeon_set_pbar_xlat(ntb, peer_addr->bar4_addr64, NTB_B2B_BAR_2); + + /* Set the translation offset for B2B registers */ + bar_addr = 0; + if (b2b_bar_num == NTB_CONFIG_BAR) + bar_addr = peer_addr->bar0_addr; + else if (b2b_bar_num == NTB_B2B_BAR_1) + bar_addr = peer_addr->bar2_addr64; + else if (b2b_bar_num == NTB_B2B_BAR_2 && !HAS_FEATURE(NTB_SPLIT_BAR)) + bar_addr = peer_addr->bar4_addr64; + else if (b2b_bar_num == NTB_B2B_BAR_2) + bar_addr = peer_addr->bar4_addr32; + else if (b2b_bar_num == NTB_B2B_BAR_3) + bar_addr = peer_addr->bar5_addr32; + else + KASSERT(false, ("invalid bar")); + + /* + * B2B_XLAT_OFFSET is a 64-bit register but can only be written 32 bits + * at a time. + */ + ntb_reg_write(4, XEON_B2B_XLAT_OFFSETL, bar_addr & 0xffffffff); + ntb_reg_write(4, XEON_B2B_XLAT_OFFSETU, bar_addr >> 32); + return (0); +} + +static inline bool +link_is_up(struct ntb_softc *ntb) { - if (ntb->dev_type == NTB_DEV_USD) { - ntb_reg_write(8, XEON_PBAR2XLAT_OFFSET, PBAR2XLAT_USD_ADDR); - if (HAS_FEATURE(NTB_REGS_THRU_MW)) - ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, - MBAR01_DSD_ADDR); - else { - ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, - PBAR4XLAT_USD_ADDR); - /* - * B2B_XLAT_OFFSET is a 64-bit register but can only be - * written 32 bits at a time. - */ - ntb_reg_write(4, XEON_B2B_XLAT_OFFSETL, - MBAR01_DSD_ADDR & 0xffffffff); - ntb_reg_write(4, XEON_B2B_XLAT_OFFSETU, - MBAR01_DSD_ADDR >> 32); - } - ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, MBAR01_USD_ADDR); - ntb_reg_write(8, XEON_SBAR2BASE_OFFSET, MBAR23_USD_ADDR); - ntb_reg_write(8, XEON_SBAR4BASE_OFFSET, MBAR45_USD_ADDR); - } else { - ntb_reg_write(8, XEON_PBAR2XLAT_OFFSET, PBAR2XLAT_DSD_ADDR); - if (HAS_FEATURE(NTB_REGS_THRU_MW)) - ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, - MBAR01_USD_ADDR); - else { - ntb_reg_write(8, XEON_PBAR4XLAT_OFFSET, - PBAR4XLAT_DSD_ADDR); - /* - * B2B_XLAT_OFFSET is a 64-bit register but can only be - * written 32 bits at a time. - */ - ntb_reg_write(4, XEON_B2B_XLAT_OFFSETL, - MBAR01_USD_ADDR & 0xffffffff); - ntb_reg_write(4, XEON_B2B_XLAT_OFFSETU, - MBAR01_USD_ADDR >> 32); - } - ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, MBAR01_DSD_ADDR); - ntb_reg_write(8, XEON_SBAR2BASE_OFFSET, MBAR23_DSD_ADDR); - ntb_reg_write(8, XEON_SBAR4BASE_OFFSET, MBAR45_DSD_ADDR); - } + if (ntb->type == NTB_XEON) + return ((ntb->lnk_sta & NTB_LINK_STATUS_ACTIVE) != 0); + + KASSERT(ntb->type == NTB_SOC, ("ntb type")); + return ((ntb->ntb_ctl & SOC_CNTL_LINK_DOWN) == 0); +} + +static inline bool +soc_link_is_err(struct ntb_softc *ntb) +{ + uint32_t status; + + KASSERT(ntb->type == NTB_SOC, ("ntb type")); + + status = ntb_reg_read(4, SOC_LTSSMSTATEJMP_OFFSET); + if ((status & SOC_LTSSMSTATEJMP_FORCEDETECT) != 0) + return (true); + + status = ntb_reg_read(4, SOC_IBSTERRRCRVSTS0_OFFSET); + return ((status & SOC_IBIST_ERR_OFLOW) != 0); } /* SOC does not have link status interrupt, poll on that platform */ static void -ntb_handle_heartbeat(void *arg) +soc_link_hb(void *arg) { struct ntb_softc *ntb = arg; - uint32_t status32; - int rc; + sbintime_t timo, poll_ts; - rc = ntb_check_link_status(ntb); - if (rc != 0) - device_printf(ntb->device, - "Error determining link status\n"); + timo = NTB_HB_TIMEOUT * hz; + poll_ts = ntb->last_ts + timo; - /* Check to see if a link error is the cause of the link down */ - if (ntb->link_status == NTB_LINK_DOWN) { - status32 = ntb_reg_read(4, SOC_LTSSMSTATEJMP_OFFSET); - if ((status32 & SOC_LTSSMSTATEJMP_FORCEDETECT) != 0) { - callout_reset(&ntb->lr_timer, 0, recover_soc_link, - ntb); - return; - } + /* + * Delay polling the link status if an interrupt was received, unless + * the cached link status says the link is down. + */ + if ((sbintime_t)ticks - poll_ts < 0 && link_is_up(ntb)) { + timo = poll_ts - ticks; + goto out; } - callout_reset(&ntb->heartbeat_timer, NTB_HB_TIMEOUT * hz, - ntb_handle_heartbeat, ntb); + if (ntb_poll_link(ntb)) + ntb_link_event(ntb); + + if (!link_is_up(ntb) && soc_link_is_err(ntb)) { + /* Link is down with error, proceed with recovery */ + callout_reset(&ntb->lr_timer, 0, recover_soc_link, ntb); + return; + } + +out: + callout_reset(&ntb->heartbeat_timer, timo, soc_link_hb, ntb); } static void @@ -988,42 +1572,183 @@ soc_perform_link_restart(struct ntb_softc *ntb) ntb_reg_write(4, SOC_LTSSMSTATEJMP_OFFSET, status); } -static void -ntb_handle_link_event(struct ntb_softc *ntb, int link_state) +/* + * ntb_set_ctx() - associate a driver context with an ntb device + * @ntb: NTB device context + * @ctx: Driver context + * @ctx_ops: Driver context operations + * + * Associate a driver context and operations with a ntb device. The context is + * provided by the client driver, and the driver may associate a different + * context with each ntb device. + * + * Return: Zero if the context is associated, otherwise an error number. + */ +int +ntb_set_ctx(struct ntb_softc *ntb, void *ctx, const struct ntb_ctx_ops *ops) { - enum ntb_hw_event event; - uint16_t status; - if (ntb->link_status == link_state) - return; + if (ctx == NULL || ops == NULL) + return (EINVAL); + if (ntb->ctx_ops != NULL) + return (EINVAL); - if (link_state == NTB_LINK_UP) { - device_printf(ntb->device, "Link Up\n"); - ntb->link_status = NTB_LINK_UP; - event = NTB_EVENT_HW_LINK_UP; + CTX_LOCK(ntb); + if (ntb->ctx_ops != NULL) { + CTX_UNLOCK(ntb); + return (EINVAL); + } + ntb->ntb_ctx = ctx; + ntb->ctx_ops = ops; + CTX_UNLOCK(ntb); - if (ntb->type == NTB_SOC || - ntb->conn_type == NTB_CONN_TRANSPARENT) - status = ntb_reg_read(2, ntb->reg_ofs.lnk_stat); - else - status = pci_read_config(ntb->device, - XEON_LINK_STATUS_OFFSET, 2); - ntb->link_width = (status & NTB_LINK_WIDTH_MASK) >> 4; - ntb->link_speed = (status & NTB_LINK_SPEED_MASK); - device_printf(ntb->device, "Link Width %d, Link Speed %d\n", - ntb->link_width, ntb->link_speed); - callout_reset(&ntb->heartbeat_timer, NTB_HB_TIMEOUT * hz, - ntb_handle_heartbeat, ntb); - } else { - device_printf(ntb->device, "Link Down\n"); - ntb->link_status = NTB_LINK_DOWN; - event = NTB_EVENT_HW_LINK_DOWN; - /* Do not modify link width/speed, we need it in link recovery */ + return (0); +} + +/* + * It is expected that this will only be used from contexts where the ctx_lock + * is not needed to protect ntb_ctx lifetime. + */ +void * +ntb_get_ctx(struct ntb_softc *ntb, const struct ntb_ctx_ops **ops) +{ + + KASSERT(ntb->ntb_ctx != NULL && ntb->ctx_ops != NULL, ("bogus")); + if (ops != NULL) + *ops = ntb->ctx_ops; + return (ntb->ntb_ctx); +} + +/* + * ntb_clear_ctx() - disassociate any driver context from an ntb device + * @ntb: NTB device context + * + * Clear any association that may exist between a driver context and the ntb + * device. + */ +void +ntb_clear_ctx(struct ntb_softc *ntb) +{ + + CTX_LOCK(ntb); + ntb->ntb_ctx = NULL; + ntb->ctx_ops = NULL; + CTX_UNLOCK(ntb); +} + +/* + * ntb_link_event() - notify driver context of a change in link status + * @ntb: NTB device context + * + * Notify the driver context that the link status may have changed. The driver + * should call ntb_link_is_up() to get the current status. + */ +void +ntb_link_event(struct ntb_softc *ntb) +{ + + CTX_LOCK(ntb); + if (ntb->ctx_ops != NULL && ntb->ctx_ops->link_event != NULL) + ntb->ctx_ops->link_event(ntb->ntb_ctx); + CTX_UNLOCK(ntb); +} + +/* + * ntb_db_event() - notify driver context of a doorbell event + * @ntb: NTB device context + * @vector: Interrupt vector number + * + * Notify the driver context of a doorbell event. If hardware supports + * multiple interrupt vectors for doorbells, the vector number indicates which + * vector received the interrupt. The vector number is relative to the first + * vector used for doorbells, starting at zero, and must be less than + * ntb_db_vector_count(). The driver may call ntb_db_read() to check which + * doorbell bits need service, and ntb_db_vector_mask() to determine which of + * those bits are associated with the vector number. + */ +static void +ntb_db_event(struct ntb_softc *ntb, uint32_t vec) +{ + + CTX_LOCK(ntb); + if (ntb->ctx_ops != NULL && ntb->ctx_ops->db_event != NULL) + ntb->ctx_ops->db_event(ntb->ntb_ctx, vec); + CTX_UNLOCK(ntb); +} + +/* + * ntb_link_enable() - enable the link on the secondary side of the ntb + * @ntb: NTB device context + * @max_speed: The maximum link speed expressed as PCIe generation number[0] + * @max_width: The maximum link width expressed as the number of PCIe lanes[0] + * + * Enable the link on the secondary side of the ntb. This can only be done + * from the primary side of the ntb in primary or b2b topology. The ntb device + * should train the link to its maximum speed and width, or the requested speed + * and width, whichever is smaller, if supported. + * + * Return: Zero on success, otherwise an error number. + * + * [0]: Only NTB_SPEED_AUTO and NTB_WIDTH_AUTO are valid inputs; other speed + * and width input will be ignored. + */ +int +ntb_link_enable(struct ntb_softc *ntb, enum ntb_speed s __unused, + enum ntb_width w __unused) +{ + uint32_t cntl; + + if (ntb->type == NTB_SOC) { + pci_write_config(ntb->device, NTB_PPD_OFFSET, + ntb->ppd | SOC_PPD_INIT_LINK, 4); + return (0); } - /* notify the upper layer if we have an event change */ - if (ntb->event_cb != NULL) - ntb->event_cb(ntb->ntb_transport, event); + if (ntb->conn_type == NTB_CONN_TRANSPARENT) { + ntb_link_event(ntb); + return (0); + } + + cntl = ntb_reg_read(4, ntb->reg->ntb_ctl); + cntl &= ~(NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK); + cntl |= NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP; + cntl |= NTB_CNTL_P2S_BAR4_SNOOP | NTB_CNTL_S2P_BAR4_SNOOP; + if (HAS_FEATURE(NTB_SPLIT_BAR)) + cntl |= NTB_CNTL_P2S_BAR5_SNOOP | NTB_CNTL_S2P_BAR5_SNOOP; + ntb_reg_write(4, ntb->reg->ntb_ctl, cntl); + return (0); +} + +/* + * ntb_link_disable() - disable the link on the secondary side of the ntb + * @ntb: NTB device context + * + * Disable the link on the secondary side of the ntb. This can only be done + * from the primary side of the ntb in primary or b2b topology. The ntb device + * should disable the link. Returning from this call must indicate that a + * barrier has passed, though with no more writes may pass in either direction + * across the link, except if this call returns an error number. + * + * Return: Zero on success, otherwise an error number. + */ +int +ntb_link_disable(struct ntb_softc *ntb) +{ + uint32_t cntl; + + if (ntb->conn_type == NTB_CONN_TRANSPARENT) { + ntb_link_event(ntb); + return (0); + } + + cntl = ntb_reg_read(4, ntb->reg->ntb_ctl); + cntl &= ~(NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP); + cntl &= ~(NTB_CNTL_P2S_BAR4_SNOOP | NTB_CNTL_S2P_BAR4_SNOOP); + if (HAS_FEATURE(NTB_SPLIT_BAR)) + cntl &= ~(NTB_CNTL_P2S_BAR5_SNOOP | NTB_CNTL_S2P_BAR5_SNOOP); + cntl |= NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK; + ntb_reg_write(4, ntb->reg->ntb_ctl, cntl); + return (0); } static void @@ -1032,7 +1757,6 @@ recover_soc_link(void *arg) struct ntb_softc *ntb = arg; uint8_t speed, width; uint32_t status32; - uint16_t status16; soc_perform_link_restart(ntb); @@ -1053,19 +1777,19 @@ recover_soc_link(void *arg) if ((status32 & SOC_IBIST_ERR_OFLOW) != 0) goto retry; - status32 = ntb_reg_read(4, ntb->reg_ofs.lnk_cntl); + status32 = ntb_reg_read(4, ntb->reg->ntb_ctl); if ((status32 & SOC_CNTL_LINK_DOWN) != 0) goto out; - status16 = ntb_reg_read(2, ntb->reg_ofs.lnk_stat); - width = (status16 & NTB_LINK_WIDTH_MASK) >> 4; - speed = (status16 & NTB_LINK_SPEED_MASK); + status32 = ntb_reg_read(4, ntb->reg->lnk_sta); + width = (status32 & NTB_LINK_WIDTH_MASK) >> 4; + speed = (status32 & NTB_LINK_SPEED_MASK); if (ntb->link_width != width || ntb->link_speed != speed) goto retry; out: - callout_reset(&ntb->heartbeat_timer, NTB_HB_TIMEOUT * hz, - ntb_handle_heartbeat, ntb); + callout_reset(&ntb->heartbeat_timer, NTB_HB_TIMEOUT * hz, soc_link_hb, + ntb); return; retry: @@ -1073,188 +1797,55 @@ retry: ntb); } -static int -ntb_check_link_status(struct ntb_softc *ntb) +/* + * Polls the HW link status register(s); returns true if something has changed. + */ +static bool +ntb_poll_link(struct ntb_softc *ntb) { - int link_state; uint32_t ntb_cntl; - uint16_t status; + uint16_t reg_val; if (ntb->type == NTB_SOC) { - ntb_cntl = ntb_reg_read(4, ntb->reg_ofs.lnk_cntl); - if ((ntb_cntl & SOC_CNTL_LINK_DOWN) != 0) - link_state = NTB_LINK_DOWN; - else - link_state = NTB_LINK_UP; + ntb_cntl = ntb_reg_read(4, ntb->reg->ntb_ctl); + if (ntb_cntl == ntb->ntb_ctl) + return (false); + + ntb->ntb_ctl = ntb_cntl; + ntb->lnk_sta = ntb_reg_read(4, ntb->reg->lnk_sta); } else { - status = pci_read_config(ntb->device, XEON_LINK_STATUS_OFFSET, - 2); + db_iowrite(ntb, ntb->reg_ofs.ldb, ntb->db_link_mask); - if ((status & NTB_LINK_STATUS_ACTIVE) != 0) - link_state = NTB_LINK_UP; - else - link_state = NTB_LINK_DOWN; + reg_val = pci_read_config(ntb->device, ntb->reg->lnk_sta, 2); + if (reg_val == ntb->lnk_sta) + return (false); + + ntb->lnk_sta = reg_val; } - - ntb_handle_link_event(ntb, link_state); - - return (0); + return (true); } -/** - * ntb_register_event_callback() - register event callback - * @ntb: pointer to ntb_softc instance - * @func: callback function to register - * - * This function registers a callback for any HW driver events such as link - * up/down, power management notices and etc. - * - * RETURNS: An appropriate ERRNO error value on error, or zero for success. - */ -int -ntb_register_event_callback(struct ntb_softc *ntb, ntb_event_callback func) +static inline enum ntb_speed +ntb_link_sta_speed(struct ntb_softc *ntb) { - if (ntb->event_cb != NULL) - return (EINVAL); - - ntb->event_cb = func; - - return (0); + if (!link_is_up(ntb)) + return (NTB_SPEED_NONE); + return (ntb->lnk_sta & NTB_LINK_SPEED_MASK); } -/** - * ntb_unregister_event_callback() - unregisters the event callback - * @ntb: pointer to ntb_softc instance - * - * This function unregisters the existing callback from transport - */ -void -ntb_unregister_event_callback(struct ntb_softc *ntb) +static inline enum ntb_width +ntb_link_sta_width(struct ntb_softc *ntb) { - ntb->event_cb = NULL; + if (!link_is_up(ntb)) + return (NTB_WIDTH_NONE); + return (NTB_LNK_STA_WIDTH(ntb->lnk_sta)); } -/** - * ntb_register_db_callback() - register a callback for doorbell interrupt - * @ntb: pointer to ntb_softc instance - * @idx: doorbell index to register callback, zero based - * @data: pointer to be returned to caller with every callback - * @func: callback function to register - * - * This function registers a callback function for the doorbell interrupt - * on the primary side. The function will unmask the doorbell as well to - * allow interrupt. - * - * RETURNS: An appropriate ERRNO error value on error, or zero for success. +/* + * Public API to the rest of the OS */ -int -ntb_register_db_callback(struct ntb_softc *ntb, unsigned int idx, void *data, - ntb_db_callback func) -{ - uint16_t mask; - - if (idx >= ntb->allocated_interrupts || ntb->db_cb[idx].callback) { - device_printf(ntb->device, "Invalid Index.\n"); - return (EINVAL); - } - - ntb->db_cb[idx].callback = func; - ntb->db_cb[idx].data = data; - - /* unmask interrupt */ - mask = ntb_reg_read(2, ntb->reg_ofs.ldb_mask); - mask &= ~(1 << (idx * ntb->bits_per_vector)); - ntb_reg_write(2, ntb->reg_ofs.ldb_mask, mask); - - return (0); -} - -/** - * ntb_unregister_db_callback() - unregister a callback for doorbell interrupt - * @ntb: pointer to ntb_softc instance - * @idx: doorbell index to register callback, zero based - * - * This function unregisters a callback function for the doorbell interrupt - * on the primary side. The function will also mask the said doorbell. - */ -void -ntb_unregister_db_callback(struct ntb_softc *ntb, unsigned int idx) -{ - unsigned long mask; - - if (idx >= ntb->allocated_interrupts || !ntb->db_cb[idx].callback) - return; - - mask = ntb_reg_read(2, ntb->reg_ofs.ldb_mask); - mask |= 1 << (idx * ntb->bits_per_vector); - ntb_reg_write(2, ntb->reg_ofs.ldb_mask, mask); - - ntb->db_cb[idx].callback = NULL; -} - -/** - * ntb_find_transport() - find the transport pointer - * @transport: pointer to pci device - * - * Given the pci device pointer, return the transport pointer passed in when - * the transport attached when it was inited. - * - * RETURNS: pointer to transport. - */ -void * -ntb_find_transport(struct ntb_softc *ntb) -{ - - return (ntb->ntb_transport); -} - -/** - * ntb_register_transport() - Register NTB transport with NTB HW driver - * @transport: transport identifier - * - * This function allows a transport to reserve the hardware driver for - * NTB usage. - * - * RETURNS: pointer to ntb_softc, NULL on error. - */ -struct ntb_softc * -ntb_register_transport(struct ntb_softc *ntb, void *transport) -{ - - /* - * TODO: when we have more than one transport, we will need to rewrite - * this to prevent race conditions - */ - if (ntb->ntb_transport != NULL) - return (NULL); - - ntb->ntb_transport = transport; - return (ntb); -} - -/** - * ntb_unregister_transport() - Unregister the transport with the NTB HW driver - * @ntb - ntb_softc of the transport to be freed - * - * This function unregisters the transport from the HW driver and performs any - * necessary cleanups. - */ -void -ntb_unregister_transport(struct ntb_softc *ntb) -{ - int i; - - if (ntb->ntb_transport == NULL) - return; - - for (i = 0; i < ntb->allocated_interrupts; i++) - ntb_unregister_db_callback(ntb, i); - - ntb_unregister_event_callback(ntb); - ntb->ntb_transport = NULL; -} /** * ntb_get_max_spads() - get the total scratch regs usable @@ -1269,11 +1860,18 @@ uint8_t ntb_get_max_spads(struct ntb_softc *ntb) { - return (ntb->limits.max_spads); + return (ntb->spad_count); +} + +uint8_t +ntb_mw_count(struct ntb_softc *ntb) +{ + + return (ntb->mw_count); } /** - * ntb_write_local_spad() - write to the secondary scratchpad register + * ntb_spad_write() - write to the secondary scratchpad register * @ntb: pointer to ntb_softc instance * @idx: index to the scratchpad register, 0 based * @val: the data value to put into the register @@ -1284,10 +1882,10 @@ ntb_get_max_spads(struct ntb_softc *ntb) * RETURNS: An appropriate ERRNO error value on error, or zero for success. */ int -ntb_write_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t val) +ntb_spad_write(struct ntb_softc *ntb, unsigned int idx, uint32_t val) { - if (idx >= ntb->limits.max_spads) + if (idx >= ntb->spad_count) return (EINVAL); ntb_reg_write(4, ntb->reg_ofs.spad_local + idx * 4, val); @@ -1296,7 +1894,7 @@ ntb_write_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t val) } /** - * ntb_read_local_spad() - read from the primary scratchpad register + * ntb_spad_read() - read from the primary scratchpad register * @ntb: pointer to ntb_softc instance * @idx: index to scratchpad register, 0 based * @val: pointer to 32bit integer for storing the register value @@ -1307,10 +1905,10 @@ ntb_write_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t val) * RETURNS: An appropriate ERRNO error value on error, or zero for success. */ int -ntb_read_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t *val) +ntb_spad_read(struct ntb_softc *ntb, unsigned int idx, uint32_t *val) { - if (idx >= ntb->limits.max_spads) + if (idx >= ntb->spad_count) return (EINVAL); *val = ntb_reg_read(4, ntb->reg_ofs.spad_local + idx * 4); @@ -1319,7 +1917,7 @@ ntb_read_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t *val) } /** - * ntb_write_remote_spad() - write to the secondary scratchpad register + * ntb_peer_spad_write() - write to the secondary scratchpad register * @ntb: pointer to ntb_softc instance * @idx: index to the scratchpad register, 0 based * @val: the data value to put into the register @@ -1330,22 +1928,22 @@ ntb_read_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t *val) * RETURNS: An appropriate ERRNO error value on error, or zero for success. */ int -ntb_write_remote_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t val) +ntb_peer_spad_write(struct ntb_softc *ntb, unsigned int idx, uint32_t val) { - if (idx >= ntb->limits.max_spads) + if (idx >= ntb->spad_count) return (EINVAL); - if (HAS_FEATURE(NTB_REGS_THRU_MW)) + if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP)) ntb_mw_write(4, XEON_SHADOW_SPAD_OFFSET + idx * 4, val); else - ntb_reg_write(4, ntb->reg_ofs.spad_remote + idx * 4, val); + ntb_reg_write(4, ntb->peer_reg->spad + idx * 4, val); return (0); } /** - * ntb_read_remote_spad() - read from the primary scratchpad register + * ntb_peer_spad_read() - read from the primary scratchpad register * @ntb: pointer to ntb_softc instance * @idx: index to scratchpad register, 0 based * @val: pointer to 32bit integer for storing the register value @@ -1356,137 +1954,245 @@ ntb_write_remote_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t val) * RETURNS: An appropriate ERRNO error value on error, or zero for success. */ int -ntb_read_remote_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t *val) +ntb_peer_spad_read(struct ntb_softc *ntb, unsigned int idx, uint32_t *val) { - if (idx >= ntb->limits.max_spads) + if (idx >= ntb->spad_count) return (EINVAL); - if (HAS_FEATURE(NTB_REGS_THRU_MW)) + if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP)) *val = ntb_mw_read(4, XEON_SHADOW_SPAD_OFFSET + idx * 4); else - *val = ntb_reg_read(4, ntb->reg_ofs.spad_remote + idx * 4); + *val = ntb_reg_read(4, ntb->peer_reg->spad + idx * 4); return (0); } -/** - * ntb_get_mw_vbase() - get virtual addr for the NTB memory window - * @ntb: pointer to ntb_softc instance - * @mw: memory window number +/* + * ntb_mw_get_range() - get the range of a memory window + * @ntb: NTB device context + * @idx: Memory window number + * @base: OUT - the base address for mapping the memory window + * @size: OUT - the size for mapping the memory window + * @align: OUT - the base alignment for translating the memory window + * @align_size: OUT - the size alignment for translating the memory window * - * This function provides the base virtual address of the memory window - * specified. + * Get the range of a memory window. NULL may be given for any output + * parameter if the value is not needed. The base and size may be used for + * mapping the memory window, to access the peer memory. The alignment and + * size may be used for translating the memory window, for the peer to access + * memory on the local system. * - * RETURNS: pointer to virtual address, or NULL on error. + * Return: Zero on success, otherwise an error number. */ -void * -ntb_get_mw_vbase(struct ntb_softc *ntb, unsigned int mw) +int +ntb_mw_get_range(struct ntb_softc *ntb, unsigned mw_idx, vm_paddr_t *base, + void **vbase, size_t *size, size_t *align, size_t *align_size) { + struct ntb_pci_bar_info *bar; + size_t bar_b2b_off; - if (mw >= NTB_NUM_MW) - return (NULL); + if (mw_idx >= ntb_mw_count(ntb)) + return (EINVAL); - return (ntb->bar_info[NTB_MW_TO_BAR(mw)].vbase); -} - -vm_paddr_t -ntb_get_mw_pbase(struct ntb_softc *ntb, unsigned int mw) -{ - - if (mw >= NTB_NUM_MW) - return (0); - - return (ntb->bar_info[NTB_MW_TO_BAR(mw)].pbase); -} - -/** - * ntb_get_mw_size() - return size of NTB memory window - * @ntb: pointer to ntb_softc instance - * @mw: memory window number - * - * This function provides the physical size of the memory window specified - * - * RETURNS: the size of the memory window or zero on error - */ -u_long -ntb_get_mw_size(struct ntb_softc *ntb, unsigned int mw) -{ - - if (mw >= NTB_NUM_MW) - return (0); - - return (ntb->bar_info[NTB_MW_TO_BAR(mw)].size); -} - -/** - * ntb_set_mw_addr - set the memory window address - * @ntb: pointer to ntb_softc instance - * @mw: memory window number - * @addr: base address for data - * - * This function sets the base physical address of the memory window. This - * memory address is where data from the remote system will be transfered into - * or out of depending on how the transport is configured. - */ -void -ntb_set_mw_addr(struct ntb_softc *ntb, unsigned int mw, uint64_t addr) -{ - - if (mw >= NTB_NUM_MW) - return; - - switch (NTB_MW_TO_BAR(mw)) { - case NTB_B2B_BAR_1: - ntb_reg_write(8, ntb->reg_ofs.bar2_xlat, addr); - break; - case NTB_B2B_BAR_2: - ntb_reg_write(8, ntb->reg_ofs.bar4_xlat, addr); - break; + bar = &ntb->bar_info[ntb_mw_to_bar(ntb, mw_idx)]; + bar_b2b_off = 0; + if (mw_idx == ntb->b2b_mw_idx) { + KASSERT(ntb->b2b_off != 0, + ("user shouldn't get non-shared b2b mw")); + bar_b2b_off = ntb->b2b_off; } + + if (base != NULL) + *base = bar->pbase + bar_b2b_off; + if (vbase != NULL) + *vbase = (char *)bar->vbase + bar_b2b_off; + if (size != NULL) + *size = bar->size - bar_b2b_off; + if (align != NULL) + *align = bar->size; + if (align_size != NULL) + *align_size = 1; + return (0); +} + +/* + * ntb_mw_set_trans() - set the translation of a memory window + * @ntb: NTB device context + * @idx: Memory window number + * @addr: The dma address local memory to expose to the peer + * @size: The size of the local memory to expose to the peer + * + * Set the translation of a memory window. The peer may access local memory + * through the window starting at the address, up to the size. The address + * must be aligned to the alignment specified by ntb_mw_get_range(). The size + * must be aligned to the size alignment specified by ntb_mw_get_range(). + * + * Return: Zero on success, otherwise an error number. + */ +int +ntb_mw_set_trans(struct ntb_softc *ntb, unsigned idx, bus_addr_t addr, + size_t size) +{ + struct ntb_pci_bar_info *bar; + uint64_t base, limit, reg_val; + size_t bar_size, mw_size; + uint32_t base_reg, xlat_reg, limit_reg; + enum ntb_bar bar_num; + + if (idx >= ntb_mw_count(ntb)) + return (EINVAL); + + bar_num = ntb_mw_to_bar(ntb, idx); + bar = &ntb->bar_info[bar_num]; + + bar_size = bar->size; + if (idx == ntb->b2b_mw_idx) + mw_size = bar_size - ntb->b2b_off; + else + mw_size = bar_size; + + /* Hardware requires that addr is aligned to bar size */ + if ((addr & (bar_size - 1)) != 0) + return (EINVAL); + + if (size > mw_size) + return (EINVAL); + + bar_get_xlat_params(ntb, bar_num, &base_reg, &xlat_reg, &limit_reg); + + limit = 0; + if (bar_is_64bit(ntb, bar_num)) { + base = ntb_reg_read(8, base_reg); + + if (limit_reg != 0 && size != mw_size) + limit = base + size; + + /* Set and verify translation address */ + ntb_reg_write(8, xlat_reg, addr); + reg_val = ntb_reg_read(8, xlat_reg); + if (reg_val != addr) { + ntb_reg_write(8, xlat_reg, 0); + return (EIO); + } + + /* Set and verify the limit */ + ntb_reg_write(8, limit_reg, limit); + reg_val = ntb_reg_read(8, limit_reg); + if (reg_val != limit) { + ntb_reg_write(8, limit_reg, base); + ntb_reg_write(8, xlat_reg, 0); + return (EIO); + } + } else { + /* Configure 32-bit (split) BAR MW */ + + if ((addr & ~UINT32_MAX) != 0) + return (EINVAL); + if (((addr + size) & ~UINT32_MAX) != 0) + return (EINVAL); + + base = ntb_reg_read(4, base_reg); + + if (limit_reg != 0 && size != mw_size) + limit = base + size; + + /* Set and verify translation address */ + ntb_reg_write(4, xlat_reg, addr); + reg_val = ntb_reg_read(4, xlat_reg); + if (reg_val != addr) { + ntb_reg_write(4, xlat_reg, 0); + return (EIO); + } + + /* Set and verify the limit */ + ntb_reg_write(4, limit_reg, limit); + reg_val = ntb_reg_read(4, limit_reg); + if (reg_val != limit) { + ntb_reg_write(4, limit_reg, base); + ntb_reg_write(4, xlat_reg, 0); + return (EIO); + } + } + return (0); } /** - * ntb_ring_doorbell() - Set the doorbell on the secondary/external side + * ntb_peer_db_set() - Set the doorbell on the secondary/external side * @ntb: pointer to ntb_softc instance - * @db: doorbell to ring + * @bit: doorbell bits to ring * * This function allows triggering of a doorbell on the secondary/external * side that will initiate an interrupt on the remote host - * - * RETURNS: An appropriate ERRNO error value on error, or zero for success. */ void -ntb_ring_doorbell(struct ntb_softc *ntb, unsigned int db) +ntb_peer_db_set(struct ntb_softc *ntb, uint64_t bit) { - if (ntb->type == NTB_SOC) - ntb_reg_write(8, ntb->reg_ofs.rdb, (uint64_t) 1 << db); - else { - if (HAS_FEATURE(NTB_REGS_THRU_MW)) - ntb_mw_write(2, XEON_SHADOW_PDOORBELL_OFFSET, - ((1 << ntb->bits_per_vector) - 1) << - (db * ntb->bits_per_vector)); - else - ntb_reg_write(2, ntb->reg_ofs.rdb, - ((1 << ntb->bits_per_vector) - 1) << - (db * ntb->bits_per_vector)); + if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP)) { + ntb_mw_write(2, XEON_SHADOW_PDOORBELL_OFFSET, bit); + return; } + + db_iowrite(ntb, ntb->peer_reg->db_bell, bit); +} + +/* + * ntb_get_peer_db_addr() - Return the address of the remote doorbell register, + * as well as the size of the register (via *sz_out). + * + * This function allows a caller using I/OAT DMA to chain the remote doorbell + * ring to its memory window write. + * + * Note that writing the peer doorbell via a memory window will *not* generate + * an interrupt on the remote host; that must be done seperately. + */ +bus_addr_t +ntb_get_peer_db_addr(struct ntb_softc *ntb, vm_size_t *sz_out) +{ + struct ntb_pci_bar_info *bar; + uint64_t regoff; + + KASSERT(sz_out != NULL, ("must be non-NULL")); + + if (!HAS_FEATURE(NTB_SDOORBELL_LOCKUP)) { + bar = &ntb->bar_info[NTB_CONFIG_BAR]; + regoff = ntb->peer_reg->db_bell; + } else { + KASSERT((HAS_FEATURE(NTB_SPLIT_BAR) && ntb->mw_count == 2) || + (!HAS_FEATURE(NTB_SPLIT_BAR) && ntb->mw_count == 1), + ("mw_count invalid after setup")); + KASSERT(ntb->b2b_mw_idx != B2B_MW_DISABLED, + ("invalid b2b idx")); + + bar = &ntb->bar_info[ntb_mw_to_bar(ntb, ntb->b2b_mw_idx)]; + regoff = XEON_SHADOW_PDOORBELL_OFFSET; + } + KASSERT(bar->pci_bus_tag != X86_BUS_SPACE_IO, ("uh oh")); + + *sz_out = ntb->reg->db_size; + /* HACK: Specific to current x86 bus implementation. */ + return ((uint64_t)bar->pci_bus_handle + regoff); } /** - * ntb_query_link_status() - return the hardware link status - * @ndev: pointer to ntb_device instance - * - * Returns true if the hardware is connected to the remote system + * ntb_link_is_up() - get the current ntb link state + * @ntb: NTB device context + * @speed: OUT - The link speed expressed as PCIe generation number + * @width: OUT - The link width expressed as the number of PCIe lanes * * RETURNS: true or false based on the hardware link state */ bool -ntb_query_link_status(struct ntb_softc *ntb) +ntb_link_is_up(struct ntb_softc *ntb, enum ntb_speed *speed, + enum ntb_width *width) { - return (ntb->link_status == NTB_LINK_UP); + if (speed != NULL) + *speed = ntb_link_sta_speed(ntb); + if (width != NULL) + *width = ntb_link_sta_width(ntb); + return (link_is_up(ntb)); } static void diff --git a/sys/dev/ntb/ntb_hw/ntb_hw.h b/sys/dev/ntb/ntb_hw/ntb_hw.h index b3eee7a35a0..c28596dc7fa 100644 --- a/sys/dev/ntb/ntb_hw/ntb_hw.h +++ b/sys/dev/ntb/ntb_hw/ntb_hw.h @@ -1,5 +1,6 @@ /*- * Copyright (C) 2013 Intel Corporation + * Copyright (C) 2015 EMC Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,53 +32,80 @@ struct ntb_softc; -#define NTB_NUM_MW 2 -#define NTB_LINK_DOWN 0 -#define NTB_LINK_UP 1 +#define NTB_MAX_NUM_MW 3 -enum ntb_hw_event { - NTB_EVENT_SW_EVENT0 = 0, - NTB_EVENT_SW_EVENT1, - NTB_EVENT_SW_EVENT2, - NTB_EVENT_HW_ERROR, - NTB_EVENT_HW_LINK_UP, - NTB_EVENT_HW_LINK_DOWN, +enum ntb_speed { + NTB_SPEED_AUTO = -1, + NTB_SPEED_NONE = 0, + NTB_SPEED_GEN1 = 1, + NTB_SPEED_GEN2 = 2, + NTB_SPEED_GEN3 = 3, +}; + +enum ntb_width { + NTB_WIDTH_AUTO = -1, + NTB_WIDTH_NONE = 0, + NTB_WIDTH_1 = 1, + NTB_WIDTH_2 = 2, + NTB_WIDTH_4 = 4, + NTB_WIDTH_8 = 8, + NTB_WIDTH_12 = 12, + NTB_WIDTH_16 = 16, + NTB_WIDTH_32 = 32, }; SYSCTL_DECL(_hw_ntb); -typedef void (*ntb_db_callback)(void *data, int db_num); -typedef void (*ntb_event_callback)(void *data, enum ntb_hw_event event); +typedef void (*ntb_db_callback)(void *data, int vector); +typedef void (*ntb_event_callback)(void *data); + +struct ntb_ctx_ops { + ntb_event_callback link_event; + ntb_db_callback db_event; +}; + +device_t ntb_get_device(struct ntb_softc *); + +bool ntb_link_is_up(struct ntb_softc *, enum ntb_speed *, enum ntb_width *); +void ntb_link_event(struct ntb_softc *); +int ntb_link_enable(struct ntb_softc *, enum ntb_speed, enum ntb_width); +int ntb_link_disable(struct ntb_softc *); + +int ntb_set_ctx(struct ntb_softc *, void *, const struct ntb_ctx_ops *); +void *ntb_get_ctx(struct ntb_softc *, const struct ntb_ctx_ops **); +void ntb_clear_ctx(struct ntb_softc *); + +uint8_t ntb_mw_count(struct ntb_softc *); +int ntb_mw_get_range(struct ntb_softc *, unsigned mw_idx, vm_paddr_t *base, + void **vbase, size_t *size, size_t *align, size_t *align_size); +int ntb_mw_set_trans(struct ntb_softc *, unsigned mw_idx, bus_addr_t, size_t); +int ntb_mw_clear_trans(struct ntb_softc *, unsigned mw_idx); -int ntb_register_event_callback(struct ntb_softc *ntb, ntb_event_callback func); -void ntb_unregister_event_callback(struct ntb_softc *ntb); -int ntb_register_db_callback(struct ntb_softc *ntb, unsigned int idx, - void *data, ntb_db_callback func); -void ntb_unregister_db_callback(struct ntb_softc *ntb, unsigned int idx); -void *ntb_find_transport(struct ntb_softc *ntb); -struct ntb_softc *ntb_register_transport(struct ntb_softc *ntb, - void *transport); -void ntb_unregister_transport(struct ntb_softc *ntb); uint8_t ntb_get_max_spads(struct ntb_softc *ntb); -int ntb_write_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t val); -int ntb_read_local_spad(struct ntb_softc *ntb, unsigned int idx, uint32_t *val); -int ntb_write_remote_spad(struct ntb_softc *ntb, unsigned int idx, +int ntb_spad_write(struct ntb_softc *ntb, unsigned int idx, uint32_t val); +int ntb_spad_read(struct ntb_softc *ntb, unsigned int idx, uint32_t *val); +int ntb_peer_spad_write(struct ntb_softc *ntb, unsigned int idx, uint32_t val); -int ntb_read_remote_spad(struct ntb_softc *ntb, unsigned int idx, +int ntb_peer_spad_read(struct ntb_softc *ntb, unsigned int idx, uint32_t *val); -void *ntb_get_mw_vbase(struct ntb_softc *ntb, unsigned int mw); -vm_paddr_t ntb_get_mw_pbase(struct ntb_softc *ntb, unsigned int mw); -u_long ntb_get_mw_size(struct ntb_softc *ntb, unsigned int mw); -void ntb_set_mw_addr(struct ntb_softc *ntb, unsigned int mw, uint64_t addr); -void ntb_ring_doorbell(struct ntb_softc *ntb, unsigned int db); -bool ntb_query_link_status(struct ntb_softc *ntb); -device_t ntb_get_device(struct ntb_softc *ntb); +uint64_t ntb_db_valid_mask(struct ntb_softc *); +bus_addr_t ntb_get_peer_db_addr(struct ntb_softc *, vm_size_t *sz_out); + +void ntb_db_clear(struct ntb_softc *, uint64_t bits); +void ntb_db_clear_mask(struct ntb_softc *, uint64_t bits); +uint64_t ntb_db_read(struct ntb_softc *); +void ntb_db_set_mask(struct ntb_softc *, uint64_t bits); +uint64_t ntb_db_vector_mask(struct ntb_softc *, int vector); +void ntb_peer_db_set(struct ntb_softc *, uint64_t bits); + +/* Hardware owns the low 32 bits of features. */ #define NTB_BAR_SIZE_4K (1 << 0) -/* REGS_THRU_MW is the equivalent of Linux's NTB_HWERR_SDOORBELL_LOCKUP */ -#define NTB_REGS_THRU_MW (1 << 1) +#define NTB_SDOORBELL_LOCKUP (1 << 1) #define NTB_SB01BASE_LOCKUP (1 << 2) #define NTB_B2BDOORBELL_BIT14 (1 << 3) +/* Software/configuration owns the top 32 bits. */ +#define NTB_SPLIT_BAR (1ull << 32) bool ntb_has_feature(struct ntb_softc *, uint64_t); #endif /* _NTB_HW_H_ */ diff --git a/sys/dev/ntb/ntb_hw/ntb_regs.h b/sys/dev/ntb/ntb_hw/ntb_regs.h index 2567f830e93..22d24227c44 100644 --- a/sys/dev/ntb/ntb_hw/ntb_regs.h +++ b/sys/dev/ntb/ntb_hw/ntb_regs.h @@ -1,5 +1,6 @@ /*- * Copyright (C) 2013 Intel Corporation + * Copyright (C) 2015 EMC Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,15 +33,17 @@ #define NTB_LINK_STATUS_ACTIVE 0x2000 #define NTB_LINK_SPEED_MASK 0x000f #define NTB_LINK_WIDTH_MASK 0x03f0 +#define NTB_LNK_STA_WIDTH(sta) (((sta) & NTB_LINK_WIDTH_MASK) >> 4) -#define XEON_MSIX_CNT 4 -#define XEON_MAX_SPADS 16 -#define XEON_MAX_COMPAT_SPADS 16 +#define XEON_SNB_MW_COUNT 2 +#define XEON_HSX_SPLIT_MW_COUNT 3 /* Reserve the uppermost bit for link interrupt */ -#define XEON_MAX_DB_BITS 15 -#define XEON_DB_BITS_PER_VEC 5 - -#define XEON_DB_HW_LINK 0x8000 +#define XEON_DB_COUNT 15 +#define XEON_DB_LINK 15 +#define XEON_DB_MSIX_VECTOR_COUNT 4 +#define XEON_DB_MSIX_VECTOR_SHIFT 5 +#define XEON_DB_LINK_BIT (1 << XEON_DB_LINK) +#define XEON_SPAD_COUNT 16 #define XEON_PCICMD_OFFSET 0x0504 #define XEON_DEVCTRL_OFFSET 0x0598 @@ -49,15 +52,20 @@ #define XEON_PBAR2LMT_OFFSET 0x0000 #define XEON_PBAR4LMT_OFFSET 0x0008 +#define XEON_PBAR5LMT_OFFSET 0x000c #define XEON_PBAR2XLAT_OFFSET 0x0010 #define XEON_PBAR4XLAT_OFFSET 0x0018 +#define XEON_PBAR5XLAT_OFFSET 0x001c #define XEON_SBAR2LMT_OFFSET 0x0020 #define XEON_SBAR4LMT_OFFSET 0x0028 +#define XEON_SBAR5LMT_OFFSET 0x002c #define XEON_SBAR2XLAT_OFFSET 0x0030 #define XEON_SBAR4XLAT_OFFSET 0x0038 +#define XEON_SBAR5XLAT_OFFSET 0x003c #define XEON_SBAR0BASE_OFFSET 0x0040 #define XEON_SBAR2BASE_OFFSET 0x0048 #define XEON_SBAR4BASE_OFFSET 0x0050 +#define XEON_SBAR5BASE_OFFSET 0x0054 #define XEON_NTBCNTL_OFFSET 0x0058 #define XEON_SBDF_OFFSET 0x005c #define XEON_PDOORBELL_OFFSET 0x0060 @@ -73,10 +81,11 @@ #define XEON_B2B_XLAT_OFFSETL 0x0144 #define XEON_B2B_XLAT_OFFSETU 0x0148 -#define SOC_MSIX_CNT 34 -#define SOC_MAX_SPADS 16 -#define SOC_MAX_DB_BITS 34 -#define SOC_DB_BITS_PER_VEC 1 +#define SOC_MW_COUNT 2 +#define SOC_DB_COUNT 34 +#define SOC_DB_MSIX_VECTOR_COUNT 34 +#define SOC_DB_MSIX_VECTOR_SHIFT 1 +#define SOC_SPAD_COUNT 16 #define SOC_PCICMD_OFFSET 0xb004 #define SOC_MBAR23_OFFSET 0xb018 @@ -110,22 +119,32 @@ #define SOC_LTSSMSTATEJMP_OFFSET (SOC_IP_BASE + 0x3040) #define SOC_IBSTERRRCRVSTS0_OFFSET (SOC_IP_BASE + 0x3324) -#define SOC_DESKEWSTS_DBERR (1 << 15) +#define SOC_DESKEWSTS_DBERR (1 << 15) #define SOC_LTSSMERRSTS0_UNEXPECTEDEI (1 << 20) #define SOC_LTSSMSTATEJMP_FORCEDETECT (1 << 2) -#define SOC_IBIST_ERR_OFLOW 0x7fff7fff +#define SOC_IBIST_ERR_OFLOW 0x7fff7fff -#define NTB_CNTL_CFG_LOCK (1 << 0) -#define NTB_CNTL_LINK_DISABLE (1 << 1) -#define NTB_CNTL_BAR23_SNOOP (1 << 2) -#define NTB_CNTL_BAR45_SNOOP (1 << 6) -#define SOC_CNTL_LINK_DOWN (1 << 16) +#define NTB_CNTL_CFG_LOCK (1 << 0) +#define NTB_CNTL_LINK_DISABLE (1 << 1) +#define NTB_CNTL_S2P_BAR23_SNOOP (1 << 2) +#define NTB_CNTL_P2S_BAR23_SNOOP (1 << 4) +#define NTB_CNTL_S2P_BAR4_SNOOP (1 << 6) +#define NTB_CNTL_P2S_BAR4_SNOOP (1 << 8) +#define NTB_CNTL_S2P_BAR5_SNOOP (1 << 12) +#define NTB_CNTL_P2S_BAR5_SNOOP (1 << 14) +#define SOC_CNTL_LINK_DOWN (1 << 16) #define XEON_PBAR23SZ_OFFSET 0x00d0 #define XEON_PBAR45SZ_OFFSET 0x00d1 +#define XEON_PBAR4SZ_OFFSET 0x00d1 +#define XEON_PBAR5SZ_OFFSET 0x00d5 +#define XEON_SBAR23SZ_OFFSET 0x00d2 +#define XEON_SBAR4SZ_OFFSET 0x00d3 +#define XEON_SBAR5SZ_OFFSET 0x00d6 #define NTB_PPD_OFFSET 0x00d4 #define XEON_PPD_CONN_TYPE 0x0003 #define XEON_PPD_DEV_TYPE 0x0010 +#define XEON_PPD_SPLIT_BAR 0x0040 #define SOC_PPD_INIT_LINK 0x0008 #define SOC_PPD_CONN_TYPE 0x0300 #define SOC_PPD_DEV_TYPE 0x1000 @@ -137,16 +156,20 @@ #define NTB_DEV_DSD 1 #define NTB_DEV_USD 0 -#define PBAR2XLAT_USD_ADDR 0x0000004000000000ull -#define PBAR4XLAT_USD_ADDR 0x0000008000000000ull -#define MBAR01_USD_ADDR 0x000000210000000cull -#define MBAR23_USD_ADDR 0x000000410000000cull -#define MBAR45_USD_ADDR 0x000000810000000cull -#define PBAR2XLAT_DSD_ADDR 0x0000004100000000ull -#define PBAR4XLAT_DSD_ADDR 0x0000008100000000ull -#define MBAR01_DSD_ADDR 0x000000200000000cull -#define MBAR23_DSD_ADDR 0x000000400000000cull -#define MBAR45_DSD_ADDR 0x000000800000000cull +/* All addresses are in low 32-bit space so 32-bit BARs can function */ +#define XEON_B2B_BAR0_USD_ADDR 0x1000000000000000ull +#define XEON_B2B_BAR2_USD_ADDR64 0x2000000000000000ull +#define XEON_B2B_BAR4_USD_ADDR64 0x4000000000000000ull +#define XEON_B2B_BAR4_USD_ADDR32 0x20000000ull +#define XEON_B2B_BAR5_USD_ADDR32 0x40000000ull +#define XEON_B2B_BAR0_DSD_ADDR 0x9000000000000000ull +#define XEON_B2B_BAR2_DSD_ADDR64 0xa000000000000000ull +#define XEON_B2B_BAR4_DSD_ADDR64 0xc000000000000000ull +#define XEON_B2B_BAR4_DSD_ADDR32 0xa0000000ull +#define XEON_B2B_BAR5_DSD_ADDR32 0xc0000000ull + +/* The peer ntb secondary config space is 32KB fixed size */ +#define XEON_B2B_MIN_SIZE 0x8000 /* XEON Shadowed MMIO Space */ #define XEON_SHADOW_PDOORBELL_OFFSET 0x60 diff --git a/sys/dev/otus/if_otus.c b/sys/dev/otus/if_otus.c index 1808c950c49..8756419747e 100644 --- a/sys/dev/otus/if_otus.c +++ b/sys/dev/otus/if_otus.c @@ -164,7 +164,8 @@ int otus_cmd(struct otus_softc *, uint8_t, const void *, int, void *, int); void otus_write(struct otus_softc *, uint32_t, uint32_t); int otus_write_barrier(struct otus_softc *); -struct ieee80211_node *otus_node_alloc(struct ieee80211com *); +static struct ieee80211_node *otus_node_alloc(struct ieee80211vap *vap, + const uint8_t mac[IEEE80211_ADDR_LEN]); int otus_media_change(struct ifnet *); int otus_read_eeprom(struct otus_softc *); void otus_newassoc(struct ieee80211_node *, int); @@ -759,6 +760,7 @@ otus_attachhook(struct otus_softc *sc) IEEE80211_C_WME | /* WME/QoS */ IEEE80211_C_SHSLOT | /* Short slot time supported. */ IEEE80211_C_FF | /* Atheros fast-frames supported. */ + IEEE80211_C_MONITOR | IEEE80211_C_WPA; /* WPA/RSN. */ /* XXX TODO: 11n */ @@ -813,6 +815,7 @@ otus_attachhook(struct otus_softc *sc) ic->ic_ampdu_enable = otus_ampdu_enable; ic->ic_wme.wme_update = otus_wme_update; ic->ic_newassoc = otus_newassoc; + ic->ic_node_alloc = otus_node_alloc; #ifdef notyet ic->ic_set_key = otus_set_key; @@ -1386,10 +1389,12 @@ otus_write_barrier(struct otus_softc *sc) return error; } -struct ieee80211_node * -otus_node_alloc(struct ieee80211com *ic) +static struct ieee80211_node * +otus_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { - return malloc(sizeof (struct otus_node), M_DEVBUF, M_NOWAIT | M_ZERO); + + return malloc(sizeof (struct otus_node), M_80211_NODE, + M_NOWAIT | M_ZERO); } #if 0 @@ -2229,6 +2234,9 @@ otus_tx(struct otus_softc *sc, struct ieee80211_node *ni, struct mbuf *m, (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) { /* Get lowest rate */ rate = otus_rate_to_hw_rate(sc, 0); + } else if (m->m_flags & M_EAPOL) { + /* Get lowest rate */ + rate = otus_rate_to_hw_rate(sc, 0); } else { (void) ieee80211_ratectl_rate(ni, NULL, 0); rate = otus_rate_to_hw_rate(sc, ni->ni_txrate); @@ -3094,13 +3102,22 @@ otus_init(struct otus_softc *sc) } #endif - /* Expect STA operation */ - otus_write(sc, 0x1c3700, 0x0f000002); - otus_write(sc, 0x1c3c40, 0x1); + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + otus_write(sc, 0x1c3700, 0x0f000002); + otus_write(sc, 0x1c3c40, 0x1); + break; + case IEEE80211_M_MONITOR: + otus_write(sc, 0x1c368c, 0xffffffff); + break; + default: + break; + } /* XXX ic_opmode? */ otus_write(sc, AR_MAC_REG_SNIFFER, (ic->ic_opmode == IEEE80211_M_MONITOR) ? 0x2000001 : 0x2000000); + (void)otus_write_barrier(sc); sc->bb_reset = 1; /* Force cold reset. */ diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 98b6b53a072..39d311f6509 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -535,7 +535,7 @@ pci_romsize(uint64_t testval) } return (ln2size); } - + /* return log2 of address range supported by map register */ static int @@ -1706,7 +1706,7 @@ pci_remap_msix_method(device_t dev, device_t child, int count, free(used, M_DEVBUF); return (EINVAL); } - + /* Make sure none of the resources are allocated. */ for (i = 0; i < msix->msix_table_len; i++) { if (msix->msix_table[i].mte_vector == 0) @@ -2003,7 +2003,7 @@ pci_remap_intr_method(device_t bus, device_t dev, u_int irq) struct msix_table_entry *mte; struct msix_vector *mv; uint64_t addr; - uint32_t data; + uint32_t data; int error, i, j; /* @@ -4830,7 +4830,7 @@ pci_deactivate_resource(device_t dev, device_t child, int type, if (error) return (error); - /* Disable decoding for device ROMs. */ + /* Disable decoding for device ROMs. */ if (device_get_parent(child) == dev) { dinfo = device_get_ivars(child); if (type == SYS_RES_MEMORY && PCIR_IS_BIOS(&dinfo->cfg, rid)) diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c index 11a4bcac795..f5a5b7415f0 100644 --- a/sys/dev/pci/pci_pci.c +++ b/sys/dev/pci/pci_pci.c @@ -57,7 +57,7 @@ static int pcib_resume(device_t dev); static int pcib_power_for_sleep(device_t pcib, device_t dev, int *pstate); static uint16_t pcib_ari_get_rid(device_t pcib, device_t dev); -static uint32_t pcib_read_config(device_t dev, u_int b, u_int s, +static uint32_t pcib_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width); static void pcib_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, uint32_t val, int width); @@ -266,7 +266,7 @@ pcib_add_window_resources(struct pcib_window *w, struct resource **res, free(w->res, M_DEVBUF); w->res = newarray; w->count += count; - + for (i = 0; i < count; i++) { error = rman_manage_region(&w->rman, rman_get_start(res[i]), rman_get_end(res[i])); @@ -783,7 +783,7 @@ pcib_get_mem_decode(struct pcib_softc *sc) sc->pmembase = PCI_PPBMEMBASE(0, pmemlow); pmemlow = pci_read_config(dev, PCIR_PMLIMITL_1, 2); - if ((pmemlow & PCIM_BRPM_MASK) == PCIM_BRPM_64) + if ((pmemlow & PCIM_BRPM_MASK) == PCIM_BRPM_64) sc->pmemlimit = PCI_PPBMEMLIMIT( pci_read_config(dev, PCIR_PMLIMITH_1, 4), pmemlow); else @@ -1126,7 +1126,7 @@ int pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct pcib_softc *sc = device_get_softc(dev); - + switch (which) { case PCIB_IVAR_DOMAIN: *result = sc->domain; @@ -1243,9 +1243,9 @@ pcib_alloc_new_window(struct pcib_softc *sc, struct pcib_window *w, int type, return (0); } } - return (ENOSPC); + return (ENOSPC); } - + wmask = (1ul << w->step) - 1; if (RF_ALIGNMENT(flags) < w->step) { flags &= ~RF_ALIGNMENT_MASK; @@ -1337,7 +1337,7 @@ pcib_expand_window(struct pcib_softc *sc, struct pcib_window *w, int type, KASSERT(w->base == rman_get_start(res), ("existing resource mismatch")); force_64k_base = 0; - } + } error = bus_adjust_resource(sc->dev, type, res, force_64k_base ? rman_get_start(res) : base, limit); @@ -1657,7 +1657,7 @@ pcib_release_resource(device_t dev, device_t child, int type, int rid, * is set up to, or capable of handling them. */ struct resource * -pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, +pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct pcib_softc *sc = device_get_softc(dev); @@ -1928,7 +1928,7 @@ pcib_route_interrupt(device_t pcib, device_t dev, int pin) int parent_intpin; int intnum; - /* + /* * * The PCI standard defines a swizzle of the child-side device/intpin to * the parent-side intpin as follows. @@ -2116,4 +2116,3 @@ pcib_try_enable_ari(device_t pcib, device_t dev) return (0); } - diff --git a/sys/dev/pci/pcib_if.m b/sys/dev/pci/pcib_if.m index 7c23ddc6973..6fdc0f42518 100644 --- a/sys/dev/pci/pcib_if.m +++ b/sys/dev/pci/pcib_if.m @@ -97,7 +97,7 @@ METHOD void write_config { }; # -# Route an interrupt. Returns a value suitable for stuffing into +# Route an interrupt. Returns a value suitable for stuffing into # a device's interrupt register. # METHOD int route_interrupt { @@ -206,4 +206,3 @@ METHOD void decode_rid { int *slot; int *func; } DEFAULT pcib_decode_rid; - diff --git a/sys/dev/sdhci/sdhci.c b/sys/dev/sdhci/sdhci.c index b1fae2916cc..e625d4f11f3 100644 --- a/sys/dev/sdhci/sdhci.c +++ b/sys/dev/sdhci/sdhci.c @@ -89,6 +89,19 @@ static void sdhci_card_task(void *, int); #define SDHCI_200_MAX_DIVIDER 256 #define SDHCI_300_MAX_DIVIDER 2046 +/* + * Broadcom BCM577xx Controller Constants + */ +#define BCM577XX_DEFAULT_MAX_DIVIDER 256 /* Maximum divider supported by the default clock source. */ +#define BCM577XX_ALT_CLOCK_BASE 63000000 /* Alternative clock's base frequency. */ + +#define BCM577XX_HOST_CONTROL 0x198 +#define BCM577XX_CTRL_CLKSEL_MASK 0xFFFFCFFF +#define BCM577XX_CTRL_CLKSEL_SHIFT 12 +#define BCM577XX_CTRL_CLKSEL_DEFAULT 0x0 +#define BCM577XX_CTRL_CLKSEL_64MHZ 0x3 + + static void sdhci_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { @@ -228,6 +241,8 @@ sdhci_init(struct sdhci_slot *slot) static void sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock) { + uint32_t clk_base; + uint32_t clk_sel; uint32_t res; uint16_t clk; uint16_t div; @@ -243,6 +258,22 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock) /* If no clock requested - left it so. */ if (clock == 0) return; + + /* Determine the clock base frequency */ + clk_base = slot->max_clk; + if (slot->quirks & SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC) { + clk_sel = RD2(slot, BCM577XX_HOST_CONTROL) & BCM577XX_CTRL_CLKSEL_MASK; + + /* Select clock source appropriate for the requested frequency. */ + if ((clk_base / BCM577XX_DEFAULT_MAX_DIVIDER) > clock) { + clk_base = BCM577XX_ALT_CLOCK_BASE; + clk_sel |= (BCM577XX_CTRL_CLKSEL_64MHZ << BCM577XX_CTRL_CLKSEL_SHIFT); + } else { + clk_sel |= (BCM577XX_CTRL_CLKSEL_DEFAULT << BCM577XX_CTRL_CLKSEL_SHIFT); + } + + WR2(slot, BCM577XX_HOST_CONTROL, clk_sel); + } /* Recalculate timeout clock frequency based on the new sd clock. */ if (slot->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) @@ -250,7 +281,7 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock) if (slot->version < SDHCI_SPEC_300) { /* Looking for highest freq <= clock. */ - res = slot->max_clk; + res = clk_base; for (div = 1; div < SDHCI_200_MAX_DIVIDER; div <<= 1) { if (res <= clock) break; @@ -261,11 +292,11 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock) } else { /* Version 3.0 divisors are multiples of two up to 1023*2 */ - if (clock >= slot->max_clk) + if (clock >= clk_base) div = 0; else { for (div = 2; div < SDHCI_300_MAX_DIVIDER; div += 2) { - if ((slot->max_clk / div) <= clock) + if ((clk_base / div) <= clock) break; } } @@ -273,8 +304,8 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock) } if (bootverbose || sdhci_debug) - slot_printf(slot, "Divider %d for freq %d (max %d)\n", - div, clock, slot->max_clk); + slot_printf(slot, "Divider %d for freq %d (base %d)\n", + div, clock, clk_base); /* Now we have got divider, set it. */ clk = (div & SDHCI_DIVIDER_MASK) << SDHCI_DIVIDER_SHIFT; diff --git a/sys/dev/sdhci/sdhci.h b/sys/dev/sdhci/sdhci.h index 3c27d0c3cd2..7683c1dbf30 100644 --- a/sys/dev/sdhci/sdhci.h +++ b/sys/dev/sdhci/sdhci.h @@ -63,6 +63,8 @@ #define SDHCI_QUIRK_WAITFOR_RESET_ASSERTED (1<<14) /* Leave controller in standard mode when putting card in HS mode. */ #define SDHCI_QUIRK_DONT_SET_HISPD_BIT (1<<15) +/* Alternate clock source is required when supplying a 400 KHz clock. */ +#define SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC (1<<16) /* * Controller registers diff --git a/sys/dev/sdhci/sdhci_pci.c b/sys/dev/sdhci/sdhci_pci.c index b818a50bde6..b149bb6fa49 100644 --- a/sys/dev/sdhci/sdhci_pci.c +++ b/sys/dev/sdhci/sdhci_pci.c @@ -105,6 +105,8 @@ static const struct sdhci_device { { 0x2381197B, 0xffff, "JMicron JMB38X SD", SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_RESET_AFTER_REQUEST }, + { 0x16bc14e4, 0xffff, "Broadcom BCM577xx SDXC/MMC Card Reader", + SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC }, { 0, 0xffff, NULL, 0 } }; @@ -334,6 +336,8 @@ sdhci_pci_attach(device_t dev) device_printf(dev, "Can't allocate memory for slot %d\n", i); continue; } + + slot->quirks = sc->quirks; if (sdhci_init_slot(dev, slot, i) != 0) continue; diff --git a/sys/dev/usb/controller/xhci_pci.c b/sys/dev/usb/controller/xhci_pci.c index 492fd3cfdac..e6f937f892c 100644 --- a/sys/dev/usb/controller/xhci_pci.c +++ b/sys/dev/usb/controller/xhci_pci.c @@ -115,6 +115,8 @@ xhci_pci_match(device_t self) return ("Intel Lynx Point USB 3.0 controller"); case 0x8cb18086: return ("Intel Wildcat Point USB 3.0 controller"); + case 0x9cb18086: + return ("Broadwell Integrated PCH-LP chipset USB 3.0 controller"); case 0xa01b177d: return ("Cavium ThunderX USB 3.0 controller"); @@ -216,6 +218,7 @@ xhci_pci_attach(device_t self) case 0x1e318086: /* Panther Point */ case 0x8c318086: /* Lynx Point */ case 0x8cb18086: /* Wildcat Point */ + case 0x9cb18086: /* Broadwell Mobile Integrated */ /* * On Intel chipsets, reroute ports from EHCI to XHCI * controller and use a different IMOD value. diff --git a/sys/dev/vnic/lmac_if.m b/sys/dev/vnic/lmac_if.m new file mode 100644 index 00000000000..e8770f22ce1 --- /dev/null +++ b/sys/dev/vnic/lmac_if.m @@ -0,0 +1,102 @@ +#- +# Copyright (c) 2015 The FreeBSD Foundation +# All rights reserved. +# +# This software was developed by Semihalf under +# the sponsorship of the FreeBSD Foundation. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +# LMAC (BGX controller) interface description +# + +INTERFACE lmac; + +CODE { + static int null_lmac_media_status(device_t dev, int lmacid, int *link, + int *duplex, int *speed) + { + return (ENXIO); + } + + static int null_lmac_media_change(device_t dev, int lmacid, int link, + int duplex, int speed) + { + return (ENXIO); + } + + static int null_lmac_phy_connect(device_t dev, int lmacid, int phy) + { + return (ENXIO); + } + + static int null_lmac_phy_disconnect(device_t dev, int lmacid, int phy) + { + return (ENXIO); + } +}; + +# Get link status +# +# 0 : Success +# +METHOD int media_status { + device_t dev; + int lmacid; + int * link; + int * duplex; + int * speed; +} DEFAULT null_lmac_media_status; + +# Change link status +# +# 0 : Success +# +METHOD int media_change { + device_t dev; + int lmacid; + int link; + int duplex; + int speed; +} DEFAULT null_lmac_media_change; + +# Connect PHY +# +# 0 : Success +# +METHOD int phy_connect { + device_t dev; + int lmacid; + int phy; +} DEFAULT null_lmac_phy_connect; + +# Disconnect PHY +# +# 0 : Success +# +METHOD int phy_disconnect { + device_t dev; + int lmacid; + int phy; +} DEFAULT null_lmac_phy_disconnect; diff --git a/sys/dev/vnic/nic.h b/sys/dev/vnic/nic.h new file mode 100644 index 00000000000..f2a997aa88e --- /dev/null +++ b/sys/dev/vnic/nic.h @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2015 Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef NIC_H +#define NIC_H + +/* PCI vendor ID */ +#define PCI_VENDOR_ID_CAVIUM 0x177D +/* PCI device IDs */ +#define PCI_DEVICE_ID_THUNDER_NIC_PF 0xA01E +#define PCI_DEVICE_ID_THUNDER_PASS1_NIC_VF 0x0011 +#define PCI_DEVICE_ID_THUNDER_NIC_VF 0xA034 +#define PCI_DEVICE_ID_THUNDER_BGX 0xA026 + +/* PCI BAR nos */ +#define PCI_CFG_REG_BAR_NUM 0 +#define PCI_MSIX_REG_BAR_NUM 4 + +/* NIC SRIOV VF count */ +#define MAX_NUM_VFS_SUPPORTED 128 +#define DEFAULT_NUM_VF_ENABLED 8 + +#define NIC_TNS_BYPASS_MODE 0 +#define NIC_TNS_MODE 1 + +/* NIC priv flags */ +#define NIC_SRIOV_ENABLED (1 << 0) +#define NIC_TNS_ENABLED (1 << 1) + +/* ARM64TODO */ +#if 0 +/* VNIC HW optimiation features */ +#define VNIC_RSS_SUPPORT +#define VNIC_MULTI_QSET_SUPPORT +#endif + +/* Min/Max packet size */ +#define NIC_HW_MIN_FRS 64 +#define NIC_HW_MAX_FRS 9200 /* 9216 max packet including FCS */ + +/* Max pkinds */ +#define NIC_MAX_PKIND 16 + +/* + * Rx Channels */ +/* Receive channel configuration in TNS bypass mode + * Below is configuration in TNS bypass mode + * BGX0-LMAC0-CHAN0 - VNIC CHAN0 + * BGX0-LMAC1-CHAN0 - VNIC CHAN16 + * ... + * BGX1-LMAC0-CHAN0 - VNIC CHAN128 + * ... + * BGX1-LMAC3-CHAN0 - VNIC CHAN174 + */ +#define NIC_INTF_COUNT 2 /* Interfaces btw VNIC and TNS/BGX */ +#define NIC_CHANS_PER_INF 128 +#define NIC_MAX_CHANS (NIC_INTF_COUNT * NIC_CHANS_PER_INF) +#define NIC_CPI_COUNT 2048 /* No of channel parse indices */ + +/* TNS bypass mode: 1-1 mapping between VNIC and BGX:LMAC */ +#define NIC_MAX_BGX MAX_BGX_PER_CN88XX +#define NIC_CPI_PER_BGX (NIC_CPI_COUNT / NIC_MAX_BGX) +#define NIC_MAX_CPI_PER_LMAC 64 /* Max when CPI_ALG is IP diffserv */ +#define NIC_RSSI_PER_BGX (NIC_RSSI_COUNT / NIC_MAX_BGX) + +/* Tx scheduling */ +#define NIC_MAX_TL4 1024 +#define NIC_MAX_TL4_SHAPERS 256 /* 1 shaper for 4 TL4s */ +#define NIC_MAX_TL3 256 +#define NIC_MAX_TL3_SHAPERS 64 /* 1 shaper for 4 TL3s */ +#define NIC_MAX_TL2 64 +#define NIC_MAX_TL2_SHAPERS 2 /* 1 shaper for 32 TL2s */ +#define NIC_MAX_TL1 2 + +/* TNS bypass mode */ +#define NIC_TL2_PER_BGX 32 +#define NIC_TL4_PER_BGX (NIC_MAX_TL4 / NIC_MAX_BGX) +#define NIC_TL4_PER_LMAC (NIC_MAX_TL4 / NIC_CHANS_PER_INF) + +/* NIC VF Interrupts */ +#define NICVF_INTR_CQ 0 +#define NICVF_INTR_SQ 1 +#define NICVF_INTR_RBDR 2 +#define NICVF_INTR_PKT_DROP 3 +#define NICVF_INTR_TCP_TIMER 4 +#define NICVF_INTR_MBOX 5 +#define NICVF_INTR_QS_ERR 6 + +#define NICVF_INTR_CQ_SHIFT 0 +#define NICVF_INTR_SQ_SHIFT 8 +#define NICVF_INTR_RBDR_SHIFT 16 +#define NICVF_INTR_PKT_DROP_SHIFT 20 +#define NICVF_INTR_TCP_TIMER_SHIFT 21 +#define NICVF_INTR_MBOX_SHIFT 22 +#define NICVF_INTR_QS_ERR_SHIFT 23 + +#define NICVF_INTR_CQ_MASK (0xFF << NICVF_INTR_CQ_SHIFT) +#define NICVF_INTR_SQ_MASK (0xFF << NICVF_INTR_SQ_SHIFT) +#define NICVF_INTR_RBDR_MASK (0x03 << NICVF_INTR_RBDR_SHIFT) +#define NICVF_INTR_PKT_DROP_MASK (1 << NICVF_INTR_PKT_DROP_SHIFT) +#define NICVF_INTR_TCP_TIMER_MASK (1 << NICVF_INTR_TCP_TIMER_SHIFT) +#define NICVF_INTR_MBOX_MASK (1 << NICVF_INTR_MBOX_SHIFT) +#define NICVF_INTR_QS_ERR_MASK (1 << NICVF_INTR_QS_ERR_SHIFT) + +/* MSI-X interrupts */ +#define NIC_PF_MSIX_VECTORS 10 +#define NIC_VF_MSIX_VECTORS 20 + +#define NIC_PF_INTR_ID_ECC0_SBE 0 +#define NIC_PF_INTR_ID_ECC0_DBE 1 +#define NIC_PF_INTR_ID_ECC1_SBE 2 +#define NIC_PF_INTR_ID_ECC1_DBE 3 +#define NIC_PF_INTR_ID_ECC2_SBE 4 +#define NIC_PF_INTR_ID_ECC2_DBE 5 +#define NIC_PF_INTR_ID_ECC3_SBE 6 +#define NIC_PF_INTR_ID_ECC3_DBE 7 +#define NIC_PF_INTR_ID_MBOX0 8 +#define NIC_PF_INTR_ID_MBOX1 9 + +struct msix_entry { + struct resource * irq_res; + void * handle; +}; + +/* + * Global timer for CQ timer thresh interrupts + * Calculated for SCLK of 700Mhz + * value written should be a 1/16th of what is expected + * + * 1 tick per 0.05usec = value of 2.2 + * This 10% would be covered in CQ timer thresh value + */ +#define NICPF_CLK_PER_INT_TICK 2 + +/* + * Time to wait before we decide that a SQ is stuck. + * + * Since both pkt rx and tx notifications are done with same CQ, + * when packets are being received at very high rate (eg: L2 forwarding) + * then freeing transmitted skbs will be delayed and watchdog + * will kick in, resetting interface. Hence keeping this value high. + */ +#define NICVF_TX_TIMEOUT (50 * HZ) + +#define NIC_RSSI_COUNT 4096 /* Total no of RSS indices */ +#define NIC_MAX_RSS_HASH_BITS 8 +#define NIC_MAX_RSS_IDR_TBL_SIZE (1 << NIC_MAX_RSS_HASH_BITS) +#define RSS_HASH_KEY_SIZE 5 /* 320 bit key */ + +enum rx_stats_reg_offset { + RX_OCTS = 0x0, + RX_UCAST = 0x1, + RX_BCAST = 0x2, + RX_MCAST = 0x3, + RX_RED = 0x4, + RX_RED_OCTS = 0x5, + RX_ORUN = 0x6, + RX_ORUN_OCTS = 0x7, + RX_FCS = 0x8, + RX_L2ERR = 0x9, + RX_DRP_BCAST = 0xa, + RX_DRP_MCAST = 0xb, + RX_DRP_L3BCAST = 0xc, + RX_DRP_L3MCAST = 0xd, + RX_STATS_ENUM_LAST, +}; + +enum tx_stats_reg_offset { + TX_OCTS = 0x0, + TX_UCAST = 0x1, + TX_BCAST = 0x2, + TX_MCAST = 0x3, + TX_DROP = 0x4, + TX_STATS_ENUM_LAST, +}; + +struct nicvf_hw_stats { + uint64_t rx_bytes; + uint64_t rx_ucast_frames; + uint64_t rx_bcast_frames; + uint64_t rx_mcast_frames; + uint64_t rx_fcs_errors; + uint64_t rx_l2_errors; + uint64_t rx_drop_red; + uint64_t rx_drop_red_bytes; + uint64_t rx_drop_overrun; + uint64_t rx_drop_overrun_bytes; + uint64_t rx_drop_bcast; + uint64_t rx_drop_mcast; + uint64_t rx_drop_l3_bcast; + uint64_t rx_drop_l3_mcast; + uint64_t rx_bgx_truncated_pkts; + uint64_t rx_jabber_errs; + uint64_t rx_fcs_errs; + uint64_t rx_bgx_errs; + uint64_t rx_prel2_errs; + uint64_t rx_l2_hdr_malformed; + uint64_t rx_oversize; + uint64_t rx_undersize; + uint64_t rx_l2_len_mismatch; + uint64_t rx_l2_pclp; + uint64_t rx_ip_ver_errs; + uint64_t rx_ip_csum_errs; + uint64_t rx_ip_hdr_malformed; + uint64_t rx_ip_payload_malformed; + uint64_t rx_ip_ttl_errs; + uint64_t rx_l3_pclp; + uint64_t rx_l4_malformed; + uint64_t rx_l4_csum_errs; + uint64_t rx_udp_len_errs; + uint64_t rx_l4_port_errs; + uint64_t rx_tcp_flag_errs; + uint64_t rx_tcp_offset_errs; + uint64_t rx_l4_pclp; + uint64_t rx_truncated_pkts; + + uint64_t tx_bytes_ok; + uint64_t tx_ucast_frames_ok; + uint64_t tx_bcast_frames_ok; + uint64_t tx_mcast_frames_ok; + uint64_t tx_drops; +}; + +struct nicvf_drv_stats { + /* Rx */ + uint64_t rx_frames_ok; + uint64_t rx_frames_64; + uint64_t rx_frames_127; + uint64_t rx_frames_255; + uint64_t rx_frames_511; + uint64_t rx_frames_1023; + uint64_t rx_frames_1518; + uint64_t rx_frames_jumbo; + uint64_t rx_drops; + + /* Tx */ + uint64_t tx_frames_ok; + uint64_t tx_drops; + uint64_t tx_tso; + uint64_t txq_stop; + uint64_t txq_wake; +}; + +struct nicvf { + struct nicvf *pnicvf; + device_t dev; + + struct ifnet * ifp; + struct sx core_sx; + struct ifmedia if_media; + uint32_t if_flags; + + uint8_t hwaddr[ETHER_ADDR_LEN]; + uint8_t vf_id; + uint8_t node; + boolean_t tns_mode:1; + boolean_t sqs_mode:1; + bool loopback_supported:1; + uint16_t mtu; + struct queue_set *qs; + uint8_t rx_queues; + uint8_t tx_queues; + uint8_t max_queues; + struct resource *reg_base; + boolean_t link_up; + uint8_t duplex; + uint32_t speed; + uint8_t cpi_alg; + /* Interrupt coalescing settings */ + uint32_t cq_coalesce_usecs; + + uint32_t msg_enable; + struct nicvf_hw_stats hw_stats; + struct nicvf_drv_stats drv_stats; + struct bgx_stats bgx_stats; + + /* Interface statistics */ + struct callout stats_callout; + struct mtx stats_mtx; + + /* MSI-X */ + boolean_t msix_enabled; + uint8_t num_vec; + struct msix_entry msix_entries[NIC_VF_MSIX_VECTORS]; + struct resource * msix_table_res; + char irq_name[NIC_VF_MSIX_VECTORS][20]; + boolean_t irq_allocated[NIC_VF_MSIX_VECTORS]; + + /* VF <-> PF mailbox communication */ + boolean_t pf_acked; + boolean_t pf_nacked; +} __aligned(CACHE_LINE_SIZE); + +/* + * PF <--> VF Mailbox communication + * Eight 64bit registers are shared between PF and VF. + * Separate set for each VF. + * Writing '1' into last register mbx7 means end of message. + */ + +/* PF <--> VF mailbox communication */ +#define NIC_PF_VF_MAILBOX_SIZE 2 +#define NIC_MBOX_MSG_TIMEOUT 2000 /* ms */ + +/* Mailbox message types */ +#define NIC_MBOX_MSG_READY 0x01 /* Is PF ready to rcv msgs */ +#define NIC_MBOX_MSG_ACK 0x02 /* ACK the message received */ +#define NIC_MBOX_MSG_NACK 0x03 /* NACK the message received */ +#define NIC_MBOX_MSG_QS_CFG 0x04 /* Configure Qset */ +#define NIC_MBOX_MSG_RQ_CFG 0x05 /* Configure receive queue */ +#define NIC_MBOX_MSG_SQ_CFG 0x06 /* Configure Send queue */ +#define NIC_MBOX_MSG_RQ_DROP_CFG 0x07 /* Configure receive queue */ +#define NIC_MBOX_MSG_SET_MAC 0x08 /* Add MAC ID to DMAC filter */ +#define NIC_MBOX_MSG_SET_MAX_FRS 0x09 /* Set max frame size */ +#define NIC_MBOX_MSG_CPI_CFG 0x0A /* Config CPI, RSSI */ +#define NIC_MBOX_MSG_RSS_SIZE 0x0B /* Get RSS indir_tbl size */ +#define NIC_MBOX_MSG_RSS_CFG 0x0C /* Config RSS table */ +#define NIC_MBOX_MSG_RSS_CFG_CONT 0x0D /* RSS config continuation */ +#define NIC_MBOX_MSG_RQ_BP_CFG 0x0E /* RQ backpressure config */ +#define NIC_MBOX_MSG_RQ_SW_SYNC 0x0F /* Flush inflight pkts to RQ */ +#define NIC_MBOX_MSG_BGX_STATS 0x10 /* Get stats from BGX */ +#define NIC_MBOX_MSG_BGX_LINK_CHANGE 0x11 /* BGX:LMAC link status */ +#define NIC_MBOX_MSG_ALLOC_SQS 0x12 /* Allocate secondary Qset */ +#define NIC_MBOX_MSG_NICVF_PTR 0x13 /* Send nicvf ptr to PF */ +#define NIC_MBOX_MSG_PNICVF_PTR 0x14 /* Get primary qset nicvf ptr */ +#define NIC_MBOX_MSG_SNICVF_PTR 0x15 /* Send sqet nicvf ptr to PVF */ +#define NIC_MBOX_MSG_LOOPBACK 0x16 /* Set interface in loopback */ +#define NIC_MBOX_MSG_CFG_DONE 0xF0 /* VF configuration done */ +#define NIC_MBOX_MSG_SHUTDOWN 0xF1 /* VF is being shutdown */ + +struct nic_cfg_msg { + uint8_t msg; + uint8_t vf_id; + uint8_t node_id; + boolean_t tns_mode:1; + boolean_t sqs_mode:1; + boolean_t loopback_supported:1; + uint8_t mac_addr[ETHER_ADDR_LEN]; +}; + +/* Qset configuration */ +struct qs_cfg_msg { + uint8_t msg; + uint8_t num; + uint8_t sqs_count; + uint64_t cfg; +}; + +/* Receive queue configuration */ +struct rq_cfg_msg { + uint8_t msg; + uint8_t qs_num; + uint8_t rq_num; + uint64_t cfg; +}; + +/* Send queue configuration */ +struct sq_cfg_msg { + uint8_t msg; + uint8_t qs_num; + uint8_t sq_num; + boolean_t sqs_mode; + uint64_t cfg; +}; + +/* Set VF's MAC address */ +struct set_mac_msg { + uint8_t msg; + uint8_t vf_id; + uint8_t mac_addr[ETHER_ADDR_LEN]; +}; + +/* Set Maximum frame size */ +struct set_frs_msg { + uint8_t msg; + uint8_t vf_id; + uint16_t max_frs; +}; + +/* Set CPI algorithm type */ +struct cpi_cfg_msg { + uint8_t msg; + uint8_t vf_id; + uint8_t rq_cnt; + uint8_t cpi_alg; +}; + +/* Get RSS table size */ +struct rss_sz_msg { + uint8_t msg; + uint8_t vf_id; + uint16_t ind_tbl_size; +}; + +/* Set RSS configuration */ +struct rss_cfg_msg { + uint8_t msg; + uint8_t vf_id; + uint8_t hash_bits; + uint8_t tbl_len; + uint8_t tbl_offset; +#define RSS_IND_TBL_LEN_PER_MBX_MSG 8 + uint8_t ind_tbl[RSS_IND_TBL_LEN_PER_MBX_MSG]; +}; + +struct bgx_stats_msg { + uint8_t msg; + uint8_t vf_id; + uint8_t rx; + uint8_t idx; + uint64_t stats; +}; + +/* Physical interface link status */ +struct bgx_link_status { + uint8_t msg; + uint8_t link_up; + uint8_t duplex; + uint32_t speed; +}; + +/* Set interface in loopback mode */ +struct set_loopback { + uint8_t msg; + uint8_t vf_id; + boolean_t enable; +}; + +/* 128 bit shared memory between PF and each VF */ +union nic_mbx { + struct { + uint8_t msg; + } msg; + struct nic_cfg_msg nic_cfg; + struct qs_cfg_msg qs; + struct rq_cfg_msg rq; + struct sq_cfg_msg sq; + struct set_mac_msg mac; + struct set_frs_msg frs; + struct cpi_cfg_msg cpi_cfg; + struct rss_sz_msg rss_size; + struct rss_cfg_msg rss_cfg; + struct bgx_stats_msg bgx_stats; + struct bgx_link_status link_status; + struct set_loopback lbk; +}; + +#define NIC_NODE_ID_MASK 0x03 +#define NIC_NODE_ID_SHIFT 44 + +static __inline int +nic_get_node_id(struct resource *res) +{ + pci_addr_t addr; + + addr = rman_get_start(res); + return ((addr >> NIC_NODE_ID_SHIFT) & NIC_NODE_ID_MASK); +} + +int nicvf_send_msg_to_pf(struct nicvf *vf, union nic_mbx *mbx); + +#endif /* NIC_H */ diff --git a/sys/dev/vnic/nic_main.c b/sys/dev/vnic/nic_main.c new file mode 100644 index 00000000000..1c350589af6 --- /dev/null +++ b/sys/dev/vnic/nic_main.c @@ -0,0 +1,1167 @@ +/* + * Copyright (C) 2015 Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#ifdef PCI_IOV +#include +#include +#endif + +#include "thunder_bgx.h" +#include "nic_reg.h" +#include "nic.h" +#include "q_struct.h" + +#define VNIC_PF_DEVSTR "Cavium Thunder NIC Physical Function Driver" + +#define VNIC_PF_REG_RID PCIR_BAR(PCI_CFG_REG_BAR_NUM) + +#define NIC_SET_VF_LMAC_MAP(bgx, lmac) ((((bgx) & 0xF) << 4) | ((lmac) & 0xF)) +#define NIC_GET_BGX_FROM_VF_LMAC_MAP(map) (((map) >> 4) & 0xF) +#define NIC_GET_LMAC_FROM_VF_LMAC_MAP(map) ((map) & 0xF) + +/* Structure to be used by the SR-IOV for VF configuration schemas */ +struct nicvf_info { + boolean_t vf_enabled; + int vf_flags; +}; + +struct nicpf { + device_t dev; + uint8_t rev_id; + uint8_t node; + u_int flags; + uint8_t num_vf_en; /* No of VF enabled */ + struct nicvf_info vf_info[MAX_NUM_VFS_SUPPORTED]; + struct resource * reg_base; /* Register start address */ + struct pkind_cfg pkind; + uint8_t vf_lmac_map[MAX_LMAC]; + boolean_t mbx_lock[MAX_NUM_VFS_SUPPORTED]; + + struct callout check_link; + struct mtx check_link_mtx; + + uint8_t link[MAX_LMAC]; + uint8_t duplex[MAX_LMAC]; + uint32_t speed[MAX_LMAC]; + uint16_t cpi_base[MAX_NUM_VFS_SUPPORTED]; + uint16_t rss_ind_tbl_size; + + /* MSI-X */ + boolean_t msix_enabled; + uint8_t num_vec; + struct msix_entry msix_entries[NIC_PF_MSIX_VECTORS]; + struct resource * msix_table_res; +}; + +static int nicpf_probe(device_t); +static int nicpf_attach(device_t); +static int nicpf_detach(device_t); + +#ifdef PCI_IOV +static int nicpf_iov_init(device_t, uint16_t, const nvlist_t *); +static void nicpf_iov_uninit(device_t); +static int nicpf_iov_addr_vf(device_t, uint16_t, const nvlist_t *); +#endif + +static device_method_t nicpf_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nicpf_probe), + DEVMETHOD(device_attach, nicpf_attach), + DEVMETHOD(device_detach, nicpf_detach), + /* PCI SR-IOV interface */ +#ifdef PCI_IOV + DEVMETHOD(pci_iov_init, nicpf_iov_init), + DEVMETHOD(pci_iov_uninit, nicpf_iov_uninit), + DEVMETHOD(pci_iov_add_vf, nicpf_iov_addr_vf), +#endif + DEVMETHOD_END, +}; + +static driver_t nicpf_driver = { + "vnicpf", + nicpf_methods, + sizeof(struct nicpf), +}; + +static devclass_t nicpf_devclass; + +DRIVER_MODULE(nicpf, pci, nicpf_driver, nicpf_devclass, 0, 0); +MODULE_DEPEND(nicpf, pci, 1, 1, 1); +MODULE_DEPEND(nicpf, ether, 1, 1, 1); +MODULE_DEPEND(nicpf, thunder_bgx, 1, 1, 1); + +static int nicpf_alloc_res(struct nicpf *); +static void nicpf_free_res(struct nicpf *); +static void nic_set_lmac_vf_mapping(struct nicpf *); +static void nic_init_hw(struct nicpf *); +static int nic_sriov_init(device_t, struct nicpf *); +static void nic_poll_for_link(void *); +static int nic_register_interrupts(struct nicpf *); +static void nic_unregister_interrupts(struct nicpf *); + +/* + * Device interface + */ +static int +nicpf_probe(device_t dev) +{ + uint16_t vendor_id; + uint16_t device_id; + + vendor_id = pci_get_vendor(dev); + device_id = pci_get_device(dev); + + if (vendor_id == PCI_VENDOR_ID_CAVIUM && + device_id == PCI_DEVICE_ID_THUNDER_NIC_PF) { + device_set_desc(dev, VNIC_PF_DEVSTR); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +nicpf_attach(device_t dev) +{ + struct nicpf *nic; + int err; + + nic = device_get_softc(dev); + nic->dev = dev; + + /* Enable bus mastering */ + pci_enable_busmaster(dev); + + /* Allocate PCI resources */ + err = nicpf_alloc_res(nic); + if (err != 0) { + device_printf(dev, "Could not allocate PCI resources\n"); + return (err); + } + + nic->node = nic_get_node_id(nic->reg_base); + nic->rev_id = pci_read_config(dev, PCIR_REVID, 1); + + /* Enable Traffic Network Switch (TNS) bypass mode by default */ + nic->flags &= ~NIC_TNS_ENABLED; + nic_set_lmac_vf_mapping(nic); + + /* Initialize hardware */ + nic_init_hw(nic); + + /* Set RSS TBL size for each VF */ + nic->rss_ind_tbl_size = NIC_MAX_RSS_IDR_TBL_SIZE; + + /* Setup interrupts */ + err = nic_register_interrupts(nic); + if (err != 0) + goto err_free_res; + + /* Configure SRIOV */ + err = nic_sriov_init(dev, nic); + if (err != 0) + goto err_free_intr; + + if (nic->flags & NIC_TNS_ENABLED) + return (0); + + mtx_init(&nic->check_link_mtx, "VNIC PF link poll", NULL, MTX_DEF); + /* Register physical link status poll callout */ + callout_init_mtx(&nic->check_link, &nic->check_link_mtx, 0); + mtx_lock(&nic->check_link_mtx); + nic_poll_for_link(nic); + mtx_unlock(&nic->check_link_mtx); + + return (0); + +err_free_intr: + nic_unregister_interrupts(nic); +err_free_res: + nicpf_free_res(nic); + pci_disable_busmaster(dev); + + return (err); +} + +static int +nicpf_detach(device_t dev) +{ + struct nicpf *nic; + + nic = device_get_softc(dev); + + callout_drain(&nic->check_link); + mtx_destroy(&nic->check_link_mtx); + + nic_unregister_interrupts(nic); + nicpf_free_res(nic); + pci_disable_busmaster(dev); + + return (0); +} + +/* + * SR-IOV interface + */ +#ifdef PCI_IOV +static int +nicpf_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *params) +{ + struct nicpf *nic; + + nic = device_get_softc(dev); + + nic->num_vf_en = 0; + if (num_vfs == 0) + return (ENXIO); + if (num_vfs > MAX_NUM_VFS_SUPPORTED) + return (EINVAL); + + /* + * Just set variables here. + * The number of VFs will be written to configuration + * space later in PCI_ADD_VF(). + */ + nic->num_vf_en = num_vfs; + nic->flags |= NIC_SRIOV_ENABLED; + + return (0); +} + +static void +nicpf_iov_uninit(device_t dev) +{ + + /* ARM64TODO: Implement this function */ +} + +static int +nicpf_iov_addr_vf(device_t dev, uint16_t vfnum, const nvlist_t *params) +{ + const void *mac; + struct nicpf *nic; + size_t size; + int bgx, lmac; + + nic = device_get_softc(dev); + + if ((nic->flags & NIC_SRIOV_ENABLED) == 0) + return (ENXIO); + + if (nvlist_exists_binary(params, "mac-addr") != 0) { + mac = nvlist_get_binary(params, "mac-addr", &size); + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vfnum]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vfnum]); + bgx_set_lmac_mac(nic->node, bgx, lmac, mac); + } + + return (0); +} +#endif + +/* + * Helper routines + */ +static int +nicpf_alloc_res(struct nicpf *nic) +{ + device_t dev; + int rid; + + dev = nic->dev; + + rid = VNIC_PF_REG_RID; + nic->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (nic->reg_base == NULL) { + /* For verbose output print some more details */ + if (bootverbose) { + device_printf(dev, + "Could not allocate registers memory\n"); + } + return (ENXIO); + } + + return (0); +} + +static void +nicpf_free_res(struct nicpf *nic) +{ + device_t dev; + + dev = nic->dev; + + if (nic->reg_base != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(nic->reg_base), nic->reg_base); + } +} + +/* Register read/write APIs */ +static __inline void +nic_reg_write(struct nicpf *nic, bus_space_handle_t offset, + uint64_t val) +{ + + bus_write_8(nic->reg_base, offset, val); +} + +static __inline uint64_t +nic_reg_read(struct nicpf *nic, uint64_t offset) +{ + uint64_t val; + + val = bus_read_8(nic->reg_base, offset); + return (val); +} + +/* PF -> VF mailbox communication APIs */ +static void +nic_enable_mbx_intr(struct nicpf *nic) +{ + + /* Enable mailbox interrupt for all 128 VFs */ + nic_reg_write(nic, NIC_PF_MAILBOX_ENA_W1S, ~0UL); + nic_reg_write(nic, NIC_PF_MAILBOX_ENA_W1S + sizeof(uint64_t), ~0UL); +} + +static void +nic_clear_mbx_intr(struct nicpf *nic, int vf, int mbx_reg) +{ + + nic_reg_write(nic, NIC_PF_MAILBOX_INT + (mbx_reg << 3), (1UL << vf)); +} + +static uint64_t +nic_get_mbx_addr(int vf) +{ + + return (NIC_PF_VF_0_127_MAILBOX_0_1 + (vf << NIC_VF_NUM_SHIFT)); +} + +/* + * Send a mailbox message to VF + * @vf: vf to which this message to be sent + * @mbx: Message to be sent + */ +static void +nic_send_msg_to_vf(struct nicpf *nic, int vf, union nic_mbx *mbx) +{ + bus_space_handle_t mbx_addr = nic_get_mbx_addr(vf); + uint64_t *msg = (uint64_t *)mbx; + + /* + * In first revision HW, mbox interrupt is triggerred + * when PF writes to MBOX(1), in next revisions when + * PF writes to MBOX(0) + */ + if (nic->rev_id == 0) { + nic_reg_write(nic, mbx_addr + 0, msg[0]); + nic_reg_write(nic, mbx_addr + 8, msg[1]); + } else { + nic_reg_write(nic, mbx_addr + 8, msg[1]); + nic_reg_write(nic, mbx_addr + 0, msg[0]); + } +} + +/* + * Responds to VF's READY message with VF's + * ID, node, MAC address e.t.c + * @vf: VF which sent READY message + */ +static void +nic_mbx_send_ready(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + int bgx_idx, lmac; + const char *mac; + + mbx.nic_cfg.msg = NIC_MBOX_MSG_READY; + mbx.nic_cfg.vf_id = vf; + + if (nic->flags & NIC_TNS_ENABLED) + mbx.nic_cfg.tns_mode = NIC_TNS_MODE; + else + mbx.nic_cfg.tns_mode = NIC_TNS_BYPASS_MODE; + + if (vf < MAX_LMAC) { + bgx_idx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + + mac = bgx_get_lmac_mac(nic->node, bgx_idx, lmac); + if (mac) { + memcpy((uint8_t *)&mbx.nic_cfg.mac_addr, mac, + ETHER_ADDR_LEN); + } + } + mbx.nic_cfg.node_id = nic->node; + + mbx.nic_cfg.loopback_supported = vf < MAX_LMAC; + + nic_send_msg_to_vf(nic, vf, &mbx); +} + +/* + * ACKs VF's mailbox message + * @vf: VF to which ACK to be sent + */ +static void +nic_mbx_send_ack(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + + mbx.msg.msg = NIC_MBOX_MSG_ACK; + nic_send_msg_to_vf(nic, vf, &mbx); +} + +/* + * NACKs VF's mailbox message that PF is not able to + * complete the action + * @vf: VF to which ACK to be sent + */ +static void +nic_mbx_send_nack(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + + mbx.msg.msg = NIC_MBOX_MSG_NACK; + nic_send_msg_to_vf(nic, vf, &mbx); +} + +/* + * Flush all in flight receive packets to memory and + * bring down an active RQ + */ +static int +nic_rcv_queue_sw_sync(struct nicpf *nic) +{ + uint16_t timeout = ~0x00; + + nic_reg_write(nic, NIC_PF_SW_SYNC_RX, 0x01); + /* Wait till sync cycle is finished */ + while (timeout) { + if (nic_reg_read(nic, NIC_PF_SW_SYNC_RX_DONE) & 0x1) + break; + timeout--; + } + nic_reg_write(nic, NIC_PF_SW_SYNC_RX, 0x00); + if (!timeout) { + device_printf(nic->dev, "Receive queue software sync failed\n"); + return (ETIMEDOUT); + } + return (0); +} + +/* Get BGX Rx/Tx stats and respond to VF's request */ +static void +nic_get_bgx_stats(struct nicpf *nic, struct bgx_stats_msg *bgx) +{ + int bgx_idx, lmac; + union nic_mbx mbx = {}; + + bgx_idx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[bgx->vf_id]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[bgx->vf_id]); + + mbx.bgx_stats.msg = NIC_MBOX_MSG_BGX_STATS; + mbx.bgx_stats.vf_id = bgx->vf_id; + mbx.bgx_stats.rx = bgx->rx; + mbx.bgx_stats.idx = bgx->idx; + if (bgx->rx != 0) { + mbx.bgx_stats.stats = + bgx_get_rx_stats(nic->node, bgx_idx, lmac, bgx->idx); + } else { + mbx.bgx_stats.stats = + bgx_get_tx_stats(nic->node, bgx_idx, lmac, bgx->idx); + } + nic_send_msg_to_vf(nic, bgx->vf_id, &mbx); +} + +/* Update hardware min/max frame size */ +static int +nic_update_hw_frs(struct nicpf *nic, int new_frs, int vf) +{ + + if ((new_frs > NIC_HW_MAX_FRS) || (new_frs < NIC_HW_MIN_FRS)) { + device_printf(nic->dev, + "Invalid MTU setting from VF%d rejected, " + "should be between %d and %d\n", + vf, NIC_HW_MIN_FRS, NIC_HW_MAX_FRS); + return (EINVAL); + } + new_frs += ETHER_HDR_LEN; + if (new_frs <= nic->pkind.maxlen) + return (0); + + nic->pkind.maxlen = new_frs; + nic_reg_write(nic, NIC_PF_PKIND_0_15_CFG, *(uint64_t *)&nic->pkind); + return (0); +} + +/* Set minimum transmit packet size */ +static void +nic_set_tx_pkt_pad(struct nicpf *nic, int size) +{ + int lmac; + uint64_t lmac_cfg; + + /* Max value that can be set is 60 */ + if (size > 60) + size = 60; + + for (lmac = 0; lmac < (MAX_BGX_PER_CN88XX * MAX_LMAC_PER_BGX); lmac++) { + lmac_cfg = nic_reg_read(nic, NIC_PF_LMAC_0_7_CFG | (lmac << 3)); + lmac_cfg &= ~(0xF << 2); + lmac_cfg |= ((size / 4) << 2); + nic_reg_write(nic, NIC_PF_LMAC_0_7_CFG | (lmac << 3), lmac_cfg); + } +} + +/* + * Function to check number of LMACs present and set VF::LMAC mapping. + * Mapping will be used while initializing channels. + */ +static void +nic_set_lmac_vf_mapping(struct nicpf *nic) +{ + unsigned bgx_map = bgx_get_map(nic->node); + int bgx, next_bgx_lmac = 0; + int lmac, lmac_cnt = 0; + uint64_t lmac_credit; + + nic->num_vf_en = 0; + if (nic->flags & NIC_TNS_ENABLED) { + nic->num_vf_en = DEFAULT_NUM_VF_ENABLED; + return; + } + + for (bgx = 0; bgx < NIC_MAX_BGX; bgx++) { + if ((bgx_map & (1 << bgx)) == 0) + continue; + lmac_cnt = bgx_get_lmac_count(nic->node, bgx); + for (lmac = 0; lmac < lmac_cnt; lmac++) + nic->vf_lmac_map[next_bgx_lmac++] = + NIC_SET_VF_LMAC_MAP(bgx, lmac); + nic->num_vf_en += lmac_cnt; + + /* Program LMAC credits */ + lmac_credit = (1UL << 1); /* channel credit enable */ + lmac_credit |= (0x1ff << 2); /* Max outstanding pkt count */ + /* 48KB BGX Tx buffer size, each unit is of size 16bytes */ + lmac_credit |= (((((48 * 1024) / lmac_cnt) - + NIC_HW_MAX_FRS) / 16) << 12); + lmac = bgx * MAX_LMAC_PER_BGX; + for (; lmac < lmac_cnt + (bgx * MAX_LMAC_PER_BGX); lmac++) { + nic_reg_write(nic, NIC_PF_LMAC_0_7_CREDIT + (lmac * 8), + lmac_credit); + } + } +} + +#define TNS_PORT0_BLOCK 6 +#define TNS_PORT1_BLOCK 7 +#define BGX0_BLOCK 8 +#define BGX1_BLOCK 9 + +static void +nic_init_hw(struct nicpf *nic) +{ + int i; + + /* Reset NIC, in case the driver is repeatedly inserted and removed */ + nic_reg_write(nic, NIC_PF_SOFT_RESET, 1); + + /* Enable NIC HW block */ + nic_reg_write(nic, NIC_PF_CFG, 0x3); + + /* Enable backpressure */ + nic_reg_write(nic, NIC_PF_BP_CFG, (1UL << 6) | 0x03); + + if (nic->flags & NIC_TNS_ENABLED) { + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG, + (NIC_TNS_MODE << 7) | TNS_PORT0_BLOCK); + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG | (1 << 8), + (NIC_TNS_MODE << 7) | TNS_PORT1_BLOCK); + nic_reg_write(nic, NIC_PF_INTF_0_1_BP_CFG, + (1UL << 63) | TNS_PORT0_BLOCK); + nic_reg_write(nic, NIC_PF_INTF_0_1_BP_CFG + (1 << 8), + (1UL << 63) | TNS_PORT1_BLOCK); + + } else { + /* Disable TNS mode on both interfaces */ + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG, + (NIC_TNS_BYPASS_MODE << 7) | BGX0_BLOCK); + nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG | (1 << 8), + (NIC_TNS_BYPASS_MODE << 7) | BGX1_BLOCK); + nic_reg_write(nic, NIC_PF_INTF_0_1_BP_CFG, + (1UL << 63) | BGX0_BLOCK); + nic_reg_write(nic, NIC_PF_INTF_0_1_BP_CFG + (1 << 8), + (1UL << 63) | BGX1_BLOCK); + } + + /* PKIND configuration */ + nic->pkind.minlen = 0; + nic->pkind.maxlen = NIC_HW_MAX_FRS + ETHER_HDR_LEN; + nic->pkind.lenerr_en = 1; + nic->pkind.rx_hdr = 0; + nic->pkind.hdr_sl = 0; + + for (i = 0; i < NIC_MAX_PKIND; i++) { + nic_reg_write(nic, NIC_PF_PKIND_0_15_CFG | (i << 3), + *(uint64_t *)&nic->pkind); + } + + nic_set_tx_pkt_pad(nic, NIC_HW_MIN_FRS); + + /* Timer config */ + nic_reg_write(nic, NIC_PF_INTR_TIMER_CFG, NICPF_CLK_PER_INT_TICK); + + /* Enable VLAN ethertype matching and stripping */ + nic_reg_write(nic, NIC_PF_RX_ETYPE_0_7, + (2 << 19) | (ETYPE_ALG_VLAN_STRIP << 16) | ETHERTYPE_VLAN); +} + +/* Channel parse index configuration */ +static void +nic_config_cpi(struct nicpf *nic, struct cpi_cfg_msg *cfg) +{ + uint32_t vnic, bgx, lmac, chan; + uint32_t padd, cpi_count = 0; + uint64_t cpi_base, cpi, rssi_base, rssi; + uint8_t qset, rq_idx = 0; + + vnic = cfg->vf_id; + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vnic]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vnic]); + + chan = (lmac * MAX_BGX_CHANS_PER_LMAC) + (bgx * NIC_CHANS_PER_INF); + cpi_base = (lmac * NIC_MAX_CPI_PER_LMAC) + (bgx * NIC_CPI_PER_BGX); + rssi_base = (lmac * nic->rss_ind_tbl_size) + (bgx * NIC_RSSI_PER_BGX); + + /* Rx channel configuration */ + nic_reg_write(nic, NIC_PF_CHAN_0_255_RX_BP_CFG | (chan << 3), + (1UL << 63) | (vnic << 0)); + nic_reg_write(nic, NIC_PF_CHAN_0_255_RX_CFG | (chan << 3), + ((uint64_t)cfg->cpi_alg << 62) | (cpi_base << 48)); + + if (cfg->cpi_alg == CPI_ALG_NONE) + cpi_count = 1; + else if (cfg->cpi_alg == CPI_ALG_VLAN) /* 3 bits of PCP */ + cpi_count = 8; + else if (cfg->cpi_alg == CPI_ALG_VLAN16) /* 3 bits PCP + DEI */ + cpi_count = 16; + else if (cfg->cpi_alg == CPI_ALG_DIFF) /* 6bits DSCP */ + cpi_count = NIC_MAX_CPI_PER_LMAC; + + /* RSS Qset, Qidx mapping */ + qset = cfg->vf_id; + rssi = rssi_base; + for (; rssi < (rssi_base + cfg->rq_cnt); rssi++) { + nic_reg_write(nic, NIC_PF_RSSI_0_4097_RQ | (rssi << 3), + (qset << 3) | rq_idx); + rq_idx++; + } + + rssi = 0; + cpi = cpi_base; + for (; cpi < (cpi_base + cpi_count); cpi++) { + /* Determine port to channel adder */ + if (cfg->cpi_alg != CPI_ALG_DIFF) + padd = cpi % cpi_count; + else + padd = cpi % 8; /* 3 bits CS out of 6bits DSCP */ + + /* Leave RSS_SIZE as '0' to disable RSS */ + nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3), + (vnic << 24) | (padd << 16) | (rssi_base + rssi)); + + if ((rssi + 1) >= cfg->rq_cnt) + continue; + + if (cfg->cpi_alg == CPI_ALG_VLAN) + rssi++; + else if (cfg->cpi_alg == CPI_ALG_VLAN16) + rssi = ((cpi - cpi_base) & 0xe) >> 1; + else if (cfg->cpi_alg == CPI_ALG_DIFF) + rssi = ((cpi - cpi_base) & 0x38) >> 3; + } + nic->cpi_base[cfg->vf_id] = cpi_base; +} + +/* + * 4 level transmit side scheduler configutation + * for TNS bypass mode + * + * Sample configuration for SQ0 + * VNIC0-SQ0 -> TL4(0) -> TL3[0] -> TL2[0] -> TL1[0] -> BGX0 + * VNIC1-SQ0 -> TL4(8) -> TL3[2] -> TL2[0] -> TL1[0] -> BGX0 + * VNIC2-SQ0 -> TL4(16) -> TL3[4] -> TL2[1] -> TL1[0] -> BGX0 + * VNIC3-SQ0 -> TL4(24) -> TL3[6] -> TL2[1] -> TL1[0] -> BGX0 + * VNIC4-SQ0 -> TL4(512) -> TL3[128] -> TL2[32] -> TL1[1] -> BGX1 + * VNIC5-SQ0 -> TL4(520) -> TL3[130] -> TL2[32] -> TL1[1] -> BGX1 + * VNIC6-SQ0 -> TL4(528) -> TL3[132] -> TL2[33] -> TL1[1] -> BGX1 + * VNIC7-SQ0 -> TL4(536) -> TL3[134] -> TL2[33] -> TL1[1] -> BGX1 + */ +static void +nic_tx_channel_cfg(struct nicpf *nic, uint8_t vnic, struct sq_cfg_msg *sq) +{ + uint32_t bgx, lmac, chan; + uint32_t tl2, tl3, tl4; + uint32_t rr_quantum; + uint8_t sq_idx = sq->sq_num; + uint8_t pqs_vnic; + + pqs_vnic = vnic; + + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[pqs_vnic]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[pqs_vnic]); + + /* 24 bytes for FCS, IPG and preamble */ + rr_quantum = ((NIC_HW_MAX_FRS + 24) / 4); + + tl4 = (lmac * NIC_TL4_PER_LMAC) + (bgx * NIC_TL4_PER_BGX); + tl4 += sq_idx; + + tl3 = tl4 / (NIC_MAX_TL4 / NIC_MAX_TL3); + nic_reg_write(nic, NIC_PF_QSET_0_127_SQ_0_7_CFG2 | + ((uint64_t)vnic << NIC_QS_ID_SHIFT) | + ((uint32_t)sq_idx << NIC_Q_NUM_SHIFT), tl4); + nic_reg_write(nic, NIC_PF_TL4_0_1023_CFG | (tl4 << 3), + ((uint64_t)vnic << 27) | ((uint32_t)sq_idx << 24) | rr_quantum); + + nic_reg_write(nic, NIC_PF_TL3_0_255_CFG | (tl3 << 3), rr_quantum); + chan = (lmac * MAX_BGX_CHANS_PER_LMAC) + (bgx * NIC_CHANS_PER_INF); + nic_reg_write(nic, NIC_PF_TL3_0_255_CHAN | (tl3 << 3), chan); + /* Enable backpressure on the channel */ + nic_reg_write(nic, NIC_PF_CHAN_0_255_TX_CFG | (chan << 3), 1); + + tl2 = tl3 >> 2; + nic_reg_write(nic, NIC_PF_TL3A_0_63_CFG | (tl2 << 3), tl2); + nic_reg_write(nic, NIC_PF_TL2_0_63_CFG | (tl2 << 3), rr_quantum); + /* No priorities as of now */ + nic_reg_write(nic, NIC_PF_TL2_0_63_PRI | (tl2 << 3), 0x00); +} + +static int +nic_config_loopback(struct nicpf *nic, struct set_loopback *lbk) +{ + int bgx_idx, lmac_idx; + + if (lbk->vf_id > MAX_LMAC) + return (ENXIO); + + bgx_idx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lbk->vf_id]); + lmac_idx = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lbk->vf_id]); + + bgx_lmac_internal_loopback(nic->node, bgx_idx, lmac_idx, lbk->enable); + + return (0); +} + +/* Interrupt handler to handle mailbox messages from VFs */ +static void +nic_handle_mbx_intr(struct nicpf *nic, int vf) +{ + union nic_mbx mbx = {}; + uint64_t *mbx_data; + uint64_t mbx_addr; + uint64_t reg_addr; + uint64_t cfg; + int bgx, lmac; + int i; + int ret = 0; + + nic->mbx_lock[vf] = TRUE; + + mbx_addr = nic_get_mbx_addr(vf); + mbx_data = (uint64_t *)&mbx; + + for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) { + *mbx_data = nic_reg_read(nic, mbx_addr); + mbx_data++; + mbx_addr += sizeof(uint64_t); + } + + switch (mbx.msg.msg) { + case NIC_MBOX_MSG_READY: + nic_mbx_send_ready(nic, vf); + if (vf < MAX_LMAC) { + nic->link[vf] = 0; + nic->duplex[vf] = 0; + nic->speed[vf] = 0; + } + ret = 1; + break; + case NIC_MBOX_MSG_QS_CFG: + reg_addr = NIC_PF_QSET_0_127_CFG | + (mbx.qs.num << NIC_QS_ID_SHIFT); + cfg = mbx.qs.cfg; + nic_reg_write(nic, reg_addr, cfg); + break; + case NIC_MBOX_MSG_RQ_CFG: + reg_addr = NIC_PF_QSET_0_127_RQ_0_7_CFG | + (mbx.rq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.rq.rq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.rq.cfg); + break; + case NIC_MBOX_MSG_RQ_BP_CFG: + reg_addr = NIC_PF_QSET_0_127_RQ_0_7_BP_CFG | + (mbx.rq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.rq.rq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.rq.cfg); + break; + case NIC_MBOX_MSG_RQ_SW_SYNC: + ret = nic_rcv_queue_sw_sync(nic); + break; + case NIC_MBOX_MSG_RQ_DROP_CFG: + reg_addr = NIC_PF_QSET_0_127_RQ_0_7_DROP_CFG | + (mbx.rq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.rq.rq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.rq.cfg); + break; + case NIC_MBOX_MSG_SQ_CFG: + reg_addr = NIC_PF_QSET_0_127_SQ_0_7_CFG | + (mbx.sq.qs_num << NIC_QS_ID_SHIFT) | + (mbx.sq.sq_num << NIC_Q_NUM_SHIFT); + nic_reg_write(nic, reg_addr, mbx.sq.cfg); + nic_tx_channel_cfg(nic, mbx.qs.num, &mbx.sq); + break; + case NIC_MBOX_MSG_SET_MAC: + lmac = mbx.mac.vf_id; + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lmac]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[lmac]); + bgx_set_lmac_mac(nic->node, bgx, lmac, mbx.mac.mac_addr); + break; + case NIC_MBOX_MSG_SET_MAX_FRS: + ret = nic_update_hw_frs(nic, mbx.frs.max_frs, mbx.frs.vf_id); + break; + case NIC_MBOX_MSG_CPI_CFG: + nic_config_cpi(nic, &mbx.cpi_cfg); + break; + case NIC_MBOX_MSG_CFG_DONE: + /* Last message of VF config msg sequence */ + nic->vf_info[vf].vf_enabled = TRUE; + goto unlock; + case NIC_MBOX_MSG_SHUTDOWN: + /* First msg in VF teardown sequence */ + nic->vf_info[vf].vf_enabled = FALSE; + break; + case NIC_MBOX_MSG_BGX_STATS: + nic_get_bgx_stats(nic, &mbx.bgx_stats); + goto unlock; + case NIC_MBOX_MSG_LOOPBACK: + ret = nic_config_loopback(nic, &mbx.lbk); + break; + default: + device_printf(nic->dev, + "Invalid msg from VF%d, msg 0x%x\n", vf, mbx.msg.msg); + break; + } + + if (ret == 0) + nic_mbx_send_ack(nic, vf); + else if (mbx.msg.msg != NIC_MBOX_MSG_READY) + nic_mbx_send_nack(nic, vf); +unlock: + nic->mbx_lock[vf] = FALSE; +} + +static void +nic_mbx_intr_handler(struct nicpf *nic, int mbx) +{ + uint64_t intr; + uint8_t vf, vf_per_mbx_reg = 64; + + intr = nic_reg_read(nic, NIC_PF_MAILBOX_INT + (mbx << 3)); + for (vf = 0; vf < vf_per_mbx_reg; vf++) { + if (intr & (1UL << vf)) { + nic_handle_mbx_intr(nic, vf + (mbx * vf_per_mbx_reg)); + nic_clear_mbx_intr(nic, vf, mbx); + } + } +} + +static void +nic_mbx0_intr_handler (void *arg) +{ + struct nicpf *nic = (struct nicpf *)arg; + + nic_mbx_intr_handler(nic, 0); +} + +static void +nic_mbx1_intr_handler (void *arg) +{ + struct nicpf *nic = (struct nicpf *)arg; + + nic_mbx_intr_handler(nic, 1); +} + +static int +nic_enable_msix(struct nicpf *nic) +{ + struct pci_devinfo *dinfo; + int rid, count; + int ret; + + dinfo = device_get_ivars(nic->dev); + rid = dinfo->cfg.msix.msix_table_bar; + nic->msix_table_res = + bus_alloc_resource_any(nic->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (nic->msix_table_res == NULL) { + device_printf(nic->dev, + "Could not allocate memory for MSI-X table\n"); + return (ENXIO); + } + + count = nic->num_vec = NIC_PF_MSIX_VECTORS; + + ret = pci_alloc_msix(nic->dev, &count); + if ((ret != 0) || (count != nic->num_vec)) { + device_printf(nic->dev, + "Request for #%d msix vectors failed, error: %d\n", + nic->num_vec, ret); + return (ret); + } + + nic->msix_enabled = 1; + return (0); +} + +static void +nic_disable_msix(struct nicpf *nic) +{ + if (nic->msix_enabled) { + pci_release_msi(nic->dev); + nic->msix_enabled = 0; + nic->num_vec = 0; + } +} + +static void +nic_free_all_interrupts(struct nicpf *nic) +{ + int irq; + + for (irq = 0; irq < nic->num_vec; irq++) { + if (nic->msix_entries[irq].irq_res == NULL) + continue; + if (nic->msix_entries[irq].handle != NULL) { + bus_teardown_intr(nic->dev, + nic->msix_entries[irq].irq_res, + nic->msix_entries[irq].handle); + } + + bus_release_resource(nic->dev, SYS_RES_IRQ, irq, + nic->msix_entries[irq].irq_res); + } +} + +static int +nic_register_interrupts(struct nicpf *nic) +{ + int irq, rid; + int ret; + + /* Enable MSI-X */ + ret = nic_enable_msix(nic); + if (ret != 0) + return (ret); + + /* Register mailbox interrupt handlers */ + irq = NIC_PF_INTR_ID_MBOX0; + rid = irq + 1; + nic->msix_entries[irq].irq_res = bus_alloc_resource_any(nic->dev, + SYS_RES_IRQ, &rid, (RF_SHAREABLE | RF_ACTIVE)); + if (nic->msix_entries[irq].irq_res == NULL) { + ret = ENXIO; + goto fail; + } + ret = bus_setup_intr(nic->dev, nic->msix_entries[irq].irq_res, + (INTR_MPSAFE | INTR_TYPE_MISC), NULL, nic_mbx0_intr_handler, nic, + &nic->msix_entries[irq].handle); + if (ret != 0) + goto fail; + + irq = NIC_PF_INTR_ID_MBOX1; + rid = irq + 1; + nic->msix_entries[irq].irq_res = bus_alloc_resource_any(nic->dev, + SYS_RES_IRQ, &rid, (RF_SHAREABLE | RF_ACTIVE)); + if (nic->msix_entries[irq].irq_res == NULL) { + ret = ENXIO; + goto fail; + } + ret = bus_setup_intr(nic->dev, nic->msix_entries[irq].irq_res, + (INTR_MPSAFE | INTR_TYPE_MISC), NULL, nic_mbx1_intr_handler, nic, + &nic->msix_entries[irq].handle); + if (ret != 0) + goto fail; + + /* Enable mailbox interrupt */ + nic_enable_mbx_intr(nic); + return (0); + +fail: + nic_free_all_interrupts(nic); + return (ret); +} + +static void +nic_unregister_interrupts(struct nicpf *nic) +{ + + nic_free_all_interrupts(nic); + nic_disable_msix(nic); +} + +static int nic_sriov_init(device_t dev, struct nicpf *nic) +{ +#ifdef PCI_IOV + nvlist_t *pf_schema, *vf_schema; + int iov_pos; + int err; + uint16_t total_vf_cnt; + + err = pci_find_extcap(dev, PCIZ_SRIOV, &iov_pos); + if (err != 0) { + device_printf(dev, + "SR-IOV capability is not found in PCIe config space\n"); + return (err); + } + /* Fix-up the number of enabled VFs */ + total_vf_cnt = pci_read_config(dev, iov_pos + PCIR_SRIOV_TOTAL_VFS, 2); + if (total_vf_cnt < nic->num_vf_en) + nic->num_vf_en = total_vf_cnt; + + if (total_vf_cnt == 0) + return (0); + + /* Attach SR-IOV */ + pf_schema = pci_iov_schema_alloc_node(); + vf_schema = pci_iov_schema_alloc_node(); + pci_iov_schema_add_unicast_mac(vf_schema, "mac-addr", 0, NULL); + /* + * All VFs can change their MACs. + * This flag will be ignored but we set it just for the record. + */ + pci_iov_schema_add_bool(vf_schema, "allow-set-mac", + IOV_SCHEMA_HASDEFAULT, TRUE); + + err = pci_iov_attach(dev, pf_schema, vf_schema); + if (err != 0) { + device_printf(dev, + "Failed to initialize SR-IOV (error=%d)\n", + err); + nic->num_vf_en = 0; + return (err); + } +#endif + return (0); +} + +/* + * Poll for BGX LMAC link status and update corresponding VF + * if there is a change, valid only if internal L2 switch + * is not present otherwise VF link is always treated as up + */ +static void +nic_poll_for_link(void *arg) +{ + union nic_mbx mbx = {}; + struct nicpf *nic; + struct bgx_link_status link; + uint8_t vf, bgx, lmac; + + nic = (struct nicpf *)arg; + + mbx.link_status.msg = NIC_MBOX_MSG_BGX_LINK_CHANGE; + + for (vf = 0; vf < nic->num_vf_en; vf++) { + /* Poll only if VF is UP */ + if (!nic->vf_info[vf].vf_enabled) + continue; + + /* Get BGX, LMAC indices for the VF */ + bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]); + /* Get interface link status */ + bgx_get_lmac_link_state(nic->node, bgx, lmac, &link); + + /* Inform VF only if link status changed */ + if (nic->link[vf] == link.link_up) + continue; + + if (!nic->mbx_lock[vf]) { + nic->link[vf] = link.link_up; + nic->duplex[vf] = link.duplex; + nic->speed[vf] = link.speed; + + /* Send a mbox message to VF with current link status */ + mbx.link_status.link_up = link.link_up; + mbx.link_status.duplex = link.duplex; + mbx.link_status.speed = link.speed; + nic_send_msg_to_vf(nic, vf, &mbx); + } + } + callout_reset(&nic->check_link, hz * 2, nic_poll_for_link, nic); +} diff --git a/sys/dev/vnic/nic_reg.h b/sys/dev/vnic/nic_reg.h new file mode 100644 index 00000000000..8d508c747a7 --- /dev/null +++ b/sys/dev/vnic/nic_reg.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2015 Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef NIC_REG_H +#define NIC_REG_H + +#define NIC_PF_REG_COUNT 29573 +#define NIC_VF_REG_COUNT 249 + +/* Physical function register offsets */ +#define NIC_PF_CFG (0x0000) +#define NIC_PF_STATUS (0x0010) +#define NIC_PF_INTR_TIMER_CFG (0x0030) +#define NIC_PF_BIST_STATUS (0x0040) +#define NIC_PF_SOFT_RESET (0x0050) +#define NIC_PF_TCP_TIMER (0x0060) +#define NIC_PF_BP_CFG (0x0080) +#define NIC_PF_RRM_CFG (0x0088) +#define NIC_PF_CQM_CF (0x00A0) +#define NIC_PF_CNM_CF (0x00A8) +#define NIC_PF_CNM_STATUS (0x00B0) +#define NIC_PF_CQ_AVG_CFG (0x00C0) +#define NIC_PF_RRM_AVG_CFG (0x00C8) +#define NIC_PF_INTF_0_1_SEND_CFG (0x0200) +#define NIC_PF_INTF_0_1_BP_CFG (0x0208) +#define NIC_PF_INTF_0_1_BP_DIS_0_1 (0x0210) +#define NIC_PF_INTF_0_1_BP_SW_0_1 (0x0220) +#define NIC_PF_RBDR_BP_STATE_0_3 (0x0240) +#define NIC_PF_MAILBOX_INT (0x0410) +#define NIC_PF_MAILBOX_INT_W1S (0x0430) +#define NIC_PF_MAILBOX_ENA_W1C (0x0450) +#define NIC_PF_MAILBOX_ENA_W1S (0x0470) +#define NIC_PF_RX_ETYPE_0_7 (0x0500) +#define NIC_PF_PKIND_0_15_CFG (0x0600) +#define NIC_PF_ECC0_FLIP0 (0x1000) +#define NIC_PF_ECC1_FLIP0 (0x1008) +#define NIC_PF_ECC2_FLIP0 (0x1010) +#define NIC_PF_ECC3_FLIP0 (0x1018) +#define NIC_PF_ECC0_FLIP1 (0x1080) +#define NIC_PF_ECC1_FLIP1 (0x1088) +#define NIC_PF_ECC2_FLIP1 (0x1090) +#define NIC_PF_ECC3_FLIP1 (0x1098) +#define NIC_PF_ECC0_CDIS (0x1100) +#define NIC_PF_ECC1_CDIS (0x1108) +#define NIC_PF_ECC2_CDIS (0x1110) +#define NIC_PF_ECC3_CDIS (0x1118) +#define NIC_PF_BIST0_STATUS (0x1280) +#define NIC_PF_BIST1_STATUS (0x1288) +#define NIC_PF_BIST2_STATUS (0x1290) +#define NIC_PF_BIST3_STATUS (0x1298) +#define NIC_PF_ECC0_SBE_INT (0x2000) +#define NIC_PF_ECC0_SBE_INT_W1S (0x2008) +#define NIC_PF_ECC0_SBE_ENA_W1C (0x2010) +#define NIC_PF_ECC0_SBE_ENA_W1S (0x2018) +#define NIC_PF_ECC0_DBE_INT (0x2100) +#define NIC_PF_ECC0_DBE_INT_W1S (0x2108) +#define NIC_PF_ECC0_DBE_ENA_W1C (0x2110) +#define NIC_PF_ECC0_DBE_ENA_W1S (0x2118) +#define NIC_PF_ECC1_SBE_INT (0x2200) +#define NIC_PF_ECC1_SBE_INT_W1S (0x2208) +#define NIC_PF_ECC1_SBE_ENA_W1C (0x2210) +#define NIC_PF_ECC1_SBE_ENA_W1S (0x2218) +#define NIC_PF_ECC1_DBE_INT (0x2300) +#define NIC_PF_ECC1_DBE_INT_W1S (0x2308) +#define NIC_PF_ECC1_DBE_ENA_W1C (0x2310) +#define NIC_PF_ECC1_DBE_ENA_W1S (0x2318) +#define NIC_PF_ECC2_SBE_INT (0x2400) +#define NIC_PF_ECC2_SBE_INT_W1S (0x2408) +#define NIC_PF_ECC2_SBE_ENA_W1C (0x2410) +#define NIC_PF_ECC2_SBE_ENA_W1S (0x2418) +#define NIC_PF_ECC2_DBE_INT (0x2500) +#define NIC_PF_ECC2_DBE_INT_W1S (0x2508) +#define NIC_PF_ECC2_DBE_ENA_W1C (0x2510) +#define NIC_PF_ECC2_DBE_ENA_W1S (0x2518) +#define NIC_PF_ECC3_SBE_INT (0x2600) +#define NIC_PF_ECC3_SBE_INT_W1S (0x2608) +#define NIC_PF_ECC3_SBE_ENA_W1C (0x2610) +#define NIC_PF_ECC3_SBE_ENA_W1S (0x2618) +#define NIC_PF_ECC3_DBE_INT (0x2700) +#define NIC_PF_ECC3_DBE_INT_W1S (0x2708) +#define NIC_PF_ECC3_DBE_ENA_W1C (0x2710) +#define NIC_PF_ECC3_DBE_ENA_W1S (0x2718) +#define NIC_PF_CPI_0_2047_CFG (0x200000) +#define NIC_PF_RSSI_0_4097_RQ (0x220000) +#define NIC_PF_LMAC_0_7_CFG (0x240000) +#define NIC_PF_LMAC_0_7_SW_XOFF (0x242000) +#define NIC_PF_LMAC_0_7_CREDIT (0x244000) +#define NIC_PF_CHAN_0_255_TX_CFG (0x400000) +#define NIC_PF_CHAN_0_255_RX_CFG (0x420000) +#define NIC_PF_CHAN_0_255_SW_XOFF (0x440000) +#define NIC_PF_CHAN_0_255_CREDIT (0x460000) +#define NIC_PF_CHAN_0_255_RX_BP_CFG (0x480000) +#define NIC_PF_SW_SYNC_RX (0x490000) +#define NIC_PF_SW_SYNC_RX_DONE (0x490008) +#define NIC_PF_TL2_0_63_CFG (0x500000) +#define NIC_PF_TL2_0_63_PRI (0x520000) +#define NIC_PF_TL2_0_63_SH_STATUS (0x580000) +#define NIC_PF_TL3A_0_63_CFG (0x5F0000) +#define NIC_PF_TL3_0_255_CFG (0x600000) +#define NIC_PF_TL3_0_255_CHAN (0x620000) +#define NIC_PF_TL3_0_255_PIR (0x640000) +#define NIC_PF_TL3_0_255_SW_XOFF (0x660000) +#define NIC_PF_TL3_0_255_CNM_RATE (0x680000) +#define NIC_PF_TL3_0_255_SH_STATUS (0x6A0000) +#define NIC_PF_TL4A_0_255_CFG (0x6F0000) +#define NIC_PF_TL4_0_1023_CFG (0x800000) +#define NIC_PF_TL4_0_1023_SW_XOFF (0x820000) +#define NIC_PF_TL4_0_1023_SH_STATUS (0x840000) +#define NIC_PF_TL4A_0_1023_CNM_RATE (0x880000) +#define NIC_PF_TL4A_0_1023_CNM_STATUS (0x8A0000) +#define NIC_PF_VF_0_127_MAILBOX_0_1 (0x20002030) +#define NIC_PF_VNIC_0_127_TX_STAT_0_4 (0x20004000) +#define NIC_PF_VNIC_0_127_RX_STAT_0_13 (0x20004100) +#define NIC_PF_QSET_0_127_LOCK_0_15 (0x20006000) +#define NIC_PF_QSET_0_127_CFG (0x20010000) +#define NIC_PF_QSET_0_127_RQ_0_7_CFG (0x20010400) +#define NIC_PF_QSET_0_127_RQ_0_7_DROP_CFG (0x20010420) +#define NIC_PF_QSET_0_127_RQ_0_7_BP_CFG (0x20010500) +#define NIC_PF_QSET_0_127_RQ_0_7_STAT_0_1 (0x20010600) +#define NIC_PF_QSET_0_127_SQ_0_7_CFG (0x20010C00) +#define NIC_PF_QSET_0_127_SQ_0_7_CFG2 (0x20010C08) +#define NIC_PF_QSET_0_127_SQ_0_7_STAT_0_1 (0x20010D00) + +#define NIC_PF_MSIX_VEC_0_18_ADDR (0x000000) +#define NIC_PF_MSIX_VEC_0_CTL (0x000008) +#define NIC_PF_MSIX_PBA_0 (0x0F0000) + +/* Virtual function register offsets */ +#define NIC_VNIC_CFG (0x000020) +#define NIC_VF_PF_MAILBOX_0_1 (0x000130) +#define NIC_VF_INT (0x000200) +#define NIC_VF_INT_W1S (0x000220) +#define NIC_VF_ENA_W1C (0x000240) +#define NIC_VF_ENA_W1S (0x000260) + +#define NIC_VNIC_RSS_CFG (0x0020E0) +#define NIC_VNIC_RSS_KEY_0_4 (0x002200) +#define NIC_VNIC_TX_STAT_0_4 (0x004000) +#define NIC_VNIC_RX_STAT_0_13 (0x004100) +#define NIC_QSET_RQ_GEN_CFG (0x010010) + +#define NIC_QSET_CQ_0_7_CFG (0x010400) +#define NIC_QSET_CQ_0_7_CFG2 (0x010408) +#define NIC_QSET_CQ_0_7_THRESH (0x010410) +#define NIC_QSET_CQ_0_7_BASE (0x010420) +#define NIC_QSET_CQ_0_7_HEAD (0x010428) +#define NIC_QSET_CQ_0_7_TAIL (0x010430) +#define NIC_QSET_CQ_0_7_DOOR (0x010438) +#define NIC_QSET_CQ_0_7_STATUS (0x010440) +#define NIC_QSET_CQ_0_7_STATUS2 (0x010448) +#define NIC_QSET_CQ_0_7_DEBUG (0x010450) + +#define NIC_QSET_RQ_0_7_CFG (0x010600) +#define NIC_QSET_RQ_0_7_STAT_0_1 (0x010700) + +#define NIC_QSET_SQ_0_7_CFG (0x010800) +#define NIC_QSET_SQ_0_7_THRESH (0x010810) +#define NIC_QSET_SQ_0_7_BASE (0x010820) +#define NIC_QSET_SQ_0_7_HEAD (0x010828) +#define NIC_QSET_SQ_0_7_TAIL (0x010830) +#define NIC_QSET_SQ_0_7_DOOR (0x010838) +#define NIC_QSET_SQ_0_7_STATUS (0x010840) +#define NIC_QSET_SQ_0_7_DEBUG (0x010848) +#define NIC_QSET_SQ_0_7_CNM_CHG (0x010860) +#define NIC_QSET_SQ_0_7_STAT_0_1 (0x010900) + +#define NIC_QSET_RBDR_0_1_CFG (0x010C00) +#define NIC_QSET_RBDR_0_1_THRESH (0x010C10) +#define NIC_QSET_RBDR_0_1_BASE (0x010C20) +#define NIC_QSET_RBDR_0_1_HEAD (0x010C28) +#define NIC_QSET_RBDR_0_1_TAIL (0x010C30) +#define NIC_QSET_RBDR_0_1_DOOR (0x010C38) +#define NIC_QSET_RBDR_0_1_STATUS0 (0x010C40) +#define NIC_QSET_RBDR_0_1_STATUS1 (0x010C48) +#define NIC_QSET_RBDR_0_1_PREFETCH_STATUS (0x010C50) + +#define NIC_VF_MSIX_VECTOR_0_19_ADDR (0x000000) +#define NIC_VF_MSIX_VECTOR_0_19_CTL (0x000008) +#define NIC_VF_MSIX_PBA (0x0F0000) + +/* Offsets within registers */ +#define NIC_MSIX_VEC_SHIFT 4 +#define NIC_Q_NUM_SHIFT 18 +#define NIC_QS_ID_SHIFT 21 +#define NIC_VF_NUM_SHIFT 21 + +/* Port kind configuration register */ +struct pkind_cfg { + uint64_t minlen:16; + uint64_t maxlen:16; + uint64_t reserved_32_32:1; + uint64_t lenerr_en:1; + uint64_t rx_hdr:3; + uint64_t hdr_sl:5; + uint64_t reserved_42_63:22; +}; + +#endif /* NIC_REG_H */ diff --git a/sys/dev/vnic/nicvf_main.c b/sys/dev/vnic/nicvf_main.c new file mode 100644 index 00000000000..42a1d37dc3a --- /dev/null +++ b/sys/dev/vnic/nicvf_main.c @@ -0,0 +1,1423 @@ +/* + * Copyright (C) 2015 Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ +#include +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include "thunder_bgx.h" +#include "nic_reg.h" +#include "nic.h" +#include "nicvf_queues.h" + +#define VNIC_VF_DEVSTR "Cavium Thunder NIC Virtual Function Driver" + +#define VNIC_VF_REG_RID PCIR_BAR(PCI_CFG_REG_BAR_NUM) + +/* Lock for core interface settings */ +#define NICVF_CORE_LOCK_INIT(nic) \ + sx_init(&(nic)->core_sx, device_get_nameunit((nic)->dev)) + +#define NICVF_CORE_LOCK_DESTROY(nic) \ + sx_destroy(&(nic)->core_sx) + +#define NICVF_CORE_LOCK(nic) sx_xlock(&(nic)->core_sx) +#define NICVF_CORE_UNLOCK(nic) sx_xunlock(&(nic)->core_sx) + +#define NICVF_CORE_LOCK_ASSERT(nic) sx_assert(&(nic)->core_sx, SA_XLOCKED) + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_10000 10000 +#define SPEED_40000 40000 + +MALLOC_DEFINE(M_NICVF, "nicvf", "ThunderX VNIC VF dynamic memory"); + +static int nicvf_probe(device_t); +static int nicvf_attach(device_t); +static int nicvf_detach(device_t); + +static device_method_t nicvf_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nicvf_probe), + DEVMETHOD(device_attach, nicvf_attach), + DEVMETHOD(device_detach, nicvf_detach), + + DEVMETHOD_END, +}; + +static driver_t nicvf_driver = { + "vnic", + nicvf_methods, + sizeof(struct nicvf), +}; + +static devclass_t nicvf_devclass; + +DRIVER_MODULE(nicvf, pci, nicvf_driver, nicvf_devclass, 0, 0); +MODULE_DEPEND(nicvf, pci, 1, 1, 1); +MODULE_DEPEND(nicvf, ether, 1, 1, 1); +MODULE_DEPEND(nicvf, vnic_pf, 1, 1, 1); + +static int nicvf_allocate_misc_interrupt(struct nicvf *); +static int nicvf_enable_misc_interrupt(struct nicvf *); +static int nicvf_allocate_net_interrupts(struct nicvf *); +static void nicvf_release_all_interrupts(struct nicvf *); +static int nicvf_hw_set_mac_addr(struct nicvf *, uint8_t *); +static void nicvf_config_cpi(struct nicvf *); +static int nicvf_init_resources(struct nicvf *); + +static int nicvf_setup_ifnet(struct nicvf *); +static int nicvf_setup_ifmedia(struct nicvf *); +static void nicvf_hw_addr_random(uint8_t *); + +static int nicvf_if_ioctl(struct ifnet *, u_long, caddr_t); +static void nicvf_if_init(void *); +static void nicvf_if_init_locked(struct nicvf *); +static int nicvf_if_transmit(struct ifnet *, struct mbuf *); +static void nicvf_if_qflush(struct ifnet *); +static uint64_t nicvf_if_getcounter(struct ifnet *, ift_counter); + +static int nicvf_stop_locked(struct nicvf *); + +static void nicvf_media_status(struct ifnet *, struct ifmediareq *); +static int nicvf_media_change(struct ifnet *); + +static void nicvf_tick_stats(void *); + +static int +nicvf_probe(device_t dev) +{ + uint16_t vendor_id; + uint16_t device_id; + + vendor_id = pci_get_vendor(dev); + device_id = pci_get_device(dev); + + if (vendor_id != PCI_VENDOR_ID_CAVIUM) + return (ENXIO); + + if (device_id == PCI_DEVICE_ID_THUNDER_NIC_VF || + device_id == PCI_DEVICE_ID_THUNDER_PASS1_NIC_VF) { + device_set_desc(dev, VNIC_VF_DEVSTR); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +nicvf_attach(device_t dev) +{ + struct nicvf *nic; + int rid, qcount; + int err = 0; + uint8_t hwaddr[ETHER_ADDR_LEN]; + uint8_t zeromac[] = {[0 ... (ETHER_ADDR_LEN - 1)] = 0}; + + nic = device_get_softc(dev); + nic->dev = dev; + nic->pnicvf = nic; + + NICVF_CORE_LOCK_INIT(nic); + + rid = VNIC_VF_REG_RID; + nic->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (nic->reg_base == NULL) { + device_printf(dev, "Could not allocate registers memory\n"); + return (ENXIO); + } + + qcount = MAX_CMP_QUEUES_PER_QS; + nic->max_queues = qcount; + + err = nicvf_set_qset_resources(nic); + if (err != 0) + goto err_free_res; + + /* Check if PF is alive and get MAC address for this VF */ + err = nicvf_allocate_misc_interrupt(nic); + if (err != 0) + goto err_free_res; + + NICVF_CORE_LOCK(nic); + err = nicvf_enable_misc_interrupt(nic); + NICVF_CORE_UNLOCK(nic); + if (err != 0) + goto err_release_intr; + + err = nicvf_allocate_net_interrupts(nic); + if (err != 0) { + device_printf(dev, + "Could not allocate network interface interrupts\n"); + goto err_free_ifnet; + } + + /* If no MAC address was obtained we generate random one */ + if (memcmp(nic->hwaddr, zeromac, ETHER_ADDR_LEN) == 0) { + nicvf_hw_addr_random(hwaddr); + memcpy(nic->hwaddr, hwaddr, ETHER_ADDR_LEN); + NICVF_CORE_LOCK(nic); + nicvf_hw_set_mac_addr(nic, hwaddr); + NICVF_CORE_UNLOCK(nic); + } + + /* Configure CPI alorithm */ + nic->cpi_alg = CPI_ALG_NONE; + NICVF_CORE_LOCK(nic); + nicvf_config_cpi(nic); + NICVF_CORE_UNLOCK(nic); + + err = nicvf_setup_ifnet(nic); + if (err != 0) { + device_printf(dev, "Could not set-up ifnet\n"); + goto err_release_intr; + } + + err = nicvf_setup_ifmedia(nic); + if (err != 0) { + device_printf(dev, "Could not set-up ifmedia\n"); + goto err_free_ifnet; + } + + mtx_init(&nic->stats_mtx, "VNIC stats", NULL, MTX_DEF); + callout_init_mtx(&nic->stats_callout, &nic->stats_mtx, 0); + + ether_ifattach(nic->ifp, nic->hwaddr); + + return (0); + +err_free_ifnet: + if_free(nic->ifp); +err_release_intr: + nicvf_release_all_interrupts(nic); +err_free_res: + bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(nic->reg_base), + nic->reg_base); + + return (err); +} + +static int +nicvf_detach(device_t dev) +{ + struct nicvf *nic; + + nic = device_get_softc(dev); + + NICVF_CORE_LOCK(nic); + /* Shut down the port and release ring resources */ + nicvf_stop_locked(nic); + /* Release stats lock */ + mtx_destroy(&nic->stats_mtx); + /* Release interrupts */ + nicvf_release_all_interrupts(nic); + /* Release memory resource */ + if (nic->reg_base != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(nic->reg_base), nic->reg_base); + } + + /* Remove all ifmedia configurations */ + ifmedia_removeall(&nic->if_media); + /* Free this ifnet */ + if_free(nic->ifp); + NICVF_CORE_UNLOCK(nic); + /* Finally destroy the lock */ + NICVF_CORE_LOCK_DESTROY(nic); + + return (0); +} + +static void +nicvf_hw_addr_random(uint8_t *hwaddr) +{ + uint32_t rnd; + uint8_t addr[ETHER_ADDR_LEN]; + + /* + * Create randomized MAC address. + * Set 'bsd' + random 24 low-order bits. + */ + rnd = arc4random() & 0x00ffffff; + addr[0] = 'b'; + addr[1] = 's'; + addr[2] = 'd'; + addr[3] = rnd >> 16; + addr[4] = rnd >> 8; + addr[5] = rnd >> 0; + + memcpy(hwaddr, addr, ETHER_ADDR_LEN); +} + +static int +nicvf_setup_ifnet(struct nicvf *nic) +{ + struct ifnet *ifp; + + ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(nic->dev, "Could not allocate ifnet structure\n"); + return (ENOMEM); + } + + nic->ifp = ifp; + + if_setsoftc(ifp, nic); + if_initname(ifp, device_get_name(nic->dev), device_get_unit(nic->dev)); + if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX); + + if_settransmitfn(ifp, nicvf_if_transmit); + if_setqflushfn(ifp, nicvf_if_qflush); + if_setioctlfn(ifp, nicvf_if_ioctl); + if_setinitfn(ifp, nicvf_if_init); + if_setgetcounterfn(ifp, nicvf_if_getcounter); + + /* Set send queue len to number to default maximum */ + if_setsendqlen(ifp, IFQ_MAXLEN); + if_setsendqready(ifp); + if_setmtu(ifp, ETHERMTU); + + if_setcapabilities(ifp, IFCAP_VLAN_MTU); +#ifdef DEVICE_POLLING +#error "DEVICE_POLLING not supported in VNIC driver yet" + if_setcapabilitiesbit(ifp, IFCAP_POLLING, 0); +#endif + if_setcapenable(ifp, if_getcapabilities(ifp)); + if_setmtu(ifp, ETHERMTU); + + return (0); +} + +static int +nicvf_setup_ifmedia(struct nicvf *nic) +{ + + ifmedia_init(&nic->if_media, IFM_IMASK, nicvf_media_change, + nicvf_media_status); + + /* + * Advertise availability of all possible connection types, + * even though not all are possible at the same time. + */ + + ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_10_T | IFM_FDX), + 0, NULL); + ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_100_TX | IFM_FDX), + 0, NULL); + ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_1000_T | IFM_FDX), + 0, NULL); + ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_10G_SR | IFM_FDX), + 0, NULL); + ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_40G_CR4 | IFM_FDX), + 0, NULL); + ifmedia_add(&nic->if_media, (IFM_ETHER | IFM_AUTO | IFM_FDX), + 0, NULL); + + ifmedia_set(&nic->if_media, (IFM_ETHER | IFM_AUTO | IFM_FDX)); + + return (0); +} + +static int +nicvf_if_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct nicvf *nic; + struct ifreq *ifr; + uint32_t flags; + int mask, err; +#if defined(INET) || defined(INET6) + struct ifaddr *ifa; + boolean_t avoid_reset = FALSE; +#endif + + nic = if_getsoftc(ifp); + ifr = (struct ifreq *)data; +#if defined(INET) || defined(INET6) + ifa = (struct ifaddr *)data; +#endif + err = 0; + switch (cmd) { + case SIOCSIFADDR: +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + avoid_reset = TRUE; +#endif +#ifdef INET6 + if (ifa->ifa_addr->sa_family == AF_INET6) + avoid_reset = TRUE; +#endif + +#if defined(INET) || defined(INET6) + /* Avoid reinitialization unless it's necessary */ + if (avoid_reset) { + ifp->if_flags |= IFF_UP; + if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) + nicvf_if_init(nic); +#ifdef INET + if (!(if_getflags(ifp) & IFF_NOARP)) + arp_ifinit(ifp, ifa); +#endif + + return (0); + } +#endif + err = ether_ioctl(ifp, cmd, data); + break; + case SIOCSIFMTU: + /* + * ARM64TODO: Needs to be implemented. + * Currently ETHERMTU is set by default. + */ + err = ether_ioctl(ifp, cmd, data); + break; + case SIOCSIFFLAGS: + NICVF_CORE_LOCK(nic); + if (if_getflags(ifp) & IFF_UP) { + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { + flags = ifp->if_flags ^ nic->if_flags; + if ((nic->if_flags & ifp->if_flags) & + IFF_PROMISC) { + /* Change promiscous mode */ +#if 0 + /* ARM64TODO */ + nicvf_set_promiscous(nic); +#endif + } + + if ((nic->if_flags ^ ifp->if_flags) & + IFF_ALLMULTI) { + /* Change multicasting settings */ +#if 0 + /* ARM64TODO */ + nicvf_set_multicast(nic); +#endif + } + } else { + nicvf_if_init_locked(nic); + } + } else if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) + nicvf_stop_locked(nic); + + nic->if_flags = ifp->if_flags; + NICVF_CORE_UNLOCK(nic); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { +#if 0 + NICVF_CORE_LOCK(nic); + /* ARM64TODO */ + nicvf_set_multicast(nic); + NICVF_CORE_UNLOCK(nic); +#endif + } + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + err = ifmedia_ioctl(ifp, ifr, &nic->if_media, cmd); + break; + + case SIOCSIFCAP: + mask = ifp->if_capenable ^ ifr->ifr_reqcap; + if (mask & IFCAP_VLAN_MTU) { + /* No work to do except acknowledge the change took. */ + ifp->if_capenable ^= IFCAP_VLAN_MTU; + } + break; + + default: + err = ether_ioctl(ifp, cmd, data); + break; + } + + return (err); +} + +static void +nicvf_if_init_locked(struct nicvf *nic) +{ + struct queue_set *qs = nic->qs; + struct ifnet *ifp; + int qidx; + int err; + caddr_t if_addr; + + NICVF_CORE_LOCK_ASSERT(nic); + ifp = nic->ifp; + + if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) + nicvf_stop_locked(nic); + + err = nicvf_enable_misc_interrupt(nic); + if (err != 0) { + if_printf(ifp, "Could not reenable Mbox interrupt\n"); + return; + } + + /* Get the latest MAC address */ + if_addr = if_getlladdr(ifp); + /* Update MAC address if changed */ + if (memcmp(nic->hwaddr, if_addr, ETHER_ADDR_LEN) != 0) { + memcpy(nic->hwaddr, if_addr, ETHER_ADDR_LEN); + nicvf_hw_set_mac_addr(nic, if_addr); + } + + /* Initialize the queues */ + err = nicvf_init_resources(nic); + if (err != 0) + goto error; + + /* Make sure queue initialization is written */ + wmb(); + + nicvf_reg_write(nic, NIC_VF_INT, ~0UL); + /* Enable Qset err interrupt */ + nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0); + + /* Enable completion queue interrupt */ + for (qidx = 0; qidx < qs->cq_cnt; qidx++) + nicvf_enable_intr(nic, NICVF_INTR_CQ, qidx); + + /* Enable RBDR threshold interrupt */ + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) + nicvf_enable_intr(nic, NICVF_INTR_RBDR, qidx); + + nic->drv_stats.txq_stop = 0; + nic->drv_stats.txq_wake = 0; + + /* Activate network interface */ + if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); + + /* Schedule callout to update stats */ + callout_reset(&nic->stats_callout, hz, nicvf_tick_stats, nic); + + return; + +error: + /* Something went very wrong. Disable this ifnet for good */ + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING); +} + +static void +nicvf_if_init(void *if_softc) +{ + struct nicvf *nic = if_softc; + + NICVF_CORE_LOCK(nic); + nicvf_if_init_locked(nic); + NICVF_CORE_UNLOCK(nic); +} + +static int +nicvf_if_transmit(struct ifnet *ifp, struct mbuf *mbuf) +{ + struct nicvf *nic = if_getsoftc(ifp); + struct queue_set *qs = nic->qs; + struct snd_queue *sq; + int qidx; + int err = 0; + + + if (__predict_false(qs == NULL)) { + panic("%s: missing queue set for %s", __func__, + device_get_nameunit(nic->dev)); + } + + /* Select queue */ + if (M_HASHTYPE_GET(mbuf) != M_HASHTYPE_NONE) + qidx = mbuf->m_pkthdr.flowid % qs->sq_cnt; + else + qidx = curcpu % qs->sq_cnt; + + sq = &qs->sq[qidx]; + + if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) { + if (mbuf != NULL) + err = drbr_enqueue(ifp, sq->br, mbuf); + return (err); + } + + if (mbuf != NULL) { + err = drbr_enqueue(ifp, sq->br, mbuf); + if (err != 0) + return (err); + } + + taskqueue_enqueue(sq->snd_taskq, &sq->snd_task); + + return (0); +} + +static void +nicvf_if_qflush(struct ifnet *ifp) +{ + struct nicvf *nic; + struct queue_set *qs; + struct snd_queue *sq; + struct mbuf *mbuf; + size_t idx; + + nic = if_getsoftc(ifp); + qs = nic->qs; + + for (idx = 0; idx < qs->sq_cnt; idx++) { + sq = &qs->sq[idx]; + NICVF_TX_LOCK(sq); + while ((mbuf = buf_ring_dequeue_sc(sq->br)) != NULL) + m_freem(mbuf); + NICVF_TX_UNLOCK(sq); + } + if_qflush(ifp); +} + +static uint64_t +nicvf_if_getcounter(struct ifnet *ifp, ift_counter cnt) +{ + struct nicvf *nic; + struct nicvf_hw_stats *hw_stats; + struct nicvf_drv_stats *drv_stats; + + nic = if_getsoftc(ifp); + hw_stats = &nic->hw_stats; + drv_stats = &nic->drv_stats; + + switch (cnt) { + case IFCOUNTER_IPACKETS: + return (drv_stats->rx_frames_ok); + case IFCOUNTER_OPACKETS: + return (drv_stats->tx_frames_ok); + case IFCOUNTER_IBYTES: + return (hw_stats->rx_bytes); + case IFCOUNTER_OBYTES: + return (hw_stats->tx_bytes_ok); + case IFCOUNTER_IMCASTS: + return (hw_stats->rx_mcast_frames); + case IFCOUNTER_COLLISIONS: + return (0); + case IFCOUNTER_IQDROPS: + return (drv_stats->rx_drops); + case IFCOUNTER_OQDROPS: + return (drv_stats->tx_drops); + default: + return (if_get_counter_default(ifp, cnt)); + } + +} + +static void +nicvf_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct nicvf *nic = if_getsoftc(ifp); + + NICVF_CORE_LOCK(nic); + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + + if (nic->link_up) { + /* Device attached to working network */ + ifmr->ifm_status |= IFM_ACTIVE; + } + + switch (nic->speed) { + case SPEED_10: + ifmr->ifm_active |= IFM_10_T; + break; + case SPEED_100: + ifmr->ifm_active |= IFM_100_TX; + break; + case SPEED_1000: + ifmr->ifm_active |= IFM_1000_T; + break; + case SPEED_10000: + ifmr->ifm_active |= IFM_10G_SR; + break; + case SPEED_40000: + ifmr->ifm_active |= IFM_40G_CR4; + break; + default: + ifmr->ifm_active |= IFM_AUTO; + break; + } + + if (nic->duplex) + ifmr->ifm_active |= IFM_FDX; + else + ifmr->ifm_active |= IFM_HDX; + + NICVF_CORE_UNLOCK(nic); +} + +static int +nicvf_media_change(struct ifnet *ifp __unused) +{ + + return (0); +} + +/* Register read/write APIs */ +void +nicvf_reg_write(struct nicvf *nic, bus_space_handle_t offset, uint64_t val) +{ + + bus_write_8(nic->reg_base, offset, val); +} + +uint64_t +nicvf_reg_read(struct nicvf *nic, uint64_t offset) +{ + + return (bus_read_8(nic->reg_base, offset)); +} + +void +nicvf_queue_reg_write(struct nicvf *nic, bus_space_handle_t offset, + uint64_t qidx, uint64_t val) +{ + + bus_write_8(nic->reg_base, offset + (qidx << NIC_Q_NUM_SHIFT), val); +} + +uint64_t +nicvf_queue_reg_read(struct nicvf *nic, bus_space_handle_t offset, + uint64_t qidx) +{ + + return (bus_read_8(nic->reg_base, offset + (qidx << NIC_Q_NUM_SHIFT))); +} + +/* VF -> PF mailbox communication */ +static void +nicvf_write_to_mbx(struct nicvf *nic, union nic_mbx *mbx) +{ + uint64_t *msg = (uint64_t *)mbx; + + nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 0, msg[0]); + nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 8, msg[1]); +} + +int +nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx) +{ + int timeout = NIC_MBOX_MSG_TIMEOUT * 10; + int sleep = 2; + + NICVF_CORE_LOCK_ASSERT(nic); + + nic->pf_acked = FALSE; + nic->pf_nacked = FALSE; + + nicvf_write_to_mbx(nic, mbx); + + /* Wait for previous message to be acked, timeout 2sec */ + while (!nic->pf_acked) { + if (nic->pf_nacked) + return (EINVAL); + + DELAY(sleep * 1000); + + if (nic->pf_acked) + break; + timeout -= sleep; + if (!timeout) { + device_printf(nic->dev, + "PF didn't ack to mbox msg %d from VF%d\n", + (mbx->msg.msg & 0xFF), nic->vf_id); + + return (EBUSY); + } + } + return (0); +} + +/* + * Checks if VF is able to comminicate with PF + * and also gets the VNIC number this VF is associated to. + */ +static int +nicvf_check_pf_ready(struct nicvf *nic) +{ + union nic_mbx mbx = {}; + + mbx.msg.msg = NIC_MBOX_MSG_READY; + if (nicvf_send_msg_to_pf(nic, &mbx)) { + device_printf(nic->dev, + "PF didn't respond to READY msg\n"); + return 0; + } + + return 1; +} + +static void +nicvf_read_bgx_stats(struct nicvf *nic, struct bgx_stats_msg *bgx) +{ + + if (bgx->rx) + nic->bgx_stats.rx_stats[bgx->idx] = bgx->stats; + else + nic->bgx_stats.tx_stats[bgx->idx] = bgx->stats; +} + +static void +nicvf_handle_mbx_intr(struct nicvf *nic) +{ + union nic_mbx mbx = {}; + uint64_t *mbx_data; + uint64_t mbx_addr; + int i; + + mbx_addr = NIC_VF_PF_MAILBOX_0_1; + mbx_data = (uint64_t *)&mbx; + + for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) { + *mbx_data = nicvf_reg_read(nic, mbx_addr); + mbx_data++; + mbx_addr += sizeof(uint64_t); + } + + switch (mbx.msg.msg) { + case NIC_MBOX_MSG_READY: + nic->pf_acked = TRUE; + nic->vf_id = mbx.nic_cfg.vf_id & 0x7F; + nic->tns_mode = mbx.nic_cfg.tns_mode & 0x7F; + nic->node = mbx.nic_cfg.node_id; + memcpy(nic->hwaddr, mbx.nic_cfg.mac_addr, ETHER_ADDR_LEN); + nic->loopback_supported = mbx.nic_cfg.loopback_supported; + nic->link_up = FALSE; + nic->duplex = 0; + nic->speed = 0; + break; + case NIC_MBOX_MSG_ACK: + nic->pf_acked = TRUE; + break; + case NIC_MBOX_MSG_NACK: + nic->pf_nacked = TRUE; + break; + case NIC_MBOX_MSG_BGX_STATS: + nicvf_read_bgx_stats(nic, &mbx.bgx_stats); + nic->pf_acked = TRUE; + break; + case NIC_MBOX_MSG_BGX_LINK_CHANGE: + nic->pf_acked = TRUE; + nic->link_up = mbx.link_status.link_up; + nic->duplex = mbx.link_status.duplex; + nic->speed = mbx.link_status.speed; + if (nic->link_up) { + if_setbaudrate(nic->ifp, nic->speed * 1000000); + if_link_state_change(nic->ifp, LINK_STATE_UP); + } else { + if_setbaudrate(nic->ifp, 0); + if_link_state_change(nic->ifp, LINK_STATE_DOWN); + } + break; + default: + device_printf(nic->dev, + "Invalid message from PF, msg 0x%x\n", mbx.msg.msg); + break; + } + nicvf_clear_intr(nic, NICVF_INTR_MBOX, 0); +} + +static int +nicvf_hw_set_mac_addr(struct nicvf *nic, uint8_t *hwaddr) +{ + union nic_mbx mbx = {}; + + mbx.mac.msg = NIC_MBOX_MSG_SET_MAC; + mbx.mac.vf_id = nic->vf_id; + memcpy(mbx.mac.mac_addr, hwaddr, ETHER_ADDR_LEN); + + return (nicvf_send_msg_to_pf(nic, &mbx)); +} + +static void +nicvf_config_cpi(struct nicvf *nic) +{ + union nic_mbx mbx = {}; + + mbx.cpi_cfg.msg = NIC_MBOX_MSG_CPI_CFG; + mbx.cpi_cfg.vf_id = nic->vf_id; + mbx.cpi_cfg.cpi_alg = nic->cpi_alg; + mbx.cpi_cfg.rq_cnt = nic->qs->rq_cnt; + + nicvf_send_msg_to_pf(nic, &mbx); +} + +static int +nicvf_init_resources(struct nicvf *nic) +{ + int err; + union nic_mbx mbx = {}; + + mbx.msg.msg = NIC_MBOX_MSG_CFG_DONE; + + /* Enable Qset */ + nicvf_qset_config(nic, TRUE); + + /* Initialize queues and HW for data transfer */ + err = nicvf_config_data_transfer(nic, TRUE); + if (err) { + device_printf(nic->dev, + "Failed to alloc/config VF's QSet resources\n"); + return (err); + } + + /* Send VF config done msg to PF */ + nicvf_write_to_mbx(nic, &mbx); + + return (0); +} + +static void +nicvf_misc_intr_handler(void *arg) +{ + struct nicvf *nic = (struct nicvf *)arg; + uint64_t intr; + + intr = nicvf_reg_read(nic, NIC_VF_INT); + /* Check for spurious interrupt */ + if (!(intr & NICVF_INTR_MBOX_MASK)) + return; + + nicvf_handle_mbx_intr(nic); +} + +static int +nicvf_intr_handler(void *arg) +{ + struct nicvf *nic; + struct cmp_queue *cq; + int qidx; + + cq = (struct cmp_queue *)arg; + nic = cq->nic; + qidx = cq->idx; + + /* Disable interrupts */ + nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx); + + taskqueue_enqueue(cq->cmp_taskq, &cq->cmp_task); + + /* Clear interrupt */ + nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx); + + return (FILTER_HANDLED); +} + +static void +nicvf_rbdr_intr_handler(void *arg) +{ + struct nicvf *nic; + struct queue_set *qs; + struct rbdr *rbdr; + int qidx; + + nic = (struct nicvf *)arg; + + /* Disable RBDR interrupt and schedule softirq */ + for (qidx = 0; qidx < nic->qs->rbdr_cnt; qidx++) { + if (!nicvf_is_intr_enabled(nic, NICVF_INTR_RBDR, qidx)) + continue; + nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx); + + qs = nic->qs; + rbdr = &qs->rbdr[qidx]; + taskqueue_enqueue(rbdr->rbdr_taskq, &rbdr->rbdr_task_nowait); + /* Clear interrupt */ + nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx); + } +} + +static void +nicvf_qs_err_intr_handler(void *arg) +{ + struct nicvf *nic = (struct nicvf *)arg; + struct queue_set *qs = nic->qs; + + /* Disable Qset err interrupt and schedule softirq */ + nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0); + taskqueue_enqueue(qs->qs_err_taskq, &qs->qs_err_task); + nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0); + +} + +static int +nicvf_enable_msix(struct nicvf *nic) +{ + struct pci_devinfo *dinfo; + int rid, count; + int ret; + + dinfo = device_get_ivars(nic->dev); + rid = dinfo->cfg.msix.msix_table_bar; + nic->msix_table_res = + bus_alloc_resource_any(nic->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (nic->msix_table_res == NULL) { + device_printf(nic->dev, + "Could not allocate memory for MSI-X table\n"); + return (ENXIO); + } + + count = nic->num_vec = NIC_VF_MSIX_VECTORS; + + ret = pci_alloc_msix(nic->dev, &count); + if ((ret != 0) || (count != nic->num_vec)) { + device_printf(nic->dev, + "Request for #%d msix vectors failed, error: %d\n", + nic->num_vec, ret); + return (ret); + } + + nic->msix_enabled = 1; + return (0); +} + +static void +nicvf_disable_msix(struct nicvf *nic) +{ + + if (nic->msix_enabled) { + pci_release_msi(nic->dev); + nic->msix_enabled = 0; + nic->num_vec = 0; + } +} + +static void +nicvf_release_all_interrupts(struct nicvf *nic) +{ + struct resource *res; + int irq; + int err; + + /* Free registered interrupts */ + for (irq = 0; irq < nic->num_vec; irq++) { + res = nic->msix_entries[irq].irq_res; + if (res == NULL) + continue; + /* Teardown interrupt first */ + if (nic->msix_entries[irq].handle != NULL) { + err = bus_teardown_intr(nic->dev, + nic->msix_entries[irq].irq_res, + nic->msix_entries[irq].handle); + KASSERT(err == 0, + ("ERROR: Unable to teardown interrupt %d", irq)); + nic->msix_entries[irq].handle = NULL; + } + + bus_release_resource(nic->dev, SYS_RES_IRQ, + rman_get_rid(res), nic->msix_entries[irq].irq_res); + nic->msix_entries[irq].irq_res = NULL; + } + /* Disable MSI-X */ + nicvf_disable_msix(nic); +} + +/* + * Initialize MSIX vectors and register MISC interrupt. + * Send READY message to PF to check if its alive + */ +static int +nicvf_allocate_misc_interrupt(struct nicvf *nic) +{ + struct resource *res; + int irq, rid; + int ret = 0; + + /* Return if mailbox interrupt is already registered */ + if (nic->msix_enabled) + return (0); + + /* Enable MSI-X */ + if (nicvf_enable_msix(nic) != 0) + return (ENXIO); + + irq = NICVF_INTR_ID_MISC; + rid = irq + 1; + nic->msix_entries[irq].irq_res = bus_alloc_resource_any(nic->dev, + SYS_RES_IRQ, &rid, (RF_SHAREABLE | RF_ACTIVE)); + if (nic->msix_entries[irq].irq_res == NULL) { + device_printf(nic->dev, + "Could not allocate Mbox interrupt for VF%d\n", + device_get_unit(nic->dev)); + return (ENXIO); + } + + ret = bus_setup_intr(nic->dev, nic->msix_entries[irq].irq_res, + (INTR_MPSAFE | INTR_TYPE_MISC), NULL, nicvf_misc_intr_handler, nic, + &nic->msix_entries[irq].handle); + if (ret != 0) { + res = nic->msix_entries[irq].irq_res; + bus_release_resource(nic->dev, SYS_RES_IRQ, + rman_get_rid(res), res); + nic->msix_entries[irq].irq_res = NULL; + return (ret); + } + + return (0); +} + +static int +nicvf_enable_misc_interrupt(struct nicvf *nic) +{ + + /* Enable mailbox interrupt */ + nicvf_enable_intr(nic, NICVF_INTR_MBOX, 0); + + /* Check if VF is able to communicate with PF */ + if (!nicvf_check_pf_ready(nic)) { + nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0); + return (ENXIO); + } + + return (0); +} + +static void +nicvf_release_net_interrupts(struct nicvf *nic) +{ + struct resource *res; + int irq; + int err; + + for_each_cq_irq(irq) { + res = nic->msix_entries[irq].irq_res; + if (res == NULL) + continue; + /* Teardown active interrupts first */ + if (nic->msix_entries[irq].handle != NULL) { + err = bus_teardown_intr(nic->dev, + nic->msix_entries[irq].irq_res, + nic->msix_entries[irq].handle); + KASSERT(err == 0, + ("ERROR: Unable to teardown CQ interrupt %d", + (irq - NICVF_INTR_ID_CQ))); + if (err != 0) + continue; + } + + /* Release resource */ + bus_release_resource(nic->dev, SYS_RES_IRQ, rman_get_rid(res), + res); + nic->msix_entries[irq].irq_res = NULL; + } + + for_each_rbdr_irq(irq) { + res = nic->msix_entries[irq].irq_res; + if (res == NULL) + continue; + /* Teardown active interrupts first */ + if (nic->msix_entries[irq].handle != NULL) { + err = bus_teardown_intr(nic->dev, + nic->msix_entries[irq].irq_res, + nic->msix_entries[irq].handle); + KASSERT(err == 0, + ("ERROR: Unable to teardown RDBR interrupt %d", + (irq - NICVF_INTR_ID_RBDR))); + if (err != 0) + continue; + } + + /* Release resource */ + bus_release_resource(nic->dev, SYS_RES_IRQ, rman_get_rid(res), + res); + nic->msix_entries[irq].irq_res = NULL; + } + + irq = NICVF_INTR_ID_QS_ERR; + res = nic->msix_entries[irq].irq_res; + if (res != NULL) { + /* Teardown active interrupts first */ + if (nic->msix_entries[irq].handle != NULL) { + err = bus_teardown_intr(nic->dev, + nic->msix_entries[irq].irq_res, + nic->msix_entries[irq].handle); + KASSERT(err == 0, + ("ERROR: Unable to teardown QS Error interrupt %d", + irq)); + if (err != 0) + return; + } + + /* Release resource */ + bus_release_resource(nic->dev, SYS_RES_IRQ, rman_get_rid(res), + res); + nic->msix_entries[irq].irq_res = NULL; + } +} + +static int +nicvf_allocate_net_interrupts(struct nicvf *nic) +{ + int irq, rid; + int qidx; + int ret = 0; + + /* MSI-X must be configured by now */ + if (!nic->msix_enabled) { + device_printf(nic->dev, "Cannot alloacte queue interrups. " + "MSI-X interrupts disabled.\n"); + return (ENXIO); + } + + /* Register CQ interrupts */ + for_each_cq_irq(irq) { + if (irq >= (NICVF_INTR_ID_CQ + nic->qs->cq_cnt)) + break; + + qidx = irq - NICVF_INTR_ID_CQ; + rid = irq + 1; + nic->msix_entries[irq].irq_res = bus_alloc_resource_any(nic->dev, + SYS_RES_IRQ, &rid, (RF_SHAREABLE | RF_ACTIVE)); + if (nic->msix_entries[irq].irq_res == NULL) { + device_printf(nic->dev, + "Could not allocate CQ interrupt %d for VF%d\n", + (irq - NICVF_INTR_ID_CQ), device_get_unit(nic->dev)); + ret = ENXIO; + goto error; + } + ret = bus_setup_intr(nic->dev, nic->msix_entries[irq].irq_res, + (INTR_MPSAFE | INTR_TYPE_NET), nicvf_intr_handler, + NULL, &nic->qs->cq[qidx], &nic->msix_entries[irq].handle); + if (ret != 0) { + device_printf(nic->dev, + "Could not setup CQ interrupt %d for VF%d\n", + (irq - NICVF_INTR_ID_CQ), device_get_unit(nic->dev)); + goto error; + } + } + + /* Register RBDR interrupt */ + for_each_rbdr_irq(irq) { + if (irq >= (NICVF_INTR_ID_RBDR + nic->qs->rbdr_cnt)) + break; + + rid = irq + 1; + nic->msix_entries[irq].irq_res = bus_alloc_resource_any(nic->dev, + SYS_RES_IRQ, &rid, (RF_SHAREABLE | RF_ACTIVE)); + if (nic->msix_entries[irq].irq_res == NULL) { + device_printf(nic->dev, + "Could not allocate RBDR interrupt %d for VF%d\n", + (irq - NICVF_INTR_ID_RBDR), + device_get_unit(nic->dev)); + ret = ENXIO; + goto error; + } + ret = bus_setup_intr(nic->dev, nic->msix_entries[irq].irq_res, + (INTR_MPSAFE | INTR_TYPE_NET), NULL, + nicvf_rbdr_intr_handler, nic, + &nic->msix_entries[irq].handle); + if (ret != 0) { + device_printf(nic->dev, + "Could not setup RBDR interrupt %d for VF%d\n", + (irq - NICVF_INTR_ID_RBDR), + device_get_unit(nic->dev)); + goto error; + } + } + + /* Register QS error interrupt */ + irq = NICVF_INTR_ID_QS_ERR; + rid = irq + 1; + nic->msix_entries[irq].irq_res = bus_alloc_resource_any(nic->dev, + SYS_RES_IRQ, &rid, (RF_SHAREABLE | RF_ACTIVE)); + if (nic->msix_entries[irq].irq_res == NULL) { + device_printf(nic->dev, + "Could not allocate QS Error interrupt for VF%d\n", + device_get_unit(nic->dev)); + ret = ENXIO; + goto error; + } + ret = bus_setup_intr(nic->dev, nic->msix_entries[irq].irq_res, + (INTR_MPSAFE | INTR_TYPE_NET), NULL, nicvf_qs_err_intr_handler, + nic, &nic->msix_entries[irq].handle); + if (ret != 0) { + device_printf(nic->dev, + "Could not setup QS Error interrupt for VF%d\n", + device_get_unit(nic->dev)); + goto error; + } + + return (0); +error: + nicvf_release_net_interrupts(nic); + return (ret); +} + +static int +nicvf_stop_locked(struct nicvf *nic) +{ + struct ifnet *ifp; + int qidx; + struct queue_set *qs = nic->qs; + union nic_mbx mbx = {}; + + NICVF_CORE_LOCK_ASSERT(nic); + /* Stop callout. Can block here since holding SX lock */ + callout_drain(&nic->stats_callout); + + ifp = nic->ifp; + + mbx.msg.msg = NIC_MBOX_MSG_SHUTDOWN; + nicvf_send_msg_to_pf(nic, &mbx); + + /* Disable RBDR & QS error interrupts */ + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) { + nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx); + nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx); + } + nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0); + nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0); + + /* Deactivate network interface */ + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING); + + /* Free resources */ + nicvf_config_data_transfer(nic, FALSE); + + /* Disable HW Qset */ + nicvf_qset_config(nic, FALSE); + + /* disable mailbox interrupt */ + nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0); + + return (0); +} + +static void +nicvf_update_stats(struct nicvf *nic) +{ + int qidx; + struct nicvf_hw_stats *stats = &nic->hw_stats; + struct nicvf_drv_stats *drv_stats = &nic->drv_stats; + struct queue_set *qs = nic->qs; + +#define GET_RX_STATS(reg) \ + nicvf_reg_read(nic, NIC_VNIC_RX_STAT_0_13 | ((reg) << 3)) +#define GET_TX_STATS(reg) \ + nicvf_reg_read(nic, NIC_VNIC_TX_STAT_0_4 | ((reg) << 3)) + + stats->rx_bytes = GET_RX_STATS(RX_OCTS); + stats->rx_ucast_frames = GET_RX_STATS(RX_UCAST); + stats->rx_bcast_frames = GET_RX_STATS(RX_BCAST); + stats->rx_mcast_frames = GET_RX_STATS(RX_MCAST); + stats->rx_fcs_errors = GET_RX_STATS(RX_FCS); + stats->rx_l2_errors = GET_RX_STATS(RX_L2ERR); + stats->rx_drop_red = GET_RX_STATS(RX_RED); + stats->rx_drop_red_bytes = GET_RX_STATS(RX_RED_OCTS); + stats->rx_drop_overrun = GET_RX_STATS(RX_ORUN); + stats->rx_drop_overrun_bytes = GET_RX_STATS(RX_ORUN_OCTS); + stats->rx_drop_bcast = GET_RX_STATS(RX_DRP_BCAST); + stats->rx_drop_mcast = GET_RX_STATS(RX_DRP_MCAST); + stats->rx_drop_l3_bcast = GET_RX_STATS(RX_DRP_L3BCAST); + stats->rx_drop_l3_mcast = GET_RX_STATS(RX_DRP_L3MCAST); + + stats->tx_bytes_ok = GET_TX_STATS(TX_OCTS); + stats->tx_ucast_frames_ok = GET_TX_STATS(TX_UCAST); + stats->tx_bcast_frames_ok = GET_TX_STATS(TX_BCAST); + stats->tx_mcast_frames_ok = GET_TX_STATS(TX_MCAST); + stats->tx_drops = GET_TX_STATS(TX_DROP); + + drv_stats->tx_frames_ok = stats->tx_ucast_frames_ok + + stats->tx_bcast_frames_ok + stats->tx_mcast_frames_ok; + drv_stats->rx_drops = stats->rx_drop_red + stats->rx_drop_overrun; + drv_stats->tx_drops = stats->tx_drops; + + /* Update RQ and SQ stats */ + for (qidx = 0; qidx < qs->rq_cnt; qidx++) + nicvf_update_rq_stats(nic, qidx); + for (qidx = 0; qidx < qs->sq_cnt; qidx++) + nicvf_update_sq_stats(nic, qidx); +} + +static void +nicvf_tick_stats(void *arg) +{ + struct nicvf *nic; + + nic = (struct nicvf *)arg; + + /* Read the statistics */ + nicvf_update_stats(nic); + + callout_reset(&nic->stats_callout, hz, nicvf_tick_stats, nic); +} diff --git a/sys/dev/vnic/nicvf_queues.c b/sys/dev/vnic/nicvf_queues.c new file mode 100644 index 00000000000..4c5d28086b3 --- /dev/null +++ b/sys/dev/vnic/nicvf_queues.c @@ -0,0 +1,2139 @@ +/* + * Copyright (C) 2015 Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "thunder_bgx.h" +#include "nic_reg.h" +#include "nic.h" +#include "q_struct.h" +#include "nicvf_queues.h" + +#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define dprintf(dev, fmt, ...) device_printf(dev, fmt, ##__VA_ARGS__) +#else +#define dprintf(dev, fmt, ...) +#endif + +MALLOC_DECLARE(M_NICVF); + +static void nicvf_free_snd_queue(struct nicvf *, struct snd_queue *); +static int nicvf_tx_mbuf_locked(struct snd_queue *, struct mbuf *); +static struct mbuf * nicvf_get_rcv_mbuf(struct nicvf *, struct cqe_rx_t *); +static void nicvf_sq_disable(struct nicvf *, int); +static void nicvf_sq_enable(struct nicvf *, struct snd_queue *, int); +static void nicvf_put_sq_desc(struct snd_queue *, int); +static void nicvf_cmp_queue_config(struct nicvf *, struct queue_set *, int, + boolean_t); +static void nicvf_sq_free_used_descs(struct nicvf *, struct snd_queue *, int); + +static void nicvf_rbdr_task(void *, int); +static void nicvf_rbdr_task_nowait(void *, int); + +struct rbuf_info { + bus_dma_tag_t dmat; + bus_dmamap_t dmap; + struct mbuf * mbuf; +}; + +#define GET_RBUF_INFO(x) ((struct rbuf_info *)((x) - NICVF_RCV_BUF_ALIGN_BYTES)) + +/* Poll a register for a specific value */ +static int nicvf_poll_reg(struct nicvf *nic, int qidx, + uint64_t reg, int bit_pos, int bits, int val) +{ + uint64_t bit_mask; + uint64_t reg_val; + int timeout = 10; + + bit_mask = (1UL << bits) - 1; + bit_mask = (bit_mask << bit_pos); + + while (timeout) { + reg_val = nicvf_queue_reg_read(nic, reg, qidx); + if (((reg_val & bit_mask) >> bit_pos) == val) + return (0); + + DELAY(1000); + timeout--; + } + device_printf(nic->dev, "Poll on reg 0x%lx failed\n", reg); + return (ETIMEDOUT); +} + +/* Callback for bus_dmamap_load() */ +static void +nicvf_dmamap_q_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + bus_addr_t *paddr; + + KASSERT(nseg == 1, ("wrong number of segments, should be 1")); + paddr = arg; + *paddr = segs->ds_addr; +} + +/* Allocate memory for a queue's descriptors */ +static int +nicvf_alloc_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem, + int q_len, int desc_size, int align_bytes) +{ + int err, err_dmat; + + /* Create DMA tag first */ + err = bus_dma_tag_create( + bus_get_dma_tag(nic->dev), /* parent tag */ + align_bytes, /* alignment */ + 0, /* boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + (q_len * desc_size), /* maxsize */ + 1, /* nsegments */ + (q_len * desc_size), /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockfuncarg */ + &dmem->dmat); /* dmat */ + + if (err != 0) { + device_printf(nic->dev, + "Failed to create busdma tag for descriptors ring\n"); + return (err); + } + + /* Allocate segment of continuous DMA safe memory */ + err = bus_dmamem_alloc( + dmem->dmat, /* DMA tag */ + &dmem->base, /* virtual address */ + (BUS_DMA_NOWAIT | BUS_DMA_ZERO), /* flags */ + &dmem->dmap); /* DMA map */ + if (err != 0) { + device_printf(nic->dev, "Failed to allocate DMA safe memory for" + "descriptors ring\n"); + goto dmamem_fail; + } + + err = bus_dmamap_load( + dmem->dmat, + dmem->dmap, + dmem->base, + (q_len * desc_size), /* allocation size */ + nicvf_dmamap_q_cb, /* map to DMA address cb. */ + &dmem->phys_base, /* physical address */ + BUS_DMA_NOWAIT); + if (err != 0) { + device_printf(nic->dev, + "Cannot load DMA map of descriptors ring\n"); + goto dmamap_fail; + } + + dmem->q_len = q_len; + dmem->size = (desc_size * q_len); + + return (0); + +dmamap_fail: + bus_dmamem_free(dmem->dmat, dmem->base, dmem->dmap); + dmem->phys_base = 0; +dmamem_fail: + err_dmat = bus_dma_tag_destroy(dmem->dmat); + dmem->base = NULL; + KASSERT(err_dmat == 0, + ("%s: Trying to destroy BUSY DMA tag", __func__)); + + return (err); +} + +/* Free queue's descriptor memory */ +static void +nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem) +{ + int err; + + if ((dmem == NULL) || (dmem->base == NULL)) + return; + + /* Unload a map */ + bus_dmamap_sync(dmem->dmat, dmem->dmap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(dmem->dmat, dmem->dmap); + /* Free DMA memory */ + bus_dmamem_free(dmem->dmat, dmem->base, dmem->dmap); + /* Destroy DMA tag */ + err = bus_dma_tag_destroy(dmem->dmat); + + KASSERT(err == 0, + ("%s: Trying to destroy BUSY DMA tag", __func__)); + + dmem->phys_base = 0; + dmem->base = NULL; +} + +/* + * Allocate buffer for packet reception + * HW returns memory address where packet is DMA'ed but not a pointer + * into RBDR ring, so save buffer address at the start of fragment and + * align the start address to a cache aligned address + */ +static __inline int +nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr, + bus_dmamap_t dmap, int mflags, uint32_t buf_len, bus_addr_t *rbuf) +{ + struct mbuf *mbuf; + struct rbuf_info *rinfo; + bus_dma_segment_t segs[1]; + int nsegs; + int err; + + mbuf = m_getjcl(mflags, MT_DATA, M_PKTHDR, MCLBYTES); + if (mbuf == NULL) + return (ENOMEM); + + /* + * The length is equal to the actual length + one 128b line + * used as a room for rbuf_info structure. + */ + mbuf->m_len = mbuf->m_pkthdr.len = buf_len; + + err = bus_dmamap_load_mbuf_sg(rbdr->rbdr_buff_dmat, dmap, mbuf, segs, + &nsegs, BUS_DMA_NOWAIT); + if (err != 0) { + device_printf(nic->dev, + "Failed to map mbuf into DMA visible memory, err: %d\n", + err); + m_freem(mbuf); + bus_dmamap_destroy(rbdr->rbdr_buff_dmat, dmap); + return (err); + } + if (nsegs != 1) + panic("Unexpected number of DMA segments for RB: %d", nsegs); + /* + * Now use the room for rbuf_info structure + * and adjust mbuf data and length. + */ + rinfo = (struct rbuf_info *)mbuf->m_data; + m_adj(mbuf, NICVF_RCV_BUF_ALIGN_BYTES); + + rinfo->dmat = rbdr->rbdr_buff_dmat; + rinfo->dmap = dmap; + rinfo->mbuf = mbuf; + + *rbuf = segs[0].ds_addr + NICVF_RCV_BUF_ALIGN_BYTES; + + return (0); +} + +/* Retrieve mbuf for received packet */ +static struct mbuf * +nicvf_rb_ptr_to_mbuf(struct nicvf *nic, bus_addr_t rb_ptr) +{ + struct mbuf *mbuf; + struct rbuf_info *rinfo; + + /* Get buffer start address and alignment offset */ + rinfo = GET_RBUF_INFO(PHYS_TO_DMAP(rb_ptr)); + + /* Now retrieve mbuf to give to stack */ + mbuf = rinfo->mbuf; + if (__predict_false(mbuf == NULL)) { + panic("%s: Received packet fragment with NULL mbuf", + device_get_nameunit(nic->dev)); + } + /* + * Clear the mbuf in the descriptor to indicate + * that this slot is processed and free to use. + */ + rinfo->mbuf = NULL; + + bus_dmamap_sync(rinfo->dmat, rinfo->dmap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(rinfo->dmat, rinfo->dmap); + + return (mbuf); +} + +/* Allocate RBDR ring and populate receive buffers */ +static int +nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr, int ring_len, + int buf_size, int qidx) +{ + bus_dmamap_t dmap; + bus_addr_t rbuf; + struct rbdr_entry_t *desc; + int idx; + int err; + + /* Allocate rbdr descriptors ring */ + err = nicvf_alloc_q_desc_mem(nic, &rbdr->dmem, ring_len, + sizeof(struct rbdr_entry_t), NICVF_RCV_BUF_ALIGN_BYTES); + if (err != 0) { + device_printf(nic->dev, + "Failed to create RBDR descriptors ring\n"); + return (err); + } + + rbdr->desc = rbdr->dmem.base; + /* + * Buffer size has to be in multiples of 128 bytes. + * Make room for metadata of size of one line (128 bytes). + */ + rbdr->dma_size = buf_size - NICVF_RCV_BUF_ALIGN_BYTES; + rbdr->enable = TRUE; + rbdr->thresh = RBDR_THRESH; + rbdr->nic = nic; + rbdr->idx = qidx; + + /* + * Create DMA tag for Rx buffers. + * Each map created using this tag is intended to store Rx payload for + * one fragment and one header structure containing rbuf_info (thus + * additional 128 byte line since RB must be a multiple of 128 byte + * cache line). + */ + if (buf_size > MCLBYTES) { + device_printf(nic->dev, + "Buffer size to large for mbuf cluster\n"); + return (EINVAL); + } + err = bus_dma_tag_create( + bus_get_dma_tag(nic->dev), /* parent tag */ + NICVF_RCV_BUF_ALIGN_BYTES, /* alignment */ + 0, /* boundary */ + DMAP_MAX_PHYSADDR, /* lowaddr */ + DMAP_MIN_PHYSADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + roundup2(buf_size, MCLBYTES), /* maxsize */ + 1, /* nsegments */ + roundup2(buf_size, MCLBYTES), /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockfuncarg */ + &rbdr->rbdr_buff_dmat); /* dmat */ + + if (err != 0) { + device_printf(nic->dev, + "Failed to create busdma tag for RBDR buffers\n"); + return (err); + } + + rbdr->rbdr_buff_dmaps = malloc(sizeof(*rbdr->rbdr_buff_dmaps) * + ring_len, M_NICVF, (M_WAITOK | M_ZERO)); + + for (idx = 0; idx < ring_len; idx++) { + err = bus_dmamap_create(rbdr->rbdr_buff_dmat, 0, &dmap); + if (err != 0) { + device_printf(nic->dev, + "Failed to create DMA map for RB\n"); + return (err); + } + rbdr->rbdr_buff_dmaps[idx] = dmap; + + err = nicvf_alloc_rcv_buffer(nic, rbdr, dmap, M_WAITOK, + DMA_BUFFER_LEN, &rbuf); + if (err != 0) + return (err); + + desc = GET_RBDR_DESC(rbdr, idx); + desc->buf_addr = (rbuf >> NICVF_RCV_BUF_ALIGN); + } + + /* Allocate taskqueue */ + TASK_INIT(&rbdr->rbdr_task, 0, nicvf_rbdr_task, rbdr); + TASK_INIT(&rbdr->rbdr_task_nowait, 0, nicvf_rbdr_task_nowait, rbdr); + rbdr->rbdr_taskq = taskqueue_create_fast("nicvf_rbdr_taskq", M_WAITOK, + taskqueue_thread_enqueue, &rbdr->rbdr_taskq); + taskqueue_start_threads(&rbdr->rbdr_taskq, 1, PI_NET, "%s: rbdr_taskq", + device_get_nameunit(nic->dev)); + + return (0); +} + +/* Free RBDR ring and its receive buffers */ +static void +nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) +{ + struct mbuf *mbuf; + struct queue_set *qs; + struct rbdr_entry_t *desc; + struct rbuf_info *rinfo; + bus_addr_t buf_addr; + int head, tail, idx; + int err; + + qs = nic->qs; + + if ((qs == NULL) || (rbdr == NULL)) + return; + + rbdr->enable = FALSE; + if (rbdr->rbdr_taskq != NULL) { + /* Remove tasks */ + while (taskqueue_cancel(rbdr->rbdr_taskq, + &rbdr->rbdr_task_nowait, NULL) != 0) { + /* Finish the nowait task first */ + taskqueue_drain(rbdr->rbdr_taskq, + &rbdr->rbdr_task_nowait); + } + taskqueue_free(rbdr->rbdr_taskq); + rbdr->rbdr_taskq = NULL; + + while (taskqueue_cancel(taskqueue_thread, + &rbdr->rbdr_task, NULL) != 0) { + /* Now finish the sleepable task */ + taskqueue_drain(taskqueue_thread, &rbdr->rbdr_task); + } + } + + /* + * Free all of the memory under the RB descriptors. + * There are assumptions here: + * 1. Corresponding RBDR is disabled + * - it is safe to operate using head and tail indexes + * 2. All bffers that were received are properly freed by + * the receive handler + * - there is no need to unload DMA map and free MBUF for other + * descriptors than unused ones + */ + if (rbdr->rbdr_buff_dmat != NULL) { + head = rbdr->head; + tail = rbdr->tail; + while (head != tail) { + desc = GET_RBDR_DESC(rbdr, head); + buf_addr = desc->buf_addr << NICVF_RCV_BUF_ALIGN; + rinfo = GET_RBUF_INFO(PHYS_TO_DMAP(buf_addr)); + bus_dmamap_unload(rbdr->rbdr_buff_dmat, rinfo->dmap); + mbuf = rinfo->mbuf; + /* This will destroy everything including rinfo! */ + m_freem(mbuf); + head++; + head &= (rbdr->dmem.q_len - 1); + } + /* Free tail descriptor */ + desc = GET_RBDR_DESC(rbdr, tail); + buf_addr = desc->buf_addr << NICVF_RCV_BUF_ALIGN; + rinfo = GET_RBUF_INFO(PHYS_TO_DMAP(buf_addr)); + bus_dmamap_unload(rbdr->rbdr_buff_dmat, rinfo->dmap); + mbuf = rinfo->mbuf; + /* This will destroy everything including rinfo! */ + m_freem(mbuf); + + /* Destroy DMA maps */ + for (idx = 0; idx < qs->rbdr_len; idx++) { + if (rbdr->rbdr_buff_dmaps[idx] == NULL) + continue; + err = bus_dmamap_destroy(rbdr->rbdr_buff_dmat, + rbdr->rbdr_buff_dmaps[idx]); + KASSERT(err == 0, + ("%s: Could not destroy DMA map for RB, desc: %d", + __func__, idx)); + rbdr->rbdr_buff_dmaps[idx] = NULL; + } + + /* Now destroy the tag */ + err = bus_dma_tag_destroy(rbdr->rbdr_buff_dmat); + KASSERT(err == 0, + ("%s: Trying to destroy BUSY DMA tag", __func__)); + + rbdr->head = 0; + rbdr->tail = 0; + } + + /* Free RBDR ring */ + nicvf_free_q_desc_mem(nic, &rbdr->dmem); +} + +/* + * Refill receive buffer descriptors with new buffers. + */ +static int +nicvf_refill_rbdr(struct rbdr *rbdr, int mflags) +{ + struct nicvf *nic; + struct queue_set *qs; + int rbdr_idx; + int tail, qcount; + int refill_rb_cnt; + struct rbdr_entry_t *desc; + bus_dmamap_t dmap; + bus_addr_t rbuf; + boolean_t rb_alloc_fail; + int new_rb; + + rb_alloc_fail = TRUE; + new_rb = 0; + nic = rbdr->nic; + qs = nic->qs; + rbdr_idx = rbdr->idx; + + /* Check if it's enabled */ + if (!rbdr->enable) + return (0); + + /* Get no of desc's to be refilled */ + qcount = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_STATUS0, rbdr_idx); + qcount &= 0x7FFFF; + /* Doorbell can be ringed with a max of ring size minus 1 */ + if (qcount >= (qs->rbdr_len - 1)) { + rb_alloc_fail = FALSE; + goto out; + } else + refill_rb_cnt = qs->rbdr_len - qcount - 1; + + /* Start filling descs from tail */ + tail = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_TAIL, rbdr_idx) >> 3; + while (refill_rb_cnt) { + tail++; + tail &= (rbdr->dmem.q_len - 1); + + dmap = rbdr->rbdr_buff_dmaps[tail]; + if (nicvf_alloc_rcv_buffer(nic, rbdr, dmap, mflags, + DMA_BUFFER_LEN, &rbuf)) { + /* Something went wrong. Resign */ + break; + } + desc = GET_RBDR_DESC(rbdr, tail); + desc->buf_addr = (rbuf >> NICVF_RCV_BUF_ALIGN); + refill_rb_cnt--; + new_rb++; + } + + /* make sure all memory stores are done before ringing doorbell */ + wmb(); + + /* Check if buffer allocation failed */ + if (refill_rb_cnt == 0) + rb_alloc_fail = FALSE; + + /* Notify HW */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_DOOR, + rbdr_idx, new_rb); +out: + if (!rb_alloc_fail) { + /* + * Re-enable RBDR interrupts only + * if buffer allocation is success. + */ + nicvf_enable_intr(nic, NICVF_INTR_RBDR, rbdr_idx); + + return (0); + } + + return (ENOMEM); +} + +/* Refill RBs even if sleep is needed to reclaim memory */ +static void +nicvf_rbdr_task(void *arg, int pending) +{ + struct rbdr *rbdr; + int err; + + rbdr = (struct rbdr *)arg; + + err = nicvf_refill_rbdr(rbdr, M_WAITOK); + if (__predict_false(err != 0)) { + panic("%s: Failed to refill RBs even when sleep enabled", + __func__); + } +} + +/* Refill RBs as soon as possible without waiting */ +static void +nicvf_rbdr_task_nowait(void *arg, int pending) +{ + struct rbdr *rbdr; + int err; + + rbdr = (struct rbdr *)arg; + + err = nicvf_refill_rbdr(rbdr, M_NOWAIT); + if (err != 0) { + /* + * Schedule another, sleepable kernel thread + * that will for sure refill the buffers. + */ + taskqueue_enqueue(taskqueue_thread, &rbdr->rbdr_task); + } +} + +static int +nicvf_rcv_pkt_handler(struct nicvf *nic, struct cmp_queue *cq, + struct cqe_rx_t *cqe_rx, int cqe_type) +{ + struct mbuf *mbuf; + int rq_idx; + int err = 0; + + rq_idx = cqe_rx->rq_idx; + + /* Check for errors */ + err = nicvf_check_cqe_rx_errs(nic, cq, cqe_rx); + if (err && !cqe_rx->rb_cnt) + return (0); + + mbuf = nicvf_get_rcv_mbuf(nic, cqe_rx); + if (mbuf == NULL) { + dprintf(nic->dev, "Packet not received\n"); + return (0); + } + + /* If error packet */ + if (err != 0) { + m_freem(mbuf); + return (0); + } + + /* + * Push this packet to the stack later to avoid + * unlocking completion task in the middle of work. + */ + err = buf_ring_enqueue(cq->rx_br, mbuf); + if (err != 0) { + /* + * Failed to enqueue this mbuf. + * We don't drop it, just schedule another task. + */ + return (err); + } + + return (0); +} + +static int +nicvf_snd_pkt_handler(struct nicvf *nic, struct cmp_queue *cq, + struct cqe_send_t *cqe_tx, int cqe_type) +{ + bus_dmamap_t dmap; + struct mbuf *mbuf; + struct snd_queue *sq; + struct sq_hdr_subdesc *hdr; + + mbuf = NULL; + sq = &nic->qs->sq[cqe_tx->sq_idx]; + /* Avoid blocking here since we hold a non-sleepable NICVF_CMP_LOCK */ + if (NICVF_TX_TRYLOCK(sq) == 0) + return (EAGAIN); + + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, cqe_tx->sqe_ptr); + if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER) { + NICVF_TX_UNLOCK(sq); + return (0); + } + + dprintf(nic->dev, + "%s Qset #%d SQ #%d SQ ptr #%d subdesc count %d\n", + __func__, cqe_tx->sq_qs, cqe_tx->sq_idx, + cqe_tx->sqe_ptr, hdr->subdesc_cnt); + + dmap = (bus_dmamap_t)sq->snd_buff[cqe_tx->sqe_ptr].dmap; + bus_dmamap_unload(sq->snd_buff_dmat, dmap); + + mbuf = (struct mbuf *)sq->snd_buff[cqe_tx->sqe_ptr].mbuf; + if (mbuf != NULL) { + m_freem(mbuf); + sq->snd_buff[cqe_tx->sqe_ptr].mbuf = NULL; + } + + nicvf_check_cqe_tx_errs(nic, cq, cqe_tx); + nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); + + NICVF_TX_UNLOCK(sq); + return (0); +} + +static int +nicvf_cq_intr_handler(struct nicvf *nic, uint8_t cq_idx) +{ + struct mbuf *mbuf; + struct ifnet *ifp; + int processed_cqe, work_done = 0, tx_done = 0; + int cqe_count, cqe_head; + struct queue_set *qs = nic->qs; + struct cmp_queue *cq = &qs->cq[cq_idx]; + struct cqe_rx_t *cq_desc; + int cmp_err; + + NICVF_CMP_LOCK(cq); + cmp_err = 0; + processed_cqe = 0; + /* Get no of valid CQ entries to process */ + cqe_count = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, cq_idx); + cqe_count &= CQ_CQE_COUNT; + if (cqe_count == 0) + goto out; + + /* Get head of the valid CQ entries */ + cqe_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, cq_idx) >> 9; + cqe_head &= 0xFFFF; + + dprintf(nic->dev, "%s CQ%d cqe_count %d cqe_head %d\n", + __func__, cq_idx, cqe_count, cqe_head); + while (processed_cqe < cqe_count) { + /* Get the CQ descriptor */ + cq_desc = (struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head); + cqe_head++; + cqe_head &= (cq->dmem.q_len - 1); + + dprintf(nic->dev, "CQ%d cq_desc->cqe_type %d\n", cq_idx, + cq_desc->cqe_type); + switch (cq_desc->cqe_type) { + case CQE_TYPE_RX: + cmp_err = nicvf_rcv_pkt_handler(nic, cq, cq_desc, + CQE_TYPE_RX); + if (__predict_false(cmp_err != 0)) { + /* + * Ups. Cannot finish now. + * Let's try again later. + */ + goto done; + } + work_done++; + break; + case CQE_TYPE_SEND: + cmp_err = nicvf_snd_pkt_handler(nic, cq, + (void *)cq_desc, CQE_TYPE_SEND); + if (__predict_false(cmp_err != 0)) { + /* + * Ups. Cannot finish now. + * Let's try again later. + */ + goto done; + } + + tx_done++; + break; + case CQE_TYPE_INVALID: + case CQE_TYPE_RX_SPLIT: + case CQE_TYPE_RX_TCP: + case CQE_TYPE_SEND_PTP: + /* Ignore for now */ + break; + } + processed_cqe++; + } +done: + dprintf(nic->dev, + "%s CQ%d processed_cqe %d work_done %d\n", + __func__, cq_idx, processed_cqe, work_done); + + /* Ring doorbell to inform H/W to reuse processed CQEs */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_DOOR, cq_idx, processed_cqe); + + if ((tx_done > 0) && + ((if_getdrvflags(nic->ifp) & IFF_DRV_RUNNING) != 0)) { + /* Reenable TXQ if its stopped earlier due to SQ full */ + if_setdrvflagbits(nic->ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); + } +out: + NICVF_CMP_UNLOCK(cq); + + ifp = nic->ifp; + /* Push received MBUFs to the stack */ + while (!buf_ring_empty(cq->rx_br)) { + mbuf = buf_ring_dequeue_mc(cq->rx_br); + if (__predict_true(mbuf != NULL)) + (*ifp->if_input)(ifp, mbuf); + } + + return (cmp_err); +} + +/* + * Qset error interrupt handler + * + * As of now only CQ errors are handled + */ +static void +nicvf_qs_err_task(void *arg, int pending) +{ + struct nicvf *nic; + struct queue_set *qs; + int qidx; + uint64_t status; + boolean_t enable = TRUE; + + nic = (struct nicvf *)arg; + qs = nic->qs; + + /* Deactivate network interface */ + if_setdrvflagbits(nic->ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING); + + /* Check if it is CQ err */ + for (qidx = 0; qidx < qs->cq_cnt; qidx++) { + status = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, + qidx); + if ((status & CQ_ERR_MASK) == 0) + continue; + /* Process already queued CQEs and reconfig CQ */ + nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx); + nicvf_sq_disable(nic, qidx); + (void)nicvf_cq_intr_handler(nic, qidx); + nicvf_cmp_queue_config(nic, qs, qidx, enable); + nicvf_sq_free_used_descs(nic, &qs->sq[qidx], qidx); + nicvf_sq_enable(nic, &qs->sq[qidx], qidx); + nicvf_enable_intr(nic, NICVF_INTR_CQ, qidx); + } + + if_setdrvflagbits(nic->ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); + /* Re-enable Qset error interrupt */ + nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0); +} + +static void +nicvf_cmp_task(void *arg, int pending) +{ + uint64_t cq_head; + struct cmp_queue *cq; + struct nicvf *nic; + int cmp_err; + + cq = (struct cmp_queue *)arg; + nic = cq->nic; + + /* Handle CQ descriptors */ + cmp_err = nicvf_cq_intr_handler(nic, cq->idx); + /* Re-enable interrupts */ + cq_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, cq->idx); + nicvf_clear_intr(nic, NICVF_INTR_CQ, cq->idx); + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_HEAD, cq->idx, cq_head); + + if (__predict_false(cmp_err != 0)) { + /* + * Schedule another thread here since we did not + * process the entire CQ due to Tx or Rx CQ parse error. + */ + taskqueue_enqueue(cq->cmp_taskq, &cq->cmp_task); + + } + + /* Reenable interrupt (previously disabled in nicvf_intr_handler() */ + nicvf_enable_intr(nic, NICVF_INTR_CQ, cq->idx); + +} + +/* Initialize completion queue */ +static int +nicvf_init_cmp_queue(struct nicvf *nic, struct cmp_queue *cq, int q_len, + int qidx) +{ + int err; + + /* Initizalize lock */ + snprintf(cq->mtx_name, sizeof(cq->mtx_name), "%s: CQ(%d) lock", + device_get_nameunit(nic->dev), qidx); + mtx_init(&cq->mtx, cq->mtx_name, NULL, MTX_DEF); + + err = nicvf_alloc_q_desc_mem(nic, &cq->dmem, q_len, CMP_QUEUE_DESC_SIZE, + NICVF_CQ_BASE_ALIGN_BYTES); + + if (err != 0) { + device_printf(nic->dev, + "Could not allocate DMA memory for CQ\n"); + return (err); + } + + cq->desc = cq->dmem.base; + cq->thresh = CMP_QUEUE_CQE_THRESH; + cq->nic = nic; + cq->idx = qidx; + nic->cq_coalesce_usecs = (CMP_QUEUE_TIMER_THRESH * 0.05) - 1; + + cq->rx_br = buf_ring_alloc(CMP_QUEUE_LEN * 8, M_DEVBUF, M_WAITOK, + &cq->mtx); + + /* Allocate taskqueue */ + TASK_INIT(&cq->cmp_task, 0, nicvf_cmp_task, cq); + cq->cmp_taskq = taskqueue_create_fast("nicvf_cmp_taskq", M_WAITOK, + taskqueue_thread_enqueue, &cq->cmp_taskq); + taskqueue_start_threads(&cq->cmp_taskq, 1, PI_NET, "%s: cmp_taskq(%d)", + device_get_nameunit(nic->dev), qidx); + + return (0); +} + +static void +nicvf_free_cmp_queue(struct nicvf *nic, struct cmp_queue *cq) +{ + + if (cq == NULL) + return; + /* + * The completion queue itself should be disabled by now + * (ref. nicvf_snd_queue_config()). + * Ensure that it is safe to disable it or panic. + */ + if (cq->enable) + panic("%s: Trying to free working CQ(%d)", __func__, cq->idx); + + if (cq->cmp_taskq != NULL) { + /* Remove task */ + while (taskqueue_cancel(cq->cmp_taskq, &cq->cmp_task, NULL) != 0) + taskqueue_drain(cq->cmp_taskq, &cq->cmp_task); + + taskqueue_free(cq->cmp_taskq); + cq->cmp_taskq = NULL; + } + /* + * Completion interrupt will possibly enable interrupts again + * so disable interrupting now after we finished processing + * completion task. It is safe to do so since the corresponding CQ + * was already disabled. + */ + nicvf_disable_intr(nic, NICVF_INTR_CQ, cq->idx); + nicvf_clear_intr(nic, NICVF_INTR_CQ, cq->idx); + + NICVF_CMP_LOCK(cq); + nicvf_free_q_desc_mem(nic, &cq->dmem); + drbr_free(cq->rx_br, M_DEVBUF); + NICVF_CMP_UNLOCK(cq); + mtx_destroy(&cq->mtx); + memset(cq->mtx_name, 0, sizeof(cq->mtx_name)); +} + +static void +nicvf_snd_task(void *arg, int pending) +{ + struct snd_queue *sq = (struct snd_queue *)arg; + struct mbuf *mbuf; + + NICVF_TX_LOCK(sq); + while (1) { + mbuf = drbr_dequeue(NULL, sq->br); + if (mbuf == NULL) + break; + + if (nicvf_tx_mbuf_locked(sq, mbuf) != 0) { + /* XXX ARM64TODO: Increase Tx drop counter */ + m_freem(mbuf); + break; + } + } + NICVF_TX_UNLOCK(sq); +} + +/* Initialize transmit queue */ +static int +nicvf_init_snd_queue(struct nicvf *nic, struct snd_queue *sq, int q_len, + int qidx) +{ + size_t i; + int err; + + /* Initizalize TX lock for this queue */ + snprintf(sq->mtx_name, sizeof(sq->mtx_name), "%s: SQ(%d) lock", + device_get_nameunit(nic->dev), qidx); + mtx_init(&sq->mtx, sq->mtx_name, NULL, MTX_DEF); + + NICVF_TX_LOCK(sq); + /* Allocate buffer ring */ + sq->br = buf_ring_alloc(q_len / MIN_SQ_DESC_PER_PKT_XMIT, M_DEVBUF, + M_NOWAIT, &sq->mtx); + if (sq->br == NULL) { + device_printf(nic->dev, + "ERROR: Could not set up buf ring for SQ(%d)\n", qidx); + err = ENOMEM; + goto error; + } + + /* Allocate DMA memory for Tx descriptors */ + err = nicvf_alloc_q_desc_mem(nic, &sq->dmem, q_len, SND_QUEUE_DESC_SIZE, + NICVF_SQ_BASE_ALIGN_BYTES); + if (err != 0) { + device_printf(nic->dev, + "Could not allocate DMA memory for SQ\n"); + goto error; + } + + sq->desc = sq->dmem.base; + sq->head = sq->tail = 0; + atomic_store_rel_int(&sq->free_cnt, q_len - 1); + sq->thresh = SND_QUEUE_THRESH; + sq->idx = qidx; + sq->nic = nic; + + /* + * Allocate DMA maps for Tx buffers + */ + + /* Create DMA tag first */ + err = bus_dma_tag_create( + bus_get_dma_tag(nic->dev), /* parent tag */ + 1, /* alignment */ + 0, /* boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + NICVF_TXBUF_MAXSIZE, /* maxsize */ + NICVF_TXBUF_NSEGS, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockfuncarg */ + &sq->snd_buff_dmat); /* dmat */ + + if (err != 0) { + device_printf(nic->dev, + "Failed to create busdma tag for Tx buffers\n"); + goto error; + } + + /* Allocate send buffers array */ + sq->snd_buff = malloc(sizeof(*sq->snd_buff) * q_len, M_NICVF, + (M_NOWAIT | M_ZERO)); + if (sq->snd_buff == NULL) { + device_printf(nic->dev, + "Could not allocate memory for Tx buffers array\n"); + err = ENOMEM; + goto error; + } + + /* Now populate maps */ + for (i = 0; i < q_len; i++) { + err = bus_dmamap_create(sq->snd_buff_dmat, 0, + &sq->snd_buff[i].dmap); + if (err != 0) { + device_printf(nic->dev, + "Failed to create DMA maps for Tx buffers\n"); + goto error; + } + } + NICVF_TX_UNLOCK(sq); + + /* Allocate taskqueue */ + TASK_INIT(&sq->snd_task, 0, nicvf_snd_task, sq); + sq->snd_taskq = taskqueue_create_fast("nicvf_snd_taskq", M_WAITOK, + taskqueue_thread_enqueue, &sq->snd_taskq); + taskqueue_start_threads(&sq->snd_taskq, 1, PI_NET, "%s: snd_taskq(%d)", + device_get_nameunit(nic->dev), qidx); + + return (0); +error: + NICVF_TX_UNLOCK(sq); + return (err); +} + +static void +nicvf_free_snd_queue(struct nicvf *nic, struct snd_queue *sq) +{ + struct queue_set *qs = nic->qs; + size_t i; + int err; + + if (sq == NULL) + return; + + if (sq->snd_taskq != NULL) { + /* Remove task */ + while (taskqueue_cancel(sq->snd_taskq, &sq->snd_task, NULL) != 0) + taskqueue_drain(sq->snd_taskq, &sq->snd_task); + + taskqueue_free(sq->snd_taskq); + sq->snd_taskq = NULL; + } + + NICVF_TX_LOCK(sq); + if (sq->snd_buff_dmat != NULL) { + if (sq->snd_buff != NULL) { + for (i = 0; i < qs->sq_len; i++) { + m_freem(sq->snd_buff[i].mbuf); + sq->snd_buff[i].mbuf = NULL; + + bus_dmamap_unload(sq->snd_buff_dmat, + sq->snd_buff[i].dmap); + err = bus_dmamap_destroy(sq->snd_buff_dmat, + sq->snd_buff[i].dmap); + /* + * If bus_dmamap_destroy fails it can cause + * random panic later if the tag is also + * destroyed in the process. + */ + KASSERT(err == 0, + ("%s: Could not destroy DMA map for SQ", + __func__)); + } + } + + free(sq->snd_buff, M_NICVF); + + err = bus_dma_tag_destroy(sq->snd_buff_dmat); + KASSERT(err == 0, + ("%s: Trying to destroy BUSY DMA tag", __func__)); + } + + /* Free private driver ring for this send queue */ + if (sq->br != NULL) + drbr_free(sq->br, M_DEVBUF); + + if (sq->dmem.base != NULL) + nicvf_free_q_desc_mem(nic, &sq->dmem); + + NICVF_TX_UNLOCK(sq); + /* Destroy Tx lock */ + mtx_destroy(&sq->mtx); + memset(sq->mtx_name, 0, sizeof(sq->mtx_name)); +} + +static void +nicvf_reclaim_snd_queue(struct nicvf *nic, struct queue_set *qs, int qidx) +{ + + /* Disable send queue */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, 0); + /* Check if SQ is stopped */ + if (nicvf_poll_reg(nic, qidx, NIC_QSET_SQ_0_7_STATUS, 21, 1, 0x01)) + return; + /* Reset send queue */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, NICVF_SQ_RESET); +} + +static void +nicvf_reclaim_rcv_queue(struct nicvf *nic, struct queue_set *qs, int qidx) +{ + union nic_mbx mbx = {}; + + /* Make sure all packets in the pipeline are written back into mem */ + mbx.msg.msg = NIC_MBOX_MSG_RQ_SW_SYNC; + nicvf_send_msg_to_pf(nic, &mbx); +} + +static void +nicvf_reclaim_cmp_queue(struct nicvf *nic, struct queue_set *qs, int qidx) +{ + + /* Disable timer threshold (doesn't get reset upon CQ reset */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG2, qidx, 0); + /* Disable completion queue */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, 0); + /* Reset completion queue */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, NICVF_CQ_RESET); +} + +static void +nicvf_reclaim_rbdr(struct nicvf *nic, struct rbdr *rbdr, int qidx) +{ + uint64_t tmp, fifo_state; + int timeout = 10; + + /* Save head and tail pointers for feeing up buffers */ + rbdr->head = + nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_HEAD, qidx) >> 3; + rbdr->tail = + nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_TAIL, qidx) >> 3; + + /* + * If RBDR FIFO is in 'FAIL' state then do a reset first + * before relaiming. + */ + fifo_state = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_STATUS0, qidx); + if (((fifo_state >> 62) & 0x03) == 0x3) { + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, + qidx, NICVF_RBDR_RESET); + } + + /* Disable RBDR */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, qidx, 0); + if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x00)) + return; + while (1) { + tmp = nicvf_queue_reg_read(nic, + NIC_QSET_RBDR_0_1_PREFETCH_STATUS, qidx); + if ((tmp & 0xFFFFFFFF) == ((tmp >> 32) & 0xFFFFFFFF)) + break; + + DELAY(1000); + timeout--; + if (!timeout) { + device_printf(nic->dev, + "Failed polling on prefetch status\n"); + return; + } + } + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, qidx, + NICVF_RBDR_RESET); + + if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x02)) + return; + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, qidx, 0x00); + if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x00)) + return; +} + +/* Configures receive queue */ +static void +nicvf_rcv_queue_config(struct nicvf *nic, struct queue_set *qs, + int qidx, bool enable) +{ + union nic_mbx mbx = {}; + struct rcv_queue *rq; + struct rq_cfg rq_cfg; + + rq = &qs->rq[qidx]; + rq->enable = enable; + + /* Disable receive queue */ + nicvf_queue_reg_write(nic, NIC_QSET_RQ_0_7_CFG, qidx, 0); + + if (!rq->enable) { + nicvf_reclaim_rcv_queue(nic, qs, qidx); + return; + } + + rq->cq_qs = qs->vnic_id; + rq->cq_idx = qidx; + rq->start_rbdr_qs = qs->vnic_id; + rq->start_qs_rbdr_idx = qs->rbdr_cnt - 1; + rq->cont_rbdr_qs = qs->vnic_id; + rq->cont_qs_rbdr_idx = qs->rbdr_cnt - 1; + /* all writes of RBDR data to be loaded into L2 Cache as well*/ + rq->caching = 1; + + /* Send a mailbox msg to PF to config RQ */ + mbx.rq.msg = NIC_MBOX_MSG_RQ_CFG; + mbx.rq.qs_num = qs->vnic_id; + mbx.rq.rq_num = qidx; + mbx.rq.cfg = (rq->caching << 26) | (rq->cq_qs << 19) | + (rq->cq_idx << 16) | (rq->cont_rbdr_qs << 9) | + (rq->cont_qs_rbdr_idx << 8) | (rq->start_rbdr_qs << 1) | + (rq->start_qs_rbdr_idx); + nicvf_send_msg_to_pf(nic, &mbx); + + mbx.rq.msg = NIC_MBOX_MSG_RQ_BP_CFG; + mbx.rq.cfg = (1UL << 63) | (1UL << 62) | (qs->vnic_id << 0); + nicvf_send_msg_to_pf(nic, &mbx); + + /* + * RQ drop config + * Enable CQ drop to reserve sufficient CQEs for all tx packets + */ + mbx.rq.msg = NIC_MBOX_MSG_RQ_DROP_CFG; + mbx.rq.cfg = (1UL << 62) | (RQ_CQ_DROP << 8); + nicvf_send_msg_to_pf(nic, &mbx); + + nicvf_queue_reg_write(nic, NIC_QSET_RQ_GEN_CFG, 0, 0x00); + + /* Enable Receive queue */ + rq_cfg.ena = 1; + rq_cfg.tcp_ena = 0; + nicvf_queue_reg_write(nic, NIC_QSET_RQ_0_7_CFG, qidx, + *(uint64_t *)&rq_cfg); +} + +/* Configures completion queue */ +static void +nicvf_cmp_queue_config(struct nicvf *nic, struct queue_set *qs, + int qidx, boolean_t enable) +{ + struct cmp_queue *cq; + struct cq_cfg cq_cfg; + + cq = &qs->cq[qidx]; + cq->enable = enable; + + if (!cq->enable) { + nicvf_reclaim_cmp_queue(nic, qs, qidx); + return; + } + + /* Reset completion queue */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, NICVF_CQ_RESET); + + /* Set completion queue base address */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_BASE, qidx, + (uint64_t)(cq->dmem.phys_base)); + + /* Enable Completion queue */ + cq_cfg.ena = 1; + cq_cfg.reset = 0; + cq_cfg.caching = 0; + cq_cfg.qsize = CMP_QSIZE; + cq_cfg.avg_con = 0; + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, *(uint64_t *)&cq_cfg); + + /* Set threshold value for interrupt generation */ + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_THRESH, qidx, cq->thresh); + nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG2, qidx, + nic->cq_coalesce_usecs); +} + +/* Configures transmit queue */ +static void +nicvf_snd_queue_config(struct nicvf *nic, struct queue_set *qs, int qidx, + boolean_t enable) +{ + union nic_mbx mbx = {}; + struct snd_queue *sq; + struct sq_cfg sq_cfg; + + sq = &qs->sq[qidx]; + sq->enable = enable; + + if (!sq->enable) { + nicvf_reclaim_snd_queue(nic, qs, qidx); + return; + } + + /* Reset send queue */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, NICVF_SQ_RESET); + + sq->cq_qs = qs->vnic_id; + sq->cq_idx = qidx; + + /* Send a mailbox msg to PF to config SQ */ + mbx.sq.msg = NIC_MBOX_MSG_SQ_CFG; + mbx.sq.qs_num = qs->vnic_id; + mbx.sq.sq_num = qidx; + mbx.sq.sqs_mode = nic->sqs_mode; + mbx.sq.cfg = (sq->cq_qs << 3) | sq->cq_idx; + nicvf_send_msg_to_pf(nic, &mbx); + + /* Set queue base address */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_BASE, qidx, + (uint64_t)(sq->dmem.phys_base)); + + /* Enable send queue & set queue size */ + sq_cfg.ena = 1; + sq_cfg.reset = 0; + sq_cfg.ldwb = 0; + sq_cfg.qsize = SND_QSIZE; + sq_cfg.tstmp_bgx_intf = 0; + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, *(uint64_t *)&sq_cfg); + + /* Set threshold value for interrupt generation */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_THRESH, qidx, sq->thresh); +} + +/* Configures receive buffer descriptor ring */ +static void +nicvf_rbdr_config(struct nicvf *nic, struct queue_set *qs, int qidx, + boolean_t enable) +{ + struct rbdr *rbdr; + struct rbdr_cfg rbdr_cfg; + + rbdr = &qs->rbdr[qidx]; + nicvf_reclaim_rbdr(nic, rbdr, qidx); + if (!enable) + return; + + /* Set descriptor base address */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_BASE, qidx, + (uint64_t)(rbdr->dmem.phys_base)); + + /* Enable RBDR & set queue size */ + /* Buffer size should be in multiples of 128 bytes */ + rbdr_cfg.ena = 1; + rbdr_cfg.reset = 0; + rbdr_cfg.ldwb = 0; + rbdr_cfg.qsize = RBDR_SIZE; + rbdr_cfg.avg_con = 0; + rbdr_cfg.lines = rbdr->dma_size / 128; + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, qidx, + *(uint64_t *)&rbdr_cfg); + + /* Notify HW */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_DOOR, qidx, + qs->rbdr_len - 1); + + /* Set threshold value for interrupt generation */ + nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_THRESH, qidx, + rbdr->thresh - 1); +} + +/* Requests PF to assign and enable Qset */ +void +nicvf_qset_config(struct nicvf *nic, boolean_t enable) +{ + union nic_mbx mbx = {}; + struct queue_set *qs; + struct qs_cfg *qs_cfg; + + qs = nic->qs; + if (qs == NULL) { + device_printf(nic->dev, + "Qset is still not allocated, don't init queues\n"); + return; + } + + qs->enable = enable; + qs->vnic_id = nic->vf_id; + + /* Send a mailbox msg to PF to config Qset */ + mbx.qs.msg = NIC_MBOX_MSG_QS_CFG; + mbx.qs.num = qs->vnic_id; + + mbx.qs.cfg = 0; + qs_cfg = (struct qs_cfg *)&mbx.qs.cfg; + if (qs->enable) { + qs_cfg->ena = 1; + qs_cfg->vnic = qs->vnic_id; + } + nicvf_send_msg_to_pf(nic, &mbx); +} + +static void +nicvf_free_resources(struct nicvf *nic) +{ + int qidx; + struct queue_set *qs; + + qs = nic->qs; + /* + * Remove QS error task first since it has to be dead + * to safely free completion queue tasks. + */ + if (qs->qs_err_taskq != NULL) { + /* Shut down QS error tasks */ + while (taskqueue_cancel(qs->qs_err_taskq, + &qs->qs_err_task, NULL) != 0) { + taskqueue_drain(qs->qs_err_taskq, &qs->qs_err_task); + + } + taskqueue_free(qs->qs_err_taskq); + qs->qs_err_taskq = NULL; + } + /* Free receive buffer descriptor ring */ + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) + nicvf_free_rbdr(nic, &qs->rbdr[qidx]); + + /* Free completion queue */ + for (qidx = 0; qidx < qs->cq_cnt; qidx++) + nicvf_free_cmp_queue(nic, &qs->cq[qidx]); + + /* Free send queue */ + for (qidx = 0; qidx < qs->sq_cnt; qidx++) + nicvf_free_snd_queue(nic, &qs->sq[qidx]); +} + +static int +nicvf_alloc_resources(struct nicvf *nic) +{ + struct queue_set *qs = nic->qs; + int qidx; + + /* Alloc receive buffer descriptor ring */ + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) { + if (nicvf_init_rbdr(nic, &qs->rbdr[qidx], qs->rbdr_len, + DMA_BUFFER_LEN, qidx)) + goto alloc_fail; + } + + /* Alloc send queue */ + for (qidx = 0; qidx < qs->sq_cnt; qidx++) { + if (nicvf_init_snd_queue(nic, &qs->sq[qidx], qs->sq_len, qidx)) + goto alloc_fail; + } + + /* Alloc completion queue */ + for (qidx = 0; qidx < qs->cq_cnt; qidx++) { + if (nicvf_init_cmp_queue(nic, &qs->cq[qidx], qs->cq_len, qidx)) + goto alloc_fail; + } + + /* Allocate QS error taskqueue */ + TASK_INIT(&qs->qs_err_task, 0, nicvf_qs_err_task, nic); + qs->qs_err_taskq = taskqueue_create_fast("nicvf_qs_err_taskq", M_WAITOK, + taskqueue_thread_enqueue, &qs->qs_err_taskq); + taskqueue_start_threads(&qs->qs_err_taskq, 1, PI_NET, "%s: qs_taskq", + device_get_nameunit(nic->dev)); + + return (0); +alloc_fail: + nicvf_free_resources(nic); + return (ENOMEM); +} + +int +nicvf_set_qset_resources(struct nicvf *nic) +{ + struct queue_set *qs; + + qs = malloc(sizeof(*qs), M_NICVF, (M_ZERO | M_WAITOK)); + nic->qs = qs; + + /* Set count of each queue */ + qs->rbdr_cnt = RBDR_CNT; + /* With no RSS we stay with single RQ */ + qs->rq_cnt = 1; + + qs->sq_cnt = SND_QUEUE_CNT; + qs->cq_cnt = CMP_QUEUE_CNT; + + /* Set queue lengths */ + qs->rbdr_len = RCV_BUF_COUNT; + qs->sq_len = SND_QUEUE_LEN; + qs->cq_len = CMP_QUEUE_LEN; + + nic->rx_queues = qs->rq_cnt; + nic->tx_queues = qs->sq_cnt; + + return (0); +} + +int +nicvf_config_data_transfer(struct nicvf *nic, boolean_t enable) +{ + boolean_t disable = FALSE; + struct queue_set *qs; + int qidx; + + qs = nic->qs; + if (qs == NULL) + return (0); + + if (enable) { + if (nicvf_alloc_resources(nic) != 0) + return (ENOMEM); + + for (qidx = 0; qidx < qs->sq_cnt; qidx++) + nicvf_snd_queue_config(nic, qs, qidx, enable); + for (qidx = 0; qidx < qs->cq_cnt; qidx++) + nicvf_cmp_queue_config(nic, qs, qidx, enable); + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) + nicvf_rbdr_config(nic, qs, qidx, enable); + for (qidx = 0; qidx < qs->rq_cnt; qidx++) + nicvf_rcv_queue_config(nic, qs, qidx, enable); + } else { + for (qidx = 0; qidx < qs->rq_cnt; qidx++) + nicvf_rcv_queue_config(nic, qs, qidx, disable); + for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) + nicvf_rbdr_config(nic, qs, qidx, disable); + for (qidx = 0; qidx < qs->sq_cnt; qidx++) + nicvf_snd_queue_config(nic, qs, qidx, disable); + for (qidx = 0; qidx < qs->cq_cnt; qidx++) + nicvf_cmp_queue_config(nic, qs, qidx, disable); + + nicvf_free_resources(nic); + } + + return (0); +} + +/* + * Get a free desc from SQ + * returns descriptor ponter & descriptor number + */ +static __inline int +nicvf_get_sq_desc(struct snd_queue *sq, int desc_cnt) +{ + int qentry; + + qentry = sq->tail; + atomic_subtract_int(&sq->free_cnt, desc_cnt); + sq->tail += desc_cnt; + sq->tail &= (sq->dmem.q_len - 1); + + return (qentry); +} + +/* Free descriptor back to SQ for future use */ +static void +nicvf_put_sq_desc(struct snd_queue *sq, int desc_cnt) +{ + + atomic_add_int(&sq->free_cnt, desc_cnt); + sq->head += desc_cnt; + sq->head &= (sq->dmem.q_len - 1); +} + +static __inline int +nicvf_get_nxt_sqentry(struct snd_queue *sq, int qentry) +{ + qentry++; + qentry &= (sq->dmem.q_len - 1); + return (qentry); +} + +static void +nicvf_sq_enable(struct nicvf *nic, struct snd_queue *sq, int qidx) +{ + uint64_t sq_cfg; + + sq_cfg = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CFG, qidx); + sq_cfg |= NICVF_SQ_EN; + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, sq_cfg); + /* Ring doorbell so that H/W restarts processing SQEs */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR, qidx, 0); +} + +static void +nicvf_sq_disable(struct nicvf *nic, int qidx) +{ + uint64_t sq_cfg; + + sq_cfg = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CFG, qidx); + sq_cfg &= ~NICVF_SQ_EN; + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, sq_cfg); +} + +static void +nicvf_sq_free_used_descs(struct nicvf *nic, struct snd_queue *sq, int qidx) +{ + uint64_t head, tail; + struct snd_buff *snd_buff; + struct sq_hdr_subdesc *hdr; + + NICVF_TX_LOCK(sq); + head = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_HEAD, qidx) >> 4; + tail = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_TAIL, qidx) >> 4; + while (sq->head != head) { + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, sq->head); + if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER) { + nicvf_put_sq_desc(sq, 1); + continue; + } + snd_buff = &sq->snd_buff[sq->head]; + if (snd_buff->mbuf != NULL) { + bus_dmamap_unload(sq->snd_buff_dmat, snd_buff->dmap); + m_freem(snd_buff->mbuf); + sq->snd_buff[sq->head].mbuf = NULL; + } + nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); + } + NICVF_TX_UNLOCK(sq); +} + +/* + * Add SQ HEADER subdescriptor. + * First subdescriptor for every send descriptor. + */ +static __inline void +nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry, + int subdesc_cnt, struct mbuf *mbuf, int len) +{ + struct sq_hdr_subdesc *hdr; + + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, qentry); + sq->snd_buff[qentry].mbuf = mbuf; + + memset(hdr, 0, SND_QUEUE_DESC_SIZE); + hdr->subdesc_type = SQ_DESC_TYPE_HEADER; + /* Enable notification via CQE after processing SQE */ + hdr->post_cqe = 1; + /* No of subdescriptors following this */ + hdr->subdesc_cnt = subdesc_cnt; + hdr->tot_len = len; + + /* ARM64TODO: Implement HW checksums calculation */ +} + +/* + * SQ GATHER subdescriptor + * Must follow HDR descriptor + */ +static inline void nicvf_sq_add_gather_subdesc(struct snd_queue *sq, int qentry, + int size, uint64_t data) +{ + struct sq_gather_subdesc *gather; + + qentry &= (sq->dmem.q_len - 1); + gather = (struct sq_gather_subdesc *)GET_SQ_DESC(sq, qentry); + + memset(gather, 0, SND_QUEUE_DESC_SIZE); + gather->subdesc_type = SQ_DESC_TYPE_GATHER; + gather->ld_type = NIC_SEND_LD_TYPE_E_LDD; + gather->size = size; + gather->addr = data; +} + +/* Put an mbuf to a SQ for packet transfer. */ +static int +nicvf_tx_mbuf_locked(struct snd_queue *sq, struct mbuf *mbuf) +{ + bus_dma_segment_t segs[256]; + struct snd_buff *snd_buff; + size_t seg; + int nsegs, qentry; + int subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT - 1; + int err; + + NICVF_TX_LOCK_ASSERT(sq); + + if (sq->free_cnt == 0) + return (ENOBUFS); + + snd_buff = &sq->snd_buff[sq->tail]; + + err = bus_dmamap_load_mbuf_sg(sq->snd_buff_dmat, snd_buff->dmap, + mbuf, segs, &nsegs, BUS_DMA_NOWAIT); + if (err != 0) { + /* ARM64TODO: Add mbuf defragmenting if we lack maps */ + return (err); + } + + /* Set how many subdescriptors is required */ + subdesc_cnt += nsegs; + + if (subdesc_cnt > sq->free_cnt) { + /* ARM64TODO: Add mbuf defragmentation if we lack descriptors */ + bus_dmamap_unload(sq->snd_buff_dmat, snd_buff->dmap); + return (ENOBUFS); + } + + qentry = nicvf_get_sq_desc(sq, subdesc_cnt); + + /* Add SQ header subdesc */ + nicvf_sq_add_hdr_subdesc(sq, qentry, subdesc_cnt - 1, mbuf, + mbuf->m_pkthdr.len); + + /* Add SQ gather subdescs */ + for (seg = 0; seg < nsegs; seg++) { + qentry = nicvf_get_nxt_sqentry(sq, qentry); + nicvf_sq_add_gather_subdesc(sq, qentry, segs[seg].ds_len, + segs[seg].ds_addr); + } + + /* make sure all memory stores are done before ringing doorbell */ + bus_dmamap_sync(sq->dmem.dmat, sq->dmem.dmap, BUS_DMASYNC_PREWRITE); + + dprintf(sq->nic->dev, "%s: sq->idx: %d, subdesc_cnt: %d\n", + __func__, sq->idx, subdesc_cnt); + /* Inform HW to xmit new packet */ + nicvf_queue_reg_write(sq->nic, NIC_QSET_SQ_0_7_DOOR, + sq->idx, subdesc_cnt); + return (0); +} + +static __inline u_int +frag_num(u_int i) +{ +#if BYTE_ORDER == BIG_ENDIAN + return ((i & ~3) + 3 - (i & 3)); +#else + return (i); +#endif +} + +/* Returns MBUF for a received packet */ +struct mbuf * +nicvf_get_rcv_mbuf(struct nicvf *nic, struct cqe_rx_t *cqe_rx) +{ + int frag; + int payload_len = 0; + struct mbuf *mbuf; + struct mbuf *mbuf_frag; + uint16_t *rb_lens = NULL; + uint64_t *rb_ptrs = NULL; + + mbuf = NULL; + rb_lens = (uint16_t *)((uint8_t *)cqe_rx + (3 * sizeof(uint64_t))); + rb_ptrs = (uint64_t *)((uint8_t *)cqe_rx + (6 * sizeof(uint64_t))); + + dprintf(nic->dev, "%s rb_cnt %d rb0_ptr %lx rb0_sz %d\n", + __func__, cqe_rx->rb_cnt, cqe_rx->rb0_ptr, cqe_rx->rb0_sz); + + for (frag = 0; frag < cqe_rx->rb_cnt; frag++) { + payload_len = rb_lens[frag_num(frag)]; + if (frag == 0) { + /* First fragment */ + mbuf = nicvf_rb_ptr_to_mbuf(nic, + (*rb_ptrs - cqe_rx->align_pad)); + mbuf->m_len = payload_len; + mbuf->m_data += cqe_rx->align_pad; + if_setrcvif(mbuf, nic->ifp); + } else { + /* Add fragments */ + mbuf_frag = nicvf_rb_ptr_to_mbuf(nic, *rb_ptrs); + m_append(mbuf, payload_len, mbuf_frag->m_data); + m_freem(mbuf_frag); + } + /* Next buffer pointer */ + rb_ptrs++; + } + + if (__predict_true(mbuf != NULL)) { + m_fixhdr(mbuf); + mbuf->m_pkthdr.flowid = cqe_rx->rq_idx; + M_HASHTYPE_SET(mbuf, M_HASHTYPE_OPAQUE); + } + + return (mbuf); +} + +/* Enable interrupt */ +void +nicvf_enable_intr(struct nicvf *nic, int int_type, int q_idx) +{ + uint64_t reg_val; + + reg_val = nicvf_reg_read(nic, NIC_VF_ENA_W1S); + + switch (int_type) { + case NICVF_INTR_CQ: + reg_val |= ((1UL << q_idx) << NICVF_INTR_CQ_SHIFT); + break; + case NICVF_INTR_SQ: + reg_val |= ((1UL << q_idx) << NICVF_INTR_SQ_SHIFT); + break; + case NICVF_INTR_RBDR: + reg_val |= ((1UL << q_idx) << NICVF_INTR_RBDR_SHIFT); + break; + case NICVF_INTR_PKT_DROP: + reg_val |= (1UL << NICVF_INTR_PKT_DROP_SHIFT); + break; + case NICVF_INTR_TCP_TIMER: + reg_val |= (1UL << NICVF_INTR_TCP_TIMER_SHIFT); + break; + case NICVF_INTR_MBOX: + reg_val |= (1UL << NICVF_INTR_MBOX_SHIFT); + break; + case NICVF_INTR_QS_ERR: + reg_val |= (1UL << NICVF_INTR_QS_ERR_SHIFT); + break; + default: + device_printf(nic->dev, + "Failed to enable interrupt: unknown type\n"); + break; + } + + nicvf_reg_write(nic, NIC_VF_ENA_W1S, reg_val); +} + +/* Disable interrupt */ +void +nicvf_disable_intr(struct nicvf *nic, int int_type, int q_idx) +{ + uint64_t reg_val = 0; + + switch (int_type) { + case NICVF_INTR_CQ: + reg_val |= ((1UL << q_idx) << NICVF_INTR_CQ_SHIFT); + break; + case NICVF_INTR_SQ: + reg_val |= ((1UL << q_idx) << NICVF_INTR_SQ_SHIFT); + break; + case NICVF_INTR_RBDR: + reg_val |= ((1UL << q_idx) << NICVF_INTR_RBDR_SHIFT); + break; + case NICVF_INTR_PKT_DROP: + reg_val |= (1UL << NICVF_INTR_PKT_DROP_SHIFT); + break; + case NICVF_INTR_TCP_TIMER: + reg_val |= (1UL << NICVF_INTR_TCP_TIMER_SHIFT); + break; + case NICVF_INTR_MBOX: + reg_val |= (1UL << NICVF_INTR_MBOX_SHIFT); + break; + case NICVF_INTR_QS_ERR: + reg_val |= (1UL << NICVF_INTR_QS_ERR_SHIFT); + break; + default: + device_printf(nic->dev, + "Failed to disable interrupt: unknown type\n"); + break; + } + + nicvf_reg_write(nic, NIC_VF_ENA_W1C, reg_val); +} + +/* Clear interrupt */ +void +nicvf_clear_intr(struct nicvf *nic, int int_type, int q_idx) +{ + uint64_t reg_val = 0; + + switch (int_type) { + case NICVF_INTR_CQ: + reg_val = ((1UL << q_idx) << NICVF_INTR_CQ_SHIFT); + break; + case NICVF_INTR_SQ: + reg_val = ((1UL << q_idx) << NICVF_INTR_SQ_SHIFT); + break; + case NICVF_INTR_RBDR: + reg_val = ((1UL << q_idx) << NICVF_INTR_RBDR_SHIFT); + break; + case NICVF_INTR_PKT_DROP: + reg_val = (1UL << NICVF_INTR_PKT_DROP_SHIFT); + break; + case NICVF_INTR_TCP_TIMER: + reg_val = (1UL << NICVF_INTR_TCP_TIMER_SHIFT); + break; + case NICVF_INTR_MBOX: + reg_val = (1UL << NICVF_INTR_MBOX_SHIFT); + break; + case NICVF_INTR_QS_ERR: + reg_val |= (1UL << NICVF_INTR_QS_ERR_SHIFT); + break; + default: + device_printf(nic->dev, + "Failed to clear interrupt: unknown type\n"); + break; + } + + nicvf_reg_write(nic, NIC_VF_INT, reg_val); +} + +/* Check if interrupt is enabled */ +int +nicvf_is_intr_enabled(struct nicvf *nic, int int_type, int q_idx) +{ + uint64_t reg_val; + uint64_t mask = 0xff; + + reg_val = nicvf_reg_read(nic, NIC_VF_ENA_W1S); + + switch (int_type) { + case NICVF_INTR_CQ: + mask = ((1UL << q_idx) << NICVF_INTR_CQ_SHIFT); + break; + case NICVF_INTR_SQ: + mask = ((1UL << q_idx) << NICVF_INTR_SQ_SHIFT); + break; + case NICVF_INTR_RBDR: + mask = ((1UL << q_idx) << NICVF_INTR_RBDR_SHIFT); + break; + case NICVF_INTR_PKT_DROP: + mask = NICVF_INTR_PKT_DROP_MASK; + break; + case NICVF_INTR_TCP_TIMER: + mask = NICVF_INTR_TCP_TIMER_MASK; + break; + case NICVF_INTR_MBOX: + mask = NICVF_INTR_MBOX_MASK; + break; + case NICVF_INTR_QS_ERR: + mask = NICVF_INTR_QS_ERR_MASK; + break; + default: + device_printf(nic->dev, + "Failed to check interrupt enable: unknown type\n"); + break; + } + + return (reg_val & mask); +} + +void +nicvf_update_rq_stats(struct nicvf *nic, int rq_idx) +{ + struct rcv_queue *rq; + +#define GET_RQ_STATS(reg) \ + nicvf_reg_read(nic, NIC_QSET_RQ_0_7_STAT_0_1 |\ + (rq_idx << NIC_Q_NUM_SHIFT) | (reg << 3)) + + rq = &nic->qs->rq[rq_idx]; + rq->stats.bytes = GET_RQ_STATS(RQ_SQ_STATS_OCTS); + rq->stats.pkts = GET_RQ_STATS(RQ_SQ_STATS_PKTS); +} + +void +nicvf_update_sq_stats(struct nicvf *nic, int sq_idx) +{ + struct snd_queue *sq; + +#define GET_SQ_STATS(reg) \ + nicvf_reg_read(nic, NIC_QSET_SQ_0_7_STAT_0_1 |\ + (sq_idx << NIC_Q_NUM_SHIFT) | (reg << 3)) + + sq = &nic->qs->sq[sq_idx]; + sq->stats.bytes = GET_SQ_STATS(RQ_SQ_STATS_OCTS); + sq->stats.pkts = GET_SQ_STATS(RQ_SQ_STATS_PKTS); +} + +/* Check for errors in the receive cmp.queue entry */ +int +nicvf_check_cqe_rx_errs(struct nicvf *nic, struct cmp_queue *cq, + struct cqe_rx_t *cqe_rx) +{ + struct nicvf_hw_stats *stats = &nic->hw_stats; + struct nicvf_drv_stats *drv_stats = &nic->drv_stats; + + if (!cqe_rx->err_level && !cqe_rx->err_opcode) { + drv_stats->rx_frames_ok++; + return (0); + } + + switch (cqe_rx->err_opcode) { + case CQ_RX_ERROP_RE_PARTIAL: + stats->rx_bgx_truncated_pkts++; + break; + case CQ_RX_ERROP_RE_JABBER: + stats->rx_jabber_errs++; + break; + case CQ_RX_ERROP_RE_FCS: + stats->rx_fcs_errs++; + break; + case CQ_RX_ERROP_RE_RX_CTL: + stats->rx_bgx_errs++; + break; + case CQ_RX_ERROP_PREL2_ERR: + stats->rx_prel2_errs++; + break; + case CQ_RX_ERROP_L2_MAL: + stats->rx_l2_hdr_malformed++; + break; + case CQ_RX_ERROP_L2_OVERSIZE: + stats->rx_oversize++; + break; + case CQ_RX_ERROP_L2_UNDERSIZE: + stats->rx_undersize++; + break; + case CQ_RX_ERROP_L2_LENMISM: + stats->rx_l2_len_mismatch++; + break; + case CQ_RX_ERROP_L2_PCLP: + stats->rx_l2_pclp++; + break; + case CQ_RX_ERROP_IP_NOT: + stats->rx_ip_ver_errs++; + break; + case CQ_RX_ERROP_IP_CSUM_ERR: + stats->rx_ip_csum_errs++; + break; + case CQ_RX_ERROP_IP_MAL: + stats->rx_ip_hdr_malformed++; + break; + case CQ_RX_ERROP_IP_MALD: + stats->rx_ip_payload_malformed++; + break; + case CQ_RX_ERROP_IP_HOP: + stats->rx_ip_ttl_errs++; + break; + case CQ_RX_ERROP_L3_PCLP: + stats->rx_l3_pclp++; + break; + case CQ_RX_ERROP_L4_MAL: + stats->rx_l4_malformed++; + break; + case CQ_RX_ERROP_L4_CHK: + stats->rx_l4_csum_errs++; + break; + case CQ_RX_ERROP_UDP_LEN: + stats->rx_udp_len_errs++; + break; + case CQ_RX_ERROP_L4_PORT: + stats->rx_l4_port_errs++; + break; + case CQ_RX_ERROP_TCP_FLAG: + stats->rx_tcp_flag_errs++; + break; + case CQ_RX_ERROP_TCP_OFFSET: + stats->rx_tcp_offset_errs++; + break; + case CQ_RX_ERROP_L4_PCLP: + stats->rx_l4_pclp++; + break; + case CQ_RX_ERROP_RBDR_TRUNC: + stats->rx_truncated_pkts++; + break; + } + + return (1); +} + +/* Check for errors in the send cmp.queue entry */ +int +nicvf_check_cqe_tx_errs(struct nicvf *nic, struct cmp_queue *cq, + struct cqe_send_t *cqe_tx) +{ + struct cmp_queue_stats *stats = &cq->stats; + + switch (cqe_tx->send_status) { + case CQ_TX_ERROP_GOOD: + stats->tx.good++; + return (0); + case CQ_TX_ERROP_DESC_FAULT: + stats->tx.desc_fault++; + break; + case CQ_TX_ERROP_HDR_CONS_ERR: + stats->tx.hdr_cons_err++; + break; + case CQ_TX_ERROP_SUBDC_ERR: + stats->tx.subdesc_err++; + break; + case CQ_TX_ERROP_IMM_SIZE_OFLOW: + stats->tx.imm_size_oflow++; + break; + case CQ_TX_ERROP_DATA_SEQUENCE_ERR: + stats->tx.data_seq_err++; + break; + case CQ_TX_ERROP_MEM_SEQUENCE_ERR: + stats->tx.mem_seq_err++; + break; + case CQ_TX_ERROP_LOCK_VIOL: + stats->tx.lock_viol++; + break; + case CQ_TX_ERROP_DATA_FAULT: + stats->tx.data_fault++; + break; + case CQ_TX_ERROP_TSTMP_CONFLICT: + stats->tx.tstmp_conflict++; + break; + case CQ_TX_ERROP_TSTMP_TIMEOUT: + stats->tx.tstmp_timeout++; + break; + case CQ_TX_ERROP_MEM_FAULT: + stats->tx.mem_fault++; + break; + case CQ_TX_ERROP_CK_OVERLAP: + stats->tx.csum_overlap++; + break; + case CQ_TX_ERROP_CK_OFLOW: + stats->tx.csum_overflow++; + break; + } + + return (1); +} diff --git a/sys/dev/vnic/nicvf_queues.h b/sys/dev/vnic/nicvf_queues.h new file mode 100644 index 00000000000..7dd19786093 --- /dev/null +++ b/sys/dev/vnic/nicvf_queues.h @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2015 Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef NICVF_QUEUES_H +#define NICVF_QUEUES_H + +#include "q_struct.h" + +#define MAX_QUEUE_SET 128 +#define MAX_RCV_QUEUES_PER_QS 8 +#define MAX_RCV_BUF_DESC_RINGS_PER_QS 2 +#define MAX_SND_QUEUES_PER_QS 8 +#define MAX_CMP_QUEUES_PER_QS 8 + +/* VF's queue interrupt ranges */ +#define NICVF_INTR_ID_CQ 0 +#define NICVF_INTR_ID_SQ 8 +#define NICVF_INTR_ID_RBDR 16 +#define NICVF_INTR_ID_MISC 18 +#define NICVF_INTR_ID_QS_ERR 19 + +#define for_each_cq_irq(irq) \ + for ((irq) = NICVF_INTR_ID_CQ; (irq) < NICVF_INTR_ID_SQ; (irq)++) +#define for_each_sq_irq(irq) \ + for ((irq) = NICVF_INTR_ID_SQ; (irq) < NICVF_INTR_ID_RBDR; (irq)++) +#define for_each_rbdr_irq(irq) \ + for ((irq) = NICVF_INTR_ID_RBDR; (irq) < NICVF_INTR_ID_MISC; (irq)++) + +#define RBDR_SIZE0 0UL /* 8K entries */ +#define RBDR_SIZE1 1UL /* 16K entries */ +#define RBDR_SIZE2 2UL /* 32K entries */ +#define RBDR_SIZE3 3UL /* 64K entries */ +#define RBDR_SIZE4 4UL /* 126K entries */ +#define RBDR_SIZE5 5UL /* 256K entries */ +#define RBDR_SIZE6 6UL /* 512K entries */ + +#define SND_QUEUE_SIZE0 0UL /* 1K entries */ +#define SND_QUEUE_SIZE1 1UL /* 2K entries */ +#define SND_QUEUE_SIZE2 2UL /* 4K entries */ +#define SND_QUEUE_SIZE3 3UL /* 8K entries */ +#define SND_QUEUE_SIZE4 4UL /* 16K entries */ +#define SND_QUEUE_SIZE5 5UL /* 32K entries */ +#define SND_QUEUE_SIZE6 6UL /* 64K entries */ + +#define CMP_QUEUE_SIZE0 0UL /* 1K entries */ +#define CMP_QUEUE_SIZE1 1UL /* 2K entries */ +#define CMP_QUEUE_SIZE2 2UL /* 4K entries */ +#define CMP_QUEUE_SIZE3 3UL /* 8K entries */ +#define CMP_QUEUE_SIZE4 4UL /* 16K entries */ +#define CMP_QUEUE_SIZE5 5UL /* 32K entries */ +#define CMP_QUEUE_SIZE6 6UL /* 64K entries */ + +/* Default queue count per QS, its lengths and threshold values */ +#define RBDR_CNT 1 +#define RCV_QUEUE_CNT 8 +#define SND_QUEUE_CNT 8 +#define CMP_QUEUE_CNT 8 /* Max of RCV and SND qcount */ + +#define SND_QSIZE SND_QUEUE_SIZE2 +#define SND_QUEUE_LEN (1UL << (SND_QSIZE + 10)) +#define MAX_SND_QUEUE_LEN (1UL << (SND_QUEUE_SIZE6 + 10)) +#define SND_QUEUE_THRESH 2UL +#define MIN_SQ_DESC_PER_PKT_XMIT 2 +/* Since timestamp not enabled, otherwise 2 */ +#define MAX_CQE_PER_PKT_XMIT 1 + +/* + * Keep CQ and SQ sizes same, if timestamping + * is enabled this equation will change. + */ +#define CMP_QSIZE CMP_QUEUE_SIZE2 +#define CMP_QUEUE_LEN (1UL << (CMP_QSIZE + 10)) +#define CMP_QUEUE_CQE_THRESH 0 +#define CMP_QUEUE_TIMER_THRESH 220 /* 10usec */ + +#define RBDR_SIZE RBDR_SIZE0 +#define RCV_BUF_COUNT (1UL << (RBDR_SIZE + 13)) +#define MAX_RCV_BUF_COUNT (1UL << (RBDR_SIZE6 + 13)) +#define RBDR_THRESH (RCV_BUF_COUNT / 2) +#define DMA_BUFFER_LEN 2048 /* In multiples of 128bytes */ + +#define MAX_CQES_FOR_TX \ + ((SND_QUEUE_LEN / MIN_SQ_DESC_PER_PKT_XMIT) * MAX_CQE_PER_PKT_XMIT) +/* Calculate number of CQEs to reserve for all SQEs. + * Its 1/256th level of CQ size. + * '+ 1' to account for pipelining + */ +#define RQ_CQ_DROP \ + ((256 / (CMP_QUEUE_LEN / (CMP_QUEUE_LEN - MAX_CQES_FOR_TX))) + 1) + +/* Descriptor size in bytes */ +#define SND_QUEUE_DESC_SIZE 16 +#define CMP_QUEUE_DESC_SIZE 512 + +/* Buffer / descriptor alignments */ +#define NICVF_RCV_BUF_ALIGN 7 +#define NICVF_RCV_BUF_ALIGN_BYTES (1UL << NICVF_RCV_BUF_ALIGN) +#define NICVF_CQ_BASE_ALIGN_BYTES 512 /* 9 bits */ +#define NICVF_SQ_BASE_ALIGN_BYTES 128 /* 7 bits */ + +#define NICVF_ALIGNED_ADDR(addr, align_bytes) \ + roundup2((addr), (align_bytes)) +#define NICVF_ADDR_ALIGN_LEN(addr, bytes) \ + (NICVF_ALIGNED_ADDR((addr), (bytes)) - (bytes)) +#define NICVF_RCV_BUF_ALIGN_LEN(addr) \ + (NICVF_ALIGNED_ADDR((addr), NICVF_RCV_BUF_ALIGN_BYTES) - (addr)) + +#define NICVF_TXBUF_MAXSIZE 9212 /* Total max payload without TSO */ +#define NICVF_TXBUF_NSEGS 256 /* Single command is at most 256 buffers + (hdr + 255 subcmds) */ + + +/* Queue enable/disable */ +#define NICVF_SQ_EN (1UL << 19) + +/* Queue reset */ +#define NICVF_CQ_RESET (1UL << 41) +#define NICVF_SQ_RESET (1UL << 17) +#define NICVF_RBDR_RESET (1UL << 43) + +enum CQ_RX_ERRLVL_E { + CQ_ERRLVL_MAC, + CQ_ERRLVL_L2, + CQ_ERRLVL_L3, + CQ_ERRLVL_L4, +}; + +enum CQ_RX_ERROP_E { + CQ_RX_ERROP_RE_NONE = 0x0, + CQ_RX_ERROP_RE_PARTIAL = 0x1, + CQ_RX_ERROP_RE_JABBER = 0x2, + CQ_RX_ERROP_RE_FCS = 0x7, + CQ_RX_ERROP_RE_TERMINATE = 0x9, + CQ_RX_ERROP_RE_RX_CTL = 0xb, + CQ_RX_ERROP_PREL2_ERR = 0x1f, + CQ_RX_ERROP_L2_FRAGMENT = 0x20, + CQ_RX_ERROP_L2_OVERRUN = 0x21, + CQ_RX_ERROP_L2_PFCS = 0x22, + CQ_RX_ERROP_L2_PUNY = 0x23, + CQ_RX_ERROP_L2_MAL = 0x24, + CQ_RX_ERROP_L2_OVERSIZE = 0x25, + CQ_RX_ERROP_L2_UNDERSIZE = 0x26, + CQ_RX_ERROP_L2_LENMISM = 0x27, + CQ_RX_ERROP_L2_PCLP = 0x28, + CQ_RX_ERROP_IP_NOT = 0x41, + CQ_RX_ERROP_IP_CSUM_ERR = 0x42, + CQ_RX_ERROP_IP_MAL = 0x43, + CQ_RX_ERROP_IP_MALD = 0x44, + CQ_RX_ERROP_IP_HOP = 0x45, + CQ_RX_ERROP_L3_ICRC = 0x46, + CQ_RX_ERROP_L3_PCLP = 0x47, + CQ_RX_ERROP_L4_MAL = 0x61, + CQ_RX_ERROP_L4_CHK = 0x62, + CQ_RX_ERROP_UDP_LEN = 0x63, + CQ_RX_ERROP_L4_PORT = 0x64, + CQ_RX_ERROP_TCP_FLAG = 0x65, + CQ_RX_ERROP_TCP_OFFSET = 0x66, + CQ_RX_ERROP_L4_PCLP = 0x67, + CQ_RX_ERROP_RBDR_TRUNC = 0x70, +}; + +enum CQ_TX_ERROP_E { + CQ_TX_ERROP_GOOD = 0x0, + CQ_TX_ERROP_DESC_FAULT = 0x10, + CQ_TX_ERROP_HDR_CONS_ERR = 0x11, + CQ_TX_ERROP_SUBDC_ERR = 0x12, + CQ_TX_ERROP_IMM_SIZE_OFLOW = 0x80, + CQ_TX_ERROP_DATA_SEQUENCE_ERR = 0x81, + CQ_TX_ERROP_MEM_SEQUENCE_ERR = 0x82, + CQ_TX_ERROP_LOCK_VIOL = 0x83, + CQ_TX_ERROP_DATA_FAULT = 0x84, + CQ_TX_ERROP_TSTMP_CONFLICT = 0x85, + CQ_TX_ERROP_TSTMP_TIMEOUT = 0x86, + CQ_TX_ERROP_MEM_FAULT = 0x87, + CQ_TX_ERROP_CK_OVERLAP = 0x88, + CQ_TX_ERROP_CK_OFLOW = 0x89, + CQ_TX_ERROP_ENUM_LAST = 0x8a, +}; + +struct cmp_queue_stats { + struct tx_stats { + uint64_t good; + uint64_t desc_fault; + uint64_t hdr_cons_err; + uint64_t subdesc_err; + uint64_t imm_size_oflow; + uint64_t data_seq_err; + uint64_t mem_seq_err; + uint64_t lock_viol; + uint64_t data_fault; + uint64_t tstmp_conflict; + uint64_t tstmp_timeout; + uint64_t mem_fault; + uint64_t csum_overlap; + uint64_t csum_overflow; + } tx; +} __aligned(CACHE_LINE_SIZE); + +enum RQ_SQ_STATS { + RQ_SQ_STATS_OCTS, + RQ_SQ_STATS_PKTS, +}; + +struct rx_tx_queue_stats { + uint64_t bytes; + uint64_t pkts; +} __aligned(CACHE_LINE_SIZE); + +struct q_desc_mem { + bus_dma_tag_t dmat; + bus_dmamap_t dmap; + void *base; + bus_addr_t phys_base; + uint64_t size; + uint16_t q_len; +}; + +struct rbdr { + boolean_t enable; + uint32_t dma_size; + uint32_t frag_len; + uint32_t thresh; /* Threshold level for interrupt */ + void *desc; + uint32_t head; + uint32_t tail; + struct q_desc_mem dmem; + + struct nicvf *nic; + int idx; + + struct task rbdr_task; + struct task rbdr_task_nowait; + struct taskqueue *rbdr_taskq; + + bus_dma_tag_t rbdr_buff_dmat; + bus_dmamap_t *rbdr_buff_dmaps; +} __aligned(CACHE_LINE_SIZE); + +struct rcv_queue { + boolean_t enable; + struct rbdr *rbdr_start; + struct rbdr *rbdr_cont; + boolean_t en_tcp_reassembly; + uint8_t cq_qs; /* CQ's QS to which this RQ is assigned */ + uint8_t cq_idx; /* CQ index (0 to 7) in the QS */ + uint8_t cont_rbdr_qs; /* Continue buffer ptrs - QS num */ + uint8_t cont_qs_rbdr_idx; /* RBDR idx in the cont QS */ + uint8_t start_rbdr_qs; /* First buffer ptrs - QS num */ + uint8_t start_qs_rbdr_idx; /* RBDR idx in the above QS */ + uint8_t caching; + struct rx_tx_queue_stats stats; +} __aligned(CACHE_LINE_SIZE); + +struct cmp_queue { + boolean_t enable; + uint16_t thresh; + + struct nicvf *nic; + int idx; /* This queue index */ + + struct buf_ring *rx_br; /* Reception buf ring */ + struct mtx mtx; /* lock to serialize processing CQEs */ + char mtx_name[32]; + + struct task cmp_task; + struct taskqueue *cmp_taskq; + + void *desc; + struct q_desc_mem dmem; + struct cmp_queue_stats stats; + int irq; +} __aligned(CACHE_LINE_SIZE); + +struct snd_buff { + bus_dmamap_t dmap; + struct mbuf *mbuf; +}; + +struct snd_queue { + boolean_t enable; + uint8_t cq_qs; /* CQ's QS to which this SQ is pointing */ + uint8_t cq_idx; /* CQ index (0 to 7) in the above QS */ + uint16_t thresh; + volatile int free_cnt; + uint32_t head; + uint32_t tail; + uint64_t *skbuff; + void *desc; + + struct nicvf *nic; + int idx; /* This queue index */ + + bus_dma_tag_t snd_buff_dmat; + struct snd_buff *snd_buff; + + struct buf_ring *br; /* Transmission buf ring */ + struct mtx mtx; + char mtx_name[32]; + + struct task snd_task; + struct taskqueue *snd_taskq; + + struct q_desc_mem dmem; + struct rx_tx_queue_stats stats; +} __aligned(CACHE_LINE_SIZE); + +struct queue_set { + boolean_t enable; + boolean_t be_en; + uint8_t vnic_id; + uint8_t rq_cnt; + uint8_t cq_cnt; + uint64_t cq_len; + uint8_t sq_cnt; + uint64_t sq_len; + uint8_t rbdr_cnt; + uint64_t rbdr_len; + struct rcv_queue rq[MAX_RCV_QUEUES_PER_QS]; + struct cmp_queue cq[MAX_CMP_QUEUES_PER_QS]; + struct snd_queue sq[MAX_SND_QUEUES_PER_QS]; + struct rbdr rbdr[MAX_RCV_BUF_DESC_RINGS_PER_QS]; + + struct task qs_err_task; + struct taskqueue *qs_err_taskq; +} __aligned(CACHE_LINE_SIZE); + +#define GET_RBDR_DESC(RING, idx) \ + (&(((struct rbdr_entry_t *)((RING)->desc))[(idx)])) +#define GET_SQ_DESC(RING, idx) \ + (&(((struct sq_hdr_subdesc *)((RING)->desc))[(idx)])) +#define GET_CQ_DESC(RING, idx) \ + (&(((union cq_desc_t *)((RING)->desc))[(idx)])) + +/* CQ status bits */ +#define CQ_WR_FUL (1UL << 26) +#define CQ_WR_DISABLE (1UL << 25) +#define CQ_WR_FAULT (1UL << 24) +#define CQ_CQE_COUNT (0xFFFF << 0) + +#define CQ_ERR_MASK (CQ_WR_FUL | CQ_WR_DISABLE | CQ_WR_FAULT) + +#define NICVF_TX_LOCK(sq) mtx_lock(&(sq)->mtx) +#define NICVF_TX_TRYLOCK(sq) mtx_trylock(&(sq)->mtx) +#define NICVF_TX_UNLOCK(sq) mtx_unlock(&(sq)->mtx) +#define NICVF_TX_LOCK_ASSERT(sq) mtx_assert(&(sq)->mtx, MA_OWNED) + +#define NICVF_CMP_LOCK(cq) mtx_lock(&(cq)->mtx) +#define NICVF_CMP_UNLOCK(cq) mtx_unlock(&(cq)->mtx) + +int nicvf_set_qset_resources(struct nicvf *); +int nicvf_config_data_transfer(struct nicvf *, boolean_t); +void nicvf_qset_config(struct nicvf *, boolean_t); + +void nicvf_enable_intr(struct nicvf *, int, int); +void nicvf_disable_intr(struct nicvf *, int, int); +void nicvf_clear_intr(struct nicvf *, int, int); +int nicvf_is_intr_enabled(struct nicvf *, int, int); + +/* Register access APIs */ +void nicvf_reg_write(struct nicvf *, uint64_t, uint64_t); +uint64_t nicvf_reg_read(struct nicvf *, uint64_t); +void nicvf_qset_reg_write(struct nicvf *, uint64_t, uint64_t); +uint64_t nicvf_qset_reg_read(struct nicvf *, uint64_t); +void nicvf_queue_reg_write(struct nicvf *, uint64_t, uint64_t, uint64_t); +uint64_t nicvf_queue_reg_read(struct nicvf *, uint64_t, uint64_t); + +/* Stats */ +void nicvf_update_rq_stats(struct nicvf *, int); +void nicvf_update_sq_stats(struct nicvf *, int); +int nicvf_check_cqe_rx_errs(struct nicvf *, struct cmp_queue *, + struct cqe_rx_t *); +int nicvf_check_cqe_tx_errs(struct nicvf *,struct cmp_queue *, + struct cqe_send_t *); +#endif /* NICVF_QUEUES_H */ diff --git a/sys/dev/vnic/q_struct.h b/sys/dev/vnic/q_struct.h new file mode 100644 index 00000000000..471cc4fada4 --- /dev/null +++ b/sys/dev/vnic/q_struct.h @@ -0,0 +1,721 @@ +/* + * Copyright (C) 2015 Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef Q_STRUCT_H +#define Q_STRUCT_H + +#define __LITTLE_ENDIAN_BITFIELD + +/* Load transaction types for reading segment bytes specified by + * NIC_SEND_GATHER_S[LD_TYPE]. + */ +enum nic_send_ld_type_e { + NIC_SEND_LD_TYPE_E_LDD = 0x0, + NIC_SEND_LD_TYPE_E_LDT = 0x1, + NIC_SEND_LD_TYPE_E_LDWB = 0x2, + NIC_SEND_LD_TYPE_E_ENUM_LAST = 0x3, +}; + +enum ether_type_algorithm { + ETYPE_ALG_NONE = 0x0, + ETYPE_ALG_SKIP = 0x1, + ETYPE_ALG_ENDPARSE = 0x2, + ETYPE_ALG_VLAN = 0x3, + ETYPE_ALG_VLAN_STRIP = 0x4, +}; + +enum layer3_type { + L3TYPE_NONE = 0x00, + L3TYPE_GRH = 0x01, + L3TYPE_IPV4 = 0x04, + L3TYPE_IPV4_OPTIONS = 0x05, + L3TYPE_IPV6 = 0x06, + L3TYPE_IPV6_OPTIONS = 0x07, + L3TYPE_ET_STOP = 0x0D, + L3TYPE_OTHER = 0x0E, +}; + +enum layer4_type { + L4TYPE_NONE = 0x00, + L4TYPE_IPSEC_ESP = 0x01, + L4TYPE_IPFRAG = 0x02, + L4TYPE_IPCOMP = 0x03, + L4TYPE_TCP = 0x04, + L4TYPE_UDP = 0x05, + L4TYPE_SCTP = 0x06, + L4TYPE_GRE = 0x07, + L4TYPE_ROCE_BTH = 0x08, + L4TYPE_OTHER = 0x0E, +}; + +/* CPI and RSSI configuration */ +enum cpi_algorithm_type { + CPI_ALG_NONE = 0x0, + CPI_ALG_VLAN = 0x1, + CPI_ALG_VLAN16 = 0x2, + CPI_ALG_DIFF = 0x3, +}; + +enum rss_algorithm_type { + RSS_ALG_NONE = 0x00, + RSS_ALG_PORT = 0x01, + RSS_ALG_IP = 0x02, + RSS_ALG_TCP_IP = 0x03, + RSS_ALG_UDP_IP = 0x04, + RSS_ALG_SCTP_IP = 0x05, + RSS_ALG_GRE_IP = 0x06, + RSS_ALG_ROCE = 0x07, +}; + +enum rss_hash_cfg { + RSS_HASH_L2ETC = 0x00, + RSS_HASH_IP = 0x01, + RSS_HASH_TCP = 0x02, + RSS_HASH_TCP_SYN_DIS = 0x03, + RSS_HASH_UDP = 0x04, + RSS_HASH_L4ETC = 0x05, + RSS_HASH_ROCE = 0x06, + RSS_L3_BIDI = 0x07, + RSS_L4_BIDI = 0x08, +}; + +/* Completion queue entry types */ +enum cqe_type { + CQE_TYPE_INVALID = 0x0, + CQE_TYPE_RX = 0x2, + CQE_TYPE_RX_SPLIT = 0x3, + CQE_TYPE_RX_TCP = 0x4, + CQE_TYPE_SEND = 0x8, + CQE_TYPE_SEND_PTP = 0x9, +}; + +enum cqe_rx_tcp_status { + CQE_RX_STATUS_VALID_TCP_CNXT = 0x00, + CQE_RX_STATUS_INVALID_TCP_CNXT = 0x0F, +}; + +enum cqe_send_status { + CQE_SEND_STATUS_GOOD = 0x00, + CQE_SEND_STATUS_DESC_FAULT = 0x01, + CQE_SEND_STATUS_HDR_CONS_ERR = 0x11, + CQE_SEND_STATUS_SUBDESC_ERR = 0x12, + CQE_SEND_STATUS_IMM_SIZE_OFLOW = 0x80, + CQE_SEND_STATUS_CRC_SEQ_ERR = 0x81, + CQE_SEND_STATUS_DATA_SEQ_ERR = 0x82, + CQE_SEND_STATUS_MEM_SEQ_ERR = 0x83, + CQE_SEND_STATUS_LOCK_VIOL = 0x84, + CQE_SEND_STATUS_LOCK_UFLOW = 0x85, + CQE_SEND_STATUS_DATA_FAULT = 0x86, + CQE_SEND_STATUS_TSTMP_CONFLICT = 0x87, + CQE_SEND_STATUS_TSTMP_TIMEOUT = 0x88, + CQE_SEND_STATUS_MEM_FAULT = 0x89, + CQE_SEND_STATUS_CSUM_OVERLAP = 0x8A, + CQE_SEND_STATUS_CSUM_OVERFLOW = 0x8B, +}; + +enum cqe_rx_tcp_end_reason { + CQE_RX_TCP_END_FIN_FLAG_DET = 0, + CQE_RX_TCP_END_INVALID_FLAG = 1, + CQE_RX_TCP_END_TIMEOUT = 2, + CQE_RX_TCP_END_OUT_OF_SEQ = 3, + CQE_RX_TCP_END_PKT_ERR = 4, + CQE_RX_TCP_END_QS_DISABLED = 0x0F, +}; + +/* Packet protocol level error enumeration */ +enum cqe_rx_err_level { + CQE_RX_ERRLVL_RE = 0x0, + CQE_RX_ERRLVL_L2 = 0x1, + CQE_RX_ERRLVL_L3 = 0x2, + CQE_RX_ERRLVL_L4 = 0x3, +}; + +/* Packet protocol level error type enumeration */ +enum cqe_rx_err_opcode { + CQE_RX_ERR_RE_NONE = 0x0, + CQE_RX_ERR_RE_PARTIAL = 0x1, + CQE_RX_ERR_RE_JABBER = 0x2, + CQE_RX_ERR_RE_FCS = 0x7, + CQE_RX_ERR_RE_TERMINATE = 0x9, + CQE_RX_ERR_RE_RX_CTL = 0xb, + CQE_RX_ERR_PREL2_ERR = 0x1f, + CQE_RX_ERR_L2_FRAGMENT = 0x20, + CQE_RX_ERR_L2_OVERRUN = 0x21, + CQE_RX_ERR_L2_PFCS = 0x22, + CQE_RX_ERR_L2_PUNY = 0x23, + CQE_RX_ERR_L2_MAL = 0x24, + CQE_RX_ERR_L2_OVERSIZE = 0x25, + CQE_RX_ERR_L2_UNDERSIZE = 0x26, + CQE_RX_ERR_L2_LENMISM = 0x27, + CQE_RX_ERR_L2_PCLP = 0x28, + CQE_RX_ERR_IP_NOT = 0x41, + CQE_RX_ERR_IP_CHK = 0x42, + CQE_RX_ERR_IP_MAL = 0x43, + CQE_RX_ERR_IP_MALD = 0x44, + CQE_RX_ERR_IP_HOP = 0x45, + CQE_RX_ERR_L3_ICRC = 0x46, + CQE_RX_ERR_L3_PCLP = 0x47, + CQE_RX_ERR_L4_MAL = 0x61, + CQE_RX_ERR_L4_CHK = 0x62, + CQE_RX_ERR_UDP_LEN = 0x63, + CQE_RX_ERR_L4_PORT = 0x64, + CQE_RX_ERR_TCP_FLAG = 0x65, + CQE_RX_ERR_TCP_OFFSET = 0x66, + CQE_RX_ERR_L4_PCLP = 0x67, + CQE_RX_ERR_RBDR_TRUNC = 0x70, +}; + +struct cqe_rx_t { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t cqe_type:4; /* W0 */ + uint64_t stdn_fault:1; + uint64_t rsvd0:1; + uint64_t rq_qs:7; + uint64_t rq_idx:3; + uint64_t rsvd1:12; + uint64_t rss_alg:4; + uint64_t rsvd2:4; + uint64_t rb_cnt:4; + uint64_t vlan_found:1; + uint64_t vlan_stripped:1; + uint64_t vlan2_found:1; + uint64_t vlan2_stripped:1; + uint64_t l4_type:4; + uint64_t l3_type:4; + uint64_t l2_present:1; + uint64_t err_level:3; + uint64_t err_opcode:8; + + uint64_t pkt_len:16; /* W1 */ + uint64_t l2_ptr:8; + uint64_t l3_ptr:8; + uint64_t l4_ptr:8; + uint64_t cq_pkt_len:8; + uint64_t align_pad:3; + uint64_t rsvd3:1; + uint64_t chan:12; + + uint64_t rss_tag:32; /* W2 */ + uint64_t vlan_tci:16; + uint64_t vlan_ptr:8; + uint64_t vlan2_ptr:8; + + uint64_t rb3_sz:16; /* W3 */ + uint64_t rb2_sz:16; + uint64_t rb1_sz:16; + uint64_t rb0_sz:16; + + uint64_t rb7_sz:16; /* W4 */ + uint64_t rb6_sz:16; + uint64_t rb5_sz:16; + uint64_t rb4_sz:16; + + uint64_t rb11_sz:16; /* W5 */ + uint64_t rb10_sz:16; + uint64_t rb9_sz:16; + uint64_t rb8_sz:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t err_opcode:8; + uint64_t err_level:3; + uint64_t l2_present:1; + uint64_t l3_type:4; + uint64_t l4_type:4; + uint64_t vlan2_stripped:1; + uint64_t vlan2_found:1; + uint64_t vlan_stripped:1; + uint64_t vlan_found:1; + uint64_t rb_cnt:4; + uint64_t rsvd2:4; + uint64_t rss_alg:4; + uint64_t rsvd1:12; + uint64_t rq_idx:3; + uint64_t rq_qs:7; + uint64_t rsvd0:1; + uint64_t stdn_fault:1; + uint64_t cqe_type:4; /* W0 */ + uint64_t chan:12; + uint64_t rsvd3:1; + uint64_t align_pad:3; + uint64_t cq_pkt_len:8; + uint64_t l4_ptr:8; + uint64_t l3_ptr:8; + uint64_t l2_ptr:8; + uint64_t pkt_len:16; /* W1 */ + uint64_t vlan2_ptr:8; + uint64_t vlan_ptr:8; + uint64_t vlan_tci:16; + uint64_t rss_tag:32; /* W2 */ + uint64_t rb0_sz:16; + uint64_t rb1_sz:16; + uint64_t rb2_sz:16; + uint64_t rb3_sz:16; /* W3 */ + uint64_t rb4_sz:16; + uint64_t rb5_sz:16; + uint64_t rb6_sz:16; + uint64_t rb7_sz:16; /* W4 */ + uint64_t rb8_sz:16; + uint64_t rb9_sz:16; + uint64_t rb10_sz:16; + uint64_t rb11_sz:16; /* W5 */ +#endif + uint64_t rb0_ptr:64; + uint64_t rb1_ptr:64; + uint64_t rb2_ptr:64; + uint64_t rb3_ptr:64; + uint64_t rb4_ptr:64; + uint64_t rb5_ptr:64; + uint64_t rb6_ptr:64; + uint64_t rb7_ptr:64; + uint64_t rb8_ptr:64; + uint64_t rb9_ptr:64; + uint64_t rb10_ptr:64; + uint64_t rb11_ptr:64; +}; + +struct cqe_rx_tcp_err_t { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t cqe_type:4; /* W0 */ + uint64_t rsvd0:60; + + uint64_t rsvd1:4; /* W1 */ + uint64_t partial_first:1; + uint64_t rsvd2:27; + uint64_t rbdr_bytes:8; + uint64_t rsvd3:24; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t rsvd0:60; + uint64_t cqe_type:4; + + uint64_t rsvd3:24; + uint64_t rbdr_bytes:8; + uint64_t rsvd2:27; + uint64_t partial_first:1; + uint64_t rsvd1:4; +#endif +}; + +struct cqe_rx_tcp_t { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t cqe_type:4; /* W0 */ + uint64_t rsvd0:52; + uint64_t cq_tcp_status:8; + + uint64_t rsvd1:32; /* W1 */ + uint64_t tcp_cntx_bytes:8; + uint64_t rsvd2:8; + uint64_t tcp_err_bytes:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t cq_tcp_status:8; + uint64_t rsvd0:52; + uint64_t cqe_type:4; /* W0 */ + + uint64_t tcp_err_bytes:16; + uint64_t rsvd2:8; + uint64_t tcp_cntx_bytes:8; + uint64_t rsvd1:32; /* W1 */ +#endif +}; + +struct cqe_send_t { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t cqe_type:4; /* W0 */ + uint64_t rsvd0:4; + uint64_t sqe_ptr:16; + uint64_t rsvd1:4; + uint64_t rsvd2:10; + uint64_t sq_qs:7; + uint64_t sq_idx:3; + uint64_t rsvd3:8; + uint64_t send_status:8; + + uint64_t ptp_timestamp:64; /* W1 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t send_status:8; + uint64_t rsvd3:8; + uint64_t sq_idx:3; + uint64_t sq_qs:7; + uint64_t rsvd2:10; + uint64_t rsvd1:4; + uint64_t sqe_ptr:16; + uint64_t rsvd0:4; + uint64_t cqe_type:4; /* W0 */ + + uint64_t ptp_timestamp:64; /* W1 */ +#endif +}; + +union cq_desc_t { + uint64_t u[64]; + struct cqe_send_t snd_hdr; + struct cqe_rx_t rx_hdr; + struct cqe_rx_tcp_t rx_tcp_hdr; + struct cqe_rx_tcp_err_t rx_tcp_err_hdr; +}; + +struct rbdr_entry_t { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t rsvd0:15; + uint64_t buf_addr:42; + uint64_t cache_align:7; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t cache_align:7; + uint64_t buf_addr:42; + uint64_t rsvd0:15; +#endif +}; + +/* TCP reassembly context */ +struct rbe_tcp_cnxt_t { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t tcp_pkt_cnt:12; + uint64_t rsvd1:4; + uint64_t align_hdr_bytes:4; + uint64_t align_ptr_bytes:4; + uint64_t ptr_bytes:16; + uint64_t rsvd2:24; + uint64_t cqe_type:4; + uint64_t rsvd0:54; + uint64_t tcp_end_reason:2; + uint64_t tcp_status:4; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t tcp_status:4; + uint64_t tcp_end_reason:2; + uint64_t rsvd0:54; + uint64_t cqe_type:4; + uint64_t rsvd2:24; + uint64_t ptr_bytes:16; + uint64_t align_ptr_bytes:4; + uint64_t align_hdr_bytes:4; + uint64_t rsvd1:4; + uint64_t tcp_pkt_cnt:12; +#endif +}; + +/* Always Big endian */ +struct rx_hdr_t { + uint64_t opaque:32; + uint64_t rss_flow:8; + uint64_t skip_length:6; + uint64_t disable_rss:1; + uint64_t disable_tcp_reassembly:1; + uint64_t nodrop:1; + uint64_t dest_alg:2; + uint64_t rsvd0:2; + uint64_t dest_rq:11; +}; + +enum send_l4_csum_type { + SEND_L4_CSUM_DISABLE = 0x00, + SEND_L4_CSUM_UDP = 0x01, + SEND_L4_CSUM_TCP = 0x02, + SEND_L4_CSUM_SCTP = 0x03, +}; + +enum send_crc_alg { + SEND_CRCALG_CRC32 = 0x00, + SEND_CRCALG_CRC32C = 0x01, + SEND_CRCALG_ICRC = 0x02, +}; + +enum send_load_type { + SEND_LD_TYPE_LDD = 0x00, + SEND_LD_TYPE_LDT = 0x01, + SEND_LD_TYPE_LDWB = 0x02, +}; + +enum send_mem_alg_type { + SEND_MEMALG_SET = 0x00, + SEND_MEMALG_ADD = 0x08, + SEND_MEMALG_SUB = 0x09, + SEND_MEMALG_ADDLEN = 0x0A, + SEND_MEMALG_SUBLEN = 0x0B, +}; + +enum send_mem_dsz_type { + SEND_MEMDSZ_B64 = 0x00, + SEND_MEMDSZ_B32 = 0x01, + SEND_MEMDSZ_B8 = 0x03, +}; + +enum sq_subdesc_type { + SQ_DESC_TYPE_INVALID = 0x00, + SQ_DESC_TYPE_HEADER = 0x01, + SQ_DESC_TYPE_CRC = 0x02, + SQ_DESC_TYPE_IMMEDIATE = 0x03, + SQ_DESC_TYPE_GATHER = 0x04, + SQ_DESC_TYPE_MEMORY = 0x05, +}; + +struct sq_crc_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t rsvd1:32; + uint64_t crc_ival:32; + uint64_t subdesc_type:4; + uint64_t crc_alg:2; + uint64_t rsvd0:10; + uint64_t crc_insert_pos:16; + uint64_t hdr_start:16; + uint64_t crc_len:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t crc_len:16; + uint64_t hdr_start:16; + uint64_t crc_insert_pos:16; + uint64_t rsvd0:10; + uint64_t crc_alg:2; + uint64_t subdesc_type:4; + uint64_t crc_ival:32; + uint64_t rsvd1:32; +#endif +}; + +struct sq_gather_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t subdesc_type:4; /* W0 */ + uint64_t ld_type:2; + uint64_t rsvd0:42; + uint64_t size:16; + + uint64_t rsvd1:15; /* W1 */ + uint64_t addr:49; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t size:16; + uint64_t rsvd0:42; + uint64_t ld_type:2; + uint64_t subdesc_type:4; /* W0 */ + + uint64_t addr:49; + uint64_t rsvd1:15; /* W1 */ +#endif +}; + +/* SQ immediate subdescriptor */ +struct sq_imm_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t subdesc_type:4; /* W0 */ + uint64_t rsvd0:46; + uint64_t len:14; + + uint64_t data:64; /* W1 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t len:14; + uint64_t rsvd0:46; + uint64_t subdesc_type:4; /* W0 */ + + uint64_t data:64; /* W1 */ +#endif +}; + +struct sq_mem_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t subdesc_type:4; /* W0 */ + uint64_t mem_alg:4; + uint64_t mem_dsz:2; + uint64_t wmem:1; + uint64_t rsvd0:21; + uint64_t offset:32; + + uint64_t rsvd1:15; /* W1 */ + uint64_t addr:49; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t offset:32; + uint64_t rsvd0:21; + uint64_t wmem:1; + uint64_t mem_dsz:2; + uint64_t mem_alg:4; + uint64_t subdesc_type:4; /* W0 */ + + uint64_t addr:49; + uint64_t rsvd1:15; /* W1 */ +#endif +}; + +struct sq_hdr_subdesc { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t subdesc_type:4; + uint64_t tso:1; + uint64_t post_cqe:1; /* Post CQE on no error also */ + uint64_t dont_send:1; + uint64_t tstmp:1; + uint64_t subdesc_cnt:8; + uint64_t csum_l4:2; + uint64_t csum_l3:1; + uint64_t rsvd0:5; + uint64_t l4_offset:8; + uint64_t l3_offset:8; + uint64_t rsvd1:4; + uint64_t tot_len:20; /* W0 */ + + uint64_t tso_sdc_cont:8; + uint64_t tso_sdc_first:8; + uint64_t tso_l4_offset:8; + uint64_t tso_flags_last:12; + uint64_t tso_flags_first:12; + uint64_t rsvd2:2; + uint64_t tso_max_paysize:14; /* W1 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t tot_len:20; + uint64_t rsvd1:4; + uint64_t l3_offset:8; + uint64_t l4_offset:8; + uint64_t rsvd0:5; + uint64_t csum_l3:1; + uint64_t csum_l4:2; + uint64_t subdesc_cnt:8; + uint64_t tstmp:1; + uint64_t dont_send:1; + uint64_t post_cqe:1; /* Post CQE on no error also */ + uint64_t tso:1; + uint64_t subdesc_type:4; /* W0 */ + + uint64_t tso_max_paysize:14; + uint64_t rsvd2:2; + uint64_t tso_flags_first:12; + uint64_t tso_flags_last:12; + uint64_t tso_l4_offset:8; + uint64_t tso_sdc_first:8; + uint64_t tso_sdc_cont:8; /* W1 */ +#endif +}; + +/* Queue config register formats */ +struct rq_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t reserved_2_63:62; + uint64_t ena:1; + uint64_t tcp_ena:1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t tcp_ena:1; + uint64_t ena:1; + uint64_t reserved_2_63:62; +#endif +}; + +struct cq_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t reserved_43_63:21; + uint64_t ena:1; + uint64_t reset:1; + uint64_t caching:1; + uint64_t reserved_35_39:5; + uint64_t qsize:3; + uint64_t reserved_25_31:7; + uint64_t avg_con:9; + uint64_t reserved_0_15:16; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t reserved_0_15:16; + uint64_t avg_con:9; + uint64_t reserved_25_31:7; + uint64_t qsize:3; + uint64_t reserved_35_39:5; + uint64_t caching:1; + uint64_t reset:1; + uint64_t ena:1; + uint64_t reserved_43_63:21; +#endif +}; + +struct sq_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t reserved_20_63:44; + uint64_t ena:1; + uint64_t reserved_18_18:1; + uint64_t reset:1; + uint64_t ldwb:1; + uint64_t reserved_11_15:5; + uint64_t qsize:3; + uint64_t reserved_3_7:5; + uint64_t tstmp_bgx_intf:3; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t tstmp_bgx_intf:3; + uint64_t reserved_3_7:5; + uint64_t qsize:3; + uint64_t reserved_11_15:5; + uint64_t ldwb:1; + uint64_t reset:1; + uint64_t reserved_18_18:1; + uint64_t ena:1; + uint64_t reserved_20_63:44; +#endif +}; + +struct rbdr_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t reserved_45_63:19; + uint64_t ena:1; + uint64_t reset:1; + uint64_t ldwb:1; + uint64_t reserved_36_41:6; + uint64_t qsize:4; + uint64_t reserved_25_31:7; + uint64_t avg_con:9; + uint64_t reserved_12_15:4; + uint64_t lines:12; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t lines:12; + uint64_t reserved_12_15:4; + uint64_t avg_con:9; + uint64_t reserved_25_31:7; + uint64_t qsize:4; + uint64_t reserved_36_41:6; + uint64_t ldwb:1; + uint64_t reset:1; + uint64_t ena: 1; + uint64_t reserved_45_63:19; +#endif +}; + +struct qs_cfg { +#if defined(__BIG_ENDIAN_BITFIELD) + uint64_t reserved_32_63:32; + uint64_t ena:1; + uint64_t reserved_27_30:4; + uint64_t sq_ins_ena:1; + uint64_t sq_ins_pos:6; + uint64_t lock_ena:1; + uint64_t lock_viol_cqe_ena:1; + uint64_t send_tstmp_ena:1; + uint64_t be:1; + uint64_t reserved_7_15:9; + uint64_t vnic:7; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint64_t vnic:7; + uint64_t reserved_7_15:9; + uint64_t be:1; + uint64_t send_tstmp_ena:1; + uint64_t lock_viol_cqe_ena:1; + uint64_t lock_ena:1; + uint64_t sq_ins_pos:6; + uint64_t sq_ins_ena:1; + uint64_t reserved_27_30:4; + uint64_t ena:1; + uint64_t reserved_32_63:32; +#endif +}; + +#endif /* Q_STRUCT_H */ diff --git a/sys/dev/vnic/thunder_bgx.c b/sys/dev/vnic/thunder_bgx.c new file mode 100644 index 00000000000..32d97c871cf --- /dev/null +++ b/sys/dev/vnic/thunder_bgx.c @@ -0,0 +1,1135 @@ +/* + * Copyright (C) 2015 Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ +#include "opt_platform.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "thunder_bgx.h" +#include "thunder_bgx_var.h" +#include "nic_reg.h" +#include "nic.h" + +#include "lmac_if.h" + +#define THUNDER_BGX_DEVSTR "ThunderX BGX Ethernet I/O Interface" + +static MALLOC_DEFINE(M_BGX, "thunder_bgx", "ThunderX BGX dynamic memory"); + +#define BGX_NODE_ID_MASK 0x1 +#define BGX_NODE_ID_SHIFT 24 + +#define DRV_NAME "thunder-BGX" +#define DRV_VERSION "1.0" + +static int bgx_init_phy(struct bgx *); + +static struct bgx *bgx_vnic[MAX_BGX_THUNDER]; +static int lmac_count __unused; /* Total no of LMACs in system */ + +static int bgx_xaui_check_link(struct lmac *lmac); +static void bgx_get_qlm_mode(struct bgx *); +static void bgx_init_hw(struct bgx *); +static int bgx_lmac_enable(struct bgx *, uint8_t); +static void bgx_lmac_disable(struct bgx *, uint8_t); + +static int thunder_bgx_probe(device_t); +static int thunder_bgx_attach(device_t); +static int thunder_bgx_detach(device_t); + +static device_method_t thunder_bgx_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, thunder_bgx_probe), + DEVMETHOD(device_attach, thunder_bgx_attach), + DEVMETHOD(device_detach, thunder_bgx_detach), + + DEVMETHOD_END, +}; + +static driver_t thunder_bgx_driver = { + "bgx", + thunder_bgx_methods, + sizeof(struct lmac), +}; + +static devclass_t thunder_bgx_devclass; + +DRIVER_MODULE(thunder_bgx, pci, thunder_bgx_driver, thunder_bgx_devclass, 0, 0); +MODULE_DEPEND(thunder_bgx, pci, 1, 1, 1); +MODULE_DEPEND(thunder_bgx, ether, 1, 1, 1); +MODULE_DEPEND(thunder_bgx, octeon_mdio, 1, 1, 1); + +static int +thunder_bgx_probe(device_t dev) +{ + uint16_t vendor_id; + uint16_t device_id; + + vendor_id = pci_get_vendor(dev); + device_id = pci_get_device(dev); + + if (vendor_id == PCI_VENDOR_ID_CAVIUM && + device_id == PCI_DEVICE_ID_THUNDER_BGX) { + device_set_desc(dev, THUNDER_BGX_DEVSTR); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +thunder_bgx_attach(device_t dev) +{ + struct bgx *bgx; + uint8_t lmac; + int err; + int rid; + + bgx = malloc(sizeof(*bgx), M_BGX, (M_WAITOK | M_ZERO)); + bgx->dev = dev; + /* Enable bus mastering */ + pci_enable_busmaster(dev); + /* Allocate resources - configuration registers */ + rid = PCIR_BAR(PCI_CFG_REG_BAR_NUM); + bgx->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (bgx->reg_base == NULL) { + device_printf(dev, "Could not allocate CSR memory space\n"); + err = ENXIO; + goto err_disable_device; + } + + bgx->bgx_id = (rman_get_start(bgx->reg_base) >> BGX_NODE_ID_SHIFT) & + BGX_NODE_ID_MASK; + bgx->bgx_id += nic_get_node_id(bgx->reg_base) * MAX_BGX_PER_CN88XX; + + bgx_vnic[bgx->bgx_id] = bgx; + bgx_get_qlm_mode(bgx); + + err = bgx_init_phy(bgx); + if (err != 0) + goto err_free_res; + + bgx_init_hw(bgx); + + /* Enable all LMACs */ + for (lmac = 0; lmac < bgx->lmac_count; lmac++) { + err = bgx_lmac_enable(bgx, lmac); + if (err) { + device_printf(dev, "BGX%d failed to enable lmac%d\n", + bgx->bgx_id, lmac); + goto err_free_res; + } + } + + return (0); + +err_free_res: + bgx_vnic[bgx->bgx_id] = NULL; + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(bgx->reg_base), bgx->reg_base); +err_disable_device: + free(bgx, M_BGX); + pci_disable_busmaster(dev); + + return (err); +} + +static int +thunder_bgx_detach(device_t dev) +{ + struct lmac *lmac; + struct bgx *bgx; + uint8_t lmacid; + + lmac = device_get_softc(dev); + bgx = lmac->bgx; + /* Disable all LMACs */ + for (lmacid = 0; lmacid < bgx->lmac_count; lmacid++) + bgx_lmac_disable(bgx, lmacid); + + return (0); +} + +/* Register read/write APIs */ +static uint64_t +bgx_reg_read(struct bgx *bgx, uint8_t lmac, uint64_t offset) +{ + bus_space_handle_t addr; + + addr = ((uint32_t)lmac << 20) + offset; + + return (bus_read_8(bgx->reg_base, addr)); +} + +static void +bgx_reg_write(struct bgx *bgx, uint8_t lmac, uint64_t offset, uint64_t val) +{ + bus_space_handle_t addr; + + addr = ((uint32_t)lmac << 20) + offset; + + bus_write_8(bgx->reg_base, addr, val); +} + +static void +bgx_reg_modify(struct bgx *bgx, uint8_t lmac, uint64_t offset, uint64_t val) +{ + bus_space_handle_t addr; + + addr = ((uint32_t)lmac << 20) + offset; + + bus_write_8(bgx->reg_base, addr, val | bus_read_8(bgx->reg_base, addr)); +} + +static int +bgx_poll_reg(struct bgx *bgx, uint8_t lmac, uint64_t reg, uint64_t mask, + boolean_t zero) +{ + int timeout = 100; + uint64_t reg_val; + + while (timeout) { + reg_val = bgx_reg_read(bgx, lmac, reg); + if (zero && !(reg_val & mask)) + return (0); + if (!zero && (reg_val & mask)) + return (0); + + DELAY(1000); + timeout--; + } + return (ETIMEDOUT); +} + +/* Return number of BGX present in HW */ +u_int +bgx_get_map(int node) +{ + int i; + u_int map = 0; + + for (i = 0; i < MAX_BGX_PER_CN88XX; i++) { + if (bgx_vnic[(node * MAX_BGX_PER_CN88XX) + i]) + map |= (1 << i); + } + + return (map); +} + +/* Return number of LMAC configured for this BGX */ +int +bgx_get_lmac_count(int node, int bgx_idx) +{ + struct bgx *bgx; + + bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx]; + if (bgx != NULL) + return (bgx->lmac_count); + + return (0); +} + +/* Returns the current link status of LMAC */ +void +bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status) +{ + struct bgx_link_status *link = (struct bgx_link_status *)status; + struct bgx *bgx; + struct lmac *lmac; + + bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx]; + if (bgx == NULL) + return; + + lmac = &bgx->lmac[lmacid]; + link->link_up = lmac->link_up; + link->duplex = lmac->last_duplex; + link->speed = lmac->last_speed; +} + +const uint8_t +*bgx_get_lmac_mac(int node, int bgx_idx, int lmacid) +{ + struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx]; + + if (bgx != NULL) + return (bgx->lmac[lmacid].mac); + + return (NULL); +} + +void +bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const uint8_t *mac) +{ + struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx]; + + if (bgx == NULL) + return; + + memcpy(bgx->lmac[lmacid].mac, mac, ETHER_ADDR_LEN); +} + +static void +bgx_sgmii_change_link_state(struct lmac *lmac) +{ + struct bgx *bgx = lmac->bgx; + uint64_t cmr_cfg; + uint64_t port_cfg = 0; + uint64_t misc_ctl = 0; + + cmr_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_CMRX_CFG); + cmr_cfg &= ~CMR_EN; + bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg); + + port_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG); + misc_ctl = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL); + + if (lmac->link_up) { + misc_ctl &= ~PCS_MISC_CTL_GMX_ENO; + port_cfg &= ~GMI_PORT_CFG_DUPLEX; + port_cfg |= (lmac->last_duplex << 2); + } else { + misc_ctl |= PCS_MISC_CTL_GMX_ENO; + } + + switch (lmac->last_speed) { + case 10: + port_cfg &= ~GMI_PORT_CFG_SPEED; /* speed 0 */ + port_cfg |= GMI_PORT_CFG_SPEED_MSB; /* speed_msb 1 */ + port_cfg &= ~GMI_PORT_CFG_SLOT_TIME; /* slottime 0 */ + misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK; + misc_ctl |= 50; /* samp_pt */ + bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 64); + bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_BURST, 0); + break; + case 100: + port_cfg &= ~GMI_PORT_CFG_SPEED; /* speed 0 */ + port_cfg &= ~GMI_PORT_CFG_SPEED_MSB; /* speed_msb 0 */ + port_cfg &= ~GMI_PORT_CFG_SLOT_TIME; /* slottime 0 */ + misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK; + misc_ctl |= 5; /* samp_pt */ + bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 64); + bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_BURST, 0); + break; + case 1000: + port_cfg |= GMI_PORT_CFG_SPEED; /* speed 1 */ + port_cfg &= ~GMI_PORT_CFG_SPEED_MSB; /* speed_msb 0 */ + port_cfg |= GMI_PORT_CFG_SLOT_TIME; /* slottime 1 */ + misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK; + misc_ctl |= 1; /* samp_pt */ + bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 512); + if (lmac->last_duplex) + bgx_reg_write(bgx, lmac->lmacid, + BGX_GMP_GMI_TXX_BURST, 0); + else + bgx_reg_write(bgx, lmac->lmacid, + BGX_GMP_GMI_TXX_BURST, 8192); + break; + default: + break; + } + bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL, misc_ctl); + bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, port_cfg); + + port_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG); + + /* renable lmac */ + cmr_cfg |= CMR_EN; + bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg); +} + +static void +bgx_lmac_handler(void *arg) +{ + struct lmac *lmac; + int link, duplex, speed; + int link_changed = 0; + int err; + + lmac = (struct lmac *)arg; + + err = LMAC_MEDIA_STATUS(lmac->phy_if_dev, lmac->lmacid, + &link, &duplex, &speed); + if (err != 0) + goto out; + + if (!link && lmac->last_link) + link_changed = -1; + + if (link && + (lmac->last_duplex != duplex || + lmac->last_link != link || + lmac->last_speed != speed)) { + link_changed = 1; + } + + lmac->last_link = link; + lmac->last_speed = speed; + lmac->last_duplex = duplex; + + if (!link_changed) + goto out; + + if (link_changed > 0) + lmac->link_up = true; + else + lmac->link_up = false; + + if (lmac->is_sgmii) + bgx_sgmii_change_link_state(lmac); + else + bgx_xaui_check_link(lmac); + +out: + callout_reset(&lmac->check_link, hz * 2, bgx_lmac_handler, lmac); +} + +uint64_t +bgx_get_rx_stats(int node, int bgx_idx, int lmac, int idx) +{ + struct bgx *bgx; + + bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx]; + if (bgx == NULL) + return (0); + + if (idx > 8) + lmac = (0); + return (bgx_reg_read(bgx, lmac, BGX_CMRX_RX_STAT0 + (idx * 8))); +} + +uint64_t +bgx_get_tx_stats(int node, int bgx_idx, int lmac, int idx) +{ + struct bgx *bgx; + + bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx]; + if (bgx == NULL) + return (0); + + return (bgx_reg_read(bgx, lmac, BGX_CMRX_TX_STAT0 + (idx * 8))); +} + +static void +bgx_flush_dmac_addrs(struct bgx *bgx, int lmac) +{ + uint64_t offset; + + while (bgx->lmac[lmac].dmac > 0) { + offset = ((bgx->lmac[lmac].dmac - 1) * sizeof(uint64_t)) + + (lmac * MAX_DMAC_PER_LMAC * sizeof(uint64_t)); + bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + offset, 0); + bgx->lmac[lmac].dmac--; + } +} + +void +bgx_add_dmac_addr(uint64_t dmac, int node, int bgx_idx, int lmac) +{ + uint64_t offset; + struct bgx *bgx; + +#ifdef BGX_IN_PROMISCUOUS_MODE + return; +#endif + + bgx_idx += node * MAX_BGX_PER_CN88XX; + bgx = bgx_vnic[bgx_idx]; + + if (!bgx) { + device_printf(bgx->dev, + "BGX%d not yet initialized, ignoring DMAC addition\n", + bgx_idx); + return; + } + + dmac = dmac | (1UL << 48) | ((uint64_t)lmac << 49); /* Enable DMAC */ + if (bgx->lmac[lmac].dmac == MAX_DMAC_PER_LMAC) { + device_printf(bgx->dev, + "Max DMAC filters for LMAC%d reached, ignoring\n", + lmac); + return; + } + + if (bgx->lmac[lmac].dmac == MAX_DMAC_PER_LMAC_TNS_BYPASS_MODE) + bgx->lmac[lmac].dmac = 1; + + offset = (bgx->lmac[lmac].dmac * sizeof(uint64_t)) + + (lmac * MAX_DMAC_PER_LMAC * sizeof(uint64_t)); + bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + offset, dmac); + bgx->lmac[lmac].dmac++; + + bgx_reg_write(bgx, lmac, BGX_CMRX_RX_DMAC_CTL, + (CAM_ACCEPT << 3) | (MCAST_MODE_CAM_FILTER << 1) | + (BCAST_ACCEPT << 0)); +} + +/* Configure BGX LMAC in internal loopback mode */ +void +bgx_lmac_internal_loopback(int node, int bgx_idx, + int lmac_idx, boolean_t enable) +{ + struct bgx *bgx; + struct lmac *lmac; + uint64_t cfg; + + bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx]; + if (bgx == NULL) + return; + + lmac = &bgx->lmac[lmac_idx]; + if (lmac->is_sgmii) { + cfg = bgx_reg_read(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL); + if (enable) + cfg |= PCS_MRX_CTL_LOOPBACK1; + else + cfg &= ~PCS_MRX_CTL_LOOPBACK1; + bgx_reg_write(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL, cfg); + } else { + cfg = bgx_reg_read(bgx, lmac_idx, BGX_SPUX_CONTROL1); + if (enable) + cfg |= SPU_CTL_LOOPBACK; + else + cfg &= ~SPU_CTL_LOOPBACK; + bgx_reg_write(bgx, lmac_idx, BGX_SPUX_CONTROL1, cfg); + } +} + +static int +bgx_lmac_sgmii_init(struct bgx *bgx, int lmacid) +{ + uint64_t cfg; + + bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_THRESH, 0x30); + /* max packet size */ + bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_RXX_JABBER, MAX_FRAME_SIZE); + + /* Disable frame alignment if using preamble */ + cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND); + if (cfg & 1) + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SGMII_CTL, 0); + + /* Enable lmac */ + bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN); + + /* PCS reset */ + bgx_reg_modify(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_RESET); + if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, + PCS_MRX_CTL_RESET, TRUE) != 0) { + device_printf(bgx->dev, "BGX PCS reset not completed\n"); + return (ENXIO); + } + + /* power down, reset autoneg, autoneg enable */ + cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MRX_CTL); + cfg &= ~PCS_MRX_CTL_PWR_DN; + cfg |= (PCS_MRX_CTL_RST_AN | PCS_MRX_CTL_AN_EN); + bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, cfg); + + if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_STATUS, + PCS_MRX_STATUS_AN_CPT, FALSE) != 0) { + device_printf(bgx->dev, "BGX AN_CPT not completed\n"); + return (ENXIO); + } + + return (0); +} + +static int +bgx_lmac_xaui_init(struct bgx *bgx, int lmacid, int lmac_type) +{ + uint64_t cfg; + + /* Reset SPU */ + bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET); + if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, + SPU_CTL_RESET, TRUE) != 0) { + device_printf(bgx->dev, "BGX SPU reset not completed\n"); + return (ENXIO); + } + + /* Disable LMAC */ + cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); + cfg &= ~CMR_EN; + bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); + + bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER); + /* Set interleaved running disparity for RXAUI */ + if (bgx->lmac_type != BGX_MODE_RXAUI) { + bgx_reg_modify(bgx, lmacid, + BGX_SPUX_MISC_CONTROL, SPU_MISC_CTL_RX_DIS); + } else { + bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL, + SPU_MISC_CTL_RX_DIS | SPU_MISC_CTL_INTLV_RDISP); + } + + /* clear all interrupts */ + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_INT); + bgx_reg_write(bgx, lmacid, BGX_SMUX_RX_INT, cfg); + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_INT); + bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_INT, cfg); + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT); + bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg); + + if (bgx->use_training) { + bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LP_CUP, 0x00); + bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_CUP, 0x00); + bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_REP, 0x00); + /* training enable */ + bgx_reg_modify(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL, + SPU_PMD_CRTL_TRAIN_EN); + } + + /* Append FCS to each packet */ + bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, SMU_TX_APPEND_FCS_D); + + /* Disable forward error correction */ + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_FEC_CONTROL); + cfg &= ~SPU_FEC_CTL_FEC_EN; + bgx_reg_write(bgx, lmacid, BGX_SPUX_FEC_CONTROL, cfg); + + /* Disable autoneg */ + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_CONTROL); + cfg = cfg & ~(SPU_AN_CTL_AN_EN | SPU_AN_CTL_XNP_EN); + bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_CONTROL, cfg); + + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_ADV); + if (bgx->lmac_type == BGX_MODE_10G_KR) + cfg |= (1 << 23); + else if (bgx->lmac_type == BGX_MODE_40G_KR) + cfg |= (1 << 24); + else + cfg &= ~((1 << 23) | (1 << 24)); + cfg = cfg & (~((1UL << 25) | (1UL << 22) | (1UL << 12))); + bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_ADV, cfg); + + cfg = bgx_reg_read(bgx, 0, BGX_SPU_DBG_CONTROL); + cfg &= ~SPU_DBG_CTL_AN_ARB_LINK_CHK_EN; + bgx_reg_write(bgx, 0, BGX_SPU_DBG_CONTROL, cfg); + + /* Enable lmac */ + bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN); + + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_CONTROL1); + cfg &= ~SPU_CTL_LOW_POWER; + bgx_reg_write(bgx, lmacid, BGX_SPUX_CONTROL1, cfg); + + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_CTL); + cfg &= ~SMU_TX_CTL_UNI_EN; + cfg |= SMU_TX_CTL_DIC_EN; + bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_CTL, cfg); + + /* take lmac_count into account */ + bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_THRESH, (0x100 - 1)); + /* max packet size */ + bgx_reg_modify(bgx, lmacid, BGX_SMUX_RX_JABBER, MAX_FRAME_SIZE); + + return (0); +} + +static int +bgx_xaui_check_link(struct lmac *lmac) +{ + struct bgx *bgx = lmac->bgx; + int lmacid = lmac->lmacid; + int lmac_type = bgx->lmac_type; + uint64_t cfg; + + bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL, SPU_MISC_CTL_RX_DIS); + if (bgx->use_training) { + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT); + if ((cfg & (1UL << 13)) == 0) { + cfg = (1UL << 13) | (1UL << 14); + bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg); + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL); + cfg |= (1UL << 0); + bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL, cfg); + return (ENXIO); + } + } + + /* wait for PCS to come out of reset */ + if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, + SPU_CTL_RESET, TRUE) != 0) { + device_printf(bgx->dev, "BGX SPU reset not completed\n"); + return (ENXIO); + } + + if ((lmac_type == BGX_MODE_10G_KR) || (lmac_type == BGX_MODE_XFI) || + (lmac_type == BGX_MODE_40G_KR) || (lmac_type == BGX_MODE_XLAUI)) { + if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BR_STATUS1, + SPU_BR_STATUS_BLK_LOCK, FALSE)) { + device_printf(bgx->dev, + "SPU_BR_STATUS_BLK_LOCK not completed\n"); + return (ENXIO); + } + } else { + if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BX_STATUS, + SPU_BX_STATUS_RX_ALIGN, FALSE) != 0) { + device_printf(bgx->dev, + "SPU_BX_STATUS_RX_ALIGN not completed\n"); + return (ENXIO); + } + } + + /* Clear rcvflt bit (latching high) and read it back */ + bgx_reg_modify(bgx, lmacid, BGX_SPUX_STATUS2, SPU_STATUS2_RCVFLT); + if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) { + device_printf(bgx->dev, "Receive fault, retry training\n"); + if (bgx->use_training) { + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT); + if ((cfg & (1UL << 13)) == 0) { + cfg = (1UL << 13) | (1UL << 14); + bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg); + cfg = bgx_reg_read(bgx, lmacid, + BGX_SPUX_BR_PMD_CRTL); + cfg |= (1UL << 0); + bgx_reg_write(bgx, lmacid, + BGX_SPUX_BR_PMD_CRTL, cfg); + return (ENXIO); + } + } + return (ENXIO); + } + + /* Wait for MAC RX to be ready */ + if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_RX_CTL, + SMU_RX_CTL_STATUS, TRUE) != 0) { + device_printf(bgx->dev, "SMU RX link not okay\n"); + return (ENXIO); + } + + /* Wait for BGX RX to be idle */ + if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, + SMU_CTL_RX_IDLE, FALSE) != 0) { + device_printf(bgx->dev, "SMU RX not idle\n"); + return (ENXIO); + } + + /* Wait for BGX TX to be idle */ + if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, + SMU_CTL_TX_IDLE, FALSE) != 0) { + device_printf(bgx->dev, "SMU TX not idle\n"); + return (ENXIO); + } + + if ((bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & + SPU_STATUS2_RCVFLT) != 0) { + device_printf(bgx->dev, "Receive fault\n"); + return (ENXIO); + } + + /* Receive link is latching low. Force it high and verify it */ + bgx_reg_modify(bgx, lmacid, BGX_SPUX_STATUS1, SPU_STATUS1_RCV_LNK); + if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_STATUS1, + SPU_STATUS1_RCV_LNK, FALSE) != 0) { + device_printf(bgx->dev, "SPU receive link down\n"); + return (ENXIO); + } + + cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_MISC_CONTROL); + cfg &= ~SPU_MISC_CTL_RX_DIS; + bgx_reg_write(bgx, lmacid, BGX_SPUX_MISC_CONTROL, cfg); + return (0); +} + +static void +bgx_poll_for_link(void *arg) +{ + struct lmac *lmac; + uint64_t link; + + lmac = (struct lmac *)arg; + + /* Receive link is latching low. Force it high and verify it */ + bgx_reg_modify(lmac->bgx, lmac->lmacid, + BGX_SPUX_STATUS1, SPU_STATUS1_RCV_LNK); + bgx_poll_reg(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1, + SPU_STATUS1_RCV_LNK, false); + + link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1); + if (link & SPU_STATUS1_RCV_LNK) { + lmac->link_up = 1; + if (lmac->bgx->lmac_type == BGX_MODE_XLAUI) + lmac->last_speed = 40000; + else + lmac->last_speed = 10000; + lmac->last_duplex = 1; + } else { + lmac->link_up = 0; + } + + if (lmac->last_link != lmac->link_up) { + lmac->last_link = lmac->link_up; + if (lmac->link_up) + bgx_xaui_check_link(lmac); + } + + callout_reset(&lmac->check_link, hz * 2, bgx_poll_for_link, lmac); +} + +static int +bgx_lmac_enable(struct bgx *bgx, uint8_t lmacid) +{ + uint64_t __unused dmac_bcast = (1UL << 48) - 1; + struct lmac *lmac; + uint64_t cfg; + + lmac = &bgx->lmac[lmacid]; + lmac->bgx = bgx; + + if (bgx->lmac_type == BGX_MODE_SGMII) { + lmac->is_sgmii = 1; + if (bgx_lmac_sgmii_init(bgx, lmacid) != 0) + return -1; + } else { + lmac->is_sgmii = 0; + if (bgx_lmac_xaui_init(bgx, lmacid, bgx->lmac_type)) + return -1; + } + + if (lmac->is_sgmii) { + cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND); + cfg |= ((1UL << 2) | (1UL << 1)); /* FCS and PAD */ + bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND, cfg); + bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_MIN_PKT, 60 - 1); + } else { + cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_APPEND); + cfg |= ((1UL << 2) | (1UL << 1)); /* FCS and PAD */ + bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, cfg); + bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_MIN_PKT, 60 + 4); + } + + /* Enable lmac */ + bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, + CMR_EN | CMR_PKT_RX_EN | CMR_PKT_TX_EN); + + /* Restore default cfg, incase low level firmware changed it */ + bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, 0x03); + + /* Add broadcast MAC into all LMAC's DMAC filters */ + bgx_add_dmac_addr(dmac_bcast, 0, bgx->bgx_id, lmacid); + + if ((bgx->lmac_type != BGX_MODE_XFI) && + (bgx->lmac_type != BGX_MODE_XAUI) && + (bgx->lmac_type != BGX_MODE_XLAUI) && + (bgx->lmac_type != BGX_MODE_40G_KR) && + (bgx->lmac_type != BGX_MODE_10G_KR)) { + if (lmac->phy_if_dev == NULL) { + device_printf(bgx->dev, + "LMAC%d missing interface to PHY\n", lmacid); + return (ENXIO); + } + + if (LMAC_PHY_CONNECT(lmac->phy_if_dev, lmac->phyaddr, + lmacid) != 0) { + device_printf(bgx->dev, + "LMAC%d could not connect to PHY\n", lmacid); + return (ENXIO); + } + mtx_init(&lmac->check_link_mtx, "BGX link poll", NULL, MTX_DEF); + callout_init_mtx(&lmac->check_link, &lmac->check_link_mtx, 0); + mtx_lock(&lmac->check_link_mtx); + bgx_lmac_handler(lmac); + mtx_unlock(&lmac->check_link_mtx); + } else { + mtx_init(&lmac->check_link_mtx, "BGX link poll", NULL, MTX_DEF); + callout_init_mtx(&lmac->check_link, &lmac->check_link_mtx, 0); + mtx_lock(&lmac->check_link_mtx); + bgx_poll_for_link(lmac); + mtx_unlock(&lmac->check_link_mtx); + } + + return (0); +} + +static void +bgx_lmac_disable(struct bgx *bgx, uint8_t lmacid) +{ + struct lmac *lmac; + uint64_t cmrx_cfg; + + lmac = &bgx->lmac[lmacid]; + + /* Stop callout */ + callout_drain(&lmac->check_link); + mtx_destroy(&lmac->check_link_mtx); + + cmrx_cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); + cmrx_cfg &= ~(1 << 15); + bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cmrx_cfg); + bgx_flush_dmac_addrs(bgx, lmacid); + + if ((bgx->lmac_type != BGX_MODE_XFI) && + (bgx->lmac_type != BGX_MODE_XLAUI) && + (bgx->lmac_type != BGX_MODE_40G_KR) && + (bgx->lmac_type != BGX_MODE_10G_KR)) { + if (lmac->phy_if_dev == NULL) { + device_printf(bgx->dev, + "LMAC%d missing interface to PHY\n", lmacid); + return; + } + if (LMAC_PHY_DISCONNECT(lmac->phy_if_dev, lmac->phyaddr, + lmacid) != 0) { + device_printf(bgx->dev, + "LMAC%d could not disconnect PHY\n", lmacid); + return; + } + lmac->phy_if_dev = NULL; + } +} + +static void +bgx_set_num_ports(struct bgx *bgx) +{ + uint64_t lmac_count; + + switch (bgx->qlm_mode) { + case QLM_MODE_SGMII: + bgx->lmac_count = 4; + bgx->lmac_type = BGX_MODE_SGMII; + bgx->lane_to_sds = 0; + break; + case QLM_MODE_XAUI_1X4: + bgx->lmac_count = 1; + bgx->lmac_type = BGX_MODE_XAUI; + bgx->lane_to_sds = 0xE4; + break; + case QLM_MODE_RXAUI_2X2: + bgx->lmac_count = 2; + bgx->lmac_type = BGX_MODE_RXAUI; + bgx->lane_to_sds = 0xE4; + break; + case QLM_MODE_XFI_4X1: + bgx->lmac_count = 4; + bgx->lmac_type = BGX_MODE_XFI; + bgx->lane_to_sds = 0; + break; + case QLM_MODE_XLAUI_1X4: + bgx->lmac_count = 1; + bgx->lmac_type = BGX_MODE_XLAUI; + bgx->lane_to_sds = 0xE4; + break; + case QLM_MODE_10G_KR_4X1: + bgx->lmac_count = 4; + bgx->lmac_type = BGX_MODE_10G_KR; + bgx->lane_to_sds = 0; + bgx->use_training = 1; + break; + case QLM_MODE_40G_KR4_1X4: + bgx->lmac_count = 1; + bgx->lmac_type = BGX_MODE_40G_KR; + bgx->lane_to_sds = 0xE4; + bgx->use_training = 1; + break; + default: + bgx->lmac_count = 0; + break; + } + + /* + * Check if low level firmware has programmed LMAC count + * based on board type, if yes consider that otherwise + * the default static values + */ + lmac_count = bgx_reg_read(bgx, 0, BGX_CMR_RX_LMACS) & 0x7; + if (lmac_count != 4) + bgx->lmac_count = lmac_count; +} + +static void +bgx_init_hw(struct bgx *bgx) +{ + int i; + + bgx_set_num_ports(bgx); + + bgx_reg_modify(bgx, 0, BGX_CMR_GLOBAL_CFG, CMR_GLOBAL_CFG_FCS_STRIP); + if (bgx_reg_read(bgx, 0, BGX_CMR_BIST_STATUS)) + device_printf(bgx->dev, "BGX%d BIST failed\n", bgx->bgx_id); + + /* Set lmac type and lane2serdes mapping */ + for (i = 0; i < bgx->lmac_count; i++) { + if (bgx->lmac_type == BGX_MODE_RXAUI) { + if (i) + bgx->lane_to_sds = 0x0e; + else + bgx->lane_to_sds = 0x04; + bgx_reg_write(bgx, i, BGX_CMRX_CFG, + (bgx->lmac_type << 8) | bgx->lane_to_sds); + continue; + } + bgx_reg_write(bgx, i, BGX_CMRX_CFG, + (bgx->lmac_type << 8) | (bgx->lane_to_sds + i)); + bgx->lmac[i].lmacid_bd = lmac_count; + lmac_count++; + } + + bgx_reg_write(bgx, 0, BGX_CMR_TX_LMACS, bgx->lmac_count); + bgx_reg_write(bgx, 0, BGX_CMR_RX_LMACS, bgx->lmac_count); + + /* Set the backpressure AND mask */ + for (i = 0; i < bgx->lmac_count; i++) { + bgx_reg_modify(bgx, 0, BGX_CMR_CHAN_MSK_AND, + ((1UL << MAX_BGX_CHANS_PER_LMAC) - 1) << + (i * MAX_BGX_CHANS_PER_LMAC)); + } + + /* Disable all MAC filtering */ + for (i = 0; i < RX_DMAC_COUNT; i++) + bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + (i * 8), 0x00); + + /* Disable MAC steering (NCSI traffic) */ + for (i = 0; i < RX_TRAFFIC_STEER_RULE_COUNT; i++) + bgx_reg_write(bgx, 0, BGX_CMR_RX_STREERING + (i * 8), 0x00); +} + +static void +bgx_get_qlm_mode(struct bgx *bgx) +{ + device_t dev = bgx->dev;; + int lmac_type; + int train_en; + + /* Read LMAC0 type to figure out QLM mode + * This is configured by low level firmware + */ + lmac_type = bgx_reg_read(bgx, 0, BGX_CMRX_CFG); + lmac_type = (lmac_type >> 8) & 0x07; + + train_en = bgx_reg_read(bgx, 0, BGX_SPUX_BR_PMD_CRTL) & + SPU_PMD_CRTL_TRAIN_EN; + + switch (lmac_type) { + case BGX_MODE_SGMII: + bgx->qlm_mode = QLM_MODE_SGMII; + if (bootverbose) { + device_printf(dev, "BGX%d QLM mode: SGMII\n", + bgx->bgx_id); + } + break; + case BGX_MODE_XAUI: + bgx->qlm_mode = QLM_MODE_XAUI_1X4; + if (bootverbose) { + device_printf(dev, "BGX%d QLM mode: XAUI\n", + bgx->bgx_id); + } + break; + case BGX_MODE_RXAUI: + bgx->qlm_mode = QLM_MODE_RXAUI_2X2; + if (bootverbose) { + device_printf(dev, "BGX%d QLM mode: RXAUI\n", + bgx->bgx_id); + } + break; + case BGX_MODE_XFI: + if (!train_en) { + bgx->qlm_mode = QLM_MODE_XFI_4X1; + if (bootverbose) { + device_printf(dev, "BGX%d QLM mode: XFI\n", + bgx->bgx_id); + } + } else { + bgx->qlm_mode = QLM_MODE_10G_KR_4X1; + if (bootverbose) { + device_printf(dev, "BGX%d QLM mode: 10G_KR\n", + bgx->bgx_id); + } + } + break; + case BGX_MODE_XLAUI: + if (!train_en) { + bgx->qlm_mode = QLM_MODE_XLAUI_1X4; + if (bootverbose) { + device_printf(dev, "BGX%d QLM mode: XLAUI\n", + bgx->bgx_id); + } + } else { + bgx->qlm_mode = QLM_MODE_40G_KR4_1X4; + if (bootverbose) { + device_printf(dev, "BGX%d QLM mode: 40G_KR4\n", + bgx->bgx_id); + } + } + break; + default: + bgx->qlm_mode = QLM_MODE_SGMII; + if (bootverbose) { + device_printf(dev, "BGX%d QLM default mode: SGMII\n", + bgx->bgx_id); + } + } +} + +static int +bgx_init_phy(struct bgx *bgx) +{ + int err; + + /* By default we fail */ + err = ENXIO; +#ifdef FDT + err = bgx_fdt_init_phy(bgx); +#endif +#ifdef ACPI + if (err != 0) { + /* ARM64TODO: Add ACPI function here */ + } +#endif + return (err); +} diff --git a/sys/dev/vnic/thunder_bgx.h b/sys/dev/vnic/thunder_bgx.h new file mode 100644 index 00000000000..374f44c3b4e --- /dev/null +++ b/sys/dev/vnic/thunder_bgx.h @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2015 Cavium Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef THUNDER_BGX_H +#define THUNDER_BGX_H + +#define MAX_BGX_THUNDER 8 /* Max 4 nodes, 2 per node */ +#define MAX_BGX_PER_CN88XX 2 +#define MAX_LMAC_PER_BGX 4 +#define MAX_BGX_CHANS_PER_LMAC 16 +#define MAX_DMAC_PER_LMAC 8 +#define MAX_FRAME_SIZE 9216 + +#define MAX_DMAC_PER_LMAC_TNS_BYPASS_MODE 2 + +#define MAX_LMAC (MAX_BGX_PER_CN88XX * MAX_LMAC_PER_BGX) + +/* Registers */ +#define BGX_CMRX_CFG 0x00 +#define CMR_PKT_TX_EN (1UL << 13) +#define CMR_PKT_RX_EN (1UL << 14) +#define CMR_EN (1UL << 15) +#define BGX_CMR_GLOBAL_CFG 0x08 +#define CMR_GLOBAL_CFG_FCS_STRIP (1UL << 6) +#define BGX_CMRX_RX_ID_MAP 0x60 +#define BGX_CMRX_RX_STAT0 0x70 +#define BGX_CMRX_RX_STAT1 0x78 +#define BGX_CMRX_RX_STAT2 0x80 +#define BGX_CMRX_RX_STAT3 0x88 +#define BGX_CMRX_RX_STAT4 0x90 +#define BGX_CMRX_RX_STAT5 0x98 +#define BGX_CMRX_RX_STAT6 0xA0 +#define BGX_CMRX_RX_STAT7 0xA8 +#define BGX_CMRX_RX_STAT8 0xB0 +#define BGX_CMRX_RX_STAT9 0xB8 +#define BGX_CMRX_RX_STAT10 0xC0 +#define BGX_CMRX_RX_BP_DROP 0xC8 +#define BGX_CMRX_RX_DMAC_CTL 0x0E8 +#define BGX_CMR_RX_DMACX_CAM 0x200 +#define RX_DMACX_CAM_EN (1UL << 48) +#define RX_DMACX_CAM_LMACID(x) (x << 49) +#define RX_DMAC_COUNT 32 +#define BGX_CMR_RX_STREERING 0x300 +#define RX_TRAFFIC_STEER_RULE_COUNT 8 +#define BGX_CMR_CHAN_MSK_AND 0x450 +#define BGX_CMR_BIST_STATUS 0x460 +#define BGX_CMR_RX_LMACS 0x468 +#define BGX_CMRX_TX_STAT0 0x600 +#define BGX_CMRX_TX_STAT1 0x608 +#define BGX_CMRX_TX_STAT2 0x610 +#define BGX_CMRX_TX_STAT3 0x618 +#define BGX_CMRX_TX_STAT4 0x620 +#define BGX_CMRX_TX_STAT5 0x628 +#define BGX_CMRX_TX_STAT6 0x630 +#define BGX_CMRX_TX_STAT7 0x638 +#define BGX_CMRX_TX_STAT8 0x640 +#define BGX_CMRX_TX_STAT9 0x648 +#define BGX_CMRX_TX_STAT10 0x650 +#define BGX_CMRX_TX_STAT11 0x658 +#define BGX_CMRX_TX_STAT12 0x660 +#define BGX_CMRX_TX_STAT13 0x668 +#define BGX_CMRX_TX_STAT14 0x670 +#define BGX_CMRX_TX_STAT15 0x678 +#define BGX_CMRX_TX_STAT16 0x680 +#define BGX_CMRX_TX_STAT17 0x688 +#define BGX_CMR_TX_LMACS 0x1000 + +#define BGX_SPUX_CONTROL1 0x10000 +#define SPU_CTL_LOW_POWER (1UL << 11) +#define SPU_CTL_LOOPBACK (1UL << 14) +#define SPU_CTL_RESET (1UL << 15) +#define BGX_SPUX_STATUS1 0x10008 +#define SPU_STATUS1_RCV_LNK (1UL << 2) +#define BGX_SPUX_STATUS2 0x10020 +#define SPU_STATUS2_RCVFLT (1UL << 10) +#define BGX_SPUX_BX_STATUS 0x10028 +#define SPU_BX_STATUS_RX_ALIGN (1UL << 12) +#define BGX_SPUX_BR_STATUS1 0x10030 +#define SPU_BR_STATUS_BLK_LOCK (1UL << 0) +#define SPU_BR_STATUS_RCV_LNK (1UL << 12) +#define BGX_SPUX_BR_PMD_CRTL 0x10068 +#define SPU_PMD_CRTL_TRAIN_EN (1UL << 1) +#define BGX_SPUX_BR_PMD_LP_CUP 0x10078 +#define BGX_SPUX_BR_PMD_LD_CUP 0x10088 +#define BGX_SPUX_BR_PMD_LD_REP 0x10090 +#define BGX_SPUX_FEC_CONTROL 0x100A0 +#define SPU_FEC_CTL_FEC_EN (1UL << 0) +#define SPU_FEC_CTL_ERR_EN (1UL << 1) +#define BGX_SPUX_AN_CONTROL 0x100C8 +#define SPU_AN_CTL_AN_EN (1UL << 12) +#define SPU_AN_CTL_XNP_EN (1UL << 13) +#define BGX_SPUX_AN_ADV 0x100D8 +#define BGX_SPUX_MISC_CONTROL 0x10218 +#define SPU_MISC_CTL_INTLV_RDISP (1UL << 10) +#define SPU_MISC_CTL_RX_DIS (1UL << 12) +#define BGX_SPUX_INT 0x10220 /* +(0..3) << 20 */ +#define BGX_SPUX_INT_W1S 0x10228 +#define BGX_SPUX_INT_ENA_W1C 0x10230 +#define BGX_SPUX_INT_ENA_W1S 0x10238 +#define BGX_SPU_DBG_CONTROL 0x10300 +#define SPU_DBG_CTL_AN_ARB_LINK_CHK_EN (1UL << 18) +#define SPU_DBG_CTL_AN_NONCE_MCT_DIS (1UL << 29) + +#define BGX_SMUX_RX_INT 0x20000 +#define BGX_SMUX_RX_JABBER 0x20030 +#define BGX_SMUX_RX_CTL 0x20048 +#define SMU_RX_CTL_STATUS (3UL << 0) +#define BGX_SMUX_TX_APPEND 0x20100 +#define SMU_TX_APPEND_FCS_D (1UL << 2) +#define BGX_SMUX_TX_MIN_PKT 0x20118 +#define BGX_SMUX_TX_INT 0x20140 +#define BGX_SMUX_TX_CTL 0x20178 +#define SMU_TX_CTL_DIC_EN (1UL << 0) +#define SMU_TX_CTL_UNI_EN (1UL << 1) +#define SMU_TX_CTL_LNK_STATUS (3UL << 4) +#define BGX_SMUX_TX_THRESH 0x20180 +#define BGX_SMUX_CTL 0x20200 +#define SMU_CTL_RX_IDLE (1UL << 0) +#define SMU_CTL_TX_IDLE (1UL << 1) + +#define BGX_GMP_PCS_MRX_CTL 0x30000 +#define PCS_MRX_CTL_RST_AN (1UL << 9) +#define PCS_MRX_CTL_PWR_DN (1UL << 11) +#define PCS_MRX_CTL_AN_EN (1UL << 12) +#define PCS_MRX_CTL_LOOPBACK1 (1UL << 14) +#define PCS_MRX_CTL_RESET (1UL << 15) +#define BGX_GMP_PCS_MRX_STATUS 0x30008 +#define PCS_MRX_STATUS_AN_CPT (1UL << 5) +#define BGX_GMP_PCS_ANX_AN_RESULTS 0x30020 +#define BGX_GMP_PCS_SGM_AN_ADV 0x30068 +#define BGX_GMP_PCS_MISCX_CTL 0x30078 +#define PCS_MISC_CTL_GMX_ENO (1UL << 11) +#define PCS_MISC_CTL_SAMP_PT_MASK 0x7FUL +#define BGX_GMP_GMI_PRTX_CFG 0x38020 +#define GMI_PORT_CFG_SPEED (1UL << 1) +#define GMI_PORT_CFG_DUPLEX (1UL << 2) +#define GMI_PORT_CFG_SLOT_TIME (1UL << 3) +#define GMI_PORT_CFG_SPEED_MSB (1UL << 8) +#define BGX_GMP_GMI_RXX_JABBER 0x38038 +#define BGX_GMP_GMI_TXX_THRESH 0x38210 +#define BGX_GMP_GMI_TXX_APPEND 0x38218 +#define BGX_GMP_GMI_TXX_SLOT 0x38220 +#define BGX_GMP_GMI_TXX_BURST 0x38228 +#define BGX_GMP_GMI_TXX_MIN_PKT 0x38240 +#define BGX_GMP_GMI_TXX_SGMII_CTL 0x38300 + +#define BGX_MSIX_VEC_0_29_ADDR 0x400000 /* +(0..29) << 4 */ +#define BGX_MSIX_VEC_0_29_CTL 0x400008 +#define BGX_MSIX_PBA_0 0x4F0000 + +/* MSI-X interrupts */ +#define BGX_MSIX_VECTORS 30 +#define BGX_LMAC_VEC_OFFSET 7 +#define BGX_MSIX_VEC_SHIFT 4 + +#define CMRX_INT 0 +#define SPUX_INT 1 +#define SMUX_RX_INT 2 +#define SMUX_TX_INT 3 +#define GMPX_PCS_INT 4 +#define GMPX_GMI_RX_INT 5 +#define GMPX_GMI_TX_INT 6 +#define CMR_MEM_INT 28 +#define SPU_MEM_INT 29 + +#define LMAC_INTR_LINK_UP (1 << 0) +#define LMAC_INTR_LINK_DOWN (1 << 1) + +/* RX_DMAC_CTL configuration*/ +enum MCAST_MODE { + MCAST_MODE_REJECT, + MCAST_MODE_ACCEPT, + MCAST_MODE_CAM_FILTER, + RSVD +}; + +#define BCAST_ACCEPT 1 +#define CAM_ACCEPT 1 + +void octeon_mdiobus_force_mod_depencency(void); +void bgx_add_dmac_addr(uint64_t dmac, int node, int bgx_idx, int lmac); +unsigned bgx_get_map(int node); +int bgx_get_lmac_count(int node, int bgx); +const uint8_t *bgx_get_lmac_mac(int node, int bgx_idx, int lmacid); +void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const uint8_t *mac); +void bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status); +void bgx_lmac_internal_loopback(int node, int bgx_idx, + int lmac_idx, boolean_t enable); +uint64_t bgx_get_rx_stats(int node, int bgx_idx, int lmac, int idx); +uint64_t bgx_get_tx_stats(int node, int bgx_idx, int lmac, int idx); +#define BGX_RX_STATS_COUNT 11 +#define BGX_TX_STATS_COUNT 18 + +struct bgx_stats { + uint64_t rx_stats[BGX_RX_STATS_COUNT]; + uint64_t tx_stats[BGX_TX_STATS_COUNT]; +}; + +#define BGX_IN_PROMISCUOUS_MODE 1 + +enum LMAC_TYPE { + BGX_MODE_SGMII = 0, /* 1 lane, 1.250 Gbaud */ + BGX_MODE_XAUI = 1, /* 4 lanes, 3.125 Gbaud */ + BGX_MODE_DXAUI = 1, /* 4 lanes, 6.250 Gbaud */ + BGX_MODE_RXAUI = 2, /* 2 lanes, 6.250 Gbaud */ + BGX_MODE_XFI = 3, /* 1 lane, 10.3125 Gbaud */ + BGX_MODE_XLAUI = 4, /* 4 lanes, 10.3125 Gbaud */ + BGX_MODE_10G_KR = 3,/* 1 lane, 10.3125 Gbaud */ + BGX_MODE_40G_KR = 4,/* 4 lanes, 10.3125 Gbaud */ +}; + +enum qlm_mode { + QLM_MODE_SGMII, /* SGMII, each lane independent */ + QLM_MODE_XAUI_1X4, /* 1 XAUI or DXAUI, 4 lanes */ + QLM_MODE_RXAUI_2X2, /* 2 RXAUI, 2 lanes each */ + QLM_MODE_XFI_4X1, /* 4 XFI, 1 lane each */ + QLM_MODE_XLAUI_1X4, /* 1 XLAUI, 4 lanes each */ + QLM_MODE_10G_KR_4X1, /* 4 10GBASE-KR, 1 lane each */ + QLM_MODE_40G_KR4_1X4, /* 1 40GBASE-KR4, 4 lanes each */ +}; + +#endif /* THUNDER_BGX_H */ diff --git a/sys/dev/vnic/thunder_bgx_fdt.c b/sys/dev/vnic/thunder_bgx_fdt.c new file mode 100644 index 00000000000..1560262c8cc --- /dev/null +++ b/sys/dev/vnic/thunder_bgx_fdt.c @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2015 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under + * the sponsorship of the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "thunder_bgx.h" +#include "thunder_bgx_var.h" + +#define CONN_TYPE_MAXLEN 16 +#define CONN_TYPE_OFFSET 2 + +int bgx_fdt_init_phy(struct bgx *); + +static void +bgx_fdt_get_macaddr(phandle_t phy, uint8_t *hwaddr) +{ + uint8_t addr[ETHER_ADDR_LEN]; + + if (OF_getprop(phy, "local-mac-address", addr, ETHER_ADDR_LEN) == -1) { + /* Missing MAC address should be marked by clearing it */ + memset(hwaddr, 0, ETHER_ADDR_LEN); + } else + memcpy(hwaddr, addr, ETHER_ADDR_LEN); +} + +static boolean_t +bgx_fdt_phy_mode_match(struct bgx *bgx, char *qlm_mode, size_t size) +{ + + size -= CONN_TYPE_OFFSET; + + switch (bgx->qlm_mode) { + case QLM_MODE_SGMII: + if (strncmp(&qlm_mode[CONN_TYPE_OFFSET], "sgmii", size) == 0) + return (TRUE); + break; + case QLM_MODE_XAUI_1X4: + if (strncmp(&qlm_mode[CONN_TYPE_OFFSET], "xaui", size) == 0) + return (TRUE); + if (strncmp(&qlm_mode[CONN_TYPE_OFFSET], "dxaui", size) == 0) + return (TRUE); + break; + case QLM_MODE_RXAUI_2X2: + if (strncmp(&qlm_mode[CONN_TYPE_OFFSET], "raui", size) == 0) + return (TRUE); + break; + case QLM_MODE_XFI_4X1: + if (strncmp(&qlm_mode[CONN_TYPE_OFFSET], "xfi", size) == 0) + return (TRUE); + break; + case QLM_MODE_XLAUI_1X4: + if (strncmp(&qlm_mode[CONN_TYPE_OFFSET], "xlaui", size) == 0) + return (TRUE); + break; + case QLM_MODE_10G_KR_4X1: + if (strncmp(&qlm_mode[CONN_TYPE_OFFSET], "xfi-10g-kr", size) == 0) + return (TRUE); + break; + case QLM_MODE_40G_KR4_1X4: + if (strncmp(&qlm_mode[CONN_TYPE_OFFSET], "xlaui-40g-kr", size) == 0) + return (TRUE); + break; + default: + return (FALSE); + } + + return (FALSE); +} + +int +bgx_fdt_init_phy(struct bgx *bgx) +{ + phandle_t node, child; + phandle_t phy, mdio; + uint8_t lmac; + char bgx_sel[6]; + char qlm_mode[CONN_TYPE_MAXLEN]; + const char *mac; + + (void)mac; + + lmac = 0; + /* Get BGX node from DT */ + snprintf(bgx_sel, 6, "/bgx%d", bgx->bgx_id); + node = OF_finddevice(bgx_sel); + if (node == 0 || node == -1) { + device_printf(bgx->dev, + "Could not find %s node in FDT\n", bgx_sel); + return (ENXIO); + } + + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + if (OF_getprop(child, "qlm-mode", qlm_mode, + sizeof(qlm_mode)) <= 0) { + /* Missing qlm-mode, skipping */ + continue; + } + + if (!bgx_fdt_phy_mode_match(bgx, qlm_mode, sizeof(qlm_mode))) { + /* + * Connection type not match with BGX mode. + */ + continue; + } + + if (OF_getencprop(child, "phy-handle", &phy, + sizeof(phy)) <= 0) { + if (bootverbose) { + device_printf(bgx->dev, + "No phy-handle in PHY node. Skipping...\n"); + } + continue; + } + + /* Acquire PHY address */ + phy = OF_node_from_xref(phy); + if (OF_getencprop(phy, "reg", &bgx->lmac[lmac].phyaddr, + sizeof(bgx->lmac[lmac].phyaddr)) <= 0) { + if (bootverbose) { + device_printf(bgx->dev, + "Could not retrieve PHY address\n"); + } + bgx->lmac[lmac].phyaddr = MII_PHY_ANY; + } + + /* + * Get PHY interface (MDIO bus) device. + * Driver must be already attached. + */ + mdio = OF_parent(phy); + bgx->lmac[lmac].phy_if_dev = + OF_device_from_xref(OF_xref_from_node(mdio)); + if (bgx->lmac[lmac].phy_if_dev == NULL) { + if (bootverbose) { + device_printf(bgx->dev, + "Could not find interface to PHY\n"); + } + continue; + } + + /* Get mac address from FDT */ + bgx_fdt_get_macaddr(phy, bgx->lmac[lmac].mac); + + bgx->lmac[lmac].lmacid = lmac; + lmac++; + if (lmac == MAX_LMAC_PER_BGX) + break; + } + if (lmac == 0) { + device_printf(bgx->dev, "Could not find matching PHY\n"); + return (ENXIO); + } + + return (0); +} diff --git a/tools/regression/posixshm/test.h b/sys/dev/vnic/thunder_bgx_var.h similarity index 60% rename from tools/regression/posixshm/test.h rename to sys/dev/vnic/thunder_bgx_var.h index 505679b1fdb..bfb1ef0ccbb 100644 --- a/tools/regression/posixshm/test.h +++ b/sys/dev/vnic/thunder_bgx_var.h @@ -1,7 +1,6 @@ -/*- - * Copyright (c) 2008 Yahoo!, Inc. +/* + * Copyright (C) 2015 Cavium Inc. * All rights reserved. - * Written by: John Baldwin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,9 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -28,32 +24,44 @@ * SUCH DAMAGE. * * $FreeBSD$ + * */ -#ifndef __TEST_H__ -#define __TEST_H__ +#ifndef __THUNDER_BGX_VAR_H__ +#define __THUNDER_BGX_VAR_H__ -#include - -struct regression_test { - void (*rt_function)(void); - const char *rt_name; +struct lmac { + struct bgx *bgx; + int dmac; + uint8_t mac[ETHER_ADDR_LEN]; + boolean_t link_up; + int lmacid; /* ID within BGX */ + int lmacid_bd; /* ID on board */ + device_t phy_if_dev; + int phyaddr; + unsigned int last_duplex; + unsigned int last_link; + unsigned int last_speed; + boolean_t is_sgmii; + struct callout check_link; + struct mtx check_link_mtx; }; -#define TEST(function, name) \ - static struct regression_test _regtest_##function = { \ - (function), \ - (name) \ - }; \ - DATA_SET(regression_tests_set, _regtest_##function) +struct bgx { + device_t dev; + struct resource * reg_base; -void fail(void); -void fail_err(const char *fmt, ...); -void pass(void); -void run_tests(void); -void skip(const char *reason); -void todo(const char *reason); + uint8_t bgx_id; + enum qlm_mode qlm_mode; + struct lmac lmac[MAX_LMAC_PER_BGX]; + int lmac_count; + int lmac_type; + int lane_to_sds; + int use_training; +}; -#define fail_errno(tag) fail_err("%s: %s", (tag), strerror(errno)) +#ifdef FDT +extern int bgx_fdt_init_phy(struct bgx *); +#endif -#endif /* !__TEST_H__ */ +#endif diff --git a/sys/dev/vnic/thunder_mdio.c b/sys/dev/vnic/thunder_mdio.c new file mode 100644 index 00000000000..81576238a80 --- /dev/null +++ b/sys/dev/vnic/thunder_mdio.c @@ -0,0 +1,512 @@ +/*- + * Copyright (c) 2015 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under + * the sponsorship of the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "thunder_mdio_var.h" + +#include "lmac_if.h" +#include "miibus_if.h" + +#define REG_BASE_RID 0 + +#define SMI_CMD 0x00 +#define SMI_CMD_PHY_REG_ADR_SHIFT (0) +#define SMI_CMD_PHY_REG_ADR_MASK (0x1FUL << SMI_CMD_PHY_REG_ADR_SHIFT) +#define SMI_CMD_PHY_ADR_SHIFT (8) +#define SMI_CMD_PHY_ADR_MASK (0x1FUL << SMI_CMD_PHY_ADR_SHIFT) +#define SMI_CMD_PHY_OP_MASK (0x3UL << 16) +#define SMI_CMD_PHY_OP_C22_READ (0x1UL << 16) +#define SMI_CMD_PHY_OP_C22_WRITE (0x0UL << 16) +#define SMI_CMD_PHY_OP_C45_READ (0x3UL << 16) +#define SMI_CMD_PHY_OP_C45_WRITE (0x1UL << 16) +#define SMI_CMD_PHY_OP_C45_ADDR (0x0UL << 16) + +#define SMI_WR_DAT 0x08 +#define SMI_WR_DAT_PENDING (1UL << 17) +#define SMI_WR_DAT_VAL (1UL << 16) +#define SMI_WR_DAT_DAT_MASK (0xFFFFUL << 0) + +#define SMI_RD_DAT 0x10 +#define SMI_RD_DAT_PENDING (1UL << 17) +#define SMI_RD_DAT_VAL (1UL << 16) +#define SMI_RD_DAT_DAT_MASK (0xFFFFUL << 0) + +#define SMI_CLK 0x18 +#define SMI_CLK_PREAMBLE (1UL << 12) +#define SMI_CLK_MODE (1UL << 24) + +#define SMI_EN 0x20 +#define SMI_EN_EN (1UL << 0) /* Enabele interface */ + +#define SMI_DRV_CTL 0x28 + +static int thunder_mdio_detach(device_t); + +static int thunder_mdio_read(device_t, int, int); +static int thunder_mdio_write(device_t, int, int, int); + +static int thunder_ifmedia_change_stub(struct ifnet *); +static void thunder_ifmedia_status_stub(struct ifnet *, struct ifmediareq *); + +static int thunder_mdio_media_status(device_t, int, int *, int *, int *); +static int thunder_mdio_media_change(device_t, int, int, int, int); +static int thunder_mdio_phy_connect(device_t, int, int); +static int thunder_mdio_phy_disconnect(device_t, int, int); + +static device_method_t thunder_mdio_methods[] = { + /* Device interface */ + DEVMETHOD(device_detach, thunder_mdio_detach), + /* LMAC interface */ + DEVMETHOD(lmac_media_status, thunder_mdio_media_status), + DEVMETHOD(lmac_media_change, thunder_mdio_media_change), + DEVMETHOD(lmac_phy_connect, thunder_mdio_phy_connect), + DEVMETHOD(lmac_phy_disconnect, thunder_mdio_phy_disconnect), + /* MII interface */ + DEVMETHOD(miibus_readreg, thunder_mdio_read), + DEVMETHOD(miibus_writereg, thunder_mdio_write), + + /* End */ + DEVMETHOD_END +}; + +DEFINE_CLASS_0(thunder_mdio, thunder_mdio_driver, thunder_mdio_methods, + sizeof(struct thunder_mdio_softc)); + +DRIVER_MODULE(miibus, thunder_mdio, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(thunder_mdio, ether, 1, 1, 1); +MODULE_DEPEND(thunder_mdio, miibus, 1, 1, 1); + +MALLOC_DEFINE(M_THUNDER_MDIO, "ThunderX MDIO", + "Cavium ThunderX MDIO dynamic memory"); + +#define MDIO_LOCK_INIT(sc, name) \ + mtx_init(&(sc)->mtx, name, NULL, MTX_DEF) + +#define MDIO_LOCK_DESTROY(sc) \ + mtx_destroy(&(sc)->mtx) + +#define MDIO_LOCK(sc) mtx_lock(&(sc)->mtx) +#define MDIO_UNLOCK(sc) mtx_unlock(&(sc)->mtx) + +#define MDIO_LOCK_ASSERT(sc) \ + mtx_assert(&(sc)->mtx, MA_OWNED) + + +#define mdio_reg_read(sc, reg) \ + bus_read_8((sc)->reg_base, (reg)) + +#define mdio_reg_write(sc, reg, val) \ + bus_write_8((sc)->reg_base, (reg), (val)) + +int +thunder_mdio_attach(device_t dev) +{ + struct thunder_mdio_softc *sc; + int rid; + + sc = device_get_softc(dev); + sc->dev = dev; + + /* Allocate memory resources */ + rid = REG_BASE_RID; + sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->reg_base == NULL) { + device_printf(dev, "Could not allocate memory\n"); + return (ENXIO); + } + + TAILQ_INIT(&sc->phy_desc_head); + MDIO_LOCK_INIT(sc, "ThunderX MDIO lock"); + + /* Enable SMI/MDIO interface */ + mdio_reg_write(sc, SMI_EN, SMI_EN_EN); + + return (0); +} + +static int +thunder_mdio_detach(device_t dev) +{ + struct thunder_mdio_softc *sc; + + sc = device_get_softc(dev); + + if (sc->reg_base != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID, + sc->reg_base); + } + + return (0); +} + +static __inline void +thunder_mdio_set_mode(struct thunder_mdio_softc *sc, + enum thunder_mdio_mode mode) +{ + uint64_t smi_clk; + + if (sc->mode == mode) + return; + + /* Set mode, IEEE CLAUSE 22 or IEEE CAUSE 45 */ + smi_clk = mdio_reg_read(sc, SMI_CLK); + if (mode == MODE_IEEE_C22) + smi_clk &= ~SMI_CLK_MODE; + else + smi_clk |= SMI_CLK_MODE; + /* Enable sending 32 bit preable on SMI transactions */ + smi_clk |= SMI_CLK_PREAMBLE; + /* Saved setings */ + mdio_reg_write(sc, SMI_CLK, smi_clk); + sc->mode = mode; +} + +static int +thunder_mdio_c45_addr(struct thunder_mdio_softc *sc, int phy, int reg) +{ + uint64_t smi_cmd, smi_wr_dat; + ssize_t timeout; + + thunder_mdio_set_mode(sc, MODE_IEEE_C45); + + /* Prepare data for transmission */ + mdio_reg_write(sc, SMI_WR_DAT, reg & SMI_WR_DAT_DAT_MASK); + /* + * Assemble command + */ + smi_cmd = 0; + /* Set opcode */ + smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE; + + /* Set PHY address */ + smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK); + /* Set PHY register offset */ + smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) & + SMI_CMD_PHY_REG_ADR_MASK); + + mdio_reg_write(sc, SMI_CMD, smi_cmd); + for (timeout = 1000; timeout > 0; timeout--) { + smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT); + if (smi_wr_dat & SMI_WR_DAT_PENDING) + DELAY(1000); + else + break; + } + + if (timeout <= 0) + return (EIO); + else { + /* Return 0 on success */ + return (0); + } +} + +static int +thunder_mdio_read(device_t dev, int phy, int reg) +{ + struct thunder_mdio_softc *sc; + uint64_t smi_cmd, smi_rd_dat; + ssize_t timeout; + int err; + + sc = device_get_softc(dev); + + /* XXX Always C22 - for <= 1Gbps only */ + thunder_mdio_set_mode(sc, MODE_IEEE_C22); + + /* + * Assemble command + */ + smi_cmd = 0; + /* Set opcode */ + if (sc->mode == MODE_IEEE_C22) + smi_cmd |= SMI_CMD_PHY_OP_C22_READ; + else { + smi_cmd |= SMI_CMD_PHY_OP_C45_READ; + err = thunder_mdio_c45_addr(sc, phy, reg); + if (err != 0) + return (err); + + reg = (reg >> 16) & 0x1F; + } + + /* Set PHY address */ + smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK); + /* Set PHY register offset */ + smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) & + SMI_CMD_PHY_REG_ADR_MASK); + + mdio_reg_write(sc, SMI_CMD, smi_cmd); + for (timeout = 1000; timeout > 0; timeout--) { + smi_rd_dat = mdio_reg_read(sc, SMI_RD_DAT); + if (smi_rd_dat & SMI_RD_DAT_PENDING) + DELAY(1000); + else + break; + } + + if (smi_rd_dat & SMI_RD_DAT_VAL) + return (smi_rd_dat & SMI_RD_DAT_DAT_MASK); + else { + /* Return 0 on error */ + return (0); + } +} + +static int +thunder_mdio_write(device_t dev, int phy, int reg, int data) +{ + struct thunder_mdio_softc *sc; + uint64_t smi_cmd, smi_wr_dat; + ssize_t timeout; + + sc = device_get_softc(dev); + + /* XXX Always C22 - for <= 1Gbps only */ + thunder_mdio_set_mode(sc, MODE_IEEE_C22); + + /* Prepare data for transmission */ + mdio_reg_write(sc, SMI_WR_DAT, data & SMI_WR_DAT_DAT_MASK); + /* + * Assemble command + */ + smi_cmd = 0; + /* Set opcode */ + if (sc->mode == MODE_IEEE_C22) + smi_cmd |= SMI_CMD_PHY_OP_C22_WRITE; + else + smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE; + + /* Set PHY address */ + smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK); + /* Set PHY register offset */ + smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) & + SMI_CMD_PHY_REG_ADR_MASK); + + mdio_reg_write(sc, SMI_CMD, smi_cmd); + for (timeout = 1000; timeout > 0; timeout--) { + smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT); + if (smi_wr_dat & SMI_WR_DAT_PENDING) + DELAY(1000); + else + break; + } + + if (timeout <= 0) + return (EIO); + else { + /* Return 0 on success */ + return (0); + } +} + +static int +thunder_ifmedia_change_stub(struct ifnet *ifp __unused) +{ + /* Will never be called by if_media */ + return (0); +} + +static void +thunder_ifmedia_status_stub(struct ifnet *ifp __unused, struct ifmediareq + *ifmr __unused) +{ + /* Will never be called by if_media */ +} + +static __inline struct phy_desc * +get_phy_desc(struct thunder_mdio_softc *sc, int lmacid) +{ + struct phy_desc *pd = NULL; + + MDIO_LOCK_ASSERT(sc); + TAILQ_FOREACH(pd, &sc->phy_desc_head, phy_desc_list) { + if (pd->lmacid == lmacid) + break; + } + + return (pd); +} +static int +thunder_mdio_media_status(device_t dev, int lmacid, int *link, int *duplex, + int *speed) +{ + struct thunder_mdio_softc *sc; + struct mii_data *mii_sc; + struct phy_desc *pd; + + sc = device_get_softc(dev); + + MDIO_LOCK(sc); + pd = get_phy_desc(sc, lmacid); + if (pd == NULL) { + /* Panic when invariants are enabled, fail otherwise. */ + KASSERT(0, ("%s: no PHY descriptor for LMAC%d", + __func__, lmacid)); + MDIO_UNLOCK(sc); + return (ENXIO); + } + mii_sc = device_get_softc(pd->miibus); + + mii_tick(mii_sc); + if ((mii_sc->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + /* Link is up */ + *link = 1; + } else + *link = 0; + + switch (IFM_SUBTYPE(mii_sc->mii_media_active)) { + case IFM_10_T: + *speed = 10; + break; + case IFM_100_TX: + *speed = 100; + break; + case IFM_1000_T: + *speed = 1000; + break; + default: + /* IFM_NONE */ + *speed = 0; + } + + if ((IFM_OPTIONS(mii_sc->mii_media_active) & IFM_FDX) != 0) + *duplex = 1; + else + *duplex = 0; + + MDIO_UNLOCK(sc); + + return (0); +} + +static int +thunder_mdio_media_change(device_t dev, int lmacid, int link, int duplex, + int speed) +{ + + return (EIO); +} + +static int +thunder_mdio_phy_connect(device_t dev, int lmacid, int phy) +{ + struct thunder_mdio_softc *sc; + struct phy_desc *pd; + int err; + + sc = device_get_softc(dev); + + MDIO_LOCK(sc); + pd = get_phy_desc(sc, lmacid); + MDIO_UNLOCK(sc); + if (pd == NULL) { + pd = malloc(sizeof(*pd), M_THUNDER_MDIO, (M_NOWAIT | M_ZERO)); + if (pd == NULL) + return (ENOMEM); + pd->ifp = if_alloc(IFT_ETHER); + if (pd->ifp == NULL) { + free(pd, M_THUNDER_MDIO); + return (ENOMEM); + } + pd->lmacid = lmacid; + } + + err = mii_attach(dev, &pd->miibus, pd->ifp, + thunder_ifmedia_change_stub, thunder_ifmedia_status_stub, + BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); + + if (err != 0) { + device_printf(dev, "Could not attach PHY%d\n", phy); + if_free(pd->ifp); + free(pd, M_THUNDER_MDIO); + return (ENXIO); + } + + MDIO_LOCK(sc); + TAILQ_INSERT_TAIL(&sc->phy_desc_head, pd, phy_desc_list); + MDIO_UNLOCK(sc); + + return (0); +} + +static int +thunder_mdio_phy_disconnect(device_t dev, int lmacid, int phy) +{ + struct thunder_mdio_softc *sc; + struct phy_desc *pd; + + sc = device_get_softc(dev); + MDIO_LOCK(sc); + + pd = get_phy_desc(sc, lmacid); + if (pd == NULL) { + MDIO_UNLOCK(sc); + return (EINVAL); + } + + /* Remove this PHY descriptor from the list */ + TAILQ_REMOVE(&sc->phy_desc_head, pd, phy_desc_list); + + /* Detach miibus */ + bus_generic_detach(dev); + device_delete_child(dev, pd->miibus); + /* Free fake ifnet */ + if_free(pd->ifp); + /* Free memory under phy descriptor */ + free(pd, M_THUNDER_MDIO); + MDIO_UNLOCK(sc); + + return (0); +} diff --git a/sys/dev/vnic/thunder_mdio_fdt.c b/sys/dev/vnic/thunder_mdio_fdt.c new file mode 100644 index 00000000000..cd2d1cff9f0 --- /dev/null +++ b/sys/dev/vnic/thunder_mdio_fdt.c @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 2015 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under + * the sponsorship of the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include + +#include "thunder_mdio_var.h" + +static int thunder_mdio_fdt_probe(device_t); +static int thunder_mdio_fdt_attach(device_t); + +static device_method_t thunder_mdio_fdt_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, thunder_mdio_fdt_probe), + DEVMETHOD(device_attach, thunder_mdio_fdt_attach), + + /* End */ + DEVMETHOD_END +}; + +DEFINE_CLASS_1(thunder_mdio, thunder_mdio_fdt_driver, thunder_mdio_fdt_methods, + sizeof(struct thunder_mdio_softc), thunder_mdio_driver); + +static devclass_t thunder_mdio_fdt_devclass; + +DRIVER_MODULE(thunder_mdio, ofwbus, thunder_mdio_fdt_driver, + thunder_mdio_fdt_devclass, 0, 0); + +static int +thunder_mdio_fdt_probe(device_t dev) +{ + + if (ofw_bus_is_compatible(dev, "cavium,octeon-3860-mdio")) { + device_set_desc(dev, THUNDER_MDIO_DEVSTR); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +thunder_mdio_fdt_attach(device_t dev) +{ + phandle_t node; + int ret; + + /* Call core attach */ + ret = thunder_mdio_attach(dev); + if (ret != 0) + return (ret); + /* + * Register device to this node/xref. + * Thanks to that we will be able to retrieve device_t structure + * while holding only node reference acquired from FDT. + */ + node = ofw_bus_get_node(dev); + OF_device_register_xref(OF_xref_from_node(node), dev); + + return (0); +} diff --git a/sys/dev/vnic/thunder_mdio_var.h b/sys/dev/vnic/thunder_mdio_var.h new file mode 100644 index 00000000000..01c4ca7f637 --- /dev/null +++ b/sys/dev/vnic/thunder_mdio_var.h @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2015 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under + * the sponsorship of the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifndef __THUNDER_MDIO_VAR_H__ +#define __THUNDER_MDIO_VAR_H__ + +#define THUNDER_MDIO_DEVSTR "Cavium ThunderX SMI/MDIO driver" +DECLARE_CLASS(thunder_mdio_driver); + +enum thunder_mdio_mode { + MODE_NONE = 0, + MODE_IEEE_C22, + MODE_IEEE_C45 +}; + +struct phy_desc { + device_t miibus; /* One miibus per LMAC */ + struct ifnet * ifp; /* Fake ifp to satisfy miibus */ + int lmacid; /* ID number of LMAC connected */ + TAILQ_ENTRY(phy_desc) phy_desc_list; +}; + +struct thunder_mdio_softc { + device_t dev; + struct mtx mtx; + struct resource * reg_base; + + enum thunder_mdio_mode mode; + + TAILQ_HEAD(,phy_desc) phy_desc_head; +}; + +int thunder_mdio_attach(device_t); +#endif diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 303e86528d3..94b25b8fe3b 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -721,21 +721,22 @@ fail: static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) { - const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header; + struct thread *td; + const Elf_Ehdr *hdr; const Elf_Phdr *phdr; Elf_Auxargs *elf_auxargs; struct vmspace *vmspace; - vm_prot_t prot; - u_long text_size = 0, data_size = 0, total_size = 0; - u_long text_addr = 0, data_addr = 0; - u_long seg_size, seg_addr; - u_long addr, baddr, et_dyn_addr, entry = 0, proghdr = 0; - int32_t osrel = 0; - int error = 0, i, n, interp_name_len = 0; - const char *err_str = NULL, *interp = NULL, *newinterp = NULL; + const char *err_str, *newinterp; + char *interp, *interp_buf, *path; Elf_Brandinfo *brand_info; - char *path; struct sysentvec *sv; + vm_prot_t prot; + u_long text_size, data_size, total_size, text_addr, data_addr; + u_long seg_size, seg_addr, addr, baddr, et_dyn_addr, entry, proghdr; + int32_t osrel; + int error, i, n, interp_name_len, have_interp; + + hdr = (const Elf_Ehdr *)imgp->image_header; /* * Do we have a valid ELF header ? @@ -763,8 +764,17 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) uprintf("Unaligned program headers\n"); return (ENOEXEC); } - n = 0; + + n = error = 0; baddr = 0; + osrel = 0; + text_size = data_size = total_size = text_addr = data_addr = 0; + entry = proghdr = 0; + interp_name_len = 0; + err_str = newinterp = NULL; + interp = interp_buf = NULL; + td = curthread; + for (i = 0; i < hdr->e_phnum; i++) { switch (phdr[i].p_type) { case PT_LOAD: @@ -774,14 +784,32 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) break; case PT_INTERP: /* Path to interpreter */ - if (phdr[i].p_filesz > MAXPATHLEN || - phdr[i].p_offset > PAGE_SIZE || - phdr[i].p_filesz > PAGE_SIZE - phdr[i].p_offset) { + if (phdr[i].p_filesz > MAXPATHLEN) { uprintf("Invalid PT_INTERP\n"); - return (ENOEXEC); + error = ENOEXEC; + goto ret; } - interp = imgp->image_header + phdr[i].p_offset; interp_name_len = phdr[i].p_filesz; + if (phdr[i].p_offset > PAGE_SIZE || + interp_name_len > PAGE_SIZE - phdr[i].p_offset) { + VOP_UNLOCK(imgp->vp, 0); + interp_buf = malloc(interp_name_len + 1, M_TEMP, + M_WAITOK); + vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); + error = vn_rdwr(UIO_READ, imgp->vp, interp_buf, + interp_name_len, phdr[i].p_offset, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, + NOCRED, NULL, td); + if (error != 0) { + uprintf("i/o error PT_INTERP\n"); + goto ret; + } + interp_buf[interp_name_len] = '\0'; + interp = interp_buf; + } else { + interp = __DECONST(char *, imgp->image_header) + + phdr[i].p_offset; + } break; case PT_GNU_STACK: if (__elfN(nxstack)) @@ -797,12 +825,14 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) if (brand_info == NULL) { uprintf("ELF binary type \"%u\" not known.\n", hdr->e_ident[EI_OSABI]); - return (ENOEXEC); + error = ENOEXEC; + goto ret; } if (hdr->e_type == ET_DYN) { if ((brand_info->flags & BI_CAN_EXEC_DYN) == 0) { uprintf("Cannot execute shared object\n"); - return (ENOEXEC); + error = ENOEXEC; + goto ret; } /* * Honour the base load address from the dso if it is @@ -835,8 +865,8 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) imgp->proc->p_sysent = sv; vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); - if (error) - return (error); + if (error != 0) + goto ret; for (i = 0; i < hdr->e_phnum; i++) { switch (phdr[i].p_type) { @@ -849,7 +879,7 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) phdr[i].p_memsz, phdr[i].p_filesz, prot, sv->sv_pagesize); if (error != 0) - return (error); + goto ret; /* * If this segment contains the program headers, @@ -921,7 +951,8 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) if (err_str != NULL) { PROC_UNLOCK(imgp->proc); uprintf("%s\n", err_str); - return (ENOMEM); + error = ENOMEM; + goto ret; } vmspace = imgp->proc->p_vmspace; @@ -936,14 +967,14 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) * calculation is that it leaves room for the heap to grow to * its maximum allowed size. */ - addr = round_page((vm_offset_t)vmspace->vm_daddr + lim_max(curthread, + addr = round_page((vm_offset_t)vmspace->vm_daddr + lim_max(td, RLIMIT_DATA)); PROC_UNLOCK(imgp->proc); imgp->entry_addr = entry; if (interp != NULL) { - int have_interp = FALSE; + have_interp = FALSE; VOP_UNLOCK(imgp->vp, 0); if (brand_info->emul_path != NULL && brand_info->emul_path[0] != '\0') { @@ -969,7 +1000,7 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); if (error != 0) { uprintf("ELF interpreter %s not found\n", interp); - return (error); + goto ret; } } else addr = et_dyn_addr; @@ -993,6 +1024,8 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) imgp->reloc_base = addr; imgp->proc->p_osrel = osrel; + ret: + free(interp_buf, M_TEMP); return (error); } @@ -2122,19 +2155,42 @@ __elfN(parse_notes)(struct image_params *imgp, Elf_Brandnote *checknote, { const Elf_Note *note, *note0, *note_end; const char *note_name; - int i; + char *buf; + int i, error; + boolean_t res; - if (pnote == NULL || pnote->p_offset > PAGE_SIZE || - pnote->p_filesz > PAGE_SIZE - pnote->p_offset) + /* We need some limit, might as well use PAGE_SIZE. */ + if (pnote == NULL || pnote->p_filesz > PAGE_SIZE) return (FALSE); - - note = note0 = (const Elf_Note *)(imgp->image_header + pnote->p_offset); - note_end = (const Elf_Note *)(imgp->image_header + - pnote->p_offset + pnote->p_filesz); + ASSERT_VOP_LOCKED(imgp->vp, "parse_notes"); + if (pnote->p_offset > PAGE_SIZE || + pnote->p_filesz > PAGE_SIZE - pnote->p_offset) { + VOP_UNLOCK(imgp->vp, 0); + buf = malloc(pnote->p_filesz, M_TEMP, M_WAITOK); + vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); + error = vn_rdwr(UIO_READ, imgp->vp, buf, pnote->p_filesz, + pnote->p_offset, UIO_SYSSPACE, IO_NODELOCKED, + curthread->td_ucred, NOCRED, NULL, curthread); + if (error != 0) { + uprintf("i/o error PT_NOTE\n"); + res = FALSE; + goto ret; + } + note = note0 = (const Elf_Note *)buf; + note_end = (const Elf_Note *)(buf + pnote->p_filesz); + } else { + note = note0 = (const Elf_Note *)(imgp->image_header + + pnote->p_offset); + note_end = (const Elf_Note *)(imgp->image_header + + pnote->p_offset + pnote->p_filesz); + buf = NULL; + } for (i = 0; i < 100 && note >= note0 && note < note_end; i++) { if (!aligned(note, Elf32_Addr) || (const char *)note_end - - (const char *)note < sizeof(Elf_Note)) - return (FALSE); + (const char *)note < sizeof(Elf_Note)) { + res = FALSE; + goto ret; + } if (note->n_namesz != checknote->hdr.n_namesz || note->n_descsz != checknote->hdr.n_descsz || note->n_type != checknote->hdr.n_type) @@ -2150,17 +2206,21 @@ __elfN(parse_notes)(struct image_params *imgp, Elf_Brandnote *checknote, * from the ELF OSABI-note if necessary. */ if ((checknote->flags & BN_TRANSLATE_OSREL) != 0 && - checknote->trans_osrel != NULL) - return (checknote->trans_osrel(note, osrel)); - return (TRUE); - + checknote->trans_osrel != NULL) { + res = checknote->trans_osrel(note, osrel); + goto ret; + } + res = TRUE; + goto ret; nextnote: note = (const Elf_Note *)((const char *)(note + 1) + roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE) + roundup2(note->n_descsz, ELF_NOTE_ROUNDSIZE)); } - - return (FALSE); + res = FALSE; +ret: + free(buf, M_TEMP); + return (res); } /* diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 0e28072f010..bb35b9b25d8 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -848,7 +848,7 @@ fork1(struct thread *td, int flags, int pages, struct proc **procp, if (flags & RFPROCDESC) { error = falloc_caps(td, &fp_procdesc, procdescp, 0, fcaps); if (error != 0) - return (error); + goto fail2; } mem_charged = 0; diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c index 7f994fef1ef..de6b313f1b4 100644 --- a/sys/kern/uipc_mbuf.c +++ b/sys/kern/uipc_mbuf.c @@ -395,7 +395,7 @@ mb_free_ext(struct mbuf *m) * Attach the cluster from *m to *n, set up m_ext in *n * and bump the refcount of the cluster. */ -static void +void mb_dupcl(struct mbuf *n, const struct mbuf *m) { diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c index 70dc565fae3..4cc9a4ed188 100644 --- a/sys/kern/vfs_bio.c +++ b/sys/kern/vfs_bio.c @@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -100,6 +101,7 @@ caddr_t unmapped_buf; /* Used below and for softdep flushing threads in ufs/ffs/ffs_softdep.c */ struct proc *bufdaemonproc; +struct proc *bufspacedaemonproc; static int inmem(struct vnode *vp, daddr_t blkno); static void vm_hold_free_pages(struct buf *bp, int newbsize); @@ -116,11 +118,18 @@ static void vfs_vmio_extend(struct buf *bp, int npages, int size); static int vfs_bio_clcheck(struct vnode *vp, int size, daddr_t lblkno, daddr_t blkno); static int buf_flush(struct vnode *vp, int); +static int buf_recycle(bool); +static int buf_scan(bool); static int flushbufqueues(struct vnode *, int, int); static void buf_daemon(void); static void bremfreel(struct buf *bp); static __inline void bd_wakeup(void); static int sysctl_runningspace(SYSCTL_HANDLER_ARGS); +static void bufkva_reclaim(vmem_t *, int); +static void bufkva_free(struct buf *); +static int buf_import(void *, void **, int, int); +static void buf_release(void *, void **, int); + #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) static int sysctl_bufspace(SYSCTL_HANDLER_ARGS); @@ -145,23 +154,23 @@ static long bufkvaspace; SYSCTL_LONG(_vfs, OID_AUTO, bufkvaspace, CTLFLAG_RD, &bufkvaspace, 0, "Kernel virtual memory used for buffers"); static long maxbufspace; -SYSCTL_LONG(_vfs, OID_AUTO, maxbufspace, CTLFLAG_RD, &maxbufspace, 0, - "Maximum allowed value of bufspace (including buf_daemon)"); +SYSCTL_LONG(_vfs, OID_AUTO, maxbufspace, CTLFLAG_RW, &maxbufspace, 0, + "Maximum allowed value of bufspace (including metadata)"); static long bufmallocspace; SYSCTL_LONG(_vfs, OID_AUTO, bufmallocspace, CTLFLAG_RD, &bufmallocspace, 0, "Amount of malloced memory for buffers"); static long maxbufmallocspace; -SYSCTL_LONG(_vfs, OID_AUTO, maxmallocbufspace, CTLFLAG_RW, &maxbufmallocspace, 0, - "Maximum amount of malloced memory for buffers"); +SYSCTL_LONG(_vfs, OID_AUTO, maxmallocbufspace, CTLFLAG_RW, &maxbufmallocspace, + 0, "Maximum amount of malloced memory for buffers"); static long lobufspace; -SYSCTL_LONG(_vfs, OID_AUTO, lobufspace, CTLFLAG_RD, &lobufspace, 0, +SYSCTL_LONG(_vfs, OID_AUTO, lobufspace, CTLFLAG_RW, &lobufspace, 0, "Minimum amount of buffers we want to have"); long hibufspace; -SYSCTL_LONG(_vfs, OID_AUTO, hibufspace, CTLFLAG_RD, &hibufspace, 0, - "Maximum allowed value of bufspace (excluding buf_daemon)"); -static int bufreusecnt; -SYSCTL_INT(_vfs, OID_AUTO, bufreusecnt, CTLFLAG_RW, &bufreusecnt, 0, - "Number of times we have reused a buffer"); +SYSCTL_LONG(_vfs, OID_AUTO, hibufspace, CTLFLAG_RW, &hibufspace, 0, + "Maximum allowed value of bufspace (excluding metadata)"); +long bufspacethresh; +SYSCTL_LONG(_vfs, OID_AUTO, bufspacethresh, CTLFLAG_RW, &bufspacethresh, + 0, "Bufspace consumed before waking the daemon to free some"); static int buffreekvacnt; SYSCTL_INT(_vfs, OID_AUTO, buffreekvacnt, CTLFLAG_RW, &buffreekvacnt, 0, "Number of times we have freed the KVA space from some buffer"); @@ -205,10 +214,10 @@ SYSCTL_INT(_vfs, OID_AUTO, numfreebuffers, CTLFLAG_RD, &numfreebuffers, 0, "Number of free buffers"); static int lofreebuffers; SYSCTL_INT(_vfs, OID_AUTO, lofreebuffers, CTLFLAG_RW, &lofreebuffers, 0, - "XXX Unused"); + "Target number of free buffers"); static int hifreebuffers; SYSCTL_INT(_vfs, OID_AUTO, hifreebuffers, CTLFLAG_RW, &hifreebuffers, 0, - "XXX Complicatedly unused"); + "Threshold for clean buffer recycling"); static int getnewbufcalls; SYSCTL_INT(_vfs, OID_AUTO, getnewbufcalls, CTLFLAG_RW, &getnewbufcalls, 0, "Number of calls to getnewbuf"); @@ -219,6 +228,9 @@ static int mappingrestarts; SYSCTL_INT(_vfs, OID_AUTO, mappingrestarts, CTLFLAG_RW, &mappingrestarts, 0, "Number of times getblk has had to restart a buffer mapping for " "unmapped buffer"); +static int numbufallocfails; +SYSCTL_INT(_vfs, OID_AUTO, numbufallocfails, CTLFLAG_RW, &numbufallocfails, 0, + "Number of times buffer allocations failed"); static int flushbufqtarget = 100; SYSCTL_INT(_vfs, OID_AUTO, flushbufqtarget, CTLFLAG_RW, &flushbufqtarget, 0, "Amount of work to do in flushbufqueues when helping bufdaemon"); @@ -232,16 +244,6 @@ SYSCTL_INT(_vfs, OID_AUTO, unmapped_buf_allowed, CTLFLAG_RD, &unmapped_buf_allowed, 0, "Permit the use of the unmapped i/o"); -/* - * Lock for the non-dirty bufqueues - */ -static struct mtx_padalign bqclean; - -/* - * Lock for the dirty queue. - */ -static struct mtx_padalign bqdirty; - /* * This lock synchronizes access to bd_request. */ @@ -270,6 +272,11 @@ static struct mtx_padalign bdirtylock; */ static int bd_request; +/* + * Request/wakeup point for the bufspace daemon. + */ +static int bufspace_request; + /* * Request for the buf daemon to write more buffers than is indicated by * lodirtybuf. This may be necessary to push out excess dependencies or @@ -298,7 +305,7 @@ static int runningbufreq; * Synchronization (sleep/wakeup) variable for buffer requests. * Can contain the VFS_BIO_NEED flags defined below; setting/clearing is done * by and/or. - * Used in numdirtywakeup(), bufspacewakeup(), bufcountadd(), bwillwrite(), + * Used in numdirtywakeup(), bufspace_wakeup(), bwillwrite(), * getnewbuf(), and getblk(). */ static volatile int needsbuffer; @@ -311,30 +318,43 @@ static int bdirtywait; /* * Definitions for the buffer free lists. */ -#define BUFFER_QUEUES 4 /* number of free buffer queues */ - #define QUEUE_NONE 0 /* on no queue */ -#define QUEUE_CLEAN 1 /* non-B_DELWRI buffers */ +#define QUEUE_EMPTY 1 /* empty buffer headers */ #define QUEUE_DIRTY 2 /* B_DELWRI buffers */ -#define QUEUE_EMPTY 3 /* empty buffer headers */ +#define QUEUE_CLEAN 3 /* non-B_DELWRI buffers */ #define QUEUE_SENTINEL 1024 /* not an queue index, but mark for sentinel */ +/* Maximum number of clean buffer queues. */ +#define CLEAN_QUEUES 16 + +/* Configured number of clean queues. */ +static int clean_queues; + +/* Maximum number of buffer queues. */ +#define BUFFER_QUEUES (QUEUE_CLEAN + CLEAN_QUEUES) + /* Queues for free buffers with various properties */ static TAILQ_HEAD(bqueues, buf) bufqueues[BUFFER_QUEUES] = { { 0 } }; #ifdef INVARIANTS static int bq_len[BUFFER_QUEUES]; #endif +/* + * Lock for each bufqueue + */ +static struct mtx_padalign bqlocks[BUFFER_QUEUES]; + +/* + * per-cpu empty buffer cache. + */ +uma_zone_t buf_zone; + /* * Single global constant for BUF_WMESG, to avoid getting multiple references. * buf_wmesg is referred from macros. */ const char *buf_wmesg = BUF_WMESG; -#define VFS_BIO_NEED_ANY 0x01 /* any freeable buffer */ -#define VFS_BIO_NEED_FREE 0x04 /* wait for free bufs, hi hysteresis */ -#define VFS_BIO_NEED_BUFSPACE 0x08 /* wait for buf space, lo hysteresis */ - static int sysctl_runningspace(SYSCTL_HANDLER_ARGS) { @@ -382,6 +402,21 @@ sysctl_bufspace(SYSCTL_HANDLER_ARGS) } #endif +static int +bqcleanq(void) +{ + static int nextq; + + return ((atomic_fetchadd_int(&nextq, 1) % clean_queues) + QUEUE_CLEAN); +} + +static int +bqisclean(int qindex) +{ + + return (qindex >= QUEUE_CLEAN && qindex < QUEUE_CLEAN + CLEAN_QUEUES); +} + /* * bqlock: * @@ -391,9 +426,7 @@ static inline struct mtx * bqlock(int qindex) { - if (qindex == QUEUE_DIRTY) - return (struct mtx *)(&bqdirty); - return (struct mtx *)(&bqclean); + return (struct mtx *)&bqlocks[qindex]; } /* @@ -447,61 +480,254 @@ bdirtyadd(void) } /* - * bufspacewakeup: + * bufspace_wakeup: * * Called when buffer space is potentially available for recovery. * getnewbuf() will block on this flag when it is unable to free * sufficient buffer space. Buffer space becomes recoverable when * bp's get placed back in the queues. */ -static __inline void -bufspacewakeup(void) +static void +bufspace_wakeup(void) { - int need_wakeup, on; /* - * If someone is waiting for bufspace, wake them up. Even - * though we may not have freed the kva space yet, the waiting - * process will be able to now. + * If someone is waiting for bufspace, wake them up. + * + * Since needsbuffer is set prior to doing an additional queue + * scan it is safe to check for the flag prior to acquiring the + * lock. The thread that is preparing to scan again before + * blocking would discover the buf we released. */ - rw_rlock(&nblock); - for (;;) { - need_wakeup = 0; - on = needsbuffer; - if ((on & VFS_BIO_NEED_BUFSPACE) == 0) - break; - need_wakeup = 1; - if (atomic_cmpset_rel_int(&needsbuffer, on, - on & ~VFS_BIO_NEED_BUFSPACE)) - break; + if (needsbuffer) { + rw_rlock(&nblock); + if (atomic_cmpset_int(&needsbuffer, 1, 0) == 1) + wakeup(__DEVOLATILE(void *, &needsbuffer)); + rw_runlock(&nblock); + } +} + +/* + * bufspace_daemonwakeup: + * + * Wakeup the daemon responsible for freeing clean bufs. + */ +static void +bufspace_daemonwakeup(void) +{ + rw_rlock(&nblock); + if (bufspace_request == 0) { + bufspace_request = 1; + wakeup(&bufspace_request); } - if (need_wakeup) - wakeup(__DEVOLATILE(void *, &needsbuffer)); rw_runlock(&nblock); } /* - * bufspaceadjust: + * bufspace_adjust: * * Adjust the reported bufspace for a KVA managed buffer, possibly * waking any waiters. */ static void -bufspaceadjust(struct buf *bp, int bufsize) +bufspace_adjust(struct buf *bp, int bufsize) { + long space; int diff; KASSERT((bp->b_flags & B_MALLOC) == 0, - ("bufspaceadjust: malloc buf %p", bp)); + ("bufspace_adjust: malloc buf %p", bp)); diff = bufsize - bp->b_bufsize; if (diff < 0) { atomic_subtract_long(&bufspace, -diff); - bufspacewakeup(); - } else - atomic_add_long(&bufspace, diff); + bufspace_wakeup(); + } else { + space = atomic_fetchadd_long(&bufspace, diff); + /* Wake up the daemon on the transition. */ + if (space < bufspacethresh && space + diff >= bufspacethresh) + bufspace_daemonwakeup(); + } bp->b_bufsize = bufsize; } +/* + * bufspace_reserve: + * + * Reserve bufspace before calling allocbuf(). metadata has a + * different space limit than data. + */ +static int +bufspace_reserve(int size, bool metadata) +{ + long limit; + long space; + + if (metadata) + limit = maxbufspace; + else + limit = hibufspace; + do { + space = bufspace; + if (space + size > limit) + return (ENOSPC); + } while (atomic_cmpset_long(&bufspace, space, space + size) == 0); + + /* Wake up the daemon on the transition. */ + if (space < bufspacethresh && space + size >= bufspacethresh) + bufspace_daemonwakeup(); + + return (0); +} + +/* + * bufspace_release: + * + * Release reserved bufspace after bufspace_adjust() has consumed it. + */ +static void +bufspace_release(int size) +{ + atomic_subtract_long(&bufspace, size); + bufspace_wakeup(); +} + +/* + * bufspace_wait: + * + * Wait for bufspace, acting as the buf daemon if a locked vnode is + * supplied. needsbuffer must be set in a safe fashion prior to + * polling for space. The operation must be re-tried on return. + */ +static void +bufspace_wait(struct vnode *vp, int gbflags, int slpflag, int slptimeo) +{ + struct thread *td; + int error, fl, norunbuf; + + if ((gbflags & GB_NOWAIT_BD) != 0) + return; + + td = curthread; + rw_wlock(&nblock); + while (needsbuffer != 0) { + if (vp != NULL && vp->v_type != VCHR && + (td->td_pflags & TDP_BUFNEED) == 0) { + rw_wunlock(&nblock); + /* + * getblk() is called with a vnode locked, and + * some majority of the dirty buffers may as + * well belong to the vnode. Flushing the + * buffers there would make a progress that + * cannot be achieved by the buf_daemon, that + * cannot lock the vnode. + */ + norunbuf = ~(TDP_BUFNEED | TDP_NORUNNINGBUF) | + (td->td_pflags & TDP_NORUNNINGBUF); + + /* + * Play bufdaemon. The getnewbuf() function + * may be called while the thread owns lock + * for another dirty buffer for the same + * vnode, which makes it impossible to use + * VOP_FSYNC() there, due to the buffer lock + * recursion. + */ + td->td_pflags |= TDP_BUFNEED | TDP_NORUNNINGBUF; + fl = buf_flush(vp, flushbufqtarget); + td->td_pflags &= norunbuf; + rw_wlock(&nblock); + if (fl != 0) + continue; + if (needsbuffer == 0) + break; + } + error = rw_sleep(__DEVOLATILE(void *, &needsbuffer), &nblock, + (PRIBIO + 4) | slpflag, "newbuf", slptimeo); + if (error != 0) + break; + } + rw_wunlock(&nblock); +} + + +/* + * bufspace_daemon: + * + * buffer space management daemon. Tries to maintain some marginal + * amount of free buffer space so that requesting processes neither + * block nor work to reclaim buffers. + */ +static void +bufspace_daemon(void) +{ + for (;;) { + kproc_suspend_check(bufspacedaemonproc); + + /* + * Free buffers from the clean queue until we meet our + * targets. + * + * Theory of operation: The buffer cache is most efficient + * when some free buffer headers and space are always + * available to getnewbuf(). This daemon attempts to prevent + * the excessive blocking and synchronization associated + * with shortfall. It goes through three phases according + * demand: + * + * 1) The daemon wakes up voluntarily once per-second + * during idle periods when the counters are below + * the wakeup thresholds (bufspacethresh, lofreebuffers). + * + * 2) The daemon wakes up as we cross the thresholds + * ahead of any potential blocking. This may bounce + * slightly according to the rate of consumption and + * release. + * + * 3) The daemon and consumers are starved for working + * clean buffers. This is the 'bufspace' sleep below + * which will inefficiently trade bufs with bqrelse + * until we return to condition 2. + */ + while (bufspace > lobufspace || + numfreebuffers < hifreebuffers) { + if (buf_recycle(false) != 0) { + atomic_set_int(&needsbuffer, 1); + if (buf_recycle(false) != 0) { + rw_wlock(&nblock); + if (needsbuffer) + rw_sleep(__DEVOLATILE(void *, + &needsbuffer), &nblock, + PRIBIO|PDROP, "bufspace", + hz/10); + else + rw_wunlock(&nblock); + } + } + maybe_yield(); + } + + /* + * Re-check our limits under the exclusive nblock. + */ + rw_wlock(&nblock); + if (bufspace < bufspacethresh && + numfreebuffers > lofreebuffers) { + bufspace_request = 0; + rw_sleep(&bufspace_request, &nblock, PRIBIO|PDROP, + "-", hz); + } else + rw_wunlock(&nblock); + } +} + +static struct kproc_desc bufspace_kp = { + "bufspacedaemon", + bufspace_daemon, + &bufspacedaemonproc +}; +SYSINIT(bufspacedaemon, SI_SUB_KTHREAD_BUF, SI_ORDER_FIRST, kproc_start, + &bufspace_kp); + /* * bufmallocadjust: * @@ -516,10 +742,9 @@ bufmallocadjust(struct buf *bp, int bufsize) KASSERT((bp->b_flags & B_MALLOC) != 0, ("bufmallocadjust: non-malloc buf %p", bp)); diff = bufsize - bp->b_bufsize; - if (diff < 0) { + if (diff < 0) atomic_subtract_long(&bufmallocspace, -diff); - bufspacewakeup(); - } else + else atomic_add_long(&bufmallocspace, diff); bp->b_bufsize = bufsize; } @@ -570,67 +795,6 @@ runningbufwakeup(struct buf *bp) runningwakeup(); } -/* - * bufcountadd: - * - * Called when a buffer has been added to one of the free queues to - * account for the buffer and to wakeup anyone waiting for free buffers. - * This typically occurs when large amounts of metadata are being handled - * by the buffer cache ( else buffer space runs out first, usually ). - */ -static __inline void -bufcountadd(struct buf *bp) -{ - int mask, need_wakeup, old, on; - - KASSERT((bp->b_flags & B_INFREECNT) == 0, - ("buf %p already counted as free", bp)); - bp->b_flags |= B_INFREECNT; - old = atomic_fetchadd_int(&numfreebuffers, 1); - KASSERT(old >= 0 && old < nbuf, - ("numfreebuffers climbed to %d", old + 1)); - mask = VFS_BIO_NEED_ANY; - if (numfreebuffers >= hifreebuffers) - mask |= VFS_BIO_NEED_FREE; - rw_rlock(&nblock); - for (;;) { - need_wakeup = 0; - on = needsbuffer; - if (on == 0) - break; - need_wakeup = 1; - if (atomic_cmpset_rel_int(&needsbuffer, on, on & ~mask)) - break; - } - if (need_wakeup) - wakeup(__DEVOLATILE(void *, &needsbuffer)); - rw_runlock(&nblock); -} - -/* - * bufcountsub: - * - * Decrement the numfreebuffers count as needed. - */ -static void -bufcountsub(struct buf *bp) -{ - int old; - - /* - * Fixup numfreebuffers count. If the buffer is invalid or not - * delayed-write, the buffer was free and we must decrement - * numfreebuffers. - */ - if ((bp->b_flags & B_INVAL) || (bp->b_flags & B_DELWRI) == 0) { - KASSERT((bp->b_flags & B_INFREECNT) != 0, - ("buf %p not counted in numfreebuffers", bp)); - bp->b_flags &= ~B_INFREECNT; - old = atomic_fetchadd_int(&numfreebuffers, -1); - KASSERT(old > 0, ("numfreebuffers dropped to %d", old - 1)); - } -} - /* * waitrunningbufspace() * @@ -847,8 +1011,10 @@ bufinit(void) int i; CTASSERT(MAXBCACHEBUF >= MAXBSIZE); - mtx_init(&bqclean, "bufq clean lock", NULL, MTX_DEF); - mtx_init(&bqdirty, "bufq dirty lock", NULL, MTX_DEF); + mtx_init(&bqlocks[QUEUE_DIRTY], "bufq dirty lock", NULL, MTX_DEF); + mtx_init(&bqlocks[QUEUE_EMPTY], "bufq empty lock", NULL, MTX_DEF); + for (i = QUEUE_CLEAN; i < QUEUE_CLEAN + CLEAN_QUEUES; i++) + mtx_init(&bqlocks[i], "bufq clean lock", NULL, MTX_DEF); mtx_init(&rbreqlock, "runningbufspace lock", NULL, MTX_DEF); rw_init(&nblock, "needsbuffer lock"); mtx_init(&bdlock, "buffer daemon lock", NULL, MTX_DEF); @@ -864,7 +1030,7 @@ bufinit(void) for (i = 0; i < nbuf; i++) { bp = &buf[i]; bzero(bp, sizeof *bp); - bp->b_flags = B_INVAL | B_INFREECNT; + bp->b_flags = B_INVAL; bp->b_rcred = NOCRED; bp->b_wcred = NOCRED; bp->b_qindex = QUEUE_EMPTY; @@ -881,18 +1047,19 @@ bufinit(void) /* * maxbufspace is the absolute maximum amount of buffer space we are * allowed to reserve in KVM and in real terms. The absolute maximum - * is nominally used by buf_daemon. hibufspace is the nominal maximum - * used by most other processes. The differential is required to - * ensure that buf_daemon is able to run when other processes might - * be blocked waiting for buffer space. + * is nominally used by metadata. hibufspace is the nominal maximum + * used by most other requests. The differential is required to + * ensure that metadata deadlocks don't occur. * * maxbufspace is based on BKVASIZE. Allocating buffers larger then * this may result in KVM fragmentation which is not handled optimally - * by the system. + * by the system. XXX This is less true with vmem. We could use + * PAGE_SIZE. */ maxbufspace = (long)nbuf * BKVASIZE; hibufspace = lmax(3 * maxbufspace / 4, maxbufspace - MAXBCACHEBUF * 10); - lobufspace = hibufspace - MAXBCACHEBUF; + lobufspace = (hibufspace / 20) * 19; /* 95% */ + bufspacethresh = lobufspace + (hibufspace - lobufspace) / 2; /* * Note: The 16 MiB upper limit for hirunningspace was chosen @@ -906,44 +1073,61 @@ bufinit(void) 16 * 1024 * 1024), 1024 * 1024); lorunningspace = roundup((hirunningspace * 2) / 3, MAXBCACHEBUF); -/* - * Limit the amount of malloc memory since it is wired permanently into - * the kernel space. Even though this is accounted for in the buffer - * allocation, we don't want the malloced region to grow uncontrolled. - * The malloc scheme improves memory utilization significantly on average - * (small) directories. - */ + /* + * Limit the amount of malloc memory since it is wired permanently into + * the kernel space. Even though this is accounted for in the buffer + * allocation, we don't want the malloced region to grow uncontrolled. + * The malloc scheme improves memory utilization significantly on + * average (small) directories. + */ maxbufmallocspace = hibufspace / 20; -/* - * Reduce the chance of a deadlock occuring by limiting the number - * of delayed-write dirty buffers we allow to stack up. - */ + /* + * Reduce the chance of a deadlock occuring by limiting the number + * of delayed-write dirty buffers we allow to stack up. + */ hidirtybuffers = nbuf / 4 + 20; dirtybufthresh = hidirtybuffers * 9 / 10; numdirtybuffers = 0; -/* - * To support extreme low-memory systems, make sure hidirtybuffers cannot - * eat up all available buffer space. This occurs when our minimum cannot - * be met. We try to size hidirtybuffers to 3/4 our buffer space assuming - * BKVASIZE'd buffers. - */ + /* + * To support extreme low-memory systems, make sure hidirtybuffers + * cannot eat up all available buffer space. This occurs when our + * minimum cannot be met. We try to size hidirtybuffers to 3/4 our + * buffer space assuming BKVASIZE'd buffers. + */ while ((long)hidirtybuffers * BKVASIZE > 3 * hibufspace / 4) { hidirtybuffers >>= 1; } lodirtybuffers = hidirtybuffers / 2; -/* - * Try to keep the number of free buffers in the specified range, - * and give special processes (e.g. like buf_daemon) access to an - * emergency reserve. - */ - lofreebuffers = nbuf / 18 + 5; - hifreebuffers = 2 * lofreebuffers; + /* + * lofreebuffers should be sufficient to avoid stalling waiting on + * buf headers under heavy utilization. The bufs in per-cpu caches + * are counted as free but will be unavailable to threads executing + * on other cpus. + * + * hifreebuffers is the free target for the bufspace daemon. This + * should be set appropriately to limit work per-iteration. + */ + lofreebuffers = MIN((nbuf / 25) + (20 * mp_ncpus), 128 * mp_ncpus); + hifreebuffers = (3 * lofreebuffers) / 2; numfreebuffers = nbuf; bogus_page = vm_page_alloc(NULL, 0, VM_ALLOC_NOOBJ | VM_ALLOC_NORMAL | VM_ALLOC_WIRED); + + /* Setup the kva and free list allocators. */ + vmem_set_reclaim(buffer_arena, bufkva_reclaim); + buf_zone = uma_zcache_create("buf free cache", sizeof(struct buf), + NULL, NULL, NULL, NULL, buf_import, buf_release, NULL, 0); + + /* + * Size the clean queue according to the amount of buffer space. + * One queue per-256mb up to the max. More queues gives better + * concurrency but less accurate LRU. + */ + clean_queues = MIN(howmany(maxbufspace, 256*1024*1024), CLEAN_QUEUES); + } #ifdef INVARIANTS @@ -1129,10 +1313,25 @@ binsfree(struct buf *bp, int qindex) { struct mtx *olock, *nlock; - BUF_ASSERT_XLOCKED(bp); + if (qindex != QUEUE_EMPTY) { + BUF_ASSERT_XLOCKED(bp); + } + /* + * Stick to the same clean queue for the lifetime of the buf to + * limit locking below. Otherwise pick ont sequentially. + */ + if (qindex == QUEUE_CLEAN) { + if (bqisclean(bp->b_qindex)) + qindex = bp->b_qindex; + else + qindex = bqcleanq(); + } + + /* + * Handle delayed bremfree() processing. + */ nlock = bqlock(qindex); - /* Handle delayed bremfree() processing. */ if (bp->b_flags & B_REMFREE) { olock = bqlock(bp->b_qindex); mtx_lock(olock); @@ -1156,15 +1355,263 @@ binsfree(struct buf *bp, int qindex) bq_len[bp->b_qindex]++; #endif mtx_unlock(nlock); +} + +/* + * buf_free: + * + * Free a buffer to the buf zone once it no longer has valid contents. + */ +static void +buf_free(struct buf *bp) +{ + + if (bp->b_flags & B_REMFREE) + bremfreef(bp); + if (bp->b_vflags & BV_BKGRDINPROG) + panic("losing buffer 1"); + if (bp->b_rcred != NOCRED) { + crfree(bp->b_rcred); + bp->b_rcred = NOCRED; + } + if (bp->b_wcred != NOCRED) { + crfree(bp->b_wcred); + bp->b_wcred = NOCRED; + } + if (!LIST_EMPTY(&bp->b_dep)) + buf_deallocate(bp); + bufkva_free(bp); + BUF_UNLOCK(bp); + uma_zfree(buf_zone, bp); + atomic_add_int(&numfreebuffers, 1); + bufspace_wakeup(); +} + +/* + * buf_import: + * + * Import bufs into the uma cache from the buf list. The system still + * expects a static array of bufs and much of the synchronization + * around bufs assumes type stable storage. As a result, UMA is used + * only as a per-cpu cache of bufs still maintained on a global list. + */ +static int +buf_import(void *arg, void **store, int cnt, int flags) +{ + struct buf *bp; + int i; + + mtx_lock(&bqlocks[QUEUE_EMPTY]); + for (i = 0; i < cnt; i++) { + bp = TAILQ_FIRST(&bufqueues[QUEUE_EMPTY]); + if (bp == NULL) + break; + bremfreel(bp); + store[i] = bp; + } + mtx_unlock(&bqlocks[QUEUE_EMPTY]); + + return (i); +} + +/* + * buf_release: + * + * Release bufs from the uma cache back to the buffer queues. + */ +static void +buf_release(void *arg, void **store, int cnt) +{ + int i; + + for (i = 0; i < cnt; i++) + binsfree(store[i], QUEUE_EMPTY); +} + +/* + * buf_alloc: + * + * Allocate an empty buffer header. + */ +static struct buf * +buf_alloc(void) +{ + struct buf *bp; + + bp = uma_zalloc(buf_zone, M_NOWAIT); + if (bp == NULL) { + bufspace_daemonwakeup(); + atomic_add_int(&numbufallocfails, 1); + return (NULL); + } /* - * Something we can maybe free or reuse. + * Wake-up the bufspace daemon on transition. */ - if (bp->b_bufsize && !(bp->b_flags & B_DELWRI)) - bufspacewakeup(); + if (atomic_fetchadd_int(&numfreebuffers, -1) == lofreebuffers) + bufspace_daemonwakeup(); - if ((bp->b_flags & B_INVAL) || !(bp->b_flags & B_DELWRI)) - bufcountadd(bp); + if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL) != 0) + panic("getnewbuf_empty: Locked buf %p on free queue.", bp); + + KASSERT(bp->b_vp == NULL, + ("bp: %p still has vnode %p.", bp, bp->b_vp)); + KASSERT((bp->b_flags & (B_DELWRI | B_NOREUSE)) == 0, + ("invalid buffer %p flags %#x", bp, bp->b_flags)); + KASSERT((bp->b_xflags & (BX_VNCLEAN|BX_VNDIRTY)) == 0, + ("bp: %p still on a buffer list. xflags %X", bp, bp->b_xflags)); + KASSERT(bp->b_npages == 0, + ("bp: %p still has %d vm pages\n", bp, bp->b_npages)); + KASSERT(bp->b_kvasize == 0, ("bp: %p still has kva\n", bp)); + KASSERT(bp->b_bufsize == 0, ("bp: %p still has bufspace\n", bp)); + + bp->b_flags = 0; + bp->b_ioflags = 0; + bp->b_xflags = 0; + bp->b_vflags = 0; + bp->b_vp = NULL; + bp->b_blkno = bp->b_lblkno = 0; + bp->b_offset = NOOFFSET; + bp->b_iodone = 0; + bp->b_error = 0; + bp->b_resid = 0; + bp->b_bcount = 0; + bp->b_npages = 0; + bp->b_dirtyoff = bp->b_dirtyend = 0; + bp->b_bufobj = NULL; + bp->b_pin_count = 0; + bp->b_data = bp->b_kvabase = unmapped_buf; + bp->b_fsprivate1 = NULL; + bp->b_fsprivate2 = NULL; + bp->b_fsprivate3 = NULL; + LIST_INIT(&bp->b_dep); + + return (bp); +} + +/* + * buf_qrecycle: + * + * Free a buffer from the given bufqueue. kva controls whether the + * freed buf must own some kva resources. This is used for + * defragmenting. + */ +static int +buf_qrecycle(int qindex, bool kva) +{ + struct buf *bp, *nbp; + + if (kva) + atomic_add_int(&bufdefragcnt, 1); + nbp = NULL; + mtx_lock(&bqlocks[qindex]); + nbp = TAILQ_FIRST(&bufqueues[qindex]); + + /* + * Run scan, possibly freeing data and/or kva mappings on the fly + * depending. + */ + while ((bp = nbp) != NULL) { + /* + * Calculate next bp (we can only use it if we do not + * release the bqlock). + */ + nbp = TAILQ_NEXT(bp, b_freelist); + + /* + * If we are defragging then we need a buffer with + * some kva to reclaim. + */ + if (kva && bp->b_kvasize == 0) + continue; + + if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL) != 0) + continue; + + /* + * Skip buffers with background writes in progress. + */ + if ((bp->b_vflags & BV_BKGRDINPROG) != 0) { + BUF_UNLOCK(bp); + continue; + } + + KASSERT(bp->b_qindex == qindex, + ("getnewbuf: inconsistent queue %d bp %p", qindex, bp)); + /* + * NOTE: nbp is now entirely invalid. We can only restart + * the scan from this point on. + */ + bremfreel(bp); + mtx_unlock(&bqlocks[qindex]); + + /* + * Requeue the background write buffer with error and + * restart the scan. + */ + if ((bp->b_vflags & BV_BKGRDERR) != 0) { + bqrelse(bp); + mtx_lock(&bqlocks[qindex]); + nbp = TAILQ_FIRST(&bufqueues[qindex]); + continue; + } + bp->b_flags |= B_INVAL; + brelse(bp); + return (0); + } + mtx_unlock(&bqlocks[qindex]); + + return (ENOBUFS); +} + +/* + * buf_recycle: + * + * Iterate through all clean queues until we find a buf to recycle or + * exhaust the search. + */ +static int +buf_recycle(bool kva) +{ + int qindex, first_qindex; + + qindex = first_qindex = bqcleanq(); + do { + if (buf_qrecycle(qindex, kva) == 0) + return (0); + if (++qindex == QUEUE_CLEAN + clean_queues) + qindex = QUEUE_CLEAN; + } while (qindex != first_qindex); + + return (ENOBUFS); +} + +/* + * buf_scan: + * + * Scan the clean queues looking for a buffer to recycle. needsbuffer + * is set on failure so that the caller may optionally bufspace_wait() + * in a race-free fashion. + */ +static int +buf_scan(bool defrag) +{ + int error; + + /* + * To avoid heavy synchronization and wakeup races we set + * needsbuffer and re-poll before failing. This ensures that + * no frees can be missed between an unsuccessful poll and + * going to sleep in a synchronized fashion. + */ + if ((error = buf_recycle(defrag)) != 0) { + atomic_set_int(&needsbuffer, 1); + bufspace_daemonwakeup(); + error = buf_recycle(defrag); + } + if (error == 0) + atomic_add_int(&getnewbufrestarts, 1); + return (error); } /* @@ -1185,7 +1632,6 @@ bremfree(struct buf *bp) BUF_ASSERT_XLOCKED(bp); bp->b_flags |= B_REMFREE; - bufcountsub(bp); } /* @@ -1219,7 +1665,9 @@ bremfreel(struct buf *bp) bp, bp->b_vp, bp->b_flags); KASSERT(bp->b_qindex != QUEUE_NONE, ("bremfreel: buffer %p not on a queue.", bp)); - BUF_ASSERT_XLOCKED(bp); + if (bp->b_qindex != QUEUE_EMPTY) { + BUF_ASSERT_XLOCKED(bp); + } mtx_assert(bqlock(bp->b_qindex), MA_OWNED); TAILQ_REMOVE(&bufqueues[bp->b_qindex], bp, b_freelist); @@ -1229,25 +1677,17 @@ bremfreel(struct buf *bp) bq_len[bp->b_qindex]--; #endif bp->b_qindex = QUEUE_NONE; - /* - * If this was a delayed bremfree() we only need to remove the buffer - * from the queue and return the stats are already done. - */ - if (bp->b_flags & B_REMFREE) { - bp->b_flags &= ~B_REMFREE; - return; - } - bufcountsub(bp); + bp->b_flags &= ~B_REMFREE; } /* - * bufkvafree: + * bufkva_free: * * Free the kva allocation for a buffer. * */ static void -bufkvafree(struct buf *bp) +bufkva_free(struct buf *bp) { #ifdef INVARIANTS @@ -1271,12 +1711,12 @@ bufkvafree(struct buf *bp) } /* - * bufkvaalloc: + * bufkva_alloc: * * Allocate the buffer KVA and set b_kvasize and b_kvabase. */ static int -bufkvaalloc(struct buf *bp, int maxsize, int gbflags) +bufkva_alloc(struct buf *bp, int maxsize, int gbflags) { vm_offset_t addr; int error; @@ -1284,7 +1724,7 @@ bufkvaalloc(struct buf *bp, int maxsize, int gbflags) KASSERT((gbflags & GB_UNMAPPED) == 0 || (gbflags & GB_KVAALLOC) != 0, ("Invalid gbflags 0x%x in %s", gbflags, __func__)); - bufkvafree(bp); + bufkva_free(bp); addr = 0; error = vmem_alloc(buffer_arena, maxsize, M_BESTFIT | M_NOWAIT, &addr); @@ -1293,7 +1733,6 @@ bufkvaalloc(struct buf *bp, int maxsize, int gbflags) * Buffer map is too fragmented. Request the caller * to defragment the map. */ - atomic_add_int(&bufdefragcnt, 1); return (error); } bp->b_kvabase = (caddr_t)addr; @@ -1309,6 +1748,24 @@ bufkvaalloc(struct buf *bp, int maxsize, int gbflags) return (0); } +/* + * bufkva_reclaim: + * + * Reclaim buffer kva by freeing buffers holding kva. This is a vmem + * callback that fires to avoid returning failure. + */ +static void +bufkva_reclaim(vmem_t *vmem, int flags) +{ + int i; + + for (i = 0; i < 5; i++) + if (buf_scan(true) != 0) + break; + return; +} + + /* * Attempt to initiate asynchronous I/O on read-ahead blocks. We must * clear BIO_ERROR and B_INVAL prior to initiating I/O . If B_CACHE is set, @@ -1900,14 +2357,11 @@ brelse(struct buf *bp) /* buffers with no memory */ if (bp->b_bufsize == 0) { - bp->b_xflags &= ~(BX_BKGRDWRITE | BX_ALTDATA); - if (bp->b_vflags & BV_BKGRDINPROG) - panic("losing buffer 1"); - bufkvafree(bp); - qindex = QUEUE_EMPTY; - bp->b_flags |= B_AGE; + buf_free(bp); + return; + } /* buffers with junk contents */ - } else if (bp->b_flags & (B_INVAL | B_NOCACHE | B_RELBUF) || + if (bp->b_flags & (B_INVAL | B_NOCACHE | B_RELBUF) || (bp->b_ioflags & BIO_ERROR)) { bp->b_xflags &= ~(BX_BKGRDWRITE | BX_ALTDATA); if (bp->b_vflags & BV_BKGRDINPROG) @@ -1927,6 +2381,8 @@ brelse(struct buf *bp) panic("brelse: not dirty"); /* unlock */ BUF_UNLOCK(bp); + if (qindex == QUEUE_CLEAN) + bufspace_wakeup(); } /* @@ -1949,6 +2405,7 @@ bqrelse(struct buf *bp) KASSERT(!(bp->b_flags & (B_CLUSTER|B_PAGING)), ("bqrelse: inappropriate B_PAGING or B_CLUSTER bp %p", bp)); + qindex = QUEUE_NONE; if (BUF_LOCKRECURSED(bp)) { /* do not release to free list */ BUF_UNLOCK(bp); @@ -1984,6 +2441,8 @@ bqrelse(struct buf *bp) out: /* unlock */ BUF_UNLOCK(bp); + if (qindex == QUEUE_CLEAN) + bufspace_wakeup(); } /* @@ -2383,297 +2842,26 @@ vfs_bio_awrite(struct buf *bp) } /* - * Ask the bufdaemon for help, or act as bufdaemon itself, when a - * locked vnode is supplied. + * getnewbuf_kva: + * + * Allocate KVA for an empty buf header according to gbflags. */ -static void -getnewbuf_bufd_help(struct vnode *vp, int gbflags, int slpflag, int slptimeo, - int defrag) -{ - struct thread *td; - char *waitmsg; - int error, fl, flags, norunbuf; - - mtx_assert(&bqclean, MA_OWNED); - - if (defrag) { - flags = VFS_BIO_NEED_BUFSPACE; - waitmsg = "nbufkv"; - } else if (bufspace >= hibufspace) { - waitmsg = "nbufbs"; - flags = VFS_BIO_NEED_BUFSPACE; - } else { - waitmsg = "newbuf"; - flags = VFS_BIO_NEED_ANY; - } - atomic_set_int(&needsbuffer, flags); - mtx_unlock(&bqclean); - - bd_speedup(); /* heeeelp */ - if ((gbflags & GB_NOWAIT_BD) != 0) - return; - - td = curthread; - rw_wlock(&nblock); - while ((needsbuffer & flags) != 0) { - if (vp != NULL && vp->v_type != VCHR && - (td->td_pflags & TDP_BUFNEED) == 0) { - rw_wunlock(&nblock); - /* - * getblk() is called with a vnode locked, and - * some majority of the dirty buffers may as - * well belong to the vnode. Flushing the - * buffers there would make a progress that - * cannot be achieved by the buf_daemon, that - * cannot lock the vnode. - */ - norunbuf = ~(TDP_BUFNEED | TDP_NORUNNINGBUF) | - (td->td_pflags & TDP_NORUNNINGBUF); - - /* - * Play bufdaemon. The getnewbuf() function - * may be called while the thread owns lock - * for another dirty buffer for the same - * vnode, which makes it impossible to use - * VOP_FSYNC() there, due to the buffer lock - * recursion. - */ - td->td_pflags |= TDP_BUFNEED | TDP_NORUNNINGBUF; - fl = buf_flush(vp, flushbufqtarget); - td->td_pflags &= norunbuf; - rw_wlock(&nblock); - if (fl != 0) - continue; - if ((needsbuffer & flags) == 0) - break; - } - error = rw_sleep(__DEVOLATILE(void *, &needsbuffer), &nblock, - (PRIBIO + 4) | slpflag, waitmsg, slptimeo); - if (error != 0) - break; - } - rw_wunlock(&nblock); -} - -static void -getnewbuf_reuse_bp(struct buf *bp, int qindex) +static int +getnewbuf_kva(struct buf *bp, int gbflags, int maxsize) { - CTR6(KTR_BUF, "getnewbuf(%p) vp %p flags %X kvasize %d bufsize %d " - "queue %d (recycling)", bp, bp->b_vp, bp->b_flags, - bp->b_kvasize, bp->b_bufsize, qindex); - mtx_assert(&bqclean, MA_NOTOWNED); + if ((gbflags & (GB_UNMAPPED | GB_KVAALLOC)) != GB_UNMAPPED) { + /* + * In order to keep fragmentation sane we only allocate kva + * in BKVASIZE chunks. XXX with vmem we can do page size. + */ + maxsize = (maxsize + BKVAMASK) & ~BKVAMASK; - /* - * Note: we no longer distinguish between VMIO and non-VMIO - * buffers. - */ - KASSERT((bp->b_flags & (B_DELWRI | B_NOREUSE)) == 0, - ("invalid buffer %p flags %#x found in queue %d", bp, bp->b_flags, - qindex)); - - /* - * When recycling a clean buffer we have to truncate it and - * release the vnode. - */ - if (qindex == QUEUE_CLEAN) { - allocbuf(bp, 0); - if (bp->b_vp != NULL) - brelvp(bp); + if (maxsize != bp->b_kvasize && + bufkva_alloc(bp, maxsize, gbflags)) + return (ENOSPC); } - - /* - * Get the rest of the buffer freed up. b_kva* is still valid - * after this operation. - */ - if (bp->b_rcred != NOCRED) { - crfree(bp->b_rcred); - bp->b_rcred = NOCRED; - } - if (bp->b_wcred != NOCRED) { - crfree(bp->b_wcred); - bp->b_wcred = NOCRED; - } - if (!LIST_EMPTY(&bp->b_dep)) - buf_deallocate(bp); - if (bp->b_vflags & BV_BKGRDINPROG) - panic("losing buffer 3"); - KASSERT(bp->b_vp == NULL, ("bp: %p still has vnode %p. qindex: %d", - bp, bp->b_vp, qindex)); - KASSERT((bp->b_xflags & (BX_VNCLEAN|BX_VNDIRTY)) == 0, - ("bp: %p still on a buffer list. xflags %X", bp, bp->b_xflags)); - KASSERT(bp->b_npages == 0, - ("bp: %p still has %d vm pages\n", bp, bp->b_npages)); - - bp->b_flags = 0; - bp->b_ioflags = 0; - bp->b_xflags = 0; - KASSERT((bp->b_flags & B_INFREECNT) == 0, - ("buf %p still counted as free?", bp)); - bp->b_vflags = 0; - bp->b_vp = NULL; - bp->b_blkno = bp->b_lblkno = 0; - bp->b_offset = NOOFFSET; - bp->b_iodone = 0; - bp->b_error = 0; - bp->b_resid = 0; - bp->b_bcount = 0; - bp->b_npages = 0; - bp->b_dirtyoff = bp->b_dirtyend = 0; - bp->b_bufobj = NULL; - bp->b_pin_count = 0; - bp->b_data = bp->b_kvabase; - bp->b_fsprivate1 = NULL; - bp->b_fsprivate2 = NULL; - bp->b_fsprivate3 = NULL; - - LIST_INIT(&bp->b_dep); -} - -static struct buf * -getnewbuf_scan(int maxsize, int defrag, int unmapped, int metadata) -{ - struct buf *bp, *nbp; - int nqindex, qindex, pass; - - KASSERT(!unmapped || !defrag, ("both unmapped and defrag")); - - pass = 0; -restart: - if (pass != 0) - atomic_add_int(&getnewbufrestarts, 1); - - nbp = NULL; - mtx_lock(&bqclean); - /* - * If we're not defragging or low on bufspace attempt to make a new - * buf from a header. - */ - if (defrag == 0 && bufspace + maxsize < hibufspace) { - nqindex = QUEUE_EMPTY; - nbp = TAILQ_FIRST(&bufqueues[nqindex]); - } - /* - * All available buffers might be clean or we need to start recycling. - */ - if (nbp == NULL) { - nqindex = QUEUE_CLEAN; - nbp = TAILQ_FIRST(&bufqueues[QUEUE_CLEAN]); - } - - /* - * Run scan, possibly freeing data and/or kva mappings on the fly - * depending. - */ - while ((bp = nbp) != NULL) { - qindex = nqindex; - - /* - * Calculate next bp (we can only use it if we do not - * release the bqlock) - */ - if ((nbp = TAILQ_NEXT(bp, b_freelist)) == NULL) { - switch (qindex) { - case QUEUE_EMPTY: - nqindex = QUEUE_CLEAN; - nbp = TAILQ_FIRST(&bufqueues[nqindex]); - if (nbp != NULL) - break; - /* FALLTHROUGH */ - case QUEUE_CLEAN: - if (metadata && pass == 0) { - pass = 1; - nqindex = QUEUE_EMPTY; - nbp = TAILQ_FIRST(&bufqueues[nqindex]); - } - /* - * nbp is NULL. - */ - break; - } - } - /* - * If we are defragging then we need a buffer with - * b_kvasize != 0. This situation occurs when we - * have many unmapped bufs. - */ - if (defrag && bp->b_kvasize == 0) - continue; - - /* - * Start freeing the bp. This is somewhat involved. nbp - * remains valid only for QUEUE_EMPTY[KVA] bp's. - */ - if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL) != 0) - continue; - /* - * BKGRDINPROG can only be set with the buf and bufobj - * locks both held. We tolerate a race to clear it here. - */ - if (bp->b_vflags & BV_BKGRDINPROG) { - BUF_UNLOCK(bp); - continue; - } - - /* - * Requeue the background write buffer with error. - */ - if ((bp->b_vflags & BV_BKGRDERR) != 0) { - bremfreel(bp); - mtx_unlock(&bqclean); - bqrelse(bp); - continue; - } - - KASSERT(bp->b_qindex == qindex, - ("getnewbuf: inconsistent queue %d bp %p", qindex, bp)); - - bremfreel(bp); - mtx_unlock(&bqclean); - - /* - * NOTE: nbp is now entirely invalid. We can only restart - * the scan from this point on. - */ - getnewbuf_reuse_bp(bp, qindex); - mtx_assert(&bqclean, MA_NOTOWNED); - - /* - * If we are defragging then free the buffer. - */ - if (defrag) { - bp->b_flags |= B_INVAL; - brelse(bp); - defrag = 0; - goto restart; - } - - /* - * Notify any waiters for the buffer lock about - * identity change by freeing the buffer. - */ - if (qindex == QUEUE_CLEAN && BUF_LOCKWAITERS(bp)) { - bp->b_flags |= B_INVAL; - brelse(bp); - goto restart; - } - - if (metadata) - break; - - /* - * If we are overcomitted then recover the buffer and its - * KVM space. This occurs in rare situations when multiple - * processes are blocked in getnewbuf() or allocbuf(). - */ - if (bufspace >= hibufspace && bp->b_kvasize != 0) { - bp->b_flags |= B_INVAL; - brelse(bp); - goto restart; - } - break; - } - return (bp); + return (0); } /* @@ -2682,86 +2870,54 @@ restart: * Find and initialize a new buffer header, freeing up existing buffers * in the bufqueues as necessary. The new buffer is returned locked. * - * Important: B_INVAL is not set. If the caller wishes to throw the - * buffer away, the caller must set B_INVAL prior to calling brelse(). - * * We block if: * We have insufficient buffer headers * We have insufficient buffer space * buffer_arena is too fragmented ( space reservation fails ) * If we have to flush dirty buffers ( but we try to avoid this ) + * + * The caller is responsible for releasing the reserved bufspace after + * allocbuf() is called. */ static struct buf * -getnewbuf(struct vnode *vp, int slpflag, int slptimeo, int size, int maxsize, - int gbflags) +getnewbuf(struct vnode *vp, int slpflag, int slptimeo, int maxsize, int gbflags) { struct buf *bp; - int defrag, metadata; + bool metadata, reserved; KASSERT((gbflags & (GB_UNMAPPED | GB_KVAALLOC)) != GB_KVAALLOC, ("GB_KVAALLOC only makes sense with GB_UNMAPPED")); if (!unmapped_buf_allowed) gbflags &= ~(GB_UNMAPPED | GB_KVAALLOC); - defrag = 0; if (vp == NULL || (vp->v_vflag & (VV_MD | VV_SYSTEM)) != 0 || vp->v_type == VCHR) - metadata = 1; + metadata = true; else - metadata = 0; - /* - * We can't afford to block since we might be holding a vnode lock, - * which may prevent system daemons from running. We deal with - * low-memory situations by proactively returning memory and running - * async I/O rather then sync I/O. - */ + metadata = false; atomic_add_int(&getnewbufcalls, 1); -restart: - bp = getnewbuf_scan(maxsize, defrag, (gbflags & (GB_UNMAPPED | - GB_KVAALLOC)) == GB_UNMAPPED, metadata); - if (bp != NULL) - defrag = 0; + reserved = false; + do { + if (reserved == false && + bufspace_reserve(maxsize, metadata) != 0) + continue; + reserved = true; + if ((bp = buf_alloc()) == NULL) + continue; + if (getnewbuf_kva(bp, gbflags, maxsize) == 0) + return (bp); + break; + } while(buf_scan(false) == 0); - /* - * If we exhausted our list, sleep as appropriate. We may have to - * wakeup various daemons and write out some dirty buffers. - * - * Generally we are sleeping due to insufficient buffer space. - */ - if (bp == NULL) { - mtx_assert(&bqclean, MA_OWNED); - getnewbuf_bufd_help(vp, gbflags, slpflag, slptimeo, defrag); - mtx_assert(&bqclean, MA_NOTOWNED); - } else if ((gbflags & (GB_UNMAPPED | GB_KVAALLOC)) == GB_UNMAPPED) { - mtx_assert(&bqclean, MA_NOTOWNED); - - bufkvafree(bp); - atomic_add_int(&bufreusecnt, 1); - } else { - mtx_assert(&bqclean, MA_NOTOWNED); - - /* - * We finally have a valid bp. We aren't quite out of the - * woods, we still have to reserve kva space. In order to - * keep fragmentation sane we only allocate kva in BKVASIZE - * chunks. - */ - maxsize = (maxsize + BKVAMASK) & ~BKVAMASK; - - if (maxsize != bp->b_kvasize && - bufkvaalloc(bp, maxsize, gbflags)) { - defrag = 1; - bp->b_flags |= B_INVAL; - brelse(bp); - goto restart; - } else if ((gbflags & (GB_UNMAPPED | GB_KVAALLOC)) == - (GB_UNMAPPED | GB_KVAALLOC)) { - bp->b_data = unmapped_buf; - BUF_CHECK_UNMAPPED(bp); - } - atomic_add_int(&bufreusecnt, 1); + if (reserved) + bufspace_release(maxsize); + if (bp != NULL) { + bp->b_flags |= B_INVAL; + brelse(bp); } - return (bp); + bufspace_wait(vp, gbflags, slpflag, slptimeo); + + return (NULL); } /* @@ -2771,7 +2927,6 @@ restart: * update daemon but if it cannot keep up this process starts to * take the load in an attempt to prevent getnewbuf() from blocking. */ - static struct kproc_desc buf_kp = { "bufdaemon", buf_daemon, @@ -2902,19 +3057,19 @@ flushbufqueues(struct vnode *lvp, int target, int flushdeps) bp = NULL; sentinel = malloc(sizeof(struct buf), M_TEMP, M_WAITOK | M_ZERO); sentinel->b_qindex = QUEUE_SENTINEL; - mtx_lock(&bqdirty); + mtx_lock(&bqlocks[queue]); TAILQ_INSERT_HEAD(&bufqueues[queue], sentinel, b_freelist); - mtx_unlock(&bqdirty); + mtx_unlock(&bqlocks[queue]); while (flushed != target) { maybe_yield(); - mtx_lock(&bqdirty); + mtx_lock(&bqlocks[queue]); bp = TAILQ_NEXT(sentinel, b_freelist); if (bp != NULL) { TAILQ_REMOVE(&bufqueues[queue], sentinel, b_freelist); TAILQ_INSERT_AFTER(&bufqueues[queue], bp, sentinel, b_freelist); } else { - mtx_unlock(&bqdirty); + mtx_unlock(&bqlocks[queue]); break; } /* @@ -2926,11 +3081,11 @@ flushbufqueues(struct vnode *lvp, int target, int flushdeps) */ if (bp->b_qindex == QUEUE_SENTINEL || (lvp != NULL && bp->b_vp != lvp)) { - mtx_unlock(&bqdirty); + mtx_unlock(&bqlocks[queue]); continue; } error = BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL); - mtx_unlock(&bqdirty); + mtx_unlock(&bqlocks[queue]); if (error != 0) continue; if (bp->b_pin_count > 0) { @@ -3013,9 +3168,9 @@ flushbufqueues(struct vnode *lvp, int target, int flushdeps) vn_finished_write(mp); BUF_UNLOCK(bp); } - mtx_lock(&bqdirty); + mtx_lock(&bqlocks[queue]); TAILQ_REMOVE(&bufqueues[queue], sentinel, b_freelist); - mtx_unlock(&bqdirty); + mtx_unlock(&bqlocks[queue]); free(sentinel, M_TEMP); return (flushed); } @@ -3196,7 +3351,6 @@ vfs_setdirty_locked_object(struct buf *bp) static void bp_unmapped_get_kva(struct buf *bp, daddr_t blkno, int size, int gbflags) { - struct buf *scratch_bp; int bsize, maxsize, need_mapping, need_kva; off_t offset; @@ -3229,37 +3383,16 @@ bp_unmapped_get_kva(struct buf *bp, daddr_t blkno, int size, int gbflags) maxsize = size + (offset & PAGE_MASK); maxsize = imax(maxsize, bsize); -mapping_loop: - if (bufkvaalloc(bp, maxsize, gbflags)) { - /* - * Request defragmentation. getnewbuf() returns us the - * allocated space by the scratch buffer KVA. - */ - scratch_bp = getnewbuf(bp->b_vp, 0, 0, size, maxsize, gbflags | - (GB_UNMAPPED | GB_KVAALLOC)); - if (scratch_bp == NULL) { - if ((gbflags & GB_NOWAIT_BD) != 0) { - /* - * XXXKIB: defragmentation cannot - * succeed, not sure what else to do. - */ - panic("GB_NOWAIT_BD and GB_UNMAPPED %p", bp); - } - atomic_add_int(&mappingrestarts, 1); - goto mapping_loop; + while (bufkva_alloc(bp, maxsize, gbflags) != 0) { + if ((gbflags & GB_NOWAIT_BD) != 0) { + /* + * XXXKIB: defragmentation cannot + * succeed, not sure what else to do. + */ + panic("GB_NOWAIT_BD and GB_UNMAPPED %p", bp); } - KASSERT(scratch_bp->b_kvabase != unmapped_buf, - ("scratch bp has no KVA %p", scratch_bp)); - /* Grab pointers. */ - bp->b_kvabase = scratch_bp->b_kvabase; - bp->b_kvasize = scratch_bp->b_kvasize; - bp->b_data = scratch_bp->b_data; - - /* Get rid of the scratch buffer. */ - scratch_bp->b_kvasize = 0; - scratch_bp->b_flags |= B_INVAL; - scratch_bp->b_data = scratch_bp->b_kvabase = unmapped_buf; - brelse(scratch_bp); + atomic_add_int(&mappingrestarts, 1); + bufspace_wait(bp->b_vp, gbflags, 0, 0); } has_addr: if (need_mapping) { @@ -3486,7 +3619,7 @@ loop: } maxsize = imax(maxsize, bsize); - bp = getnewbuf(vp, slpflag, slptimeo, size, maxsize, flags); + bp = getnewbuf(vp, slpflag, slptimeo, maxsize, flags); if (bp == NULL) { if (slpflag || slptimeo) return NULL; @@ -3510,6 +3643,7 @@ loop: BO_UNLOCK(bo); bp->b_flags |= B_INVAL; brelse(bp); + bufspace_release(maxsize); goto loop; } @@ -3543,6 +3677,7 @@ loop: } allocbuf(bp, size); + bufspace_release(maxsize); bp->b_flags &= ~B_DONE; } CTR4(KTR_BUF, "getblk(%p, %ld, %d) = %p", vp, (long)blkno, size, bp); @@ -3564,12 +3699,13 @@ geteblk(int size, int flags) int maxsize; maxsize = (size + BKVAMASK) & ~BKVAMASK; - while ((bp = getnewbuf(NULL, 0, 0, size, maxsize, flags)) == NULL) { + while ((bp = getnewbuf(NULL, 0, 0, maxsize, flags)) == NULL) { if ((flags & GB_NOWAIT_BD) && (curthread->td_pflags & TDP_BUFNEED) != 0) return (NULL); } allocbuf(bp, size); + bufspace_release(maxsize); bp->b_flags |= B_INVAL; /* b_dep cleared by getnewbuf() */ BUF_ASSERT_HELD(bp); return (bp); @@ -3595,7 +3731,7 @@ vfs_nonvmio_truncate(struct buf *bp, int newbsize) return; } vm_hold_free_pages(bp, newbsize); - bufspaceadjust(bp, newbsize); + bufspace_adjust(bp, newbsize); } /* @@ -3646,7 +3782,7 @@ vfs_nonvmio_extend(struct buf *bp, int newbsize) bcopy(origbuf, bp->b_data, origbufsize); free(origbuf, M_BIOBUF); } - bufspaceadjust(bp, newbsize); + bufspace_adjust(bp, newbsize); } /* @@ -3708,7 +3844,7 @@ allocbuf(struct buf *bp, int size) /* XXX This looks as if it should be newbsize > b_bufsize */ else if (size > bp->b_bcount) vfs_vmio_extend(bp, desiredpages, size); - bufspaceadjust(bp, newbsize); + bufspace_adjust(bp, newbsize); } bp->b_bcount = size; /* requested buffer size. */ return (1); @@ -4596,7 +4732,7 @@ DB_COMMAND(countfreebufs, db_coundfreebufs) for (i = 0; i < nbuf; i++) { bp = &buf[i]; - if ((bp->b_flags & B_INFREECNT) != 0) + if (bp->b_qindex == QUEUE_EMPTY) nfree++; else used++; diff --git a/sys/kern/vfs_mountroot.c b/sys/kern/vfs_mountroot.c index 73ed5b652b2..aecddbda2a6 100644 --- a/sys/kern/vfs_mountroot.c +++ b/sys/kern/vfs_mountroot.c @@ -468,9 +468,9 @@ parse_dir_ask(char **conf) printf("\n"); printf(" eg. ufs:/dev/da0s1a\n"); printf(" zfs:tank\n"); - printf(" cd9660:/dev/acd0 ro\n"); + printf(" cd9660:/dev/cd0 ro\n"); printf(" (which is equivalent to: "); - printf("mount -t cd9660 -o ro /dev/acd0 /)\n"); + printf("mount -t cd9660 -o ro /dev/cd0 /)\n"); printf("\n"); printf(" ? List valid disk boot devices\n"); printf(" . Yield 1 second (for background tasks)\n"); @@ -837,7 +837,7 @@ vfs_mountroot_conf0(struct sbuf *sb) if (boothowto & RB_CDROM) { sbuf_printf(sb, "cd9660:/dev/cd0 ro\n"); sbuf_printf(sb, ".timeout 0\n"); - sbuf_printf(sb, "cd9660:/dev/acd0 ro\n"); + sbuf_printf(sb, "cd9660:/dev/cd1 ro\n"); sbuf_printf(sb, ".timeout %d\n", root_mount_timeout); } s = kern_getenv("vfs.root.mountfrom"); diff --git a/sys/mips/atheros/if_arge.c b/sys/mips/atheros/if_arge.c index d0676f2ea43..446cd64d328 100644 --- a/sys/mips/atheros/if_arge.c +++ b/sys/mips/atheros/if_arge.c @@ -298,6 +298,29 @@ arge_attach_sysctl(device_t dev) "tx_pkts_unaligned", CTLFLAG_RW, &sc->stats.tx_pkts_unaligned, 0, "number of TX unaligned packets"); + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "tx_pkts_unaligned_start", CTLFLAG_RW, &sc->stats.tx_pkts_unaligned_start, + 0, "number of TX unaligned packets (start)"); + + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "tx_pkts_unaligned_len", CTLFLAG_RW, &sc->stats.tx_pkts_unaligned_len, + 0, "number of TX unaligned packets (len)"); + + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "tx_pkts_nosegs", CTLFLAG_RW, &sc->stats.tx_pkts_nosegs, + 0, "number of TX packets fail with no ring slots avail"); + + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "intr_stray_filter", CTLFLAG_RW, &sc->stats.intr_stray, + 0, "number of stray interrupts (filter)"); + + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "intr_stray_intr", CTLFLAG_RW, &sc->stats.intr_stray2, + 0, "number of stray interrupts (intr)"); + + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "intr_ok", CTLFLAG_RW, &sc->stats.intr_ok, + 0, "number of OK interrupts"); #ifdef ARGE_DEBUG SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_prod", CTLFLAG_RW, &sc->arge_cdata.arge_tx_prod, 0, ""); @@ -626,6 +649,22 @@ arge_attach(device_t dev) local_macstr = NULL; } + /* + * Hardware workarounds. + */ + switch (ar71xx_soc) { + case AR71XX_SOC_QCA9556: + case AR71XX_SOC_QCA9558: + /* Arbitrary alignment */ + sc->arge_hw_flags |= ARGE_HW_FLG_TX_DESC_ALIGN_1BYTE; + sc->arge_hw_flags |= ARGE_HW_FLG_RX_DESC_ALIGN_1BYTE; + break; + default: + sc->arge_hw_flags |= ARGE_HW_FLG_TX_DESC_ALIGN_4BYTE; + sc->arge_hw_flags |= ARGE_HW_FLG_RX_DESC_ALIGN_4BYTE; + break; + } + /* * Some units (eg the TP-Link WR-1043ND) do not have a convenient * EEPROM location to read the ethernet MAC address from. @@ -825,6 +864,9 @@ arge_attach(device_t dev) ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0, FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT); + /* + * SoC specific bits. + */ switch (ar71xx_soc) { case AR71XX_SOC_AR7240: case AR71XX_SOC_AR7241: @@ -1351,24 +1393,35 @@ arge_init_locked(struct arge_softc *sc) * Return whether the mbuf chain is correctly aligned * for the arge TX engine. * - * The TX engine requires each fragment to be aligned to a - * 4 byte boundary and the size of each fragment except - * the last to be a multiple of 4 bytes. + * All the MACs have a length requirement: any non-final + * fragment (ie, descriptor with MORE bit set) needs to have + * a length divisible by 4. * - * XXX TODO: I believe this is only a bug on the AR71xx and - * AR913x MACs. The later MACs (AR724x and later) does not - * need this workaround. + * The AR71xx, AR913x require the start address also be + * DWORD aligned. The later MACs don't. */ static int -arge_mbuf_chain_is_tx_aligned(struct mbuf *m0) +arge_mbuf_chain_is_tx_aligned(struct arge_softc *sc, struct mbuf *m0) { struct mbuf *m; for (m = m0; m != NULL; m = m->m_next) { - if((mtod(m, intptr_t) & 3) != 0) + /* + * Only do this for chips that require it. + */ + if ((sc->arge_hw_flags & ARGE_HW_FLG_TX_DESC_ALIGN_4BYTE) && + (mtod(m, intptr_t) & 3) != 0) { + sc->stats.tx_pkts_unaligned_start++; return 0; - if ((m->m_next != NULL) && ((m->m_len & 0x03) != 0)) + } + + /* + * All chips have this requirement for length. + */ + if ((m->m_next != NULL) && ((m->m_len & 0x03) != 0)) { + sc->stats.tx_pkts_unaligned_len++; return 0; + } } return 1; } @@ -1389,15 +1442,10 @@ arge_encap(struct arge_softc *sc, struct mbuf **m_head) ARGE_LOCK_ASSERT(sc); /* - * Fix mbuf chain, all fragments should be 4 bytes aligned and - * even 4 bytes - * - * XXX TODO: I believe this is only a bug on the AR71xx and - * AR913x MACs. The later MACs (AR724x and later) does not - * need this workaround. + * Fix mbuf chain based on hardware alignment constraints. */ m = *m_head; - if (! arge_mbuf_chain_is_tx_aligned(m)) { + if (! arge_mbuf_chain_is_tx_aligned(sc, m)) { sc->stats.tx_pkts_unaligned++; m = m_defrag(*m_head, M_NOWAIT); if (m == NULL) { @@ -1427,6 +1475,7 @@ arge_encap(struct arge_softc *sc, struct mbuf **m_head) /* Check number of available descriptors. */ if (sc->arge_cdata.arge_tx_cnt + nsegs >= (ARGE_TX_RING_COUNT - 1)) { bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); + sc->stats.tx_pkts_nosegs++; return (ENOBUFS); } @@ -1444,7 +1493,9 @@ arge_encap(struct arge_softc *sc, struct mbuf **m_head) desc = &sc->arge_rdata.arge_tx_ring[prod]; desc->packet_ctrl = ARGE_DMASIZE(txsegs[i].ds_len); - if (txsegs[i].ds_addr & 3) + /* XXX Note: only relevant for older MACs; but check length! */ + if ((sc->arge_hw_flags & ARGE_HW_FLG_TX_DESC_ALIGN_4BYTE) && + (txsegs[i].ds_addr & 3)) panic("TX packet address unaligned\n"); desc->packet_addr = txsegs[i].ds_addr; @@ -1715,6 +1766,16 @@ arge_dma_alloc(struct arge_softc *sc) struct arge_txdesc *txd; struct arge_rxdesc *rxd; int error, i; + int arge_tx_align, arge_rx_align; + + /* Assume 4 byte alignment by default */ + arge_tx_align = 4; + arge_rx_align = 4; + + if (sc->arge_hw_flags & ARGE_HW_FLG_TX_DESC_ALIGN_1BYTE) + arge_tx_align = 1; + if (sc->arge_hw_flags & ARGE_HW_FLG_RX_DESC_ALIGN_1BYTE) + arge_rx_align = 1; /* Create parent DMA tag. */ error = bus_dma_tag_create( @@ -1775,7 +1836,7 @@ arge_dma_alloc(struct arge_softc *sc) /* Create tag for Tx buffers. */ error = bus_dma_tag_create( sc->arge_cdata.arge_parent_tag, /* parent */ - sizeof(uint32_t), 0, /* alignment, boundary */ + arge_tx_align, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ @@ -1793,7 +1854,7 @@ arge_dma_alloc(struct arge_softc *sc) /* Create tag for Rx buffers. */ error = bus_dma_tag_create( sc->arge_cdata.arge_parent_tag, /* parent */ - ARGE_RX_ALIGN, 0, /* alignment, boundary */ + arge_rx_align, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ @@ -2108,6 +2169,11 @@ arge_newbuf(struct arge_softc *sc, int idx) if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = MCLBYTES; + + /* + * Add extra space to "adjust" (copy) the packet back to be aligned + * for purposes of IPv4/IPv6 header contents. + */ m_adj(m, sizeof(uint64_t)); if (bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_rx_tag, @@ -2126,7 +2192,8 @@ arge_newbuf(struct arge_softc *sc, int idx) sc->arge_cdata.arge_rx_sparemap = map; rxd->rx_m = m; desc = rxd->desc; - if (segs[0].ds_addr & 3) + if ((sc->arge_hw_flags & ARGE_HW_FLG_RX_DESC_ALIGN_4BYTE) && + segs[0].ds_addr & 3) panic("RX packet address unaligned"); desc->packet_addr = segs[0].ds_addr; desc->packet_ctrl = ARGE_DESC_EMPTY | ARGE_DMASIZE(segs[0].ds_len); @@ -2331,10 +2398,12 @@ arge_intr_filter(void *arg) if (status & DMA_INTR_ALL) { sc->arge_intr_status |= status; ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); + sc->stats.intr_ok++; return (FILTER_SCHEDULE_THREAD); } sc->arge_intr_status = 0; + sc->stats.intr_stray++; return (FILTER_STRAY); } @@ -2355,8 +2424,10 @@ arge_intr(void *arg) /* * Is it our interrupt at all? */ - if (status == 0) + if (status == 0) { + sc->stats.intr_stray2++; return; + } if (status & DMA_INTR_RX_BUS_ERROR) { ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR); diff --git a/sys/mips/atheros/if_argevar.h b/sys/mips/atheros/if_argevar.h index 048c375ae7b..ae94588b97d 100644 --- a/sys/mips/atheros/if_argevar.h +++ b/sys/mips/atheros/if_argevar.h @@ -37,7 +37,10 @@ #define ARGE_TX_DMA_SIZE ARGE_TX_RING_COUNT * sizeof(struct arge_desc) #define ARGE_MAXFRAGS 8 #define ARGE_RING_ALIGN sizeof(struct arge_desc) -#define ARGE_RX_ALIGN sizeof(uint32_t) +#define ARGE_RX_ALIGN_4BYTE sizeof(uint32_t) +#define ARGE_RX_ALIGN_1BYTE sizeof(char) +#define ARGE_TX_ALIGN_4BYTE sizeof(uint32_t) +#define ARGE_TX_ALIGN_1BYTE sizeof(char) #define ARGE_MAXFRAGS 8 #define ARGE_TX_RING_ADDR(sc, i) \ ((sc)->arge_rdata.arge_tx_ring_paddr + sizeof(struct arge_desc) * (i)) @@ -149,6 +152,22 @@ struct arge_pll_data { uint32_t pll_1000; }; +/* + * Hardware specific behaviours. + */ + +/* + * Older chips support 4 byte only transmit and receive + * addresses. + * + * Later chips support arbitrary TX and later later, + * arbitrary RX addresses. + */ +#define ARGE_HW_FLG_TX_DESC_ALIGN_4BYTE 0x00000001 +#define ARGE_HW_FLG_RX_DESC_ALIGN_4BYTE 0x00000002 +#define ARGE_HW_FLG_TX_DESC_ALIGN_1BYTE 0x00000004 +#define ARGE_HW_FLG_RX_DESC_ALIGN_1BYTE 0x00000008 + struct arge_softc { struct ifnet *arge_ifp; /* interface info */ device_t arge_dev; @@ -180,13 +199,20 @@ struct arge_softc { uint32_t arge_intr_status; int arge_mac_unit; int arge_if_flags; + uint32_t arge_hw_flags; uint32_t arge_debug; uint32_t arge_mdiofreq; struct { uint32_t tx_pkts_unaligned; + uint32_t tx_pkts_unaligned_start; + uint32_t tx_pkts_unaligned_len; + uint32_t tx_pkts_nosegs; uint32_t tx_pkts_aligned; uint32_t rx_overflow; uint32_t tx_underflow; + uint32_t intr_stray; + uint32_t intr_stray2; + uint32_t intr_ok; } stats; }; diff --git a/sys/mips/conf/AR933X_BASE b/sys/mips/conf/AR933X_BASE index 6669de668ef..38f847db12e 100644 --- a/sys/mips/conf/AR933X_BASE +++ b/sys/mips/conf/AR933X_BASE @@ -40,6 +40,9 @@ options SCSI_NO_OP_STRINGS # .. And no sysctl strings options NO_SYSCTL_DESCR +# For small memory footprints +options VM_KMEM_SIZE_SCALE=1 + # Limit IO size options NBUF=128 diff --git a/sys/mips/conf/ERL b/sys/mips/conf/ERL new file mode 100644 index 00000000000..2afd12116ca --- /dev/null +++ b/sys/mips/conf/ERL @@ -0,0 +1,212 @@ +# +# ERL - EdgeRouter Lite kernel config +# Based on configuration from http://rtfm.net/FreeBSD/ERL +# +# For more information on this file, please read the config(5) manual page, +# and/or the handbook section on Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +ident ERL + +makeoptions ARCH_FLAGS="-march=octeon -mabi=64" +makeoptions LDSCRIPT_NAME=ldscript.mips.octeon1 + +# Don't build any modules yet. +makeoptions MODULES_OVERRIDE="" +makeoptions KERNLOADADDR=0xffffffff80100000 + +# We don't need to build a trampolined version of the kernel. +makeoptions WITHOUT_KERNEL_TRAMPOLINE=1 + +include "../cavium/std.octeon1" + +hints "OCTEON1.hints" #Default places to look for devices. + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols + +# Board-specific support that cannot be auto-detected at runtime. +#options OCTEON_VENDOR_LANNER # Support for Lanner boards. +#options OCTEON_VENDOR_RADISYS # Support for Radisys boards. +options OCTEON_VENDOR_UBIQUITI # Support for Ubiquiti boards. +#options OCTEON_VENDOR_GEFES # Support for GE LANIC boards +#options OCTEON_BOARD_CAPK_0100ND # Support for CAPK-0100nd. + +# Compile for a specified Octeon model. If not specified, support for +# detection at runtime will be used instead, which may give inferior +# performance. +# +# See sys/contrib/octeon-sdk/octeon-model.h for possible values. +options OCTEON_MODEL=OCTEON_CN50XX_PASS1 + +options SCHED_ULE # ULE scheduler +options PREEMPTION # Enable kernel thread preemption +options INET # InterNETworking +options INET6 # IPv6 communications protocols +options SCTP # Stream Control Transmission Protocol +options FFS # Berkeley Fast Filesystem +options SOFTUPDATES # Enable FFS soft updates support +options UFS_ACL # Support for access control lists +options UFS_DIRHASH # Improve performance on big directories +options UFS_GJOURNAL # Enable gjournal-based UFS journaling +options MD_ROOT # MD is a potential root device +options NFSCL # Network Filesystem Client +options NFSD # Network Filesystem Server +options NFSLOCKD # Network Lock Manager +options NFS_ROOT # NFS usable as /, requires NFSCL +options MSDOSFS # MSDOS Filesystem +options CD9660 # ISO 9660 Filesystem +options PROCFS # Process filesystem (requires PSEUDOFS) +options PSEUDOFS # Pseudo-filesystem framework +options GEOM_PART_GPT # GUID Partition Tables. +options GEOM_LABEL # Provides labelization +options COMPAT_FREEBSD32 # Compatible with o32 binaries +options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI +options KTRACE # ktrace(1) support +options STACK # stack(9) support +options SYSVSHM # SYSV-style shared memory +options SYSVMSG # SYSV-style message queues +options SYSVSEM # SYSV-style semaphores +options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions +options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed. +options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4) +options AUDIT # Security event auditing +options MAC # TrustedBSD MAC Framework +#options KDTRACE_FRAME # Ensure frames are compiled in +#options KDTRACE_HOOKS # Kernel DTrace hooks +options INCLUDE_CONFIG_FILE # Include this file in kernel +options NO_SWAPPING # Disable support for paging +options TMPFS # Temporary file system + +# Debugging for use in -current +#options KDB # Enable kernel debugger support. +#options DDB # Support DDB. +#options GDB # Support remote GDB. +#options DEADLKRES # Enable the deadlock resolver +#options INVARIANTS # Enable calls of extra sanity checking +#options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS +#options WITNESS # Enable checks to detect deadlocks and cycles +#options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed +#options MALLOC_DEBUG_MAXZONES=8 # Separate malloc(9) zones + +# Make an SMP-capable kernel by default +options SMP # Symmetric MultiProcessor Kernel + +options ROOTDEVNAME=\"ufs:da0s2a\" # Default root filesystem. + +# ATA/SCSI peripherals +device scbus # SCSI bus (required for ATA/SCSI) +device ch # SCSI media changers +device da # Direct Access (disks) +device sa # Sequential Access (tape etc) +device cd # CD +device pass # Passthrough device (direct ATA/SCSI access) +device ses # Enclosure Services (SES and SAF-TE) + +# Serial (COM) ports +device uart # Generic UART driver + +# On-board Cavium Octeon Ethernet. +# NOTE: Be sure to keep the 'device miibus' line in order to use these NICs! +device octe + +# Cavium Octeon management Ethernet. +device octm + +# Switch PHY support for the octe driver. These currently present a VLAN per +# physical port, but may eventually provide support for DSA or similar instead. +#device mv88e61xxphy # Marvell 88E61XX + +# Wireless NIC cards +device wlan # 802.11 support +options IEEE80211_DEBUG # enable debug msgs +options IEEE80211_AMPDU_AGE # age frames in AMPDU reorder q's +options IEEE80211_SUPPORT_MESH # enable 802.11s draft support +device wlan_wep # 802.11 WEP support +device wlan_ccmp # 802.11 CCMP support +device wlan_tkip # 802.11 TKIP support +device wlan_amrr # AMRR transmit rate control algorithm +#device ath # Atheros NIC's +#device ath_pci # Atheros pci/cardbus glue +#device ath_hal # pci/cardbus chip support +#options AH_SUPPORT_AR5416 # enable AR5416 tx/rx descriptors +#device ath_rate_sample # SampleRate tx rate control for ath + +# Pseudo devices. +device loop # Network loopback +device random # Entropy device +device ether # Ethernet support +device vlan # 802.1Q VLAN support +device tun # Packet tunnel. +device md # Memory "disks" +device gif # IPv6 and IPv4 tunneling +device firmware # firmware assist module + +# The `bpf' device enables the Berkeley Packet Filter. +# Be aware of the administrative consequences of enabling this! +# Note that 'bpf' is required for DHCP. +device bpf # Berkeley packet filter + +# Hardware watchdog support. +#device octeon_wdog # Octeon hardware watchdog + +# USB support +options USB_DEBUG # enable debug msgs +device octusb # Cavium Octeon on-board USB interface (USB 2.0) +device uhci # UHCI PCI->USB interface +device ohci # OHCI PCI->USB interface +device ehci # EHCI PCI->USB interface (USB 2.0) +device usb # USB Bus (required) +#device udbp # USB Double Bulk Pipe devices +device uhid # "Human Interface Devices" +device ulpt # Printer +device umass # Disks/Mass storage - Requires scbus and da +device ums # Mouse +device urio # Diamond Rio 500 MP3 player +# USB Serial devices +device u3g # USB-based 3G modems (Option, Huawei, Sierra) +device uark # Technologies ARK3116 based serial adapters +device ubsa # Belkin F5U103 and compatible serial adapters +device uftdi # For FTDI usb serial adapters +device uipaq # Some WinCE based devices +device uplcom # Prolific PL-2303 serial adapters +device uslcom # SI Labs CP2101/CP2102 serial adapters +device uvisor # Visor and Palm devices +device uvscom # USB serial support for DDI pocket's PHS +# USB Ethernet, requires miibus +device miibus # MII bus support +device aue # ADMtek USB Ethernet +device axe # ASIX Electronics USB Ethernet +device cdce # Generic USB over Ethernet +device cue # CATC USB Ethernet +device kue # Kawasaki LSI USB Ethernet +device rue # RealTek RTL8150 USB Ethernet +device udav # Davicom DM9601E USB +# USB Wireless +device rum # Ralink Technology RT2501USB wireless NICs +device uath # Atheros AR5523 wireless NICs +device ural # Ralink Technology RT2500USB wireless NICs +device zyd # ZyDAS zd1211/zd1211b wireless NICs + +# crypto subsystem +device crypto # core crypto support +device cryptodev # /dev/crypto for access to h/w +device cryptocteon # Octeon coprocessor 2 crypto offload + +# GPIO support +#device gpio + +# PMC support +#device hwpmc diff --git a/sys/mips/conf/TP-MR3020 b/sys/mips/conf/TP-MR3020 index a19f41bcb07..b388e9bbebb 100644 --- a/sys/mips/conf/TP-MR3020 +++ b/sys/mips/conf/TP-MR3020 @@ -44,9 +44,6 @@ device arswitch # redboot stuff. options AR71XX_ENV_UBOOT -# uzip - to boot natively from flash -device geom_uncompress - # Used for the static uboot partition map device geom_map diff --git a/sys/mips/conf/TP-MR3020.hints b/sys/mips/conf/TP-MR3020.hints index 9e8946f2a0d..1ec95474619 100644 --- a/sys/mips/conf/TP-MR3020.hints +++ b/sys/mips/conf/TP-MR3020.hints @@ -26,18 +26,18 @@ hint.arswitch.0.is_gmii=1 # arge1 <-> switch PHY is GMII # arge0 - MII, autoneg, phy(4) hint.arge.0.phymask=0x10 # PHY4 hint.arge.0.mdio=mdioproxy1 # .. off of the switch mdiobus +hint.arge.0.eeprommac=0x1fff0000 # arge1 - GMII, 1000/full hint.arge.1.phymask=0x0 # No directly mapped PHYs hint.arge.1.media=1000 hint.arge.1.fduplex=1 +hint.arge.1.eeprommac=0x1fff0006 # Where the ART is - last 64k in the flash # 0x9fff1000 ? -hint.ath.0.eepromaddr=0x1fff1000 +hint.ath.0.eepromaddr=0x1fff0000 hint.ath.0.eepromsize=16384 - -hint.ar71xx.0.eeprom_mac_addr=0x1f01fc00 # The board 4MiB flash layout in uboot env: # diff --git a/sys/modules/cloudabi64/Makefile b/sys/modules/cloudabi64/Makefile index 1e1a058acc7..a340e50439c 100644 --- a/sys/modules/cloudabi64/Makefile +++ b/sys/modules/cloudabi64/Makefile @@ -1,7 +1,7 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../compat/cloudabi64 -.PATH: ${.CURDIR}/../../${MACHINE_CPUARCH}/cloudabi64 +.PATH: ${.CURDIR}/../../${MACHINE}/cloudabi64 KMOD= cloudabi64 SRCS= cloudabi64_fd.c cloudabi64_poll.c cloudabi64_sock.c \ diff --git a/sys/modules/cxgbe/if_cxgbe/Makefile b/sys/modules/cxgbe/if_cxgbe/Makefile index a66e45a045f..df343b27ea7 100644 --- a/sys/modules/cxgbe/if_cxgbe/Makefile +++ b/sys/modules/cxgbe/if_cxgbe/Makefile @@ -11,6 +11,7 @@ SRCS+= device_if.h SRCS+= opt_inet.h SRCS+= opt_inet6.h SRCS+= opt_ofed.h +SRCS+= opt_rss.h SRCS+= pci_if.h SRCS+= t4_hw.c SRCS+= t4_l2t.c diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c index 0f286717e07..095f87066a9 100644 --- a/sys/net/if_lagg.c +++ b/sys/net/if_lagg.c @@ -916,7 +916,8 @@ lagg_port_destroy(struct lagg_port *lp, int rundelport) * iflladdr_event. */ sc->sc_primary = lp0; - lagg_port_lladdr(lp0, lladdr); + if (lp0 != NULL) + lagg_port_lladdr(lp0, lladdr); } /* Remove any pending lladdr changes from the queue */ diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index ea90dc88914..747da412abd 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1552,6 +1552,8 @@ extern void pf_print_state(struct pf_state *); extern void pf_print_flags(u_int8_t); extern u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t, u_int8_t); +extern u_int16_t pf_proto_cksum_fixup(struct mbuf *, u_int16_t, + u_int16_t, u_int16_t, u_int8_t); VNET_DECLARE(struct ifnet *, sync_ifp); #define V_sync_ifp VNET(sync_ifp); @@ -1581,6 +1583,9 @@ u_int32_t pf_new_isn(struct pf_state *); void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *, sa_family_t); void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); +void pf_change_proto_a(struct mbuf *, void *, u_int16_t *, u_int32_t, + u_int8_t); +void pf_change_tcp_a(struct mbuf *, void *, u_int16_t *, u_int32_t); void pf_send_deferred_syn(struct pf_state *); int pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *, struct pf_addr *, sa_family_t); diff --git a/sys/net/route.c b/sys/net/route.c index 4035b03db62..2bcd4ed8713 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -931,13 +931,6 @@ rt_flushifroutes(struct ifnet *ifp) #define ifpaddr info->rti_info[RTAX_IFP] #define flags info->rti_flags -int -rt_getifa(struct rt_addrinfo *info) -{ - - return (rt_getifa_fib(info, RT_DEFAULT_FIB)); -} - /* * Look up rt_addrinfo for a specific fib. Note that if rti_ifa is defined, * it will be referenced so the caller must free it. diff --git a/sys/net/route.h b/sys/net/route.h index c363032ea23..e62aaa613e7 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -387,7 +387,6 @@ void rt_flushifroutes(struct ifnet *ifp); /* XXX MRT COMPAT VERSIONS THAT SET UNIVERSE to 0 */ /* Thes are used by old code not yet converted to use multiple FIBS */ -int rt_getifa(struct rt_addrinfo *); void rtalloc_ign(struct route *ro, u_long ignflags); void rtalloc(struct route *ro); /* XXX deprecated, use rtalloc_ign(ro, 0) */ struct rtentry *rtalloc1(struct sockaddr *, int, u_long); diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 1441936c380..2607a1750ff 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -734,14 +734,10 @@ in_scrubprefixlle(struct in_ifaddr *ia, int all, u_int flags) struct sockaddr *saddr, *smask; struct ifnet *ifp; - /* - * remove all L2 entries on the given prefix - */ saddr = (struct sockaddr *)&addr; bzero(&addr, sizeof(addr)); addr.sin_len = sizeof(addr); addr.sin_family = AF_INET; - addr.sin_addr.s_addr = ntohl(ia->ia_addr.sin_addr.s_addr); smask = (struct sockaddr *)&mask; bzero(&mask, sizeof(mask)); mask.sin_len = sizeof(mask); @@ -749,10 +745,21 @@ in_scrubprefixlle(struct in_ifaddr *ia, int all, u_int flags) mask.sin_addr.s_addr = ia->ia_subnetmask; ifp = ia->ia_ifp; - if (all) + if (all) { + + /* + * Remove all L2 entries matching given prefix. + * Convert address to host representation to avoid + * doing this on every callback. ia_subnetmask is already + * stored in host representation. + */ + addr.sin_addr.s_addr = ntohl(ia->ia_addr.sin_addr.s_addr); lltable_prefix_free(AF_INET, saddr, smask, flags); - else + } else { + /* Remove interface address only */ + addr.sin_addr.s_addr = ia->ia_addr.sin_addr.s_addr; lltable_delete_addr(LLTABLE(ifp), LLE_IFADDR, saddr); + } } /* @@ -808,6 +815,14 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags) fibnum = V_rt_add_addr_allfibs ? RT_ALL_FIBS : target->ia_ifp->if_fib; rt_addrmsg(RTM_DELETE, &target->ia_ifa, fibnum); + + /* + * Removing address from !IFF_UP interface or + * prefix which exists on other interface (along with route). + * No entries should exist here except target addr. + * Given that, delete this entry only. + */ + in_scrubprefixlle(target, 0, flags); return (0); } diff --git a/sys/netinet/in_rmx.c b/sys/netinet/in_rmx.c index 0c50cddd663..e17d36fa7ed 100644 --- a/sys/netinet/in_rmx.c +++ b/sys/netinet/in_rmx.c @@ -228,19 +228,6 @@ in_rtalloc_ign(struct route *ro, u_long ignflags, u_int fibnum) rtalloc_ign_fib(ro, ignflags, fibnum); } -int -in_rtrequest( int req, - struct sockaddr *dst, - struct sockaddr *gateway, - struct sockaddr *netmask, - int flags, - struct rtentry **ret_nrt, - u_int fibnum) -{ - return (rtrequest_fib(req, dst, gateway, netmask, - flags, ret_nrt, fibnum)); -} - struct rtentry * in_rtalloc1(struct sockaddr *dst, int report, u_long ignflags, u_int fibnum) { @@ -264,10 +251,3 @@ in_rtalloc(struct route *ro, u_int fibnum) rtalloc_ign_fib(ro, 0UL, fibnum); } -#if 0 -int in_rt_getifa(struct rt_addrinfo *, u_int fibnum); -int in_rtioctl(u_long, caddr_t, u_int); -int in_rtrequest1(int, struct rt_addrinfo *, struct rtentry **, u_int); -#endif - - diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index f9e98128bc1..e491ba4dd5a 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -391,8 +391,6 @@ void in_rtalloc(struct route *ro, u_int fibnum); struct rtentry *in_rtalloc1(struct sockaddr *, int, u_long, u_int); void in_rtredirect(struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct sockaddr *, u_int); -int in_rtrequest(int, struct sockaddr *, - struct sockaddr *, struct sockaddr *, int, struct rtentry **, u_int); #endif /* _KERNEL */ /* INET6 stuff */ diff --git a/sys/netinet/sctp_auth.c b/sys/netinet/sctp_auth.c index 13454df92f1..07dbf8ba440 100644 --- a/sys/netinet/sctp_auth.c +++ b/sys/netinet/sctp_auth.c @@ -558,7 +558,7 @@ sctp_auth_key_acquire(struct sctp_tcb *stcb, uint16_t key_id) atomic_add_int(&skey->refcount, 1); SCTPDBG(SCTP_DEBUG_AUTH2, "%s: stcb %p key %u refcount acquire to %d\n", - __FUNCTION__, (void *)stcb, key_id, skey->refcount); + __func__, (void *)stcb, key_id, skey->refcount); } } @@ -578,7 +578,7 @@ sctp_auth_key_release(struct sctp_tcb *stcb, uint16_t key_id, int so_locked if (skey) { SCTPDBG(SCTP_DEBUG_AUTH2, "%s: stcb %p key %u refcount release to %d\n", - __FUNCTION__, (void *)stcb, key_id, skey->refcount); + __func__, (void *)stcb, key_id, skey->refcount); /* see if a notification should be generated */ if ((skey->refcount <= 2) && (skey->deactivated)) { @@ -587,7 +587,7 @@ sctp_auth_key_release(struct sctp_tcb *stcb, uint16_t key_id, int so_locked key_id, 0, so_locked); SCTPDBG(SCTP_DEBUG_AUTH2, "%s: stcb %p key %u no longer used, %d\n", - __FUNCTION__, (void *)stcb, key_id, skey->refcount); + __func__, (void *)stcb, key_id, skey->refcount); } sctp_free_sharedkey(skey); } diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index 37d9e6051ea..8ed155104a5 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -4625,7 +4625,7 @@ __attribute__((noinline)) } } if (stcb == NULL) { - snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __FUNCTION__); + snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__); op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), msg); /* no association, so it's out of the blue... */ @@ -4669,7 +4669,7 @@ __attribute__((noinline)) if (locked_tcb) { SCTP_TCB_UNLOCK(locked_tcb); } - snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __FUNCTION__); + snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__); op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), msg); sctp_handle_ootb(m, iphlen, *offset, src, dst, @@ -5821,7 +5821,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt */ SCTP_TCB_UNLOCK(stcb); stcb = NULL; - snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __FUNCTION__); + snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__); op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), msg); sctp_handle_ootb(m, iphlen, offset, src, dst, sh, inp, op_err, @@ -5873,7 +5873,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt } if (stcb == NULL) { /* out of the blue DATA chunk */ - snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __FUNCTION__); + snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__); op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), msg); sctp_handle_ootb(m, iphlen, offset, src, dst, sh, inp, op_err, @@ -5945,7 +5945,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt /* * We consider OOTB any data sent during asoc setup. */ - snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __FUNCTION__); + snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__); op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), msg); sctp_handle_ootb(m, iphlen, offset, src, dst, sh, inp, op_err, diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index f515d6a6cd2..7d13aa22e95 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -2417,7 +2417,7 @@ sctp_is_addr_restricted(struct sctp_tcb *stcb, struct sctp_ifa *ifa) LIST_FOREACH(laddr, &stcb->asoc.sctp_restricted_addrs, sctp_nxt_addr) { if (laddr->ifa == NULL) { SCTPDBG(SCTP_DEBUG_OUTPUT1, "%s: NULL ifa\n", - __FUNCTION__); + __func__); continue; } if (laddr->ifa == ifa) { @@ -2439,7 +2439,7 @@ sctp_is_addr_in_ep(struct sctp_inpcb *inp, struct sctp_ifa *ifa) LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { SCTPDBG(SCTP_DEBUG_OUTPUT1, "%s: NULL ifa\n", - __FUNCTION__); + __func__); continue; } if ((laddr->ifa == ifa) && laddr->action == 0) @@ -5524,7 +5524,7 @@ do_a_abort: if (op_err == NULL) { char msg[SCTP_DIAG_INFO_LEN]; - snprintf(msg, sizeof(msg), "%s:%d at %s", __FILE__, __LINE__, __FUNCTION__); + snprintf(msg, sizeof(msg), "%s:%d at %s", __FILE__, __LINE__, __func__); op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), msg); } @@ -6687,7 +6687,7 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, abort_anyway: snprintf(msg, sizeof(msg), - "%s:%d at %s", __FILE__, __LINE__, __FUNCTION__); + "%s:%d at %s", __FILE__, __LINE__, __func__); op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), msg); atomic_add_int(&stcb->asoc.refcnt, 1); @@ -13387,7 +13387,7 @@ dataless_eof: free_cnt_applied = 0; } snprintf(msg, sizeof(msg), - "%s:%d at %s", __FILE__, __LINE__, __FUNCTION__); + "%s:%d at %s", __FILE__, __LINE__, __func__); op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), msg); sctp_abort_an_association(stcb->sctp_ep, stcb, diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 07119f15d9f..92a84d6a299 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -1115,7 +1115,7 @@ sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from, LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { - SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n", __FUNCTION__); + SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n", __func__); continue; } if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) { @@ -1773,7 +1773,7 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head, LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n", - __FUNCTION__); + __func__); continue; } SCTPDBG(SCTP_DEBUG_PCB1, "Ok laddr->ifa:%p is possible, ", @@ -2343,7 +2343,7 @@ sctp_findassociation_ep_asconf(struct mbuf *m, int offset, &parm_buf, sizeof(struct sctp_paramhdr)); if (phdr == NULL) { SCTPDBG(SCTP_DEBUG_INPUT3, "%s: failed to get asconf lookup addr\n", - __FUNCTION__); + __func__); return NULL; } ptype = (int)((uint32_t) ntohs(phdr->param_type)); @@ -2363,7 +2363,7 @@ sctp_findassociation_ep_asconf(struct mbuf *m, int offset, &p6_buf.ph, sizeof(*p6)); if (p6 == NULL) { SCTPDBG(SCTP_DEBUG_INPUT3, "%s: failed to get asconf v6 lookup addr\n", - __FUNCTION__); + __func__); return (NULL); } sin6 = &remote_store.sin6; @@ -2390,7 +2390,7 @@ sctp_findassociation_ep_asconf(struct mbuf *m, int offset, &p4_buf.ph, sizeof(*p4)); if (p4 == NULL) { SCTPDBG(SCTP_DEBUG_INPUT3, "%s: failed to get asconf v4 lookup addr\n", - __FUNCTION__); + __func__); return (NULL); } sin = &remote_store.sin; @@ -5300,7 +5300,7 @@ sctp_update_ep_vflag(struct sctp_inpcb *inp) LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n", - __FUNCTION__); + __func__); continue; } if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) { @@ -6258,7 +6258,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, * abort this guy */ snprintf(msg, sizeof(msg), - "%s:%d at %s", __FILE__, __LINE__, __FUNCTION__); + "%s:%d at %s", __FILE__, __LINE__, __func__); op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), msg); sctp_abort_an_association(stcb_tmp->sctp_ep, @@ -6357,7 +6357,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, * abort this guy */ snprintf(msg, sizeof(msg), - "%s:%d at %s", __FILE__, __LINE__, __FUNCTION__); + "%s:%d at %s", __FILE__, __LINE__, __func__); op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code), msg); sctp_abort_an_association(stcb_tmp->sctp_ep, diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index 78f4508c501..266861ecb9c 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -5815,7 +5815,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { SCTPDBG(SCTP_DEBUG_OUTPUT1, "%s: NULL ifa\n", - __FUNCTION__); + __func__); continue; } if (laddr->ifa == ifa) { diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index 8a4a65e48b5..e451cb18d38 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -2183,13 +2183,13 @@ sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, break; default: SCTPDBG(SCTP_DEBUG_TIMER1, "%s: Unknown timer type %d\n", - __FUNCTION__, t_type); + __func__, t_type); return; break; } if ((to_ticks <= 0) || (tmr == NULL)) { SCTPDBG(SCTP_DEBUG_TIMER1, "%s: %d:software error to_ticks:%d tmr:%p not set ??\n", - __FUNCTION__, t_type, to_ticks, (void *)tmr); + __func__, t_type, to_ticks, (void *)tmr); return; } if (SCTP_OS_TIMER_PENDING(&tmr->timer)) { @@ -2345,7 +2345,7 @@ sctp_timer_stop(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, break; default: SCTPDBG(SCTP_DEBUG_TIMER1, "%s: Unknown timer type %d\n", - __FUNCTION__, t_type); + __func__, t_type); break; } if (tmr == NULL) { @@ -3777,7 +3777,7 @@ sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb, break; default: SCTPDBG(SCTP_DEBUG_UTIL1, "%s: unknown notification %xh (%u)\n", - __FUNCTION__, notification, notification); + __func__, notification, notification); break; } /* end switch */ } diff --git a/sys/netinet/tcp.h b/sys/netinet/tcp.h index fb2f8108d4f..37608605367 100644 --- a/sys/netinet/tcp.h +++ b/sys/netinet/tcp.h @@ -165,6 +165,8 @@ struct tcphdr { #define TCP_KEEPIDLE 256 /* L,N,X start keeplives after this period */ #define TCP_KEEPINTVL 512 /* L,N interval between keepalives */ #define TCP_KEEPCNT 1024 /* L,N number of keepalives before close */ +#define TCP_PCAP_OUT 2048 /* number of output packets to keep */ +#define TCP_PCAP_IN 4096 /* number of input packets to keep */ /* Start of reserved space for third-party user-settable options. */ #define TCP_VENDOR SO_VENDOR diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index b6ea859de31..64971f3da29 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -104,6 +104,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#ifdef TCPPCAP +#include +#endif #include #ifdef TCPDEBUG #include @@ -1524,6 +1527,11 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, KASSERT(tp->t_state != TCPS_TIME_WAIT, ("%s: TCPS_TIME_WAIT", __func__)); +#ifdef TCPPCAP + /* Save segment, if requested. */ + tcp_pcap_add(th, m, &(tp->t_inpkts)); +#endif + /* * Segment received on connection. * Reset idle time and keep-alive timer. diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c index fc90b069b9d..d295dcfbcd6 100644 --- a/sys/netinet/tcp_output.c +++ b/sys/netinet/tcp_output.c @@ -74,6 +74,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#ifdef TCPPCAP +#include +#endif #ifdef TCPDEBUG #include #endif @@ -1305,6 +1308,11 @@ send: TCP_PROBE5(send, NULL, tp, ip6, tp, th); +#ifdef TCPPCAP + /* Save packet, if requested. */ + tcp_pcap_add(th, m, &(tp->t_outpkts)); +#endif + /* TODO: IPv6 IP6TOS_ECT bit on */ error = ip6_output(m, tp->t_inpcb->in6p_outputopts, &ro, ((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0), @@ -1348,6 +1356,11 @@ send: TCP_PROBE5(send, NULL, tp, ip, tp, th); +#ifdef TCPPCAP + /* Save packet, if requested. */ + tcp_pcap_add(th, m, &(tp->t_outpkts)); +#endif + error = ip_output(m, tp->t_inpcb->inp_options, &ro, ((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0), 0, tp->t_inpcb); diff --git a/sys/netinet/tcp_pcap.c b/sys/netinet/tcp_pcap.c new file mode 100644 index 00000000000..f0c651dab55 --- /dev/null +++ b/sys/netinet/tcp_pcap.c @@ -0,0 +1,441 @@ +/*- + * Copyright (c) 2015 + * Jonathan Looney. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define M_LEADINGSPACE_NOWRITE(m) \ + ((m)->m_data - M_START(m)) + +static int tcp_pcap_clusters_referenced_cur = 0; +static int tcp_pcap_clusters_referenced_max = 0; + +SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcp_pcap_clusters_referenced_cur, + CTLFLAG_RD, &tcp_pcap_clusters_referenced_cur, 0, + "Number of clusters currently referenced on TCP PCAP queues"); +SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcp_pcap_clusters_referenced_max, + CTLFLAG_RW, &tcp_pcap_clusters_referenced_max, 0, + "Maximum number of clusters allowed to be referenced on TCP PCAP " + "queues"); + +static int tcp_pcap_alloc_reuse_ext = 0; +static int tcp_pcap_alloc_reuse_mbuf = 0; +static int tcp_pcap_alloc_new_mbuf = 0; +SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcp_pcap_alloc_reuse_ext, + CTLFLAG_RD, &tcp_pcap_alloc_reuse_ext, 0, + "Number of mbufs with external storage reused for the TCP PCAP " + "functionality"); +SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcp_pcap_alloc_reuse_mbuf, + CTLFLAG_RD, &tcp_pcap_alloc_reuse_mbuf, 0, + "Number of mbufs with internal storage reused for the TCP PCAP " + "functionality"); +SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcp_pcap_alloc_new_mbuf, + CTLFLAG_RD, &tcp_pcap_alloc_new_mbuf, 0, + "Number of new mbufs allocated for the TCP PCAP functionality"); + +VNET_DEFINE(int, tcp_pcap_packets) = 0; +#define V_tcp_pcap_packets VNET(tcp_pcap_packets) +SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcp_pcap_packets, + CTLFLAG_RW, &VNET_NAME(tcp_pcap_packets), 0, + "Default number of packets saved per direction per TCPCB"); + +/* Initialize the values. */ +static void +tcp_pcap_max_set(void) +{ + + tcp_pcap_clusters_referenced_max = nmbclusters / 4; +} + +void +tcp_pcap_init(void) +{ + + tcp_pcap_max_set(); + EVENTHANDLER_REGISTER(nmbclusters_change, tcp_pcap_max_set, + NULL, EVENTHANDLER_PRI_ANY); +} + +/* + * If we are below the maximum allowed cluster references, + * increment the reference count and return TRUE. Otherwise, + * leave the reference count alone and return FALSE. + */ +static __inline bool +tcp_pcap_take_cluster_reference(void) +{ + if (atomic_fetchadd_int(&tcp_pcap_clusters_referenced_cur, 1) >= + tcp_pcap_clusters_referenced_max) { + atomic_add_int(&tcp_pcap_clusters_referenced_cur, -1); + return FALSE; + } + return TRUE; +} + +/* + * For all the external entries in m, apply the given adjustment. + * This can be used to adjust the counter when an mbuf chain is + * copied or freed. + */ +static __inline void +tcp_pcap_adj_cluster_reference(struct mbuf *m, int adj) +{ + while (m) { + if (m->m_flags & M_EXT) + atomic_add_int(&tcp_pcap_clusters_referenced_cur, adj); + + m = m->m_next; + } +} + +/* + * Free all mbufs in a chain, decrementing the reference count as + * necessary. + * + * Functions in this file should use this instead of m_freem() when + * they are freeing mbuf chains that may contain clusters that were + * already included in tcp_pcap_clusters_referenced_cur. + */ +static void +tcp_pcap_m_freem(struct mbuf *mb) +{ + while (mb != NULL) { + if (mb->m_flags & M_EXT) + atomic_subtract_int(&tcp_pcap_clusters_referenced_cur, + 1); + mb = m_free(mb); + } +} + +/* + * Copy data from m to n, where n cannot fit all the data we might + * want from m. + * + * Prioritize data like this: + * 1. TCP header + * 2. IP header + * 3. Data + */ +static void +tcp_pcap_copy_bestfit(struct tcphdr *th, struct mbuf *m, struct mbuf *n) +{ + struct mbuf *m_cur = m; + int bytes_to_copy=0, trailing_data, skip=0, tcp_off; + + /* Below, we assume these will be non-NULL. */ + KASSERT(th, ("%s: called with th == NULL", __func__)); + KASSERT(m, ("%s: called with m == NULL", __func__)); + KASSERT(n, ("%s: called with n == NULL", __func__)); + + /* We assume this initialization occurred elsewhere. */ + KASSERT(n->m_len == 0, ("%s: called with n->m_len=%d (expected 0)", + __func__, n->m_len)); + KASSERT(n->m_data == M_START(n), + ("%s: called with n->m_data != M_START(n)", __func__)); + + /* + * Calculate the size of the TCP header. We use this often + * enough that it is worth just calculating at the start. + */ + tcp_off = th->th_off << 2; + + /* Trim off leading empty mbufs. */ + while (m && m->m_len == 0) + m = m->m_next; + + if (m) { + m_cur = m; + } + else { + /* + * No data? Highly unusual. We would expect to at + * least see a TCP header in the mbuf. + * As we have a pointer to the TCP header, I guess + * we should just copy that. (???) + */ +fallback: + bytes_to_copy = tcp_off; + if (bytes_to_copy > M_SIZE(n)) + bytes_to_copy = M_SIZE(n); + bcopy(th, n->m_data, bytes_to_copy); + n->m_len = bytes_to_copy; + return; + } + + /* + * Find TCP header. Record the total number of bytes up to, + * and including, the TCP header. + */ + while (m_cur) { + if ((caddr_t) th >= (caddr_t) m_cur->m_data && + (caddr_t) th < (caddr_t) (m_cur->m_data + m_cur->m_len)) + break; + bytes_to_copy += m_cur->m_len; + m_cur = m_cur->m_next; + } + if (m_cur) + bytes_to_copy += (caddr_t) th - (caddr_t) m_cur->m_data; + else + goto fallback; + bytes_to_copy += tcp_off; + + /* + * If we already want to copy more bytes than we can hold + * in the destination mbuf, skip leading bytes and copy + * what we can. + * + * Otherwise, consider trailing data. + */ + if (bytes_to_copy > M_SIZE(n)) { + skip = bytes_to_copy - M_SIZE(n); + bytes_to_copy = M_SIZE(n); + } + else { + /* + * Determine how much trailing data is in the chain. + * We start with the length of this mbuf (the one + * containing th) and subtract the size of the TCP + * header (tcp_off) and the size of the data prior + * to th (th - m_cur->m_data). + * + * This *should not* be negative, as the TCP code + * should put the whole TCP header in a single + * mbuf. But, it isn't a problem if it is. We will + * simple work off our negative balance as we look + * at subsequent mbufs. + */ + trailing_data = m_cur->m_len - tcp_off; + trailing_data -= (caddr_t) th - (caddr_t) m_cur->m_data; + m_cur = m_cur->m_next; + while (m_cur) { + trailing_data += m_cur->m_len; + m_cur = m_cur->m_next; + } + if ((bytes_to_copy + trailing_data) > M_SIZE(n)) + bytes_to_copy = M_SIZE(n); + else + bytes_to_copy += trailing_data; + } + + m_copydata(m, skip, bytes_to_copy, n->m_data); + n->m_len = bytes_to_copy; +} + +void +tcp_pcap_add(struct tcphdr *th, struct mbuf *m, struct mbufq *queue) +{ + struct mbuf *n = NULL, *mhead; + + KASSERT(th, ("%s: called with th == NULL", __func__)); + KASSERT(m, ("%s: called with m == NULL", __func__)); + KASSERT(queue, ("%s: called with queue == NULL", __func__)); + + /* We only care about data packets. */ + while (m && m->m_type != MT_DATA) + m = m->m_next; + + /* We only need to do something if we still have an mbuf. */ + if (!m) + return; + + /* If we are not saving mbufs, return now. */ + if (queue->mq_maxlen == 0) + return; + + /* + * Check to see if we will need to recycle mbufs. + * + * If we need to get rid of mbufs to stay below + * our packet count, try to reuse the mbuf. Once + * we already have a new mbuf (n), then we can + * simply free subsequent mbufs. + * + * Note that most of the logic in here is to deal + * with the reuse. If we are fine with constant + * mbuf allocs/deallocs, we could ditch this logic. + * But, it only seems to make sense to reuse + * mbufs we already have. + */ + while (mbufq_full(queue)) { + mhead = mbufq_dequeue(queue); + + if (n) { + tcp_pcap_m_freem(mhead); + } + else { + /* + * If this held an external cluster, try to + * detach the cluster. But, if we held the + * last reference, go through the normal + * free-ing process. + */ + if (mhead->m_flags & M_EXT) { + switch (mhead->m_ext.ext_type) { + case EXT_SFBUF: + /* Don't mess around with these. */ + tcp_pcap_m_freem(mhead); + continue; + default: + if (atomic_fetchadd_int( + mhead->m_ext.ext_cnt, -1) == 1) + { + /* + * We held the last reference + * on this cluster. Restore + * the reference count and put + * it back in the pool. + */ + *(mhead->m_ext.ext_cnt) = 1; + tcp_pcap_m_freem(mhead); + continue; + } + /* + * We were able to cleanly free the + * reference. + */ + atomic_subtract_int( + &tcp_pcap_clusters_referenced_cur, + 1); + tcp_pcap_alloc_reuse_ext++; + break; + } + } + else { + tcp_pcap_alloc_reuse_mbuf++; + } + + n = mhead; + tcp_pcap_m_freem(n->m_next); + m_init(n, NULL, 0, M_NOWAIT, MT_DATA, 0); + } + } + + /* Check to see if we need to get a new mbuf. */ + if (!n) { + if (!(n = m_get(M_NOWAIT, MT_DATA))) + return; + tcp_pcap_alloc_new_mbuf++; + } + + /* + * What are we dealing with? If a cluster, attach it. Otherwise, + * try to copy the data from the beginning of the mbuf to the + * end of data. (There may be data between the start of the data + * area and the current data pointer. We want to get this, because + * it may contain header information that is useful.) + * In cases where that isn't possible, settle for what we can + * get. + */ + if ((m->m_flags & M_EXT) && tcp_pcap_take_cluster_reference()) { + n->m_data = m->m_data; + n->m_len = m->m_len; + mb_dupcl(n, m); + } + else if (((m->m_data + m->m_len) - M_START(m)) <= M_SIZE(n)) { + /* + * At this point, n is guaranteed to be a normal mbuf + * with no cluster and no packet header. Because the + * logic in this code block requires this, the assert + * is here to catch any instances where someone + * changes the logic to invalidate that assumption. + */ + KASSERT((n->m_flags & (M_EXT | M_PKTHDR)) == 0, + ("%s: Unexpected flags (%#x) for mbuf", + __func__, n->m_flags)); + n->m_data = n->m_dat + M_LEADINGSPACE_NOWRITE(m); + n->m_len = m->m_len; + bcopy(M_START(m), n->m_dat, + m->m_len + M_LEADINGSPACE_NOWRITE(m)); + } + else { + /* + * This is the case where we need to "settle for what + * we can get". The most probable way to this code + * path is that we've already taken references to the + * maximum number of mbuf clusters we can, and the data + * is too long to fit in an mbuf's internal storage. + * Try for a "best fit". + */ + tcp_pcap_copy_bestfit(th, m, n); + + /* Don't try to get additional data. */ + goto add_to_queue; + } + + if (m->m_next) { + n->m_next = m_copym(m->m_next, 0, M_COPYALL, M_NOWAIT); + tcp_pcap_adj_cluster_reference(n->m_next, 1); + } + +add_to_queue: + /* Add the new mbuf to the list. */ + if (mbufq_enqueue(queue, n)) { + /* This shouldn't happen. If INVARIANTS is defined, panic. */ + KASSERT(0, ("%s: mbufq was unexpectedly full!", __func__)); + tcp_pcap_m_freem(n); + } +} + +void +tcp_pcap_drain(struct mbufq *queue) +{ + struct mbuf *m; + while ((m = mbufq_dequeue(queue))) + tcp_pcap_m_freem(m); +} + +void +tcp_pcap_tcpcb_init(struct tcpcb *tp) +{ + mbufq_init(&(tp->t_inpkts), V_tcp_pcap_packets); + mbufq_init(&(tp->t_outpkts), V_tcp_pcap_packets); +} + +void +tcp_pcap_set_sock_max(struct mbufq *queue, int newval) +{ + queue->mq_maxlen = newval; + while (queue->mq_len > queue->mq_maxlen) + tcp_pcap_m_freem(mbufq_dequeue(queue)); +} + +int +tcp_pcap_get_sock_max(struct mbufq *queue) +{ + return queue->mq_maxlen; +} diff --git a/sys/netinet/tcp_pcap.h b/sys/netinet/tcp_pcap.h new file mode 100644 index 00000000000..ec8fdde0383 --- /dev/null +++ b/sys/netinet/tcp_pcap.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2015 + * Jonathan Looney. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NETINET_TCP_PCAP_H_ +#define _NETINET_TCP_PCAP_H_ + +void tcp_pcap_init(void); +void tcp_pcap_add(struct tcphdr *th, struct mbuf *m, struct mbufq *queue); +void tcp_pcap_drain(struct mbufq *queue); +void tcp_pcap_tcpcb_init(struct tcpcb *tp); +void tcp_pcap_set_sock_max(struct mbufq *queue, int newval); +int tcp_pcap_get_sock_max(struct mbufq *queue); + +#endif /* _NETINET_TCP_PCAP_H_ */ diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index a20bd810931..e3f5b1324ce 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -92,6 +92,9 @@ __FBSDID("$FreeBSD$"); #include #endif #include +#ifdef TCPPCAP +#include +#endif #ifdef TCPDEBUG #include #endif @@ -427,6 +430,9 @@ tcp_init(void) SHUTDOWN_PRI_DEFAULT); EVENTHANDLER_REGISTER(maxsockets_change, tcp_zone_change, NULL, EVENTHANDLER_PRI_ANY); +#ifdef TCPPCAP + tcp_pcap_init(); +#endif } #ifdef VIMAGE @@ -832,6 +838,12 @@ tcp_newtcpcb(struct inpcb *inp) */ inp->inp_ip_ttl = V_ip_defttl; inp->inp_ppcb = tp; +#ifdef TCPPCAP + /* + * Init the TCP PCAP queues. + */ + tcp_pcap_tcpcb_init(tp); +#endif return (tp); /* XXX */ } @@ -1016,6 +1028,12 @@ tcp_discardcb(struct tcpcb *tp) tcp_free_sackholes(tp); +#ifdef TCPPCAP + /* Free the TCP PCAP queues. */ + tcp_pcap_drain(&(tp->t_inpkts)); + tcp_pcap_drain(&(tp->t_outpkts)); +#endif + /* Allow the CC algorithm to clean up after itself. */ if (CC_ALGO(tp)->cb_destroy != NULL) CC_ALGO(tp)->cb_destroy(tp->ccv); diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c index 60b882f869f..d3933967497 100644 --- a/sys/netinet/tcp_timer.c +++ b/sys/netinet/tcp_timer.c @@ -664,9 +664,15 @@ tcp_timer_rexmt(void * xtp) int isipv6; #endif + /* + * Idea here is that at each stage of mtu probe (usually, 1448 + * -> 1188 -> 524) should be given 2 chances to recover before + * further clamping down. 'tp->t_rxtshift % 2 == 0' should + * take care of that. + */ if (((tp->t_flags2 & (TF2_PLPMTU_PMTUD|TF2_PLPMTU_MAXSEGSNT)) == (TF2_PLPMTU_PMTUD|TF2_PLPMTU_MAXSEGSNT)) && - (tp->t_rxtshift <= 2)) { + (tp->t_rxtshift >= 2 && tp->t_rxtshift % 2 == 0)) { /* * Enter Path MTU Black-hole Detection mechanism: * - Disable Path MTU Discovery (IP "DF" bit). @@ -734,9 +740,11 @@ tcp_timer_rexmt(void * xtp) * with a lowered MTU, maybe this isn't a blackhole and * we restore the previous MSS and blackhole detection * flags. + * The limit '6' is determined by giving each probe + * stage (1448, 1188, 524) 2 chances to recover. */ if ((tp->t_flags2 & TF2_PLPMTU_BLACKHOLE) && - (tp->t_rxtshift > 4)) { + (tp->t_rxtshift > 6)) { tp->t_flags2 |= TF2_PLPMTU_PMTUD; tp->t_flags2 &= ~TF2_PLPMTU_BLACKHOLE; optlen = tp->t_maxopd - tp->t_maxseg; diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 7d1ec6a0f2a..a1f8a0c07fa 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -86,6 +86,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#ifdef TCPPCAP +#include +#endif #ifdef TCPDEBUG #include #endif @@ -1577,6 +1580,25 @@ unlock_and_done: TP_MAXIDLE(tp)); goto unlock_and_done; +#ifdef TCPPCAP + case TCP_PCAP_OUT: + case TCP_PCAP_IN: + INP_WUNLOCK(inp); + error = sooptcopyin(sopt, &optval, sizeof optval, + sizeof optval); + if (error) + return (error); + + INP_WLOCK_RECHECK(inp); + if (optval >= 0) + tcp_pcap_set_sock_max(TCP_PCAP_OUT ? + &(tp->t_outpkts) : &(tp->t_inpkts), + optval); + else + error = EINVAL; + goto unlock_and_done; +#endif + default: INP_WUNLOCK(inp); error = ENOPROTOOPT; @@ -1647,6 +1669,15 @@ unlock_and_done: INP_WUNLOCK(inp); error = sooptcopyout(sopt, &ui, sizeof(ui)); break; +#ifdef TCPPCAP + case TCP_PCAP_OUT: + case TCP_PCAP_IN: + optval = tcp_pcap_get_sock_max(TCP_PCAP_OUT ? + &(tp->t_outpkts) : &(tp->t_inpkts)); + INP_WUNLOCK(inp); + error = sooptcopyout(sopt, &optval, sizeof optval); + break; +#endif default: INP_WUNLOCK(inp); error = ENOPROTOOPT; diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index 61fc41903fd..07a28feb495 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -37,6 +37,7 @@ #ifdef _KERNEL #include +#include /* * Kernel variables for tcp. @@ -204,7 +205,17 @@ struct tcpcb { uint32_t t_ispare[8]; /* 5 UTO, 3 TBD */ void *t_pspare2[4]; /* 1 TCP_SIGNATURE, 3 TBD */ - uint64_t _pad[6]; /* 6 TBD (1-2 CC/RTT?) */ +#if defined(_KERNEL) && defined(TCPPCAP) + struct mbufq t_inpkts; /* List of saved input packets. */ + struct mbufq t_outpkts; /* List of saved output packets. */ +#ifdef _LP64 + uint64_t _pad[0]; /* all used! */ +#else + uint64_t _pad[2]; /* 2 are available */ +#endif /* _LP64 */ +#else + uint64_t _pad[6]; +#endif /* defined(_KERNEL) && defined(TCPPCAP) */ }; /* diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 1f6d5a24165..81213144796 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -202,7 +202,7 @@ static void pf_init_threshold(struct pf_threshold *, u_int32_t, static void pf_add_threshold(struct pf_threshold *); static int pf_check_threshold(struct pf_threshold *); -static void pf_change_ap(struct pf_addr *, u_int16_t *, +static void pf_change_ap(struct mbuf *, struct pf_addr *, u_int16_t *, u_int16_t *, u_int16_t *, struct pf_addr *, u_int16_t, u_int8_t, sa_family_t); static int pf_modulate_sack(struct mbuf *, int, struct pf_pdesc *, @@ -1991,6 +1991,22 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) } } +/** + * Checksum updates are a little complicated because the checksum in the TCP/UDP + * header isn't always a full checksum. In some cases (i.e. output) it's a + * pseudo-header checksum, which is a partial checksum over src/dst IP + * addresses, protocol number and length. + * + * That means we have the following cases: + * * Input or forwarding: we don't have TSO, the checksum fields are full + * checksums, we need to update the checksum whenever we change anything. + * * Output (i.e. the checksum is a pseudo-header checksum): + * x The field being updated is src/dst address or affects the length of + * the packet. We need to update the pseudo-header checksum (note that this + * checksum is not ones' complement). + * x Some other field is being modified (e.g. src/dst port numbers): We + * don't have to update anything. + **/ u_int16_t pf_cksum_fixup(u_int16_t cksum, u_int16_t old, u_int16_t new, u_int8_t udp) { @@ -2006,9 +2022,20 @@ pf_cksum_fixup(u_int16_t cksum, u_int16_t old, u_int16_t new, u_int8_t udp) return (l); } +u_int16_t +pf_proto_cksum_fixup(struct mbuf *m, u_int16_t cksum, u_int16_t old, + u_int16_t new, u_int8_t udp) +{ + if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) + return (cksum); + + return (pf_cksum_fixup(cksum, old, new, udp)); +} + static void -pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, - struct pf_addr *an, u_int16_t pn, u_int8_t u, sa_family_t af) +pf_change_ap(struct mbuf *m, struct pf_addr *a, u_int16_t *p, u_int16_t *ic, + u_int16_t *pc, struct pf_addr *an, u_int16_t pn, u_int8_t u, + sa_family_t af) { struct pf_addr ao; u_int16_t po = *p; @@ -2016,6 +2043,9 @@ pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, PF_ACPY(&ao, a, af); PF_ACPY(a, an, af); + if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) + *pc = ~*pc; + *p = pn; switch (af) { @@ -2025,17 +2055,19 @@ pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, ao.addr16[0], an->addr16[0], 0), ao.addr16[1], an->addr16[1], 0); *p = pn; - *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, + + *pc = pf_cksum_fixup(pf_cksum_fixup(*pc, ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u), - po, pn, u); + ao.addr16[1], an->addr16[1], u); + + *pc = pf_proto_cksum_fixup(m, *pc, po, pn, u); break; #endif /* INET */ #ifdef INET6 case AF_INET6: *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, + pf_cksum_fixup(pf_cksum_fixup(*pc, ao.addr16[0], an->addr16[0], u), ao.addr16[1], an->addr16[1], u), ao.addr16[2], an->addr16[2], u), @@ -2043,13 +2075,20 @@ pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, ao.addr16[4], an->addr16[4], u), ao.addr16[5], an->addr16[5], u), ao.addr16[6], an->addr16[6], u), - ao.addr16[7], an->addr16[7], u), - po, pn, u); + ao.addr16[7], an->addr16[7], u); + + *pc = pf_proto_cksum_fixup(m, *pc, po, pn, u); break; #endif /* INET6 */ } -} + if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA | + CSUM_DELAY_DATA_IPV6)) { + *pc = ~*pc; + if (! *pc) + *pc = 0xffff; + } +} /* Changes a u_int32_t. Uses a void * so there are no align restrictions */ void @@ -2063,6 +2102,19 @@ pf_change_a(void *a, u_int16_t *c, u_int32_t an, u_int8_t u) ao % 65536, an % 65536, u); } +void +pf_change_proto_a(struct mbuf *m, void *a, u_int16_t *c, u_int32_t an, u_int8_t udp) +{ + u_int32_t ao; + + memcpy(&ao, a, sizeof(ao)); + memcpy(a, &an, sizeof(u_int32_t)); + + *c = pf_proto_cksum_fixup(m, + pf_proto_cksum_fixup(m, *c, ao / 65536, an / 65536, udp), + ao % 65536, an % 65536, udp); +} + #ifdef INET6 static void pf_change_a6(struct pf_addr *a, u_int16_t *c, struct pf_addr *an, u_int8_t u) @@ -2208,12 +2260,10 @@ pf_modulate_sack(struct mbuf *m, int off, struct pf_pdesc *pd, for (i = 2; i + TCPOLEN_SACK <= olen; i += TCPOLEN_SACK) { memcpy(&sack, &opt[i], sizeof(sack)); - pf_change_a(&sack.start, &th->th_sum, - htonl(ntohl(sack.start) - - dst->seqdiff), 0); - pf_change_a(&sack.end, &th->th_sum, - htonl(ntohl(sack.end) - - dst->seqdiff), 0); + pf_change_proto_a(m, &sack.start, &th->th_sum, + htonl(ntohl(sack.start) - dst->seqdiff), 0); + pf_change_proto_a(m, &sack.end, &th->th_sum, + htonl(ntohl(sack.end) - dst->seqdiff), 0); memcpy(&opt[i], &sack, sizeof(sack)); } copyback = 1; @@ -3117,7 +3167,7 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, if (PF_ANEQ(saddr, &nk->addr[pd->sidx], af) || nk->port[pd->sidx] != sport) { - pf_change_ap(saddr, &th->th_sport, pd->ip_sum, + pf_change_ap(m, saddr, &th->th_sport, pd->ip_sum, &th->th_sum, &nk->addr[pd->sidx], nk->port[pd->sidx], 0, af); pd->sport = &th->th_sport; @@ -3126,7 +3176,7 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, if (PF_ANEQ(daddr, &nk->addr[pd->didx], af) || nk->port[pd->didx] != dport) { - pf_change_ap(daddr, &th->th_dport, pd->ip_sum, + pf_change_ap(m, daddr, &th->th_dport, pd->ip_sum, &th->th_sum, &nk->addr[pd->didx], nk->port[pd->didx], 0, af); dport = th->th_dport; @@ -3140,7 +3190,7 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, if (PF_ANEQ(saddr, &nk->addr[pd->sidx], af) || nk->port[pd->sidx] != sport) { - pf_change_ap(saddr, &pd->hdr.udp->uh_sport, + pf_change_ap(m, saddr, &pd->hdr.udp->uh_sport, pd->ip_sum, &pd->hdr.udp->uh_sum, &nk->addr[pd->sidx], nk->port[pd->sidx], 1, af); @@ -3150,7 +3200,7 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction, if (PF_ANEQ(daddr, &nk->addr[pd->didx], af) || nk->port[pd->didx] != dport) { - pf_change_ap(daddr, &pd->hdr.udp->uh_dport, + pf_change_ap(m, daddr, &pd->hdr.udp->uh_dport, pd->ip_sum, &pd->hdr.udp->uh_sum, &nk->addr[pd->didx], nk->port[pd->didx], 1, af); @@ -3502,7 +3552,7 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a, if ((s->src.seqdiff = pf_tcp_iss(pd) - s->src.seqlo) == 0) s->src.seqdiff = 1; - pf_change_a(&th->th_seq, &th->th_sum, + pf_change_proto_a(m, &th->th_seq, &th->th_sum, htonl(s->src.seqlo + s->src.seqdiff), 0); *rewrite = 1; } else @@ -3826,9 +3876,9 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, while ((src->seqdiff = arc4random() - seq) == 0) ; ack = ntohl(th->th_ack) - dst->seqdiff; - pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + + pf_change_proto_a(m, &th->th_seq, &th->th_sum, htonl(seq + src->seqdiff), 0); - pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); + pf_change_proto_a(m, &th->th_ack, &th->th_sum, htonl(ack), 0); *copyback = 1; } else { ack = ntohl(th->th_ack); @@ -3878,9 +3928,9 @@ pf_tcp_track_full(struct pf_state_peer *src, struct pf_state_peer *dst, ack = ntohl(th->th_ack) - dst->seqdiff; if (src->seqdiff) { /* Modulate sequence numbers */ - pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + + pf_change_proto_a(m, &th->th_seq, &th->th_sum, htonl(seq + src->seqdiff), 0); - pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); + pf_change_proto_a(m, &th->th_ack, &th->th_sum, htonl(ack), 0); *copyback = 1; } end = seq + pd->p_len; @@ -4334,14 +4384,14 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || nk->port[pd->sidx] != th->th_sport) - pf_change_ap(pd->src, &th->th_sport, pd->ip_sum, - &th->th_sum, &nk->addr[pd->sidx], + pf_change_ap(m, pd->src, &th->th_sport, + pd->ip_sum, &th->th_sum, &nk->addr[pd->sidx], nk->port[pd->sidx], 0, pd->af); if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || nk->port[pd->didx] != th->th_dport) - pf_change_ap(pd->dst, &th->th_dport, pd->ip_sum, - &th->th_sum, &nk->addr[pd->didx], + pf_change_ap(m, pd->dst, &th->th_dport, + pd->ip_sum, &th->th_sum, &nk->addr[pd->didx], nk->port[pd->didx], 0, pd->af); copyback = 1; } @@ -4405,13 +4455,13 @@ pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || nk->port[pd->sidx] != uh->uh_sport) - pf_change_ap(pd->src, &uh->uh_sport, pd->ip_sum, + pf_change_ap(m, pd->src, &uh->uh_sport, pd->ip_sum, &uh->uh_sum, &nk->addr[pd->sidx], nk->port[pd->sidx], 1, pd->af); if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || nk->port[pd->didx] != uh->uh_dport) - pf_change_ap(pd->dst, &uh->uh_dport, pd->ip_sum, + pf_change_ap(m, pd->dst, &uh->uh_dport, pd->ip_sum, &uh->uh_sum, &nk->addr[pd->didx], nk->port[pd->didx], 1, pd->af); m_copyback(m, off, sizeof(*uh), (caddr_t)uh); diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index ba43de8d37b..a5a516fd3e5 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -3566,12 +3566,6 @@ pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, { int chk; - /* We need a proper CSUM befor we start (s. OpenBSD ip_output) */ - if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { - in_delayed_cksum(*m); - (*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; - } - chk = pf_test(PF_OUT, ifp, m, inp); if (chk && *m) { m_freem(*m); @@ -3610,13 +3604,6 @@ pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir, { int chk; - /* We need a proper CSUM before we start (s. OpenBSD ip_output) */ - if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { - in6_delayed_cksum(*m, - (*m)->m_pkthdr.len - sizeof(struct ip6_hdr), - sizeof(struct ip6_hdr)); - (*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6; - } CURVNET_SET(ifp->if_vnet); chk = pf_test6(PF_OUT, ifp, m, inp); CURVNET_RESTORE(); diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c index 8605554442a..99cca637c30 100644 --- a/sys/netpfil/pf/pf_norm.c +++ b/sys/netpfil/pf/pf_norm.c @@ -1215,13 +1215,14 @@ pf_normalize_tcp(int dir, struct pfi_kif *kif, struct mbuf *m, int ipoff, th->th_x2 = 0; nv = *(u_int16_t *)(&th->th_ack + 1); - th->th_sum = pf_cksum_fixup(th->th_sum, ov, nv, 0); + th->th_sum = pf_proto_cksum_fixup(m, th->th_sum, ov, nv, 0); rewrite = 1; } /* Remove urgent pointer, if TH_URG is not set */ if (!(flags & TH_URG) && th->th_urp) { - th->th_sum = pf_cksum_fixup(th->th_sum, th->th_urp, 0, 0); + th->th_sum = pf_proto_cksum_fixup(m, th->th_sum, th->th_urp, + 0, 0); th->th_urp = 0; rewrite = 1; } @@ -1422,7 +1423,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, (src->scrub->pfss_flags & PFSS_TIMESTAMP)) { tsval = ntohl(tsval); - pf_change_a(&opt[2], + pf_change_proto_a(m, &opt[2], &th->th_sum, htonl(tsval + src->scrub->pfss_ts_mod), @@ -1438,7 +1439,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, PFSS_TIMESTAMP)) { tsecr = ntohl(tsecr) - dst->scrub->pfss_ts_mod; - pf_change_a(&opt[6], + pf_change_proto_a(m, &opt[6], &th->th_sum, htonl(tsecr), 0); copyback = 1; @@ -1765,8 +1766,8 @@ pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, case TCPOPT_MAXSEG: mss = (u_int16_t *)(optp + 2); if ((ntohs(*mss)) > r->max_mss) { - th->th_sum = pf_cksum_fixup(th->th_sum, - *mss, htons(r->max_mss), 0); + th->th_sum = pf_proto_cksum_fixup(m, + th->th_sum, *mss, htons(r->max_mss), 0); *mss = htons(r->max_mss); rewrite = 1; } diff --git a/sys/ofed/include/linux/dma-mapping.h b/sys/ofed/include/linux/dma-mapping.h index f9fc3cb095b..3af3e227be0 100644 --- a/sys/ofed/include/linux/dma-mapping.h +++ b/sys/ofed/include/linux/dma-mapping.h @@ -87,7 +87,7 @@ struct dma_map_ops { int is_phys; }; -#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL << (n)) - 1)) +#define DMA_BIT_MASK(n) ((2ULL << ((n) - 1)) - 1ULL) static inline int dma_supported(struct device *dev, u64 mask) diff --git a/sys/ofed/include/linux/math64.h b/sys/ofed/include/linux/math64.h index cc3d946deff..a898f37e2d8 100644 --- a/sys/ofed/include/linux/math64.h +++ b/sys/ofed/include/linux/math64.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved. - * Copyright (c) 2014 Mellanox Technologies, Ltd. All rights reserved. + * Copyright (c) 2014-2015 Mellanox Technologies, Ltd. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,108 +26,29 @@ */ #ifndef _LINUX_MATH64_H -#define _LINUX_MATH64_H +#define _LINUX_MATH64_H -#include -#include +#include -#if BITS_PER_LONG == 64 - -# define do_div(n, base) ({ \ - uint32_t __base = (base); \ - uint32_t __rem; \ - __rem = ((uint64_t)(n)) % __base; \ - (n) = ((uint64_t)(n)) / __base; \ - __rem; \ +#define do_div(n, base) ({ \ + uint32_t __base = (base); \ + uint32_t __rem; \ + __rem = ((uint64_t)(n)) % __base; \ + (n) = ((uint64_t)(n)) / __base; \ + __rem; \ }) -/** -* div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder -* -* This is commonly provided by 32bit archs to provide an optimized 64bit -* divide. -*/ -static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) +static inline uint64_t +div_u64_rem(uint64_t dividend, uint32_t divisor, uint32_t *remainder) { - *remainder = dividend % divisor; - return dividend / divisor; + *remainder = dividend % divisor; + return (dividend / divisor); } - -#elif BITS_PER_LONG == 32 - -static uint32_t __div64_32(uint64_t *n, uint32_t base) +static inline uint64_t +div_u64(uint64_t dividend, uint32_t divisor) { - uint64_t rem = *n; - uint64_t b = base; - uint64_t res, d = 1; - uint32_t high = rem >> 32; - - /* Reduce the thing a bit first */ - res = 0; - if (high >= base) { - high /= base; - res = (uint64_t) high << 32; - rem -= (uint64_t) (high*base) << 32; - } - - while ((int64_t)b > 0 && b < rem) { - b = b+b; - d = d+d; - } - - do { - if (rem >= b) { - rem -= b; - res += d; - } - b >>= 1; - d >>= 1; - } while (d); - - *n = res; - return rem; + return (dividend / divisor); } - -# define do_div(n, base) ({ \ - uint32_t __base = (base); \ - uint32_t __rem; \ - (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ - if (likely(((n) >> 32) == 0)) { \ - __rem = (uint32_t)(n) % __base; \ - (n) = (uint32_t)(n) / __base; \ - } else \ - __rem = __div64_32(&(n), __base); \ - __rem; \ -}) -#ifndef div_u64_rem -static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) -{ - *remainder = do_div(dividend, divisor); - return dividend; -} -#endif - - -#endif /* BITS_PER_LONG */ - - - -/** - ** div_u64 - unsigned 64bit divide with 32bit divisor - ** - ** This is the most common 64bit divide and should be used if possible, - ** as many 32bit archs can optimize this variant better than a full 64bit - ** divide. - * */ -#ifndef div_u64 - -static inline u64 div_u64(u64 dividend, u32 divisor) -{ - u32 remainder; - return div_u64_rem(dividend, divisor, &remainder); -} -#endif - -#endif /* _LINUX_MATH64_H */ +#endif /* _LINUX_MATH64_H */ diff --git a/sys/ofed/include/linux/moduleparam.h b/sys/ofed/include/linux/moduleparam.h index 439237d8630..c1817ea796a 100644 --- a/sys/ofed/include/linux/moduleparam.h +++ b/sys/ofed/include/linux/moduleparam.h @@ -48,7 +48,7 @@ struct kernel_param { u16 flags; param_set_fn set; param_get_fn get; - union { + union { void *arg; struct kparam_string *str; struct kparam_array *arr; @@ -83,8 +83,8 @@ param_sysinit(struct kernel_param *param) SYSINIT(name##_param_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, \ param_sysinit, &__param_##name); -#define module_param_string(name, string, len, perm) - +#define module_param_string(name, string, len, perm) + #define module_param_named(name, var, type, mode) \ module_param_call(name, param_set_##type, param_get_##type, &var, mode) @@ -118,7 +118,7 @@ param_set_short(const char *val, struct kernel_param *kp) return 0; } -static inline int +static inline int param_get_short(char *buffer, struct kernel_param *kp) { @@ -126,14 +126,14 @@ param_get_short(char *buffer, struct kernel_param *kp) } -static inline int +static inline int param_set_ushort(const char *val, struct kernel_param *kp) { return 0; } -static inline int +static inline int param_get_ushort(char *buffer, struct kernel_param *kp) { @@ -141,14 +141,14 @@ param_get_ushort(char *buffer, struct kernel_param *kp) } -static inline int +static inline int param_set_int(const char *val, struct kernel_param *kp) { return 0; } -static inline int +static inline int param_get_int(char *buffer, struct kernel_param *kp) { @@ -156,14 +156,14 @@ param_get_int(char *buffer, struct kernel_param *kp) } -static inline int +static inline int param_set_uint(const char *val, struct kernel_param *kp) { return 0; } -static inline int +static inline int param_get_uint(char *buffer, struct kernel_param *kp) { @@ -171,14 +171,14 @@ param_get_uint(char *buffer, struct kernel_param *kp) } -static inline int +static inline int param_set_long(const char *val, struct kernel_param *kp) { return 0; } -static inline int +static inline int param_get_long(char *buffer, struct kernel_param *kp) { @@ -186,14 +186,14 @@ param_get_long(char *buffer, struct kernel_param *kp) } -static inline int +static inline int param_set_ulong(const char *val, struct kernel_param *kp) { return 0; } -static inline int +static inline int param_get_ulong(char *buffer, struct kernel_param *kp) { @@ -201,14 +201,14 @@ param_get_ulong(char *buffer, struct kernel_param *kp) } -static inline int +static inline int param_set_charp(const char *val, struct kernel_param *kp) { return 0; } -static inline int +static inline int param_get_charp(char *buffer, struct kernel_param *kp) { @@ -216,14 +216,14 @@ param_get_charp(char *buffer, struct kernel_param *kp) } -static inline int +static inline int param_set_bool(const char *val, struct kernel_param *kp) { return 0; } -static inline int +static inline int param_get_bool(char *buffer, struct kernel_param *kp) { diff --git a/sys/ofed/include/linux/scatterlist.h b/sys/ofed/include/linux/scatterlist.h index 595e2e9c8d8..b8e7c2955e6 100644 --- a/sys/ofed/include/linux/scatterlist.h +++ b/sys/ofed/include/linux/scatterlist.h @@ -3,6 +3,7 @@ * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. + * Copyright (c) 2015 Matthew Dillon * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -64,6 +65,12 @@ struct sg_table { unsigned int orig_nents; /* original size of list */ }; +struct sg_page_iter { + struct scatterlist *sg; + unsigned int sg_pgoffset; /* page index */ + unsigned int maxents; +}; + /* * Maximum number of entries that will be allocated in one piece, if * a list larger than this is required then chaining will be utilized. @@ -93,7 +100,7 @@ static inline void sg_set_buf(struct scatterlist *sg, const void *buf, unsigned int buflen) { sg_set_page(sg, virt_to_page(buf), buflen, - ((uintptr_t)buf) & ~PAGE_MASK); + ((uintptr_t)buf) & (PAGE_SIZE - 1)); } static inline void @@ -326,6 +333,66 @@ sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) return ret; } +/* + * Iterate pages in sg list. + */ +static inline void +_sg_iter_next(struct sg_page_iter *iter) +{ + struct scatterlist *sg; + unsigned int pgcount; + + sg = iter->sg; + pgcount = (sg->offset + sg->length + PAGE_SIZE - 1) >> PAGE_SHIFT; + + ++iter->sg_pgoffset; + while (iter->sg_pgoffset >= pgcount) { + iter->sg_pgoffset -= pgcount; + sg = sg_next(sg); + --iter->maxents; + if (sg == NULL || iter->maxents == 0) + break; + pgcount = (sg->offset + sg->length + PAGE_SIZE - 1) >> PAGE_SHIFT; + } + iter->sg = sg; +} + +/* + * NOTE: pgoffset is really a page index, not a byte offset. + */ +static inline void +_sg_iter_init(struct scatterlist *sgl, struct sg_page_iter *iter, + unsigned int nents, unsigned long pgoffset) +{ + if (nents) { + /* + * Nominal case. Note subtract 1 from starting page index + * for initial _sg_iter_next() call. + */ + iter->sg = sgl; + iter->sg_pgoffset = pgoffset - 1; + iter->maxents = nents; + _sg_iter_next(iter); + } else { + /* + * Degenerate case + */ + iter->sg = NULL; + iter->sg_pgoffset = 0; + iter->maxents = 0; + } +} + +static inline dma_addr_t +sg_page_iter_dma_address(struct sg_page_iter *spi) +{ + return spi->sg->address + (spi->sg_pgoffset << PAGE_SHIFT); +} + +#define for_each_sg_page(sgl, iter, nents, pgoffset) \ + for (_sg_iter_init(sgl, iter, nents, pgoffset); \ + (iter)->sg; _sg_iter_next(iter)) + #define for_each_sg(sglist, sg, sgmax, _itr) \ for (_itr = 0, sg = (sglist); _itr < (sgmax); _itr++, sg = sg_next(sg)) diff --git a/sys/ofed/include/linux/sched.h b/sys/ofed/include/linux/sched.h index da25359456f..c2fb8dfd141 100644 --- a/sys/ofed/include/linux/sched.h +++ b/sys/ofed/include/linux/sched.h @@ -107,4 +107,15 @@ do { \ #define sched_yield() sched_relinquish(curthread) +static inline long +schedule_timeout(signed long timeout) +{ + if (timeout < 0) + return 0; + + pause("lstim", timeout); + + return 0; +} + #endif /* _LINUX_SCHED_H_ */ diff --git a/sys/ofed/include/linux/uaccess.h b/sys/ofed/include/linux/uaccess.h index 6ba34f7025b..bab848c6ea0 100644 --- a/sys/ofed/include/linux/uaccess.h +++ b/sys/ofed/include/linux/uaccess.h @@ -2,7 +2,8 @@ * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. - * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. + * Copyright (c) 2013-2015 Mellanox Technologies, Ltd. + * Copyright (c) 2015 François Tigeot * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,4 +33,14 @@ #define get_user(_x, _p) -copyin((_p), &(_x), sizeof(*(_p))) #define put_user(_x, _p) -copyout(&(_x), (_p), sizeof(*(_p))) +static inline void pagefault_disable(void) +{ + curthread_pflags_set(TDP_NOFAULTING | TDP_RESETSPUR); +} + +static inline void pagefault_enable(void) +{ + curthread_pflags_restore(~(TDP_NOFAULTING | TDP_RESETSPUR)); +} + #endif /* _LINUX_UACCESS_H_ */ diff --git a/sys/ofed/include/linux/wait.h b/sys/ofed/include/linux/wait.h index 80047f2e5d8..dccd0f3d5ad 100644 --- a/sys/ofed/include/linux/wait.h +++ b/sys/ofed/include/linux/wait.h @@ -39,15 +39,18 @@ #include #include -struct __wait_queue_head { - unsigned int wchan; -}; -typedef struct __wait_queue_head wait_queue_head_t; +typedef struct { +} wait_queue_t; -#define init_waitqueue_head(x) +typedef struct { + unsigned int wchan; +} wait_queue_head_t; + +#define init_waitqueue_head(x) \ + do { } while (0) static inline void -__wake_up(struct __wait_queue_head *q, int all) +__wake_up(wait_queue_head_t *q, int all) { int wakeup_swapper; void *c; @@ -108,6 +111,23 @@ do { \ -_error; \ }) -#define DEFINE_WAIT(x) +static inline int +waitqueue_active(wait_queue_head_t *q) +{ + return 0; /* XXX: not really implemented */ +} + +#define DEFINE_WAIT(name) \ + wait_queue_t name = {} + +static inline void +prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state) +{ +} + +static inline void +finish_wait(wait_queue_head_t *q, wait_queue_t *wait) +{ +} #endif /* _LINUX_WAIT_H_ */ diff --git a/sys/ofed/include/linux/workqueue.h b/sys/ofed/include/linux/workqueue.h index 075016ad5a3..3f2c7fe5569 100644 --- a/sys/ofed/include/linux/workqueue.h +++ b/sys/ofed/include/linux/workqueue.h @@ -91,11 +91,11 @@ do { \ #define flush_scheduled_work() flush_taskqueue(taskqueue_thread) -static inline int queue_work (struct workqueue_struct *q, struct work_struct *work) +static inline int queue_work(struct workqueue_struct *q, struct work_struct *work) { (work)->taskqueue = (q)->taskqueue; /* Return opposite val to align with Linux logic */ - return !taskqueue_enqueue((q)->taskqueue, &(work)->work_task); + return !taskqueue_enqueue((q)->taskqueue, &(work)->work_task); } static inline void @@ -151,6 +151,12 @@ _create_workqueue_common(char *name, int cpus) #define create_workqueue(name) \ _create_workqueue_common(name, MAXCPU) +#define alloc_ordered_workqueue(name, flags) \ + _create_workqueue_common(name, 1) + +#define alloc_workqueue(name, flags, max_active) \ + _create_workqueue_common(name, max_active) + static inline void destroy_workqueue(struct workqueue_struct *wq) { diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index 8d335507456..8509a6ce49a 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -344,7 +344,7 @@ struct mbuf { */ #define EXT_CLUSTER 1 /* mbuf cluster */ #define EXT_SFBUF 2 /* sendfile(2)'s sf_bufs */ -#define EXT_JUMBOP 3 /* jumbo cluster 4096 bytes */ +#define EXT_JUMBOP 3 /* jumbo cluster page sized */ #define EXT_JUMBO9 4 /* jumbo cluster 9216 bytes */ #define EXT_JUMBO16 5 /* jumbo cluster 16184 bytes */ #define EXT_PACKET 6 /* mbuf+cluster from packet zone */ @@ -524,6 +524,7 @@ extern uma_zone_t zone_jumbo9; extern uma_zone_t zone_jumbo16; extern uma_zone_t zone_ext_refcnt; +void mb_dupcl(struct mbuf *, const struct mbuf *); void mb_free_ext(struct mbuf *); int m_pkthdr_init(struct mbuf *, int); diff --git a/sys/sys/param.h b/sys/sys/param.h index 67c525f7a34..25870c321c9 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1100080 /* Master, propagated to newvers */ +#define __FreeBSD_version 1100081 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/sys/tools/fw_stub.awk b/sys/tools/fw_stub.awk index 0c7d5676a3f..4a82c0b2441 100644 --- a/sys/tools/fw_stub.awk +++ b/sys/tools/fw_stub.awk @@ -156,7 +156,7 @@ if (opt_l) { printc("\ TUNABLE_LONG_FETCH(\"legal." opt_l ".license_ack\", &" opt_l "_license_ack);\ if (!" opt_l "_license_ack) {\ - printf(\"" opt_m ": You need to read the LICENSE file in /usr/share/doc/legal/" opt_l "/.\\n\");\ + printf(\"" opt_m ": You need to read the LICENSE file in /usr/share/doc/legal/" opt_l ".LICENSE.\\n\");\ printf(\"" opt_m ": If you agree with the license, set legal." opt_l ".license_ack=1 in /boot/loader.conf.\\n\");\ return(EPERM);\ }\n"); diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index c587dfb3bbe..19c00de6e1b 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -481,9 +481,19 @@ ffs_reallocblks(ap) struct cluster_save *a_buflist; } */ *ap; { + struct ufsmount *ump; - if (doreallocblks == 0) + /* + * If the underlying device can do deletes, then skip reallocating + * the blocks of this file into contiguous sequences. Devices that + * benefit from BIO_DELETE also benefit from not moving the data. + * These devices are flash and therefore work less well with this + * optimization. Also skip if reallocblks has been disabled globally. + */ + ump = VTOI(ap->a_vp)->i_ump; + if (ump->um_candelete || doreallocblks == 0) return (ENOSPC); + /* * We can't wait in softdep prealloc as it may fsync and recurse * here. Instead we simply fail to reallocate blocks if this @@ -492,7 +502,7 @@ ffs_reallocblks(ap) if (DOINGSOFTDEP(ap->a_vp)) if (softdep_prealloc(ap->a_vp, MNT_NOWAIT) != 0) return (ENOSPC); - if (VTOI(ap->a_vp)->i_ump->um_fstype == UFS1) + if (ump->um_fstype == UFS1) return (ffs_reallocblks_ufs1(ap)); return (ffs_reallocblks_ufs2(ap)); } diff --git a/sys/vm/vm_init.c b/sys/vm/vm_init.c index be103875882..e6645858ad3 100644 --- a/sys/vm/vm_init.c +++ b/sys/vm/vm_init.c @@ -74,6 +74,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -229,12 +230,15 @@ again: /* * Allocate the buffer arena. + * + * Enable the quantum cache if we have more than 4 cpus. This + * avoids lock contention at the expense of some fragmentation. */ size = (long)nbuf * BKVASIZE; kmi->buffer_sva = firstaddr; kmi->buffer_eva = kmi->buffer_sva + size; vmem_init(buffer_arena, "buffer arena", kmi->buffer_sva, size, - PAGE_SIZE, 0, 0); + PAGE_SIZE, (mp_ncpus > 4) ? BKVASIZE * 8 : 0, 0); firstaddr += size; /* diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c index e0d00f5172f..188866efbec 100644 --- a/sys/vm/vm_pageout.c +++ b/sys/vm/vm_pageout.c @@ -292,11 +292,21 @@ vm_pageout_fallback_object_lock(vm_page_t m, vm_page_t *next) vm_page_lock(m); vm_pagequeue_lock(pq); - /* Page queue might have changed. */ + /* + * The page's object might have changed, and/or the page might + * have moved from its original position in the queue. If the + * page's object has changed, then the caller should abandon + * processing the page because the wrong object lock was + * acquired. Use the marker's plinks.q, not the page's, to + * determine if the page has been moved. The state of the + * page's plinks.q can be indeterminate; whereas, the marker's + * plinks.q must be valid. + */ *next = TAILQ_NEXT(&marker, plinks.q); - unchanged = (m->queue == queue && - m->object == object && - &marker == TAILQ_NEXT(m, plinks.q)); + unchanged = m->object == object && + m == TAILQ_PREV(&marker, pglist, plinks.q); + KASSERT(!unchanged || m->queue == queue, + ("page %p queue %d %d", m, queue, m->queue)); TAILQ_REMOVE(&pq->pq_pl, &marker, plinks.q); return (unchanged); } @@ -333,7 +343,9 @@ vm_pageout_page_lock(vm_page_t m, vm_page_t *next) /* Page queue might have changed. */ *next = TAILQ_NEXT(&marker, plinks.q); - unchanged = (m->queue == queue && &marker == TAILQ_NEXT(m, plinks.q)); + unchanged = m == TAILQ_PREV(&marker, pglist, plinks.q); + KASSERT(!unchanged || m->queue == queue, + ("page %p queue %d %d", m, queue, m->queue)); TAILQ_REMOVE(&pq->pq_pl, &marker, plinks.q); return (unchanged); } diff --git a/sys/x86/x86/busdma_bounce.c b/sys/x86/x86/busdma_bounce.c index 48f9bb31787..cac6c7158ca 100644 --- a/sys/x86/x86/busdma_bounce.c +++ b/sys/x86/x86/busdma_bounce.c @@ -1006,7 +1006,8 @@ add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, vm_offset_t vaddr, bpage->busaddr |= addr & PAGE_MASK; } bpage->datavaddr = vaddr; - bpage->datapage = PHYS_TO_VM_PAGE(addr & ~PAGE_MASK); + /* PHYS_TO_VM_PAGE() will truncate unaligned addresses. */ + bpage->datapage = PHYS_TO_VM_PAGE(addr); bpage->dataoffs = addr & PAGE_MASK; bpage->datacount = size; STAILQ_INSERT_TAIL(&(map->bpages), bpage, links); diff --git a/tests/Makefile b/tests/Makefile index 7fdc5d782b4..8b3ccb1452a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -4,6 +4,7 @@ SUBDIR+= etc SUBDIR+= sys +SUBDIR_PARALLEL= TESTSDIR= ${TESTSBASE} KYUAFILE= yes diff --git a/tests/sys/Makefile b/tests/sys/Makefile index 066c918188b..015040ddfd6 100644 --- a/tests/sys/Makefile +++ b/tests/sys/Makefile @@ -4,6 +4,7 @@ TESTSDIR= ${TESTSBASE}/sys +TESTS_SUBDIRS+= acl TESTS_SUBDIRS+= aio TESTS_SUBDIRS+= fifo TESTS_SUBDIRS+= file @@ -12,9 +13,13 @@ TESTS_SUBDIRS+= kqueue TESTS_SUBDIRS+= mqueue TESTS_SUBDIRS+= netinet TESTS_SUBDIRS+= opencrypto +TESTS_SUBDIRS+= posixshm +TESTS_SUBDIRS+= vfs TESTS_SUBDIRS+= vm # Items not integrated into kyua runs by default SUBDIR+= pjdfstest +SUBDIR_PARALLEL= + .include diff --git a/tools/regression/acltools/00.t b/tests/sys/acl/00.sh similarity index 89% rename from tools/regression/acltools/00.t rename to tests/sys/acl/00.sh index 88990887773..61e8115525f 100644 --- a/tools/regression/acltools/00.t +++ b/tests/sys/acl/00.sh @@ -37,12 +37,16 @@ # # Output should be obvious. -echo "1..4" - -if [ `whoami` != "root" ]; then - echo "not ok 1 - you need to be root to run this test." - exit 1 +if [ $(sysctl -n kern.features.ufs_acl 2>/dev/null || echo 0) -eq 0 ]; then + echo "1..0 # SKIP system does not have UFS ACL support" + exit 0 fi +if [ $(id -u) -ne 0 ]; then + echo "1..0 # SKIP you must be root" + exit 0 +fi + +echo "1..4" TESTDIR=$(dirname $(realpath $0)) @@ -50,9 +54,11 @@ TESTDIR=$(dirname $(realpath $0)) MD=`mdconfig -at swap -s 10m` MNT=`mktemp -dt acltools` newfs /dev/$MD > /dev/null +trap "cd /; umount -f $MNT; rmdir $MNT; mdconfig -d -u $MD" EXIT mount -o acls /dev/$MD $MNT if [ $? -ne 0 ]; then echo "not ok 1 - mount failed." + echo 'Bail out!' exit 1 fi @@ -78,8 +84,5 @@ else fi cd / -umount -f $MNT -rmdir $MNT -mdconfig -du $MD echo "ok 4" diff --git a/tools/regression/acltools/01.t b/tests/sys/acl/01.sh similarity index 89% rename from tools/regression/acltools/01.t rename to tests/sys/acl/01.sh index f84e0a98b46..93487a77263 100644 --- a/tools/regression/acltools/01.t +++ b/tests/sys/acl/01.sh @@ -39,21 +39,27 @@ # # Output should be obvious. -echo "1..4" - -if [ `whoami` != "root" ]; then - echo "not ok 1 - you need to be root to run this test." - exit 1 +if ! sysctl vfs.zfs.version.spa >/dev/null 2>&1; then + echo "1..0 # SKIP system doesn't have ZFS loaded" + exit 0 fi +if [ $(id -u) -ne 0 ]; then + echo "1..0 # SKIP you must be root" + exit 0 +fi + +echo "1..4" TESTDIR=$(dirname $(realpath $0)) # Set up the test filesystem. MD=`mdconfig -at swap -s 64m` MNT=`mktemp -dt acltools` +trap "cd /; zpool destroy -f acltools; rmdir $MNT; mdconfig -d -u $MD" EXIT zpool create -m $MNT acltools /dev/$MD if [ $? -ne 0 ]; then echo "not ok 1 - 'zpool create' failed." + echo 'Bail out!' exit 1 fi @@ -78,9 +84,4 @@ else echo "not ok 3" fi -cd / -zpool destroy -f acltools -rmdir $MNT -mdconfig -du $MD - echo "ok 4" diff --git a/tools/regression/acltools/02.t b/tests/sys/acl/02.sh similarity index 89% rename from tools/regression/acltools/02.t rename to tests/sys/acl/02.sh index 9a82c78d145..8fbb39de4d6 100644 --- a/tools/regression/acltools/02.t +++ b/tests/sys/acl/02.sh @@ -37,12 +37,16 @@ # # Output should be obvious. -echo "1..4" - -if [ `whoami` != "root" ]; then - echo "not ok 1 - you need to be root to run this test." - exit 1 +if [ $(sysctl -n kern.features.ufs_acl 2>/dev/null || echo 0) -eq 0 ]; then + echo "1..0 # SKIP system does not have UFS ACL support" + exit 0 fi +if [ $(id -u) -ne 0 ]; then + echo "1..0 # SKIP you must be root" + exit 0 +fi + +echo "1..4" TESTDIR=$(dirname $(realpath $0)) @@ -50,9 +54,11 @@ TESTDIR=$(dirname $(realpath $0)) MD=`mdconfig -at swap -s 10m` MNT=`mktemp -dt acltools` newfs /dev/$MD > /dev/null +trap "cd /; umount -f $MNT; rmdir $MNT; mdconfig -d -u $MD" EXIT mount -o nfsv4acls /dev/$MD $MNT if [ $? -ne 0 ]; then echo "not ok 1 - mount failed." + echo 'Bail out!' exit 1 fi @@ -82,9 +88,6 @@ else fi cd / -umount -f $MNT -rmdir $MNT -mdconfig -du $MD echo "ok 4" diff --git a/tools/regression/acltools/03.t b/tests/sys/acl/03.sh similarity index 91% rename from tools/regression/acltools/03.t rename to tests/sys/acl/03.sh index a0b4199c2bf..2b81b266230 100644 --- a/tools/regression/acltools/03.t +++ b/tests/sys/acl/03.sh @@ -34,12 +34,16 @@ # # Output should be obvious. -echo "1..5" - -if [ `whoami` != "root" ]; then - echo "not ok 1 - you need to be root to run this test." - exit 1 +if ! sysctl vfs.zfs.version.spa >/dev/null 2>&1; then + echo "1..0 # SKIP system doesn't have ZFS loaded" + exit 0 fi +if [ $(id -u) -ne 0 ]; then + echo "1..0 # SKIP you must be root" + exit 0 +fi + +echo "1..5" TESTDIR=$(dirname $(realpath $0)) MNTROOT=`mktemp -dt acltools` @@ -51,6 +55,7 @@ mkdir $MNT1 zpool create -m $MNT1 acltools /dev/$MD1 if [ $? -ne 0 ]; then echo "not ok 1 - 'zpool create' failed." + echo 'Bail out!' exit 1 fi @@ -63,6 +68,7 @@ newfs /dev/$MD2 > /dev/null mount -o acls /dev/$MD2 $MNT2 if [ $? -ne 0 ]; then echo "not ok 2 - mount failed." + echo 'Bail out!' exit 1 fi @@ -75,6 +81,7 @@ newfs /dev/$MD3 > /dev/null mount /dev/$MD3 $MNT3 if [ $? -ne 0 ]; then echo "not ok 3 - mount failed." + echo 'Bail out!' exit 1 fi diff --git a/tools/regression/acltools/04.t b/tests/sys/acl/04.sh similarity index 91% rename from tools/regression/acltools/04.t rename to tests/sys/acl/04.sh index 1a13183475f..5669c5a48d2 100644 --- a/tools/regression/acltools/04.t +++ b/tests/sys/acl/04.sh @@ -31,12 +31,16 @@ # # WARNING: It uses hardcoded ZFS pool name "acltools" -echo "1..3" - -if [ `whoami` != "root" ]; then - echo "not ok 1 - you need to be root to run this test." - exit 1 +if ! sysctl vfs.zfs.version.spa >/dev/null 2>&1; then + echo "1..0 # SKIP system doesn't have ZFS loaded" + exit 0 fi +if [ $(id -u) -ne 0 ]; then + echo "1..0 # SKIP you must be root" + exit 0 +fi + +echo "1..3" TESTDIR=$(dirname $(realpath $0)) diff --git a/tests/sys/acl/Makefile b/tests/sys/acl/Makefile new file mode 100644 index 00000000000..298b0a27c91 --- /dev/null +++ b/tests/sys/acl/Makefile @@ -0,0 +1,29 @@ +# $FreeBSD$ + +TESTSDIR= ${TESTSBASE}/sys/acl + +BINDIR= ${TESTSDIR} + +FILES+= tools-crossfs.test +FILES+= tools-nfs4.test +FILES+= tools-nfs4-psarc.test +FILES+= tools-nfs4-trivial.test +FILES+= tools-posix.test + +SCRIPTS+= run + +TAP_TESTS_SH+= 00 +TAP_TESTS_SH+= 01 +TAP_TESTS_SH+= 02 +TAP_TESTS_SH+= 03 +TAP_TESTS_SH+= 04 + +.for t in ${TAP_TESTS_SH} +TEST_METADATA.$t+= required_user="root" +.endfor + +.for t in 01 03 04 +TEST_METADATA.$t+= required_programs="/sbin/zpool" +.endfor + +.include diff --git a/tools/regression/acltools/aclfuzzer.sh b/tests/sys/acl/aclfuzzer.sh similarity index 100% rename from tools/regression/acltools/aclfuzzer.sh rename to tests/sys/acl/aclfuzzer.sh diff --git a/tools/regression/acltools/mktrivial.sh b/tests/sys/acl/mktrivial.sh similarity index 100% rename from tools/regression/acltools/mktrivial.sh rename to tests/sys/acl/mktrivial.sh diff --git a/tools/regression/acltools/run b/tests/sys/acl/run similarity index 100% rename from tools/regression/acltools/run rename to tests/sys/acl/run diff --git a/tools/regression/acltools/tools-crossfs.test b/tests/sys/acl/tools-crossfs.test similarity index 100% rename from tools/regression/acltools/tools-crossfs.test rename to tests/sys/acl/tools-crossfs.test diff --git a/tools/regression/acltools/tools-nfs4-psarc.test b/tests/sys/acl/tools-nfs4-psarc.test similarity index 100% rename from tools/regression/acltools/tools-nfs4-psarc.test rename to tests/sys/acl/tools-nfs4-psarc.test diff --git a/tools/regression/acltools/tools-nfs4-trivial.test b/tests/sys/acl/tools-nfs4-trivial.test similarity index 100% rename from tools/regression/acltools/tools-nfs4-trivial.test rename to tests/sys/acl/tools-nfs4-trivial.test diff --git a/tools/regression/acltools/tools-nfs4.test b/tests/sys/acl/tools-nfs4.test similarity index 100% rename from tools/regression/acltools/tools-nfs4.test rename to tests/sys/acl/tools-nfs4.test diff --git a/tools/regression/acltools/tools-posix.test b/tests/sys/acl/tools-posix.test similarity index 100% rename from tools/regression/acltools/tools-posix.test rename to tests/sys/acl/tools-posix.test diff --git a/tests/sys/pjdfstest/Makefile b/tests/sys/pjdfstest/Makefile index 7047811528c..a398d394ea2 100644 --- a/tests/sys/pjdfstest/Makefile +++ b/tests/sys/pjdfstest/Makefile @@ -2,5 +2,6 @@ SUBDIR+= pjdfstest SUBDIR+= tests +SUBDIR_PARALLEL= .include diff --git a/tests/sys/pjdfstest/tests/Makefile b/tests/sys/pjdfstest/tests/Makefile index 95b4bbe29b4..61b15dd12c2 100644 --- a/tests/sys/pjdfstest/tests/Makefile +++ b/tests/sys/pjdfstest/tests/Makefile @@ -36,4 +36,6 @@ TESTS_SUBDIRS+= symlink TESTS_SUBDIRS+= truncate TESTS_SUBDIRS+= unlink +SUBDIR_PARALLEL= + .include diff --git a/tests/sys/posixshm/Makefile b/tests/sys/posixshm/Makefile new file mode 100644 index 00000000000..2acdfa4f64b --- /dev/null +++ b/tests/sys/posixshm/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +TESTSDIR= ${TESTSBASE}/sys/posixshm + +ATF_TESTS_C+= posixshm_test + +.include diff --git a/tests/sys/posixshm/posixshm_test.c b/tests/sys/posixshm/posixshm_test.c new file mode 100644 index 00000000000..b1a7c08db9a --- /dev/null +++ b/tests/sys/posixshm/posixshm_test.c @@ -0,0 +1,629 @@ +/*- + * Copyright (c) 2006 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define TEST_PATH_LEN 256 +static char test_path[TEST_PATH_LEN]; + +static void +gen_test_path(void) +{ + char *tmpdir = getenv("TMPDIR"); + + if (tmpdir == NULL) + tmpdir = "/tmp"; + + snprintf(test_path, sizeof(test_path), "%s/tmp.XXXXXX", tmpdir); + test_path[sizeof(test_path) - 1] = '\0'; + ATF_REQUIRE_MSG(mkstemp(test_path) != -1, + "mkstemp failed; errno=%d", errno); + ATF_REQUIRE_MSG(unlink(test_path) == 0, + "unlink failed; errno=%d", errno); +} + +/* + * Attempt a shm_open() that should fail with an expected error of 'error'. + */ +static void +shm_open_should_fail(const char *path, int flags, mode_t mode, int error) +{ + int fd; + + fd = shm_open(path, flags, mode); + ATF_CHECK_MSG(fd == -1, "shm_open didn't fail"); + ATF_CHECK_MSG(error == errno, + "shm_open didn't fail with expected errno; errno=%d; expected " + "errno=%d", errno, error); +} + +/* + * Attempt a shm_unlink() that should fail with an expected error of 'error'. + */ +static void +shm_unlink_should_fail(const char *path, int error) +{ + + ATF_CHECK_MSG(shm_unlink(path) == -1, "shm_unlink didn't fail"); + ATF_CHECK_MSG(error == errno, + "shm_unlink didn't fail with expected errno; errno=%d; expected " + "errno=%d", errno, error); +} + +/* + * Open the test object and write '1' to the first byte. Returns valid fd + * on success and -1 on failure. + */ +static int +scribble_object(void) +{ + char *page; + int fd; + + gen_test_path(); + + fd = shm_open(test_path, O_CREAT|O_EXCL|O_RDWR, 0777); + if (fd < 0 && errno == EEXIST) { + if (shm_unlink(test_path) < 0) + atf_tc_fail("shm_unlink"); + fd = shm_open(test_path, O_CREAT | O_EXCL | O_RDWR, 0777); + } + if (fd < 0) + atf_tc_fail("shm_open failed; errno=%d", errno); + if (ftruncate(fd, getpagesize()) < 0) + atf_tc_fail("ftruncate failed; errno=%d", errno); + + page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + if (page == MAP_FAILED) + atf_tc_fail("mmap failed; errno=%d", errno); + + page[0] = '1'; + if (munmap(page, getpagesize()) < 0) + atf_tc_fail("munmap failed; errno=%d", errno); + + return (fd); +} + +ATF_TC_WITHOUT_HEAD(remap_object); +ATF_TC_BODY(remap_object, tc) +{ + char *page; + int fd; + + fd = scribble_object(); + + page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + if (page == MAP_FAILED) + atf_tc_fail("mmap(2) failed; errno=%d", errno); + + if (page[0] != '1') + atf_tc_fail("missing data ('%c' != '1')", page[0]); + + close(fd); + if (munmap(page, getpagesize()) < 0) + atf_tc_fail("munmap failed; errno=%d", errno); + + ATF_REQUIRE_MSG(shm_unlink(test_path) != -1, + "shm_unlink failed; errno=%d", errno); +} + +ATF_TC_WITHOUT_HEAD(reopen_object); +ATF_TC_BODY(reopen_object, tc) +{ + char *page; + int fd; + + fd = scribble_object(); + close(fd); + + fd = shm_open(test_path, O_RDONLY, 0777); + if (fd < 0) + atf_tc_fail("shm_open(2) failed; errno=%d", errno); + + page = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, fd, 0); + if (page == MAP_FAILED) + atf_tc_fail("mmap(2) failed; errno=%d", errno); + + if (page[0] != '1') + atf_tc_fail("missing data ('%c' != '1')", page[0]); + + munmap(page, getpagesize()); + close(fd); + ATF_REQUIRE_MSG(shm_unlink(test_path) != -1, + "shm_unlink failed; errno=%d", errno); +} + +ATF_TC_WITHOUT_HEAD(readonly_mmap_write); +ATF_TC_BODY(readonly_mmap_write, tc) +{ + char *page; + int fd; + + gen_test_path(); + + fd = shm_open(test_path, O_RDONLY | O_CREAT, 0777); + ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno); + + /* PROT_WRITE should fail with EACCES. */ + page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + if (page != MAP_FAILED) + atf_tc_fail("mmap(PROT_WRITE) succeeded unexpectedly"); + + if (errno != EACCES) + atf_tc_fail("mmap(PROT_WRITE) didn't fail with EACCES; " + "errno=%d", errno); + + close(fd); + ATF_REQUIRE_MSG(shm_unlink(test_path) != -1, + "shm_unlink failed; errno=%d", errno); +} + +ATF_TC_WITHOUT_HEAD(open_after_link); +ATF_TC_BODY(open_after_link, tc) +{ + int fd; + + gen_test_path(); + + fd = shm_open(test_path, O_RDONLY | O_CREAT, 0777); + ATF_REQUIRE_MSG(fd >= 0, "shm_open(1) failed; errno=%d", errno); + close(fd); + + ATF_REQUIRE_MSG(shm_unlink(test_path) != -1, "shm_unlink failed: %d", + errno); + + shm_open_should_fail(test_path, O_RDONLY, 0777, ENOENT); +} + +ATF_TC_WITHOUT_HEAD(open_invalid_path); +ATF_TC_BODY(open_invalid_path, tc) +{ + + shm_open_should_fail("blah", O_RDONLY, 0777, EINVAL); +} + +ATF_TC_WITHOUT_HEAD(open_write_only); +ATF_TC_BODY(open_write_only, tc) +{ + + gen_test_path(); + + shm_open_should_fail(test_path, O_WRONLY, 0777, EINVAL); +} + +ATF_TC_WITHOUT_HEAD(open_extra_flags); +ATF_TC_BODY(open_extra_flags, tc) +{ + + gen_test_path(); + + shm_open_should_fail(test_path, O_RDONLY | O_DIRECT, 0777, EINVAL); +} + +ATF_TC_WITHOUT_HEAD(open_anon); +ATF_TC_BODY(open_anon, tc) +{ + int fd; + + fd = shm_open(SHM_ANON, O_RDWR, 0777); + ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno); + close(fd); +} + +ATF_TC_WITHOUT_HEAD(open_anon_readonly); +ATF_TC_BODY(open_anon_readonly, tc) +{ + + shm_open_should_fail(SHM_ANON, O_RDONLY, 0777, EINVAL); +} + +ATF_TC_WITHOUT_HEAD(open_bad_path_pointer); +ATF_TC_BODY(open_bad_path_pointer, tc) +{ + + shm_open_should_fail((char *)1024, O_RDONLY, 0777, EFAULT); +} + +ATF_TC_WITHOUT_HEAD(open_path_too_long); +ATF_TC_BODY(open_path_too_long, tc) +{ + char *page; + + page = malloc(MAXPATHLEN + 1); + memset(page, 'a', MAXPATHLEN); + page[MAXPATHLEN] = '\0'; + shm_open_should_fail(page, O_RDONLY, 0777, ENAMETOOLONG); + free(page); +} + +ATF_TC_WITHOUT_HEAD(open_nonexisting_object); +ATF_TC_BODY(open_nonexisting_object, tc) +{ + + shm_open_should_fail("/notreallythere", O_RDONLY, 0777, ENOENT); +} + +ATF_TC_WITHOUT_HEAD(open_create_existing_object); +ATF_TC_BODY(open_create_existing_object, tc) +{ + int fd; + + gen_test_path(); + + fd = shm_open(test_path, O_RDONLY|O_CREAT, 0777); + ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno); + close(fd); + + shm_open_should_fail(test_path, O_RDONLY|O_CREAT|O_EXCL, + 0777, EEXIST); + + ATF_REQUIRE_MSG(shm_unlink(test_path) != -1, + "shm_unlink failed; errno=%d", errno); +} + +ATF_TC_WITHOUT_HEAD(trunc_resets_object); +ATF_TC_BODY(trunc_resets_object, tc) +{ + struct stat sb; + int fd; + + gen_test_path(); + + /* Create object and set size to 1024. */ + fd = shm_open(test_path, O_RDWR | O_CREAT, 0777); + ATF_REQUIRE_MSG(fd >= 0, "shm_open(1) failed; errno=%d", errno); + ATF_REQUIRE_MSG(ftruncate(fd, 1024) != -1, + "ftruncate failed; errno=%d", errno); + ATF_REQUIRE_MSG(fstat(fd, &sb) != -1, + "fstat(1) failed; errno=%d", errno); + ATF_REQUIRE_MSG(sb.st_size == 1024, "size %d != 1024", (int)sb.st_size); + close(fd); + + /* Open with O_TRUNC which should reset size to 0. */ + fd = shm_open(test_path, O_RDWR | O_TRUNC, 0777); + ATF_REQUIRE_MSG(fd >= 0, "shm_open(2) failed; errno=%d", errno); + ATF_REQUIRE_MSG(fstat(fd, &sb) != -1, + "fstat(2) failed; errno=%d", errno); + ATF_REQUIRE_MSG(sb.st_size == 0, + "size was not 0 after truncation: %d", (int)sb.st_size); + close(fd); + ATF_REQUIRE_MSG(shm_unlink(test_path) != -1, + "shm_unlink failed; errno=%d", errno); +} + +ATF_TC_WITHOUT_HEAD(unlink_bad_path_pointer); +ATF_TC_BODY(unlink_bad_path_pointer, tc) +{ + + shm_unlink_should_fail((char *)1024, EFAULT); +} + +ATF_TC_WITHOUT_HEAD(unlink_path_too_long); +ATF_TC_BODY(unlink_path_too_long, tc) +{ + char *page; + + page = malloc(MAXPATHLEN + 1); + memset(page, 'a', MAXPATHLEN); + page[MAXPATHLEN] = '\0'; + shm_unlink_should_fail(page, ENAMETOOLONG); + free(page); +} + +ATF_TC_WITHOUT_HEAD(object_resize); +ATF_TC_BODY(object_resize, tc) +{ + pid_t pid; + struct stat sb; + char err_buf[1024], *page; + int fd, status; + + /* Start off with a size of a single page. */ + fd = shm_open(SHM_ANON, O_CREAT|O_RDWR, 0777); + if (fd < 0) + atf_tc_fail("shm_open failed; errno=%d", errno); + + if (ftruncate(fd, getpagesize()) < 0) + atf_tc_fail("ftruncate(1) failed; errno=%d", errno); + + if (fstat(fd, &sb) < 0) + atf_tc_fail("fstat(1) failed; errno=%d", errno); + + if (sb.st_size != getpagesize()) + atf_tc_fail("first resize failed (%d != %d)", + (int)sb.st_size, getpagesize()); + + /* Write a '1' to the first byte. */ + page = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, + 0); + if (page == MAP_FAILED) + atf_tc_fail("mmap(1)"); + + page[0] = '1'; + + if (munmap(page, getpagesize()) < 0) + atf_tc_fail("munmap(1) failed; errno=%d", errno); + + /* Grow the object to 2 pages. */ + if (ftruncate(fd, getpagesize() * 2) < 0) + atf_tc_fail("ftruncate(2) failed; errno=%d", errno); + + if (fstat(fd, &sb) < 0) + atf_tc_fail("fstat(2) failed; errno=%d", errno); + + if (sb.st_size != getpagesize() * 2) + atf_tc_fail("second resize failed (%d != %d)", + (int)sb.st_size, getpagesize() * 2); + + /* Check for '1' at the first byte. */ + page = mmap(0, getpagesize() * 2, PROT_READ|PROT_WRITE, MAP_SHARED, + fd, 0); + if (page == MAP_FAILED) + atf_tc_fail("mmap(2) failed; errno=%d", errno); + + if (page[0] != '1') + atf_tc_fail("'%c' != '1'", page[0]); + + /* Write a '2' at the start of the second page. */ + page[getpagesize()] = '2'; + + /* Shrink the object back to 1 page. */ + if (ftruncate(fd, getpagesize()) < 0) + atf_tc_fail("ftruncate(3) failed; errno=%d", errno); + + if (fstat(fd, &sb) < 0) + atf_tc_fail("fstat(3) failed; errno=%d", errno); + + if (sb.st_size != getpagesize()) + atf_tc_fail("third resize failed (%d != %d)", + (int)sb.st_size, getpagesize()); + + /* + * Fork a child process to make sure the second page is no + * longer valid. + */ + pid = fork(); + if (pid == -1) + atf_tc_fail("fork failed; errno=%d", errno); + + if (pid == 0) { + struct rlimit lim; + char c; + + /* Don't generate a core dump. */ + getrlimit(RLIMIT_CORE, &lim); + lim.rlim_cur = 0; + setrlimit(RLIMIT_CORE, &lim); + + /* + * The previous ftruncate(2) shrunk the backing object + * so that this address is no longer valid, so reading + * from it should trigger a SIGSEGV. + */ + c = page[getpagesize()]; + fprintf(stderr, "child: page 1: '%c'\n", c); + exit(0); + } + + if (wait(&status) < 0) + atf_tc_fail("wait failed; errno=%d", errno); + + if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGSEGV) + atf_tc_fail("child terminated with status %x", status); + + /* Grow the object back to 2 pages. */ + if (ftruncate(fd, getpagesize() * 2) < 0) + atf_tc_fail("ftruncate(2) failed; errno=%d", errno); + + if (fstat(fd, &sb) < 0) + atf_tc_fail("fstat(2) failed; errno=%d", errno); + + if (sb.st_size != getpagesize() * 2) + atf_tc_fail("fourth resize failed (%d != %d)", + (int)sb.st_size, getpagesize()); + + /* + * Note that the mapping at 'page' for the second page is + * still valid, and now that the shm object has been grown + * back up to 2 pages, there is now memory backing this page + * so the read will work. However, the data should be zero + * rather than '2' as the old data was thrown away when the + * object was shrunk and the new pages when an object are + * grown are zero-filled. + */ + if (page[getpagesize()] != 0) + atf_tc_fail("invalid data at %d: %x != 0", + getpagesize(), (int)page[getpagesize()]); + + close(fd); +} + +/* Signal handler which does nothing. */ +static void +ignoreit(int sig __unused) +{ + ; +} + +ATF_TC_WITHOUT_HEAD(shm_functionality_across_fork); +ATF_TC_BODY(shm_functionality_across_fork, tc) +{ + char *cp, c; + int error, desc, rv; + long scval; + sigset_t ss; + struct sigaction sa; + void *region; + size_t i, psize; + +#ifndef _POSIX_SHARED_MEMORY_OBJECTS + printf("_POSIX_SHARED_MEMORY_OBJECTS is undefined\n"); +#else + printf("_POSIX_SHARED_MEMORY_OBJECTS is defined as %ld\n", + (long)_POSIX_SHARED_MEMORY_OBJECTS - 0); + if (_POSIX_SHARED_MEMORY_OBJECTS - 0 == -1) + printf("***Indicates this feature may be unsupported!\n"); +#endif + errno = 0; + scval = sysconf(_SC_SHARED_MEMORY_OBJECTS); + if (scval == -1 && errno != 0) { + atf_tc_fail("sysconf(_SC_SHARED_MEMORY_OBJECTS) failed; " + "errno=%d", errno); + } else { + printf("sysconf(_SC_SHARED_MEMORY_OBJECTS) returns %ld\n", + scval); + if (scval == -1) + printf("***Indicates this feature is unsupported!\n"); + } + + errno = 0; + scval = sysconf(_SC_PAGESIZE); + if (scval == -1 && errno != 0) { + atf_tc_fail("sysconf(_SC_PAGESIZE) failed; errno=%d", errno); + } else if (scval <= 0 || (size_t)psize != psize) { + fprintf(stderr, "bogus return from sysconf(_SC_PAGESIZE): %ld", + scval); + psize = 4096; + } else { + printf("sysconf(_SC_PAGESIZE) returns %ld\n", scval); + psize = scval; + } + + gen_test_path(); + desc = shm_open(test_path, O_EXCL | O_CREAT | O_RDWR, 0600); + + ATF_REQUIRE_MSG(desc >= 0, "shm_open failed; errno=%d", errno); + ATF_REQUIRE_MSG(shm_unlink(test_path) == 0, + "shm_unlink failed; errno=%d", errno); + ATF_REQUIRE_MSG(ftruncate(desc, (off_t)psize) != -1, + "ftruncate failed; errno=%d", errno); + + region = mmap((void *)0, psize, PROT_READ | PROT_WRITE, MAP_SHARED, + desc, (off_t)0); + ATF_REQUIRE_MSG(region != MAP_FAILED, "mmap failed; errno=%d", errno); + memset(region, '\377', psize); + + sa.sa_flags = 0; + sa.sa_handler = ignoreit; + sigemptyset(&sa.sa_mask); + ATF_REQUIRE_MSG(sigaction(SIGUSR1, &sa, (struct sigaction *)0) == 0, + "sigaction failed; errno=%d", errno); + + sigemptyset(&ss); + sigaddset(&ss, SIGUSR1); + ATF_REQUIRE_MSG(sigprocmask(SIG_BLOCK, &ss, (sigset_t *)0) == 0, + "sigprocmask failed; errno=%d", errno); + + rv = fork(); + ATF_REQUIRE_MSG(rv != -1, "fork failed; errno=%d", errno); + if (rv == 0) { + sigemptyset(&ss); + sigsuspend(&ss); + + for (cp = region; cp < (char *)region + psize; cp++) { + if (*cp != '\151') + _exit(1); + } + if (lseek(desc, 0, SEEK_SET) == -1) + _exit(1); + for (i = 0; i < psize; i++) { + error = read(desc, &c, 1); + if (c != '\151') + _exit(1); + } + _exit(0); + } else { + int status; + + memset(region, '\151', psize - 2); + error = pwrite(desc, region, 2, psize - 2); + if (error != 2) { + if (error >= 0) + atf_tc_fail("short write; %d bytes written", + error); + else + atf_tc_fail("shmfd write"); + } + kill(rv, SIGUSR1); + waitpid(rv, &status, 0); + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + printf("Functionality test successful\n"); + } else if (WIFEXITED(status)) { + atf_tc_fail("Child process exited with status %d", + WEXITSTATUS(status)); + } else { + atf_tc_fail("Child process terminated with %s", + strsignal(WTERMSIG(status))); + } + } +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, remap_object); + ATF_TP_ADD_TC(tp, reopen_object); + ATF_TP_ADD_TC(tp, readonly_mmap_write); + ATF_TP_ADD_TC(tp, open_after_link); + ATF_TP_ADD_TC(tp, open_invalid_path); + ATF_TP_ADD_TC(tp, open_write_only); + ATF_TP_ADD_TC(tp, open_extra_flags); + ATF_TP_ADD_TC(tp, open_anon); + ATF_TP_ADD_TC(tp, open_anon_readonly); + ATF_TP_ADD_TC(tp, open_bad_path_pointer); + ATF_TP_ADD_TC(tp, open_path_too_long); + ATF_TP_ADD_TC(tp, open_nonexisting_object); + ATF_TP_ADD_TC(tp, open_create_existing_object); + ATF_TP_ADD_TC(tp, shm_functionality_across_fork); + ATF_TP_ADD_TC(tp, trunc_resets_object); + ATF_TP_ADD_TC(tp, unlink_bad_path_pointer); + ATF_TP_ADD_TC(tp, unlink_path_too_long); + ATF_TP_ADD_TC(tp, object_resize); + + return (atf_no_error()); +} diff --git a/tests/sys/vfs/Makefile b/tests/sys/vfs/Makefile new file mode 100644 index 00000000000..48f522634bf --- /dev/null +++ b/tests/sys/vfs/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +TESTSDIR= ${TESTSBASE}/sys/vfs + +PLAIN_TESTS_SH+= trailing_slash + +.include diff --git a/tools/regression/vfs/trailing_slash.t b/tests/sys/vfs/trailing_slash.sh similarity index 100% rename from tools/regression/vfs/trailing_slash.t rename to tests/sys/vfs/trailing_slash.sh diff --git a/tools/build/options/WITHOUT_LLDB b/tools/build/options/WITHOUT_LLDB new file mode 100644 index 00000000000..7cc45d91efd --- /dev/null +++ b/tools/build/options/WITHOUT_LLDB @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Set to not build the LLDB debugger. diff --git a/tools/regression/lib/msun/test-fenv.c b/tools/regression/lib/msun/test-fenv.c index 71e8eedb3bc..0ea6e42a7c8 100644 --- a/tools/regression/lib/msun/test-fenv.c +++ b/tools/regression/lib/msun/test-fenv.c @@ -133,7 +133,34 @@ test_dfl_env(void) fenv_t env; fegetenv(&env); + +#ifdef __amd64__ + /* + * Compare the fields that the AMD [1] and Intel [2] specs say will be + * set once fnstenv returns. + * + * Not all amd64 capable processors implement the fnstenv instruction + * by zero'ing out the env.__x87.__other field (example: AMD Opteron + * 6308). The AMD64/x64 specs aren't explicit on what the + * env.__x87.__other field will contain after fnstenv is executed, so + * the values in env.__x87.__other could be filled with arbitrary + * data depending on how the CPU implements fnstenv. + * + * 1. http://support.amd.com/TechDocs/26569_APM_v5.pdf + * 2. http://www.intel.com/Assets/en_US/PDF/manual/253666.pdf + */ + assert(memcmp(&env.__mxcsr, &FE_DFL_ENV->__mxcsr, + sizeof(env.__mxcsr)) == 0); + assert(memcmp(&env.__x87.__control, &FE_DFL_ENV->__x87.__control, + sizeof(env.__x87.__control)) == 0); + assert(memcmp(&env.__x87.__status, &FE_DFL_ENV->__x87.__status, + sizeof(env.__x87.__status)) == 0); + assert(memcmp(&env.__x87.__tag, &FE_DFL_ENV->__x87.__tag, + sizeof(env.__x87.__tag)) == 0); +#else assert(memcmp(&env, FE_DFL_ENV, sizeof(env)) == 0); +#endif + #endif assert(fetestexcept(FE_ALL_EXCEPT) == 0); } diff --git a/tools/regression/posixshm/Makefile b/tools/regression/posixshm/Makefile deleted file mode 100644 index 5ef70c93bbd..00000000000 --- a/tools/regression/posixshm/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# $FreeBSD$ - -PROG= posixshm -SRCS= posixshm.c test.c -MAN= - -WARNS?= 3 - -.include diff --git a/tools/regression/posixshm/posixshm.c b/tools/regression/posixshm/posixshm.c deleted file mode 100644 index 6f4c3069397..00000000000 --- a/tools/regression/posixshm/posixshm.c +++ /dev/null @@ -1,627 +0,0 @@ -/*- - * Copyright (c) 2006 Robert N. M. Watson - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "test.h" - -#define TEST_PATH "/tmp/posixshm_regression_test" - -/* - * Attempt a shm_open() that should fail with an expected error of 'error'. - */ -static void -shm_open_should_fail(const char *path, int flags, mode_t mode, int error) -{ - int fd; - - fd = shm_open(path, flags, mode); - if (fd >= 0) { - fail_err("shm_open() didn't fail"); - close(fd); - return; - } - if (errno != error) { - fail_errno("shm_open"); - return; - } - pass(); -} - -/* - * Attempt a shm_unlink() that should fail with an expected error of 'error'. - */ -static void -shm_unlink_should_fail(const char *path, int error) -{ - - if (shm_unlink(path) >= 0) { - fail_err("shm_unlink() didn't fail"); - return; - } - if (errno != error) { - fail_errno("shm_unlink"); - return; - } - pass(); -} - -/* - * Open the test object and write '1' to the first byte. Returns valid fd - * on success and -1 on failure. - */ -static int -scribble_object(void) -{ - char *page; - int fd; - - fd = shm_open(TEST_PATH, O_CREAT | O_EXCL | O_RDWR, 0777); - if (fd < 0 && errno == EEXIST) { - if (shm_unlink(TEST_PATH) < 0) { - fail_errno("shm_unlink"); - return (-1); - } - fd = shm_open(TEST_PATH, O_CREAT | O_EXCL | O_RDWR, 0777); - } - if (fd < 0) { - fail_errno("shm_open"); - return (-1); - } - if (ftruncate(fd, getpagesize()) < 0) { - fail_errno("ftruncate"); - close(fd); - shm_unlink(TEST_PATH); - return (-1); - } - - page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, - 0); - if (page == MAP_FAILED) { - fail_errno("mmap"); - close(fd); - shm_unlink(TEST_PATH); - return (-1); - } - - page[0] = '1'; - - if (munmap(page, getpagesize()) < 0) { - fail_errno("munmap"); - close(fd); - shm_unlink(TEST_PATH); - return (-1); - } - - return (fd); -} - -static void -remap_object(void) -{ - char *page; - int fd; - - fd = scribble_object(); - if (fd < 0) - return; - - page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, - 0); - if (page == MAP_FAILED) { - fail_errno("mmap(2)"); - close(fd); - shm_unlink(TEST_PATH); - return; - } - - if (page[0] != '1') { - fail_err("missing data"); - close(fd); - shm_unlink(TEST_PATH); - return; - } - - close(fd); - if (munmap(page, getpagesize()) < 0) { - fail_errno("munmap"); - shm_unlink(TEST_PATH); - return; - } - - if (shm_unlink(TEST_PATH) < 0) { - fail_errno("shm_unlink"); - return; - } - - pass(); -} -TEST(remap_object, "remap object"); - -static void -reopen_object(void) -{ - char *page; - int fd; - - fd = scribble_object(); - if (fd < 0) - return; - close(fd); - - fd = shm_open(TEST_PATH, O_RDONLY, 0777); - if (fd < 0) { - fail_errno("shm_open(2)"); - shm_unlink(TEST_PATH); - return; - } - page = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, fd, 0); - if (page == MAP_FAILED) { - fail_errno("mmap(2)"); - close(fd); - shm_unlink(TEST_PATH); - return; - } - - if (page[0] != '1') { - fail_err("missing data"); - munmap(page, getpagesize()); - close(fd); - shm_unlink(TEST_PATH); - return; - } - - munmap(page, getpagesize()); - close(fd); - shm_unlink(TEST_PATH); - pass(); -} -TEST(reopen_object, "reopen object"); - -static void -readonly_mmap_write(void) -{ - char *page; - int fd; - - fd = shm_open(TEST_PATH, O_RDONLY | O_CREAT, 0777); - if (fd < 0) { - fail_errno("shm_open"); - return; - } - - /* PROT_WRITE should fail with EACCES. */ - page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, - 0); - if (page != MAP_FAILED) { - fail_err("mmap(PROT_WRITE) succeeded"); - munmap(page, getpagesize()); - close(fd); - shm_unlink(TEST_PATH); - return; - } - if (errno != EACCES) { - fail_errno("mmap"); - close(fd); - shm_unlink(TEST_PATH); - return; - } - - close(fd); - shm_unlink(TEST_PATH); - pass(); -} -TEST(readonly_mmap_write, "RDONLY object"); - -static void -open_after_unlink(void) -{ - int fd; - - fd = shm_open(TEST_PATH, O_RDONLY | O_CREAT, 0777); - if (fd < 0) { - fail_errno("shm_open(1)"); - return; - } - close(fd); - - if (shm_unlink(TEST_PATH) < 0) { - fail_errno("shm_unlink"); - return; - } - - shm_open_should_fail(TEST_PATH, O_RDONLY, 0777, ENOENT); -} -TEST(open_after_unlink, "open after unlink"); - -static void -open_invalid_path(void) -{ - - shm_open_should_fail("blah", O_RDONLY, 0777, EINVAL); -} -TEST(open_invalid_path, "open invalid path"); - -static void -open_write_only(void) -{ - - shm_open_should_fail(TEST_PATH, O_WRONLY, 0777, EINVAL); -} -TEST(open_write_only, "open with O_WRONLY"); - -static void -open_extra_flags(void) -{ - - shm_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, EINVAL); -} -TEST(open_extra_flags, "open with extra flags"); - -static void -open_anon(void) -{ - int fd; - - fd = shm_open(SHM_ANON, O_RDWR, 0777); - if (fd < 0) { - fail_errno("shm_open"); - return; - } - close(fd); - pass(); -} -TEST(open_anon, "open anonymous object"); - -static void -open_anon_readonly(void) -{ - - shm_open_should_fail(SHM_ANON, O_RDONLY, 0777, EINVAL); -} -TEST(open_anon_readonly, "open SHM_ANON with O_RDONLY"); - -static void -open_bad_path_pointer(void) -{ - - shm_open_should_fail((char *)1024, O_RDONLY, 0777, EFAULT); -} -TEST(open_bad_path_pointer, "open bad path pointer"); - -static void -open_path_too_long(void) -{ - char *page; - - page = malloc(MAXPATHLEN + 1); - memset(page, 'a', MAXPATHLEN); - page[MAXPATHLEN] = '\0'; - shm_open_should_fail(page, O_RDONLY, 0777, ENAMETOOLONG); - free(page); -} -TEST(open_path_too_long, "open pathname too long"); - -static void -open_nonexisting_object(void) -{ - - shm_open_should_fail("/notreallythere", O_RDONLY, 0777, ENOENT); -} -TEST(open_nonexisting_object, "open nonexistent object"); - -static void -exclusive_create_existing_object(void) -{ - int fd; - - fd = shm_open("/tmp/notreallythere", O_RDONLY | O_CREAT, 0777); - if (fd < 0) { - fail_errno("shm_open(O_CREAT)"); - return; - } - close(fd); - - shm_open_should_fail("/tmp/notreallythere", O_RDONLY | O_CREAT | O_EXCL, - 0777, EEXIST); - - shm_unlink("/tmp/notreallythere"); -} -TEST(exclusive_create_existing_object, "O_EXCL of existing object"); - -static void -trunc_resets_object(void) -{ - struct stat sb; - int fd; - - /* Create object and set size to 1024. */ - fd = shm_open(TEST_PATH, O_RDWR | O_CREAT, 0777); - if (fd < 0) { - fail_errno("shm_open(1)"); - return; - } - if (ftruncate(fd, 1024) < 0) { - fail_errno("ftruncate"); - close(fd); - return; - } - if (fstat(fd, &sb) < 0) { - fail_errno("fstat(1)"); - close(fd); - return; - } - if (sb.st_size != 1024) { - fail_err("size %d != 1024", (int)sb.st_size); - close(fd); - return; - } - close(fd); - - /* Open with O_TRUNC which should reset size to 0. */ - fd = shm_open(TEST_PATH, O_RDWR | O_TRUNC, 0777); - if (fd < 0) { - fail_errno("shm_open(2)"); - return; - } - if (fstat(fd, &sb) < 0) { - fail_errno("fstat(2)"); - close(fd); - return; - } - if (sb.st_size != 0) { - fail_err("size after O_TRUNC %d != 0", (int)sb.st_size); - close(fd); - return; - } - close(fd); - if (shm_unlink(TEST_PATH) < 0) { - fail_errno("shm_unlink"); - return; - } - pass(); -} -TEST(trunc_resets_object, "O_TRUNC resets size"); - -static void -unlink_bad_path_pointer(void) -{ - - shm_unlink_should_fail((char *)1024, EFAULT); -} -TEST(unlink_bad_path_pointer, "unlink bad path pointer"); - -static void -unlink_path_too_long(void) -{ - char *page; - - page = malloc(MAXPATHLEN + 1); - memset(page, 'a', MAXPATHLEN); - page[MAXPATHLEN] = '\0'; - shm_unlink_should_fail(page, ENAMETOOLONG); - free(page); -} -TEST(unlink_path_too_long, "unlink pathname too long"); - -static void -test_object_resize(void) -{ - pid_t pid; - struct stat sb; - char *page; - int fd, status; - - /* Start off with a size of a single page. */ - fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0777); - if (fd < 0) { - fail_errno("shm_open"); - return; - } - if (ftruncate(fd, getpagesize()) < 0) { - fail_errno("ftruncate(1)"); - close(fd); - return; - } - if (fstat(fd, &sb) < 0) { - fail_errno("fstat(1)"); - close(fd); - return; - } - if (sb.st_size != getpagesize()) { - fail_err("first resize failed"); - close(fd); - return; - } - - /* Write a '1' to the first byte. */ - page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, - 0); - if (page == MAP_FAILED) { - fail_errno("mmap(1)"); - close(fd); - return; - } - - page[0] = '1'; - - if (munmap(page, getpagesize()) < 0) { - fail_errno("munmap(1)"); - close(fd); - return; - } - - /* Grow the object to 2 pages. */ - if (ftruncate(fd, getpagesize() * 2) < 0) { - fail_errno("ftruncate(2)"); - close(fd); - return; - } - if (fstat(fd, &sb) < 0) { - fail_errno("fstat(2)"); - close(fd); - return; - } - if (sb.st_size != getpagesize() * 2) { - fail_err("second resize failed"); - close(fd); - return; - } - - /* Check for '1' at the first byte. */ - page = mmap(0, getpagesize() * 2, PROT_READ | PROT_WRITE, MAP_SHARED, - fd, 0); - if (page == MAP_FAILED) { - fail_errno("mmap(2)"); - close(fd); - return; - } - - if (page[0] != '1') { - fail_err("missing data at 0"); - close(fd); - return; - } - - /* Write a '2' at the start of the second page. */ - page[getpagesize()] = '2'; - - /* Shrink the object back to 1 page. */ - if (ftruncate(fd, getpagesize()) < 0) { - fail_errno("ftruncate(3)"); - close(fd); - return; - } - if (fstat(fd, &sb) < 0) { - fail_errno("fstat(3)"); - close(fd); - return; - } - if (sb.st_size != getpagesize()) { - fail_err("third resize failed"); - close(fd); - return; - } - - /* - * Fork a child process to make sure the second page is no - * longer valid. - */ - pid = fork(); - if (pid < 0) { - fail_errno("fork"); - close(fd); - return; - } - - if (pid == 0) { - struct rlimit lim; - char c; - - /* Don't generate a core dump. */ - getrlimit(RLIMIT_CORE, &lim); - lim.rlim_cur = 0; - setrlimit(RLIMIT_CORE, &lim); - - /* - * The previous ftruncate(2) shrunk the backing object - * so that this address is no longer valid, so reading - * from it should trigger a SIGSEGV. - */ - c = page[getpagesize()]; - fprintf(stderr, "child: page 1: '%c'\n", c); - exit(0); - } - if (wait(&status) < 0) { - fail_errno("wait"); - close(fd); - return; - } - if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGSEGV) { - fail_err("child terminated with status %x", status); - close(fd); - return; - } - - /* Grow the object back to 2 pages. */ - if (ftruncate(fd, getpagesize() * 2) < 0) { - fail_errno("ftruncate(4)"); - close(fd); - return; - } - if (fstat(fd, &sb) < 0) { - fail_errno("fstat(4)"); - close(fd); - return; - } - if (sb.st_size != getpagesize() * 2) { - fail_err("second resize failed"); - close(fd); - return; - } - - /* - * Note that the mapping at 'page' for the second page is - * still valid, and now that the shm object has been grown - * back up to 2 pages, there is now memory backing this page - * so the read will work. However, the data should be zero - * rather than '2' as the old data was thrown away when the - * object was shrunk and the new pages when an object are - * grown are zero-filled. - */ - if (page[getpagesize()] != 0) { - fail_err("invalid data at %d", getpagesize()); - close(fd); - return; - } - - close(fd); - pass(); -} -TEST(test_object_resize, "object resize"); - -int -main(int argc, char *argv[]) -{ - - run_tests(); - return (0); -} diff --git a/tools/regression/posixshm/posixshm.t b/tools/regression/posixshm/posixshm.t deleted file mode 100644 index a2159aca05a..00000000000 --- a/tools/regression/posixshm/posixshm.t +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -# -# $FreeBSD$ - -./posixshm diff --git a/tools/regression/posixshm/test.c b/tools/regression/posixshm/test.c deleted file mode 100644 index 8583a0dbbac..00000000000 --- a/tools/regression/posixshm/test.c +++ /dev/null @@ -1,128 +0,0 @@ -/*- - * Copyright (c) 2008 Yahoo!, Inc. - * All rights reserved. - * Written by: John Baldwin - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include - -#include "test.h" - -static int test_index; -static struct regression_test *test; -static int test_acknowleged; - -SET_DECLARE(regression_tests_set, struct regression_test); - -/* - * Outputs a test summary of the following: - * - * [name] [# [fmt args]] - */ -static void -vprint_status(const char *status, const char *fmt, va_list ap) -{ - - printf("%s %d", status, test_index); - if (test->rt_name) - printf(" - %s", test->rt_name); - if (fmt) { - printf(" # "); - vprintf(fmt, ap); - } - printf("\n"); - test_acknowleged = 1; -} - -static void -print_status(const char *status, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vprint_status(status, fmt, ap); - va_end(ap); -} - -void -pass(void) -{ - - print_status("ok", NULL); -} - -void -fail(void) -{ - - print_status("not ok", NULL); -} - -void -fail_err(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vprint_status("not ok", fmt, ap); - va_end(ap); -} - -void -skip(const char *reason) -{ - - print_status("ok", "skip %s", reason); -} - -void -todo(const char *reason) -{ - - print_status("not ok", "TODO %s", reason); -} - -void -run_tests(void) -{ - struct regression_test **testp; - - printf("1..%td\n", SET_COUNT(regression_tests_set)); - test_index = 1; - SET_FOREACH(testp, regression_tests_set) { - test_acknowleged = 0; - test = *testp; - test->rt_function(); - if (!test_acknowleged) - print_status("not ok", "unknown status"); - test_index++; - } -} diff --git a/tools/test/posixshm/README b/tools/test/posixshm/README deleted file mode 100644 index 514e18b893a..00000000000 --- a/tools/test/posixshm/README +++ /dev/null @@ -1,4 +0,0 @@ -$FreeBSD$ - -This is a simple program to test/demonstrate the POSIX Shared Memory -Objects feature set. `make shm_test' to build. diff --git a/tools/test/posixshm/shm_test.c b/tools/test/posixshm/shm_test.c deleted file mode 100644 index 0ec890a8578..00000000000 --- a/tools/test/posixshm/shm_test.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Test the POSIX shared-memory API. - * Dedicated to the public domain by Garrett A. Wollman, 2000. - * $FreeBSD$ - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Signal handler which does nothing. - */ -static void -ignoreit(int sig __unused) -{ - ; -} - -int -main(int argc, char **argv) -{ - char buf[1024], *cp, c; - int error, desc, rv; - long scval; - sigset_t ss; - struct sigaction sa; - void *region; - size_t i, psize; - -#ifndef _POSIX_SHARED_MEMORY_OBJECTS - printf("_POSIX_SHARED_MEMORY_OBJECTS is undefined\n"); -#else - printf("_POSIX_SHARED_MEMORY_OBJECTS is defined as %ld\n", - (long)_POSIX_SHARED_MEMORY_OBJECTS - 0); - if (_POSIX_SHARED_MEMORY_OBJECTS - 0 == -1) - printf("***Indicates this feature may be unsupported!\n"); -#endif - errno = 0; - scval = sysconf(_SC_SHARED_MEMORY_OBJECTS); - if (scval == -1 && errno != 0) { - err(1, "sysconf(_SC_SHARED_MEMORY_OBJECTS)"); - } else { - printf("sysconf(_SC_SHARED_MEMORY_OBJECTS) returns %ld\n", - scval); - if (scval == -1) - printf("***Indicates this feature is unsupported!\n"); - } - - errno = 0; - scval = sysconf(_SC_PAGESIZE); - if (scval == -1 && errno != 0) { - err(1, "sysconf(_SC_PAGESIZE)"); - } else if (scval <= 0 || (size_t)psize != psize) { - warnx("bogus return from sysconf(_SC_PAGESIZE): %ld", - scval); - psize = 4096; - } else { - printf("sysconf(_SC_PAGESIZE) returns %ld\n", scval); - psize = scval; - } - - argc--, argv++; - - if (*argv) { - strncat(buf, *argv, (sizeof buf) - 1); - desc = shm_open(buf, O_EXCL | O_CREAT | O_RDWR, 0600); - } else { - do { - /* - * Can't use mkstemp for obvious reasons... - */ - strcpy(buf, "/tmp/shmtest.XXXXXXXXXXXX"); - mktemp(buf); - desc = shm_open(buf, O_EXCL | O_CREAT | O_RDWR, 0600); - } while (desc < 0 && errno == EEXIST); - } - - if (desc < 0) - err(1, "shm_open"); - - if (shm_unlink(buf) < 0) - err(1, "shm_unlink"); - - if (ftruncate(desc, (off_t)psize) < 0) - err(1, "ftruncate"); - - region = mmap((void *)0, psize, PROT_READ | PROT_WRITE, MAP_SHARED, - desc, (off_t)0); - if (region == MAP_FAILED) - err(1, "mmap"); - memset(region, '\377', psize); - - sa.sa_flags = 0; - sa.sa_handler = ignoreit; - sigemptyset(&sa.sa_mask); - if (sigaction(SIGUSR1, &sa, (struct sigaction *)0) < 0) - err(1, "sigaction"); - - sigemptyset(&ss); - sigaddset(&ss, SIGUSR1); - if (sigprocmask(SIG_BLOCK, &ss, (sigset_t *)0) < 0) - err(1, "sigprocmask"); - - rv = fork(); - if (rv < 0) { - err(1, "fork"); - } else if (rv == 0) { - sigemptyset(&ss); - sigsuspend(&ss); - - for (cp = region; cp < (char *)region + psize; cp++) { - if (*cp != '\151') - _exit(1); - } - if (lseek(desc, 0, SEEK_SET) == -1) - _exit(1); - for (i = 0; i < psize; i++) { - error = read(desc, &c, 1); - if (c != '\151') - _exit(1); - } - _exit(0); - } else { - int status; - - memset(region, '\151', psize - 2); - error = pwrite(desc, region, 2, psize - 2); - if (error != 2) { - if (error >= 0) - errx(1, "short write %d", error); - else - err(1, "shmfd write"); - } - kill(rv, SIGUSR1); - waitpid(rv, &status, 0); - - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { - printf("Functionality test successful\n"); - exit(0); - } else if (WIFEXITED(status)) { - printf("Child process exited with status %d\n", - WEXITSTATUS(status)); - } else { - printf("Child process terminated with %s\n", - strsignal(WTERMSIG(status))); - } - } - exit(1); -} diff --git a/usr.bin/iscsictl/Makefile b/usr.bin/iscsictl/Makefile index 9b2e1f17587..4437e2d1e3c 100644 --- a/usr.bin/iscsictl/Makefile +++ b/usr.bin/iscsictl/Makefile @@ -6,7 +6,7 @@ CFLAGS+= -I${.CURDIR} CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi MAN= iscsi.conf.5 iscsictl.8 -LIBADD= xo util +LIBADD= xo YFLAGS+= -v LFLAGS+= -i diff --git a/usr.bin/iscsictl/iscsictl.8 b/usr.bin/iscsictl/iscsictl.8 index 93bf7ed18f8..ffa2e15022e 100644 --- a/usr.bin/iscsictl/iscsictl.8 +++ b/usr.bin/iscsictl/iscsictl.8 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 12, 2014 +.Dd October 17, 2015 .Dt ISCSICTL 8 .Os .Sh NAME @@ -36,7 +36,9 @@ .Sh SYNOPSIS .Nm .Fl A -.Fl p Ar portal Fl t Ar target Op Fl u Ar user Fl s Ar secret +.Fl p Ar portal Fl t Ar target +.Op Fl u Ar user Fl s Ar secret +.Op Fl w Ar timeout .Nm .Fl A .Fl d Ar discovery-host Op Fl u Ar user Fl s Ar secret @@ -70,6 +72,7 @@ .Nm .Fl L .Op Fl v +.Op Fl w Ar timeout .Sh DESCRIPTION The .Nm @@ -113,6 +116,10 @@ Target name. CHAP login. .It Fl v Verbose mode. +.It Fl w +Instead of returning immediately, wait up to +.Ar timeout +seconds until all configured sessions are successfully established. .El .Pp Certain parameters are necessary when adding a session. @@ -132,9 +139,11 @@ via configuration file. .Pp Since connecting to the target is performed in background, non-zero exit status does not mean that the session was successfully established. -Use +Use either .Nm Fl L -to check the connection status. +to check the connection status, or the +.Fl w +flag to wait for session establishment. .Pp Note that in order for the iSCSI initiator to be able to connect to a target, the diff --git a/usr.bin/iscsictl/iscsictl.c b/usr.bin/iscsictl/iscsictl.c index ac83b490eca..12ede3c7948 100644 --- a/usr.bin/iscsictl/iscsictl.c +++ b/usr.bin/iscsictl/iscsictl.c @@ -592,12 +592,68 @@ kernel_list(int iscsi_fd, const struct target *targ __unused, return (0); } +static int +kernel_wait(int iscsi_fd, int timeout) +{ + struct iscsi_session_state *states = NULL; + const struct iscsi_session_state *state; + struct iscsi_session_list isl; + unsigned int i, nentries = 1; + bool all_connected; + int error; + + for (;;) { + for (;;) { + states = realloc(states, + nentries * sizeof(struct iscsi_session_state)); + if (states == NULL) + xo_err(1, "realloc"); + + memset(&isl, 0, sizeof(isl)); + isl.isl_nentries = nentries; + isl.isl_pstates = states; + + error = ioctl(iscsi_fd, ISCSISLIST, &isl); + if (error != 0 && errno == EMSGSIZE) { + nentries *= 4; + continue; + } + break; + } + if (error != 0) { + xo_warn("ISCSISLIST"); + return (error); + } + + all_connected = true; + for (i = 0; i < isl.isl_nentries; i++) { + state = &states[i]; + + if (!state->iss_connected) { + all_connected = false; + break; + } + } + + if (all_connected) + return (0); + + sleep(1); + + if (timeout > 0) { + timeout--; + if (timeout == 0) + return (1); + } + } +} + static void usage(void) { fprintf(stderr, "usage: iscsictl -A -p portal -t target " - "[-u user -s secret]\n"); + "[-u user -s secret] [-w timeout]\n"); fprintf(stderr, " iscsictl -A -d discovery-host " "[-u user -s secret]\n"); fprintf(stderr, " iscsictl -A -a [-c path]\n"); @@ -609,7 +665,7 @@ usage(void) fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n"); fprintf(stderr, " iscsictl -R -a\n"); fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); - fprintf(stderr, " iscsictl -L [-v]\n"); + fprintf(stderr, " iscsictl -L [-v] [-w timeout]\n"); exit(1); } @@ -631,6 +687,7 @@ main(int argc, char **argv) const char *conf_path = DEFAULT_CONFIG_PATH; char *nickname = NULL, *discovery_host = NULL, *portal = NULL, *target = NULL, *user = NULL, *secret = NULL; + int timeout = -1; long long session_id = -1; char *end; int ch, error, iscsi_fd, retval, saved_errno; @@ -641,7 +698,7 @@ main(int argc, char **argv) argc = xo_parse_args(argc, argv); xo_open_container("iscsictl"); - while ((ch = getopt(argc, argv, "AMRLac:d:i:n:p:t:u:s:v")) != -1) { + while ((ch = getopt(argc, argv, "AMRLac:d:i:n:p:t:u:s:vw:")) != -1) { switch (ch) { case 'A': Aflag = 1; @@ -692,6 +749,13 @@ main(int argc, char **argv) case 'v': vflag = 1; break; + case 'w': + timeout = strtol(optarg, &end, 10); + if ((size_t)(end - optarg) != strlen(optarg)) + xo_errx(1, "trailing characters after timeout"); + if (timeout < 0) + xo_errx(1, "timeout cannot be negative"); + break; case '?': default: usage(); @@ -782,6 +846,8 @@ main(int argc, char **argv) if (vflag != 0) xo_errx(1, "-v cannot be used with -M"); + if (timeout != -1) + xo_errx(1, "-w cannot be used with -M"); } else if (Rflag != 0) { if (user != NULL) @@ -811,6 +877,8 @@ main(int argc, char **argv) xo_errx(1, "-i cannot be used with -R"); if (vflag != 0) xo_errx(1, "-v cannot be used with -R"); + if (timeout != -1) + xo_errx(1, "-w cannot be used with -R"); } else { assert(Lflag != 0); @@ -896,6 +964,9 @@ main(int argc, char **argv) failed += kernel_list(iscsi_fd, targ, vflag); } + if (timeout != -1) + failed += kernel_wait(iscsi_fd, timeout); + error = close(iscsi_fd); if (error != 0) xo_err(1, "close"); diff --git a/usr.bin/ldd/ldd.c b/usr.bin/ldd/ldd.c index 2f48e04a911..c8e0be3d455 100644 --- a/usr.bin/ldd/ldd.c +++ b/usr.bin/ldd/ldd.c @@ -386,9 +386,20 @@ is_executable(const char *fname, int fd, int *is_shlib, int *type) return (0); } if (hdr.elf.e_type == ET_DYN) { - if (hdr.elf.e_ident[EI_OSABI] == ELFOSABI_FREEBSD) { + switch (hdr.elf.e_ident[EI_OSABI]) { + case ELFOSABI_FREEBSD: *is_shlib = 1; return (1); +#ifdef __ARM_EABI__ + case ELFOSABI_NONE: + if (hdr.elf.e_machine != EM_ARM) + break; + if (EF_ARM_EABI_VERSION(hdr.elf.e_flags) < + EF_ARM_EABI_FREEBSD_MIN) + break; + *is_shlib = 1; + return (1); +#endif } warnx("%s: not a FreeBSD ELF shared object", fname); return (0); diff --git a/usr.bin/mkimg/ebr.c b/usr.bin/mkimg/ebr.c index 28931ea4de8..526c494084c 100644 --- a/usr.bin/mkimg/ebr.c +++ b/usr.bin/mkimg/ebr.c @@ -39,11 +39,15 @@ __FBSDID("$FreeBSD$"); #include "mkimg.h" #include "scheme.h" +#ifndef DOSPTYP_FAT16B +#define DOSPTYP_FAT16B 0x06 +#endif #ifndef DOSPTYP_FAT32 #define DOSPTYP_FAT32 0x0b #endif static struct mkimg_alias ebr_aliases[] = { + { ALIAS_FAT16B, ALIAS_INT2TYPE(DOSPTYP_FAT16B) }, { ALIAS_FAT32, ALIAS_INT2TYPE(DOSPTYP_FAT32) }, { ALIAS_FREEBSD, ALIAS_INT2TYPE(DOSPTYP_386BSD) }, { ALIAS_NONE, 0 } diff --git a/usr.bin/mkimg/mbr.c b/usr.bin/mkimg/mbr.c index 961ca45b45e..20d4d91e223 100644 --- a/usr.bin/mkimg/mbr.c +++ b/usr.bin/mkimg/mbr.c @@ -39,6 +39,9 @@ __FBSDID("$FreeBSD$"); #include "mkimg.h" #include "scheme.h" +#ifndef DOSPTYP_FAT16B +#define DOSPTYP_FAT16B 0x06 +#endif #ifndef DOSPTYP_FAT32 #define DOSPTYP_FAT32 0x0b #endif @@ -49,6 +52,7 @@ __FBSDID("$FreeBSD$"); static struct mkimg_alias mbr_aliases[] = { { ALIAS_EBR, ALIAS_INT2TYPE(DOSPTYP_EXT) }, { ALIAS_EFI, ALIAS_INT2TYPE(DOSPTYP_EFI) }, + { ALIAS_FAT16B, ALIAS_INT2TYPE(DOSPTYP_FAT16B) }, { ALIAS_FAT32, ALIAS_INT2TYPE(DOSPTYP_FAT32) }, { ALIAS_FREEBSD, ALIAS_INT2TYPE(DOSPTYP_386BSD) }, { ALIAS_NTFS, ALIAS_INT2TYPE(DOSPTYP_NTFS) }, diff --git a/usr.bin/mkimg/scheme.c b/usr.bin/mkimg/scheme.c index 9bdf8a5a546..1e648557e75 100644 --- a/usr.bin/mkimg/scheme.c +++ b/usr.bin/mkimg/scheme.c @@ -50,6 +50,7 @@ static struct { } scheme_alias[] = { { "ebr", ALIAS_EBR }, { "efi", ALIAS_EFI }, + { "fat16b", ALIAS_FAT16B }, { "fat32", ALIAS_FAT32 }, { "freebsd", ALIAS_FREEBSD }, { "freebsd-boot", ALIAS_FREEBSD_BOOT }, diff --git a/usr.bin/mkimg/scheme.h b/usr.bin/mkimg/scheme.h index 73b06eb7a4f..3ba4243bba9 100644 --- a/usr.bin/mkimg/scheme.h +++ b/usr.bin/mkimg/scheme.h @@ -36,6 +36,7 @@ enum alias { /* start */ ALIAS_EBR, ALIAS_EFI, + ALIAS_FAT16B, ALIAS_FAT32, ALIAS_FREEBSD, ALIAS_FREEBSD_BOOT, diff --git a/usr.bin/wc/Makefile b/usr.bin/wc/Makefile index 6fa21fa6975..540e33d15c9 100644 --- a/usr.bin/wc/Makefile +++ b/usr.bin/wc/Makefile @@ -2,6 +2,6 @@ # $FreeBSD$ PROG= wc -LIBADD= xo util +LIBADD= xo .include diff --git a/usr.bin/xo/Makefile b/usr.bin/xo/Makefile index 92692e550cf..0773987d6ab 100644 --- a/usr.bin/xo/Makefile +++ b/usr.bin/xo/Makefile @@ -12,7 +12,7 @@ MAN= xo.1 # XXX For xoversion.h CFLAGS+=-I${LIBXOSRC}/libxo -LIBADD= xo util +LIBADD= xo .if ${MK_TESTS} != "no" SUBDIR+= tests diff --git a/usr.sbin/freebsd-update/freebsd-update.sh b/usr.sbin/freebsd-update/freebsd-update.sh index 9fcc0121475..cce2af574f6 100644 --- a/usr.sbin/freebsd-update/freebsd-update.sh +++ b/usr.sbin/freebsd-update/freebsd-update.sh @@ -2736,7 +2736,7 @@ backup_kernel () { if [ $BACKUPKERNELSYMBOLFILES = yes ]; then FINDFILTER="" else - FINDFILTER=-"a ! -name *.symbols" + FINDFILTER="-a ! -name *.debug -a ! -name *.symbols" fi # Backup all the kernel files using hardlinks. diff --git a/usr.sbin/tzsetup/tzsetup.c b/usr.sbin/tzsetup/tzsetup.c index fc803646c23..7d2a9360ebb 100644 --- a/usr.sbin/tzsetup/tzsetup.c +++ b/usr.sbin/tzsetup/tzsetup.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -944,23 +945,18 @@ main(int argc, char **argv) if (argc - optind > 1) usage(); - if (chrootenv == NULL) { - strcpy(path_zonetab, _PATH_ZONETAB); - strcpy(path_iso3166, _PATH_ISO3166); - strcpy(path_zoneinfo, _PATH_ZONEINFO); - strcpy(path_localtime, _PATH_LOCALTIME); - strcpy(path_db, _PATH_DB); - strcpy(path_wall_cmos_clock, _PATH_WALL_CMOS_CLOCK); - } else { - sprintf(path_zonetab, "%s/%s", chrootenv, _PATH_ZONETAB); - sprintf(path_iso3166, "%s/%s", chrootenv, _PATH_ISO3166); - sprintf(path_zoneinfo, "%s/%s", chrootenv, _PATH_ZONEINFO); - sprintf(path_localtime, "%s/%s", chrootenv, _PATH_LOCALTIME); - sprintf(path_db, "%s/%s", chrootenv, _PATH_DB); - sprintf(path_wall_cmos_clock, "%s/%s", chrootenv, - _PATH_WALL_CMOS_CLOCK); + if (chrootenv != NULL) { + rv = chroot(chrootenv); + if (rv != 0) + err(EX_OSERR, "chroot to %s", chrootenv); } + strcpy(path_zonetab, _PATH_ZONETAB); + strcpy(path_iso3166, _PATH_ISO3166); + strcpy(path_zoneinfo, _PATH_ZONEINFO); + strcpy(path_localtime, _PATH_LOCALTIME); + strcpy(path_db, _PATH_DB); + strcpy(path_wall_cmos_clock, _PATH_WALL_CMOS_CLOCK); /* Override the user-supplied umask. */ (void)umask(S_IWGRP | S_IWOTH); diff --git a/usr.sbin/wpa/Makefile.crypto b/usr.sbin/wpa/Makefile.crypto index 46cd9ef9a03..5c03f7d21d0 100644 --- a/usr.sbin/wpa/Makefile.crypto +++ b/usr.sbin/wpa/Makefile.crypto @@ -1,7 +1,7 @@ # $FreeBSD$ .if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH) -SRCS+= crypto_openssl.c random.c sha1-prf.c sha256-prf.c +SRCS+= crypto_openssl.c random.c sha1-prf.c sha256-prf.c sha256-tlsprf.c LIBADD+= ssl crypto CFLAGS+= -DCONFIG_SHA256 .else @@ -19,6 +19,7 @@ CONFIG_INTERNAL_TLS=y CONFIG_INTERNAL_DH5=y CONFIG_INTERNAL_DH=y NEED_AES_ENC=true +NEED_AES_CBC=true .endif .if defined(TLS_FUNCS) diff --git a/usr.sbin/wpa/hostapd/Makefile b/usr.sbin/wpa/hostapd/Makefile index 743f91771b5..4437839b7ad 100644 --- a/usr.sbin/wpa/hostapd/Makefile +++ b/usr.sbin/wpa/hostapd/Makefile @@ -97,7 +97,6 @@ NEED_SIM_COMMON=y .if defined(NEED_SIM_COMMON) SRCS+= eap_sim_common.c \ eap_sim_db.c -NEED_AES_CBC=y NEED_FIPS186_2_PRF=y .endif diff --git a/usr.sbin/wpa/wpa_supplicant/Makefile b/usr.sbin/wpa/wpa_supplicant/Makefile index badae0dddd5..65b0c77b45f 100644 --- a/usr.sbin/wpa/wpa_supplicant/Makefile +++ b/usr.sbin/wpa/wpa_supplicant/Makefile @@ -96,10 +96,6 @@ NEED_AES_ENCBLOCK=y NEED_AES_OMAC1=y .endif -.if !empty(CFLAGS:M-DCONFIG_WPS) -NEED_AES_CBC=y -.endif - .if !empty(CFLAGS:M*-DEAP_AKA) SRCS+= eap_aka.c NEED_SIM_COMMON=y