From: Michael Tremer Date: Sun, 25 Jul 2010 12:17:13 +0000 (+0200) Subject: network: Experimental support for wireless access points. X-Git-Tag: 001~38 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d76f51071530b7603146051648d060ba1554f1bc;p=network.git network: Experimental support for wireless access points. --- diff --git a/functions.cli b/functions.cli index 7986413e..aa0bddf6 100644 --- a/functions.cli +++ b/functions.cli @@ -506,3 +506,12 @@ function cli_yesno() { return ${EXIT_ERROR} } + +function cli_get_key() { + local key="${1%%=*}" + echo "${key/--/}" +} + +function cli_get_val() { + echo "${1##*=}" +} diff --git a/functions.ports b/functions.ports index 6c59578d..74be61df 100644 --- a/functions.ports +++ b/functions.ports @@ -158,3 +158,21 @@ function ports_init() { } init_register ports_init + +function port_find_free() { + local pattern=${1} + + assert isset pattern + + local port + local i=0 + + while [ ${i} -lt 99 ]; do + port=${pattern//N/${i}} + if ! port_exists ${port} && ! device_exists ${port}; then + echo "${port}" + break + fi + i=$(( ${i} + 1 )) + done +} diff --git a/functions.util b/functions.util index 8be825ee..104316ff 100644 --- a/functions.util +++ b/functions.util @@ -386,3 +386,22 @@ function binary_exists() { return ${EXIT_ERROR} } + +function process_kill() { + local process=${1} + + if ! isinteger process; then + process=$(pidof ${process}) + fi + + local pid + local sig + for pid in ${process}; do + for sig in 15 9; do + [ -d "/proc/${pid}" ] || break + + kill -${sig} ${pid} + sleep 1 + done + done +} diff --git a/functions.wireless b/functions.wireless new file mode 100644 index 00000000..7cfd7f13 --- /dev/null +++ b/functions.wireless @@ -0,0 +1,278 @@ +#!/bin/bash +# XXX header missing + +PHY_DIR="/sys/class/ieee80211" + +function phy_dir() { + local phy=${1} + + echo "${PHY_DIR}/${phy}" +} + +function phy_exists() { + local phy=${1} + + [ -d "$(phy_dir ${phy})" ] +} + +function phy_list() { + local phy + for phy in $(phy_dir)/*; do + phy=$(basename ${phy}) + echo "${phy}" + done +} + +function phy_get() { + local info=${1} + + local phy + + if listmatch ${info} $(phy_list); then + phy="${info}" + elif device_exists ${info}; then + info=$(device_get_address ${info}) + fi + + if [ -z "${phy}" ] && mac_is_valid ${info}; then + local i + for i in $(phy_list); do + if [ "${info}" = "$(phy_get_address ${i})" ]; then + phy=${i} + break + fi + done + fi + + if [ -z "${phy}" ]; then + return ${EXIT_ERROR} + fi + + echo "${phy}" + return ${EXIT_OK} +} + +function phy_get_address() { + local phy=${1} + + assert isset phy + + cat $(phy_dir ${phy})/macaddress 2>/dev/null +} + +function wireless_create() { + local device=${1} + local phy=$(phy_get ${2}) + local type=${3} + local mac=${4} + + assert isset device + assert isset phy + assert isset type + + isset mac || mac=$(mac_generate) + + assert phy_exists ${phy} + assert isoneof type managed __ap + + iw phy ${phy} interface add ${device} type ${type} + + if device_exists ${device}; then + device_set_address ${device} ${mac} + fi + + device_set_up ${device} +} + +function wireless_remove() { + local device=${1} + + assert device_exists ${device} + + device_set_down ${device} + + iw dev ${device} del +} + +function wireless_set_channel() { + local device=${1} + local channel=${2} + + assert isset device + assert device_exists ${device} + assert isset channel + + iw dev ${device} set channel ${channel} $@ +} + +function hostapd_init() { + mkdir -p $(hostapd_config_dir) +} + +init_register hostapd_init + +function hostapd_config_dir() { + local device=${1} + + echo "${RUN_DIR}/hostapd/${device}" +} + +function hostapd_config_write() { + local device=${1} + shift + + assert device_exists ${device} + + local broadcast_ssid + local channel + local country_code + local mode + local ssid + + while [ $# -gt 0 ]; do + case "${1}" in + --broadcast-ssid=*) + broadcast_ssid=${1#--broadcast-ssid=} + ;; + --channel=*) + channel=${1#--channel=} + ;; + --country-code=*) + country_code=${1#--country-code=} + ;; + --mode=*) + mode=${1#--mode=} + ;; + --ssid=*) + ssid=${1#--ssid=} + ;; + *) + warning_log "Ignoring unknown argument '${1}'." + ;; + esac + shift + done + + assert isset broadcast_ssid + assert isbool broadcast_ssid + + assert isset channel + assert isinteger channel + + assert isset country_code + assert isset mode + assert isset ssid + + local ignore_broadcast_ssid + if enabled broadcast_ssid; then + ignore_broadcast_ssid="0" + else + ignore_broadcast_ssid="1" + fi + + cat < ${config_file} + + hostapd -dd -B -P ${config_dir}/pid ${config_file} + local ret=$? + + case "${ret}" in + 0) + log DEBUG "Hostapd was successfully started for '${device}'." + return ${EXIT_OK} + ;; + 1) + error_log "Could not start hostapd properly for '${device}'." + + error_log "Configuration file dump:" + local line + while read line; do + error_log " ${line}" + done < ${config_file} + + return ${EXIT_ERROR} + ;; + esac +} + +function hostapd_stop() { + local device=${1} + + assert isset device + + local pid=$(hostapd_get_pid ${device}) + + if isset pid; then + process_kill ${pid} + else + warning_log "Could not find pid file for hostapd process running for ${device}." + fi + + rm -rf $(hostapd_config_dir ${device}) +} + +function hostapd_get_pid() { + local device=${1} + + assert isset device + + local pid_file="$(hostapd_config_dir ${device})/pid" + + [ -e "${pid_file}" ] || return ${EXIT_ERROR} + + cat ${pid_file} 2>/dev/null + return ${EXIT_OK} +} + +function hostapd_is_running() { + local device=${1} + + assert isset device + + local pid=$(hostapd_get_pid ${device}) + + if isset pid && [ -d "/proc/${pid}" ]; then + return ${EXIT_OK} + fi + + return ${EXIT_ERROR} +} diff --git a/hooks/ports/wireless-ap b/hooks/ports/wireless-ap new file mode 100755 index 00000000..4340a75d --- /dev/null +++ b/hooks/ports/wireless-ap @@ -0,0 +1,201 @@ +#!/bin/bash +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2010 Michael Tremer & Christian Schmidt # +# # +# 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 3 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, see . # +# # +############################################################################### + +. /lib/network/header-port + +DEVICE_PATTERN="wifiN" + +HOOK_SETTINGS="HOOK ADDRESS BROADCAST_SSID COUNTRY_CODE MODE PHY SSID" + +ADDRESS=$(mac_generate) +BROADCAST_SSID=on +CHANNEL=1 +COUNTRY_CODE="US" +MODE="g" +SSID= + +function _check() { + assert isset ADDRESS + assert ismac ADDRESS + assert isset BROADCAST_SSID + assert isbool BROADCAST_SSID + assert isset CHANNEL + assert isset COUNTRY_CODE + assert isset MODE + assert isoneof MODE b g + assert isset PHY + assert ismac PHY + assert isset SSID +} + +function _create() { + while [ $# -gt 0 ]; do + case "${1}" in + --broadcast-ssid=*) + BROADCAST_SSID=$(cli_get_val ${1}) + ;; + --channel=*) + CHANNEL=$(cli_get_val ${1}) + ;; + --country-code=*) + COUNTRY_CODE=$(cli_get_val ${1}) + ;; + --mac=*) + ADDRESS=$(cli_get_val ${1}) + ;; + --mode=*) + MODE=$(cli_get_val ${1}) + ;; + --phy=*) + PHY=$(cli_get_val ${1}) + ;; + --ssid=*) + SSID=$(cli_get_val ${1}) + ;; + *) + warning "Ignoring unknown argument '${1}'" + ;; + esac + shift + done + + # Save address of phy do identify it again + PHY=$(phy_get ${PHY}) + PHY=$(phy_get_address ${PHY}) + + local port=$(port_find_free ${DEVICE_PATTERN}) + assert isset port + + config_write $(port_file ${port}) ${HOOK_SETTINGS} + + exit ${EXIT_OK} +} + +function _edit() { + local port=${1} + shift + + assert isset port + + config_read $(port_file ${port}) + + while [ $# -gt 0 ]; do + case "${1}" in + --broadcast-ssid=*) + BROADCAST_SSID=$(cli_get_val ${1}) + ;; + --channel=*) + CHANNEL=$(cli_get_val ${1}) + ;; + --country-code=*) + COUNTRY_CODE=$(cli_get_val ${1}) + ;; + --ssid=*) + SSID=$(cli_get_val ${1}) + ;; + --mode=*) + MODE=$(cli_get_val ${1}) + ;; + *) + warning "Unknown argument '${1}'" + ;; + esac + shift + done + + config_write $(port_file ${port}) ${HOOK_SETTINGS} + + exit ${EXIT_OK} +} + +function _up() { + local port=${1} + + assert isset port + + config_read $(port_file ${port}) + + if ! device_exists ${port}; then + wireless_create ${port} ${PHY} __ap ${ADDRESS} + fi + + if ! hostapd_is_running ${port}; then + hostapd_start ${port} \ + --broadcast-ssid="${BROADCAST_SSID}" \ + --channel="${CHANNEL}" \ + --country-code="${COUNTRY_CODE}" \ + --mode="${MODE}" \ + --ssid="${SSID}" + + local ret=$? + + if [ ${ret} -eq ${EXIT_ERROR} ]; then + error_log "Could not start '${port}' because hostapd crashed previously." + ( _down ${port} ) + exit ${EXIT_ERROR} + fi + fi + + exit ${EXIT_OK} +} + +function _down() { + local port=${1} + + assert isset port + + config_read $(port_file ${port}) + + if ! device_exists ${port}; then + exit ${EXIT_OK} + fi + + hostapd_stop ${port} + wireless_remove ${port} + + exit ${EXIT_OK} +} + +function _status() { + local zone=${1} + local port=${2} + +config_read $(zone_dir ${zone})/${port} + + local device=$(devicify ${DEVICE_MAC}) + + printf " %-10s - " "${device}" + if ! device_is_up ${device}; then + echo -ne "${COLOUR_DOWN} DOWN ${COLOUR_NORMAL}" + else + local state=$(stp_port_state ${zone} ${device}) + local colour="COLOUR_STP_${state}" + printf "${!colour}%10s${COLOUR_NORMAL}" ${state} + fi + + echo -n " - DSR: $(stp_port_designated_root ${zone} ${device})" + echo -n " - Cost: $(stp_port_pathcost ${zone} ${device})" + echo + + exit ${EXIT_OK} +} + +run $@ diff --git a/hooks/zones/bridge.ports/wireless-ap b/hooks/zones/bridge.ports/wireless-ap new file mode 100755 index 00000000..f7dd5064 --- /dev/null +++ b/hooks/zones/bridge.ports/wireless-ap @@ -0,0 +1,155 @@ +#!/bin/bash +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2010 Michael Tremer & Christian Schmidt # +# # +# 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 3 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, see . # +# # +############################################################################### + +. /lib/network/header-port + +HOOK_SETTINGS="COST PRIORITY" + +function _check() { + local i + for i in COST PRIORITY; do + if isset ${i}; then + assert isinteger ${i} + fi + done +} + +function _add() { + local zone=${1} + local port=${2} + shift 2 + + assert isset zone + assert isset port + + if ! port_exists ${port}; then + error "Port '${port}' does not exist." + exit ${EXIT_ERROR} + fi + + config_read $(zone_dir ${zone})/ports/${port} + + while [ $# -gt 0 ]; do + case "${1}" in + --priority=*) + PRIORITY=${1#--priority=} + ;; + --cost=*) + COST=${1#--cost=} + ;; + esac + shift + done + + config_write $(zone_dir ${zone})/ports/${port} ${HOOK_SETTINGS} + + exit ${EXIT_OK} +} + +function _edit() { + _add $@ +} + +function _rem() { + local zone=${1} + local port=${2} + + assert isset zone + assert isset port + + assert zone_exists ${zone} + + if ! listmatch ${port} $(zone_get_ports ${zone}); then + error "Port '${port}' does not belong to '${zone}'." + error "Won't remove anything." + exit ${EXIT_ERROR} + fi + + if port_exists ${port}; then + ( _down ${zone} ${port} ) + fi + + rm -f $(zone_dir ${zone})/ports/${port} + + exit ${EXIT_OK} +} + +function _up() { + local zone=${1} + local port=${2} + + assert isset zone + assert isset port + + assert zone_exists ${zone} + assert port_exists ${port} + + port_up ${port} + + # Set same MTU to device that the bridge has got + device_set_mtu ${port} $(device_get_mtu ${zone}) + + bridge_attach_device ${zone} ${port} + + # XXX must set cost and prio here + + exit ${EXIT_OK} +} + +function _down() { + local zone=${1} + local port=${2} + + assert isset zone + assert isset port + + assert zone_exists ${zone} + assert port_exists ${port} + + bridge_detach_device ${zone} ${port} + + port_down ${port} + + exit ${EXIT_OK} +} + +function _status() { + local zone=${1} + local port=${2} + + printf " %-10s - " "${port}" + if ! device_is_up ${port}; then + echo -ne "${COLOUR_DOWN} DOWN ${COLOUR_NORMAL}" + else + local state=$(stp_port_state ${zone} ${port}) + local colour="COLOUR_STP_${state}" + printf "${!colour}%10s${COLOUR_NORMAL}" ${state} + + echo -n " - DSR: $(stp_port_designated_root ${zone} ${port})" + echo -n " - Cost: $(stp_port_pathcost ${zone} ${port})" + fi + + echo + + exit ${EXIT_OK} +} + +run $@