diff options
Diffstat (limited to 'package/network')
17 files changed, 2192 insertions, 0 deletions
diff --git a/package/network/config/ltq-dsl-app/Config.in b/package/network/config/ltq-dsl-app/Config.in new file mode 100644 index 000000000..728fe65b3 --- /dev/null +++ b/package/network/config/ltq-dsl-app/Config.in @@ -0,0 +1,9 @@ +config LTQ_DSL_ENABLE_SOAP + bool "Enable SOAP support" + depends on PACKAGE_ltq-dsl-app + default n + +config LTQ_DSL_ENABLE_DSL_EVENT_POLLING + bool "Include Event Polling support" + depends on PACKAGE_ltq-dsl-app + default n diff --git a/package/network/config/ltq-dsl-app/Makefile b/package/network/config/ltq-dsl-app/Makefile new file mode 100644 index 000000000..8985484fd --- /dev/null +++ b/package/network/config/ltq-dsl-app/Makefile @@ -0,0 +1,97 @@ +# +# Copyright (C) 2011 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=dsl_cpe_control_danube +PKG_VERSION:=3.24.4.4 +PKG_RELEASE:=1 +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_BUILD_DIR:=$(BUILD_DIR)/dsl_cpe_control-$(PKG_VERSION) +PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources/ +PKG_MD5SUM:=ee315306626b68794d3d3636dabfe161 +PKG_MAINTAINER:=John Crispin <blogic@openwrt.org> + +PKG_CONFIG_DEPENDS:=\ + CONFIG_LTQ_DSL_ENABLE_SOAP \ + CONFIG_LTQ_DSL_ENABLE_DSL_EVENT_POLLING + +include $(INCLUDE_DIR)/package.mk + +PKG_BUILD_DEPENDS:=TARGET_lantiq_danube:kmod-ltq-dsl-danube TARGET_lantiq_ar9:kmod-ltq-dsl-ar9 \ + TARGET_lantiq_vr9:kmod-ltq-dsl-vr9 TARGET_lantiq_ase:kmod-ltq-dsl-ase + +define Package/ltq-dsl-app + SECTION:=net + CATEGORY:=Network + TITLE:=Lantiq DSL userland tool + URL:=http://www.lantiq.com/ + DEPENDS:=@TARGET_lantiq&&!(TARGET_lantiq_falcon||TARGET_lantiq_falcon_stable) +libpthread + MENU:=1 +endef + +define Package/ltq-dsl-app/description + Infineon DSL CPE API for Amazon SE, Danube and Vinax. +endef + +define Package/ltq-dsl-app/config + source "$(SOURCE)/Config.in" +endef + +LTQ_DSL_MAX_DEVICE=1 +LTQ_DSL_LINES_PER_DEVICE=1 +LTQ_DSL_CHANNELS_PER_LINE=1 + +CONFIGURE_ARGS += \ + --with-max-device="$(LTQ_DSL_MAX_DEVICE)" \ + --with-lines-per-device="$(LTQ_DSL_LINES_PER_DEVICE)" \ + --with-channels-per-line="$(LTQ_DSL_CHANNELS_PER_LINE)" \ + --enable-danube \ + --enable-driver-include="-I$(STAGING_DIR)/usr/include" \ + --enable-debug-prints \ + --enable-add-appl-cflags="-DMAX_CLI_PIPES=2" \ + --enable-cli-support \ + --enable-cmv-scripts \ + --enable-debug-tool-interface \ + --enable-adsl-led \ + --enable-dsl-ceoc \ + --enable-script-notification \ + --enable-dsl-pm \ + --enable-dsl-pm-total \ + --enable-dsl-pm-history \ + --enable-dsl-pm-showtime \ + --enable-dsl-pm-channel-counters \ + --enable-dsl-pm-datapath-counters \ + --enable-dsl-pm-line-counters \ + --enable-dsl-pm-channel-thresholds \ + --enable-dsl-pm-datapath-thresholds \ + --enable-dsl-pm-line-thresholds \ + --enable-dsl-pm-optional-parameters + +ifeq ($(CONFIG_LTQ_DSL_ENABLE_SOAP),y) +CONFIGURE_ARGS += \ + --enable-soap-support +endif + +ifeq ($(CONFIG_LTQ_DSL_ENABLE_DSL_EVENT_POLLING),y) +CONFIGURE_ARGS += \ + --enable-dsl-event-polling +endif + +TARGET_CFLAGS += -I$(LINUX_DIR)/include + +define Package/ltq-dsl-app/install + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/dsl_control $(1)/etc/init.d/ + + $(INSTALL_DIR) $(1)/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/dsl_cpe_control $(1)/sbin + $(INSTALL_BIN) ./files/dsl_notify.sh $(1)/sbin +endef + +$(eval $(call BuildPackage,ltq-dsl-app)) diff --git a/package/network/config/ltq-dsl-app/files/dsl_control b/package/network/config/ltq-dsl-app/files/dsl_control new file mode 100644 index 000000000..02e89a0fd --- /dev/null +++ b/package/network/config/ltq-dsl-app/files/dsl_control @@ -0,0 +1,316 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2008 OpenWrt.org +START=99 + +. /lib/functions.sh + +EXTRA_COMMANDS="status lucistat" +EXTRA_HELP=" status Get DSL status information + lucistat Get status information if lua friendly format" + +SERVICE_DAEMONIZE=1 +SERVICE_WRITE_PID=1 + +# +# Basic functions to send CLI commands to the dsl_cpe_control daemon +# +dsl_cmd() { + killall -0 dsl_cpe_control && ( + echo "$@" > /tmp/pipe/dsl_cpe0_cmd + cat /tmp/pipe/dsl_cpe0_ack + ) +} +dsl_val() { + echo $(expr "$1" : '.*'$2'=\([-\.[:alnum:]]*\).*') +} + +# +# Simple divide by 10 routine to cope with one decimal place +# +dbt() { + local a=$(expr $1 / 10) + local b=$(expr $1 % 10) + echo "${a}.${b}" +} +# +# Take a number and convert to k or meg +# +scale() { + local val=$1 + local a + local b + + if [ "$val" -gt 1000000 ]; then + a=$(expr $val / 1000) + b=$(expr $a % 1000) + a=$(expr $a / 1000) + printf "%d.%03d Mb" ${a} ${b} + elif [ "$val" -gt 1000 ]; then + a=$(expr $val / 1000) + printf "%d Kb" ${a} + else + echo "${val} b" + fi +} + +# +# Read the data rates for both directions +# +data_rates() { + local csg + local dru + local drd + local sdru + local sdrd + + csg=$(dsl_cmd g997csg 0 1) + drd=$(dsl_val "$csg" ActualDataRate) + + csg=$(dsl_cmd g997csg 0 0) + dru=$(dsl_val "$csg" ActualDataRate) + + [ -z "$drd" ] && drd=0 + [ -z "$dru" ] && dru=0 + + sdrd=$(scale $drd) + sdru=$(scale $dru) + + if [ "$action" = "lucistat" ]; then + echo "dsl.data_rate_down=$drd" + echo "dsl.data_rate_up=$dru" + echo "dsl.data_rate_down_s=\"$sdrd\"" + echo "dsl.data_rate_up_s=\"$sdru\"" + else + echo "Data Rate: ${sdrd}/s / ${sdru}/s" + fi +} + +# +# Chipset +# +chipset() { + local vig + local cs + local csv + + vig=$(dsl_cmd vig) + cs=$(dsl_val "$vig" DSL_ChipSetType) + csv=$(dsl_val "$vig" DSL_ChipSetHWVersion) + + if [ "$action" = "lucistat" ]; then + echo "dsl.chipset=\"${cs} ${csv}\"" + else + echo "Chipset: ${cs} ${csv}" + fi +} + +# +# Work out how long the line has been up +# +line_uptime() { + local ccsg + local et + local etr + local d + local h + local m + local s + local rc="" + + ccsg=$(dsl_cmd pmccsg 0 0 0) + et=$(dsl_val "$ccsg" nElapsedTime) + + [ -z "$et" ] && et=0 + + if [ "$action" = "lucistat" ]; then + echo "dsl.line_uptime=${et}" + return + fi + + d=$(expr $et / 86400) + etr=$(expr $et % 86400) + h=$(expr $etr / 3600) + etr=$(expr $etr % 3600) + m=$(expr $etr / 60) + s=$(expr $etr % 60) + + + [ "${d}${h}${m}${s}" -ne 0 ] && rc="${s}s" + [ "${d}${h}${m}" -ne 0 ] && rc="${m}m ${rc}" + [ "${d}${h}" -ne 0 ] && rc="${h}h ${rc}" + [ "${d}" -ne 0 ] && rc="${d}d ${rc}" + + [ -z "$rc" ] && rc="down" + echo "Line Uptime: ${rc}" +} + +# +# Get noise and attenuation figures +# +line_data() { + local lsg + local latnu + local latnd + local snru + local snrd + + lsg=$(dsl_cmd g997lsg 1 1) + latnd=$(dsl_val "$lsg" LATN) + snrd=$(dsl_val "$lsg" SNR) + + lsg=$(dsl_cmd g997lsg 0 1) + latnu=$(dsl_val "$lsg" LATN) + snru=$(dsl_val "$lsg" SNR) + + [ -z "$latnd" ] && latnd=0 + [ -z "$latnu" ] && latnu=0 + [ -z "$snrd" ] && snrd=0 + [ -z "$snru" ] && snru=0 + + latnd=$(dbt $latnd) + latnu=$(dbt $latnu) + snrd=$(dbt $snrd) + snru=$(dbt $snru) + + if [ "$action" = "lucistat" ]; then + echo "dsl.line_attenuation_down=$latnd" + echo "dsl.line_attenuation_up=$latnu" + echo "dsl.noise_margin_down=$snrd" + echo "dsl.noise_margin_up=$snru" + else + echo "Line Attenuation: ${latnd}dB / ${latnu}dB" + echo "Noise Margin: ${snrd}dB / ${snru}dB" + fi +} + +# +# Is the line up? Or what state is it in? +# +line_state() { + local lsg=$(dsl_cmd lsg) + local ls=$(dsl_val "$lsg" nLineState); + local s; + + case "$ls" in + "0x0") s="not initialized" ;; + "0x1") s="exception" ;; + "0x10") s="not updated" ;; + "0xff") s="idle request" ;; + "0x100") s="idle" ;; + "0x1ff") s="silent request" ;; + "0x200") s="silent" ;; + "0x300") s="handshake" ;; + "0x380") s="full_init" ;; + "0x400") s="discovery" ;; + "0x500") s="training" ;; + "0x600") s="analysis" ;; + "0x700") s="exchange" ;; + "0x800") s="showtime_no_sync" ;; + "0x801") s="showtime_tc_sync" ;; + "0x900") s="fastretrain" ;; + "0xa00") s="lowpower_l2" ;; + "0xb00") s="loopdiagnostic active" ;; + "0xb10") s="loopdiagnostic data exchange" ;; + "0xb20") s="loopdiagnostic data request" ;; + "0xc00") s="loopdiagnostic complete" ;; + "0x1000000") s="test" ;; + "0xd00") s="resync" ;; + "0x3c0") s="short init entry" ;; + "") s="not running daemon"; ls="0xfff" ;; + *) s="unknown" ;; + esac + + if [ $action = "lucistat" ]; then + echo "dsl.line_state_num=$ls" + echo "dsl.line_state_detail=\"$s\"" + if [ "$ls" = "0x801" ]; then + echo "dsl.line_state=\"UP\"" + else + echo "dsl.line_state=\"DOWN\"" + fi + else + if [ "$ls" = "0x801" ]; then + echo "Line State: UP [$ls: $s]" + else + echo "Line State: DOWN [$ls: $s]" + fi + fi +} + +# +# Main status routine +# +status() { + chipset + line_state + data_rates + line_data + line_uptime +} + +# +# Luci (lua) compatible version that's easy to parse +# +lucistat() { + echo "local dsl={}" + status + echo "return dsl" +} + + +annex_b=10_00_10_00_00_04_00_00 +annex_bdmt=10_00_00_00_00_00_00_00 +annex_b2=00_00_10_00_00_00_00_00 +annex_b2p=00_00_00_00_00_04_00_00 +annex_a=04_01_04_00_00_01_00_00 +annex_at1=01_00_00_00_00_00_00_00 +annex_alite=00_01_00_00_00_00_00_00 +annex_admt=04_00_00_00_00_00_00_00 +annex_a2=00_00_04_00_00_00_00_00 +annex_a2p=00_00_00_00_00_01_00_00 +annex_l=00_00_00_00_04_00_00_00 +annex_m=00_00_00_00_40_00_04_00 +annex_m2=00_00_00_00_40_00_00_00 +annex_m2p=00_00_00_00_00_00_04_00 + +# +# Simple start routine +# +start() { + local annex + local fwannex + local xtu + config_load network + config_get annex adsl annex + config_get fwannex adsl fwannex + + # get xtu + eval "xtu=\"\${annex_$annex}\"" + + # check for invalid annex mode + [ -n "${annex}" -a -z "${xtu}" ] && + echo "unknown annex mode $annex" + + # check for invalid fw annex mode + [[ "$fwannex" != "a" && "$fwannex" != "b" ]] && + echo "invalid fwannex: $fwannex" + + # start CPE dsl daemon in the background + service_start /sbin/dsl_cpe_control -i${xtu} \ + -n /sbin/dsl_notify.sh \ + -f /lib/firmware/dsl-fw-${fwannex}.bin +} + +# +# For stop we want to simulate the notification call for when +# the line goes down, so that we can stop the ppp link before +# we die. +# +stop() { + DSL_NOTIFICATION_TYPE="DSL_INTERFACE_STATUS" \ + DSL_INTERFACE_STATUS="DOWN" \ + /sbin/dsl_notify.sh + + service_stop /sbin/dsl_cpe_control +} + diff --git a/package/network/config/ltq-dsl-app/files/dsl_notify.sh b/package/network/config/ltq-dsl-app/files/dsl_notify.sh new file mode 100644 index 000000000..5e9f3964e --- /dev/null +++ b/package/network/config/ltq-dsl-app/files/dsl_notify.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# +# This script is called by dsl_cpe_control whenever there is a DSL event, +# we only actually care about the DSL_INTERFACE_STATUS events as these +# tell us the line has either come up or gone down. +# +# The rest of the code is basically the same at the atm hotplug code +# + +[ "$DSL_NOTIFICATION_TYPE" = "DSL_INTERFACE_STATUS" ] || exit 0 + +. /lib/functions.sh + +include /lib/network +scan_interfaces + +local found=0 +local ifc +for ifc in $interfaces; do + local up + config_get_bool up "$ifc" up 0 + + local auto + config_get_bool auto "$ifc" auto 1 + + local proto + config_get proto "$ifc" proto + + if [ "$DSL_INTERFACE_STATUS" = "UP" ]; then + if [ "$proto" = "pppoa" ] && [ "$up" != 1 ] && [ "$auto" = 1 ]; then + found=1 + ( sleep 1; ifup "$ifc" ) & + fi + else + if [ "$proto" = "pppoa" ] && [ "$up" = 1 ] && [ "$auto" = 1 ]; then + found=1 + ( sleep 1; ifdown "$ifc" ) & + fi + fi +done + +if [ "$found" != 1 ]; then + logger "Found no matching interface for DSL notification ($DSL_INTERFACE_STATUS)" +fi diff --git a/package/network/config/soloscli/Makefile b/package/network/config/soloscli/Makefile new file mode 100644 index 000000000..9a7cd894f --- /dev/null +++ b/package/network/config/soloscli/Makefile @@ -0,0 +1,45 @@ +# +# Copyright (C) 2006-2011 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=soloscli +PKG_VERSION:=0.11 +PKG_RELEASE:=3 + +PKG_SOURCE:=solos-pci-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=@SF/openadsl +PKG_MD5SUM:=ab3f3fa654ef7c3a402980eca094e690 + +PKG_INSTALL:=1 + +PKG_BUILD_DIR:=$(BUILD_DIR)/solos-pci-$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk + +define Package/soloscli + SECTION:=net + CATEGORY:=Network + TITLE:=Configuration utility for Solos ADSL2+ modems + DEPENDS:=+kmod-solos-pci + URL:=http://sourceforge.net/projects/openadsl +endef + +define Package/soloscli/description + This package contains the soloscli utility + for interrogating Traverse Technologies' Solos ADSL2+ modems. +endef + +define Package/soloscli/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/soloscli/soloscli $(1)/usr/bin/ + $(INSTALL_BIN) ./files/solos-log-stats $(1)/usr/bin/ + $(INSTALL_DIR) $(1)/etc/hotplug.d/atm + $(INSTALL_DATA) ./files/etc/hotplug.d/atm/15-solos-init $(1)/etc/hotplug.d/atm/ +endef + +$(eval $(call BuildPackage,soloscli)) diff --git a/package/network/config/soloscli/files/etc/hotplug.d/atm/15-solos-init b/package/network/config/soloscli/files/etc/hotplug.d/atm/15-solos-init new file mode 100644 index 000000000..36d13ea5a --- /dev/null +++ b/package/network/config/soloscli/files/etc/hotplug.d/atm/15-solos-init @@ -0,0 +1,26 @@ +#!/bin/sh + +dialog() { + local tag="$(echo "$1" | cut -d= -f1)" + local value="$(echo "$1" | cut -d= -f2-)" + local response + + response="$(soloscli -s "$port" "$tag" "$value")" + [ $? -ne 0 ] && { + logger "soloscli($port): $tag '$value' returns $response" + } +} + +if [ "$ACTION" = "add" ]; then + include /lib/network + scan_interfaces + + case $DEVICENAME in + solos-pci[0-3]) + port="${DEVICENAME#solos-pci}" + device="solos${port}" + + config_list_foreach wan "$device" dialog + ;; + esac +fi diff --git a/package/network/config/soloscli/files/etc/uci-default/solos b/package/network/config/soloscli/files/etc/uci-default/solos new file mode 100644 index 000000000..7f69da62c --- /dev/null +++ b/package/network/config/soloscli/files/etc/uci-default/solos @@ -0,0 +1,15 @@ +#!/bin/sh + +uci batch <<__EOF__ + +delete network.wan.solos0 + +add_list network.wan.solos0="ActivateLine=Abort" +add_list network.wan.solos0="Retrain=EnableAll" +add_list network.wan.solos0="DetectNoise=Enable" +add_list network.wan.solos0="BisMCapability=Disable" +add_list network.wan.solos0="BisACapability=Disable" +add_list network.wan.solos0="ActivateLine=Start" + +commit network +__EOF__ diff --git a/package/network/config/soloscli/files/solos-log-stats b/package/network/config/soloscli/files/solos-log-stats new file mode 100644 index 000000000..2b75ee30c --- /dev/null +++ b/package/network/config/soloscli/files/solos-log-stats @@ -0,0 +1,19 @@ +#!/bin/sh + +cd /sys/class/atm/ || exit 1 + +for PORT in solos-pci* ; do + + RXRATE=`cat $PORT/parameters/RxBitRate` + TXRATE=`cat $PORT/parameters/TxBitRate` + RXSNR=`cat $PORT/parameters/LocalSNRMargin | sed "s/ dB//"` + TXSNR=`cat $PORT/parameters/RemoteSNRMargin | sed "s/ dB//"` + RXERR=`cat $PORT/parameters/RSUnCorrectedErrorsDn` + TXERR=`cat $PORT/parameters/RSUnCorrectedErrorsUp` + RXFEC=`cat $PORT/parameters/RSCorrectedErrorsDn` + TXFEC=`cat $PORT/parameters/RSCorrectedErrorsUp` + + echo "$RXRATE $RXSNR $RXERR $RXFEC / $TXRATE $TXSNR $TXERR $TXFEC" | + logger -t $PORT +done + diff --git a/package/network/config/soloscli/patches/000-Makefile.patch b/package/network/config/soloscli/patches/000-Makefile.patch new file mode 100644 index 000000000..914d6d886 --- /dev/null +++ b/package/network/config/soloscli/patches/000-Makefile.patch @@ -0,0 +1,21 @@ +--- a/Makefile 2010-06-14 14:17:11.000000000 +1000 ++++ b/Makefile 2010-06-14 14:17:54.000000000 +1000 +@@ -13,6 +13,11 @@ + KDIR ?= /lib/modules/$(shell uname -r)/build + PWD := $(shell pwd) + ++soloscli/soloscli: ++ $(MAKE) -C soloscli ++ ++install: ++ + default: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +@@ -20,5 +25,6 @@ + rm -f *.o *.ko *.mod.c .*.cmd + rm -rf .tmp_versions + rm -f Module.symvers ++ $(MAKE) -C soloscli clean + + endif diff --git a/package/network/config/soloscli/patches/001-newline.patch b/package/network/config/soloscli/patches/001-newline.patch new file mode 100644 index 000000000..4b663e889 --- /dev/null +++ b/package/network/config/soloscli/patches/001-newline.patch @@ -0,0 +1,15 @@ +--- a/soloscli/soloscli.c ++++ b/soloscli/soloscli.c +@@ -238,7 +238,11 @@ + } + if (strcmp(buf,pid) == 0) { + /* printf("Sequence matches.\n"); */ +- printf("%s",bufp); ++ if(buf[(len-1)] == '\n'){ ++ printf("%s",bufp); ++ } else { ++ printf("%s\n",bufp); ++ } + } else { + printf("Sequence incorrect.\n"); + buf[i] = '\n'; diff --git a/package/network/config/swconfig/Makefile b/package/network/config/swconfig/Makefile new file mode 100644 index 000000000..adc214745 --- /dev/null +++ b/package/network/config/swconfig/Makefile @@ -0,0 +1,51 @@ +# +# Copyright (C) 2008-2010 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=swconfig +PKG_RELEASE:=10 + +PKG_MAINTAINER:=Felix Fietkau <nbd@openwrt.org> + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/kernel.mk + +define Package/swconfig + SECTION:=base + CATEGORY:=Base system + DEPENDS:=@!TARGET_brcm47xx +libuci +libnl-tiny + TITLE:=Switch configuration utility +endef + +TARGET_CPPFLAGS := \ + -D_GNU_SOURCE \ + -I$(STAGING_DIR)/usr/include/libnl-tiny \ + -I$(LINUX_DIR)/include \ + -I$(LINUX_DIR)/arch/$(LINUX_KARCH)/include \ + -I$(PKG_BUILD_DIR) \ + $(TARGET_CPPFLAGS) + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Compile + CFLAGS="$(TARGET_CPPFLAGS) $(TARGET_CFLAGS)" \ + $(MAKE) -C $(PKG_BUILD_DIR) \ + $(TARGET_CONFIGURE_OPTS) \ + LIBS="$(TARGET_LDFLAGS) -lnl-tiny -lm -luci" +endef + +define Package/swconfig/install + $(INSTALL_DIR) $(1)/sbin $(1)/lib/network + $(INSTALL_BIN) $(PKG_BUILD_DIR)/swconfig $(1)/sbin/swconfig + $(INSTALL_DATA) ./files/switch.sh $(1)/lib/network/ +endef + +$(eval $(call BuildPackage,swconfig)) diff --git a/package/network/config/swconfig/files/switch.sh b/package/network/config/swconfig/files/switch.sh new file mode 100644 index 000000000..18d5fbd2c --- /dev/null +++ b/package/network/config/swconfig/files/switch.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Copyright (C) 2009 OpenWrt.org + +setup_switch_dev() { + config_get name "$1" name + name="${name:-$1}" + [ -d "/sys/class/net/$name" ] && ifconfig "$name" up + swconfig dev "$name" load network +} + +setup_switch() { + config_load network + config_foreach setup_switch_dev switch +} diff --git a/package/network/config/swconfig/src/Makefile b/package/network/config/swconfig/src/Makefile new file mode 100644 index 000000000..0d56f4315 --- /dev/null +++ b/package/network/config/swconfig/src/Makefile @@ -0,0 +1,12 @@ +ifndef CFLAGS +CFLAGS = -O2 -g -I ../src +endif +LIBS=-lnl -lnl-genl + +all: swconfig + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $^ + +swconfig: cli.o swlib.o uci.o + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) diff --git a/package/network/config/swconfig/src/cli.c b/package/network/config/swconfig/src/cli.c new file mode 100644 index 000000000..9cd16abb5 --- /dev/null +++ b/package/network/config/swconfig/src/cli.c @@ -0,0 +1,340 @@ +/* + * swconfig.c: Switch configuration utility + * + * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2010 Martin Mares <mj@ucw.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundatio. + * + * 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. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <stdint.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <uci.h> + +#include <linux/types.h> +#include <linux/netlink.h> +#include <linux/genetlink.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <linux/switch.h> +#include "swlib.h" + +enum { + CMD_NONE, + CMD_GET, + CMD_SET, + CMD_LOAD, + CMD_HELP, + CMD_SHOW, +}; + +static void +print_attrs(const struct switch_attr *attr) +{ + int i = 0; + while (attr) { + const char *type; + switch(attr->type) { + case SWITCH_TYPE_INT: + type = "int"; + break; + case SWITCH_TYPE_STRING: + type = "string"; + break; + case SWITCH_TYPE_PORTS: + type = "ports"; + break; + case SWITCH_TYPE_NOVAL: + type = "none"; + break; + default: + type = "unknown"; + break; + } + printf("\tAttribute %d (%s): %s (%s)\n", ++i, type, attr->name, attr->description); + attr = attr->next; + } +} + +static void +list_attributes(struct switch_dev *dev) +{ + printf("%s: %s(%s), ports: %d (cpu @ %d), vlans: %d\n", dev->dev_name, dev->alias, dev->name, dev->ports, dev->cpu_port, dev->vlans); + printf(" --switch\n"); + print_attrs(dev->ops); + printf(" --vlan\n"); + print_attrs(dev->vlan_ops); + printf(" --port\n"); + print_attrs(dev->port_ops); +} + +static void +print_attr_val(const struct switch_attr *attr, const struct switch_val *val) +{ + int i; + + switch (attr->type) { + case SWITCH_TYPE_INT: + printf("%d", val->value.i); + break; + case SWITCH_TYPE_STRING: + printf("%s", val->value.s); + break; + case SWITCH_TYPE_PORTS: + for(i = 0; i < val->len; i++) { + printf("%d%s ", + val->value.ports[i].id, + (val->value.ports[i].flags & + SWLIB_PORT_FLAG_TAGGED) ? "t" : ""); + } + break; + default: + printf("?unknown-type?"); + } +} + +static void +show_attrs(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) +{ + while (attr) { + if (attr->type != SWITCH_TYPE_NOVAL) { + printf("\t%s: ", attr->name); + if (swlib_get_attr(dev, attr, val) < 0) + printf("???"); + else + print_attr_val(attr, val); + putchar('\n'); + } + attr = attr->next; + } +} + +static void +show_global(struct switch_dev *dev) +{ + struct switch_val val; + + printf("Global attributes:\n"); + show_attrs(dev, dev->ops, &val); +} + +static void +show_port(struct switch_dev *dev, int port) +{ + struct switch_val val; + + printf("Port %d:\n", port); + val.port_vlan = port; + show_attrs(dev, dev->port_ops, &val); +} + +static void +show_vlan(struct switch_dev *dev, int vlan, bool all) +{ + struct switch_val val; + struct switch_attr *attr; + + val.port_vlan = vlan; + + if (all) { + attr = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_VLAN, "ports"); + if (swlib_get_attr(dev, attr, &val) < 0) + return; + + if (!val.len) + return; + } + + printf("VLAN %d:\n", vlan); + show_attrs(dev, dev->vlan_ops, &val); +} + +static void +print_usage(void) +{ + printf("swconfig dev <dev> [port <port>|vlan <vlan>] (help|set <key> <value>|get <key>|load <config>|show)\n"); + exit(1); +} + +static void +swconfig_load_uci(struct switch_dev *dev, const char *name) +{ + struct uci_context *ctx; + struct uci_package *p = NULL; + struct uci_element *e; + int ret = -1; + + ctx = uci_alloc_context(); + if (!ctx) + return; + + uci_load(ctx, name, &p); + if (!p) { + uci_perror(ctx, "Failed to load config file: "); + goto out; + } + + ret = swlib_apply_from_uci(dev, p); + if (ret < 0) + fprintf(stderr, "Failed to apply configuration for switch '%s'\n", dev->dev_name); + +out: + uci_free_context(ctx); + exit(ret); +} + +int main(int argc, char **argv) +{ + int retval = 0; + struct switch_dev *dev; + struct switch_attr *a; + struct switch_val val; + int err; + int i; + + int cmd = CMD_NONE; + char *cdev = NULL; + int cport = -1; + int cvlan = -1; + char *ckey = NULL; + char *cvalue = NULL; + + if(argc < 4) + print_usage(); + + if(strcmp(argv[1], "dev")) + print_usage(); + + cdev = argv[2]; + + for(i = 3; i < argc; i++) + { + char *arg = argv[i]; + if (cmd != CMD_NONE) { + print_usage(); + } else if (!strcmp(arg, "port") && i+1 < argc) { + cport = atoi(argv[++i]); + } else if (!strcmp(arg, "vlan") && i+1 < argc) { + cvlan = atoi(argv[++i]); + } else if (!strcmp(arg, "help")) { + cmd = CMD_HELP; + } else if (!strcmp(arg, "set") && i+1 < argc) { + cmd = CMD_SET; + ckey = argv[++i]; + if (i+1 < argc) + cvalue = argv[++i]; + } else if (!strcmp(arg, "get") && i+1 < argc) { + cmd = CMD_GET; + ckey = argv[++i]; + } else if (!strcmp(arg, "load") && i+1 < argc) { + if ((cport >= 0) || (cvlan >= 0)) + print_usage(); + cmd = CMD_LOAD; + ckey = argv[++i]; + } else if (!strcmp(arg, "show")) { + cmd = CMD_SHOW; + } else { + print_usage(); + } + } + + if (cmd == CMD_NONE) + print_usage(); + if (cport > -1 && cvlan > -1) + print_usage(); + + dev = swlib_connect(cdev); + if (!dev) { + fprintf(stderr, "Failed to connect to the switch\n"); + return 1; + } + + swlib_scan(dev); + + if (cmd == CMD_GET || cmd == CMD_SET) { + if(cport > -1) + a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_PORT, ckey); + else if(cvlan > -1) + a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_VLAN, ckey); + else + a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_GLOBAL, ckey); + + if(!a) + { + fprintf(stderr, "Unknown attribute \"%s\"\n", ckey); + goto out; + } + } + + switch(cmd) + { + case CMD_SET: + if ((a->type != SWITCH_TYPE_NOVAL) && + (cvalue == NULL)) + print_usage(); + + if(cvlan > -1) + cport = cvlan; + + if(swlib_set_attr_string(dev, a, cport, cvalue) < 0) + { + fprintf(stderr, "failed\n"); + retval = -1; + goto out; + } + break; + case CMD_GET: + if(cvlan > -1) + val.port_vlan = cvlan; + if(cport > -1) + val.port_vlan = cport; + if(swlib_get_attr(dev, a, &val) < 0) + { + fprintf(stderr, "failed\n"); + retval = -1; + goto out; + } + print_attr_val(a, &val); + putchar('\n'); + break; + case CMD_LOAD: + swconfig_load_uci(dev, ckey); + break; + case CMD_HELP: + list_attributes(dev); + break; + case CMD_SHOW: + if (cport >= 0 || cvlan >= 0) { + if (cport >= 0) + show_port(dev, cport); + else + show_vlan(dev, cvlan, false); + } else { + show_global(dev); + for (i=0; i < dev->ports; i++) + show_port(dev, i); + for (i=0; i < dev->vlans; i++) + show_vlan(dev, i, true); + } + break; + } + +out: + swlib_free_all(dev); + return 0; +} diff --git a/package/network/config/swconfig/src/swlib.c b/package/network/config/swconfig/src/swlib.c new file mode 100644 index 000000000..de08717e3 --- /dev/null +++ b/package/network/config/swconfig/src/swlib.c @@ -0,0 +1,686 @@ +/* + * swlib.c: Switch configuration API (user space part) + * + * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * 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. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <stdint.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/switch.h> +#include "swlib.h" +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> + +//#define DEBUG 1 +#ifdef DEBUG +#define DPRINTF(fmt, ...) fprintf(stderr, "%s(%d): " fmt, __func__, __LINE__, ##__VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +static struct nl_sock *handle; +static struct nl_cache *cache; +static struct genl_family *family; +static struct nlattr *tb[SWITCH_ATTR_MAX + 1]; +static int refcount = 0; + +static struct nla_policy port_policy[] = { + [SWITCH_PORT_ID] = { .type = NLA_U32 }, + [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG }, +}; + +static inline void * +swlib_alloc(size_t size) +{ + void *ptr; + + ptr = malloc(size); + if (!ptr) + goto done; + memset(ptr, 0, size); + +done: + return ptr; +} + +static int +wait_handler(struct nl_msg *msg, void *arg) +{ + int *finished = arg; + + *finished = 1; + return NL_STOP; +} + +/* helper function for performing netlink requests */ +static int +swlib_call(int cmd, int (*call)(struct nl_msg *, void *), + int (*data)(struct nl_msg *, void *), void *arg) +{ + struct nl_msg *msg; + struct nl_cb *cb = NULL; + int finished; + int flags = 0; + int err; + + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + + if (!data) + flags |= NLM_F_DUMP; + + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, genl_family_get_id(family), 0, flags, cmd, 0); + if (data) { + if (data(msg, arg) < 0) + goto nla_put_failure; + } + + cb = nl_cb_alloc(NL_CB_CUSTOM); + if (!cb) { + fprintf(stderr, "nl_cb_alloc failed.\n"); + exit(1); + } + + err = nl_send_auto_complete(handle, msg); + if (err < 0) { + fprintf(stderr, "nl_send_auto_complete failed: %d\n", err); + goto out; + } + + finished = 0; + + if (call) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, call, arg); + + if (data) + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished); + else + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler, &finished); + + err = nl_recvmsgs(handle, cb); + if (err < 0) { + goto out; + } + + if (!finished) + err = nl_wait_for_ack(handle); + +out: + if (cb) + nl_cb_put(cb); +nla_put_failure: + nlmsg_free(msg); + return err; +} + +static int +send_attr(struct nl_msg *msg, void *arg) +{ + struct switch_val *val = arg; + struct switch_attr *attr = val->attr; + + NLA_PUT_U32(msg, SWITCH_ATTR_ID, attr->dev->id); + NLA_PUT_U32(msg, SWITCH_ATTR_OP_ID, attr->id); + switch(attr->atype) { + case SWLIB_ATTR_GROUP_PORT: + NLA_PUT_U32(msg, SWITCH_ATTR_OP_PORT, val->port_vlan); + break; + case SWLIB_ATTR_GROUP_VLAN: + NLA_PUT_U32(msg, SWITCH_ATTR_OP_VLAN, val->port_vlan); + break; + default: + break; + } + + return 0; + +nla_put_failure: + return -1; +} + +static int +store_port_val(struct nl_msg *msg, struct nlattr *nla, struct switch_val *val) +{ + struct nlattr *p; + int ports = val->attr->dev->ports; + int err = 0; + int remaining; + + if (!val->value.ports) + val->value.ports = malloc(sizeof(struct switch_port) * ports); + + nla_for_each_nested(p, nla, remaining) { + struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1]; + struct switch_port *port; + + if (val->len >= ports) + break; + + err = nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, p, port_policy); + if (err < 0) + goto out; + + if (!tb[SWITCH_PORT_ID]) + continue; + + port = &val->value.ports[val->len]; + port->id = nla_get_u32(tb[SWITCH_PORT_ID]); + port->flags = 0; + if (tb[SWITCH_PORT_FLAG_TAGGED]) + port->flags |= SWLIB_PORT_FLAG_TAGGED; + + val->len++; + } + +out: + return err; +} + +static int +store_val(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct switch_val *val = arg; + struct switch_attr *attr = val->attr; + + if (!val) + goto error; + + if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL) < 0) { + goto error; + } + + if (tb[SWITCH_ATTR_OP_VALUE_INT]) + val->value.i = nla_get_u32(tb[SWITCH_ATTR_OP_VALUE_INT]); + else if (tb[SWITCH_ATTR_OP_VALUE_STR]) + val->value.s = strdup(nla_get_string(tb[SWITCH_ATTR_OP_VALUE_STR])); + else if (tb[SWITCH_ATTR_OP_VALUE_PORTS]) + val->err = store_port_val(msg, tb[SWITCH_ATTR_OP_VALUE_PORTS], val); + + val->err = 0; + return 0; + +error: + return NL_SKIP; +} + +int +swlib_get_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) +{ + int cmd; + int err; + + switch(attr->atype) { + case SWLIB_ATTR_GROUP_GLOBAL: + cmd = SWITCH_CMD_GET_GLOBAL; + break; + case SWLIB_ATTR_GROUP_PORT: + cmd = SWITCH_CMD_GET_PORT; + break; + case SWLIB_ATTR_GROUP_VLAN: + cmd = SWITCH_CMD_GET_VLAN; + break; + default: + return -EINVAL; + } + + memset(&val->value, 0, sizeof(val->value)); + val->len = 0; + val->attr = attr; + val->err = -EINVAL; + err = swlib_call(cmd, store_val, send_attr, val); + if (!err) + err = val->err; + + return err; +} + +static int +send_attr_ports(struct nl_msg *msg, struct switch_val *val) +{ + struct nlattr *n; + int i; + + /* TODO implement multipart? */ + if (val->len == 0) + goto done; + n = nla_nest_start(msg, SWITCH_ATTR_OP_VALUE_PORTS); + if (!n) + goto nla_put_failure; + for (i = 0; i < val->len; i++) { + struct switch_port *port = &val->value.ports[i]; + struct nlattr *np; + + np = nla_nest_start(msg, SWITCH_ATTR_PORT); + if (!np) + goto nla_put_failure; + + NLA_PUT_U32(msg, SWITCH_PORT_ID, port->id); + if (port->flags & SWLIB_PORT_FLAG_TAGGED) + NLA_PUT_FLAG(msg, SWITCH_PORT_FLAG_TAGGED); + + nla_nest_end(msg, np); + } + nla_nest_end(msg, n); +done: + return 0; + +nla_put_failure: + return -1; +} + +static int +send_attr_val(struct nl_msg *msg, void *arg) +{ + struct switch_val *val = arg; + struct switch_attr *attr = val->attr; + + if (send_attr(msg, arg)) + goto nla_put_failure; + + switch(attr->type) { + case SWITCH_TYPE_NOVAL: + break; + case SWITCH_TYPE_INT: + NLA_PUT_U32(msg, SWITCH_ATTR_OP_VALUE_INT, val->value.i); + break; + case SWITCH_TYPE_STRING: + if (!val->value.s) + goto nla_put_failure; + NLA_PUT_STRING(msg, SWITCH_ATTR_OP_VALUE_STR, val->value.s); + break; + case SWITCH_TYPE_PORTS: + if (send_attr_ports(msg, val) < 0) + goto nla_put_failure; + break; + default: + goto nla_put_failure; + } + return 0; + +nla_put_failure: + return -1; +} + +int +swlib_set_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) +{ + int cmd; + + switch(attr->atype) { + case SWLIB_ATTR_GROUP_GLOBAL: + cmd = SWITCH_CMD_SET_GLOBAL; + break; + case SWLIB_ATTR_GROUP_PORT: + cmd = SWITCH_CMD_SET_PORT; + break; + case SWLIB_ATTR_GROUP_VLAN: + cmd = SWITCH_CMD_SET_VLAN; + break; + default: + return -EINVAL; + } + + val->attr = attr; + return swlib_call(cmd, NULL, send_attr_val, val); +} + +int swlib_set_attr_string(struct switch_dev *dev, struct switch_attr *a, int port_vlan, const char *str) +{ + struct switch_port *ports; + struct switch_val val; + char *ptr; + + memset(&val, 0, sizeof(val)); + val.port_vlan = port_vlan; + switch(a->type) { + case SWITCH_TYPE_INT: + val.value.i = atoi(str); + break; + case SWITCH_TYPE_STRING: + val.value.s = str; + break; + case SWITCH_TYPE_PORTS: + ports = alloca(sizeof(struct switch_port) * dev->ports); + memset(ports, 0, sizeof(struct switch_port) * dev->ports); + val.len = 0; + ptr = (char *)str; + while(ptr && *ptr) + { + while(*ptr && isspace(*ptr)) + ptr++; + + if (!*ptr) + break; + + if (!isdigit(*ptr)) + return -1; + + if (val.len >= dev->ports) + return -1; + + ports[val.len].flags = 0; + ports[val.len].id = strtoul(ptr, &ptr, 10); + while(*ptr && !isspace(*ptr)) { + if (*ptr == 't') + ports[val.len].flags |= SWLIB_PORT_FLAG_TAGGED; + else + return -1; + + ptr++; + } + if (*ptr) + ptr++; + val.len++; + } + val.value.ports = ports; + break; + case SWITCH_TYPE_NOVAL: + if (str && !strcmp(str, "0")) + return 0; + + break; + default: + return -1; + } + return swlib_set_attr(dev, a, &val); +} + + +struct attrlist_arg { + int id; + int atype; + struct switch_dev *dev; + struct switch_attr *prev; + struct switch_attr **head; +}; + +static int +add_id(struct nl_msg *msg, void *arg) +{ + struct attrlist_arg *l = arg; + + NLA_PUT_U32(msg, SWITCH_ATTR_ID, l->id); + + return 0; +nla_put_failure: + return -1; +} + +static int +add_attr(struct nl_msg *msg, void *ptr) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct attrlist_arg *arg = ptr; + struct switch_attr *new; + + if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL) < 0) + goto done; + + new = swlib_alloc(sizeof(struct switch_attr)); + if (!new) + goto done; + + new->dev = arg->dev; + new->atype = arg->atype; + if (arg->prev) { + arg->prev->next = new; + } else { + arg->prev = *arg->head; + } + *arg->head = new; + arg->head = &new->next; + + if (tb[SWITCH_ATTR_OP_ID]) + new->id = nla_get_u32(tb[SWITCH_ATTR_OP_ID]); + if (tb[SWITCH_ATTR_OP_TYPE]) + new->type = nla_get_u32(tb[SWITCH_ATTR_OP_TYPE]); + if (tb[SWITCH_ATTR_OP_NAME]) + new->name = strdup(nla_get_string(tb[SWITCH_ATTR_OP_NAME])); + if (tb[SWITCH_ATTR_OP_DESCRIPTION]) + new->description = strdup(nla_get_string(tb[SWITCH_ATTR_OP_DESCRIPTION])); + +done: + return NL_SKIP; +} + +int +swlib_scan(struct switch_dev *dev) +{ + struct attrlist_arg arg; + + if (dev->ops || dev->port_ops || dev->vlan_ops) + return 0; + + arg.atype = SWLIB_ATTR_GROUP_GLOBAL; + arg.dev = dev; + arg.id = dev->id; + arg.prev = NULL; + arg.head = &dev->ops; + swlib_call(SWITCH_CMD_LIST_GLOBAL, add_attr, add_id, &arg); + + arg.atype = SWLIB_ATTR_GROUP_PORT; + arg.prev = NULL; + arg.head = &dev->port_ops; + swlib_call(SWITCH_CMD_LIST_PORT, add_attr, add_id, &arg); + + arg.atype = SWLIB_ATTR_GROUP_VLAN; + arg.prev = NULL; + arg.head = &dev->vlan_ops; + swlib_call(SWITCH_CMD_LIST_VLAN, add_attr, add_id, &arg); + + return 0; +} + +struct switch_attr *swlib_lookup_attr(struct switch_dev *dev, + enum swlib_attr_group atype, const char *name) +{ + struct switch_attr *head; + + if (!name || !dev) + return NULL; + + switch(atype) { + case SWLIB_ATTR_GROUP_GLOBAL: + head = dev->ops; + break; + case SWLIB_ATTR_GROUP_PORT: + head = dev->port_ops; + break; + case SWLIB_ATTR_GROUP_VLAN: + head = dev->vlan_ops; + break; + } + while(head) { + if (!strcmp(name, head->name)) + return head; + head = head->next; + } + + return NULL; +} + +static void +swlib_priv_free(void) +{ + if (cache) + nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + handle = NULL; + cache = NULL; +} + +static int +swlib_priv_init(void) +{ + int ret; + + handle = nl_socket_alloc(); + if (!handle) { + DPRINTF("Failed to create handle\n"); + goto err; + } + + if (genl_connect(handle)) { + DPRINTF("Failed to connect to generic netlink\n"); + goto err; + } + + ret = genl_ctrl_alloc_cache(handle, &cache); + if (ret < 0) { + DPRINTF("Failed to allocate netlink cache\n"); + goto err; + } + + family = genl_ctrl_search_by_name(cache, "switch"); + if (!family) { + DPRINTF("Switch API not present\n"); + goto err; + } + return 0; + +err: + swlib_priv_free(); + return -EINVAL; +} + +struct swlib_scan_arg { + const char *name; + struct switch_dev *head; + struct switch_dev *ptr; +}; + +static int +add_switch(struct nl_msg *msg, void *arg) +{ + struct swlib_scan_arg *sa = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct switch_dev *dev; + const char *name; + const char *alias; + + if (nla_parse(tb, SWITCH_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) + goto done; + + if (!tb[SWITCH_ATTR_DEV_NAME]) + goto done; + + name = nla_get_string(tb[SWITCH_ATTR_DEV_NAME]); + alias = nla_get_string(tb[SWITCH_ATTR_ALIAS]); + + if (sa->name && (strcmp(name, sa->name) != 0) && (strcmp(alias, sa->name) != 0)) + goto done; + + dev = swlib_alloc(sizeof(struct switch_dev)); + if (!dev) + goto done; + + strncpy(dev->dev_name, name, IFNAMSIZ - 1); + dev->alias = strdup(alias); + if (tb[SWITCH_ATTR_ID]) + dev->id = nla_get_u32(tb[SWITCH_ATTR_ID]); + if (tb[SWITCH_ATTR_NAME]) + dev->name = strdup(nla_get_string(tb[SWITCH_ATTR_NAME])); + if (tb[SWITCH_ATTR_PORTS]) + dev->ports = nla_get_u32(tb[SWITCH_ATTR_PORTS]); + if (tb[SWITCH_ATTR_VLANS]) + dev->vlans = nla_get_u32(tb[SWITCH_ATTR_VLANS]); + if (tb[SWITCH_ATTR_CPU_PORT]) + dev->cpu_port = nla_get_u32(tb[SWITCH_ATTR_CPU_PORT]); + + if (!sa->head) { + sa->head = dev; + sa->ptr = dev; + } else { + sa->ptr->next = dev; + sa->ptr = dev; + } + + refcount++; +done: + return NL_SKIP; +} + + +struct switch_dev * +swlib_connect(const char *name) +{ + struct swlib_scan_arg arg; + int err; + + if (!refcount) { + if (swlib_priv_init() < 0) + return NULL; + }; + + arg.head = NULL; + arg.ptr = NULL; + arg.name = name; + swlib_call(SWITCH_CMD_GET_SWITCH, add_switch, NULL, &arg); + + if (!refcount) + swlib_priv_free(); + + return arg.head; +} + +static void +swlib_free_attributes(struct switch_attr **head) +{ + struct switch_attr *a = *head; + struct switch_attr *next; + + while (a) { + next = a->next; + free(a); + a = next; + } + *head = NULL; +} + +void +swlib_free(struct switch_dev *dev) +{ + swlib_free_attributes(&dev->ops); + swlib_free_attributes(&dev->port_ops); + swlib_free_attributes(&dev->vlan_ops); + free(dev); + + if (--refcount == 0) + swlib_priv_free(); +} + +void +swlib_free_all(struct switch_dev *dev) +{ + struct switch_dev *p; + + while (dev) { + p = dev->next; + swlib_free(dev); + dev = p; + } +} diff --git a/package/network/config/swconfig/src/swlib.h b/package/network/config/swconfig/src/swlib.h new file mode 100644 index 000000000..ff73969c8 --- /dev/null +++ b/package/network/config/swconfig/src/swlib.h @@ -0,0 +1,234 @@ +/* + * swlib.h: Switch configuration API (user space part) + * + * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * 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. + * + +Usage of the library functions: + + The main datastructure for a switch is the struct switch_device + To get started, you first need to use switch_connect() to probe + for switches and allocate an instance of this struct. + + There are two possible usage modes: + dev = switch_connect("eth0"); + - this call will look for a switch registered for the linux device + "eth0" and only allocate a switch_device for this particular switch. + + dev = switch_connect(NULL) + - this will return one switch_device struct for each available + switch. The switch_device structs are chained with by ->next pointer + + Then to query a switch for all available attributes, use: + swlib_scan(dev); + + All allocated datastructures for the switch_device struct can be freed with + swlib_free(dev); + or + swlib_free_all(dev); + + The latter traverses a whole chain of switch_device structs and frees them all + + Switch attributes (struct switch_attr) are divided into three groups: + dev->ops: + - global settings + dev->port_ops: + - per-port settings + dev->vlan_ops: + - per-vlan settings + + switch_lookup_attr() is a small helper function to locate attributes + by name. + + switch_set_attr() and switch_get_attr() can alter or request the values + of attributes. + +Usage of the switch_attr struct: + + ->atype: attribute group, one of: + - SWLIB_ATTR_GROUP_GLOBAL + - SWLIB_ATTR_GROUP_VLAN + - SWLIB_ATTR_GROUP_PORT + + ->id: identifier for the attribute + + ->type: data type, one of: + - SWITCH_TYPE_INT + - SWITCH_TYPE_STRING + - SWITCH_TYPE_PORT + + ->name: short name of the attribute + ->description: longer description + ->next: pointer to the next attribute of the current group + + +Usage of the switch_val struct: + + When setting attributes, following members of the struct switch_val need + to be set up: + + ->len (for attr->type == SWITCH_TYPE_PORT) + ->port_vlan: + - port number (for attr->atype == SWLIB_ATTR_GROUP_PORT), or: + - vlan number (for attr->atype == SWLIB_ATTR_GROUP_VLAN) + ->value.i (for attr->type == SWITCH_TYPE_INT) + ->value.s (for attr->type == SWITCH_TYPE_STRING) + - owned by the caller, not stored in the library internally + ->value.ports (for attr->type == SWITCH_TYPE_PORT) + - must point to an array of at lest val->len * sizeof(struct switch_port) + + When getting string attributes, val->value.s must be freed by the caller + When getting port list attributes, an internal static buffer is used, + which changes from call to call. + + */ + +#ifndef __SWLIB_H +#define __SWLIB_H + +enum swlib_attr_group { + SWLIB_ATTR_GROUP_GLOBAL, + SWLIB_ATTR_GROUP_VLAN, + SWLIB_ATTR_GROUP_PORT, +}; + +enum swlib_port_flags { + SWLIB_PORT_FLAG_TAGGED = (1 << 0), +}; + + +struct switch_dev; +struct switch_attr; +struct switch_port; +struct switch_val; +struct uci_package; + +struct switch_dev { + int id; + char dev_name[IFNAMSIZ]; + const char *name; + const char *alias; + int ports; + int vlans; + int cpu_port; + struct switch_attr *ops; + struct switch_attr *port_ops; + struct switch_attr *vlan_ops; + struct switch_dev *next; + void *priv; +}; + +struct switch_val { + struct switch_attr *attr; + int len; + int err; + int port_vlan; + union { + const char *s; + int i; + struct switch_port *ports; + } value; +}; + +struct switch_attr { + struct switch_dev *dev; + int atype; + int id; + int type; + const char *name; + const char *description; + struct switch_attr *next; +}; + +struct switch_port { + unsigned int id; + unsigned int flags; +}; + +/** + * swlib_connect: connect to the switch through netlink + * @name: name of the ethernet interface, + * + * if name is NULL, it connect and builds a chain of all switches + */ +struct switch_dev *swlib_connect(const char *name); + +/** + * swlib_free: free all dynamically allocated data for the switch connection + * @dev: switch device struct + * + * all members of a switch device chain (generated by swlib_connect(NULL)) + * must be freed individually + */ +void swlib_free(struct switch_dev *dev); + +/** + * swlib_free_all: run swlib_free on all devices in the chain + * @dev: switch device struct + */ +void swlib_free_all(struct switch_dev *dev); + +/** + * swlib_scan: probe the switch driver for available commands/attributes + * @dev: switch device struct + */ +int swlib_scan(struct switch_dev *dev); + +/** + * swlib_lookup_attr: look up a switch attribute + * @dev: switch device struct + * @type: global, port or vlan + * @name: name of the attribute + */ +struct switch_attr *swlib_lookup_attr(struct switch_dev *dev, + enum swlib_attr_group atype, const char *name); + +/** + * swlib_set_attr: set the value for an attribute + * @dev: switch device struct + * @attr: switch attribute struct + * @val: attribute value pointer + * returns 0 on success + */ +int swlib_set_attr(struct switch_dev *dev, struct switch_attr *attr, + struct switch_val *val); + +/** + * swlib_set_attr_string: set the value for an attribute with type conversion + * @dev: switch device struct + * @attr: switch attribute struct + * @port_vlan: port or vlan (if applicable) + * @str: string value + * returns 0 on success + */ +int swlib_set_attr_string(struct switch_dev *dev, struct switch_attr *attr, + int port_vlan, const char *str); + +/** + * swlib_get_attr: get the value for an attribute + * @dev: switch device struct + * @attr: switch attribute struct + * @val: attribute value pointer + * returns 0 on success + * for string attributes, the result string must be freed by the caller + */ +int swlib_get_attr(struct switch_dev *dev, struct switch_attr *attr, + struct switch_val *val); + +/** + * swlib_apply_from_uci: set up the switch from a uci configuration + * @dev: switch device struct + * @p: uci package which contains the desired global config + */ +int swlib_apply_from_uci(struct switch_dev *dev, struct uci_package *p); + +#endif diff --git a/package/network/config/swconfig/src/uci.c b/package/network/config/swconfig/src/uci.c new file mode 100644 index 000000000..893e3d1dd --- /dev/null +++ b/package/network/config/swconfig/src/uci.c @@ -0,0 +1,248 @@ +/* + * uci.c: UCI binding for the switch configuration utility + * + * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundatio. + * + * 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. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <stdint.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <uci.h> + +#include <linux/types.h> +#include <linux/netlink.h> +#include <linux/genetlink.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <linux/switch.h> +#include "swlib.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +struct swlib_setting { + struct switch_attr *attr; + const char *name; + int port_vlan; + const char *val; + struct swlib_setting *next; +}; + +struct swlib_setting early_settings[] = { + { .name = "reset", .val = "1" }, + { .name = "enable_vlan", .val = "1" }, +}; + +static struct swlib_setting *settings; +static struct swlib_setting **head; + +static bool swlib_match_name(struct switch_dev *dev, const char *name) +{ + return (strcmp(name, dev->dev_name) == 0 || + strcmp(name, dev->alias) == 0); +} + +static int +swlib_map_settings(struct switch_dev *dev, int type, int port_vlan, struct uci_section *s) +{ + struct swlib_setting *setting; + struct switch_attr *attr; + struct uci_element *e; + struct uci_option *o; + int i; + + uci_foreach_element(&s->options, e) { + o = uci_to_option(e); + + if (o->type != UCI_TYPE_STRING) + continue; + + if (!strcmp(e->name, "device")) + continue; + + /* map early settings */ + if (type == SWLIB_ATTR_GROUP_GLOBAL) { + int i; + + for (i = 0; i < ARRAY_SIZE(early_settings); i++) { + if (strcmp(e->name, early_settings[i].name) != 0) + continue; + + early_settings[i].val = o->v.string; + goto skip; + } + } + + attr = swlib_lookup_attr(dev, type, e->name); + if (!attr) + continue; + + setting = malloc(sizeof(struct swlib_setting)); + memset(setting, 0, sizeof(struct swlib_setting)); + setting->attr = attr; + setting->port_vlan = port_vlan; + setting->val = o->v.string; + *head = setting; + head = &setting->next; +skip: + continue; + } +} + +int swlib_apply_from_uci(struct switch_dev *dev, struct uci_package *p) +{ + struct switch_attr *attr; + struct uci_context *ctx = p->ctx; + struct uci_element *e; + struct uci_section *s; + struct uci_option *o; + struct uci_ptr ptr; + struct switch_val val; + int i; + + settings = NULL; + head = &settings; + + uci_foreach_element(&p->sections, e) { + struct uci_element *n; + + s = uci_to_section(e); + + if (strcmp(s->type, "switch") != 0) + continue; + + uci_foreach_element(&s->options, n) { + struct uci_option *o = uci_to_option(n); + + if (strcmp(n->name, "name") != 0) + continue; + + if (o->type != UCI_TYPE_STRING) + continue; + + if (swlib_match_name(dev, o->v.string)) + goto found; + + break; + } + + if (!swlib_match_name(dev, e->name)) + continue; + + goto found; + } + + /* not found */ + return -1; + +found: + /* look up available early options, which need to be taken care + * of in the correct order */ + for (i = 0; i < ARRAY_SIZE(early_settings); i++) { + early_settings[i].attr = swlib_lookup_attr(dev, + SWLIB_ATTR_GROUP_GLOBAL, early_settings[i].name); + } + swlib_map_settings(dev, SWLIB_ATTR_GROUP_GLOBAL, 0, s); + + /* look for port or vlan sections */ + uci_foreach_element(&p->sections, e) { + struct uci_element *os; + s = uci_to_section(e); + + if (!strcmp(s->type, "switch_port")) { + char *devn, *port, *port_err = NULL; + int port_n; + + uci_foreach_element(&s->options, os) { + o = uci_to_option(os); + if (o->type != UCI_TYPE_STRING) + continue; + + if (!strcmp(os->name, "device")) { + devn = o->v.string; + if (!swlib_match_name(dev, devn)) + devn = NULL; + } else if (!strcmp(os->name, "port")) { + port = o->v.string; + } + } + if (!devn || !port || !port[0]) + continue; + + port_n = strtoul(port, &port_err, 0); + if (port_err && port_err[0]) + continue; + + swlib_map_settings(dev, SWLIB_ATTR_GROUP_PORT, port_n, s); + } else if (!strcmp(s->type, "switch_vlan")) { + char *devn, *vlan, *vlan_err = NULL; + int vlan_n; + + uci_foreach_element(&s->options, os) { + o = uci_to_option(os); + if (o->type != UCI_TYPE_STRING) + continue; + + if (!strcmp(os->name, "device")) { + devn = o->v.string; + if (!swlib_match_name(dev, devn)) + devn = NULL; + } else if (!strcmp(os->name, "vlan")) { + vlan = o->v.string; + } + } + if (!devn || !vlan || !vlan[0]) + continue; + + vlan_n = strtoul(vlan, &vlan_err, 0); + if (vlan_err && vlan_err[0]) + continue; + + swlib_map_settings(dev, SWLIB_ATTR_GROUP_VLAN, vlan_n, s); + } + } + + for (i = 0; i < ARRAY_SIZE(early_settings); i++) { + struct swlib_setting *st = &early_settings[i]; + if (!st->attr || !st->val) + continue; + swlib_set_attr_string(dev, st->attr, st->port_vlan, st->val); + + } + + while (settings) { + struct swlib_setting *st = settings; + + swlib_set_attr_string(dev, st->attr, st->port_vlan, st->val); + st = st->next; + free(settings); + settings = st; + } + + /* Apply the config */ + attr = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_GLOBAL, "apply"); + if (!attr) + return 0; + + memset(&val, 0, sizeof(val)); + swlib_set_attr(dev, attr, &val); + + return 0; +} |