#! /bin/bash
#
# Copyright (c) 2002 SuSE Linux AG Nuernberg, Germany.
# All rights reserved.
#
# 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., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA
#
# Author: Christian Zoz <zoz@suse.de>, 2001
#         Joachim Gleissner <jg@suse.de>, 2003
#
# $Id: ifup-wireless,v 1.20 2003/09/15 12:38:05 jg Exp $
#

usage () {
	echo $@
	echo "usage: if{up,status}-wireless <config> [<interface>]  [-o <options>]"
	echo "  In most cases config==interface, for details see man 8 ifup"
	echo "options are: [on]boot : we are currently booting (or shutting down)"
	echo "             hotplug  : we are handling a hotplug event"
	echo "all other or wrong options are silently ignored"
	exit $R_USAGE
}

######################################################################
# change the working direcory and source some common files
#
R_INTERNAL=1      # internal error, e.g. no config or missing scripts
cd /etc/sysconfig/network || exit $R_INTERNAL
test -f scripts/functions && . scripts/functions || exit $R_INTERNAL
test -f config && . config

######################################################################
# check arguments and how we are called (in case of links)
#
SCRIPTNAME=`basename $0`
debug $*
case "${SCRIPTNAME}" in
	ifup-*) ACTION=start ;;
	ifstatus-*) ACTION=status ;;
	*) usage
esac
CONFIG=$1
case "$CONFIG" in ""|-h|*help*) usage; esac
shift
test "$1" != "-o" && INTERFACE=$1
shift
test "$1" = "-o" && shift
OPTIONS=$@
MODE=manual
while [ $# -gt 0 ]; do
	case $1 in
		boot|onboot) MODE=onboot ;;
		hotplug)     MODE=hotplug ;;
		quiet)       BE_QUIET=yes ;;
		debug)       BE_QUIET=no
		             DEBUG=yes ;;
		*)           debug "unknown option $1 ignored" ;;
	esac
	shift
done

######################################################################
# get the interface and check if it is available or up
#
test -z "$INTERFACE" && INTERFACE=$CONFIG
# can't "ip link list" labeled interfaces (aliases), only masters:
if ! is_iface_available  ${INTERFACE%:*} ; then
	test "$ACTION" != status && logerror "interface ${INTERFACE} is not available"
	exit $R_NODEV
fi
#if ! is_iface_up $INTERFACE ; then
#	logerror "interface ${INTERFACE} is not up"
#	exit $R_NOTRUNNING
#fi

######################################################################
# check presence of global configuration file and source it
#
test -f wireless && . wireless

# setting wireless to "yes" in case type is "wlan"
test "${CONFIG%%-*}" = "wlan" && WIRELESS="yes"

# source device specific configuration file
test -f ifcfg-$CONFIG && . ifcfg-$CONFIG

######################################################################
# check for needed tools and if interface is wireless 
#
# $WIRELESS=yes/no can be set in a config file if there is no other way to find
# out. If it is empty we try to find out somehow (e.g. MAC addresses).
# If HOTPLUG knows it, it can export HOTPLUG_WIRELESS=yes/no; this will
# overwrite everything else.
test -n "$HOTPLUG_WIRELESS" && WIRELESS=$HOTPLUG_WIRELESS

# exit in case WIRELESS is excplicitely set to no
test "$WIRELESS" = "no" && exit

# check for needed tools
if ! iwconfig &>/dev/null ; then
	if [ "$WIRELESS" = "yes" ]; then
		logerror "cannot find 'iwconfig': please install package 'wireless-tools'"
		exit $R_ERROR
	else
		# we should autodetect but have no iwconfig
		# aborting without much noise
		debug "Warning: 'iwconfig' not found, please install 'wireless-tools'"
		exit 0
	fi
fi

# check whether our device is wireless
# if set we use value of WIRELESS, otherwise we do autodetect
if [ "$WIRELESS" != "yes" ]; then
	test -z "$(iwconfig 2>&1 | grep -E ^$INTERFACE | grep -v 'no wireless extensions')" && exit
fi

if [ -z "`type -p wlanctl-ng`" ] ; then
	logerror "cannot find 'wlanctl-ng': please install package 'pcmcia'"
	exit $R_ERROR
fi
if [ -z "`type -p hexdump`" ] ; then
	logerror "cannot find 'hexdump': please install package 'util-linux'"
	exit $R_ERROR
fi
if [ -z "`type -p sed`" ] ; then
	logerror "cannot find 'sed': please install package 'sed'"
	exit $R_ERROR
