diff options
Diffstat (limited to 'src/wifi')
| -rw-r--r-- | src/wifi/Makefile.am | 31 | ||||
| -rw-r--r-- | src/wifi/Makefile.in | 665 | ||||
| -rw-r--r-- | src/wifi/wifi-utils-nl80211.c | 819 | ||||
| -rw-r--r-- | src/wifi/wifi-utils-nl80211.h | 30 | ||||
| -rw-r--r-- | src/wifi/wifi-utils-private.h | 74 | ||||
| -rw-r--r-- | src/wifi/wifi-utils-wext.c | 663 | ||||
| -rw-r--r-- | src/wifi/wifi-utils-wext.h | 30 | ||||
| -rw-r--r-- | src/wifi/wifi-utils.c | 213 | ||||
| -rw-r--r-- | src/wifi/wifi-utils.h | 72 |
9 files changed, 2597 insertions, 0 deletions
diff --git a/src/wifi/Makefile.am b/src/wifi/Makefile.am new file mode 100644 index 00000000..c9a1b474 --- /dev/null +++ b/src/wifi/Makefile.am @@ -0,0 +1,31 @@ +INCLUDES = \ + -I${top_srcdir}/include \ + -I${top_builddir}/include \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/libnm-util \ + -I${top_builddir}/libnm-util \ + -I${top_srcdir}/src + +noinst_LTLIBRARIES = libwifi-utils.la + +libwifi_utils_la_SOURCES = \ + wifi-utils.c \ + wifi-utils.h \ + wifi-utils-private.h \ + wifi-utils-nl80211.c \ + wifi-utils-nl80211.h + +if WITH_WEXT +libwifi_utils_la_SOURCES += wifi-utils-wext.c wifi-utils-wext.h +endif + +libwifi_utils_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(LIBNL_CFLAGS) + +libwifi_utils_la_LIBADD = \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(GLIB_LIBS) \ + $(LIBNL_LIBS) + diff --git a/src/wifi/Makefile.in b/src/wifi/Makefile.in new file mode 100644 index 00000000..028469bf --- /dev/null +++ b/src/wifi/Makefile.in @@ -0,0 +1,665 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@WITH_WEXT_TRUE@am__append_1 = wifi-utils-wext.c wifi-utils-wext.h +subdir = src/wifi +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \ + $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/gtk-doc.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/intltool.m4 $(top_srcdir)/m4/introspection.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libnl-check.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libwifi_utils_la_DEPENDENCIES = \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am__libwifi_utils_la_SOURCES_DIST = wifi-utils.c wifi-utils.h \ + wifi-utils-private.h wifi-utils-nl80211.c wifi-utils-nl80211.h \ + wifi-utils-wext.c wifi-utils-wext.h +@WITH_WEXT_TRUE@am__objects_1 = libwifi_utils_la-wifi-utils-wext.lo +am_libwifi_utils_la_OBJECTS = libwifi_utils_la-wifi-utils.lo \ + libwifi_utils_la-wifi-utils-nl80211.lo $(am__objects_1) +libwifi_utils_la_OBJECTS = $(am_libwifi_utils_la_OBJECTS) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(libwifi_utils_la_SOURCES) +DIST_SOURCES = $(am__libwifi_utils_la_SOURCES_DIST) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALL_LINGUAS = @ALL_LINGUAS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DBUS_CFLAGS = @DBUS_CFLAGS@ +DBUS_LIBS = @DBUS_LIBS@ +DBUS_SYS_DIR = @DBUS_SYS_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DHCLIENT_PATH = @DHCLIENT_PATH@ +DHCLIENT_VERSION = @DHCLIENT_VERSION@ +DHCPCD_PATH = @DHCPCD_PATH@ +DISABLE_DEPRECATED = @DISABLE_DEPRECATED@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MAKEFILE = @GLIB_MAKEFILE@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@ +INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ +INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@ +INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@ +INTROSPECTION_LIBS = @INTROSPECTION_LIBS@ +INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ +INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ +INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ +IPTABLES_PATH = @IPTABLES_PATH@ +IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@ +IWMX_SDK_LIBS = @IWMX_SDK_LIBS@ +KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBDL = @LIBDL@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBNL1_CFLAGS = @LIBNL1_CFLAGS@ +LIBNL1_LIBS = @LIBNL1_LIBS@ +LIBNL2_CFLAGS = @LIBNL2_CFLAGS@ +LIBNL2_LIBS = @LIBNL2_LIBS@ +LIBNL3_CFLAGS = @LIBNL3_CFLAGS@ +LIBNL3_LIBS = @LIBNL3_LIBS@ +LIBNL_CFLAGS = @LIBNL_CFLAGS@ +LIBNL_GENL3_CFLAGS = @LIBNL_GENL3_CFLAGS@ +LIBNL_GENL3_LIBS = @LIBNL_GENL3_LIBS@ +LIBNL_LIBS = @LIBNL_LIBS@ +LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@ +LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ +LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MOC = @MOC@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ +NM_MICRO_VERSION = @NM_MICRO_VERSION@ +NM_MINOR_VERSION = @NM_MINOR_VERSION@ +NM_VERSION = @NM_VERSION@ +NSS_CFLAGS = @NSS_CFLAGS@ +NSS_LIBS = @NSS_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKGCONFIG_PATH = @PKGCONFIG_PATH@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POLKIT_CFLAGS = @POLKIT_CFLAGS@ +POLKIT_LIBS = @POLKIT_LIBS@ +POSUB = @POSUB@ +PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ +QT_CFLAGS = @QT_CFLAGS@ +QT_LIBS = @QT_LIBS@ +RANLIB = @RANLIB@ +RESOLVCONF_PATH = @RESOLVCONF_PATH@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ +SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ +UDEV_BASE_DIR = @UDEV_BASE_DIR@ +USE_NLS = @USE_NLS@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I${top_srcdir}/include \ + -I${top_builddir}/include \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/libnm-util \ + -I${top_builddir}/libnm-util \ + -I${top_srcdir}/src + +noinst_LTLIBRARIES = libwifi-utils.la +libwifi_utils_la_SOURCES = wifi-utils.c wifi-utils.h \ + wifi-utils-private.h wifi-utils-nl80211.c wifi-utils-nl80211.h \ + $(am__append_1) +libwifi_utils_la_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(LIBNL_CFLAGS) + +libwifi_utils_la_LIBADD = \ + $(top_builddir)/src/logging/libnm-logging.la \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(GLIB_LIBS) \ + $(LIBNL_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/wifi/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/wifi/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libwifi-utils.la: $(libwifi_utils_la_OBJECTS) $(libwifi_utils_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libwifi_utils_la_OBJECTS) $(libwifi_utils_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwifi_utils_la-wifi-utils-nl80211.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwifi_utils_la-wifi-utils-wext.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwifi_utils_la-wifi-utils.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libwifi_utils_la-wifi-utils.lo: wifi-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libwifi_utils_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libwifi_utils_la-wifi-utils.lo -MD -MP -MF $(DEPDIR)/libwifi_utils_la-wifi-utils.Tpo -c -o libwifi_utils_la-wifi-utils.lo `test -f 'wifi-utils.c' || echo '$(srcdir)/'`wifi-utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libwifi_utils_la-wifi-utils.Tpo $(DEPDIR)/libwifi_utils_la-wifi-utils.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='wifi-utils.c' object='libwifi_utils_la-wifi-utils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libwifi_utils_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libwifi_utils_la-wifi-utils.lo `test -f 'wifi-utils.c' || echo '$(srcdir)/'`wifi-utils.c + +libwifi_utils_la-wifi-utils-nl80211.lo: wifi-utils-nl80211.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libwifi_utils_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libwifi_utils_la-wifi-utils-nl80211.lo -MD -MP -MF $(DEPDIR)/libwifi_utils_la-wifi-utils-nl80211.Tpo -c -o libwifi_utils_la-wifi-utils-nl80211.lo `test -f 'wifi-utils-nl80211.c' || echo '$(srcdir)/'`wifi-utils-nl80211.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libwifi_utils_la-wifi-utils-nl80211.Tpo $(DEPDIR)/libwifi_utils_la-wifi-utils-nl80211.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='wifi-utils-nl80211.c' object='libwifi_utils_la-wifi-utils-nl80211.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libwifi_utils_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libwifi_utils_la-wifi-utils-nl80211.lo `test -f 'wifi-utils-nl80211.c' || echo '$(srcdir)/'`wifi-utils-nl80211.c + +libwifi_utils_la-wifi-utils-wext.lo: wifi-utils-wext.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libwifi_utils_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libwifi_utils_la-wifi-utils-wext.lo -MD -MP -MF $(DEPDIR)/libwifi_utils_la-wifi-utils-wext.Tpo -c -o libwifi_utils_la-wifi-utils-wext.lo `test -f 'wifi-utils-wext.c' || echo '$(srcdir)/'`wifi-utils-wext.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libwifi_utils_la-wifi-utils-wext.Tpo $(DEPDIR)/libwifi_utils_la-wifi-utils-wext.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='wifi-utils-wext.c' object='libwifi_utils_la-wifi-utils-wext.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libwifi_utils_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libwifi_utils_la-wifi-utils-wext.lo `test -f 'wifi-utils-wext.c' || echo '$(srcdir)/'`wifi-utils-wext.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/wifi/wifi-utils-nl80211.c b/src/wifi/wifi-utils-nl80211.c new file mode 100644 index 00000000..4a4e661e --- /dev/null +++ b/src/wifi/wifi-utils-nl80211.c @@ -0,0 +1,819 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2011 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + * Copyright (C) 2011 Intel Corporation. All rights reserved. + */ + +#include <config.h> +#include <errno.h> +#include <string.h> +#include <sys/ioctl.h> +#include <net/ethernet.h> +#include <unistd.h> +#include <math.h> + +#include <glib.h> + +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> + +#include <linux/nl80211.h> + +#include "wifi-utils-private.h" +#include "wifi-utils-nl80211.h" +#include "nm-logging.h" +#include "nm-utils.h" +#include "nm-netlink-compat.h" + +typedef struct { + WifiData parent; + struct nl_sock *nl_sock; + int id; + struct nl_cb *nl_cb; + guint32 *freqs; + int num_freqs; +} WifiDataNl80211; + +static int ack_handler (struct nl_msg *msg, void *arg) +{ + int *done = arg; + *done = 1; + return NL_STOP; +} + +static int finish_handler (struct nl_msg *msg, void *arg) +{ + int *done = arg; + *done = 1; + return NL_SKIP; +} + +static int error_handler (struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *done = arg; + *done = err->error; + return NL_SKIP; +} + +static struct nl_msg * +_nl80211_alloc_msg (int id, int ifindex, guint32 cmd, guint32 flags) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc (); + if (msg) { + genlmsg_put (msg, 0, 0, id, 0, flags, cmd, 0); + NLA_PUT_U32 (msg, NL80211_ATTR_IFINDEX, ifindex); + } + return msg; + + nla_put_failure: + nlmsg_free (msg); + return NULL; +} + +static struct nl_msg * +nl80211_alloc_msg (WifiDataNl80211 *nl80211, guint32 cmd, guint32 flags) +{ + return _nl80211_alloc_msg (nl80211->id, nl80211->parent.ifindex, cmd, flags); +} + +/* NOTE: this function consumes 'msg' */ +static int +_nl80211_send_and_recv (struct nl_sock *nl_sock, + struct nl_cb *nl_cb, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + struct nl_cb *cb; + int err, done; + + g_return_val_if_fail (msg != NULL, -ENOMEM); + g_return_val_if_fail (valid_handler != NULL, -EINVAL); + + cb = nl_cb_clone (nl_cb); + if (!cb) { + err = -ENOMEM; + goto out; + } + + err = nl_send_auto_complete (nl_sock, msg); + if (err < 0) + goto out; + + done = 0; + nl_cb_err (cb, NL_CB_CUSTOM, error_handler, &done); + nl_cb_set (cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &done); + nl_cb_set (cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &done); + nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data); + + /* Loop until one of our NL callbacks says we're done; on success + * done will be 1, on error it will be < 0. + */ + while (!done) { + err = nl_recvmsgs (nl_sock, cb); + if (err && err != -NLE_AGAIN) { + nm_log_warn (LOGD_WIFI, "nl_recvmsgs() error: (%d) %s", + err, nl_geterror (err)); + break; + } + } + if (err == 0 && done < 0) + err = done; + + out: + nl_cb_put (cb); + nlmsg_free (msg); + return err; +} + +static int +nl80211_send_and_recv (WifiDataNl80211 *nl80211, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + return _nl80211_send_and_recv (nl80211->nl_sock, nl80211->nl_cb, msg, valid_handler, valid_data); +} + +static void +wifi_nl80211_deinit (WifiData *parent) +{ + WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) parent; + + if (nl80211->nl_sock) + nl_socket_free (nl80211->nl_sock); + if (nl80211->nl_cb) + nl_cb_put (nl80211->nl_cb); +} + +struct nl80211_iface_info { + NM80211Mode mode; +}; + +static int nl80211_iface_info_handler (struct nl_msg *msg, void *arg) +{ + struct nl80211_iface_info *info = arg; + struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), + genlmsg_attrlen (gnlh, 0), NULL) < 0) + return NL_SKIP; + + if (!tb[NL80211_ATTR_IFTYPE]) + return NL_SKIP; + + switch (nla_get_u32 (tb[NL80211_ATTR_IFTYPE])) { + case NL80211_IFTYPE_ADHOC: + info->mode = NM_802_11_MODE_ADHOC; + break; + case NL80211_IFTYPE_STATION: + info->mode = NM_802_11_MODE_INFRA; + break; + } + + return NL_SKIP; +} + +static NM80211Mode +wifi_nl80211_get_mode (WifiData *data) +{ + WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + struct nl80211_iface_info iface_info = { + .mode = NM_802_11_MODE_UNKNOWN, + }; + struct nl_msg *msg; + + msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_INTERFACE, 0); + + if (nl80211_send_and_recv (nl80211, msg, nl80211_iface_info_handler, + &iface_info) < 0) + return NM_802_11_MODE_UNKNOWN; + + return iface_info.mode; +} + +static gboolean +wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode) +{ + /* + * Used only to set mode for scanning as some old cards + * don't properly scan in IBSS mode, nl80211 cards are + * expected to scan properly so ignore this. + */ + return TRUE; +} + +/* @divisor: pass what value @xbm should be divided by to get dBm */ +static guint32 nl80211_xbm_to_percent (gint32 xbm, guint32 divisor) +{ +#define NOISE_FLOOR_DBM -90 +#define SIGNAL_MAX_DBM -20 + + xbm /= divisor; + xbm = CLAMP(xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM); + + return 100 - 70 * (((float) SIGNAL_MAX_DBM - (float) xbm) / + ((float) SIGNAL_MAX_DBM - (float) NOISE_FLOOR_DBM)); +} + +struct nl80211_bss_info { + guint32 freq; + guint8 bssid[ETH_ALEN]; + guint8 ssid[32]; + guint32 ssid_len; + guint32 beacon_signal; + gboolean valid; +}; + +#define WLAN_EID_SSID 0 + +static void find_ssid (guint8 *ies, guint32 ies_len, + guint8 **ssid, guint32 *ssid_len) +{ + *ssid = NULL; + *ssid_len = 0; + + while (ies_len > 2 && ies[0] != WLAN_EID_SSID) { + ies_len -= ies[1] + 2; + ies += ies[1] + 2; + } + if (ies_len < 2) + return; + if (ies_len < 2 + ies[1]) + return; + + *ssid_len = ies[1]; + *ssid = ies + 2; +} + +static int nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) +{ + struct nl80211_bss_info *info = arg; + struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct nlattr *bss[NL80211_BSS_MAX + 1]; + static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { + [NL80211_BSS_TSF] = { .type = NLA_U64 }, + [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_BSS_BSSID] = { }, + [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, + [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, + [NL80211_BSS_INFORMATION_ELEMENTS] = { }, + [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, + [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, + [NL80211_BSS_STATUS] = { .type = NLA_U32 }, + }; + guint32 status; + + if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), + genlmsg_attrlen (gnlh, 0), NULL) < 0) + return NL_SKIP; + + if (tb[NL80211_ATTR_BSS] == NULL) + return NL_SKIP; + + if (nla_parse_nested (bss, NL80211_BSS_MAX, + tb[NL80211_ATTR_BSS], + bss_policy)) + return NL_SKIP; + + if (bss[NL80211_BSS_STATUS] == NULL) + return NL_SKIP; + + status = nla_get_u32 (bss[NL80211_BSS_STATUS]); + + if (status != NL80211_BSS_STATUS_ASSOCIATED && + status != NL80211_BSS_STATUS_IBSS_JOINED) + return NL_SKIP; + + if (bss[NL80211_BSS_BSSID] == NULL) + return NL_SKIP; + memcpy(info->bssid, nla_data (bss[NL80211_BSS_BSSID]), ETH_ALEN); + + if (bss[NL80211_BSS_FREQUENCY]) + info->freq = nla_get_u32 (bss[NL80211_BSS_FREQUENCY]); + + if (bss[NL80211_BSS_SIGNAL_UNSPEC]) + info->beacon_signal = + nla_get_u8 (bss[NL80211_BSS_SIGNAL_UNSPEC]); + + if (bss[NL80211_BSS_SIGNAL_MBM]) + info->beacon_signal = + nl80211_xbm_to_percent (nla_get_u32 (bss[NL80211_BSS_SIGNAL_MBM]), 100); + + if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { + guint8 *ssid; + guint32 ssid_len; + + find_ssid(nla_data (bss[NL80211_BSS_INFORMATION_ELEMENTS]), + nla_len (bss[NL80211_BSS_INFORMATION_ELEMENTS]), + &ssid, &ssid_len); + if (ssid && ssid_len && ssid_len <= sizeof(info->ssid)) { + memcpy (info->ssid, ssid, ssid_len); + info->ssid_len = ssid_len; + } + } + + info->valid = TRUE; + + return NL_SKIP; +} + +static void nl80211_get_bss_info (WifiDataNl80211 *nl80211, + struct nl80211_bss_info *bss_info) +{ + struct nl_msg *msg; + + memset(bss_info, 0, sizeof(*bss_info)); + + msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_SCAN, NLM_F_DUMP); + + nl80211_send_and_recv (nl80211, msg, nl80211_bss_dump_handler, bss_info); +} + +static guint32 +wifi_nl80211_get_freq (WifiData *data) +{ + WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + struct nl80211_bss_info bss_info; + + nl80211_get_bss_info (nl80211, &bss_info); + + return bss_info.freq; +} + +static guint32 +wifi_nl80211_find_freq (WifiData *data, const guint32 *freqs) +{ + WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + int i; + + for (i = 0; i < nl80211->num_freqs; i++) { + while (*freqs) { + if (nl80211->freqs[i] == *freqs) + return *freqs; + freqs++; + } + } + return 0; +} + +static GByteArray * +wifi_nl80211_get_ssid (WifiData *data) +{ + WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + GByteArray *array = NULL; + struct nl80211_bss_info bss_info; + + nl80211_get_bss_info (nl80211, &bss_info); + + if (bss_info.valid) { + array = g_byte_array_sized_new (bss_info.ssid_len); + g_byte_array_append (array, (const guint8 *) bss_info.ssid, + bss_info.ssid_len); + } + + return array; +} + +static gboolean +wifi_nl80211_get_bssid (WifiData *data, struct ether_addr *out_bssid) +{ + WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + struct nl80211_bss_info bss_info; + + nl80211_get_bss_info (nl80211, &bss_info); + + if (bss_info.valid) + memcpy(out_bssid, bss_info.bssid, ETH_ALEN); + + return bss_info.valid; +} + +struct nl80211_station_info { + guint32 txrate; + gboolean txrate_valid; + guint8 signal; + gboolean signal_valid; +}; + +static int nl80211_station_handler (struct nl_msg *msg, void *arg) +{ + struct nl80211_station_info *info = arg; + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); + struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; + struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; + static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED }, + [NL80211_STA_INFO_LLID] = { .type = NLA_U16 }, + [NL80211_STA_INFO_PLID] = { .type = NLA_U16 }, + [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 }, + }; + + static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, + [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, + [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, + }; + + if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), + genlmsg_attrlen (gnlh, 0), NULL) < 0) + return NL_SKIP; + + if (tb[NL80211_ATTR_STA_INFO] == NULL) + return NL_SKIP; + + if (nla_parse_nested (sinfo, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], + stats_policy)) + return NL_SKIP; + + if (sinfo[NL80211_STA_INFO_TX_BITRATE] == NULL) + return NL_SKIP; + + if (nla_parse_nested (rinfo, NL80211_RATE_INFO_MAX, + sinfo[NL80211_STA_INFO_TX_BITRATE], + rate_policy)) + return NL_SKIP; + + if (rinfo[NL80211_RATE_INFO_BITRATE] == NULL) + return NL_SKIP; + + /* convert from nl80211's units of 100kbps to NM's kbps */ + info->txrate = nla_get_u16 (rinfo[NL80211_RATE_INFO_BITRATE]) * 100; + info->txrate_valid = TRUE; + + if (sinfo[NL80211_STA_INFO_SIGNAL] != NULL) { + info->signal = nl80211_xbm_to_percent ((gint8) nla_get_u8 (sinfo[NL80211_STA_INFO_SIGNAL]), 1); + info->signal_valid = TRUE; + } + + return NL_SKIP; +} + +static void nl80211_get_ap_info (WifiDataNl80211 *nl80211, + struct nl80211_station_info *sta_info) +{ + struct nl_msg *msg; + struct nl80211_bss_info bss_info; + + memset(sta_info, 0, sizeof(*sta_info)); + + nl80211_get_bss_info (nl80211, &bss_info); + if (!bss_info.valid) + return; + + msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_STATION, 0); + if (msg) { + NLA_PUT (msg, NL80211_ATTR_MAC, ETH_ALEN, bss_info.bssid); + + nl80211_send_and_recv (nl80211, msg, nl80211_station_handler, sta_info); + if (!sta_info->signal_valid) { + /* Fall back to bss_info signal quality (both are in percent) */ + sta_info->signal = bss_info.beacon_signal; + } + } + + return; + + nla_put_failure: + nlmsg_free (msg); + return; +} + +static guint32 +wifi_nl80211_get_rate (WifiData *data) +{ + WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + struct nl80211_station_info sta_info; + + nl80211_get_ap_info (nl80211, &sta_info); + + return sta_info.txrate; +} + +static int +wifi_nl80211_get_qual (WifiData *data) +{ + WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + struct nl80211_station_info sta_info; + + nl80211_get_ap_info (nl80211, &sta_info); + return sta_info.signal; +} + +struct nl80211_device_info { + guint32 *freqs; + int num_freqs; + guint32 caps; + gboolean can_scan, can_scan_ssid; + gboolean success; +}; + +static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); + struct nl80211_device_info *info = arg; + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + struct nlattr *nl_band; + struct nlattr *nl_freq; + int rem_freq; + int rem_band; + int freq_idx; + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, + }; + + if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen (gnlh, 0), NULL) < 0) + return NL_SKIP; + + if (tb[NL80211_ATTR_WIPHY_BANDS] == NULL) + return NL_SKIP; + + if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) { + info->can_scan_ssid = + nla_get_u8 (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) > 0; + } else { + /* old kernel that only had mac80211, so assume it can */ + info->can_scan_ssid = TRUE; + } + + if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) { + struct nlattr *nl_cmd; + int i; + + nla_for_each_nested (nl_cmd, + tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) { + guint32 cmd = nla_get_u32 (nl_cmd); + if (cmd == NL80211_CMD_TRIGGER_SCAN) + info->can_scan = TRUE; + } + } + + info->num_freqs = 0; + + nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { + if (nla_parse_nested (tb_band, NL80211_BAND_ATTR_MAX, nl_band, + NULL) < 0) + return NL_SKIP; + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], + rem_freq) { + nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nl_freq, freq_policy); + + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + + info->num_freqs++; + } + } + + info->freqs = g_malloc0 (sizeof(guint32) * info->num_freqs); + + freq_idx = 0; + nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { + if (nla_parse_nested (tb_band, NL80211_BAND_ATTR_MAX, nl_band, + NULL) < 0) + return NL_SKIP; + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], + rem_freq) { + nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nl_freq, freq_policy); + + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + + info->freqs[freq_idx] = + nla_get_u32 (tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + freq_idx++; + } + } + + if (tb[NL80211_ATTR_CIPHER_SUITES]) { + int num; + int i; + __u32 *ciphers = nla_data (tb[NL80211_ATTR_CIPHER_SUITES]); + + num = nla_len (tb[NL80211_ATTR_CIPHER_SUITES]) / sizeof(__u32); + for (i = 0; i < num; i++) { + switch (ciphers[i]) { + case 0x000fac01: + info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40; + break; + case 0x000fac05: + info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP104; + break; + case 0x000fac02: + info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP | + NM_WIFI_DEVICE_CAP_WPA; + break; + case 0x000fac04: + info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP | + NM_WIFI_DEVICE_CAP_RSN; + break; + } + } + } + + if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) { + struct nlattr *nl_mode; + int i; + + nla_for_each_nested (nl_mode, tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) { + if (nla_type (nl_mode) == NL80211_IFTYPE_AP) { + info->caps |= NM_WIFI_DEVICE_CAP_AP; + break; + } + } + } + + info->success = TRUE; + + return NL_SKIP; +} + +WifiData * +wifi_nl80211_init (const char *iface, int ifindex) +{ + WifiDataNl80211 *nl80211; + struct nl_msg *msg; + struct nl80211_device_info device_info = {}; + + nl80211 = wifi_data_new (iface, ifindex, sizeof (*nl80211)); + nl80211->parent.get_mode = wifi_nl80211_get_mode; + nl80211->parent.set_mode = wifi_nl80211_set_mode; + nl80211->parent.get_freq = wifi_nl80211_get_freq; + nl80211->parent.find_freq = wifi_nl80211_find_freq; + nl80211->parent.get_ssid = wifi_nl80211_get_ssid; + nl80211->parent.get_bssid = wifi_nl80211_get_bssid; + nl80211->parent.get_rate = wifi_nl80211_get_rate; + nl80211->parent.get_qual = wifi_nl80211_get_qual; + nl80211->parent.deinit = wifi_nl80211_deinit; + + nl80211->nl_sock = nl_socket_alloc (); + if (nl80211->nl_sock == NULL) + goto error; + + if (genl_connect (nl80211->nl_sock)) + goto error; + + nl80211->id = genl_ctrl_resolve (nl80211->nl_sock, "nl80211"); + if (nl80211->id < 0) + goto error; + + nl80211->nl_cb = nl_cb_alloc (NL_CB_DEFAULT); + if (nl80211->nl_cb == NULL) + goto error; + + msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WIPHY, 0); + + if (nl80211_send_and_recv (nl80211, msg, nl80211_wiphy_info_handler, + &device_info) < 0) { + nm_log_dbg (LOGD_HW | LOGD_WIFI, + "(%s): NL80211_CMD_GET_WIPHY request failed", + nl80211->parent.iface); + goto error; + } + + if (!device_info.success) { + nm_log_dbg (LOGD_HW | LOGD_WIFI, + "(%s): NL80211_CMD_GET_WIPHY request indicated failure", + nl80211->parent.iface); + goto error; + } + + if (!device_info.can_scan_ssid) { + nm_log_err (LOGD_HW | LOGD_WIFI, + "(%s): driver does not support SSID scans", + nl80211->parent.iface); + goto error; + } + + if (device_info.num_freqs == 0 || device_info.freqs == NULL) { + nm_log_err (LOGD_HW | LOGD_WIFI, + "(%s): driver reports no supported frequencies", + nl80211->parent.iface); + goto error; + } + + nl80211->freqs = device_info.freqs; + nl80211->num_freqs = device_info.num_freqs; + nl80211->parent.can_scan_ssid = device_info.can_scan_ssid; + nl80211->parent.caps = device_info.caps; + + nm_log_info (LOGD_HW | LOGD_WIFI, + "(%s): using nl80211 for WiFi device control", + nl80211->parent.iface); + + return (WifiData *) nl80211; + +error: + wifi_utils_deinit ((WifiData *) nl80211); + return NULL; +} + +static int +iface_to_index (struct nl_sock *nl_sock, const char *iface) +{ + struct nl_cache *link_cache = NULL; + int err, ifindex; + + /* name to index */ + err = rtnl_link_alloc_cache (nl_sock, &link_cache); + if (err < 0) { + nm_log_warn (LOGD_HW, "failed to allocate link cache: (%d) %s", + err, nl_geterror (err)); + return -1; + } + nl_cache_mngt_provide (link_cache); + nl_cache_refill (nl_sock, link_cache); + ifindex = rtnl_link_name2i (link_cache, iface); + nl_cache_free (link_cache); + + return ifindex; +} + +gboolean +wifi_nl80211_is_wifi (const char *iface) +{ + struct nl_sock *nl_sock; + struct nl_cb *nl_cb = NULL; + struct nl_msg *msg = NULL; + int id, ifindex; + struct nl80211_iface_info iface_info = { + .mode = NM_802_11_MODE_UNKNOWN, + }; + gboolean is_wifi = FALSE; + + nl_sock = nl_socket_alloc (); + if (nl_sock == NULL) + return FALSE; + + if (genl_connect (nl_sock)) + goto error; + + ifindex = iface_to_index (nl_sock, iface); + if (ifindex < 0) + goto error; + + id = genl_ctrl_resolve (nl_sock, "nl80211"); + if (id < 0) + goto error; + + nl_cb = nl_cb_alloc (NL_CB_DEFAULT); + if (nl_cb) { + msg = _nl80211_alloc_msg (id, ifindex, NL80211_CMD_GET_INTERFACE, 0); + if (_nl80211_send_and_recv (nl_sock, + nl_cb, + msg, + nl80211_iface_info_handler, + &iface_info) >= 0) + is_wifi = (iface_info.mode != NM_802_11_MODE_UNKNOWN); + } + + error: + if (nl_cb) + nl_cb_put (nl_cb); + nl_socket_free (nl_sock); + return is_wifi; +} + diff --git a/src/wifi/wifi-utils-nl80211.h b/src/wifi/wifi-utils-nl80211.h new file mode 100644 index 00000000..2a7fe874 --- /dev/null +++ b/src/wifi/wifi-utils-nl80211.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + */ + +#ifndef WIFI_UTILS_NL80211_H +#define WIFI_UTILS_NL80211_H + +#include "wifi-utils.h" + +WifiData *wifi_nl80211_init (const char *iface, int ifindex); + +gboolean wifi_nl80211_is_wifi (const char *iface); + +#endif /* WIFI_UTILS_NL80211_H */ diff --git a/src/wifi/wifi-utils-private.h b/src/wifi/wifi-utils-private.h new file mode 100644 index 00000000..83075090 --- /dev/null +++ b/src/wifi/wifi-utils-private.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2011 Red Hat, Inc. + */ + +#ifndef WIFI_UTILS_PRIVATE_H +#define WIFI_UTILS_PRIVATE_H + +#include <glib.h> + +#include "NetworkManager.h" +#include "wifi-utils.h" + +struct WifiData { + char *iface; + int ifindex; + NMDeviceWifiCapabilities caps; + gboolean can_scan_ssid; + + NM80211Mode (*get_mode) (WifiData *data); + + gboolean (*set_mode) (WifiData *data, const NM80211Mode mode); + + /* Return current frequency in MHz (really associated BSS frequency) */ + guint32 (*get_freq) (WifiData *data); + + /* Return first supported frequency in the zero-terminated list */ + guint32 (*find_freq) (WifiData *data, const guint32 *freqs); + + /* If SSID is empty/blank (zero-length or all \0s) return NULL */ + GByteArray * (*get_ssid) (WifiData *data); + + /* Return current bitrate in Kbps */ + guint32 (*get_rate) (WifiData *data); + + gboolean (*get_bssid) (WifiData *data, struct ether_addr *out_bssid); + + /* Return a signal strength percentage 0 - 100% for the current BSSID; + * return -1 on errors or if not associated. + */ + int (*get_qual) (WifiData *data); + + void (*deinit) (WifiData *data); + + /* OLPC Mesh-only functions */ + + guint32 (*get_mesh_channel) (WifiData *data); + + /* channel == 0 means "auto channel" */ + gboolean (*set_mesh_channel) (WifiData *data, guint32 channel); + + /* ssid == NULL means "auto SSID" */ + gboolean (*set_mesh_ssid) (WifiData *data, const GByteArray *ssid); +}; + +gpointer wifi_data_new (const char *iface, int ifindex, gsize len); +void wifi_data_free (WifiData *data); + +#endif /* WIFI_UTILS_PRIVATE_H */ diff --git a/src/wifi/wifi-utils-wext.c b/src/wifi/wifi-utils-wext.c new file mode 100644 index 00000000..75d00f41 --- /dev/null +++ b/src/wifi/wifi-utils-wext.c @@ -0,0 +1,663 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2011 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <config.h> +#include <errno.h> +#include <string.h> +#include <sys/ioctl.h> +#include <net/ethernet.h> +#include <unistd.h> +#include <math.h> + +#include <glib.h> + +#include "wifi-utils-private.h" +#include "wifi-utils-wext.h" +#include "nm-logging.h" +#include "nm-utils.h" + +/* Hacks necessary to #include wireless.h; yay for WEXT */ +#ifndef __user +#define __user +#endif +#include <sys/types.h> +#include <linux/types.h> +#include <sys/socket.h> +#include <linux/if.h> +#include <linux/wireless.h> + + +typedef struct { + WifiData parent; + int fd; + struct iw_quality max_qual; + gint8 num_freqs; + guint32 freqs[IW_MAX_FREQUENCIES]; +} WifiDataWext; + +/* Until a new wireless-tools comes out that has the defs and the structure, + * need to copy them here. + */ +/* Scan capability flags - in (struct iw_range *)->scan_capa */ +#define NM_IW_SCAN_CAPA_NONE 0x00 +#define NM_IW_SCAN_CAPA_ESSID 0x01 + +struct iw_range_with_scan_capa +{ + guint32 throughput; + guint32 min_nwid; + guint32 max_nwid; + guint16 old_num_channels; + guint8 old_num_frequency; + + guint8 scan_capa; +/* don't need the rest... */ +}; + +static guint32 +iw_freq_to_uint32 (struct iw_freq *freq) +{ + if (freq->e == 0) { + /* Some drivers report channel not frequency. Convert to a + * frequency; but this assumes that the device is in b/g mode. + */ + if ((freq->m >= 1) && (freq->m <= 13)) + return 2407 + (5 * freq->m); + else if (freq->m == 14) + return 2484; + } + + return (guint32) (((double) freq->m) * pow (10, freq->e) / 1000000); +} + +static void +wifi_wext_deinit (WifiData *parent) +{ + WifiDataWext *wext = (WifiDataWext *) parent; + + if (wext->fd >= 0) + close (wext->fd); +} + +static NM80211Mode +wifi_wext_get_mode (WifiData *data) +{ + WifiDataWext *wext = (WifiDataWext *) data; + struct iwreq wrq; + + memset (&wrq, 0, sizeof (struct iwreq)); + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + + if (ioctl (wext->fd, SIOCGIWMODE, &wrq) < 0) { + if (errno != ENODEV) { + nm_log_warn (LOGD_HW | LOGD_WIFI, + "(%s): error %d getting card mode", + wext->parent.iface, errno); + } + return NM_802_11_MODE_UNKNOWN; + } + + switch (wrq.u.mode) { + case IW_MODE_ADHOC: + return NM_802_11_MODE_ADHOC; + case IW_MODE_INFRA: + return NM_802_11_MODE_INFRA; + default: + break; + } + return NM_802_11_MODE_UNKNOWN; +} + +static gboolean +wifi_wext_set_mode (WifiData *data, const NM80211Mode mode) +{ + WifiDataWext *wext = (WifiDataWext *) data; + struct iwreq wrq; + + if (wifi_wext_get_mode (data) == mode) + return TRUE; + + memset (&wrq, 0, sizeof (struct iwreq)); + switch (mode) { + case NM_802_11_MODE_ADHOC: + wrq.u.mode = IW_MODE_ADHOC; + break; + case NM_802_11_MODE_INFRA: + wrq.u.mode = IW_MODE_INFRA; + break; + default: + g_warn_if_reached (); + return FALSE; + } + + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + if (ioctl (wext->fd, SIOCSIWMODE, &wrq) < 0) { + if (errno != ENODEV) { + nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): error setting mode %d", + wext->parent.iface, mode); + } + return FALSE; + } + + return TRUE; +} + +static guint32 +wifi_wext_get_freq (WifiData *data) +{ + WifiDataWext *wext = (WifiDataWext *) data; + struct iwreq wrq; + + memset (&wrq, 0, sizeof (struct iwreq)); + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + if (ioctl (wext->fd, SIOCGIWFREQ, &wrq) < 0) { + nm_log_warn (LOGD_HW | LOGD_WIFI, + "(%s): error getting frequency: %s", + wext->parent.iface, strerror (errno)); + return 0; + } + + return iw_freq_to_uint32 (&wrq.u.freq); +} + +static guint32 +wifi_wext_find_freq (WifiData *data, const guint32 *freqs) +{ + WifiDataWext *wext = (WifiDataWext *) data; + int i; + + for (i = 0; i < wext->num_freqs; i++) { + while (*freqs) { + if (wext->freqs[i] == *freqs) + return *freqs; + freqs++; + } + } + return 0; +} + +static GByteArray * +wifi_wext_get_ssid (WifiData *data) +{ + WifiDataWext *wext = (WifiDataWext *) data; + struct iwreq wrq; + char ssid[IW_ESSID_MAX_SIZE + 2]; + guint32 len; + GByteArray *array = NULL; + + memset (ssid, 0, sizeof (ssid)); + wrq.u.essid.pointer = (caddr_t) &ssid; + wrq.u.essid.length = sizeof (ssid); + wrq.u.essid.flags = 0; + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + + if (ioctl (wext->fd, SIOCGIWESSID, &wrq) < 0) { + nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): couldn't get SSID: %d", + wext->parent.iface, errno); + return NULL; + } + + len = wrq.u.essid.length; + if (nm_utils_is_empty_ssid ((guint8 *) ssid, len) == FALSE) { + array = g_byte_array_sized_new (len); + g_byte_array_append (array, (const guint8 *) ssid, len); + } + + return array; +} + +static gboolean +wifi_wext_get_bssid (WifiData *data, struct ether_addr *out_bssid) +{ + WifiDataWext *wext = (WifiDataWext *) data; + struct iwreq wrq; + + memset (&wrq, 0, sizeof (wrq)); + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + if (ioctl (wext->fd, SIOCGIWAP, &wrq) < 0) { + nm_log_warn (LOGD_HW | LOGD_WIFI, + "(%s): error getting associated BSSID: %s", + wext->parent.iface, strerror (errno)); + return FALSE; + } + memcpy (out_bssid->ether_addr_octet, &(wrq.u.ap_addr.sa_data), ETH_ALEN); + return TRUE; +} + +static guint32 +wifi_wext_get_rate (WifiData *data) +{ + WifiDataWext *wext = (WifiDataWext *) data; + struct iwreq wrq; + int err; + + memset (&wrq, 0, sizeof (wrq)); + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + err = ioctl (wext->fd, SIOCGIWRATE, &wrq); + return ((err == 0) ? wrq.u.bitrate.value / 1000 : 0); +} + +static int +wext_qual_to_percent (const struct iw_quality *qual, + const struct iw_quality *max_qual) +{ + int percent = -1; + int level_percent = -1; + + g_return_val_if_fail (qual != NULL, -1); + g_return_val_if_fail (max_qual != NULL, -1); + + /* Magically convert the many different WEXT quality representations to a percentage */ + + nm_log_dbg (LOGD_WIFI, + "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X", + (__s8) qual->qual, qual->qual, qual->qual, + (__s8) qual->level, qual->level, qual->level, + (__s8) qual->noise, qual->noise, qual->noise, + qual->updated, + (__s8) max_qual->qual, max_qual->qual, max_qual->qual, + (__s8) max_qual->level, max_qual->level, max_qual->level, + (__s8) max_qual->noise, max_qual->noise, max_qual->noise, + max_qual->updated); + + /* Try using the card's idea of the signal quality first as long as it tells us what the max quality is. + * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be + * bounded by 0 and max_qual->qual, and MUST change in a linear fashion. Within those bounds, drivers + * are free to use whatever they want to calculate "Link Quality". + */ + if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) && !(qual->updated & IW_QUAL_QUAL_INVALID)) + percent = (int)(100 * ((double)qual->qual / (double)max_qual->qual)); + + /* If the driver doesn't specify a complete and valid quality, we have two options: + * + * 1) dBm: driver must specify max_qual->level = 0, and have valid values for + * qual->level and (qual->noise OR max_qual->noise) + * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for + * qual->level and max_qual->level + * + * This is the WEXT spec. If this interpretation is wrong, I'll fix it. Otherwise, + * If drivers don't conform to it, they are wrong and need to be fixed. + */ + + if ( (max_qual->level == 0) && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level == 0 */ + && !(qual->updated & IW_QUAL_LEVEL_INVALID) /* Must have valid qual->level */ + && ( ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */ + || ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))) /* OR valid qual->noise */ + ) { + /* Absolute power values (dBm) */ + + /* Reasonable fallbacks for dumb drivers that don't specify either level. */ + #define FALLBACK_NOISE_FLOOR_DBM -90 + #define FALLBACK_SIGNAL_MAX_DBM -20 + int max_level = FALLBACK_SIGNAL_MAX_DBM; + int noise = FALLBACK_NOISE_FLOOR_DBM; + int level = qual->level - 0x100; + + level = CLAMP (level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); + + if ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID)) + noise = qual->noise - 0x100; + else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) + noise = max_qual->noise - 0x100; + noise = CLAMP (noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); + + /* A sort of signal-to-noise ratio calculation */ + level_percent = (int)(100 - 70 *( + ((double)max_level - (double)level) / + ((double)max_level - (double)noise))); + nm_log_dbg (LOGD_WIFI, "QL1: level_percent is %d. max_level %d, level %d, noise_floor %d.", + level_percent, max_level, level, noise); + } else if ( (max_qual->level != 0) + && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */ + && !(qual->updated & IW_QUAL_LEVEL_INVALID)) { + /* Relative power values (RSSI) */ + + int level = qual->level; + + /* Signal level is relavtive (0 -> max_qual->level) */ + level = CLAMP (level, 0, max_qual->level); + level_percent = (int)(100 * ((double)level / (double)max_qual->level)); + nm_log_dbg (LOGD_WIFI, "QL2: level_percent is %d. max_level %d, level %d.", + level_percent, max_qual->level, level); + } else if (percent == -1) { + nm_log_dbg (LOGD_WIFI, "QL: Could not get quality %% value from driver. Driver is probably buggy."); + } + + /* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */ + if ((percent < 1) && (level_percent >= 0)) + percent = level_percent; + + nm_log_dbg (LOGD_WIFI, "QL: Final quality percent is %d (%d).", + percent, CLAMP (percent, 0, 100)); + return (CLAMP (percent, 0, 100)); +} + +static int +wifi_wext_get_qual (WifiData *data) +{ + WifiDataWext *wext = (WifiDataWext *) data; + struct iwreq wrq; + struct iw_statistics stats; + + memset (&stats, 0, sizeof (stats)); + wrq.u.data.pointer = &stats; + wrq.u.data.length = sizeof (stats); + wrq.u.data.flags = 1; /* Clear updated flag */ + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + + if (ioctl (wext->fd, SIOCGIWSTATS, &wrq) < 0) { + nm_log_warn (LOGD_HW | LOGD_WIFI, + "(%s): error getting signal strength: %s", + wext->parent.iface, strerror (errno)); + return -1; + } + + return wext_qual_to_percent (&stats.qual, &wext->max_qual); +} + +/*********************/ +/* OLPC Mesh-only functions */ + +static guint32 +wifi_wext_get_mesh_channel (WifiData *data) +{ + WifiDataWext *wext = (WifiDataWext *) data; + guint32 freq; + int i; + + freq = wifi_utils_get_freq (data); + for (i = 0; i < wext->num_freqs; i++) { + if (freq == wext->freqs[i]) + return i + 1; + } + return 0; +} + +static gboolean +wifi_wext_set_mesh_channel (WifiData *data, guint32 channel) +{ + WifiDataWext *wext = (WifiDataWext *) data; + struct iwreq wrq; + + memset (&wrq, 0, sizeof (struct iwreq)); + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + + if (channel > 0) { + wrq.u.freq.flags = IW_FREQ_FIXED; + wrq.u.freq.e = 0; + wrq.u.freq.m = channel; + } + + if (ioctl (wext->fd, SIOCSIWFREQ, &wrq) < 0) { + nm_log_err (LOGD_HW | LOGD_WIFI | LOGD_OLPC_MESH, + "(%s): error setting channel to %d: %s", + wext->parent.iface, channel, strerror (errno)); + return FALSE; + } + + return TRUE; +} + +static gboolean +wifi_wext_set_mesh_ssid (WifiData *data, const GByteArray *ssid) +{ + WifiDataWext *wext = (WifiDataWext *) data; + struct iwreq wrq; + guint32 len = 0; + char buf[IW_ESSID_MAX_SIZE + 1]; + + memset (buf, 0, sizeof (buf)); + if (ssid) { + len = ssid->len; + memcpy (buf, ssid->data, MIN (sizeof (buf) - 1, len)); + } + wrq.u.essid.pointer = (caddr_t) buf; + wrq.u.essid.length = len; + wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */ + + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + if (ioctl (wext->fd, SIOCSIWESSID, &wrq) == 0) + return TRUE; + + if (errno != ENODEV) { + nm_log_err (LOGD_HW | LOGD_WIFI | LOGD_OLPC_MESH, + "(%s): error setting SSID to '%s': %s", + wext->parent.iface, + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(null)", + strerror (errno)); + } + + return FALSE; +} + +/*********************/ + +static gboolean +wext_can_scan (WifiDataWext *wext) +{ + struct iwreq wrq; + + memset (&wrq, 0, sizeof (struct iwreq)); + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + if (ioctl (wext->fd, SIOCSIWSCAN, &wrq) < 0) { + if (errno == EOPNOTSUPP) + return FALSE; + } + return TRUE; +} + +static gboolean +wext_get_range (WifiDataWext *wext, + struct iw_range *range, + guint32 *response_len) +{ + int i = 26; + gboolean success = FALSE; + struct iwreq wrq; + + memset (&wrq, 0, sizeof (struct iwreq)); + strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ); + wrq.u.data.pointer = (caddr_t) range; + wrq.u.data.length = sizeof (struct iw_range); + + /* Need to give some drivers time to recover after suspend/resume + * (ex ipw3945 takes a few seconds to talk to its regulatory daemon; + * see rh bz#362421) + */ + while (i-- > 0) { + if (ioctl (wext->fd, SIOCGIWRANGE, &wrq) == 0) { + if (response_len) + *response_len = wrq.u.data.length; + success = TRUE; + break; + } else if (errno != EAGAIN) { + nm_log_err (LOGD_HW | LOGD_WIFI, + "(%s): couldn't get driver range information (%d).", + wext->parent.iface, errno); + break; + } + + g_usleep (G_USEC_PER_SEC / 4); + } + + if (i <= 0) { + nm_log_warn (LOGD_HW | LOGD_WIFI, + "(%s): driver took too long to respond to IWRANGE query.", + wext->parent.iface); + } + + return success; +} + +#define WPA_CAPS (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | \ + NM_WIFI_DEVICE_CAP_CIPHER_CCMP | \ + NM_WIFI_DEVICE_CAP_WPA | \ + NM_WIFI_DEVICE_CAP_RSN) + +static guint32 +wext_get_caps (WifiDataWext *wext, struct iw_range *range) +{ + guint32 caps = NM_WIFI_DEVICE_CAP_NONE; + + g_return_val_if_fail (wext != NULL, NM_WIFI_DEVICE_CAP_NONE); + g_return_val_if_fail (range != NULL, NM_WIFI_DEVICE_CAP_NONE); + + /* All drivers should support WEP by default */ + caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104; + + if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) + caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP; + + if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) + caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP; + + if (range->enc_capa & IW_ENC_CAPA_WPA) + caps |= NM_WIFI_DEVICE_CAP_WPA; + + if (range->enc_capa & IW_ENC_CAPA_WPA2) + caps |= NM_WIFI_DEVICE_CAP_RSN; + + /* Check for cipher support but not WPA support */ + if ( (caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) + && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) { + nm_log_warn (LOGD_WIFI, "%s: device supports WPA ciphers but not WPA protocol; " + "WPA unavailable.", wext->parent.iface); + caps &= ~WPA_CAPS; + } + + /* Check for WPA support but not cipher support */ + if ( (caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN)) + && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) { + nm_log_warn (LOGD_WIFI, "%s: device supports WPA protocol but not WPA ciphers; " + "WPA unavailable.", wext->parent.iface); + caps &= ~WPA_CAPS; + } + + return caps; +} + +WifiData * +wifi_wext_init (const char *iface, int ifindex, gboolean check_scan) +{ + WifiDataWext *wext; + struct iw_range range; + guint32 response_len = 0; + struct iw_range_with_scan_capa *scan_capa_range; + int i; + + wext = wifi_data_new (iface, ifindex, sizeof (*wext)); + wext->parent.get_mode = wifi_wext_get_mode; + wext->parent.set_mode = wifi_wext_set_mode; + wext->parent.get_freq = wifi_wext_get_freq; + wext->parent.find_freq = wifi_wext_find_freq; + wext->parent.get_ssid = wifi_wext_get_ssid; + wext->parent.get_bssid = wifi_wext_get_bssid; + wext->parent.get_rate = wifi_wext_get_rate; + wext->parent.get_qual = wifi_wext_get_qual; + wext->parent.deinit = wifi_wext_deinit; + wext->parent.get_mesh_channel = wifi_wext_get_mesh_channel; + wext->parent.set_mesh_channel = wifi_wext_set_mesh_channel; + wext->parent.set_mesh_ssid = wifi_wext_set_mesh_ssid; + + wext->fd = socket (PF_INET, SOCK_DGRAM, 0); + if (wext->fd < 0) + goto error; + + memset (&range, 0, sizeof (struct iw_range)); + if (wext_get_range (wext, &range, &response_len) == FALSE) { + nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver WEXT range request failed", + wext->parent.iface); + goto error; + } + + if ((response_len < 300) || (range.we_version_compiled < 21)) { + nm_log_info (LOGD_HW | LOGD_WIFI, + "(%s): driver WEXT version too old (got %d, expected >= 21)", + wext->parent.iface, + range.we_version_compiled); + goto error; + } + + wext->max_qual.qual = range.max_qual.qual; + wext->max_qual.level = range.max_qual.level; + wext->max_qual.noise = range.max_qual.noise; + wext->max_qual.updated = range.max_qual.updated; + + wext->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES); + for (i = 0; i < wext->num_freqs; i++) + wext->freqs[i] = iw_freq_to_uint32 (&range.freq[i]); + + /* Check for scanning capability; cards that can't scan are not supported */ + if (check_scan && (wext_can_scan (wext) == FALSE)) { + nm_log_info (LOGD_HW | LOGD_WIFI, + "(%s): drivers that cannot scan are unsupported", + wext->parent.iface); + goto error; + } + + /* Check for the ability to scan specific SSIDs. Until the scan_capa + * field gets added to wireless-tools, need to work around that by casting + * to the custom structure. + */ + scan_capa_range = (struct iw_range_with_scan_capa *) ⦥ + if (scan_capa_range->scan_capa & NM_IW_SCAN_CAPA_ESSID) { + wext->parent.can_scan_ssid = TRUE; + nm_log_info (LOGD_HW | LOGD_WIFI, + "(%s): driver supports SSID scans (scan_capa 0x%02X).", + wext->parent.iface, + scan_capa_range->scan_capa); + } else { + nm_log_info (LOGD_HW | LOGD_WIFI, + "(%s): driver does not support SSID scans (scan_capa 0x%02X).", + wext->parent.iface, + scan_capa_range->scan_capa); + } + + wext->parent.caps = wext_get_caps (wext, &range); + + nm_log_info (LOGD_HW | LOGD_WIFI, + "(%s): using WEXT for WiFi device control", + wext->parent.iface); + + return (WifiData *) wext; + +error: + wifi_utils_deinit ((WifiData *) wext); + return NULL; +} + +gboolean +wifi_wext_is_wifi (const char *iface) +{ + int fd; + struct iwreq iwr; + gboolean is_wifi = FALSE; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd >= 0) { + strncpy (iwr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ); + if (ioctl (fd, SIOCGIWNAME, &iwr) == 0) + is_wifi = TRUE; + close (fd); + } + return is_wifi; +} + diff --git a/src/wifi/wifi-utils-wext.h b/src/wifi/wifi-utils-wext.h new file mode 100644 index 00000000..96ad74eb --- /dev/null +++ b/src/wifi/wifi-utils-wext.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2011 Red Hat, Inc. + */ + +#ifndef WIFI_UTILS_WEXT_H +#define WIFI_UTILS_WEXT_H + +#include "wifi-utils.h" + +WifiData *wifi_wext_init (const char *iface, int ifindex, gboolean check_scan); + +gboolean wifi_wext_is_wifi (const char *iface); + +#endif /* WIFI_UTILS_WEXT_H */ diff --git a/src/wifi/wifi-utils.c b/src/wifi/wifi-utils.c new file mode 100644 index 00000000..a99a4b7b --- /dev/null +++ b/src/wifi/wifi-utils.c @@ -0,0 +1,213 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2011 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include <config.h> +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> +#include <glib.h> + +#include "wifi-utils.h" +#include "wifi-utils-private.h" +#include "wifi-utils-nl80211.h" +#if HAVE_WEXT +#include "wifi-utils-wext.h" +#endif + +gpointer +wifi_data_new (const char *iface, int ifindex, gsize len) +{ + WifiData *data; + + data = g_malloc0 (len); + data->iface = g_strdup (iface); + data->ifindex = ifindex; + return data; +} + +void +wifi_data_free (WifiData *data) +{ + g_free (data->iface); + memset (data, 0, sizeof (*data)); + g_free (data); +} + +/***************************************************************/ + +WifiData * +wifi_utils_init (const char *iface, int ifindex, gboolean check_scan) +{ + WifiData *ret; + + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (ifindex > 0, NULL); + + ret = wifi_nl80211_init (iface, ifindex); + if (ret == NULL) { +#if HAVE_WEXT + ret = wifi_wext_init (iface, ifindex, check_scan); +#endif + } + return ret; +} + +NMDeviceWifiCapabilities +wifi_utils_get_caps (WifiData *data) +{ + g_return_val_if_fail (data != NULL, NM_WIFI_DEVICE_CAP_NONE); + + return data->caps; +} + +gboolean +wifi_utils_can_scan_ssid (WifiData *data) +{ + g_return_val_if_fail (data != NULL, FALSE); + return data->can_scan_ssid; +} + +NM80211Mode +wifi_utils_get_mode (WifiData *data) +{ + g_return_val_if_fail (data != NULL, NM_802_11_MODE_UNKNOWN); + return data->get_mode (data); +} + +gboolean +wifi_utils_set_mode (WifiData *data, const NM80211Mode mode) +{ + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail ((mode == NM_802_11_MODE_INFRA) || (mode == NM_802_11_MODE_ADHOC), FALSE); + + /* nl80211 probably doesn't need this */ + return data->set_mode ? data->set_mode (data, mode) : TRUE; +} + +guint32 +wifi_utils_get_freq (WifiData *data) +{ + g_return_val_if_fail (data != NULL, 0); + return data->get_freq (data); +} + +guint32 +wifi_utils_find_freq (WifiData *data, const guint32 *freqs) +{ + g_return_val_if_fail (data != NULL, 0); + g_return_val_if_fail (freqs != NULL, 0); + return data->find_freq (data, freqs); +} + +GByteArray * +wifi_utils_get_ssid (WifiData *data) +{ + g_return_val_if_fail (data != NULL, NULL); + return data->get_ssid (data); +} + +gboolean +wifi_utils_get_bssid (WifiData *data, struct ether_addr *out_bssid) +{ + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (out_bssid != NULL, FALSE); + + memset (out_bssid, 0, sizeof (*out_bssid)); + return data->get_bssid (data, out_bssid); +} + +guint32 +wifi_utils_get_rate (WifiData *data) +{ + g_return_val_if_fail (data != NULL, 0); + return data->get_rate (data); +} + +int +wifi_utils_get_qual (WifiData *data) +{ + g_return_val_if_fail (data != NULL, 0); + return data->get_qual (data); +} + +void +wifi_utils_deinit (WifiData *data) +{ + g_return_if_fail (data != NULL); + data->deinit (data); + wifi_data_free (data); +} + +gboolean +wifi_utils_is_wifi (const char *iface, const char *sysfs_path) +{ + char phy80211_path[255]; + struct stat s; + + g_return_val_if_fail (iface != NULL, FALSE); + + if (sysfs_path) { + /* Check for nl80211 sysfs paths */ + snprintf (phy80211_path, sizeof (phy80211_path), "%s/phy80211", sysfs_path); + if ((stat (phy80211_path, &s) == 0 && (s.st_mode & S_IFDIR))) + return TRUE; + } + + if (wifi_nl80211_is_wifi (iface)) + return TRUE; + +#if HAVE_WEXT + if (wifi_wext_is_wifi (iface)) + return TRUE; +#endif + + return FALSE; +} + + +/* OLPC Mesh-only functions */ + +guint32 +wifi_utils_get_mesh_channel (WifiData *data) +{ + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (data->get_mesh_channel != NULL, FALSE); + return data->get_mesh_channel (data); +} + +gboolean +wifi_utils_set_mesh_channel (WifiData *data, guint32 channel) +{ + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (channel >= 0, FALSE); + g_return_val_if_fail (channel <= 13, FALSE); + g_return_val_if_fail (data->set_mesh_channel != NULL, FALSE); + return data->set_mesh_channel (data, channel); +} + +gboolean +wifi_utils_set_mesh_ssid (WifiData *data, const GByteArray *ssid) +{ + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (data->set_mesh_ssid != NULL, FALSE); + return data->set_mesh_ssid (data, ssid); +} + diff --git a/src/wifi/wifi-utils.h b/src/wifi/wifi-utils.h new file mode 100644 index 00000000..09583e7b --- /dev/null +++ b/src/wifi/wifi-utils.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2011 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef WIFI_UTILS_H +#define WIFI_UTILS_H + +#include <net/ethernet.h> +#include <glib.h> + +#include "NetworkManager.h" + +typedef struct WifiData WifiData; + +gboolean wifi_utils_is_wifi (const char *iface, const char *sysfs_path); + +WifiData *wifi_utils_init (const char *iface, int ifindex, gboolean check_scan); + +void wifi_utils_deinit (WifiData *data); + +NMDeviceWifiCapabilities wifi_utils_get_caps (WifiData *data); + +gboolean wifi_utils_can_scan_ssid (WifiData *data); + +NM80211Mode wifi_utils_get_mode (WifiData *data); + +gboolean wifi_utils_set_mode (WifiData *data, const NM80211Mode mode); + +/* Returns frequency in MHz */ +guint32 wifi_utils_get_freq (WifiData *data); + +/* Return the first supported frequency in the zero-terminated list */ +guint32 wifi_utils_find_freq (WifiData *data, const guint32 *freqs); + +/* Caller must free returned byte array */ +GByteArray *wifi_utils_get_ssid (WifiData *data); + +/* Caller must free returned byte array */ +gboolean wifi_utils_get_bssid (WifiData *data, struct ether_addr *out_bssid); + +/* Returns current bitrate in Kbps */ +guint32 wifi_utils_get_rate (WifiData *data); + +/* Returns quality 0 - 100% on succes, or -1 on error */ +int wifi_utils_get_qual (WifiData *data); + + +/* OLPC Mesh-only functions */ +guint32 wifi_utils_get_mesh_channel (WifiData *data); + +gboolean wifi_utils_set_mesh_channel (WifiData *data, guint32 channel); + +gboolean wifi_utils_set_mesh_ssid (WifiData *data, const GByteArray *ssid); + +#endif /* WIFI_UTILS_H */ |