fi
if [ -z "`type -p awk`" ] ; then
	logerror "cannot find 'awk': please install package 'gawk'"
	exit $R_ERROR
fi


run_iw_tool() {
	local COMMAND MESSAGE
	RETVAL=0
	test -z "$3" && return
	debug "run_iw_tool()" "$@"
	case $1 in 
		config|spy|priv) IWTOOL=iw${1} ;;
		*) exit $R_INTERNAL ;;
	esac
	shift
	MESSAGE=`$IWTOOL $INTERFACE "$@" 2>&1` || RETVAL=$?
	test -z "$MESSAGE" && return
	logerror "command '$IWTOOL $INTERFACE $*' returned\n $MESSAGE"
}

RETVAL=0

wlanctl()
{
	local MESSAGE
	test -z "$1" && return
	debug "running wlanctl-ng $INTERFACE $@"
	MESSAGE=`wlanctl-ng $INTERFACE "$@" 2>&1` || RETVAL=$?
	debug $MESSAGE
}

ascii_to_hex()
{
	if [ -z "${1:7:1}" ]; then
		echo -n ${1#s:*} | hexdump -e '5/1 "%2x"' | sed -e 's/ /0/g'
	else
		echo -n ${1#s:*} | hexdump -e '13/1 "%2x"' | sed -e 's/ /0/g'
	fi
}

generate_keys()
{
	if [ -z "$WIRELESS_KEY" ]; then
		message "warning: using NO encryption"
		return
	fi
	test -z "$WIRELESS_KEY_0" && WIRELESS_KEY_0=$WIRELESS_KEY
	for i in 0 1 2 3 ; do
		eval K=\$WIRELESS_KEY_$i
		if [ -n "$K" ]; then
			if [ ${K:0:1} = "s" ]; then
				# we are using ascii key representation (iwconfig method)
				eval WIRELESS_KEY_$i=$(ascii_to_hex $K)
			elif [ ${K:0:1} = "h" ]; then
				if [ "$WIRELESS_KEY_LENGTH" = "64" ]; then
					eval WIRELESS_KEY_$i=$(nwepgen ${K:2} | head -n 1 | tr -d ':')
				else
					eval WIRELESS_KEY_$i=$(genwepkey ${K:2})
				fi
			fi
		fi
	done
}

format_key()
{
	# key has usually format XXXX-XXXX-XX [...]
	echo $1 | tr -d '-' | awk '{ KEY=$0 ;
		if (length()>10) for (i=0;i<26-length();i++) KEY=KEY"0"
		for (i=1;i<length(KEY)-1;i+=2) FKEY=FKEY substr(KEY, i, 2)":"
		FKEY=FKEY substr(KEY, i, 2)
		print FKEY }' 
}

setup_iwdev()
{
	# Mode need to be first : some settings apply only in a specific mode !
	run_iw_tool config mode $WIRELESS_MODE
	# This is a bit hackish, but should do the job right...
	if [ -n "$WIRELESS_ESSID" -o -n "$WIRELESS_MODE" ] ; then
		test -z "$WIRELESS_NICK" && WIRELESS_NICK=`/bin/hostname`
	fi
	# Regular stuff...
	while read OPT ARG; do
		run_iw_tool config $OPT "$ARG"
	done <<-EOL
		nick $WIRELESS_NICK
		nwid $WIRELESS_NWID
		freq $WIRELESS_FREQ
		channel $WIRELESS_CHANNEL
		sens $WIRELESS_SENS
		rate $WIRELESS_RATE
		rts $WIRELESS_RTS
		frag $WIRELESS_FRAG
		$WIRELESS_IWCONFIG_OPTIONS
		EOL
	# set encryption key(s)
	if [ -n "$WIRELESS_KEY_0" -a \
	     -z "$WIRELESS_KEY_1" -a \
	     -z "$WIRELESS_KEY_2" -a \
	     -z "$WIRELESS_KEY_3" ]; then
		# some drivers (at least madwifi) do not like multiple keys
		# so we do not use that setting method when we have only one
		run_iw_tool config key $WIRELESS_SEC_MODE $WIRELESS_KEY_0
	elif [ -n "$WIRELESS_KEY_0" ]; then
		ARG="key $WIRELESS_SEC_MODE $WIRELESS_KEY_0 [1]"
		for i in 1 2 3 ; do 
			eval K=\$WIRELESS_KEY_$i
			test -n "$K" && ARG="$ARG key $K [$((i+1))]"
		done
		run_iw_tool config $ARG
		if [ $RETVAL -ne 0 ]; then
			logerror "setting encryption key FAILED, aborting interface setup"
			exit $R_ERROR
		fi
		test -z "$WIRELESS_DEFAULT_KEY" && WIRELESS_DEFAULT_KEY=0
		run_iw_tool config key [$((WIRELESS_DEFAULT_KEY+1))]
	fi
	run_iw_tool spy $WIRELESS_IWSPY_OPTIONS
	run_iw_tool priv $WIRELESS_IWPRIV_OPTIONS
	# ESSID need to be last : most device re-perform the scanning/discovery
	# when this is set, and things like encryption keys are better be
	# defined if we want to discover the right set of APs/nodes.
	run_iw_tool config essid "$WIRELESS_ESSID"
}

setup_wlanngdev()
{
	wlanctl dot11req_reset setdefaultmib=false
	if [ -z "$WIRELESS_MODE" ]; then
		message "warning: WIRELESS_MODE is unset, using Ad-hoc"
		WIRELESS_MODE="Ad-hoc"
	fi
	if [ -z "$WIRELESS_KEY" ]; then
		wlanctl dot11req_mibset mibattribute=dot11PrivacyInvoked=false
		wlanctl dot11req_mibset mibattribute=dot11ExcludeUnencrypted=false
	else
		result=`wlanctl-ng $INTERFACE dot11req_mibget mibattribute=dot11PrivacyOptionImplemented`
		if [ $? = 0 ] ; then
			eval $result
			eval $mibattribute
		else
			logerror "Could not query device: $result"
			exit $R_ERROR
		fi
		if [ "$dot11PrivacyOptionImplemented" = "false" ]; then
			logerror "Could not set encryption, device does not support it"
			exit $R_ERROR
		fi
		wlanctl dot11req_mibset mibattribute=dot11PrivacyInvoked=true
		test -z "$WIRELESS_DEFAULT_KEY" && WIRELESS_DEFAULT_KEY=0
		wlanctl dot11req_mibset mibattribute=dot11WEPDefaultKeyID=$WIRELESS_DEFAULT_KEY
		for i in 0 1 2 3 ; do
			eval K=\$WIRELESS_KEY_$i
			if [ -n "$K" ]; then
				wlanctl dot11req_mibset mibattribute=dot11WEPDefaultKey$i=$( format_key $K )
				test $RETVAL -ne 0 && 
					{ logerror "Setting encryption key failed, aborting interface setup" ;
					  exit $R_ERROR ; }
			fi
		done
		case "$WIRELESS_SEC_MODE" in
			restricted) wlanctl dot11req_mibset mibattribute=dot11ExcludeUnencrypted=true;;
			open) wlanctl dot11req_mibset mibattribute=dot11ExcludeUnencrypted=false;;
		esac
	fi
	case "$WIRELESS_MODE" in
		Managed)
		wlanctl lnxreq_autojoin ssid="$WIRELESS_ESSID" authtype=opensystem
		;;
		Ad-hoc)
		test -z "$WIRELESS_ESSID" && WIRELESS_ESSID="linux"
		test -z "$WIRLEESS_CHANNEL" && WIRELESS_CHANNEL=7
		wlanctl dot11req_start \
				ssid=$WIRELESS_ESSID \
				bsstype=independent \
				beaconperiod=100 \
				dtimperiod=3 \
				cfpollable=false \
				cfpollreq=false \
				cfpperiod=3 \
				cfpmaxduration=100 \
				probedelay=100 \
				dschannel=$WIRELESS_CHANNEL \
				basicrate1=2 \
				basicrate2=4 \
				operationalrate1=2 \
				operationalrate2=4 \
				operationalrate3=11 \
				operationalrate4=22
		;;
		Master|Repeater|Secondary)
		logerrer "Mode $WIRELESS_MODE is not supported yet for wlanng devices"
		exit $R_ERROR
		;;
	esac
}

case $ACTION in
	start)
		debug "warning: debug mode logs your encryption keys!"
		generate_keys
		# probe for (and enable) wlan-ng devices
		wlanctl-ng $INTERFACE lnxreq_ifstate ifstate=enable >/dev/null 2>&1
		if [ $? -eq 0 ]; then
			setup_wlanngdev
		else
			setup_iwdev
		fi
		;;
	status)
		iwconfig $INTERFACE
		;;
esac

exit $RETVAL
